0%

Git 服务器

一个公用的 git 远程仓库可以让开发者之间高效协作,每个人都有权利访问远程仓库,并且可以从那里推送和拉取资料。一个远程仓库通常只是一个裸仓库(bare repository)——即一个没有当前工作目录的仓库。 因为该仓库仅仅作为合作媒介,不需要从磁盘检查快照;存放的只有 Git 的资料。 简单的说,裸仓库就是你工程目录内的 .git 子目录内容,不包含其他资料。Git 支持四种不同的传输协议:本地协议(Local)、HTTP(S) 协议、SSH(Secure Shell)协议以及 Git 协议,这四种协议在不同的场合有不同的用途,并且各有利弊,可以根据实际情况来选择。

本地协议

本地协议是 Git 最基本的协议,当我们想在本地做一些 Git 实验时,这将非常有用。我们首先建立两个目录:/git/repo~/working,前者作为远程版本库,后者作为本地工作目录。

1
aneasystone@little-stone:~$ sudo mkdir -p /git/repo``aneasystone@little-stone:~$ sudo git init --bare /git/repo/test.git``已初始化空的 Git 仓库于 /git/repo/test.git/

我们在 /git/repo 目录通过 git init --bare 命令创建一个裸仓库(bare repository,即一个不包含当前工作目录的仓库),只要这一步,我们就可以开始使用了。接着我们在工作目录 clone 这个版本库:

1
aneasystone@little-stone:~$ cd ~/working/``aneasystone@little-stone:~/working$ git clone /git/repo/test.git``正克隆到 'test'...``warning: 您似乎克隆了一个空仓库。``完成。

然后我们可以使用 pullpush 就像操作其他的版本库一样。

1
aneasystone@little-stone:~/working$ cd test/``aneasystone@little-stone:~/working/test$ touch 1``aneasystone@little-stone:~/working/test$ touch 2``aneasystone@little-stone:~/working/test$ git add .``aneasystone@little-stone:~/working/test$ git commit -m 'first commit'``[master (根提交) 4983f84] first commit`` ``2 files changed, 0 insertions(+), 0 deletions(-)`` ``create mode 100644 1`` ``create mode 100644 2``aneasystone@little-stone:~/working/test$ sudo git push``[sudo] aneasystone 的密码: ``对象计数中: 3, 完成.``Delta compression using up to 8 threads.``压缩对象中: 100% (2/2), 完成.``写入对象中: 100% (3/3), 205 bytes | 205.00 KiB/s, 完成.``Total 3 (delta 0), reused 0 (delta 0)``To /git/repo/test.git`` ``* [new branch]   master -> master

本地协议不仅在做 Git 实验时很有用,如果你的团队有一个共享文件系统,可以在这个共享文件系统上创建一个远程版本库,团队成员把这个共享文件系统挂在本地,就可以直接使用本地协议进行协作开发,完全不需要搭建一台专门的 Git 服务器。

SSH 协议

本地协议虽然简单,但是一般来说并不适用,因为你无法控制用户对共享文件系统的操作,用户拥有 push 权限也就意味着用户对远程目录拥有完整的 Shell 权限,他们有可能会无意甚至有意的修改或删除 Git 内部文件,损坏 Git 仓库。

更安全的做法是使用专门的 Git 服务器,如果你有一台可以使用 SSH 连接的服务器,搭建 Git 服务将会非常简单。首先我们要确保服务器上运行着 SSH 服务(sshd),大多数 Linux 服务器版本都默认包含了该服务,如果没有,可以先安装 openssh-server。然后在服务器上创建 Git 远程版本库:

1
root@myserver:~# mkdir -p /git/repo``root@myserver:~# git init --bare /git/repo/test.git``已初始化空的 Git 仓库于 /git/repo/test.git/

然后在本地 clone 这个版本库:

1
aneasystone@little-stone:~/working$ git clone ssh://root@myserver/git/repo/test.git``正克隆到 'test'...``root@myserver's password: ``warning: 您似乎克隆了一个空仓库。

可以看到和使用本地协议几乎一样,不同的地方在于,在 clone 的时候需要在 URL 前面加上 ssh://root@myserver,你也可以使用 scp 式的写法:

1
$ git clone root@myserver:/git/repo/test.git

