1
0
Fork 0
mirror of https://github.com/imjasonh/infinite-git synced 2026-07-09 09:29:09 +00:00
infinite-git/internal/object/object.go
Jason Hall 30ca57dae3
Implement infinite Git HTTP server
This server generates a new commit every time someone pulls from it.
Features:
- Pure Go implementation of Git object format and protocols
- No dependency on git CLI commands
- Thread-safe commit generation
- Read-only Git HTTP smart protocol support
- Comprehensive test suite

Each pull creates a new commit with a unique file containing a timestamp
and counter, making the repository grow infinitely with each pull operation.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-23 21:19:02 -04:00

136 lines
2.9 KiB
Go

package object
import (
"bytes"
"compress/zlib"
"crypto/sha1"
"fmt"
"io"
"os"
"path/filepath"
)
// Type represents a Git object type.
type Type string
const (
TypeBlob Type = "blob"
TypeTree Type = "tree"
TypeCommit Type = "commit"
)
// Object represents a Git object.
type Object interface {
Type() Type
Serialize() []byte
}
// Hash computes the SHA-1 hash of an object.
func Hash(obj Object) string {
data := obj.Serialize()
header := fmt.Sprintf("%s %d\x00", obj.Type(), len(data))
h := sha1.New()
h.Write([]byte(header))
h.Write(data)
return fmt.Sprintf("%x", h.Sum(nil))
}
// Write writes an object to the Git object store.
func Write(gitDir string, obj Object) (string, error) {
// Compute hash
hash := Hash(obj)
// Prepare object data
data := obj.Serialize()
header := fmt.Sprintf("%s %d\x00", obj.Type(), len(data))
// Create object directory
objDir := filepath.Join(gitDir, "objects", hash[:2])
if err := os.MkdirAll(objDir, 0755); err != nil {
return "", fmt.Errorf("creating object dir: %w", err)
}
// Write compressed object
objPath := filepath.Join(objDir, hash[2:])
file, err := os.Create(objPath)
if err != nil {
return "", fmt.Errorf("creating object file: %w", err)
}
defer file.Close()
// Compress with zlib
w := zlib.NewWriter(file)
defer w.Close()
if _, err := w.Write([]byte(header)); err != nil {
return "", fmt.Errorf("writing header: %w", err)
}
if _, err := w.Write(data); err != nil {
return "", fmt.Errorf("writing data: %w", err)
}
if err := w.Close(); err != nil {
return "", fmt.Errorf("closing zlib writer: %w", err)
}
return hash, nil
}
// ReadFull reads an object from the Git object store with its header.
func ReadFull(gitDir string, hash string) ([]byte, error) {
objPath := filepath.Join(gitDir, "objects", hash[:2], hash[2:])
file, err := os.Open(objPath)
if err != nil {
return nil, fmt.Errorf("opening object file: %w", err)
}
defer file.Close()
// Decompress
r, err := zlib.NewReader(file)
if err != nil {
return nil, fmt.Errorf("creating zlib reader: %w", err)
}
defer r.Close()
data, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("reading object: %w", err)
}
return data, nil
}
// Read reads an object from the Git object store.
func Read(gitDir string, hash string) ([]byte, error) {
objPath := filepath.Join(gitDir, "objects", hash[:2], hash[2:])
file, err := os.Open(objPath)
if err != nil {
return nil, fmt.Errorf("opening object file: %w", err)
}
defer file.Close()
// Decompress
r, err := zlib.NewReader(file)
if err != nil {
return nil, fmt.Errorf("creating zlib reader: %w", err)
}
defer r.Close()
data, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("reading object: %w", err)
}
// Parse header
nullIndex := bytes.IndexByte(data, 0)
if nullIndex == -1 {
return nil, fmt.Errorf("invalid object format: no null byte")
}
// Return content after header
return data[nullIndex+1:], nil
}