mirror of
https://github.com/imjasonh/infinite-git
synced 2026-07-17 22:34:49 +00:00
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>
This commit is contained in:
parent
fefad053a8
commit
30ca57dae3
19 changed files with 2172 additions and 0 deletions
136
internal/object/object.go
Normal file
136
internal/object/object.go
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue