shithub: furgit

Download patch

ref: f23c6a801eef4e7d115b568d7fa905596cd1b253
parent: d4483c3d47e019d3b3c110e09a09b501f8aef64f
author: Runxi Yu <runxiyu@umich.edu>
date: Mon Mar 30 09:21:59 EDT 2026

object/store/memory: Remove AddObject, fix lints

--- a/commitquery/queries_is_ancestor_unit_test.go
+++ b/commitquery/queries_is_ancestor_unit_test.go
@@ -55,23 +55,55 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := memory.New(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeAncestorTree(t, &objecttree.Tree{Entries: []objecttree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeAncestorTree(t, &objecttree.Tree{Entries: []objecttree.TreeEntry{{
 			Mode: objecttree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
-		c1 := store.AddObject(objecttype.TypeCommit, ancestorCommitBody(tree))
-		c2 := store.AddObject(objecttype.TypeCommit, ancestorCommitBody(tree, c1))
-		otherBlob := store.AddObject(objecttype.TypeBlob, []byte("other-blob\n"))
-		otherTree := store.AddObject(objecttype.TypeTree, mustSerializeAncestorTree(t, &objecttree.Tree{Entries: []objecttree.TreeEntry{{
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		c1, err := store.WriteBytesContent(objecttype.TypeCommit, ancestorCommitBody(tree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		c2, err := store.WriteBytesContent(objecttype.TypeCommit, ancestorCommitBody(tree, c1))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		otherBlob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("other-blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		otherTree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeAncestorTree(t, &objecttree.Tree{Entries: []objecttree.TreeEntry{{
 			Mode: objecttree.FileModeRegular,
 			Name: []byte("g"),
 			ID:   otherBlob,
 		}}}))
-		c3 := store.AddObject(objecttype.TypeCommit, ancestorCommitBody(otherTree))
-		tag := store.AddObject(objecttype.TypeTag, ancestorTagBody(c2, objecttype.TypeCommit))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		c3, err := store.WriteBytesContent(objecttype.TypeCommit, ancestorCommitBody(otherTree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tag, err := store.WriteBytesContent(objecttype.TypeTag, ancestorTagBody(c2, objecttype.TypeCommit))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		ok, err := commitquery.New(fetch.New(store), nil).IsAncestor(c1, tag)
 		if err != nil {
 			t.Fatalf("Is(c1, tag): %v", err)
@@ -97,16 +129,32 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := memory.New(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeAncestorTree(t, &objecttree.Tree{Entries: []objecttree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeAncestorTree(t, &objecttree.Tree{Entries: []objecttree.TreeEntry{{
 			Mode: objecttree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
-		commit := store.AddObject(objecttype.TypeCommit, ancestorCommitBody(tree))
-		tagToTree := store.AddObject(objecttype.TypeTag, ancestorTagBody(tree, objecttype.TypeTree))
+		if err != nil {
+			t.Fatal(err)
+		}
 
-		_, err := commitquery.New(fetch.New(store), nil).IsAncestor(commit, tagToTree)
+		commit, err := store.WriteBytesContent(objecttype.TypeCommit, ancestorCommitBody(tree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tagToTree, err := store.WriteBytesContent(objecttype.TypeTag, ancestorTagBody(tree, objecttype.TypeTree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		_, err = commitquery.New(fetch.New(store), nil).IsAncestor(commit, tagToTree)
 		if err == nil {
 			t.Fatal("expected error")
 		}
--- a/commitquery/queries_merge_bases_unit_test.go
+++ b/commitquery/queries_merge_bases_unit_test.go
@@ -74,16 +74,36 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := memory.New(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
-		base := store.AddObject(objecttype.TypeCommit, commitBody(tree))
-		left := store.AddObject(objecttype.TypeCommit, commitBody(tree, base))
-		right := store.AddObject(objecttype.TypeCommit, commitBody(tree, left))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		base, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		left, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree, base))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		right, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree, left))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		query := commitquery.New(fetch.New(store), nil)
 
 		got, err := query.MergeBases(left, right)
@@ -115,22 +135,50 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := memory.New(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		leftTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		leftTree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("left"),
 			ID:   blob,
 		}}}))
-		rightTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		rightTree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("right"),
 			ID:   blob,
 		}}}))
-		base := store.AddObject(objecttype.TypeCommit, commitBody(leftTree))
-		left := store.AddObject(objecttype.TypeCommit, commitBody(leftTree, base))
-		right := store.AddObject(objecttype.TypeCommit, commitBody(rightTree, base))
-		tag := store.AddObject(objecttype.TypeTag, tagBody(right, objecttype.TypeCommit))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		base, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(leftTree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		left, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(leftTree, base))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		right, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(rightTree, base))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tag, err := store.WriteBytesContent(objecttype.TypeTag, tagBody(right, objecttype.TypeCommit))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		query := commitquery.New(fetch.New(store), nil)
 
 		got, err := query.MergeBases(left, tag)
@@ -149,38 +197,82 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := memory.New(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		rootTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		rootTree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("root"),
 			ID:   blob,
 		}}}))
-		base1Tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		base1Tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("base1"),
 			ID:   blob,
 		}}}))
-		base2Tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		base2Tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("base2"),
 			ID:   blob,
 		}}}))
