如何将我最近在 master 上的提交移动到新分支,并将 master 重置为在提交之前?例如从这里:
master A - B - C - D - E
对此:
newbranch C - D - E
/
master A - B
移动到现有分支
如果您想将提交移动到现有分支,它将如下所示:
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。
对于那些想知道它为什么起作用的人(就像我一开始一样):
您想回到 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
以使更改显示在主存储库中。
origin/master
。如果您推送到 origin/master
然后进行上述更改,当然,事情会变得有趣。但这是一个“医生,我这样做的时候很痛”之类的问题。这超出了原始问题的范围。我建议您编写自己的问题来探索您的场景,而不是劫持这个问题。
git branch -t newbranch
”。回去再读一遍答案。 没有人建议这样做。
newbranch
基于他们现有的本地 master
分支。执行接受的答案后,当用户开始在 newbranch
中运行 git rebase
时,git 会提醒他们忘记设置上游分支,因此他们将运行 git branch --set-upstream-to=master
然后 git rebase
并具有相同的问题。他们不妨一开始就使用 git branch -t newbranch
。
一般来说...
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。
大多数以前的答案都是危险的错误!
不要这样做:
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
。所以不,他们没有危险的错误。
git config --global branch.autosetuprebase always
,您在 git branch
中引用的 -t
会隐式发生。即使您不这样做,我already explained告诉您,如果您在执行这些命令后设置跟踪,也会出现同样的问题,因为 OP 可能打算根据他们的问题这样做。
还有另一种方法,只使用 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
有什么不同?
.
是现任董事。 git 可以推送到 REMOTES 或 GIT URL。 path to local directory
是受支持的 Git URL 语法。请参阅 git help clone
中的 GIT URLS 部分。
使用 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,那么当它被推送到上游时没有影响,但是将重写的历史推送给合作者可能会让人头疼。
git add
和 git commit
命令,所以我所要做的就是按向上箭头并输入几次然后砰!一切都回来了,但现在在正确的分支上。
git checkout -b newbranch
从技术意义上讲,这不会“移动”它们,但具有相同的效果:
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
。
要在不重写历史记录的情况下执行此操作(即,如果您已经推送了提交):
git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>
然后可以不用力推动两个分支!
最简单的方法:
1. 将 master
分支重命名为您的 newbranch
(假设您在 master
分支上):
git branch -m newbranch
2. 从您希望的提交创建 master
分支:
git checkout -b master <seven_char_commit_id>
例如 git checkout -b master a34bc22
注意: newbranch
的上游是 origin/master
。
newbranch
现在不是指向 origin/master
吗?
刚遇到这种情况:
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
我怎么能从这个
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
如果您只需要将所有未推送的提交移动到新分支,那么您只需要,
从当前分支创建一个新分支:git branch new-branch-name 推送您的新分支:git push origin new-branch-name 将您的旧(当前)分支恢复到上次推送/稳定状态:git reset --hard origin /旧分行名称
有些人还有其他 upstreams
而不是 origin
,他们应该使用适当的 upstream
你可以做到这只是我使用的 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
这里的大多数解决方案都会计算您想要返回的提交数量。我认为这是一种容易出错的方法。计数需要重新计数。
您可以简单地传递您希望在 HEAD 的提交的提交哈希,或者换句话说,您希望通过以下方式成为最后一次提交的提交:
(注意见提交哈希)
为了避免这种情况:
1) git checkout master
2) git branch <feature branch> master
3) git reset --hard <commit hash>
4) git push -f origin master
TLDR
git checkout branch_to_remove_commits
git reset --hard ${hash_of_new_tip}
git checkout -b branch_to_store_commits
# Move commits (single hash, list of hashes or range ffaa..ffoo)
git cherry-pick ${commit_hash}
git push --set-upstream origin branch_to_store_commits
# Switch back to last branch
git checkout -
git push -f
为了我
git log --pretty=oneline -n ${NUMBER}
最适合识别有问题的提交哈希。
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) 现在从当前分支 (master
) 中删除最后 3 个提交。现在强制推送分支,在分支名称前加上 +
符号。
git push origin +master
我很惊讶没有人推荐这种方式:
git checkout master
git checkout <commit hash from which you want to split>
git checkout -b new_branch
git rebase master
git checkout master
git reset --hard <commit hash you splitted>
解释:
步骤我们检查我们想要拆分的提交,然后从这个提交创建一个新的分支,做 rebase 将同步 new_branch 和 master。所以现在我们有两个相同的分支,具有相同的提交并在 master 上重置,我们在拆分列表项后清理最后一次提交
使用 Emacs 的 git瓷器 Magit,您只需点击 b s
(magit-branch-spinoff) 即可完成此操作。系统会要求您输入新分支的名称,然后按 Enter 键,瞧。
此命令创建并签出一个从当前分支开始并跟踪的新分支。该分支又被重置为它与上游共享的最后一个提交。如果当前分支没有上游或未推送的提交,则无论如何都会创建新分支,并且不会触及先前的当前分支。这对于在旧分支(可能但不一定是“主”)上已经开始工作后创建功能分支很有用。
我必须将 7 个提交从一个旧分支移动到一个新分支。
git checkout old-branch # in the example, master
git reset --hard h4sh # h4sh is the hash for the commit
git checkout -b new-branch
git push origin new-branch
之后,这两个分支都与我所做的 7 次提交有关。在 git checkout new-branch
之后,git log
和 git status
都很好,但是,当访问旧分支 (git checkout old-branch
) 时,我收到消息“git 落后 7 次提交并且可以快进”。对我有用的删除此消息的是以下内容:
git checkout old-branch
git status
> git is behind by 7 commits and can be fast-forwarded
git push origin old-branch -f
在该步骤之后,最后 7 个提交仅被引用为新分支,而之前的提交在 Bitbucket 树中被引用为旧分支和新分支。
如果您是像我这样的 UI 人员并且您正在使用 Visual Studio。然后您可以执行以下操作:就我而言,我想将最新的提交提交到另一个分支。
右键单击之前的一个(提交)。
https://i.stack.imgur.com/2tz8w.png
因此,所有提交更改都将显示在 Git 更改窗格中。现在,隐藏您的更改
https://i.stack.imgur.com/UJxJg.png
转到您的目标分支或从右下角创建一个新分支。
https://i.stack.imgur.com/LnwZO.png
在“Git Changes”中双击你最新的 Stash。 “存储详细信息”窗格将打开。单击“Pop”,然后解决冲突(如果存在)。
https://i.stack.imgur.com/uEGUU.png
最后,提交您的更改。
从其他帖子中获取一些想法,避免与重置有关,并且极度偏执,我的解决方案是:
git branch # 更改在新分支中可用 git push # 上传,您可能需要弄乱“--set-upstream”,例如 git push --set-upstream https:/// 通过 a 检查新分支是否在 git GUI 销毁当前目录从 git 存储库重新克隆
我并不骄傲,但我保留了我的数据;)
不定期副业成功案例分享