Skip to content
Home
Advanced Git Commands Every Developer Should Know

Advanced Git Commands Every Developer Should Know

Developer Tools Developer Tools 8 min read 1646 words Beginner ExcellentWiki Editorial Team

Once you’ve mastered git add, commit, push, and pull, these advanced commands will save you hours of manual work and rescue you from seemingly impossible situations.

Interactive Rebase

Clean up commit history before pushing:

git rebase -i HEAD~5

Opens an editor where you can:

  • pick — keep the commit
  • reword — change the message
  • squash — merge into the previous commit
  • fixup — merge and discard the message
  • drop — remove the commit

Use case: Combine “fix typo” and “oops forgot file” commits into one clean commit before opening a PR.

Rewriting History Safely

Interactive rebase rewrites commit history by creating new commits with new hashes. This is perfectly safe for commits that haven’t been pushed or are on a personal feature branch. Never rebase commits that other team members have already pulled — the rewritten history causes merge conflicts for everyone. If you need to undo a public commit, use git revert instead, which creates a new commit that undoes the changes without rewriting history.

Practical Rebase Workflow

# Before: 30 messy commits on feature branch
git log --oneline
# a1b2c3 fix typo in login
# d4e5f6 oops forgot to add the file
# g7h8i9 WIP trying to get auth working
# j0k1l2 add login feature

# Squash into 3 meaningful commits
git rebase -i HEAD~4
# Change 'pick' to 'fixup' for the fixup commits
# Keep a clean commit for each logical change

# After: clean, reviewable history

Git Reflog

Git’s safety net. Records every movement of HEAD:

git reflog

Use case: You accidentally reset to the wrong commit. Find the lost commit in reflog and git reset --hard <sha> to restore it.

Reflog in Depth

The reflog tracks every change to the HEAD reference for the last 90 days (by default). This includes commits, checkouts, resets, rebases, and merges. Even if a commit isn’t in any branch, it’s in the reflog as long as you’ve checked it out recently.

# View reflog with dates
git reflog --date=iso

# Restore a lost commit
git reflog
# a1b2c3c HEAD@{2}: reset: moving to HEAD~3
git checkout a1b2c3c   # or git reset --hard a1b2c3c

The reflog is local — it’s not shared with remotes. If you deleted a branch that was never pushed, the reflog is your only recovery option.

Git Bisect

Binary search to find the commit that introduced a bug:

git bisect start
git bisect bad                # current commit is broken
git bisect good v2.0.0        # this tag was working
# Git checks out a middle commit. Test it.
git bisect good               # if this commit works
# or
git bisect bad                # if this commit is broken
# Repeat until Git identifies the first bad commit
git bisect reset

Use case: A test started failing sometime in the last 50 commits. Bisect finds the exact commit in ~6 steps.

Automating Bisect

For maximum efficiency, automate bisect with a script:

git bisect start HEAD v2.0.0
git bisect run npm test        # automatically marks each commit
git bisect reset

Bisect run executes the given command on each commit. If the command exits with 0, the commit is marked “good.” Any non-zero exit marks it “bad.” This is incredibly powerful for CI pipelines where a regression was introduced but you don’t know when — just fire off an automated bisect overnight.

Git Cherry-Pick

Apply specific commits from another branch:

git cherry-pick <commit-sha>
git cherry-pick <sha1> <sha2> <sha3>  # multiple commits
git cherry-pick main..feature          # range of commits

Use case: A bug fix was committed to the develop branch but needs to be in main immediately without merging the whole branch.

Cherry-Pick vs Merge vs Rebase

OperationWhen to Use
cherry-pickSelectively apply individual commits
mergeCombine entire branch histories
rebaseMove an entire branch to a new base

Cherry-pick is ideal for hotfixes and backporting. If a security patch is committed on main and needs to go into a release/v1 branch, cherry-pick applies just that fix. Avoid overusing cherry-pick, though — it creates duplicate commits that can cause confusing merge conflicts later if the branches are eventually merged.

Git Stash Advanced

git stash push -m "WIP: refactoring auth"  # named stash
git stash list                               # view all stashes
git stash show -p stash@{1}                  # view changes
git stash branch new-feature stash@{1}       # create branch from stash

Use case: You’re working on a feature but need to fix an urgent bug. Stash your work, fix the bug, then git stash pop to resume.

Stash Management Tips

# Stash only unstaged changes
git stash push -k -m "only staged changes"

# Stash specific files
git stash push -m "config only" -- config.yml

# Interactive stash (select hunks)
git stash push -p

# Drop a specific stash
git stash drop stash@{2}

# Clear all stashes
git stash clear