另外一点不同的地方是,每次 pullpush 的时候都需要输入远程服务器的 root 密码。很显然,让每个 Git 用户都使用 root 来访问服务器是一种很不安全的做法,有几种方法可以解决这个问题:

  • 最显而易见的方法是为每个 Git 用户创建一个独立的账号,并分别为他们分配对仓库的读写权限,这种方法行的通,但是对账号的管理非常麻烦,在团队人员不是很多的时候可以尝试,但是并不推荐;
  • 另一种方法是配置 SSH 服务器使用某个已有的认证系统来管理用户,比如 LDAP,这在很多企业中是很常见的,这样可以省去用 adduser 手工管理服务器账号的麻烦;
  • 还有一种方法是只创建一个账号,比如叫做 git,他对仓库具有读写权限,大家都使用这个账号来访问仓库。这种方法的好处是用户管理起来比较简单,而且可以使用后面介绍的 authorized_keys 文件对用户的公钥进行管理;

下面我们尝试下第三种方法。首先在服务器上创建一个名叫 git 的账号:

1
root@myserver:~# adduser git``Adding user `git' ...``Adding new group `git' (1000) ...``Adding new user `git' (1000) with group `git' ...``Creating home directory `/home/git' ...``Copying files from `/etc/skel' ...``Enter new UNIX password: ``Retype new UNIX password: ``passwd: password updated successfully``Changing the user information for git``Enter the new value, or press ENTER for the default``  ``Full Name []: git``  ``Room Number []:  ``  ``Work Phone []: ``  ``Home Phone []: ``  ``Other []: ``Is the information correct? [Y/n] Y

再设置一下 git 仓库的权限(默认情况下,git 仓库的权限为 rwxr-xr-x,只有创建者 root 有写的权限,也就意味着使用 git 账号只能 clone pull,不能 push):

1
# chmod a+w -R /git/repo/test.git

我们这里非常粗暴的使用 chmod a+w 将 git 仓库设置为对所有人可写,这里可以想一想,如果我们希望设置某些用户对仓库具有只读的权限,该怎么做呢?

然后就可以在本地愉快的进行 git 操作了:

1
$ git clone git@myserver:/git/repo/test.git

到这里似乎一切都很正常,但是几次实操之后你就会发现,每次 git 操作都要输入一次密码,这也太麻烦了,能不能“免密提交代码”呢?首先我们要知道,只要能通过 SSH 登陆到服务器,我们就能操作 git,所以如果 SSH 能支持免密登陆,我们就可以“免密提交代码”。还好,SSH 支持公钥认证,这种认证方式无需密码登陆。在 Linux 操作系统中,每个用户都可以拥有自己的一个或多个密钥对(公钥和私钥成对出现),这些密钥一般情况会保存在 ~/.ssh 目录下,在开始之前,我们先确认下自己是否已经生成过公钥了,可以看下这个目录下是否有 id_dsa.pubid_rsa.pub 这样的文件,如果没有,我们通过 ssh-keygen 来生成:

1
aneasystone@little-stone:~/.ssh$ ssh-keygen``Generating public/private rsa key pair.``Enter file in which to save the key (/home/aneasystone/.ssh/id_rsa): ``Enter passphrase (empty for no passphrase): ``Enter same passphrase again: ``Your identification has been saved in /home/aneasystone/.ssh/id_rsa.``Your public key has been saved in /home/aneasystone/.ssh/id_rsa.pub.``The key fingerprint is:``SHA256:4Ulpufuhs/AgDMb0VXnqMUTw6bD/HrAOI2z9c1cod9I aneasystone@little-stone``The key's randomart image is:``+---[RSA 2048]----+``|   .oo.    |``|    oo+.   |``| .  o.Oo    |``| o . . B++    |``| + . ..So  o  |``| . + . ..+. + E |``|  * * + oo +  |``|  . o Oo+.o.  |``|    **+.   |``+----[SHA256]-----+

这样我们在 ~/.ssh 目录生成了两个文件,id_rsa 是你的私钥,id_rsa.pub 是你的公钥。关于私钥和公钥的原理以及 RSA 加密算法等内容可以参考我之前写过的一篇介绍 HTTPS 和证书 的文章。

我们假设你的 Git 服务器是由专门的服务器管理员负责维护和管理,当你生成你的公钥之后,就可以给服务器管理员发送一封申请 Git 服务的邮件,并附上你的公钥。服务器管理员在收到你的申请之后,如果同意了,就可以进行下面的操作:

