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

问题描述:

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

master A - B - C - D - E

对此:

newbranch     C - D - E
             /
master A - B 

解决方案1:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

移动到现有分支

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

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

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

搬到新的分支

警告:此方法有效,因为您正在使用第一个命令创建一个新分支:git branch newbranch。如果您想将提交移动到现有分支,您需要在执行 git reset --hard HEAD~3 之前将更改合并到现有分支(请参阅移动到现有分支< /em> 上面)。 如果您不先合并更改,它们将会丢失。

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

# 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,而只需在 masterorigin/master 的引用) > (/current) 分支,例如:

git reset --hard a1b2c3d4

*1 你只会从主分支“丢失”提交,但别担心,你会在新分支中拥有这些提交!

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

git push origin master --force

警告: 对于 Git 2.0 及更高版本,如果您稍后在原始 (master) 分支上创建新分支 git rebase,则在变基期间可能需要显式 --no-fork-point 选项以避免丢失结转的提交。设置 branch.autosetuprebase always 使这更有可能。有关详细信息,请参阅 John Mellor’s answer。

尤其是,不要试图返回比您上次将提交推送到其他人可能已从中提取的另一个存储库的点更远的地方。

想知道您是否可以解释为什么会这样。对我来说,您正在创建一个新分支,从您仍在使用的旧分支中删除 3 个提交,然后检查您创建的分支。那么你删除的提交是如何神奇地出现在新分支中的呢?

@Jonathan Dumaine:因为我在从旧分支中删除提交之前创建了新分支。他们还在新的分店里。

git 中的分支只是指向历史提交的标记,没有任何内容被克隆、创建或删除(标记除外)

另请注意:不要对工作副本中未提交的更改执行此操作!这只是咬我! :(

解决方案2:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

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

您想回到 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 将 master 指针移回两次提交。但是由于您没有移动 newBranch,它仍然指向它最初所做的提交。

我还需要执行 git push origin master --force 以使更改显示在主存储库中。

这个答案会导致提交丢失:下次您 git rebase 时,这 3 个提交将从 newbranch 中默默地丢弃。有关详细信息和更安全的替代方案,请参阅 my answer。

@John,那是胡说八道。在不知道自己在做什么的情况下进行变基会导致提交丢失。如果您丢失了提交,我很抱歉,但这个答案并没有丢失您的提交。请注意,上图中没有出现 origin/master。如果您推送到 origin/master 然后进行上述更改,当然,事情会变得有趣。但这是一个“医生,我这样做的时候很痛”之类的问题。这超出了原始问题的范围。我建议您编写自己的问题来探索您的场景,而不是劫持这个问题。

@John,在您的回答中,您说“不要这样做!git branch -t newbranch”。回去再读一遍答案。 没有人建议这样做。

@Kyralessa,当然,但是如果您查看问题中的图表,很明显他们希望 newbranch 基于他们现有的本地 master 分支。执行接受的答案后,当用户开始在 newbranch 中运行 git rebase 时,git 会提醒他们忘记设置上游分支,因此他们将运行 git branch --set-upstream-to=master 然后 git rebase 并具有相同的问题。他们不妨一开始就使用 git branch -t newbranch。

解决方案3:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

一般来说…

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

为了实现 OP 想要的,它有两个步骤:

第 1 步 - 记下您希望在新分支上从 master 提交的哪些提交

执行

git checkout master
git log

请注意您想要在 newbranch 上的(比如 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

git cherry-pick 将这三个提交应用于 newbranch。

如果您不小心提交了错误的非 master 分支,这将非常有效,此时您应该创建一个新的功能分支。

git cherry-pick 上的信息很好,但是这篇文章中的命令不起作用。 1) 'git checkout newbranch' 应该是 'git checkout -b newbranch' 因为 newbranch 尚不存在; 2)如果您从现有的 master 分支签出 newbranch ,它已经包含了这三个提交,因此选择它们没有用。归根结底,要获得 OP 想要的东西,您仍然需要进行某种形式的重置 --hard HEAD。

