Skip to content

Commit 8376a70

Browse files
peffgitster
authored andcommitted
branch: clean up commit flags after merge-filter walk
When we run `branch --merged`, we use prepare_revision_walk with the merge-filter marked as UNINTERESTING. Any branch tips that are marked UNINTERESTING after it returns must be ancestors of that commit. As we iterate through the list of refs to show, we check item->commit->object.flags to see whether it was marked. This interacts badly with --verbose, which will do a separate walk to find the ahead/behind information for each branch. There are two bad things that can happen: 1. The ahead/behind walk may get the wrong results, because it can see a bogus UNINTERESTING flag leftover from the merge-filter walk. 2. We may omit some branches if their tips are involved in the ahead/behind traversal of a branch shown earlier. The ahead/behind walk carefully cleans up its commit flags, meaning it may also erase the UNINTERESTING flag that we expect to check later. We can solve this by moving the merge-filter state for each ref into its "struct ref_item" as soon as we finish the merge-filter walk. That fixes (2). Then we are free to clear the commit flags we used in the walk, fixing (1). Note that we actually do away with the matches_merge_filter helper entirely here, and inline it between the revision walk and the flag-clearing. This ensures that nobody accidentally calls it at the wrong time (it is only safe to check in that instant between the setting and clearing of the global flag). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 30d45f7 commit 8376a70

File tree

2 files changed

+48
-14
lines changed

2 files changed

+48
-14
lines changed

builtin/branch.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ struct ref_item {
280280
char *dest;
281281
unsigned int kind, width;
282282
struct commit *commit;
283+
int ignore;
283284
};
284285

285286
struct ref_list {
@@ -385,6 +386,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
385386
newitem->commit = commit;
386387
newitem->width = utf8_strwidth(refname);
387388
newitem->dest = resolve_symref(orig_refname, prefix);
389+
newitem->ignore = 0;
388390
/* adjust for "remotes/" */
389391
if (newitem->kind == REF_REMOTE_BRANCH &&
390392
ref_list->kinds != REF_REMOTE_BRANCH)
@@ -484,17 +486,6 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
484486
free(ref);
485487
}
486488

487-
static int matches_merge_filter(struct commit *commit)
488-
{
489-
int is_merged;
490-
491-
if (merge_filter == NO_FILTER)
492-
return 1;
493-
494-
is_merged = !!(commit->object.flags & UNINTERESTING);
495-
return (is_merged == (merge_filter == SHOW_MERGED));
496-
}
497-
498489
static void add_verbose_info(struct strbuf *out, struct ref_item *item,
499490
int verbose, int abbrev)
500491
{
@@ -522,10 +513,9 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
522513
{
523514
char c;
524515
int color;
525-
struct commit *commit = item->commit;
526516
struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
527517

528-
if (!matches_merge_filter(commit))
518+
if (item->ignore)
529519
return;
530520

531521
switch (item->kind) {
@@ -575,7 +565,7 @@ static int calc_maxwidth(struct ref_list *refs)
575565
{
576566
int i, w = 0;
577567
for (i = 0; i < refs->index; i++) {
578-
if (!matches_merge_filter(refs->list[i].commit))
568+
if (refs->list[i].ignore)
579569
continue;
580570
if (refs->list[i].width > w)
581571
w = refs->list[i].width;
@@ -618,6 +608,7 @@ static void show_detached(struct ref_list *ref_list)
618608
item.kind = REF_LOCAL_BRANCH;
619609
item.dest = NULL;
620610
item.commit = head_commit;
611+
item.ignore = 0;
621612
if (item.width > ref_list->maxwidth)
622613
ref_list->maxwidth = item.width;
623614
print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
@@ -656,6 +647,20 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
656647

657648
if (prepare_revision_walk(&ref_list.revs))
658649
die(_("revision walk setup failed"));
650+
651+
for (i = 0; i < ref_list.index; i++) {
652+
struct ref_item *item = &ref_list.list[i];
653+
struct commit *commit = item->commit;
654+
int is_merged = !!(commit->object.flags & UNINTERESTING);
655+
item->ignore = is_merged != (merge_filter == SHOW_MERGED);
656+
}
657+
658+
for (i = 0; i < ref_list.index; i++) {
659+
struct ref_item *item = &ref_list.list[i];
660+
clear_commit_marks(item->commit, ALL_REV_FLAGS);
661+
}
662+
clear_commit_marks(filter, ALL_REV_FLAGS);
663+
659664
if (verbose)
660665
ref_list.maxwidth = calc_maxwidth(&ref_list);
661666
}

t/t3201-branch-contains.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,33 @@ test_expect_success 'implicit --list conflicts with modification options' '
130130
131131
'
132132

133+
# We want to set up a case where the walk for the tracking info
134+
# of one branch crosses the tip of another branch (and make sure
135+
# that the latter walk does not mess up our flag to see if it was
136+
# merged).
137+
#
138+
# Here "topic" tracks "master" with one extra commit, and "zzz" points to the
139+
# same tip as master The name "zzz" must come alphabetically after "topic"
140+
# as we process them in that order.
141+
test_expect_success 'branch --merged with --verbose' '
142+
git branch --track topic master &&
143+
git branch zzz topic &&
144+
git checkout topic &&
145+
test_commit foo &&
146+
git branch --merged topic >actual &&
147+
cat >expect <<-\EOF &&
148+
master
149+
* topic
150+
zzz
151+
EOF
152+
test_cmp expect actual &&
153+
git branch --verbose --merged topic >actual &&
154+
cat >expect <<-\EOF &&
155+
master c77a0a9 second on master
156+
* topic 2c939f4 [ahead 1] foo
157+
zzz c77a0a9 second on master
158+
EOF
159+
test_cmp expect actual
160+
'
161+
133162
test_done

0 commit comments

Comments
 (0)