Merge branch 'ps/fetch-mirror-optim'

Various optimization for "git fetch".

* ps/fetch-mirror-optim:
  refs/files-backend: optimize reading of symbolic refs
  remote: read symbolic refs via `refs_read_symbolic_ref()`
  refs: add ability for backends to special-case reading of symbolic refs
  fetch: avoid lookup of commits when not appending to FETCH_HEAD
  upload-pack: look up "want" lines via commit-graph
This commit is contained in:
Junio C Hamano 2022-03-16 17:53:07 -07:00
commit 6969ac64bf
10 changed files with 122 additions and 33 deletions

View File

@ -1146,7 +1146,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
want_status <= FETCH_HEAD_IGNORE;
want_status++) {
for (rm = ref_map; rm; rm = rm->next) {
struct commit *commit = NULL;
struct ref *ref = NULL;
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
@ -1157,21 +1156,34 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
}
/*
* References in "refs/tags/" are often going to point
* to annotated tags, which are not part of the
* commit-graph. We thus only try to look up refs in
* the graph which are not in that namespace to not
* regress performance in repositories with many
* annotated tags.
* When writing FETCH_HEAD we need to determine whether
* we already have the commit or not. If not, then the
* reference is not for merge and needs to be written
* to the reflog after other commits which we already
* have. We're not interested in this property though
* in case FETCH_HEAD is not to be updated, so we can
* skip the classification in that case.
*/
if (!starts_with(rm->name, "refs/tags/"))
commit = lookup_commit_in_graph(the_repository, &rm->old_oid);
if (!commit) {
commit = lookup_commit_reference_gently(the_repository,
&rm->old_oid,
1);
if (!commit)
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
if (fetch_head->fp) {
struct commit *commit = NULL;
/*
* References in "refs/tags/" are often going to point
* to annotated tags, which are not part of the
* commit-graph. We thus only try to look up refs in
* the graph which are not in that namespace to not
* regress performance in repositories with many
* annotated tags.
*/
if (!starts_with(rm->name, "refs/tags/"))
commit = lookup_commit_in_graph(the_repository, &rm->old_oid);
if (!commit) {
commit = lookup_commit_reference_gently(the_repository,
&rm->old_oid,
1);
if (!commit)
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
}
}
if (rm->fetch_head_status != want_status)

View File

@ -766,13 +766,15 @@ static int mv(int argc, const char **argv)
for_each_ref(read_remote_branches, &rename);
for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + i;
int flag = 0;
struct strbuf referent = STRBUF_INIT;
read_ref_full(item->string, RESOLVE_REF_READING, NULL, &flag);
if (!(flag & REF_ISSYMREF))
if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string,
&referent))
continue;
if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
die(_("deleting '%s' failed"), item->string);
strbuf_release(&referent);
}
for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + i;

17
refs.c
View File

