X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=revision.c;h=5f2f0be3825e521152448a9bb47a17c79951d56b;hb=5d8ee9ceb8912c83336191d32b8898943b8944b8;hp=12cd0529a5dfac9af01af0f47e895e9cf142c42a;hpb=35a9f5d91eef7f95a50615e5ec3d36f475878c66;p=git.git diff --git a/revision.c b/revision.c index 12cd0529..5f2f0be3 100644 --- a/revision.c +++ b/revision.c @@ -233,25 +233,20 @@ static void file_change(struct diff_options *options, tree_difference = REV_TREE_DIFFERENT; } -static struct diff_options diff_opt = { - .recursive = 1, - .add_remove = file_add_remove, - .change = file_change, -}; - -int rev_compare_tree(struct tree *t1, struct tree *t2) +int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2) { if (!t1) return REV_TREE_NEW; if (!t2) return REV_TREE_DIFFERENT; tree_difference = REV_TREE_SAME; - if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &diff_opt) < 0) + if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", + &revs->diffopt) < 0) return REV_TREE_DIFFERENT; return tree_difference; } -int rev_same_tree_as_empty(struct tree *t1) +int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1) { int retval; void *tree; @@ -260,7 +255,7 @@ int rev_same_tree_as_empty(struct tree *t1) if (!t1) return 0; - tree = read_object_with_reference(t1->object.sha1, "tree", &real.size, NULL); + tree = read_object_with_reference(t1->object.sha1, tree_type, &real.size, NULL); if (!tree) return 0; real.buf = tree; @@ -269,7 +264,7 @@ int rev_same_tree_as_empty(struct tree *t1) empty.size = 0; tree_difference = 0; - retval = diff_tree(&empty, &real, "", &diff_opt); + retval = diff_tree(&empty, &real, "", &revs->diffopt); free(tree); return retval >= 0 && !tree_difference; @@ -284,7 +279,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) return; if (!commit->parents) { - if (!rev_same_tree_as_empty(commit->tree)) + if (!rev_same_tree_as_empty(revs, commit->tree)) commit->object.flags |= TREECHANGE; return; } @@ -294,7 +289,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) struct commit *p = parent->item; parse_commit(p); - switch (rev_compare_tree(p->tree, commit->tree)) { + switch (rev_compare_tree(revs, p->tree, commit->tree)) { case REV_TREE_SAME: if (p->object.flags & UNINTERESTING) { /* Even if a merge with an uninteresting @@ -312,7 +307,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) case REV_TREE_NEW: if (revs->remove_empty_trees && - rev_same_tree_as_empty(p->tree)) { + rev_same_tree_as_empty(revs, p->tree)) { /* We are adding all the specified * paths from this parent, so the * history beyond this parent is not @@ -340,6 +335,10 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st { struct commit_list *parent = commit->parents; + if (commit->object.flags & ADDED) + return; + commit->object.flags |= ADDED; + /* * If the commit is uninteresting, don't try to * prune parents - we want the maximal uninteresting @@ -419,6 +418,36 @@ static void limit_list(struct rev_info *revs) continue; p = &commit_list_insert(commit, p)->next; } + if (revs->boundary) { + /* mark the ones that are on the result list first */ + for (list = newlist; list; list = list->next) { + struct commit *commit = list->item; + commit->object.flags |= TMP_MARK; + } + for (list = newlist; list; list = list->next) { + struct commit *commit = list->item; + struct object *obj = &commit->object; + struct commit_list *parent; + if (obj->flags & UNINTERESTING) + continue; + for (parent = commit->parents; + parent; + parent = parent->next) { + struct commit *pcommit = parent->item; + if (!(pcommit->object.flags & UNINTERESTING)) + continue; + pcommit->object.flags |= BOUNDARY; + if (pcommit->object.flags & TMP_MARK) + continue; + pcommit->object.flags |= TMP_MARK; + p = &commit_list_insert(pcommit, p)->next; + } + } + for (list = newlist; list; list = list->next) { + struct commit *commit = list->item; + commit->object.flags &= ~TMP_MARK; + } + } revs->commits = newlist; } @@ -450,6 +479,9 @@ static void handle_all(struct rev_info *revs, unsigned flags) void init_revisions(struct rev_info *revs) { memset(revs, 0, sizeof(*revs)); + revs->diffopt.recursive = 1; + revs->diffopt.add_remove = file_add_remove; + revs->diffopt.change = file_change; revs->lifo = 1; revs->dense = 1; revs->prefix = setup_git_directory(); @@ -505,7 +537,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->max_count = atoi(arg + 12); continue; } - /* accept -, like traditilnal "head" */ + /* accept -, like traditional "head" */ if ((*arg == '-') && isdigit(arg[1])) { revs->max_count = atoi(arg + 1); continue; @@ -522,32 +554,26 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (!strncmp(arg, "--max-age=", 10)) { revs->max_age = atoi(arg + 10); - revs->limited = 1; - continue; - } - if (!strncmp(arg, "--min-age=", 10)) { - revs->min_age = atoi(arg + 10); - revs->limited = 1; continue; } if (!strncmp(arg, "--since=", 8)) { revs->max_age = approxidate(arg + 8); - revs->limited = 1; continue; } if (!strncmp(arg, "--after=", 8)) { revs->max_age = approxidate(arg + 8); - revs->limited = 1; + continue; + } + if (!strncmp(arg, "--min-age=", 10)) { + revs->min_age = atoi(arg + 10); continue; } if (!strncmp(arg, "--before=", 9)) { revs->min_age = approxidate(arg + 9); - revs->limited = 1; continue; } if (!strncmp(arg, "--until=", 8)) { revs->min_age = approxidate(arg + 8); - revs->limited = 1; continue; } if (!strcmp(arg, "--all")) { @@ -566,13 +592,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (!strcmp(arg, "--topo-order")) { revs->topo_order = 1; - revs->limited = 1; continue; } if (!strcmp(arg, "--date-order")) { revs->lifo = 0; revs->topo_order = 1; - revs->limited = 1; + continue; + } + if (!strcmp(arg, "--parents")) { + revs->parents = 1; continue; } if (!strcmp(arg, "--dense")) { @@ -587,10 +615,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->remove_empty_trees = 1; continue; } - if (!strncmp(arg, "--no-merges", 11)) { + if (!strcmp(arg, "--no-merges")) { revs->no_merges = 1; continue; } + if (!strcmp(arg, "--boundary")) { + revs->boundary = 1; + continue; + } if (!strcmp(arg, "--objects")) { revs->tag_objects = 1; revs->tree_objects = 1; @@ -606,7 +638,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (!strcmp(arg, "--unpacked")) { revs->unpacked = 1; - revs->limited = 1; continue; } *unrecognized++ = arg; @@ -616,15 +647,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch dotdot = strstr(arg, ".."); if (dotdot) { unsigned char from_sha1[20]; - char *next = dotdot + 2; + const char *next = dotdot + 2; + const char *this = arg; *dotdot = 0; if (!*next) next = "HEAD"; - if (!get_sha1(arg, from_sha1) && !get_sha1(next, sha1)) { + if (dotdot == arg) + this = "HEAD"; + if (!get_sha1(this, from_sha1) && + !get_sha1(next, sha1)) { struct commit *exclude; struct commit *include; - exclude = get_commit_reference(revs, arg, from_sha1, flags ^ UNINTERESTING); + exclude = get_commit_reference(revs, this, from_sha1, flags ^ UNINTERESTING); include = get_commit_reference(revs, next, sha1, flags); if (!exclude || !include) die("Invalid revision range %s..%s", arg, next); @@ -640,17 +675,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch arg++; } if (get_sha1(arg, sha1) < 0) { - struct stat st; int j; if (seen_dashdash || local_flags) die("bad revision '%s'", arg); /* If we didn't have a "--", all filenames must exist */ - for (j = i; j < argc; j++) { - if (lstat(argv[j], &st) < 0) - die("'%s': %s", arg, strerror(errno)); - } + for (j = i; j < argc; j++) + verify_filename(revs->prefix, argv[j]); + revs->prune_data = get_pathspec(revs->prefix, argv + i); break; } @@ -666,10 +699,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch add_one_commit(commit, revs); } + if (revs->topo_order || revs->unpacked) + revs->limited = 1; + if (revs->prune_data) { - diff_tree_setup_paths(revs->prune_data); + diff_tree_setup_paths(revs->prune_data, &revs->diffopt); revs->prune_fn = try_to_simplify_commit; - revs->limited = 1; } return left; @@ -686,10 +721,12 @@ void prepare_revision_walk(struct rev_info *revs) revs->topo_getter); } -static int rewrite_one(struct commit **pp) +static int rewrite_one(struct rev_info *revs, struct commit **pp) { for (;;) { struct commit *p = *pp; + if (!revs->limited) + add_parents_to_list(revs, p, &revs->commits); if (p->object.flags & (TREECHANGE | UNINTERESTING)) return 0; if (!p->parents) @@ -698,12 +735,12 @@ static int rewrite_one(struct commit **pp) } } -static void rewrite_parents(struct commit *commit) +static void rewrite_parents(struct rev_info *revs, struct commit *commit) { struct commit_list **pp = &commit->parents; while (*pp) { struct commit_list *parent = *pp; - if (rewrite_one(&parent->item) < 0) { + if (rewrite_one(revs, &parent->item) < 0) { *pp = parent->next; continue; } @@ -711,6 +748,17 @@ static void rewrite_parents(struct commit *commit) } } +static void mark_boundary_to_show(struct commit *commit) +{ + struct commit_list *p = commit->parents; + while (p) { + commit = p->item; + p = p->next; + if (commit->object.flags & BOUNDARY) + commit->object.flags |= BOUNDARY_SHOW; + } +} + struct commit *get_revision(struct rev_info *revs) { struct commit_list *list = revs->commits; @@ -731,26 +779,53 @@ struct commit *get_revision(struct rev_info *revs) do { struct commit *commit = revs->commits->item; - if (commit->object.flags & (UNINTERESTING|SHOWN)) - goto next; + revs->commits = revs->commits->next; + + /* + * If we haven't done the list limiting, we need to look at + * the parents here. We also need to do the date-based limiting + * that we'd otherwise have done in limit_list(). + */ + if (!revs->limited) { + if ((revs->unpacked && + has_sha1_pack(commit->object.sha1)) || + (revs->max_age != -1 && + (commit->date < revs->max_age))) + continue; + add_parents_to_list(revs, commit, &revs->commits); + } + if (commit->object.flags & SHOWN) + continue; + + /* We want to show boundary commits only when their + * children are shown. When path-limiter is in effect, + * rewrite_parents() drops some commits from getting shown, + * and there is no point showing boundary parents that + * are not shown. After rewrite_parents() rewrites the + * parents of a commit that is shown, we mark the boundary + * parents with BOUNDARY_SHOW. + */ + if (commit->object.flags & BOUNDARY_SHOW) { + commit->object.flags |= SHOWN; + return commit; + } + if (commit->object.flags & UNINTERESTING) + continue; if (revs->min_age != -1 && (commit->date > revs->min_age)) - goto next; - if (revs->max_age != -1 && (commit->date < revs->max_age)) - return NULL; - if (revs->no_merges && commit->parents && commit->parents->next) - goto next; + continue; + if (revs->no_merges && + commit->parents && commit->parents->next) + continue; if (revs->prune_fn && revs->dense) { if (!(commit->object.flags & TREECHANGE)) - goto next; - rewrite_parents(commit); + continue; + if (revs->parents) + rewrite_parents(revs, commit); } - /* More to go? */ - if (revs->max_count) - pop_most_recent_commit(&revs->commits, SEEN); + if (revs->boundary) + mark_boundary_to_show(commit); commit->object.flags |= SHOWN; return commit; -next: - pop_most_recent_commit(&revs->commits, SEEN); } while (revs->commits); return NULL; }