Git Under the Hood: The DevOps Engineer's 'Undo' Button

Rantideb Howlader16 min read

Introduction: The Day I Almost Lost Everything

I still remember the first time I nearly deleted a project.

It was 4:45 PM on a Friday. (Why does it always happen on a Friday?). I was trying to update a feature branch that was a few weeks old. I typed a few commands, hit Enter, and then...

My files were gone.

I sat there, staring at the empty screen. My stomach dropped. I had just spent two days writing that code, and I hadn't pushed it to the server yet. I thought it was gone forever.

I froze. I was afraid to touch anything.

If you are new to coding, this is the moment you panic. But if you've been doing this for a while, you just take a deep breath, type git reflog, and fix it in 30 seconds.

This guide isn't about the basic commands like git add or git commit. You already know those. This guide is about the specific tools that act like a "Save Game" button. It's about understanding how Git actually works so you never have to be scared of it again.


Part 1: It's Not Magic, It's Just a Graph

The biggest lie people tell beginners is that Git is complicated. It's not. It's just a map.

When you type git commit, you aren't just saving a file. You are taking a snapshot of your whole project at that exact moment.

The .git Folder

If you open the hidden .git folder in your project, you'll see a few things:

  • objects/: This is the database. Every file and commit lives here.
  • refs/: These are like sticky notes. Branches and Tags are just named sticky notes that point to a specific commit.
  • HEAD: This is a simple file that says "You are here."

The Three Main Pieces

To fix Git, you just need to know three words:

  1. The Blob: The raw file data. If you have a file hello.txt, Git compresses it and calls it a Blob. Ideally, if two files have the exact same text, Git only stores one Blob. It's smart like that.

  2. The Tree: This is just a folder listing. It says "This folder contains hello.txt (which is that Blob over there)."

  3. The Commit: This is the wrapper. It holds:

    • The Tree (The snapshot of files).
    • The Author (You).
    • The Message ("Fixed the bug").
    • The Parent: The commit that came before this one.

That "Parent" link is the key. It links everything together into a long chain. That chain is the history of your project.


Part 2: The "Undo" Button (Reflog)

Let's go back to my Friday afternoon panic.

I had run git reset --hard HEAD~1. This tells Git: "Move the main pointer back one step, and destroy any changes in the working directory that don't match."

My commit was gone from the history (git log). It looked like it never happened.

But Git is a hoarder. It throws nothing away.

Enter git reflog

The "Reference Log" records every single time the HEAD pointer moves. Did you commit? HEAD moved. Did you checkout? HEAD moved. Did you reset? HEAD moved.

git reflog

Output:

8f9a12b HEAD@{0}: reset: moving to HEAD~1
3c4d5e6 HEAD@{1}: commit: fixed the login bug  <-- THIS IS MY LOST COMMIT!
7b8a9c0 HEAD@{2}: checkout: moving from dev to main

There it was. 3c4d5e6. The hash of my lost work. Even though no branch pointed to it anymore (it was a "dangling commit"), it still existed in the objects/ folder.

The Recovery

To get it back, I just had to tell Git to point a branch at it.

git branch rescue-friday-work 3c4d5e6

Boom. I now had a branch called rescue-friday-work that contained all my lost code. I merged it back in, and I was done.

Protocol: If you ever think you lost work, STOP. Do not close the terminal. Do not delete the folder. Run git reflog. Your safety net is there.


Part 3: Detached HEAD (It’s Not a horror movie)

This scares everyone. You checkout a specific commit:

git checkout 3c4d5e6

Git warns you: "You are in 'detached HEAD' state. You can look around, make experimental changes... but if you switch branches, you will lose them."

What is happening?

Usually, HEAD points to a Branch Name (like main), and the Branch Name points to a Commit Hash. HEAD -> refs/heads/main -> 3c4d5e6

When you "detach," you point HEAD directly at the Commit Hash. HEAD -> 3c4d5e6

You are floating in space. You aren't "on" a branch. If you make a new commit here, HEAD moves to the new commit, but no branch name moves with it.

