Skip to content

Commit f3fc5d9

Browse files
mialloj6tphil-blain
authored andcommitted
revision: implement git log --merge also for rebase/cherry-pick/revert
'git log' learned in ae3e5e1 (git log -p --merge [[--] paths...], 2006-07-03) to show commits touching conflicted files in the range HEAD...MERGE_HEAD, an addition documented in d249b45 (Document rev-list's option --merge, 2006-08-04). It can be useful to look at the commit history to understand what lead to merge conflicts also for other mergy operations besides merges, like cherry-pick, revert and rebase. For rebases and cherry-picks, an interesting range to look at is HEAD...{REBASE_HEAD,CHERRY_PICK_HEAD}, since even if all the commits included in that range are not directly part of the 3-way merge, conflicts encountered during these operations can indeed be caused by changes introduced in preceding commits on both sides of the history. For revert, as we are (most likely) reversing changes from a previous commit, an appropriate range is REVERT_HEAD..HEAD, which is equivalent to REVERT_HEAD...HEAD and to HEAD...REVERT_HEAD, if we keep HEAD and its parents on the left side of the range. As such, adjust the code in prepare_show_merge so it constructs the range HEAD...$OTHER for OTHER={MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD}. Note that we try these pseudorefs in order, so keep REBASE_HEAD last since the three other operations can be performed during a rebase. Note also that in the uncommon case where $OTHER and HEAD do not share a common ancestor, this will show the complete histories of both sides since their root commits, which is the same behaviour as currently happens in that case for HEAD and MERGE_HEAD. Adjust the documentation of this option accordingly. Co-authored-by: Johannes Sixt <[email protected]> Co-authored-by: Philippe Blain <[email protected]> Signed-off-by: Michael Lohmann <[email protected]> [jc: tweaked in j6t's precedence fix that tries REBASE_HEAD last] Signed-off-by: Junio C Hamano <[email protected]> Signed-off-by: Philippe Blain <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f476143 commit f3fc5d9

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed

Documentation/rev-list-options.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,11 @@ See also linkgit:git-reflog[1].
341341
Under `--pretty=reference`, this information will not be shown at all.
342342

343343
--merge::
344-
After a failed merge, show refs that touch files having a
345-
conflict and don't exist on all heads to merge.
344+
Show commits touching conflicted paths in the range `HEAD...<other>`,
345+
where `<other>` is the first existing pseudoref in `MERGE_HEAD`,
346+
`CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works
347+
when the index has unmerged entries. This option can be used to show
348+
relevant commits when resolving conflicts from a 3-way merge.
346349

347350
--boundary::
348351
Output excluded boundary commits. Boundary commits are

revision.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,27 +1961,42 @@ static void add_pending_commit_list(struct rev_info *revs,
19611961
}
19621962
}
19631963

1964+
static const char *lookup_other_head(struct object_id *oid)
1965+
{
1966+
int i;
1967+
static const char *const other_head[] = {
1968+
"MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "REBASE_HEAD"
1969+
};
1970+
1971+
for (i = 0; i < ARRAY_SIZE(other_head); i++)
1972+
if (!read_ref_full(other_head[i],
1973+
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
1974+
oid, NULL)) {
1975+
if (is_null_oid(oid))
1976+
die(_("%s exists but is a symbolic ref"), other_head[i]);
1977+
return other_head[i];
1978+
}
1979+
1980+
die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
1981+
}
1982+
19641983
static void prepare_show_merge(struct rev_info *revs)
19651984
{
19661985
struct commit_list *bases;
19671986
struct commit *head, *other;
19681987
struct object_id oid;
1988+
const char *other_name;
19691989
const char **prune = NULL;
19701990
int i, prune_num = 1; /* counting terminating NULL */
19711991
struct index_state *istate = revs->repo->index;
19721992

19731993
if (repo_get_oid(the_repository, "HEAD", &oid))
19741994
die("--merge without HEAD?");
19751995
head = lookup_commit_or_die(&oid, "HEAD");
1976-
if (read_ref_full("MERGE_HEAD",
1977-
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
1978-
&oid, NULL))
1979-
die("--merge without MERGE_HEAD?");
1980-
if (is_null_oid(&oid))
1981-
die(_("MERGE_HEAD exists but is a symbolic ref"));
1982-
other = lookup_commit_or_die(&oid, "MERGE_HEAD");
1996+
other_name = lookup_other_head(&oid);
1997+
other = lookup_commit_or_die(&oid, other_name);
19831998
add_pending_object(revs, &head->object, "HEAD");
1984-
add_pending_object(revs, &other->object, "MERGE_HEAD");
1999+
add_pending_object(revs, &other->object, other_name);
19852000
bases = repo_get_merge_bases(the_repository, head, other);
19862001
add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
19872002
add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);

0 commit comments

Comments
 (0)