shithub: furgit

Download patch

ref: f49c95662bda1f2c337dbe872644afa1ca5cbbec
parent: dc110eaaf376c02fd957853330566a75b1511f3c
author: Runxi Yu <me@runxiyu.org>
date: Fri Feb 20 17:52:58 EST 2026

objectid: Rename from oid

--- a/config/config_test.go
+++ b/config/config_test.go
@@ -7,7 +7,7 @@
 	"testing"
 
 	"codeberg.org/lindenii/furgit/internal/testgit"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 func openConfig(t *testing.T, repo *testgit.TestRepo) *os.File {
@@ -25,7 +25,7 @@
 }
 
 func TestConfigAgainstGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		repo.Run(t, "config", "core.bare", "true")
 		repo.Run(t, "config", "core.filemode", "false")
@@ -56,7 +56,7 @@
 }
 
 func TestConfigSubsectionAgainstGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		repo.Run(t, "config", "remote.origin.url", "https://example.org/repo.git")
 		repo.Run(t, "config", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*")
@@ -79,7 +79,7 @@
 }
 
 func TestConfigMultiValueAgainstGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		repo.Run(t, "config", "--add", "remote.origin.fetch", "+refs/heads/main:refs/remotes/origin/main")
 		repo.Run(t, "config", "--add", "remote.origin.fetch", "+refs/heads/dev:refs/remotes/origin/dev")
@@ -112,7 +112,7 @@
 }
 
 func TestConfigCaseInsensitiveAgainstGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		repo.Run(t, "config", "Core.Bare", "true")
 		repo.Run(t, "config", "CORE.FileMode", "false")
@@ -141,7 +141,7 @@
 }
 
 func TestConfigBooleanAgainstGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		repo.Run(t, "config", "test.flag1", "true")
 		repo.Run(t, "config", "test.flag2", "false")
@@ -175,7 +175,7 @@
 }
 
 func TestConfigComplexValuesAgainstGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		repo.Run(t, "config", "test.spaced", "value with spaces")
 		repo.Run(t, "config", "test.special", "value=with=equals")
@@ -201,7 +201,7 @@
 }
 
 func TestConfigEntriesAgainstGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		repo.Run(t, "config", "core.bare", "true")
 		repo.Run(t, "config", "core.filemode", "false")
