diff options
| author | 2026-04-02 06:23:30 +0000 | |
|---|---|---|
| committer | 2026-04-02 06:28:39 +0000 | |
| commit | a041d523de389b65b98a5373a8034041db2a8d83 (patch) | |
| tree | 7b423dc735f463be616045f2c3c2095a7737aca7 /object/tree | |
| parent | research: Add dynamic pack resources (diff) | |
| signature | No signature | |
*: Remove
Diffstat (limited to 'object/tree')
| -rw-r--r-- | object/tree/entry.go | 57 | ||||
| -rw-r--r-- | object/tree/helpers_test.go | 114 | ||||
| -rw-r--r-- | object/tree/insert.go | 24 | ||||
| -rw-r--r-- | object/tree/lookup.go | 18 | ||||
| -rw-r--r-- | object/tree/mode.go | 12 | ||||
| -rw-r--r-- | object/tree/mode_details.go | 10 | ||||
| -rw-r--r-- | object/tree/mode_has_same_type.go | 12 | ||||
| -rw-r--r-- | object/tree/mode_is_blob_like.go | 8 | ||||
| -rw-r--r-- | object/tree/mode_is_regular_file.go | 6 | ||||
| -rw-r--r-- | object/tree/mode_table.go | 24 | ||||
| -rw-r--r-- | object/tree/name.go | 51 | ||||
| -rw-r--r-- | object/tree/parse.go | 58 | ||||
| -rw-r--r-- | object/tree/parse_test.go | 109 | ||||
| -rw-r--r-- | object/tree/path_append.go | 14 | ||||
| -rw-r--r-- | object/tree/path_clone.go | 16 | ||||
| -rw-r--r-- | object/tree/path_prefix.go | 19 | ||||
| -rw-r--r-- | object/tree/path_split.go | 19 | ||||
| -rw-r--r-- | object/tree/remove.go | 22 | ||||
| -rw-r--r-- | object/tree/serialize.go | 55 | ||||
| -rw-r--r-- | object/tree/serialize_test.go | 73 | ||||
| -rw-r--r-- | object/tree/tree.go | 12 | ||||
| -rw-r--r-- | object/tree/type.go | 10 |
22 files changed, 0 insertions, 743 deletions
diff --git a/object/tree/entry.go b/object/tree/entry.go deleted file mode 100644 index b3089b74..00000000 --- a/object/tree/entry.go +++ /dev/null @@ -1,57 +0,0 @@ -package tree - -import ( - "bytes" - "slices" - - objectid "codeberg.org/lindenii/furgit/object/id" -) - -// TreeEntry represents a single entry in a tree. -type TreeEntry struct { - Mode FileMode - // Name is part of the tree ordering. Mutating it after insertion may break - // Tree ordering and lookup behavior. - Name []byte - ID objectid.ObjectID -} - -func (tree *Tree) entry(name []byte, searchIsTree bool) *TreeEntry { - index, ok := slices.BinarySearchFunc(tree.Entries, name, func(entry TreeEntry, name []byte) int { - return TreeEntryNameCompare(entry.Name, entry.Mode, name, searchIsTree) - }) - if !ok { - return nil - } - - entry := &tree.Entries[index] - if !bytes.Equal(entry.Name, name) { - return nil - } - - return entry -} - -func (tree *Tree) entryIndex(name []byte) (int, bool) { - index, ok := tree.entryIndexWithMode(name, true) - if ok { - return index, true - } - - return tree.entryIndexWithMode(name, false) -} - -func (tree *Tree) entryIndexWithMode(name []byte, searchIsTree bool) (int, bool) { - index, ok := slices.BinarySearchFunc(tree.Entries, name, func(entry TreeEntry, name []byte) int { - return TreeEntryNameCompare(entry.Name, entry.Mode, name, searchIsTree) - }) - if !ok { - return 0, false - } - - if !bytes.Equal(tree.Entries[index].Name, name) { - return 0, false - } - - return index, true -} diff --git a/object/tree/helpers_test.go b/object/tree/helpers_test.go deleted file mode 100644 index 3da92ce4..00000000 --- a/object/tree/helpers_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package tree_test - -import ( - "bytes" - "fmt" - "strings" - "testing" - - "codeberg.org/lindenii/furgit/internal/testgit" - "codeberg.org/lindenii/furgit/object/tree" -) - -func buildGitMktreeInput(entries []tree.TreeEntry) string { - var b strings.Builder - for _, e := range entries { - fmt.Fprintf(&b, "%o %s %s\t%s\n", e.Mode, mktreeTypeFromMode(e.Mode), e.ID.String(), e.Name) - } - - return b.String() -} - -func mktreeTypeFromMode(mode tree.FileMode) string { - switch mode { - case tree.FileModeDir: - return "tree" - case tree.FileModeRegular, tree.FileModeExecutable, tree.FileModeSymlink: - return "blob" - case tree.FileModeGitlink: - return "commit" - default: - return "" - } -} - -func gitLsTreeNames(out []byte) [][]byte { - if len(out) == 0 { - return nil - } - - parts := bytes.Split(out, []byte{0}) - if len(parts) > 0 && len(parts[len(parts)-1]) == 0 { - parts = parts[:len(parts)-1] - } - - names := make([][]byte, 0, len(parts)) - for _, name := range parts { - names = append(names, append([]byte(nil), name...)) - } - - return names -} - -func adversarialRootEntries(t *testing.T, testRepo *testgit.TestRepo) []tree.TreeEntry { - t.Helper() - - blobA := testRepo.HashObject(t, "blob", []byte("blob-A\n")) - blobB := testRepo.HashObject(t, "blob", []byte("blob-B\n")) - blobC := testRepo.HashObject(t, "blob", []byte("blob-C\n")) - - subDirA := testRepo.Mktree(t, - fmt.Sprintf("100644 blob %s\tnested-a.txt\n100755 blob %s\trun-a.sh\n", blobA.String(), blobB.String())) - subDirB := testRepo.Mktree(t, - fmt.Sprintf("100644 blob %s\tnested-b.txt\n100644 blob %s\tz-last\n", blobB.String(), blobC.String())) - subDirC := testRepo.Mktree(t, - fmt.Sprintf("120000 blob %s\tlink-c\n100644 blob %s\tchild\n", blobC.String(), blobA.String())) - subDirD := testRepo.Mktree(t, - fmt.Sprintf("100644 blob %s\tleaf\n", blobA.String())) - - return []tree.TreeEntry{ - {Mode: tree.FileModeRegular, Name: []byte("z"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("A"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("aa"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("a0"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("a-"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("a."), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("a_"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("a~"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("Z"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("0"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("9"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("00"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("这是一些非 ASCII 的字符"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("是新进入 Unicode 的字符"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("Emoji 👀"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("_"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("-dash"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("dot.file"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte(".hidden"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("CAPS"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("caps"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("mixCase"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("name with space"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("name-with-dash"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("name.with.dot"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("name_with_underscore"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("tilde~name"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("brace{name}"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("plus+name"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("equal=name"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("at@name"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("percent%name"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("caret^name"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("comma,name"), ID: blobA}, - {Mode: tree.FileModeRegular, Name: []byte("semi;name"), ID: blobB}, - {Mode: tree.FileModeRegular, Name: []byte("paren(name)"), ID: blobC}, - {Mode: tree.FileModeRegular, Name: []byte("bracket[name]"), ID: blobA}, - {Mode: tree.FileModeExecutable, Name: []byte("exec.sh"), ID: blobB}, - {Mode: tree.FileModeSymlink, Name: []byte("sym.link"), ID: blobC}, - {Mode: tree.FileModeDir, Name: []byte("dir"), ID: subDirA}, - {Mode: tree.FileModeDir, Name: []byte("dir0"), ID: subDirB}, - {Mode: tree.FileModeDir, Name: []byte("dir.space"), ID: subDirC}, - {Mode: tree.FileModeDir, Name: []byte("x"), ID: subDirD}, - } -} diff --git a/object/tree/insert.go b/object/tree/insert.go deleted file mode 100644 index 22bda74f..00000000 --- a/object/tree/insert.go +++ /dev/null @@ -1,24 +0,0 @@ -package tree - -import ( - "fmt" - "slices" -) - -// InsertEntry inserts a tree entry while preserving Git ordering. -// -// InsertEntry copies newEntry.Name. -func (tree *Tree) InsertEntry(newEntry TreeEntry) error { - if tree.entry(newEntry.Name, true) != nil || tree.entry(newEntry.Name, false) != nil { - return fmt.Errorf("object: tree: entry %q already exists", newEntry.Name) - } - - newEntry.Name = append([]byte(nil), newEntry.Name...) - - insertAt, _ := slices.BinarySearchFunc(tree.Entries, newEntry.Name, func(entry TreeEntry, name []byte) int { - return TreeEntryNameCompare(entry.Name, entry.Mode, name, newEntry.Mode == FileModeDir) - }) - tree.Entries = slices.Insert(tree.Entries, insertAt, newEntry) - - return nil -} diff --git a/object/tree/lookup.go b/object/tree/lookup.go deleted file mode 100644 index 249efd0f..00000000 --- a/object/tree/lookup.go +++ /dev/null @@ -1,18 +0,0 @@ -package tree - -// Entry looks up a tree entry by name. -// -// The returned pointer refers to storage within tree.Entries and must not be -// retained across InsertEntry or RemoveEntry calls. -func (tree *Tree) Entry(name []byte) *TreeEntry { - if len(tree.Entries) == 0 { - return nil - } - - index, ok := tree.entryIndex(name) - if !ok { - return nil - } - - return &tree.Entries[index] -} diff --git a/object/tree/mode.go b/object/tree/mode.go deleted file mode 100644 index b1cbc6bc..00000000 --- a/object/tree/mode.go +++ /dev/null @@ -1,12 +0,0 @@ -package tree - -// FileMode represents the mode of a file in a Git tree. -type FileMode uint32 - -const ( - FileModeDir FileMode = 0o40000 - FileModeRegular FileMode = 0o100644 - FileModeExecutable FileMode = 0o100755 - FileModeSymlink FileMode = 0o120000 - FileModeGitlink FileMode = 0o160000 -) diff --git a/object/tree/mode_details.go b/object/tree/mode_details.go deleted file mode 100644 index 9c34fd7c..00000000 --- a/object/tree/mode_details.go +++ /dev/null @@ -1,10 +0,0 @@ -package tree - -type fileModeDetails struct { - isBlobLike bool - isRegularFile bool -} - -func (mode FileMode) details() fileModeDetails { - return fileModeTable[mode] -} diff --git a/object/tree/mode_has_same_type.go b/object/tree/mode_has_same_type.go deleted file mode 100644 index a058cb9c..00000000 --- a/object/tree/mode_has_same_type.go +++ /dev/null @@ -1,12 +0,0 @@ -package tree - -// HasSameType reports whether mode and other describe the same tree entry kind. -// -// Regular files and executable files have the same type for diff-status purposes. -func (mode FileMode) HasSameType(other FileMode) bool { - if mode == other { - return true - } - - return mode.details().isRegularFile && other.details().isRegularFile -} diff --git a/object/tree/mode_is_blob_like.go b/object/tree/mode_is_blob_like.go deleted file mode 100644 index 3ec3a308..00000000 --- a/object/tree/mode_is_blob_like.go +++ /dev/null @@ -1,8 +0,0 @@ -package tree - -// IsBlobLike reports whether mode names one blob-like tree entry kind. -// -// Blob-like entries store blob object IDs as their targets. -func (mode FileMode) IsBlobLike() bool { - return mode.details().isBlobLike -} diff --git a/object/tree/mode_is_regular_file.go b/object/tree/mode_is_regular_file.go deleted file mode 100644 index 115395c0..00000000 --- a/object/tree/mode_is_regular_file.go +++ /dev/null @@ -1,6 +0,0 @@ -package tree - -// IsRegularFile reports whether mode names one regular-file variant. -func (mode FileMode) IsRegularFile() bool { - return mode.details().isRegularFile -} diff --git a/object/tree/mode_table.go b/object/tree/mode_table.go deleted file mode 100644 index 1695f270..00000000 --- a/object/tree/mode_table.go +++ /dev/null @@ -1,24 +0,0 @@ -package tree - -var fileModeTable = map[FileMode]fileModeDetails{ //nolint:gochecknoglobals - FileModeDir: { - isBlobLike: false, - isRegularFile: false, - }, - FileModeRegular: { - isBlobLike: true, - isRegularFile: true, - }, - FileModeExecutable: { - isBlobLike: true, - isRegularFile: true, - }, - FileModeSymlink: { - isBlobLike: true, - isRegularFile: false, - }, - FileModeGitlink: { - isBlobLike: false, - isRegularFile: false, - }, -} diff --git a/object/tree/name.go b/object/tree/name.go deleted file mode 100644 index 02af3292..00000000 --- a/object/tree/name.go +++ /dev/null @@ -1,51 +0,0 @@ -package tree - -// TreeEntryNameCompare compares names using Git tree ordering rules. -func TreeEntryNameCompare(entryName []byte, entryMode FileMode, searchName []byte, searchIsTree bool) int { - isEntryTree := entryMode == FileModeDir - - entryLen := len(entryName) - if isEntryTree { - entryLen++ - } - - searchLen := len(searchName) - if searchIsTree { - searchLen++ - } - - n := min(searchLen, entryLen) - - for i := range n { - var ec, sc byte - if i < len(entryName) { - ec = entryName[i] - } else { - ec = '/' - } - - if i < len(searchName) { - sc = searchName[i] - } else { - sc = '/' - } - - if ec < sc { - return -1 - } - - if ec > sc { - return 1 - } - } - - if entryLen < searchLen { - return -1 - } - - if entryLen > searchLen { - return 1 - } - - return 0 -} diff --git a/object/tree/parse.go b/object/tree/parse.go deleted file mode 100644 index bb874828..00000000 --- a/object/tree/parse.go +++ /dev/null @@ -1,58 +0,0 @@ -package tree - -import ( - "bytes" - "fmt" - "strconv" - - objectid "codeberg.org/lindenii/furgit/object/id" -) - -// Parse decodes a tree object body into a fully materialized Tree. -func Parse(body []byte, algo objectid.Algorithm) (*Tree, error) { - var entries []TreeEntry - - i := 0 - for i < len(body) { - space := bytes.IndexByte(body[i:], ' ') - if space < 0 { - return nil, fmt.Errorf("object: tree: missing mode terminator") - } - - modeBytes := body[i : i+space] - i += space + 1 - - nul := bytes.IndexByte(body[i:], 0) - if nul < 0 { - return nil, fmt.Errorf("object: tree: missing name terminator") - } - - nameBytes := body[i : i+nul] - i += nul + 1 - - idEnd := i + algo.Size() - if idEnd > len(body) { - return nil, fmt.Errorf("object: tree: truncated child object id") - } - - id, err := objectid.FromBytes(algo, body[i:idEnd]) - if err != nil { - return nil, err - } - - i = idEnd - - mode, err := strconv.ParseUint(string(modeBytes), 8, 32) - if err != nil { - return nil, fmt.Errorf("object: tree: parse mode: %w", err) - } - - entries = append(entries, TreeEntry{ - Mode: FileMode(mode), - Name: append([]byte(nil), nameBytes...), - ID: id, - }) - } - - return &Tree{Entries: entries}, nil -} diff --git a/object/tree/parse_test.go b/object/tree/parse_test.go deleted file mode 100644 index 2b98ede7..00000000 --- a/object/tree/parse_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package tree_test - -import ( - "bytes" - "testing" - - "codeberg.org/lindenii/furgit/internal/testgit" - objectid "codeberg.org/lindenii/furgit/object/id" - "codeberg.org/lindenii/furgit/object/tree" -) - -func TestTreeParseFromGit(t *testing.T) { - t.Parallel() - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true}) - entries := adversarialRootEntries(t, testRepo) - - inserted := &tree.Tree{} - for _, entry := range entries { - err := inserted.InsertEntry(entry) - if err != nil { - t.Fatalf("InsertEntry(%q): %v", entry.Name, err) - } - } - - treeID := testRepo.Mktree(t, buildGitMktreeInput(inserted.Entries)) - - rawBody := testRepo.CatFile(t, "tree", treeID) - - parsed, err := tree.Parse(rawBody, algo) - if err != nil { - t.Fatalf("ParseTree: %v", err) - } - - if len(parsed.Entries) != len(inserted.Entries) { - t.Fatalf("entry count = %d, want %d", len(parsed.Entries), len(inserted.Entries)) - } - - for i := range inserted.Entries { - got := parsed.Entries[i] - - want := inserted.Entries[i] - if got.Mode != want.Mode || got.ID != want.ID || !bytes.Equal(got.Name, want.Name) { - t.Fatalf("entry[%d] mismatch: got (%o,%q,%s) want (%o,%q,%s)", - i, got.Mode, got.Name, got.ID, want.Mode, want.Name, want.ID) - } - } - - lsNames := gitLsTreeNames(testRepo.RunBytes(t, "ls-tree", "--name-only", "-z", treeID.String())) - if len(lsNames) != len(parsed.Entries) { - t.Fatalf("ls-tree names = %d, want %d", len(lsNames), len(parsed.Entries)) - } - - for i := range lsNames { - if !bytes.Equal(lsNames[i], parsed.Entries[i].Name) { - t.Fatalf("ordering mismatch at %d: git=%q parsed=%q", i, lsNames[i], parsed.Entries[i].Name) - } - } - - for _, want := range inserted.Entries { - got := parsed.Entry(want.Name) - - if got == nil { - t.Fatalf("Entry(%q) returned nil", want.Name) - } - - if got.Mode != want.Mode || got.ID != want.ID { - t.Fatalf("Entry(%q) mismatch", want.Name) - } - } - - if parsed.Entry([]byte("does-not-exist")) != nil { - t.Fatalf("Entry on missing name should be nil") - } - }) -} - -func TestTreeInsertEntryCopiesName(t *testing.T) { - t.Parallel() - - var tr tree.Tree - - name := []byte("alpha") - entry := tree.TreeEntry{ - Mode: tree.FileModeRegular, - Name: name, - ID: objectid.ObjectID{}, - } - - err := tr.InsertEntry(entry) - if err != nil { - t.Fatalf("InsertEntry: %v", err) - } - - name[0] = 'b' - - got := tr.Entry([]byte("alpha")) - if got == nil { - t.Fatalf("Entry(alpha) returned nil") - } - - if !bytes.Equal(got.Name, []byte("alpha")) { - t.Fatalf("stored name = %q, want %q", got.Name, []byte("alpha")) - } - - if tr.Entry([]byte("blpha")) != nil { - t.Fatalf("mutating caller name should not affect stored entry") - } -} diff --git a/object/tree/path_append.go b/object/tree/path_append.go deleted file mode 100644 index 609d5279..00000000 --- a/object/tree/path_append.go +++ /dev/null @@ -1,14 +0,0 @@ -package tree - -// AppendPath appends path to dst as one slash-separated byte path. -func AppendPath(dst []byte, path [][]byte) []byte { - for i := range path { - if i > 0 { - dst = append(dst, '/') - } - - dst = append(dst, path[i]...) - } - - return dst -} diff --git a/object/tree/path_clone.go b/object/tree/path_clone.go deleted file mode 100644 index a4668add..00000000 --- a/object/tree/path_clone.go +++ /dev/null @@ -1,16 +0,0 @@ -package tree - -import ( - "bytes" - "slices" -) - -// ClonePath returns one deep copy of path. -func ClonePath(path [][]byte) [][]byte { - cloned := slices.Clone(path) - for i := range cloned { - cloned[i] = bytes.Clone(cloned[i]) - } - - return cloned -} diff --git a/object/tree/path_prefix.go b/object/tree/path_prefix.go deleted file mode 100644 index ed658cee..00000000 --- a/object/tree/path_prefix.go +++ /dev/null @@ -1,19 +0,0 @@ -package tree - -import ( - "bytes" - "slices" -) - -// HasPathPrefix reports whether path begins with prefix as whole components. -func HasPathPrefix(path, prefix [][]byte) bool { - if len(prefix) == 0 { - return true - } - - if len(path) < len(prefix) { - return false - } - - return slices.EqualFunc(path[:len(prefix)], prefix, bytes.Equal) -} diff --git a/object/tree/path_split.go b/object/tree/path_split.go deleted file mode 100644 index c147dd25..00000000 --- a/object/tree/path_split.go +++ /dev/null @@ -1,19 +0,0 @@ -package tree - -import ( - "bytes" -) - -// SplitPath splits one slash-separated tree path into components. -func SplitPath(path []byte) [][]byte { - if len(path) == 0 { - return nil - } - - parts := bytes.Split(path, []byte{'/'}) - for i := range parts { - parts[i] = bytes.Clone(parts[i]) - } - - return parts -} diff --git a/object/tree/remove.go b/object/tree/remove.go deleted file mode 100644 index 94de88da..00000000 --- a/object/tree/remove.go +++ /dev/null @@ -1,22 +0,0 @@ -package tree - -import ( - "fmt" - "slices" -) - -// RemoveEntry removes a tree entry by name. -func (tree *Tree) RemoveEntry(name []byte) error { - if len(tree.Entries) == 0 { - return fmt.Errorf("object: tree: entry %q not found", name) - } - - index, ok := tree.entryIndex(name) - if !ok { - return fmt.Errorf("object: tree: entry %q not found", name) - } - - tree.Entries = slices.Delete(tree.Entries, index, index+1) - - return nil -} diff --git a/object/tree/serialize.go b/object/tree/serialize.go deleted file mode 100644 index 69deacda..00000000 --- a/object/tree/serialize.go +++ /dev/null @@ -1,55 +0,0 @@ -package tree - -import ( - "errors" - "strconv" - - objectheader "codeberg.org/lindenii/furgit/object/header" - objecttype "codeberg.org/lindenii/furgit/object/type" -) - -// SerializeWithoutHeader renders the raw tree body bytes. -func (tree *Tree) SerializeWithoutHeader() ([]byte, error) { - var bodyLen int - - for _, entry := range tree.Entries { - mode := strconv.FormatUint(uint64(entry.Mode), 8) - bodyLen += len(mode) + 1 + len(entry.Name) + 1 + entry.ID.Algorithm().Size() - } - - body := make([]byte, bodyLen) - pos := 0 - - for _, entry := range tree.Entries { - mode := strconv.FormatUint(uint64(entry.Mode), 8) - pos += copy(body[pos:], mode) - body[pos] = ' ' - pos++ - pos += copy(body[pos:], entry.Name) - body[pos] = 0 - pos++ - id := entry.ID.Bytes() - pos += copy(body[pos:], id) - } - - return body, nil -} - -// SerializeWithHeader renders the raw object (header + body). -func (tree *Tree) SerializeWithHeader() ([]byte, error) { - body, err := tree.SerializeWithoutHeader() - if err != nil { - return nil, err - } - - header, ok := objectheader.Encode(objecttype.TypeTree, int64(len(body))) - if !ok { - return nil, errors.New("object: tree: failed to encode object header") - } - - raw := make([]byte, len(header)+len(body)) - copy(raw, header) - copy(raw[len(header):], body) - - return raw, nil -} diff --git a/object/tree/serialize_test.go b/object/tree/serialize_test.go deleted file mode 100644 index 9c9a2f1c..00000000 --- a/object/tree/serialize_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package tree_test - -import ( - "testing" - - "codeberg.org/lindenii/furgit/internal/testgit" - objectid "codeberg.org/lindenii/furgit/object/id" - "codeberg.org/lindenii/furgit/object/tree" -) - -func TestTreeSerialize(t *testing.T) { - t.Parallel() - testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper - testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true}) - entries := adversarialRootEntries(t, testRepo) - obj := &tree.Tree{} - - for i := len(entries) - 1; i >= 0; i-- { - err := obj.InsertEntry(entries[i]) - if err != nil { - t.Fatalf("InsertEntry(%q): %v", entries[i].Name, err) - } - } - - if len(obj.Entries) < 32 { - t.Fatalf("expected at least 32 entries, got %d", len(obj.Entries)) - } - - dup := obj.Entries[0] - - err := obj.InsertEntry(dup) - if err == nil { - t.Fatalf("duplicate InsertEntry should fail") - } - - removed := obj.Entries[len(obj.Entries)/2] - - err = obj.RemoveEntry(removed.Name) - if err != nil { - t.Fatalf("RemoveEntry(%q): %v", removed.Name, err) - } - - if obj.Entry(removed.Name) != nil { - t.Fatalf("Entry(%q) should be nil after remove", removed.Name) - } - - err = obj.RemoveEntry([]byte("no-such-entry")) - if err == nil { - t.Fatalf("RemoveEntry missing entry should fail") - } - - err = obj.InsertEntry(removed) - if err != nil { - t.Fatalf("re-InsertEntry(%q): %v", removed.Name, err) - } - - if obj.Entry(removed.Name) == nil { - t.Fatalf("Entry(%q) should exist after reinsert", removed.Name) - } - - wantTreeID := testRepo.Mktree(t, buildGitMktreeInput(obj.Entries)) - - rawObj, err := obj.SerializeWithHeader() - if err != nil { - t.Fatalf("SerializeWithHeader: %v", err) - } - - gotTreeID := algo.Sum(rawObj) - if gotTreeID != wantTreeID { - t.Fatalf("tree id mismatch: got %s want %s", gotTreeID, wantTreeID) - } - }) -} diff --git a/object/tree/tree.go b/object/tree/tree.go deleted file mode 100644 index d0c7f4f0..00000000 --- a/object/tree/tree.go +++ /dev/null @@ -1,12 +0,0 @@ -// Package tree provides representations, parsers, and serializers for tree objects. -package tree - -// Tree represents a fully materialized Git tree object. -// -// Labels: MT-Unsafe. -type Tree struct { - // Entries must be sorted by TreeEntryNameCompare. - // Use the Tree methods to preserve ordering and copy semantics rather than - // modifying the slice directly. - Entries []TreeEntry -} diff --git a/object/tree/type.go b/object/tree/type.go deleted file mode 100644 index 416544af..00000000 --- a/object/tree/type.go +++ /dev/null @@ -1,10 +0,0 @@ -package tree - -import objecttype "codeberg.org/lindenii/furgit/object/type" - -// ObjectType returns TypeTree. -func (tree *Tree) ObjectType() objecttype.Type { - _ = tree - - return objecttype.TypeTree -} |
