ref: 27e8e288ded5056ad6e1a5e14f91ec9275ff1e29
parent: a3e71ee0bb60730df98502289b5aea0566f5b8e2
author: Runxi Yu <me@runxiyu.org>
date: Fri Feb 20 07:51:41 EST 2026
Revert "pack: basic packfile writing" This reverts commit e6919174cfb82e283ba7201a06067650dab3cb15.
--- a/pack_pack_write.go
+++ /dev/null
@@ -1,262 +1,0 @@
-package furgit
-
-import (
- "crypto/sha1"
- "crypto/sha256"
- "encoding/binary"
- "errors"
- "hash"
- "io"
-
- "codeberg.org/lindenii/furgit/internal/zlib"
-)
-
-// TODO
-var errPackDeltaUnimplemented = errors.New("furgit: pack: delta writing not implemented")-
-// packWriter writes a PACKv2 stream.
-type packWriter struct {- w io.Writer
- h hash.Hash
- algo hashAlgorithm
- objCount uint32
- wroteHeader bool
- bytesWritten uint64
-}
-
-func newPackWriter(w io.Writer, algo hashAlgorithm, objCount uint32) (*packWriter, error) {- if w == nil {- return nil, ErrInvalidObject
- }
- h, err := algo.New()
- if err != nil {- return nil, err
- }
- return &packWriter{- w: w,
- h: h,
- algo: algo,
- objCount: objCount,
- }, nil
-}
-
-func (pw *packWriter) writePacked(p []byte) error {- if len(p) == 0 {- return nil
- }
- n, err := pw.w.Write(p)
- if n > 0 {- _, _ = pw.h.Write(p[:n])
- pw.bytesWritten += uint64(n)
- }
- if err != nil {- return err
- }
- if n != len(p) {- return io.ErrShortWrite
- }
- return nil
-}
-
-func (pw *packWriter) WriteHeader() error {- if pw == nil || pw.wroteHeader {- return ErrInvalidObject
- }
- var hdr [12]byte
- binary.BigEndian.PutUint32(hdr[0:4], packMagic)
- binary.BigEndian.PutUint32(hdr[4:8], packVersion2)
- binary.BigEndian.PutUint32(hdr[8:12], pw.objCount)
- if err := pw.writePacked(hdr[:]); err != nil {- return err
- }
- pw.wroteHeader = true
- return nil
-}
-
-func (pw *packWriter) WriteObject(ty ObjectType, body []byte) error {- if pw == nil || !pw.wroteHeader {- return ErrInvalidObject
- }
- switch ty {- case ObjectTypeCommit, ObjectTypeTree, ObjectTypeBlob, ObjectTypeTag:
- // remember that go switches don't fallthrough lol
- default:
- return ErrInvalidObject
- }
- if body == nil {- body = []byte{}- }
-
- hdr, err := packHeaderEncode(ty, len(body))
- if err != nil {- return err
- }
- if err := pw.writePacked(hdr); err != nil {- return err
- }
-
- zw := zlib.NewWriter(&packHashWriter{pw: pw})- if _, err := zw.Write(body); err != nil {- _ = zw.Close()
- return err
- }
- return zw.Close()
-}
-
-func (pw *packWriter) WriteOfsDelta(baseOffset uint64, baseSize, resultSize int, delta []byte) error {- _ = baseOffset
- _ = baseSize
- _ = resultSize
- _ = delta
- return errPackDeltaUnimplemented
-}
-
-func (pw *packWriter) WriteRefDelta(base Hash, baseSize, resultSize int, delta []byte) error {- _ = base
- _ = baseSize
- _ = resultSize
- _ = delta
- return errPackDeltaUnimplemented
-}
-
-func (pw *packWriter) Close() (Hash, error) {- if pw == nil || !pw.wroteHeader {- return Hash{}, ErrInvalidObject- }
- sum := pw.h.Sum(nil)
- if _, err := pw.w.Write(sum); err != nil {- return Hash{}, err- }
- var out Hash
- copy(out.data[:], sum)
- out.algo = pw.algo
- return out, nil
-}
-
-type packHashWriter struct {- pw *packWriter
-}
-
-func (w *packHashWriter) Write(p []byte) (int, error) {- if w == nil || w.pw == nil {- return 0, ErrInvalidObject
- }
- if err := w.pw.writePacked(p); err != nil {- return 0, err
- }
- return len(p), nil
-}
-
-// packHeaderEncode encodes a pack object header (type + size).
-func packHeaderEncode(ty ObjectType, size int) ([]byte, error) {- if size < 0 {- return nil, ErrInvalidObject
- }
- var out [16]byte
- pos := 0
-
- b := byte(size & 0x0f)
- size >>= 4
- b |= byte(ty&0x07) << 4
- if size > 0 {- b |= 0x80
- }
- out[pos] = b
- pos++
-
- for size > 0 {- b = byte(size & 0x7f)
- size >>= 7
- if size > 0 {- b |= 0x80
- }
- out[pos] = b
- pos++
- }
-
- return out[:pos], nil
-}
-
-// packVarintEncode encodes a 7-bit varint.
-func packVarintEncode(size int) ([]byte, error) {- if size < 0 {- return nil, ErrInvalidObject
- }
- var out [16]byte
- pos := 0
- for {- b := byte(size & 0x7f)
- size >>= 7
- if size != 0 {- b |= 0x80
- }
- out[pos] = b
- pos++
- if size == 0 {- break
- }
- }
- return out[:pos], nil
-}
-
-// packOfsEncode encodes an ofs-delta distance.
-func packOfsEncode(dist uint64) ([]byte, error) {- if dist == 0 {- return nil, ErrInvalidObject
- }
- var out [16]byte
- pos := 0
- out[pos] = byte(dist & 0x7f)
- pos++
- dist >>= 7
- for dist != 0 {- b := byte((dist - 1) & 0x7f)
- out[pos] = b | 0x80
- pos++
- dist >>= 7
- }
- for i, j := 0, pos-1; i < j; i, j = i+1, j-1 {- out[i], out[j] = out[j], out[i]
- }
- return out[:pos], nil
-}
-
-// packWrite writes a pack stream for the provided object ids.
-func (repo *Repository) packWrite(w io.Writer, objects []Hash, opts packWriteOptions) (Hash, error) {- if repo == nil {- return Hash{}, ErrInvalidObject- }
- if opts.EnableDeltas || opts.EnableThinPack {- return Hash{}, errPackDeltaUnimplemented- }
- if len(objects) > int(^uint32(0)) {- return Hash{}, ErrInvalidObject- }
-
- pw, err := newPackWriter(w, repo.hashAlgo, uint32(len(objects)))
- if err != nil {- return Hash{}, err- }
- if err := pw.WriteHeader(); err != nil {- return Hash{}, err- }
-
- for _, id := range objects {- ty, body, err := repo.ReadObjectTypeRaw(id)
- if err != nil {- return Hash{}, err- }
- if err := pw.WriteObject(ty, body); err != nil {- return Hash{}, err- }
- }
-
- return pw.Close()
-}
-
-type packWriteOptions struct {- EnableDeltas bool
- EnableThinPack bool
- MinDeltaSavings int
- MaxDeltaDepth int
-}
--- a/pack_write_test.go
+++ /dev/null
@@ -1,226 +1,0 @@
-package furgit
-
-import (
- "bytes"
- "errors"
- "os"
- "path/filepath"
- "strings"
- "testing"
-)
-
-func TestPackHeaderEncodeParseRoundtrip(t *testing.T) {- cases := []struct {- ty ObjectType
- sizes []int
- }{- {ObjectTypeCommit, []int{0, 1, 15, 16, 127, 128, 1024, 1 << 20}},- {ObjectTypeTree, []int{0, 3, 31, 32, 255, 256, 4096}},- {ObjectTypeBlob, []int{0, 7, 63, 64, 511, 512, 99999}},- {ObjectTypeTag, []int{0, 2, 14, 15, 16, 127, 128}},- }
-
- for _, c := range cases {- for _, size := range c.sizes {- encoded, err := packHeaderEncode(c.ty, size)
- if err != nil {- t.Fatalf("packHeaderEncode(%v,%d) error: %v", c.ty, size, err)- }
- gotTy, gotSize, consumed, err := packHeaderParse(encoded)
- if err != nil {- t.Fatalf("packHeaderParse error: %v", err)- }
- if gotTy != c.ty || gotSize != size {- t.Fatalf("roundtrip mismatch: got (%v,%d), want (%v,%d)", gotTy, gotSize, c.ty, size)- }
- if consumed != len(encoded) {- t.Fatalf("consumed=%d, encoded=%d", consumed, len(encoded))- }
- }
- }
-}
-
-func TestPackVarintEncodeRoundtrip(t *testing.T) {- values := []int{0, 1, 2, 7, 8, 127, 128, 129, 255, 1024, 1 << 20}- for _, v := range values {- encoded, err := packVarintEncode(v)
- if err != nil {- t.Fatalf("packVarintEncode(%d) error: %v", v, err)- }
- pos := 0
- got, err := packVarintRead(encoded, &pos)
- if err != nil {- t.Fatalf("packVarintRead error: %v", err)- }
- if got != v {- t.Fatalf("roundtrip mismatch: got %d, want %d", got, v)- }
- if pos != len(encoded) {- t.Fatalf("pos=%d, encoded=%d", pos, len(encoded))- }
- }
-}
-
-func TestPackOfsEncodeRoundtrip(t *testing.T) {- values := []uint64{1, 2, 7, 8, 9, 0x7f, 0x80, 0x81, 0x1000, 0x12345}- for _, v := range values {- encoded, err := packOfsEncode(v)
- if err != nil {- t.Fatalf("packOfsEncode(%d) error: %v", v, err)- }
- dist, consumed, err := packDeltaReadOfsDistance(encoded)
- if err != nil {- t.Fatalf("packDeltaReadOfsDistance error: %v", err)- }
- if dist != v {- t.Fatalf("roundtrip mismatch: got %d, want %d", dist, v)- }
- if consumed != len(encoded) {- t.Fatalf("consumed=%d, encoded=%d", consumed, len(encoded))- }
- }
-}
-
-func TestPackWriteNoDeltas(t *testing.T) {- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- workDir, cleanupWork := setupWorkDir(t)
- defer cleanupWork()
-
- if err := os.WriteFile(filepath.Join(workDir, "file1.txt"), []byte("content1"), 0o644); err != nil {- t.Fatalf("failed to write file1.txt: %v", err)- }
- if err := os.WriteFile(filepath.Join(workDir, "file2.txt"), []byte("content2"), 0o644); err != nil {- t.Fatalf("failed to write file2.txt: %v", err)- }
-
- gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
- gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "Test commit")
- commitHash := gitCmd(t, repoPath, "rev-parse", "HEAD")
-
- commitBody := gitCatFile(t, repoPath, "commit", commitHash)
- lines := bytes.Split(commitBody, []byte{'\n'})- if len(lines) == 0 || !bytes.HasPrefix(lines[0], []byte("tree ")) {- t.Fatalf("commit missing tree header")- }
- treeHash := strings.TrimSpace(string(bytes.TrimPrefix(lines[0], []byte("tree "))))-
- lsTree := gitCmd(t, repoPath, "ls-tree", "-r", treeHash)
- var blobHashes []string
- for _, line := range strings.Split(lsTree, "\n") {- if line == "" {- continue
- }
- fields := strings.Fields(line)
- if len(fields) < 3 {- t.Fatalf("unexpected ls-tree line: %q", line)- }
- blobHashes = append(blobHashes, fields[2])
- }
-
- repo, err := OpenRepository(repoPath)
- if err != nil {- t.Fatalf("OpenRepository failed: %v", err)- }
- defer func() { _ = repo.Close() }()-
- var objects []Hash
- commitID, _ := repo.ParseHash(commitHash)
- objects = append(objects, commitID)
- treeID, _ := repo.ParseHash(treeHash)
- objects = append(objects, treeID)
- for _, bh := range blobHashes {- id, _ := repo.ParseHash(bh)
- objects = append(objects, id)
- }
- expectedOids := append([]string{commitHash, treeHash}, blobHashes...)-
- packDir := filepath.Join(repoPath, "objects", "pack")
- if err := os.MkdirAll(packDir, 0o755); err != nil {- t.Fatalf("failed to create pack dir: %v", err)- }
- pf, err := os.CreateTemp(packDir, "furgit-test-*.pack")
- if err != nil {- t.Fatalf("failed to create pack file: %v", err)- }
- packPath := pf.Name()
- idxPath := strings.TrimSuffix(packPath, ".pack") + ".idx"
- if _, err := repo.packWrite(pf, objects, packWriteOptions{}); err != nil {- _ = pf.Close()
- t.Fatalf("packWrite failed: %v", err)- }
- if err := pf.Close(); err != nil {- t.Fatalf("failed to close pack file: %v", err)- }
-
- defer func() {- _ = os.Remove(packPath)
- _ = os.Remove(idxPath)
- }()
-
- _ = gitCmd(t, repoPath, "index-pack", "-o", idxPath, packPath)
-
- verifyOut := gitCmd(t, repoPath, "verify-pack", "-v", idxPath)
- seen := make(map[string]struct{})- for _, line := range strings.Split(verifyOut, "\n") {- if strings.TrimSpace(line) == "" {- continue
- }
- if strings.HasPrefix(line, "chain length") || strings.HasPrefix(line, "non delta") {- continue
- }
- parts := strings.Fields(line)
- if len(parts) == 0 {- continue
- }
- seen[parts[0]] = struct{}{}- }
- for _, oid := range expectedOids {- if _, ok := seen[oid]; !ok {- t.Fatalf("verify-pack missing object %s", oid)- }
- }
-
- for _, oid := range expectedOids {- if err := removeLooseObject(repoPath, oid); err != nil {- t.Fatalf("remove loose object %s: %v", oid, err)- }
- }
- for _, oid := range expectedOids {- _ = gitCmd(t, repoPath, "cat-file", "-p", oid)
- }
-
- _ = gitCmd(t, repoPath, "fsck", "--full", "--strict")
-}
-
-func TestPackWriteDeltasUnimplemented(t *testing.T) {- repoPath, cleanup := setupTestRepo(t)
- defer cleanup()
-
- repo, err := OpenRepository(repoPath)
- if err != nil {- t.Fatalf("OpenRepository failed: %v", err)- }
- defer func() { _ = repo.Close() }()-
- buf := new(bytes.Buffer)
- _, err = repo.packWrite(buf, nil, packWriteOptions{EnableDeltas: true})- if !errors.Is(err, errPackDeltaUnimplemented) {- t.Fatalf("expected errPackDeltaUnimplemented, got %v", err)- }
-}
-
-func removeLooseObject(repoPath, oid string) error {- if len(oid) < 2 {- return ErrInvalidObject
- }
- path := filepath.Join(repoPath, "objects", oid[:2], oid[2:])
- if err := os.Remove(path); err != nil {- if os.IsNotExist(err) {- return nil
- }
- return err
- }
- return nil
-}
--
⑨