我正尝试开始使用git分支,但是有些我不理解。
从我的master分支上的一个小变化开始,到现在已经发展成为一个大功能,因此我想将其移至一个名为dev的新分支,以便我仍然可以在master分支上进行较小的更改并合并dev分支完成后。
我尝试了各种方法,但由于我的行为异常,因此正在使用this one as example。
因此,使用git stash
,所有未提交的更改都将移到存储中,而主服务器又恢复到其最后的提交状态。
然后创建一个新的“ dev”分支并切换到该分支。
使用stash apply
时,已保存的存储将应用回该分支,并且所有更改都将返回。
现在,当我checkout master
时,我再次看到所有更改的文件。我期望只有dev分支具有这些更改,并且当切换回master时,它将具有最后一次提交的状态。我不确定这是否是正常行为,并且我的期望是错误的(有更多问题),还是master分支应该保持在最后一次提交状态?
答案 0 :(得分:2)
您看到的是您的工作树(您的工作区域)与实际的提交之间有很大的区别。
如果您不认为Git存储了更改,这可能会有所帮助,因为事实并非如此。它存储快照。每次提交都是所有文件的完整副本,当然,还包括所有已提交的文件,但这有点多余,因此,请考虑“所有文件”。保存的快照将永久冻结-好吧,只要提交本身存在就可以生存,但是无论如何默认情况下都是永久的。
这些快照以特殊的压缩,冻结,仅Git格式保存。它们对Git毫无好处,因此对于您来说,在git checkout
上与它们一起工作-Git必须将它们扩展为正常的形式,使它们处于未冻结和有用的状态,当然您可以更改他们周围。那就是您的工作树,您将在其中完成工作。
因此,每个文件都有两个“活动”副本:提交中的冻结副本和您正在处理的副本。当您将事物视为更改时,就是在比较这些不同的副本。
Git在这里增加了额外的皱纹,因为实际上每个文件都有一个 1/3 副本。当Git第一次提取提交时,它会将冻结的仅Git的文件复制到Git调用的内容中,以各种方式 index , staging area 或 cache < / em>。在这里,文件仍然仅适用于Git,但是现在它们不再冻结了。它们更加肮脏,易于冻结,特别是,您可以用任何新副本替换任何文件的索引副本。这就是git add
的作用。
当您使用git stash
时,Git真正要做的是提交一次,或更准确地说,两次提交。然后,它清除索引和工作树,以使其与当前提交或HEAD
匹配。此隐藏提交的主要特殊之处在于它不在 any 分支上。因此,无论切换到哪个分支,您都可以切换到另一个分支并使用git apply
重新提取它。
切换到另一个分支意味着选择该分支的tip提交作为当前提交,并选择该分支作为当前分支。 Git将从那个快照中复制所有那些文件到索引和工作树中。如果您以前很干净,那么您将再次处于干净状态,但是现在HEAD
提交是其他一些提交。现在,您可以再次修改工作树。
与Philip Couling noted一样,有一种特殊情况,即两个分支名称标识 same 提交。如果我们将提交绘制为链状,则两个分支名称指向最新的提交:
...--o--o--* <-- master, develop
那么,从某种意义上说,无论我们git checkout master
还是git checkout develop
都没关系。两者都标识相同的最后一次提交。 HEAD
中的活动快照,索引和提交将具有相同的内容。如果您从提交*
切换到提交*
,则Git不必对索引和工作树做任何事情,因为您实际上并未在其中进行任何切换!
但这还不是git checkout
的全部功能!特别是,检出master
告诉Git将名称HEAD
附加到名称master
,而检出develop
则告诉Git将其附加到develop
。当您提交 new 提交时,这会影响哪个分支名称被更新 。如果您是这样设置的:
...--o--o--* <-- master, develop (HEAD)
然后进行 new 提交,Git将移动名称develop
指向新的提交:
...--o--o--o <-- master
\
* <-- develop (HEAD)
请注意,HEAD
仍仅附加到develop
。诀窍是develop
现在可以标识此新提交。
Git根据 index 中的内容而不是工作树中的内容进行新的提交。您使用git add
告诉Git:将工作树文件复制到索引文件的顶部。这将准备冻结文件,将其压缩为特殊的仅Git格式。那时工作树版本中的所有内容。然后git commit
只需要冻结索引副本,以使新提交冻结所有文件的副本。
因此,对于这个特殊的案例,git stash
进行了一次提交,清理了您的工作树,然后您将HEAD
重新附加到同一提交但又不同分支名称,然后重新应用您的工作树更改。隐藏和应用完全没有必要。但是,如果develop
和master
指向不同提交,则通常需要执行该存储并应用。 (即使那样,在许多情况下,Git仍可让您摆脱切换提交的麻烦。有关所有详细信息,请参见Checkout another branch when there are uncommitted changes on the current branch。)
答案 1 :(得分:1)
在大多数情况下,如果您在未提交更改的情况下尝试签出,则会收到错误消息。如果两个分支(您所在的分支和您要签出的分支)没有指向同一提交,则会发生这种情况。为了在两个分支之间保持未提交的更改,您需要像完成操作一样使用git stash
。
创建分支时,它指向您当前正在进行的提交。因此dev
和master
都指向同一提交。在这种特殊情况下,git将让您自由切换分支,因为checkout
不会更改任何文件。
这对于您的用例最有用...开始进行更改,但是忘记启动新分支。在这种情况下,您可以仅创建新分支并切换到该分支,而无需使用git stash
。
编辑:
如果在git-stash
之后不存在更改,则在提交到另一个分支后它们将不再存在。如果您真的想测试一下,没有什么可以阻止您这样做的:
git checkout dev
git add *
git commit -m 'Test commit'
git checkout master
# look around
git checkout dev
git reset HEAD~
最后一行弹出您的上一次提交,并将文件保留为未提交的更改。只是不要按test commit
。
答案 2 :(得分:0)
我通常会接受菲利普的分析。
只需添加以下内容:如果您位于新创建的分支上,并且需要签出回master,期望在此处找不到更改,则需要在新分支上提交这些更改,然后再进行切换:
# just after your stash apply on the new branch
git commit -a -m"temporary message"
git checkout master
然后,如果您要继续在新分支上工作,请撤消此临时提交,直到完成后再进行工作,然后“真正”提交:
git checkout dev
# the following line will undo the commit but keep your changes in working tree
git reset --soft HEAD^
# work some more
git commit -a -m "real final message"
答案 3 :(得分:0)
Git仅存储您的承诺。由于您尚未提交这些更改,因此它们不会存储在任何一个分支中。 Stash是一种创建不属于任何特定分支的提交的特殊方式,它包括已分阶段的更改和未分阶段的更改(有多种选项可以修改此行为);但是当您应用或弹出存储时,它不会自动在分支上创建新的提交。
Git对失去工作非常敏感。当您要求git签出master时,它注意到您尚未提交更改。它在后台进行了快速检查,以查看从开发人员转移到熟练人员时是否会丢失任何工作。由于两个分支具有相同的历史记录(因此两个分支之间的文件没有差异),因此git知道不会有冲突,因此您未提交的更改不会被覆盖或“丢失”。它只是保留了它们。当您意识到自己在错误的分支上工作时,这是一个方便的功能...只要没有冲突,您就可以尝试切换分支并与您进行所有未提交的工作。再次注意,未提交的更改只有在提交后才存储在两个分支中。