If you then git checkout main, HEAD moves back to main. Your new commit has no name pointing to it. It becomes "dangling." Garbage collection will eventually eat it (in about 90 days), but strictly speaking, it is "lost" from the view of git log.

The Fix

If you made commits in Detached HEAD and want to save them: Give them a name.

git checkout -b NEW-BRANCH-NAME

Git will create a new sticky note (Branch) pointing at your current spot. Now you are safe.


Part 4: Git Bisect (The Bug Hunter)

Let's say you have a bug.

  • It wasn't there last week (Release v1.0).
  • It is there today (Release v1.1).
  • There are 400 commits in between.

You could checkout random commits and test. Or you could use Binary Search.

git bisect is a wizard. It asks you for a "Good" commit (last week) and a "Bad" commit (today). Then it picks the commit right in the middle and checks it out for you.

The Workflow

  1. Start: git bisect start
  2. Mark Bad: git bisect bad (Current version is broken)
  3. Mark Good: git bisect good v1.0 (Last week was fine)
  4. Test: Git checks out the middle commit. You run your app.
    • Is it broken? Type git bisect bad.
    • Is it working? Type git bisect good.
  5. Repeat: Git cuts the list in half again. And again.

In 400 commits, it takes only 9 steps (2^9 > 400) to find the exact commit that broke everything.

Output:

a1b2c3d is the first bad commit
commit a1b2c3d
Author: Dave <dave@company.com>
Date:   Tue Jan 10 14:00:00 2026
 
    Refactored the login logic (optimization)

Thanks, Dave. now we know exactly what line of code caused the regression.


Part 5: Interactive Rebase (Rewriting History)

This is the "Dark Arts." Sometimes you commit 10 times in an hour:

  • "Fixing typo"
  • "Fixing typo again"
  • "Forgot semicolon"
  • "Please work"

You don't want to push this garbage to the shared repo. You want to squash it into one beautiful commit: "Implmented Login Feature."

git rebase -i

git rebase -i HEAD~4

This opens your text editor with a list of the last 4 commits. You can change the word pick to:

  • reword: Change the commit message.
  • squash: Melt this commit into the one above it.
  • drop: Delete this commit entirely (Deadly!).

Warning: Never, ever rebase commits that you have already pushed to a shared branch (like main). If you rewrite history that your teammates have downloaded, their Git will reject your changes, and you will have to force push (git push -f), which will overwrite their work. Rule: Rebase applies only to your local, private feature branch.


Part 6: Plumbing vs. Porcelain

Git commands are divided into two layers:

  1. Porcelain: The pretty commands for humans (add, commit, push).
  2. Plumbing: The raw commands for scripts (hash-object, update-ref, cat-file).

As a DevOps Engineer, you sometimes need the Plumbing.

Scenario: You want your CI/CD pipeline to verify that the commit message follows a regulated format (e.g., "JIRA-123: Message").

You don't parse git log. That's meant for humans and the format changes. You use git rev-list and git cat-file.

# Get the full message of the current commit
git cat-file -p HEAD | sed '1,/^$/d'

This reads the raw commit object from the database and strips out the headers, leaving just the message. It is robust, fast, and scriptable.


Part 7: The .gitignore Trap

