From 60d30b02fc188da269da094df8bc67139ad6eb04 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 31 Jul 2008 22:17:13 -0700 Subject: revision.c: whitespace fix Signed-off-by: Junio C Hamano --- revision.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/revision.c b/revision.c index e75079a6e1..71f0bea899 100644 --- a/revision.c +++ b/revision.c @@ -489,7 +489,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, p->object.flags |= SEEN; insert_by_date_cached(p, list, cached_base, cache_ptr); } - if(revs->first_parent_only) + if (revs->first_parent_only) break; } return 0; -- cgit v1.2.3 From 6546b5931e5608dfdcd3e4fbf9ebc90ee0982b56 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 31 Jul 2008 01:17:41 -0700 Subject: revision traversal: show full history with merge simplification The --full-history traversal keeps all merges in addition to non-merge commits that touch paths in the given pathspec. This is useful to view both sides of a merge in a topology like this: A---M---o / / ---O---B even when A and B makes identical change to the given paths. The revision traversal without --full-history aims to come up with the simplest history to explain the final state of the tree, and one of the side branches can be pruned away. The behaviour to keep all merges however is inconvenient if neither A nor B touches the paths we are interested in. --full-history reduces the topology to: ---O---M---o in such a case, without removing M. This adds a post processing phase on top of --full-history traversal to remove needless merges from the resulting history. The idea is to compute, for each commit in the "full history" result set, the commit that should replace it in the simplified history. The commit to replace it in the final history is determined as follows: * In any case, we first figure out the replacement commits of parents of the commit we are looking at. The commit we are looking at is rewritten as if the replacement commits of its original parents are its parents. While doing so, we reduce the redundant parents from the rewritten parent list by not just removing the identical ones, but also removing a parent that is an ancestor of another parent. * After the above parent simplification, if the commit is a root commit, an UNINTERESTING commit, a merge commit, or modifies the paths we are interested in, then the replacement commit of the commit is itself. In other words, such a commit is not dropped from the final result. The first point above essentially means that the history is rewritten in the bottom up direction. We can rewrite the parent list of a commit only after we know how all of its parents are rewritten. This means that the processing needs to happen on the full history (i.e. after limit_list()). Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 10 ++- revision.c | 171 ++++++++++++++++++++++++++++++++----- revision.h | 1 + 3 files changed, 160 insertions(+), 22 deletions(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 3aa38097e6..ee6822a85d 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -193,12 +193,18 @@ endif::git-rev-list[] --full-history:: - Show also parts of history irrelevant to current state of a given - path. This turns off history simplification, which removed merges + Show also parts of history irrelevant to current state of given + paths. 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. +--simplify-merges:: + + Simplify away commits that did not change the given paths, similar + to `--full-history`, and further remove merges none of whose + parent history changes the given paths. + --no-merges:: Do not print commits with more than one parent. diff --git a/revision.c b/revision.c index 71f0bea899..662d66be7e 100644 --- a/revision.c +++ b/revision.c @@ -1045,6 +1045,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--topo-order")) { revs->lifo = 1; revs->topo_order = 1; + } else if (!strcmp(arg, "--simplify-merges")) { + revs->simplify_merges = 1; + revs->rewrite_parents = 1; + revs->simplify_history = 0; + revs->limited = 1; } else if (!strcmp(arg, "--date-order")) { revs->lifo = 0; revs->topo_order = 1; @@ -1378,6 +1383,150 @@ static void add_child(struct rev_info *revs, struct commit *parent, struct commi l->next = add_decoration(&revs->children, &parent->object, l); } +static int remove_duplicate_parents(struct commit *commit) +{ + struct commit_list **pp, *p; + int surviving_parents; + + /* Examine existing parents while marking ones we have seen... */ + pp = &commit->parents; + while ((p = *pp) != NULL) { + struct commit *parent = p->item; + if (parent->object.flags & TMP_MARK) { + *pp = p->next; + continue; + } + parent->object.flags |= TMP_MARK; + pp = &p->next; + } + /* count them while clearing the temporary mark */ + surviving_parents = 0; + for (p = commit->parents; p; p = p->next) { + p->item->object.flags &= ~TMP_MARK; + surviving_parents++; + } + return surviving_parents; +} + +static struct commit_list **simplify_one(struct commit *commit, struct commit_list **tail) +{ + struct commit_list *p; + int cnt; + + /* + * We store which commit each one simplifies to in its util field. + * Have we handled this one? + */ + if (commit->util) + return tail; + + /* + * An UNINTERESTING commit simplifies to itself, so does a + * root commit. We do not rewrite parents of such commit + * anyway. + */ + if ((commit->object.flags & UNINTERESTING) || !commit->parents) { + commit->util = commit; + return tail; + } + + /* + * Do we know what commit all of our parents should be rewritten to? + * Otherwise we are not ready to rewrite this one yet. + */ + for (cnt = 0, p = commit->parents; p; p = p->next) { + if (!p->item->util) { + tail = &commit_list_insert(p->item, tail)->next; + cnt++; + } + } + if (cnt) + return tail; + + /* + * Rewrite our list of parents. + */ + for (p = commit->parents; p; p = p->next) + p->item = p->item->util; + cnt = remove_duplicate_parents(commit); + + /* + * It is possible that we are a merge and one side branch + * does not have any commit that touches the given paths; + * in such a case, the immediate parents will be rewritten + * to different commits. + * + * o----X X: the commit we are looking at; + * / / o: a commit that touches the paths; + * ---o----' + * + * Further reduce the parents by removing redundant parents. + */ + if (1 < cnt) { + struct commit_list *h = reduce_heads(commit->parents); + cnt = commit_list_count(h); + free_commit_list(commit->parents); + commit->parents = h; + } + + /* + * A commit simplifies to itself if it is a root, if it is + * UNINTERESTING, if it touches the given paths, or if it is a + * merge and its parents simplifies to more than one commits + * (the first two cases are already handled at the beginning of + * this function). + * + * Otherwise, it simplifies to what its sole parent simplifies to. + */ + if (!cnt || + (commit->object.flags & UNINTERESTING) || + !(commit->object.flags & TREESAME) || + (1 < cnt)) + commit->util = commit; + else + commit->util = commit->parents->item->util; + return tail; +} + +static void simplify_merges(struct rev_info *revs) +{ + struct commit_list *list; + struct commit_list *yet_to_do, **tail; + + /* feed the list reversed */ + yet_to_do = NULL; + for (list = revs->commits; list; list = list->next) + commit_list_insert(list->item, &yet_to_do); + while (yet_to_do) { + list = yet_to_do; + yet_to_do = NULL; + tail = &yet_to_do; + while (list) { + struct commit *commit = list->item; + struct commit_list *next = list->next; + free(list); + list = next; + tail = simplify_one(commit, tail); + } + } + + /* clean up the result, removing the simplified ones */ + list = revs->commits; + revs->commits = NULL; + tail = &revs->commits; + while (list) { + struct commit *commit = list->item; + struct commit_list *next = list->next; + free(list); + list = next; + if (commit->util == commit) + tail = &commit_list_insert(commit, tail)->next; + } + + /* sort topologically at the end */ + sort_in_topological_order(&revs->commits, revs->lifo); +} + static void set_children(struct rev_info *revs) { struct commit_list *l; @@ -1418,6 +1567,8 @@ int prepare_revision_walk(struct rev_info *revs) return -1; if (revs->topo_order) sort_in_topological_order(&revs->commits, revs->lifo); + if (revs->simplify_merges) + simplify_merges(revs); if (revs->children.name) set_children(revs); return 0; @@ -1450,26 +1601,6 @@ static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp } } -static void remove_duplicate_parents(struct commit *commit) -{ - struct commit_list **pp, *p; - - /* Examine existing parents while marking ones we have seen... */ - pp = &commit->parents; - while ((p = *pp) != NULL) { - struct commit *parent = p->item; - if (parent->object.flags & TMP_MARK) { - *pp = p->next; - continue; - } - parent->object.flags |= TMP_MARK; - pp = &p->next; - } - /* ... and clear the temporary mark */ - for (p = commit->parents; p; p = p->next) - p->item->object.flags &= ~TMP_MARK; -} - static int rewrite_parents(struct rev_info *revs, struct commit *commit) { struct commit_list **pp = &commit->parents; diff --git a/revision.h b/revision.h index f64e8ce7ff..dfa06b5210 100644 --- a/revision.h +++ b/revision.h @@ -41,6 +41,7 @@ struct rev_info { simplify_history:1, lifo:1, topo_order:1, + simplify_merges:1, tag_objects:1, tree_objects:1, blob_objects:1, -- cgit v1.2.3 From 65347030590bcc251a9ff2ed96487a0f1b9e9fa8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 3 Aug 2008 17:47:16 -0700 Subject: Topo-sort before --simplify-merges This makes the algorithm more honest about what it is doing. We start from an already limited, topo-sorted list, and postprocess it by simplifying the irrelevant merges away. Signed-off-by: Junio C Hamano --- revision.c | 5 +-- t/t6012-rev-list-simplify.sh | 93 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) create mode 100755 t/t6012-rev-list-simplify.sh diff --git a/revision.c b/revision.c index 662d66be7e..0aaa4c10b9 100644 --- a/revision.c +++ b/revision.c @@ -1493,6 +1493,8 @@ static void simplify_merges(struct rev_info *revs) struct commit_list *list; struct commit_list *yet_to_do, **tail; + sort_in_topological_order(&revs->commits, revs->lifo); + /* feed the list reversed */ yet_to_do = NULL; for (list = revs->commits; list; list = list->next) @@ -1522,9 +1524,6 @@ static void simplify_merges(struct rev_info *revs) if (commit->util == commit) tail = &commit_list_insert(commit, tail)->next; } - - /* sort topologically at the end */ - sort_in_topological_order(&revs->commits, revs->lifo); } static void set_children(struct rev_info *revs) diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh new file mode 100755 index 0000000000..510bb9679f --- /dev/null +++ b/t/t6012-rev-list-simplify.sh @@ -0,0 +1,93 @@ +#!/bin/sh + +test_description='merge simplification' + +. ./test-lib.sh + +note () { + git tag "$1" +} + +_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' +_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" + +unnote () { + git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\)) |\1 |g" +} + +test_expect_success setup ' + echo "Hi there" >file && + git add file && + test_tick && git commit -m "Initial file" && + note A && + + git branch other-branch && + + echo "Hello" >file && + git add file && + test_tick && git commit -m "Modified file" && + note B && + + git checkout other-branch && + + echo "Hello" >file && + git add file && + test_tick && git commit -m "Modified the file identically" && + note C && + + echo "This is a stupid example" >another-file && + git add another-file && + test_tick && git commit -m "Add another file" && + note D && + + test_tick && git merge -m "merge" master && + note E && + + echo "Yet another" >elif && + git add elif && + test_tick && git commit -m "Irrelevant change" && + note F && + + git checkout master && + echo "Yet another" >elif && + git add elif && + test_tick && git commit -m "Another irrelevant change" && + note G && + + test_tick && git merge -m "merge" other-branch && + note H && + + echo "Final change" >file && + test_tick && git commit -a -m "Final change" && + note I +' + +FMT='tformat:%P %H | %s' + +check_result () { + for c in $1 + do + echo "$c" + done >expect && + shift && + param="$*" && + test_expect_success "log $param" ' + git log --pretty="$FMT" --parents $param | + unnote >actual && + sed -e "s/^.* \([^ ]*\) .*/\1/" >check Date: Tue, 12 Aug 2008 10:45:57 +0200 Subject: filter-branch: Extend test to show rewriting bug This extends the --subdirectory-filter test in t7003 to demonstrate a rewriting bug: when rewriting two refs A and B such that B is an ancestor of A, it fails to rewrite B. The underlying issue is that the rev-list invocation at git-filter-branch.sh:332 more or less boils down to git rev-list B --boundary ^A which outputs nothing because B is an ancestor of A. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- t/t7003-filter-branch.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index a0ab096c8f..4382baa27d 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -96,13 +96,17 @@ test_expect_success 'filter subdirectory only' ' test_tick && git commit -m "again not subdir" && git branch sub && - git-filter-branch -f --subdirectory-filter subdir refs/heads/sub + git branch sub-earlier HEAD~2 && + git-filter-branch -f --subdirectory-filter subdir \ + refs/heads/sub refs/heads/sub-earlier ' -test_expect_success 'subdirectory filter result looks okay' ' +test_expect_failure 'subdirectory filter result looks okay' ' test 2 = $(git rev-list sub | wc -l) && git show sub:new && - test_must_fail git show sub:subdir + test_must_fail git show sub:subdir && + git show sub-earlier:new && + test_must_fail git show sub-earlier:subdir ' test_expect_success 'more setup' ' -- cgit v1.2.3 From a0e46390d397e71182e42930b98b6b59a1a84898 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Tue, 12 Aug 2008 10:45:58 +0200 Subject: filter-branch: fix ref rewriting with --subdirectory-filter The previous ancestor discovery code failed on any refs that are (pre-rewrite) ancestors of commits marked for rewriting. This means that in a situation A -- B(topic) -- C(master) where B is dropped by --subdirectory-filter pruning, the 'topic' was not moved up to A as intended, but left unrewritten because we asked about 'git rev-list ^master topic', which does not return anything. Instead, we use the straightforward git rev-list -1 $ref -- $filter_subdir to find the right ancestor. To justify this, note that the nearest ancestor is unique: We use the output of git rev-list --parents -- $filter_subdir to rewrite commits in the first pass, before any ref rewriting. If B is a non-merge commit, the only candidate is its parent. If it is a merge, there are two cases: - All sides of the merge bring the same subdirectory contents. Then rev-list already pruned away the merge in favour for just one of its parents, so there is only one candidate. - Some merge sides, or the merge outcome, differ. Then the merge is not pruned and can be rewritten directly. So it is always safe to use rev-list -1. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- git-filter-branch.sh | 27 +++++++++++---------------- t/t7003-filter-branch.sh | 2 +- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index a324cf0596..a140337e35 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -317,24 +317,19 @@ done <../revs # In case of a subdirectory filter, it is possible that a specified head # is not in the set of rewritten commits, because it was pruned by the -# revision walker. Fix it by mapping these heads to the next rewritten -# ancestor(s), i.e. the boundaries in the set of rewritten commits. +# revision walker. Fix it by mapping these heads to the unique nearest +# ancestor that survived the pruning. -# NEEDSWORK: we should sort the unmapped refs topologically first -while read ref -do - sha1=$(git rev-parse "$ref"^0) - test -f "$workdir"/../map/$sha1 && continue - # Assign the boundarie(s) in the set of rewritten commits - # as the replacement commit(s). - # (This would look a bit nicer if --not --stdin worked.) - for p in $( (cd "$workdir"/../map; ls | sed "s/^/^/") | - git rev-list $ref --boundary --stdin | - sed -n "s/^-//p") +if test "$filter_subdir" +then + while read ref do - map $p >> "$workdir"/../map/$sha1 - done -done < "$tempdir"/heads + sha1=$(git rev-parse "$ref"^0) + test -f "$workdir"/../map/$sha1 && continue + ancestor=$(git rev-list -1 $ref -- "$filter_subdir") + test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1 + done < "$tempdir"/heads +fi # Finally update the refs diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 4382baa27d..233254f2b5 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -101,7 +101,7 @@ test_expect_success 'filter subdirectory only' ' refs/heads/sub refs/heads/sub-earlier ' -test_expect_failure 'subdirectory filter result looks okay' ' +test_expect_success 'subdirectory filter result looks okay' ' test 2 = $(git rev-list sub | wc -l) && git show sub:new && test_must_fail git show sub:subdir && -- cgit v1.2.3 From f34a9416ab9efa13a53a31cabd9061d583aaba7e Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Tue, 12 Aug 2008 10:45:59 +0200 Subject: filter-branch: use --simplify-merges Use rev-list --simplify-merges everywhere. This changes the behaviour of --subdirectory-filter in cases such as O -- A -\ \ \ \- B -- M where A and B bring the same changes to the subdirectory: It now keeps both sides of the merge. Previously, the history would have been simplified to 'O -- A'. Merges of unrelated side histories that never touch the subdirectory are still removed. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- git-filter-branch.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index a140337e35..2688254af3 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -232,11 +232,11 @@ mkdir ../map || die "Could not create map/ directory" case "$filter_subdir" in "") git rev-list --reverse --topo-order --default HEAD \ - --parents "$@" + --parents --simplify-merges "$@" ;; *) git rev-list --reverse --topo-order --default HEAD \ - --parents "$@" -- "$filter_subdir" + --parents --simplify-merges "$@" -- "$filter_subdir" esac > ../revs || die "Could not get the commits" commits=$(wc -l <../revs | tr -d " ") @@ -326,7 +326,8 @@ then do sha1=$(git rev-parse "$ref"^0) test -f "$workdir"/../map/$sha1 && continue - ancestor=$(git rev-list -1 $ref -- "$filter_subdir") + ancestor=$(git rev-list --simplify-merges -1 \ + $ref -- "$filter_subdir") test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1 done < "$tempdir"/heads fi -- cgit v1.2.3 From d266a988456fcaab9918eae39f5faf8dcb20cb26 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Tue, 12 Aug 2008 01:55:37 +0200 Subject: Documentation: rev-list-options: move --simplify-merges documentation Fits --simplify-merges documentation into the 'History Simplification' section, including example. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 48 +++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 059ae69d84..15752b92a5 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -193,12 +193,6 @@ endif::git-rev-list[] Stop when a given path disappears from the tree. ---simplify-merges:: - - Simplify away commits that did not change the given paths, similar - to `--full-history`, and further remove merges none of whose - parent history changes the given paths. - --no-merges:: Do not print commits with more than one parent. @@ -413,6 +407,48 @@ Note that without '\--full-history', this still simplifies merges: if one of the parents is TREESAME, we follow only that one, so the other sides of the merge are never walked. +Finally, there is a fourth simplification mode available: + +--simplify-merges:: + + First, build a history graph in the same way that + '\--full-history' with parent rewriting does (see above). ++ +Then simplify each commit `C` to its replacement `C'` in the final +history according to the following rules: ++ +-- +* Set `C'` to `C`. ++ +* Replace each parent `P` of `C'` with its simplification `P'`. In + the process, drop parents that are ancestors of other parents, and + remove duplicates. ++ +* If after this parent rewriting, `C'` is a root or merge commit (has + zero or >1 parents), a boundary commit, or !TREESAME, it remains. + Otherwise, it is replaced with its only parent. +-- ++ +The effect of this is best shown by way of comparing to +'\--full-history' with parent rewriting. The example turns into: ++ +----------------------------------------------------------------------- + .-A---M---N---O + / / / + I B D + \ / / + `---------' +----------------------------------------------------------------------- ++ +Note the major differences in `N` and `P` over '\--full-history': ++ +-- +* `N`'s parent list had `I` removed, because it is an ancestor of the + other parent `M`. Still, `N` remained because it is !TREESAME. ++ +* `P`'s parent list similarly had `I` removed. `P` was then + removed completely, because it had one parent and is TREESAME. +-- ifdef::git-rev-list[] Bisection Helpers -- cgit v1.2.3 From faf0156b278d1a760362cda1d294a88be7608de4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 14 Aug 2008 10:59:44 -0700 Subject: revision --simplify-merges: use decoration instead of commit->util field The users of revision walking machinery may want to use the util pointer for their own use. Use decoration to hold the data needed during merge simplification instead. Signed-off-by: Junio C Hamano --- revision.c | 49 +++++++++++++++++++++++++++++++++++++------------ revision.h | 1 + 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/revision.c b/revision.c index 0aaa4c10b9..33cb207f28 100644 --- a/revision.c +++ b/revision.c @@ -1408,16 +1408,34 @@ static int remove_duplicate_parents(struct commit *commit) return surviving_parents; } -static struct commit_list **simplify_one(struct commit *commit, struct commit_list **tail) +struct merge_simplify_state { + struct commit *simplified; +}; + +static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs, struct commit *commit) +{ + struct merge_simplify_state *st; + + st = lookup_decoration(&revs->merge_simplification, &commit->object); + if (!st) { + st = xcalloc(1, sizeof(*st)); + add_decoration(&revs->merge_simplification, &commit->object, st); + } + return st; +} + +static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail) { struct commit_list *p; + struct merge_simplify_state *st, *pst; int cnt; + st = locate_simplify_state(revs, commit); + /* - * We store which commit each one simplifies to in its util field. * Have we handled this one? */ - if (commit->util) + if (st->simplified) return tail; /* @@ -1426,7 +1444,7 @@ static struct commit_list **simplify_one(struct commit *commit, struct commit_li * anyway. */ if ((commit->object.flags & UNINTERESTING) || !commit->parents) { - commit->util = commit; + st->simplified = commit; return tail; } @@ -1435,7 +1453,8 @@ static struct commit_list **simplify_one(struct commit *commit, struct commit_li * Otherwise we are not ready to rewrite this one yet. */ for (cnt = 0, p = commit->parents; p; p = p->next) { - if (!p->item->util) { + pst = locate_simplify_state(revs, p->item); + if (!pst->simplified) { tail = &commit_list_insert(p->item, tail)->next; cnt++; } @@ -1446,8 +1465,10 @@ static struct commit_list **simplify_one(struct commit *commit, struct commit_li /* * Rewrite our list of parents. */ - for (p = commit->parents; p; p = p->next) - p->item = p->item->util; + for (p = commit->parents; p; p = p->next) { + pst = locate_simplify_state(revs, p->item); + p->item = pst->simplified; + } cnt = remove_duplicate_parents(commit); /* @@ -1482,9 +1503,11 @@ static struct commit_list **simplify_one(struct commit *commit, struct commit_li (commit->object.flags & UNINTERESTING) || !(commit->object.flags & TREESAME) || (1 < cnt)) - commit->util = commit; - else - commit->util = commit->parents->item->util; + st->simplified = commit; + else { + pst = locate_simplify_state(revs, commit->parents->item); + st->simplified = pst->simplified; + } return tail; } @@ -1508,7 +1531,7 @@ static void simplify_merges(struct rev_info *revs) struct commit_list *next = list->next; free(list); list = next; - tail = simplify_one(commit, tail); + tail = simplify_one(revs, commit, tail); } } @@ -1519,9 +1542,11 @@ static void simplify_merges(struct rev_info *revs) while (list) { struct commit *commit = list->item; struct commit_list *next = list->next; + struct merge_simplify_state *st; free(list); list = next; - if (commit->util == commit) + st = locate_simplify_state(revs, commit); + if (st->simplified == commit) tail = &commit_list_insert(commit, tail)->next; } } diff --git a/revision.h b/revision.h index dfa06b5210..765ef6c5e2 100644 --- a/revision.h +++ b/revision.h @@ -110,6 +110,7 @@ struct rev_info { struct reflog_walk_info *reflog_info; struct decoration children; + struct decoration merge_simplification; }; #define REV_TREE_SAME 0 -- cgit v1.2.3 From 53030f8d1199ef9da86e7ddc2b8a659a355e5f69 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 18 Aug 2008 00:37:34 -0700 Subject: revision --simplify-merges: do not leave commits unprocessed When we still do not know how parents of a commit simplify to, we should defer processing of the commit, not discard it. Signed-off-by: Junio C Hamano --- revision.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/revision.c b/revision.c index 33cb207f28..8cd39da2ef 100644 --- a/revision.c +++ b/revision.c @@ -1459,8 +1459,10 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c cnt++; } } - if (cnt) + if (cnt) { + tail = &commit_list_insert(commit, tail)->next; return tail; + } /* * Rewrite our list of parents. -- cgit v1.2.3 From 5eac739e05620e491555850d5b513ef60595c016 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 14 Aug 2008 13:52:36 -0700 Subject: revision --simplify-merges: make it a no-op without pathspec When we are not pruning there is no reason to run the merge simplification. Also avoid running topo-order sort twice. Signed-off-by: Junio C Hamano --- revision.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/revision.c b/revision.c index 8cd39da2ef..db2ab2b11a 100644 --- a/revision.c +++ b/revision.c @@ -1518,7 +1518,10 @@ static void simplify_merges(struct rev_info *revs) struct commit_list *list; struct commit_list *yet_to_do, **tail; - sort_in_topological_order(&revs->commits, revs->lifo); + if (!revs->topo_order) + sort_in_topological_order(&revs->commits, revs->lifo); + if (!revs->prune) + return; /* feed the list reversed */ yet_to_do = NULL; -- cgit v1.2.3