diff options
Diffstat (limited to 'revision.c')
-rw-r--r-- | revision.c | 272 |
1 files changed, 217 insertions, 55 deletions
diff --git a/revision.c b/revision.c index 3ba6d991f6..ded881263b 100644 --- a/revision.c +++ b/revision.c @@ -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; @@ -547,6 +548,9 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) right_count++; } + if (!left_count || !right_count) + return; + left_first = left_count < right_count; init_patch_ids(&ids); if (revs->diffopt.nr_paths) { @@ -642,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; @@ -649,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; @@ -690,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; } @@ -717,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) @@ -823,6 +926,7 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->grep_filter.status_only = 1; revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list); + revs->grep_filter.header_tail = &(revs->grep_filter.header_list); revs->grep_filter.regflags = REG_NEWLINE; diff_setup(&revs->diffopt); @@ -1044,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") || @@ -1056,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")) { @@ -1141,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; @@ -1181,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); @@ -1225,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")) { @@ -1248,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")) { @@ -1283,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) @@ -1328,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; + 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; @@ -1356,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=")) { @@ -1456,16 +1615,20 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch append_prune_data(&prune_data, argv + i); break; } + else + got_rev_arg = 1; } if (prune_data) 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) { + if (revs->def && !revs->pending.nr && !got_rev_arg) { unsigned char sha1[20]; struct object *object; unsigned mode; @@ -1496,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"); @@ -1755,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) @@ -1801,7 +1961,7 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit) static int commit_match(struct commit *commit, struct rev_info *opt) { - if (!opt->grep_filter.pattern_list) + if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list) return 1; return grep_buffer(&opt->grep_filter, NULL, /* we say nothing, not even filename */ @@ -1870,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 |