首先将公钥文件拷贝到服务器上:

1
# scp id_rsa.pub root@myserver:/home/git

将公钥文件的内容追加到 git 账户的 authorized_keys 文件中(要注意的是,如果是第一次操作,/home/git 目录下是没有 .ssh 目录的,需要手工创建 .ssh 目录和 authorized_keys 文件):

1
root@myserver:/home/git# cat id_rsa.pub >> /home/git/.ssh/authorized_keys

后续如果有其他的用户申请 Git 服务,都可以按照这个步骤操作。一旦完成这个操作,服务器管理员将会回复你的邮件,通知你 Git 服务已经开通,这个时候你再进行 git 操作就可以不用输入密码了。关于 SSH 的使用,更详细的步骤可以参考 Github 上的这篇指南:Connecting to GitHub with SSH

作为服务器管理员,关于 SSH 还有一点需要考虑,那就是 SSH 的安全问题。在上面介绍本地协议时,我们说这种方式无法控制用户对 Git 仓库的操作,无法防止用户有意或无意的损坏 Git 仓库,使用 SSH 协议一样存在这样的问题,用户能通过 SSH 拉取和提交代码,也就意味着用户可以通过 SSH 连接到服务器,对 Git 仓库进行任何操作,这是一件很让人担心的事情。

因此,我们还需要对 git 账号做一些限制。默认情况下,我们新建账号的登陆 shell 是 /bin/bash,这个配置在 /etc/passwd 文件中:

1
git:x:1000:1000:git,,,:/home/git:/bin/bash

可以使用 chsh 命令修改用户的登陆 shell,让他不能通过 SSH 访问服务器,怎么修改呢?我们可以看一下 /etc/shells 文件,这里定义了所有可以使用的登陆 shell,你可以将 /bin/bash 改成这里的任何一个:

1
root@myserver:~# cat /etc/shells ``# /etc/shells: valid login shells``/bin/sh``/bin/dash``/bin/bash``/bin/rbash

很显然,这些 shell 并不是我们想要的,有没有一个 shell 只允许用户进行 git 操作,而不允许其他操作呢?还好,Git 的软件包提供了一个名叫 git-shell 的登陆 shell,我们可以把他加进去,一般情况下位于 /usr/bin/git-shell。我们使用 chsh 修改 git 的登陆 shell:

1
root@myserver:~# chsh git``Changing the login shell for git``Enter the new value, or press ENTER for the default``  ``Login Shell [/bin/bash]: /usr/bin/git-shell

这样当用户 git 通过 SSH 连接服务器时,就会直接被拒绝了。

Git 协议

SSH 协议解决了用户直接操作 Git 仓库的权限问题,但是如果我们希望对除仓库维护者之外的所有人都开放 Git 仓库的只读权限,这在开源项目中和企业内部往往是很常见的,任何人都可以去查看仓库的代码,这时管理员需要给每一个用户配置 SSH 密钥是非常麻烦的。虽然也可以使用变通的方法来达到这个效果,但是很繁琐,下面是具体的步骤:

  • 使用 g+w 设置 Git 仓库的权限,让仓库创建者所在的用户组具有写权限,而不是所有人都有写权限(这一步通常也可以在 git init 的时候加上 --shared 参数);
  • 然后将 git 账号加到仓库创建者的用户组;
  • 再创建一个 git_ro 账户,这个账户对仓库只有只读权限;
  • 最后为 git_ro 账户创建一个密钥对,把 git_ro 的私钥公开出来供所有人使用。

可以看到使用 SSH 协议最终都逃不过授权这一步,而且公开私钥的做法也不是很优雅。实际上,Git 提供了另一种方式来让这个操作更简单,那就是 Git 协议。使用 Git 协议必须要在服务器上运行 Git 守护进程,git 命令自带了一个 daemon 参数:

1
root@myserver:~# git daemon --reuseaddr --base-path=/git/repo/ /git/repo/

上面的各个参数可以 参考 git-daemon 的文档。git-daemon 会监听 9418 端口,如果你的服务器有防火墙,需要将该端口添加到白名单,如果你使用的是阿里云服务器,需要像下面这样添加一个安全组规则:

security-group.jpg

为了让所有的用户都可以访问我们的仓库,还需要在仓库目录下创建一个名为 git-daemon-export-ok 的文件:

