99

Taking into consideration that there are several git commands that make no sense in a bare repository (because bare repositories don't use indexes and do not have a working directory),

git reset --hard HEAD^ 

is not a solution to uncommit the last change in such a repository.

Searching through the Internet, all I could find related to the topic is this, in which I am presented three ways of doing this:
1. "update the ref manually (which involves plumbing)";
2. "git push -f from a non-bare repository";
3. "git branch -f this $that".

Which solution do yo think is more appropriate or what other ways are there to do this? Unfortunately, the documentation I found about git bare repositories is fairly poor.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
  • 8
    @Lavinia-Garbriela Dobrovol Don't use the complicated stuff below. You're trying to move HEAD to a different commit and that's what git reset is intended for, even in a bare repo. Per my answer below, use: git reset --soft With --soft, you don't try changing a working tree and index that doesn't exist, so git lets you do the reset no problem. – Hazok May 12 '11 at 15:42

4 Answers4

141

You can use the git update-ref command. To remove the last commit, you would use:

$ git update-ref HEAD HEAD^

Or if you're not in the branch from which you cant to remove the last commit:

$ git update-ref refs/heads/branch-name branch-name^

You could also pass a sha1 if you want:

$ git update-ref refs/heads/branch-name a12d48e2

See the documentation of the git-update-ref command.

winteck
  • 417
  • 2
  • 4
  • 18
Sylvain Defresne
  • 42,429
  • 12
  • 75
  • 85
  • @Lavinia-Gabriela Dobrovolschi: right, I was'nt familiar with the exact syntax. – VonC Jan 07 '11 at 13:46
  • @VonC You can specify the in `git update-ref ` to be the right branch, like "refs/heads/master" instead of HEAD, for example. I hope I did not misunderstand your question. – Lavinia-Gabriela Dobrovolschi Jan 07 '11 at 13:53
  • @Sylvain: +1 good edit. @Lavinia-Gabriela Dobrovolschi thank you for the precisions. That is much more practical (if you have a direct access to the remote server, I presume). – VonC Jan 07 '11 at 15:44
  • 3
    The examples are misleading with regards to the `branch-name` argument. When using `update-ref` with a “branch” you absolutely must specify the branch’s full ref name (i.e. prepend `refs/heads/` to the normal short branch name). If you just use the short name you will end up creating/updating `$GIT_DIR/branch-name` instead of `$GIT_DIR/refs/heads/branch-name`. The existence of both `branch-name` and `refs/heads/branch-name` will cause “refname … is ambiguous” warnings. – Chris Johnsen May 17 '11 at 02:43
  • This answer is much more complicated then what Zach proposed. And his solution works fine. – Krystian Feb 07 '12 at 10:34
36

If you use the following in a bare repo:

git reset --soft <commit>

then you don't run into the issues you have using --hard and --mixed options in a bare repo since you're not trying to change something the bare repo doesn't have (i.e. working tree and index). In your case specifically you would want to use (from the bare repo):

git reset --soft HEAD^

To switch branches on the remote repo do:

git symbolic-ref HEAD refs/heads/<branch_name>

To see current selected branch use:

git symbolic-ref HEAD

https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-symbolic-ref.html

Samuel Åslund
  • 2,814
  • 2
  • 18
  • 23
Hazok
  • 5,373
  • 4
  • 38
  • 48
  • 3
    How do you select the branch that you want to move? Your example works fine on master but `git checkout other_branch` does not work in a bare. – Gauthier Dec 20 '11 at 13:33
  • 1
    Hmmm... I'm wondering who voted me down on this. The question didn't ask how to switch branches on a remote repo, it asked how to reset on a bare repo. To change the default branch in a remote repo, use git symbolic-ref HEAD refs/heads/. – Hazok May 19 '13 at 22:18
6

The git push -f should work fine:
if you clone that bare repo, remove the last commit (git reset --hard HEAD^ as you mention, but in a local non-bare repo) and push back (-f):

  • you don't change any SHA1 for the other commits preceding the one you remove.
  • you are sure you push back the exact content of the bare repo minus the extra commit (because you just cloned it first).
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • @ VonC Hi Von, I've seen you answer a lot on Git so wanted to ask you... I was curious, why wouldn't `git reset --soft ` as shown in my answer below be the recommended practice for moving HEAD on a bare repo? – Hazok May 17 '11 at 01:23
  • I guess another reason I ask is that using soft reset for bare repos isn't information that is readily available and many forums seem to have unnecessarily complex workarounds when it seems the soft reset is the best practice due to least amount of typing and least chance for error. – Hazok May 17 '11 at 01:25
  • 2
    @Zach: a `reset --soft` should work when done directly on a bare repo. I suspect this s rarely done because a bare repo is generally an **upstream repo** (i.e. a repo to which you are pushing data), and most of the time, you *don't* have a direct local access to it. But if you do, then this is certainly another good example of "`reset --soft`" usage (as in http://stackoverflow.com/questions/5203535/practical-uses-of-git-reset-soft ) So +1 to your answer. – VonC May 17 '11 at 03:59
2

You can also use git refspec notation and do something like this:

git push -f origin +<commit you want to revert to>:<destination_head | branch_name>

This forces update of destination branch (as denoted by the ref) to the source commit as denoted by the +<object ref> part.

alup
  • 2,961
  • 1
  • 21
  • 12
  • 2
    except when there is an acl on the branch - which is usually the case if you "need to do it on the bare repo itself" ... – David Schmitt Jan 31 '12 at 17:12