Loading episodes…
0:00 0:00

Git Will Finally Make Sense: A Developer's Guide Beyond the Commands

00:00
BACK TO HOME

Git Will Finally Make Sense: A Developer's Guide Beyond the Commands

10xTeam December 15, 2025 10 min read

You use Git every single day. Commit, push, pull. It works until it doesn’t. And then you’re googling how to undo a git rebase at 11 p.m., mass-copying commands from Stack Overflow, praying you don’t make the situation worse.

An uncomfortable truth is that many experienced developers don’t truly understand Git. They memorize commands, but they don’t know what’s happening underneath. In the next few minutes, let’s change that. Let’s understand Git better and never fear it again.

Back to Basics: The Commit

Let’s start from zero. Forget everything you think you know. Git is a database, and the fundamental unit of that database is the commit.

So, what is a commit? It’s a snapshot. Think of it as a complete photograph of your entire project at one moment in time. It’s not just the changes you made; it’s the entire state of every single file.

Each commit contains three essential things:

  1. A pointer to the snapshot: This points to the complete state of every file exactly as it existed at that moment.
  2. Metadata: This includes who created the commit, when they created it, and the all-important commit message.
  3. A pointer to the parent commit: This is a reference to the commit that came directly before it.

When you make a new commit, Git saves the full state of your project and links it back to where you were. This creates a chain. Each commit points to its parent, and those parents point to their parents, all the way back to the very first commit. That initial commit is special; it has no parent and serves as the origin point of your project’s history.

Later, when you merge branches, you’ll create commits with two parents, but for now, remember this crucial rule: commits point backwards. Always backwards. Children know their parents, but parents never know their future children.

The Project History as a Graph

If everyone just committed one after another, you’d have a simple, straight-line history. But real development is messier. You branch off for a new feature. A colleague branches to fix a bug. Suddenly, you have commits sharing the same parent but heading in different directions. Then you merge, creating a commit with two parents.

This structure has a name: a Directed Acyclic Graph (DAG). It sounds intimidating, but it’s not. Think of it like a family tree.

  • Directed means relationships only go one way. Children point to parents, never the reverse.
  • Acyclic means there are no loops. You can’t be your own grandparent; you can’t create a cycle in history.
  • Graph simply means it’s a collection of nodes (commits) and connections (the links between them).

This graph is your project’s history. Every branch, every merge, every decision any developer ever made is captured in this structure. Here’s what makes Git so powerful: because every commit is a complete snapshot, you can jump to any point in this graph and see your project exactly as it existed. No reconstruction, no replaying changes. It’s just there.

Branches are Just Sticky Notes

Now, this graph can get complex. How do we navigate it? How does Git know which commits matter? That’s where branches come in, and they are far simpler than you’d believe.

When learning Git, people often assume branches are heavy, complex things—a whole separate copy of the codebase. This is completely wrong.

A branch is just a sticky note. It’s a pointer, a tiny text file that contains one single piece of information: the hash of a commit. That’s it. When you create a new branch called feature-login, Git creates a small file that says this branch points to commit A1B23C. Nothing more.

Branches are just labels stuck on different commits. The commits themselves have no idea what branches exist. Branches don’t contain commits; they point at them. When you make a new commit while on a branch, Git performs two simple steps:

  1. It creates the new commit, pointing back to where you were.
  2. It moves the sticky note (the branch pointer) forward to the new commit.

That’s branching. The entire concept. This is why creating a branch is instantaneous. You’re not copying anything; you’re just placing a sticky note. And what about main? It’s not special. It’s just another sticky note that, by convention, we’ve agreed is the primary line of work.

Where Are You? Meet HEAD

So we have commits, and we have branches pointing at commits. But how does Git know where you are and what you’re working on? Meet HEAD.

HEAD is Git’s way of tracking your current location. It’s another pointer, but usually, instead of pointing directly at a commit, it points at a branch. When you’re on the main branch, HEAD points to main, and main points to a commit. That’s your current location.

Run git checkout feature, and HEAD moves to point at the feature branch. You’re now working on that line of history.

But here’s a situation that confuses people: the detached HEAD state. What if you check out a specific commit hash, not a branch? Now, HEAD points directly to that commit, with no branch in between. This isn’t as scary as it sounds if you understand what’s happening. You can still work and make commits, but no branch is following along. When you switch away, those new commits are orphaned—floating in space with no branch pointing to them. Eventually, Git’s garbage collection will clean them up.

Imagine a developer checks out an old commit to test something, finds a bug, fixes it, and commits the fix right there. Then they run git checkout main to merge their work. The fix vanishes. It was never on a branch. It was orphaned, then garbage collected. Hours of work, gone. This is why Git warns you about a detached HEAD—not because your repository is broken, but because anything you commit won’t be saved unless you create a new branch to hold it.

