How Git Works
Published on: 03 January 2026
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;