Anonymous committed 6631c73

revision.c: --full-history fix.

With history simplification, we still show merges that are required
to make the history _complete_, i.e. say that you had:

/ \
c d
| |

and neither "a" nor "b" actually changed the file, but both "c" and "d"
did: in this case we have to leave "b" around just because otherwise there
would be no way to show the _relationship_, even if "b" itself doesn't
actually change the tree in any way what-so-ever.

It would make sense to make that further simplification if the
"--parents" flag wasn't present. In that case the user is
literally asking for a list of commits and is not interested in
the relationship between them.

This patch also fixes a real bug. Without this patch, the
"--parents --full-history" combination (which you'd get if you
do something like

gitk --full-history Makefile

or similar) will actually _drop_ merges where all children are identical.
That's wrong in the --full-history case, because it means that the graph
ends up missing lots of entries.

In the process, this also should make

git-rev-list --full-history Makefile

give just the _true_ list of all commits that changed Makefile (and
properly ignore merges that were identical in one parent), because now
we're not asking for "--parent", so we don't need the unnecessary merge
commits to keep the history together.

Signed-off-by: Linus Torvalds <>
Signed-off-by: Junio C Hamano <>

  • Participants
  • Parent commits ac3bc6c

Comments (0)

Files changed (1)

 static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
 	struct commit_list **pp, *parent;
-	int tree_changed = 0;
+	int tree_changed = 0, tree_same = 0;
 	if (!commit->tree)
 		switch (rev_compare_tree(revs, p->tree, commit->tree)) {
 		case REV_TREE_SAME:
+			tree_same = 1;
 			if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
 				/* Even if a merge with an uninteresting
 				 * side branch brought the entire change
 		die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
-	if (tree_changed)
+	if (tree_changed && !tree_same)
 		commit->object.flags |= TREECHANGE;
 		struct commit *p = *pp;
 		if (!revs->limited)
 			add_parents_to_list(revs, p, &revs->commits);
+		if (p->parents && p->parents->next)
+			return 0;
 		if (p->object.flags & (TREECHANGE | UNINTERESTING))
 			return 0;
 		if (!p->parents)
 		    commit->parents && commit->parents->next)
 		if (revs->prune_fn && revs->dense) {
-			if (!(commit->object.flags & TREECHANGE))
-				continue;
+			/* Commit without changes? */
+			if (!(commit->object.flags & TREECHANGE)) {
+				/* drop merges unless we want parenthood */
+				if (!revs->parents)
+					continue;
+				/* non-merge - always ignore it */
+				if (commit->parents && !commit->parents->next)
+					continue;
+			}
 			if (revs->parents)
 				rewrite_parents(revs, commit);