shithub: furgit

Download patch

ref: e667c3c52a535ee67fe895bb0240fbad6e920087
parent: 175c8ed3c342f34110cdca42dc4027050b39d7fb
author: Runxi Yu <me@runxiyu.org>
date: Sat Mar 7 13:09:20 EST 2026

refstore/files: Accept timeout instead of reading from config

And split things up again.

--- /dev/null
+++ b/refstore/files/close.go
@@ -1,0 +1,13 @@
+package files
+
+// Close releases resources associated with the store.
+func (store *Store) Close() error {
+	err := store.gitRoot.Close()
+	commonErr := store.commonRoot.Close()
+
+	if err != nil {
+		return err
+	}
+
+	return commonErr
+}
--- /dev/null
+++ b/refstore/files/errors.go
@@ -1,0 +1,16 @@
+package files
+
+import "fmt"
+
+type brokenRefError struct {
+	name string
+	err  error
+}
+
+func (err brokenRefError) Error() string {
+	return fmt.Sprintf("refstore/files: broken reference %q: %v", err.name, err.err)
+}
+
+func (err brokenRefError) Unwrap() error {
+	return err.err
+}
--- a/refstore/files/helpers_test.go
+++ b/refstore/files/helpers_test.go
@@ -5,6 +5,7 @@
 	"slices"
 	"strings"
 	"testing"
+	"time"
 
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	"codeberg.org/lindenii/furgit/objectid"
@@ -11,12 +12,14 @@
 	"codeberg.org/lindenii/furgit/refstore/files"
 )
 
+const testPackedRefsTimeout = time.Second
+
 func openFilesStore(t *testing.T, testRepo *testgit.TestRepo, algo objectid.Algorithm) *files.Store {
 	t.Helper()
 
 	root := testRepo.OpenGitRoot(t)
 
-	store, err := files.New(root, algo)
+	store, err := files.New(root, algo, testPackedRefsTimeout)
 	if err != nil {
 		t.Fatalf("files.New: %v", err)
 	}
@@ -27,7 +30,7 @@
 func openFilesStoreAt(t *testing.T, root *os.Root, algo objectid.Algorithm) *files.Store {
 	t.Helper()
 
-	store, err := files.New(root, algo)
+	store, err := files.New(root, algo, testPackedRefsTimeout)
 	if err != nil {
 		t.Fatalf("files.New: %v", err)
 	}
--- /dev/null
+++ b/refstore/files/new.go
@@ -1,0 +1,29 @@
+package files
+
+import (
+	"math/rand"
+	"os"
+	"time"
+
+	"codeberg.org/lindenii/furgit/objectid"
+)
+
+// New creates one files ref store rooted at one repository gitdir.
+func New(root *os.Root, algo objectid.Algorithm, packedRefsTimeout time.Duration) (*Store, error) {
+	if algo.Size() == 0 {
+		return nil, objectid.ErrInvalidAlgorithm
+	}
+
+	commonRoot, err := openCommonRoot(root)
+	if err != nil {
+		return nil, err
+	}
+
+	return &Store{
+		gitRoot:           root,
+		commonRoot:        commonRoot,
+		algo:              algo,
+		lockRand:          rand.New(rand.NewSource(time.Now().UnixNano())), //nolint:gosec
+		packedRefsTimeout: packedRefsTimeout,
+	}, nil
+}
--- a/refstore/files/packed_delete_test.go
+++ b/refstore/files/packed_delete_test.go
@@ -232,7 +232,6 @@
 		testRepo.UpdateRef(t, prefix+"/foo", packedID)
 		testRepo.PackRefs(t, "--all", "--prune")
 		testRepo.UpdateRef(t, prefix+"/foo", looseID)
-		testRepo.Run(t, "config", "core.packedrefstimeout", "3000")
 		testRepo.WriteFile(t, "packed-refs.lock", []byte{}, 0o644)
 
 		store := openFilesStore(t, testRepo, algo)
--- /dev/null
+++ b/refstore/files/packed_parse.go
@@ -1,0 +1,113 @@
+package files
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"strings"
+
+	"codeberg.org/lindenii/furgit/objectid"
+	"codeberg.org/lindenii/furgit/ref"
+)
+
+func parsePackedRefs(r io.Reader, algo objectid.Algorithm) (map[string]ref.Detached, []ref.Detached, error) {
+	byName := make(map[string]ref.Detached)
+	ordered := make([]ref.Detached, 0, 32)
+
+	br := bufio.NewReader(r)
+	prev := -1
+	lineNum := 0
+	hexsz := algo.Size() * 2
+
+	for {
+		line, err := br.ReadString('\n')
+		if err != nil && err != io.EOF {
+			return nil, nil, err
+		}
+
+		if line == "" && err == io.EOF {
+			break
+		}
+
+		lineNum++
+		hadNewline := strings.HasSuffix(line, "\n")
+		line = strings.TrimSuffix(line, "\n")
+
+		if err == io.EOF && !hadNewline {
+			return nil, nil, fmt.Errorf("refstore/files: line %d: unterminated line", lineNum)
+		}
+
+		if line == "" || strings.HasPrefix(line, "#") {
+			if err == io.EOF {
+				break
+			}
+
+			continue
+		}
+
+		if strings.HasPrefix(line, "^") {
+			if prev < 0 {
+				return nil, nil, fmt.Errorf("refstore/files: line %d: peeled line without preceding ref", lineNum)
+			}
+
+			if len(line) != hexsz+1 {
+				return nil, nil, fmt.Errorf("refstore/files: line %d: malformed peeled line", lineNum)
+			}
+
+			peeled, parseErr := objectid.ParseHex(algo, line[1:])
+			if parseErr != nil {
+				return nil, nil, fmt.Errorf("refstore/files: line %d: invalid peeled oid: %w", lineNum, parseErr)
+			}
+
+			peeledCopy := peeled
+			cur := ordered[prev]
+			cur.Peeled = &peeledCopy
+			ordered[prev] = cur
+			byName[cur.Name()] = cur
+
+			if err == io.EOF {
+				break
+			}
+
+			continue
+		}
+
+		if len(line) < hexsz+2 {
+			return nil, nil, fmt.Errorf("refstore/files: line %d: malformed entry", lineNum)
+		}
+
+		if line[hexsz] != ' ' {
+			return nil, nil, fmt.Errorf("refstore/files: line %d: malformed entry", lineNum)
+		}
+
+		idText := line[:hexsz]
+
+		name := line[hexsz+1:]
+		if name == "" {
+			return nil, nil, fmt.Errorf("refstore/files: line %d: empty ref name", lineNum)
+		}
+
+		id, parseErr := objectid.ParseHex(algo, idText)
+		if parseErr != nil {
+			return nil, nil, fmt.Errorf("refstore/files: line %d: invalid oid: %w", lineNum, parseErr)
+		}
+
+		if _, exists := byName[name]; exists {
+			return nil, nil, fmt.Errorf("refstore/files: line %d: duplicate ref %q", lineNum, name)
+		}
+
+		detached := ref.Detached{
+			RefName: name,
+			ID:      id,
+		}
+		ordered = append(ordered, detached)
+		prev = len(ordered) - 1
+		byName[name] = detached
+
+		if err == io.EOF {
+			break
+		}
+	}
+
+	return byName, ordered, nil
+}
--- /dev/null
+++ b/refstore/files/packed_read.go
@@ -1,0 +1,35 @@
+package files
+
+import (
+	"errors"
+	"fmt"
+	"os"
+
+	"codeberg.org/lindenii/furgit/ref"
+)
+
+func (store *Store) readPackedRefs() (*packedRefs, error) {
+	file, err := store.commonRoot.Open("packed-refs")
+	if err != nil {
+		if errors.Is(err, os.ErrNotExist) {
+			return &packedRefs{
+				byName:  make(map[string]ref.Detached),
+				ordered: nil,
+			}, nil
+		}
+
+		return nil, fmt.Errorf("refstore/files: open packed-refs: %w", err)
+	}
+
+	defer func() { _ = file.Close() }()
+
+	byName, ordered, err := parsePackedRefs(file, store.algo)
+	if err != nil {
+		return nil, err
+	}
+
+	return &packedRefs{
+		byName:  byName,
+		ordered: ordered,
+	}, nil
+}
--- a/refstore/files/packed_refs.go
+++ b/refstore/files/packed_refs.go
@@ -1,14 +1,6 @@
 package files
 
 import (
-	"bufio"
-	"errors"
-	"fmt"
-	"io"
-	"os"
-	"strings"
-
-	"codeberg.org/lindenii/furgit/objectid"
 	"codeberg.org/lindenii/furgit/ref"
 )
 
@@ -15,136 +7,4 @@
 type packedRefs struct {
 	byName  map[string]ref.Detached
 	ordered []ref.Detached
-}
-
-func (store *Store) readPackedRefs() (*packedRefs, error) {
-	file, err := store.commonRoot.Open("packed-refs")
-	if err != nil {
-		if errorsIsNotExist(err) {
-			return &packedRefs{
-				byName:  make(map[string]ref.Detached),
-				ordered: nil,
-			}, nil
-		}
-
-		return nil, fmt.Errorf("refstore/files: open packed-refs: %w", err)
-	}
-
-	defer func() { _ = file.Close() }()
-
-	byName, ordered, err := parsePackedRefs(file, store.algo)
-	if err != nil {
-		return nil, err
-	}
-
-	return &packedRefs{
-		byName:  byName,
-		ordered: ordered,
-	}, nil
-}
-
-func parsePackedRefs(r io.Reader, algo objectid.Algorithm) (map[string]ref.Detached, []ref.Detached, error) {
-	byName := make(map[string]ref.Detached)
-	ordered := make([]ref.Detached, 0, 32)
-
-	br := bufio.NewReader(r)
-	prev := -1
-	lineNum := 0
-	hexsz := algo.Size() * 2
-
-	for {
-		line, err := br.ReadString('\n')
-		if err != nil && err != io.EOF {
-			return nil, nil, err
-		}
-
-		if line == "" && err == io.EOF {
-			break
-		}
-
-		lineNum++
-		hadNewline := strings.HasSuffix(line, "\n")
-		line = strings.TrimSuffix(line, "\n")
-
-		if err == io.EOF && !hadNewline {
-			return nil, nil, fmt.Errorf("refstore/files: line %d: unterminated line", lineNum)
-		}
-
-		if line == "" || strings.HasPrefix(line, "#") {
-			if err == io.EOF {
-				break
-			}
-
-			continue
-		}
-
-		if strings.HasPrefix(line, "^") {
-			if prev < 0 {
-				return nil, nil, fmt.Errorf("refstore/files: line %d: peeled line without preceding ref", lineNum)
-			}
-
-			if len(line) != hexsz+1 {
-				return nil, nil, fmt.Errorf("refstore/files: line %d: malformed peeled line", lineNum)
-			}
-
-			peeled, parseErr := objectid.ParseHex(algo, line[1:])
-			if parseErr != nil {
-				return nil, nil, fmt.Errorf("refstore/files: line %d: invalid peeled oid: %w", lineNum, parseErr)
-			}
-
-			peeledCopy := peeled
-			cur := ordered[prev]
-			cur.Peeled = &peeledCopy
-			ordered[prev] = cur
-			byName[cur.Name()] = cur
-
-			if err == io.EOF {
-				break
-			}
-
-			continue
-		}
-
-		if len(line) < hexsz+2 {
-			return nil, nil, fmt.Errorf("refstore/files: line %d: malformed entry", lineNum)
-		}
-
-		if line[hexsz] != ' ' {
-			return nil, nil, fmt.Errorf("refstore/files: line %d: malformed entry", lineNum)
-		}
-
-		idText := line[:hexsz]
-
-		name := line[hexsz+1:]
-		if name == "" {
-			return nil, nil, fmt.Errorf("refstore/files: line %d: empty ref name", lineNum)
-		}
-
-		id, parseErr := objectid.ParseHex(algo, idText)
-		if parseErr != nil {
-			return nil, nil, fmt.Errorf("refstore/files: line %d: invalid oid: %w", lineNum, parseErr)
-		}
-
-		if _, exists := byName[name]; exists {
-			return nil, nil, fmt.Errorf("refstore/files: line %d: duplicate ref %q", lineNum, name)
-		}
-
-		detached := ref.Detached{
-			RefName: name,
-			ID:      id,
-		}
-		ordered = append(ordered, detached)
-		prev = len(ordered) - 1
-		byName[name] = detached
-
-		if err == io.EOF {
-			break
-		}
-	}
-
-	return byName, ordered, nil
-}
-
-func errorsIsNotExist(err error) bool {
-	return errors.Is(err, os.ErrNotExist)
 }
--- a/refstore/files/read.go
+++ /dev/null
@@ -1,74 +1,0 @@
-package files
-
-import (
-	"errors"
-	"fmt"
-	"os"
-	"strings"
-
-	"codeberg.org/lindenii/furgit/objectid"
-	"codeberg.org/lindenii/furgit/ref"
-	"codeberg.org/lindenii/furgit/refstore"
-)
-
-type brokenRefError struct {
-	name string
-	err  error
-}
-
-func (err brokenRefError) Error() string {
-	return fmt.Sprintf("refstore/files: broken reference %q: %v", err.name, err.err)
-}
-
-func (err brokenRefError) Unwrap() error {
-	return err.err
-}
-
-func (store *Store) readLooseRef(name string) (ref.Ref, error) { //nolint:ireturn
-	refPath := store.loosePath(name)
-
-	data, err := store.rootFor(refPath.root).ReadFile(refPath.path)
-	if err != nil {
-		if errors.Is(err, os.ErrNotExist) {
-			return nil, refstore.ErrReferenceNotFound
-		}
-
-		return nil, err
-	}
-
-	line := trimTrailingRefWhitespace(string(data))
-	if strings.HasPrefix(line, "ref:") {
-		target := strings.TrimLeftFunc(line[len("ref:"):], isRefWhitespace)
-		if target == "" {
-			return nil, brokenRefError{name: name, err: fmt.Errorf("empty symbolic target")}
-		}
-
-		return ref.Symbolic{
-			RefName: name,
-			Target:  target,
-		}, nil
-	}
-
-	id, err := objectid.ParseHex(store.algo, line)
-	if err != nil {
-		return nil, brokenRefError{name: name, err: err}
-	}
-
-	return ref.Detached{
-		RefName: name,
-		ID:      id,
-	}, nil
-}
-
-func trimTrailingRefWhitespace(text string) string {
-	return strings.TrimRightFunc(text, isRefWhitespace)
-}
-
-func isRefWhitespace(r rune) bool {
-	switch r {
-	case ' ', '\t', '\n', '\r', '\v', '\f':
-		return true
-	default:
-		return false
-	}
-}
--- a/refstore/files/read_list.go
+++ b/refstore/files/read_list.go
@@ -2,10 +2,8 @@
 
 import (
 	"errors"
-	"os"
 	"path"
 	"slices"
-	"strings"
 
 	"codeberg.org/lindenii/furgit/ref"
 	"codeberg.org/lindenii/furgit/refstore"
@@ -75,74 +73,4 @@
 	}
 
 	return refs, nil
-}
-
-func (store *Store) collectLooseRefNames() ([]string, error) {
-	names := make([]string, 0, 16)
-	seen := make(map[string]struct{}, 16)
-
-	_, err := store.gitRoot.Stat("HEAD")
-	if err == nil {
-		names = append(names, "HEAD")
-		seen["HEAD"] = struct{}{}
-	} else if !errors.Is(err, os.ErrNotExist) {
-		return nil, err
-	}
-
-	var walk func(*os.Root, string) error
-
-	walk = func(root *os.Root, dir string) error {
-		file, openErr := root.Open(dir)
-		if openErr != nil {
-			if errors.Is(openErr, os.ErrNotExist) {
-				return nil
-			}
-
-			return openErr
-		}
-
-		defer func() { _ = file.Close() }()
-
-		entries, readErr := file.ReadDir(-1)
-		if readErr != nil {
-			return readErr
-		}
-
-		for _, entry := range entries {
-			name := path.Join(dir, entry.Name())
-			if entry.IsDir() {
-				err := walk(root, name)
-				if err != nil {
-					return err
-				}
-
-				continue
-			}
-
-			if strings.HasSuffix(name, ".lock") {
-				continue
-			}
-
-			if _, ok := seen[name]; ok {
-				continue
-			}
-
-			seen[name] = struct{}{}
-			names = append(names, name)
-		}
-
-		return nil
-	}
-
-	err = walk(store.commonRoot, "refs")
-	if err != nil {
-		return nil, err
-	}
-
-	err = walk(store.gitRoot, "refs")
-	if err != nil {
-		return nil, err
-	}
-
-	return names, nil
 }