@ -1673,6 +1673,23 @@ int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
type, failure_errno);
}
int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
struct strbuf *referent)
{
struct object_id oid;
int ret, failure_errno = 0;
unsigned int type = 0;
if (ref_store->be->read_symbolic_ref)
return ref_store->be->read_symbolic_ref(ref_store, refname, referent);
ret = refs_read_raw_ref(ref_store, refname, &oid, referent, &type, &failure_errno);
if (ret || !(type & REF_ISSYMREF))
return -1;
return 0;
}
const char *refs_resolve_ref_unsafe(struct ref_store *refs,
const char *refname,
int resolve_flags,

3
refs.h
View File

@ -82,6 +82,9 @@ int read_ref_full(const char *refname, int resolve_flags,
struct object_id *oid, int *flags);
int read_ref(const char *refname, struct object_id *oid);
int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
struct strbuf *referent);
/*
* Return 0 if a reference named refname could be created without
* conflicting with the name of an existing reference. Otherwise,

View File

@ -435,6 +435,7 @@ struct ref_storage_be refs_be_debug = {
debug_ref_iterator_begin,
debug_read_raw_ref,
NULL,
debug_reflog_iterator_begin,
debug_for_each_reflog_ent,

View File

@ -338,9 +338,9 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
return refs->loose;
}
static int files_read_raw_ref(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent,
unsigned int *type, int *failure_errno)
static int read_ref_internal(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent,
unsigned int *type, int *failure_errno, int skip_packed_refs)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
@ -381,7 +381,7 @@ stat_ref:
if (lstat(path, &st) < 0) {
int ignore_errno;
myerr = errno;
if (myerr != ENOENT)
if (myerr != ENOENT || skip_packed_refs)
goto out;
if (refs_read_raw_ref(refs->packed_ref_store, refname, oid,
referent, type, &ignore_errno)) {
@ -425,7 +425,8 @@ stat_ref:
* ref is supposed to be, there could still be a
* packed ref:
*/
if (refs_read_raw_ref(refs->packed_ref_store, refname, oid,
if (skip_packed_refs ||
refs_read_raw_ref(refs->packed_ref_store, refname, oid,
referent, type, &ignore_errno)) {
myerr = EISDIR;
goto out;
@ -470,6 +471,27 @@ out:
return ret;
}
static int files_read_raw_ref(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent,
unsigned int *type, int *failure_errno)
{
return read_ref_internal(ref_store, refname, oid, referent, type, failure_errno, 0);
}
static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
struct strbuf *referent)
{
struct object_id oid;
int failure_errno, ret;
unsigned int type;
ret = read_ref_internal(ref_store, refname, &oid, referent, &type, &failure_errno, 1);
if (ret)
return ret;
return !(type & REF_ISSYMREF);
}
int parse_loose_ref_contents(const char *buf, struct object_id *oid,
struct strbuf *referent, unsigned int *type,
int *failure_errno)
@ -3286,6 +3308,7 @@ struct ref_storage_be refs_be_files = {
files_ref_iterator_begin,
files_read_raw_ref,
files_read_symbolic_ref,
files_reflog_iterator_begin,
files_for_each_reflog_ent,

View File

@ -1684,6 +1684,7 @@ struct ref_storage_be refs_be_packed = {
packed_ref_iterator_begin,
packed_read_raw_ref,
NULL,
packed_reflog_iterator_begin,
packed_for_each_reflog_ent,

View File

@ -649,6 +649,21 @@ typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent,
unsigned int *type, int *failure_errno);
/*
* Read a symbolic reference from the specified reference store. This function
* is optional: if not implemented by a backend, then `read_raw_ref_fn` is used
* to read the symbolcic reference instead. It is intended to be implemented
* only in case the backend can optimize the reading of symbolic references.
*
* Return 0 on success, or -1 on failure. `referent` will be set to the target
* of the symbolic reference on success. This function explicitly does not
* distinguish between error cases and the reference not being a symbolic
* reference to allow backends to optimize this operation in case symbolic and
* non-symbolic references are treated differently.
*/
typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refname,
struct strbuf *referent);
struct ref_storage_be {
struct ref_storage_be *next;
const char *name;
@ -668,6 +683,7 @@ struct ref_storage_be {
ref_iterator_begin_fn *iterator_begin;
read_raw_ref_fn *read_raw_ref;
read_symbolic_ref_fn *read_symbolic_ref;
reflog_iterator_begin_fn *reflog_iterator_begin;
for_each_reflog_ent_fn *for_each_reflog_ent;

View File

@ -1945,13 +1945,9 @@ const char *branch_get_push(struct branch *branch, struct strbuf *err)
return branch->push_tracking_ref;
}
static int ignore_symref_update(const char *refname)
static int ignore_symref_update(const char *refname, struct strbuf *scratch)
{
int flag;
if (!resolve_ref_unsafe(refname, 0, NULL, &flag))
return 0; /* non-existing refs are OK */
return (flag & REF_ISSYMREF);
return !refs_read_symbolic_ref(get_main_ref_store(the_repository), refname, scratch);
}
/*
@ -1964,6 +1960,7 @@ static int ignore_symref_update(const char *refname)
static struct ref *get_expanded_map(const struct ref *remote_refs,
const struct refspec_item *refspec)
{
struct strbuf scratch = STRBUF_INIT;
const struct ref *ref;
struct ref *ret = NULL;
struct ref **tail = &ret;
@ -1971,11 +1968,13 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
for (ref = remote_refs; ref; ref = ref->next) {
char *expn_name = NULL;
strbuf_reset(&scratch);
if (strchr(ref->name, '^'))
continue; /* a dereference item */
if (match_name_with_pattern(refspec->src, ref->name,
refspec->dst, &expn_name) &&
!ignore_symref_update(expn_name)) {
!ignore_symref_update(expn_name, &scratch)) {
struct ref *cpy = copy_ref(ref);
cpy->peer_ref = alloc_ref(expn_name);
@ -1987,6 +1986,7 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
free(expn_name);
}
strbuf_release(&scratch);
return ret;
}

View File

@ -1400,13 +1400,19 @@ static int parse_want(struct packet_writer *writer, const char *line,
const char *arg;
if (skip_prefix(line, "want ", &arg)) {
struct object_id oid;
struct commit *commit;
struct object *o;
if (get_oid_hex(arg, &oid))
die("git upload-pack: protocol error, "
"expected to get oid, not '%s'", line);
o = parse_object(the_repository, &oid);
commit = lookup_commit_in_graph(the_repository, &oid);
if (commit)
o = &commit->object;
else
o = parse_object(the_repository, &oid);
if (!o) {
packet_writer_error(writer,
"upload-pack: not our ref %s",
@ -1434,7 +1440,7 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
if (skip_prefix(line, "want-ref ", &refname_nons)) {
struct object_id oid;
struct string_list_item *item;
struct object *o;
struct object *o = NULL;
struct strbuf refname = STRBUF_INIT;
strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons);
@ -1448,7 +1454,15 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
item = string_list_append(wanted_refs, refname_nons);
item->util = oiddup(&oid);
o = parse_object_or_die(&oid, refname_nons);
if (!starts_with(refname_nons, "refs/tags/")) {
struct commit *commit = lookup_commit_in_graph(the_repository, &oid);
if (commit)
o = &commit->object;
}
if (!o)
o = parse_object_or_die(&oid, refname_nons);
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
add_object_array(o, NULL, want_obj);