Skip to content

Commit 89284c1

Browse files
ttaylorrgitster
authored andcommitted
transport.c: introduce core.alternateRefsCommand
When in a repository containing one or more alternates, Git would sometimes like to list references from those alternates. For example, 'git receive-pack' lists the "tips" pointed to by references in those alternates as special ".have" references. Listing ".have" references is designed to make pushing changes from upstream to a fork a lightweight operation, by advertising to the pusher that the fork already has the objects (via its alternate). Thus, the client can avoid sending them. However, when the alternate (upstream, in the previous example) has a pathologically large number of references, the initial advertisement is too expensive. In fact, it can dominate any such optimization where the pusher avoids sending certain objects. Introduce "core.alternateRefsCommand" in order to provide a facility to limit or filter alternate references. This can be used, for example, to filter out references the alternate does not wish to send (for space concerns, or otherwise) during the initial advertisement. Let the repository that has alternates configure this command to avoid trusting the alternate to provide us a safe command to run in the shell. To find the alternate, pass its absolute path as the first argument. Signed-off-by: Taylor Blau <[email protected]> Acked-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1e5f31d commit 89284c1

File tree

3 files changed

+59
-4
lines changed

3 files changed

+59
-4
lines changed

Documentation/config.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,17 @@ 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+
619630
core.bare::
620631
If true this repository is assumed to be 'bare' and has no
621632
working directory associated with it. If this is the case a

t/t5410-receive-pack-alternates.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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_done

transport.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,10 +1328,21 @@ char *transport_anonymize_url(const char *url)
13281328
static void fill_alternate_refs_command(struct child_process *cmd,
13291329
const char *repo_path)
13301330
{
1331-
cmd->git_cmd = 1;
1332-
argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
1333-
argv_array_push(&cmd->args, "for-each-ref");
1334-
argv_array_push(&cmd->args, "--format=%(objectname)");
1331+
const char *value;
1332+
1333+
if (!git_config_get_value("core.alternateRefsCommand", &value)) {
1334+
cmd->use_shell = 1;
1335+
1336+
argv_array_push(&cmd->args, value);
1337+
argv_array_push(&cmd->args, repo_path);
1338+
} else {
1339+
cmd->git_cmd = 1;
1340+
1341+
argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
1342+
argv_array_push(&cmd->args, "for-each-ref");
1343+
argv_array_push(&cmd->args, "--format=%(objectname)");
1344+
}
1345+
13351346
cmd->env = local_repo_env;
13361347
cmd->out = -1;
13371348
}

0 commit comments

Comments
 (0)