--- /dev/null
+++ b/refstore/files/read_list_collect.go
@@ -1,0 +1,78 @@
+package files
+
+import (
+	"errors"
+	"os"
+	"path"
+	"strings"
+)
+
+func (store *Store) collectLooseRefNames() ([]string, error) {
+	names := make([]string, 0, 16)
+	seen := make(map[string]struct{}, 16)
+
+	_, err := store.gitRoot.Stat("HEAD")
+	if err == nil {
+		names = append(names, "HEAD")
+		seen["HEAD"] = struct{}{}
+	} else if !errors.Is(err, os.ErrNotExist) {
+		return nil, err
+	}
+
+	var walk func(*os.Root, string) error
+
+	walk = func(root *os.Root, dir string) error {
+		file, openErr := root.Open(dir)
+		if openErr != nil {
+			if errors.Is(openErr, os.ErrNotExist) {
+				return nil
+			}
+
+			return openErr
+		}
+
+		defer func() { _ = file.Close() }()
+
+		entries, readErr := file.ReadDir(-1)
+		if readErr != nil {
+			return readErr
+		}
+
+		for _, entry := range entries {
+			name := path.Join(dir, entry.Name())
+			if entry.IsDir() {
+				err := walk(root, name)
+				if err != nil {
+					return err
+				}
+
+				continue
+			}
+
+			if strings.HasSuffix(name, ".lock") {
+				continue
+			}
+
+			if _, ok := seen[name]; ok {
+				continue
+			}
+
+			seen[name] = struct{}{}
+			names = append(names, name)
+		}
+
+		return nil
+	}
+
+	err = walk(store.commonRoot, "refs")
+	if err != nil {
+		return nil, err
+	}
+
+	err = walk(store.gitRoot, "refs")
+	if err != nil {
+		return nil, err
+	}
+
+	return names, nil
+}
--- /dev/null
+++ b/refstore/files/read_loose.go
@@ -1,0 +1,48 @@
+package files
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"strings"
+
+	"codeberg.org/lindenii/furgit/objectid"
+	"codeberg.org/lindenii/furgit/ref"
+	"codeberg.org/lindenii/furgit/refstore"
+)
+
+func (store *Store) readLooseRef(name string) (ref.Ref, error) { //nolint:ireturn
+	refPath := store.loosePath(name)
+
+	data, err := store.rootFor(refPath.root).ReadFile(refPath.path)
+	if err != nil {
+		if errors.Is(err, os.ErrNotExist) {
+			return nil, refstore.ErrReferenceNotFound
+		}
+
+		return nil, err
+	}
+
+	line := strings.TrimRightFunc(string(data), isRefWhitespace)
+	if strings.HasPrefix(line, "ref:") {
+		target := strings.TrimLeftFunc(line[len("ref:"):], isRefWhitespace)
+		if target == "" {
+			return nil, brokenRefError{name: name, err: fmt.Errorf("empty symbolic target")}
+		}
+
+		return ref.Symbolic{
+			RefName: name,
+			Target:  target,
+		}, nil
+	}
+
+	id, err := objectid.ParseHex(store.algo, line)
+	if err != nil {
+		return nil, brokenRefError{name: name, err: err}
+	}
+
+	return ref.Detached{
+		RefName: name,
+		ID:      id,
+	}, nil
+}
--- a/refstore/files/read_resolve.go
+++ b/refstore/files/read_resolve.go
@@ -2,8 +2,6 @@
 
 import (
 	"errors"
-	"fmt"
-	"strings"
 
 	"codeberg.org/lindenii/furgit/ref"
 	"codeberg.org/lindenii/furgit/refstore"
@@ -40,38 +38,4 @@
 	}
 
 	return detached, nil
