将master分支中未提交的更改移至dev分支并还原master

时间:2019-01-08 16:24:19

标签: git branch git-branch

我正尝试开始使用git分支,但是有些我不理解。

从我的master分支上的一个小变化开始,到现在已经发展成为一个大功能,因此我想将其移至一个名为dev的新分支,以便我仍然可以在master分支上进行较小的更改并合并dev分支完成后。

我尝试了各种方法,但由于我的行为异常,因此正在使用this one as example

因此,使用git stash,所有未提交的更改都将移到存储中,而主服务器又恢复到其最后的提交状态。 然后创建一个新的“ dev”分支并切换到该分支。 使用stash apply时,已保存的存储将应用回该分支,并且所有更改都将返回。 现在,当我checkout master时,我再次看到所有更改的文件。我期望只有dev分支具有这些更改,并且当切换回master时,它将具有最后一次提交的状态。我不确定这是否是正常行为,并且我的期望是错误的(有更多问题),还是master分支应该保持在最后一次提交状态?

4 个答案:

答案 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重新附加到同一提交但又不同分支名称,然后重新应用您的工作树更改。隐藏和应用完全没有必要。但是,如果developmaster指向不同提交,则通常需要执行该存储并应用。 (即使那样,在许多情况下,Git仍可让您摆脱切换提交的麻烦。有关所有详细信息,请参见Checkout another branch when there are uncommitted changes on the current branch。)

答案 1 :(得分:1)

在大多数情况下,如果您在未提交更改的情况下尝试签出,则会收到错误消息。如果两个分支(您所在的分支和您要签出的分支)没有指向同一提交,则会发生这种情况。为了在两个分支之间保持未提交的更改,您需要像完成操作一样使用git stash

创建分支时,它指向您当前正在进行的提交。因此devmaster都指向同一提交。在这种特殊情况下,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知道不会有冲突,因此您未提交的更改不会被覆盖或“丢失”。它只是保留了它们。当您意识到自己在错误的分支上工作时,这是一个方便的功能...只要没有冲突,您就可以尝试切换分支并与您进行所有未提交的工作。再次注意,未提交的更改只有在提交后才存储在两个分支中。