worktrunk

max-sixty活跃维护

用 Rust 写的 Git worktree 管理命令行工具,专为并行 AI 编码代理设计,让多个 agent 各自在独立工作树里干活、互不干扰。

Worktrunk is a CLI for Git worktree management, designed for parallel AI agent workflows

v0.81.0发布于 2026-10-20 21:28 UTC12,297star +353 / 7 天480fork同步于 2026-11-10 05:28 UTC

怎么上手

由 README 提炼,执行前请确认
brew install worktrunk && wt config shell install
硬件要求按 README 说明

README 未说明,纯 CLI 工具,无 GPU 或显存要求

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

用分支名管理 git worktree 的 CLI,让多个 AI agent 并行开发互不干扰

用 Rust 写的 Git worktree 管理命令行工具,专为并行 AI 编码代理设计,让多个 agent 各自在独立工作树里干活、互不干扰。

核心能力

  • 用 wt switch 按分支名切换或创建工作树
  • wt switch -c -x claude 创建后直接启动 agent
  • wt list 展示各分支改动、领先提交与远程状态
  • wt merge 一条命令完成 squash、rebase、合并与清理
  • 钩子自动化创建与合并流程,并共享构建缓存

容易踩的坑

装完必须执行 wt config shell install,否则命令无法切换目录;Windows 上 wt 与 Windows Terminal 冲突,Winget 版需用 git-wt;LLM 提交信息与 AI 摘要需另行配置模型

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v0.81.0
  1. v0.81.00.81.0最新正式版2026-10-20 · 2026-10-20 21:28 UTC

    Release Notes

    Improved

    • Hooks can prompt: hooks that run serially in the foreground (pre-* hooks, wt hook <type> --foreground) inherit wt's stdin, so gum confirm 'trust this worktree?' && mise trust works as a hook. Concurrent and background hooks read EOF. (Breaking: hooks no longer receive their context as JSON on stdin; pass values as template arguments, or whole objects with the new tojson filter. wt step for-each keeps its JSON.) Docs (#3129, #4451, #4524, thanks @endigma for #3093)

    • Experimental wt switch --path: places one worktree outside the worktree-path template and keeps its branch name, as in wt switch --create feature/JIRA-1234 --path ../dark-mode. Commands reach it by branch or by path. Docs (#4317, thanks @christowiz for #1982)

    • wt merge --branch: merges another worktree's branch without leaving the current worktree. Commit, squash, rebase, and hooks run in the source worktree, and cleanup removes it. Docs (#4360, thanks @liuyinchu)

    • OpenCode 2 worktrees go through Worktrunk: after re-running wt config plugins opencode install, worktrees OpenCode creates and removes use wt switch --create and wt remove, as the Claude Code plugin's do. OpenCode's worktree.directory stops applying, and new branches start from the default branch. Like the Claude Code plugin, it opens an existing branch's worktree, which OpenCode's remove then removes. Docs (#4489, thanks @rzane for #4488)

    • Faster bulk removal: removal hands files to one background deleter per repository, not one rm -rf each, and deletes read-only directories it used to leave in .git/wt/trash. wt step prune no longer stalls when two worktrees share a directory name, as Codex worktrees do. (Breaking: prune runs pre-remove hooks concurrently; hooks writing shared state must coordinate.) (#4516, #4361, #4337)

    • wt switch - returns to the worktree it left, including a detached worktree or the second checkout of a branch, and wt remove - targets the same worktree. Pre-switch hooks see the resolved destination, so wt switch origin/feature passes branch=feature. (Breaking: from a bare repository's directory, commands given no argument act on the worktree of the branch HEAD names.) (#4497, #4496, #4521)

    • Template errors name the variable: {{ brnch }} reports undefined variable brnch and suggests branch. (Breaking: filters such as sanitize reject an undefined input, so {{ upstream | sanitize }} needs a guard or default on a branch with no upstream.) (#4350, #4440)

    • Templates use MiniJinja 3 (Breaking): objects and lists render Python-style ({'port': 3000}) instead of as JSON, so use | tojson where JSON is needed; round rounds half to even; title capitalizes fewer word boundaries (feat/x-y becomes Feat/x-Y); division by zero errors. ([#4442](https://github.com/max-sixty/worktrunk/p

  2. v0.80.00.80.02026-09-27 · 2026-09-27 21:18 UTC
    查看更新说明

    Release Notes

    Improved

    • wt list and the wt switch picker settle faster in repos with many worktrees: rows fill from the top down, and on macOS with core.fsmonitor=true no daemon start is forked for a worktree whose daemon is already running. On a 50-worktree repo the picker's per-row work starts at 47ms, down from 120ms. (#4286, #4290, #4288, #4284)

    • CI status skips branches that were never pushed: wt list --full, the picker, and the statusline no longer query the forge for a local branch no remote has, so a repo with 239 local branches made 25 gh calls instead of 249. A PR opened from a branch another clone pushed shows once you fetch. GitLab looks up its project ID once per command, not per row. (#4289, #4216)

    • Leaner Codex commit generation: the recommended Codex command moves to gpt-6-luna with reasoning off and unneeded Codex features disabled, so each message sends fewer input tokens. Accepting the first-run Codex prompt now also creates ~/.codex/worktrunk-commit-instructions.txt, which the command reads. Docs (#4261)

    • Releases ship a signed packslip: each GitHub release includes packslip.sigstore.json, a manifest of the archives' digests, platforms, completions, and agent skills, signed by the release workflow so installers such as mise can verify downloads before unpacking. (#4301, thanks @jdx)

    Fixed

    • A squash whose commit fails keeps the branch: when a git commit hook or signing stopped the commit in wt merge or wt step squash, the branch was left at the merge base with its changes staged, recoverable from the reflog. Now the branch is untouched. (Breaking: git commit hooks run on a detached HEAD during a squash.) (#4206, thanks @Bennyjitsu for finding it in #4193)

    • wt step diff when local main lags origin/main: for a branch built on the newer origin/main, the diff included the upstream commits in between. It now uses the same base as wt merge. (#4281, thanks @starlightromero, whose #3519 reported the same case for wt merge)

    • Submodules with an ignore setting: when submodule.<name>.ignore or diff.ignoreSubmodules hid uncommitted changes inside a submodule, wt remove and wt step prune read the worktree as clean and removed it along with them; they now keep it. With ignore=all, wt step commit and squashes now commit a staged submodule pointer instead of leaving it staged. (#4252, #4192, #4206, #4303, thanks @Duang777)

    • Worktrees whose .git was deleted but whose directory remains (macOS $TMPDIR cleanup can do this): wt remove <branch> failed, and wt step prune skipped or errored on them. Both now unregister the worktree and leave its files; one with staged changes or an operation in progress is kept by wt step prune, and refused by wt remove without --force. (#4228, #4226, #4302)

    • wt merge --no-squash with a stage mode that stages nothing: with --stage=none and only unstaged changes

  3. v0.79.00.79.02026-09-21 · 2026-09-21 01:01 UTC
    查看更新说明

    Release Notes

    Improved

    • wt switch --no-cd -x <program> runs the program in the worktree: it started in the invoking directory, so a program meant for the new worktree had to be handed {{ worktree_path }}. --no-cd now governs only your shell — wt switch feature --no-cd -x code -- . opens the worktree in an editor and leaves your terminal put. (Breaking: a program that wanted the invoking directory needs an explicit cd.) Fixes #4042. (#4179, thanks @yajo for reporting)

    • The picker's preview tabs show which have content: a tab's label is dim when that tab is empty for the selected row, and underlined when it's the active tab. Availability sat on the tab's number alone, so every inactive label read dim whatever the row held. (#4174)

    • Messages name the destination, and end with the command to run: without shell integration, wt merge and wt remove printed Cannot change directory — shell integration not installed and no path; the warning now names the worktree, as wt switch already did. "Cannot determine default branch" had three wordings, and three recovery hints put their command first rather than last. (#4163, #4155, thanks @blrain3 for reporting)

    • /wt-switch-create no longer stalls at Claude Code's path-entry dialog: Claude Code confirms every entry outside .claude/worktrees/, which is every Worktrunk worktree, so a background session waited for someone to attach. The plugin now approves entry into a worktree at its worktree-path location, and reads a bare name as the repo or the branch. (#4158, #4159)

    • wt config update --output accepts the config being migrated: it refused that path when the migration dropped approved-commands. Those approvals now move to approvals.toml first, exactly as the in-place update moves them. (#4204)

    Fixed

    • wt merge on a dirty worktree describes what it commits: the generated message covered the branch's earlier commits and said nothing about the work the merge had just staged into the same commit. wt step squash --dry-run now previews what the run would commit, as wt step commit --dry-run does. (#4178)

    • A config save no longer rewrites the rest of your config: declining a prompt, or setting the commit-generation command, rewrote the whole file, dropping values written at their default and re-spelling hook pipelines and dropping their comments. It also reloaded that file over the in-memory config, so wt step commit --config-set 'commit.stage="none"' staged everything once it had asked about commit generation. (#4160, #4156, #4151)

    • Diff display config no longer reaches Worktrunk's prompts or its integration check: color.ui = always and diff.external reached the commit, squash, and summary prompts. Two corner cases also made wt remove and wt step prune delete an unmerged branch: diff.relative set and a Worktrunk command run from a subdirectory the branch's changes sat entirely outside, or submodule.<name>.ignore = all (git config or .gitmodules) and a submodule bump as its only change. (#4146, #4147)

    • Auto-staging names a wholly untracked directory once: since 0.78.0 the warning expanded each one into its files, so wt step commit, wt step squash, and wt merge lis

  4. v0.78.00.78.02026-09-16 · 2026-09-16 21:04 UTC
    查看更新说明

    Release Notes

    Improved

    • Hook scripts read renamed context keys: the JSON piped to every hook drops worktree, repo_root, main_worktree, and main_worktree_path in favour of worktree_path, repo_path, repo, and primary_worktree_path. Config templates migrate on load, so hooks written with the old names keep working; scripts reading the JSON, --execute templates, and --var overrides need updating. (Breaking.) (#4080)

    • wt config plugins pi installs for Pi; oh-my-pi moves to wt config plugins omp: pi install wrote an oh-my-pi hook, which Pi never loads; Pi users re-run it to get a Pi extension. Existing oh-my-pi hooks keep working, listed as outdated until wt config plugins omp install. (Breaking: scripts that run wt config plugins pi for oh-my-pi need omp.) Docs (#4135, thanks @ashebanow)

    • Interactive prompts no longer open with a blank line: wt config shell install, wt config plugins claude install, and the commit-message setup offer in wt merge began with one. (#4059)

    • wt config show gives a single wt config shell install hint: an outdated wrapper, a fish wrapper at the deprecated conf.d path, and missing fish completions each printed their own hint. The zsh compinit snippet is now syntax-highlighted. (#4059)

    • wt config update --output <path> confirms the write: it now prints ✓ Wrote user config migration @ ~/migrated.toml where it printed nothing. --output=- stays silent. (#4053)

    Fixed

    • Removal no longer discards untracked files hidden by status.showUntrackedFiles = no: under that setting, a worktree holding only untracked files looked clean, so wt remove, wt merge, and wt step prune removed it without --force. Safety checks now always count untracked files, and auto-staging warnings list every file they stage. (#4111, thanks @Duang777)

    • wt remove and wt merge check the right worktree under an inherited GIT_DIR: run from a !wt git alias or a git hook, which pass GIT_DIR down, wt remove could delete a dirty worktree without --force, and wt merge could refuse a clean merge. Both now check the worktree they act on. (#4082, thanks @Duang777)

    • wt merge no longer deletes a worktree locked with git worktree lock: the merge completes and prints Worktree preserved (locked). Every Worktrunk removal refuses a locked worktree, even with --force; run git worktree unlock <path> first. (#4073, thanks @Duang777)

    • wt config update no longer overwrites a config edited while its prompt is open: it wrote the pre-prompt snapshot over the newer file and reported success. It now fails with the path and leaves the newer file; re-running previews the current contents. (#4127, thanks @zach-hammad-vs for reporting)

    • wt step prune no longer deletes a branch created minutes ago from an older commit: --min-age aged a branch without a worktree by its commit's date, so a new branch off a day-old commit was pruned on the next run. Age now comes from the branch's oldest reflog entry. (#4077)

    • A comment or blank line above an inline config section no longer breaks the config: a save or migration that rewrote commit = { … } as [commit] put that line inside the brackets. wt switch, wt merge, wt remove, and several wt step commands then failed with Failed to load config; wt list warned and ran without the config. Saves also d

  5. v0.77.00.77.02026-09-08 · 2026-09-08 22:12 UTC
    查看更新说明

    Release Notes

    Improved

    • wt list sizes and aligns its columns to the terminal width: one long branch name no longer sizes the Branch column for every row — it caps at 32 characters and elides, so a narrow terminal keeps its other columns instead of degenerating into a branch list. Remote⇅ stops holding blank space open in a repo with no remote, and alignment now follows the value type. --format=json still carries the whole name. (#3998)

    • wt list names what it hid and marks detached worktrees: the summary reads hidden: Path, Commit instead of counting columns, a detached worktree shows ⊘ rather than borrowing ⚑, and a prunable row leaves its cells blank instead of showing loading dots that never resolve. (#3998)

    • wt list --format=json gains a marker field and stops reporting detached twice: the branch marker set by wt config state marker is now readable without parsing it out of symbols, and state no longer reports a detached worktree as branch_worktree_mismatch — the sibling detached boolean carries it. (#3998)

    • wt list --format=json now defaults to schema 2: callers get the envelope with repository metadata and orthogonal per-item facts without configuring [list] json-schema. wt list statusline --format=json follows the same key. Set json-schema = 1 to retain the original bare-array format. (Breaking.) (#4038)

    • wt config show fails on a broken config: it exits 1 on an unreadable or invalid config, an invalid [list] columns, or an invalid approvals.toml. Unknown keys and deprecations stay warnings and still exit 0. A new APPROVALS section counts project commands awaiting approval. (Breaking: it always exited 0 before.) (#3999)

    • Config migration output names its destination: wt config update --output <path> writes the migration artifact to that file instead of applying it in place, and --output=- writes it to stdout. Output mode includes project config when run from a linked worktree. (Breaking: --print was removed.) (#4021)

    • Pi joins the agent integrations: wt config plugins pi install writes an activity hook to ~/.omp/agent/hooks/pre/worktrunk.ts, so Pi sessions show 🤖/💬 markers in wt list like Claude Code, Codex, OpenCode, and Gemini. $PI_CONFIG_DIR, $PI_CODING_AGENT_DIR, and named $OMP_PROFILE/$PI_PROFILE profiles are honored. Docs (#3594, thanks @adity982, and @ortonomy for the request)

    • wt config plugins codex install installs the plugin too: it previously registered the marketplace and left you to run /plugins in Codex yourself. Uninstall follows for both Codex and Claude Code, removing the plugin and then the marketplace, so the two commands are inverses. (Breaking: Codex uninstall previously left an installed plugin alone; Claude uninstall now also removes the marketplace.) (#4019)

    • wt step copy-ignored says whether it reflinked or copied: the summary now reads (reflinked, no extra disk), (full copy), or a partial count, so the same 29.5 GB line distinguishes a free copy from one that actually wrote the bytes. --format=json gains reflinked and written. (#4025)

    • wt config update removes keys its destination cannot hold: migrating [select] or [commit-generation] drops keys the new section has no field for, reporting each one. Otherwise the key landed at a path the user never typed and warned on every command, with no way to clear it. ([#3994](https://github.com/max-si

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Docs License: MIT OR Apache-2.0 CI Codecov Stars maintained with tend

    November 2026: Worktrunk was released at the start of the year, and has quickly become the most popular git worktree manager. It's built with love (there's no slop!). Please let me know any frictions at all; I'm intensely focused on continuing to make Worktrunk excellent, and the biggest help is folks posting problems they perceive.

    Worktrunk is a CLI for git worktree management, designed for running AI agents in parallel.

    Worktrunk's three core commands make worktrees as easy as branches. Plus, Worktrunk has a bunch of quality-of-life features to simplify working with many parallel changes, including hooks to automate local workflows & copy-on-write build caches.

    A quick demo:

    Worktrunk Demo

    📚 Full documentation at worktrunk.dev 📚

    Context: git worktrees

    AI agents like Claude Code and Codex can handle longer tasks without supervision, such that it's possible to manage 5-10+ in parallel. Git's native worktree feature gives each agent its own working directory, so they don't step on each other's changes.

    But the git worktree UX is clunky. Even a task as small as starting a new worktree requires typing the branch name three times: git worktree add -b feat ../repo.feat, then cd ../repo.feat.

    Worktrunk makes git worktrees as easy as branches

    Worktrees are addressed by branch name; paths are computed from a configurable template. Commands that take a branch also accept the path of the worktree it is checked out in.

    Core commands:

    Multiple parallel agents, same simple commands:

    Worktrunk omnibus demo: multiple Claude agents in Zellij tabs with hooks, LLM commits, and merge workflow

    Install

    Homebrew (macOS & Linux):

    brew install worktrunk && wt config shell install
    

    Shell integration allows commands to change directories.

    Cargo:

    cargo install worktrunk && wt config shell install
    

    Windows. wt defaults to Windows Terminal's command, so Winget additionally installs Worktrunk as git-wt to avoid the conflict:

    winget install max-sixty.worktrunk
    git-wt config shell install
    

    Alternatively, disable Windows Terminal's alias (Settings → Apps → Advanced app settings → App execution aliases → "Terminal"/"Terminal Preview") to use wt directly.

    Free code signing provided by SignPath.io, certificate by SignPath Foundation — policy.

    Arch Linux:

    sudo pacman -S worktrunk && wt config shell install
    

    Conda / Pixi (community-maintained feedstock):

    conda install -c conda-forge worktrunk && wt config shell install
    

    Or with Pixi: pixi global install worktrunk && wt config shell install.

    Quick start

    Create a worktree for a new feature:

    $ wt switch --create feature-auth
    ✓ Created branch feature-auth from main and worktree @ ~/repo.feature-auth
    

    This creates a new branch and worktree, then switches to it. Do your work, then check all worktrees with wt list:

    $ wt list
      Branch        Status       @±        ^↕        ^…±     Remote⇅  Commit    Age  Message
    @ feature-auth  +   ↑      +27   -8   ↑1       +31                4bc72dc    2h  Add authenticatio…
    ^ main              ^⇡                                    ⇡1      0e631ad    1d  Initial commit
    
    ○ Showing 2 worktrees, 1 with changes, 1 ahead, hidden: Path
    

    The @ marks the current worktree. + means staged changes, ↑1 means 1 commit ahead of main, ⇡ means unpushed commits.

    When done, either:

    PR workflow — commit, push, open a PR, merge via GitHub/GitLab, then clean up:

    wt step commit                    # commit staged changes
    gh pr create                      # or glab mr create
    wt remove                         # after PR is merged
    

    Local merge — squash, rebase onto main, fast-forward merge, clean up:

    $ wt merge main
    ◎ Generating commit message and committing changes... (2 files, +53, no squashing needed)
      Add authentication module
    ✓ Committed changes @ a1b2c3d
    ◎ Merging 1 commit to main @ a1b2c3d (no rebase needed)
      * a1b2c3d Add authentication module
       auth.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
       lib.rs  |  2 ++
       2 files changed, 53 insertions(+)
    ✓ Merged to main (1 commit, 2 files, +53)
    ◎ Removing feature-auth worktree & branch in background (same commit as main, _)
    ○ Switched to worktree for main @ ~/repo
    

    For parallel agents, create multiple worktrees and launch an agent in each:

    wt switch -x claude -c feature-a -- 'Add user authentication'
    wt switch -x claude -c feature-b -- 'Fix the pagination bug'
    wt switch -x claude -c feature-c -- 'Write tests for the API'
    

    The -x flag runs a command after switching; arguments after -- are passed to it. Configure post-start hooks to automate setup (install deps, start dev servers).

    Next steps

    Contributing

    📚 Full documentation at worktrunk.dev 📚

    Star history