ref: d11150df176016acb5a690c0214889c0e5bb696d
dir: /hybrid_test.go/
package furgit
import (
"bytes"
"fmt"
"os"
"path/filepath"
"testing"
)
func TestTreeNestedDeep(t *testing.T) {
repoPath, cleanup := setupTestRepo(t)
defer cleanup()
workDir, cleanupWork := setupWorkDir(t)
defer cleanupWork()
depth := 50
currentDir := workDir
for i := 0; i < depth; i++ {
currentDir = filepath.Join(currentDir, fmt.Sprintf("level%d", i))
err := os.MkdirAll(currentDir, 0o755)
if err != nil {
t.Fatalf("failed to create directory %s: %v", currentDir, err)
}
}
err := os.WriteFile(filepath.Join(currentDir, "deep.txt"), []byte("deep content"), 0o644)
if err != nil {
t.Fatalf("failed to create deep.txt: %v", err)
}
gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
treeHash := gitCmd(t, repoPath, "--work-tree="+workDir, "write-tree")
repo, err := OpenRepository(repoPath)
if err != nil {
t.Fatalf("OpenRepository failed: %v", err)
}
defer func() {
_ = repo.Close()
}()
hash, _ := repo.ParseHash(treeHash)
obj, _ := repo.ReadObject(hash)
tree := obj.(*StoredTree)
path := make([][]byte, depth+1)
for i := 0; i < depth; i++ {
path[i] = []byte(fmt.Sprintf("level%d", i))
}
path[depth] = []byte("deep.txt")
entry, err := tree.EntryRecursive(repo, path)
if err != nil {
t.Fatalf("EntryRecursive failed for deep path: %v", err)
}
blobObj, _ := repo.ReadObject(entry.ID)
blob := blobObj.(*StoredBlob)
if !bytes.Equal(blob.Data, []byte("deep content")) {
t.Errorf("deep file content: got %q, want %q", blob.Data, "deep content")
}
}
func TestTreeMixedModes(t *testing.T) {
repoPath, cleanup := setupTestRepo(t)
defer cleanup()
workDir, cleanupWork := setupWorkDir(t)
defer cleanupWork()
err := os.WriteFile(filepath.Join(workDir, "normal.txt"), []byte("normal"), 0o644)
if err != nil {
t.Fatalf("failed to create normal.txt: %v", err)
}
err = os.WriteFile(filepath.Join(workDir, "executable.sh"), []byte("#!/bin/sh\necho test"), 0o755)
if err != nil {
t.Fatalf("failed to create executable.sh: %v", err)
}
err = os.Symlink("normal.txt", filepath.Join(workDir, "link.txt"))
if err != nil {
t.Fatalf("failed to create symlink: %v", err)
}
gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
treeHash := gitCmd(t, repoPath, "--work-tree="+workDir, "write-tree")
repo, err := OpenRepository(repoPath)
if err != nil {
t.Fatalf("OpenRepository failed: %v", err)
}
defer func() {
_ = repo.Close()
}()
hash, _ := repo.ParseHash(treeHash)
obj, _ := repo.ReadObject(hash)
tree := obj.(*StoredTree)
modes := make(map[string]FileMode)
for _, entry := range tree.Entries {
modes[string(entry.Name)] = entry.Mode
}
if modes["normal.txt"] != 0o100644 {
t.Errorf("normal.txt mode: got %o, want %o", modes["normal.txt"], 0o100644)
}
if modes["executable.sh"] != 0o100755 {
t.Errorf("executable.sh mode: got %o, want %o", modes["executable.sh"], 0o100755)
}
if modes["link.txt"] != 0o120000 {
t.Errorf("link.txt mode: got %o, want %o", modes["link.txt"], 0o120000)
}
}
func TestCommitChain(t *testing.T) {
repoPath, cleanup := setupTestRepo(t)
defer cleanup()
workDir, cleanupWork := setupWorkDir(t)
defer cleanupWork()
numCommits := 100
var commits []string
for i := 0; i < numCommits; i++ {
filename := filepath.Join(workDir, fmt.Sprintf("file%d.txt", i))
err := os.WriteFile(filename, []byte(fmt.Sprintf("content %d", i)), 0o644)
if err != nil {
t.Fatalf("failed to create %s: %v", filename, err)
}
gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", fmt.Sprintf("Commit %d", i))
commitHash := gitCmd(t, repoPath, "rev-parse", "HEAD")
commits = append(commits, commitHash)
}
repo, err := OpenRepository(repoPath)
if err != nil {
t.Fatalf("OpenRepository failed: %v", err)
}
defer func() {
_ = repo.Close()
}()
hash, _ := repo.ParseHash(commits[len(commits)-1])
for i := numCommits - 1; i >= 0; i-- {
obj, err := repo.ReadObject(hash)
if err != nil {
t.Fatalf("failed to read commit %d: %v", i, err)
}
commit, ok := obj.(*StoredCommit)
if !ok {
t.Fatalf("expected *StoredCommit at %d, got %T", i, obj)
}
expectedMsg := fmt.Sprintf("Commit %d\n", i)
if !bytes.Equal(commit.Message, []byte(expectedMsg)) {
t.Errorf("commit %d message: got %q, want %q", i, commit.Message, expectedMsg)
}
if i > 0 {
if len(commit.Parents) != 1 {
t.Fatalf("commit %d should have 1 parent, got %d", i, len(commit.Parents))
}
hash = commit.Parents[0]
} else {
if len(commit.Parents) != 0 {
t.Errorf("first commit should have 0 parents, got %d", len(commit.Parents))
}
}
}
}
func TestMultipleTags(t *testing.T) {
repoPath, cleanup := setupTestRepo(t)
defer cleanup()
workDir, cleanupWork := setupWorkDir(t)
defer cleanupWork()
err := os.WriteFile(filepath.Join(workDir, "file.txt"), []byte("content"), 0o644)
if err != nil {
t.Fatalf("failed to create file.txt: %v", err)
}
gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", "Tagged commit")
commitHash := gitCmd(t, repoPath, "rev-parse", "HEAD")
tags := []string{"v1.0.0", "v1.0.1", "v1.1.0", "v2.0.0"}
for _, tagName := range tags {
gitCmd(t, repoPath, "tag", "-a", "-m", fmt.Sprintf("Release %s", tagName), tagName, commitHash)
}
repo, err := OpenRepository(repoPath)
if err != nil {
t.Fatalf("OpenRepository failed: %v", err)
}
defer func() {
_ = repo.Close()
}()
for _, tagName := range tags {
tagHash := gitCmd(t, repoPath, "rev-parse", tagName)
hash, _ := repo.ParseHash(tagHash)
obj, err := repo.ReadObject(hash)
if err != nil {
t.Errorf("failed to read tag %s: %v", tagName, err)
continue
}
tag, ok := obj.(*StoredTag)
if !ok {
t.Errorf("tag %s: expected *StoredTag, got %T", tagName, obj)
continue
}
if !bytes.Equal(tag.Name, []byte(tagName)) {
t.Errorf("tag name: got %q, want %q", tag.Name, tagName)
}
}
}
func TestPackfileAfterMultipleRepacks(t *testing.T) {
if testing.Short() {
t.Skip("skipping multiple repack test in short mode")
}
repoPath, cleanup := setupTestRepo(t)
defer cleanup()
gitCmd(t, repoPath, "config", "gc.auto", "0")
workDir, cleanupWork := setupWorkDir(t)
defer cleanupWork()
for i := 0; i < 5; i++ {
err := os.WriteFile(filepath.Join(workDir, fmt.Sprintf("file%d.txt", i)), []byte(fmt.Sprintf("content %d", i)), 0o644)
if err != nil {
t.Fatalf("failed to create file%d.txt: %v", i, err)
}
gitCmd(t, repoPath, "--work-tree="+workDir, "add", ".")
gitCmd(t, repoPath, "--work-tree="+workDir, "commit", "-m", fmt.Sprintf("Commit %d", i))
gitCmd(t, repoPath, "repack", "-d")
}
gitCmd(t, repoPath, "repack", "-a", "-d")
repo, err := OpenRepository(repoPath)
if err != nil {
t.Fatalf("OpenRepository failed: %v", err)
}
defer func() {
_ = repo.Close()
}()
headHash := gitCmd(t, repoPath, "rev-parse", "HEAD")
hash, _ := repo.ParseHash(headHash)
obj, err := repo.ReadObject(hash)
if err != nil {
t.Fatalf("failed to read HEAD from final packfile: %v", err)
}
commit := obj.(*StoredCommit)
if !bytes.Contains(commit.Message, []byte("Commit 4")) {
t.Errorf("HEAD commit message incorrect: got %q", commit.Message)
}
}