X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=revision.c;h=f1e0caaae3d2a96de6cf9bad12e8641c19d788dd;hb=ce4231ffa80da1888a668cc7c33f5349800ab4a3;hp=b963f2adfd92050310f7327e1ee10e21d5996a7c;hpb=f4241c4c9a03d15957759e836eda7b8108ef881d;p=git.git diff --git a/revision.c b/revision.c index b963f2adf..f1e0caaae 100644 --- a/revision.c +++ b/revision.c @@ -6,6 +6,8 @@ #include "diff.h" #include "refs.h" #include "revision.h" +#include +#include "grep.h" static char *path_name(struct name_path *path, const char *name) { @@ -135,7 +137,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object /* * Tag object? Look what it points to.. */ - while (object->type == TYPE_TAG) { + while (object->type == OBJ_TAG) { struct tag *tag = (struct tag *) object; if (revs->tag_objects && !(flags & UNINTERESTING)) add_pending_object(revs, object, tag->tag); @@ -148,7 +150,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object * Commit object? Just return it, we'll do all the complex * reachability crud. */ - if (object->type == TYPE_COMMIT) { + if (object->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)object; if (parse_commit(commit) < 0) die("unable to parse commit %s", name); @@ -164,7 +166,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object * Tree object? Either mark it uniniteresting, or add it * to the list of objects to look at later.. */ - if (object->type == TYPE_TREE) { + if (object->type == OBJ_TREE) { struct tree *tree = (struct tree *)object; if (!revs->tree_objects) return NULL; @@ -179,7 +181,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object /* * Blob object? You know the drill by now.. */ - if (object->type == TYPE_BLOB) { + if (object->type == OBJ_BLOB) { struct blob *blob = (struct blob *)object; if (!revs->blob_objects) return NULL; @@ -280,7 +282,7 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1) static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) { struct commit_list **pp, *parent; - int tree_changed = 0; + int tree_changed = 0, tree_same = 0; if (!commit->tree) return; @@ -298,6 +300,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) parse_commit(p); switch (rev_compare_tree(revs, p->tree, commit->tree)) { case REV_TREE_SAME: + tree_same = 1; if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) { /* Even if a merge with an uninteresting * side branch brought the entire change @@ -334,7 +337,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) } die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1)); } - if (tree_changed) + if (tree_changed && !tree_same) commit->object.flags |= TREECHANGE; } @@ -415,7 +418,8 @@ static void limit_list(struct rev_info *revs) if (revs->max_age != -1 && (commit->date < revs->max_age)) obj->flags |= UNINTERESTING; - if (revs->unpacked && has_sha1_pack(obj->sha1)) + if (revs->unpacked && + has_sha1_pack(obj->sha1, revs->ignore_packed)) obj->flags |= UNINTERESTING; add_parents_to_list(revs, commit, &list); if (obj->flags & UNINTERESTING) { @@ -493,11 +497,11 @@ 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->type != TYPE_TAG) + if (it->type != OBJ_TAG) break; - memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20); + hashcpy(sha1, ((struct tag*)it)->tagged->sha1); } - if (it->type != TYPE_COMMIT) + if (it->type != OBJ_COMMIT) return 0; commit = (struct commit *)it; for (parents = commit->parents; parents; parents = parents->next) { @@ -508,7 +512,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags) return 1; } -void init_revisions(struct rev_info *revs) +void init_revisions(struct rev_info *revs, const char *prefix) { memset(revs, 0, sizeof(*revs)); @@ -520,7 +524,7 @@ void init_revisions(struct rev_info *revs) revs->pruning.change = file_change; revs->lifo = 1; revs->dense = 1; - revs->prefix = setup_git_directory(); + revs->prefix = prefix; revs->max_age = -1; revs->min_age = -1; revs->max_count = -1; @@ -536,6 +540,186 @@ void init_revisions(struct rev_info *revs) diff_setup(&revs->diffopt); } +static void add_pending_commit_list(struct rev_info *revs, + struct commit_list *commit_list, + unsigned int flags) +{ + while (commit_list) { + struct object *object = &commit_list->item->object; + object->flags |= flags; + add_pending_object(revs, object, sha1_to_hex(object->sha1)); + commit_list = commit_list->next; + } +} + +static void prepare_show_merge(struct rev_info *revs) +{ + struct commit_list *bases; + struct commit *head, *other; + unsigned char sha1[20]; + const char **prune = NULL; + int i, prune_num = 1; /* counting terminating NULL */ + + if (get_sha1("HEAD", sha1) || !(head = lookup_commit(sha1))) + die("--merge without HEAD?"); + if (get_sha1("MERGE_HEAD", sha1) || !(other = lookup_commit(sha1))) + die("--merge without MERGE_HEAD?"); + add_pending_object(revs, &head->object, "HEAD"); + add_pending_object(revs, &other->object, "MERGE_HEAD"); + bases = get_merge_bases(head, other, 1); + while (bases) { + struct commit *it = bases->item; + struct commit_list *n = bases->next; + free(bases); + bases = n; + it->object.flags |= UNINTERESTING; + add_pending_object(revs, &it->object, "(merge-base)"); + } + + if (!active_nr) + read_cache(); + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (!ce_stage(ce)) + continue; + if (ce_path_match(ce, revs->prune_data)) { + prune_num++; + prune = xrealloc(prune, sizeof(*prune) * prune_num); + prune[prune_num-2] = ce->name; + prune[prune_num-1] = NULL; + } + while ((i+1 < active_nr) && + ce_same_name(ce, active_cache[i+1])) + i++; + } + revs->prune_data = prune; +} + +int handle_revision_arg(const char *arg, struct rev_info *revs, + int flags, + int cant_be_filename) +{ + char *dotdot; + struct object *object; + unsigned char sha1[20]; + int local_flags; + + dotdot = strstr(arg, ".."); + if (dotdot) { + unsigned char from_sha1[20]; + const char *next = dotdot + 2; + const char *this = arg; + int symmetric = *next == '.'; + unsigned int flags_exclude = flags ^ UNINTERESTING; + + *dotdot = 0; + next += symmetric; + + if (!*next) + next = "HEAD"; + if (dotdot == arg) + this = "HEAD"; + if (!get_sha1(this, from_sha1) && + !get_sha1(next, sha1)) { + struct commit *a, *b; + struct commit_list *exclude; + + a = lookup_commit_reference(from_sha1); + b = lookup_commit_reference(sha1); + if (!a || !b) { + die(symmetric ? + "Invalid symmetric difference expression %s...%s" : + "Invalid revision range %s..%s", + arg, next); + } + + if (!cant_be_filename) { + *dotdot = '.'; + verify_non_filename(revs->prefix, arg); + } + + if (symmetric) { + exclude = get_merge_bases(a, b, 1); + add_pending_commit_list(revs, exclude, + flags_exclude); + free_commit_list(exclude); + a->object.flags |= flags; + } else + a->object.flags |= flags_exclude; + b->object.flags |= flags; + add_pending_object(revs, &a->object, this); + add_pending_object(revs, &b->object, next); + return 0; + } + *dotdot = '.'; + } + dotdot = strstr(arg, "^@"); + if (dotdot && !dotdot[2]) { + *dotdot = 0; + if (add_parents_only(revs, arg, flags)) + return 0; + *dotdot = '^'; + } + local_flags = 0; + if (*arg == '^') { + local_flags = UNINTERESTING; + arg++; + } + if (get_sha1(arg, sha1)) + return -1; + if (!cant_be_filename) + verify_non_filename(revs->prefix, arg); + object = get_reference(revs, arg, sha1, flags ^ local_flags); + add_pending_object(revs, object, arg); + return 0; +} + +static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what) +{ + if (!revs->grep_filter) { + struct grep_opt *opt = xcalloc(1, sizeof(*opt)); + opt->status_only = 1; + opt->pattern_tail = &(opt->pattern_list); + opt->regflags = REG_NEWLINE; + revs->grep_filter = opt; + } + append_grep_pattern(revs->grep_filter, ptn, + "command line", 0, what); +} + +static void add_header_grep(struct rev_info *revs, const char *field, const char *pattern) +{ + char *pat; + const char *prefix; + int patlen, fldlen; + + fldlen = strlen(field); + patlen = strlen(pattern); + pat = xmalloc(patlen + fldlen + 10); + prefix = ".*"; + if (*pattern == '^') { + prefix = ""; + pattern++; + } + sprintf(pat, "^%s %s%s", field, prefix, pattern); + add_grep(revs, pat, GREP_PATTERN_HEAD); +} + +static void add_message_grep(struct rev_info *revs, const char *pattern) +{ + add_grep(revs, pattern, GREP_PATTERN_BODY); +} + +static void add_ignore_packed(struct rev_info *revs, const char *name) +{ + int num = ++revs->num_ignore_packed; + + revs->ignore_packed = xrealloc(revs->ignore_packed, + sizeof(const char **) * (num + 1)); + revs->ignore_packed[num-1] = name; + revs->ignore_packed[num] = NULL; +} + /* * Parse revision information, filling in the "rev_info" structure, * and removing the used arguments from the argument list. @@ -545,9 +729,10 @@ void init_revisions(struct rev_info *revs) */ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def) { - int i, flags, seen_dashdash; + int i, flags, seen_dashdash, show_merge; const char **unrecognized = argv + 1; int left = 1; + int all_match = 0; /* First, search for "--" */ seen_dashdash = 0; @@ -562,14 +747,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch break; } - flags = 0; + flags = show_merge = 0; for (i = 1; i < argc; i++) { - struct object *object; const char *arg = argv[i]; - unsigned char sha1[20]; - char *dotdot; - int local_flags; - if (*arg == '-') { int opts; if (!strncmp(arg, "--max-count=", 12)) { @@ -629,6 +809,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch def = argv[i]; continue; } + if (!strcmp(arg, "--merge")) { + show_merge = 1; + continue; + } if (!strcmp(arg, "--topo-order")) { revs->topo_order = 1; continue; @@ -677,6 +861,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (!strcmp(arg, "--unpacked")) { revs->unpacked = 1; + free(revs->ignore_packed); + revs->ignore_packed = NULL; + revs->num_ignore_packed = 0; + continue; + } + if (!strncmp(arg, "--unpacked=", 11)) { + revs->unpacked = 1; + add_ignore_packed(revs, arg+11); continue; } if (!strcmp(arg, "-r")) { @@ -756,6 +948,31 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->simplify_history = 0; continue; } + if (!strcmp(arg, "--relative-date")) { + revs->relative_date = 1; + continue; + } + + /* + * Grepping the commit log + */ + if (!strncmp(arg, "--author=", 9)) { + add_header_grep(revs, "author", arg+9); + continue; + } + if (!strncmp(arg, "--committer=", 12)) { + add_header_grep(revs, "committer", arg+12); + continue; + } + if (!strncmp(arg, "--grep=", 7)) { + add_message_grep(revs, arg+7); + continue; + } + if (!strcmp(arg, "--all-match")) { + all_match = 1; + continue; + } + opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); if (opts > 0) { revs->diff = 1; @@ -766,52 +983,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch left++; continue; } - dotdot = strstr(arg, ".."); - if (dotdot) { - unsigned char from_sha1[20]; - const char *next = dotdot + 2; - const char *this = arg; - *dotdot = 0; - if (!*next) - next = "HEAD"; - if (dotdot == arg) - this = "HEAD"; - if (!get_sha1(this, from_sha1) && - !get_sha1(next, sha1)) { - struct object *exclude; - struct object *include; - - exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING); - include = get_reference(revs, next, sha1, flags); - if (!exclude || !include) - die("Invalid revision range %s..%s", arg, next); - - if (!seen_dashdash) { - *dotdot = '.'; - verify_non_filename(revs->prefix, arg); - } - add_pending_object(revs, exclude, this); - add_pending_object(revs, include, next); - continue; - } - *dotdot = '.'; - } - dotdot = strstr(arg, "^@"); - if (dotdot && !dotdot[2]) { - *dotdot = 0; - if (add_parents_only(revs, arg, flags)) - continue; - *dotdot = '^'; - } - local_flags = 0; - if (*arg == '^') { - local_flags = UNINTERESTING; - arg++; - } - if (get_sha1(arg, sha1)) { - int j; - if (seen_dashdash || local_flags) + if (handle_revision_arg(arg, revs, flags, seen_dashdash)) { + int j; + if (seen_dashdash || *arg == '^') die("bad revision '%s'", arg); /* If we didn't have a "--": @@ -823,14 +998,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch for (j = i; j < argc; j++) verify_filename(revs->prefix, argv[j]); - revs->prune_data = get_pathspec(revs->prefix, argv + i); + revs->prune_data = get_pathspec(revs->prefix, + argv + i); break; } - if (!seen_dashdash) - verify_non_filename(revs->prefix, arg); - object = get_reference(revs, arg, sha1, flags ^ local_flags); - add_pending_object(revs, object, arg); } + + if (show_merge) + prepare_show_merge(revs); if (def && !revs->pending.nr) { unsigned char sha1[20]; struct object *object; @@ -851,12 +1026,17 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (revs->combine_merges) { revs->ignore_merges = 0; - if (revs->dense_combined_merges && - (revs->diffopt.output_format != DIFF_FORMAT_DIFFSTAT)) + if (revs->dense_combined_merges && !revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; } revs->diffopt.abbrev = revs->abbrev; - diff_setup_done(&revs->diffopt); + if (diff_setup_done(&revs->diffopt) < 0) + die("diff_setup_done failed"); + + if (revs->grep_filter) { + revs->grep_filter->all_match = all_match; + compile_grep_patterns(revs->grep_filter); + } return left; } @@ -896,6 +1076,8 @@ static int rewrite_one(struct rev_info *revs, struct commit **pp) struct commit *p = *pp; if (!revs->limited) add_parents_to_list(revs, p, &revs->commits); + if (p->parents && p->parents->next) + return 0; if (p->object.flags & (TREECHANGE | UNINTERESTING)) return 0; if (!p->parents) @@ -928,6 +1110,15 @@ static void mark_boundary_to_show(struct commit *commit) } } +static int commit_match(struct commit *commit, struct rev_info *opt) +{ + if (!opt->grep_filter) + return 1; + return grep_buffer(opt->grep_filter, + NULL, /* we say nothing, not even filename */ + commit->buffer, strlen(commit->buffer)); +} + struct commit *get_revision(struct rev_info *revs) { struct commit_list *list = revs->commits; @@ -959,7 +1150,8 @@ struct commit *get_revision(struct rev_info *revs) */ if (!revs->limited) { if ((revs->unpacked && - has_sha1_pack(commit->object.sha1)) || + has_sha1_pack(commit->object.sha1, + revs->ignore_packed)) || (revs->max_age != -1 && (commit->date < revs->max_age))) continue; @@ -987,9 +1179,18 @@ struct commit *get_revision(struct rev_info *revs) if (revs->no_merges && commit->parents && commit->parents->next) continue; + if (!commit_match(commit, revs)) + continue; if (revs->prune_fn && revs->dense) { - if (!(commit->object.flags & TREECHANGE)) - continue; + /* Commit without changes? */ + if (!(commit->object.flags & TREECHANGE)) { + /* drop merges unless we want parenthood */ + if (!revs->parents) + continue; + /* non-merge - always ignore it */ + if (!commit->parents || !commit->parents->next) + continue; + } if (revs->parents) rewrite_parents(revs, commit); }