Skip to content

Commit f9f9e63

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 f9f9e63

File tree

5 files changed

+158
-0
lines changed

5 files changed

+158
-0
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

+61
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "trace.h"
3939
#include "trace2.h"
4040
#include "bundle-uri.h"
41+
#include "wildmatch.h"
4142

4243
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
4344

@@ -485,6 +486,49 @@ static void filter_prefetch_refspec(struct refspec *rs)
485486
}
486487
}
487488

489+
static int matches_prefetch_refs(const char *refname, const struct string_list *prefetch_refs)
490+
{
491+
int i;
492+
int has_positive = 0;
493+
int matched_positive = 0;
494+
int matched_negative = 0;
495+
496+
for (i = 0; i < prefetch_refs->nr; i++) {
497+
const char *pattern = prefetch_refs->items[i].string;
498+
int is_negative = (*pattern == '!');
499+
500+
if (is_negative)
501+
pattern++;
502+
else
503+
has_positive = 1;
504+
505+
if (wildmatch(pattern, refname, 0) == 0) {
506+
if (is_negative)
507+
matched_negative = 1;
508+
else
509+
matched_positive = 1;
510+
}
511+
}
512+
513+
if (!has_positive)
514+
return !matched_negative;
515+
516+
return matched_positive && !matched_negative;
517+
}
518+
519+
520+
static void ref_remove(struct ref **head, struct ref *to_remove)
521+
{
522+
struct ref **pp, *p;
523+
524+
for (pp = head; (p = *pp) != NULL; pp = &p->next) {
525+
if (p == to_remove) {
526+
*pp = p->next;
527+
return;
528+
}
529+
}
530+
}
531+
488532
static struct ref *get_ref_map(struct remote *remote,
489533
const struct ref *remote_refs,
490534
struct refspec *rs,
@@ -502,6 +546,7 @@ static struct ref *get_ref_map(struct remote *remote,
502546
int existing_refs_populated = 0;
503547

504548
filter_prefetch_refspec(rs);
549+
505550
if (remote)
506551
filter_prefetch_refspec(&remote->fetch);
507552

@@ -610,6 +655,22 @@ static struct ref *get_ref_map(struct remote *remote,
610655
else
611656
ref_map = apply_negative_refspecs(ref_map, &remote->fetch);
612657

658+
/**
659+
* Filter out advertised refs that we don't want to fetch during
660+
* prefetch if a prefetchref config is set
661+
*/
662+
if (prefetch && remote->prefetch_refs.nr) {
663+
struct ref *ref, *next;
664+
for (ref = ref_map; ref; ref = next) {
665+
next = ref->next;
666+
667+
if (!matches_prefetch_refs(ref->name, &remote->prefetch_refs)) {
668+
ref_remove(&ref_map, ref);
669+
free_one_ref(ref);
670+
}
671+
}
672+
}
673+
613674
ref_map = ref_remove_duplicates(ref_map);
614675

615676
for (rm = ref_map; rm; rm = rm->next) {

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

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

248+
test_expect_success 'prefetch with positive prefetch ref patterns' '
249+
test_create_repo filter-prefetch-positive &&
250+
(
251+
cd filter-prefetch-positive &&
252+
test_commit initial &&
253+
git clone . clone2 &&
254+
git remote add remote2 "file://$(pwd)/clone2" &&
255+
256+
cd clone2 &&
257+
git checkout -b feature && test_commit feature-commit-2 &&
258+
git checkout -b wip/test && test_commit wip-test-commit-2 &&
259+
git checkout -b topic/x && test_commit topic-x-commit-2 &&
260+
git push -f origin feature wip/test topic/x&&
261+
cd .. &&
262+
263+
git config remote.remote2.prefetchref "refs/heads/feature" &&
264+
fetchargs="--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet" &&
265+
GIT_TRACE2_EVENT="$(pwd)/prefetch-positive.txt" git maintenance run --task=prefetch 2>/dev/null &&
266+
test_subcommand git fetch remote2 $fetchargs <prefetch-positive.txt &&
267+
268+
git rev-parse refs/prefetch/remotes/remote2/feature &&
269+
test_must_fail git rev-parse refs/prefetch/remotes/remote2/wip/test &&
270+
test_must_fail git rev-parse refs/prefetch/remotes/remote2/topic/x
271+
)
272+
'
273+
274+
test_expect_success 'prefetch with negative prefetch ref patterns' '
275+
test_create_repo filter-prefetch-negative &&
276+
(
277+
cd filter-prefetch-negative &&
278+
test_commit initial &&
279+
git clone . clone3 &&
280+
git remote add remote3 "file://$(pwd)/clone3" &&
281+
cat .git/config &&
282+
283+
cd clone3 &&
284+
git checkout -b feature && test_commit feature-commit-3 &&
285+
git checkout -b wip/test && test_commit wip-test-commit-3 &&
286+
git checkout -b topic/x && test_commit topic-x-commit-3 &&
287+
git push -f origin feature wip/test topic/x &&
288+
cd .. &&
289+
290+
git config remote.remote3.prefetchref "!refs/heads/wip/*" &&
291+
fetchargs="--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet" &&
292+
GIT_TRACE2_EVENT="$(pwd)/prefetch-negative.txt" git maintenance run --task=prefetch 2>/dev/null &&
293+
test_subcommand git fetch remote3 $fetchargs <prefetch-negative.txt &&
294+
git rev-parse refs/prefetch/remotes/remote3/feature &&
295+
git rev-parse refs/prefetch/remotes/remote3/topic/x &&
296+
test_must_fail git rev-parse refs/prefetch/remotes/remote3/wip/test
297+
)
298+
'
299+
300+
test_expect_success 'prefetch with positive & negative prefetch ref patterns' '
301+
test_create_repo filter-prefetch-mixed &&
302+
(
303+
cd filter-prefetch-mixed &&
304+
test_commit initial &&
305+
git clone . clone4 &&
306+
git remote add remote4 "file://$(pwd)/clone4" &&
307+
308+
cd clone4 &&
309+
git checkout -b feature && test_commit feature-commit-4 &&
310+
git checkout -b topic/x && test_commit topic-x-commit-4 &&
311+
git checkout -b topic/y && test_commit topic-y-commit-4 &&
312+
git push -f origin feature topic/x topic/y &&
313+
cd .. &&
314+
315+
git config remote.remote4.prefetchref "refs/heads/topic/* !refs/heads/topic/y" &&
316+
# git config --add remote.remote4.prefetchref "!refs/topic/y" &&
317+
cat .git/config &&
318+
fetchargs="--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet" &&
319+
GIT_TRACE2_EVENT="$(pwd)/prefetch-mixed.txt" git maintenance run --task=prefetch 2>/dev/null &&
320+
test_subcommand git fetch remote4 $fetchargs <prefetch-mixed.txt &&
321+
322+
test_must_fail git rev-parse refs/prefetch/remotes/remote4/feature &&
323+
test_must_fail git rev-parse refs/prefetch/remotes/remote4/topic/y &&
324+
git rev-parse refs/prefetch/remotes/remote4/topic/x
325+
)
326+
'
327+
248328
test_expect_success 'loose-objects task' '
249329
# Repack everything so we know the state of the object dir
250330
git repack -adk &&

0 commit comments

Comments
 (0)