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/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.

Show what a merged change request landed

2841d922c15ce32fb600d992f9f5dc2d22c48f90 · Keenan Nemetz · 14d ago · parent c765ae2e

Show what a merged change request landed

Landing moves the base to include the head, so the three-dot diff the
Files tab reviews an open request with becomes empty by definition: the
merge base of the base and the head is the head itself. A merged request
therefore looked like it had never changed anything.

The landing mark now records `merged_base`, the tip the base pointed at
before it moved, and the Files tab diffs that against what landed: the
head for a fast-forward, the merge commit otherwise, so conflict
resolution is part of the review too. The record is written in the same
commit as the mark, and a retry that finds the head already landed keeps
the base it has instead of naming the moved tip.

`merged_base` is optional on read because landings made before the field
existed have none. Such a record falls back to the merge commit's first
parent when the landing was a merge, and claims nothing when it was a
fast-forward and the starting point is gone.

The request page shows the file count and the review link for a merged
request as well, with the merge named beside it, instead of replacing
them with "Already merged".

forge/landing/land_test.go +13

@@ -166,6 +166,11 @@ func TestLandFastForwardsAnApprovedRequest(t *testing.T) {
166 166 t.Fatalf("merged record = %q by %q at %v, want head %s by reviewer",
167 167 got.MergedHead, got.MergedBy, got.MergedAt, head)
168 168 }
169 // The base tip before the landing is the "before" side of what the merge
170 // changed: once the head is in main, it is the only record of the starting point.
171 if got.MergedBase != base {
172 t.Fatalf("merged_base = %s, want the base tip %s", got.MergedBase, base)
173 }
169 174 }
170 175
171 176 func TestLandRefusesWithoutAnApproval(t *testing.T) {
@@ -230,6 +235,9 @@ func TestLandWritesAMergeCommitWhenTheBaseMoved(t *testing.T) {
230 235 if got.MergedHead != res.NewTip {
231 236 t.Fatalf("merged_head = %s, want the merge commit %s", got.MergedHead, res.NewTip)
232 237 }
238 if got.MergedBase != moved {
239 t.Fatalf("merged_base = %s, want the base tip %s the merge started from", got.MergedBase, moved)
240 }
233 241 }
234 242
235 243 func TestLandRefusesAConflict(t *testing.T) {
@@ -284,6 +292,11 @@ func TestLandTwiceMarksOnceAndKeepsTheHead(t *testing.T) {
284 292 if got.State != "merged" || got.MergedHead != head {
285 293 t.Fatalf("record = %q/%q, want merged at %s", got.State, got.MergedHead, head)
286 294 }
295 // A retry has no base tip of its own to name, so it keeps the one the first
296 // landing recorded instead of overwriting it with the moved tip.
297 if got.MergedBase != base {
298 t.Fatalf("merged_base = %s, want the retained %s", got.MergedBase, base)
299 }
287 300 }
288 301
289 302 func TestLandRefusesAClosedRequest(t *testing.T) {