agentws: check out --branch from origin when it exists
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline failed

`agentws new --branch` always forked from the base branch, so a worktree for a
branch that already existed on origin started at the base's HEAD with none of
the branch's commits, and callers had to reset --hard afterwards to recover.

- Check out an existing origin/<branch> and set the branch to track it
- Fast-forward a stale local branch onto origin, keeping unpushed commits
- Fork from --from or the remote default only when origin lacks the branch
- Name which of the two paths was taken in the command's output
- Ignore --from, with a note, when the branch is already on origin
This commit is contained in:
2026-09-19 16:11:59 +10:00
parent 72adebbf8b
commit 7510187243
4 changed files with 309 additions and 10 deletions
+31
View File
@@ -109,6 +109,37 @@ func GitRemoteBranchExists(repoDir, remote, branch string) bool {
return err == nil
}
// GitRevParse resolves ref to a full object id in repoDir.
func GitRevParse(repoDir, ref string) (string, error) {
return runGit(repoDir, "rev-parse", ref)
}
// GitAheadCount counts commits reachable from head that upstream does not hold.
func GitAheadCount(repoDir, upstream, head string) (int, error) {
out, err := runGit(repoDir, "rev-list", "--count", upstream+".."+head)
if err != nil {
return 0, err
}
n, err := strconv.Atoi(strings.TrimSpace(out))
if err != nil {
return 0, fmt.Errorf("parse rev-list count %q: %w", out, err)
}
return n, nil
}
// GitMergeFFOnly advances the branch checked out at dir to ref, failing rather
// than writing a merge commit when the move is not a fast-forward.
func GitMergeFFOnly(dir, ref string) error {
_, err := runGit(dir, "merge", "--ff-only", ref)
return err
}
// GitSetUpstream points branch at the remote-tracking ref upstream.
func GitSetUpstream(repoDir, branch, upstream string) error {
_, err := runGit(repoDir, "branch", "--set-upstream-to="+upstream, branch)
return err
}
// GitIsDirty reports whether the checkout at dir has uncommitted or untracked
// changes.
func GitIsDirty(dir string) (bool, error) {