File: test_reftable.py

package info (click to toggle)
dulwich 1.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 7,388 kB
  • sloc: python: 99,991; makefile: 163; sh: 67
file content (811 lines) | stat: -rw-r--r-- 31,001 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
"""Reftable compatibility tests with C git."""

import os
import struct
import tempfile

from dulwich.reftable import (
    REF_VALUE_DELETE,
    REF_VALUE_REF,
    REF_VALUE_SYMREF,
    REFTABLE_MAGIC,
    REFTABLE_VERSION,
    ReftableReader,
    ReftableRefsContainer,
)
from dulwich.repo import Repo

from .utils import CompatTestCase, rmtree_ro, run_git_or_fail


class ReftableCompatTestCase(CompatTestCase):
    """Test reftable compatibility with C git."""

    min_git_version = (2, 45, 0)  # Version with stable reftable support

    def setUp(self):
        """Set up test environment."""
        super().setUp()
        self.test_dir = tempfile.mkdtemp()
        self.addCleanup(self._cleanup)

    def _cleanup(self):
        """Clean up test directory."""
        rmtree_ro(self.test_dir)

    def _run_git(self, args, **kwargs):
        """Run git command in test directory."""
        return run_git_or_fail(args, cwd=self.test_dir, **kwargs)

    def _create_git_repo_with_reftable(self):
        """Create a git repository with reftable format."""
        # Initialize repo with reftable format
        self._run_git(["init", "--bare", "--ref-format=reftable", "."])

        # Create some test objects and refs using proper commits
        blob1_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content 1\n"
        ).strip()
        blob2_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content 2\n"
        ).strip()

        # Create tree objects
        tree1_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob1_sha.decode()}\tfile1.txt\n".encode()
        ).strip()
        tree2_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob2_sha.decode()}\tfile2.txt\n".encode()
        ).strip()

        # Create commit objects
        sha1 = self._run_git(
            ["commit-tree", tree1_sha.decode(), "-m", "First commit"]
        ).strip()
        sha2 = self._run_git(
            ["commit-tree", tree2_sha.decode(), "-m", "Second commit"]
        ).strip()

        # Create refs
        self._run_git(["update-ref", "refs/heads/master", sha1.decode()])
        self._run_git(["update-ref", "refs/heads/develop", sha2.decode()])
        self._run_git(["symbolic-ref", "HEAD", "refs/heads/master"])

        return sha1, sha2

    def _create_reftable_repo(self):
        """Create a Dulwich repository with reftable extension configured."""
        from io import BytesIO

        from dulwich.config import ConfigFile

        # Initialize bare repo
        repo = Repo.init_bare(self.test_dir)
        repo.close()

        # Add reftable extension to config
        config_path = os.path.join(self.test_dir, "config")
        with open(config_path, "rb") as f:
            config_data = f.read()

        config = ConfigFile()
        config.from_file(BytesIO(config_data))
        config.set(b"core", b"repositoryformatversion", b"1")
        config.set(b"extensions", b"refStorage", b"reftable")
        # Use master as default branch for reftable compatibility
        config.set(b"init", b"defaultBranch", b"master")

        with open(config_path, "wb") as f:
            config.write_to_file(f)

        # Reopen repo with reftable extension
        return Repo(self.test_dir)

    def _get_reftable_files(self):
        """Get list of reftable files in the repository."""
        reftable_dir = os.path.join(self.test_dir, "reftable")
        if not os.path.exists(reftable_dir):
            return []

        files = []
        for filename in os.listdir(reftable_dir):
            if filename.endswith((".ref", ".log")):
                files.append(os.path.join(reftable_dir, filename))
        return sorted(files)

    def _validate_reftable_format(self, filepath: str):
        """Validate that a file follows the reftable format specification."""
        with open(filepath, "rb") as f:
            # Check magic header
            magic = f.read(4)
            self.assertEqual(magic, REFTABLE_MAGIC, "Invalid reftable magic")

            # Check version + block size (4 bytes total)
            # Format: uint8(version) + uint24(block_size)
            version_and_blocksize = struct.unpack(">I", f.read(4))[0]
            version = (version_and_blocksize >> 24) & 0xFF  # First byte
            block_size = version_and_blocksize & 0xFFFFFF  # Last 3 bytes

            self.assertEqual(
                version, REFTABLE_VERSION, f"Unsupported version: {version}"
            )
            self.assertGreater(block_size, 0, "Invalid block size")

            # Check that we can read the header without errors
            min_update_index = struct.unpack(">Q", f.read(8))[0]
            max_update_index = struct.unpack(">Q", f.read(8))[0]

            # Update indices should be valid
            self.assertGreaterEqual(max_update_index, min_update_index)

    def test_git_creates_valid_reftable_format(self):
        """Test that git creates reftable files with valid format."""
        _sha1, _sha2 = self._create_git_repo_with_reftable()

        # Check that reftable files were created
        reftable_files = self._get_reftable_files()
        self.assertGreater(len(reftable_files), 0, "No reftable files created")

        # Validate format of each reftable file
        for filepath in reftable_files:
            if filepath.endswith(".ref"):
                self._validate_reftable_format(filepath)

    def test_dulwich_can_read_git_reftables(self):
        """Test that Dulwich can read reftables created by git."""
        sha1, sha2 = self._create_git_repo_with_reftable()

        # Open with Dulwich
        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)

        # Verify it's using reftable
        self.assertIsInstance(repo.refs, ReftableRefsContainer)

        # Check that we can read the refs
        all_refs = repo.get_refs()
        self.assertIn(b"refs/heads/master", all_refs)
        self.assertIn(b"refs/heads/develop", all_refs)
        self.assertIn(b"HEAD", all_refs)

        # Verify ref values
        self.assertEqual(all_refs[b"refs/heads/master"], sha1)
        self.assertEqual(all_refs[b"refs/heads/develop"], sha2)
        self.assertEqual(all_refs[b"HEAD"], sha1)  # HEAD -> refs/heads/master

        repo.close()

    def test_git_can_read_dulwich_reftables(self):
        """Test that git can read reftables created by Dulwich."""
        # Create a repo with reftable extension
        repo = self._create_reftable_repo()
        self.assertIsInstance(repo.refs, ReftableRefsContainer)

        # Create real objects that git can validate
        blob1_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content 1\n"
        ).strip()
        blob2_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content 2\n"
        ).strip()

        # Create tree objects
        tree1_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob1_sha.decode()}\tfile1.txt\n".encode()
        ).strip()
        tree2_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob2_sha.decode()}\tfile2.txt\n".encode()
        ).strip()

        # Create commit objects
        sha1 = self._run_git(
            ["commit-tree", tree1_sha.decode(), "-m", "First commit"]
        ).strip()
        sha2 = self._run_git(
            ["commit-tree", tree2_sha.decode(), "-m", "Second commit"]
        ).strip()

        # Create exactly what Git does: consolidated table with multiple refs

        # Use batched operations to create a single consolidated reftable like git
        with repo.refs.batch_update():
            repo.refs.set_if_equals(b"refs/heads/master", None, sha1)
            repo.refs.set_if_equals(b"refs/heads/develop", None, sha2)
            repo.refs.set_symbolic_ref(b"HEAD", b"refs/heads/master")

        repo.refs._update_tables_list()

        repo.close()

        # Test that git can read our reftables
        # Test symbolic ref reading
        head_output = self._run_git(["symbolic-ref", "HEAD"])
        self.assertEqual(head_output.strip(), b"refs/heads/master")

        # Test ref parsing
        master_output = self._run_git(["rev-parse", "HEAD"])
        self.assertEqual(master_output.strip(), sha1)

        # Test show-ref
        show_output = self._run_git(["show-ref"])
        self.assertIn(b"refs/heads/master", show_output)
        self.assertIn(b"refs/heads/develop", show_output)

    def test_reftable_file_structure_compatibility(self):
        """Test that reftable file structure matches git's expectations."""
        self._create_git_repo_with_reftable()

        reftable_files = self._get_reftable_files()
        ref_files = [f for f in reftable_files if f.endswith(".ref")]

        for ref_file in ref_files:
            with open(ref_file, "rb") as f:
                # Read with our ReftableReader
                reader = ReftableReader(f)
                refs = reader.all_refs()

                # Should have some refs
                self.assertGreater(len(refs), 0)

                # All refs should have valid types and values
                for refname, (ref_type, value) in refs.items():
                    self.assertIsInstance(refname, bytes)
                    self.assertIn(
                        ref_type,
                        [REF_VALUE_REF, REF_VALUE_SYMREF, REF_VALUE_DELETE],
                    )
                    self.assertIsInstance(value, bytes)

                    if ref_type == REF_VALUE_REF:
                        self.assertEqual(
                            len(value), 40, f"Invalid SHA length for {refname}"
                        )

    def test_ref_operations_match_git_behavior(self):
        """Test that ref operations produce the same results as git."""
        self._create_git_repo_with_reftable()

        # Read refs with git
        git_output = self._run_git(["show-ref"])
        git_refs = {}
        for line in git_output.split(b"\n"):
            if line.strip():
                sha, refname = line.strip().split(b" ", 1)
                git_refs[refname] = sha

        # Read refs with Dulwich
        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)
        dulwich_refs = repo.get_refs()

        # Compare non-symbolic refs
        for refname, sha in git_refs.items():
            if refname != b"HEAD":  # HEAD is symbolic, compare differently
                self.assertIn(refname, dulwich_refs)
                self.assertEqual(dulwich_refs[refname], sha)

        # Check HEAD symbolic ref
        head_target = self._run_git(["rev-parse", "HEAD"]).strip()

        self.assertEqual(dulwich_refs[b"HEAD"], head_target)
        repo.close()

    def test_multiple_table_files_compatibility(self):
        """Test compatibility when multiple reftable files exist."""
        _sha1, _sha2 = self._create_git_repo_with_reftable()

        # Add more refs to potentially create multiple table files
        for i in range(10):
            content = f"test content {i}\n".encode()
            sha = self._run_git(["hash-object", "-w", "--stdin"], input=content).strip()
            self._run_git(["update-ref", f"refs/tags/tag{i}", sha.decode()])

        # Read with both git and Dulwich
        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)
        dulwich_refs = repo.get_refs()

        git_output = self._run_git(["show-ref"])
        git_ref_count = len([line for line in git_output.split(b"\n") if line.strip()])

        # Should have roughly the same number of refs
        self.assertGreaterEqual(
            len(dulwich_refs), git_ref_count - 1
        )  # -1 for potential HEAD differences
        repo.close()

    def test_empty_reftable_compatibility(self):
        """Test compatibility with empty reftable repositories."""
        # Create repo with reftable extension
        repo = self._create_reftable_repo()
        self.assertIsInstance(repo.refs, ReftableRefsContainer)

        # Should have no refs initially (reftable doesn't create default HEAD)
        all_keys = list(repo.refs.allkeys())
        self.assertEqual(len(all_keys), 0)

        # Add a single ref
        test_sha = b"1234567890abcdef1234567890abcdef12345678"
        repo.refs.set_if_equals(b"refs/heads/master", None, test_sha)

        # Should now have our ref
        all_keys = list(repo.refs.allkeys())
        self.assertEqual(len(all_keys), 1)
        self.assertIn(b"refs/heads/master", all_keys)
        self.assertEqual(repo.refs.read_loose_ref(b"refs/heads/master"), test_sha)

        repo.close()

    def test_reftable_update_compatibility(self):
        """Test that ref updates work compatibly with git."""
        repo = self._create_reftable_repo()

        # Create initial ref
        sha1 = b"1111111111111111111111111111111111111111"
        sha2 = b"2222222222222222222222222222222222222222"

        repo.refs.set_if_equals(b"refs/heads/master", None, sha1)

        # Update ref
        success = repo.refs.set_if_equals(b"refs/heads/master", sha1, sha2)
        self.assertTrue(success)

        # Verify update
        self.assertEqual(repo.refs.read_loose_ref(b"refs/heads/master"), sha2)

        # Try invalid update (should fail)
        success = repo.refs.set_if_equals(b"refs/heads/master", sha1, b"3" * 40)
        self.assertFalse(success)

        # Ref should be unchanged
        self.assertEqual(repo.refs.read_loose_ref(b"refs/heads/master"), sha2)

        repo.close()

    def test_symbolic_ref_compatibility(self):
        """Test symbolic reference compatibility."""
        repo = self._create_reftable_repo()

        # Create target ref
        test_sha = b"abcdef1234567890abcdef1234567890abcdef12"
        repo.refs.set_if_equals(b"refs/heads/master", None, test_sha)

        # Create symbolic ref
        repo.refs.set_symbolic_ref(b"HEAD", b"refs/heads/master")

        # Verify symbolic ref resolves correctly
        self.assertEqual(repo.refs[b"HEAD"], test_sha)
        # Verify read_loose_ref returns the symbolic ref format
        self.assertEqual(repo.refs.read_loose_ref(b"HEAD"), b"ref: refs/heads/master")

        # Update target and verify symbolic ref follows
        new_sha = b"fedcba0987654321fedcba0987654321fedcba09"
        repo.refs.set_if_equals(b"refs/heads/master", test_sha, new_sha)
        self.assertEqual(repo.refs[b"HEAD"], new_sha)

        repo.close()

    def test_complex_ref_scenarios_compatibility(self):
        """Test complex ref scenarios that git should handle correctly."""
        # Initialize repo
        self._run_git(["init", "--bare", "."])
        self._run_git(["config", "core.repositoryformatversion", "1"])
        self._run_git(["config", "extensions.refStorage", "reftable"])

        # Create test objects
        blob_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content"
        ).strip()
        tree_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob_sha.decode()}\tfile.txt\n".encode()
        ).strip()
        commit_sha1 = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "First commit"]
        ).strip()
        commit_sha2 = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "Second commit"]
        ).strip()
        commit_sha3 = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "Third commit"]
        ).strip()

        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)

        # Test complex batched operations
        with repo.refs.batch_update():
            # Multiple branches
            repo.refs.set_if_equals(b"refs/heads/master", None, commit_sha1)
            repo.refs.set_if_equals(b"refs/heads/feature/awesome", None, commit_sha2)
            repo.refs.set_if_equals(b"refs/heads/hotfix/critical", None, commit_sha3)

            # Multiple tags
            repo.refs.set_if_equals(b"refs/tags/v1.0.0", None, commit_sha1)
            repo.refs.set_if_equals(b"refs/tags/v1.1.0", None, commit_sha2)

            # Symbolic refs
            repo.refs.set_symbolic_ref(b"HEAD", b"refs/heads/master")
            repo.refs.set_symbolic_ref(
                b"refs/remotes/origin/HEAD", b"refs/remotes/origin/main"
            )

            # Remote refs
            repo.refs.set_if_equals(b"refs/remotes/origin/main", None, commit_sha1)
            repo.refs.set_if_equals(
                b"refs/remotes/origin/feature/awesome", None, commit_sha2
            )

        repo.close()

        # Verify git can read all refs correctly
        git_refs = {}
        git_output = self._run_git(["show-ref", "--head"])
        for line in git_output.split(b"\n"):
            if line.strip():
                sha, refname = line.strip().split(b" ", 1)
                git_refs[refname] = sha

        # Verify HEAD symbolic ref
        head_target = self._run_git(["symbolic-ref", "HEAD"]).strip()
        self.assertEqual(head_target, b"refs/heads/master")

        # Verify all branches resolve correctly
        main_sha = self._run_git(["rev-parse", "refs/heads/master"]).strip()
        self.assertEqual(main_sha, commit_sha1)

        feature_sha = self._run_git(["rev-parse", "refs/heads/feature/awesome"]).strip()
        self.assertEqual(feature_sha, commit_sha2)

        # Verify tags
        tag_sha = self._run_git(["rev-parse", "refs/tags/v1.0.0"]).strip()
        self.assertEqual(tag_sha, commit_sha1)

    def test_ref_deletion_compatibility(self):
        """Test that ref deletion works correctly with git."""
        # Initialize repo with refs
        self._run_git(["init", "--bare", "."])
        self._run_git(["config", "core.repositoryformatversion", "1"])
        self._run_git(["config", "extensions.refStorage", "reftable"])

        # Create test objects
        blob_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content"
        ).strip()
        tree_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob_sha.decode()}\tfile.txt\n".encode()
        ).strip()
        commit_sha1 = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "Commit 1"]
        ).strip()
        commit_sha2 = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "Commit 2"]
        ).strip()

        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)

        # Create multiple refs
        with repo.refs.batch_update():
            repo.refs.set_if_equals(b"refs/heads/master", None, commit_sha1)
            repo.refs.set_if_equals(b"refs/heads/feature", None, commit_sha2)
            repo.refs.set_if_equals(b"refs/tags/v1.0", None, commit_sha1)
            repo.refs.set_symbolic_ref(b"HEAD", b"refs/heads/master")

        # Verify refs exist
        self.assertEqual(
            self._run_git(["rev-parse", "refs/heads/feature"]).strip(), commit_sha2
        )

        # Delete a ref using dulwich
        with repo.refs.batch_update():
            repo.refs.remove_if_equals(b"refs/heads/feature", commit_sha2)

        repo.close()

        # Verify git sees the ref as deleted (should fail)
        with self.assertRaises(AssertionError):
            self._run_git(["rev-parse", "refs/heads/feature"])

        # Verify other refs still exist
        self.assertEqual(
            self._run_git(["rev-parse", "refs/heads/master"]).strip(), commit_sha1
        )
        self.assertEqual(
            self._run_git(["symbolic-ref", "HEAD"]).strip(), b"refs/heads/master"
        )

    def test_large_number_of_refs_compatibility(self):
        """Test compatibility with large numbers of refs."""
        # Initialize repo
        self._run_git(["init", "--bare", "."])
        self._run_git(["config", "core.repositoryformatversion", "1"])
        self._run_git(["config", "extensions.refStorage", "reftable"])

        # Create base objects
        blob_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content"
        ).strip()
        tree_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob_sha.decode()}\tfile.txt\n".encode()
        ).strip()
        base_commit = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "Base commit"]
        ).strip()

        # Create many refs efficiently
        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)

        with repo.refs.batch_update():
            # Create 50 branches
            for i in range(50):
                content = f"branch content {i}".encode()
                commit_sha = self._run_git(
                    ["commit-tree", tree_sha.decode(), "-m", f"Branch {i} commit"],
                    input=content,
                ).strip()
                repo.refs.set_if_equals(
                    f"refs/heads/branch{i:02d}".encode(), None, commit_sha
                )

            # Create 30 tags
            for i in range(30):
                repo.refs.set_if_equals(
                    f"refs/tags/tag{i:02d}".encode(), None, base_commit
                )

            # Set HEAD to first branch
            repo.refs.set_symbolic_ref(b"HEAD", b"refs/heads/branch00")

        repo.close()

        # Verify git can list all refs
        git_output = self._run_git(["show-ref"])
        ref_count = len([line for line in git_output.split(b"\n") if line.strip()])

        # Should have 50 branches + 30 tags = 80 refs
        self.assertGreaterEqual(ref_count, 80)

        # Verify HEAD points to correct branch
        head_target = self._run_git(["symbolic-ref", "HEAD"]).strip()
        self.assertEqual(head_target, b"refs/heads/branch00")

        # Verify some random refs resolve correctly
        branch10_sha = self._run_git(["rev-parse", "refs/heads/branch10"]).strip()
        self.assertEqual(len(branch10_sha), 40)  # Valid SHA

        tag05_sha = self._run_git(["rev-parse", "refs/tags/tag05"]).strip()
        self.assertEqual(tag05_sha, base_commit)

    def test_nested_symbolic_refs_compatibility(self):
        """Test compatibility with nested symbolic references."""
        # Initialize repo
        self._run_git(["init", "--bare", "."])
        self._run_git(["config", "core.repositoryformatversion", "1"])
        self._run_git(["config", "extensions.refStorage", "reftable"])

        # Create test objects
        blob_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content"
        ).strip()
        tree_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob_sha.decode()}\tfile.txt\n".encode()
        ).strip()
        commit_sha = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "Test commit"]
        ).strip()

        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)

        # Create chain of symbolic refs
        with repo.refs.batch_update():
            # Real ref
            repo.refs.set_if_equals(b"refs/heads/master", None, commit_sha)

            # Chain: HEAD -> current -> master
            repo.refs.set_symbolic_ref(b"refs/heads/current", b"refs/heads/master")
            repo.refs.set_symbolic_ref(b"HEAD", b"refs/heads/current")

        repo.close()

        # Verify git can resolve the chain correctly
        head_sha = self._run_git(["rev-parse", "HEAD"]).strip()
        self.assertEqual(head_sha, commit_sha)

        # Verify intermediate symbolic ref
        current_target = self._run_git(["symbolic-ref", "refs/heads/current"]).strip()
        self.assertEqual(current_target, b"refs/heads/master")

        # Verify HEAD points to master (Git resolves symref chains for HEAD)
        head_target = self._run_git(["symbolic-ref", "HEAD"]).strip()
        self.assertEqual(head_target, b"refs/heads/master")

    def test_special_ref_names_compatibility(self):
        """Test compatibility with special and edge-case ref names."""
        # Initialize repo
        self._run_git(["init", "--bare", "."])
        self._run_git(["config", "core.repositoryformatversion", "1"])
        self._run_git(["config", "extensions.refStorage", "reftable"])

        # Create test objects
        blob_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content"
        ).strip()
        tree_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob_sha.decode()}\tfile.txt\n".encode()
        ).strip()
        commit_sha = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "Test commit"]
        ).strip()

        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)

        # Test refs with special characters and structures
        special_refs = [
            b"refs/heads/feature/sub-feature",
            b"refs/heads/feature_with_underscores",
            b"refs/heads/UPPERCASE-BRANCH",
            b"refs/tags/v1.0.0-alpha.1",
            b"refs/tags/release/2023.12.31",
            b"refs/remotes/origin/main",
            b"refs/remotes/upstream/develop",
            b"refs/notes/commits",
        ]

        with repo.refs.batch_update():
            for ref_name in special_refs:
                repo.refs.set_if_equals(ref_name, None, commit_sha)

            # Set HEAD to a normal branch
            repo.refs.set_if_equals(b"refs/heads/master", None, commit_sha)
            repo.refs.set_symbolic_ref(b"HEAD", b"refs/heads/master")

        repo.close()

        # Verify git can read all special refs
        for ref_name in special_refs:
            ref_sha = self._run_git(["rev-parse", ref_name.decode()]).strip()
            self.assertEqual(ref_sha, commit_sha, f"Failed to resolve {ref_name}")

        # Verify show-ref includes all refs
        git_output = self._run_git(["show-ref"])
        for ref_name in special_refs:
            self.assertIn(ref_name, git_output, f"show-ref missing {ref_name}")

    def test_concurrent_ref_operations_compatibility(self):
        """Test compatibility with multiple ref operations."""
        # Initialize repo
        self._run_git(["init", "--bare", "."])
        self._run_git(["config", "core.repositoryformatversion", "1"])
        self._run_git(["config", "extensions.refStorage", "reftable"])

        # Create test objects
        blob_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content"
        ).strip()
        tree_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob_sha.decode()}\tfile.txt\n".encode()
        ).strip()

        commits = []
        for i in range(5):
            commit_sha = self._run_git(
                ["commit-tree", tree_sha.decode(), "-m", f"Commit {i}"]
            ).strip()
            commits.append(commit_sha)

        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)

        # Simulate concurrent operations with multiple batch updates
        # First batch: Create initial refs
        with repo.refs.batch_update():
            repo.refs.set_if_equals(b"refs/heads/master", None, commits[0])
            repo.refs.set_if_equals(b"refs/heads/develop", None, commits[1])
            repo.refs.set_symbolic_ref(b"HEAD", b"refs/heads/master")

        repo.refs._update_tables_list()

        # Second batch: Update some refs and add new ones
        with repo.refs.batch_update():
            repo.refs.set_if_equals(
                b"refs/heads/master", commits[0], commits[2]
            )  # Update main
            repo.refs.set_if_equals(
                b"refs/heads/feature", None, commits[3]
            )  # Add feature
            repo.refs.set_if_equals(b"refs/tags/v1.0", None, commits[0])  # Add tag

        repo.refs._update_tables_list()

        # Third batch: More complex operations
        with repo.refs.batch_update():
            repo.refs.set_if_equals(
                b"refs/heads/develop", commits[1], commits[4]
            )  # Update develop
            repo.refs.remove_if_equals(
                b"refs/heads/feature", commits[3]
            )  # Delete feature
            repo.refs.set_symbolic_ref(
                b"HEAD", b"refs/heads/develop"
            )  # Change HEAD target

        repo.refs._update_tables_list()

        repo.close()

        # Verify final state with git
        main_sha = self._run_git(["rev-parse", "refs/heads/master"]).strip()
        self.assertEqual(main_sha, commits[2])

        develop_sha = self._run_git(["rev-parse", "refs/heads/develop"]).strip()
        self.assertEqual(develop_sha, commits[4])

        # Verify feature branch was deleted (should fail)
        with self.assertRaises(AssertionError):
            self._run_git(["rev-parse", "refs/heads/feature"])

        # Verify HEAD points to develop
        head_target = self._run_git(["symbolic-ref", "HEAD"]).strip()
        self.assertEqual(head_target, b"refs/heads/develop")

        # Verify tag exists
        tag_sha = self._run_git(["rev-parse", "refs/tags/v1.0"]).strip()
        self.assertEqual(tag_sha, commits[0])

    def test_reftable_gc_compatibility(self):
        """Test that reftables work correctly after git operations."""
        # Initialize repo
        self._run_git(["init", "--bare", "."])
        self._run_git(["config", "core.repositoryformatversion", "1"])
        self._run_git(["config", "extensions.refStorage", "reftable"])

        # Create initial state with dulwich
        blob_sha = self._run_git(
            ["hash-object", "-w", "--stdin"], input=b"test content"
        ).strip()
        tree_sha = self._run_git(
            ["mktree"], input=f"100644 blob {blob_sha.decode()}\tfile.txt\n".encode()
        ).strip()
        commit_sha = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "Initial commit"]
        ).strip()

        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)

        with repo.refs.batch_update():
            repo.refs.set_if_equals(b"refs/heads/master", None, commit_sha)
            repo.refs.set_symbolic_ref(b"HEAD", b"refs/heads/master")

        repo.close()

        # Perform git operations
        new_commit = self._run_git(
            ["commit-tree", tree_sha.decode(), "-m", "New commit"]
        ).strip()
        self._run_git(["update-ref", "refs/heads/master", new_commit.decode()])
        self._run_git(["update-ref", "refs/heads/branch2", new_commit.decode()])

        # Verify dulwich can still read after git modifications
        repo = Repo(self.test_dir)
        self.addCleanup(repo.close)
        dulwich_refs = repo.get_refs()

        # Should be able to read git-modified refs
        self.assertEqual(dulwich_refs[b"refs/heads/master"], new_commit)
        self.assertEqual(dulwich_refs[b"refs/heads/branch2"], new_commit)

        # Should still resolve HEAD correctly
        head_sha = repo.refs[b"HEAD"]
        self.assertEqual(head_sha, new_commit)

        repo.close()


if __name__ == "__main__":
    import unittest

    unittest.main()