--- a/internal/testgit/algorithms.go
+++ b/internal/testgit/algorithms.go
@@ -3,19 +3,19 @@
 import (
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // SupportedAlgorithms returns all object ID algorithms supported by furgit.
-func SupportedAlgorithms() []oid.Algorithm {
-	return []oid.Algorithm{
-		oid.AlgorithmSHA1,
-		oid.AlgorithmSHA256,
+func SupportedAlgorithms() []objectid.Algorithm {
+	return []objectid.Algorithm{
+		objectid.AlgorithmSHA1,
+		objectid.AlgorithmSHA256,
 	}
 }
 
 // ForEachAlgorithm runs a subtest for every supported algorithm.
-func ForEachAlgorithm(t *testing.T, fn func(t *testing.T, algo oid.Algorithm)) {
+func ForEachAlgorithm(t *testing.T, fn func(t *testing.T, algo objectid.Algorithm)) {
 	t.Helper()
 	for _, algo := range SupportedAlgorithms() {
 		t.Run(algo.String(), func(t *testing.T) {
--- a/internal/testgit/repo.go
+++ b/internal/testgit/repo.go
@@ -1,10 +1,10 @@
 package testgit
 
-import "codeberg.org/lindenii/furgit/oid"
+import "codeberg.org/lindenii/furgit/objectid"
 
 // TestRepo is a temporary git repository harness for integration tests.
 type TestRepo struct {
 	dir  string
-	algo oid.Algorithm
+	algo objectid.Algorithm
 	env  []string
 }
--- a/internal/testgit/repo_cat_file.go
+++ b/internal/testgit/repo_cat_file.go
@@ -3,11 +3,11 @@
 import (
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // CatFile returns raw output from git cat-file.
-func (repo *TestRepo) CatFile(tb testing.TB, mode string, id oid.ObjectID) []byte {
+func (repo *TestRepo) CatFile(tb testing.TB, mode string, id objectid.ObjectID) []byte {
 	tb.Helper()
 	return repo.RunBytes(tb, "cat-file", mode, id.String())
 }
--- a/internal/testgit/repo_commit_tree.go
+++ b/internal/testgit/repo_commit_tree.go
@@ -3,11 +3,11 @@
 import (
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // CommitTree creates a commit from a tree and message, optionally with parents.
-func (repo *TestRepo) CommitTree(tb testing.TB, tree oid.ObjectID, message string, parents ...oid.ObjectID) oid.ObjectID {
+func (repo *TestRepo) CommitTree(tb testing.TB, tree objectid.ObjectID, message string, parents ...objectid.ObjectID) objectid.ObjectID {
 	tb.Helper()
 	args := []string{"commit-tree", tree.String()}
 	for _, p := range parents {
@@ -15,7 +15,7 @@
 	}
 	args = append(args, "-m", message)
 	hex := repo.Run(tb, args...)
-	id, err := oid.ParseHex(repo.algo, hex)
+	id, err := objectid.ParseHex(repo.algo, hex)
 	if err != nil {
 		tb.Fatalf("parse commit-tree output %q: %v", hex, err)
 	}
--- a/internal/testgit/repo_hash_object.go
+++ b/internal/testgit/repo_hash_object.go
@@ -3,14 +3,14 @@
 import (
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // HashObject hashes and writes an object and returns its object ID.
-func (repo *TestRepo) HashObject(tb testing.TB, objType string, body []byte) oid.ObjectID {
+func (repo *TestRepo) HashObject(tb testing.TB, objType string, body []byte) objectid.ObjectID {
 	tb.Helper()
 	hex := repo.RunInput(tb, body, "hash-object", "-t", objType, "-w", "--stdin")
-	id, err := oid.ParseHex(repo.algo, hex)
+	id, err := objectid.ParseHex(repo.algo, hex)
 	if err != nil {
 		tb.Fatalf("parse git hash-object output %q: %v", hex, err)
 	}
--- a/internal/testgit/repo_make_commit.go
+++ b/internal/testgit/repo_make_commit.go
@@ -3,11 +3,11 @@
 import (
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // MakeCommit creates a commit over a single-file tree and returns (blobID, treeID, commitID).
-func (repo *TestRepo) MakeCommit(tb testing.TB, message string) (oid.ObjectID, oid.ObjectID, oid.ObjectID) {
+func (repo *TestRepo) MakeCommit(tb testing.TB, message string) (objectid.ObjectID, objectid.ObjectID, objectid.ObjectID) {
 	tb.Helper()
 	blobID, treeID := repo.MakeSingleFileTree(tb, "file.txt", []byte("commit-body\n"))
 	commitID := repo.CommitTree(tb, treeID, message)
--- a/internal/testgit/repo_make_single_file_tree.go
+++ b/internal/testgit/repo_make_single_file_tree.go
@@ -4,11 +4,11 @@
 	"fmt"
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // MakeSingleFileTree writes one blob and one tree entry for it and returns (blobID, treeID).
-func (repo *TestRepo) MakeSingleFileTree(tb testing.TB, fileName string, fileContent []byte) (oid.ObjectID, oid.ObjectID) {
+func (repo *TestRepo) MakeSingleFileTree(tb testing.TB, fileName string, fileContent []byte) (objectid.ObjectID, objectid.ObjectID) {
 	tb.Helper()
 	blobID := repo.HashObject(tb, "blob", fileContent)
 	treeInput := fmt.Sprintf("100644 blob %s\t%s\n", blobID.String(), fileName)
--- a/internal/testgit/repo_mktree.go
+++ b/internal/testgit/repo_mktree.go
@@ -3,14 +3,14 @@
 import (
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // Mktree creates a tree from textual mktree input and returns its ID.
-func (repo *TestRepo) Mktree(tb testing.TB, input string) oid.ObjectID {
+func (repo *TestRepo) Mktree(tb testing.TB, input string) objectid.ObjectID {
 	tb.Helper()
 	hex := repo.RunInput(tb, []byte(input), "mktree")
-	id, err := oid.ParseHex(repo.algo, hex)
+	id, err := objectid.ParseHex(repo.algo, hex)
 	if err != nil {
 		tb.Fatalf("parse mktree output %q: %v", hex, err)
 	}
--- a/internal/testgit/repo_new.go
+++ b/internal/testgit/repo_new.go
@@ -4,22 +4,22 @@
 	"os"
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // NewBareRepo creates a temporary bare repository initialized with the requested algorithm.
-func NewBareRepo(tb testing.TB, algo oid.Algorithm) *TestRepo {
+func NewBareRepo(tb testing.TB, algo objectid.Algorithm) *TestRepo {
 	tb.Helper()
 	return newRepo(tb, algo, true)
 }
 
 // NewWorkRepo creates a temporary non-bare repository initialized with the requested algorithm.
-func NewWorkRepo(tb testing.TB, algo oid.Algorithm) *TestRepo {
+func NewWorkRepo(tb testing.TB, algo objectid.Algorithm) *TestRepo {
 	tb.Helper()
 	return newRepo(tb, algo, false)
 }
 
-func newRepo(tb testing.TB, algo oid.Algorithm, bare bool) *TestRepo {
+func newRepo(tb testing.TB, algo objectid.Algorithm, bare bool) *TestRepo {
 	tb.Helper()
 	if algo.Size() == 0 {
 		tb.Fatalf("invalid algorithm: %v", algo)
--- a/internal/testgit/repo_properties.go
+++ b/internal/testgit/repo_properties.go
@@ -1,6 +1,6 @@
 package testgit
 
-import "codeberg.org/lindenii/furgit/oid"
+import "codeberg.org/lindenii/furgit/objectid"
 
 // Dir returns the repository directory path.
 func (repo *TestRepo) Dir() string {
@@ -8,6 +8,6 @@
 }
 
 // Algorithm returns the object ID algorithm configured for this repository.
-func (repo *TestRepo) Algorithm() oid.Algorithm {
+func (repo *TestRepo) Algorithm() objectid.Algorithm {
 	return repo.algo
 }
--- a/internal/testgit/repo_rev_parse.go
+++ b/internal/testgit/repo_rev_parse.go
@@ -3,14 +3,14 @@
 import (
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // RevParse resolves rev expressions to object IDs.
-func (repo *TestRepo) RevParse(tb testing.TB, spec string) oid.ObjectID {
+func (repo *TestRepo) RevParse(tb testing.TB, spec string) objectid.ObjectID {
 	tb.Helper()
 	hex := repo.Run(tb, "rev-parse", spec)
-	id, err := oid.ParseHex(repo.algo, hex)
+	id, err := objectid.ParseHex(repo.algo, hex)
 	if err != nil {
 		tb.Fatalf("parse rev-parse output %q: %v", hex, err)
 	}
--- a/internal/testgit/repo_tag_annotated.go
+++ b/internal/testgit/repo_tag_annotated.go
@@ -4,11 +4,11 @@
 	"fmt"
 	"testing"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // TagAnnotated creates an annotated tag object and returns the resulting tag object ID.
-func (repo *TestRepo) TagAnnotated(tb testing.TB, name string, target oid.ObjectID, message string) oid.ObjectID {
+func (repo *TestRepo) TagAnnotated(tb testing.TB, name string, target objectid.ObjectID, message string) objectid.ObjectID {
 	tb.Helper()
 	repo.Run(tb, "tag", "-a", name, target.String(), "-m", message)
 	return repo.RevParse(tb, fmt.Sprintf("refs/tags/%s", name))
--- a/object/blob_parse_test.go
+++ b/object/blob_parse_test.go
@@ -6,11 +6,11 @@
 
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	"codeberg.org/lindenii/furgit/object"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 func TestBlobParseFromGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		body := []byte("hello\nblob\n")
 		blobID := repo.HashObject(t, "blob", body)
--- a/object/blob_serialize_test.go
+++ b/object/blob_serialize_test.go
@@ -5,11 +5,11 @@
 
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	"codeberg.org/lindenii/furgit/object"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 func TestBlobSerialize(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		body := []byte("hello\nblob\n")
 		wantID := repo.HashObject(t, "blob", body)
--- a/object/commit.go
+++ b/object/commit.go
@@ -2,13 +2,13 @@
 
 import (
 	"codeberg.org/lindenii/furgit/objecttype"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // Commit represents a Git commit object.
 type Commit struct {
-	Tree         oid.ObjectID
-	Parents      []oid.ObjectID
+	Tree         objectid.ObjectID
+	Parents      []objectid.ObjectID
 	Author       Ident
 	Committer    Ident
 	Message      []byte
--- a/object/commit_parse.go
+++ b/object/commit_parse.go
@@ -5,11 +5,11 @@
 	"errors"
 	"fmt"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // ParseCommit decodes a commit object body.
-func ParseCommit(body []byte, algo oid.Algorithm) (*Commit, error) {
+func ParseCommit(body []byte, algo objectid.Algorithm) (*Commit, error) {
 	c := new(Commit)
 	i := 0
 	for i < len(body) {
@@ -30,13 +30,13 @@
 
 		switch string(key) {
 		case "tree":
-			id, err := oid.ParseHex(algo, string(value))
+			id, err := objectid.ParseHex(algo, string(value))
 			if err != nil {
 				return nil, fmt.Errorf("object: commit: tree: %w", err)
 			}
 			c.Tree = id
 		case "parent":
-			id, err := oid.ParseHex(algo, string(value))
+			id, err := objectid.ParseHex(algo, string(value))
 			if err != nil {
 				return nil, fmt.Errorf("object: commit: parent: %w", err)
 			}
--- a/object/commit_parse_test.go
+++ b/object/commit_parse_test.go
@@ -6,11 +6,11 @@
 
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	"codeberg.org/lindenii/furgit/object"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 func TestCommitParseFromGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		_, treeID, commitID := repo.MakeCommit(t, "subject\n\nbody")
 
--- a/object/commit_serialize_test.go
+++ b/object/commit_serialize_test.go
@@ -5,11 +5,11 @@
 
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	"codeberg.org/lindenii/furgit/object"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 func TestCommitSerialize(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		_, _, commitID := repo.MakeCommit(t, "subject\n\nbody")
 
--- a/object/parse.go
+++ b/object/parse.go
@@ -5,11 +5,11 @@
 
 	"codeberg.org/lindenii/furgit/internal/objectheader"
 	"codeberg.org/lindenii/furgit/objecttype"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // ParseObjectWithoutHeader parses a typed object body.
-func ParseObjectWithoutHeader(ty objecttype.Type, body []byte, algo oid.Algorithm) (Object, error) {
+func ParseObjectWithoutHeader(ty objecttype.Type, body []byte, algo objectid.Algorithm) (Object, error) {
 	switch ty {
 	case objecttype.TypeBlob:
 		return ParseBlob(body)
@@ -25,7 +25,7 @@
 }
 
 // ParseObjectWithHeader parses a loose object in "type size\\x00body" format.
-func ParseObjectWithHeader(raw []byte, algo oid.Algorithm) (Object, error) {
+func ParseObjectWithHeader(raw []byte, algo objectid.Algorithm) (Object, error) {
 	ty, size, headerLen, ok := objectheader.Parse(raw)
 	if !ok {
 		return nil, fmt.Errorf("object: malformed object header")
--- a/object/tag.go
+++ b/object/tag.go
@@ -2,12 +2,12 @@
 
 import (
 	"codeberg.org/lindenii/furgit/objecttype"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // Tag represents a Git annotated tag object.
 type Tag struct {
-	Target     oid.ObjectID
+	Target     objectid.ObjectID
 	TargetType objecttype.Type
 	Name       []byte
 	Tagger     *Ident
--- a/object/tag_parse.go
+++ b/object/tag_parse.go
@@ -6,11 +6,11 @@
 	"fmt"
 
 	"codeberg.org/lindenii/furgit/objecttype"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // ParseTag decodes a tag object body.
-func ParseTag(body []byte, algo oid.Algorithm) (*Tag, error) {
+func ParseTag(body []byte, algo objectid.Algorithm) (*Tag, error) {
 	t := new(Tag)
 	i := 0
 	var haveTarget, haveType bool
@@ -33,7 +33,7 @@
 
 		switch string(key) {
 		case "object":
-			id, err := oid.ParseHex(algo, string(value))
+			id, err := objectid.ParseHex(algo, string(value))
 			if err != nil {
 				return nil, fmt.Errorf("object: tag: object: %w", err)
 			}
--- a/object/tag_parse_test.go
+++ b/object/tag_parse_test.go
@@ -7,11 +7,11 @@
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	"codeberg.org/lindenii/furgit/object"
 	"codeberg.org/lindenii/furgit/objecttype"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 func TestTagParseFromGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		_, _, commitID := repo.MakeCommit(t, "subject\n\nbody")
 		tagID := repo.TagAnnotated(t, "v1", commitID, "tag message")
--- a/object/tag_serialize_test.go
+++ b/object/tag_serialize_test.go
@@ -5,11 +5,11 @@
 
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	"codeberg.org/lindenii/furgit/object"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 func TestTagSerialize(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		_, _, commitID := repo.MakeCommit(t, "subject\n\nbody")
 		tagID := repo.TagAnnotated(t, "v1", commitID, "tag message")
--- a/object/tree.go
+++ b/object/tree.go
@@ -6,7 +6,7 @@
 	"sort"
 
 	"codeberg.org/lindenii/furgit/objecttype"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // FileMode represents the mode of a file in a Git tree.
@@ -24,7 +24,7 @@
 type TreeEntry struct {
 	Mode FileMode
 	Name []byte
-	ID   oid.ObjectID
+	ID   objectid.ObjectID
 }
 
 // Tree represents a Git tree object.
--- a/object/tree_parse.go
+++ b/object/tree_parse.go
@@ -5,11 +5,11 @@
 	"fmt"
 	"strconv"
 
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 // ParseTree decodes a tree object body.
-func ParseTree(body []byte, algo oid.Algorithm) (*Tree, error) {
+func ParseTree(body []byte, algo objectid.Algorithm) (*Tree, error) {
 	var entries []TreeEntry
 	i := 0
 	for i < len(body) {
@@ -31,7 +31,7 @@
 		if idEnd > len(body) {
 			return nil, fmt.Errorf("object: tree: truncated child object id")
 		}
-		id, err := oid.FromBytes(algo, body[i:idEnd])
+		id, err := objectid.FromBytes(algo, body[i:idEnd])
 		if err != nil {
 			return nil, err
 		}
--- a/object/tree_parse_test.go
+++ b/object/tree_parse_test.go
@@ -6,11 +6,11 @@
 
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	"codeberg.org/lindenii/furgit/object"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 func TestTreeParseFromGit(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		entries := adversarialRootEntries(t, repo)
 		inserted := &object.Tree{}
--- a/object/tree_serialize_test.go
+++ b/object/tree_serialize_test.go
@@ -5,11 +5,11 @@
 
 	"codeberg.org/lindenii/furgit/internal/testgit"
 	"codeberg.org/lindenii/furgit/object"
-	"codeberg.org/lindenii/furgit/oid"
+	"codeberg.org/lindenii/furgit/objectid"
 )
 
 func TestTreeSerialize(t *testing.T) {
-	testgit.ForEachAlgorithm(t, func(t *testing.T, algo oid.Algorithm) {
+	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
 		repo := testgit.NewBareRepo(t, algo)
 		entries := adversarialRootEntries(t, repo)
 		tree := &object.Tree{}
--- /dev/null
+++ b/objectid/objectid.go
@@ -1,0 +1,186 @@
+// Package objectid provides object ID and algorithm primitives for Git objects.
+package objectid
+
+import (
+	"crypto/sha1"
+	"crypto/sha256"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"hash"
+)
+
+var (
+	// ErrInvalidAlgorithm indicates an unsupported object ID algorithm.
+	ErrInvalidAlgorithm = errors.New("objectid: invalid algorithm")
+	// ErrInvalidObjectID indicates malformed object ID data.
+	ErrInvalidObjectID = errors.New("objectid: invalid object id")
+)
+
+// maxObjectIDSize MUST be >= the largest supported algorithm size.
+const maxObjectIDSize = sha256.Size
+
+// Algorithm identifies the hash algorithm used for Git object IDs.
+type Algorithm uint8
+
+const (
+	AlgorithmUnknown Algorithm = iota
+	AlgorithmSHA1
+	AlgorithmSHA256
+)
+
+type algorithmDetails struct {
+	name string
+	size int
+	sum  func([]byte) ObjectID
+	new  func() hash.Hash
+}
+
+var algorithmTable = [...]algorithmDetails{
+	AlgorithmUnknown: {},
+	AlgorithmSHA1: {
+		name: "sha1",
+		size: sha1.Size,
+		sum: func(data []byte) ObjectID {
+			sum := sha1.Sum(data)
+			var id ObjectID
+			copy(id.data[:], sum[:])
+			id.algo = AlgorithmSHA1
+			return id
+		},
+		new: func() hash.Hash {
+			return sha1.New()
+		},
+	},
+	AlgorithmSHA256: {
+		name: "sha256",
+		size: sha256.Size,
+		sum: func(data []byte) ObjectID {
+			sum := sha256.Sum256(data)
+			var id ObjectID
+			copy(id.data[:], sum[:])
+			id.algo = AlgorithmSHA256
+			return id
+		},
+		new: func() hash.Hash {
+			return sha256.New()
+		},
+	},
+}
+
+var algorithmByName = map[string]Algorithm{}
+
+func init() {
+	for algo, info := range algorithmTable {
+		if info.name == "" {
+			continue
+		}
+		algorithmByName[info.name] = Algorithm(algo)
+	}
+}
+
+func (algo Algorithm) info() algorithmDetails {
+	return algorithmTable[algo]
+}
+
+// ParseAlgorithm parses a canonical algorithm name (e.g. "sha1", "sha256").
+func ParseAlgorithm(s string) (Algorithm, bool) {
+	algo, ok := algorithmByName[s]
+	return algo, ok
+}
+
+// Size returns the hash size in bytes.
+func (algo Algorithm) Size() int {
+	return algo.info().size
+}
+
+// String returns the canonical algorithm name.
+func (algo Algorithm) String() string {
+	inf := algo.info()
+	if inf.name == "" {
+		return "unknown"
+	}
+	return inf.name
+}
+
+// HexLen returns the encoded hexadecimal length.
+func (algo Algorithm) HexLen() int {
+	return algo.Size() * 2
+}
+
+// Sum computes an object ID from raw data using the selected algorithm.
+func (algo Algorithm) Sum(data []byte) ObjectID {
+	return algo.info().sum(data)
+}
+
+// New returns a new hash.Hash for this algorithm.
+func (algo Algorithm) New() (hash.Hash, error) {
+	newFn := algo.info().new
+	if newFn == nil {
+		return nil, ErrInvalidAlgorithm
+	}
+	return newFn(), nil
+}
+
+// ObjectID represents a Git object ID.
+type ObjectID struct {
+	algo Algorithm
+	data [maxObjectIDSize]byte
+}
+
+// Algorithm returns the object ID's hash algorithm.
+func (id ObjectID) Algorithm() Algorithm {
+	return id.algo
+}
+
+// Size returns the object ID size in bytes.
+func (id ObjectID) Size() int {
+	return id.algo.Size()
+}
+
+// String returns the canonical hex representation.
+func (id ObjectID) String() string {
+	size := id.Size()
+	return hex.EncodeToString(id.data[:size])
+}
+
+// Bytes returns a copy of the object ID bytes.
+func (id ObjectID) Bytes() []byte {
+	size := id.Size()
+	return append([]byte(nil), id.data[:size]...)
+}
+
+// ParseHex parses an object ID from hex for the specified algorithm.
+func ParseHex(algo Algorithm, s string) (ObjectID, error) {
+	var id ObjectID
+	if algo.Size() == 0 {
+		return id, ErrInvalidAlgorithm
+	}
+	if len(s)%2 != 0 {
+		return id, fmt.Errorf("%w: odd hex length %d", ErrInvalidObjectID, len(s))
+	}
+	if len(s) != algo.HexLen() {
+		return id, fmt.Errorf("%w: got %d chars, expected %d", ErrInvalidObjectID, len(s), algo.HexLen())
+	}
+	decoded, err := hex.DecodeString(s)
+	if err != nil {
+		return id, fmt.Errorf("%w: decode: %v", ErrInvalidObjectID, err)
+	}
+	copy(id.data[:], decoded)
+	id.algo = algo
+	return id, nil
+}
+
+// FromBytes builds an object ID from raw bytes for the specified algorithm.
+func FromBytes(algo Algorithm, b []byte) (ObjectID, error) {
+	var id ObjectID
+	if algo.Size() == 0 {
+		return id, ErrInvalidAlgorithm
+	}
+	if len(b) != algo.Size() {
+		return id, fmt.Errorf("%w: got %d bytes, expected %d", ErrInvalidObjectID, len(b), algo.Size())
+	}
+	copy(id.data[:], b)
+	id.algo = algo
+	return id, nil
+}
--- /dev/null
+++ b/objectid/objectid_test.go
@@ -1,0 +1,144 @@
+package objectid
+
+import (
+	"bytes"
+	"testing"
+)
+
+func TestParseAlgorithm(t *testing.T) {
+	t.Parallel()
+
+	algo, ok := ParseAlgorithm("sha1")
+	if !ok || algo != AlgorithmSHA1 {
+		t.Fatalf("ParseAlgorithm(sha1) = (%v,%v)", algo, ok)
+	}
+
+	algo, ok = ParseAlgorithm("sha256")
+	if !ok || algo != AlgorithmSHA256 {
+		t.Fatalf("ParseAlgorithm(sha256) = (%v,%v)", algo, ok)
+	}
+
+	if _, ok := ParseAlgorithm("md5"); ok {
+		t.Fatalf("ParseAlgorithm(md5) should fail")
+	}
+}
+
+func TestParseHexRoundtrip(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		name string
+		algo Algorithm
+		hex  string
+	}{
+		{
+			name: "sha1",
+			algo: AlgorithmSHA1,
+			hex:  "0123456789abcdef0123456789abcdef01234567",
+		},
+		{
+			name: "sha256",
+			algo: AlgorithmSHA256,
+			hex:  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			id, err := ParseHex(tt.algo, tt.hex)
+			if err != nil {
+				t.Fatalf("ParseHex failed: %v", err)
+			}
+			if got := id.String(); got != tt.hex {
+				t.Fatalf("String() = %q, want %q", got, tt.hex)
+			}
+			if got := id.Size(); got != tt.algo.Size() {
+				t.Fatalf("Size() = %d, want %d", got, tt.algo.Size())
+			}
+
+			raw := id.Bytes()
+			if len(raw) != tt.algo.Size() {
+				t.Fatalf("Bytes len = %d, want %d", len(raw), tt.algo.Size())
+			}
+
+			id2, err := FromBytes(tt.algo, raw)
+			if err != nil {
+				t.Fatalf("FromBytes failed: %v", err)
+			}
+			if id2.String() != tt.hex {
+				t.Fatalf("FromBytes roundtrip = %q, want %q", id2.String(), tt.hex)
+			}
+		})
+	}
+}
+
+func TestParseHexErrors(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		name string
+		algo Algorithm
+		hex  string
+	}{
+		{"unknown algo", AlgorithmUnknown, "00"},
+		{"odd len", AlgorithmSHA1, "0"},
+		{"wrong len", AlgorithmSHA1, "0123"},
+		{"invalid hex", AlgorithmSHA1, "zz23456789abcdef0123456789abcdef01234567"},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if _, err := ParseHex(tt.algo, tt.hex); err == nil {
+				t.Fatalf("expected ParseHex error")
+			}
+		})
+	}
+}
+
+func TestFromBytesErrors(t *testing.T) {
+	t.Parallel()
+
+	if _, err := FromBytes(AlgorithmUnknown, []byte{1, 2}); err == nil {
+		t.Fatalf("expected FromBytes unknown algo error")
+	}
+	if _, err := FromBytes(AlgorithmSHA1, []byte{1, 2}); err == nil {
+		t.Fatalf("expected FromBytes wrong size error")
+	}
+}
+
+func TestBytesReturnsCopy(t *testing.T) {
+	t.Parallel()
+
+	id, err := ParseHex(AlgorithmSHA1, "0123456789abcdef0123456789abcdef01234567")
+	if err != nil {
+		t.Fatalf("ParseHex failed: %v", err)
+	}
+
+	b1 := id.Bytes()
+	b2 := id.Bytes()
+	if !bytes.Equal(b1, b2) {
+		t.Fatalf("Bytes mismatch")
+	}
+	b1[0] ^= 0xff
+	if bytes.Equal(b1, b2) {
+		t.Fatalf("Bytes should return independent copies")
+	}
+}
+
+func TestAlgorithmSum(t *testing.T) {
+	t.Parallel()
+
+	id1 := AlgorithmSHA1.Sum([]byte("hello"))
+	if id1.Algorithm() != AlgorithmSHA1 || id1.Size() != AlgorithmSHA1.Size() {
+		t.Fatalf("sha1 sum produced invalid object id")
+	}
+
+	id2 := AlgorithmSHA256.Sum([]byte("hello"))
+	if id2.Algorithm() != AlgorithmSHA256 || id2.Size() != AlgorithmSHA256.Size() {
+		t.Fatalf("sha256 sum produced invalid object id")
+	}
+
+	if id1.String() == id2.String() {
+		t.Fatalf("sha1 and sha256 should differ")
+	}
+}
--- a/oid/objectid.go
+++ /dev/null
@@ -1,186 +1,0 @@
-// Package oid provides object ID and algorithm primitives for Git objects.
-package oid
-
-import (
-	"crypto/sha1"
-	"crypto/sha256"
-	"encoding/hex"
-	"errors"
-	"fmt"
-	"hash"
-)
-
-var (
-	// ErrInvalidAlgorithm indicates an unsupported object ID algorithm.
-	ErrInvalidAlgorithm = errors.New("oid: invalid algorithm")
-	// ErrInvalidObjectID indicates malformed object ID data.
-	ErrInvalidObjectID = errors.New("oid: invalid object id")
-)
-
-// maxObjectIDSize MUST be >= the largest supported algorithm size.
-const maxObjectIDSize = sha256.Size
-
-// Algorithm identifies the hash algorithm used for Git object IDs.
-type Algorithm uint8
-
-const (
-	AlgorithmUnknown Algorithm = iota
-	AlgorithmSHA1
-	AlgorithmSHA256
-)
-
-type algorithmDetails struct {
-	name string
-	size int
-	sum  func([]byte) ObjectID
-	new  func() hash.Hash
-}
-
-var algorithmTable = [...]algorithmDetails{
-	AlgorithmUnknown: {},
-	AlgorithmSHA1: {
-		name: "sha1",
-		size: sha1.Size,
-		sum: func(data []byte) ObjectID {
-			sum := sha1.Sum(data)
-			var id ObjectID
-			copy(id.data[:], sum[:])
-			id.algo = AlgorithmSHA1
-			return id
-		},
-		new: func() hash.Hash {
-			return sha1.New()
-		},
-	},
-	AlgorithmSHA256: {
-		name: "sha256",
-		size: sha256.Size,
-		sum: func(data []byte) ObjectID {
-			sum := sha256.Sum256(data)
-			var id ObjectID
-			copy(id.data[:], sum[:])
-			id.algo = AlgorithmSHA256
-			return id
-		},
-		new: func() hash.Hash {
-			return sha256.New()
-		},
-	},
-}
-
-var algorithmByName = map[string]Algorithm{}
-
-func init() {
-	for algo, info := range algorithmTable {
-		if info.name == "" {
-			continue
-		}
-		algorithmByName[info.name] = Algorithm(algo)
-	}
-}
-
-func (algo Algorithm) info() algorithmDetails {
-	return algorithmTable[algo]
-}
-
-// ParseAlgorithm parses a canonical algorithm name (e.g. "sha1", "sha256").
-func ParseAlgorithm(s string) (Algorithm, bool) {
-	algo, ok := algorithmByName[s]
-	return algo, ok
-}
-
-// Size returns the hash size in bytes.
-func (algo Algorithm) Size() int {
-	return algo.info().size
-}
-
-// String returns the canonical algorithm name.
-func (algo Algorithm) String() string {
-	inf := algo.info()
-	if inf.name == "" {
-		return "unknown"
-	}
-	return inf.name
-}
-
-// HexLen returns the encoded hexadecimal length.
-func (algo Algorithm) HexLen() int {
-	return algo.Size() * 2
-}
-
-// Sum computes an object ID from raw data using the selected algorithm.
-func (algo Algorithm) Sum(data []byte) ObjectID {
-	return algo.info().sum(data)
-}
-
-// New returns a new hash.Hash for this algorithm.
-func (algo Algorithm) New() (hash.Hash, error) {
-	newFn := algo.info().new
-	if newFn == nil {
-		return nil, ErrInvalidAlgorithm
-	}
-	return newFn(), nil
-}
-
-// ObjectID represents a Git object ID.
-type ObjectID struct {
-	algo Algorithm
-	data [maxObjectIDSize]byte
-}
-
-// Algorithm returns the object ID's hash algorithm.
-func (id ObjectID) Algorithm() Algorithm {
-	return id.algo
-}
-
-// Size returns the object ID size in bytes.
-func (id ObjectID) Size() int {
-	return id.algo.Size()
-}
-
-// String returns the canonical hex representation.
-func (id ObjectID) String() string {
-	size := id.Size()
-	return hex.EncodeToString(id.data[:size])
-}
-
-// Bytes returns a copy of the object ID bytes.
-func (id ObjectID) Bytes() []byte {
-	size := id.Size()
-	return append([]byte(nil), id.data[:size]...)
-}
-
-// ParseHex parses an object ID from hex for the specified algorithm.
-func ParseHex(algo Algorithm, s string) (ObjectID, error) {
-	var id ObjectID
-	if algo.Size() == 0 {
-		return id, ErrInvalidAlgorithm
-	}
-	if len(s)%2 != 0 {
-		return id, fmt.Errorf("%w: odd hex length %d", ErrInvalidObjectID, len(s))
-	}
-	if len(s) != algo.HexLen() {
-		return id, fmt.Errorf("%w: got %d chars, expected %d", ErrInvalidObjectID, len(s), algo.HexLen())
-	}
-	decoded, err := hex.DecodeString(s)
-	if err != nil {
-		return id, fmt.Errorf("%w: decode: %v", ErrInvalidObjectID, err)
-	}
-	copy(id.data[:], decoded)
-	id.algo = algo
-	return id, nil
-}
-
-// FromBytes builds an object ID from raw bytes for the specified algorithm.
-func FromBytes(algo Algorithm, b []byte) (ObjectID, error) {
-	var id ObjectID
-	if algo.Size() == 0 {
-		return id, ErrInvalidAlgorithm
-	}
-	if len(b) != algo.Size() {
-		return id, fmt.Errorf("%w: got %d bytes, expected %d", ErrInvalidObjectID, len(b), algo.Size())
-	}
-	copy(id.data[:], b)
-	id.algo = algo
-	return id, nil
-}
--- a/oid/objectid_test.go
+++ /dev/null
@@ -1,144 +1,0 @@
-package oid
-
-import (
-	"bytes"
-	"testing"
-)
-
-func TestParseAlgorithm(t *testing.T) {
-	t.Parallel()
-
-	algo, ok := ParseAlgorithm("sha1")
-	if !ok || algo != AlgorithmSHA1 {
-		t.Fatalf("ParseAlgorithm(sha1) = (%v,%v)", algo, ok)
-	}
-
-	algo, ok = ParseAlgorithm("sha256")
-	if !ok || algo != AlgorithmSHA256 {
-		t.Fatalf("ParseAlgorithm(sha256) = (%v,%v)", algo, ok)
-	}
-
-	if _, ok := ParseAlgorithm("md5"); ok {
-		t.Fatalf("ParseAlgorithm(md5) should fail")
-	}
-}
-
-func TestParseHexRoundtrip(t *testing.T) {
-	t.Parallel()
-
-	tests := []struct {
-		name string
-		algo Algorithm
-		hex  string
-	}{
-		{
-			name: "sha1",
-			algo: AlgorithmSHA1,
-			hex:  "0123456789abcdef0123456789abcdef01234567",
-		},
-		{
-			name: "sha256",
-			algo: AlgorithmSHA256,
-			hex:  "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			id, err := ParseHex(tt.algo, tt.hex)
-			if err != nil {
-				t.Fatalf("ParseHex failed: %v", err)
-			}
-			if got := id.String(); got != tt.hex {
-				t.Fatalf("String() = %q, want %q", got, tt.hex)
-			}
-			if got := id.Size(); got != tt.algo.Size() {
-				t.Fatalf("Size() = %d, want %d", got, tt.algo.Size())
-			}
-
-			raw := id.Bytes()
-			if len(raw) != tt.algo.Size() {
-				t.Fatalf("Bytes len = %d, want %d", len(raw), tt.algo.Size())
-			}
-
-			id2, err := FromBytes(tt.algo, raw)
-			if err != nil {
-				t.Fatalf("FromBytes failed: %v", err)
-			}
-			if id2.String() != tt.hex {
-				t.Fatalf("FromBytes roundtrip = %q, want %q", id2.String(), tt.hex)
-			}
-		})
-	}
-}
-
-func TestParseHexErrors(t *testing.T) {
-	t.Parallel()
-
-	tests := []struct {
-		name string
-		algo Algorithm
-		hex  string
-	}{
-		{"unknown algo", AlgorithmUnknown, "00"},
-		{"odd len", AlgorithmSHA1, "0"},
-		{"wrong len", AlgorithmSHA1, "0123"},
-		{"invalid hex", AlgorithmSHA1, "zz23456789abcdef0123456789abcdef01234567"},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if _, err := ParseHex(tt.algo, tt.hex); err == nil {
-				t.Fatalf("expected ParseHex error")
-			}
-		})
-	}
-}
-
-func TestFromBytesErrors(t *testing.T) {
-	t.Parallel()
-
-	if _, err := FromBytes(AlgorithmUnknown, []byte{1, 2}); err == nil {
-		t.Fatalf("expected FromBytes unknown algo error")
-	}
-	if _, err := FromBytes(AlgorithmSHA1, []byte{1, 2}); err == nil {
-		t.Fatalf("expected FromBytes wrong size error")
-	}
-}
-
-func TestBytesReturnsCopy(t *testing.T) {
-	t.Parallel()
-
-	id, err := ParseHex(AlgorithmSHA1, "0123456789abcdef0123456789abcdef01234567")
-	if err != nil {
-		t.Fatalf("ParseHex failed: %v", err)
-	}
-
-	b1 := id.Bytes()
-	b2 := id.Bytes()
-	if !bytes.Equal(b1, b2) {
-		t.Fatalf("Bytes mismatch")
-	}
-	b1[0] ^= 0xff
-	if bytes.Equal(b1, b2) {
-		t.Fatalf("Bytes should return independent copies")
-	}
-}
-
-func TestAlgorithmSum(t *testing.T) {
-	t.Parallel()
-
-	id1 := AlgorithmSHA1.Sum([]byte("hello"))
-	if id1.Algorithm() != AlgorithmSHA1 || id1.Size() != AlgorithmSHA1.Size() {
-		t.Fatalf("sha1 sum produced invalid object id")
-	}
-
-	id2 := AlgorithmSHA256.Sum([]byte("hello"))
-	if id2.Algorithm() != AlgorithmSHA256 || id2.Size() != AlgorithmSHA256.Size() {
-		t.Fatalf("sha256 sum produced invalid object id")
-	}
-
-	if id1.String() == id2.String() {
-		t.Fatalf("sha1 and sha256 should differ")
-	}
-}
--