Git

All You Need for Version Control

Posted by Sirin on September 20, 2024

Git: Version Control

Theoretical Part

Git’s Data Model

在Git的模型中,文件(也就是一些字节数据)被称为blob,目录称为treesnapshot就是指当前正在追踪的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