-}
-
-// ResolveFully resolves symbolic references through the visible files store
-// namespace until one detached reference is reached.
-func (store *Store) ResolveFully(name string) (ref.Detached, error) {
-	cur := name
-	seen := make(map[string]struct{})
-
-	for {
-		if _, ok := seen[cur]; ok {
-			return ref.Detached{}, fmt.Errorf("refstore/files: symbolic reference cycle at %q", cur)
-		}
-
-		seen[cur] = struct{}{}
-
-		resolved, err := store.Resolve(cur)
-		if err != nil {
-			return ref.Detached{}, err
-		}
-
-		switch resolved := resolved.(type) {
-		case ref.Detached:
-			return resolved, nil
-		case ref.Symbolic:
-			target := strings.TrimSpace(resolved.Target)
-			if target == "" {
-				return ref.Detached{}, fmt.Errorf("refstore/files: symbolic reference %q has empty target", resolved.Name())
-			}
-
-			cur = target
-		default:
-			return ref.Detached{}, fmt.Errorf("refstore/files: unsupported reference type %T", resolved)
-		}
-	}
 }
--- /dev/null
+++ b/refstore/files/read_resolve_fully.go
@@ -1,0 +1,42 @@
+package files
+
+import (
+	"fmt"
+	"strings"
+
+	"codeberg.org/lindenii/furgit/ref"
+)
+
+// ResolveFully resolves symbolic references through the visible files store
+// namespace until one detached reference is reached.
+func (store *Store) ResolveFully(name string) (ref.Detached, error) {
+	cur := name
+	seen := make(map[string]struct{})
+
+	for {
+		if _, ok := seen[cur]; ok {
+			return ref.Detached{}, fmt.Errorf("refstore/files: symbolic reference cycle at %q", cur)
+		}
+
+		seen[cur] = struct{}{}
+
+		resolved, err := store.Resolve(cur)
+		if err != nil {
+			return ref.Detached{}, err
+		}
+
+		switch resolved := resolved.(type) {
+		case ref.Detached:
+			return resolved, nil
+		case ref.Symbolic:
+			target := strings.TrimSpace(resolved.Target)
+			if target == "" {
+				return ref.Detached{}, fmt.Errorf("refstore/files: symbolic reference %q has empty target", resolved.Name())
+			}
+
+			cur = target
+		default:
+			return ref.Detached{}, fmt.Errorf("refstore/files: unsupported reference type %T", resolved)
+		}
+	}
+}
--- /dev/null
+++ b/refstore/files/root_for.go
@@ -1,0 +1,13 @@
+package files
+
+import (
+	"os"
+)
+
+func (store *Store) rootFor(kind rootKind) *os.Root {
+	if kind == rootCommon {
+		return store.commonRoot
+	}
+
+	return store.gitRoot
+}
--- /dev/null
+++ b/refstore/files/root_kind.go
@@ -1,0 +1,8 @@
+package files
+
+type rootKind uint8
+
+const (
+	rootGit rootKind = iota
+	rootCommon
+)
--- /dev/null
+++ b/refstore/files/root_loose_path.go
@@ -1,0 +1,24 @@
+package files
+
+import (
+	"path"
+
+	"codeberg.org/lindenii/furgit/ref/refname"
+)
+
+func (store *Store) loosePath(name string) refPath {
+	parsed := refname.ParseWorktree(name)
+	switch parsed.Type {
+	case refname.WorktreeCurrent:
+		return refPath{root: rootGit, path: parsed.BareRefName}
+	case refname.WorktreeMain, refname.WorktreeShared:
+		return refPath{root: rootCommon, path: parsed.BareRefName}
+	case refname.WorktreeOther:
+		return refPath{
+			root: rootCommon,
+			path: path.Join("worktrees", parsed.WorktreeName, parsed.BareRefName),
+		}
+	default:
+		return refPath{root: rootCommon, path: name}
+	}
+}
--- /dev/null
+++ b/refstore/files/root_open_common.go
@@ -1,0 +1,31 @@
+package files
+
+import (
+	"errors"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+func openCommonRoot(gitRoot *os.Root) (*os.Root, error) {
+	content, err := gitRoot.ReadFile("commondir")
+	if err != nil {
+		if errors.Is(err, os.ErrNotExist) {
+			return gitRoot.OpenRoot(".")
+		}
+
+		return nil, err
+	}
+
+	commonDir := strings.TrimSpace(string(content))
+	if commonDir == "" {
+		return nil, os.ErrNotExist
+	}
+
+	if filepath.IsAbs(commonDir) {
+		return os.OpenRoot(commonDir)
+	}
+
+	// This is okay because that's how Git defines it anyway.
+	return os.OpenRoot(filepath.Join(gitRoot.Name(), commonDir))
+}
--- /dev/null
+++ b/refstore/files/root_ref_path.go
@@ -1,0 +1,28 @@
+package files
+
+import (
+	"fmt"
+	"strings"
+)
+
+type refPath struct {
+	root rootKind
+	path string
+}
+
+func (tx *Transaction) targetKey(name refPath) string {
+	return fmt.Sprintf("%d:%s", name.root, name.path)
+}
+
+func refPathFromKey(key string) refPath {
+	rootValue, pathValue, ok := strings.Cut(key, ":")
+	if !ok || rootValue == "" {
+		return refPath{root: rootCommon, path: key}
+	}
+
+	if rootValue == "0" {
+		return refPath{root: rootGit, path: pathValue}
+	}
+
+	return refPath{root: rootCommon, path: pathValue}
+}
--- a/refstore/files/store.go
+++ b/refstore/files/store.go
@@ -3,18 +3,11 @@
 package files
 
 import (
-	"errors"
-	"io"
 	"math/rand"
 	"os"
-	"path"
-	"path/filepath"
-	"strings"
 	"time"
 
-	"codeberg.org/lindenii/furgit/config"
 	"codeberg.org/lindenii/furgit/objectid"
-	"codeberg.org/lindenii/furgit/ref/refname"
 	"codeberg.org/lindenii/furgit/refstore"
 )
 
@@ -35,118 +28,3 @@
 	_ refstore.ReadingStore       = (*Store)(nil)
 	_ refstore.TransactionalStore = (*Store)(nil)
 )
-
-type rootKind uint8
-
-const (
-	rootGit rootKind = iota
-	rootCommon
-)
-
-type refPath struct {
-	root rootKind
-	path string
-}
-
-// New creates one files ref store rooted at one repository gitdir.
-func New(root *os.Root, algo objectid.Algorithm) (*Store, error) {
-	if algo.Size() == 0 {
-		return nil, objectid.ErrInvalidAlgorithm
-	}
-
-	commonRoot, err := openCommonRoot(root)
-	if err != nil {
-		return nil, err
-	}
-
-	return &Store{
-		gitRoot:           root,
-		commonRoot:        commonRoot,
-		algo:              algo,
-		lockRand:          rand.New(rand.NewSource(time.Now().UnixNano())), //nolint:gosec
-		packedRefsTimeout: detectPackedRefsTimeout(commonRoot),
-	}, nil
-}
-
-// Close releases resources associated with the store.
-func (store *Store) Close() error {
-	err := store.gitRoot.Close()
-	commonErr := store.commonRoot.Close()
-
-	if err != nil {
-		return err
-	}
-
-	return commonErr
-}
-
-func openCommonRoot(gitRoot *os.Root) (*os.Root, error) {
-	content, err := gitRoot.ReadFile("commondir")
-	if err != nil {
-		if errorsIsNotExist(err) {
-			return gitRoot.OpenRoot(".")
-		}
-
-		return nil, err
-	}
-
-	commonDir := strings.TrimSpace(string(content))
-	if commonDir == "" {
-		return nil, os.ErrNotExist
-	}
-
-	if filepath.IsAbs(commonDir) {
-		return os.OpenRoot(commonDir)
-	}
-
-	// This is okay because that's how Git defines it anyway.
-	return os.OpenRoot(filepath.Join(gitRoot.Name(), commonDir))
-}
-
-func (store *Store) rootFor(kind rootKind) *os.Root {
-	if kind == rootCommon {
-		return store.commonRoot
-	}
-
-	return store.gitRoot
-}
-
-func (store *Store) loosePath(name string) refPath {
-	parsed := refname.ParseWorktree(name)
-	switch parsed.Type {
-	case refname.WorktreeCurrent:
-		return refPath{root: rootGit, path: parsed.BareRefName}
-	case refname.WorktreeMain, refname.WorktreeShared:
-		return refPath{root: rootCommon, path: parsed.BareRefName}
-	case refname.WorktreeOther:
-		return refPath{
-			root: rootCommon,
-			path: path.Join("worktrees", parsed.WorktreeName, parsed.BareRefName),
-		}
-	default:
-		return refPath{root: rootCommon, path: name}
-	}
-}
-
-func detectPackedRefsTimeout(commonRoot *os.Root) time.Duration {
-	const defaultTimeout = time.Second
-
-	file, err := commonRoot.Open("config")
-	if err != nil {
-		return defaultTimeout
-	}
-
-	defer func() { _ = file.Close() }()
-
-	cfg, err := config.ParseConfig(file)
-	if err != nil && !errors.Is(err, io.EOF) {
-		return defaultTimeout
-	}
-
-	timeoutValue, err := cfg.Lookup("core", "", "packedrefstimeout").Int()
-	if err != nil {
-		return defaultTimeout
-	}
-
-	return time.Duration(timeoutValue) * time.Millisecond
-}
--- a/refstore/files/transaction.go
+++ b/refstore/files/transaction.go
@@ -1,66 +1,9 @@
 package files
 
 import (
-	"errors"
-	"fmt"
-	"os"
-	"strings"
-
-	"codeberg.org/lindenii/furgit/objectid"
-	"codeberg.org/lindenii/furgit/ref/refname"
 	"codeberg.org/lindenii/furgit/refstore"
 )
 
