最近发现发现很多同学使用git存在误区,很多人使用git已经很久了,在push代码时看到merge的错误信息依然会恐慌。对很多新手程序员来说,git对他们的作用,其实就是备份代码而已。其实备份代码,用网盘就可以了,自动同步,特别方便。

理解Git

这篇文章是写给git新手看的。现在很多新手程序员没有接触过svn,我觉得如果先入手svn,再使用git可能容易理解“版本控制”,因为svn足够简单,容易上手。Git是分布式,相对于svn来说是太复杂了。

Git和svn的不同之处大致如下:

  • 分布式 vs 中心式
  • 全量clone vs 部分check
  • 本地merge vs 服务器merge
  • SHA-1 vs 序号id

有的同学一听到svn就嗤之以鼻,觉得git是天之骄子。其实svn的中心式架构在大规模项目开发中很有优势,git天生适合小团队开发,如果项目团队超过5个人,merge代码是很频繁的,非常影响工作效率。如果团队管理不善,代码分工不明确,每次commit一大佗代码merge起来都是灾难性的,代码的版本树复杂到界面显示不下。

得益于中心式架构,svn可以只check某一个目录,一个svn可以把整个项目涉及的所以文档、代码、资源统统管理起来,一个svn可以轻松管理前端、后端、app多个project。对于git,一般都是老老实实的一个project一个git,然后再建一个git专门存放各种文档资源。所以有些大厂比如腾讯,至今还在用svn,不是没有道理的。

那既然看起来git有这么多缺点,大家还要用git呢?因为git功能实在太强大了,相比而言,svn就是个玩具。Git的fork、pull request是非常伟大的发明,没有git就没有互联网百花齐放、百家争鸣的繁荣景象,熟练使用git是程序员翱翔于知识海洋(github)的前提。如果是快速发展的小团队,使用git就太合适了,使用git意味着与开源世界无缝衔接,站在巨人的肩膀上。

Git常用操作

Git功能强大,比svn强大得多,有很多不容易理解的功能,并不是说有多难,而是因为平常工作中几乎用不到,偶尔用一下还特别容易出错,一不小心把把代码库搞趴掉。对于新手来说,我觉得熟练使用以下操作就足够了。

  • 克隆(clone)
  • 提交(commit)
  • 推送(push)
  • 合并(merge)
  • 设置忽略(.gitignore)
  • 分支操作(branch)

Clone、commit、push大家都很熟练了。日常开发过程中,我强烈新手找一款自己喜欢的GUI客户端,比如SourceTree、TortoiseGit、GitExtensions,windows下Git自带的Git GUI也可以。非常不建议在实际项目中使用命令行完成上述操作。有些同学看了网上的教程后就上手了,直接git commit -a提交所有,这是不可取的,因为实际开发过程中,修改的代码不检查一股脑儿提交是很危险的,这些代码中可能有未经测试的代码、语法错误,甚至是为了排查bug临时修改的代码,很多团队没有单元测试的,这种情况下特别容易因为失误提交错误的代码。有时候一些配置项,比如debug标识、测试环境配置等,push后会影响其他同事。所以一定要用GUI提交代码,提交前仔细过一遍修改的代码差异,尤其是一些关键的类、公用的代码,磨刀不误砍柴工。IDE自带的Git GUI,就很好用,能满足大部分的日常操作。

Merge——新手的拦路虎

Merge操作是很多新手入门的门槛。不要畏惧merge,merge不是错误,除非一个人独自开发,否则merge是不可避免的。多人开发的情况下,一般大家都负责各自的模块,各自有各自的代码目录,此时git一般会自动完成merge,只需要commit一下merge状态就行了,SourceTree的话,merge后会自动commit、push,特别人性化。不过更多的时候,git无法自动merge,此时代码文件会出现类似如下的错误:

