如题

Git 权限管理工具

Git 仓库的权限管理,我们可以手动直接通过 ssh key 进行管理和配置,也可以通过其他辅助工具。
在 Git 服务管理工具这个领域,主要有三种流行的方案,它们分别是:

  1. Gitosis - 轻量级, 开源项目,使用SSH公钥认证,只能做到库级的权限控制。目前项目已经停止开发,不再维护。
  2. Gitolite - 轻量级,开源项目,使用SSH公钥认证,能做到分支级的权限控制。
  3. Git + Repo + Gerrit - 超级重量级,集版本控制,库管理和代码审核为一身。可管理大型及超大型项目。

    大名鼎鼎的 Android 平台就是使用的 Git + Repo + Gerrit。对于个人,中小型企业及一些开源项目而言,如果没有特殊的要求,其 Gitolite 提供的服务已经足够用了。

下面将详细讲解怎么搭建 Gitolite 服务器。

创建管理项目用的组 ggit 和用户 ugit

建一个git用户组和用户,用来运行git服务
ggit 前面的 g 代表 group, ugit 前面的 u 代表 user,组名和用户名都加前缀,以示区别。

1
2
3
4
groupadd ggit
mkdir -p /work
useradd ugit -g ggit -d /work/ugit # /work 为单独的分区,代码放到这个目录下更加安全,可避免由于操作系统出错导致的数据丢失
passwd ugit

安装 Gitolite

客户端:在管理员电脑 win10 上,生成 SSH key 备用

  • 在管理员电脑上操作,以下为 win10 操作系统下的 Git Bash

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    $ ssh-keygen -t rsa -C "1614923608@qq.com"
    Generating public/private rsa key pair.
    Enter file in which to save the key (/c/Users/AndyChen/.ssh/id_rsa):
    Enter passphrase (empty for no passphrase):
    Enter same passphrase again:
    Your identification has been saved in /c/Users/AndyChen/.ssh/id_rsa.
    Your public key has been saved in /c/Users/AndyChen/.ssh/id_rsa.pub.
    The key fingerprint is:
    SHA256:gLOgJmZI4T5Z769yTwiSee3Ig7O/9MbNn04L0MGN0nY 1614923608@qq.com
    The key's randomart image is:
    +---[RSA 2048]----+
    | . |
    |. . .o o |
    | o..o..* E |
    |+.=.oo+.o |
    |+@ o.+ .S |
    |= * = o |
    | o =.+oo . |
    | +.o+ooo o |
    | ..o=oooo= |
    +----[SHA256]-----+
    $
  • 运行命令后,根据相关提示,输入路径和密码,如果用默认路径,如果不设置密码,则一路回车,直到生成 key。生成的 id_rsa.pub 文件备用。

服务器端:用管理员 SSH public key 来安装配置 gitolite

  1. 以 ugit 的身份登录。
  2. 确保在 ugit 用户根目录(登录之后默认就在用户根目录)。
  3. 上传上一步在 win10 下生成的公匙文件 id_rsa.pub,如我的位于:C:\Users\AndyChen\.ssh,其中 AndyChen 是计算机用户名。上传之后更改为管理员用户名。

    1
    [ugit@centOS7BasicForTest ~]$ mv id_rsa.pub andy.pub
  4. 拉取 gitolite 代码并安装

    1
    2
    3
    4
    git clone https://github.com/sitaramc/gitolite
    mkdir -p ~/bin
    ~/gitolite/install -to ~/bin
    ./bin/gitolite setup -pk andy.pub

    第一次执行,会提示:

    1
    2
    3
    4
    5
    6
    Initialized empty Git repository in /work/ugit/repositories/gitolite-admin.git/
    Initialized empty Git repository in /work/ugit/repositories/testing.git/
    WARNING: /work/ugit/.ssh missing; creating a new one
    (this is normal on a brand new install)
    WARNING: /work/ugit/.ssh/authorized_keys missing; creating a new one
    (this is normal on a brand new install)
    > 上面的警告,提示 .ssh 目录不存在并已经创建了,以及在该目录下不存在文件 authorized_keys,并创建了该文件。再次执行 `./bin/gitolite setup -pk andy.pub` ,因为已经存在目录和文件,所以不会再报警告。
    
  5. ./bin/gitolite setup -pk andy.pub 命令执行之后,会在 repositories 目录生成两个默认的仓库,其中 gitolite-admin 为远程管理的仓库,下面的介绍会用到。
    1
    2
    3
    4
    5
    6
    [ugit@centOS7BasicForTest ~]$ cd repositories/
    [ugit@centOS7BasicForTest repositories]$ ll
    total 8
    drwx------ 8 ugit ggit 4096 Jun 2 21:05 gitolite-admin.git
    drwx------ 7 ugit ggit 4096 Jun 2 21:05 testing.git
    [ugit@centOS7BasicForTest repositories]$

