关于版本控制

很久之前在团队的分享,整理一下分享给大家!

什么是版本控制

从字面上理解,就是记录文件或项目的修改历史的系统,方便追踪文件的变更,例如软件开发的代码文件、文档撰写中的文档、设计过程中的图形文件,这些文件会不断被修改。版本控制能够记录谁在什么时候对文件做了什么修改,这样可以方便地回溯到之前的某个版本,或者查看文件是如何逐步演变的。

人为版本控制

将版本控制数据存储在电脑上,真“人工智能”。例如,使用简单的文件复制来保存不同的版本。可以在自己的电脑上手动复制项目文件夹,并在文件名后面添加版本号(如 project_v1、project_v2 等)来记录不同的修改阶段。但是这种方法比较原始,对于复杂的项目和多人协作来说效率很低。

image.png

本地版本控制系统

其中最流行的一种叫做 RCS,现今许多计算机系统上都还看得到它的踪影。 RCS 的工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。

image.png

存在缺点:无法协同

集中化的版本控制系统

诸如 CVS、Subversion 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新

image.png

存在缺点:

  • 中央服务器的单点故障,影响提交、丢失变更历史

分布式版本控制系统

在这类系统中,像 Git、Mercurial、Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。

image.png

本次主要分享大家十分熟悉的代码版本控制工具 - Git (偏原理分析)

Git 特点

基于差异

image.png

基于快照

image.png

  • 直接记录快照,而非差异比较
  • 近乎所有操作都是本地执行
  • Git 保证完整性
  • Git 一般只添加数据

Git 关键词

  • Workspace:工作区

  • Index / Stage:暂存区

  • Repository:仓库区(或本地仓库)

  • Remote:远程仓库

  • Git 状态 (git status)

    • Untracked files: 未跟踪的文件
    • Changes not staged for commit: 尚未暂存以备提交的变更
    • Changes to be committed:要提交的变更
    • Committed:

image-20240816231328145

Git 工作流程

image.png

Git Remote init Demo

难以想象,上大一(2010 年)那会,一开始啥都不懂,根本没有 github/gitlab 等概念,多人协作开发就是找一台服务器,找个目录,执行一下以下命令,然后就开干,也没有明确的 git flow / github flow / gitlab flow 这些概念,居然还学起 code review,只能说那会 Too young, too simple,你无法想象我们的 code review 流程


我:我代码提交了,你帮忙 review 一下

同学 A: git pull -> 逐个 commit 看 diff -> 看到有问题的,写注释 -> 提交注释 -> 我看完了

我:git pull -> 搜索注释 -> 改代码 -> git push -> 我改好了


# git init 用于初始化一个新的 git 仓库,--bare 参数表示创建一个“裸”仓库。与普通的git仓库不同,裸仓库没有工作目录
# 换句话说 --bare 初始化的是共享的远程仓库,通常这个操作由 gitlab/github 的创建仓库来实现
git init --bare remote

# 正常我们克隆仓库是 git clone https://github.com/vitejs/vite.git,这里我们把刚刚创建的远程仓库作为目标仓库
git clone ~/code/git-demo/remote local

详细过程见演示动画

iShot_2024-11-02_21.51.00

Git 目录结构

当我们通过 git pull 把代码(空项目)拉下来之后,我们可以看到以下目录

.git
├── HEAD # 记录当前 checkout 分支 -> ref: refs/heads/master
├── config # 项目特有配置项, 它可以覆盖全局的 Git 配置(~/.gitconfig),例如项目特定的用户信息(user.name和user.email)
├── description # 仓库描述信息,主要给 gitweb 等git托管系统使用
├── hooks # git的钩子脚本,后面详细讲
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   ├── push-to-checkout.sample
│   ├── sendemail-validate.sample
│   └── update.sample
├── info  # 目录,包含一个全局性排除文件, 用以放置那些不希望被记录在 .gitignore 文件中的忽略模式
│   └── exclude
├── objects  # 存储所有的数据内容
│   ├── info
│   └── pack
└── refs # 存储指向数据的提交对象指针
    ├── heads
    └── tags

