如何在不提交的情况下解决Git储藏冲突?

698

正如这个问题所问,我也想知道如何解决冲突的git stash pop,而不将所有修改都添加到提交中(就像没有冲突的“git stash pop”一样)。

我的当前方法非常不好,因为我是这样做的:

git stash pop  # -> CONFLICT
git stash drop
# [resolve conflict]
# [add conflict files]
git reset HEAD # <all files that are in commit-mode>
如何复现:
mkdir foo; cd foo; git init
echo "1" > one
echo "2" > two
git add -A; git commit -m "first"
echo "1.1" > one
echo "2.1" > two
git stash
echo "2.2" > two
git commit -a -m "second"
echo "Only this file would stay in HEAD without the conflict" > third
git add third
git stash pop
git status

2016-06-27: 添加了一个名为“third”的新文件到示例中,以表明来自scy的解决方案等变通方法仅适用于空的HEAD,但无法修复首次提交时HEAD与不带冲突的git stash pop相比,内容不同的初始问题。


那么,如果你使用 git add 命令将冲突解决后的文件加入暂存区,然后希望不再在暂存区中包含它们? - Romain
没错,我只想要git stash pop在没有冲突发生时的行为(但需要通知哪些文件需要合并)。 - Sven
2
看起来这个问题的答案在这里:https://dev59.com/-FHTa4cB1Zd3GeqPPSwL。在所选答案中,第四条评论中,Adam解释了为什么git会这样做。 - Patrick
3
@Patrick 谢谢您提供的信息 - 看起来似乎没有解决方案可用,因为这是“按设计”的。 - Sven
@Patrick所提到的评论直接链接是https://dev59.com/-FHTa4cB1Zd3GeqPPSwL#iZ6cEYcBWogLw_1b5WGX。 - BrunoElo
13个回答

844

不要跟随其他答案...

当然,你可以跟随它们。 但我认为,在其他答案中建议的进行提交,然后重置分支以删除刚创建的提交和类似方法并不是解决此问题的干净方式。

干净的解决方案

以下解决方案在我看来更加干净,并且它也被Git本身建议 - 尝试在具有冲突的仓库中执行git status

Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

Note: git restore命令是在Git版本2.23.0中引入的。旧版本的Git建议使用命令git reset HEAD <file>...代替git restore --staged <file>...。您也可以使用git reset将暂存区中的任何文件全部取消暂存(称为索引)。恢复命令的等效方式是git restore --staged .(点是必须的,它指定了任何文件)。目前,这些命令中的任何一个都可以使用,并且结果相同。如果要了解这些命令之间的差异,请查看文档

所以让我们按照Git的建议去做(不制作和撤消任何无意义的提交):

  1. 手动解决冲突(最好使用一些合并工具,请参见下文)。
  2. 使用git restore --staged .将冲突标记为已解决,并取消暂存暂存区中的所有文件。如果只想取消暂存特定文件,请改用命令git restore --staged <file>。您不必在执行之前执行git add
  3. 最后,使用git stash drop删除存储,因为Git在冲突上不会自动执行此操作。

翻译成命令行命令:

$ git stash pop

# ...resolve conflict(s)

$ git restore --staged .

$ git stash drop

默认行为说明

解决冲突的两种方式是:git addgit restore --staged <file>...。虽然 git restore --staged <file>... 可以将冲突标记为已解决并从索引中删除文件,git add 也可以标记冲突为已解决,但保留文件在索引中。

在解决冲突后将文件添加到索引中是故意的。这样你就可以区分先前存储和冲突解决后所做的更改。如果不喜欢这样做,你可以使用 git restore --staged . 将所有东西都从索引中删除。

合并工具

我强烈建议使用任何三向合并工具来解决冲突,例如KDiff3Meld等,而不是手动解决。它通常可以自动解决所有或大多数冲突,是一个巨大的时间节省者!


