Skip to content

Add worktree checkout to gh issue develop - #14136

Merged
babakks merged 6 commits into
trunkfrom
270-gh-issue-develop-worktree
Aug 21, 2026
Merged

Add worktree checkout to gh issue develop#14136
babakks merged 6 commits into
trunkfrom
270-gh-issue-develop-worktree

Conversation

@sergiou87

@sergiou87 sergiou87 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

gh issue develop --checkout currently checks a linked development branch out in the caller's current working copy. This makes it difficult to start work on an issue without interrupting whatever branch is already checked out there.

This change adds --worktree <path> to gh issue develop. When it is combined with --checkout, the command creates the linked branch as usual but checks it out in a new Git worktree at the requested path. The original working copy remains on its current branch.

The implementation supports both checkout paths used by issue develop:

  • If the linked branch does not exist locally, the command creates it in the new worktree as a tracking branch from the matching remote.
  • If the linked branch already exists locally, the command adds a worktree for it and attempts the same fast-forward-only pull used by the existing checkout flow, scoped to the new worktree with git -C.

The remote is still selected from the linked branch repository, so --branch-repo works when the issue and development branch belong to different repositories. Relative worktree paths are normalized to absolute paths, and paths containing spaces are passed to Git as individual arguments.

The new flag requires --checkout, rejects an explicitly blank path, and is mutually exclusive with --list. Without --worktree, gh issue develop follows its existing checkout behavior.

A reusable git.Client.AddWorktree helper encapsulates the two supported Git operations:

git worktree add -- <path> <existing-branch>
git worktree add --track -b <new-branch> -- <path> <remote>/<new-branch>

Acceptance scenarios cover same-repository and cross-repository worktree creation, both local branch states, --name, --base, relative paths containing spaces, flag validation, occupied targets, and unchanged traditional checkout behavior.

How did you test this change?

Flags

Testing gh issue develop --worktree flag validation

$ ../cli/bin/gh issue develop --help
Manage linked branches for an issue.

When using the --base flag, the new development branch will be created from the specified
remote branch. The new branch will be configured as the base branch for pull requests created using
gh pr create.

USAGE
gh issue develop { | } [flags]

FLAGS
-b, --base string Name of the remote branch you want to make your new branch from
--branch-repo string Name or URL of the repository where you want to create your new branch
-c, --checkout Checkout the branch after creating it
-l, --list List linked branches for the issue
-n, --name string Name of the branch to create
--worktree path Check out the branch into a worktree at the given path

INHERITED FLAGS
--help Show help for command
-R, --repo [HOST/]OWNER/REPO Select another repository using the [HOST/]OWNER/REPO format

EXAMPLES

List branches for issue 123

$ gh issue develop --list 123

List branches for issue 123 in repo cli/cli

$ gh issue develop --list --repo cli/cli 123

Create a branch for issue 123 based on the my-feature branch

$ gh issue develop 123 --base my-feature

Create a branch for issue 123 and check it out

$ gh issue develop 123 --checkout

Create a branch for issue 123 and check it out in a worktree

$ gh issue develop 123 --checkout --worktree /path/to/worktree

Create a branch in repo monalisa/cli for issue 123 in repo cli/cli

$ gh issue develop 123 --repo cli/cli --branch-repo monalisa/cli

LEARN MORE
Use gh <command> <subcommand> --help for more information about a command.
Read the manual at https://cli.github.com/manual
Learn about exit codes using gh help exit-codes
Learn about accessibility experiences using gh help accessibility

$ ../cli/bin/gh issue develop 1 --repo sergiou87/test-repo --worktree ../unused-worktree
--worktree requires --checkout

Usage: gh issue develop { | } [flags]

Flags:
-b, --base string Name of the remote branch you want to make your new branch from
--branch-repo string Name or URL of the repository where you want to create your new branch
-c, --checkout Checkout the branch after creating it
-l, --list List linked branches for the issue
-n, --name string Name of the branch to create
--worktree path Check out the branch into a worktree at the given path

[exit status: 1]
PASS: rejected with expected error

$ ../cli/bin/gh issue develop 1 --repo sergiou87/test-repo --list --worktree ../unused-worktree
specify only one of --list or --worktree

Usage: gh issue develop { | } [flags]