+1 在某些情况下是一种有用的方法。如果您只想将自己的提交(与其他提交)拉入新分支,这很好。

这是更好的答案。这样你就可以将提交移动到任何分支。

我无法使用cherry-pick范围,在其中一个提交上得到“错误:无法应用c682e4b ...”,但是当一次对每个提交使用cherry-pick时,它工作正常(我有在错误发生后执行 git cherry-pick --abort)。评论是因为我认为那些是等价的。这是 git 版本 2.3.8 (Apple Git-58)

解决方案4:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

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

不要这样做:

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,但更安全,因为失败而不是丢弃未提交的更改)。

然后它分叉新分支。

然后它将这 3 个提交挑选回新分支。由于它们不再被分支引用,它通过使用 git 的 reflog 来做到这一点: HEAD@{2} 是 HEAD 之前用于引用 2 个操作的提交,即在我们 1. 签出 newbranch 和 2. 使用 git 之前重置以丢弃 3 个提交。

警告:默认情况下启用 reflog,但如果您手动禁用它(例如,通过使用“裸”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 来尝试对上游分支被强制推送的鲁棒性。

假设您在 origin/master 包含提交 M1、M2、M3 时分支了它,然后您自己进行了三个提交:

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

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

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

使用您的本地 reflog,git rebase 可以看到您是从 origin/master 分支的较早版本派生出来的,因此 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 重写上游的历史记录以删除提交,因此下次运行 git rebase 时它会像其他任何操作一样丢弃它们已从上游删除的提交。

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

有关更多详细信息,请参阅 git rebase 和 git merge-base 文档中的 --fork-point 定义。

这个答案说“不要这样做!”上面没有人建议做的事情。

大多数人不会重写已发布的历史记录,尤其是在 master。所以不,他们没有危险的错误。

@Kyralessa,如果您设置了 git config --global branch.autosetuprebase always,您在 git branch 中引用的 -t 会隐式发生。即使您不这样做,我already explained告诉您,如果您在执行这些命令后设置跟踪,也会出现同样的问题,因为 OP 可能打算根据他们的问题这样做。

@RockLee,是的,解决这种情况的一般方法是从安全的起点创建一个新的分支(newbranch2),然后挑选您想要保留的所有提交(从 badnewbranch 到 newbranch2)。 Cherry-picking 将为提交提供新的哈希值,因此您将能够安全地重新设置 newbranch2(并且现在可以删除 badnewbranch)。

太糟糕了,这不是公认的答案。正如您所描述的,我按照接受的答案中的步骤操作并丢失了 6 次提交!

解决方案5:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

还有另一种方法,只使用 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 到 . 是一个很好的技巧。

当前目录。我想这只有在您位于顶级目录中时才有效。

本地推送很有趣,但仔细想想,这里的 git branch -f 有什么不同?

@GerardSexton . 是现任董事。 git 可以推送到 REMOTES 或 GIT URL。 path to local directory 是受支持的 Git URL 语法。请参阅 git help clone 中的 GIT URLS 部分。

我不知道为什么这个评分不高。非常简单,并且没有 git reset --hard 的微小但潜在的危险。

@Godsmith 我的猜测是人们更喜欢三个简单的命令而不是两个稍微晦涩的命令。此外,投票最多的答案由于首先显示的性质而获得更多的赞成票。

解决方案6:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

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

这是提交到错误分支的更简单的解决方案。从具有三个错误提交的分支 master 开始:

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

什么时候用这个?

如果您的主要目的是回滚 master

您想保留文件更改

你不关心错误提交的消息

你还没有推送

你希望这很容易记住

您不希望出现临时/新分支、查找和复制提交哈希以及其他令人头疼的问题

这是做什么的,按行号

撤消对 master 的最后三个提交(及其消息),但保留所有工作文件不变 隐藏所有工作文件更改,使 master 工作树完全等于 HEAD~3 状态 切换到现有分支 newbranch 应用隐藏的更改到您的工作目录并清除存储

您现在可以像往常一样使用 git add 和 git commit。所有新提交都将添加到 newbranch。

这不做什么

它不会让随机的临时树枝把你的树弄得乱七八糟

它不会保留错误的提交消息,因此您需要向此新提交添加新的提交消息

更新!使用向上箭头滚动命令缓冲区以重新应用先前的提交及其提交消息(感谢@ARK)

目标

OP 表示目标是在不丢失更改的情况下“让 master 回到提交之前”,而这个解决方案就是这样做的。

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

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

其他人可能已经取消了这些更改。如果你只是重写你的本地master,那么当它被推送到上游时没有影响,但是将重写的历史推送给合作者可能会让人头疼。

谢谢,很高兴我读了很多过去/通读了很多东西才能到达这里,因为这对我来说也是一个很常见的用例。我们有那么不典型吗?

我认为我们是完全典型的,“我错误地承诺掌握的oops”是需要恢复少数或更少提交的最常见用例。幸运的是,这个解决方案非常简单,我现在已经记住了。

这应该是公认的答案。它简单明了,易于理解且易于记忆

如果您碰巧在您的 CLI(命令行)历史记录中有提交消息,您也可以轻松地取回提交消息。我碰巧有我使用的 git add 和 git commit 命令,所以我所要做的就是按向上箭头并输入几次然后砰!一切都回来了,但现在在正确的分支上。

如果“newbranch”还不存在,那么您可以在重置后将未暂存的更改移动到新分支,使用此命令无需存储:git checkout -b newbranch

解决方案7:

与HuntsBot一起,探索全球自由职业机会–huntsbot.com

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

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)

