Git正解 脱水版 【4. 架设远程仓库】

为了实现Git的协作开发,用户需要一个远程仓库,虽然个人的本地仓库,理论上也可实现变更的推送和获取,但需要格外小心的处理,否则很容易产生混乱,同时本地仓库很难保证持续在线,因此需要一个更可靠的远程仓库,方便协作者之间,共享开发成果。Git服务器的用法简单,首先需选择通讯协议,以及完成服务器的相关配置,如果觉得麻烦,还可将项目代码,托管到他人的服务器上。

一个远程仓库通常是一个裸仓库,不包含工作区,因为仓库只是一个用于协作的存储区,所以裸仓库只包含开发项目的.git目录。

4.1 协议

Git可使用四种协议,本地协议,ssh协议,Git协议和http协议,注意,除了http协议,其他协议都要求在服务器端,安装并运行Git。

本地协议

在本地协议中,远程仓库将视为,本机的另一个目录,通常是一个共享文件系统,比如NFS文件系统,或者是一台共享主机,当然这并不安全。如果使用共享文件系统,用户能从本地仓库中,实现克隆,推送和获取,为实现本地目录的克隆,可运行,

$ git clone /srv/git/project.git
或者
$ git clone file:///srv/git/project.git

如果给出一个路径,Git将直接查找路径,如果使用file://前缀,Git将尝试网络连接,当然效率更低,如果用户希望得到一个干净的仓库副本(无外部的引用或对象),可给出file://前缀,通常情况下,从其他VCS导入的仓库,会包含外部的引用或对象,这里只使用路径格式,以获得最快的传输速率,为本地项目,配置一个远程仓库,可使用git remote add [本地项目名] [仓库路径名],仓库路径名,需指定.git文件,比如/srv/git/project.git,之后可直接使用本地项目名,实现仓库数据的获取和推送。

优点

协议简单,也可以使用网络访问,同时可使用原有的文件权限,如果已配置好一个共享文件系统,很容易设置一个远程仓库,将裸仓库移动到共享目录,并设定共享目录的权限,协作者可使用git pull /home/john/project,轻松获取远程仓库的数据。

缺点

共享访问比网络访问,需要更多的配置步骤,比如需要挂载远端磁盘,才能实现共享访问,同时速度也更慢,这类协议无法保证远程仓库的安全,所有人都具有相同的权限,可随意修改远程仓库,所以这类协议的风险很高。

http协议

http协议有两种模式,1.6.6版本之前,只有一种模式,即只读模式,从1.6.6版本开始,加入了智能模式,这使得Git能够在线约定,数据传输的格式,这类似于ssh协议,近几年,智能模式已变成主流。

智能模式

智能模式同样很简单,并能在http端口上,实现类似于ssh和Git协议的传输,它能使用不同的http验证机制,同时又支持匿名访问,另外不同的URL可放入不同的数据,如果用户需要推送数据到远程仓库,则必须通过验证,同时远程仓库又支持匿名读取,比如GitHub已支持该模式,使用URL,就可在线查看和克隆远程仓库,如果用户拥有权限,则可推送数据。

只读模式

如果服务器不支持智能模式,Git客户端将尝试只读模式,只读模式会将裸仓库,视为web服务器的简单文件,它的优势,依然是配置简单,将裸仓库放入http document的根目录,再配置一个特定的post-update hook,远程仓库已设置完毕,能访问web服务器的任何用户,都能读取和克隆远程仓库,

$ cd /var/www/htdocs/
$ git clone --bare /path/to/git_project gitproject.git
$ cd gitproject.git
$ mv hooks/post-update.sample hooks/post-update
$ chmod a+x hooks/post-update

在Git中,默认运行git update-server-info命令,可开启post-update hook,以保证远程仓库的获取和克隆,当用户推送数据时,上述命令也会运行,任意用户可使用以下命令,实现仓库克隆,
$ git clone http://example.com/gitproject.git

在Apache服务器中,可使用公共路径/var/www/htdocs,而在其他静态web服务器上,裸仓库可设定不同路径,同时仓库数据将被视为静态文件,通常情况下,用户可选择智能模式,或只读模式,而两种模式的混合使用,则比较少见。

