看起来你想修改你的提交图(commit graph),而不修改附加到这些提交的任何树(trees)。
如果你理解git内部工作原理,这个语句就会更有意义。关键在于:
所有的提交都是永久且不可更改的,因为提交(或者实际上Git的四个内部对象之一)的“真名”是其内容的SHA-1加密校验和。这意味着,如果您尝试更改任何内容(或失败的磁盘驱动器更改了某些内容),Git将会抱怨校验和错误,因为每个对象中看到的内容不再与其“真名”匹配。
每个提交还携带一个“tree”对象的SHA-1 ID,该对象充当与该提交相关联的源代码的完整快照。(树给出每个文件或目录的文件名和SHA-1“真名”,子目录由另一个树来表示。在这里细节并不太重要。)
每个提交还列出了其父项的“真名”SHA-1 IDs。因此,给定develop
的尖端提交,Git可以读取该提交并找到其直接父项。读取该父节点(或这些父节点),Git会找到下一个ID(S),并读取其父亲,依此类推。此过程在读取“根”提交时停止,即没有父节点的提交。执行--orphan
检出,然后进行提交会导致一个新的根提交,这无疑是您创建分支的方式。
这些父ID和树ID存储在任何给定的提交中,被称为"指向"Git存储库中的其他对象。 (存储库中只有四种类型的对象。我们已经提到了"提交"和"树",另外两个是"blob"——这是Git保存文件的方式——和"tag"。树指向blob和子树,而"tag"对象用于带注释的标签。)
因此,您想要做的是更改
develop
分支的根提交,以便它现在具有父提交,特别是从当前
master
分支的末尾可达的某个提交。(也许您想要末尾本身,也许您想要像
master〜100
这样的东西:当您进行更改时,此细节仅很重要。)
坏消息是,由于项目#1的原因,您不能完全这样做。
好消息是,您可以使用三种替代方法之一来完成此操作(然后如果需要和需要,可以使移植永久化)。
首先,git有一个称为“移植”的东西。 它们的工作效果不太好,因此git有一个新的更好的东西叫做“替换”。 根据您的git版本,您应该至少拥有一个,几乎肯定两者都有。
这两者都采用了相同的基本思路。在 git 执行其图形遍历,从提交到父级时,你希望在某个点上能让 git 更改其遍历路径。使用 git 梗概,只需指定当有一个 ID 为
<X>
的对象时,它应遍历到父级
<Y>
,这样就可以找到当前
develop
分支上的根提交,并将其直接嫁接到
master
分支的某个提交上。
这些梗概不会被
git clone
复制,并引入其他问题,因此现在 git 有了
git replace
。它在存储库中创建了一个实际对象,允许您检查提交图形及其替换。要在此情况下使用它,您需要为根提交创建替换提交,该提交与现有根提交完全相同,只是具有所需的父级。
如果您想完全重写历史,使得所有用户在经过一次痛苦的重写步骤后都能轻松使用,那么您可以设置一个嫁接点,然后使用
git filter-branch
来复制和替换嫁接点。或者,也许稍微容易一些的方法是指定一个父筛选器(而没有其他筛选器)。有关详细信息,请参见
git filter-branch
文档;它有一个非常相似的示例。(文档建议创建嫁接点更简单,但我认为使用
--parent-filter
更简单;但无论哪种方式,都有一个方便的示例。)
请注意,filter-branch会
复制每个“筛选”提交,并应用一些更改,通过(虚拟)检出该提交,然后应用筛选器,然后从结果中创建一个新的提交。在这种情况下,第一个更改是向根提交添加父ID。第二个更改是filter-branch自动执行的:使跟随不再孤立的根复制点的提交指向根复制。第三个更改与第二个相同,依此类推:
A
...--o
\
A'-B'-C' <-- develop (after filtering)
在这里,提交
A'
与提交
A
完全相同,只是它有一个父ID;提交
B'
与提交
B
完全相同,只是它的父代是
A'
而不是
A
;等等。
(顺便说一下,这里所有的父级箭头都指向左边,但是
filter-branch
从左到右工作。它通过首先枚举要过滤的所有提交,以从右到左的方式获取它们的ID,然后从左到右进行过滤。)
这个现有的SO问答中有更多关于使用嫁接或替换的内容。
我上面确实提到了第三种方法。这种方法仅在
develop
没有现有合并提交时才有效。您可以像
siride建议的那样,将
develop
中的每个提交简单地变基到目标提交上。您需要使用
--root
运行该变基命令,以从独立的
develop
分支的根部复制每个提交。
这样做是因为
git rebase
只是复制提交,就像
git filter-branch
一样。从某种意义上说,这更加费力,因为重新应用差异(使用重复的
git cherry-pick
)而不是仅保留每个复制提交的现有“树”对象的方式来复制提交,但它比
git filter-branch
容易得多。这里的缺点是rebase根本不处理合并提交(好吧,除非您使用
--preserve-merges
,这会使用交互式变基代码)。
git rebase --onto master develop
? - siride