235

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?

Andrey Fedorov
  • 2,079
  • 4
  • 16
  • 12
  • I am using "Github for Windows" and switch between two Github accounts: http://stackoverflow.com/questions/18565876/cannot-access-remote-git-repository/31010578#31010578 – Alisa Jun 23 '15 at 18:52

10 Answers10

203

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.

womble
  • 95,029
  • 29
  • 173
  • 228
  • 67
    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 '10 at 09:04
  • 7
    +1 for POSIX extended attributes - news to me! – RobM Nov 02 '11 at 15:08
  • 5
    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 '12 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 '12 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 (http://git-scm.com/docs/git-init), however, allows you to setgid and override the umask of the user. – Chase T. Apr 09 '14 at 15:17
  • 1
    Note to all, `git --bare init` but `git --shared --bare init` needs to be rephrased as `git init --bare --shared` because the shared needs to be after init ;) – lol Jul 14 '15 at 05:29
  • @ptman: ` That removes...setting the umask as git will set it to a sane value before manipulating files` As of today, my Git on OS X doesn't seem to set shared permissions correctly when I **clone an existing repository** into a bare one with `git clone --bare /path/to/git_repo ./git_repo.git` and after that reinitialize the whole repository with `cd ./git_repo.git && git init --bare --shared`. The repository database `./objects` is not writable by the shared group of the repo, neither is `info`, `hooks`. I can only push with the owner of the repo... I guess it has to do with OS X's `umask`. – user3019105 Aug 21 '15 at 07:14
  • As a ZFS user, I initially got a lot of errors about unsupported operations when using the `setfacl` solution. It turns out that you need to run `zfs set acltype=posixacl dataset` (where "dataset" is the name of your dataset, usually the mount point without the initial slash) before it will work. – Jules Apr 18 '16 at 19:53
  • As for `core.sharedRepository`, how would git know which group to apply, if user is in several ones? It just takes primary group? I'd rather use `setgid` bit then. – x-yuri Nov 22 '16 at 12:58
  • 1
    Since this needs to be run with sudo (in most environments) piping find output to xargs will break, but the `-exec` arg in `find` will work: `sudo find . -type d -exec setfacl -R -m d:g:$whatever_group:rwX {} \;` – hobs May 10 '17 at 18:18
  • Or you could run `sudo xargs`. But since we're on a sysadmin Q&A site, "have to run it as root" is, in general, implied. – womble May 11 '17 at 01:14
  • I think you don't need find/xargs. setfacl in recursive mode doesn't set default ACLs for plain files. – woky Oct 22 '18 at 23:01
131

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.

Beachhouse
  • 208
  • 1
  • 5
  • 9
    This is definitely the correct answer. – ELLIOTTCABLE Dec 05 '11 at 07: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 '12 at 05:42
  • 3
    What is `NNN`—permissions mask or a group number or something else? – Craig McQueen Jul 08 '14 at 02:48
  • 24
    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 '14 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. – interestedparty333 Apr 29 '16 at 17:24
  • 1
    This is not a complete solution. The work tree still has permissions problems (accepted answer may address these). – Sam Brightman Apr 20 '17 at 12:59
  • I like @Beachhouse's [answer](https://serverfault.com/posts/113688/timeline) telling about SSH-based permission-able git: https://github.com/res0nat0r/gitosis https://github.com/sitaramc/gitolite (fork von gitosis) – Pokulo Feb 18 '20 at 14:32
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.

Niels Joubert
  • 669
  • 5
  • 3
  • 7
    According to git-config(1) (http://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. – David Schmitt Aug 12 '11 at 10:05
  • 16
    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 '11 at 03:43
  • Does it still change the ownership of a file when it's pushed to remote? – Duc Tran Jul 28 '16 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 '16 at 11:02
  • The config file is `.git/config`. – Friedrich Oct 01 '21 at 09:40
22

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.

Roman Byshko
  • 254
  • 2
  • 13
jtimberman
  • 7,511
  • 2
  • 33
  • 42
  • 1
    I like Gitosis. It's a pretty effective way to control access based on public keys. – Mike Mazur Jun 17 '09 at 07:42
  • How do any of these solutions solve the problem of "I'd like multiple people to pull to that repository"? – womble Jun 17 '09 at 07:59
  • have a look at gitosis. that one solves your problem. – pilif Jun 17 '09 at 08:43
  • @pilif: No it doesn't, as far as I can tell. Care to elaborate? – womble Jun 17 '09 at 08:59
  • 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 '09 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. – Jeremy Bouse Jun 18 '09 at 18:42
  • I'd like multiple people to pull *TO* that repository ;) – Andrey Fedorov Jun 26 '09 at 02:08
9

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

mt3
  • 310
  • 1
  • 3
  • 12
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).

Justin Ludwig
  • 1,006
  • 7
  • 8
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
bkmks
  • 69
  • 1
  • 2
  • the right answer for when users have different default groups – Pat Nov 23 '11 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 '12 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 '16 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. – Daniel Böhmer Dec 06 '18 at 13:42
3

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.

Vihang D
  • 632
  • 4
  • 7
3

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
0

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

git clone --config core.sharedRepository=true