Skip to content

Commit fe7df03

Browse files
pks-tgitster
authored andcommitted
fetch: speed up lookup of want refs via commit-graph
When updating our local refs based on the refs fetched from the remote, we need to iterate through all requested refs and load their respective commits such that we can determine whether they need to be appended to FETCH_HEAD or not. In cases where we're fetching from a remote with exceedingly many refs, resolving these refs can be quite expensive given that we repeatedly need to unpack object headers for each of the referenced objects. Speed this up by opportunistically trying to resolve object IDs via the commit graph. We only do so for any refs which are not in "refs/tags": more likely than not, these are going to be a commit anyway, and this lets us avoid having to unpack object headers completely in case the object is a commit that is part of the commit-graph. This significantly speeds up mirror-fetches in a real-world repository with 2.3M refs: Benchmark #1: HEAD~: git-fetch Time (mean ± σ): 56.482 s ± 0.384 s [User: 53.340 s, System: 5.365 s] Range (min … max): 56.050 s … 57.045 s 5 runs Benchmark #2: HEAD: git-fetch Time (mean ± σ): 33.727 s ± 0.170 s [User: 30.252 s, System: 5.194 s] Range (min … max): 33.452 s … 33.871 s 5 runs Summary 'HEAD: git-fetch' ran 1.67 ± 0.01 times faster than 'HEAD~: git-fetch' Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6c40894 commit fe7df03

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

builtin/fetch.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,7 +1074,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
10741074
int connectivity_checked, struct ref *ref_map)
10751075
{
10761076
struct fetch_head fetch_head;
1077-
struct commit *commit;
10781077
int url_len, i, rc = 0;
10791078
struct strbuf note = STRBUF_INIT, err = STRBUF_INIT;
10801079
struct ref_transaction *transaction = NULL;
@@ -1122,6 +1121,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
11221121
want_status <= FETCH_HEAD_IGNORE;
11231122
want_status++) {
11241123
for (rm = ref_map; rm; rm = rm->next) {
1124+
struct commit *commit = NULL;
11251125
struct ref *ref = NULL;
11261126

11271127
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
@@ -1131,11 +1131,23 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
11311131
continue;
11321132
}
11331133

1134-
commit = lookup_commit_reference_gently(the_repository,
1135-
&rm->old_oid,
1136-
1);
1137-
if (!commit)
1138-
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
1134+
/*
1135+
* References in "refs/tags/" are often going to point
1136+
* to annotated tags, which are not part of the
1137+
* commit-graph. We thus only try to look up refs in
1138+
* the graph which are not in that namespace to not
1139+
* regress performance in repositories with many
1140+
* annotated tags.
1141+
*/
1142+
if (!starts_with(rm->name, "refs/tags/"))
1143+
commit = lookup_commit_in_graph(the_repository, &rm->old_oid);
1144+
if (!commit) {
1145+
commit = lookup_commit_reference_gently(the_repository,
1146+
&rm->old_oid,
1147+
1);
1148+
if (!commit)
1149+
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
1150+
}
11391151

11401152
if (rm->fetch_head_status != want_status)
11411153
continue;

0 commit comments

Comments
 (0)