ref: 130f45404af37a4fe02052a5378c33bcb02b9ebc
parent: 2a7a1e8c95025da4fbd6c398a5e51672fb1a8bd2
author: Runxi Yu <me@runxiyu.org>
date: Sun Feb 22 07:54:45 EST 2026
repository: Accept root instead of repopath
--- a/cmd/show-object/main.go
+++ b/cmd/show-object/main.go
@@ -22,7 +22,13 @@
log.Fatal("must provide -r <repo> and -h <ref-or-object-id>")}
- 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)}
--- a/repository/read_stored_passthrough_test.go
+++ b/repository/read_stored_passthrough_test.go
@@ -3,6 +3,7 @@
import (
"bytes"
"io"
+ "os"
"testing"
"codeberg.org/lindenii/furgit/internal/testgit"
@@ -23,7 +24,13 @@
_, _, 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)}
--- a/repository/refs_test.go
+++ b/repository/refs_test.go
@@ -1,6 +1,7 @@
package repository_test
import (
+ "os"
"strings"
"testing"
@@ -25,8 +26,14 @@
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)}
defer func() { _ = repo.Close() }()@@ -79,8 +86,14 @@
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)}
defer func() { _ = repo.Close() }()@@ -114,7 +127,13 @@
_, _, 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)}
--- a/repository/repository.go
+++ b/repository/repository.go
@@ -21,7 +21,7 @@
// 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 @@
}
// 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 @@
}
}()
- cfg, err := parseRepositoryConfig(setupRoot)
+ cfg, err := parseRepositoryConfig(root)
if err != nil {return nil, err
}
@@ -59,7 +55,7 @@
}
repo.algo = algo
- objects, objectsLooseForWritingOnly, err := openObjectStore(path, algo)
+ objects, objectsLooseForWritingOnly, err := openObjectStore(root, algo)
if err != nil {return nil, err
}
@@ -66,7 +62,7 @@
repo.objects = objects
repo.objectsLooseForWritingOnly = objectsLooseForWritingOnly
- refs, err := openRefStore(path, algo)
+ refs, err := openRefStore(root, algo)
if err != nil {return nil, err
}
@@ -148,15 +144,9 @@
return algo, nil
}
-func openObjectStore(path string, algo objectid.Algorithm) (objectstore.Store, *objectloose.Store, error) {- repoRoot, err := os.OpenRoot(path)
+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 root: %w", err)- }
- defer func() { _ = repoRoot.Close() }()-
- objectsRoot, err := repoRoot.OpenRoot("objects")- if err != nil { return nil, nil, fmt.Errorf("repository: open objects: %w", err)}
@@ -182,7 +172,7 @@
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 @@
return objectsChain, objectsLooseForWritingOnly, nil
}
-func openRefStore(path string, algo objectid.Algorithm) (out refstore.Store, err error) {- metaRoot, err := os.OpenRoot(path)
+func openRefStore(root *os.Root, algo objectid.Algorithm) (out refstore.Store, err error) {+ hasReftable, err := hasReftableStack(root)
if err != nil {- return nil, fmt.Errorf("repository: open root: %w", err)- }
- defer func() { _ = metaRoot.Close() }()-
- hasReftable, err := hasReftableStack(metaRoot)
- 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 @@
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 @@
}
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()
--- 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,8 +25,14 @@
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)}
defer func() { _ = repo.Close() }()@@ -108,7 +115,13 @@
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)}
--- a/repository/stored_test.go
+++ b/repository/stored_test.go
@@ -2,6 +2,7 @@
import (
"fmt"
+ "os"
"strings"
"testing"
@@ -23,8 +24,14 @@
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)}
defer func() { _ = repo.Close() }()@@ -78,8 +85,14 @@
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)}
defer func() { _ = repo.Close() }()@@ -116,8 +129,14 @@
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)}
defer func() { _ = repo.Close() }()@@ -143,8 +162,14 @@
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)}
defer func() { _ = repo.Close() }()@@ -185,8 +210,14 @@
}
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)}
defer func() { _ = repo.Close() }()@@ -235,7 +266,13 @@
),
)
- 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)}
--- a/repository/traversal_bench_test.go
+++ b/repository/traversal_bench_test.go
@@ -21,9 +21,17 @@
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()
--- a/repository/traversal_test.go
+++ b/repository/traversal_test.go
@@ -92,9 +92,15 @@
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() }()--- a/repository/write_loose_test.go
+++ b/repository/write_loose_test.go
@@ -2,6 +2,7 @@
import (
"bytes"
+ "os"
"testing"
"codeberg.org/lindenii/furgit/internal/testgit"
@@ -20,8 +21,14 @@
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)}
defer func() { _ = repo.Close() }()@@ -60,8 +67,14 @@
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)}
defer func() { _ = repo.Close() }()@@ -90,7 +103,13 @@
})
_, _, 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)}
--
⑨