【问题标题】:git: how to move a branch's root two commits backgit:如何将分支的根目录移回两个提交
【发布时间】:2017-04-20 22:53:21
【问题描述】:

假设我有:

A - B - C - D - E - F  master
            \ 
             \- G - H  new feature branch

现在我意识到实际上提交了 B 和 C 属于新功能,所以我想将它们移动到“新功能分支”。换句话说,我希望“新功能分支”从 A 开始,并包括提交 B 和 C:

A - D - E - F  master
 \ 
  \- B - C - G - H  new feature branch

我该怎么做?从我读过的内容来看,rebase 似乎是我正在寻找的功能,但我想在搞砸我的存储库之前确定一下。

(我搜索并发现了很多非常相似的问题和示例,但没有一个与我所描述的场景完全,所以我要求确定(a毕竟,回购是一件很珍贵的东西))。

【问题讨论】:

    标签: git rebase


    【解决方案1】:

    Edmundo's answer 是正确的(并且被赞成),但值得指出一些额外的项目。

    首先,您的问题是关于“移动分支的根”——但这是 Git;分支没有根,反正不是你想的那样。让我们看看你的图表绘制:

    A - B - C - D - E - F  master
                \ 
                 \- G - H  new feature branch
    

    我认为你和我刚开始使用 Git 时一样,想将提交 A-B-C-D-E-F 视为在 master 分支上,并将提交 G-H 视为在功能分支上。但这不是 Git 的工作方式。让我们重新绘制它,而不更改任何提交链接:

               E--F   <-- master
              /
    A--B--C--D
              \
               G--H   <-- feature
    

    这应该更清楚地表明,就 Git 而言,提交 A-B-C-D 位于 both 分支上。只有一个根。那是提交A:它是一个根,因为它没有父提交,在从提交到父的链中没有反向链接。

    其次,无论你怎么做,你最终都不得不复制一些提交。原因是每个提交的父 ID 都是该提交身份的一部分。任何提交的“真实名称”都是其哈希 ID,哈希 ID 是通过读取提交的完整内容构建的:源树、提交消息、作者姓名和日期等,但也始终包括父 ID。 1 您希望最终的图表类似于:

      D--E--F   <-- master
     /
    A
     \ 
      B--C--G--H   <-- feature
    

    但现有的D 链接(或指向)回现有的C,而不是A,现有的G 又指向现有的D

    这就是cherry-pick有效的原因:git cherry-pick本质上是复制一个提交。新副本与原始副本“做同样的事情”,但有一些东西不同,即使它像“我的父母是......”一样简单。 (通常它也有一个不同的附加 tree 对象,只是它所做的 change 与它的新父级相比,与原来的更改相同将原件与原件的父代进行比较时生成)。这意味着you can't actually get what you want, just what you need:

      D'-E'-F'  <-- master
     /
    A
     \ 
      B--C--G'-H'  <-- feature
    

    小勾号表示结果是原件的副本

    原件会怎样?答案是:它们仍然存在,在存储库中,以防您需要它们。全图更像这样:

        D'-E'-F'   <-- master
       /
      /        E--F   [abandoned]
     /        /
    A--B--C--D
           \  \
            \  G--H   [abandoned]
             \
              G'-H'   <-- feature
    

    虽然git cherry-pick 有效,但git rebase——尤其是git rebase -i——所做的是以一种奇特的自动化方式完成这些副本,最后一步是移动分支名称,放弃原始提交。所以git rebase -i 有时是一种更容易的方式。

    如果你运行git rebase -i,你会看到所有那些pick 命令,而那些实际上运行git cherry-pick:它确实是在做一系列的挑选。如果您自己执行这些操作,可能会更清楚发生了什么,并且您可以更好地控制何时移动分支标签。


    1对于合并提交,这是父s,复数形式。所有父母都参与哈希。

    【讨论】:

      【解决方案2】:

      修复大师:

      git checkout A
      git cherry-pick master~3..master # apply the changes we actually want to keep on master
      git branch -f master # reposition master on new position
      git checkout master
      

      从功能分支中删除提交 D:

      git checkout feature-branch~3 # or git checkout C
      git cherry-pick feature-branch~2..feature-branch # apply the last 2 revisions
      git branch -f feature-branch
      git checkout feature-branch
      

      【讨论】:

      • 感谢您的回复。所以,只是为了让我了解发生了什么:“git cherry-pick”正在挑选提交 D、E 和 F,对吗?
      • 完全正确。 master~3 是 C,所以就像说git cherry-pick C..master。因为cherry-pick C 是它将“开始”的地方,因此从该版本(从 B 到 C)的更改不是“应用”的。
      • 我对功能分支实际上是错误的。功能分支必须“删除”修订版 D。应该是这样的:git checkout feature-branch~3 # or git checkout C; git cherry-pick feature-branch~2..feature-branch # apply the last 2 revisions; git branch -f feature-branch; git checkout feature-branch
      • 那么,除了 master 分支中的指令,我还需要在 feature 分支中这样做吗?
      • 是的。试一试,直到您完成挑选,然后执行gitk master HEAD(假设 master 已经更正)并确保 HEAD 实际上是您希望功能分支的方式(删除了 D 修订版)。
      【解决方案3】:

      您可以使用git rebase 来做到这一点。

      首先,让我们对来自master 的一些提交进行rebase:

      $ git checkout master
      $ git rebase --onto A C
      

      这会将 C 到 master 范围内的所有提交(但不包括 C 本身)移动到提交 A。

      现在变基 feature 但丢弃提交 D:

      $ git checkout feature
      $ git rebase --onto C D
      

      与上一个命令类似,这会将范围从 D 到 feature(不包括 D 本身)的提交移动到 C。

      【讨论】:

        猜你喜欢
        • 2019-11-24
        • 2019-01-04
        • 2012-11-08
        • 2014-11-19
        • 1970-01-01
        • 1970-01-01
        • 2011-03-11
        • 2012-03-02
        • 1970-01-01
        相关资源
        最近更新 更多