我实际上在学习Git,我想知道实现“所需状态”的最佳做法是什么:
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还没有准备好在生产中合并。
我已经阅读了有用的stackoverflow帖子:How to merge a specific commit in Git使用git cherry-pick
但是我不太确定我是否应该关注“这种更改提交ID会破坏git的合并功能除其他外“?我是否需要更改我的提交愿景,以便不重现这种情况?
当我的开发完成后,我计划将Dev的修改合并为Master:
Future desired state:
Master
A-----B------C------D------E------F
答案 0 :(得分:3)
如评论中所述,理解提交和分支之间的松散关系非常重要。提交要么可以从分支到达,要么不能;它可以从许多分支机构到达。它是在签出特定分支时创建的,这一事实并没有赋予它与该分支的任何特殊关系。
您已指出的是,您希望在C
中所做的更改也应用于dev
分支。有几种方法可以做到这一点。首先让我们重新绘制你当前的状态"用更好地反映git概念的符号
A-----B------C <--(master)
\
\
D------E------F <--(dev)
我们在此处看到master
是一个当前指向C
的引用。 A
和B
最初可能是在master
上创建的,当然可以从master
(通过提交父指针)访问;但他们与master
的关系并不强于此。实际上,它们也可以从dev
到达,并且与当前状态下dev
的{{1}}具有完全相同的关系;这是查看为什么master
反映您在创建提交时所做的事情的一种方式。
无论如何,dev
是一个指向dev
的引用,F
,A
,B
和D
也可以从中找到E
现在还有一些额外的问题,如何你的期望状态&#34;绘制;我将为您的工作制定一些选项,希望这些概念更加清晰。
因此,一个选项是重新定位dev
,使其包含C
。从技术上讲,这意味着您创建了新的提交(D'
,E'
和F'
),这些提交是&#34;重播&#34;从D
,E
和F
到C
的更改。
git rebase master dev
会给你
A-----B------C <--(master)
\
\
D'------E'------F' <--(dev)
请注意,使用此选项,您不需要C'
,因为您只需要C
&#34;可以访问&#34;来自dev
。但是,无法更改D
的父级,因此您需要创建D'
(然后这会强制您创建E'
来代替E
等等。)
值得注意的是,D'
和E'
是未经测试的代码状态。 (所以F'
也是如此,但你可能会测试它。)你可能会或可能不会回过头来测试这些生成的中间快照,但是如果你不这样做可能会影响未来的跟踪工作记下一个错误(例如使用bisect
)。如果D'
和E'
不足以进行测试,那么它们可能并不重要,无法保留;所以在这种情况下你应该考虑以交互方式进行rebase,squashing
提交E
和F
,给予
A-----B------C <--(master)
\
\
DEF <--(dev)
(其中DEF
是通过重播D
,E
和F
的更改而产生的单个提交。
如果您希望将dev
纳入master
,假设master
之间没有进一步的变化,您可以选择&#34;快进&#34; master
;事实上,如果你说
git checkout master
git merge dev
从这种情况来看。这会给你
A-----B------C------D'------E'------F' <--(dev)(master)
(或
A-----B------C------DEF <--(dev)(master)
如果您使用了交互式rebase来压缩dev
提交。这是许多人喜欢它的简单线性的结果。另一种选择是
git checkout master
git merge --no-ff dev
给你
A-----B------C --------------------M <--(master)
\ /
\ /
D'------E'------F' <--(dev)
其中M
与TREE
具有相同的F'
(内容状态)(因为master
没有在{{dev
中已经考虑过的更改1}})。 (如果你在最初的rebase期间被压扁,D'
...... F'
将被DEF
替换。)这有点不寻常,因为rebase
通常是--no-ff
用于设置快进以便您拥有线性历史记录。如果您想保留分支拓扑(这就是您使用C
的原因),您可以选择另一种方法来反映dev
master
中的dev
更改首先。
回到当前状态,而不是重新定位你可以从git checkout dev
git merge master
合并到A-----B--------------------C <--(master)
\ \
\ \
D------E------F------M <--(dev)
C
这会给你
dev
同样,这使得M
中的master
更改可以在不重复提交的情况下进行更改。实际上,您不会以这种方式复制或替换任何现有提交,如果现有提交已与其他开发人员共享(例如,推送到原点),则会特别好。
但是在这里你创建了一个合并提交(A-----B--------------------C
\ \
\ \
D------E------F------M <--(dev)(master)
),有些人认为这些&#34;向后合并&#34;承诺是丑陋的混乱。这是一种可以理解的批评;假设你这样做了,然后测试了合并后的结果,然后立即合并回M
;你要么允许快进又要得到
--first-parent
看起来没问题,但是A-----B--------------------C-----M2 <--(master)
\ \ /
\ \ /
D------E------F------M <--(dev)
的父母没有按照通常预期的顺序列出,这可能会让使用git checkout dev
git reset --hard HEAD^
的人混淆导航历史...
或禁止快进并获取
M
看起来有点奇怪。
避免这些结果的一个工作流程是考虑&#34;向后合并&#34;临时(仅用于测试)并在测试完成后撤消它。
dev
这再次意味着你最终会存储可能未经测试的代码状态。此外,如果master
有任何冲突,那么通过这样做就会失去对这些冲突的解决;如果git rerere
没有立即准备重新集成到C'
,您可能会重复重新执行该解决方案。 C
可用于缓解此问题。
fwiw,我的偏好是接受合并提交&#34;杂乱&#34;因为它通常不会伤害任何东西(git可以帮助你将其从日志输出中过滤掉)。
无论如何,您发现我们仍然无法创建master
(因为dev
并不属于C
&#34 ;以任何方式阻止我们将其直接纳入C'
)。在dev
&#34;上重播master
中的更改为新的dev
提交&#34; 是一个选项,但我建议不要这样做,因为重复提交只会产生不必要的合并冲突的风险。如果你真的想这样做,你可以使用樱桃采摘,或者在你的网站的特定情况下,使用所谓的&#34;壁球合并&#34;将{{1}}改为{{1}}。 (还有其他方法。)
答案 1 :(得分:2)
第一个看起来像你正在寻找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应用于主人的想法是什么?然而,对于这种情况,正如您已经提到的,git cherry-pick似乎是合适的(git cherry-pick F
)。通常情况下,git cherry-pick
不会导致任何问题(至少在&#34;标准&#34;我使用它的情况下)。