Skip to content

Commit 631ba30

Browse files
author
Junio C Hamano
committed
Merge branches 'jc/sb' and 'jc/mb'
3 parents 592ee97 + 4bc51db + 53de71f commit 631ba30

File tree

4 files changed

+202
-16
lines changed

4 files changed

+202
-16
lines changed

merge-base.c

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,95 @@ static struct commit *interesting(struct commit_list *list)
8080
* Now, list does not have any interesting commit. So we find the newest
8181
* commit from the result list that is not marked uninteresting. Which is
8282
* commit B.
83+
*
84+
*
85+
* Another pathological example how this thing can fail to mark an ancestor
86+
* of a merge base as UNINTERESTING without the postprocessing phase.
87+
*
88+
* 2
89+
* H
90+
* 1 / \
91+
* G A \
92+
* |\ / \
93+
* | B \
94+
* | \ \
95+
* \ C F
96+
* \ \ /
97+
* \ D /
98+
* \ | /
99+
* \| /
100+
* E
101+
*
102+
* list A B C D E F G H
103+
* G1 H2 - - - - - - 1 2
104+
* H2 E1 B1 - 1 - - 1 - 1 2
105+
* F2 E1 B1 A2 2 1 - - 1 2 1 2
106+
* E3 B1 A2 2 1 - - 3 2 1 2
107+
* B1 A2 2 1 - - 3 2 1 2
108+
* C1 A2 2 1 1 - 3 2 1 2
109+
* D1 A2 2 1 1 1 3 2 1 2
110+
* A2 2 1 1 1 3 2 1 2
111+
* B3 2 3 1 1 3 2 1 2
112+
* C7 2 3 7 1 3 2 1 2
113+
*
114+
* At this point, unfortunately, everybody in the list is
115+
* uninteresting, so we fail to complete the following two
116+
* steps to fully marking uninteresting commits.
117+
*
118+
* D7 2 3 7 7 3 2 1 2
119+
* E7 2 3 7 7 7 2 1 2
120+
*
121+
* and we end up showing E as an interesting merge base.
83122
*/
84123

85124
static int show_all = 0;
86125

