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 virtual branches.
If everything crashes or the UI isn't working at all, you may be surprised to know that even though your virtual branches don't show up in a normal git branch output, we do actually constantly write them out as Git references (just not in refs/heads).
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 virtual branches, one fully committed and one with work pending:
I can view the git branches like this:
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:
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:
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
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.
If we look at this commit, we can see the history of all of the project history snapshots that GitButler is keeping:
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:
So, it's not a snapshot of your working directory alone, but it includes a bunch of other meta information.
The state of your working directory is indeed stored in the workdir tree:
So you can easily recover file contents from any of these snapshots if needed:
Beyond that, we also store the state of your index in index as a tree, the state of what your target (trunk) tree looked like in target_tree, conflicts if you were in a conflicted state in conflicts and the state of your virtual branches.
The virtual branches toml file has the metadata:
The virtual_branches tree has the actual contents of those computed branches in case we need to recreate them:
commit-message.txt
tree (subtree)
commit-message.txt
tree (subtree)
This allows you to get contents of any file in any of your virtual branch states as well.