如何在 Dev 处于 head 状态时从 Master 添加一个 "behind" commit 到 Dev?
How to add one "behind" commit from Master to Dev where Dev is in head state?
我实际上正在学习 Git,我想知道实现 "Desired state" 的最佳实践是什么:
Actual state:
Master A-----B------C
\
\
Dev D------E------F
Desired state:
Master
A-----B------C
|
|
Dev C------D------E------F
仅供参考:我在 Master/Commit-B 上生产,在 Dev/Commit-D-E-F 上开发。我已经看到我想对生产进行更改,但 Dev/Commit-D-E-F 还没有准备好合并到生产中。
我读过有用的 Whosebug post:How to merge a specific commit in Git 使用 git cherry-pick
但我不太确定我是否应该关注 "This changing of commit IDs breaks git's merging functionality among other things"?我是否需要更改我的提交愿景以不重现这种情况?
当我的开发完成后,我计划将 Dev 中的修改合并到 Master 中:
Future desired state:
Master
A-----B------C------D------E------F
第一个看起来像你在找 git rebase。
在 Dev
时:git rebase master
.
Master A-----B------C
\
\
Dev D------E------F
After Rebase
Master A-----B------C
\
\
Dev D------E------F
但是,我没有得到你的第二张图表。开发D、E和F然后只将F应用到master上是什么想法?然而,对于那种情况,正如您已经提到的,git cherry-pick 似乎是合适的 (git cherry-pick F
)。通常,git cherry-pick
不会造成任何问题(至少在我使用它的 "standard" 案例中)。
如评论中所述,了解提交和分支之间的松散关系很重要。一个提交要么可以从一个分支到达,要么不能;它可能可以从许多分支机构到达。它是在签出特定分支时创建的,这一事实并没有赋予它与该分支的任何特殊关系。
您所指出的是您希望在 C
中所做的更改也应用于 dev
分支。有几种方法可以做到这一点。首先让我们 re-draw 你的 "current state" 用一种更好地反映 git 概念的符号
A-----B------C <--(master)
\
\
D------E------F <--(dev)
这里我们看到 master
是一个当前指向 C
的引用。 A
和 B
可能最初是在 master
上创建的,并且肯定可以从 master
访问(通过提交 parent 指针);但他们与master
的关系也不过如此。事实上,它们也可以从 dev
到达,并且与 dev
的关系与它们在当前状态下与 master
的关系完全相同;这是查看为什么 dev
反映了您在创建这些提交时所做的事情的一种方式。
总之,dev
是指向F
的ref,从中A
、B
、D
、E
也可达。
现在您的 "desired state" 的绘制方式还有一些其他问题;我将针对您可以做的事情拟定几个选项,希望这些概念会更加清晰。
因此,一种选择是变基 dev
,以便它包含 C
。从技术上讲,这意味着您创建了新的提交(D'
、E'
和 F'
),这些提交是 "replaying" 来自 D
、[=42] 的更改的结果=], F
到 C
.
git rebase master dev
会给你
A-----B------C <--(master)
\
\
D'------E'------F' <--(dev)
请注意,使用此选项,您不需要 C'
,因为您只是从 dev
生成 C
"reachable"。但是,不可能更改 D
的 parent,因此您创建 D'
(然后这反过来又迫使您创建 E'
代替 E
, 等等)
值得注意的是,D'
和 E'
是代码的未测试状态。 (F'
也是如此,但无论如何您可能都会对其进行测试。)您可能会或可能不会回头测试这些生成的中间快照,但如果您不这样做,可能会干扰未来追踪错误(例如使用 bisect
)。如果 D'
和 E'
不够重要,无法测试,那么它们可能不够重要,无法保留;所以在那种情况下你应该考虑交互地做变基并且squashing
提交E
和F
,给
A-----B------C <--(master)
\
\
DEF <--(dev)
(其中 DEF
是重播来自 D
、E
和 F
的更改的单个提交)。
当您想将dev
合并到master
中时,假设master
之间没有进一步的变化,您可以选择"fast forward" master
;事实上,如果你说
,那是默认设置
git checkout master
git merge dev
从这种情况。这会给你
A-----B------C------D'------E'------F' <--(dev)(master)
(或
A-----B------C------DEF <--(dev)(master)
如果您使用交互式变基来压缩 dev
提交)。这是许多人喜欢的结果,因为它具有简单的线性度。另一种选择是
git checkout master
git merge --no-ff dev
这给了你
A-----B------C --------------------M <--(master)
\ /
\ /
D'------E'------F' <--(dev)
其中 M
将具有与 F'
相同的 TREE
(内容状态)(因为 master
没有未在 dev
). (再次 D'
...F'
将被 DEF
替换,如果你在原始变基期间被压扁。)这有点不寻常,因为 rebase
通常是用于设置 fast-forward 以便您拥有线性历史记录。如果你想保留分支拓扑(这就是你使用 --no-ff
的原因),你可能会选择一种不同的方式来首先反映 C
在 dev
中的变化。
所以回到你当前的状态,而不是重新设置你的基础 可以 从 master
合并到 dev
git checkout dev
git merge master
这会给你
A-----B--------------------C <--(master)
\ \
\ \
D------E------F------M <--(dev)
同样,这使得 C
更改可以在 dev
中访问,而无需重复提交。事实上,您不会以这种方式复制或替换 any 现有提交,如果现有提交已与其他开发人员共享(例如,推送到原点),这尤其好。
但是在这里你创建了一个合并提交(M
),有些人认为这些 "backwards merge" 提交是丑陋的混乱。这是一种可以理解的批评;假设你这样做了,然后测试合并结果,然后立即合并回 master
;您要么允许 fast-forward 并获得 get
A-----B--------------------C
\ \
\ \
D------E------F------M <--(dev)(master)
看起来不错,但是 M
的 parent 没有列在 usually-expected 顺序中,这可能会使使用 --first-parent
浏览历史记录的人感到困惑。 .
或禁止fast-forward并获得
A-----B--------------------C-----M2 <--(master)
\ \ /
\ \ /
D------E------F------M <--(dev)
这看起来有点奇怪。
避免这些结果的一个工作流程是考虑 "backwards merge" 临时(仅用于测试)并在测试完成后撤消它。
git checkout dev
git reset --hard HEAD^
这又意味着你最终要存储 probably-untested 合作的状态e.此外,如果 M
有任何冲突,那么您将失去解决这些冲突的方法;如果 dev
没有立即准备好 re-integration 进入 master
,您可能会重复 re-doing 该决议。 git rerere
可缓解此问题。
fwiw,我的偏好是只接受合并提交 "clutter",因为它通常不会造成任何伤害(而且 git 可以帮助您从日志输出中过滤掉它)。
无论如何,您会看到我们仍然不必创建 C'
(因为 C
不会 "belong to master
" 以任何方式阻止我们将其直接合并到 dev
).将 C
中的更改重播到新的 C'
提交 "on dev
" 是 一个选项,但我建议不要这样做,因为重复提交只会产生风险不必要的合并冲突。如果你真的想这样做,你可以使用 cherry-picking 或者,在你站点的特定情况下,master
的 so-called "squash merge" 到 dev
. (还有其他方法。)
我实际上正在学习 Git,我想知道实现 "Desired state" 的最佳实践是什么:
Actual state:
Master A-----B------C
\
\
Dev D------E------F
Desired state:
Master
A-----B------C
|
|
Dev C------D------E------F
仅供参考:我在 Master/Commit-B 上生产,在 Dev/Commit-D-E-F 上开发。我已经看到我想对生产进行更改,但 Dev/Commit-D-E-F 还没有准备好合并到生产中。
我读过有用的 Whosebug post:How to merge a specific commit in Git 使用 git cherry-pick
但我不太确定我是否应该关注 "This changing of commit IDs breaks git's merging functionality among other things"?我是否需要更改我的提交愿景以不重现这种情况?
当我的开发完成后,我计划将 Dev 中的修改合并到 Master 中:
Future desired state:
Master
A-----B------C------D------E------F
第一个看起来像你在找 git rebase。
在 Dev
时:git rebase master
.
Master A-----B------C
\
\
Dev D------E------F
After Rebase
Master A-----B------C
\
\
Dev D------E------F
但是,我没有得到你的第二张图表。开发D、E和F然后只将F应用到master上是什么想法?然而,对于那种情况,正如您已经提到的,git cherry-pick 似乎是合适的 (git cherry-pick F
)。通常,git cherry-pick
不会造成任何问题(至少在我使用它的 "standard" 案例中)。
如评论中所述,了解提交和分支之间的松散关系很重要。一个提交要么可以从一个分支到达,要么不能;它可能可以从许多分支机构到达。它是在签出特定分支时创建的,这一事实并没有赋予它与该分支的任何特殊关系。
您所指出的是您希望在 C
中所做的更改也应用于 dev
分支。有几种方法可以做到这一点。首先让我们 re-draw 你的 "current state" 用一种更好地反映 git 概念的符号
A-----B------C <--(master)
\
\
D------E------F <--(dev)
这里我们看到 master
是一个当前指向 C
的引用。 A
和 B
可能最初是在 master
上创建的,并且肯定可以从 master
访问(通过提交 parent 指针);但他们与master
的关系也不过如此。事实上,它们也可以从 dev
到达,并且与 dev
的关系与它们在当前状态下与 master
的关系完全相同;这是查看为什么 dev
反映了您在创建这些提交时所做的事情的一种方式。
总之,dev
是指向F
的ref,从中A
、B
、D
、E
也可达。
现在您的 "desired state" 的绘制方式还有一些其他问题;我将针对您可以做的事情拟定几个选项,希望这些概念会更加清晰。
因此,一种选择是变基 dev
,以便它包含 C
。从技术上讲,这意味着您创建了新的提交(D'
、E'
和 F'
),这些提交是 "replaying" 来自 D
、[=42] 的更改的结果=], F
到 C
.
git rebase master dev
会给你
A-----B------C <--(master)
\
\
D'------E'------F' <--(dev)
请注意,使用此选项,您不需要 C'
,因为您只是从 dev
生成 C
"reachable"。但是,不可能更改 D
的 parent,因此您创建 D'
(然后这反过来又迫使您创建 E'
代替 E
, 等等)
值得注意的是,D'
和 E'
是代码的未测试状态。 (F'
也是如此,但无论如何您可能都会对其进行测试。)您可能会或可能不会回头测试这些生成的中间快照,但如果您不这样做,可能会干扰未来追踪错误(例如使用 bisect
)。如果 D'
和 E'
不够重要,无法测试,那么它们可能不够重要,无法保留;所以在那种情况下你应该考虑交互地做变基并且squashing
提交E
和F
,给
A-----B------C <--(master)
\
\
DEF <--(dev)
(其中 DEF
是重播来自 D
、E
和 F
的更改的单个提交)。
当您想将dev
合并到master
中时,假设master
之间没有进一步的变化,您可以选择"fast forward" master
;事实上,如果你说
git checkout master
git merge dev
从这种情况。这会给你
A-----B------C------D'------E'------F' <--(dev)(master)
(或
A-----B------C------DEF <--(dev)(master)
如果您使用交互式变基来压缩 dev
提交)。这是许多人喜欢的结果,因为它具有简单的线性度。另一种选择是
git checkout master
git merge --no-ff dev
这给了你
A-----B------C --------------------M <--(master)
\ /
\ /
D'------E'------F' <--(dev)
其中 M
将具有与 F'
相同的 TREE
(内容状态)(因为 master
没有未在 dev
). (再次 D'
...F'
将被 DEF
替换,如果你在原始变基期间被压扁。)这有点不寻常,因为 rebase
通常是用于设置 fast-forward 以便您拥有线性历史记录。如果你想保留分支拓扑(这就是你使用 --no-ff
的原因),你可能会选择一种不同的方式来首先反映 C
在 dev
中的变化。
所以回到你当前的状态,而不是重新设置你的基础 可以 从 master
合并到 dev
git checkout dev
git merge master
这会给你
A-----B--------------------C <--(master)
\ \
\ \
D------E------F------M <--(dev)
同样,这使得 C
更改可以在 dev
中访问,而无需重复提交。事实上,您不会以这种方式复制或替换 any 现有提交,如果现有提交已与其他开发人员共享(例如,推送到原点),这尤其好。
但是在这里你创建了一个合并提交(M
),有些人认为这些 "backwards merge" 提交是丑陋的混乱。这是一种可以理解的批评;假设你这样做了,然后测试合并结果,然后立即合并回 master
;您要么允许 fast-forward 并获得 get
A-----B--------------------C
\ \
\ \
D------E------F------M <--(dev)(master)
看起来不错,但是 M
的 parent 没有列在 usually-expected 顺序中,这可能会使使用 --first-parent
浏览历史记录的人感到困惑。 .
或禁止fast-forward并获得
A-----B--------------------C-----M2 <--(master)
\ \ /
\ \ /
D------E------F------M <--(dev)
这看起来有点奇怪。
避免这些结果的一个工作流程是考虑 "backwards merge" 临时(仅用于测试)并在测试完成后撤消它。
git checkout dev
git reset --hard HEAD^
这又意味着你最终要存储 probably-untested 合作的状态e.此外,如果 M
有任何冲突,那么您将失去解决这些冲突的方法;如果 dev
没有立即准备好 re-integration 进入 master
,您可能会重复 re-doing 该决议。 git rerere
可缓解此问题。
fwiw,我的偏好是只接受合并提交 "clutter",因为它通常不会造成任何伤害(而且 git 可以帮助您从日志输出中过滤掉它)。
无论如何,您会看到我们仍然不必创建 C'
(因为 C
不会 "belong to master
" 以任何方式阻止我们将其直接合并到 dev
).将 C
中的更改重播到新的 C'
提交 "on dev
" 是 一个选项,但我建议不要这样做,因为重复提交只会产生风险不必要的合并冲突。如果你真的想这样做,你可以使用 cherry-picking 或者,在你站点的特定情况下,master
的 so-called "squash merge" 到 dev
. (还有其他方法。)