我正在同时处理同一个仓库中的两个功能,并且我已经意识到一个分支中的功能在另一个分支(在同一个文件中)中会很有用。第一个分支不是100%完成,所以我不想将那个合并/重新组合到第二个分支上。我真的只需要一小块代码。
显然,我可以复制和粘贴,但是这不会保留提交历史记录,我想,当我将它们合并到主服务器中时,它可能会导致合并冲突。
我需要的功能分散在多个提交中,其中还有其他更改,因此我不能轻易地挑选构成该代码块的提交。
我也不能做一个交互式的rebase并改变第一个分支的历史(让我更容易挑选我需要的一点代码),因为除了大量的代码之外,我和#39;我已经将我的代码推送到GitHub,并且在我可以合并到master之前等待代码审查,所以在我的同事正在审查我的代码时,我不应该重写历史记录。
那么,我最好的选择是什么?这种感觉就像在这种情况下我需要一些疯狂的git-fu来保持良好和干净。
我能想到的唯一另一个选择是在本地进行交互式rebase,而不是推送到GitHub,然后挑选一个包含我需要的函数的提交,然后reset
我的第一个分支回到远程分支。但这感觉就像一个不太理想的解决方案,而且我不确定与简单地复制和粘贴代码真的会有很大不同。
答案 0 :(得分:1)
显然,我可以复制并粘贴,但这不会保留提交历史...
正确。历史是提交;复制代码或文件,并进行新的提交,创造了新的历史。
我认为,当我将它们合并为主时,它可能会导致合并冲突。
也许。幸运的是,没有。这里的关键是合并通过组合两个单独的差异来工作。你现在拥有的是这样的:
A--B--C--D <-- yourbranch
/
...--o--* <-- master, origin/master
(根据需要将master
替换为branch
)。在这里&#34;你的承诺&#34;是A
到D
的标记,构成A--B--C--D
链。
如果你复制(全部)你的提交,这就是你得到的:
A--B--C--D <-- yourbranch
/
...--o--* <-- master, origin/master
\
A'-B'-C'-D' <-- yourcopy
现在,git merge
的工作方式如下。我们假设您在master
并且正在合并yourbranch
:
tip1=$(git rev-parse master)
tip2=$(git rev-parse yourbranch)
base=$(git merge-base $tip1 $tip2)
完成此操作后,tip2
指向提交D
,而tip1
指向当前分支master
的提示,即提交*
。同时base
指向这两个提交的合并基础。这是两个分支加入的第一个提交,即...再次提交*
!
不以某种方式强制合并(--no-ff
或--ff-only
)的合并将检查合并基础是否与当前提示相同(tip1或commit *
) 。由于是,因此该合并将变为快进,而根本不是合并。
强制--no-ff
的合并继续并进行真正的合并,即使它是微不足道的。在这种情况下,我们将得到:
A--B--C--D <-- yourbranch
/ \
...--o--*------------M <-- master, origin/master
\
A'-B'-C'-D' <-- yourcopy
( 强制--ff-only
的合并仍然会检查。如果它无法执行快进,则只会失败。)
目前,我们假设我们有强制合并M
。
现在,假设您已复制yourcopy
并再次提交E
:
A--B--C--D <-- yourbranch
/ \
...--o--*------------M <-- master, origin/master
\
A'-B'-C'-D'-E <-- yourcopy
你现在可以向Git询问git merge yourcopy
,它会做同样的事情:
tip1=$(git rev-parse master) # commit M
tip2=$(git rev-parse yourcopy) # commit E
base=$(git merge-base $tip1 $tip2)
master
和yourcopy
在哪里加入?向后关注提交,直到两个流加入:再次提交*
。因此合并基础是提交*
。现在我们有一个非平凡的合并,所以Git必须做两个差异:
git diff $base $tip1 # * vs M: basically, A+B+C+D as a patch
git diff $base $tip2 # A'+B'+C'+D'+E as a patch
合并代码现在尽力合并这两个补丁,每次更改 。但A+B+C+D
与A'+B'+C'+D'
完全相同,因此这两个补丁之间的唯一区别基本上只是提交E
。在&#34;好&#34;案例 - 最终令人惊讶的共同点 - 这里很少甚至没有合并冲突。
即使您只复制提交的部分,情况也是如此:
A--B--C--D <-- yourbranch
/ \
...--o--*------------M <-- master, origin/master
\
B'-D'-E <-- yourcopy
一侧&#34;差异&#34;基本上是A+B+C+D
,而另一方面的差异是#{1}}。 B'+D'+E
只是B+D+E
。 Git可能(并且通常)注意到B'
和D'
已经在那里,并且只进行了E
的更改。
即使原始&#34;合并&#34;所有这一切仍然是正确的。是一个快进的人。我们只是将中间提交图重绘为:
A--B--C--D <-- yourbranch, master, origin/master
/
...--o--*
\
A'-B'-C'-D'-E <-- yourcopy
合并基础仍为*
,一切正常。如果您现在进行合并(当前分支设置为master
),则得到:
A--B--C--D <-- yourbranch, origin/master
/ \
...--o--* ---M <-- master
\ /
A'-B'-C'-D'-E <-- yourcopy
如果您在合并A'-B'-C'-D'-E
上进行合并M2
,则会得到一个类似的图表,其中包含M
序列。在这两种情况下,唯一的烦恼是复制的提交保留在存储库中,因此当您运行git log
时,您会看到它们两次。
当然,在这种情况下,您只需在E
上添加D
:
E <-- feature2
/
A--B--C--D <-- yourbranch
/
...--o--* <-- master, origin/master
这将是要走的路:没有复制的提交,一切都很好(虽然feature2
现在取决于yourbranch
)。
你真正拥有的东西看起来更像是这样,毫无疑问:
A--B--C--D <-- yourbranch
/
...--o--* <-- master, origin/master
\
E--F--G <-- feature2
您现在已经注意到,您希望A-B-C-D
中的feature2
序列中的部分或全部代码,,就好像您已重新定位feature2
顶部yourbranch
。但是,您可以再次git cherry-pick
尽可能多地进行A'
,B'
,C'
和/或D'
提交。您可以根据需要对这些提交进行重组(rebase -i
并重新组织),但我们只需将B
和D
添加为副本即可:
A--B--C--D <-- yourbranch
/
...--o--* <-- master, origin/master
\
E--F--G--B'-D' <-- feature2
和以前一样,合并将尝试不复制代码(尽管来自额外提交副本的历史将显示)。但是,让我们说你以后会在你的(合并)feature2
- 通过 - A
上面的 rebase D
获得批准。当你这样做时会发生什么更好,因为git rebase
- 通过复制提交工作,与git cherry-pick
相同 - 实际上搜索已经复制的提交并将其保留。因此,让我们说master
和origin/master
快速转发以包含D
,或D
合并进来 - 无论发生什么都没关系,只要提交A
到D
现在全部 on master
(即使是通过合并提交):
A--B--C--D <-- yourbranch
/ \
...--o--*------------M <-- master, origin/master
\
E--F--G--B'-D' <-- feature2
现在您结帐feature2
并运行git rebase master
。你的git找到了提交E-F-G-B'-D'
并设置为复制它们,但是当它复制时,它会检查:这个提交已经在那里了吗?此检查不是通过提交哈希 - 此检查将失败 - 而是通过修补程序哈希(有关详细信息,请参阅the git patch-id
documentation)。
由于B'
是B
的副本(因此具有相同的修补程序ID),D'
是D
的副本,rebase
份只有E-F-G
,给予:
A--B--C--D <-- yourbranch
/ \
...--o--*------------M <-- master, origin/master
\ \
\ E'-F'-G' <-- feature2
\
E--F--G--B'-D' [old feature2, now abandoned]
掩盖&#34;放弃&#34;提交(用你的手阻挡它们,例如:-))看起来Git已经神奇地完成了你想要的东西。 (它并不完全是魔术 - 它只是补丁ID - 但它可能就是你想要的。当然,如果你被迫重做B
和/或D
,最终进入的重新制作的作品可能不再与你制作的副本相匹配,然后你会真正看到缺乏魔法。)
答案 1 :(得分:1)
如果在另一个答案中更改原始版本是不合适的,则可以将该函数复制并粘贴到具有不同名称的文件中,以便将来合并时不会发生复杂的冲突。您只需要删除临时文件。根据您的语言,由于重复的定义,您可能会因编译错误而注意到它。
答案 2 :(得分:0)
将该函数的实现提取到一个单独的分支中,将其合并到集成分支中,然后在最新的集成分支之上进行rebase。现在,在与最初实现该功能的分支进行一些冲突解决后,您现在可以在两个分支中使用该功能。