Advance Git Commands

A practical guide to advanced Git workflows — rewriting history, rebasing, fetching, pulling with rebase, and using reflog to recover lost commits.

Git is more than add, commit, and push. Once you start collaborating on real projects, you’ll often need to rewrite history, clean up commits, rebase branches, or even recover “lost” work. This guide explores advanced Git features with explanations and space for practical demonstrations.

1. Commit History Rewriting in Git

1.1 Amending Git Commits

Amending in Git is like fixing or updating your most recent commit without creating a brand-new one. Instead of stacking another commit on top, you “rewrite” the last one.

git log --oneline
0832375 (HEAD -> main, origin/main) HTML File
f86213d first commit


git commit -am "main:modified the title - blog.html"
[main ab40261] main:modified the title - blog.html
 2 files changed, 9 insertions(+), 1 deletion(-)


git log --oneline                                   
ab40261 (HEAD -> main) main:modified the title - blog.html
0832375 (origin/main) HTML File
f86213d first commit
# Stage the changes
git add 404.html

git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   404.html

In other words, now we want to rewrite the previous commit SHA “ab40261” and amend it to reflect the changes made in both blog.html and 404.html file.

# Commit the changes with amend and no edit flag
git commit --amend --no-edit

[main 4bf9f69] main:modified the title - blog.html
 Date: Thu Sep 11 20:15:36 2025 +0200
 3 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 404.html

# The commit has been made with new SHA and the old commit shaw is deleted
git log --oneline           
4bf9f69 (HEAD -> main) main:modified the title - blog.html
0832375 (origin/main) HTML File
f86213d first commit

Tomake changes to the message of the commit:

git add config.yaml
git commit --amend

[main 65cf379] main:modified the title - blog.html and added 404.html; added config.yaml
 Date: Thu Sep 11 20:15:36 2025 +0200
 4 files changed, 80 insertions(+), 1 deletion(-)
 create mode 100644 404.html
 create mode 100644 config.yaml

git log --oneline  
65cf379 (HEAD -> main) main:modified the title - blog.html and added 404.html; added config.yaml
0832375 (origin/main) HTML File
f86213d first commit

If you only want to fix the commit message and not change the files, Git gives you a flag for that:

git commit --amend -m "New, corrected commit message"

That command replaces the old message with the new one while keeping the commit’s content exactly the same.

Alternatively, if you want to open your editor to edit the message interactively:

git commit --amend

Just don’t stage any new files before running it—otherwise Git will also include those changes in the amended commit.

2. Git Rebase

Rebase moves your commits to a new base commit. Instead of merging two branches (which creates a merge commit), rebase replays your branch’s commits on top of another branch, producing a linear history.

# Create a new branch named feature-branch
git checkout -b feature-branch 

# sample command for rebase
git rebase <BASE>
# base can be: an ID, a branch name, a tag, or a relative referance to HEAD
git rebase main
git log --oneline --decorate --graph --all
* 89293e4 (HEAD -> main, origin/main) Rebasing
* 86bcbc6 Updated Readme with future section
* 65cf379 main:modified the title - blog.html and added 404.html; added config.yaml
* 0832375 HTML File
* f86213d first commit

Practical Example (with Conflict Simulation)

In this walkthrough, we will simulate a real scenario: creating a feature branch, making changes, modifying main, and then rebasing with conflicts.

Step 1- Create a New Feature Branch

git checkout -b feature-branch

Output:

Switched to a new branch 'feature-branch'

At this point, both main and feature-branch point to the same commit.

Step 2- Add Commits to Feature Branch

git commit -am "feature-branch: modified title - 404.html"
[feature-branch 82ff182] feature-branch: modified title - 404.html
 2 files changed, 21 insertions(+), 1 deletion(-)

git commit -am "feature-branch: modified Error Message - 404.html"
[feature-branch e7f8a2e] feature-branch: modified Error Message - 404.html
 2 files changed, 8 insertions(+), 2 deletions(-)

Now, feature-branch has two commits that are not present in main.

Step 3- Add Commits to Main

Switch back to main and simulate independent changes:

git checkout main
git commit -am "main: modified title again- 404.html"
[main 84900fe] main: modified title again- 404.html
 1 file changed, 2 insertions(+), 2 deletions(-)