优点

使用一个URL地址,就能满足所有的访问类型,验证机制也使用户权限更容易管理,用户名加用户密码的验证,比ssh协议更方便,同时它的速度和效率也很高,如果是基于https的只读仓库,这意味着传输内容可以加密,或者客户端可使用特殊的SSL签名证书,https的另一个优势在于,企业防火墙通常不会禁止该协议的传输。

缺点

如果基于http,实现验证推送,有时申请签名证书的难度,远远超过ssh key的生成,目前只有几个签名证书的申请工具,比如macOS的Keychain, Windows的Credential Manager,同时申请的过程异常痛苦。

ssh协议

ssh是登录宿主环境的一类传输协议,同时也是一类具有用户验证机制的网络协议,由于简单的配置和用法,因此应用广泛,如果基于ssh,克隆一个远程仓库,可使用ssh://前缀,

$ git clone ssh://[user@]server/project.git

当然也可使用ssh协议的简写格式,

$ git clone [user@]server:project.git

如果上述两个命令中,未指定用户名(user),Git将假定用户名为当前用户名。

优点

首先ssh容易配置,其次ssh后台程序对于大多数网络管理员来说,驾轻就熟,同时大多数操作系统都包含ssh的管理工具,ssh也能实现安全访问,即传输数据可以加密,必须通过用户验证机制,并且传输效率也很高。

缺点

不支持匿名访问,这使得ssh对开源项目并不友好,如果使用公司内网,ssh将是唯一的选择,如果需要实现匿名只读访问,同时使用ssh,那么只能利用ssh完成推送,而获取功能则必须使用其他协议。

Git协议

Git软件包的后台程序中,包含了该协议,该协议将监听一个特殊端口9418,这与ssh类似,同时不需要用户验证,为了支持Git协议,用户必须创建一个git-daemon-export-ok文件,没有该文件,Git后台程序将无法,为远程仓库提供服务,同时该协议无安全措施,要么开放所有人的访问,要么关闭,这意味着该协议,不适合作为数据推送协议。

优点

它的速度最快,适用于大型开源项目的只读访问,无验证机制的同时,也节省了数据加密和用户验证的开销。

缺点

缺少验证机制,需要添加ssh或https协议,以授予少数开发者的数据推送权限,同时Git协议的配置最复杂,它必须运行自己的后台程序,而后台程序又必须添加到xinetd或systemd配置文件中,同时防火墙必须开放9418端口,而企业级防火墙通常不会默认开启这个端口。

4.2 基本配置

应当注意,这里只能给出基本的配置方法,基于不同的操作系统,以及不同的功能需求,相应的配置会有所差异。

为了配置一个Git服务器,可导入一个远程仓库的原有配置,生成一个裸仓库配置,这也是常用的简单方法,

$ git clone --bare my_project my_project.git
Cloning into bare repository 'my_project.git'...
done.

基于原有仓库my_project,生成了一个裸仓库的配置目录my_project.git,上述命令等同于

$ cp -Rf my_project/.git my_project.git

不同仓库的初始配置,差异不大,且需要重新设定少数配置。

把裸仓库移入服务器

此时用户已基于原有仓库,生成了一个裸副本,之后需将裸副本,放入服务器,并设定相应的传输协议,假定服务器地址为git.example.com,并支持ssh协议,同时裸仓库将放入/srv/git目录,

$ scp -r my_project.git [email protected]:/srv/git

如果其他用户具有ssh读取权限,可访问裸仓库,并进行克隆,

$ git clone [email protected]:/srv/git/my_project.git

而具有ssh写入权限的用户,可向裸仓库推送数据,如果此时用户运行git init --shared,Git会自动在仓库属性中,添加写入权限组,此命令不会丢失仓库的提交或refs等信息。

$ ssh [email protected]
$ cd /srv/git/my_project.git
$ git init --bare --shared

此时已完成远程仓库的基本配置,开发协作者已经能够启动项目协作,

简单配置

