43

I made a mistake, and started making commits "to the last tag", which lands my commits in "no branch". They should have been applied at the head of an already existing branch. I have not pushed my changes yet. I've found a few other question for when commits are in the wrong branch, but now I have no branch, so I don't know how to handle this.

Preferably, I would like to erase my mistake entirely, and "move" my changes to the end of the right branch. If I must leave my mistake in the history, I need to merge them in atleast.

Full Stack Alien
  • 11,244
  • 1
  • 24
  • 37
Letharion
  • 4,067
  • 7
  • 31
  • 42

7 Answers7

79

You are currently in a detached HEAD state. To resolve that, all you need to do is create a new branch with git branch <branchname> or git checkout -b <branchname>. That will leave you with a local branch you can play with, and even delete when you're done with it.

Branches in git are just pointers to commits, so if you create a new branch where you are the new branch will point to your current commit, and then you can merge it or whatnot.

Your "mistake" need not be erased, you simply created new commits on top of previous ones. You did not modify history or anything like that.

EDIT: In response to your comment, what you need to do is:

git branch temp
git checkout master # or any other branch
git merge temp
git branch -d temp
drrlvn
  • 8,189
  • 2
  • 43
  • 57
  • 1
    If I understand you correctly, that will create a _new_ branch, but I want to apply all the commits to an already existing branch. I've updated my question to make that more clear. – Letharion Jan 21 '12 at 13:26
  • 2
    @Letharion, then all you need to do after creating a new branch, is to merge it to the branch where you want these changes, and then you can delete the new temp branch. – drrlvn Jan 21 '12 at 13:27
  • In this case you'll want to rebase on main branch instead of merge – CharlesB Jan 21 '12 at 13:45
  • @CharlesB, The default behavior of `git merge` is to fast-forward if possible, which is the same as `rebase`. – drrlvn Jan 21 '12 at 13:50
  • @spatz: Sure but I assume that the merge won't be fast-forward in this case, because commits were made starting from an ancient tag. – CharlesB Jan 21 '12 at 14:21
  • 1
    @Letharion: Then after creating the branch, run `git rebase master temp` to transplant the commits onto master, before checking out master and merging into it. (The merge will be a fast-forward then.) – Cascabel Jan 21 '12 at 15:12
  • 1
    I followed the instructions spatz gave, and it worked well. I'm not sure how the rebase suggestion plays in, but I'll refer to that if I happen to do the same again. – Letharion Jan 21 '12 at 15:36
  • Thank you, this is very useful. – wangzhengyi Apr 12 '16 at 11:57
  • @spatz: The behavior of `git branch temp` that I know of is to create a branch without switching to it. Does it have a special meaning of "also assign all no-branch-owned commits to this new branch"? – Stefan Monov Mar 27 '17 at 15:14
  • This doesn't work. `git branch master` yields "fatal: not a valid object name: 'master'" – pabrams Aug 16 '18 at 12:45
  • I was helping someone who somehow had no branches. All commits had no branch name. I did git branch master && git checkout master and voila all the commits claimed to be on branch master. The previously failing git push to a bare upstream repository started working. – jla Aug 08 '22 at 16:25
21

You can view all your commits using git reflog

So you can simply got to another branch, and do git cherry-pick <commit-hash> for the required commits.

But I'd prefer the branch way as spatz mentioned.

Sailesh
  • 25,517
  • 4
  • 34
  • 47
  • Just went through this scenario today with a co-worker. He had checked out another branch, but we could see the commit hash in the shell :) – Marcello DeSales Apr 11 '13 at 20:30
8

Note: you have also

enter image description here

In both case, doing a tmp branch and merging it back to the actual branch is the solution.

Cerran
  • 2,087
  • 2
  • 21
  • 33
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
7

Another solution, which does not involve creating a temp branch, is described here. You just merge with you last commit rather than a temp branch.

$ git checkout master
$ git merge d2bdb98

If you don't know what commit you are on you can find it with git log. I don't know if this solution is different than "cherry-picking" but it had the expected results for me.

Nathan Buesgens
  • 1,415
  • 1
  • 15
  • 29
  • It is different and merging is want you want in this case. If you made more than one commit in detached HEAD state, cherry-picking would only recover the changes of the latest. – jan-glx Sep 15 '16 at 13:15
  • `git reflog` lists commits of other (and no) branches too and is more useful in this scenario – jan-glx Sep 15 '16 at 14:34
2

I just had a situation where I had a master branch I wanted to commit to, but accidentally committed to a detached HEAD (note, the commit hash is not important here, but just to illustrate the situation):

$ git branch
* (detached from 29d2cfb)
  master

Here was my quick fix:

$ git branch -f master HEAD
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Piece of cake.

Note that this trick only works if master originally pointed to the same commit you made your mistaken commit on top of. If that's not the case, you'll have to rebase (or cherry-pick, or merge...).

mpontillo
  • 13,559
  • 7
  • 62
  • 90
1

To offer yet another solution, you can do this with a form of rebasing (which I believe will be the most accurate answer to your actual question). This solution will actually move the commits from one place to the other, no merging.

As always with a rebase, the command includes references not to commits but to parents of commits. For instance, a simple rebase of a feature branch on top of a master branch with “$ git rebase master” is a shorthand for “$ git rebase master feature”, meaning, “take the commit that master and feature have in common, consider that commit as the old parent commit, and replace that old parent commit by the new parent commit; namely the head of master.” The automatic consequence is the moving of commits from one branch to the other. This basic scenario shows you why rebasing is concerned with parents of commits, and not with ‘moving commits around’. (I hope this helps as an introduction to what follows.)

For your situation the solution is similar in its logic, but the command involves the —onto flag and uses commit hashes instead of branch names. (This is quite identical, as branches just point to commits.) If you identify the commit hash of the commit that you branched off of (say sha1), that will be the ‘old parent’. The new parent will be the commit hash that is the head of your branch that you mention; let’s call this sha2. Then the solution to your problem is

$ git rebase —onto sha2 sha1

Which replaces the old parent by the new parent, which essentially takes the commits to the branch that you want them.

Until here your question is basically answered, as this moves the commits, but with this solution you’ll still be in detached head state. For those final steps there are many different approaches. I’d suggest to use $ git checkout -b temp; $ git checkout yourbranch; $ git merge temp; $ git branch -D temp;

The final steps are not that interesting, but I urge everyone to have a solid understanding of the git rebase —onto command. It’s the essence of the problem, and an invaluable skill for the future :)

0

Not sure why this was not answered up to now. If you want to replace the actual branch with a detached HEAD (no-branch), you need to make that no-branch a temporary branch at first, as in the other answers. And then you need to overwrite everything you have in your actual branch by resetting it to the master (to get rid of all the work in the actual branch and start from new). Then merge the temporary branch into your actual branch.

git checkout choose_the_latest_commit_you_want_to_put_into_tmp
git branch tmp
git checkout the_branch_you_want_to_replace_with_tmp # (= actual branch in question)
git reset --hard master
git merge tmp
git branch -d tmp

If you make a mistake, you can always get the earlier work back by checking out the earlier id in git reflog.

questionto42
  • 7,175
  • 4
  • 57
  • 90