记录一下Git的基础操作原理,还有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 <faith4444@163.com>
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系统中有四中类型的对象,所有的Git操作都是基于这四种类型的对象。
SHA1哈希值
在Git系统中,每个Git对象都通过哈希值来代表这个对象。哈希值是通过SHA1算法计算出来的,长度为40个字符(40-digit)。
Commit 对象
commit对象一般包含以下信息:
-
代表commit的哈希值
-
指向tree对象的哈希值
-
作者
-
提交者
-
注释
Tree 对象
Tree对象一般包含以下信息:
Blob 对象
Blob对象一般包含用来存储文件的内容。
Tag 对象
标签对象一般包含以下信息:
- 一个对象名(SHA1签名)
- 对象类型
对象间的关系
commit、tree、blob三个对象的简单关系如下:
若仓库的目录结构如下:
|-- README
`-- lib
|-- inc
| `-- tricks.rb
`-- mylib.rb
git对象结构的关系图如下:
.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 <faith4444@163.com> 1535977002 +0800
committer Rai4over <faith4444@163.com> 1535977002 +0800
test2
Rain@Rai4over MINGW64 ~/Desktop/git_test (master)
$ git log -n 1
commit 29bb92d948b861d13c0912694a6000d33b62fea3 (HEAD -> master, test)
Author: Rai4over <faith4444@163.com>
Date: Mon Sep 3 20:16:42 2018 +0800
test2
所有的内容都是环环相扣的,我们通过HEAD找到一个当前分支,然后通过当前分支的引用找到最新的commit,然后通过commit可以找到整个对象关系模型:
引用和分支
除了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
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等命令的操作历史。
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泄漏 | Example
我们常用的.git泄漏文件恢复工具有很多,但是如果不懂原理,可能会遇到恢复文件缺少的问题,因为多数工具只会恢复HEAD指针的commit对象。
这里拿某比赛的题目举个例子:
打开浏览器console发现提示,并且恢复HEAD指针的commit对象并不能得出正确源码。
获得commit
这里可以想到使用git stash,当然最稳妥的办法当然是遍历所有commit,不管是通过log,refs,HEAD等文件或目录均可,得到的commit指针都是一样的。
获得tree
获得Blob