我想我的问题可能比较常见,它应该有众所周知的解决方案或最佳实践。 不幸的是,我发现和/或尝试的每种解决方案都会产生某种不良副作用。
我的回购基本上看起来像下面这样,即使在我的真实回购中,E和G之间有25个左右的提交,而其他一些分支在两侧突出:
A--B------E--F--G <-- master
\ /
C--D <-- branch_1
Branch_1包含提交B中存在的功能的实施提议,但实现方式不同。决定branch_1中的实施提案应该是正式的,因此branch_1被合并为master。
由于在提交F和G中已经对branch_1进行了多次错误修复,因此一些与该功能相关,而另一些则与此不相关。
事实证明,来自branch_1的实现提议毕竟不是那么大,所以我需要将提交B中的实现提交给master的一个新提交。我还希望明确提交B是新提交的贡献者,例如当我发出git log --graph --oneline --decorate --all
时。我想要的是这样的:
-------------H <-- branch_2
/ /
A--B------E--F--G <-- master
\ /
C--D <-- branch_1
TLDR; 如何创建branch_2并将提交G合并到其中,不包括commit_1在commit E中引入的更改?
请继续阅读我迄今为止尝试过的内容......
我尝试的第一件事就是结帐B,创建一个新的分支&#39; branch_2&#39;并将master合并到branch_2。发生了什么事情是git得出结论,快进是有序的,因为所有后续提交都是新的,所以我最终得到了一个与master相同的branch_2,从而失去了我在commit B中所需要的东西。当然--no-ff
给出了出于同样的原因,结果相同。
我尝试过各种合并策略,除了ours
之外,它们都创建了相同的结果。我希望手动合并零碎,但我希望尽可能减少这种劳动。我试图找到一种方法来使git创建合并冲突,但我总是最终得到一个与master或commit B相同的代码库而没有任何冲突。
由于以这种方式合并似乎并没有解决我的问题,我认为我会选择樱桃。所以我用git reset --hard B
重置了我的branch_2并尝试了git cherry-pick F --no-commit
,因为提交E只是一个合并提交。
生成的代码库包含一个易于解决的合并冲突。但是当我解决冲突时,我意识到我错过了我需要的F代码变化的80%以上。我尝试从E中采摘樱桃而git告诉我使用-m
,但取决于我是否使用&#39; 1&#39;或者&#39; 2&#39;作为主线,我最终遇到了类似于合并时的问题;生成的代码库与B或E相同。
此时我花了很多时间绕过git diff
,interdiff
和rediff/recountdiff
尝试创建补丁,其中B和G之间的差异与E和G之间的差异相比较。我猜这是可能的,但我仍然没有想出如何完成我之后的事情。另一方面,我想git应该能够帮助我。
对我而言,上述是使用版本控制系统/软件的主要目的;我可以回到我的开发历史中,恢复我需要的代码片段。 我确定这是关于我的git技能有限,我只使用git半年。也许git中有一个概念让我无法理解,或者我目前不熟悉的一个术语,因为我无法在搜索中找到问题的正确答案。 我知道如何在ClearCase中完成上述操作,但这并没有帮助...; - )
答案 0 :(得分:1)
一个更简单的解决方案是恢复E
(git revert
)
git checkout master
git checkout -b branch_2
git revert -m 1 <SHA1 of commit E>
这将取消E
引入的任何修改,创建新的提交H
,这将是E
的负面形象。
F
和G
仍然是新历史的一部分。
H <-- branch_2
/
A--B------E--F--G <-- master
\ /
C--D <-- branch_1
答案 1 :(得分:1)
...尝试创建具有B和G之间差异的补丁 与E和G之间的差异相比。我想这是可能的,但我 仍然没有弄清楚如何完成我所追求的目标。在 另一方面我想认为git应该能够帮助我 有这个。
在这种情况下,我会使用git rebase --onto
使用显式版本参数。你想要的是一个基于B
的新分支,其中添加了F
和G
的更改,对吗?这可以通过
$ git branch branch_2 master # Point branch_2 to the latest change to include
$ git rebase --onto B E branch_2 # Take changes from E (not including)
# to (including) G and put on top of B
那应该给你一些像
--F'--G' <-- branch_2
/
A--B------E--F--G <-- master
\ /
C--D <-- branch_1
F'
和G'
提交是F和G相对于B的变化。
由于branch_1的合并已经进行了多次修复 提交F和G,一些与功能相关,另一些则不然 相关。
因此,我会尝试将提交分为两部分,一部分与branch_1相关的更改,另一部分用于其余部分。
$ git branch branch_2 master
$ git rebase -i E
...
# Select `edit` for all versions and exit the editor
...
Git现在将带您回到第一个版本,F。要分割提交,我执行以下操作:重置/删除存储在git数据库中的提交但不使用命令撤消/删除现有文件中的任何内容
$ git reset HEAD^
现在,在F中完成的所有更改仍然存在但是作为未经修改的更改(您可以使用git status
进行检查)。现在,您希望再次提交更改,但需要两个(或更多)步骤。运行
$ git add -p
...
# Add only those parts related to branch_1
...
$ git diff --cached # Should show only changes related to branch_1
$ git diff # Should show the other changes
同时检查(使用git status
)是否有任何未跟踪的文件应与git add
一起添加。满意后运行
$ git commit -m "commit F1" # Possibly some other commit message...
$ git add <all_the_remaining_files>
$ git commit -m "commit F2"
$ git rebase --continue
Git现在将停止在G版本上,相应地重复上述内容。
以上所有“拆分提交操作”都不会引入任何合并冲突,并且100%安全(并且在某种程度上没有改变任何内容......),但现在希望包含/排除你想要的东西。
你现在应该
--F1--F2--G1--G2 <-- branch_2
/
A--B------E--F--G <-- master
\ /
C--D <-- branch_1
此时你可以做到
$ git checkout -b branch_2 B
$ git cherry-pick F2
$ git cherry-pick G2
如果没有发生冲突,你就完成了。但是在发生冲突的情况下,当cherry-pick失败时,git对使用mergetool没有任何支持,所以从 1 中恢复有点麻烦,所以以下是更好的方法。
$ git checkout -b branch_2 master
$ git rebase -i E
...
# Remove the F1 and G1 commits
...
这可能会导致冲突,就像樱桃选择一样,但我认为git mergetool
在这种情况下会起作用(我总是使用自己的脚本......)。如果没有解决冲突或冲突,请继续
$ git rebase --onto B E branch_2
你现在应该
--F2--G2 <-- branch_2
/
A--B------E--F--G <-- master
\ /
C--D <-- branch_1
1 我有自己的“git add -p”脚本解析git ls-files -u
并使用git cat-file
获取版本并将其提供给kdiff3。