记录一下Git的基础操作原理,还有Git信息泄漏在比赛中的一些问题。
git init
创建一个git_test文件夹,并使用git init命令初始化一个空的Git 仓库
Rain@Rai4over MINGW64 ~/Desktop $ mkdir git_test Rain@Rai4over MINGW64 ~/Desktop $ cd git_test/ Rain@Rai4over MINGW64 ~/Desktop/git_test $ git init Initialized empty Git repository in C:/Users/Rain/Desktop/git_test/.git/ Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ ls -a ./ ../ .git/
git add && git commit
创建一个test1.txt文件,通过git add跟踪这个文件,然后使用git commit提交更新到仓库。
Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ echo 'test1file haha' >test1.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat test1.txt test1file haha Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) test1.txt nothing added to commit but untracked files present (use "git add" to track) Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git add test1.txt warning: LF will be replaced by CRLF in test1.txt. The file will have its original line endings in your working directory. Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: test1.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git commit -m "test1file" [master (root-commit) 4089fd9] test1file 1 file changed, 1 insertion(+) create mode 100644 test1.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git log commit 4089fd99a9bd7ee0cec8f94ce1c66b650054080e (HEAD -> master) Author: Rai4over <[email protected]> Date: Sun Sep 2 17:52:35 2018 +0800 test1file
查看状态 && 暂存区
git status命令可以查看仓库状态,列出当前目录所有还没有被git管理追踪的文件和被git管理且被修改但还未提交更新的文件.。
文件状态
仓库中的文件可能存在于这三种状态:
- Untracked files → 文件未被跟踪;
- Changes to be committed → 文件已暂存,这是下次提交的内容;
- Changes not staged for commit → 已跟踪文件的内容发生了变化,但还没有放到暂存区。
通过git add的文件会加入暂存区,之后git commit会将暂存区的所有文件提交更新。
Git对象
在Git系统中有四中类型的对象,所有的Git操作都是基于这四种类型的对象。
"blob",这种对象用来保存文件的内容。
"tree",可以理解成一个对象关系树,它管理一些"tree"和“blob”对象。
"commit",指向一个"tree",它用来标记项目某一个特定时间点的状态。它包括以下关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交、
"tag",给某个提交增添一个标记。
SHA1哈希值
在Git系统中,每个Git对象都通过哈希值来代表这个对象。哈希值是通过SHA1算法计算出来的,长度为40个字符(40-digit)。
Commit 对象
commit对象一般包含以下信息:
代表commit的哈希值
指向tree对象的哈希值
作者
提交者
注释
Tree 对象
Tree对象一般包含以下信息:
代表blog的哈希值
指向tree对象的哈希值
Blob 对象
Blob对象一般包含用来存储文件的内容。
Tag 对象
标签对象一般包含以下信息:
- 一个对象名(SHA1签名)
- 对象类型
对象间的关系
commit、tree、blob三个对象的简单关系如下:
若仓库的目录结构如下:
|-- README
`-- lib
|-- inc
| `-- tricks.rb
`-- mylib.rb
git对象结构的关系图如下:
在我们使用git init初始化git仓库的时候,会生成一个.git的隐藏目录,git会将所有的文件,目录,提交等转化为git对象,压缩存储在这个文件夹当中。
COMMIT_EDITMSG:保存最新的commit message,Git系统不会用到这个文件,只是给用户一个参考
config:这个是GIt仓库的配置文件
description:仓库的描述信息,主要给gitweb等git托管系统使用
HEAD:这个文件包含了一个档期分支(branch)的引用,通过这个文件Git可以得到下一次commit的parent
hooks:这个目录存放一些shell脚本,可以设置特定的git命令后触发相应的脚本;在搭建gitweb系统或其他
git托管系统会经常用到hook script
index:这个文件就是我们前面提到的暂存区(stage),是一个二进制文件
info:包含仓库的一些信息
logs:保存所有更新的引用记录
objects:所有的Git对象都会存放在这个目录中,对象的SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名
refs:这个目录一般包括三个子文件夹,heads、remotes和tags,heads中的文件标识了项目中的各个分支指向的当前commit
ORIG_HEAD:HEAD指针的前一个状态
Git引用
Git中的引用是一个非常重要的概念,对于理解分支(branch)、HEAD指针以及reflog非常有帮助。
Git系统中的分支名、远程分支名、tag等都是指向某个commit的引用。比如master分支,origin/master远程分支,命名为V1.0.0.0的tag等都是引用,它们通过该保存某个commit的SHA1哈希值指向某个commit
HEAD
HEAD也是一个引用,一般情况下间接指向你当前所在的分支的最新的commit上。HEAD跟Git中一般的引用不同,它并不包含某个commit的SHA1哈希值,而是包含当前所在的分支,所有HEAD直接执行当前所在的分支,然后间接指向当前所在分支的最新提交。
查看当前分支,当前分支为master,并通过git log可以发现这个commit就是master分支上最新的提交。
Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git status On branch master nothing to commit, working tree clean Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat .git/HEAD ref: refs/heads/master Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat .git/refs/heads/master 29bb92d948b861d13c0912694a6000d33b62fea3 Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git cat-file -p 29bb92d948b861d13c0912694a6000d33b62fea3 tree 57f5feedc4fa5a298d6d89aa93fd205bb588458e parent 4089fd99a9bd7ee0cec8f94ce1c66b650054080e author Rai4over <[email protected]> 1535977002 +0800 committer Rai4over <[email protected]> 1535977002 +0800 test2 Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git log -n 1 commit 29bb92d948b861d13c0912694a6000d33b62fea3 (HEAD -> master, test) Author: Rai4over <[email protected]> Date: Mon Sep 3 20:16:42 2018 +0800 test2
所有的内容都是环环相扣的,我们通过HEAD找到一个当前分支,然后通过当前分支的引用找到最新的commit,然后通过commit可以找到整个对象关系模型:
引用和分支
git branch test,创建名为test分支。
git checkout test,切换到test分支。
除了master分支,创建了一个test的分支,切换到新分支,查看HEAD,HEAD的指向发生了变化。
再次查看“.git/refs/heads/”目录,可以看到除了master文件之外,又多了一个test文件,查看该文件的内容也是一个哈希值。
Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat .git/HEAD ref: refs/heads/master Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat .git/refs/heads/master 29bb92d948b861d13c0912694a6000d33b62fea3 Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git checkout test Switched to branch 'test' Rain@Rai4over MINGW64 ~/Desktop/git_test (test) $ cat .git/HEAD ref: refs/heads/test Rain@Rai4over MINGW64 ~/Desktop/git_test (test) $ cat .git/refs/heads/test 29bb92d948b861d13c0912694a6000d33b62fea3
git show-ref --heads,命令就可以查看所有的头
Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git show-ref --heads 29bb92d948b861d13c0912694a6000d33b62fea3 refs/heads/master 29bb92d948b861d13c0912694a6000d33b62fea3 refs/heads/test
日志
进入.git/logs文件夹,可以看到这个文件夹也有一个HEAD文件和refs目录,这些就是记录仓库修改的地方。
目录下记录了包括git commit,git checkout,git stash等命令的操作历史。
git stash,保存当前工作进度,会把暂存区和工作区的改动保存起来。
执行完这个命令后,在运行git status命令,就会发现当前是一个干净的工作区,没有任何改动。
当然可以使用git stash pop来恢复之前的进度.
Rain@Rai4over MINGW64 ~/Desktop/git_test/.git/logs (GIT_DIR!) $ ls HEAD refs/ Rain@Rai4over MINGW64 ~/Desktop/git_test/.git/logs (GIT_DIR!) $ ls refs/ heads/ stash Rain@Rai4over MINGW64 ~/Desktop/git_test/.git/logs (GIT_DIR!) $ ls refs/heads/ master test
索引index
index/stage,就是更新的暂存区,看看index文件。
index(索引)是一个存放了已排序的路径的二进制文件,并且每个路径都对应一个SHA1哈希值。在Git系统中,可以通过git ls-files --stage来显示index文件的内容。
创建新文件,添加到暂存区(SHA1哈希值已经改变)
$ git ls-files --stage 100644 1453b33002ce30e42459ca3ea867c2aeefe1a0fa 0 test1.txt 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 test2.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ echo 333 > test3.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git ls-files --stage 100644 1453b33002ce30e42459ca3ea867c2aeefe1a0fa 0 test1.txt 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 test2.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git add test3.txt warning: LF will be replaced by CRLF in test3.txt. The file will have its original line endings in your working directory. Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git ls-files --stage 100644 1453b33002ce30e42459ca3ea867c2aeefe1a0fa 0 test1.txt 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 test2.txt 100644 55bd0ac4c42e46cd751eb7405e12a35e61425550 0 test3.txt
对象的存储
所有的Git对象都会存放在.git/objects目录中,对象SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名。
我们常用的.git泄漏文件恢复工具有很多,但是如果不懂原理,可能会遇到恢复文件缺少的问题,因为多数工具只会恢复HEAD指针的commit对象。
这里拿某比赛的题目举个例子:
打开浏览器console发现提示,并且恢复HEAD指针的commit对象并不能得出正确源码。
获得commit
这里可以想到使用git stash,当然最稳妥的办法当然是遍历所有commit,不管是通过log,refs,HEAD等文件或目录均可,得到的commit指针都是一样的。
commit hash ,e5b2a2443c2b6d395d06960123142bc91123148c
访问commit对象,.git/objects/e5/b2a2443c2b6d395d06960123142bc91123148c
获得tree
根据commit对象得到tree对象的hash
访问tree对象,.git/objects/76/9905f5a6f425ce62ed9a1cbf375a61fb56b406
获得Blob
根据tree对象得到blob对象的hash
访问blob对象,.git/objects/8e/f569f235780f24c42b60f50d528a03f7238c80
其实只要明白了原理,不管commit的hash藏在哪里都能应对自如。