Files
simplegit/gitcmd/merge_test.go
T
2026-07-15 11:06:48 -04:00

173 lines
5.2 KiB
Go

package gitcmd
import (
"path/filepath"
"testing"
)
func buildFFRepo(t *testing.T) (repo *Repository, c2 string) {
t.Helper()
dir := t.TempDir()
work := filepath.Join(dir, "work")
bare := filepath.Join(dir, "bare.git")
gitRun(t, "", nil, "init", "-q", "-b", "main", work)
writeFile(t, filepath.Join(work, "a.txt"), "a\n")
commitAs(t, work, "A", "a@x", "2024-03-01T00:00:00+00:00", "c1")
gitRun(t, work, nil, "branch", "feature")
gitRun(t, work, nil, "checkout", "-q", "feature")
writeFile(t, filepath.Join(work, "b.txt"), "b\n")
commitAs(t, work, "A", "a@x", "2024-03-02T00:00:00+00:00", "c2")
c2 = headSHA(t, work)
return cloneBareFromWork(t, work, bare), c2
}
func buildConflictRepo(t *testing.T) *Repository {
t.Helper()
dir := t.TempDir()
work := filepath.Join(dir, "work")
bare := filepath.Join(dir, "bare.git")
gitRun(t, "", nil, "init", "-q", "-b", "main", work)
writeFile(t, filepath.Join(work, "f.txt"), "base\n")
commitAs(t, work, "A", "a@x", "2024-04-01T00:00:00+00:00", "c1")
gitRun(t, work, nil, "branch", "feature")
writeFile(t, filepath.Join(work, "f.txt"), "main\n")
commitAs(t, work, "A", "a@x", "2024-04-02T00:00:00+00:00", "c2main")
gitRun(t, work, nil, "checkout", "-q", "feature")
writeFile(t, filepath.Join(work, "f.txt"), "feature\n")
commitAs(t, work, "A", "a@x", "2024-04-03T00:00:00+00:00", "c3feat")
gitRun(t, work, nil, "checkout", "-q", "main")
return cloneBareFromWork(t, work, bare)
}
func TestMerge_Clean(t *testing.T) {
f := newMergeFixture(t)
res, err := f.repo.Merge("main", "feature", "merge feature", wAuthor, wAuthorEmail, false)
if err != nil {
t.Fatalf("Merge: %v", err)
}
if len(res.Conflicts) != 0 {
t.Errorf("conflicts = %v, want none", res.Conflicts)
}
if res.MergeCommit == nil {
t.Fatal("nil merge commit")
}
if res.FastForward {
t.Error("want real merge, got FF")
}
if len(res.MergeCommit.Parents) != 2 {
t.Errorf("parents = %v, want 2", res.MergeCommit.Parents)
}
tree, _ := f.repo.GetTree(res.MergeCommit.ID, "")
names := treeNames(tree)
for _, want := range []string{"a.txt", "b.txt", "c.txt"} {
if !containsStr(names, want) {
t.Errorf("merged tree missing %s; got %v", want, names)
}
}
}
func TestMerge_FastForward(t *testing.T) {
repo, c2 := buildFFRepo(t)
res, err := repo.Merge("main", "feature", "ff", wAuthor, wAuthorEmail, false)
if err != nil {
t.Fatalf("Merge: %v", err)
}
if !res.FastForward {
t.Error("want FF")
}
if res.MergeCommit == nil || res.MergeCommit.ID != c2 {
t.Errorf("merge commit = %v, want %s", res.MergeCommit, c2)
}
tip, _ := repo.refSHA("refs/heads/main")
if tip != c2 {
t.Errorf("main tip = %s, want %s", tip, c2)
}
}
func TestMerge_Conflict(t *testing.T) {
repo := buildConflictRepo(t)
mainBefore, _ := repo.refSHA("refs/heads/main")
res, err := repo.Merge("main", "feature", "merge", wAuthor, wAuthorEmail, false)
if err != nil {
t.Fatalf("Merge: %v", err)
}
if res.MergeCommit != nil {
t.Error("want nil merge commit on conflict")
}
if len(res.Conflicts) == 0 {
t.Error("want conflicts")
} else if !containsStr(res.Conflicts, "f.txt") {
t.Errorf("conflicts = %v, want f.txt", res.Conflicts)
}
mainAfter, _ := repo.refSHA("refs/heads/main")
if mainAfter != mainBefore {
t.Errorf("main moved on conflict: %s -> %s", mainBefore, mainAfter)
}
}
func TestMergeViaTempRepo_Clean(t *testing.T) {
f := newMergeFixture(t)
res, err := f.repo.MergeViaTempRepo("main", "feature", "merge feature", wAuthor, wAuthorEmail, false)
if err != nil {
t.Fatalf("MergeViaTempRepo: %v", err)
}
if len(res.Conflicts) != 0 {
t.Errorf("conflicts = %v, want none", res.Conflicts)
}
if res.MergeCommit == nil {
t.Fatal("nil merge commit")
}
if res.FastForward {
t.Error("want real merge, got FF")
}
if len(res.MergeCommit.Parents) != 2 {
t.Errorf("parents = %v, want 2", res.MergeCommit.Parents)
}
tree, _ := f.repo.GetTree(res.MergeCommit.ID, "")
names := treeNames(tree)
for _, want := range []string{"a.txt", "b.txt", "c.txt"} {
if !containsStr(names, want) {
t.Errorf("merged tree missing %s; got %v", want, names)
}
}
}
func TestMergeViaTempRepo_FastForward(t *testing.T) {
repo, c2 := buildFFRepo(t)
res, err := repo.MergeViaTempRepo("main", "feature", "ff", wAuthor, wAuthorEmail, false)
if err != nil {
t.Fatalf("MergeViaTempRepo: %v", err)
}
if !res.FastForward {
t.Error("want FF")
}
if res.MergeCommit == nil || res.MergeCommit.ID != c2 {
t.Errorf("merge commit = %v, want %s", res.MergeCommit, c2)
}
tip, _ := repo.refSHA("refs/heads/main")
if tip != c2 {
t.Errorf("main tip = %s, want %s", tip, c2)
}
}
func TestMergeViaTempRepo_Conflict(t *testing.T) {
repo := buildConflictRepo(t)
mainBefore, _ := repo.refSHA("refs/heads/main")
res, err := repo.MergeViaTempRepo("main", "feature", "merge", wAuthor, wAuthorEmail, false)
if err != nil {
t.Fatalf("MergeViaTempRepo: %v", err)
}
if res.MergeCommit != nil {
t.Error("want nil merge commit on conflict")
}
if len(res.Conflicts) == 0 {
t.Error("want conflicts")
} else if !containsStr(res.Conflicts, "f.txt") {
t.Errorf("conflicts = %v, want f.txt", res.Conflicts)
}
mainAfter, _ := repo.refSHA("refs/heads/main")
if mainAfter != mainBefore {
t.Errorf("main moved on conflict: %s -> %s", mainBefore, mainAfter)
}
}