9 directories, 18 files

我们提交点内容,看看有什么变化

echo "git demo" > README.md
git add .
git commit -m "docs: init"
git push origin master

枚举对象中: 3, 完成.
对象计数中: 100% (3/3), 完成.
写入对象中: 100% (3/3), 225 字节 | 225.00 KiB/s, 完成.
总共 3(差异 0),复用 0(差异 0),包复用 0(来自  0 个包)
To /Users/justinlxy/code/git-demo/remote
 * [new branch]      master -> master
 
git clone ~/code/git-demo/remote local-2
# 新增点内容,增加 .gitignore 文件						q		````
echo "tmp/*" > .gitignore
git add .
git commit -m "docs: update readme"
git push origin master

枚举对象中: 5, 完成.
对象计数中: 100% (5/5), 完成.
使用 16 个线程进行压缩
压缩对象中: 100% (2/2), 完成.
写入对象中: 100% (3/3), 905 字节 | 905.00 KiB/s, 完成.
总共 3(差异 0),复用 0(差异 0),包复用 0(来自  0 个包)

切换到第一个目录,拉取下最新代码,这个时候再看下 .git 发生了啥改变

.git
├── COMMIT_EDITMSG # git commit 时 msg 内容
├── FETCH_HEAD # Git 的暂存区(staging area)相关的文件
├── ORIG_HEAD # ORIG_HEAD是一个引用(reference),它通常用于保存某些操作之前的HEAD
├── index # Git 的暂存区(staging area)相关的文件
├── logs # 用于存储 Git 操作的日志信息。
│   ├── HEAD
│   └── refs
│       ├── heads
│       │   └── master
│       └── remotes
│           └── origin
│               └── master
├── objects # 存储所有的数据内容
│   ├── 00
│   │   └── a63a3c21f42bff7e625c2a5dee821305c84166
│   ├── 92
│   │   └── c2ac02b090ff00e9aba3919b1babbe7bed49b9
│   ├── aa
│   │   └── 374134282a1a1e69bb0b754b541b21cecaff8a
│   ├── af
│   │   └── 20a39679d218ae9d1f021fea3afcc1d430ac62
│   ├── c6
│   │   └── c37b0d6ec3894ee7374f44602e1f96f40b550b
│   ├── e2
│   │   └── 4dfb4ba3c22d734c1aaab99b82865d9d12fd32
│   ├── info
│   └── pack
└── refs # 存储指向数据的提交对象指针
    ├── heads
    │   └── master
    ├── remotes
    │   └── origin
    │       └── master
    └── tags

22 directories, 33 files

先看看操作效果,再分析原理

COMMIT_EDITMSG

这个文件用于存储在执行git commit操作时用户输入的提交消息(commit message)内容。当你使用git commit命令后,Git 会打开一个文本编辑器让你输入提交消息,这些消息就存储在这个文件中。提交消息通常用于简要描述本次提交所做的更改,如 “docs: init” 等,这有助于其他开发人员理解代码变更的目的。

这里还涉及到是否增加 -m 的区别,如果可以,请不要使用 -m ,因为 git commit 支持更规范的 commit message 内容,包括三个部分:Header,Body 和 Footer,具体规范后续讲hook的时候会讲到,这里先略过

<type>(<scope>): <subject>
// 空一行
<body>
// 空一行
<footer>

FETCH_HEAD

FETCH_HEAD 是 Git 中的一个引用文件,它记录了最近一次从远程仓库获取(fetch)操作的结果。具体来说,它包含了从远程仓库获取到的所有分支(或其他引用)的最新提交(commit)的信息。通常靠 FETCH_HEAD 的内容来快速确定远程仓库是否有新的提交

ORIG_HEAD

