Git 在处理功能远程分支困境时变基
Git rebase while working on feature remote branch dilema
我看过很多关于 git
功能分支上 rebase 的帖子和博客,但仍然感到 100% 困惑。
场景:我在处理来自 master 的新创建的功能分支时执行了以下步骤。
% git clone <repository>
% git checkout -b feature_branch origin/master
% git add .
% git commit -m "initial commit"
% git push
% git feature_branch [-u|--set-upstream-to]
% git checkout master
% git pull
% git rebase
% git checkout feature_branch
//make some changes locally
% git add .
% git commit
% git push
在这个阶段,我收到消息说您的远程分支位于本地存储库之前。请先拉。何时以及如何更改我的远程功能分支?
我知道这里有 master 的本地仓库和我的功能分支,当我拉取东西时,它们来自远程 origin/master
。因此,当我在本地进行更改时,我假设我的本地 feature_branch 领先于远程 feature_branch。
在这种情况下,我可能在哪里采取了错误的步骤?在初始本地 feature_branch
上工作的实际步骤应该是什么,然后转到 remote
进行备份,以防在开发过程中出现问题。我想使用 rebase
来保持 git 历史记录干净。那么步骤是怎样的呢?上面我说的?
我在这里做了几个假设;我会记下来的。
以下是命令序列,以及我假设的内容和它们随后执行的操作:
% git clone <repository>
我假设这有效,然后你也 cd
进入新的克隆(你没有显示这个)。
% git checkout -b feature_branch origin/master
这会在您的新本地存储库中创建一个新的 b运行ch 名称 feature_branch
,select 与 origin/master
相同的提交。名字 origin/master
——refs/remotes/origin/master
的缩写——实际上不是 b运行ch 名字,而是你的 Git对他们(origin
的)master
的记忆,你的Git最后一次更新自己的记忆:在这种情况下,你运行 git clone
.
他们可能已经或可能还没有一个名为 feature_branch
的 b运行ch!您在本地创建了您的 feature_branch
。如果他们确实有 feature_branch
,你的与他们的没有关系,除非碰巧他们的 master
也与他们的 master
(即你的 origin/master
)完全相同。
% git add .
这不会做任何事情,因为您没有修改任何文件,并且工作树中的所有文件都来自提交。
% git commit -m "initial commit"
这样也没什么用。您现有的 feature_branch
仍然与他们的 master
、您的 origin/master
.
持平
如果您确实在 git add .
和 git commit
之前修改了一些文件,这会在您的 feature_branch
.
上进行一次新提交
% git push
这会给你一个错误,在现代 Git 中,还有很多建议建议 git branch --set-upstream-to
或 git push -u
或类似的东西:
% git feature_branch [-u|--set-upstream-to]
这不是命令。如果这实际上是:
git branch --set-upstream-to origin/feature_branch
和你在这里没有错误,这意味着他们已经有自己的feature_branch
。这似乎是这里最有可能的问题。
(如果你实际上 运行,而是:
git push -u origin feature_branch
那将 创建 一个新名称,feature_branch
,在 origin
的 Git 存储库上,基于您的 [=32] =],我们以后可能不会看到任何错误。)
% git checkout master
% git pull
做这两件事真的没有任何意义,他们可能什么也没做。如果他们确实做了一些事情,通常 是无害的——尽管这取决于谁在 origin
.[=123= 上对 Git 存储库做什么。 ]
% git rebase
此时,您仍在 master
上,它又什么都不做。
% git checkout feature_branch
//make some changes locally
% git add .
% git commit
在这里,我们终于有所作为:我们在本地添加一个新提交到名为 feature_branch
的本地 b运行ch。但是如果他们有自己的特征b运行ch,你的本地b运行ch和他们的现在已经分道扬镳了,并且:
% git push
这会抱怨你需要使用 git pull
或类似的东西来适应他们的工作。
此时您可能想要做的是:
git rebase --onto origin/feature_branch origin/master
这将复制您在 feature_branch
上所做的实际新提交,该提交从 origin/master
延伸出去。该提交的新改进副本将在 他们的 最后一次提交 他们的 feature_branch
之后,这是你的 origin/feature_branch
.
作为绘图,我们可能有这样的东西:
K <-- feature_branch (HEAD)
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
这张图的意思是:
您当前的 b运行ch 是 feature_branch
。就是这里的特殊名称HEAD
您当前的 提交 是 K
。任何提交的真实哈希 ID 都是唯一的,但又大又丑,人类无法处理:它看起来像 运行dom 乱码。因此,当我们对存储库中发生的事情进行简化绘图时,我们通常会通过用更简单的名称替换提交来得到很好的服务。这就是我在这里所做的。
他们的 master
(你的 origin/master
)和你的 master
都 select 提交 H
.
他们的feature_branch
——你的origin/feature_branch
——select比H
晚提交J
.
请注意,我已将后来的提交绘制在右侧。每个提交都向后链接到更早的提交,因此提交 H
向后链接到某个更早的提交 G
,它向后链接到我懒得尝试绘制的更早的提交。同时,提交 J
向后链接到 I
,后者向后链接到 H
.
这种向后链接是 Git 提交的真正意义所在。每次你进行 new 提交时,Git 打包每个文件的快照,就像它现在的形式一样(在 git add
将副本复制到Git:您可以看到和工作的副本 on/with 实际上并不 在 Git 中,它有不可见的秘密副本)。 Git 添加 元数据 ,例如您的姓名和电子邮件地址以及当前日期和时间。 Git 制作此元数据,使其包含 当前 提交的哈希 ID——在本例中为提交 K
——并将所有内容写出以生成新提交,我们可以称之为 L
:
L
/
K
/
...--G--H
\
I--J
现在,作为它的最后一个技巧,git commit
将该提交的原始哈希 ID 写入 current b运行ch name,给我们:
K--L <-- feature_branch (HEAD)
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
我不必在此处单独绘制 L
,因为我不需要绘制任何指向提交 K
的箭头,但如果你再画一个 b 运行ch name temp
指向 K
,我会把 L
放在单独的行上:
L <-- feature_branch (HEAD)
/
K <-- temp
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
这是(打算)同一张图,但添加了名称 temp
。
(您可能想要 运行 git log --all --graph --decorate --oneline
或 Pretty Git branch graphs 的答案中显示的任何其他命令,以查看查看您拥有的提交集的其他方式。 )
git rebase
的重点是复制提交
提交的好处在于它永远无法更改……根本。您无法更改 任何有关现有提交的内容。甚至 Git 本身也做不到。
提交的坏处在于它永远无法更改。提交 K
——你在 feature_branch
开始时所做的那个:
...--G--H <-- master, origin/master, feature_branch (HEAD)
\
I--J <-- origin/feature_branch
卡在原处,向后指向现有提交 H
。你可能喜欢关于它的所有 else,但你不喜欢它指向 H
的事实,因为 他们的 最新 feature_branch
提交是提交 J
。所以你需要一个新的改进版本 K
.
rebase 命令是关于进行这些新的和改进的提交。它可以 复制 旧提交 K
到新的和改进的 K'
。然后它有你的 Git 存储库 放弃 旧的 K
并使用新的和改进的 K'
代替。
使用 rebase 的坏处是:
你必须注意它复制了多少提交。有时默认值是正确的,有时则不是。
因为它复制提交,任何else拥有原件的人*也必须放弃旧的新的和改进的。这需要他们的努力。
如果你没有给出旧的,没有其他人拥有旧的,所以变基是安全的。否则,您需要告诉拥有它们的人您正在这样做。
无论如何,给定:
K <-- feature_branch (HEAD)
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
运行宁:
git rebase --onto origin/feature_branch origin/master
告诉你的Git:复制K
,在H
处停止复制,并将副本放在J
之后.
如果你的提交图 是 我画的那个,一个更简单的 rebase 会产生同样的效果——但是你的提交图可能看起来像这样:
K <-- feature_branch (HEAD)
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
在这种情况下,更简单的命令(我仍然没有显示)会尝试将 H
复制到一个新的和改进的 H'
,您不想这样做。
在这两种情况下,在 变基之后,您新的和改进的 K'
将在 J
之后出现,例如:
K [abandoned]
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
\
K' <-- feature_branch (HEAD)
因为我们——和Git——find 从 b运行ch 名称开始提交,跟随箭头到提交,然后工作向后(在这些图中向左),没有人会再次看到提交 K
。它仍然在那里——没有人也没有什么能改变它——但它也可能已经消失了。
(最终——默认情况下至少 30 天后——Git 会注意到没有人可以 找到 K
,并真正将其剥离, 但你也不会注意到这一点。)
我看过很多关于 git
功能分支上 rebase 的帖子和博客,但仍然感到 100% 困惑。
场景:我在处理来自 master 的新创建的功能分支时执行了以下步骤。
% git clone <repository>
% git checkout -b feature_branch origin/master
% git add .
% git commit -m "initial commit"
% git push
% git feature_branch [-u|--set-upstream-to]
% git checkout master
% git pull
% git rebase
% git checkout feature_branch
//make some changes locally
% git add .
% git commit
% git push
在这个阶段,我收到消息说您的远程分支位于本地存储库之前。请先拉。何时以及如何更改我的远程功能分支?
我知道这里有 master 的本地仓库和我的功能分支,当我拉取东西时,它们来自远程 origin/master
。因此,当我在本地进行更改时,我假设我的本地 feature_branch 领先于远程 feature_branch。
在这种情况下,我可能在哪里采取了错误的步骤?在初始本地 feature_branch
上工作的实际步骤应该是什么,然后转到 remote
进行备份,以防在开发过程中出现问题。我想使用 rebase
来保持 git 历史记录干净。那么步骤是怎样的呢?上面我说的?
我在这里做了几个假设;我会记下来的。
以下是命令序列,以及我假设的内容和它们随后执行的操作:
% git clone <repository>
我假设这有效,然后你也 cd
进入新的克隆(你没有显示这个)。
% git checkout -b feature_branch origin/master
这会在您的新本地存储库中创建一个新的 b运行ch 名称 feature_branch
,select 与 origin/master
相同的提交。名字 origin/master
——refs/remotes/origin/master
的缩写——实际上不是 b运行ch 名字,而是你的 Git对他们(origin
的)master
的记忆,你的Git最后一次更新自己的记忆:在这种情况下,你运行 git clone
.
他们可能已经或可能还没有一个名为 feature_branch
的 b运行ch!您在本地创建了您的 feature_branch
。如果他们确实有 feature_branch
,你的与他们的没有关系,除非碰巧他们的 master
也与他们的 master
(即你的 origin/master
)完全相同。
% git add .
这不会做任何事情,因为您没有修改任何文件,并且工作树中的所有文件都来自提交。
% git commit -m "initial commit"
这样也没什么用。您现有的 feature_branch
仍然与他们的 master
、您的 origin/master
.
如果您确实在 git add .
和 git commit
之前修改了一些文件,这会在您的 feature_branch
.
% git push
这会给你一个错误,在现代 Git 中,还有很多建议建议 git branch --set-upstream-to
或 git push -u
或类似的东西:
% git feature_branch [-u|--set-upstream-to]
这不是命令。如果这实际上是:
git branch --set-upstream-to origin/feature_branch
和你在这里没有错误,这意味着他们已经有自己的feature_branch
。这似乎是这里最有可能的问题。
(如果你实际上 运行,而是:
git push -u origin feature_branch
那将 创建 一个新名称,feature_branch
,在 origin
的 Git 存储库上,基于您的 [=32] =],我们以后可能不会看到任何错误。)
% git checkout master % git pull
做这两件事真的没有任何意义,他们可能什么也没做。如果他们确实做了一些事情,通常 是无害的——尽管这取决于谁在 origin
.[=123= 上对 Git 存储库做什么。 ]
% git rebase
此时,您仍在 master
上,它又什么都不做。
% git checkout feature_branch //make some changes locally % git add . % git commit
在这里,我们终于有所作为:我们在本地添加一个新提交到名为 feature_branch
的本地 b运行ch。但是如果他们有自己的特征b运行ch,你的本地b运行ch和他们的现在已经分道扬镳了,并且:
% git push
这会抱怨你需要使用 git pull
或类似的东西来适应他们的工作。
此时您可能想要做的是:
git rebase --onto origin/feature_branch origin/master
这将复制您在 feature_branch
上所做的实际新提交,该提交从 origin/master
延伸出去。该提交的新改进副本将在 他们的 最后一次提交 他们的 feature_branch
之后,这是你的 origin/feature_branch
.
作为绘图,我们可能有这样的东西:
K <-- feature_branch (HEAD)
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
这张图的意思是:
您当前的 b运行ch 是
feature_branch
。就是这里的特殊名称HEAD
您当前的 提交 是
K
。任何提交的真实哈希 ID 都是唯一的,但又大又丑,人类无法处理:它看起来像 运行dom 乱码。因此,当我们对存储库中发生的事情进行简化绘图时,我们通常会通过用更简单的名称替换提交来得到很好的服务。这就是我在这里所做的。他们的
master
(你的origin/master
)和你的master
都 select 提交H
.他们的
feature_branch
——你的origin/feature_branch
——select比H
晚提交J
.
请注意,我已将后来的提交绘制在右侧。每个提交都向后链接到更早的提交,因此提交 H
向后链接到某个更早的提交 G
,它向后链接到我懒得尝试绘制的更早的提交。同时,提交 J
向后链接到 I
,后者向后链接到 H
.
这种向后链接是 Git 提交的真正意义所在。每次你进行 new 提交时,Git 打包每个文件的快照,就像它现在的形式一样(在 git add
将副本复制到Git:您可以看到和工作的副本 on/with 实际上并不 在 Git 中,它有不可见的秘密副本)。 Git 添加 元数据 ,例如您的姓名和电子邮件地址以及当前日期和时间。 Git 制作此元数据,使其包含 当前 提交的哈希 ID——在本例中为提交 K
——并将所有内容写出以生成新提交,我们可以称之为 L
:
L
/
K
/
...--G--H
\
I--J
现在,作为它的最后一个技巧,git commit
将该提交的原始哈希 ID 写入 current b运行ch name,给我们:
K--L <-- feature_branch (HEAD)
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
我不必在此处单独绘制 L
,因为我不需要绘制任何指向提交 K
的箭头,但如果你再画一个 b 运行ch name temp
指向 K
,我会把 L
放在单独的行上:
L <-- feature_branch (HEAD)
/
K <-- temp
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
这是(打算)同一张图,但添加了名称 temp
。
(您可能想要 运行 git log --all --graph --decorate --oneline
或 Pretty Git branch graphs 的答案中显示的任何其他命令,以查看查看您拥有的提交集的其他方式。 )
git rebase
的重点是复制提交
提交的好处在于它永远无法更改……根本。您无法更改 任何有关现有提交的内容。甚至 Git 本身也做不到。
提交的坏处在于它永远无法更改。提交 K
——你在 feature_branch
开始时所做的那个:
...--G--H <-- master, origin/master, feature_branch (HEAD)
\
I--J <-- origin/feature_branch
卡在原处,向后指向现有提交 H
。你可能喜欢关于它的所有 else,但你不喜欢它指向 H
的事实,因为 他们的 最新 feature_branch
提交是提交 J
。所以你需要一个新的改进版本 K
.
rebase 命令是关于进行这些新的和改进的提交。它可以 复制 旧提交 K
到新的和改进的 K'
。然后它有你的 Git 存储库 放弃 旧的 K
并使用新的和改进的 K'
代替。
使用 rebase 的坏处是:
你必须注意它复制了多少提交。有时默认值是正确的,有时则不是。
因为它复制提交,任何else拥有原件的人*也必须放弃旧的新的和改进的。这需要他们的努力。
如果你没有给出旧的,没有其他人拥有旧的,所以变基是安全的。否则,您需要告诉拥有它们的人您正在这样做。
无论如何,给定:
K <-- feature_branch (HEAD)
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
运行宁:
git rebase --onto origin/feature_branch origin/master
告诉你的Git:复制K
,在H
处停止复制,并将副本放在J
之后.
如果你的提交图 是 我画的那个,一个更简单的 rebase 会产生同样的效果——但是你的提交图可能看起来像这样:
K <-- feature_branch (HEAD)
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
在这种情况下,更简单的命令(我仍然没有显示)会尝试将 H
复制到一个新的和改进的 H'
,您不想这样做。
在这两种情况下,在 变基之后,您新的和改进的 K'
将在 J
之后出现,例如:
K [abandoned]
/
...--G--H <-- master, origin/master
\
I--J <-- origin/feature_branch
\
K' <-- feature_branch (HEAD)
因为我们——和Git——find 从 b运行ch 名称开始提交,跟随箭头到提交,然后工作向后(在这些图中向左),没有人会再次看到提交 K
。它仍然在那里——没有人也没有什么能改变它——但它也可能已经消失了。
(最终——默认情况下至少 30 天后——Git 会注意到没有人可以 找到 K
,并真正将其剥离, 但你也不会注意到这一点。)