Flags:
-b, --base string Name of the remote branch you want to make your new branch from
--branch-repo string Name or URL of the repository where you want to create your new branch
-c, --checkout Checkout the branch after creating it
-l, --list List linked branches for the issue
-n, --name string Name of the branch to create
--worktree path Check out the branch into a worktree at the given path

[exit status: 1]
PASS: rejected with expected error

$ ../cli/bin/gh issue develop 1 --repo sergiou87/test-repo --checkout --worktree ''
--worktree cannot be blank

Usage: gh issue develop { | } [flags]

Flags:
-b, --base string Name of the remote branch you want to make your new branch from
--branch-repo string Name or URL of the repository where you want to create your new branch
-c, --checkout Checkout the branch after creating it
-l, --list List linked branches for the issue
-n, --name string Name of the branch to create
--worktree path Check out the branch into a worktree at the given path

[exit status: 1]
PASS: rejected with expected error

$ git -C ../cli diff --exit-code trunk...HEAD -- docs share
PASS: command help contains new source documentation; generated docs are unchanged

=== Focused unit coverage ===

$ (cd ../cli && go test ./git -run ^TestClientAddWorktree$)
ok github.com/cli/cli/v2/git (cached)

$ (cd ../cli && go test ./pkg/cmd/issue/develop -run ^(TestNewCmdDevelop|TestDevelopRun|TestCheckoutBranchWorktree)$)
ok github.com/cli/cli/v2/pkg/cmd/issue/develop (cached)

All flag and help checks passed.

Basic functionality

=== New local branch from remote; relative path with spaces ===

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --name issue-develop-wt-20260813140601-15620-new --base main --checkout --worktree ../issue\ develop\ worktrees\ 20260813140601-15620/new\ branch
github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-new
From https://github.com/sergiou87/test-repo

  • [new branch] issue-develop-wt-20260813140601-15620-new -> origin/issue-develop-wt-20260813140601-15620-new

$ git branch --show-current
commit-msg-demo
PASS

$ git -C ../issue\ develop\ worktrees\ 20260813140601-15620/new\ branch rev-parse --show-toplevel
/Users/sergiou87/Developer/GitHub/issue develop worktrees 20260813140601-15620/new branch
PASS

$ git -C ../issue\ develop\ worktrees\ 20260813140601-15620/new\ branch branch --show-current
issue-develop-wt-20260813140601-15620-new
PASS

$ git -C ../issue\ develop\ worktrees\ 20260813140601-15620/new\ branch rev-parse --abbrev-ref @{upstream}
origin/issue-develop-wt-20260813140601-15620-new
PASS

$ git config --get branch.issue-develop-wt-20260813140601-15620-new.gh-merge-base
main
PASS

$ git worktree list
/Users/sergiou87/Developer/GitHub/test-repo 78b771c [commit-msg-demo]
/Users/sergiou87/Developer/GitHub/issue develop worktrees 20260813140601-15620/new branch 076bfa3 [issue-develop-wt-20260813140601-15620-new]
PASS: caller checkout unchanged; absolute worktree uses named branch and tracks origin

=== Fresh-worktree scope; rerun delegates failure to git ===

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --name issue-develop-wt-20260813140601-15620-new --checkout --worktree ../issue\ develop\ worktrees\ 20260813140601-15620/new\ branch
github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-new
failed to run git: Preparing worktree (checking out 'issue-develop-wt-20260813140601-15620-new')
fatal: '/Users/sergiou87/Developer/GitHub/issue develop worktrees 20260813140601-15620/new branch' already exists
[exit status: 1]
PASS: git rejected target as expected

=== Existing local branch reused in a new worktree ===

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --name issue-develop-wt-20260813140601-15620-existing --base main
github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-existing
From https://github.com/sergiou87/test-repo

  • [new branch] issue-develop-wt-20260813140601-15620-existing -> origin/issue-develop-wt-20260813140601-15620-existing

$ git branch --track issue-develop-wt-20260813140601-15620-existing origin/issue-develop-wt-20260813140601-15620-existing
branch 'issue-develop-wt-20260813140601-15620-existing' set up to track 'origin/issue-develop-wt-20260813140601-15620-existing'.

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --name issue-develop-wt-20260813140601-15620-existing --base main --checkout --worktree ../issue\ develop\ worktrees\ 20260813140601-15620/existing\ branch
github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-existing
From https://github.com/sergiou87/test-repo

  • branch issue-develop-wt-20260813140601-15620-existing -> FETCH_HEAD
    Already up to date.

