您如何将已提交的更改从分支转移到主更改作为未决更改?

时间:2019-01-03 02:24:52

标签: git branch rebase feature-branch

我正在实现一个新功能,为此我创建了一个新分支,并对它进行了一些提交,然后进行了推送。我现在希望以待定/未提交的更改的形式继续在master中使用此功能。等待中,因为在我提交之前需要做更多的工作。我该怎么做?

当我最终提交时,master的修订历史记录应仅指示一个提交,就好像我从未创建另一个分支或进行中间提交一样。我该怎么做?

手动方法是创建第二个git工作区,在其中一个中打开master,在另一个中打开分支,然后将每个修改后的文件复制粘贴。有自动的方法吗?

3 个答案:

答案 0 :(得分:3)

首先,我将给出命令,然后给出它们的实际作用以及为什么这样做可以实现您想要的结果。我在下面假设功能分支仅称为branch -如果它具有其他名称,则将其赋予git merge

git status               # and make sure everything is committed on your branch
                         # do any extra commits here if required

git checkout master      # change the current branch/commit, index, and work-tree
git merge --squash --no-commit branch

(在git merge中,--squash仍暗示着--no-commit,因此您可以在此处省略第二个标志参数。我在其中提供了更多示例说明。此外,--squash的意思是< em>在索引和工作树中执行合并操作,但不要进行合并提交。)

您可能要视工具而定,git reset(默认情况下将--mixed作为提交的HEAD重置)来复制HEAD提交进入索引,因此普通git diff会向您显示所有内容,而git status会将所有内容显示为未上载提交

重要的是要注意,这里没有真正的待更改。 Git根本不存储更改。 Git存储的是 commits ,并且每次提交都是所有文件的完整快照。当您问Git 发生了什么变化时,您必须告诉Git关于两个提交,或至少两个两棵树已满的文件。然后,Git从这两个输入中找出快照A和B中的区别。然后Git告诉您这些区别-但是A和B仍是完整快照。在某些方面,这无关紧要-您会在需要时看到更改,并在需要时看到快照。但是,如果您的思维模式与Git的真正功能相匹配,您将解开的谜团就会更少:您自己的工作会更容易。最重要的方式是记住:要查看更改或差异,您必须提供两个输入。有时,这些输入中有一个是隐含的。有时候,这两个都是

最后一个是简单的git diffgit diff --cached / git diff --staged的情况:Git将索引与工作树(普通git diff)或{ {1}}提交到索引(HEAD--staged)。该选项(如果有)确定两个输入。也就是说,Git从此列表中获取了三件事中的两件事:

  • --cached提交,这是一个真实的提交,包含所有特殊格式的仅Git,冻结/只读形式的文件;
  • 索引,本质上是建议的下一次提交,并且包含所有文件的副本,也采用仅Git形式,但并不完全冻结;
  • 工作树,该文件以普通有用的格式保存文件。

Git掌握了其中的两个内容,将它们进行比较并告诉您有什么不同。 HEAD命令作为这两个git status两者的一部分运行(始终设置了某些选项,例如git diff和{{1 }})并总结结果:--name-status的输出是为提交而进行的更改,而--find-renames=50%的输出是为提交而未进行的更改

您还可以手动运行git diff --staged。这将选择git diff和您的工作树作为两个输入:您在参数中显式命名了其中一个git diff HEAD,而另一个则隐含。它将冻结的HEAD内容与未冻结的常规格式工作树内容进行比较,并向您展示了它们之间的区别。再次,Git正在比较完整快照:第二一个是实时工作树,通过差异化过程进行快照。

(此外:在我看来,您想做的事情可能是错误的处理方法。您经常可以通过尽早而频繁地进行工作来简化自己的工作。做差异是个好主意,但是您可以从基础提交到技巧提交(即跨一定范围的提交)进行评估,以评估总体结果,然后可以使用交互式变基或我的首选方法附加分支,将较小的提交重构为更大的提交。明智的一系列提交,但这大部分是个人喜好,听起来您的Xcode工具可以比它做的要好得多;您使用它的方式并不是错误,这简直是可怕的限制。)

这如何以及为什么起作用

我开始写更长的帖子,但是它失控了,所以我只想说:请参阅我的其他StackOverflow答案,以了解Git如何从索引而不是工作树中进行提交,以及这些提交如何进行更新附加了HEAD的分支名称。

Git的合并本身也是一个很大的话题。但是,可以说,合并使用 index 达到其合并结果,而 work-tree 扮演次要角色,主要是在存在合并冲突的情况下。

