From 72c08c17299edb038304fbf6790e7425e84a76c7 Mon Sep 17 00:00:00 2001 From: Scott Chacon Date: Wed, 4 Feb 2026 13:38:56 +0100 Subject: [PATCH] update the commands --- app/(docs)/[[...slug]]/page.tsx | 15 +- app/components/CommandPageComponents.tsx | 104 + content/docs/commands/but-absorb.mdx | 10 +- content/docs/commands/but-alias.mdx | 24 +- content/docs/commands/but-amend.mdx | 4 - content/docs/commands/but-branch.mdx | 7 +- content/docs/commands/but-commit.mdx | 14 +- content/docs/commands/but-config.mdx | 34 +- content/docs/commands/but-diff.mdx | 8 +- content/docs/commands/but-discard.mdx | 17 +- content/docs/commands/but-gui.mdx | 12 +- content/docs/commands/but-mark.mdx | 13 +- content/docs/commands/but-merge.mdx | 4 - content/docs/commands/but-move.mdx | 22 +- content/docs/commands/but-oplog.mdx | 4 - content/docs/commands/but-pick.mdx | 4 - content/docs/commands/but-pr.mdx | 4 - content/docs/commands/but-pull.mdx | 4 - content/docs/commands/but-push.mdx | 13 +- content/docs/commands/but-resolve.mdx | 4 - content/docs/commands/but-reword.mdx | 4 - content/docs/commands/but-rub.mdx | 41 +- content/docs/commands/but-setup.mdx | 19 +- content/docs/commands/but-show.mdx | 29 +- content/docs/commands/but-skill.mdx | 4 - content/docs/commands/but-squash.mdx | 4 - content/docs/commands/but-stage.mdx | 4 - content/docs/commands/but-status.mdx | 20 +- content/docs/commands/but-teardown.mdx | 15 +- content/docs/commands/but-uncommit.mdx | 4 - content/docs/commands/but-undo.mdx | 7 +- content/docs/commands/but-unmark.mdx | 6 +- content/docs/commands/but-update.mdx | 4 - content/docs/commands/meta.json | 4 +- public/llms-full.txt | 5846 +++++++++++++--------- 35 files changed, 3657 insertions(+), 2675 deletions(-) create mode 100644 app/components/CommandPageComponents.tsx diff --git a/app/(docs)/[[...slug]]/page.tsx b/app/(docs)/[[...slug]]/page.tsx index e5c91ba..238d93c 100644 --- a/app/(docs)/[[...slug]]/page.tsx +++ b/app/(docs)/[[...slug]]/page.tsx @@ -9,6 +9,7 @@ import { TypeTable } from "fumadocs-ui/components/type-table" import { Accordion, Accordions } from "fumadocs-ui/components/accordion" import ImageSection from "@/app/components/ImageSection" import CliBlock from "@/app/components/CliBlock" +import { getCommandPageComponents } from "@/app/components/CommandPageComponents" import type { ComponentProps, FC } from "react" interface Param { @@ -75,6 +76,10 @@ export default async function Page(props: { params: Promise }): Promise ) + // Detect if this is a command page + const isCommandPage = page.file.path.includes('commands/') + const commandComponents = getCommandPageComponents(isCommandPage) + return ( }): Promise - {page.data.title.replace(/`/g, '')} + {isCommandPage ? ( +
+ >_ + {page.data.title.replace(/`/g, '')} +
+ ) : ( + {page.data.title.replace(/`/g, '')} + )} {page.data.description} + >_ +

{children}

