ChatGPT解决这个技术问题 Extra ChatGPT

git stash blunder: git stash pop and ended up with merge conflicts

I did a git stash pop and ended up with merge conflicts. I removed the files from the file system and did a git checkout as shown below, but it thinks the files are still unmerged. I then tried replacing the files and doing a git checkout again and same result. I event tried forcing it with -f flag. Any help would be appreciated!

chirag-patels-macbook-pro:haloror patelc75$ git status
app/views/layouts/_choose_patient.html.erb: needs merge
app/views/layouts/_links.html.erb: needs merge
# On branch prod-temp
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   db/schema.rb
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       unmerged:   app/views/layouts/_choose_patient.html.erb
#       unmerged:   app/views/layouts/_links.html.erb

chirag-patels-macbook-pro:haloror patelc75$ git checkout app/views/layouts/_choose_patient.html.erb
error: path 'app/views/layouts/_choose_patient.html.erb' is unmerged
chirag-patels-macbook-pro:haloror patelc75$ git checkout -f app/views/layouts/_choose_patient.html.erb
warning: path 'app/views/layouts/_choose_patient.html.erb' is unmerged
Note: restoring the state before the git stash apply/pop should be easier with Git 2.5 (Q2 2015), since the working tree now needs to be clean: see my answer below

C
Community

See man git merge (HOW TO RESOLVE CONFLICTS):

After seeing a conflict, you can do two things: Decide not to merge. The only clean-ups you need are to reset the index file to the HEAD commit to reverse 2. and to clean up working tree changes made by 2. and 3.; git-reset --hard can be used for this. Resolve the conflicts. Git will mark the conflicts in the working tree. Edit the files into shape and git add them to the index. Use git commit to seal the deal.

And under TRUE MERGE (to see what 2. and 3. refers to):

When it is not obvious how to reconcile the changes, the following happens: The HEAD pointer stays the same. The MERGE_HEAD ref is set to point to the other branch head. Paths that merged cleanly are updated both in the index file and in your working tree. ...

So: use git reset --hard if you want to remove the stash changes from your working tree, or git reset if you want to just clean up the index and leave the conflicts in your working tree to merge by hand.

Under man git stash (OPTIONS, pop) you can read in addition:

Applying the state can fail with conflicts; in this case, it is not removed from the stash list. You need to resolve the conflicts by hand and call git stash drop manually afterwards.


In fact, even after you drop a stash, it's still possible (albeit more difficult) to retrieve it again, because the changeset still exists in the repository. stackoverflow.com/search?q=git+recover+dropped+stash
@nalply: is that good or bad? You are welcome to improve my answer, where you didn't understand it in the first place ...
I think that source code revisioning is a complex problem domain. It is easy to get confused. I still think that your answer is good because it reconfirmed my approach.
Not only did it help me a lot to realise that the stash hasn't been removed as I assumed it had, but this explains why my stash kept growing even when I was certain I hadn't been forgetting to retrieve things from it.
"Applying the state can fail with conflicts; in this case, it is not removed from the stash list." This is the most important part of the post, in my opinion. Consider editing your answer to put it up front along with a the words DON'T PANIC in large, friendly letters. (+1 already though.) Thanks.
A
Aaron

I had a similar thing happen to me. I didn't want to stage the files just yet so I added them with git add and then just did git reset. This basically just added and then unstaged my changes but cleared the unmerged paths.


This seems to be better than using reset --hard because it doesn't overwrite your files (except the ones with merge problems). Thanks!
I didn't want to stage the files just yet so I added them -- doesn't add stage content from the working tree to the index? I don't think I understand why your answer works from the description.
git add does stage them but git reset, which I do immediately after, unstages them. Essentially it clears the unmerged paths and returns me to my normal working tree by faking git out.
You don't need to git add if you're going to git reset. The git reset effectively "undoes" the git add. git reset (--mixed <- default) effectively does not touch the working directory so exactly what was in your working directory, merge conflicts and all, are left alone. The index (and technically the branch head) is reset though (without a ref they get reset back to HEAD, which probably means no change for the branch head, and effectively undoes any git add done to the index, as well as clearing the unmerged paths state).
The sequence, edit/resolve, git reset and git stash drop works well. It does what git stash pop would have done with no conflicts. It does seem the git add is not needed; although it maybe useful it you have many files with conflicts. As each is resolved, they can be added and git status keeps track of them.
P
Pedro Gimeno

If, like me, what you usually want is to overwrite the contents of the working directory with that of the stashed files, and you still get a conflict, then what you want is to resolve the conflict using git checkout --theirs -- . from the root.

After that, you can git reset to bring all the changes from the index to the working directory, since apparently in case of conflict the changes to non-conflicted files stay in the index.

You may also want to run git stash drop [<stash name>] afterwards, to get rid of the stash, because git stash pop doesn't delete it in case of conflicts.


V
VonC

Note that Git 2.5 (Q2 2015) a future Git might try to make that scenario impossible.

See commit ed178ef by Jeff King (peff), 22 Apr 2015.
(Merged by Junio C Hamano -- gitster -- in commit 05c3967, 19 May 2015)

Note: This has been reverted. See below.

stash: require a clean index to apply/pop

Problem

If you have staged contents in your index and run "stash apply/pop", we may hit a conflict and put new entries into the index. Recovering to your original state is difficult at that point, because tools like "git reset --keep" will blow away anything staged.

In other words:

"git stash pop/apply" forgot to make sure that not just the working tree is clean but also the index is clean. The latter is important as a stash application can conflict and the index will be used for conflict resolution.

Solution

We can make this safer by refusing to apply when there are staged changes. That means if there were merges before because of applying a stash on modified files (added but not committed), now they would not be any merges because the stash apply/pop would stop immediately with:

Cannot apply stash: Your index contains uncommitted changes.

Forcing you to commit the changes means that, in case of merges, you can easily restore the initial state( before git stash apply/pop) with a git reset --hard.

See commit 1937610 (15 Jun 2015), and commit ed178ef (22 Apr 2015) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit bfb539b, 24 Jun 2015)

That commit was an attempt to improve the safety of applying a stash, because the application process may create conflicted index entries, after which it is hard to restore the original index state. Unfortunately, this hurts some common workflows around "git stash -k", like:

git add -p       ;# (1) stage set of proposed changes
git stash -k     ;# (2) get rid of everything else
make test        ;# (3) make sure proposal is reasonable
git stash apply  ;# (4) restore original working tree

If you "git commit" between steps (3) and (4), then this just works. However, if these steps are part of a pre-commit hook, you don't have that opportunity (you have to restore the original state regardless of whether the tests passed or failed).


z
z atef
git restore --staged  name-of-file-that-has--merge-conflict

This seems like a useful alternative solution, but can add an introduction explaining what effect this has exactly? There are several options one might want, and this one won't always be the right one.