$ git branch --show-current
commit-msg-demo
PASS

$ git -C ../issue\ develop\ worktrees\ 20260813140601-15620/existing\ branch branch --show-current
issue-develop-wt-20260813140601-15620-existing
PASS

$ git -C ../issue\ develop\ worktrees\ 20260813140601-15620/existing\ branch rev-parse --abbrev-ref @{upstream}
origin/issue-develop-wt-20260813140601-15620-existing
PASS

$ git config --get branch.issue-develop-wt-20260813140601-15620-existing.gh-merge-base
main
PASS
PASS: existing local branch checked out and fast-forwarded in worktree

=== Occupied path delegates failure to git ===

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --name issue-develop-wt-20260813140601-15620-occupied --base main
github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-occupied
From https://github.com/sergiou87/test-repo

  • [new branch] issue-develop-wt-20260813140601-15620-occupied -> origin/issue-develop-wt-20260813140601-15620-occupied

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --name issue-develop-wt-20260813140601-15620-occupied --checkout --worktree ../issue\ develop\ worktrees\ 20260813140601-15620/occupied\ path
github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-occupied
failed to run git: Preparing worktree (new branch 'issue-develop-wt-20260813140601-15620-occupied')
fatal: '/Users/sergiou87/Developer/GitHub/issue develop worktrees 20260813140601-15620/occupied path' already exists
[exit status: 1]
PASS: git rejected target as expected

$ sed -n 1p ../issue\ develop\ worktrees\ 20260813140601-15620/occupied\ path/existing-file
do not overwrite
PASS

=== Existing behavior without --worktree, isolated in disposable clone ===

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --name issue-develop-wt-20260813140601-15620-legacy --base main
github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-legacy
From https://github.com/sergiou87/test-repo

  • [new branch] issue-develop-wt-20260813140601-15620-legacy -> origin/issue-develop-wt-20260813140601-15620-legacy

$ git clone --no-local . ../issue\ develop\ worktrees\ 20260813140601-15620/legacy\ clone
Cloning into '../issue develop worktrees 20260813140601-15620/legacy clone'...

$ git -C ../issue\ develop\ worktrees\ 20260813140601-15620/legacy\ clone remote set-url origin https://github.com/sergiou87/test-repo.git

$ (cd ../issue\ develop\ worktrees\ 20260813140601-15620/legacy\ clone && env GH_PAGER=cat /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --name issue-develop-wt-20260813140601-15620-legacy --checkout)
github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-legacy
From https://github.com/sergiou87/test-repo

  • [new branch] issue-develop-wt-20260813140601-15620-legacy -> origin/issue-develop-wt-20260813140601-15620-legacy

$ git -C ../issue\ develop\ worktrees\ 20260813140601-15620/legacy\ clone branch --show-current
issue-develop-wt-20260813140601-15620-legacy
PASS

$ git -C ../issue\ develop\ worktrees\ 20260813140601-15620/legacy\ clone rev-parse --abbrev-ref @{upstream}
origin/issue-develop-wt-20260813140601-15620-legacy
PASS

$ sh -c git\ -C\ "../issue\ develop\ worktrees\ 20260813140601-15620/legacy\ clone"\ worktree\ list\ --porcelain\ |\ grep\ -c\ '^worktree\ '
1
PASS
PASS: no-worktree checkout still switches checkout and creates no extra worktree

=== Linked branches created during test ===

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --list
issue-develop-wt-20260813140601-15620-new https://github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-new
issue-develop-wt-20260813140601-15620-existing https://github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-existing
issue-develop-wt-20260813140601-15620-occupied https://github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-occupied
issue-develop-wt-20260813140601-15620-legacy https://github.com/sergiou87/test-repo/tree/issue-develop-wt-20260813140601-15620-legacy

$ git branch --show-current
commit-msg-demo
PASS

PASS: all live same-repository scenarios passed; caller branch and status unchanged.

Cross-repo support

Issue repository: sergiou87/test-repo Branch repository: sergiou87-org/test-repo Branch repository base: main

$ git remote add issue-develop-cross-20260813144150-88754 https://github.com/sergiou87-org/test-repo.git

