Git: Version Control
Theoretical Part
Git’s Data Model
在Git的模型中,文件(也就是一些字节数据)被称为blob,目录称为tree,snapshot就是指当前正在追踪的root树,下面即是一个snapshot的示例
<root> (tree)
|
+- demoFile (tree)
| |
| +- cat.txt (blob, contents = "Meow")
|
+- dog.txt (blob, contents = "Woof")
Git中的版本历史是由多个snapshots构成的有向无环图
o <-- o <-- o <-- o <---- [o]
^ /
\ v
--- o <--- o
其中最左边的o
可以看作是最初的snapshot,在后续commit的时候,产生了两个branches,最后的[o]
表示两个snapshot被merge之后的结果。
Objects and Content-Addressing
这部分暂且略过,只需要知道在Git中所有的对象都是以SHA-1 hash
的方式存储了地址,通过git cat-file -p <hash>
来看到对应的内容信息。
References
显然,使用上面这种Hash的方法来查看内容不适合人类。因此Git中使用了References
来帮助查看开发过程中的commit信息。通常来讲,默认创建的master
指向的是在main branch上开发的最新的commit,使用HEAD
来指向当前所在。
Command Part
这一部分给出常用的Git指令,更多详细的内容可以查看这里。在Youtube上也有相应的lecture video。
1. Basic
$ git help
$ git init
初始化一个Git仓库,可以用ls .git
看到相应的内容
$ git status
查看目前的状况,一般是Git当前追踪的修改内容
$ git add <filename>
向暂存区(staging area)增加相应的文件
$ git commit
创建一个新的commit,关于如何写出good commit messages在后面会讲
通过$ git commit -m 'commit message'
可以直接给出相应的信息
$ git log --all --graph --decorate
以DAG的形式查看当前的修改历史,可以加入--oneline
选项来显示简略的信息
$ git diff <filename>
查看staging area中的文件的修改情况
$ git diff <revision> <filename>
查看不同$snapshot$之间的文件差异
$ git checkout <revision>
将HEAD更新到指定的位置
2. Branching and Merging
$ git branch
展示当前的branches
$ git branch <name>
创建一个新的branch,但HEAD不会移动到该位置,还需要进行checkout
$ git checkout -b <name>
创建一个新的branch并且将HEAD切换过去
$ git merge <revision>
将对应的branch merges到当前的branch上
$ git mergetool
使用Git提供的tools来进行自定义的操作从而解决merge conflicts
$ git rebase
rebase其实是merge的一种特殊情况,例如在主分支master上pull了一个节点B产生了新branch(feature),而后master上又进行了开发并commit了,之后feature分支上的开发完成。此时master上的最新状态已经不再是节点B,而feature的基底还是节点B,故需要rebase来操作,下面给出例子来解释
A --> B --> M(master)
|
+ --> C --> D(feature)
此时执行git checkout feature; git rebase master
A --> B --> M(master) --> C' --> D'(feature)
即Git会先基于B提取出feature上的修改,然后将M和这些修改做比较,处理冲突后形成C'
和D'
,对应一个人(甲)早拉取晚提交和另一人(乙)晚拉取早提交所形成的branch差异问题。
3. Remotes
$ git remote
列出remote信息
$ git remote add <name> <url>
新建一个remote的仓库
$ git push <remote> <local branch>:<remote branch>
将本地内容发送到remote并更新remote reference
$ git branch --set-upstream-to=<remote> or <remote branch>
指定默认的local和remote branch交互对象
$ git fetch
从remote处获取对象/references
$ git pull
同时进行进行fetch和merge操作
$ git clone
从远程获取仓库
4. Undo
$ git commit --amend
修改当前所在位置的commit message信息
$ git reset HEAD <file>
将一个文件从暂存区弹出
EX-1. About Github
Github对于contributing code有单独的pull requests
EX-2. About Vimdiff
+--------------------------------+
| LOCAL | BASE | REMOTE |
+--------------------------------+
| MERGED |
+--------------------------------+
<Vimdiff Demo>
vimdiff常用于处理merge conflicts,这个工具的简单使用可以看这个Blog,在StackOverflow有更加详细的解答。
How to Write Your Commit Messages
1. 标题与具体描述之间隔一个空行
对于commit message,如果修改的内容较多,建议首先用一个标题来描述本次修改的主要内容,接下来隔一个空行,然后针对commit进行具体的描述,例如:
Fix the accuracy problem in Polynomial()
To improve the accuracy and alleviate cumulative error, we use GNU MPFR library.
2. 标题不要超过50个字符
标题应该简洁的表明本次commit的操作和对象,在50个字符内完成表述,不要超过72字符(死线,超过这些的字符无法直接显示)
3. 标题开头要大写
4. 标题结束不要带标点
5. 标题使用祈使语气
使用Merge branch 'dogfeature'
而不是Merged branch 'dogfeature'
commit message应该做到能够适应以下的句型
- If Applied, this commit will ‘commit message’
例如
If applied, this commit will update getting started docs
6. 描述部分72个字符处进行手动换行
7. 描述部分应当解释What和Why,而不是How
对于commit message,应该解释做了什么修改(What)以及为什么这样修改(Why),不需要解释是怎么修改的(How),那是代码和注释应该做的事!
Learn More About Git
Pro Git 是关于Git的开源书籍,上面对于Git工具有着更加详细和全面的讲解。
Reference
Version Control (Git), MIT Missing Semester LA:2024-9-20
How to Write a Git Commit Message LA:2024-9-20
Pro Git LA:2024-9-20