In the previous tutorial, we learned to work with GitHub and set up CI/CD. Now it is time for the advanced tools.

Every developer makes mistakes. You commit the wrong file. You break something and need to find out which commit caused it. You need to grab a single commit from another branch.

Git has tools for all of this. In this tutorial, you will learn to undo mistakes, recover lost work, and use power features that save hours of debugging.

Undoing Changes in Your Working Directory

You edited a file and want to throw away the changes. The file is not staged yet.

git restore hello.txt

This replaces the file with the version from your last commit. Your changes are gone. Be careful — there is no way to get them back.

To undo changes in all tracked files:

git restore .

Note: In older Git versions, this was git checkout -- hello.txt. The git restore command was added in Git 2.23 and is clearer about what it does.

Unstaging Files

You staged a file with git add but changed your mind. You want to unstage it without losing your changes.

git restore --staged hello.txt

The file goes back to “modified but not staged.” Your changes are still there.

To unstage everything:

git restore --staged .

git reset — Undoing Commits

git reset moves the branch pointer backwards. It has three modes, and the mode decides what happens to your changes.

Soft Reset

git reset --soft HEAD~1

This undoes the last commit. Your changes stay staged (ready to commit again).

Before: A --- B --- C (HEAD)
After:  A --- B (HEAD)    [C's changes are staged]

Use this when you want to change your commit message or add more files to the commit.

Mixed Reset (Default)

git reset HEAD~1

This undoes the last commit. Your changes stay in the working directory but are not staged.

Before: A --- B --- C (HEAD)
After:  A --- B (HEAD)    [C's changes are unstaged]

This is the default. If you type git reset without a flag, this is what happens.

Hard Reset

git reset --hard HEAD~1

This undoes the last commit and deletes your changes. Everything is gone.

Before: A --- B --- C (HEAD)
After:  A --- B (HEAD)    [C's changes are deleted]

Use this only when you are sure you do not need the changes. There is no undo for a hard reset (unless you use git reflog — see below).

Reset Multiple Commits

git reset --soft HEAD~3

This undoes the last 3 commits. All their changes become staged.

Summary Table

CommandCommitStaging AreaWorking Directory
--softUndoneKeeps changesKeeps changes
--mixed (default)UndoneClears stagingKeeps changes
--hardUndoneClears stagingDeletes changes

git revert — Safely Undoing a Commit

git reset rewrites history. If you already pushed the commit, resetting causes problems for others. Use git revert instead.

git revert a1b2c3d
[main e5f6g7h] Revert "Add broken feature"
 1 file changed, 0 insertions(+), 5 deletions(-)

This creates a new commit that undoes the changes from the specified commit. The original commit stays in the history.

Before: A --- B --- C (broken)
After:  A --- B --- C --- D (D reverts C)

This is safe for shared branches. Everyone gets the revert as a normal commit.

To revert without auto-committing (so you can review first):

git revert --no-commit a1b2c3d

Reset vs Revert

git resetgit revert
What it doesRemoves commits from historyCreates a new commit that undoes changes
Changes history?YesNo
Safe for pushed commits?NoYes
Use whenUndoing local, unpushed commitsUndoing pushed commits

git reflog — Recovering Lost Commits

Deleted a branch by accident? Did a hard reset and regret it? git reflog is your safety net.

The reflog records every time HEAD moves. Even after a hard reset, the old commits are still there — they are just not attached to any branch.

git reflog
e5f6g7h (HEAD -> main) HEAD@{0}: revert: Revert "Add broken feature"
c3d4e5f HEAD@{1}: commit: Add broken feature
b2c3d4e HEAD@{2}: commit: Update greeting
a1b2c3d HEAD@{3}: commit: Add hello.txt
d4e5f6g HEAD@{4}: checkout: moving from feature/login to main
f6g7h8i HEAD@{5}: commit: Add login page

Every entry shows what happened and the commit hash. To recover a lost commit:

# Create a branch pointing to the lost commit
git branch recovered f6g7h8i

Or go back to that point:

git reset --hard HEAD@{5}

The reflog keeps entries for about 90 days by default. After that, Git garbage-collects them.

Recovering a Deleted Branch

# Oops, deleted the branch
git branch -D feature/important

# Find the commit in the reflog
git reflog | grep "feature/important"

# Recreate the branch
git branch feature/important f6g7h8i

git cherry-pick — Apply Specific Commits

Cherry-pick takes a single commit from one branch and applies it to another. This is useful when you need one specific change without merging an entire branch.

git switch main
git cherry-pick a1b2c3d
[main h8i9j0k] Fix login validation
 1 file changed, 3 insertions(+)

This creates a new commit on main with the same changes as commit a1b2c3d.

When to Use Cherry-Pick

  • A bug fix on a feature branch needs to go to main immediately
  • You committed to the wrong branch and want to move the commit
  • You need a specific change from a branch that is not ready to merge

Cherry-Pick Multiple Commits

git cherry-pick a1b2c3d b2c3d4e c3d4e5f

Or a range:

git cherry-pick a1b2c3d..c3d4e5f

Note: the range excludes the first commit. It picks everything after a1b2c3d up to and including c3d4e5f.

git bisect — Find the Bug

Your code worked last week but is broken now. You have 50 commits in between. Which one broke it?

git bisect uses binary search to find the bad commit. Instead of checking all 50 commits, you only check about 6 (log2 of 50).

# Start bisecting
git bisect start

# Tell Git the current commit is bad
git bisect bad

# Tell Git a commit from last week was good
git bisect good a1b2c3d
Bisecting: 25 revisions left to test after this (roughly 5 steps)
[d4e5f6g] Add user search feature

Git checks out a commit in the middle. Test your code:

# If this commit works
git bisect good

# If this commit is broken
git bisect bad

Git narrows down the range each time. After a few steps:

c3d4e5f is the first bad commit
commit c3d4e5f
Author: Sam <sam@example.com>
Date:   Wed May 28 14:30:00 2026

    Update database query

Now you know exactly which commit introduced the bug.

When you are done:

git bisect reset

This returns you to where you were before bisecting.

git blame — Who Changed What

git blame shows who last modified each line of a file:

git blame hello.txt
a1b2c3d (Alex 2026-05-25 09:00:00 +0000 1) Hello, Git!
b2c3d4e (Sam  2026-05-26 14:00:00 +0000 2) This is my first Git project.
c3d4e5f (Alex 2026-05-27 10:00:00 +0000 3) Welcome to the team!

Each line shows:

  • The commit hash
  • The author
  • The date
  • The line content

This is not for blaming people — it is for understanding code history. “Why was this line added? Let me check the commit.”

Show a specific range of lines:

git blame -L 10,20 hello.txt

This shows blame for lines 10 through 20 only.

git tag — Marking Releases

Tags mark important points in your history. They are commonly used for releases.

Lightweight Tags

git tag v1.0.0

This tags the current commit.

git tag -a v1.0.0 -m "First stable release"

Annotated tags store the tagger’s name, date, and a message. They are better for releases.

Tag a Previous Commit

git tag -a v0.9.0 -m "Beta release" a1b2c3d

List Tags

git tag
v0.9.0
v1.0.0

Push Tags to GitHub

Tags are not pushed by default. You need to push them explicitly:

# Push a specific tag
git push origin v1.0.0

# Push all tags
git push origin --tags

Delete a Tag

# Delete locally
git tag -d v0.9.0

# Delete on remote
git push origin --delete v0.9.0

Git Aliases

Tired of typing long commands? Create aliases.

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.cm "commit -m"
git config --global alias.lg "log --oneline --graph --all"

Now you can type:

git st          # instead of git status
git co main     # instead of git checkout main
git br          # instead of git branch
git cm "Fix bug"  # instead of git commit -m "Fix bug"
git lg          # instead of git log --oneline --graph --all

.gitconfig Tips

Your Git configuration lives in ~/.gitconfig. Here are some useful settings:

[user]
    name = Alex
    email = alex@example.com

[init]
    defaultBranch = main

[alias]
    st = status
    co = checkout
    br = branch
    cm = commit -m
    lg = log --oneline --graph --all
    last = log -1 HEAD
    unstage = restore --staged

[pull]
    rebase = true

[core]
    editor = vim
    autocrlf = input

The [pull] rebase = true setting makes git pull use rebase instead of merge by default. This keeps your history cleaner.

The autocrlf = input setting prevents line ending problems between Windows and macOS/Linux.

Quick Reference: Undo Commands

Here is a summary of when to use each undo command:

SituationCommand
Discard changes in a file (not staged)git restore <file>
Unstage a file (keep changes)git restore --staged <file>
Undo last commit (keep changes staged)git reset --soft HEAD~1
Undo last commit (keep changes unstaged)git reset HEAD~1
Undo last commit (delete changes)git reset --hard HEAD~1
Undo a pushed commit (safe)git revert <commit>
Recover a lost commitgit reflog then git branch recovered <hash>

Common Mistakes

  1. Using git reset --hard without thinking — Hard reset deletes your changes permanently. Always check git status and git diff before resetting. If you do reset by accident, git reflog can often save you — but act quickly before Git garbage-collects.

  2. Resetting pushed commits — If you reset a commit that is already on GitHub, your local history diverges from the remote. You would need a force push, which overwrites remote history. Use git revert for pushed commits instead.

  3. Cherry-picking instead of merging — Cherry-pick is for one-off commits. If you find yourself cherry-picking many commits from the same branch, you should merge or rebase that branch instead. Cherry-picking creates duplicate commits with different hashes.

What’s Next?

Congratulations! You have completed the Git sub-series. You now know:

  • Git basics (init, add, commit, status, log, diff)
  • Branching (create, switch, delete)
  • Merging and rebasing (merge, rebase, stash, conflicts)
  • GitHub workflow (push, pull, PRs, CI/CD)
  • Advanced features (reset, revert, reflog, cherry-pick, bisect, blame, tags, aliases)

These skills cover everything you need for daily work with Git. Keep the Git Commands Cheat Sheet bookmarked for quick reference.

Stay tuned for the Docker tutorials coming next, where we will learn to package and deploy applications with containers.


This is part 5 of the DevTools Tutorial series.