git commit -am "main: modified Error Message again- 404.html"
[main 9cbea38] main: modified Error Message again- 404.html
 1 file changed, 2 insertions(+), 2 deletions(-)

Visual Log (git log --oneline --decorate --graph --all):

* 9cbea38 (HEAD -> main) main: modified Error Message again- 404.html
* 84900fe main: modified title again- 404.html
| * e7f8a2e (feature-branch) feature-branch: modified Error Message - 404.html
| * 82ff182 feature-branch: modified title - 404.html
|/  
* 89293e4 (origin/main) Rebasing
* 86bcbc6 Updated Readme with future section

Now both branches have diverged.

Step 4- Start Rebasing

git checkout feature-branch
Switched to branch 'feature-branch'

git rebase main

Output:

Auto-merging 404.html
CONFLICT (content): Merge conflict in 404.html
error: could not apply 82ff182... feature-branch: modified title - 404.html
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".

Git stopped because of a conflict in 404.html.

Step 5- Resolve the First Conflict

git mergetool

Output:

Merging:
404.html

Normal merge conflict for '404.html':
  {local}: modified file
  {remote}: modified file

Open the file, resolve the conflict, then stage the changes:

git add 404.html
git rebase --continue

Output:

[detached HEAD 352a722] feature-branch: Rebasing the feature branch - 404.html
 2 files changed, 21 insertions(+), 1 deletion(-)
Auto-merging 404.html
CONFLICT (content): Merge conflict in 404.html
error: could not apply e7f8a2e... feature-branch: modified Error Message - 404.html

Another conflict occurred in the next commit.

Step 6- Resolve the Second Conflict

Fix the file again, then run:

git add 404.html
git rebase --continue

Output:

[detached HEAD 24d3a81] feature-branch: rebase in progress - 404.html
 2 files changed, 8 insertions(+), 2 deletions(-)
Successfully rebased and updated refs/heads/feature-branch.

All conflicts are resolved, and rebasing is complete.

Step 7- Verify Commit History

git log --oneline --decorate --graph --all

Output:

* fca2f48 (HEAD -> feature-branch) feature-branch: modified README.md File - README.md
* 24d3a81 feature-branch: rebase in progress - 404.html
* 352a722 feature-branch: Rebasing the feature branch - 404.html
* 9cbea38 (main) main: modified Error Message again- 404.html
* 84900fe main: modified title again- 404.html
* 89293e4 (origin/main) Rebasing

Notice how feature-branch commits are now on top of main, forming a linear history.

Step 8- Merge Feature Branch into Main

Since feature-branch is rebased, merging results in a fast-forward:

git checkout main
git merge feature-branch

Output:

Updating 9cbea38..fca2f48
Fast-forward
 404.html  |  4 ++--
 README.md | 38 ++++++++++++++++++++++++++++++++++++++

main now contains all rebased commits.

Step 9- Cleanup

git branch -d feature-branch
Deleted branch feature-branch (was fca2f48).

Handy Rebase Commands

Key Takeaways

This simulation demonstrated a real-world rebasing scenario with conflicts, including how to resolve them and preserve a clean history.

3. Git Fetch

When working with Git in collaborative projects, it’s important to keep your local repository aware of changes that have happened on the remote without immediately applying them. That’s where git fetch comes in.

The command:

git fetch origin

This makes git fetch a safe operation—you can inspect remote changes before deciding whether to merge or rebase them into your local branch.

Step 1- Checking Branches

List all local and remote branches:

git branch -a

Output:

* main
  remotes/origin/main

Check only local branches:

git branch -v
* main 453b2c9 Rebasing Done with Updated Readme

Check only remote branches:

git branch -r
  origin/main

Check remote-tracking with last commit:

git branch -rv
  origin/main 453b2c9 Rebasing Done with Updated Readme

See everything:

git branch -av
* main                453b2c9 Rebasing Done with Updated Readme
  remotes/origin/main 453b2c9 Rebasing Done with Updated Readme

Explanation

Both point to the same commit 453b2c9, meaning your local branch is in sync with remote.

Step 2- Detecting Divergence

Suppose new commits were added on the remote repository. Before fetching:

