Git Fundamentals That AI Won't Teach You
By The IT Hustle Team
This article was generated with AI assistance and reviewed by our team for accuracy and quality. All technical information and examples have been verified.
Ask an AI how to undo a git commit and it will give you one of six different commands, each with wildly different consequences. git reset --soft, git reset --hard, git revert, git checkout, git restore, git reflog — which one? Depends on the context. And that context requires understanding what git is actually doing under the hood, not just memorizing commands.
Git is the most important tool in a developer's toolkit that most developers don't actually understand. They learn enough to add, commit, push, and pull. They google the rest. And now they ask ChatGPT the rest. The problem is that git commands without mental models are like driving without understanding traffic laws — it works until it doesn't, and when it doesn't, the consequences can be severe.
Git's DAG Model: The Foundation of Everything
At its core, git is a directed acyclic graph (DAG) of content-addressed objects. That sounds academic, but understanding it changes how you think about every git operation.
Every commit is a node in this graph. Each commit points to its parent commit(s). A merge commit has two parents. The graph can branch and reconverge, but it never forms cycles (that's the "acyclic" part). When you "branch," you're not copying files — you're creating a new pointer to an existing commit. When you "merge," you're creating a new commit that has two parents.
This matters because once you understand the DAG, you understand that:
- Branches are cheap — they're just 41-byte files containing a commit hash
- Commits are immutable — you can't change a commit, only create a new one that replaces it
- Rebase doesn't "move" commits — it creates new commits with the same changes and different parents
- Nothing is ever truly deleted until garbage collection runs — the reflog keeps references to "lost" commits
Refs and Objects: What Git Actually Stores
Git stores four types of objects, all identified by SHA-1 hashes:
- Blobs — the contents of a file (not the filename, just the contents)
- Trees — directory listings that map filenames to blobs and other trees
- Commits — metadata (author, message, timestamp) plus a pointer to a tree and parent commit(s)
- Tags — named pointers to commits (annotated tags are full objects; lightweight tags are just refs)
Refs are human-readable names that point to commit hashes. main is a ref. HEAD is a special ref that points to the current branch (which in turn points to a commit). origin/main is a remote-tracking ref. Understanding that these are all just pointers to the same underlying DAG of objects demystifies most of git's behavior.
git rev-parse HEAD
git rev-parse main
# See the raw content of a git object
git cat-file -t HEAD # shows "commit"
git cat-file -p HEAD # shows commit contents (tree, parent, author, message)
The Staging Area: Git's Most Misunderstood Feature
Most developers think of git add as "marking files to be committed." That's partially true, but it misses the deeper purpose. The staging area (also called the index) is a deliberate design choice that lets you craft commits.
Without a staging area, every commit would include all changed files. You'd have no way to commit a subset of your changes. But real development doesn't happen in neat, atomic units. You might fix a bug, refactor a function, and update a comment all in the same file. The staging area lets you commit each of those changes separately with meaningful commit messages.
git add -p filename.js
# See what's staged vs. what's modified but not staged
git diff --staged # staged changes
git diff # unstaged changes
# Unstage a file without losing changes
git restore --staged filename.js
Why it matters:
Clean git history isn't vanity — it's a communication tool. When someone runs git blame on a line six months from now, the commit message should explain why that line exists. "Fixed stuff" as a commit message with 47 changed files helps nobody. The staging area exists to help you create commits that tell a story.
Interactive Rebase: Power and Danger
Interactive rebase (git rebase -i) is one of git's most powerful features. It lets you reorder, squash, edit, and drop commits before sharing them. It's also one of the most dangerous features if used without understanding what's happening.
Here's the critical thing to understand: rebase rewrites history. It creates new commits with new hashes. If those commits have already been pushed to a shared branch, you've created a divergence that will cause problems for every developer who has pulled those commits.
git rebase -i HEAD~3 # rewrite the last 3 commits
# DANGEROUS: Rebasing commits that have been pushed
git rebase -i origin/main # if you push this, others are in trouble
# The golden rule:
# Never rebase commits that exist outside your local repository
AI often suggests rebase as a solution to messy history without warning about these consequences. It'll say "just rebase on main" without asking whether you've already pushed your branch. This is exactly the kind of context-dependent advice where blind AI suggestions can cause real damage.
Merge vs. Rebase: The Eternal Debate
This isn't a religious war — both strategies have concrete tradeoffs:
Merge creates a merge commit that preserves the complete history of both branches. The DAG shows exactly when the branch was created, what commits were made on it, and when it was integrated. This is truthful but can create a messy graph with many branch-and-merge patterns.
Rebase replays your commits on top of the target branch, creating a linear history. It looks cleaner but hides the fact that development happened in parallel. And as we discussed, it rewrites commit hashes, which is dangerous for shared branches.
A pragmatic approach that many teams use:
- Rebase your feature branch onto main before merging (to keep history linear)
- Use merge (not rebase) to integrate feature branches into main
- Never rebase a branch that other people are working on
- Squash merge for small features (collapses all branch commits into one)
Reflog: Your Safety Net
If you only learn one advanced git concept, make it reflog. The reflog is a local log of every time HEAD changed — every commit, checkout, merge, rebase, reset, and pull. Even when you think you've lost commits (reset --hard, botched rebase, deleted branch), the reflog usually has them.
git reflog
# Output looks like:
# a1b2c3d HEAD@{0}: commit: Add user authentication
# e4f5g6h HEAD@{1}: rebase: moving to main
# i7j8k9l HEAD@{2}: commit: Fix login bug
# Recover a "lost" commit by resetting to it
git reset --hard HEAD@{2}
# Or create a new branch pointing to the lost commit
git branch recovery-branch HEAD@{2}
Reflog entries expire after 90 days by default (30 days for unreachable commits). This gives you a generous window to recover from mistakes. But it's local only — it's not shared with remotes, and it doesn't survive if you delete the repository.
.gitignore Best Practices
A proper .gitignore file prevents sensitive data, build artifacts, and environment-specific files from being committed. This seems basic, but getting it right is surprisingly nuanced:
- Start with a template for your language/framework (GitHub maintains excellent templates)
- Always ignore:
.env,node_modules/,.DS_Store, IDE configuration directories - Use a global gitignore for OS and editor files:
git config --global core.excludesfile ~/.gitignore_global - If a file is already tracked, adding it to .gitignore won't remove it — you need
git rm --cached filename - Use negation patterns (
!important.log) sparingly — they make .gitignore files confusing
Building a .gitignore from scratch for every project is tedious. Use our .gitignore Generator to create comprehensive ignore files for any tech stack in seconds.
Reset vs. Revert vs. Checkout: Know the Difference
This is where AI gives the most dangerous advice, because these three commands sound similar but do fundamentally different things:
git reset moves the branch pointer. It changes where HEAD points. With --soft, it keeps your changes staged. With --mixed (default), it keeps your changes but unstages them. With --hard, it discards everything. Reset rewrites history — if the commits were pushed, you'll need to force push (and everyone else will hate you).
git reset --mixed HEAD~1 # undo last commit, unstage changes
git reset --hard HEAD~1 # undo last commit, DISCARD all changes
git revert creates a NEW commit that undoes the changes from a previous commit. It doesn't rewrite history — it adds to it. This is safe for shared branches because it doesn't change existing commits.
git checkout / git restore operates on files, not commits. It replaces working directory files with versions from a specific commit without changing any branch pointers or history. In modern git, git restore handles file restoration while git switch handles branch switching — both were split out from the overloaded git checkout command.
git restore --source=abc123 -- filename.js
# Discard all unstaged changes in working directory
git restore .
# Switch branches (modern syntax)
git switch feature-branch
Commit Message Conventions
Good commit messages are a form of documentation that ages better than comments or README files because they're tied directly to the changes they describe. The widely adopted convention:
Longer description wrapped at 72 characters explaining WHY
this change was made, not WHAT was changed (the diff shows that).
- Bullet points are fine
- Reference issue numbers: Fixes #123
Types: feat, fix, docs, style, refactor, test, chore
Scope: the module or component affected
Use the imperative mood ("Add feature" not "Added feature") because a commit message completes the sentence: "If applied, this commit will... add feature."
Branch Naming Strategies
Consistent branch naming makes automation possible and history navigable:
bugfix/login-redirect-loop
hotfix/payment-calculation-error
release/v2.1.0
chore/upgrade-dependencies
Prefixing with the type makes it easy to write CI rules (e.g., "deploy release/* branches to staging") and helps team members understand the purpose of a branch without reading its commits. Use lowercase with hyphens, not underscores or camelCase. Keep names descriptive but concise.
Why AI Gives Dangerous Git Advice
Git commands are context-sensitive in a way that AI consistently fails to account for. The "right" command depends on:
- Whether you've pushed the commits in question
- Whether other people are working on the same branch
- Whether you need to preserve history for audit purposes
- What your team's workflow conventions are
- Whether you're working with submodules, worktrees, or sparse checkouts
AI doesn't ask these questions. It gives you a command that solves the literal question you asked without considering the broader consequences. "How do I undo the last commit?" could mean reset --soft, reset --hard, or revert depending on circumstances the AI doesn't know about and doesn't ask about.
The worst AI git advice I've seen: suggesting git push --force on a shared branch to "fix" a divergence. This rewrites remote history and causes data loss for anyone who has pulled the original commits. The correct solution is almost always git pull --rebase or a merge — but the AI doesn't know the branch is shared because it doesn't have that context.
Learn git fundamentals. Understand the DAG, the three areas (working directory, staging, repository), and how refs work. Once you have that mental model, every git command makes intuitive sense, and you won't need to trust AI with decisions that could wipe out your team's work. And when you're setting up a new project, grab a proper .gitignore from our .gitignore Generator before your first commit.
Git is one of those tools where the fundamentals pay compound interest. Every hour you spend understanding the DAG, the staging area, and the difference between reset and revert will save you dozens of hours of panic and data recovery. Don't outsource that understanding to an AI that doesn't know whether your branch is shared.
We build free developer tools and write about AI, automation, and developer productivity. 30 tools, 33 articles, and an AI Prompt Engine — all built to help workers navigate the AI era. Published by Salty Rantz LLC.
The IT Hustle Weekly
What changed in AI this week and what it means for your job. Free tools, honest reviews, zero spam.
Generate Your Own Anti-Hallucination Prompts
Our AI Prompt Engine uses patent-pending technology to generate prompts with built-in verification and contradiction testing.
Try 3 Free Generations →