上述配置已满足几个开发者的小型团队或是公司的内部试用,当然Git服务器中最复杂的配置,来自于用户管理,如果远程仓库既支持只读访问,又支持写入访问,用户的权限管理会相当复制。

ssh访问

如果所有用户的ssh权限都相同,远程仓库的配置会相对简单,如果需要更复杂的权限管理,则需要在服务器的操作系统中,处理文件系统的权限。

首先应确定团队中,需要写入权限的用户,之后在ssh中配置这些用户,当然存在一些用户管理的基本方案,第一种方案,单独配置每个用户的权限,由此产生的工作量较大,因为不得不重复使用adduser,为每个新用户,设定一个临时密码。其次,创建一个用户账号git,通知所有需要写入权限的用户,将他们的ssh公钥,发送给管理员,管理员将这些公钥,添加到git账号的~/.ssh/authorized_keys文件,这些用户则可使用git账号,访问远程仓库,应当注意,ssh用户名不会影响到提交所包含的信息,最后一种方案,使用LDAP服务器或类似的中心式验证系统,完成ssh的验证,这样所有用户都能基于不同的ssh验证机制,获得对应的权限,

4.3 生成ssh公钥

目前大多数Git服务器都选择了ssh公钥验证模式,所有用户都必须生成一个自己的公钥,不同操作系统中,公钥的生成大同小异,首先用户需确认之前未生成过公钥,默认情况下,用户公钥保存在~/.ssh目录,检查该目录,是否已包含了用户公钥,

$ cd ~/.ssh
$ ls
authorized_keys2 id_dsa     known_hosts
config           id_dsa.pub

如果目录中包含了一个id_dsa或id_rsa文件,以及一个同名的pub文件,pub文件为用户公钥,而另一个文件则为用户私钥,如果目录中未包含上述文件,用户则需要运行ssh-keygen,生成用户的公钥和私钥,

$ ssh-keygen -o
Generating public/private rsa key pair.
Enter file in which to save the key (/home/schacon/.ssh/id_rsa):
Created directory '/home/schacon/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/schacon/.ssh/id_rsa.
Your public key has been saved in /home/schacon/.ssh/id_rsa.pub.
The key fingerprint is:
d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 [email protected]

ssh-keygen首先将确认ssh密钥的保存位置,之后将设定密钥的使用密码,会询问用户两次,如果用户不希望设定密钥的使用密码,可直接回车,如果用户需要使用密码,应为ssh-keygen附加-o选项,这可保证用户私钥的保存格式比默认格式,更能对抗暴力破解。

当所有用户都获得了自己的公钥,就可将pub文件的内容,复制到邮件,并发送给管理员,以下是一个公钥示例,

$ cat ~/.ssh/id_rsa.pub
 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
 GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
 Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
 t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
 mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
 NrRFi9wrf+M7Q== schacon@ agadorlaptop.local

4.4 密钥配置

假定使用Ubuntu操作系统,并使用authorized_keys方法,实现用户验证,注意,使用ssh-copy-id命令,可实现用户公钥的自动管理,那么就无需手动复制和安装用户公钥,首先,管理员需创建一个git账号,并在用户根目录下,创建一个.ssh子目录,

$ sudo adduser git
$ su git
$ cd
$ mkdir .ssh && chmod 700 .ssh
$ touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys

之后管理员需将用户公钥,添加到git用户的authorized_keys文件中,假定tmp目录下,已保存了若干个用户公钥,手动添加这些公钥,

$ cat /tmp/id_rsa.john.pub >> ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.josie.pub >> ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.jessica.pub >> ~/.ssh/authorized_keys

完成上述验证配置后,可运行git init --bare,创建一个裸仓库,

$ cd /srv/git
$ mkdir project.git
$ cd project.git
$ git init --bare
Initialized empty Git repository in /srv/git/project.git/

之后协作者Join,Josie和Jessica,可将上述裸仓库,设定为远程仓库,并推送开发项目的首个版本,注意,每当新的开发项目,需添加到服务器时,管理员都必须登录服务器,创建一个对应的裸仓库,假定服务器名为gitserver,共用账号为git,开发项目名为myproject,在协作者的电脑上,可运行以下命令,实现数据推送,

