Git服务器搭建与常见问题总结

1. Git数据传输协议

Git 支持的数据传输协议有下面这些

  • 本地传输
  • SSH 协议(最常见)
  • Git 协议
  • HTTP/HTTPS 协议(速度最慢)

本地传输、SSH 协议与 Git 协议在传输数据时都会尽可能对数据压缩,所以相对于 HTTP/HTTPS 协议这三个协议在传输文件时会快好多。本文配置 Git 服务器,使得它支持 SSH 与 Git 协议,并通过这两种协议 Clone 上面建立的远程仓库。


2. Git协议

为了使 Git 服务器支持 Git 协议,需要在 Git 服务器上安装 git-daemon。git-daemon 是 Git 自带的一项功能,它能够让所有人都能拥有读取仓库的权限。

安装 git-daemon

1
yum install git-daemon -y

修改 git-daemon 的 systemd 配置文件 /usr/lib/systemd/system/git@.service

1
vim /usr/lib/systemd/system/git@.service

内容如下,--base-path=/home/git/repositories对应为下面介绍的Gitosis路径

1
2
3
4
5
6
7
8
9
[Unit]
Description=Git Repositories Server Daemon
Documentation=man:git-daemon(1)

[Service]
User=git
ExecStart=-/usr/libexec/git-core/git-daemon --base-path=/home/git/repositories
--syslog --inetd --verbose
StandardInput=socket

启动 git-daemon

1
systemctl start git.socket

如果启用了防火墙,则需要开放 9418 端口


3. Gitosis

简介

把所有用户的公钥保存在 authorized_keys 文件的做法,当用户数量达到几百人的规模时,管理十分不利。每次改删用户都必须登录服务器,这种做法还缺少必要的权限管理 — 每个人都对所有项目拥有完整的读写权限。

Gitosis 就是一套用来管理 authorized_keys文件和实现简单连接限制的脚本。用来添加用户和设定权限的并非通过网页程序,是管理一个特殊的 Git 仓库。管理员只需要在这个特殊仓库内做好相应的设定,然后推送到服务器上,Gitosis 就会随之改变运行策略。

软件安装&初始化

以root用户安装gitosis,显示Finished processing dependencies for gitosis==0.2即表示成功

1
2
3
4
yum install python python-setuptools -y
git clone git://github.com/res0nat0r/gitosis.git
cd gitosis
python setup.py install

创建git用户

1
useradd -c 'git version manage' -m -d /home/git -s /bin/bash  git

切换到git用户,这里使用git用户来管理gitosis权限管理仓库,因此初始化ssh密钥。

1
2
3
su - git
ssh-keygen -t rsa
chmod 700 .ssh

在git用户目录下,使用命令gitosis-init < ~/.ssh/id_rsa.pub初始化gitosis,显示以下信息即表示成功

1
2
Initialized empty Git repository in /home/git/repositories/gitosis-admin.git/
Reinitialized existing Git repository in /home/git/repositories/gitosis-admin.git/

则在当前目录下生成了两个文件夹gitosisrepositories,其中repositories是存放软件仓库的地方。

为了安全,建议删除公钥

1
rm -rf /home/git/.ssh/id_rsa.pub

gitosis-admin管理

在git用户目录下,将/home/git/repositories/目录下的gitosis-admin项目clone到本地

1
2
cd ~
git clone git@localhost:gitosis-admin.git

clone的项目中,gitosis.conf保存的是关于git用户相关的设置,keydir保存的是用户公钥。

gitosis-admin管理员添加

修改gitosis.conf,添加test@localhost用户为gitosis-admin仓库的管理员

1
2
3
4
5
[gitosis]                                

[group gitosis-admin]
members = git@localhost test@localhost
writable = gitosis-admin

test账户的公钥,拷贝到~/gitosis-admin/keydir中,修改用书属主和属组为git,并重命名为`test@localhost.pub`,公钥的文件名需要和members参数对应的用户名一致!

完成上述修改后,将本地修改推送到仓库

