首先,附注:您已经向前绘制了存储库。 Git 是倒着做的。放置根提交last,即在左边,并且latest commit first,即右侧:
C1 <-C2 <-C3 <-C4 <-C5 <--branchname
分支名称始终保存分支的哈希 IDlatest犯罪。这就是 Git 知道是哪一个的方式is最新的。 Git 使用C5
找到C4
,因为提交C5
本身有一个唯一的哈希ID,contains提交的唯一哈希IDC4
。同时C4
保存哈希 IDC3
, 等等;C2
保存唯一的哈希 IDC1
, and C1
is根提交because它内部没有父哈希 ID。
其次,实际上没有任何提交可以更改。所以你在这里要做的是not“更改提交C1
“——你和 Git 都无法做到这一点——而是进行新的提交,C1'
or C21
或者我们想怎么称呼它。在这个新的提交之前,我们需要另一个新的提交,CX
,这是根提交并包含所需的内容。
You can使用中的方法安东尼·哈奇金斯的回答 https://stackoverflow.com/a/9736098/1256452,但现在,要做到这一点的方法是从git checkout --orphan
(而不是棘手的符号引用命令):
git checkout --orphan newroot
git rm -rf --cached . # same as before
<arrange work tree as desired> # same as before
git add . # or git add each file, same as before
git commit # same as before
这就是创建提交的操作序列CX
,这样我们就有:
C1--C2--C3--C4--C5 <-- master
CX <-- newroot
现在我们到了最有趣的部分。使用git rebase
是错误的方法becauserebase 是通过挑选来工作的。这变成了提交C1
进入一个请求add每个文件——提交C1
,作为根提交,与空树 https://stackoverflow.com/q/9765453/1256452查看发生了什么变化 - 正如您所看到的,这会导致您添加的每个文件都发生添加/添加冲突C1
那已经在CX
.
您必须在这里回答的问题(我无法为您回答)是:What content你想要在你的新副本中C1
那就像C1
除了它有CX
作为它的父级?也就是说,最终我们将得到:
C1--C2--C3--C4--C5 [abandoned]
CX--C1'-C2'-C3'-C4-C5' <-- master
在我们的存储库中。如果我跑git checkout
在哈希 ID 上C1'
,我应该看到相同内容就像我跑一样git checkout
在哈希 ID 上C1
?两次提交将具有不同的哈希 ID,但具有相同的作者和提交者以及时间戳和日志消息等。如果他们应该有相同的tree(快照)也是如此,现在的工作非常简单,使用git replace
and git filter-branch
。这是基本食谱:
git replace --graft <hash-of-C1> newroot
(此时你可以运行git log
以确保它看起来正确)。然后运行:
git filter-branch -- master
(假设您只想要可从以下位置访问的提交master
复制并参考master
更新)。无操作过滤器分支(未指定过滤器)以正确的(即向前,向后到 Git)顺序复制每个可到达的提交,复制C1
首先,然后C2
等等,但是——最重要的是——遵守更换指令。所以现在我们有这个:
C1--C2--C3--C4--C5 refs/original/refs/heads/master
CX <-- refs/replace/<hash-of-C1>
\
C1'-C2'-C3'-C4-C5' <-- master
如果这是期望的最终结果,我们现在只需删除refs/original/
名称,以及不再有用的refs/replace/<hash-of-C1>
name:
git update-ref -d refs/original/refs/heads/master
git update-ref -d refs/replace/<hash-of-C1>
Your history is now rewritten. All old clones must be destroyed (or at least, you should stop using them) because the hash IDs in them are those of the original commits, that you don't want to come back into your life to make that life miserable. The abandoned C1-through-C5 in your updated clone are harmless: they can be left there, in their abandoned state, until Git's Grim Reaper Collector, git gc
, gets around to garbage-collecting them.