Merge branch 'jc/post-simplify' into tr/filter-branch
* jc/post-simplify: Topo-sort before --simplify-merges revision traversal: show full history with merge simplification revision.c: whitespace fix
This commit is contained in:
commit
5b2eeed1ba
@ -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.
|
||||
|
172
revision.c
172
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;
|
||||
@ -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,149 @@ 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;
|
||||
|
||||
sort_in_topological_order(&revs->commits, revs->lifo);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_children(struct rev_info *revs)
|
||||
{
|
||||
struct commit_list *l;
|
||||
@ -1418,6 +1566,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 +1600,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;
|
||||
|
@ -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,
|
||||
|
93
t/t6012-rev-list-simplify.sh
Executable file
93
t/t6012-rev-list-simplify.sh
Executable file
@ -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 <actual &&
|
||||
test_cmp expect check || {
|
||||
cat actual
|
||||
false
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
check_result 'I H G F E D C B A' --full-history
|
||||
check_result 'I H E C B A' --full-history -- file
|
||||
check_result 'I H E C B A' --full-history --topo-order -- file
|
||||
check_result 'I H E C B A' --full-history --date-order -- file
|
||||
check_result 'I E C B A' --simplify-merges -- file
|
||||
check_result 'I B A' -- file
|
||||
check_result 'I B A' --topo-order -- file
|
||||
|
||||
test_done
|
Loading…
Reference in New Issue
Block a user