gitbutlerapp/gitbutler · error

Could not get a tree of all applied virtual branches merged

Error message

Could not get a tree of all applied virtual branches merged

What it means

Raised while materializing a snapshot's workdir tree for diffs. In the cached path, if the snapshot commit id is not yet cached and tree_from_applied_vbranches fails, nothing is inserted into the cache and the subsequent cache.get returns None, producing this error. tree_from_applied_vbranches fails when the snapshot tree lacks the entries it expects (workspace/worktree/target_tree paths), when legacy virtual-branch TOML metadata can't be read, or when fail-fast tree merges of the applied branch trees conflict.

Source

Thrown at crates/gitbutler-oplog/src/oplog.rs:388

    if let Some(details) = details
        && details.version == Version(3)
    {
        let worktree_id = get_v3_workdir_tree(snapshot_commit.tree()?)?.context(format!(
            "no entry at 'worktree' on sha {:?}, version: {:?}",
            &snapshot_commit.id(),
            &details.version,
        ))?;
        return Ok(worktree_id);
    }
    match wd_trees_cache {
        Some(cache) => {
            if let Entry::Vacant(entry) = cache.entry(snapshot_commit.id)
                && let Ok(tree_id) = tree_from_applied_vbranches(repo, snapshot_commit.id)
            {
                entry.insert(tree_id);
            }
            cache.get(&snapshot_commit.id).copied().ok_or_else(|| {
                anyhow!("Could not get a tree of all applied virtual branches merged")
            })
        }
        None => tree_from_applied_vbranches(repo, snapshot_commit.id),
    }
}

struct IndexTrees {
    index: gix::ObjectId,
    conflicts: Option<gix::ObjectId>,
}

fn write_index_trees(ctx: &Context) -> Result<IndexTrees> {
    let repo = ctx.repo.get()?;
    let index = repo.index_or_empty()?;
    // The detached editor writes trees without checking that each entry's blob exists
    // locally, which it may not, e.g. for unfetched files in a partial clone with a
    // sparse checkout.
    let mut tree = repo.empty_tree().edit()?.detach();

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Update GitButler to the latest version so all historical snapshot formats parse
  2. Verify object availability: `git fsck` and check the snapshot commit's tree exists (aggressive `git gc --prune=now` can drop oplog trees)
  3. When iterating the timeline, skip the unreadable snapshot and continue with the next entry rather than aborting the whole listing
  4. If reproducible, capture the snapshot sha and report it — a format migration gap is usually fixable in tree_from_applied_vbranches

Example fix

// before
let tree_id = snapshot_workdir_tree(repo, snapshot_commit, cache)?; // aborts timeline walk

// after: skip unreadable snapshots, keep the timeline usable
let tree_id = match snapshot_workdir_tree(repo, snapshot_commit, cache) {
    Ok(tree) => tree,
    Err(err) => {
        tracing::warn!("skipping snapshot {}: {err}", snapshot_commit.id);
        continue;
    }
};
Defensive patterns

Strategy: fallback

Try / catch

// when walking the timeline, degrade per-snapshot instead of aborting
let tree_id = match snapshot_workdir_tree(repo, &snapshot_commit, cache) {
    Ok(tree) => tree,
    Err(err) => {
        tracing::warn!(
            "cannot build merged tree for snapshot {} ({}), skipping entry",
            snapshot_commit.id, err
        );
        continue; // timeline stays usable; entry is shown without diff
    }
};

Prevention

When it happens

Trigger: Rendering a diff for a very old snapshot that predates the 'virtual_branches/workspace/tree' and 'worktree' entries and has unreadable legacy metadata; a snapshot whose applied-branch trees merge with conflicts under merge_options_fail_fast; corrupted or externally modified oplog commits; snapshot trees written by a substantially older app version.

Common situations: Long-lived projects where the oplog timeline spans many format migrations; restoring or diffing an old timeline entry after upgrading; repositories whose .git objects were pruned/GC'd aggressively, dropping trees the oplog still references.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/2e214e5c37975da2. Report an issue: GitHub.