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: 指定不追踪的文件
posted @ 2021-09-29 10:38  zju_cxl  阅读(185)  评论(0编辑  收藏  举报