1
2
3
git add .
git commit -m "add new Administrator"
git push origin master

这时候test账户下可以管理gitosis-admin仓库了!

增加及设置管理项目

查看git服务器已经上传密钥,`root@tokyo.pub`为已经上传的客户端生成的公钥。

1
2
ls keydir  
cat keydir/root@tokyo.pub

编辑项目管理配置文件,在文件尾增加以下内容

1
2
3
[group test]            # 具有写权限的组名称
writable = test # 该组可写的项目名称
members = user1@xyz user2@xyz #该组的成员(密钥用户名) 多个用户协同开发时,以空格分隔

如果要增加只读的组 参考如下

1
2
3
[group test-readnoly]          # 具有都权限的组名称 
readonly = test # 该组只读的项目名称
members = user3@xyz # 该组的成员

gitosis-admin/keydir中加入公钥`usr1@xyz.pub,user2@xyz.pub,项目成员的名字为与公钥文件名需要一样。例如user1@xyz对应的公钥为user1.xyz.pub`

提交修改,这样增加了一个test的git项目,并增加了一个user1@xzyuser2@xzy的可写权限成员,以及一个user3@xzy的只读权限成员。

1
2
3
git add .
git commit -m "add test repo"
git push

初始,增加及使用项目test

在客户端初始化,增加以及使用test项目

1
2
3
4
5
6
7
8
9
cd ~/repo  
mkdir test
cd test
git init
touch readme
git add .
git commit -a -m "init test-git"
git remote add origin git@qingdao:test.git
git push origin master

启用Git协议

使用密钥管理代码仓库可以保证私密性和安全性,但是有些情况下需要代码仓库的匿名读权限,可以通过git协议来实现

修改gitosis.conf,将项目test加入git协议

1
2
3
4
[repo test]
owner = user3 <user3@xyz>
description = test
daemon = yes

则可以通过git://<git服务器地址>/test访问该项目。


4. Gitweb

gitweb安装配置

安装gitweb以及代码高亮插件

1
2
yum install gitweb -y
yum install highlight -y

编辑gitweb配置文件/etc/gitweb.conf

  • projectroot:所有项目的路径
  • git_base_url_list:可以访问git服务器的url
  • projects_list:如果想让所有项目直接出现在gitweb页面之上,不添加变量projects_list;如果想通过Gitosis的配置文件控制哪些项目出现在gitweb页面之上,启用projects_list
  • feature{‘highlight’}{‘default’}:代码高亮
  • feature{‘avatar’}{‘default’} :添加用户头像
  • home_link_str: 主页名字
1
2
3
4
5
6
7
8
9
10
11
12
our $projectroot = "/home/git/repositories";

