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

[PATCH] git log -p -m: Document, honor --first-parent

From
Petr Baudis <pasky@suse.cz>
Date
Feb 10, 2010, 01:11 UTC
Message-ID
<20100210011149.GR9553@machine.or.cz>
In-Reply-To
<7v1vguuf8h.fsf@alter.siamese.dyndns.org>
On Tue, Feb 09, 2010 at 04:05:50PM -0800, Junio C Hamano wrote:
Show 22 quoted lines
> Petr Baudis <pasky@suse.cz> writes:
> 
> >   I'm not sure if there is any clever switch for this, but I usually
> > just use one of
> >
> > 	git diff mergecommit^1 mergecommit
> > 	git diff mergecommit^2 mergecommit
> >
> > depending on which parent I want the diff against. If you always do your
> > merges as "on mainline, merging in a topic" without fast-forwarding,
> > diff against the first parent will be probably the right one and you can
> > simply use:
> >
> > 	git diff mergecommit^ mergecommit
> 
> Frankly, we should make "git log --first-parent -p" DTRT, I think.
> 
> The attitude towards merges we maintain officially is "all parents are
> equal", but in practice, there often are cases where --first-parent
> traversal makes a lot more sense when browsing the history (especially
> "the official" one).  The use of that option should be a clear enough sign
> that diff between the first parent and the merge result is asked for.

I have also discovered -m while digging into this, which seems to have the effect of showing the merge TWICE, each time against a different parent; this is sort-of-almost what Christian also wanted.

This seems to be undocumented and does have this effect only in log, not in show - I have absolutely no idea why from cursory code examination.

I think making just --first-parent alone imply this behavior is wrong, IMHO first-parent alone does not warrant avoiding combined-diff behavior. I'm not really sure though, so feel free to add another

	if (revs->diff && revs->follow_first_parent)
		revs->ignore_merges = 0;

test at the right place (whatever that is). At any rate, -m explicitly states the intent and the current behavior of not honoring --first-parent is IMHO a bug.

I think --first-parent documentation is still accurate with the new behavior, so I adjusted just -m documentation - also making the flag actually visible for non-diff-tree-stdin cases. Sorry that it's both conflated in a single patch, I'd rather avoid creating a patch queue out of this supposedly-5-minute hack.

--8<--

git log -p -m has a special magic behavior of showin one merge entry per parent, with an appropriate diff; this can be frequently useful when examining histories where full set of changes introduced by a merged branch is interesting, not only the conflicts.

This patch properly documents the -m switch, which has so far been mentioned only as a fairly special diff-tree flag. It also makes the code show full patch entry only for the first parent in case --first-parent is used. Thus,

	git log -p -m --first-parent

will show the history from the "main branch perspective", while also including full diff of changes introduced by other merged in branches.

Signed-off-by: Petr Baudis <pasky@suse.cz>
---
diff --git a/Documentation/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt
index 0f25ba7..8f9a241 100644
--- a/Documentation/diff-generate-patch.txt
+++ b/Documentation/diff-generate-patch.txt
@@ -56,7 +56,8 @@ combined diff format
 
 "git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or
 '--cc' option to produce 'combined diff'.  For showing a merge commit
-with "git log -p", this is the default format.
+with "git log -p", this is the default format; you can force showing
+full diff with the '-m' option.
 A 'combined diff' format looks like this:
 
 ------------
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 0e39bb6..a2a2d04 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -118,6 +118,15 @@ git log master --not --remotes=*/master::
 	Shows all commits that are in local master but not in any remote
 	repository master branches.
 
+git log -p -m --first-parent::
+
+	Shows the history including change diffs, but only from the
+	"main branch" perspective, skipping commits that come only from
+	merges, and showing full diffs of changes introduced by the merges.
+	This makes sense only when following a strict policy of merging all
+	topic branches when staying on a single integration branch and
+	making sure the merges are not fast-forwards.
+
 Discussion
 ----------
 
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 6e9baf8..d7d0dee 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -108,8 +108,8 @@ options may be given. See linkgit:git-diff-files[1] for more options.
 
 -c::
 
-	This flag changes the way a merge commit is displayed.  It shows
-	the differences from each of the parents to the merge result
+	This flag forces the default way a merge commit is displayed.  It
+	shows the differences from each of the parents to the merge result
 	simultaneously instead of showing pairwise diff between a parent
 	and the result one at a time. Furthermore, it lists only files
 	which were modified from all parents.
@@ -121,6 +121,15 @@ options may be given. See linkgit:git-diff-files[1] for more options.
 	the parents have only two variants and the merge result picks
 	one of them without modification.
 
+-m::
+
+	This flag makes the merge commits show the full diff like
+	regular commits; for each merge parent, a separate log entry
+	and diff is generated. (An exception is made if '--first-parent'
+	option has been also passed; in that case, only diff against
+	the first parent is shown, representing the changes the merge
+	brought _into_ the then-current branch.)
+
 -r::
 
 	Show recursive diffs.
diff --git a/log-tree.c b/log-tree.c
index 27afcf6..fb990a1 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -514,6 +514,14 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
 			return 0;
 		else if (opt->combine_merges)
 			return do_diff_combined(opt, commit);
+		else if (opt->first_parent_only) {
+			/* Generate merge log entry only for the first
+			 * parent, showing summary diff of the others
+			 * we merged _in_. */
+			diff_tree_sha1(parents->item->object.sha1, sha1, "", &opt->diffopt);
+			log_tree_diff_flush(opt);
+			return !opt->loginfo;
+		}
 
 		/* If we show individual diffs, show the parent info */
 		log->parent = parents->item;
Previous: Junio C HamanoNext: Junio C Hamano
Message 5 of 9 in “What should I do to display the diff of even a simple merge commit?”
  1. Christian MICHONFeb 9, 2010
  2. Christian MICHONFeb 9, 2010
  3. Petr BaudisFeb 9, 2010
  4. Junio C HamanoFeb 10, 2010
  5. git log -p -m: Document, honor --first-parentPetr Baudis, Feb 10, 2010
  6. Junio C HamanoFeb 10, 2010
  7. Petr BaudisFeb 10, 2010
  8. Junio C HamanoFeb 10, 2010
  9. Christian MICHONFeb 10, 2010

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.