10. 团队协作_快进式推送
Git 作为版本控制系统,其主要功能就是团队协作,成员之间必然就存在着数据交换,而数据交换需要协议,Git 支持的协议包括:SSH、GIT、HTTP、HTTPS 等。
在本篇,我们将模拟一个公共版本库(想象为远程服务器),多个不同的用户工作环境(想象为在不同的主机上,由不同的用户进行操作)。
下面正式演示一个共享版本库的搭建以及两个用户 user1 和 user2 在各自的工作区是如何工作并进行数据交换的,具体过程如下:
-
在 E:\git_study\repos/中创建一个共享的版本库shared.git,以裸版本库方式创建:
local1.png
- 用户
user1克隆版本库到上一级目录的user1/workspace目录中 :
local2.png
这里通过本地协议 file:// 的方式来克隆,克隆一个刚初始化完成的裸版本库会显示警告,警告正在克隆的版本库是一个空的版本库。
注意:本地协议 file:// 后面的路径为绝对路径,不能用相对路径,否则会报错。
- 设置
user.name和user.email配置变量
配置的是版本库级别的,所以不要加上 --global 或 --system 参数,和全局设置区分开来,所有用户共用全局配置,就无法模拟了。
local3.png
local4.png
- 用户
user1创建初始数据并提交,这里新建了文件README并输入内容Hello.。
local5.png
local6.png
- 用户
user1将本地版本库的提交推送到远程版本库:
local7.png
上述命令中使用了别名 origin ,其实际指向的就是 file:///e/git_study/repos/shared.git 这个远程版本库,可通过配置文件查看:
local4.png
- 用户
user2也克隆远程版本库并设置name和email:
local8.png
可以看到,用户 user2 的本地版本库和用户 user1 有同样的提交。证明 user1 已成功推送到远程, user2 也成功从远程获取到数据。
现在,两个用户的工作区是相同的,如果两人各自在本地版本库中进行独立的提交,然后再分别向远程版本库推送,会怎样?是互相覆盖还是提交失败呢?我们来模拟一下这个场景吧。
- 用户
user1先在本地提交,然后推送到远程版本库:
创建对应目录和文件 team/user1.txt ,在 user1.txt 中输入文本,进行提交和推送,如下图:
local9.png
local10.png
可以看到,用户 user1 成功推送到远程版本库,也就是说远程版本库比用户 user2 领先一个提交,如果 user2 仍基于旧数据而对本地进行改动,然后向远程版本库推送,会有什么结果呢?
- 用户
user2创建对应目录和文件team/user2.txt,在user2.txt中输入文本,进行提交:
local11.png
- 用户
user2进行推送:
local12.png
推送失败。一般情况下,推送只允许 “快进式” 推送,就是要推送的本地版本库的提交是建立在远程版本库相应分支的现有提交基础上的,即远程版本库相应分支的最新提交是本地版本库最新提交的祖先提交。由于用户 user1 推送了一个提交,所以用户 user2 的推送是非快进式的。
Git 是如何判断此次推送是否快进式的呢?
判断方式类似以下操作:用 git rev-list HEAD 命令显示本地版本库的最新提交及其历史提交,然后用 git ls-remote origin 命令显示远程版本库的引用对应的哈希值,最后判断远程的哈希值是否本地版本库的祖先提交。
local13.png
由图可知,998627b 并不是祖先提交,于是产生警告并终止了推送。
解决这个问题的方案有多个,可以强制推送,但是用户 user1 的推送会被覆盖,这种方式会形成竞争而不是协作,是不合适的,就不介绍这种解决方式了。
非快进式强制推送如果被滥用,就会成为项目的灾难,为了避免强制推送的问题,可以对版本库进行配置来禁止用户进行非快进式强制推送。将远程版本库的配置变量 receive.denyNonFastForwards 设置为 true 可以禁止任何用户进行非快进式推送操作:
local18.png
总结:合理的工作协同要避免非快进式推送,向服务器推送时发现错误,不应该使用更改历史的操作(变基、修补提交),而是采用不会改变历史提交的反转提交等操作。由于他人先推送了新的提交导致遇到非快进式推送警告时,应该先执行 git pull 获取服务器端最新的提交并和本地提交进行合并,合并成功后再向服务器进行提交推送。
- 执行
git pull,该命令会包含两个动作:获取远程版本库的提交(git fetch) ,以及将获取到的远程版本库提交与本地提交进行合并(git merge)。
local14.png
- 合并之后,看看版本库的提交关系图:
local15.png
远程服务器中的最新提交 998627b 成为了当前提交 a943da7 的父提交了,那么现在推送就是快进式的了,就不会有任何问题。
- 执行
git push命令进行推送:
local16.png
- 查看一下远程版本库的提交历史:
local17.png
可以看到,增加了用户 user2 的提交并增加了一个合并的提交。