Git 1.7.4.4
[gitweb.git] / revision.c
index 490b484084e875e9b6c5aea1168437bba4bb500a..7b9eaefae4ed03e994c2122453144b3c09591b9c 100644 (file)
@@ -12,6 +12,7 @@
 #include "patch-ids.h"
 #include "decorate.h"
 #include "log-tree.h"
+#include "string-list.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -443,15 +444,15 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
        commit->object.flags |= TREESAME;
 }
 
-static void insert_by_date_cached(struct commit *p, struct commit_list **head,
+static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head,
                    struct commit_list *cached_base, struct commit_list **cache)
 {
        struct commit_list *new_entry;
 
        if (cached_base && p->date < cached_base->item->date)
-               new_entry = insert_by_date(p, &cached_base->next);
+               new_entry = commit_list_insert_by_date(p, &cached_base->next);
        else
-               new_entry = insert_by_date(p, head);
+               new_entry = commit_list_insert_by_date(p, head);
 
        if (cache && (!*cache || p->date < (*cache)->item->date))
                *cache = new_entry;
@@ -493,7 +494,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
                        if (p->object.flags & SEEN)
                                continue;
                        p->object.flags |= SEEN;
-                       insert_by_date_cached(p, list, cached_base, cache_ptr);
+                       commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                return 0;
        }
@@ -520,7 +521,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
                p->object.flags |= left_flag;
                if (!(p->object.flags & SEEN)) {
                        p->object.flags |= SEEN;
-                       insert_by_date_cached(p, list, cached_base, cache_ptr);
+                       commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                if (revs->first_parent_only)
                        break;
@@ -645,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;
@@ -652,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;
@@ -693,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;
 }
@@ -720,12 +820,12 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->all_flags = flags;
 }
 
