然而他说,他有时会执行pull origin master,导致其他人的更改被放入他的索引或工作树中,好像是他进行了这些更改,而不是那些实际的更改作者。听起来他遇到了合并冲突,但不理解它们是什么。这是一个极其常见的问题,不幸的是,我们还不知道如何有效避免它(例如,回到SVN也无法避免它)。
究竟怎么会发生这种情况呢?让我们称你的开发者为Alice和Bob。Alice提交了1-5次提交,而Bob提交了A和X。以下是可能的历史:
1. Bob提交了A。
2. Alice提交了1-5,然后将它们推送到中央仓库。
3. Bob尝试推送A,但由于他的仓库不是最新的,所以无法推送。
$ git push
! [rejected] master -> master (non-fast-forward)
接着Bob会按照你告诉他的去做:首先是拉取代码。然而,由于提交A和提交1-5都修改了同一部分代码,所以他遇到了合并冲突。
$ git pull
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed
Bob在他的工作目录中看到了其他人的更改,但不理解这些更改是为什么存在的。
$ git status
both modified: file.txt
他认为Git出了问题,实际上是Git要求他解决合并冲突。他尝试检出新的副本,但是出现了错误:
$ git checkout HEAD file.txt
error: path 'file.txt' is unmerged
由于它无法正常工作,他尝试使用-f
:
$ git checkout -f HEAD file.txt
warning: path 'file.txt' is unmerged
成功!他提交并推送。
$ git commit
$ git push
越来越难的地方
有很多Git工具。 Visual Studio和Xcode都附带了Git集成,还有其他几个GUI,甚至有多个命令行客户端。人们在描述他们如何使用Git时也往往不太严谨,大多数开发者对Git的工作原理并不是非常熟悉。
不久前,有一篇关于这个主题的优秀论文(我很难找到它)。其中的一些结论是(请原谅我的记忆):
大多数开发者并不真正知道如何使用源代码控制,除了一些非常简单的命令(提交、推送)。
当源代码控制不按开发者的期望运作时,他们会采取各种策略,例如复制粘贴一些他们不太理解的命令来“修复问题”,添加-f
标志或删除存储库并从干净的副本重新开始。
在开发团队中,通常只有领导开发者真正知道仓库中正在发生什么。
所以这真的是一个教育挑战。
我认为Bob需要学习的关键课程是git pull
其实只是git fetch
和git merge
,即使没有报告冲突,你也可能会遇到合并冲突,需要以非常谨慎和有目的的方式解决它们。即使没有报告冲突,你也需要以非常谨慎和有目的的方式解决合并。这同样适用于没有冲突报告的情况...但现在先不要让Bob感到太过惊讶!
另一个关键课程是领导开发者需要花时间确保团队中的每个人都能正确地使用源代码控制,并理解拉取、推送、分支和合并之间的关系。这是一个举办午餐讲座的绝佳机会:准备一些幻灯片,买披萨,然后谈论Git的工作原理。