shithub: furgit

Download patch

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)
 		}
--