-		leftTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		leftTree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("left"),
 			ID:   blob,
 		}}}))
-		rightTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		rightTree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("right"),
 			ID:   blob,
 		}}}))
-		root := store.AddObject(objecttype.TypeCommit, commitBody(rootTree))
-		base1 := store.AddObject(objecttype.TypeCommit, commitBody(base1Tree, root))
-		base2 := store.AddObject(objecttype.TypeCommit, commitBody(base2Tree, root))
-		left := store.AddObject(objecttype.TypeCommit, commitBody(leftTree, base1, base2))
-		right := store.AddObject(objecttype.TypeCommit, commitBody(rightTree, base2, base1))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		root, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(rootTree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		base1, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(base1Tree, root))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		base2, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(base2Tree, root))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		left, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(leftTree, base1, base2))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		right, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(rightTree, base2, base1))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		query := commitquery.New(fetch.New(store), nil)
 
 		all, err := query.MergeBases(left, right)
@@ -215,21 +307,45 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := memory.New(algo)
-		leftBlob := store.AddObject(objecttype.TypeBlob, []byte("left\n"))
-		leftTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		leftBlob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("left\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		leftTree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("left"),
 			ID:   leftBlob,
 		}}}))
-		rightBlob := store.AddObject(objecttype.TypeBlob, []byte("right\n"))
-		rightTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		rightBlob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("right\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		rightTree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("right"),
 			ID:   rightBlob,
 		}}}))
