📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-05-29 08:33:53 +00:00
parent fdb52f1e96
commit 06e0d13d57
1615 changed files with 232858 additions and 0 deletions
@@ -0,0 +1,250 @@
---
name: gitbutler-virtual-branches
description: This skill should be used when the user asks to "create a virtual branch", "assign file to branch", "work on multiple features simultaneously", "organize commits after coding", "use but commands", or mentions GitButler, virtual branches, parallel development without checkout, post-hoc commit organization, multi-agent concurrent development, or `--gitbutler`/`--but` flags.
metadata:
version: "1.0.0"
author: outfitter
category: version-control
related-skills:
- gitbutler-multi-agent
- gitbutler-stacks
- gitbutler-complete-branch
---
# GitButler Virtual Branches
Virtual branches → parallel development → post-hoc organization.
<when_to_use>
- Multiple unrelated features in same workspace simultaneously
- Multi-agent concurrent development (agents in same repo)
- Exploratory coding where organization comes after writing
- Post-hoc commit reorganization needed
- Visual organization preferred (GUI + CLI)
NOT for: projects using Graphite (incompatible models), simple linear workflows (use plain git)
</when_to_use>
## This, Not That
| Task | This | Not That |
| ---- | ---- | -------- |
| Initialize workspace | `but setup` | manual setup |
| Create branch | `but branch new name` | `git checkout -b name` |
| View changes | `but status` | `git status` |
| Assign file to branch | `but rub <file-id> <branch>` | manual staging |
| Commit to branch | `but commit <branch> -m "msg"` | `git commit -m "msg"` |
| Move commit | `but rub <sha> <branch>` | `git cherry-pick` |
| Squash commits | `but squash <branch>` | `git rebase -i` |
| Undo operation | `but undo` | `git reset` |
| Switch context | Create new branch | `git checkout` |
**Key difference from Git**: All branches visible at once. Organize files to branches after editing. No checkout.
## Core Concepts
| Concept | Description |
|---------|-------------|
| Virtual branches | Multiple branches applied simultaneously to working directory |
| Integration branch | `gitbutler/workspace` tracks virtual branch state — never touch directly |
| Target branch | Base branch (e.g., `origin/main`) all work diverges from |
| File assignment | Assign file hunks to branches with `but rub` |
| Oplog | Operations log for undo/restore — your safety net |
## Quick Start
```bash
# Initialize (one time)
but setup
# Create branch
but branch new feature-auth
# Make changes, check status for file IDs
but status
# ╭┄00 [Unassigned Changes]
# │ m6 A src/auth.ts
# Assign file to branch using ID
but rub m6 feature-auth
# Commit
but commit feature-auth -m "feat: add authentication"
```
## Core Loop
1. **Create**: `but branch new <name>`
2. **Edit**: Make changes in working directory
3. **Check**: `but status` to see file IDs
4. **Assign**: `but rub <file-id> <branch-name>`
5. **Commit**: `but commit <branch> -m "message"`
6. **Repeat**: Continue with other features in parallel
## The Power of `but rub`
Swiss Army knife — combines entities to perform operations:
| Source | Target | Operation |
|--------|--------|-----------|
| File ID | Branch | Assign file to branch |
| File ID | Commit | Amend commit with file |
| Commit SHA | Branch | Move commit between branches |
| Commit SHA | Commit SHA | Squash (newer into older) |
## Essential Commands
| Command | Purpose |
|---------|---------|
| `but setup` | Initialize GitButler in repository |
| `but status` | View changes and file IDs |
| `but branch new <name>` | Create virtual branch |
| `but branch new <name> --anchor <parent>` | Create stacked branch |
| `but rub <source> <target>` | Assign/move/squash/amend |
| `but commit <branch> -m "msg"` | Commit to branch |
| `but commit <branch> -o -m "msg"` | Commit only assigned files |
| `but absorb` | Auto-amend uncommitted changes to appropriate commits |
| `but squash <commits>` | Squash commits (by IDs, range, or branch) |
| `but show <id>` | Inspect a commit or branch in detail |
| `but resolve <commit>` | Enter conflict resolution mode |
| `but push` | Push branches to remote |
| `but pr new` | Create/update PRs on forge |
| `but config forge auth` | Authenticate with GitHub (OAuth) |
| `but mark <branch>` | Auto-assign new changes to branch |
| `but unmark` | Remove all mark rules from workspace |
| `but oplog` | Show operation history |
| `but undo` | Undo last operation |
| `but oplog snapshot --message "msg"` | Create manual snapshot |
| `but pull` | Update workspace with latest base |
| `but gui` | Open GitButler GUI for current repo |
**JSON output**: Use `--json` or `-j` flag on any command: `but status --json`
## Parallel Development
```bash
# Create two independent features
but branch new feature-a
but branch new feature-b
# Edit files for both (same workspace!)
echo "Feature A" > feature-a.ts
echo "Feature B" > feature-b.ts
# Assign to respective branches
but rub <id-a> feature-a
but rub <id-b> feature-b
# Commit independently
but commit feature-a -m "feat: implement feature A"
but commit feature-b -m "feat: implement feature B"
# Both branches exist, zero conflicts, same directory
```
## Conflict Handling
GitButler handles conflicts **per-commit** during rebase/update (unlike Git's all-or-nothing model):
1. Rebase continues even when some commits conflict
2. Conflicted commits marked in UI/status
3. Use `but resolve` to enter resolution mode per commit
4. Partial resolution can be saved for later
```bash
# Update base (may cause conflicts)
but pull
# Check which commits have conflicts
but status
# Enter resolution mode for a specific commit
but resolve <commit-id>
# Fix conflicts in your editor, then check remaining
but resolve status
# Finalize when done (or cancel to abort)
but resolve finish
```
For detailed conflict resolution workflows, see `references/reference.md#conflict-resolution`.
## Auto-Assignment with Marks
Set up workspace rules to auto-assign files to branches:
```bash
# Auto-assign new changes to auth-feature branch
but mark auth-feature
# Remove all rules
but unmark
```
Useful for multi-agent workflows where changes should route to a specific branch.
<rules>
ALWAYS:
- Use `but` for all work within virtual branches
- Use `git` only for integrating completed work into main
- Return to `gitbutler/workspace` after git operations: `git checkout gitbutler/workspace`
- Snapshot before risky operations: `but oplog snapshot --message "..."`
- Assign files immediately after creating: `but rub <id> <branch>`
- Check file IDs with `but status` before using `but rub`
NEVER:
- Use `git commit` on virtual branches — breaks GitButler state
- Use `git add` — GitButler manages index
- Use `git checkout` on virtual branches — no checkout needed
- Push `gitbutler/integration` to remote — it's local-only
- Mix Graphite and GitButler in same repo — incompatible models
- Pipe `but status` directly — causes panic; capture output first:
```bash
status_output=$(but status)
echo "$status_output" | head -5
```
</rules>
## Troubleshooting
| Symptom | Solution |
|---------|----------|
| Files not committing | Assign first: `but rub <file-id> <branch>` |
| Broken pipe panic | Capture output: `output=$(but status)` |
| Mixed git/but broke state | `but pull` or `but setup` |
| Lost work | `but undo` or `but oplog restore <snapshot-id>` |
For detailed troubleshooting (branch tracking, conflicts, filename issues), see `references/reference.md#troubleshooting-guide`.
## Recovery
Quick undo: `but undo` | Full restore: `but oplog restore <snapshot-id>` | View history: `but oplog`
For recovery from lost work or corrupted state, see `references/reference.md#recovery-scenarios`.
<references>
### Reference Files
- **`references/reference.md`** — Complete CLI reference, JSON schemas, troubleshooting
- **`references/examples.md`** — Real-world workflow patterns with commands
- **`references/ai-integration.md`** — Hooks, MCP server, agent lifecycle
### Related Skills
- [gitbutler-multi-agent](../multi-agent/SKILL.md) — Multi-agent coordination
- [gitbutler-stacks](../stacks/SKILL.md) — Stacked branches
- [gitbutler-complete-branch](../complete-branch/SKILL.md) — Merging to main
### External
- [GitButler Docs](https://docs.gitbutler.com/) — Official documentation
- [GitButler AI Integration](https://docs.gitbutler.com/features/ai-integration/) — Hooks and MCP
</references>
@@ -0,0 +1,238 @@
# AI Integration Reference
GitButler provides hooks, MCP server, and GUI features for AI agent integration.
---
## Lifecycle Hooks
GitButler CLI commands that can be called from AI agent lifecycle hooks.
### Claude Code Hooks
Add to `.claude/hooks.json`:
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|MultiEdit|Write",
"hooks": [
{
"type": "command",
"command": "but claude pre-tool"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|MultiEdit|Write",
"hooks": [
{
"type": "command",
"command": "but claude post-tool"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "but claude stop"
}
]
}
]
}
}
```
**Hook commands:**
| Command | Trigger | Purpose |
|---------|---------|---------|
| `but claude pre-tool` | Before Edit/Write | Prepare workspace, snapshot |
| `but claude post-tool` | After Edit/Write | Auto-assign changes |
| `but claude stop` | Session ends | Finalize commits, cleanup |
### Cursor Hooks
```json
{
"hooks": {
"after-edit": "but cursor after-edit",
"stop": "but cursor stop"
}
}
```
**Hook commands:**
| Command | Purpose |
|---------|---------|
| `but cursor after-edit` | Auto-assign/commit after Cursor edits |
| `but cursor stop` | Finalize when task completes |
---
## MCP Server
Start GitButler's MCP server for programmatic agent access:
```bash
but mcp
```
### Available Tool
**`gitbutler_update_branches`**
Updates commits based on prompt and changes. Designed for async processing:
1. Agent calls after making code changes
2. GitButler records changes + prompt immediately (returns fast)
3. Processing happens asynchronously to create commits
**Input schema:**
```typescript
{
prompt: string // Description of changes made
}
```
**Current limitations:**
- No branch creation via MCP
- No stack management
- No push/submit operations
- No branch navigation
- No restack commands
**Future roadmap (from GitButler docs):**
- Auto-absorbing changes into existing commits
- Creating new branches based on prompt theme
- Creating stacked branches
- More sophisticated commit organization
---
## Agents Tab (GUI)
GitButler GUI provides an Agents Tab for Claude Code integration:
1. **Branch-Agent Binding**: Each virtual branch can be tied to an agent session
2. **Parallel Execution**: Multiple agents run simultaneously, isolated by branch
3. **Automatic Commit Management**: Agent work auto-committed to their branch
4. **Session Persistence**: Agent context preserved across restarts
### Setup
1. Open GitButler GUI for repo: `but gui`
2. Navigate to Agents Tab
3. Create agent sessions tied to virtual branches
4. Configure which branches each agent can modify
---
## Agent Workflow Patterns
### Pattern 1: Hook-Based Auto-Commit
Let GitButler handle commits automatically:
```bash
# Agent instruction
"Never use the git commit command after a task is finished"
```
PostToolUse hook creates commits automatically.
### Pattern 2: Explicit Agent Commits
Agent controls commit timing:
```bash
# Agent creates branch
but branch new agent-feature
# Agent makes changes...
# Agent assigns and commits explicitly
but rub <file-id> agent-feature
but commit agent-feature -m "feat: implementation"
```
### Pattern 3: Multi-Agent with Marks
Set up auto-assignment for agent branches:
```bash
# Agent A's branch receives new changes
but mark agent-a-auth
# Agent B's branch receives new changes
but mark agent-b-api
```
---
## Key Agent Instructions
When configuring agents to work with GitButler:
1. **Never use `git commit`** - Breaks GitButler state
2. **Never use `git add`** - GitButler manages index
3. **Never use `git checkout`** - All branches always applied
4. **Always return to workspace** after any git operations: `git checkout gitbutler/workspace`
5. **Use `but status` to find file IDs** before using `but rub`
---
## Troubleshooting
### Agent commit "orphaned"
**Cause:** Agent used `git commit` instead of `but commit`.
**Solution:**
```bash
git reflog # Find orphaned commit
but branch new recovered-work
# Manually apply changes or cherry-pick
```
### MCP server not responding
**Cause:** Feature may be behind experimental flag.
**Solution:**
1. Check GitButler version (0.16+)
2. Enable experimental features in GUI settings
3. Restart `but mcp`
### Hooks not triggering
**Verify:**
1. Hook file location correct (`.claude/hooks.json`)
2. JSON syntax valid
3. `but` command in PATH
4. GitButler initialized in repo
---
## References
- [GitButler MCP Server Docs](https://docs.gitbutler.com/features/ai-integration/mcp-server)
- [Claude Code Hooks Docs](https://docs.gitbutler.com/features/ai-integration/claude-code-hooks)
- [Cursor Hooks Docs](https://docs.gitbutler.com/features/ai-integration/cursor-hooks)
- [Agents Tab Blog Post](https://blog.gitbutler.com/agents-tab)
@@ -0,0 +1,402 @@
# GitButler Examples
Real-world patterns and workflows for virtual branches, multi-agent collaboration, and post-hoc organization.
---
## Basic Workflows
### First Virtual Branch
```bash
# Initialize (one time)
cd /path/to/repo
but setup
# Check state
but status
# ● 0c60c71 (common base) [origin/main]
# Create branch
but branch new feature-user-auth
# Make changes
echo "export function authenticate()" > src/auth.ts
echo "test('authenticates user')" > src/auth.test.ts
# Check status for file IDs
but status
# ╭┄00 [Unassigned Changes]
# │ m6 A src/auth.ts
# │ p9 A src/auth.test.ts
# Assign and commit
but rub m6 feature-user-auth
but rub p9 feature-user-auth
but commit feature-user-auth -m "feat: add user authentication"
```
### Context Switching (No Checkout!)
```bash
# Working on feature when bug reported
but branch new feature-dashboard
echo "Dashboard code" > dashboard.ts
but rub <id> feature-dashboard
# Bug reported - switch context immediately (no checkout!)
but branch new bugfix-login-timeout
echo "Fix timeout" > login.ts
but rub <id> bugfix-login-timeout
# Both exist in same workspace
but status # Shows both branches
# Commit bugfix first (urgent)
but commit bugfix-login-timeout -m "fix: resolve login timeout"
# Continue feature work
but commit feature-dashboard -m "feat: add dashboard"
```
---
## Reorganizing Work
### Moving Commits Between Branches
```bash
# Oops, committed to wrong branch!
but status
# Shows def5678 "feat: add new feature" on bugfix-branch
# Create correct branch
but branch new feature-new-capability
# Move the commit
but rub def5678 feature-new-capability
# Commit moved!
but status
```
### Squashing Commits
```bash
# Too many small commits on feature-branch
# Squash using explicit command
but squash feature-branch
```
### Post-Hoc File Assignment
```bash
# Made changes without branches
echo "Auth code" > auth.ts
echo "API code" > api.ts
echo "Docs" > README.md
but status
# Shows all files in Unassigned Changes
# Create branches and organize
but branch new feature-auth
but branch new feature-api
but branch new docs-update
# Assign to respective branches
but rub m6 feature-auth
but rub p9 feature-api
but rub i3 docs-update
# Commit each
but commit feature-auth -m "feat: add authentication"
but commit feature-api -m "feat: add API endpoints"
but commit docs-update -m "docs: update readme"
```
---
## Multi-Agent Patterns
### Parallel Feature Development
```bash
# Agent 1 (Claude)
but branch new claude-feature-auth
echo "Auth implementation" > src/auth.ts
but rub <id> claude-feature-auth
but commit claude-feature-auth -m "feat: add authentication"
# Agent 2 (Droid) - simultaneously, same workspace!
but branch new droid-feature-api
echo "API implementation" > src/api.ts
but rub <id> droid-feature-api
but commit droid-feature-api -m "feat: add API endpoints"
# Zero conflicts, zero coordination overhead
```
### Sequential Handoffs
```bash
# Agent A: Initial implementation
but branch new feature-user-management
echo "Initial user code" > user.ts
but rub <id> feature-user-management
but commit feature-user-management -m "feat: initial user management"
# Agent A hands off to Agent B
but branch new feature-user-management-tests --anchor feature-user-management
# Agent B: Adds tests
echo "Tests for user management" > user.test.ts
but rub <id> feature-user-management-tests
but commit feature-user-management-tests -m "test: add user management tests"
```
### Cross-Agent Commit Transfer
```bash
# Agent A finishes work
but branch new agent-a-feature
but commit agent-a-feature -m "feat: implementation complete"
# Agent B creates their branch
but branch new agent-b-continuation
# Transfer commit from A to B
but rub abc1234 agent-b-continuation
# Agent B continues
echo "More work" >> feature.ts
but commit agent-b-continuation -m "feat: continue implementation"
```
---
## Stack Management
### Creating a Linear Stack
```bash
# Base refactoring
but branch new refactor-database
echo "Refactor database layer" > db-refactor.ts
but rub <id> refactor-database
but commit refactor-database -m "refactor: restructure database"
# Build on refactoring
but branch new feature-new-model --anchor refactor-database
echo "New data model" > model.ts
but rub <id> feature-new-model
but commit feature-new-model -m "feat: add new data model"
# Add tests on top
but branch new test-new-model --anchor feature-new-model
echo "Model tests" > model.test.ts
but rub <id> test-new-model
but commit test-new-model -m "test: comprehensive model tests"
# Visualize stack
but status
```
### Submit Stack as PRs
```bash
# Using but CLI (preferred)
but push refactor-database
but pr new refactor-database
but push feature-new-model
but pr new feature-new-model
but push test-new-model
but pr new test-new-model
```
```bash
# Alternative: using git + gh directly
git push origin refactor-database
gh pr create --title "refactor: database layer" --base main
git push origin feature-new-model
gh pr create --title "feat: new data model" --base refactor-database
git push origin test-new-model
gh pr create --title "test: model tests" --base feature-new-model
```
---
## Emergency Recovery
### Recover Deleted Branch
```bash
# Oops, deleted wrong branch
but branch delete important-feature --force
# Check oplog
but oplog
# Undo deletion
but undo
# Verify recovery
but status # Branch recovered!
```
### Recover from Bad Reorganization
```bash
# Snapshot before risky operations
but oplog snapshot --message "Before reorganizing commits"
# Attempt reorganization
but rub <commit1> <branch1>
but rub <commit2> <branch2>
# Result is a mess - restore to snapshot
snapshot_id=$(but oplog | grep "Before reorganizing" | awk '{print $1}')
but oplog restore $snapshot_id
# Back to pre-reorganization state!
```
### Recover from Mixed Git/But Commands
```bash
# Made changes on virtual branch
but branch new my-feature
echo "changes" > file.ts
# Accidentally used git
git add file.ts
git commit -m "oops" # WRONG!
# Recovery
but pull
# If still broken, reinitialize
but oplog snapshot --message "Before recovery"
but setup
```
---
## New in 0.19.0
### Selective Commit with `--changes`
```bash
# Check status for file/hunk IDs
but status
# ╭┄00 [Unassigned Changes]
# │ m6 A src/auth.ts
# │ p9 A src/api.ts
# │ i3 M README.md
# Commit only specific files by ID
but commit feature-auth -p m6,i3 -m "feat: add auth and update docs"
# p9 remains uncommitted
```
### Conflict Resolution with `but resolve`
```bash
# After pulling, a commit has conflicts
but pull
but status
# Shows conflicted commit with ⚠️ marker
# Enter resolution mode
but resolve abc1234
# Fix conflict markers in your editor
# Check what's left
but resolve status
# Finalize
but resolve finish
```
### Squashing with Ranges
```bash
# Squash all commits in a branch
but squash feature-branch
# Squash specific commits
but squash abc1234 def5678
# Squash a range
but squash abc1234..ghi9012
```
### Absorb with Preview
```bash
# Preview where changes would be absorbed
but absorb --dry-run
# Absorb into new commits instead of amending
but absorb --new
```
### Push with Preview
```bash
# See what would be pushed without pushing
but push --dry-run
# Push a specific branch
but push feature-auth
# Push all unpushed branches
but push
```
---
## Tips and Patterns
### Branch Naming
```bash
# Agent-based naming
but branch new claude-feat-user-auth
but branch new droid-fix-api-timeout
# Task-based naming
but branch new feature-authentication
but branch new bugfix-timeout
```
### Snapshot Cadence
```bash
but oplog snapshot --message "Before major reorganization"
but oplog snapshot --message "Before multi-agent coordination"
but oplog snapshot --message "Before complex stack changes"
```
### File Assignment Discipline
```bash
# Good: Assign immediately
echo "code" > file1.ts
but rub <id> my-branch # Right away
```
### JSON Output
```bash
# Get branch commits
but show feature-branch --json | jq '.commits[] | .id'
# Workspace overview
but status --json | jq '.stacks'
```
@@ -0,0 +1,534 @@
# GitButler Reference
Complete CLI reference, JSON schemas, troubleshooting, and recovery patterns.
---
## Command Reference
### Global Options
```bash
but [OPTIONS] <COMMAND>
Global Options (must come BEFORE subcommand):
-C, --current-dir <PATH> 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 <id>` | Show detailed info about a commit or branch |
| `but diff` | Show diff of all uncommitted changes |
| `but diff <id>` | 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 <name>` | Create virtual branch (based on trunk) |
| `but branch new <name> --anchor <parent>` | Create stacked branch |
| `but branch new <name> -a <parent>` | Short form for stacked branch |
| `but branch delete <name>` | Soft delete (requires confirmation) |
| `but branch delete <name> --force` | Force delete |
| `but branch list` | List all branches |
| `but branch list --local` | Only local branches |
| `but unapply <name>` | Remove branch from workspace (keeps in Git) |
| `but apply <name>` | Apply an unapplied branch to workspace |
| `but pick <source> [branch]` | Cherry-pick commit from unapplied branch |
### Committing
| Command | Description |
|---------|-------------|
| `but commit -m "message"` | Commit to inferred branch |
| `but commit <branch> -m "message"` | Commit to specific branch |
| `but commit <branch> -o -m "msg"` | Only commit assigned files (`-o` flag) |
| `but commit -p <id>,<id>` | 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 <target>` | Insert blank commit before target |
| `but commit empty --after <target>` | 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>
```
| 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 <target>` | 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 <commits>` | Squash commits together (by IDs, range, or branch name) |
| `but move <commit> <target>` | Move commit to a different location in the stack |
| `but amend <file> <commit>` | Amend a file change into a specific commit |
| `but uncommit <source>` | Uncommit changes back to unstaged area |
| `but discard <id>` | Discard uncommitted changes from worktree |
| `but mark <branch>` | 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 <username>` | 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 <snapshot-id>` | Restore to specific snapshot |
| `but oplog snapshot --message "msg"` | Create manual snapshot |
### Conflict Resolution
| Command | Description |
|---------|-------------|
| `but resolve <commit-id>` | 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 <commit-id>` 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 <user>` | 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 <branch> --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 <branch>` or `but pick <commit>` |
| Files not committing | Not assigned | `but rub <file-id> <branch>` |
| 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 <file-id> <branch>
# Commit with -o flag
but commit <branch> -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 <head_sha>
# 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 <snapshot-id>
```
#### 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 <commit-sha>
# 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:**
```
<snapshot-commit>
├── 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 <snapshot-sha>:virtual_branches.toml
# Extract file from snapshot
git show <snapshot-sha>: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