-static void handle_refs(struct rev_info *revs, unsigned flags,
-               int (*for_each)(each_ref_fn, void *))
+static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
+               int (*for_each)(const char *, each_ref_fn, void *))
 {
        struct all_refs_cb cb;
        init_all_refs_cb(&cb, revs, flags);
-       for_each(handle_one_ref, &cb);
+       for_each(submodule, handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@ -1048,6 +1148,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                               int *unkc, const char **unkv)
 {
        const char *arg = argv[0];
+       const char *optarg;
+       int argcount;
 
        /* pseudo revision arguments */
        if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
@@ -1060,34 +1162,50 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                return 1;
        }
 
-       if (!prefixcmp(arg, "--max-count=")) {
-               revs->max_count = atoi(arg + 12);
-       } else if (!prefixcmp(arg, "--skip=")) {
-               revs->skip_count = atoi(arg + 7);
+       if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
+               revs->max_count = atoi(optarg);
+               revs->no_walk = 0;
+               return argcount;
+       } else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
+               revs->skip_count = atoi(optarg);
+               return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
        /* accept -<digit>, like traditional "head" */
                revs->max_count = atoi(arg + 1);
+               revs->no_walk = 0;
        } else if (!strcmp(arg, "-n")) {
                if (argc <= 1)
                        return error("-n requires an argument");
                revs->max_count = atoi(argv[1]);
+               revs->no_walk = 0;
                return 2;
        } else if (!prefixcmp(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
-       } else if (!prefixcmp(arg, "--max-age=")) {
-               revs->max_age = atoi(arg + 10);
-       } else if (!prefixcmp(arg, "--since=")) {
-               revs->max_age = approxidate(arg + 8);
-       } else if (!prefixcmp(arg, "--after=")) {
-               revs->max_age = approxidate(arg + 8);
-       } else if (!prefixcmp(arg, "--min-age=")) {
-               revs->min_age = atoi(arg + 10);
-       } else if (!prefixcmp(arg, "--before=")) {
-               revs->min_age = approxidate(arg + 9);
-       } else if (!prefixcmp(arg, "--until=")) {
-               revs->min_age = approxidate(arg + 8);
+               revs->no_walk = 0;
+       } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
+               revs->max_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("since", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("after", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
+               revs->min_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("before", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("until", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
        } 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")) {
@@ -1145,6 +1263,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->boundary = 1;
        } else if (!strcmp(arg, "--left-right")) {
                revs->left_right = 1;
+       } else if (!strcmp(arg, "--count")) {
+               revs->count = 1;
        } else if (!strcmp(arg, "--cherry-pick")) {
                revs->cherry_pick = 1;
                revs->limited = 1;
@@ -1185,15 +1305,39 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
        } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+               /*
+                * Detached form ("--pretty X" as opposed to "--pretty=X")
+                * not allowed, since the argument is optional.
+                */
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
        } else if (!strcmp(arg, "--show-notes")) {
                revs->show_notes = 1;
                revs->show_notes_given = 1;
+       } else if (!prefixcmp(arg, "--show-notes=")) {
+               struct strbuf buf = STRBUF_INIT;
+               revs->show_notes = 1;
+               revs->show_notes_given = 1;
+               if (!revs->notes_opt.extra_notes_refs)
+                       revs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list));
+               if (!prefixcmp(arg+13, "refs/"))
+                       /* happy */;
+               else if (!prefixcmp(arg+13, "notes/"))
+                       strbuf_addstr(&buf, "refs/");
+               else
+                       strbuf_addstr(&buf, "refs/notes/");
+               strbuf_addstr(&buf, arg+13);
+               string_list_append(revs->notes_opt.extra_notes_refs,
+                                  strbuf_detach(&buf, NULL));
        } else if (!strcmp(arg, "--no-notes")) {
                revs->show_notes = 0;
                revs->show_notes_given = 1;
+       } else if (!strcmp(arg, "--standard-notes")) {
+               revs->show_notes_given = 1;
+               revs->notes_opt.suppress_default_notes = 0;
+       } else if (!strcmp(arg, "--no-standard-notes")) {
+               revs->notes_opt.suppress_default_notes = 1;
        } else if (!strcmp(arg, "--oneline")) {
                revs->verbose_header = 1;
                get_commit_format("oneline", revs);
@@ -1229,21 +1373,25 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--relative-date")) {
                revs->date_mode = DATE_RELATIVE;
                revs->date_mode_explicit = 1;
-       } else if (!strncmp(arg, "--date=", 7)) {
-               revs->date_mode = parse_date_format(arg + 7);
+       } else if ((argcount = parse_long_opt("date", argv, &optarg))) {
+               revs->date_mode = parse_date_format(optarg);
                revs->date_mode_explicit = 1;
+               return argcount;
        } else if (!strcmp(arg, "--log-size")) {
                revs->show_log_size = 1;
        }
        /*
         * Grepping the commit log
         */
