shithub: furgit

Download patch

ref: 84342973be94c2981fac56c6680d9f87eb9fa9ce
parent: 3ba67c27db0cf52b120e4f15c09047aa96d3bcb4
author: Runxi Yu <runxiyu@umich.edu>
date: Mon Mar 30 15:40:23 EDT 2026

repository: Use dual

--- a/repository/objects.go
+++ b/repository/objects.go
@@ -1,14 +1,13 @@
 package repository
 
 import (
-	"errors"
 	"fmt"
 	"os"
 
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	objectstore "codeberg.org/lindenii/furgit/object/store"
+	objectdual "codeberg.org/lindenii/furgit/object/store/dual"
 	objectloose "codeberg.org/lindenii/furgit/object/store/loose"
-	objectmix "codeberg.org/lindenii/furgit/object/store/mix"
 	objectpacked "codeberg.org/lindenii/furgit/object/store/packed"
 )
 
@@ -26,7 +25,7 @@
 	root *os.Root,
 	algo objectid.Algorithm,
 ) (
-	objects objectstore.Reader,
+	objects *objectdual.Dual,
 	objectsRoot *os.Root,
 	objectsPackRoot *os.Root,
 	objectsLoose *objectloose.Store,
@@ -45,32 +44,39 @@
 		return nil, nil, nil, nil, nil, err
 	}
 
-	backends := []objectstore.Reader{objectsLoose}
+	err = objectsRoot.Mkdir("pack", 0o755)
+	if err != nil && !os.IsExist(err) {
+		_ = objectsLoose.Close()
+		_ = objectsRoot.Close()
 
+		return nil, nil, nil, nil, nil, fmt.Errorf("repository: create objects/pack: %w", err)
+	}
+
 	objectsPackRoot, err = objectsRoot.OpenRoot("pack")
-	if err == nil {
-		objectsPacked, err = objectpacked.New(
-			objectsPackRoot,
-			algo,
-			objectpacked.Options{RefreshPolicy: objectpacked.RefreshPolicyNever},
-		)
-		if err != nil {
-			_ = objectsPackRoot.Close()
-			_ = objectsLoose.Close()
-			_ = objectsRoot.Close()
+	if err != nil {
+		_ = objectsLoose.Close()
+		_ = objectsRoot.Close()
 
-			return nil, nil, nil, nil, nil, err
-		}
+		return nil, nil, nil, nil, nil, fmt.Errorf("repository: open objects/pack: %w", err)
+	}
 
-		backends = append(backends, objectsPacked)
-	} else if !errors.Is(err, os.ErrNotExist) {
+	objectsPacked, err = objectpacked.New(
+		objectsPackRoot,
+		algo,
+		objectpacked.Options{
+			RefreshPolicy: objectpacked.RefreshPolicyNever,
+			WriteRev:      true,
+		},
+	)
+	if err != nil {
+		_ = objectsPackRoot.Close()
 		_ = objectsLoose.Close()
 		_ = objectsRoot.Close()
 
-		return nil, nil, nil, nil, nil, fmt.Errorf("repository: open objects/pack: %w", err)
+		return nil, nil, nil, nil, nil, err
 	}
 
-	objects = objectmix.New(backends...)
+	objects = objectdual.New(objectsLoose, objectsPacked)
 
 	return objects, objectsRoot, objectsPackRoot, objectsLoose, objectsPacked, nil
 }
@@ -78,12 +84,17 @@
 // Objects returns the configured object store.
 //
 // Use Objects for direct object-ID lookups, object headers, sizes, raw object
-// bytes, or streamed object contents. Callers who want typed object values
-// should usually prefer [Repository.Fetcher].
+// bytes, streamed object contents, object writes, pack ingestion, and
+// coordinated quarantines. Callers who want typed object values should usually
+// prefer [Repository.Fetcher].
 //
 // Labels: Life-Parent.
 //
 //nolint:ireturn
-func (repo *Repository) Objects() objectstore.Reader {
+func (repo *Repository) Objects() interface {
+	objectstore.Reader
+	objectstore.Writer
+	objectstore.Quarantiner
+} {
 	return repo.objects
 }
--- a/repository/repository.go
+++ b/repository/repository.go
@@ -15,7 +15,7 @@
 	commitgraphread "codeberg.org/lindenii/furgit/format/commitgraph/read"
 	"codeberg.org/lindenii/furgit/object/fetch"
 	objectid "codeberg.org/lindenii/furgit/object/id"
-	objectstore "codeberg.org/lindenii/furgit/object/store"
+	objectdual "codeberg.org/lindenii/furgit/object/store/dual"
 	objectloose "codeberg.org/lindenii/furgit/object/store/loose"
 	objectpacked "codeberg.org/lindenii/furgit/object/store/packed"
 	refstore "codeberg.org/lindenii/furgit/ref/store"
@@ -32,7 +32,7 @@
 	config *config.Config
 	algo   objectid.Algorithm
 
-	objects         objectstore.Reader
+	objects         *objectdual.Dual
 	fetcher         *fetch.Fetcher
 	objectsRoot     *os.Root
 	objectsPackRoot *os.Root
--- a/repository/write_loose.go
+++ /dev/null
@@ -1,12 +1,0 @@
-package repository
-
-import (
-	objectloose "codeberg.org/lindenii/furgit/object/store/loose"
-)
-
-// LooseStoreForWriting returns the repository's loose-object writer.
-//
-// Labels: Life-Parent, Close-No.
-func (repo *Repository) LooseStoreForWriting() *objectloose.Store {
-	return repo.objectsLoose
-}
--- a/repository/write_loose_test.go
+++ b/repository/write_loose_test.go
@@ -23,7 +23,7 @@
 
 		content := []byte("write-loose-bytes-content\n")
 
-		gotID, err := repo.LooseStoreForWriting().WriteBytesContent(objecttype.TypeBlob, content)
+		gotID, err := repo.Objects().WriteBytesContent(objecttype.TypeBlob, content)
 		if err != nil {
 			t.Fatalf("WriteLooseBytesContent: %v", err)
 		}
@@ -62,7 +62,7 @@
 
 		content := []byte("write-loose-reader-content\n")
 
-		gotID, err := repo.LooseStoreForWriting().WriteReaderContent(objecttype.TypeBlob, int64(len(content)), bytes.NewReader(content))
+		gotID, err := repo.Objects().WriteReaderContent(objecttype.TypeBlob, int64(len(content)), bytes.NewReader(content))
 		if err != nil {
 			t.Fatalf("WriteLooseReaderContent: %v", err)
 		}
@@ -92,7 +92,7 @@
 			t.Fatalf("ReadStoredBytesFull: %v", err)
 		}
 
-		idFromBytes, err := repo.LooseStoreForWriting().WriteBytesFull(raw)
+		idFromBytes, err := repo.Objects().WriteBytesFull(raw)
 		if err != nil {
 			t.Fatalf("WriteLooseBytesFull: %v", err)
 		}
@@ -101,7 +101,7 @@
 			t.Fatalf("WriteLooseBytesFull id = %s, want %s", idFromBytes, commitID)
 		}
 
-		idFromReader, err := repo.LooseStoreForWriting().WriteReaderFull(bytes.NewReader(raw))
+		idFromReader, err := repo.Objects().WriteReaderFull(bytes.NewReader(raw))
 		if err != nil {
 			t.Fatalf("WriteLooseReaderFull: %v", err)
 		}
--