到这里,安装就算完成了,下面是讲如何管理(配置)项目(仓库)。

项目权限管理(配置)

clone gitolite-admin

项目 clone, 一定要用 Git Bash 来操作,我试图用 TortoiseGit 客户端可视化界面来操作,失败了,没做深入研究,如果有哪位看官知道如何才能用 TortoiseGit 操作成功,请不吝赐教(左上角有我的 QQ 邮箱)。
可能用 http 协议才会成功。

  1. 接下来,管理员把远程管理仓库 gitolite-admin clone 到本地,用管理这个仓库的方式来管理 Git 项目(仓库),包括用户权限。
    • ugit@code.apg.com:gitolite-admin.git,仓库目录后面的 .git 可以省略,可写成:ugit@code.apg.com:gitolite-admin
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      # 下载远程管理仓库, 请把 code.apg.com 换成你自己服务器的域名或者 ip
      $ git clone ugit@code.apg.com:gitolite-admin.git
      Cloning into 'gitolite-admin'...
      The authenticity of host 'code.apg.com (192.168.1.91)' can't be established.
      ECDSA key fingerprint is SHA256:njf9IMbOojDSgS2lA2EEYxD2J5pPfPqaCDKIsO7iamA.
      Are you sure you want to continue connecting (yes/no)? yes
      Warning: Permanently added 'code.apg.com,192.168.1.91' (ECDSA) to the list of known hosts.
      Enter passphrase for key '/c/Users/AndyChen/.ssh/id_rsa': # ssh key 生成时,设置了密码,所以,在 clone 时需要输入密码,否则不会。
      remote: Counting objects: 10, done.
      remote: Compressing objects: 100% (7/7), done.
      remote: Total 10 (delta 1), reused 0 (delta 0)
      Receiving objects: 100% (10/10), done.
      Resolving deltas: 100% (1/1), done.
      $

注意,命令里的 ugit 是用户名。命令结构: git clone 用户名@ip/domain:项目名,一个项目对应一个以 .git 为后缀的目录名,clone 时,可不用写 .git 后缀。
下面这张截图特意去掉项目名后面的 .git 作了测试。

添加其他开发小伙伴

  1. 其他开发小伙伴,打开 Git Bash,用命令 ssh-keygen -t rsa -C “<邮箱>” 生成 SSH key 后,将 key 文件发给管理员。建议把<邮箱>换成真实的。
  2. 管理员将各开发伙伴的 key 文件(id_rsa.pub)添加到 gitolite-admin 仓库的 keydir 目录下,并根据不同人员重新命名,注意文件名字格式为 userName.pub,userName 是配置权限时要用到的用户名。

    管理员的 key 文件默认已经添加至该目录。

  3. 项目配置信息,通过文件 gitolite.conf 来配置(管理)。
  4. 第一次用 IDEA commit 代码时,会要求填写用户名和邮箱,写自己的账户名和邮箱即可,主要是为了方便代码管理,方便通过用户名或邮箱能识别到相应的开发人员。

通过文件 gitolite.conf 配置(管理)项目

配置用户对仓库的读写权限,直接修改 conf 文件夹下的 gitolite.conf 文件。

  1. repo:项目(仓库)名,如果新添加一个 repo,在 push 之后,服务端会自动新建一个对应的同名空仓库。
  2. RW:可读可写。
  3. @all:所有人。
  4. master 和 dev 为相应的分支。

