SovranCode
HomeCourses Git & GitHub Git Internals
This device
Course contentsGit Internals · 41 topics

1. Git Fundamentals

Git IntroductionGit Version ControlGit vs GitHubGit InstallationGit Working TreeGit RepositoryGit CommitGit .gitignoreGit Project: Learning Journal

2. Git History

Git LogGit DiffGit ResetGit RevertGit StashGit Project: Messy Repository

3. Branches & History

Git BranchGit MergeGit Merge ConflictsGit RebaseGit Cherry-PickGit TagsGit Project: Feature Branch

4. Remotes and GitHub

Git RemoteGitHub AuthenticationGitHub RepositoryGitHub ForkGitHub IssuesGitHub PagesGit Project: Two Clone Sync

5. GitHub Collaboration

GitHub Pull RequestsGitHub Code ReviewGit Branching WorkflowGitHub Branch ProtectionGitHub CollaborationGitHub ContributingGit Project: Reviewed Pull Request

6. Automation and Professional Git

GitHub ActionsGit InternalsGit RecoveryGitHub SecurityGit Project: Team Repository CIProject
Learn Git & GitHub40 complete · 1 planned

1. Git Fundamentals

Git IntroductionGit Version ControlGit vs GitHubGit InstallationGit Working TreeGit RepositoryGit CommitGit .gitignoreGit Project: Learning Journal

2. Git History

Git LogGit DiffGit ResetGit RevertGit StashGit Project: Messy Repository

3. Branches & History

Git BranchGit MergeGit Merge ConflictsGit RebaseGit Cherry-PickGit TagsGit Project: Feature Branch

4. Remotes and GitHub

Git RemoteGitHub AuthenticationGitHub RepositoryGitHub ForkGitHub IssuesGitHub PagesGit Project: Two Clone Sync

5. GitHub Collaboration

GitHub Pull RequestsGitHub Code ReviewGit Branching WorkflowGitHub Branch ProtectionGitHub CollaborationGitHub ContributingGit Project: Reviewed Pull Request

6. Automation and Professional Git

GitHub ActionsGit InternalsGit RecoveryGitHub SecurityGit Project: Team Repository CIProject
PREVIOUS LESSONGitHub Actions
NEXT LESSONGit Recovery
6. Automation and Professional Git 85 min

Git Internals

Inspect objects and refs until commit, branch, and tag are locations in a graph rather than product-button names.

What you will leave with

You will walk from HEAD to a commit object, its tree, and a blob; show that identical file bytes share one blob id; and read the branch ref that names that commit. You will not hand-edit .git/objects, will not treat a SHA as encryption, and will not use GitHub for this lab.

Objects are Git, not GitHub

Git Working Tree named three places: working tree, index, object database. This lesson opens the third place. Git Commit already wrote a commit object. Git Branch already treated a branch as a pointer. Internals makes those sentences checkable with git cat-file, git ls-tree, and git rev-parse.

GIT

Objects and refs

Blobs, trees, commits, and tag objects live in the repository database. Branch and tag names are refs that point at objects. They travel with a clone.

GITHUB

Not this lesson

Pull requests, Actions checks, and the website file browser are GitHub. A SHA on github.com is a view of a Git object. It is not a different kind of object.

# These are Git. None of them talks to GitHub.
git cat-file -t HEAD
git cat-file -p HEAD
git ls-tree HEAD
git hash-object note.txt
git rev-parse HEAD

Use a disposable local repository. A journal or feature-branch project folder is fine if you still control it. You do not need a GitHub remote. You do not need gh.

A hash is an identifier, not a lock

Anyone who can read the repository can git cat-file -p a blob. Deleting a filename from a later commit does not delete earlier blobs. Secrets still belong in Git .gitignore, not in objects you hoped nobody would open.

Four object types

commit  9c2e…     ← refs/heads/main and HEAD name this
  │
  ├─ parent  4a11…  (earlier commit, or none on a root)
  ├─ tree    7f08…  (the snapshot)
  ├─ author / committer / message
  │
  ▼
tree 7f08…
  note.txt     blob  e4d9…   "hello\n"
  copy.txt     blob  e4d9…   same content, same blob
  src/         tree  1a2b…

A filename lives on the tree entry. The blob does not know it.

blob

File content. No path, no author, no newline policy beyond the bytes you stored. git cat-file -t prints blob.

tree

A directory listing: mode, type, object id, name. Nested directories are trees. This is the snapshot Git Commit recorded from the index.

commit

Root tree, zero or more parents, author, committer, dates, message. History is commits pointing at parents, not a folder of copies.