此时就要人工合并代码,删去错误的代码和箭头,然后commit就可以了。当然,用工具不代表不需要掌握git命令,SourceTree、TortoiseGit等工具其实就是把这些常用命令封装了,每次操作时都会显示当前实际执行的命令,开发人员一定要多留意,了解工具的本质。工具使用出现问题不要慌,不要一遍又一遍的点重试,工具上都会显示错误信息的,仔细阅读一下提示信息,很容易找到处理办法。

Merge之后,代码树上的时间线会经较形象的展示出分开与合并的效果,有些同学觉得不好看,就是喜欢一条时间线,看到时间线上出现疙瘩就很难过。这能说啥呢,习惯了就好了,如果对此有洁癖,也有办法解决,比如stash后再拉代码、比如rebase衍合提交。这些操作流程稍多一些,搞不好会搞成一团粥,操作Git要胆大心细。Git的魅力就在于灵活,方法不惟一,不同能力层次的人都可以找到适合自己的用法。

.gitignore——提前配置

设置忽略就是配置.gitignore文件。需要注意的是.gitignore配置要趁早,如果文件已经存在了,这个时候再设置忽略就比较麻烦。此时必须要先删除该文件,然后commit、push删除服务器文件,然后再重新添加文件。有的同学对ignore文件里的通配符不太熟悉,其实可以在GUI commit界面上设置忽略。不过用工具操作.gitignore经常会出一些诡异的现象,经常ignore不生效,一般是因为git文件太多了,文件状态不能及时刷新。这种问题确实比较头疼,可以刷新一下git状态,clean一下看看效果。

有的同学可能还会有另一种设置忽略的需求,就是代码库里要保留某个文件,但是不希望提交对这个文件的修改,说实话这个需求.gitignore无法做到。因为只要文件在代码库里存在了,即使用设置了.gitignore,代码依然会被跟踪。查git命令发现还是有办法的,可以在本地仓库git update-index --assume-unchanged来忽略本地的修改。我觉得这个方法不太实用。毕竟这个不需要修改的文件,一般只是大多数时候不修改而已,有的时候还是要改的。这个时候因为设置了忽略反而容易忘掉,容易捅篓子:本地运行正常,服务器上就不行,代码明明全提交了呀,你看暂存区空空如也,最后排查一圈发现是被忽略了,气到吐血。所以还是实用第一,不如在commit代码的时候仔细过一遍代码,把不需要提交的文件给revert了,这样要稳妥些。

分支操作——很容易

很多项目会都会有同时开发多个版本的需求,此时就需要checkout新的分支。有的团队各个开发人员都建立自己的分支,开发完功能后再合并到主分支。相对于checkout新分支,分支的合并略复杂一点,其实也不复杂,就是一次普普通通的merge操作而已。如果分支分得时间太长了,合并会有一点麻烦,可能两边代码可能已经改得面目全非了,这种情况下合并代码还是把大伙一起叫过来围在身边一起操作比较靠谱。

有时候合并分支并不想把开发分支上的所有代码都合并到主分支上去,可能只需要合并某一个文件,或者某一次commit。或者通俗的说就是要把一个commit,同时提交到两个分支上去。这种需求也很常见,比如紧急修改一些严重bug、修改重要的配置文件等等。这个时候就要用到cherry-pick了,cherry-pick可以直接单独摘取某一次提交合并到其他分支。Git的分支管理比Svn强大太多了,Git分支管理的一种妙用就是配合自动构建,实现Devops。

上图显示一种比较简单持续集成方式,开发人员在开发分支上开发,开发完成后按照产品经理的要求把相应的版本merge到发布分支,自动构建系统会自动check发布分支实现构建和发布。图中的“提交8”就是一次cherry-pick,实际实践过程中肯定不止这两个分支,比如可能会有测试分支,灰度发布分支等等。

总结

文章介绍了一些git常见用法,用于培训新人,满足日常开发需要,Git功能强大,使用Git一定要胆大心细,多多体验,Git上手其实很容易。学会git、善用git可以大大提高我们的开发效率,Git贯穿于软件开发过程的方方面面,一个程序员如果精通Git,那他的综合素养一定不会差的。