代码之家  ›  专栏  ›  技术社区  ›  Mark A. Nicolosi

使用Git将最近的提交移动到新分支

  •  6404
  • Mark A. Nicolosi  · 技术社区  · 16 年前

    我如何将最近在master上的提交移动到一个新分支,并在提交之前将master重置为?例如,由此:

    master A - B - C - D - E
    

    newbranch     C - D - E
                 /
    master A - B 
    
    22 回复  |  直到 4 年前
        1
  •  5246
  •   sykora    8 年前

    现有分支机构

    git checkout existingbranch
    git merge branchToMoveCommitFrom
    git checkout branchToMoveCommitFrom
    git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
    git checkout existingbranch
    

    在执行此操作之前,您可以使用以下命令将未提交的编辑存储到存储库中 git stash 。完成后,您可以使用以下命令检索隐藏的未提交编辑 git stash pop

    警告: git branch newbranch .如果你想移动提交给 现有分支机构 git reset --hard HEAD~3 (参见 移动到现有分支 上文)。 如果不先合并更改,它们将丢失。

    除非涉及其他情况,否则可以通过分支和回滚轻松完成。

    # Note: Any changes not committed will be lost.
    git branch newbranch      # Create a new branch, saving the desired commits
    git checkout master       # checkout master, this is the place you want to go back
    git reset --hard HEAD~3   # Move master back by 3 commits (Make sure you know how many commits you need to go back)
    git checkout newbranch    # Go to the new branch that still has the desired commits
    

    但一定要确定有多少承诺要返回。或者,您可以代替 HEAD~3 来源/主人 )您想在 主人 (/当前)分支,例如:

    git reset --hard a1b2c3d4
    

    注: 你将 仅 从master分支“丢失”提交,但别担心,你会在newbranch中得到这些提交!在完成上述4步命令序列后,检查这一点的一个简单方法是查看 git log -n4 这将显示newbranch实际保留了3个提交的历史记录(原因是newbranch是在master上已经提交这些更改时创建的!)。它们只是从主人那里被移除了,因为 git reset 仅影响执行时签出的分支,即master(请参见 git reset 描述: 重置 当前的 HEAD到指定状态 ). git status 然而,新分行不会显示任何结账信息,这起初可能会令人惊讶,但实际上是意料之中的。

    最后, you may need 强制将您的最新更改推送到主仓库:

    git push origin master --force
    

    警告: 对于Git 2.0及更高版本,如果你以后 git rebase 原来的新分支( master )分支,您可能需要一个明确的 --no-fork-point 在重基期间选择此选项,以避免丢失结转的提交。拥有 branch.autosetuprebase always set使这种情况更有可能发生。看 John Mellor's answer 了解详情。

        2
  •  893
  •   Ryan Lundy    10 年前

    对于那些想知道为什么它有效的人(就像我最初一样):

    你想回到C,把D和E移动到新的分支。这是它最初的样子:

    A-B-C-D-E (HEAD)
            ↑
          master
    

    之后 git branch newBranch :

        newBranch
            ↓
    A-B-C-D-E (HEAD)
            ↑
          master
    

    之后 git reset --hard HEAD~2 :

        newBranch
            ↓
    A-B-C-D-E (HEAD)
        ↑
      master
    

    由于分支只是一个指针, 主人 新分行 ,您只需创建一个指向上次提交的新指针。然后使用 git reset 你移动了 主人 指针指向两次提交。但既然你没有动 新分行 ,它仍然指向它最初所做的承诺。

        3
  •  374
  •   Ivan    8 年前

    一般来说。..

    git樱桃采摘 :

    要实现OP的目标,需要两个步骤:

    步骤1-注意你想在master上提交哪些内容 newbranch

    执行

    git checkout master
    git log
    

    注意你想要的(比如3个)提交的哈希值 新分行
    C承诺: 9aa1233
    453ac3d
    E承诺: 612ecb3

    注: 您可以使用前七个字符或 整个提交哈希

    第二步-把它们放在 新分行

    git checkout newbranch
    git cherry-pick 612ecb3
    git cherry-pick 453ac3d
    git cherry-pick 9aa1233
    

    OR(在Git 1.7.2+上,使用范围)

    git checkout newbranch
    git cherry-pick 612ecb3~1..9aa1233
    

    git樱桃采摘 将这三个提交应用于newbranch。

        4
  •  275
  •   aragaer    8 年前

    以前的大多数答案都是危险的错误!

    不要这样做:

    git branch -t newbranch
    git reset --hard HEAD~3
    git checkout newbranch
    

    当你下次跑步时 git rebase git pull --rebase )这3个提交将被默默地丢弃 newbranch !(见下文解释)

    相反,请这样做:

    git reset --keep HEAD~3
    git checkout -t -b newbranch
    git cherry-pick ..HEAD@{2}
    
    • 首先,它丢弃最近的3个提交( --keep 就像 --hard ,但更安全,因为失败而不是丢弃未提交的更改)。
    • 然后它分叉了 新分行 .
    • 新分行 。由于它们不再被分支引用,因此它通过使用git来实现 reflog : HEAD@{2} 是承诺 HEAD 用于指2个操作之前,即我们1之前。结账 新分行 以及2。使用过的 git reset

    警告:默认情况下,reflog是启用的,但如果您手动禁用了它(例如使用“裸”git存储库),则在运行后将无法恢复3次提交 git reset --keep HEAD~3 .

    另一种不依赖于reflog的方法是:

    # newbranch will omit the 3 most recent commits.
    git checkout -b newbranch HEAD~3
    git branch --set-upstream-to=oldbranch
    # Cherry-picks the extra commits from oldbranch.
    git cherry-pick ..oldbranch
    # Discards the 3 most recent commits from oldbranch.
    git branch --force oldbranch oldbranch~3
    

    (如果你愿意,你可以写 @{-1} -之前已退房的分行-而不是 oldbranch ).


    技术说明

    为什么会 git 变基 在第一个示例之后丢弃3个提交?这是因为 git 变基 无参数启用 --fork-point

    假设你在origin/master包含提交M1、M2、M3时将其分支,然后自己进行了三次提交:

    M1--M2--M3  <-- origin/master
             \
              T1--T2--T3  <-- topic
    

    但随后有人通过强制推origin/master删除M2来重写历史:

    M1--M3'  <-- origin/master
     \
      M2--M3--T1--T2--T3  <-- topic
    

    使用您本地的reflog, git 变基 可以看到你从origin/master分支的早期版本分叉,因此M2和M3提交并不是你的主题分支的一部分。因此,它合理地假设,既然M2已从上游分支中删除,那么一旦主题分支重基化,您也不希望它再出现在主题分支中:

    M1--M3'  <-- origin/master
         \
          T1'--T2'--T3'  <-- topic (rebased)
    

    这种行为是有道理的,而且在重设基准时通常是正确的做法。

    git分支-t新分支
    git重置--硬头~3
    git结账新分行
    

    这是因为它们使reflog处于错误的状态。Git看到 新分行 因为在包含3个提交的修订版中分叉了上游分支,那么 reset --hard 重写上游的历史记录以删除提交,因此下次运行时 git 变基 它像从上游删除的任何其他提交一样丢弃它们。

    但在这种特殊情况下,我们希望这3个提交被视为主题分支的一部分。为了实现这一点,我们需要在不包括3个提交的早期版本中分叉上游。这就是我建议的解决方案所做的,因此它们都使reflog处于正确的状态。

    有关更多详细信息,请参阅 --分叉点 在 git rebase 和 git merge-base 文档。

        5
  •  251
  •   John Mellor    10 年前

    还有一种方法可以做到这一点,只使用2个命令。还可以保持当前工作树的完整性。

    git checkout -b newbranch # switch to a new branch
    git branch -f master HEAD~3 # make master point to some older commit
    

    旧版本 -在我了解之前 git branch -f

    git checkout -b newbranch # switch to a new branch
    git push . +HEAD~3:master # make master point to some older commit 
    

    能够 push 到 . 知道这是个好把戏。

        6
  •  46
  •   Slam    7 年前

    使用git stash的更简单的解决方案

    master 有三个错误的提交:

    git reset HEAD~3
    git stash
    git checkout newbranch
    git stash pop
    

    什么时候用这个?

    • 主人
    • 您想保留文件更改
    • 你不在乎错误提交的消息
    • 你还没推
    • 你想让这容易记住吗
    • 你不希望出现临时/新分支、查找和复制提交哈希值等复杂问题,以及其他令人头疼的问题

    这是什么,按行号

    1. 撤消最后三次提交(及其消息) 主人
    2. 主人
    3. 切换到现有分支 newbranch
    4. 将隐藏的更改应用于您的工作目录并清除隐藏

    您现在可以使用 git add 和 git commit 就像你通常会做的那样。所有新提交都将添加到 新分行 .

    这没什么用

    • 它不会让随机的临时树枝弄乱你的树
    • 更新!使用向上箭头滚动命令缓冲区,以重新应用之前的提交及其提交消息(谢谢@ARK)

    目标

    主人 而不是 develop 。通常我只有一次回滚承诺,在这种情况下,使用 git reset HEAD^ 第1行是回滚一次提交的更简单方法。

    如果您将master的更改推到上游,则不要这样做

    其他人可能已经做出了这些改变。如果你只是重写你的本地主数据,那么当它被推送到上游时不会有任何影响,但将重写的历史推送给合作者可能会引起麻烦。

        7
  •  28
  •   hamdiakoguz Sukima    12 年前

    最简单的方法:

    1. 重命名 master 分支到你的 newbranch (假设你在 主人 分支):

    git branch -m newbranch
    

    2. 创建 主人 从您希望的承诺中分支:

    git checkout -b master <seven_char_commit_id>
    

    例如,git结账-b master a34bc22

    制造 主人 轨道 origin/master :

    git branch -u origin/master master
    

    注:

    上游为 新分行 将会是 来源/主人 .

        8
  •  21
  •   teh_senaus    10 年前

    这不会在技术意义上“移动”它们,但具有相同的效果:

    A--B--C  (branch-foo)
     \    ^-- I wanted them here!
      \
       D--E--F--G  (branch-bar)
          ^--^--^-- Opps wrong branch!
    
    While on branch-bar:
    $ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)
    
    A--B--C  (branch-foo)
     \
      \
       D-(E--F--G) detached
       ^-- (branch-bar)
    
    Switch to branch-foo
    $ git cherry-pick E..G
    
    A--B--C--E'--F'--G' (branch-foo)
     \   E--F--G detached (This can be ignored)
      \ /
       D--H--I (branch-bar)
    
    Now you won't need to worry about the detached branch because it is basically
    like they are in the trash can waiting for the day it gets garbage collected.
    Eventually some time in the far future it will look like:
    
    A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
     \
      \
       D--H--I--J--K--.... (branch-bar)
    
        9
  •  12
  •   Darkglow    13 年前

    要在不重写历史的情况下做到这一点(即如果你已经推送了提交):

    git checkout master
    git revert <commitID(s)>
    git checkout -b new-branch
    git cherry-pick <commitID(s)>
    

    然后,这两个分支都可以不用用力推!

        10
  •  2
  •   rashok    8 年前

    刚刚出现这种情况:

    Branch one: A B C D E F     J   L M  
                           \ (Merge)
    Branch two:             G I   K     N
    

    我表演了:

    git branch newbranch 
    git reset --hard HEAD~8 
    git checkout newbranch
    

    我原本以为commit I会是HEAD,但commit L现在就是了。..

    为了确保在历史上找到正确的位置,更容易处理提交的哈希值

    git branch newbranch 
    git reset --hard #########
    git checkout newbranch
    
        11
  •  0
  •   Pankaj Kumar    8 年前

    我该怎么办

    A - B - C - D - E 
                    |
                    master
    

    这个?

    A - B - C - D - E 
        |           |
        master      newbranch
    

    有两个命令

    • git分支-m主新分支

    给予

    A - B - C - D - E 
                    |
                    newbranch
    

    和

    给予

    A - B - C - D - E
        |           |
        master      newbranch
    
    推荐文章