summaryrefslogtreecommitdiff
path: root/revision.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2010-06-22 09:45:21 -0700
committerJunio C Hamano <gitster@pobox.com>2010-06-22 09:45:21 -0700
commita214afd25b7077fd4efebb90cf65d5403ce82dc0 (patch)
treeaf0806fed7622692edb9b2951e91ae36fe308c8e /revision.c
parent13cbf011a93d93cab524d4a2f68c259ed7e1594b (diff)
parentcb7529e13bce186f8b883c9fbb08602cd3a0795f (diff)
downloadgit-a214afd25b7077fd4efebb90cf65d5403ce82dc0.tar.gz
Merge branch 'jc/rev-list-ancestry-path'
* jc/rev-list-ancestry-path: revision: Turn off history simplification in --ancestry-path mode revision: Fix typo in --ancestry-path error message Documentation/rev-list-options.txt: Explain --ancestry-path Documentation/rev-list-options.txt: Fix missing line in example history graph revision: --ancestry-path
Diffstat (limited to 'revision.c')
-rw-r--r--revision.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/revision.c b/revision.c
index b209d493e1..7847921658 100644
--- a/revision.c
+++ b/revision.c
@@ -646,6 +646,93 @@ static int still_interesting(struct commit_list *src, unsigned long date, int sl
return slop-1;
}
+/*
+ * "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".
+ */
+static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
+{
+ struct commit_list *p;
+ struct commit_list *rlist = NULL;
+ int made_progress;
+
+ /*
+ * Reverse the list so that it will be likely that we would
+ * process parents before children.
+ */
+ for (p = list; p; p = p->next)
+ commit_list_insert(p->item, &rlist);
+
+ for (p = bottom; p; p = p->next)
+ p->item->object.flags |= TMP_MARK;
+
+ /*
+ * Mark the ones that can reach bottom commits in "list",
+ * in a bottom-up fashion.
+ */
+ do {
+ made_progress = 0;
+ for (p = rlist; p; p = p->next) {
+ struct commit *c = p->item;
+ struct commit_list *parents;
+ if (c->object.flags & (TMP_MARK | UNINTERESTING))
+ continue;
+ for (parents = c->parents;
+ parents;
+ parents = parents->next) {
+ if (!(parents->item->object.flags & TMP_MARK))
+ continue;
+ c->object.flags |= TMP_MARK;
+ made_progress = 1;
+ break;
+ }
+ }
+ } while (made_progress);
+
+ /*
+ * NEEDSWORK: decide if we want to remove parents that are
+ * not marked with TMP_MARK from commit->parents for commits
+ * in the resulting list. We may not want to do that, though.
+ */
+
+ /*
+ * The ones that are not marked with TMP_MARK are uninteresting
+ */
+ for (p = list; p; p = p->next) {
+ struct commit *c = p->item;
+ if (c->object.flags & TMP_MARK)
+ continue;
+ c->object.flags |= UNINTERESTING;
+ }
+
+ /* We are done with the TMP_MARK */
+ 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;
+ free_commit_list(rlist);
+}
+
+/*
+ * Before walking the history, keep the set of "negative" refs the
+ * caller has asked to exclude.
+ *
+ * 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)
+{
+ struct commit_list *elem, *bottom = NULL;
+ for (elem = list; elem; elem = elem->next)
+ if (elem->item->object.flags & UNINTERESTING)
+ commit_list_insert(elem->item, &bottom);
+ return bottom;
+}
+
static int limit_list(struct rev_info *revs)
{
int slop = SLOP;
@@ -653,6 +740,13 @@ static int limit_list(struct rev_info *revs)
struct commit_list *list = revs->commits;
struct commit_list *newlist = NULL;
struct commit_list **p = &newlist;
+ struct commit_list *bottom = NULL;
+
+ if (revs->ancestry_path) {
+ bottom = collect_bottom_commits(list);
+ if (!bottom)
+ die("--ancestry-path given but there are no bottom commits");
+ }
while (list) {
struct commit_list *entry = list;
@@ -694,6 +788,11 @@ static int limit_list(struct rev_info *revs)
if (revs->cherry_pick)
cherry_pick_list(newlist, revs);
+ if (bottom) {
+ limit_to_ancestry(bottom, newlist);
+ free_commit_list(bottom);
+ }
+
revs->commits = newlist;
return 0;
}
@@ -1089,6 +1188,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->min_age = approxidate(arg + 8);
} else if (!strcmp(arg, "--first-parent")) {
revs->first_parent_only = 1;
+ } else if (!strcmp(arg, "--ancestry-path")) {
+ revs->ancestry_path = 1;
+ revs->simplify_history = 0;
+ revs->limited = 1;
} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
init_reflog_walk(&revs->reflog_info);
} else if (!strcmp(arg, "--default")) {