A common pitfall: git stash pop applies the most recent stash and removes it from the stash list. Use git stash apply if you want to keep the stash for later use (useful when applying the same fix to multiple branches).

Git Submodules

Include one repo inside another:

git submodule add https://github.com/example/lib.git lib/
git clone --recurse-submodules <repo-url>
git submodule update --remote  # update all submodules

Use case: You have a shared library used by multiple projects. Keep it as a submodule to version it independently.

Submodule Alternatives

Submodules can be frustrating — forgetting --recurse-submodules on clone leaves empty directories, and updating submodules requires explicit commands. Consider alternatives:

  • Git subtree — Merges an external repo into your repo. No separate commands needed when cloning, but history is embedded in your repo.
  • Package manager — For libraries, a language-specific package manager (npm, pip, cargo) is almost always better than submodules.
  • Monorepo — If teams share significant code, a monorepo eliminates submodule complexity entirely.

Use submodules only when you truly need independent versioning of a dependency that isn’t available as a package.

Git Worktree

Work on multiple branches simultaneously:

git worktree add ../project-release release-branch
git worktree add -b new-feature ../project-feature
git worktree list
git worktree remove ../project-release

Use case: You need to make a hotfix on main without stashing or committing your current feature branch work. Create a worktree for main.

Worktree Advantages

Unlike stashing (which requires saving your current state) or cloning (which duplicates the entire repo and re-downloads objects), worktrees share the same .git directory but maintain separate working directories and indexes. This means:

  • No disk space wasted on duplicate objects
  • Both worktrees can have staged and unstaged changes simultaneously
  • Switching between contexts is instant — just cd to the other directory

Worktrees are particularly useful for reviewing PRs. Check out a PR branch in a worktree, review it, delete the worktree — all while keeping your main development branch untouched.

Git Log Formatting

# One-line graph with branches
git log --oneline --graph --all --decorate

# Show changes in each commit
git log -p

# Search commits by message
git log --grep="fix bug"

# Search commits by author
git log --author="Alice"

# Custom format
git log --pretty=format:"%h %s by %an, %ar"

Creating Git Aliases

Turn complex log commands into simple aliases:

git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.find "log --oneline --all --grep"
git config --global alias.hist "log --pretty=format:'%C(yellow)%h%Creset %s %Cgreen(%an)%Creset %Cred%ad%Creset' --date=short"

Now git lg shows the full commit graph, git find "auth" searches all commits for “auth,” and git hist shows a formatted timeline. Aliases transform Git from a cumbersome tool into a frictionless daily driver.

Git Filter-Branch and Filter-Repo

For large-scale history rewrites, such as removing a committed secret or splitting a repository:

# Remove a file from all history (filter-repo, faster and safer)
git filter-repo --path config/credentials.json --invert-paths

# Remove a file using filter-branch (older approach)
git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch config/credentials.json' \
  --prune-empty --tag-name-filter cat -- --all

git filter-repo is the modern replacement for filter-branch and is significantly faster on large repositories. It is a standalone tool that must be installed separately but integrates with git. Use it when you need to remove sensitive data, split a monorepo, or change author information across the entire project history.

Git Range-Diff

Compare two commit ranges side by side:

git range-diff main..feature-1 main..feature-2

This is invaluable during code review when a PR has been rebased or updated. Instead of re-reviewing the entire diff, range-diff shows only the changes between the old and new versions of the commit range. Each commit in the old range is compared against its counterpart in the new range, highlighting what changed between review rounds.

FAQ

What is the difference between git revert and git reset?

git revert creates a new commit that undoes changes from a previous commit, preserving history. git reset moves the branch pointer backward, discarding commits. Use revert for public history and reset for local, unpublished changes.

How do I recover a deleted branch?

Use git reflog to find the SHA of the commit that was at the tip of the deleted branch, then run git branch <branch-name> <sha>. The reflog records all HEAD movements locally for 90 days.

When should I use git stash pop vs git stash apply?

Use pop when you want to apply the stash and remove it from the stash list. Use apply when you want to apply the stash to multiple branches or keep it for later reuse.

How can I undo a merge?

If the merge hasn’t been pushed, use git reset --hard ORIG_HEAD to undo it. If it has been pushed, use git revert -m 1 <merge-commit-sha> to create a revert commit.

What is the difference between git merge and git rebase?

Merge creates a new commit that joins two histories, preserving the exact timeline of both branches. Rebase rewrites commits from one branch onto the tip of another, creating a linear history. Merge is safer for shared branches; rebase is cleaner for personal feature branches.


Related: Learn how to undo commits and Git branching strategies.

Section: Developer Tools 1646 words 8 min read Beginner 756 articles in section Report inaccuracy Back to top