GIT rebase 需要重新提交更改
GIT rebase requires to recommit changes
首先,我是 git 分支的新手。我不知道每个功能分支都应该从 master
分支出来,并且只使用与下一个功能分支有这种关系的先决条件功能分支。
我有三个分支。 master
、feature-1
和 feature-2
都推送了一个 Bitbucket 存储库(启用了问题跟踪)。问题是提交 M4
和 M5
是关键提交,所有分支都应在提交合并之前对其进行变基(git rebase
的任务)
M1 -- M2 -- M3 -- M4 -- M5 [master]
\ /
A1 --- A2 [feature-1]
\
B1 -- B2 -- B3 [feature-2]
feature-2
的开发已经完成,现在需要合并到 master。这是我为 feature-2
变基 M4
和 M5
提交到 feature-2
.
的任务的优先级
git push
- feature-2
的发展
git checkout feature-2
git rebase master
- 解决冲突
git pull
执行完这些步骤后,我在执行 git status
后注意到了。我必须再次推送所有提交(feature-2
、M4
、M5
和冲突提交)。好吧,我所要做的就是 git push
并发起拉取请求,我就完成了,对吧?但这会向问题跟踪器添加另一个 git 提交评论。
有没有办法将 feature-2
变基为 master
而无需再次推送 feature-2
、M4
和 M5
的提交以及git log
应该包含冲突提交。
更新
- 更改问题详细信息以获得更好的说明
Is there a way to rebase feature-2
to master
without the need to push again the commits of feature-2
不是真的,考虑到 rebase 将在 master 之上重播 feature-2 的提交,给你这个:
M1 -- M2 -- M3 -- M4 -- M5 [master]
\ / \
A1 --- A2 [feature-1] A1' --- A2'
\
B1' -- B2' -- B3' [feature-2]
如 '
符号所示,所有提交都已更改(它们的 SHA1 不同)。
因为 A1
和 A2
已经合并到 master
,更好的 rebase 应该是
git rebase --onto master A2 feature-2
那会得到:
M1 -- M2 -- M3 -- M4 -- M5 [master]
\ / \
A1 --- A2 [feature-1] B1' -- B2' -- B3' [feature-2]
您应该 git push --force
新修订的 feature-2
分支以更新您的拉取请求。
强制推送应该更新 Bitbucket 中的拉取请求,因为它自 2012 年第 4 季度起支持此类推送 (issue 4913)。
强制推送不会产生任何其他不利影响,考虑到您正在将分支推送到 您的 分支,据推测,您是唯一推送更新的人。
由于拉取请求将使用 feature-2
的新历史自动更新自身,这不会损害所述拉取请求。
既然你已经推送了两个功能分支,你根本不应该变基。强烈建议不要对已发布的分支进行变基,因为它会破坏其他开发人员的存储库。原因是变基只是提交的 完全重写 。您要重新设置基准的提交会重新创建,内容会发生变化,而且——最重要的是——会使用不同的哈希值。这会导致新提交与旧提交不兼容,因此拥有旧提交的人最终会与替换它们的新提交发生冲突。
正确的解决方案是简单地合并更改。虽然这可能最终看起来不那么漂亮,但它是一个 non-destructive 操作,其中没有更改现有的提交。所发生的只是提交 添加 这将不会导致推或拉时出现问题。
也就是说,您 可以 变基并仍然发布更改的分支。但要做到这一点,您将需要 force-push 分支,而其他提取这些更改的开发人员将需要将他们的分支重置为新版本。
将我在下面的一些评论合并到答案中:重要的是要了解在 Git 中,分支只是指向提交的指针。整个历史——没有分支——是一个大的非循环图,其中提交只指向他们的 parent 提交。因此,以问题中的示例为例,这是历史,没有任何分支指针:
A -- B -- C -- D -- E
\ /
F --- G
\
H -- I -- J
每个字母代表一个提交,所有与左边相关的提交都是它的parent。因此,例如 F
的 parent 是 A
,而 C
是 parent 的 B
和 [=22= 的合并提交].
现在,如果我们向该可视化添加分支,那么我们只需添加指向某些提交的指针。真的没什么别的(分支实际上只是一个包含提交哈希的文件):
master
↓
A -- B -- C -- D -- E
\ /
F --- G ← feature-1
\
H -- I -- J
↑
feature-2
现在,假设我们提交到 feature-2
分支。我们将该提交添加到树中......
\
H -- I -- J -- K
↑
feature-2
…然后我们将分支指针向前移动一位:
\
H -- I -- J -- K
↑
feature-2
现在,要了解推送期间发生的情况,我们需要记住远程分支也只是分支,因此只是另一组指针。所以它实际上看起来像这样:
\
H -- I -- J ----------- K
↑ ↑
origin/feature-2 feature-2
我想您现在可以想象在推送期间会发生什么:我们告诉远程存储库更新其分支指针,使其指向 K
。但是服务器只有 J
,因此我们需要为服务器提供所有内容以构建可由 K
访问的树(因此中间的任何其他提交,以及这些提交的所有实际内容)。但当然我们不需要物理上推动 J
,或 H
,甚至 A
(尽管这些都是 技术上 feature-2
分支,因为你可以 到达 他们); Git 足够聪明,可以找出实际上缺少哪些 objects(您可以在开始推送时看到 Git 计算)。
所以一旦我们将丢失的 objects 传输到远程存储库,我们就会告诉远程存储库更新它的 feature-1
指针,所以它也会指向 K
。如果成功,我们还会更新我们的远程分支 (origin/feature-2
) 以指向它(只是为了同步)。
现在,合并的情况确实是一样的。想象一下,我们将 master
合并到 feature-2
(在 feature-2
上使用 git merge master
):
master
↓
A -- B -- C -- D -- E -----
\ / \
F --- G ← feature-1 \
\ \
H -- I -- J -- K -- L
↑
feature-2
现在,如果我们想要推送 feature-2
,我们需要再次为远程存储库提供它没有的所有 objects。由于我们现在正在进行合并提交,我们需要检查 all parents:因此,如果服务器没有 K
,我们将需要推送K
;而且,如果它没有 E
,我们将不得不推送 E
。当然,我们需要再次跟踪那些 parent,以确保所有 objects 都存在于遥控器上。一旦完成,我们只需再次告诉遥控器更新分支指针。
因此总结一下:分支 包含 所有提交,这些提交可以通过在非循环树中导航其提交的 parent 以某种方式访问。但即使这意味着分支通常非常“大”(在历史长度上),Git 只会将那些 objects 转移到它没有的远程存储库。因此,尽管合并可以向分支添加更多提交,但如果远程已经从另一个分支知道它们,则不一定必须传输这些提交。
最后,关于变基的一些最后的话:上面我们git merge master
将master
分支合并到feature-2
中。如果我们改为 git rebase master
,那么完整的树现在看起来像这样:
master feature-2
↓ ↓
A -- B -- C -- D -- E -- H' -- I' -- J' -- K'
\ /
F --- G ← feature-1
\
H -- I -- J -- K
↑
origin/feature-2
如您所见,有新提交 H'
、I'
、J'
和 K'
。这些是重写的提交,因此它们从 E
(其中 master
在变基时指向)而不是 G
开始。由于我们重新定位,因此没有合并提交 L
。如上所述,原始提交仍然存在。只是没有指针指向它们;所以它们“丢失”了,最终会被垃圾收集。
那么现在推送有什么问题呢?远程分支仍然指向原来的 K
,但我们希望它现在指向 K'
。所以我们开始为远程存储库提供它需要的所有 objects ,就像以前一样。但是当我们告诉它设置更新分支指针时,它会拒绝这样做。这样做的原因是,通过将指针设置为 K'
,它必须“回到历史”并忽略提交 H
到 K
的存在。它不知道我们已经对它们进行了 rebase,并且重写的和原始的之间也没有 link。所以为了防止数据意外丢失,远程会拒绝更新分支指针。
现在,您可以强制推送 分支。这将告诉远程存储库更新分支指针,即使这样做会丢弃那些原始提交。所以你这样做,情况将是这样的:
origin/feature-2
master feature-2
↓ ↓
A -- B -- C -- D -- E -- H' -- I' -- J' -- K'
\ /
F --- G ← feature-1
到目前为止,一切都很好:您决定对分支进行变基,并告诉远程存储库接受它而不质疑它。但现在想象一下,我想 拉 那个;我的分支仍然指向 I
。所以 运行 pull 与反向 push 的作用相同:遥控器为我提供了完成历史记录所需的所有 objects,然后它告诉我在哪里设置分支指针。那时,我的本地 Git 拒绝这样做,原因与远程存储库之前这样做的原因相同。
通过之前的推送,我们知道我们想要替换原来的提交;但是我们没有那个,所以我们现在需要调查或四处询问,我们是否应该只更换我们的本地分支,或者远程是否确实存在一些故障。如果我们在本地自己完成了一些我们现在想合并的工作,情况会变得更糟。
这些问题发生在每个曾经获取过那些原始提交的人身上。一般来说,你想完全避免这种混乱,所以规则是永远不要 rebase 已经发布的东西。只要没有其他人获得这些原始提交,您就可以变基,但一旦情况不再如此,对所有相关人员来说都会一团糟。所以合并肯定是首选。
据我了解,您的代码库(团队)的规则要求您根据 master
重新设置您的功能分支。你可以通过说 git rebase --onto master A2 feature-2
来做到这一点,这意味着 "take commits of the feature-2 starting with A2 exclusive and place them on top of the master"。然后,您可以将更改直接推送到 master
并删除或保持 feature-2
分支不变,同样取决于工作流约定。
如果另一方面,不需要变基 - 您可以将 feature-2
简单合并到 master
,将更改推送到 master
,因为@poke推荐。
首先,我是 git 分支的新手。我不知道每个功能分支都应该从 master
分支出来,并且只使用与下一个功能分支有这种关系的先决条件功能分支。
我有三个分支。 master
、feature-1
和 feature-2
都推送了一个 Bitbucket 存储库(启用了问题跟踪)。问题是提交 M4
和 M5
是关键提交,所有分支都应在提交合并之前对其进行变基(git rebase
的任务)
M1 -- M2 -- M3 -- M4 -- M5 [master]
\ /
A1 --- A2 [feature-1]
\
B1 -- B2 -- B3 [feature-2]
feature-2
的开发已经完成,现在需要合并到 master。这是我为 feature-2
变基 M4
和 M5
提交到 feature-2
.
git push
-feature-2
的发展
git checkout feature-2
git rebase master
- 解决冲突
git pull
执行完这些步骤后,我在执行 git status
后注意到了。我必须再次推送所有提交(feature-2
、M4
、M5
和冲突提交)。好吧,我所要做的就是 git push
并发起拉取请求,我就完成了,对吧?但这会向问题跟踪器添加另一个 git 提交评论。
有没有办法将 feature-2
变基为 master
而无需再次推送 feature-2
、M4
和 M5
的提交以及git log
应该包含冲突提交。
更新
- 更改问题详细信息以获得更好的说明
Is there a way to rebase
feature-2
tomaster
without the need to push again the commits offeature-2
不是真的,考虑到 rebase 将在 master 之上重播 feature-2 的提交,给你这个:
M1 -- M2 -- M3 -- M4 -- M5 [master]
\ / \
A1 --- A2 [feature-1] A1' --- A2'
\
B1' -- B2' -- B3' [feature-2]
如 '
符号所示,所有提交都已更改(它们的 SHA1 不同)。
因为 A1
和 A2
已经合并到 master
,更好的 rebase 应该是
git rebase --onto master A2 feature-2
那会得到:
M1 -- M2 -- M3 -- M4 -- M5 [master]
\ / \
A1 --- A2 [feature-1] B1' -- B2' -- B3' [feature-2]
您应该 git push --force
新修订的 feature-2
分支以更新您的拉取请求。
强制推送应该更新 Bitbucket 中的拉取请求,因为它自 2012 年第 4 季度起支持此类推送 (issue 4913)。
强制推送不会产生任何其他不利影响,考虑到您正在将分支推送到 您的 分支,据推测,您是唯一推送更新的人。
由于拉取请求将使用 feature-2
的新历史自动更新自身,这不会损害所述拉取请求。
既然你已经推送了两个功能分支,你根本不应该变基。强烈建议不要对已发布的分支进行变基,因为它会破坏其他开发人员的存储库。原因是变基只是提交的 完全重写 。您要重新设置基准的提交会重新创建,内容会发生变化,而且——最重要的是——会使用不同的哈希值。这会导致新提交与旧提交不兼容,因此拥有旧提交的人最终会与替换它们的新提交发生冲突。
正确的解决方案是简单地合并更改。虽然这可能最终看起来不那么漂亮,但它是一个 non-destructive 操作,其中没有更改现有的提交。所发生的只是提交 添加 这将不会导致推或拉时出现问题。
也就是说,您 可以 变基并仍然发布更改的分支。但要做到这一点,您将需要 force-push 分支,而其他提取这些更改的开发人员将需要将他们的分支重置为新版本。
将我在下面的一些评论合并到答案中:重要的是要了解在 Git 中,分支只是指向提交的指针。整个历史——没有分支——是一个大的非循环图,其中提交只指向他们的 parent 提交。因此,以问题中的示例为例,这是历史,没有任何分支指针:
A -- B -- C -- D -- E
\ /
F --- G
\
H -- I -- J
每个字母代表一个提交,所有与左边相关的提交都是它的parent。因此,例如 F
的 parent 是 A
,而 C
是 parent 的 B
和 [=22= 的合并提交].
现在,如果我们向该可视化添加分支,那么我们只需添加指向某些提交的指针。真的没什么别的(分支实际上只是一个包含提交哈希的文件):
master
↓
A -- B -- C -- D -- E
\ /
F --- G ← feature-1
\
H -- I -- J
↑
feature-2
现在,假设我们提交到 feature-2
分支。我们将该提交添加到树中......
\
H -- I -- J -- K
↑
feature-2
…然后我们将分支指针向前移动一位:
\
H -- I -- J -- K
↑
feature-2
现在,要了解推送期间发生的情况,我们需要记住远程分支也只是分支,因此只是另一组指针。所以它实际上看起来像这样:
\
H -- I -- J ----------- K
↑ ↑
origin/feature-2 feature-2
我想您现在可以想象在推送期间会发生什么:我们告诉远程存储库更新其分支指针,使其指向 K
。但是服务器只有 J
,因此我们需要为服务器提供所有内容以构建可由 K
访问的树(因此中间的任何其他提交,以及这些提交的所有实际内容)。但当然我们不需要物理上推动 J
,或 H
,甚至 A
(尽管这些都是 技术上 feature-2
分支,因为你可以 到达 他们); Git 足够聪明,可以找出实际上缺少哪些 objects(您可以在开始推送时看到 Git 计算)。
所以一旦我们将丢失的 objects 传输到远程存储库,我们就会告诉远程存储库更新它的 feature-1
指针,所以它也会指向 K
。如果成功,我们还会更新我们的远程分支 (origin/feature-2
) 以指向它(只是为了同步)。
现在,合并的情况确实是一样的。想象一下,我们将 master
合并到 feature-2
(在 feature-2
上使用 git merge master
):
master
↓
A -- B -- C -- D -- E -----
\ / \
F --- G ← feature-1 \
\ \
H -- I -- J -- K -- L
↑
feature-2
现在,如果我们想要推送 feature-2
,我们需要再次为远程存储库提供它没有的所有 objects。由于我们现在正在进行合并提交,我们需要检查 all parents:因此,如果服务器没有 K
,我们将需要推送K
;而且,如果它没有 E
,我们将不得不推送 E
。当然,我们需要再次跟踪那些 parent,以确保所有 objects 都存在于遥控器上。一旦完成,我们只需再次告诉遥控器更新分支指针。
因此总结一下:分支 包含 所有提交,这些提交可以通过在非循环树中导航其提交的 parent 以某种方式访问。但即使这意味着分支通常非常“大”(在历史长度上),Git 只会将那些 objects 转移到它没有的远程存储库。因此,尽管合并可以向分支添加更多提交,但如果远程已经从另一个分支知道它们,则不一定必须传输这些提交。
最后,关于变基的一些最后的话:上面我们git merge master
将master
分支合并到feature-2
中。如果我们改为 git rebase master
,那么完整的树现在看起来像这样:
master feature-2
↓ ↓
A -- B -- C -- D -- E -- H' -- I' -- J' -- K'
\ /
F --- G ← feature-1
\
H -- I -- J -- K
↑
origin/feature-2
如您所见,有新提交 H'
、I'
、J'
和 K'
。这些是重写的提交,因此它们从 E
(其中 master
在变基时指向)而不是 G
开始。由于我们重新定位,因此没有合并提交 L
。如上所述,原始提交仍然存在。只是没有指针指向它们;所以它们“丢失”了,最终会被垃圾收集。
那么现在推送有什么问题呢?远程分支仍然指向原来的 K
,但我们希望它现在指向 K'
。所以我们开始为远程存储库提供它需要的所有 objects ,就像以前一样。但是当我们告诉它设置更新分支指针时,它会拒绝这样做。这样做的原因是,通过将指针设置为 K'
,它必须“回到历史”并忽略提交 H
到 K
的存在。它不知道我们已经对它们进行了 rebase,并且重写的和原始的之间也没有 link。所以为了防止数据意外丢失,远程会拒绝更新分支指针。
现在,您可以强制推送 分支。这将告诉远程存储库更新分支指针,即使这样做会丢弃那些原始提交。所以你这样做,情况将是这样的:
origin/feature-2
master feature-2
↓ ↓
A -- B -- C -- D -- E -- H' -- I' -- J' -- K'
\ /
F --- G ← feature-1
到目前为止,一切都很好:您决定对分支进行变基,并告诉远程存储库接受它而不质疑它。但现在想象一下,我想 拉 那个;我的分支仍然指向 I
。所以 运行 pull 与反向 push 的作用相同:遥控器为我提供了完成历史记录所需的所有 objects,然后它告诉我在哪里设置分支指针。那时,我的本地 Git 拒绝这样做,原因与远程存储库之前这样做的原因相同。
通过之前的推送,我们知道我们想要替换原来的提交;但是我们没有那个,所以我们现在需要调查或四处询问,我们是否应该只更换我们的本地分支,或者远程是否确实存在一些故障。如果我们在本地自己完成了一些我们现在想合并的工作,情况会变得更糟。
这些问题发生在每个曾经获取过那些原始提交的人身上。一般来说,你想完全避免这种混乱,所以规则是永远不要 rebase 已经发布的东西。只要没有其他人获得这些原始提交,您就可以变基,但一旦情况不再如此,对所有相关人员来说都会一团糟。所以合并肯定是首选。
据我了解,您的代码库(团队)的规则要求您根据 master
重新设置您的功能分支。你可以通过说 git rebase --onto master A2 feature-2
来做到这一点,这意味着 "take commits of the feature-2 starting with A2 exclusive and place them on top of the master"。然后,您可以将更改直接推送到 master
并删除或保持 feature-2
分支不变,同样取决于工作流约定。
如果另一方面,不需要变基 - 您可以将 feature-2
简单合并到 master
,将更改推送到 master
,因为@poke推荐。