vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Test the merge result before landing it
e5478085e5b53a2892d0649d6f07c6f0a5653378 · Keenan Nemetz · 11d ago · parent db5e4382
Test the merge result before landing it A protected branch could receive a non-fast-forward landing whose tree was never built: the gate asked for checks on the request head, and the merge commit was made at landing time. A pushed merge with the right parents but extra content was accepted too. Publish the merge commit under refs/vole/candidates/, test it as the base branch, and make the gate require its results. Approvals name the head and the base branch, so a moved base retests without a new approval. A merger who clicks while that run is still going waits for it instead of clicking again. A pushed merge commit now has to carry exactly the tree a clean merge would produce. Closes: 01M61QQ56TESQ1HTMB1Z5C3733
server/web/templates/requests.html +37−3
| @@ -114,6 +114,7 @@ | |||
| 114 | 114 | {{define "request_sidebar"}} | |
| 115 | 115 | <div id="request-sidebar"> | |
| 116 | 116 | {{if .Error}}<p class="error">{{.Error}}</p>{{end}} | |
| 117 | {{if .Notice}}<p class="muted">{{.Notice}}</p>{{end}} | ||
| 117 | 118 | <section> | |
| 118 | 119 | <h2 class="with-more"><span>{{icon "git-branch"}}Review{{template "help" "The request follows its head branch, so a push moves the head with it. Changing the base or head starts approvals over. A conflict here blocks landing."}}</span> <a class="more sha" href="/r/{{.Name}}/commit/{{.Request.Head}}" title="commit under review">{{short .Request.Head}}</a></h2> | |
| 119 | 120 | {{if .MergeChecked}}{{if .Conflicts}} | |
| @@ -199,6 +200,34 @@ | |||
| 199 | 200 | <p class="muted">No CI run for this commit yet.</p> | |
| 200 | 201 | {{end}} | |
| 201 | 202 | </section> | |
| 203 | |||
| 204 | {{if .CombinedNeeded}} | ||
| 205 | <section> | ||
| 206 | <h2 class="with-more"><span>{{icon "git-merge"}}Combined result{{template "help" "The merge that would land, built from this change and the latest base. It is tested automatically so the branch is ready when the review is. Only a conflict needs work on the branch."}}</span> {{if .CombinedSHA}}<a class="more sha" href="/r/{{.Name}}/commit/{{.CombinedSHA}}" title="combined result commit">{{short .CombinedSHA}}</a>{{end}}</h2> | ||
| 207 | {{if eq .CombinedState "passed"}} | ||
| 208 | <p class="muted">Passed with the latest <code>{{.Request.Base}}</code>.</p> | ||
| 209 | {{else if eq .CombinedState "testing"}} | ||
| 210 | <p class="muted">Testing the merge with the latest <code>{{.Request.Base}}</code>…</p> | ||
| 211 | {{else if eq .CombinedState "failed"}} | ||
| 212 | <p class="conflict">The combined result with <code>{{.Request.Base}}</code> failed. The runs below name the job.</p> | ||
| 213 | {{else if eq .CombinedState "conflict"}} | ||
| 214 | <p class="conflict">This change conflicts with the latest <code>{{.Request.Base}}</code>. Resolve it on the branch and push again.</p> | ||
| 215 | {{end}} | ||
| 216 | {{if .CombinedRuns}} | ||
| 217 | <div class="side-list"> | ||
| 218 | {{range .CombinedRuns}} | ||
| 219 | <a class="side-run" href="/r/{{$.Name}}/runs/{{.ID}}"> | ||
| 220 | <span class="side-run-top"> | ||
| 221 | <span class="status {{.Status}}">{{.Status}}</span> | ||
| 222 | <span class="side-subject">{{.Job}}</span> | ||
| 223 | </span> | ||
| 224 | <span class="side-meta"><span>{{.Workflow}}</span><span>{{if not .Started.IsZero}}{{duration .Duration}}{{else}}{{duration .Wait}} queued{{end}}</span><span>{{since .Created.Unix}}</span></span> | ||
| 225 | </a> | ||
| 226 | {{end}} | ||
| 227 | </div> | ||
| 228 | {{end}} | ||
| 229 | </section> | ||
| 230 | {{end}} | ||
| 202 | 231 | </div> | |
| 203 | 232 | {{end}} | |
| 204 | 233 | ||
| @@ -237,7 +266,7 @@ | |||
| 237 | 266 | {{end}} | |
| 238 | 267 | </p> | |
| 239 | 268 | </div> | |
| 240 | {{if or .CanApprove .CanClose .CanMerge .ContributorCI}} | ||
| 269 | {{if or .CanApprove .CanClose .CanMerge .MergeArmed .ContributorCI}} | ||
| 241 | 270 | <div class="page-head-actions"> | |
| 242 | 271 | {{with .ContributorCI}}{{if .CanAllow}} | |
| 243 | 272 | <form method="post" action="/r/{{$.Name}}/ci/allow"> | |
| @@ -255,10 +284,15 @@ | |||
| 255 | 284 | <button class="success" type="submit">{{icon "check"}}Approve</button> | |
| 256 | 285 | </form> | |
| 257 | 286 | {{end}} | |
| 258 | {{if .CanMerge}} | ||
| 287 | {{if .MergeArmed}} | ||
| 288 | <form method="post" action="/r/{{.Name}}/requests/{{.Request.ID}}/merge/cancel"> | ||
| 289 | <input type="hidden" name="csrf" value="{{.CSRF}}"> | ||
| 290 | <button class="secondary" type="submit">{{icon "circle-x"}}Cancel merge</button> | ||
| 291 | </form> | ||
| 292 | {{else if .CanMerge}} | ||
| 259 | 293 | <form method="post" action="/r/{{.Name}}/requests/{{.Request.ID}}/merge"> | |
| 260 | 294 | <input type="hidden" name="csrf" value="{{.CSRF}}"> | |
| 261 | <button class="success" type="submit">{{icon "git-merge"}}Merge</button> | ||
| 295 | <button class="success" type="submit"{{if .MergeBlocked}} disabled{{end}}>{{icon "git-merge"}}{{if .MergePending}}Merge when ready{{else}}Merge{{end}}</button> | ||
| 262 | 296 | </form> | |
| 263 | 297 | {{end}} | |
| 264 | 298 | {{if .CanClose}} | |