我有两个存储库A和B.我想在没有完整历史的情况下将A合并到B中,所以我做了:
git pull --squash A_URL master
似乎有效。 B没有初始提交和合并提交。 我继续研究A然后再次尝试将它合并到B中,但这次保留了历史。
git pull A_URL master
问题是A的所有历史现在都在B中显示,即使是最初被压缩的提交。有没有解决这个问题? 基本上我只想获得最近的提交(从最初的壁球开始)。
答案 0 :(得分:2)
我认为你至少在一个误解之下工作。
让我们把它分解为这些部分:
git log
。它的工作原理是遵循commit graph 。此图是在您根据每个提交中存储的信息运行git log
时构造的。大多数提交都有一个父提交,git log
向您显示提交,然后显示其父级,然后显示其父级的父级,依此类推,形成一个良好的有序线性但是, 1 merge 提交至少有两个父提交,git log
显示两者< /他们,他们的父母,以及这两个父母和他们的父母。父母,等等。 2 git pull
命令仅为git fetch
,后跟另一个命令,通常为git merge
。git merge
通常会进行合并提交,但git merge --squash
却没有。它不仅仅是它抑制了提交本身(它确实如此),而且更重要的是:当你你手动运行git commit
时你得到的提交,你必须这样做完成壁球&#34;合并&#34;,不是合并提交。这很重要,部分原因是项目#1,但也因为项目#4。git merge
计算要合并的内容的方式是使用提交图 - 与第1项中相同的图。这非常依赖于 3 在图中进行真实的实际合并。正常(或至少更典型)使用git fetch
的方式是使用名为remote 的(或只是&#34;远程&#34;因为遥控器只是一个名称和&#34;命名名称&#34;有点多余)。这里可以使用原始URL,这就是您使用git fetch
的方式。当您使用原始网址时,您的git fetch
必须立即通过git merge
之类的其他命令跟随 4 ,当然还有git pull
为你做的。 (事实上,这就是git pull
最初设计使用的方式,在Git社区普遍发现像origin
这样的命名遥控器在大多数实际用途中都要好得多之前。这种旧方法是然而,为什么 git pull
是如此复杂的解释和使用。)
在任何情况下,正如您所观察到的,合并两个图形会导致git log
跟随两个图形 - 这是按设计工作的; Git 应该来做到这一点。您可以告诉git log
不要这样做,但默认设置是遵循两个图表。
您的第一个pull
运行git merge --squash
,它复制了自合并库提交 5 以来完成的一些工作 - 并且您已将其手动提交为工作树一个定期的,非合并的公共场合。
你的第二个pull
,没有--squash
,复制了一些工作 - 特别是自合并基础提交以来完成的工作,几乎可以肯定已经完成了同样的工作 6 -into yourin work-tree并将其作为真正的合并提交提交。
任何后续 pull
只会将新作品复制到您的工作树中,因为您的分支机构的历史记录已与其他存储库的历史记录,通过此pull
- 和 - 实际合并。那部分是你想要的。 git log
向您展示其他历史记录的事实是您(显然)不想要的部分,但除非您使用其他技术, 7 您将被困在这个:这就是Git的设计用途。
(请注意,随后的--squash
合并或拉动会让您陷入这种不那么伟大的状态&#34;重新扫描所有工作&#34;状态,并且因为任何真正的合并而获得的收益很少已经按照你想要的方式加入了历史。)
1 有点破坏了漂亮的整齐链,git log
首先找出要显示的提交,然后按日期标记对它们进行排序,然后显示它们。这意味着如果日期被意外或故意搞砸了 - 你所看到的并不是发生了什么。为避免这种情况,您可以告诉git log
使用其他排序顺序;最简单的方法是添加--graph
,这会强制拓扑排序,也会绘制实际提交图的文本模式表示。
2 如果历史记录重新加入 - 他们在大多数典型的Git提交图中都会重新加入 - 日志也会重新加入,也就是说,从这一点开始,您只能看到一个提交副本。 Git首先选择要显示的所有提交,由于历史记录分支,可以多次选择任何一个提交,然后显示它们,这表明只提交一次。 (另见脚注1)。
您可以告诉git log
不关注多个历史记录:git log --first-parent
仅跟随&#34; main&#34;历史。您可能希望使用此方法而不是尝试定义更高级的工作流程。如果是这样,你可能也希望尽可能地坚持使用rebase工作流程。
3 特别是,合并必须找到合并基础,合并基础是历史记录连接的点。当你进行壁球&#34;合并&#34;时,你将变化从一个分支混合到另一个分支,但你没有加入他们的历史。这意味着未来git merge
必须再次合并那些相同的更改 ,除非您采取一些额外的操作(即记录真正的合并提交)。
4 不必 立即:git fetch
将其所做的内容写入FETCH_HEAD
,直到下一个git fetch
仍然有效{1}}覆盖它(并且有一个标志来git fetch
追加而不是覆盖)。但是,根据您运行git fetch
的方式,FETCH_HEAD
中的信息可能不太有用,并且在任何情况下,用于下一次合并或rebase的命令都有点复杂,因为您可能需要丢弃标记为not-for-merge
的条目。 pull
命令会为您完成所有这些操作,代价是在获取后立即进行合并或重新绑定,即使看到的内容通常也很好。
5 这假设存在合并基础提交。如果你&#34;分叉&#34;您的存储库来自您现在通过URL提取的存储库(您可能应该使用远程而不是URL),您的历史记录及其历史记录会在某些时候加入,这就是合并基础提交将是。如果这两个存储库完全不相关,那么您将获得被认为过于危险的行为,并且现在默认情况下在Git版本2.9中被禁用。
6 由于合并的定义方式,如果运气好的话,这就是计算成本高昂的无所事事的方式。 (如果没有,它的计算成本仍然很高,但它的作用是破坏你的代码。)原因是合并动作通过做两个差异来工作:git diff $base HEAD
,看看你< / em>已完成,git diff $base $other
,看看他们做了什么。 (此处$base
表示合并基础提交,$other
表示您要合并到分支中的提交。一如既往,HEAD
是您自己的当前分支提示。)你做了第一次壁球合并,你得到了他们的工作,所以 - 假设他们没有做任何新的事情 - 无论如何 - 现在&#34;你做了什么&#34; 包括&#34;他们做了什么&#34;。因此,在经过这两组差异的大量工作之后,Git决定&#34;他们做了什么&#34;已经包含在您的HEAD
提交中,因此它不会为您的HEAD
添加任何新内容。
7 而不是正常的merge-into-mainline方法,你在一个侧支上工作直到它准备就绪,然后合并它,你可以反转合并流。您仍然会创建一个侧分支并继续处理它,直到它准备好,然后您将侧分支压缩到主线(master
或发布分支)。然后 - 这是关键部分 - 一旦侧分支要完成更多的工作,你就会从主线回到侧分支进行真正的合并而不是压缩合并。此合并将形成下一个合并操作的合并基础(无论这是真正的合并,还是壁球 - &#34;合并&#34;)。
然后你必须永远留下这些侧枝。 8 这个特殊的流程模仿你在Mercurial中可以做的事情。
8 如果您愿意,可以模仿Mercurial&#34;关闭&#34;通过为&#34;封闭分支&#34;提供特殊引用或引用名称空间来分支。或&#34;关闭分支&#34;,并将正常分支名称重命名为此名称空间。你也可以使用虚拟合并将这些分支绑定到彼此,如果由于某种原因你想只有一个外部名称让它们保持活着。 Git没有这些操作的工具,但是使用Git的管道命令很容易构建(不过你必须作弊来保存reflog)。
或者,如果您愿意,您可以(强制)完全删除侧分支名称,并将整个分支链丢失到垃圾收集器。但是,除了琐碎的分支外,这似乎是一个坏主意。