merges from the resulting history, as there are no selected
commits contributing to this merge.
---ancestry-path::
+--ancestry-path[=<commit>]::
When given a range of commits to display (e.g. 'commit1..commit2'
- or 'commit2 {caret}commit1'), only display commits that exist
- directly on the ancestry chain between the 'commit1' and
- 'commit2', i.e. commits that are both descendants of 'commit1',
- and ancestors of 'commit2'.
+ or 'commit2 {caret}commit1'), only display commits in that range
+ that are ancestors of <commit>, descendants of <commit>, or
+ <commit> itself. If no commit is specified, use 'commit1' (the
+ excluded part of the range) as <commit>. Can be passed multiple
+ times; if so, a commit is included if it is any of the commits
+ given or if it is an ancestor or descendant of one of them.
A more detailed explanation follows.
There is another simplification mode available:
---ancestry-path::
- Limit the displayed commits to those directly on the ancestry
- chain between the ``from'' and ``to'' commits in the given commit
- range. I.e. only display commits that are ancestor of the ``to''
- commit and descendants of the ``from'' commit.
+--ancestry-path[=<commit>]::
+ Limit the displayed commits to those which are an ancestor of
+ <commit>, or which are a descendant of <commit>, or are <commit>
+ itself.
+
As an example use case, consider the following commit history:
+
\
L--M
-----------------------------------------------------------------------
++
+We can also use `--ancestry-path=D` instead of `--ancestry-path` which
+means the same thing when applied to the 'D..M' range but is just more
+explicit.
++
+If we instead are interested in a given topic within this range, and all
+commits affected by that topic, we may only want to view the subset of
+`D..M` which contain that topic in their ancestry path. So, using
+`--ancestry-path=H D..M` for example would result in:
++
+-----------------------------------------------------------------------
+ E
+ \
+ G---H---I---J
+ \
+ L--M
+-----------------------------------------------------------------------
++
+Whereas `--ancestry-path=K D..M` would result in
++
+-----------------------------------------------------------------------
+ K---------------L--M
+-----------------------------------------------------------------------
Before discussing another option, `--show-pulls`, we need to
create a new example history.
struct commit_list **list, struct prio_queue *queue)
{
struct commit_list *parent = commit->parents;
- unsigned left_flag;
+ unsigned pass_flags;
if (commit->object.flags & ADDED)
return 0;
if (revs->no_walk)
return 0;
- left_flag = (commit->object.flags & SYMMETRIC_LEFT);
+ pass_flags = (commit->object.flags & (SYMMETRIC_LEFT | ANCESTRY_PATH));
for (parent = commit->parents; parent; parent = parent->next) {
struct commit *p = parent->item;
if (!*slot)
*slot = *revision_sources_at(revs->sources, commit);
}
- p->object.flags |= left_flag;
+ p->object.flags |= pass_flags;
if (!(p->object.flags & SEEN)) {
p->object.flags |= (SEEN | NOT_USER_GIVEN);
if (list)
}
/*
- * "rev-list --ancestry-path A..B" computes commits that are ancestors
- * of B but not ancestors of A but further limits the result to those
- * that are descendants of A. This takes the list of bottom commits and
- * the result of "A..B" without --ancestry-path, and limits the latter
- * further to the ones that can reach one of the commits in "bottom".
+ * "rev-list --ancestry-path=C_0 [--ancestry-path=C_1 ...] A..B"
+ * computes commits that are ancestors of B but not ancestors of A but
+ * further limits the result to those that have any of C in their
+ * ancestry path (i.e. are either ancestors of any of C, descendants
+ * of any of C, or are any of C). If --ancestry-path is specified with
+ * no commit, we use all bottom commits for C.
+ *
+ * Before this function is called, ancestors of C will have already
+ * been marked with ANCESTRY_PATH previously.
+ *
+ * This takes the list of bottom commits and the result of "A..B"
+ * without --ancestry-path, and limits the latter further to the ones
+ * that have any of C in their ancestry path. Since the ancestors of C
+ * have already been marked (a prerequisite of this function), we just
+ * need to mark the descendants, then exclude any commit that does not
+ * have any of these marks.
*/
-static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
+static void limit_to_ancestry(struct commit_list *bottoms, struct commit_list *list)
{
struct commit_list *p;
struct commit_list *rlist = NULL;
for (p = list; p; p = p->next)
commit_list_insert(p->item, &rlist);
- for (p = bottom; p; p = p->next)
+ for (p = bottoms; p; p = p->next)
p->item->object.flags |= TMP_MARK;
/*
*/
/*
- * The ones that are not marked with TMP_MARK are uninteresting
+ * The ones that are not marked with either TMP_MARK or
+ * ANCESTRY_PATH are uninteresting
*/
for (p = list; p; p = p->next) {
struct commit *c = p->item;
- if (c->object.flags & TMP_MARK)
+ if (c->object.flags & (TMP_MARK | ANCESTRY_PATH))
continue;
c->object.flags |= UNINTERESTING;
}
- /* We are done with the TMP_MARK */
+ /* We are done with TMP_MARK and ANCESTRY_PATH */
for (p = list; p; p = p->next)
- p->item->object.flags &= ~TMP_MARK;
- for (p = bottom; p; p = p->next)
- p->item->object.flags &= ~TMP_MARK;
+ p->item->object.flags &= ~(TMP_MARK | ANCESTRY_PATH);
+ for (p = bottoms; p; p = p->next)
+ p->item->object.flags &= ~(TMP_MARK | ANCESTRY_PATH);
free_commit_list(rlist);
}
/*
- * Before walking the history, keep the set of "negative" refs the
- * caller has asked to exclude.
+ * Before walking the history, add the set of "negative" refs the
+ * caller has asked to exclude to the bottom list.
*
* This is used to compute "rev-list --ancestry-path A..B", as we need
* to filter the result of "A..B" further to the ones that can actually
* reach A.
*/
-static struct commit_list *collect_bottom_commits(struct commit_list *list)
+static void collect_bottom_commits(struct commit_list *list,
+ struct commit_list **bottom)
{
- struct commit_list *elem, *bottom = NULL;
+ struct commit_list *elem;
for (elem = list; elem; elem = elem->next)
if (elem->item->object.flags & BOTTOM)
- commit_list_insert(elem->item, &bottom);
- return bottom;
+ commit_list_insert(elem->item, bottom);
}
/* Assumes either left_only or right_only is set */
struct commit_list *original_list = revs->commits;
struct commit_list *newlist = NULL;
struct commit_list **p = &newlist;
- struct commit_list *bottom = NULL;
struct commit *interesting_cache = NULL;
- if (revs->ancestry_path) {
- bottom = collect_bottom_commits(original_list);
- if (!bottom)
+ if (revs->ancestry_path_implicit_bottoms) {
+ collect_bottom_commits(original_list,
+ &revs->ancestry_path_bottoms);
+ if (!revs->ancestry_path_bottoms)
die("--ancestry-path given but there are no bottom commits");
}
if (revs->left_only || revs->right_only)
limit_left_right(newlist, revs);
- if (bottom)
- limit_to_ancestry(bottom, newlist);
- free_commit_list(bottom);
+ if (revs->ancestry_path)
+ limit_to_ancestry(revs->ancestry_path_bottoms, newlist);
/*
* Check if any commits have become TREESAME by some of their parents
const struct setup_revision_opt* opt)
{
const char *arg = argv[0];
- const char *optarg;
+ const char *optarg = NULL;
int argcount;
const unsigned hexsz = the_hash_algo->hexsz;
revs->ancestry_path = 1;
revs->simplify_history = 0;
revs->limited = 1;
+ revs->ancestry_path_implicit_bottoms = 1;
+ } else if (skip_prefix(arg, "--ancestry-path=", &optarg)) {
+ struct commit *c;
+ struct object_id oid;
+ const char *msg = _("could not get commit for ancestry-path argument %s");
+
+ revs->ancestry_path = 1;
+ revs->simplify_history = 0;
+ revs->limited = 1;
+
+ if (repo_get_oid_committish(revs->repo, optarg, &oid))
+ return error(msg, optarg);
+ get_reference(revs, optarg, &oid, ANCESTRY_PATH);
+ c = lookup_commit_reference(revs->repo, &oid);
+ if (!c)
+ return error(msg, optarg);
+ commit_list_insert(c, &revs->ancestry_path_bottoms);
} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
init_reflog_walk(&revs->reflog_info);
} else if (!strcmp(arg, "--default")) {
void release_revisions(struct rev_info *revs)
{
free_commit_list(revs->commits);
+ free_commit_list(revs->ancestry_path_bottoms);
object_array_clear(&revs->pending);
object_array_clear(&revs->boundary_commits);
release_revisions_cmdline(&revs->cmdline);