-type txKind uint8
-
-const (
-	txCreate txKind = iota
-	txUpdate
-	txDelete
-	txVerify
-	txCreateSymbolic
-	txUpdateSymbolic
-	txDeleteSymbolic
-	txVerifySymbolic
-)
-
-type txOp struct {
-	name      string
-	kind      txKind
-	newID     objectid.ObjectID
-	oldID     objectid.ObjectID
-	newTarget string
-	oldTarget string
-}
-
-type directKind uint8
-
-const (
-	directMissing directKind = iota
-	directDetached
-	directSymbolic
-)
-
-type directRef struct {
-	kind     directKind
-	name     string
-	id       objectid.ObjectID
-	target   string
-	isLoose  bool
-	isPacked bool
-}
-
-type resolvedWriteTarget struct {
-	name string
-	loc  refPath
-	ref  directRef
-}
-
-type preparedTxOp struct {
-	op     txOp
-	target resolvedWriteTarget
-}
-
 type Transaction struct {
 	store  *Store
 	ops    []txOp
@@ -68,195 +11,3 @@
 }
 
 var _ refstore.Transaction = (*Transaction)(nil)
-
-// BeginTransaction creates one new files transaction.
-//
-//nolint:ireturn
-func (store *Store) BeginTransaction() (refstore.Transaction, error) {
-	return &Transaction{
-		store: store,
-		ops:   make([]txOp, 0, 8),
-	}, nil
-}
-
-func (tx *Transaction) Create(name string, newID objectid.ObjectID) error {
-	return tx.queue(txOp{name: name, kind: txCreate, newID: newID})
-}
-
-func (tx *Transaction) Update(name string, newID, oldID objectid.ObjectID) error {
-	return tx.queue(txOp{name: name, kind: txUpdate, newID: newID, oldID: oldID})
-}
-
-func (tx *Transaction) Delete(name string, oldID objectid.ObjectID) error {
-	return tx.queue(txOp{name: name, kind: txDelete, oldID: oldID})
-}
-
-func (tx *Transaction) Verify(name string, oldID objectid.ObjectID) error {
-	return tx.queue(txOp{name: name, kind: txVerify, oldID: oldID})
-}
-
-func (tx *Transaction) CreateSymbolic(name, newTarget string) error {
-	return tx.queue(txOp{name: name, kind: txCreateSymbolic, newTarget: newTarget})
-}
-
-func (tx *Transaction) UpdateSymbolic(name, newTarget, oldTarget string) error {
-	return tx.queue(txOp{name: name, kind: txUpdateSymbolic, newTarget: newTarget, oldTarget: oldTarget})
-}
-
-func (tx *Transaction) DeleteSymbolic(name, oldTarget string) error {
-	return tx.queue(txOp{name: name, kind: txDeleteSymbolic, oldTarget: oldTarget})
-}
-
-func (tx *Transaction) VerifySymbolic(name, oldTarget string) error {
-	return tx.queue(txOp{name: name, kind: txVerifySymbolic, oldTarget: oldTarget})
-}
-
-func (tx *Transaction) Commit() error {
-	err := tx.ensureOpen()
-	if err != nil {
-		return err
-	}
-
-	prepared, err := tx.prepare()
-	if err != nil {
-		tx.closed = true
-
-		return err
-	}
-
-	defer func() {
-		_ = tx.cleanup(prepared)
-	}()
-
-	for _, item := range prepared {
-		if item.op.kind == txDelete || item.op.kind == txDeleteSymbolic || item.op.kind == txVerify || item.op.kind == txVerifySymbolic {
-			continue
-		}
-
-		err = tx.writeLoose(item)
-		if err != nil {
-			tx.closed = true
-
-			return err
-		}
-	}
-
-	err = tx.applyPackedDeletes(prepared)
-	if err != nil {
-		tx.closed = true
-
-		return err
-	}
-
-	for _, item := range prepared {
-		switch item.op.kind {
-		case txDelete, txDeleteSymbolic:
-			if item.target.ref.isLoose {
-				err = tx.store.rootFor(item.target.loc.root).Remove(item.target.loc.path)
-				if err != nil && !errors.Is(err, os.ErrNotExist) {
-					tx.closed = true
-
-					return err
-				}
-
-				tx.tryRemoveEmptyParents(item.target.name)
-			}
-		case txCreate, txUpdate, txVerify, txCreateSymbolic, txUpdateSymbolic, txVerifySymbolic:
-		}
-	}
-
-	tx.closed = true
-
-	return nil
-}
-
-func (tx *Transaction) Abort() error {
-	err := tx.ensureOpen()
-	if err != nil {
-		return err
-	}
-
-	tx.closed = true
-
-	return nil
-}
-
-func (tx *Transaction) ensureOpen() error {
-	if tx.closed {
-		return fmt.Errorf("refstore/files: transaction already closed")
-	}
-
-	return nil
-}
-
-func (tx *Transaction) queue(op txOp) error {
-	err := tx.ensureOpen()
-	if err != nil {
-		return err
-	}
-
-	err = tx.validateOp(op)
-	if err != nil {
-		return err
-	}
-
-	tx.ops = append(tx.ops, op)
-
-	return nil
-}
-
-func (tx *Transaction) validateOp(op txOp) error {
-	if op.name == "" {
-		return fmt.Errorf("refstore/files: empty reference name")
-	}
-
-	switch op.kind {
-	case txCreate, txUpdate:
-		err := refname.ValidateUpdateName(op.name, true)
-		if err != nil {
-			return err
-		}
-
-		if op.newID.Size() == 0 {
-			return objectid.ErrInvalidAlgorithm
-		}
-	case txDelete, txVerify:
-		err := refname.ValidateUpdateName(op.name, false)
-		if err != nil {
-			return err
-		}
-
-		if op.oldID.Size() == 0 {
-			return objectid.ErrInvalidAlgorithm
-		}
-	case txCreateSymbolic, txUpdateSymbolic:
-		err := refname.ValidateUpdateName(op.name, true)
-		if err != nil {
-			return err
-		}
-
-		if strings.TrimSpace(op.newTarget) == "" {
-			return fmt.Errorf("refstore/files: empty symbolic target")
-		}
-
-		err = refname.ValidateSymbolicTarget(op.name, strings.TrimSpace(op.newTarget))
-		if err != nil {
-			return err
-		}
-	case txDeleteSymbolic, txVerifySymbolic:
-		err := refname.ValidateUpdateName(op.name, false)
-		if err != nil {
-			return err
-		}
-	default:
-		return fmt.Errorf("refstore/files: unsupported transaction operation %d", op.kind)
-	}
-
-	if op.kind == txUpdateSymbolic || op.kind == txDeleteSymbolic || op.kind == txVerifySymbolic {
-		if strings.TrimSpace(op.oldTarget) == "" {
-			return fmt.Errorf("refstore/files: empty symbolic old target")
-		}
-	}
-
-	return nil
-}
--- /dev/null
+++ b/refstore/files/transaction_abort.go
@@ -1,0 +1,22 @@
+package files
+
+import "fmt"
+
+func (tx *Transaction) Abort() error {
+	err := tx.ensureOpen()
+	if err != nil {
+		return err
+	}
+
+	tx.closed = true
+
+	return nil
+}
+
+func (tx *Transaction) ensureOpen() error {
+	if tx.closed {
+		return fmt.Errorf("refstore/files: transaction already closed")
+	}
+
+	return nil
+}
--- /dev/null
+++ b/refstore/files/transaction_begin.go
@@ -1,0 +1,13 @@
+package files
+
+import "codeberg.org/lindenii/furgit/refstore"
+
+// BeginTransaction creates one new files transaction.
+//
+//nolint:ireturn
+func (store *Store) BeginTransaction() (refstore.Transaction, error) {
+	return &Transaction{
+		store: store,
+		ops:   make([]txOp, 0, 8),
+	}, nil
+}
--- a/refstore/files/transaction_cleanup.go
+++ b/refstore/files/transaction_cleanup.go
@@ -2,9 +2,7 @@
 
 import (
 	"errors"
-	"fmt"
 	"os"
-	"path"
 	"slices"
 )
 
@@ -38,83 +36,4 @@
 	}
 
 	return firstErr
-}
-
-func (tx *Transaction) tryRemoveEmptyParents(name string) {
-	loc := tx.store.loosePath(name)
-	tx.tryRemoveEmptyParentPaths(loc.root, loc.path)
-}
-
-func (tx *Transaction) tryRemoveEmptyParentPaths(kind rootKind, name string) {
-	root := tx.store.rootFor(kind)
-	dir := path.Dir(name)
-
-	for dir != "." && dir != "/" {
-		err := root.Remove(dir)
-		if err != nil {
-			if errors.Is(err, os.ErrNotExist) {
-				return
-			}
-
-			var pathErr *os.PathError
-			if errors.As(err, &pathErr) {
-				return
-			}
-
-			return
-		}
-
-		dir = path.Dir(dir)
-	}
-}
-
-func (tx *Transaction) removeEmptyDirTree(name refPath) error {
-	root := tx.store.rootFor(name.root)
-
-	info, err := root.Stat(name.path)
-	if err != nil {
-		if errors.Is(err, os.ErrNotExist) {
-			return nil
-		}
-
-		return err
-	}
-
-	if !info.IsDir() {
-		return nil
-	}
-
-	return tx.removeEmptyDirTreeRecursive(name)
-}
-
-func (tx *Transaction) removeEmptyDirTreeRecursive(name refPath) error {
-	root := tx.store.rootFor(name.root)
-
-	dir, err := root.Open(name.path)
-	if err != nil {
-		return err
-	}
-
-	entries, err := dir.ReadDir(-1)
-	_ = dir.Close()
-
-	if err != nil {
-		return err
-	}
-
-	for _, entry := range entries {
-		if !entry.IsDir() {
-			return fmt.Errorf("refstore/files: non-empty directory blocks reference %q", name.path)
-		}
-
-		err = tx.removeEmptyDirTreeRecursive(refPath{
-			root: name.root,
-			path: path.Join(name.path, entry.Name()),
-		})
-		if err != nil {
-			return err
-		}
-	}
-
-	return root.Remove(name.path)
 }
