推送后修改提交

Sha*_*hin 13 git commit git-amend

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

过去我是这样做的:

git reset HEAD~1 --soft
git push -f
# correct the spelling
git add .
git commit -m "bug10 fix"
Run Code Online (Sandbox Code Playgroud)

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

我试过:

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

tor*_*rek 36

更改任何提交都是不可能的。这包括在推送之前。知道这一点很重要\xe2\x80\x94你需要知道这git commit --amend是一个谎言的原因\xe2\x80\x94是git commit --amend本地所做的事情,可以在将提交推送到另一个Git存储库时在这里完成。

\n

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

\n

您可以随时执行此操作。你只需要知道每个提交的编号为\xe2\x80\x94 每个提交都有一个唯一的编号,即它的哈希 ID \xe2\x80\x94 并且 Git以一种向后(甚至可能是backassward )的方式查找提交,通过使用分支名称或其他名称为您找到最近的提交,然后从那里向后工作。

\n

想象一个简单的提交链,以您最近的提交结束,其哈希值是一些大而丑陋的随机哈希 ID,我们将其称为H。以下是Git如何查看\xe2\x80\x94 并查找\xe2\x80\x94 这些提交:

\n
... <-F <-G <-H   <--your-branch\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

CommitG是一次提交,由一些看起来又大又难看的随机哈希 ID 进行编号,并且具有快照和元数据,因此向后指向更早的 commit F。这是一次提交,因此它被编号并且具有相同类型的内容并且再次指向向后,依此类推。

\n

H现在,如果您在 commit和 run中有一个微小的拼写错误git commit --amend,Git 所做的就是进行一个新的 commit。新的提交与其他完全相同,H但有两个方面不同:

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

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

\n
            H   ???\n           /\n... <-F <-G <-I   <--your-branch\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
...--F--G--H   <--their-branch\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
          H   <--their-branch\n         /\n...--F--G\n         \\\n          I\n
Run Code Online (Sandbox Code Playgroud)\n

然后你让你的 Git 要求他们的 Git 设置他们的分支名称来指向I

\n

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

\n

您可以使用:

\n
git push --force\n
Run Code Online (Sandbox Code Playgroud)\n

或者:

\n
git push --force-with-lease\n
Run Code Online (Sandbox Code Playgroud)\n

向他们发送一条命令,而不是礼貌的请求,告诉他们他们绝对应该将自己的名字指向I(常规--force),或者您认为他们的名字指向H现在,如果是这样,他们应该将其指向I--force-with-lease)。如果他们会从您\xe2\x80\x94大多数托管站点(例如 GitHub 和 Bitbucket)获取此类命令,GitLab 会在 Git 上添加奇特的配置来控制这些东西\xe2\x80\x94,那么这毕竟可以让您替换HI

\n

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

\n

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

\n
          H--J--K   <--their-branch\n         /\n...--F--G\n         \\\n          I\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

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

\n

  • @JiříBaum:是的,这就是为什么变基或修改的一般规则(本质上是相同的)是所有可能受此影响的人都应该在完成之前同意完成此操作。 (3认同)
  • 更复杂的是 - 如果其他人已将“H”拉入他们的本地存储库,并且可能开始在其基础上工作,您将不得不向他们所有人发送电子邮件并要求他们将他们的工作重新建立在“I”上。如果你这样做是为了一个可以在另一个提交中修复的简单拼写错误,他们会非常恼火......所以只有在基本上没有其他人使用该存储库,或者问题是一个重大问题需要这样做时才这样做一种破坏。 (2认同)