将单个分支历史转换为许多较小的合并分支
Convert single branch history into many smaller merged branches
问题
基本上,我有一个坏习惯,就是在开发过程中忘记创建分支。这给我留下了大量的提交痕迹,很难看出一个功能从哪里开始,下一个功能从哪里结束。
可能的解决方案
提交是有序的我只是希望能够将单个 master 分支分成几个较小的功能分支,然后合并回 master。
当前 Git 回购:
-- A -- B -- C -- D -- E -- F -- G -- H => master
理想Git回购:
B -- C => feature1
/ \
-- A --------- D -- E --------- H => master
\ /
F -- G => feature2
我试过的
我已经阅读了其他一些类似的问题,但找不到任何描述我的问题的内容。我试过 git rebase
但我不太确定在这种情况下如何使用它,而且显然它可能需要 --onto
选项。
非常感谢任何帮助,因为这将使我的存储库更美观!
我可能会遵循以下模式:
git checkout -b master_backup master
然后
git checkout master
git reset --hard [SHA position]
注意 SHA 位置就是您想要开始的提交哈希。
现在
git checkout -b feature-name master
git cherry-pick [sha-1]^...[sha-2]
注意,^
符号使 cherry pick 范围包括 [sha-1]
然后终于
git checkout master
git merge --no-ff feature
然后针对要添加到主拓扑的每个功能继续最后 4 个步骤
"ideal" 图有点模棱两可,因为合并提交没有明确指定。在图中,D
和 H
似乎是合并提交,但它们不应该是。
这是重建历史图表的一种可能解决方案:
git checkout master
git reset A --hard
git cherry-pick D
git branch feature1 C
git merge feature1 --no-ff
git cherry-pick E
git branch feature2
git cherry-pick H
git checkout feature2
git cherry-pick F G
git checkout master
git merge feature2 --no-ff
请注意,feature1
和 feature2
目前是本地分支。如有必要,您可以将它们推送到远程。此外,master
已被重写。您可能需要 force-push 它才能在远程存储库中更新它。
图例
A'
表示来自提交 A
的补丁和消息,但不同的提交哈希,因为父级已更改。
branch-name*
表示 branch-name
是当前结帐分支,因为 HEAD
-- A -- B -- C -- D -- E -- F -- G -- H => master
B -- C -- D
/ \
-- A ------------- M1 -- E ------------ M2 => master
\ /
F -- G -- H
Note that in your diagram you have marked having your previous non-merge commits D
and H
as merge commits in the end. I don't believe this is what you want as you would usually have different patches and messages in these commits which you want to keep separate from the merge commit. I've added M1
and M2
respectively in the end result.
你应该可以使用这个:
git checkout master
git reset --hard A
-- A => master*
\
B -- C -- D -- E -- F -- G -- H
git checkout -b feature1 D
(不需要,但可以作为标签)
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/
-- A => master*
git merge --no-ff feature1
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A ------------- M1 => master*
git cherry-pick E
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A -------------- M1 -- E' => master*
git checkout -b feature2 H
=> feature1
/
B -- C -- D -- E -- F -- G -- H => feature2*
/ \
-- A -------------- M1 -- E' => master
git rebase --onto E master
Reset to master, cherry pick everything from E
to H
on top with rebase.
Note we choose E
here and not E'
as --onto
is about the history of the branch being rebased, not what we are rebasing on top of. Equivalent would be git rebase --onto F^ master
where F^
means the parent of F
in Git.
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A -------------- M1 -- E' => master
\
F' -- G' -- H' => feature2*
git checkout master
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A -------------- M1 -- E' => master*
\
F' -- G' -- H' => feature2
git merge --no-ff feature2
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A ------------ M1 -- E' --------------- M2 => master*
\ /
F' -- G' -- H' => feature2
当没有任何命名引用指向它们的提交被丢弃时的最终结果:
B -- C -- D => feature1
/ \
-- A ------------ M1 -- E' --------------- M2 => master*
\ /
F' -- G' -- H' => feature2
问题
基本上,我有一个坏习惯,就是在开发过程中忘记创建分支。这给我留下了大量的提交痕迹,很难看出一个功能从哪里开始,下一个功能从哪里结束。
可能的解决方案
提交是有序的我只是希望能够将单个 master 分支分成几个较小的功能分支,然后合并回 master。
当前 Git 回购:
-- A -- B -- C -- D -- E -- F -- G -- H => master
理想Git回购:
B -- C => feature1
/ \
-- A --------- D -- E --------- H => master
\ /
F -- G => feature2
我试过的
我已经阅读了其他一些类似的问题,但找不到任何描述我的问题的内容。我试过 git rebase
但我不太确定在这种情况下如何使用它,而且显然它可能需要 --onto
选项。
非常感谢任何帮助,因为这将使我的存储库更美观!
我可能会遵循以下模式:
git checkout -b master_backup master
然后
git checkout master
git reset --hard [SHA position]
注意 SHA 位置就是您想要开始的提交哈希。
现在
git checkout -b feature-name master
git cherry-pick [sha-1]^...[sha-2]
注意,^
符号使 cherry pick 范围包括 [sha-1]
然后终于
git checkout master
git merge --no-ff feature
然后针对要添加到主拓扑的每个功能继续最后 4 个步骤
"ideal" 图有点模棱两可,因为合并提交没有明确指定。在图中,D
和 H
似乎是合并提交,但它们不应该是。
这是重建历史图表的一种可能解决方案:
git checkout master
git reset A --hard
git cherry-pick D
git branch feature1 C
git merge feature1 --no-ff
git cherry-pick E
git branch feature2
git cherry-pick H
git checkout feature2
git cherry-pick F G
git checkout master
git merge feature2 --no-ff
请注意,feature1
和 feature2
目前是本地分支。如有必要,您可以将它们推送到远程。此外,master
已被重写。您可能需要 force-push 它才能在远程存储库中更新它。
图例
A'
表示来自提交A
的补丁和消息,但不同的提交哈希,因为父级已更改。branch-name*
表示branch-name
是当前结帐分支,因为HEAD
-- A -- B -- C -- D -- E -- F -- G -- H => master
B -- C -- D
/ \
-- A ------------- M1 -- E ------------ M2 => master
\ /
F -- G -- H
Note that in your diagram you have marked having your previous non-merge commits
D
andH
as merge commits in the end. I don't believe this is what you want as you would usually have different patches and messages in these commits which you want to keep separate from the merge commit. I've addedM1
andM2
respectively in the end result.
你应该可以使用这个:
git checkout master
git reset --hard A
-- A => master*
\
B -- C -- D -- E -- F -- G -- H
git checkout -b feature1 D
(不需要,但可以作为标签)
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/
-- A => master*
git merge --no-ff feature1
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A ------------- M1 => master*
git cherry-pick E
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A -------------- M1 -- E' => master*
git checkout -b feature2 H
=> feature1
/
B -- C -- D -- E -- F -- G -- H => feature2*
/ \
-- A -------------- M1 -- E' => master
git rebase --onto E master
Reset to master, cherry pick everything from
E
toH
on top with rebase.
Note we choose
E
here and notE'
as--onto
is about the history of the branch being rebased, not what we are rebasing on top of. Equivalent would begit rebase --onto F^ master
whereF^
means the parent ofF
in Git.
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A -------------- M1 -- E' => master
\
F' -- G' -- H' => feature2*
git checkout master
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A -------------- M1 -- E' => master*
\
F' -- G' -- H' => feature2
git merge --no-ff feature2
=> feature1
/
B -- C -- D -- E -- F -- G -- H
/ \
-- A ------------ M1 -- E' --------------- M2 => master*
\ /
F' -- G' -- H' => feature2
当没有任何命名引用指向它们的提交被丢弃时的最终结果:
B -- C -- D => feature1
/ \
-- A ------------ M1 -- E' --------------- M2 => master*
\ /
F' -- G' -- H' => feature2