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

时间:2009-10-27 03:07:35

标签: git git-branch branching-and-merging

我想将我已经承诺掌握的最后几个提交移动到一个新的分支,然后在完成这些提交之前将主人带回来。不幸的是,我的Git-fu还不够强大,有什么帮助吗?

即。我怎么能从这个

master A - B - C - D - E

到此?

newbranch     C - D - E
             /
master A - B 

15 个答案:

答案 0 :(得分:5468)

转移到新分支

警告:此方法有效,因为您使用第一个命令创建新分支: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,只需在 master <上提供你想要“恢复到”的提交的哈希值(或像 origin / master 这样的引用) / em>(/ current)分支,例如:

git reset --hard a1b2c3d4

* 1您将从主分支“丢失”提交,但不要担心,您将在newbranch中进行这些提交!

警告:使用Git 2.0及更高版本,如果您稍后git rebase原始(master)分支上的新分支,您可能需要一个明确的{{1在rebase期间的选项,以避免丢失结转提交。设置--no-fork-point会使此更有可能。有关详细信息,请参阅John Mellor's answer

转移到现有分支

如果您想将提交移至现有分支,它将如下所示:

branch.autosetuprebase always

答案 1 :(得分:924)

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

您想要返回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

由于分支只是一个指针, master 指向最后一次提交。当您创建 newBranch 时,您只需创建一个指向上一次提交的新指针。然后使用git reset指针移回两次提交。但是既然你没有移动 newBranch ,它仍然指向它最初的提交。

答案 2 :(得分:390)

一般......

在这种情况下,sykora公开的方法是最佳选择。但有时并不是最容易的,也不是一般的方法。对于一般方法,请使用 git cherry-pick

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

第1步 - 注意在newbranch

上提交您想要的主人

执行

git checkout master
git log

请注意newbranch上所需的哈希值(例如3)。我将在这里使用:
C commit:9aa1233
D commit:453ac3d
E commit:612ecb3

  

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

第2步 - 将它们放在newbranch

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 cherry-pick 将这三个提交应用于newbranch。

答案 3 :(得分:287)

另一种方法是使用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.是一个很好的诀窍。

答案 4 :(得分:263)

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

不要这样做:

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,但更安全,因为失败而不是丢弃未提交的更改。)
  • 然后它将newbranch分开。
  • 然后樱桃挑选这3个提交回newbranch。由于它们不再被分支引用,它通过使用git&#39; s reflog来实现:HEAD@{2}HEAD用于引用2个操作的提交,即我们之前1.签出newbranch和2.使用git reset放弃3次提交。

警告:默认情况下启用了reflog,但如果您已手动禁用它(例如使用&#34;裸&#34; git存储库),您将无法启用在运行git reset --keep HEAD~3之后获得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 rebase在第一个例子之后会丢弃3个提交?这是因为没有参数的git rebase默认情况下启用--fork-point选项,它使用本地reflog尝试对强制推送的上游分支进行健壮。

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

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

然后有人通过强制推动origin / master来删除M2来重写历史记录:

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

使用本地reflog,git rebase可以看到你从源/主分支的早期版本分叉,因此M2和M3提交实际上不是主题分支的一部分。因此,它合理地假设,由于M2已从上游分支中删除,因此一旦主题分支被重新定位,您就不再需要在主题分支中使用它了:

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

这种行为很有意义,而且在变基时通常是正确的做法。

以下命令失败的原因是:

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

是因为他们让reflog处于错误的状态。 Git认为newbranch已经在包含3次提交的修订版中分离了上游分支,然后reset --hard重写了上游的历史记录以删除提交,因此下次运行时{ {1}}它会像从上游删除的任何其他提交一样丢弃它们。

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

有关详细信息,请参阅git rebasegit merge-base文档中git rebase的定义。

答案 5 :(得分:64)

使用git stash

的更简单的解决方案

然后以下更简单(从分支master开始,有三个错误的提交):

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

何时使用?

  • 如果您的主要目的是回滚master
  • 您希望保留文件更改
  • 您不关心错误提交的消息
  • 你尚未推动
  • 您希望此内容易于记忆
  • 您不希望出现临时/新分支,查找和复制提交哈希以及其他令人头疼的问题。

这是做什么的,按行号

  1. 将最后三次提交(及其消息)撤消到master,但保留所有正常工作的文件
  2. 隐藏所有工作文件更改,使master工作树完全等于HEAD~3状态
  3. 切换到现有分支newbranch
  4. 将隐藏的更改应用于您的工作目录并清除存储
  5. 您现在可以像往常一样使用git addgit commit。所有新提交都将添加到newbranch

    这不做什么

    • 它不会让随机的临时分支混乱你的树
    • 它不会保留错误的提交和提交消息,因此您需要向此新提交添加新的提交消息

    目标

    OP表示目标是“在做出这些提交之前将主人带回来”#34;不失变化,这个解决方案就是这样做的。

    我偶然向master而不是develop提交新提交时,每周至少执行一次。通常我只有一次提交回滚,在这种情况下,在第1行使用git reset HEAD^是一种简单的方法来回滚一次提交。

    如果您推动上游主人的改变,请不要这样做

    其他人可能已经取消了这些变化。如果您只是重写本地主人,那么当它向上游推进时没有任何影响,但是将重写的历史记录推送给协作者会引起麻烦。

答案 6 :(得分:29)

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

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)

答案 7 :(得分:22)

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

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

然后可以无力推动两个分支!

答案 8 :(得分:13)

只是这种情况:

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

我期望提交我将成为HEAD,但现在提交L就是......

为了确保登陆历史中的正确位置,更容易使用提交的哈希值

git branch newbranch 
git reset --hard #########
git checkout newbranch

答案 9 :(得分:10)

执行此操作的最简单方法:

1。。将master分支重命名为您的newbranch(假设您位于master分支中):

git branch -m newbranch

2。。根据您希望的提交创建master分支:

git checkout -b master <seven_char_commit_id>

例如git checkout -b master a34bc22

答案 10 :(得分:2)

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

git checkout -b new_branch

2)然后回到老分行。

git checkout master

3)做git rebase

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)在所有这3次提交中将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 :(得分:1)

我该怎么走

A - B - C - D - E 
                |
                master

对此吗?

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

有两个命令

  • git branch -m master newbranch

给予

A - B - C - D - E 
                |
                newbranch

  • git分支母版B

给予

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

答案 12 :(得分:0)

你可以做到这只是我使用的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

答案 13 :(得分:0)

如果您只需要将所有未按下提交移动到新分支, 那么您只需要

    从当前分支
  1. 创建一个新分支git branch new-branch-name

  2. 推送您的新分支git push origin new-branch-name

  3. 还原您的旧(当前)分支到上一个推送/稳定状态:git reset --hard origin/old-branch-name

有些人还有其他upstreams而不是origin, 他们应该使用适当的upstream

答案 14 :(得分:0)

这里的大多数解决方案都会计算您要返回的提交量。我认为这是一种容易出错的方法。计数需要重新计数。

您可以通过以下方式简单地传递要在HEAD处提交的提交的提交哈希值:换句话说,您想要作为最后一次提交的提交:

(注意,请参阅提交哈希)

为避免这种情况:

1) git checkout master

2) git branch <feature branch> master

3) git reset --hard <commit hash>

4) git push -f origin master