参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@admin = andy
@dev = tom jeery rose

repo gitolite-admin # 定义了版本库 gitolite-admin。
RW+ = andy # 指定只有用户 andy 才能够访问,并拥有读(R)写(W)和强制更新(+)的权限。

repo temp/.+ # 通过正则表达式定义了一组版本库,即在 temp/ 目录下的所有版本库。
C = @admin # 用户组 @admin 中的用户,可以在 temp/ 目录下创建版本库。创建版本库的用户,具有对版本库操作的所有权限。
RW = @all # 所有用户都可以读写 temp 目录下的版本库,但不能强制更新。

repo projectName # 定义的 projectName 版本库授权使用了引用授权语法。
RW+ = @admin # 用户组 @admin 对所有的分支和里程碑拥有读写、重置、添加和删除的授权。
RW master = tom # 用户 tom 可以读写 master 分支。(还包括名字以 master 开头的其他分支,如果有的话)。
RW+ dev = @dev # 用户组 @dev 可以读写、强制更新、创建以及删除 dev 开头的分支。
RW test$ = jeery # 用户 jeery 可以读写 test 分支。 (仅此分支,精确匹配)。
RW refs/tags/v[0-9] = rose # 用户 rose 可以创建以 v+数字0到9的分支

更详细的请参考官方文档

注意事项

  1. 在 CentOS 上创建公钥,将 id_rsa.pub 改名,如改为 utomcat.pub,git clone,提示:fatal: Could not read from remote repository.
    • 解决办法,重新生成名为 id_rsa.pub 的公钥,或者将 utomcat.pub 再改回成 id_rsa.pub。
  2. 如果需要在多台服务器上,以不同的用户提供 ssh key,那么,需要创建不同的用户,分别生成公钥,shell 脚本在执行的时候,可以指定用户,这样即可达到目的。
    如,以 root 的身份执行的脚本,在脚本里再以 utomcat 的身份执行别的脚本文件,指令如下:

    1
    su - utomcat -lc "sh /home/utomcat/shell-script/startTomcat.sh"

    这可是血的教训,在测试环境上执行同步代码至 gitee 项目时,因为没配置好,让好几个同事等待了 10 多 20 分钟。

用 TortoiseGit clone 项目的坑

因为 gitolite 配置的是 ssh 公钥认证的方式,而 TortoiseGit 走用户名密码的方式,所以会要求输入密码,而 gitolite 的机制天生就不是针对用户名密码认证方式,所以行不通。

*会失败,不过还是记录一下,以免看官走弯路。

  1. 以下提示框,点【yes】

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #---------------------------
    TortoiseGitPlink Security Alert
    #---------------------------
    The server's host key is not cached in the registry. You
    have no guarantee that the server is the computer you
    think it is.
    The server's ssh-ed25519 key fingerprint is:
    ssh-ed25519 256 f7:4e:73:7b:fb:eb:4d:48:19:14:86:b0:16:3a:47:1e
    If you trust this host, hit Yes to add the key to
    PuTTY's cache and carry on connecting.
    If you want to carry on connecting just once, without
    adding the key to the cache, hit No.
    If you do not trust this host, hit Cancel to abandon the
    connection.

    #---------------------------
    Yes No Cancel
    #---------------------------


  2. 接下来,输入密码,输入用户 ugit 的密码,但提示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    git.exe clone --progress -v "ugit@code.apg.com:gitolite-admin.git" "C:\workspace\gitolite-admin"

    Cloning into 'C:\workspace\gitolite-admin'...
    fatal: 'gitolite-admin.git' does not appear to be a git repository
    fatal: Could not read from remote repository.

    Please make sure you have the correct access rights
    and the repository exists.

    git did not exit cleanly (exit code 128) (109219 ms @ 2018-06-03 9:07:57 AM)

  3. 密码说明
    • 此处确定是输入 ugit 的密码,而非 ssh 公匙的密码。
    • 如果随便输入,会提示认证失败,需要再次输入,输入 ugit 的密码,则直接输入操作结果,不过,这个结果是 clone 失败。
    • 同样的执行命令,即:git clone –progress -v “ugit@code.apg.com:testing” “C:\workspace\testing”,在 Git Bash 执行,能正常 clone,可能是 TortoiseGit 的某种机制导致了这种操作的失败,没有深入研究。