32
git stash pop出现冲突时似乎需要它。 - Emile Bergeron
25
@kamalpal 是的,Git 甚至会在冲突情况下通知您未删除存储。而问题就是针对这种情况的,因此您**确实需要执行git stash drop**,除非您想保留该存储。 - David Ferenczy Rogožan
1
@DavidFerenczyRogožan Git根本没有通知我它没有删除stash条目。这里是版本2.17.1。 - Robert Siemer
1
我在应用一个存储时遇到了合并冲突。我解决了冲突,添加了文件并提交了它们。但是之后什么也没有发生。通常我会执行类似于 git rebase --continue 的操作,以便Git继续进展。但是我尝试了你的命令 git stash drop,结果只是删除了我的存储而没有应用更改。谢谢你帮我丢失所有更改。 - winklerrr
2
当前版本的git现在输出建议使用git restore --staged而不是git reset,另一个问题的这个答案表示两者具有相同的效果。 - M. Justin
显示剩余11条评论

308

假设你遇到了这样的情况,在从远程仓库拉取代码之前,你需要先保存您的修改。可能是因为您在某个设置文件中只打开了debug: true选项。现在您执行拉取操作,但发现有新的设置选项被引入,导致了代码冲突。

git status命令的输出结果如下:

# On branch master
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#   both modified:      src/js/globals.tpl.js
no changes added to commit (use "git add" and/or "git commit -a")

好的,我决定采用Git建议的方式:解决冲突并提交:

vim src/js/globals.tpl.js
# type type type …
git commit -a -m WIP   # (short for "work in progress")

现在我的工作副本处于我想要的状态,但我创建了一个不想要的提交。我如何去掉这个提交而不修改我的工作副本?等等,有一个流行的命令可以做到这一点!

git reset HEAD^

我的工作副本没有被更改,但WIP提交已经消失了。这正是我想要的!(请注意,我这里没有使用--soft,因为如果你的存储区有自动合并的文件,则这些文件会自动暂存,因此在reset之后这些文件将再次被暂存。)

但还有一件事情:关于git stash pop的手册提醒我们,“应用状态可能会因冲突而失败;在这种情况下,它不会从存储列表中删除。您需要手动解决冲突并随后调用git stash drop。”所以现在我们就这么做:

git stash drop

完成了。


37
在必须使用"commit reset HEAD^"来处理仅应影响工作树的事情时,这种做法本身就有很多不良之处。 - user1115652
9
为什么不直接解决冲突,然后执行git add <已解决的冲突文件>,再跟着执行git reset HEAD呢? - BoltzmannBrain
谢谢您的建议,但这并不能解决最初的问题,即这与没有冲突的 git stash pop 的行为不同。在进行有冲突的 git stash pop 之前,只需将另一个文件添加到 HEAD 中,然后您的 git commit -a -m WIP 也会将新文件添加到提交中。但是,如果没有冲突,只有新文件会留在 HEAD 中,而不是 git stash pop 文件。 - Sven
7
不必先提交再撤消提交。只需按照Dawid Ferenczy的回答进行重置即可达到相同的效果。 - vladkras
4
对于Windows用户而言,^符号被用作特殊的命令行延续符号,使用时可能会出现More?提示符而不是执行命令。建议改用以下命令:git reset --soft HEAD~1。参考文章:如何删除未推送的Git提交记录? - mrfelis
你不觉得,与其使用 git commit -a 命令,直接使用 git reset HEAD 命令会更好吗?这样一切都会变得很简单。 - pqnet

94

不需要将你所做的更改添加到解决冲突的过程中,你可以使用git reset HEAD file来解决冲突而不暂存你的更改。

但是,你可能需要运行这个命令两次。一次标记冲突已经解决,另一次取消之前由冲突解决程序暂存的更改。

虽然现在没有这样的重置模式,但有可能会有一个既能标记冲突已解决又能取消暂存更改的模式。


2
重置模式是我正在寻找的模式 - 其他解决方法就像我描述的那样,对于超过5个文件就不实用了。 - Sven
25
然后使用 "git stash drop" 完成 "git stash pop"。 - David Liu
2
虽然问题没有明确要求,但更新答案以包括“git stash drop”可能是有用的,因为在冲突的情况下,存储不会自动删除。 - Abhishek Pathak
git reset 就足够了。在你解决冲突(更改文件)之后执行一次即可。 - x-yuri

47
git checkout stash -- .

对我来说有效。

