本地版本库完成所有的更新后,就需要再更新到远程版本库。由于Git作为一个分布式版本控制系统,远程版本库实际上充当了异地协同办公的桥梁,从此再也不用担心代码丢失的问题了。之前我的博客hexo博客同步与备份就是一个使用git操作远程版本库的例子,大家可以作为参考。不过在这篇文章中,会去系统介绍操作远程版本库的内容。
说到远程版本库,大家最熟悉的就是Github了,Github实际上就相当与一个远程版本库,托管着所有的本地版本库的提交,同时也可以把Github上的内容拉取到本地,从而实现协同工作。在使用Git操作版本库使用最多的命令如下:
git remote
git fetch
git pull
git push
其实在之前的文章的演示中,实际上也使用到了以上的操作,比如将user1和user2的修改推送到共享版本库的操作就属于操作远程版本库的例子。在操作远程版本库这部分中,重点介绍的内容如下:
1、远程分支操作
2、远程版本库你知多少
3、git push和git pull
4、远程分支与里程碑
在之前的演示中一个本地版本库关联的都是一个远程版本库,实际上一个本地版本库可以关联多个远程版本库。为了演示远程分支的操作,需要创建其他两个远程版本库,执行如下操作:
cd ../repos2/
git clone --bare hello-user1.git share.git
git clone --bare hello-user2.git share.git
这样就基于share版本库创建了两个新的版本库,为了演示需要,创建user3的工作环境:
cd to2/
mkdir user3
cd user3
git clone file:///home/rhwayfun/java/notes/repos2/share.git project
查看远程share版本库的分支可以使用如下命令:
git ls-remote --heads file:///home/rhwayfun/java/notes/repos2/share.git
输出结果如下:
55889c651eb582daa37733709479012d565dde5c refs/heads/hello-1.x
79d0a541cdf77a7403faad355b79cba8f5426cc1 refs/heads/master
79d0a541cdf77a7403faad355b79cba8f5426cc1 refs/heads/user2/dev
从输出结果可以看到远程版本库有三个分支:hello-1.x、master和user2/dev。通过命令git branch
可以查看本地的分支为master
,继续查看远程分支以及引用:
//加上-r参数就可以查看远程版本库的引用和分支啦
git branch -r
输出结果如下:
origin/HEAD -> origin/master
origin/hello-1.x
origin/master
origin/user2/dev
可以发现在远程版本库中明明有三个分支,而本地就只有master一个分支,实际上在本地已经把远程版本库的分支复制到了本地,只不过放在了另外的空间中,位于.git/refs/remotes/origin/
目录下,这样可以避免如果在本地存在相同分支的情况发生分支覆盖的危险。前缀origin/
表示这是来自远程版本库的分支或者引用,实际上这些都在.git/config
这个配置文件中进行配置的。查看配置文件:
cat .git/config
输出结果为:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote “origin”]
url = file:///home/rhwayfun/java/notes/repos2/share.git
fetch = +refs/heads/:refs/remotes/origin/
[branch “master”]
remote = origin
merge = refs/heads/master
这样就很清楚了,因为配置了remote小节,所以我们在使用git fetch等命令的时候才知道从哪里取最新的提交。注意到加粗的fetch
后有一个+
号,这个+
号表示在执行远程操作获取最新的更新时会强制进行引用的替换,什么意思呢?就是执行git fetch
时会自动补上后面的内容。所以实际在执行git fetch
的完整命令如下:
git fetch origin +refs/heads/*:refs/remotes/origin/*
远程版本库虽然也有分支,不是真正意义的分支,远程分支更像是类似里程碑的引用,因为在检出远程分支的时候会使得HEAD指针处理分离头指针的状态(DETACHED HEAD)。除了以refs/heads
为前缀的引用之外,其他非任何检出都会导致分离头指针状态的出现。
另外,基于远程版本库的分支创建的本地分支会自动追踪远程版本库,也就是说,执行git pull、git push
会自动推动到远程版本库同名非分支中。在之前文章的演示实例中,发现在本地的非master分支上执行git push等操作,是无法执行成功的,原因就在于Git找不到远程版本库同名的分支。
为了演示这个情形,在user3的工作区执行如下操作:
//基于远程hello-1.x创建本地hello-1.x分支,这样可以实现对远程版本库上的同名分支自动追踪
git checkout -b hello-1.x origin/hello-1.x
//查看是否成功切换分支
git branch
//将分支回退两个提交
git reset --hard HEAD^^
//查看状态
git status
执行git status
可以看到如下输出:
On branch hello-1.x
Your branch is behind ‘origin/hello-1.x’ by 2 commits, and can be fast-forwarded.
(use “git pull” to update your local branch)
nothing to commit, working directory clean
很清楚地告诉我们,当前分支的提交落后了远程的hello-1.x分支两个提交,这样也说明了基于远程分支创建的本地分支确实能够自动追踪远程版本库的同名分支。
之后user3在工作区执行如下操作,演示不基于远程分支创建本地分支的情况:
git pull
//基于本地分支创建的另一个本地分支
git checkout -b hello-rx hello-1.x
git reset --hard HEAD^^
git status
这回执行git status
得到的输出如下:
On branch hello-rx
nothing to commit, working directory clean
其实,很好理解,基于本地分支创建非本地分支当然不能追踪到远程分支了,而如果需要对基于本地的分支创建的分支执行追踪(注意:追踪的不是远程分支了,而是该分支的父分支),只需要加上--track
参数就可以。
同样在user3的工作区执行如下操作:
git branch -d hello-rx
git checkout master
git checkout --track -b hello-rx hello-1.x
git reset --hard HEAD^^
git status
这次加上--track
参数执行git status
后输出的结果如下:
On branch hello-rx
Your branch is behind ‘hello-1.x’ by 2 commits, and can be fast-forwarded.
(use “git pull” to update your local branch)
nothing to commit, working directory clean
从以上非演示中可以得出以下的结论:
基于远程分支创建的分支可以自动进行追踪
基于本地分支创建非分支默认不会进行追踪,除非加上--track
参数
下面说说操作版本库的CRUD:
mkdir user4
cd user4
git clone file:///home/rhwayfun/java/notes/repos2/hello-user1.git project //注册一个新的远程版本库
git remote add my-origin file:///home/rhwayfun/java/notes/repos2/hello-user1.git //查看注册的远程版本库
git remote -v //修改远程版本库的url
git remote set-url my-origin file:///home/rhwayfun/java/notes/repos2/hello-user2.git
创建新的远程版本库实际上是在.git/config
配置文件中做了如下设置:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = file:///home/rhwayfun/java/notes/repos2/hello-user1.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[remote "my-origin"]
url = file:///home/rhwayfun/java/notes/repos2/hello-user1.git
fetch = +refs/heads/*:refs/remotes/my-origin/*
注意最后中括号的内容,就是我们创建的除了origin
远程版本库之外的另外的版本库。
这两个操作是操作远程版本库使用频率最高的命令了,当执行不带参数的git push
命令时,执行的过程如下:
.git/config
配置文件中配置remote
小节,那么在执行git push相当执行git push <remote>
(<remote>
可能是origin,也可能是创建的其他远程版本库)remote
小节,则相当于执行默认的git push origin
remote
小节的url地址作为推送的地址执行不带参数的git pull
命令时,执行的过程的前两个步骤与git push
相同,只不过改成了git pull
而已。如果另外创建的远程版本库指定了fetch参数配置项,则通过该参数的引用表达式作为fetch的url。
因为在执行git pull命令在发现远程版本库与本地版本库出现分离的情况时,会使用合并操作,于是会产生第三个提交,在上一篇文章中发现变基操作相比合并操作更优雅,如果需要配置git pull默认的合并策略为变基操作,可以使用如下命令:
git config branch.<branchname>.rebase true
在前面的演示中,可以发现如果一个开发者推送了新的里程碑的时候,另一个开发者执行git pull命令时会自动拉取该里程碑到本地,有时候这可能不是我们想要的。还有比如当执行push发生冲突的时候可以使用加上-f
参数强制更新远程版本库,这在某些情况下是危险的,也可以通过设置禁止强制更新来阻止强制更新。
//设置不获取里程碑,只获取分支和提交
git fetch --no-tags file:///home/rhwayfun/java/notes/repos/hello-user2.git refs/heads/*:refs/remotes/hello-user2/*
//关闭强制更新
git config receive.denyNonFastForwards true
//关闭分支删除功能
git config receive.denyDelete true
//进行某个远程版本库更新
git config remote.user2.skipDefaultUpdate true
围绕远程版本库的操作进行了详细说明,包括执行git push
和git pull
等命令的执行过程,以及远程分支处理策略。通过实际演示说明了操作远程版本库需要注意的问题以及如何修改一些默认的Git行为。