missing semester - Version Control (Git)
Git’s data model
Snapshots 快照
Git 将顶级目录中的文件和文件夹作为集合,并通过一系列快照来管理其历史记录。
在Git的术语里,文件被称作Blob对象(数据对象),也就是一组数据。目录则被称之为“tree(树)”,它将名字与 Blob 对象或树对象进行映射(使得目录中可以包含其他目录)。
快照则是被追踪的最顶层的树。例如,一个树看起来可能是这样的:
<root> (tree)
|
+- foo (tree)
| |
| + bar.txt (blob, contents = "hello world")
|
+- baz.txt (blob, contents = "git is wonderful")
Modeling history: relating snapshots
在 Git 中,历史记录是一个由快照组成的有向无环图(DAG)。
o <-- o <-- o <-- o <---- o
^ /
\ v
--- o <-- o
其中 o
表示一次提交(快照),箭头指向了当前提交的父辈。
Git 中的提交是不可改变的。但这并不代表错误不能被修改,只不过这种“修改”实际上是创建了一个全新的提交记录。而引用则被更新为指向这些新的提交。
Data model, as pseudocode
以伪代码形式表示上述数据模型,如下:
// 文件就是一组数据
type blob = array<byte>
// 一个包含文件和目录的目录
type tree = map<string, tree | blob>
// 每个提交都包含一个父辈,元数据和顶层树
type commit = struct {
parent: array<commit>
author: string
message: string
snapshot: tree
}
Objects and content-addressing
Git 中的对象可以是 blob、tree或commit:
type object = blob | tree | commit
Git 在储存数据时,所有的对象都会基于它们的 SHA-1哈希 进行寻址。
objects = map<string, object>
def store(object):
id = sha1(object)
objects[id] = object
def load(id):
return objects[id]
Blobs、tree和commit都一样,它们都是对象。当它们引用其他对象时,它们并没有真正的在硬盘上保存这些对象,而是仅仅保存了它们的哈希值作为引用。(可以通过 git cat-file -p hash_num
来进行可视化)
References
所有的快照都可以通过它们的 SHA-1 哈希值来标记。但去记住一串 40 位的十六进制字符的十分困难的。
针对这一问题,Git 的解决方法是给这些哈希值赋予人类可读的名字,也就是引用(references)。引用是指向提交的指针。与对象不同的是,它是可变的(引用可以被更新,指向新的提交)。例如,master
引用通常会指向主分支的最新一次提交。
references = map<string, string>
def update_reference(name, id):
references[name] = id
def read_reference(name):
return references[name]
def load_reference(name_or_id):
if name_or_id in references:
return load(references[name_or_id])
else:
return load(name_or_id)
在 Git 中,当前的位置有一个特殊的索引- HEAD
。
Repositories
Git 仓库的定义:对象
和 引用
。
在硬盘上,Git 仅存储对象和引用:因为其数据模型仅包含这些东西。所有的 git
命令都对应着对提交树的操作,例如增加对象,增加或删除引用。
Staging area
Git 使用一种叫做 “暂存区(staging area)”的机制,它允许您指定下次快照中要包括那些改动。
Git command-line interface
Basics
-
git help <command>
: 获取 git 命令的帮助信息 -
git init
: 创建一个新的 git 仓库,其数据会存放在一个名为.git
的目录下 -
git status
: 显示当前的仓库状态 -
git add <filename>
: 添加文件到暂存区 -
git commit
: 创建一个新的提交
-
git log
: 显示历史日志 -
git log --all --graph --decorate
: 可视化历史记录(有向无环图) -
git diff <filename>
: 显示与暂存区文件的差异 -
git diff <revision> <filename>
: 显示某个文件两个版本之间的差异 -
git checkout <revision>
: 更新 HEAD 和目前的分支
Branching and merging
git branch
: 显示分支git branch <name>
: 创建分支git checkout -b <name>
: 创建分支并切换到该分支- 相当于
git branch <name>; git checkout <name>
- 相当于
git merge <revision>
: 合并到当前分支git mergetool
: 使用工具来处理合并冲突、git rebase
: 将一系列补丁变基(rebase)为新的基线
Remotes
git remote
: 列出远端git remote add <name> <url>
: 添加一个远端git push <remote> <local branch>:<remote branch>
: 将对象传送至远端并更新远端引用git branch --set-upstream-to=<remote>/<remote branch>
: 创建本地和远端分支的关联关系git fetch
: 从远端获取对象/索引git pull
: 相当于git fetch; git merge
git clone
: 从远端下载仓库
Undo
git commit --amend
: 编辑提交的内容或信息git reset HEAD <file>
: 恢复暂存的文件git checkout -- <file>
: 丢弃修改
Advanced Git
git config
: Git 是一个高度可定制的 工具git clone --depth=1
: 浅克隆(shallow clone),不包括完整的版本历史信息git add -p
: 交互式暂存git rebase -i
: 交互式变基git blame
: 查看最后修改某行的人git stash
: 暂时移除工作目录下的修改内容git bisect
: 通过二分查找搜索历史记录.gitignore
: 指定不追踪的文件