TranslateProject/sources/tech/20240126 Inside .git.md
DarkSun 61215256e2 选题[tech]: 20240126 Inside .git
sources/tech/20240126 Inside .git.md
2024-01-27 05:21:57 +08:00

10 KiB
Raw Permalink Blame History

Inside .git

Hello! I posted a comic on Mastodon this week about whats in the .git directory and someone requested a text version, so here it is. I added some extra notes too. First, heres the image. Its a ~15 word explanation of each part of your .git directory.

You can git clone https://github.com/jvns/inside-git if you want to run all these examples yourself.

Heres a table of contents:

The first 5 parts (HEAD, branch, commit, tree, blobs) are the core of git.

HEAD: .git/head

HEAD is a tiny file that just contains the name of your current branch.

Example contents:


    $ cat .git/HEAD
    ref: refs/heads/main

HEAD can also be a commit ID, thats called “detached HEAD state”.

branch: .git/refs/heads/main

A branch is stored as a tiny file that just contains 1 commit ID. Its stored in a folder called refs/heads.

Example contents:


    $ cat .git/refs/heads/main
    1093da429f08e0e54cdc2b31526159e745d98ce0

commit: .git/objects/10/93da429...

A commit is a small file containing its parent(s), message, tree , and author.

Example contents:


    $ git cat-file -p 1093da429f08e0e54cdc2b31526159e745d98ce0
    tree 9f83ee7550919867e9219a75c23624c92ab5bd83
    parent 33a0481b440426f0268c613d036b820bc064cdea
    author Julia Evans <julia@example.com> 1706120622 -0500
    committer Julia Evans <julia@example.com> 1706120622 -0500

    add hello.py

These files are compressed, the best way to see objects is with git cat-file -p HASH.

tree: .git/objects/9f/83ee7550...

Trees are small files with directory listings. The files in it are called blobs.

Example contents:


    $  git cat-file -p 9f83ee7550919867e9219a75c23624c92ab5bd83
    100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    .gitignore
    100644 blob 665c637a360874ce43bf74018768a96d2d4d219a    hello.py
    040000 tree 24420a1530b1f4ec20ddb14c76df8c78c48f76a6    lib

The permissions here LOOK like unix permissions, but theyre actually super restricted, only 644 and 755 are allowed.

blobs: .git/objects/5a/475762c...

blobs are the files that contain your actual code

Example contents:


    $ git cat-file -p 665c637a360874ce43bf74018768a96d2d4d219a
    print("hello world!")

Storing a new blob with every change can get big, so git gc periodically packs them for efficiency in .git/objects/pack.

reflog: .git/logs/refs/heads/main

The reflog stores the history of every branch, tag, and HEAD. For (mostly) every file in .git/refs, theres a corresponding log in .git/logs/refs.

Example content for the main branch:


    $ tail -n 1 .git/logs/refs/heads/main
    33a0481b440426f0268c613d036b820bc064cdea
    1093da429f08e0e54cdc2b31526159e745d98ce0
    Julia Evans <julia@example.com>
    1706119866 -0500
    commit: add hello.py

each line of the reflog has:

  • before/after commit IDs
  • user
  • timestamp
  • log message

Normally its all one line, I just wrapped it for readability here.

remote-tracking branches: .git/refs/remotes/origin/main

Remote-tracking branches store the most recently seen commit ID for a remote branch

Example content:


    $ cat .git/refs/remotes/origin/main
    fcdeb177797e8ad8ad4c5381b97fc26bc8ddd5a2

When git status says “youre up to date with origin/main”, its just looking at this. Its often out of date, you can update it with git fetch origin main.

tags: .git/refs/tags/v1.0

A tag is a tiny file in .git/refs/tags containing a commit ID.

Example content:


    $ cat .git/refs/tags/v1.0
    1093da429f08e0e54cdc2b31526159e745d98ce0

Unlike branches, when you make new commits it doesnt update the tag.

the stash: .git/refs/stash

The stash is a tiny file called .git/refs/stash. It contains the commit ID of a commit thats created when you run git stash.


    cat .git/refs/stash
    62caf3d918112d54bcfa24f3c78a94c224283a78

The stash is a stack, and previous values are stored in .git/logs/refs/stash (the reflog for stash).


    cat .git/logs/refs/stash
    62caf3d9 e85c950f Julia Evans <julia@example.com> 1706290652 -0500    WIP on main: 1093da4 add hello.py
    00000000 62caf3d9 Julia Evans <julia@example.com> 1706290668 -0500    WIP on main: 1093da4 add hello.py

Unlike branches and tags, if you git stash pop a commit from the stash, its deleted from the reflog so its almost impossible to find it again. The stash is the only reflog in git where things get deleted very soon after theyre added. (entries expire out of the branch reflogs too, but generally only after 90 days)

A note on refs:

At this point youve probably noticed that a lot of things (branches, remote-tracking branches, tags, and the stash) are commit IDs in .git/refs. Theyre called “references” or “refs”. Every ref is a commit ID, but the different types of refs are treated VERY differently by git, so I find it useful to think about them separately even though they all use the same file format. For example, git deletes things from the stash reflog in a way that it wont for branch or tag reflogs.

.git/config

.git/config is a config file for the repository. Its where you configure your remotes.

Example content:


    [remote "origin"]
    url = git@github.com: jvns/int-exposed
    fetch = +refs/heads/*: refs/remotes/origin/*
    [branch "main"]
    remote = origin
    merge refs/heads/main

git has local and global settings, the local settings are here and the global ones are in ~/.gitconfig hooks

hooks: .git/hooks/pre-commit

Hooks are optional scripts that you can set up to run (eg before a commit) to do anything you want.

Example content:


    #!/bin/bash
    any-commands-you-want

(this obviously isnt a real pre-commit hook)

the staging area: .git/index

The staging area stores files when youre preparing to commit. This one is a binary file, unlike a lot of things in git which are essentially plain text files.

As far as I can tell the best way to look at the contents of the index is with git ls-files --stage:


    $ git ls-files --stage
    100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0   .gitignore
    100644 665c637a360874ce43bf74018768a96d2d4d219a 0   hello.py
    100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0   lib/empty.py

this isnt exhaustive

There are some other things in .git like FETCH_HEAD, worktrees, and info. I only included the ones that Ive found it useful to understand.

this isnt meant to completely explain git

One of the most common pieces of advice I hear about git is “just learn how the .git directory is structured and then youll understand everything!“.

I love understanding the internals of things more than anyone, but theres a LOT that “how the .git directory is structured” doesnt explain, like:

  • how merges and rebases work and how they can go wrong (for instance this list of what can go wrong with rebase)
  • how exactly your colleagues are using git, and what guidelines you should be following to work with them successfully
  • how pushing/pulling code from other repositories works
  • how to handle merge conflicts

Hopefully this will be useful to some folks out there though.

some other references:


via: https://jvns.ca/blog/2024/01/26/inside-git/

作者:Julia Evans 选题:lujun9972 译者:译者ID 校对:校对者ID

本文由 LCTT 原创编译,Linux中国 荣誉推出