Skip to content

Commit 0d6c40f

Browse files
committed
This commit introduces a new configuration option,
remote.<name>.prefetchref, which allows users to specify specific ref patterns to be prefetched during a git fetch --prefetch operation. The new option accepts a space-separated list of ref patterns. When the --prefetch option is used with git fetch, only the refs matching these patterns will be prefetched, instead of the default behavior of prefetching all fetchable refs. Example usage in .git/config: [remote "origin"] prefetchref = "refs/heads/main refs/heads/feature/*" This change allows users to optimize their prefetch operations, potentially reducing network traffic and improving performance for large repositories with many refs. This change allows users to optimize their prefetch operations, potentially reducing network traffic and improving performance for large repositories with many refs. Signed-off-by: Shubham Kanodia <[email protected]>
1 parent 2e7b89e commit 0d6c40f

File tree

5 files changed

+115
-1
lines changed

5 files changed

+115
-1
lines changed

Documentation/config/remote.txt

+6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ remote.<name>.fetch::
3333
The default set of "refspec" for linkgit:git-fetch[1]. See
3434
linkgit:git-fetch[1].
3535

36+
remote.<name>.prefetchref::
37+
Specify the refs to be prefetched when fetching from this remote.
38+
The value is a space-separated list of ref patterns (e.g., "refs/heads/master refs/heads/develop*").
39+
These patterns are used as the source part of the refspecs for prefetching.
40+
This can be used to optimize fetch operations by specifying exactly which refs should be prefetched.
41+
3642
remote.<name>.push::
3743
The default set of "refspec" for linkgit:git-push[1]. See
3844
linkgit:git-push[1].

builtin/fetch.c

+28-1
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,30 @@ static void find_non_local_tags(const struct ref *refs,
434434
oidset_clear(&fetch_oids);
435435
}
436436

