在下面的几个案例里面,为了使讲述的内容更简洁,我们将不会按照上面的规范去写很规范的提交注释,并使用-m
选项去做git commit
。听我们说,但是不要学我们做^^。
冲突发生在不同文件
你遇到的最简单的私有项目的配置可能是只有两三个开发者。这里的“私有项目”是指闭源不会对外公布的项目,但是你和其他开发者都可以访问代码库。
在这个背景下, 你可以像使用Subversion或其他代码管控系统一样去跟踪一个项目流程。你仍然可以使用比如离线上传、分支、合并等功能,并且流程是非常相似的。主要的不同是合并操作可以只需要有客户端,而不必立即同步到服务段。让我们来看一下,当两个开发者开始共同开发这个代码库会是什么样子。John是其中一位开发者,克隆了这个代码库,做了一些修改后,然后把修改上传到了本地。(在这个案例里面,我们将以…代替出现的大段出现的协议信息,以简化案例)
# John's Machine
$ git clone john@githost:simplegit.git
Initialized empty Git repository in /home/john/simplegit/.git/
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'removed invalid default value'
[master 738ee87] removed invalid default value
1 files changed, 1 insertions(+), 1 deletions(-)
另外一位开发者,Jessica,做了同样的事情, 克隆了工程并提交了自己的修改到本地:
# Jessica's Machine
$ git clone jessica@githost:simplegit.git
Initialized empty Git repository in /home/jessica/simplegit/.git/
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'add reset task'
[master fbff5bc] add reset task
1 files changed, 1 insertions(+), 0 deletions(-)
现在Jessica同步她的修改到服务器上:
# Jessica's Machine
$ git push origin master
...
To jessica@githost:simplegit.git
1edee6b..fbff5bc master -> master
John也打算同步他的修改到服务器:
# John's Machine
$ git push origin master
To john@githost:simplegit.git
! [rejected] master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'
John没能成功同步他的修改到服务器,因为Jessica也在同时做了一样的事情。这一点对曾用过Subversion的你特别重要,因为你可能注意到了两个开发者的修改没有涉及相同的文件。在这种情况下,Subversion会自动完成这个合并,但是Git却不会,所以你需要先在本地merge两个人的修改。John不得不先获取Jessica的修改,将这些修改合并再和服务器同步。
$ git fetch origin
...
From john@githost:simplegit
+ 049d078...fbff5bc master -> origin/master
这时,John的本地仓库差不多是这样的:
John’s divergent history
John知道Jessica的修改与自己的并不相关,但是他还是不得不先把这些修改合并到他本地的库中,这样才会被服务器允许同步他的修改:
$ git merge origin/master
Merge made by recursive.
TODO | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
合并动作完成了,John的上传记录现在是这个样子的:
John’s repository after merging origin/master.
现在John可以测试他的代码以保证它们仍然正常有效,并且他可以同步自己的修改到服务器了:
$ git push origin master
...
To john@githost:simplegit.git
fbff5bc..72bbc59 master -> master
最终,John的提交记录是这个样子的:
John’s history after pushing to the origin server.
冲突发生涉及同一文件
与此同时,Jessica还在开发一个主题分支。她创建了一个名为issue54的分支,然后在这个分支上提交了三次修改。她还没有获取John的修改,现在他的提交记录是这样的:
Jessica’s topic branch.
Jessica要同步John的修改:
# Jessica's Machine
$ git fetch origin
...
From jessica@githost:simplegit
fbff5bc..72bbc59 master -> origin/master
Jessica拉取了John同步到服务器上的修改,现在Jessica的提交记录是这样的:
Jessica’s history after fetching John’s changes.
Jessica认为她的分支已经准备好了,但是她想知道她必须merge哪些东西后她才可以向服务器同步她的修改。她运行git log
去查看以下内容:
$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <jsmith@example.com>
Date: Fri May 29 16:01:27 2009 -0700
removed invalid default value
“issue54..origin/master”这个参数的语法是一个日志过滤器,可以要求Git只显示在第二个参数分支origin/master上有,但是issue54分支上还没有的commits的列表。更多这个参数语法请参看”Commits Ranges”。
现在,我们从输出看到Jessica未整合的John的提交只有一个。如果Jessica要整合origin/master,这个commit将会修改她在本地的内容。现在Jessica要先合并她的主题分支到主分支中,再合并John的那个commit到她的主分支,最后同步到服务器。 首先,Jessica回到她的主分支去整合所有这些修改:
$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
Jessica可以先合并 origin/master,也可以先合并issue54分支,它们都是需要上传的,所以顺序并不重要。最后的项目状态snapshot都应该是一样的, 不管采用什么顺序,只有上传记录会有一点不同而已。Jessica选择先合并issue54分支:
$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
README | 1 +
lib/simplegit.rb | 6 +++++-
2 files changed, 6 insertions(+), 1 deletions(-)
合并成功。就像你看到的这是一个非常简单快推进, 现在Jessica要开始合入John的修改了(即origin/master主干分支的内容):
$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by recursive.
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
所有的东西都很干净的合并了,现在Jessica查看到的上传记录像是这样:
Jessica’s history after merging John’s changes
Jessica的master分支是可以访问origin/master的,所以她能够成功的同步修改到服务器(假设John在这段时间没有上传过其他修改):
$ git push origin master
...
To jessica@githost:simplegit.git
72bbc59..8059c15 master -> master
每一个开发者都提交自己的修改并且也成功合并过别人的修改。
Jessica’s history after pushing all changes back to the server
这是一个最简单的工作流程实例。你在开发一段时间后,通常是指在一个主题分支开发中,要整合这段时间完成的修改到你的master分支以便提交。当你打算分享?你的修改,你把修改合入你的master分支,获取origin/master并且合入,最后同步到服务器。这流程顺序类似下面这个图:
General sequence of events for a simple multiple-developer Git workflow.
内容有点多,翻译不够细致,后续持续改进。