在保持提交树不变的情况下,将整个git分支重设为孤立分支

在保持提交树不变的情况下,将整个git分支重设为孤立分支,git,git-rebase,Git,Git Rebase,我有一个回购协议,其中我有两个分支,master和master old,这是作为孤立分支创建的 现在我想将整个master重设到master old上,但每次提交的树应该保持不变,即master和master old上的每个提交的工作副本在重设基础前后的外观应该完全相同 Current state ------------- A - B - C - D <--- master E - F - G - H <--- master-old Desired state

我有一个回购协议,其中我有两个分支,
master
master old
,这是作为孤立分支创建的

现在我想将整个
master
重设到
master old
上,但每次提交的树应该保持不变,即
master
master old
上的每个提交的工作副本在重设基础前后的外观应该完全相同

Current state
-------------
A - B - C - D     <--- master

E - F - G - H     <--- master-old

Desired state
-------------
E'- F'- G'- H'- A'- B'- C'- D' <--- master
当前状态
-------------

A-B-C-D如果您希望保留与原始提交的
A--B--C--D
系列相关联的树,那么您根本不想重新设置基础。重定基址意味着将提交转换为差异(变更集),然后将这些变更集(一次一个)应用于某个现有的起点,但您只需将附加到
a
的树复制到父级为
H
的新提交
a'
,然后将附加到
B
的树复制到父级为
A'
的新提交
B'
,依此类推

这就是
git过滤器分支
工作良好的地方。运行时:

git filter-branch <filter-list> <branch-name>
(不完全是你想要的,但可能更好)

(要获得复制的
E-F-G-H
链,您还必须将
master old
作为正引用传递,并且由于任何逐位相同的提交都必须与原始提交具有相同的散列ID,因此您必须对提交
E
进行至少一次更改,例如,对于Instant,将提交者tiemstamp更改1秒(笑)

这里值得一提的还有另外两种方法。一种是使用
--commit filter
:这是实际执行新提交的命令。您可以在这里执行任何操作,包括完全忽略某些提交;但是使用所有其他过滤器的原因是为了简化操作,因此在本例中没有理由使用提交根本不过滤

使用
git替换
最后,还有一个问题。
git replace
所做的是创建留在存储库中的新对象,由
refs/replace/
名称空间中的一个特殊名称引用。每当git通过散列ID查看某个对象时,git通常首先检查
refs/replace/
是否存在。如果存在,git就会查看ob该参考点所指向的对象

这意味着您可以构造一个新的Git对象,它与commit
a
非常相似,但稍有不同。稍有不同的是,新的commit对象中存储了一个父哈希ID。父哈希ID是commit
H
的哈希ID(请注意,它与
a
具有相同的树)

现在您有了这个新对象,让我们称它为
A'
——将它粘贴到存储库中,并使
refs/replace/
指向它:

A--B--C--D   <-- master

E--F--G--H   <-- master-old
          \
           A'   <-- refs/replace/deadcabf001...

A--B--C--D您所做的正是您需要做的。如果存在合并冲突,如果已经完成了,这是不可避免的。可能相关:感谢您的详细回答。我真的需要使用您的解决方案才能真正理解它们。
git filter branch
文档实际上有我需要运行的确切命令例如:
git filter branch——父过滤器'sed“s/^\$/-p/“'HEAD
。但是,我不知道为什么它会说“而不是”。这个“graft id”仅仅是因为它们意味着这是新副本的新父级:新构建的分支已经在那一点上被移植了。过滤器分支很有趣(尽管速度很慢)进行试验。始终在真实存储库的副本(备用克隆)上进行试验,以防万一。在进行试验时,您可能希望从一个简单的手动构建的存储库开始,以使其运行更快。
A--B--C--D   <-- master

E--F--G--H   <-- master-old
          \
           A'   <-- refs/replace/deadcabf001...