$ git remote -v
issue-develop-cross-20260813144150-88754 https://github.com/sergiou87-org/test-repo.git (fetch)
issue-develop-cross-20260813144150-88754 https://github.com/sergiou87-org/test-repo.git (push)
origin https://github.com/sergiou87/test-repo.git (fetch)
origin https://github.com/sergiou87/test-repo.git (push)

=== Cross-repository worktree checkout ===

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --branch-repo sergiou87-org/test-repo --name issue-develop-wt-20260813144150-88754-cross-repo --base main --checkout --worktree ../issue\ develop\ cross\ repo\ 20260813144150-88754/cross\ repo\ branch
github.com/sergiou87-org/test-repo/tree/issue-develop-wt-20260813144150-88754-cross-repo
From https://github.com/sergiou87-org/test-repo

  • [new branch] issue-develop-wt-20260813144150-88754-cross-repo -> issue-develop-cross-20260813144150-88754/issue-develop-wt-20260813144150-88754-cross-repo

$ git branch --show-current
commit-msg-demo
PASS

$ git -C ../issue\ develop\ cross\ repo\ 20260813144150-88754/cross\ repo\ branch rev-parse --show-toplevel
/Users/sergiou87/Developer/GitHub/issue develop cross repo 20260813144150-88754/cross repo branch
PASS

$ git -C ../issue\ develop\ cross\ repo\ 20260813144150-88754/cross\ repo\ branch branch --show-current
issue-develop-wt-20260813144150-88754-cross-repo
PASS

$ git -C ../issue\ develop\ cross\ repo\ 20260813144150-88754/cross\ repo\ branch rev-parse --abbrev-ref @{upstream}
issue-develop-cross-20260813144150-88754/issue-develop-wt-20260813144150-88754-cross-repo
PASS

$ git config --get branch.issue-develop-wt-20260813144150-88754-cross-repo.gh-merge-base
main
PASS

$ git remote get-url issue-develop-cross-20260813144150-88754
https://github.com/sergiou87-org/test-repo.git
PASS

$ git worktree list
/Users/sergiou87/Developer/GitHub/test-repo 78b771c [commit-msg-demo]
/Users/sergiou87/Developer/GitHub/issue develop cross repo 20260813144150-88754/cross repo branch 076bfa3 [issue-develop-wt-20260813144150-88754-cross-repo]

$ /Users/sergiou87/Developer/GitHub/cli/bin/gh issue develop 1 --repo sergiou87/test-repo --list
issue-develop-wt-20260813144150-88754-cross-repo https://github.com/sergiou87-org/test-repo/tree/issue-develop-wt-20260813144150-88754-cross-repo

PASS: cross-repo branch tracks remote matching --branch-repo; caller checkout unchanged.

Key points

  • This change creates fresh worktrees only. It does not attempt to detect or reuse an existing worktree at the requested path. Occupied paths and branches already checked out elsewhere are delegated to Git so users receive native git worktree add errors.
  • The broader gh pr checkout --worktree implementation includes reuse, force, and detached-HEAD behavior. Refactoring that state machine was intentionally avoided because issue develop only needs the two fresh-worktree operations described above.
  • Existing local branches retain the current warning-only behavior when they cannot be fast-forwarded. The pull runs inside the newly added worktree rather than the caller's working copy.
  • The command source remains the documentation authority; the new flag and example will flow into generated command documentation.

Notes for reviewers

Start with checkoutBranch in pkg/cmd/issue/develop/develop.go, which contains the behavior split between traditional checkout and worktree checkout. Then review git.Client.AddWorktree in git/client.go for the exact Git argument forms.

The command-constructor tests cover flag curation and validation, while the checkout tests cover existing and missing local branches, cross-repository remote selection, paths containing spaces, and Git error propagation.

Authorship and follow-up

Who wrote this:

  • A human wrote it.
  • An agent wrote it under close human direction.
  • An agent wrote it independently, and no human has guided the implementation beyond the initial prompt.

Who answers review comments:

  • @sergiou87 will read and reply directly.
  • An agent will draft replies and @username will read them before they are posted.
  • Nobody has explicitly committed to replying.

Teach gh issue develop to accept --worktree <path> together with --checkout, allowing a linked development branch to be checked out in a new Git worktree without changing the branch in the caller's current working tree.

Validate that --worktree is non-empty, requires --checkout, and cannot be combined with --list. Resolve relative targets to absolute paths before invoking Git so later worktree-local operations use one stable path while paths containing spaces remain valid.

