Merge branch 'jz/rev-list-exclude-first-parent-only'
"git log" and friends learned an option --exclude-first-parent-only to propagate UNINTERESTING bit down only along the first-parent chain, just like --first-parent option shows commits that lack the UNINTERESTING bit only along the first-parent chain. * jz/rev-list-exclude-first-parent-only: git-rev-list: add --exclude-first-parent-only flag
This commit is contained in:
@ -122,19 +122,27 @@ again. Equivalent forms are `--min-parents=0` (any commit has 0 or more
|
|||||||
parents) and `--max-parents=-1` (negative numbers denote no upper limit).
|
parents) and `--max-parents=-1` (negative numbers denote no upper limit).
|
||||||
|
|
||||||
--first-parent::
|
--first-parent::
|
||||||
Follow only the first parent commit upon seeing a merge
|
When finding commits to include, follow only the first
|
||||||
commit. This option can give a better overview when
|
parent commit upon seeing a merge commit. This option
|
||||||
viewing the evolution of a particular topic branch,
|
can give a better overview when viewing the evolution of
|
||||||
because merges into a topic branch tend to be only about
|
a particular topic branch, because merges into a topic
|
||||||
adjusting to updated upstream from time to time, and
|
branch tend to be only about adjusting to updated upstream
|
||||||
this option allows you to ignore the individual commits
|
from time to time, and this option allows you to ignore
|
||||||
brought in to your history by such a merge.
|
the individual commits brought in to your history by such
|
||||||
|
a merge.
|
||||||
ifdef::git-log[]
|
ifdef::git-log[]
|
||||||
+
|
+
|
||||||
This option also changes default diff format for merge commits
|
This option also changes default diff format for merge commits
|
||||||
to `first-parent`, see `--diff-merges=first-parent` for details.
|
to `first-parent`, see `--diff-merges=first-parent` for details.
|
||||||
endif::git-log[]
|
endif::git-log[]
|
||||||
|
|
||||||
|
--exclude-first-parent-only::
|
||||||
|
When finding commits to exclude (with a '{caret}'), follow only
|
||||||
|
the first parent commit upon seeing a merge commit.
|
||||||
|
This can be used to find the set of changes in a topic branch
|
||||||
|
from the point where it diverged from the remote branch, given
|
||||||
|
that arbitrary merges can be valid topic branch changes.
|
||||||
|
|
||||||
--not::
|
--not::
|
||||||
Reverses the meaning of the '{caret}' prefix (or lack thereof)
|
Reverses the meaning of the '{caret}' prefix (or lack thereof)
|
||||||
for all following revision specifiers, up to the next `--not`.
|
for all following revision specifiers, up to the next `--not`.
|
||||||
|
2
blame.c
2
blame.c
@ -2615,7 +2615,7 @@ void assign_blame(struct blame_scoreboard *sb, int opt)
|
|||||||
else {
|
else {
|
||||||
commit->object.flags |= UNINTERESTING;
|
commit->object.flags |= UNINTERESTING;
|
||||||
if (commit->object.parsed)
|
if (commit->object.parsed)
|
||||||
mark_parents_uninteresting(commit);
|
mark_parents_uninteresting(sb->revs, commit);
|
||||||
}
|
}
|
||||||
/* treat root commit as boundary */
|
/* treat root commit as boundary */
|
||||||
if (!commit->parents && !sb->show_root)
|
if (!commit->parents && !sb->show_root)
|
||||||
|
30
revision.c
30
revision.c
@ -273,7 +273,7 @@ static void commit_stack_clear(struct commit_stack *stack)
|
|||||||
stack->nr = stack->alloc = 0;
|
stack->nr = stack->alloc = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_one_parent_uninteresting(struct commit *commit,
|
static void mark_one_parent_uninteresting(struct rev_info *revs, struct commit *commit,
|
||||||
struct commit_stack *pending)
|
struct commit_stack *pending)
|
||||||
{
|
{
|
||||||
struct commit_list *l;
|
struct commit_list *l;
|
||||||
@ -290,20 +290,26 @@ static void mark_one_parent_uninteresting(struct commit *commit,
|
|||||||
* wasn't uninteresting), in which case we need
|
* wasn't uninteresting), in which case we need
|
||||||
* to mark its parents recursively too..
|
* to mark its parents recursively too..
|
||||||
*/
|
*/
|
||||||
for (l = commit->parents; l; l = l->next)
|
for (l = commit->parents; l; l = l->next) {
|
||||||
commit_stack_push(pending, l->item);
|
commit_stack_push(pending, l->item);
|
||||||
|
if (revs && revs->exclude_first_parent_only)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mark_parents_uninteresting(struct commit *commit)
|
void mark_parents_uninteresting(struct rev_info *revs, struct commit *commit)
|
||||||
{
|
{
|
||||||
struct commit_stack pending = COMMIT_STACK_INIT;
|
struct commit_stack pending = COMMIT_STACK_INIT;
|
||||||
struct commit_list *l;
|
struct commit_list *l;
|
||||||
|
|
||||||
for (l = commit->parents; l; l = l->next)
|
for (l = commit->parents; l; l = l->next) {
|
||||||
mark_one_parent_uninteresting(l->item, &pending);
|
mark_one_parent_uninteresting(revs, l->item, &pending);
|
||||||
|
if (revs && revs->exclude_first_parent_only)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
while (pending.nr > 0)
|
while (pending.nr > 0)
|
||||||
mark_one_parent_uninteresting(commit_stack_pop(&pending),
|
mark_one_parent_uninteresting(revs, commit_stack_pop(&pending),
|
||||||
&pending);
|
&pending);
|
||||||
|
|
||||||
commit_stack_clear(&pending);
|
commit_stack_clear(&pending);
|
||||||
@ -441,7 +447,7 @@ static struct commit *handle_commit(struct rev_info *revs,
|
|||||||
if (repo_parse_commit(revs->repo, commit) < 0)
|
if (repo_parse_commit(revs->repo, commit) < 0)
|
||||||
die("unable to parse commit %s", name);
|
die("unable to parse commit %s", name);
|
||||||
if (flags & UNINTERESTING) {
|
if (flags & UNINTERESTING) {
|
||||||
mark_parents_uninteresting(commit);
|
mark_parents_uninteresting(revs, commit);
|
||||||
|
|
||||||
if (!revs->topo_order || !generation_numbers_enabled(the_repository))
|
if (!revs->topo_order || !generation_numbers_enabled(the_repository))
|
||||||
revs->limited = 1;
|
revs->limited = 1;
|
||||||
@ -1124,7 +1130,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
|
|||||||
if (repo_parse_commit_gently(revs->repo, p, 1) < 0)
|
if (repo_parse_commit_gently(revs->repo, p, 1) < 0)
|
||||||
continue;
|
continue;
|
||||||
if (p->parents)
|
if (p->parents)
|
||||||
mark_parents_uninteresting(p);
|
mark_parents_uninteresting(revs, p);
|
||||||
if (p->object.flags & SEEN)
|
if (p->object.flags & SEEN)
|
||||||
continue;
|
continue;
|
||||||
p->object.flags |= (SEEN | NOT_USER_GIVEN);
|
p->object.flags |= (SEEN | NOT_USER_GIVEN);
|
||||||
@ -1132,6 +1138,8 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
|
|||||||
commit_list_insert_by_date(p, list);
|
commit_list_insert_by_date(p, list);
|
||||||
if (queue)
|
if (queue)
|
||||||
prio_queue_put(queue, p);
|
prio_queue_put(queue, p);
|
||||||
|
if (revs->exclude_first_parent_only)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1422,7 +1430,7 @@ static int limit_list(struct rev_info *revs)
|
|||||||
if (process_parents(revs, commit, &original_list, NULL) < 0)
|
if (process_parents(revs, commit, &original_list, NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (obj->flags & UNINTERESTING) {
|
if (obj->flags & UNINTERESTING) {
|
||||||
mark_parents_uninteresting(commit);
|
mark_parents_uninteresting(revs, commit);
|
||||||
slop = still_interesting(original_list, date, slop, &interesting_cache);
|
slop = still_interesting(original_list, date, slop, &interesting_cache);
|
||||||
if (slop)
|
if (slop)
|
||||||
continue;
|
continue;
|
||||||
@ -2223,6 +2231,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
|
|||||||
return argcount;
|
return argcount;
|
||||||
} else if (!strcmp(arg, "--first-parent")) {
|
} else if (!strcmp(arg, "--first-parent")) {
|
||||||
revs->first_parent_only = 1;
|
revs->first_parent_only = 1;
|
||||||
|
} else if (!strcmp(arg, "--exclude-first-parent-only")) {
|
||||||
|
revs->exclude_first_parent_only = 1;
|
||||||
} else if (!strcmp(arg, "--ancestry-path")) {
|
} else if (!strcmp(arg, "--ancestry-path")) {
|
||||||
revs->ancestry_path = 1;
|
revs->ancestry_path = 1;
|
||||||
revs->simplify_history = 0;
|
revs->simplify_history = 0;
|
||||||
@ -3345,7 +3355,7 @@ static void explore_walk_step(struct rev_info *revs)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (c->object.flags & UNINTERESTING)
|
if (c->object.flags & UNINTERESTING)
|
||||||
mark_parents_uninteresting(c);
|
mark_parents_uninteresting(revs, c);
|
||||||
|
|
||||||
for (p = c->parents; p; p = p->next)
|
for (p = c->parents; p; p = p->next)
|
||||||
test_flag_and_insert(&info->explore_queue, p->item, TOPO_WALK_EXPLORED);
|
test_flag_and_insert(&info->explore_queue, p->item, TOPO_WALK_EXPLORED);
|
||||||
|
@ -158,6 +158,7 @@ struct rev_info {
|
|||||||
bisect:1,
|
bisect:1,
|
||||||
ancestry_path:1,
|
ancestry_path:1,
|
||||||
first_parent_only:1,
|
first_parent_only:1,
|
||||||
|
exclude_first_parent_only:1,
|
||||||
line_level_traverse:1,
|
line_level_traverse:1,
|
||||||
tree_blobs_in_commit_order:1,
|
tree_blobs_in_commit_order:1,
|
||||||
|
|
||||||
@ -402,7 +403,7 @@ const char *get_revision_mark(const struct rev_info *revs,
|
|||||||
void put_revision_mark(const struct rev_info *revs,
|
void put_revision_mark(const struct rev_info *revs,
|
||||||
const struct commit *commit);
|
const struct commit *commit);
|
||||||
|
|
||||||
void mark_parents_uninteresting(struct commit *commit);
|
void mark_parents_uninteresting(struct rev_info *revs, struct commit *commit);
|
||||||
void mark_tree_uninteresting(struct repository *r, struct tree *tree);
|
void mark_tree_uninteresting(struct repository *r, struct tree *tree);
|
||||||
void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees);
|
void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees);
|
||||||
|
|
||||||
|
@ -603,7 +603,7 @@ static int mark_uninteresting(const char *refname, const struct object_id *oid,
|
|||||||
if (!commit)
|
if (!commit)
|
||||||
return 0;
|
return 0;
|
||||||
commit->object.flags |= UNINTERESTING;
|
commit->object.flags |= UNINTERESTING;
|
||||||
mark_parents_uninteresting(commit);
|
mark_parents_uninteresting(NULL, commit);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,13 +16,12 @@ unnote () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Create a test repo with interesting commit graph:
|
# Create a test repo with an interesting commit graph:
|
||||||
#
|
#
|
||||||
# A--B----------G--H--I--K--L
|
# A-----B-----G--H--I--K--L
|
||||||
# \ \ / /
|
# \ \ / /
|
||||||
# \ \ / /
|
# \ \ / /
|
||||||
# C------E---F J
|
# C--D--E--F J
|
||||||
# \_/
|
|
||||||
#
|
#
|
||||||
# The commits are laid out from left-to-right starting with
|
# The commits are laid out from left-to-right starting with
|
||||||
# the root commit A and terminating at the tip commit L.
|
# the root commit A and terminating at the tip commit L.
|
||||||
@ -142,6 +141,13 @@ check_result 'I B A' --author-date-order -- file
|
|||||||
check_result 'H' --first-parent -- another-file
|
check_result 'H' --first-parent -- another-file
|
||||||
check_result 'H' --first-parent --topo-order -- another-file
|
check_result 'H' --first-parent --topo-order -- another-file
|
||||||
|
|
||||||
|
check_result 'L K I H G B A' --first-parent L
|
||||||
|
check_result 'F E D C' --exclude-first-parent-only F ^L
|
||||||
|
check_result '' F ^L
|
||||||
|
check_result 'L K I H G J' L ^F
|
||||||
|
check_result 'L K I H G B J' --exclude-first-parent-only L ^F
|
||||||
|
check_result 'L K I H G B' --exclude-first-parent-only --first-parent L ^F
|
||||||
|
|
||||||
check_result 'E C B A' --full-history E -- lost
|
check_result 'E C B A' --full-history E -- lost
|
||||||
test_expect_success 'full history simplification without parent' '
|
test_expect_success 'full history simplification without parent' '
|
||||||
printf "%s\n" E C B A >expect &&
|
printf "%s\n" E C B A >expect &&
|
||||||
|
Reference in New Issue
Block a user