Juan
Juan

Reputation: 439

Git Hooks post-receive not working as expected

I setup a git repo on my server by running git init --bare on /var/repo/app.example.com.git dir and adding post-receive file with a code of

#!/bin/bash
git --work-tree=/var/www/app.example.com --git-dir=/var/repo/app.example.com.git checkout -f

then do

sudo chmod +x post-receive

then on my local repo i added a remote

git remote add deploy-dev ssh://user@serverip/var/repo/app.example.com.git

when i run this for the first time it works fine.

git push deploy-dev dev

the i added changes on dev branch then i push on dev.

git push -u origin dev

and after successfully push on dev branch i run

git push deploy-dev dev

it doesn't applied my last push on dev branch

Any suggestion?

Upvotes: 0

Views: 675

Answers (2)

torek
torek

Reputation: 488103

With 99.997% probability, the problem is that you're checking out master, not dev.

Each repository has its own current branch

All Git repositories, including one on a server, have these parts:

  • the repository database itself, which stores objects and references like refs/heads/master for the master branch;
  • a HEAD, which stores the current branch;
  • an index, which is where you build the next commit; and
  • an optional work-tree, which is where you do your work.

(A --bare repository, which is one with core.bare configured to true, omits the work-tree, but you can set one temporarily.)

The current branch is, as always, the one stored in HEAD.

In a bare repository, the index gets no direct use: since the major purpose of the index is to use it with the work-tree to build the next commit to make, and a bare repository has no work-tree, its index gets no exercise unless you write a post-receive deployment script.

Think about the current branch

Your post-receive hook, being just one line, is pretty trivial: it means that every time the server has had something—anything, maybe a new tag for instance—pushed to it, it runs the one line command:

git --work-tree=/var/www/app.example.com \
    --git-dir=/var/repo/app.example.com.git \
    checkout -f

(I broke the one line out into three for display/discussion purposes). Think about what each of these parts do:

--work-tree=...

assigns a work tree, so that your git command works on that path as its work-tree. This overrides any core.bare setting: the resulting Git command has a work-tree. Next:

--git-dir=...

This overrides the default method of finding the Git directory. It will work out of the given repository, no matter what $GIT_DIR is set to, and no matter which directory it's actually running in when you start it.

checkout -f

This command should be familiar to you. What does it do?

You already know that git checkout checks out a branch. The -f option, which you can spell as --force, tells Git that even if the checkout would overwrite unsaved work-tree files, it should go ahead and overwrite them. But: what branch does this check out?

The git checkout documentation has some readability issues, but it does contain the answer:

git checkout <branch>

    To prepare for working on <branch>, switch to it by updating the index and the files in the working tree, and by pointing HEAD at the branch. Local modifications to the files in the working tree are kept, so that they can be committed to the <branch>.
    ...
    You could omit <branch>, in which case the command degenerates to "check out the current branch" ...

(boldface mine).

If you intend to deploy some particular branch, that had better be the current branch, if you use this form of git checkout.

Note that git checkout depends on the current index as well: it assumes that the current index describes, in a sense, the current work-tree. This is why the index is also called the cache. Git caches information about the work-tree in the index, and uses this to avoid looking closely at the work-tree at all, if it can. This is one of Git's speed secrets: by comparing some fast-to-find information about the work-tree with some fast-to-find information stored in the cache-like index, Git can skip some slow operations on the work-tree.

If you swap the work-tree out from under Git using --work-tree=, the index had best describe this work-tree now, and not some other one. If your post-receive deployment script always uses a single work-tree and always deploys from a bare repository, that's OK, but if not, it's not.

There are other, fancier ways to do Git deployments that have different or no limitations, but the key take-aways here are:

  • Use this only with a bare repository.
  • Use only one --work-tree argument. If you need more, use a fancier deployment script.
  • Remember that this deploys the current branch, regardless of what anyone pushed. If you need something different, do not use this deployment script. If there's just one branch to deploy, either make it the current branch before running this deployment script, or modify this deployment script to read git checkout <branch-name> to make it the current branch during each deployment. (Either way you are now stuck with that as the current branch of the underlying repository, so be sure that's OK too!)

Upvotes: 1

user5845413
user5845413

Reputation:

Let me explain what you are doing: First you added a remote url for you remote branch deploy-dev. Then you pushed your local branch dev to your remote branch deploy-dev.

And then you make a mistake:

git push -u origin dev

git push -u origin <branch> means, that you push your current branch dev to the remote branch with the name dev. Earlier you named your remote branch deploy-dev. So I guess when you run git fetch --all you will see 2 remote branches.

And for your information: If you added a remote url and you set the remote branch as a upstream for your local branch. You finally just check if you are on the right branch and can use git push .

Upvotes: 0

Related Questions