GitButler Logo
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 virtual branches.

GitButler References

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

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 virtual branches, one fully committed and one with work pending:

Virtual Branches Example

I can view the git branches like this:

Terminal
 git show gitbutler/Convert-tables-to-utf8mb4
commit 841e4db701ca41206c03f1f4fe345f7e27d05eab
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Feb 23 10:30:17 2024 +0100
 
    my latest commit
 
 git show gitbutler/Add-database-schema-conversion-script
commit d95e7f4da1611ea6bb8a80da06e66ca923fbff55
Author: GitButler <gitbutler@gitbutler.com>
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:

Terminal
 git log
commit 2d8afe0ea811b5f24b9a6f84f6d024bb323a2db5 (HEAD -> gitbutler/integration)
Author: GitButler <gitbutler@gitbutler.com>
Date:   Fri Feb 23 10:30:18 2024 +0100
 
    GitButler Integration Commit
 
    This is an integration commit for the virtual branches that GitButler is tracking.
 
    Due to GitButler managing multiple virtual branches, you cannot switch back and
    forth between git branches and virtual 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/virtual-branches/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:

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

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.

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:

Terminal
 git log 16e47cb1d091ca9dd44327fef2f5305b09403a9 -2
commit 16e47cb1d091ca9dd44327fef2f5305b09403a95
Author: GitButler <gitbutler@gitbutler.com>
Date:   Thu Jun 6 10:43:26 2024 +0200
 
    CreateBranch
 
    Version: 1
    Operation: CreateBranch
    name: Virtual branch
 
commit 2c95aa06d76b3230f1a51d9f89a211770d93ae51
Author: GitButler <gitbutler@gitbutler.com>
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:

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 virtual branches toml file has the interesting metadata:

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:

commit
tree
[commit-id2]
tree
tree

This allows you to get contents of any file in any of your virtual branch states as well.

Last updated on

On this page

Edit on GitHubGive us feedback