# GitButler Reference Complete CLI reference, JSON schemas, troubleshooting, and recovery patterns. --- ## Command Reference ### Global Options ```bash but [OPTIONS] Global Options (must come BEFORE subcommand): -C, --current-dir Run from specified directory -j, --json JSON output format -h, --help Show help ``` **JSON output**: Use `--json` or `-j` per command, or as a global flag: ```bash but status --json # Per-command flag but --json status # Global flag (also works) ``` ### Inspection Commands | Command | Description | |---------|-------------| | `but status` | View uncommitted changes and file assignments | | `but status -f` | Show modified files in each commit | | `but status -v, --verbose` | Show verbose output with commit author and timestamp | | `but show ` | Show detailed info about a commit or branch | | `but diff` | Show diff of all uncommitted changes | | `but diff ` | Show diff for a specific entity (file, branch, commit) | | `but oplog` | View operations history (snapshots) | | `but gui` | Open GitButler GUI for current repo | **Status Output Example:** ``` ╭┄00 [Unassigned Changes] │ m6 A test-file.md │ p9 M existing-file.ts ├╯ ╭┄g4 [feature-branch] │ 🔒 i3 M locked-file.ts ● abc1234 feat: initial commit ├╯ ● 0c60c71 (common base) [origin/main] ``` **File Status Codes:** - `A` — Added - `M` — Modified - `D` — Deleted - `🔒` — Locked (belongs to this branch's commits) **IDs:** - `00`, `g4` — Branch IDs - `m6`, `p9`, `i3` — File/hunk IDs (use with `but rub`) ### Branch Management | Command | Description | |---------|-------------| | `but branch new ` | Create virtual branch (based on trunk) | | `but branch new --anchor ` | Create stacked branch | | `but branch new -a ` | Short form for stacked branch | | `but branch delete ` | Soft delete (requires confirmation) | | `but branch delete --force` | Force delete | | `but branch list` | List all branches | | `but branch list --local` | Only local branches | | `but unapply ` | Remove branch from workspace (keeps in Git) | | `but apply ` | Apply an unapplied branch to workspace | | `but pick [branch]` | Cherry-pick commit from unapplied branch | ### Committing | Command | Description | |---------|-------------| | `but commit -m "message"` | Commit to inferred branch | | `but commit -m "message"` | Commit to specific branch | | `but commit -o -m "msg"` | Only commit assigned files (`-o` flag) | | `but commit -p ,` | Commit specific files/hunks by CLI ID | | `but commit --ai` | Generate commit message with AI | | `but commit -c` | Create new branch for this commit | | `but commit` | Opens `$EDITOR` for message | | `but commit empty --before ` | Insert blank commit before target | | `but commit empty --after ` | Insert blank commit after target | **Note:** Unlike git, GitButler commits all changes by default. Use `-o/--only` to commit only assigned files, or `-p/--changes` to select specific file/hunk IDs. ### File and Commit Manipulation #### `but rub` (Swiss Army Knife) ```bash but rub ``` | Source | Target | Operation | Description | |--------|--------|-----------|-------------| | File ID | Branch ID | **Assign** | Move file to branch | | File ID | Commit SHA | **Amend** | Add file changes to commit | | Commit SHA | Branch ID | **Move** | Relocate commit to branch | | Commit SHA | Commit SHA | **Squash** | Combine newer into older | #### Other Editing Commands | Command | Description | |---------|-------------| | `but commit empty --before/--after ` | Insert blank commit before or after target | | `but reword` | Edit commit message or rename branch | | `but absorb` | Auto-amend uncommitted changes to appropriate commits based on context | | `but absorb --dry-run` | Preview what absorb would do without changing anything | | `but absorb --new` | Create new commits instead of amending existing ones | | `but squash ` | Squash commits together (by IDs, range, or branch name) | | `but move ` | Move commit to a different location in the stack | | `but amend ` | Amend a file change into a specific commit | | `but uncommit ` | Uncommit changes back to unstaged area | | `but discard ` | Discard uncommitted changes from worktree | | `but mark ` | Auto-assign new changes to branch | | `but unmark` | Remove all mark rules from workspace | **`but absorb`**: Analyzes uncommitted changes and automatically amends them to the appropriate existing commits based on file context and change location. Similar to `git absorb` but integrated with virtual branches. ### Forge Integration (GitHub) | Command | Description | |---------|-------------| | `but config forge auth` | Authenticate with GitHub via OAuth flow | | `but config forge list-users` | List authenticated accounts | | `but config forge forget ` | Remove authenticated account | | `but push [branch]` | Push branch to remote | | `but push -d, --dry-run` | Preview what would be pushed | | `but push -f, --with-force` | Force push | | `but push -r, --run-hooks` | Execute pre-push hooks | | `but pr new [branch]` | Create PR for branch on forge | **Push + PR workflow:** 1. Push branch to remote: `but push feature-auth` 2. Create PR: `but pr new feature-auth` 3. Or push all unpushed branches: `but push` (non-interactive) 4. Requires prior `but config forge auth` for first-time setup ### Base Branch Operations | Command | Description | |---------|-------------| | `but pull --check` | Fetch remotes and check mergeability | | `but pull` | Update workspace with latest from base | ### Operations History (Undo/Restore) | Command | Description | |---------|-------------| | `but oplog` | View operation history | | `but undo` | Undo last operation | | `but oplog restore ` | Restore to specific snapshot | | `but oplog snapshot --message "msg"` | Create manual snapshot | ### Conflict Resolution | Command | Description | |---------|-------------| | `but resolve ` | Enter resolution mode for a conflicted commit | | `but resolve status` | Show remaining conflicted files | | `but resolve finish` | Finalize resolution and return to workspace | | `but resolve cancel` | Cancel resolution and return to workspace | **Workflow:** 1. `but status` shows conflicted commits 2. `but resolve ` to enter resolution mode 3. Fix conflict markers in your editor 4. `but resolve status` to check remaining conflicts 5. `but resolve finish` to finalize ### Workspace Lifecycle | Command | Description | |---------|-------------| | `but setup` | Initialize GitButler project from existing Git repo | | `but setup --init` | Initialize new Git repo and set up GitButler | | `but teardown` | Exit GitButler mode, return to normal Git | **`but teardown`**: Creates an oplog snapshot, checks out the first active branch as a regular Git branch, and provides instructions for returning to GitButler mode. ### Configuration | Command | Description | |---------|-------------| | `but config` | Show configuration overview | | `but config user` | View user configuration | | `but config user set name "Name"` | Set user name | | `but config user set email "email"` | Set user email | | `but config forge` | View forge configuration | | `but config forge auth` | Authenticate with forge (GitHub OAuth) | | `but config forge list-users` | List authenticated accounts | | `but config forge forget ` | Remove authenticated account | ### AI Integration Commands **Claude Code Hooks:** | Command | Purpose | |---------|---------| | `but claude pre-tool` | Run before code generation/editing | | `but claude post-tool` | Run after editing completes | | `but claude stop` | Run when agent session ends | **Cursor Hooks:** | Command | Purpose | |---------|---------| | `but cursor after-edit` | Triggered when Cursor edits files | | `but cursor stop` | Triggered when task completes | **MCP Server:** | Command | Purpose | |---------|---------| | `but mcp` | Start MCP server for agent integration | --- ## JSON Output Schemas ### `but status --json` Key fields: - `path` — Filename as ASCII array (requires decoding) - `assignments` — Hunk-level file assignments - `stackId` — Which stack this belongs to (null if unassigned) **Limitations:** - File IDs (`m6`, `g4`) not exposed in JSON - Paths are ASCII arrays, not strings - Parse text output for IDs ### `but show --json` Shows detailed branch info with commits. Key fields: - `commits` — Array of commits on the branch - `commits[].id` — Commit SHA ### `but diff --json` Shows diffs in JSON format for programmatic analysis. **Useful jq patterns:** ```bash # Get branch commits but show feature-branch --json | jq '.commits[] | .id' # Workspace overview but status --json | jq '.stacks' ``` --- ## GitButler vs Graphite | Aspect | Graphite | GitButler | |--------|----------|-----------| | **Model** | Linear stacks of physical branches | Virtual branches with optional stacking | | **Workflow** | Plan → Branch → Code → Commit → Stack | Code → Organize → Assign → Commit | | **Branch Switching** | Required (`gt up`/`gt down`) | Never needed (all applied) | | **Branch Creation** | `gt create -am "msg"` | `but branch new name [--anchor parent]` | | **Committing** | `gt modify -cam "msg"` | `but commit -m "msg"` | | **Stack Navigation** | ✓ `gt up`/`gt down` | ✗ No CLI equivalent (all applied) | | **PR Submission** | ✓ `gt submit --stack` | ✓ `but push` + `but pr new` | | **JSON Output** | Limited | ✓ Comprehensive via `--json` per command | | **Multi-Feature Work** | Switch branches | All in one workspace | | **CLI Completeness** | ✓ Full automation | ✓ Full automation (as of 0.19.0) | | **Conflict Resolution** | Standard git rebase | ✓ Per-commit via `but resolve` | **Choose Graphite when:** - Stack navigation commands needed (`gt up`/`gt down`) - Terminal-first linear workflow - Established stacked PR practices **Choose GitButler when:** - Multiple unrelated features simultaneously - Multi-agent concurrent development - Exploratory coding (organize after) - Post-hoc commit reorganization - Per-commit conflict resolution needed - Visual organization preferred (GUI + CLI) **Don't use both in same repo** — incompatible models. --- ## Troubleshooting Guide ### Quick Reference | Symptom | Cause | Solution | |---------|-------|----------| | Broken pipe panic | Output piped directly | Capture to variable first | | Filename with dash fails | Interpreted as range | Use file ID from `but status` | | Branch not visible | Not applied | `but apply ` or `but pick ` | | Files not committing | Not assigned | `but rub ` | | Mixed git/but broke state | Used git commands | `but pull` or `but setup` | | Workspace stuck loading | Backend timeout | Check oplog, restore snapshot | | "Workspace commit not found" | HEAD changed externally | `git checkout gitbutler/workspace` | ### Common Issues #### Broken Pipe Panic **Problem:** `but status` panics when output consumed partially. ```bash ✗ but status | head -5 # Panic! ✓ status_output=$(but status) echo "$status_output" | head -5 ``` #### Filename Parsing Issues **Problem:** Dashes in filenames interpreted as range syntax. ```bash ✗ but rub file-with-dashes.md branch # Fails ✓ but rub m6 branch # Use file ID from but status ``` #### Integration Branch Conflicts **Problem:** Mixed `git` and `but` commands corrupted state. **Solutions:** 1. `but pull` to resync 2. If severely broken: `but setup` to reinitialize #### Files Not Committing **Causes:** 1. Files not assigned to branch 2. Missing `-o` flag (only commit assigned files) ```bash # Check assignments but status # Assign files but rub # Commit with -o flag but commit -o -m "message" ``` #### Workspace Stuck Loading **Symptoms:** - Loading spinner indefinitely - Can see trunk/remote branches but not workspace **Recovery:** 1. Wait 60 seconds for timeout 2. Check logs: `~/Library/Logs/com.gitbutler.app/GitButler.log` (macOS) 3. Use Operations History to restore previous snapshot 4. Last resort: Remove and re-add project #### "GitButler workspace commit not found" **Cause:** `gitbutler/workspace` branch modified or deleted outside GitButler. **Recovery:** ```bash # Return to integration branch git checkout gitbutler/integration # If that fails, check oplog cat .git/gitbutler/operations-log.toml git log # Remove and re-add project to GitButler ``` ### Recovery Scenarios #### Lost Work (Accidentally Deleted Branch) ```bash # Check oplog for deletion but oplog # Undo deletion (if last operation) but undo # Or restore to snapshot before deletion but oplog restore ``` #### Corrupted Workspace State ```bash # Step 1: Snapshot current state but oplog snapshot --message "Before recovery" # Step 2: Update base but pull # Step 3: Last resort - reinitialize but setup ``` #### Recovering from Mixed Git/But Commands **If you committed with `git commit`:** ```bash # Work is still in working directory # Find orphaned commit git reflog # Create branch from it git branch recovered # Return to GitButler git checkout gitbutler/integration ``` **If you checked out another branch:** ```bash # Return to GitButler git checkout gitbutler/integration # GitButler will resume operation ``` #### Virtual Branches Disappeared Virtual branches are Git refs — they're still there: ```bash # List all virtual branch refs git for-each-ref refs/gitbutler/ # Create regular branch from virtual branch git branch recovered-feature refs/gitbutler/Feature-A # Or push directly to remote git push origin refs/gitbutler/Feature-A:refs/heads/feature-a ``` #### Extract Data from Corrupted Project ```bash # Backup everything cp -r .git .git-backup # Extract all virtual branch refs git for-each-ref refs/gitbutler/ > gitbutler-refs.txt # Create regular branch from each while read sha type ref; do name=$(basename "$ref") git branch "recovered-$name" "$sha" done < gitbutler-refs.txt # Extract latest oplog snapshot LATEST=$(cat .git/gitbutler/operations-log.toml | grep head_sha | awk '{print $3}' | tr -d '"') git archive $LATEST index/ | tar -x -C recovered-uncommitted/ ``` ### Operations Log (Oplog) Deep Dive **Location:** `.git/gitbutler/operations-log.toml` **Snapshot contents:** ``` ├── virtual_branches.toml # Branch metadata ├── virtual_branches/ # Branch content trees ├── index/ # Working directory state ├── target_tree/ # Base branch (e.g., main) └── conflicts/ # Merge conflict info ``` **Operation types:** - `CreateCommit` — Made a commit - `CreateBranch` — Created branch - `UpdateWorkspaceBase` — Updated base branch - `RestoreFromSnapshot` — Reverted to snapshot - `FileChanges` — Uncommitted changes detected - `DeleteBranch` — Deleted branch - `SquashCommit` — Squashed commits **Manual inspection:** ```bash # Find oplog head OPLOG_HEAD=$(cat .git/gitbutler/operations-log.toml | grep head_sha | awk '{print $3}' | tr -d '"') # View snapshot history git log $OPLOG_HEAD --oneline # Show virtual branches config from snapshot git show :virtual_branches.toml # Extract file from snapshot git show :index/path/to/file.txt ``` ### Prevention Best Practices **Golden Rules:** 1. **NEVER remove project to fix errors** — may delete actual source files 2. **Commit frequently** — committed work is safer than WIP 3. **Push virtual branches to remote** — backup your work 4. **Don't mix GitButler and stock Git commands** — choose one workflow **Before risky operations:** ```bash but oplog snapshot --message "Before major reorganization" ``` **Before GitButler updates:** 1. Commit everything 2. Push all branches to remote 3. Verify Operations History accessible