"Successfully rebased and updated refs/heads/master " 消息是否总是显示?
Does the message "Successfully rebased and updated refs/heads/master " always get displayed?
我在 master 分支上工作并尝试了这个 git rebase -i HEAD~3
。我实际上试图从提交历史中删除一个提交,但意识到没有必要这样做。
nano 编辑器打开后,我没有对文件做任何更改,没有保存就关闭了它。但是,命令行中显示一条消息,指出:
"Successfully rebased and updated refs/heads/master "。那是什么意思?我没有(据我所知)做任何改变。当我查看 git 日志和 git 状态时,我没有看到任何变化。
之后我做了一些提交并将它们推送到远程。
我的问题是:为什么显示该消息?真的有什么改变吗?
我问这个是因为这实际上是一个共享项目,我正要在重新设置某些东西时犯下一个重大错误,现在我担心我可能会这样做。正如您可能会说的那样,我是 git 的菜鸟 :)
感谢您的帮助!
在您的特定情况下,实际上没有发生任何事情。 rebase 成功了——并且在这个过程中做了一些适度的工作——但它所做的一切都导致 完全是原始提交 。因此,最后一部分,当它说 updated refs/heads/master
时,意味着:我将名称 master
从标识提交 X 更改为标识提交 X(对于某些哈希 ID X)。本质上,它删除了旧条目并将其替换为绝对、完全、100% 相同的条目。
这是怎么回事(如果你关心的话)
这一切的原因有点复杂。 git rebase
所做的,本质上是 copy 提交。这里的问题是,一旦提交,就会及时冻结:任何现有提交的任何部分都不能更改。如果我们做了一个不好的提交,或者甚至只是 "not so great",我们可能想要 替换 它为一个新的和改进的:我们可能想要将原始文件提取到 work-area,对 work-area 进行一些更改,并进行新的提交,该提交大部分相同,但略有不同。
如果我们这样做——如果我们制作了一个更改了某些部分的提交副本——我们会得到一个新的、不同的提交,它具有一个新的、不同的哈希 ID。这导致了一个难题,因为 Git 分支名称只记住 one 哈希 ID。具体来说,分支名称会记住 last 提交的哈希 ID,我们希望将其称为分支的一部分。
同时,每个提交,也记住一个哈希ID。更准确地说,每次提交都会记住零个或多个哈希 ID,但通常只有一个。提交记住的通常哈希 ID 是此特定提交之前 提交的哈希 ID。 Git 调用提交的 parent。
请注意,当 child 提交是 "born"(创建)时,Git 知道 child 的 parent 的哈希 ID(或,对于合并提交,parents,复数)。因此 Git 可以在创建过程中将哈希 ID 填充到 和 child 中。但是一旦 child 被写出,它就永远冻结了。所以,当那个 child 本身变成一个 parent 之后,它就不能再添加它的 children 了。它只能一直记住它的 parent(s).
但这已经足够了!如果我们绘制这种情况,我们会发现提交构成了一个很好的简单 backwards-looking 链:
... <-F <-G <-H
此处 H
代表 last 提交的实际哈希 ID。提交 H
是 child-most;它的 parent 是 G
; H
记住了 G
的 ID。提交 G
记住 F
的 ID,F
记住另一个 parent,依此类推,一直回到存储库的开头。
一个分支名称像master
因此只需要记住最后提交的ID,在这种情况下H
:
...--F--G--H <-- master
为了添加一个新的提交,我们Git提取最新的master
一个,即H
到work-area。然后我们处理它并准备一个新的提交。一旦一切准备就绪(git add
等等),我们 运行 git commit
。 Git 现在冻结我们告诉它为新提交保存的所有内容,添加 H
的实际哈希 ID,并写出一个新提交——它会得到一个新的、唯一的、又大又丑的哈希 ID我们将调用 I
:
...--F--G--H <-- master
\
I
最后步骤是git commit
将这个新的散列ID写入名称master
,这样master
现在会记住提交I
而不是提交 H
。没关系,因为 commit I
本身会记住 commit H
:
...--F--G--H
\
I <-- master
如果我们决定提交 I
,我们实际上根本无法 更改 I
,但我们可以 复制它到一个新的和改进的替代品,也许是一个叫做J
:
J
/
...--F--G--H
\
I <-- master
如果我们现在强制 Git 让名字 master
记住 J
的哈希 ID,它 看起来像 我们不知何故神奇更改 I
,只要我们不注意哈希 ID。 (Git,相比之下,非常严格关注哈希 ID。哈希 ID 有点像它的 life-blood:它们几乎是 Git 有效。)
对于某些变基,我们希望复制一系列提交,以便它们位于链中的不同位置:
...--F--G--H <-- master
\
I--J <-- feature
在这里,如果我们希望我们的功能基于提交 H
而不是提交 F
,我们必须 re-copy I
和 J
让它们出现在 H
之后,并且可能也使用稍微不同的源代码,然后我们将 Git 从 J
中删除名称 feature
并制作它指向新副本:
I'-J' <-- feature
/
...--F--G--H <-- master
\
I--J [abandoned]
在其他情况下,我们只是想做一些小的修复。例如,我们开始于:
...--F--G--H <-- master
\
I--J <-- feature
但决定我们要更改日志消息(reword
in git rebase -i
)。这将像以前一样创建一个新的和改进的 I'
。如果我们所做的只是 reword
,那么 J
是 很好,但是 J
的 parent 是 I
,所以我们需要一个新的印象派ved J'
其 parent 是副本 I'
:
I'-J' <-- feature
/
...--F--G--H <-- master
\
I--J [abandoned]
git rebase
的聪明之处在于,除非您告诉它不要这样做(例如使用 --force
),否则它会注意到实际上 根本没有变化的情况 提交 I
,只是 re-use 原始提交 I
。如果出现这种情况:
- 任何来源都没有变化;
- 作者姓名、日志消息等没有变化;和
- parent 哈希 ID 没有变化。
你的 rebase 就是这种情况:你说 不要对任何东西做任何更改 并且 parent 哈希 ID 也是相同的,所以 Git 只是保留了所有三个提交,仔细确定这样做是可以的。1
然后,就像Git在完成rebase操作后一样,Git将last-copied-commit的hash ID填充到分支名称中,以便名称记录最后一次提交在分支机构。那是 updated
部分。
1由于 --force
选项,git rebase
确实会对此进行检查。如果你告诉 rebase 它绝对 必须 替换提交,它会做一个微不足道的改变——例如更新作者日期——这样新的提交就有一个新的和不同的散列身份证.
git filter-branch
命令,它在其他方面类似于 git rebase
类固醇——它复制大部分提交,同时根据过滤器参数对它们进行任意更改—— 不会 进行任何此类检查。它依赖于这样一个事实,即如果您进行绝对、完全、100% bit-for-bit 相同的提交,该提交与之前的某个现有提交相匹配,您实际上最终会获得 原始哈希 ID 而不是在数据库中存储新的 object。如果 git rebase
没有 --force
,它可能只是这样做,而不是检查。使用 git filter-branch
,如果你想强制复制,你应该在你的一个过滤器中安排它。
请注意,100% 匹配要求意味着新提交必须具有:
- 相同的源代码树
- 相同的作者和提交者姓名和 dates/times
- 相同的日志消息,包括每个字符的准确拼写(包括白色 space)和相同的编码(UTF-8 或其他)
- 相同的 parent 哈希 ID,即导致该提交的相同历史记录
如果提交非常匹配,那么,它是原始提交,而不是更改后的副本。所以Git到re-use这里的原文是正确的
我在 master 分支上工作并尝试了这个 git rebase -i HEAD~3
。我实际上试图从提交历史中删除一个提交,但意识到没有必要这样做。
nano 编辑器打开后,我没有对文件做任何更改,没有保存就关闭了它。但是,命令行中显示一条消息,指出: "Successfully rebased and updated refs/heads/master "。那是什么意思?我没有(据我所知)做任何改变。当我查看 git 日志和 git 状态时,我没有看到任何变化。
之后我做了一些提交并将它们推送到远程。
我的问题是:为什么显示该消息?真的有什么改变吗?
我问这个是因为这实际上是一个共享项目,我正要在重新设置某些东西时犯下一个重大错误,现在我担心我可能会这样做。正如您可能会说的那样,我是 git 的菜鸟 :)
感谢您的帮助!
在您的特定情况下,实际上没有发生任何事情。 rebase 成功了——并且在这个过程中做了一些适度的工作——但它所做的一切都导致 完全是原始提交 。因此,最后一部分,当它说 updated refs/heads/master
时,意味着:我将名称 master
从标识提交 X 更改为标识提交 X(对于某些哈希 ID X)。本质上,它删除了旧条目并将其替换为绝对、完全、100% 相同的条目。
这是怎么回事(如果你关心的话)
这一切的原因有点复杂。 git rebase
所做的,本质上是 copy 提交。这里的问题是,一旦提交,就会及时冻结:任何现有提交的任何部分都不能更改。如果我们做了一个不好的提交,或者甚至只是 "not so great",我们可能想要 替换 它为一个新的和改进的:我们可能想要将原始文件提取到 work-area,对 work-area 进行一些更改,并进行新的提交,该提交大部分相同,但略有不同。
如果我们这样做——如果我们制作了一个更改了某些部分的提交副本——我们会得到一个新的、不同的提交,它具有一个新的、不同的哈希 ID。这导致了一个难题,因为 Git 分支名称只记住 one 哈希 ID。具体来说,分支名称会记住 last 提交的哈希 ID,我们希望将其称为分支的一部分。
同时,每个提交,也记住一个哈希ID。更准确地说,每次提交都会记住零个或多个哈希 ID,但通常只有一个。提交记住的通常哈希 ID 是此特定提交之前 提交的哈希 ID。 Git 调用提交的 parent。
请注意,当 child 提交是 "born"(创建)时,Git 知道 child 的 parent 的哈希 ID(或,对于合并提交,parents,复数)。因此 Git 可以在创建过程中将哈希 ID 填充到 和 child 中。但是一旦 child 被写出,它就永远冻结了。所以,当那个 child 本身变成一个 parent 之后,它就不能再添加它的 children 了。它只能一直记住它的 parent(s).
但这已经足够了!如果我们绘制这种情况,我们会发现提交构成了一个很好的简单 backwards-looking 链:
... <-F <-G <-H
此处 H
代表 last 提交的实际哈希 ID。提交 H
是 child-most;它的 parent 是 G
; H
记住了 G
的 ID。提交 G
记住 F
的 ID,F
记住另一个 parent,依此类推,一直回到存储库的开头。
一个分支名称像master
因此只需要记住最后提交的ID,在这种情况下H
:
...--F--G--H <-- master
为了添加一个新的提交,我们Git提取最新的master
一个,即H
到work-area。然后我们处理它并准备一个新的提交。一旦一切准备就绪(git add
等等),我们 运行 git commit
。 Git 现在冻结我们告诉它为新提交保存的所有内容,添加 H
的实际哈希 ID,并写出一个新提交——它会得到一个新的、唯一的、又大又丑的哈希 ID我们将调用 I
:
...--F--G--H <-- master
\
I
最后步骤是git commit
将这个新的散列ID写入名称master
,这样master
现在会记住提交I
而不是提交 H
。没关系,因为 commit I
本身会记住 commit H
:
...--F--G--H
\
I <-- master
如果我们决定提交 I
,我们实际上根本无法 更改 I
,但我们可以 复制它到一个新的和改进的替代品,也许是一个叫做J
:
J
/
...--F--G--H
\
I <-- master
如果我们现在强制 Git 让名字 master
记住 J
的哈希 ID,它 看起来像 我们不知何故神奇更改 I
,只要我们不注意哈希 ID。 (Git,相比之下,非常严格关注哈希 ID。哈希 ID 有点像它的 life-blood:它们几乎是 Git 有效。)
对于某些变基,我们希望复制一系列提交,以便它们位于链中的不同位置:
...--F--G--H <-- master
\
I--J <-- feature
在这里,如果我们希望我们的功能基于提交 H
而不是提交 F
,我们必须 re-copy I
和 J
让它们出现在 H
之后,并且可能也使用稍微不同的源代码,然后我们将 Git 从 J
中删除名称 feature
并制作它指向新副本:
I'-J' <-- feature
/
...--F--G--H <-- master
\
I--J [abandoned]
在其他情况下,我们只是想做一些小的修复。例如,我们开始于:
...--F--G--H <-- master
\
I--J <-- feature
但决定我们要更改日志消息(reword
in git rebase -i
)。这将像以前一样创建一个新的和改进的 I'
。如果我们所做的只是 reword
,那么 J
是 很好,但是 J
的 parent 是 I
,所以我们需要一个新的印象派ved J'
其 parent 是副本 I'
:
I'-J' <-- feature
/
...--F--G--H <-- master
\
I--J [abandoned]
git rebase
的聪明之处在于,除非您告诉它不要这样做(例如使用 --force
),否则它会注意到实际上 根本没有变化的情况 提交 I
,只是 re-use 原始提交 I
。如果出现这种情况:
- 任何来源都没有变化;
- 作者姓名、日志消息等没有变化;和
- parent 哈希 ID 没有变化。
你的 rebase 就是这种情况:你说 不要对任何东西做任何更改 并且 parent 哈希 ID 也是相同的,所以 Git 只是保留了所有三个提交,仔细确定这样做是可以的。1
然后,就像Git在完成rebase操作后一样,Git将last-copied-commit的hash ID填充到分支名称中,以便名称记录最后一次提交在分支机构。那是 updated
部分。
1由于 --force
选项,git rebase
确实会对此进行检查。如果你告诉 rebase 它绝对 必须 替换提交,它会做一个微不足道的改变——例如更新作者日期——这样新的提交就有一个新的和不同的散列身份证.
git filter-branch
命令,它在其他方面类似于 git rebase
类固醇——它复制大部分提交,同时根据过滤器参数对它们进行任意更改—— 不会 进行任何此类检查。它依赖于这样一个事实,即如果您进行绝对、完全、100% bit-for-bit 相同的提交,该提交与之前的某个现有提交相匹配,您实际上最终会获得 原始哈希 ID 而不是在数据库中存储新的 object。如果 git rebase
没有 --force
,它可能只是这样做,而不是检查。使用 git filter-branch
,如果你想强制复制,你应该在你的一个过滤器中安排它。
请注意,100% 匹配要求意味着新提交必须具有:
- 相同的源代码树
- 相同的作者和提交者姓名和 dates/times
- 相同的日志消息,包括每个字符的准确拼写(包括白色 space)和相同的编码(UTF-8 或其他)
- 相同的 parent 哈希 ID,即导致该提交的相同历史记录
如果提交非常匹配,那么,它是原始提交,而不是更改后的副本。所以Git到re-use这里的原文是正确的