-		left := store.AddObject(objecttype.TypeCommit, commitBody(leftTree))
-		right := store.AddObject(objecttype.TypeCommit, commitBody(rightTree))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		left, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(leftTree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		right, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(rightTree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		query := commitquery.New(fetch.New(store), nil)
 
 		got, err := query.MergeBases(left, right)
@@ -257,18 +373,34 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := memory.New(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
-		commit := store.AddObject(objecttype.TypeCommit, commitBody(tree))
-		tagToTree := store.AddObject(objecttype.TypeTag, tagBody(tree, objecttype.TypeTree))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		commit, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tagToTree, err := store.WriteBytesContent(objecttype.TypeTag, tagBody(tree, objecttype.TypeTree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		query := commitquery.New(fetch.New(store), nil)
 
-		_, err := query.MergeBases(commit, tagToTree)
+		_, err = query.MergeBases(commit, tagToTree)
 		if err == nil {
 			t.Fatal("expected error")
 		}
@@ -289,15 +421,35 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := memory.New(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
-		base := store.AddObject(objecttype.TypeCommit, commitBody(tree))
-		left := store.AddObject(objecttype.TypeCommit, commitBody(tree, base))
-		right := store.AddObject(objecttype.TypeCommit, commitBody(tree, left))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		base, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		left, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree, base))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		right, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree, left))
+		if err != nil {
+			t.Fatal(err)
+		}
 
 		query := commitquery.New(fetch.New(store), nil)
 
--- a/object/store/memory/add.go
+++ /dev/null
@@ -1,18 +1,0 @@
-package memory
-
-import (
-	objectid "codeberg.org/lindenii/furgit/object/id"
-	objecttype "codeberg.org/lindenii/furgit/object/type"
-)
-
-// AddObject stores one object body and returns its object ID.
-//
-//go:fix inline
-func (store *Store) AddObject(ty objecttype.Type, body []byte) objectid.ObjectID {
-	id, err := store.WriteBytesContent(ty, body)
-	if err != nil {
-		panic(err)
-	}
-
-	return id
-}
--- a/object/store/memory/write_test.go
+++ b/object/store/memory/write_test.go
@@ -1,4 +1,4 @@
-package memory
+package memory_test
 
 import (
 	"bytes"
@@ -7,6 +7,7 @@
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	objectheader "codeberg.org/lindenii/furgit/object/header"
 	objectid "codeberg.org/lindenii/furgit/object/id"
+	"codeberg.org/lindenii/furgit/object/store/memory"
 	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
 
@@ -13,7 +14,7 @@
 func TestStoreWriteReaderContent(t *testing.T) {
 	t.Parallel()
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
-		store := New(algo)
+		store := memory.New(algo)
 		content := []byte("memory-content\n")
 
 		gotID, err := store.WriteReaderContent(objecttype.TypeBlob, int64(len(content)), bytes.NewReader(content))
@@ -21,7 +22,7 @@
 			t.Fatalf("WriteReaderContent: %v", err)
 		}
 
-		wantID := algo.Sum(buildRawObject(objecttype.TypeBlob, content))
+		wantID := algo.Sum(buildRawObject(t, objecttype.TypeBlob, content))
 		if gotID != wantID {
 			t.Fatalf("WriteReaderContent id = %s, want %s", gotID, wantID)
 		}
@@ -44,9 +45,9 @@
 func TestStoreWriteReaderFull(t *testing.T) {
 	t.Parallel()
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
-		store := New(algo)
+		store := memory.New(algo)
 		content := []byte("memory-full\n")
-		raw := buildRawObject(objecttype.TypeBlob, content)
+		raw := buildRawObject(t, objecttype.TypeBlob, content)
 
 		gotID, err := store.WriteReaderFull(bytes.NewReader(raw))
 		if err != nil {
@@ -72,7 +73,7 @@
 func TestStoreWriteBytes(t *testing.T) {
 	t.Parallel()
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
-		store := New(algo)
+		store := memory.New(algo)
 		content := []byte("memory-bytes\n")
 
 		gotID, err := store.WriteBytesContent(objecttype.TypeBlob, content)
@@ -80,12 +81,12 @@
 			t.Fatalf("WriteBytesContent: %v", err)
 		}
 
-		wantID := algo.Sum(buildRawObject(objecttype.TypeBlob, content))
+		wantID := algo.Sum(buildRawObject(t, objecttype.TypeBlob, content))
 		if gotID != wantID {
 			t.Fatalf("WriteBytesContent id = %s, want %s", gotID, wantID)
 		}
 
-		raw := buildRawObject(objecttype.TypeBlob, content)
+		raw := buildRawObject(t, objecttype.TypeBlob, content)
 
 		gotID2, err := store.WriteBytesFull(raw)
 		if err != nil {
@@ -103,8 +104,9 @@
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		t.Run("content overflow", func(t *testing.T) {
 			t.Parallel()
-			store := New(algo)
 
+			store := memory.New(algo)
+
 			_, err := store.WriteReaderContent(objecttype.TypeBlob, 1, bytes.NewReader([]byte("hello")))
 			if err == nil {
 				t.Fatalf("expected error after overflow")
@@ -113,8 +115,9 @@
 
 		t.Run("content short", func(t *testing.T) {
 			t.Parallel()
-			store := New(algo)
 
+			store := memory.New(algo)
+
 			_, err := store.WriteReaderContent(objecttype.TypeBlob, 5, bytes.NewReader([]byte("x")))
 			if err == nil {
 				t.Fatalf("expected error for short content")
@@ -123,8 +126,9 @@
 
 		t.Run("full malformed header", func(t *testing.T) {
 			t.Parallel()
-			store := New(algo)
 
+			store := memory.New(algo)
+
 			_, err := store.WriteReaderFull(bytes.NewReader([]byte("not-a-header")))
 			if err == nil {
 				t.Fatalf("expected error for malformed header")
@@ -133,8 +137,9 @@
 
 		t.Run("full size mismatch", func(t *testing.T) {
 			t.Parallel()
-			store := New(algo)
 
+			store := memory.New(algo)
+
 			_, err := store.WriteReaderFull(bytes.NewReader([]byte("blob 1\x00hello")))
 			if err == nil {
 				t.Fatalf("expected error after mismatch")
@@ -143,8 +148,9 @@
 
 		t.Run("bytes malformed header", func(t *testing.T) {
 			t.Parallel()
-			store := New(algo)
 
+			store := memory.New(algo)
+
 			_, err := store.WriteBytesFull([]byte("not-a-header"))
 			if err == nil {
 				t.Fatalf("expected error for malformed byte header")
@@ -157,7 +163,8 @@
 	t.Parallel()
 
 	content := []byte("body")
-	raw := buildRawObject(objecttype.TypeBlob, content)
+	raw := buildRawObject(t, objecttype.TypeBlob, content)
+
 	header, ok := objectheader.Encode(objecttype.TypeBlob, int64(len(content)))
 	if !ok {
 		t.Fatalf("objectheader.Encode failed")
@@ -167,4 +174,19 @@
 	if !bytes.Equal(raw, want) {
 		t.Fatalf("buildRawObject mismatch")
 	}
+}
+
+func buildRawObject(tb testing.TB, ty objecttype.Type, body []byte) []byte { //nolint:unparam
+	tb.Helper()
+
+	header, ok := objectheader.Encode(ty, int64(len(body)))
+	if !ok {
+		tb.Fatalf("objectheader.Encode(%v, %d) failed", ty, len(body))
+	}
+
+	raw := make([]byte, len(header)+len(body))
+	copy(raw, header)
+	copy(raw[len(header):], body)
+
+	return raw
 }
--- a/reachability/unit_test.go
+++ b/reachability/unit_test.go
@@ -83,23 +83,47 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := newCountingMemStore(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
-		commit1 := store.AddObject(objecttype.TypeCommit, commitBody(tree))
-		commit2 := store.AddObject(objecttype.TypeCommit, commitBody(tree, commit1))
-		tag1 := store.AddObject(objecttype.TypeTag, tagBody(commit2, objecttype.TypeCommit))
-		tag2 := store.AddObject(objecttype.TypeTag, tagBody(tag1, objecttype.TypeTag))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		commit1, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		commit2, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree, commit1))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tag1, err := store.WriteBytesContent(objecttype.TypeTag, tagBody(commit2, objecttype.TypeCommit))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tag2, err := store.WriteBytesContent(objecttype.TypeTag, tagBody(tag1, objecttype.TypeTag))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		r := reachability.New(store, nil)
 		walk := r.Walk(reachability.DomainCommits, nil, map[objectid.ObjectID]struct{}{tag2: {}})
 
 		got := collectSeq(walk.Seq())
 
-		err := walk.Err()
+		err = walk.Err()
 		if err != nil {
 			t.Fatalf("walk.Err(): %v", err)
 		}
@@ -118,20 +142,32 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := newCountingMemStore(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
-		commit := store.AddObject(objecttype.TypeCommit, commitBody(tree))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		commit, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		r := reachability.New(store, nil)
 		walk := r.Walk(reachability.DomainCommits, map[objectid.ObjectID]struct{}{commit: {}}, map[objectid.ObjectID]struct{}{commit: {}})
 
 		got := collectSeq(walk.Seq())
 
-		err := walk.Err()
+		err = walk.Err()
 		if err != nil {
 			t.Fatalf("walk.Err(): %v", err)
 		}
@@ -147,19 +183,31 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := newCountingMemStore(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
-		tag := store.AddObject(objecttype.TypeTag, tagBody(tree, objecttype.TypeTree))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		tag, err := store.WriteBytesContent(objecttype.TypeTag, tagBody(tree, objecttype.TypeTree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		r := reachability.New(store, nil)
 		walk := r.Walk(reachability.DomainCommits, nil, map[objectid.ObjectID]struct{}{tag: {}})
 		_ = collectSeq(walk.Seq())
 
-		err := walk.Err()
+		err = walk.Err()
 		if err == nil {
 			t.Fatal("expected error")
 		}
@@ -180,17 +228,41 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := newCountingMemStore(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
-		commit1 := store.AddObject(objecttype.TypeCommit, commitBody(tree))
-		commit2 := store.AddObject(objecttype.TypeCommit, commitBody(tree, commit1))
-		tag1 := store.AddObject(objecttype.TypeTag, tagBody(commit2, objecttype.TypeCommit))
-		tag2 := store.AddObject(objecttype.TypeTag, tagBody(tag1, objecttype.TypeTag))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		commit1, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		commit2, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree, commit1))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tag1, err := store.WriteBytesContent(objecttype.TypeTag, tagBody(commit2, objecttype.TypeCommit))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tag2, err := store.WriteBytesContent(objecttype.TypeTag, tagBody(tag1, objecttype.TypeTag))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		r := reachability.New(store, nil)
 		walk := r.Walk(
 			reachability.DomainCommits,
@@ -200,7 +272,7 @@
 
 		got := collectSeq(walk.Seq())
 
-		err := walk.Err()
+		err = walk.Err()
 		if err != nil {
 			t.Fatalf("walk.Err(): %v", err)
 		}
@@ -220,28 +292,47 @@
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := newCountingMemStore(algo)
 
-		blob1 := store.AddObject(objecttype.TypeBlob, []byte("b1\n"))
-		blob2 := store.AddObject(objecttype.TypeBlob, []byte("b2\n"))
+		blob1, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("b1\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		blob2, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("b2\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		gitlinkTarget := store.Algorithm().Sum([]byte("external-submodule"))
 
-		subtree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+		subtree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("nested"),
 			ID:   blob2,
 		}}}))
-		rootTree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		rootTree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{
 			{Mode: tree.FileModeRegular, Name: []byte("a"), ID: blob1},
 			{Mode: tree.FileModeDir, Name: []byte("dir"), ID: subtree},
 			{Mode: tree.FileModeGitlink, Name: []byte("submodule"), ID: gitlinkTarget},
 		}}))
-		commit := store.AddObject(objecttype.TypeCommit, commitBody(rootTree))
+		if err != nil {
+			t.Fatal(err)
+		}
 
+		commit, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(rootTree))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		r := reachability.New(store, nil)
 		walk := r.Walk(reachability.DomainObjects, nil, map[objectid.ObjectID]struct{}{commit: {}})
 
 		got := collectSeq(walk.Seq())
 
-		err := walk.Err()
+		err = walk.Err()
 		if err != nil {
 			t.Fatalf("walk.Err(): %v", err)
 		}
@@ -264,18 +355,31 @@
 
 	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
 		store := newCountingMemStore(algo)
-		blob := store.AddObject(objecttype.TypeBlob, []byte("blob\n"))
-		tree := store.AddObject(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
+
+		blob, err := store.WriteBytesContent(objecttype.TypeBlob, []byte("blob\n"))
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tree, err := store.WriteBytesContent(objecttype.TypeTree, mustSerializeTree(t, &tree.Tree{Entries: []tree.TreeEntry{{
 			Mode: tree.FileModeRegular,
 			Name: []byte("f"),
 			ID:   blob,
 		}}}))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		missingParent := store.Algorithm().Sum([]byte("missing-parent"))
-		commit := store.AddObject(objecttype.TypeCommit, commitBody(tree, missingParent))
 
+		commit, err := store.WriteBytesContent(objecttype.TypeCommit, commitBody(tree, missingParent))
+		if err != nil {
+			t.Fatal(err)
+		}
+
 		r := reachability.New(store, nil)
 
-		err := r.CheckConnected(reachability.DomainCommits, nil, map[objectid.ObjectID]struct{}{commit: {}})
+		err = r.CheckConnected(reachability.DomainCommits, nil, map[objectid.ObjectID]struct{}{commit: {}})
 		if err == nil {
 			t.Fatal("expected error")
 		}
--