Pro-Git-Notes

第4章 Git 服务器

4.8 GitLab

4.8.1 安装

GitLab 是使用数据库的 Web 应用.

可以从 这个网站 下载镜像.

4.8.2 管理

GitLab 的管理界面使用过 Web 访问的.

在浏览器中输入安装有 GitLab 的服务器的主机名或者 IP 地址, 然后使用管理员身份登陆.

默认用户名是 admin@local.host. 默认密码是 5iveL!fe

第10章 Git 内幕

.git 目录下的:

  • description 文件仅限于 GitWeb 程序使用.
  • config 文件包含配置
  • info 目录包含一个全局性排除文件, 记录不希望放在 .gitignore 文件中的忽略模式.
  • hooks 目录包含客户端或服务器钩子脚本

剩下的重要文件或目录:

  • object 目录,存储个人数据库的所有内容, 所有的对象都在这里
  • refs 目录,存储指针, 指向数据的提交对象
  • HEAD 文件,指向已检出的分支
  • index 文件,Git 用来保存暂存区信息.

10.2 Git 对象

Git 的核心就是一个简单的 “键-值” 数据存储.

用到的一个命令:

1
$ echo 'test content' | git hash-object -w --stdin 

hash-object 表明要生成一个 blob 对象,其返回一个哈希值,-w 参数表示保存这个对象,--stdin 表明从 stdin 获取输入.

SHA-1 的散列值是 40 个字符的校验和. 其由存储的内容加上头部信息计算得出.

相同的内容会得到相同的散列值. 而这个散列值只会保存一份. 其只和文件内容相关,和文件名无关. git 不会存储文件名.

显示内容的命令:

1
$ git cat-file -p 哈希值

这里的 -p 参数可能为 print, cat-file 后面必须加一个参数.

使用 -t (type) 参数可以获取类型.

10.2.1 树对象

其解决了存储文件名的问题.

树对象相当于目录,blob 对象相当于文件内容.

同样用 cat-file 查看树对象内容. 如:

可以看到,其保存了文件名. 也就是说,只有 commit 之后才会保存文件名. add 只能保存文件内容.

底层命令将文件添加至暂存区:

1
2
$ git update-index --add --cacheinfo 100644 \
哈希值 文件名

这里的 “文件名” 可以取其他的,其会最终存入树对象中.

其会修改 .git/index 文件的内容,

将树对象加入暂存区使用 read-tree 命令:

1
2
$ git read-tree --prefix=bak 树对象的哈希值
$ git write-tree

--prefix 参数即这个树对象名, 自己取即可.

10.2.2 提交对象

创建提交对象需要使用 commit-tree 命令. 需指明一个树对象的哈希值和父提交对象 (如果有).

1
$ echo 'first commit' | git commit-tree 哈希值

或:

1
$ echo 'first commit' | git commit-tree 哈希值 -p 父提交哈希值

提交对象的格式:

  • 快照顶层树对象
  • 作者信息 (user.name, user.email) 和时间戳
  • 空行
  • 提交消息

10.2.3 对象存储

Git 构造出以对象类型作为开头的头部信息:

  • 对象类型
  • 空格
  • 内容的长度
  • 空字节

Ruby 代码:

1
>> header = "blob #{content.length}\0"

Git 将头部信息和原始内容拼接在一起,计算 SHA-1 校验和.

然后用校验和的前两位作为子目录名,剩余为文件名,将原始内容用 zlib 压缩并存储在这个文件中.

因此,校验和只是标记路径,和内容无关.

10.3 Git 引用

Git 中的 “引用” (reference 或 ref) 就是用文件来存储 SHA-1 值,然后将这个文件当作一个指针.

位于 .git/refs 下.

更新某个引用,使用 update-ref 命令:

1
$ git update-ref refs/heads/master 哈希值

执行 git branch 这样的命令,Git 会使用 update-ref 命令将当前分支中最后一次提交的 SHA-1 添加到你要创建的新引用中.

10.3.1 HEAD

HEAD 文件是一个到当前所在分支的符号引用.

执行 git commit 命令时,该命令会创建一个提交对象,并把该提交对象的父对象设置为 HEAD 文件中引用所指向的 SHA-1 值. (即当前分支)

可利用 git symbolic-ref HEAD 命令来读取 HEAD 的值.

1
$ git symbolic-ref HEAD

设置:

1
$ git symbolic-ref HEAD refs/heads/test

10.3.2 标签对象

标签对象与提交对象类似,其包含:

  • 标签创建者
  • 一个日期
  • 一条消息
  • 一个指针

但标签对象通常指向的是提交对象,而不是树对象.

有两种类型的标签:

  • 注释标签
  • 轻量标签

创建一个轻量标签, 其不会创建标签对象:

1
$ git update-ref refs/tags/v1.0 哈希值

.git/refs/tags/v1.0 文件中的内容是提交对象的哈希值.

创建一个注释标签,其会创建一个标签对象.

1
$ git tag -a v1.1 哈希值 -m 'test tag'

-a 参数指明要创建一个注释标签.

.git/refs/tags/v1.1 文件中的内容是标签对象的哈希值.

10.3.3 远程引用

添加远程仓库并向其推送,Git 会将最后一次推送到该远程仓库的每一分支的值保存在 refs/remotes 目录中.