--- /dev/null
+++ b/refstore/files/transaction_cleanup_parents.go
@@ -1,0 +1,35 @@
+package files
+
+import (
+	"errors"
+	"os"
+	"path"
+)
+
+func (tx *Transaction) tryRemoveEmptyParents(name string) {
+	loc := tx.store.loosePath(name)
+	tx.tryRemoveEmptyParentPaths(loc.root, loc.path)
+}
+
+func (tx *Transaction) tryRemoveEmptyParentPaths(kind rootKind, name string) {
+	root := tx.store.rootFor(kind)
+	dir := path.Dir(name)
+
+	for dir != "." && dir != "/" {
+		err := root.Remove(dir)
+		if err != nil {
+			if errors.Is(err, os.ErrNotExist) {
+				return
+			}
+
+			var pathErr *os.PathError
+			if errors.As(err, &pathErr) {
+				return
+			}
+
+			return
+		}
+
+		dir = path.Dir(dir)
+	}
+}
--- /dev/null
+++ b/refstore/files/transaction_commit.go
@@ -1,0 +1,65 @@
+package files
+
+import (
+	"errors"
+	"os"
+)
+
+func (tx *Transaction) Commit() error {
+	err := tx.ensureOpen()
+	if err != nil {
+		return err
+	}
+
+	prepared, err := tx.prepare()
+	if err != nil {
+		tx.closed = true
+
+		return err
+	}
+
+	defer func() {
+		_ = tx.cleanup(prepared)
+	}()
+
+	for _, item := range prepared {
+		if item.op.kind == txDelete || item.op.kind == txDeleteSymbolic || item.op.kind == txVerify || item.op.kind == txVerifySymbolic {
+			continue
+		}
+
+		err = tx.writeLoose(item)
+		if err != nil {
+			tx.closed = true
+
+			return err
+		}
+	}
+
+	err = tx.applyPackedDeletes(prepared)
+	if err != nil {
+		tx.closed = true
+
+		return err
+	}
+
+	for _, item := range prepared {
+		switch item.op.kind {
+		case txDelete, txDeleteSymbolic:
+			if item.target.ref.isLoose {
+				err = tx.store.rootFor(item.target.loc.root).Remove(item.target.loc.path)
+				if err != nil && !errors.Is(err, os.ErrNotExist) {
+					tx.closed = true
+
+					return err
+				}
+
+				tx.tryRemoveEmptyParents(item.target.name)
+			}
+		case txCreate, txUpdate, txVerify, txCreateSymbolic, txUpdateSymbolic, txVerifySymbolic:
+		}
+	}
+
+	tx.closed = true
+
+	return nil
+}
--- /dev/null
+++ b/refstore/files/transaction_dir_tree.go
@@ -1,0 +1,59 @@
+package files
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"path"
+)
+
+func (tx *Transaction) removeEmptyDirTree(name refPath) error {
+	root := tx.store.rootFor(name.root)
+
+	info, err := root.Stat(name.path)
+	if err != nil {
+		if errors.Is(err, os.ErrNotExist) {
+			return nil
+		}
+
+		return err
+	}
+
+	if !info.IsDir() {
+		return nil
+	}
+
+	return tx.removeEmptyDirTreeRecursive(name)
+}
+
+func (tx *Transaction) removeEmptyDirTreeRecursive(name refPath) error {
+	root := tx.store.rootFor(name.root)
+
+	dir, err := root.Open(name.path)
+	if err != nil {
+		return err
+	}
+
+	entries, err := dir.ReadDir(-1)
+	_ = dir.Close()
+
+	if err != nil {
+		return err
+	}
+
+	for _, entry := range entries {
+		if !entry.IsDir() {
+			return fmt.Errorf("refstore/files: non-empty directory blocks reference %q", name.path)
+		}
+
+		err = tx.removeEmptyDirTreeRecursive(refPath{
+			root: name.root,
+			path: path.Join(name.path, entry.Name()),
+		})
+		if err != nil {
+			return err
+		}
+	}
+
+	return root.Remove(name.path)
+}
--- /dev/null
+++ b/refstore/files/transaction_direct_read.go
@@ -1,0 +1,76 @@
+package files
+
+import (
+	"errors"
+	"fmt"
+
+	"codeberg.org/lindenii/furgit/ref"
+	"codeberg.org/lindenii/furgit/ref/refname"
+	"codeberg.org/lindenii/furgit/refstore"
+)
+
+func (tx *Transaction) directRead(name string) (directRef, error) {
+	loc := tx.store.loosePath(name)
+	hasPacked := false
+
+	if loc.root == rootCommon && refname.ParseWorktree(name).Type == refname.WorktreeShared {
+		packed, packedErr := tx.store.readPackedRefs()
+		if packedErr != nil {
+			return directRef{}, packedErr
+		}
+
+		_, hasPacked = packed.byName[name]
+	}
+
+	loose, err := tx.store.readLooseRef(name)
+	if err == nil {
+		switch loose := loose.(type) {
+		case ref.Detached:
+			return directRef{
+				kind:     directDetached,
+				name:     name,
+				id:       loose.ID,
+				isLoose:  true,
+				isPacked: hasPacked,
+			}, nil
+		case ref.Symbolic:
+			return directRef{
+				kind:     directSymbolic,
+				name:     name,
+				target:   loose.Target,
+				isLoose:  true,
+				isPacked: hasPacked,
+			}, nil
+		default:
+			return directRef{}, fmt.Errorf("refstore/files: unsupported reference type %T", loose)
+		}
+	}
+
+	if !errors.Is(err, refstore.ErrReferenceNotFound) {
+		info, statErr := tx.store.rootFor(loc.root).Stat(loc.path)
+		if statErr != nil || !info.IsDir() {
+			return directRef{}, err
+		}
+	}
+
+	if hasPacked {
+		packed, packedErr := tx.store.readPackedRefs()
+		if packedErr != nil {
+			return directRef{}, packedErr
+		}
+
+		detached := packed.byName[name]
+
+		return directRef{
+			kind:     directDetached,
+			name:     name,
+			id:       detached.ID,
+			isPacked: true,
+		}, nil
+	}
+
+	return directRef{
+		kind: directMissing,
+		name: name,
+	}, nil
+}
--- /dev/null
+++ b/refstore/files/transaction_direct_ref.go
@@ -1,0 +1,20 @@
+package files
+
+import "codeberg.org/lindenii/furgit/objectid"
+
+type directKind uint8
+
+const (
+	directMissing directKind = iota
+	directDetached
+	directSymbolic
+)
+
+type directRef struct {
+	kind     directKind
+	name     string
+	id       objectid.ObjectID
+	target   string
+	isLoose  bool
+	isPacked bool
+}
--- /dev/null
+++ b/refstore/files/transaction_kind.go
@@ -1,0 +1,14 @@
+package files
+
+type txKind uint8
+
+const (
+	txCreate txKind = iota
+	txUpdate
+	txDelete
+	txVerify
+	txCreateSymbolic
+	txUpdateSymbolic
+	txDeleteSymbolic
+	txVerifySymbolic
+)
--- a/refstore/files/transaction_lock.go
+++ b/refstore/files/transaction_lock.go
@@ -1,12 +1,8 @@
 package files
 
 import (
-	"errors"
-	"fmt"
 	"os"
 	"path"
-	"strings"
-	"time"
 )
 
 func (tx *Transaction) createLock(name refPath) error {
@@ -26,59 +22,4 @@
 	}
 
 	return file.Close()
-}
-
-func (tx *Transaction) createPackedLock() error {
-	const (
-		initialBackoffMs     = 1
-		backoffMaxMultiplier = 1000
-	)
-
-	timeout := tx.store.packedRefsTimeout
-	deadline := time.Now().Add(timeout)
-	multiplier := 1
-	n := 1
-
-	for {
-		file, err := tx.store.commonRoot.OpenFile("packed-refs.lock", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644)
-		if err == nil {
-			return file.Close()
-		}
-
-		if !errors.Is(err, os.ErrExist) {
-			return err
-		}
-
-		if timeout == 0 || (timeout > 0 && time.Now().After(deadline)) {
-			return err
-		}
-
-		backoffMs := multiplier * initialBackoffMs
-		waitMs := (750 + tx.store.lockRand.Intn(500)) * backoffMs / 1000
-		time.Sleep(time.Duration(waitMs) * time.Millisecond)
-
-		multiplier += 2*n + 1
-		if multiplier > backoffMaxMultiplier {
-			multiplier = backoffMaxMultiplier
-		} else {
-			n++
-		}
-	}
-}
-
-func (tx *Transaction) targetKey(name refPath) string {
-	return fmt.Sprintf("%d:%s", name.root, name.path)
-}
-
-func refPathFromKey(key string) refPath {
-	rootValue, pathValue, ok := strings.Cut(key, ":")
-	if !ok || rootValue == "" {
-		return refPath{root: rootCommon, path: key}
-	}
-
-	if rootValue == "0" {
-		return refPath{root: rootGit, path: pathValue}
-	}
-
-	return refPath{root: rootCommon, path: pathValue}
 }
