Use GitButler CLI for efficient branching, committing, and diffing.

Install

mkdir -p .claude/skills/but && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7231" && unzip -o skill.zip -d .claude/skills/but && rm skill.zip

Installs to .claude/skills/but

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Commit, push, branch, and manage version control with GitButler. Use for commits, selective dirty-file or hunk commits, branches, diffs, PRs, history edits, squashes, amends, undo, merge, apply, and unapply. For selected dirty files or hunks, inspect with `but diff`; use compact `but status` for commit order, branch/stack placement, or conflict overview; use `but status -fv` when file/hunk IDs or per-commit file details matter. Replaces git write commands.
460 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • →Stage specific file hunks for commits
  • →Manage multiple branches in a workspace
  • →Amend or squash existing commit history
  • →Undo recent git operations
  • →Create and push branches with PRs

How it works

The tool replaces standard Git write commands with a specialized CLI that manages commit order, branch placement, and history edits through stable change IDs.

Inputs & outputs

You give it
Git repository changes
You get back
Version control state updates

When to use but

  • →Stage specific file hunks for commits
  • →Manage multiple branches in a workspace
  • →Amend or squash existing commit history
  • →Undo recent git operations

About this skill

GitButler CLI Skill

Use GitButler CLI (but) as the default version-control interface.

Start Here

Choose the narrowest first command by task; avoid ritual status checks:

# Selected dirty files/hunks:
but diff

# Commit order, branch/stack placement, conflict overview:
but status

# File/hunk IDs, per-commit files, amend/split details:
but status -fv

# Details for one known branch or commit:
but show <id>

Do not run plain but status and then but status -fv unless the compact output lacks file/hunk details needed for the task.

For "commit just/only/specific changes on a new branch", use the fast path:

but diff
but commit -b <branch> -m "<msg>" <id> <id>

but commit -b <branch> ... creates the branch when it does not exist and prints the created commit. Do not run a separate but branch new, status command, or verification diff unless the task needs workspace information that the result does not provide.

IDs

The first token on each but diff / but status line is that line's ID. When a command needs an ID, copy it exactly from the current output; never hardcode or invent one. ID lifetimes differ by entity:

  • Changes and sources are positional, space-separated IDs (but commit -b feat -m "msg" uvw:2e4 uvw:e2c). An uncommitted hunk ID is written <file-id>:<hunk-id> (e.g. uvw:2e4, copied from bare but diff) — the part after the colon is the hunk's ID, not a line range (uvw:16-40 is invalid). Do not invent flags like --changes / --hunk / --ids, pass a line range, or comma-separate IDs — uvw,qyo is parsed as one ID and fails.
  • but diff is the exception: it accepts at most one target. Bare but diff shows all uncommitted files; inspect committed files or other entities one target at a time — never but diff <id> <id>.
  • A committed file is <commit-id>:<file-id> (e.g. mzm:uvw, shown under each commit). A committed hunk is <commit-id>:<file-id>:<hunk-id> and is shown by but diff <commit-id>. @ means the uncommitted area.
  • Commit IDs are stable change IDs that survive history edits (amend, squash, move, uncommit, reword). Commits without a change ID (e.g. upstream-only) lead with a sha prefix instead, and #N-suffixed refs disambiguate duplicates — both go stale after history edits, and a stale sha can silently resolve to the wrong commit. The (sha …) on verbose commit lines is informational — do not pass it to commands.
  • Branch short IDs are snapshot-local selectors. Use full branch names for branch-targeting mutations; short IDs are safe only for immediate read-only inspection.
  • File/hunk IDs copied from one diff read generally remain usable across chained commits. If one stops resolving, re-read but diff and retry.

Chaining: mutation output is concise by default, so you may chain mutations with && off one inspection read. Do not chain branch short IDs through mutations; use full branch names. Add --status-after only when the next step needs workspace IDs or details that the mutation result does not provide. Chained but commit calls stack in the order written — the first is oldest, each later one goes on top. History edits may run in sequence 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. Chaining but uncommit <id> && but diff is safe because bare diff needs no ID from the uncommit output.

Non-Negotiable Rules

  1. Use but for all write operations. Never run git add, git commit, git push, git checkout, git merge, git rebase, git stash, or git cherry-pick. If the user says a git write command, translate it to but and run that. Running from a linked worktree shows the same workspace and IDs as the main worktree, and @ still means the main worktree's changes; a bare but commit or but diff takes that worktree's changes, and <worktree>:@ names them from anywhere. but setup refuses to run from a worktree.
  2. Mutation commands print their result without appending workspace status. Add --status-after only when the next step needs resulting workspace IDs or details; otherwise trust the mutation result and do not run a verification status/diff.
  3. Branches marked (merged upstream) have landed; start new work on another branch. That marker alone does not mean but pull removes them: run but pull --check first. but pull removes only branches listed [integrated] ("has been integrated upstream and removed locally") and rebases the rest, which stay applied. To clear a landed branch that stays applied, use but unapply <branch> (acts on its whole stack; but apply <branch> restores it) rather than but discard. push and mutations (commit, amend, squash, uncommit, reword, move) refuse landed branches and commits, absorb skips them with a notice, and commit skips them when picking a default target.
  4. In non-interactive CLI workflows, do not narrate progress between routine commands. Execute the needed but commands and give a concise final summary.
  5. Prefer this skill and but skill reference over exploratory help calls. Use <command> --help when required syntax is missing or a command fails; use top-level help only when you genuinely need to discover an undocumented command.

