Skip to content

Commit

Permalink
transport.c: introduce core.alternateRefsCommand
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
ttaylorr authored and gitster committed Oct 9, 2018
1 parent 1e5f31d commit 89284c1
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 4 deletions.
11 changes: 11 additions & 0 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,17 @@ core.preferSymlinkRefs::
This is sometimes needed to work with old scripts that
expect HEAD to be a symbolic link.

core.alternateRefsCommand::
When advertising tips of available history from an alternate, use the shell to
execute the specified command instead of linkgit:git-for-each-ref[1]. The
first argument is the absolute path of the alternate. Output must contain one
hex object id per line (i.e., the same as produce by `git for-each-ref
--format='%(objectname)'`).
+
Note that you cannot generally put `git for-each-ref` directly into the config
value, as it does not take a repository path as an argument (but you can wrap
the command above in a shell script).

core.bare::
If true this repository is assumed to be 'bare' and has no
working directory associated with it. If this is the case a
Expand Down
33 changes: 33 additions & 0 deletions t/t5410-receive-pack-alternates.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/sh

test_description='git receive-pack with alternate ref filtering'

. ./test-lib.sh

test_expect_success 'setup' '
test_commit base &&
git clone -s --bare . fork &&
git checkout -b public/branch master &&
test_commit public &&
git checkout -b private/branch master &&
test_commit private
'

extract_haves () {
depacketize | perl -lne '/^(\S+) \.have/ and print $1'
}

test_expect_success 'with core.alternateRefsCommand' '
write_script fork/alternate-refs <<-\EOF &&
git --git-dir="$1" for-each-ref \
--format="%(objectname)" \
refs/heads/public/
EOF
test_config -C fork core.alternateRefsCommand alternate-refs &&
git rev-parse public/branch >expect &&
printf "0000" | git receive-pack fork >actual &&
extract_haves <actual >actual.haves &&
test_cmp expect actual.haves
'

test_done
19 changes: 15 additions & 4 deletions transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -1328,10 +1328,21 @@ char *transport_anonymize_url(const char *url)
static void fill_alternate_refs_command(struct child_process *cmd,
const char *repo_path)
{
cmd->git_cmd = 1;
argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
argv_array_push(&cmd->args, "for-each-ref");
argv_array_push(&cmd->args, "--format=%(objectname)");
const char *value;

if (!git_config_get_value("core.alternateRefsCommand", &value)) {
cmd->use_shell = 1;

argv_array_push(&cmd->args, value);
argv_array_push(&cmd->args, repo_path);
} else {
cmd->git_cmd = 1;

argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
argv_array_push(&cmd->args, "for-each-ref");
argv_array_push(&cmd->args, "--format=%(objectname)");
}

cmd->env = local_repo_env;
cmd->out = -1;
}
Expand Down

0 comments on commit 89284c1

Please sign in to comment.