远程引用与分支 (refs/heads 目录下的引用) 的主要不同在于前者是只读,可以对其使用 git checkout,但是 Git 不会使 HEAD 指向它,因此也就无法使用 commit 命令更新远程引用.

10.4 包文件

查看对象的大小用 git cat-file -s 哈希值.

一个文件提交后,修改部分内容再次提交,新的对象并不是只保存差异.

Git 在磁盘上保存对象所采用的格式叫做 “松散式” 对象格式,但有时为了节省磁盘空间和提高操作效率,Git 会将最个这种格式的对象打包塞进一个叫做 “包文件” 的二进制文件中。

可手动执行 git gc 命令要求 Git 打包这些对象.

上述命令会删除提交了的对象,并创建包文件和索引文件,

  • 包文件中包含了从文件系统中删除的所有对象
  • 索引文件包含了针对包文件内容的偏移 (毕竟几个文件的内容都放进了一个文件中,要知道那一部分是哪一个文件)

打包可以进一步压缩空间。此时 Git 会查找名称和大小相近的文件,只保存不同版本之间的差异. 第二个版本完整地保存了文件内容,而原始版本是以差异方式保存的 (毕竟大部分访问的都是文件的最新版本).

可以用:

1
$ git verify-pack -v .git/objects/pack/pack-这是文件名

查看打包的内容.

10.5 引用规格

若添加了一个远程仓库, 如:

1
$ git remote add origin https://github.com/schacom/simplegit-progit

该命令会在 .git/config 文件中添加内容, 包括:

  • 远程仓库名称,如 origin
  • URL
  • 用于获取的引用规格

如:

1
2
3
[remote "origin"] 
url = https://github.com/schacom/simplegit-progit
fetch = +refs/heads/*:refs/remotes/origin/*

fetch 的格式为:

  • 可选的 +, 指示 Git 更新引用
  • <src>:<dst>, <src> 是远程端的引用样式, <dst> 是远程引用在本地要写入的位置

Git 获取服务器端 refs/heads/ 下的所有引用,然后将其写入到本地的 refs/remotes/origin/.

可指定多个引用规格,即拉取多个分支:

命令如下:

1
2
$ git fetch origin master:refs/remote/origin/mymaster \
topic:refs/remote/origin/mymaster

修改 .git/config 文件:

1
2
3
4
[remote "origin"]
url = https://github.com/schacom/simplegit-progit
fetch = +refs/heads/master:refs/remotes/origin/master
fetch = +refs/heads/experiment:refs/remotes/origin/experiment

10.5.1 推送引用规格

命令如:

1
$ git push origin master:refs/heads/qa/master

配置文件为:

1
2
3
4
[remote "origin"]
url = https://github.com/schacom/simplegit-progit
fetch = +refs/heads/*:refs/remotes/origin/*
push = refs/heads/master:refs/heads/qa/master

10.5.2 删除引用

从远程服务器中删除引用:

1
$ git push origin :topic

10.6 传输协议

主要使用两个传输协议:

  • “哑” (dumb) 协议
  • “智能” (smart) 协议

10.6.1 哑协议

10.6.2 智能协议

10.7 维护与数据恢复

git gc 中的 gc 是 garbage collect (垃圾回收) 的缩写. 其:

  • 收集所有的松散对象并将其放到包文件中
  • 将多个包文件合并成一个大的包文件
  • 删除没有与任何提交关联的成旧对象

可以修改 gc.autolgc.autopacklimit 来配置.

10.7.2 数据恢复

在使用 git reset --hard 哈希值 重置到一个旧的分支后,无法得知最后一次提交的 SHA-1 值。可使用 git reflog 工具查看.

每次提交或修改分支,引用日志 (reflog) 都会更新。

git update-ref 命令也会更新日志.

使用 git log -g 命令会将引用日志 (reflog) 按照正常的日志格式输出.

引用日志的内容保存在 .git/logs 中,若引用日志被删除,使用:

1
$ git fsck --full

其会检查数据的完整性,显示出所有没有被其他对象指向的对象.

10.7.3 移除对象

git clone 会下载项目的整个历史记录,包括每一个文件的每一个版本.

有人添加了一个大文件在提交历史中,就算在本次提交删除,该文件也一直存在到提交历史中.

想要彻底删除这个文件的方法,可能会破坏提交历史.

可以执行 count-objects 命令快速查看磁盘空间的使用情况:

1
$ git count-objects -v

查找大体积文件:

1
2
3
$ git verify-pack -v .git/objects/pack/pack-29_69.odx \
| sort -k 3 -n \
| tail -3

具体删除操作看书.

10.8 环境变量

10.8.1 全局行为

GIT_EXEC_PATH 决定了 Git 从哪里查找它的子程序 (如 git-commit, git-diff). 可以用:

1
$ git --exec-path

来检查当前设置.

Git 通过 HOME 查找全局配置文件.

GIT-PAGER 用于控制在命令行上显示多页输出的程序,没有设置则使用 PAGER

Git 运行 GIT_EDITOR 作为编辑器,如果没有设置,则使用 EDITOR

还有 PREFIX, GIT_CONFIG_NOSYSTEM, 具体看书.

10.8.2 仓库位置

10.8.3 路径规格

10.8.4 提交

10.8.5 网络

10.8.6 差异和合并

10.8.7 调试

10.8.8 杂项


Pro-Git-Notes
http://example.com/2022/11/04/Pro-Git-Notes/
作者
Jie
发布于
2022年11月4日
许可协议