我想从分支机构到另一个分支机构挑选单个提交。 我希望文件重命名很常见,但仍然希望能够在没有人为干预的情况下应用更改。
由于内置的cherry-pick命令没有接缝来检测重命名(至少在我的测试用例中),特别是当与修改重命名的文件结合使用时。
我尝试了一下,最后想出了一个涉及两个rebase操作的解决方案。
假设我有一个名为 target 的分支,指向我想要应用樱桃选择的提交。 我想要挑选的提交由名为 source 的分支指向。
然后我执行以下命令:git rebase --strategy="recursive" --strategy-option="rename-threshold=30" target sourceTemp
(可能使用其他阈值;测试文件非常小,因此更改相对较大)git rebase --onto target sourceTemp~ sourceTemp
这仅适用于分支源中最后一次提交引入的更改为目标。
我也把测试放在github上:
https://github.com/fraschfn/cherry-pick
我想知道的是,如果这种方法是可行的,或者它只能在我简单的测试环境中工作!
更新:替代方法
我将补丁重新绑定到来源和目标的合并基础:
开始情况
A - B <--- target
/
M
\
C - D <--- source
我想把D挑选到B上。
在创建新分支补丁
后将D重新映射到M上 A - B <--- target
/
M - D' <--- patch
\
C - D <--- source
合并C和D'以获取源
的替代品合并B和D'以获取目标
的修补版本 A - B <--- target
/ \
/ E <--- patched target
/ /
M - D' <--- patch
\ \
\ F <--- new source (same snapshot as source different history)
\ /
C - D <--- source (will be discarded)
优点是E和F现在可以合并而没有问题。 替代方法:尽可能早地在层次结构中包含补丁,这样就不会创建D而是直接创建D'并保存自己的rebase。
以前版本的优点是你可以合并两个分支“new source”和“patched target”并且它将起作用(如果源和目标的合并当然可以工作)并且不会引入相同的变更集两次因为git知道由于合并操作将变更集引入两个分支。
答案 0 :(得分:6)
您的rename-threshold
方法对于您正在尝试的方法是可行的。除非您的分支机构是永远不会合并的分叉项目,否则分支机构之间的常规樱桃选择永远不是一个可持续的工作流程。如果是这样的话,那就去吧,祝你好运。如果您曾期望将您的分支合并为一个有凝聚力的整体,我建议您更改代码流的方式。以下是一些很好的资源:
在分支之间定期挑选樱桃,生成具有不同SHA1哈希值的相同变更集。在足够长的时间内缩放,跟踪代码变得困难,理解你的历史变得几乎不可能,并且合并分支会让你觉得你昏迷并在M.C. Escher painting.中醒来。这不是一个好时机。
根据您对此答案的评论,您的用例听起来像是一个可行的樱桃选择。在这种情况下,我建议将补丁集应用于重命名的文件的工作量略微减少:
git checkout branchB
git diff <commit>~1 <commit> |
sed 's:<path_on_branchA>:<path_on_branchB>:g' |
git apply
其中<commit>
是您要从branchA
移至branchB
的提交。使用此方法,您将无法获取提交元数据,即它只是应用更改,它不会提交它。但您可以使用git format-patch
和sed
轻松操纵git am
的输出。只取决于你想做什么。
这将为您节省生成临时分支,选择正确的重命名阈值和重新定位的麻烦。它永远不会像直线git merge
一样干净,但我之前已经使用过它,一旦掌握它就很容易。
答案 1 :(得分:1)
git rebase
使用与git merge
相同的重命名检测逻辑,甚至使用相同的选项--strategy-option="rename-threshold=30"
来控制它。
这里发生的事情是你首先将整个源分支重新绑定到你的目标分支(如sourceTemp
),然后基本上挑选最后一次提交到target
。顺便提一下,git rebase -onto target src~ src
与git cherry-pick src
上的target
相同:在src
处进行一次提交并将其应用于target
。
您的工作流程与直接挑选的更改是您逐步处理源分支上的提交,这可能比一步完成所有操作都更容易重命名。如果您直接挑选,则必须通过查看target
和src~
之间的差异来识别重命名,这可能有很长的路要走;当第一次重新定义整个源分支时,重命名将以小步骤处理。
答案 2 :(得分:0)
git checkout target
# Make an artificial merge to link target and source's parent
git merge source^ --strategy=ours
# Equivalent to cherry-pick but includes rename detection
git merge source --no-ff
# ... fix all merge conflicts and finish the merge
# Get rid of those artificial merges preserving file changes
git reset --soft HEAD~2
# Make a commit to store cherry-picked files
git commit -m "Cherry-pick source branch"
答案 3 :(得分:0)
有时,文件分歧太多,因此没有一个重命名算法可以准确地确定重命名,所以我使用了#34;锤子方法&#34;。
git checkout target
# get list of files modified in source branch
git diff source^ source --name-only
# ... (A) rename corresponding target files to match source's ones (possibly write script to automate this)
git add -A
git commit -m "Temporarily rename files for cherry-pick purposes"
# There is no need for rename detection anymore
git cherry-pick source
# ... resolve all conflicts and finish the cherry-pick
# ... rename files back, reverting (A) step. Note that you cannot use `git revert` here
git add -A
git commit -m "Rename files back"
# Get rid of the artificial commits made but preserve file changes
git reset --soft HEAD~3
# Make a commit to store cherry-picked files
git commit -m "Cherry-pick source branch"