our @git_base_url_list = qw(ssh://192.168.1.151
git://192.168.1.151
ssh://172.18.42.10
git://172.18.42.10);

our $projects_list = "/home/git/gitosis/projects.list";

$feature{'highlight'}{'default'} = [1];
$feature{'avatar'}{'default'} = ['gravatar'];
$home_link_str = "Bread";

http服务安装配置

需要安装http服务器,这里使用的是apache

1
2
3
yum install -y httpd
systemctl start httpd
systemctl enable httpd

将http服务的apache用户加入到git用户组,并修改/home/git的权限,给予属组用户执行权限。否则http服务器无权限读取git目录!

1
2
usermod -a -G git apache 
chmod 750 /home/git

修改http服务配置文件,在apache配置文件夹/etc/httpd/conf.d中创建git.conf文件,内容如下。

Alias /git /var/www/git的路径为安装gitweb自动创建。AuthTypeRequire字段是关于访问验证,使用htpasswd工具创建验证文件/etc/httpd/.htpasswd。这样访问gitweb页面需要进行验证。

然后重启http服务

1
2
3
4
5
6
7
8
9
10
Alias /git /var/www/git
<Directory /var/www/git>
Options +ExecCGI +FollowSymLinks
AddHandler cgi-script .cgi
DirectoryIndex gitweb.cgi
AuthType Basic
AuthUserFile /etc/httpd/.htpasswd
AuthName "Sign In Here To Gain Access To the Site"
Require valid-user
</Directory>

Gitweb美化

下载并安装,不要将项目克隆到/root目录下,因为软链接的管理

1
2
3
4
cd /var/www/
git clone https://github.com/kogakure/gitweb-theme
cd gitweb-theme
./setup -t /var/www/git -vi --install

重启http服务

更多详情参考https://github.com/kogakure/gitweb-theme

Gitosis设置

在gitweb配置文件中启用了变量projects_list的情况下,如果想将某个项目在gitweb页面上显示,需要在Gitosis配置文件中添加

1
2
3
4
[repo test]
owner = user3 <user3@xyz>
description = test
gitweb = yes

则这里将test项目显示在gitweb页面之上了。


5. Git常用命令

远程仓库相关命令

  • 检出仓库: $ git clone git://github.com/jQuery/jquery.git

  • 查看远程仓库:$ git remote -v

  • 添加远程仓库:$ git remote add [name] [url]

  • 删除远程仓库:$ git remote rm [name]

  • 修改远程仓库:$ git remote set-url –push [name] [newUrl]

  • 拉取远程仓库:$ git pull [remoteName] [localBranchName]

  • 推送远程仓库:$ git push [remoteName] [localBranchName]

如果想把本地的某个分支test提交到远程仓库,并作为远程仓库的master分支,或者作为另外一个名叫test的分支,如下:

1
2
$git push origin test:master         // 提交本地test分支作为远程的master分支
$git push origin test:test // 提交本地test分支作为远程的test分支

分支(branch)操作相关命令

  • 查看本地分支:$ git branch

  • 查看远程分支:$ git branch -r

  • 创建本地分支:$ git branch [name] —-注意新分支创建后不会自动切换为当前分支

  • 切换分支:$ git checkout [name]

  • 创建新分支并立即切换到新分支:$ git checkout -b [name]

  • 删除分支:$ git branch -d [name] —-d选项只能删除已经参与了合并的分支,对于未有合并的分支是无法删除的。如果想强制删除一个分支,可以使用-D选项

  • 合并分支:$ git merge [name] —-将名称为[name]的分支与当前分支合并

  • 创建远程分支(本地分支push到远程):$ git push origin [name]

  • 删除远程分支:$ git push origin :heads/[name] 或 $ gitpush origin :[name]

创建空的分支:(执行命令之前记得先提交你当前分支的修改,否则会被强制删干净没得后悔)

$git symbolic-ref HEAD refs/heads/[name]
$rm .git/index
$git clean -fdx

版本(tag)操作相关命令

  • 查看版本:$ git tag

  • 创建版本:$ git tag [name]

  • 删除版本:$ git tag -d [name]

  • 查看远程版本:$ git tag -r

  • 创建远程版本(本地版本push到远程):$ git push origin [name]

  • 删除远程版本:$ git push origin :refs/tags/[name]

  • 合并远程仓库的tag到本地:$ git pull origin –tags

  • 上传本地tag到远程仓库:$ git push origin –tags

  • 创建带注释的tag:$ git tag -a [name] -m ‘yourMessage’

子模块(submodule)相关操作命令

  • 添加子模块:$ git submodule add [url] [path]
    如:$git submodule add git://github.com/soberh/ui-libs.git src/main/webapp/ui-libs

  • 初始化子模块:$ git submodule init —-只在首次检出仓库时运行一次就行

  • 更新子模块:$ git submodule update —-每次更新或切换分支后都需要运行一下

  • 删除子模块:(分4步走哦)
    1) $ git rm –cached [path]
    2) 编辑“.gitmodules”文件,将子模块的相关配置节点删除掉
    3) 编辑“ .git/config”文件,将子模块的相关配置节点删除掉
    4) 手动删除子模块残留的目录

忽略一些文件、文件夹不提交