ORIG_HEAD是一个引用(reference),它通常用于保存某些操作之前的HEAD(当前分支引用)的位置。它是 Git 内部用于跟踪分支状态变化的一种机制。

  1. 合并(merge)操作中的备份,例如,你在master分支上执行git merge feature - branch来合并feature - branch,在合并操作开始前,HEAD指向master分支的当前提交,Git 会把这个master分支原来的位置记录在ORIG_HEAD中。这样做的好处是,如果合并过程中出现问题,比如产生了冲突并且你想要撤销这个合并操作,就可以通过git reset --hard ORIG_HEAD命令快速地将HEAD恢复到合并之前的状态,让分支回到合并操作之前的样子。
  2. 重置(reset)操作的参考点, 例如,如果你不小心执行了一个错误的git reset操作,导致分支指针移动到了一个不期望的位置,你可以利用ORIG_HEAD来恢复之前的状态。
  3. 变基(rebase)操作的备份,例如,当你对一个分支进行变基操作(如git rebase another - branch),Git 会先将当前分支的原始HEAD位置记录到ORIG_HEAD中。变基操作可能会重写分支的提交历史,过程相对复杂,如果在变基过程中出现问题或者你对结果不满意,ORIG_HEAD可以帮助你将分支恢复到变基之前的状态。

HEAD 是 Git 中一个非常重要的指针,它代表当前工作目录中检出(checkout)的分支引用。简单来说,它指向你正在进行操作的分支的最新提交。例如,当你切换到master分支并且在该分支上进行工作时,HEAD 就指向master分支的最新提交。

hooks

提交工作流钩子
  • pre-commit:钩子在键入提交信息前运行,通常用来做代码格式化、代码检查、测试用例等等,通常我们会结合几个工具来实现

    husky、lint-staged、prettier 来实现提交前进行代码格式化,最新版本的实现十分优雅,大致原理如下

    1. 通过修改 .git/config 下的 hostsPath 配置,执行 husky 对于的 hooks,解决 .git/hooks 不提交问题
    2. git commit 时,触发 ./husky/pre-commit 脚本,执行 npx --no-install lint-staged --quiet
    3. lint-staged 识别 stage 文件类型,并根据.lintstagedrc 配置,实现不同文件类型执行不同脚本逻辑

    image-20241114223258239

  • prepare-commit-msg:钩子在启动提交信息编辑器之前,默认信息被创建之后运行,它对一般的提交来说并没有什么用;然而对那些会自动产生默认信息的提交,如提交信息模板、合并提交、压缩提交和修订提交等非常实用

image.png

  • commit-msg:钩子在commit之后触发,可以用来在提交通过前验证项目状态或提交信息,进一步可以结合 changelog 生成插件,来生成发布日志

image.png image.png image.png

  • post-commit:钩子在整个提交过程完成后运行,该钩子一般用于通知之类的事情
电子邮件工作流钩子
  • applypatch-msg :由 git am 命令调用的,它接收单个参数:包含请求合并信息的临时文件的名字。 如果脚本返回非零值,Git 将放弃该补丁。 你可以用该脚本来确保提交信息符合格式,或直接用脚本修正格式错误。
  • pre-applypatch :在 git am 运行期间被调用的,你可以用这个脚本运行测试或检查工作区。 如果有什么遗漏,或测试未能通过,脚本会以非零值退出,中断 git am 的运行,这样补丁就不会被提交。
  • post-applypatch:运行于提交产生之后,是在 git am 运行期间最后被调用的钩子。 你可以用它把结果通知给一个小组或所拉取的补丁的作者。 但你没办法用它停止打补丁的过程
