repository: require Rust support for interoperability

We'll be implementing some of our interoperability code, like the loose object map, in Rust. While the code currently compiles with the old loose object map format, which is written entirely in C, we'll soon replace that with the Rust-based implementation. Require the use of Rust for compatibility mode and die if it is not supported. Because the repo argument is not used when Rust is missing, cast it to void to silence the compiler warning, which we do not care about. Add a prerequisite in our tests, RUST, that checks if Rust functionality is available and use it in the tests that handle interoperability. This is technically a regression in functionality compared to our existing state, but pack index v3 is not yet implemented and thus the functionality is mostly quite broken, which is why we've recently marked this functionality as experimental. We don't believe anyone is getting useful use out of the interoperability code in its current state, so no actual users should be negatively impacted by this change. Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

brian m. carlson committed Feb 7, 2026 at 20:04 UTC 13ecc938422b204170a3276edb427e0ff38e3670
7 files changed +77 -33
repository.c
+7 -1
@@ -3,6 +3,7 @@
3 #include "repository.h"
4 #include "odb.h"
5 #include "config.h"
6 +#include "gettext.h"
7 #include "object.h"
8 #include "lockfile.h"
9 #include "path.h"
@@ -183,13 +184,18 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo)
184 repo->hash_algo = &hash_algos[hash_algo];
185 }
186
186 -void repo_set_compat_hash_algo(struct repository *repo, int algo)
187 +void repo_set_compat_hash_algo(struct repository *repo MAYBE_UNUSED, int algo)
188 {
189 +#ifdef WITH_RUST
190 if (hash_algo_by_ptr(repo->hash_algo) == algo)
191 BUG("hash_algo and compat_hash_algo match");
192 repo->compat_hash_algo = algo ? &hash_algos[algo] : NULL;
193 if (repo->compat_hash_algo)
194 repo_read_loose_object_map(repo);
195 +#else
196 + if (algo)
197 + die(_("compatibility hash algorithm support requires Rust"));
198 +#endif
199 }
200
201 void repo_set_ref_storage_format(struct repository *repo,
t/t1006-cat-file.sh
+55 -27
@@ -241,10 +241,16 @@ hello_content="Hello World"
241 hello_size=$(strlen "$hello_content")
242 hello_oid=$(echo_without_newline "$hello_content" | git hash-object --stdin)
243
244 -test_expect_success "setup" '
244 +test_expect_success "setup part 1" '
245 git config core.repositoryformatversion 1 &&
246 - git config extensions.objectformat $test_hash_algo &&
247 - git config extensions.compatobjectformat $test_compat_hash_algo &&
246 + git config extensions.objectformat $test_hash_algo
247 +'
248 +
249 +test_expect_success RUST 'compat setup' '
250 + git config extensions.compatobjectformat $test_compat_hash_algo
251 +'
252 +
253 +test_expect_success 'setup part 2' '
254 echo_without_newline "$hello_content" > hello &&
255 git update-index --add hello &&
256 echo_without_newline "$hello_content" > "path with spaces" &&
@@ -273,9 +279,13 @@ run_blob_tests () {
279 '
280 }
281
276 -hello_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $hello_oid)
282 run_blob_tests $hello_oid
278 -run_blob_tests $hello_compat_oid
283 +
284 +if test_have_prereq RUST
285 +then
286 + hello_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $hello_oid)
287 + run_blob_tests $hello_compat_oid
288 +fi
289
290 test_expect_success '--batch-check without %(rest) considers whole line' '
291 echo "$hello_oid blob $hello_size" >expect &&
@@ -286,62 +296,76 @@ test_expect_success '--batch-check without %(rest) considers whole line' '
296 '
297
298 tree_oid=$(git write-tree)
289 -tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid)
299 tree_size=$((2 * $(test_oid rawsz) + 13 + 24))
291 -tree_compat_size=$((2 * $(test_oid --hash=compat rawsz) + 13 + 24))
300 tree_pretty_content="100644 blob $hello_oid hello${LF}100755 blob $hello_oid path with spaces${LF}"
293 -tree_compat_pretty_content="100644 blob $hello_compat_oid hello${LF}100755 blob $hello_compat_oid path with spaces${LF}"
301
302 run_tests 'tree' $tree_oid "" $tree_size "" "$tree_pretty_content"
296 -run_tests 'tree' $tree_compat_oid "" $tree_compat_size "" "$tree_compat_pretty_content"
303 run_tests 'blob' "$tree_oid:hello" "100644" $hello_size "" "$hello_content" $hello_oid
298 -run_tests 'blob' "$tree_compat_oid:hello" "100644" $hello_size "" "$hello_content" $hello_compat_oid
304 run_tests 'blob' "$tree_oid:path with spaces" "100755" $hello_size "" "$hello_content" $hello_oid
300 -run_tests 'blob' "$tree_compat_oid:path with spaces" "100755" $hello_size "" "$hello_content" $hello_compat_oid
305 +
306 +if test_have_prereq RUST
307 +then
308 + tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid)
309 + tree_compat_size=$((2 * $(test_oid --hash=compat rawsz) + 13 + 24))
310 + tree_compat_pretty_content="100644 blob $hello_compat_oid hello${LF}100755 blob $hello_compat_oid path with spaces${LF}"
311 +
312 + run_tests 'tree' $tree_compat_oid "" $tree_compat_size "" "$tree_compat_pretty_content"
313 + run_tests 'blob' "$tree_compat_oid:hello" "100644" $hello_size "" "$hello_content" $hello_compat_oid
314 + run_tests 'blob' "$tree_compat_oid:path with spaces" "100755" $hello_size "" "$hello_content" $hello_compat_oid
315 +fi
316
317 commit_message="Initial commit"
318 commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid)
304 -commit_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $commit_oid)
319 commit_size=$(($(test_oid hexsz) + 137))
306 -commit_compat_size=$(($(test_oid --hash=compat hexsz) + 137))
320 commit_content="tree $tree_oid
321 author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
322 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
323
324 $commit_message"
325
313 -commit_compat_content="tree $tree_compat_oid
326 +run_tests 'commit' $commit_oid "" $commit_size "$commit_content" "$commit_content"
327 +
328 +if test_have_prereq RUST
329 +then
330 + commit_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $commit_oid)
331 + commit_compat_size=$(($(test_oid --hash=compat hexsz) + 137))
332 + commit_compat_content="tree $tree_compat_oid
333 author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
334 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
335
336 $commit_message"
337
319 -run_tests 'commit' $commit_oid "" $commit_size "$commit_content" "$commit_content"
320 -run_tests 'commit' $commit_compat_oid "" $commit_compat_size "$commit_compat_content" "$commit_compat_content"
338 + run_tests 'commit' $commit_compat_oid "" $commit_compat_size "$commit_compat_content" "$commit_compat_content"
339 +fi
340
341 tag_header_without_oid="type blob
342 tag hellotag
343 tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
344 tag_header_without_timestamp="object $hello_oid
345 $tag_header_without_oid"
327 -tag_compat_header_without_timestamp="object $hello_compat_oid
328 -$tag_header_without_oid"
346 tag_description="This is a tag"
347 tag_content="$tag_header_without_timestamp 0 +0000
348
332 -$tag_description"
333 -tag_compat_content="$tag_compat_header_without_timestamp 0 +0000
334 -
349 $tag_description"
350
351 tag_oid=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
352 tag_size=$(strlen "$tag_content")
353
340 -tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
341 -tag_compat_size=$(strlen "$tag_compat_content")
342 -
354 run_tests 'tag' $tag_oid "" $tag_size "$tag_content" "$tag_content"
344 -run_tests 'tag' $tag_compat_oid "" $tag_compat_size "$tag_compat_content" "$tag_compat_content"
355 +
356 +if test_have_prereq RUST
357 +then
358 + tag_compat_header_without_timestamp="object $hello_compat_oid
359 +$tag_header_without_oid"
360 + tag_compat_content="$tag_compat_header_without_timestamp 0 +0000
361 +
362 +$tag_description"
363 +
364 + tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
365 + tag_compat_size=$(strlen "$tag_compat_content")
366 +
367 + run_tests 'tag' $tag_compat_oid "" $tag_compat_size "$tag_compat_content" "$tag_compat_content"
368 +fi
369
370 test_expect_success "Reach a blob from a tag pointing to it" '
371 echo_without_newline "$hello_content" >expect &&
@@ -590,7 +614,8 @@ flush"
614 }
615
616 batch_tests $hello_oid $tree_oid $tree_size $commit_oid $commit_size "$commit_content" $tag_oid $tag_size "$tag_content"
593 -batch_tests $hello_compat_oid $tree_compat_oid $tree_compat_size $commit_compat_oid $commit_compat_size "$commit_compat_content" $tag_compat_oid $tag_compat_size "$tag_compat_content"
617 +
618 +test_have_prereq RUST && batch_tests $hello_compat_oid $tree_compat_oid $tree_compat_size $commit_compat_oid $commit_compat_size "$commit_compat_content" $tag_compat_oid $tag_compat_size "$tag_compat_content"
619
620
621 test_expect_success FUNNYNAMES 'setup with newline in input' '
@@ -1236,7 +1261,10 @@ test_expect_success 'batch-check with a submodule' '
1261 test_unconfig extensions.compatobjectformat &&
1262 printf "160000 commit $(test_oid deadbeef)\tsub\n" >tree-with-sub &&
1263 tree=$(git mktree <tree-with-sub) &&
1239 - test_config extensions.compatobjectformat $test_compat_hash_algo &&
1264 + if test_have_prereq RUST
1265 + then
1266 + test_config extensions.compatobjectformat $test_compat_hash_algo
1267 + fi &&
1268
1269 git cat-file --batch-check >actual <<-EOF &&
1270 $tree:sub
t/t1016-compatObjectFormat.sh
+6
@@ -8,6 +8,12 @@ test_description='Test how well compatObjectFormat works'
8 . ./test-lib.sh
9 . "$TEST_DIRECTORY"/lib-gpg.sh
10
11 +if ! test_have_prereq RUST
12 +then
13 + skip_all='interoperability requires a Git built with Rust'
14 + test_done
15 +fi
16 +
17 # All of the follow variables must be defined in the environment:
18 # GIT_AUTHOR_NAME
19 # GIT_AUTHOR_EMAIL
t/t1500-rev-parse.sh
+1 -1
@@ -208,7 +208,7 @@ test_expect_success 'rev-parse --show-object-format in repo' '
208 '
209
210
211 -test_expect_success 'rev-parse --show-object-format in repo with compat mode' '
211 +test_expect_success RUST 'rev-parse --show-object-format in repo with compat mode' '
212 mkdir repo &&
213 (
214 sane_unset GIT_DEFAULT_HASH &&
t/t9305-fast-import-signatures.sh
+2 -2
@@ -70,7 +70,7 @@ test_expect_success GPGSSH 'strip SSH signature with --signed-commits=strip' '
70 test_must_be_empty log
71 '
72
73 -test_expect_success GPG 'setup a commit with dual OpenPGP signatures on its SHA-1 and SHA-256 formats' '
73 +test_expect_success RUST,GPG 'setup a commit with dual OpenPGP signatures on its SHA-1 and SHA-256 formats' '
74 # Create a signed SHA-256 commit
75 git init --object-format=sha256 explicit-sha256 &&
76 git -C explicit-sha256 config extensions.compatObjectFormat sha1 &&
@@ -91,7 +91,7 @@ test_expect_success GPG 'setup a commit with dual OpenPGP signatures on its SHA-
91 test_grep -E "^gpgsig-sha256 " out
92 '
93
94 -test_expect_success GPG 'strip both OpenPGP signatures with --signed-commits=warn-strip' '
94 +test_expect_success RUST,GPG 'strip both OpenPGP signatures with --signed-commits=warn-strip' '
95 git -C explicit-sha256 fast-export --signed-commits=verbatim dual-signed >output &&
96 test_grep -E "^gpgsig sha1 openpgp" output &&
97 test_grep -E "^gpgsig sha256 openpgp" output &&
t/t9350-fast-export.sh
+2 -2
@@ -972,7 +972,7 @@ test_expect_success 'fast-export handles --end-of-options' '
972 test_cmp expect actual
973 '
974
975 -test_expect_success GPG 'setup a commit with dual signatures on its SHA-1 and SHA-256 formats' '
975 +test_expect_success GPG,RUST 'setup a commit with dual signatures on its SHA-1 and SHA-256 formats' '
976 # Create a signed SHA-256 commit
977 git init --object-format=sha256 explicit-sha256 &&
978 git -C explicit-sha256 config extensions.compatObjectFormat sha1 &&
@@ -993,7 +993,7 @@ test_expect_success GPG 'setup a commit with dual signatures on its SHA-1 and SH
993 test_grep -E "^gpgsig-sha256 " out
994 '
995
996 -test_expect_success GPG 'export and import of doubly signed commit' '
996 +test_expect_success GPG,RUST 'export and import of doubly signed commit' '
997 git -C explicit-sha256 fast-export --signed-commits=verbatim dual-signed >output &&
998 test_grep -E "^gpgsig sha1 openpgp" output &&
999 test_grep -E "^gpgsig sha256 openpgp" output &&
t/test-lib.sh
+4
@@ -1891,6 +1891,10 @@ test_lazy_prereq LONG_IS_64BIT '
1891 test 8 -le "$(build_option sizeof-long)"
1892 '
1893
1894 +test_lazy_prereq RUST '
1895 + test "$(build_option rust)" = enabled
1896 +'
1897 +
1898 test_lazy_prereq TIME_IS_64BIT 'test-tool date is64bit'
1899 test_lazy_prereq TIME_T_IS_64BIT 'test-tool date time_t-is64bit'
1900