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.
Resolve a change request's head on the remote, not the clone
6755b7515c2e3e404e8325eabc0b1e0b74ab901f · Keenan Nemetz · 10d ago · parent 26c9be33
Resolve a change request's head on the remote, not the clone `change new --head <branch>` resolved the branch in the local clone. A clone that was behind recorded the old commit, and when that commit was already in the base the request contained nothing — which is how #117 merged nothing, closed its issue, and left the work on a side branch. - A branch's head is resolved with `git ls-remote` against the remote the metadata write pushes to, so the recorded commit is the forge's tip. If local and remote differ, the remote tip is recorded and stderr says so; a local-only branch is recorded with a note; `--no-push` may fall back to the local ref when the remote cannot be reached. - A request whose head is already in the base (the same commit, or an ancestor) is refused before anything is written, for `change new` and `change update --head`. The base is resolved on the remote too: a feature worktree commits on its local base branch, so the local base can be the head itself and would call every request empty. - The tag/commit path is unchanged: it still pins the request to that commit with no branch. Tests cover a branch that moved after the clone, a local-only branch, a head equal to the base, a head that is an ancestor of the base, and the `change update --head` refusal. The head-resolution note is documented in docs/change-requests.md.
server/integration_test.go +10−1
| @@ -1108,8 +1108,17 @@ func TestRequestAuthorComesFromTheKey(t *testing.T) { | |||
| 1108 | 1108 | if _, errs, err := runGit(t, clone, "config", "user.name", "Keenan Nemetz"); err != nil { | |
| 1109 | 1109 | t.Fatalf("config: %s", errs) | |
| 1110 | 1110 | } | |
| 1111 | // A request reviews a change, so it needs a head that is not already the base. | ||
| 1112 | runGit(t, clone, "checkout", "-q", "-b", "topic") | ||
| 1113 | if err := os.WriteFile(filepath.Join(clone, "readme.md"), []byte("hi\ntopic\n"), 0o644); err != nil { | ||
| 1114 | t.Fatal(err) | ||
| 1115 | } | ||
| 1116 | runGit(t, clone, "commit", "-qam", "topic") | ||
| 1117 | if _, errs, err := f.git(clone, "push", f.sshURL(f.repo), "topic:refs/heads/topic"); err != nil { | ||
| 1118 | t.Fatalf("push topic: %s", errs) | ||
| 1119 | } | ||
| 1111 | 1120 | ||
| 1112 | out, errs, err := f.cli(clone, "change", "new", "--title", "author from the key", "-m", "body") | ||
| 1121 | out, errs, err := f.cli(clone, "change", "new", "--title", "author from the key", "--head", "topic", "-m", "body") | ||
| 1113 | 1122 | if err != nil { | |
| 1114 | 1123 | t.Fatalf("request new: %v\n%s", err, errs) | |
| 1115 | 1124 | } | |