背景
掌握 Git 不是一件容易的事情。虽然我现在 Git 用得已经比较顺手了,但回想起刚接触 Git 时(也就 4、5 年前),看到那么多纷繁复杂的功能和流程,那种望而生畏、不知何从下手的感觉仍然历历在目。
回顾一下这几年,Git 的学习还是有一个清晰的脉络的,那就是“需求推动学习”:因为接触到了更复杂的项目,需要更强大的版本控制功能来支持,所以顺理成章地掌握了更多 Git 的功能。
如果不是因为项目实际需要,很难想象我会强迫自己去把那些 Git 功能一个个“啃”下来,而且即便当时记住了,不用也很快就会忘掉。
下面先简单总结一下我的 Git 学习曲线,然后看一些对我帮助比较大的可以提高生产力的 Git 技巧和工具。
Git 学习曲线
总的来说,我的 Git 学习曲线分为以下 4 个阶段:
阶段 1:init, commit, push
作为程序员,我的第一份工作是在一个初创公司做 NLP(自然语言处理)算法相关的事情。当然,我也发明不了什么算法,只不过是调用一些软件包和工具,把现有的算法应用在我们的数据集上而已(也包括数据的收集、清洗等等)。
当时团队里只有我一个人做算法,整个 repo 下只有我一个人在提交代码。可以想象,Git 对于这样的场景实在是没什么发挥空间。
除了 git init 创建 git repo 并设置 remote url 以外,就是每次更新代码的时候执行:
$ git add .
$ git commit -m "some text"
$ git push
这几个命令全都是我在 GitHub 新建 repo 的帮助页面上复制粘贴下来的。
当时对这些命令的原理一无所知,只是觉得很麻烦,每次提交代码需要运行 3 个命令。既然每次都是运行这 3 个命令,为什么不能用一个命令解决呢?后来我就用一个 alias 把这几个命令串在一起了。
简单来说,当时我完全只是把 GitHub 当成了一个代码的“云盘”。版本控制几乎没有发挥出什么作用。
当然,即便你没有过类似的工作,在自己学习编程的时候也一定会遇到这样的场景。因为没有人一起合作,Git 能发挥作用的地方不多,自然也不会想要去掌握更多功能。
这里再补充一下:有的时候,一个人可以在这个阶段停留很久。后来我在某家非互联网公司的一个内部团队(写内部工具,不需要服务外部顾客)写 Java,当时组里只有一个前端,她从实习到工作一直都是一个人,处境和我当年非常相似。因为不需要合作,她甚至都没有使用 Git!后来项目催得紧,我帮她一起写前端,这才开始用上了公司内的 GitLab。由于对 Git 原理几乎一无所知,有一次她从网上抄来一个 git reset --hard 直接就用(那时候我也不知道这个命令是干啥的),结果把没 commit 的东西全都丢了,我花了半天才帮她恢复。
阶段 2:status, log, reset
过了一段时间,我在某家大型互联网公司做服务端测试,主要工作是写一些 API 测试脚本。
这时候就涉及到合作的问题了,因为整个测试团队(大概 7、8 个人)都在同一个 repo 上提交代码。每次你在 push 之前,git 都会提醒你要先 pull 下来一大堆别人的改动,这种感觉很新鲜,也很有意思!
不过虽然大家都在同一个 repo 上提交代码,但彼此却完全互不干涉。API 测试脚本之间是独立的,我写的部分和别人写的完全没有交集,也几乎不需要引入任何共用组件。所以当时大家都在 master 上提交代码,既没有其他分支也没有 PR,而且没有任何需要解决的冲突。
当时的项目有个问题:repo 里没有 .gitignore 。我还保留着之前的习惯,每次写完代码就 git add . 然后全都提交上去了,导致我提交了很多不应该提交的文件。后来同事提醒我说,提交之前先用 git status 看一下准备提交的文件都有哪些。(当然,我还是把 .gitignore 加上了)。
我就这样学会了用 git status 、git log 这些命令,也明白了并不总是要用 git add . ,因为你可能只需要提交部分代码。
从而也就明白了 Git 里为什么要有 stage 这个阶段,这就好比是把子弹上了膛,准备好发射了。如果每次都提交全部改动,那 stage 阶段确实就没有意义了;但如果你只想提交部分文件的改动,那么就可以只把这些文件加入 stage 区,这样在 commit 的时候,那些你不想提交的改动就不会被提交。
这样一来,也就自然明白了 git reset :把 staged 的改动取消 stage,这是可逆、无害的,可以随便用;以及 reset --hard ,这是把改动抹掉,是不可逆的,不能随便用!真不明白为啥要这两个操作绑定在同一个命令上,如果 reset --hard 改叫 discard 之类的名字应该会更准确一些吧!
阶段 3:branch, PR, conflicts, stash
这个阶段,可以说是一名正常的程序员所处的典型阶段了。前面两个阶段,还没有进 Git 的大门呢!
这时我来到另一家大型互联网公司做前端开发,第一次接触了多人一起开发一个项目的感觉(虽然当时还是一个小项目,其实只有两个人)。两个人可能会在一个 repo 下同时开发几个不同 feature,所以需要在不同分支上分别开发,便于分别部署和测试。
后来我在同一个项目组下转到后端开发,就更标准一些了,需要提 Merge Request 才可以把代码提交到 master 上,MR 的时候也就会有 code review。
这个阶段,也就逐渐熟悉了 git checkout 之类分支相关的命令(当然,手输很累,后来都用 IDE 或者 GitHub Desktop 来切换分支了),以及解决冲突、git stash 之类的命令。
在这个阶段掌握这些命令可以说是顺利成章了。但可以想象,如果一开始就让我学习这些命令,没有实际项目作为背景,那一定会搞得云里雾里!
阶段 4:cherry-pick, rebase…
后来也逐渐掌握了一些比较“高级”的 Git 命令,比如 cherry-pick, rebase 之类的。当然了,真正用过一两次之后,也就觉得这些命令没有什么高级或者神秘了。
到这个阶段,终于可以说 Git 使用得“比较顺手”了。但即便如此,很多时候还是要到 Stack Overflow 上查一些具体命令,因为那些使用机会不多的命令我也不会刻意去记,记也记不过来。
小结
本来还想写一写提高生产力的 Git 技巧和工具呢,没想到已经写了这么多,就放到下次再写吧!
|