git rebase 影响远程分支还是本地分支
Does git rebase affect the remote branch or local branch
我正在我的分支 "role" 上工作。远程主机正在频繁更新。因此,在我将更改提交到远程分支之后,我需要将分支重新设置为最新的主分支。所以这是我的步骤:
# from local
git add .
git commit 'Add feature'
git push origin role
# rebase
git pull --rebase origin master
变基是只影响本地分支还是远程分支,还是两者都有?如果只有本地分支变基,我是否应该在变基后再次提交到原点?
总结:是的,但是你的问题是错误的。 :-)
这里有一堆东西要直说。 Git 术语开始时不太好——“远程”、“跟踪”、“分支”和“remote-tracking 分支”的意思完全不同!——而你正在使用另一个一组术语,这让事情变得更加棘手。所以,在我回答你想问的问题之前,让我们定义这些术语。 (另请参阅 gitglossary
,但并非所有这些都在其中。)
A remote 是一个类似于 origin
的短名称。它主要作为一个URL的占位符,这样你就不用一直输入一些很长的URL,同时也是[=339=的名称的一部分] branch(我们稍后会定义)。
A branch 是一个模棱两可的术语:参见 What exactly do we mean by "branch"? 在这种特殊情况下,我们将首先使用它来表示 分支名称 喜欢master
。 Git 中的分支名称只是特定提交的名称,我们称之为分支的 tip,但是 local(普通)分支名称如 master
有一个特殊的 属性,我们很快就会得到。
A remote-tracking 分支 是您的 Git 分配的名称,并在 您的分支中为您更新 存储库,基于你的 Git 在你的 Git 调用其他 Git 时看到的内容。这些是像 origin/master
这样的名字。这些 remote-tracking 分支名称 没有 普通(本地)分支名称的特殊 属性。
Tracking 是一个可怕的动词,Git 用作 shorthand 更现代和冗长的方式来表达同样的事情.可以将本地分支设置为“跟踪”remote-tracking 分支。更现代的说法是,本地分支将 remote-tracking 分支设置为其 上游 。设置 upstream 不会做任何你不能“手动”做的事情,但是 Git 会自动为你做,这很方便。
通常,我们会将remote-tracking分支origin/master
设置为本地分支master
的上游。同样,我们将 origin/role
设置为本地分支 role
的上游。 请务必记住,此时本地分支和 remote-tracking 分支都在 您自己的 Git 中。它们实际上都是本地的! remote-tracking 分支被称为“remote-tracking”,因为它们是自动更新。
接下来,请记住,当您使用 git fetch
和 git push
(以及 git pull
,但请参阅末尾)时,您的 Git 会调用另一个 Git。另一个 Git 有自己独立的存储库。您的 Git 获取另一个 Git 的方式是通过一些 URL,并且 URL 存储在“远程”名称下,例如 origin
: 你的 Git 通过 Internet-phone 呼叫来源的 Git 并与另一个 Git 交谈。你的 Git 从他们那里得到东西 (git fetch
) 或给他们东西 (git push
),你的 Git 和他们的 Git 都 纯粹在本地工作,你的Git在你的存储库上工作,他们的Git在他们的存储库上工作。
一切都是本地的
这就是为什么,在 Git 中,至少在第一个近似值中,一切都是本地的。 实际上没有任何远程的东西,只有本地实体,加上这些特殊的 Internet-phone-调用,其中多个 Git 在本地工作时相互交换数据。幸运的是,由于 fetch
和 push
是主要的两个接触点,因此很容易保持直截了当:在您获取或推送之前,一切都是本地的。 这些是您的 Git 和另一个 Git 传输数据并进行(本地!)更改的点。
那么,现在我们可以回答您的问题了:
Does rebase affect only the local branch or remote branch or both?
这个很简单:如果你不推动,你只能影响当地的事情。
If only local branch is rebased, should I commit to origin again after the rebase?
这个有一个内在的误解。当您进行提交时,您仍然只影响本地事物。要将您本地的东西发送到其他地方,您需要推送它们(或者,如果您的机器可以充当服务器,让您的机器回答 别人的 git fetch
phone-call —但我们不要去那里,至少现在不要去!)。
这里有一个大问题,因为 git fetch
和 git push
不对称。
推送和获取不对称
当您 运行 git fetch
时,您的 Git 会调用他们的 Git 并从他们那里获得新的东西。您的 Git 保存新的东西——提交和那些提交需要的任何其他东西,真的——在您的存储库中,然后更新您的 remote-tracking 分支名称。这些与您自己的本地分支名称完全不同:您的 origin/master
与您的 master
是分开的,您的 origin/role
与您的 role
是分开的。 唯一 你的 origin/*
分支名称做的事情是记住你的 Git 在他们的 Git 上看到的东西,所以这是很安全的随时更改 他们 Git 在他们的 Git 上看到新内容。
当您 运行 git push
时,您的 Git 会调用他们的 Git,向他们发送 您的 内容,然后要求他们更改 master
或 role
。 他们没有 ddd/master
或 ddd/role
:您让 Git 要求他们更改自己的,并且只有 master
或 role
. 如果他们在 master
或 role
中有一些新内容,而您还没有将其纳入您正在推动的内容中,您将要求他们 放弃那些新事物,取而代之。
现在是时候提出本地分支名称的特殊 属性,看看分支名称和提交是如何工作的,看看 git rebase
实际上是如何工作的。这也是回顾另一个问题的好时机,What exactly do we mean by "branch"?
分支是如何正常生长的,暴力变基对此有何影响
Git 主要是关于提交。提交是 Git 的 raison d'être:它们是您提交的任何内容的永恒不变的快照。没有提交可以 ever 更改,尽管可以将现有提交复制到新的(不同的)提交,方法是提取旧快照,进行更改(无论它是什么)并制作新快照在一些新的位置。但是这里的“位置”是什么意思?这是 提交图 进入画面的地方。
每个提交都有一个唯一的 ID。这是丑陋的 commit face0ff...
种 number-and-letter 组合 Git 在不同时期的表现。 (事实上 ,每个 Git 对象都有这些 ID 之一,但现在我们只关心提交对象。)
每次提交都会保存,连同您从上一次提交中 git add
编辑或保留的任何内容的快照、您的姓名、您的电子邮件地址、您的日志消息,以及——这是这里的关键项目— 之前提交的 ID。
这意味着提交形式(向后)链:
A <- B <- C <-- master
分支 names,如 master
,指向 tip 提交。提示提交是链中最新的。最近的指向其 parent(较早的)提交,它指向另一个父级,依此类推。此链仅在我们到达第一个没有父项的提交时结束。
由于新提交指向旧提交,我们需要Git“推进”分支名称,每当我们创建新提交时,指向我们刚刚做出的新承诺。也就是说,如果我们有上面的 A <- B <- C
并且我们创建一个新的 D
,我们现在需要 Git 移动 master
指向 D
:
A <- B <- C <- D <-- master
这是本地分支名称的特殊属性。他们自动前进,到我们刚刚做出的新提交,当我们在该分支上进行提交时。
这引出了一条关于分支名称的简单规则(但很快就违反了这条规则):它们以一种保留所有旧提交的方式前进。我们向一个分支添加一个 new 提交,新提交指向旧提交,我们只 添加到 分支。 (请注意,我们现在谈论的分支是 提交集合 ,而不是 名称 指向 一个提交!)
Rebase 故意违反了这个简单的规则。让我们看看创建分支时会发生什么。让我们也停止绘制所有内部箭头,只记住它们都指向后方:当我们从较新的提交向后移动到较旧的提交时,我们总是移动 left-ish。
A--B--C--D <-- master
\
E--F--G <-- role
这里我们总共有七个提交,其中三个仅在分支 role
上,一个提交 D
- 仅在分支 master
上,三个 - A-B-C
链——在 两个 分支上。 (这是 Git 的另一个奇特之处:提交通常在 许多 分支上。)
当我们使用 git rebase
时,我们希望移动提交,通常紧跟在其他分支的(新)提示之后。例如,在这种特殊情况下,我们希望将 E-F-G
链移动到 D
之后,而不是 C
之后。 但是不能更改提交,只能复制。
这就是 git rebase
所做的:它 复制 提交。我们将 E-F-G
复制到新的提交 E'-F'-G'
,它们与 很像 E-F-G
,但它们在图中的不同位置:它们在 D
:
之后
A--B--C--D <-- master
\ \
\ E'-F'-G' <-- role
\
E--F--G [previous role, now abandoned]
rebase 命令首先复制提交,然后将分支 name role
从旧的 tip 提交 G
剥离并将其粘贴到新副本上G'
.
这会丢失旧的 E-F-G
链,放弃它以支持新的闪亮 E'-F'-G'
链。新提交略有不同——如果没有别的,E'
与 E
的不同之处在于 E'
有 D
作为它的父级,而 C
——有不同的 ID。
因此当我们推送时我们必须force-push
如果我们将原始 E-F-G
推送到 origin
,我们就已经给了他们 exact-duplicate 个原始 E-F-G
的副本。这些在 C
之后,而不是在 D
之后。现在我们又要去git push origin role
了,所以我们会叫我们的Git叫另一个Git交出E'-F'-G'
,然后让他们设置他们的role
指向提交 G'
.
他们会说“不”!
如果他们将 role
设置为指向 G'
,他们将 失去 提交 E-F-G
。现在,他们 Git 找到 G
的 唯一 方法是查看(他们的)名字 role
,如果他们 改变他们的role
指向G'
,他们会失去这个link到G
。
当然,这就是我们希望他们做的!但默认情况下他们会说“不,如果我这样做,我将失去一些提交”。 (这个默认的原因很简单:他们不知道也不关心 我们 给了他们 G
,他们只知道,现在,他们会 lose G
.) 所以我们必须将 Git 的礼貌请求“请移动 role
” 更改为更有力的命令:“移动 role
!"
理想情况下,我们甚至可以说:“我们认为您的 role
名称 G
,如果是这样,它应该改为名称 G'
;但如果我们知道,请告诉我们你的 role
错了,好吗?” Git 称其为 --force-with-lease
。并非 Git 的每个版本都支持它,如果您 确定 您 是唯一更改过的人,则没有必要他们的role
;但是需要某种强制,因为你需要让 他们的 Git 放弃旧的(复制的)提交,就像你的 Git 所做的那样。
旁白:git pull
git pull
命令是为了方便起见。每当你 运行 git fetch
时,你都可以像往常一样从远程获取一堆新提交,但这些新提交都在它们的 remote-tracking 分支下结束。一旦你 有 这些提交,你通常想 用 做一些事情。您可以用它们做的两件事是 git merge
或 git rebase
。所以 git pull
将 git fetch
与立即 git merge
或 git rebase
结合起来。
这样做的主要问题是,在您获取 并检查 任何新提交之前,您不一定确切地知道 什么 你会想和他们一起做。使用 git pull
可以让您提前决定如何处理它们。想象一下:“我要把手伸进这个黑暗的壁橱,拿出一瓶酒,不管是什么,我都要喝掉它。”如果您拿出啤酒,那很好;但是如果你拿出一瓶漂白剂呢?
无论如何,以上所有内容仍然适用于git pull
,因为只是git fetch
后面跟了第二个命令。 fetch
通过存储在远程名称下的 URL 调用另一个 Git。第二步,无论是git merge
还是git rebase
,然后在本地工作。 git pull
特别令人困惑的是,因为它早于 remote-tracking 分支名称的发明,所以它有自己的特殊语法。这就是为什么,即使你是从原点获取,然后在 origin/master
上变基,你写的是“pull (as rebase) origin master”而不是“pull (and then rebase on) origin/master”。
我正在我的分支 "role" 上工作。远程主机正在频繁更新。因此,在我将更改提交到远程分支之后,我需要将分支重新设置为最新的主分支。所以这是我的步骤:
# from local
git add .
git commit 'Add feature'
git push origin role
# rebase
git pull --rebase origin master
变基是只影响本地分支还是远程分支,还是两者都有?如果只有本地分支变基,我是否应该在变基后再次提交到原点?
总结:是的,但是你的问题是错误的。 :-)
这里有一堆东西要直说。 Git 术语开始时不太好——“远程”、“跟踪”、“分支”和“remote-tracking 分支”的意思完全不同!——而你正在使用另一个一组术语,这让事情变得更加棘手。所以,在我回答你想问的问题之前,让我们定义这些术语。 (另请参阅 gitglossary
,但并非所有这些都在其中。)
A remote 是一个类似于
origin
的短名称。它主要作为一个URL的占位符,这样你就不用一直输入一些很长的URL,同时也是[=339=的名称的一部分] branch(我们稍后会定义)。A branch 是一个模棱两可的术语:参见 What exactly do we mean by "branch"? 在这种特殊情况下,我们将首先使用它来表示 分支名称 喜欢
master
。 Git 中的分支名称只是特定提交的名称,我们称之为分支的 tip,但是 local(普通)分支名称如master
有一个特殊的 属性,我们很快就会得到。A remote-tracking 分支 是您的 Git 分配的名称,并在 您的分支中为您更新 存储库,基于你的 Git 在你的 Git 调用其他 Git 时看到的内容。这些是像
origin/master
这样的名字。这些 remote-tracking 分支名称 没有 普通(本地)分支名称的特殊 属性。Tracking 是一个可怕的动词,Git 用作 shorthand 更现代和冗长的方式来表达同样的事情.可以将本地分支设置为“跟踪”remote-tracking 分支。更现代的说法是,本地分支将 remote-tracking 分支设置为其 上游 。设置 upstream 不会做任何你不能“手动”做的事情,但是 Git 会自动为你做,这很方便。
通常,我们会将remote-tracking分支
origin/master
设置为本地分支master
的上游。同样,我们将origin/role
设置为本地分支role
的上游。 请务必记住,此时本地分支和 remote-tracking 分支都在 您自己的 Git 中。它们实际上都是本地的! remote-tracking 分支被称为“remote-tracking”,因为它们是自动更新。
接下来,请记住,当您使用 git fetch
和 git push
(以及 git pull
,但请参阅末尾)时,您的 Git 会调用另一个 Git。另一个 Git 有自己独立的存储库。您的 Git 获取另一个 Git 的方式是通过一些 URL,并且 URL 存储在“远程”名称下,例如 origin
: 你的 Git 通过 Internet-phone 呼叫来源的 Git 并与另一个 Git 交谈。你的 Git 从他们那里得到东西 (git fetch
) 或给他们东西 (git push
),你的 Git 和他们的 Git 都 纯粹在本地工作,你的Git在你的存储库上工作,他们的Git在他们的存储库上工作。
一切都是本地的
这就是为什么,在 Git 中,至少在第一个近似值中,一切都是本地的。 实际上没有任何远程的东西,只有本地实体,加上这些特殊的 Internet-phone-调用,其中多个 Git 在本地工作时相互交换数据。幸运的是,由于 fetch
和 push
是主要的两个接触点,因此很容易保持直截了当:在您获取或推送之前,一切都是本地的。 这些是您的 Git 和另一个 Git 传输数据并进行(本地!)更改的点。
那么,现在我们可以回答您的问题了:
Does rebase affect only the local branch or remote branch or both?
这个很简单:如果你不推动,你只能影响当地的事情。
If only local branch is rebased, should I commit to origin again after the rebase?
这个有一个内在的误解。当您进行提交时,您仍然只影响本地事物。要将您本地的东西发送到其他地方,您需要推送它们(或者,如果您的机器可以充当服务器,让您的机器回答 别人的 git fetch
phone-call —但我们不要去那里,至少现在不要去!)。
这里有一个大问题,因为 git fetch
和 git push
不对称。
推送和获取不对称
当您 运行 git fetch
时,您的 Git 会调用他们的 Git 并从他们那里获得新的东西。您的 Git 保存新的东西——提交和那些提交需要的任何其他东西,真的——在您的存储库中,然后更新您的 remote-tracking 分支名称。这些与您自己的本地分支名称完全不同:您的 origin/master
与您的 master
是分开的,您的 origin/role
与您的 role
是分开的。 唯一 你的 origin/*
分支名称做的事情是记住你的 Git 在他们的 Git 上看到的东西,所以这是很安全的随时更改 他们 Git 在他们的 Git 上看到新内容。
当您 运行 git push
时,您的 Git 会调用他们的 Git,向他们发送 您的 内容,然后要求他们更改 master
或 role
。 他们没有 ddd/master
或 ddd/role
:您让 Git 要求他们更改自己的,并且只有 master
或 role
. 如果他们在 master
或 role
中有一些新内容,而您还没有将其纳入您正在推动的内容中,您将要求他们 放弃那些新事物,取而代之。
现在是时候提出本地分支名称的特殊 属性,看看分支名称和提交是如何工作的,看看 git rebase
实际上是如何工作的。这也是回顾另一个问题的好时机,What exactly do we mean by "branch"?
分支是如何正常生长的,暴力变基对此有何影响
Git 主要是关于提交。提交是 Git 的 raison d'être:它们是您提交的任何内容的永恒不变的快照。没有提交可以 ever 更改,尽管可以将现有提交复制到新的(不同的)提交,方法是提取旧快照,进行更改(无论它是什么)并制作新快照在一些新的位置。但是这里的“位置”是什么意思?这是 提交图 进入画面的地方。
每个提交都有一个唯一的 ID。这是丑陋的 commit face0ff...
种 number-and-letter 组合 Git 在不同时期的表现。 (事实上 ,每个 Git 对象都有这些 ID 之一,但现在我们只关心提交对象。)
每次提交都会保存,连同您从上一次提交中 git add
编辑或保留的任何内容的快照、您的姓名、您的电子邮件地址、您的日志消息,以及——这是这里的关键项目— 之前提交的 ID。
这意味着提交形式(向后)链:
A <- B <- C <-- master
分支 names,如 master
,指向 tip 提交。提示提交是链中最新的。最近的指向其 parent(较早的)提交,它指向另一个父级,依此类推。此链仅在我们到达第一个没有父项的提交时结束。
由于新提交指向旧提交,我们需要Git“推进”分支名称,每当我们创建新提交时,指向我们刚刚做出的新承诺。也就是说,如果我们有上面的 A <- B <- C
并且我们创建一个新的 D
,我们现在需要 Git 移动 master
指向 D
:
A <- B <- C <- D <-- master
这是本地分支名称的特殊属性。他们自动前进,到我们刚刚做出的新提交,当我们在该分支上进行提交时。
这引出了一条关于分支名称的简单规则(但很快就违反了这条规则):它们以一种保留所有旧提交的方式前进。我们向一个分支添加一个 new 提交,新提交指向旧提交,我们只 添加到 分支。 (请注意,我们现在谈论的分支是 提交集合 ,而不是 名称 指向 一个提交!)
Rebase 故意违反了这个简单的规则。让我们看看创建分支时会发生什么。让我们也停止绘制所有内部箭头,只记住它们都指向后方:当我们从较新的提交向后移动到较旧的提交时,我们总是移动 left-ish。
A--B--C--D <-- master
\
E--F--G <-- role
这里我们总共有七个提交,其中三个仅在分支 role
上,一个提交 D
- 仅在分支 master
上,三个 - A-B-C
链——在 两个 分支上。 (这是 Git 的另一个奇特之处:提交通常在 许多 分支上。)
当我们使用 git rebase
时,我们希望移动提交,通常紧跟在其他分支的(新)提示之后。例如,在这种特殊情况下,我们希望将 E-F-G
链移动到 D
之后,而不是 C
之后。 但是不能更改提交,只能复制。
这就是 git rebase
所做的:它 复制 提交。我们将 E-F-G
复制到新的提交 E'-F'-G'
,它们与 很像 E-F-G
,但它们在图中的不同位置:它们在 D
:
A--B--C--D <-- master
\ \
\ E'-F'-G' <-- role
\
E--F--G [previous role, now abandoned]
rebase 命令首先复制提交,然后将分支 name role
从旧的 tip 提交 G
剥离并将其粘贴到新副本上G'
.
这会丢失旧的 E-F-G
链,放弃它以支持新的闪亮 E'-F'-G'
链。新提交略有不同——如果没有别的,E'
与 E
的不同之处在于 E'
有 D
作为它的父级,而 C
——有不同的 ID。
因此当我们推送时我们必须force-push
如果我们将原始 E-F-G
推送到 origin
,我们就已经给了他们 exact-duplicate 个原始 E-F-G
的副本。这些在 C
之后,而不是在 D
之后。现在我们又要去git push origin role
了,所以我们会叫我们的Git叫另一个Git交出E'-F'-G'
,然后让他们设置他们的role
指向提交 G'
.
他们会说“不”!
如果他们将 role
设置为指向 G'
,他们将 失去 提交 E-F-G
。现在,他们 Git 找到 G
的 唯一 方法是查看(他们的)名字 role
,如果他们 改变他们的role
指向G'
,他们会失去这个link到G
。
当然,这就是我们希望他们做的!但默认情况下他们会说“不,如果我这样做,我将失去一些提交”。 (这个默认的原因很简单:他们不知道也不关心 我们 给了他们 G
,他们只知道,现在,他们会 lose G
.) 所以我们必须将 Git 的礼貌请求“请移动 role
” 更改为更有力的命令:“移动 role
!"
理想情况下,我们甚至可以说:“我们认为您的 role
名称 G
,如果是这样,它应该改为名称 G'
;但如果我们知道,请告诉我们你的 role
错了,好吗?” Git 称其为 --force-with-lease
。并非 Git 的每个版本都支持它,如果您 确定 您 是唯一更改过的人,则没有必要他们的role
;但是需要某种强制,因为你需要让 他们的 Git 放弃旧的(复制的)提交,就像你的 Git 所做的那样。
旁白:git pull
git pull
命令是为了方便起见。每当你 运行 git fetch
时,你都可以像往常一样从远程获取一堆新提交,但这些新提交都在它们的 remote-tracking 分支下结束。一旦你 有 这些提交,你通常想 用 做一些事情。您可以用它们做的两件事是 git merge
或 git rebase
。所以 git pull
将 git fetch
与立即 git merge
或 git rebase
结合起来。
这样做的主要问题是,在您获取 并检查 任何新提交之前,您不一定确切地知道 什么 你会想和他们一起做。使用 git pull
可以让您提前决定如何处理它们。想象一下:“我要把手伸进这个黑暗的壁橱,拿出一瓶酒,不管是什么,我都要喝掉它。”如果您拿出啤酒,那很好;但是如果你拿出一瓶漂白剂呢?
无论如何,以上所有内容仍然适用于git pull
,因为只是git fetch
后面跟了第二个命令。 fetch
通过存储在远程名称下的 URL 调用另一个 Git。第二步,无论是git merge
还是git rebase
,然后在本地工作。 git pull
特别令人困惑的是,因为它早于 remote-tracking 分支名称的发明,所以它有自己的特殊语法。这就是为什么,即使你是从原点获取,然后在 origin/master
上变基,你写的是“pull (as rebase) origin master”而不是“pull (and then rebase on) origin/master”。