threads / patch / 9700

patchsimplify history by default

Subject: [PATCH] simplify history by default

## tl;dr

2 messages between Aug 30, 2007 and Aug 30, 2007. Diffs are folded; open one to read it.

replies: 1people: 2as markdown or json

Michael S. Tsirkin· Aug 30, 2007, 06:25 UTC · lore

Simplify history if --no-merges is set, even if no pathspec is given.

Signed-off-by: Michael S. Tsirkin <mst@dev.mellanox.co.il>
---

Hi! If I merge in a branch with -s ours, plain git log will still show commits from this branch:

$git checkout b $git merge -s ours a $ git log --pretty=oneline 185166cad796945c52769033f16a4c1c9cd02f08 Merge branch 'a' into b 4ee339d9c905dcc39e208fd476a5ca1163d2f8b3 commit for branch b a2c872723eb585665c5e092059cc97115e63e80f commit for branch a 0b4a099bd90c2ffcd21273afd59a0ee7d182a084 initial commit

If I limit the log to a path (any path), git log simplifies the commit history:

$git log --pretty=oneline -- . 4ee339d9c905dcc39e208fd476a5ca1163d2f8b3 commit for branch b 0b4a099bd90c2ffcd21273afd59a0ee7d182a084 initial commit

Which is better IMO, because the a branch is actually ignored.
But, is this behaviour documented? I actually discovered
this trick looking at the source:
	setup_revisions does
		/* First, search for "--" */
		seen_dashdash = 0;
		for (i = 1; i < argc; i++) {
			const char *arg = argv[i];
			if (strcmp(arg, "--"))
				continue;
			argv[i] = NULL;
			argc = i;
			revs->prune_data = get_pathspec(revs->prefix, argv + i + 1);
			seen_dashdash = 1;
			break;
		}
	so prune_data seems to be set if there's a path spec; and then
		if (revs->prune_data) {
			diff_tree_setup_paths(revs->prune_data, &revs->pruning);
			/* Can't prune commits with rename following: the paths change.. */
			if (!revs->diffopt.follow_renames)
				revs->prune_fn = try_to_simplify_commit;
			if (!revs->full_diff)
				diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
		}
	so try_to_simplify_commit is only called if there is prune_data,

Further, man git-rev-list says --full-history::

	Show also parts of history irrelevant to current state of a given path.
		This turns off history simplification, which removed merges
		which didn't change anything at all at some child. It will still
		actually simplify away merges that didn't change anything at all
		into either child.

which makes it seem like the default should be to do history simplification, but what actually happens is that the default is not to do simplification unless you give a path, in which case the default is to do simplification, and --full-history changes this:

$git-rev-list HEAD 185166cad796945c52769033f16a4c1c9cd02f08 4ee339d9c905dcc39e208fd476a5ca1163d2f8b3 a2c872723eb585665c5e092059cc97115e63e80f 0b4a099bd90c2ffcd21273afd59a0ee7d182a084

So it didn't hide branch a.

Wouldn't it make sense to make the simplified form a default, or enable it if --no-merges is set, or add a flag to enable history simplification, or at least document this better?

The following patch implements the second option. Comments?

I would also really like to see, by default: $ git log --pretty=oneline 185166cad796945c52769033f16a4c1c9cd02f08 Merge branch 'a' into b 4ee339d9c905dcc39e208fd476a5ca1163d2f8b3 commit for branch b 0b4a099bd90c2ffcd21273afd59a0ee7d182a084 initial commit Opinions?

Show changes to revision.c +3 −1
diff --git a/revision.c b/revision.c
index 51fff0e..746eb9f 100644
--- a/revision.c
+++ b/revision.c
@@ -1262,7 +1262,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 			revs->prune_fn = try_to_simplify_commit;
 		if (!revs->full_diff)
 			diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
-	}
+	} else if (revs->no_merges) /* Simplification can hide merge commits */
+		revs->prune_fn = try_to_simplify_commit;
+
 	if (revs->combine_merges) {
 		revs->ignore_merges = 0;
 		if (revs->dense_combined_merges && !revs->diffopt.output_format)
-- 
MST
Junio C Hamano· Aug 30, 2007, 07:25 UTC · re: Michael S. Tsirkin · lore

Re: [PATCH] simplify history by default

"Michael S. Tsirkin" <mst@dev.mellanox.co.il> writes:
> Which is better IMO, because the a branch is actually ignored.
> But, is this behaviour documented?

Formal documentation is probably lacking. Both Linus and I have explained this "history simplification" countless times here and on #git. As a matter of fact, I think I did my latest round of explanation on #git just a few days ago.

The thing is, neither of us is very good at documention --- we tend to offer explanation on demand _in context_.

Show 12 quoted lines
> Further, man git-rev-list says
> --full-history::
>
> 	Show also parts of history irrelevant to current state of a given path.
> 		This turns off history simplification, which removed merges
> 		which didn't change anything at all at some child. It will still
> 		actually simplify away merges that didn't change anything at all
> 		into either child.
>
> which makes it seem like the default should be to do history simplification, but
> what actually happens is that the default is not to do simplification
> unless you give a path, in which case the default is to do simplification,

I would agree around the part you quoted is a good place to add descriptions we gave in the past, and it is a good idea to do so at the very technical level.

At the same time, I think section #2 of the user manual "Exploring git history" should talk about path limited, simplified history, in its "Browsing revisions" subsection, when it gives the example

	$ git log v2.5.. Makefile fs/

As the previous section has already introduced the history diagram, it would help to draw a merge graph and how the history is simplified.

Slides #116, #117 and #118 from
	http://members.cox.net/junkio/200607-ols.pdf

talk about history simplification, which may be a good starting point to steal from.

As to your patch, I am a bit reluctant to make the history simplification for the "whole tree" history traversal default, even with --no-merges. One thing we often use --no-merges is for shortlog and I do want to see commits from both branches, when I have something like this:

 ... maint ...----o----A----B---C'
                             \   \
                              \   \
 ... master ... -----o----C----M---N

where I first made a fix C on the 'master' branch, later backported it to 'maint' as C' to fix the same issue (but applicable for the context of 'maint'). The merge made at N will most likely result as if 'maint' was merged using 'ours' strategy to 'master' because at point M, the master branch is supposed to be in sync with and contain everything from 'maint', including the effect C' would bring to 'maint'. I feel somewhat uneasy to drop C' when I am preparing to write release notes for N (or later). Maybe this is just superstition, but when not using path-limiting, I am really interested in who contributed in the whole system, whether the fruits of their efforts survived at the end or not. The use case is quite different when looking at history with path limiters.

← back to recent threads