a blog by @captainsafia
What’s inside the .git directory?
So, in my last blog post, I got a little bit into figuring out how
git init works. The central point of the functions associated with
git init was creating the files that are stored in the
.git directory. I figured that the logical next step is to take a look at what kinds of things are inside the
.git directory and figure out what they are required for. To do this, I started by initializing Git inside an empty directory.
$ ls -lFh .git/ total 24 -rw-r--r-- 1 captainsafia staff 23B Mar 4 20:30 HEAD drwxr-xr-x 2 captainsafia staff 64B Mar 4 20:30 branches/ -rw-r--r-- 1 captainsafia staff 137B Mar 4 20:30 config -rw-r--r-- 1 captainsafia staff 73B Mar 4 20:30 description drwxr-xr-x 12 captainsafia staff 384B Mar 4 20:30 hooks/ drwxr-xr-x 3 captainsafia staff 96B Mar 4 20:30 info/ drwxr-xr-x 4 captainsafia staff 128B Mar 4 20:30 objects/ drwxr-xr-x 4 captainsafia staff 128B Mar 4 20:30 refs/
I set up the
ls statement above so that you can easily see which objects are directories and which are files. Let’s start at the top, shall we? What’s in HEAD?
$ cat .git/HEAD ref: refs/heads/master
Oh, interesting! So it looks like it stores the reference to the current branch we are on. So assuming this is true if I check out a new branch, the contents of HEAD should change.
$ git checkout -b new-branch $ cat .git/HEAD ref: refs/heads/new-branch
So the next thing to look at would be the
branches/ directory, but I find myself more intrigued by the
refs/ directory since it was referenced in the HEAD file above. What can we find in there?
$ ls .git/refs heads tags $ ls .git/refs/heads
refs/ directory contains the
tags/ directories. However, the
refs/heads directory doesn’t contain anything. I think this is because I haven’t committed anything yet so there isn’t really a commit to reference here. So, I assume that if I create a commit, the
refs/heads/master file should be populated.
$ touch test.txt $ git add test.txt $ git commit -m "Initial commit" [new-thing (root-commit) 38eea52] Initial commit 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test.txt $ ls .git/refs/heads new-thing $ cat .git/refs/heads/new-thing 38eea52e57f993d5c594aa7091cc9377b6063f5c
OK! That makes sense. I created a file and committed to the
new-thing branch. Shortly after that, a
refs/heads/new-thing file was located, and its contents consisted of the commit hash of the commit that I just made. This makes sense. What happens if I make another commit?
$ echo "Some content." >> test.txt $ git add test.txt $ git commit -m "Change #1" [new-thing 63916e1] Change #1 1 file changed, 1 insertion(+) $ cat .git/refs/heads/new-thing 63916e1e1856e84d1f26dc9e0c26ec5b2344b991
What happens if I go back to the older commit?
$ git checkout 38eea52 $ cat .git/refs/heads/new-thing 63916e1e1856e84d1f26dc9e0c26ec5b2344b991 $ cat .git/HEAD 38eea52e57f993d5c594aa7091cc9377b6063f5c
So, when I check out the older commit, I enter detached HEAD mode. In this state, the head reference on the
new-thing branch still points to the latest commit but the HEAD file points to the commit hash associated with our initial commit. This makes sense because when we are in the detached HEAD mode, we technically aren’t in the
new-thing branch, so it doesn’t make sense to have HEAD reference it.
Speaking of which, what’s inside the
$ ls -lFh .git/branches/
Hm. Nothing. That surprised me! I expected there to be some sort of reference to the
master branches. Some Googling revealed that these
branches/ directory is actually a presently-deprecated way to store references to URLs that are used in commands like
git pull and
The next thing on the list is the
config file. I’ve tinkered around with this configuration file before, so I’m familiar with its innards. For those unfamiliar, the file looks a little something like this.
$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true
I think I’ll probably do another blog post where I look at all the possible ways that you can configure this file in more details. For now, I’ll try my best to keep my focus on the
.git directory. Harharhar!
The next object on the list is the
description file. What’s in there?
$ cat .git/description Unnamed repository; edit this file 'description' to name the repository.
Hm. Interesting. It states the repository is unnamed. I would expect the name of the repository to be the name of the directory I am working in (
git-test), but that is not the case. I did some Googling, and it turns out that this particular file is used by the GitWeb program (it’s like GitHub, except it’s not GitHub). This web application uses the data stored in the ‘description’ file.
I’m also a little familiar with the purpose of the next object on the list, the
hooks/ directory. Usually, I will use it to configure a post-commit hook that will run the Prettier code formatter.
$ ls .git/hooks/ applypatch-msg.sample post-update.sample pre-commit.sample pre-rebase.sample prepare-commit-msg.sample commit-msg.sample pre-applypatch.sample pre-push.sample pre-receive.sample update.sample
By default, the directory contains some sample files that showcase how you can use hooks. Hooks are basically just shell scripts, so you would program them the same you code any other shell script.
The next directory in the list is the
info/ directory. What’s in that directory?
$ ls .git/info/ exclude
Interesting. What’s in the
$ cat .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] # *~
So it looks like the exclude file, which is aptly-named, contains a list of some files that you are likely to want to exclude when running certain Git commands.
The last directory in the list is the
objects directory. I discovered how this directory was initialized in my last blog post, but let’s check out what’s in it now.
$ ls .git/objects/ 38 5e 63 e6 f9 info pack
So, from looking at some of the subdirectories in the
objects/ file, I can see that they are clearly references to the commit hashes of the commits that were made. This brings up a pretty important question, in my opinion. What exactly is an object? I’m gonna be a little lazy here and just link y’all to a good article that I found that explains the contents of the
.git/objects directory in full.
So that exploration was pretty interesting. I definitely feel much more confident about the content of the
.git directory. I also have a couple more questions to answer.
- What are the options available in the
- How is the hierarchy of subdirectories in
- How are the scripts in the
hooksdirectory integrated into Git’s command?
Until next time!