鸡蛋不装在一个篮子里

Git的版本库目录和工作区在一起,因此存在一损俱损的问题,即如果删除一个项目的工作区,同时也会把这个项目的版本库删除掉。一个项目仅在一个工作区中维护太危险了,如果有两个工作区就会好很多。

上图中一个项目使用了两个版本库进行维护,两个版本库之间通过拉回(PULL)和/或推送(PUSH)操作实现同步。

  • 版本库A通过克隆操作创建克隆版本库B。

  • 版本库A可以通过推送(PUSH)操作,将新提交传递给版本库B;

  • 版本库A可以通过拉回(PULL)操作,将版本库B中的新提交拉回到自身(A)。

  • 版本库B可以通过推送(PUSH)操作,将新提交传递给版本库A;

Git使用git clone命令实现版本库克隆,主要有如下三种用法:

用法1: git clone <repository> <directory>
用法2: git clone --bare <repository> <directory.git>
用法3: git clone --mirror <repository> <directory.git>

这三种用法的区别如下:

  • 用法1将<repository>指向的版本库创建一个克隆到<directory>目录。目录<directory>相当于克隆版本库的工作区,文件都会检出,版本库位于工作区下的.git目录中。

  • 用法2和用法3创建的克隆版本库都不含工作区,直接就是版本库的内容,这样的版本库称为裸版本库。一般约定俗成裸版本库的目录名以.git为后缀,所以上面示例中将克隆出来的裸版本库目录名写做<directory.git>。

  • 用法3区别于用法2之处在于用法3克隆出来的裸版本对上游版本库进行了注册,这样可以在裸版本库中使用git fetch命令和上游版本库进行持续同步。

  • 用法3只在 1.6.0 或更新版本的Git才提供。

Git的PUSH和PULL命令的用法相似,使用下面的语法:

git push [<remote-repos> [<refspec>]]
git pull [<remote-repos> [<refspec>]]

其中方括号的含义是参数可以省略,<remote-repos>是远程版本库的地址或名称,<refspec>是引用表达式,暂时理解为引用即可。在后面的章节再具体介绍PUSH和PULL命令的细节。

对等工作区

不使用--bare或者--mirror创建出来的克隆包含工作区,这样就会产生两个包含工作区的版本库。这两个版本库是对等的,如下图。

这两个工作区本质上没有区别,但是往往提交是在一个版本(A)中进行的,另外一个(B)作为备份。对于这种对等工作区模式,版本库的同步只有一种可行的操作模式,就是在备份库(B)执行 git pull 命令从源版本库(A)拉回新的提交实现版本库同步。为什么不能从版本库A向版本库B执行 git push 的推送操作呢?看看下面的操作。

执行克隆命令,将版本库/path/to/my/workspace/demo克隆到/path/to/my/workspace/demo-backup。

$ git clone /path/to/my/workspace/demo /path/to/my/workspace/demo-backup
Cloning into /path/to/my/workspace/demo-backup...
done.

进入 demo 版本库,生成一些测试提交(使用--allow-empty参数可以生成空提交)。

$ cd /path/to/my/workspace/demo/
$ git commit --allow-empty -m "sync test 1"
[master 790e72a] sync test 1
$ git commit --allow-empty -m "sync test 2"
[master f86b7bf] sync test 2

能够在 demo 版本库向 demo-backup 版本库执行PUSH操作么?执行一下git push看一看。

$ git push /path/to/my/workspace/demo-backup
Counting objects: 2, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 274 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 /path/to/my/workspace/demo-backup
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '/path/to/my/workspace/demo-backup'

翻译成中文:

$ git push /path/to/my/workspace/demo-backup
...
对方说: 错了:
拒绝更新已检出的分支 refs/heads/master 。
缺省更新非裸版本库的当前分支是不被允许的,因为这将会导致
暂存区和工作区与您推送至版本库的新提交不一致。这太古怪了。 如果您一意孤行,也不是不允许,但是您需要为我设置如下参数: receive.denyCurrentBranch = ignore|warn 到 /path/to/my/workspace/demo-backup ! [对方拒绝] master -> master (分支当前已检出)
错误: 部分引用的推送失败了, 至 '/path/to/my/workspace/demo-backup'