在仓库根目录下创建名称为“.gitignore”的文件,写入不需要的文件夹名或文件,每个元素占一行即可


6. Git命令详解

  • git pull:从其他的版本库(既可以是远程的也可以是本地的)将代码更新到本地,例如:’git pull origin master’就是将origin这个版本库的代码更新到本地的master主枝,该功能类似于SVN的update

  • git add:是将当前更改或者新增的文件加入到Git的索引中,加入到Git的索引中就表示记入了版本历史中,这也是提交之前所需要执行的一步,例如’git add app/model/user.rb’就会增加app/model/user.rb文件到Git的索引中,该功能类似于SVN的add

  • git rm:从当前的工作空间中和索引中删除文件,例如’git rm app/model/user.rb’,该功能类似于SVN的rm、del

  • git commit:提交当前工作空间的修改内容,类似于SVN的commit命令,例如’git commit -m story #3, add user model’,提交的时候必须用-m来输入一条提交信息,该功能类似于SVN的commit

  • git push:将本地commit的代码更新到远程版本库中,例如’git push origin’就会将本地的代码更新到名为orgin的远程版本库中

  • git log:查看历史日志,该功能类似于SVN的log

  • git revert:还原一个版本的修改,必须提供一个具体的Git版本号,例如’git revert bbaf6fb5060b4875b18ff9ff637ce118256d6f20’,Git的版本号都是生成的一个哈希值

上面的命令几乎都是每个版本控制工具所公有的,下面就开始尝试一下Git独有的一些命令:

  • git branch:对分支的增、删、查等操作,例如’git branch new_branch’会从当前的工作版本创建一个叫做new_branch的新分支,’git branch -D new_branch’就会强制删除叫做new_branch的分支,’git branch’就会列出本地所有的分支

  • git checkout:Git的checkout有两个作用,其一是在不同的branch之间进行切换,例如’git checkout new_branch’就会切换到new_branch的分支上去;另一个功能是还原代码的作用,例如’git checkout app/model/user.rb’就会将user.rb文件从上一个已提交的版本中更新回来,未提交的内容全部会回滚

  • git rebase:用下图解释会比较清楚一些,rebase命令执行后,实际上是将分支点从C移到了G,这样分支也就具有了从C到G的功能

图4-1

  • git reset:将当前的工作目录完全回滚到指定的版本号,假设如图4-2,我们有A-G五次提交的版本,其中C的版本号是 bbaf6fb5060b4875b18ff9ff637ce118256d6f20,我们执行了’git reset bbaf6fb5060b4875b18ff9ff637ce118256d6f20’那么结果就只剩下了A-C三个提交的版本

    • git reset –soft 只撤销commit,保留working tree和index file。

    • git reset –hard 撤销commit、index file和working tree,即撤销销毁最近一次的commit

    • git reset –mixed 撤销commit和index file,保留working tree

    • git reset和git reset –mixed完全一样

    • git reset –用于删除登记在index file里的某个文件

图4-2

  • git stash:将当前未提交的工作存入Git工作栈中,时机成熟的时候再应用回来,这里暂时提一下这个命令的用法,后面在技巧篇会重点讲解

  • git config:利用这个命令可以新增、更改Git的各种设置,例如’git config branch.master.remote origin’就将master的远程版本库设置为别名叫做origin版本库,后面在技巧篇会利用这个命令个性化设置你的Git,为你打造独一无二的 Git

  • git tag:可以将某个具体的版本打上一个标签,这样你就不需要记忆复杂的版本号哈希值了,例如你可以使用’git tag revert_version bbaf6fb5060b4875b18ff9ff637ce118256d6f20’来标记这个被你还原的版本,那么以后你想查看该版本时,就可以使用 revert_version标签名,而不是哈希值了


7. Git服务器搭建排错

Untracked working tree file ‘external/broadcom/Android.mk’ would be overwritten by merge. Aborting

需要执行下面的命令才能修复:

1
2
3
git reset --hard HEAD    
git clean -f -d
git pull

Please, commit your changes or stash them before you can merge.

