如果我推送修改后的提交,它会创建一个新的提交吗?
If I push an amended commit will it create a new commit?
我已经在远程分支上推送了一个提交,现在我想更改它的内容,所以我尝试了 git amend
。
If do git status
它表示两个分支分别有 1 和 1 个不同的提交。
现在,如果我使用相同的提交消息推送修改后的提交,会添加一个新的提交还是会更改我推送的最后一次提交?
一旦你推送到远程,你不应该在本地修改任何现有的提交。
您实际上已经在本地创建了一个新的提交(有一个新的提交 ID)并替换了旧的,但旧的仍然存在于远程。这会导致您看到 1 ahead 和 1 behind 消息。
要解决这个问题,您需要创建一个新分支来保留您的修改,检查初始分支,重置回修改前的状态,从远程拉下更改。然后合并到您的单独分支并向上推。
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
到远程,大家都很高兴。
tl;博士
推送修改后的提交意味着推送 不同的 提交。
提交 ID 剖析
提交的唯一 ID 由其元数据的 SHA-1 哈希值组成。哪个元数据?找出方法之一是使用 cat-file
plumbing command:
git cat-file -p HEAD
执行 运行 这个命令后,您将看到一个包含以下字段的列表:
- 树
- Parent
- 作者
- 提交者
- 留言
如果这些字段中的 任何 发生变化,它们的 SHA-1 哈希值也会发生变化,从而为提交授予一个完整的 新 ID。
修正就是改写历史
这就是为什么如果你修改一个提交——例如通过改变它的消息——它将有一个不同的ID之前。你实际上是 rewriting history.
请注意,提交的 parent 的 ID 也包含在元数据中。这意味着一旦提交更改了 ID,它的所有后代也将像多米诺骨牌效应一样更改 ID。
我已经在远程分支上推送了一个提交,现在我想更改它的内容,所以我尝试了 git amend
。
If do git status
它表示两个分支分别有 1 和 1 个不同的提交。
现在,如果我使用相同的提交消息推送修改后的提交,会添加一个新的提交还是会更改我推送的最后一次提交?
一旦你推送到远程,你不应该在本地修改任何现有的提交。
您实际上已经在本地创建了一个新的提交(有一个新的提交 ID)并替换了旧的,但旧的仍然存在于远程。这会导致您看到 1 ahead 和 1 behind 消息。
要解决这个问题,您需要创建一个新分支来保留您的修改,检查初始分支,重置回修改前的状态,从远程拉下更改。然后合并到您的单独分支并向上推。
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
到远程,大家都很高兴。
tl;博士
推送修改后的提交意味着推送 不同的 提交。
提交 ID 剖析
提交的唯一 ID 由其元数据的 SHA-1 哈希值组成。哪个元数据?找出方法之一是使用 cat-file
plumbing command:
git cat-file -p HEAD
执行 运行 这个命令后,您将看到一个包含以下字段的列表:
- 树
- Parent
- 作者
- 提交者
- 留言
如果这些字段中的 任何 发生变化,它们的 SHA-1 哈希值也会发生变化,从而为提交授予一个完整的 新 ID。
修正就是改写历史
这就是为什么如果你修改一个提交——例如通过改变它的消息——它将有一个不同的ID之前。你实际上是 rewriting history.
请注意,提交的 parent 的 ID 也包含在元数据中。这意味着一旦提交更改了 ID,它的所有后代也将像多米诺骨牌效应一样更改 ID。