Don't do or do your best.

Git 学习总结

Posted on By Jason Li

在工作中使用 git 已经有 9 个月有余,但是自己就是一顿 git status,git add -A,git commit 以及 git push 和 git pull。完全是不知 所以然,这样一知半解的工作实在不是我的风格。但是虽然我一直想弄清楚 git 的工作方式,但限于时间有限,要学习的内容也多,而又没找到 合适的教程,所以一直搁置了。最近无意看到廖雪峰老师的 git 博客, 才有焕然大悟之感。进而总结一下,毕竟经过自己咀嚼的东西,自己才能消化。


Git 的工作原理


我认为要理解 git 的工作原理,只需要厘清三个概念:

  1. git 的工作方式
  2. git 快照
  3. git 的工作区以及暂存区

git 的工作方式

git 是一种分布式管理系统,那么什么是分布式版本控制系统呢?它是相对集中式版本控制系统而言的,那什么是集中式版本控制系统呢?(左图)

先说集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活, 干完活了,再把自己的活推送给中央服务器。 中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。

而分布式如上右图示,每一个电脑就相当于一个 git 仓库,各个仓库之间的地位其实是平等的,而各个仓库之间可以实现相互通信。

当然,实际工作中,没必要任意两个仓库都互相交互,这样管理就很混乱了。像我们公司,就是我们都和一个测试服务器进行交互,把最新的代码 push 给它,并把别人的提交 pull 下来。 这样乍一看分布式集中式还是蛮接近的,但理念是完全不同的,毕竟集中式只有一个中央服务器,离开了这台心脏,那就要 game over 了,而 git 每一台电脑都是一台中央服务器,谁离开 谁都是无所谓的。

另外,git 中每一个仓库都是一个完整的版本库,包含所有的版本,因此我们可以任意切换到我们需要的版本,而它又是怎么做到的呢?就是通过快照。

git 快照

究竟什么是 git 快照?这个纠结了我很久,后来看到廖老师为每次 commit 做的一个图(上图),以及知乎用户的回答才算是明白了这个问题。

  • git会把出现变更的文件直接拷贝,形成新的blob,而非与上一个版本的diff。
  • 所以一旦需要查看某版本直接load即可,而其他差异版本控制需要做merge,所以快。空间换时间。
  • 并非每个当前版本都需要做备份,如果没有改变,那么快照其实是链接上一个版本。
  • git会在隐藏目录.git里存在object里,定期会优化,保证快照空间,和读取时间的平衡。

如上图,每一次 commit 会产生一个新的版本,则上图的节点则多一个。而我怎么知道我当前是哪一个分支呢?即通过 head 指针,head 指针指向哪里,哪里就是当前分支。

其实所谓的快照说白了,就是一个备份,在文档每次 commit 的时候,则拷贝一份,并被赋值独一无二的 ID。所谓的版本控制,再这里也很清楚了,就是根据 commit 得到的 ID 来加载 不同的版本而已。

git 工作区和暂存区

就像文章的前言所说,其实我们只要会 git status、add、commit、push、pull 即可了,这是我们使用 git 工作的主线功能。而要明白这个主线功能是怎么来的,我们就有必要了解这两个区 的概念了。

上图非常形象,当我们开始工作时是在工作区下工作的,一旦我们开始,就肯定有改动,怎么知道我们的改动呢?就是用 git status 可以来查看。查看之后我们可以自行选择把那个修改加入到 暂存区,用 git add。然后 git commit 来提交本次修改,并生成唯一版本库。而 git push 则是把当前的版本推送到远程服务器上,更新它的版本库。git pull 则是相反,当远程仓库的版本高于 当前本地的版本时,需要先把远程版本取过来,更新本地版本库。这样就可以保证所有仓库都有完整的版本库了。


Git 的进阶命


于工作需要而言,git 本身也并不复杂。在了解了以上知识之后,剩下的其实就是定制化自己的工作了,只需要再用到的时候查一下相关命令即可。因此这里本人并不写出所有命令,大概梳理 出几个大概的工作方向。

关联远程仓库

为了方便我们随时提交代码,我们肯定需要一个 24 小时开机的远程仓库。那么如何关联远程仓库呢?

	git remote add origin remote-repository_address 
	本地仓库关联远程仓库 origin 地址为 remote-repository_address
	
	git push -u origin master 
	第一次需带上 -u 参数,会自动把本地的 master 分支和远程的 master 关联起来(毕竟远程可能有不知一个分支,推送到哪个分支是个问题,这里提前进行设置)

版本管理

上面已经提到过 git 的版本,那么不同的版本之间是怎么管理的呢?

	git log 查看所有版本
	git reset --hard HEAD^ 回退到上一版本(慎重操作,容易悲剧)
	git reset HEAD^ file  把 file 文件回退到上一个版本

假设我回退到上一版本之后,则回退之前的最新版本就没有了。但是假如我又想回到之前最先的版本?只要能拿到该版本的 commit id 即可。这里用了回退看似问题不大,前提 是之前最新版本已经 commit ,如果没有 commit,甚至是 add 都没有,则无法恢复到最新的版本,意味着新写的代码都会丢失。

	git reflog 查看每一条 git 命令,这里就可以得到之前最新版本的 commit_id
	git reset --hard commit_id 回到 commit_id 的版本

其他版本管理命令

	git diff 
	git checkout -- file 回退到上一状态,如果commit,就回退到 add,如果add,就回到最初

feature 分支 和 bug 分支

工作中我们是很可能遇到这样的情况,当前的工作并没有完成,暂时不可以提交,但是当前有更重要的事情要先完成,比如临时计划新加入一个功能(feature)或者修改已知的一个 bug 并且要 立马提交,这个时候我们就需要用到其他的分支了,即 feature 分支 和 bug 分支。从这里我们可以看出,所谓的 feature 分支和 bug 分支,只是从功能上进行区分的,和普通分支并没有什么本质上的区别。 当然需要在哪个分支上修改,则首先切换到该分支,然后再从该分支上新建分支。之所以要先切换到该分支,是因为不同分支的版本库可能是不同的,如果在一个另外的分支上做本分支的新建分支 并修改,是可能出现问题的,逻辑上也比较混乱。

这时候需要涉及到的命令为

	git branch 查看所有分支
	git branch newbranch 新建分支
	git checkout branch 切换分支
	git checkout -b newbranch 新建并切换分支,相当于上面两命令集合
	
	
	git stash 把当前的工作现场储存起来,需要的时候再用
	git stash list 查看储存的工作现场
	git stash apply yourstash 应用储存的分支
	
	git stash 把当前的工作现场储存起来,需要的时候再用
	git stash list 查看储存的工作现场
	git stash apply yourstash 应用储存的分支

标签管理

由前面的内容可知,我们管理版本是依靠 commit_id,但是 commit_id 是一大串无规则的排列,显然是不太方便管理的。当然,对大多数版本我们是无所谓的, 基本修改过后也不会再用了,但是有些版本是需要标记出来的,比如说每次正式对外发布的新版本。

	git tag <name> [commit id] 对版本为 commit_id 的版本进行打标签
	git tag 标签列表
	git show 查看标签信息

文件管理

显然我们仓库中有一些文件是没有必要提交的,那么提交的时候需要略过它们。只需要在当前仓库下常见一个 .gitignore 文件,写入相应规则即可。

具体可参考:GitHub 提供的部分配置

搭建 git 服务器

假如我们不想用 github 来作为 git 服务器,而是想搭建一个自己的情况下。这个建议直接参考廖老师的博客,或者去搜索吧。