切换管理员

  1. 重新在管理员电脑上生成 SSH key

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    $ ssh-keygen -t rsa -C "552087293@qq.com"
    Generating public/private rsa key pair.
    Enter file in which to save the key (/c/Users/AndyChen/.ssh/id_rsa):
    /c/Users/AndyChen/.ssh/id_rsa already exists.
    Overwrite (y/n)? y
    Enter passphrase (empty for no passphrase):
    Enter same passphrase again:
    Your identification has been saved in /c/Users/AndyChen/.ssh/id_rsa.
    Your public key has been saved in /c/Users/AndyChen/.ssh/id_rsa.pub.
    The key fingerprint is:
    SHA256:F46f/NUi3nDqIpVhTnMsM224TeiXuksjttk+YLIe4Hw 552087293@qq.com
    The key's randomart image is:
    +---[RSA 2048]----+
    | |
    | |
    | .= |
    | o@.* |
    | . S=o& . |
    | o .. o+*.+ . |
    | o E+oo*oo + .|
    | ..o.*++ B . |
    | .. oo=*= . |
    +----[SHA256]-----+
  2. 将新生成的 SSH key 改成管理员的用户名,比如我的,改为:andy.pub,覆盖到 gitolite-admin 项目的 keydir 目录下的同名文件。

  3. 这时候直接 commit,push,会要求输入 ugit 的密码,而且 push 的时候,直接提示 push 失败。接着按下面的步骤操作。
  4. 以 ugit 的身份登录 Gitolite 所在服务器,删除之前的 SSH key,再上传上一步新生成的 id_rsa.pub,更名为管理员用户名(也许这一步非必须,感兴趣的童鞋自己试一下)mv id_rsa.pub andy.pub,再执行 ./bin/gitolite setup -pk andy.pub。因为之前执行该命令配置过项目,所以本次不会有任何提示,第一次会有提示,请看上面有关第一次配置的操作描述。
  5. 再次 push gitolite-admin 的更改,不会再要求输入 ugit 用户密码,push 成功。获取其它项目的最新,也成功了,说明管理员切换成功。

切换管理员之后记

如果管理员邮箱变了,相当于重新设置管理员。切换管理员之后,将生成的 id_rsa.pub 文件替换到 Gitolite 所在服务器的 ugit 根目录下并执行 ./bin/gitolite setup -pk andy.pub 即可,不用更新到 keydir 下也可正常 push 和 pull。

以下提示,可不管:

1
2
3
4
5
6
AndyChens-MacBook-Air-on-lenovo:temp andy$ git clone ugit@code.apg.com:exam
Cloning into 'exam'...
The authenticity of host 'code.apg.com (192.168.1.91)' can't be established.
ECDSA key fingerprint is SHA256:njf9IMbOojDSgS2lA2EEYxD2J5pPfPqaCDKIsO7iamA.
Are you sure you want to continue connecting (yes/no)? yes
...

其实在本地搭建 git 服务端,邮箱的真实性是无关紧要的。但是,如果我们想要借助 gitee 上线至生产环境,就需要把项目同步到 gitee 上的私有项目,这样的话,可以在 gitee 上查看提交情况,配置 SSH key 时用的邮箱地址会与 gitee 账号的邮箱地址匹配,这样,就知道是谁在什么时候提交了什么代码了,同时也支持不在办公室的环境下也能提交代码并最终同步至办公室的 git 环境,非常方便远程项目协作和远程项目管理。

管理员 clone gitolite-admin 的坑

git describe failed; cannot deduce version number

运行 ~/gitolite/install -to ~/bin 时提示 git describe failed; cannot deduce version number

git clone 的时候,我为了加快下载速度,用了参数 --depth 1 导致。

1
git clone --depth 1 https://github.com/sitaramc/gitolite

换成:

1
git clone https://github.com/sitaramc/gitolite

要求输入密码

此错误发生在 2019-08-30,新创建 git server 的过程中。之前按上面说明操作,非常顺利,这次操作花费了一个周末的时间也没能找到原因,汗~~。我只是换了一种思路满足了我的需求。

在管理员的电脑上(workstation),clone gitolite-admin 项目时,要求输入密码

1
2
3
$ git clone ugit@192.168.1.168:gitolite-admin
Cloning into 'gitolite-admin'...
ugit@192.168.1.168's password:

相关官方文档:appendix 1: ssh daemon asks for a password

输入 ugit 用户密码进行尝试(看过官方文档才知道,这种尝试是多余的)

在输入 ugit 用户密码后,提示:’gitolite-admin.git’ does not appear to be a git repository

1
2
3
4
5
6
7
8
$ git clone ugit@192.168.1.168:gitolite-admin.git
Cloning into 'gitolite-admin'...
ugit@192.168.1.168's password:
fatal: 'gitolite-admin.git' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

继续搜索解决方案,将项目名带上路径:repositories/gitolite-admin.git

1
2
3
4
5
6
7
8
9
$ git clone ugit@192.168.1.168:repositories/gitolite-admin.git
Cloning into 'gitolite-admin'...
ugit@192.168.1.168's password:
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 10 (delta 1), reused 0 (delta 0)
Receiving objects: 100% (10/10), done.
Resolving deltas: 100% (1/1), done.

可以 clone 了,但是,push 时会报错,看了官方文档,如果在管理员电脑上(上传 ssh key 的电脑) clone gitolite-admin 项目时,提示需要密码,那一定是不正常的,即使通过添加项目路径成功 clone,在 push 时一样会报错,所以,这并不是解决方案。

push 报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git push
ugit@192.168.1.168's password:
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 811 bytes | 270.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote: Empty compile time value given to use lib at hooks/update line 6.
remote: Use of uninitialized value in require at hooks/update line 7.
remote: Can't locate Gitolite/Hooks/Update.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_per l /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at hooks/update line 7.
remote: BEGIN failed--compilation aborted at hooks/update line 7.
remote: error: hook declined to update refs/heads/master
To 192.168.1.168:repositories/gitolite-admin
! [remote rejected] master -> master (hook declined)
error: failed to push some refs to 'ugit@192.168.1.168:repositories/gitolite-admin'

官网相关错误的帮助说明https://gitolite.com/gitolite/emergencies.html#ce
摘抄如下:

  • common errors
    • WARNING: keydir/.pub duplicates a non-gitolite key, sshd will ignore it
      You used a key that is already set to give you shell access. You cannot use the same key to get shell access as well as access gitolite repos.
      Solution: use a different keypair for gitolite. There’s a wee bit more on this in the setup section of the install page. Also see why bypassing causes a problem and both the pages linked from ssh for background.
    • Empty compile time value given to use lib at hooks/update line 6
      (followed by Can’t locate Gitolite/Hooks/Update.pm in @INC a couple of lines later).
      You’re bypassing gitolite. You cloned the repo using the full path (i.e., including the repositories/ prefix), either directly on the server, or via ssh with a key that gives you shell access.
      Solution: same as for the previous bullet.
      NOTE: If you really must do it, and this is on the server and is a one-time thing, you can try gitolite push instead of git push. BUT… this defeats all gitolite access control, so if you’re going to do this often, maybe you don’t need gitolite!
  • why bypassing causes a problem

不算解决方案的解决方案

有关要求输入密码的官方文档https://gitolite.com/gitolite/sts#appendix-1-ssh-daemon-asks-for-a-password

看了很蒙,照着可能的方案操作,均以失败告终。比如:

  • 在 windows10 上新创建一个不同的管理员来生成密钥,用于管理 gitolite,失败。
  • 在 CentOS7 上创建一个新用户 ugitadmin 来生成密钥,用于管理 gitolite,失败。
  • 删除 ugit 用户,重新操作,失败。
  • 反复检查操作步骤,重新操作多次,均以失败告终。