$ cd myproject
$ git init
$ git add .
$ git commit -m 'initial commit'
$ git remote add origin git@gitserver:/srv/git/project.git
$ git push origin master

而其他协作者则能克隆仓库,再推送数据,

$ git clone git@gitserver:/srv/git/project.git
$ cd project
$ vim README
$ git commit -am 'fix for the README file'
$ git push origin master

应当注意,所有使用git账号,登录服务器的用户,都可获得一个shell环境,这当然存在安全隐患,为了限制用户的操作,需使用git-shell(Git定制的shell环境)替换掉通用shell,首先需要将,包含完整路径的git-shell添加到/etc/shells,

$ cat /etc/shells # see if `git-shell` is already in there. If not...
$ which git-shell # make sure git-shell is installed on your system.
$ sudo -e /etc/shells # and add the path to git-shell from last command

使用系统命令chsh,可修改linux中,系统预设的shell环境,

$ sudo chsh git -s $(which git-shell)

这时git账号只能用于推送和获取远程仓库,而无法使用通用shell,进入服务器的操作系统,

$ ssh git@gitserver
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to gitserver closed.

4.5 Git后台程序

开启Git后台程序,可使远程仓库支持Git协议,这是一个快速的无验证的网络协议,假定已使用Git协议,如果服务器运行在防火墙之外,服务器只能包含开源项目,如果服务器运行在防火墙之内,Git协议可服务大量的只读用户,同时无需管理所有用户的公钥,另外Git协议也很容易配置,通常只需一条命令,

$ git daemon --reuseaddr --base-path=/srv/git/ /srv/git/

–reuseaddr选项,可使服务器无需等待原有链接的静默超时,而开始接受其他用户的连接请求,–base-path可使用户在克隆仓库时,无需给出,存放克隆仓库的完整路径,注意,用户必须在防火墙中,开放9418端口。

不同的操作系统,将决定Git后台程序不同的启动方式,在主流的linux发行版本中,通常会选择systemd,作为初始化工具,因此管理员可在/etc/systemd/system目录下,放入git-daemon.service文件,如下,用于配置Git后台程序,

[Unit]
Description=Start Git Daemon

[Service]
ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/srv/git/ /srv/git/

Restart=always
RestartSec=500ms
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=git-daemon
User=git
Group=git

[Install]
WantedBy=multi-user.target

从文件中可知,Git后台程序只能被git用户组或git用户调用,如果修改相关信息,应确定用户组或用户名是否存在,另外还需要确认Git后台程序的路径是否为/usr/bin/git,运行systemctl enable git-daemon,可实现系统启动时,Git后台程序的自动调用,使用systemctl start git-daemon和systemctl stop git-daemon,可启动和退出Git后台程序,

在类Unix系统中,可能需要使用xinetd,即sysvinit开机系统的一个脚本,

之后还需要在仓库中,创建git-daemon-export-ok,以告知Git,该仓库支持无验证的用户访问。

$ cd /path/to/project.git
$ touch git-daemon-export-ok

4.6 智能HTTP

ssh可实现验证登录,git可实现无验证登录,同时智能http还可同时支持两种不同的登录方式,只需在服务器上,调用一个Git自带的CGI脚本git-http-backend,该CGI可读取,附带了http url的git fetch或git push命令所包含的信息,并确定客户端是否支持http的智能通讯(1.6.6版开始支持该功能),如果不支持,将回退到只读模式,用于兼容老版本的客户端。

这里将提供一个简单的配置过程,并使用Apache作为CGI服务器,同时用户无需精通Apache的配置,

$ sudo apt-get install apache2 apache2-utils
$ a2enmod cgi alias env

以上命令可使能mod_cgi, mod_alias, mod_env模块,同时设定/srv/git目录的用户组为www-data,以便web服务器可对仓库进行读写,因为Apache将默认使用www-data的组用户,来运行CGI脚本,

$ chgrp -R www-data /srv/git

