From f49c95662bda1f2c337dbe872644afa1ca5cbbec Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Fri, 20 Feb 2026 22:52:58 +0800 Subject: objectid: Rename from oid --- config/config_test.go | 16 +-- internal/testgit/algorithms.go | 12 +- internal/testgit/repo.go | 4 +- internal/testgit/repo_cat_file.go | 4 +- internal/testgit/repo_commit_tree.go | 6 +- internal/testgit/repo_hash_object.go | 6 +- internal/testgit/repo_make_commit.go | 4 +- internal/testgit/repo_make_single_file_tree.go | 4 +- internal/testgit/repo_mktree.go | 6 +- internal/testgit/repo_new.go | 8 +- internal/testgit/repo_properties.go | 4 +- internal/testgit/repo_rev_parse.go | 6 +- internal/testgit/repo_tag_annotated.go | 4 +- object/blob_parse_test.go | 4 +- object/blob_serialize_test.go | 4 +- object/commit.go | 6 +- object/commit_parse.go | 8 +- object/commit_parse_test.go | 4 +- object/commit_serialize_test.go | 4 +- object/parse.go | 6 +- object/tag.go | 4 +- object/tag_parse.go | 6 +- object/tag_parse_test.go | 4 +- object/tag_serialize_test.go | 4 +- object/tree.go | 4 +- object/tree_parse.go | 6 +- object/tree_parse_test.go | 4 +- object/tree_serialize_test.go | 4 +- objectid/objectid.go | 186 +++++++++++++++++++++++++ objectid/objectid_test.go | 144 +++++++++++++++++++ oid/objectid.go | 186 ------------------------- oid/objectid_test.go | 144 ------------------- 32 files changed, 408 insertions(+), 408 deletions(-) create mode 100644 objectid/objectid.go create mode 100644 objectid/objectid_test.go delete mode 100644 oid/objectid.go delete mode 100644 oid/objectid_test.go diff --git a/config/config_test.go b/config/config_test.go index d159a1dc..eadd4d86 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -7,7 +7,7 @@ import ( "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 gitConfigGet(t *testing.T, repo *testgit.TestRepo, key string) string { } 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 TestConfigAgainstGit(t *testing.T) { } 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 TestConfigSubsectionAgainstGit(t *testing.T) { } 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 TestConfigMultiValueAgainstGit(t *testing.T) { } 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 TestConfigCaseInsensitiveAgainstGit(t *testing.T) { } 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 TestConfigBooleanAgainstGit(t *testing.T) { } 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 TestConfigComplexValuesAgainstGit(t *testing.T) { } 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") diff --git a/internal/testgit/algorithms.go b/internal/testgit/algorithms.go index 833b846b..6d7a7565 100644 --- a/internal/testgit/algorithms.go +++ b/internal/testgit/algorithms.go @@ -3,19 +3,19 @@ package testgit 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) { diff --git a/internal/testgit/repo.go b/internal/testgit/repo.go index c2d8b088..7118aa7a 100644 --- 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 } diff --git a/internal/testgit/repo_cat_file.go b/internal/testgit/repo_cat_file.go index 7533e484..c905521f 100644 --- a/internal/testgit/repo_cat_file.go +++ b/internal/testgit/repo_cat_file.go @@ -3,11 +3,11 @@ package testgit 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()) } diff --git a/internal/testgit/repo_commit_tree.go b/internal/testgit/repo_commit_tree.go index b5f2f587..1b33b5c0 100644 --- a/internal/testgit/repo_commit_tree.go +++ b/internal/testgit/repo_commit_tree.go @@ -3,11 +3,11 @@ package testgit 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 @@ func (repo *TestRepo) CommitTree(tb testing.TB, tree oid.ObjectID, message strin } 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) } diff --git a/internal/testgit/repo_hash_object.go b/internal/testgit/repo_hash_object.go index 97a86be5..b21e1231 100644 --- a/internal/testgit/repo_hash_object.go +++ b/internal/testgit/repo_hash_object.go @@ -3,14 +3,14 @@ package testgit 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) } diff --git a/internal/testgit/repo_make_commit.go b/internal/testgit/repo_make_commit.go index a329ee8a..35619334 100644 --- a/internal/testgit/repo_make_commit.go +++ b/internal/testgit/repo_make_commit.go @@ -3,11 +3,11 @@ package testgit 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) diff --git a/internal/testgit/repo_make_single_file_tree.go b/internal/testgit/repo_make_single_file_tree.go index 69b2362e..a0ccce9b 100644 --- a/internal/testgit/repo_make_single_file_tree.go +++ b/internal/testgit/repo_make_single_file_tree.go @@ -4,11 +4,11 @@ import ( "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) diff --git a/internal/testgit/repo_mktree.go b/internal/testgit/repo_mktree.go index bd2785e2..0d3f0ea7 100644 --- a/internal/testgit/repo_mktree.go +++ b/internal/testgit/repo_mktree.go @@ -3,14 +3,14 @@ package testgit 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) } diff --git a/internal/testgit/repo_new.go b/internal/testgit/repo_new.go index 51277f78..308d8156 100644 --- a/internal/testgit/repo_new.go +++ b/internal/testgit/repo_new.go @@ -4,22 +4,22 @@ import ( "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) diff --git a/internal/testgit/repo_properties.go b/internal/testgit/repo_properties.go index 9a48a43b..a25c329c 100644 --- 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 @@ func (repo *TestRepo) Dir() string { } // 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 } diff --git a/internal/testgit/repo_rev_parse.go b/internal/testgit/repo_rev_parse.go index 7c3365a1..d545f97b 100644 --- a/internal/testgit/repo_rev_parse.go +++ b/internal/testgit/repo_rev_parse.go @@ -3,14 +3,14 @@ package testgit 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) } diff --git a/internal/testgit/repo_tag_annotated.go b/internal/testgit/repo_tag_annotated.go index 167aaa96..3db6cee9 100644 --- a/internal/testgit/repo_tag_annotated.go +++ b/internal/testgit/repo_tag_annotated.go @@ -4,11 +4,11 @@ import ( "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)) diff --git a/object/blob_parse_test.go b/object/blob_parse_test.go index 02c059ca..ad0f7ef3 100644 --- a/object/blob_parse_test.go +++ b/object/blob_parse_test.go @@ -6,11 +6,11 @@ import ( "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) diff --git a/object/blob_serialize_test.go b/object/blob_serialize_test.go index 79fbc1b9..07811365 100644 --- a/object/blob_serialize_test.go +++ b/object/blob_serialize_test.go @@ -5,11 +5,11 @@ import ( "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) diff --git a/object/commit.go b/object/commit.go index 92b7fcdc..50d94057 100644 --- a/object/commit.go +++ b/object/commit.go @@ -2,13 +2,13 @@ package object 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 diff --git a/object/commit_parse.go b/object/commit_parse.go index 9693b4da..2d207add 100644 --- a/object/commit_parse.go +++ b/object/commit_parse.go @@ -5,11 +5,11 @@ import ( "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 @@ func ParseCommit(body []byte, algo oid.Algorithm) (*Commit, error) { 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) } diff --git a/object/commit_parse_test.go b/object/commit_parse_test.go index 8d1f192d..e1bfb3d1 100644 --- a/object/commit_parse_test.go +++ b/object/commit_parse_test.go @@ -6,11 +6,11 @@ import ( "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") diff --git a/object/commit_serialize_test.go b/object/commit_serialize_test.go index 2061495d..5f4cf0cf 100644 --- a/object/commit_serialize_test.go +++ b/object/commit_serialize_test.go @@ -5,11 +5,11 @@ import ( "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") diff --git a/object/parse.go b/object/parse.go index c87f2713..4ab37ae6 100644 --- a/object/parse.go +++ b/object/parse.go @@ -5,11 +5,11 @@ import ( "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 @@ func ParseObjectWithoutHeader(ty objecttype.Type, body []byte, algo oid.Algorith } // 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") diff --git a/object/tag.go b/object/tag.go index 4e150ba2..1fac845b 100644 --- a/object/tag.go +++ b/object/tag.go @@ -2,12 +2,12 @@ package object 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 diff --git a/object/tag_parse.go b/object/tag_parse.go index 27dc998d..c991404f 100644 --- a/object/tag_parse.go +++ b/object/tag_parse.go @@ -6,11 +6,11 @@ import ( "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 @@ func ParseTag(body []byte, algo oid.Algorithm) (*Tag, error) { 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) } diff --git a/object/tag_parse_test.go b/object/tag_parse_test.go index b8628bb3..84d29069 100644 --- a/object/tag_parse_test.go +++ b/object/tag_parse_test.go @@ -7,11 +7,11 @@ import ( "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") diff --git a/object/tag_serialize_test.go b/object/tag_serialize_test.go index 4585bc16..435259b4 100644 --- a/object/tag_serialize_test.go +++ b/object/tag_serialize_test.go @@ -5,11 +5,11 @@ import ( "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") diff --git a/object/tree.go b/object/tree.go index a28bfcc9..eae34c04 100644 --- a/object/tree.go +++ b/object/tree.go @@ -6,7 +6,7 @@ import ( "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 @@ const ( type TreeEntry struct { Mode FileMode Name []byte - ID oid.ObjectID + ID objectid.ObjectID } // Tree represents a Git tree object. diff --git a/object/tree_parse.go b/object/tree_parse.go index b9063d75..37a2fa4b 100644 --- a/object/tree_parse.go +++ b/object/tree_parse.go @@ -5,11 +5,11 @@ import ( "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 @@ func ParseTree(body []byte, algo oid.Algorithm) (*Tree, error) { 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 } diff --git a/object/tree_parse_test.go b/object/tree_parse_test.go index bbe7c69b..09c396e3 100644 --- a/object/tree_parse_test.go +++ b/object/tree_parse_test.go @@ -6,11 +6,11 @@ import ( "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{} diff --git a/object/tree_serialize_test.go b/object/tree_serialize_test.go index 59d09ea1..462eff9d 100644 --- a/object/tree_serialize_test.go +++ b/object/tree_serialize_test.go @@ -5,11 +5,11 @@ import ( "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{} diff --git a/objectid/objectid.go b/objectid/objectid.go new file mode 100644 index 00000000..f97fd197 --- /dev/null +++ b/objectid/objectid.go @@ -0,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 +} diff --git a/objectid/objectid_test.go b/objectid/objectid_test.go new file mode 100644 index 00000000..2598a4ed --- /dev/null +++ b/objectid/objectid_test.go @@ -0,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") + } +} diff --git a/oid/objectid.go b/oid/objectid.go deleted file mode 100644 index 6507306e..00000000 --- a/oid/objectid.go +++ /dev/null @@ -1,186 +0,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 -} diff --git a/oid/objectid_test.go b/oid/objectid_test.go deleted file mode 100644 index e650da1e..00000000 --- a/oid/objectid_test.go +++ /dev/null @@ -1,144 +0,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") - } -} -- cgit v1.3.1-10-gc9f91