In this tutorial, we want to install GIT on a debian server, once installed coworkers must be allowed to access our GIT repositories via Apache or SSH. The Coworkers can be on Mac OS X, Linux or Windows.
I tested every written lines of this page with:
- Debian 6 (Squeeze) as a Server and a Client
- Mac OS 10.6 (Snow Leopard) as a Client
- Windows 7 as a Client
Install locally, on the Server
Git cannot create the remote repository, it only operates on existing ones, so we need to create an empty repository locally on the server.
GIT itself
As you certainly guessed, we start by installing Git with Aptitude
apt-get install git
For each commit we make while using GIT, a name and email are necessary, let's introduce ourself
git config --global user.name "chris"
git config --global user.email chris@youremail.com
To check if the user as been recorded
git config -l
It will show
user.name=chris
user.email=chris@youremail.com
A first project
Let's create a first project (test001)
cd /var/cache/git
mkdir test001
cd test001
NB: you must not name your folder test001.git, otherwise the apache config won't work. Name it test001 only For the moment it's a normal directory, let's make it a GIT directory :
git --bare init
NB: if we didn't want to use it remotely we could have created a simple local repository with the following command
cd /path/myfolder
git init
And later we could still create a copy, ready for a remote copy into the server folder /var/cache/git
git --bare clone /path/myfolder /path/newfolder
The new folder would be usable only as remote repository in order to clone from it, and then pull and push, not as a working repository dedicated to the commit.
Now let's create a working folder in my home folder
mkdir ~/projects/
cd ~/projects
git clone /var/cache/git/test001 test001.git
Let's change the project description in this file : ~/projects/test001.git/.git/description
My first GIT project - Hello World
Let's exclude some annoying files from the commits, add in this file: ~/projects/test001.git/.gitignore
cd ~/projects/test001.git
echo .DS_Store >> .gitignore
echo Thumb.db >>.gitignore
git add .gitignore
git commit -a -m "gitignore configured"
NB: it's annoying, but .git/info/exclude and description can be changed only locally and will never be sent to or recovered from the server.
NB2: .DS_Store on Mac, Thumb.db on Windows, keep thumbnails of your photos folders and should never be backed up.
Now the working folder is ready, let's create the first file ~/projects/test001.git/test001.php
<?php
echo "hello world!";
?>
For now, GIT still does not recognize the file test001.php. We need to explicitly add the file to tell GIT to start tracking it
cd ~/projects/test001.git
git add test001.php
Status will show you that the file is still not commited
git status
git commit -a -m "My very first commit"
(-a is for ALL FILES, -m for Message associated to this commit)
To see history
git log
NB: when we cloned the repository, git stored its location in the repository configuration, and that location is used for pulls. But you can change it in ~/projects/test001/.git/config
To push your commit to the server this simple command will do it
git push origin master
NB: if you are not doing this test with root, you won't be allowed to push yet, the security is setup later on this page
And if you want to download the changes made by somebody else
git pull
In conclusion for this section, the folder ~/projects/test001 allowed us to verify that everything works perfectly locally, without SSH or HTTP connexion. If your PUSH and PULL works properly here, you can continue to the next section.
Share it with Apache (HTTP)
While we will allow the GIT clients to access our repository via HTTP(S), we will also install GITWEB to get a Web Interface directly browsable.
Install of the Web Interface
if Apache is not installed yet
apt-get install apache2
we need to install some basic files which will enable us to publish our repositories on Apache
apt-get install gitweb
It creates the following folder
cd /usr/share/gitweb
The location for publishable repository is defined by the variable $projectroot from /etc/gitweb.conf, which happens to be
/var/cache/git
With a standard Apache install, because of the file /etc/apache2/conf.d/gitweb my repository is now viewable at
http://server.domaine.com/gitweb/
On an existant Apache, this config is an issue, so I edit this file and comment the first line
#Alias /gitweb /usr/share/gitweb
<Directory /usr/share/gitweb>
Options FollowSymLinks +ExecCGI
AddHandler cgi-script .cgi
</Directory>
Let's reload apache
/etc/init.d/apache2 reload
Activate Read/Write Access
Before early 2010, WebDAV was the only solution in order to commit to the server via HTTP. It was a very slow process. Now WebDAV is useless, the SMART HTTP method allows the use of POST, with only one file containing everything (much faster)
We will use the rewriting capabilities of Apache
a2enmod rewrite
But if you want to allow your client to PUSH (their COMMIT) into your repositories, you will have to change the folder access in order to allow www-data to write
chown -R root.www-data /var/cache/git/test001
chmod -R g+w /var/cache/git/test001
#The following commented lines are for a HTTPS server instead of unsecured HTTP
#<IfModule mod_ssl.c>
#<VirtualHost _default_:443>
#SSLEngine on
#SSLCertificateFile /etc/ssl/private/server.crt
#SSLCertificateKeyFile /etc/ssl/private/server.key
#SSLCertificateChainFile /etc/ssl/private/bundle.crt
#if you uncomment the previous lines, also uncomment the last line of the file and comment the following one:
<VirtualHost _default_:80>
SetEnv GITWEB_CONFIG /etc/gitweb.conf
SetEnv GIT_PROJECT_ROOT /var/cache/git
SetEnv GIT_HTTP_EXPORT_ALL
ServerName git.company.com
DocumentRoot /usr/share/gitweb
AliasMatch ^/(.*/objects/[0-9a-f]{2}/[0-9a-f]{38})$ /var/cache/git/$1
AliasMatch ^/(.*/objects/pack/pack-[0-9a-f]{40}.(pack|idx))$ /var/cache/git/$1
ScriptAliasMatch \
"(?x)^/(.*?)\.git/(HEAD | \
info/refs | \
objects/info/[^/]+ | \
git-(upload|receive)-pack)$" \
/usr/lib/git-core/git-http-backend/$1/$2
#ScriptAlias / /usr/share/gitweb/
<LocationMatch "^/.*?\.git/.*?$">
AuthType Basic
AuthName "git repository"
AuthUserFile /var/cache/git/htpasswd.git
AuthGroupFile /var/cache/git/htgroup.git
Require group cloners
</LocationMatch>
<LocationMatch "^/.*?\.git/git-receive-pack$">
AuthType Basic
AuthName "git repository"
AuthUserFile /var/cache/git/htpasswd.git
AuthGroupFile /var/cache/git/htgroup.git
Require group commiters
</LocationMatch>
<LocationMatch "^/test001\.git/.*?$">
AuthType Basic
AuthName "git repository"
AuthUserFile /var/cache/git/htpasswd.git
AuthGroupFile /var/cache/git/htgroup.git
Require group test001-read
</LocationMatch>
<LocationMatch "^/test001\.git/git-receive-pack$">
AuthType Basic
AuthName "git repository"
AuthUserFile /var/cache/git/htpasswd.git
AuthGroupFile /var/cache/git/htgroup.git
Require group test001-write
</LocationMatch>
<Directory /usr/share/gitweb/>
Options ExecCGI FollowSymLinks Indexes
Allow from all
Order allow,deny
AddHandler cgi-script cgi
DirectoryIndex index.cgi
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.* /index.cgi/$0 [L,PT]
</Directory>
</VirtualHost>
#</IfModule>
And enable this site
cd /etc/apache2/sites-enabled/
ln -s ../sites-available/git 99-git
Restart apache
/etc/init.d/apache2 restart
Create the password file with a first user (chris) and choose a password for him
htpasswd -c /var/cache/git/htpasswd.git chris
If you want to add a second user
htpasswd /var/cache/git/htpasswd.git user2
Then create the following groups by creating the file /var/cache/git/htgroup.git
vi /var/cache/git/htgroup.git
test001-read:chris user2 user3
test001-write:chris user2
commiters:chris
cloners:chris user2 user5
NB: cloners/commiters will have access to every repository except test001 which have its own groups test001-read/test001-write
NB2: each write access must be allowed in couple with a read access (Write access does not work without read access)
The folder must be writable by apache. An easy way to keep writing rights for CHRIS and Apache is:
chown -R chris.www-data /var/cache/git/test001
chmod -R 770 /var/cache/git/test001
It should work, from now you have:
HTTP Access from clients on Linux or Mac OS X
If you need to commit, define username and email
git config --global user.name "chris"
git config --global user.email chris@youremail.com
To clone the Repository on your computer
git clone http://git.company.com/test001.git/
Or you can avoid to type your login each time
git clone http://chris@git.company.com/test001.git/
Even your password if you really want, during your tests
git clone http://chris:mypasswd@git.company.com/test001.git/
NB: the login used in HTTP is not linked to the one used by "git config --global user.name "chris""
Each user should locally create the file ~/.gitconfig with the following content
[user]
name = chris
email = chris@youremail.com
[core]
excludesfile = /Users/chris/.gitignore
[http]
#sslCAInfo=/Users/chris/.git.company.com.cer
#sslVerify=no
And add .DS_Store and Thumb.db to the ignore list
echo ".DS_Store" >> ~/.gitignore
echo "Thumb.db" >> ~/.gitignore
NB: for using HTTPS with a self-signed certificate, uncomment sslCAInfo which lets you target a PEM certificate. I exported it from Firefox
- Accept the certificat in firefox
- Export in PEM format in “Preferences”->”Advanced”->”View Certificates”
- name the file ~/.git.company.com.cer
Or you can uncomment sslVeriry which will avoid to check the certificate
HTTP Access from clients on Windows
If you like the command line, you can install only GIT for Windows and follow the Linux/Mac previous bloc, but I doubt that most Windows user would enjoy, TortoiseGit seems the best graphical choice for them.
TortoiseGit works with HTTP and SSH. So we must consider a proper installation of SSH in the same time, even if you are not sure to use it one day.
Install Git for Windows (msysgit) and then TortoiseGit
If you don't have Putty, download its suite first to enable the Use of SSH
Their install is very easy, when installing msysgit, I chose:
- Run Git from the Windows Command Prompt
- Use (Tortoise)Plink (not OpenSSH)
- my Plink was in c:\program files\putty\plink.exe (I installed Putty and all its tools months ago)
- Checkout Windows-style
After that, idem for tortoisegit: I didn't chose OpenSSH either
Now TortoiseGit is as easy as TortoiseSVN
Anyway, I could not make it work with SSH, while for HTTP is worked fine :)
Share it with SSH
HTTP seems easier, but SSH can be a good choice in some case. Personnaly I would use SSH for me and HTTP for external contractor since I can avoid to create a user account on linux.
I assume that things are set up so that you can ssh to your server without having to type a password, which means most of the time that :
- your public key is on the server in ~/.ssh/authorized_keys
- you are running ssh-agent locally (already started on Mac OS X)
- you use locally ~/.ssh/config as following if you have a special port (22222) and a different login (chris)
an example of ~/.ssh/config could be the following
Host *.company.com
port 22222
protocol 2
PubKeyAuthentication yes
PasswordAuthentication no
User chris
Lets remember that the location of the repository is:
/var/cache/git/test001/
And the Server name is
git.company.com
This user (here Chris) must have read access on this folder, but it's certainly already like that. Otherwise the next section is about Security.
SSH clients on Mac OS X or Linux
NB: git is automatically installed by XCode on Mac OS X
just use the following command on Mac or Linux
git clone ssh://git.company.com/var/cache/git/test001
Then, after some
git commit -a -m "blablabla"
you can push the update to the server with
git push
or the first time with
git push origin master
It's as easy as that with ssh on Linux/Mac OS X.
SSH Access from clients on Windows
The tools used for SSH are the same than HTTP, look at this previous paragraph:
Anyway, I could not make it work with SSH, meanwhile with HTTP is worked fine.
Security
HTTP
We already configured Apache to take care of the security
the users must be added in /var/cache/git/htpasswd.git
htpasswd /var/cache/git/htpasswd.git user4
the groups are managed in /var/cache/git/htgroup.git
essai001-read:user1 user4 user2
essai001-write:user1 user4
project002-read:user3 user4 user1 user2
project003-write:user3 user4
NB: adding a user or a group can be done without restarting Apache
Every new repository will be shared via HTTP to the groups commiters and cloners, if you want to replace this groups you must add in /etc/apache2/sites-available/git
<LocationMatch "^/project002\.git/.*?$">
AuthType Basic
AuthName "git repository"
AuthUserFile /var/cache/git/htpasswd.git
AuthGroupFile /var/cache/git/htgroup.git
Require group project002-read
</LocationMatch>
<LocationMatch "^/project002\.git/git-receive-pack$">
AuthType Basic
AuthName "git repository"
AuthUserFile /var/cache/git/htpasswd.git
AuthGroupFile /var/cache/git/htgroup.git
Require group project002-write
</LocationMatch>
And reload apache
/etc/init.d/apache2 reload
www-data must be allowed to access to this repository
chown -R chris.www-data /var/cache/git/project002
chmod -R 770 /var/cache/git/project002
If you don't allow www-data to access to the repository, it won't be listed in gitweb either.
SSH
One of the nice design decisions made by the Git developers is that access control should not be the responsibility of the SCM tool. This means that the tool is free to concentrate on doing its job (tracking content), while leaving questions of authentication and authorization in the hands of other tools which are much more flexible and better suited to the job: tools like SSH, filesystem permissions, ACLs, and a host of other mechanisms.
Let's see 2 possibilities
Simple access control
We can define 2 kinds of users :
- public: read only on open source project
- developer: read/write on every project
In this case we can control the access with SSH Let's create a user GIT
adduser git
And give him all the ownership of the repositories
chmod -R git /var/cache/git
Or for staying apache capable
chmod -R git.www-data /var/cache/git
Add each developer's public key in /home/git/.ssh/authorized_keys
I didn't test this method, but its simplicity seems very interesting. If all your team must access to all the projects.
Each user would have to connect as GIT
git clone ssh://git@server/var/cache/git/essai001
And have their SSH private key working
Complex access control
For maximum control you can use ACLs and apply them selectively to specific repos
If your filesystem is ext2 or ext3, the filesystem will needto be mounted with the "acl" option. This can be done by editing /etc/fstab and changing something like:
/dev/md1 / ext3 defaults 1 1
to:
/dev/md1 / ext3 rw,acl 1 1
in my case
/dev/xvda2 / ext3 noatime,nodiratime,errors=remount-ro 0 1
became
/dev/xvda2 / ext3 noatime,nodiratime,errors=remount-ro,acl 0 1
Install acl on Debian
apt-get install acl
you need to remount the filesystem
mount -v -o remount /
We create a group of users allowed to read/write our repository Chris will be member of this group
groupadd git_test001_write
addgroup chris git_test001_write
groupadd git_test001_read
addgroup user2 git_test001_read
Give access only to these groups
chmod -R 700 /var/cache/git/test001
setfacl -R -m g:git_test001_write:rwX /var/cache/git/test001
find /var/cache/git/test001 -type d | xargs setfacl -R -m d:g:git_test001_write:rwX
setfacl -R -m g:git_test001_read:rX /var/cache/git/test001
find /var/cache/git/test001 -type d | xargs setfacl -R -m d:g:git_test001_read:rX
The first line close t The third line sets up the same permissions as a default ACL to be applied to any new files created in those directories. This access control is at the OS-level. Consequently there is no need to use git init --shared or otherwise set the core.sharedRepository config variable.
Explanation.
- -R: apply recursively
- -m: modify the ACL
- g:<group>: permissions for group <group>
- r: read access
- w: write access
- X: execute permissions, but only if the object is a directory, or already has execute permissions for some user
- d:<spec>: set a default ACL (defined by <spec>) to be applied to any new files or directories added to the directory in the future
If you need to check what the current rights are, use getfacl
getfacl /var/cache/git/test001
Apache need it's own ACL if you still want to share your project via HTTPS Read only:
addgroup www-data git_test001_read
Read and Write:
addgroup www-data git_test001_write
NB: Apache need to be restarted or reloaded for each group change, a unix user (like Windows) record its groups at the login and keep them until next login.
/etc/init.d/apache2 reload
Personnally I also use ALL ACCESS groups:
groupadd git_all_write
groupadd git_all_read
Then give access to main folder (read only anyway)
setfacl -R -m g:git_all_write:rX /var/cache/git
setfacl -R -m g:git_all_read:rX /var/cache/git
and give access to all current and futur repositories
find /var/cache/git -type d | xargs setfacl -R -m d:g:git_all_write:rwX
find /var/cache/git -type d | xargs setfacl -R -m d:g:git_all_read:rX
Transfert of my existing work
Here is how I transfer my existing local XCode repositories:
Git and XCode
Issues
Name of folder
if you name the folder /var/cache/git/project001.git instead of /var/cache/git/project001 the apache config won't work without changing the regular expressions and most of the apache configuration file
HTTP clone & push
I had the following message when I use "git clone" and I was still using WebDAV (useless nowaday) instead of the Smart Http mode
$git clone http://chris@git.company.com/test001.git/
Cloning into test001...
Password:
warning: You appear to have cloned an empty repository.
The solution was to remember to do that
cd /var/cache/git/test001
git update-server-info
cd hooks
mv post-update.sample post-update
HTTPS and self-signed SSL Certificate
$ git clone https://chris@git.company.com/essai001.git/
Cloning into essai001...
Password:
error: SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed while accessing https://chris@git.company.com/essai001.git/info/refs
fatal: HTTP request failed
I use self-signed certificate with Apache-SSL.
The solution is either:
- cancelling the certificate validation from GIT (Not really an option)
- indicating the certificate
Cancelling the validation is easy, add into ~/.gitconfig
[http]
sslVerify=no
Indicating the certificate is almost as easy, add into ~/.gitconfig
[http]
sslCAInfo=/Users/chris/.git.company.cer
NB: the certificate can be exported into PEM format if you use Firefox in “Preferences”->”Advanced”->”View Certificates”