Git储藏两个分支

19
这是情况:
1. 我在A分支上进行了更改。 2. 在A分支上运行了 `git stash` 命令。 3. 切换到B分支并在B分支上进行了更改。 4. 在B分支上运行了 `git stash` 命令。 5. 切换回A分支。 6. 在A分支上运行了 `git stash pop` 命令。 7. 在这个列表的第7个步骤之后,我在存储之前对分支A所做的更改没有恢复,而我在分支B上所做的更改弹出到了分支A上。我在分支A上使用了 `cmd z`,文件回到了先前的状态,其中包括我所做的更改。似乎分支A的HEAD移动到了分支B的HEAD。当我在分支B上使用 `git checkout B` 和 `git stash pop` 时,同样的事情发生了:分支B拥有分支A上所做的所有更改,但不包括我在分支B上所做的更改。我再次使用了 `cmd z` 回滚到我需要的分支状态。
这给我带来了很多问题,直到我被允许再次提交代码到项目中(由于提交会自动推送到服务器,并且经理不希望在运行一些测试之前将新代码放入服务器,因此在一段时间内没有人能够提交)。我该如何只弹出在分支上所做的更改,而不是其他分支上所做的更改呢?

3
我不知道有哪个功能可以将一个stash与一个分支联系起来。然而,一个stash条目有一个描述,并且默认情况下该描述包括创建它的分支的名称。您可以使用git stash list查看这些描述。然后例如使用git stash pop stash@{1}从堆栈中检索第二个项目。 - Raffael
1
此外,提交后自动推送似乎是一个可怕的想法!本地提交是分布式版本控制系统相对于传统集中式方法最重要的改进之一。还有备份。 - Raffael
@Raffael:我会尝试那个命令。至于自动推送,我认为它可能是因为某种原因而暂时需要的(总的来说,我同意你的看法)。我大约两周前加入了这个项目,还在适应事物的运作方式。 - Gabriel Ferraz
自动推送到备份服务器将是一个功能;你正在使用的是纯粹愚蠢的东西。 +++ 我通过艰难的方式学会了不要过度使用 git stash,我建议你只在简单的事情上使用它。当事情变得更加复杂时,我会选择临时分支。即使有适当命名的存储条目,也很容易迷失方向。 +++ 还有 git stash show -p stash@{1} 可以查看详细信息和类似的内容,但使用分支更简单。 - maaartinus
我同意,分支更好。或者在当前分支的头部创建一个临时提交(我喜欢将其命名为WIP-工作正在进行中)。无论哪种方式更适合您的情况。 - Raffael
正如我所说,项目工作流在我加入项目之前就已经设置好了。这不是我的决定。但还是谢谢你的指令。 - Gabriel Ferraz
4个回答

21
git stash命令会创建一个提交(commit)。 当然,git commit命令也会创建一个提交。那么为什么我们还需要git stash呢? 这两个命令之间的一个重大区别是:git stash所创建的提交(commit)“不属于任何分支(branch)”。这使得您可以在一个分支上暂存(stash),然后切换到另一个分支并在那里应用该stash。换句话说,它允许您移动进行中的工作。 通常情况下,您也可以移动进行中的工作,但并非总是如此。请参见Git-在当前分支上有未提交的更改时切换到另一个分支。但是,当您无法移动进行中的工作时,您可以使用 git stash 来处理问题。 另一方面,如果您想要“在分支上暂存”,就像您的情况一样,则最好只做普通提交(commit),而不是特殊的stash提交(commit)。这样的提交更容易处理,并且也没有git stash存在的错误(bug)。您可能不太可能遇到此错误,但是“普通提交比stash更简单易用”(分支上的提交与stash相比)是避免git stash的一个非常好的理由。

如果您仍然喜欢使用git stash,请注意每个新的stash将"推"先前的stash,使它们在"stash堆栈"中上升。旧的stash变为stash@{1},而原来的stash@{1}变为stash@{2},以此类推。当您drop(丢弃)或pop(尝试应用,然后丢弃)一个stash时,上面堆叠的stash会向下移动,因此如果您在有stash@{4}stash@{5}的情况下执行git stash drop stash@{3},现在您将只剩下stash@{3}stash@{4}

您可以通过此方式对任何stash进行命名,包括最近的stash:stash@{0}stash是同一意思。(Git实际上使用stashreflog来实现所有这些功能。)


1实际上,它至少进行两个提交,有时候是三个。两个提交存储索引(index)和工作树(work-tree)状态;第三个提交(如果存在)来自于-u-a,并存储未暂存的(-u)或所有的(-a)文件。工作树提交是一个非常奇怪的合并提交,其第二个父提交为索引提交,第三个提交(如果存在)为第三个父提交。工作树提交的第一个父提交,以及索引提交的唯一父提交,是运行git stash时当前的提交。

如果你在Git中进行复杂的操作,画出提交图片段是一个好主意。这时,索引和工作树的提交对似乎悬挂在原始提交之外,refs/stash引用指向该对,而不是分支名称。它看起来几乎像一个小手提包,或者像一个吊在树枝上的食物储藏库,以避免熊靠近,我喜欢称之为"stash bag"。


3
Torek(一如既往)提供了一个很好的答案,但我认为这里简洁的答案就是注意stash作为一个堆栈(LIFO)包含数据。因此,当你推送A的工作然后是B的工作时,它首先弹出B,然后是A。所以当你回到A,然后进行第一次弹出时,你得到了保存的B工作。

1
当你执行“git stash pop”命令时,无论你之前在哪个分支上stash过,它都会重新应用最后一次stash。所以,你遇到的问题是在分支B上创建的最后一个stash被应用到了分支A上。下一次,你需要指定想要应用哪个stash。
解决方法如下:
  1. 再次对当前状态进行stash
  2. 弹出倒数第二个stash
就这样。

1
如果您键入git stash list,将根据您的情况显示类似于此的内容。
stash@{0}: WIP on branch-b
stash@{1}: WIP on branch-a

输入 git stash pop 命令将总是获取 stash@{0} 节点。

因此,如果您在 分支a 中,并且想要应用该分支中的进度。根据列表,您应该输入:

git stash pop stash@{1}

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