From 6237432a7d0246bca1bfae656e01e2abfd780ab4 Mon Sep 17 00:00:00 2001 From: Edmund Kohlwey Date: Mon, 1 Jun 2026 11:33:26 -0400 Subject: [PATCH 1/2] integration: short-circuit gs branch checkout for the integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'gs bco ' previously fell into the generic "branch not tracked" path: in interactive mode the user was prompted to track the branch (and accepting would then fail with "cannot track integration branch"), and in non-interactive mode they got a misleading warning to run 'gs branch track'. Both are noise — the integration is deliberately untracked and the user typed its name because they want to switch to it. Detect the integration branch in the branchCheckoutCmd up front. If the requested name matches, emit a single targeted warning that the branch is throwaway and short-circuit ShouldTrack to return false so the existing CheckoutBranch path falls through to the plain checkout without prompting. This makes 'gs bco ' a viable alias for 'gs intco' without introducing a separate command path or changing the checkout handler's contract. --- .../unreleased/Added-20260601-113312.yaml | 3 ++ branch_checkout.go | 20 ++++++++++ .../script/branch_checkout_integration.txt | 38 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 .changes/unreleased/Added-20260601-113312.yaml create mode 100644 testdata/script/branch_checkout_integration.txt diff --git a/.changes/unreleased/Added-20260601-113312.yaml b/.changes/unreleased/Added-20260601-113312.yaml new file mode 100644 index 000000000..ca049daeb --- /dev/null +++ b/.changes/unreleased/Added-20260601-113312.yaml @@ -0,0 +1,3 @@ +kind: Added +body: '''branch checkout'': Treat the integration branch name as a shortcut to the integration branch, with a one-line throwaway warning instead of an untracked-branch prompt.' +time: 2026-06-01T11:33:12.704475-04:00 diff --git a/branch_checkout.go b/branch_checkout.go index 7c7233905..f9c279130 100644 --- a/branch_checkout.go +++ b/branch_checkout.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding" + "errors" "fmt" "strings" @@ -10,6 +11,7 @@ import ( "go.abhg.dev/gs/internal/git" "go.abhg.dev/gs/internal/handler/checkout" "go.abhg.dev/gs/internal/silog" + "go.abhg.dev/gs/internal/spice/state" "go.abhg.dev/gs/internal/text" "go.abhg.dev/gs/internal/ui" ) @@ -146,6 +148,7 @@ func (cmd *branchCheckoutCmd) Run( ctx context.Context, log *silog.Logger, view ui.View, + store *state.Store, handler CheckoutHandler, ) error { mode := cmd.TrackUntracked @@ -162,10 +165,27 @@ func (cmd *branchCheckoutCmd) Run( log.Warnf("Please use spice.branchCheckout.trackUntracked=%v instead", mode.String()) } + // The integration branch is a deliberately-untracked singleton; the + // generic "branch not tracked" prompt is confusing for it. Detect + // the case once up front so ShouldTrack can short-circuit and the + // user gets a single, targeted warning that the branch is + // throwaway. + isIntegration := false + if info, err := store.Integration(ctx); err == nil && info.Name == cmd.Branch { + isIntegration = true + log.Warnf("%v: integration branch is throwaway and not tracked by git-spice", cmd.Branch) + } else if err != nil && !errors.Is(err, state.ErrNotExist) { + log.Warn("Could not load integration branch configuration", "error", err) + } + return handler.CheckoutBranch(ctx, &checkout.Request{ Branch: cmd.Branch, Options: &cmd.Options, ShouldTrack: func(branch string) (bool, error) { + if isIntegration { + return false, nil + } + switch mode { case trackUntrackedAlways: log.Infof("%v: automatically tracking branch", branch) diff --git a/testdata/script/branch_checkout_integration.txt b/testdata/script/branch_checkout_integration.txt new file mode 100644 index 000000000..fb7dd8c18 --- /dev/null +++ b/testdata/script/branch_checkout_integration.txt @@ -0,0 +1,38 @@ +# Verifies 'gs branch checkout ' is a no-friction +# shortcut to the integration branch: it switches without prompting +# to track the branch and warns once that the branch is throwaway. + +as 'Test ' +at '2025-06-01T00:00:00Z' + +cd repo +git init +git commit --allow-empty -m 'Initial commit' +gs repo init + +git add feat-a.txt +gs bc feat-a -m 'Add feat-a' + +gs trunk +gs integration create preview --tip feat-a +gs integration rebuild + +# After rebuild, the worktree is back on the previously checked-out +# branch (main). Switching to the integration branch by name should +# just work, without the 'branch not tracked' prompt path. +gs bco preview +stderr 'integration branch is throwaway' +stderr 'switched to branch: preview' + +# Sanity: HEAD now points at the integration branch. +git symbolic-ref HEAD +cmp stdout $WORK/golden/head.txt + +# Sanity: tracking is not implicitly added. +! gs branch track preview +stderr 'cannot track integration branch' + +-- repo/feat-a.txt -- +feature a +-- golden/head.txt -- +refs/heads/preview From b7a6d447e11a392a13983ff3a3c3119bc0afb731 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 01:39:12 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- doc/includes/cli-reference.md | 36 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/doc/includes/cli-reference.md b/doc/includes/cli-reference.md index ecad060ec..7ea9b9192 100644 --- a/doc/includes/cli-reference.md +++ b/doc/includes/cli-reference.md @@ -310,9 +310,11 @@ Before merging, the stack is checked for branches whose base PR was already merged on the forge. Use --no-branch-check to skip this validation. -Before each merge, waits for CI checks to pass. -Use --build-timeout to configure the maximum wait -before failing if checks are not ready. +Before each merge, waits for merge readiness: +the forge must observe the pushed head +and report that the CR is ready to merge. +Use --ready-timeout to configure the maximum wait +before failing if merge readiness is not reached. By default, a branch failure skips that branch's upstack descendants, but independent sibling branches continue. @@ -321,12 +323,12 @@ Use --fail-fast to stop the queue after the first branch failure. **Flags** * `--method=METHOD` ([:material-wrench:{ .middle title="spice.merge.method" }](/cli/config.md#spicemergemethod)): Preferred merge method. One of 'merge', 'squash', and 'rebase'. -* `--build-timeout=30m` ([:material-wrench:{ .middle title="spice.merge.buildTimeout" }](/cli/config.md#spicemergebuildtimeout)): Max time to wait for CI checks before each merge. 0 means check once. +* `--ready-timeout=30m` ([:material-wrench:{ .middle title="spice.merge.readyTimeout" }](/cli/config.md#spicemergereadytimeout)): Max time to wait for merge readiness before each merge. 0 means check once. * `--no-branch-check`: Skip stale base validation before merging. * `--fail-fast`: Stop the merge queue after the first branch failure. * `--branch=NAME`: Branch whose stack to merge -**Configuration**: [spice.merge.buildTimeout](/cli/config.md#spicemergebuildtimeout), [spice.merge.method](/cli/config.md#spicemergemethod) +**Configuration**: [spice.merge.method](/cli/config.md#spicemergemethod), [spice.merge.readyTimeout](/cli/config.md#spicemergereadytimeout) ### git-spice stack restack {#gs-stack-restack} @@ -624,7 +626,7 @@ This command acts as a local merge queue: it merges one Change Request, waits for that merge to finish, restacks and updates the next Change Request, -waits for its CI checks to pass, +waits for merge readiness on the updated Change Request, and then repeats the process. For a stack like this: @@ -642,13 +644,15 @@ Before merging, the downstack is checked for branches whose base PR was already merged on the forge. Use --no-branch-check to skip this validation. -Before each merge, waits for CI checks to pass. -Use --build-timeout to configure the maximum wait +Before each merge, waits for merge readiness: +the forge must observe the pushed head +and report that the CR is ready to merge. +Use --ready-timeout to configure the maximum wait (default: 30m, 0 means fail immediately if not ready). Between merges, the command waits for each merge to complete, restacks and updates the next PR, -waits for CI checks on the updated PR, +waits for merge readiness on the updated PR, and syncs merged branch cleanup. Use --no-wait for single branch merging @@ -658,12 +662,12 @@ when you don't want to wait for the merge to propagate. **Flags** * `--method=METHOD` ([:material-wrench:{ .middle title="spice.merge.method" }](/cli/config.md#spicemergemethod)): Preferred merge method. One of 'merge', 'squash', and 'rebase'. -* `--build-timeout=30m` ([:material-wrench:{ .middle title="spice.merge.buildTimeout" }](/cli/config.md#spicemergebuildtimeout)): Max time to wait for CI checks before each merge. 0 means check once. +* `--ready-timeout=30m` ([:material-wrench:{ .middle title="spice.merge.readyTimeout" }](/cli/config.md#spicemergereadytimeout)): Max time to wait for merge readiness before each merge. 0 means check once. * `--no-wait`: Skip polling for a single branch merge to propagate. * `--no-branch-check`: Skip stale base validation before merging. * `--branch=NAME`: Branch to start merging from -**Configuration**: [spice.merge.buildTimeout](/cli/config.md#spicemergebuildtimeout), [spice.merge.method](/cli/config.md#spicemergemethod) +**Configuration**: [spice.merge.method](/cli/config.md#spicemergemethod), [spice.merge.readyTimeout](/cli/config.md#spicemergereadytimeout) ### git-spice downstack edit {#gs-downstack-edit} @@ -1133,16 +1137,18 @@ Use --branch to merge a different branch. The branch must be based directly on trunk. To merge a stacked branch, use 'gs downstack merge'. -Before merging, waits for CI checks to pass. -Use --build-timeout to configure the maximum wait. +Before merging, waits for merge readiness: +the forge must observe the pushed head +and report that the CR is ready to merge. +Use --ready-timeout to configure the maximum wait. **Flags** * `--method=METHOD` ([:material-wrench:{ .middle title="spice.merge.method" }](/cli/config.md#spicemergemethod)): Preferred merge method. One of 'merge', 'squash', and 'rebase'. -* `--build-timeout=30m` ([:material-wrench:{ .middle title="spice.merge.buildTimeout" }](/cli/config.md#spicemergebuildtimeout)): Max time to wait for CI checks before each merge. 0 means check once. +* `--ready-timeout=30m` ([:material-wrench:{ .middle title="spice.merge.readyTimeout" }](/cli/config.md#spicemergereadytimeout)): Max time to wait for merge readiness before each merge. 0 means check once. * `--branch=NAME`: Branch to merge -**Configuration**: [spice.merge.buildTimeout](/cli/config.md#spicemergebuildtimeout), [spice.merge.method](/cli/config.md#spicemergemethod) +**Configuration**: [spice.merge.method](/cli/config.md#spicemergemethod), [spice.merge.readyTimeout](/cli/config.md#spicemergereadytimeout) ### git-spice branch submit {#gs-branch-submit}