--- /dev/null
+++ b/refstore/files/transaction_lock_packed.go
@@ -1,0 +1,44 @@
+package files
+
+import (
+	"errors"
+	"os"
+	"time"
+)
+
+func (tx *Transaction) createPackedLock(timeout time.Duration) error {
+	const (
+		initialBackoffMs     = 1
+		backoffMaxMultiplier = 1000
+	)
+
+	deadline := time.Now().Add(timeout)
+	multiplier := 1
+	n := 1
+
+	for {
+		file, err := tx.store.commonRoot.OpenFile("packed-refs.lock", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644)
+		if err == nil {
+			return file.Close()
+		}
+
+		if !errors.Is(err, os.ErrExist) {
+			return err
+		}
+
+		if timeout == 0 || (timeout > 0 && time.Now().After(deadline)) {
+			return err
+		}
+
+		backoffMs := multiplier * initialBackoffMs
+		waitMs := (750 + tx.store.lockRand.Intn(500)) * backoffMs / 1000
+		time.Sleep(time.Duration(waitMs) * time.Millisecond)
+
+		multiplier += 2*n + 1
+		if multiplier > backoffMaxMultiplier {
+			multiplier = backoffMaxMultiplier
+		} else {
+			n++
+		}
+	}
+}
--- /dev/null
+++ b/refstore/files/transaction_operation.go
@@ -1,0 +1,23 @@
+package files
+
+import "codeberg.org/lindenii/furgit/objectid"
+
+type txOp struct {
+	name      string
+	kind      txKind
+	newID     objectid.ObjectID
+	oldID     objectid.ObjectID
+	newTarget string
+	oldTarget string
+}
+
+type preparedTxOp struct {
+	op     txOp
+	target resolvedWriteTarget
+}
+
+type resolvedWriteTarget struct {
+	name string
+	loc  refPath
+	ref  directRef
+}
--- a/refstore/files/transaction_prepare.go
+++ b/refstore/files/transaction_prepare.go
@@ -1,14 +1,8 @@
 package files
 
 import (
-	"errors"
 	"fmt"
 	"slices"
-	"strings"
-
-	"codeberg.org/lindenii/furgit/ref"
-	"codeberg.org/lindenii/furgit/ref/refname"
-	"codeberg.org/lindenii/furgit/refstore"
 )
 
 func (tx *Transaction) prepare() (prepared []preparedTxOp, err error) {
@@ -82,7 +76,7 @@
 
 	hasDeletes := len(deleted) > 0
 	if hasDeletes {
-		err = tx.createPackedLock()
+		err = tx.createPackedLock(tx.store.packedRefsTimeout)
 		if err != nil {
 			return prepared, err
 		}
@@ -105,188 +99,4 @@
 	}
 
 	return prepared, nil
-}
-
-func (tx *Transaction) resolveTarget(op txOp) (resolvedWriteTarget, error) {
-	switch op.kind {
-	case txCreate:
-		return tx.resolveOrdinaryTarget(op.name, true)
-	case txUpdate, txDelete, txVerify:
-		return tx.resolveOrdinaryTarget(op.name, false)
-	case txCreateSymbolic, txUpdateSymbolic, txDeleteSymbolic, txVerifySymbolic:
-		refState, err := tx.directRead(op.name)
-		if err != nil {
-			return resolvedWriteTarget{}, err
-		}
-
-		return resolvedWriteTarget{name: op.name, loc: tx.store.loosePath(op.name), ref: refState}, nil
-	default:
-		return resolvedWriteTarget{}, fmt.Errorf("refstore/files: unsupported transaction operation %d", op.kind)
-	}
-}
-
-func (tx *Transaction) resolveOrdinaryTarget(name string, allowMissing bool) (resolvedWriteTarget, error) {
-	cur := name
-	seen := make(map[string]struct{})
-
-	for {
-		if _, ok := seen[cur]; ok {
-			return resolvedWriteTarget{}, fmt.Errorf("refstore/files: symbolic reference cycle at %q", cur)
-		}
-
-		seen[cur] = struct{}{}
-
-		refState, err := tx.directRead(cur)
-		if err != nil {
-			return resolvedWriteTarget{}, err
-		}
-
-		switch refState.kind {
-		case directMissing:
-			if !allowMissing {
-				return resolvedWriteTarget{}, refstore.ErrReferenceNotFound
-			}
-
-			return resolvedWriteTarget{name: cur, loc: tx.store.loosePath(cur), ref: refState}, nil
-		case directDetached:
-			return resolvedWriteTarget{name: cur, loc: tx.store.loosePath(cur), ref: refState}, nil
-		case directSymbolic:
-			target := strings.TrimSpace(refState.target)
-			if target == "" {
-				return resolvedWriteTarget{}, fmt.Errorf("refstore/files: symbolic reference %q has empty target", cur)
-			}
-
-			cur = target
-		default:
-			return resolvedWriteTarget{}, fmt.Errorf("refstore/files: unsupported direct reference state %d", refState.kind)
-		}
-	}
-}
-
-func (tx *Transaction) directRead(name string) (directRef, error) {
-	loc := tx.store.loosePath(name)
-	hasPacked := false
-
-	if loc.root == rootCommon && refname.ParseWorktree(name).Type == refname.WorktreeShared {
-		packed, packedErr := tx.store.readPackedRefs()
-		if packedErr != nil {
-			return directRef{}, packedErr
-		}
-
-		_, hasPacked = packed.byName[name]
-	}
-
-	loose, err := tx.store.readLooseRef(name)
-	if err == nil {
-		switch loose := loose.(type) {
-		case ref.Detached:
-			return directRef{
-				kind:     directDetached,
-				name:     name,
-				id:       loose.ID,
-				isLoose:  true,
-				isPacked: hasPacked,
-			}, nil
-		case ref.Symbolic:
-			return directRef{
-				kind:     directSymbolic,
-				name:     name,
-				target:   loose.Target,
-				isLoose:  true,
-				isPacked: hasPacked,
-			}, nil
-		default:
-			return directRef{}, fmt.Errorf("refstore/files: unsupported reference type %T", loose)
-		}
-	}
-
-	if !errors.Is(err, refstore.ErrReferenceNotFound) {
-		info, statErr := tx.store.rootFor(loc.root).Stat(loc.path)
-		if statErr != nil || !info.IsDir() {
-			return directRef{}, err
-		}
-	}
-
-	if hasPacked {
-		packed, packedErr := tx.store.readPackedRefs()
-		if packedErr != nil {
-			return directRef{}, packedErr
-		}
-
-		detached := packed.byName[name]
-
-		return directRef{
-			kind:     directDetached,
-			name:     name,
-			id:       detached.ID,
-			isPacked: true,
-		}, nil
-	}
-
-	return directRef{
-		kind: directMissing,
-		name: name,
-	}, nil
-}
-
-func (tx *Transaction) visibleNames() (map[string]struct{}, error) {
-	names := make(map[string]struct{})
-
-	looseNames, err := tx.store.collectLooseRefNames()
-	if err != nil {
-		return nil, err
-	}
-
-	for _, name := range looseNames {
-		names[name] = struct{}{}
-	}
-
-	packed, err := tx.store.readPackedRefs()
-	if err != nil {
-		return nil, err
-	}
-
-	for name := range packed.byName {
-		if _, exists := names[name]; exists {
-			continue
-		}
-
-		names[name] = struct{}{}
-	}
-
-	return names, nil
-}
-
-func verifyRefnameAvailable(name string, existing map[string]struct{}, writes []string, deleted map[string]struct{}) error {
-	for existingName := range existing {
-		if existingName == name {
-			continue
-		}
-
-		if _, skip := deleted[existingName]; skip {
-			continue
-		}
-
-		if refnamesConflict(name, existingName) {
-			return fmt.Errorf("refstore/files: reference name conflict between %q and %q", name, existingName)
-		}
-	}
-
-	for _, other := range writes {
-		if other == name {
-			continue
-		}
-
-		if refnamesConflict(name, other) {
-			return fmt.Errorf("refstore/files: reference name conflict between %q and %q", name, other)
-		}
-	}
-
-	return nil
-}
-
-func refnamesConflict(left, right string) bool {
-	return left == right ||
-		strings.HasPrefix(left, right+"/") ||
-		strings.HasPrefix(right, left+"/")
 }