437+
static void apply_prefetch_refspec(struct remote *remote, struct refspec *rs)
438+
{
439+
if (remote->prefetch_refs.nr > 0) {
440+
int i;
441+
for (i = 0; i < remote->prefetch_refs.nr; i++) {
442+
const char *src = remote->prefetch_refs.items[i].string;
443+
struct strbuf dst = STRBUF_INIT;
444+
445+
strbuf_addf(&dst, "refs/prefetch/%s/", remote->name);
446+
if (starts_with(src, "refs/heads/")) {
447+
strbuf_addstr(&dst, src + 11);
448+
} else if (starts_with(src, "refs/")) {
449+
strbuf_addstr(&dst, src + 5);
450+
} else {
451+
strbuf_addstr(&dst, src);
452+
}
453+
454+
refspec_appendf(rs, "%s:%s", src, dst.buf);
455+
strbuf_release(&dst);
456+
}
457+
}
458+
}
459+
460+
437461
static void filter_prefetch_refspec(struct refspec *rs)
438462
{
439463
int i;
@@ -502,8 +526,11 @@ static struct ref *get_ref_map(struct remote *remote,
502526
int existing_refs_populated = 0;
503527

504528
filter_prefetch_refspec(rs);
505-
if (remote)
529+
if (remote) {
506530
filter_prefetch_refspec(&remote->fetch);
531+
if (prefetch)
532+
apply_prefetch_refspec(remote, rs);
533+
}
507534

508535
if (rs->nr) {
509536
struct refspec *fetch_refspec;

remote.c

+8
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ static struct remote *make_remote(struct remote_state *remote_state,
141141
ret->prune = -1; /* unspecified */
142142
ret->prune_tags = -1; /* unspecified */
143143
ret->name = xstrndup(name, len);
144+
string_list_init_dup(&ret->prefetch_refs);
144145
refspec_init(&ret->push, REFSPEC_PUSH);
145146
refspec_init(&ret->fetch, REFSPEC_FETCH);
146147

@@ -166,6 +167,7 @@ static void remote_clear(struct remote *remote)
166167
free((char *)remote->uploadpack);
167168
FREE_AND_NULL(remote->http_proxy);
168169
FREE_AND_NULL(remote->http_proxy_authmethod);
170+
string_list_clear(&remote->prefetch_refs, 0);
169171
}
170172

171173
static void add_merge(struct branch *branch, const char *name)
@@ -456,6 +458,12 @@ static int handle_config(const char *key, const char *value,
456458
remote->prune = git_config_bool(key, value);
457459
else if (!strcmp(subkey, "prunetags"))
458460
remote->prune_tags = git_config_bool(key, value);
461+
else if (!strcmp(subkey, "prefetchref")) {
462+
if (!value)
463+
return config_error_nonbool(key);
464+
string_list_split(&remote->prefetch_refs, value, ' ', -1);
465+
return 0;
466+
}
459467
else if (!strcmp(subkey, "url")) {
460468
if (!value)
461469
return config_error_nonbool(key);

remote.h

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "hashmap.h"
66
#include "refspec.h"
77
#include "strvec.h"
8+
#include "string-list.h"
89

910
struct option;
1011
struct transport_ls_refs_options;
@@ -77,6 +78,8 @@ struct remote {
7778

7879
struct refspec fetch;
7980

81+
struct string_list prefetch_refs;
82+
8083
/*
8184
* The setting for whether to fetch tags (as a separate rule from the
8285
* configured refspecs);

t/t7900-maintenance.sh

+70
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,76 @@ test_expect_success 'prefetch multiple remotes' '
245245
test_subcommand git fetch remote2 $fetchargs <skip-remote1.txt
246246
'
247247

248+
test_expect_success 'prefetch only acts on remote.<name>.prefetchref refs if present' '
249+
test_create_repo prefetch-test-mixed-patterns &&
250+
(
251+
cd prefetch-test-mixed-patterns &&
252+
test_commit initial &&
253+
git clone . clone1 &&
254+
git clone . clone2 &&
255+
256+
git remote add remote1 "file://$(pwd)/clone1" &&
257+
git remote add remote2 "file://$(pwd)/clone2" &&
258+
259+
# Set single prefetchref pattern for remote1 and multiple for remote2
260+
git config remote.remote1.prefetchref "refs/heads/foo" &&
261+
git config remote.remote2.prefetchref "refs/heads/feature/* refs/heads/topic" &&
262+
263+
# Create branches in clone1 and push
264+
(
265+
cd clone1 &&
266+
git checkout -b foo &&
267+
test_commit foo-commit &&
268+
git checkout -b feature/a &&
269+
test_commit feature-a-commit &&
270+
git checkout -b other &&
271+
test_commit other-commit &&
272+
git push origin foo feature/a other
273+
) &&
274+
275+
# Create branches in clone2 and push
276+
(
277+
cd clone2 &&
278+
git checkout -b topic &&
279+
test_commit master-commit &&
280+
git checkout -b feature/x &&
281+
test_commit feature-x-commit &&
282+
git checkout -b feature/y &&
283+
test_commit feature-y-commit &&
284+
git checkout -b dev &&
285+
test_commit dev-commit &&
286+
git push origin topic feature/x feature/y dev
287+
) &&
288+
289+
# Run maintenance prefetch task
290+
GIT_TRACE2_EVENT="$(pwd)/prefetch.txt" git maintenance run --task=prefetch 2>/dev/null &&
291+
292+
# Check that only specified refs were prefetched
293+
fetchargs="--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet" &&
294+
test_subcommand git fetch remote1 $fetchargs <prefetch.txt &&
295+
test_subcommand git fetch remote2 $fetchargs <prefetch.txt &&
296+
ls -R .git/refs/prefetch &&
297+
298+
# Verify that only specified refs are in the prefetch refs for remote1
299+
git rev-parse refs/prefetch/remotes/remote1/foo &&
300+
test_must_fail git rev-parse refs/prefetch/remotes/remote1/feature/a &&
301+
test_must_fail git rev-parse refs/prefetch/remotes/remote1/other &&
302+
303+
# Verify that only specified refs are in the prefetch refs for remote2
304+
git rev-parse refs/prefetch/remotes/remote2/feature/x &&
305+
git rev-parse refs/prefetch/remotes/remote2/feature/y &&
306+
git rev-parse refs/prefetch/remotes/remote2/topic &&
307+
test_must_fail git rev-parse refs/prefetch/remotes/remote2/dev &&
308+
309+
# Fetch all refs and compare
310+
git fetch --all &&
311+
test_cmp_rev refs/remotes/remote1/foo refs/prefetch/remotes/remote1/foo &&
312+
test_cmp_rev refs/remotes/remote2/feature/x refs/prefetch/remotes/remote2/feature/x &&
313+
test_cmp_rev refs/remotes/remote2/feature/y refs/prefetch/remotes/remote2/feature/y &&
314+
test_cmp_rev refs/remotes/remote2/topic refs/prefetch/remotes/remote2/topic
315+
)
316+
'
317+
248318
test_expect_success 'loose-objects task' '
249319
# Repack everything so we know the state of the object dir
250320
git repack -adk &&

0 commit comments

Comments
 (0)