其它客户端钩子
  • pre-rebase :钩子运行于变基之前,以非零值退出可以中止变基的过程。 你可以使用这个钩子来禁止对已经推送的提交变基。 Git 自带的 pre-rebase 钩子示例就是这么做的,不过它所做的一些假设可能与你的工作流程不匹配
  • post-rewrite :钩子被那些会替换提交记录的命令调用,比如 git commit --amendgit rebase
  • post-checkout:git checkout 成功运行后, 你可以根据你的项目环境用它调整你的工作目录。 其中包括放入大的二进制文件、自动生成文档或进行其他类似这样的操作
  • post-merge:在 git merge 成功运行后,你可以用它恢复 Git 无法跟踪的工作区数据,比如权限数据。 这个钩子也可以用来验证某些在 Git 控制之外的文件是否存在,这样你就能在工作区改变时,把这些文件复制进来。
  • pre-push:钩子会在 git push 运行期间, 更新了远程引用但尚未传送对象时被调用。 它接受远程分支的名字和位置作为参数,同时从标准输入中读取一系列待更新的引用。 你可以在推送开始之前,用它验证对引用的更新操作
服务器端钩子
  • pre-receive:处理来自客户端的推送操作时,最先被调用的脚本是 pre-receive。 它从标准输入获取一系列被推送的引用。如果它以非零值退出,所有的推送内容都不会被接受。 你可以用这个钩子阻止对引用进行非快进(non-fast-forward)的更新,或者对该推送所修改的所有引用和文件进行访问控制
  • update:update 脚本和 pre-receive 脚本十分类似,不同之处在于它会为每一个准备更新的分支各运行一次。 假如推送者同时向多个分支推送内容,pre-receive 只运行一次,相比之下 update 则会为每一个被推送的分支各运行一次。 它不会从标准输入读取内容,而是接受三个参数:引用的名字(分支),推送前的引用指向的内容的 SHA-1 值,以及用户准备推送的内容的 SHA-1 值。 如果 update 脚本以非零值退出,只有相应的那一个引用会被拒绝;其余的依然会被更新
  • post-receive:挂钩在整个过程完结以后运行,可以用来更新其他系统服务或者通知用户。 它接受与 pre-receive 相同的标准输入数据。 它的用途包括给某个邮件列表发信,通知持续集成(continous integration)的服务器, 或者更新问题追踪系统(ticket-tracking system) —— 甚至可以通过分析提交信息来决定某个问题(ticket)是否应该被开启,修改或者关闭。 该脚本无法终止推送进程,不过客户端在它结束运行之前将保持连接状态, 所以如果你想做其他操作需谨慎使用它,因为它将耗费你很长的一段时间

index

Git索引是一个在你的工作目录和项目仓库间的暂存区(staging area),索引文件存储在.git/index中,是一个二进制文件

$ git ls-files --stage
100644 2538dd5ae17a8ada8b0fefd082b1827f009864fd 0	.gitignore
100644 fa010c09a0efc8d6f120d011d3bbd19f145826df 0	README.md
100644 a2781f6f50cf31f4cdd2909b3b737fab6348916f 0	git-index


