Commit acb7f82838
acb7f82838156b034aad81b4613ecf9b9add372e
parent: 245c368edd
Verified · cmc ci/build: success ci/test: success
cmc <hello@cleberg.net> · 2026-09-11 19:34 UTC
control, e2e, wiki: repo commit-file acts on commit references
A commit written by repo commit-file, the web editor's path, lands on
the default branch without receive-pack or a merge, so Closes #N in
its message closed nothing. It now runs ProcessCommitMessages for the
one commit with the session's scope, as the merge path does.
Closes #210
.gitbay/wiki/Users.org
+2 −1
| @@ -345,7 +345,8 @@ activity on their public repositories at =/you/activity.atom=. The |
| 345 | 345 | pages carry the discovery link. |
| 346 | 346 | |
| 347 | 347 | Commit messages act on issues when the commits land on the default |
| 348 | | branch (direct push or MR merge): =closes/fixes/resolves #4= closes the |
| 348 | branch (direct push, MR merge, =repo commit-file= or the web editor): |
| 349 | =closes/fixes/resolves #4= closes the |
| 349 | 350 | issue with a linking comment, and a bare =#4= leaves a reference |
| 350 | 351 | comment. Each issue/commit pair acts once, ever. =Closes owner/name#N= closes an issue in another repository when |
| 351 | 352 | you 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 | 109 | if strings.Count(out, "referenced in commit") != 1 { |
| 110 | 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 | 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 | 96 | sha, err := gitutil.CommitFileChange(dir, ref, filePath, content, |
| 94 | 97 | c.User.Username, email, message) |
| 95 | 98 | if err != nil { |
| 96 | 99 | return c.fail(protocol.ExitFailure, "%v", err) |
| 97 | 100 | } |
| 98 | 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 | 109 | d := struct { |
| 101 | 110 | Path string `json:"path"` |