The Three Arenas: Working Directory, Staging Area, and Repository

Before we talk about undoing things, we need to understand one more core concept. Git has three areas where your code can live:

  1. The Working Directory: The actual files on your disk, what you see in your code editor.
  2. The Staging Area (or Index): A waiting room where you prepare what will go into your next commit.
  3. The Repository: The .git directory, the database of commits, the permanent history.

When you edit a file, it changes in your working directory. When you run git add, you move a copy of those changes to the staging area. When you run git commit, Git takes everything in the staging area and creates a new, permanent commit in the repository. Understanding these three layers is the key to mastering commands that undo changes.

Undoing Mistakes: Checkout, Reset, and Revert

checkout, reset, and revert all seem to undo things, but they perform completely different operations. Confusing them can cost you work.

  • git checkout: This command’s main job is to move HEAD. Running git checkout main points HEAD to the main branch. Running git checkout C1 points HEAD directly to that commit (a detached state). Your working directory updates to match that commit’s snapshot. Importantly, no commits are changed, and no branches are moved. History is untouched. You’re just looking around. It’s safe and non-destructive.

  • git reset: This command is more dangerous; it moves a branch pointer. When you’re on main and run git reset C1, you are telling Git to move the main branch to point at commit C1. Any commits that came after C1 still exist in the database for a while, but they are now orphaned. reset has three modes:
    • --soft: Moves the branch pointer but leaves your staging area and working directory unchanged. The changes from the “undone” commits will appear as staged, ready to be committed again. Useful for squashing multiple commits into one.
    • --mixed (the default): Moves the branch pointer and resets the staging area to match the target commit. Your changes still exist in your working directory files but are unstaged.
    • --hard: Moves the branch pointer and resets both the staging area and the working directory. Your files on disk are changed. Uncommitted work is gone. Be very careful with this command, as it can lead to permanent data loss for uncommitted changes.
  • git revert: This command takes a completely different philosophy. It doesn’t move or abandon anything. Instead, revert creates a new commit that does the exact opposite of an old commit. If commit C added 50 lines, git revert C creates a new commit D that removes those same 50 lines. The original history is preserved. You’ve simply recorded a new decision: “we decided to undo what we did earlier.” This is the safe way to undo something that has already been pushed and shared with others.

The Power and Peril of Rebase

Finally, the command that confuses most developers: rebase.

Let’s set the scene. You created a feature branch from main and made a few commits. Meanwhile, your colleagues have updated main with their own commits. You have two options to integrate their changes:

  1. Merge: This creates a new “merge commit” with two parents, showing the true, parallel history of the work.
  2. Rebase: This takes your commits and replays them, one by one, on top of the latest version of main.

You must understand this: a commit’s identity is its hash, which is generated from its content, metadata, and parent pointer. Change any of those, and you get a completely different commit. Git can’t “move” commits. So, rebase works by creating new commits. It looks at your first commit, calculates the changes, and creates a new commit with those same changes but with the latest main as its parent. It repeats this for all your commits, creating a clean, linear history.

The old commits are orphaned and will eventually be garbage collected. This is why you never rebase commits that others have seen or pulled. If a colleague has the old commits and you push the newly rebased ones, Git sees them as completely unrelated work, leading to a nightmare of duplicate changes and conflicts. On local branches you haven’t shared, however, rebase is a powerful tool for keeping history linear and clean.

Your Safety Net: The Reflog

You’ve made a mistake. You ran git reset --hard by accident. You rebased incorrectly. Your commits are gone. What do you do?

Run git reflog.

The reflog is a log of everywhere HEAD has pointed recently. Every checkout, every commit, every reset. Those “lost” commits from your reset or the old commits from before your rebase are probably still here. Find the hash in the reflog, create a branch pointing to it (git branch recovered-work <hash>), and you’ve recovered your work.

Git almost never truly deletes anything immediately; it just hides it. The reflog is your map to finding it.

Conclusion

Let’s step back. Git is a database of snapshots. Commits point to parents, forming a graph. Branches and HEAD are just pointers telling Git what matters and where you are.

  • checkout moves your view.
  • reset moves branches.
  • revert adds corrective history.
  • rebase replays commits with new parents.
  • And when everything goes wrong, reflog is your safety net.

The next time something breaks, you won’t be copying Stack Overflow commands and praying. You’ll be thinking in graphs and pointers. You’ll know exactly what happened and exactly how to fix it.


Join the 10xdev Community

Subscribe and get 8+ free PDFs that contain detailed roadmaps with recommended learning periods for each programming language or field, along with links to free resources such as books, YouTube tutorials, and courses with certificates.

Audio Interrupted

We lost the audio stream. Retry with shorter sentences?