撤消 Git Rebase
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32490288/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Undo Git Rebase
提问by user1960364
I performed a git rebase master
on my branch and didn't realize it wasn't what I wanted until after I pushed it to the remote. It's only myself and 1 other person working on the project, so I know they've not pulled the latest changes.
我git rebase master
在我的分支上执行了一个,直到我将它推送到远程之后才意识到这不是我想要的。只有我自己和另外 1 个人在从事该项目,所以我知道他们没有提取最新的更改。
Reading other questions on StackOverflow, it said to use git reflog
and then git reset --hard HEAD@{n}
to before the rebase. I did this to go to a commit I created before the rebase, but it didn't restore things to how it was before.
在 StackOverflow 上阅读其他问题,它说在 rebase 之前使用git reflog
然后git reset --hard HEAD@{n}
到。我这样做是为了转到我在 rebase 之前创建的提交,但它没有将事情恢复到以前的状态。
Am I missing a step? Is there a way to have the other person force-push his repo back up to restore things to how they were?
我错过了一步吗?有没有办法让另一个人强制推动他的回购以将事情恢复到原来的状态?
Thanks
谢谢
回答by torek
As Makoto already noted, you probably shouldn't bother undoing this rebase: it's probably what you wanted. Nonetheless, feel free to read on for how to undo it.
正如Makoto 已经指出的那样,您可能不应该费心撤消此变基:这可能是您想要的。尽管如此,请继续阅读以了解如何撤消它。
Use the reflog for the branch, as it will be easier to read. (The HEAD
reflog has the same information, but has a whole lot more stuff in it, hence it's harder to find what you're looking for.)
将 reflog用于 branch,因为它更易于阅读。(HEAD
reflog 具有相同的信息,但其中包含更多内容,因此更难找到您要查找的内容。)
For instance if I had just rebased mybranch
, I would see:
例如,如果我刚刚重新定位mybranch
,我会看到:
$ git reflog mybranch
nnnnnnn mybranch@{0}: rebase finished: refs/heads/mybranch onto biguglysha1
ooooooo mybranch@{1}: commit: some sort of commit message
...
The name mybranch@{1}
is therefore synonymous (at the moment) with ooooooo
, the old abbreviated SHA-1. Every time you do something to the branch (such as git reset
) the number inside the @{...}
part will change, while the SHA-1s are forever permanent, so it's a bit safer to use the SHA-1 (full or abbreviated) for cut-and-paste.
mybranch@{1}
因此,该名称(目前)与ooooooo
旧缩写 SHA-1同义。每次对分支执行某些操作(例如git reset
)时,@{...}
部件内的数字都会改变,而 SHA-1 是永久永久的,因此使用 SHA-1(完整或缩写)进行剪切和-更安全一些粘贴。
If you then:
如果你那时:
$ git checkout mybranch # if needed
and:
和:
$ git reset --hard ooooooo # or mybranch@{1}
you should have the original back. This is because rebase
simply copiescommits and then moves the label. After the rebase, but before the reset, the commit graph looks something like this, where A
through C
are "your" commits:
你应该有原来的回来。这是因为rebase
简单地复制提交然后移动标签。底垫后,但复位前,提交图形看起来像这样,在A
通过C
是“你”的提交:
A - B - C <-- (only in reflog now)
/
... - o - o - o - A' - B' - C' <-- mybranch (after rebase)
and git reset
simply1erases the current branch label and pastes it on to the supplied SHA-1 (turning a reflog name into an SHA-1 first if needed). Hence, after the reset
:
并git reset
简单地1擦除当前分支标签并将其粘贴到提供的 SHA-1 上(如果需要,首先将引用日志名称转换为 SHA-1)。因此,在reset
:
A - B - C <-- mybranch, plus older reflog
/
... - o - o - o - A' - B' - C' <-- (only in reflog now)
Note that now, post-reset
, the rebase-made commit copies are the "abandoned" ones that are only found in reflog entries. The originals, which had been abandoned, are now claimed under mybranch
again.
请注意,现在, post- reset
,rebase 制作的提交副本是仅在 reflog 条目中找到的“废弃”副本。被遗弃的原件现在mybranch
再次被认领。
The way to think about this stuff is to draw the commit graph (with new commits pointing back at their parent commits), then draw in branch labels with long arrows pointing to the commit graph. The graph never2changes except to addnewcommits, which have new and different big ugly SHA-1s (which is why I use letters like A
B
and C
instead, and tack on additives like A'
for copies). The SHA-1s are guaranteed to be unique3and are permanent, but the labels with their long arrows, get erased and re-pointed all the time. (If you do this on a whiteboard, you should generally use black for the commit graph and a color, or several colors, for the labels.)
考虑这些东西的方法是绘制提交图(新提交指向它们的父提交),然后用指向提交图的长箭头绘制分支标签。除了添加新的提交之外,该图永远不会有2 次更改,这些提交具有新的和不同的大丑陋 SHA-1(这就是为什么我使用像和这样的字母,并添加像副本这样的添加剂)。SHA-1 保证是唯一的3并且是永久的,但是带有长箭头的标签会一直被擦除和重新指向。(如果你在白板上这样做,你通常应该使用黑色作为提交图,使用一种颜色或多种颜色作为标签。)A
B
C
A'
1Well, git reset
does more than just move the label, unless you add some command-line flags. By default, it moves the label andresets the index; with --hard
, it moves the label andresets the index andcleans out your work-tree. With --soft
it justmoves the label, leaving the index and work-tree alone. With git being what it is, there are a bunch more flags that twist up the meaning even further, but those are the big three: --soft
, nothing aka --mixed
, and --hard
.
1好吧,git reset
除非您添加一些命令行标志,否则不仅仅是移动标签。默认情况下,它移动标签并重置索引;使用--hard
,它会移动标签并重置索引并清除您的工作树。有了--soft
它只是移动的标签,留下独自索引和工作树。与 git 一样,还有更多的标志可以进一步扭曲其含义,但这些是三大标志:--soft
,也就是--mixed
, 和--hard
。
2If git only ever added things, your repo would grow huge over time. So, eventually, "unreachable" commits—those with no labels, and not even any leftover reflog entries, pointing to them, and that are not pointed-to by some commit that doeshave a label, or some other pointed-to commit—eventually these unreachable commits (and any other unreachable objects) are removed when git runs git gc
for you automatically. You can force them to be removed earlier, but there's rarely a good reason to bother.
2如果 git 只添加东西,你的 repo 会随着时间的推移变得巨大。所以,最终,“无法访问”的提交,那些没有标签,甚至没有任何剩余的引用日志条目,指着他们,而不是指向的一些承诺是不会有一个标签,或其他一些指向的commit-最终,当 gitgit gc
自动为您运行时,这些无法访问的提交(以及任何其他无法访问的对象)将被删除。您可以强制提前删除它们,但很少有充分的理由去打扰。
3Git itself depends on the guarantee. It's mathematically possible, but extremely improbable, for any two different objects to wind up with the same SHA-1. If this happens, git breaks.4If the distribution is good enough the probability is 1 out of 2160, which is really tiny. This is a good thing as the "Birthday paradox"raises the possibility pretty rapidly, but because it started so tiny, it stays tiny and in practice it's never been a problem.
3Git 本身依赖于保证。任何两个不同的对象都以相同的 SHA-1 结束,这在数学上是可能的,但极不可能。如果发生这种情况,git 会中断。4如果分布足够好,那么概率是 2 160 分之 1,这真的很小。这是一件好事,因为“生日悖论”迅速提高了这种可能性,但因为它开始时很小,所以它一直很小,而且在实践中从来都不是问题。
4By design, the "breakage" is that git simply stops adding objects, so that everything-so-far is still good. You then have to move to whatever new system has been devised to handle billions-of-objects repositories, the "next generation" git or whatever.
4按照设计,“破坏”是 git 只是停止添加对象,所以到目前为止一切都还好。然后,您必须迁移到已设计用于处理数十亿对象存储库的任何新系统、“下一代”git 或其他任何系统。
回答by Makoto
I have good news and bad news for you.
我有好消息和坏消息要告诉你。
The good news is, you're in the state that you express that you want! The last commit on master
is indeed now in your branch, and all is well with the universe.
好消息是,您处于您想要表达的状态!最后一次提交master
确实现在在您的分支中,并且宇宙一切都很好。
The bad news is that now, with your rebase operation, you have effectively moved your branch to the tip of master; that is, your development branch is now in the same state as if you had simply branched from the tip of master.
坏消息是,现在,通过 rebase 操作,您已经有效地将分支移动到了 master 的尖端;也就是说,您的开发分支现在处于与您只是从 master 的尖端分支一样的状态。
But this is [considered to be] a good thing: provided that you haven't had any merge conflicts, you haven't lost any data in this process, and you also don't have a worthless merge commit on your branch.
但这 [被认为是] 一件好事:假设您没有发生任何合并冲突,在此过程中您没有丢失任何数据,并且您的分支上也没有毫无价值的合并提交。
The book does a great job explaining what a rebase is, so I won't repeat it. From the operations you describe, you shouldn't have lost any work.
这本书很好地解释了什么是变基,所以我不会重复。从您描述的操作来看,您不应该丢失任何工作。
If you do want to recreate your original history before you force-pushed, then someone with a non-pulled history can force-push their branch up to the remote repository. This will cause the state of your repo to be whatever was on their box at the time they pushed.
如果您确实想在强制推送之前重新创建原始历史记录,那么具有非拉取历史记录的人可以将他们的分支强制推送到远程存储库。这将导致您的回购的状态成为他们推送时他们的盒子上的任何内容。
回答by Keefe Roedersheimer
If you do take the commit hash from the reflog (also the tail of the entry in a normal git log) you can do get reset --hard COMMIT and then git push -f origin master which is usually a bad thing. I would recommend checking out the full history to a separate, clean directory before doing a force push.
如果您确实从 reflog(也是普通 git 日志中条目的尾部)中获取提交哈希,您可以执行 reset --hard COMMIT 然后 git push -f origin master 这通常是一件坏事。我建议在强制推送之前将完整的历史记录检查到一个单独的、干净的目录中。