So what's that bare/non-bare stuff all about and why is it making your life complicated? Well, lend me five minutes of your time and I'll teach you to love it (or at least I will have tried).
A non-bare repository is what you have already worked with: it's a directory
with files in it that you work on (we call this the working tree), and a
special .git
directory that contains git's magic metadata (and all version
history).
So far, so good. But what about...
Bare simply means "just that .git
directory". In other words, we skip the
whole working tree business. A bare repository contains all the things you'd
normally see in .git
, and nothing else. The obvious conclusion is that you
can't easily edit files in a bare repository.
I'm glad you asked! It's for repositories that don't need a working tree. That's most often the case if the repository lives on a server and is pretty much only used for pushing to and fetching/pulling from.
Generally, if you want to host a central repository of some kind, you'll always want to make it a bare one. It's easy to create a bare repository:
git init --bare
You'll often want to use --shared
, too, but that's beyond the scope of this
article thingy here.
That's where it gets a bit more involved, I'm afraid. Basically, when you push into a repository that has a working tree, the files in the working tree won't be updated automatically (only their history metadata will). This is a safety precaution of git, since it assumes the files could have been changed... and combining the new changes with the ones in your working tree could lead to conflicts, and you absolutely don't want push to magically create conflicted files in the target repository. And if you actually do want to do that, we'll just call you crazy and burn you at the stake, because we git folks are very mean.
Even if you were okay with an out-of-date working tree, chances are that
things will get very confusing. After a push with unchanged working tree,
you'll still have the old files in your working tree (and index), so git diff
--cached
will make it look like you actually edited all the files so they are
precisely what they were before the push! Scary.
(Side note: all this applies only if the branch you are pushing to is actually checked out. If it isn't, there's nothing to update anyway.)
So suppose that you really, really, want to have a remote repository that
automatically updates its working tree when pushed to. There is a simple
solution for the simple case, when only one person pushes to the repository:
just get this post-update hook script (I
stole the link from the official git FAQ entry about this very
problem), put it into
/your/repo/hooks
and make it executable. This hook will try hard to update
the working tree even in the presence of uncommitted changes (and it works
perfectly if there are no changes).
git-something
!"Easy to fix. Edit the script and replace all occurences of "git-" with "git " (note the space at the end). That's one more replacement than you need but it's simple and gets the job done.
This is where you have to get creative.
If you can simply give all of those users a common system group and assign all
of the files to that group, it's simple. Just add something like this to the
hook script (after the line that says git-reset --hard HEAD)
):
chgrp -R <group> `git rev-parse --show-cdup`
chmod -R g=u `git rev-parse --show-cdup`
In other cases you either need to use POSIX ACLs (which is a quite involved and separate topic, so I won't cover it here) or set up some elaborate scheme where the hook script delegates its work to one fixed user (so the question of permissions is sidestepped).
You could write a cronjob that periodically throws away all changes in that working tree. That way you get rid of permission problems, but that won't cut it if you have changes in the working tree of that repository (because they'll simply vanish), or if you need changes to get into the working tree instantly.