说明:个人在学习git工作流的过程中,从原有的 SVN 模式很难完全理解git的协作模式,直到有一天我看到了下面的文章,好多遗留在心中的困惑迎刃而解:
我个人很感激这篇文章,所以进行了整理,希望能帮到更多的人。整篇文章由 xirong 整理自 oldratlee 的github,方便统一的学习回顾,在此感谢下面两位的贡献。 原文链接:Git Workflows and Tutorials
一、译序工作流其实不是一个初级主题,背后的本质问题其实是有效的项目流程管理和高效的开发协同约定,不仅是 这篇指南以大家在 行文中实践原则和操作示例并重,对于 关于 PS: 文中 PPS: 本指南循序渐进地讲解工作流,如果
:see_no_evil: 自己理解粗浅,翻译中不足和不对之处,欢迎建议(提交Issue)和指正(Fork后提交代码)! 二、 |
1 2 | ssh user @host git init --bare /path/to/repo.git |
确保写上有效的user
(SSH
的用户名),host
(服务器的域名或IP地址),/path/to/repo.git
(你想存放仓库的位置)。
注意,为了表示是一个裸仓库,按照约定加上.git
扩展名到仓库名上。
下一步,各个开发者创建整个项目的本地拷贝。通过git clone
命令完成:
1 | git clone ssh: //user@host/path/to/repo.git |
基于你后续会持续和克隆的仓库做交互的假设,克隆仓库时Git
会自动添加远程别名origin
指回『父』仓库。
在小明的本地仓库中,他使用标准的Git
过程开发功能:编辑、暂存(Stage
)和提交。
如果你不熟悉暂存区(Staging Area
),这里说明一下:暂存区的用来准备一个提交,但可以不用把工作目录中所有的修改内容都包含进来。
这样你可以创建一个高度聚焦的提交,尽管你本地修改很多内容。
1 2 3 4 | git status # 查看本地仓库的修改状态 git add # 暂存文件 git commit # 提交文件 <div> |
请记住,因为这些命令生成的是本地提交,小明可以按自己需求反复操作多次,而不用担心中央仓库上有了什么操作。
对需要多个更简单更原子分块的大功能,这个做法是很有用的。
与此同时,小红在自己的本地仓库中用相同的编辑、暂存和提交过程开发功能。和小明一样,她也不关心中央仓库有没有新提交;
当然更不关心小明在他的本地仓库中的操作,因为所有本地仓库都是私有的。
一旦小明完成了他的功能开发,会发布他的本地提交到中央仓库中,这样其它团队成员可以看到他的修改。他可以用下面的git push
命令:
1 | git push origin master |
注意,origin
是在小明克隆仓库时Git
创建的远程中央仓库别名。master
参数告诉Git
推送的分支。
由于中央仓库自从小明克隆以来还没有被更新过,所以push
操作不会有冲突,成功完成。
一起来看看在小明发布修改后,小红push
修改会怎么样?她使用完全一样的push
命令:
1 | git push origin master |
但她的本地历史已经和中央仓库有分岐了,Git
拒绝操作并给出下面很长的出错消息:
1 2 3 4 5 | error: failed to push some refs to '/path/to/repo.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Merge the remote changes (e.g. 'git pull' ) hint: before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. |
这避免了小红覆写正式的提交。她要先pull
小明的更新到她的本地仓库合并上她的本地修改后,再重试。
rebase
小红用git pull
合并上游的修改到自己的仓库中。
这条命令类似svn update
——拉取所有上游提交命令到小红的本地仓库,并尝试和她的本地修改合并:
1 |
|
--rebase
选项告诉Git
把小红的提交移到同步了中央仓库修改后的master
分支的顶部,如下图所示:
如果你忘加了这个选项,pull
操作仍然可以完成,但每次pull
操作要同步中央仓库中别人修改时,提交历史会以一个多余的『合并提交』结尾。
对于集中式工作流,最好是使用rebase
而不是生成一个合并提交。
rebase
操作过程是把本地提交一次一个地迁移到更新了的中央仓库master
分支之上。
这意味着可能要解决在迁移某个提交时出现的合并冲突,而不是解决包含了所有提交的大型合并时所出现的冲突。
这样的方式让你尽可能保持每个提交的聚焦和项目历史的整洁。反过来,简化了哪里引入Bug
的分析,如果有必要,回滚修改也可以做到对项目影响最小。
如果小红和小明的功能是相关的,不大可能在rebase
过程中有冲突。如果有,Git
在合并有冲突的提交处暂停rebase
过程,输出下面的信息并带上相关的指令:
1 |
|
Git
很赞的一点是,任何人可以解决他自己的冲突。在这个例子中,小红可以简单的运行git status
命令来查看哪里有问题。
冲突文件列在Unmerged paths
(未合并路径)一节中:
1 2 3 4 5 | # Unmerged paths: # (use 'git reset HEAD <some-file>...' to unstage) # (use 'git add/rm <some-file>...' as appropriate to mark resolution) # # both modified: <some-file> |
接着小红编辑这些文件。修改完成后,用老套路暂存这些文件,并让git rebase
完成剩下的事:
1 2 | git add <some-file> git rebase -- continue |
要做的就这些了。Git
会继续一个一个地合并后面的提交,如其它的提交有冲突就重复这个过程。
如果你碰到了冲突,但发现搞不定,不要惊慌。只要执行下面这条命令,就可以回到你执行git pull --rebase
命令前的样子:
1 | git rebase --abort |
小红完成和中央仓库的同步后,就能成功发布她的修改了:
1 | git push origin master |
如你所见,仅使用几个Git
命令我们就可以模拟出传统Subversion
开发环境。对于要从SVN
迁移过来的团队来说这太好了,但没有发挥出Git
分布式本质的优势。
如果你的团队适应了集中式工作流,但想要更流畅的协作效果,绝对值得探索一下功能分支工作流
的收益。
通过为一个功能分配一个专门的分支,能够做到一个新增功能集成到正式项目之前对新功能进行深入讨论。
更详细内容请点击原文连接查看。
|