我想把我对master的最后几次承诺转移到一个新的分支,并在做出这些承诺之前将master带回。不幸的是,我的功夫还不够强,有什么帮助吗?
也就是说,我怎样才能摆脱这个困境呢?
1
| master A - B - C - D - E |
对此?
1 2 3
| newbranch C - D - E
/
master A - B |
- 注:我在这里问的是相反的问题
- eddmann.com/posts/…这一个有效
- 这里的评论被清除了吗?我问是因为在我每两个月访问这个问题的时候,我总是滚动这个评论。
移动到新分支
警告:此方法有效,因为您正在使用第一个命令创建一个新的分支:git branch newbranch。如果要将提交移动到现有分支,则需要在执行git reset --hard HEAD~3之前将更改合并到现有分支(请参见下面的移动到现有分支)。如果不先合并更改,则更改将丢失。
除非涉及到其他情况,否则很容易通过分支和回滚来完成。
1 2 3 4
| # 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,只需提供要在主(当前)分支上"还原到"的提交(或引用,如origin/master)的哈希,例如:
1
| git reset --hard a1b2c3d4 |
*1您只会"失去"来自主分支机构的承诺,但不要担心,您将在Newbranch中获得这些承诺!
警告:对于Git版本2.0及更高版本,如果您在原来的(master分支上更新git rebase分支,您可能需要在rebase期间明确的--no-fork-point选项,以避免丢失结转的承诺。拥有branch.autosetuprebase always集使这更可能发生。详情请参阅约翰·梅勒的回答。
移动到现有分支
如果要将提交移动到现有分支,它将如下所示:
1 2 3 4 5
| 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 |
- 尤其是,不要试图追溯到上次将提交推送到另一个存储库(可能是其他人从中提取的)之后。
- 想知道你能不能解释一下为什么这是有效的。对我来说,您正在创建一个新的分支,从仍在使用的旧分支中删除3个提交,然后签出您所创建的分支。那么,您移除的提交如何神奇地出现在新的分支中呢?
- @乔纳森:因为我创建了新的分支,然后从旧的分支中删除了承诺。他们还在新的分店里。
- 我懂了。分支使克隆处于当前状态。出于某种原因,我假设那些提交不会被克隆,因为它们不在远程服务器上(太多git-svn-haha)。谢谢您。
- Git中的分支只是指向提交的标记,在历史中没有克隆、创建或删除任何内容(标记除外)。
- 但是您将如何将其推送到另一个存储库呢?当我在git reset --hard HEAD~3之后做git push时,我会被拒绝。
- 我会将"Git Reset--Hard Head ~3"更改为"Git Reset--Hard Origin/Master"。那么你就不必知道你领先于主人的承诺有多少了。它将使您的本地主服务器与服务器上的主服务器同步。
- 另请注意:不要对工作副本中未提交的更改执行此操作!这只咬了我一口!:(
- @自动按钮哦,是的。但不管怎样,Stash还是很有效。
- 呃,我们需要投票支持亚当·塔特尔的命令。它目前没有显示,因为其他评论有更多的赞成票。我只是被这个咬了一口,因为在重置之前我没有看到他的评论:(
- 为了消除将master重置回错误提交的风险,这一次将分支设置回更精确的位置。"git reset—hard 1a0c9b,其中"1a0c9b"是要返回的提交哈希的开始。
- Mazrick的评论没有被更多否决,有什么理由吗?这听起来比计算提交次数更可靠。
- 为什么经常推荐git reset --hard?为什么不是git checkout head~3 .(注意.,否则你会在一个无头的树枝上结束)?(当然,可以用上面提到的替代品来代替head~3)。似乎checkout更安全,因为它不会破坏你的历史。我错过什么了吗?
- 是的,您确实这样做了,最有可能的人意外地犯了错误的分支,他们希望在将这些提交放到另一个分支之后,硬删除这些提交,另一个分支最终很可能在审阅和测试后合并到意外提交的分支中。
- "如果不首先合并更改,它们将丢失。"——这并非完全正确。承诺只会变得超然。垃圾收集需要很长时间(比如大于1000次提交,或者10000次提交)?所以你仍然可以恢复它们。要么记住沙斯,要么和埃多克斯1〔7〕或埃多克斯1〔8〕一起找到它们。有关丢失提交的详细信息
- 我的意思是30天,而不是超过1000次。
- 为什么你要做不必要的退房和垃圾你的工作树?git checkout -b newbranch; git branch -f master master~3(或…origin/master)。
- 如果我去了新牧场,做了一个git rebase master的话,新牧场会不会取消承诺?如果我用merge而不是rebase,我会避免这个吗?
- 如果您想推动主分支,那么(或者您回滚的原始分支)使用git push --force origin master。如果您不键入force,git将拒绝push,并要求您从远程提取回滚提交。
- 我用的是:git push --force -u origin master,然后是git push -u origin newbranch。
- Sykora也许你想看看上面的元讨论:meta.stackoverflow.com/questions/298067/…-也许你想把你的答案扩展到引用下面的非常整洁的版本@aragaer(我认为这是非常低估的贡献)。
- 如果可能的话,我想知道如何使用新的Github桌面软件。对于某些事情,我知道如何绕过命令行,但是在基于文本的环境中,git太复杂了。现在有了一个可访问的、免费的、基于GUI的官方解决方案,我想使用它。
- 非常感谢为我工作得很好,只有那时重置一个git stash应用,我改为正确的分支和应用git stash pop!用它我得到我的变化和我的通信地点!
- 在你不想离开我之后
- 这个答案会导致承诺丢失:下一次当你的git rebase时,3个承诺将从newbranch中被悄悄丢弃。有关详细信息和更安全的备选方案,请参阅我的答案。
- 听约翰·梅勒说。我做这件事失去了承诺…
- 我用新的分支方法测试了程序。但这并不能改变承诺。正如有人指出的那样,强制是必要的,并且需要将头部重新连接到提交尾部。不是吗?(很抱歉,如果我错过了大量评论中的一些内容。)
- 遵循这些指示使我失去了所有试图移动的承诺。现在我必须重新做这项工作。重置master的提交操作会改变分支的分叉点,而分支本身没有提交操作。
- 主分支机构和新分支机构的git push --force -u origin都应该是答案的一部分。
- 我遵循这些指示,认为我丢失了所有的更改,然后改为新的分支,它们就在那里。有点让我心烦意乱,直到我意识到它为什么起作用。我是哑巴。谢谢。
对于那些想知道为什么它会起作用的人(就像我起初那样):
你想回到C,把D和E移到新的分支。首先是这样的:
1 2 3
| A-B-C-D-E (HEAD)
↑
master |
在git branch newbranch之后:
1 2 3 4 5
| newBranch
↓
A-B-C-D-E (HEAD)
↑
master |
在git reset --hard HEAD~2之后:
1 2 3 4 5
| newBranch
↓
A-B-C-D-E (HEAD)
↑
master |
由于分支只是一个指针,所以master指向最后一次提交。当您创建Newbranch时,只需为最后一次提交创建一个新指针。然后使用git reset将主指针移回两次提交。但是,由于你没有移动Newbranch,它仍然指向它最初所做的承诺。
- 您确实需要——很难,因为否则git会将重置提交中的更改留在工作目录中(或者至少为我保留了这些更改)。
- @安德鲁,你说得对。一个月前我写这篇文章的时候,我的思想还是有点混乱。固定的。
- @Andrew without——很难将更改留在工作目录中。在这种情况下,我们可以创建一个新分支,并在新分支上对这些更改进行通信。这种方法是否会消除确保其他人没有改变您的需求?
- 我还需要做一个git push origin master --force,以便更改显示在主存储库中。
- 如果有人已经在你的旧链条上做出了承诺,那么使用--force-with-lease可能是个好主意。
- 这个答案会导致承诺丢失:下一次当你的git rebase时,3个承诺将从newbranch中被悄悄丢弃。有关详细信息和更安全的备选方案,请参阅我的答案。
- @约翰,那是胡说八道。在不知道自己在做什么的情况下重新平衡会导致承诺丢失。如果你输了承诺,我很抱歉,但这个答案没有失去你的承诺。注意上面的图表中没有出现origin/master。如果你推到origin/master,然后进行上面的更改,当然,事情会变得很有趣。但那是一个"医生,我这样做会很痛"的问题。它超出了原始问题的范围。我建议你写自己的问题来探索你的场景,而不是劫持这个场景。
- @Kyralessa:这个问题要求newbranch以其现有的本地分支(恰好称为master)为基础,这意味着它应该将其作为本地上游进行跟踪。在将3个承诺拆分为newbranch之后,如果现有的本地分支后来发生了更改(例如在本地添加新的承诺,或从源站/主服务器拉入),那么git rebase是将现有本地分支的更改合并为newbranch的正确合理的方法。(合并是一种有效的选择,但会导致更复杂的历史)。
- @约翰,在你的回答中,你说"不要这样做!git branch -t newbranch。回去再看一遍答案。没人建议这么做。
- @当然,Kyralessa,但是如果你看这个问题的图表,很明显他们希望newbranch基于他们现有的本地master分支。当用户在newbranch中运行git rebase后,git会提醒用户忘记设置上游分支,所以会先运行git branch --set-upstream-to=master,然后运行git rebase,出现同样的问题。他们也可以首先使用git branch -t newbranch。
- @johnmellor,你说得对,不带参数调用git rebase会使--fork-point选项失效,这会导致提交丢失。我认为这是git rebase的缺陷。不过,这与我上面的回答无关。在许多情况下,没有论据的git rebase都会造成麻烦。这个故事的寓意(不幸的是)不是不经过一个分支就称之为git rebase,即使这个分支已经是上游。Git的可用性在过去的八年左右已经有了很大的提高,但这是一个可以使用更多工作的领域。
一般来说。。。
在这种情况下,Sykora公开的方法是最好的选择。但有时不是最简单的方法,也不是一般的方法。对于一般方法,使用git cherry pick:
要实现OP想要的,它有两个步骤:
第1步-注意您希望从主服务器提交的
newbranch执行
1 2
| git checkout master
git log |
注意你在newbranch上想要的(比如3)承诺的散列。在这里我将使用:C承诺:9aa1233。D承诺:453ac3d。承诺:612ecb3。
Note: You can use the first seven characters or
the whole commit hash
第二步-把它们放在newbranch上
1 2 3 4
| git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233 |
或(在Git 1.7.2+上,使用范围)
1 2
| git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233 |
Git Cherry Pick将这三项承诺应用于Newbranch。
- 是不是这家公司想从老板搬到新牧场?如果在master上执行cherry-pick,那么您将向master分支添加内容——实际上,添加了它已经拥有的提交内容。它既不移回树枝头,也不移回B。或者有什么微妙和酷的东西我没有得到?
- 嗯,这是一个例子,但你是对的,在OP提出的情况下,它应该在新牧场工作。我编辑以避免错误。谢谢。
- 如果选择樱桃是最后n个承诺,那么您可以将您的主头部向后移动w/git reset --hard HEAD~N。
- 如果您不小心提交了错误的、非主分支(当您应该创建一个新的功能分支时),这将非常有效。
- 关于git cherry pick的信息很好,但是本文中的命令不起作用。1)"git checkout newbranch"应该是"git checkout-b newbranch",因为newbranch还不存在;2)如果您从现有的主分支机构签出newbranch,它已经包含了这三个承诺,因此在选择它们时没有用。在一天结束的时候,为了得到操作人员想要的,你仍然需要做某种形式的重置——硬头。
- +1对于某些情况下的有用方法。如果您只想将自己的提交(与其他人分散在一起)拉到一个新的分支中,这是很好的。
- 这是更好的答案。这样就可以将提交移动到任何分支。
- 摘樱桃的顺序重要吗?
- 不适合我。错误消息,"没有添加要提交的内容"。以前的樱桃采摘现在是空的。允许清空或继续。无论如何,它似乎不起作用,承诺没有被转移。
- 我无法使用cherry-pick范围,得到了"错误:无法应用c682e4b…"对其中一个提交,但当每次对每个提交使用cherry-pick时,它工作得很好(我必须执行git cherry-pick--错误后中止)。评论,因为我认为这些是等效的。这是Git版本2.3.8(Apple Git-58)
- 对于refs,您通常可以使用3-5个十六进制;但是要唯一地标识提交,需要使用git。另外,您可以使用master~3..master来引用master上的最后3个提交,而不是哈希。
- @我相信秩序很重要。您要先执行最旧的提交。
- 谢谢,这不是cherry-pick命令的最佳解释和用例!
- 第二个cherry pick示例不正确。612ecb3..9aa1233是指除612ecb3以外,到9aa1233为止的所有承诺。要获得所有三项承诺,您需要cherry-pick 612ecb3~1..9aa1233。
- [email protected]我已经编辑了这个例子。谢谢
- Cherry Picking是否创建了新的承诺?当你可以简单地从master创建一个新的分支并将master的头重新设置回来时,为什么要这样做呢?这样的操作会更快更便宜。
- 如果您想回复现有分支的提交本体,而不是创建一个新的、错误放置提交的分支,那么这个方法非常有效。在并行处理许多特性时,有时会遇到这种情况。
另一种方法是,只使用两个命令。同时保持当前工作树的完整性。
1 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之前
1 2
| 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可以推送到远程或Git URL。支持git-urls语法。参见git help clone中的git-urls部分。
- 我不知道这为什么不被高分。死的很简单,而且没有Git重置的小但潜在的危险——很难。
- @戈德史密斯我猜人们更喜欢三个简单的命令而不是两个稍微模糊的命令。而且,根据首先显示的性质,最热门的答案得到更多的赞成票。
- 我同意@godsmith!为什么这个评分不高?关于强制master指向以前的提交的部分绝对是我在这里看到的最佳选择。谢谢你@aragaer
- 伟大的回答!您也可以使用@~3代替HEAD~3。
- 为我工作过……但你们中有人能解释一下,你更喜欢这个方法而不是reset --hard方法吗?谢谢!
- 它根本不会改变工作树——无论您对代码做了什么修改,都不会受到影响——您甚至不需要提交任何东西。
- 我同意,非常简单。又是@d?埃南在前面的回答中说,不要忘记做git push origin master --force做,也远程保存更改。
- 这是指向git branch命令git-scm.com/docs/git-branch的链接。为了澄清,通过命令git branch -f master HEAD~3,它不必是master。
以前的大多数答案都是危险的错误!
不要这样做:
1 2 3
| git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch |
下次运行git rebase或git pull --rebase时,这些3个提交将从newbranch中被悄悄丢弃!(见下文解释)
而是这样做:
1 2 3
| git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2} |
- 首先,它放弃了最近的3个承诺(--keep与--hard相似,但更安全,因为失败而不是放弃未承诺的更改)。
- 然后它从newbranch分叉。
- 然后,cherry选择这3个提交回newbranch。由于分支不再引用它们,因此使用git的reflog可以做到这一点:HEAD@{2}是HEAD以前用来引用2个操作的承诺,即在we 1之前。已签出newbranch和2。使用git reset放弃3次提交。
警告:reflog在默认情况下是启用的,但是如果您手动禁用了它(例如,通过使用"裸"Git存储库),则在运行git reset --keep HEAD~3之后将无法获得3次提交。
另一种不依赖于反射的方法是:
1 2 3 4 5 6 7
| # 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,然后自己进行了三次提交:
1 2 3
| M1--M2--M3 <-- origin/master
\
T1--T2--T3 <-- topic |
但随后有人强迫原点/主数据删除m2来重写历史:
1 2 3
| M1--M3' <-- origin/master
\
M2--M3--T1--T2--T3 <-- topic |
使用您的本地reflog,git rebase可以看到您来自一个早期的起源/主分支的化身,因此m2和m3提交实际上不是主题分支的一部分。因此,它合理地假设,由于m2已从上游分支中删除,因此在主题分支重新调整后,您不再希望它出现在主题分支中:
1 2 3
| M1--M3' <-- origin/master
\
T1'--T2'--T3' <-- topic (rebased) |
这种行为是有意义的,而且通常在重新平衡时是正确的。
所以以下命令失败的原因是:
1 2 3
| git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch |
是因为他们让反射处于错误的状态。Git将newbranch视为在包含3个提交的修订中分叉上游分支,然后reset --hard重写上游的历史以删除提交,因此下次运行git rebase时,它会像从上游删除的任何其他提交一样丢弃它们。
但在这个特定的案例中,我们希望这3个承诺被视为主题分支的一部分。为了实现这一点,我们需要在早期的版本(不包括3个提交)中划分上游。这就是我建议的解决方案所做的,因此它们都将reflog保持在正确的状态。
有关详细信息,请参见git-rebase和git-merge基本文档中的--fork-point的定义。
- 我想如果你能证明在这种情况下什么是"错误的"反应状态,而不是正确的状态,我会对这个答案更满意。
- 在您的场景中,如果我将m2和m3保留在主题分支中,然后尝试将主题分支合并回master,那么我是否重新引入其他人试图删除的编辑(即m2)?这似乎是不受欢迎的行为,不是吗?对我来说,如果有人要重写历史,你会希望你的主题分支能够跟踪新的历史,这样在合并时就不会重写历史了。
- 这个答案说"不要这样做!"在没有人建议做的事情上面。
- 如果我已经使用stackoverflow.com/a/1628584/2423194方法,并执行git push --force删除我已经移动到新分支的提交,有没有办法修复它?
- 大多数人不会重写出版的历史,尤其是在master上。所以不,他们没有危险的错误。
- @Makoto,在执行"不要这样做"步骤后,git reflog newbranch将有一个单条目db8f2fe newbranch@{0}: branch: Created from oldbranch,其中db8f2fe是t3的散列,更重要的是,git reflog oldbranch将列出T1、T2和T3的精确散列。综上所述,很明显,当新牧场分岔时,承诺T1、T2、T3已经是旧牧场的一部分,因此下一个git rebase将意外地从新牧场移除。因此,git merge-base --fork-point newbranch oldbranch也无法找到分叉点(而不是打印M3的散列)。
- @Makoto,如果你执行我的任何一个替代步骤,git reflog newbranch将从4b242c2 newbranch@{3}: branch: Created from HEAD开始,其中4b242c2是M3的散列,后面是3个樱桃精选条目。git reflog oldbranch将列出T1、T2和T3,但它们在那里的散列值与在Newbranch中的散列值不同,因为它们是通过cherry picking复制到Newbranch的(特别是cherry picking更新了时间戳-假设它自commit创建以来已经超过一秒钟了-导致它得到一个新的散列值,即使它是一个快速的forwarD)。
- @Gordonbean,是的,当从origin/master中重写m2时,git rebase从主题分支中删除m2是很好的。这仅仅意味着在这种特殊情况下必须小心(将现有提交从本地分支拆分到依赖的本地分支),因为Git无法区分这两种情况。
- @Kyralessa,你在git branch中所指的-t是隐式发生的,如果你有git config --global branch.autosetuprebase always集。即使您不这样做,我已经向您解释过,如果您在执行这些命令后设置跟踪,就会出现同样的问题,正如OP在给出他们的问题时可能会做的那样。
- @Rocklee,是的,解决这种情况的一般方法是从一个安全的起点创建一个新的分支(Newbranch2),然后Cherry选择您想要保留的所有承诺(从Badnewbranch到Newbranch2)。cherry-picking将提供提交的新哈希,因此您可以安全地重新设置newbranch2(现在可以删除badnebranch)。
- @沃尔夫,你误解了:Git Rebase的设计是为了抵御上游的历史重写。不幸的是,这种稳健性的副作用影响到每个人,即使他们和他们的上游从来没有重写历史。
- 好吧,我更喜欢cherry-pick方法,因为它不可能丢失承诺(总是在git log中可见)。然而,如果历史从未被重写,那么这种丢失的提交情况是如何发生的?
- 注意:要检查哪些承诺包括,您可以使用提供head@xxx_参考的git reflog。
- 考虑到很多人总是设置自动设置库,我们非常欢迎您的警告。谢谢约翰·梅勒。@Sykora和Kyralessa,我认为值得在公认的答案上记下什么时候是危险的(如果在.gitconfig中设置了autosetuprebase,很多人都会这么做!).上面至少有一条来自李岩的评论,他遵循了公认的答案,似乎被烧掉了。
- @在这种情况下,历史是由git reset --keep重写的。在这个例子中,您通过放弃提交来重写历史,这就是重新平衡时它们"丢失"的方式。
- 运行git rebase时,也可以使用--no-fork-point。
- 这里要避免的危险是,对newbranch的承诺将在一个重新平衡中丢失。如果在重新平衡之前,newbranch与oldbranch合并,这仍然是一个危险吗?
- 我尝试过将合并放在另一个分支上,但是CherryPick不选择合并中的其他提交(仅顶部提交),至少我这么认为。然而,似乎在重置之后,我可以只"git checkout"(使用从reflog获得的commit),然后从那里创建一个新的分支。你觉得呢?
- 如果使用cherry-pick方法,则需要重置Newbranch的远程合并。我不知道git命令可以做到这一点,我发现编辑.git/config更容易。
- 可惜这不是公认的答案。正如你所描述的,我遵循了公认答案中的步骤,失去了6个承诺!
- 不使用相对修订格式,只取当前头部修订,如aaaaaa、主bbbbbb的新头部、bbbbbb后的修订,如cccccc,再做git reset --keep bbbbbb、git checkout -t -b newbranch、git cherry-pick cccccc..aaaaaa。
- 您的"不依赖于reflog的备选方案"与git push不互操作:无论我尝试将哪个分支推向远程回购,它都会出错:无论是旧的还是新的。拇指向下
使用git stash的更简单的解决方案
如果:
- 您的主要目的是回滚master,以及
- 您希望保留文件更改,但不特别关心错误提交消息,以及
- 你还没推,还有
- 您希望这很容易,并且不复杂,包括临时分支、提交哈希和其他令人头疼的问题。
那么,下面就简单多了(从有三个错误承诺的master分支开始):
1 2 3 4
| git reset HEAD~3
git stash
git checkout newbranch
git stash pop |
这是做什么的,按行号
撤消对master的最后三次提交(及其消息),但保留所有工作文件的完整性。
隐藏所有工作文件更改,使master工作树完全等于head~3状态
切换到现有分支newbranch。
将隐藏的更改应用于您的工作目录并清除隐藏
现在您可以像平时那样使用git add和git commit。所有新的承诺都将添加到newbranch中。
这不能做什么
- 它不会在你的树上留下杂乱的临时树枝
- 它不会保留错误的提交和提交消息,因此您需要向这个新提交添加一个新的提交消息。
目标
OP表示,目标是"在提交之前将master恢复到原来的状态",而不会丢失更改,而这个解决方案会做到这一点。
我每周至少做一次这样的事情,因为我不小心把新的承诺交给了master,而不是develop。通常我只有一个提交回滚,在这种情况下,在第1行上使用git reset HEAD^是只回滚一个提交的更简单的方法。
如果你把大师的改变推到上游,不要这样做。
其他人可能已经改变了。如果你只是在重写你的本地大师,那么当它被推到上游时没有任何影响,但是将一个重写的历史推给合作者会导致头痛。
- 谢谢,我很高兴能读完这么多文章来这里,因为这对我来说也是一个很常见的用例。我们是不是太不典型了?
- 我认为我们完全是典型的,对于需要恢复少数或更少的承诺的需求,最常见的用例是"我错误地提交给主控"。幸运的是,这个解决方案如此简单,我现在已经记住了。
- 这应该是公认的答案。它很简单,容易理解,容易记忆
从技术角度来说,这不会"移动"它们,但它具有相同的效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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。
要在不重写历史记录的情况下执行此操作(例如,如果您已经推送了提交):
1 2 3 4
| git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)> |
然后可以不用力地推两个分支!
- 但是,接下来你必须处理恢复场景,这取决于你的情况,可能会更加棘手。如果您在分支上恢复一个提交,Git仍然会看到那些已经发生的提交,所以为了撤销它,您必须恢复该恢复。这会灼伤很多人,尤其是当他们恢复合并并试图将分支合并回来时,结果发现Git相信它已经合并了该分支(这是完全正确的)。
- 这就是为什么我在最后选择了一个新的分支。这样,Git将它们视为新的提交,从而解决了您的问题。
- 这比它最初看起来更危险,因为您在改变存储库历史的状态,而没有真正理解这个状态的含义。
- 我不理解你的论点——这个答案的要点是你不会改变历史,只需要添加新的提交(这会有效地撤销修改)。这些新提交可以正常推送和合并。
就在这种情况下:
1 2 3
| Branch one: A B C D E F J L M
\ (Merge)
Branch two: G I K N |
我表演:
1 2 3
| git branch newbranch
git reset --hard HEAD~8
git checkout newbranch |
我原以为我会是负责人,但现在我是…
为了确保降落在历史上的正确位置,更容易处理提交的混乱问题。
1 2 3
| git branch newbranch
git reset --hard #########
git checkout newbranch |
1)创建一个新分支,将所有更改移动到新分支。
1
| git checkout -b new_branch |
2)然后回到旧的分支。
3)做吉特钢筋
1
| git rebase -i <short-hash-of-B-commit> |
4)然后打开的编辑器包含最后3个提交信息。
1 2 3 4 5
| ...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
... |
5)将所有3项承诺中的pick改为drop。然后保存并关闭编辑器。
1 2 3 4 5
| ...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
... |
6)现在从当前分支中删除最后3个提交(master)。现在用力推树枝,在树枝名称前加上+号。
你可以这样做,这只是我使用的3个简单步骤。
1)创建新的分支,在其中提交最近的更新。
git branch
2)查找新分支上提交的最近提交ID。
git log
3)复制该提交ID注意,最新的提交列表在顶部。所以你可以找到你的承诺。你也可以通过信息找到这个。
git cherry-pick d34bcef232f6c...
您还可以提供一些范围的提交ID。
git cherry-pick d34bcef...86d2aec
现在你的工作完成了。如果您选择了正确的ID和正确的分支,那么您将成功。所以在这样做之前要小心。否则会出现另一个问题。
现在你可以推你的代码了
git push