Skip to content

feat: add interactive worktree PR checkout command (gwp)#502

Closed
wfxr wants to merge 2 commits intomainfrom
feat/pr-checkout
Closed

feat: add interactive worktree PR checkout command (gwp)#502
wfxr wants to merge 2 commits intomainfrom
feat/pr-checkout

Conversation

@wfxr
Copy link
Owner

@wfxr wfxr commented Mar 17, 2026

Check list

  • I have performed a self-review of my code
  • I have commented my code in hard-to-understand areas
  • I have added unit tests for my code
  • I have made corresponding changes to the documentation

Description

image

Add gwp command (forgit::worktree::pr / git forgit worktree_pr) that streamlines local PR review workflow:

  1. Lists open PRs via gh pr list in fzf (with title, author, draft status, relative time)
  2. Preview shows PR metadata (branch direction, commits, changed files) and diff
  3. On selection: fetches PR head, creates worktree at .wt/pr-<N>/<branch>, and cds into it
  4. Idempotent — reselecting an existing PR worktree returns its path without re-fetching

Requires GitHub CLI (gh).

Configurable via:

  • FORGIT_WORKTREE_PR_FZF_OPTS — extra fzf options
  • FORGIT_WORKTREE_PR_DIR — custom worktree directory
  • FORGIT_WORKTREE_PR_LIMIT — max PRs to list (default 100)

Type of change

  • Bug fix
  • New feature
  • Refactor
  • Breaking change
  • Test
  • Documentation change

Test environment

  • Shell
    • bash
    • zsh
    • fish
  • OS
    • Linux
    • Mac OS X
    • Windows
    • Others:

Summary by CodeRabbit

  • New Features

    • Added interactive GitHub PR checkout into git worktrees. The new gwp command lists open PRs with previews (author, creation time, commits, changed files) and automatically creates local worktrees for selected PRs.
  • Documentation

    • Updated README with gwp command documentation and shell alias configuration.

Select a PR from fzf, fetch it, and create a worktree in one step.
Requires GitHub CLI (gh). Preview shows PR metadata, commits,
changed files, and diff. Idempotent — reselecting an existing
PR worktree returns its path without re-fetching.
@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0e9385ab-c754-41e0-89da-f9cb57708847

📥 Commits

Reviewing files that changed from the base of the PR and between dc8832e and dc2138e.

📒 Files selected for processing (1)
  • bin/git-forgit
🚧 Files skipped from review as they are similar to previous changes (1)
  • bin/git-forgit

📝 Walkthrough

Walkthrough

Adds an interactive PR-to-worktree command (gwp / worktree_pr): core implementation in the main script, shell integrations (zsh/fish), completion updates, and README documentation entries for the new subcommand and options.

Changes

Cohort / File(s) Summary
Core Implementation
bin/git-forgit
Added _forgit_pr_preview() to render GH PR details and _forgit_worktree_pr() to list/select PRs, fetch PR heads, and create or return worktree paths. Registered worktree_pr in PUBLIC_COMMANDS.
Shell Integrations
forgit.plugin.zsh, conf.d/forgit.plugin.fish
Added forgit::worktree::pr wrappers that invoke git-forgit worktree_pr, change directory into the returned worktree, and registered the gwp alias / forgit_worktree_pr variable.
Shell Completions
completions/_git-forgit, completions/git-forgit.bash
Added worktree_pr to completion lists and no-op case branches so the new subcommand is recognized by zsh and bash completions.
Documentation
README.md
Added gwp entry to Full Command List, new per-command option FORGIT_WORKTREE_PR_FZF_OPTS in options table, and an alias mapping for forgit_worktree_pr=gwp; minor table alignment updates.

Sequence Diagram

sequenceDiagram
    actor User
    participant Shell as Shell (zsh/fish)
    participant Main as git-forgit:worktree_pr
    participant FZF as FZF (selector)
    participant GH as gh (GitHub CLI)
    participant Git as Git
    User->>Shell: run gwp
    Shell->>Main: invoke worktree_pr
    Main->>GH: list open PRs / pr view (preview)
    GH-->>Main: PR list / details
    Main->>FZF: present PRs with preview
    FZF->>Main: selected PR
    Main->>GH: fetch PR diff/details for preview
    GH-->>Main: PR preview
    Main->>Git: check for existing worktree / fetch PR head
    Git-->>Main: branch fetched / existing worktree path
    Main->>Git: create worktree if needed
    Git-->>Main: new worktree path
    Main-->>Shell: return worktree path
    Shell->>User: cd into worktree
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested Reviewers

  • cjappl
  • carlfriedrich
  • sandr01d