$ hexdump -C .git/index
00000000  44 49 52 43 00 00 00 02  00 00 00 03 61 24 b5 ce  |DIRC........a$..|
00000010  1f dd ba 7a 61 24 b5 ce  1e ea a1 77 01 00 00 04  |...za$.....w....|
00000020  0a ac 15 eb 00 00 81 a4  00 00 01 f6 00 00 00 14  |................|
00000030  00 00 00 82 25 38 dd 5a  e1 7a 8a da 8b 0f ef d0  |....%8.Z.z......|
00000040  82 b1 82 7f 00 98 64 fd  00 0a 2e 67 69 74 69 67  |......d....gitig|
00000050  6e 6f 72 65 00 00 00 00  00 00 00 00 60 11 31 0b  |nore........`.1.|
00000060  32 57 81 8d 60 11 31 0b  31 60 74 23 01 00 00 05  |2W..`.1.1`t#....|
00000070  07 3f 0f ef 00 00 81 a4  00 00 01 f6 00 00 00 14  |.?..............|
00000080  00 00 06 69 fa 01 0c 09  a0 ef c8 d6 f1 20 d0 11  |...i......... ..|
00000090  d3 bb d1 9f 14 58 26 df  00 09 52 45 41 44 4d 45  |.....X&...README|
000000a0  2e 6d 64 00 61 25 a8 67  11 3c 70 fb 61 25 a8 67  |.md.a%.g.<p.a%.g|
000000b0  11 3c 70 fb 01 00 00 04  0a b0 ff 7c 00 00 81 a4  |.<p........|....|
000000c0  00 00 01 f6 00 00 00 14  00 00 00 0f a2 78 1f 6f  |.............x.o|
000000d0  50 cf 31 f4 cd d2 90 9b  3b 73 7f ab 63 48 91 6f  |P.1.....;s..cH.o|
000000e0  00 09 67 69 74 2d 69 6e  64 65 78 00 54 52 45 45  |..git-index.TREE|
000000f0  00 00 00 06 00 2d 31 20  30 0a 89 9c 39 c4 b8 a5  |.....-1 0...9...|
00000100  f1 c3 49 c2 8e 45 a0 a1  16 d0 1d 54 19 ab        |..I..E.....T..|
0000010e

00000000 44 49 52 43 00 00 00 02 00 00 00 03 61 24 b5 ce |DIRC........a$..|

  • 前四字节包含了『DIRC』(0x44495243),指『DirCache』,用于标识该文件是否是合法的索引文件
  • 中间四字节包含了索引文件的版本,当前版本为『2』(0x00000002)
  • 后面四字节为32位无符号整数,标识了索引的文件数目,该仓库内存储有3个文件,即文件数目为3(0x00000003)
$ stat -f 'mtime=%m ctime=%c' .gitignore | tr ' ' '\n'
mtime=1629795790
ctime=1629795790

$ stat -t '%FT%T' .gitignore
16777220 179049963 -rw-r--r-- 1 justin staff 0 130 "2021-08-24T17:11:47" "2021-08-24T17:03:10" "2021-08-24T17:03:10" "2021-08-24T17:03:10" 4096 8 0 .gitignore
  • 最后四字节表示文件创建时间 1629795790 -> 61 24 b5 ce

经过所有内容的分析,大致可以得到类似的文件索引结构

| 0           | 4            | 8           | C              |
  |-------------|--------------|-------------|----------------|
0 | DIRC        | Version      | File count  | ctime       ...| 0
  | ...         | mtime                      | device         |
2 | inode       | mode         | UID         | GID            | 2
  | File size   | Entry SHA-1                              ...|
4 | ...                        | Flags       | Index SHA-1 ...| 4
  | ...                                                       |

info

├── info # 目录,包含一个全局性排除文件, 用以放置那些不希望被记录在 .gitignore 文件中的忽略模式 │ └── exclude .gitignore: 有一些文件不需要提交,将这些文件添加到.gitignore文件中

# 此为注释 – 将被 Git 忽略
*.a       # 忽略所有 .a 结尾的文件
!lib.a    # 但 lib.a 除外
/TODO     # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/    # 忽略 build/ 目录下的所有文件
doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
  • 已经提交的内容,如何添加到 .gitignore
  • 通过.gitignore 创建空目录

exclude: 本地仓库忽略,这里配置的忽略文件不会提交到代码库中,对团队里的其他人不会有影响,只影响自己本地仓库

logs

git 所有分支所有操作记录,包含已删除commit和reset记录,所以,我们通过 git reflog 可以还原任何操作

├── HEAD
└── refs
    ├── heads
    │   ├── master
    │   └── test
    └── remotes
        └── origin
            ├── master
            └── test

4 directories, 5 files

$ git reflog
8e832c9 (HEAD -> test, origin/test, master) HEAD@{0}: reset: moving to HEAD
8e832c9 (HEAD -> test, origin/test, master) HEAD@{1}: checkout: moving from master to test
8e832c9 (HEAD -> test, origin/test, master) HEAD@{2}: commit: feat: add ignore file
252ba50 (origin/master) HEAD@{3}: reset: moving to HEAD
252ba50 (origin/master) HEAD@{4}: reset: moving to HEAD^
d95cc8a HEAD@{5}: commit: feat: add testfile
252ba50 (origin/master) HEAD@{6}: commit: fix: ignore
56d29d8 HEAD@{7}: commit: docs: update readme
ad5f217 HEAD@{8}: commit (initial): feat: add READM.md

objects

存储对象的目录,本地仓库,git中对象分为三种:commit对象,tree对象(多叉树),blob对象;

# justin @ C02W96WVG8WN in ~/xx/git-tech/local/.git/objects on git:test o [16:37:16]
$ echo "test content" | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4

# justin @ C02W96WVG8WN in ~/xx/git-tech/local/.git/objects on git:test o [16:37:33]
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content

$ find .git/objects -type f

.git/objects/50/586c34bcabede1353a6918e1700dabea8e5b1a
.git/objects/56/d29d8606ad3e10bc90a2525a2f5e9118d06ffa
.git/objects/93/56d74f4d0ed558a28b0587157a9fae5147e6ad
.git/objects/33/d1be956f10eed37f5a7a163f26d9dddfde49c9
.git/objects/d9/5cc8af3b9c88eed30b363c3c4bffa0ec918ef1
.git/objects/ad/5f217eecfb2a24eb996aaa4a74300aa9aa4029
.git/objects/a2/781f6f50cf31f4cdd2909b3b737fab6348916f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects/f3/e86835b0b4d91ac4d9b35171c399f61422d3c8
.git/objects/88/d754ce374e07489dc50e790df98d21c8e84b00
.git/objects/98/11f5e0d32f90e5bb4a1634ca9e998155f78a89
.git/objects/01/02eb012bc6c306c2ff86b59e13ff709beea191
.git/objects/fa/010c09a0efc8d6f120d011d3bbd19f145826df
.git/objects/2d/d586034c65f5095d427aa05f8db4b4a3125b15
.git/objects/77/ef8ad33a72f3bf3774f2a2c3e65aa418f64c34
.git/objects/40/6657c932af8c14b4e5c57d885a5625ef94b906
.git/objects/8b/e988bd0c4c2e05762954a7750bf903e920348f
.git/objects/8e/832c91e3b4811636d956c481503178a6f99b9d
.git/objects/25/38dd5ae17a8ada8b0fefd082b1827f009864fd
.git/objects/25/2ba50bf9fc190e76518523015e7debdae31655


$ git cat-file -p 50586c34bcabede1353a6918e1700dabea8e5b1a
tree 406657c932af8c14b4e5c57d885a5625ef94b906
parent 8e832c91e3b4811636d956c481503178a6f99b9d
author -- <--> 1629857826 +0800
committer -- <--> 1629857826 +0800

index on test: 8e832c9 feat: add ignore file


$ git cat-file -p 406657c932af8c14b4e5c57d885a5625ef94b906
100644 blob 2538dd5ae17a8ada8b0fefd082b1827f009864fd	.gitignore
100644 blob fa010c09a0efc8d6f120d011d3bbd19f145826df	README.md
100644 blob a2781f6f50cf31f4cdd2909b3b737fab6348916f	git-index


$ git cat-file -p a2781f6f50cf31f4cdd2909b3b737fab6348916f
test git index


$ find .git/objects/pack -type f
.git/objects/pack/pack-225c9f48d7beb9e4cf3bd70296a4c4043f841c23.pack
.git/objects/pack/pack-df391f7bfd71a13d80d9d909b50830c65783fe98.idx
.git/objects/pack/pack-e76486446dc3fd7bf41142b2e1b622b8f3e69635.pack
.git/objects/pack/pack-288b471d14e0c79da8dc0001c36f69c6b9309ecc.idx

$ git verify-pack -v .git/objects/pack/pack-df391f7bfd71a13d80d9d909b50830c65783fe98.idx
019c1e56320279623c11a78cba0d2c4289fc1be8 commit 344 226 12
aef3f113eef479ee6350a284d17d9b13eba2dbac commit 301 218 238
a28b0aaaac181464d58075ee416f1490ebad3e39 commit 245 161 456
6f89000d0373f17336e67034e6f364c6359e9428 commit 309 218 617
b2eee1f9ae66a9048d9624ce57b90e65b576659b commit 309 217 835
98c0ad22418f11c44fb4263a6cf8c3cf0a455b2a commit 291 186 1052
57b9bb67e5b395f601cd0bbc623f444302182cdf commit 301 217 1238
...

$ git gc
Enumerating objects: 26978, done.
Counting objects: 100% (26978/26978), done.
Delta compression using up to 8 threads
Compressing objects: 100% (26239/26239), done.
Writing objects: 100% (26978/26978), done.
Total 26978 (delta 14997), reused 10987 (delta 255)
Removing duplicate objects: 100% (256/256), done.
Computing commit graph generation numbers: 100% (3598/3598), done.

git gc 前 image.png

git gc 后 image.png

refs

git 引用,记录索引值

$ tree
.
├── heads
│   ├── master
│   └── test
├── remotes
│   └── origin
│       ├── master
│       └── test
├── stash
└── tags
    ├── 1.0.0
    └── 1.0.1


4 directories, 7 files

$ cat tags/1.0.0
3334c25c10929768f377dfc09541aac5678ff229

$ git show 3334c25c10929768f377dfc09541aac5678ff229

image.png

篇幅原因,以下内容,下次继续!

  • git 常见命令
    • Plumbing Commands (底层命令)
    • Porcelain Commands (上层命令)
      • add Add file contents to the index
      • am Apply a series of patches from a mailbox
      • archive Create an archive of files from a named tree
      • bisect Use binary search to find the commit that introduced a bug
      • branch List, create, or delete branches
      • bundle Move objects and refs by archive
      • checkout Switch branches or restore working tree files
      • cherry-pick Apply the changes introduced by some existing commits
      • citool Graphical alternative to git-commit
      • clean Remove untracked files from the working tree
      • clone Clone a repository into a new directory
      • commit Record changes to the repository
      • describe Give an object a human readable name based on an available ref
      • diff Show changes between commits, commit and working tree, etc
      • fetch Download objects and refs from another repository
      • format-patch Prepare patches for e-mail submission
      • gc Cleanup unnecessary files and optimize the local repository
      • gitk The Git repository browser
      • grep Print lines matching a pattern
      • gui A portable graphical interface to Git
      • init Create an empty Git repository or reinitialize an existing one
      • log Show commit logs
      • merge Join two or more development histories together
      • mv Move or rename a file, a directory, or a symlink
      • notes Add or inspect object notes
      • pull Fetch from and integrate with another repository or a local branch
      • push Update remote refs along with associated objects
      • range-diff Compare two commit ranges (e.g. two versions of a branch)
      • rebase Reapply commits on top of another base tip
      • reset Reset current HEAD to the specified state
      • restore Restore working tree files
      • revert Revert some existing commits
      • rm Remove files from the working tree and from the index
      • shortlog Summarize 'git log' output
      • show Show various types of objects
      • stash Stash the changes in a dirty working directory away
      • status Show the working tree status
      • submodule Initialize, update or inspect submodules
      • switch Switch branches
      • tag Create, list, delete or verify a tag object signed with GPG
      • worktree Manage multiple working trees
  • git 工作流
    • git flow
    • github flow
    • gitlab flow
    • 功能分支工作流
    • 集中式工作流

参考资料:https://git-scm.com/