Add git.Client.AddWorktree to handle both supported branch states. Existing local branches use git worktree add and are then fast-forwarded with git -C <worktree> pull --ff-only. Missing local branches are created in the worktree as tracking branches from the remote selected for the linked branch repository, including --branch-repo workflows.

Keep the existing checkout behavior unchanged when --worktree is absent, and let git worktree add surface native errors for occupied paths or branches already checked out elsewhere.

Cover flag parsing and validation, relative and spaced paths, existing and new local branches, cross-repository remote selection, command construction, and Git error propagation.
Copilot AI balanced review requested due to automatic review settings August 13, 2026 10:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds worktree-based checkout support to gh issue develop --checkout.

Changes:

  • Adds and validates the --worktree flag.
  • Creates or updates branches inside a new worktree.
  • Adds Git helper and unit tests.
Show a summary per file
File Description
pkg/cmd/issue/develop/develop.go Implements worktree checkout behavior.
pkg/cmd/issue/develop/develop_test.go Tests flag handling and checkout flows.
git/client.go Adds the AddWorktree helper.
git/client_test.go Tests generated Git commands and errors.

Review details

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread git/client_test.go Outdated
Comment thread pkg/cmd/issue/develop/develop_test.go Outdated
Co-authored-by: Copilot Autofix powered by AI <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@sergiou87
sergiou87 marked this pull request as ready for review August 13, 2026 15:04
@sergiou87
sergiou87 requested a review from a team as a code owner August 13, 2026 15:04
@sergiou87
sergiou87 requested a review from babakks August 13, 2026 15:04
eshaanshah1 added a commit to eshaanshah1/shepherd that referenced this pull request Aug 14, 2026
GitHub becomes an extension. A task already carries the join GitHub
needs — one branch, named after the task's slug, checked out in every
one of its repos — so "which PRs belong to this task" is a lookup
rather than a guess, and everything here follows from having it.

It reaches the screen in two places and nowhere else: one glyph on the
task's row, tinted by the worst state across its PRs, and a `review`
tab showing every PR and then one PR in full.

ADR 0044 — a pane may be a contributed view. There was no non-terminal
pane in v2; a leaf of the layout tree may now carry a component. The
enforcement is the renderer never mounting TerminalPane for one,
because TerminalPane attaches on mount and attaching spawns a pty.

ADR 0045 — the app's PATH is harvested from the login shell. A
Finder-launched .app is a child of launchd and inherits
/usr/bin:/bin:/usr/sbin:/sbin, so the same exec call succeeds in
`pnpm dev` and fails with ENOENT in the shipped app. Startup now asks
`$SHELL -ilc` once and merges the answer into process.env: measured at
28 directories added in 159ms from a launchd-shaped PATH, and 0 when
launched from a terminal. Read from pingdotgg/t3code, which solves the
same problem the same way.

tasks never learns what a pull request is. The glyph arrives through a
new `tasks.cardFacts` point, so a second integration lands the same
way. Two more seams the work needed: `sessions.write`, which brackets
its paste iff the running program asked for bracketed paste, and a
real secrets store behind Electron's safeStorage.

The diff and the file tree are @pierre/diffs and @pierre/trees rather
than hand-rolled. Three things measured on the way, each a comment in
the code: GitHub sends hunks and not patches, so PatchDiff refuses one
until a `diff --git` header is synthesised; a thread naming a file is
not a thread the diff can show, since its line may have moved out of
the change; and an effect whose callback prop is a dependency does not
terminate when the parent passes an inline arrow.

PR bodies render as markdown. They used to split on blank lines, on
the argument that a body is two sentences — true of a human's PR and
false of this app's, where an agent writes the headings, the fenced
commands and the lists. Measured against cli/cli#14136.

Verified against a real repo: auth through `gh auth token`, the
GraphQL query by head branch, mergeStateStatus behind its preview
media type, and REST pulls.listFiles for patches.

Co-Authored-By: Claude Opus 5 <[email protected]>

@babakks babakks left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together, @sergiou87! 🙏

Heads up that I haven't gone through the tests yet. I suspect the implementation will shift based on the points below, and the tests will follow, so I'll hold off reviewing them until things settle.