如果希望保留生产服务器上所做的改动,仅仅并入新配置项, 处理方法如下:

1
2
3
git stash
git pull
git stash pop

然后可以使用Git diff -w +文件名 来确认代码自动合并的情况.

反过来,如果希望用代码库中的文件完全覆盖本地工作版本. 方法如下:

1
2
git reset --hard
git pull

其中git reset是针对版本,如果想针对文件回退本地修改,使用

1
git checkout HEAD file/to/restore

does not appear to be a git repository

路径错误,可以分别尝试绝对路径或者相对路径

如果是把另一个服务器的纯仓库弄到服务器,也会出现这种情况。我的临时做法是把另一个服务器的内容clone到本地服务器,然后在本地服务器创建纯仓库,放到repository的路径下,就可以了。有关这个错误,我在这篇文章里尝试寻找原因:

http://blog.csdn.net/xzongyuan/article/details/9366873

ERROR:gitosis.serve.main:Repository read access denied

修改本地gitosis-admin的gitosis-conf后(如下),push到仓库中,还会遇到该问题

1
2
3
[group customer]
members = nexus b
readonly = box_4.2

原因1:gitosis.conf写错
gitosis.conf中的members与keydir中的用户名不一致,如gitosis中的members = foo@bar,但keydir中的公密名却叫foo.pub
解决
使keydir的名称与gitosis中members所指的名称一致。
改为members = foo 或 公密名称改为foo@bar.pub

参考
http://blog.csdn.net/lixinso/article/details/6526643

注意,中间如果遇到这样的错误,很可能是gitosis.conf配置的不对
ERROR:gitosis.serve.main:Repository read access denied
fatal: The remote end hung up unexpectedly

有可能是:

  • gitosis 中写的用户名,和keydir里面的key的名字没有完全对应上
  • 有的地方写错了,比如我把members写成了member

**原因2:地址错误:

虽然有时候,地址错误时,会提示did not apear to be a git repositry。但我也遇到这个错误,写错了相对路径,就会提示没有权限,因为gitosis.conf根本就没有这个文件的配置嘛。可以看看我的记录:

第一次,写错相对路径,自己不知道:

1
2
3
4
norton@norton-laptop:~/work$ git clone git@192.168.0.3:/repositories/gitosis-admin.git
Initialized empty Git repository in /home/norton/work/gitosis-admin/.git/
ERROR:gitosis.serve.main:Repository read access denied
fatal: The remote end hung up unexpectedly

第二次,故意写个不存在的路径

1
2
3
4
norton@norton-laptop:~/work$ git clone git@192.168.0.3:/repositories/gitosis-admin.git2
Initialized empty Git repository in /home/norton/work/gitosis-admin.git2/.git/
ERROR:gitosis.serve.main:Repository read access denied
fatal: The remote end hung up unexpectedly

第三次,写对相对路径。可见,相对路径的根目录是/home/git/repositories,记得不要多写了不必要的路径。提示一下,repositories是在初始化gitosis前就已经手动建立的,是一个软链接,链接到/home/repo。如果没做这一步,初始化的时候就会建立一个repositories文件夹,那么gitosis-admin这个仓库就会在这个实在的文件夹下,而不会通过软连接放到/home/repo中

1
2
3
4
5
6
norton@norton-laptop:~/work$ git clone git@192.168.0.3:gitosis-admin.git
Initialized empty Git repository in /home/norton/work/gitosis-admin/.git/
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 5 (delta 0), reused 5 (delta 0)
Receiving objects: 100% (5/5), done.

原因3:开错账户

有时候,头脑不清醒了,就会弄错账户,所以犯这个错,要思考下是不是搞错账户了,在主帐号admin中,不断地测试下载,而我的目的其实是用b的帐号测试下载。如配置如下(并没有给admin读取teamwork的权限,而我却一直在clone teamwork)

1
[gitosis]
1
2
3
4
5
6
7
[group gitosis-admin]
members = admin
writable = gitosis-admin test

