239

I have a Git repository on a staging server which multiple developers need to be able to pull to. git-init seems to have a flag very close to what I'm looking for: --shared, except I'd like multiple people to pull to that repository, as well. The git-clone's --shared flag does something entirely different.

What's the easiest way to change an existing repository's permissions?

1

10 Answers 10

208

Permissions are a pest.

Basically, you need to make sure that all of those developers can write to everything in the git repo.

Skip down to The New-Wave Solution for the superior method of granting a group of developers write capability.

The Standard Solution

If you put all the developers in a specially-created group, you can, in principle, just do:

chgrp -R <whatever group> gitrepo
chmod -R g+swX gitrepo

Then change the umask for the users to 002, so that new files get created with group-writable permissions.

The problems with this are legion; if you’re on a distro that assumes a umask of 022 (such as having a common users group that includes everyone by default), this can open up security problems elsewhere. And sooner or later, something is going to screw up your carefully crafted permissions scheme, putting the repo out of action until you get root access and fix it up (i.e., re-running the above commands).

The New-Wave Solution

A superior solution—though less well understood, and which requires a bit more OS/tool support—is to use POSIX extended attributes. I’ve only come to this area fairly recently, so my knowledge here isn’t as hot as it could be. But basically, an extended ACL is the ability to set permissions on more than just the 3 default slots (user/group/other).

So once again, create your group, then run:

setfacl -R -m g:<whatever group>:rwX gitrepo
find gitrepo -type d | xargs setfacl -R -m d:g:<whatever group>:rwX

This sets up the extended ACL for the group so that the group members can read/write/access whatever files are already there (the first line); then, also tell all existing directories that new files should have this same ACL applied (the second line).

Hope that gets you on your way.

12
  • 70
    git init has a parameter called --shared which sets up the core.sharedRepository variable for group work. You can also set the variable on an existing repository. That removes the need of manually setting the umask as git will set it to a sane value before manipulating files.
    – ptman
    Feb 17, 2010 at 9:04
  • 7
    +1 for POSIX extended attributes - news to me!
    – RobM
    Nov 2, 2011 at 15:08
  • 6
    When I did chmod -R g+swX, it made Git very unhappy and it decided it wasn't a git repository anymore ("repo does not appear to be a git repository"). I had to chmod g-s all of the files. To just set the setgid bit on the directories, try find /path/to/repo -type d -print0 | xargs -0 chmod g+s. Still do the chgrp -R thegroup /path/to/repo.
    – rescdsk
    Apr 25, 2012 at 19:01
  • 13
    chmod -R g+swX gitrepo will apply the setguid bit to files, which is a security risk. Instead, you can use find . -type d -exec chmod g+s {} + to apply it only to directories.
    – Ian Dunn
    Sep 19, 2012 at 19:47
  • 1
    ACL (setfacl) doesn't have setting for setgid to enforce new files and subdirectories created within a directory to inherit its group ID. Therefore, you must set setgid separately through chmod. Git's --shared option (git-scm.com/docs/git-init), however, allows you to setgid and override the umask of the user.
    – Chase T.
    Apr 9, 2014 at 15:17
134

if you created the repository (or cloned a new bare repo off an existing one) with

$ git init --shared=group 

or

$ git init --shared=0NNN

Git is supposed to handle permissions above and beyond what your default umask provides. At last this is true on my version of Git (1.6.3). Of course this assumes your users are in the same group.

If I needed management of users in multiple groups with varying degrees of read/write however, I'd go with gitosis. I have also heard mention of gitolite (http://github.com/sitaramc/gitolite), a gitosis fork that is suppossed to provide branch level permissions, can't say I've every used it personally though.

7
  • 9
    This is definitely the correct answer. Dec 5, 2011 at 7:45
  • 4
    I had this issue and this is by far the best answer. The only problem is that the --shared argument takes in an octal, not hexadecimal. I have confirmed this in the source of Git 1.7.8 and the second example should be git init --shared=0NNN.
    – qpingu
    Jan 25, 2012 at 5:42
  • 3
    What is NNN—permissions mask or a group number or something else? Jul 8, 2014 at 2:48
  • 25
    BTW, "group" above is a keyword, not a placeholder for your group name. You assign the group using the chgrp command. For a new repo it is git init --bare --shared=group myproj where myproj is your repo name, followed by chgrp -R mygroup myproj where mygroup is your group name.
    – labradort
    Jul 17, 2014 at 13:05
  • 2
    Heads up that if your users make commits when their default group is different than what it should be, it can screw things up. To fix this problem, you'll need each user to chgrp every file they own in the repo to the right group. This will recur unless you figure out how to make everyone create/switch new files under/to the right group before committing and pushing. Apr 29, 2016 at 17:24
56

This has not been said, so I want to quickly add it.

To ensure that permissions issues do not crop their ugly head, make sure to set the following on your git shared repository's config file:

[core]
    sharedRepository = true

This will ensure that your system's "umask" settings are respected.

