如何将使用'git replace'编辑的历史记录推送到远程

时间:2017-05-17 15:01:47

标签: git version-control merge bitbucket

我有一个遥控器,其历史记录如下:

enter image description here

正如你所看到的,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-branchgit push --force-with-lease origin main-branch,但我得到了相同的结果:一切都是最新的。

如何正确合并这些历史记录更改并将其推送到BitBucket而无需重新创建整个仓库?

3 个答案:

答案 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。

将替换从一个Git转移到另一个

通常,您可以使用以下方法推送此类对象:

refs/replace/

(或者特别列出您要推送的替换参考号)。要获取这些对象:

git push <repository> 'refs/replace/*:refs/replace/*'

(您可以将此fetch refspec添加到每个克隆中的git fetch <repository> 'refs/replace/*:refs/replace/*' 配置中。使用fetchgit 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 da4398dcommit 6ebd1cacommit 72470aaJeff King (peff)(2018年7月18日)。
(由Junio C Hamano -- gitster --commit 1689c22中合并,2018年8月15日)

  

添加core.usereplacerefs配置选项

     

我们已经可以使用命令行选项或环境变量来禁用替换引用,但是这些替换引用普遍适用。让我们添加一个配置选项来执行相同的操作。

     

提出了一个问题,为什么人们可能要普遍这样做。答案是replace refs违反了对象的不变性。例如,如果您想   缓存提交XYZ与它的父对象之间的差异,然后从理论上讲永远不变。哈希XYZ代表总状态。
  但是替换refs违反了这一点;增加一个新的裁判可能会创建一个全新的差异。

     

明显的“如果不舒服就不要做”的答案是,如果您要进行这种缓存,则不要创建替换引用。
  但是对于托管任意存储库的站点,他们可能希望允许用户彼此共享替换引用,但实际上并不尊重站点上的替换引用(因为缓存比替换功能更重要)。