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 which branch a change request came from
8e1005eb2c104934adef950c5afc090d4025ce06 · Keenan Nemetz · 13d ago · parent b7ac1a00
Show which branch a change request came from A request records the exact commit under review, and the page showed only that: `main ← 43f98bcd`. The branch the work is on is how people talk about a change, fetch it, and find it after it lands, and it was being thrown away when the request was opened — the web form resolved the chosen branch to a commit and kept only the commit. `request.md` now records `head_ref:` beside `head:`. It is a display hint and nothing else: approvals, checks, the diff and the landing gate still name the commit, and a test pins that a branch name cannot authorise a different tip. The request page, the request list and `vole change show` show it, and the page marks the branch as moved or deleted when it no longer points at the reviewed commit, so the name reads as provenance rather than as a claim about what is being reviewed. It is optional on read: records written before it existed still load, and a request opened from a bare commit id records no branch.
cmd/vole/change.go +19
| @@ -9,6 +9,7 @@ import ( | |||
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | 11 | "github.com/rodent-software/vole/client" | |
| 12 | "github.com/rodent-software/vole/core/git" | ||
| 12 | 13 | "github.com/rodent-software/vole/internal/format" | |
| 13 | 14 | "github.com/rodent-software/vole/metadata" | |
| 14 | 15 | ) | |
| @@ -139,6 +140,7 @@ func cmdChange(args []string) error { | |||
| 139 | 140 | if err != nil { | |
| 140 | 141 | return err | |
| 141 | 142 | } | |
| 143 | r.HeadRef = headBranch(c.Repo, f.one("head"), head) | ||
| 142 | 144 | // A linked issue is a promise that landing this request closes it, so the ids | |
| 143 | 145 | // are checked when the request is written rather than at the merge: a typo | |
| 144 | 146 | // should fail while the author is still looking at the command. | |
| @@ -309,6 +311,9 @@ func cmdChange(args []string) error { | |||
| 309 | 311 | fmt.Printf("author: %s\n", r.Author) | |
| 310 | 312 | fmt.Printf("base: %s\n", r.Base) | |
| 311 | 313 | fmt.Printf("head: %s\n", r.Head) | |
| 314 | if r.HeadRef != "" { | ||
| 315 | fmt.Printf("branch: %s\n", r.HeadRef) | ||
| 316 | } | ||
| 312 | 317 | if line := client.MergeLine(c, r); line != "" { | |
| 313 | 318 | fmt.Printf("merge: %s\n", line) | |
| 314 | 319 | } | |
| @@ -466,6 +471,20 @@ func cmdChange(args []string) error { | |||
| 466 | 471 | return usagef("unknown change subcommand %q", sub) | |
| 467 | 472 | } | |
| 468 | 473 | ||
| 474 | // headBranch names the branch a --head revision was resolved from, when it is one. | ||
| 475 | // The request records the commit it reviews; the branch is a display hint, so a raw | ||
| 476 | // commit id, a tag, or HEAD records nothing. | ||
| 477 | func headBranch(g *git.Repo, rev, head string) string { | ||
| 478 | name := strings.TrimPrefix(strings.TrimSpace(rev), "refs/heads/") | ||
| 479 | if name == "" || !g.ValidBranch(name) { | ||
| 480 | return "" | ||
| 481 | } | ||
| 482 | if tip, err := g.RevParse("refs/heads/" + name); err != nil || tip != head { | ||
| 483 | return "" | ||
| 484 | } | ||
| 485 | return name | ||
| 486 | } | ||
| 487 | |||
| 469 | 488 | // closesFlag normalises --closes, which is repeatable and may also carry a | |
| 470 | 489 | // comma-separated list: both habits are common and neither is worth a second flag. | |
| 471 | 490 | func closesFlag(values []string) []string { | |