-       else if (!prefixcmp(arg, "--author=")) {
-               add_header_grep(revs, GREP_HEADER_AUTHOR, arg+9);
-       } else if (!prefixcmp(arg, "--committer=")) {
-               add_header_grep(revs, GREP_HEADER_COMMITTER, arg+12);
-       } else if (!prefixcmp(arg, "--grep=")) {
-               add_message_grep(revs, arg+7);
+       else if ((argcount = parse_long_opt("author", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_AUTHOR, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("committer", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_COMMITTER, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
+               add_message_grep(revs, optarg);
+               return argcount;
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
@@ -1252,12 +1400,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
-       } else if (!prefixcmp(arg, "--encoding=")) {
-               arg += 11;
-               if (strcmp(arg, "none"))
-                       git_log_output_encoding = xstrdup(arg);
+       } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+               if (strcmp(optarg, "none"))
+                       git_log_output_encoding = xstrdup(optarg);
                else
                        git_log_output_encoding = "";
+               return argcount;
        } else if (!strcmp(arg, "--reverse")) {
                revs->reverse ^= 1;
        } else if (!strcmp(arg, "--children")) {
@@ -1287,14 +1435,14 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
        ctx->argc -= n;
 }
 
-static int for_each_bad_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/bad", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data);
 }
 
-static int for_each_good_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/good", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
 }
 
 static void append_prune_data(const char ***prune_data, const char **av)
@@ -1332,10 +1480,16 @@ static void append_prune_data(const char ***prune_data, const char **av)
  * Returns the number of arguments left that weren't recognized
  * (which are also moved to the head of the argument list)
  */
-int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
+int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
 {
        int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
        const char **prune_data = NULL;
+       const char *submodule = NULL;
+       const char *optarg;
+       int argcount;
+
+       if (opt)
+               submodule = opt->submodule;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -1360,32 +1514,33 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        int opts;
 
                        if (!strcmp(arg, "--all")) {
-                               handle_refs(revs, flags, for_each_ref);
-                               handle_refs(revs, flags, head_ref);
+                               handle_refs(submodule, revs, flags, for_each_ref_submodule);
+                               handle_refs(submodule, revs, flags, head_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
-                               handle_refs(revs, flags, for_each_branch_ref);
+                               handle_refs(submodule, revs, flags, for_each_branch_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
-                               handle_refs(revs, flags, for_each_bad_bisect_ref);
-                               handle_refs(revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
+                               handle_refs(submodule, revs, flags, for_each_bad_bisect_ref);
+                               handle_refs(submodule, revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
                                revs->bisect = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--tags")) {
-                               handle_refs(revs, flags, for_each_tag_ref);
+                               handle_refs(submodule, revs, flags, for_each_tag_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--remotes")) {
-                               handle_refs(revs, flags, for_each_remote_ref);
+                               handle_refs(submodule, revs, flags, for_each_remote_ref_submodule);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--glob=")) {
+                       if ((argcount = parse_long_opt("glob", argv + i, &optarg))) {
                                struct all_refs_cb cb;
+                               i += argcount - 1;
                                init_all_refs_cb(&cb, revs, flags);
-                               for_each_glob_ref(handle_one_ref, arg + 7, &cb);
+                               for_each_glob_ref(handle_one_ref, optarg, &cb);
                                continue;
                        }
                        if (!prefixcmp(arg, "--branches=")) {
@@ -1468,7 +1623,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                revs->prune_data = get_pathspec(revs->prefix, prune_data);
 
        if (revs->def == NULL)
-               revs->def = def;
+               revs->def = opt ? opt->def : NULL;
+       if (opt && opt->tweak)
+               opt->tweak(revs, opt);
        if (revs->show_merge)
                prepare_show_merge(revs);
        if (revs->def && !revs->pending.nr && !got_rev_arg) {
@@ -1502,11 +1659,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                if (!revs->full_diff)
                        diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
        }
-       if (revs->combine_merges) {
+       if (revs->combine_merges)
                revs->ignore_merges = 0;
-               if (revs->dense_combined_merges && !revs->diffopt.output_format)
-                       revs->diffopt.output_format = DIFF_FORMAT_PATCH;
-       }
        revs->diffopt.abbrev = revs->abbrev;
        if (diff_setup_done(&revs->diffopt) < 0)
                die("diff_setup_done failed");
@@ -1737,7 +1891,7 @@ int prepare_revision_walk(struct rev_info *revs)
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
-                               insert_by_date(commit, &revs->commits);
+                               commit_list_insert_by_date(commit, &revs->commits);
                        }
                }
                e++;
@@ -1761,7 +1915,7 @@ int prepare_revision_walk(struct rev_info *revs)
 enum rewrite_result {
        rewrite_one_ok,
        rewrite_one_noparents,
-       rewrite_one_error,
+       rewrite_one_error
 };
 
 static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
@@ -1876,8 +2030,10 @@ static struct commit *get_revision_1(struct rev_info *revs)
                revs->commits = entry->next;
                free(entry);
 
-               if (revs->reflog_info)
+               if (revs->reflog_info) {
                        fake_reflog_parent(revs->reflog_info, commit);
+                       commit->object.flags &= ~(ADDED | SEEN | SHOWN);
+               }
 
                /*
                 * If we haven't done the list limiting, we need to look at