repo commit-file acts on commit references !378

merged merged by cmc on 2026-09-11 23:41 UTC · krz/gitbay:fix-210-commit-file-refs into main

3 files changed, +35 −1

Layout: unified · split

.gitbay/wiki/Users.org +2 −1
@@ -345,7 +345,8 @@ activity on their public repositories at =/you/activity.atom=. The
345pages carry the discovery link. 345pages carry the discovery link.
346 346
347Commit messages act on issues when the commits land on the default 347Commit messages act on issues when the commits land on the default
348branch (direct push or MR merge): =closes/fixes/resolves #4= closes the 348branch (direct push, MR merge, =repo commit-file= or the web editor):
349=closes/fixes/resolves #4= closes the
349issue with a linking comment, and a bare =#4= leaves a reference 350issue with a linking comment, and a bare =#4= leaves a reference
350comment. Each issue/commit pair acts once, ever. =Closes owner/name#N= closes an issue in another repository when 351comment. Each issue/commit pair acts once, ever. =Closes owner/name#N= closes an issue in another repository when
351you hold write there; otherwise it stays a plain link. The comment left 352you hold write there; otherwise it stays a plain link. The comment left
e2e/commitrefs_test.go +24
@@ -109,4 +109,28 @@ func TestCommitMessageIssueActions(t *testing.T) {
109 if strings.Count(out, "referenced in commit") != 1 { 109 if strings.Count(out, "referenced in commit") != 1 {
110 t.Fatalf("reference duplicated: %s", out) 110 t.Fatalf("reference duplicated: %s", out)
111 } 111 }
112
113 // A commit written by repo commit-file (the web editor's path) lands
114 // on the default branch without receive-pack or a merge, and acts
115 // the same (#210). On another branch it does nothing.
116 if _, errOut, code := inst.ssh(t, aliceKey, "e\n", "repo", "commit-file", "alice/app", "e.txt",
117 "--ref", "feat", "--message", "'Closes #2 on a branch'", "--file", "-"); code != 0 {
118 t.Fatalf("commit-file on branch: %s", errOut)
119 }
120 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "2", "--json")
121 if !strings.Contains(out, `"state":"open"`) {
122 t.Fatalf("commit-file on a branch acted: %s", out)
123 }
124 if _, errOut, code := inst.ssh(t, aliceKey, "e\n", "repo", "commit-file", "alice/app", "e.txt",
125 "--ref", "main", "--message", "'Closes #2 from the editor'", "--file", "-"); code != 0 {
126 t.Fatalf("commit-file: %s", errOut)
127 }
128 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "2", "--json")
129 if !strings.Contains(out, `"state":"closed"`) || !strings.Contains(out, "closed by commit") ||
130 !strings.Contains(out, "by [alice](/alice): Closes #2 from the editor") {
131 t.Fatalf("commit-file did not close issue 2: %s", out)
132 }
133 if strings.Count(out, "referenced in commit") != 1 {
134 t.Fatalf("commit-file re-acted on earlier commits: %s", out)
135 }
112} 136}
internal/control/commitfile.go +9
@@ -90,12 +90,21 @@ func runCommitFile(c *Ctx, args []string) int {
90 } 90 }
91 91
92 dir := RepoDir(c.Cfg.Server.Root, repo.OwnerName, repo.Name) 92 dir := RepoDir(c.Cfg.Server.Root, repo.OwnerName, repo.Name)
93 // The head before the commit bounds what landed; a branch that does
94 // not exist fails in CommitFileChange with its own message.
95 parent, _ := gitutil.ResolveRef(dir, "refs/heads/"+ref)
93 sha, err := gitutil.CommitFileChange(dir, ref, filePath, content, 96 sha, err := gitutil.CommitFileChange(dir, ref, filePath, content,
94 c.User.Username, email, message) 97 c.User.Username, email, message)
95 if err != nil { 98 if err != nil {
96 return c.fail(protocol.ExitFailure, "%v", err) 99 return c.fail(protocol.ExitFailure, "%v", err)
97 } 100 }
98 c.Store.MarkMirrorsDirty(repo.ID, "push") 101 c.Store.MarkMirrorsDirty(repo.ID, "push")
102 // This bypasses receive-pack like a merge does, so the commit-message
103 // issue actions (closes #N, references) run here for the default
104 // branch, with the session's scope (#210).
105 if ref == repo.DefaultBranch {
106 ProcessCommitMessages(c.Store, dir, repo, c.User.ID, c.Scope, parent, sha)
107 }
99 108
100 d := struct { 109 d := struct {
101 Path string `json:"path"` 110 Path string `json:"path"`