diff options
Diffstat (limited to 'revision.c')
-rw-r--r-- | revision.c | 300 |
1 files changed, 238 insertions, 62 deletions
diff --git a/revision.c b/revision.c index 935e7a7ba4..7f7a8ab7cb 100644 --- a/revision.c +++ b/revision.c @@ -13,6 +13,7 @@ #include "decorate.h" #include "log-tree.h" #include "string-list.h" +#include "mailmap.h" volatile show_early_output_fn_t show_early_output; @@ -345,6 +346,7 @@ static int tree_difference = REV_TREE_SAME; static void file_add_remove(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, + int sha1_valid, const char *fullpath, unsigned dirty_submodule) { int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD; @@ -358,6 +360,7 @@ static void file_change(struct diff_options *options, unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, + int old_sha1_valid, int new_sha1_valid, const char *fullpath, unsigned old_dirty_submodule, unsigned new_dirty_submodule) { @@ -706,7 +709,7 @@ static int still_interesting(struct commit_list *src, unsigned long date, int sl * Does the destination list contain entries with a date * before the source list? Definitely _not_ done. */ - if (date < src->item->date) + if (date <= src->item->date) return SLOP; /* @@ -912,6 +915,19 @@ static void add_rev_cmdline(struct rev_info *revs, info->nr++; } +static void add_rev_cmdline_list(struct rev_info *revs, + struct commit_list *commit_list, + int whence, + unsigned flags) +{ + while (commit_list) { + struct object *object = &commit_list->item->object; + add_rev_cmdline(revs, object, sha1_to_hex(object->sha1), + whence, flags); + commit_list = commit_list->next; + } +} + struct all_refs_cb { int all_flags; int warned_bad_reflog; @@ -1000,7 +1016,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags) flags ^= UNINTERESTING; arg++; } - if (get_sha1(arg, sha1)) + if (get_sha1_committish(arg, sha1)) return 0; while (1) { it = get_reference(revs, arg, sha1, 0); @@ -1046,9 +1062,9 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->commit_format = CMIT_FMT_DEFAULT; + init_grep_defaults(); + grep_init(&revs->grep_filter, 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); @@ -1089,6 +1105,7 @@ static void prepare_show_merge(struct rev_info *revs) add_pending_object(revs, &head->object, "HEAD"); add_pending_object(revs, &other->object, "MERGE_HEAD"); bases = get_merge_bases(head, other, 1); + add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING); add_pending_commit_list(revs, bases, UNINTERESTING); free_commit_list(bases); head->object.flags |= SYMMETRIC_LEFT; @@ -1114,16 +1131,16 @@ static void prepare_show_merge(struct rev_info *revs) revs->limited = 1; } -int handle_revision_arg(const char *arg_, struct rev_info *revs, - int flags, - int cant_be_filename) +int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt) { - unsigned mode; + struct object_context oc; char *dotdot; struct object *object; unsigned char sha1[20]; int local_flags; const char *arg = arg_; + int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME; + unsigned get_sha1_flags = 0; dotdot = strstr(arg, ".."); if (dotdot) { @@ -1132,17 +1149,29 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, const char *this = arg; int symmetric = *next == '.'; unsigned int flags_exclude = flags ^ UNINTERESTING; + static const char head_by_default[] = "HEAD"; unsigned int a_flags; *dotdot = 0; next += symmetric; if (!*next) - next = "HEAD"; + next = head_by_default; if (dotdot == arg) - this = "HEAD"; - if (!get_sha1(this, from_sha1) && - !get_sha1(next, sha1)) { + this = head_by_default; + if (this == head_by_default && next == head_by_default && + !symmetric) { + /* + * Just ".."? That is not a range but the + * pathspec for the parent directory. + */ + if (!cant_be_filename) { + *dotdot = '.'; + return -1; + } + } + if (!get_sha1_committish(this, from_sha1) && + !get_sha1_committish(next, sha1)) { struct commit *a, *b; struct commit_list *exclude; @@ -1164,6 +1193,9 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, if (symmetric) { exclude = get_merge_bases(a, b, 1); + add_rev_cmdline_list(revs, exclude, + REV_CMD_MERGE_BASE, + flags_exclude); add_pending_commit_list(revs, exclude, flags_exclude); free_commit_list(exclude); @@ -1201,13 +1233,17 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, local_flags = UNINTERESTING; arg++; } - if (get_sha1_with_mode(arg, sha1, &mode)) + + if (revarg_opt & REVARG_COMMITTISH) + get_sha1_flags = GET_SHA1_COMMITTISH; + + if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc)) 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); add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags); - add_pending_object_with_mode(revs, object, arg, mode); + add_pending_object_with_mode(revs, object, arg, oc.mode); return 0; } @@ -1257,7 +1293,8 @@ static void read_revisions_from_stdin(struct rev_info *revs, } die("options not supported in --stdin mode"); } - if (handle_revision_arg(sb.buf, revs, 0, 1)) + if (handle_revision_arg(xstrdup(sb.buf), revs, 0, + REVARG_CANNOT_BE_FILENAME)) die("bad revision '%s'", sb.buf); } if (seen_dashdash) @@ -1294,7 +1331,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") || !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") || !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") || - !prefixcmp(arg, "--remotes=")) + !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk=")) { unkv[(*unkc)++] = arg; return 1; @@ -1358,11 +1395,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->topo_order = 1; } else if (!strcmp(arg, "--simplify-merges")) { revs->simplify_merges = 1; + revs->topo_order = 1; revs->rewrite_parents = 1; revs->simplify_history = 0; revs->limited = 1; } else if (!strcmp(arg, "--simplify-by-decoration")) { revs->simplify_merges = 1; + revs->topo_order = 1; revs->rewrite_parents = 1; revs->simplify_history = 0; revs->simplify_by_decoration = 1; @@ -1575,16 +1614,25 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } 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-reflog", argv, &optarg))) { + add_header_grep(revs, GREP_HEADER_REFLOG, optarg); + return argcount; } else if ((argcount = parse_long_opt("grep", argv, &optarg))) { add_message_grep(revs, optarg); return argcount; + } else if (!strcmp(arg, "--grep-debug")) { + revs->grep_filter.debug = 1; + } else if (!strcmp(arg, "--basic-regexp")) { + grep_set_pattern_type_option(GREP_PATTERN_TYPE_BRE, &revs->grep_filter); } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) { - revs->grep_filter.regflags |= REG_EXTENDED; + grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, &revs->grep_filter); } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) { revs->grep_filter.regflags |= REG_ICASE; DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE); } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) { - revs->grep_filter.fixed = 1; + grep_set_pattern_type_option(GREP_PATTERN_TYPE_FIXED, &revs->grep_filter); + } else if (!strcmp(arg, "--perl-regexp")) { + grep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter); } else if (!strcmp(arg, "--all-match")) { revs->grep_filter.all_match = 1; } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) { @@ -1687,7 +1735,18 @@ static int handle_revision_pseudo_opt(const char *submodule, } else if (!strcmp(arg, "--not")) { *flags ^= UNINTERESTING; } else if (!strcmp(arg, "--no-walk")) { - revs->no_walk = 1; + revs->no_walk = REVISION_WALK_NO_WALK_SORTED; + } else if (!prefixcmp(arg, "--no-walk=")) { + /* + * Detached form ("--no-walk X" as opposed to "--no-walk=X") + * not allowed, since the argument is optional. + */ + if (!strcmp(arg + 10, "sorted")) + revs->no_walk = REVISION_WALK_NO_WALK_SORTED; + else if (!strcmp(arg + 10, "unsorted")) + revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED; + else + return error("invalid argument to --no-walk"); } else if (!strcmp(arg, "--do-walk")) { revs->no_walk = 0; } else { @@ -1706,7 +1765,7 @@ static int handle_revision_pseudo_opt(const char *submodule, */ 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; + int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt; struct cmdline_pathspec prune_data; const char *submodule = NULL; @@ -1734,6 +1793,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s /* Second, deal with arguments and options */ flags = 0; + revarg_opt = opt ? opt->revarg_opt : 0; + if (seen_dashdash) + revarg_opt |= REVARG_CANNOT_BE_FILENAME; read_from_stdin = 0; for (left = i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -1769,7 +1831,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s continue; } - if (handle_revision_arg(arg, revs, flags, seen_dashdash)) { + + if (handle_revision_arg(arg, revs, flags, revarg_opt)) { int j; if (seen_dashdash || *arg == '^') die("bad revision '%s'", arg); @@ -1781,7 +1844,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s * but the latter we have checked in the main loop. */ for (j = i; j < argc; j++) - verify_filename(revs->prefix, argv[j]); + verify_filename(revs->prefix, argv[j], j == i); append_prune_data(&prune_data, argv + i); break; @@ -1820,11 +1883,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (revs->def && !revs->pending.nr && !got_rev_arg) { unsigned char sha1[20]; struct object *object; - unsigned mode; - if (get_sha1_with_mode(revs->def, sha1, &mode)) + struct object_context oc; + if (get_sha1_with_context(revs->def, 0, sha1, &oc)) die("bad default revision '%s'", revs->def); object = get_reference(revs, revs->def, sha1, 0); - add_pending_object_with_mode(revs, object, revs->def, mode); + add_pending_object_with_mode(revs, object, revs->def, oc.mode); } /* Did the user ask for any diff output? Run the diff! */ @@ -1851,9 +1914,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (revs->combine_merges) revs->ignore_merges = 0; revs->diffopt.abbrev = revs->abbrev; - if (diff_setup_done(&revs->diffopt) < 0) - die("diff_setup_done failed"); + diff_setup_done(&revs->diffopt); + grep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED, + &revs->grep_filter); compile_grep_patterns(&revs->grep_filter); if (revs->reverse && revs->reflog_info) @@ -1869,6 +1933,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (revs->reflog_info && revs->graph) die("cannot combine --walk-reflogs with --graph"); + if (!revs->reflog_info && revs->grep_filter.use_reflog_filter) + die("cannot use --grep-reflog without --walk-reflogs"); return left; } @@ -1947,8 +2013,9 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c } /* - * Do we know what commit all of our parents should be rewritten to? - * Otherwise we are not ready to rewrite this one yet. + * Do we know what commit all of our parents that matter + * should be rewritten to? Otherwise we are not ready to + * rewrite this one yet. */ for (cnt = 0, p = commit->parents; p; p = p->next) { pst = locate_simplify_state(revs, p->item); @@ -1956,6 +2023,8 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c tail = &commit_list_insert(p->item, tail)->next; cnt++; } + if (revs->first_parent_only) + break; } if (cnt) { tail = &commit_list_insert(commit, tail)->next; @@ -1968,8 +2037,14 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c for (p = commit->parents; p; p = p->next) { pst = locate_simplify_state(revs, p->item); p->item = pst->simplified; + if (revs->first_parent_only) + break; } - cnt = remove_duplicate_parents(commit); + + if (revs->first_parent_only) + cnt = 1; + else + cnt = remove_duplicate_parents(commit); /* * It is possible that we are a merge and one side branch @@ -2013,25 +2088,31 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c static void simplify_merges(struct rev_info *revs) { - struct commit_list *list; + struct commit_list *list, *next; struct commit_list *yet_to_do, **tail; + struct commit *commit; - if (!revs->topo_order) - sort_in_topological_order(&revs->commits, revs->lifo); if (!revs->prune) return; /* feed the list reversed */ yet_to_do = NULL; - for (list = revs->commits; list; list = list->next) - commit_list_insert(list->item, &yet_to_do); + for (list = revs->commits; list; list = next) { + commit = list->item; + next = list->next; + /* + * Do not free(list) here yet; the original list + * is used later in this function. + */ + commit_list_insert(commit, &yet_to_do); + } while (yet_to_do) { list = yet_to_do; yet_to_do = NULL; tail = &yet_to_do; while (list) { - struct commit *commit = list->item; - struct commit_list *next = list->next; + commit = list->item; + next = list->next; free(list); list = next; tail = simplify_one(revs, commit, tail); @@ -2043,9 +2124,10 @@ static void simplify_merges(struct rev_info *revs) revs->commits = NULL; tail = &revs->commits; while (list) { - struct commit *commit = list->item; - struct commit_list *next = list->next; struct merge_simplify_state *st; + + commit = list->item; + next = list->next; free(list); list = next; st = locate_simplify_state(revs, commit); @@ -2091,10 +2173,11 @@ int prepare_revision_walk(struct rev_info *revs) } e++; } - commit_list_sort_by_date(&revs->commits); if (!revs->leak_pending) free(list); + if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED) + commit_list_sort_by_date(&revs->commits); if (revs->no_walk) return 0; if (revs->limited) @@ -2156,12 +2239,106 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit) return 0; } +static int commit_rewrite_person(struct strbuf *buf, const char *what, struct string_list *mailmap) +{ + char *person, *endp; + size_t len, namelen, maillen; + const char *name; + const char *mail; + struct ident_split ident; + + person = strstr(buf->buf, what); + if (!person) + return 0; + + person += strlen(what); + endp = strchr(person, '\n'); + if (!endp) + return 0; + + len = endp - person; + + if (split_ident_line(&ident, person, len)) + return 0; + + mail = ident.mail_begin; + maillen = ident.mail_end - ident.mail_begin; + name = ident.name_begin; + namelen = ident.name_end - ident.name_begin; + + if (map_user(mailmap, &mail, &maillen, &name, &namelen)) { + struct strbuf namemail = STRBUF_INIT; + + strbuf_addf(&namemail, "%.*s <%.*s>", + (int)namelen, name, (int)maillen, mail); + + strbuf_splice(buf, ident.name_begin - buf->buf, + ident.mail_end - ident.name_begin + 1, + namemail.buf, namemail.len); + + strbuf_release(&namemail); + + return 1; + } + + return 0; +} + static int commit_match(struct commit *commit, struct rev_info *opt) { + int retval; + const char *encoding; + char *message; + struct strbuf buf = STRBUF_INIT; + if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list) return 1; - return grep_buffer(&opt->grep_filter, - commit->buffer, strlen(commit->buffer)); + + /* Prepend "fake" headers as needed */ + if (opt->grep_filter.use_reflog_filter) { + strbuf_addstr(&buf, "reflog "); + get_reflog_message(&buf, opt->reflog_info); + strbuf_addch(&buf, '\n'); + } + + /* + * We grep in the user's output encoding, under the assumption that it + * is the encoding they are most likely to write their grep pattern + * for. In addition, it means we will match the "notes" encoding below, + * so we will not end up with a buffer that has two different encodings + * in it. + */ + encoding = get_log_output_encoding(); + message = logmsg_reencode(commit, NULL, encoding); + + /* Copy the commit to temporary if we are using "fake" headers */ + if (buf.len) + strbuf_addstr(&buf, message); + + if (opt->grep_filter.header_list && opt->mailmap) { + if (!buf.len) + strbuf_addstr(&buf, message); + + commit_rewrite_person(&buf, "\nauthor ", opt->mailmap); + commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap); + } + + /* Append "fake" message parts as needed */ + if (opt->show_notes) { + if (!buf.len) + strbuf_addstr(&buf, message); + format_display_notes(commit->object.sha1, &buf, encoding, 1); + } + + /* Find either in the original commit message, or in the temporary */ + if (buf.len) + retval = grep_buffer(&opt->grep_filter, buf.buf, buf.len); + else + retval = grep_buffer(&opt->grep_filter, + message, strlen(message)); + strbuf_release(&buf); + logmsg_free(message, commit); + return retval; } static inline int want_ancestry(struct rev_info *revs) @@ -2344,29 +2521,28 @@ static struct commit *get_revision_internal(struct rev_info *revs) } /* - * Now pick up what they want to give us + * If our max_count counter has reached zero, then we are done. We + * don't simply return NULL because we still might need to show + * boundary commits. But we want to avoid calling get_revision_1, which + * might do a considerable amount of work finding the next commit only + * for us to throw it away. + * + * If it is non-zero, then either we don't have a max_count at all + * (-1), or it is still counting, in which case we decrement. */ - c = get_revision_1(revs); - if (c) { - while (0 < revs->skip_count) { - revs->skip_count--; - c = get_revision_1(revs); - if (!c) - break; + if (revs->max_count) { + c = get_revision_1(revs); + if (c) { + while (0 < revs->skip_count) { + revs->skip_count--; + c = get_revision_1(revs); + if (!c) + break; + } } - } - /* - * Check the max_count. - */ - switch (revs->max_count) { - case -1: - break; - case 0: - c = NULL; - break; - default: - revs->max_count--; + if (revs->max_count > 0) + revs->max_count--; } if (c) |