Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I influence the merge strategy used by "recursive" when applying a git stash?

I have a set of debug utilities living in a git stash slot; I'm moving between branches which differ only very slightly regarding the changes in the stash. I git stash apply the top stash on the respective branch to test a feature.

However I ran into a proper merge conflict in one of the branches, but I want to prefer what's in the stash, so I want the merge-strategy "recursive" to prefer the version coming from the stash (I guess that'd be "theirs", cf. man git-merge section MERGE-STRATEGIES, subsection recursive).

Can I somehow tell git stash apply what merge strategy to use?

like image 745
bitmask Avatar asked Oct 11 '13 09:10

bitmask


People also ask

How do you solve merge by recursive strategy?

You can use git merge-base --all to see the merge base candidate commits. Using -s resolve will pick one of them, while -s recursive will take all of them, merge them into a new commit, and use that new commit as the merge base.

What is recursive merging?

Recursive is the default merge strategy when pulling or merging one branch. Additionally this can detect and handle merges involving renames, but currently cannot make use of detected copies. This is the default merge strategy when pulling or merging one branch.

What is the best merge strategy in git?

Resolve is generally considered a safe and fast merge strategy. However, it can only resolve two heads—your current branch and the one you're pulling from—with a three-way merge algorithm. Resolve is suitable for criss-cross merge situations as well as “regular” merges where the merge history might be complex.


3 Answers

Yes, you can just do the merge yourself. The stash commit's named stash.

git cherry-pick -n -m1 -Xtheirs stash

Cherrypick does a merge with the cherrypick's parent as the base. Stash commits record the worktree state with two parents, the checked-out commit and the stashed index.

The -m1 tells cherrypick to use the first parent as the merge base, when as here there's any ambiguity about exactly which changes you want merged. The -n says not to commit the result.

This will produce a worktree and index with changes that match the ones in the stashed worktree. If instead you want to apply the changes in the stashed index, cherrypick stash^2 instead; alternately, if you want to apply the changes from the stashed index (i.e. the added aka staged aka indexed content) to the stashed worktree use -m2 rather than -m1.

If you want to merge only to the worktree, do

savetree=`git write-tree`
git cherry-pick -n -m1 -Xtheirs stash
git read-tree $savetree

That's enough to handle the case in the question here (applying a handy set of changes), but it doesn't completely reproduce everything stash does for you. Applying the stashed index changes only to the current index and the stashed worktree changes only to the current worktree is just plain finicky. git stash is a script in /usr/libexec/git-core/git-stash if anybody wants to see it.

like image 162
jthill Avatar answered Sep 30 '22 02:09

jthill


No, not without modifying the stash script (you'll find it in the git-core directory).

Instead of doing that I'd use git stash branch newbranch [stash-id] to turn the stash into a real branch. This will:

  • check out the commit the stash was made on
  • git checkout -b newbranch
  • apply --index and (on success, which should be always) drop the stash.

(You can restore that stash by git stash save-ing immediately afterward, if you want to keep it around as a stash, and then git stash apply --index to put it back into effect on the new branch.)

At this point, you can commit the index and/or commit the new working directory, and/or fuss with it on the new branch, all as needed/desired, and then you have a "real branch" you can merge.

like image 44
torek Avatar answered Sep 30 '22 04:09

torek


I ran into this same problem and ended up using a convoluted process with a temporary branch. IMO, this could be easily fixed if the stash command passed -X parameters through to the merge command. Consider this an ugly workaround.

Net effect: apply the stash to the current master, discarding any differences in favor of those in the stash.

  1. Create a temporary branch for the stash, as suggested by @torek.

    git stash branch tmp [stash-id]
    
  2. Check in your changes. Use any message, as this commit will not be permanent.

    git add --all .
    git commit -m 'tmp commit'
    
  3. Rebase the tmp branch onto your destination, using the 'ours' merge strategy

    git rebase -X ours master
    
  4. Reset the temporary commit, adding changes to your current index

    git reset HEAD^
    
  5. Clean up the temporary branch

    git checkout master
    git branch -d tmp
    

This can be quite convenient when both the stash and master contain some identical changes (e.g. if a collaborator and you both rename a file).

like image 34
Quantum7 Avatar answered Sep 30 '22 03:09

Quantum7