git - 将提交从一个分支移动到另一个分支,然后将它们 merge 回去

标签 git

我有一棵树:

       /--b1---b2 <-- topic b
      /    
a1---a2 <-- topic a

其中“b”取决于“a”。然后我意识到我需要做一些与主题“a”相关的更改才能继续“b”,但我想在“b”上做这些更改作为“b”的正常开发过程:

       /--b1---b2---a3---a4---b3---b4---a5---b5 <-- topic b
      /    
a1---a2 <-- topic a

然后,当我想在“b”上完成的事情完成时,我希望我的树看起来像这样:

       /--b1---b2--------m---b3'---b4'---b5' <-- topic b
      /                 /
a1---a2---a3'---a4'---a5' <-- topic a

就好像我真的在“a”上做了所有更改,然后在“b”上 merge 它们,然后在“b”上继续。

我知道我可以手动执行此操作:

1- rebase/cherry-pick 'a' 从分支 'b' 提交到 'a'
2- 在“b”上创建时间分支“b-tmp”。
3- 将分支 'b' 重置为 'b2'。
4- 将 'a' merge 到 'b' 上。
5- rebase/cherry-pick 'b' 从 'b-tmp' 提交到 'b'。
6- 删除分支 'b-tmp'。

我可以创建一些脚本来执行此操作,我只是想知道除了这 6 个步骤之外是否还有更好的方法/想法。

最佳答案

首先让我说我宁愿经常集成主题分支(尽早集成,经常构建......敲响了钟声?)。所以实际上,当最终应该对 a 进行更改时,我会返回,在 a 上进行更改,并在 之上重新设置 b一个

如果 a 出于某些其他目的需要保持稳定,我会创建一个 a-for-b 分支来临时包含工作,然后 rebase b 最重要的是。

如果您真的必须在分支“b”中处理“topic-a”更改,这就是我要做的。

注意:包含截屏视频


到达起点

这是一个简单的工作树,带有问题的布局:

mkdir /tmp/q
cd /tmp/q
git init .
touch f
git add f
git commit -am initial
git checkout -b a; for a in a{1,2}; do echo $a>$a; git add $a; git commit -am $a; git tag $a; done
git checkout -b b; for a in b{1,2} a{3,4} b{3,4} a5 b5; do echo $a>$a; git add $a; git commit -am $a; git tag $a; done
git show-branch 

参见 screencast here


将提交重新组织到它们的主题分支上

git checkout -b a_new a     # for safety, work on a clone of branch a
git reset --hard b          # start by moving it to the end of the b branch
git status                  # (just show where we are)
git log --oneline

git rebase -i a             # rebase it on top of the original branch a

    # not shown: delete every line with non-branch-a commits (b1-b5)
    # see screencast

git log --oneline           # (just show where we are again)

git checkout -b b_new b     # for safety, work on a clone of branch b
git log --oneline           # (just show where we are again: the end of branch b)
git rebase -i a_new         # this time, rebase it on top of the new branch a_new
git log --oneline           # (check results)
git show-branch b_new a_new

再次,see screencast


检查结果

我们现在可以做一个之前/之后的树比较:

sehe@natty:/tmp/q$ git show-branch a b
! [a] a2
 ! [b] b5
--
 + [b] b5
 + [b^] a5
 + [b~2] b4
 + [b~3] b3
 + [b~4] a4
 + [b~5] a3
 + [b~6] b2
 + [b~7] b1
++ [a] a2

sehe@natty:/tmp/q$ git show-branch a_new  b_new 
! [a_new] a5
 * [b_new] b5
--
 * [b_new] b5
 * [b_new^] b4
 * [b_new~2] b3
 * [b_new~3] b2
 * [b_new~4] b1
+* [a_new] a5

使其永久化:

for name in a b; 
do 
     git branch -m $name ${name}_old && 
     git branch -m ${name}_new $name
done
git branch -D a_old b_old # when sure

注意事项

我特意选择对演示进行不冲突的更改。当然在现实生活中你会遇到 merge 冲突。使用 git mergetoolgit rebase --continue .

如果您的更改是卫生的并且确实属于它们各自的主题分支,那么冲突应该很少并且很容易解决。 否则,是时候修改您的分支方案了(参见 Martin Fowler e.a.)

讨论

回应

You also say that "when there are changes that should go onto 'a' eventually, I'd go back, do the changes on 'a', and rebase 'b' on top of 'a'". I'm not sure I understand this also. Could you explain?

我的意思是无论如何我都会尝试在 a 分支上进行 a3、a4、a5 修订,并将 b rebase 到那个分支上,所以

  • 进行 merge 的人与进行提交的人是同一个人
  • 你连续提交和 merge (意味着你不会因为短期内存丢失而把事情搞砸) <-- this is the <强> Merge Early/Soon/Frequently mantra
  • 避免不必要的 merge 冲突
  • 您以后不必“回到过去”并重写原始提交:a3、a4、a5 只会被 merge ,而不是被复制(参见 Git Cherry-pick vs Merge Workflow)

这里是“开始”点,但适应了我理想化的工作流程。请注意,此替代结果的最终结果已经完全在上面“将提交重新组织到其主题分支”下显示的所有杂耍之后最终得到的结果!

mkdir /tmp/q
cd /tmp/q
git init .
touch f
git add f
git commit -am initial
git checkout -b a;                        # no change
echo a1>a1; git add a1; git commit -am a1
echo a2>a2; git add a2; git commit -am a2

git checkout -b b;                        # start off the a branch
echo b1>b1; git add b1; git commit -am b1
echo b2>b2; git add b2; git commit -am b2

git checkout a                            # go back to the a branch for a3 and a4
echo a3>a3; git add a3; git commit -am a3
echo a4>a4; git add a4; git commit -am a4

git checkout b
git rebase a                              # here is the magic: rebase early
echo b3>b3; git add b3; git commit -am b3
echo b4>b4; git add b4; git commit -am b4

git checkout a                            # go back to the a branch for a5
echo a5>a5; git add a5; git commit -am a5

git checkout b
git rebase a                              # again: rebase early
echo b5>b5; git add b5; git commit -am b5

git show-branch

注意实际上返回 a 分支提交 a3/a4/a5 提交并不难:

  • 如果你只意识到你触摸了属于那个分支的东西,你可以切换a 分支有待处理的本地更改 2
  • 然后您有选择地 (!!) 暂存需要进入 a 分支的部分( git add -i 或 git guis 中的类似内容或例如 vim fugitive )
  • promise
  • 切换回 b 分支 ( git checkout b )
  • 提交剩余部分
  • rebase ba 中的最新版本

2 只要它们不与 a..b 的其他更改冲突;在那种情况下你会git stash首先和git stash applya 分支上

关于git - 将提交从一个分支移动到另一个分支,然后将它们 merge 回去,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7545982/

相关文章:

git - Azure 分支 PR 策略 - 要求源分支是最新的

Git:显示用户在当前分支中添加的每一行

git - 操作在 300000 毫秒后超时,在 "git clone"接收到 0 个字节中的 0 个字节

java - Jenkins - 在每次构建之前检查 Git

git - 如何获取包含特定文件的 git 标签列表?

git - 使用git扫描源代码生成待办事项列表等

git - 我需要将子模块文件夹添加到 .gitignore 中吗?

git - 如何配置 git 以忽略本地某些文件?

GIT 在切换分支时提示文件随机更改

git - Heroku 推送被拒绝,子模块安装失败