我正在实现一个新功能,为此我创建了一个新分支,并对它进行了一些提交,然后进行了推送。我现在希望以待定/未提交的更改的形式继续在master中使用此功能。等待中,因为在我提交之前需要做更多的工作。我该怎么做?
当我最终提交时,master的修订历史记录应仅指示一个提交,就好像我从未创建另一个分支或进行中间提交一样。我该怎么做?
手动方法是创建第二个git工作区,在其中一个中打开master,在另一个中打开分支,然后将每个修改后的文件复制粘贴。有自动的方法吗?
答案 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 diff
或git diff --cached
/ git diff --staged
的情况:Git将索引与工作树(普通git diff
)或{ {1}}提交到索引(HEAD
或--staged
)。该选项(如果有)确定两个输入。也就是说,Git从此列表中获取了三件事中的两件事:
--cached
提交,这是一个真实的提交,包含所有特殊格式的仅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将合并基础与HEAD
和git merge
分支技巧进行比较,就好像使用了两个--ours
命令:一个从基础到我们的命令,一个从基础到他们的命令。
忽略诸如添加,删除或重命名文件之类的问题,对这三个提交的比较产生了谁更改了哪些文件的列表。如果我们更改了一些他们没有更改的文件,或者他们更改了我们没有更改的文件,则合并这些文件很容易:Git可以获取我们的文件,或者他们的档案。如果我们俩都没有更改文件,则Git可以使用文件的三个版本中的任何一个(合并基本版本,我们版本或它们的版本)。
在困难的情况下(我们都更改了一些文件),Git从合并基础开始,然后合并我们的更改及其更改,并将合并的更改应用于合并基础副本。如果我们的更改及其更改触及不同的 line ,则合并很简单,Git声明结果是成功的(即使在现实中没有意义)。如果我们和他们都更改了 same 行,则Git会声明合并冲突。
合并冲突的情况最混乱。在这里,Git将所有三个输入文件保留在索引中,然后将冲突的合并写入工作树。像--theirs
那样的人,您的工作就是根据自己的喜好提出正确的合并。您可以使用文件的三个索引副本,工作树副本或所有这些。您自己解决合并问题,然后使用git diff --find-renames
将正确的结果写入索引,用单个合并副本替换三个未合并的副本。解决了这一冲突;解决所有冲突后,就可以完成合并。
如果您将git merge
与git 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中的since
和revision range
有关详细信息。)
第二,我建议您将所有cat
个文件*.patch
合并为一个。
第三,将累积的 patch 文件的更改放入 master 的索引中:
git checkout master
patch -p1 < accumulated.patch
git diff
git add .
...
patch
是 bash 中的一个,并且-p1
是必需的,用于剥离 patch 文件中特定于git的路径前缀。 / p>