[group RK_Download]
members = b nexus
readonly = teamwork box_4.2

测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
admin@admin:~/work/test$ git clone git@192.168.0.3:test.git
Initialized empty Git repository in /home/admin/work/test/test/.git/
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
admin@admin:~/work/test$ git clone git@192.168.0.3:teamwork.git
Initialized empty Git repository in /home/admin/work/test/teamwork/.git/
ERROR:gitosis.serve.main:Repository read access denied
fatal: The remote end hung up unexpectedly

admin@admin:~/work/test$ git clone git@192.168.0.3:teamwork.git
Initialized empty Git repository in /home/admin/work/test/teamwork/.git/
ERROR:gitosis.serve.main:Repository read access denied
fatal: The remote end hung up unexpectedly

admin@admin:~/work/test$ git clone git@192.168.0.3:teamwork.git
Initialized empty Git repository in /home/admin/work/test/teamwork/.git/
ERROR:gitosis.serve.main:Repository read access denied
fatal: The remote end hung up unexpectedly

刚记录完,又犯傻逼了,clone 了N次test,都不行,结果发现,自己根本没有把给b设定test的权限。再次说明头脑要清醒。

1
2
3
4
$ git clone git@192.168.0.3:test.git
Initialized empty Git repository in /tmp/test/.git/
ERROR:gitosis.serve.main:Repository read access denied
fatal: The remote end hung up unexpectedly

修改后

1
2
3
4
Initialized empty Git repository in /tmp/teamwork/test/.git/
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.

原因4: 不能写绝对路径

暂时不知道为啥,反正路径写了git@:/home/repo/xxx.git就会出现这个错误。貌似如果你如果密钥验证失败,要求你输入密码的情况下,是输入绝对路径的,而如果密钥验证成功,输入绝对路径,它就不认了。我想,这是为了保证系统安全,不让客户端用git账户乱clone不在repositories下的文件,即限定在repositories下了,所以只能用相对路径。

SSH: The authenticity of host can’t be established

0 down vote
This message is just SSH telling you that it’s never seen this particular host key before, so it isn’t able to truly verify that you’re connecting to the host you think you are. When you say “Yes” it puts the ssh key into your known_hosts file, and then on subsequent connections will compare the key it gets from the host to the one in the known_hosts file.

There was a related article on stack overflow showing how to disable this warning,http://stackoverflow.com/questions/3663895/ssh-the-authenticity-of-host-hostname-cant-be-established.

unrecognized command ‘gitosis-serve b’ && 每次登录要求输入密码

1
2
3
4
$ git clone git@192.168.0.3:/home/repo/teamwork.git
Initialized empty Git repository in /home/b/work/teamwork/.git/
fatal: unrecognized command 'gitosis-serve b'
fatal: The remote end hung up unexpectedly

遇到这个问题,b是我一个普通账户,而另一个admin每次登录都要求输入密码(ssh有两种登录方式:要求输入密码,和不需要输入密码——利用密钥),我就怀疑,gitosis的配置已经给我弄乱了,所以无法识别正确的密钥。
这个时候,我已经改了好多次密钥对,gitosis已经配置过好多次。通过gitosis-init是不会修复该问题的,于是,我删掉/home/git/下和repository有关的文件夹,包括.ssh下的authoritykey。还要删掉在/home/repo下的gitosis-admin.git。这样重新gitosis-init一下,就可以了。

如果server端的/etc/passwd中git的账户设置中,git使用的是/usr/bin/git-shell,而不是/bin/sh,也会报这个错误。

Agent admitted failure to sign using the key.

通过图形界面切换到b用户时,遇到这个问题,在原来到界面中,su b是可以clone的。
解決方式 使用 ssh-add 指令将私钥 加进来 (根据个人的密匙命名不同更改 id_rsa)

1
ssh-add   ~/.ssh/id_rsa

订版本信息

修订版本 时间 备注
文档创建 2016/6/18 文件创建
文档修改1 2018/1/5 添加Gitosis以及git协议

参考