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

将最新提交移动到带有Git的新分支

  •  4131
  • Mark A. Nicolosi  · 技术社区  · 15 年前

    我想把我对master的最后几次承诺转移到一个新的分支,并在做出这些承诺之前将master带回。不幸的是,我的功夫还不够强,有什么帮助吗?

    也就是说,我怎样才能摆脱这个困境呢?

    master A - B - C - D - E
    

    对此?

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

    移动到新分支

    警告: 此方法有效,因为您正在使用第一个命令创建新分支: 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 reset --hard HEAD~3   # Move master back by 3 commits (GONE from master)
    git checkout newbranch    # Go to the new branch that still has the desired commits
    

    但一定要确保返回多少提交。或者,您可以代替 HEAD~3 ,只需提供提交的散列(或类似的引用 起源/大师 )您希望在 主人 (/当前)分支,例如:

    git reset --hard a1b2c3d4
    

    * 1你将 只有 别担心,你会在新农场得到那些承诺的!

    警告: 如果您以后使用Git 2.0及更高版本 git rebase 原来的新分支( master )分支,您可能需要一个明确的 --no-fork-point 在REBASE期间的选项,以避免丢失结转提交。有 branch.autosetuprebase always 设置使这更可能发生。参见 John Mellor's answer 详情。

    移动到现有分支

    如果你想把你的承诺转移到 现有分支 ,如下所示:

    git checkout existingbranch
    git merge master
    git checkout master
    git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
    git checkout existingbranch
    
        2
  •  893
  •   Ryan Lundy    9 年前

    对于那些想知道为什么它会起作用的人(就像我起初那样):

    你想回到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    7 年前

    一般来说。。。

    在这种情况下,Sykora公开的方法是最好的选择。但有时不是最简单的方法,也不是一般的方法。一般方法使用 樱桃樱桃 以下内容:

    要实现OP想要的,它需要两个步骤:

    第1步-注意您要从主服务器提交的 newbranch

    执行

    git checkout master
    git log
    

    注意您想要的(比如说3)提交的哈希值 纽科 . 在这里我将使用:
    C提交: 9aa1233
    D提交: 453ac3d
    E提交: 612ecb3

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

    第2步-把它们放在 纽科

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

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

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

    樱桃樱桃 将这三个承诺应用于Newbranch。

        4
  •  275
  •   aragaer    7 年前

    另一种方法是,只使用两个命令。同时保持当前工作树的完整性。

    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 . 是个很好的技巧。

        5
  •  251
  •   John Mellor    8 年前

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

    不要这样做:

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

    当你下次跑步时 git rebase (或) git pull --rebase )这三项承诺将从 newbranch !(见下文解释)

    而是这样做:

    git reset --keep HEAD~3
    git checkout -t -b newbranch
    git cherry-pick ..HEAD@{2}
    
    • 首先,它放弃最近3次提交( --keep 就像 --hard ,但更安全,因为失败而不是放弃未提交的更改)。
    • 然后它分叉了 纽科 .
    • 然后,Cherry选择了这3个承诺 纽科 . 因为分支不再引用它们,所以使用git reflog : HEAD@{2} 是否承诺 HEAD 以前指的是两个操作,即在我们1之前。签出 纽科 2。习惯于 git reset 放弃三项承诺。

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

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

    # 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 默认情况下的选项,它使用本地reflog尝试对被强制推送的上游分支保持健壮。

    假设您在包含提交m1、m2、m3时将origin/master分支,然后自己进行三次提交:

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

    但随后有人强迫原点/主数据删除m2来重写历史:

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

    利用你的本地反应, GIT重碱 可以看到,您来自一个早期的起源/主分支的化身,因此M2和M3提交实际上不是主题分支的一部分。因此,它合理地假设,由于m2已从上游分支中删除,因此在主题分支重新调整后,您不再希望它出现在主题分支中:

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

    这种行为是有意义的,而且通常在重新平衡时是正确的。

    所以以下命令失败的原因是:

    吉特分行-新牧场
    Git重置--硬头~3
    新牧场Git结账
    

    是因为他们让反射处于错误的状态。吉特见 纽科 在包括3个提交的修订中分叉上游分支,然后 reset --hard 重写上游的历史记录以删除提交,因此下次运行时 GIT重碱 它像从上游删除的任何其他提交一样丢弃它们。

    但在这个特定的案例中,我们希望这3个承诺被视为主题分支的一部分。为了实现这一点,我们需要在早期的版本(不包括3个提交)中划分上游。这就是我建议的解决方案所做的,因此它们都将reflog保持在正确的状态。

    有关详细信息,请参见 --叉点 git rebase git merge-base 博士学位。

        6
  •  46
  •   Slam    6 年前

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

    如果:

    • 你的主要目的是恢复 master
    • 您希望保留文件更改,但不特别关心错误提交消息,以及
    • 你还没推,还有
    • 您希望这很容易,并且不复杂,包括临时分支、提交哈希和其他令人头疼的问题。

    那么下面的步骤就简单多了(从分支开始 主人 有三个错误的承诺:

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

    这是做什么的,按行号

    1. 撤消最后三次提交(及其消息) 主人 ,但保留所有工作文件的完整性
    2. 隐藏所有工作文件更改,使 主人 工作树完全等于头3状态
    3. 切换到现有分支 newbranch
    4. 将隐藏的更改应用于您的工作目录并清除隐藏

    您现在可以使用 git add git commit 就像你平时一样。所有新提交都将添加到 纽科 .

    这不能做什么

    • 它不会在你的树上留下杂乱的临时树枝
    • 它不会保留错误的提交和提交消息, 所以您需要向这个新提交添加一个新的提交消息

    目标

    OP表示,目标是“在提交之前将master恢复到原来的状态”,而不会丢失更改,而这个解决方案会做到这一点。

    当我不小心做出新的承诺时,我每周至少要这样做一次 主人 而不是 develop . 通常我只有一个提交要回滚,在这种情况下,使用 git reset HEAD^ 第1行是只回滚一次提交的简单方法。

    如果你把大师的改变推到上游,不要这样做。

    其他人可能已经改变了。如果你只是在重写你的本地大师,那么当它被推到上游时没有任何影响,但是将一个重写的历史推给合作者会导致头痛。

        7
  •  28
  •   hamdiakoguz Sukima    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)
    
        8
  •  21
  •   teh_senaus    9 年前

    要在不重写历史记录的情况下执行此操作(例如,如果您已经推送了提交):

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

    然后可以不用力地推两个分支!

        9
  •  12
  •   Darkglow    11 年前

    就在这种情况下:

    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
    

    我原以为我会是负责人,但现在我是…

    为了确保降落在历史上的正确位置,更容易处理提交的混乱问题。

    git branch newbranch 
    git reset --hard #########
    git checkout newbranch
    
        10
  •  2
  •   rashok    7 年前

    1)创建一个新分支,将所有更改移动到新分支。

    git checkout -b new_branch
    

    2)然后回到旧的分支。

    git checkout master
    

    3)做吉特钢筋

    git rebase -i <short-hash-of-B-commit>
    

    4)然后打开的编辑器包含最后3个提交信息。

    ...
    pick <C's hash> C
    pick <D's hash> D
    pick <E's hash> E
    ...
    

    5)变化 pick drop 在这三项承诺中。然后保存并关闭编辑器。

    ...
    drop <C's hash> C
    drop <D's hash> D
    drop <E's hash> E
    ...
    

    6)现在从当前分支中删除最后3个提交( master )现在用力推树枝 + 在分支名称前签名。

    git push origin +master
    
        11
  •  0
  •   Pankaj Kumar    7 年前

    你可以这样做,这只是我使用的3个简单步骤。

    1)创建新的分支,在其中提交最近的更新。

    git branch <branch name>

    2)查找新分支上提交的最近提交ID。

    git log

    3)复制该提交ID注意,最新的提交列表在顶部。所以你可以找到你的承诺。你也可以通过信息找到这个。

    git cherry-pick d34bcef232f6c...

    您还可以提供一些范围的提交ID。

    git cherry-pick d34bcef...86d2aec

    现在你的工作完成了。如果您选择了正确的ID和正确的分支,那么您将成功。所以在这样做之前要小心。否则会出现另一个问题。

    现在你可以推你的代码了

    git push