# 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.

Creating a rule that assigns changes to the leftmost lane

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

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

## Managing Rules ### Editing Rules To edit an existing rule: 1. Double-click the rule or click the ellipsis menu (...) and select "Edit rule" 2. Modify the branch assignment or filters 3. Click **Save rule** ### Deleting Rules To delete a rule: 1. Click the ellipsis menu (...) on the rule 2. Select "Delete rule" 3. Confirm the deletion ## Understanding Rule Evaluation ### Order Matters Rules are evaluated in the order they appear in the Rules drawer (most recent first). The first matching rule determines where a change is assigned. ### AND Logic Within Rules When a rule has multiple filters, **all filters must match** for the rule to apply. For example, a rule with both "Path Matches Regex: `.*\.ts$`" and "Content Matches Regex: `TODO`" will only match TypeScript files that contain the text "TODO" in their changes. ### OR Logic Across Rules If you want to match changes that meet any of several conditions (OR logic), create separate rules for each condition. ### Interaction with Hunk Dependencies Rules respect hunk dependencies (locks). If a change depends on a commit in a specific branch, it cannot be automatically reassigned by rules, even if it matches a rule's filters. ## Best Practices 1. **Start simple**: Begin with one or two basic path-matching rules before adding complex filters 2. **Order your rules**: Place more specific rules before general catch-all rules 3. **Test your regex**: Make sure your regular expressions match what you intend - it's easy to be too broad or too narrow 4. **Use catch-all rules carefully**: A rule with no filters will match everything, which can interfere with other rules 5. **Consider your workflow**: Rules work best when you have predictable patterns in how your work is organized 6. **Leverage leftmost/rightmost**: Using position-based targeting lets you reorganize lanes without updating rules ## Limitations - Rules can only assign changes to branches that exist in your workspace (applied branches) - Rules currently only support the `assign` action for filesystem changes ## Related Features - [Parallel Branches](/features/branch-management/virtual-branches): Understanding the branch system that rules work with - [Branch Lanes](/features/branch-management/branch-lanes): How lanes are organized and how rules interact with lane positioning # features-branch-management-signing-commits GitHub and GitLab provide a mechanism to verify signed commits using an uploaded public SSH or GPG key. GitButler can be configured automatically sign all your commits. Git provides a mechanism to sign your commits with a GPG key or SSH key. This enables other developers to make sure that you were actually the person who committed it, rather than someone else just setting their email to yours and committing it as if they were you. To make this work, a signature is added to the commit header and then that signature is checked against public key stored somewhere, generally for most people the most useful way to verify these signatures is through GitHub or GitLab. This is what a verified commit looks like on both systems: This means that the server has a public key that you used to sign the commits that is associated to your account and has verified that this user actually signed this commit. In order for this to work, you need to: 1. Tell GitButler to sign your commits 2. Upload your key as a "signing key" to GitHub or GitLab (or elsewhere) ## Telling GitButler to Sign For GitButler to sign commits, you need to setup Git to sign commits, as we do roughly the same thing that Git itself tries to do, and we read and respect most of the same Git config settings. The main difference is that instead of only the `commit.gpgSign` as the flag that tells Git to automatically sign commits, we look for `gitbutler.signCommits` first. Thus, if Git would sign, GitButler will attempt to sign your commits with the normal Git settings as well. But if something goes wrong, `gitbutler.signCommits` will be set to `false` in the repository-local settings to prevent commits from failing generally. Set `gitbutler.signCommits` to `true` in your user-level `~/.gitconfig`, to prevent GitButler from automatically disabling commit signing for that repository. We look to see if we have a signing key in `user.signingkey`. If we have a key, we look for 'ssh' in `gpg.format`, otherwise we use GPG. We will respect `gpg.ssh.program` for ssh if there is a different binary path, and `gpg.program` for GPG. We also identify literal SSH keys in the `user.signingkey` field. The only major thing we don't support yet is `gpg.ssh.defaultKeyCommand` for other ways to get a key other than the `user.signingkey` field. We also don't support the X.509 smime stuff. Here is an example, if you have a public key here `.ssh/id_ed25519.pub` then you can setup signing with something like this: ```bash title="Terminal" $ git config --global user.signingkey "/Users/schacon/.ssh/id_ed25519.pub" $ git config --global gpg.format ssh $ git config --global gitbutler.signCommits true ``` You can also set this up in your project settings, perhaps a little more easily: The nice thing here is that you can also test the settings easily by hitting the "Test Signing" button. There are lots of other ways to set up GPG or SSH commit signing: - 1Password is a very easy way to [SSH sign commits](https://blog.1password.com/git-commit-signing/). - GitHub has a [good guide](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key) on how to setup GPG or SSH keysigning. - Here is a nice simple gist for [GPG signing on Windows](https://gist.github.com/BoGnY/f9b1be6393234537c3e247f33e74094a). Earlier versions of GitButler would only sign with its generated SSH key. Although we've removed that functionality, you can easily set it back up by pointing the signingKey at the generated SSH Key. The key is located in the following locations: ```bash /Users/[username]/Library/Application Support/com.gitbutler.app/keys/ed25519.pub ``` ```bash C:\Users\[username]\AppData\Roaming\com.gitbutler.app\keys\ed25519.pub ``` ```bash ~/.local/share/gitbutler-tauri/keys/ed25519.pub ``` ## Upload Your Signing Key For GitHub or GitLab to verify your signatures, you need to say that the SSH or GPG public key we are using is a valid signing key for your user. ### Adding to GitHub You can click on the "Add key to GitHub" link in the settings page right about the signing toggle, or you can go here (https://github.com/settings/ssh/new) to paste that public key in. Now your signed commits should show up as "Verified". ### Adding to GitLab For GitLab you need to go to "SSH Keys" in your profile: https://gitlab.com/-/profile/keys and click the "Add new key" button. Now paste in the public SSH key you copied from GitButler, name it and make sure the "Usage Type" is either "Signing" or "Authentication and Signing". # features-branch-management-stacked-branches Use a stack when one change depends on another but each change still deserves its own review. GitButler keeps every branch in the same workspace and publishes each branch as a focused pull request. The branch closest to your target branch is the **bottom** of the stack. Work that depends on it goes above it. ## Create a local stack In GitButler Desktop: 1. Create the bottom branch and commit the first reviewable change. 2. On that branch, click the **Create new branch** stack icon. 3. Name the dependent branch and commit the next change to it. 4. Repeat for each dependent change. New commits go to the top branch unless you select a different branch first. You can also [move existing branches between stacks](/features/branch-management/moving-branches) or drag commits to the branch where they belong. To stack two existing branches with the CLI, put the dependent branch above its dependency: ```bash but move checkout-ui --above checkout-api ``` ## Publish the pull request stack Connect your forge account before publishing. For GitHub, see [Connect GitButler to GitHub](/features/forge-integration/github-integration). In Desktop, create pull requests from the bottom branch upward. If you try to create one above a lower branch that has no pull request yet, GitButler warns you that the lower pull request provides its review base. With the CLI, name the top branch to publish missing pull requests for the whole stack. This example uses `-t` to accept the default titles and descriptions without prompting: ```bash but pr new checkout-ui -t ``` GitButler pushes the branches and sets the pull request bases: - The bottom pull request targets the repository's target branch, usually `main`. - Every pull request above it targets the nearest branch below it that also has a pull request. That keeps each pull request's **Files changed** view limited to the change being reviewed. ## Choose how the stack is published GitButler publishes stacks as native GitHub stacked pull requests when the repository supports them. Otherwise, it uses ordinary pull requests or merge requests with GitButler-managed stack information in their descriptions. The default GitHub mode, **Auto**, uses native stacks when available and falls back otherwise. To require or disable native stacks, see [Use native GitHub stacked pull requests](/features/forge-integration/github-integration#use-native-github-stacked-pull-requests). ## Update a published stack Amend commits, move branches, or add another branch as the review changes. When you push again, GitButler updates the review bases to match the local branch order. It also synchronizes the native GitHub stack or the stack information in the review descriptions. ## Merge the stack For a native GitHub stack, use GitHub's stack controls. GitHub can merge a higher pull request together with the pull requests below it. For description-based stacks, merge from the bottom upward. Enable [automatic branch deletion on GitHub](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-the-automatic-deletion-of-branches) so GitHub can retarget the next pull request after its base branch lands. After merging, update the workspace from the target branch. In the CLI, run: ```bash but pull ``` # features-branch-management-upstream-integration Sometimes you work on a branch and someone else pushes to the same upstream branch. Often you won't know this until you try to push and Git tells you something like this: In this scenario, GitButler gives you some nice tooling to help you know when this happens as early as possible and help you deal with it easily. If someone else has pushed to a branch that you're working on, you will see the upstream commits without having to integrate them. You can look at the commits without having to merge them into your branch or rebase your work on top of them. When you decide that you do want to integrate the changes, you have two options - rebase or interactively integrate. ## Rebase the changes If you select "Rebase upstream changes", it will do the equivalent of a `git pull --rebase` which rebases the commits you have locally on top of the ones that the other person has pushed, so you end up with a state like this: Now you can push your commit back upstream without a force push. Easy peasy. ## Interactively integrate the changes However, let's say that you want to do something more complex. Maybe the other implemented the same thing that you did and you want to drop one of them or one of yours, or squash commits together or reorder them. In any of these cases, you can choose the "Interactive integration" option and you get something that looks like this: Here you can reorder commits however you want, you can choose to skip some of them, you can squash some of them down, etc. Just make the commits look however you prefer and then hit the "Integrate changes" button and push your final result back to the server. # features-branch-management-virtual-branches Parallel branches are a powerful feature of GitButler that allow you to work on multiple branches at the same time, committing to them independently and simultaneously. This is a key part of the GitButler experience, allowing you to manage your work in a flexible and efficient way that is not possible with traditional Git tooling. ## Overview With normal Git branching, you can only work on one branch at a time. There is one `HEAD` reference and one index. With parallel branches, you can have multiple branches applied to your working directory at the same time. Each branch is represented as a vertical lane, and you can drag changes between these lanes to commit them independently. Each lane also has its own staging area, so you can stage changes for each branch before deciding to commit them. ## How it works Let's say that you make changes to two different files and `git status` would list two modified files. In GitButler, you can stage the change in each file to a different branch, then commit each branch independently. One of the nice things with this approach is that since you're starting from changes in a single working directory, you can be sure that all branches that you create from it will merge cleanly, as you're essentially starting from the merge product and extracting branches of work from it. # features-forge-integration-github-integration Connect GitButler to a GitHub account to create and manage pull requests without leaving the app. You can connect multiple GitHub accounts and choose which one each project uses. ## Connect a GitHub account Open GitButler settings from the profile button in the sidebar. Go to **Integrations**, then click **Add account** for GitHub. You can add an account three ways: - **Device Flow** generates a code and opens GitHub in your browser for authorization. This is the recommended option for most users. - **Personal Access Token** connects an account with a token you create on GitHub. - **GitHub Enterprise** connects to an Enterprise instance with its API URL and a personal access token. We'll select the **Device Flow** option for this example. A code will be generated for you. Copy it to your clipboard. Click **Open GitHub activation page** to continue in your browser. Paste the code into GitHub and approve the authorization request. Return to GitButler and click **Check the status**. Your GitHub username appears in the Integrations tab after the account connects. To disconnect an account, click **Forget** next to its name. To connect with a personal access token, select **Personal Access Token** when adding the account. GitButler needs read access to **Metadata** and read and write access to **Pull Requests**. These requirements may change as GitButler adds GitHub features. For GitHub Enterprise, select **GitHub Enterprise** and enter the instance's API base URL and a personal access token with the same permissions. For example, if your GitHub Enterprise Server's hostname is **github.a-super-serious-company.com**, then set the base URL to **github.a-super-serious-company.com/api/v3**. See [GitHub's Octokit base URL documentation](https://github.com/octokit/octokit.js?tab=readme-ov-file#constructor-options) for more information. ## Create and manage pull requests After connecting GitHub, create a pull request from its GitButler branch. GitButler checks whether each branch has an associated pull request. You can view those pull requests from the **Pull Requests** section of the Branches page. ## Use native GitHub stacked pull requests GitButler uses native GitHub stacked pull requests when: - The repository is enrolled in GitHub's stacked pull requests private preview. - The pull requests use branches in the same repository, not a fork. - The project has not disabled native stacks. The default mode is **Auto**. GitButler detects whether the repository supports native stacks and registers stacks containing at least two open pull requests. GitHub renders the stack, so GitButler does not add stack information to the pull request descriptions. If the repository is not enrolled, Auto falls back to ordinary pull requests with GitButler-managed stack information in their descriptions. Fork-backed pull requests always use this fallback. Change the mode under **Project settings → Project → Native GitHub stacked pull requests**: - **Auto** uses native stacks when available and falls back otherwise. - **Native** requires native stack support and reports an error when the repository is not enrolled. - **Disabled** always uses description-based stack information. The **Stack information in review descriptions** setting controls whether fallback information appears at the top, bottom, or not at all. It does not affect native stacks. The same project-local setting is available in the CLI: ```bash but config forge github-stacks auto but config forge github-stacks enable but config forge github-stacks disable ``` The CLI values map to Desktop as follows: - `auto` selects **Auto**. - `enable` selects **Native**. - `disable` selects **Disabled**. Changes take effect on the next push or pull request creation. GitButler then updates the pull request bases and either the native stack membership or the fallback descriptions. See [Create stacked branches and pull requests](/features/branch-management/stacked-branches) for the publishing workflow. # features-forge-integration-gitlab-integration Create your GitLab Merge requests without leaving GitButler. ## Setting up the GitLab Integration In order to set up the GitLab integration, you will need two things. Firstly, a GitLab Personal Token and the Project ID for your project. ### Creating a GitLab Personal Token To create a GitLab Personal Token, navigate to your GitLab preferences, and then click on the "Access tokens" tab. You will then see an "Add new token" button. Pressing this button will provide you with a form (as captured below) where you can specify the token name, expiration date, and scopes. We currently require the `api` scope. In this example, I've chosen to set the expiration date for the token to a year from now. On completion, you will be showen the following screen which contains your GitLab Personal token. ### Finding your GitLab Project ID Navigate to the main page of the repository you want to configure the integration for. On the top left next to the "Fork" button, you will see three dots. Click on those three dots, and click on "Copy project ID". This will copy the GitLab Project ID to your clipboard. ### Configuring GitButler Inside GitButler, navigate to the project settings by clicking on the small cog icon in the bottom left. Scroll down to the bottom of the "Project" tab where you will see a form for entering your GitLab details. The provided GitLab Personal Token will be stored securly in your operating system's keychain. ### Custom GitLab Instances You may also provide a different Instance URL if you are using a self-hosted GitLab instance. Note that if you use a custom GitLab instance, you will likely need to configure 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. ## Usage You will now have a "Submit for Review" button on each branch which you can use to create a Merge Request. Once the Merge Request is created you will be able to see the status of it from within the client. Please note that we do not yet integrate with CI status. # features-gerrit-mode Not _everyone_ uses GitHub or GitLab to review code and collaborate. If you use the [Gerrit](https://www.gerritcodereview.com/) code review tool, GitButler has a mode for you! In fact, GitButler is the best Gerrit client there is. ## What is Gerrit If you've never heard of Gerrit, it's used by large teams like the Android or Chrome projects to manage huge numbers of changes and users across large numbers of interdependent repositories. Here is an example of incoming changesets on the [Android project](https://android-review.googlesource.com/q/status:open+-is:wip,50): ## How is Gerrit different than Pull/Merge Requests? Good question. With GitHub or GitLab, when you send a pull/merge request, the review process is branch based. If you add more commits on top of your branch, the changes are squashed into one big unified diff for review. Most teams tend to avoid rebasing anything that was already shared. Gerrit is a commit based review system. Every review is based on exactly one commit. It's very common to edit shared commits and submit new versions of them to address feedback. This model works _very well_ with GitButler's easy [commit editing](/features/branch-management/commits) features. With any other Git client, interactive rebasing and amending tends to be quite painful and error prone, making it fairly difficult to work with Gerrit's model. With GitButler, it's ideal. Just drag and drop changes and update your changesets easily. ## How to turn on Gerrit Mode If you have a Gerrit remote, GitButler will automatically enable Gerrit Mode when the project is being added to GitButler. You can also enable it manually. To manually turn on Gerrit Mode in GitButler, you just have to set a Git config option called `gitbutler.gerritMode` in the project you want to act in a Gerrit compatible fashion: ``` ❯ cd my_project ❯ git config gitbutler.gerritMode 1 ``` ## What is Gerrit Mode Now GitButler will change its behavior in the following ways: - When you commit, we will automatically inject a `Change-Id` trailer into the commit in the format that Gerrit expects. You do not need to [setup a `commit-msg` hook](https://gerrit-review.googlesource.com/Documentation/cmd-hook-commit-msg.html) like you do with other Git clients. - When you push, it will not push to a matching branch name on the remote. Instead it will push to `refs/for/main` (or whatever the name of the target branch is set to be). - After a push, we record the change url and show you the link and number for each commit automatically. We can also set some extra push options when we push, including: - [Topics](https://gerrit-review.googlesource.com/Documentation/cross-repository-changes.html) - [Hashtags](https://gerrit-review.googlesource.com/Documentation/intro-user.html#hashtags) - [WIP status](https://gerrit-review.googlesource.com/Documentation/intro-user.html#wip) # features-timeline Undo nearly any of your actions or go back in time to an earlier state. ## How it works Before GitButler does any major action, it records the state of everything (your virtual branch state, your uncommitted work, conflict state, etc) and stores it in your Git object database as snapshots. You can hit the 'revert' button on any of the entries and it will restore the state of all of these things to what they looked like when they were recorded, letting you go back in time. ## Restoring State If you hover over any of the entries, you will see a button named "Revert" that will restore the state of things to right before you did that action. So if you revert one that says "Create Commit", it will put you where you were right before you made that commit. ## Recovering Content Occasionally, GitButler will also take snapshots of files that were changed recently, even if they weren't committed. If this, or any other action, sees changes in files, you can see which ones and view the change by clicking on the file name. # review-overview # Butler Review We have paused work on Butler Review for now to concentrate more on the client experience. Review will be coming back in our upcoming server functionality, stay tuned. Butler Review is a new approach to code review that focuses on reviewing code as a series of evolving patches rather than a unified diff of a whole branch. ## Why Butler Review? Traditional code review tools like GitHub and GitLab show changes as a single diff of the entire branch. This makes it hard to review changes in a branch incrementally, especially when the branch is large. It also discourages creating good commit or commit messages, since they are all sqaushed in review and messages are difficult to find and unrelated to the code review UI. Butler Review approaches the problem differently, by making it easy to review individual commits as patches and tracking changes to the series when you rebase or amend the branch. This makes it easier to review changes incrementally and evolve a series rather than pushing poor commits to the branch to address feedback. ## Enabling Butler Review To start using Butler Reviews, you need to enable the feature in your GitButler client in the project settings. Click the gear icon in the bottom left corner of the GitButler client and go to the Server tab. Once you enable Butler Review, you can start creating reviews for your branches. ## Creating a Review Once you have enabled Butler Review for a project, you can create a review for a branch by clicking the new "Create Butler Review" button in the branch header card. Once you create a review, you will see a new card in the branch header that shows the status of the review. If you have GitHub integration enabled, you will see a dropdown to let you choose to create a PR or a Butler Review. You can also create both, in either order and they will be linked together. Once a Review has been opened, you will have a URL that you can share with your team to get feedback on your changes. ## Reviewing a Series When a reviewer opens a review, they will see a list of patches that make up the branch. Currently, all reviews are "unlisted", meaning that anyone with the URL can see the review. In the future, we will implement fully public and fully private reviews, but currently all reviews are unlisted. However, if someone has the URL, they will only be able to see the patch data, they will not have access to all of your source code, nor will they be able to make changes, only approve or request changes. Now the reviewer can hit "Start Review" to start reviewing the patches in the series. It will start them at the "bottom", the first patch and they can work their way up. The reviewer can also click on a patch to go directly to it. ## Reviewing a Patch Once a reviewer starts reviewing a patch, they will see the changes in the commit and a chat window to leave comments. The reviewer can leave comments on the patch, approve the patch, or request changes. The chat discussion is real time, so reviewers can discuss the changes with the author as they review the patch. You can also select lines of code to comment on. This will highlight the line in the diff and show the comment in the chat window. Every patch needs to be approved before the branch is considered approved. ## Requesting Changes A reviewer can either approve a patch or request changes. If they request changes, the author can amend that specific commit and publish the changes to the review. Reviewers will be able to see in the chat that a new version of the patch has been published and they can review the changes again. Currently we don't have interdiffs, so reviewers will need to review the entire patch again. We plan to add interdiffs in the very near future. ## Approving a Patch Once a reviewer is happy with a patch, they can approve it. This will mark the patch as approved and move the reviewer to the next patch in the series. ## Closing a Review Once the branch has been merged, it will automatically mark the review as closed. The author can also manually close the review if they decide to abandon the series. # troubleshooting-custom-csp By default GitButler uses a strict Content Security Policy (CSP) to protect against various attacks, such as cross-site scripting (XSS) and data injection attacks. This policy restricts the sources from which content can be loaded, as well as the hosts the application can connect to, ensuring that only trusted sources are allowed. However, there are some cases where you may need to customize the CSP to allow certain features or integrations. Some examples include: - Self-hosted GitHub Enterprise instances - Self-hosted GitLab instances - Self-hosted Ollama instances In those cases you are likely to observe an error message that looks something like this: ``` Refused to connect to https://./api/v4/projects/9/merge_requests because it does not appear in the connect-src directive of the Content Security Policy. ``` You can resolve this issue by adding your host to the CSP. ## Adding a Custom CSP You can add a custom CSP by editing the GitButler configuration file, found at the following location: ```bash ~/Library/Application\ Support/gitbutler/settings.json ``` ```bash C:\Users\[username]\AppData\Roaming\gitbutler\settings.json ``` ```bash ~/.config/gitbutler/settings.json ``` The file is in JSONC format and follows the [following schema](https://github.com/gitbutlerapp/gitbutler/blob/master/crates/but-settings/assets/defaults.jsonc) In order to add your custom CSP entry, you want to add an `extraCsp` entry to the JSON file. The `extraCsp` entry is an object that contains a `hosts` array, which is where you can add your custom hosts. For example: ```json "extraCsp": { "hosts": ["https://subdomain.example.com", "http://another-subdomain.example.com"] } ``` Note that if `extraCsp` is the only entry in the JSON file, you may want to enclose it in a top-level object, like this: ```json { "extraCsp": { "hosts": ["https://subdomain.example.com", "http://another-subdomain.example.com"] } } ``` The changes will take effect the next time you start GitButler. # troubleshooting-fetch-push GitButler uses your system Git executable to communicate with remotes. If pushing or fetching fails, check that your system Git can authenticate with the same remote. ## Configuring the auto-fetch frequency GitButler will periodically fetch from your configured remotes in order to display new branches etc. By default, this happens every every 15 minutes. You can configure the interval or completely disable this behavior by editing the application's `settings.json` file: ```bash ~/Library/Application\ Support/gitbutler/settings.json ``` ```bash C:\Users\[username]\AppData\Roaming\gitbutler\settings.json ``` ```bash ~/.config/gitbutler/settings.json ``` The file is in JSONC format and follows the [following schema](https://github.com/gitbutlerapp/gitbutler/blob/master/crates/but-settings/assets/defaults.jsonc) ```json "fetch": { "autoFetchIntervalMinutes": 15 } ``` A negative value (e.g. -1) disables auto fetching. Note that if `fetch` is the only entry in the JSON file, you may want to enclose it in a top-level object. ## Check Git authentication GitButler no longer provides separate authentication modes for selecting an SSH key or credential helper. Configure authentication for your system Git instead: 1. In the repository, run `git fetch` from your terminal. 2. If it fails, fix your Git authentication. Configure SSH through your SSH agent and `~/.ssh/config`, or configure an HTTPS [credential helper](https://git-scm.com/doc/credential-helpers). 3. In GitButler, open the project's settings, go to the Git section, and select **Test credentials**. SSH keys managed by tools such as 1Password or backed by a FIDO security key work when they are available to your system Git through the SSH agent. ## Get more help If system Git can fetch and push but GitButler still fails, share the error and your [GitButler logs](/development/debugging#logs) in our [Discord](https://discord.gg/MmFkmaJ42D). # troubleshooting-fixing-conflicts-outside-gitbutler If you have ended up with conflicted commits and GitButler is completely unresponsive, they can be recovered using plain git commands in the following manner: ## Consider hopping on a call with one of us. The resolution steps make use of some advanced git functions, if you are not comfortable with any of the steps - we are more than happy to walk you through any recovery processes. Join our Discord and let us know about your situation. One of us will help you work through your problem either through text or via a call. ## Backup! First, make a copy of your entire repo. We don't want to lose any data if we make a mistake in recovery. ## Make a new branch Conflicts often come up as part of a cherry pick so we want to re-perform the rebase manually - resolving any conflicted commits as we go. I want the commits to sit on top of origin/master, so I'll run the following commands to make a new and empty branch to re-build the branch on top of: ``` git switch -c reconstruction git reset --hard origin/master ``` ## Looking at the commits We can now get an idea of what operations we need to perform to reconstruct the branch. By running: ``` git log --graph --oneline ``` We can see all the commits that are in our branch. For my branch, it looks as follows: ``` > git log --oneline --graph reimplement-insert-blank-commit * b1b1bf07d (reimplement-insert-blank-commit) Improvements to rebase engine for better composability * c8f5b92a0 Rename reword_commit to commit_reword * e1fc3b9f5 Reimplement insert blank commit ``` We want to work from the bottom of this list to the top. To get a better idea of the state of a given commit, we can run: ``` git cat-file -p ``` We can identify if the commit is conflicted by the presence of a header that looks as follows: ``` gitbutler-conflicted ``` ## Reconstructing the branch Depending on the state of a commit and it's parent, there are some different operations we want to perform to re-perform the rebase. ### If a commit is conflicted If a commit is conflicted, we want to first look at the tree of the conflicted commit. We can do that with the following command: ``` git cat-file -p ^{tree} ``` For the first commit in my list, that looks like: ``` > git cat-file -p e1fc3b9f5^{tree} 040000 tree 24e291fb0867efec629b933c00aaeaff39365efd .auto-resolution 040000 tree ffde17e2a4d4c045869b300b4ec9027851581e33 .conflict-base-0 100644 blob dca5869dd76a1eeadeba9387ec7f94b318085c7e .conflict-files 040000 tree 3b23a61344b84fa3f7b93b1ca058d24846a31f57 .conflict-side-0 040000 tree b5a91de1f2ce0a248472d03c1701a20289e4d657 .conflict-side-1 100644 blob 2af04b7f1384300b742f6112005cddc5a87be022 README.txt ``` Here we see the conflicted representation of a commit in GitButler. There are four entries that are relevant here: - `.auto-resolution` - This contains a resolution attempt that GitButler made when cherry-picking the commit. - `.conflict-base-0` - This contains the tree of the commit that was cherry-picked to produce the conflicted commit. - `.conflict-side-0` - This contains the tree of the commit that we tried to cherry-pick onto. - `.conflict-side-1` - This contains the tree of the origional commit before it was cherry-picked. To re-perform the cherry-pick that GitButler was trying to do. We do that by first making a commit that holds the `.conflict-base-0` tree which can be done by running: ``` git commit-tree -p HEAD -m "base" ``` For me, that looks like: ``` > git commit-tree ffde17e2a4d4c045869b300b4ec9027851581e33 -p HEAD -m "base" 0100ea63fe63a2894567de42371f8d6cf79e4a85 ``` This has given us an OID in return. This is the object ID of the commit we just created. We then want to create a commit that contains the `.conflict-side-1` tree, and has that new "base" commit as it's parent. We can do that by running: ``` git commit-tree -p -m "Desired commit message" ``` For me this looks like: ``` git commit-tree b5a91de1f2ce0a248472d03c1701a20289e4d657 -p 0100ea63fe63a2894567de42371f8d6cf79e4a85 -m "Reimplement insert blank commit" 35d518d2ea68635631593faff34b11e3b1904014 ``` Using that returned commit OID, we can then bring that commit on top of our branch with: ``` git cherry-pick ``` For me, that looked like: ``` git cherry-pick 35d518d2ea68635631593faff34b11e3b1904014 ``` Git may prompt you to solve some conflicts here which you can resolve in the standard manner. ### If a commit is **not conflicted**, but has a **conflicted parent**. If the commit is not conflicted, but the commit before it in your log WAS conflicted, then we similarly need to create a commit to cherry-pick on our own. First, you will want to take a look at that parent's commit tree with: ``` git cat-file -p ``` We want to make a base commit that uses the `.auto-resolution` tree. We can do that with: ``` git commit-tree -p HEAD -m "Desired commit message" ``` We then want to make a commit that has the tree of the non-conflicted commit, with the parent as the base commit we just made. We can first find the tree of the non-conflicted commit by running: ``` git cat-file -p ``` and copying the entry after `tree`. We then want to make our commit to cherry pick with: ``` git commit-tree -p -m "desired commit message" ``` We can then cherry-pick that commit with `git cherry-pick` onto our branch, following the standard conflict flow if applicable. ### If the commit is **not conflicted** and its parent is **not conflicted** If this is the case, we can run the standard `git cherry-pick` command to bring that commit into our reconstruction branch, following the standard conflict flow if applicable. ## Pushing your reconstructed branch Once you have finished bringing all of your commits into your reconstruction branch, you can then push it to your remote via `git push`. # troubleshooting-recovering-stuff How to dig around our internal data to find (nearly) anything GitButler saves data in a few different ways. As we're still in beta, sometimes things might break and it may look like you've lost work, but you almost certainly haven't. We're pretty good about saving stuff a lot. Here's how to recover almost anything you had in your working directory or parallel branches. ## GitButler References If everything crashes or the UI isn't working at all, you may be surprised to know that even though your parallel branches don't show up in a normal git branch output, we do actually constantly write them out as Git references (just not in refs/heads). ```bash title="Terminal" ❯ git for-each-ref | grep gitbutler e63b3bac82835dc17083a785d25db8b4b46744b9 commit refs/gitbutler/add-can-create-method-to-notebook 98ef3cd6eea14ee4159a600e448271c0d777efe2 commit refs/gitbutler/add-conditional-blocks-for-image-and-video c7e27b9f99f25160a4d5f07d5972c217bdd44319 commit refs/gitbutler/add-database-schema-conversion-script 4afdfed6c14b57491a9d295c31613fd79b92f63a commit refs/gitbutler/add-gems-for-test-group ``` These references are just like git branches - they point to a commit that has the latest version of your branch. You can create other git branches off of them, you can push them to GitHub, etc. You will have one for each virtual branch (applied or unapplied) that you've created (that you haven't deleted). If you've committed everything on a virtual branch, the reference will just point to the latest commit. If you have work in progress on the branch, it will point to a WIP commit that includes those changes. So for example, if I have the following two parallel branches, one fully committed and one with work pending: I can view the git branches like this: ```bash title="Terminal" ❯ git show gitbutler/Convert-tables-to-utf8mb4 commit 841e4db701ca41206c03f1f4fe345f7e27d05eab Author: Scott Chacon Date: Fri Feb 23 10:30:17 2024 +0100 my latest commit ❯ git show gitbutler/Add-database-schema-conversion-script commit d95e7f4da1611ea6bb8a80da06e66ca923fbff55 Author: GitButler Date: Fri Feb 23 10:30:18 2024 +0100 GitButler WIP Commit This is a WIP commit for the virtual branch 'Add database schema conversion script' This commit is used to store the state of the virtual branch while you are working on it. It is not meant to be used for anything else. ``` See how the `Add-database-schema-conversion-script` reference points to a "WIP commit"? The tree of that commit has all those changed files in it as though we had committed them. If you don't want to search through all your refs with `for-each-refs`, you can also just run a normal `git log` command and we'll show you what references we've written and which modified files are in each one: ```bash title="Terminal" ❯ git log commit 2d8afe0ea811b5f24b9a6f84f6d024bb323a2db5 (HEAD -> gitbutler/workspace) Author: GitButler Date: Fri Feb 23 10:30:18 2024 +0100 GitButler Integration Commit This is an integration commit for the parallel branches that GitButler is tracking. Due to GitButler managing multiple parallel branches, you cannot switch back and forth between git branches and parallel branches easily. If you switch to another branch, GitButler will need to be reinitialized. If you commit on this branch, GitButler will throw it away. Here are the branches that are currently applied: - Add database schema conversion script (refs/gitbutler/Add-database-schema-conversion-script) - butler/Gemfile - butler/README.md - butler/db/schema.rb - butler/db/migrate/20240209144600_change_mysql_charset.rb - .pscale.yml - Convert tables to utf8mb4 (refs/gitbutler/Convert-tables-to-utf8mb4) branch head: 841e4db701ca41206c03f1f4fe345f7e27d05eab - butler/create_column_conversions.rb Your previous branch was: refs/heads/sc-branch-comments The sha for that commit was: 5e16e99667db9d26f78110df807853a896120ff3 For more information about what we're doing here, check out our docs: https://docs.gitbutler.com/features/branch-management/integration-branch ``` You can see the two `gitbutler` refs under the "Here are the branches that are currently applied" section. Again, these are real git refs, just not under `refs/heads` so that we don't pollute your `git branch` output. But if GitButler crashes at some point, you can still push them to GitHub or whatever you want. Here is an example pushing my virtual branch to a GitHub branch called `convert-tables`: ```bash title="Terminal" ❯ git push origin refs/gitbutler/Convert-tables-to-utf8mb4:refs/heads/convert-tables Enumerating objects: 6, done. Counting objects: 100% (6/6), done. Delta compression using up to 10 threads Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 474 bytes | 474.00 KiB/s, done. Total 4 (delta 2), reused 1 (delta 0), pack-reused 0 remote: Resolving deltas: 100% (2/2), completed with 2 local objects. remote: remote: Create a pull request for 'convert-tables' on GitHub by visiting: remote: https://github.com/gitbutlerapp/web/pull/new/convert-tables remote: To github.com:gitbutlerapp/web.git * [new branch] refs/gitbutler/Convert-tables-to-utf8mb4 -> convert-tables ``` ## GitButler Operations Log Ok, let's say that your work was not in one of those refs for some reason. Maybe you hit some weird bug and it completely changed everything in a way where now you're sitting on the couch in the dark with a glass of whisky, slowly mumbling the word "GitButler..." and plotting your revenge. Most of the time, we'll have whatever you're looking for in our operations log. The easiest way to access this is to use the built in Project History UI: [Project History](/features/timeline) However, let's dig into how this works, just in case you want to check it out yourself. Every time that GitButler does some possibly data-changing operation, we store a snapshot of your project state in our operations log before the operation happens so you can undo it if you want to. This is stored as a Git commit history that is parallel to your projects (ie, no common parents). You can inspect this by looking at the `.git/gitbutler/operations-log.toml` file. ```bash title="Terminal" ❯ tree .git/gitbutler .git/gitbutler ├── operations-log.toml └── virtual_branches.toml 1 directory, 2 files ❯ cat .git/gitbutler/operations-log.toml head_sha = "16e47cb1d091ca9dd44327fef2f5305b09403a95" [modified_at] secs_since_epoch = 1717663406 nanos_since_epoch = 560458000 ``` If we look at this commit, we can see the history of all of the project history snapshots that GitButler is keeping: ```bash title="Terminal" ❯ git log 16e47cb1d091ca9dd44327fef2f5305b09403a9 -2 commit 16e47cb1d091ca9dd44327fef2f5305b09403a95 Author: GitButler Date: Thu Jun 6 10:43:26 2024 +0200 CreateBranch Version: 1 Operation: CreateBranch name: Virtual branch commit 2c95aa06d76b3230f1a51d9f89a211770d93ae51 Author: GitButler Date: Thu Jun 6 10:28:30 2024 +0200 UpdateWorkspaceBase Version: 1 Operation: UpdateWorkspaceBase ``` You can see that before creating a branch or updating our workspace with upstream work, we're recording the state of our project so we have an undo point. So what data are we keeping here in addition to this trailer information? Let's look at the tree of one of these commits: ```bash title="Terminal" ❯ git cat-file -p 16e47cb1d091ca9dd44327fef2f5305b09403a95^{tree} 040000 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 conflicts 040000 tree b283f22f5abf4ed9c612c1b5b3b9a98ec25474b0 index 040000 tree b283f22f5abf4ed9c612c1b5b3b9a98ec25474b0 target_tree 100644 blob d839dca7e14f5833ad737b4adbf337bd20489927 virtual_branches.toml 040000 tree a0821552c0e7d5defe369d577af5e3a87b442469 virtual_branches ``` The parallel branches toml file has the interesting metadata: ```bash title="Terminal" ❯ git cat-file -p 16e47cb1d091ca9dd44^{tree}:virtual_branches.toml [default_target] branchName = "master" remoteName = "origin" remoteUrl = "git@github.com:gitbutlerapp/gitbutler.git" sha = "e00e54af7f903ef84600079a45490a7f07e4702e" pushRemoteName = "origin" [branch_targets] [branches.09ef54c4-1081-4a52-8182-a5ec725016b6] id = "09ef54c4-1081-4a52-8182-a5ec725016b6" name = "commit signing settings" notes = "" applied = false upstream = "refs/remotes/origin/commit-signing-settings" upstream_head = "b60a66452dfecef74103346af6a3291ad677d246" created_timestamp_ms = "1717489406268" updated_timestamp_ms = "1717489406268" tree = "b28e7eefdd7b6f36456516b696146a2ea7638ca4" head = "b60a66452dfecef74103346af6a3291ad677d246" ownership = "" order = 4 selected_for_changes = 1717489406268 ``` The virtual_branches tree has the actual contents of those computed branches in case we need to recreate them: This allows you to get contents of any file in any of your virtual branch states as well.