Git实践
Git简介
Git是一个开源的分布式版本控制系统,可以高效地处理任何项目。其最初是为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。
底层实现细节
文件夹模型
在Git中,顶层文件夹被称为 tree ,而文件则被称为 blob 。在文件夹中,这形成了一个递归结构,文件夹可以包含文件夹和文件,而文件则不再有更细节的底层结构。即, tree 可以包含 tree 或 blob ,而 blob 则类似叶节点。一般Git会跟踪最顶层文件夹,被称为 root 。
历史记录模型
快照可以被认为是一个带时间戳和更改信息等元数据的文件夹副本,历史记录模型即表明了不同快照之间的联系。 Git在历史记录上不使用线性结构,而使用有向无环图来模拟历史:每个快照会指向其前一状态的快照,这允许了分支与合并。
数据结构实现
blob 可以是一个字节数组, tree 是一个从字符串到 tree 或 blob 的映射, commit 则是一个包含元信息、所有父节点和自身对应的 tree 。
Git中,用类型 object 指代上述三种数据结构的其中之一,来统一地对它们进行可以字节访问的存储。每一个 object 都会由哈希函数计算出一个 id ,以此进行访问。
此处的 id 即为"SHA-1"哈希,是一个40字符长度的十六进制字符串,需要占用160位。但是 id 本身没有具体逻辑性,所以需要有人类易读易理解的名字来对应不同对象,存在这样一个引用将有逻辑性的名字映射到 id 。
如下为伪代码示例:
type blob = array <byte>
type tree = map <string, tree | blob>
type commit = struct {
parents: array <id_of_commit>
author: string
message: string
snapshot: id_of_tree
}
type object = blob | tree | commit
references = map <string, string>
objects = map <string, object>
命令交互
开始Git之旅
在需要创建Git仓库的目录下,使用 git init 命令。这会建立一个".git"的隐藏文件夹,用于磁盘上存储所有Git的内部数据。
若对命令有疑问,可以使用 git help 命令并附上需要了解的命令,譬如 git help init 。
使用命令 git status 以得知当前仓库状况,会返回当前所在分支、未提交更改等信息。
更新版本
若创建了新文件,使用 git status 会输出有未跟踪文件的信息,这表明在下一次创建快照时该文件不会被纳入。
使用命令 git add hello.txt 可以将 "hello.txt" 文件加入暂存区,再使用 git commit 并编写详细的提交信息后,就会完成快照的创建。
若想选择性地将文件中的部分行加入暂存区,可以使用 git add -p ,这会允许用户交互式地暂存文件片段。
查看历史记录
使用 git log 可以查看版本更新历史详细记录,通过 git cat-file -p <id> 可以查看对应 id 值的 object 具体内容。
要使历史记录更易读,我们可以使用 git log --all --graph --decorate 来以图的形式展现历史记录。
使用 git diff 和其他参数可以查看当前状态和历史状态的差异,或查看历史记录中两状态的差异。
分支与合并
当初始化一个Git仓库时,默认情况下会创建一个名为"master"的引用,表示代码中的主开发分支,一般指向最新的更新。
"HEAD"则是指向当前正在查看的提交的更新,使用命令 git checkout <id> 可以改变当前工作目录的状态至该 id 对应的那个提交,"HEAD"所指对象也会改变。当然使用 git checkout 后接引用名字也可以变更,如 git checkout master 返回最新更新状态。
使用 git branch 命令会列出本地仓库中存在的所有分支。也可以用其创建分支,譬如 git branch cat 会创建一个名为"cat"的分支,指向当前所在位置的引用,即和"HEAD"指向相同。
默认情况下,更新历史后"master"指向新的更新,"HEAD"随着"master"一起变动。使用命令 git checkout cat 后,则会切换到"cat"分支,更新后"cat"指向新分支,"HEAD"随"cat"一起变动,而"master"留在原地。
用于合并的命令为 git merge 。当在一个特定提交上并且有一个其他分支是以此提交状态为基础进行修改的,合并该分支时则不会创建新快照,而是直接移动主分支的指针到对应分支。更一般情况下,会合并两个分支并创建新快照,"HEAD"随原来的主分支一起移动到新快照上。
当合并出现冲突时,可以在使用 git merge 合并完成前处理对应文件,再使用命令 git merge --continue 来完成合并。
远程仓库
命令 git remote 会列出当前仓库所知道的所有远程仓库,譬如"GitHub"上的远程副本。与远程仓库的交互,可以将本地仓库的更新推送到远程仓库,也可以从远程仓库获取更新内容同步本地仓库。
使用命令 git remote add <name> <url> 来添加远程仓库,仓库名可以自由命名,"origin"为典型名称。
使用 git push <remote_name> <local_branch>:<remote_branch> 来将本地对应分支的内容推送到远程仓库的对应分支。要简化命令,可以使用 git branch --set-upstream-to=name/branch 来使当前分支跟踪对应仓库名的对应分支,这样只需要简单的 git push 命令便可以完成操作。
命令 git clone <url> <folder_name> 可以复制一个仓库到本地,并在本地创建一个包含所有历史记录的Git仓库。使用 git clone --shallow 则只会在本地保留最新记录。
为了同步更新,需要不同地方的本地仓库拉取推送到远程仓库上的更新,以此完成多个终端的协同开发。这需要 git fetch <remote_name> 命令来将最新历史记录同步到本地,而要同步则需再使用 git merge 来将本地的"master"分支向上移动到最新处。
git pull 命令综合了上述两个步骤,相当于先执行 git fetch 再执行 git merge 。
Git配置
为命令 git config 添加一些标志来修改文本配置文件,譬如配置"GitHub"的用户名密码来更快捷地连接到远程仓库。
创建一个名为".gitignore"的文件并在其中写入想要忽略的文件名,每次添加文件时Git会忽略其中的文件。
参考资料
MIT Missing Semester