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.

Compact the change request target controls

41d1fee900568b1bff5c7cff3b4c52942f47fabd · Keenan Nemetz · 13d ago · parent e050e14a

Compact the change request target controls

Replace the separate base and head forms and the duplicated labels with one
row: base picker, head picker, update. The pickers preselect the current
targets, and a pinned head appears as the current commit. Collapse the help
text to one line.

Post both targets to one /target handler and write them in one metadata
commit. Either change invalidates approvals; keeping the current value is
allowed even if its branch has gone missing so the other side can still be
repaired.

metadata/requests_test.go +54

@@ -596,6 +596,60 @@ func TestSetRequestBaseRetargetsAStackedRequest(t *testing.T) {
596 596 }
597 597 }
598 598
599 // The request page posts base and head together, so the writer moves both in one
600 // metadata commit. Either field changing makes approvals stale; setting both to what
601 // they already are is not a commit.
602 func TestSetRequestTargetMovesBaseAndFollowedHead(t *testing.T) {
603 t.Parallel()
604 g := newRequestRepo(t)
605 now := time.Date(2026, 10, 27, 12, 0, 0, 0, time.UTC)
606 r, err := NewRequest("Stacked", "", "keenan", "main", headSHA, now)
607 if err != nil {
608 t.Fatal(err)
609 }
610 r.HeadRef = "feature"
611 if err := CreateRequest(g, r, "keenan"); err != nil {
612 t.Fatal(err)
613 }
614 if _, err := ApproveRequest(g, r.ID, "wilbur", "main", headSHA, now, "wilbur"); err != nil {
615 t.Fatal(err)
616 }
617
618 got, err := SetRequestTarget(g, r.ID, "next", sideSHA, "other", "keenan")
619 if err != nil {
620 t.Fatal(err)
621 }
622 if got.Base != "next" || got.Head != sideSHA || got.HeadRef != "other" {
623 t.Fatalf("target = %s <- %s at %s", got.Base, got.Head, got.HeadRef)
624 }
625 if got.CurrentApprovals() != 0 {
626 t.Fatalf("an approval of the old target still counts: %+v", got.Approvals)
627 }
628
629 // Repeating the same target is not an edit, so the metadata branch does not move.
630 before, err := g.RevParse(Ref)
631 if err != nil {
632 t.Fatal(err)
633 }
634 if _, err := SetRequestTarget(g, r.ID, "next", sideSHA, "other", "keenan"); err != nil {
635 t.Fatal(err)
636 }
637 after, err := g.RevParse(Ref)
638 if err != nil {
639 t.Fatal(err)
640 }
641 if after != before {
642 t.Fatalf("a no-op target move changed %s: %s -> %s", Ref, before, after)
643 }
644
645 if _, err := SetRequestState(g, r.ID, "closed", "keenan"); err != nil {
646 t.Fatal(err)
647 }
648 if _, err := SetRequestTarget(g, r.ID, "main", headSHA, "feature", "keenan"); err == nil {
649 t.Fatal("a closed request's target moved")
650 }
651 }
652
599 653 // A request opened from a branch follows it: a push moves the head to the new tip,
600 654 // the approval of the old tip stops counting, and a pinned or closed request is left
601 655 // alone. A sync that changes nothing does not grow the metadata log.