真正的合并-即HEAD,它执行完整的三向合并,最后将进行类型为 merge commit 的新提交。与两个父母在一起。这两个父对象是用于计算合并结果的三个输入中的两个。第三个输入由其他两个隐式表示。这是合并基础。 Git使用提交图自行查找合并基础。然后,Git将合并基础与HEADgit merge分支技巧进行比较,就好像使用了两个--ours命令:一个从基础到我们的命令,一个从基础到他们的命令。

忽略诸如添加,删除或重命名文件之类的问题,对这三个提交的比较产生了谁更改了哪些文件的列表。如果我们更改了一些他们没有更改的文件,或者他们更改了我们没有更改的文件,则合并这些文件很容易:Git可以获取我们的文件,或者他们的档案。如果我们俩都没有更改文件,则Git可以使用文件的三个版本中的任何一个(合并基本版本,我们版本或它们的版本)。

在困难的情况下(我们都更改了一些文件),Git从合并基础开始,然后合并我们的更改及其更改,并将合并的更改应用于合并基础副本。如果我们的更改及其更改触及不同的 line ,则合并很简单,Git声明结果是成功的(即使在现实中没有意义)。如果我们和他们都更改了 same 行,则Git会声明合并冲突。

合并冲突的情况最混乱。在这里,Git将所有三个输入文件保留在索引中,然后将冲突的合并写入工作树。像--theirs那样的人,您的工作就是根据自己的喜好提出正​​确的合并。您可以使用文件的三个索引副本,工作树副本或所有这些。您自己解决合并问题,然后使用git diff --find-renames将正确的结果写入索引,用单个合并副本替换三个未合并的副本。解决了这一冲突;解决所有冲突后,就可以完成合并。

如果您将git mergegit add一起运行,则Git将自行进行尽可能多的合并(可能是全部合并),然后停止,进行合并的方式相同冲突。如果没有冲突,则索引和工作树都将处于准备提交状态。 (如果存在冲突,则git merge选项无效:Git已经停下来,留下的乱七八糟,现在仍然停下来,剩下的乱七八糟供您修复。)

尽管如此,在所有这些情况下,Git都会在--no-commit目录中留下一个文件,告诉Git next 提交-无论是--no-commit还是{{1 }}-应该有两个父母。这使新提交成为合并提交,它将两个分支捆绑在一起:

.git

这不是您想要的。因此,我们使用git merge --continue,它告诉Git:像往常一样执行合并工作,但不是进行 merge 提交,而是将下一个提交安排为普通提交。也就是说,如果Git进行新的提交git commit,它将看起来像这样:

             I--J   [master pointed to J when we started]
            /    \
...--F--G--H      M   <-- master (HEAD)
            \    /
             K--L   <-- branch

由于没有特别好的原因,即使合并顺利,git merge --squash也会禁止提交。因此,M之后,索引和工作树将更新,但是您可以对工作树进行更多更改,使用 I--J--M <-- master (HEAD) / ...--F--G--H \ K--L <-- branch 将更新的文件复制到索引中,并且仅在完成后,请使用--squash进行新的提交。

(对于更琐碎的合并,Git将默认使用它进行快进,git merge --squash也会禁止快进模式,就像git add一样。所以git commit ,没有--squash且没有git merge --no-ff就足够了。)

最后的--squash(如果使用和需要)仅将--no-ff提交复制回索引中。也就是说,您可能希望您的工具将--no-commit提交与工作树进行比较,就好像您在运行git reset一样。如果该工具将索引与工作树进行比较,则在HEAD操作对其进行更新之后,可以通过删除索引以匹配HEAD来获得相同的效果。

答案 1 :(得分:1)

您是否考虑过合并到master中,然后使用首选的diff工具来比较提交历史记录?

合并到master中将是一次提交。但是只要您不push对远程更改(例如'origin'),就可以始终使用git reset HEAD~2重置合并以放弃合并提交。

还有一个注意事项,“ HEAD〜2”是在提交历史记录中重置为的提交次数的示例。如果您的合并提交使您的提交数比远程提交多了“ x”,那么您将用提交git status报告的提交数代替“ 2”。

答案 2 :(得分:1)

我敢肯定有很多有效的方法可以做到这一点。我想用 patches 从功能分支转移您的代码更改。

cd $the-root-of-your-project
git checkout feature-branch
git format-patch <the-commit-since-you-began-the-feature>

这将为每次提交创建一个 patch 文件。 (提示:您需要在最后一个命令中输入的提交是要传输的第一个命令之前的 。请参见https://git-scm.com/docs/git-format-patch中的sincerevision range有关详细信息。)

第二,我建议您将所有cat个文件*.patch合并为一个。

第三,将累积的 patch 文件的更改放入 master 的索引中:

git checkout master
patch -p1 < accumulated.patch
git diff
git add .
...

patch bash 中的一个,并且-p1是必需的,用于剥离 patch 文件中特定于git的路径前缀。 / p>