tag

An annotated tag is a fourth object: tagger, message, and a pointer to another object. A lightweight tag is only a ref. Git Tags already split those; here you git cat-file -t v1.4.0 and read tag or commit.

Git Log walks commits. git show often prints a commit plus a patch against its parent. That patch is a convenience. The stored object does not contain a unified diff.

Name an object, then print it

HEAD is a revision. Git resolves it to a commit (when you are on a branch). git cat-file -t prints the type. git cat-file -p pretty-prints the content.

# Disposable local repository. GitHub is not required.
git switch main
git rev-parse HEAD
git cat-file -t HEAD
git cat-file -p HEAD
QUICK CHECK

Test what you learned

Type the Git command that prints the object type of HEAD.

QUICK CHECK

Test what you learned

Type the Git command that pretty-prints the object HEAD names.

The first lines of a commit are tree and, except on a root commit, parent. Those values are object ids. Copy one and run git cat-file -t on it. You are no longer taking the vocabulary on trust.

git hash-object note.txt hashes the file the same way Git names a blob. If the path is already committed unchanged, that id matches the blob column of git ls-tree. hash-object without -w prints the id and writes nothing. Do not add -w in this lab; dangling blobs are noise, not a lesson.

Walk HEAD to a blob

# HEAD^{tree} is the root tree of that commit.
git rev-parse HEAD^{tree}
git ls-tree HEAD
git cat-file -t <blob-id-from-ls-tree>
git cat-file -p <blob-id-from-ls-tree>

# Same blob id without looking it up first:
git hash-object note.txt
QUICK CHECK

Test what you learned

Type the Git command that lists the root tree of HEAD.

Each line from git ls-tree HEAD is mode, type, object id, and name. A blob line is a file. A tree line is a directory; git ls-tree that id to go one level down. git ls-tree -r HEAD flattens the walk. Learn the one-level listing first so you can see a nested tree as an object, not a path trick.

echo 'hello' > note.txt
git add note.txt
git commit -m "Add note"

echo 'hello' > copy.txt
git add copy.txt
git commit -m "Add a copy of the same bytes"

git ls-tree HEAD
# note.txt and copy.txt should list the same blob id.
git hash-object note.txt
git hash-object copy.txt

Identical bytes share a blob. Different bytes create a new blob. A later commit that only changes a message still points at a tree; if the tree did not change, Git reuses that tree object too. That is why two snapshots can be cheap without copying the project.

The working tree is not the blob

Edit note.txt and save. git hash-object note.txt changes. git cat-file -p of the committed blob does not. The object database still holds the last committed bytes until a new commit records a new blob.

Refs are names, not copies

# HEAD is usually a pointer to a branch name, not a commit id.
git symbolic-ref HEAD
# prints refs/heads/main

git rev-parse HEAD
git rev-parse refs/heads/main

# On a typical new repo these two files match that SHA:
# .git/HEAD              →  ref: refs/heads/main
# .git/refs/heads/main   →  9c2e…

git rev-parse HEAD and git rev-parse refs/heads/main print the same SHA while you are on main. git symbolic-ref HEAD prints the branch name HEAD currently follows. That is the attached state Git Branch taught. Detached HEAD means .git/HEAD stores a commit SHA directly instead of ref: refs/heads/….

.git/HEAD

Usually one line: ref: refs/heads/main. It is not the commit object.

.git/refs/heads/

One file per local branch, contents a SHA plus newline. Creating a branch adds a name at a commit; it does not duplicate blobs.

.git/refs/tags/

Lightweight tags store a SHA here. Annotated tags store the tag object's id; peel with v1.4.0^{} as Git Tags already showed.

packed-refs

After enough history, Git may pack ref names into .git/packed-refs. The commands still work. Do not invent a second naming scheme by editing that file.

Read those files if you want the disk picture. Change refs with git switch, git branch, and git tag—not with a text editor. A truncated SHA in a ref file is a broken repository, not a shortcut.

Reachable is not the same as exists

An object can sit in the database without any branch, tag, or HEAD naming it. Commands that walk history start from refs. That is why git log can miss a commit you still git cat-file -p if you still know the SHA.

Git Recovery teaches reflog and how to get a durable name back on a commit. This lesson only needs the distinction: deleting a branch does not immediately delete objects. Running git gc may eventually prune unreachable ones. Do not prune as homework. Do not delete files under .git/objects to make the folder look tidy.

Leave gc and reflog for Recovery

You may see git gc pack loose objects. Packed objects remain readable with git cat-file. Do not unpack packs by hand. Recovery begins by preserving evidence, not pruning it.

