Skip to content

Commit 3d9ff4d

Browse files
pcloudsgitster
authored andcommitted
shallow.c: implement a generic shallow boundary finder based on rev-list
Instead of a custom commit walker like get_shallow_commits(), this new function uses rev-list to mark NOT_SHALLOW to all reachable commits, except borders. The definition of reachable is to be defined by the protocol later. This makes it more flexible to define shallow boundary. The way we find border is paint all reachable commits NOT_SHALLOW. Any of them that "touches" commits without NOT_SHALLOW flag are considered shallow (e.g. zero parents via grafting mechanism). Shallow commits and their true parents are all marked SHALLOW. Then NOT_SHALLOW is removed from shallow commits at the end. There is an interesting observation. With a generic walker, we can produce all kinds of shallow cutting. In the following graph, every commit but "x" is reachable. "b" is a parent of "a". x -- a -- o / / x -- c -- b -- o After this function is run, "a" and "c" are both considered shallow commits. After grafting occurs at the client side, what we see is a -- o / c -- b -- o Notice that because of grafting, "a" has zero parents, so "b" is no longer a parent of "a". This is unfortunate and may be solved in two ways. The first is change the way shallow grafting works and keep "a -- b" connection if "b" exists and always ends at shallow commits (iow, no loose ends). This is hard to detect, or at least not cheap to do. The second way is mark one "x" as shallow commit instead of "a" and produce this graph at client side: x -- a -- o / / c -- b -- o More commits, but simpler grafting rules. Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 79891cb commit 3d9ff4d

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

commit.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ extern int for_each_commit_graft(each_commit_graft_fn, void *);
258258
extern int is_repository_shallow(void);
259259
extern struct commit_list *get_shallow_commits(struct object_array *heads,
260260
int depth, int shallow_flag, int not_shallow_flag);
261+
extern struct commit_list *get_shallow_commits_by_rev_list(
262+
int ac, const char **av, int shallow_flag, int not_shallow_flag);
261263
extern void set_alternate_shallow_file(const char *path, int override);
262264
extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
263265
const struct sha1_array *extra);

shallow.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "diff.h"
1111
#include "revision.h"
1212
#include "commit-slab.h"
13+
#include "revision.h"
14+
#include "list-objects.h"
1315

1416
static int is_shallow = -1;
1517
static struct stat_validity shallow_stat;
@@ -137,6 +139,82 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
137139
return result;
138140
}
139141

142+
static void show_commit(struct commit *commit, void *data)
143+
{
144+
commit_list_insert(commit, data);
145+
}
146+
147+
/*
148+
* Given rev-list arguments, run rev-list. All reachable commits
149+
* except border ones are marked with not_shallow_flag. Border commits
150+
* are marked with shallow_flag. The list of border/shallow commits
151+
* are also returned.
152+
*/
153+
struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
154+
int shallow_flag,
155+
int not_shallow_flag)
156+
{
157+
struct commit_list *result = NULL, *p;
158+
struct commit_list *not_shallow_list = NULL;
159+
struct rev_info revs;
160+
int both_flags = shallow_flag | not_shallow_flag;
161+
162+
/*
163+
* SHALLOW (excluded) and NOT_SHALLOW (included) should not be
164+
* set at this point. But better be safe than sorry.
165+
*/
166+
clear_object_flags(both_flags);
167+
168+
is_repository_shallow(); /* make sure shallows are read */
169+
170+
init_revisions(&revs, NULL);
171+
save_commit_buffer = 0;
172+
setup_revisions(ac, av, &revs, NULL);
173+
174+
if (prepare_revision_walk(&revs))
175+
die("revision walk setup failed");
176+
traverse_commit_list(&revs, show_commit, NULL, &not_shallow_list);
177+
178+
/* Mark all reachable commits as NOT_SHALLOW */
179+
for (p = not_shallow_list; p; p = p->next)
180+
p->item->object.flags |= not_shallow_flag;
181+
182+
/*
183+
* mark border commits SHALLOW + NOT_SHALLOW.
184+
* We cannot clear NOT_SHALLOW right now. Imagine border
185+
* commit A is processed first, then commit B, whose parent is
186+
* A, later. If NOT_SHALLOW on A is cleared at step 1, B
187+
* itself is considered border at step 2, which is incorrect.
188+
*/
189+
for (p = not_shallow_list; p; p = p->next) {
190+
struct commit *c = p->item;
191+
struct commit_list *parent;
192+
193+
if (parse_commit(c))
194+
die("unable to parse commit %s",
195+
oid_to_hex(&c->object.oid));
196+
197+
for (parent = c->parents; parent; parent = parent->next)
198+
if (!(parent->item->object.flags & not_shallow_flag)) {
199+
c->object.flags |= shallow_flag;
200+
commit_list_insert(c, &result);
201+
break;
202+
}
203+
}
204+
free_commit_list(not_shallow_list);
205+
206+
/*
207+
* Now we can clean up NOT_SHALLOW on border commits. Having
208+
* both flags set can confuse the caller.
209+
*/
210+
for (p = result; p; p = p->next) {
211+
struct object *o = &p->item->object;
212+
if ((o->flags & both_flags) == both_flags)
213+
o->flags &= ~not_shallow_flag;
214+
}
215+
return result;
216+
}
217+
140218
static void check_shallow_file_for_update(void)
141219
{
142220
if (is_shallow == -1)

0 commit comments

Comments
 (0)