send-pack: honor `no-ref-delta` capability
Add a 'no-ref-delta' receive-pack capability and teach send-pack to pass '--no-ref-delta' to 'pack-objects' when the server advertises it. Keep this separate from 'ofs-delta' so that a server may request that `send-pack` omit `REF_DELTA` without also accepting `OFS_DELTA`. Signed-off-by: Taylor Blau <ttaylorr@openai.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Taylor Blau committed
Jul 12, 2026 at 18:12 UTC
90202ce6e1da1d8fe46fdb7a1ccd1dd2d598b70c
5 files changed
+38
-3
Documentation/gitprotocol-capabilities.adoc
+14
-3
@@ -34,9 +34,9 @@ were sent. Server MUST NOT ignore capabilities that client requested
34
and server advertised. As a consequence of these rules, server MUST
35
NOT advertise capabilities it does not understand.
36
37
-The 'atomic', 'report-status', 'report-status-v2', 'delete-refs', 'quiet',
38
-and 'push-cert' capabilities are sent and recognized by the receive-pack
39
-(push to server) process.
37
+The 'atomic', 'report-status', 'report-status-v2', 'delete-refs',
38
+'no-ref-delta', 'quiet', and 'push-cert' capabilities are sent and
39
+recognized by the receive-pack (push to server) process.
40
41
The 'ofs-delta' and 'side-band-64k' capabilities are sent and recognized
42
by both upload-pack and receive-pack protocols. The 'agent' and 'session-id'
@@ -174,6 +174,17 @@ The server can send, and the client can understand, PACKv2 with delta referring
174
its base by position in pack rather than by an obj-id. That is, they can
175
send/read OBJ_OFS_DELTA (aka type 6) in a packfile.
176
177
+no-ref-delta
178
+------------
179
+
180
+The receive-pack server can request, and the client can send, PACKv2
181
+without deltas referring to their bases by an obj-id. That is, the
182
+client MUST NOT send OBJ_REF_DELTA (aka type 7) in a packfile when the
183
+server advertises this capability.
184
+
185
+This does not imply that the server understands OBJ_OFS_DELTA entries;
186
+that is negotiated separately with the 'ofs-delta' capability.
187
+
188
agent
189
-----
190
builtin/receive-pack.c
+5
@@ -66,6 +66,7 @@ static struct strbuf fsck_msg_types = STRBUF_INIT;
66
static int receive_unpack_limit = -1;
67
static int transfer_unpack_limit = -1;
68
static int advertise_atomic_push = 1;
69
+static int advertise_no_ref_delta;
70
static int advertise_push_options;
71
static int advertise_sid;
72
static int unpack_limit = 100;
@@ -290,6 +291,8 @@ static void show_ref(const char *path, const struct object_id *oid)
291
strbuf_addstr(&cap, " atomic");
292
if (prefer_ofs_delta)
293
strbuf_addstr(&cap, " ofs-delta");
294
+ if (advertise_no_ref_delta)
295
+ strbuf_addstr(&cap, " no-ref-delta");
296
if (push_cert_nonce)
297
strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
298
if (advertise_push_options)
@@ -2631,6 +2634,8 @@ int cmd_receive_pack(int argc,
2634
OPT_HIDDEN_BOOL(0, "http-backend-info-refs", &advertise_refs, NULL),
2635
OPT_ALIAS(0, "advertise-refs", "http-backend-info-refs"),
2636
OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL),
2637
+ OPT_HIDDEN_BOOL(0, "advertise-no-ref-delta-for-testing",
2638
+ &advertise_no_ref_delta, NULL),
2639
OPT_END()
2640
};
2641
send-pack.c
+4
@@ -80,6 +80,8 @@ static int pack_objects(struct repository *r,
80
strvec_push(&po.args, "--thin");
81
if (args->use_ofs_delta)
82
strvec_push(&po.args, "--delta-base-offset");
83
+ if (args->no_ref_delta)
84
+ strvec_push(&po.args, "--no-ref-delta");
85
if (args->quiet || !args->progress)
86
strvec_push(&po.args, "-q");
87
if (args->progress)
@@ -570,6 +572,8 @@ int send_pack(struct repository *r,
572
allow_deleting_refs = 1;
573
if (server_supports("ofs-delta"))
574
args->use_ofs_delta = 1;
575
+ if (server_supports("no-ref-delta"))
576
+ args->no_ref_delta = 1;
577
if (server_supports("side-band-64k"))
578
use_sideband = 1;
579
if (server_supports("quiet"))
send-pack.h
+1
@@ -28,6 +28,7 @@ struct send_pack_args {
28
force_update:1,
29
use_thin_pack:1,
30
use_ofs_delta:1,
31
+ no_ref_delta:1,
32
dry_run:1,
33
/* One of the SEND_PACK_PUSH_CERT_* constants. */
34
push_cert:2,
t/t5516-fetch-push.sh
+14
@@ -1548,6 +1548,20 @@ EOF
1548
git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/main:refs/heads/foo
1549
'
1550
1551
+test_expect_success 'push honors no-ref-delta capability' '
1552
+ test_commit no-ref-delta &&
1553
+
1554
+ rcvpck="git receive-pack --advertise-no-ref-delta-for-testing" &&
1555
+
1556
+ GIT_TRACE2_EVENT="$PWD/no-ref-delta" \
1557
+ git push --receive-pack="$rcvpck" no-thin/.git \
1558
+ refs/heads/main:refs/heads/bar &&
1559
+
1560
+ test_subcommand git pack-objects --all-progress-implied --revs \
1561
+ --stdout --thin --delta-base-offset --no-ref-delta -q \
1562
+ <no-ref-delta
1563
+'
1564
+
1565
test_expect_success 'pushing a tag pushes the tagged object' '
1566
blob=$(echo unreferenced | git hash-object -w --stdin) &&
1567
git tag -m foo tag-of-blob $blob &&