All of lore.kernel.org
 help / color / mirror / Atom feed
From: Taylor Blau <ttaylorr@openai.com>
To: git@vger.kernel.org, git@vger.kernel.org
Cc: Jeff King <peff@peff.net>, Junio C Hamano <gitster@pobox.com>
Subject: [PATCH 2/4] pack-objects: introduce `--no-ref-delta`
Date: Sun, 12 Jul 2026 18:11:57 -0700	[thread overview]
Message-ID: <alQ7XVq5CYD8CyE8@com-79390> (raw)
In-Reply-To: <cover.1783905084.git.ttaylorr@openai.com>

Some consumers of 'pack-objects' may wish to avoid packs which contain
`REF_DELTA` entries. For instance, a 'receive-pack' implementation which
retains the resulting pack without building an index of object IDs may
prefer every delta base to be discoverable from an earlier entry in the
same pack.

Teach 'pack-objects' a new `--no-ref-delta` option to avoid writing
`REF_DELTA` entries, without changing whether `OFS_DELTA` is allowed.

When used without `--delta-base-offset`, no delta representation
remains, so avoid delta search entirely. Otherwise, allow new deltas
whose bases appear earlier in the same pack.

For now, disable delta- and bitmap-reuse under `--no-ref-delta`, since
either may copy an existing `REF_DELTA` entry. This is overly
pessimistic, but simplifies the changes in this commit. The next commit
re-enables reuse in the cases which do not require `REF_DELTA`.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
---
 Documentation/git-pack-objects.adoc |  8 ++++-
 builtin/pack-objects.c              | 16 ++++++---
 t/t5300-pack-object.sh              | 52 +++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-pack-objects.adoc b/Documentation/git-pack-objects.adoc
index 65cd00c152..5e42e4429d 100644
--- a/Documentation/git-pack-objects.adoc
+++ b/Documentation/git-pack-objects.adoc
@@ -10,7 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git pack-objects' [-q | --progress | --all-progress] [--all-progress-implied]
-		   [--no-reuse-delta] [--delta-base-offset] [--non-empty]
+		   [--no-reuse-delta] [--delta-base-offset] [--no-ref-delta]
+		   [--non-empty]
 		   [--local] [--incremental] [--window=<n>] [--depth=<n>]
 		   [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]
 		   [--cruft] [--cruft-expiration=<time>]
@@ -297,6 +298,11 @@ Note: Porcelain commands such as `git gc` (see linkgit:git-gc[1]),
 in modern Git when they put objects in your repository into pack files.
 So does `git bundle` (see linkgit:git-bundle[1]) when it creates a bundle.
 
