如果我在分支主机中有未提交的更改,并且我签出了一个功能分支并提交了它们,然后再次签出主机,然后"git状态";将不会显示这些更改,因为它们已提交到不同的分支。
我如何才能做到这一点,这样我就可以回到主分支,让更改仍然存在并且仍然未被调试,就像我签出我提交它们的功能分支之前一样?
git stash
git checkout <featurebranch>
git stash apply
git commit -a -m "saved changes"
git checkout -
git stash pop
有一些不同的方法可以实现您想要的。最直接的方法是当你在master
:
git show feature-branch | git apply
但是,由于目标是(我假设)从未提交的更改中进行提交,您也可以选择提交:
git cherry-pick feature-branch
人们给了你几个食谱(我自己的食谱是使用git cherry-pick -n
,这很像j6t的答案,但不需要管道)。更有趣的事情是理解为什么这样工作。
Git存储提交:也就是说,存储库中的东西主要是提交对象,以及它们丑陋的哈希ID。这些对象存在于一个大的、只读的、(大部分)只追加的"数据库中;所有Git对象";。每个提交存储两件事,即每个文件的完整快照,以及一些元数据。快照是一种特殊的、压缩的、Git化的格式,其中文件内容都是针对所有现有存储的文件(包括同一提交和早期提交中的文件)进行重复数据消除的。Git实际上是通过将文件存储为大数据库中的blob对象来实现这一点的。
(除了存储的对象,Git还有一个names数据库,它将名称(分支名称、标记名称、远程跟踪名称等)映射到哈希ID。还有许多其他文件/数据库,如reflogs、实现Git索引的文件等。因此,隐藏的.git
目录中有很多内容。但是,大的全对象数据库和小的名称数据库是最重要的两个:它们是git clone
可以看到的。克隆复制对象数据库,并对名称数据库执行一些奇怪的操作,而不是直接复制它,新存储库中的其余辅助文件都是从头开始构建的。)
然而,这给我们留下了一个看似不可能的问题:任何给定提交中的文件都被冻结、Git化,通常对Git本身之外的任何东西都毫无用处。没有其他程序可以读取,也没有任何程序——甚至Git本身——可以写入既不能读也不能写的文件有什么好处
有一个显而易见的答案。我们不妨问:存档有什么好处这当然有利于提取。我们从一些档案中提取文件,现在我们在(普通?)计算机上的普通存储器中的普通文件夹中有了普通文件。我们现在可以完成我们的(普通的?)工作了。
Git就是这么做的。Git提供了一个区域——我们称之为工作树或的工作树——我们在这里完成工作。但是,这是您提出问题所需的关键见解,这些文件根本不在Git中。当然,Git从一些提交中提取了它们。当然,我们已经研究了一段时间了。当然,我们将告诉Git将它们放入新的提交中,像往常一样使用git add
,然后使用git commit
。但是,尽管它们在我们的普通工作树中是普通的普通文件,但在Git中它们不是。
我们运行git commit
并生成新的快照和元数据,这些快照和元数据现在一直处于冻结状态或者我们运行:
git checkout feature-branch
或:
git switch feature-branch
有时,当我们这样做的时候,Git会说好吧,我做到了,你们都准备好了。有时Git会说对不起,不能这样做,会丢失你的一些更改。我们何时以及为什么会出现这些错误的描述很复杂,因为它涉及Git索引和工作树的血腥细节(请参阅在当前分支上有未提交的更改时签出另一个分支)。但在某种基本级别上,Git可以这样做当且仅当;切换到另一个分支";操作不需要删除和替换我们一直在更改的文件。
分支切换步骤在逻辑上意味着:从我的工作区域中删除我现在使用的提交中产生的每个文件;在我的工作区域中,替换来自我将切换到的提交的每个文件。但是Git提交了消除重复的文件。其中许多文件将被证明是完全重复的。如果Git以缓慢而愚蠢的方式进行删除和替换操作,删除每个文件并替换它,那么许多文件最终都会是一样的。因此,Git试图做到聪明:对于每个完全重复的文件,什么都不做。只要我们所有的";更改";是什么都不做的文件,我们很好!
(对于每个文件,这种"要么恢复要么什么都不做"的实际实现涉及每个文件的三个副本,即两个提交中的副本和当前索引中的副本,这就是为什么当我们进入低级细节时会如此混乱。但原理足够清楚。)
所以,我们最终";关于";所需的分支,因为我们所有的更改都在这些无所事事的文件中。然后我们添加并提交,然后我们将git switch
返回到master
或main
,或者切换到功能分支之前所在的任何分支。
现在,记住我们是如何注意到切换分支意味着R&R所有文件,或者至少是所有不同的文件。我们只是将更改提交给了一些文件:这些文件是我们关心的。因此,我们关心的文件被更改,因此必须是R&R-ed。他们会的,现在我们的改变是";"消失";。
但以下是我们所知道的我们的切换有效,所以我们刚刚提交的文件是我们"已更改";当我们在另一个分支中进行新的提交时。因此,git show feature-branch
或git diff feature-branch~1 feature-branch
的结果正是我们所说的那些变化;想要回来";。因此,我们可以git show feature-branch | git apply
或git cherry-pick -n feature-branch
。
git show feature-branch | git apply
和git cherry-pick -n
的区别在于,git apply
只修改工作树副本,而git cherry-pick -n
同时修改和索引副本。所以我们得到CCD_;"免费";,不管我们愿不愿意。这反过来可以引导你选择这两种选择中的哪一种。但两者都会很好。
您可以在功能分支上的提交之间创建差异,然后在主分支上应用更改。不过,这会导致重复,所以要小心,以后选择合并功能分支时可能会出现合并冲突。
# While you're on the feature branch, get the commit-id of HEAD
git log -1
# Then use the COMMIT-ID to create a diff from the previous commit on this feature branch and write this to the diff file.
git diff <COMMIT-ID>^ <COMMIT-ID> > changes.diff
# Now, checkout to the master branch
git checkout master
# Apply the changes, they will not be commited
git apply changes.diff
只是要小心,不要在这里合并功能分支。
使用git stash
,但当您将更改隐藏到master并切换到feature分支时,请使用git stash apply
而不是git stash pop
将更改应用到feature。提交完所有内容后,您可以返回master,并且存储仍然可以使用git stash pop
将它们重新应用到您的工作目录中。
git stash
git switch feature
git stash apply
# commit as usual
git switch master
git stash pop