Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 53m ago 4b8c9e1d
history blame raw 3.9 KiB
1package git
2
3// The merge check is the one thing a reviewer needs that a diff cannot answer:
4// a branch can look right line by line and still refuse to merge. These tests pin
5// the two shapes (clean, conflicted) and the parse, which is the part that would
6// silently pass a broken branch if it were wrong.
7
8import (
9 "os"
10 "os/exec"
11 "path/filepath"
12 "strings"
13 "testing"
14)
15
16// conflictRepo builds main and a side branch that both change the middle line of
17// one file, so merging them conflicts, plus a second branch that touches a
18// different file so a clean merge can be asked for too.
19func conflictRepo(t *testing.T) string {
20 t.Helper()
21 dir := t.TempDir()
22 run := func(args ...string) {
23 t.Helper()
24 cmd := exec.Command("git", args...)
25 cmd.Dir = dir
26 cmd.Env = append(os.Environ(),
27 "GIT_CONFIG_GLOBAL=/dev/null", "GIT_CONFIG_SYSTEM=/dev/null",
28 "GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@example.com",
29 "GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@example.com")
30 if out, err := cmd.CombinedOutput(); err != nil {
31 t.Fatalf("git %s: %v: %s", strings.Join(args, " "), err, out)
32 }
33 }
34 write := func(name, body string) {
35 t.Helper()
36 if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil {
37 t.Fatal(err)
38 }
39 }
40 run("init", "-q", "-b", "main")
41 write("f.txt", "a\nb\nc\n")
42 write("g.txt", "x\n")
43 run("add", "-A")
44 run("commit", "-qm", "base")
45
46 run("switch", "-q", "-c", "side")
47 write("f.txt", "a\nSIDE\nc\n")
48 run("commit", "-qam", "side changes the middle")
49
50 run("switch", "-q", "main")
51 write("f.txt", "a\nMAIN\nc\n")
52 write("g.txt", "x\ny\n")
53 run("add", "-A")
54 run("commit", "-qm", "main changes the middle too")
55
56 run("switch", "-q", "-c", "elsewhere", "main")
57 write("g.txt", "x\nz\n")
58 run("commit", "-qam", "elsewhere touches another file")
59 run("switch", "-q", "main")
60 return dir
61}
62
63func TestMergeConflictsNamesThePaths(t *testing.T) {
64 r := open(t, conflictRepo(t))
65 paths, err := r.MergeConflicts("main", "side")
66 if err != nil {
67 t.Fatalf("MergeConflicts: %v", err)
68 }
69 if len(paths) != 1 || paths[0] != "f.txt" {
70 t.Fatalf("conflicts = %v, want [f.txt]", paths)
71 }
72}
73
74func TestMergeConflictsIsEmptyWhenItWouldLand(t *testing.T) {
75 r := open(t, conflictRepo(t))
76 paths, err := r.MergeConflicts("main", "elsewhere")
77 if err != nil {
78 t.Fatalf("MergeConflicts: %v", err)
79 }
80 if len(paths) != 0 {
81 t.Fatalf("a clean merge reported conflicts: %v", paths)
82 }
83 // Merging a branch into itself is the trivial case and must stay clean.
84 if paths, err := r.MergeConflicts("main", "main"); err != nil || len(paths) != 0 {
85 t.Fatalf("self-merge: %v %v", paths, err)
86 }
87}
88
89// TestMergeConflictsReportsAnUnaskableQuestion: an unknown revision is an error,
90// not "no conflicts". A page that treated it as clean would vouch for a branch it
91// never checked.
92func TestMergeConflictsReportsAnUnaskableQuestion(t *testing.T) {
93 r := open(t, conflictRepo(t))
94 if _, err := r.MergeConflicts("main", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); err == nil {
95 t.Fatal("an unknown revision was reported as no conflicts")
96 }
97}
98
99// TestParseMergeTree covers the output shapes merge-tree can produce, including
100// the informational section that follows the paths when messages are enabled.
101func TestParseMergeTree(t *testing.T) {
102 cases := []struct {
103 name string
104 out string
105 want []string
106 }{
107 {"clean", "904a7da1538496ad28d0611296e5bcf7bb0cbe58\n", nil},
108 {"empty", "", nil},
109 {"one path", "baadc1af8cd4fcf7d36031de2001ae4346cfacc5\nf.txt\n", []string{"f.txt"}},
110 {"two paths", "tree-oid\na.txt\nb.txt\n", []string{"a.txt", "b.txt"}},
111 {"messages after a blank line", "tree-oid\nf.txt\n\nAuto-merging f.txt\nCONFLICT (content): Merge conflict in f.txt\n", []string{"f.txt"}},
112 }
113 for _, c := range cases {
114 got := parseMergeTree(c.out)
115 if len(got) != len(c.want) {
116 t.Fatalf("%s: %v, want %v", c.name, got, c.want)
117 }
118 for i := range got {
119 if got[i] != c.want[i] {
120 t.Fatalf("%s: %v, want %v", c.name, got, c.want)
121 }
122 }
123 }
124}
125