【问题标题】:amending a commit after it has been pushed [duplicate]在提交后修改提交[重复]
【发布时间】:2021-12-31 04:22:34
【问题描述】:

在我最近提交到已推送的存储库中时,我注意到我在文件中拼错了一个单词,现在想更改它。但是,我不想创建一个全新的提交。有没有办法修改我的最新提交并将其推送到我的存储库中,而无需重置、删除和推送?

过去我做过:

git reset HEAD~1 --soft
git push -f
# correct the spelling
git add .
git commit -m "bug10 fix"

有没有办法更正文件的拼写,修改我最近的提交并推送而无需重置,强制删除,然后重新提交?

我试过了:

# fixed the file
git add .
git commit --amend --no-edit
git push
 ! [rejected]        Nov21 -> Nov21 (non-fast-forward)

【问题讨论】:

    标签: git commit git-amend


    【解决方案1】:

    不可能更改 任何 提交。这包括在它被推送之前。了解这一点很重要——你需要知道 git commit --amend 是一个谎言的原因——是 git commit --amend 在本地所做的,可以在将提交推送到另一个 Git 存储库时在此处完成.

    git commit --amend 的(小)谎言是它根本没有更改提交。它只是似乎这样做。它真正做的是进行新的和改进的替换提交

    您可以随时执行此操作。你只需要知道每个提交都有编号——每个提交都有一个唯一的编号,这是它的hash ID——并且Git finds 以一种向后(甚至可能是 backassward)的方式提交,通过 分支名称 或其他名称为您定位 最近的 提交,然后从那里向后工作。

    想象一个简单的提交链,以您最近的提交结束,其哈希是一些看起来很丑的随机哈希 ID,我们将称之为 H。以下是 Git 如何查看和发现这些提交:

    ... <-F <-G <-H   <--your-branch
    

    您的分支名称“指向”(包含哈希 ID)提交 H。提交H 本身包含先前提交G 的哈希ID。 (提交H 还包含每个文件的完整快照,尽管我们不会在这里查看。)

    提交G,作为一个提交,由一些看起来很丑的随机哈希ID编号,并具有快照和元数据,因此向后指向更早的提交F。那是一个提交,所以它被编号并且具有相同类型的东西并且再次向后指向,等等。

    现在,如果您在提交 H 中有一个小错字并运行 git commit --amend,Git 所做的就是创建一个新的提交。新的提交与H 完全一样,除了两个方面:

    • 它有不同的哈希ID,所以我们称它为I;和
    • 已纠正错字。

    Commit H 仍然存在于您的存储库中。它只是不再使用了。 H 仍然指向 G,但新提交 I 也是如此:

                H   ???
               /
    ... <-F <-G <-I   <--your-branch
    

    Git 更新您的 分支名称 以指向新的提交 I。这有效地“将H 踢出分支”,因为 Git finds 通过从末尾开始并向后工作来提交。

    现在,如果您运行 git push,您所做的是将提交 H 发送到另一个 Git 存储库,然后让他们更新一个他们的分支名称— 可能与您在存储库中使用的名称相同 — 指向现在共享的提交:

    ...--F--G--H   <--their-branch
    

    如果你做出一个新的提交 I 这是一个新的和改进的替换 H 并且只需将它与常规的git push 一起发送给他们,他们就会接受这个提交并将其暂时放入他们的存储库中:

              H   <--their-branch
             /
    ...--F--G
             \
              I
    

    然后您的 Git 要求他们的 Git 将 他们的 分支名称也设置为指向 I

    问题就在这里,因为当你这样做时,他们会检查:如果我将分支名称更新为指向 I,我是否会丢失任何提交? 当然他们这样做: 他们在分支末尾丢失了提交H。这就是你希望他们做的事——毕竟这就是 你的Git 对 你的git commit --amend 所做的事——但 他们没有不知道。他们现在只知道他们确实有H,而你现在要求他们放弃它。

    你可以使用:

    git push --force
    

    或:

    git push --force-with-lease
    

    向他们发送命令,而不是礼貌的请求,告诉他们绝对应该将他们的名字指向I(常规--force) ,或者你认为他们的名字现在指向H,如果是这样,他们应该让它指向I而不是(--force-with-lease)。如果他们会从你那里获取这样的命令——大多数托管网站,比如 GitHub、Bitbucket 和 GitLab 都有他们在 Git 上添加的精美配置来控制这些东西——那么这让你毕竟可以用 I 替换 H

    为什么你可能会使用--force-with-lease

    请注意,如果托管网站正在接受来自其他人git push请求,他们现在可能已经:

              H--J--K   <--their-branch
             /
    ...--F--G
             \
              I
    

    因此,您将分支设置为指向 I 的命令不仅会删除您的提交 H,还会删除其他两个额外的提交 J-K,从其分支的末尾。

    使用git push --force-with-lease 将您的新提交I 发送给他们,但也会将您认为他们在其分支上的最终提交的哈希ID H 发送给他们。如果您的信念是错误的,那么您的条件命令(即他们必须更改名称以指向 I)会收到拒绝,说您的 Git 现在已过时,您需要先运行 git fetch

    如果这种情况不能发生,您就不需要--force-with-lease。非常旧的 Git 版本也没有 --force-with-lease,但所有现代版本都有,即使没有必要,保持安全检查通常是最明智的,因为“习惯”可能会变成“坏习惯”习惯”随着时间的推移。

    【讨论】:

    • 非常感谢您的详细回复!
    • 更复杂的情况 - 如果其他人已将 H 拉入他们的本地存储库,并且可能开始在此基础上工作,您将不得不向他们所有人发送电子邮件并要求他们将他们的工作重新基于 @ 987654379@。如果你这样做是因为一个简单的错字可能已经在另一个提交中修复了,他们会非常恼火......所以只有在基本上没有其他人使用 repo 时才这样做,或者如果问题是一个需要这样做的主要问题一种破坏。
    • @JiříBaum:是的,这就是为什么 rebase 或 amend 的一般规则(基本相同)是所有可能受此影响的人都应该同意在完成之前完成此操作.
    猜你喜欢
    • 2011-04-25
    • 2020-04-29
    • 2021-01-11
    • 2013-06-15
    • 2013-06-24
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    相关资源
    最近更新 更多