Git 请教一个问题, 如何获取当前 index area 的 哈希 (SHA1 值)

zw963 · 2012年09月13日 · 最后由 zw963 回复于 2012年09月14日 · 4507 次阅读

翻了半天命令,没发现有这个功能,是否有其他折衷的办法可以搞定?

例如:这个哈希存放在 .git 下面的某个文件内。又或者,没有 submit 的 这个 commit, 根本就没有这个 hash 值?? 我觉得肯定有...

顺便呼叫下 @chunzi 帮忙解答,还得感谢你,你翻译的 Progit 对于我之前学习 git 帮助很大。

顺便呼叫下楼下几位: @Saito, @HuangYuHei, @HungYuHei, @happypeter, @knwang

Git 一共有 4 种 SHA1. tag, commit, tree, blob. 每一种 SHA1 其实也代表了 Git 内部的一个基本对象。

稍微解释一下就知道了。

如果做下面的简单操作:

mkdir helloworld
cd helloworld
git init
echo hello world > readme.md
git add .
git commit -am"add readme.md"

在这里看来?哪一步会创建哪些对象?

答案很简单。

git add .

在这里操作中会添加一个 blob 对象,代表 readme 文件本身。这也是为什么你需要在 add 过一个对象之后,撤销需要执行 rm 与 cache 相关命令的原因。

blob

而文件的内容则如下所示。

helloworld git:master ❯ git ls-files -s readme.md 
100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0   readme.md
helloworld git:master ❯ git show 3b18e512dba79
hello world

tree

当执行下面这句的时候:

git commit -am"add readme.md"

这一步中将创建两个对象,一个是当前 work tree 的映射 tree 对象,还有一个就是 commit 对象。

tree 对象是可以包含另外的 tree 以及 blob 的。

helloworld git:master ❯ git show 7394b8cc9ca                                      
tree 7394b8cc9ca

readme.md
helloworld git:master ❯ git cat-file -p 7394b8cc9ca                                                                           
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad    readme.md

上面是一个 tree 对象的具体内容。tree 里面实际上就是描述了当前 tree 的内容以及 blob 的引用。

commit

同理 commit 也可以用相同的方式查看:

helloworld git:master ❯ git show 84073a0
commit 84073a0bffd4c80598dbc4941a5f84d78cc2adcd
Author: Saito <[email protected]>
Date:   Fri Sep 14 01:12:07 2012 +0800

    add readme

diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/readme.md
@@ -0,0 +1 @@
+hello world
helloworld git:master ❯ git cat-file -p 84073a0                                                                               
tree 7394b8cc9ca916312a79ce8078c34b49b1617718
author Saito <[email protected]> 1347556327 +0800
committer Saito <[email protected]> 1347556327 +0800

add readme

可以看到,commit 里面实际上保存的实际上是一个 tree 的 SHA1 以及 commit 的 author commiter 以及最终的 commit message.

tag

最后我们给当前状态打一个 1.0 版本的 tag.

helloworld git:master ❯ git tag 1.0 -m"create a tag"

再来看看 tag 的 SHA1 内容。

helloworld git:master ❯ git show 1518c0a02530b3                                   
tag 1.0
Tagger: Saito <[email protected]>
Date:   Fri Sep 14 01:24:39 2012 +0800

create a tag

commit 84073a0bffd4c80598dbc4941a5f84d78cc2adcd
Author: Saito <[email protected]>
Date:   Fri Sep 14 01:12:07 2012 +0800

    add readme

diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/readme.md
@@ -0,0 +1 @@
+hello world
helloworld git:master ❯ git cat-file -p 1518c0a02530b3
object 84073a0bffd4c80598dbc4941a5f84d78cc2adcd
type commit
tag 1.0
tagger Saito <[email protected]> Fri Sep 14 01:24:39 2012 +0800

create a tag

可以看到,tag 里面主要存储的是 commit 的 SHA1 值,tag name 以及 tagger, 还有,如果有 -m 参数的话会有 tag message.

为什么没有 branch

因为 branch 不在这套体制内。是体制外的。

跟基本对象相关的东西都存在 .git/objects 里面。

而 branch 是在同级的 .git/refs 目录里面的。而 refs 目录里面,每条 ref 记录,实际上 也是只 记录了一个 commit SHA1 而已。

结论

四种对象创建完毕.. 回头再来看,什么时候会有什么 SHA1 已经很清楚的。

回到最初的问题。有没有办法能拿到 tree SHA1. 答案是没有。

没 commit 当然也不能拿到 commit SHA1.

当然也不是 tag. 所以不能拿 tag SHA1.

YES!. 你可以拿到当前修改过并且已经被 add 过的 blob 的 SHA1 git ls-files -s readme.md .

就是这样。

写了也算不短的内容,顺便将内容贴到 blog 里面了。http://saito.im/note/Git-Internals/

#1 楼 @Saito 鼓掌 佩服

@zw963 不知道这个行不行

git diff --cached | shasum

#1 楼 @Saito

谢谢,你写的太棒,太详细了!!!

不过有一点貌似不对,也是之所以我有这个问题的缘故,引用 Progit 中的一段话:

You can create your own tree. Git normally creates a tree by taking the state of your staging area or index and writing a tree object from it. So, to create a tree object, you first have to set up an index by staging some files. To create an index with a single entry — the first version of your text.txt file — you can use the plumbing command update-index.

你把建立树对象的这个步骤延后到提交 commit 之后,但是应该是 add 之后,这个树对象已经存在了。只不过不是以 objects 下 可见的形式存在的。但是 git 有办法获取他的 sha1 (这里指的是那个入口), 否则,git diff --cached, 就没办法工作了。我觉得一定有一个 sha1, 也许就在那个 .git/index 文件内。

我理解错了,哈哈~~ 我又细细看了下,貌似就是你说的那样~~ 👍

再说下之前的需求,以前 git diff 总是以 `单文件' 的形式,打开代码比较工具,常常是打开一堆 TAB, 一点都不直观,我要这个 SHA1, 最初,我是希望通过 git archive 的方式,打包成 tar, 然后直接比较 tar 文档的目录结构,一目了然。不过后来发现,获取 index 以及 获取当前目录下的文件 (所有被索引文件以及除去所有被 ignore 文件这两步不太好搞), 所以,最后我干脆用拷贝到临时目录的形式来实现了。所以这个需求也就解决了。

#3 楼 @knwang

不行,无效的 SHA1

#4 楼 @zw963 没太懂你的需求要解决什么问题

#5 楼 @knwang

可能是我的需求太怪了吧。我希望比较一个项目内任意两个 SHA1 或者 分支,或者 INDEX, 不是使用 单个文件逐个打开' 的方式, 而是首先以目录结构的方式, 显示所有文件的改动, 然后我再查看我感兴趣的文本内容`, 这样一目了然。

我上一张图解释下:

喜欢 GUI 类的 tool Mac 下可以试试 Tower,diff 应该给你改动的文件列表 http://www.git-tower.com/index.html 其他 的系统应该也有些 GUI tool

还有,把 index commit 了不就有 SHA1 了,比较完在 reset 不就行了

#7 楼 @knwang

我基本上也是用类似办法搞定的。呵呵。效果还不错,比以前直观多了。一眼看出很多东西。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号