crates/but/skill/references/reference.md
Agent-focused reference for useful but commands.
status, show, diffbranch new, apply, unapply, branch delete, pickcommitsquash, amend, move, uncommit, reword, discardresolvepush, pull, pr, landcleanundo, oplogsetup, teardown, config, update, skillbut statusOverview of branch, stack, commit, and workspace state. Use this when you need existing branch/stack/commit/conflict context. For selected dirty-file or hunk commits, start with but diff instead.
but status # Compact overview with branch, stack, commit IDs, and commit subjects
but status -fv # File-centric view with full commit details and file IDs
but status --verbose # Detailed information
but status --upstream # Show upstream relationship
Shows:
The first token on each line is that line's ID. Commit lines lead with the commit's change ID (stable across history edits); commits without a change ID lead with a sha prefix, which goes stale after history edits. Verbose output appends an informational (sha …) after the timestamp — do not pass the sha to commands.
but show <id>Details about a commit or branch.
but show <id> # Show details
but show <id> --verbose # Show with full messages and file details
but diff [target]Display diff for file, branch, stack, or commit.
but diff # Diff for entire workspace; best first command for selective dirty commits
but diff <file-id> # Diff for specific file
but diff <branch-id> # Diff for all changes in branch
but diff <commit-id> # Diff for specific commit
but diff accepts at most one target. Bare but diff shows every uncommitted file;
inspect committed files or other entities one target at a time. Unlike commit,
amend, and discard, it does not accept several positional IDs.
Hunk IDs: For uncommitted changes, but diff shows each hunk with an ID (e.g., qs:5, uo:d). Pass these IDs to but commit for fine-grained, hunk-level commits.
For the full CLI ID model, but help cli-ids documents every ID kind and its stability.
but branchList all branches (default when no subcommand).
but branch # List branches
but branch list [filter] # Filter branches by name (case-insensitive substring)
but branch list --no-ahead # Skip commits-ahead calculation (faster)
but branch list --no-check # Skip clean-merge check (faster)
but branch list -r # Show only remote branches
but branch list -l # Show only local branches
but branch list -a # Show all branches (not just active + 20 most recent)
but branch list --empty # Include empty branches
but branch list --review # Fetch and display review information
but branch new [name]Create a new branch.
but branch new # Generated branch name
but branch new feature # Independent branch (parallel work)
but branch new feature -a <anchor> # Stacked branch (dependent work)
Use parallel branches for independent tasks. Use stacked branches when work depends on another branch.
For "commit these selected changes on a new branch", prefer but commit -b <branch> -m "message" <ids> instead of a separate but branch new or preflight but status -fv — -b creates the branch when it does not exist.
but apply <branch-name>Activate a branch in the workspace.
but apply feature-branch # Activate branch in workspace
Default human output reports whether the branch was applied, was already active, or conflicted. Conflicts are reported as non-zero CLI errors.
but unapply <id>Deactivate a branch from the workspace.
but unapply <id> # Deactivate branch from workspace
The identifier can be a CLI ID pointing to a stack or branch, or a branch name. If a branch is specified, the entire stack containing that branch will be unapplied.
but branch delete <id>Delete a branch.
but branch delete <id>
but branch -d <id> # Short form
but branch show <id>Show commits ahead of base for a branch.
but branch show <id>
but branch show <id> -f # Show files modified in each commit with line counts
but branch show <id> --ai # Generate AI summary of branch changes
but branch show <id> --check # Check if branch merges cleanly into upstream
but branch show <id> -r # Fetch and display review information
but pick <source> [target]Cherry-pick commits from unapplied branches into applied branches.
but pick <commit-sha> <branch> # Pick specific commit into branch
but pick <cli-id> <branch> # Pick using CLI ID (e.g., "nn")
Name both the source commit and the target branch. Passing a branch as the source opens an
interactive commit picker, and omitting the target prompts for one when several branches exist —
both block. The source can be a commit SHA (full or short) or a CLI ID from but status.
but commit [CHANGES]...Create a commit. Changes are positional CLI IDs; where the commit goes is a flag.
but commit -b <branch> -m "message" # Commit ALL uncommitted changes to branch
but commit -b <branch> -m "message" <id> <id> # Commit specific files or hunks by CLI ID
but commit -b <branch> -m "msg" -m "body" # Repeat -m; parts joined by a blank line
but commit --above <target> -m "message" <id> # Place the commit above a commit or branch
but commit --below <target> -m "message" <id> # Place the commit below a commit or branch
but commit -b <branch> --no-message <id> # Commit without a message
but commit --empty -b <branch> -m "message" # Insert an empty commit
Where the commit goes: -b/--branch, -A/--above, and -B/--below are mutually exclusive.
-b <branch> places the commit at the tip of <branch>, creating it as an unstacked branch if it does not exist. -b with no value creates a branch with a generated name. Targeting a branch that exists but is not applied is an error.--above <commit> / --below <commit> insert relative to a commit on that commit's branch. Against a branch, they create a new branch above/below it.Important: but commit -b <branch> -m "msg" with no IDs commits ALL uncommitted changes. Pass IDs to commit only specific files or hunks.
but commit is not supported from linked worktrees. Use Git directly for the worktree-local commit, and do not run but setup there.
Committing specific files or hunks: Start with but diff for selective dirty commits, then pass CLI IDs as positional arguments:
but diff or but status -fv: commits entire files<file-id>:<hunk-id>) from but diff: commits individual hunks<id> <id>). Commas are not separators — a1,b2 is parsed as a single ID and fails to resolve.Placing commits: Use --above <target> or --below <target> when the new commit should be inserted at a specific position in existing history. Change-ID refs of existing commits remain valid after an insertion; sha and #N-suffixed refs may go stale — add --status-after when subsequent history edits need fresh refs.
Several commits from one diff: Chain but commit calls with && to split a broad uncommitted change into several semantic commits: but commit -b <branch> -m "msg1" a1 b2 && but commit -b <branch> -m "msg2" c3 d4. Mutation output is concise by default. Add --status-after only when the next step needs workspace IDs or details that the mutation result does not provide. The commits stack in the order you write them — the first but commit is the oldest of the new commits and each later one goes on top (newest). File/hunk IDs copied from the original output generally remain usable across commits; if an ID stops resolving, re-read the diff and continue. History edits (amend, squash, move, uncommit, reword) may run in sequence off one status read when every commit ref involved is a change-ID ref; run them one at a time with --status-after when a ref is sha-based or #N-suffixed, or when the next command needs freshly issued IDs. Bare but diff needs no ID from the preceding command, so but uncommit <id> && but diff is safe. If commits from that branch must stay above the new ones, see "Split an existing commit" in SKILL.md: commit the replacements, then move the preserved block together with but move <preserved-id> [<preserved-id>...] -b <branch> so its internal order stays intact.
Example: but commit -b my-branch -m "Fix bug" ab cd commits files/hunks ab and cd.
Example new branch: but commit -b feature/contact-form -m "Validate contact form input" ab cd creates feature/contact-form and commits only those selected file or hunk IDs.
To commit specific hunks from a file with multiple changes, use but diff to see hunk IDs, then specify them individually.
Edge case: if wanted and unwanted edits are in the same hunk, GitButler cannot split that hunk by ID. Only when the task requires keeping part of that hunk uncommitted, temporarily edit the working tree to isolate the wanted lines, commit those IDs, then restore the leftover lines so they remain uncommitted.
but squash <SOURCES>... [-t <target>]Move changes into a target. Sources are positional; the target is -t/--target.
This one command covers squashing, amending, and uncommitting.
but squash <commit> -t <commit> -m "msg" # Squash one commit into another
but squash <commit> <commit> -t <commit> -m "msg" # Squash several commits into a target
but squash <branch> -m "msg" # Squash all commits on a branch into one
but squash <branch> -t <commit> -m "msg" # Squash a branch into a commit, removing the branch
but squash <commit> -t <branch> -m "msg" # Target a branch: squashes into its newest commit
but squash <file-or-hunk-id> -t <commit> # Amend an uncommitted change (`but amend` does this)
but squash zz -t <commit> # Amend all uncommitted changes into a commit
but squash <commit> -t zz # Uncommit a commit
but squash <commit-id>:<file-id> -t <commit> # Move a committed file into another commit
All sources must be the same kind (all commits, all branches, all uncommitted changes, zz, or all
committed files) and committed-file sources must come from one commit. If -t is omitted, <SOURCES>
must be exactly one branch, which squashes that branch's commits together.
Message flags (mutually exclusive). Commit and branch sources compose a new message, so without a flag they open an editor and block — always pass one. Uncommitted and committed-file sources reuse the target's message and need no flag:
-m "msg" # New message; repeat -m to append paragraphs
--no-message # No message
-u, --use-target-message # Keep the target's message, drop the sources'
--use-source-message # Keep the sources' message, drop the target's
None of the message flags may be used when the target is zz.
For multiple independent squash groups, prefer newer/top groups first; change-ID refs from
one status read stay valid across squashes (the target keeps its ref), so the
groups may run in sequence — use --status-after to get fresh refs only
when a ref is sha-based or #N-suffixed.
but amend -t <commit-or-branch> <SOURCES>...Amend uncommitted files/hunks into a specific commit. Use when you know exactly which commit the change belongs to — prefer it over the equivalent squash form. Sources must be uncommitted; amend rejects commits and committed files, so use squash or move for those. A branch target resolves to that branch's newest commit, so name the commit explicitly when the change belongs further down.
but amend -t <commit-id> <file-id> <hunk-id>
but amend -t <branch> <file-id> # Amends into the branch's newest commit (its tip)
Decide the target commit yourself: check but status -fv, find the commit the change logically belongs to, then amend into it.
but move <SOURCES>... <--above|--below|--branch|--unstack>Move commits, committed files, or a branch to a different location. Sources are positional and space-separated; a target flag is required.
but move <commit> --below <target-commit> # Place below target (older) — matches status order
but move <commit> --above <target-commit> # Place above target (newer)
but move <commit> <commit> --below <target-commit> # Move an adjacent block in one command
but move <commit> <commit> --above <target-commit> # Same block move, anchored from the other side
but move <commit> -b <branch> # Move commit to the tip of a branch (created if missing)
but move <commit> --unstack # Move commit onto a new unstacked branch
but move <branch> --above <target-branch> # Stack branch on top of target branch
but move <branch> --unstack # Tear off (unstack) a branch
but move <commit-id>:<file-id> --above <commit> # Move a committed file into a new commit above another
Sources may not mix kinds, all committed files must come from the same commit, and only one branch
may be moved at a time. Source order does not matter. For a branch source only --above and
--unstack apply; --below and -b <name> require commit or committed-file sources. --branch
with no value is equivalent to --unstack.
but uncommit <SOURCES>...Move commits or committed files back to the uncommitted area.
but uncommit <commit-id> # Uncommit an entire commit
but uncommit <commit-id>:<file-id> # Uncommit one file from its commit
Multiple whole commits may be passed together. Multiple committed-file sources must all come from the same commit; uncommit files from different commits in separate commands.
When you need file and hunk IDs to recommit selectively, use
but uncommit <id> && but diff in one shell call.
but reword <id>Reword commit message or rename branch.
but reword <id> -m "new" # Always pass -m; without it an editor opens and blocks
but reword <id> --fix-formatting # Format to 72-char wrapping
but discard <CHANGES>...Permanently drop branches, commits, or changes. Undo with but undo.
but discard <file-id> # Discard an uncommitted file's changes
but discard <hunk-id> # Discard a single hunk
but discard zz # Discard all uncommitted changes
but discard <commit-id> # Drop a commit
but discard <commit-id>:<file-id> # Drop one file's changes from its commit
but discard <branch> # Drop a branch and its commits
All provided IDs must be the same kind, and committed files must come from the same commit.
When commits have conflicts (history-editing commands warn about newly conflicted commits in their output; the but pull summary lists them; but status marks them as conflicted):
but resolve <commit>Enter resolution mode for a conflicted commit.
but resolve <commit-id>
but resolve statusShow remaining conflicted files.
but resolve status
but resolve finishFinalize conflict resolution.
but resolve finish
but resolve finish --status-after # When clearing the last conflict and its workspace is needed
The concise result reports leftover markers, surviving uncommitted changes, every remaining
conflicted commit, and the exact current but resolve <id> command. Add --status-after to the
finish you expect to clear the last conflict only when the task needs the complete resulting
workspace. When it says no conflicted commits remain, stop; do not run a verification status.
but resolve cancelCancel conflict resolution and return to workspace mode.
but resolve cancel
but resolve cancel --force
Workflow:
but resolve <commit-id> — enter resolution mode using the commit ID from the but pull summary (or but status); the conflict regions are printed with line numbers<<<<<<<, |||||||, =======, >>>>>>>; conflicts are diff3-style, so the ||||||| common-ancestor section is present too) and keep the correct content (but resolve status re-lists what remains when several files are conflicted)but resolve finish; add --status-after to the finish you expect to clear the last conflict only when the task needs the complete resulting workspace. When it says no conflicted commits remain, stop; do not run a verification statusImportant: Never use git add, git commit, or other git write commands during conflict resolution. Only use but resolve commands and edit files directly.
but push <branch>Push a branch to remote. Always specify which branch to push: without one, but push prompts for a selection in interactive terminals and pushes ALL branches with unpushed commits otherwise. Accepts a full branch name or a branch CLI ID — prefer the name; it stays valid across mutations.
but push <branch-name> # Push specific branch
but push <branch-name> --dry-run # Preview what would be pushed
but push <branch-name> -s # Skip force push protection checks
but push <branch-name> --no-hooks # Bypass pre-push hooks (--no-verify also works)
Force push is enabled by default with protection checks. Use -s only when intentionally skipping those checks.
After a successful push, GitButler also synchronizes PR targets and stack descriptions for the
selected branch and its ancestors. A forge update failure is reported as a warning; it does not
turn the completed Git push into a failure.
but pullUpdate applied branches onto the latest target branch changes (usually main).
Use this for "get latest from main" in a GitButler workspace.
but pull # Fetch and rebase applied branches
but pull --check # Dry-run preview: report what would happen, change nothing
Run but pull directly for a straightforward update; its output reports the result and but undo
reverts it. Use --check first when the user or repository policy requires a preview without
updating.
Do not use raw git pull or git rebase.
but prCreate and manage pull requests.
but pr new <branch-id> -m "Title..." # Push branch and create PR (recommended); first message line is title, rest is description
but pr new <branch-id> -F pr_message.txt # Use file: first line is title, rest is description
but pr new <branch-id> -t # Use default content (commit message), skip prompts
but pr new <branch-id> --draft # Create as draft
but pr new <branch-id> --no-hooks # Bypass pre-push hooks (--no-verify also works)
but pr new <branch-id> -s # Skip force-push protection checks
but pr --draft # Top-level draft flag
but pr auto-merge <selector> # Enable auto-merge
but pr set-draft <selector> # Mark review as draft
but pr set-ready <selector> # Mark review as ready
Key behavior: but pr new automatically pushes the branch to remote before creating the PR. No need to run but push first. Force push and pre-push hooks run by default.
Use --no-hooks to bypass pre-push hooks when needed.
Review creation remains successful if the follow-up stack synchronization fails, and reports that
partial success as a warning.
Selectors for auto-merge, set-draft, and set-ready can be branch names, branch IDs, stack IDs, or numeric review IDs, comma-separated.
Agents must use --message (-m), --file (-F), or --default (-t) to avoid editor prompts. The -t flag uses the commit message as title/description for single-commit branches; for multi-commit branches it falls back to the branch name as the title.
Stacked branches: Use but pr for stacked PRs. It creates reviews against the right bases and updates GitButler stack footers in PR descriptions. Creating stacked PRs with gh pr create or another forge tool loses that stack-aware behavior. To publish a whole stack, run but pr new <top-branch-id> -t; custom messages (-m or -F) only apply to the selected branch, while dependent branches use default messages (commit title/description).
When the selected branch sits on dependencies that already have PRs, the summary lists those as "PR already exists for ..." and ends with the newly created review. The already-exists lines are normal stack reporting, not a failure to create the selected branch's PR.
Requires forge integration to be configured via but config forge auth.
Same-repository pull requests are automatically registered with GitHub's native stacked pull
requests API when the repository is enrolled in GitHub's private preview; otherwise GitButler uses
description footers. but config forge github-stacks disable opts out. The setting is
project-local and shared with Desktop.
but land <branch>Land a branch directly onto the target (e.g. origin/master), skipping a pull request. Fast-forwards
when possible, otherwise makes a signed merge commit; for a gb-local target it moves the refs
locally. Then reconciles the remaining branches like but pull.
but land <branch-id> --yes # Land onto the target (--yes required non-interactively)
but land <branch-id> --no-ff --yes # Force a merge commit instead of fast-forwarding
but land <top-branch> --whole-stack --yes # Land an entire stack by naming its top segment
Direct target updates are hard to reverse, so confirmation is required (agents must pass --yes).
A branch stacked on other segments is refused (its tip would also publish them); --whole-stack
is the explicit opt-in, and only the stack's top segment can be named with it.
but cleanRemove empty branches from the workspace.
but clean # Delete all empty branches
but clean --dry-run # Preview which branches would be deleted
but clean --pull # Pull latest changes first, then clean
but clean --include-upstream # Also remove branches with upstream-only commits
A branch is considered empty if it has no local commits and no assigned changes. Branches with upstream-only commits are preserved by default unless --include-upstream is used.
The entire operation is a single oplog entry — use but undo to restore all deleted branches.
but undo / but redoUndo or redo operations.
but undo
but redo
but oplogView operation history.
but oplog
but oplog list --since <snapshot-id>
but oplog list --snapshot
but oplog snapshot -m "known good"
Shows all operations with snapshot IDs.
but oplog restore <snapshot>Restore to a specific oplog snapshot.
but oplog restore <snapshot-id>
but setupInitialize GitButler in current git repository.
but setup
but setup --init # Also initialize a new git repo if none exists
Converts a regular Git repository to the managed GitButler workspace model. Use --init in
non-interactive environments (CI/CD) to ensure a Git repository exists before setup. When the
experimental single-branch feature is enabled, normal CLI use does not require this command: the
repository is registered and its target is inferred lazily without checking out
gitbutler/workspace or installing setup hooks.
Rerunning but setup on an already-configured project also repairs a missing default target — for
example if virtual_branches.toml was reset while the target survived in Git config — so it is the
recovery path when target configuration looks broken.
but teardownExit GitButler mode and return to normal git workflow.
but teardown
but configView and manage GitButler configuration.
but config
but config user # Also: forge, target, metrics, feature, ui, ai
but config ai openai # Also: anthropic, ollama, lmstudio, openrouter
but config target # Show the current target branch
but config target origin/main # Set the fetch target
but config push-remote # Show the current push remote
but config push-remote origin # Set the push remote without changing the target
but config feature # List configurable feature flags
but config feature single-branch # Show a feature flag's value
but config feature single-branch enable # Also: disable
but update checkManage GitButler CLI and app updates.
but update check
but update install
but update install [nightly|release|0.18.7]
but skillManage installed GitButler skill files.
but skill check
but skill check --update
but skill install --detect
Useful to agents:
-C, --current-dir <PATH> - Run as if started in different directory-h, --help - Show help for command. Avoid routine help probes; use this reference first.Important: Not available for Windows yet
Similar to Git, if <command> is not a built-in but command and but-<command> exists on PATH, but runs that executable instead (for example but forecast … invokes but-forecast …).
Restriction: <command> must consist of characters in the set [a-zA-Z_-]
but --help # List all commands
but <subcommand> --help # Detailed help for specific command
Prefer this reference over exploratory help calls. Use command-specific help when required syntax is missing or a command fails; use top-level help only to discover an undocumented command.
Full documentation: https://docs.gitbutler.com/cli-overview