--- /dev/null
+++ b/refstore/files/transaction_queue.go
@@ -1,0 +1,17 @@
+package files
+
+func (tx *Transaction) queue(op txOp) error {
+	err := tx.ensureOpen()
+	if err != nil {
+		return err
+	}
+
+	err = tx.validateOp(op)
+	if err != nil {
+		return err
+	}
+
+	tx.ops = append(tx.ops, op)
+
+	return nil
+}
--- /dev/null
+++ b/refstore/files/transaction_queue_ops.go
@@ -1,0 +1,35 @@
+package files
+
+import "codeberg.org/lindenii/furgit/objectid"
+
+func (tx *Transaction) Create(name string, newID objectid.ObjectID) error {
+	return tx.queue(txOp{name: name, kind: txCreate, newID: newID})
+}
+
+func (tx *Transaction) Update(name string, newID, oldID objectid.ObjectID) error {
+	return tx.queue(txOp{name: name, kind: txUpdate, newID: newID, oldID: oldID})
+}
+
+func (tx *Transaction) Delete(name string, oldID objectid.ObjectID) error {
+	return tx.queue(txOp{name: name, kind: txDelete, oldID: oldID})
+}
+
+func (tx *Transaction) Verify(name string, oldID objectid.ObjectID) error {
+	return tx.queue(txOp{name: name, kind: txVerify, oldID: oldID})
+}
+
+func (tx *Transaction) CreateSymbolic(name, newTarget string) error {
+	return tx.queue(txOp{name: name, kind: txCreateSymbolic, newTarget: newTarget})
+}
+
+func (tx *Transaction) UpdateSymbolic(name, newTarget, oldTarget string) error {
+	return tx.queue(txOp{name: name, kind: txUpdateSymbolic, newTarget: newTarget, oldTarget: oldTarget})
+}
+
+func (tx *Transaction) DeleteSymbolic(name, oldTarget string) error {
+	return tx.queue(txOp{name: name, kind: txDeleteSymbolic, oldTarget: oldTarget})
+}
+
+func (tx *Transaction) VerifySymbolic(name, oldTarget string) error {
+	return tx.queue(txOp{name: name, kind: txVerifySymbolic, oldTarget: oldTarget})
+}
--- /dev/null
+++ b/refstore/files/transaction_resolve_target.go
@@ -1,0 +1,21 @@
+package files
+
+import "fmt"
+
+func (tx *Transaction) resolveTarget(op txOp) (resolvedWriteTarget, error) {
+	switch op.kind {
+	case txCreate:
+		return tx.resolveOrdinaryTarget(op.name, true)
+	case txUpdate, txDelete, txVerify:
+		return tx.resolveOrdinaryTarget(op.name, false)
+	case txCreateSymbolic, txUpdateSymbolic, txDeleteSymbolic, txVerifySymbolic:
+		refState, err := tx.directRead(op.name)
+		if err != nil {
+			return resolvedWriteTarget{}, err
+		}
+
+		return resolvedWriteTarget{name: op.name, loc: tx.store.loosePath(op.name), ref: refState}, nil
+	default:
+		return resolvedWriteTarget{}, fmt.Errorf("refstore/files: unsupported transaction operation %d", op.kind)
+	}
+}
--- /dev/null
+++ b/refstore/files/transaction_resolve_target_ordinary.go
@@ -1,0 +1,46 @@
+package files
+
+import (
+	"fmt"
+	"strings"
+
+	"codeberg.org/lindenii/furgit/refstore"
+)
+
+func (tx *Transaction) resolveOrdinaryTarget(name string, allowMissing bool) (resolvedWriteTarget, error) {
+	cur := name
+	seen := make(map[string]struct{})
+
+	for {
+		if _, ok := seen[cur]; ok {
+			return resolvedWriteTarget{}, fmt.Errorf("refstore/files: symbolic reference cycle at %q", cur)
+		}
+
+		seen[cur] = struct{}{}
+
+		refState, err := tx.directRead(cur)
+		if err != nil {
+			return resolvedWriteTarget{}, err
+		}
+
+		switch refState.kind {
+		case directMissing:
+			if !allowMissing {
+				return resolvedWriteTarget{}, refstore.ErrReferenceNotFound
+			}
+
+			return resolvedWriteTarget{name: cur, loc: tx.store.loosePath(cur), ref: refState}, nil
+		case directDetached:
+			return resolvedWriteTarget{name: cur, loc: tx.store.loosePath(cur), ref: refState}, nil
+		case directSymbolic:
+			target := strings.TrimSpace(refState.target)
+			if target == "" {
+				return resolvedWriteTarget{}, fmt.Errorf("refstore/files: symbolic reference %q has empty target", cur)
+			}
+
+			cur = target
+		default:
+			return resolvedWriteTarget{}, fmt.Errorf("refstore/files: unsupported direct reference state %d", refState.kind)
+		}
+	}
+}
--- /dev/null
+++ b/refstore/files/transaction_validate.go
@@ -1,0 +1,65 @@
+package files
+
+import (
+	"fmt"
+	"strings"
+
+	"codeberg.org/lindenii/furgit/objectid"
+	"codeberg.org/lindenii/furgit/ref/refname"
+)
+
+func (tx *Transaction) validateOp(op txOp) error {
+	if op.name == "" {
+		return fmt.Errorf("refstore/files: empty reference name")
+	}
+
+	switch op.kind {
+	case txCreate, txUpdate:
+		err := refname.ValidateUpdateName(op.name, true)
+		if err != nil {
+			return err
+		}
+
+		if op.newID.Size() == 0 {
+			return objectid.ErrInvalidAlgorithm
+		}
+	case txDelete, txVerify:
+		err := refname.ValidateUpdateName(op.name, false)
+		if err != nil {
+			return err
+		}
+
+		if op.oldID.Size() == 0 {
+			return objectid.ErrInvalidAlgorithm
+		}
+	case txCreateSymbolic, txUpdateSymbolic:
+		err := refname.ValidateUpdateName(op.name, true)
+		if err != nil {
+			return err
+		}
+
+		if strings.TrimSpace(op.newTarget) == "" {
+			return fmt.Errorf("refstore/files: empty symbolic target")
+		}
+
+		err = refname.ValidateSymbolicTarget(op.name, strings.TrimSpace(op.newTarget))
+		if err != nil {
+			return err
+		}
+	case txDeleteSymbolic, txVerifySymbolic:
+		err := refname.ValidateUpdateName(op.name, false)
+		if err != nil {
+			return err
+		}
+	default:
+		return fmt.Errorf("refstore/files: unsupported transaction operation %d", op.kind)
+	}
+
+	if op.kind == txUpdateSymbolic || op.kind == txDeleteSymbolic || op.kind == txVerifySymbolic {
+		if strings.TrimSpace(op.oldTarget) == "" {
+			return fmt.Errorf("refstore/files: empty symbolic old target")
+		}
+	}
+
+	return nil
+}
--- /dev/null
+++ b/refstore/files/transaction_verify_current.go
@@ -1,0 +1,53 @@
+package files
+
+import (
+	"fmt"
+	"strings"
+)
+
+func (tx *Transaction) verifyCurrent(item preparedTxOp) error {
+	switch item.op.kind {
+	case txCreate:
+		if item.target.ref.kind != directMissing {
+			return fmt.Errorf("refstore/files: reference %q already exists", item.target.name)
+		}
+
+		return nil
+	case txUpdate, txDelete, txVerify:
+		if item.target.ref.kind == directMissing {
+			return fmt.Errorf("refstore/files: reference %q is missing", item.target.name)
+		}
+
+		if item.target.ref.kind != directDetached {
+			return fmt.Errorf("refstore/files: reference %q is not detached", item.target.name)
+		}
+
+		if item.target.ref.id != item.op.oldID {
+			return fmt.Errorf("refstore/files: reference %q is at %s but expected %s", item.target.name, item.target.ref.id, item.op.oldID)
+		}
+
+		return nil
+	case txCreateSymbolic:
+		if item.target.ref.kind != directMissing {
+			return fmt.Errorf("refstore/files: reference %q already exists", item.target.name)
+		}
+
+		return nil
+	case txUpdateSymbolic, txDeleteSymbolic, txVerifySymbolic:
+		if item.target.ref.kind == directMissing {
+			return fmt.Errorf("refstore/files: symbolic reference %q is missing", item.target.name)
+		}
+
+		if item.target.ref.kind != directSymbolic {
+			return fmt.Errorf("refstore/files: reference %q is not symbolic", item.target.name)
+		}
+
+		if strings.TrimSpace(item.target.ref.target) != strings.TrimSpace(item.op.oldTarget) {
+			return fmt.Errorf("refstore/files: reference %q points at %q, expected %q", item.target.name, item.target.ref.target, item.op.oldTarget)
+		}
+
+		return nil
+	default:
+		return fmt.Errorf("refstore/files: unsupported transaction operation %d", item.op.kind)
+	}
+}
--- /dev/null
+++ b/refstore/files/transaction_verify_refnames.go
@@ -1,0 +1,40 @@
+package files
+
+import (
+	"fmt"
+	"strings"
+)
+
+func verifyRefnameAvailable(name string, existing map[string]struct{}, writes []string, deleted map[string]struct{}) error {
+	for existingName := range existing {
+		if existingName == name {
+			continue
+		}
+
+		if _, skip := deleted[existingName]; skip {
+			continue
+		}
+
+		if refnamesConflict(name, existingName) {
+			return fmt.Errorf("refstore/files: reference name conflict between %q and %q", name, existingName)
+		}
+	}
+
+	for _, other := range writes {
+		if other == name {
+			continue
+		}
+
+		if refnamesConflict(name, other) {
+			return fmt.Errorf("refstore/files: reference name conflict between %q and %q", name, other)
+		}
+	}
+
+	return nil
+}
+
+func refnamesConflict(left, right string) bool {
+	return left == right ||
+		strings.HasPrefix(left, right+"/") ||
+		strings.HasPrefix(right, left+"/")
+}
--- /dev/null
+++ b/refstore/files/transaction_visible_names.go
@@ -1,0 +1,29 @@
+package files
+
+func (tx *Transaction) visibleNames() (map[string]struct{}, error) {
+	names := make(map[string]struct{})
+
+	looseNames, err := tx.store.collectLooseRefNames()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, name := range looseNames {
+		names[name] = struct{}{}
+	}
+
+	packed, err := tx.store.readPackedRefs()
+	if err != nil {
+		return nil, err
+	}
+
+	for name := range packed.byName {
+		if _, exists := names[name]; exists {
+			continue
+		}
+
+		names[name] = struct{}{}
+	}
+
+	return names, nil
+}
--- a/refstore/files/transaction_write.go
+++ /dev/null
@@ -1,199 +1,0 @@
-package files
-
-import (
-	"errors"
-	"fmt"
-	"os"
-	"path"
-	"strings"
-)
-
-func (tx *Transaction) verifyCurrent(item preparedTxOp) error {
-	switch item.op.kind {
-	case txCreate:
-		if item.target.ref.kind != directMissing {
-			return fmt.Errorf("refstore/files: reference %q already exists", item.target.name)
-		}
-
-		return nil
-	case txUpdate, txDelete, txVerify:
-		if item.target.ref.kind == directMissing {
-			return fmt.Errorf("refstore/files: reference %q is missing", item.target.name)
-		}
-
-		if item.target.ref.kind != directDetached {
-			return fmt.Errorf("refstore/files: reference %q is not detached", item.target.name)
-		}
-
-		if item.target.ref.id != item.op.oldID {
-			return fmt.Errorf("refstore/files: reference %q is at %s but expected %s", item.target.name, item.target.ref.id, item.op.oldID)
-		}
-
-		return nil
-	case txCreateSymbolic:
-		if item.target.ref.kind != directMissing {
-			return fmt.Errorf("refstore/files: reference %q already exists", item.target.name)
-		}
-
-		return nil
-	case txUpdateSymbolic, txDeleteSymbolic, txVerifySymbolic:
-		if item.target.ref.kind == directMissing {
-			return fmt.Errorf("refstore/files: symbolic reference %q is missing", item.target.name)
-		}
-
-		if item.target.ref.kind != directSymbolic {
-			return fmt.Errorf("refstore/files: reference %q is not symbolic", item.target.name)
-		}
-
-		if strings.TrimSpace(item.target.ref.target) != strings.TrimSpace(item.op.oldTarget) {
-			return fmt.Errorf("refstore/files: reference %q points at %q, expected %q", item.target.name, item.target.ref.target, item.op.oldTarget)
-		}
-
-		return nil
-	default:
-		return fmt.Errorf("refstore/files: unsupported transaction operation %d", item.op.kind)
-	}
-}
-
-func (tx *Transaction) writeLoose(item preparedTxOp) error {
-	root := tx.store.rootFor(item.target.loc.root)
-	lockName := item.target.loc.path + ".lock"
-
-	lock, err := root.OpenFile(lockName, os.O_WRONLY|os.O_TRUNC, 0o644)
-	if err != nil {
-		return err
-	}
-
-	var content string
-
-	switch item.op.kind {
-	case txCreate, txUpdate:
-		content = item.op.newID.String() + "\n"
-	case txCreateSymbolic, txUpdateSymbolic:
-		content = "ref: " + strings.TrimSpace(item.op.newTarget) + "\n"
-	case txDelete, txVerify, txDeleteSymbolic, txVerifySymbolic:
-	default:
-		_ = lock.Close()
-
-		return fmt.Errorf("refstore/files: unsupported write operation %d", item.op.kind)
-	}
-
-	_, err = lock.WriteString(content)
-	if err != nil {
-		_ = lock.Close()
-
-		return err
-	}
-
-	err = lock.Close()
-	if err != nil {
-		return err
-	}
-
-	dir := path.Dir(item.target.loc.path)
-	if dir != "." {
-		err = root.MkdirAll(dir, 0o755)
-		if err != nil {
-			return err
-		}
-	}
-
-	err = tx.removeEmptyDirTree(item.target.loc)
-	if err != nil {
-		return err
-	}
-
-	return root.Rename(lockName, item.target.loc.path)
-}
-
-func (tx *Transaction) applyPackedDeletes(prepared []preparedTxOp) error {
-	_, err := tx.store.commonRoot.Stat("packed-refs.lock")
-	if err != nil {
-		if errors.Is(err, os.ErrNotExist) {
-			return nil
-		}
-
-		return err
-	}
-
-	packed, err := tx.store.readPackedRefs()
-	if err != nil {
-		return err
-	}
-
-	deleted := make(map[string]struct{})
-	needed := false
-
-	for _, item := range prepared {
-		if item.op.kind != txDelete && item.op.kind != txDeleteSymbolic {
-			continue
-		}
-
-		deleted[item.target.name] = struct{}{}
-		if item.target.ref.isPacked {
-			needed = true
-		}
-	}
-
-	if !needed {
-		return nil
-	}
-
-	lock, err := tx.store.commonRoot.OpenFile("packed-refs.new", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644)
-	if err != nil {
-		return err
-	}
-
-	createdTemp := true
-
-	defer func() {
-		if !createdTemp {
-			return
-		}
-
-		_ = tx.store.commonRoot.Remove("packed-refs.new")
-	}()
-
-	_, err = lock.WriteString("# pack-refs with: peeled fully-peeled sorted\n")
-	if err != nil {
-		_ = lock.Close()
-
-		return err
-	}
-
-	for _, entry := range packed.ordered {
-		if _, skip := deleted[entry.Name()]; skip {
-			continue
-		}
-
-		_, err = lock.WriteString(entry.ID.String() + " " + entry.Name() + "\n")
-		if err != nil {
-			_ = lock.Close()
-
-			return err
-		}
-
-		if entry.Peeled != nil {
-			_, err = lock.WriteString("^" + entry.Peeled.String() + "\n")
-			if err != nil {
-				_ = lock.Close()
-
-				return err
-			}
-		}
-	}
-
-	err = lock.Close()
-	if err != nil {
-		return err
-	}
-
-	err = tx.store.commonRoot.Rename("packed-refs.new", "packed-refs")
-	if err != nil {
-		return err
-	}
-
-	createdTemp = false
-
-	return nil
-}
--- /dev/null
+++ b/refstore/files/transaction_write_loose.go
@@ -1,0 +1,59 @@
+package files
+
+import (
+	"fmt"
+	"os"
+	"path"
+	"strings"
+)
+
+func (tx *Transaction) writeLoose(item preparedTxOp) error {
+	root := tx.store.rootFor(item.target.loc.root)
+	lockName := item.target.loc.path + ".lock"
+
+	lock, err := root.OpenFile(lockName, os.O_WRONLY|os.O_TRUNC, 0o644)
+	if err != nil {
+		return err
+	}
+
+	var content string
+
+	switch item.op.kind {
+	case txCreate, txUpdate:
+		content = item.op.newID.String() + "\n"
+	case txCreateSymbolic, txUpdateSymbolic:
+		content = "ref: " + strings.TrimSpace(item.op.newTarget) + "\n"
+	case txDelete, txVerify, txDeleteSymbolic, txVerifySymbolic:
+	default:
+		_ = lock.Close()
+
+		return fmt.Errorf("refstore/files: unsupported write operation %d", item.op.kind)
+	}
+
+	_, err = lock.WriteString(content)
+	if err != nil {
+		_ = lock.Close()
+
+		return err
+	}
+
+	err = lock.Close()
+	if err != nil {
+		return err
+	}
+
+	dir := path.Dir(item.target.loc.path)
+	if dir != "." {
+		err = root.MkdirAll(dir, 0o755)
+		if err != nil {
+			return err
+		}
+	}
+
+	err = tx.removeEmptyDirTree(item.target.loc)
+	if err != nil {
+		return err
+	}
+
+	return root.Rename(lockName, item.target.loc.path)
+}
--- /dev/null
+++ b/refstore/files/transaction_write_packed_deltas.go
@@ -1,0 +1,98 @@
+package files
+
+import (
+	"errors"
+	"os"
+)
+
+func (tx *Transaction) applyPackedDeletes(prepared []preparedTxOp) error {
+	_, err := tx.store.commonRoot.Stat("packed-refs.lock")
+	if err != nil {
+		if errors.Is(err, os.ErrNotExist) {
+			return nil
+		}
+
+		return err
+	}
+
+	packed, err := tx.store.readPackedRefs()
+	if err != nil {
+		return err
+	}
+
+	deleted := make(map[string]struct{})
+	needed := false
+
+	for _, item := range prepared {
+		if item.op.kind != txDelete && item.op.kind != txDeleteSymbolic {
+			continue
+		}
+
+		deleted[item.target.name] = struct{}{}
+		if item.target.ref.isPacked {
+			needed = true
+		}
+	}
+
+	if !needed {
+		return nil
+	}
+
+	lock, err := tx.store.commonRoot.OpenFile("packed-refs.new", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644)
+	if err != nil {
+		return err
+	}
+
+	createdTemp := true
+
+	defer func() {
+		if !createdTemp {
+			return
+		}
+
+		_ = tx.store.commonRoot.Remove("packed-refs.new")
+	}()
+
+	_, err = lock.WriteString("# pack-refs with: peeled fully-peeled sorted\n")
+	if err != nil {
+		_ = lock.Close()
+
+		return err
+	}
+
+	for _, entry := range packed.ordered {
+		if _, skip := deleted[entry.Name()]; skip {
+			continue
+		}
+
+		_, err = lock.WriteString(entry.ID.String() + " " + entry.Name() + "\n")
+		if err != nil {
+			_ = lock.Close()
+
+			return err
+		}
+
+		if entry.Peeled != nil {
+			_, err = lock.WriteString("^" + entry.Peeled.String() + "\n")
+			if err != nil {
+				_ = lock.Close()
+
+				return err
+			}
+		}
+	}
+
+	err = lock.Close()
+	if err != nil {
+		return err
+	}
+
+	err = tx.store.commonRoot.Rename("packed-refs.new", "packed-refs")
+	if err != nil {
+		return err
+	}
+
+	createdTemp = false
+
+	return nil
+}
--- /dev/null
+++ b/refstore/files/trim.go
@@ -1,0 +1,10 @@
+package files
+
+func isRefWhitespace(r rune) bool {
+	switch r {
+	case ' ', '\t', '\n', '\r', '\v', '\f':
+		return true
+	default:
+		return false
+	}
+}
--- a/repository/open.go
+++ b/repository/open.go
@@ -36,7 +36,7 @@
 	repo.objects = objects
 	repo.objectsLooseForWritingOnly = objectsLooseForWritingOnly
 
