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.

Make change requests follow branches and allow base retargeting

e050e14a64f6f19c1b1478c4c073d690ef3d3129 · Keenan Nemetz · 13d ago · parent 59464445

Make change requests follow branches and allow base retargeting

A request opened from a branch now follows it: post-receive moves the
request's head to the new tip after an accepted push, so approvals and
checks that named the old tip stop counting. Approvals stay bound to an
exact base and commit; the approval writers now require the base and head
the approver saw, so a push or retarget cannot silently approve a target
nobody reviewed. Merged requests freeze the reviewed head.

Add `vole change update --base` and a Change base control for stacked
requests. A new base changes the diff and therefore invalidates approvals,
which is what retargeting an upper request after its lower branch lands
needs. Base names may now contain slashes.

Also fix a second-boundary flake in the landing test.

server/web/templates/requests.html +24−3

@@ -42,7 +42,7 @@
42 42 <a class="row-title" href="/r/{{$.Name}}/requests/{{.ID}}">{{.Title}}</a>
43 43 <div class="row-meta"><span class="sha">#{{short .ID}}</span><span>{{len .Comments}} comment(s)</span></div>
44 44 </td>
45 <td><code>{{.Base}}</code> <span class="muted">←</span> {{if .HeadRef}}<code title="the branch the request was opened from">{{.HeadRef}}</code> {{end}}<span class="sha">{{short .Head}}</span></td>
45 <td><code>{{.Base}}</code> <span class="muted">←</span> {{if .HeadRef}}<code title="the branch this request follows">{{.HeadRef}}</code> {{end}}<span class="sha">{{short .Head}}</span></td>
46 46 <td class="muted nowrap">{{who .Author}}</td>
47 47 <td class="muted">{{.Current}}/{{$.Required}}</td>
48 48 <td class="muted nowrap">{{since .Created.Unix}}</td>
@@ -106,12 +106,26 @@
106 106 <h2>{{icon "git-branch"}}Review</h2>
107 107 <dl class="meta-list">
108 108 <dt>Base</dt><dd><code>{{.Request.Base}}</code></dd>
109 <dt>Head</dt><dd>{{with .Request.HeadRef}}<code title="the branch the request was opened from">{{.}}</code>{{if $.HeadRefNote}} <span class="muted">({{$.HeadRefNote}})</span>{{end}} {{end}}<a class="sha" href="/r/{{.Name}}/commit/{{.Request.Head}}">{{short .Request.Head}}</a></dd>
109 <dt>Head</dt><dd>{{with .Request.HeadRef}}<code title="the branch this request follows">{{.}}</code>{{if $.HeadRefNote}} <span class="muted">({{$.HeadRefNote}})</span>{{end}} {{end}}<a class="sha" href="/r/{{.Name}}/commit/{{.Request.Head}}">{{short .Request.Head}}</a></dd>
110 110 </dl>
111 {{if .CanChangeBase}}
112 <form method="post" action="/r/{{.Name}}/requests/{{.Request.ID}}/base" class="base-move">
113 <input type="hidden" name="csrf" value="{{.CSRF}}">
114 <label>Change base
115 <select name="base" required>
116 <option value="" selected disabled>Choose a branch</option>
117 {{range .BaseBranches}}<option value="{{.}}">{{.}}</option>{{end}}
118 </select>
119 </label>
120 <button class="secondary" type="submit"{{if not .BaseBranches}} disabled{{end}}>{{icon "rotate-ccw"}}Update</button>
121 </form>
122 <p class="muted">A new base changes the diff and starts approvals over. Retarget a stacked request after the branch below it lands.</p>
123 {{if not .BaseBranches}}<p class="muted">Push a branch first.</p>{{end}}
124 {{end}}
111 125 {{if .CanMoveHead}}
112 126 <form method="post" action="/r/{{.Name}}/requests/{{.Request.ID}}/head" class="head-move">
113 127 <input type="hidden" name="csrf" value="{{.CSRF}}">
114 <label>Move head to
128 <label>Repoint request
115 129 <select name="head" required>
116 130 <option value="" selected disabled>Choose a branch</option>
117 131 {{range .HeadBranches}}<option value="{{.}}">{{.}}</option>{{end}}
@@ -119,6 +133,11 @@
119 133 </label>
120 134 <button class="secondary" type="submit"{{if not .HeadBranches}} disabled{{end}}>{{icon "rotate-ccw"}}Update</button>
121 135 </form>
136 {{if .Request.HeadRef}}
137 <p class="muted">Pushes to <code>{{.Request.HeadRef}}</code> move this request automatically. Repoint only to follow a different branch.</p>
138 {{else}}
139 <p class="muted">This request is pinned to a commit. Choose a branch to make it follow pushes.</p>
140 {{end}}
122 141 {{if not .HeadBranches}}<p class="muted">Push a branch first.</p>{{end}}
123 142 {{end}}
124 143 </section>
@@ -210,6 +229,8 @@
210 229 {{if .CanApprove}}
211 230 <form method="post" action="/r/{{.Name}}/requests/{{.Request.ID}}/approve">
212 231 <input type="hidden" name="csrf" value="{{.CSRF}}">
232 <input type="hidden" name="base" value="{{.Request.Base}}">
233 <input type="hidden" name="head" value="{{.Request.Head}}">
213 234 <button class="success" type="submit">{{icon "check"}}Approve</button>
214 235 </form>
215 236 {{end}}