Overview
Work on several branches at the same time, committing and stashing them independently and simultaneously.
One of the main strengths of GitButler is it's 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 is not possible with vanilla Git.

When using GitButler, we will setup a special branch to handle the multiple branches in away that
Git can understand a bit better. This means that 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 that stock Git can only handle one branch at a time, it does not have tooling to use or understand multiple, so most commands having to do with the index or HEAD or branching (commit, branch, checkout, etc) may behave unexpectedly.
To understand why and how to get out of any difficulties you run into, please read our integration branch docs. We are working on making this interaction better.
Target Branch
With virtual branches, you are not working off of local main or master branches. Everything that you do is on a virtual branch, automatically.
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, everything in your working directory that differs from it is branched code and must belong to a virtual branch.
This means that you don't have to create a new branch when you want to start working, you simply start working and then change the branch name later. There is no local "main" or "master" because it doesn't make sense. Everything is a branch, with work that is meant to eventually be integrated into your target branch.
Virtual Branches
You can easily work in a single branch at a time, but GitButler can handle several virtual 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 looks like that. 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 virtual 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 that, you will see the incoming work and how to update your active branches on top of it.
For each virtual 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 just keep a virtual branch applied locally until it is integrated and it will go away automatically.
Conflicting Branches
If you do rebase work that has 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 we'll 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 being able to easily create pull requests and integrate features.
Last updated on