如何在Master处于头状态的Dev中添加一个“后面”提交?

时间:2017-08-10 16:11:27

标签: git git-commit git-cherry-pick

我实际上在学习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

2 个答案:

答案 0 :(得分:3)

如评论中所述,理解提交和分支之间的松散关系非常重要。提交要么可以从分支到达,要么不能;它可以从许多分支机构到达。它是在签出特定分支时创建的,这一事实并没有赋予它与该分支的任何特殊关系。

您已指出的是,您希望在C中所做的更改也应用于dev分支。有几种方法可以做到这一点。首先让我们重新绘制你当前的状态"用更好地反映git概念的符号

A-----B------C <--(master)
       \ 
        \
         D------E------F <--(dev)

我们在此处看到master是一个当前指向C的引用。 AB最初可能是在master上创建的,当然可以从master(通过提交父指针)访问;但他们与master的关系并不强于此。实际上,它们也可以从dev到达,并且与当前状态下dev的{​​{1}}具有完全相同的关系;这是查看为什么master反映您在创建提交时所做的事情的一种方式。

无论如何,dev是一个指向dev的引用,FABD也可以从中找到E

现在还有一些额外的问题,如何你的期望状态&#34;绘制;我将为您的工作制定一些选项,希望这些概念更加清晰。

因此,一个选项是重新定位dev,使其包含C。从技术上讲,这意味着您创建了新的提交(D'E'F'),这些提交是&#34;重播&#34;从DEFC的更改。

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提交EF,给予

A-----B------C <--(master)
              \ 
               \
                DEF <--(dev)

(其中DEF是通过重播DEF的更改而产生的单个提交。

如果您希望将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)

其中MTREE具有相同的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;我使用它的情况下)。