Agile Zone is brought to you in partnership with:

Patrick Debois has been working on closing the gap between development and operations for many years. In 2009 he organized the first devopsdays.org conference and since then the world is stuck with the term 'devops'. Always seeking for opportunities to optimize the global IT instead of local optimizations. Patrick is a DZone MVB and is not an employee of DZone and has posted 39 posts at DZone. You can read more from them at their website. View Full User Profile

DZone Top Article of 2011: 8 Ways to Share Your Git Repository

01.11.2012
| 18956 views |
  • submit to reddit
This blogpost provides a summary of different ways to share a git repository. Depending on your needs you can opt for different solutions.


RepositoryProConControlling AccessCreate Repositories
File shareNo network access requiredNot internet friendlyUsing filepermissionsNeeds preparation per project on the share
Git daemonFast git protocolNot internet friendly portno good permission control. Read by default, Write can be enabled , but only anonymousper project needs to be blessed
Plain SSH serverAllows good securityNot internet friendly port, requires account creation per user on server Uses filepermissionsper project inited
SSH server git-shellEnhancement of Plain SSH Server scenarioNot internet friendly port, requires account creation per user on serverUses filepermissionsper project inited
GitosisAdds good remote management of users and repositories, only requires one system accountNot internet friendly portUses gitosis-config fileNo server initalisation, only config is needed
Apache httpFalls back to standard apache config, only requires one system account, internet friendlySlightly overheadUses htpasswdper project inited
Apache http + gitwebFalls back to standard apache config, only requires one system account, internet friendly, adds nice view of repository Slightly overhead, read-only accessUses htpasswdper project inited
githubInternet accessible, easy to use webinterfaceHosted externallyManaging sshkeysWeb interface

http://www.kernel.org/pub/software/scm/git/docs/user-manual.html#setting-up-a-public-repository




Preparation:Git enable a simple project-X

Let's say you have project called project-X that you have been working on, on your local machine. It contains one file called README.
To enable git on this project you can:
$ cd $HOME/project-X


# git init enables the git repository
$ git init


# You add the readme file to the repository
$ git add README


# You commit your changed to your local repository
$ git commit -a -m 'Added README'

this will give you a .git directory in the project-X directory , ready to use in this tutorial


Share it over a file share

Instead of having people reference your local repository, you can put your repository on a file share.
Let's say we have share path /share/git/ where we want to put our project-X

Preparing the repository
# First we navigate to the repository place and will create a new project-X dir
$ cd /share/git
$ mkdir project-X	
$ cd project-X


# now we initialize this directory
# but instead of using git init, we use  git --bare init
# "A short aside about what git means by bare: A default git repository assumes that you will be using it as your working directory, 
# so git stores the actual bare repository files in a .git directory alongside all the project files. Remote repositories don't need 
# copies of the files on the filesystem unlike working copies, all they need are the deltas and binary what-nots of the repository itself. This is what "bare" means to git. Just the repository itself."
$ git --bare init

Pushing your local repository to the shared repository
# First go to your local repository
$ cd $HOME/project-X
# Then make the link to the shared repository
$ git remote add origin file:///share/git/project-X


# We push to the remote repository
$ git push origin master

Controlling access
As we are dealing with a fileshare, permissions are handled by filesystem permissions. You could create two groups: project-X-read, project-X-write. To set these different group permissions you could use:


Accessing the repository
# Another user can now clone the repository using:
$ git clone file:///share/git/project-X
# Change something
$ ....
# Commit the changes
$ git commit -a
# Push the changes to the central repository
$ git push

Share using git-daemon

Installing git-daemon
Git includes a simple daemon that you use to share your project using the git protocol.
On Centos,Redhat this seems to required another package:
$ sudo yum install git-daemon
More detail can be found http://www.kernel.org/pub/software/scm/git/docs/git-daemon.html
# Start the git daemon
$ git daemon
This creates a network listener on the GIT port. It allows by default read access to git projects.

Prepare the repository
To allow git-daemon to read your project you have to add the file .git/git-daemon-export-ok to your project.

Controlling access
Git daemon has no notion of users. By default is allows read to everybody, if you want to give write access, then you have to enable it to everybody anonymously.
This is what makes is less suitable for controlling access.
If you try to update you receive the following erors:
# On the local side
$ git push git://localhost/...
Initialized empty Git repository in ....
localhost[0: ::1]: errno=Connection refused
localhost[0: fe80::1]: errno=Connection refused
fatal: The remote end hung up unexpectedly


