推送后修改提交

amending a commit after it has been pushed

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

过去我做过:

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 --amend 是一个谎言的原因——是 git commit --amend 在本地做的事情, 可以 在这里做将提交推送到另一个 Git 存储库。

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

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

想象一个简单的提交链,以您最近的提交结束,其哈希是一些丑陋的 运行dom-looking 哈希 ID,我们将其称为 H。以下是 Git 查看和发现这些提交的方式:

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

您的 b运行ch 名称“指向”(包含哈希 ID)提交 H。提交 H 本身包含早期提交 G 的哈希 ID。 (提交 H 还包含每个文件的完整快照,尽管我们在这里根本不会看这个。)

提交 G,作为一个提交,由一些丑陋的 运行dom 外观的大哈希 ID 编号,并且具有快照和元数据,因此向后指向更早的提交 F。这是一个提交,所以它被编号并且具有相同类型的东西并且再次向后指向,依此类推。

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

  • 它有一个不同的哈希ID,所以我们称它为I;和
  • 已更正拼写错误。

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

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

Git 更新您的 b运行ch name 以指向新提交 I。这有效地“从 b运行ch 开始 H”,因为 Git finds 通过从末尾开始并向后工作来提交。

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

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

如果你做出新的提交 I 这是一个新的和改进的 替代 H 并且只需将它与常规 git push,他们将提交并暂时将其放入他们的存储库:

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

然后你让你的 Git 要求他们的 Git 设置 他们的 b运行ch 名称指向 I 也是

问题出在这里,因为当您这样做时,他们会检查:如果我更新我的 b运行ch 名称以指向 I,我是否会丢失任何commits off the end? 当然,他们 做: 他们在 b运行ch 的末尾失去了 commit 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 上添加的奇特配置来控制这些东西——那么这可以让你替换毕竟 HI

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

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

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

因此你的命令他们将他们的 b运行ch 设置为指向 I 不仅会丢弃你的提交 H,还会丢弃另外两个额外的提交 J-K, 关闭他们的 b运行ch.

使用 git push --force-with-lease 向他们发送您的新提交 I,同时向他们发送您认为他们拥有的哈希 ID H 作为他们在 b运行 上的最终提交通道如果你的信念是错误的,你的条件命令——他们必须更改他们的名字以指向 I——会被拒绝,说你的 Git 现在已经过时了,你需要 运行git fetch先.

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