Command Patterns

  • Commit: but commit -b <branch> -m "<msg>" <id> <id> — -b <branch> creates the branch if it does not exist
  • Commit everything uncommitted: but commit -b <branch> -m "<msg>" (omit the IDs)
  • Several commits from one diff: chain but commit calls with && (commits stack oldest-first)
  • Commit at a specific history position: --above <commit-or-branch> or --below <commit-or-branch> (mutually exclusive). With a branch target, add -b <new-name> to name the new branch; otherwise its name is generated. The name must not already exist. Do not combine -b (even without a name) with commit/worktree targets.
  • Targeting is required when more than one stack is applied; without it but commit fails with "Unclear where to commit. Found more than one stack". Several branches stacked together count as one stack — an untargeted commit then silently lands on the stack's top branch, so pass -b whenever the branch matters.
  • Always pass -m "<msg>" (or --no-message) to but commit, and to but squash whenever its sources are commits or branches unless the target is @ — those compose a new message; without a flag an agent run keeps whatever the command composed (empty for but commit, the joined source messages for but squash) and a terminal opens an editor. Squash sources that are uncommitted or committed changes reuse the target's message and need no flag; squashing into @ rejects message flags outright.
  • Amend: but amend -t <commit-or-branch> <file-or-hunk-id> <file-or-hunk-id> — a branch target resolves to its newest commit
  • Uncommit: but uncommit <commit-id> (whole commit), but uncommit <branch> (all commits and remove branch), but uncommit <commit-id>:<file-id> (committed file), or but uncommit <commit-id>:<file-id>:<hunk-id> (committed hunk); committed files and hunks may be mixed, but all must come from one commit
  • Insert empty commit: but commit --empty -b <branch> -m "<msg>"
  • Pick onto a new named branch: but pick <commit-sha> --above <branch> -b <new-name> (--below also works). The name must not exist; -b cannot be combined with commit/worktree targets.
  • Squash commits: but squash <source-commit-id> [<source-commit-id>...] -t <target-commit-id> -m "<msg>"
  • Move committed changes into an existing commit or @: but squash <commit-id>:<file-id> -t <commit-id-or-@> for a committed file; but squash <commit-id>:<file-id>:<hunk-id> -t <commit-id-or-@> for a committed hunk; all sources must come from one commit
  • Move committed changes into a new commit at a chosen position: but move <commit-id>:<file-id> --above <commit-or-branch> -m "<msg>" for a committed file; but move <commit-id>:<file-id>:<hunk-id> --above <commit-or-branch> -m "<msg>" for a committed hunk (--below, --branch, and --unstack also work)
  • Split selected committed changes into a new commit immediately above their source: but split <committed-file-or-hunk-id> [<committed-file-or-hunk-id>...] -m "<msg>"
  • For but split and committed-change but move, use -m/--message to name the new commit directly; repeated -m values are joined with blank lines. Omitting it creates an empty-message commit without opening an editor. but move -m rejects whole-commit and branch sources.
  • Squash a whole branch into one commit: but squash <branch> -m "<msg>" (no -t)
  • Uncommit and remove a branch: but uncommit <branch>
  • Reorder commits: but move <commit-id> --below <commit-id> (--above for the other direction; commit IDs, not branch names)
  • Reorder a block: but move <commit-id> <commit-id> --below <following-commit-id> or --above <preceding-commit-id> (both anchors accept multiple space-separated sources)
  • Move commit to branch top: but move <commit-id> -b <branch>
  • Move commits or committed changes onto a new named branch: but move <source> --above <branch> -b <new-name> (--below also works); use but move <source> --unstack -b <new-name> for an independent branch. Omit -b for a generated name. Naming is not supported when stacking or unstacking a branch source.
  • Stack branches: but move <branch-name> --above <target-branch-name> (use full branch names)
  • Tear off a branch: but move <branch> --unstack
  • Discard: but discard <id> [<id>...] — accepts branches, commits, committed changes, uncommitted changes, or @ for all uncommitted changes
  • Push: but push <top-branch> — pushes the selected branch and its ancestors; to update a stack, select its top branch once and never loop. Bare but push pushes all unpushed work when run non-interactive

Content truncated.

When not to use it

  • →Linked worktrees
  • →Conflicted commits when using amend

Limitations

  • →Linked worktrees are unsupported
  • →Conflicted commits do not support amend

How it compares

It provides a higher-level interface that manages staging and history mutations as atomic operations rather than requiring manual Git index manipulation.

Compared to similar skills

but side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
but (this skill)12moReviewIntermediate
resolve-conflicts8110moReviewIntermediate
openspec-onboard108moReviewBeginner
codex-cli-bridge911moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry