summaryrefslogtreecommitdiff
path: root/revision.c
diff options
context:
space:
mode:
Diffstat (limited to 'revision.c')
-rw-r--r--revision.c748
1 files changed, 559 insertions, 189 deletions
diff --git a/revision.c b/revision.c
index 7da0907c85..f1e6929d41 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"
@@ -19,12 +21,20 @@
#include "dir.h"
#include "cache-tree.h"
#include "bisect.h"
+#include "packfile.h"
+#include "worktree.h"
+#include "argv-array.h"
+#include "commit-reach.h"
+#include "commit-graph.h"
+#include "prio-queue.h"
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;
@@ -44,25 +54,23 @@ static void mark_blob_uninteresting(struct blob *blob)
blob->object.flags |= UNINTERESTING;
}
-static void mark_tree_contents_uninteresting(struct tree *tree)
+static void mark_tree_contents_uninteresting(struct repository *r,
+ 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(r, lookup_tree(r, entry.oid));
break;
case OBJ_BLOB:
- mark_blob_uninteresting(lookup_blob(entry.oid));
+ mark_blob_uninteresting(lookup_blob(r, entry.oid));
break;
default:
/* Subproject commit - not in this repository */
@@ -77,7 +85,7 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
free_tree_buffer(tree);
}
-void mark_tree_uninteresting(struct tree *tree)
+void mark_tree_uninteresting(struct repository *r, struct tree *tree)
{
struct object *obj;
@@ -88,52 +96,66 @@ void mark_tree_uninteresting(struct tree *tree)
if (obj->flags & UNINTERESTING)
return;
obj->flags |= UNINTERESTING;
- mark_tree_contents_uninteresting(tree);
+ mark_tree_contents_uninteresting(r, 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;
+
+ /*
+ * 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);
+}
- commit->object.flags |= UNINTERESTING;
+void mark_parents_uninteresting(struct commit *commit)
+{
+ struct commit_stack pending = COMMIT_STACK_INIT;
+ struct commit_list *l;
- /*
- * 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;
+ for (l = commit->parents; l; l = l->next)
+ mark_one_parent_uninteresting(l->item, &pending);
- for (l = commit->parents->next; l; l = l->next)
- commit_list_insert(l->item, &parents);
- commit = commit->parents->item;
- }
- }
+ 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,
@@ -179,7 +201,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(revs->repo, &oid);
if (!obj)
return;
add_pending_object(revs, obj, "HEAD");
@@ -191,10 +213,12 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
{
struct object *object;
- object = parse_object(oid);
+ object = parse_object(revs->repo, 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;
@@ -226,10 +250,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(revs->repo, &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;
@@ -248,14 +275,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;
}
@@ -268,7 +300,7 @@ static struct commit *handle_commit(struct rev_info *revs,
if (!revs->tree_objects)
return NULL;
if (flags & UNINTERESTING) {
- mark_tree_contents_uninteresting(tree);
+ mark_tree_contents_uninteresting(revs->repo, tree);
return NULL;
}
add_pending_object_with_path(revs, object, name, mode, path);
@@ -392,8 +424,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;
@@ -404,10 +444,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,
@@ -419,14 +460,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;
@@ -452,7 +493,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;
@@ -462,13 +503,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);
@@ -600,7 +641,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) {
@@ -728,8 +769,8 @@ static void commit_list_insert_by_date_cached(struct commit *p, struct commit_li
*cache = new_entry;
}
-static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
- struct commit_list **list, struct commit_list **cache_ptr)
+static int process_parents(struct rev_info *revs, struct commit *commit,
+ struct commit_list **list, struct commit_list **cache_ptr)
{
struct commit_list *parent = commit->parents;
unsigned left_flag;
@@ -768,7 +809,8 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
if (p->object.flags & SEEN)
continue;
p->object.flags |= SEEN;
- commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
+ if (list)
+ commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
}
return 0;
}
@@ -787,15 +829,28 @@ 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;
- commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
+ if (list)
+ commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
}
if (revs->first_parent_only)
break;
@@ -827,7 +882,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
return;
left_first = left_count < right_count;
- init_patch_ids(&ids);
+ init_patch_ids(revs->repo, &ids);
ids.diffopts.pathspec = revs->diffopt.pathspec;
/* Compute patch-ids for one side */
@@ -1039,18 +1094,13 @@ static int limit_list(struct rev_info *revs)
if (revs->max_age != -1 && (commit->date < revs->max_age))
obj->flags |= UNINTERESTING;
- if (add_parents_to_list(revs, commit, &list, NULL) < 0)
+ if (process_parents(revs, commit, &list, NULL) < 0)
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))
@@ -1131,6 +1181,7 @@ struct all_refs_cb {
int warned_bad_reflog;
struct rev_info *all_revs;
const char *name_for_errormsg;
+ struct worktree *wt;
};
int ref_excluded(struct string_list *ref_excludes, const char *path)
@@ -1167,6 +1218,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
cb->all_revs = revs;
cb->all_flags = flags;
revs->rev_input_given = 1;
+ cb->wt = NULL;
}
void clear_ref_exclusion(struct string_list **ref_excludes_p)
@@ -1187,19 +1239,26 @@ void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
string_list_append(*ref_excludes_p, exclude);
}
-static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
- int (*for_each)(const char *, each_ref_fn, void *))
+static void handle_refs(struct ref_store *refs,
+ struct rev_info *revs, unsigned flags,
+ int (*for_each)(struct ref_store *, each_ref_fn, void *))
{
struct all_refs_cb cb;
+
+ if (!refs) {
+ /* this could happen with uninitialized submodules */
+ return;
+ }
+
init_all_refs_cb(&cb, revs, flags);
- for_each(submodule, handle_one_ref, &cb);
+ for_each(refs, handle_one_ref, &cb);
}
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(cb->all_revs->repo, oid);
if (o) {
o->flags |= cb->all_flags;
/* ??? CMDLINEFLAGS ??? */
@@ -1222,33 +1281,64 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
return 0;
}
-static int handle_one_reflog(const char *path, const struct object_id *oid,
+static int handle_one_reflog(const char *refname_in_wt,
+ const struct object_id *oid,
int flag, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
+ struct strbuf refname = STRBUF_INIT;
+
cb->warned_bad_reflog = 0;
- cb->name_for_errormsg = path;
- for_each_reflog_ent(path, handle_one_reflog_ent, cb_data);
+ strbuf_worktree_ref(cb->wt, &refname, refname_in_wt);
+ cb->name_for_errormsg = refname.buf;
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ refname.buf,
+ handle_one_reflog_ent, cb_data);
+ strbuf_release(&refname);
return 0;
}
+static void add_other_reflogs_to_pending(struct all_refs_cb *cb)
+{
+ struct worktree **worktrees, **p;
+
+ worktrees = get_worktrees(0);
+ for (p = worktrees; *p; p++) {
+ struct worktree *wt = *p;
+
+ if (wt->is_current)
+ continue;
+
+ cb->wt = wt;
+ refs_for_each_reflog(get_worktree_ref_store(wt),
+ handle_one_reflog,
+ cb);
+ }
+ free_worktrees(worktrees);
+}
+
void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
{
struct all_refs_cb cb;
cb.all_revs = revs;
cb.all_flags = flags;
+ cb.wt = NULL;
for_each_reflog(handle_one_reflog, &cb);
+
+ if (!revs->single_worktree)
+ add_other_reflogs_to_pending(&cb);
}
static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
- struct strbuf *path)
+ struct strbuf *path, unsigned int flags)
{
size_t baselen = path->len;
int i;
if (it->entry_count >= 0) {
- struct tree *tree = lookup_tree(&it->oid);
+ struct tree *tree = lookup_tree(revs->repo, &it->oid);
+ tree->object.flags |= flags;
add_pending_object_with_path(revs, &tree->object, "",
040000, path->buf);
}
@@ -1256,38 +1346,67 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
for (i = 0; i < it->subtree_nr; i++) {
struct cache_tree_sub *sub = it->down[i];
strbuf_addf(path, "%s%s", baselen ? "/" : "", sub->name);
- add_cache_tree(sub->cache_tree, revs, path);
+ add_cache_tree(sub->cache_tree, revs, path, flags);
strbuf_setlen(path, baselen);
}
}
-void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
+static void do_add_index_objects_to_pending(struct rev_info *revs,
+ struct index_state *istate,
+ unsigned int flags)
{
int i;
- read_cache();
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
struct blob *blob;
if (S_ISGITLINK(ce->ce_mode))
continue;
- blob = lookup_blob(&ce->oid);
+ blob = lookup_blob(revs->repo, &ce->oid);
if (!blob)
die("unable to add index blob to traversal");
+ blob->object.flags |= flags;
add_pending_object_with_path(revs, &blob->object, "",
ce->ce_mode, ce->name);
}
- if (active_cache_tree) {
+ if (istate->cache_tree) {
struct strbuf path = STRBUF_INIT;
- add_cache_tree(active_cache_tree, revs, &path);
+ add_cache_tree(istate->cache_tree, revs, &path, flags);
strbuf_release(&path);
}
}
+void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
+{
+ struct worktree **worktrees, **p;
+
+ read_index(revs->repo->index);
+ do_add_index_objects_to_pending(revs, revs->repo->index, flags);
+
+ if (revs->single_worktree)
+ return;
+
+ worktrees = get_worktrees(0);
+ for (p = worktrees; *p; p++) {
+ struct worktree *wt = *p;
+ struct index_state istate = { NULL };
+
+ if (wt->is_current)
+ continue; /* current index already taken care of */
+
+ if (read_index_from(&istate,
+ worktree_git_path(wt, "index"),
+ get_worktree_git_dir(wt)) > 0)
+ do_add_index_objects_to_pending(revs, &istate, flags);
+ discard_index(&istate);
+ }
+ free_worktrees(worktrees);
+}
+
static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
int exclude_parent)
{
@@ -1302,7 +1421,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
flags ^= UNINTERESTING | BOTTOM;
arg++;
}
- if (get_sha1_committish(arg, oid.hash))
+ if (get_oid_committish(arg, &oid))
return 0;
while (1) {
it = get_reference(revs, arg, &oid, 0);
@@ -1334,17 +1453,22 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
return 1;
}
-void init_revisions(struct rev_info *revs, const char *prefix)
+void repo_init_revisions(struct repository *r,
+ struct rev_info *revs,
+ const char *prefix)
{
memset(revs, 0, sizeof(*revs));
+ revs->repo = r;
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.repo = r;
+ 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;
@@ -1358,11 +1482,11 @@ void init_revisions(struct rev_info *revs, const char *prefix)
revs->commit_format = CMIT_FMT_DEFAULT;
revs->expand_tabs_in_log_default = 8;
- init_grep_defaults();
- grep_init(&revs->grep_filter, prefix);
+ init_grep_defaults(revs->repo);
+ grep_init(&revs->grep_filter, revs->repo, prefix);
revs->grep_filter.status_only = 1;
- diff_setup(&revs->diffopt);
+ repo_diff_setup(revs->repo, &revs->diffopt);
if (prefix && !revs->diffopt.prefix) {
revs->diffopt.prefix = prefix;
revs->diffopt.prefix_length = strlen(prefix);
@@ -1390,6 +1514,7 @@ static void prepare_show_merge(struct rev_info *revs)
struct object_id oid;
const char **prune = NULL;
int i, prune_num = 1; /* counting terminating NULL */
+ struct index_state *istate = revs->repo->index;
if (get_oid("HEAD", &oid))
die("--merge without HEAD?");
@@ -1405,20 +1530,20 @@ static void prepare_show_merge(struct rev_info *revs)
free_commit_list(bases);
head->object.flags |= SYMMETRIC_LEFT;
- if (!active_nr)
- read_cache();
- for (i = 0; i < active_nr; i++) {
- const struct cache_entry *ce = active_cache[i];
+ if (!istate->cache_nr)
+ read_index(istate);
+ for (i = 0; i < istate->cache_nr; i++) {
+ const struct cache_entry *ce = istate->cache[i];
if (!ce_stage(ce))
continue;
- if (ce_path_match(ce, &revs->prune_data, NULL)) {
+ if (ce_path_match(istate, ce, &revs->prune_data, NULL)) {
prune_num++;
REALLOC_ARRAY(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]))
+ while ((i+1 < istate->cache_nr) &&
+ ce_same_name(ce, istate->cache[i+1]))
i++;
}
clear_pathspec(&revs->prune_data);
@@ -1451,7 +1576,7 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
unsigned int a_flags, b_flags;
int symmetric = 0;
unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
- unsigned int oc_flags = GET_SHA1_COMMITTISH | GET_SHA1_RECORD_PATH;
+ unsigned int oc_flags = GET_OID_COMMITTISH | GET_OID_RECORD_PATH;
a_name = arg;
if (!*a_name)
@@ -1465,8 +1590,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
if (!*b_name)
b_name = "HEAD";
- if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, a_oc) ||
- get_sha1_with_context(b_name, oc_flags, b_oid.hash, b_oc))
+ if (get_oid_with_context(a_name, oc_flags, &a_oid, a_oc) ||
+ get_oid_with_context(b_name, oc_flags, &b_oid, b_oc))
return -1;
if (!cant_be_filename) {
@@ -1475,8 +1600,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(revs->repo, &a_oid);
+ b_obj = parse_object(revs->repo, &b_oid);
if (!a_obj || !b_obj)
return dotdot_missing(arg, dotdot, revs, symmetric);
@@ -1489,8 +1614,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(revs->repo, &a_obj->oid);
+ b = lookup_commit_reference(revs->repo, &b_obj->oid);
if (!a || !b)
return dotdot_missing(arg, dotdot, revs, symmetric);
@@ -1547,7 +1672,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
int local_flags;
const char *arg = arg_;
int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
- unsigned get_sha1_flags = GET_SHA1_RECORD_PATH;
+ unsigned get_sha1_flags = GET_OID_RECORD_PATH;
flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
@@ -1598,44 +1723,30 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
}
if (revarg_opt & REVARG_COMMITTISH)
- get_sha1_flags |= GET_SHA1_COMMITTISH;
+ get_sha1_flags |= GET_OID_COMMITTISH;
- if (get_sha1_with_context(arg, get_sha1_flags, oid.hash, &oc))
+ if (get_oid_with_context(arg, get_sha1_flags, &oid, &oc))
return revs->ignore_missing ? 0 : -1;
if (!cant_be_filename)
verify_non_filename(revs->prefix, arg);
object = get_reference(revs, arg, &oid, flags ^ local_flags);
+ if (!object)
+ return revs->ignore_missing ? 0 : -1;
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
free(oc.path);
return 0;
}
-struct cmdline_pathspec {
- int alloc;
- int nr;
- const char **path;
-};
-
-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)
+ struct argv_array *prune)
{
- while (strbuf_getline(sb, stdin) != EOF) {
- ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
- prune->path[prune->nr++] = xstrdup(sb->buf);
- }
+ while (strbuf_getline(sb, stdin) != EOF)
+ argv_array_push(prune, sb->buf);
}
static void read_revisions_from_stdin(struct rev_info *revs,
- struct cmdline_pathspec *prune)
+ struct argv_array *prune)
{
struct strbuf sb;
int seen_dashdash = 0;
@@ -1688,6 +1799,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") ||
@@ -1776,7 +1888,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;
@@ -1797,8 +1909,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")) {
@@ -1871,11 +1983,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")) {
@@ -1975,8 +2087,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;
@@ -2020,7 +2132,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")) {
@@ -2042,6 +2154,11 @@ 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 (revs->allow_exclude_promisor_objects_opt &&
+ !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)
@@ -2068,23 +2185,25 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
ctx->argc -= n;
}
-static int for_each_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data, const char *term) {
+static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
+ void *cb_data, const char *term)
+{
struct strbuf bisect_refs = STRBUF_INIT;
int status;
strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
- status = for_each_fullref_in_submodule(submodule, bisect_refs.buf, fn, cb_data, 0);
+ status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0);
strbuf_release(&bisect_refs);
return status;
}
-static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
- return for_each_bisect_ref(submodule, fn, cb_data, term_bad);
+ return for_each_bisect_ref(refs, fn, cb_data, term_bad);
}
-static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
- return for_each_bisect_ref(submodule, fn, cb_data, term_good);
+ return for_each_bisect_ref(refs, fn, cb_data, term_good);
}
static int handle_revision_pseudo_opt(const char *submodule,
@@ -2093,8 +2212,22 @@ static int handle_revision_pseudo_opt(const char *submodule,
{
const char *arg = argv[0];
const char *optarg;
+ struct ref_store *refs;
int argcount;
+ if (submodule) {
+ /*
+ * We need some something like get_submodule_worktrees()
+ * before we can go through all worktrees of a submodule,
+ * .e.g with adding all HEADs from --all, which is not
+ * supported right now, so stick to single worktree.
+ */
+ if (!revs->single_worktree)
+ BUG("--single-worktree cannot be used together with submodule");
+ refs = get_submodule_ref_store(submodule);
+ } else
+ refs = get_main_ref_store(revs->repo);
+
/*
* NOTE!
*
@@ -2106,22 +2239,29 @@ static int handle_revision_pseudo_opt(const char *submodule,
* 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);
+ handle_refs(refs, revs, *flags, refs_for_each_ref);
+ handle_refs(refs, revs, *flags, refs_head_ref);
+ if (!revs->single_worktree) {
+ struct all_refs_cb cb;
+
+ init_all_refs_cb(&cb, revs, *flags);
+ other_head_refs(handle_one_ref, &cb);
+ }
clear_ref_exclusion(&revs->ref_excludes);
} else if (!strcmp(arg, "--branches")) {
- handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
+ handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
clear_ref_exclusion(&revs->ref_excludes);
} else if (!strcmp(arg, "--bisect")) {
read_bisect_terms(&term_bad, &term_good);
- handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
- handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
+ handle_refs(refs, revs, *flags, for_each_bad_bisect_ref);
+ handle_refs(refs, revs, *flags ^ (UNINTERESTING | BOTTOM),
+ for_each_good_bisect_ref);
revs->bisect = 1;
} else if (!strcmp(arg, "--tags")) {
- handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
+ handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
clear_ref_exclusion(&revs->ref_excludes);
} else if (!strcmp(arg, "--remotes")) {
- handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
+ handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
clear_ref_exclusion(&revs->ref_excludes);
} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
struct all_refs_cb cb;
@@ -2168,6 +2308,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
return error("invalid argument to --no-walk");
} else if (!strcmp(arg, "--do-walk")) {
revs->no_walk = 0;
+ } else if (!strcmp(arg, "--single-worktree")) {
+ revs->single_worktree = 1;
} else {
return 0;
}
@@ -2177,11 +2319,10 @@ static int handle_revision_pseudo_opt(const char *submodule,
static void NORETURN diagnose_missing_default(const char *def)
{
- unsigned char sha1[20];
int flags;
const char *refname;
- refname = resolve_ref_unsafe(def, 0, sha1, &flags);
+ refname = resolve_ref_unsafe(def, 0, NULL, &flags);
if (!refname || !(flags & REF_ISSYMREF) || (flags & REF_ISBROKEN))
die(_("your current branch appears to be broken"));
@@ -2199,11 +2340,10 @@ static void NORETURN diagnose_missing_default(const char *def)
*/
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, revarg_opt;
- struct cmdline_pathspec prune_data;
+ int i, flags, left, seen_dashdash, got_rev_arg = 0, revarg_opt;
+ struct argv_array prune_data = ARGV_ARRAY_INIT;
const char *submodule = NULL;
- memset(&prune_data, 0, sizeof(prune_data));
if (opt)
submodule = opt->submodule;
@@ -2219,7 +2359,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
argv[i] = NULL;
argc = i;
if (argv[i + 1])
- append_prune_data(&prune_data, argv + i + 1);
+ argv_array_pushv(&prune_data, argv + i + 1);
seen_dashdash = 1;
break;
}
@@ -2230,7 +2370,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
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];
if (*arg == '-') {
@@ -2249,7 +2388,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
argv[left++] = arg;
continue;
}
- if (read_from_stdin++)
+ if (revs->read_from_stdin++)
die("--stdin given twice?");
read_revisions_from_stdin(revs, &prune_data);
continue;
@@ -2280,14 +2419,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
for (j = i; j < argc; j++)
verify_filename(revs->prefix, argv[j], j == i);
- append_prune_data(&prune_data, argv + i);
+ argv_array_pushv(&prune_data, argv + i);
break;
}
else
got_rev_arg = 1;
}
- if (prune_data.nr) {
+ if (prune_data.argc) {
/*
* If we need to introduce the magic "a lone ':' means no
* pathspec whatsoever", here is the place to do so.
@@ -2302,11 +2441,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
* 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;
parse_pathspec(&revs->prune_data, 0, 0,
- revs->prefix, prune_data.path);
+ revs->prefix, prune_data.argv);
}
+ argv_array_clear(&prune_data);
if (revs->def == NULL)
revs->def = opt ? opt->def : NULL;
@@ -2318,7 +2456,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
struct object_id oid;
struct object *object;
struct object_context oc;
- if (get_sha1_with_context(revs->def, 0, oid.hash, &oc))
+ if (get_oid_with_context(revs->def, 0, &oid, &oc))
diagnose_missing_default(revs->def);
object = get_reference(revs, revs->def, &oid, 0);
add_pending_object_with_mode(revs, object, revs->def, oc.mode);
@@ -2329,18 +2467,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->topo_order)
+ if (revs->diffopt.objfind)
+ revs->simplify_history = 0;
+
+ if (revs->topo_order && !generation_numbers_enabled(the_repository))
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,
@@ -2762,6 +2903,228 @@ 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 *cb)
+{
+ struct rev_info *revs = cb;
+ struct object *o = parse_object(revs->repo, oid);
+ o->flags |= UNINTERESTING | SEEN;
+ return 0;
+}
+
+define_commit_slab(indegree_slab, int);
+define_commit_slab(author_date_slab, timestamp_t);
+
+struct topo_walk_info {
+ uint32_t min_generation;
+ struct prio_queue explore_queue;
+ struct prio_queue indegree_queue;
+ struct prio_queue topo_queue;
+ struct indegree_slab indegree;
+ struct author_date_slab author_date;
+};
+
+static inline void test_flag_and_insert(struct prio_queue *q, struct commit *c, int flag)
+{
+ if (c->object.flags & flag)
+ return;
+
+ c->object.flags |= flag;
+ prio_queue_put(q, c);
+}
+
+static void explore_walk_step(struct rev_info *revs)
+{
+ struct topo_walk_info *info = revs->topo_walk_info;
+ struct commit_list *p;
+ struct commit *c = prio_queue_get(&info->explore_queue);
+
+ if (!c)
+ return;
+
+ if (parse_commit_gently(c, 1) < 0)
+ return;
+
+ if (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)
+ record_author_date(&info->author_date, c);
+
+ if (revs->max_age != -1 && (c->date < revs->max_age))
+ c->object.flags |= UNINTERESTING;
+
+ if (process_parents(revs, c, NULL, NULL) < 0)
+ return;
+
+ if (c->object.flags & UNINTERESTING)
+ mark_parents_uninteresting(c);
+
+ for (p = c->parents; p; p = p->next)
+ test_flag_and_insert(&info->explore_queue, p->item, TOPO_WALK_EXPLORED);
+}
+
+static void explore_to_depth(struct rev_info *revs,
+ uint32_t gen_cutoff)
+{
+ struct topo_walk_info *info = revs->topo_walk_info;
+ struct commit *c;
+ while ((c = prio_queue_peek(&info->explore_queue)) &&
+ c->generation >= gen_cutoff)
+ explore_walk_step(revs);
+}
+
+static void indegree_walk_step(struct rev_info *revs)
+{
+ struct commit_list *p;
+ struct topo_walk_info *info = revs->topo_walk_info;
+ struct commit *c = prio_queue_get(&info->indegree_queue);
+
+ if (!c)
+ return;
+
+ if (parse_commit_gently(c, 1) < 0)
+ return;
+
+ explore_to_depth(revs, c->generation);
+
+ for (p = c->parents; p; p = p->next) {
+ struct commit *parent = p->item;
+ int *pi = indegree_slab_at(&info->indegree, parent);
+
+ if (*pi)
+ (*pi)++;
+ else
+ *pi = 2;
+
+ test_flag_and_insert(&info->indegree_queue, parent, TOPO_WALK_INDEGREE);
+
+ if (revs->first_parent_only)
+ return;
+ }
+}
+
+static void compute_indegrees_to_depth(struct rev_info *revs,
+ uint32_t gen_cutoff)
+{
+ struct topo_walk_info *info = revs->topo_walk_info;
+ struct commit *c;
+ while ((c = prio_queue_peek(&info->indegree_queue)) &&
+ c->generation >= gen_cutoff)
+ indegree_walk_step(revs);
+}
+
+static void init_topo_walk(struct rev_info *revs)
+{
+ struct topo_walk_info *info;
+ struct commit_list *list;
+ revs->topo_walk_info = xmalloc(sizeof(struct topo_walk_info));
+ info = revs->topo_walk_info;
+ memset(info, 0, sizeof(struct topo_walk_info));
+
+ init_indegree_slab(&info->indegree);
+ memset(&info->explore_queue, 0, sizeof(info->explore_queue));
+ memset(&info->indegree_queue, 0, sizeof(info->indegree_queue));
+ memset(&info->topo_queue, 0, sizeof(info->topo_queue));
+
+ switch (revs->sort_order) {
+ default: /* REV_SORT_IN_GRAPH_ORDER */
+ info->topo_queue.compare = NULL;
+ break;
+ case REV_SORT_BY_COMMIT_DATE:
+ info->topo_queue.compare = compare_commits_by_commit_date;
+ break;
+ case REV_SORT_BY_AUTHOR_DATE:
+ init_author_date_slab(&info->author_date);
+ info->topo_queue.compare = compare_commits_by_author_date;
+ info->topo_queue.cb_data = &info->author_date;
+ break;
+ }
+
+ info->explore_queue.compare = compare_commits_by_gen_then_commit_date;
+ info->indegree_queue.compare = compare_commits_by_gen_then_commit_date;
+
+ info->min_generation = GENERATION_NUMBER_INFINITY;
+ for (list = revs->commits; list; list = list->next) {
+ struct commit *c = list->item;
+
+ if (parse_commit_gently(c, 1))
+ continue;
+
+ test_flag_and_insert(&info->explore_queue, c, TOPO_WALK_EXPLORED);
+ test_flag_and_insert(&info->indegree_queue, c, TOPO_WALK_INDEGREE);
+
+ if (c->generation < info->min_generation)
+ info->min_generation = c->generation;
+
+ *(indegree_slab_at(&info->indegree, c)) = 1;
+
+ if (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)
+ record_author_date(&info->author_date, c);
+ }
+ compute_indegrees_to_depth(revs, info->min_generation);
+
+ for (list = revs->commits; list; list = list->next) {
+ struct commit *c = list->item;
+
+ if (*(indegree_slab_at(&info->indegree, c)) == 1)
+ prio_queue_put(&info->topo_queue, c);
+ }
+
+ /*
+ * This is unfortunate; the initial tips need to be shown
+ * in the order given from the revision traversal machinery.
+ */
+ if (revs->sort_order == REV_SORT_IN_GRAPH_ORDER)
+ prio_queue_reverse(&info->topo_queue);
+}
+
+static struct commit *next_topo_commit(struct rev_info *revs)
+{
+ struct commit *c;
+ struct topo_walk_info *info = revs->topo_walk_info;
+
+ /* pop next off of topo_queue */
+ c = prio_queue_get(&info->topo_queue);
+
+ if (c)
+ *(indegree_slab_at(&info->indegree, c)) = 0;
+
+ return c;
+}
+
+static void expand_topo_walk(struct rev_info *revs, struct commit *commit)
+{
+ struct commit_list *p;
+ struct topo_walk_info *info = revs->topo_walk_info;
+ if (process_parents(revs, commit, NULL, NULL) < 0) {
+ if (!revs->ignore_missing_links)
+ die("Failed to traverse parents of commit %s",
+ oid_to_hex(&commit->object.oid));
+ }
+
+ for (p = commit->parents; p; p = p->next) {
+ struct commit *parent = p->item;
+ int *pi;
+
+ if (parse_commit_gently(parent, 1) < 0)
+ continue;
+
+ if (parent->generation < info->min_generation) {
+ info->min_generation = parent->generation;
+ compute_indegrees_to_depth(revs, info->min_generation);
+ }
+
+ pi = indegree_slab_at(&info->indegree, parent);
+
+ (*pi)--;
+ if (*pi == 1)
+ prio_queue_put(&info->topo_queue, parent);
+
+ if (revs->first_parent_only)
+ return;
+ }
+}
+
int prepare_revision_walk(struct rev_info *revs)
{
int i;
@@ -2782,23 +3145,29 @@ 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, revs,
+ 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)
return 0;
- if (revs->limited)
+ if (revs->limited) {
if (limit_list(revs) < 0)
return -1;
- if (revs->topo_order)
- sort_in_topological_order(&revs->commits, revs->sort_order);
+ if (revs->topo_order)
+ sort_in_topological_order(&revs->commits, revs->sort_order);
+ } else if (revs->topo_order)
+ init_topo_walk(revs);
if (revs->line_level_traverse)
line_log_filter(revs);
if (revs->simplify_merges)
@@ -2815,7 +3184,7 @@ static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp
for (;;) {
struct commit *p = *pp;
if (!revs->limited)
- if (add_parents_to_list(revs, p, &revs->commits, &cache) < 0)
+ if (process_parents(revs, p, &revs->commits, &cache) < 0)
return rewrite_one_error;
if (p->object.flags & UNINTERESTING)
return rewrite_one_ok;
@@ -2980,10 +3349,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 &&
@@ -3082,7 +3449,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
@@ -3107,7 +3473,7 @@ static void track_linear(struct rev_info *revs, struct commit *commit)
struct commit_list *p;
for (p = revs->previous_parents; p; p = p->next)
if (p->item == NULL || /* first commit */
- !oidcmp(&p->item->object.oid, &commit->object.oid))
+ oideq(&p->item->object.oid, &commit->object.oid))
break;
revs->linear = p != NULL;
}
@@ -3126,6 +3492,8 @@ static struct commit *get_revision_1(struct rev_info *revs)
if (revs->reflog_info)
commit = next_reflog_entry(revs->reflog_info);
+ else if (revs->topo_walk_info)
+ commit = next_topo_commit(revs);
else
commit = pop_commit(&revs->commits);
@@ -3147,7 +3515,9 @@ static struct commit *get_revision_1(struct rev_info *revs)
if (revs->reflog_info)
try_to_simplify_commit(revs, commit);
- else if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) {
+ else if (revs->topo_walk_info)
+ expand_topo_walk(revs, commit);
+ else if (process_parents(revs, commit, &revs->commits, NULL) < 0) {
if (!revs->ignore_missing_links)
die("Failed to traverse parents of commit %s",
oid_to_hex(&commit->object.oid));