问题
Does anybody know how to easily undo a git rebase?
The only way that comes to mind is to go at it manually:
- git checkout the commit parent to both of the branches
- then create a temp branch from there
- cherry-pick all commits by hand
- replace the branch in which I rebased by the manually-created branch
In my current situation this is gonna work because I can easily spot commits from both branches (one was my stuff, the other was my colleague\'s stuff).
However my approach strikes me as suboptimal and error-prone (let\'s say I had just rebased with 2 of my own branches).
Any ideas?
Clarification: I\'m talking about a rebase during which a bunch of commits were replayed. Not only one.
回答1:
The easiest way would be to find the head commit of the branch as it was immediately before the rebase started in the reflog...
git reflog
and to reset the current branch to it (with the usual caveats about being absolutely sure before reseting with the --hard
option).
Suppose the old commit was HEAD@{5}
in the ref log:
git reset --hard HEAD@{5}
In Windows, you may need to quote the reference:
git reset --hard "HEAD@{5}"
You can check the history of the candidate old head by just doing a git log HEAD@{5}
(Windows: git log "HEAD@{5}"
).
If you've not disabled per branch reflogs you should be able to simply do git reflog branchname@{1}
as a rebase detaches the branch head before reattaching to the final head. I would double check this, though as I haven't verified this recently.
Per default, all reflogs are activated for non-bare repositories:
[core]
logAllRefUpdates = true
回答2:
Actually, rebase saves your starting point to ORIG_HEAD
so this is usually as simple as:
git reset --hard ORIG_HEAD
However, the reset
, rebase
and merge
all save your original HEAD
pointer into ORIG_HEAD
so, if you've done any of those commands since the rebase you're trying to undo then you'll have to use the reflog.
回答3:
Charles's answer works, but you may want to do this:
git rebase --abort
to clean up after the reset
.
Otherwise, you may get the message “Interactive rebase already started
”.
回答4:
Resetting the branch to the dangling commit object of its old tip is of course the best solution, because it restores the previous state without expending any effort. But if you happen to have lost those commits (f.ex. because you garbage-collected your repository in the meantime, or this is a fresh clone), you can always rebase the branch again. The key to this is the --onto
switch.
Let’s say you had a topic branch imaginatively called topic
, that you branched off master
when the tip of master
was the 0deadbeef
commit. At some point while on the topic
branch, you did git rebase master
. Now you want to undo this. Here’s how:
git rebase --onto 0deadbeef master topic
This will take all commits on topic
that aren’t on master
and replay them on top of 0deadbeef
.
With --onto
, you can rearrange your history into pretty much any shape whatsoever.
Have fun. :-)
回答5:
I actually put a backup tag on the branch before I do any nontrivial operation (most rebases are trivial, but I'd do that if it looks anywhere complex).
Then, restoring is as easy as git reset --hard BACKUP
.
回答6:
In case you had pushed your branch to remote repository (usually it's origin) and then you've done a succesfull rebase (without merge) (git rebase --abort
gives "No rebase in progress") you can easily reset branch using
command:
git reset --hard origin/{branchName}
Example:
$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is ahead of 'origin/{branchName}' by 135 commits.
(use "git push" to publish your local commits)
nothing to commit, working directory clean
$ ~/work/projects/{ProjectName} $ git reset --hard origin/{branchName}
HEAD is now at 6df5719 "Commit message".
$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is up-to-date with 'origin/{branchName}.
nothing to commit, working directory clean
回答7:
Using reflog
didn't work for me.
What worked for me was similar to as described here. Open the file in .git/logs/refs named after the branch that was rebased and find the line that contains "rebase finsihed", something like:
5fce6b51 88552c8f Kris Leech <me@example.com> 1329744625 +0000 rebase finished: refs/heads/integrate onto 9e460878
Checkout the second commit listed on the line.
git checkout 88552c8f
Once confirmed this contained my lost changes I branched and let out a sigh of relief.
git log
git checkout -b lost_changes
回答8:
For multiple commits, remember that any commit references all the history leading up to that commit. So in Charles' answer, read "the old commit" as "the newest of the old commits". If you reset to that commit, then all the history leading up to that commit will reappear. This should do what you want.
回答9:
Following the solution of @Allan and @Zearin, I wish I could simply do a comment though but I don't enough reputation, so I have used the following command:
Instead of doing git rebase -i --abort
(note the -i) I had to simply do git rebase --abort
(without the -i).
Using both -i
and --abort
at the same time causes Git to show me a list of usage/options.
So my previous and current branch status with this solution is:
matbhz@myPc /my/project/environment (branch-123|REBASE-i)
$ git rebase --abort
matbhz@myPc /my/project/environment (branch-123)
$
回答10:
If you successfully rebased against remote branch and can not git rebase --abort
you still can do some tricks to save your work and don't have forced pushes.
Suppose your current branch that was rebased by mistake is called your-branch
and is tracking origin/your-branch
git branch -m your-branch-rebased
# rename current branchgit checkout origin/your-branch
# checkout to latest state that is known to origingit checkout -b your-branch
- check
git log your-branch-rebased
, compare togit log your-branch
and define commits that are missing fromyour-branch
git cherry-pick COMMIT_HASH
for every commit inyour-branch-rebased
- push your changes. Please aware that two local branches are associated with
remote/your-branch
and you should push onlyyour-branch
回答11:
Let's say I rebase master to my feature branch and I get 30 new commits which break something. I've found that often it's easiest to just remove the bad commits.
git rebase -i HEAD~31
Interactive rebase for the last 31 commits (it doesn't hurt if you pick way too many).
Simply take the commits that you want to get rid of and mark them with "d" instead of "pick". Now the commits are deleted effectively undoing the rebase (if you remove only the commits you just got when rebasing).
回答12:
For newbies/anyone too scared of doing a hard reset, you could checkout the commit from the reflog, and then save it as a new branch.
git reflog
Find the commit just before you started rebasing. You may need to scroll further down to find it (press Enter or PageDown). Take note of the HEAD number and replace 57:
git checkout HEAD@{57}
Review the branch/commits, if it looks good create a new branch using this HEAD:
git checkout -b new_branch_name
回答13:
If you are on a branch you can use:
git reset --hard @{1}
There is not only a reference log for HEAD (obtained by git reflog
), there are also reflogs for each branch (obtained by git reflog <branch>
). So, if you are on master
then git reflog master
will list all changes to that branch. You can refer to that changes by master@{1}
, master@{2}
, etc.
git rebase
will usually change HEAD multiple times but the current branch will be updated only once.
@{1}
is simply a shortcut for the current branch, so it's equal to master@{1}
if you are on master
.
git reset --hard ORIG_HEAD
will not work if you used git reset
during an interactive rebase
.
回答14:
What I usually do is
git reset #commit_hash
to the last commit where I think rebase had no effect.
then git pull
Now your branch should match exactly like master and rebased commits should not be in it.
Now one can just cherry-pick the commits on this branch.
回答15:
If you mess something up within a git rebase, e.g. git rebase --abort
, while you have uncommitted files, they will be lost and git reflog
will not help. This happened to me and you will need to think outside the box here. If you are lucky like me and use IntelliJ Webstorm then you can right-click->local history
and can revert to a previous state of your file/folders no matter what mistakes you have done with versioning software. It is always good to have another failsafe running.
回答16:
git reset --hard origin/{branchName}
is the correct solution to reset all your local changes done by rebase.
来源:https://stackoverflow.com/questions/134882/undoing-a-git-rebase