是的,您也可以在上述场景中的分离分支上使用 rebase。

解决方案8:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

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

git checkout master
git revert 
git checkout -b new-branch
git cherry-pick 

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

但是随后您必须处理还原情况,根据您的情况,这可能会更加棘手。如果您恢复分支上的提交,Git 仍然会看到这些提交已经发生,因此为了撤消它,您必须恢复恢复。这让很多人感到痛苦,特别是当他们恢复合并并尝试将分支合并回来时,却发现 Git 认为它已经合并了该分支(这是完全正确的)。

这就是为什么我在最后挑选提交,到一个新的分支上。这样 git 将它们视为新提交,从而解决了您的问题。

这比最初看起来更危险,因为您在没有真正了解此状态的含义的情况下更改存储库历史的状态。

我不遵循您的论点-此答案的重点是您没有更改历史记录,而只是添加了新提交(这有效地撤消了重做更改)。这些新提交可以正常推送和合并。

解决方案9:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

最简单的方法:

  1. 将 master 分支重命名为您的 newbranch(假设您在 master 分支上):
git branch -m newbranch

  1. 从您希望的提交创建 master 分支:
git checkout -b master 

例如 git checkout -b master a34bc22

注意: newbranch 的上游是 origin/master。

喜欢这个解决方案,因为您不必重写 git commit 标题/描述。

这不会弄乱远程上游分支吗? newbranch 现在不是指向 origin/master 吗?

解决方案10:

huntsbot.com – 高效赚钱,自由工作

刚遇到这种情况:

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

解决方案11:

huntsbot.com – 高效赚钱,自由工作

我怎么能从这个

A - B - C - D - E 
                |
                master

到这个?

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

有两个命令

git 分支 -m 主新分支

给予

A - B - C - D - E 
                |
                newbranch

git 分支主 B

给予

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

是的,这很有效,而且很容易。 Sourcetree GUI 对 git shell 中所做的更改有点困惑,但在 fetch 之后又好了。

是的,他们就像问题一样。前几个图表旨在与问题中的图表等效,只是为了在答案中进行说明而按照我想要的方式重新绘制。基本上将主分支重命名为 newbranch 并在您想要的位置创建一个新的主分支。

原文链接:https://www.huntsbot.com/qa/Vd1D/move-the-most-recent-commits-to-a-new-branch-with-git?lang=zh_CN

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值