What not to invent

Editing objects

No hex editors in .git/objects. No renaming SHA files. Inspection is cat-file.

A GitHub tour

Do not open github.com to “see the blob.” The object is already local. Remotes copy Git objects; they do not replace them.

hash-object -w as a habit

Writing a blob that no tree names leaves junk. Print the id. Let git add write objects that belong to a snapshot.

Treating SHA as a secret

The id is a name. It does not hide content. GitHub Security signs exact object bytes without encrypting them.

Guided practice: open one commit

  1. 01
    Pick a local repository you own

    Clean status. On a branch. git rev-parse HEAD prints a SHA.

  2. 02
    Print the commit

    git cat-file -t HEAD is commit. git cat-file -p HEAD shows tree, parent, message.

  3. 03
    List the tree

    git ls-tree HEAD. Copy one blob id. git cat-file -p that id. Confirm it matches the working file if that path is unchanged.

  4. 04
    Prove a ref

    git symbolic-ref HEAD and git rev-parse refs/heads/$(git branch --show-current) match git rev-parse HEAD.

  5. 05
    Optional reuse

    Commit a second path with the same bytes. git ls-tree HEAD shows one blob id twice.

Independent lab: prove the graph

  1. In a disposable local repository you own, record two commits. The second commit adds a second path whose content matches an existing file so two tree entries share one blob.
  2. Show git cat-file -t HEAD as commit and git cat-file -p HEAD with a tree line and a parent line. Copy the tree id. Show git ls-tree HEAD with two names and the same blob id. Show git hash-object on either file matching that blob.
  3. Show git symbolic-ref HEAD and the SHA from git rev-parse HEAD. If you read .git/HEAD and the branch file under .git/refs/heads/, they must agree with those commands. Do not edit them.
  4. Write six lines: commit id, tree id, shared blob id, current branch ref (for example refs/heads/main), one sentence that distinguishes an object from a ref, and one sentence that says why you did not edit .git/objects.
  5. Do not push. Do not open GitHub. Do not run git gc --prune=now. Keep this object walk separate from the published Git Recovery investigation.
Definition of done

You can start at HEAD, print a commit, list its tree, show a blob, and name the ref that points at that commit—on a repository you own, without treating GitHub or a text editor as the database.

Common internals mistakes

Calling the working file the blob

The blob is committed (or hashed) bytes. The working file can disagree.

Calling a branch a folder of copies

The ref is a SHA. Checkout writes a worktree from the commit's tree.

Expecting git show to be the object

show adds a patch. git cat-file -p is the stored commit.

Cleaning .git by hand

Unknown files under objects are not clutter. Leave them. Recovery and gc come later.

Lesson review

You can inspect Git objects and refs until commit, branch, and tag are locations in a graph rather than product-button names. Git Recovery uses that model to preserve displaced commits and isolate regressions. GitHub Security binds a signing key to exact commit bytes. The team CI project stays planned.

  • I can git cat-file -t HEAD and git cat-file -p HEAD and read tree, parent, and message.
  • I can git ls-tree HEAD, open a blob, and explain why two names can share one blob id.
  • I can name the current branch ref and show it points at the same SHA as HEAD.
  • I inspect with Git commands and leave .git/objects unedited.

Related lessons

  • Git Working Tree — The three-place model is the same object database.
  • Git Commit — A commit object stores a tree, parents, and a message.
  • Git Branch — HEAD and branch names are refs, not copies of files.
  • Git Tags — A tag is another named ref in that graph.
  • Git Log — show pretty-prints a commit; cat-file shows the same object without inventing a second database.
  • Git Recovery — Reflog can give a reachable object a durable name again.
KNOWLEDGE CHECK

Check your Git internals model

Keep objects, refs, and the working tree distinct. A SHA is a Git name, not a GitHub page and not encryption.

01What is a Git object?
02What does git cat-file -p HEAD show?
03Where does a filename live?
04What is a branch such as refs/heads/main?
05Does git hash-object note.txt talk to GitHub?
06After git gc, can git cat-file still print a reachable commit?
07When should you hand-edit files under .git/objects?
PREVIOUS LESSONGitHub Actions
NEXT LESSONGit Recovery
ON THIS PAGEGit InternalsObjects are Git, not GitHubFour object typesName an object, then print itWalk HEAD to a blobRefs are names, not copiesReachable is not the same as existsWhat not to inventGuided practice: open one commitIndependent lab: prove the graphCommon internals mistakesLesson reviewKnowledge checkRelated lessons
Course contents