之后在Apache配置中,加入git-http-backend,

SetEnv GIT_PROJECT_ROOT /srv/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/lib/git-core/git-http-backend/

如果去除环境变量GIT_HTTP_EXPORT_ALL,Git只支持无验证登录,所以还需添加验证配置信息,

<Files "git-http-backend">
    AuthType Basic
    AuthName "Git Access"
    AuthUserFile /srv/git/.htpasswd
    Require expr !(%{QUERY_STRING} -strmatch '*service=git-receive-pack*' || %{REQUEST_URI} =~ m#/git-receive-pack$#)
    Require valid-user
</Files>

之后可创建一个.htpasswd文件,用于保存用户密码,以下创建了schacon用户的密码文件,

$ htpasswd -c /srv/git/.htpasswd schacon

Apache验证用户的配置方法有很多,这里只提供了一种最简单的方法,还可以配置,基于SSL的数据加密。除了Apache之外,还可选择其他支持CGI功能的web服务器,虽然Git CGI无法支持所有的验证模式,但CGI更利于web服务器的分层管理。

4.7 GitWeb

现在已实现了基于http的读写和只读访问,为了配置一个更简单的web页面,还可使用Git提供的另一个CGI脚本GitWeb,
在这里插入图片描述
Git只需一条命令,就可在轻量级web服务器(lighttpd或webrick)上,展示一个临时示例,在linux系统中,可使用git instaweb,如果未使用lighttpd,必须附带–httpd选项,用于配置特定的web服务器,

$ git instaweb --httpd=webrick
 [2009-02-21 10:02:21] INFO WEBrick 1.3.1
 [2009-02-21 10:02:21] INFO ruby 1.8.6 (2008-03-03) [universal-darwin9.0]

这时在1234端口上,已启动了httpd服务,之后在web浏览器中,可显示上述页面,如果需要退出服务器,可附带–stop选项,

$ git instaweb --httpd=webrick --stop

如果需要一个长期运行的web接口,则必须在标准的web服务器上,配置GitWeb脚本,同时在一些linux发行版本中,已存在gitweb包,可使用apt进行安装,或者编译源码进行安装,完成安装后,将生成一个CGI脚本,

$ git clone git://git.kernel.org/pub/scm/git/git.git
$ cd git/
$ make GITWEB_PROJECTROOT="/srv/git" prefix=/usr gitweb
    SUBDIR gitweb
    SUBDIR ../
make[2]: `GIT-VERSION-FILE' is up to date.
    GEN gitweb.cgi
    GEN static/gitweb.js
$ sudo cp -Rf gitweb /var/www/

GITWEB_PROJECTROOT用于指定仓库的位置,如果需要Apache运行该脚本,则必须在配置文件中,加入

<VirtualHost *:80>
    ServerName gitserver
    DocumentRoot /var/www/gitweb
    <Directory /var/www/gitweb>
        Options +ExecCGI +FollowSymLinks +SymLinksIfOwnerMatch
        AllowOverride All
        order allow,deny
        Allow from all
        AddHandler cgi-script cgi
        DirectoryIndex gitweb.cgi
    </Directory>
</VirtualHost>

注意,支持CGI或Perl的web服务器都可运行GitWeb,配置过程也不复杂,完成相关配置后,在浏览器中,输入http://gitserver/,就可在线查看Git仓库。

4.8 GitLab

GitLab是一个常用的Git服务器,虽然它比GitWeb更复杂,同时也能提供更强大的功能。

安装

GitLab是一个基于数据库的web应用,为了实现快速安装,可从https://bitnami.com/stack/gitlab页面,下载一个安装工具,或是进入一个虚拟机环境,在以下的登录界面中,输入默认的用户名和密码,
在这里插入图片描述
在https://gitlab.com/gitlab-org/gitlab-ce/tree/master页面中,给出一个安装指南,其中包含了不同操作系统的安装方式。

管理

完成gitlab的安装后,在浏览器中,输入对应的主机名或主机IP地址,即可访问gitlab的web管理页面,再使用管理员账号登录,默认的管理员账号为 [email protected],密码为5iveL!fe,完成登录后,点击菜单按钮Admin area,
在这里插入图片描述

用户

每个用户账号中,除了个人信息,还提供了与之关联的项目信息,比如用户jane正在开发一个项目project,项目URL为http://server/jane/project,

移除一个用户账号,有两种方法,其一,禁止用户登录gitlab,与用户相关的所有数据都将保留,提交中出现的用户邮件,也将一直保留在项目简介中,其二,从数据库和文件系统中,移除与用户相关的所有数据,当然这类情况极少出现。

项目组

gitlab项目组可视为一个项目集合,其中包含了参与该项目的所有用户,以及开发项目的信息,比如项目组training正在开发一个项目materials, url为http://server/training/materials,每个项目组会包含若干个用户,每个用户可配置不同权限,从Guest(只有提交和讨论的权限)到Owner(项目组的完全控制权),可设定不同的权限类型。

项目

一个项目对应一个Git仓库,每个项目可从属于一个用户或一个项目组,同时每个项目也设定不同的公开类型,用于控制项目页面和项目仓库的只读访问,如果是私有项目,项目管理者可配置特定用户的访问,如果是协作项目,只有登录用户才可见,如果是开源项目,所有人都可访问,注意,上述管理机制,对于git fetch命令和web UI的控制效果相同。

hook

无论是仓库层还是系统层,gitlab都支持hook,当相关事件出现时,gitlab服务将执行一次http post(其中包含了json描述),这是一种连接git仓库和gitlab实例的最佳方案,有利于构建自动化的开发环境,比如CI服务,聊天室,开发工具。

基本用法

用户首先需要在gitlab中,创建一个新项目,即点击工具栏的图标+,之后用户需要设定项目名称,项目属于哪个用户或项目组,项目的公开程度,当然这些配置能够随时修改,点击Create Project,完成项目创建。

项目创建之后,则需要推送本地仓库,使用以下命令,可将本地仓库推送到远程仓库gitlab,url即为项目的存放目录

$ git remote add gitlab https://server/namespace/project.git

推送完毕,其他协作者则能克隆该项目,

$ git clone https://server/namespace/project.git

web界面可提供项目仓库的访问,每个项目主页中,可给出项目的当前状态,以及项目文件和提交日志的查看链接。

协作开发

协作开发的最简方式,配置不同用户的权限,如果某一用户需要读取仓库,管理员可在项目设置的Members级别,加入该用户,使其获得访问级权限,如果另一用户需要开发级以上的权限,可将该用户加入Developer以上的级别,使其能上传提交和分支。

另一个更安全的协作方式,即合并请求,这种方式可使所有用户的贡献,处于可控的状态下,用户可在本地复制一个仓库副本,并创建一个新分支,完成开发和提交后,启动一个合并申请,请求将创建的新分支,合并到主分支或其他分支,同时用户并无远程仓库的推送权限,只有读取权限,这使得项目管理者能够完全控制整个项目,同时又能接纳外部用户的贡献。

在gitlab的讨论话题中,主要有两大类,合并请求和发行问题,每个合并请求都支持变更的逐行讨论(并可附带少量的代码),每行讨论又可视为一个讨论支线,所有讨论可按用户名,或是关键字排列。

4.9 第三方主机

如果用户不想自行配置一台Git服务器,可选择网上的专用主机,来存放开发项目,这类方式的优势在于,第三方主机更易配置,开发项目更易启动,无需服务器的维护和监控,既然用户已自行配置了一台服务器,也需要一个公共主机,来展示开源代码,因为这更容易吸引开源社区,为用户提供帮助。

目前已有大量的第三方主机可供选择,当然每一种都有自己的优点和缺点,可在Git wiki的GitHosting页面下,看到第三方主机的最新列表,网址,https://git.wiki.kernel.org/index.php/GitHosting

后续将介绍GitHub的细节,目前它是最大的Git线上服务器,可使用不同的存储模式,来保存用户的开发项目。

在这里插入图片描述

发布了80 篇原创文章 · 获赞 10 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/osoon/article/details/103939362