1

I accidentally rebased my branch with the DEV branch and then pushed it to a remote repository. With the rebase, I selected the current changes and hence my local changes got overwritten.

I lost my earlier commit in the rebase but found it by running git log. Then I ran git checkout commitId and even git reset --hard commitId. However, in both cases my code base still shows the latest rebased code on my branch.

How do I restore my branch back to its previous state?

Community
  • 1
  • 1
Saurabh Tiwari
  • 4,632
  • 9
  • 42
  • 82
  • Did you commit the local changes that got overridden? If yes, pick a keyword from the changes and try `git log -S --reflog` to find out which commit removed the changes. – ElpieKay Apr 22 '19 at 09:17
  • Reset moves branch labels about. If you detach HEAD from a branch first, reset won’t have a branch to move. – evolutionxbox Apr 22 '19 at 13:34

2 Answers2

2

Detached What? Dude, Where’s My Commit?

Checking out a commit’s hash or object name enters what the git documentation refers to as detached HEAD state.

It is sometimes useful to be able to checkout a commit that is not at the tip of any named branch, or even to create a new commit that is not referenced by a named branch. Let’s look at what happens when we checkout commit b (here we show [three] ways this may be done):

$ git checkout v2.0      # or
$ git checkout master^^  # or
$ git checkout b

   HEAD (refers to commit 'b')
    |
    v
a---b---c---d  branch 'master' (refers to commit 'd')
    ^
    |
  tag 'v2.0' (refers to commit 'b')

Notice that regardless of which checkout command we use, HEAD now refers directly to commit b. This is known as being in detached HEAD state. It means simply that HEAD refers to a specific commit, as opposed to referring to a named branch.

As you observed, git will happily create new commits, rebase, merge, and so on with a detached HEAD, but because no tag or branch refers to the resulting unnamed branch, losing it will be easy. You were able to find your original commit with git log. When you have done more drastic surgery, the output of git reflog is another place to look.

The Fix

Fixing your branch as described in your question will involve the following sequence.

  1. Reattach HEAD to your branch (referred to below as topic/my-branch)
  2. Reset topic/my-branch to where it was before
  3. Fix origin/topic/my-branch from the earlier push.
    • Either do it all at once with a force push, or
    • Delete the old remote branch and push your fixed history

Reattach HEAD

Rather than by its SHA-1 hash, check out your branch by name

git checkout topic/my-branch

Fix your local branch

Next, put it back where it was with git reset --hard. You will use the same command, but the context is different: HEAD after git checkout points to topic/my-branch rather than to commitId directly.

git reset --hard commitId

Fix the remote branch

You said you pushed your rebased branch, so update the remote repository to reflect your changes. The way to do it all in one command is

git push --force origin topic/my-branch

The administrator of the remote repository may have taken the highly reasonable step of denying force pushes (for why, see below). If so, try a sequence of

  1. Delete your branch on the remote side, and then
  2. Push your corrected local branch

In the bad old days, we had to delete remote branches with the non-obvious

git push origin :topic/my-branch

but nowadays, it’s spelled

git push --delete origin topic/my-branch

Having cleared the way, now push your branch to put things back to where they were before.

git push origin topic/my-branch

If both force pushes and deletes are disabled on your remote, ask for help from someone with administrative access to that repository.

Words of Caution

Most of the time, you have to work very hard to convince git to destroy work. However, git reset --hard, deleting remote branches, and git push --force are all sharp tools — useful when you need them but dangerous when used carelessly. As with rm -rf, pause to consider whether you really mean the command you’re about to run.

Greg Bacon
  • 134,834
  • 32
  • 188
  • 245
1

I suspect a minor confusion in the commands order here. If you did

git checkout <commitId>

it will have detached your HEAD then your subsequent

git reset --hard <commitId>

had no effect since no branch is currently checked out.

Checkout your branch before the reset and it'll be ok :

git checkout <yourBranchName>
git reset --hard <commitId>
Romain Valeri
  • 19,645
  • 3
  • 36
  • 61
  • It's not that `git reset --hard` has *no* effect after checking out a commit hash. It's just that it does not affect any *branch name* since there is no current branch. You still move to the selected commit, erasing any uncommitted work—so that `git reset --hard ` is a lot like another `git checkout ` except that the latter will leave uncommitted modifications to the index and work-tree alone, at the expense of failing the `git checkout` in some cases. – torek Apr 22 '19 at 15:07
  • @torek I was meaning that is has "no effect *on the branch*", but you're right, let's avoid misunderstandings. – Romain Valeri Apr 22 '19 at 17:02