# GitButler Documentation
## Table of Contents
- [butler-flow](#butler-flow)
- [cli-overview](#cli-overview)
- [gitbutler-tui](#gitbutler-tui)
- [guide](#guide)
- [index](#index)
- [overview](#overview)
- [releases](#releases)
- [why-gitbutler](#why-gitbutler)
- [workspace-branch](#workspace-branch)
- [ai-agents-getting-started](#ai-agents-getting-started)
- [ai-agents-overview](#ai-agents-overview)
- [ai-agents-parallel-agents](#ai-agents-parallel-agents)
- [ai-agents-review-agent-work](#ai-agents-review-agent-work)
- [ai-agents-tuning-agent-behavior](#ai-agents-tuning-agent-behavior)
- [ai-agents-useful-requests](#ai-agents-useful-requests)
- [cli-guides-cli-tutorial-ai-stuff](#cli-guides-cli-tutorial-ai-stuff)
- [cli-guides-cli-tutorial-branching-and-commiting](#cli-guides-cli-tutorial-branching-and-commiting)
- [cli-guides-cli-tutorial-conclusion](#cli-guides-cli-tutorial-conclusion)
- [cli-guides-cli-tutorial-configuration](#cli-guides-cli-tutorial-configuration)
- [cli-guides-cli-tutorial-conflict-resolution](#cli-guides-cli-tutorial-conflict-resolution)
- [cli-guides-cli-tutorial-editing-commits](#cli-guides-cli-tutorial-editing-commits)
- [cli-guides-cli-tutorial-forges](#cli-guides-cli-tutorial-forges)
- [cli-guides-cli-tutorial-initializing-a-repository](#cli-guides-cli-tutorial-initializing-a-repository)
- [cli-guides-cli-tutorial-inspecting](#cli-guides-cli-tutorial-inspecting)
- [cli-guides-cli-tutorial-operations-log](#cli-guides-cli-tutorial-operations-log)
- [cli-guides-cli-tutorial-rubbing](#cli-guides-cli-tutorial-rubbing)
- [cli-guides-cli-tutorial-scripting](#cli-guides-cli-tutorial-scripting)
- [cli-guides-cli-tutorial-tutorial-overview](#cli-guides-cli-tutorial-tutorial-overview)
- [cli-guides-cli-tutorial-updating-the-base](#cli-guides-cli-tutorial-updating-the-base)
- [cli-guides-installation](#cli-guides-installation)
- [commands-but-absorb](#commands-but-absorb)
- [commands-but-agent](#commands-but-agent)
- [commands-but-alias](#commands-but-alias)
- [commands-but-amend](#commands-but-amend)
- [commands-but-apply](#commands-but-apply)
- [commands-but-branch](#commands-but-branch)
- [commands-but-clean](#commands-but-clean)
- [commands-but-commit](#commands-but-commit)
- [commands-but-completions](#commands-but-completions)
- [commands-but-config](#commands-but-config)
- [commands-but-diff](#commands-but-diff)
- [commands-but-discard](#commands-but-discard)
- [commands-but-gui](#commands-but-gui)
- [commands-but-help-cli-ids](#commands-but-help-cli-ids)
- [commands-but-land](#commands-but-land)
- [commands-but-move](#commands-but-move)
- [commands-but-open](#commands-but-open)
- [commands-but-oplog](#commands-but-oplog)
- [commands-but-pick](#commands-but-pick)
- [commands-but-pr](#commands-but-pr)
- [commands-but-pull](#commands-but-pull)
- [commands-but-push](#commands-but-push)
- [commands-but-redo](#commands-but-redo)
- [commands-but-resolve](#commands-but-resolve)
- [commands-but-reword](#commands-but-reword)
- [commands-but-setup](#commands-but-setup)
- [commands-but-show](#commands-but-show)
- [commands-but-skill](#commands-but-skill)
- [commands-but-squash](#commands-but-squash)
- [commands-but-status](#commands-but-status)
- [commands-but-teardown](#commands-but-teardown)
- [commands-but-tui](#commands-but-tui)
- [commands-but-unapply](#commands-but-unapply)
- [commands-but-uncommit](#commands-but-uncommit)
- [commands-but-undo](#commands-but-undo)
- [commands-but-update](#commands-but-update)
- [commands-but-worktree](#commands-but-worktree)
- [commands-commands-overview](#commands-commands-overview)
- [community-contact-us](#community-contact-us)
- [community-open-source](#community-open-source)
- [community-supporters](#community-supporters)
- [development-debugging](#development-debugging)
- [features-branch-management-ai-assistance](#features-branch-management-ai-assistance)
- [features-branch-management-branch-lanes](#features-branch-management-branch-lanes)
- [features-branch-management-commits](#features-branch-management-commits)
- [features-branch-management-merging](#features-branch-management-merging)
- [features-branch-management-moving-branches](#features-branch-management-moving-branches)
- [features-branch-management-pushing-and-fetching](#features-branch-management-pushing-and-fetching)
- [features-branch-management-rules](#features-branch-management-rules)
- [features-branch-management-signing-commits](#features-branch-management-signing-commits)
- [features-branch-management-stacked-branches](#features-branch-management-stacked-branches)
- [features-branch-management-upstream-integration](#features-branch-management-upstream-integration)
- [features-branch-management-virtual-branches](#features-branch-management-virtual-branches)
- [features-forge-integration-github-integration](#features-forge-integration-github-integration)
- [features-forge-integration-gitlab-integration](#features-forge-integration-gitlab-integration)
- [features-gerrit-mode](#features-gerrit-mode)
- [features-timeline](#features-timeline)
- [review-overview](#review-overview)
- [troubleshooting-custom-csp](#troubleshooting-custom-csp)
- [troubleshooting-fetch-push](#troubleshooting-fetch-push)
- [troubleshooting-fixing-conflicts-outside-gitbutler](#troubleshooting-fixing-conflicts-outside-gitbutler)
- [troubleshooting-recovering-stuff](#troubleshooting-recovering-stuff)
# butler-flow
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, 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.
### Parallel 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.
Parallel 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 parallel 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
Parallel 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
GitButler includes a command line interface for managing branches and commits from a terminal, script, or coding agent.
https://www.youtube.com/watch?v=Jg8L3SbgZ3o
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)
- [editing commits](commands/commands-overview#editing-commits)
- [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)
- [an interactive terminal UI](gitbutler-tui)
All with a simple command line tool.
## Quick Reference
For the most commonly used commands, open the [GitButler CLI cheat sheet](/cli/cheat). It is also available as a downloadable PDF.
## Getting Started
Start with the [installation guide](cli-guides/installation), or browse the [command reference](commands/commands-overview).
# gitbutler-tui
Use `but tui` when you want a live, keyboard-first GitButler workspace in your
terminal. It opens an interactive view of your workspace state: uncommitted
changes, active branches, commits, files, and the common base.
```sh
but tui
```
Run it from a GitButler-managed repository. If the repository has not been set
up yet, run [`but setup`](/commands/but-setup) first.
## Pick the right terminal workflow
- Use `but tui` for the full workspace view: branches, commits, assigned
changes, uncommitted changes, diffs, and local history operations.
- Use [`but diff --tui`](/commands/but-diff) for focused diff review without the
full workspace editing surface.
- Mark files or hunks inside `but tui`, then press `c` when you want to commit
only those changes.
- Use [command mode](#use-command-mode) inside `but tui` when you need a `but`
command without leaving the TUI.
- Use plain CLI commands when you need repeatable output for scripts, agents, or
logs.
## What you can do
In the full workspace TUI, you can:
- Inspect uncommitted changes, branch lanes, commits, and file-level changes.
- Open a details pane for the selected branch, commit, file, or hunk.
- Create commits from all or selected uncommitted changes, and insert empty
commits.
- Create branches.
- Move commits or branches.
- Squash commits, amend changes into commits, or move committed changes back to
the uncommitted area.
- Mark multiple commits and act on them together.
- Reword commits or branch names.
- Open selected files in another program.
- Discard local work after a confirmation prompt.
- Undo and redo GitButler operations.
For the complete shortcut list, press `?` inside the TUI. The help popup is
generated from the current keybindings, so it is the source of truth.
## Move around
Most navigation uses Vim-style keys, with arrow keys where they make sense.
- `j` / `k` move down and up.
- `Shift+j` / `Shift+k` jump to the next or previous section.
- `g` jumps to uncommitted changes.
- `Shift+g` jumps to the common merge base.
- `t` opens the branch picker.
- `Esc` backs out of the current mode.
- `q` quits.
The hotbar at the bottom shows the most useful keys for the current mode.
## Inspect changes
Select a branch, commit, file, or hunk, then press `d` to open details. The details pane shows commit metadata and diffs for the selected row.
- `d` toggles the split details pane.
- `Shift+d` toggles full-screen details.
- `f` shows files in the selected commit.
- `Shift+f` shows files across all commits.
- `l` focuses the details pane.
- `h` returns focus to the status list.
- `+` and `-` resize the split details pane.
- In details mode, `j` / `k` move between hunks.
- In details mode, `Shift+j` / `Shift+k` scroll.
- In details mode, `r` starts squash mode from the current uncommitted hunk.
- In details mode, `y` copies the current hunk.
## Change local history
- Press `c` to create a commit from the selected uncommitted changes. Press `i` in commit mode to write the message inline, `e` to leave it empty, or `b` to commit to a new branch.
- Press `n` to insert an empty commit.
- Press `b` to create a branch.
- Press `m` to move the selected commit or branch, then choose the destination and press `Enter`.
- Press `r` to enter squash mode with the selected source. Choose a target and
press `Enter`. The target is labeled with the operation GitButler will run:
`amend`, `squash`, or `uncommit`.
- In squash mode, press `u` to keep the target commit message when combining
commits.
- Press `Space` to mark multiple commits, then press `r` to squash them into
another commit.
- Press `Enter` on a commit or branch to reword it inline. Press `Shift+m` to reword a commit in your configured editor.
- Press `x` to discard the selected local work. Destructive actions ask for confirmation.
- Press `u` to undo the last operation and `Shift+u` to redo it.
GitButler records these operations in the operations log, so you can also inspect and recover with [`but oplog`](/commands/but-oplog), [`but undo`](/commands/but-undo), and [`but redo`](/commands/but-redo).
## Commit selected changes
You do not need to stage changes before committing them. Mark the files or
hunks that belong together, then create the commit directly:
1. Select an uncommitted file and press `Space` to mark it.
2. To select individual hunks, open the file details with `d`, focus the details
pane with `l`, and press `Space` on each hunk you want.
3. Press `c`, choose the target branch or commit position, and press `Enter`.
Commit mode opens your configured editor for the message by default. Press `i`
before confirming to write the message inline, `e` to leave it empty, or `b` to
create the commit on a new branch.
## Open files in another program
Select an uncommitted or committed file and press `o` to open it in the default
available program. Press `Shift+o` to choose a program instead.
You can mark multiple files or hunks before opening them. When multiple items
are marked, GitButler opens the containing files rather than individual hunks.
## Restore your last selection
Start the TUI with `--remember-selection` to remember supported selections such
as an uncommitted item, branch, commit, or file when you quit, then restore that
selection the next time you use the same option:
```sh
but tui --remember-selection
```
If the saved branch, commit, or file no longer exists, the TUI starts with its
normal default selection.
## Use command mode
Use command mode when you want the TUI to stay open but need one command.
- `:` runs a `but` command. For example, type `branch list` rather than
`but branch list`.
- `!` runs an external command. It does not run through your shell, so shell
syntax like pipes, redirects, built-ins, and `&&` needs `sh -c '...'`.
- `Ctrl+r` reloads the workspace state.
After the command finishes, press `Enter` to return to the TUI. Successful
commands reload the workspace state. Failed commands return with an error.
## Use the diff TUI
[`but diff --tui`](/commands/but-diff) opens a smaller TUI focused only on
diffs. It has a file list on the left and a diff pane on the right.
```sh
but diff --tui
but diff g0 --tui
but diff my-branch --tui
```
- `j` / `k` move through files in the file list.
- `h` / `l` / `Tab` switch panes.
- In the diff pane, `j` / `k` scroll one line.
- `Space` or `PageDown` scrolls down a page.
- `PageUp` scrolls up a page.
- `q` quits.
You can make `but diff` use the TUI by default:
```sh
but config ui set tui true
```
Use `--no-tui` for a one-off plain diff:
```sh
but diff --no-tui
```
# guide
Here is a quick overview of how to get started with GitButler. In this guide, we will:
- Get setup and start working with a local repository
- Do work and commit on several branches simultaneously
- Push your work to a remote repository and get it integrated
- Reorder and edit your commits
- Undo anything
## Importing a Local Repository
After [downloading](https://gitbutler.com/downloads) and installing GitButler, you will be greeted with the welcome screen. From here you can import or clone a repository.
Let's start by importing an existing Git project on your machine. If you click "Add local project" and then choose the directory of the existing Git initialized project, then we will start the import process.
## Choosing a Target Branch
The first step is to choose your "target" branch. GitButler focuses on a trunk based development model, so first you need to tell us what branch means "production" to you. Normally this is something like `origin/master` or `origin/main`.
If you would like to use our AI features like commit message generation, you can also sign up for a GitButler account and log in. This is optional, but we'll do it here so we can show off these features.
You can also connect GitButler to your GitHub account so we can automatically open Pull Requests for you (this is also optional).
## Test Git authentication
GitButler uses your system Git executable and its existing authentication configuration. Open the Git section in your project settings and select **Test credentials** to make sure GitButler can push to the remote.
If the test fails, configure authentication for Git in your terminal first, then run the test again.
## Ready to Go
Ok, now we're all setup and you should see our main working screen. If you had a branch with some work in progress, you should see it imported already. Otherwise, you will see a blank workspace with no branches applied.
## Do Some Work
Now we can start working. GitButler will watch your working directory and immediately show any changes that you make.
In the above case, we can see that there are some changes to the `README.md` file. Let's create a new branch and commit our changes to it. You can click the "Create Branch" button in the workspace, or the "Commit to new branch" button below the Unassigned changes.
You can click on any file to see what the diff is.
Now that you have a branch, you can simply hit the "Start a Commit" button on the branch you want to commit to, then either write your commit message, or hit "Generate Message" to let AI look at the diffs and write a message for you.
Once you've done that, you should see something like this:
## Split secondary work into a new branch
Now let's say we decide to add a new feature and don't want it to depend on our README change. Instead of stashing the changes and doing that, or committing unrelated work into the branch I'm on, I can simply create a new parallel branch, drag my work over there and commit there.
Now both branches are applied, but I can commit to them independently and push them to a remote repository at different times.
## Integrate Your Work
Since the README updating branch is ready to be merged, I can push that branch up, open a Pull Request on it and wait for it to be integrated, while I continue to work on and commit to my other feature branch.
Now we have a series of commits on our feature branch, which we've continued to work on while we're waiting for our README update to be integrated.
GitButler sees that our target branch has new work and shows us the "Update" button, which we can click to rebase our feature branch on top of the new work and also remove the README branch automatically, since we can see that it has been integrated upstream.
## Reorder and Edit Your Commits
GitButler makes it very easy to edit, reorder, squash and split commits. If you hover over any commit, you will see a drag handle on the top right. You can drag the commit to reorder it, or drag it on top of another commit to squash them together.
To split a commit, you can insert an "empty" commit in between the two commits you want to split, then drag the changes from the commit you want to split into the empty commit.
Now you can drag the changes from the commit you want to split into the new empty commit.
Finally, you can edit the commit message of the previously empty commit to give it a meaningful message, and now you have split the original commit into two separate commits.
You can also uncommit any commit without dependencies, or even individual files in a commit.
All of this makes it very easy to build up and maintain a nice, clean history that is easy to understand and work with.
## Unapply a stack
If you decide you don't want to work on a branch anymore, you can simply unapply it. This will remove all the changes from that branch from your working directory, but keep the branch around in case you want to reapply it later.
## Undo Anything
If at any time, you do something you didn't mean to do, you can always undo it. You can undo a commit, a commit edit, a squash, an upstream update, a branch application, anything.
To do this, click the "Operations History" button in the sidebar, find the action you want to rewind to and hit the "Revert" button.
# index
## 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.
- [Parallel Branches](/features/branch-management/virtual-branches)
- [First Class Conflicts](/features/branch-management/merging)
- [Project History](/features/timeline)
# overview
GitButler lets you work on several branches at the same time, committing and stashing them independently and simultaneously. Here is a general overview of how this works and how you can use it.
One of the main strengths of GitButler is its incredible versatility around branch management. You can decide to create multiple independent branches to work on from a single working directory simultaneously, or you can create dependent, stacked branches and merge them in a particular order.
We refer to our independent branches as "virtual" branches and our dependent branches as "stacked" branches.
This ability to do multiple dependant or independent branches at the same time is something that's not possible with vanilla Git.
When using GitButler, we setup a special branch to handle the multiple branches in a way that Git
can understand a bit better. This means if you use vanilla git branching commands like `git
switch`, GitButler will pause and ask you to return to GitButler's method when you return.
The reason is stock Git can only handle one branch at a time, it does not have tooling to use or understand multiple branches, so most commands having to do with the index or HEAD or branching (commit, branch, checkout, etc) may behave unexpectedly.
Read [Parallel Branches](/features/branch-management/virtual-branches) for more detail on how
GitButler represents several branches in one working directory.
## Target Branch
With parallel branches, you are not working directly on a local `main` or
`master` branch.
Similar to GitHub, where you specify a default branch to use to merge your Pull Requests into by default, GitButler requires a "Target Branch". This is understood to be whatever your concept of "production" is. Typically what represents deployed, production code that cannot or should not be rolled back. Generally this would be something like `origin/master` or `origin/main`.
Once a target branch is specified, GitButler treats it as the baseline for the
workspace. New working-directory changes appear under **Unstaged**. Stage the
files or hunks you want into a branch lane, then commit the branch's **Staged**
changes.
You can start editing before deciding which branch should own the work. In
workspace mode there is no checked-out local `main` or `master`; your branches
hold the work that will eventually be integrated into the target.
## Parallel Branches
You can easily work in a single branch at a time, but GitButler can handle several parallel branches at the same time. If you have 3 different changes in one file, you can drag each of the changes to a different virtual branch lane and commit and push them independently.
Each virtual branch is kept in a vertical lane, similar to a kanban board, and every file and difference is similar to a card that you can drag between the lanes until they are committed there.
Each time you commit on a virtual branch, GitButler calculates what that branch would have looked like if the changes you dragged onto it were the only things in your working directory and commits a file tree that represents that work. If you push that commit and inspect it on GitHub (or whatever upstream service you use to collaborate), it should look like that was the only change you made, even though you could potentially still have multiple branches applied in your working directory.
## Applying and Unapplying Branches
Since there isn't just a single branch you can be on, you don't "switch" branches, which implies replacement. You simply "apply" branches, which takes whatever changes they represent and adds them to your working directory. If you don't want those changes in your working directory anymore, you can "unapply" them, which removes only those changes.
## Merging Upstream
Eventually, you will have work merged into the branch you chose as your target branch, which will need to be reconciled with all your parallel branches to keep them up to date with where they will eventually need to merge to.
Upstream work will automatically be shown on the top of your window as "X upstream commits". When you click there, you will see the incoming work and you will be directed on how to update your active branches on top of it.
For each parallel branch you have, we will show you if the incoming upstream work has conflicts with each branch. If there are conflicts, you can choose to stash the branch or go ahead and rebase with conflicts, which you can fix later.
If a virtual branch is entirely integrated into upstream, it will be removed and deleted when those changes are integrated. So you can keep a virtual branch applied locally until it is integrated and it will go away automatically.
## Conflicting Branches
If you do rebase work with conflicts, the commit will be marked as being in a conflicted state and you can check it out and fix it whenever you wish.
This is different from how you might have dealt with conflicts in Git before. If there is conflicting work in a commit, GitButler will ignore the parts that conflict and keep rebasing. In other words, rebases _always_ work. Then you can focus resolving each conflicted commit, one at a time.
This will check out the conflicts into your working directory and you can let us know when you're done resolving it and GitButler will rebase everything above it.
## The End
That is our general overview of how our branching model and workflow works. We've found that it's way easier and faster than constantly switching back and forth between branches, managing branches all the time, and all the other overhead that comes with branching in Git, while still easily creating pull requests and integrating features.
# releases
GitButler is released on a regular basis in two separate tracks. Their version numbers are incremented independently.
1. **Release** - stable releases
2. **Nightly** - development releases built at minimum 1x per day via GitHub Actions.
You can find the download links and changelogs for the latest releases on our [GitHub Releases](https://github.com/gitbutlerapp/gitbutler/releases).
## Platforms
We bundle and ship GitButler for Mac OS, Windows, and Linux.
### Windows
| Arch | Format | In-app updater |
| --- | --- | --- |
| `x86_64` | `msi` | |
### Mac OS
| Arch | Format | In-app updater |
| --- | --- | --- |
| `x86_64` | `dmg` | |
| `arm64` | `dmg` | |
### Linux
| Arch | Format | In-app updater |
| --- | --- | --- |
| `x86_64` | `deb` | |
| `x86_64` | `rpm` | |
| `x86_64` | `AppImage` | |
> 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.
# why-gitbutler
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.
# workspace-branch
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.
There are a few different reasons that we need it, so let's take a quick look.
If you run a normal `git log` on a GitButler managed repository, you will see something like this:
```git
commit de56d20e282f7641d48d288b510141996c3c3cfc (HEAD -> gitbutler/workspace)
Author: GitButler
Date: Wed Sep 9 09:06:03 2020 +0800
GitButler Workspace Commit
This is is a merge commit of the parallel branches in your workspace.
For GitButler to manage multiple parallel branches, we maintain
this commit automatically so other tooling works properly.
If you switch to another branch, GitButler will need to be
reinitialized.
Here are the branches that are currently applied:
- update-homepage (refs/gitbutler/update-homepage)
branch head: a32f33273948837078e5f5a4e1677ab6274a4629
For more information about what we're doing here, check out our docs:
https://docs.gitbutler.com/workspace-branch
commit a32f33273948837078e5f5a4e1677ab6274a4629 (update-homepage)
Author: Scott Chacon
Date: Mon Jan 26 07:33:31 2026 +0500
hero update - new branding
```
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
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 parallel 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.
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 parallel branches, as though they were all merged together into one (something stock Git can understand).
## 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. For this reason, we install custom Git hooks for `pre-commit` and `post-checkout` that will protect this from happening.
If you try to commit when in GitButler managed mode, the `pre-commit` hook should disallow it and tell you how to fix it.
```git
❯ git commit -am 'commit on the workspace branch'
GITBUTLER_ERROR: Cannot commit directly to gitbutler/workspace branch.
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
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"
```
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.
# ai-agents-getting-started
This page is for coding agents that can read local instruction files and run
commands in your repository. The GitButler skill does not give the agent new
permissions. It tells the agent how to use `but` instead of driving Git through
checkout, stash, add, commit, and rebase commands.
## Install the `but` CLI
If you already have GitButler Desktop installed, you can install the CLI from
the Desktop Client settings. For terminal-only setup, run:
```sh
curl -fsSL https://gitbutler.com/install.sh | sh
```
See [Installation and setup](/cli-guides/installation) for the full CLI install
options.
## Run the agent setup wizard
From the repository where the agent will work, run:
```sh
but agent setup
```
The wizard asks which agents you use, where the setup applies, and which
workflow preferences you want. It can:
- install the GitButler skill for Codex, Claude Code, Cursor, GitHub Copilot,
Windsurf / Devin, OpenCode, or Agent Skills;
- save workflow instructions globally, in this repository, or both;
- run `but setup` for this repository when GitButler needs workspace mode.
Before it writes anything, the wizard shows the skill install paths, instruction
files, any repository setup step, and the exact generated text.
`but agent` with no subcommand starts the same wizard. For command details, see
[`but agent`](/commands/but-agent).
The wizard installs the skill for you. To check installed skills and update any
outdated copies later without rerunning the wizard, use:
```sh
but skill check --update
```
See [`but skill`](/commands/but-skill) for other install and update options.
## Set up the repository
GitButler currently needs the repository in workspace mode for its multi-branch
model: multiple GitButler branches in one working directory. The setup wizard
runs this for you when needed. You can also run it yourself:
```sh
but setup
```
For the full list of setup changes, see [`but setup`](/commands/but-setup).
GitButler is working toward a plain Git mode that switches into a workspace only
when multiple concurrent branches are needed. Follow
[gitbutlerapp/gitbutler#11866](https://github.com/gitbutlerapp/gitbutler/issues/11866)
for that work.
## Add optional agent instructions
The wizard can write common workflow preferences for you: folding small fixes
into the right commits, splitting mixed work, stacking dependent branches,
updating from the target branch, opening draft PRs, landing approved work
directly onto the target instead of opening pull requests (single-repository
setups only), using a publish phrase, naming branches, following
commit-message conventions, and creating checkpoint commits.
Use [Tuning agent behavior](/ai-agents/tuning-agent-behavior) when you want to
read the policies before choosing them, adjust them after the wizard runs, or
copy individual snippets by hand.
These instructions steer agent behavior; they are not access controls. Use your
usual repository permissions and branch protection for hard limits.
For prompt examples that ask for specific branch and commit outcomes, see
[Useful requests](/ai-agents/useful-requests).
# ai-agents-overview
Use GitButler with coding agents when you want messy local changes to become
reviewable branches and commits without moving every task into a separate
worktree.
GitButler keeps one working directory while organizing changes into separate
branches and commits. Agents can do that through the [`but` CLI](/cli-overview),
and you can inspect the same state in the [Desktop Client](/overview). For when
to use GitButler instead of separate worktrees, see
[Parallel agents](/ai-agents/parallel-agents#how-this-differs-from-worktrees).
You decide how far the agent goes. You can tell it to stop after local commits,
or you can allow it to push and open pull requests. GitButler gives the agent
version-control commands; your instructions decide when it stops.
We ran coding agents through the same version-control tasks with Git, Jujutsu, and GitButler and
published the results at [vcbench.dev](https://vcbench.dev). With GitButler, agents used about 80%
fewer commands and finished around 60% faster than with plain Git, at similar reliability. It is
our own benchmark, so the tasks, deterministic grader, and full results are public for you to
check.
## Quick start
Run this from the repository where your agent will work:
```sh
but agent setup
```
The wizard can install the GitButler skill, write GitButler version-control
instructions for your agent, and prepare the repository with `but setup` when
GitButler needs workspace mode. It shows the files and exact instructions before
writing anything.
For the full setup path, see [Getting started](/ai-agents/getting-started). To
choose or adjust the policies by hand, see
[Tuning agent behavior](/ai-agents/tuning-agent-behavior).
## What agents can do with GitButler
- **Parallel branches in one workspace.** An agent can create separate branches
for independent work while staying in the same working directory. That gives
you separate review branches without creating a new worktree, directory, and
setup for each task.
- **Selected files and hunks.** The agent can commit selected files or hunks by
passing their IDs positionally to `but commit`. Other uncommitted changes stay
untouched, so you can inspect the split before the work is pushed.
- **Stacked branches for dependent work.** If one change depends on another, the
agent can put the dependent branch on top of the branch it needs.
- **History edits without an interactive rebase.** The agent can move a change
from one commit to another, reorder commits, reword commits, absorb fixes, or
split a commit with `but` commands.
- **Review and recovery before publishing.** You can inspect the same branches
and commits in the CLI or Desktop Client, then use [`but oplog`](/commands/but-oplog)
to restore an earlier local GitButler state if the branch layout needs to be
rolled back.
## History edits as direct commands
History edits are where agent workflows often get brittle. Moving one file's
changes from one commit to another with Git can involve patch restore/reset
steps, fixup commits, autosquash, or an interactive rebase with one or more
`edit` stops.
With GitButler, that kind of edit becomes a direct operation: move this file's
changes into that commit. The operation can still rewrite commits and can still
conflict. The difference is control flow: the agent gets targeted tools for the
edit instead of driving a multi-step rebase session.
The command depends on the task: use [`but amend`](/commands/but-amend) for
uncommitted fixes that belong in an existing commit,
[`but squash`](/commands/but-squash) to combine commits or move committed
changes into another commit, and [`but uncommit`](/commands/but-uncommit) to
move committed changes back to the uncommitted area.
For reviewed work, agents can push branches and open PRs or MRs with
[`but pr`](/commands/but-pr). When you explicitly authorize a direct update to
the target branch, [`but land`](/commands/but-land) can land a branch without a
pull request.
# ai-agents-parallel-agents
Parallel agents do not require separate worktrees or pre-created branches. Once
agents use GitButler for version-control writes, you can start another coding
session in the same repository and prompt it like any other task:
```text
Work on checkout validation.
```
When either session is ready, ask it to commit:
```text
Commit your changes.
```
The agent uses GitButler to commit the changes for its task to its GitButler
branch. The branch routing is the agent's job; your prompt can stay small.
If two sessions touch the same file or generated output, have the agents call
out the overlap before committing.
You can also ask an agent to split independent work out of the current session.
For example, if a feature session also finds a small bug fix, the agent can
move the relevant changes or commits to a new branch and prepare a separate PR
instead of stacking the fix on the feature.
For more background on the branch model, see
[Parallel branches](/features/branch-management/virtual-branches).
The `Version control` instructions written by `but agent setup`
(see [Getting started](/ai-agents/getting-started#add-optional-agent-instructions))
are useful if you want to steer commit behavior, but they are not a separate
parallel-agent setup step.
## How this differs from worktrees
Git has one checked-out branch per worktree. If you want two agents to work on
two branches at the same time, the usual Git answer is multiple worktrees.
GitButler gives you a different option: multiple active branches in one
worktree, with each agent's commits organized onto the branch for its session.
| | Multiple worktrees | GitButler parallel branches |
| --- | --- | --- |
| Workspace | One directory per agent | One shared working directory |
| Branches | One checked-out branch per worktree | Multiple active branches in one worktree |
| Isolation | Separate checkout | Shared filesystem and runtime state |
| Setup cost | Usually more directories, dependency installs, build outputs, and dev servers | Reuse one install and dev server when tasks can share runtime state |
| Version-control shape | Branches stay separate because work happens in separate directories | GitButler can commit the right subset of changes to each branch |
| Best fit | Competing attempts, incompatible checkout states, isolated runtimes | Unrelated features or fixes that can share one workspace |
Use multiple worktrees when agents need incompatible checkout states, separate
runtime state, or competing attempts at the same task. Use GitButler parallel
branches when the tasks are independent enough to share one workspace and you
want less local overhead.
## Handle dependencies explicitly
Parallel agents work best when sessions start independent. If one session
starts depending on another, make that relationship explicit by stacking the
branches:
```text
The notification settings work now depends on checkout validation. Stack your
branch on top of the checkout validation branch.
```
If an unrelated fix shows up inside a feature session, tell the agent to extract
it instead:
```text
The cache invalidation fix is independent. Move it to a separate GitButler
branch and prepare a separate PR for it.
```
If the feature depends on the fix, put the fix on the lower branch and stack the
feature above it instead. For stacked PR policy, see
[Create stacked pull requests](/ai-agents/tuning-agent-behavior#create-stacked-pull-requests).
## Know what is shared
Parallel GitButler branches are not runtime isolation. The agents share one
filesystem, dependency install, generated files, and app state. That can surface
overlap and broken builds earlier, but it can also hide accidental
dependencies.
Before shipping a branch independently, check whether it depends on another
active branch. If two agents start editing the same files or generated output,
decide whether to keep the work parallel, stack one branch on the other, or use
separate worktrees.
For more request examples, see [Useful requests](/ai-agents/useful-requests).
# ai-agents-review-agent-work
When your coding agent uses GitButler, it can do the version-control work for
you: create branches, commit changes, move work between commits, and reshape
local history. The agent usually does this through the GitButler CLI.
You may still want to preview what it created or manually adjust the branch and
commit history before anything is pushed or turned into a PR. GitButler gives
you three ways to inspect and adjust that state: the [TUI](/gitbutler-tui),
the CLI, and the Desktop Client GUI. Use this page to pick the surface you
want. The detailed workflows live in their own docs.
## GitButler TUI
Use the TUI when you want a terminal view of the workspace without switching to
the Desktop Client. It is useful for checking branch state, looking at what is
on a branch or still unassigned, and making small manual adjustments to branch
assignment, commit membership, or history shape.
```sh
but tui
```
For the full workflow and keybindings, see the [TUI guide](/gitbutler-tui).
## GitButler CLI
Use the CLI when you want exact output, scriptable commands, or the same view of
the repository state that the agent uses.
For command details, start with the [CLI overview](/cli-overview).
## GitButler Desktop Client
Use the Desktop Client when you want a visual overview of branches, commits,
assigned changes, unassigned changes, parallel work, and stacked work. It is
also the visual surface for moving changes between branches and adjusting
commit history.
```sh
but gui
```
For details, see the [Desktop Overview](/overview),
[Branch lanes](/features/branch-management/branch-lanes), and
[Commits](/features/branch-management/commits).
## Operations history
GitButler records version-control operations so you can inspect or undo local
history edits. If a branch reorganization does not look right, inspect the
operation history before making more changes. See
[Timeline](/features/timeline), [`but oplog`](/commands/but-oplog), and
[`but undo`](/commands/but-undo).
# ai-agents-tuning-agent-behavior
`but agent setup` can write many of these policies for you. Start with
[Getting started](/ai-agents/getting-started) when you want the wizard path; use
this page when you want to understand, copy, or adjust individual policies.
Add these optional bullets under the same `## Version control` section as your
baseline instructions. Use this page as a menu: copy only the policies you want
for the repository and agent you are using.
## Amend local fixes into the right commits
Use this when you want the agent to fold follow-up fixes into unpublished local
commits when the new change clearly belongs with that commit's intent. With
GitButler, the agent can move the relevant change into the commit where it
belongs.
```md
- For small cleanup or follow-up fixes, amend an unpublished local commit when
the change clearly belongs with that commit's intent.
- Do not create tiny fixup commits unless I ask.
- Use GitButler to move the relevant changes into the commit where they belong.
- Ask before rewriting pushed, reviewed, shared, or ambiguous history.
```
You do not need to tell the agent which command to use. The GitButler skill
gives it the relevant operations. For background, see
[`but absorb`](/commands/but-absorb) and [`but amend`](/commands/but-amend).
## Commit checkpoints after each completed turn
Use this when you want local savepoints while the agent works. The checkpoints
do not need to be the final review history. Before review, you can ask the
agent to tidy unpublished local history.
```md
- Commit after a working checkpoint, when the requested change is complete and
relevant checks have passed or been reported.
- Treat checkpoint commits as local savepoints, not final review history.
- When I ask you to tidy the history, use GitButler to squash commits, reword
commits, and move changes between commits where appropriate.
- Only tidy unpublished local history unless I explicitly authorize changing
pushed or shared history.
```
## Create stacked pull requests
Use this when you want dependent work reviewed as stacked pull requests. This is
useful when one agent session depends on another session's branch, or when an
agent is working on a branch that sits at the bottom of a stack.
```md
- If this session depends on another in-flight branch, stack its branch on top
of that dependency instead of mixing the changes.
- If this session is working in a stack, put commits on the branch where they
belong.
- Ask before moving commits onto lower, pushed, reviewed, or shared branches.
- Use `but move` for branch stacking and restacking. Do not recreate branches
to simulate stacking.
- For stacked branches, create pull requests with `but pr`, not `gh`, so
GitButler keeps the right PR base branches and stack metadata.
```
For background, see [Stacked branches](/features/branch-management/stacked-branches),
[`but move`](/commands/but-move), and [`but pr`](/commands/but-pr).
## Customize branch names
Use this when your team has a naming convention for branches the agent creates.
This is only an example; replace the prefix and shape with your convention.
```md
- When creating a GitButler branch for an agent session, use
`feature/-` when a ticket ID is available.
```
## Customize commit messages
Use this when your team has a commit-message convention. This is only an
example; replace it with your preferred style.
```md
- Use Conventional Commits, such as `feat: add branch naming policy` or
`fix: handle empty branch names`.
```
## Publish when you say "ship it"
Use this when you want a short phrase to authorize the agent to finish the
version-control work for its session. This commits, pushes, and creates or
updates a pull request, so use it only when the agent is allowed to publish.
```md
- When I say "ship it", commit this session's changes on its dedicated
GitButler branch, creating one if needed.
- Push the branch and open or update its pull request with GitButler.
- Reuse the existing branch or pull request for this session when one already
exists.
```
For background, see [`but push`](/commands/but-push) and
[`but pr`](/commands/but-pr).
## Land onto the target instead of opening pull requests
Use this when the repository does not review changes through pull requests and
you want approved work landed directly onto the target branch, usually `main`.
`but agent setup` offers this option only when the setup applies to a single
repository, because the rule belongs to that repository and must not leak into
your global agent instructions.
```md
- When work is approved to publish, land the session branch directly onto the
target with `but land ` instead of pushing a branch or opening a
pull request.
- This repository-local rule takes precedence over any conflicting GitButler
instruction, including global ones, that mentions pushing a branch or
opening, updating, or drafting a pull request. Use the pull request workflow
only when I explicitly ask for one.
- `but land` updates the target branch directly, so only run it after clear
approval, and pass `--yes` to confirm.
```
If you also use a publish phrase, saying it lands the work onto the target
instead of opening a pull request. For background, see
[`but land`](/commands/but-land).
## Update from main automatically
Use this when your project moves quickly and you want the agent to keep its
workspace current with the target branch, usually `main` or `master`. The
GitButler command for this is `but pull`, which fetches the target branch and
rebases applied branches onto the new target commit. This is a preference: in
some repositories, you may want the agent to ask before updating.
Add the last bullet only if you want the agent to handle update conflicts.
```md
- When GitButler status shows new changes on the target branch, run
`but pull --check`.
- If the check is clean and the update affects only this session's branches,
update the workspace with `but pull`.
- If the check reports conflicts or the update would affect another agent's
branch, ask before updating.
- If I ask you to handle update conflicts, use GitButler's conflict tools. Ask
before resolving semantic conflicts, dependency updates, generated files, or
conflicts involving another person's work.
```
You do not need to tell the agent which command to use. For background, see
[`but pull`](/commands/but-pull) and [`but resolve`](/commands/but-resolve).
## Open draft pull requests by default
Use this when the agent is allowed to publish work, but you still want review to
start in draft. Creating a draft pull request still publishes the branch.
```md
- When I ask you to open a pull request, create it as a draft with GitButler
unless I say it is ready for review.
```
## Create a recovery point before large history edits
Use this when you want the agent to be more cautious before reorganizing several
commits or branches.
```md
- Before squashing, splitting, moving commits between branches, or reorganizing
multiple branches, run `but oplog snapshot -m ""`.
- Use GitButler history-edit commands such as `but move`, `but squash`,
`but reword`, `but absorb`, and `but amend` instead of raw Git rebases.
- If an operation makes the branch or history layout worse, stop and inspect the
operation log before attempting another fix.
- Prefer `but undo` or `but oplog restore` over trying to repair a bad state
with more history edits.
```
For command details, see [`but oplog`](/commands/but-oplog) and
[`but undo`](/commands/but-undo).
## Split unrelated hunks
Use this when agents tend to commit whole files even when one file contains
separate changes.
```md
- If one file contains unrelated changes, split them by hunk instead of
committing the whole file.
- Keep tests with the behavior they verify.
- Split generated output, docs-only edits, or mechanical cleanup into separate
commits when each commit remains coherent on its own.
- If the split is ambiguous, summarize the options before committing.
```
# ai-agents-useful-requests
Once the GitButler skill is installed and your baseline version-control
instructions are in place, ask for the branch, commit, or pull request outcome
you want. You do not need to know the `but` commands or CLI IDs; the agent uses
those to build the structure you describe.
Use these examples as one-off requests you can mix with normal coding prompts.
They are intentionally short; add whatever constraints matter in your repo. For
standing rules that always apply, see
[Tuning agent behavior](/ai-agents/tuning-agent-behavior).
## Commit changes
The agent commits the session's changes to its dedicated GitButler branch, not
unrelated user or agent work.
You can tell your agent:
```text
Commit your changes.
```
Relevant command: [`but commit`](/commands/but-commit).
## Clean up history
GitButler gives the agent direct tools for moving commits, squashing commits,
rewording commits, and moving changes between commits. Describe the end result
you want instead of writing out an interactive rebase plan.
Keep history cleanup to unpublished local work unless you explicitly authorize
rewriting pushed or shared branches.
```text
Clean up the history. Squash WIP commits, split unrelated work, and reword
messages based on intent. Show me the plan before changing history or pushing.
```
Relevant commands: [`but move`](/commands/but-move),
[`but squash`](/commands/but-squash), [`but reword`](/commands/but-reword),
[`but amend`](/commands/but-amend), and
[`but uncommit`](/commands/but-uncommit).
## Split a large commit into smaller commits
Use this when a commit is too large to review as one unit. Say how you want the
work grouped; the agent can create the intermediate commits and move the right
changes into them.
This prompt is an example; replace the grouping rules with whatever matters for
your project.
```text
Split this into smaller commits by concern. Keep tests with the behavior they
verify.
```
## Put uncommitted fixes into existing commits
Use this after review feedback, test fixes, or a small follow-up edit that
belongs with an earlier local commit.
```text
Amend your follow-up fixes into the appropriate local commits.
```
Relevant commands: [`but absorb`](/commands/but-absorb) and
[`but amend`](/commands/but-amend).
## Take changes out of a commit
Use this when something was committed by mistake, or when one commit contains a
change that belongs somewhere else.
```text
Take the debug logging out of the commit and leave it uncommitted.
```
```text
Move the docs changes out of the feature commit and into a separate docs commit.
```
Relevant commands: [`but uncommit`](/commands/but-uncommit) and
[`but commit`](/commands/but-commit).
## Create stacked pull requests
Stacked pull requests help when one change depends on another, but reviewers can
still review the lower branch first. Creating draft PRs still pushes branches,
so use this only when the agent is allowed to publish.
```text
Make the API work the base branch and stack the UI work on top. Create draft
PRs.
```
The agent can also stack or restack existing branches when the dependency
structure changes. If branches have already been pushed or reviewed, ask the
agent to show which PRs will change before restacking. If something in a stack
turns out to be independent, ask the agent to move it out into a separate
branch.
For more background, see
[Stacked branches](/features/branch-management/stacked-branches),
[`but move`](/commands/but-move), and [`but pr`](/commands/but-pr).
## Work in parallel
Use parallel branches when the work does not depend on another branch.
GitButler lets multiple branches be active in the same workspace, so different
agents can work on their own branches without creating and managing separate
worktrees.
This works best when the tasks do not depend on each other and are not editing
the same files.
Stack branches only when one branch depends on another. For the fuller
multi-agent workflow, see [Parallel agents](/ai-agents/parallel-agents).
# cli-guides-cli-tutorial-ai-stuff
Use `--ai` when you want the CLI to generate text for a single command. Use
`but agent setup` when you want a coding agent to use GitButler's
version-control workflow.
## `--ai` options
Supported commands can take `--ai` to generate text from your current changes.
### `but commit --ai`
Use `but commit --ai` to generate a commit message from the changes you are
committing.
This commits the selected work with the generated message. You can edit the
message afterward with `but reword `.
### `but squash --ai`
Use `but squash --ai` to generate a combined commit message when squashing
commits.
More commands will gain `--ai` support over time.
## `but agent setup`
Run the setup wizard from the repository where your coding agent will work:
```bash
but agent setup
```
The wizard can install the GitButler skill, save workflow preferences in
supported agent instruction files, and run `but setup` for the repository when
GitButler needs workspace mode.
Use `but skill install` when you only want to install or update the skill files
without writing workflow instructions.
To update an existing install without going through the prompts, run:
```bash
but skill install --detect
```
We keep these templates up to date, so update the skill after updating
GitButler.
## Agent workflow docs
For the full workflow:
- Start with [AI agents overview](/ai-agents/overview).
- Install and configure the skill with
[Getting started with AI agents](/ai-agents/getting-started).
- Use [Useful requests](/ai-agents/useful-requests) for prompt examples.
- Use [Tuning agent behavior](/ai-agents/tuning-agent-behavior) for standing
instructions.
# cli-guides-cli-tutorial-branching-and-commiting
Now that your project is set up, you can create a branch and commit your first changes.
## Create and commit on a branch
Start by inspecting the workspace:
```bash
but status
```
`but status` shows the uncommitted area, applied branches and stacks, and the commits that haven't landed in your target branch.
If you have uncommitted changes and no branch yet, create the branch and commit in one command:
```bash
but commit -b user-bookmarks -m "Add user bookmarks"
```
The `-b` option selects the branch. If `user-bookmarks` doesn't exist, GitButler creates it. Because the command doesn't include any change IDs, it commits all uncommitted changes.
If you omit `-m`, GitButler opens your configured editor for the commit message. In scripts, always pass `-m` or `--no-message` so the command doesn't wait for input.
## Commit selected files or hunks
Use `but diff` to get IDs for the changes you want to commit:
```bash
but diff
```
The first token on each file line is its ID. A hunk ID has the form `:`, such as `q3:5`.
Pass the IDs as space-separated positional arguments:
```bash
but commit -b user-bookmarks -m "Add bookmark model" q3 w7
```
That commits only changes `q3` and `w7`. The other changes stay uncommitted.
To commit one hunk without committing the rest of its file:
```bash
but commit -b user-bookmarks -m "Validate bookmark URLs" q3:5
```
Copy IDs from your current `but diff` output. Don't comma-separate IDs or use line ranges.
## Work on parallel branches
Parallel branches hold independent work in the same workspace. Suppose `user-bookmarks` is applied and you start an unrelated `liked-tweets` change. Inspect the new changes, then commit them to another branch:
```bash
but diff
but commit -b liked-tweets -m "Add liked tweets" n2 p8
```
GitButler creates `liked-tweets` alongside `user-bookmarks`. Run `but status` to see both branches:
```bash
but status
```
When more than one independent stack is applied, always use `-b`, `--above`, or `--below` to say where a commit belongs. GitButler refuses an untargeted commit when the destination is ambiguous.
## Stack dependent branches
If the `liked-tweets` work turns out to depend on `user-bookmarks`, move that
branch above `user-bookmarks` to turn the two parallel branches into a stack:
```bash
but move liked-tweets --above user-bookmarks
```
Once you've authenticated with `but config forge auth`, publish the stack from its top branch:
```bash
but pr new liked-tweets -t
```
GitButler pushes the stack and creates the missing reviews from the bottom upward. See [Create stacked branches and pull requests](/features/branch-management/stacked-branches) for the full stacked-review workflow.
# cli-guides-cli-tutorial-conclusion
Ok, that's a short guide to GitButler's command line interface.
Join us in [Discord](https://discord.com/invite/MmFkmaJ42D) if you have any other questions or suggestions for how we can improve the tool.
Thanks!
# cli-guides-cli-tutorial-configuration
We've already covered `but config` a bit in dealing with forges and target branches.
You can also use it for some basic user configuration, such as setting your name and email address for commits and your default editor.
```cli [configuration-but-config-1, 286px]
but config user
```
## Aliases
GitButler also has a built in aliasing system in case you want to provide some shorthands.
```cli [configuration-but-alias-1, 286px]
but alias
```
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.
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.
# cli-guides-cli-tutorial-conflict-resolution
In this world nothing can be said to be certain, except death, taxes and merge conflicts.
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).
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.
## First Class Conflicts in GitButler
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".
This means that:
- 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.
So, let's take a look at what this looks like and how we can deal with conflicted commits when they arise.
{/* restore [e53a4a85d83d] */}
{/* run git push -f origin 96ccca9:main */}
```cli [conflict-resolution-but-status-1, 396px]
but status
```
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).
```cli [conflict-resolution-but-pull-1, 286px]
but pull
```
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`".
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.
If we were to dig into the details here, a few things have actually happened.
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.
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.
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.
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:
Branch: update-homepage
● 42165fe branding change: readme
● 404b604 hero update - new branding
Would you like to start resolving these conflicts?
Enter commit ID to resolve [default: 42165fe]:
```
If you hit enter, it will check out the conflict markers in that commit into your working directory.
```git
❯ but resolve
You are currently in conflict resolution mode.
- resolve all conflicts
- finalize with but resolve finish
- OR cancel with but resolve cancel
Conflicted files remaining:
✗ README.md
Checking out conflicted commit 42165fe
```
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.
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
```git
❯ head README.md
<<<<<< ours
# The Why Experience
|||||| ancestor
# Twitter Clone
======
# X Clone
>>>>>> theirs
```
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
No conflicted files remaining!
Files resolved:
✓ README.md
```
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.
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.
```git
❯ but resolve
You are currently in conflict resolution mode.
- resolve all conflicts
- finalize with but resolve finish
- OR cancel with but resolve cancel
No conflicted files remaining!
Files resolved:
✓ README.md
All conflicts have been resolved!
Finalize the resolution now? [Y/n]: y
Initiated a background sync...
✓ Conflict resolution finalized successfully!
The commit has been updated with your resolved changes.
⚠ Warning: New conflicts were introduced during the rebase:
● 4f671a1 hero update - new branding
Run but status to see all conflicted commits, or but resolve to resolve them.
```
# cli-guides-cli-tutorial-editing-commits
Use `but reword` to change a commit message or branch name. When you reword a commit, GitButler rewrites it and rebases dependent work above it.
## Change a commit message
Find the commit ID:
```bash
but status
```
Then pass the new message:
```bash
but reword nn -m "Add user bookmarks"
```
Omit `-m` when you want to edit the existing message in your configured editor:
```bash
but reword nn
```
Use `--fix-formatting` to wrap the existing message to 72 characters without opening an editor:
```bash
but reword nn --fix-formatting
```
## Rename a branch
Pass the branch name or CLI ID as the target:
```bash
but reword user-bookmarks -m feature/user-bookmarks
```
Run `but status` to see the new branch name.
# cli-guides-cli-tutorial-forges
Use `but push` to publish a branch, `but pr` to create a review, or `but land` when your project accepts direct updates to the target branch.
## Push a branch
Name the branch you want to push:
```bash
but push update-homepage
```
Preview the push without changing the remote:
```bash
but push update-homepage --dry-run
```
In an interactive terminal, bare `but push` opens a branch picker. In a non-interactive session, it pushes every branch with unpushed commits, so naming the branch is safer in scripts.
## Create a pull or merge request
Authenticate with the repository's forge first:
```bash
but config forge auth
```
GitButler supports accounts for GitHub, GitLab, and Bitbucket. The authentication flow offers the methods supported by the selected provider, including self-hosted GitHub and GitLab instances.
Create a review for a branch:
```bash
but pr new update-homepage
```
GitButler pushes the branch before creating its pull request or merge request. Use `but mr` as an alias when you prefer GitLab terminology.
Pass `-t` to use the default title and description without prompts:
```bash
but pr new update-homepage -t
```
For a stacked branch, name the top branch. GitButler pushes the whole stack and creates missing reviews from the bottom upward:
```bash
but pr new feature-polish -t
```
## Manage forge accounts
Show configured accounts and their authentication status:
```bash
but config forge
```
List accounts or remove one that you no longer use:
```bash
but config forge list-users
but config forge forget alice
```
## Land without a review
If your project accepts direct updates to the target branch, land a branch with:
```bash
but land update-homepage
```
This bypasses code review and any checks enforced only by your review workflow.
GitButler asks for confirmation because the target update isn't easily
reversible.
The target fast-forwards when possible; otherwise GitButler creates a merge commit. After landing, GitButler reconciles the remaining applied branches onto the updated target.
If the remote rejects direct pushes because of branch protection, create a review with `but pr new` instead. See [`but land`](/commands/but-land) for stack and non-interactive options.
# cli-guides-cli-tutorial-initializing-a-repository
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.
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.
You can also run `but setup` manually to set everything up explicitly:
```ansi but-setup-5faf7f36
but setup
```
As the command says, it does a few things to prepare your repository for being managed by GitButler.
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.
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.
This means that we do two things:
- 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.
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.
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.
You can also simply checkout a git branch with `git checkout ` and the `post-checkout` hook we installed should clean up after itself.
# cli-guides-cli-tutorial-inspecting
GitButler adds concise views for common workspace tasks, while normal read-only Git commands such as `git show`, `git log`, and `git blame` continue to work.
## Inspect the workspace
Start with:
```bash
but status
```
This shows the uncommitted area, applied branches and stacks, commit order, and upstream state. Add `-f` to include the files in each commit:
```bash
but status -f
```
The first token on each entity line is the CLI ID you can pass to other `but` commands.
## Diff changes
Run `but diff` without a target to inspect all uncommitted changes:
```bash
but diff
```
Pass one CLI ID to narrow the diff:
```bash
but diff q3
but diff user-bookmarks
but diff nn
but diff nn:a
```
These examples inspect an uncommitted file, a branch, a commit, and one file within a commit. `but diff` accepts at most one target, so run it again to inspect another entity.
You can still use `git diff` when you need Git's raw patch format:
```bash
git diff HEAD
```
## List branches
Run `but branch` to show the active branch and the 20 most recently updated branches:
```bash
but branch
```
The list includes useful context such as how far each branch is ahead of the target and whether it merges cleanly into upstream.
Filter branches by a partial name:
```bash
but branch list book
```
Useful filters include:
- `--all` to include all branches
- `--local` or `--remote` to limit the source
- `--review` to fetch review information
- `--no-ahead` and `--no-check` to skip slower calculations
## Inspect a branch
Show the commits on a branch that haven't landed in the target:
```bash
but branch show feature-awesome-thing
```
Add details when you need them:
```bash
but branch show feature-awesome-thing --files --review --check
```
Use `--ai` to generate a summary of the branch changes:
```bash
but branch show feature-awesome-thing --ai
```
## Inspect a commit
Pass a commit's CLI ID or SHA to `but show`:
```bash
but show nn
```
For a branch, `but show --verbose` includes full commit messages and changed files.
## Delete a branch
Deleting a branch removes it and its commits from the workspace. Check the branch before deleting work you may still need:
```bash
but branch show old-experiment
but branch delete old-experiment
```
GitButler asks for confirmation when the branch contains unpushed commits.
# cli-guides-cli-tutorial-operations-log
GitButler maintains a detailed log of all operations, making it easy to track what happened and undo changes when needed.
## Viewing the Operations Log
See all recent GitButler operations:
```cli [operations-log-but-oplog-1, 550px]
but oplog
```
## Undoing the last operation
Undo the last operation:
```cli [operations-log-but-undo-1, 132px]
but undo
```
## Restoring to a previous point
You can restore to any point in the operations history by running the `but oplog restore` command with the SHA from `but oplog`.
```cli [operations-log-but-oplog-2, 154px]
but oplog restore 6fdd8fb1d547
```
Restorations create a new oplog entry before running, so you can always easily undo it in the same manner.
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.
## Creating Snapshots
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 [operations-log-but-oplog-3, 154px]
but oplog snapshot
```
Now you can copy that SHA and restore to that exact point at any time in the future.
# cli-guides-cli-tutorial-rubbing
GitButler gives each history-editing task a focused command. Start with `but status -fv` when you need commit IDs and the file IDs inside each commit:
```bash
but status -fv
```
Use the IDs shown in your own output. Commit IDs with a change ID remain stable across history edits; a SHA-based ID can change when GitButler rewrites history.
## Amend uncommitted changes
Use `but amend` when new work belongs in an existing commit. First inspect the uncommitted changes and history:
```bash
but diff
but status
```
Then pass the uncommitted file or hunk IDs as positional sources and select the commit with `-t`:
```bash
but amend -t nn q3 w7:5
```
This adds file `q3` and hunk `w7:5` to commit `nn`. If you target a branch instead, GitButler amends its newest commit:
```bash
but amend -t user-bookmarks q3
```
## Squash commits
To combine a newer commit with an older target commit, pass the source commit first and the target with `-t`:
```bash
but squash ss -t nn -m "Add bookmark validation"
```
The source commit disappears and its changes become part of the target. Pass `-m` when squashing commits so GitButler doesn't open an editor.
To collapse every commit on one branch into a single commit:
```bash
but squash user-bookmarks -m "Add user bookmarks"
```
## Uncommit work
Use `but uncommit` to move a whole commit back to the uncommitted area:
```bash
but uncommit nn
```
To uncommit one file from a commit, get its committed-file ID from `but status -f`, then combine the commit and file IDs:
```bash
but uncommit nn:a
```
The rest of commit `nn` stays committed.
## Move commits
If a commit landed on the wrong branch, move it to another branch's tip:
```bash
but move nn -b liked-tweets
```
If `liked-tweets` doesn't exist, GitButler creates it as an independent branch.
You can also reorder commits on a branch. `--below` makes the source older than the target, while `--above` makes it newer:
```bash
but move ss --below nn
but move ss --above nn
```
Move a branch above another branch to turn two independent branches into a stack:
```bash
but move liked-tweets --above user-bookmarks
```
## Move a file between commits
Run `but status -f` to get committed-file IDs:
```bash
but status -f
```
Move file `a` out of commit `nn` and into commit `mm`:
```bash
but squash nn:a -t mm
```
When the source is a committed file, GitButler keeps the target commit's message.
## Split a commit
For a top commit, the cleanest split is to uncommit it and create replacement commits from selected changes.
1. Uncommit the source and inspect the resulting file and hunk IDs:
```bash
but uncommit nn
but diff
```
2. Create the replacement commits from oldest to newest:
```bash
but commit -b user-bookmarks -m "Add bookmark model" q3 w7
but commit -b user-bookmarks -m "Validate bookmark URLs" p8:5
```
Changes you don't name remain uncommitted. If the source commit has other commits above it, those commits may need to be moved back to the branch tip after you create the replacements.
## Add an empty placeholder commit
An empty commit can reserve a place in history for work you plan to amend later:
```bash
but commit --empty --below nn -m "Add bookmark error handling"
```
When the implementation is ready, inspect its change IDs and amend them into the placeholder:
```bash
but diff
but amend -t ee q3 w7
```
In `but status`, `--above` places the placeholder on the line above the target (newer), while `--below` places it on the line below (older).
Next, learn how to [change commit messages and branch names](editing-commits).
# cli-guides-cli-tutorial-scripting
Commands that expose structured output accept the `--json` option. For example, inspect the workspace with `jq`:
```bash
but status --json | jq
```
Or inspect one commit:
```bash
but show nn --json | jq
```
Mutation commands such as `but commit`, `but amend`, and `but move` also support `--json`:
```bash
but commit -b user-bookmarks -m "Add user bookmarks" q3 w7 --json
```
Not every command has JSON output, and there is no `-j` shorthand. Check `but --help` before relying on `--json` in a script.
Avoid commands that can prompt for input. Pass a commit message, name explicit branch targets, and use command-specific non-interactive options when available.
# cli-guides-cli-tutorial-tutorial-overview
Using the GitButler CLI is meant to make a specific common workflow very simple, which is roughly:
- 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
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.
Let's walk through some of the things it can do and what a typical day using the GitButler CLI might look like.
# cli-guides-cli-tutorial-updating-the-base
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 Target Branch
The target branch is typically your main production branch that acts as the basis for all your local branches.
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, 572px]
but config
```
Or get more information with `but config target`:
```cli [updating-the-base-but-config-2, 308px]
but config target
```
When you start working, everything that is different from that base goes into a branch based off of it.
## Understanding Upstream
When you first set your target branch (ie, `origin/main`), we record the state of the branch at that time.
However, if someone else merges work into that branch while you're working, the target branch moves forward, but the work you're doing is still based off of where it was. We call this 'upstream' work, and the commit that the target _was_ pointing at and your branches were based off of is your "base".
The problem is that now the stuff we're working on is out of date. It may conflict with what is upstream, it may need the work that is upstream, etc. So how do we get our branch up to date?
## Viewing Upstream
When you run `but status`, we will by default show you a summary of upstream work if there is any. You can see a more detailed list of what is upstream by `but status --upstream` (or `-u`).
{/* restore [cc9d20b8099c] */}
{/* run git push -f origin 32a2175758f7f649ed7a030a17fd21213a5e400f:refs/heads/main */}
Let's take a look at what this looks like. Let's say that our project is at this state:
```cli [updating-the-base-but-status-1, 396px]
but status -u
```
We can see that there are two commits upstream (ie, merged into `origin/main` since we started our branch).
Technically, there could be more reachable commits, but we only show the first parents, so merges of long branches show up as just the merge commits, to simplify things a bit.
Now let's say that we would like to pull in the upstream work and rebase our branches on top of the new upstream to update them. We can check what all would happen with `but pull --check`.
```cli [updating-the-base-but-pull-1, 308px]
but pull --check
```
This will fetch the very latest work, then check that upstream work against your currently applied branches to see if anything has been integrated (and thus we can remove), anything conflicts with upstream work, or a merge/rebase should work cleanly.
In this example, we can see that our `user-bookmarks` branch would be cleanly rebased if we did a pull, and that there are two things that have been merged since we started our branches.
## Updating the Base
When you feel like you want to get your active branches up to date, you can run `but pull`. This will fetch the very latest work, then rebase your active branches on top of the new target commit to be your new base.
Let's run it in our example.
```cli [updating-the-base-but-pull-2, 396px]
but pull
```
```cli [updating-the-base-but-status-2, 308px]
but status
```
OK, now we can see that our integrated branch was removed, our `gemfile-fixes` branch was successfully rebased and our `sc-branch-26` work is marked as conflicted. We'll see how to deal with that state in a minute.
# cli-guides-installation
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.
### Curl install
You can install the CLI by running the following command in your terminal:
```bash
curl -fsSL https://gitbutler.com/install.sh | sh
```
## 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.
# commands-but-absorb
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
- If a change is assigned to a branch in the GitButler app, it will be amended into a commit there
- If there are no commits on that branch, a new commit is created there
- Changes are amended into the topmost commit of the leftmost (first) branch
Optionally an identifier to an Uncommitted File may be provided.
- If an Uncommitted File id is provided, absorb will be performed for just that file
- 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.
**Usage:** `but absorb [SOURCE] [OPTIONS]`
## Arguments
* `` — If the Source is an uncommitted change - the change will be absorbed. If not provided, everything that is uncommitted will be absorbed
## Options
* `--dry-run` — Show the absorption plan without making any changes
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
# commands-but-agent
Runs a guided setup wizard for installing GitButler agent skills and
writing workflow steering instructions into supported agent instruction
files.
## Examples
Start the interactive setup wizard (`but agent setup` is equivalent):
```text
but agent
```
Print the default generated steering text:
```text
but agent setup --print
```
**Usage:** `but agent `
## Subcommands
### `but agent setup`
Configure GitButler skills and workflow instructions for coding agents.
Starts an interactive wizard that generates GitButler workflow steering,
installs selected agent skills, and optionally writes the generated
steering into agent instruction files.
## Examples
Start the setup wizard:
```text
but agent setup
```
Print the default steering text without modifying files:
```text
but agent setup --print
```
**Usage:** `but agent setup [OPTIONS]`
**Options:**
* `--print` — Print the default generated steering text without prompting or modifying files
# commands-but-alias
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:
```text
but alias
```
Create a new alias:
```text
but alias add st status
but alias add stv "status --verbose"
```
Remove an alias:
```text
but alias remove st
```
**Usage:** `but alias `
## Subcommands
### `but alias list`
List all configured aliases (default)
**Usage:** `but alias list`
### `but alias add`
Add a new alias
Creates a new alias that expands to the given command.
Examples
but alias add st status
but alias add stv "status --verbose"
but alias add branches "branch list --all"
**Usage:** `but alias add [OPTIONS]`
**Arguments:**
* `` — The name of the alias to create (required)
* `` — The command and arguments that the alias should expand to
If the value contains spaces or special characters, quote it: "status --verbose" (required)
**Options:**
* `-g`, `--global` — Store the alias globally (in ~/.gitconfig) instead of locally
### `but alias remove`
Remove an existing alias
Examples
but alias remove st
but alias remove co --global
**Usage:** `but alias remove [OPTIONS]`
**Arguments:**
* `` — The name of the alias to remove (required)
**Options:**
* `-g`, `--global` — Remove from global config (in ~/.gitconfig) instead of local
# commands-but-amend
Sources must be uncommitted files or hunks. To move changes that are already committed, or to
combine commits, use `but squash`.
If the target is a branch, the changes are amended into that branch's newest commit (its tip).
Name the commit explicitly to amend into anything below the tip.
For more details about CLI IDs, see `but help cli-ids`.
**Usage:** `but amend [SOURCES] [OPTIONS]`
## Arguments
* `` — One or more uncommitted files or hunks to amend.
If omitted, all changes in the uncommitted area (zz) are amended.
## Options
* `-t`, `--target` `` — The commit or branch to amend into (required)
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
# commands-but-apply
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.
**Usage:** `but apply `
## Arguments
* `` — The branch to apply (required)
# commands-but-branch
This includes creating, deleting, listing, and showing details about branches.
By default without a subcommand, it will list the branches.
To apply or unapply branches, use `but apply` and `but unapply`.
To rename an applied branch, use `but reword -m `.
**Usage:** `but branch `
## Subcommands
### `but branch new`
Create a new branch.
Use `--above` or `--below` to created stacked branches. Omitting these create a new unstacked
branch.
For more details about CLI IDs, see `but help cli-ids`.
**Usage:** `but branch new [NAME] [OPTIONS]`
**Arguments:**
* `` — Name of the new branch.
If omitted the new branch will get a generated name.
**Options:**
* `-A`, `--above` `` — Place the branch above BRANCH_OR_COMMIT, which must be an applied branch or commit.
If BRANCH_OR_COMMIT is a commit, the new branch is created above the commit.
If BRANCH_OR_COMMIT is a branch, the new branch is created above the targeted branch.
* `-B`, `--below` `` — Place the branch below BRANCH_OR_COMMIT, which must be an applied branch or commit.
If BRANCH_OR_COMMIT is a commit, the new branch is created below the commit.
If BRANCH_OR_COMMIT is a branch, the new branch is created below the targeted branch.
* `-s`, `--switch` — Switch to the newly created branch instead of applying it to the GitButler workspace
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
### `but branch delete`
Delete branchs from the workspace
**Usage:** `but branch delete `
**Arguments:**
* `` — One or more branches to delete (required)
### `but branch list`
List the branches in the repository
By default, shows the active branch and the 20 most recently updated branches.
You can use the `--all` flag to show all branches, `--local` to show only
local branches, or `--remote` to show only remote branches.
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.
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.
**Usage:** `but branch list [FILTER] [OPTIONS]`
**Arguments:**
* `` — Filter branches by name (case-insensitive substring match)
**Options:**
* `-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
* `--empty` — Include branches with no commits on them (hidden by default)
### `but branch show`
Show commits ahead of base for a specific branch
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.
**Usage:** `but branch show [OPTIONS]`
**Arguments:**
* `` — CLI ID or name of the branch to show (required)
**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
### `but branch update`
Update your local branch with the content of its remote counterpart.
This allows you to resolve the divergence between your local branch and its
tracked remote in different ways.
**Usage:** `but branch update [OPTIONS]`
**Arguments:**
* `` — Name of the local branch to integrate (required)
**Options:**
* `-s`, `--strategy` `` — Strategy to use for the integration. If no strategy is specified, we default to pull-rebase (default: `pull-rebase`)
* `--dry-run` — Preview the resulting branch state without persisting changes
* `-v`, `--verbose` — Show additional dry-run details like the current divergence
* `-i`, `--interactive` — Open the generated integration script in an editor
# commands-but-clean
A branch is considered empty if it has no local commits and (by default)
no upstream-only commits. Stacks with uncommitted changes assigned to them
in the GitButler app are skipped entirely.
The entire operation is recorded as a single oplog entry, so it can
be undone with `but undo`.
## Examples
Remove all empty branches:
```text
but clean
```
Preview which branches would be removed:
```text
but clean --dry-run
```
Pull latest changes first, then clean:
```text
but clean --pull
```
Also remove branches that only have upstream commits:
```text
but clean --include-upstream
```
**Usage:** `but clean [OPTIONS]`
## Options
* `--dry-run` — Preview which branches would be removed without actually deleting them
* `--pull` — Pull latest changes from the remote before cleaning
* `--include-upstream` — Also remove branches that have upstream-only commits but no local commits or changes
# commands-but-commit
By default, all uncommitted changes are included in the commit. This can be controlled with
change flags such as `--empty` and `--interactive`, or by providing `CHANGES` as positional
arguments.
If there are no branches applied, a new branch is created for the commit. If there is only one
stack of branches applied, the commit is placed at the tip of that stack. Otherwise, the
targeting flags `--above`, `--below` and `--branch` control where the commit is placed. Note
that only one of the targeting flags can be provided at a time.
The commit is expected to have a commit message unless `--no-message` is provided. If neither of
`--no-message` nor `--message` is provided, the user's preferred editor is opened to input a
message.
For more details about CLI IDs, see `but help cli-ids`.
**Usage:** `but commit [CHANGES] [OPTIONS]`
## Arguments
* `` — One or more changes to commit.
A change can either be a file or a hunk.
## Options
* `-m`, `--message` `` — The message to use for the commit.
Can be supplied any amount of times, each value being appended to the preceding ones with a blank line in between.
* `--no-message` — Creates the commit without a commit message
* `-b`, `--branch` `` — Place the commit on the branch BRANCH.
If BRANCH does not exist, it is created as an unstacked branch.
If BRANCH is omitted, an unstacked branch with a generated name is created.
If BRANCH is a linked worktree or a branch checked out in one, the commit is placed on the tip of that worktree's branch.
Attempting to place a commit on a branch that exists but is not applied is an error.
* `-A`, `--above` `` — Place the commit above BRANCH_OR_COMMIT, which must be an applied branch or commit.
If BRANCH_OR_COMMIT is a commit, the new commit is placed on the same branch as the targeted commit.
If BRANCH_OR_COMMIT is a branch, the new commit is placed on a new branch above the targeted branch.
* `-B`, `--below` `` — Place the commit below BRANCH_OR_COMMIT, which must be an applied branch or commit.
If BRANCH_OR_COMMIT is a commit, the new commit is placed on the same branch as the targeted commit.
If BRANCH_OR_COMMIT is a branch, the new commit is placed on a new branch below the targeted branch. Branches are treated as buckets, meaning that "below a branch" is treated as below the oldest ancestor on that branch.
If BRANCH_OR_COMMIT is a linked worktree, the new commit is placed on the tip of the branch that worktree has checked out.
* `--empty` — Forces the commit to be empty regardless of repository state
* `-i`, `--interactive` — Open the TUI to interactively select what to commit
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
# commands-but-completions
## Examples
```bash
# bash, put in .bashrc or .bash_profile depending on system setup
eval "$(but completions bash)"
# zsh, put in .zshrc
eval "$(but completions zsh)"
# fish, put in config.fish
but completions fish | source
```
**Usage:** `but completions [SHELL]`
## Arguments
* `` — The shell to generate completions for, or the one extracted from the SHELL environment variable
# commands-but-config
Without a subcommand, displays an overview of important settings including
user information, target branch, forge configuration, and AI setup.
## Examples
View configuration overview:
```text
but config
```
View/set user configuration:
```text
but config user
but config user set name "John Doe"
but config user set email john@example.com
```
View/set forge configuration:
```text
but config forge
```
View/set target branch:
```text
but config target
but config push-remote
```
View/set metrics:
```text
but config metrics
```
**Usage:** `but config `
## Subcommands
### `but config user`
View and configure user information (name, email, editor).
Without arguments, displays current user.name, user.email, and core.editor. Use subcommands to set or unset configuration values.
Examples
View user configuration:
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
Unset a local value:
but config user unset name
**Usage:** `but config user`
### `but config forge`
View and manage forge configuration.
Shows configured forge accounts (GitHub, GitLab, Bitbucket) and authentication status. Use subcommands to manage accounts or native GitHub stacked pull requests.
Examples
View configured forge accounts:
but config forge
Authenticate with a forge:
but config forge auth
List authenticated accounts:
but config forge list-users
Forget an account:
but config forge forget username
View or configure native GitHub stacked pull requests:
but config forge github-stacks
but config forge github-stacks enable
**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
Set a target branch and push branches to a fork:
but config target upstream/main --push-remote origin
**Usage:** `but config target [BRANCH] [OPTIONS]`
**Arguments:**
* `` — New target branch to set (e.g., "origin/main")
**Options:**
* `--push-remote` `` — Remote to push branches to (e.g., "origin" for a fork)
### `but config push-remote`
View or set the remote used to push branches.
Without arguments, displays the effective push remote. With a remote name, updates the push remote without changing the target branch.
Examples
View the current push remote:
but config push-remote
Push branches to a fork remote:
but config push-remote origin
**Usage:** `but config push-remote [REMOTE]`
**Arguments:**
* `` — New remote to use when pushing branches (e.g., "origin")
### `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 config metrics [STATUS]`
**Arguments:**
* `` — Whether metrics are enabled
### `but config feature`
View and configure feature flags.
Without arguments, displays all feature flags that can be changed through the CLI. Specify a flag to view its current value, or add enable or disable to update it.
Examples
View all feature flags:
but config feature
Enable single-branch mode:
but config feature single-branch enable
**Usage:** `but config feature [FLAG] [STATUS]`
**Arguments:**
* `` — Feature flag to view or update
* `` — Whether the feature flag is enabled
### `but config ai`
View and configure AI provider settings.
Without subcommands, this starts an interactive setup flow. Use provider subcommands for non-interactive configuration.
Examples
Interactive setup:
but config ai
View current AI configuration:
but config ai show
Configure OpenAI non-interactively:
but config ai openai --key-option bring-your-own --api-key-env OPENAI_API_KEY --model gpt-5.4-nano
Configure Ollama locally:
but config ai --local ollama --endpoint localhost:11434 --model llama3.1
**Usage:** `but config ai [OPTIONS]`
**Options:**
* `--local` — Configure local repository git config instead of global user config
* `--global` — Configure global user git config
# commands-but-diff
Without any arguments, it shows the diff of all uncommitted changes. Optionally, provide one
CLI ID to show the diff for an uncommitted file, branch, commit, committed file, or worktree.
`TARGET` accepts at most one entity. To show several entities, run this command once per entity.
**Usage:** `but diff [TARGET]`
## Arguments
* `` — The CLI ID of the entity to show the diff for.
If omitted shows the diff of all uncommitted changes.
For more details about CLI IDs, see but help cli-ids.
# commands-but-discard
Changes may be selected by branch, commit, committed file, uncommitted file, or uncommitted
hunk CLI ID. Use `zz`, or omit ``, to discard all uncommitted changes.
All provided changes must be the same kind. Committed files must come from the same commit.
The entire operation is recorded as a single oplog entry, so it can be undone with `but undo`.
For more details about CLI IDs, see `but help cli-ids`.
**Usage:** `but discard [CHANGES]`
## Arguments
* `` — One or more branches, commits, or changes to discard.
If omitted all uncommitted changes will be discarded.
# commands-but-gui
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.
**Usage:** `but gui [PATH] [OPTIONS]`
## Arguments
* `` — Path to the directory to open as a GitButler project. Defaults to the current directory
## Options
* `-n`, `--new-window` — Open the project in a new application window (default: `false`)
# commands-but-help-cli-ids
CLI IDs are used by `but` to reference things such as commits, branches and files. While
obvious identifiers like full commit hashes and entire branch names are viable CLI IDs,
there are also various shorter identifiers that can be used in place of the full names.
In general, `but status` will show all currently available CLI IDs in front of the "thing".
Typical CLI IDs include:
* **Commit:**
- The entire commit ID
- The entire change ID
- Any prefix of the commit ID or change ID that is unique in the current context. `but status`
highlights the shortest possible prefix.
* **Branch:**
- The entire branch name
- An exact short ID for the branch name, as shown by `but status`
* **Uncommitted file:** A path-derived ID that is typically 1-3 characters
* **Uncommitted hunk:** `:`
- Run `but diff` to show all current uncommitted hunks and their IDs
* **Uncommitted area:** Always `zz`
* **Committed file:** `:`
- Run `but status -f` to show committed files
Many CLI IDs depend on the context and may change if the context changes, such as when new
data is written to files, commits are made or rearranged and branches are created or
deleted.
Some CLI IDs are more stable than others. For example, a commit's change ID is stable even
when commits are made and moved around, but the minimum prefix may increase as other IDs are
introduced.
Most but not all commands accept CLI IDs to perform various actions. See the documentation
for the individual command (`but --help`) for details on how to use CLI IDs for
that command in particular.
**Usage:** `but help cli-ids`
# commands-but-land
Lands the branch onto the configured target (for example `origin/master`) without going
through a pull request — the "just push to the target" workflow. By default the target is
fast-forwarded to the branch tip when possible (no merge commit); otherwise a merge commit
is created. For a local (`gb-local`) target the refs are moved locally; otherwise the result
is pushed to the remote. After landing, the remaining applied branches are reconciled onto
the moved target, just like `but pull`.
Requires an active GitButler workspace. Updating the target is direct and not easily
reversible, so a confirmation is required (use `--yes` to skip it in scripts).
When NOT to use this: if your project lands changes through pull requests / code review,
use `but push` and open a PR (`but pr new`) instead — `but land` deliberately bypasses that
process. On a real remote, a branch protected against direct pushes will reject the land.
Landing a segment with other segments below it is refused unless `--whole-stack` is
passed with the stack's top segment, which lands the entire stack.
## Examples
Land a branch by its CLI ID:
```text
but land bu
```
Land a branch by name, forcing a merge commit:
```text
but land my-feature-branch --no-ff
```
Land an entire stack by naming its top segment:
```text
but land top-branch --whole-stack
```
**Usage:** `but land [OPTIONS]`
## Arguments
* `` — Branch ID or name to land onto the target branch (required)
## Options
* `--yes` — Skip the confirmation prompt
* `--no-ff` — Always create a merge commit, even when the branch can be fast-forwarded
* `--whole-stack` — Land the entire stack: BRANCH must be the top segment, and the segments below it are published to the target along with it
# commands-but-move
Move a set of `` around relative to a `TARGET`.
`` is a set of commits, a set of committed files or a single branch. You are not
allowed to mix kinds of sources (e.g. commits and committed files) in a single command.
`TARGET` is one of `--above`, `--below`, `--unstack` or `--branch` and defines how ``
should be moved. Depending on how `` and `TARGET` are combined, a commit and/or branch
may be created as part of the move.
**A branch is created when:**
* You move a commit or committed file relative to a branch
* You unstack a commit or committed file
**A commit is created when:**
* You move a committed file relative to a commit or branch
* You unstack a committed file
Note the overlap between the above conditions. For example, unstacking a committed file both
creates a new commit for the file and a branch for the commit.
For more details about CLI IDs, see `but help cli-ids`.
**Usage:** `but move [OPTIONS]`
## Arguments
* `` — One or more sources to move.
You may provide one of the following kinds of sources:
- Commits
- Committed files
- All files must come from the same commit
- A branch
- Branches can only be moved one at a time
Mixing sources in a single command is not allowed.
The order of the sources does not matter.
Providing any of the sources as an argument for a target such as --above or --below is an error. (required)
## Options
* `-b`, `--branch` `` — Place <SOURCES> on the branch BRANCH.
If BRANCH exists, commits or committed files are moved onto its tip. A branch source is instead stacked on top of BRANCH, equivalent to --above BRANCH.
If BRANCH does not exist, it is created as an unstacked branch for commit or committed-file sources. Using a branch source with a nonexistent BRANCH is an error.
If BRANCH is a linked worktree or a branch checked out in one, commit or committed-file sources are moved onto the tip of that worktree's branch.
If BRANCH is omitted, an unstacked branch with a generated name is created. This is exactly equivalent to --unstack and is allowed for any source kind.
Attempting to place <SOURCES> on a branch that exists but is not applied is an error.
* `-A`, `--above` `` — Place <SOURCES> above BRANCH_OR_COMMIT.
If BRANCH_OR_COMMIT is a commit, <SOURCES> are placed on the same branch as the targeted commit.
If BRANCH_OR_COMMIT is a branch, the sources are placed on a new branch above the targeted branch.
This target is applicable for all kinds of <SOURCES>.
* `-B`, `--below` `` — Place <SOURCES> below BRANCH_OR_COMMIT.
If BRANCH_OR_COMMIT is a commit, the <SOURCES> are placed on the same branch as the targeted commit.
If BRANCH_OR_COMMIT is a branch, <SOURCES> are placed on a new branch below the targeted branch. Branches are treated as buckets, meaning that "below a branch" is treated as below the oldest ancestor on that branch.
If BRANCH_OR_COMMIT is a linked worktree, <SOURCES> are placed on the tip of the branch that worktree has checked out.
This target is only applicable for <SOURCES> that are commits or committed files.
* `--unstack` — Unstack <SOURCES> from their current stacks.
--unstack does not take an argument, so --unstack <SOURCES> and <SOURCES> --unstack are equivalent.
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
# commands-but-open
With no argument this opens the workspace. Given a branch or a commit, the
app opens with that selected, so a link can point at the thing you are
talking about rather than at the app.
Commits are addressed by their change ID where they have one, which
survives amending and rebasing — the link keeps working after the commit is
rewritten.
**Usage:** `but open [TARGET] [OPTIONS]`
## Arguments
* `` — The branch or commit to select, defaulting to the workspace itself
## Options
* `--print` — Print the link instead of opening it
# commands-but-oplog
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.
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`).
**Usage:** `but oplog `
## Subcommands
### `but oplog list`
List operation history.
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.
You can use `but oplog restore ` to restore to a specific state.
**Usage:** `but oplog list [OPTIONS]`
**Options:**
* `--since` `` — Start from this oplog SHA instead of the head
* `-s`, `--snapshot` — Show only on-demand snapshot entries
### `but oplog snapshot`
Create an on-demand snapshot with optional message.
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.
You can provide an optional message to describe the snapshot.
**Usage:** `but oplog snapshot [OPTIONS]`
**Options:**
* `-m`, `--message` `` — Message to include with the snapshot
### `but oplog restore`
Restore to a specific oplog snapshot.
This command allows you to revert the repository to a previous state
captured in an oplog snapshot.
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`.
**Usage:** `but oplog restore `
**Arguments:**
* `` — Oplog SHA to restore to (required)
# commands-but-pick
Each source commit is copied to the target location as a new commit.
If there are no branches applied, a new branch is created for the picked commits. If there is
only one stack of branches applied, the commits are placed at the tip of that stack. Otherwise,
the targeting flags `--above`, `--below`, and `--branch` control where the commits are placed.
Only one targeting flag can be provided at a time.
For more details about CLI IDs, see `but help cli-ids`.
**Usage:** `but pick [OPTIONS]`
## Arguments
* `` — The sources to cherry-pick (required)
## Options
* `-b`, `--branch` `` — Place the picked commits on the branch BRANCH.
If BRANCH does not exist, it is created as an unstacked branch.
If BRANCH is omitted, an unstacked branch with a generated name is created.
Attempting to pick onto a branch that exists but is not applied is an error.
* `-A`, `--above` `` — Place the picked commits above BRANCH_OR_COMMIT.
If BRANCH_OR_COMMIT is a commit, the picked commits are placed on the same branch as the targeted commit.
If BRANCH_OR_COMMIT is a branch, the picked commits are placed on a new branch above the targeted branch.
* `-B`, `--below` `` — Place the picked commits below BRANCH_OR_COMMIT.
If BRANCH_OR_COMMIT is a commit, the picked commits are placed on the same branch as the targeted commit.
If BRANCH_OR_COMMIT is a branch, the picked commits are placed on a new branch below the targeted branch. Branches are treated as buckets, meaning that "below a branch" is treated as below the oldest ancestor on that branch.
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
# commands-but-pr
If you are authenticated with a forge using but config forge auth, you can use the but pr or but mr 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.
**Usage:** `but pr [OPTIONS]`
## Subcommands
### `but pr new`
Create a new review for a branch. If no branch is specified, you will be prompted to select one. If there is only one branch without a review, you will be asked to confirm.
If the branch is part of a stack, GitButler pushes that branch and its ancestors and creates missing reviews from the bottom upward. It also updates stack metadata using native GitHub stacks when enabled and supported, or review descriptions otherwise.
**Usage:** `but pr new [BRANCH] [OPTIONS]`
**Arguments:**
* `` — The branch to create a review for
**Options:**
* `-m`, `--message` `` — review title and description. The first line is the title, the rest is the description
* `-F`, `--file` `` — Read review 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
* `--no-hooks` — Bypass pre-push hooks
* `-t`, `--default` — Use the default content for the review title and description, skipping any prompts. If the branch contains only a single commit, the commit message will be used (default: `false`)
* `-d`, `--draft` — Whether to create reviews as a draft (default: `false`)
### `but pr auto-merge`
Enable or disable the automatic merging of a review or reviews. If no reviews are specified, you will be prompted to select one or multiple of the review associated with branches in your workspace
**Usage:** `but pr auto-merge [SELECTOR] [OPTIONS]`
**Arguments:**
* `` — The target of this operation. This can be one or multiple (comma-separated):
- Branch names,
- Branch IDs,
- Stack IDs (in which case, all the reviews associated with the stacked branches are selected),
- Associated review IDs (i.e. PR numeric IDs or MR numeric IDs, without the symbol).
**Options:**
* `-d`, `--off` — Whether to disable the automatic merging of the review(s) (default: `false`)
### `but pr set-draft`
Set an existing review (or set of reviews) as draft. If no reviews are specified, you will be prompted to select one or multiple of the review associated with branches in your workspace
**Usage:** `but pr set-draft [SELECTOR]`
**Arguments:**
* `` — The target of this operation. This can be one or multiple (comma-separated):
- Branch names,
- Branch IDs,
- Stack IDs (in which case, all the reviews associated with the stacked branches are selected),
- Associated review IDs (i.e. PR numeric IDs or MR numeric IDs, without the symbol).
### `but pr set-ready`
Set an existing review (or set of reviews) as ready-to-review. If no reviews are specified, you will be prompted to select one or multiple of the review associated with branches in your workspace
**Usage:** `but pr set-ready [SELECTOR]`
**Arguments:**
* `` — The target of this operation. This can be one or multiple (comma-separated):
- Branch names,
- Branch IDs,
- Stack IDs (in which case, all the reviews associated with the stacked branches are selected),
- Associated review IDs (i.e. PR numeric IDs or MR numeric IDs, without the symbol).
### `but pr template`
Configure the template to use for review descriptions. This will list all available templates found in the repository and allow you to select one
**Usage:** `but pr template [TEMPLATE_PATH]`
**Arguments:**
* `` — Path to the review template file within the repository
## Options
* `-d`, `--draft` — Whether to create reviews as a draft (default: `false`)
# commands-but-pull
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.
You can run `but pull --check` first to see if your branches can be cleanly
merged into the target branch before running the update.
**Usage:** `but pull [OPTIONS]`
## Options
* `-c`, `--check` — Only check the status without updating (equivalent to the old but base check)
# commands-but-push
`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"
**Usage:** `but push [BRANCH_ID] [OPTIONS]`
## Arguments
* `` — Branch name or CLI ID to push. If not specified, will list all branches and prompt for selection in interactive mode
## Options
* `-f`, `--with-force` — Force push even if it's not fast-forward (default: `true`)
* `-s`, `--skip-force-push-protection` — Skip force push protection checks
* `--no-hooks` — Bypass pre-push hooks
* `-d`, `--dry-run` — Show what would be pushed without actually pushing
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
# commands-but-redo
**Usage:** `but redo`
# commands-but-resolve
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.
Uncommitted files marked `{conflicted}` by `but status` are resolved with
`but resolve ...` once they hold the desired content (or were deleted).
## Workflow
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`
Alternatively, resolve with AI in one step: `but resolve --ai`,
or `but resolve --ai` to resolve all conflicted commits, oldest first.
When in resolution mode, `but status` will also show that you're resolving conflicts.
**Usage:** `but resolve [TARGETS] [OPTIONS]`
## Subcommands
### `but resolve conflicts`
List the conflicts of a conflicted commit, without entering resolution mode.
Each conflict is shown with its ours side (the new base the commit was
rebased onto), the common ancestor, and its theirs side (the commit's
own version), numbered per file for use with `but resolve apply`.
**Usage:** `but resolve conflicts [COMMIT]`
**Arguments:**
* `` — A conflicted commit, or a branch (meaning its oldest conflicted commit). Defaults to the first conflicted branch's oldest conflicted commit
### `but resolve apply`
Resolve conflicts of a conflicted commit, without entering resolution mode.
Targets one conflict (`:`, numbers from `but resolve conflicts`)
or every conflict in a file (`` with `--ours`/`--theirs`). The
replacement content for mixed resolutions is read from `--file` or stdin.
Resolving only some conflicts keeps the commit conflicted with the rest,
so conflicts can be worked off incrementally; the commit id changes with
every apply. Undo with `but undo`.
**Usage:** `but resolve apply [OPTIONS]`
**Arguments:**
* `` — The conflicted file, optionally with a 1-based conflict number (<path>:<N>) (required)
**Options:**
* `--commit` `` — A conflicted commit, or a branch (meaning its oldest conflicted commit — branch names stay stable across applies, unlike commit ids). Defaults to the first conflicted branch's oldest conflicted commit
* `--ours` — Take the ours side: the new base the commit was rebased onto
* `--theirs` — Take the theirs side: the commit's own version
* `--ai` — Let the configured AI model merge the targeted conflicts
* `-F`, `--file` `` — Read the replacement content from this file (otherwise from stdin)
### `but resolve status`
Show the status of conflict resolution, listing remaining conflicted files.
**Usage:** `but resolve status`
### `but resolve finish`
Finalize conflict resolution and return to workspace mode.
This commits the resolved changes, rebases any commits on top of the
resolved commit, and returns to the normal workspace.
**Usage:** `but resolve finish`
### `but resolve cancel`
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 [OPTIONS]`
**Options:**
* `-f`, `--force` — Forcibly remove any changes made
## Options
* `--ai` — Resolve the conflicts with the configured AI model and apply the result.
With a commit ID this resolves only that commit; without one it resolves all conflicted commits in the workspace, oldest first. Undo the result with but undo.
# commands-but-reword
You can easily change the commit message of any of your commits by
running `but reword ` and providing a new message in the
editor.
This will recreate the commit with the new message and then rebase any
dependent commits on top of it.
You can also use `but reword ` to rename the branch.
**Usage:** `but reword [OPTIONS]`
## Arguments
* `` — Commit ID to edit the message for, or branch ID to rename (required)
## Options
* `-m`, `--message` `` — The new commit message or branch name. If not provided, opens an editor
* `-f`, `--fix-formatting` — Format the existing commit message to 72-char line wrapping without opening an editor
* `--diff` — Always show diff inside the editor.
By default the diff will be shown unless it's large. The diff will always be shown if --diff is passed, regardless of the size of the diff. (default: `false`)
* `--no-diff` — Never show the diff inside the editor (default: `false`)
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
# commands-but-setup
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.
## Examples
Initialize a new git repository and set up GitButler:
```text
but setup --init
```
**Usage:** `but setup [OPTIONS]`
## Options
* `--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.
# commands-but-show
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
Show commit details by short commit ID:
```text
but show a1b2c3d
```
Show commit details by CLI ID:
```text
but show c5
```
Show branch commits by branch name:
```text
but show my-feature-branch
```
Show branch with full commit details:
```text
but show my-feature-branch --verbose
```
**Usage:** `but show [OPTIONS]`
## Arguments
* `` — The commit ID (short or full SHA), branch name, or CLI ID to show details for (required)
## Options
* `-v`, `--verbose` — Show full commit messages and files changed for each commit
# commands-but-skill
Skills provide enhanced AI capabilities for working with GitButler through
Claude Code, Codex, and other AI assistants.
Use `but skill install` to install the GitButler skill files. By default,
it prompts for scope (repository or global home directory) and then format.
When run outside a git repository, local scope is unavailable and the
default install location is global (home directory). You can still
install to a custom location with `--path` using an absolute or `~` path.
## Examples
Install interactively (prompts for scope and format):
```text
but skill install
```
Install the skill globally:
```text
but skill install --global
```
**Usage:** `but skill `
## Subcommands
### `but skill install`
Install the GitButler CLI skill files for Coding agents
By default, the command prompts you to choose installation scope first
(current repository or global home directory), then prompts you to
select a skill folder format (Agent Skills / .agents, Claude Code,
OpenCode, Codex, GitHub Copilot, Cursor, Windsurf, Poolside) unless you
specify a custom path with --path.
When run outside a git repository, local scope is unavailable and the
default install location is global (home directory). You can still
install to a custom location with `--path` using an absolute or `~` path.
Use --global to install the skill in a global location instead of the
current repository.
In non-interactive mode, a detected agent uses its global skill directory;
otherwise specify --path or --detect.
## Examples
Install interactively (prompts for scope and format):
```text
but skill install
```
Install globally (prompts for format):
```text
but skill install --global
```
Install to a custom path:
```text
but skill install --path .agents/skills/gitbutler
```
Auto-detect installation location (update existing installations):
```text
but skill install --detect
```
**Usage:** `but skill install [OPTIONS]`
**Options:**
* `-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). Outside a repository, relative paths require --global
* `-d`, `--detect` — Refresh existing installations in place, updating every GitButler skill found in the current scope (local before global)
### `but skill check`
Check if installed GitButler skills are up to date with the CLI version
Scans for installed skill files and compares their version with the current
CLI version. By default, checks both local (repository) and global installations.
## Examples
Check all installed skills:
```text
but skill check
```
Check and automatically update outdated skills:
```text
but skill check --update
```
Check only global installations:
```text
but skill check --global
```
**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
# commands-but-squash
Squash is flexible in the ways it can move changes around. It can
- Squash commits into other commits
- Squash branches into commits
- Move changes between commits
- Amend uncommitted changes into a commit
- Uncommit commits
- Uncommit changes in commits
- Uncommit branches
If no message-related flag is passed when squashing commits or branches, an editor may be
opened where the new message can be composed; other squashes keep the target's message.
For more details about CLI IDs, see `but help cli-ids`.
**Usage:** `but squash [SOURCES] [OPTIONS]`
## Arguments
* `` — The sources to squash.
If --target is provided and <SOURCES> is omitted, the uncommitted area (zz) is used.
If <SOURCES> is one or more commits they will be squashed into the target.
If <SOURCES> is one or more branches all the commits on the branches will be squashed into the target and the branches will be removed. When the target is the uncommitted area (zz), all commits on the branches are uncommitted.
If TARGET is omitted and <SOURCES> is exactly one branch all commits on the branch will be squashed.
If <SOURCES> is one or more uncommitted files or hunks they will be squashed into the target.
If <SOURCES> is the uncommitted area (zz) all uncommitted changes will be squashed into the target.
If <SOURCES> is a committed file those changes will be moved into the target. All changes must come from the same commit. It is not possible to move changes from multiple source commits into a single target.
It is not possible to mix sources of different types, i.e., all sources must either be commits, branches, uncommitted files, zz, or committed files.
## Options
* `-m`, `--message` `` — The message to use for the new commit.
Can be supplied any number of times, each value being appended to the preceding ones with a blank line in between.
This cannot be used when TARGET is the uncommitted area (zz).
* `--no-message` — Creates the commit without a commit message.
This cannot be used when TARGET is the uncommitted area (zz).
* `-u`, `--use-target-message` — Use the message of the target.
The message of the source(s) will be discarded.
This cannot be used when TARGET is the uncommitted area (zz).
* `--use-source-message` — Use the message of the source(s).
The message of the target will be discarded.
Cannot be used if <SOURCES> are not committed, if TARGET is the uncommitted area (zz), or if moving committed changes between commits.
* `-t`, `--target` `` — The target to squash into.
If TARGET is a commit the sources will be added to the commit.
If TARGET is a branch the sources will be added to that branch's newest commit (its tip).
If TARGET is the uncommitted area (zz) the sources will be uncommitted.
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
# commands-but-status
This shows uncommitted files, 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:
```text
but status
```
Shorthand with listing files modified
```text
but status -f
```
**Usage:** `but status [OPTIONS]`
## Options
* `-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`)
# commands-but-teardown
This command:
- Creates an oplog snapshot of the current state
- Finds the first active branch and checks it out
- Alternatively, use `--checkout-to ` to override this default
- 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.
## Examples
Exit GitButler mode:
```text
but teardown
```
```text
but teardown --checkout-to my-feature-branch
```
**Usage:** `but teardown [OPTIONS]`
## Options
* `-c`, `--checkout-to` `` — Explicit override for which local branch to checkout to
# commands-but-tui
The GitButler TUI provides a visual experience similar to the GitButler GUI - right in your
terminal. For the full workflow and key bindings, see [https://docs.gitbutler.com/gitbutler-tui](https://docs.gitbutler.com/gitbutler-tui)
**Environment variables:**
**BUT_THEME** Sets the theme for but. Options: dark, light. [default: detected from the terminal, falling back to dark]
**Usage:** `but tui [TARGET] [OPTIONS]`
## Arguments
* `` — The commit, branch, committed file, or uncommitted file or hunk to select
## Options
* `--remember-selection` — When the TUI quits save the selection and restore it when re-opening.
If the saved selection cannot be restore the TUI launch normally as if --remember-selection wasn't passed. (default: `false`)
* `--diff` — Automatically show the diff when opening the TUI
# commands-but-unapply
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 `.
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`.
The identifier can be:
- A CLI ID pointing to a stack or branch (e.g., "bu" from `but status`)
- A branch name
If a branch name (or an identifier pointing to a branch) is provided,
the entire stack containing that branch will be unapplied.
For more details about CLI IDs, see `but help cli-ids`.
**Usage:** `but unapply `
## Arguments
* `` — The branch or stack to unapply (required)
# commands-but-uncommit
For more details about CLI IDs, see `but help cli-ids`.
**Usage:** `but uncommit [OPTIONS]`
## Arguments
* `` — One or more commits, branches, or committed files to uncommit.
Sources must all be the same kind. (required)
## Options
* `--allow-merged` — Allow targeting branches and commits that are already merged upstream.
By default, mutations refuse to touch history that has landed in the target branch, since the results tend to conflict on the next but pull.
# commands-but-undo
**Usage:** `but undo`
# commands-but-update
Check for new versions, install updates, or suppress update notifications.
**Usage:** `but update `
## Subcommands
### `but update check`
Check if a new version of the GitButler CLI is available
**Usage:** `but update check`
### `but update suppress`
Suppress update notifications temporarily
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:**
* `` — Number of days to suppress (1-30, default: 1)
### `but update install`
Install or update the GitButler desktop application.
By default, auto-detects your current channel (release/nightly) and installs the latest version for that channel.
macOS: Installs the full GitButler desktop application. The CLI (but) is included with the app and will also be updated.
Linux: Installs and updates only the CLI itself.
Note: For other platforms and install forms, see https://gitbutler.com/downloads
**Usage:** `but update install [TARGET]`
**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
# commands-but-worktree
Without a subcommand, lists the worktrees.
**Usage:** `but worktree `
## Subcommands
### `but worktree list`
List linked worktrees, most recently updated first.
By default this lists every active worktree and the three most recently updated
archived ones. A worktree is shown by its name, followed by the branch it has checked
out when that differs from the name, and its path.
**Usage:** `but worktree list [OPTIONS]`
**Options:**
* `--archived` — List all archived worktrees
* `--active` — List all active worktrees
### `but worktree archive`
Hide a worktree from the workspace
**Usage:** `but worktree archive `
**Arguments:**
* `` — The worktree, by CLI ID (see but wt list) or name (required)
### `but worktree unarchive`
Show an archived worktree in the workspace again
**Usage:** `but worktree unarchive `
**Arguments:**
* `` — The worktree, by CLI ID (see but wt list) or name (required)
### `but worktree remove`
Remove a worktree from disk, like `git worktree remove`.
This works on archived worktrees too, and keeps the branch the worktree had checked out.
**Usage:** `but worktree remove [OPTIONS]`
**Arguments:**
* `` — The worktree, by CLI ID (see but wt list) or name (required)
**Options:**
* `-f`, `--force` — Remove the worktree even if it has uncommitted changes
# commands-commands-overview
## Command reference
### Basics
- [setup](./but-setup): Set up a Git repository to be managed by GitButler
- [teardown](./but-teardown): Exit GitButler mode and return to normal Git
### Inspection
- [status](./but-status): Show the project workspace state
- [diff](./but-diff): Show a diff for the workspace or a CLI ID
- [show](./but-show): Show information about a commit or branch
- [help cli-ids](./but-help-cli-ids): Learn how to use IDs from command output
### Branching and committing
- [commit](./but-commit): Commit changes to a stack
- [branch](./but-branch): Commands for managing branches
- [discard](./but-discard): Discard uncommitted changes
- [resolve](./but-resolve): Resolve conflicts in a commit
- [unapply](./but-unapply): Unapply a branch from the workspace
- [apply](./but-apply): Apply a branch to the workspace
- [clean](./but-clean): Remove empty branches from the workspace
- [pick](./but-pick): Cherry-pick a commit from an unapplied branch
### Server interactions
- [push](./but-push): Push changes in a branch to remote
- [pull](./but-pull): Pull upstream changes and update your branches
- [pr](./but-pr): Create and manage reviews on GitHub, GitLab, and other forges
- [land](./but-land): Land a branch directly onto the target branch
### Editing commits
- [absorb](./but-absorb): Amend changes into the commits where they belong
- [reword](./but-reword): Edit a commit message or branch name
- [uncommit](./but-uncommit): Move commit changes back to the uncommitted area
- [amend](./but-amend): Amend a file change into a commit
- [squash](./but-squash): Squash commits together
- [move](./but-move): Move a commit or branch to a different location
### Operations log
- [oplog](./but-oplog): View and manage operation history
- [undo](./but-undo): Undo the last operation
- [redo](./but-redo): Redo the last undo
### Helper commands
- [gui](./but-gui): Open the GitButler GUI for the current project
- [tui](./but-tui): Open the interactive terminal UI
- [update](./but-update): Manage GitButler CLI and app updates
- [completions](./but-completions): Generate shell completions
- [alias](./but-alias): Manage command aliases
- [config](./but-config): View and manage GitButler configuration
- [agent](./but-agent): Set up GitButler for AI coding agents
- [skill](./but-skill): Manage AI agent skills for GitButler
# community-contact-us
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"
/>
# community-open-source
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).
# community-supporters
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://gitbutler.com/profile"
title="GitButler"
description="Support GitButler with a monthly contribution"
/>
Thanks, from the GitButler Crew!
# development-debugging
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.
### Per-project repository data
Most repository-specific GitButler state lives next to the repository itself, inside the `.git` directory. By default, GitButler stores that data in `.git/gitbutler` for
release builds, nightly builds and developer builds.
You can override that location with a Git config change, here for the stable build:
```bash title="Terminal"
❯ git config --local gitbutler.storagePath gitbutler-alt
```
You can also set it globally if you want all repositories opened by GitButler on that machine to use the same base configuration:
Setting it to a shared directory is useful if the project locations themselves are on a filesystem that doesn't support Sqlite very well,
like a network drive.
```bash title="Terminal"
❯ git config --global gitbutler.storagePath /path/to/gitbutler-projects
```
Setting it to a relative path would force a channel, like Nightly, to reuse the data of stable builds.
```bash title="Terminal"
❯ git config --global gitbutler.nightly.storagePath gitbutler
```
The Git config key depends on the app channel:
- Release builds use `gitbutler.storagePath`
- Nightly builds use `gitbutler.nightly.storagePath`
- Developer builds use `gitbutler.dev.storagePath`
There are a couple of constraints on the configured path:
- Relative paths are resolved relative to the repository's `.git` directory
- If the resolved path stays inside `.git`, it must be under a top-level directory whose name starts with `gitbutler`, case-insensitive
- You cannot point it at `.git` itself
- If the resolved path ends up outside `.git`, GitButler appends a project-path specific identifier so multiple repositories can share the same base directory safely
For example, these are valid:
```bash title="Terminal"
❯ git config --local gitbutler.storagePath gitbutler-alt
❯ git config --local gitbutler.storagePath ../../gitbutler-projects
```
If you use a path outside `.git`, GitButler treats it as a base directory. For example, `../../gitbutler-projects` becomes something like `../../gitbutler-projects/` after resolution.
## 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`.
# features-branch-management-ai-assistance
## 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
# features-branch-management-branch-lanes
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 parallel 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 parallel 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 parallel 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.
# features-branch-management-commits
GitButler gives you a lot of tooling for creating, modifying, squashing, splitting and undoing commits.
GitButler has lots of ways to craft the exact commits that you want to end up with. With other Git clients, you tend to have to run somewhat complicated `git rebase -i` type commands to change commit messages, split a commit up or absorb new changes into an existing commit. With GitButler, most of these are simply drag-and-drop operations.
Here are some of the cool things you can do very easily with GitButler.
## Creating Commits
Once you have changes on a virtual branch and want to commit them, you can hit the "Start a Commit" button in any lane, which gives you an editor to write a summary and optional description for your commit message.
If you want AI to use your diff to generate a commit message, you can hit the "Generate message" button.
## AI Commit Message Settings
If you want to use AI for generating your commit messages or branch names from time to time, there are quite a few options in your user preferences. You can choose from [OpenAI](https://platform.openai.com/), [Anthropic](https://www.anthropic.com/), [Ollama](https://www.ollama.com/) or [LM Studio](https://lmstudio.ai/) as your engine.
For both OpenAI and Anthropic, you can either use your own API key to directly send your request to their servers, or you can proxy via our server (which you need to be logged in for).
If you use your own key for OpenAI or Anthropic, you can choose which model you would like us to use.
If you don't want to send your diff to another server, you can also use Ollama or LM Studio, which are a local LLM servers.
With Ollama, you can run nearly any open source large language model ([Llama 3](https://www.ollama.com/library/llama3), [Phi 3](https://www.ollama.com/library/phi3), [Mistral](https://www.ollama.com/library/mistral), [Gemma](https://www.ollama.com/library/gemma), etc) entirely locally.
Note that if you choose to configure a self-hosted Ollama server, you will likely need to add a custom CSP (Content Security Policy) to allow GitButler to connect to it.
You can find more information on how to do that in the [Custom Content Security Policy (CSP)](/troubleshooting/custom-csp) section of the documentation.
With all of these models, you can also customize the prompt if you want something more specific. In the "Custom AI prompts" section, you can add new prompts and select which one you want to use per project. This is useful for following certain formats or generating messages in other languages, etc.
Custom prompts can contain three variables which we will replace with the appropriate values. Those include:
- `%{emoji_style}` - Instructs the LLM whether or not to make use of [GitMoji](https://gitmoji.dev) in the title prefix, based on your settings.
- `%{brief_style}` - Instructs the LLM to not exceed 1 sentence when generating the commit message.
- `%{diff}` - The contents of the diff.
- `%{branch_name}` - The name of the current branch. Available in "Commit Message" custom prompt only.
## Absorbing New Work
If you have a commit and get some feedback on it or find an issue and wish to amend it, you can very easily absorb changes into existing commits. Simply drag the file into the commit you want to absorb that change into and drop it there.
This will both rewrite that commit to include the new changes and also rebase every commit upstream from it automatically.
## Undoing Commits
You can easily undo any commit in your stack by expanding the commit and hitting the 'Undo' button. This will rebase all the commits above it and leave whatever work was in that commit as new uncommitted changes.
"Undo"ing a commit does not throw it away, it simply makes that work not in a commit anymore. It
will not discard the changes.
## Undoing One File in a Commit
If you want to undo a single file in a commit, you can expand the commit and click on the file you want to undo. Then hit "Uncommit". This will remove that file from the commit and leave it as uncommitted changes.
## Squashing Commits
Squashing two commits into a single combined commit is also very simple. Just drag one commit on top of another one.
## Splitting Commits
Splitting commits is slightly more complex. GitButler allows you to create an "empty" commit anywhere and then drag changes into it. Here is an example of creating an empty commit between two other commits, dragging changes from both of them into it and then absorbing new work into it as well.
You can also notice that I easily edit the commit message by just hitting the "edit message" button.
## Moving Commits
You can also arbitrarily change the order of your commits by dragging and dropping them, which rebases everything to change the order.
## 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 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.
Then you can change whatever you want and when you click "Save and exit", it will amend the commit you were editing and rebase anything on top of it.
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.
# features-branch-management-merging
By default, GitButler rebases the work on your parallel branches when you update your target branch (upstream) work.
Often this works just fine and the commits are simply rebased. Occasionally, you will have conflicts with upstream work.
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:
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.
When you click that, GitButler will remove the other parallel 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.
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.
# features-branch-management-moving-branches
You can drag a branch from one stack to another to change what it depends on.
You can also tear a branch off into an independent stack.
## Move a branch to another stack
Drag the branch to the position where it belongs in the target stack:
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.
### Drop zones
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
The drop zones appear as horizontal lines with visual feedback when you hover over them during a drag operation.
## Tear a branch off into a new stack
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.
### 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 only that 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 the branch has an associated pull request or merge request:
- **Pull request bases** - Each pull request targets the nearest branch below it that also has a pull request
- **GitHub stack membership** - GitButler updates the native stack on GitHub to match the new branch order
- **Description-based stacks** - Repositories without native stack support get updated stack information in their pull request descriptions
The synchronization runs on the next push or pull request creation.
## Related features
- [Create stacked branches and pull requests](/features/branch-management/stacked-branches)
- [Use the Branches page](/features/branch-management/branch-lanes)
- [Work with parallel branches](/features/branch-management/virtual-branches)
# features-branch-management-pushing-and-fetching
GitButler uses your system Git executable to authenticate with your remote. This means it uses the same SSH configuration, SSH agent, or [credential helper](https://git-scm.com/doc/credential-helpers) as Git in your terminal.
Open your project settings, go to the Git section, and select **Test credentials** to verify that GitButler can push to the remote. The test pushes an existing commit to a temporary remote branch and removes the branch after the check.
If the test fails, configure authentication for your system Git first, then run the test again. GitButler no longer has separate settings for selecting an SSH key or credential helper.
Once authentication works, GitButler can automatically fetch upstream work and push new branches to your remote.
## Land branches without pull requests
If your project does not review changes through pull requests, you can switch a project to the "push to main" workflow. During repository onboarding, check "Push to main / Skip pull requests mode". For an existing project, open the project settings, go to the Git section, and turn on "Land branches directly".
With this on, the "Create PR" button on the bottom branch of a stack becomes a "Land" button. Landing integrates the branch straight into the target branch — fast-forwarding when possible, otherwise with a merge commit — and pushes the result. It works without a forge integration.
Landing bypasses code review, CI checks, and branch protection, and a branch protected against direct pushes will reject it. The CLI equivalent is [`but land`](/commands/but-land).
# features-branch-management-rules
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.