5
  • 7
    According to git-config(1) (kernel.org/pub/software/scm/git/docs/git-config.html) core.sharedRepository, you need to set this to "umask" or "false" to have git respect the user's umask. Aug 12, 2011 at 10:05
  • 18
    This and user35117's answer is correct. Note that "true" is the same as "group", and this can be set with the command git config core.sharedRepository true.
    – ColinM
    Dec 22, 2011 at 3:43
  • Does it still change the ownership of a file when it's pushed to remote?
    – Duc Tran
    Jul 28, 2016 at 22:42
  • 3
    If you want to set this up when cloning rather than after the fact, the equivalent of git init --shared is git clone --config core.sharedRepository=true. Strange of git to use --shared for such different meanings in such similar commands.
    – stevek_mcc
    Sep 30, 2016 at 11:02
  • The config file is .git/config. Oct 1, 2021 at 9:40
21

The Git User Manual describes how to share a repository in several ways.

More complicated, though feature-full ways to share repositories are:

We use GitHub for a team of 6 developers.

7
  • 1
    I like Gitosis. It's a pretty effective way to control access based on public keys.
    – Mike Mazur
    Jun 17, 2009 at 7:42
  • How do any of these solutions solve the problem of "I'd like multiple people to pull to that repository"?
    – womble
    Jun 17, 2009 at 7:59
  • have a look at gitosis. that one solves your problem.
    – pilif
    Jun 17, 2009 at 8:43
  • 3
    When you share the repository, people will be able to pull from it. They'll likely need to clone it, or add a remote branch. The documentation I linked will very clearly walk you through solving your issue; I've used all the methods described to help developers collaborate source code with Git. To my knowledge ServerFault isn't for handholding.
    – jtimberman
    Jun 17, 2009 at 17:34
  • 3
    I have to agree with using Gitosis. It gets around the permissions issue by using a single account authenticated by multiple SSH keys. It is also managed entirely itself via git commits. Jun 18, 2009 at 18:42
8

Also look at gitolite for hosting your git repository. Gitosis apparently isn't being developed anymore.

8

To aggregate the bits and pieces of good advice from the various other answers and comments about setting up a new repo:

If you're setting up a brand new repo myrepo in /srv/git for the group mygroup, this is what you want:

mkdir /srv/git/myrepo.git
chgrp mygroup /srv/git/myrepo.git
git init --bare --shared /srv/git/myrepo.git
  1. the first line creates the repo dir
  2. the second line sets its group to mygroup
  3. the third line initializes a bare repo with the following configuration:
    1. core.bare = true: make it a bare repo
    2. core.sharedrepository = 1 (same as core.sharedrepository = group): the repo directory and all directories later created in it will be managed by git to allow mygroup read, write, and execute permissions (with the sgid bit set as well -- so as to work with users for whom mygroup is not their primary group)
    3. receive.denyNonFastforwards = 1: deny non fast-forward pushes to the repo

If you want to fine-tune the user, group, or other users' permissions, use --shared=0NNN, where NNN are the standard user, group, and other bits for files (the execute and sgid bits on directories will be managed appropriately by git). For example, this allows read and write access to the user, and read-only access to the group (and no access to other):

git init --bare --shared=0640 /srv/git/myrepo.git

This allows read and write access to the user and group (and no access to other):

git init --bare --shared=0660 /srv/git/myrepo.git

This allows read and write access to the user and group, and read-only access to other:

git init --bare --shared=0664 /srv/git/myrepo.git

Note that if you're not going to allow write access to the group, make sure to first use chown to set the owner of the repo, and then run the git init command as that user (to make sure the repo is initialized with the correct owner for all the initial files and sub-directories).

1
  • More correct than the higher vote responses.
    – XO01
    Oct 22, 2018 at 3:14
6

One way to fix permissions in the shared repository, so users won't have permission problems when pushing, is to create a post-update hook script which will do just that. This should work in any git version.

Suppose you have a shared repository in /myrepo.git. All files in that repository belong to say mysharedgroup. All users pushing to that repository should belong to mysharedgroup also. Now create the following file (changing mysharedgroup to your preferences):

/myrepo.git/hooks/post-update

#!/bin/sh
chmod -R g+w . 2>/dev/null
chgrp -R mysharedgroup . 2>/dev/null
4
  • the right answer for when users have different default groups
    – Pat
    Nov 23, 2011 at 23:42
  • Setting the setgid bit on a directory will cause files that users create to inherit the same group ownership as the directory (if the users belong to that group). Even if it's not the users' default group. Then this hook is not needed. This is what @womble's answer (and my comment on it) do.
    – rescdsk
    Apr 25, 2012 at 22:21
  • On my centos7 machine, after trying every solution listed on this page, a variant of @bkmks's solution above was the only option that actually worked (setting the post-merge and post-checkout hooks instead of post-update as above).
    – Mike Godin
    Jun 15, 2016 at 15:58
  • 1
    I think it's bad advice to promote a solution which pipes error messages or warnings on STDER to /dev/null. Please let the user see these messages first and then decide on their own. Dec 6, 2018 at 13:42
4

Doing exactly this worked for me, for an existing repository. This takes advice from several answers and comments before:

From your repository parent directory, at the server:

chgrp -R <whatever group> gitrepo
chmod -R g+wX gitrepo
cd gitrepo
find . -type d -exec chmod g+s {} +
git config core.sharedRepository group
1
  • I also did that. And I set the ACLs, as documented above.
    – Scottie H
    May 16, 2022 at 21:10
2

You can use git-daemon to share the repository. Read the documentation for git-daemon for more information.

EDIT:

Also check this article 8 ways to share your git repository.

0

@stevek_mcc answer is the one I was looking for when I googled for this question

git clone --config core.sharedRepository=true

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .