【问题标题】:How do I split up a large Git branch into lots of smaller branches?如何将一个大的 Git 分支拆分为许多较小的分支?
【发布时间】:2012-09-22 03:31:08
【问题描述】:

我已经从 SVN 导入到 Git,现在我有一个大分支,像这样:

  • 处理功能 C
  • 处理功能 B
  • 处理功能 C
  • 处理功能 C
  • 处理功能 B
  • 处理功能 A

我想要单独的功能分支,用于 A、B、C。我正在挑选新分支的提交,但这不会将它们从原始分支中删除,因此我必须手动跟踪我已拉出哪些分支。

大约有 800 个提交需要拆分,可能还有 50 个功能/错误修复。

如果我拉出的那些以某种方式反映在 git 日志中会很好,这样我就知道我已经完成了哪些。这可能吗?

我可以 rebase 整个分支,跳过我已经退出的提交,但我担心这会导致很多冲突。我不想每次拉出提交时都解决 500 个冲突。

从一个 uber 分支提取提交到较小的功能分支,同时跟踪您的进度的最佳方法是什么?

【问题讨论】:

    标签: git branch cherry-pick


    【解决方案1】:

    在这种情况下我要做的是使用 interactive rebase。

    在您的HEAD,创建您的分支A、B 和C。还要创建一个“备份”分支(您可以将其命名为 backup),以防万一出现问题并且您需要返回原始的 HEAD。

    git branch feature-a
    git branch feature-b
    git branch feature-c
    git-branch backup-before-rebase
    

    然后,在您希望他们开始的提交处创建一个分支,也许在一个方便的稳定提交处。叫它new_trunk 什么的。

    git checkout HEAD~50       ## this will be the new tree-trunk
    git branch new_trunk
    

    然后,进行交互式rebases 并选择您想要保留在该分支中的提交。用这种方式,基本就跟cherry-picking 一样了。

    git checkout feature-a
    git rebase -i new_trunk    ## -i is for "Interactive"
    

    完成后,您应该有 3 个具有独立历史记录的分支,从 new_trunk 开始,如果您仍然需要它,还有一个 backup 分支反映旧的 HEAD。

    【讨论】:

    • 如果您一次又一次地遇到相同的冲突,也请查看 rerere 以提供帮助。
    • 我通常使用标签而不是分支,用于应该保持不变的东西——尤其是备份。
    • 标签也很好 - new_trunk 是标签而不是分支的好候选
    • 这是一个很好的建议,但它并没有解决我概述的难题:如何跟踪原始分支上的哪些提交已被樱桃挑选/重新定位到另一个分支?一个想法是在挑选出每个提交后简单地为每个提交添加一个标签。另一个想法是一个接一个地选择提交,并保持一个标签是最新的,以了解我要去哪里。不过,我怀疑有更好的方法。
    • 我不清楚为什么不这样做 - 来自根分支的“路径”反映在 git 树中。您能否在问题中详细说明重组完成后需要进行哪些取证?
    【解决方案2】:

    就我个人而言,我真的会考虑如此大的更改的利弊(如果您已经这样做了,请再考虑一次)。如果您遇到冲突(这在大型 rebase/cherry-pick 中很烦人且本身难以解决),您在将功能合并回“主”分支时可能会遇到困难。

    冻结您的大分支,让它“完成”(或“足够好”)并在其上创建新的功能分支不是更好/更容易吗? (或者只排除一些分支?)

    但是对于你的问题:

    如果您想自动跟踪更改/丢失的提交,请使用 git cherry 命令。

    git cherry featureBranch bigBranch
    

    如果在挑选或重新定位您的功能分支时没有冲突,您可以使用以前的代码和一些额外的管道:

    git cherry featureBranch bigBranch | awk '{ print "pick " $2 }' | tee remaining
    

    这将打印(并保存到名为“remaining”的文件)featureBranch 中缺少的提交。您可以将此添加到 bigBranch 上的交互式 rebase 以丢弃您不再需要的提交。 (也许您可以使用“ed”编辑器作为 git 编辑器编写更多脚本,并将命令传递给交互式变基的标准输入,但我没有尝试过。)

    【讨论】:

    • 不知道git cherry。很棒的提示。
    • git cherry 很好。但是,我可以将它与多个未合并的功能分支一起使用吗?我不想将所有潜在的功能分支合并到一个混乱的大分支中,只是为了比较剩余的提交。我是否必须将每个功能分支分别与原始分支进行比较,然后以某种方式划掉所有不在每个比较中的提交 ID?在我挑选它们时以某种方式标记每个提交怎么样?
    【解决方案3】:

    只是为了进一步简化willoller's answer,

    制作功能分支,并备份,以防万一

    git branch feature-a
    git branch feature-b
    git branch feature-c
    git branch backup-before-rebase
    

    然后签出一个功能分支并从您希望它们开始的提交中进行交互式变基

    git checkout feature-a
    git rebase -i <safecommit>
    enter code here
    

    如果您希望某些功能分支共享一些提交以保持您的树干净,请不要在开始时创建稍后的功能分支,但是一旦您有了一个重新定位的功能分支,然后使用共享的提交引用作为您的下一个安全提交

    #on branch feature-a
    git checkout -b feature-d
    git rebase -i <sharedcommit>
    

    【讨论】:

      【解决方案4】:

      老实说,我不会这样做,除非您有大量需要拆分的提交,并且它们是非常独立的功能,即不会在有冲突需要解决的地方更改同一行。

      正如其他人建议的那样,为每个功能创建一个新分支并使用 git rebase --interactive 包含所需的提交。

      为确保不会误入歧途,请通过以下方式创建 git-rebase-todo 文件的内容

      • 编辑所有所需提交的列表并按功能对其进行分类
      • 将提交列表分离到单独的文件中

      您可以使用类似的命令创建提交列表

      git log --oneline --reverse  44e19^... > log.txt
      

      显示从 44e19 开始的提交。这会给你一个像这样的文件

      44e1936 dSRGratuities (SummaryRecord)
      67fedda Receipt Report HEADER: 20! multiply by Paym_FieldCount
      69d70e2 Receipt Report: Payment
      ....
      

      在编辑时(添加分类:特征 a、b、c 等)可能看起来像我的 sorted.txt

      c 44e1936 dSRGratuities (SummaryRecord)
      a 67fedda Receipt Report HEADER: 20! multiply by Paym_FieldCount
      b 69d70e2 Receipt Report: Payment
      c abea7db Receipt Report: Cashback
      a cf96185 Receipt Report: Gratuity
      c 70e987a Receipt Report: use amount tendered for printing
      a 7722ac8 Receipt Report: use amount tendered for calculations
      c 47f1754 Receipt Report: store amount tendered
      b b69a73f Receipt Report: Use enum Paym_FieldCount
      a 9a0b471 Receipt Report HEADER: enum PaymentEntries (with Paym_FieldCount)
      c ad67e79 Use SharpReport enum
      b 3c510c6 enum SharpReport
      a e470e07 m_Gratuities m_dSSGratuities (SalesSummary)
      b 4e0c3e4 m_Gratuities m_szGratuities (SalesSummaryRecord)
      b bd054f7 _gx_fn_Cashback
      

      然后使用您最喜欢的脚本语言编写脚本,将排序列表转换为git-rebase-todo 文件的集合。您的脚本可能与我刚刚编写的脚本相似。

      foreachline text sorted.txt {
          set fields  [split $text { }]
          set branch  [lindex $fields 0]
          set commit  [lindex $fields 1]
          set comment [string range $text 10 end]
          set command "echo pick $commit $comment"
          exec cmd /S /C $command >> $branch.txt
      }
      

      该脚本逐行读取提交排序文件,并用空格字符 { } 分割以获取两个字段 branch 和 commit,并获取一个子字符串(字符 10 以后)作为提交的描述。描述不是必需的,但它对我们人类检查错误很有用。

      然后它将一行放入适当的git-rebase-todo 文件中,为每个功能创建一个文件。我通过执行一个非常丑陋的 Windows echo string &gt;&gt; file 命令解决了这个问题。

      这会创建许多文件,例如我的档案a.txt

      pick 67fedda Receipt Report HEADER: 20! multiply by Paym_FieldCount
      pick cf96185 Receipt Report: Gratuity
      pick 7722ac8 Receipt Report: use amount tendered for calculations
      pick 9a0b471 Receipt Report HEADER: enum PaymentEntries (with Paym_FieldCount)
      pick e470e07 m_Gratuities m_dSSGratuities (SalesSummary)
      

      整个事情都很丑陋。除非您必须这样做并且擅长编写脚本,否则我不推荐它。


      我前段时间写了上面的文字,我对事情进行了一些反思。上面我暗示这是很多工作,不值得做,但从那以后我看到有人做了上面的事情,而且非常值得。

      我记得 Visual Studio for MFC/C++ 的版本,每个新版本都会有编译器更改、IDE 更改、MFC 改进,并在更高版本的 Windows 上运行。这意味着,如果您想让您的编译器远离 VS6 和 Windows XP,您可能必须更改语言以满足编译器的要求,并更改函数调用以满足 MFC 等。

      现在假设 Microsoft 在开发 Visual Studio 时每周进行一次备份,有人有条不紊地进行旧备份并将代码更改提交到 Git 等版本控制系统中。然后他们开始对变化进行分类......

      • 一个。 = 编译器更改

      • b. = 库更改

      • c。 = IDE 更改

      • d。 = 安全改进

        等等

      Microsoft 可以为其中的每一个创建分支,并开始拥有最新最好的 IDE(包括c),在最新的 Windows 上运行,并且仍然能够使用该语言编译旧的遗留程序(没有 a)和图书馆(不是b)。

      以前锁定在遗留软件中的开发人员随后可以以逻辑和渐进的方式进行改进,例如语言更改和库更改相互独立,并且可以在最新最好的 Visual Studio 上完成,而无需通过所有中间版本。

      例子

        <LanguageStandard>stdcpp14</LanguageStandard>
      

      现在我并不是说这就是发生的事情,但在我看来,Visual Studio 的最新版本在允许更新遗留程序方面要好得多,而不是丢弃和(永远?)重写,而且它在我看来,这是由于版本控制和将旧软件更改组织到逻辑分支:编译器版本、DLL/库版本。

      因此,我可以看到将大量旧提交拆分为不同分支的情况可能是值得的。

      在 Visual Studio 2019 上,我可以添加该行

      <PlatformToolset>v141_xp</PlatformToolset>
      

      到一个配置文件并设法编译和运行一个旧的 windows 程序,该程序无法编译和链接到 VS 2015 和 VS 2017。看起来很像微软的某个人在一些旧软件上重新定位了性能和安全性改进,而省略了经常伴随现代化而来的breaking changes。

      【讨论】:

        【解决方案5】:

        我刚刚发现的另一种方法是使用“git notes”。

        http://alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html

        此功能允许将 cmets 添加到现有提交中,而无需实际更改分支/需要变基。跟踪哪些提交已被拉出的一种方法是为每个提交添加一个 git 注释:

        Cherry-picked to features\xyz 925a5239d4fbcf7ad7cd656020793f83275ef45b

        这可以在很大程度上帮助手动过程 - 您可以编写一个小脚本来挑选一个特定分支的提交,然后将相关的 git 注释添加回原始提交。

        或者,如果你想变得非常时髦,你可以通过以下方式自动化整个过程:

        1. 在每个提交中添加一个 git 注释,说明您希望将其挑选到哪个功能分支:TOCHERRYPICK: features\xyz
        2. 编写一个脚本来扫描所有 git 注释,并自动创建所有功能分支并挑选正确的选定提交。然后它可以将 git note 更改为 CHERRYPICKED: features\xxx at 925a5239d4fbcf7ad7cd656020793f83275ef45b 以允许稍后重新运行该工具以挑选更多提交。
        3. 如果您真的很想在提交被选中时突出显示,您还可以自动创建具有相似名称的标签:CHERRYPICKED:&lt;branch&gt;:SHA

        【讨论】:

          【解决方案6】:

          // 在开发分支上

          git diff develop your/branch > diff.patch
          git apply diff.patch
          

          【讨论】:

            猜你喜欢
            • 2021-11-30
            • 2021-07-04
            • 2021-07-03
            • 2020-03-24
            • 2012-09-03
            • 2018-03-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多