# On the git daemon side
Error on daemon side:
 [9027] 'receive-pack': service not enabled for git://localhost/...
..



Accessing the repository
$ git clone git:localhost/<i>your-path</i>/project-X

Share using ssh server

Sharing over ssh is similar to sharing it over a filesystem.
Preparing the repository
# On the SSH server we assume var/git as the central repository place and will create a new project-X dir
$ cd /var/git
$ mkdir project-X       
$ cd project-X


# now we initialize this directory
# but instead of using git init, we use  git --bare init
# "A short aside about what git means by bare: A default git repository assumes that you will be using it as your working directory
# , so git stores the actual bare repository files in a .git directory alongside all the project files. Remote repositories don't need copies of the
 files on the filesystem unlike working copies, all they need are the deltas and binary what-nots of the repository itself. This is what "bare" means to git. Just the repository itself."
$ git --bare init

Pushing your local repository to the shared repository
# First go to your local repository
$ cd $HOME/project-X
# Then make the link to the shared repository
$ git remote add origin ssh://user@gitserver/var/git/project-X


# We push to the remote repository
$ git push origin master

Controlling access
To have access, all users must have an account on the ssh server. So that means user-add for each user.
Permissions are handled by filesystem permissions. You could create two groups: project-X-read, project-X-write.
To set these different group permissions you could use:
Accessing the repository
# Another user can now clone the repository using:
$ git clone file:///share/git/project-X
# Change something
$ ....
# Commit the changes
$ git commit -a
# Push the changes to the central repository
$ git push

Share using git-daemon

Starting the daemin
Git includes a simple daemon that you use to share your project using the git protocol.
More detail can be found http://www.kernel.org/pub/software/scm/git/docs/git-daemon.html
# Start the git daemon
$ git daemon
This creates a network listener on the GIT port. It allows by default read access to git projects.
Prepare the repository
To allow git-daemon to read your project you have to add the file .git/git-daemon-export-ok to your project.


Controlling access
Git daemon has no notion of users. By default is allows read to everybody, if you want to give write access, then you have to enable it to everybody anonymously.
This is what makes is less suitable for controlling access.
If you try to update you receive the following erors:
# On the local side
$ git push git://localhost/...
Initialized empty Git repository in ....
localhost[0: ::1]: errno=Connection refused
localhost[0: fe80::1]: errno=Connection refused
fatal: The remote end hung up unexpectedly


# On the git daemon side
Error on daemon side:
 [9027] 'receive-pack': service not enabled for git://localhost/...
..



Accessing the repository
$ git clone git:localhost/your-path/project-X

Share using ssh server

Sharing over ssh is similar to sharing it over a filesystem.
Preparing the repository
# On the SSH server we assume var/git as the central repository place and will create a new project-X dir
$ cd /var/git
$ mkdir project-X       
$ cd project-X


# now we initialize this directory
# but instead of using git init, we use  git --bare init
# "A short aside about what git means by bare: A default git repository assumes that you will be using it as your working directory
# , so git stores the actual bare repository files in a .git directory alongside all the project files. Remote repositories don't need copies of the
 files on the filesystem unlike working copies, all they need are the deltas and binary what-nots of the repository itself. This is what "bare" means to git. Just the repository itself."
$ git --bare init

Pushing your local repository to the shared repository
# First go to your local repository
$ cd $HOME/project-X
# Then make the link to the shared repository
$ git remote add origin ssh://user@gitserver/var/git/project-X


# We push to the remote repository
$ git push origin master

Controlling access
To have access, all users must have an account on the ssh server. So that means user-add for each user.
Permissions are handled by filesystem permissions. You could create two groups: project-X-read, project-X-write.
To set these different group permissions you would use the set To set these different group permissions you would use the setfacl command on linux or the chmod +a on MacOSX
You could off course have multiple keys to one account, but this would not allow you to differentiate between different rights

Accessing the repository
# Another user can now clone the repository using:
$ git clone ssh://user@gitserver/var/git/project-X
# Change something
$ ....
# Commit the changes
$ git commit -a
# Push the changes to the central repository
$ git push

Share using git-shell

Similar to the SSH Repository setup. The only difference is that instead of giving each user a normal shell f.i. '/bin/login', you set '/usr/bin/git-shell' as a more restrictive shell, that only allows the git commands.
Access it in any other way will result in fun message:
 fatal: What do you think I am? A shell?

