2

我错误地做了超过 100 次修改提交。我怎样才能将它们转换为通常的提交?或者至少要为每个修改提交获得不同的 git log?gitk如果我运行或git log -p现在,我一次只能看到所有修改提交的区别。我可以看到所有修改提交的哈希值,但只能使用 reflog。我也可以看到区别,git diff amend_hash1 amend_hash2但不是 in gitkor git log -p。尽管它们在.git/logs/refs/heads/master.git/logs/HEAD

我只跑了git commit --amend100 次,每 100 次更改一次。然后,当我在没有修改的情况下运行 git commit 时,我对所有 100 个更改都进行了一次大提交。

我发现如何只撤消一个修改提交......

4

3 回答 3

3

我认为这更能用图片来解释,尽管我的 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像往常一样进行新的提交,除了HF而不是G

       G   [you were here]
      /
...--F--H   <-- branch (HEAD)

然后,Imake ,我们像往常一样制作它,除了IF不是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它的父级(它确实如此)不是)。

这意味着您必须复制错误的提交。让我们首先将其复制HH'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 logrepaired, 或, 时运行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来保存新构建的链。

于 2018-09-24T20:25:54.830 回答
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 并使用先前的 reflog。但是,这并不能保证。例如,在提交和修改之间可能存在签出和拉取。

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
于 2018-09-24T19:14:17.033 回答
0

它们都在进行修改的 repo 的 reflog 中,并且它们是完全普通的提交,因此您可以例如git show查看它们所做的更改以及git cherry-pick它们以获取这些更改等等。查看git reflog文档,并查看例如.git/logs/refs/heads/master,看看它在争论什么。

于 2018-09-24T19:13:41.260 回答