文章目录
前言
【裸仓库】指的是使用 git init --bare
命令得到的仓库,是对这种操作结果的一种直译,这个词对于刚接触 git 软件的小伙伴来说可能是第一次听说,而我也是最近实际操作了几次才渐渐理解,下面解释一下什么是裸仓库,以及为什么要使用它,有理解不对的地方还请大家指正。
普通库和裸仓库
普通库
在解释裸仓库之前,还是先来看看 git init
命令创建一个普通仓库的目录结构:
[root@VM-0-3-centos data]# git init simple
Initialized empty Git repository in /data/simple/.git/
[root@VM-0-3-centos data]# cd simple/
[root@VM-0-3-centos simple]# touch README.md
[root@VM-0-3-centos simple]# cd ..
[root@VM-0-3-centos data]# tree -a simple/
simple/
|-- .git
| |-- branches
| |-- config
| |-- description
| |-- HEAD
| |-- hooks
| | |-- applypatch-msg.sample
| | |-- commit-msg.sample
| | |-- post-update.sample
| | |-- pre-applypatch.sample
| | |-- pre-commit.sample
| | |-- prepare-commit-msg.sample
| | |-- pre-push.sample
| | |-- pre-rebase.sample
| | `-- update.sample
| |-- info
| | `-- exclude
| |-- objects
| | |-- info
| | `-- pack
| `-- refs
| |-- heads
| `-- tags
`-- README.md
10 directories, 14 files
通过上述命令操作后可以看到,git init simple
操作之后,创建了一个名为 simple
的库,simple
目录下还有一个 .git
子目录,其中包含了git系统常用的文件,在 .git
目录外是我们的工作区,可以存放我们库中待更新的文件,修改之后可以通过 git add
,git commit
等命令更新 .git
中的内容,简单来说普通库就是在工作目录 simple
中还包括一个 .git
目录,下面添加一个文件试试。
[root@VM-0-3-centos simple]# git add README.md
[root@VM-0-3-centos simple]# git commit -m"add readme file"
[master (root-commit) 9a9b255] add readme file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README.md
[root@VM-0-3-centos simple]# tree . -a
.
|-- .git
| |-- branches
| |-- COMMIT_EDITMSG
| |-- config
| |-- description
| |-- HEAD
| |-- hooks
| | |-- applypatch-msg.sample
| | |-- commit-msg.sample
| | |-- post-update.sample
| | |-- pre-applypatch.sample
| | |-- pre-commit.sample
| | |-- prepare-commit-msg.sample
| | |-- pre-push.sample
| | |-- pre-rebase.sample
| | `-- update.sample
| |-- index
| |-- info
| | `-- exclude
| |-- logs
| | |-- HEAD
| | `-- refs
| | `-- heads
| | `-- master
| |-- objects
| | |-- 9a
| | | `-- 9b255b81e994fa9af2b9c7ecbd852eb716ad6c
| | |-- e6
| | | `-- 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
| | |-- f9
| | | `-- 3e3a1a1525fb5b91020da86e44810c87a2d7bc
| | |-- info
| | `-- pack
| `-- refs
| |-- heads
| | `-- master
| `-- tags
`-- README.md
16 directories, 22 files
[root@VM-0-3-centos simple]#
添加文件之后,.git
目录中的内容发生了变化,多了3个新的object。
裸仓库
还是先从目录结构入手,我们使用 git init --bare
命令创建一个裸仓库,目录结构如下:
[root@VM-0-3-centos data]# git init --bare bare.git
Initialized empty Git repository in /data/bare.git/
[root@VM-0-3-centos data]# tree bare.git/ -a
bare.git/
|-- branches
|-- config
|-- description
|-- HEAD
|-- hooks
| |-- applypatch-msg.sample
| |-- commit-msg.sample
| |-- post-update.sample
| |-- pre-applypatch.sample
| |-- pre-commit.sample
| |-- prepare-commit-msg.sample
| |-- pre-push.sample
| |-- pre-rebase.sample
| `-- update.sample
|-- info
| `-- exclude
|-- objects
| |-- info
| `-- pack
`-- refs
|-- heads
`-- tags
9 directories, 13 files
[root@VM-0-3-centos simple]#
从目录结构来看裸仓库和普通库很像,但是仔细对比你会发现,这个裸仓库相比普通库少了一层目录,库目录 bare.git
内直接就是之前普通库 .git
目录下的内容,也就是说在 git
目录外层没有了工作目录来进行文件的增删改操作,那么我们仿照普通库操作在这个目录下提交一个文件会怎样呢?
[root@VM-0-3-centos data]# cd bare.git/
[root@VM-0-3-centos bare.git]# touch README.md
[root@VM-0-3-centos bare.git]# git add README.md
fatal: This operation must be run in a work tree
[root@VM-0-3-centos bare.git]# git status
fatal: This operation must be run in a work tree
[root@VM-0-3-centos bare.git]#
通过操作发现这个裸仓库不允许增删改库内的文件,甚至连 git status
这种命令都无法使用,统一提示了 fatal: This operation must be run in a work tree
这句话,告诉用户这些命令都必须在工作区内操作,既然不能修改,那么这个裸仓库就是“只读”的,那么它还有什么用呢?
虽然裸仓库不允许直接修改,但是可以作为服务端远程仓库,在本地克隆这个远程仓库之后再进行修改,这也是最常见的应用方式,总结来说,普通库和裸仓库的区别就是:普通库拥有工作目录,并且工作目录中可以存放正常编辑和提交的文件,而裸库只存放这些文件的commit记录,不允许用户直接在上面进行各种git操作。
使用裸仓库
前面提到裸仓库不能直接修改,但是我们可以采取修改克隆后库文件的方式达到更新的目的,下面列举两种常见的方式:
使用 git remote add 方式关联
这种方式需要我们先在本地初始化一个普通库,再使用 git remote add
命令建立关联(PowerShell命令行操作,git命令是相同的):
PS-Win D:\data\maingit\test> git init barebyremote
Initialized empty Git repository in D:/data/maingit/test/barebyremote/.git/
PS-Win D:\data\maingit\test> cd .\barebyremote\
PS-Win D:\data\maingit\test\barebyremote> git remote add origin root@82.156.125.196:/data/bare.git
PS-Win D:\data\maingit\test\barebyremote> new-item README.md
目录: D:\data\maingit\test\barebyremote
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/6/12 16:51 0 README.md
PS-Win D:\data\maingit\test\barebyremote> git add .\README.md
PS-Win D:\data\maingit\test\barebyremote> git commit -m"add readme file"
[master (root-commit) f1c41db] add readme file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README.md
PS-Win D:\data\maingit\test\barebyremote> git push -u origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 223 bytes | 223.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To 82.156.125.196:/data/bare.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
PS-Win D:\data\maingit\test\barebyremote> git log -3
commit f1c41db4699f71e9750d8d6aa2c01875ac6d4a14 (HEAD -> master, origin/master)
Author: albert <albert@163.com>
Date: Sun Jun 12 16:51:34 2022 +0800
add readme file
PS-Win D:\data\maingit\test\barebyremote>
使用 git clone 直接克隆
使用克隆方式时,按照普通库来操作就可以(PowerShell命令行操作,git命令是相同的):
PS-Win D:\data\maingit\test> git clone root@82.156.125.196:/data/bare.git barebyclone
Cloning into 'barebyclone'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
PS-Win D:\data\maingit\test> cd .\barebyclone\
PS-Win D:\data\maingit\test\barebyclone> git log -3
commit f1c41db4699f71e9750d8d6aa2c01875ac6d4a14 (HEAD -> master, origin/master, origin/HEAD)
Author: albert <albert@163.com>
Date: Sun Jun 12 16:51:34 2022 +0800
add readme file
PS-Win D:\data\maingit\test\barebyclone> ls
目录: D:\data\maingit\test\barebyclone
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/6/12 16:57 0 README.md
为什么要使用裸仓库
既然裸仓库相比于普通库只是少了工作目录,那么我们直接用普通库作为远程仓库可不可以呢?结论是可以,但是不建议,我们来实际操作一下,利用刚刚的建立的 simple
作为远端库,我们在本地clone后修改,再上传看看会遇到什么问题。
PS-Win D:\data\maingit\test> git clone root@82.156.125.196:/data/simple simple
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
PS-Win D:\data\maingit\test> cd .\simple\
PS-Win D:\data\maingit\test\simple> git log -3
commit 9a9b255b81e994fa9af2b9c7ecbd852eb716ad6c (HEAD -> master, origin/master, origin/HEAD)
Author: albert <albert@example.com>
Date: Sun Jun 12 15:53:30 2022 +0800
add readme file
PS-Win D:\data\maingit\test\simple> new-item .gitignore
目录: D:\data\maingit\test\simple
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/6/12 17:20 0 .gitignore
PS-Win D:\data\maingit\test\simple> git add .\.gitignore
PS-Win D:\data\maingit\test\simple> git commit -m"add gitignore file"
[master b5a679f] add gitignore file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 .gitignore
PS-Win D:\data\maingit\test\simple> git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 263 bytes | 263.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error:
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error:
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To 82.156.125.196:/data/simple
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to 'root@82.156.125.196:/data/simple'
PS-Win D:\data\maingit\test\simple>
克隆之后正常的修改和提交都没有问题,但是 git push
的时候报错,原因提示 ! [remote rejected] master -> master (branch is currently checked out)
,提示当前的 master
分支是检出状态,不允许直接推送。
仔细想想就会有些思路,普通库实际上包含两份数据的,一份在 .git
目录中以object形式存在,一份在工作目录中以源文件形式存在,我们每次使用 git
命令,可以保证工作目录内文件和 .git
目录数据是一致的,但是如果将普通库作为远端时,在下游提交数据时,远端库中的 .git
目录会直接更新,但是工作区却不知道此时谁在用,不能直接更新覆盖,这就造成了数据不一致的情况。
如果非得使用普通库作为服务端仓库,那么可以参照上面报错的建议,在采用额外方式保证一致性的同时,修改服务端库的 receive.denyCurrentBranch
这个git配置项,或者将服务端分支切换到一个无人使用的分支上,这样下游端就可以直接推送了。
[root@VM-0-3-centos data]# cd simple/
[root@VM-0-3-centos simple]# git checkout -b unless
Switched to a new branch 'unless'
[root@VM-0-3-centos simple]# git branch -a
master
* unless
[root@VM-0-3-centos simple]#
PS-Win D:\data\maingit\test\simple> pwd
Path
----
D:\data\maingit\test\simple
PS-Win D:\data\maingit\test\simple> git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 263 bytes | 263.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To 82.156.125.196:/data/simple
9a9b255..b5a679f master -> master
PS-Win D:\data\maingit\test\simple>
自动化部署
利用 post-receive
进行自动化部署的原理就是,git
本身提供了一些脚本接口,在某些 git
操作发生时,会调用预定脚本执行命令,相当于给 git
用户开放了接口,我们可以修改 post-receive
脚本,在修改提交后自动部署最新内容,进一步实现自动化集成。
因为前面已经介绍了很多有关裸仓库的知识,接下来我只叙述操作步骤,看了之前的介绍,这部分内容应该没什么难度了。
需求
服务端建立裸仓库,在接收到新的提交时,自动将项目部署到/data/publish/game
目录下
操作步骤
服务端远端操作
建立裸仓库 /data/repo/game.git
,对应部署目录是 /data/publish/game
[root@VM-0-3-centos data]# mkdir -p /data/repo
[root@VM-0-3-centos data]# mkdir -p /data/publish/game
[root@VM-0-3-centos data]# cd repo/
[root@VM-0-3-centos repo]# git init --bare game.git
Initialized empty Git repository in /data/repo/game.git/
[root@VM-0-3-centos repo]#
新建 /data/repo/game.git/hooks/post-receive
脚本,可以拷贝 post-receive.sample
进行修改,脚本内编写内容如下:
# 指定部署目录
DIR=/data/publish/game
git --work-tree=${DIR} clean -fd
# 强制检出
git --work-tree=${DIR} checkout --force
# 运行启动脚本
cd ${DIR}
chmod 755 start.sh
./start.sh
客户端本地操作
本地项目普通库目录结构如下,启动脚本为 start.sh
:
albert@home-pc MINGW64 /d/data/maingit/test/game (master)
$ tree game/ -a
game/
├── .git
│ ├── COMMIT_EDITMSG
│ ├── config
│ ├── description
│ ├── HEAD
│ ├── hooks
│ │ ├── applypatch-msg.sample
│ │ ├── commit-msg.sample
│ │ ├── fsmonitor-watchman.sample
│ │ ├── post-update.sample
│ │ ├── pre-applypatch.sample
│ │ ├── pre-commit.sample
│ │ ├── pre-merge-commit.sample
│ │ ├── prepare-commit-msg.sample
│ │ ├── pre-push.sample
│ │ ├── pre-rebase.sample
│ │ ├── pre-receive.sample
│ │ └── update.sample
│ ├── index
│ ├── info
│ │ └── exclude
│ ├── logs
│ │ ├── HEAD
│ │ └── refs
│ │ └── heads
│ │ └── master
│ ├── objects
│ │ ├── 53
│ │ │ └── dd8b65afe02329eb73cbe142b9359ffd2c4c70
│ │ ├── 68
│ │ │ └── 31f81503989c192a10b47ecf48bc6bfe7c2cf4
│ │ ├── 81
│ │ │ └── aaa9093e1d32996c53766fa5f943e3ea6c79b0
│ │ ├── e6
│ │ │ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│ │ ├── info
│ │ └── pack
│ └── refs
│ ├── heads
│ │ └── master
│ └── tags
├── README.md
└── start.sh
16 directories, 27 files
albert@home-pc MINGW64 /d/data/maingit/test/game (master)
$ cat start.sh
cp README.md test.txt
与远端裸仓库建立关联
albert@home-pc MINGW64 /d/data/maingit/test/game (master)
$ git remote add origin root@82.156.125.196:/data/repo/game.git
albert@home-pc MINGW64 /d/data/maingit/test/game (master)
$ git push -u origin master
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 286 bytes | 286.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0)
To 82.156.125.196:/data/repo/game.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
至此自动化部署环境已建立,当本地 game 仓库推送更新时,远端服务器会自动更新部署
快速回顾
文中主要命令收于此节,方便自己后期快速查找操作
- 服务端远程新建裸仓库
cd /data/repo
git init --bare game.git
- 本地库与远端库建立关联
git remote add origin root@82.156.125.196:/data/repo/game.git
- 新建或修改
hooks
目录下post-receive
脚本
DIR=/data/publish/game
git --work-tree=${DIR} clean -fd
git --work-tree=${DIR} checkout --force
cd ${DIR}
chmod 755 start.sh
./start.sh
总结
- 裸仓库是一个只包含提交记录,没有工作目录的仓库,适合用来做服务端远程仓库
- 裸仓库不能直接在仓库中执行修改文件的git命令,可以在客户端克隆之后修改之后再进行提交
- 自动化部署利用了git服务器提供的脚本接口,当新的推送达到时会调用
post-receive
脚本 - 配置自动化部署环境时需要注意,如果没有配置ssh免密码登陆,需要在push代码的时候输入密码
- 另外自动化部署时要注意各个文件及目录的权限,因为要运行脚本,要保证推送用户有足够的运行权限
每个人都有自己的选择,很多看似突如其来的决定,往往都是深思熟虑后的结果,每个人在自己的旅途中不断的分类、选择、分类、选择,无法逃离的坚持到最后一刻~