【发布时间】:2016-04-01 08:43:53
【问题描述】:
我已经在远程分支上推送了一个提交,现在我想更改它的内容,所以我尝试了git amend。
如果git status 表示两个分支分别有 1 个和 1 个不同的提交。
现在,如果我使用相同的提交消息推送修改后的提交,那会添加新的提交还是会更改我推送的最后一个提交?
【问题讨论】:
我已经在远程分支上推送了一个提交,现在我想更改它的内容,所以我尝试了git amend。
如果git status 表示两个分支分别有 1 个和 1 个不同的提交。
现在,如果我使用相同的提交消息推送修改后的提交,那会添加新的提交还是会更改我推送的最后一个提交?
【问题讨论】:
git commit --amend,就像git rebase,将创建一个新的提交对象。该对象基于先前存在的提交,但它仍然是一个新的提交,并完全替换了旧的提交。
回顾历史,可能是这样的:
master
↓
* --- * --- * --- A
考虑到 A 是原始提交。如果我们现在修改这个提交,那么我们会得到以下结果:
* --- * --- * --- A
\
--- A'
↑
master
所以我们得到了一个不同的提交对象A',具有不同的哈希值,并且我们所在的分支(这里:master)被更新为指向这个。
现在,如果我们向该视图添加一个远程存储库,并且我们之前将 A 推送到远程,那么它看起来像这样:
origin/master
↓
* --- * --- * --- A
\
--- A'
↑
master
所以远程仍然指向原始提交A,但我们的本地分支指向修改后的A'。这是一个问题,因为我们不能推送 A' 并让 origin/master 指向 A',因为这会从历史记录中删除已经推送的提交 A。
我们可以使用git push --force 执行强制推送以强制Git 更新远程分支并确实从历史记录中删除A。需要注意的是,这将打破每个已经从远程获取A 的人的历史记录。如果其他开发人员拥有A,而现在远程指向A',那么他们就有了必须手动修复的冲突。这通常是一件很痛苦的事情,因此您应该始终遵循一条规则:
永远不要变基(或修改)以前发布的提交。
更好的选择是添加一个新的提交B,它只是对A进行修复:
origin/master
↓
* --- * --- * --- A --- B
↑
master
这样,已经发布的历史仍然兼容新的历史,所以我们可以将B推送到远程而不冲突,每个人都很开心。
【讨论】:
git push --force 覆盖遥控器。或者将该修复推送为提交B。
推送修改后的提交意味着推送不同的提交。
提交的唯一 ID 由其元数据的 SHA-1 哈希组成。哪个元数据?找出方法之一是使用cat-fileplumbing command:
git cat-file -p HEAD
运行此命令后,您将看到一个包含以下字段的列表:
如果这些字段中的任何发生变化,它们的 SHA-1 哈希值也会发生变化,从而授予提交一个完整的新 ID。
这就是为什么如果你修改一个提交——例如通过改变它的message——它将有一个不同于以前的ID。你实际上是rewriting history。
请注意,提交的父级 的 ID 也包含在元数据中。这意味着一旦提交更改了 ID,它的所有后代也将更改 ID,就像多米诺骨牌效应一样。
【讨论】:
一旦你推送到远程,你不应该在本地修改任何现有的提交。
您实际上已经在本地创建了一个新的提交(有一个新的提交 ID)并替换了旧的,但旧的仍然存在于远程。这会导致您看到 1 前和 1 后消息。
要解决此问题,您需要创建一个新分支来保留您的修改,检查出初始分支,重置回您进行修改之前,从远程下拉更改。然后合并到你单独的分支并向上推。
【讨论】:
git pull && git push。尽管如此,OP 并没有指定遥控器是公开的。如果不是,那么只需 git push -f 即可避免合并。