Share using gitosis

Gitosis allows you to have on account on the ssh server and have all commits pass through that account.

Installing gitosis
# On Centos,Redhat to install gitosis
$ sudo yum install gitosis


# This creates /var/lib/gitosis
# This creates a user gitosis with as default homedir /var/lib/gitosis
$ grep gitosis /etc/passwd
gitosis:x:100:101:git repository hosting:/var/lib/gitosis:/bin/sh

Initialize gitosis
To have gitosis initialize itself, it needs to have public key (id_dsa.pub) of a user that will be the first administrator
# To initialize the gitosis account $ sudo -H -u gitosis gitosis-init < /tmp/id_dsa.pub Initialized empty Git repository in ./ Reinitialized existing Git repository in ./ **
This results in the following structure created under:
.
|-- .gitosis.conf -
/var/lib/gitosis/repositories/gitosis-admin.git/gitosis.conf
|-- .ssh
|   `-- authorized_keys
|-- gitosis
|   `-- projects.list
`-- repositories
    `-- gitosis-admin.git
        |-- HEAD
        |-- branches
        |-- config
        |-- description
        |-- gitosis-export
        |   `-- keydir
        |       `-- myself@my-Portable.local.pub
        |-- gitosis.conf
        |-- hooks
        |   |-- applypatch-msg
        |   |-- commit-msg
        |   |-- post-commit
        |   |-- post-receive
        |   |-- post-update
        |   |-- pre-applypatch
        |   |-- pre-commit
        |   |-- pre-rebase
        |   |-- prepare-commit-msg
        |   `-- update
        |-- index
        |-- info
        |   `-- exclude
        |-- objects
        |   |-- info
        |   `-- pack
        |       |-- pack-82e64648d14e24258fa7c569100c6805edfc314c.idx
        |       `-- pack-82e64648d14e24258fa7c569100c6805edfc314c.pack
        `-- refs
            |-- heads
            |   `-- master
            `-- tags
15 directories, 23 files

Apart from this directory structures, gitosis-init also add the key to the authorized_keys of the gitosis user.
It does not allow interactive login, and restricts the commands to the gitosis-server command.
$ sudo -H -u gitosis cat $HOME/.ssh/authorized_keys
### autogenerated by gitosis, DO NOT EDIT
command="gitosis-serve myself@my-Portable.local",no-port-forwarding ,no-X11-forwarding,no-agent-forwarding,no-pty ssh-dss AAAAB3NzaC1kc3MAAACBAMAgxc ....

To make gitosis handle updates correctly we have to make the post-update executable
sudo chmod 755 /var/lib/gitosis/repositories/gitosis-admin.git/hooks/post-update

Controlling Access
Before you can push your local repository to gitosis, you have to setup the correct permissions
# Checkout the gitosis-admin repository (using the public key you used to gitosis-init
$ mkdir gitosis
$ git clone ssh://gitosis@gitosis-server:gitosis-admin.git
# And two groups to the gitosis-conf
   [group project-X-read]
   members = usera, userb
   readable = project-X


   [group project-X-write]
   members = userc, userd
   writable = project-X


# Copy the public keys of usera,userb, userc, userd into the  keydir, with the correct names
        |-- gitosis-export
        |   `-- keydir
        |       `-- myself@my-Portable.local.pub
        |           usersa.pub
        |           usersb.pub
        |           usersc.pub
        |           usersd.pub
        |-- gitosis.conf
# Add the keys
$ git add gitosis-export/keydir/*.pub
$ git commit -a -m 'Added users'
# push them back to the repository
$ git push
Pushing your local repo to to the gitosis server
$ cd project-X
$ git remote add origin gitosis@gitosis-server:project-X.git
$ git push origin master:refs/heads/master
This will create a directory project-X under /var/lib/gitosis/repositories/

Accessing the repository Nothing special here. If your key was added to the gitosis-admin repository, then you can acces it with.
$ git clone gitosis@gitosis-server:project-X.git

Share over apache http

Preparing the repository
# On the web server we assume var/git as the central repository place and will create a new project-X dir
$ cd /var/git
$ mkdir project-X
$ cd project-X


# now we initialize this directory
# but instead of using git init, we use  git --bare init
# "A short aside about what git means by bare: A default git repository assumes that you will be using it as your working directory
# , so git stores the actual bare repository files in a .git directory alongside all the project files. Remote repositories don't need copies of the
 files on the filesystem unlike working copies, all they need are the deltas and binary what-nots of the repository itself. This is what "bare" means to git. Just the repository itself."
$ git --bare init
Now that we created the project directory we need to give apache access to it:
  • Be sure to set the correct permissions on the /var/git directory so that it can be read by the webuser. chown -R apache:apache /var/git/project-X
  • If you have selinux enabled: chcon -R -t httpd_sys_content_t /var/git/project-X
  • Enable the post-update hook: chmod +x /var/git/project-X/hooks/post-update

When you did not set the post commithook:
$ git clone http://git.yourdomain.com/project-X
Initialized empty Git repository in /Users/mydir/project-X/.git/
fatal: http://git.yourdomain.com/project-X.git/info/refs not found: did you run git update-server-info on the server?
Then you can need to run it manually the first time
$ cd /var/git/project-X
$ sudo -u apache git update-server-info

Preparing apache
This document assumes you have a basic apache setup. And you have virtual name server working. Most of it is standard acces to the directory.
To allow write access, we need to have Webdav enabled.
http://www.kernel.org/pub/software/scm/git/docs/howto/setup-git-server-over-http.txt
<VirtualHost some-ip:80>
	Servername git.mydomain.com
	DocumentRoot /var/git
	<Directory "/var/git">
	   <b>DAV On</b>
	   Options +Indexes +FollowSymLinks 
		AllowOverride None
		Allow from all
		Order allow,deny
	</Directory>
</VirtualHost>
This will add a virtual server that has access to the /var/git directory using simple browsing.
In case you are experiencing trouble:
  • Remove the restrictions from welcome.conf: in this default file, it disables the index option. Error: ...
  • Note the + before the options, to allow the merge of permissions
Controlling access
We control access to your repository using apache groupfiles and password files
<VirtualHost YOUR-IP:80>
	ServerName git.yourdomain.com
	DocumentRoot /var/git
	<Directory /var/git/>
		DAV On
		Options ExecCGI FollowSymLinks Indexes
		# Deny everyything here
		<b>Deny from all
		AuthType Basic
		AuthName "git repository"
		AuthUserFile /var/git/htpasswd.git
		AuthGroupFile /var/git/htgroup.git</b>
	</Directory>


	<b><Directory /var/git/project-X>
		Allow from all
		Order allow,deny
		<Limit GET>
			Require group project-X-read
		</Limit>
		<Limit GET PUT POST DELETE PROPPATCH MKCOL COPY MOVE LOCK UNLOCK>
			Require group project-X-write
		</Limit>
		</Directory></b>
</VirtualHost>



Accessing the repository
Git uses curl to access http repositories. Because our repository is now protected we need to create an entry in our $HOME/.netrc file
$ cat $HOME/.netrc
machine git.yourdomain.com
login reader
password reader
Now you should be able to clone project-X
$ git clone http://git.mydomain.com/project-X
Possible Errors
Trying update
error: Cannot access URL http://git.yourdomain.com/project-X/, return code 22
error: failed to push some refs to 'http://git.yourdomain.com/project-X'
If there's something wrong with the permissions. Maybe you don't have webdav enabled, the user is in the wrong group, or filepermissions are not set correctly. Check your apache error_log

$ git clone http://git.yourdomain.com/project-X
$ git push
fatal: git-push is not available for http/https repository when not compiled with USE_CURL_MULTIerror: failed to push some refs to 'http://git.yourdomain.com/project-X'
Either you compile your git client with the correct curl options. Or you can alternatively mount the remote repository as webdav share and access it via file:// references. See http://wiki.dreamhost.com/Talk:Git
The following happens if your curl library  was not compiled with the correct options to post to 
<a href="http://kerneltrap.org/mailarchive/git/2008/1/13/564431">http://kerneltrap.org/mailarchive/git/2008/1/13/564431</a>
After a bit of research it seems that CURL compilation into GIT was not entirely successful for the Git on Mac OS X. As I was already mounting the git repository via WebDAV you an push and pull to your locally mounted repository by replacing the http URL with the path to your mounted WebDAV (/Volumes//). This worked pretty well for me and works well with Dreamhost with very little configuration.


$ git push
Fetching remote heads...
   refs/
   refs/tags/
   refs/heads/
   No refs in common and none specified; doing nothing.

This happens when you cloned an empty repository: Because you cloned an empty repository , you need to specify the origin and master , after this first push, you can use git push as usual
$ git push origin master

Sharing repository using apache and gitweb

By default, the apache does not provide a friendly view of the repository. To allow a better web interface, you can use git-web. This is a python script.
Before you take this option, be sure to setup the simple apache http first. This allows you to better check where the problem lies.
Installing gitweb
# Easy gitweb installation on Centos,Redhat, which installs itself in /var/www/git by default
$ yum install gitweb
# You have to manually create a /etc/gitweb.conf file
   $GIT= "/usr/bin/git";
   $projectroot = "/var/git/";

Gitweb provides a nice interface but if you would have it take control of your web rendering, git would be confused.
To solve this we allow different ways to access the same repository:
  • Using http://git.yourdomain.com/project-X.git : using AliasMatch we can map this to the traditional index rendering
  • With http://git.yourdomain.com/project-X : this will show the gitweb interface
<VirtualHost YOUR-IP:80>
	<b>SetEnv GITWEB_CONFIG /etc/gitweb.conf</b>
	ServerName git.yourdomain.com
	<b>DocumentRoot /var/www/git</b>
	<b>AliasMatch ^(/.*?)(\.git)(/.*)? /var/git$1$3</b>
	<Directory /var/git/>
		DAV On
		Options ExecCGI FollowSymLinks Indexes
		# Deny everyything here
		Deny from all
		AuthType Basic
		AuthName "git repository"
		AuthUserFile /var/git/htpasswd.git
		AuthGroupFile /var/git/htgroup.git
	</Directory>


	<Directory /var/git/project-X>
		Allow from all
		Order allow,deny
		<Limit GET>
			Require group project-X-read
		</Limit>
		<Limit GET PUT POST DELETE PROPPATCH MKCOL COPY MOVE LOCK UNLOCK>
			Require group project-X-write
		</Limit>
		</Directory>


	<b>
	<Directory /var/www/git>
		Options ExecCGI FollowSymLinks Indexes
		Allow from all
		Order allow,deny
		AddHandler cgi-script cgi
		DirectoryIndex gitweb.cgi
		RewriteEngine on
		RewriteCond %{REQUEST_FILENAME} !-f
		RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
	</Directory>
	</b>
</VirtualHost>

Controlling Access
Similar to the Apache http Repository
Publish your local repository
Similar to the Apache http Repository but now you need to use the .git extension
$ git clone http://git.yourdomain.com/project-X.git
A very common error
$ git push
No refs in common and none specified; doing nothing.
Perhaps you should specify a branch such as 'master'.
fatal: The remote end hung up unexpectedly
error: failed to push some refs to 'file:///share/projects/project-X'
If you have gone through the steps of git remote add origin ...., you might think that git would be smart enough to now that a git push needs to go to your origin.
The first time after adding this, you can't use the default git push, but you have to specify the full path
	$ git push origin master
Another way to solve this is to add the following lines to your .git/config file
$ vi .git/config
        [branch "master"]
        remote = origin
        merge = refs/heads/master
Or stil another way is to specify the option while you add the remote http://swedishcampground.com/adding-a-remote-to-existing-git-repo
        
$ git remote add --track master origin file:///share/projects/project-X
References
Published at DZone with permission of Patrick Debois, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Mario Guenterberg replied on Fri, 2011/04/15 - 10:40am

Good overview!

Vincent Dutat replied on Fri, 2011/04/15 - 12:45pm

what about https://gitent-scm.com ?

Paul Templin Ashford replied on Fri, 2011/04/15 - 2:25pm

Missed one:
Using SSH, you can have multiple users "share" the same ssh account, but access it via different ssh-keys, and only give them git-shell access so that they don't get actuall shell access to the remote server.

 

http://www.site5.com/support/kb/creating-a-new-git-repository/

 

You talk about git-shell, but you also say it requires user creation on the server, which is not true.

 

Winter Morning replied on Fri, 2011/04/15 - 6:11pm

good summary. 5 stars .

Łukasz Rżanek replied on Sun, 2011/04/17 - 8:18am

Gitolite is a very nice alternative for administration of Git repositories, keys and such.

James Moger replied on Thu, 2012/01/12 - 11:55am

As an alternative to httpd+gitweb you might consider Gitblit, an Apache-licensed, pure Java Git server based on JGit.

Paul Russel replied on Sun, 2012/06/10 - 8:08am

great article! a great comparison table to make a decision.
it helped me to setup git with http (for fast read-write repositories).
thanks!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.