1
0
Fork 0
mirror of https://github.com/imjasonh/infinite-git synced 2026-07-08 09:05:06 +00:00
infinite-git/internal/generator/commit.go
Jason Hall db0c5c91a5
Update to use single hello.txt file instead of creating new files
Instead of creating a new file for each pull (pull_1.txt, pull_2.txt, etc.),
now updates a single hello.txt file with the pull number and timestamp
with nanosecond precision.

Changes:
- Modified commit generator to update hello.txt instead of creating new files
- Added hello.txt to initial repository commit
- Updated tests to verify hello.txt content instead of checking for new files
- Fixed test expectations to account for counter increment during clone

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-23 22:56:40 -04:00

183 lines
4.1 KiB
Go

package generator
import (
"fmt"
"sync/atomic"
"time"
"github.com/imjasonh/infinite-git/internal/object"
"github.com/imjasonh/infinite-git/internal/repo"
)
// Generator creates new commits on demand.
type Generator struct {
repo *repo.Repository
counter int64
}
// New creates a new commit generator.
func New(r *repo.Repository) *Generator {
return &Generator{
repo: r,
}
}
// GenerateCommit creates a new commit and updates the main branch.
func (g *Generator) GenerateCommit() (string, error) {
// Increment counter atomically
count := atomic.AddInt64(&g.counter, 1)
// Get current HEAD commit
refs, err := g.repo.GetRefs()
if err != nil {
return "", fmt.Errorf("getting refs: %w", err)
}
parentHash := refs["refs/heads/main"]
if parentHash == "" {
return "", fmt.Errorf("main branch not found")
}
// Read parent commit to get its tree
parentData, err := g.repo.ReadObject(parentHash)
if err != nil {
return "", fmt.Errorf("reading parent commit: %w", err)
}
// Parse parent commit to find tree hash
var parentTreeHash string
lines := splitLines(string(parentData))
for _, line := range lines {
if len(line) > 5 && line[:5] == "tree " {
parentTreeHash = line[5:]
break
}
}
// Read parent tree
parentTreeData, err := g.repo.ReadObject(parentTreeHash)
if err != nil {
return "", fmt.Errorf("reading parent tree: %w", err)
}
// Parse existing tree entries
existingEntries := parseTree(parentTreeData)
// Create updated hello.txt content with nanosecond precision
filename := "hello.txt"
content := fmt.Sprintf("Pull #%d\nTimestamp: %s\n", count, time.Now().Format("2006-01-02 15:04:05.999999999"))
// Create blob for updated file
blob := object.NewBlob([]byte(content))
blobHash, err := g.repo.WriteObject(blob)
if err != nil {
return "", fmt.Errorf("writing blob: %w", err)
}
// Create new tree with all existing entries, replacing hello.txt
tree := object.NewTree()
// Add existing entries, but skip hello.txt as we'll add the updated one
for _, entry := range existingEntries {
if entry.Name != filename {
tree.AddEntry(entry.Mode, entry.Name, entry.Hash)
}
}
// Add updated hello.txt
tree.AddEntry("100644", filename, blobHash)
treeHash, err := g.repo.WriteObject(tree)
if err != nil {
return "", fmt.Errorf("writing tree: %w", err)
}
// Create commit
commitMsg := fmt.Sprintf("Pull #%d at %s", count, time.Now().Format("2006-01-02 15:04:05"))
commit := object.NewCommit(
treeHash,
parentHash,
"Infinite Git <infinite@example.com>",
"Infinite Git <infinite@example.com>",
commitMsg,
)
commitHash, err := g.repo.WriteObject(commit)
if err != nil {
return "", fmt.Errorf("writing commit: %w", err)
}
// Update refs/heads/main
if err := g.repo.UpdateRef("refs/heads/main", commitHash); err != nil {
return "", fmt.Errorf("updating ref: %w", err)
}
return commitHash, nil
}
// GetCounter returns the current counter value.
func (g *Generator) GetCounter() int64 {
return atomic.LoadInt64(&g.counter)
}
// splitLines splits a string into lines.
func splitLines(s string) []string {
var lines []string
start := 0
for i, c := range s {
if c == '\n' {
lines = append(lines, s[start:i])
start = i + 1
}
}
if start < len(s) {
lines = append(lines, s[start:])
}
return lines
}
// parseTree parses tree object data into entries.
func parseTree(data []byte) []object.TreeEntry {
var entries []object.TreeEntry
i := 0
for i < len(data) {
// Find space (end of mode)
modeEnd := i
for modeEnd < len(data) && data[modeEnd] != ' ' {
modeEnd++
}
if modeEnd >= len(data) {
break
}
mode := string(data[i:modeEnd])
// Find null (end of name)
nameStart := modeEnd + 1
nameEnd := nameStart
for nameEnd < len(data) && data[nameEnd] != 0 {
nameEnd++
}
if nameEnd >= len(data) {
break
}
name := string(data[nameStart:nameEnd])
// Read 20-byte SHA-1
hashStart := nameEnd + 1
if hashStart+20 > len(data) {
break
}
hash := fmt.Sprintf("%x", data[hashStart:hashStart+20])
entries = append(entries, object.TreeEntry{
Mode: mode,
Name: name,
Hash: hash,
})
i = hashStart + 20
}
return entries
}