Description
This is similar to but not a duplicate of the problem reported in #155. In general, the language in chapter 4.2 needs to be a bit more specific and explicit about sharing a bare repository among server users to avoid readers running into subtle permissions problems.
Chapter 4.2 states,
If a user SSHs into a server and has write access to the /opt/git/my_project.git directory, they will also automatically have push access.
Git will automatically add group write permissions to a repository properly if you run the git init command with the --shared option.
The former paragraph is correct but only to the extent that it does not consider what happens when multiple users collaborate in the same directory. The text should more explicitly state that for a shared repository, the latter paragraph is necessary, not simply a convenience.
The second paragraph does not go far enough, as git init --shared
alone will not set group ownership correctly on systems using User Private Groups.
Later, 4.2 reiterates,
It’s important to note that this is literally all you need to do to run a useful Git server to which several people have access – just add SSH-able accounts on a server, and stick a bare repository somewhere that all those users have read and write access to. You’re ready to go – nothing else needed.
Again, this is almost true, but flawed: correct group ownership and core.sharedRepository
(set by --shared
) is also necessary.
A user who finds that they've enabled write access somehow for multiple users and stops there without setting group ownership or core.sharedRepository
correctly will likely run into problems in the future. An improperly configured shared repo could appear to work for some time, until it breaks when collaborators start modifying each others' branches or when git gc
fires.
Without --shared
, the setgid bit will not be applied, and UPG systems will have users creating new files and directories owned by their private groups, which will eventually cause problems. Without --shared
, git will not adjust umask
, so non-UPG systems will have users creating new files and directories lacking group-write permissions, which will eventually cause problems. Read- and write-access will initially appear to make things work, but eventually permissions will conflict and git will die with strange errors, like:
error: insufficient permission for adding an object to repository database ./objects
fatal: failed to write object
error: unpack failed: unpack-objects abnormal exit
To /opt/git/my-project.git
! [remote rejected] HEAD -> SHARED_BRANCH (n/a (unpacker error))
error: failed to push some refs to '/opt/git/my-project.git'
Also, it might be irresponsible to recommend that users simply find a way to enable write-access and leave it at that without further guidance. The text should warn users not to make their directories world-writable in an effort to enable shared access, as that could expose them to security hazards.
Finally, some light testing demonstrates that running git init --shared --bare
against an existing repository containing objects does not set the setgid bit everywhere it is needed. I'll have to confirm that. If so the text should describe how to recursively apply the setgid bit to directories. (chmod --recursive g+s=rwX
) (Also, I'd issue a bug report to the git project.)
To fix these issues, I think it might be best to first change the general process described in 4.2, which is currently of the form:
- perform a
git clone --bare
of a pre-existing repo locally - use
scp
to copy this bare clone to the server - fix up the permissions and configuration to make it shareable among multiple users
I think it would be better to describe a process like this:
- Initialize a new, empty bare repo on the server with permissions set correctly, by executing
newgrp users
to make the shared group the default for the next commandgit init --bare --shared /path/to/repo.git
- Treat the new repository as any other remote. Either:
- Add the remote to a local repo (initializing one if needed), and push all branches into it, or
- Clone the new, empty repo and begin a project from scratch.
This has some advantages:
- It gets permissions right regardless of whether or not the user performs the steps as root (avoiding Get a error on Chapter 4 - Git on the Server #155), and regardless of whether or not their server uses User Private Groups (avoiding problems mentioned above). It even works properly with
sudo
so long as it is applied to the first or both commands. - It describes both how to start from an existing repository and how to start a new, empty project.
- The process is similar to that used in 4.4 to upload an existing repository when no shell access is available. Why describe multiple differing procedures? I would expect that a single procedure reduces cognitive overhead and makes things less scary for new users.
- The process is similar to that used with services like GitHub, Gitorious, and BitBucket, to which one cannot
scp
one's existing repositories. You might even link to their documentation. - You could employ simple language by assuming the reader is familiar with git remotes, and referring back to section 2.5 "Working with Remotes" where necessary.
- This method avoids the creation of a broken remote in the uploaded bare clone.
- It's easier to describe the process to get permissions set up correctly this way, than to explain User Private Groups, chmod, umask, etc.
Finally, I would like to propose an additional section, or aside, or foot-note: how to set up permissions, if you want to make an existing non-shared bare repo shared, or how to clean up permissions, if you've made a mistake and are seeing errors like the one above.