From 7ddaf1eb2fde11a9e07df0215646c1dca08ccc50 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Sun, 22 Mar 2026 22:07:39 +0000 Subject: refstore, repository: Ownership/lifetimes fix --- refstore/batch.go | 4 ++++ refstore/files/close.go | 13 +++++-------- refstore/files/store.go | 3 ++- refstore/reading.go | 3 +++ refstore/transaction.go | 4 ++++ repository/close.go | 7 +++++++ repository/open.go | 17 +++++++++++++++-- repository/refs.go | 27 +-------------------------- repository/repository.go | 1 + 9 files changed, 42 insertions(+), 37 deletions(-) diff --git a/refstore/batch.go b/refstore/batch.go index 6352159c..b170916b 100644 --- a/refstore/batch.go +++ b/refstore/batch.go @@ -34,8 +34,12 @@ type Batch interface { // Apply validates and applies queued operations, returning one result per // queued operation in order. Fatal backend failures are returned separately. + // + // Apply is terminal. Further use of the batch is undefined behavior. Apply() ([]BatchResult, error) // Abort abandons the batch and releases any resources it holds. + // + // Abort is terminal. Further use of the batch is undefined behavior. Abort() error } diff --git a/refstore/files/close.go b/refstore/files/close.go index 37dde9b9..6dfe3668 100644 --- a/refstore/files/close.go +++ b/refstore/files/close.go @@ -1,13 +1,10 @@ package files // Close releases resources associated with the store. +// +// Store borrows gitRoot, so Close does not close it. +// +// Repeated calls to Close are undefined behavior. func (store *Store) Close() error { - err := store.gitRoot.Close() - commonErr := store.commonRoot.Close() - - if err != nil { - return err - } - - return commonErr + return store.commonRoot.Close() } diff --git a/refstore/files/store.go b/refstore/files/store.go index 6091c000..378c0af0 100644 --- a/refstore/files/store.go +++ b/refstore/files/store.go @@ -14,7 +14,8 @@ import ( // Store reads and writes one Git files ref namespace rooted at one repository // gitdir plus its commondir. // -// Store owns both roots and closes them in Close. +// Store borrows gitRoot and owns commonRoot. Close releases only resources +// opened by the store itself. type Store struct { gitRoot *os.Root commonRoot *os.Root diff --git a/refstore/reading.go b/refstore/reading.go index c302f350..98a5bd51 100644 --- a/refstore/reading.go +++ b/refstore/reading.go @@ -25,5 +25,8 @@ type ReadingStore interface { // The exact pattern language is backend-defined. List(pattern string) ([]ref.Ref, error) // Close releases resources associated with the store. + // + // Repeated calls to Close are undefined behavior unless the implementation + // explicitly documents otherwise. Close() error } diff --git a/refstore/transaction.go b/refstore/transaction.go index 539229c9..fc0a8d76 100644 --- a/refstore/transaction.go +++ b/refstore/transaction.go @@ -37,7 +37,11 @@ type Transaction interface { VerifySymbolic(name, oldTarget string) error // Commit validates and applies all queued operations atomically. + // + // Commit is terminal. Further use of the transaction is undefined behavior. Commit() error // Abort abandons the transaction and releases any resources it holds. + // + // Abort is terminal. Further use of the transaction is undefined behavior. Abort() error } diff --git a/repository/close.go b/repository/close.go index 2b76cde1..9654918c 100644 --- a/repository/close.go +++ b/repository/close.go @@ -49,5 +49,12 @@ func (repo *Repository) Close() error { } } + if repo.refRoot != nil { + err := repo.refRoot.Close() + if err != nil { + errs = append(errs, err) + } + } + return errors.Join(errs...) } diff --git a/repository/open.go b/repository/open.go index 797da7ad..f7f2dd1c 100644 --- a/repository/open.go +++ b/repository/open.go @@ -1,6 +1,11 @@ package repository -import "os" +import ( + "fmt" + "os" + + reffiles "codeberg.org/lindenii/furgit/refstore/files" +) // Open opens a repository and wires object/ref stores from its on-disk format. // @@ -39,12 +44,20 @@ func Open(root *os.Root) (repo *Repository, err error) { repo.objectsLooseForWritingOnly = objectsLooseForWritingOnly repo.objectsWriteRoot = objectsWriteRoot - refs, err := openRefStore(root, algo, detectPackedRefsTimeout(cfg)) + refRoot, err := root.OpenRoot(".") if err != nil { + return nil, fmt.Errorf("repository: open root for refs: %w", err) + } + + refs, err := reffiles.New(refRoot, algo, detectPackedRefsTimeout(cfg)) + if err != nil { + _ = refRoot.Close() + return nil, err } repo.refs = refs + repo.refRoot = refRoot return repo, nil } diff --git a/repository/refs.go b/repository/refs.go index 0af7c462..a3e2cfb8 100644 --- a/repository/refs.go +++ b/repository/refs.go @@ -1,31 +1,6 @@ package repository -import ( - "fmt" - "os" - "time" - - "codeberg.org/lindenii/furgit/objectid" - "codeberg.org/lindenii/furgit/refstore" - reffiles "codeberg.org/lindenii/furgit/refstore/files" -) - -//nolint:ireturn -func openRefStore(root *os.Root, algo objectid.Algorithm, packedRefsTimeout time.Duration) (out refstore.ReadWriteStore, err error) { - refRoot, err := root.OpenRoot(".") - if err != nil { - return nil, fmt.Errorf("repository: open root for refs: %w", err) - } - - store, err := reffiles.New(refRoot, algo, packedRefsTimeout) - if err != nil { - _ = refRoot.Close() - - return nil, err - } - - return store, nil -} +import "codeberg.org/lindenii/furgit/refstore" // Refs returns the configured ref store. // diff --git a/repository/repository.go b/repository/repository.go index 04ca34a8..f9bb2d2b 100644 --- a/repository/repository.go +++ b/repository/repository.go @@ -24,5 +24,6 @@ type Repository struct { objectsPackRoot *os.Root objectsLooseForWritingOnly *objectloose.Store objectsWriteRoot *os.Root + refRoot *os.Root refs refstore.ReadWriteStore } -- cgit v1.3.1-10-gc9f91