ref: 673902b14458f32fbf47efa3757279872bdfcb7e
parent: 53e166913d9e76b82aa53361b251390a2c0726bd
author: Runxi Yu <me@runxiyu.org>
date: Sat Feb 21 10:29:40 EST 2026
repository, {ref,object}store: Make stores own their roots
--- a/objectstore/loose/store.go
+++ b/objectstore/loose/store.go
@@ -9,11 +9,11 @@
// Store reads loose Git objects from an objects directory root.
//
-// Store does not own root. Callers are responsible for closing root.
+// Store owns root and closes it in Close.
type Store struct {// root is the objects directory capability used for all object file access.
// Object files are opened by relative paths like "<first2>/<rest>".
- // Store does not own this root.
+ // Store owns this root.
root *os.Root
// algo is the expected object ID algorithm for lookups.
algo objectid.Algorithm
@@ -32,6 +32,5 @@
// Close releases resources associated with the backend.
func (store *Store) Close() error {- _ = store
- return nil
+ return store.root.Close()
}
--- a/objectstore/packed/store.go
+++ b/objectstore/packed/store.go
@@ -12,7 +12,7 @@
// Store reads Git objects from pack/index files under an objects/pack root.
//
-// Store does not own root. Callers are responsible for closing root.
+// Store owns root and closes it in Close.
type Store struct {// root is the objects/pack capability used for all file access.
root *os.Root
@@ -65,10 +65,9 @@
return nil
}
store.closed = true
+ root := store.root
packs := store.packs
- store.packs = make(map[string]*packFile)
indexes := store.indexes
- store.indexes = nil
store.stateMu.Unlock()
var closeErr error
@@ -86,10 +85,12 @@
}
}
store.cacheMu.Lock()
- if store.deltaCache != nil {- store.deltaCache.clear()
- }
+ store.deltaCache.clear()
store.cacheMu.Unlock()
+
+ if err := root.Close(); err != nil && closeErr == nil {+ closeErr = err
+ }
return closeErr
}
--- a/refstore/loose/store.go
+++ b/refstore/loose/store.go
@@ -10,7 +10,7 @@
// Store reads loose references from a repository root.
//
-// Store does not own root. Callers are responsible for closing root.
+// Store owns root and closes it in Close.
type Store struct {// root is the repository root capability.
root *os.Root
@@ -33,5 +33,5 @@
// Close releases resources associated with the backend.
func (store *Store) Close() error {- return nil
+ return store.root.Close()
}
--- a/refstore/reftable/store.go
+++ b/refstore/reftable/store.go
@@ -15,7 +15,7 @@
// Store reads references from a reftable stack rooted at $GIT_DIR/reftable.
//
-// Store does not own root. Callers are responsible for closing root.
+// Store owns root and closes it in Close.
type Store struct {// root is the reftable directory capability.
root *os.Root
@@ -53,8 +53,8 @@
return nil
}
store.closed = true
+ root := store.root
tables := store.tables
- store.tables = nil
store.stateMu.Unlock()
var closeErr error
@@ -65,6 +65,9 @@
if err := table.close(); err != nil && closeErr == nil {closeErr = err
}
+ }
+ if err := root.Close(); err != nil && closeErr == nil {+ closeErr = err
}
return closeErr
}
--- a/repository/repository.go
+++ b/repository/repository.go
@@ -24,11 +24,6 @@
// Open expects path to be the Git directory itself:
// a bare repository root or a non-bare ".git" directory.
type Repository struct {- root *os.Root
- objectsRoot *os.Root
- packRoot *os.Root
- reftableRoot *os.Root
-
config *config.Config
algo objectid.Algorithm
@@ -38,12 +33,13 @@
// Open opens a repository and wires object/ref stores from its on-disk format.
func Open(path string) (repo *Repository, err error) {- root, err := os.OpenRoot(path)
+ setupRoot, err := os.OpenRoot(path)
if err != nil {return nil, err
}
+ defer func() { _ = setupRoot.Close() }()- repo = &Repository{root: root}+ repo = &Repository{} defer func() { if err != nil {_ = repo.Close()
@@ -50,7 +46,7 @@
}
}()
- cfg, err := parseRepositoryConfig(root)
+ cfg, err := parseRepositoryConfig(setupRoot)
if err != nil {return nil, err
}
@@ -62,20 +58,17 @@
}
repo.algo = algo
- objects, objectsRoot, packRoot, err := openObjectStore(root, algo)
+ objects, err := openObjectStore(path, algo)
if err != nil {return nil, err
}
repo.objects = objects
- repo.objectsRoot = objectsRoot
- repo.packRoot = packRoot
- refs, reftableRoot, err := openRefStore(root, algo)
+ refs, err := openRefStore(path, algo)
if err != nil {return nil, err
}
repo.refs = refs
- repo.reftableRoot = reftableRoot
return repo, nil
}
@@ -112,40 +105,13 @@
if err := repo.refs.Close(); err != nil {errs = append(errs, err)
}
- repo.refs = nil
}
if repo.objects != nil { if err := repo.objects.Close(); err != nil {errs = append(errs, err)
}
- repo.objects = nil
}
- if repo.reftableRoot != nil {- if err := repo.reftableRoot.Close(); err != nil {- errs = append(errs, err)
- }
- repo.reftableRoot = nil
- }
- if repo.packRoot != nil {- if err := repo.packRoot.Close(); err != nil {- errs = append(errs, err)
- }
- repo.packRoot = nil
- }
- if repo.objectsRoot != nil {- if err := repo.objectsRoot.Close(); err != nil {- errs = append(errs, err)
- }
- repo.objectsRoot = nil
- }
- if repo.root != nil {- if err := repo.root.Close(); err != nil {- errs = append(errs, err)
- }
- repo.root = nil
- }
-
return errors.Join(errs...)
}
@@ -175,11 +141,18 @@
return algo, nil
}
-func openObjectStore(root *os.Root, algo objectid.Algorithm) (out objectstore.Store, objectsRoot *os.Root, packRoot *os.Root, err error) {- objectsRoot, err = root.OpenRoot("objects")+func openObjectStore(path string, algo objectid.Algorithm) (out objectstore.Store, err error) {+ repoRoot, err := os.OpenRoot(path)
if err != nil {- return nil, nil, nil, fmt.Errorf("repository: open objects: %w", err)+ return nil, fmt.Errorf("repository: open root: %w", err)}
+ defer func() { _ = repoRoot.Close() }()+
+ objectsRoot, err := repoRoot.OpenRoot("objects")+ if err != nil {+ return nil, fmt.Errorf("repository: open objects: %w", err)+ }
+ var packRoot *os.Root
defer func() { if err != nil { if out != nil {@@ -194,7 +167,7 @@
looseStore, err := objectloose.New(objectsRoot, algo)
if err != nil {- return nil, nil, nil, err
+ return nil, err
}
backends := []objectstore.Store{looseStore}@@ -203,77 +176,68 @@
var packedStore *objectpacked.Store
packedStore, err = objectpacked.New(packRoot, algo)
if err != nil {- return nil, nil, nil, err
+ return nil, err
}
backends = append(backends, packedStore)
} else if !errors.Is(err, os.ErrNotExist) {- return nil, nil, nil, fmt.Errorf("repository: open objects/pack: %w", err)+ return nil, fmt.Errorf("repository: open objects/pack: %w", err)}
err = nil
out = objectchain.New(backends...)
- return out, objectsRoot, packRoot, nil
+ return out, nil
}
-func openRefStore(root *os.Root, algo objectid.Algorithm) (out refstore.Store, reftableRoot *os.Root, err error) {- var closePackedStore refstore.Store
- defer func() {- if err != nil {- if out != nil {- _ = out.Close()
- }
- if closePackedStore != nil {- _ = closePackedStore.Close()
- }
- if reftableRoot != nil {- _ = reftableRoot.Close()
- }
- }
- }()
+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(root)
+ hasReftable, err := hasReftableStack(metaRoot)
if err != nil {- return nil, nil, err
+ return nil, err
}
if hasReftable {- reftableRoot, err = root.OpenRoot("reftable")+ reftableRoot, err := metaRoot.OpenRoot("reftable") if err != nil {- return nil, nil, fmt.Errorf("repository: open reftable: %w", err)+ return nil, fmt.Errorf("repository: open reftable: %w", err)}
- var reftableStore *reftable.Store
- reftableStore, err = reftable.New(reftableRoot, algo)
+ reftableStore, err := reftable.New(reftableRoot, algo)
if err != nil {- return nil, nil, err
+ _ = reftableRoot.Close()
+ return nil, err
}
- err = nil
- out = reftableStore
- return reftableStore, reftableRoot, nil
+ return reftableStore, nil
}
- looseStore, err := refloose.New(root, algo)
+ looseRoot, err := os.OpenRoot(path)
if err != nil {- return nil, nil, err
+ return nil, fmt.Errorf("repository: open root for loose refs: %w", err)}
+ looseStore, err := refloose.New(looseRoot, algo)
+ if err != nil {+ _ = looseRoot.Close()
+ return nil, err
+ }
backends := []refstore.Store{looseStore}- packedRefsFile, err := root.Open("packed-refs")+ packedRefsFile, err := metaRoot.Open("packed-refs") if err == nil {packedStore, packedErr := refpacked.New(packedRefsFile, algo)
_ = packedRefsFile.Close()
if packedErr != nil {- err = packedErr
- return nil, nil, err
+ _ = looseStore.Close()
+ return nil, packedErr
}
- closePackedStore = packedStore
backends = append(backends, packedStore)
} else if !errors.Is(err, os.ErrNotExist) {- return nil, nil, fmt.Errorf("repository: open packed-refs: %w", err)+ _ = looseStore.Close()
+ return nil, fmt.Errorf("repository: open packed-refs: %w", err)}
- err = nil
- out = refchain.New(backends...)
- closePackedStore = nil
- return out, nil, nil
+ return refchain.New(backends...), nil
}
func hasReftableStack(root *os.Root) (bool, error) {--
⑨