如何撤消多个“ git commit --amend”或获取每个修订提交的区别?

时间:2018-09-24 19:06:49

标签: git commit undo amend

我错误地做了100多次修订。我如何将它们转换为常规提交?或者至少要获得每次修改提交都不同的git日志? 如果我现在运行gitkgit log -p,一次只能看到所有修订提交的差异。我可以看到所有的修订提交哈希,但只能使用reflog。我还可以看到与git diff amend_hash1 amend_hash2的区别,但在gitkgit log -p中看不到区别。尽管它们在.git/logs/refs/heads/master.git/logs/HEAD

中正确链接,但它们跳过了这些修改。

我刚运行git commit --amend 100次,每100次更改一次。然后当我运行git commit而不进行修改时,我对所有100个更改都进行了一次大提交。

我发现了如何仅撤消一次修订提交...

3 个答案:

答案 0 :(得分:3)

我认为用图片更能解释这一点,尽管我的ASCII艺术能力仅在几个--amends之后就用完了。诀窍是要认识到git commit --amend与没有git commit的{​​{1}}相比,是要更改存储在新提交中的父哈希

常规--amend将索引内容冻结到树中,并使用新树(您是作者和提交者),日志消息以及当前提交来使用新树进行新提交。新提交的父项:

git commit

然后在整理图形之后,进行新的提交...--F--G <-- [you were here] \ H <-- branch (HEAD) [you are here now]

I

和新提交J:

...--F--G--H--I   <-- branch (HEAD)

以此类推。

但是,使用...--F--G--H--I--J <-- branch (HEAD) ,我们像往常一样进行新的提交git commit --amend ,除了H parent 是{ {1}},而不是H

F

然后,制作G,我们照常制作 ,除了 G [you were here] / ...--F--H <-- branch (HEAD) parent I而不是{ {1}}:

I

以此类推。

如果您想象此时正在运行F,而提交H指向提交 G [you were here] / ...--F--H [you were here too] \ I <-- branch (HEAD) ,则您将看到提交git log,然后提交{{1} },然后I,依此类推。提交FIFE不可见(但将显示在G输出中,因为它不遵循父链)。 / p>

经过100次这样的操作,我用完了提交信,无法吸引所有指向H的提交的疯狂爱好者,但是您可以想象得到;或者我只能从gitkgit log绘制9个这样的提交:

git reflog

这些不同的提交中的每一个都有您想要的 tree message 。除了F本身以外,所有这些提交都有什么问题,那就是它具有错误的 parent:您希望G拥有O作为它的父级(确实如此),但随后您希望 GH | I |/ J ...--F==K |\ L | M ON G作为其父级(事实并非如此)。

这意味着您必须复制错误的提交。首先,将G复制到以F为父节点的H,但使用相同的 tree message 以及其他元数据为G

H

现在,我们需要将H'复制到新的提交GH的父级不是 H' / GH | I |/ J ...--F==K |\ L | M ON ,而是I

I'

我们将I'重复到G,使用H'作为 H'-I' / GH | I |/ J ...--F==K |\ L | M ON 的父对象,依此类推,直到我们将每个“错误”的提交复制到“正确的” “一个。然后,我们可以设置一个分支名称以指向最后一个这样复制的提交:

J

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'使用Shell脚本循环轻松进行每个提交。如果要保留该元数据,则比较困难:在每次调用L'之前,需要设置一系列Git环境变量,设置为:

  • git loggitkgit commit-tree:针对作者
  • git commit-treeGIT_AUTHOR_NAMEGIT_AUTHOR_EMAIL:相似,但对于提交者

可以使用GIT_AUTHOR_DATE或类似的方法从错误的提交中直接复制日志消息,以截取直到第一个空白行(包括第一行)的所有内容(请注意,这会丢弃任何i18n编码数据,并且{{1} }对于提交消息中未终止的最终文本行,可能表现不佳,但可以容忍;否则,请从GIT_COMMITTER_NAME中提取相关代码。

使用GIT_COMMITTER_EMAIL以正确的顺序(要复制的最旧,最后要复制的=最新)获取要复制的所有提交的哈希ID。将它们放在文件中,每行一个条目。然后,以下 unested shell脚本可能就足够了:

GIT_COMMITTER_DATE

这将创建一个新的分支名称sed,以容纳新建的链。

答案 1 :(得分:1)

  

这些提交在gitk中显示为一次提交– l0pan

我怀疑实际发生的事情是您压缩了100次提交,可能是git merge --squash。真幸运,这比实际修改100次提交要容易得多(这是非常不寻常的)。我们需要找到分支的原始头。

如果幸运的话,ORIG_HEAD仍设置为分支的原始标题。检查git log ORIG_HEAD

否则,在进行壁球合并之前,请使用git reflog查找分支的尖端。您正在寻找类似这样的东西...

a83ad6b (master) HEAD@{1}: commit: Squashed commit of the following:
78c06ed HEAD@{2}: checkout: moving from feature to master

a83ad6b是新的压缩提交。 78c06ed是分支的原始未压缩尖端。

找到原始提交后,将分支移回原始提交。

git branch -f <commit id>

如果您确实修改了100次提交,请按以下步骤找到它们。

您将使用git reflog查找原始的未修改的提交。您正在寻找类似这样的东西...

6493a4c HEAD@{2}: commit (amend): The log message
a2a99ea HEAD@{3}: commit: The log message

a2a99ea是未经修改的原始提交。 6493a4c是新的修订提交。

通常,您应该能够对commit (amend)进行grep并使用具有相同提交消息的以前的引用日志。但是,不能保证。例如,在提交和修改之间可能会有检出和提取。

0742a3a HEAD@{111}: commit (amend): chore: Add a simple monitoring process.
b71620d HEAD@{112}: pull: checkout e29402b1fda83664cf17782b1a34e2bb4a77d44f: returning to refs/heads/master
b71620d HEAD@{113}: pull: checkout e29402b1fda83664cf17782b1a34e2bb4a77d44f: chore: Add a simple monitoring process.
e29402b HEAD@{114}: pull: checkout e29402b1fda83664cf17782b1a34e2bb4a77d44f
fab188c HEAD@{115}: commit: chore: Add a simple monitoring process.

否则修改可能会更改提交消息。

6493a4c HEAD@{2}: commit (amend): A different log message
a2a99ea HEAD@{3}: commit: The log message

答案 2 :(得分:0)

它们全都在做修改的仓库中,它们完全是普通的提交,因此您可以例如git show让他们查看所做的更改,git cherry-pick让他们抓住这些更改,依此类推。请参阅git reflog文档,然后例如.git/logs/refs/heads/master来看看它在吵什么。