summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Junio C Hamano <gitster@pobox.com>2021-09-03 13:49:27 -0700
committerLibravatar Junio C Hamano <gitster@pobox.com>2021-09-03 13:49:27 -0700
commita5619d4f8d91a179d0e21aa16fda52fb4f0f7eaf (patch)
tree1c49fce2c9c7752763bbb5693be246b128ae5626
parentThe second batch (diff)
parentrevision: avoid hitting packfiles when commits are in commit-graph (diff)
downloadtgif-a5619d4f8d91a179d0e21aa16fda52fb4f0f7eaf.tar.xz
Merge branch 'ps/connectivity-optim'
The revision traversal API has been optimized by taking advantage of the commit-graph, when available, to determine if a commit is reachable from any of the existing refs. * ps/connectivity-optim: revision: avoid hitting packfiles when commits are in commit-graph commit-graph: split out function to search commit position revision: stop retrieving reference twice connected: do not sort input revisions revision: separate walk and unsorted flags
-rw-r--r--Documentation/rev-list-options.txt8
-rw-r--r--builtin/log.c2
-rw-r--r--builtin/revert.c3
-rw-r--r--commit-graph.c75
-rw-r--r--commit-graph.h8
-rw-r--r--connected.c1
-rw-r--r--revision.c38
-rw-r--r--revision.h7
-rwxr-xr-xt/t6000-rev-list-misc.sh31
9 files changed, 127 insertions, 46 deletions
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 24569b06d1..b7bd27e171 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -968,6 +968,11 @@ list of the missing objects. Object IDs are prefixed with a ``?'' character.
objects.
endif::git-rev-list[]
+--unsorted-input::
+ Show commits in the order they were given on the command line instead
+ of sorting them in reverse chronological order by commit time. Cannot
+ be combined with `--no-walk` or `--no-walk=sorted`.
+
--no-walk[=(sorted|unsorted)]::
Only show the given commits, but do not traverse their ancestors.
This has no effect if a range is specified. If the argument
@@ -975,7 +980,8 @@ endif::git-rev-list[]
given on the command line. Otherwise (if `sorted` or no argument
was given), the commits are shown in reverse chronological order
by commit time.
- Cannot be combined with `--graph`.
+ Cannot be combined with `--graph`. Cannot be combined with
+ `--unsorted-input` if `sorted` or no argument was given.
--do-walk::
Overrides a previous `--no-walk`.
diff --git a/builtin/log.c b/builtin/log.c
index 3d7717ba5c..f75d87e8d7 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -637,7 +637,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
repo_init_revisions(the_repository, &rev, prefix);
rev.diff = 1;
rev.always_show_header = 1;
- rev.no_walk = REVISION_WALK_NO_WALK_SORTED;
+ rev.no_walk = 1;
rev.diffopt.stat_width = -1; /* Scale to real terminal size */
memset(&opt, 0, sizeof(opt));
diff --git a/builtin/revert.c b/builtin/revert.c
index 237f2f18d4..2e13660e4b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -191,7 +191,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
struct setup_revision_opt s_r_opt;
opts->revs = xmalloc(sizeof(*opts->revs));
repo_init_revisions(the_repository, opts->revs, NULL);
- opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
+ opts->revs->no_walk = 1;
+ opts->revs->unsorted_input = 1;
if (argc < 2)
usage_with_options(usage_str, options);
if (!strcmp(argv[1], "-"))
diff --git a/commit-graph.c b/commit-graph.c
index 3860a0d847..00614acd65 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -723,7 +723,7 @@ void close_commit_graph(struct raw_object_store *o)
o->commit_graph = NULL;
}
-static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
+static int bsearch_graph(struct commit_graph *g, const struct object_id *oid, uint32_t *pos)
{
return bsearch_hash(oid->hash, g->chunk_oid_fanout,
g->chunk_oid_lookup, g->hash_len, pos);
@@ -864,26 +864,55 @@ static int fill_commit_in_graph(struct repository *r,
return 1;
}
-static int find_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t *pos)
+static int search_commit_pos_in_graph(const struct object_id *id, struct commit_graph *g, uint32_t *pos)
+{
+ struct commit_graph *cur_g = g;
+ uint32_t lex_index;
+
+ while (cur_g && !bsearch_graph(cur_g, id, &lex_index))
+ cur_g = cur_g->base_graph;
+
+ if (cur_g) {
+ *pos = lex_index + cur_g->num_commits_in_base;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int find_commit_pos_in_graph(struct commit *item, struct commit_graph *g, uint32_t *pos)
{
uint32_t graph_pos = commit_graph_position(item);
if (graph_pos != COMMIT_NOT_FROM_GRAPH) {
*pos = graph_pos;
return 1;
} else {
- struct commit_graph *cur_g = g;
- uint32_t lex_index;
+ return search_commit_pos_in_graph(&item->object.oid, g, pos);
+ }
+}
- while (cur_g && !bsearch_graph(cur_g, &(item->object.oid), &lex_index))
- cur_g = cur_g->base_graph;
+struct commit *lookup_commit_in_graph(struct repository *repo, const struct object_id *id)
+{
+ struct commit *commit;
+ uint32_t pos;
- if (cur_g) {
- *pos = lex_index + cur_g->num_commits_in_base;
- return 1;
- }
+ if (!repo->objects->commit_graph)
+ return NULL;
+ if (!search_commit_pos_in_graph(id, repo->objects->commit_graph, &pos))
+ return NULL;
+ if (!repo_has_object_file(repo, id))
+ return NULL;
- return 0;
- }
+ commit = lookup_commit(repo, id);
+ if (!commit)
+ return NULL;
+ if (commit->object.parsed)
+ return commit;
+
+ if (!fill_commit_in_graph(repo, commit, repo->objects->commit_graph, pos))
+ return NULL;
+
+ return commit;
}
static int parse_commit_in_graph_one(struct repository *r,
@@ -895,7 +924,7 @@ static int parse_commit_in_graph_one(struct repository *r,
if (item->object.parsed)
return 1;
- if (find_commit_in_graph(item, g, &pos))
+ if (find_commit_pos_in_graph(item, g, &pos))
return fill_commit_in_graph(r, item, g, pos);
return 0;
@@ -921,7 +950,7 @@ void load_commit_graph_info(struct repository *r, struct commit *item)
uint32_t pos;
if (!prepare_commit_graph(r))
return;
- if (find_commit_in_graph(item, r->objects->commit_graph, &pos))
+ if (find_commit_pos_in_graph(item, r->objects->commit_graph, &pos))
fill_commit_graph_info(item, r->objects->commit_graph, pos);
}
@@ -1091,9 +1120,9 @@ static int write_graph_chunk_data(struct hashfile *f,
edge_value += ctx->new_num_commits_in_base;
else if (ctx->new_base_graph) {
uint32_t pos;
- if (find_commit_in_graph(parent->item,
- ctx->new_base_graph,
- &pos))
+ if (find_commit_pos_in_graph(parent->item,
+ ctx->new_base_graph,
+ &pos))
edge_value = pos;
}
@@ -1122,9 +1151,9 @@ static int write_graph_chunk_data(struct hashfile *f,
edge_value += ctx->new_num_commits_in_base;
else if (ctx->new_base_graph) {
uint32_t pos;
- if (find_commit_in_graph(parent->item,
- ctx->new_base_graph,
- &pos))
+ if (find_commit_pos_in_graph(parent->item,
+ ctx->new_base_graph,
+ &pos))
edge_value = pos;
}
@@ -1235,9 +1264,9 @@ static int write_graph_chunk_extra_edges(struct hashfile *f,
edge_value += ctx->new_num_commits_in_base;
else if (ctx->new_base_graph) {
uint32_t pos;
- if (find_commit_in_graph(parent->item,
- ctx->new_base_graph,
- &pos))
+ if (find_commit_pos_in_graph(parent->item,
+ ctx->new_base_graph,
+ &pos))
edge_value = pos;
}
diff --git a/commit-graph.h b/commit-graph.h
index 96c24fb577..04a94e1830 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -41,6 +41,14 @@ int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
int parse_commit_in_graph(struct repository *r, struct commit *item);
/*
+ * Look up the given commit ID in the commit-graph. This will only return a
+ * commit if the ID exists both in the graph and in the object database such
+ * that we don't return commits whose object has been pruned. Otherwise, this
+ * function returns `NULL`.
+ */
+struct commit *lookup_commit_in_graph(struct repository *repo, const struct object_id *id);
+
+/*
* It is possible that we loaded commit contents from the commit buffer,
* but we also want to ensure the commit-graph content is correctly
* checked and filled. Fill the graph_pos and generation members of
diff --git a/connected.c b/connected.c
index b18299fdf0..b5f9523a5f 100644
--- a/connected.c
+++ b/connected.c
@@ -106,6 +106,7 @@ no_promisor_pack_found:
if (opt->progress)
strvec_pushf(&rev_list.args, "--progress=%s",
_("Checking connectivity"));
+ strvec_push(&rev_list.args, "--unsorted-input");
rev_list.git_cmd = 1;
rev_list.env = opt->env;
diff --git a/revision.c b/revision.c
index cddd0542a6..0dabb5a0bc 100644
--- a/revision.c
+++ b/revision.c
@@ -360,20 +360,18 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
unsigned int flags)
{
struct object *object;
+ struct commit *commit;
/*
- * If the repository has commit graphs, repo_parse_commit() avoids
- * reading the object buffer, so use it whenever possible.
+ * If the repository has commit graphs, we try to opportunistically
+ * look up the object ID in those graphs. Like this, we can avoid
+ * parsing commit data from disk.
*/
- if (oid_object_info(revs->repo, oid, NULL) == OBJ_COMMIT) {
- struct commit *c = lookup_commit(revs->repo, oid);
- if (!repo_parse_commit(revs->repo, c))
- object = (struct object *) c;
- else
- object = NULL;
- } else {
+ commit = lookup_commit_in_graph(revs->repo, oid);
+ if (commit)
+ object = &commit->object;
+ else
object = parse_object(revs->repo, oid);
- }
if (!object) {
if (revs->ignore_missing)
@@ -1534,7 +1532,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid,
object = get_reference(cb->all_revs, path, oid, cb->all_flags);
add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
- add_pending_oid(cb->all_revs, path, oid, cb->all_flags);
+ add_pending_object(cb->all_revs, object, path);
return 0;
}
@@ -2256,6 +2254,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--author-date-order")) {
revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
revs->topo_order = 1;
+ } else if (!strcmp(arg, "--unsorted-input")) {
+ if (revs->no_walk)
+ die(_("--unsorted-input is incompatible with --no-walk"));
+ revs->unsorted_input = 1;
} else if (!strcmp(arg, "--early-output")) {
revs->early_output = 100;
revs->topo_order = 1;
@@ -2651,16 +2653,22 @@ static int handle_revision_pseudo_opt(const char *submodule,
} else if (!strcmp(arg, "--not")) {
*flags ^= UNINTERESTING | BOTTOM;
} else if (!strcmp(arg, "--no-walk")) {
- revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+ if (!revs->no_walk && revs->unsorted_input)
+ die(_("--no-walk is incompatible with --unsorted-input"));
+ revs->no_walk = 1;
} else if (skip_prefix(arg, "--no-walk=", &optarg)) {
+ if (!revs->no_walk && revs->unsorted_input)
+ die(_("--no-walk is incompatible with --unsorted-input"));
+
/*
* Detached form ("--no-walk X" as opposed to "--no-walk=X")
* not allowed, since the argument is optional.
*/
+ revs->no_walk = 1;
if (!strcmp(optarg, "sorted"))
- revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+ revs->unsorted_input = 0;
else if (!strcmp(optarg, "unsorted"))
- revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
+ revs->unsorted_input = 1;
else
return error("invalid argument to --no-walk");
} else if (!strcmp(arg, "--do-walk")) {
@@ -3584,7 +3592,7 @@ int prepare_revision_walk(struct rev_info *revs)
if (!revs->reflog_info)
prepare_to_use_bloom_filter(revs);
- if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
+ if (!revs->unsorted_input)
commit_list_sort_by_date(&revs->commits);
if (revs->no_walk)
return 0;
diff --git a/revision.h b/revision.h
index fbb068da9f..0c65a760ee 100644
--- a/revision.h
+++ b/revision.h
@@ -79,10 +79,6 @@ struct rev_cmdline_info {
} *rev;
};
-#define REVISION_WALK_WALK 0
-#define REVISION_WALK_NO_WALK_SORTED 1
-#define REVISION_WALK_NO_WALK_UNSORTED 2
-
struct oidset;
struct topo_walk_info;
@@ -129,7 +125,8 @@ struct rev_info {
/* Traversal flags */
unsigned int dense:1,
prune:1,
- no_walk:2,
+ no_walk:1,
+ unsorted_input:1,
remove_empty_trees:1,
simplify_history:1,
show_pulls:1,
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index 12def7bcbf..ef849e5bc8 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -169,4 +169,35 @@ test_expect_success 'rev-list --count --objects' '
test_line_count = $count actual
'
+test_expect_success 'rev-list --unsorted-input results in different sorting' '
+ git rev-list --unsorted-input HEAD HEAD~ >first &&
+ git rev-list --unsorted-input HEAD~ HEAD >second &&
+ ! test_cmp first second &&
+ sort first >first.sorted &&
+ sort second >second.sorted &&
+ test_cmp first.sorted second.sorted
+'
+
+test_expect_success 'rev-list --unsorted-input incompatible with --no-walk' '
+ cat >expect <<-EOF &&
+ fatal: --no-walk is incompatible with --unsorted-input
+ EOF
+ test_must_fail git rev-list --unsorted-input --no-walk HEAD 2>error &&
+ test_cmp expect error &&
+ test_must_fail git rev-list --unsorted-input --no-walk=sorted HEAD 2>error &&
+ test_cmp expect error &&
+ test_must_fail git rev-list --unsorted-input --no-walk=unsorted HEAD 2>error &&
+ test_cmp expect error &&
+
+ cat >expect <<-EOF &&
+ fatal: --unsorted-input is incompatible with --no-walk
+ EOF
+ test_must_fail git rev-list --no-walk --unsorted-input HEAD 2>error &&
+ test_cmp expect error &&
+ test_must_fail git rev-list --no-walk=sorted --unsorted-input HEAD 2>error &&
+ test_cmp expect error &&
+ test_must_fail git rev-list --no-walk=unsorted --unsorted-input HEAD 2>error &&
+ test_cmp expect error
+'
+
test_done