git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v6 2/2] revision: implement `git log --merge` also for rebase/cherry-pick/revert

From
Philippe Blain <levraiphilippeblain@gmail.com>
Date
Feb 28, 2024, 13:54 UTC
Message-ID
<20240228-ml-log-merge-with-cherry-pick-and-other-pseudo-heads-v6-2-8ec34c052b39@gmail.com>
In-Reply-To
<20240228-ml-log-merge-with-cherry-pick-and-other-pseudo-heads-v6-0-8ec34c052b39@gmail.com>
From: Michael Lohmann <mi.al.lohmann@gmail.com>

'git log' learned in ae3e5e1ef2 (git log -p --merge [[--] paths...], 2006-07-03) to show commits touching conflicted files in the range HEAD...MERGE_HEAD, an addition documented in d249b45547 (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 <j6t@kdbg.org>
Co-authored-by: Philippe Blain <levraiphilippeblain@gmail.com>
Signed-off-by: Michael Lohmann <mi.al.lohmann@gmail.com>
[jc: tweaked in j6t's precedence fix that tries REBASE_HEAD last]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Philippe Blain <levraiphilippeblain@gmail.com>
---
 Documentation/rev-list-options.txt |  7 +++++--
 revision.c                         | 31 +++++++++++++++++++++++--------
 2 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index a583b52c61..29e7b50bcf 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -341,8 +341,11 @@ See also linkgit:git-reflog[1].
 Under `--pretty=reference`, this information will not be shown at all.
 
 --merge::
-	After a failed merge, show refs that touch files having a
-	conflict and don't exist on all heads to merge.
+	Show commits touching conflicted paths in the range `HEAD...<other>`,
+	where `<other>` is the first existing pseudoref in `MERGE_HEAD`,
+	`CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works
+	when the index has unmerged entries. This option can be used to show
+	relevant commits when resolving conflicts from a 3-way merge.
 
 --boundary::
 	Output excluded boundary commits. Boundary commits are
diff --git a/revision.c b/revision.c
index df775f74d0..d2ebdd045a 100644
--- a/revision.c
+++ b/revision.c
@@ -1961,11 +1961,31 @@ static void add_pending_commit_list(struct rev_info *revs,
 	}
 }
 
+static const char *lookup_other_head(struct object_id *oid)
+{
+	int i;
+	static const char *const other_head[] = {
+		"MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "REBASE_HEAD"
+	};
+
+	for (i = 0; i < ARRAY_SIZE(other_head); i++)
+		if (!read_ref_full(other_head[i],
+				RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+				oid, NULL)) {
+			if (is_null_oid(oid))
+				die(_("%s exists but is a symbolic ref"), other_head[i]);
+			return other_head[i];
+		}
+
+	die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
+}
+
 static void prepare_show_merge(struct rev_info *revs)
 {
 	struct commit_list *bases;
 	struct commit *head, *other;
 	struct object_id oid;
+	const char *other_name;
 	const char **prune = NULL;
 	int i, prune_num = 1; /* counting terminating NULL */
 	struct index_state *istate = revs->repo->index;
@@ -1973,15 +1993,10 @@ static void prepare_show_merge(struct rev_info *revs)
 	if (repo_get_oid(the_repository, "HEAD", &oid))
 		die("--merge without HEAD?");
 	head = lookup_commit_or_die(&oid, "HEAD");
-	if (read_ref_full("MERGE_HEAD",
-			RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-			&oid, NULL))
-		die("--merge without MERGE_HEAD?");
-	if (is_null_oid(&oid))
-		die(_("MERGE_HEAD exists but is a symbolic ref"));
-	other = lookup_commit_or_die(&oid, "MERGE_HEAD");
+	other_name = lookup_other_head(&oid);
+	other = lookup_commit_or_die(&oid, other_name);
 	add_pending_object(revs, &head->object, "HEAD");
-	add_pending_object(revs, &other->object, "MERGE_HEAD");
+	add_pending_object(revs, &other->object, other_name);
 	bases = repo_get_merge_bases(the_repository, head, other);
 	add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
 	add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
-- 
2.39.1
Previous: Philippe BlainNext: phillip.wood123@gmail.com
Message 24 of 26 in “Implement `git log --merge` also for rebase/cherry-pick/revert”
  1. 0/2 Implement `git log --merge` also for rebase/cherry-pick/revertPhilippe Blain, Feb 10, 2024
  2. 1/2 revision: ensure MERGE_HEAD is a ref in prepare_show_mergePhilippe Blain, Feb 10, 2024
  3. 2/2 revision: implement `git log --merge` also for rebase/cherry-pick/revertPhilippe Blain, Feb 10, 2024
  4. Johannes SixtFeb 11, 2024
  5. Philippe BlainFeb 11, 2024
  6. Johannes SixtFeb 11, 2024
  7. Junio C HamanoFeb 12, 2024
  8. Phillip WoodFeb 12, 2024
  9. Philippe BlainFeb 13, 2024
  10. Phillip WoodFeb 14, 2024
  11. Jean-Noël AvilaFeb 13, 2024
  12. Philippe BlainFeb 13, 2024
  13. 0/2 Implement `git log --merge` also for rebase/cherry-pick/revertPhilippe Blain, Feb 25, 2024
  14. 1/2 revision: ensure MERGE_HEAD is a ref in prepare_show_mergePhilippe Blain, Feb 25, 2024
  15. Jean-Noël AvilaFeb 26, 2024
  16. Philippe BlainFeb 26, 2024
  17. 2/2 revision: implement `git log --merge` also for rebase/cherry-pick/revertPhilippe Blain, Feb 25, 2024
  18. Junio C HamanoFeb 26, 2024
  19. Philippe BlainFeb 26, 2024
  20. Phillip WoodFeb 27, 2024
  21. Junio C HamanoFeb 27, 2024
  22. 0/2 Implement `git log --merge` also for rebase/cherry-pick/revertPhilippe Blain, Feb 28, 2024
  23. 1/2 revision: ensure MERGE_HEAD is a ref in prepare_show_mergePhilippe Blain, Feb 28, 2024
  24. 2/2 revision: implement `git log --merge` also for rebase/cherry-pick/revertPhilippe Blain, Feb 28, 2024
  25. phillip.wood123@gmail.comFeb 28, 2024
  26. Philippe BlainMar 2, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.