24

I've setup my environment so I can push to a remote bare repository. I used these commands to set up the remote repository:

$ mkdir ~/website.git && cd ~/website.git
$ git init --bare

And

$ cat > hooks/post-receive

#!/bin/sh
GIT_WORK_TREE=/var/www/website git checkout -f

$ chmod +x hooks/post-receive

And on my local environment:

$ git remote add web ssh://website.com/home/website.git
$ git push web +master:refs/heads/master

Now I can deploy to this remote using git push web, and everything works great..

The problem: Submodules

I have a few submodules on my project that aren't getting initialized/updated at the remote repository. I can't run git submodule update on the bare because it's bare, and I can't run it on the /var/www/website folder because it's just a copy of the files and not a git repo.

Community
  • 1
  • 1
Thiago Belem
  • 7,524
  • 4
  • 40
  • 64

3 Answers3

11

I figured out another solution which looks rather clean to me. Just give git all the info it needs to perform the submodule stuff:

$ cd /path/to/your/git_work_tree
$ git --git-dir=/path/to/your/bare_repo.git --work-tree=. submodule init
$ git --git-dir=/path/to/your/bare_repo.git --work-tree=. submodule update
trivialanalog
  • 111
  • 1
  • 4
10

One possible way might be:

In other words:
Pull from the bare repo instead of trying to checkout from a bare repo: a non-bare repo should be able then to accommodate the git submodule update step.

An example script may look like

#!/bin/sh

# Get the latest code
cd /path/to/bare/repo

# Set git variables
GIT_WORK_TREE=/var/www/website
GIT_DIR=/var/www/website/.git

# Go to website and pull
cd /var/www/website
git pull /path/to/bare/repo
git submodule update --init --recursive

# Run additional build stuff here
VonC
  • 1,042,979
  • 435
  • 3,649
  • 4,283
  • Can you provide an example for the post-receive / post-update script (although this answer is pretty old)? Every time I try to push I gets errors. – akohout Oct 20 '15 at 12:01
  • remote: fatal: Not a git repository: '/var/www/website' – akohout Oct 20 '15 at 12:08
  • @raveN is there a `.git` folder in `/var/www/website`? – VonC Oct 20 '15 at 12:09
  • yes :) I cloned the repository as non-bare (as you have written) – akohout Oct 20 '15 at 12:09
  • 1
    Is `GIT_DIR` set to `/var/ww/website/.git` and `GIT_WORK_TREE` to `/var/ww/website/`? – VonC Oct 20 '15 at 12:11
  • Ahhh, the GIT_DIR wasn't set to .git - thank you so much for your fast help. I will edit your answer to provide a full example. – akohout Oct 20 '15 at 12:34
  • @VonC do you think it's necessary to `git fetch`? I ran into the same problem and solved it as you suggested without fetching. because fetching requires origin and in this case `bare repo` has to point it to itself. – Abu Shumon Apr 28 '18 at 18:43
  • 1
    @johnshumon Right! I have removed the unnecessary step. Not sure what I was thinking about when I write this answer 6 years ago... – VonC Apr 28 '18 at 18:55
6

I stumbled across this thread two days ago while I struggled with the same issue. After finally arriving at a nice, tidy solution, I wrote an article about it here:

Git push with submodules: a how-to guide

I realized that if I'm going to push to a bare repo, only to use post-receive to pull into a non-bare repo, I might as well just keep it simple and push directly to the non-bare repository. This is a clear case where the "best practice" of only pushing to a bare repo is only adding complexity.

In case of link rot, I'll paste my solution here, skipping over the bits where I run into all of the same problems that I'm sure you did.


First, let’s create a universal post-receive hook, one that I won’t need to change on a per-repository basis:

[aaron@aaronadams]$ cat > /usr/local/share/git-core/templates/hooks/post-receive.sample
#!/bin/sh
#
# An example hook script to update the working tree, including its
# submodules, after receiving a push.
#
# This hook requires core.worktree to be explicitly set, and
# receive.denyCurrentBranch to be set to false.
#
# To enable this hook, rename this file to "post-receive".