git log --oneline main
453b2c9 Rebasing Done with Updated Readme
... (older commits)
git log --oneline origin/main
453b2c9 Rebasing Done with Updated Readme
... (older commits)

Both look the same.

Now, after someone pushes two new commits (Included Hyperparameter tuning, Created svm_classification.py) to remote, your local copy is outdated.

Run:

git fetch origin main

Output:

From github.com:SurajBhar/advance_git
 * branch            main       -> FETCH_HEAD
   453b2c9..e5c4de0  main       -> origin/main

Remote-tracking branch origin/main is now updated, but local main is untouched.

Step 3- Inspect Remote Changes

Check remote branch logs:

git log --oneline origin/main
e5c4de0 (origin/main) Included Hyperparameter tuning
86f6a84 Created svm_classification.py
453b2c9 Rebasing Done with Updated Readme
... (older commits)

Compare with local:

git status
On branch main
Your branch is behind 'origin/main' by 2 commits, and can be fast-forwarded.

At this point:

Step 4- Merging Remote Changes

To bring your local branch up to date:

git merge origin/main

Output:

Updating 453b2c9..e5c4de0
Fast-forward
 svm_classification.py | 125 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 125 insertions(+)
 create mode 100644 svm_classification.py

Now:

git log --oneline --decorate --graph --all
* e5c4de0 (HEAD -> main, origin/main) Included Hyperparameter tuning
* 86f6a84 Created svm_classification.py
* 453b2c9 Rebasing Done with Updated Readme
...

Your local main is synced with origin/main.

Advantages of git fetch

Disadvantages / Gotchas

Tips for Users

Key Takeaway

4. Git Pull and Pull with Rebase

When collaborating with others, keeping your local repository in sync with the remote repository is crucial. After all, you’re not the only one pushing changes!

In other words:

git pull = git fetch + git merge

Step 1- Basic Git Pull

Let’s say you are on main and new commits have appeared on the remote repository. Running:

git pull origin main

Step 2- Why Pull with Rebase?

Sometimes merging is not the cleanest solution. If you are working on local changes but also want to integrate the latest updates from remote:

The command:

git pull --rebase origin main

works like this:

  1. git fetch → gets latest changes from origin/main.
  2. git rebase → temporarily “removes” your local commits, applies the new commits from origin/main, then replays your local commits on top.

Step 3- Example Walkthrough

Before pulling, the commit history looks like this:

git log --oneline --decorate --graph --all
* e5c4de0 (HEAD -> main, origin/main) Included Hyperparameter tuning
* 86f6a84 Created svm_classification.py
* 453b2c9 Rebasing Done with Updated Readme
* fca2f48 feature-branch:modified README.md File - README.md
* 24d3a81 feature-branch: rebase in progress - 404.html
* 352a722 feature-branch: Rebasing the feature branch - 404.html
* 9cbea38 main: modified Error Message again- 404.html
* 84900fe main: modified title again- 404.html
...

Two new commits (Update 2 config.yaml, Updated 1 config.yaml) exist on the remote but are not yet on local.

Now run:

git pull --rebase origin main

Output:

From github.com:SurajBhar/advance_git
 * branch            main       -> FETCH_HEAD
   e5c4de0..0535e79  main       -> origin/main
Successfully rebased and updated refs/heads/main.

Step 4- After Rebase

Check the new commit history:

git log --oneline --decorate --graph --all
* ebf7c5c (HEAD -> main) main: modified readme - README.md
* 0535e79 (origin/main) Update 2 config.yaml
* d9b0764 Updated 1 config.yaml
* e5c4de0 Included Hyperparameter tuning
* 86f6a84 Created svm_classification.py
* 453b2c9 Rebasing Done with Updated Readme
...

Notice the difference:

Advantages of git pull --rebase

Disadvantages / Cautions

Tips for Using Pull with Rebase

git config --global pull.rebase true
git rebase --continue
git rebase --abort

Key Takeaway

5. Git Reference Logs (Reflog)

Git’s reference logs (reflog) act as a safety net for your repository. While git log shows the commits that are part of your branch’s history, git reflog records all changes to the tips of branches and other references—even if those commits don’t appear in the visible history anymore.

Think of the reflog as a personal diary of your Git actions, maintained locally. Every time you:

…the HEAD reference is updated, and the reflog records it.

Step 1- Viewing the Reflog

Run:

git reflog

Example output:

ebf7c5c (HEAD -> main, origin/main) HEAD@{0}: pull --rebase origin main (finish): returning to refs/heads/main
ebf7c5c (HEAD -> main, origin/main) HEAD@{1}: pull --rebase origin main (pick): main: modified readme - README.md
0535e79 HEAD@{2}: pull --rebase origin main (start): checkout 0535e79...
e7d0f19 HEAD@{3}: commit: main: modified readme - README.md
e5c4de0 HEAD@{4}: merge origin/main: Fast-forward
453b2c9 HEAD@{5}: commit: Rebasing Done with Updated Readme
...

Here’s what’s happening:

Unlike git log, these entries remain visible even after resets or rebases.

Step 2- Why Reflog Matters

Let’s say you reset a branch or made changes you regret.

That’s why reflog is Git’s time machine.

Step 3- Inspecting Historical States

You can inspect any past state of the repository with:

git show HEAD@{n}

Example:

git show HEAD@{29}

Output shows the very first commit:

commit f86213de...
Author: Suraj Bhardwaj
Date:   Thu Sep 11 17:45:43 2025 +0200

    first commit

You can also query reflog by time references:

git show main@{1.hour.ago}
git show main@{yesterday}
git show main@{1.week.ago}

Example:

git show main@{1.hour.ago}

Output:

commit e5c4de0...
Author: Suraj Bhardwaj
Date:   Fri Sep 12 10:29:35 2025 +0200

    Included Hyperparameter tuning

Step 4- Using Reflog to Recover from Mistakes

Scenario: Hard Reset

Suppose you ran:

git reset --hard bee45fc

This moved your branch pointer back to an older commit. At first glance, it looks like you lost your newer commits. But reflog recorded them:

git reflog
bee45fc (HEAD -> main) HEAD@{0}: reset: moving to bee45fc
ebf7c5c (origin/main) HEAD@{1}: reset: moving to ebf7c5c
bee45fc (HEAD -> main) HEAD@{2}: commit: main: deleted server info - config.yaml
...

You can still recover the commits via their reflog entries.

Scenario- Restoring Lost Commits

You decide those commits weren’t mistakes after all. To bring them back:

# Checkout an old reflog entry in detached HEAD state
git checkout HEAD@{2}

# Create a new branch to save those commits
git checkout -b restore-branch

# Merge back into main
git checkout main
git merge restore-branch

# Delete the temporary branch
git branch -d restore-branch

Now your “lost” commits are restored into your main history.

Step 5- Where Reflog Data Lives

Step 6- Cheat Sheet

Here’s a quick summary of useful reflog commands:

# Show reflog for HEAD (all actions)
git reflog

# Show reflog for a specific branch
git reflog show main

# Show commit at a specific reflog entry
git show HEAD@{5}

# Checkout an old state
git checkout HEAD@{10}

# Recover work by branching from a reflog entry
git checkout -b restore-branch HEAD@{8}

# Show history by time references
git show main@{1.hour.ago}
git show main@{yesterday}

# Diff between old state and current
git diff @{1.hour.ago}

# Show reflog in log format
git log -g

Key Takeaways

Final Thoughts

In real-world projects, Git is more than just a tool for saving code — it’s the foundation of teamwork and clean collaboration.

The commands we covered — amending commits, rebasing, fetching, pulling with rebase, and using reflog — are the safety nets and precision tools that keep your history tidy and your team in sync. They also give you confidence that no mistake is truly permanent, since Git always keeps a trace.

As an AI/ML Engineer, I’ve seen how messy histories and merge chaos can slow teams down. Mastering these advanced commands makes a big difference: your commits stay meaningful, your branches merge cleanly, and your teammates trust the process more.

Next time you’re unsure about a Git situation, take a step back:

Keep practicing these on side projects — they’ll become second nature. And once they do, you’ll find that Git feels less like a source of stress and more like a true partner in your development workflow.

What’s Next?

In the next post, I’ll explore Git Stash — a lifesaver for juggling multiple tasks in real-world AI/ML projects.

You can also check out the official Git documentation for deeper details.