我错误地做了100多次修订。我如何将它们转换为常规提交?或者至少要获得每次修改提交都不同的git日志?
如果我现在运行gitk
或git log -p
,一次只能看到所有修订提交的差异。我可以看到所有的修订提交哈希,但只能使用reflog。我还可以看到与git diff amend_hash1 amend_hash2
的区别,但在gitk
或git log -p
中看不到区别。尽管它们在.git/logs/refs/heads/master
和.git/logs/HEAD
我刚运行git commit --amend
100次,每100次更改一次。然后当我运行git commit而不进行修改时,我对所有100个更改都进行了一次大提交。
我发现了如何仅撤消一次修订提交...
答案 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
,依此类推。提交F
和I
对F
或E
不可见(但将显示在G
输出中,因为它不遵循父链)。 / p>
经过100次这样的操作,我用完了提交信,无法吸引所有指向H
的提交的疯狂爱好者,但是您可以想象得到;或者我只能从gitk
到git 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'
复制到新的提交G
。 H
的父级不是 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 log
,gitk
,git commit-tree
:针对作者git commit-tree
,GIT_AUTHOR_NAME
,GIT_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
来看看它在吵什么。