Like most developers, I primarily use Git as my version control system, and I have been doing so for about six years. Over that time, I’ve almost exclusively interacted with Git through its command line interface, and never through any of the various Git clients available.
While I don’t think Git command line (or Git in general for that matter) is perfect, I’ve just never had a compelling reason to switch off of CLI as my primary means of using the tool. Of the various Git clients I’ve been introduced to, none of them have actually solved any problems for me, or made my workflow better.
That being said, I’ve recently learned about a newish Git client named GitButler. This post is about some of the reasons why it is actually a compelling client for me, and why it might be the first Git client I ever actually use after all these years.
Note: If you don’t care about any of my uses for Git or opinions I have on it, feel free to just skip to the last section where I actually talk about GitButler and why I think its interesting.
When I first started using Git, it was in the simplest way
possible, and I probably exclusively used commands like
git init, git commit, and
git push. Commit messages I wrote were probably
something like “fix stuff” and “update” with no additional
information.
I used Git primarily to keep versioned states of my project and to store my work on a Git forge like GitHub. At this stage, Git seemed easy and intuitive, the command line interface made sense and was blazingly efficient, and I was already working primarily out of terminal based programs like Vim. There was no reason to even consider anything other than pure Git.
Over time I gradually learned more elements of Git and started incorporating them into my workflows as necessary. Today I probably use something like 20+ top level Git commands regularly, and who knows how many of the various subcommands and options. What hasn’t changed is that I still use the command line for all of these options, and while I still find it fast and efficient, I don’t necessarily find it intuitive anymore.
Even with all my regular use, I still find myself looking up how to do more obscure operations, but I just haven’t found anything that actually improved upon the default Git experience. To me it seemed like Git was just bound to be a little clunky with all that it can do, and nothing out there made sense to replace the CLI as my primary Git client.
I’ve been introduced to plenty of Git clients (programs that wrap Git but implement some sort of new, often graphical, interface to use Git through), but none of them have really intrigued me. GUI programs like Git Extensions and Tortoise Git only serve to get you out of the terminal, and they don’t actually add any new functionality that I find compelling. I’m perfectly comfortable in the terminal, and I find it faster to type commands than have to click through menus to perform them. TUI based programs like Fugitive or Lazy Git are a little more streamlined, but even they still only seem to slow me down. All other Git clients I’ve seen pretty much fall into these same pitfalls.
Essentially, the Git clients I’ve seen just don’t add anything new that I care about. Git works well enough for me as is, and if I’m going to completely change up something I’ve already grown accustomed to, I need to be won over by something that improves my efficiency or by something that implements a new feature that isn’t done well by Git alone.
I will fully admit this isn’t the case for everyone. Some people find the terminal unappealing and simply prefer graphical clients for that reason. In that case a bare bones program that only does Git but gives you menus to do things is a good improvement for them. However for the reasons stated, it’s just not for me.
So what are some actual problems that could be solved by a Git client that would win me over? Well here’s a list of things that I think Git doesn’t do well and annoy me to some extent.
I don’t want to always be thinking in the mindset of “when should I commit.” Generally when working on a feature, I want to separate commits out to individual functional components.
For example with a conventional commits style, commits are separated into categories like features, fixes, documentation, chores, etc. Furthermore, each commit can be granularized to its smallest functional component within each category.
The point is, I often will work on things and get to a state where it makes sense to reconsider how I’m organizing my commits, or I may go down a rabbit hole of refactoring and then decide, actually I liked it the way before better. In these cases, it’s often hard to know ahead of time, “Oh I should have made more commits along the way to save the various project states.” Often I end up with work in progress commit(s) that I then amend/rebase later into whatever I want, but this is generally suboptimal for me.
I think one thing that would be nice to alleviate some of this anguish would be automatic state tracking. Think Google Docs style where I can revert back to points in time without having to be intentional about saving. It surely won’t be perfect, but ideally it can get me 80-90% to where I want, and then I can do smaller manual fixups afterwards.
Often when working on bigger projects it may make sense for me to
want multiple branches checked out at the same time. This is
something you can technically do already with
git worktree, but this is one case where I actually
find the CLI to be a little too cumbersome. Worktrees aren’t super
intuitive to me and seem to involve lots of manual work to be happy
together.
My ideal case is where I can have multiple branches checked out at the same time and seamlessly move and stage changes between them. This falls into a similar use case as above where I may be working on a feature and along the route fix a bug or two. Not always, but often it is desirable to create individual commits and branches for those differently grouped changes and then to create different pull requests for them.
These things can generally be done with a single branch workflow through rebasing and branching as you go or by branching and cherry-picking. I’m sure there are other ways too, but the methods I’ve tried seem to require going back to cleanup things rather than it being part of the natural development flow. It would be nice to have a worktrees like experience that’s a little less cumbersome and is more intuitive.
Submodules are really nice in some ways, but really ugly in
others. One particular grievance I have with submodules is that they
really slow down a repository. Commands like git status
can really chug if you haven’t configured Git to mostly ignore
changes in submodules.
Also, having to regularly interact or think about submodules is
not always desirable. Even after having configured Git to be a
little more sane, I often still have to
git submodule update regularly or manually checkout the
updated sub-repo. Even worse, when a reference gets messed up for
one reason or another, I sometimes have to manually run a prune from
within the submodule so that it’ll be recursively fetched properly
without interrupting the other submodule updates.
I’m not sure what the solution here is. Maybe it’s just to improve my submodule configs further or not use submodules altogether. However, it just seems like a draggy experience and I’m not sure if the alternative is all sunshine and roses either. Yes some futzing with submodules is expected since they are part of the project after all, but ideally it’s not such a frequent experience and they just kinda work.
Enter GitButler. GitButler is a graphical Git client that to me seems refreshing and intriguing because it does seem to actually take a different approach to making a Git client. This program doesn’t just feel like a clunkier, slower wrapper on top of Git, it feels like something that was designed with actually improving Git in mind. It’s certainly not perfect, and learning the program definitely gave me a certain level of anxiety that only comes with not trusting to fully work, but overall it seems like something that could actually have a place in my tooling. Here’s what I like and don’t like.
GitButler improves 2 of the grievances I have above, multiple branches and time-based project history. Working in multiple branches seems to be GitButler’s main appeal. They’ve implemented some smarts on top of worktrees through what they call “virtual branches.”
Honestly I’m not all that sure (since I haven’t bothered to dig that far into their documentation or GitHub repository yet) what is actually that special about virtual branches. However, what I do know is that they seem to have made an improvement on worktrees by making the experience more intuitive. With GitButler, I can apply (checkout) multiple branches to my workspace (worktree) at the same time, and then I can seamlessly drag commits, or even just changes prior to committing, between branches. I don’t have to think about it ahead of time, and I can easily create a new branch for a new feature or fix by simply dragging the associated changes to a new area in the workspace.
Another feature which I really like the idea of, but haven’t spent a ton of time testing yet, is the time based project history. GitButler provides a timeline of actions and changes made to the repository that don’t necessarily have to correlate with Git actions. This means I can make changes, decide I don’t like them, and revert back to a prior state without ever having made a commit. Similarly, I can undo a series of Git commands without having to think about the proper inverse actions.
To top this off, GitButler comes with a modern UI that doesn’t bury things in menus. Things I want to do like rebasing and reordering commits are done through simple drags and drops. The most used actions like committing, pulling, and pushing are all single button presses. Generally I don’t feel significantly slower using the GitButler UI compared to the CLI.
It’s not perfect though. Learning GitButler was scary. Part of this is because Git is so integral to my workflow, it’s just hard to drastically switch it up and not feel some anxiety. That being said, I still think GitButler is to blame for a lot of this anxiety too. At times I felt that I didn’t understand what the underlying operations of GitButler were going to be. When I click “update” on my workspace, what is that going to actually do? Is it going to merge upstream changes to my local branch(es) and create a merge commit? Is it going to rebase my changes and require me to now force push to the remote? Will I be given a choice between these two?
Other times I feel like I’ve gotten myself stuck into certain states. If I drag a change around and rewrite my history by accident, I want to be able to just undo that. Even after manually trying to reverse my changes I’ve still been stuck with the only option being to force push and rewrite by branch history. In these cases I’ve had to revert back to the command line to save my changes and reapply them to a fresh copy of my remote branch.
Granted, the program is still in beta, and I expect that many of these issues may improve over time. As of now, GitButler doesn’t even handle submodules; it just completely ignores them, so I still have to think about those and manually update/commit/checkout them as necessary. It’s also just my opinion that obfuscating the underlying Git actions is scary. For some people this may just be a better experience; after all, the ideal Git client might be one where you don’t even really think in terms of “Git.”
Overall, GitButler is a new and refreshing take on Git clients. I do think it’s something that will actually have a home in my tooling and workflows from now on. It’s not going to totally replace the command line for me; I think in many cases the CLI is still all that I need. However with bigger projects, having an easier way to manage multiple branches is super attractive, and I will probably end up leaning on it for that more and more.