A common "Ghost Story": You add config.env to your .gitignore. You edit config.env. You run git status. It ignores the file. Good. But wait! You change your mind. You want to commit it (maybe it's an example file). You run git add config.env. Git says: "The following paths are ignored..."

The Fix: Force it.

git add -f config.env

The Bigger Trap: You accidentally committed .env last week. You realized your mistake. You added .env to .gitignore. You pushed. The file is still there. .gitignore only prevents new files from being tracked. It does NOT remove files that are already being tracked.

The Fix: You must remove it from the index (the staging area) but keep it on your disk.

git rm --cached .env
git commit -m "Stop tracking sensitive file"

Now it is truly ignored.


Part 8: The "Force Push" Etiquette

git push --force is a weapon. It says: "I don't care what is on the server. My version is the Truth. Overwrite everything."

If your colleague pushed a commit while you were rebasing, and you force push, you delete their commit.

The Safe Way: git push --force-with-lease

This is the polite version. It checks: "Has the remote branch moved since the last time I fetched?"

  • If No (Nobody touched it): Allow the force push.
  • If Yes (Someone pushed code): Abort. Fail the push.

Configure your alias now. Never use raw --force again.


Part 9: Git Hooks (The Automation Layer)

If you are manually linting your code before committing, you are doing it wrong. Git has a built-in event system called Hooks. They live in .git/hooks.

Client-Side Hooks

These run on your laptop.

  • pre-commit: The most useful hook. It runs before the commit object is created.

    • Use Case: Prevent committing secrets.
    • Script:
      #!/bin/sh
      # Check for AWS Keys
      if grep -q "AKIA" .; then
        echo "❌ AWS Key detected! Aborting."
        exit 1
      fi
    • If this script exits with 1, the commit fails. It saves you from revoking keys.
  • prepare-commit-msg: Runs before the commit editor opens.

    • Use Case: Automatically prepend the JIRA Ticket ID (from the branch name) to the commit message.

Server-Side Hooks

These run on the remote (e.g., GitHub Enterprise, GitLab, Bitbucket).

  • pre-receive: The gatekeeper. It runs when someone pushes.
    • Use Case: Enforce policy. "No pushes to main allowed unless you are an Admin." "No commits completely larger than 5MB."

The "Husky" Wrapper

Nobody writes raw shell scripts in .git/hooks anymore because they aren't committed to the repo. (The .git folder is not tracked by Git). We use tools like Husky (for Node) or Pre-commit (for Python). They allow you to define hooks in a config file (.pre-commit-config.yaml) which is versioned. When a new dev clones the repo and runs install, the hooks are set up automatically.


Part 10: Git Worktrees (The Multitasker)

Scenario: You are deep in the zone on feature-login. Production catches fire. You need to fix main immediately. Old Way:

  1. git stash (Hide your messy work).
  2. git checkout main.
  3. Fix bug.
  4. git checkout feature-login.
  5. git stash pop.
  6. Deal with stash conflicts. Cry.

The "Senior" Way: Git Worktrees. A Worktree allows you to have multiple folders linked to the same repository.

git worktree add ../hotfix-folder main

Now you have a new folder ../hotfix-folder on your disk.

  • It is checked out to main.
  • Your original folder is still checked out to feature-login.
  • You can work in both simultaneously.
  • You can run the app in both simultaneously (on different ports).

When you are done:

cd ../hotfix-folder
git commit -m "Fixed prod"
git push
cd ../original-folder
git worktree remove ../hotfix-folder

No stashing. No context switching. Pure efficiency.


Part 11: Signing Commits (Proving It's You)

In 2026, Supply Chain Security is everything. How do I know that you wrote that commit? Anyone can configure git config user.name "Linus Torvalds".

We use GPG (or SSH) Signing.

Setting it up (The Modern SSH Way)

We used to use GPG keys, which are a pain to manage. Now Git supports SSH keys (the same ones you use to push).

  1. Configure Git:
    git config gpg.format ssh
    git config user.signingkey ~/.ssh/id_rsa.pub
  2. Sign:
    git commit -S -m "Signed commit"
  3. Verify: GitHub/GitLab will show a green "Verified" badge next to your commit.

If you don't sign your commits, you are essentially writing anonymous code. In regulated industries (Banking, Healthcare), unsigned code is rejected by the pre-receive hook.


Part 12: Git LFS (Large File Storage) - When Text Tools Fail

Git is optimized for text. It uses algorithms (Delta Compression) to store differences between lines. It is terrible at binary files (Images, PSDs, Compiled Binaries). If you commit a 100MB logo.psd:

  1. Repo grows by 100MB.
  2. You change one pixel.
  3. Repo grows by another 100MB. (Git can't diff binaries well, so it stores a whole new copy).
  4. After 10 edits, you have 1GB of history that everyone must clone.

The Solution: Git LFS. LFS replaces the large file with a tiny "Text Pointer" (like a shortcut).

version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e23b8
size 12345

The actual 100MB file is stored on a separate server (S3). When you check out the branch, Git LFS downloads the real file lazily.

Commands:

git lfs install
git lfs track "*.psd"
git add .gitattributes

Warning: Migrating an existing repo to LFS is painful. You have to rewrite history to "remove" the old big blobs and replace them with pointers using git lfs migrate. Do this early, or suffer later.


Part 13: Repo Maintenance (Garbage Collection)

Your .git folder grows over time. Dangling commits (from rebase). Stashes you forgot. Loose objects.

Git runs git gc automatically sometimes, but on big repos, you need to force it.

The Commands:

  1. git gc --prune=now: Aggressive garbage collection. Deletes all loose objects older than "now".
  2. git fsck: File System Check. Verifies the integrity of the database.
  3. BFG Repo-Cleaner: A third-party tool that is faster than git filter-branch. Use it to remove passwords or sensitive data from history.

Maintenance Schedule: Run git maintenance start. This enables a background cron job that runs prefetch (updating references) and commit-graph (optimizing the graph structure) hourly. This makes git status instant even on massive repos.


Part 14: Monorepo vs Polyrepo (The Senior Engineer's Dilemma)

This is the biggest architectural decision you will make.

Polyrepo (One Repo Per Service)

  • Pros: Simple ACLs. Fast clones. Clear ownership.
  • Cons: Dependency Hell. To update a shared library, you have to open PRs in 50 repos. Integration testing is hard.

Monorepo (One Repo For Everything)

  • Pros: Atomic Commits. (You can change the API and the Consumer in one PR). Code reuse is trivial.
  • Cons: Tooling is hard. git status takes 10 seconds if you don't optimize. You need tools like Bazel, Nx, or Turborepo.

Git's Limit: Git starts to choke around 5GB repo size or 500k commits. If you go Monorepo, you need VFS for Git (Microsoft's creation) or Sparse Checkout.

Sparse Checkout

"I only want the frontend folder. I don't need the 50GB backend folder."

git clone --filter=blob:none --no-checkout https://github.com/org/mono.git
cd mono
git sparse-checkout set frontend
git checkout main

This downloads only the files you need. It is essential for Modern DevOps.


Part 15: Deep Glossary for the Expert

  • DAG (Directed Acyclic Graph): The mathematical structure of Git history. It flows one way (time) and never loops.
  • Detached HEAD: A state where HEAD points to a Commit OID instead of a Branch Ref.
  • Refspec: The syntax for mapping remote branches (+refs/heads/*:refs/remotes/origin/*).
  • Fast-Forward: Moving a branch pointer forward without creating a merge commit (because history is linear).
  • Three-Way Merge: Using the Common Ancestor (Base) to merge two diverging branches.
  • Rebase: Replaying commits from one branch onto another base. Rewrites history.
  • Cherry-Pick: Copying the changes from one commit and applying them to the current branch as a new commit.
  • Index (Staging Area): The binary file .git/index that tracks file states before commit.
  • Stash: A stack structure to temp-save dirty working directory state.
  • Upstream: The remote branch that your local branch tracks (origin/main).

Part 16: Final Thoughts on "The Perfect Commit"

What makes a Senior Engineer? It's not the code. It's the Commit.

A Perfect Commit:

  1. Is Atomic: Fixes one thing. Not "Fixed bug and reformatted CSS".
  2. Has a Subject: "Fix(auth): Handle JWT expiration" (Conventional Commits).
  3. Has a Body: Explains Why, not What. "The token was expiring but the interceptor wasn't refreshing it, causing 401s."
  4. Is Verified: Signed and Tested.

Git is your journal. It is your legacy. Ten years from now, when you are gone, people will read your git log to understand why the system works the way it does. Write it for them.

Conclusion (The End of the DAG)

Git is a tool that rewards curiosity. The engineers who treat it as a black box live in fear of merge conflicts. The engineers who understand the Graph use it as a weapon.

They use Worktrees to multitask. They use Reflog to travel time. They use Bisect to hunt bugs. They use Hooks to enforce quality. They use Sparse Checkout to tame monoliths.

You are now one of those engineers. Go forth and rebase with confidence.

Further Reading (Deep Cuts)


Ranti

Rantideb Howlader

Author

Connect