如何在Git中合并多个stash

87

这是在分支frontend上的一个流水线,涵盖了过去两周的时间。

| Stash@{3}包含自Stash@{1}以来所有的代码(不包括两个微小的提交)
| 微小的提交
| 微小的提交
| 两周前的大量提交已经被重置并移动到Stash@{1}

我的工作目录目前是干净的。
Stash@{1}包含了两周前的一次普通开发代码的大量提交内容(应该一开始就应该被存储)。但是这个提交被撤销并移动到了存储区 (stash)中。
Stash@{3}是自Stash@{1}以来该树中最新的工作内容(剔除了已提交的一些更改)。

我需要将这两个存储区的内容合并到我的工作目录中,这样我才能从这个巨大的工作池中进行多次提交。

我运行了git stash apply stash@{1},然后尝试了:

git stash apply stash@{3}
git stash show -p | git stash apply stash@{3}

但是在这两种情况下,我都得到了“工作目录不干净”的提示。如何合并这些工作内容呢?因为stash@{3}更新,所以我希望它能够覆盖stash@{1}中的冲突部分。

5个回答

153

虽然有点复杂,但这几乎总是有效的:

  1. 弹出第一个stash

$ git stash pop
  • 暂时提交第一个存储的更改

  • $ git add . && git commit -am 'WIP'
    
  • 弹出第二个存储库

  • $ git stash pop
    
    撤销临时提交,保留其引入的更改。
    $ git reset --soft HEAD^
    

    10
    花了我一些时间才意识到它的美妙之处。它比尝试应用补丁更有效,因为补丁常常因为空格或文件路径变化而失败。 - Kirby
    2
    不要忘记在执行 git diff 命令之前重置文件,以便查看统一的更改。因为当前已经重置的更改是暂存的,并且可以通过 git diff --staged 命令单独查看。 - Nakilon
    2
    这似乎是最好的解决方案,但我仍然不明白为什么我们不能在有未暂存更改的情况下简单地弹出现有的存储。例如,只需运行 git stash pop 然后跟着运行 git stash pop 就很好了。也许我对 git 存储区内部机制有所遗漏。 - modulitos
    如果弹出第二个存储库时出现合并冲突(即两个连续的弹出不起作用),则在解决冲突后还需要执行 git add <conflicted_files> - Sameer
    2
    @Nakilon 提到了这一点,但是我想要添加以下步骤作为第5步(从项目的根目录中输入):git reset HEAD . - Grant Humphries
    如果您进行了更改,它的效果可能不太好... - Att Righ

    55

    如果工作树中有与修改文件存在冲突,则只能在没有冲突时应用储藏,所以首先确保在 git status 中没有修改的文件,如果有,请提交它们。然后执行以下操作:

    git stash apply stash@{1}
    git commit -a
    # Enter your commit message
    git stash apply stash@{3}
    

    然后,您可以创建一个新提交或修改先前的提交以将它们合并。每次应用后您可能需要解决合并冲突。

    此外,如果您决定使用git stash pop 而不是 apply,请注意 stash@{3} 将变成 stash@{2} ,因为第一个已弹出。


    4
    这两个存储库都很庞大,包含很多重叠的工作内容,所以我想避免提交。能否在工作树中将它们简单合并,或者我必须提交这些巨大的代码块,然后在vim中将2个糟糕的提交合并并分成约10个良好的提交? - sscirrus
    另一件事是:我不想提交所有这些代码。其中很大一部分仍然只用于开发。 - sscirrus
    9
    在工作树中合并和修改有什么区别?或者在应用第二个存储后执行git reset HEAD^呢?您无法在脏的工作树上应用暂存,因此必须在应用之前进行提交,但这并不意味着在应用后不能撤销该提交。 - Andrew Marshall
    @AndrewMarshall “你只能将存储应用于干净的工作树”……这个说法仍然是最新和准确的吗? - Bradley Thomas

    17

    这对我很有帮助。

    1. 分阶段提交当前更改(如果未暂存任何更改,请跳过此步骤)

      git add .
      
    2. 应用您希望的stash

    3. git stash apply stash@{0}
      
    4. 展示当前的更改

    5. git add .
      

      或者 git add <stashed_filename>

    6. 多次执行步骤2和3

    7. 然后取消暂存所有更改

      git reset 
      

    完成!


    1
    所以基本上不要在未暂存的更改上应用stash。谢谢! - Micer
    2
    在应用另一个/第二个存储之前,不需要创建临时的“git提交”。 - Bharat Pahalwani

    15
    更好的方法是只需使用 git stash show -p stash@{某个版本号} > stash-{某个版本号}.diff,然后对每一个使用 git apply

    1
    这是迄今为止最简单的方法,如果更改不重叠的话。我觉得必须提交更改才能合并内容很傻。 - Marcelo Diniz
    1
    @MarceloDiniz:如果内容发生变化,就必须进行提交。这一点都不傻。 - siride
    如果存储的内容相对较简单,则将存储应用为补丁是可行的,但如果存在中间的、非平凡的空格更改或文件路径更改,则无法使用。 - Kirby
    3
    无需中间文件,直接将内容导入git apply命令:git stash show stash@<n> | git apply - - Andrew Marshall
    4
    我在那里有四年半的"stask",直到现在没有人注意到。 - siride
    显示剩余5条评论

    3

    我曾经遇到过类似的问题,并通过以下方式解决:

    使用git stash pop应用其中一个存储。然后使用git diff -p > ../stash.diff创建此存储的补丁。接着,您可以重置您的工作树(或再次存储更改),并使用git stash pop stash@{1}弹出另一个存储。如果您在此时应用您的补丁,则可以“合并”这两个不同的存储。

    您可能需要解决一些冲突。如果一切顺利,您就可以放弃已存储的更改了。


    或者只需将stash@{1}作为差异提取并在本地应用(例如siride的答案和评论),但这也可以,谢谢! - rogerdpack

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