summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2025-05-28 07:59:56 -0700
committerJunio C Hamano <gitster@pobox.com>2025-05-28 07:59:56 -0700
commitb4847a4477d8cf312bb6e2aef2f3291d13563d36 (patch)
tree2ede53784417e0bbb607e3e57ae015339383e027
parentb5afd0a7ee809b2065c84d592f0117f19824576a (diff)
parent68cb0b5253a05d62adc5cf6c0a60dc58038b546e (diff)
Merge branch 'jt/receive-pack-skip-connectivity-check'
"git receive-pack" optionally learns not to care about connectivity check, which can be useful when the repository arranges to ensure connectivity by some other means. * jt/receive-pack-skip-connectivity-check: builtin/receive-pack: add option to skip connectivity check t5410: test receive-pack connectivity check
-rw-r--r--Documentation/git-receive-pack.adoc12
-rw-r--r--builtin/receive-pack.c40
-rw-r--r--t/meson.build2
-rwxr-xr-xt/t5410-receive-pack-alternates.sh44
-rwxr-xr-xt/t5410-receive-pack.sh87
5 files changed, 122 insertions, 63 deletions
diff --git a/Documentation/git-receive-pack.adoc b/Documentation/git-receive-pack.adoc
index 20aca92073..0956086d61 100644
--- a/Documentation/git-receive-pack.adoc
+++ b/Documentation/git-receive-pack.adoc
@@ -46,6 +46,18 @@ OPTIONS
`$GIT_URL/info/refs?service=git-receive-pack` requests. See
`--http-backend-info-refs` in linkgit:git-upload-pack[1].
+--skip-connectivity-check::
+ Bypasses the connectivity checks that validate the existence of all
+ objects in the transitive closure of reachable objects. This option is
+ intended for server operators that want to implement their own object
+ connectivity validation outside of Git. This is useful in such cases
+ where the server-side knows additional information about how Git is
+ being used and thus can rely on certain guarantees to more efficiently
+ compute object connectivity that Git itself cannot make. Usage of this
+ option without a reliable external mechanism to ensure full reachable
+ object connectivity risks corrupting the repository and should not be
+ used in the general case.
+
PRE-RECEIVE HOOK
----------------
Before any ref is updated, if $GIT_DIR/hooks/pre-receive file exists
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c92e57ba18..a317d6c278 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -81,6 +81,7 @@ static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
static int reject_thin;
+static int skip_connectivity_check;
static int stateless_rpc;
static const char *service_dir;
static const char *head_name;
@@ -1938,27 +1939,29 @@ static void execute_commands(struct command *commands,
return;
}
- if (use_sideband) {
- memset(&muxer, 0, sizeof(muxer));
- muxer.proc = copy_to_sideband;
- muxer.in = -1;
- if (!start_async(&muxer))
- err_fd = muxer.in;
- /* ...else, continue without relaying sideband */
- }
+ if (!skip_connectivity_check) {
+ if (use_sideband) {
+ memset(&muxer, 0, sizeof(muxer));
+ muxer.proc = copy_to_sideband;
+ muxer.in = -1;
+ if (!start_async(&muxer))
+ err_fd = muxer.in;
+ /* ...else, continue without relaying sideband */
+ }
- data.cmds = commands;
- data.si = si;
- opt.err_fd = err_fd;
- opt.progress = err_fd && !quiet;
- opt.env = tmp_objdir_env(tmp_objdir);
- opt.exclude_hidden_refs_section = "receive";
+ data.cmds = commands;
+ data.si = si;
+ opt.err_fd = err_fd;
+ opt.progress = err_fd && !quiet;
+ opt.env = tmp_objdir_env(tmp_objdir);
+ opt.exclude_hidden_refs_section = "receive";
- if (check_connected(iterate_receive_command_list, &data, &opt))
- set_connectivity_errors(commands, si);
+ if (check_connected(iterate_receive_command_list, &data, &opt))
+ set_connectivity_errors(commands, si);
- if (use_sideband)
- finish_async(&muxer);
+ if (use_sideband)
+ finish_async(&muxer);
+ }
reject_updates_to_hidden(commands);
@@ -2519,6 +2522,7 @@ int cmd_receive_pack(int argc,
struct option options[] = {
OPT__QUIET(&quiet, N_("quiet")),
+ OPT_HIDDEN_BOOL(0, "skip-connectivity-check", &skip_connectivity_check, NULL),
OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL),
OPT_HIDDEN_BOOL(0, "http-backend-info-refs", &advertise_refs, NULL),
OPT_ALIAS(0, "advertise-refs", "http-backend-info-refs"),
diff --git a/t/meson.build b/t/meson.build
index fcfc1c2c2b..d052fc3e23 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -629,7 +629,7 @@ integration_tests = [
't5407-post-rewrite-hook.sh',
't5408-send-pack-stdin.sh',
't5409-colorize-remote-messages.sh',
- 't5410-receive-pack-alternates.sh',
+ 't5410-receive-pack.sh',
't5411-proc-receive-hook.sh',
't5500-fetch-pack.sh',
't5501-fetch-push-alternates.sh',
diff --git a/t/t5410-receive-pack-alternates.sh b/t/t5410-receive-pack-alternates.sh
deleted file mode 100755
index 4e82fd102e..0000000000
--- a/t/t5410-receive-pack-alternates.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-test_description='git receive-pack with alternate ref filtering'
-
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
-. ./test-lib.sh
-
-test_expect_success 'setup' '
- test_commit base &&
- git clone -s --bare . fork &&
- git checkout -b public/branch main &&
- test_commit public &&
- git checkout -b private/branch main &&
- test_commit private
-'
-
-extract_haves () {
- depacketize | sed -n 's/^\([^ ][^ ]*\) \.have/\1/p'
-}
-
-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_expect_success 'with core.alternateRefsPrefixes' '
- test_config -C fork core.alternateRefsPrefixes "refs/heads/private" &&
- git rev-parse private/branch >expect &&
- printf "0000" | git receive-pack fork >actual &&
- extract_haves <actual >actual.haves &&
- test_cmp expect actual.haves
-'
-
-test_done
diff --git a/t/t5410-receive-pack.sh b/t/t5410-receive-pack.sh
new file mode 100755
index 0000000000..f76a22943e
--- /dev/null
+++ b/t/t5410-receive-pack.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='git receive-pack'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit base &&
+ git clone -s --bare . fork &&
+ git checkout -b public/branch main &&
+ test_commit public &&
+ git checkout -b private/branch main &&
+ test_commit private
+'
+
+extract_haves () {
+ depacketize | sed -n 's/^\([^ ][^ ]*\) \.have/\1/p'
+}
+
+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_expect_success 'with core.alternateRefsPrefixes' '
+ test_config -C fork core.alternateRefsPrefixes "refs/heads/private" &&
+ git rev-parse private/branch >expect &&
+ printf "0000" | git receive-pack fork >actual &&
+ extract_haves <actual >actual.haves &&
+ test_cmp expect actual.haves
+'
+
+test_expect_success 'receive-pack missing objects fails connectivity check' '
+ test_when_finished rm -rf repo remote.git setup.git &&
+
+ git init repo &&
+ git -C repo commit --allow-empty -m 1 &&
+ git clone --bare repo setup.git &&
+ git -C repo commit --allow-empty -m 2 &&
+
+ # Capture git-send-pack(1) output sent to git-receive-pack(1).
+ git -C repo send-pack ../setup.git --all \
+ --receive-pack="tee ${SQ}$(pwd)/out${SQ} | git-receive-pack" &&
+
+ # Replay captured git-send-pack(1) output on new empty repository.
+ git init --bare remote.git &&
+ git receive-pack remote.git <out >actual 2>err &&
+
+ test_grep "missing necessary objects" actual &&
+ test_grep "fatal: Failed to traverse parents" err &&
+ test_must_fail git -C remote.git cat-file -e $(git -C repo rev-parse HEAD)
+'
+
+test_expect_success 'receive-pack missing objects bypasses connectivity check' '
+ test_when_finished rm -rf repo remote.git setup.git &&
+
+ git init repo &&
+ git -C repo commit --allow-empty -m 1 &&
+ git clone --bare repo setup.git &&
+ git -C repo commit --allow-empty -m 2 &&
+
+ # Capture git-send-pack(1) output sent to git-receive-pack(1).
+ git -C repo send-pack ../setup.git --all \
+ --receive-pack="tee ${SQ}$(pwd)/out${SQ} | git-receive-pack" &&
+
+ # Replay captured git-send-pack(1) output on new empty repository.
+ git init --bare remote.git &&
+ git receive-pack --skip-connectivity-check remote.git <out >actual 2>err &&
+
+ test_grep ! "missing necessary objects" actual &&
+ test_must_be_empty err &&
+ git -C remote.git cat-file -e $(git -C repo rev-parse HEAD) &&
+ test_must_fail git -C remote.git rev-list $(git -C repo rev-parse HEAD)
+'
+
+test_done