一、git 解决的问题
1. 版本控制
没有 git 的时候......假如你写了一篇文章,要删除某一个段落,为了防止以后需要找回这个段落,你需要为这个文件备份。
如果你第一次删除了段落1,第二次修改了段落2,第三次添加了段落4,则需要为每一个版本都建立一个备份。(还有一个办法是注释掉修改的代码,在下面写添加的代码,但是时间久了就弄不清楚了)你的文件可能看上去是这个样子:
版本多了,你想恢复修改前的段落2,却不记得是那个备份,所以你需要一个一个文件去翻阅查找,非常麻烦。
这时你可能会写一个说明文档,文档中记录了:副本(1)——删除段落1;副本(2)——修改段落2;副本(3)添加段落3...... 有了这些摘要你能很快定位到需要的文件。
可是修改了第112行,或者是修改了114、118和119行呢?你不可能有耐心记录下每个版本修改了哪几行,所以这些修改很难定位,找起来特别费神。
版本管理可以解决以上述所有麻烦(这些麻烦是我在未接触版本管理时写代码真实遇到的)。每次有新的修改,添加版本说明并提交。从 git log 信息中你可以看到所有版本和版本摘要,看起来就像下面这样(这就不用手动备份文件和写摘要文档了):
| 版本 | 用户 | 说明 | 日期 |
|---|---|---|---|
| 1 | 张三 | 删除段落1 | 2017.1.1 |
| 2 | 李四 | 添加段落4 | 2017.1.3 |
对于任意两个版本,用 git diff 命令,可以比较两个文件的不同,这样就解决了修改定位的问题。完全自动化,Cool!
2. 代码同步
我曾经和同学 H 合作写一个小插件,刚开始没有使用 git 或者 svn,一度觉得自己没办法干活了。比如我编辑MainServlet.java,他编辑 index.jsp,现在需要运行看效果,我们就要通过 qq 把自己修改的文件传给对方,然后手动覆盖。有时我和他同时修改 index.jsp,就得让他把他的文件发给我,我合并两人的修改后,再把合并后的文件发给他。后来改动多了,我们也不记得我们修改了哪些东西,完全混乱,于是就在同一台机器上你敲一会儿,我嗑瓜子,再我敲一会儿,你嗑瓜子,严重影响了效率。有时老板要检查我们的进度,或者要帮助我们解决bug,我们就得将最新的版本打成压缩包 qq 传给总监。
后来我们用了 SVN,这些麻烦就全没有了。要同步两人的进度,只需要一人commit,另一人checkout,根本不需要自己记改了哪些东西。解决冲突同样是相对麻烦的事情,但是先 checkout,修改冲突的地方再提交,步骤清晰且有详细记录,比之前好了不知道多少。老板需要最新的代码时,也无需找我们要,直接从仓库里下载下来就好啦!
3. 分支管理
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。现在有了分支,就不用怕了。
二、Git 与 SVN比较
1. 分布式与集中式
git 是分布式版本管理系统,SVN 是集中式版本管理系统。
集中式:
在没有联网的情况下不能 commit 操作。只有中央服务器拥有完整仓库。
分布式:
每台机器上安装了同一套 git 程序,所以都能持有完整的仓库。可以方便的克隆仓库,仓库与仓库之间可以互相同步。
svn 并不能克隆仓库,只能从仓库中下载文件,即使你用 TortoiseSVN 创建了一个本地仓库,你和中央服务器的仓库也无法进行关联。
git 分布式为什么比 svn 集中式好?
svn 不联网就不能 commit。假设小明公司的中央服务器部署在内网,回家了便无法访问,但小明周末仍然勤奋地在家写代码。周六晚他想将这一天写的东西 commit,但是并不能够。周天他将代码改出bug来了,想回退到周六晚的版本,事实残忍地拒绝了他,并让他手撕bug。
集中式中,中央服务器挂了,仓库就挂了。分布式中,挂了一台机器,还能从另一台机器恢复仓库。不过据最新消息证实,有些 svn 的客户端现在也搞分布式技术,一个仓库同时存在几台服务器上,一个服务器提交了修改,几个服务器自动同步,但是这仍改不了我们工作的笔记本上没有仓库的事实。如果我们笔记本上仓库也能和远程同步,等等,那不就和 git 的模式一模一样了吗?瞎折腾啥呢少年,不如一开始就用 git。
git 中我们可以 commit 到本地仓库而不 push 到远程仓库。假设我的 getDataService.java 只写了一半,仍然会报错,这时候用 svn 将修改提交,就会影响到队友(队友更新代码 了以后说卧槽怎么 run 不起来了)。更可怕的是我修改的是已有的文件。但是不提交,我又面临着和小明一样丢失进度的风险。在 git 中只 commit 不 push 就能解决这个问题。在我的第一个实习公司,那时想自己改一改代码玩,就自己在本地创建了一个分支,随便瞎折腾,只要不提交,别人完全不知道我干了啥,不会影响到任何人。
2. 分支管理
git 的分支功能比 SVN 好用得多。
其他版本控制系统如SVN等都有分支管理,但是用过之后你会发现,这些版本控制系统创建和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。
SVN 使用分支,一般来说是我们直接手动管理文件夹,见 trunk+-branches+-tags 目录结构的使用。举个例子,如果需要基于分支1新建一个分支2,我们就手动将分支1的文件夹拷贝一份命名为分支2。如果要切换分支,我们就切换文件夹。类似这些操作,实际上是将文件夹“视作”分支,用了 move、cd 等操作,至于合并分支的 merge 命令,大概没什么人敢用吧。
但Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。
3. 权限控制
SVN 可以严格控制每个目录的访问权限(还有一套很变态的规则),git 只能控制每个仓库分支的权限。SVN 可以 checkout 任意子目录,而 git 只能 clone 某个分支。
实际上,SVN的目录权限控制和 checkout 子目录主要是为了方便了基于trunk+-branches+-tags 结构的开发。现在 git 既然已经有了好用的分支功能,目录权限控制就没有那么必要了。
补充:
- 在任意目录checkout,意味着在任何位置都可能出现.svn的隐藏文件夹,这样对整个工程的代码产生了污染。
- 也有非要像SVN那样进行目录权限管理的时候,见知乎的这个问题:https://www.zhihu.com/question/20216542 在上面 SVN 总算占据了一点优势。
4. .gitignore 文件
git 可以忽略某些文件的提交,而SVN没有这个功能。
5. 版本号
svn 的版本号是1、2、3......的自然数,git 的版本号是一堆哈希码,看起来非常反人类。 其实 git 这样设计和它的分布式结构有关,作用是防止版本号冲突。
反例如下:如果 git 用自然数作为版本号,小明在本地提交了版本1、2,小刚也在本地提交了版本1、2,两个仓库同事 push 到公司的 gitlab上时,版本号就发生了冲突。而 svn 仓库只有一个,不用与别的仓库同步,版本也一定有个提交的先后顺序,所以不会发生冲突。
工作区与暂存区
为什么要先 add 再 commit 呢?我觉得这是为了命令行的使用方便。有时一天的活儿干完,修复了两个bug,所以我要分两次提交。那么先把第一次要提交的文件 add 到暂存区,再将暂存区的所有文件进行commit。写命令行的时候,如果没有 add 只有commit,就要一口气写很多个文件,很不方便。但是图形界面不存在这样的不便,所以大多数 git 的图形化操作软件都把 add 和 commit 合二为一操作。
当然,暂存区还有一些更灵活的使用,只要理解下图,便可以依照你自己的喜好使用。