我认为对于这个目录的研究,可以对Git的工作有一个很全面的了解,所以我会在学习和使用的过程中不时对此文档进行补充。
.git 目录下有如下内容:
branches COMMIT_EDITMSG config description HEAD hooks index info logs objects refs
.git 目录下有如下内容:
branches COMMIT_EDITMSG config description HEAD hooks index info logs objects refs
对于以上文件和目录可简单描述为:
hooks:这个目录存放一些shell脚本,可以设置特定的git命令后触发相应的脚本;在搭建gitweb系统或其他git托管系统会经常用到hook script
info:包含仓库的一些信息
logs:保存所有更新的引用记录
objects:所有的Git对象都会存放在这个目录中,对象的SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名
refs:这个目录一般包括三个子文件夹,heads、remotes和tags,heads中的文件标识了项目中的各个分支指向的当前commit
COMMIT_EDITMSG:保存最新的commit message,Git系统不会用到这个文件,只是给用户一个参考
config:这个是GIt仓库的配置文件
description:仓库的描述信息,主要给gitweb等git托管系统使用
index:这个文件就是暂存区(stage),是一个二进制文件
HEAD:这个文件包含了一个档期分支(branch)的引用,通过这个文件Git可以得到下一次commit的 parent
ORIG_HEAD:HEAD指针的前一个状态
hooks :
发现hooks目录下是一些脚本的样例文件。至于何时,以何种方式执行需要的的脚本,需要根据需要,先不细说。
info :
.git/info下有一个exclude,
在将某个目录添加到 Git
版本库、或提交某个git版本库的改动时,可能希望忽略掉一些文件或目录(如编译时生成的.o、.a文件等),可以修改.git/info/exclude文件来实现。
举例如下:
vi .git/info/exclude
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
*.[oa] #忽略以.o或.a结尾的文件或目录
*.pyc
*.exe #忽略以.exe结尾的文件或目录
.* #忽略以.开头的文件或目录
*.rar
*.zip
*.gz
*.bz2
*.db
*.sqlite
.git/info/exclude 这里设置的则是你自己本地需要排除的文件。 不会影响到其他人。也不会提交到版本库中去。
.gitignore与之不同的是,这个文件本身会提交到版本库中去。用来保存的是公共的需要排除的文件。
.gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。
那么对于这些文件该如何处理呢?
1.删除Git数据库中对该文件的追踪 git rm --cached
2.把对应的规则写入.gitignore,让忽略生效
3.提交,推送
.gitignore 还有个有意思的小功能, 一个空的 .gitignore 文件 可以当作是一个 placeholder 。当你需要为项目创建一个空的 log 目录时, 这就变的很有用。 你可以创建一个 log 目录 在里面放置一个空的 .gitignore 文件。这样当你 clone 这个 repo 的时候 git 会自动的创建好一个空的 log 目录了。
说太多了,在学这个的时候看到一些相关的东西,就记录在这个上面了。。。
logs:
进入“.git/logs”文件夹,可以看到这个文件夹也有一个HEAD文件和refs目录,这些就是记录commit历史记录的地方。我们可以通过commit的哈希值,把repo退到一个指定的状态。
另外:refs下:logs/refs/heads/master
logs/refs/remotes/origin/master
具体说明待补充。。。
现举一例子:
添加一个文件,并提交它。发现此时refs/heads/master 指向了新提交的ID,refs/heads/master就像一个游标,有新的提交时就会指向新的提交。
游标可上可下,Git提供了git reset命令,可以将游标指向任意一个存在的提交ID。
git reset --hard HEAD^ 命令让游标指向HEAD^,HEAD^是HEAD的一个父提交,所以ls,new_one.txt不见了,出现了我之前提交的一个文件new-commit.txt(之前把它删了,所以上一个ls没有显示)。
Git提供了git reflog 命令,用show子命令可以显示此文件的内容。然后把master改为所需要的值。另外恢复master的操作也是记录在日志中的。
对于git reset命令还是有必要去了解的,很常用,而且很危险。应当去了解一下他的各种参数,以及可能出现的危险。
objects:
前面提到所有的Git对象都会存放在“.git/objects”目录中,对象SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名。下面是之前提到的master最新的commit对象的哈希值:
26012d1a51e0be8780d35a9dfa2bcf36b152fedf
在Git系统中有两种对象存储的方式,松散对象存储和打包对象存储
松散对象(loose object)
松散对象存储就是前面提到的,每一个对象都被写入一个单独文件中,对象SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名。
打包对象(packed object)
对应松散存储,把每个文件的每个版本都作为一个单独的对象,它的效率比较低,而且浪费空间。所以就有了通过打包文件(packfile)的存储方式。
Git使用打包文件(packfile)去节省空间。在这个格式中,Git只会保存第二个文件中改变的部分,然后用一个指针指向相似的那个文件。
一般Git系统会自动完成打包的工作,在已经发生过打包的Git仓库中,object/pack目录下回成对出现很多“pack-***.idx”和“pack-***.pak”文件。
HEAD:
指向master的一个游标。
可见HEAD的内容是指向一个引用:refs/heads/master
再看.git/refs/heads/master的内容,1b1a288f2cbaf1951f1007eef59668c28780e863。
用git cat-file -t命令查看其类型,类型为commit,是分支中最新提交的提交ID
查看这个提交的内容。
INDEX:
执行git status(或git diff命令)扫描工作区改动的时候,现依据.git/index文件中记录的(用于跟踪工作区文件的)时间戳、长度等信息判断工作区文件是否改变,如果工作区文件的时间戳改变了,说明文件的内容可能被改变了,需要打开文件,读取文件内容,与更改的原始文件相比较,判断文件是否被更改。如果文件内容没有被改变,则将该文件新的时间戳记录到.git/index文件中。因为如果要判断文件是否更改,使用时间戳、文件长度比较比通过文件内容比较更快。所以Git这样的实现方式可以让工作区状态扫描更快速地执行。
文件.git/index实际上就是一个包含文件索引的目录树,像是一个虚拟的工作区。在这个虚拟工作区的目录树中,记录了文件名和文件的状态信息(时间戳和文件长度等)。文件的内容并没有存储在其中,而是保存在Git对象库.git/objects目录中,文件索引建立了文件和对象库中对象实体之间的对应。