Git post-receive hook to checkout each branch to different folders?
Asked Answered
F

2

7

The basic setup of our Git workflow is a bare repository on a local network server with two developers pushing/pulling to it.

We would like to automagically copy (checkout) each branch that gets pushed to a different location on our local network server. To exemplify:

Pushing the 'develop' branch copies to 'develop' sub-folder. Pushing the 'master' branch copies to the 'master' sub-folder.

The problem we are having is getting the post-receive hook to do this. Here is what we currently have:

#!/bin/bash

while read oldrev newrev ref
do
  branch=`echo $ref | cut -d/ -f3`

  if [ "master" == "$branch" ]; then
    GIT_WORK_TREE=/master
    git checkout -f $branch
    echo 'Changes pushed master.'
  fi

  if [ "develop" == "$branch" ]; then
    GIT_WORK_TREE=/develop
    git checkout -f $branch
    echo 'Changes pushed to develop.'
  fi
done

The error received is:

'remote: fatal: This operation must be run in a work tree remote: Changes pushed to develop.'

As you would expect from that error - nothing is actually checked out.

I had also tried the post-receive this way but the same issue:

#!/bin/bash

while read oldrev newrev ref
do
  branch=`echo $ref | cut -d/ -f3`

  if [ "master" == "$branch" ]; then
    git --work-tree=/master checkout -f $branch
    echo 'Changes pushed master.'
  fi

  if [ "develop" == "$branch" ]; then
    git --work-tree=/develop checkout -f $branch
    echo 'Changes pushed to develop.'
  fi
done

Can anyone explain what I am doing wrong here (feel free to explain it like you would to a 3-year old :)). Thanks.

To make the answer clearer for future readers, Torek hit it on the head. I was using --work-tree=/master to try and get to to a folder called 'master' inside the root of my bare repo (e.g. alongside 'branches', 'hooks' etc). As soon as I changed this to --work-tree=./master (note the dot before the forward slash) everything worked as I expected.

Fecundity answered 9/10, 2013 at 10:25 Comment(0)
D
3

You're getting this because /master and /develop are non-existent directories:

$ git --work-tree=/nonexistent checkout master
fatal: This operation must be run in a work tree

You may also want to see my answer to a question about another problem that crops up with this approach, which also addresses a small bug you've copied from a popular-but-wrong post-receive technique (the use of cut to parse the updated ref).

[Your phrasing also makes me wonder if you are thinking of deploying those two branches into a sub-directory within the (presumably --bare) repository that is receiving the pushes. This is probably not a great idea.]

Another (different) method for deploying is to have a "real" git tree in the deployment location. Then, instead of git --work-tree=... checkout you do something like this instead:

deploy()
{
    local path=$1 branch=$2

    (cd $path && unset GIT_DIR && git fetch && git checkout -f origin/$branch)
}

(untested, feel free to experiment and/or modify). This has other, slightly different tradeoffs with respect to disk space and update windows (which I mention in the other answer).

Divert answered 9/10, 2013 at 11:57 Comment(2)
torek, Forgive my basic questions. The 'master' and 'develop' folders do exist but I don't doubt I am misunderstanding things. Fundamentally, my need is to copy files form one branch to one server location and the files from another to another. I had used sub-folders of the original bare repo as the destination merely to just 'get it working' but it sounds you have a better idea. With the deploy() snippet you have provided - would that go in the post-receive hook? Sorry for the basic questions but this is all very much over my head at present.Fecundity
You keep using the word "folder", are you on a non-unix/linux-type system? Note that /xxx denotes a file or directory in the root, not a sub-directory ("sub-folder") of anything else. (To name a sub-directory of "here", use ./xxx for example. But in a bare repo that's writing directly into git's private storage area, hence "not a great idea": you might accidentally overwrite something important to git.) As for deploy: follow the link, there's a script in there that works for someone else with a similar setup.Divert
D
8

you should put 'GIT_WORK_TREE=/master git checkout -f $branch' in one line, then it worked, after read your post, my post-receive is :

#!/bin/sh

while read oldrev newrev ref
do
  branch=`echo $ref | cut -d/ -f3`

  if [ "master" == "$branch" ]; then
    GIT_WORK_TREE=/home/tnj/www/www.test.com git checkout -f $branch
    chmod -R 775 /home/tnj/www/www.test.com/
    echo 'changes pushed to www.test.com'
  fi

  if [ "develop" == "$branch" ]; then
    GIT_WORK_TREE=/home/tnj/www/www.test.com.dev git checkout -f $branch
    chmod -R 775 /home/tnj/www/www.test.com.dev/
    echo 'changes pushed to www.test.com.dev'
  fi
done

it worked fine, thank you for your post ! :)

Derosa answered 16/12, 2014 at 2:30 Comment(0)
D
3

You're getting this because /master and /develop are non-existent directories:

$ git --work-tree=/nonexistent checkout master
fatal: This operation must be run in a work tree

You may also want to see my answer to a question about another problem that crops up with this approach, which also addresses a small bug you've copied from a popular-but-wrong post-receive technique (the use of cut to parse the updated ref).

[Your phrasing also makes me wonder if you are thinking of deploying those two branches into a sub-directory within the (presumably --bare) repository that is receiving the pushes. This is probably not a great idea.]

Another (different) method for deploying is to have a "real" git tree in the deployment location. Then, instead of git --work-tree=... checkout you do something like this instead:

deploy()
{
    local path=$1 branch=$2

    (cd $path && unset GIT_DIR && git fetch && git checkout -f origin/$branch)
}

(untested, feel free to experiment and/or modify). This has other, slightly different tradeoffs with respect to disk space and update windows (which I mention in the other answer).

Divert answered 9/10, 2013 at 11:57 Comment(2)
torek, Forgive my basic questions. The 'master' and 'develop' folders do exist but I don't doubt I am misunderstanding things. Fundamentally, my need is to copy files form one branch to one server location and the files from another to another. I had used sub-folders of the original bare repo as the destination merely to just 'get it working' but it sounds you have a better idea. With the deploy() snippet you have provided - would that go in the post-receive hook? Sorry for the basic questions but this is all very much over my head at present.Fecundity
You keep using the word "folder", are you on a non-unix/linux-type system? Note that /xxx denotes a file or directory in the root, not a sub-directory ("sub-folder") of anything else. (To name a sub-directory of "here", use ./xxx for example. But in a bare repo that's writing directly into git's private storage area, hence "not a great idea": you might accidentally overwrite something important to git.) As for deploy: follow the link, there's a script in there that works for someone else with a similar setup.Divert

© 2022 - 2024 — McMap. All rights reserved.