我想将我已承诺掌握的最后几个提交移动到新的分支,并在做出这些提交之前将master重新带回。不幸的是,我的Git-fu还不够坚固,有什么帮助吗?
即我该如何去
master A - B - C - D - E
对此吗?
newbranch C - D - E
/
master A - B
我想将我已承诺掌握的最后几个提交移动到新的分支,并在做出这些提交之前将master重新带回。不幸的是,我的Git-fu还不够坚固,有什么帮助吗?
即我该如何去
master A - B - C - D - E
对此吗?
newbranch C - D - E
/
master A - B
Answers:
如果要将提交移至现有分支,它将如下所示:
git checkout existingbranch
git merge master # Bring the commits here
git checkout master
git reset --keep HEAD~3 # Move master back by 3 commits.
git checkout existingbranch
的 --keep
选项将保留您可能在不相关的文件中进行的所有未提交的更改,或者如果必须覆盖这些更改,则中止该操作(与执行操作类似)git checkout
。如果中止,则git stash
您的更改将重试,或者用于--hard
丢失更改(即使来自提交之间未更改的文件!)
此方法通过使用第一个命令创建新分支(git branch newbranch
)但不切换到该。然后,我们回滚当前分支(主节点)并切换到新分支以继续工作。
git branch newbranch # Create a new branch, containing all current commits
git reset --keep 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
# Warning: after this it's not safe to do a rebase in newbranch without extra care.
但是,请确保要返回多少次提交。或者,HEAD~3
您可以提供提交的哈希值(或类似origin/master
要还原回),例如:
git reset --keep a1b2c3d4
警告:在Git 2.0版及更高版本中,如果稍后git rebase
将新分支添加到原始(master
)分支上,则--no-fork-point
在改基期间可能需要显式选项,以避免丢失从master分支移走的提交。拥有branch.autosetuprebase always
一套使这个可能性比较大。有关详细信息,请参见John Mellor的答案。
对于那些想知道它为什么起作用的人(就像我刚开始那样):
您想回到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,所以它仍然指向它最初执行的提交。
git push origin master --force
更改,以使更改显示在主存储库中。
origin/master
上图中没有出现。如果您推动origin/master
并进行上述更改,那么肯定会很有趣。但这是“医生,当我这样做时会很痛”这类问题。这超出了原始问题的范围。我建议您写自己的问题来探讨您的情况,而不是劫持这个问题。
git branch -t newbranch
”。返回并再次阅读答案。 没有人建议这样做。
newbranch
基于现有的本地master
分支。执行接受的答案后,当用户都绕运行git rebase
中newbranch
,git会提醒他们,他们忘了设定上游分支,所以他们会跑git branch --set-upstream-to=master
,然后git rebase
和有同样的问题。他们可能git branch -t newbranch
首先使用。
在这种情况下,sykora公开的方法是最佳选择。但是有时不是最简单的方法,也不是通用方法。对于一般方法,请使用git cherry-pick:
为了实现OP想要的功能,它需要两个步骤:
newbranch
执行
git checkout master
git log
请注意您想要的(假设3)提交的哈希值newbranch
。在这里,我将使用:
C提交:9aa1233
D提交:453ac3d
E提交:612ecb3
注意:您可以使用前七个字符或整个提交哈希
newbranch
git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233
git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233
git cherry-pick将这三个提交应用于newbranch。
另一种方法,仅使用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 URLS部分git help clone
。
不要这样做:
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}
--keep
例如--hard
,但更安全,因为失败而不是丢弃未提交的更改)。newbranch
。newbranch
。由于它们不再被分支引用,因此它使用git的reflog来实现:HEAD@{2}
是HEAD
以前用来引用2个操作的提交,即在我们1.检出newbranch
和2. git reset
丢弃3个提交之前。警告:reflog默认是启用的,但是如果您手动禁用了reflog(例如,通过使用“裸露”的git仓库),则运行后将无法取回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 rebase
在第一个示例之后丢弃3次提交?这是因为默认情况下,git rebase
不带参数--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
使用本地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保持正确的状态。
master
。所以不,它们不是危险的错误。
这是提交错误分支的简单得多的解决方案。从master
具有三个错误提交的分支开始:
git reset HEAD~3
git stash
git checkout newbranch
git stash pop
master
master
,但所有工作文件保持不变master
工作树与HEAD〜3 状态完全相等newbranch
您现在可以像平常一样使用git add
和git commit
。所有新的提交都将添加到中newbranch
。
OP表示,目标是在不丢失更改的情况下“使母带回到那些提交之前”,而此解决方案可以做到这一点。
当我不小心对master
而不是进行新的提交时,我每周至少执行一次develop
。通常我只有一次提交来回滚,在这种情况下,git reset HEAD^
在第1行使用只是一种简单的方法来回滚一次提交。
如果您将主控的更改推到上游,请不要这样做
其他人可能已经撤消了这些更改。如果仅重写本地母版,将其推到上游不会有任何影响,但是将重写的历史推给合作者可能会引起头痛。
git add
和git commit
命令,所以我所要做的就是按下箭头并输入几次,然后开始!一切都回来了,但是现在在正确的分支上。
从技术上讲,这并不是“移动”它们,但是具有相同的效果:
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
同一件事吗?
rebase
在上述场景中的分离分支上使用。
为此,无需重写历史记录(例如,如果您已经推送了提交):
git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>
然后可以不加力地推开两个分支!
我该如何去
A - B - C - D - E
|
master
对此吗?
A - B - C - D - E
| |
master newbranch
用两个命令
给予
A - B - C - D - E
|
newbranch
和
给予
A - B - C - D - E
| |
master newbranch
如果您只需要将所有未推送的提交移动到新分支,则只需,
从当前分支创建一个新分支:git branch new-branch-name
推送您的新分支:git push origin new-branch-name
将您的旧(当前)分支还原到上一推送/稳定状态:git reset --hard origin/old-branch-name
有些人还有其他upstreams
而不是origin
,他们应该使用适当的upstream
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)更改pick
为drop
所有这3个提交。然后保存并关闭编辑器。
...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...
6)现在,最后3个提交已从当前分支(master
)中删除。现在,用力推动分支,+
在分支名称前加符号。
git push origin +master
您可以做到这只是我使用的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