本节学习如何使用git clone命令建立版本库克隆,以及如何使用git push和gitpull命令实现克隆之间的同步。
Git的版本库目录和工作区在一起,因此存在一损俱损的问题,即如果删除一个项目的工作区,同时也会把这个项目的版本库删除掉。一个项目仅在一个工作区中维护太危险,如果有两个工作区就会好很多。
图片中一个项目使用了两个版本库进行维护,两个版本库之间通过pull和push操作实现同步:
<1>版本库A通过克隆操作创建克隆版本库B
<2>版本库A可以通过push(推送)操作,将新提交传递给版本库B
<3>版本库A可以通过pull(拉回)操作,将版本库B中的新提交拉回到自身
<4>版本库B可以通过pull操作,将版本库A中的新提交拉回到自身
<5>版本库B可以通过push操作,将新提交传递给版本库A
Git使用git clone命令实现版本库克隆,主要有三种用法:
<1>git clone <repository> <directory>
<2>git clone --bare <repository> <director.git>
<3>git clone --mirror <repository> <director.git>
这三种用法的区别:
<1>用法一将<repository>指向的版本库创建一个克隆到<directory>目录。目录<directory>相当于克隆版本库的工作区,文件都会检出,版本库位于工作区下的.git目录中。
<2>用法二和用法三创建的版本库都不包含工作区,直接就是版本库的内容,这样的版本库称为裸版本库。一般约定俗成裸版本库的目录以.git为后缀。
<3>用法三区别于用法二之处在于用法三克隆出来的裸版本对上游版本库进行了注册,这样可以在裸版本库中使用git fetch命令和上游版本库进行持续同步。
对等工作区,不使用--bare或--mirror创建出来的克隆包含工作区,这样就会产生两个包含工作区的版本库,这两个版本库是对等的。
这两个工作区本质上没有区别,但是往往提交是在一个版本中进行的,另外一个作为备份。对于这种对等工作区模式,版本库的同步只有一种可行的操作模式,就是备份库执行git pull命令从源版本库中拉回新的提交实现版本库同步。为什么不能从版本库A向版本库B执行git push推送操作呢?看下面的操作。
<1>执行克隆命令
[root@git demo]# git clone /git/my/workspace/demo /git/my/workspace/demo-backup
Initialized empty Git repository in /git/my/workspace/demo-backup/.git/
<2>进入demo版本库,生成一些测试提交(使用--allow-empty参数可以生成空提交)
[root@git demo]# git commit --allow-empty -m "sync test 1"
[master 4f18802] sync test 1
[root@git demo]# git commit --allow-empty -m "sync test 2"
[master e8d59b4] sync test 2
<3>能够在demo版本库向demo-backup版本库执行push操作吗?
[root@git demo]# git push /git/my/workspace/demo-backup/
Counting objects: 2, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 270 bytes, done.
Total 2 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
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 /git/my/workspace/demo-backup/
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '/git/my/workspace/demo-backup/'
<4>为了实现同步,需要进入到备份库中执行git pull操作
[root@git demo]# cd /git/my/workspace/demo-backup/
[root@git demo-backup]# git pull
From /git/my/workspace/demo
06e5df8..e8d59b4 master -> origin/master
Updating 06e5df8..e8d59b4
Fast-forward
[root@git demo-backup]# git log --oneline -2
e8d59b4 sync test 2
4f18802 sync test 1
下面介绍下裸版本库,裸版本库不但可以通过克隆的方式创建,还可以通过git init命令以初始化的方式创建,之后的同步和上面的同步方式大同小异。
命令git init是用于初始化一个版本库的,之前执行git init命令初始化的版本库是带工作区的,如何以裸版本库的方式初始化一个版本库呢?答案是使用--bare参数。
<1>创建一个裸版本仓库。
[root@git repos]# git init --bare demo-init.git
Initialized empty Git repository in /path/to/repos/demo-init.git/
<2>查看一下demo-init.git下的内容
[root@git repos]# ls -F demo-init.git/
branches/ config description HEAD hooks/ info/ objects/ refs/
<3>查看一下这个版本库的配置core.bare的值
[root@git repos]# git --git-dir=/path/to/repos/demo-init.git config core.bare
true
<4>向裸版本库中推送数据,进入源版本库中执行操作
[root@git demo]# git push /path/to/repos/demo-init.git master:master
Counting objects: 21, done.
Compressing objects: 100% (13/13), done.
Writing objects: 100% (21/21), 1.90 KiB, done.
Total 21 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (21/21), done.
To /path/to/repos/demo-init.git
* [new branch] master -> master
<5>查看一下demo-init.git版本库中的提交
[root@git demo]# git --git-dir=/path/to/repos/demo-init.git log --oneline -2
e8d59b4 sync test 2
4f18802 sync test 1
<6>继续在demo中执行几次提交,再向demo-init.git中推送
[root@git demo]# git commit --allow-empty -m "sync test 5"
[master 2007420] sync test 5
[root@git demo]# git commit --allow-empty -m "sync test 6"
[master 49f01b9] sync test 6
[root@git demo]# git push /path/to/repos/demo-init.git
Counting objects: 2, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 269 bytes, done.
Total 2 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
To /path/to/repos/demo-init.git
e8d59b4..49f01b9 master -> master
为什么这次使用git push命令后面没有跟上分支名呢?这是因为远程版本库中已经不再是空版本库了,有名为master的分支,通过以下命令查看远程版本库分支
[root@git demo]# git ls-remote /path/to/repos/demo-init.git
49f01b9d4d7a4c558def93fcbe88f8e3137b5b6fHEAD
49f01b9d4d7a4c558def93fcbe88f8e3137b5b6frefs/heads/master