126+
static void mark_reachable_commits(struct commit_list *result,
127+
struct commit_list *list)
128+
{
129+
struct commit_list *tmp;
130+
131+
/*
132+
* Postprocess to fully contaminate the well.
133+
*/
134+
for (tmp = result; tmp; tmp = tmp->next) {
135+
struct commit *c = tmp->item;
136+
/* Reinject uninteresting ones to list,
137+
* so we can scan their parents.
138+
*/
139+
if (c->object.flags & UNINTERESTING)
140+
commit_list_insert(c, &list);
141+
}
142+
while (list) {
143+
struct commit *c = list->item;
144+
struct commit_list *parents;
145+
146+
tmp = list;
147+
list = list->next;
148+
free(tmp);
149+
150+
/* Anything taken out of the list is uninteresting, so
151+
* mark all its parents uninteresting. We do not
152+
* parse new ones (we already parsed all the relevant
153+
* ones).
154+
*/
155+
parents = c->parents;
156+
while (parents) {
157+
struct commit *p = parents->item;
158+
parents = parents->next;
159+
if (!(p->object.flags & UNINTERESTING)) {
160+
p->object.flags |= UNINTERESTING;
161+
commit_list_insert(p, &list);
162+
}
163+
}
164+
}
165+
}
166+
87167
static int merge_base(struct commit *rev1, struct commit *rev2)
88168
{
89169
struct commit_list *list = NULL;
90170
struct commit_list *result = NULL;
171+
struct commit_list *tmp = NULL;
91172

92173
if (rev1 == rev2) {
93174
printf("%s\n", sha1_to_hex(rev1->object.sha1));
@@ -104,9 +185,10 @@ static int merge_base(struct commit *rev1, struct commit *rev2)
104185

105186
while (interesting(list)) {
106187
struct commit *commit = list->item;
107-
struct commit_list *tmp = list, *parents;
188+
struct commit_list *parents;
108189
int flags = commit->object.flags & 7;
109190

191+
tmp = list;
110192
list = list->next;
111193
free(tmp);
112194
if (flags == 3) {
@@ -130,6 +212,9 @@ static int merge_base(struct commit *rev1, struct commit *rev2)
130212
if (!result)
131213
return 1;
132214

215+
if (result->next && list)
216+
mark_reachable_commits(result, list);
217+
133218
while (result) {
134219
struct commit *commit = result->item;
135220
result = result->next;

show-branch.c

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,11 @@ static void join_revs(struct commit_list **list_p,
181181

182182
while (*list_p) {
183183
struct commit_list *parents;
184+
int still_interesting = !!interesting(*list_p);
184185
struct commit *commit = pop_one_commit(list_p);
185186
int flags = commit->object.flags & all_mask;
186-
int still_interesting = !!interesting(*list_p);
187187

188-
if (!still_interesting && extra < 0)
188+
if (!still_interesting && extra <= 0)
189189
break;
190190

191191
mark_seen(commit, seen_p);
@@ -199,18 +199,58 @@ static void join_revs(struct commit_list **list_p,
199199
parents = parents->next;
200200
if ((this_flag & flags) == flags)
201201
continue;
202-
parse_commit(p);
202+
if (!p->object.parsed)
203+
parse_commit(p);
203204
if (mark_seen(p, seen_p) && !still_interesting)
204205
extra--;
205206
p->object.flags |= flags;
206207
insert_by_date(p, list_p);
207208
}
208209
}
210+
211+
/*
212+
* Postprocess to complete well-poisoning.
213+
*
214+
* At this point we have all the commits we have seen in
215+
* seen_p list (which happens to be sorted chronologically but
216+
* it does not really matter). Mark anything that can be
217+
* reached from uninteresting commits not interesting.
218+
*/
219+
for (;;) {
220+
int changed = 0;
221+
struct commit_list *s;
222+
for (s = *seen_p; s; s = s->next) {
223+
struct commit *c = s->item;
224+
struct commit_list *parents;
225+
226+
if (((c->object.flags & all_revs) != all_revs) &&
227+
!(c->object.flags & UNINTERESTING))
228+
continue;
229+
230+
/* The current commit is either a merge base or
231+
* already uninteresting one. Mark its parents
232+
* as uninteresting commits _only_ if they are
233+
* already parsed. No reason to find new ones
234+
* here.
235+
*/
236+
parents = c->parents;
237+
while (parents) {
238+
struct commit *p = parents->item;
239+
parents = parents->next;
240+
if (!(p->object.flags & UNINTERESTING)) {
241+
p->object.flags |= UNINTERESTING;
242+
changed = 1;
243+
}
244+
}
245+
}
246+
if (!changed)
247+
break;
248+
}
209249
}
210250

211251
static void show_one_commit(struct commit *commit, int no_name)
212252
{
213-
char pretty[128], *cp;
253+
char pretty[256], *cp;
214254
struct commit_name *name = commit->object.util;
215255
if (commit->object.parsed)
216256
pretty_print_commit(CMIT_FMT_ONELINE, commit->buffer, ~0,
@@ -360,7 +400,7 @@ int main(int ac, char **av)
360400
unsigned int rev_mask[MAX_REVS];
361401
int num_rev, i, extra = 0;
362402
int all_heads = 0, all_tags = 0;
363-
int all_mask, all_revs, shown_merge_point;
403+
int all_mask, all_revs;
364404
char head_path[128];
365405
const char *head_path_p;
366406
int head_path_len;
@@ -369,6 +409,8 @@ int main(int ac, char **av)
369409
int independent = 0;
370410
int no_name = 0;
371411
int sha1_name = 0;
412+
int shown_merge_point = 0;
413+
int topo_order = 0;
372414

373415
setup_git_directory();
374416

@@ -394,6 +436,8 @@ int main(int ac, char **av)
394436
merge_base = 1;
395437
else if (!strcmp(arg, "--independent"))
396438
independent = 1;
439+
else if (!strcmp(arg, "--topo-order"))
440+
topo_order = 1;
397441
else
398442
usage(show_branch_usage);
399443
ac--; av++;
@@ -496,23 +540,21 @@ int main(int ac, char **av)
496540
exit(0);
497541

498542
/* Sort topologically */
499-
sort_in_topological_order(&seen);
543+
if (topo_order)
544+
sort_in_topological_order(&seen);
500545

501546
/* Give names to commits */
502547
if (!sha1_name && !no_name)
503548
name_commits(seen, rev, ref_name, num_rev);
504549

505550
all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
506551
all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
507-
shown_merge_point = 0;
508552

509553
while (seen) {
510554
struct commit *commit = pop_one_commit(&seen);
511555
int this_flag = commit->object.flags;
512-
int is_merge_point = (this_flag & all_revs) == all_revs;
513556

514-
if (is_merge_point)
515-
shown_merge_point = 1;
557+
shown_merge_point |= ((this_flag & all_revs) == all_revs);
516558

517559
if (1 < num_rev) {
518560
for (i = 0; i < num_rev; i++)
@@ -521,9 +563,9 @@ int main(int ac, char **av)
521563
putchar(' ');
522564
}
523565
show_one_commit(commit, no_name);
524-
if (shown_merge_point && is_merge_point)
525-
if (--extra < 0)
526-
break;
566+
567+
if (shown_merge_point && --extra < 0)
568+
break;
527569
}
528570
return 0;
529571
}

t/t1200-tutorial.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ cat > show-branch.expect << EOF
122122
++ [mybranch] Some work.
123123
EOF
124124

125-
git show-branch master mybranch > show-branch.output
125+
git show-branch --topo-order master mybranch > show-branch.output
126126
test_expect_success 'git show-branch' 'cmp show-branch.expect show-branch.output'
127127

128128
git checkout mybranch
@@ -145,7 +145,7 @@ cat > show-branch2.expect << EOF
145145
++ [master] Merged "mybranch" changes.
146146
EOF
147147

148-
git show-branch master mybranch > show-branch2.output
148+
git show-branch --topo-order master mybranch > show-branch2.output
149149
test_expect_success 'git show-branch' 'cmp show-branch2.expect show-branch2.output'
150150

151151
# TODO: test git fetch

t/t6010-merge-base.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (c) 2005 Junio C Hamano
4+
#
5+
6+
test_description='Merge base computation.
7+
'
8+
9+
. ./test-lib.sh
10+
11+
T=$(git-write-tree)
12+
13+
M=1130000000
14+
Z=+0000
15+
16+
export GIT_COMMITTER_EMAIL=git@comm.iter.xz
17+
export GIT_COMMITTER_NAME='C O Mmiter'
18+
export GIT_AUTHOR_NAME='A U Thor'
19+
export GIT_AUTHOR_EMAIL=git@au.thor.xz
20+
21+
doit() {
22+
OFFSET=$1; shift
23+
NAME=$1; shift
24+
PARENTS=
25+
for P
26+
do
27+
PARENTS="${PARENTS}-p $P "
28+
done
29+
GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z"
30+
GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE
31+
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
32+
commit=$(echo $NAME | git-commit-tree $T $PARENTS)
33+
echo $commit >.git/refs/tags/$NAME
34+
echo $commit
35+
}
36+
37+
# Setup...
38+
E=$(doit 5 E)
39+
D=$(doit 4 D $E)
40+
F=$(doit 6 F $E)
41+
C=$(doit 3 C $D)
42+
B=$(doit 2 B $C)
43+
A=$(doit 1 A $B)
44+
G=$(doit 7 G $B $E)
45+
H=$(doit 8 H $A $F)
46+
47+
test_expect_success 'compute merge-base (single)' \
48+
'MB=$(git-merge-base G H) &&
49+
expr "$(git-name-rev "$MB")" : "[0-9a-f]* B"'
50+
51+
test_expect_success 'compute merge-base (all)' \
52+
'MB=$(git-merge-base --all G H) &&
53+
expr "$(git-name-rev "$MB")" : "[0-9a-f]* B"'
54+
55+
test_expect_success 'compute merge-base with show-branch' \
56+
'MB=$(git-show-branch --merge-base G H) &&
57+
expr "$(git-name-rev "$MB")" : "[0-9a-f]* B"'
58+
59+
test_done

0 commit comments

Comments
 (0)