+ + ) +} + +// Custom strong/bold for options - styles them as green pills +export function CommandStrong({ children }: ComponentProps<'strong'>) { + const text = String(children) + + // Check if this looks like option syntax with Usage: + if (text.includes('Usage:')) { + return {children} + } + + return {children} +} + +// Helper to check if content looks like an option/argument +function isOptionOrArgument(text: string): boolean { + return ( + text.startsWith('-') || + text.includes('--') || + text.startsWith('<') && text.endsWith('>') || + text.match(/^-[a-z],\s+--[a-z-]+/i) !== null + ) +} + +// Custom code for inline code - adds green background for options +export function CommandCode({ children, className, ...props }: ComponentProps<'code'>) { + // If it's in a pre tag (code block), don't style it + if (className?.includes('language-')) { + return {children} + } + + // Check if this looks like an option flag or argument + const text = String(children) + + if (isOptionOrArgument(text)) { + return ( + + {children} + + ) + } + + return {children} +} + +// Custom list item for options/arguments +export function CommandLi({ children, ...props }: ComponentProps<'li'>) { + // Process children to find code elements + let hasOptionCode = false + + Children.forEach(children, (child) => { + if (isValidElement(child) && child.type === 'code') { + const childProps = child.props as any + const text = String(childProps.children || '') + if (isOptionOrArgument(text)) { + hasOptionCode = true + } + } + }) + + if (hasOptionCode) { + return ( +
  • +
    {children}
    +
  • + ) + } + + return
  • {children}
  • +} + +// Custom unordered list +export function CommandUl({ children, ...props }: ComponentProps<'ul'>) { + return
      {children}
    +} + +// Wrapper to detect command pages and apply custom styling +export function getCommandPageComponents(isCommandPage: boolean): Record { + if (!isCommandPage) { + return {} + } + + return { + h1: CommandH1, + code: CommandCode, + li: CommandLi, + ul: CommandUl, + strong: CommandStrong, + } as Record +} diff --git a/content/docs/commands/but-absorb.mdx b/content/docs/commands/but-absorb.mdx index b66f556..b3f9aa6 100644 --- a/content/docs/commands/but-absorb.mdx +++ b/content/docs/commands/but-absorb.mdx @@ -3,10 +3,6 @@ title: "`but absorb`" description: "Amends changes into the appropriate commits where they belong." --- -# Amends changes into the appropriate commits where they belong. - -Amends changes into the appropriate commits where they belong. - The semantic for finding "the appropriate commit" is as follows: - If a change has a dependency to a particular commit, it will be amended into that particular commit @@ -20,9 +16,11 @@ Optionally an identifier to an Uncommitted File or a Branch (stack) may be provi - If a Branch (stack) id is provided, absorb will be performed for all changes staged to that stack - If no source is provided, absorb is performed for all uncommitted changes -If --dry-run is specified, no changes will be made; instead, the absorption plan (what changes would be absorbed by which commits) will be shown. +If `--dry-run` is specified, no changes will be made; instead, the absorption plan +(what changes would be absorbed by which commits) will be shown. -If --new is specified, new commits will be created for absorbed changes instead of amending existing commits. +If `--new` is specified, new commits will be created for absorbed changes +instead of amending existing commits. **Usage:** `but absorb [SOURCE] [OPTIONS]` diff --git a/content/docs/commands/but-alias.mdx b/content/docs/commands/but-alias.mdx index 9e5cf7d..437e42c 100644 --- a/content/docs/commands/but-alias.mdx +++ b/content/docs/commands/but-alias.mdx @@ -3,27 +3,29 @@ title: "`but alias`" description: "Manage command aliases." --- -# Manage command aliases. +Aliases allow you to create shortcuts for commonly used commands. +They are stored in git config under the `but.alias.*` namespace. -Manage command aliases. - -Aliases allow you to create shortcuts for commonly used commands. They are stored in git config under the but.alias.* namespace. - -Examples +## Examples List all configured aliases: - but alias +```text +but alias +``` Create a new alias: - but alias add st status - but alias add stv "status --verbose" +```text +but alias add st status +but alias add stv "status --verbose" +``` Remove an alias: - but alias remove st - +```text +but alias remove st +``` **Usage:** `but alias ` diff --git a/content/docs/commands/but-amend.mdx b/content/docs/commands/but-amend.mdx index cec40b5..9b17b1b 100644 --- a/content/docs/commands/but-amend.mdx +++ b/content/docs/commands/but-amend.mdx @@ -3,10 +3,6 @@ title: "`but amend`" description: "Amend a file change into a specific commit and rebases any dependent commits." --- -# Amend a file change into a specific commit and rebases any dependent commits. - -Amend a file change into a specific commit and rebases any dependent commits. - Wrapper for `but rub `. **Usage:** `but amend ` diff --git a/content/docs/commands/but-branch.mdx b/content/docs/commands/but-branch.mdx index 6f6210c..ccde73d 100644 --- a/content/docs/commands/but-branch.mdx +++ b/content/docs/commands/but-branch.mdx @@ -3,11 +3,8 @@ title: "`but branch`" description: "Commands for managing branches." --- -# Commands for managing branches. - -Commands for managing branches. - -This includes creating, deleting, listing, showing details about, and applying and unapplying branches. +This includes creating, deleting, listing, showing details about, and +applying and unapplying branches. By default without a subcommand, it will list the branches. diff --git a/content/docs/commands/but-commit.mdx b/content/docs/commands/but-commit.mdx index 1957122..d4fce13 100644 --- a/content/docs/commands/but-commit.mdx +++ b/content/docs/commands/but-commit.mdx @@ -3,10 +3,6 @@ title: "`but commit`" description: "Commit changes to a stack." --- -# Commit changes to a stack. - -Commit changes to a stack. - The `but commit` command allows you to create a new commit on a specified branch (stack) with the current uncommitted changes. @@ -82,3 +78,13 @@ If a target is provided without --before or --after, defaults to --before behavi * `--before` `` — Insert the blank commit before this commit or branch * `--after` `` — Insert the blank commit after this commit or branch +## Options + +* `-m`, `--message` `` — Commit message +* `-f`, `--file` `` — Read commit message from file +* `-c`, `--create` — Whether to create a new branch for this commit. If the branch name given matches an existing branch, that branch will be used instead. If no branch name is given, a new branch with a generated name will be created +* `-o`, `--only` — Only commit staged files, not unstaged files +* `-n`, `--no-hooks` — Bypass pre-commit hooks +* `-i`, `--ai` `` — Generate commit message using AI with optional user summary +* `-F`, `--files` `` — Uncommitted file or hunk CLI IDs to include in the commit. Can be specified multiple times or as comma-separated values. If not specified, all uncommitted changes (or changes staged to the target branch) are committed + diff --git a/content/docs/commands/but-config.mdx b/content/docs/commands/but-config.mdx index dd097ca..fe860c8 100644 --- a/content/docs/commands/but-config.mdx +++ b/content/docs/commands/but-config.mdx @@ -3,36 +3,42 @@ title: "`but config`" description: "View and manage GitButler configuration." --- -# View and manage GitButler configuration. +Without a subcommand, displays an overview of important settings including +user information, target branch, forge configuration, and AI setup. -View and manage GitButler configuration. - -Without a subcommand, displays an overview of important settings including user information, target branch, forge configuration, and AI setup. - -Examples +## Examples View configuration overview: - but config +```text +but config +``` View/set user configuration: - but config user - but config user set name "John Doe" - but config user set email john@example.com +```text +but config user +but config user set name "John Doe" +but config user set email john@example.com +``` View/set forge configuration: - but config forge +```text +but config forge +``` View/set target branch: - but config target +```text +but config target +``` View/set metrics: - but config metrics - +```text +but config metrics +``` **Usage:** `but config ` diff --git a/content/docs/commands/but-diff.mdx b/content/docs/commands/but-diff.mdx index be87717..838e5ad 100644 --- a/content/docs/commands/but-diff.mdx +++ b/content/docs/commands/but-diff.mdx @@ -3,12 +3,8 @@ title: "`but diff`" description: "Displays the diff of changes in the repo." --- -# Displays the diff of changes in the repo. - -Displays the diff of changes in the repo. - -Without any arguments, it shows the diff of all uncommitted changes. Optionally, a CLI ID argument can be provided, which chan show the diff specific to - +Without any arguments, it shows the diff of all uncommitted changes. +Optionally, a CLI ID argument can be provided, which chan show the diff specific to - an uncommitted file - a branch - an entire stack diff --git a/content/docs/commands/but-discard.mdx b/content/docs/commands/but-discard.mdx index d539f63..d1c53f0 100644 --- a/content/docs/commands/but-discard.mdx +++ b/content/docs/commands/but-discard.mdx @@ -3,20 +3,19 @@ title: "`but discard`" description: "Discard uncommitted changes from the worktree." --- -# Discard uncommitted changes from the worktree. +This command permanently discards changes to files, restoring them to their +state in the HEAD commit. Use this to undo unwanted modifications. -Discard uncommitted changes from the worktree. +The ID parameter should be a file ID as shown in `but status`. You can +discard a whole file or specific hunks within a file. -This command permanently discards changes to files, restoring them to their state in the HEAD commit. Use this to undo unwanted modifications. - -The ID parameter should be a file ID as shown in but status. You can discard a whole file or specific hunks within a file. - -Examples +## Examples Discard all changes to a file: - but discard a1 - +```text +but discard a1 +``` **Usage:** `but discard ` diff --git a/content/docs/commands/but-gui.mdx b/content/docs/commands/but-gui.mdx index 4bc2423..ce6baf7 100644 --- a/content/docs/commands/but-gui.mdx +++ b/content/docs/commands/but-gui.mdx @@ -3,15 +3,13 @@ title: "`but gui`" description: "Open the GitButler GUI for the current project." --- -# Open the GitButler GUI for the current project. +Running `but gui` will launch the GitButler graphical user interface +in the current directory's GitButler project. -Open the GitButler GUI for the current project. +This provides a visual way to manage branches, commits, and uncommitted +changes, complementing the command-line interface. -Running but gui will launch the GitButler graphical user interface in the current directory's GitButler project. - -This provides a visual way to manage branches, commits, and uncommitted changes, complementing the command-line interface. - -You can also just run but . as a shorthand to open the GUI. +You can also just run `but .` as a shorthand to open the GUI. **Usage:** `but gui` diff --git a/content/docs/commands/but-mark.mdx b/content/docs/commands/but-mark.mdx index 0a03352..a2bd417 100644 --- a/content/docs/commands/but-mark.mdx +++ b/content/docs/commands/but-mark.mdx @@ -3,15 +3,14 @@ title: "`but mark`" description: "Mark a commit or branch for auto-stage or auto-commit." --- -# Mark a commit or branch for auto-stage or auto-commit. +Creates or removes a rule for auto-staging or auto-committing changes +to the specified target entity. -Mark a commit or branch for auto-stage or auto-commit. +If you mark a branch, new unstaged changes that GitButler sees when +you run any command will be automatically staged to that branch. -Creates or removes a rule for auto-staging or auto-committing changes to the specified target entity. - -If you mark a branch, new unstaged changes that GitButler sees when you run any command will be automatically staged to that branch. - -If you mark a commit, new uncommitted changes will automatically be amended into the marked commit. +If you mark a commit, new uncommitted changes will automatically be +amended into the marked commit. **Usage:** `but mark [OPTIONS]` diff --git a/content/docs/commands/but-merge.mdx b/content/docs/commands/but-merge.mdx index e697a58..dce24ef 100644 --- a/content/docs/commands/but-merge.mdx +++ b/content/docs/commands/but-merge.mdx @@ -3,10 +3,6 @@ title: "`but merge`" description: "Merge a branch into your local target branch." --- -# Merge a branch into your local target branch. - -Merge a branch into your local target branch. - If the target branch is local (`gb-local`), finds the local branch that the target references (e.g., `gb-local/master` becomes `master`) and merges the specified branch into that local branch. After merging, runs the equivalent of `but pull` diff --git a/content/docs/commands/but-move.mdx b/content/docs/commands/but-move.mdx index 28b7310..230de27 100644 --- a/content/docs/commands/but-move.mdx +++ b/content/docs/commands/but-move.mdx @@ -3,28 +3,30 @@ title: "`but move`" description: "Move a commit to a different location in the stack." --- -# Move a commit to a different location in the stack. - -Move a commit to a different location in the stack. - -By default, commits are moved to be before (below) the target. Use --after to move the commit after (above) the target instead. +By default, commits are moved to be before (below) the target. +Use `--after` to move the commit after (above) the target instead. When moving to a branch, the commit is placed at the top of that branch's stack. -Examples +## Examples Move a commit before another commit: - but move abc123 def456 +```text +but move abc123 def456 +``` Move a commit after another commit: - but move abc123 def456 --after +```text +but move abc123 def456 --after +``` Move a commit to a different branch (places at top): - but move abc123 my-feature-branch - +```text +but move abc123 my-feature-branch +``` **Usage:** `but move [OPTIONS]` diff --git a/content/docs/commands/but-oplog.mdx b/content/docs/commands/but-oplog.mdx index de20831..2228928 100644 --- a/content/docs/commands/but-oplog.mdx +++ b/content/docs/commands/but-oplog.mdx @@ -3,10 +3,6 @@ title: "`but oplog`" description: "Commands for viewing and managing operation history." --- -# Commands for viewing and managing operation history. - -Commands for viewing and managing operation history. - Displays a list of past operations performed in the repository, including their timestamps and descriptions. diff --git a/content/docs/commands/but-pick.mdx b/content/docs/commands/but-pick.mdx index 72c5b50..7f817eb 100644 --- a/content/docs/commands/but-pick.mdx +++ b/content/docs/commands/but-pick.mdx @@ -3,10 +3,6 @@ title: "`but pick`" description: "Cherry-pick a commit from an unapplied branch into an applied virtual branch." --- -# Cherry-pick a commit from an unapplied branch into an applied virtual branch. - -Cherry-pick a commit from an unapplied branch into an applied virtual branch. - This command allows you to pick individual commits from unapplied branches and apply them to your current workspace branches. diff --git a/content/docs/commands/but-pr.mdx b/content/docs/commands/but-pr.mdx index 80ac2ff..e2e211c 100644 --- a/content/docs/commands/but-pr.mdx +++ b/content/docs/commands/but-pr.mdx @@ -3,10 +3,6 @@ title: "`but pr`" description: "Commands for creating and managing pull requests on a forge." --- -# Commands for creating and managing pull requests on a forge. - -Commands for creating and managing pull requests on a forge. - If you are authenticated with a forge using but config forge auth, you can use the but pr commands to create pull requests (or merge requests) on the remote repository for your branches. Running but pr without a subcommand defaults to but pr new, which will prompt you to select a branch to create a PR for. diff --git a/content/docs/commands/but-pull.mdx b/content/docs/commands/but-pull.mdx index a036632..58b007b 100644 --- a/content/docs/commands/but-pull.mdx +++ b/content/docs/commands/but-pull.mdx @@ -3,10 +3,6 @@ title: "`but pull`" description: "Updates all applied branches to be up to date with the target branch." --- -# Updates all applied branches to be up to date with the target branch. - -Updates all applied branches to be up to date with the target branch. - This fetches the latest changes from the remote and rebases all applied branches on top of the updated target branch. diff --git a/content/docs/commands/but-push.mdx b/content/docs/commands/but-push.mdx index 057bc7e..dbf0d5e 100644 --- a/content/docs/commands/but-push.mdx +++ b/content/docs/commands/but-push.mdx @@ -3,21 +3,16 @@ title: "`but push`" description: "Push changes in a branch to remote." --- -# Push changes in a branch to remote. - -Push changes in a branch to remote. - -but push will update the remote with the latest commits from the applied branch(es). +`but push` will update the remote with the latest commits from the +applied branch(es). Without a branch ID: - - Interactive mode: Lists all branches with unpushed commits and prompts for selection - Non-interactive mode: Automatically pushes all branches with unpushed commits With a branch ID: - -- but push bu - push the branch with CLI ID "bu" -- but push feature-branch - push the branch named "feature-branch" +- `but push bu` - push the branch with CLI ID "bu" +- `but push feature-branch` - push the branch named "feature-branch" **Usage:** `but push [BRANCH_ID] [OPTIONS]` diff --git a/content/docs/commands/but-resolve.mdx b/content/docs/commands/but-resolve.mdx index eaca328..1903658 100644 --- a/content/docs/commands/but-resolve.mdx +++ b/content/docs/commands/but-resolve.mdx @@ -3,10 +3,6 @@ title: "`but resolve`" description: "Resolve conflicts in a commit." --- -# Resolve conflicts in a commit. - -Resolve conflicts in a commit. - When a commit is in a conflicted state (marked with conflicts during rebase), use this command to enter resolution mode, resolve the conflicts, and finalize. diff --git a/content/docs/commands/but-reword.mdx b/content/docs/commands/but-reword.mdx index 4361b1e..7091d0f 100644 --- a/content/docs/commands/but-reword.mdx +++ b/content/docs/commands/but-reword.mdx @@ -3,10 +3,6 @@ title: "`but reword`" description: "Edit the commit message of the specified commit." --- -# Edit the commit message of the specified commit. - -Edit the commit message of the specified commit. - You can easily change the commit message of any of your commits by running `but reword ` and providing a new message in the editor. diff --git a/content/docs/commands/but-rub.mdx b/content/docs/commands/but-rub.mdx index 9787fc8..2d3e349 100644 --- a/content/docs/commands/but-rub.mdx +++ b/content/docs/commands/but-rub.mdx @@ -3,37 +3,44 @@ title: "`but rub`" description: "Combines two entities together to perform an operation like amend, squash, stage, or move." --- -# Combines two entities together to perform an operation like amend, squash, stage, or move. +The `rub` command is a simple verb that helps you do a number of editing +operations by doing combinations of two things. -Combines two entities together to perform an operation like amend, squash, stage, or move. - -The rub command is a simple verb that helps you do a number of editing operations by doing combinations of two things. - -For example, you can "rub" a file onto a branch to stage that file to the branch. You can also "rub" a commit onto another commit to squash them together. You can rub a commit onto a branch to move that commit. You can rub a file from one commit to another. +For example, you can "rub" a file onto a branch to stage that file to +the branch. You can also "rub" a commit onto another commit to squash +them together. You can rub a commit onto a branch to move that commit. +You can rub a file from one commit to another. Non-exhaustive list of operations: - │Source │Target - ──────┼───────────┼────── - Amend │File,Branch│Commit - Squash│Commit │Commit - Stage │File,Branch│Branch - Move │Commit │Branch +```text + │Source │Target +──────┼───────────┼────── +Amend │File,Branch│Commit +Squash│Commit │Commit +Stage │File,Branch│Branch +Move │Commit │Branch +``` -Examples +## Examples Squashing two commits into one (combining the commit messages): - but rub 3868155 abe3f53f +```text +but rub 3868155 abe3f53f +``` Amending a commit with the contents of a modified file: - but rub README.md abe3f53f +```text +but rub README.md abe3f53f +``` Moving a commit from one branch to another: - but rub 3868155 feature-branch - +```text +but rub 3868155 feature-branch +``` **Usage:** `but rub ` diff --git a/content/docs/commands/but-setup.mdx b/content/docs/commands/but-setup.mdx index 6a5370c..a8a779e 100644 --- a/content/docs/commands/but-setup.mdx +++ b/content/docs/commands/but-setup.mdx @@ -3,25 +3,23 @@ title: "`but setup`" description: "Sets up a GitButler project from a git repository in the current directory." --- -# Sets up a GitButler project from a git repository in the current directory. - -Sets up a GitButler project from a git repository in the current directory. - This command will: - - Add the repository to the global GitButler project registry - Switch to the gitbutler/workspace branch (if not already on it) - Set up a default target branch (the remote's HEAD) - Add a gb-local remote if no push remote exists -If you have an existing Git repository and want to start using GitButler with it, you can run this command to set up the necessary configuration and data structures. +If you have an existing Git repository and want to start using GitButler +with it, you can run this command to set up the necessary configuration +and data structures. -Examples +## Examples Initialize a new git repository and set up GitButler: - but setup --init - +```text +but setup --init +``` **Usage:** `but setup [OPTIONS]` @@ -29,5 +27,6 @@ Initialize a new git repository and set up GitButler: * `--init` — Initialize a new git repository with an empty commit if one doesn't exist. -This is useful when running in non-interactive environments (like CI/CD) where you want to ensure a git repository exists before setting up GitButler. +This is useful when running in non-interactive environments (like CI/CD) +where you want to ensure a git repository exists before setting up GitButler. diff --git a/content/docs/commands/but-show.mdx b/content/docs/commands/but-show.mdx index b878f69..857dd70 100644 --- a/content/docs/commands/but-show.mdx +++ b/content/docs/commands/but-show.mdx @@ -3,32 +3,37 @@ title: "`but show`" description: "Shows detailed information about a commit or branch." --- -# Shows detailed information about a commit or branch. +When given a commit ID, displays the full commit message, author information, +committer information (if different from author), and the list of files modified. -Shows detailed information about a commit or branch. +When given a branch name, displays the branch name and a list of all commits +on that branch. Use --verbose to show full commit messages and files changed. -When given a commit ID, displays the full commit message, author information, committer information (if different from author), and the list of files modified. - -When given a branch name, displays the branch name and a list of all commits on that branch. Use --verbose to show full commit messages and files changed. - -Examples +## Examples Show commit details by short commit ID: - but show a1b2c3d +```text +but show a1b2c3d +``` Show commit details by CLI ID: - but show c5 +```text +but show c5 +``` Show branch commits by branch name: - but show my-feature-branch +```text +but show my-feature-branch +``` Show branch with full commit details: - but show my-feature-branch --verbose - +```text +but show my-feature-branch --verbose +``` **Usage:** `but show [OPTIONS]` diff --git a/content/docs/commands/but-skill.mdx b/content/docs/commands/but-skill.mdx index 466e0d7..fdedf60 100644 --- a/content/docs/commands/but-skill.mdx +++ b/content/docs/commands/but-skill.mdx @@ -3,10 +3,6 @@ title: "`but skill`" description: "Manage Claude AI skills for GitButler." --- -# Manage Claude AI skills for GitButler. - -Manage Claude AI skills for GitButler. - Skills provide enhanced AI capabilities for working with GitButler through Claude Code and other AI assistants. diff --git a/content/docs/commands/but-squash.mdx b/content/docs/commands/but-squash.mdx index d9d9b7d..d0eb104 100644 --- a/content/docs/commands/but-squash.mdx +++ b/content/docs/commands/but-squash.mdx @@ -3,10 +3,6 @@ title: "`but squash`" description: "Squash commits together." --- -# Squash commits together. - -Squash commits together. - Can be invoked in three ways: 1. Using commit identifiers: `but squash ` or `but squash ...` - Squashes all commits except the last into the last commit diff --git a/content/docs/commands/but-stage.mdx b/content/docs/commands/but-stage.mdx index 3fb6b98..d15f9e7 100644 --- a/content/docs/commands/but-stage.mdx +++ b/content/docs/commands/but-stage.mdx @@ -3,10 +3,6 @@ title: "`but stage`" description: "Stages a file or hunk to a specific branch." --- -# Stages a file or hunk to a specific branch. - -Stages a file or hunk to a specific branch. - Wrapper for `but rub `. **Usage:** `but stage ` diff --git a/content/docs/commands/but-status.mdx b/content/docs/commands/but-status.mdx index 2a58222..8484ef2 100644 --- a/content/docs/commands/but-status.mdx +++ b/content/docs/commands/but-status.mdx @@ -3,22 +3,24 @@ title: "`but status`" description: "Overview of the project workspace state." --- -# Overview of the project workspace state. +This shows unstaged files, files staged to stacks, all applied +branches (stacked or parallel), commits on each of those branches, +upstream commits that are unintegrated, commit status (pushed or local), +and base branch information. -Overview of the project workspace state. - -This shows unstaged files, files staged to stacks, all applied branches (stacked or parallel), commits on each of those branches, upstream commits that are unintegrated, commit status (pushed or local), and base branch information. - -Examples +## Examples Normal usage: - but status +```text +but status +``` Shorthand with listing files modified - but status -f - +```text +but status -f +``` **Usage:** `but status [OPTIONS]` diff --git a/content/docs/commands/but-teardown.mdx b/content/docs/commands/but-teardown.mdx index 1928096..b203d3c 100644 --- a/content/docs/commands/but-teardown.mdx +++ b/content/docs/commands/but-teardown.mdx @@ -3,25 +3,22 @@ title: "`but teardown`" description: "Exit GitButler mode and return to normal Git workflow." --- -# Exit GitButler mode and return to normal Git workflow. - -Exit GitButler mode and return to normal Git workflow. - This command: - - Creates an oplog snapshot of the current state - Finds the first active branch and checks it out - Cherry-picks any dangling commits from gitbutler/workspace - Provides instructions on how to return to GitButler mode -This is useful when you want to temporarily or permanently leave GitButler management and work with standard Git commands. +This is useful when you want to temporarily or permanently leave GitButler +management and work with standard Git commands. -Examples +## Examples Exit GitButler mode: - but teardown - +```text +but teardown +``` **Usage:** `but teardown` diff --git a/content/docs/commands/but-uncommit.mdx b/content/docs/commands/but-uncommit.mdx index 106ee34..1ef4520 100644 --- a/content/docs/commands/but-uncommit.mdx +++ b/content/docs/commands/but-uncommit.mdx @@ -3,10 +3,6 @@ title: "`but uncommit`" description: "Uncommit changes from a commit or file-in-commit to the unstaged area." --- -# Uncommit changes from a commit or file-in-commit to the unstaged area. - -Uncommit changes from a commit or file-in-commit to the unstaged area. - Wrapper for `but rub zz`. **Usage:** `but uncommit ` diff --git a/content/docs/commands/but-undo.mdx b/content/docs/commands/but-undo.mdx index c833792..560c0b9 100644 --- a/content/docs/commands/but-undo.mdx +++ b/content/docs/commands/but-undo.mdx @@ -3,11 +3,8 @@ title: "`but undo`" description: "Undo the last operation by reverting to the previous snapshot." --- -# Undo the last operation by reverting to the previous snapshot. - -Undo the last operation by reverting to the previous snapshot. - -This is a shorthand for restoring to the last oplog entry before the current one. It allows you to quickly undo the most recent operation. +This is a shorthand for restoring to the last oplog entry before the +current one. It allows you to quickly undo the most recent operation. **Usage:** `but undo` diff --git a/content/docs/commands/but-unmark.mdx b/content/docs/commands/but-unmark.mdx index 8f41f13..e5796e3 100644 --- a/content/docs/commands/but-unmark.mdx +++ b/content/docs/commands/but-unmark.mdx @@ -3,11 +3,7 @@ title: "`but unmark`" description: "Removes any marks from the workspace" --- -# Removes any marks from the workspace - -Removes any marks from the workspace - -This will unmark anything that has been marked by the but mark command. +This will unmark anything that has been marked by the `but mark` command. **Usage:** `but unmark` diff --git a/content/docs/commands/but-update.mdx b/content/docs/commands/but-update.mdx index cbcba8c..2a6988b 100644 --- a/content/docs/commands/but-update.mdx +++ b/content/docs/commands/but-update.mdx @@ -3,10 +3,6 @@ title: "`but update`" description: "Manage GitButler CLI and app updates." --- -# Manage GitButler CLI and app updates. - -Manage GitButler CLI and app updates. - Check for new versions, install updates, or suppress update notifications. **Usage:** `but update ` diff --git a/content/docs/commands/meta.json b/content/docs/commands/meta.json index 93bf6e5..acccdfe 100644 --- a/content/docs/commands/meta.json +++ b/content/docs/commands/meta.json @@ -31,8 +31,6 @@ "but-uncommit", "but-amend", "but-squash", - "but-move", - "but-pick", "---Operations Log---", "but-oplog", "but-undo", @@ -42,6 +40,8 @@ "but-alias", "but-config", "---Miscellaneous---", + "but-move", + "but-pick", "but-skill" ] } diff --git a/public/llms-full.txt b/public/llms-full.txt index eda7bbc..c496f33 100644 --- a/public/llms-full.txt +++ b/public/llms-full.txt @@ -10,6 +10,7 @@ - [overview](#overview.mdx) - [releases](#releases.mdx) - [why-gitbutler](#why-gitbutler.mdx) +- [workspace-branch](#workspace-branch.mdx) - [custom-csp](#custom-csp.mdx) - [fetch-push](#fetch-push.mdx) - [fixing-conflicts-outside-gitbutler](#fixing-conflicts-outside-gitbutler.mdx) @@ -22,7 +23,6 @@ - [ai-assistance](#ai-assistance.mdx) - [branch-lanes](#branch-lanes.mdx) - [commits](#commits.mdx) -- [integration-branch](#integration-branch.mdx) - [merging](#merging.mdx) - [moving-branches](#moving-branches.mdx) - [pushing-and-fetching](#pushing-and-fetching.mdx) @@ -42,51 +42,179 @@ - [but-absorb](#but-absorb.mdx) - [but-alias](#but-alias.mdx) - [but-amend](#but-amend.mdx) -- [but-apply](#but-apply.mdx) -- [but-base](#but-base.mdx) - [but-branch](#but-branch.mdx) - [but-commit](#but-commit.mdx) - [but-config](#but-config.mdx) -- [but-describe](#but-describe.mdx) - [but-diff](#but-diff.mdx) - [but-discard](#but-discard.mdx) -- [but-forge](#but-forge.mdx) - [but-gui](#but-gui.mdx) -- [but-init](#but-init.mdx) -- [but-lazy](#but-lazy.mdx) - [but-mark](#but-mark.mdx) - [but-merge](#but-merge.mdx) -- [but-new](#but-new.mdx) +- [but-move](#but-move.mdx) - [but-oplog](#but-oplog.mdx) +- [but-pick](#but-pick.mdx) - [but-pr](#but-pr.mdx) - [but-pull](#but-pull.mdx) - [but-push](#but-push.mdx) - [but-resolve](#but-resolve.mdx) -- [but-restore](#but-restore.mdx) - [but-reword](#but-reword.mdx) - [but-rub](#but-rub.mdx) - [but-setup](#but-setup.mdx) - [but-show](#but-show.mdx) +- [but-skill](#but-skill.mdx) - [but-squash](#but-squash.mdx) - [but-stage](#but-stage.mdx) - [but-status](#but-status.mdx) - [but-teardown](#but-teardown.mdx) -- [but-unapply](#but-unapply.mdx) - [but-uncommit](#but-uncommit.mdx) - [but-undo](#but-undo.mdx) - [but-unmark](#but-unmark.mdx) - [but-update](#but-update.mdx) - [commands-overview](#commands-overview.mdx) - [installation](#installation.mdx) +- [ai-stuff](#ai-stuff.mdx) - [branching-and-commiting](#branching-and-commiting.mdx) +- [conclusion](#conclusion.mdx) +- [configuration](#configuration.mdx) +- [conflict-resolution](#conflict-resolution.mdx) - [editing-commits](#editing-commits.mdx) +- [forges](#forges.mdx) - [initializing-a-repository](#initializing-a-repository.mdx) +- [inspecting](#inspecting.mdx) - [operations-log](#operations-log.mdx) - [rubbing](#rubbing.mdx) +- [scripting](#scripting.mdx) - [tutorial-overview](#tutorial-overview.mdx) - [updating-the-base](#updating-the-base.mdx) +# butler-flow.mdx + +Butler Flow is a lightweight, branch-based workflow enabled by GitButler's virtual branch functionality. + +For most modern software development teams that want to set up a culture of shipping, who push to production every day, who are constantly testing and deploying, one of the most popular and effective development workflows is [GitHub Flow](https://docs.github.com/en/get-started/quickstart/github-flow). + +These are wise words, enterprising men quote 'em. Don't act surprised, you guys, [cuz I wrote 'em](https://scottchacon.com/2011/08/31/github-flow). + +However, a decade later, and with a new and more powerful branching toolset, we're able to amend that basic workflow to be simpler, faster, more flexible, and less error prone. Let's take a quick minute to explore what the Butler Flow is for software development teams using GitButler and how this can make all of our lives easier. + +## Overview + +In a nutshell, the basic development cycle is very simple. + +- All work is based off a "target branch", which is a representation of released production code. +- All work immediately exists in a branch. +- Work can be shared with teammates for review as early as possible. +- All branches that are close to merging can be applied locally for integration testing. +- Branches can be reviewed independently and merged after final review. +- Integrated branches are automatically removed from developer context. + +### The Target Branch + +In stock, vanilla Git tooling, there is nothing specified as the production branch, no special "trunk". It is only by convention that this is enforced. + +In GitButler, virtual branches will not work without the specification of a special "target branch". Everything exists in relation to this special branch, everything that differs from it must be accounted for by being owned by some other branch, until those changes are integrated. + +### Virtual Branches + +Once you choose a target branch, everything that is in your working directory and not ignored by the Git Ignore specification must be owned by a virtual branch. If you don't have one active, GitButler will automatically create one for you. + +All subsequent changes to your working directory, either by applying other branches or directly modifying files, must be owned by a virtual branch. + +Branches are meant to be small, independent and rapidly integrated. However, longer lived branches can be continuously re-integrated, keeping them clean and mergeable for long periods of time if needed, while still being shareable and reviewable with your team. + +Virtual branches can be started and ended entirely independently of each other. Developers can work on longer branches, while starting, reviewing, finishing, merging, and deleting small ones without ever changing branch context. + +### Collaboration + +All your team's work, whether created and managed by GitButler or not, exists on the central server as normal Git branches. These will automatically be pulled down and kept up to date by GitButler and can be converted into virtual branches and applied to your working directory in addition to your branches. + +This allows you to integrate work from the rest of your team continuously and early, while still keeping non-dependent changes separated and independently reviewable and mergable. It allows you to review code without needing to entirely switch contexts, and to do so early and often in an executable environment. + +Merge conflicts are known almost as soon as they occur and can be communicated about and collaborated on far before an upstream merge. + +### Maintenance + +Virtual branches can remain applied locally until they are merged into your upstream target branch. Once integrated by any fashion (squash merge, rebase, merge), the virtual branch is automatically disposed of, keeping cruft and overhead low. + + +# cli-overview.mdx + +In addition to the Desktop graphical client, GitButler ships with a command line interface that allows you to do all of the same powerful things that the GUI does, but from the terminal or via scripts or agents. + +https://www.youtube.com/watch?v=loavN_hHuEs + +This includes tooling for: + +- [managing stacked and parallel branches](cli-guides/cli-tutorial/branching-and-commiting) +- [inspecting and diffing](cli-guides/cli-tutorial/inspecting) +- [an operations log and restoration of older states](cli-guides/cli-tutorial/operations-log) +- [powerful commit editing](cli-guides/cli-tutorial/rubbing) +- [upstream management](cli-guides/cli-tutorial/updating-the-base) +- [conflict resolution](cli-guides/cli-tutorial/conflict-resolution) +- [working with forges](cli-guides/cli-tutorial/forges) +- [aliases and configuration](cli-guides/cli-tutorial/configuration) + +All with a simple command line tool. + +## Quick Reference + +For a quick reference of the most commonly used commands, check out our [**GitButler CLI Cheat Sheet**](/cli/cheat) - available as a web page or downloadable PDF. + +## Getting Started + +Feel free to get started with our [Tutorial](cli-guides/installation) or just dig into the available commands. + + +# index.mdx + +## Overview + +GitButler is a new Source Code Management system designed to manage your branches, record and backup your work, be your Git client, help with your code and much more. Our focus is everything after writing code in your editor and before sharing it on GitHub. We're focused on your working directory and how we can help with everything you do there. + +We ship a [desktop GUI client](/overview) and [a CLI](/cli-overview) that work together. + +### Desktop Client + + + +### Command Line Interface + + + +Here you will find documentation on the product and the way that we're running our beta and building our product with your help. + +## Getting Started + +Check out our [Getting Started](/guide) guide to get started with GitButler, or check out our helpful video overview: + +https://www.youtube.com/watch?v=DhJtNNhCNLM + +## Why We're Doing This + +Read about it over [Why GitButler](/why-gitbutler) Section. + +## What We Do + +The GitButler client is a powerful Git client. You can manage your branches, work on multiple things at once, push and fetch from your Git server, easily rebase and modify commits and more. We have a unique approach to merge conflicts that helps split up any conflicting work. It also keeps a timeline so that you can easily undo any operation. + +- [Virtual Branches](/features/branch-management/virtual-branches) +- [First Class Conflicts](/features/branch-management/merging) +- [Project History](/features/timeline) + + # guide.mdx Here is a quick overview of how to get started with GitButler. In this guide, we will: @@ -306,235 +434,143 @@ To do this, click the "Operations History" button in the sidebar, find the actio /> -# butler-flow.mdx +# releases.mdx -Butler Flow is a lightweight, branch-based workflow enabled by GitButler's virtual branch functionality. -For most modern software development teams that want to set up a culture of shipping, who push to production every day, who are constantly testing and deploying, one of the most popular and effective development workflows is [GitHub Flow](https://docs.github.com/en/get-started/quickstart/github-flow). +GitButler is released on a regular basis in two separate tracks. Their version numbers are incremented independently. -These are wise words, enterprising men quote 'em. Don't act surprised, you guys, [cuz I wrote 'em](https://scottchacon.com/2011/08/31/github-flow). +1. **Release** - stable releases +2. **Nightly** - development releases built at minimum 1x per day via GitHub Actions. -However, a decade later, and with a new and more powerful branching toolset, we're able to amend that basic workflow to be simpler, faster, more flexible, and less error prone. Let's take a quick minute to explore what the Butler Flow is for software development teams using GitButler and how this can make all of our lives easier. -## Overview + +You can find the download links and changelogs for the latest releases on our [GitHub Releases](https://github.com/gitbutlerapp/gitbutler/releases). + -In a nutshell, the basic development cycle is very simple. +## Platforms -- All work is based off a "target branch", which is a representation of released production code. -- All work immediately exists in a branch. -- Work can be shared with teammates for review as early as possible. -- All branches that are close to merging can be applied locally for integration testing. -- Branches can be reviewed independently and merged after final review. -- Integrated branches are automatically removed from developer context. +We bundle and ship GitButler for Mac OS, Windows, and Linux. -### The Target Branch +### Windows -In stock, vanilla Git tooling, there is nothing specified as the production branch, no special "trunk". It is only by convention that this is enforced. +| Arch | Format | In-app updater | +| --- | --- | --- | +| `x86_64` | `msi` | | -In GitButler, virtual branches will not work without the specification of a special "target branch". Everything exists in relation to this special branch, everything that differs from it must be accounted for by being owned by some other branch, until those changes are integrated. +### Mac OS -### Virtual Branches +| Arch | Format | In-app updater | +| --- | --- | --- | +| `x86_64` | `dmg` | | +| `arm64` | `dmg` | | -Once you choose a target branch, everything that is in your working directory and not ignored by the Git Ignore specification must be owned by a virtual branch. If you don't have one active, GitButler will automatically create one for you. +### Linux -All subsequent changes to your working directory, either by applying other branches or directly modifying files, must be owned by a virtual branch. +| Arch | Format | In-app updater | +| --- | --- | --- | +| `x86_64` | `deb` | | +| `x86_64` | `rpm` | | +| `x86_64` | `AppImage` | | -Branches are meant to be small, independent and rapidly integrated. However, longer lived branches can be continuously re-integrated, keeping them clean and mergeable for long periods of time if needed, while still being shareable and reviewable with your team. +> Support for the Linux releases are complicated a bit through a core dependency of our framework, `libwebkit2gtk`, which is used to provide the web view on Linux. Tauri v1 required `libwebkit2gtk-4.0` which is not available in Ubuntu 24.04 or Debian 13 and newer. +> +> We've recently upgraded to Tauri v2 (as of Nightly `0.5.845` and Release `0.13.9`), and it now requires `libwebkit2gtk-4.1`. This version of the package is not available in the repositories for Ubuntu 20.04 and older as well as Debian 11 and older. +> +> For more information, check out the [pinned issue](https://github.com/tauri-apps/tauri/issues/9662) in the Tauri repository. -Virtual branches can be started and ended entirely independently of each other. Developers can work on longer branches, while starting, reviewing, finishing, merging, and deleting small ones without ever changing branch context. -### Collaboration +# workspace-branch.mdx -All your team's work, whether created and managed by GitButler or not, exists on the central server as normal Git branches. These will automatically be pulled down and kept up to date by GitButler and can be converted into virtual branches and applied to your working directory in addition to your branches. +If you run some normal Git commands (like `git log`) while in GitButler mode, you'll see a few special branches that GitButler maintains behind the scenes. The one that most people get confused by is the `gitbutler/workspace` commit. -This allows you to integrate work from the rest of your team continuously and early, while still keeping non-dependent changes separated and independently reviewable and mergable. It allows you to review code without needing to entirely switch contexts, and to do so early and often in an executable environment. +There are a few different reasons that we need it, so let's take a quick look. -Merge conflicts are known almost as soon as they occur and can be communicated about and collaborated on far before an upstream merge. +If you run a normal `git log` on a GitButler managed repository, you will see something like this: -### Maintenance +```git +commit de56d20e282f7641d48d288b510141996c3c3cfc (HEAD -> gitbutler/workspace) +Author: GitButler +Date: Wed Sep 9 09:06:03 2020 +0800 -Virtual branches can remain applied locally until they are merged into your upstream target branch. Once integrated by any fashion (squash merge, rebase, merge), the virtual branch is automatically disposed of, keeping cruft and overhead low. + GitButler Workspace Commit + This is is a merge commit of the virtual branches in your workspace. -# cli-overview.mdx + For GitButler to manage multiple parallel branches, we maintain + this commit automatically so other tooling works properly. -In addition to the Desktop graphical client, GitButler ships with a command line interface that allows you to do all of the same powerful things that the GUI does, but from the terminal or via scripts or agents. + If you switch to another branch, GitButler will need to be + reinitialized. -https://www.youtube.com/watch?v=loavN_hHuEs + Here are the branches that are currently applied: -This includes tooling for: + - update-homepage (refs/gitbutler/update-homepage) + branch head: a32f33273948837078e5f5a4e1677ab6274a4629 -- [managing stacked and parallel branches](cli-guides/cli-tutorial/branching-and-commiting) -- [an operations log and restoration of older states](cli-guides/cli-tutorial/operations-log) -- [powerful commit editing](cli-guides/cli-tutorial/rubbing) -- [upstream management](cli-guides/cli-tutorial/updating-the-base) -- conflict resolution -- worktree management + For more information about what we're doing here, check out our docs: + https://docs.gitbutler.com/workspace-branch -All with a simple command line tool. +commit a32f33273948837078e5f5a4e1677ab6274a4629 (update-homepage) +Author: Scott Chacon +Date: Mon Jan 26 07:33:31 2026 +0500 -Feel free to get started with our [Tutorial](cli-guides/installation) or just dig into the available commands. + hero update - new branding +``` -# overview.mdx +That first commit is a merge commit that we rebuild as you modify branches in GitButler. The reason that it exists is mainly because if you have more than one branch applied in your workspace, when other tools run `git status`, it will look strange, since Git has no concept of having several branches applied at once. +## Status, Diff and Log -# Butler Review +To keep Git command output for things that look at the index and HEAD (such as status or diff) somewhat sane, we modify your index to look like the union of all the committed states of all your applied virtual branches. This makes git diff and git status behave more or less like you would expect. - - We have paused work on Butler Review for now to concentrate more on the client experience. Review - will be coming back in our upcoming server functionality, stay tuned. - +For instance, if you have two files on Branch A and two files on Branch B, then git status will simply list four files as modified. -Butler Review is a new approach to code review that focuses on reviewing code -as a series of evolving patches rather than a unified diff of a whole branch. +If you run git log, the first commit should be our custom commit message and the tree of that commit is the union of all the committed work on all your applied virtual branches, as though they were all merged together into one (something stock Git can understand). - +## Committing, Branching, Checking Out -## Why Butler Review? +However, if you try to use something that writes to HEAD, like git commit or git checkout, then you might have some headaches. For this reason, we install custom Git hooks for `pre-commit` and `post-checkout` that will protect this from happening. -Traditional code review tools like GitHub and GitLab show changes as a single -diff of the entire branch. This makes it hard to review changes in a branch -incrementally, especially when the branch is large. It also discourages creating -good commit or commit messages, since they are all sqaushed in review and -messages are difficult to find and unrelated to the code review UI. +If you try to commit when in GitButler managed mode, the `pre-commit` hook should disallow it and tell you how to fix it. -Butler Review approaches the problem differently, by making it easy to review -individual commits as patches and tracking changes to the series when you rebase -or amend the branch. +```git +❯ git commit -am 'commit on the workspace branch' -This makes it easier to review changes incrementally and evolve a series rather -than pushing poor commits to the branch to address feedback. +GITBUTLER_ERROR: Cannot commit directly to gitbutler/workspace branch. -## Enabling Butler Review +GitButler manages commits on this branch. Please use GitButler to commit your changes: + - Use the GitButler app to create commits + - Or run 'but commit' from the command line -To start using Butler Reviews, you need to enable the feature in your GitButler -client in the project settings. Click the gear icon in the bottom left corner of -the GitButler client and go to the Server tab. +If you want to exit GitButler mode and use normal git: + - Run 'but teardown' to switch to a regular branch + - Or directly checkout another branch: git checkout - +If you no longer have the GitButler CLI installed, you can simply remove this hook and checkout another branch: + rm ".git/hooks/pre-commit" +``` -Once you enable Butler Review, you can start creating reviews for your branches. +If you want to get out of this mode, you can follow any of those instructions. The easiest is running `but teardown`, but simply switching directly to a normal Git branch will also do the trick. -## Creating a Review -Once you have enabled Butler Review for a project, you can create a review for -a branch by clicking the new "Create Butler Review" button in the branch header card. +# custom-csp.mdx - -Once you create a review, you will see a new card in the branch header that -shows the status of the review. +By default GitButler uses a strict Content Security Policy (CSP) to protect against various attacks, such as cross-site scripting (XSS) and data injection attacks. This policy restricts the sources from which content can be loaded, as well as the hosts the application can connect to, ensuring that only trusted sources are allowed. - +However, there are some cases where you may need to customize the CSP to allow certain features or integrations. Some examples include: +- Self-hosted GitHub Enterprise instances +- Self-hosted GitLab instances +- Self-hosted Ollama instances -If you have GitHub integration enabled, you will see a dropdown to let you choose -to create a PR or a Butler Review. You can also create both, in either order and -they will be linked together. +In those cases you are likely to observe an error message that looks something like this: - +``` +Refused to connect to https://./api/v4/projects/9/merge_requests because it does not appear in the connect-src directive of the Content Security Policy. +``` -Once a Review has been opened, you will have a URL that you can share with your -team to get feedback on your changes. - -## Reviewing a Series - -When a reviewer opens a review, they will see a list of patches that make up the branch. - - - - - Currently, all reviews are "unlisted", meaning that anyone with the URL can see the review. - In the future, we will implement fully public and fully private reviews, but currently all reviews - are unlisted. - -However, if someone has the URL, they will only be able to see the patch data, -they will not have access to all of your source code, nor will they be able to make changes, -only approve or request changes. - - - -Now the reviewer can hit "Start Review" to start reviewing the patches in the series. -It will start them at the "bottom", the first patch and they can work their way up. -The reviewer can also click on a patch to go directly to it. - -## Reviewing a Patch - -Once a reviewer starts reviewing a patch, they will see the changes in the commit -and a chat window to leave comments. - - - -The reviewer can leave comments on the patch, approve the patch, or request changes. -The chat discussion is real time, so reviewers can discuss the changes with the author -as they review the patch. - -You can also select lines of code to comment on. This will highlight the line in the -diff and show the comment in the chat window. - -Every patch needs to be approved before the branch is considered approved. - -## Requesting Changes - -A reviewer can either approve a patch or request changes. If they request changes, -the author can amend that specific commit and publish the changes to the review. - -Reviewers will be able to see in the chat that a new version of the patch has been -published and they can review the changes again. - - - Currently we don't have interdiffs, so reviewers will need to review the entire patch again. We - plan to add interdiffs in the very near future. - - -## Approving a Patch - -Once a reviewer is happy with a patch, they can approve it. This will mark the patch -as approved and move the reviewer to the next patch in the series. - -## Closing a Review - -Once the branch has been merged, it will automatically mark the review as closed. -The author can also manually close the review if they decide to abandon the series. - - -# custom-csp.mdx - - -By default GitButler uses a strict Content Security Policy (CSP) to protect against various attacks, such as cross-site scripting (XSS) and data injection attacks. This policy restricts the sources from which content can be loaded, as well as the hosts the application can connect to, ensuring that only trusted sources are allowed. - -However, there are some cases where you may need to customize the CSP to allow certain features or integrations. Some examples include: -- Self-hosted GitHub Enterprise instances -- Self-hosted GitLab instances -- Self-hosted Ollama instances - -In those cases you are likely to observe an error message that looks something like this: - -``` -Refused to connect to https://./api/v4/projects/9/merge_requests because it does not appear in the connect-src directive of the Content Security Policy. -``` - -You can resolve this issue by adding your host to the CSP. +You can resolve this issue by adding your host to the CSP. ## Adding a Custom CSP @@ -580,35 +616,6 @@ Note that if `extraCsp` is the only entry in the JSON file, you may want to encl The changes will take effect the next time you start GitButler. -# why-gitbutler.mdx - -The GitButler manifesto, as it were. - -Everyone loves a good manifesto. So, why is there a need for a new Git client in the world? Don't we have enough? Isn't the command line just fine? - -Having cofounded GitHub, trained dozens of corporate teams on distributed version control tools and literally written the book on Git, we have spent a lot of time and energy over the last decade thinking about the source code management tools that software developers use every day. - -GitHub has changed the way that millions of developers across the world collaborate and work with their source code, but as sophisticated and user friendly as that tool is in our daily coding lives, using GitHub or GitLab or Bitbucket still requires all of those developers to work with a command line tool that is confusing, difficult to use, error prone and not originally designed or built for the workflows and processes that most developers today use. That tool is Git. - -Sure, some small minority will use a GUI of some sort, but even those tools are mostly wrappers around the core concepts of Git itself, never reimagining what source code management could be or if Git itself is actually good at helping them with the tasks they face on a daily basis. I've never personally used one because they do little that Git itself doesn't and honestly it's generally easier to just do those tasks on the command line, where it's quick and efficient and I don't have to take my hands off the keyboard. - -But what if we broke down everything that you try to accomplish with Git, with source code management tools in general, reduce them down to first principles and imagine a tool that does all of those things better? Are you using Git because it's the best way you can imagine accomplishing those tasks, or are you using it because it's what is there, it's what works with GitHub, it's the only real option? - -The reality is that source code management tools have changed very little on a fundamental level in the last 40 years. If you look at the tools and commands and interface that RCS had in the 80s, or Subversion had in the 90s, is it really massively different than how you use Git today on a daily basis? - -Yes, Git has easy branching, acceptable merging, a nice network transport method to move your code around, but you're still making manual checkins, you're still trying to remember obscure arguments, you're still losing work when things get complicated. - -GitButler is rethinking everything between when you write code in your editor of choice and when you push that code to GitHub for review. Why are you making 'wip' commits when your SCM should be recording everything for you? Why are everyone's commit messages close to useless? Why is `git blame` the best way to get context on the code your team has written? Why can't you seamlessly transition work between computers? - -We are creating not only a new kind of Git client, but an entirely new way of thinking about managing the code that you work on. A tool that helps you at every step of the software development process. A code concierge, hard at work for you to ensure that you'll never lose a moment of work again. That you'll have all the context and support you'll need around every line of code you work on. - -Managing your source code can be different, smarter, leaps ahead of the 40 year old concepts that we're using today. - -Our goal is to make sure that nobody ever has to read Scott's book again. That you don't have to learn how to manage your source code management tool. - - - - # fetch-push.mdx @@ -700,332 +707,347 @@ If none of the available options helps, feel free to hop on our [Discord](https: -# releases.mdx - +# fixing-conflicts-outside-gitbutler.mdx -GitButler is released on a regular basis in two separate tracks. Their version numbers are incremented independently. +If you have ended up with conflicted commits and GitButler is completely +unresponsive, they can be recovered using plain git commands in the following +manner: -1. **Release** - stable releases -2. **Nightly** - development releases built at minimum 1x per day via GitHub Actions. +## Consider hopping on a call with one of us. +The resolution steps make use of some advanced git functions, if you are not +comfortable with any of the steps - we are more than happy to walk you through +any recovery processes. - -You can find the download links and changelogs for the latest releases on our [GitHub Releases](https://github.com/gitbutlerapp/gitbutler/releases). - +Join our Discord and let us know about your situation. One of us will help you +work through your problem either through text or via a call. -## Platforms +## Backup! -We bundle and ship GitButler for Mac OS, Windows, and Linux. +First, make a copy of your entire repo. We don't want to lose any data if we +make a mistake in recovery. -### Windows +## Make a new branch -| Arch | Format | In-app updater | -| --- | --- | --- | -| `x86_64` | `msi` | | +Conflicts often come up as part of a cherry pick so we want to re-perform +the rebase manually - resolving any conflicted commits as we go. -### Mac OS +I want the commits to sit on top of origin/master, so I'll run the following +commands to make a new and empty branch to re-build the branch on top of: -| Arch | Format | In-app updater | -| --- | --- | --- | -| `x86_64` | `dmg` | | -| `arm64` | `dmg` | | +``` +git switch -c reconstruction +git reset --hard origin/master +``` -### Linux +## Looking at the commits -| Arch | Format | In-app updater | -| --- | --- | --- | -| `x86_64` | `deb` | | -| `x86_64` | `rpm` | | -| `x86_64` | `AppImage` | | +We can now get an idea of what operations we need to perform to reconstruct the +branch. -> Support for the Linux releases are complicated a bit through a core dependency of our framework, `libwebkit2gtk`, which is used to provide the web view on Linux. Tauri v1 required `libwebkit2gtk-4.0` which is not available in Ubuntu 24.04 or Debian 13 and newer. -> -> We've recently upgraded to Tauri v2 (as of Nightly `0.5.845` and Release `0.13.9`), and it now requires `libwebkit2gtk-4.1`. This version of the package is not available in the repositories for Ubuntu 20.04 and older as well as Debian 11 and older. -> -> For more information, check out the [pinned issue](https://github.com/tauri-apps/tauri/issues/9662) in the Tauri repository. +By running: +``` +git log --graph --oneline +``` -# recovering-stuff.mdx +We can see all the commits that are in our branch. +For my branch, it looks as follows: -How to dig around our internal data to find (nearly) anything +``` +> git log --oneline --graph reimplement-insert-blank-commit +* b1b1bf07d (reimplement-insert-blank-commit) Improvements to rebase engine for better composability +* c8f5b92a0 Rename reword_commit to commit_reword +* e1fc3b9f5 Reimplement insert blank commit +``` -GitButler saves data in a few different ways. As we're still in beta, sometimes things might break and it may look like you've lost work, but you almost certainly haven't. We're pretty good about saving stuff a lot. Here's how to recover almost anything you had in your working directory or virtual branches. +We want to work from the bottom of this list to the top. -## GitButler References +To get a better idea of the state of a given commit, we can run: -If everything crashes or the UI isn't working at all, you may be surprised to know that even though your virtual branches don't show up in a normal git branch output, we do actually constantly write them out as Git references (just not in refs/heads). +``` +git cat-file -p +``` +We can identify if the commit is conflicted by the presence of a header that +looks as follows: -```bash title="Terminal" -❯ git for-each-ref | grep gitbutler -e63b3bac82835dc17083a785d25db8b4b46744b9 commit refs/gitbutler/add-can-create-method-to-notebook -98ef3cd6eea14ee4159a600e448271c0d777efe2 commit refs/gitbutler/add-conditional-blocks-for-image-and-video -c7e27b9f99f25160a4d5f07d5972c217bdd44319 commit refs/gitbutler/add-database-schema-conversion-script -4afdfed6c14b57491a9d295c31613fd79b92f63a commit refs/gitbutler/add-gems-for-test-group +``` +gitbutler-conflicted ``` +## Reconstructing the branch -These references are just like git branches - they point to a commit that has the latest version of your branch. You can create other git branches off of them, you can push them to GitHub, etc. +Depending on the state of a commit and it's parent, there are some different +operations we want to perform to re-perform the rebase. -You will have one for each virtual branch (applied or unapplied) that you've created (that you haven't deleted). +### If a commit is conflicted -If you've committed everything on a virtual branch, the reference will just point to the latest commit. If you have work in progress on the branch, it will point to a WIP commit that includes those changes. +If a commit is conflicted, we want to first look at the tree of the conflicted +commit. We can do that with the following command: -So for example, if I have the following two virtual branches, one fully committed and one with work pending: +``` +git cat-file -p ^{tree} +``` - +For the first commit in my list, that looks like: +``` +> git cat-file -p e1fc3b9f5^{tree} +040000 tree 24e291fb0867efec629b933c00aaeaff39365efd .auto-resolution +040000 tree ffde17e2a4d4c045869b300b4ec9027851581e33 .conflict-base-0 +100644 blob dca5869dd76a1eeadeba9387ec7f94b318085c7e .conflict-files +040000 tree 3b23a61344b84fa3f7b93b1ca058d24846a31f57 .conflict-side-0 +040000 tree b5a91de1f2ce0a248472d03c1701a20289e4d657 .conflict-side-1 +100644 blob 2af04b7f1384300b742f6112005cddc5a87be022 README.txt +``` -I can view the git branches like this: +Here we see the conflicted representation of a commit in GitButler. -```bash title="Terminal" -❯ git show gitbutler/Convert-tables-to-utf8mb4 -commit 841e4db701ca41206c03f1f4fe345f7e27d05eab -Author: Scott Chacon -Date: Fri Feb 23 10:30:17 2024 +0100 +There are four entries that are relevant here: - my latest commit +- `.auto-resolution` - This contains a resolution attempt that GitButler made + when cherry-picking the commit. +- `.conflict-base-0` - This contains the tree of the commit that was + cherry-picked to produce the conflicted commit. +- `.conflict-side-0` - This contains the tree of the commit that we tried to + cherry-pick onto. +- `.conflict-side-1` - This contains the tree of the origional commit before it + was cherry-picked. -❯ git show gitbutler/Add-database-schema-conversion-script -commit d95e7f4da1611ea6bb8a80da06e66ca923fbff55 -Author: GitButler -Date: Fri Feb 23 10:30:18 2024 +0100 +To re-perform the cherry-pick that GitButler was trying to do. We do that by +first making a commit that holds the `.conflict-base-0` tree which can be done +by running: - GitButler WIP Commit +``` +git commit-tree -p HEAD -m "base" +``` - This is a WIP commit for the virtual branch 'Add database schema conversion script' +For me, that looks like: - This commit is used to store the state of the virtual branch - while you are working on it. It is not meant to be used for - anything else. +``` +> git commit-tree ffde17e2a4d4c045869b300b4ec9027851581e33 -p HEAD -m "base" +0100ea63fe63a2894567de42371f8d6cf79e4a85 ``` -See how the `Add-database-schema-conversion-script` reference points to a "WIP commit"? The tree of that commit has all those changed files in it as though we had committed them. +This has given us an OID in return. This is the object ID of the commit we just created. -If you don't want to search through all your refs with `for-each-refs`, you can also just run a normal `git log` command and we'll show you what references we've written and which modified files are in each one: +We then want to create a commit that contains the `.conflict-side-1` tree, and +has that new "base" commit as it's parent. We can do that by running: -```bash title="Terminal" -❯ git log -commit 2d8afe0ea811b5f24b9a6f84f6d024bb323a2db5 (HEAD -> gitbutler/workspace) -Author: GitButler -Date: Fri Feb 23 10:30:18 2024 +0100 +``` +git commit-tree -p -m "Desired commit message" +``` - GitButler Integration Commit +For me this looks like: - This is an integration commit for the virtual branches that GitButler is tracking. +``` +git commit-tree b5a91de1f2ce0a248472d03c1701a20289e4d657 -p 0100ea63fe63a2894567de42371f8d6cf79e4a85 -m "Reimplement insert blank commit" +35d518d2ea68635631593faff34b11e3b1904014 +``` - Due to GitButler managing multiple virtual branches, you cannot switch back and - forth between git branches and virtual branches easily. +Using that returned commit OID, we can then bring that commit on top of our +branch with: - If you switch to another branch, GitButler will need to be reinitialized. - If you commit on this branch, GitButler will throw it away. +``` +git cherry-pick +``` - Here are the branches that are currently applied: - - Add database schema conversion script (refs/gitbutler/Add-database-schema-conversion-script) - - butler/Gemfile - - butler/README.md - - butler/db/schema.rb - - butler/db/migrate/20240209144600_change_mysql_charset.rb - - .pscale.yml - - Convert tables to utf8mb4 (refs/gitbutler/Convert-tables-to-utf8mb4) - branch head: 841e4db701ca41206c03f1f4fe345f7e27d05eab - - butler/create_column_conversions.rb +For me, that looked like: - Your previous branch was: refs/heads/sc-branch-comments +``` +git cherry-pick 35d518d2ea68635631593faff34b11e3b1904014 +``` - The sha for that commit was: 5e16e99667db9d26f78110df807853a896120ff3 +Git may prompt you to solve some conflicts here which you can resolve in the +standard manner. - For more information about what we're doing here, check out our docs: - https://docs.gitbutler.com/features/branch-management/integration-branch -``` +### If a commit is **not conflicted**, but has a **conflicted parent**. -You can see the two `gitbutler` refs under the "Here are the branches that are currently applied" section. +If the commit is not conflicted, but the commit before it in your log WAS +conflicted, then we similarly need to create a commit to cherry-pick on our own. -Again, these are real git refs, just not under `refs/heads` so that we don't pollute your `git branch` output. But if GitButler crashes at some point, you can still push them to GitHub or whatever you want. Here is an example pushing my virtual branch to a GitHub branch called `convert-tables`: +First, you will want to take a look at that parent's commit tree with: -```bash title="Terminal" -❯ git push origin refs/gitbutler/Convert-tables-to-utf8mb4:refs/heads/convert-tables -Enumerating objects: 6, done. -Counting objects: 100% (6/6), done. -Delta compression using up to 10 threads -Compressing objects: 100% (4/4), done. -Writing objects: 100% (4/4), 474 bytes | 474.00 KiB/s, done. -Total 4 (delta 2), reused 1 (delta 0), pack-reused 0 -remote: Resolving deltas: 100% (2/2), completed with 2 local objects. -remote: -remote: Create a pull request for 'convert-tables' on GitHub by visiting: -remote: https://github.com/gitbutlerapp/web/pull/new/convert-tables -remote: -To github.com:gitbutlerapp/web.git - * [new branch] refs/gitbutler/Convert-tables-to-utf8mb4 -> convert-tables +``` +git cat-file -p ``` -## GitButler Operations Log +We want to make a base commit that uses the `.auto-resolution` tree. We can do +that with: -Ok, let's say that your work was not in one of those refs for some reason. Maybe you hit some weird bug and it completely changed everything in a way where now you're sitting on the couch in the dark with a glass of whisky, slowly mumbling the word "GitButler..." and plotting your revenge. +``` +git commit-tree -p HEAD -m "Desired commit message" +``` -Most of the time, we'll have whatever you're looking for in our operations log. +We then want to make a commit that has the tree of the non-conflicted commit, +with the parent as the base commit we just made. -The easiest way to access this is to use the built in Project History UI: [Project History](/features/timeline) +We can first find the tree of the non-conflicted commit by running: -However, let's dig into how this works, just in case you want to check it out yourself. +``` +git cat-file -p +``` -Every time that GitButler does some possibly data-changing operation, we store a snapshot of your project state in our operations log before the operation happens so you can undo it if you want to. This is stored as a Git commit history that is parallel to your projects (ie, no common parents). +and copying the entry after `tree`. -You can inspect this by looking at the `.git/gitbutler/operations-log.toml` file. +We then want to make our commit to cherry pick with: -```bash title="Terminal" -❯ tree .git/gitbutler -.git/gitbutler -├── operations-log.toml -└── virtual_branches.toml +``` +git commit-tree -p -m "desired commit message" +``` -1 directory, 2 files +We can then cherry-pick that commit with `git cherry-pick` onto our branch, +following the standard conflict flow if applicable. -❯ cat .git/gitbutler/operations-log.toml -head_sha = "16e47cb1d091ca9dd44327fef2f5305b09403a95" +### If the commit is **not conflicted** and its parent is **not conflicted** -[modified_at] -secs_since_epoch = 1717663406 -nanos_since_epoch = 560458000 -``` +If this is the case, we can run the standard `git cherry-pick` command to bring +that commit into our reconstruction branch, following the standard conflict flow +if applicable. -If we look at this commit, we can see the history of all of the project history snapshots that GitButler is keeping: +## Pushing your reconstructed branch -```bash title="Terminal" -❯ git log 16e47cb1d091ca9dd44327fef2f5305b09403a9 -2 -commit 16e47cb1d091ca9dd44327fef2f5305b09403a95 -Author: GitButler -Date: Thu Jun 6 10:43:26 2024 +0200 +Once you have finished bringing all of your commits into your reconstruction +branch, you can then push it to your remote via `git push`. - CreateBranch - Version: 1 - Operation: CreateBranch - name: Virtual branch +# why-gitbutler.mdx -commit 2c95aa06d76b3230f1a51d9f89a211770d93ae51 -Author: GitButler -Date: Thu Jun 6 10:28:30 2024 +0200 +The GitButler manifesto, as it were. - UpdateWorkspaceBase +Everyone loves a good manifesto. So, why is there a need for a new Git client in the world? Don't we have enough? Isn't the command line just fine? - Version: 1 - Operation: UpdateWorkspaceBase -``` +Having cofounded GitHub, trained dozens of corporate teams on distributed version control tools and literally written the book on Git, we have spent a lot of time and energy over the last decade thinking about the source code management tools that software developers use every day. -You can see that before creating a branch or updating our workspace with upstream work, we're recording the state of our project so we have an undo point. So what data are we keeping here in addition to this trailer information? +GitHub has changed the way that millions of developers across the world collaborate and work with their source code, but as sophisticated and user friendly as that tool is in our daily coding lives, using GitHub or GitLab or Bitbucket still requires all of those developers to work with a command line tool that is confusing, difficult to use, error prone and not originally designed or built for the workflows and processes that most developers today use. That tool is Git. -Let's look at the tree of one of these commits: +Sure, some small minority will use a GUI of some sort, but even those tools are mostly wrappers around the core concepts of Git itself, never reimagining what source code management could be or if Git itself is actually good at helping them with the tasks they face on a daily basis. I've never personally used one because they do little that Git itself doesn't and honestly it's generally easier to just do those tasks on the command line, where it's quick and efficient and I don't have to take my hands off the keyboard. -```bash title="Terminal" -❯ git cat-file -p 16e47cb1d091ca9dd44327fef2f5305b09403a95^{tree} -040000 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 conflicts -040000 tree b283f22f5abf4ed9c612c1b5b3b9a98ec25474b0 index -040000 tree b283f22f5abf4ed9c612c1b5b3b9a98ec25474b0 target_tree -100644 blob d839dca7e14f5833ad737b4adbf337bd20489927 virtual_branches.toml -040000 tree a0821552c0e7d5defe369d577af5e3a87b442469 virtual_branches -``` +But what if we broke down everything that you try to accomplish with Git, with source code management tools in general, reduce them down to first principles and imagine a tool that does all of those things better? Are you using Git because it's the best way you can imagine accomplishing those tasks, or are you using it because it's what is there, it's what works with GitHub, it's the only real option? -The virtual branches toml file has the interesting metadata: +The reality is that source code management tools have changed very little on a fundamental level in the last 40 years. If you look at the tools and commands and interface that RCS had in the 80s, or Subversion had in the 90s, is it really massively different than how you use Git today on a daily basis? -```bash title="Terminal" -❯ git cat-file -p 16e47cb1d091ca9dd44^{tree}:virtual_branches.toml -[default_target] -branchName = "master" -remoteName = "origin" -remoteUrl = "git@github.com:gitbutlerapp/gitbutler.git" -sha = "e00e54af7f903ef84600079a45490a7f07e4702e" -pushRemoteName = "origin" +Yes, Git has easy branching, acceptable merging, a nice network transport method to move your code around, but you're still making manual checkins, you're still trying to remember obscure arguments, you're still losing work when things get complicated. -[branch_targets] +GitButler is rethinking everything between when you write code in your editor of choice and when you push that code to GitHub for review. Why are you making 'wip' commits when your SCM should be recording everything for you? Why are everyone's commit messages close to useless? Why is `git blame` the best way to get context on the code your team has written? Why can't you seamlessly transition work between computers? -[branches.09ef54c4-1081-4a52-8182-a5ec725016b6] -id = "09ef54c4-1081-4a52-8182-a5ec725016b6" -name = "commit signing settings" -notes = "" -applied = false -upstream = "refs/remotes/origin/commit-signing-settings" -upstream_head = "b60a66452dfecef74103346af6a3291ad677d246" -created_timestamp_ms = "1717489406268" -updated_timestamp_ms = "1717489406268" -tree = "b28e7eefdd7b6f36456516b696146a2ea7638ca4" -head = "b60a66452dfecef74103346af6a3291ad677d246" -ownership = "" -order = 4 -selected_for_changes = 1717489406268 -``` +We are creating not only a new kind of Git client, but an entirely new way of thinking about managing the code that you work on. A tool that helps you at every step of the software development process. A code concierge, hard at work for you to ensure that you'll never lose a moment of work again. That you'll have all the context and support you'll need around every line of code you work on. -The virtual_branches tree has the actual contents of those computed branches in case we need to recreate them: +Managing your source code can be different, smarter, leaps ahead of the 40 year old concepts that we're using today. - - - - - - - - - - - - - - - - - - - - - +Our goal is to make sure that nobody ever has to read Scott's book again. That you don't have to learn how to manage your source code management tool. -This allows you to get contents of any file in any of your virtual branch states as well. + -# coding-agents.mdx +# gerrit-mode.mdx +Not _everyone_ uses GitHub or GitLab to review code and collaborate. If you use the [Gerrit](https://www.gerritcodereview.com/) code review tool, GitButler has a mode for you! In fact, GitButler is the best Gerrit client there is. -GitButler can also orchestrate your coding agents and help automatically manage, checkpoint and commit the work that they do for you. +## What is Gerrit - - Currently the only supported agent is [Claude Code](https://www.anthropic.com/claude-code), but we - are working on more agent support. - +If you've never heard of Gerrit, it's used by large teams like the Android or Chrome projects to manage huge numbers of changes and users across large numbers of interdependent repositories. -You can start a session by clicking the AI stars on a branch header, or use the shortcut for a new branch and AI session in the top right corner of the window. +Here is an example of incoming changesets on the [Android project](https://android-review.googlesource.com/q/status:open+-is:wip,50): -Each session is tied to a branch such that you can run them in parallel, and changes are assigned to the respective branch as long as they are mutually exclusive. Information about the branch is also added to the respective context, giving the agent knowledge of the work they contain. +## How is Gerrit different than Pull/Merge Requests? -# Setup +Good question. With GitHub or GitLab, when you send a pull/merge request, the review process is branch based. If you add more commits on top of your branch, the changes are squashed into one big unified diff for review. Most teams tend to avoid rebasing anything that was already shared. -There is some basic setup that may need to be done the first time, if you've never used Claude Code before. +Gerrit is a commit based review system. Every review is based on exactly one commit. It's very common to edit shared commits and submit new versions of them to address feedback. -### Installing Claude Code +This model works _very well_ with GitButler's easy [commit editing](/features/branch-management/commits) features. With any other Git client, interactive rebasing and amending tends to be quite painful and error prone, making it fairly difficult to work with Gerrit's model. With GitButler, it's ideal. Just drag and drop changes and update your changesets easily. -Before you can use the agents integration, you need to have Claude Code installed as we wrap the SDK that it provides. If we can't find the binary, you should see something like this: +## How to turn on Gerrit Mode + +If you have a Gerrit remote, GitButler will automatically enable Gerrit Mode when the project is being added to GitButler. You can also enable it manually. +To manually turn on Gerrit Mode in GitButler, you just have to set a Git config option called `gitbutler.gerritMode` in the project you want to act in a Gerrit compatible fashion: + +``` +❯ cd my_project +❯ git config gitbutler.gerritMode 1 +``` + +## What is Gerrit Mode + +Now GitButler will change its behavior in the following ways: + +- When you commit, we will automatically inject a `Change-Id` trailer into the commit in the format that Gerrit expects. You do not need to [setup a `commit-msg` hook](https://gerrit-review.googlesource.com/Documentation/cmd-hook-commit-msg.html) like you do with other Git clients. +- When you push, it will not push to a matching branch name on the remote. Instead it will push to `refs/for/main` (or whatever the name of the target branch is set to be). +- After a push, we record the change url and show you the link and number for each commit automatically. -To install Claude Code, you can read through the docs [here](https://docs.anthropic.com/en/docs/claude-code/quickstart), but the simple version is to use their "Native Install" method: +We can also set some extra push options when we push, including: + +- [Topics](https://gerrit-review.googlesource.com/Documentation/cross-repository-changes.html) +- [Hashtags](https://gerrit-review.googlesource.com/Documentation/intro-user.html#hashtags) +- [WIP status](https://gerrit-review.googlesource.com/Documentation/intro-user.html#wip) + + + + +# coding-agents.mdx + + +GitButler can also orchestrate your coding agents and help automatically manage, checkpoint and commit the work that they do for you. + + + Currently the only supported agent is [Claude Code](https://www.anthropic.com/claude-code), but we + are working on more agent support. + + +You can start a session by clicking the AI stars on a branch header, or use the shortcut for a new branch and AI session in the top right corner of the window. + + + +Each session is tied to a branch such that you can run them in parallel, and changes are assigned to the respective branch as long as they are mutually exclusive. Information about the branch is also added to the respective context, giving the agent knowledge of the work they contain. + +# Setup + +There is some basic setup that may need to be done the first time, if you've never used Claude Code before. + +### Installing Claude Code + +Before you can use the agents integration, you need to have Claude Code installed as we wrap the SDK that it provides. If we can't find the binary, you should see something like this: + + + +To install Claude Code, you can read through the docs [here](https://docs.anthropic.com/en/docs/claude-code/quickstart), but the simple version is to use their "Native Install" method: {/* */} @@ -1056,11 +1078,19 @@ You will then need to setup and login to Claude Code, which will require an Anth Code usage, we simply help manage the agent. -# Generating Code in the Agents Tab +# Generating Code with an Agent Once your agent is setup, you can select a branch to work on, type in a prompt and we will run that though Claude Code, same as if you ran it in the terminal. -We will do some cool things as it runs, however. + + +While this is still running Claude Code, running through the GitButler UI will make sure that each agent works on the branch it is attached to. You can run multiple agents at once, each committing to their attached branches. ### Model Selection @@ -1068,7 +1098,7 @@ There are a few different models that you can choose from for running your task. ### Prompt Templates @@ -1092,7 +1122,7 @@ If you find that you're using similar prompts over and over, you can easily setu -``` -git log --graph --oneline -``` -We can see all the commits that are in our branch. +I can view the git branches like this: -For my branch, it looks as follows: +```bash title="Terminal" +❯ git show gitbutler/Convert-tables-to-utf8mb4 +commit 841e4db701ca41206c03f1f4fe345f7e27d05eab +Author: Scott Chacon +Date: Fri Feb 23 10:30:17 2024 +0100 -``` -> git log --oneline --graph reimplement-insert-blank-commit -* b1b1bf07d (reimplement-insert-blank-commit) Improvements to rebase engine for better composability -* c8f5b92a0 Rename reword_commit to commit_reword -* e1fc3b9f5 Reimplement insert blank commit -``` + my latest commit -We want to work from the bottom of this list to the top. +❯ git show gitbutler/Add-database-schema-conversion-script +commit d95e7f4da1611ea6bb8a80da06e66ca923fbff55 +Author: GitButler +Date: Fri Feb 23 10:30:18 2024 +0100 -To get a better idea of the state of a given commit, we can run: + GitButler WIP Commit -``` -git cat-file -p + This is a WIP commit for the virtual branch 'Add database schema conversion script' + + This commit is used to store the state of the virtual branch + while you are working on it. It is not meant to be used for + anything else. ``` -We can identify if the commit is conflicted by the presence of a header that -looks as follows: +See how the `Add-database-schema-conversion-script` reference points to a "WIP commit"? The tree of that commit has all those changed files in it as though we had committed them. -``` -gitbutler-conflicted -``` +If you don't want to search through all your refs with `for-each-refs`, you can also just run a normal `git log` command and we'll show you what references we've written and which modified files are in each one: -## Reconstructing the branch +```bash title="Terminal" +❯ git log +commit 2d8afe0ea811b5f24b9a6f84f6d024bb323a2db5 (HEAD -> gitbutler/workspace) +Author: GitButler +Date: Fri Feb 23 10:30:18 2024 +0100 -Depending on the state of a commit and it's parent, there are some different -operations we want to perform to re-perform the rebase. + GitButler Integration Commit -### If a commit is conflicted + This is an integration commit for the virtual branches that GitButler is tracking. -If a commit is conflicted, we want to first look at the tree of the conflicted -commit. We can do that with the following command: + Due to GitButler managing multiple virtual branches, you cannot switch back and + forth between git branches and virtual branches easily. -``` -git cat-file -p ^{tree} -``` + If you switch to another branch, GitButler will need to be reinitialized. + If you commit on this branch, GitButler will throw it away. -For the first commit in my list, that looks like: + Here are the branches that are currently applied: + - Add database schema conversion script (refs/gitbutler/Add-database-schema-conversion-script) + - butler/Gemfile + - butler/README.md + - butler/db/schema.rb + - butler/db/migrate/20240209144600_change_mysql_charset.rb + - .pscale.yml + - Convert tables to utf8mb4 (refs/gitbutler/Convert-tables-to-utf8mb4) + branch head: 841e4db701ca41206c03f1f4fe345f7e27d05eab + - butler/create_column_conversions.rb -``` -> git cat-file -p e1fc3b9f5^{tree} -040000 tree 24e291fb0867efec629b933c00aaeaff39365efd .auto-resolution -040000 tree ffde17e2a4d4c045869b300b4ec9027851581e33 .conflict-base-0 -100644 blob dca5869dd76a1eeadeba9387ec7f94b318085c7e .conflict-files -040000 tree 3b23a61344b84fa3f7b93b1ca058d24846a31f57 .conflict-side-0 -040000 tree b5a91de1f2ce0a248472d03c1701a20289e4d657 .conflict-side-1 -100644 blob 2af04b7f1384300b742f6112005cddc5a87be022 README.txt -``` + Your previous branch was: refs/heads/sc-branch-comments -Here we see the conflicted representation of a commit in GitButler. + The sha for that commit was: 5e16e99667db9d26f78110df807853a896120ff3 -There are four entries that are relevant here: + For more information about what we're doing here, check out our docs: + https://docs.gitbutler.com/features/branch-management/integration-branch +``` -- `.auto-resolution` - This contains a resolution attempt that GitButler made - when cherry-picking the commit. -- `.conflict-base-0` - This contains the tree of the commit that was - cherry-picked to produce the conflicted commit. -- `.conflict-side-0` - This contains the tree of the commit that we tried to - cherry-pick onto. -- `.conflict-side-1` - This contains the tree of the origional commit before it - was cherry-picked. +You can see the two `gitbutler` refs under the "Here are the branches that are currently applied" section. -To re-perform the cherry-pick that GitButler was trying to do. We do that by -first making a commit that holds the `.conflict-base-0` tree which can be done -by running: +Again, these are real git refs, just not under `refs/heads` so that we don't pollute your `git branch` output. But if GitButler crashes at some point, you can still push them to GitHub or whatever you want. Here is an example pushing my virtual branch to a GitHub branch called `convert-tables`: -``` -git commit-tree -p HEAD -m "base" +```bash title="Terminal" +❯ git push origin refs/gitbutler/Convert-tables-to-utf8mb4:refs/heads/convert-tables +Enumerating objects: 6, done. +Counting objects: 100% (6/6), done. +Delta compression using up to 10 threads +Compressing objects: 100% (4/4), done. +Writing objects: 100% (4/4), 474 bytes | 474.00 KiB/s, done. +Total 4 (delta 2), reused 1 (delta 0), pack-reused 0 +remote: Resolving deltas: 100% (2/2), completed with 2 local objects. +remote: +remote: Create a pull request for 'convert-tables' on GitHub by visiting: +remote: https://github.com/gitbutlerapp/web/pull/new/convert-tables +remote: +To github.com:gitbutlerapp/web.git + * [new branch] refs/gitbutler/Convert-tables-to-utf8mb4 -> convert-tables ``` -For me, that looks like: +## GitButler Operations Log -``` -> git commit-tree ffde17e2a4d4c045869b300b4ec9027851581e33 -p HEAD -m "base" -0100ea63fe63a2894567de42371f8d6cf79e4a85 -``` +Ok, let's say that your work was not in one of those refs for some reason. Maybe you hit some weird bug and it completely changed everything in a way where now you're sitting on the couch in the dark with a glass of whisky, slowly mumbling the word "GitButler..." and plotting your revenge. -This has given us an OID in return. This is the object ID of the commit we just created. +Most of the time, we'll have whatever you're looking for in our operations log. -We then want to create a commit that contains the `.conflict-side-1` tree, and -has that new "base" commit as it's parent. We can do that by running: +The easiest way to access this is to use the built in Project History UI: [Project History](/features/timeline) -``` -git commit-tree -p -m "Desired commit message" -``` +However, let's dig into how this works, just in case you want to check it out yourself. -For me this looks like: +Every time that GitButler does some possibly data-changing operation, we store a snapshot of your project state in our operations log before the operation happens so you can undo it if you want to. This is stored as a Git commit history that is parallel to your projects (ie, no common parents). -``` -git commit-tree b5a91de1f2ce0a248472d03c1701a20289e4d657 -p 0100ea63fe63a2894567de42371f8d6cf79e4a85 -m "Reimplement insert blank commit" -35d518d2ea68635631593faff34b11e3b1904014 -``` +You can inspect this by looking at the `.git/gitbutler/operations-log.toml` file. -Using that returned commit OID, we can then bring that commit on top of our -branch with: +```bash title="Terminal" +❯ tree .git/gitbutler +.git/gitbutler +├── operations-log.toml +└── virtual_branches.toml -``` -git cherry-pick -``` +1 directory, 2 files -For me, that looked like: +❯ cat .git/gitbutler/operations-log.toml +head_sha = "16e47cb1d091ca9dd44327fef2f5305b09403a95" +[modified_at] +secs_since_epoch = 1717663406 +nanos_since_epoch = 560458000 ``` -git cherry-pick 35d518d2ea68635631593faff34b11e3b1904014 -``` - -Git may prompt you to solve some conflicts here which you can resolve in the -standard manner. -### If a commit is **not conflicted**, but has a **conflicted parent**. - -If the commit is not conflicted, but the commit before it in your log WAS -conflicted, then we similarly need to create a commit to cherry-pick on our own. - -First, you will want to take a look at that parent's commit tree with: +If we look at this commit, we can see the history of all of the project history snapshots that GitButler is keeping: -``` -git cat-file -p -``` +```bash title="Terminal" +❯ git log 16e47cb1d091ca9dd44327fef2f5305b09403a9 -2 +commit 16e47cb1d091ca9dd44327fef2f5305b09403a95 +Author: GitButler +Date: Thu Jun 6 10:43:26 2024 +0200 -We want to make a base commit that uses the `.auto-resolution` tree. We can do -that with: + CreateBranch -``` -git commit-tree -p HEAD -m "Desired commit message" -``` + Version: 1 + Operation: CreateBranch + name: Virtual branch -We then want to make a commit that has the tree of the non-conflicted commit, -with the parent as the base commit we just made. +commit 2c95aa06d76b3230f1a51d9f89a211770d93ae51 +Author: GitButler +Date: Thu Jun 6 10:28:30 2024 +0200 -We can first find the tree of the non-conflicted commit by running: + UpdateWorkspaceBase -``` -git cat-file -p + Version: 1 + Operation: UpdateWorkspaceBase ``` -and copying the entry after `tree`. +You can see that before creating a branch or updating our workspace with upstream work, we're recording the state of our project so we have an undo point. So what data are we keeping here in addition to this trailer information? -We then want to make our commit to cherry pick with: +Let's look at the tree of one of these commits: +```bash title="Terminal" +❯ git cat-file -p 16e47cb1d091ca9dd44327fef2f5305b09403a95^{tree} +040000 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 conflicts +040000 tree b283f22f5abf4ed9c612c1b5b3b9a98ec25474b0 index +040000 tree b283f22f5abf4ed9c612c1b5b3b9a98ec25474b0 target_tree +100644 blob d839dca7e14f5833ad737b4adbf337bd20489927 virtual_branches.toml +040000 tree a0821552c0e7d5defe369d577af5e3a87b442469 virtual_branches ``` -git commit-tree -p -m "desired commit message" -``` - -We can then cherry-pick that commit with `git cherry-pick` onto our branch, -following the standard conflict flow if applicable. - -### If the commit is **not conflicted** and its parent is **not conflicted** - -If this is the case, we can run the standard `git cherry-pick` command to bring -that commit into our reconstruction branch, following the standard conflict flow -if applicable. - -## Pushing your reconstructed branch - -Once you have finished bringing all of your commits into your reconstruction -branch, you can then push it to your remote via `git push`. - - -# gerrit-mode.mdx - -Not _everyone_ uses GitHub or GitLab to review code and collaborate. If you use the [Gerrit](https://www.gerritcodereview.com/) code review tool, GitButler has a mode for you! In fact, GitButler is the best Gerrit client there is. - -## What is Gerrit - -If you've never heard of Gerrit, it's used by large teams like the Android or Chrome projects to manage huge numbers of changes and users across large numbers of interdependent repositories. - -Here is an example of incoming changesets on the [Android project](https://android-review.googlesource.com/q/status:open+-is:wip,50): - - - -## How is Gerrit different than Pull/Merge Requests? - -Good question. With GitHub or GitLab, when you send a pull/merge request, the review process is branch based. If you add more commits on top of your branch, the changes are squashed into one big unified diff for review. Most teams tend to avoid rebasing anything that was already shared. - -Gerrit is a commit based review system. Every review is based on exactly one commit. It's very common to edit shared commits and submit new versions of them to address feedback. -This model works _very well_ with GitButler's easy [commit editing](/features/branch-management/commits) features. With any other Git client, interactive rebasing and amending tends to be quite painful and error prone, making it fairly difficult to work with Gerrit's model. With GitButler, it's ideal. Just drag and drop changes and update your changesets easily. +The virtual branches toml file has the interesting metadata: -## How to turn on Gerrit Mode +```bash title="Terminal" +❯ git cat-file -p 16e47cb1d091ca9dd44^{tree}:virtual_branches.toml +[default_target] +branchName = "master" +remoteName = "origin" +remoteUrl = "git@github.com:gitbutlerapp/gitbutler.git" +sha = "e00e54af7f903ef84600079a45490a7f07e4702e" +pushRemoteName = "origin" -If you have a Gerrit remote, GitButler will automatically enable Gerrit Mode when the project is being added to GitButler. You can also enable it manually. -To manually turn on Gerrit Mode in GitButler, you just have to set a Git config option called `gitbutler.gerritMode` in the project you want to act in a Gerrit compatible fashion: +[branch_targets] +[branches.09ef54c4-1081-4a52-8182-a5ec725016b6] +id = "09ef54c4-1081-4a52-8182-a5ec725016b6" +name = "commit signing settings" +notes = "" +applied = false +upstream = "refs/remotes/origin/commit-signing-settings" +upstream_head = "b60a66452dfecef74103346af6a3291ad677d246" +created_timestamp_ms = "1717489406268" +updated_timestamp_ms = "1717489406268" +tree = "b28e7eefdd7b6f36456516b696146a2ea7638ca4" +head = "b60a66452dfecef74103346af6a3291ad677d246" +ownership = "" +order = 4 +selected_for_changes = 1717489406268 ``` -❯ cd my_project -❯ git config gitbutler.gerritMode 1 -``` - -## What is Gerrit Mode -Now GitButler will change its behavior in the following ways: - -- When you commit, we will automatically inject a `Change-Id` trailer into the commit in the format that Gerrit expects. You do not need to [setup a `commit-msg` hook](https://gerrit-review.googlesource.com/Documentation/cmd-hook-commit-msg.html) like you do with other Git clients. -- When you push, it will not push to a matching branch name on the remote. Instead it will push to `refs/for/main` (or whatever the name of the target branch is set to be). -- After a push, we record the change url and show you the link and number for each commit automatically. - - - -We can also set some extra push options when we push, including: - -- [Topics](https://gerrit-review.googlesource.com/Documentation/cross-repository-changes.html) -- [Hashtags](https://gerrit-review.googlesource.com/Documentation/intro-user.html#hashtags) -- [WIP status](https://gerrit-review.googlesource.com/Documentation/intro-user.html#wip) - - - - -# timeline.mdx - - -Undo nearly any of your actions or go back in time to an earlier state. - -## How it works - -Before GitButler does any major action, it records the state of everything (your virtual branch state, your uncommitted work, conflict state, etc) and stores it in your Git object database as snapshots. You can hit the 'revert' button on any of the entries and it will restore the state of all of these things to what they looked like when they were recorded, letting you go back in time. - - - -## Restoring State - -If you hover over any of the entries, you will see a button named "Revert" that will restore the state of things to right before you did that action. So if you revert one that says "Create Commit", it will put you where you were right before you made that commit. - - - -## Recovering Content - -Occasionally, GitButler will also take snapshots of files that were changed recently, even if they weren't committed. If this, or any other action, sees changes in files, you can see which ones and view the change by clicking on the file name. - - - - -# branch-lanes.mdx - - -All of your branches - remote, local, and virtual / applied or not - are managed in the Branch Tab. This is where you can see all of your branches, apply them to your workspace, and manage your virtual branches. - -You can access the Branches tab by clicking on the "Branches" icon in the sidebar. - -The interface looks something like this: - - - -## Branch List - -The first pane on the left shows you the virtual branches and stacks that you have as well as the other branches that you have available (legacy git branches, remote branches and PRs). - -All of these branches can be converted into virtual branches by clicking them and then clicking the "Apply to workspace" button on the top of the branch view (middle pane). - -Local branches can also be fully deleted here. - -### Current Workspace Target +The virtual_branches tree has the actual contents of those computed branches in case we need to recreate them: -The "Current workspace target" is the view of the target branch that you've set. It will show you essentially a `git log` of `origin/master` or whatever you set as your target branch, and it will show you if there are any commits upstream that you have not integrated locally yet. We will automatically check for new upstream changes every few minutes, but you can also click the update button to check immediately. + + + + + + + + + + + + + + + + + + + + + - +This allows you to get contents of any file in any of your virtual branch states as well. # gitlab-integration.mdx @@ -1652,6 +1571,212 @@ Once you have set up GitHub integration, you can open your pull requests directl When you create a new branch or commit changes, GitButler will automatically check if there are any associated pull requests on GitHub. You can view these pull requests in the "Pull Requests" tab in the sidebar of the Branches page. +# timeline.mdx + + +Undo nearly any of your actions or go back in time to an earlier state. + +## How it works + +Before GitButler does any major action, it records the state of everything (your virtual branch state, your uncommitted work, conflict state, etc) and stores it in your Git object database as snapshots. You can hit the 'revert' button on any of the entries and it will restore the state of all of these things to what they looked like when they were recorded, letting you go back in time. + + + +## Restoring State + +If you hover over any of the entries, you will see a button named "Revert" that will restore the state of things to right before you did that action. So if you revert one that says "Create Commit", it will put you where you were right before you made that commit. + + + +## Recovering Content + +Occasionally, GitButler will also take snapshots of files that were changed recently, even if they weren't committed. If this, or any other action, sees changes in files, you can see which ones and view the change by clicking on the file name. + + + + +# ai-assistance.mdx + +## Getting Started + +### Global AI Setup (One-time) + +1. Navigate to **Global settings** → **AI Options** +2. Choose your AI provider: + - **GitButler API** (default): Uses OpenAI through GitButler's servers - no API key needed + - **Your own key**: Bring your own OpenAI, Claude, Ollama, or LM Studio credentials +3. If using your own key, enter your API credentials + +### Per-Project Setup + +1. Open **Project settings** → **AI options** +2. Enable **"Enable branch and commit message generation"** +3. Optionally enable **"Enable experimental AI features"** for advanced functionality + +## Features + +### Branch Name Generation + +Automatically creates descriptive, kebab-case branch names based on your code changes. + +**Usage:** + +- Right-click on a branch header and select **Generate branch name** +- GitButler analyzes commit messages in the branch and suggests an appropriate name +- Generated names use kebab-case format and avoid conflicts with existing branches + +### Commit Message Generation + +Creates professional commit messages following best practices. + +**Features:** + +- Semantic prefixes (`feat:`, `fix:`, `refactor:`) +- 50-character title limit, 72-character body wrap +- Explains what changed and why +- Real-time streaming as AI generates the message +- Based on actual code diffs, not just file names + +**Usage:** + +1. Make changes to your files (staging is automatic in GitButler) +2. Click the **Generate message** button in the commit message editor +3. AI streams the generated message in real-time +4. Review and edit before committing + +**Example format:** + +``` +feat: add user authentication system + +Implements JWT-based authentication with login and registration +endpoints. Includes password hashing and session management +to secure user accounts. +``` + +### Pull Request Descriptions + +Generates comprehensive PR descriptions when creating pull requests. + +**Usage:** + +1. Create a pull request from your branch +2. In the PR creation dialog, click **Generate PR description** +3. AI analyzes all commits in your branch and generates a description +4. Review and edit the generated content before creating the PR + +**Generated content includes:** + +- High-level summary based on commit messages +- Structured description following PR templates (if configured) +- Context derived from the changes in your branch + +## Advanced Features + +### Custom Prompts + +- **Global Prompts**: Create custom prompts in **Global settings** → **AI Options** +- **Project-Specific Prompts**: Assign specific prompts per project in **Project settings** → **AI options** +- **Commit & Branch Prompts**: Separate customization for commit messages and branch names + +## Configuration + +### Global Settings (Global settings → AI Options) + +- **AI Provider**: Choose between OpenAI, Anthropic, Ollama, or LM Studio +- **Key Options**: Use GitButler API or bring your own credentials for each provider +- **Model Selection**: Choose specific models per provider (GPT-4o, Claude Sonnet, etc.) +- **Amount of provided context**: Set how many characters of git diff to send to AI + +### Project Settings (Project settings → AI options) + +- **Enable branch and commit message generation**: Master toggle for AI features in this project +- **Enable experimental AI features**: Access to advanced AI functionality (requires GitButler API) +- **Custom prompts**: Assign specific prompts from global settings to this project for commits and branches + +## Troubleshooting + +**AI features not working?** + +1. **Check Global Settings**: Navigate to **Global settings** → **AI Options** and verify: + - AI provider is configured (OpenAI, Anthropic, etc.) + - Key option is selected (GitButler API or your own key) + - If using your own key, ensure it's entered correctly + - Model is selected for your chosen provider +2. **Check Project Settings**: Open **Project settings** → **AI options** and ensure: + - **"Enable branch and commit message generation"** is turned ON + - This setting must be enabled for each project individually +3. **Verify API Access**: Ensure sufficient API quota and valid credentials + +**AI buttons not appearing?** + +- The project-level toggle in **Project settings** → **AI options** controls button visibility +- Without this enabled, Generate buttons won't appear in the UI + +**Need better suggestions?** + +- Customize prompt templates in **Global settings** → **AI Options** +- Make meaningful code changes with clear patterns +- Use descriptive variable names and comments in your code +- Review [troubleshooting guide](https://docs.gitbutler.com/troubleshooting/custom-csp) for advanced configurations + + +# branch-lanes.mdx + + +All of your branches - remote, local, and virtual / applied or not - are managed in the Branch Tab. This is where you can see all of your branches, apply them to your workspace, and manage your virtual branches. + +You can access the Branches tab by clicking on the "Branches" icon in the sidebar. + +The interface looks something like this: + + + +## Branch List + +The first pane on the left shows you the virtual branches and stacks that you have as well as the other branches that you have available (legacy git branches, remote branches and PRs). + +All of these branches can be converted into virtual branches by clicking them and then clicking the "Apply to workspace" button on the top of the branch view (middle pane). + +Local branches can also be fully deleted here. + +### Current Workspace Target + +The "Current workspace target" is the view of the target branch that you've set. It will show you essentially a `git log` of `origin/master` or whatever you set as your target branch, and it will show you if there are any commits upstream that you have not integrated locally yet. We will automatically check for new upstream changes every few minutes, but you can also click the update button to check immediately. + + + + # commits.mdx @@ -1789,7 +1914,7 @@ You can also arbitrarily change the order of your commits by dragging and droppi ## Edit Mode -The other way that you can modify a commit is to go into "Edit Mode". When you click on a commit, there is a button that says "Edit patch". If you click this, GitButler will check out that commit by itself into your working directory (automatically stashing everything else temporarily). +The other way that you can modify a commit is to go into "Edit Mode". When you click on a commit, there is a button that says "Edit commit". If you click this, GitButler will check out that commit by itself into your working directory (automatically stashing everything else temporarily). The screen will go into "Edit mode", indicating that you're in a special state where you're focusing on this one commit. @@ -1806,248 +1931,496 @@ Then you can change whatever you want and when you click "Save and exit", it wil This is useful for things like getting feedback on a series and being able to go into the appropriate commit, make the changes and continue, as opposed to squashing work. -# integration-branch.mdx - +# merging.mdx -Bundling all your virtual branches together -Since GitButler does some pretty fun stuff with branches in order to enable virtual branches to work, some Git commands run from other git clients, including stock Git in the terminal are bound to behave a little strangely. +By default, GitButler rebases the work on your virtual branches when you update your target branch (upstream) work. -We're getting the git data to think in three dimensions, then asking 2-D Git how to deal with it. +Often this works just fine and the commits are simply rebased. Occasionally, you will have conflicts with upstream work. -While some commands cannot work well because of this single-branch limitation (commit, checkout), we do try our best to keep everything in a state where most other commands work reasonably well. Anything having to do with the index or HEAD is a little problematic, but doable in a smooshed state (all branches look like one), while commands like `log` or `blame` work totally fine with no shenanigans. +In this case, GitButler will not do what Git normally does, which is to stop at each conflicted commit and make you fix it before moving on. Instead, it will apply the changes that it can and store the commit as a "conflicted" commit and continue the rebasing process. -## The Integration Commit -The way that we handle this relatively well is by creating an "integration" commit every time you change the committed state of your collective virtual branches. +When you go to update from upstream, GitButler will show you all the branches that it will rebase and will let you know if any of them will have conflicts: +In this case, when you perform the rebase, that branch will then contain "conflicted" commits. They will be marked in the UI as conflicted and you can click on them to get a "resolve conflict" button to start the resolution process. -So what is an "integration" commit? Well, when you apply or unapply branches, or you commit on one of your applied branches, you change the state of what GitButler sees as your overall committed state with regards to your working directory. - -## Status, Diff and Log - -To keep Git command output for things that look at the index and HEAD (such as `status` or `diff`) somewhat sane, we modify your index to look like the union of all the committed states of all your applied virtual branches. This makes `git diff` and `git status` behave more or less like you would expect. + -For instance, if you have two files on Branch A and two files on Branch B, then `git status` will simply list four files as modified. +When you click that, GitButler will remove the other virtual branches and other work from your working directory and check out just this commit with its conflict markers. It will show you a special "edit mode" screen, where you are directly editing this commit. -However, to help out, we also write out a branch with a custom commit message that tries to explain the state of things and what is happening. This is written to a branch we own called `gitbutler/workspace` and you shouldn't touch it. + -If you run `git log`, the first commit should be our custom commit message and the tree of that commit is the union of all the committed work on all your applied virtual branches, as though they were all merged together into one (something stock Git can understand). +If you want to cancel this conflict resolution, you can just hit 'Cancel' and it will go back to your normal state. If you have fixed all the issues, you can click "Save and Exit" and it will commit the conflict resolution and if needed, rebase any further commits on that branch on top of your new work. -## Committing, Branching, Checking Out + -However, if you try to use something that writes to HEAD, like `git commit` or `git checkout`, then you might have some headaches. By default, our client will simply overwrite the `gitbutler/workspace` branch commit whenever something significant changes. -We won't touch the working directory in an unexpected way, so whatever you commit won't be lost, but the commit itself will be forgotten. Don't do branch stuff in stock Git while trying to use GitButler for now. We have ideas on how to make this somewhat doable in the future, but right now it's easier on everyone to stick with one or the other. +# moving-branches.mdx -## Git Add and the Index +## Overview -If you attempt to modify the index directly (running `git add` or `git rm`), GitButler won't notice or care. It will simply overwrite it with whatever it needs during its operations, so while I wouldn't do it, there is also not a lot of danger. +GitButler allows you to move branches between stacks through a simple drag-and-drop interface. This is useful when you want to reorganize your work, change branch dependencies, or consolidate related branches into a single stack. -The worst that would happen is that you do some complex `git add -i` patch staging and then we wipe it out by rewriting the index. But again, you shouldn't be using stock Git commands related to committing or branching. You gotta choose one or the other for now, you can't go back and forth super easily. +## Moving a Branch to Another Stack -## Recovering or Stopping GitButler Usage +You can move a branch from one stack to another by dragging it to a specific position in the target stack. -If you want to stop using GitButler and go back to using stock Git commands for committing and branching, simply check out another branch. GitButler will realize that you've changed its branch and stop functioning until you reset it. +### How to Move a Branch -To help with remembering where you were, the integration commit should have the branch name and commit SHA that you were on when GitButler was initially activated. You should be able to easily go back to that branch and its last known commit state. +1. **Drag the branch header** - Click and hold on the branch header (the section displaying the branch name) +2. **Drop on target location** - Drag it to the position where you want to insert it in another stack +When you hover over a valid drop target during the drag operation, you'll see visual indicators showing where the branch will be placed. -# index.mdx + -## Overview +### Drop Zones -GitButler is a new Source Code Management system designed to manage your branches, record and backup your work, be your Git client, help with your code and much more. Our focus is everything after writing code in your editor and before sharing it on GitHub. We're focused on your working directory and how we can help with everything you do there. +There are two types of drop zones when moving branches: - +- **Between branches** - Drop the branch between two existing branches in a stack to insert it at that position +- **Top of stack** - Drop at the top of a stack to make the moved branch the first branch -Here you will find documentation on the product and the way that we're running our beta and building our product with your help. +The drop zones appear as horizontal lines with visual feedback when you hover over them during a drag operation. -## Getting Started +## Creating a New Stack (Tearing Off) -Check out our [Getting Started](/guide) guide to get started with GitButler, or check out our helpful video overview: +You can also separate a branch from its current stack and create a new independent stack for it. This is called "tearing off" a branch. -https://www.youtube.com/watch?v=DhJtNNhCNLM +### How to Tear Off a Branch -## Why We're Doing This +1. **Drag the branch header** - Click and hold on the branch header you want to separate +2. **Drop outside the stacks area** - Drag it to the designated dropzone outside of the existing stacks -Read about it over [Why GitButler](/why-gitbutler) Section. +This creates a new standalone stack containing just that branch. -## What We Do + -The GitButler client is a powerful Git client. You can manage your branches, work on multiple things at once, push and fetch from your Git server, easily rebase and modify commits and more. We have a unique approach to merge conflicts that helps split up any conflicting work. It also keeps a timeline so that you can easily undo any operation. +## Important Considerations -- [Virtual Branches](/features/branch-management/virtual-branches) -- [First Class Conflicts](/features/branch-management/merging) -- [Project History](/features/timeline) +### Branch Requirements +Not all branches can be moved. A branch must meet these conditions to be movable: +- **No conflicts** - The branch must not have any merge conflicts +- **Has commits** - The branch must contain at least one commit +- **Different target stack** - You can only move branches to different stacks (not within the same stack) -# ai-assistance.mdx +### What Happens During a Move -## Getting Started +When you move a branch: -### Global AI Setup (One-time) +1. **Branch is extracted** - The branch and its commits are extracted from the source stack +2. **Rebasing occurs** - The branch is rebased onto the new base in the target stack +3. **References updated** - Git references are updated to reflect the new structure +4. **Stack cleanup** - If the moved branch was the last one in its stack, that stack is deleted -1. Navigate to **Global settings** → **AI Options** -2. Choose your AI provider: - - **GitButler API** (default): Uses OpenAI through GitButler's servers - no API key needed - - **Your own key**: Bring your own OpenAI, Claude, Ollama, or LM Studio credentials -3. If using your own key, enter your API credentials +### Pull Request Updates -### Per-Project Setup +If you're using GitHub integration and the branch has an associated pull request: -1. Open **Project settings** → **AI options** -2. Enable **"Enable branch and commit message generation"** -3. Optionally enable **"Enable experimental AI features"** for advanced functionality +- **PR descriptions are automatically updated** - The PR base and description are updated to reflect the new stack structure +- **Stack PRs are synchronized** - All PRs in both the source and target stacks have their descriptions updated to maintain proper stacking information -## Features +## Related Features -### Branch Name Generation +- [Stacked Branches](/features/branch-management/stacked-branches) - Learn about working with stacked branches +- [Branch Lanes](/features/branch-management/branch-lanes) - Understand the branch lane interface +- [Virtual Branches](/features/branch-management/virtual-branches) - Core concepts of GitButler's virtual branch system -Automatically creates descriptive, kebab-case branch names based on your code changes. -**Usage:** +# pushing-and-fetching.mdx -- Right-click on a branch header and select **Generate branch name** -- GitButler analyzes commit messages in the branch and suggests an appropriate name -- Generated names use kebab-case format and avoid conflicts with existing branches -### Commit Message Generation +GitButler can authenticate with an upstream Git server in several different ways. -Creates professional commit messages following best practices. + You can just tell us to use the system Git executable, which you can setup however you want. You can use our built in SSH protocol with your own SSH key (this does not require you to have Git installed), or you can use the default [Git credentials helper](https://git-scm.com/doc/credential-helpers). -**Features:** +You can set your preference (and test if it works) in your project's "Git authentication" section: -- Semantic prefixes (`feat:`, `fix:`, `refactor:`) -- 50-character title limit, 72-character body wrap -- Explains what changed and why -- Real-time streaming as AI generates the message -- Based on actual code diffs, not just file names + -**Usage:** +Once that's done, GitButler will be able to automatically fetch upstream work and push new branches to your upstream server. -1. Make changes to your files (staging is automatic in GitButler) -2. Click the **Generate message** button in the commit message editor -3. AI streams the generated message in real-time -4. Review and edit before committing -**Example format:** +# overview.mdx -``` -feat: add user authentication system -Implements JWT-based authentication with login and registration -endpoints. Includes password hashing and session management -to secure user accounts. -``` +# Butler Review -### Pull Request Descriptions + + We have paused work on Butler Review for now to concentrate more on the client experience. Review + will be coming back in our upcoming server functionality, stay tuned. + -Generates comprehensive PR descriptions when creating pull requests. +Butler Review is a new approach to code review that focuses on reviewing code +as a series of evolving patches rather than a unified diff of a whole branch. -**Usage:** + -1. Create a pull request from your branch -2. In the PR creation dialog, click **Generate PR description** -3. AI analyzes all commits in your branch and generates a description -4. Review and edit the generated content before creating the PR +## Why Butler Review? -**Generated content includes:** +Traditional code review tools like GitHub and GitLab show changes as a single +diff of the entire branch. This makes it hard to review changes in a branch +incrementally, especially when the branch is large. It also discourages creating +good commit or commit messages, since they are all sqaushed in review and +messages are difficult to find and unrelated to the code review UI. -- High-level summary based on commit messages -- Structured description following PR templates (if configured) -- Context derived from the changes in your branch +Butler Review approaches the problem differently, by making it easy to review +individual commits as patches and tracking changes to the series when you rebase +or amend the branch. -## Advanced Features +This makes it easier to review changes incrementally and evolve a series rather +than pushing poor commits to the branch to address feedback. -### Custom Prompts +## Enabling Butler Review -- **Global Prompts**: Create custom prompts in **Global settings** → **AI Options** -- **Project-Specific Prompts**: Assign specific prompts per project in **Project settings** → **AI options** -- **Commit & Branch Prompts**: Separate customization for commit messages and branch names +To start using Butler Reviews, you need to enable the feature in your GitButler +client in the project settings. Click the gear icon in the bottom left corner of +the GitButler client and go to the Server tab. -## Configuration + -### Global Settings (Global settings → AI Options) +Once you enable Butler Review, you can start creating reviews for your branches. -- **AI Provider**: Choose between OpenAI, Anthropic, Ollama, or LM Studio -- **Key Options**: Use GitButler API or bring your own credentials for each provider -- **Model Selection**: Choose specific models per provider (GPT-4o, Claude Sonnet, etc.) -- **Amount of provided context**: Set how many characters of git diff to send to AI +## Creating a Review -### Project Settings (Project settings → AI options) +Once you have enabled Butler Review for a project, you can create a review for +a branch by clicking the new "Create Butler Review" button in the branch header card. -- **Enable branch and commit message generation**: Master toggle for AI features in this project -- **Enable experimental AI features**: Access to advanced AI functionality (requires GitButler API) -- **Custom prompts**: Assign specific prompts from global settings to this project for commits and branches + -## Troubleshooting +Once you create a review, you will see a new card in the branch header that +shows the status of the review. -**AI features not working?** + -1. **Check Global Settings**: Navigate to **Global settings** → **AI Options** and verify: - - AI provider is configured (OpenAI, Anthropic, etc.) - - Key option is selected (GitButler API or your own key) - - If using your own key, ensure it's entered correctly - - Model is selected for your chosen provider -2. **Check Project Settings**: Open **Project settings** → **AI options** and ensure: - - **"Enable branch and commit message generation"** is turned ON - - This setting must be enabled for each project individually -3. **Verify API Access**: Ensure sufficient API quota and valid credentials +If you have GitHub integration enabled, you will see a dropdown to let you choose +to create a PR or a Butler Review. You can also create both, in either order and +they will be linked together. -**AI buttons not appearing?** + -- The project-level toggle in **Project settings** → **AI options** controls button visibility -- Without this enabled, Generate buttons won't appear in the UI +Once a Review has been opened, you will have a URL that you can share with your +team to get feedback on your changes. -**Need better suggestions?** +## Reviewing a Series -- Customize prompt templates in **Global settings** → **AI Options** -- Make meaningful code changes with clear patterns -- Use descriptive variable names and comments in your code -- Review [troubleshooting guide](https://docs.gitbutler.com/troubleshooting/custom-csp) for advanced configurations +When a reviewer opens a review, they will see a list of patches that make up the branch. + -# signing-commits.mdx + + Currently, all reviews are "unlisted", meaning that anyone with the URL can see the review. + In the future, we will implement fully public and fully private reviews, but currently all reviews + are unlisted. +However, if someone has the URL, they will only be able to see the patch data, +they will not have access to all of your source code, nor will they be able to make changes, +only approve or request changes. -GitHub and GitLab provide a mechanism to verify signed commits using an uploaded public SSH or GPG key. GitButler can be configured automatically sign all your commits. + -Git provides a mechanism to sign your commits with a GPG key or SSH key. This enables other developers to make sure that you were actually the person who committed it, rather than someone else just setting their email to yours and committing it as if they were you. +Now the reviewer can hit "Start Review" to start reviewing the patches in the series. +It will start them at the "bottom", the first patch and they can work their way up. +The reviewer can also click on a patch to go directly to it. -To make this work, a signature is added to the commit header and then that signature is checked against public key stored somewhere, generally for most people the most useful way to verify these signatures is through GitHub or GitLab. +## Reviewing a Patch -This is what a verified commit looks like on both systems: +Once a reviewer starts reviewing a patch, they will see the changes in the commit +and a chat window to leave comments. - + - +The reviewer can leave comments on the patch, approve the patch, or request changes. +The chat discussion is real time, so reviewers can discuss the changes with the author +as they review the patch. -This means that the server has a public key that you used to sign the commits that is associated to your account and has verified that this user actually signed this commit. +You can also select lines of code to comment on. This will highlight the line in the +diff and show the comment in the chat window. -In order for this to work, you need to: +Every patch needs to be approved before the branch is considered approved. -1. Tell GitButler to sign your commits +## Requesting Changes + +A reviewer can either approve a patch or request changes. If they request changes, +the author can amend that specific commit and publish the changes to the review. + +Reviewers will be able to see in the chat that a new version of the patch has been +published and they can review the changes again. + + + Currently we don't have interdiffs, so reviewers will need to review the entire patch again. We + plan to add interdiffs in the very near future. + + +## Approving a Patch + +Once a reviewer is happy with a patch, they can approve it. This will mark the patch +as approved and move the reviewer to the next patch in the series. + +## Closing a Review + +Once the branch has been merged, it will automatically mark the review as closed. +The author can also manually close the review if they decide to abandon the series. + + +# rules.mdx + + +Rules are a powerful automation feature in GitButler that automatically assign file changes to specific branches based on conditions you define. Instead of manually dragging changes between lanes, you can set up rules that automatically route changes where they belong. + +## Overview + +When you're working on multiple branches simultaneously, you often know in advance which types of changes should go to which branch. For example, documentation updates might always go to a `docs` branch, while UI changes go to a `feature/ui-redesign` branch. + +Rules eliminate the manual work of assigning changes by automatically evaluating your uncommitted changes and routing them to the appropriate branch based on filters you define. + +## How Rules Work + +Rules are evaluated whenever files change in your working directory (the `fileSytemChange` trigger). Each rule consists of: + +1. **Filters**: Conditions that determine which changes the rule applies to +2. **Action**: Assigns matching changes to a specific branch + +Multiple rules can exist, and they are evaluated in order. Within a single rule, multiple filters are combined with AND logic - all conditions must match for the rule to apply. + +## Creating a Rule + +To create a rule: + +1. Open the **Rules** drawer at the bottom of the GitButler interface +2. Click the **+** button to add a new rule +3. Select the target branch where matching changes should be assigned: + - Specify a branch by name + - **Leftmost lane**: The leftmost branch in your workspace + - **Rightmost lane**: The rightmost branch in your workspace +4. (Optional) Add filters to specify which changes should match +5. Click **Save rule** + +If you don't add any filters, the rule will match all changes. + + +

    + Creating a rule that assigns changes to the leftmost lane +

    + +## Filter Types + +Rules support several types of filters to match changes: + +### Path Matches Regex + +Matches file paths using a regular expression pattern. + +**Example use cases:** + +- Match all TypeScript files: `.*\.ts$` +- Match files in a specific directory: `^src/components/.*` +- Match documentation files: `.*\.(md|mdx)$` + +### Content Matches Regex + +Matches the content of changed lines using a regular expression pattern. This filter only looks at added lines (lines that start with `+` in the diff). + +**Example use cases:** + +- Match changes containing TODOs: `TODO` +- Match changes with specific function calls: `console\.log` +- Match changes with certain patterns: `@deprecated` + + +

    + Creating a rule that assigns changes containing "fix" to a specific branch +

    + +### Claude Code Session ID + +Matches changes that originated from a specific Claude Code session. This filter is automatically used when GitButler's AI features create branches and rules for you. + +**Note**: Rules with Claude Code Session ID filters have lower priority than manually created rules. If a change matches both an AI-generated rule and a manual rule, the manual rule takes precedence. + + +

    + AI-generated rules based on Claude Code sessions +

    + +## Managing Rules + +### Editing Rules + +To edit an existing rule: + +1. Double-click the rule or click the elepsis menu (...) and select "Edit rule" +2. Modify the branch assignment or filters +3. Click **Save rule** + +### Deleting Rules + +To delete a rule: + +1. Click the elepsis menu (...) on the rule +2. Select "Delete rule" +3. Confirm the deletion + +**Note**: AI-generated rules and implicit rules cannot be edited. + +## Understanding Rule Evaluation + +### Order Matters + +Rules are evaluated in the order they appear in the Rules drawer (most recent first). The first matching rule determines where a change is assigned. + +### AND Logic Within Rules + +When a rule has multiple filters, **all filters must match** for the rule to apply. For example, a rule with both "Path Matches Regex: `.*\.ts$`" and "Content Matches Regex: `TODO`" will only match TypeScript files that contain the text "TODO" in their changes. + +### OR Logic Across Rules + +If you want to match changes that meet any of several conditions (OR logic), create separate rules for each condition. + +### Interaction with Hunk Dependencies + +Rules respect hunk dependencies (locks). If a change depends on a commit in a specific branch, it cannot be automatically reassigned by rules, even if it matches a rule's filters. + +## Best Practices + +1. **Start simple**: Begin with one or two basic path-matching rules before adding complex filters +2. **Order your rules**: Place more specific rules before general catch-all rules +3. **Test your regex**: Make sure your regular expressions match what you intend - it's easy to be too broad or too narrow +4. **Use catch-all rules carefully**: A rule with no filters will match everything, which can interfere with other rules +5. **Consider your workflow**: Rules work best when you have predictable patterns in how your work is organized +6. **Leverage leftmost/rightmost**: Using position-based targeting lets you reorganize lanes without updating rules + +## Limitations + +- Rules can only assign changes to branches that exist in your workspace (applied branches) +- Implicit (AI-determined) rules cannot be edited through the UI +- Rules currently only support the `assign` action for filesystem changes + +## Related Features + +- [Virtual Branches](/features/branch-management/virtual-branches): Understanding the branch system that rules work with +- [Branch Lanes](/features/branch-management/branch-lanes): How lanes are organized and how rules interact with lane positioning +- [AI Assistance](/features/branch-management/ai-assistance): How AI can create and manage rules automatically + + +# signing-commits.mdx + + +GitHub and GitLab provide a mechanism to verify signed commits using an uploaded public SSH or GPG key. GitButler can be configured automatically sign all your commits. + +Git provides a mechanism to sign your commits with a GPG key or SSH key. This enables other developers to make sure that you were actually the person who committed it, rather than someone else just setting their email to yours and committing it as if they were you. + +To make this work, a signature is added to the commit header and then that signature is checked against public key stored somewhere, generally for most people the most useful way to verify these signatures is through GitHub or GitLab. + +This is what a verified commit looks like on both systems: + + + + + +This means that the server has a public key that you used to sign the commits that is associated to your account and has verified that this user actually signed this commit. + +In order for this to work, you need to: + +1. Tell GitButler to sign your commits 2. Upload your key as a "signing key" to GitHub or GitLab (or elsewhere) ## Telling GitButler to Sign @@ -2057,6 +2430,7 @@ For GitButler to sign commits, you need to setup Git to sign commits, as we do r The main difference is that instead of only the `commit.gpgSign` as the flag that tells Git to automatically sign commits, we look for `gitbutler.signCommits` first. Thus, if Git would sign, GitButler will attempt to sign your commits with the normal Git settings as well. But if something goes wrong, `gitbutler.signCommits` will be set to `false` in the repository-local settings to prevent commits from failing generally. +Set `gitbutler.signCommits` to `true` in your user-level `~/.gitconfig`, to prevent GitButler from automatically disabling commit signing for that repository. We look to see if we have a signing key in `user.signingkey`. If we have a key, we look for 'ssh' in `gpg.format`, otherwise we use GPG. We will respect `gpg.ssh.program` for ssh if there is a different binary path, and `gpg.program` for GPG. We also identify literal SSH keys in the `user.signingkey` field. @@ -2143,168 +2517,37 @@ Now paste in the public SSH key you copied from GitButler, name it and make sure /> -# moving-branches.mdx +# stacked-branches.mdx -## Overview -GitButler allows you to move branches between stacks through a simple drag-and-drop interface. This is useful when you want to reorganize your work, change branch dependencies, or consolidate related branches into a single stack. +Create a stack of dependent branches to be reviewed and merged in order. -## Moving a Branch to Another Stack +## Overview -You can move a branch from one stack to another by dragging it to a specific position in the target stack. +GitButler allows you to create an ordered stack of branches where each branch depends on (and is based on) the previous one. +The application also supports creating the appropriate stacked Pull Requests (when used with a GitHub remote). +This is useful when you have multiple changesets that depend on each other but it is desirable to have them reviewed and merged separately (and in sequence). -### How to Move a Branch +> All of the Pull Request stack orchestration is done locally in the client, which means that your repo content is not shared with a cloud service. -1. **Drag the branch header** - Click and hold on the branch header (the section displaying the branch name) -2. **Drop on target location** - Drag it to the position where you want to insert it in another stack + -When you hover over a valid drop target during the drag operation, you'll see visual indicators showing where the branch will be placed. +## Use cases - +Using stacked branches (Pull Requests) can be helpful for shipping smaller changes more frequently. -### Drop Zones +### Breaking up a larger change into smaller ones -There are two types of drop zones when moving branches: +Consider a scenario where you are implementing a medium/large feature in your software project. +In the course of implementation you end up performing the following sub-tasks: -- **Between branches** - Drop the branch between two existing branches in a stack to insert it at that position -- **Top of stack** - Drop at the top of a stack to make the moved branch the first branch +1. Refactor a part of the codebase to accommodate the new feature +2. Implement an API endpoint supporting the feature +3. Implement the frontend part of the feature consuming the API -The drop zones appear as horizontal lines with visual feedback when you hover over them during a drag operation. - -## Creating a New Stack (Tearing Off) - -You can also separate a branch from its current stack and create a new independent stack for it. This is called "tearing off" a branch. - -### How to Tear Off a Branch - -1. **Drag the branch header** - Click and hold on the branch header you want to separate -2. **Drop outside the stacks area** - Drag it to the designated dropzone outside of the existing stacks - -This creates a new standalone stack containing just that branch. - - - -## Important Considerations - -### Branch Requirements - -Not all branches can be moved. A branch must meet these conditions to be movable: - -- **No conflicts** - The branch must not have any merge conflicts -- **Has commits** - The branch must contain at least one commit -- **Different target stack** - You can only move branches to different stacks (not within the same stack) - -### What Happens During a Move - -When you move a branch: - -1. **Branch is extracted** - The branch and its commits are extracted from the source stack -2. **Rebasing occurs** - The branch is rebased onto the new base in the target stack -3. **References updated** - Git references are updated to reflect the new structure -4. **Stack cleanup** - If the moved branch was the last one in its stack, that stack is deleted - -### Pull Request Updates - -If you're using GitHub integration and the branch has an associated pull request: - -- **PR descriptions are automatically updated** - The PR base and description are updated to reflect the new stack structure -- **Stack PRs are synchronized** - All PRs in both the source and target stacks have their descriptions updated to maintain proper stacking information - -## Related Features - -- [Stacked Branches](/docs/features/branch-management/stacked-branches) - Learn about working with stacked branches -- [Branch Lanes](/docs/features/branch-management/branch-lanes) - Understand the branch lane interface -- [Virtual Branches](/docs/features/branch-management/virtual-branches) - Core concepts of GitButler's virtual branch system - - -# pushing-and-fetching.mdx - - -GitButler can authenticate with an upstream Git server in several different ways. - - You can just tell us to use the system Git executable, which you can setup however you want. You can use our built in SSH protocol with your own SSH key (this does not require you to have Git installed), or you can use the default [Git credentials helper](https://git-scm.com/doc/credential-helpers). - -You can set your preference (and test if it works) in your project's "Git authentication" section: - - - -Once that's done, GitButler will be able to automatically fetch upstream work and push new branches to your upstream server. - - -# virtual-branches.mdx - - -Virtual branches are a powerful feature of GitButler that allow you to work on multiple branches at the same time, committing to them independently and simultaneously. This is a key part of the GitButler experience, allowing you to manage your work in a flexible and efficient way that is not possible with traditional Git tooling. - -## Overview - -With normal Git branching, you can only work on one branch at a time. There is one `HEAD` reference and one index. - -With virtual branches, you can have multiple branches applied to your working directory at the same time. Each branch is represented as a vertical lane, and you can drag changes between these lanes to commit them independently. - -Each lane also has its own staging area, so you can stage changes for each branch before deciding to commit them. - - - -## How it works - -Let's say that you make changes to two different files and `git status` would list two modified files. In GitButler, you can "assign" the change in each file to a different "virtual" branch, then when you commit, it will create a commit that only contains the changes in that file for that branch. - -One of the nice things with this approach is that since you're starting from changes in a single working directory, you can be sure that all branches that you create from it will merge cleanly, as you're essentially starting from the merge product and extracting branches of work from it. - - -# stacked-branches.mdx - - -Create a stack of dependent branches to be reviewed and merged in order. - -## Overview - -GitButler allows you to create an ordered stack of branches where each branch depends on (and is based on) the previous one. -The application also supports creating the appropriate stacked Pull Requests (when used with a GitHub remote). -This is useful when you have multiple changesets that depend on each other but it is desirable to have them reviewed and merged separately (and in sequence). - -> All of the Pull Request stack orchestration is done locally in the client, which means that your repo content is not shared with a cloud service. - - - -## Use cases - -Using stacked branches (Pull Requests) can be helpful for shipping smaller changes more frequently. - -### Breaking up a larger change into smaller ones - -Consider a scenario where you are implementing a medium/large feature in your software project. -In the course of implementation you end up performing the following sub-tasks: - -1. Refactor a part of the codebase to accommodate the new feature -2. Implement an API endpoint supporting the feature -3. Implement the frontend part of the feature consuming the API - -While the feature is considered complete only when all of the subtasks are implemented, reviewed and merged, in many cases it is considered beneficial -to ship each stage of the feature on its own, potentially behind a feature flag. Not only the risk of merge conflicts with colleagues is reduced, -but also eventual bugs are easier to track down / revert / fix as compared to a single large change. +While the feature is considered complete only when all of the subtasks are implemented, reviewed and merged, in many cases it is considered beneficial +to ship each stage of the feature on its own, potentially behind a feature flag. Not only the risk of merge conflicts with colleagues is reduced, +but also eventual bugs are easier to track down / revert / fix as compared to a single large change. ### More granular (easier) review process @@ -2441,6 +2684,52 @@ In this case it will merged in the parent branch. In order to recover from this situation you can simply force push the branches and then re-create the PR that was incorrectly merged. +# virtual-branches.mdx + + +Virtual branches are a powerful feature of GitButler that allow you to work on multiple branches at the same time, committing to them independently and simultaneously. This is a key part of the GitButler experience, allowing you to manage your work in a flexible and efficient way that is not possible with traditional Git tooling. + +## Overview + +With normal Git branching, you can only work on one branch at a time. There is one `HEAD` reference and one index. + +With virtual branches, you can have multiple branches applied to your working directory at the same time. Each branch is represented as a vertical lane, and you can drag changes between these lanes to commit them independently. + +Each lane also has its own staging area, so you can stage changes for each branch before deciding to commit them. + + + +## How it works + +Let's say that you make changes to two different files and `git status` would list two modified files. In GitButler, you can "assign" the change in each file to a different "virtual" branch, then when you commit, it will create a commit that only contains the changes in that file for that branch. + +One of the nice things with this approach is that since you're starting from changes in a single working directory, you can be sure that all branches that you create from it will merge cleanly, as you're essentially starting from the merge product and extracting branches of work from it. + + +# ai-overview.mdx + + +If you're using AI agent tools like Cursor, Windsurf, or Claude Code, GitButler can enhance your coding experience by managing commits, saving points, and more. These integrations allow you to focus on coding with your agents while GitButler handles the version control aspects. + +https://www.youtube.com/watch?v=J6xV_Wyz9zg + +There are currently three main ways to use AI tools with GitButler: + +1. **Our Coding Agent**: If you have Claude Code setup, you can use our [Code Agent](/features/coding-agents) as a GUI for running Claude Code directly. +2. **Using Hooks in Claude Code or Cursor**: This method allows you to use GitButler's CLI as hook commands to manage commits and branches in either Claude Code or Cursor. +3. **Using the MCP Server**: This method allows you to set up your AI agent to communicate with GitButler's MCP server, enabling features like automatic commits and save points. + +## Enabling the experimental feature flag + +Note that as of GitButler version `0.15.2` these features have to be enabled via an experimental feature flag. You can find that under `Global Settings` -> `Experimental` -> `GitButler Actions`. + + # claude-code-hooks.mdx If you are using Claude Code, you can use the new ["hooks"](https://docs.anthropic.com/en/docs/claude-code/hooks) functionality to manage the output of even multiple simultaneous instances, while isolating all the generated code into virtual or stacked branches automatically. In this case, there is no need to set up the MCP server, as the hooks will handle everything for you. @@ -2526,438 +2815,808 @@ For example, if you have three sessions of Claude Code running at the same time, When the agent is done, GitButler will commit all the changes and write a more sophisticated commit message based on what you had prompted your agent. -# ai-overview.mdx +# mcp-server.mdx -If you're using AI agent tools like Cursor, Windsurf, or Claude Code, GitButler can enhance your coding experience by managing commits, saving points, and more. These integrations allow you to focus on coding with your agents while GitButler handles the version control aspects. +If you use an AI agent (such as Cursor, Windsurf, Claude Code) to help you with your code, you can easily setup GitButler to manage your commits automatically, keep save points, and more. You know, _vibe_ commit... -https://www.youtube.com/watch?v=J6xV_Wyz9zg +## Setting up your Agent to use GitButler -There are currently three main ways to use AI tools with GitButler: +The first step is to let your agent know about GitButler, which is done via MCP - you need to tell your agent to use the GitButler MCP server. -1. **Our Coding Agent**: If you have Claude Code setup, you can use our [Code Agent](/features/coding-agents) as a GUI for running Claude Code directly. -2. **Using Hooks in Claude Code or Cursor**: This method allows you to use GitButler's CLI as hook commands to manage commits and branches in either Claude Code or Cursor. -3. **Using the MCP Server**: This method allows you to set up your AI agent to communicate with GitButler's MCP server, enabling features like automatic commits and save points. +### Installing the CLI -## Enabling the experimental feature flag +GitButler provides a CLI that can be used to interact with the GitButler platform. Before you can setup AI Agent integration, you will need to install the CLI. -Note that as of GitButler version `0.15.2` these features have to be enabled via an experimental feature flag. You can find that under `Global Settings` -> `Experimental` -> `GitButler Actions`. +This can be found by opening the GitButler global settings, and then clicking on the "Install CLI" button in the General settings. + + +Now that you have the `but` CLI installed, your agent can use the CLI's MCP server to interact with GitButler. -# cursor-hooks.mdx +### Cursor + +To install the GitButler MCP server in Cursor, first go to the Cursor settings, and then click on the "Extensions" tab, then click on "Tools and Integrations" and click on "New MCP Server". + +This will open your `~/.cursor/mcp.json` file. + +Add the following to the `mcpServers` object: + +```json +{ + "mcpServers": { + "gitbutler": { + "command": "but", + "args": ["mcp"] + } + } +} +``` + +You should see the GitButler MCP server in the list of MCP servers and it should have the tool `gitbutler_update_branches` available. + +### VSCode + +To install the GitButler MCP server in VSCode, you need to select "MCP: List Servers" from the actions menu. Then select "Add Server". Select "stdio" as the server type. + +Now you can type your command (`but mcp`) and name it something. After this, it should open up your settings file and show you something like this: + +```json + "mcp": { + "servers": { + Running | Stop | Restart | 1 tools + "gitbutler-mcp": { + "type": "stdio", + "command": "but", + "args": ["mcp"] + } + } + } +``` + +However, if you have Cursor's MCP already setup, VSCode will notice and help you automatically reuse the settings. + + + +### Claude Code + +Adding an MCP server to Claude Code is done by running the `claude mcp add` command. + +``` +❯ claude mcp add gitbutler but mcp +Added stdio MCP server gitbutler with command: but mcp to local config + +❯ claude mcp list +gitbutler: but mcp +``` + +## Rules: How to configure auto committing + +Once you have installed the MCP server in your editor or agent, you can optionally configure it to automatically commit your changes. + +We've found that adding something like this to your rules works well: + +``` +If you generate code or modify files, run the gitbutler update branches MCP tool. +``` + +## How to add rules + +Cursor stores its rules in `~/.cursor/rules` file, but you can also manually set them by going to the Cursor Settings pane, clicking 'Rules' and adding them to the User Rules section. + +In VSCode's Copilot Agent Mode, you can use ["custom instructions"](https://code.visualstudio.com/docs/copilot/copilot-customization#_custom-instructions) to accomplish this. + +In Claude Code, they are now called "memories" and you can add them by hitting '#' and storing them in user memory (or local if you just want them in one project). + + + +Or directly in your `~/.claude/CLAUDE.md` rules file: + +``` +❯ cat ~/.claude/CLAUDE.md +## Development Workflow +- When you're done with a task where code was created or files edited, please run the gitbutler mcp update_branches command. +``` + +## Using GitButler with your agent + +If you've set up a rule/instruction/memory, then every time a chat session is completed, the agent will send the changes and prompt to GitButler and it will automatically commit the changes. + + + +If you're using Claude Code, it may look something like this: + + + +If you don't have the agent setup to automatically call our tool, then you can also just manually type 'update gitbutler branches' in the chat, but that's a little less magical. + +## GitButler interface + +There are two phases to GitButler's MCP agent interaction. The first is the agent sending the changes and prompt to GitButler, which GitButler will quickly record and then return a success to the agent. The second is GitButler processing that raw recorded change and attempting to process that change into a commit. + +### Recording the changes + +When your agent calls the `gitbutler_update_branches` tool, GitButler will record the changes and prompt and then immediately return to the agent, so the call should be very fast. + +So for instance, let's say that I prompted my coding agent to update my `README.md` file to add a list of contributing authors. When the agent is done, it should call the update branches MCP tool, which will record a commit that looks something like this: + + + +### Processing the changes + +Then, if you have AI tooling setup, GitButler will see that and turn it into a commit message like this: + + + +You can see all of these steps in the "Actions" section of the GitButler interface, which you can toggle by hitting the "Actions" button in the top right of the interface. + + + +In the near future, we will also be able to do more interesting things like auto-absorbing changes into existing commits, creating new branches based on the prompt theme, creating stacked branches, and more. + + +# cursor-hooks.mdx + +GitButler integrates seamlessly with Cursor through hooks that automatically manage your commits and branches while you're using AI coding features. This allows you to automatically maintain clean git history and organized virtual branches. + +Here's a short video showing how GitButler works with Cursor hooks: + +https://youtu.be/NOYK7LTFvZM + +Ok, let's get it set up. + +## Install the GitButler CLI + +First, you need to install the GitButler CLI, which can be done in your General settings. See the [MCP Server documentation](mcp-server) for more details on how to install the CLI. + +## Installing GitButler as a Hook + +Once the command line tool is installed, you can add the `but cursor` commands as hooks. + +You will need to create or edit your `~/.cursor/hooks.json` file (globally) or `[project]/.cursor/hooks.json` file (single project) to have `afterFileEdit` and `stop` hooks like this: + +```json +{ + "version": 1, + "hooks": { + "afterFileEdit": [ + { + "command": "but cursor after-edit" + } + ], + "stop": [ + { + "command": "but cursor stop" + } + ] + } +} +``` + +## Using GitButler with Cursor + +Once the hooks are setup, Cursor will automatically call GitButler when it edits files and when it's done with a task, which will trigger GitButler to: + +- Create a branch if the chat session is new +- Assign edits to an active branch +- Commit with a message based on the prompt when a task is done + + +# debugging.mdx + + +If you are having technical issues with the GitButler client, here are a few things you can do to help us help you. Or help yourself. + +If you get stuck or need help with anything, hit us up over on Discord, here's [GitButler Discord Server Link](https://discord.gg/MmFkmaJ42D). + + + +The first things to try is checking out the frontend related logs in the console by opening the developer tools in GitButler via the "View" -> "Developer Tools" menu option. Next, if you launch GitButler from the command line, you can view the backend logs directly in your terminal. + +## Logs + +Often the most helpful thing is to look at the logs. GitButler is a Tauri app, so the logs are in your OS's [app log directory](https://docs.rs/tauri/1.8.1/tauri/api/path/fn.app_log_dir.html). This should be: + + + + ```bash + ~/Library/Logs/com.gitbutler.app/ + ``` + + + ```bash + C:\Users\[username]\AppData\Local\com.gitbutler.app\logs + ``` + + + ```bash + ~/.config/gitbutler/logs/ [OR] + ~/.local/share/gitbutler-tauri/logs/ + ``` + + + +In this directory, there should be rolling daily logs: + + +```bash title="Terminal" +❯ cd ~/Library/Logs/com.gitbutler.app + +❯ tree -L 1 + +├── GitButler.log +├── GitButler.log.2023-09-02 +├── GitButler.log.2023-09-03 +├── GitButler.log.2023-09-04 +├── GitButler.log.2023-09-05 +├── GitButler.log.2023-09-06 +├── GitButler.log.2023-09-07 +├── GitButler.log.2023-09-08 +├── GitButler.log.2023-10-10 +├── GitButler.log.2024-01-30 +└── tokio-console + +❯ tail GitButler.log.2024-01-30 +2024-01-30T13:02:56.319843Z INFO get_public_key: gitbutler-app/src/keys/commands.rs:20: new +2024-01-30T13:02:56.320000Z INFO git_get_global_config: gitbutler-app/src/commands.rs:116: new key="gitbutler.utmostDiscretion" +2024-01-30T13:02:56.320117Z INFO git_get_global_config: gitbutler-app/src/commands.rs:116: new key="gitbutler.signCommits" +2024-01-30T13:02:56.320194Z INFO get_public_key: gitbutler-app/src/keys/commands.rs:20: close time.busy=317µs time.idle=47.0µs +2024-01-30T13:02:56.320224Z INFO git_get_global_config: gitbutler-app/src/commands.rs:116: close time.busy=204µs time.idle=25.3µs key="gitbutler.utmostDiscretion" +2024-01-30T13:02:56.320276Z INFO git_get_global_config: gitbutler-app/src/commands.rs:116: close time.busy=133µs time.idle=35.8µs key="gitbutler.signCommits" +2024-01-30T13:02:56.343467Z INFO menu_item_set_enabled: gitbutler-app/src/menu.rs:11: new menu_item_id="project/settings" enabled=false +2024-01-30T13:02:56.343524Z INFO menu_item_set_enabled: gitbutler-app/src/menu.rs:11: close time.busy=35.7µs time.idle=28.8µs menu_item_id="project/settings" enabled=false +``` + + +## Data Files + +GitButler also keeps its own data about each of your projects. The virtual branch metadata, your user config stuff, a log of changes in each file, etc. If you want to inspect what GitButler is doing or debug or reset everything, you can go to our data directory. + + + + ```bash + ~/Library/Application Support/com.gitbutler.app/ + ``` + + + ```bash + C:\Users\[username]\AppData\Roaming\com.gitbutler.app + ``` + + + ```bash + ~/.local/share/gitbutler-tauri/ + ``` + + + +In this folder there are a bunch of interesting things. + + +```bash title="Terminal" +❯ cd ~/Library/Application\ Support/com.gitbutler.app + +❯ tree +. +├── keys +│ ├── ed25519 +│ └── ed25519.pub +├── projects.json +└── settings.json + +4 directories, 4 files +``` + + +The `projects.json` file will have a list of your projects metadata: + + +```bash title="Terminal" +❯ cat projects.json +[ + { + "id": "71218b1b-ee2e-4e0f-8393-54f467cd665b", + "title": "gitbutler-blog", + "description": null, + "path": "/Users/scottchacon/projects/gitbutler-blog", + "preferred_key": "generated", + "ok_with_force_push": true, + "api": null, + "gitbutler_data_last_fetch": null, + "gitbutler_code_push_state": null, + "project_data_last_fetch": { + "fetched": { + "timestamp": { + "secs_since_epoch": 1706619724, + "nanos_since_epoch": 202467000 + } + } + } + } +] +``` + + +The `settings.json` are some top level preferences you've set. + + +```bash title="Terminal" +❯ cat settings.json +{ + "appAnalyticsConfirmed": true, + "appNonAnonMetricsEnabled": true +} +``` + + +Finally, the `keys` directory holds the SSH key that we generate for you in case you don't want to go through creating your own. It's only used if you want to use it to sign commits or use it for authentication. + +## Linux + +### `glibc` Errors + +The Linux installation is currently being built in a GitHub Action with Ubuntu 24.04. This means support is limited to those installations using the same or newer version of `glibc`. Unfortunately we cannot build using earlier versions of Ubuntu due to another incompatibility with `libwebkit2gtk-4.1` and Tauri at the moment. + +If you're using an older distribution, you may be interested in trying our Flatpak package available on Flathub. + +### `Failed to create EGL image from DMABuf` + +If you start GitButler from the command line and see a bunch of these or similar `EGL` / `DMABuf` related messages printed to the console and are only getting a white screen to render, you can try launching GitButler with the following environment variables: + +- `WEBKIT_DISABLE_DMABUF_RENDERER=1` +- `WEBKIT_DISABLE_COMPOSITING_MODE=1` + +This issue most likely stems from an incompatibility between your version of OpenGL (`mesa`) and `libwebkit2gtk-4.1`. + + +# upstream-integration.mdx + +Sometimes you work on a branch and someone else pushes to the same upstream branch. Often you won't know this until you try to push and Git tells you something like this: + + + +In this scenario, GitButler gives you some nice tooling to help you know when this happens as early as possible and help you deal with it easily. + +If someone else has pushed to a branch that you're working on, you will see the upstream commits without having to integrate them. You can look at the commits without having to merge them into your branch or rebase your work on top of them. + + + +When you decide that you do want to integrate the changes, you have two options - rebase or interactively integrate. + +## Rebase the changes + +If you select "Rebase upstream changes", it will do the equivalent of a `git pull --rebase` which rebases the commits you have locally on top of the ones that the other person has pushed, so you end up with a state like this: + + + +Now you can push your commit back upstream without a force push. Easy peasy. + +## Interactively integrate the changes + +However, let's say that you want to do something more complex. Maybe the other implemented the same thing that you did and you want to drop one of them or one of yours, or squash commits together or reorder them. In any of these cases, you can choose the "Interactive integration" option and you get something that looks like this: + + + +Here you can reorder commits however you want, you can choose to skip some of them, you can squash some of them down, etc. Just make the commits look however you prefer and then hit the "Integrate changes" button and push your final result back to the server. + + +# contact-us.mdx + + +There are a few ways to get in touch with us for feedback, bug reports, feature requests, etc. + + + } + href="mailto:hello@gitbutler.com" + title="Email" + description="The simplest way to get in touch with us is to email us" + /> + } + href="https://discord.com/invite/MmFkmaJ42D" + title="Discord" + description="We are also available to chat on our Discord server" + /> + + + +# open-source.mdx + +GitButler is a [Fair Source software project](https://blog.gitbutler.com/gitbutler-is-now-fair-source/), built on a vast foundation of open source software. We're committed to giving back to the community and supporting the projects that we rely on. + +As part of our commitment to open source, we are an early member of the [Open Source Pledge](https://osspledge.com/). We have pledged to give back to the open source community by donating $2000 per year per full time software developer that we employ to the OSS projects and maintainers that we depend on. + +You can read more about our reasoning to join the Open Source Pledge in our announcement blog post and 2024 report: [GitButler Joins the Open Source Pledge](https://blog.gitbutler.com/open-source-pledge-2024). + + +# supporters.mdx + + +Thinking about paying for Beta software? Sounds odd, right? + +No worries, the main stuff in GitButler stays the same whether you pay or not. + +But hey, we're all about building a cool gang here. We want to know who really digs our butler. And those early supporters? They're like VIPs to us. + +## Perks for Early Supporters + +- Access to our Early Bird Discord room, for life +- Invitations to exclusive Berlin parties, when it's warm here +- Care packages of schwag, sent your way +- Pricing locked in, no matter how we decide to charge later +- First look at any new features as we go +- Whatever else we can think of over time + +Your support helps us grow and make GitButler even better. Join us on this adventure! + +## How to Support Us +You need to have a GitButler account to support us. If you don't have one, sign up first. + + + } + href="https://app.gitbutler.com/supporter" + title="GitButler" + description="Support GitButler with a monthly contribution" + /> + + +Thanks, from the GitButler Crew! + + + + + +# but-alias.mdx + +Aliases allow you to create shortcuts for commonly used commands. They are stored in git config under the but.alias.* namespace. + +Examples + +List all configured aliases: + + but alias + +Create a new alias: -GitButler integrates seamlessly with Cursor through hooks that automatically manage your commits and branches while you're using AI coding features. This allows you to automatically maintain clean git history and organized virtual branches. + but alias add st status + but alias add stv "status --verbose" -Here's a short video showing how GitButler works with Cursor hooks: +Remove an alias: -https://youtu.be/NOYK7LTFvZM + but alias remove st -Ok, let's get it set up. +**Usage:** `but alias ` -## Install the GitButler CLI +## Subcommands -First, you need to install the GitButler CLI, which can be done in your General settings. See the [MCP Server documentation](mcp-server) for more details on how to install the CLI. +### `but alias list` -## Installing GitButler as a Hook +List all configured aliases (default) -Once the command line tool is installed, you can add the `but cursor` commands as hooks. +**Usage:** `but alias list` -You will need to create or edit your `~/.cursor/hooks.json` file (globally) or `[project]/.cursor/hooks.json` file (single project) to have `afterFileEdit` and `stop` hooks like this: +### `but alias add` -```json -{ - "version": 1, - "hooks": { - "afterFileEdit": [ - { - "command": "but cursor after-edit" - } - ], - "stop": [ - { - "command": "but cursor stop" - } - ] - } -} -``` +Add a new alias -## Using GitButler with Cursor +Creates a new alias that expands to the given command. -Once the hooks are setup, Cursor will automatically call GitButler when it edits files and when it's done with a task, which will trigger GitButler to: +Examples -- Create a branch if the chat session is new -- Assign edits to an active branch -- Commit with a message based on the prompt when a task is done + but alias add st status + but alias add stv "status --verbose" + but alias add co "commit --only" -# mcp-server.mdx +**Usage:** `but alias add [OPTIONS]` +**Arguments:** -If you use an AI agent (such as Cursor, Windsurf, Claude Code) to help you with your code, you can easily setup GitButler to manage your commits automatically, keep save points, and more. You know, _vibe_ commit... +* `` — The name of the alias to create (required) +* `` — The command and arguments that the alias should expand to -## Setting up your Agent to use GitButler +If the value contains spaces or special characters, quote it: "status --verbose" (required) -The first step is to let your agent know about GitButler, which is done via MCP - you need to tell your agent to use the GitButler MCP server. +**Options:** -### Installing the CLI +* `-g`, `--global` — Store the alias globally (in ~/.gitconfig) instead of locally -GitButler provides a CLI that can be used to interact with the GitButler platform. Before you can setup AI Agent integration, you will need to install the CLI. +### `but alias remove` -This can be found by opening the GitButler global settings, and then clicking on the "Install CLI" button in the General settings. +Remove an existing alias - +Examples -Now that you have the `but` CLI installed, your agent can use the CLI's MCP server to interact with GitButler. + but alias remove st + but alias remove co --global -### Cursor -To install the GitButler MCP server in Cursor, first go to the Cursor settings, and then click on the "Extensions" tab, then click on "Tools and Integrations" and click on "New MCP Server". +**Usage:** `but alias remove [OPTIONS]` -This will open your `~/.cursor/mcp.json` file. +**Arguments:** -Add the following to the `mcpServers` object: +* `` — The name of the alias to remove (required) -```json -{ - "mcpServers": { - "gitbutler": { - "command": "but", - "args": ["mcp"] - } - } -} -``` +**Options:** -You should see the GitButler MCP server in the list of MCP servers and it should have the tool `gitbutler_update_branches` available. +* `-g`, `--global` — Remove from global config (in ~/.gitconfig) instead of local -### VSCode +## Platform Options -To install the GitButler MCP server in VSCode, you need to select "MCP: List Servers" from the actions menu. Then select "Add Server". Select "stdio" as the server type. +These options are available for all `but` commands: -Now you can type your command (`but mcp`) and name it something. After this, it should open up your settings file and show you something like this: +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -```json - "mcp": { - "servers": { - Running | Stop | Restart | 1 tools - "gitbutler-mcp": { - "type": "stdio", - "command": "but", - "args": ["mcp"] - } - } - } -``` +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -However, if you have Cursor's MCP already setup, VSCode will notice and help you automatically reuse the settings. - -### Claude Code +# but-amend.mdx -Adding an MCP server to Claude Code is done by running the `claude mcp add` command. +Wrapper for `but rub `. -``` -❯ claude mcp add gitbutler but mcp -Added stdio MCP server gitbutler with command: but mcp to local config +**Usage:** `but amend ` -❯ claude mcp list -gitbutler: but mcp -``` +## Arguments -## Rules: How to configure auto committing +* `` — File ID to amend (required) +* `` — Commit ID to amend into (required) -Once you have installed the MCP server in your editor or agent, you can optionally configure it to automatically commit your changes. +## Platform Options -We've found that adding something like this to your rules works well: +These options are available for all `but` commands: -``` -If you generate code or modify files, run the gitbutler update branches MCP tool. -``` +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -## How to add rules +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -Cursor stores its rules in `~/.cursor/rules` file, but you can also manually set them by going to the Cursor Settings pane, clicking 'Rules' and adding them to the User Rules section. -In VSCode's Copilot Agent Mode, you can use ["custom instructions"](https://code.visualstudio.com/docs/copilot/copilot-customization#_custom-instructions) to accomplish this. -In Claude Code, they are now called "memories" and you can add them by hitting '#' and storing them in user memory (or local if you just want them in one project). +# but-branch.mdx - +This includes creating, deleting, listing, showing details about, and applying and unapplying branches. -Or directly in your `~/.claude/CLAUDE.md` rules file: +By default without a subcommand, it will list the branches. -``` -❯ cat ~/.claude/CLAUDE.md -## Development Workflow -- When you're done with a task where code was created or files edited, please run the gitbutler mcp update_branches command. -``` +**Usage:** `but branch ` -## Using GitButler with your agent +## Subcommands -If you've set up a rule/instruction/memory, then every time a chat session is completed, the agent will send the changes and prompt to GitButler and it will automatically commit the changes. +### `but branch new` - +Creates a new branch in the workspace -If you're using Claude Code, it may look something like this: +If no branch name is provided, a new parallel branch with a generated +name will be created. - +You can also specify an anchor point using the `--anchor` option, +which can be either a commit ID or an existing branch name to create +the new branch from. This allows you to create stacked branches. -If you don't have the agent setup to automatically call our tool, then you can also just manually type 'update gitbutler branches' in the chat, but that's a little less magical. +**Usage:** `but branch new [BRANCH_NAME] [OPTIONS]` -## GitButler interface +**Arguments:** -There are two phases to GitButler's MCP agent interaction. The first is the agent sending the changes and prompt to GitButler, which GitButler will quickly record and then return a success to the agent. The second is GitButler processing that raw recorded change and attempting to process that change into a commit. +* `` — Name of the new branch -### Recording the changes +**Options:** -When your agent calls the `gitbutler_update_branches` tool, GitButler will record the changes and prompt and then immediately return to the agent, so the call should be very fast. +* `-a`, `--anchor` `` — Anchor point - either a commit ID or branch name to create the new branch from -So for instance, let's say that I prompted my coding agent to update my `README.md` file to add a list of contributing authors. When the agent is done, it should call the update branches MCP tool, which will record a commit that looks something like this: +### `but branch delete` - +Deletes a branch from the workspace -### Processing the changes +This will remove the branch and all its commits from the workspace. +If the branch has unpushed commits, you will be prompted for confirmation +unless the `--force` flag is used. -Then, if you have AI tooling setup, GitButler will see that and turn it into a commit message like this: +**Usage:** `but branch delete [OPTIONS]` - +**Arguments:** -You can see all of these steps in the "Actions" section of the GitButler interface, which you can toggle by hitting the "Actions" button in the top right of the interface. +* `` — Name of the branch to delete (required) - +**Options:** -In the near future, we will also be able to do more interesting things like auto-absorbing changes into existing commits, creating new branches based on the prompt theme, creating stacked branches, and more. +* `-f`, `--force` — Force deletion without confirmation +### `but branch list` -# debugging.mdx +List the branches in the repository +By default, shows the active branch and the 20 most recently updated branches. -If you are having technical issues with the GitButler client, here are a few things you can do to help us help you. Or help yourself. +You can use the `--all` flag to show all branches, `--local` to show only +local branches, or `--remote` to show only remote branches. -If you get stuck or need help with anything, hit us up over on Discord, here's [GitButler Discord Server Link](https://discord.gg/MmFkmaJ42D). +You can also filter branch names by specifying a substring, such as +`but branch list feature` to show only branches with "feature" in the name. - +If you want to check for review status, you can add `--review` to fetch +and display pull request or merge request information for each branch. +This will make the command slower as it needs to query the forge. -The first things to try is checking out the frontend related logs in the console by opening the developer tools in GitButler via the "View" -> "Developer Tools" menu option. Next, if you launch GitButler from the command line, you can view the backend logs directly in your terminal. +By default, the command checks if each branch merges cleanly into +the *upstream base target branch* (not your workspace). +You can disable this check with `--no-check` to make the command faster. -## Logs +By default it also calculates the number of commits each branch is ahead +of the base branch. You can disable this with `--no-ahead` to +make the command faster. -Often the most helpful thing is to look at the logs. GitButler is a Tauri app, so the logs are in your OS's [app log directory](https://docs.rs/tauri/1.8.1/tauri/api/path/fn.app_log_dir.html). This should be: +**Usage:** `but branch list [FILTER] [OPTIONS]` - - - ```bash - ~/Library/Logs/com.gitbutler.app/ - ``` - - - ```bash - C:\Users\[username]\AppData\Local\com.gitbutler.app\logs - ``` - - - ```bash - ~/.config/gitbutler/logs/ [OR] - ~/.local/share/gitbutler-tauri/logs/ - ``` - - +**Arguments:** -In this directory, there should be rolling daily logs: +* `` — Filter branches by name (case-insensitive substring match) +**Options:** -```bash title="Terminal" -❯ cd ~/Library/Logs/com.gitbutler.app +* `-l`, `--local` — Show only local branches +* `-r`, `--remote` — Show only remote branches +* `-a`, `--all` — Show all branches (not just active + 20 most recent) +* `--no-ahead` — Don't calculate and show number of commits ahead of base (faster) +* `--review` — Fetch and display review information (PRs, MRs, etc.) +* `--no-check` — Don't check if each branch merges cleanly into upstream -❯ tree -L 1 +### `but branch show` -├── GitButler.log -├── GitButler.log.2023-09-02 -├── GitButler.log.2023-09-03 -├── GitButler.log.2023-09-04 -├── GitButler.log.2023-09-05 -├── GitButler.log.2023-09-06 -├── GitButler.log.2023-09-07 -├── GitButler.log.2023-09-08 -├── GitButler.log.2023-10-10 -├── GitButler.log.2024-01-30 -└── tokio-console +Show commits ahead of base for a specific branch -❯ tail GitButler.log.2024-01-30 -2024-01-30T13:02:56.319843Z INFO get_public_key: gitbutler-app/src/keys/commands.rs:20: new -2024-01-30T13:02:56.320000Z INFO git_get_global_config: gitbutler-app/src/commands.rs:116: new key="gitbutler.utmostDiscretion" -2024-01-30T13:02:56.320117Z INFO git_get_global_config: gitbutler-app/src/commands.rs:116: new key="gitbutler.signCommits" -2024-01-30T13:02:56.320194Z INFO get_public_key: gitbutler-app/src/keys/commands.rs:20: close time.busy=317µs time.idle=47.0µs -2024-01-30T13:02:56.320224Z INFO git_get_global_config: gitbutler-app/src/commands.rs:116: close time.busy=204µs time.idle=25.3µs key="gitbutler.utmostDiscretion" -2024-01-30T13:02:56.320276Z INFO git_get_global_config: gitbutler-app/src/commands.rs:116: close time.busy=133µs time.idle=35.8µs key="gitbutler.signCommits" -2024-01-30T13:02:56.343467Z INFO menu_item_set_enabled: gitbutler-app/src/menu.rs:11: new menu_item_id="project/settings" enabled=false -2024-01-30T13:02:56.343524Z INFO menu_item_set_enabled: gitbutler-app/src/menu.rs:11: close time.busy=35.7µs time.idle=28.8µs menu_item_id="project/settings" enabled=false -``` +This shows the list of commits that are on the specified branch but not yet integrated into the base target branch. +You can also choose to fetch and display review information, show files modified in each commit with line counts, generate an AI summary of the branch changes, and check if the branch merges cleanly into upstream. -## Data Files +**Usage:** `but branch show [OPTIONS]` -GitButler also keeps its own data about each of your projects. The virtual branch metadata, your user config stuff, a log of changes in each file, etc. If you want to inspect what GitButler is doing or debug or reset everything, you can go to our data directory. +**Arguments:** - - - ```bash - ~/Library/Application Support/com.gitbutler.app/ - ``` - - - ```bash - C:\Users\[username]\AppData\Roaming\com.gitbutler.app - ``` - - - ```bash - ~/.local/share/gitbutler-tauri/ - ``` - - +* `` — CLI ID or name of the branch to show (required) -In this folder there are a bunch of interesting things. +**Options:** +* `-r`, `--review` — Fetch and display review information +* `-f`, `--files` — Show files modified in each commit with line counts +* `--ai` — Generate AI summary of the branch changes +* `--check` — Check if the branch merges cleanly into upstream and identify conflicting commits -```bash title="Terminal" -❯ cd ~/Library/Application\ Support/com.gitbutler.app +### `but branch apply` -❯ tree -. -├── keys -│ ├── ed25519 -│ └── ed25519.pub -├── projects.json -└── settings.json +Apply a branch to the workspace -4 directories, 4 files -``` +If you want to apply an unapplied branch to your workspace so you +can work on it, you can run `but branch apply `. +This will apply the changes in that branch into your working directory +as a parallel applied branch. -The `projects.json` file will have a list of your projects metadata: +**Usage:** `but branch apply ` +**Arguments:** -```bash title="Terminal" -❯ cat projects.json -[ - { - "id": "71218b1b-ee2e-4e0f-8393-54f467cd665b", - "title": "gitbutler-blog", - "description": null, - "path": "/Users/scottchacon/projects/gitbutler-blog", - "preferred_key": "generated", - "ok_with_force_push": true, - "api": null, - "gitbutler_data_last_fetch": null, - "gitbutler_code_push_state": null, - "project_data_last_fetch": { - "fetched": { - "timestamp": { - "secs_since_epoch": 1706619724, - "nanos_since_epoch": 202467000 - } - } - } - } -] -``` +* `` — Name of the branch to apply (required) +### `but branch unapply` -The `settings.json` are some top level preferences you've set. +Unapply a branch from the workspace +If you want to unapply an applied branch from your workspace +(effectively stashing it) so you can work on other branches, +you can run `but branch unapply `. -```bash title="Terminal" -❯ cat settings.json -{ - "appAnalyticsConfirmed": true, - "appNonAnonMetricsEnabled": true -} -``` +This will remove the changes in that branch from your working +directory and you can re-apply it later when needed. You will then +see the branch as unapplied in `but branch list`. +**Usage:** `but branch unapply [OPTIONS]` -Finally, the `keys` directory holds the SSH key that we generate for you in case you don't want to go through creating your own. It's only used if you want to use it to sign commits or use it for authentication. +**Arguments:** -## Linux +* `` — Name of the branch to unapply (required) -### `glibc` Errors +**Options:** -The Linux installation is currently being built in a GitHub Action with Ubuntu 24.04. This means support is limited to those installations using the same or newer version of `glibc`. Unfortunately we cannot build using earlier versions of Ubuntu due to another incompatibility with `libwebkit2gtk-4.1` and Tauri at the moment. +* `-f`, `--force` — Force unapply without confirmation -If you're using an older distribution, you may be interested in trying our Flatpak package available on Flathub. +## Platform Options -### `Failed to create EGL image from DMABuf` +These options are available for all `but` commands: -If you start GitButler from the command line and see a bunch of these or similar `EGL` / `DMABuf` related messages printed to the console and are only getting a white screen to render, you can try launching GitButler with the following environment variables: +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -- `WEBKIT_DISABLE_DMABUF_RENDERER=1` -- `WEBKIT_DISABLE_COMPOSITING_MODE=1` +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -This issue most likely stems from an incompatibility between your version of OpenGL (`mesa`) and `libwebkit2gtk-4.1`. # but-absorb.mdx -# Amends changes into the appropriate commits where they belong. - -Amends changes into the appropriate commits where they belong. - The semantic for finding "the appropriate commit" is as follows: -- Changes are amended into the topmost commit of the leftmost (first) lane (branch) +- If a change has a dependency to a particular commit, it will be amended into that particular commit - If a change is staged to a particular lane (branch), it will be amended into a commit there - If there are no commits in this branch, a new commit is created -- If a change has a dependency to a particular commit, it will be amended into that particular commit +- Changes are amended into the topmost commit of the leftmost (first) lane (branch) Optionally an identifier to an Uncommitted File or a Branch (stack) may be provided. @@ -2967,6 +3626,8 @@ Optionally an identifier to an Uncommitted File or a Branch (stack) may be provi If --dry-run is specified, no changes will be made; instead, the absorption plan (what changes would be absorbed by which commits) will be shown. +If --new is specified, new commits will be created for absorbed changes instead of amending existing commits. + **Usage:** `but absorb [SOURCE] [OPTIONS]` ## Arguments @@ -2976,949 +3637,1018 @@ If --dry-run is specified, no changes will be made; instead, the absorption plan ## Options * `--dry-run` — Show the absorption plan without making any changes +* `-n`, `--new` — Create new commits, instead of amending existing ones. This is useful when you want to preserve existing commits and add new ones for the absorbed changes +## Platform Options +These options are available for all `but` commands: -# contact-us.mdx +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -There are a few ways to get in touch with us for feedback, bug reports, feature requests, etc. - - } - href="mailto:hello@gitbutler.com" - title="Email" - description="The simplest way to get in touch with us is to email us" - /> - } - href="https://discord.com/invite/MmFkmaJ42D" - title="Discord" - description="We are also available to chat on our Discord server" - /> - +# but-config.mdx -# but-amend.mdx +Without a subcommand, displays an overview of important settings including user information, target branch, forge configuration, and AI setup. -# Amend a file change into a specific commit and rebases any dependent commits. +Examples -Amend a file change into a specific commit and rebases any dependent commits. +View configuration overview: -Wrapper for `but rub `. + but config -**Usage:** `but amend FILE COMMIT` +View/set user configuration: -## Arguments + but config user + but config user set name "John Doe" + but config user set email john@example.com -* `FILE` — File ID to amend (required) -* `COMMIT` — Commit ID to amend into (required) +View/set forge configuration: + but config forge +View/set target branch: -# supporters.mdx + but config target +View/set metrics: -Thinking about paying for Beta software? Sounds odd, right? + but config metrics -No worries, the main stuff in GitButler stays the same whether you pay or not. +**Usage:** `but config ` -But hey, we're all about building a cool gang here. We want to know who really digs our butler. And those early supporters? They're like VIPs to us. +## Subcommands -## Perks for Early Supporters +### `but config user` -- Access to our Early Bird Discord room, for life -- Invitations to exclusive Berlin parties, when it's warm here -- Care packages of schwag, sent your way -- Pricing locked in, no matter how we decide to charge later -- First look at any new features as we go -- Whatever else we can think of over time +View and configure user information (name, email, editor). -Your support helps us grow and make GitButler even better. Join us on this adventure! +Without arguments, displays current user.name, user.email, and core.editor. Use subcommands to set or unset configuration values. -## How to Support Us -You need to have a GitButler account to support us. If you don't have one, sign up first. +Examples - - } - href="https://app.gitbutler.com/supporter" - title="GitButler" - description="Support GitButler with a monthly contribution" - /> - +View user configuration: -Thanks, from the GitButler Crew! + but config user - +Set user name (locally): + + but config user set name "John Doe" +Set user email globally: + but config user set --global email john@example.com -# but-alias.mdx +Unset a local value: -# Manage command aliases. + but config user unset name -Manage command aliases. -Aliases allow you to create shortcuts for commonly used commands. They are stored in git config under the but.alias.* namespace. +**Usage:** `but config user` + +### `but config forge` + +View and manage forge configuration. + +Shows configured forge accounts (GitHub, GitLab, etc.) and authentication status. Use subcommands to authenticate or forget accounts. Examples -List all configured aliases: +View configured forge accounts: - but alias + but config forge -Create a new alias: +Authenticate with a forge: - but alias add st status - but alias add stv "status --verbose" + but config forge auth -Remove an alias: +List authenticated accounts: - but alias remove st + but config forge list-users + +Forget an account: + + but config forge forget username + + +**Usage:** `but config forge` + +### `but config target` + +View or set the target branch. + +Without arguments, displays the current target branch. With a branch name, sets the target branch. + +Examples + +View current target: + + but config target + +Set target branch: + + but config target origin/main + + +**Usage:** `but config target [BRANCH]` + +**Arguments:** + +* `` — New target branch to set (e.g., "origin/main") + +### `but config metrics` + +View or set metrics collection. + +GitButler uses metrics to help us know what is useful and improve it. Privacy policy: https://gitbutler.com/privacy + +Without arguments, displays the current setting. + +Examples + +View metrics configuration: + + but config metrics + +Enable metrics: + + but config metrics enable + +Disable metrics: + but config metrics disable -**Usage:** `but alias ` -## Subcommands +**Usage:** `but config metrics [STATUS]` -### `but alias list` +**Arguments:** -List all configured aliases (default) +* `` — Whether metrics are enabled -**Usage:** `but alias list` +## Platform Options -### `but alias add` +These options are available for all `but` commands: -Add a new alias +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -Creates a new alias that expands to the given command. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -Examples - but alias add st status - but alias add stv "status --verbose" - but alias add co "commit --only" +# but-commit.mdx -**Usage:** `but alias add [OPTIONS]` +The `but commit` command allows you to create a new commit +on a specified branch (stack) with the current uncommitted changes. -**Arguments:** +If there is only one branch applied, it will commit to that branch by default. -* `` — The name of the alias to create (required) -* `` — The command and arguments that the alias should expand to +If there are multiple branches applied, you must specify which branch to +commit to, or if in interactive mode, you will be prompted to select one. -If the value contains spaces or special characters, quote it: "status --verbose" (required) +By default, all uncommitted changes and all changes already staged to that +branch will be included in the commit. If you only want to commit the changes +that are already staged to that branch, you can use the `--only` flag. -**Options:** +It will not commit changes staged to other branches. -* `-g`, `--global` — Store the alias globally (in ~/.gitconfig) instead of locally +Use `but commit empty --before ` or `but commit empty --after ` +to insert a blank commit. This is useful for creating a placeholder +commit that you can amend changes into later using `but mark`, `but rub` or `but absorb`. -### `but alias remove` +**Usage:** `but commit [BRANCH] [OPTIONS]` -Remove an existing alias +## Subcommands -Examples +### `but commit empty` - but alias remove st - but alias remove co --global +Insert a blank commit before or after the specified commit. +This is useful for creating a placeholder commit that you can +then amend changes into later using `but mark`, `but rub` or `but absorb`. -**Usage:** `but alias remove [OPTIONS]` +You can modify the empty commit message at any time using `but reword`. -**Arguments:** +This allows for a more Jujutsu style workflow where you create commits +first and then fill them in as you work. Create an empty commit, mark it +for auto-commit, and then just work on your changes. Write the commit +message whenever you prefer. -* `` — The name of the alias to remove (required) +## Examples -**Options:** +Insert at the top of the first branch (no arguments): -* `-g`, `--global` — Remove from global config (in ~/.gitconfig) instead of local +```text +but commit empty +``` +Insert before a commit: +```text +but commit empty ab +``` -# rules.mdx +Explicitly insert before a commit: +```text +but commit empty --before ab +``` -Rules are a powerful automation feature in GitButler that automatically assign file changes to specific branches based on conditions you define. Instead of manually dragging changes between lanes, you can set up rules that automatically route changes where they belong. +Insert after a commit (at the top of the stack if target is a branch): -## Overview +```text +but commit empty --after ab +``` -When you're working on multiple branches simultaneously, you often know in advance which types of changes should go to which branch. For example, documentation updates might always go to a `docs` branch, while UI changes go to a `feature/ui-redesign` branch. +**Usage:** `but commit empty [TARGET] [OPTIONS]` -Rules eliminate the manual work of assigning changes by automatically evaluating your uncommitted changes and routing them to the appropriate branch based on filters you define. +**Arguments:** -## How Rules Work +* `` — The target commit or branch to insert relative to. -Rules are evaluated whenever files change in your working directory (the `fileSytemChange` trigger). Each rule consists of: +If a target is provided without --before or --after, defaults to --before behavior. If no arguments are provided at all, inserts at the top of the first branch. -1. **Filters**: Conditions that determine which changes the rule applies to -2. **Action**: Assigns matching changes to a specific branch +**Options:** -Multiple rules can exist, and they are evaluated in order. Within a single rule, multiple filters are combined with AND logic - all conditions must match for the rule to apply. +* `--before` `` — Insert the blank commit before this commit or branch +* `--after` `` — Insert the blank commit after this commit or branch -## Creating a Rule +## Arguments -To create a rule: +* `` — Branch CLI ID or name to derive the stack to commit to -1. Open the **Rules** drawer at the bottom of the GitButler interface -2. Click the **+** button to add a new rule -3. Select the target branch where matching changes should be assigned: - - Specify a branch by name - - **Leftmost lane**: The leftmost branch in your workspace - - **Rightmost lane**: The rightmost branch in your workspace -4. (Optional) Add filters to specify which changes should match -5. Click **Save rule** +## Options -If you don't add any filters, the rule will match all changes. +* `-m`, `--message` `` — Commit message +* `-f`, `--file` `` — Read commit message from file +* `-c`, `--create` — Whether to create a new branch for this commit. If the branch name given matches an existing branch, that branch will be used instead. If no branch name is given, a new branch with a generated name will be created +* `-o`, `--only` — Only commit staged files, not unstaged files +* `-n`, `--no-hooks` — Bypass pre-commit hooks +* `-i`, `--ai` `` — Generate commit message using AI with optional user summary +* `-F`, `--files` `` — Uncommitted file or hunk CLI IDs to include in the commit. Can be specified multiple times or as comma-separated values. If not specified, all uncommitted changes (or changes staged to the target branch) are committed - -

    - Creating a rule that assigns changes to the leftmost lane -

    +## Platform Options -## Filter Types +These options are available for all `but` commands: -Rules support several types of filters to match changes: +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-j`, `--json` — Whether to use JSON output format -### Path Matches Regex -Matches file paths using a regular expression pattern. -**Example use cases:** +# but-diff.mdx -- Match all TypeScript files: `.*\.ts$` -- Match files in a specific directory: `^src/components/.*` -- Match documentation files: `.*\.(md|mdx)$` +Without any arguments, it shows the diff of all uncommitted changes. Optionally, a CLI ID argument can be provided, which chan show the diff specific to -### Content Matches Regex +- an uncommitted file +- a branch +- an entire stack +- a commit +- a file change within a commit -Matches the content of changed lines using a regular expression pattern. This filter only looks at added lines (lines that start with `+` in the diff). +**Usage:** `but diff [TARGET]` -**Example use cases:** +## Arguments -- Match changes containing TODOs: `TODO` -- Match changes with specific function calls: `console\.log` -- Match changes with certain patterns: `@deprecated` +* `` — The CLI ID of the entity to show the diff for - -

    - Creating a rule that assigns changes containing "fix" to a specific branch -

    +## Platform Options -### Claude Code Session ID +These options are available for all `but` commands: -Matches changes that originated from a specific Claude Code session. This filter is automatically used when GitButler's AI features create branches and rules for you. +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -**Note**: Rules with Claude Code Session ID filters have lower priority than manually created rules. If a change matches both an AI-generated rule and a manual rule, the manual rule takes precedence. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format - -

    - AI-generated rules based on Claude Code sessions -

    -## Managing Rules -### Editing Rules +# but-gui.mdx -To edit an existing rule: +Running but gui will launch the GitButler graphical user interface in the current directory's GitButler project. -1. Double-click the rule or click the elepsis menu (...) and select "Edit rule" -2. Modify the branch assignment or filters -3. Click **Save rule** +This provides a visual way to manage branches, commits, and uncommitted changes, complementing the command-line interface. -### Deleting Rules +You can also just run but . as a shorthand to open the GUI. -To delete a rule: +**Usage:** `but gui` -1. Click the elepsis menu (...) on the rule -2. Select "Delete rule" -3. Confirm the deletion +## Platform Options -**Note**: AI-generated rules and implicit rules cannot be edited. +These options are available for all `but` commands: -## Understanding Rule Evaluation +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -### Order Matters +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -Rules are evaluated in the order they appear in the Rules drawer (most recent first). The first matching rule determines where a change is assigned. -### AND Logic Within Rules -When a rule has multiple filters, **all filters must match** for the rule to apply. For example, a rule with both "Path Matches Regex: `.*\.ts$`" and "Content Matches Regex: `TODO`" will only match TypeScript files that contain the text "TODO" in their changes. +# but-merge.mdx -### OR Logic Across Rules +If the target branch is local (`gb-local`), finds the local branch that the target +references (e.g., `gb-local/master` becomes `master`) and merges the specified +branch into that local branch. After merging, runs the equivalent of `but pull` +to update all branches. -If you want to match changes that meet any of several conditions (OR logic), create separate rules for each condition. +## Examples -### Interaction with Hunk Dependencies +Merge a branch by its CLI ID: -Rules respect hunk dependencies (locks). If a change depends on a commit in a specific branch, it cannot be automatically reassigned by rules, even if it matches a rule's filters. +```text +but merge bu +``` -## Best Practices +Merge a branch by name: -1. **Start simple**: Begin with one or two basic path-matching rules before adding complex filters -2. **Order your rules**: Place more specific rules before general catch-all rules -3. **Test your regex**: Make sure your regular expressions match what you intend - it's easy to be too broad or too narrow -4. **Use catch-all rules carefully**: A rule with no filters will match everything, which can interfere with other rules -5. **Consider your workflow**: Rules work best when you have predictable patterns in how your work is organized -6. **Leverage leftmost/rightmost**: Using position-based targeting lets you reorganize lanes without updating rules +```text +but merge my-feature-branch +``` -## Limitations +**Usage:** `but merge ` -- Rules can only assign changes to branches that exist in your workspace (applied branches) -- Implicit (AI-determined) rules cannot be edited through the UI -- Rules currently only support the `assign` action for filesystem changes +## Arguments -## Related Features +* `` — Branch ID or name to merge (required) -- [Virtual Branches](/docs/features/branch-management/virtual-branches): Understanding the branch system that rules work with -- [Branch Lanes](/docs/features/branch-management/branch-lanes): How lanes are organized and how rules interact with lane positioning -- [AI Assistance](/docs/features/branch-management/ai-assistance): How AI can create and manage rules automatically +## Platform Options +These options are available for all `but` commands: -# but-apply.mdx +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -# Apply a branch to the workspace. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -Apply a branch to the workspace. -If you want to apply an unapplied branch to your workspace so you can work on it, you can run but apply ``. -This will apply the changes in that branch into your working directory as a parallel applied branch. +# but-mark.mdx -You can specify the branch by: +Creates or removes a rule for auto-staging or auto-committing changes to the specified target entity. -- CLI ID from but status (e.g., bu) -- Short ID from but branch list (e.g., 00) -- Full branch name (e.g., feature-branch) +If you mark a branch, new unstaged changes that GitButler sees when you run any command will be automatically staged to that branch. -If no branch is specified, an interactive list will be shown to choose from. +If you mark a commit, new uncommitted changes will automatically be amended into the marked commit. -**Usage:** `but apply [BRANCH]` +**Usage:** `but mark [OPTIONS]` ## Arguments -* `` — Branch to apply (CLI ID, short ID from list, or full name) +* `` — The target entity that will be marked (required) +## Options +* `-d`, `--delete` — Deletes a mark -# merging.mdx +## Platform Options +These options are available for all `but` commands: -By default, GitButler rebases the work on your virtual branches when you update your target branch (upstream) work. +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -Often this works just fine and the commits are simply rebased. Occasionally, you will have conflicts with upstream work. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -In this case, GitButler will not do what Git normally does, which is to stop at each conflicted commit and make you fix it before moving on. Instead, it will apply the changes that it can and store the commit as a "conflicted" commit and continue the rebasing process. -When you go to update from upstream, GitButler will show you all the branches that it will rebase and will let you know if any of them will have conflicts: - +# but-discard.mdx -In this case, when you perform the rebase, that branch will then contain "conflicted" commits. They will be marked in the UI as conflicted and you can click on them to get a "resolve conflict" button to start the resolution process. +This command permanently discards changes to files, restoring them to their state in the HEAD commit. Use this to undo unwanted modifications. - +The ID parameter should be a file ID as shown in but status. You can discard a whole file or specific hunks within a file. -When you click that, GitButler will remove the other virtual branches and other work from your working directory and check out just this commit with its conflict markers. It will show you a special "edit mode" screen, where you are directly editing this commit. +Examples - +Discard all changes to a file: -If you want to cancel this conflict resolution, you can just hit 'Cancel' and it will go back to your normal state. If you have fixed all the issues, you can click "Save and Exit" and it will commit the conflict resolution and if needed, rebase any further commits on that branch on top of your new work. + but discard a1 - +**Usage:** `but discard ` +## Arguments -# upstream-integration.mdx +* `` — The ID of the file or hunk to discard (as shown in but status) (required) -Sometimes you work on a branch and someone else pushes to the same upstream branch. Often you won't know this until you try to push and Git tells you something like this: +## Platform Options - +These options are available for all `but` commands: -In this scenario, GitButler gives you some nice tooling to help you know when this happens as early as possible and help you deal with it easily. +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -If someone else has pushed to a branch that you're working on, you will see the upstream commits without having to integrate them. You can look at the commits without having to merge them into your branch or rebase your work on top of them. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format - -When you decide that you do want to integrate the changes, you have two options - rebase or interactively integrate. -## Rebase the changes +# but-oplog.mdx + +Displays a list of past operations performed in the repository, +including their timestamps and descriptions. + +This allows you to restore to any previous point in the history of the +project. All state is preserved in operations, including uncommitted changes. -If you select "Rebase upstream changes", it will do the equivalent of a `git pull --rebase` which rebases the commits you have locally on top of the ones that the other person has pushed, so you end up with a state like this: +You can use `but oplog restore ` to restore to a specific state. - +By default, shows the last 20 oplog entries (same as `but oplog list`). -Now you can push your commit back upstream without a force push. Easy peasy. +**Usage:** `but oplog ` -## Interactively integrate the changes +## Subcommands -However, let's say that you want to do something more complex. Maybe the other implemented the same thing that you did and you want to drop one of them or one of yours, or squash commits together or reorder them. In any of these cases, you can choose the "Interactive integration" option and you get something that looks like this: +### `but oplog list` - +List operation history. -Here you can reorder commits however you want, you can choose to skip some of them, you can squash some of them down, etc. Just make the commits look however you prefer and then hit the "Integrate changes" button and push your final result back to the server. +Displays a list of past operations performed in the repository, +including their timestamps and descriptions. +This allows you to restore to any previous point in the history of the +project. All state is preserved in operations, including uncommitted changes. -# but-describe.mdx +You can use `but oplog restore ` to restore to a specific state. -Edit the commit message of the specified commit. +**Usage:** `but oplog list [OPTIONS]` -You can easily change the commit message of any of your commits by running but describe `` and providing a new message in the editor. +**Options:** -This will rewrite the commit with the new message and then rebase any dependent commits on top of it. +* `--since` `` — Start from this oplog SHA instead of the head +* `-s`, `--snapshot` — Show only on-demand snapshot entries -You can also use but describe `` to rename the branch. +### `but oplog snapshot` -**Usage:** `but describe ` +Create an on-demand snapshot with optional message. -## Arguments +This allows you to create a named snapshot of the current state, which can be helpful to always be able to return to a known good state. -* `` — Commit ID to edit the message for, or branch ID to rename (required) +You can provide an optional message to describe the snapshot. +**Usage:** `but oplog snapshot [OPTIONS]` +**Options:** -# but-config.mdx +* `-m`, `--message` `` — Message to include with the snapshot -# View and manage GitButler configuration. +### `but oplog restore` -View and manage GitButler configuration. +Restore to a specific oplog snapshot. -Without a subcommand, displays an overview of important settings including user information, target branch, forge configuration, and AI setup. +This command allows you to revert the repository to a previous state +captured in an oplog snapshot. -Examples +You need to provide the SHA of the oplog entry you want to restore to, +which you can find by running `but oplog` or `but oplog list`. -View configuration overview: +**Usage:** `but oplog restore [OPTIONS]` - but config +**Arguments:** -View/set user configuration: +* `` — Oplog SHA to restore to (required) - but config user - but config user set name "John Doe" - but config user set email john@example.com +**Options:** -View/set forge configuration: +* `-f`, `--force` — Skip confirmation prompt - but config forge +## Platform Options -View/set target branch: +These options are available for all `but` commands: - but config target +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -**Usage:** `but config ` -## Subcommands -### `but config user` +# but-move.mdx -View and configure user information (name, email, editor). +By default, commits are moved to be before (below) the target. Use --after to move the commit after (above) the target instead. -Without arguments, displays current user.name, user.email, and core.editor. Use subcommands to set or unset configuration values. +When moving to a branch, the commit is placed at the top of that branch's stack. Examples -View user configuration: +Move a commit before another commit: - but config user + but move abc123 def456 -Set user name (locally): +Move a commit after another commit: - but config user set name "John Doe" + but move abc123 def456 --after -Set user email globally: +Move a commit to a different branch (places at top): - but config user set --global email john@example.com + but move abc123 my-feature-branch -Unset a local value: +**Usage:** `but move [OPTIONS]` - but config user unset name +## Arguments +* `` — Commit ID to move (required) +* `` — Target commit ID or branch name (required) -**Usage:** `but config user` +## Options -### `but config forge` +* `-a`, `--after` — Move the commit after (above) the target instead of before (below) -View and manage forge configuration. +## Platform Options -Shows configured forge accounts (GitHub, GitLab, etc.) and authentication status. Use subcommands to authenticate or forget accounts. +These options are available for all `but` commands: -Examples +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -View configured forge accounts: +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format - but config forge -Authenticate with a forge: - but config forge auth +# but-pull.mdx -List authenticated accounts: +This fetches the latest changes from the remote and rebases all applied branches +on top of the updated target branch. - but config forge list-users +You should run this regularly to keep your branches up to date with the latest +changes from the main development line. -Forget an account: +You can run `but pull --check` first to see if your branches can be cleanly +merged into the target branch before running the update. - but config forge forget username +**Usage:** `but pull [OPTIONS]` +## Options -**Usage:** `but config forge` +* `-c`, `--check` — Only check the status without updating (equivalent to the old but base check) -### `but config target` +## Platform Options -View or set the target branch. +These options are available for all `but` commands: -Without arguments, displays the current target branch. With a branch name, sets the target branch. +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -Examples +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -View current target: - but config target -Set target branch: +# but-pick.mdx - but config target origin/main +This command allows you to pick individual commits from unapplied branches +and apply them to your current workspace branches. +The source can be: +- A commit SHA (full or short) +- A CLI ID (e.g., "c5" from `but status`) +- An unapplied branch name (shows interactive commit selection) -**Usage:** `but config target [BRANCH]` +If no target branch is specified: +- In interactive mode: prompts you to select a target branch +- If only one branch exists: automatically uses that branch +- In non-interactive mode: fails with an error -**Arguments:** +## Examples -* `` — New target branch to set (e.g., "origin/main") +Pick a specific commit into a branch: +```text +but pick abc1234 my-feature +``` +Pick using a CLI ID: -# but-diff.mdx +```text +but pick c5 my-feature +``` -# Displays the diff of changes in the repo. +Interactively select commits from an unapplied branch: -Displays the diff of changes in the repo. +```text +but pick feature-branch +``` -Without any arguments, it shows the diff of all uncommitted changes. Optionally, a CLI ID argument can be provided, which chan show the diff specific to +**Usage:** `but pick [TARGET_BRANCH]` -- an uncommitted file -- a branch -- an entire stack -- a commit -- a file change within a commit +## Arguments -**Usage:** `but diff [TARGET]` +* `` — The commit SHA, CLI ID, or unapplied branch name to cherry-pick from (required) +* `` — The target virtual branch to apply the commit(s) to -## Arguments +## Platform Options -* `` — The CLI ID of the entity to show the diff for +These options are available for all `but` commands: +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -# but-commit.mdx -# Commit changes to a stack. -Commit changes to a stack. +# but-push.mdx -The but commit command allows you to create a new commit on a specified branch (stack) with the current uncommitted changes. +but push will update the remote with the latest commits from the applied branch(es). -If there is only one branch applied, it will commit to that branch by default. +Without a branch ID: -If there are multiple branches applied, you must specify which branch to commit to, or if in interactive mode, you will be prompted to select one. +- Interactive mode: Lists all branches with unpushed commits and prompts for selection +- Non-interactive mode: Automatically pushes all branches with unpushed commits -By default, all uncommitted changes and all changes already staged to that branch will be included in the commit. If you only want to commit the changes that are already staged to that branch, you can use the --only flag. +With a branch ID: -It will not commit changes staged to other branches. +- but push bu - push the branch with CLI ID "bu" +- but push feature-branch - push the branch named "feature-branch" -**Usage:** `but commit [BRANCH] [OPTIONS]` +**Usage:** `but push [BRANCH_ID] [OPTIONS]` ## Arguments -* `` — Branch CLI ID or name to derive the stack to commit to +* `` — Branch name or CLI ID to push. If not specified, will list all branches and prompt for selection in interactive mode ## Options -* `-m`, `--message` `` — Commit message -* `-f`, `--file` `` — Read commit message from file -* `-c`, `--create` — Whether to create a new branch for this commit. If the branch name given matches an existing branch, that branch will be used instead. If no branch name is given, a new branch with a generated name will be created -* `-o`, `--only` — Only commit staged files, not unstaged files +* `-f`, `--with-force` — Force push even if it's not fast-forward (default: `true`) +* `-s`, `--skip-force-push-protection` — Skip force push protection checks +* `-r`, `--run-hooks` — Run pre-push hooks (default: `true`) +* `-d`, `--dry-run` — Show what would be pushed without actually pushing +## Platform Options +These options are available for all `but` commands: -# but-gui.mdx +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-j`, `--json` — Whether to use JSON output format -# Open the GitButler GUI for the current project. -Open the GitButler GUI for the current project. -Running but gui will launch the GitButler graphical user interface in the current directory's GitButler project. +# but-resolve.mdx -This provides a visual way to manage branches, commits, and uncommitted changes, complementing the command-line interface. +When a commit is in a conflicted state (marked with conflicts during rebase), +use this command to enter resolution mode, resolve the conflicts, and finalize. -You can also just run but . as a shorthand to open the GUI. +## Workflow -**Usage:** `but gui` +1. Enter resolution mode: `but resolve ` +2. Resolve conflicts in your editor (remove conflict markers) +3. Check remaining conflicts: `but resolve status` +4. Finalize resolution: `but resolve finish` + Or cancel: `but resolve cancel` +When in resolution mode, `but status` will also show that you're resolving conflicts. +**Usage:** `but resolve [COMMIT]` -# but-discard.mdx +## Subcommands -# Discard uncommitted changes from the worktree. +### `but resolve status` -Discard uncommitted changes from the worktree. +Show the status of conflict resolution, listing remaining conflicted files -This command permanently discards changes to files, restoring them to their state in the HEAD commit. Use this to undo unwanted modifications. +**Usage:** `but resolve status` -The ID parameter should be a file ID as shown in but status. You can discard a whole file or specific hunks within a file. +### `but resolve finish` -Examples +Finalize conflict resolution and return to workspace mode. -Discard all changes to a file: +This commits the resolved changes, rebases any commits on top of the resolved commit, and returns to the normal workspace. - but discard a1 +**Usage:** `but resolve finish` +### `but resolve cancel` -**Usage:** `but discard ` +Cancel conflict resolution and return to workspace mode. + +This discards all changes made during resolution and restores the workspace to its pre-resolution state. + +**Usage:** `but resolve cancel` ## Arguments -* `` — The ID of the file or hunk to discard (as shown in but status) (required) +* `` — Commit ID to enter resolution mode for (when no subcommand is provided) +## Platform Options +These options are available for all `but` commands: -# open-source.mdx +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -GitButler is a [Fair Source software project](https://blog.gitbutler.com/gitbutler-is-now-fair-source/), built on a vast foundation of open source software. We're committed to giving back to the community and supporting the projects that we rely on. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -As part of our commitment to open source, we are an early member of the [Open Source Pledge](https://osspledge.com/). We have pledged to give back to the open source community by donating $2000 per year per full time software developer that we employ to the OSS projects and maintainers that we depend on. -You can read more about our reasoning to join the Open Source Pledge in our announcement blog post and 2024 report: [GitButler Joins the Open Source Pledge](https://blog.gitbutler.com/open-source-pledge-2024). +# but-reword.mdx -# but-base.mdx +You can easily change the commit message of any of your commits by +running `but reword ` and providing a new message in the +editor. -Commands for managing the base target branch. +This will rewrite the commit with the new message and then rebase any +dependent commits on top of it. -Every branch managed by GitButler is based off a common base branch on your remote repository (usually origin/main or origin/master). This is the target branch that all changes will eventually be integrated into. +You can also use `but reword ` to rename the branch. -The base subcommand allows you to manage and update this base branch. +**Usage:** `but reword [OPTIONS]` -When you run but base update, GitButler will fetch the latest changes from the remote and rebase all your applied branches on top of the updated base branch. You will want to do this regularly to keep your branches up to date with the latest changes from the main development line. +## Arguments -You can also use but base check to verify that your branches can be cleanly merged into the base branch without conflicts and see what work is upstream an not yet integrated into your branches. +* `` — Commit ID to edit the message for, or branch ID to rename (required) -**Usage:** `but base ` +## Options -## Subcommands +* `-m`, `--message` `` — The new commit message or branch name. If not provided, opens an editor +* `-f`, `--format` — Format the existing commit message to 72-char line wrapping without opening an editor + +## Platform Options -### `but base check` +These options are available for all `but` commands: -Fetches from the remote and checks the mergeability of the branches in the workspace. +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-j`, `--json` — Whether to use JSON output format -This will see if the target branch has had new work merged into it, and if so, it will check if each branch in the workspace can be cleanly merged into the updated target branch. -It will also show what work is upstream that has not yet been integrated into the branches. -**Usage:** `but base check` +# but-rub.mdx -### `but base update` +The rub command is a simple verb that helps you do a number of editing operations by doing combinations of two things. -Updates all applied branches to be up to date with the target branch +For example, you can "rub" a file onto a branch to stage that file to the branch. You can also "rub" a commit onto another commit to squash them together. You can rub a commit onto a branch to move that commit. You can rub a file from one commit to another. -This fetches the latest changes from the remote and rebases all applied branches on top of the updated target branch. +Non-exhaustive list of operations: -You should run this regularly to keep your branches up to date with the latest changes from the main development line. + │Source │Target + ──────┼───────────┼────── + Amend │File,Branch│Commit + Squash│Commit │Commit + Stage │File,Branch│Branch + Move │Commit │Branch -You can run but base check first to see if your branches can be cleanly merged into the target branch before running the update. +Examples -**Usage:** `but base update` +Squashing two commits into one (combining the commit messages): + but rub 3868155 abe3f53f +Amending a commit with the contents of a modified file: -# but-lazy.mdx + but rub README.md abe3f53f -**Usage:** `but lazy` +Moving a commit from one branch to another: + but rub 3868155 feature-branch +**Usage:** `but rub ` -# but-branch.mdx +## Arguments -# Commands for managing branches. +* `` — The source entity to combine (required) +* `` — The target entity to combine with the source (required) -Commands for managing branches. +## Platform Options -This includes creating, deleting, listing, showing details about, and applying and unapplying branches. +These options are available for all `but` commands: -By default without a subcommand, it will list the branches. +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -**Usage:** `but branch ` +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -## Subcommands -### `but branch new` -Creates a new branch in the workspace +# but-show.mdx -If no branch name is provided, a new parallel branch with a generated name will be created. +When given a commit ID, displays the full commit message, author information, committer information (if different from author), and the list of files modified. -You can also specify an anchor point using the --anchor option, which can be either a commit ID or an existing branch name to create the new branch from. This allows you to create stacked branches. +When given a branch name, displays the branch name and a list of all commits on that branch. Use --verbose to show full commit messages and files changed. -**Usage:** `but branch new [BRANCH_NAME] [OPTIONS]` +Examples -**Arguments:** +Show commit details by short commit ID: -* `` — Name of the new branch + but show a1b2c3d -**Options:** +Show commit details by CLI ID: -* `-a`, `--anchor` `` — Anchor point - either a commit ID or branch name to create the new branch from + but show c5 -### `but branch delete` +Show branch commits by branch name: -Deletes a branch from the workspace + but show my-feature-branch -This will remove the branch and all its commits from the workspace. If the branch has unpushed commits, you will be prompted for confirmation unless the --force flag is used. +Show branch with full commit details: -**Usage:** `but branch delete [OPTIONS]` + but show my-feature-branch --verbose -**Arguments:** +**Usage:** `but show [OPTIONS]` -* `` — Name of the branch to delete (required) +## Arguments -**Options:** +* `` — The commit ID (short or full SHA), branch name, or CLI ID to show details for (required) -* `-f`, `--force` — Force deletion without confirmation +## Options -### `but branch list` +* `-v`, `--verbose` — Show full commit messages and files changed for each commit -List the branches in the repository +## Platform Options -By default, shows the active branch and the 20 most recently updated branches. +These options are available for all `but` commands: -You can use the --all flag to show all branches, --local to show only local branches, or --remote to show only remote branches. +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -You can also filter branch names by specifying a substring, such as but branch list feature to show only branches with "feature" in the name. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -If you want to check for review status, you can add --review to fetch and display pull request or merge request information for each branch. This will make the command slower as it needs to query the forge. -By default, the command checks if each branch merges cleanly into the upstream base target branch (not your workspace). You can disable this check with --no-check to make the command faster. -By default it also calculates the number of commits each branch is ahead of the base branch. You can disable this with --no-ahead to make the command faster. +# but-setup.mdx -**Usage:** `but branch list [FILTER] [OPTIONS]` +This command will: -**Arguments:** +- Add the repository to the global GitButler project registry +- Switch to the gitbutler/workspace branch (if not already on it) +- Set up a default target branch (the remote's HEAD) +- Add a gb-local remote if no push remote exists -* `` — Filter branches by name (case-insensitive substring match) +If you have an existing Git repository and want to start using GitButler with it, you can run this command to set up the necessary configuration and data structures. -**Options:** +Examples -* `-l`, `--local` — Show only local branches -* `-r`, `--remote` — Show only remote branches -* `-a`, `--all` — Show all branches (not just active + 20 most recent) -* `--no-ahead` — Don't calculate and show number of commits ahead of base (faster) -* `--review` — Fetch and display review information (PRs, MRs, etc.) -* `--no-check` — Don't check if each branch merges cleanly into upstream +Initialize a new git repository and set up GitButler: -### `but branch show` + but setup --init -Show commits ahead of base for a specific branch +**Usage:** `but setup [OPTIONS]` -This shows the list of commits that are on the specified branch but not yet integrated into the base target branch. +## Options -You can also choose to fetch and display review information, show files modified in each commit with line counts, generate an AI summary of the branch changes, and check if the branch merges cleanly into upstream. +* `--init` — Initialize a new git repository with an empty commit if one doesn't exist. -**Usage:** `but branch show [OPTIONS]` +This is useful when running in non-interactive environments (like CI/CD) where you want to ensure a git repository exists before setting up GitButler. -**Arguments:** +## Platform Options -* `` — CLI ID or name of the branch to show (required) +These options are available for all `but` commands: -**Options:** +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -* `-r`, `--review` — Fetch and display review information -* `-f`, `--files` — Show files modified in each commit with line counts -* `--ai` — Generate AI summary of the branch changes -* `--check` — Check if the branch merges cleanly into upstream and identify conflicting commits +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -# but-oplog.mdx +# but-skill.mdx -# Commands for viewing and managing operation history. +Skills provide enhanced AI capabilities for working with GitButler through +Claude Code and other AI assistants. -Commands for viewing and managing operation history. +Use `but skill install` to install the GitButler skill files into your +repository or globally. -Displays a list of past operations performed in the repository, including their timestamps and descriptions. +## Examples -This allows you to restore to any previous point in the history of the project. All state is preserved in operations, including uncommitted changes. +Install the skill in the current repository: -You can use but restore `` to restore to a specific state. +```text +but skill install +``` -By default, shows the last 20 oplog entries (same as but oplog list). +Install the skill globally: -**Usage:** `but oplog ` +```text +but skill install --global +``` -## Subcommands +**Usage:** `but skill ` -### `but oplog list` +## Subcommands -List operation history. +### `but skill install` -Displays a list of past operations performed in the repository, including their timestamps and descriptions. +Install the GitButler CLI skill files for Coding agents -This allows you to restore to any previous point in the history of the project. All state is preserved in operations, including uncommitted changes. +By default, installs the skill into the current repository. The command will prompt you to select a skill folder format (Claude Code, OpenCode, Codex, GitHub Copilot, Cursor, Windsurf) unless you specify a custom path with --path. -You can use but restore `` to restore to a specific state. +Use --global to install the skill in a global location instead of the current repository. -**Usage:** `but oplog list [OPTIONS]` +Examples -**Options:** +Install in current repository (prompts for format): -* `--since` `` — Start from this oplog SHA instead of the head -* `-s`, `--snapshot` — Show only on-demand snapshot entries + but skill install -### `but oplog snapshot` +Install globally (prompts for format): -Create an on-demand snapshot with optional message. + but skill install --global -This allows you to create a named snapshot of the current state, which can be helpful to always be able to return to a known good state. +Install to a custom path: -You can provide an optional message to describe the snapshot. + but skill install --path .claude/skills/gitbutler -**Usage:** `but oplog snapshot [OPTIONS]` +Auto-detect installation location (update existing installation): -**Options:** + but skill install --detect -* `-m`, `--message` `` — Message to include with the snapshot +**Usage:** `but skill install [OPTIONS]` +**Options:** -# but-merge.mdx +* `-g`, `--global` — Install the skill globally instead of in the current repository +* `-p`, `--path` `` — Custom path where to install the skill (relative to repository root or absolute) +* `-d`, `--detect` — Automatically detect where to install by finding existing installation -# Merge a branch into your local target branch. +### `but skill check` -Merge a branch into your local target branch. +Check if installed GitButler skills are up to date with the CLI version -If the target branch is local (gb-local), finds the local branch that the target references (e.g., gb-local/master becomes master) and merges the specified branch into that local branch. After merging, runs the equivalent of but pull to update all branches. +Scans for installed skill files and compares their version with the current CLI version. By default, checks both local (repository) and global installations. Examples -Merge a branch by its CLI ID: +Check all installed skills: - but merge bu + but skill check -Merge a branch by name: +Check and automatically update outdated skills: - but merge my-feature-branch + but skill check --update +Check only global installations: -**Usage:** `but merge ` + but skill check --global -## Arguments -* `` — Branch ID or name to merge (required) +**Usage:** `but skill check [OPTIONS]` + +**Options:** + +* `-g`, `--global` — Only check global installations (in home directory) +* `-l`, `--local` — Only check local installations (in current repository) +* `-u`, `--update` — Automatically update any outdated skills found +## Platform Options +These options are available for all `but` commands: -# but-new.mdx +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -# Insert a blank commit before the specified commit, or at the top of a stack. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -Insert a blank commit before the specified commit, or at the top of a stack. -This is useful for creating a placeholder commit that you can then amend changes into later using but mark, but rub or but absorb. -You can modify the empty commit message at any time using but describe. +# but-squash.mdx -This allows for a more Jujutsu style workflow where you create commits first and then fill them in as you work. Create an empty commit, mark it for auto-commit, and then just work on your changes. Write the commit message whenever you prefer. +Can be invoked in three ways: +1. Using commit identifiers: `but squash ` or `but squash ...` + - Squashes all commits except the last into the last commit +2. Using a commit range: `but squash ..` + - Squashes all commits in the range into the last commit in the range +3. Using a branch name: `but squash ` + - Squashes all commits in the branch into the bottom-most commit -**Usage:** `but new ` +**Usage:** `but squash [COMMITS] [OPTIONS]` ## Arguments -* `` — Commit ID to insert before, or branch ID to insert at top of stack (required) +* `` — Commit identifiers, a range (commit1..commit2), or a branch name + +## Options +* `-d`, `--drop-message` — Drop source commit messages and keep only the target commit's message +* `-m`, `--message` `` — Provide a new commit message for the resulting commit +* `-i`, `--ai` `` — Generate commit message using AI with optional user summary or instructions +## Platform Options -# but-pr.mdx +These options are available for all `but` commands: -# Commands for creating and managing pull requests on a forge. +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -Commands for creating and managing pull requests on a forge. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format + + + +# but-pr.mdx If you are authenticated with a forge using but config forge auth, you can use the but pr commands to create pull requests (or merge requests) on the remote repository for your branches. @@ -3940,6 +4670,8 @@ Create a new pull request for a branch. If no branch is specified, you will be p **Options:** +* `-m`, `--message` `` — PR title and description. The first line is the title, the rest is the description +* `-F`, `--file` `` — Read PR title and description from file. The first line is the title, the rest is the description * `-f`, `--with-force` — Force push even if it's not fast-forward (defaults to true) (default: `true`) * `-s`, `--skip-force-push-protection` — Skip force push protection checks * `-r`, `--run-hooks` — Run pre-push hooks (defaults to true) (default: `true`) @@ -3955,879 +4687,1114 @@ Configure the template to use for PR descriptions. This will list all available * `` — Path to the PR template file within the repository +## Platform Options +These options are available for all `but` commands: -# but-pull.mdx +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -# Updates all applied branches to be up to date with the target branch. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -Updates all applied branches to be up to date with the target branch. -This fetches the latest changes from the remote and rebases all applied branches on top of the updated target branch. -You should run this regularly to keep your branches up to date with the latest changes from the main development line. +# but-stage.mdx -You can run but pull --check first to see if your branches can be cleanly merged into the target branch before running the update. +Wrapper for `but rub `. -**Usage:** `but pull [OPTIONS]` +**Usage:** `but stage ` -## Options +## Arguments -* `-c`, `--check` — Only check the status without updating (equivalent to the old but base check) +* `` — File or hunk ID to stage (required) +* `` — Branch ID to stage to (required) +## Platform Options +These options are available for all `but` commands: -# but-resolve.mdx +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -# Resolve conflicts in a commit. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -Resolve conflicts in a commit. -When a commit is in a conflicted state (marked with conflicts during rebase), use this command to enter resolution mode, resolve the conflicts, and finalize. -Workflow +# but-undo.mdx -1. Enter resolution mode: but resolve `` -2. Resolve conflicts in your editor (remove conflict markers) -3. Check remaining conflicts: but resolve status -4. Finalize resolution: but resolve finish Or cancel: but resolve cancel +This is a shorthand for restoring to the last oplog entry before the current one. It allows you to quickly undo the most recent operation. -When in resolution mode, but status will also show that you're resolving conflicts. +**Usage:** `but undo` -**Usage:** `but resolve [COMMIT]` +## Platform Options -## Subcommands +These options are available for all `but` commands: -### `but resolve status` +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -Show the status of conflict resolution, listing remaining conflicted files +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -**Usage:** `but resolve status` -### `but resolve finish` -Finalize conflict resolution and return to workspace mode. +# but-uncommit.mdx -This commits the resolved changes, rebases any commits on top of the resolved commit, and returns to the normal workspace. +Wrapper for `but rub zz`. -**Usage:** `but resolve finish` +**Usage:** `but uncommit ` -### `but resolve cancel` +## Arguments -Cancel conflict resolution and return to workspace mode. +* `` — Commit ID or file-in-commit ID to uncommit (required) -This discards all changes made during resolution and restores the workspace to its pre-resolution state. +## Platform Options -**Usage:** `but resolve cancel` +These options are available for all `but` commands: +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -# but-push.mdx -# Push changes in a branch to remote. -Push changes in a branch to remote. +# but-teardown.mdx -but push will update the remote with the latest commits from the applied branch(es). +This command: -Without a branch ID: +- Creates an oplog snapshot of the current state +- Finds the first active branch and checks it out +- Cherry-picks any dangling commits from gitbutler/workspace +- Provides instructions on how to return to GitButler mode -- Interactive mode: Lists all branches with unpushed commits and prompts for selection -- Non-interactive mode: Automatically pushes all branches with unpushed commits +This is useful when you want to temporarily or permanently leave GitButler management and work with standard Git commands. -With a branch ID: +Examples -- but push bu - push the branch with CLI ID "bu" -- but push feature-branch - push the branch named "feature-branch" +Exit GitButler mode: -**Usage:** `but push [BRANCH_ID] [OPTIONS]` + but teardown -## Arguments +**Usage:** `but teardown` -* `` — Branch name or CLI ID to push. If not specified, will list all branches and prompt for selection in interactive mode +## Platform Options -## Options +These options are available for all `but` commands: -* `-f`, `--with-force` — Force push even if it's not fast-forward (default: `true`) -* `-s`, `--skip-force-push-protection` — Skip force push protection checks -* `-r`, `--run-hooks` — Run pre-push hooks (default: `true`) -* `-d`, `--dry-run` — Show what would be pushed without actually pushing +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -# but-mark.mdx -# Mark a commit or branch for auto-stage or auto-commit. +# but-update.mdx -Mark a commit or branch for auto-stage or auto-commit. +Check for new versions, install updates, or suppress update notifications. -Creates or removes a rule for auto-staging or auto-committing changes to the specified target entity. +**Usage:** `but update ` -If you mark a branch, new unstaged changes that GitButler sees when you run any command will be automatically staged to that branch. +## Subcommands -If you mark a commit, new uncommitted changes will automatically be amended into the marked commit. +### `but update check` -**Usage:** `but mark [OPTIONS]` +Check if a new version of the GitButler CLI is available -## Arguments +**Usage:** `but update check` -* `` — The target entity that will be marked (required) +### `but update suppress` -## Options +Suppress update notifications temporarily -* `-d`, `--delete` — Deletes a mark +Hide update notifications for the specified number of days (1-30). Useful when you want to stay on a specific version temporarily. +**Usage:** `but update suppress [DAYS]` +**Arguments:** -# but-restore.mdx +* `` — Number of days to suppress (1-30, default: 1) -# Restore to a specific oplog snapshot. +### `but update install` -Restore to a specific oplog snapshot. +Install or update the GitButler desktop application (macOS only) -This command allows you to revert the repository to a previous state captured in an oplog snapshot. +Downloads and installs the GitButler desktop app. The CLI (but) is included with the app and will also be updated. -You need to provide the SHA of the oplog entry you want to restore to, which you can find by running but oplog. +By default, auto-detects your current channel (release/nightly) and installs the latest version for that channel. -**Usage:** `but restore [OPTIONS]` +Note: Currently only supported on macOS. For other platforms, download from https://gitbutler.com/downloads -## Arguments +**Usage:** `but update install [TARGET]` -* `` — Oplog SHA to restore to (required) +**Arguments:** + +* `` — What to install: "nightly", "release", or a version like "0.18.7" + +Examples: but update install Auto-detect channel and install latest but update install nightly Install latest nightly build but update install release Install latest stable release but update install 0.18.7 Install specific version + +## Platform Options + +These options are available for all `but` commands: + +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. + +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format + + + +# but-status.mdx + +This shows unstaged files, files staged to stacks, all applied branches (stacked or parallel), commits on each of those branches, upstream commits that are unintegrated, commit status (pushed or local), and base branch information. + +Examples + +Normal usage: + + but status + +Shorthand with listing files modified + + but status -f + +**Usage:** `but status [OPTIONS]` ## Options -* `-f`, `--force` — Skip confirmation prompt +* `-f` — Determines whether the committed files should be shown as well (default: `false`) +* `-v`, `--verbose` — Show verbose output with commit author and timestamp (default: `false`) +* `-r`, `--refresh-prs` — Forces a sync of pull requests from the forge before showing status (default: `false`) +* `-u`, `--upstream` — Show detailed list of upstream commits that haven't been integrated yet (default: `false`) +* `--no-hint` — Disable hints about available commands at the end of output (default: `false`) +## Platform Options +These options are available for all `but` commands: -# but-rub.mdx +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-j`, `--json` — Whether to use JSON output format -# Combines two entities together to perform an operation like amend, squash, stage, or move. -Combines two entities together to perform an operation like amend, squash, stage, or move. -The rub command is a simple verb that helps you do a number of editing operations by doing combinations of two things. +# commands-overview.mdx -For example, you can "rub" a file onto a branch to stage that file to the branch. You can also "rub" a commit onto another commit to squash them together. You can rub a commit onto a branch to move that commit. You can rub a file from one commit to another. +## Command Reference -Non-exhaustive list of operations: +### Basics - │Source │Target - ──────┼───────────┼────── - Amend │File,Branch│Commit - Squash│Commit │Commit - Stage │File,Branch│Branch - Move │Commit │Branch +- [setup](./but-setup): Setup a Git repository to be managed by GitButler +- [teardown](./but-teardown): Go back to vanilla Git branch management -Examples +### Inspection -Squashing two commits into one (combining the commit messages): +- [status](./but-status): Overview of the uncommitted changes in the repository +- [diff](./but-diff): Show a diff of what's changed +- [show](./but-show): Show information about a branch or commit - but rub 3868155 abe3f53f +### Branching and Committing -Amending a commit with the contents of a modified file: +- [commit](./but-commit): Create commits on your branches +- [stage](./but-stage): Stage changes to a branch +- [branch](./but-branch): Commands for managing branches +- [discard](./but-discard): Remove changes +- [resolve](./but-resolve): Resolve commit conflicts +- [merge](./but-merge): Local branch merging - but rub README.md abe3f53f +### Rules -Moving a commit from one branch to another: +- [mark](./but-mark): Create or remove a rule for auto-assigning or auto-committing +- [unmark](./but-unmark): Remove all marks from the workspace - but rub 3868155 feature-branch +### Server Interactions +- [push](./but-push): Push a branch/stack to remote +- [pull](./but-pull): Pull upstream changes and update your branches +- [pr](./but-pr): Commands for interacting with forges like GitHub, GitLab, etc. -**Usage:** `but rub ` +### Editing Commits -## Arguments +- [rub](./but-rub): Combine two entities together to perform an operation +- [absorb](./but-absorb): Absorb changes into existing commits +- [reword](./but-reword): Edit the commit message of a specified commit +- [uncommit](./but-uncommit): Uncommit an existing commit +- [amend](./but-amend): Amend an existing commit with new changes +- [squash](./but-squash): Combine two commits into a single new commit +- [move](./but-move): Reorder commits or move a commit from one branch to another +- [pick](./but-pick): Cherry-pick a commit from an unapplied branch into an applied virtual branch -* `` — The source entity to combine (required) -* `` — The target entity to combine with the source (required) +### Operations Log +- [oplog](./but-oplog): Show operation history +- [undo](./but-undo): Undo the last operation by reverting to the previous snapshot -# but-reword.mdx +# but-unmark.mdx -# Edit the commit message of the specified commit. +This will unmark anything that has been marked by the but mark command. -Edit the commit message of the specified commit. +**Usage:** `but unmark` -You can easily change the commit message of any of your commits by running but reword `` and providing a new message in the editor. +## Platform Options -This will rewrite the commit with the new message and then rebase any dependent commits on top of it. +These options are available for all `but` commands: -You can also use but reword `` to rename the branch. +* `-C`, `--current-dir` `` — Run as if gitbutler-cli was started in PATH instead of the current working directory (default: `.`) +* `-f`, `--format` `` — Explicitly control how output should be formatted. -**Usage:** `but reword [OPTIONS]` +If unset and from a terminal, it defaults to human output, when redirected it's for shells. (default: `human`) +* `-j`, `--json` — Whether to use JSON output format -## Arguments -* `` — Commit ID to edit the message for, or branch ID to rename (required) -## Options +# installation.mdx + +How to install and setup the GitButler CLI. + +## Installing the `but` CLI + +Ok, first thing is first, let's get our `but` CLI installed. Currently there are two ways to do this. + +### Via the Desktop Client + +If you have the desktop client installed, you can go into your global settings and click on the "Install CLI" button in the "general" section. + + + +### Homebrew + +If you're running on a Mac and use Homebrew, you can install GitButler via `brew install gitbutler` and it will install the CLI for you automatically. + +## Setup + +If you go into any existing Git repository and run `but setup`, it will make some neccesary changes to your setup in order for GitButler to manage your data. + +If you run almost any `but` command in an existing Git repository in an interactive terminal, it will ask you if you want to set it up and then run the command you were trying to run. So basically just run `but` anywhere to get started. + +At any time after this, you can run `but teardown` to undo the GitButler changes and go back to being a boring old Git project. It will not remove GitButler metadata, so feel free to go back and forth if you need to. + + +# branching-and-commiting.mdx + +Now that your project is setup and GitButler is installed and configured, you can start branching and committing. + +## The Simple Flow + +Let’s begin with a simple workflow, one that should be familiar to Git users. We will: + +- Do some work +- Create a new branch +- Commit to that branch + +### Status + +Let’s begin by seeing what the status of the working directory is by running `but status`. This will tell you a little more than `git status`, it will list: + +1. All files in your working directory that differ from your base branch (`origin/main`) the last time you updated it that aren’t assigned to a branch +2. A list of the active branches that you have and + 1. All assigned file changes in each branch + 2. All commits in each branch -* `-m`, `--message` `` — The new commit message or branch name. If not provided, opens an editor -* `-f`, `--format` — Format the existing commit message to 72-char line wrapping without opening an editor +So it's sort of like a combination of `git status` and a shortlog of what is on your branches that is not on `origin/master`. +It looks something like this: +{/* restore [f0f437258043] */} +{/* run but rub wu gemfile-fixes */} +{/* run but rub te feature-bookmarks */} -# but-show.mdx +```cli [branching-and-commiting-but-status-1, 506px] +but status +``` -# Shows detailed information about a commit or branch. +Here we can see three applied branches: `gemfile-fixes` stacked on `feature-bookmarks` and independent `sc-branch-26`. There are also three unassigned files. -Shows detailed information about a commit or branch. + -When given a commit ID, displays the full commit message, author information, committer information (if different from author), and the list of files modified. +You can also simply run `but` to get the status. Check out [`but alias`](/commands/but-alias) to set the default of `but` to something else. -When given a branch name, displays the branch name and a list of all commits on that branch. Use --verbose to show full commit messages and files changed. + -Examples +### Create a Branch -Show commit details by short commit ID: +Let’s look at a very simple case first. Let’s say we’ve just modified some files and don’t have a branch yet. Our status might look like this: - but show a1b2c3d +{/* restore [04d62f15beb4] */} -Show commit details by CLI ID: +```cli [branching-and-commiting-but-status-2, 308px] +but status +``` - but show c5 +Now let’s say that we want to put those unassigned file changes into a commit on a new branch called `user-bookmarks`. -Show branch commits by branch name: +To do this, you can use the `but branch new ` command. - but show my-feature-branch +{/* run git branch -D user-bookmarks */} -Show branch with full commit details: +```cli [branching-and-commiting-but-branch-1, 110px] +but branch new user-bookmarks +``` - but show my-feature-branch --verbose +Now if you run `but status` you can see your new empty branch: +```cli [branching-and-commiting-but-status-3, 374px] +but status +``` -**Usage:** `but show [OPTIONS]` +### Commit to a Branch -## Arguments +Now we can commit our unassigned changes to that branch. You can simply assign your changes to the branch first to commit later (we'll cover that later in [Rubbing](./rubbing)), but for now let's keep it simple and just commit them directly using the `but commit` command. -* `` — The commit ID (short or full SHA), branch name, or CLI ID to show details for (required) +```cli [branching-and-commiting-but-commit-1, 110px] +but commit -m 'all the user bookmarks' +``` -## Options +If you don’t specify the `-m` commit message, GitButler will try to open an editor with a tempfile where you can write a longer commit message. It will use the `$EDITOR` environment variable if it’s set, or the `core.editor` Git or GitButler config setting, or it will prompt you for a command to run if you’re in an interactive terminal. -* `-v`, `--verbose` — Show full commit messages and files changed for each commit +Now our status looks like this, with all unassigned files in a new commit on our new branch: +```cli [branching-and-commiting-but-status-4, 286px] +but status +``` + -# but-setup.mdx +You also don't _need_ to create a branch to commit if you have none currently applied. If you run `but commit` and there are no active branches, GitButler will simply create a temporarily named one that you can later rename if you want. -# Sets up a GitButler project from a git repository in the current directory. + -Sets up a GitButler project from a git repository in the current directory. +## Stacked and Parallel Branches -This command will: +Ok, that’s the simple case, pretty straightforward. However, GitButler can also do some pretty cool things that Git either cannot do or struggles with, namely: -- Add the repository to the global GitButler project registry -- Switch to the gitbutler/workspace branch (if not already on it) -- Set up a default target branch (the remote's HEAD) -- Add a gb-local remote if no push remote exists +- Having multiple active branches that you can work on in parallel. +- Managing stacked branches. -If you have an existing Git repository and want to start using GitButler with it, you can run this command to set up the necessary configuration and data structures. +That is, both multiple independent and dependent active branches. Even at the same time if you want. -Examples +### Parallel Branches -Initialize a new git repository and set up GitButler: +Parallel branches is very simple, you can create multiple simultaneously active branches that you can assign and commit changes to in your workspace. - but setup --init +To create a parallel branch, you simply create a new branch the same way we did before. Let’s say that we want to create a `liked-tweets` branch alongside our existing `user-bookmarks`. We simply run the same `but branch new` command again: +{/* run git branch -D liked-tweets */} +{/* run echo 'test' > app/controllers/likes_controller.rb */} +{/* run echo 'test' > app/models/like.rb */} -**Usage:** `but setup [OPTIONS]` +```cli [branching-and-commiting-but-branch-2, 110px] +but branch new liked-tweets +``` -## Options +Now if we run `but status` we can see our previous branch and our new empty branch. -* `--init` — Initialize a new git repository with an empty commit if one doesn't exist. +```cli [branching-and-commiting-but-status-5, 374px] +but status +``` -This is useful when running in non-interactive environments (like CI/CD) where you want to ensure a git repository exists before setting up GitButler. +We can see our previous branch and the commit we made, our new empty branch and a couple of modified files. Now we can commit the unassigned changes to that branch with `but commit -m "liked tweets changes" liked-tweets` +```cli [branching-and-commiting-but-commit-2, 110px] +but commit -m "liked tweets changes" liked-tweets +``` +And now we have one commit in each lane. -# but-squash.mdx +```cli [branching-and-commiting-but-status-6, 374px] +but status +``` -# Squash two commits together. +Here we specified the entire branch name as the commit target (as there is more than one), but you can also use the two character short code that is next to each one. -Squash two commits together. +If you don’t specify a branch identifier and you have more than one active branch, then GitButler will prompt you for which branch you wish to commit the unassigned changes to. -Wrapper for but rub `` ``. +We can also see which files were modified in each commit with the `--files` or `-f` option to `but status`: -**Usage:** `but squash [OPTIONS]` +```cli [branching-and-commiting-but-status-7, 550px] +but status -f +``` -## Arguments +### Stacked Branches -* `` — First commit ID (will be squashed into the second) (required) -* `` — Second commit ID (target commit) (required) +The other way you can create new branches is to make them stacked, that is, one depends on another one and has to be merged in that order. -## Options +To create a new stacked branch in GitButler, you can run `but branch new` with a target branch ID. If we go back in time and instead stack our `liked-tweets` branch, we can make it dependent on the `user-bookmarks` branch by providing it as a stacking "anchor" with `-a` option: -* `-d`, `--drop-message` — Drop the first commit's message and keep only the second commit's message +{/* run git branch -D liked-tweets-stacked */} +{/* restore [e32713a1f41c] */} +```cli [branching-and-commiting-but-branch-3, 110px] +but branch new -a user-bookmarks liked-tweets-stacked +``` +```cli [branching-and-commiting-but-status-8, 374px] +but status +``` -# but-stage.mdx +Now we can commit to our stacked branch. -# Stages a file or hunk to a specific branch. +```cli [branching-and-commiting-but-commit-3, 110px] +but commit -m "liked tweets changes" liked-tweets-stacked +``` -Stages a file or hunk to a specific branch. +```cli [branching-and-commiting-but-status-9, 352px] +but status +``` -Wrapper for but rub `` ``. +Now if you push to a forge, GitButler will set up the reviews (Pull Request or Merge Request) as a stacked request, where `user-bookmarks` has to be merged either before or with `liked-tweets` but they can be reviewed independently. -**Usage:** `but stage ` +## Assigning and Committing Changes -## Arguments +The other way to commit to a branch is to explicitly assign changes to it. This is somewhat like running `git add` in Git, where you’re staging some changes for a future commit. However, unlike Git where you have to do this or override it with `-a` or something, the default in GitButler is to commit all changes by default and only leave out unassigned changes with the flag `-o` or `--only`. -* `` — File or hunk ID to stage (required) -* `` — Branch ID to stage to (required) +### Staging Changes +So, how do we stage changes to a specific branch and then only commit those changes? +Let’s look at an example `but status` with six modified files and two empty, parallel branches and assign and commit one file to each branch as a separate commit. -# but-status.mdx +{/* restore [d5c7317b0fd4] */} -# Overview of the project workspace state. +```cli [branching-and-commiting-but-status-10, 440px] +but status +``` -Overview of the project workspace state. +We will assign each file to a different branch and then see the result. We assign file changes to branches using the `but stage` command, which assigns changes to branches, much like `git add`, but you can do this for multiple branches. -This shows unstaged files, files staged to stacks, all applied branches (stacked or parallel), commits on each of those branches, upstream commits that are unintegrated, commit status (pushed or local), and base branch information. +You can either stage the file identifier that you see next to each file, or all or part of the file path. For example, in this case to identify the `app/models/bookmark.rb` file, you can do either: -Examples +- `g0` +- `app/models/bookmark.rb` -Normal usage: +So lets stage the bookmark changes to the bookmarks branch: - but status +```cli [branching-and-commiting-but-stage-1, 154px] +but stage h0,i0,k0 user-bookmarks +``` -Shorthand with listing files modified +Now we can run `status` and see that these are staged. - but status -f +```cli [branching-and-commiting-but-st-1, 484px] +but st +``` +Now let's rub the user changes into the `user-changes` branch: -**Usage:** `but status [OPTIONS]` +```cli [branching-and-commiting-but-stage-2, 110px] +but stage h0 user-changes +``` -## Options +Now we have some file changes assigned to each branch and still some unassigned changes: -* `-f` — Determines whether the committed files should be shown as well (default: `false`) -* `-v`, `--verbose` — Show verbose output with commit author and timestamp (default: `false`) -* `-r`, `--refresh-prs` — Forces a sync of pull requests from the forge before showing status (default: `false`) -* `-u`, `--upstream` — Show detailed list of upstream commits that haven't been integrated yet (default: `false`) -* `--no-hint` — Disable hints about available commands at the end of output (default: `false`) +```cli [branching-and-commiting-but-status-11, 528px] +but status +``` +Now, if we want to create a commit in the `user-bookmarks` branch, we can either run `but commit bo` which will create a commit with the files assigned as well as both files that are unassigned, but _not_ the file assigned to the `user-changes` lane. +Or, we can make a commit with _only_ the assigned files in `user-bookmarks` by using the `-o` option to `but commit`. -# but-teardown.mdx +```cli [branching-and-commiting-but-commit-4, 110px] +but commit -o -m "liked tweets view" bo +``` -# Exit GitButler mode and return to normal Git workflow. +Now if we look at our status we can see a commit on our branch instead of the assigned changes: -Exit GitButler mode and return to normal Git workflow. +```cli [branching-and-commiting-but-st-2, 440px] +but st +``` -This command: +Now let's commit all the rest of the changes (assigned and unassigned) to our other branch: -- Creates an oplog snapshot of the current state -- Finds the first active branch and checks it out -- Cherry-picks any dangling commits from gitbutler/workspace -- Provides instructions on how to return to GitButler mode +```cli [branching-and-commiting-but-commit-5, 110px] +but commit -m 'bookmarks stuff' ch +``` -This is useful when you want to temporarily or permanently leave GitButler management and work with standard Git commands. +```cli [branching-and-commiting-but-status-12, 374px] +but status +``` -Examples +### Committing Specific Files or Hunks -Exit GitButler mode: +Instead of staging files first and then committing with `-o`, you can also directly specify which files or hunks to include in a commit using the `-F` or `--files` option. This lets you commit only specific changes without having to assign them to a branch first. - but teardown +For example, if you have multiple unassigned files and only want to commit some of them: +```bash +but commit -F h0,i0 -m "only these two files" user-bookmarks +``` -**Usage:** `but teardown` +You can specify files in several ways: +- **By CLI ID**: Use the short identifier shown in `but status` (e.g., `h0`, `i0`) +- **Space-separated**: `--files h0 i0 k0` +- **Comma-separated**: `-F h0,i0,k0` +- **By path**: `-F app/models/bookmark.rb` +This also works with hunk IDs. When a file has multiple hunks (shown in `but status -f` or `but diff`), you can commit individual hunks rather than the entire file. This is useful when you have changes in the same file that belong to different logical commits. -# but-uncommit.mdx + -# Uncommit changes from a commit or file-in-commit to the unstaged area. +If you don't specify `-F`, all uncommitted changes (or changes staged to the target branch) are committed. Use `-F` when you need fine-grained control over what goes into a commit. -Uncommit changes from a commit or file-in-commit to the unstaged area. + -Wrapper for but rub `` zz. +### Assigning Ranges -**Usage:** `but uncommit ` +If you happen to have a large number of changes, you can also use ranges or lists for rubbing assignment. So for example, if we go back to this status: -## Arguments +{/* restore [6fdd8fb1d547] */} +{/* run but rub l0 zz */} -* `` — Commit ID or file-in-commit ID to uncommit (required) +```cli [branching-and-commiting-but-status-13, 484px] +but status +``` +Then you can assign the everything in `app/` to a branch with: +```cli [branching-and-commiting-but-stage-3, 154px] +but stage h0-j0 user-bookmarks +``` -# but-unapply.mdx +```cli [branching-and-commiting-but-status-14, 528px] +but status +``` -# Unapply a branch from the workspace. -Unapply a branch from the workspace. +# configuration.mdx -If you want to unapply an applied branch from your workspace (effectively stashing it) so you can work on other branches, you can run but unapply ``. +We've already covered `but config` a bit in dealing with forges and target branches. -This will remove the changes in that branch from your working directory and you can re-apply it later when needed. You will then see the branch as unapplied in but branch list. +You can also use it for some basic user configuration, such as setting your name and email address for commits and your default editor. -You can specify the branch by: +```cli [configuration-but-config-1, 308px] +but config user +``` -- CLI ID from but status (e.g., bu) -- Full branch name (e.g., feature-branch) +## Aliases -If no branch is specified, an interactive list will be shown to choose from. +GitButler also has a built in aliasing system in case you want to provide some shorthands. -**Usage:** `but unapply [BRANCH] [OPTIONS]` +```cli [configuration-but-alias-1, 308px] +but alias +``` -## Arguments +To add a new alias, you can run `but alias add `, which you can also provide a `-g` or `--global` if you want it to be a global alias. This will put this data in your local or global Git config file. -* `` — Branch to unapply (CLI ID or full name) +You will notice that there is a `default` alias, which is what runs when you just run `but` with no arguments. If you overwrite the `default` alias, you can set up something other than `status` to run by default. -## Options -* `-f`, `--force` — Force unapply without confirmation +# ai-stuff.mdx +## Dash Dash AI +`but squash --ai` -# but-undo.mdx +`but commit --ai` -# Undo the last operation by reverting to the previous snapshot. +## Skills -Undo the last operation by reverting to the previous snapshot. +`but skill install` -This is a shorthand for restoring to the last oplog entry before the current one. It allows you to quickly undo the most recent operation. +## Hooks and MCP -**Usage:** `but undo` +You can also install explicit hooks or an MCP server if you prefer. +# conflict-resolution.mdx -# but-update.mdx +In this world nothing can be said to be certain, except death, taxes and merge conflicts. -# Check for and install updates to the GitButler CLI +There are several different ways that you can run into merge conflicts when using Git (and thus, GitButler, or any other branching version control system). -**Usage:** `but update ` +Perhaps there are changes that have been merged upstream that modified the same files as you did in your branch. Or maybe you uncommitted something that commits above it depended on. -## Subcommands +## First Class Conflicts in GitButler -### `but update check` +First, it's important to understand how GitButler deals with conflicts. While Git generally has to check out conflicts in your working directory and make you resolve them before you can commit, GitButler can partially apply a conflicting change and store the commit marked as "conflicted". -Check for available updates to the GitButler CLI. +This means that: -Queries the update server to see if a newer version is available for your platform and release channel. +- Rebases _always_ succeed, just sometimes it results with commits in a conflicted state. +- You can deal with conflicts in any order and at any time. -**Usage:** `but update check` +So, let's take a look at what this looks like and how we can deal with conflicted commits when they arise. -### `but update suppress` +{/* restore [e53a4a85d83d] */} +{/* run git push -f origin 96ccca9:main */} -Suppress update notifications for a specified duration, defaulting to 1 day. +```cli [conflict-resolution-but-status-1, 308px] +but status +``` -Temporarily hide update notifications for the CLI. The suppression will automatically expire after the specified number of days (default: 1 day). Maximum suppression duration is 30 days. +Let's say that this is our status and we've decided to pull in from upstream. The changes that have been merged in by someone else upstream conflict with ours. When we run `but pull`, it will result in conflicts in our branch (but it will succeed). -**Usage:** `but update suppress [DAYS]` +```cli [conflict-resolution-but-pull-1, 308px] +but pull +``` -**Arguments:** +Ok, the `pull` tells us that we have conflicts and it also gives us a cheat sheet for what to do to resolve them, which is essentially "run `but resolve`". -* `` — Number of days to suppress update notifications (1-30, default: 1) +So first let's see what our conflicted branch looks like with `but status`. +```cli [conflict-resolution-but-status-2, 330px] +but status +``` +Notice how we have _two_ commits that are conflicted, but one that is not. You could have any number of commits marked as conflicted in a branch, and you'll need to resolve each of them one by one. -# but-unmark.mdx +If we were to dig into the details here, a few things have actually happened. -# Removes any marks from the workspace +First of all, we have applied the upstream changes, so if we were to look at the files that conflict, we will see the upstream version rather than what we had done. -Removes any marks from the workspace +Second, the commits that are not in a conflicted state are still applied - those changes are still in your working directory. In fact, even the conflicted commit's changes will be applied in the areas where they don't conflict. -This will unmark anything that has been marked by the but mark command. +You could potentially have several conflicted commits in your branch. When you resolve one, everything above it is rebased and may introduce new conflicts or may resolve other conflicts, depending on the resolution. -**Usage:** `but unmark` +However, for now, let's look at a simple resolution flow. All you really need is one command: `but resolve`. +If you run `but resolve`, it will look through all your applied branches for any conflicted commits. If it finds any, it will list them out and ask you which you want to start with and default to the lowest one on the first branch. +```git +❯ but resolve +Found conflicted commits: -# but-init.mdx +Branch: update-homepage + ● 42165fe branding change: readme + ● 404b604 hero update - new branding -Initializes a GitButler project from a git repository in the current directory. +Would you like to start resolving these conflicts? +Enter commit ID to resolve [default: 42165fe]: +``` -If you have an existing Git repository and want to start using GitButler with it, you can run this command to set up the necessary configuration and data structures. +If you hit enter, it will check out the conflict markers in that commit into your working directory. -This is automatically run when you run any other but command in a git repository that is not yet initialized with GitButler. +```git +❯ but resolve +You are currently in conflict resolution mode. + - resolve all conflicts + - finalize with but resolve finish + - OR cancel with but resolve cancel -Note: Currently, if there is no Git repository already, you will need to initialize it with git init and add a remote first, as GitButler needs a remote to base the branches on. +Conflicted files remaining: + ✗ README.md +Checking out conflicted commit 42165fe +``` -We are working on removing this limitation, but for now this is something to be aware of. +So now you're in a special mode called "Edit Mode" in GitButler, where we've directly checked out a commit to work on. If you run any other commands, we'll warn you that you're currently in this mode. -**Usage:** `but init [OPTIONS]` +For the conflicts, we put in zdiff3 style headers, so you can see your side, their side and also the ancestor. For example, if we look at the conflicted README.md file -## Options +```git +❯ head README.md +<<<<<< ours +# The Why Experience +|||||| ancestor +# Twitter Clone +====== +# X Clone +>>>>>> theirs +``` -* `-r`, `--repo` — Also initializes a git repository in the current directory if one does not exist +So you can see that we started with "Twitter Clone" and upstream changed it to "X Clone" and locally I changed the same line to "The Why Experience". Now I can resolve these three versions into a single line. +If I do that and then again run `but status`, you can see that GitButler notices that the conflicts in the README file has been resolved. +```git +❯ but st +Initiated a background sync... +You are currently in conflict resolution mode. + - resolve all conflicts + - finalize with but resolve finish + - OR cancel with but resolve cancel -# editing-commits.mdx +No conflicted files remaining! +Files resolved: + ✓ README.md +``` -While you can rub changes in and out of commits, you can also edit the commit -message of any commit in your workspace quite easily. +If you had other conflicted files, it would give you a list of what was still unresolved so you could work your way through the list. -## Editing Commit Messages +However, now that we've resolved everything, we can either run `but resolve finish` or just `but resolve` and it will move us to the next step. Technically you can just keep running `but resolve` and it will figure out what the next thing to do is. -You can edit commit messages with the `but describe` command. So if we have this status: +```git +❯ but resolve +You are currently in conflict resolution mode. + - resolve all conflicts + - finalize with but resolve finish + - OR cancel with but resolve cancel -{/* restore [d69fffa7c6eb] */} +No conflicted files remaining! +Files resolved: + ✓ README.md -```cli [ee0b96f297788f6e, 330px] -but status -``` +All conflicts have been resolved! +Finalize the resolution now? [Y/n]: y -Then you can edit the message of any commit by running `but describe `, which will open up your editor of choice with the existing commit message and when you exit the editor, replace that message in the commit and rebase everything above it. +Initiated a background sync... +✓ Conflict resolution finalized successfully! +The commit has been updated with your resolved changes. -The editor would look something like this: +⚠ Warning: New conflicts were introduced during the rebase: -``` -add user changes + ● 4f671a1 hero update - new branding -# Please enter the commit message for your changes. Lines starting -# with '#' will be ignored, and an empty message aborts the commit. -# -# Changes in this commit: -# modified: app/models/user.rb -# modified: config/routes.rb -# -~ -~ -~ -~ +Run but status to see all conflicted commits, or but resolve to resolve them. ``` -Pretty simple. - -{/* TODO: Edit Mode */} +# inspecting.mdx -# branching-and-commiting.mdx +When you're working with projects, sometimes you'll need to inspect things to see what the differences are or summarize work. -Now that your project is setup and GitButler is installed and configured, you can start branching and committing. +The first thing to remember is that GitButler is basically an advanced Git client, which means that you can use any Git inspection command without problems when you're using GitButler. -## The Simple Flow +This includes things like `git show`, `git diff`, `git log`, `git blame`, `git bisect`, etc. So we have not tried to recreate the functionality of these, but instead focused on some of the common needs in a modern workflow that these tools may not do well. -Let’s begin with a simple workflow, one that should be familiar to Git users. We will: +Let's look at a simple scenario. -- Do some work -- Create a new branch -- Commit to that branch +{/* restore [bd526ee8c76c] */} +{/* run but stage h0 us */} +{/* run git push -f origin 32a2175758f7f649ed7a030a17fd21213a5e400f:refs/heads/main */} -### Status +```cli [inspecting-but-status-1, 396px] +but status --files +``` -Let’s begin by seeing what the status of the working directory is by running `but status`. This will tell you a little more than `git status`, it will list: +Here we have an unstaged file (`Gemfile`), a file staged to the `user-bookmarks` branch (`README.md`), and two commits on our branch. -1. All files in your working directory that differ from your base branch (`origin/main`) the last time you updated it that aren’t assigned to a branch -2. A list of the active branches that you have and - 1. All assigned file changes in each branch - 2. All commits in each branch +## Diffing things -So it's sort of like a combination of `git status` and a shortlog of what is on your branches that is not on `origin/master`. +For most of this, you could use `git diff`. For example, to see everything that is uncommitted, you can just run `git diff`. -It looks something like this: +```cli [inspecting-git-1, 836px] +git diff HEAD +``` -{/* restore [f0f437258043] */} -{/* run but rub wu gemfile-fixes */} -{/* run but rub te feature-bookmarks */} +However, I don't find that a super readable format, even if it's useful in applying with the Unix `patch` command. Since most people tend not to be emailing patches around, we tried to optimize for a much more human readable format: -```cli [3f402d2c55b2d6ed, 528px] -but status +```cli [inspecting-but-diff-1, 814px] +but diff ``` -Here we can see three applied branches: `gemfile-fixes` stacked on `feature-bookmarks` and independent `sc-branch-26`. There are also three unassigned files. +You can see that this is the same information, but a bit more easily understandable. -### Create a Branch +You can also focus the diff output to any of the short codes in that status output. For example, to just see what is staged to `user-bookmarks` you can run `but diff l0`. To only see the changes committed to the bookmarks controller file in the "create bookmarks" commit you can run `but diff n0`, to only see what modifications have not been staged you can run `but diff zz`, and so on. -Let’s look at a very simple case first. Let’s say we’ve just modified some files and don’t have a branch yet. Our status might look like this: +## Listing Branches + +When GitButler creates and modifies branches, it is manipulating real Git branches, so you can see them and inspect them with normal Git commands as well. While you can use the `git branch` command to see all your branches, the `but branch` command is a bit nicer. -{/* restore [04d62f15beb4] */} +Here is what `git branch` might output (this example repo has over 100 branches, so let's just truncate it): -```cli [df173079f271dcd7, 330px] -but status +```cli [inspecting-git-2, 286px] +git branch | head -10 ``` -Now let’s say that we want to put those unassigned file changes into a commit on a new branch called `user-bookmarks`. - -To do this, you can use the `but branch new ` command. + -{/* run git branch -D user-bookmarks */} +This output is actually _better_ than the default Git output for this, because I have a config setting of `branch.sort -comitterdate`, so at least it's showing me the branches by last commit rather than the default of alphabetically. -```cli [5e4522e7ae7283a8, 110px] -but branch new user-bookmarks -``` + -Now if you run `but status` you can see your new empty branch: +The `but branch` command, however, is built specifically to help you identify the branches you're looking for and give you some useful information about them. Let's give it a try: -```cli [7c444dcf5f7ad37b, 396px] -but status +```cli [inspecting-but-branch-1, 660px] +but branch ``` -### Commit to a Branch +You can immediately notice that this is a very different type of listing. We're not just showing the names of the branches, but also some very useful information about all of the branches that are available to us. -Now we can commit our unassigned changes to that branch. You can simply assign your changes to the branch first to commit later (we’ll cover that later in [Rubbing](https://www.notion.so/Rubbing-2545a4bfdeac80209d37cd4d629316cc?pvs=21)), but for now let’s keep it simple and just commit them directly using the `but commit` command. +First, we show any applied branches - these are the branches that are currently applied into your workspace. Next, all the _unapplied_ branches - that is, the other branches that you don't currently have active in your working directory. -```cli [7b239ccb4f9cbf71, 110px] -but commit -m 'all the user bookmarks' -``` +For each branch, you'll see how many commits "ahead" it is, that is, how many commits are on that branch that are not on the target branch (eg `origin/main`). In other words, if this branch were merged to production, what would come in with it? -If you don’t specify the `-m` commit message, GitButler will try to open an editor with a tempfile where you can write a longer commit message. It will use the `$EDITOR` environment variable if it’s set, or the `core.editor` Git or GitButler config setting, or it will prompt you for a command to run if you’re in an interactive terminal. +There is also a "✓" or "✗" that indicates if this branch is cleanly mergeable with your target branch. -Now our status looks like this, with all unassigned files in a new commit on our new branch: +It also shows the last author of a commit on that branch and orders everything by how long ago the last commit was. -```cli [9b7a40c4c648df55, 308px] -but status -``` +The point of this listing is to help you easily see what work you have available, not merged into your target, that you might want to work on. -## Stacked and Parallel Branches +## Filtering your Branches -Ok, that’s the simple case, pretty straightforward. However, GitButler can also do some pretty cool things that Git either cannot do or struggles with, namely having multiple active branches that you can work on in parallel, and managing stacked branches. That is, both multiple independent and dependent active branches. +Running `but branch` defaults to running `but branch list`, which has a bunch of other options (filtering to only local or remote branches, not calculating mergability for speed, etc). The most useful option might be the filtering, for example, you can type a partial match string and it will filter the output: -### Parallel Branches +```cli [inspecting-but-branch-2, 242px] +but branch list book +``` -Parallel branches is very simple, you can create multiple simultaneously active branches that you can assign and commit changes to in your workspace. +## Looking at a Branches -To create a parallel branch, you simply create a new branch the same way we did before. Let’s say that we want to create a `liked-tweets` branch alongside our existing `user-bookmarks`. We simply run the same `but branch new` command again: +If you want to see what is on a branch, you can inspect a specific branch by running `but branch show `. This will show the commits on this branch ahead of your target. -{/* run git branch -D liked-tweets */} -{/* run echo 'test' > app/controllers/likes_controller.rb */} -{/* run echo 'test' > app/models/like.rb */} +Essentially, it runs the equivalent of `git log origin/main..` with some more introspection. -```cli [325dc612815c8059, 110px] -but branch new liked-tweets +```cli [inspecting-git-3, 308px] +git log origin/main..feature-awesome-thing ``` -Now if we run `but status` we can see our previous branch and our new empty branch. +Now let's look at `but branch show` -```cli [9e1d79e742dc6996, 396px] -but status +```cli [inspecting-but-branch-3, 286px] +but branch show feature-awesome-thing ``` -We can see our previous branch and the commit we made, our new empty branch and a couple of modified files. Now we can commit the unassigned changes to that branch with `but commit -m "liked tweets changes" liked-tweets` +Pretty much a short log of the branch difference from our target branch. However, there are a bunch of options if you want to dig in further. -```cli [f3f50c447dd54919, 110px] -but commit -m "liked tweets changes" liked-tweets -``` +The `-r` option will show you PR information if one is opened on this branch. The `-f` option will show you the files modified in each commit. -And now we have one commit in each lane. +The real fun one is adding `--ai`, which will take a look at the changes and summarize what the changes actually do. -```cli [1a984478e3be31fb, 396px] -but status -``` +Let's run all of them at the same time: -Here we specified the entire branch name as the commit target (as there is more than one), but you can also use the two character short code that is next to each one. +```cli [inspecting-but-branch-4, 704px] +but branch show sc-branch-28 --ai -r -f +``` -If you don’t specify a branch identifier and you have more than one active branch, then GitButler will prompt you for which branch you wish to commit the unassigned changes to. +## Showing a Commit -We can also see which files were modified in each commit with the `--files` or `-f` option to `but status`: +If you want to look at a specific commit in any of these circumstances, you can use the `but show` command with the commit hash. -```cli [d4d844153342a54a, 572px] -but status -f +```cli [inspecting-but-show-1, 286px] +but show a42580b96ed7b432 ``` -### Stacked Branches +## Deleting Branches -The other way you can create new branches is to make them stacked, that is, one depends on another one and has to be merged in that order. +As long as we're talking about branches, let's show how to get rid of them. If you've merged one, it will by default not be shown anymore with `but branch` anyhow, but if you have one with some work on it that you want to abandon, you can also easily delete it with `but branch delete` (or `-d`). -To create a new stacked branch in GitButler, you can run `but branch new` with a target branch ID. If we go back in time and instead stack our `liked-tweets` branch, we can make it dependent on the `user-bookmarks` branch by providing it as a stacking "anchor" with `-a` option: -{/* run git branch -D liked-tweets-stacked */} -{/* restore [e32713a1f41c] */} +# editing-commits.mdx -```cli [9057fc328f83bf48, 110px] -but branch new -a user-bookmarks liked-tweets-stacked -``` +While you can rub changes in and out of commits, you can also edit the commit +message of any commit in your workspace quite easily. -```cli [8d0e5bc2d53966ec, 396px] -but status -``` +## Editing Commit Messages -Now we can commit to our stacked branch. +You can edit commit messages with the `but describe` command. So if we have this status: -```cli [cfaac56c81b6cbcf, 110px] -but commit -m "liked tweets changes" liked-tweets-stacked -``` +{/* restore [d69fffa7c6eb] */} -```cli [1e83a142a5cae105, 374px] +```cli [editing-commits-but-status-1, 330px] but status ``` -Now if you push to a forge, GitButler will set up the reviews (Pull Request or Merge Request) as a stacked request, where `user-bookmarks` has to be merged either before or with `liked-tweets` but they can be reviewed independently. +Then you can edit the message of any commit by running `but reword `, which will open up your editor of choice with the existing commit message and when you exit the editor, replace that message in the commit and rebase everything above it. -## Assigning and Committing Changes +The editor would look something like this: -The other way to commit to a branch is to explicitly assign changes to it. This is somewhat like running `git add` in Git, where you’re staging some changes for a future commit. However, unlike Git where you have to do this or override it with `-a` or something, the default in GitButler is to commit all changes by default and only leave out unassigned changes with the flag `-o` or `--only`. +``` +add user changes -### Assigning Changes +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# +# Changes in this commit: +# modified: app/models/user.rb +# modified: config/routes.rb +# +~ +~ +~ +~ +``` -So, how do we assign changes to a specific branch and then only commit those changes? +Pretty simple. -Let’s look at an example `but status` with six modified files and two empty, parallel branches and assign and commit one file to each branch as a separate commit. +## Changing Branch Names -{/* restore [d5c7317b0fd4] */} +Just like changing commit messages, you can also use `but reword` to change the name of a branch. So, in the above example, if we wanted to change the branch name from `user-bookmarks` to `feature-awesome-thing`, we can do this: -```cli [f2605919d821204d, 550px] +```cli [editing-commits-but-reword-1, 110px] +but reword -m feature-awesome-thing us +``` + +Et voila. + +```cli [editing-commits-but-status-2, 330px] but status ``` -We will assign each file to a different branch and then see the result. We assign file changes to branches using the `but rub` command, which combines things. We'll go more into all that rubbing can do later. +{/* TODO: Edit Mode */} + -You can either rub the file identifier that you see next to each file, or all or part of the file path. For example, in this case to identify the `app/models/bookmark.rb` file, you can do any of: +# operations-log.mdx -- `xw` -- `app/models/` -- `bookmark` -- `app/models/bookmark.rb` +GitButler maintains a detailed log of all operations, making it easy to track what happened and undo changes when needed. -In order to differentiate a shortcode from a path, a shortcode is exactly 2 characters and a path needs to be at least 3. This is the same pattern matching used for the branch ID too. +## Viewing the Operations Log -So lets rub the bookmark changes into the bookmarks branch: +See all recent GitButler operations: -```cli [52c2db7eb27eaec3, 88px] -but rub xw,ie,rt user-bookmarks +```cli [operations-log-but-oplog-1, 550px] +but oplog ``` -Now let's rub the user changes into the `user-changes` branch: +## Undoing the last operation + +Undo the last operation: -```cli [52c2db7eb27eaec3, 88px] -but rub ku user-changes +```cli [operations-log-but-undo-1, 154px] +but undo ``` -Now we have some file changes assigned to each branch and still some unassigned changes: +## Restoring to a previous point -```cli [f2605919d821204d, 550px] -but status +You can easily restore to any point in the operations history by running the `but oplog restore` command. If it will modify your working directory (maybe it's just changes to the commit history or staging stuff), then it will prompt you if you want to continue. If you don't want that, you can pass `-f` or `--force` to make it do it anyhow. + +```cli [operations-log-but-oplog-2, 154px] +but oplog restore -f 6fdd8fb1d547 ``` -Now, if we want to create a commit in the `user-bookmarks` branch, we can either run `but commit nd` which will create a commit with the files assigned as well as both files that are unassigned, but _not_ the file assigned to the `user-changes` lane. +Restorations create a new oplog entry before running, so you can always easily undo it in the same manner. -Or, we can make a commit with _only_ the assigned files in `user-bookmarks` by using the `-o` option to `but commit`. + -```cli [52c2db7eb27eaec3, 88px] -but commit -o -m "liked tweets view" h4 -``` +It can be a bit confusing as to what state it restores to. It will restore to what your project looked like _before_ the operation was run. So for example, if there is a `CreateCommit` operation and you restore to that SHA, it will put your state back to the moment before the commit happened. -Now if we look at our status we can see a commit on our branch instead of the assigned changes: + -```cli [f2605919d821204d, 550px] -but status -``` +## Creating Snapshots -Now let's commit all the rest of the changes (assigned and unassigned) to our other branch: +You can also manually create snapshots of moments that you want to be able to revert to at any point, without some other operation needing to automatically save it. -```cli [52c2db7eb27eaec3, 88px] -but commit -m 'bookmarks stuff' nd +```cli [operations-log-but-oplog-3, 154px] +but oplog snapshot ``` -```cli [f2605919d821204d, 550px] -but status -``` +Now you can copy that SHA and restore to that exact point at any time in the future. -### Assigning Ranges -If you happen to have a large number of changes, you can also use ranges or lists for rubbing assignment. So for example, if we go back to this status: +# conclusion.mdx -{/* restore [6fdd8fb1d547] */} -{/* run but rub nd 00 */} +Ok, that's a short guide to GitButler's command line interface. -```cli [8973cce08261500a, 550px] -but status -``` +Join us in [Discord](https://discord.com/invite/MmFkmaJ42D) if you have any other questions or suggestions for how we can improve the tool. -Then you can assign the first, third, and fifth file to a branch with: +Thanks! -```cli [52c2db7eb27eaec3, 88px] -but rub nx,xw,rt user-bookmarks -``` -```cli [8973cce08261500a, 550px] -but status -``` +# initializing-a-repository.mdx +If you run any `but` command in a repository that has never been seen by GitButler before, it will automatically ask you if you want to setup the repository for GitButler. -# but-forge.mdx +It will guess most things needed, but everything can be changed later if it got anything wrong. The most important thing is to figure out the target branch - the main branch that you'll want to merge things into and you consider 'production' or 'golden'. Normally this is something like `origin/main`, but GitButler should be pretty good at guessing. -Commands for interacting with forges like GitHub, GitLab (coming soon), etc. +You can also run `but setup` manually to set everything up explicitly: -The but forge tools allow you to authenticate with a forge from the CLI, which then enables features like creating pull requests with the but review commands. +```ansi but-setup-5faf7f36 +but setup +``` -Start by running but forge auth to authenticate with your forge. +As the command says, it does a few things to prepare your repository for being managed by GitButler. -You can also authenticate several different users on a forge and see them listed with but forge list-users or forget a user with but forge forget. +Unlike a tool like [Jujutsu](https://docs.jj-vcs.dev/latest/git-compatibility/) or [Sapling](https://sapling-scm.com/docs/git/git_support_modes/), GitButler mainly operates on normal Git repositories, so nearly all Git commands will work with anything produced or managed by GitButler. You can think of it more like a new porcelain than a different system. -Currently only GitHub is supported, but more forges will be added in the near future. +However, one thing we need to do in order to enable having parallel applied branches is create a "megamerge" commit that automatically merges in the heads of all your applied branches, so that other tools `git status` will correctly show you what you expect. -**Usage:** `but forge ` +This means that we do two things: -## Subcommands +- We create a new branch called `gitbutler/workspace` pointing to a constantly rewritten and ephemeral merge commit and check this branch out so HEAD is pointing to it (again, for `git status` reasons in tools like VSCode or whatever) +- We add custom `pre-commit` and `post-checkout` hooks (moving and continuing to call any existing hooks) to try to prevent you from accidentally committing on top of our managed mega-merge commit. -### `but forge auth` + -Authenticate with your forge provider (at the moment, only GitHub is supported) +The mega-merge workspace commit will soon only be needed once you actually have more than one branch applied, so at some point we won't automatically do it on setup, but we're working on it. -**Usage:** `but forge auth` + -### `but forge list-users` +Both of these things can be quickly and easily undone by running `but teardown`, doing any Git committing stuff you need to do and then re-running `but setup` to go back to GitButler tooling. -List authenticated forge accounts known to GitButler +You can also simply checkout a git branch with `git checkout ` and the `post-checkout` hook we installed should clean up after itself. -**Usage:** `but forge list-users` -### `but forge forget` +# forges.mdx -Forget a previously authenticated forge account +We've touched on how to update your local branches with `but pull`, but what about when you want to take your awesome new branches of work and put them on a server to get them integrated or collaborate with other people? -**Usage:** `but forge forget [USERNAME]` +The two main commands to get work out are `but push` and `but pr`. Let's start with `but push` as it's a little simpler. -**Arguments:** +## Pushing -* `` — The username of the forge account to forget If not provided, you'll be prompted to select which account(s) to forget. If only one account exists, it will be forgotten automatically +The very simple example would be to simply run `but push `. +``` +❯ but push update-homepage +✓ Push completed successfully -# commands-overview.mdx + update-homepage -> origin/update-homepage ((new branch) -> 1d31833) +``` -## Command Reference +You can also run `but push` by itself. If there is only one applied branch, it will push that one. If there are several applied branches, you can choose which to push or select 'all' to push all of them. -### Basics +You can also supply `-d` or `--dry-run` to see what _would_ be pushed up. -- [init](./but-init): Initialize a GitButler project from a git repository +## Pull Requests -### Inspection +That's the simple way to push branches to your default remote and update already pushed ones. -- [status](./but-status): Overview of the uncommitted changes in the repository +The other common thing to do is to open Pull Requests on GitHub. GitButler's CLI has a built in command for opening and updating PRs, called `but pr`. -### Committing +Much like `but push`, if there is only one branch, it will open a PR for that, otherwise it will ask you which branch to open one for. Or you can be explicit with something like `but pr `. -- [base](./but-base): Commands for managing the base branch -- [branch](./but-branch): Commands for managing branches -- [rub](./but-rub): Combine two entities together to perform an operation -- [commit](./but-commit): Commit changes to a stack -- [mark](./but-mark): Create or remove a rule for auto-assigning or auto-committing -- [unmark](./but-unmark): Remove all marks from the workspace +``` +❯ but pr +Do you want to open a new PR on branch 'sc-switch-wording-to-x'? [Y/n]: y +→ Pushing sc-switch-wording-to-x... + ✓ Pushed to origin +→ Creating PR for sc-switch-wording-to-x → main... + +✓ Created PR #69 + Title: Switch Wording to X + Branch: sc-switch-wording-to-x + URL: https://github.com/schacon/why/pull/69 +``` -### Editing +Once it's opened, you'll get a URL you can view the pull request on. -- [describe](./but-describe): Edit the commit message of a specified commit -- [new](./but-new): Insert a blank commit before a specified commit or at the top of a stack +## Forge Authentication -### Forge Stuff +In order to open a PR on GitHub, you'll need to authenticate to that forge. You can see which authentications you have by running `but config`: -- [push](./but-push): Push a branch/stack to remote -- [publish](./but-publish): Publish review requests for active branches in your workspace -- [forge](./but-forge): Commands for interacting with forges like GitHub, GitLab, etc. +```cli [forges-but-config-1, 484px] +but config +``` -### Operations Log +If you have not authenticated to a forge yet, you can run `but config forge auth`, which will ask you which type of authentication you would like to do: -- [oplog](./but-oplog): Show operation history -- [undo](./but-undo): Undo the last operation by reverting to the previous snapshot -- [restore](./but-restore): Restore to a specific oplog snapshot -- [snapshot](./but-snapshot): Create an on-demand snapshot with optional message +``` +❯ but config forge auth +? Select an authentication method: +> Device flow (OAuth) + Personal Access Token (PAT) + GitHub Enterprise +``` -### AI Tools +Selecting one of the options will allow you to provide an authentication token for GitButler to use for opening and modifying PRs. -- [mcp](./but-mcp): Start up the MCP server + + Currently, GitButler only authenticates to GitHub servers, but support for Merge Requests on + GitLab and other forges is coming. + # rubbing.mdx -As we saw in the [Branching and Committing](branching-and-commiting) section, the `but rub` command can be used to assign changes to branch lanes. +As we saw in the [Branching and Committing](branching-and-commiting) section, the `but stage` command can be used to assign changes to branch lanes. -However, it can be used to do _so much_ more. Rubbing is essentially combining two things. Since there are lots of _things_ in the tool, combining them together can do lots of different operations. Most of them should be fairly intuitive once you understand the concept. +However, we can also do this with a command called `but rub`. Not only can it stage changes though, it can be used to do _so much_ more. Rubbing is essentially combining two things. Since there are lots of _things_ in the tool, combining them together can do lots of different operations. Most of them should be fairly intuitive once you understand the concept. Let’s take a look at what is possible with this very straightforward command. @@ -4835,19 +5802,18 @@ Let’s take a look at what is possible with this very straightforward command. We already showed how you can use `rub` to assign a file change or set of changes to a branch for later committing (rubbing a file and a branch), but what if you want to undo that? Move assignments to a different lane or revert them to being unassigned for later? -As you may have noticed in the `but status` output, there is a special identifier `00` which is always the “unassigned” ID. If you rub anything to `00` then it will move it to unassigned. +As you may have noticed in the `but status` output, there is a special identifier `zz` which is always the “unassigned” ID. If you rub anything to `zz` then it will move it to unassigned. So given this status: {/* restore [f0f437258043] */} -{/* run but rub wu gemfile-fixes */} -{/* run but rub te feature-bookmarks */} +{/* run but rub g0,h0 gemfile-fixes */} -```cli [b9607c89de61f064, 550px] +```cli [rubbing-but-status-1, 528px] but status ``` -We can re-unassign the `README.new.md` file with `but rub np 00`. Or, we can re-assign that file to the `sc-branch-26` parallel branch with `but rub np q6`. +We can re-unassign the `README.new.md` file with `but rub h0 zz`. Or, we can re-assign that file to the `sc-branch-26` parallel branch with `but rub h0 sc-branch-26`. ## Amending Commits @@ -4859,41 +5825,49 @@ However, with `rub` it’s incredibly simple. Just rub the new changes into the Let’s say that we have a branch with some commits in it, we’ve made changes to two files and want to amend two different commits with the new changes. -```cli [b9607c89de61f064, 550px] +```cli [rubbing-but-status-2, 528px] but status ``` If we want to update the first commit (`da42d06`) with the `README-es.md` changes and the last commit (`fdbd753`) with the `app/views/bookmarks/index.html.erb` changes, we can run the following two `rub` commands: -```cli [52c2db7eb27eaec3, 88px] -but rub q2 da +```cli [rubbing-but-rub-1, 110px] +but rub h0 da42d06 ``` -```cli [52c2db7eb27eaec3, 88px] -but rub rt fd +```cli [rubbing-but-rub-2, 110px] +but rub app/views/bookmarks/index.html.erb fdbd753 ``` -```cli [75bb1c3353c5f15b, 792px] +```cli [rubbing-but-status-3, 770px] but status --files ``` Notice that the SHAs have changed for those commits. It has rewritten the commits to have the same messages but incorporated the changes you rubbed into those patches. -If you wanted to rub all the unassigned changes into a specific commit, you could also do that by rubbing the unassigned section to a commit, for example `but rub 00 1l` which would take all unassigned changes and amend commit `1l` with them. +Also notice that you can use either the commit SHA or file path instead of the short ID if you prefer more typing. + +If you wanted to rub all the unassigned changes into a specific commit, you could also do that by rubbing the unstaged section to a commit, for example `but rub zz f55a30e` which would take all unstaged changes (if there were any) and amend commit `f55a30e` with them. ## Squashing Commits -File changes are not the only thing that you can rub. You can also rub commits into things. To squash two commits together, you simply rub them together. Let’s take the last status output and squash the two commits in `gemfile-fixes` together: +File changes are not the only thing that you can rub. You can also rub commits into things. To squash two commits together, you simply rub them together. Let’s look at a simple example of a branch with two commits on it. We'll squash them into one: + +{/* restore [eb63f26e885a] */} + +```cli [rubbing-but-status-4, 330px] +but status +``` -{/* restore [f2eae624cc2f] */} +We can absorb the top commit into the bottom one by running `but rub `: -```cli [56c233339cef3d84, 110px] -but rub 21 f6 +```cli [rubbing-but-rub-3, 110px] +but rub 0f 08 ``` Now we can see that we only have one commit in our branch: -```cli [5ae65a2bbd5ad512, 462px] +```cli [rubbing-but-status-5, 308px] but status ``` @@ -4905,21 +5879,21 @@ Let’s say that we want to just _undo_ a commit - that is, pretend that we had So, if we’re back to this status: -{/* restore [f0f437258043] */} +{/* restore [eb63f26e885a] */} -```cli [3f402d2c55b2d6ed, 528px] +```cli [rubbing-but-status-6, 330px] but status ``` -And we want to un-commit the first commit (`fdbd753`) as though we had never made it, you can rub to `00`: +And we want to un-commit the first commit (`0fa2965`) as though we had never made it, you can rub to `zz`: -```cli [52c2db7eb27eaec3, 88px] -but rub fd 00 +```cli [rubbing-but-rub-4, 110px] +but rub 0f zz ``` Now if we look at our status again, we will see that commit removed and those files back in the unassigned status: -```cli [3f402d2c55b2d6ed, 528px] +```cli [rubbing-but-status-7, 418px] but status ``` @@ -4927,23 +5901,23 @@ but status We can also use rubbing to move a commit from one branch to another branch if we have multiple active branches and committed to the wrong one, or otherwise decide that we want to split up independent work. -Let’s say that we have two commits on one branch and one commit on a second parallel branch and want to move one: +Let’s say that we have two commits on one branch and created a second parallel branch to move one of the commits to so it's not dependent. -{/* restore [f2eae624cc2f] */} +{/* restore [f71c049739a9] */} -```cli [09bcaab8701882d9, 484px] +```cli [rubbing-but-status-8, 396px] but status ``` -We can move the “Add Spanish README and bookmarks feature” commit to the `sc-branch-26` branch with `but rub`: +We can move the “second commit” commit to the `move-second-commit` branch with `but rub`: -```cli [52c2db7eb27eaec3, 88px] -but rub 21 q6 +```cli [rubbing-but-rub-5, 110px] +but rub 0f mo ``` -Now we can see that the commit has been moved to the top of the `sc-branch-26` branch: +Now we can see that the commit has been moved to the `move-second-commit` branch, breaking up the series into two independent branches with one commit each. -```cli [09bcaab8701882d9, 484px] +```cli [rubbing-but-status-9, 396px] but status ``` @@ -4955,95 +5929,107 @@ As you might imagine, you can also simultaneously move and squash by rubbing a c You can also move specific file changes from one commit to another. +{/* restore [6ccc38e33e0e] */} + To do that, you need identifiers for the files and hunks in an existing commit, which you can get via a `but status -f`, or `but status --files` that tells status to also list commit file IDs. -```cli [19250c7db82a8537, 748px] +```cli [rubbing-but-status-10, 638px] but status -f ``` -So now we can move the changes from one commit to another by rubbing pretty easily. Let’s take the `app/controllers/bookmarks_controller.rb` change and move it down to the "add bookmark model and associations" commit: +So now we can move the changes from one commit to another by rubbing pretty easily. Let’s take the `app/controllers/bookmarks_controller.rb` change and move it down to the "second commit" commit on the other branch: -```cli [52c2db7eb27eaec3, 88px] -but rub vo f5 +```cli [rubbing-but-rub-6, 88px] +but rub 08:3 2e ``` -Now the change is in the previous commit: +Now the change is in the "second commit" on the other branch: -```cli [19250c7db82a8537, 748px] +```cli [rubbing-but-status-11, 638px] but status -f ``` +Also notice that the SHAs of both commits were changed, as they both needed to have content modified. + ## Splitting Commits -Ok, so now we can be pretty specifc about moving changes around to all these different states. The last thing we’ll cover here is splitting commits, which requires a new command that creates a new empty commit called `but new`. +Ok, so now we can be pretty specifc about moving changes around to all these different states. The last thing we’ll cover here is splitting commits, which requires a new command that creates a new empty commit called `but commit empty`. -By default, `but new` will create a new empty commit at the top of the most recently created branch, however you can specify a different branch, or even a specific place within a branch with existing commits. +The general strategy here is that to split a commit, you would make a new empty commit above or below it, then rub changes from the one commit into the empty commit until it's how you want it to look, then you're done. -Let’s say that we’re back to this state: +Let’s say we have a branch with a single commit on it and want to split it into two commits. -{/* restore [f0f437258043] */} +{/* restore [64df45a76e99] */} -```cli [3f402d2c55b2d6ed, 528px] +```cli [rubbing-but-status-12, 308px] but status ``` Now we want to split the "add bookmark model and associations" into two separate commits. The way we do this is to insert a blank commit in between `06` and `f5` and then rub changes into it (then probably edit the commit message). -We can insert a blank commit by running `but new f5` which inserts a blank commit under the specified commit. +We can insert a blank commit by running `but commit empty --after 6a` which inserts a blank commit above the specified commit. -```cli [130fd7c63a28b2d9, 110px] -but new f5 +```cli [rubbing-but-commit-1, 88px] +but commit empty --after 6a ``` Now we have a blank commit: -```cli [cf4b1a77ef11bb2d, 792px] +```cli [rubbing-but-status-13, 440px] but status -f ``` -Now we can use the previous method of moving file changes from other commits into it, then edit the commit message with `but describe 54` (for more on the `describe` command, see [Editing Commits](editing-commits), coming up next). - - -# operations-log.mdx +Now we can use the previous method of moving file changes from other commits into it, then edit the commit message with `but reword 54` (for more on the `reword` command, see [Editing Commits](editing-commits), coming up next). -GitButler maintains a detailed log of all operations, making it easy to track what happened and undo changes when needed. -## Viewing the Operations Log +# scripting.mdx -See all recent GitButler operations: +JSON! -```cli [3667c7fd2e803f94, 550px] -but oplog -``` -## Undoing the last operation +# tutorial-overview.mdx -Undo the last operation: +Using the GitButler CLI is meant to make a specific common workflow very simple, which is roughly: -```cli [41679fc41582c5bd, 154px] -but undo -``` +- Create a branch +- Do work on that branch +- Commit to that branch +- Optionally, create another branch if you find unrelated work you need to do +- Work on and commit to that branch +- Submit a branch for review +- Create a stacked branch if needed to continue on dependent work +- Update your base if work has been integrated to remove merged work +- Rinse and repeat -## Restoring to a previous point +Additionally, GitButler is very good at editing commits (amending fixup work, squashing, rewording messages, etc), it keeps a simple log of what you've done in case you need to go back in time, it makes collaborating on a branch with others easy, it has great GitHub/Lab integration and more. -```cli [6ddcdbd01f90a00e, 176px] -but restore -f 6fdd8fb1d547 -``` +Let's walk through some of the things it can do and what a typical day using the GitButler CLI might look like. # updating-the-base.mdx -The base branch is the foundation that your feature branches build upon. Keeping it updated and managing it properly is crucial for a smooth workflow. +The target branch is the foundation that your feature branches build upon. Keeping it updated and managing it properly is crucial for a smooth workflow. -## Understanding the Base Branch +## Understanding the Target Branch -The base branch is typically your main development branch that acts as the basis -for all your local branches. +The target branch is typically your main production branch that acts as the basis for all your local branches. -In practice, this is actually the branch on whatever server you're using to collaborate and merge changes into, so generally it's actually `origin/main` or `origin/master`. +In practice, this is generally not actually a local branch, it's usually the branch on whatever server you're using to collaborate and merge changes into, so generally it's something like `origin/main` or `origin/master`. When GitButler is first initialized in a project, you are asked to choose a branch to target, as everything in your working directory that doesn't exactly match the tip of this branch is technically a fork of what is considered production. Whatever that target branch looks like when you choose it is set as your 'base'. +You can always check your target branch setting with `but config`: + +```cli [updating-the-base-but-config-1, 484px] +but config +``` + +Or get more information with `but config target`: + +```cli [updating-the-base-but-config-2, 330px] +but config target +``` + When you start working, everything that is different from that base goes into a branch based off of it. - -Currently a base branch needs to be on a remote. Our workflow currently assumes you are pushing to a forge and merging after review on that branch to the target base. Support for local-only workflows is coming, but not current possible. - - - - -# installation.mdx - -How to install and setup the GitButler CLI. - -## Installing the `but` CLI - -Ok, first thing is first, let's get our `but` CLI installed. Currently there are two ways to do this. - -### Via the Desktop Client - -If you have the desktop client installed, you can go into your global settings and click on the "Install CLI" button in the "general" section. - - - -### Homebrew - -If you're running on a Mac and use Homebrew, you can install GitButler via `brew install gitbutler` and it will install the CLI for you automatically. - -## Setup - -There isn't currently much setup you can do, but this will change in the near future, such as setting your name and email for commits or the editor to use for your commit messages. - -The next step is to initialize an existing Git repository to use GitButler branching and tooling, which you can read about in the next section, the Tutorial.