A few things I'd like us to address:

  • Replicate the worktree safety checks from pr checkout: The symlink and other target safety validations that pr checkout performs (rejecting leaf symlinks, non-directory targets, paths inside a different repository, paths inside an existing worktree, etc.) aren't present here. We should replicate them. Rather than duplicating, I think we should extract that logic from pr checkout into a shared package, probably pkg/cmd/pr/shared, since there's already precedent for sharing logic between PRs and issues there. @tidy-dev did a great job in pr checkout for worktrees covering various use cases and security related concerns. I think the big lift here is to just extract them into the shared package and happily reuse them here.

  • Match pr checkout's worktree reuse behavior: I think users will expect issue develop and pr checkout to behave the same way around worktrees, in particular the existing worktree reuse behavior that pr checkout provides. That's another reason to pull the pr checkout internals into a shared place so both commands stay consistent.

  • Add acceptance tests: We need acceptance tests under acceptance/testdata/issue, mirroring the existing pr checkout --worktree cases in acceptance/testdata/pr/pr-checkout-worktree-*.txtar.

Comment thread pkg/cmd/issue/develop/develop.go Outdated
Comment thread pkg/cmd/issue/develop/develop_test.go Outdated
Extract worktree target validation and branch placement so issue develop inherits pr checkout safety and reuse behavior. Add unit and acceptance coverage for fresh, reused, and cross-repository worktrees.\n\nCo-authored-by: Copilot App <[email protected]>\nCopilot-Session: 2ce1b530-30dc-47e3-9c30-392f1368ddd8
Protect issue develop's existing nonfatal fast-forward behavior when reusing a linked worktree.

Co-authored-by: Copilot App <[email protected]>

Copilot-Session: 2ce1b530-30dc-47e3-9c30-392f1368ddd8
@tidy-dev

Copy link
Copy Markdown
Contributor

@babakks I’ve picked this up while Sergio is OOO and pushed updates addressing your review:

  • Extracted the pr checkout worktree safety and reuse behavior into shared code used by both commands.
  • Moved the worktree cases into TestDevelopRun.
  • Added same-repository and cross-repository acceptance coverage.
  • Updated the help example for parity with pr checkout.

The targeted, full, race, lint, and live acceptance tests pass. When you have a chance, could you please take another look?

@tidy-dev
tidy-dev requested a review from babakks August 20, 2026 17:31
Co-authored-by: Copilot App <[email protected]>
Copilot-Session: 2ce1b530-30dc-47e3-9c30-392f1368ddd8

@babakks babakks left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on, @sergiou87, and thanks to @tidy-dev for making the changes! 🎉 This is a really thorough piece of work, and I especially appreciate that you extracted the worktree logic into a shared package so gh pr checkout and gh issue develop stay in sync rather than growing two parallel implementations.

I went through the checkout.go refactor and confirmed it is a faithful, behavior preserving extraction (the path safety guard, the resolve logic, and all four command shapes are reproduced exactly), and the existing acceptance tests back that up. I also exercised the new issue develop behaviors by hand against a scratch repo: fast forwarding into a worktree, relative paths containing spaces, the current repo guard, reuse of an existing linked worktree, and the traditional no worktree checkout all work as expected. Nicely done. 🙏

One thing I would like your take on before we wrap up:

When --worktree targets an existing but non empty directory that is not a git worktree, git worktree add correctly fails, but by then CreateLinkedBranch has already run, so we are left with a linked branch on the remote and no local worktree. In pr checkout this is harmless since no branch is created, but in issue develop it leaves an orphan. Do you think ResolveWorktreeTarget should reject a non empty target up front so we fail before creating the branch, or is leaving that to git worktree add acceptable here?

Everything else is minor and I will leave those as inline notes. Thanks again for the care that went into this. 🍻

@babakks

babakks commented Aug 21, 2026

Copy link
Copy Markdown
Member

When --worktree targets an existing but non empty directory that is not a git worktree, git worktree add correctly fails, but by then CreateLinkedBranch has already run, so we are left with a linked branch on the remote and no local worktree. In pr checkout this is harmless since no branch is created, but in issue develop it leaves an orphan. Do you think ResolveWorktreeTarget should reject a non empty target up front so we fail before creating the branch, or is leaving that to git worktree add acceptable here?

Re this, I'm happy to leave it for now, or have it as a follow up. So, I think we can merge this one out.

@babakks
babakks merged commit 62e3e71 into trunk Aug 21, 2026
11 checks passed
@babakks
babakks deleted the 270-gh-issue-develop-worktree branch August 21, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants