reset hard on git push
Asked Answered
H

3

16

I have a post-receive hook script sitting on the remote repo I am pushing to that does a git reset --hard

Something like this:

$ git push opal
Counting objects: 74, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (45/45), done.
Writing objects: 100% (53/53), 16.68 KiB, done.
Total 53 (delta 20), reused 0 (delta 0)
remote: warning: updating the current branch
remote: HEAD is now at 88f1e35 tweak lavalamp styles

What i don't understand here is - the remote says head is now at XXX but when i log into the server - the remote working copy is not updated at all!

any idea?

Hilarius answered 3/4, 2011 at 17:48 Comment(2)
You should really post your hook script when you are having problems with it.Kevin
Chris, my post-receive script only contains that single line. no need to post.Hilarius
K
25

The issue is a difference in how Git commands behave in the environment created for hook scripts versus your normal environment.

First, hook scripts run with their current working directory set to the Git directory itself (i.e. the .git/ directory of a non-bare repository). Second, hook scripts run with the GIT_DIR environment variable set and pointing to the Git repository (again, the .git/ directory of a non-bare repository).

Normally, if you try to run git reset --hard from the .git/ directory, it will die with the following message:

fatal: This operation must be run in a work tree

But when GIT_DIR is set, Git commands assume that the current directory is the working tree. Since the current directory when the hook runs is the .git/ directory, your git reset --hard is actually “checking out” your working tree files directly into .git/ instead of its parent directory (i.e. you now have a copy of your versioned content in your .git/ directory).

Hopefully none of versioned content in your repository has pathnames that coincide with pathnames that Git uses in Git repositories themselves. If they do coincide, then your git reset --hard will have overwritten some bit of the internal structure of your repository and you will probably want to re-clone it from some other repository. If you are confident that none of the versioned content conflicts with Git’s internal pathnames, then you may be able to clean it up with this:

# make a backup of your repository first!
(cd .git && GIT_DIR=$PWD git ls-files -cz | xargs -0 rm)

This will only remove currently tracked files (it will leave behind files that have since been removed, but were once tracked in tip commits pushed while the broken hook was active).


One solution is to change the current working directory to the normal working tree and unset GIT_DIR and GIT_WORK_TREE before calling Git commands.

⋮
test "${PWD%/.git}" != "$PWD" && cd .. 
unset GIT_DIR GIT_WORK_TREE
# you can now safely use Git commands
⋮

Another solution is to explicitly reset GIT_DIR, set GIT_WORK_TREE and chdir there. The Git FAQ “Why won't I see changes in the remote repo after "git push"?” recommends a post-update script that does just this. The linked script is also much safer since it makes a stash if the index or working tree is dirty before doing the hard reset.

Kevin answered 4/4, 2011 at 4:11 Comment(3)
+1 Aargh, that's horrendous. Even worse, I didn't realise previously that $PWD and $GIT_DIR aren't consistent for different hooks when run in a non-bare repository. (e.g. in post-commit they're set to the working tree and .git respectively.)Monteiro
@Mark: Hmm, I had not given much consideration to “local” hooks. It looks like it may be just the the “remote” hooks (receive and update variations) that setup this problematic environment. The other hooks are mostly aimed at activities that require a working tree anyway and they seem to be run with a sane configuration for Git commands that need a working tree (which makes sense since such hooks are usually invoked from commands that need a working tree).Kevin
right - I've just done a blog post describing the GIT environment variables and current directories for each hook here: longair.net/blog/2011/04/09/missing-git-hooks-documentationMonteiro
H
18

In short, use the hook one-liner:

git --git-dir=. --work-tree=$PWD/.. reset --hard

To be more precise, edit the file .git/hooks/post-receive on the server:

#!/bin/sh
git --git-dir=. --work-tree=$PWD/.. reset --hard

Set it executable:

chmod +x .git/hooks/post-receive

When pushing to this repo from the client, it should say something like:

HEAD is now at abcd123 comment
Hautrhin answered 21/11, 2012 at 10:17 Comment(1)
Even better than mutating the environment and available in git before the question was asked.Disastrous
P
0

Well the script is probably not run. It will not run over dumb http server. It will run over ssh. I'm not sure with smart http server.

If it's not that, you ought to check the 'execute' permisssion on the hook (chmod +x .git/hooks/post-receive). While you're at it, generally check owenership and permissions.

If that seems ok, simply include a logstatement as first line in the script (e.g. date "%T $0 executed" >> /tmp/debug_hook.log) and check the logfile to see whether anything got updated.

Also, it is possible for a push to not actually do anything (everything up-to-date). In that case, it makes sense that the hook isn't called

If all this doesn't give a hint, please post .git/config as it resides on the server (or at least part of). Does git log -1 HEAD give the expected outcome on the server? Does your hook script contain anything that may override GIT_DIR, GIT_WORK_TREE or GIT_INDEX_FILE?

Ponder answered 3/4, 2011 at 23:31 Comment(2)
The HEAD is now at … message is only likely to be coming from git checkout or git reset. Neither are a part of a normal push, so the hook script is almost certainly being run.Kevin
Did you do any of the other checks? Especially the HEAD check? Reading the other post, you'd have to had a positive result on the 'GIT_DIR' check...Ponder

© 2022 - 2024 — McMap. All rights reserved.