Skip to content

Commit 3113d8b

Browse files
committed
remote: introduce config to set prefetch refs
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. Signed-off-by: Shubham Kanodia <[email protected]>
1 parent 2e7b89e commit 3113d8b

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)