diff options
Diffstat (limited to 'revision.c')
-rw-r--r-- | revision.c | 399 |
1 files changed, 253 insertions, 146 deletions
diff --git a/revision.c b/revision.c index 7b9eaefae4..c46cfaa3e4 100644 --- a/revision.c +++ b/revision.c @@ -133,6 +133,8 @@ void mark_parents_uninteresting(struct commit *commit) static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode) { + if (!obj) + return; if (revs->no_walk && (obj->flags & UNINTERESTING)) revs->no_walk = 0; if (revs->reflog_info && obj->type == OBJ_COMMIT) { @@ -174,8 +176,11 @@ static struct object *get_reference(struct rev_info *revs, const char *name, con struct object *object; object = parse_object(sha1); - if (!object) + if (!object) { + if (revs->ignore_missing) + return object; die("bad object %s", name); + } object->flags |= flags; return object; } @@ -323,7 +328,7 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct * tagged commit by specifying both --simplify-by-decoration * and pathspec. */ - if (!revs->prune_data) + if (!revs->prune_data.nr) return REV_TREE_SAME; } @@ -535,6 +540,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) int left_count = 0, right_count = 0; int left_first; struct patch_ids ids; + unsigned cherry_flag; /* First count the commits on the left and on the right */ for (p = list; p; p = p->next) { @@ -553,11 +559,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) left_first = left_count < right_count; init_patch_ids(&ids); - if (revs->diffopt.nr_paths) { - ids.diffopts.nr_paths = revs->diffopt.nr_paths; - ids.diffopts.paths = revs->diffopt.paths; - ids.diffopts.pathlens = revs->diffopt.pathlens; - } + ids.diffopts.pathspec = revs->diffopt.pathspec; /* Compute patch-ids for one side */ for (p = list; p; p = p->next) { @@ -576,6 +578,9 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) commit->util = add_commit_patch_id(commit, &ids); } + /* either cherry_mark or cherry_pick are true */ + cherry_flag = revs->cherry_mark ? PATCHSAME : SHOWN; + /* Check the other side */ for (p = list; p; p = p->next) { struct commit *commit = p->item; @@ -598,7 +603,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) if (!id) continue; id->seen = 1; - commit->object.flags |= SHOWN; + commit->object.flags |= cherry_flag; } /* Now check the original side for seen ones */ @@ -610,7 +615,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) if (!ent) continue; if (ent->seen) - commit->object.flags |= SHOWN; + commit->object.flags |= cherry_flag; commit->util = NULL; } @@ -733,6 +738,23 @@ static struct commit_list *collect_bottom_commits(struct commit_list *list) return bottom; } +/* Assumes either left_only or right_only is set */ +static void limit_left_right(struct commit_list *list, struct rev_info *revs) +{ + struct commit_list *p; + + for (p = list; p; p = p->next) { + struct commit *commit = p->item; + + if (revs->right_only) { + if (commit->object.flags & SYMMETRIC_LEFT) + commit->object.flags |= SHOWN; + } else /* revs->left_only is set */ + if (!(commit->object.flags & SYMMETRIC_LEFT)) + commit->object.flags |= SHOWN; + } +} + static int limit_list(struct rev_info *revs) { int slop = SLOP; @@ -785,9 +807,12 @@ static int limit_list(struct rev_info *revs) show(revs, newlist); show_early_output = NULL; } - if (revs->cherry_pick) + if (revs->cherry_pick || revs->cherry_mark) cherry_pick_list(newlist, revs); + if (revs->left_only || revs->right_only) + limit_left_right(newlist, revs); + if (bottom) { limit_to_ancestry(bottom, newlist); free_commit_list(bottom); @@ -886,6 +911,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags) return 0; while (1) { it = get_reference(revs, arg, sha1, 0); + if (!it && revs->ignore_missing) + return 0; if (it->type != OBJ_TAG) break; if (!((struct tag*)it)->tagged) @@ -921,6 +948,7 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->min_age = -1; revs->skip_count = -1; revs->max_count = -1; + revs->max_parents = -1; revs->commit_format = CMIT_FMT_DEFAULT; @@ -934,6 +962,8 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->diffopt.prefix = prefix; revs->diffopt.prefix_length = strlen(prefix); } + + revs->notes_opt.use_default_notes = -1; } static void add_pending_commit_list(struct rev_info *revs, @@ -973,7 +1003,7 @@ static void prepare_show_merge(struct rev_info *revs) struct cache_entry *ce = active_cache[i]; if (!ce_stage(ce)) continue; - if (ce_path_match(ce, revs->prune_data)) { + if (ce_path_match(ce, &revs->prune_data)) { prune_num++; prune = xrealloc(prune, sizeof(*prune) * prune_num); prune[prune_num-2] = ce->name; @@ -983,7 +1013,8 @@ static void prepare_show_merge(struct rev_info *revs) ce_same_name(ce, active_cache[i+1])) i++; } - revs->prune_data = prune; + free_pathspec(&revs->prune_data); + init_pathspec(&revs->prune_data, prune); revs->limited = 1; } @@ -1020,6 +1051,8 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, a = lookup_commit_reference(from_sha1); b = lookup_commit_reference(sha1); if (!a || !b) { + if (revs->ignore_missing) + return 0; die(symmetric ? "Invalid symmetric difference expression %s...%s" : "Invalid revision range %s..%s", @@ -1066,7 +1099,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, arg++; } if (get_sha1_with_mode(arg, sha1, &mode)) - return -1; + return revs->ignore_missing ? 0 : -1; if (!cant_be_filename) verify_non_filename(revs->prefix, arg); object = get_reference(revs, arg, sha1, flags ^ local_flags); @@ -1074,35 +1107,34 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, return 0; } -static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb, const char ***prune_data) -{ - const char **prune = *prune_data; - int prune_nr; - int prune_alloc; +struct cmdline_pathspec { + int alloc; + int nr; + const char **path; +}; - /* count existing ones */ - if (!prune) - prune_nr = 0; - else - for (prune_nr = 0; prune[prune_nr]; prune_nr++) - ; - prune_alloc = prune_nr; /* not really, but we do not know */ +static void append_prune_data(struct cmdline_pathspec *prune, const char **av) +{ + while (*av) { + ALLOC_GROW(prune->path, prune->nr+1, prune->alloc); + prune->path[prune->nr++] = *(av++); + } +} +static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb, + struct cmdline_pathspec *prune) +{ while (strbuf_getwholeline(sb, stdin, '\n') != EOF) { int len = sb->len; if (len && sb->buf[len - 1] == '\n') sb->buf[--len] = '\0'; - ALLOC_GROW(prune, prune_nr+1, prune_alloc); - prune[prune_nr++] = xstrdup(sb->buf); + ALLOC_GROW(prune->path, prune->nr+1, prune->alloc); + prune->path[prune->nr++] = xstrdup(sb->buf); } - if (prune) { - ALLOC_GROW(prune, prune_nr+1, prune_alloc); - prune[prune_nr] = NULL; - } - *prune_data = prune; } -static void read_revisions_from_stdin(struct rev_info *revs, const char ***prune) +static void read_revisions_from_stdin(struct rev_info *revs, + struct cmdline_pathspec *prune) { struct strbuf sb; int seen_dashdash = 0; @@ -1156,7 +1188,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") || !strcmp(arg, "--reflog") || !strcmp(arg, "--not") || !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") || - !strcmp(arg, "--bisect")) + !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") || + !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") || + !prefixcmp(arg, "--remotes=")) { unkv[(*unkc)++] = arg; return 1; @@ -1256,16 +1290,47 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--remove-empty")) { revs->remove_empty_trees = 1; } else if (!strcmp(arg, "--merges")) { - revs->merges_only = 1; + revs->min_parents = 2; } else if (!strcmp(arg, "--no-merges")) { - revs->no_merges = 1; + revs->max_parents = 1; + } else if (!prefixcmp(arg, "--min-parents=")) { + revs->min_parents = atoi(arg+14); + } else if (!prefixcmp(arg, "--no-min-parents")) { + revs->min_parents = 0; + } else if (!prefixcmp(arg, "--max-parents=")) { + revs->max_parents = atoi(arg+14); + } else if (!prefixcmp(arg, "--no-max-parents")) { + revs->max_parents = -1; } else if (!strcmp(arg, "--boundary")) { revs->boundary = 1; } else if (!strcmp(arg, "--left-right")) { revs->left_right = 1; + } else if (!strcmp(arg, "--left-only")) { + if (revs->right_only) + die("--left-only is incompatible with --right-only" + " or --cherry"); + revs->left_only = 1; + } else if (!strcmp(arg, "--right-only")) { + if (revs->left_only) + die("--right-only is incompatible with --left-only"); + revs->right_only = 1; + } else if (!strcmp(arg, "--cherry")) { + if (revs->left_only) + die("--cherry is incompatible with --left-only"); + revs->cherry_mark = 1; + revs->right_only = 1; + revs->max_parents = 1; + revs->limited = 1; } else if (!strcmp(arg, "--count")) { revs->count = 1; + } else if (!strcmp(arg, "--cherry-mark")) { + if (revs->cherry_pick) + die("--cherry-mark is incompatible with --cherry-pick"); + revs->cherry_mark = 1; + revs->limited = 1; /* needs limit_list() */ } else if (!strcmp(arg, "--cherry-pick")) { + if (revs->cherry_mark) + die("--cherry-pick is incompatible with --cherry-mark"); revs->cherry_pick = 1; revs->limited = 1; } else if (!strcmp(arg, "--objects")) { @@ -1312,32 +1377,39 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->verbose_header = 1; revs->pretty_given = 1; get_commit_format(arg+9, revs); - } else if (!strcmp(arg, "--show-notes")) { + } else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) { revs->show_notes = 1; revs->show_notes_given = 1; - } else if (!prefixcmp(arg, "--show-notes=")) { + revs->notes_opt.use_default_notes = 1; + } else if (!prefixcmp(arg, "--show-notes=") || + !prefixcmp(arg, "--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/"); + if (!prefixcmp(arg, "--show-notes")) { + if (revs->notes_opt.use_default_notes < 0) + revs->notes_opt.use_default_notes = 1; + strbuf_addstr(&buf, arg+13); + } else - strbuf_addstr(&buf, "refs/notes/"); - strbuf_addstr(&buf, arg+13); - string_list_append(revs->notes_opt.extra_notes_refs, + strbuf_addstr(&buf, arg+8); + expand_notes_ref(&buf); + 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; + revs->notes_opt.use_default_notes = -1; + /* we have been strdup'ing ourselves, so trick + * string_list into free()ing strings */ + revs->notes_opt.extra_notes_refs.strdup_strings = 1; + string_list_clear(&revs->notes_opt.extra_notes_refs, 0); + revs->notes_opt.extra_notes_refs.strdup_strings = 0; } else if (!strcmp(arg, "--standard-notes")) { revs->show_notes_given = 1; - revs->notes_opt.suppress_default_notes = 0; + revs->notes_opt.use_default_notes = 1; } else if (!strcmp(arg, "--no-standard-notes")) { - revs->notes_opt.suppress_default_notes = 1; + revs->notes_opt.use_default_notes = 0; } else if (!strcmp(arg, "--oneline")) { revs->verbose_header = 1; get_commit_format("oneline", revs); @@ -1365,6 +1437,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->abbrev = 40; } else if (!strcmp(arg, "--abbrev-commit")) { revs->abbrev_commit = 1; + revs->abbrev_commit_given = 1; + } else if (!strcmp(arg, "--no-abbrev-commit")) { + revs->abbrev_commit = 0; } else if (!strcmp(arg, "--full-diff")) { revs->diff = 1; revs->full_diff = 1; @@ -1411,6 +1486,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--children")) { revs->children.name = "children"; revs->limited = 1; + } else if (!strcmp(arg, "--ignore-missing")) { + revs->ignore_missing = 1; } else { int opts = diff_opt_parse(&revs->diffopt, argv, argc); if (!opts) @@ -1445,32 +1522,67 @@ static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void 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) +static int handle_revision_pseudo_opt(const char *submodule, + struct rev_info *revs, + int argc, const char **argv, int *flags) { - const char **prune = *prune_data; - int prune_nr; - int prune_alloc; + const char *arg = argv[0]; + const char *optarg; + int argcount; - if (!prune) { - *prune_data = av; - return; + /* + * NOTE! + * + * Commands like "git shortlog" will not accept the options below + * unless parse_revision_opt queues them (as opposed to erroring + * out). + * + * When implementing your new pseudo-option, remember to + * register it in the list at the top of handle_revision_opt. + */ + if (!strcmp(arg, "--all")) { + handle_refs(submodule, revs, *flags, for_each_ref_submodule); + handle_refs(submodule, revs, *flags, head_ref_submodule); + } else if (!strcmp(arg, "--branches")) { + handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule); + } else if (!strcmp(arg, "--bisect")) { + handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref); + handle_refs(submodule, revs, *flags ^ UNINTERESTING, for_each_good_bisect_ref); + revs->bisect = 1; + } else if (!strcmp(arg, "--tags")) { + handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule); + } else if (!strcmp(arg, "--remotes")) { + handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule); + } else if ((argcount = parse_long_opt("glob", argv, &optarg))) { + struct all_refs_cb cb; + init_all_refs_cb(&cb, revs, *flags); + for_each_glob_ref(handle_one_ref, optarg, &cb); + return argcount; + } else if (!prefixcmp(arg, "--branches=")) { + struct all_refs_cb cb; + init_all_refs_cb(&cb, revs, *flags); + for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb); + } else if (!prefixcmp(arg, "--tags=")) { + struct all_refs_cb cb; + init_all_refs_cb(&cb, revs, *flags); + for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb); + } else if (!prefixcmp(arg, "--remotes=")) { + struct all_refs_cb cb; + init_all_refs_cb(&cb, revs, *flags); + for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb); + } else if (!strcmp(arg, "--reflog")) { + handle_reflog(revs, *flags); + } else if (!strcmp(arg, "--not")) { + *flags ^= UNINTERESTING; + } else if (!strcmp(arg, "--no-walk")) { + revs->no_walk = 1; + } else if (!strcmp(arg, "--do-walk")) { + revs->no_walk = 0; + } else { + return 0; } - /* count existing ones */ - for (prune_nr = 0; prune[prune_nr]; prune_nr++) - ; - prune_alloc = prune_nr; /* not really, but we do not know */ - - while (*av) { - ALLOC_GROW(prune, prune_nr+1, prune_alloc); - prune[prune_nr++] = *av; - av++; - } - if (prune) { - ALLOC_GROW(prune, prune_nr+1, prune_alloc); - prune[prune_nr] = NULL; - } - *prune_data = prune; + return 1; } /* @@ -1483,11 +1595,10 @@ static void append_prune_data(const char ***prune_data, const char **av) 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; + struct cmdline_pathspec prune_data; const char *submodule = NULL; - const char *optarg; - int argcount; + memset(&prune_data, 0, sizeof(prune_data)); if (opt) submodule = opt->submodule; @@ -1500,7 +1611,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s argv[i] = NULL; argc = i; if (argv[i + 1]) - prune_data = argv + i + 1; + append_prune_data(&prune_data, argv + i + 1); seen_dashdash = 1; break; } @@ -1513,70 +1624,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (*arg == '-') { int opts; - if (!strcmp(arg, "--all")) { - handle_refs(submodule, revs, flags, for_each_ref_submodule); - handle_refs(submodule, revs, flags, head_ref_submodule); - continue; - } - if (!strcmp(arg, "--branches")) { - handle_refs(submodule, revs, flags, for_each_branch_ref_submodule); - continue; - } - if (!strcmp(arg, "--bisect")) { - 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(submodule, revs, flags, for_each_tag_ref_submodule); - continue; - } - if (!strcmp(arg, "--remotes")) { - handle_refs(submodule, revs, flags, for_each_remote_ref_submodule); - continue; - } - 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, optarg, &cb); - continue; - } - if (!prefixcmp(arg, "--branches=")) { - struct all_refs_cb cb; - init_all_refs_cb(&cb, revs, flags); - for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb); - continue; - } - if (!prefixcmp(arg, "--tags=")) { - struct all_refs_cb cb; - init_all_refs_cb(&cb, revs, flags); - for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb); - continue; - } - if (!prefixcmp(arg, "--remotes=")) { - struct all_refs_cb cb; - init_all_refs_cb(&cb, revs, flags); - for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb); - continue; - } - if (!strcmp(arg, "--reflog")) { - handle_reflog(revs, flags); - continue; - } - if (!strcmp(arg, "--not")) { - flags ^= UNINTERESTING; - continue; - } - if (!strcmp(arg, "--no-walk")) { - revs->no_walk = 1; - continue; - } - if (!strcmp(arg, "--do-walk")) { - revs->no_walk = 0; + opts = handle_revision_pseudo_opt(submodule, + revs, argc - i, argv + i, + &flags); + if (opts > 0) { + i += opts - 1; continue; } + if (!strcmp(arg, "--stdin")) { if (revs->disable_stdin) { argv[left++] = arg; @@ -1619,8 +1674,26 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s got_rev_arg = 1; } - if (prune_data) - revs->prune_data = get_pathspec(revs->prefix, prune_data); + if (prune_data.nr) { + /* + * If we need to introduce the magic "a lone ':' means no + * pathspec whatsoever", here is the place to do so. + * + * if (prune_data.nr == 1 && !strcmp(prune_data[0], ":")) { + * prune_data.nr = 0; + * prune_data.alloc = 0; + * free(prune_data.path); + * prune_data.path = NULL; + * } else { + * terminate prune_data.alloc with NULL and + * call init_pathspec() to set revs->prune_data here. + * } + */ + ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc); + prune_data.path[prune_data.nr++] = NULL; + init_pathspec(&revs->prune_data, + get_pathspec(revs->prefix, prune_data.path)); + } if (revs->def == NULL) revs->def = opt ? opt->def : NULL; @@ -1651,13 +1724,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (revs->topo_order) revs->limited = 1; - if (revs->prune_data) { - diff_tree_setup_paths(revs->prune_data, &revs->pruning); + if (revs->prune_data.nr) { + diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning); /* Can't prune commits with rename following: the paths change.. */ if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) revs->prune = 1; if (!revs->full_diff) - diff_tree_setup_paths(revs->prune_data, &revs->diffopt); + diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt); } if (revs->combine_merges) revs->ignore_merges = 0; @@ -1985,10 +2058,15 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi return commit_ignore; if (revs->min_age != -1 && (commit->date > revs->min_age)) return commit_ignore; - if (revs->no_merges && commit->parents && commit->parents->next) - return commit_ignore; - if (revs->merges_only && !(commit->parents && commit->parents->next)) - return commit_ignore; + if (revs->min_parents || (revs->max_parents >= 0)) { + int n = 0; + struct commit_list *p; + for (p = commit->parents; p; p = p->next) + n++; + if ((n < revs->min_parents) || + ((revs->max_parents >= 0) && (n > revs->max_parents))) + return commit_ignore; + } if (!commit_match(commit, revs)) return commit_ignore; if (revs->prune && revs->dense) { @@ -2235,3 +2313,32 @@ struct commit *get_revision(struct rev_info *revs) graph_update(revs->graph, c); return c; } + +char *get_revision_mark(const struct rev_info *revs, const struct commit *commit) +{ + if (commit->object.flags & BOUNDARY) + return "-"; + else if (commit->object.flags & UNINTERESTING) + return "^"; + else if (commit->object.flags & PATCHSAME) + return "="; + else if (!revs || revs->left_right) { + if (commit->object.flags & SYMMETRIC_LEFT) + return "<"; + else + return ">"; + } else if (revs->graph) + return "*"; + else if (revs->cherry_mark) + return "+"; + return ""; +} + +void put_revision_mark(const struct rev_info *revs, const struct commit *commit) +{ + char *mark = get_revision_mark(revs, commit); + if (!strlen(mark)) + return; + fputs(mark, stdout); + putchar(' '); +} |