summaryrefslogtreecommitdiff
path: root/revision.c
diff options
context:
space:
mode:
Diffstat (limited to 'revision.c')
-rw-r--r--revision.c257
1 files changed, 160 insertions, 97 deletions
diff --git a/revision.c b/revision.c
index d167223e69..0627494378 100644
--- a/revision.c
+++ b/revision.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "object-store.h"
#include "tag.h"
#include "blob.h"
#include "tree.h"
@@ -6,6 +7,7 @@
#include "diff.h"
#include "refs.h"
#include "revision.h"
+#include "repository.h"
#include "graph.h"
#include "grep.h"
#include "reflog-walk.h"
@@ -28,6 +30,8 @@ volatile show_early_output_fn_t show_early_output;
static const char *term_bad;
static const char *term_good;
+implement_shared_commit_slab(revision_sources, char *);
+
void show_object_with_name(FILE *out, struct object *obj, const char *name)
{
const char *p;
@@ -51,21 +55,18 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
{
struct tree_desc desc;
struct name_entry entry;
- struct object *obj = &tree->object;
- if (!has_object_file(&obj->oid))
+ if (parse_tree_gently(tree, 1) < 0)
return;
- if (parse_tree(tree) < 0)
- die("bad tree %s", oid_to_hex(&obj->oid));
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
- mark_tree_uninteresting(lookup_tree(entry.oid));
+ mark_tree_uninteresting(lookup_tree(the_repository, entry.oid));
break;
case OBJ_BLOB:
- mark_blob_uninteresting(lookup_blob(entry.oid));
+ mark_blob_uninteresting(lookup_blob(the_repository, entry.oid));
break;
default:
/* Subproject commit - not in this repository */
@@ -94,49 +95,63 @@ void mark_tree_uninteresting(struct tree *tree)
mark_tree_contents_uninteresting(tree);
}
-void mark_parents_uninteresting(struct commit *commit)
+struct commit_stack {
+ struct commit **items;
+ size_t nr, alloc;
+};
+#define COMMIT_STACK_INIT { NULL, 0, 0 }
+
+static void commit_stack_push(struct commit_stack *stack, struct commit *commit)
{
- struct commit_list *parents = NULL, *l;
+ ALLOC_GROW(stack->items, stack->nr + 1, stack->alloc);
+ stack->items[stack->nr++] = commit;
+}
- for (l = commit->parents; l; l = l->next)
- commit_list_insert(l->item, &parents);
+static struct commit *commit_stack_pop(struct commit_stack *stack)
+{
+ return stack->nr ? stack->items[--stack->nr] : NULL;
+}
- while (parents) {
- struct commit *commit = pop_commit(&parents);
+static void commit_stack_clear(struct commit_stack *stack)
+{
+ FREE_AND_NULL(stack->items);
+ stack->nr = stack->alloc = 0;
+}
- while (commit) {
- /*
- * A missing commit is ok iff its parent is marked
- * uninteresting.
- *
- * We just mark such a thing parsed, so that when
- * it is popped next time around, we won't be trying
- * to parse it and get an error.
- */
- if (!has_object_file(&commit->object.oid))
- commit->object.parsed = 1;
+static void mark_one_parent_uninteresting(struct commit *commit,
+ struct commit_stack *pending)
+{
+ struct commit_list *l;
- if (commit->object.flags & UNINTERESTING)
- break;
+ if (commit->object.flags & UNINTERESTING)
+ return;
+ commit->object.flags |= UNINTERESTING;
- commit->object.flags |= UNINTERESTING;
+ /*
+ * Normally we haven't parsed the parent
+ * yet, so we won't have a parent of a parent
+ * here. However, it may turn out that we've
+ * reached this commit some other way (where it
+ * wasn't uninteresting), in which case we need
+ * to mark its parents recursively too..
+ */
+ for (l = commit->parents; l; l = l->next)
+ commit_stack_push(pending, l->item);
+}
- /*
- * Normally we haven't parsed the parent
- * yet, so we won't have a parent of a parent
- * here. However, it may turn out that we've
- * reached this commit some other way (where it
- * wasn't uninteresting), in which case we need
- * to mark its parents recursively too..
- */
- if (!commit->parents)
- break;
+void mark_parents_uninteresting(struct commit *commit)
+{
+ struct commit_stack pending = COMMIT_STACK_INIT;
+ struct commit_list *l;
- for (l = commit->parents->next; l; l = l->next)
- commit_list_insert(l->item, &parents);
- commit = commit->parents->item;
- }
- }
+ for (l = commit->parents; l; l = l->next)
+ mark_one_parent_uninteresting(l->item, &pending);
+
+ while (pending.nr > 0)
+ mark_one_parent_uninteresting(commit_stack_pop(&pending),
+ &pending);
+
+ commit_stack_clear(&pending);
}
static void add_pending_object_with_path(struct rev_info *revs,
@@ -160,6 +175,7 @@ static void add_pending_object_with_path(struct rev_info *revs,
strbuf_release(&buf);
return; /* do not add the commit itself */
}
+ obj->flags |= USER_GIVEN;
add_object_array_with_path(obj, name, &revs->pending, mode, path);
}
@@ -182,7 +198,7 @@ void add_head_to_pending(struct rev_info *revs)
struct object *obj;
if (get_oid("HEAD", &oid))
return;
- obj = parse_object(&oid);
+ obj = parse_object(the_repository, &oid);
if (!obj)
return;
add_pending_object(revs, obj, "HEAD");
@@ -194,10 +210,12 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
{
struct object *object;
- object = parse_object(oid);
+ object = parse_object(the_repository, oid);
if (!object) {
if (revs->ignore_missing)
return object;
+ if (revs->exclude_promisor_objects && is_promisor_object(oid))
+ return NULL;
die("bad object %s", name);
}
object->flags |= flags;
@@ -229,10 +247,13 @@ static struct commit *handle_commit(struct rev_info *revs,
add_pending_object(revs, object, tag->tag);
if (!tag->tagged)
die("bad tag");
- object = parse_object(&tag->tagged->oid);
+ object = parse_object(the_repository, &tag->tagged->oid);
if (!object) {
if (revs->ignore_missing_links || (flags & UNINTERESTING))
return NULL;
+ if (revs->exclude_promisor_objects &&
+ is_promisor_object(&tag->tagged->oid))
+ return NULL;
die("bad object %s", oid_to_hex(&tag->tagged->oid));
}
object->flags |= flags;
@@ -251,14 +272,19 @@ static struct commit *handle_commit(struct rev_info *revs,
*/
if (object->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)object;
+
if (parse_commit(commit) < 0)
die("unable to parse commit %s", name);
if (flags & UNINTERESTING) {
mark_parents_uninteresting(commit);
revs->limited = 1;
}
- if (revs->show_source && !commit->util)
- commit->util = xstrdup(name);
+ if (revs->sources) {
+ char **slot = revision_sources_at(revs->sources, commit);
+
+ if (!*slot)
+ *slot = xstrdup(name);
+ }
return commit;
}
@@ -395,8 +421,16 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,
* if the whole diff is removal of old data, and otherwise
* REV_TREE_DIFFERENT (of course if the trees are the same we
* want REV_TREE_SAME).
- * That means that once we get to REV_TREE_DIFFERENT, we do not
- * have to look any further.
+ *
+ * The only time we care about the distinction is when
+ * remove_empty_trees is in effect, in which case we care only about
+ * whether the whole change is REV_TREE_NEW, or if there's another type
+ * of change. Which means we can stop the diff early in either of these
+ * cases:
+ *
+ * 1. We're not using remove_empty_trees at all.
+ *
+ * 2. We saw anything except REV_TREE_NEW.
*/
static int tree_difference = REV_TREE_SAME;
@@ -407,10 +441,11 @@ static void file_add_remove(struct diff_options *options,
const char *fullpath, unsigned dirty_submodule)
{
int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
+ struct rev_info *revs = options->change_fn_data;
tree_difference |= diff;
- if (tree_difference == REV_TREE_DIFFERENT)
- DIFF_OPT_SET(options, HAS_CHANGES);
+ if (!revs->remove_empty_trees || tree_difference != REV_TREE_NEW)
+ options->flags.has_changes = 1;
}
static void file_change(struct diff_options *options,
@@ -422,14 +457,14 @@ static void file_change(struct diff_options *options,
unsigned old_dirty_submodule, unsigned new_dirty_submodule)
{
tree_difference = REV_TREE_DIFFERENT;
- DIFF_OPT_SET(options, HAS_CHANGES);
+ options->flags.has_changes = 1;
}
static int rev_compare_tree(struct rev_info *revs,
struct commit *parent, struct commit *commit)
{
- struct tree *t1 = parent->tree;
- struct tree *t2 = commit->tree;
+ struct tree *t1 = get_commit_tree(parent);
+ struct tree *t2 = get_commit_tree(commit);
if (!t1)
return REV_TREE_NEW;
@@ -455,7 +490,7 @@ static int rev_compare_tree(struct rev_info *revs,
}
tree_difference = REV_TREE_SAME;
- DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+ revs->pruning.flags.has_changes = 0;
if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
&revs->pruning) < 0)
return REV_TREE_DIFFERENT;
@@ -465,13 +500,13 @@ static int rev_compare_tree(struct rev_info *revs,
static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
{
int retval;
- struct tree *t1 = commit->tree;
+ struct tree *t1 = get_commit_tree(commit);
if (!t1)
return 0;
tree_difference = REV_TREE_SAME;
- DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+ revs->pruning.flags.has_changes = 0;
retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
return retval >= 0 && (tree_difference == REV_TREE_SAME);
@@ -603,7 +638,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
if (!revs->prune)
return;
- if (!commit->tree)
+ if (!get_commit_tree(commit))
return;
if (!commit->parents) {
@@ -790,11 +825,23 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
for (parent = commit->parents; parent; parent = parent->next) {
struct commit *p = parent->item;
-
- if (parse_commit_gently(p, revs->ignore_missing_links) < 0)
+ int gently = revs->ignore_missing_links ||
+ revs->exclude_promisor_objects;
+ if (parse_commit_gently(p, gently) < 0) {
+ if (revs->exclude_promisor_objects &&
+ is_promisor_object(&p->object.oid)) {
+ if (revs->first_parent_only)
+ break;
+ continue;
+ }
return -1;
- if (revs->show_source && !p->util)
- p->util = commit->util;
+ }
+ if (revs->sources) {
+ char **slot = revision_sources_at(revs->sources, p);
+
+ if (!*slot)
+ *slot = *revision_sources_at(revs->sources, commit);
+ }
p->object.flags |= left_flag;
if (!(p->object.flags & SEEN)) {
p->object.flags |= SEEN;
@@ -1046,14 +1093,9 @@ static int limit_list(struct rev_info *revs)
return -1;
if (obj->flags & UNINTERESTING) {
mark_parents_uninteresting(commit);
- if (revs->show_all)
- p = &commit_list_insert(commit, p)->next;
slop = still_interesting(list, date, slop, &interesting_cache);
if (slop)
continue;
- /* If showing all, add the whole pending list to the end */
- if (revs->show_all)
- *p = list;
break;
}
if (revs->min_age != -1 && (commit->date > revs->min_age))
@@ -1211,7 +1253,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
if (!is_null_oid(oid)) {
- struct object *o = parse_object(oid);
+ struct object *o = parse_object(the_repository, oid);
if (o) {
o->flags |= cb->all_flags;
/* ??? CMDLINEFLAGS ??? */
@@ -1270,7 +1312,7 @@ void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
cb.all_revs = revs;
cb.all_flags = flags;
- cb.refs = get_main_ref_store();
+ cb.refs = get_main_ref_store(the_repository);
for_each_reflog(handle_one_reflog, &cb);
if (!revs->single_worktree)
@@ -1284,7 +1326,7 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
int i;
if (it->entry_count >= 0) {
- struct tree *tree = lookup_tree(&it->oid);
+ struct tree *tree = lookup_tree(the_repository, &it->oid);
add_pending_object_with_path(revs, &tree->object, "",
040000, path->buf);
}
@@ -1310,7 +1352,7 @@ static void do_add_index_objects_to_pending(struct rev_info *revs,
if (S_ISGITLINK(ce->ce_mode))
continue;
- blob = lookup_blob(&ce->oid);
+ blob = lookup_blob(the_repository, &ce->oid);
if (!blob)
die("unable to add index blob to traversal");
add_pending_object_with_path(revs, &blob->object, "",
@@ -1343,7 +1385,8 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
continue; /* current index already taken care of */
if (read_index_from(&istate,
- worktree_git_path(wt, "index")) > 0)
+ worktree_git_path(wt, "index"),
+ get_worktree_git_dir(wt)) > 0)
do_add_index_objects_to_pending(revs, &istate);
discard_index(&istate);
}
@@ -1403,10 +1446,11 @@ void init_revisions(struct rev_info *revs, const char *prefix)
revs->abbrev = DEFAULT_ABBREV;
revs->ignore_merges = 1;
revs->simplify_history = 1;
- DIFF_OPT_SET(&revs->pruning, RECURSIVE);
- DIFF_OPT_SET(&revs->pruning, QUICK);
+ revs->pruning.flags.recursive = 1;
+ revs->pruning.flags.quick = 1;
revs->pruning.add_remove = file_add_remove;
revs->pruning.change = file_change;
+ revs->pruning.change_fn_data = revs;
revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
revs->dense = 1;
revs->prefix = prefix;
@@ -1537,8 +1581,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
*dotdot = '\0';
}
- a_obj = parse_object(&a_oid);
- b_obj = parse_object(&b_oid);
+ a_obj = parse_object(the_repository, &a_oid);
+ b_obj = parse_object(the_repository, &b_oid);
if (!a_obj || !b_obj)
return dotdot_missing(arg, dotdot, revs, symmetric);
@@ -1551,8 +1595,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
struct commit *a, *b;
struct commit_list *exclude;
- a = lookup_commit_reference(&a_obj->oid);
- b = lookup_commit_reference(&b_obj->oid);
+ a = lookup_commit_reference(the_repository, &a_obj->oid);
+ b = lookup_commit_reference(the_repository, &b_obj->oid);
if (!a || !b)
return dotdot_missing(arg, dotdot, revs, symmetric);
@@ -1734,6 +1778,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
const char *arg = argv[0];
const char *optarg;
int argcount;
+ const unsigned hexsz = the_hash_algo->hexsz;
/* pseudo revision arguments */
if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
@@ -1822,7 +1867,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->simplify_by_decoration = 1;
revs->limited = 1;
revs->prune = 1;
- load_ref_decorations(DECORATE_SHORT_REFS);
+ load_ref_decorations(NULL, DECORATE_SHORT_REFS);
} else if (!strcmp(arg, "--date-order")) {
revs->sort_order = REV_SORT_BY_COMMIT_DATE;
revs->topo_order = 1;
@@ -1843,8 +1888,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->dense = 1;
} else if (!strcmp(arg, "--sparse")) {
revs->dense = 0;
- } else if (!strcmp(arg, "--show-all")) {
- revs->show_all = 1;
+ } else if (!strcmp(arg, "--in-commit-order")) {
+ revs->tree_blobs_in_commit_order = 1;
} else if (!strcmp(arg, "--remove-empty")) {
revs->remove_empty_trees = 1;
} else if (!strcmp(arg, "--merges")) {
@@ -1917,11 +1962,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
die("--unpacked=<packfile> no longer supported.");
} else if (!strcmp(arg, "-r")) {
revs->diff = 1;
- DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
+ revs->diffopt.flags.recursive = 1;
} else if (!strcmp(arg, "-t")) {
revs->diff = 1;
- DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
- DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
+ revs->diffopt.flags.recursive = 1;
+ revs->diffopt.flags.tree_in_recursive = 1;
} else if (!strcmp(arg, "-m")) {
revs->ignore_merges = 0;
} else if (!strcmp(arg, "-c")) {
@@ -2021,8 +2066,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->abbrev = strtoul(optarg, NULL, 10);
if (revs->abbrev < MINIMUM_ABBREV)
revs->abbrev = MINIMUM_ABBREV;
- else if (revs->abbrev > 40)
- revs->abbrev = 40;
+ else if (revs->abbrev > hexsz)
+ revs->abbrev = hexsz;
} else if (!strcmp(arg, "--abbrev-commit")) {
revs->abbrev_commit = 1;
revs->abbrev_commit_given = 1;
@@ -2066,7 +2111,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
revs->grep_filter.ignore_case = 1;
- DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
+ revs->diffopt.pickaxe_opts |= DIFF_PICKAXE_IGNORE_CASE;
} else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
} else if (!strcmp(arg, "--perl-regexp") || !strcmp(arg, "-P")) {
@@ -2088,6 +2133,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->limited = 1;
} else if (!strcmp(arg, "--ignore-missing")) {
revs->ignore_missing = 1;
+ } else if (!strcmp(arg, "--exclude-promisor-objects")) {
+ if (fetch_if_missing)
+ BUG("exclude_promisor_objects can only be used when fetch_if_missing is 0");
+ revs->exclude_promisor_objects = 1;
} else {
int opts = diff_opt_parse(&revs->diffopt, argv, argc, revs->prefix);
if (!opts)
@@ -2152,10 +2201,10 @@ static int handle_revision_pseudo_opt(const char *submodule,
* supported right now, so stick to single worktree.
*/
if (!revs->single_worktree)
- die("BUG: --single-worktree cannot be used together with submodule");
+ BUG("--single-worktree cannot be used together with submodule");
refs = get_submodule_ref_store(submodule);
} else
- refs = get_main_ref_store();
+ refs = get_main_ref_store(the_repository);
/*
* NOTE!
@@ -2397,18 +2446,21 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
revs->diff = 1;
/* Pickaxe, diff-filter and rename following need diffs */
- if (revs->diffopt.pickaxe ||
+ if ((revs->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||
revs->diffopt.filter ||
- DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+ revs->diffopt.flags.follow_renames)
revs->diff = 1;
+ if (revs->diffopt.objfind)
+ revs->simplify_history = 0;
+
if (revs->topo_order)
revs->limited = 1;
if (revs->prune_data.nr) {
copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
/* Can't prune commits with rename following: the paths change.. */
- if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+ if (!revs->diffopt.flags.follow_renames)
revs->prune = 1;
if (!revs->full_diff)
copy_pathspec(&revs->diffopt.pathspec,
@@ -2830,6 +2882,16 @@ void reset_revision_walk(void)
clear_object_flags(SEEN | ADDED | SHOWN);
}
+static int mark_uninteresting(const struct object_id *oid,
+ struct packed_git *pack,
+ uint32_t pos,
+ void *unused)
+{
+ struct object *o = parse_object(the_repository, oid);
+ o->flags |= UNINTERESTING | SEEN;
+ return 0;
+}
+
int prepare_revision_walk(struct rev_info *revs)
{
int i;
@@ -2850,14 +2912,18 @@ int prepare_revision_walk(struct rev_info *revs)
}
}
}
- if (!revs->leak_pending)
- object_array_clear(&old_pending);
+ object_array_clear(&old_pending);
/* Signal whether we need per-parent treesame decoration */
if (revs->simplify_merges ||
(revs->limited && limiting_can_increase_treesame(revs)))
revs->treesame.name = "treesame";
+ if (revs->exclude_promisor_objects) {
+ for_each_packed_object(mark_uninteresting, NULL,
+ FOR_EACH_OBJECT_PROMISOR_ONLY);
+ }
+
if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
commit_list_sort_by_date(&revs->commits);
if (revs->no_walk)
@@ -3048,10 +3114,8 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
{
if (commit->object.flags & SHOWN)
return commit_ignore;
- if (revs->unpacked && has_sha1_pack(commit->object.oid.hash))
+ if (revs->unpacked && has_object_pack(&commit->object.oid))
return commit_ignore;
- if (revs->show_all)
- return commit_show;
if (commit->object.flags & UNINTERESTING)
return commit_ignore;
if (revs->min_age != -1 &&
@@ -3150,7 +3214,6 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
enum commit_action action = get_commit_action(revs, commit);
if (action == commit_show &&
- !revs->show_all &&
revs->prune && revs->dense && want_ancestry(revs)) {
/*
* --full-diff on simplified parents is no good: it