于是

  • 我开始怀疑以前的操作步骤是否还适用于当前的 gitolite 版本。就差怀疑人生了~~
  • 反复看官方文档反复确认,为了验证文档的正确性,我在用于测试的虚拟机上按照文档操作,非常顺利,说明操作步骤是没有问题的,当然,文档本身也是没有问题的。
  • 尝试到这里,想起并进一步深刻的体会到那句业内流行的话了:如果系统有问题,终极解决方案就是重装系统,如果一次不能解决,那就再来一次,还不行,那就再重装~~
    • 估计我操作的那台 CentOS7 服务器,重新安装后再操作,一定就象我在虚拟机上操作一样顺利。
    • 不过,一想到重装,连带的软件以及项目环境都要从头配置,脑壳就开始痛了~~
  • 继续在服务器 CentOS7 上操作。后面的操作干脆彻底换一个用户名来安装 gitolite,管理 gitolite-admin 的用户也从 windows 上换到 CentOS7 上了,然后终于“大功告成”。

我的方案

原因分析

  • 也许是 ugit 这个用户本身的问题,哪怕删除再重新创建都不行,期间还重启之后再创建也不行。
  • 也许是另外一个用户 utomcat 影响到了,这个用户设置了 ssh 密钥并上传到 gitee.com 上,用于自动部署 gitee.com 上的项目。

删除旧用户,用新用户来配置

在 CentOS7 服务器上,删除用户 ugit 和 utomcat(删除前备份 utomcat home 目录下的文件),创建用户 git 用于安装 gitolite,创建用户 gitadmin 用来管理 gitolite-admin,成功!成功之后,再次创建用户 utomcat,用于管理 java 项目,创建之后,恢复该用户之前的文件,简单配置之后,utomcat 又重见天日了,就象未曾删除过一样,并且,重点是,对 gitolite 并无影响。

以下是操作步骤,用户目录采用默认配置:

1
2
useradd git
useradd gitadmin

以 gitadmin 身份登录,生成 ssh 密钥,不设置访问密码,一路回车,将 id_rsa.pub 存到 windows10,并重命名为 gitadmin.pub

1
ssh-keygen -t rsa

以 git 身份登录,上传 gitadmin.pub 到根目录

1
2
3
4
git clone https://github.com/sitaramc/gitolite
mkdir -p ~/bin
~/gitolite/install -to ~/bin
./bin/gitolite setup -pk gitadmin.pub

切换到 gitadmin,执行 git ls-remote git@127.0.0.1:gitolite-admin.git,没有要求输入密码,显示如下,则表示配置成功。

1
2
3
[gitadmin@localhost ~]$ git ls-remote git@127.0.0.1:gitolite-admin.git
dee261b6777ebd135be021f7d47ce41995a4a005 HEAD
dee261b6777ebd135be021f7d47ce41995a4a005 refs/heads/master

查看安装的 gitolite 版本

1
2
3
4
5
[gitadmin@localhost ~]$ ssh git@127.0.0.1 info
hello gitadmin, this is git@localhost running gitolite3 v3.6.11-4-gef9ab68 on git 2.22.1

R W gitolite-admin
R W testing

进一步测试:

  • 在 CentOS7 服务器上以 gitadmin 身份登录,clone gitolite-admin 项目,并修改 gitolite-admin/conf/gitolite.conf,添加内容:

    1
    2
    3
    4
    5
    repo mis-ui
    RW+ = @all

    repo mis-api
    RW+ = @all
  • 把 windows10 上的 id_rsa.pub 上传至 gitolite-admin/keydir 下,并重命名为 andy.pub。

  • 提交修改(commit + push)。
  • 在 windows10 上 clone 新配置的两个项目,均未要求输入密码,说明配置成功:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    AndyChen@AndyChen MINGW64 /c/study/temp
    $ git clone git@192.168.1.168:mis-api
    Cloning into 'mis-api'...
    warning: You appear to have cloned an empty repository.

    AndyChen@AndyChen MINGW64 /c/study/temp
    $ git clone git@192.168.1.168:mis-ui
    Cloning into 'mis-ui'...
    warning: You appear to have cloned an empty repository.