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
This commit is contained in:
@ -968,6 +968,11 @@ list of the missing objects. Object IDs are prefixed with a ``?'' character.
|
|||||||
objects.
|
objects.
|
||||||
endif::git-rev-list[]
|
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)]::
|
--no-walk[=(sorted|unsorted)]::
|
||||||
Only show the given commits, but do not traverse their ancestors.
|
Only show the given commits, but do not traverse their ancestors.
|
||||||
This has no effect if a range is specified. If the argument
|
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
|
given on the command line. Otherwise (if `sorted` or no argument
|
||||||
was given), the commits are shown in reverse chronological order
|
was given), the commits are shown in reverse chronological order
|
||||||
by commit time.
|
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::
|
--do-walk::
|
||||||
Overrides a previous `--no-walk`.
|
Overrides a previous `--no-walk`.
|
||||||
|
@ -637,7 +637,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
|
|||||||
repo_init_revisions(the_repository, &rev, prefix);
|
repo_init_revisions(the_repository, &rev, prefix);
|
||||||
rev.diff = 1;
|
rev.diff = 1;
|
||||||
rev.always_show_header = 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 */
|
rev.diffopt.stat_width = -1; /* Scale to real terminal size */
|
||||||
|
|
||||||
memset(&opt, 0, sizeof(opt));
|
memset(&opt, 0, sizeof(opt));
|
||||||
|
@ -191,7 +191,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
|
|||||||
struct setup_revision_opt s_r_opt;
|
struct setup_revision_opt s_r_opt;
|
||||||
opts->revs = xmalloc(sizeof(*opts->revs));
|
opts->revs = xmalloc(sizeof(*opts->revs));
|
||||||
repo_init_revisions(the_repository, opts->revs, NULL);
|
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)
|
if (argc < 2)
|
||||||
usage_with_options(usage_str, options);
|
usage_with_options(usage_str, options);
|
||||||
if (!strcmp(argv[1], "-"))
|
if (!strcmp(argv[1], "-"))
|
||||||
|
@ -723,7 +723,7 @@ void close_commit_graph(struct raw_object_store *o)
|
|||||||
o->commit_graph = NULL;
|
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,
|
return bsearch_hash(oid->hash, g->chunk_oid_fanout,
|
||||||
g->chunk_oid_lookup, g->hash_len, pos);
|
g->chunk_oid_lookup, g->hash_len, pos);
|
||||||
@ -864,28 +864,57 @@ static int fill_commit_in_graph(struct repository *r,
|
|||||||
return 1;
|
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);
|
uint32_t graph_pos = commit_graph_position(item);
|
||||||
if (graph_pos != COMMIT_NOT_FROM_GRAPH) {
|
if (graph_pos != COMMIT_NOT_FROM_GRAPH) {
|
||||||
*pos = graph_pos;
|
*pos = graph_pos;
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
struct commit_graph *cur_g = g;
|
return search_commit_pos_in_graph(&item->object.oid, g, pos);
|
||||||
uint32_t lex_index;
|
|
||||||
|
|
||||||
while (cur_g && !bsearch_graph(cur_g, &(item->object.oid), &lex_index))
|
|
||||||
cur_g = cur_g->base_graph;
|
|
||||||
|
|
||||||
if (cur_g) {
|
|
||||||
*pos = lex_index + cur_g->num_commits_in_base;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct commit *lookup_commit_in_graph(struct repository *repo, const struct object_id *id)
|
||||||
|
{
|
||||||
|
struct commit *commit;
|
||||||
|
uint32_t pos;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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,
|
static int parse_commit_in_graph_one(struct repository *r,
|
||||||
struct commit_graph *g,
|
struct commit_graph *g,
|
||||||
struct commit *item)
|
struct commit *item)
|
||||||
@ -895,7 +924,7 @@ static int parse_commit_in_graph_one(struct repository *r,
|
|||||||
if (item->object.parsed)
|
if (item->object.parsed)
|
||||||
return 1;
|
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 fill_commit_in_graph(r, item, g, pos);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -921,7 +950,7 @@ void load_commit_graph_info(struct repository *r, struct commit *item)
|
|||||||
uint32_t pos;
|
uint32_t pos;
|
||||||
if (!prepare_commit_graph(r))
|
if (!prepare_commit_graph(r))
|
||||||
return;
|
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);
|
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;
|
edge_value += ctx->new_num_commits_in_base;
|
||||||
else if (ctx->new_base_graph) {
|
else if (ctx->new_base_graph) {
|
||||||
uint32_t pos;
|
uint32_t pos;
|
||||||
if (find_commit_in_graph(parent->item,
|
if (find_commit_pos_in_graph(parent->item,
|
||||||
ctx->new_base_graph,
|
ctx->new_base_graph,
|
||||||
&pos))
|
&pos))
|
||||||
edge_value = 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;
|
edge_value += ctx->new_num_commits_in_base;
|
||||||
else if (ctx->new_base_graph) {
|
else if (ctx->new_base_graph) {
|
||||||
uint32_t pos;
|
uint32_t pos;
|
||||||
if (find_commit_in_graph(parent->item,
|
if (find_commit_pos_in_graph(parent->item,
|
||||||
ctx->new_base_graph,
|
ctx->new_base_graph,
|
||||||
&pos))
|
&pos))
|
||||||
edge_value = 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;
|
edge_value += ctx->new_num_commits_in_base;
|
||||||
else if (ctx->new_base_graph) {
|
else if (ctx->new_base_graph) {
|
||||||
uint32_t pos;
|
uint32_t pos;
|
||||||
if (find_commit_in_graph(parent->item,
|
if (find_commit_pos_in_graph(parent->item,
|
||||||
ctx->new_base_graph,
|
ctx->new_base_graph,
|
||||||
&pos))
|
&pos))
|
||||||
edge_value = pos;
|
edge_value = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,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);
|
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,
|
* It is possible that we loaded commit contents from the commit buffer,
|
||||||
* but we also want to ensure the commit-graph content is correctly
|
* but we also want to ensure the commit-graph content is correctly
|
||||||
|
@ -106,6 +106,7 @@ no_promisor_pack_found:
|
|||||||
if (opt->progress)
|
if (opt->progress)
|
||||||
strvec_pushf(&rev_list.args, "--progress=%s",
|
strvec_pushf(&rev_list.args, "--progress=%s",
|
||||||
_("Checking connectivity"));
|
_("Checking connectivity"));
|
||||||
|
strvec_push(&rev_list.args, "--unsorted-input");
|
||||||
|
|
||||||
rev_list.git_cmd = 1;
|
rev_list.git_cmd = 1;
|
||||||
rev_list.env = opt->env;
|
rev_list.env = opt->env;
|
||||||
|
38
revision.c
38
revision.c
@ -360,20 +360,18 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
|
|||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct object *object;
|
struct object *object;
|
||||||
|
struct commit *commit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the repository has commit graphs, repo_parse_commit() avoids
|
* If the repository has commit graphs, we try to opportunistically
|
||||||
* reading the object buffer, so use it whenever possible.
|
* 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) {
|
commit = lookup_commit_in_graph(revs->repo, oid);
|
||||||
struct commit *c = lookup_commit(revs->repo, oid);
|
if (commit)
|
||||||
if (!repo_parse_commit(revs->repo, c))
|
object = &commit->object;
|
||||||
object = (struct object *) c;
|
else
|
||||||
else
|
|
||||||
object = NULL;
|
|
||||||
} else {
|
|
||||||
object = parse_object(revs->repo, oid);
|
object = parse_object(revs->repo, oid);
|
||||||
}
|
|
||||||
|
|
||||||
if (!object) {
|
if (!object) {
|
||||||
if (revs->ignore_missing)
|
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);
|
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_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;
|
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")) {
|
} else if (!strcmp(arg, "--author-date-order")) {
|
||||||
revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
|
revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
|
||||||
revs->topo_order = 1;
|
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")) {
|
} else if (!strcmp(arg, "--early-output")) {
|
||||||
revs->early_output = 100;
|
revs->early_output = 100;
|
||||||
revs->topo_order = 1;
|
revs->topo_order = 1;
|
||||||
@ -2651,16 +2653,22 @@ static int handle_revision_pseudo_opt(const char *submodule,
|
|||||||
} else if (!strcmp(arg, "--not")) {
|
} else if (!strcmp(arg, "--not")) {
|
||||||
*flags ^= UNINTERESTING | BOTTOM;
|
*flags ^= UNINTERESTING | BOTTOM;
|
||||||
} else if (!strcmp(arg, "--no-walk")) {
|
} 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)) {
|
} 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")
|
* Detached form ("--no-walk X" as opposed to "--no-walk=X")
|
||||||
* not allowed, since the argument is optional.
|
* not allowed, since the argument is optional.
|
||||||
*/
|
*/
|
||||||
|
revs->no_walk = 1;
|
||||||
if (!strcmp(optarg, "sorted"))
|
if (!strcmp(optarg, "sorted"))
|
||||||
revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
|
revs->unsorted_input = 0;
|
||||||
else if (!strcmp(optarg, "unsorted"))
|
else if (!strcmp(optarg, "unsorted"))
|
||||||
revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
|
revs->unsorted_input = 1;
|
||||||
else
|
else
|
||||||
return error("invalid argument to --no-walk");
|
return error("invalid argument to --no-walk");
|
||||||
} else if (!strcmp(arg, "--do-walk")) {
|
} else if (!strcmp(arg, "--do-walk")) {
|
||||||
@ -3584,7 +3592,7 @@ int prepare_revision_walk(struct rev_info *revs)
|
|||||||
|
|
||||||
if (!revs->reflog_info)
|
if (!revs->reflog_info)
|
||||||
prepare_to_use_bloom_filter(revs);
|
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);
|
commit_list_sort_by_date(&revs->commits);
|
||||||
if (revs->no_walk)
|
if (revs->no_walk)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -79,10 +79,6 @@ struct rev_cmdline_info {
|
|||||||
} *rev;
|
} *rev;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define REVISION_WALK_WALK 0
|
|
||||||
#define REVISION_WALK_NO_WALK_SORTED 1
|
|
||||||
#define REVISION_WALK_NO_WALK_UNSORTED 2
|
|
||||||
|
|
||||||
struct oidset;
|
struct oidset;
|
||||||
struct topo_walk_info;
|
struct topo_walk_info;
|
||||||
|
|
||||||
@ -129,7 +125,8 @@ struct rev_info {
|
|||||||
/* Traversal flags */
|
/* Traversal flags */
|
||||||
unsigned int dense:1,
|
unsigned int dense:1,
|
||||||
prune:1,
|
prune:1,
|
||||||
no_walk:2,
|
no_walk:1,
|
||||||
|
unsorted_input:1,
|
||||||
remove_empty_trees:1,
|
remove_empty_trees:1,
|
||||||
simplify_history:1,
|
simplify_history:1,
|
||||||
show_pulls:1,
|
show_pulls:1,
|
||||||
|
@ -169,4 +169,35 @@ test_expect_success 'rev-list --count --objects' '
|
|||||||
test_line_count = $count actual
|
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
|
test_done
|
||||||
|
Reference in New Issue
Block a user