+--no-ref-delta::
+	Do not emit deltas which represent their base by their literal
+	object ID. This is independent of `--delta-base-offset`;
+	without that option, no deltas are emitted.
+
 --threads=<n>::
 	Specifies the number of threads to spawn when searching for best
 	delta matches.  This requires that pack-objects be compiled with
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index e3760b3492..c3574fcb8a 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -190,7 +190,8 @@ static inline void oe_set_delta_size(struct packing_data *pack,
 
 static const char *const pack_usage[] = {
 	N_("git pack-objects [-q | --progress | --all-progress] [--all-progress-implied]\n"
-	   "                 [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n"
+	   "                 [--no-reuse-delta] [--delta-base-offset] [--no-ref-delta]\n"
+	   "                 [--non-empty]\n"
 	   "                 [--local] [--incremental] [--window=<n>] [--depth=<n>]\n"
 	   "                 [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n"
 	   "                 [--cruft] [--cruft-expiration=<time>]\n"
@@ -221,6 +222,7 @@ static int ignore_packed_keep_in_core;
 static int ignore_packed_keep_in_core_open;
 static int ignore_packed_keep_in_core_has_cruft;
 static int allow_ofs_delta;
+static int allow_ref_delta = 1;
 static struct pack_idx_option pack_idx_opts;
 static const char *base_name;
 static int progress = 1;
@@ -3405,6 +3407,9 @@ static int should_attempt_deltas(struct object_entry *entry)
 	if (entry->no_try_delta)
 		return 0;
 
+	if (entry->preferred_base && !allow_ref_delta)
+		return 0;
+
 	if (!entry->preferred_base) {
 		if (oe_type(entry) < 0)
 			die(_("unable to get type of object %s"),
@@ -3647,7 +3652,8 @@ static void prepare_pack(int window, int depth)
 	if (!pack_to_stdout)
 		do_check_packed_object_crc = 1;
 
-	if (!to_pack.nr_objects || !window || !depth)
+	if (!to_pack.nr_objects || !window || !depth ||
+	    (!allow_ref_delta && !allow_ofs_delta))
 		return;
 
 	if (path_walk)
@@ -4662,7 +4668,7 @@ static int pack_options_allow_reuse(void)
 	       !ignore_packed_keep_on_disk &&
 	       !ignore_packed_keep_in_core &&
 	       (!local || !have_non_local_packs) &&
-	       !incremental;
+	       !incremental && allow_ref_delta;
 }
 
 static int get_object_list_from_bitmap(struct rev_info *revs)
@@ -5111,6 +5117,8 @@ int cmd_pack_objects(int argc,
 			 N_("reuse existing objects")),
 		OPT_BOOL(0, "delta-base-offset", &allow_ofs_delta,
 			 N_("use OFS_DELTA objects")),
+		OPT_BOOL(0, "ref-delta", &allow_ref_delta,
+			 N_("use REF_DELTA objects")),
 		OPT_INTEGER(0, "threads", &delta_search_threads,
 			    N_("use threads when searching for best delta matches")),
 		OPT_BOOL(0, "non-empty", &non_empty,
@@ -5309,7 +5317,7 @@ int cmd_pack_objects(int argc,
 	if (unpack_unreachable || keep_unreachable || pack_loose_unreachable)
 		use_internal_rev_list = 1;
 
-	if (!reuse_object)
+	if (!reuse_object || !allow_ref_delta)
 		reuse_delta = 0;
 	if (cfg->pack_compression_level == -1)
 		cfg->pack_compression_level = Z_DEFAULT_COMPRESSION;
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 4bee490ff6..b9e36044b9 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -211,6 +211,58 @@ test_expect_success 'pack with OFS_DELTA' '
 	test_grep " OFS_DELTA " deltas
 '
 
+test_expect_success 'pack without REF_DELTA' '
+	git pack-objects --no-ref-delta --stdout <obj-list >no-ref.pack &&
+	git index-pack -o no-ref.idx no-ref.pack &&
+
+	test-tool pack-deltas --list-deltas no-ref.idx >deltas &&
+	test_must_be_empty deltas
+'
+
+test_expect_success 'pack without REF_DELTA with OFS_DELTA' '
+	git pack-objects --delta-base-offset --no-ref-delta --stdout \
+		<obj-list >no-ref-ofs.pack &&
+	git index-pack -o no-ref-ofs.idx no-ref-ofs.pack &&
+
+	test-tool pack-deltas --list-deltas no-ref-ofs.idx >deltas &&
+	test_grep " OFS_DELTA " deltas &&
+	test_grep ! " REF_DELTA " deltas
+'
+
+test_expect_success 'pack without REF_DELTA skips excluded delta bases' '
+	test_when_finished "git read-tree $tree" &&
+
+	echo bar >>d &&
+	git update-index --add d &&
+	thin_tree=$(git write-tree) &&
+	thin_commit=$(git commit-tree $thin_tree -p $commit </dev/null) &&
+
+	{
+		echo $thin_commit &&
+		echo ^$commit
+	} >thin-revs &&
+
+	# Each type appears only once in the output, so any delta must
+	# use an excluded base and therefore be a REF_DELTA.
+	git pack-objects --thin --stdout --revs \
+		<thin-revs >thin.pack &&
+	git index-pack --fix-thin --stdin thin-fixed.pack \
+		<thin.pack >/dev/null &&
+
+	test-tool pack-deltas --list-deltas thin-fixed.idx >deltas &&
+	test_grep ! " OFS_DELTA " deltas &&
+	test_grep " REF_DELTA " deltas &&
+
+	git pack-objects --thin --stdout --revs \
+		--delta-base-offset --no-ref-delta \
+		<thin-revs >no-ref-thin.pack &&
+	git index-pack --fix-thin --stdin no-ref-thin-fixed.pack \
+		<no-ref-thin.pack >/dev/null &&
+
+	test-tool pack-deltas --list-deltas no-ref-thin-fixed.idx >deltas &&
+	test_must_be_empty deltas
+'
+
 test_expect_success 'unpack with OFS_DELTA' '
 	check_unpack test-3-${packname_3} obj-list
 '
-- 
2.55.0


  parent reply	other threads:[~2026-07-13  1:12 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <cover.1783905084.git.ttaylorr@openai.com>
2026-07-13  1:11 ` [PATCH 1/4] t/helper: teach pack-deltas to list delta entries Taylor Blau
2026-07-13  1:11 ` Taylor Blau [this message]
2026-07-13  1:12 ` [PATCH 3/4] pack-objects: support reuse with `--no-ref-delta` Taylor Blau
2026-07-13  1:12 ` [PATCH 4/4] send-pack: honor `no-ref-delta` capability Taylor Blau

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=alQ7XVq5CYD8CyE8@com-79390 \
    --to=ttaylorr@openai.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=peff@peff.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.