乐闻世界logo
搜索文章和话题

Git 如何将最近的提交移动到新的分支?

9 个月前提问
5 个月前修改
浏览次数98

5个答案

1
2
3
4
5

对于那些想知道它为什么有效的人

您想返回 C,并将 D 和 E 移动到新分支。

一开始看起来是这样的:

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

git branch newBranch

shell
newBranch A-B-C-D-E (HEAD) master

git reset --hard HEAD~2

shell
newBranch A-B-C-D-E (HEAD) master

由于分支只是一个指针,因此_master指向最后一次提交。当您创建newBranch_时,您只需创建一个指向最后一次提交的新指针。然后使用 git reset您将_主_指针移回两个提交。但由于您没有移动_newBranch_,它仍然指向它最初所做的提交。

2024年6月29日 12:07 回复

当你想要将最近的提交移动到一个新分支时,你可以按照以下步骤操作。这里假设你已经在你的本地仓库中,并且你所要移动的提交已经在当前的分支上。

  1. 创建一个新分支,但不要切换到那个分支:

    sh
    git branch new-branch-name
  2. 重置当前分支的HEAD到你想要移动的提交的父提交。如果你只想移动最近的一个提交,可以执行:

    sh
    git reset --hard HEAD~1

    如果是最近的几个提交,可以将 1替换为相应的数字。

  3. 切换到新分支

    sh
    git checkout new-branch-name

下面是这个过程的一个示例:

假设你的最近一次提交是你想要移动到新分支的提交。

首先,创建新分支但停留在当前分支:

sh
git branch new-feature

接着,使用 git reset将当前分支重置到上一次提交(即移动前的状态):

sh
git reset --hard HEAD~1

然后,切换到你的新分支:

sh
git checkout new-feature

现在,你的最近一次提交在 new-feature分支上,而原分支则回到了移动提交之前的状态。

请注意,使用 git reset --hard会丢弃当前分支上 HEAD之后的改动,所以在执行这个命令之前,请确保你确实不需要这些改动或者已经将它们保存在别的地方。如果你想保留对这些文件的更改而不是提交,可以使用 git reset --soft,这样改动会保留在工作目录中。

2024年6月29日 12:07 回复

一般来说...

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

要实现 OP 想要的目标,需要分两步:

第 1 步 - 记下您想要的 master 提交newbranch

执行

shell
git checkout master git log

记下您想要的(比如 3)提交的哈希值newbranch。这里我将使用:
C commit: 9aa1233
D commit: 453ac3d
E commit:612ecb3

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

第 2 步 - 将它们放在newbranch

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

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

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

_gitcherry-pick_将这三个提交应用于 newbranch。

2024年6月29日 12:07 回复

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

不要这样做:

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

当您下次运行git rebase(或git pull --rebase)时,这 3 个提交将被默默地丢弃newbranch!(见下面的解释)

相反,这样做:

shell
git reset --keep HEAD~3 git checkout -t -b newbranch git cherry-pick ..HEAD@{2}
  • 首先,它会丢弃最近的 3 个提交(--keep类似于--hard,但更安全,因为失败而不是丢弃未提交的更改)。
  • 然后它分叉了newbranch
  • 然后它会挑选这 3 个提交回到newbranch. 由于它们不再被分支引用,因此它通过使用 git 的reflog来实现:是用于引用 2 个操作之前的HEAD@{2}提交,即在我们 1. 签出和 2. 用于丢弃 3 个提交之前。HEAD``newbranch``git reset

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

不依赖 reflog 的替代方案是:

shell
# 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默认启用该选项,它使用本地引用日志来尝试对强制推送的上游分支保持鲁棒性。

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

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

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

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

使用本地引用日志,git rebase可以看到您是从 origin/master 分支的早期版本分叉的,因此 M2 和 M3 提交实际上并不是主题分支的一部分。因此,它合理地假设,由于 M2 已从上游分支中删除,因此一旦主题分支重新建立基础,您就不再希望它出现在主题分支中:

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

这种行为是有道理的,并且通常是变基时正确的做法。

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

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

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

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

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

2024年6月29日 12:07 回复

还有另一种方法可以做到这一点,只需使用 2 个命令。还可以保持您当前的工作树完好无损。

shell
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

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

能够push做到这一点.是一个很好的技巧。

2024年6月29日 12:07 回复

你的答案