从错误输出可以看出,虽然可以改变Git的缺省行为,允许向工作区推送已经检出的分支,但是这么做实在不高明。

为了实现同步,需要进入到备份版本库中,执行git pull命令。

$ git pull
From /path/to/my/workspace/demo
6e6753a..f86b7bf master -> origin/master
Updating 6e6753a..f86b7bf
Fast-forward

在 demo-backup 版本库中查看提交日志,可以看到在 demo 版本库中的新提交已经被拉回到 demo-backup 版本库中。

$ git log --oneline -2
f86b7bf sync test 2
790e72a sync test 1

为什么执行 git pull 拉回命令没有像执行 git push 命令那样提供那么多的参数呢?

这是因为在执行git clone操作后,克隆出来的demo-backup版本库中对源版本库(上游版本库)进行了注册,所以当在 demo-backup 版本库执行拉回操作,无须设置上游版本库的地址。

在 demo-backup 版本库中可以使用下面的命令查看对上游版本库的注册信息:

$ cd /path/to/my/workspace/demo-backup
$ git remote -v
origin /path/to/my/workspace/demo (fetch)
origin /path/to/my/workspace/demo (push)

实际注册上游远程版本库的奥秘都在Git的配置文件中(略去无关的行):

$ cat /path/to/my/workspace/demo-backup/.git/config
...
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = /path/to/my/workspace/demo
[branch "master"]
remote = origin
merge = refs/heads/master

克隆生成裸版本库

上一节在对等工作区模式下,工作区之间执行推送,可能会引发大段的错误输出,如果采用裸版本库则没有相应的问题。这是因为裸版本库没有工作区。没有工作区还有一个好处就是空间占用会更小。

使用--bare参数克隆demo版本库到/path/to/repos/demo.git,然后就可以从 demo 版本库向克隆的裸版本库执行推送操作了。

$ git clone --bare /path/to/my/workspace/demo /path/to/repos/demo.git
Cloning into bare repository /path/to/repos/demo.git...
done.

克隆出来的/path/to/repos/demo.git目录就是版本库目录,不含工作区。

  • 看看/path/to/repos/demo.git目录的内容。
$ ls -F /path/to/repos/demo.git
branches/ config description HEAD hooks/ info/ objects/ packed-refs refs/

还可以看到demo.git版本库core.bare的配置为true。

$ git --git-dir=/path/to/repos/demo.git config core.bare
true

进入demo版本库,生成一些测试提交。

$ cd /path/to/my/workspace/demo/
$ git commit --allow-empty -m "sync test 3"
[master d4b42b7] sync test 3
$ git commit --allow-empty -m "sync test 4"
[master 0285742] sync test 4

在demo版本库向demo-backup版本库执行PUSH操作,还会有错误么?

  • 不带参数执行git push,因为未设定上游远程版本库,因此会报错:
$ git push
fatal: No destination configured to push to.
  • 在执行git push时使用/path/to/repos/demo.git作为参数。

推送成功。

$ git push /path/to/repos/demo.git
Counting objects: 2, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 275 bytes, done.
Total 2 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
To /path/to/repos/demo.git
f86b7bf..0285742 master -> master

看看demo.git版本库,是否已经完成了同步?

$ git log --oneline -2
0285742 sync test 4
d4b42b7 sync test 3

这个方式实现版本库本地镜像显然是更好的方法,因为可以直接在工作区修改、提交,然后执行git push命令实现推送。稍有一点遗憾的是推送命令还需要加上裸版本库的路径。

创建生成裸版本库

裸版本库不但可以通过克隆的方式创建,还可以通过git init命令以初始化的方式创建。之后的同步方式和上一节大同小异。

命令git init是用于初始化一个版本库的。之前执行git init命令初始化的版本库是带工作区的,如何以裸版本库的方式初始化一个版本库呢?奥秘就在于--bare参数。

下面的命令会创建一个空的裸版本库于目录/path/to/repos/demo-init.git 中。

