我有一个遥控器,其历史记录如下:
正如你所看到的,O和P是合并提交,它们都关闭了它们的旧分支,所以现在只有一个分支。
我想将C-D-E-G-J-K-L-N压缩成一个提交,将F-H-I-M压缩到另一个提交,因为它们只是很小的提交,使历史混乱。
在当地,我设法使用the answer by John O'M. to this question中描述的方法压缩C-D-E-G-J-K-L-N,如下所示:
git checkout -b squashing-1 N
git reset --soft C~
git commit -m "Squashed history"
git replace N [ID_of_the_commit_i_just_made]
这是有效的,来自主分支的本地git log
正确报告Q,P,O,X,M,I等(X是新的压缩提交)。
从这里接下来的步骤是(1)检查主分支并合并更改,(2)删除临时本地分支,然后(3)将更改推送到远程仓库。但是(1)和(3)报告已经是最新的或一切都是最新的因为树没有实际的变化,这正是所有这些。
我也尝试使用git push --force origin main-branch
和git push --force-with-lease origin main-branch
,但我得到了相同的结果:一切都是最新的。
如何正确合并这些历史记录更改并将其推送到BitBucket而无需重新创建整个仓库?
答案 0 :(得分:5)
您基本上可以选择:您是希望让每个人都使用替换引用,还是更喜欢重写整个存储库并让每个人都有一个标志日,在此期间他们从“旧存储库”切换到“新的存储库”?这两种方法都不是特别有趣或有利可图。 : - )
git replace
做的是将新对象添加到Git存储库中,并在refs/replace/
名称空间中为其命名。此名称空间中的名称是新对象替换的对象的哈希ID。例如,如果您要替换提交1234567...
,则新对象的名称(其ID不是1234567...
- 具体而言,假设它是fedcba9...
)是{{1} }。
Git的 rest ,在查找对象时,首先检查是否有refs/replace/1234567...
个对象。如果是这样(并且未禁用替换),则Git的其余部分将返回refs/replace/<hash-id>
名称指向的对象,而不是实际对象。所以当Git的其他部分读取一些提交“我的父提交是refs/replace/
”时,Git的其他部分去查找1234567...
,看到1234567...
存在,并返回对象而是refs/replace/1234567...
。然后,您会看到替换。
如果你不有引用fedcba9...
,你的Git永远不会交换替换对象。 (无论你是否拥有替换对象,都是如此。引用本身导致替换发生。引用保证你拥有对象。)< / p>
因此,对于某些其他 Git执行相同的替换过程,您必须将refs/replace/1234567...
引用传递给其他Git。
通常,您可以使用以下方法推送此类对象:
refs/replace/
(或者特别列出您要推送的替换参考号)。要获取这些对象:
git push <repository> 'refs/replace/*:refs/replace/*'
(您可以将此fetch refspec添加到每个克隆中的git fetch <repository> 'refs/replace/*:refs/replace/*'
配置中。使用fetch
或git fetch
将自动获取所有推送的新替换对象。推送仍然是一个痛苦当然,必须在每个新克隆上重复这一步骤。)
请注意,这里的refspec都没有设置强制标志。如果发生这样的事情,是否要强制覆盖现有的git fetch <repository>
引用,取决于您。
或者,一旦你有替换,你就可以运行一个存储库复制操作 - 我的意思是一个提交提交副本,而不是像refs/replace/
这样的快速拷贝 - 比如{{1 }}。如果在不禁用替换的情况下运行此复制操作,则替换的对象不复制;相反,他们的替换被复制。因此:
git clone --mirror
在复制的存储库中永远存在“粘合替换”的副作用。然后,您可以丢弃所有原始引用和所有替换引用。 (执行此操作的简单方法是克隆已过滤的存储库。)
当然,由于这是一个新的不同的存储库,它 与原始存储库或其任何克隆不兼容。但它不再需要仔细协调git filter-branch
名称空间(因为它不再有任何替换对象!)。
答案 1 :(得分:1)
从这里开始,接下来的步骤是(1)检查主分支并合并更改,(2)删除临时本地分支,然后(3)将更改推送到远程仓库。
似乎你误解了git replace
真正做了什么。没有什么可以合并,因为git replace
的真实历史不会以任何方式改变。更确切地说,replace
向一边说明&#34;默认情况下,在浏览历史记录时,如果找到此对象,请替换此对象&#34;。你实际上仍然可以看到真实的历史,例如git --no-replace-objects log
。
因此replace
会创建重写历史记录的幻觉。因为它不是真正的重写,因此不能创建一个&#34;上游的rebase&#34;其他开发者的情况,这很酷。 OTOH它不能被信任作为从repo中擦除敏感数据的一种方式,因为重写真的只是一种错觉。从git命令获得的输出可能会产生误导,因为它可能意味着&#34;真实对象&#34; SHA ID与&#34;替换对象相关联&#34;内容(实际上它基本上确定所述内容不会散列到所述SHA)。
如果你决定继续与原籍人分享替代品,你 需要做什么
git push origin refs/replace/*
请注意,有一些已知的错误/怪癖,文档表明可能存在未知的错误/怪癖。
答案 2 :(得分:0)
注意:您将需要确保服务器允许它:Git 2.19(2018年第三季度)已添加了一个新的配置变量core.usereplacerefs
,主要是为了帮助希望忽略该服务器的服务器安装。
完全替换机制。
请参见commit da4398d的commit 6ebd1ca,commit 72470aa,Jeff King (peff
)(2018年7月18日)。
(由Junio C Hamano -- gitster
--在commit 1689c22中合并,2018年8月15日)
添加
core.usereplacerefs
配置选项我们已经可以使用命令行选项或环境变量来禁用替换引用,但是这些替换引用普遍适用。让我们添加一个配置选项来执行相同的操作。
提出了一个问题,为什么人们可能要普遍这样做。答案是replace refs违反了对象的不变性。例如,如果您想 缓存提交XYZ与它的父对象之间的差异,然后从理论上讲永远不变。哈希XYZ代表总状态。
但是替换refs违反了这一点;增加一个新的裁判可能会创建一个全新的差异。明显的“如果不舒服就不要做”的答案是,如果您要进行这种缓存,则不要创建替换引用。
但是对于托管任意存储库的站点,他们可能希望允许用户彼此共享替换引用,但实际上并不尊重站点上的替换引用(因为缓存比替换功能更重要)。