我有一个非常简单的git存储库,它是为培训而创建的。它有一个仅包含初始提交的master分支,以及几个从master植根的分支。关键是我想编辑初始提交(由master指向)以添加带有一些指令的自述文件,但是这样做之后,似乎没有创建第一个提交,而是创建了一个新的提交,并且master分支更新以指向它。我尝试了不同的策略来做到这一点:修改第一次提交并使用--root
选项进行交互基础调整,但是所有这些都会产生相同的结果。
这是我的git日志:
* e2c1de0 (origin/rerere-base, rerere-base) Add second line to
rerere/file.txt to provoke a conflict
* 58af1bf Add file.txt for rerere example
...
| * 91d8f5b (origin/bisect-run, bisect-run) Add new test for minus
| * c7db602 Intentionally add a failing commit
...
| * 4ffaae9 Add calculator class with sum method
| * 188e648 Create package skeleton
|/
* 1d44a36 (HEAD -> master, origin/master, origin/HEAD) Initial commit
这是编辑第一次提交后的结果:
* 60f5e5c (HEAD -> master) Initial commit
* e2c1de0 (origin/rerere-base) Add second line to rerere/file.txt to provoke a conflict
* 58af1bf Add file.txt for rerere example
...
| * 91d8f5b (origin/bisect-run) Add new test for minus
| * c7db602 Intentionally add a failing commit
...
| * 4ffaae9 Add calculator class with sum method
| * 188e648 Create package skeleton
|/
* 1d44a36 (origin/master, origin/HEAD) Initial commit
请注意,新的提交已添加,消息为Initial commit
,其父级为e2c1de0 (origin/rerere-base)
,而不是原始的初始提交。
真的很奇怪,不是吗?
有人可以解释一下发生了什么,理想情况下可以提供解决方案吗?
前面提到的原始存储库位于https://github.com/beni0888/gitlikeapro,以防万一有人想自己尝试。
谢谢!
编辑: 感谢@torek的回答,我已经能够了解这里发生了什么。这个问题暗示两件事:
--oneline
选项的git log来检查历史记录,这使我产生了误解,因为我认为新生成的提交不是根提交,而是历史上上一个提交的孩子,这是不正确的,如果您显示的历史记录没有--oneline
选项,则可以进行检查。 请参见以下区别:
* commit 60f5e5c474719f2a6557859b04154c1b7a4d9e4b (HEAD -> master, origin/master, origin/HEAD)
Author: ...
...
* commit e2c1de0a44be64e187a16a4e6bb380caef2343b7 (origin/rerere-base)
| Author: ...
| ...
* commit 58af1bfce7a5a27143a3ca5dff464e004306c7f5
| Author: ...
| ...
...
答案 0 :(得分:1)
这是正常现象。 任何尝试更改提交的尝试实际上都会产生一个新的提交。
更改提交实际上是不可能的 1 ,因为提交的哈希ID是提交内容的加密校验和。如果您进行具有不同内容的新提交,则这是具有不同哈希ID的不同提交。
这就是git rebase
的工作方式:它需要整个现有提交链,并从中进行新的,改进的,不同提交。 分支名称(用于查找旧的迟钝提交的最后一个),然后被重写以指向闪亮的新提交的最后一个。任何不知道旧提交的哈希ID,仅使用 name 来访问提交的人,都会看到新提交:好像它们已更改。
但是仍然有原始提交的哈希ID的任何人都可以在旧ID下看到旧提交,因为它们仍在存储库中。
要使每个人都可以使用新的提交,对于每个“之后”的提交(后代),必须将 all 的提交复制到新的提交之后,重写 all 的名称。 -from)您尝试更改但实际上已复制的那一。这是因为每个后续提交都包含其父级的哈希ID,因此您需要将子级复制到具有新的父级且不同的父级的新提交中,然后复制该子级的子级以对其父级重新编号,等等。
请注意,git log --graph
无 --oneline
将向您显示新提交是根提交。使用--oneline
很难说是这样,因为ASCII图在同一列中只有两个*
字符:
* <hash> new root commit
* <hash> tip of other branch
如果有更多的文本行,您将看到以下两者之间的区别:
* <hash> tip of some branch
| that uses more than one
| line in the output
|
* <hash> another commit
和:
* <hash> new root commit
with more than one line
* <hash> tip of some branch
...
其他图形查看器(gitk,git-gui等)绘制出更好的图形线并使之更清晰,但是很少有像这样的多重根出现的。
1 好吧,不是 quit 不可能:如果您发现哈希冲突(例如,请参见https://shattered.it/),则可以进行两个不同的提交,每个提交只有一个哈希ID。但是,由于Git的内部设置,如果您的新对象具有相同的哈希ID,则无法替换,因此即使在以下情况下,也无法更改:这个案例。您可以做的是仔细构造两个单独的存储库,一个存储库的一个提交具有冲突的ID,另一个存储库的另一个提交具有相同的ID。
答案 1 :(得分:0)
我想编辑初始提交(由主控点指向)以添加一些说明的自述文件
将新文件添加为新提交。
如果您需要多个分支,请合并或选择。
Git doco状态:
-修改(...) 如果您修改已经发布的提交,则应该了解重写历史的含义。 (请参阅git-rebase中的“从UPSTREAM REBASE恢复”部分。)
RECOVERING FROM UPSTREAM REBASE状态
让其他人基于其工作的分支重新建立基础(或进行任何其他形式的重写)是一个坏主意:该分支下游的任何人都必须手动修复其历史记录。
如有疑问,您在这里还有另一则消息a tutorial :
不修改公共承诺
修订的提交实际上是全新的提交,以前的提交将不再位于您的当前分支上。这具有与重置公共快照相同的结果。避免修改其他开发人员基于其所做的提交。对于开发人员来说,这是一个令人困惑的情况,并且要从中恢复很复杂。