将单个分支历史转换为许多较小的合并分支

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" 图有点模棱两可,因为合并提交没有明确指定。在图中,DH 似乎是合并提交,但它们不应该是。

这是重建历史图表的一种可能解决方案:

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

请注意,feature1feature2 目前是本地分支。如有必要,您可以将它们推送到远程。此外,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