Fast implementation of Git in pure Go
at master 188 lines 5.4 kB view raw
1package furgit 2 3import ( 4 "bytes" 5 "fmt" 6 "os" 7 "path/filepath" 8 "testing" 9 "time" 10) 11 12func TestCommitWrite(t *testing.T) { 13 repoPath, cleanup := setupTestRepo(t) 14 defer cleanup() 15 16 blobHash := gitHashObject(t, repoPath, "blob", []byte("content")) 17 18 repo, err := OpenRepository(repoPath) 19 if err != nil { 20 t.Fatalf("OpenRepository failed: %v", err) 21 } 22 defer func() { 23 _ = repo.Close() 24 }() 25 26 blobHashObj, _ := repo.ParseHash(blobHash) 27 tree := &Tree{ 28 Entries: []TreeEntry{ 29 {Mode: 0o100644, Name: []byte("file.txt"), ID: blobHashObj}, 30 }, 31 } 32 treeHash, _ := repo.WriteLooseObject(tree) 33 34 whenUnix := time.Date(2023, 11, 16, 12, 0, 0, 0, time.UTC).Unix() 35 commit := &Commit{ 36 Tree: treeHash, 37 Author: Ident{ 38 Name: []byte("Test Author"), 39 Email: []byte("test@example.org"), 40 WhenUnix: whenUnix, 41 OffsetMinutes: 0, 42 }, 43 Committer: Ident{ 44 Name: []byte("Test Committer"), 45 Email: []byte("committer@example.org"), 46 WhenUnix: whenUnix, 47 OffsetMinutes: 0, 48 }, 49 Message: []byte("Initial commit\n"), 50 } 51 52 commitHash, err := repo.WriteLooseObject(commit) 53 if err != nil { 54 t.Fatalf("WriteLooseObject failed: %v", err) 55 } 56 57 gitType := string(gitCatFile(t, repoPath, "-t", commitHash.String())) 58 if gitType != "commit" { 59 t.Errorf("git type: got %q, want %q", gitType, "commit") 60 } 61 62 readObj, err := repo.ReadObject(commitHash) 63 if err != nil { 64 t.Fatalf("ReadObject failed after write: %v", err) 65 } 66 readCommit, ok := readObj.(*StoredCommit) 67 if !ok { 68 t.Fatalf("expected *StoredCommit, got %T", readObj) 69 } 70 71 if !bytes.HasPrefix(readCommit.Author.Name, []byte("Test Author")) { 72 t.Errorf("author name: got %q, want prefix %q", readCommit.Author.Name, "Test Author") 73 } 74 if !bytes.Equal(readCommit.Message, []byte("Initial commit\n")) { 75 t.Errorf("message: got %q, want %q", readCommit.Message, "Initial commit\n") 76 } 77} 78 79func TestCommitRead(t *testing.T) { 80 repoPath, cleanup := setupTestRepo(t) 81 defer cleanup() 82 83 workDir, cleanupWork := setupWorkDir(t) 84 defer cleanupWork() 85 86 err := os.WriteFile(filepath.Join(workDir, "file.txt"), []byte("content"), 0o644) 87 if err != nil { 88 t.Fatalf("failed to write file.txt: %v", err) 89 } 90 gitCmd(t, repoPath, nil, "--work-tree="+workDir, "add", ".") 91 gitCmd(t, repoPath, nil, "--work-tree="+workDir, "commit", "-m", "Test commit") 92 commitHash := gitCmd(t, repoPath, nil, "rev-parse", "HEAD") 93 94 repo, err := OpenRepository(repoPath) 95 if err != nil { 96 t.Fatalf("OpenRepository failed: %v", err) 97 } 98 defer func() { 99 _ = repo.Close() 100 }() 101 102 hash, _ := repo.ParseHash(commitHash) 103 obj, err := repo.ReadObject(hash) 104 if err != nil { 105 t.Fatalf("ReadObject failed: %v", err) 106 } 107 108 commit, ok := obj.(*StoredCommit) 109 if !ok { 110 t.Fatalf("expected *StoredCommit, got %T", obj) 111 } 112 113 if !bytes.HasPrefix(commit.Author.Name, []byte("Test Author")) { 114 t.Errorf("author name: got %q", commit.Author.Name) 115 } 116 if !bytes.Equal(commit.Author.Email, []byte("test@example.org")) { 117 t.Errorf("author email: got %q", commit.Author.Email) 118 } 119 if !bytes.Equal(commit.Message, []byte("Test commit\n")) { 120 t.Errorf("message: got %q", commit.Message) 121 } 122 if commit.ObjectType() != ObjectTypeCommit { 123 t.Errorf("ObjectType(): got %d, want %d", commit.ObjectType(), ObjectTypeCommit) 124 } 125} 126 127func TestCommitWithParents(t *testing.T) { 128 repoPath, cleanup := setupTestRepo(t) 129 defer cleanup() 130 131 workDir, cleanupWork := setupWorkDir(t) 132 defer cleanupWork() 133 134 err := os.WriteFile(filepath.Join(workDir, "file1.txt"), []byte("content1"), 0o644) 135 if err != nil { 136 t.Fatalf("failed to write file1.txt: %v", err) 137 } 138 gitCmd(t, repoPath, nil, "--work-tree="+workDir, "add", ".") 139 gitCmd(t, repoPath, nil, "--work-tree="+workDir, "commit", "-m", "First commit") 140 parent1Hash := gitCmd(t, repoPath, nil, "rev-parse", "HEAD") 141 142 err = os.WriteFile(filepath.Join(workDir, "file2.txt"), []byte("content2"), 0o644) 143 if err != nil { 144 t.Fatalf("failed to write file2.txt: %v", err) 145 } 146 gitCmd(t, repoPath, nil, "--work-tree="+workDir, "add", ".") 147 gitCmd(t, repoPath, nil, "--work-tree="+workDir, "commit", "-m", "Second commit") 148 parent2Hash := gitCmd(t, repoPath, nil, "rev-parse", "HEAD") 149 150 err = os.WriteFile(filepath.Join(workDir, "file3.txt"), []byte("content3"), 0o644) 151 if err != nil { 152 t.Fatalf("failed to write file3.txt: %v", err) 153 } 154 gitCmd(t, repoPath, nil, "--work-tree="+workDir, "add", ".") 155 treeHash := gitCmd(t, repoPath, nil, "--work-tree="+workDir, "write-tree") 156 157 mergeCommitData := fmt.Sprintf("tree %s\nparent %s\nparent %s\nauthor Test Author <test@example.org> 1234567890 +0000\ncommitter Test Committer <committer@example.org> 1234567890 +0000\n\nMerge commit\n", 158 treeHash, parent1Hash, parent2Hash) 159 160 cmd := gitHashObject(t, repoPath, "commit", []byte(mergeCommitData)) 161 mergeHash := cmd 162 163 repo, err := OpenRepository(repoPath) 164 if err != nil { 165 t.Fatalf("OpenRepository failed: %v", err) 166 } 167 defer func() { 168 _ = repo.Close() 169 }() 170 171 hash, _ := repo.ParseHash(mergeHash) 172 obj, _ := repo.ReadObject(hash) 173 commit := obj.(*StoredCommit) 174 175 if len(commit.Parents) != 2 { 176 t.Fatalf("parents count: got %d, want 2", len(commit.Parents)) 177 } 178 179 p1, _ := repo.ParseHash(parent1Hash) 180 p2, _ := repo.ParseHash(parent2Hash) 181 182 if commit.Parents[0] != p1 { 183 t.Errorf("parent[0]: got %s, want %s", commit.Parents[0], parent1Hash) 184 } 185 if commit.Parents[1] != p2 { 186 t.Errorf("parent[1]: got %s, want %s", commit.Parents[1], parent2Hash) 187 } 188}