How Git Works

Published on: 03 January 2026

Tags: #content-addressable #version-control #storing-snapshots


The Basics (Blob & Tree)

graph TD
    %% Styling
    classDef tree fill:#E1F5FE,stroke:#01579B,stroke-width:2px;
    classDef blob fill:#FFF3E0,stroke:#E65100,stroke-width:2px;

    %% Nodes - Note that I removed permissions from inside the nodes
    RootTree["Tree (Root Hash)"]:::tree
    SrcTree["Tree (src Hash)"]:::tree

    BlobReadMe("Blob (README Hash)
Content: 'Start Project...'"):::blob BlobScript("Blob (Script Hash)
Content: 'console.log...'"):::blob %% Connections - Metadata is on the link! RootTree -->|mode: 040000
name: src| SrcTree RootTree -->|mode: 100644
name: README.md| BlobReadMe SrcTree -->|mode: 100644
name: script.js| BlobScript

The Commit (The Snapshot)

graph TD
    %% Styling
    classDef commit fill:#F3E5F5,stroke:#4A148C,stroke-width:2px;
    classDef tree fill:#E1F5FE,stroke:#01579B,stroke-width:2px;
    classDef blob fill:#FFF3E0,stroke:#E65100,stroke-width:2px;

    subgraph "Git Object Database (.git/objects)"
        direction TB
        Commit["Commit Object (Hash C1)
type: commit"]:::commit RootTree["Tree (Root Hash)
type: tree"]:::tree Blob1["Blob Hash A
type: blob"]:::blob Blob2["Blob Hash B
type: blob"]:::blob end %% Connections Commit -->|Points to tree| RootTree RootTree -->|name: README.md| Blob1 RootTree -->|name: main.js| Blob2

History & Efficiency (The DAG)

graph LR
    %% Styling
    classDef commit fill:#F3E5F5,stroke:#4A148C,stroke-width:2px;
    classDef tree fill:#E1F5FE,stroke:#01579B,stroke-width:2px;
    classDef blob fill:#FFF3E0,stroke:#E65100,stroke-width:2px;
    classDef newblob fill:#E8F5E9,stroke:#1B5E20,stroke-width:2px,stroke-dasharray: 5 5;

    %% COMMITS
    subgraph "Time (History)"
        CommitA("Commit A
(Initial)"):::commit CommitB("Commit B
(Changed file1)"):::commit end %% OBJECTS subgraph "Space (Object Database)" %% Tree A Structure TreeA["Tree A (Root)"]:::tree Blob1("Blob 1
(file1.txt v1)"):::blob Blob2("Blob 2
(file2.txt)"):::blob %% Tree B Structure TreeB["Tree B (Root)"]:::tree Blob1_v2("Blob 3
(file1.txt v2)"):::newblob end %% RELATIONS %% History Flow (B points to A) CommitB --> |Parent| CommitA %% Commit A Pointers CommitA --> TreeA TreeA --> |file1.txt| Blob1 TreeA --> |file2.txt| Blob2 %% Commit B Pointers CommitB --> TreeB TreeB --> |file1.txt| Blob1_v2 %% THE MAGIC: REUSE TreeB --> |file2.txt| Blob2 %% Highlight the reuse link in Red linkStyle 6 stroke:#D32F2F,stroke-width:4px;

References (Branches & HEAD)

graph TD
    %% Styling
    classDef ptr fill:#FFF9C4,stroke:#FBC02D,stroke-width:2px,stroke-dasharray: 5 5;
    classDef file fill:#ECEFF1,stroke:#455A64,stroke-width:2px;
    classDef commit fill:#F3E5F5,stroke:#4A148C,stroke-width:2px;

    subgraph "Pointer Files (.git/)"
        HEAD("HEAD
(The Active Pointer)"):::ptr subgraph "refs/heads/" Main("main
(Text file)"):::file Feat("feature-x
(Text file)"):::file end end subgraph "Object Database" CommitA(Commit A):::commit CommitB(Commit B):::commit CommitC(Commit C):::commit end %% POINTER LOGIC HEAD -->|Contains: ref: refs/heads/main| Main Main -->|Contains: Hash C| CommitC Feat -->|Contains: Hash B| CommitB %% HISTORY CommitC -->|Parent| CommitB CommitB -->|Parent| CommitA

The Three-Tree Architecture

graph LR
    %% Styles
    classDef work fill:#FFEBEE,stroke:#D32F2F,stroke-width:2px;
    classDef stage fill:#E8F5E9,stroke:#388E3C,stroke-width:2px;
    classDef repo fill:#E3F2FD,stroke:#1976D2,stroke-width:2px;
    classDef action stroke:#333,stroke-dasharray: 5 5,fill:#fff;

    subgraph "Your Computer (Sandbox)"
        WD["Working Directory
(Actual Files)"]:::work end subgraph "Git Internal (.git)" Index["The Index / Stage
(Binary File: .git/index)"]:::stage Repo["Repository / HEAD
(Object Database)"]:::repo end %% Data Flow WD --> |git add| Index Index --> |git commit| Repo %% Reverse Flow Repo --> |git checkout| Index Index --> |git checkout| WD %% Comparisons linkStyle 0 stroke:#388E3C,stroke-width:2px; linkStyle 1 stroke:#1976D2,stroke-width:2px;

The Master Interaction Map

graph LR
    %% Styles
    classDef work fill:#FFEBEE,stroke:#D32F2F,stroke-width:2px;
    classDef stage fill:#E8F5E9,stroke:#388E3C,stroke-width:2px;
    classDef repo fill:#E3F2FD,stroke:#1976D2,stroke-width:2px;
    classDef diff stroke:#FF9800,stroke-dasharray: 5 5,stroke-width:2px,fill:none;

    subgraph "Your Files"
        WD[Working Directory]:::work
    end

    subgraph "Git Database"
        Index[The Index / Stage]:::stage
        Repo[Repository / HEAD]:::repo
    end

    %% CORE FLOW
    WD --> |git add| Index
    Index --> |git commit| Repo

    %% UNDO FLOW (The confusing part simplified)
    Repo --> |git reset --soft| Repo
    Repo --> |git reset --mixed| Index
    Repo --> |git reset --hard| WD

    %% DIFF SCOPES (What are you comparing?)
    WD <--> |git diff| Index
    Index <--> |git diff --staged| Repo
    WD <--> |git diff HEAD| Repo

    %% VISUAL ADJUSTMENTS
    linkStyle 2 stroke:#9E9E9E,stroke-width:1px;
    linkStyle 3 stroke:#388E3C,stroke-width:2px;
    linkStyle 4 stroke:#D32F2F,stroke-width:2px;

    linkStyle 5 stroke:#FF9800,stroke-width:2px;
    linkStyle 6 stroke:#FF9800,stroke-width:2px;
    linkStyle 7 stroke:#FF9800,stroke-width:2px;

Share this post

Share on X  •  Share on LinkedIn  •  Share via Email