-
Notifications
You must be signed in to change notification settings - Fork 664
V3 stashing #9725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
V3 stashing #9725
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The latest updates on your projects. Learn more about Vercel for GitHub.
|
@Byron is attempting to deploy a commit to the GitButler Team on Vercel. A member of the Team first needs to authorize it. |
1 task
Just some fixes while perusing the codebase.
That way it's possible to reconstruct the exact status, and know if it's coming from a change in the index, or a change in the worktree. This is relevant later when storing index changes.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Figure out which V3 primitive is feasible next. Answer: stashing
It seems like any mutation that affects the worktree needs to deal with uncommitted changes first, so stashing should be a primitive.
Let's validate and research, and also see how the V3 version can benefit the current code. Answer: it should be possible to backport stashing, assuming that the merges and 'move tree between workspaces' functionality effectively is just a way to apply a stash, and nothing that uses side-effects to do what it wants and apply a stash at the same time.
Something that's always an obstacle right now is conflicted commits - could this be gotten rid of before?. There is no need to that just yet, but it's needed to do proper rebasing. The rebasing definitely needs to know 'hidden' conflicts in commits to work correctly, but with a new system at least there is no chance to get meta-trees into the workspace.
Tasks
gix
@mainSkipped Follow-Up PRs
RefInfo
workspaceapply
at least, maybe moreupdate_workspace
as well, maybe some of the merge-conflicts can then be prevented?Follow-Up PR
but-workspace/snapshot/apply
Shortcomings
git2
reset/checkout, there is no support for configured worktree filters likegit-lfs
, and it's unlikely that submodules work like we'd want or as is intuitive.Future Directions
How else can the snapshotting be used for value extraction?
undo/redo
based on new oplog (where store-snapshots are completely optional)Research
My thoughts
What to do next?
Thus, proper stash handling enables apply, unapply, reordering and commit manipulation. Committing itself is the only operation that only needs rebasing, which is also required by update worktree.
So key-capabilities are:
apply
andunapply
apply
andunapply
(without rebasing support).apply
would need another iteration to help with conflicts - can rebasing help then? Or can it be applied with conflicts, assuming our workspace-commit-with-conflicts handling is good enough? Could workspace conflict resolution not just be 'show conflicts' and users resolve and commit them to stacks of their choice? Or should stacks be re-based one commit at a time to record conflicts, to be resolved one at a time?General Ideas: Stack
How to create a stash?
worktree
andindex
, with the actual trees below. For simplicity, omit theindex
if it's the same as theworktree
.Stashing the index
Git creates a separate commit for the index and writes the index as tree. Deepwiki Details
How to restore a stash?
Right now
git2
is used to reset or checkout, often withforce
, but applying stashes should be safe, ideally, while also dealing with conflicts responsibly. It's probably OK to keep usinggit2
for that, as long as the index or tree to checkout is correct. Untracked files should never be accidentally considered added, so Git won't remove them.What about ignored files? They aren't ever seen but may be removed as they are expendable.
To restore a stash, in any case there needs to be a cherry-pick/three-way merge between the old HEAD, the new HEAD and the tree representing the worktree. This seems to be naturally catered to by creating a commit on top of old HEAD, while associating the stash with a namespaced reference of the same name as the one that pointed to old HEAD at the time of stash creation. This means only one stash per ref, unless the next stash commits can sit on top of the previous stash and on top of the old HEAD commit, using a 'fake merge'.
Conflicts when applying a stash need special consideration, and should probably be rejected by default to let the user select a policy, i.e. apply with markers, auto-resolve with ours or theirs (stash or workspace), or cancel.
How it is handled now
create_wd_tree()
to get a Git tree for the worktreecreate_wd_tree()
to keep the statecreate_wd_tree()
function is used to incorporate the worktree changes into the computation to unapply a stack.update_uncommited_changes
Copilot (Sonnet)
I'll help you find where the backend handles uncommitted files/worktree changes before altering the worktree. Let me search through the codebase to identify where stashing or similar operations are implemented. The search results look very promising! I found significant information about how GitButler handles uncommitted changes before altering the worktree. Let me search for more specific details about stashing and look at the
gitbutler-edit-mode
crate that showed up in the results. Now let me look at the mod.rs file which had interesting comments about stashing: Let me search for more specific stashing-related functions: Let me look at thecreate_wd_tree
function which seems to be the core function for creating a tree from worktree changes: Let me look at the repository extension that implements the corecreate_wd_tree
function: Let me look at the workspace handling functions and see how they use this stashing mechanism: Now let me look at the actual implementation ofupdate_uncommited_changes_with_tree
to understand how uncommitted changes are handled: Now let me search for specific operations that would trigger this stashing behavior, like branch switching: Let me look for theunapply_stack
function to see how it handles uncommitted changes: Now let me look at the actualunapply_stack
function implementation to see how it handles uncommitted changes: Now let me examine where the actual worktree operations happen when switching branches or workspace states. Let me search for the workspace base change handling: Let me look at theupdate_workspace_commit
function which seems to be central to how workspace updates are handled: Perfect! Now I have a comprehensive understanding of how GitButler handles uncommitted files/worktree changes before altering the worktree. Let me provide you with a detailed summary.How GitButler Handles Uncommitted Files/Worktree Changes
Based on my analysis of the GitButler codebase, here's how the backend handles uncommitted files and worktree changes before altering the worktree:
1. Core Stashing Mechanism via
create_wd_tree()
The main mechanism GitButler uses is the
create_wd_tree()
function found in:This function creates a Git tree object containing all uncommitted changes in the project, including:
2. Stashing Strategy in Edit Mode
When entering edit mode, GitButler uses a sophisticated stashing approach in lib.rs:
The uncommitted changes are stored as a Git reference at
refs/gitbutler/edit-uncommitted-changes
.3. Branch/Stack Operations
When unapplying stacks (the equivalent of switching away from a branch), GitButler:
ours
: Current working directory tree (cwdt)theirs
: Workspace base treebase
: Head of the branch being unappliedFrom branch_removal.rs:
4. Workspace State Management
GitButler uses a sophisticated workspace state management system in branch_trees.rs:
update_uncommited_changes()
: Updates uncommitted changes when the workspace state changesmove_tree_between_workspaces()
: Moves changes from one workspace state to another using Git's merge algorithmWorkspaceState
: Captures snapshots of the workspace at different points in time5. Where This Happens
The stashing/uncommitted change handling is triggered in several key operations:
unapply_stack()
operationsenter_edit_mode()
andsave_and_return_to_workspace()
update_workspace_commit()
after any stack modifications6. Key Advantages of This Approach
git stash
but rather its own tree-based approach7. Storage Locations
refs/gitbutler/edit-uncommitted-changes
.git/workspace
file for previous branch informationThis approach allows GitButler to safely preserve and restore uncommitted changes while switching between different workspace configurations, without relying on traditional Git stashing mechanisms.
Other
Carryover
Information to transfer between PRs just to not forget about these. Maybe offload into an issue one day once it's clear it won't be forgotten.
Performance Tasks
Not to forget