注意:这可能很危险,因为它不会尝试将储藏的更改与您的工作副本合并,而是使用储藏的文件覆盖它。因此,您可能会丢失未提交的更改。


当 "git pull --autostash" 引入不必要的合并提交和 git checkout stash -- . 无条件地覆盖了来自存储区的冲突时,可以采用这种方法。 - Alec Istomin
这帮助我摆脱了“git地狱”,即使按照git建议解决了相同的冲突,它仍然会不断出现。我会执行git stash pop,解决冲突,git add/commit,而git stash pop会产生相同的问题。运行上述命令后,我能够运行git stash pop并且它起作用了。 - steveb
这个帮助了我在一个情况下,Github Actions构建替换Kubernetes清单中的Docker镜像标签,并将它们从dev推送到staging分支。创建PR以合并修改后的staging分支到主分支会导致冲突,而这个答案真正地拯救了我。 - dzhi
然后 git stash drop 就不用说了。 - Daniel C. Sobral
尝试了所有其他方法,只有这个有效。我的情况更复杂,是在 Github Actions 工作流中触发的。干得好! - clementiano

34
git add .
git reset

git add .会将所有文件添加到暂存区,告诉Git你已经解决了冲突

git reset会将所有暂存的文件取消暂存,但不会创建提交


这其实不是一个坏答案,它几乎就像 git add -u 然后 git reset - ebob
1
clean and to the point - bareMetal

11

没问题。你需要的只是一个简单的git reset HEAD命令,因为它会保留你的文件修改,就像非冲突的git stash pop一样。

唯一的问题是,你的冲突文件仍然会带有冲突标记,而git将不再用“both_modified”标志报告它们,这是很有用的。

为了解决这个问题,在运行 git reset HEAD 命令之前,先解决冲突(编辑和修复冲突文件),然后就大功告成了...

在此过程结束时,你的储藏仍会保留在队列中,所以只需执行git stash drop命令进行清理即可。

这种情况刚好发生在我身上,我搜索了这个问题,所以这个解决方法经过了测试。

我认为这是最干净的做法了...


6
似乎这可能是你正在寻找的答案,我自己还没有尝试过,但它似乎可以解决问题。使用此命令,GIT 将尝试将更改应用为之前的状态,而不会尝试将所有更改添加到提交中。 git stash apply --index 完整的解释在这里: http://git-scm.com/book/en/Git-Tools-Stashing

谢谢这个提示,但是当我已经执行了 git stash pop 后,这不会有帮助——或者说,如果我发现 git stash pop 会冲突,有没有一种方法可以回滚并执行 git stash apply --index - Sven
我已经添加了一个示例,说明如何生成此内容 - 想象一下您正在编辑超过10个文件,因此您不知道哪些文件在存储之外被修改。 - Sven
3
如果你查看这篇文章的底部[HERE](https://dev59.com/C3E85IYBdhLWcg3wUBsZ),它会说,如果你运行`git stash pop并且出现冲突,那么stash不会被删除...所以你可以运行git reset --hard`来撤销pop,然后尝试我建议的解决方案。 - Marco Ponti
刚试了一下,如果文件处于冲突状态,即使你手动解决了冲突,它也无法正常工作。 - Sam3k
如果你的工作区包含已暂存/未暂存的更改,并且你使用git stash --keep-index && git stash pop --index命令,仍可能导致合并冲突。解决这种情况的方法是类似于git stash --keep-index; do-stuff; git stash && git stash pop stash@{1} --index && git stash pop --index - CervEd

4

我发现最快的方法是解决冲突,然后执行git add -u命令,接着执行git reset HEAD命令,这样就可以不需要提交。


2

git stash branch 可以创建一个新分支,切换到你暂存工作时所在的提交版本,将你的工作应用到这个版本上,如果成功应用则删除暂存。详情请参考此链接


1
请确保如果您正在使用SourceTree进行Git操作,它们会提示您提交更改或解决冲突。请不要这样做。
相反,请打开您的终端并键入:
git add .
git stash

这个源代码树在提示你“提交你的更改”,因为他们不知道这个更改是否会产生冲突,但是他们正在尝试为了安全起见备份。

这个git stash命令将删除你合并的冲突。谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接