$ git init --bare /path/to/repos/demo-init.git
Initialized empty Git repository in /path/to/repos/demo-init.git/

创建的果真是裸版本库么?

  • 看看 /path/to/repos/demo-init.git 下的内容:
$ ls -F /path/to/repos/demo-init.git
branches/ config description HEAD hooks/ info/ objects/ refs/
  • 看看这个版本库的配置core.bare的值:
$ git --git-dir=/path/to/repos/demo-init.git config core.bare
true

可是空版本库没有内容啊,那就执行PUSH操作为其创建内容呗。

$ cd /path/to/my/workspace/demo
$ git push /path/to/repos/demo-init.git
No refs in common and none specified; doing nothing.
Perhaps you should specify a branch such as 'master'.
fatal: The remote end hung up unexpectedly
error: failed to push some refs to '/path/to/repos/demo-init.git'

为什么出错了?翻译一下错误输出。

$ cd /path/to/my/workspace/demo
$ git push /path/to/repos/demo-init.git
没有指定要推送的引用,而且两个版本库也没有共同的引用。
所以什么也没有做。
可能您需要提供要推送的分支名,如 'master'。
严重错误:远程操作意外终止
错误:部分引用推送失败,至 '/path/to/repos/demo-init.git'

因为/path/to/repos/demo-init.git 版本库刚刚初始化完成,还没有任何提交更不要说分支了。当执行git push命令时,如果没有设定推送的分支,而且当前分支也没有注册到远程某个分支,将检查远程分支是否有和本地相同的分支名(如master),如果有,则推送,否则报错。

所以需要把git push命令写的再完整一些。像下面这样操作,就可以完成向空的裸版本库的推送。

$ git push /path/to/repos/demo-init.git master:master
Counting objects: 26, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (20/20), done.
Writing objects: 100% (26/26), 2.49 KiB, done.
Total 26 (delta 8), reused 0 (delta 0)
Unpacking objects: 100% (26/26), done.
To /path/to/repos/demo-init.git
* [new branch] master -> master

上面的git push命令也可以简写为:git push /pat h/to/repos/demo-init.git master。

推送成功了么?看看demo-init.git版本库中的提交。

$ git --git-dir=/path/to/repos/demo-init.git log --oneline -2
0285742 sync test 4
d4b42b7 sync test 3

好了继续在 demo 中执行几次提交。

$ cd /path/to/my/workspace/demo/
$ git commit --allow-empty -m "sync test 5"
[master 424aa67] sync test 5
$ git commit --allow-empty -m "sync test 6"
[master 70a5aa7] sync test 6

然后再向demo-init.git推送。注意这次使用的命令。

$ git push /path/to/repos/demo-init.git
Counting objects: 2, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 273 bytes, done.
Total 2 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
To /path/to/repos/demo-init.git
0285742..70a5aa7 master -> master

为什么这次使用git push命令后面没有跟上分支名呢?这是因为远程版本库(demo-init.git)中已经不再是空版本库了,而且有名为master的分支。

通过下面的命令可以查看远程版本库的分支。

$ git ls-remote /path/to/repos/demo-init.git
70a5aa7a7469076fd435a9e4f89c4657ba603ced HEAD
70a5aa7a7469076fd435a9e4f89c4657ba603ced refs/heads/master

