From 130f45404af37a4fe02052a5378c33bcb02b9ebc Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Sun, 22 Feb 2026 12:54:45 +0800 Subject: repository: Accept root instead of repopath --- cmd/show-object/main.go | 8 ++++- repository/read_stored_passthrough_test.go | 9 +++++- repository/refs_test.go | 25 +++++++++++++-- repository/repository.go | 46 +++++++++------------------- repository/repository_test.go | 17 +++++++++-- repository/stored_test.go | 49 ++++++++++++++++++++++++++---- repository/traversal_bench_test.go | 12 ++++++-- repository/traversal_test.go | 10 ++++-- repository/write_loose_test.go | 25 +++++++++++++-- 9 files changed, 150 insertions(+), 51 deletions(-) diff --git a/cmd/show-object/main.go b/cmd/show-object/main.go index e0d32193..1df925e0 100644 --- a/cmd/show-object/main.go +++ b/cmd/show-object/main.go @@ -22,7 +22,13 @@ func main() { log.Fatal("must provide -r and -h ") } - repo, err := repository.Open(*repoPath) + root, err := os.OpenRoot(*repoPath) + if err != nil { + log.Fatalf("open repo root: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { log.Fatalf("open repository: %v", err) } diff --git a/repository/read_stored_passthrough_test.go b/repository/read_stored_passthrough_test.go index f320907a..3adcc103 100644 --- a/repository/read_stored_passthrough_test.go +++ b/repository/read_stored_passthrough_test.go @@ -3,6 +3,7 @@ package repository_test import ( "bytes" "io" + "os" "testing" "codeberg.org/lindenii/furgit/internal/testgit" @@ -23,7 +24,13 @@ func TestReadStoredPassThroughs(t *testing.T) { _, _, commitID := repoHarness.MakeCommit(t, "pass-through") - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } diff --git a/repository/refs_test.go b/repository/refs_test.go index 8ebf93a6..d0cb216b 100644 --- a/repository/refs_test.go +++ b/repository/refs_test.go @@ -1,6 +1,7 @@ package repository_test import ( + "os" "strings" "testing" @@ -25,7 +26,13 @@ func TestRefConvenienceMethods(t *testing.T) { repoHarness.SymbolicRef(t, "HEAD", "refs/heads/main") repoHarness.UpdateRef(t, "refs/tags/v1", commitID) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -79,7 +86,13 @@ func TestResolveRefErrorSurface(t *testing.T) { RefFormat: "files", }) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -114,7 +127,13 @@ func TestListRefsLooseOverridesPacked(t *testing.T) { _, _, commit2 := repoHarness.MakeCommit(t, "commit-two") repoHarness.UpdateRef(t, "refs/heads/main", commit2) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } diff --git a/repository/repository.go b/repository/repository.go index bd5ac8f3..418941a0 100644 --- a/repository/repository.go +++ b/repository/repository.go @@ -21,7 +21,7 @@ import ( // Repository is a thin composition root for repository-local stores. // -// Open expects path to be the Git directory itself: +// Open expects a root for the Git directory itself: // a bare repository root or a non-bare ".git" directory. type Repository struct { config *config.Config @@ -33,13 +33,9 @@ type Repository struct { } // Open opens a repository and wires object/ref stores from its on-disk format. -func Open(path string) (repo *Repository, err error) { - setupRoot, err := os.OpenRoot(path) - if err != nil { - return nil, err - } - defer func() { _ = setupRoot.Close() }() - +// +// Open borrows root during construction and does not close it. +func Open(root *os.Root) (repo *Repository, err error) { repo = &Repository{} defer func() { if err != nil { @@ -47,7 +43,7 @@ func Open(path string) (repo *Repository, err error) { } }() - cfg, err := parseRepositoryConfig(setupRoot) + cfg, err := parseRepositoryConfig(root) if err != nil { return nil, err } @@ -59,14 +55,14 @@ func Open(path string) (repo *Repository, err error) { } repo.algo = algo - objects, objectsLooseForWritingOnly, err := openObjectStore(path, algo) + objects, objectsLooseForWritingOnly, err := openObjectStore(root, algo) if err != nil { return nil, err } repo.objects = objects repo.objectsLooseForWritingOnly = objectsLooseForWritingOnly - refs, err := openRefStore(path, algo) + refs, err := openRefStore(root, algo) if err != nil { return nil, err } @@ -148,14 +144,8 @@ func detectObjectAlgorithm(cfg *config.Config) (objectid.Algorithm, error) { return algo, nil } -func openObjectStore(path string, algo objectid.Algorithm) (objectstore.Store, *objectloose.Store, error) { - repoRoot, err := os.OpenRoot(path) - if err != nil { - return nil, nil, fmt.Errorf("repository: open root: %w", err) - } - defer func() { _ = repoRoot.Close() }() - - objectsRoot, err := repoRoot.OpenRoot("objects") +func openObjectStore(root *os.Root, algo objectid.Algorithm) (objectstore.Store, *objectloose.Store, error) { + objectsRoot, err := root.OpenRoot("objects") if err != nil { return nil, nil, fmt.Errorf("repository: open objects: %w", err) } @@ -182,7 +172,7 @@ func openObjectStore(path string, algo objectid.Algorithm) (objectstore.Store, * objectsChain := objectchain.New(backends...) - objectsRootForWriting, err := repoRoot.OpenRoot("objects") + objectsRootForWriting, err := root.OpenRoot("objects") if err != nil { _ = objectsChain.Close() return nil, nil, fmt.Errorf("repository: open objects for loose writing: %w", err) @@ -197,19 +187,13 @@ func openObjectStore(path string, algo objectid.Algorithm) (objectstore.Store, * return objectsChain, objectsLooseForWritingOnly, nil } -func openRefStore(path string, algo objectid.Algorithm) (out refstore.Store, err error) { - metaRoot, err := os.OpenRoot(path) - if err != nil { - return nil, fmt.Errorf("repository: open root: %w", err) - } - defer func() { _ = metaRoot.Close() }() - - hasReftable, err := hasReftableStack(metaRoot) +func openRefStore(root *os.Root, algo objectid.Algorithm) (out refstore.Store, err error) { + hasReftable, err := hasReftableStack(root) if err != nil { return nil, err } if hasReftable { - reftableRoot, err := metaRoot.OpenRoot("reftable") + reftableRoot, err := root.OpenRoot("reftable") if err != nil { return nil, fmt.Errorf("repository: open reftable: %w", err) } @@ -221,7 +205,7 @@ func openRefStore(path string, algo objectid.Algorithm) (out refstore.Store, err return reftableStore, nil } - looseRoot, err := os.OpenRoot(path) + looseRoot, err := root.OpenRoot(".") if err != nil { return nil, fmt.Errorf("repository: open root for loose refs: %w", err) } @@ -232,7 +216,7 @@ func openRefStore(path string, algo objectid.Algorithm) (out refstore.Store, err } backends := []refstore.Store{looseStore} - packedRefsFile, err := metaRoot.Open("packed-refs") + packedRefsFile, err := root.Open("packed-refs") if err == nil { packedStore, packedErr := refpacked.New(packedRefsFile, algo) _ = packedRefsFile.Close() diff --git a/repository/repository_test.go b/repository/repository_test.go index de6b8428..f8b33c8a 100644 --- a/repository/repository_test.go +++ b/repository/repository_test.go @@ -1,6 +1,7 @@ package repository_test import ( + "os" "testing" "codeberg.org/lindenii/furgit/internal/testgit" @@ -24,7 +25,13 @@ func TestOpenFilesRefFormat(t *testing.T) { repoHarness.UpdateRef(t, "refs/heads/main", commitID) repoHarness.SymbolicRef(t, "HEAD", "refs/heads/main") - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -108,7 +115,13 @@ func writeMainAndHead(t *testing.T, repoHarness *testgit.TestRepo) objectid.Obje func assertResolveFully(t *testing.T, repoHarness *testgit.TestRepo, name string, want objectid.ObjectID) { t.Helper() - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } diff --git a/repository/stored_test.go b/repository/stored_test.go index 3768d450..e3e2266d 100644 --- a/repository/stored_test.go +++ b/repository/stored_test.go @@ -2,6 +2,7 @@ package repository_test import ( "fmt" + "os" "strings" "testing" @@ -23,7 +24,13 @@ func TestReadStoredTyped(t *testing.T) { blobID, treeID, commitID := repoHarness.MakeCommit(t, "stored types") - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -78,7 +85,13 @@ func TestResolveTreeEntry(t *testing.T) { childTreeID := repoHarness.Mktree(t, fmt.Sprintf("100644 blob %s\tleaf.txt\n", blobID)) rootTreeID := repoHarness.Mktree(t, fmt.Sprintf("040000 tree %s\tdir\n", childTreeID)) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -116,7 +129,13 @@ func TestResolveTreeEntryErrors(t *testing.T) { blobID := repoHarness.HashObject(t, "blob", []byte("body\n")) rootTreeID := repoHarness.Mktree(t, fmt.Sprintf("100644 blob %s\tfile.txt\n", blobID)) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -143,7 +162,13 @@ func TestResolveTreeEntryErrors(t *testing.T) { blobID := repoHarness.HashObject(t, "blob", []byte("body\n")) rootTreeID := repoHarness.Mktree(t, fmt.Sprintf("100644 blob %s\tdir\n", blobID)) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -185,7 +210,13 @@ func TestResolveTreeEntryDeepPath(t *testing.T) { } parts = append(parts, []byte("leaf.txt")) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -235,7 +266,13 @@ func TestReadStoredTreeMixedModes(t *testing.T) { ), ) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } diff --git a/repository/traversal_bench_test.go b/repository/traversal_bench_test.go index da49c639..63e131de 100644 --- a/repository/traversal_bench_test.go +++ b/repository/traversal_bench_test.go @@ -21,9 +21,17 @@ func BenchmarkTraverseHeadTree(b *testing.B) { b.Fatalf("missing %s", benchRepoPathEnv) } - repo, err := repository.Open(repoPath) + root, err := os.OpenRoot(repoPath) if err != nil { - b.Fatalf("repository.Open(%q): %v", repoPath, err) + b.Fatalf("os.OpenRoot(%q): %v", repoPath, err) + } + b.Cleanup(func() { + _ = root.Close() + }) + + repo, err := repository.Open(root) + if err != nil { + b.Fatalf("repository.Open(root for %q): %v", repoPath, err) } b.Cleanup(func() { _ = repo.Close() diff --git a/repository/traversal_test.go b/repository/traversal_test.go index e6e3445a..28c03a2c 100644 --- a/repository/traversal_test.go +++ b/repository/traversal_test.go @@ -92,9 +92,15 @@ func TestRepositoryDepthFirstEnumerationCurrentWorktree(t *testing.T) { func walkRepositoryFromHead(t *testing.T, repoPath string) { t.Helper() - repo, err := repository.Open(repoPath) + root, err := os.OpenRoot(repoPath) if err != nil { - t.Fatalf("repository.Open(%q): %v", repoPath, err) + t.Fatalf("os.OpenRoot(%q): %v", repoPath, err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) + if err != nil { + t.Fatalf("repository.Open(root for %q): %v", repoPath, err) } defer func() { _ = repo.Close() }() diff --git a/repository/write_loose_test.go b/repository/write_loose_test.go index aae118e2..603b3a88 100644 --- a/repository/write_loose_test.go +++ b/repository/write_loose_test.go @@ -2,6 +2,7 @@ package repository_test import ( "bytes" + "os" "testing" "codeberg.org/lindenii/furgit/internal/testgit" @@ -20,7 +21,13 @@ func TestWriteLooseBytesContent(t *testing.T) { RefFormat: "files", }) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -60,7 +67,13 @@ func TestWriteLooseReaderContent(t *testing.T) { RefFormat: "files", }) - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } @@ -90,7 +103,13 @@ func TestWriteLooseFull(t *testing.T) { }) _, _, commitID := repoHarness.MakeCommit(t, "write-loose-full") - repo, err := repository.Open(repoHarness.Dir()) + root, err := os.OpenRoot(repoHarness.Dir()) + if err != nil { + t.Fatalf("os.OpenRoot: %v", err) + } + defer func() { _ = root.Close() }() + + repo, err := repository.Open(root) if err != nil { t.Fatalf("repository.Open: %v", err) } -- cgit v1.3.1-10-gc9f91