Search code examples
gitgit-stash

use git stash version in case of merge conflict


I just did

git stash
git pull
git stash apply

and got the message

Auto-merging my_notebook.ipynb
CONFLICT (content): Merge conflict in my_notebook.ipynb

It's a binary file, so I wouldn't know how to manually resolving the conflict like I normally do for source code merge conflicts.

How can I just replace the file in my working directory with the file that I stashed? Overwrite the version that was pulled?


Solution

  • This is relatively straightforward. The stash itself consists of two (or sometimes three, but in this case, two) commits, one of which you can refer to with the name stash and one that requires the name stash^2. The one under stash is the one that corresponds to your working directory at the time you ran git stash, so that's the one you want.

    Hence any of these will work on a Unix/Linux system:

    git restore --worktree --source=stash my_notebook.ipynb
    
    git show stash:my_notebook.ipynb > my_notebook.ipynb
    
    git checkout stash -- my_notebook.ipynb
    

    The first is generally the one to use because it also works on other less friendly systems, such as Windows. Note that it writes only to your working tree copy. The second also writes only to the working tree copy (using I/O redirection), but the third writes to both Git's index (aka "the staging area") and your working tree copy. You might want this so that you need not run git add on the file afterward; in that case, you can get this effect by modifying your git restore command to use git restore -SW --source stash for instance.1

    (Because git stash apply ran an internal merge with an "ours" and "theirs" commit, it's possible to use --ours or --theirs to extract the HEAD and working-tree versions, but, given how they get swapped around by various operations,2 even I can't keep track of which one is which myself and hence I don't recommend git checkout --ours or git restore --theirs or whatever here. Just name stash directly so that you know which commit you mean.)


    1The -S or --staged option means write to staging and the -W or --worktree option means write to the working tree. The --source option can take =stash or stash as a separate word. The idea here was to show all the different ways to invoke this.

    2E.g., during git merge, --ours makes sense, but during git rebase, --ours often doesn't and --theirs means "my original commit", which seems like it should be called "ours". There are always technical reasons for whatever the outcome ends up being, and one can always work it out from first principles, but for something one does rarely, it seems silly to bother. Naming the commit directly is easier.