Git-Git克隆的更多相关文章

  1. Git学习系列之Git基本操作克隆项目(图文详解)

    不多说,直接上干货! 想必,能进来看我写的这篇博文的朋友,肯定是了解过. 比如SVN的操作吧,最常见的是 检出(Check out ...), 更新 (Update ...), 以及 提交(Commi ...

  2. git怎么克隆远程仓库到本地仓库

    参考: https://blog.csdn.net/zhangzeshan/article/details/81564990 不知道为什么输入git的克隆地址就会提示密码错误 ,使用http地址就直接 ...

  3. 如何使用Git命令克隆仓库代码

    今天我的电脑装了新系统,刚装了Git到电脑上,突然有一个大胆的想法,以后不适用可视化工具了. 要逐步锻炼我的命令的操作能力,不能太依赖可视化工具. 今天先记录一下如何使用git命令克隆仓库代码 git ...

  4. git使用-克隆仓库

    1.git clone 克隆地址 克隆地址: 2.克隆命令

  5. error setting certificate verify locations: CAfile: E:/git/Git/mingw64/ssl/certs/ca-bundle.crt

    一.问题: 当git clone项目时报 error setting certificate verify locations: CAfile: E:/git/Git/mingw64/ssl/cert ...

  6. [git] git 的基础功能

    有两种方法获得一个 git 仓库:自行初始化,克隆别人已有的仓库 自行初始化 git init 克隆别人已有的库 git clone git@github.com:garrisonz/gitStudy ...

  7. [git]git project仓库迁移

    转自:https://segmentfault.com/q/1010000000124379 如果你想从别的 Git 托管服务那里复制一份源代码到新的 Git 托管服务器上的话,可以通过以下步骤来操作 ...

  8. Git Git管理码云项目

    Git  一.下载安装 1. 要使用git 先安转git 请到官网下载最新git  https://git-scm.com/downloads 2. 一路默认安装,安装完成右键查看下是否有Git. 二 ...

  9. <Git>git学习

    1.安装 分布式版本控制:工作电脑保存完整的代码,中央服务器挂了也可以使用 集中式版本控制:中央服务器挂了就凉凉 sudo apt-get install git git安装 检测安装成功 git 2 ...

  10. [git]git 分支

    什么动作,关键看你想完成什么 1. 添加新的远程分支: git push origin current_local_branch:new_remote_branch 2. 删除远程分支(冒号前必须要有 ...

随机推荐

  1. xampp中mysql重置root密码

    1.  停止mysql:用图形化工具或者在cmd命令下输入net stop mysql,在c盘根目录下输入 2.  打开cmd,切换目录到 /xampp/mysql/bin, 运行    mysqld ...

  2. selenium入门14 窗口切换

    窗口切换: 当前窗口句柄 current_window_handle 所有的窗口句柄 window_handles 切换窗口 switch_to_window() #coding=utf-8 #切换窗 ...

  3. QT学习之QT判断界面当前点击的按钮和当前鼠标坐标

    1.QObject::sender( ) 返回发送信号的对象的指针,返回类型为QObject* .可使用qobject_cast动态类型转换成对应的发送信息的对象(对象类的基类中需要有QObject) ...

  4. 腾讯CodeStar第二季前端突击队腐蚀的画解法步骤笔记

    所有题目地址:http://codestar.alloyteam.com/q2 本题内容:http://www.cnblogs.com/yedeying/p/3617593.html 腐蚀的画涉及到的 ...

  5. jsc

    之前发现一个神器,js代码测试脱离浏览器,mac终端也可以做到 调试js的时候,一般都是用浏览器的开发者工具,这里给大家推荐另外一种,抛开浏览器,在终端执行的方式,Mac内置了一个javascript ...

  6. 2018.8.18 servlet使用的会话跟踪除session外还有哪些方式

    解释HTTP HTTP是一种无连接的协议,如果一个客户端只是单纯地请求一个文件(HTML或GIF),服务器端可以响应给客户端,并不需要知道一连串的请求是否来自于相同的客户端,而且也不需要担心客户端是否 ...

  7. 利用python的numpy创建矩阵并对其赋值

    创建一个3X3的矩阵并对其赋值: x = numpy.array([[1,2,3],[4,5,6],[7,8,9]]) print x print x.shape 运行结果: [[ ] [ ] [ ] ...

  8. 尝试将 SCRIPT ompbox\private\ompmex 作为函数执行

    1.安装VS2010 2.配置ombox 在ombox路径下 mex -setup C++ 然后 make

  9. ajax(form)图片上传(spring)

    第一步:spring-web.xml <!--配置上传下载--> <bean id="multipartResolver" class="org.spr ...

  10. HttpWebRequest类之基本定义

    HttpWebRequest和HttpWebResponse类是用于发送和接收HTTP数据的最好选择.它们支持一系列有用的属性.这两个类位 于System.Net命名空间,默认情况下这个类对于控制台程 ...