🐰 A hop, a fetch, a branch to try,
gwp brings PRs close and nigh.
Preview, pick, and then we go —
A rabbit's nudge, and worktrees grow. 🌿✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a new interactive worktree PR checkout command (gwp), which is the primary focus of this pull request.
Description check ✅ Passed The description covers most required sections including a clear summary, type of change (new feature), test environment, and checklist items. All critical information is present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/pr-checkout
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
bin/git-forgit (2)

1543-1543: Hardcoded origin remote may not work for all repository configurations.

The fetch command assumes the remote is named origin. While this is the common default, repositories may use different remote names or may have been cloned from a fork.

This is acceptable for the initial implementation since gh pr commands inherently target the repository's default remote. If issues arise, consider dynamically determining the remote:

# Example approach (for future consideration):
local remote
remote=$(gh repo view --json name -q '.name' 2>/dev/null && echo "origin" || git remote | head -1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/git-forgit` at line 1543, Replace the hardcoded "origin" in the git fetch
call by computing a remote variable and using it in the fetch; locate the fetch
invocation that uses git fetch origin "pull/${pr_number}/head:${local_branch}"
and introduce a REMOTE (or remote) variable determined from gh/git (prefer using
gh/CLI to get the repo remote when available, falling back to git remote | head
-1), then use "${REMOTE}" in the git fetch to avoid assuming the remote is named
origin while keeping existing pr_number and local_branch usage.

1531-1531: Consider escaping special characters in branch name for grep.

The branch name $local_branch is used directly in a grep regex pattern. If the branch name contains regex metacharacters (e.g., ., *, [), the match could fail or match incorrectly.

♻️ Proposed fix using grep -F for literal matching
-    if git worktree list --porcelain | grep -q "^branch refs/heads/${local_branch}$"; then
+    if git worktree list --porcelain | grep -qF "branch refs/heads/${local_branch}"; then

Note: Using -F for fixed-string matching avoids regex interpretation. The ^ and $ anchors are lost, but since refs/heads/ is a unique prefix and branch names don't contain newlines, false positives are unlikely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/git-forgit` at line 1531, The grep pattern in the git worktree check uses
an unescaped regex with ${local_branch}, which can misbehave for branch names
containing regex metacharacters; update the command that contains git worktree
list | grep -q "^branch refs/heads/${local_branch}$" to perform a literal,
whole-line match (e.g., use grep -q -xF "branch refs/heads/${local_branch}") so
the branch name is treated as a fixed string; change the line in the script
where the worktree check is performed to use grep -xF with the literal string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bin/git-forgit`:
- Line 1543: Replace the hardcoded "origin" in the git fetch call by computing a
remote variable and using it in the fetch; locate the fetch invocation that uses
git fetch origin "pull/${pr_number}/head:${local_branch}" and introduce a REMOTE
(or remote) variable determined from gh/git (prefer using gh/CLI to get the repo
remote when available, falling back to git remote | head -1), then use
"${REMOTE}" in the git fetch to avoid assuming the remote is named origin while
keeping existing pr_number and local_branch usage.
- Line 1531: The grep pattern in the git worktree check uses an unescaped regex
with ${local_branch}, which can misbehave for branch names containing regex
metacharacters; update the command that contains git worktree list | grep -q
"^branch refs/heads/${local_branch}$" to perform a literal, whole-line match
(e.g., use grep -q -xF "branch refs/heads/${local_branch}") so the branch name
is treated as a fixed string; change the line in the script where the worktree
check is performed to use grep -xF with the literal string.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dbb237cc-2143-43f5-94e2-88cb779c96f2

📥 Commits

Reviewing files that changed from the base of the PR and between 30aff90 and dc8832e.

📒 Files selected for processing (6)
  • README.md
  • bin/git-forgit
  • completions/_git-forgit
  • completions/git-forgit.bash
  • conf.d/forgit.plugin.fish
  • forgit.plugin.zsh

Branch names from PRs may contain regex metacharacters (e.g. dots),
which would cause the grep pattern match to behave incorrectly.
@sandr01d
Copy link
Collaborator

This looks neat, but I personally think that this feature is out of scope for this project. So far, every forgit command is a wrapper around git, whereas this one is mostly a wrapper around gh. We had a similar discussion in #406, where @cjappl and @carlfriedrich convinced me that the feature I suggested there was out of scope for the same reason. I think that their good arguments also apply here. Would be interested on their point of view here too.
I also don't like the fact that this command is specific to GitHub. IMO forgit should not cater to a specific git forge. Maybe it would be a better fit to have this in a separate tool that wraps around gh similar to how forgit does with git?

@wfxr
Copy link
Owner Author

wfxr commented Mar 18, 2026

Thanks for the thoughtful feedback @sandr01d, and I appreciate you referencing #406 for context. I do think there's an important distinction worth drawing here though.

The license generator in #406 had no relationship to git at all — it was purely a project management utility. gwp, on the other hand, is fundamentally a git worktree operation. The core of the command is git fetch + git worktree add; gh is only used as a data source to list open PRs and their metadata. The actual heavy lifting is pure git. So I'd argue this falls comfortably within forgit's scope of "interactive git workflows powered by fzf."

Regarding the GitHub-specific concern — I understand the principle, but I think we should be pragmatic here. GitHub hosts the vast majority of open-source projects (including forgit itself), and gh has become a de facto standard tool in most developers' toolkits.

forgit already has a pattern of gracefully integrating with external tools — bat, delta, pbcopy, tree, emoji, etc. — none are hard dependencies; they just enhance the experience when present. gh would follow the same spirit: when it's installed, you get gwp; when it's not, everything else works exactly the same. The difference is that gh enables a new command rather than enhancing an existing one's output, but the principle of optional, non-intrusive integration is the same.

That said, I'm open to hearing what @cjappl and @carlfriedrich think. If the consensus is that this crosses a line, I'd respect that — but I do believe the git-centrality of this command meaningfully differentiates it from #406.

@carlfriedrich
Copy link
Collaborator

Thanks for the ping. I am actually more on @sandr01d's side here. While I understand the difference to #406, I still also do see a difference compared to the existing forgit features.

Yes, forgit gracefully integrates optional external tools, but up to now it does so to optionally improve the user experience of its features. All of the features also work without any of these external tools.

This PR adds an optional external tool to implement a completely new feature. The feature won't work without this tool. That is a difference.

To me, indeed, this PR screems "feature creep!!". I definitely see the benefit of it, but I also think that it makes forgit stick less to the unix philosophy. That being said, I am also open to the "let's be pragmatic" argument here.

The bigger issue I see here, though: IMO the function does too much. Why does it fetch a PR AND create a worktree (a function we already have in forgit)? The new function obviously duplicates functionality.

IMO the better UI here would be to add a gfp ("fetch PR") command to fetch a PR into a local branch. Then use forgit's other functions to decide what to do with it (check it out or create a worktree for it).

(On a side note: that's actually something I already do in my own workflow using navi (which is also fzf-based). See my navi cheat for fetching GitHub PRs for reference, which simply queries the GitHub API via curl. This does not depend on the gh binary and hence works on all platforms (still GitHub-dependent, though). Really tiny code, but it works for what I need.).

Maybe we can reconsider the design from this perspective, and maybe we find a solution which fits better into forgit's scope while still bringing the benefit @wfxr is looking for.

@carlfriedrich
Copy link
Collaborator

Screenshot how it looks with my navi workflow:

grafik

@wfxr
Copy link
Owner Author

wfxr commented Mar 18, 2026

After reflecting on the discussion, I think you're right that this sits outside forgit's scope. I'll keep it in my own dotfiles for now. Thanks for the thoughtful feedback — it actually helped clarify the boundary nicely.

Who knows, maybe one day there'll be a forgh project for interactive gh workflows. 😄 Closing this one.

@wfxr wfxr closed this Mar 18, 2026
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.

3 participants