我认为这更能用图片来解释,尽管我的 ASCII-art 能力在几个--amends. 诀窍是要意识到git commit --amend,与git commit没有相比--amend,所做的是更改存储在新提交中的父哈希。
法线git commit将索引内容冻结到树中并使用新树进行新提交,您作为作者和提交者,您的日志消息和当前提交作为新提交的父级:
...--F--G <-- [you were here]
\
H <-- branch (HEAD) [you are here now]
然后我们I在理顺绘图后进行新的提交:
...--F--G--H--I <-- branch (HEAD)
和新的提交 J:
...--F--G--H--I--J <-- branch (HEAD)
等等。
但是,使用git commit --amend,我们H像往常一样进行新的提交,除了父级H是F而不是G:
G [you were here]
/
...--F--H <-- branch (HEAD)
然后,Imake ,我们像往常一样制作它,除了父级是I而F不是H:
G [you were here]
/
...--F--H [you were here too]
\
I <-- branch (HEAD)
等等。
如果你想象git log在这一点上运行——commitI指向Fcommit——你会看到 commit I,然后是 commit F,然后E是 ,依此类推。提交G并且H将对gitkor不可见git log(但会显示在git reflog输出中,因为它不遵循父链)。
在 100 次这样的操作之后,我已经用完了提交信,并且不可能吸引所有提交的疯狂粉丝都指向F,但你可以想象它们;G或者我可以通过以下方式仅绘制 9 个这样的提交O:
GH
| I
|/ J
...--F==K
|\ L
| M
ON
这些不同的提交中的每一个都有您想要的树和消息。除了它自己之外,这些提交中的每一个都有什么问题,G那就是它有错误的父级:你想G拥有F它的父级(它确实如此),但是你想H拥有G它的父级(它确实如此)不是)。
这意味着您必须复制错误的提交。让我们首先将其复制H到H'它G的父级,但否则使用相同的树和消息以及其他元数据H:
H'
/
GH
| I
|/ J
...--F==K
|\ L
| M
ON
现在我们需要复制I到一个新的提交I'。I'is not Gbut rather的父级H':
H'-I'
/
GH
| I
|/ J
...--F==K
|\ L
| M
ON
我们重复Jto J',使用I'作为 的父级J',依此类推,直到我们将每个“错误”提交复制到“正确”提交。然后我们可以设置一个分支名称来指向最后一个这样的复制提交:
H'-I'-J'-K'-L'-M'-N'-O' <-- repaired
/
GH
| I
|/ J
...--F==K
|\ L
| M
ON
git log在repaired, 或, 时运行gitk --all现在将显示提交N'导致返回到M'返回L'等等。请记住,git log(and gitk) 向后跟随父链接,根本不查看 reflog。
如果您愿意让一些元数据(作者和提交者姓名、电子邮件和时间戳)被破坏,使用git commit-tree. git commit-tree如果您想保留该元数据,那就更难了:您需要在每次调用之前设置一系列 Git 环境变量,设置:
GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE: 对于作者
GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL, GIT_COMMITTER_DATE: 类似但对于提交者
日志消息可以直接从不正确的提交中复制,使用sed或类似的方法来切断所有内容,包括第一个空行(请注意,这会丢弃任何 i18n 编码数据,并且sed可能在提交消息中未终止的最终文本行中表现不佳,但这些可能是可以容忍的;如果不是,请从git filter-branch) 中提取相关代码。
用于git reflog以正确的顺序获取要复制的所有提交的哈希 ID(最旧的先复制,最后复制的 = 最新的最后)。将它们放在一个文件中,每行一个条目。那么以下未经测试的shell 脚本可能就足够了:
parent=$hash # hash ID of commit G, onto which new chain will be built
while read tocopy; do
tree=$(git rev-parse $tocopy^{tree})
parent=$(git cat-file -p $tocopy | sed '1,/^$/d' |
git commit-tree -p $parent -t $tree)
done < $file_of_commits
git branch repaired $parent
这将创建一个新的分支名称repaired来保存新构建的链。