Git Finally Explained: Understand It Once and Stop Memorizing Commands
Most developers use Git every day.
They commit, push, pull, and merge without thinking much about what happens behind the scenes. Everything feels fine until something goes wrong. Then comes the panic: searching for commands online, copying solutions from Stack Overflow, and hoping not to make things worse.
The reality is that many developers know Git commands but do not truly understand Git itself.
Once you understand the concepts underneath, Git becomes far less intimidating. Instead of memorizing commands, you start thinking about how Git actually works.
Git Is a Database
At its core, Git is a database.
The fundamental unit of that database is the commit.
Many developers think a commit stores only the changes they made. In reality, a commit is a complete snapshot of the entire project at a specific moment in time.
Every commit contains:
- A snapshot of every file in the project
- Metadata such as the author, timestamp, and commit message
- A reference to its parent commit
When you create a new commit, Git saves the current state of the project and links it back to the previous commit.
Over time, this creates a chain of commits where each commit points to the one that came before it.
The very first commit is unique because it has no parent. It marks the beginning of the project's history.
Understanding Git's History Graph
If development happened in a perfectly straight line, project history would be simple.
In reality, developers create branches, work on features independently, fix bugs, and eventually merge their work together.
As a result, Git's history becomes a graph rather than a simple line.
This structure is known as a Directed Acyclic Graph (DAG).
Despite the intimidating name, the concept is straightforward.
- Directed means relationships move in one direction only. Commits point to their parents.
- Acyclic means there are no loops. A commit cannot become its own ancestor.
- Graph simply means a collection of nodes and connections.
Think of it as a family tree. Children know their parents, but parents do not know their future children.
This graph represents every decision made throughout the life of the project.
Because each commit contains a complete snapshot, Git can instantly show the exact state of the project at any point in history.
Branches Are Just Pointers
One of the biggest misconceptions about Git is that branches are separate copies of a project.
They are not.
A branch is simply a pointer to a commit.
You can think of a branch as a sticky note attached to a specific commit.
When you create a branch called feature-login, Git does not duplicate files or create a new project. It simply creates a pointer that references an existing commit.
When you make a new commit on that branch:
- Git creates the commit.
- The commit points back to its parent.
- The branch pointer moves forward to the new commit.
That is all branching really is.
This is why creating branches is nearly instantaneous.
The main branch is not special either. It is simply another pointer that teams have agreed to use as their primary branch.
HEAD: Knowing Where You Are
Git also maintains a special pointer called HEAD.
HEAD represents your current position in the repository.
Most of the time, HEAD points to a branch.
For example:
HEAD → main
main → latest commit
When you switch branches, HEAD moves to point at a different branch.
However, if you check out a specific commit directly, HEAD points to that commit instead of a branch.
This is called a detached HEAD state.
Despite its intimidating name, detached HEAD is not dangerous by itself.
The risk comes when you start creating commits while detached.
Those commits are not attached to any branch. If you later switch away without creating a branch, those commits become orphaned and may eventually be removed by Git's cleanup process.
That is why Git warns you when entering a detached HEAD state.
The Three Areas of Git
To understand Git commands properly, you must understand the three places where code can exist.
1. Working Directory
These are the files you actively edit in your code editor.
2. Staging Area
Also called the index, this is where you prepare changes for the next commit.
3. Repository
This is Git's permanent history containing all commits.
A typical workflow looks like this:
Working Directory
↓
git add
↓
Staging Area
↓
git commit
↓
Repository
Many Git commands affect these areas differently, which explains why some commands feel confusing.
Understanding Checkout
The purpose of git checkout is simple.
It changes where HEAD points.
When you switch branches or view an old commit, Git updates your working directory to match the selected commit.
What checkout does not do:
- It does not modify commits.
- It does not move branches.
- It does not rewrite history.
Checkout simply changes your viewpoint.
Think of it as traveling to a different point in your project's timeline without altering the timeline itself.
Understanding Reset
Unlike checkout, git reset moves a branch.
Suppose your main branch points to the latest commit.
When you run:
git reset <commit>
Git moves the branch pointer to the specified commit.
The newer commits still exist in Git's database, but no branch points to them anymore.
Reset comes in three modes.
Soft Reset
git reset --soft <commit>
What changes:
- Branch moves
- Staging area remains unchanged
- Working directory remains unchanged
Use this when you want to combine multiple commits into one.
Mixed Reset
git reset <commit>
or
git reset --mixed <commit>
What changes:
- Branch moves
- Staging area resets
- Working directory remains unchanged
Use this when you want to reorganize commits or restage changes differently.
Hard Reset
git reset --hard <commit>
What changes:
- Branch moves
- Staging area resets
- Working directory resets
This removes uncommitted changes from your local machine.
Use it only when you are absolutely certain you want to discard your current work.
Understanding Revert
git revert follows a completely different philosophy.
Instead of moving branches or removing commits, it creates a new commit that undoes the effects of an older commit.
For example:
git revert abc123
If commit abc123 added 50 lines of code, Git creates a new commit that removes those same 50 lines.
The original commit remains in history.
Nothing is erased.
This makes revert the safest option when working with commits that have already been shared with other developers.
Checkout vs Reset vs Revert
| Command | Moves HEAD | Moves Branch | Creates Commit | Safe for Shared History |
|---|---|---|---|---|
| Checkout | ✅ | ❌ | ❌ | ✅ |
| Reset | ✅ | ✅ | ❌ | ❌ |
| Revert | ❌ | ❌ | ✅ | ✅ |
A simple rule to remember:
- Checkout explores history.
- Reset rewrites local history.
- Revert records an undo operation.
Understanding Rebase
Rebase is often the most misunderstood Git command.
Imagine this situation:
A --- B --- C (feature)
\
X --- Y (main)
Your feature branch was created earlier, while the main branch continued to move forward.
You now need to integrate your work.
Option 1: Merge
A --- B --- C -------- M
\ /
X --- Y ----------
Git creates a merge commit with two parents.
History reflects exactly what happened.
Option 2: Rebase
X --- Y --- B' --- C'
Instead of creating a merge commit, Git rebuilds your commits on top of the latest main branch.
This is where many developers misunderstand what rebase actually does.
Git does not move commits.
It creates entirely new commits.
Every Git commit is identified by a hash generated from:
- Content
- Metadata
- Parent commit
Changing the parent creates a different hash.
Therefore:
- Commit
BbecomesB' - Commit
CbecomesC'
The original commits eventually become orphaned.
This process produces a cleaner and more linear history.
However, it also rewrites history.
That is why you should avoid rebasing commits that other developers have already pulled.
Your Lifeline: Git Reflog
Everyone eventually makes mistakes.
A hard reset goes wrong.
A rebase removes commits.
An accidental checkout leaves work stranded.
Before assuming your work is lost, run:
git reflog
Reflog records where HEAD has been recently.
It tracks:
- Checkouts
- Commits
- Resets
- Rebases
In many cases, the commit you thought was gone is still available.
Find its hash and create a branch pointing to it:
git branch recovery-branch <commit-hash>
Your work is back.
Git rarely deletes anything immediately.
Most "lost" work is simply hidden.
Reflog is often the fastest way to recover it.
Final Thoughts
Git becomes much easier once you stop thinking in commands and start thinking in pointers and snapshots.
Remember the core ideas:
- Git is a database of snapshots.
- Commits point to their parents.
- Branches are simple pointers.
- HEAD tracks your current location.
- Checkout changes your view.
- Reset moves branches.
- Revert adds corrective history.
- Rebase recreates commits with new parents.
- Reflog helps recover mistakes.
The next time Git behaves unexpectedly, you will not need to blindly copy commands from the internet.
You will understand what is happening underneath and know exactly how to fix it.