1
root@myserver:~# cd /git/repo/test.git/``root@myserver:/git/repo/test.git/# touch git-daemon-export-ok

至此,所有人都可以通过 Git 协议来克隆或拉取项目源码了(注意上面指定了 base-path 参数为 /git/repo/,所以 URL 可以直接写 git://myserver/test.git):

1
aneasystone@little-stone:~/working$ git clone git://myserver/test.git

一般情况下,服务器管理员还会做一些其他的配置,譬如在服务器重启时让 Git 守护进程自动启动,这有很多种方式可以实现,可以参考《Pro Git》 Git 守护进程 这一节的内容。

HTTP(S) 协议

我们一般通过 Git 协议进行无授权访问,通过 SSH 协议进行授权访问,如果你的项目是内部项目,只针对部分授权用户,那使用 SSH 协议就足够了,但是如果既需要授权访问也需要无授权访问,可能需要 SSH 协议和 Git 协议搭配使用,这在维护上成本很高。这时就到了我们的压轴戏 —— HTTP 协议出场的时候了,它同时支持上面两种访问方式。

通过 HTTP 协议访问 Git 服务是目前使用最广泛的方式,它支持两种模式:旧版本的 Dumb HTTP 和 新版本的 Smart HTTP,Dumb HTTP 一般很少使用,下面除非特殊说明,所说的 HTTP 协议都是 Smart HTTP。使用 HTTP 协议的好处是可以使用各种 HTTP 认证机制,比如用户名/密码,这比配置 SSH 密钥要简单的多,对普通用户来说也更能接受。如果担心数据传输安全,也可以配置 HTTPS 协议,这和普通的 Web 服务是一样的。

下面我们就来尝试搭建一个基于 HTTP 协议的 Git 服务器。《Pro Git》上提供了一个基于 Apache 的配置示例,如果你是使用 Apache 作为 Web 服务器,可以参考之,我们这里使用 Nginx 来作为 Web 服务器,其原理本质上是一样的,都是通过 Web 服务器接受 HTTP 请求,并将请求转发到 Git 自带的一个名为 git-http-backend 的 CGI 脚本

首先我们安装所需的软件:

1
# apt-get install -y git-core nginx fcgiwrap apache2-utils

其中,Nginx 作为 Web 服务器,本身是不能执行外部 CGI 脚本的,需要通过 fcgiwrap 来中转,就像使用 php-fpm 来执行 PHP 脚本一样。apache2-utils 是 Apache 提供的一个 Web 服务器的工具集,包含了一些有用的小工具,譬如下面我们会用到的 htpasswd 可以生成 Basic 认证文件。

启动 nginx 和 fcgiwrap,并访问 http://myserver 测试 Web 服务器是否能正常访问:

1
# service nginx start``# service fcgiwrap start

然后我们打开并编辑 Nginx 的配置文件(/etc/nginx/sites-available/default):

1
location / {``    ``include fastcgi_params;``    ``fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;``    ``fastcgi_param GIT_HTTP_EXPORT_ALL "";``    ``fastcgi_param GIT_PROJECT_ROOT /git/repo;``    ``fastcgi_param PATH_INFO $uri;``    ``fastcgi_param REMOTE_USER $remote_user;``    ``fastcgi_pass unix:/var/run/fcgiwrap.socket;``}

这里通过 fastcgi_param 设置了一堆的 FastCGI 参数,如下:

  • SCRIPT_FILENAME:指定 CGI 脚本 git-http-backend 的位置,表示每次 HTTP 请求会被转发到该 CGI 脚本;
  • GIT_HTTP_EXPORT_ALL:git-http-backend 默认只能访问目录下有 git-daemon-export-ok 文件的 Git 仓库,和上面介绍的 Git 协议是一样的,如果指定了 GIT_HTTP_EXPORT_ALL,表示允许访问所有仓库;
  • GIT_PROJECT_ROOT:Git 仓库的根目录;
  • REMOTE_USER:如果有认证,将认证的用户信息传到 CGI 脚本;

改完之后我们重启 Nginx,并通过 HTTP 协议 clone 仓库:

1
aneasystone@little-stone:~/working$ git clone http://myserver/test.git

开启身份认证

到这里一切 OK,但是当我们 push 代码的时候,却会报下面的 403 错误:

1
aneasystone@little-stone:~/working/test$ git push origin master``fatal: unable to access 'http://myserver/test.git/': The requested URL returned error: 403

为了解决这个错误,我们可以在 git-http-backend 的官网文档 上找到这样的一段描述:

By default, only the upload-pack service is enabled, which serves git fetch-pack and git ls-remote clients, which are invoked from git fetch, git pull, and git clone. If the client is authenticated, the receive-pack service is enabled, which serves git send-pack clients, which is invoked from git push.

第一次读这段话可能会有些不知所云,这是因为我们对这里提到的 upload-packfetch-packreceive-packsend-pack 这几个概念还没有什么认识。但是我们大抵可以猜出来,默认情况下,只有认证的用户才可以 push 代码,如果某个 Git 仓库希望所有用户都有权限 push 代码,可以为相应的仓库设置 http.receivepack

1
root@myserver:/# cd /git/repo/test.git/``root@myserver:/git/repo/test.git# git config http.receivepack true

当然最好的做法还是对 push 操作开启认证,官网文档上有一个 lighttpd 的配置 我们可以借鉴:

1
$HTTP["querystring"] =~ "service=git-receive-pack" {``  ``include "git-auth.conf"``}``$HTTP["url"] =~ "^/git/.*/git-receive-pack$" {``  ``include "git-auth.conf"``}

这个配置看上去非常简单,但是想要理解为什么这样配置,就必须去了解下 Git 的内部原理。正如上面 git-http-backend 文档上的那段描述,当 Git 客户端执行 git fetch, git pull, and git clone 时,会调用 upload-pack 服务,当执行 git push 时,会调用 receive-pack 服务,为了更清楚的说明这一点,我们来看看 Nginx 的访问日志。

执行 git clone

1
[27/Nov/2018:22:18:00] "GET /test.git/info/refs?service=git-upload-pack HTTP/1.1" 200 363 "-" "git/1.9.1"``[27/Nov/2018:22:18:00] "POST /test.git/git-upload-pack HTTP/1.1" 200 306 "-" "git/1.9.1"

执行 git pull

1
[27/Nov/2018:22:20:25] "GET /test.git/info/refs?service=git-upload-pack HTTP/1.1" 200 363 "-" "git/1.9.1"``[27/Nov/2018:22:20:25] "POST /test.git/git-upload-pack HTTP/1.1" 200 551 "-" "git/1.9.1"

执行 git push

1
[27/Nov/2018:22:19:33] "GET /test.git/info/refs?service=git-receive-pack HTTP/1.1" 401 204 "-" "git/1.9.1"``admin [27/Nov/2018:22:19:33] "GET /test.git/info/refs?service=git-receive-pack HTTP/1.1" 200 193 "-" "git/1.9.1"``admin [27/Nov/2018:22:19:33] "POST /test.git/git-receive-pack HTTP/1.1" 200 63 "-" "git/1.9.1"

可以看到执行 clone 和 pull 请求的接口是一样的,先请求 /info/refs?service=git-upload-pack,然后再请求 /git-upload-pack;而 push 是先请求 /info/refs?service=git-receive-pack,然后再请求 /git-receive-pack,所以在上面的 lighttpd 的配置中我们看到了两条记录,如果要对 push 做访问控制,那么对这两个请求都要限制。关于 Git 传输的原理可以参考 《Pro Git》的 Git 内部原理 - 传输协议 这一节。

我们依葫芦画瓢,Nginx 配置文件如下:

1
location @auth {``    ``auth_basic "Git Server";``    ``auth_basic_user_file /etc/nginx/passwd;` `    ``include fastcgi_params;``    ``fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;``    ``fastcgi_param GIT_HTTP_EXPORT_ALL "";``    ``fastcgi_param GIT_PROJECT_ROOT /git/repo;``    ``fastcgi_param PATH_INFO $uri;``    ``fastcgi_param REMOTE_USER $remote_user;``    ``fastcgi_pass unix:/var/run/fcgiwrap.socket;``}` `location / {``    ``error_page 418 = @auth;``    ``if ( $query_string = "service=git-receive-pack" ) { return 418; }``    ``if ( $uri ~ "git-receive-pack$" ) { return 418; }` `    ``include fastcgi_params;``    ``fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;``    ``fastcgi_param GIT_HTTP_EXPORT_ALL "";``    ``fastcgi_param GIT_PROJECT_ROOT /git/repo;``    ``fastcgi_param PATH_INFO $uri;``    ``fastcgi_param REMOTE_USER $remote_user;``    ``fastcgi_pass unix:/var/run/fcgiwrap.socket;``}

其中相同的配置我们也可以用 include 指令放在一个共用的配置文件里,这样我们就实现了在 push 的时候需要填写用户名和密码了。我们通过 Nginx 的 auth_basic_user_file 指令来做身份认证,用户名和密码保存在 /etc/nginx/passwd 文件中,这个文件可以使用上面提到的 apache2-utils 包里的 htpasswd 来生成:

1
root@myserver:/# htpasswd -cb /etc/nginx/passwd admin 123456

另外,在 push 的时候,有时候可能会遇到 unpack failed: unable to create temporary object directory 这样的提示错误:

1
aneasystone@little-stone:~/working/test$ git push origin master``Counting objects: 3, done.``Writing objects: 100% (3/3), 193 bytes | 0 bytes/s, done.``Total 3 (delta 0), reused 0 (delta 0)``error: unpack failed: unable to create temporary object directory``To http://myserver/test.git`` ``! [remote rejected] master -> master (unpacker error)``error: failed to push some refs to 'http://myserver/test.git'

这一般情况下都是由于 Git 仓库目录的权限问题导致的,在这里 Git 仓库的根目录 /git/repo 是 root 创建的,而运行 nginx 和 fcgiwrap 的用户都是 www-data,我们可以把 Git 仓库目录设置成对所有人可读可写,也可以像下面这样将它的拥有者设置成 www-data 用户:

1
root@myserver:/# chown -R www-data:www-data /git/repo

凭证管理

上面我们站在管理员的角度解决了用户身份认证的问题,但是站在用户的角度,每次提交代码都要输入用户名和密码是一件很痛苦的事情。在上面介绍 SSH 协议时,我们可以使用 SSH 协议自带的公钥认证机制来省去输入密码的麻烦,那么在 HTTP 协议中是否存在类似的方法呢?答案是肯定的,那就是 Git 的凭证存储工具:credential.helper

譬如像下面这样,将用户名和密码信息保存在缓存中:

1
$ git config --global credential.helper cache

这种方式默认只保留 15 分钟,如果要改变保留的时间,可以通过 --timeout 参数设置,或者像下面这样,将密码保存在文件中:

1
$ git config --global credential.helper store

这种方式虽然可以保证密码不过期,但是要记住的是,这种方式密码是以明文的方式保存在你的 home 目录下的。可以借鉴操作系统自带的凭证管理工具来解决这个问题, 比如 OSX Keychain 或者 Git Credential Manager for Windows。更多的内容可以参考《Pro Git》凭证存储 这一节。

除此之外,还有一种更简单粗暴的方式:

1
aneasystone@little-stone:~/working$ git clone http://admin:123456@myserver/test.git

综合对比

这一节对 Git 的四大协议做一个综合对比。

  • 本地协议
    • 优点:架设简单,不依赖外部服务,直接使用现有文件和网络权限,常用于共享文件系统
    • 缺点:共享文件系统的配置和使用不方便,且无法保护仓库被意外损坏,传输性能较低
  • SSH 协议
    • 优点:架设简单,所有数据经过授权加密,数据传输很安全,传输性能很高
    • 缺点:不支持匿名访问,配置 SSH 的密钥对小白用户有一定的门槛
  • Git 协议
    • 优点:对开放的项目很适用,无需授权,传输性能最高
    • 缺点:缺乏授权机制,架设较麻烦,企业一般不会默认开放 9418 端口需要另行添加
  • HTTP/S 协议
    • 优点:同时支持授权访问和无授权访问,传输性能较高,配合 HTTPS 也可以实现数据安全
    • 缺点:架设 HTTP 服务较麻烦,认证凭证不好管理

更高级的工具

上面介绍的是搭建 Git 服务器最基本的方法,如果你只是希望能找一个版本控制系统来替代现有的 SVN,这也许就足够了。但如果你希望你的版本控制系统能拥有一个更友好的 UI 界面,能更好的管理你的用户和权限,能支持更现代的 Pull Request 功能以及能和 CI/CD 系统更紧密的联系起来,你就需要一个更高级的工具,你可以试试 GitWebGitoliteGitlabGogsGitea,当然,如果你愿意,你也可以把代码放在那些流行的代码托管平台上,比如 GithubBitbucket 等等。

参考资料