-	refs, err := openRefStore(root, algo)
+	refs, err := openRefStore(root, algo, detectPackedRefsTimeout(cfg))
 	if err != nil {
 		return nil, err
 	}
--- a/repository/refs.go
+++ b/repository/refs.go
@@ -3,6 +3,7 @@
 import (
 	"fmt"
 	"os"
+	"time"
 
 	"codeberg.org/lindenii/furgit/objectid"
 	"codeberg.org/lindenii/furgit/refstore"
@@ -10,13 +11,13 @@
 )
 
 //nolint:ireturn
-func openRefStore(root *os.Root, algo objectid.Algorithm) (out refstore.ReadingStore, err error) {
+func openRefStore(root *os.Root, algo objectid.Algorithm, packedRefsTimeout time.Duration) (out refstore.ReadingStore, 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)
+	store, err := reffiles.New(refRoot, algo, packedRefsTimeout)
 	if err != nil {
 		_ = refRoot.Close()
 
--- /dev/null
+++ b/repository/refs_timeout.go
@@ -1,0 +1,16 @@
+package repository
+
+import (
+	"time"
+
+	"codeberg.org/lindenii/furgit/config"
+)
+
+func detectPackedRefsTimeout(cfg *config.Config) time.Duration {
+	timeoutValue, err := cfg.Lookup("core", "", "packedrefstimeout").Int()
+	if err != nil {
+		return time.Second
+	}
+
+	return time.Duration(timeoutValue) * time.Millisecond
+}
--