更换分支不会丢弃本地更改。

3

所以,我有些困惑。

我不太懂git,但是我记得如果你在一个有未提交更改的分支上,尝试切换到另一个分支,git 要么不让你这样做,要么将放弃你的更改。

我还查看了 Chacon 和 Straub 的 Apress 《Pro Git》书籍,证实了这种行为是正确的。

enter image description here

还有

enter image description here

我承认我的英语不是很好,但我记得这个解释是正确的,因为从前我经常使用git。或者说我对于git在改变分支时丢弃未提交的更改的理解是错误的吗?我是否错误地理解了书中的内容?

如果我是正确的,为什么我的存储库会将更改的文件从一个分支移动到另一个分支?您可以在此处检查其行为:

enter image description here

使用 git status 检查当前是否在主分支并且干净无更改。

创建一个新的分支并切换到它。

进行一些随机更改,比如使用 ed 删除几行。

切换回主分支。

惊喜的是,git diffgit status 显示主分支上的文件也被更改了。

难道不应该由 git 阻止我更改分支或者放弃我的更改吗?这很令人困惑,我花了一段时间才意识到为什么我的主分支表现出了意外的行为,即使我没有对它做任何更改。

git version 2.30.1 (Apple Git-130)


编辑: 一些答案提到git switchgit checkout不同。 我仍然不确定这是否正确,因为不同的人(和文档)有不同的说法。

但是,我也尝试了使用git checkout来做同样的事情,它也会移动文件,而不是阻止我切换或警告我工作目录中的差异。

您可以看到git checkout做相同的事情如下:

enter image description here


1
我一直观察到这种行为:要么你无法切换到另一个分支,要么更改会被保留但永远不会被丢弃。这意味着你会失去你的更改。 - Gaël J
它将允许您检查您修改的文件是否在HEAD和您尝试切换到的位置之间完全相同。如果不是这种情况,git会发出警告并停止(除非您使用-f选项)。 - eftshift0
这是正确的@elshift0。这就是我的理解。在我看来,如果我删除文件中的一行,就像我在截图中所做的那样,修改后的文件在HEAD和main之间并不完全相同,对吧?或者说,少了内容的文件怎么算是相同的呢?那什么才能真正地称为“冲突”呢?在我展示的情况下,我认为git不应该让我切换,除非我明确要求移动那些文件... - jacksonbenete
虽然 git switchgit checkout 有一些不同,但真正的关键区别在于,某些 git checkout 的参数集会以 Git 新手甚至有经验的 Git 用户意想不到的方式破坏未提交的工作。switch 命令的引入 (a) 解决了这个问题(即使在 git checkout 中:它现在会提示您的请求是模棱两可的),并且 (b) 给新手一个不能以这种方式破坏未提交工作的命令:没有请求语法可以实现这一点。 - torek
2
有关 何时 checkout/switch 允许您切换,以及何时不允许,即使您有未提交的工作,请参见当当前分支存在未提交更改时,如何检出另一个分支 - torek
3个回答

2
当您切换分支时,未提交的更改将保持不变。否则,即使无意中切换分支也会默默删除您所做的任何本地工作。这不好(用户会抱怨;有理由)。请记住:工作树中的本地更改与任何分支都没有关联。它们只存在于您的工作树中。您的第一个引用/屏幕截图仅涉及已提交的更改:当您切换分支时,您的工作目录将反映所切换到的分支的内容。它还提到“如果Git不能干净地执行”(我强调)-这意味着仅在同一文件在本地(未提交)和其他分支中进行了更改时,Git才会中止。第二个引用明确提到“冲突的未提交更改”。在这种情况下,Git将拒绝切换分支。您可以提交更改、删除更改或隐藏更改-之后,您可以切换到另一个分支。只有在分支之间存在不同的(提交的)文件时才会发生冲突。在切换分支时,本地未提交的更改不被视为“冲突检测”的一部分。git checkout的这种行为一直是相同的。git switch(checkout的新替代品)的行为方式相同。

那么问题就在于我对“文件冲突”理解上的问题。 如果我在主分支中有一个名为expand的文件,然后切换到另一个分支并编辑名为expand的文件,再切回主分支,这不算是“未提交的冲突更改”吗?同名文件(同一文件)内容不同。这怎么不算是冲突呢?您能否详细说明一下?也许真正符合冲突条件的理解将是我理解的关键。谢谢! - jacksonbenete
@jacksonbenete 只有在分支之间的(已提交)文件不同时才会发生冲突。如果文件在两个分支中相同,则Git不需要更新该文件,并且可以将所有本地(未提交)更改保留在文件中。只有当文件的提交内容不同时,Git才会拒绝切换。为什么?因为它需要将您的(未提交)更改重新应用于文件的不同版本,这可能会出错,潜在地破坏您的工作。 - knittl

1
问题的关键在于,你所描述的行为适用于git checkout命令,但是你正在使用相对较新的git switch命令来切换分支。这进一步复杂化了你引用的文档中的内容,比如“当你切换分支时”,但它们可能也指的是git checkout
如果你使用git switch,并且你有本地更改与要切换到的分支不冲突,它将只是切换并移动你的更改,基本上是这样。 从文档中

切换到指定的分支。工作树和索引将更新以匹配该分支。所有新提交将添加到该分支的末尾。

...

切换分支不需要干净的索引和工作树(即与HEAD没有差异)。但是,如果该操作导致本地更改丢失,则会中止操作,除非使用--discard-changes--merge告诉它否则。


我最初认为会是这种情况,但使用git checkout也会移动更改(我上传了另一张截图显示了这一点)。也许这一直是与@knittl提到的相同行为,但如果是这种情况,那么我就不明白文件之间真正的“冲突”是什么了。 - jacksonbenete

1

需要知道的事情

第一件要知道的事情:每个提交都包含了所有您的文件。

第二件要知道的事情:检出或切换到一个提交意味着使用该提交中的所有内容完全替换可见文件(工作树)。

切换到新分支

常见的用例是,您在A分支上编辑和/或添加了一些文件,即将提交,然后突然想到:等等,不行,我应该在新分支上进行。因此,您创建了一个新分支并切换到它,一切都很好;您的所有工作都会跟随您进入新分支。为什么?

因为检出新分支没有任何影响。该分支只是您已经在的相同提交的新名称。

切换到旧分支

现在考虑另一种情况。

在新分支上,您进行了编辑、添加和提交,并开始编辑一个文件。突然间,您决定切换回第一个分支。

这意味着您要求Git使用第一个分支所代表的提交中的内容完全替换所有文件。这可能是非常彻底的更改。尽管如此,通常Git很乐意这样做。

但是,如果您正在编辑一个文件,而该文件已经被Git跟踪,替换或删除它会破坏未提交的工作。因此,Git会拒绝执行。

当您检出分支时,Git将永远不会覆盖您未提交的更改。

摧毁所有东西

但是...

如果您要求检出或恢复特定文件,那么Git会认为您知道自己在做什么,并允许您摧毁自己未提交的工作。类似地,使用硬重置。这些操作本质上是破坏性的,Git会退后一步并让您自行决定是否进行破坏。


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