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 <= FETCH_HEAD_IGNORE;
want_status++) { want_status++) {
for (rm = ref_map; rm; rm = rm->next) { for (rm = ref_map; rm; rm = rm->next) {
struct commit *commit = NULL;
struct ref *ref = NULL; struct ref *ref = NULL;
if (rm->status == REF_STATUS_REJECT_SHALLOW) { 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 * When writing FETCH_HEAD we need to determine whether
* to annotated tags, which are not part of the * we already have the commit or not. If not, then the
* commit-graph. We thus only try to look up refs in * reference is not for merge and needs to be written
* the graph which are not in that namespace to not * to the reflog after other commits which we already
* regress performance in repositories with many * have. We're not interested in this property though
* annotated tags. * 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/")) if (fetch_head->fp) {
commit = lookup_commit_in_graph(the_repository, &rm->old_oid); struct commit *commit = NULL;
if (!commit) {
commit = lookup_commit_reference_gently(the_repository, /*
&rm->old_oid, * References in "refs/tags/" are often going to point
1); * to annotated tags, which are not part of the
if (!commit) * commit-graph. We thus only try to look up refs in
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE; * 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) 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_each_ref(read_remote_branches, &rename);
for (i = 0; i < remote_branches.nr; i++) { for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + 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 (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string,
if (!(flag & REF_ISSYMREF)) &referent))
continue; continue;
if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF)) if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
die(_("deleting '%s' failed"), item->string); die(_("deleting '%s' failed"), item->string);
strbuf_release(&referent);
} }
for (i = 0; i < remote_branches.nr; i++) { for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + 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); 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 *refs_resolve_ref_unsafe(struct ref_store *refs,
const char *refname, const char *refname,
int resolve_flags, 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); struct object_id *oid, int *flags);
int read_ref(const char *refname, struct object_id *oid); 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 * Return 0 if a reference named refname could be created without
* conflicting with the name of an existing reference. Otherwise, * 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_ref_iterator_begin,
debug_read_raw_ref, debug_read_raw_ref,
NULL,
debug_reflog_iterator_begin, debug_reflog_iterator_begin,
debug_for_each_reflog_ent, 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; return refs->loose;
} }
static int files_read_raw_ref(struct ref_store *ref_store, const char *refname, static int read_ref_internal(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent, struct object_id *oid, struct strbuf *referent,
unsigned int *type, int *failure_errno) unsigned int *type, int *failure_errno, int skip_packed_refs)
{ {
struct files_ref_store *refs = struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
@ -381,7 +381,7 @@ stat_ref:
if (lstat(path, &st) < 0) { if (lstat(path, &st) < 0) {
int ignore_errno; int ignore_errno;
myerr = errno; myerr = errno;
if (myerr != ENOENT) if (myerr != ENOENT || skip_packed_refs)
goto out; goto out;
if (refs_read_raw_ref(refs->packed_ref_store, refname, oid, if (refs_read_raw_ref(refs->packed_ref_store, refname, oid,
referent, type, &ignore_errno)) { referent, type, &ignore_errno)) {
@ -425,7 +425,8 @@ stat_ref:
* ref is supposed to be, there could still be a * ref is supposed to be, there could still be a
* packed ref: * 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)) { referent, type, &ignore_errno)) {
myerr = EISDIR; myerr = EISDIR;
goto out; goto out;
@ -470,6 +471,27 @@ out:
return ret; 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, int parse_loose_ref_contents(const char *buf, struct object_id *oid,
struct strbuf *referent, unsigned int *type, struct strbuf *referent, unsigned int *type,
int *failure_errno) int *failure_errno)
@ -3286,6 +3308,7 @@ struct ref_storage_be refs_be_files = {
files_ref_iterator_begin, files_ref_iterator_begin,
files_read_raw_ref, files_read_raw_ref,
files_read_symbolic_ref,
files_reflog_iterator_begin, files_reflog_iterator_begin,
files_for_each_reflog_ent, files_for_each_reflog_ent,

View File

@ -1684,6 +1684,7 @@ struct ref_storage_be refs_be_packed = {
packed_ref_iterator_begin, packed_ref_iterator_begin,
packed_read_raw_ref, packed_read_raw_ref,
NULL,
packed_reflog_iterator_begin, packed_reflog_iterator_begin,
packed_for_each_reflog_ent, 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, struct object_id *oid, struct strbuf *referent,
unsigned int *type, int *failure_errno); 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 {
struct ref_storage_be *next; struct ref_storage_be *next;
const char *name; const char *name;
@ -668,6 +683,7 @@ struct ref_storage_be {
ref_iterator_begin_fn *iterator_begin; ref_iterator_begin_fn *iterator_begin;
read_raw_ref_fn *read_raw_ref; read_raw_ref_fn *read_raw_ref;
read_symbolic_ref_fn *read_symbolic_ref;
reflog_iterator_begin_fn *reflog_iterator_begin; reflog_iterator_begin_fn *reflog_iterator_begin;
for_each_reflog_ent_fn *for_each_reflog_ent; 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; 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; return !refs_read_symbolic_ref(get_main_ref_store(the_repository), refname, scratch);
if (!resolve_ref_unsafe(refname, 0, NULL, &flag))
return 0; /* non-existing refs are OK */
return (flag & REF_ISSYMREF);
} }
/* /*
@ -1964,6 +1960,7 @@ static int ignore_symref_update(const char *refname)
static struct ref *get_expanded_map(const struct ref *remote_refs, static struct ref *get_expanded_map(const struct ref *remote_refs,
const struct refspec_item *refspec) const struct refspec_item *refspec)
{ {
struct strbuf scratch = STRBUF_INIT;
const struct ref *ref; const struct ref *ref;
struct ref *ret = NULL; struct ref *ret = NULL;
struct ref **tail = &ret; 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) { for (ref = remote_refs; ref; ref = ref->next) {
char *expn_name = NULL; char *expn_name = NULL;
strbuf_reset(&scratch);
if (strchr(ref->name, '^')) if (strchr(ref->name, '^'))
continue; /* a dereference item */ continue; /* a dereference item */
if (match_name_with_pattern(refspec->src, ref->name, if (match_name_with_pattern(refspec->src, ref->name,
refspec->dst, &expn_name) && refspec->dst, &expn_name) &&
!ignore_symref_update(expn_name)) { !ignore_symref_update(expn_name, &scratch)) {
struct ref *cpy = copy_ref(ref); struct ref *cpy = copy_ref(ref);
cpy->peer_ref = alloc_ref(expn_name); 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); free(expn_name);
} }
strbuf_release(&scratch);
return ret; return ret;
} }

View File

@ -1400,13 +1400,19 @@ static int parse_want(struct packet_writer *writer, const char *line,
const char *arg; const char *arg;
if (skip_prefix(line, "want ", &arg)) { if (skip_prefix(line, "want ", &arg)) {
struct object_id oid; struct object_id oid;
struct commit *commit;
struct object *o; struct object *o;
if (get_oid_hex(arg, &oid)) if (get_oid_hex(arg, &oid))
die("git upload-pack: protocol error, " die("git upload-pack: protocol error, "
"expected to get oid, not '%s'", line); "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) { if (!o) {
packet_writer_error(writer, packet_writer_error(writer,
"upload-pack: not our ref %s", "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)) { if (skip_prefix(line, "want-ref ", &refname_nons)) {
struct object_id oid; struct object_id oid;
struct string_list_item *item; struct string_list_item *item;
struct object *o; struct object *o = NULL;
struct strbuf refname = STRBUF_INIT; struct strbuf refname = STRBUF_INIT;
strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons); 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 = string_list_append(wanted_refs, refname_nons);
item->util = oiddup(&oid); 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)) { if (!(o->flags & WANTED)) {
o->flags |= WANTED; o->flags |= WANTED;
add_object_array(o, NULL, want_obj); add_object_array(o, NULL, want_obj);