Skip to content

Commit 465e73f

Browse files
committed
Merge branch 'tb/filter-alternate-refs'
When pushing into a repository that borrows its objects from an alternate object store, "git receive-pack" that responds to the push request on the other side lists the tips of refs in the alternate to reduce the amount of objects transferred. This sometimes is detrimental when the number of refs in the alternate is absurdly large, in which case the bandwidth saved in potentially fewer objects transferred is wasted in excessively large ref advertisement. The alternate refs that are advertised are now configurable with a pair of configuration variables. * tb/filter-alternate-refs: transport.c: introduce core.alternateRefsPrefixes transport.c: introduce core.alternateRefsCommand transport.c: extract 'fill_alternate_refs_command' transport: drop refnames from for_each_alternate_ref
2 parents 0527fba + 40f327f commit 465e73f

File tree

6 files changed

+92
-13
lines changed

6 files changed

+92
-13
lines changed

Documentation/config.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,24 @@ core.preferSymlinkRefs::
616616
This is sometimes needed to work with old scripts that
617617
expect HEAD to be a symbolic link.
618618

619+
core.alternateRefsCommand::
620+
When advertising tips of available history from an alternate, use the shell to
621+
execute the specified command instead of linkgit:git-for-each-ref[1]. The
622+
first argument is the absolute path of the alternate. Output must contain one
623+
hex object id per line (i.e., the same as produce by `git for-each-ref
624+
--format='%(objectname)'`).
625+
+
626+
Note that you cannot generally put `git for-each-ref` directly into the config
627+
value, as it does not take a repository path as an argument (but you can wrap
628+
the command above in a shell script).
629+
630+
core.alternateRefsPrefixes::
631+
When listing references from an alternate, list only references that begin
632+
with the given prefix. Prefixes match as if they were given as arguments to
633+
linkgit:git-for-each-ref[1]. To list multiple prefixes, separate them with
634+
whitespace. If `core.alternateRefsCommand` is set, setting
635+
`core.alternateRefsPrefixes` has no effect.
636+
619637
core.bare::
620638
If true this repository is assumed to be 'bare' and has no
621639
working directory associated with it. If this is the case a

builtin/receive-pack.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,7 @@ static int show_ref_cb(const char *path_full, const struct object_id *oid,
281281
return 0;
282282
}
283283

284-
static void show_one_alternate_ref(const char *refname,
285-
const struct object_id *oid,
284+
static void show_one_alternate_ref(const struct object_id *oid,
286285
void *data)
287286
{
288287
struct oidset *seen = data;

fetch-pack.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ struct alternate_object_cache {
7676
size_t nr, alloc;
7777
};
7878

79-
static void cache_one_alternate(const char *refname,
80-
const struct object_id *oid,
79+
static void cache_one_alternate(const struct object_id *oid,
8180
void *vcache)
8281
{
8382
struct alternate_object_cache *cache = vcache;

t/t5410-receive-pack-alternates.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/sh
2+
3+
test_description='git receive-pack with alternate ref filtering'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'setup' '
8+
test_commit base &&
9+
git clone -s --bare . fork &&
10+
git checkout -b public/branch master &&
11+
test_commit public &&
12+
git checkout -b private/branch master &&
13+
test_commit private
14+
'
15+
16+
extract_haves () {
17+
depacketize | perl -lne '/^(\S+) \.have/ and print $1'
18+
}
19+
20+
test_expect_success 'with core.alternateRefsCommand' '
21+
write_script fork/alternate-refs <<-\EOF &&
22+
git --git-dir="$1" for-each-ref \
23+
--format="%(objectname)" \
24+
refs/heads/public/
25+
EOF
26+
test_config -C fork core.alternateRefsCommand alternate-refs &&
27+
git rev-parse public/branch >expect &&
28+
printf "0000" | git receive-pack fork >actual &&
29+
extract_haves <actual >actual.haves &&
30+
test_cmp expect actual.haves
31+
'
32+
33+
test_expect_success 'with core.alternateRefsPrefixes' '
34+
test_config -C fork core.alternateRefsPrefixes "refs/heads/private" &&
35+
git rev-parse private/branch >expect &&
36+
printf "0000" | git receive-pack fork >actual &&
37+
extract_haves <actual >actual.haves &&
38+
test_cmp expect actual.haves
39+
'
40+
41+
test_done

transport.c

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,33 @@ char *transport_anonymize_url(const char *url)
13701370
return xstrdup(url);
13711371
}
13721372

1373+
static void fill_alternate_refs_command(struct child_process *cmd,
1374+
const char *repo_path)
1375+
{
1376+
const char *value;
1377+
1378+
if (!git_config_get_value("core.alternateRefsCommand", &value)) {
1379+
cmd->use_shell = 1;
1380+
1381+
argv_array_push(&cmd->args, value);
1382+
argv_array_push(&cmd->args, repo_path);
1383+
} else {
1384+
cmd->git_cmd = 1;
1385+
1386+
argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
1387+
argv_array_push(&cmd->args, "for-each-ref");
1388+
argv_array_push(&cmd->args, "--format=%(objectname)");
1389+
1390+
if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
1391+
argv_array_push(&cmd->args, "--");
1392+
argv_array_split(&cmd->args, value);
1393+
}
1394+
}
1395+
1396+
cmd->env = local_repo_env;
1397+
cmd->out = -1;
1398+
}
1399+
13731400
static void read_alternate_refs(const char *path,
13741401
alternate_ref_fn *cb,
13751402
void *data)
@@ -1378,12 +1405,7 @@ static void read_alternate_refs(const char *path,
13781405
struct strbuf line = STRBUF_INIT;
13791406
FILE *fh;
13801407

1381-
cmd.git_cmd = 1;
1382-
argv_array_pushf(&cmd.args, "--git-dir=%s", path);
1383-
argv_array_push(&cmd.args, "for-each-ref");
1384-
argv_array_push(&cmd.args, "--format=%(objectname) %(refname)");
1385-
cmd.env = local_repo_env;
1386-
cmd.out = -1;
1408+
fill_alternate_refs_command(&cmd, path);
13871409

13881410
if (start_command(&cmd))
13891411
return;
@@ -1393,13 +1415,13 @@ static void read_alternate_refs(const char *path,
13931415
struct object_id oid;
13941416

13951417
if (get_oid_hex(line.buf, &oid) ||
1396-
line.buf[GIT_SHA1_HEXSZ] != ' ') {
1418+
line.buf[GIT_SHA1_HEXSZ]) {
13971419
warning(_("invalid line while parsing alternate refs: %s"),
13981420
line.buf);
13991421
break;
14001422
}
14011423

1402-
cb(line.buf + GIT_SHA1_HEXSZ + 1, &oid, data);
1424+
cb(&oid, data);
14031425
}
14041426

14051427
fclose(fh);

transport.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,6 @@ int transport_refs_pushed(struct ref *ref);
261261
void transport_print_push_status(const char *dest, struct ref *refs,
262262
int verbose, int porcelain, unsigned int *reject_reasons);
263263

264-
typedef void alternate_ref_fn(const char *refname, const struct object_id *oid, void *);
264+
typedef void alternate_ref_fn(const struct object_id *oid, void *);
265265
extern void for_each_alternate_ref(alternate_ref_fn, void *);
266266
#endif

0 commit comments

Comments
 (0)