# Read standard input or hook will fail
while read oldrev newrev refname
do
:
done

# Unset GIT_DIR or the universe will implode
unset GIT_DIR

# Change directory to the working tree; exit on failure
cd `git config --get core.worktree` || exit

# Force checkout
git checkout --force

# Force update submodules
git submodule update --init --recursive --force
[aaron@aaronadams]$ chmod +x /usr/local/share/git-core/templates/hooks/post-receive.sample

Now let’s go ahead and break all the rules.

We’re going to initialize a non-bare Git repository, right in our website directory; make sure it can receive from git push; explicitly set its working tree to its parent directory; and enable our hook we just created.

[aaron@aaronadams]$ cd /var/www/vhosts/aaronadams.ca/sites/staging.aaronadams.ca
[aaron@aaronadams]$ git init && git config --bool receive.denyCurrentBranch false && git config --path core.worktree ../ && mv .git/hooks/post-receive.sample .git/hooks/post-receive
Initialized empty Git repository in /var/www/vhosts/aaronadams.ca/sites/staging.aaronadams.ca/.git/

Finally, on our local machine, we’ll change our remote to reflect the location of our new repository, and push.

[aaron@aaronadams]$ git remote set-url staging aaron@aaronadams.ca:sites/staging.aaronadams.ca
[aaron@aaronadams]$ git push staging master
remote: Submodule 'codeigniter' (git://github.com/EllisLab/CodeIgniter.git) registered for path 'codeigniter'
remote: Cloning into 'codeigniter'...
remote: Submodule path 'codeigniter': checked out 'fd24adf31255822d6aa9a5d2dce9010ad2ee4cf0'
To aaron@aaronadams.ca:sites/staging.aaronadams.ca
 * [new branch]      master -> master

Holy crap, it worked!

Not only is this method compatible with submodules, it also requires just one command to set up a new remote repository (which, okay, consists of four commands). It also keeps the repository and the working tree in the same place; and with no absolute paths required in our configuration or hook files, it’s now completely portable as well.


I hope this answer helps somebody as much as everyone else's Stack Exchange posts helped me over the last two days!

Aaron Adams
  • 1,534
  • 15
  • 22
  • I tried your article but after the first push it breaks. I notice it is not storing files in the objects directory. The error is: error: unable to create temporary sha1 filename ./objects/9e: No such file or directory. I believe it should be .git/objects/9e. Any ideas? This is very close – Dobler May 09 '13 at 06:41
  • Interesting. It sounds like your configuration maybe ended up a little different from mine in the end – but hey, as long as it's working! – Aaron Adams May 09 '13 at 19:48
  • 1
    This will likely cause problems if multiple programmers are deploying to the same website. See ["all about bare repos"](http://gitolite.com/concepts/bare.html). VonC's solution avoids this hiccup. – jrhorn424 Sep 01 '13 at 05:39
  • 2
    @jrhorn424 Indeed, there's lots wrong with the above solution; I've learned plenty since I wrote it, the most important being that submodules create more problems than they solve. I'd strongly recommend switching to the submodule-free solution I've documented here: http://stackoverflow.com/questions/14656047/how-do-i-add-upgrade-downgrade-a-remote-git-project-in-my-repository-without-usi – Aaron Adams Sep 02 '13 at 04:13
  • Thanks, @AaronAdams. Good write up. Actually, I couldn't get VonC's solution to work after all, and this "fake bare" technique is actually used by [vcsh](https://github.com/RichiH/vcsh). Since my use case is dotfiles, I think this is OK, but I wouldn't use it for websites. – jrhorn424 Sep 02 '13 at 18:17
  • 1
    @jrhorn424 Indeed, my original blog post documents all of the pains I encountered in attempting to implement that solution myself. – Aaron Adams Sep 02 '13 at 21:41
  • @AaronAdams I think this is more of a overkill. – Abu Shumon May 01 '18 at 18:34
  • @AbuShumon Hey man, could be. The Internet didn't work that great in 2013. There are almost certainly better solutions in 2019. – Aaron Adams Jan 30 '19 at 20:14