By Kedar Salunkhe · Updated March 2026
There is a specific problem with how most people prepare for Git interview questions.
They find a list of commands. They read what each one does. They go into the interview and give answers that sound exactly like someone who found a list of commands and read what each one does.
Experienced interviewers hear this immediately. The answers are technically correct but flat. There is no context. No sense of when you would choose one approach over another. No real example that shows the command in a situation that actually happened.
This guide is built differently. Every command here is explained with what it does, why you would use it, what the output looks like, and where it fits into real work. Not every Git command in existence — just the ones that come up in interviews and in daily professional use, explained properly.
Read through this blog “top git commands explained“. Then open a terminal and run the commands yourself. That combination is what makes answers feel natural in an interview rather than recited.
Table of Contents
1. Setup and Configuration Commands
These are the first commands you run on any new machine. They set up your identity and preferences globally across all repositories. Interviewers sometimes ask about configuration to check whether you understand the scope system — local, global, and system settings.
git config
Sets configuration values for Git. The most important ones are your name and email, which get attached to every commit you make. Without these, Git will either refuse to commit or use placeholder values that make your commits look wrong in the history.
# Set your name and email globally (once per machine)
$ git config --global user.name "Rohan Mehta"
$ git config --global user.email "rohan@example.com"
# Set default branch name for new repositories
$ git config --global init.defaultBranch main
# Set VS Code as your default editor
$ git config --global core.editor "code --wait"
# See all your current config values
$ git config --list
# See a specific value
$ git config user.name
The scope hierarchy: Git has three levels of configuration. System level applies to every user on the machine. Global applies to your user account. Local applies only to the current repository. Local overrides global, global overrides system. Most developers only ever use global and local.
Interview angle: If asked “how do you configure Git differently for a work project vs a personal project,” the answer is git config --local user.email "work@company.com" inside the work repository. Local config overrides your global settings for that repo only.
2. Starting and Getting a Repository
git init
Initialises a new Git repository in the current directory. Creates a hidden .git folder that contains everything Git needs. Before this command, the folder is just a regular directory. After it, Git tracks everything inside.
# Initialise in the current directory
$ git init
Initialized empty Git repository in /my-project/.git/
# Initialise and immediately name the default branch
$ git init -b main
# Initialise a new directory in one step
$ git init new-project
Use case: Starting a brand new project from scratch that does not yet exist anywhere online. Once you have made your first commits, you connect it to GitHub using git remote add origin and push.
What not to do: Running git init inside a folder that is already inside another Git repository creates a nested repository, which causes confusing behaviour. Always check that you are not already inside a Git repo before running git init.
git clone
Creates a local copy of an existing remote repository. Downloads everything: all files, all commits, all branches, the complete history. Also automatically sets up the remote as origin.
# Clone a repository from GitHub
$ git clone https://github.com/username/project.git
# Clone into a specific folder name
$ git clone https://github.com/username/project.git my-folder-name
# Clone a specific branch only
$ git clone -b develop https://github.com/username/project.git
# Clone with SSH (preferred when you have SSH keys set up)
$ git clone git@github.com:username/project.git
# Shallow clone — only get recent history (faster for large repos)
$ git clone --depth=1 https://github.com/username/project.git
Use case: Joining an existing project or contributing to open source. This is the starting point for most real-world work. You clone once per machine per project, then use fetch and pull to stay updated.
Interview angle: The difference between git clone and git init is a very common beginner question. Clone is for getting an existing repo. Init is for starting a new one. They are not interchangeable.
3. Snapshot and Staging Commands
These are the commands you use dozens of times every day. The staging cycle — status, add, commit — is the heartbeat of working with Git. Getting this section comfortable is the single most valuable preparation you can do.
git status
Shows the current state of your working directory and staging area. Which branch you are on. Which files are modified but not staged. Which files are staged and ready to commit. Which new files exist that Git is not tracking yet.
$ git status
On branch feature/user-auth
Changes to be committed:
(use "git restore --staged ..." to unstage)
new file: src/auth/login.js
Changes not staged for commit:
(use "git add ..." to update what will be committed)
modified: src/app.js
Untracked files:
(use "git add ..." to include in what will be committed)
src/auth/register.js
Use case: Run this before staging, before committing, before pushing, and any time you are not sure where things stand. It is read-only — it never changes anything. There is no cost to running it as often as you want.
Compact version: git status -s gives a shorter two-column output. The left column shows the staging area, the right shows the working directory. M means modified, A means added, ?? means untracked.
git add
Moves changes from the working directory into the staging area. You are telling Git: include these changes in the next commit.
# Stage a specific file
$ git add src/auth/login.js
# Stage all changes in the current directory and subdirectories
$ git add .
# Stage all changes to tracked files (but not new untracked files)
$ git add -u
# Stage specific parts of a file interactively
$ git add -p src/app.js
# Stage an entire directory
$ git add src/auth/
The -p flag is worth knowing for interviews: git add -p (patch mode) lets you stage specific chunks of a file rather than the whole file. It walks you through each changed section and asks whether to stage it. This is how you create clean, focused commits when you have made multiple unrelated changes to the same file.
Use case: You have been working for two hours and modified six files. Three of them relate to one feature, three relate to a bug fix. Stage and commit them separately with meaningful messages rather than one giant “changed stuff” commit.
git commit
Creates a permanent snapshot of everything in the staging area. Each commit is immutable once created — it gets a unique SHA-1 hash and is stored in the repository forever.
# Standard commit with inline message
$ git commit -m "Add email validation to login form"
# Stage all tracked modified files and commit in one step
$ git commit -am "Fix typo in error message"
# Open editor to write a longer multi-line commit message
$ git commit
# Amend the last commit (before pushing only)
$ git commit --amend -m "Corrected commit message"
# Amend the last commit adding a forgotten file
$ git add forgotten-file.js
$ git commit --amend --no-edit
What makes a good commit message: Subject line under 50 characters in the imperative mood. “Add validation” not “Added validation.” It should complete the sentence: “If applied, this commit will… Add validation.” If more explanation is needed, leave a blank line after the subject and write a body.
Interview angle: Interviewers ask about --amend regularly. Key point: only use it on commits that have not been pushed to a shared remote. Amending rewrites the commit and changes its hash. If others have already pulled that commit, amending creates a conflict between your rewritten version and their original.
git stash
Saves uncommitted work to a temporary stack and cleans your working directory back to the last commit. Lets you switch context without committing incomplete work.
# Stash current work with a descriptive message
$ git stash push -m "WIP: half-finished user profile page"
# List all stashes
$ git stash list
stash@{0}: WIP: half-finished user profile page
stash@{1}: WIP: experimental nav refactor
# Restore the most recent stash and remove it from the stack
$ git stash pop
# Restore a specific stash without removing it
$ git stash apply stash@{1}
# Delete a specific stash
$ git stash drop stash@{1}
# Clear all stashes
$ git stash clear
# Stash only unstaged changes (keep staged ones as-is)
$ git stash --keep-index
Real scenario: You are building a dashboard feature. Halfway through, your lead messages about an urgent production bug. Your changes are not commit-ready. Run git stash push -m "WIP dashboard", switch to main, fix and push the bug, come back to your feature branch, run git stash pop. Your half-finished work is exactly where you left it.
4. Branching and Merging Commands
Branching questions are almost guaranteed in any Git interview. Know every command here and be able to explain the difference between merge and rebase clearly — that one comes up constantly.
git branch
Lists, creates, renames, and deletes branches. One of the most versatile commands.
# List all local branches (* marks the current one)
$ git branch
# List local branches with last commit info
$ git branch -v
# List all branches including remote-tracking
$ git branch -a
# List only remote-tracking branches
$ git branch -r
# Create a new branch (does not switch to it)
$ git branch feature/search
# Create a branch at a specific commit
$ git branch hotfix/payment a3f9c12
# Rename a branch
$ git branch -m old-name new-name
# Delete a merged branch (safe)
$ git branch -d feature/old-feature
# Force delete regardless of merge status
$ git branch -D feature/abandoned
# List branches merged into current branch
$ git branch --merged
# List branches not yet merged
$ git branch --no-merged
Interview angle: git branch --merged is a useful interview mention. Running it on main shows every branch that has already been merged into main and is therefore safe to delete. It is a practical cleanup command that shows you understand repository hygiene.
git checkout / git switch
git checkout was historically used for switching branches, checking out files, and more. Git 2.23 split this into two focused commands: git switch for changing branches and git restore for restoring files. Both old and new syntax still work — know both.
# Switch to an existing branch (old syntax)
$ git checkout main
# Switch to an existing branch (new syntax)
$ git switch main
# Create and switch in one step (old syntax)
$ git checkout -b feature/new-dashboard
# Create and switch in one step (new syntax)
$ git switch -c feature/new-dashboard
# Switch back to the previous branch
$ git switch -
$ git checkout -
# Check out a specific commit (creates detached HEAD)
$ git checkout a3f9c12
# Create a new branch from a specific tag
$ git checkout -b hotfix/v2-patch v2.0.0
Use case for the last one: Production is running version 2.0.0 but main has three weeks of unreleased changes. A critical bug needs a patch. Branch off the v2.0.0 tag, fix the bug, release as v2.0.1, then merge the fix back into main so future releases include it.
git merge
Integrates the history of one branch into another. You run it while on the destination branch.
# Merge a feature branch into main
$ git checkout main
$ git merge feature/user-auth
# Fast-forward merge output (no divergence)
Updating a3f9c12..f4d8a1b
Fast-forward
src/auth/login.js | 42 ++++++++++++++
# Force a merge commit even when fast-forward is possible
$ git merge --no-ff feature/user-auth
# Merge and automatically resolve by using the current branch's version
$ git merge -X ours feature/experimental
# Abort a merge that has conflicts
$ git merge --abort
# Squash all commits from a branch into one uncommitted change
$ git merge --squash feature/user-auth
Fast-forward vs merge commit: When main has not moved since you created your feature branch, Git can fast-forward — simply move the main pointer forward. No merge commit is created and history stays linear. When both branches have new commits, a three-way merge happens and a merge commit with two parents is created. The --no-ff flag forces a merge commit even when fast-forward is possible — useful when you want the history to show clearly when a feature was integrated.
git rebase
Moves or replays commits from one branch onto another. Instead of creating a merge commit, it rewrites the commit history to appear linear — as if you had started your work from the current tip of the target branch.
# Rebase current branch onto main
$ git checkout feature/search
$ git rebase main
# Interactive rebase — edit the last 4 commits
$ git rebase -i HEAD~4
# Continue after resolving a rebase conflict
$ git rebase --continue
# Skip a commit during rebase
$ git rebase --skip
# Abort the entire rebase
$ git rebase --abort
# Rebase onto a specific commit
$ git rebase --onto main feature/old feature/new
Interactive rebase is interview gold. With git rebase -i HEAD~4, Git opens an editor listing the last four commits. You can change pick to squash to combine commits, reword to edit a message, drop to remove a commit entirely, or reorder the lines to change commit order. This is how developers clean up a messy branch history before opening a Pull Request.
The golden rule: Never rebase commits that have already been pushed to a shared remote branch. Rebase creates new commit objects with different hashes. If teammates have already pulled the original commits, their history no longer matches yours and reconciling it is painful.
git cherry-pick
Applies the changes from a specific commit onto the current branch as a new commit. You are picking exactly one commit from anywhere in the history and replaying it where you are now.
# Apply a single commit to the current branch
$ git cherry-pick a3f9c12
# Apply multiple commits
$ git cherry-pick a3f9c12 b4e8d23
# Apply a range of commits
$ git cherry-pick a3f9c12..f4d8a1b
# Cherry-pick without immediately committing (stage only)
$ git cherry-pick -n a3f9c12
# Continue after resolving a cherry-pick conflict
$ git cherry-pick --continue
# Abort cherry-pick
$ git cherry-pick --abort
Classic real-world use case: A developer has been working on a big feature for two weeks. Inside that branch is one commit that fixes a critical bug. Production needs that fix now. Cherry-pick that one commit onto main. The rest of the unfinished feature stays on its branch. Production gets only the fix.
5. Remote Repository Commands
These commands manage the connection between your local repository and remote servers like GitHub. Remote commands are where a lot of beginners have gaps — they know push and pull but not the underlying mechanics.
git remote
Manages connections to remote repositories. Each connection is a named URL that Git remembers.
# Show all remotes with their URLs
$ git remote -v
origin https://github.com/username/project.git (fetch)
origin https://github.com/username/project.git (push)
# Add a new remote
$ git remote add origin https://github.com/username/project.git
# Add an upstream remote (common when you have forked a repo)
$ git remote add upstream https://github.com/original/project.git
# Change a remote's URL
$ git remote set-url origin https://github.com/username/new-project.git
# Remove a remote
$ git remote remove upstream
# Rename a remote
$ git remote rename origin github
Interview angle: When you fork an open-source repository, you typically set up two remotes. origin points to your fork. upstream points to the original repository. You push your changes to origin and open a Pull Request from there. You pull updates from upstream to keep your fork current. Knowing this two-remote pattern shows you have done real open-source work.
git fetch
Downloads changes from the remote and updates your remote-tracking branches. Does not touch your local branches or working directory. It is a safe read operation.
# Fetch all branches from origin
$ git fetch origin
# Fetch a specific branch
$ git fetch origin feature/new-search
# Fetch from all remotes
$ git fetch --all
# Fetch and remove remote-tracking branches
# that no longer exist on the remote
$ git fetch --prune
# Fetch and automatically prune (set this globally)
$ git config --global fetch.prune true
Why fetch before pull matters: After git fetch, run git log origin/main --oneline to see exactly what changed on the remote before deciding how to integrate it. You can inspect, compare, even cherry-pick from the fetched changes before merging. This is safer than git pull which fetches and immediately merges without giving you that review step.
git pull
Fetches from the remote and immediately merges the changes into the current local branch. The combination of git fetch plus git merge in one command.
# Standard pull — fetch and merge
$ git pull
# Pull from a specific remote and branch
$ git pull origin main
# Pull using rebase instead of merge (keeps history linear)
$ git pull --rebase
# Pull with rebase always (set globally)
$ git config --global pull.rebase true
# Fetch only, no merge (equivalent to git fetch)
$ git pull --no-commit --ff-only
Interview angle: The difference between git pull and git pull --rebase comes up regularly. With the default merge, if your local branch has commits the remote does not have, pulling creates a merge commit that clutters the history. With --rebase, your local commits are replayed on top of the remote commits, keeping history linear. Many teams configure pull.rebase true globally to make this the default.
git push
Uploads local commits to the remote repository. Specifically sends the commits on your current branch that the remote does not have yet.
# Push current branch to its tracked remote
$ git push
# First-time push of a new branch — set upstream tracking
$ git push -u origin feature/new-dashboard
# Push to a specific remote and branch
$ git push origin main
# Push all local branches
$ git push --all origin
# Push all tags
$ git push origin --tags
# Delete a remote branch
$ git push origin --delete feature/old-feature
# Force push (rewrites remote history — use with extreme caution)
$ git push --force
# Safer force push — fails if remote has new commits since you last fetched
$ git push --force-with-lease
--force-with-lease is worth knowing for interviews. Regular --force overwrites the remote regardless of what is there. If a teammate pushed a commit after you last fetched, --force destroys their work silently. --force-with-lease checks that the remote branch is still where you last saw it — if anyone has pushed since your last fetch, it refuses and asks you to fetch first. It is the responsible way to force-push when you have to.
6. Inspection and Comparison Commands
These commands are how you answer the question “what happened?” They are the diagnostic tools of Git and come up both in interviews and in real debugging situations.
git log
Shows commit history. Enormously customisable. The default output is verbose — most developers use options to make it more readable.
# Default — full history, verbose
$ git log
# One line per commit (most commonly used)
$ git log --oneline
# Visual branch graph
$ git log --oneline --graph --all
# Last 5 commits only
$ git log -5 --oneline
# Commits by a specific author
$ git log --author="Rohan" --oneline
# Commits between two dates
$ git log --since="2026-01-01" --until="2026-01-31" --oneline
# Commits that touched a specific file
$ git log --oneline -- src/auth/login.js
# Commits with the actual diff included
$ git log -p
# Show stats (which files changed and how many lines)
$ git log --stat --oneline
# Search commit messages
$ git log --grep="payment" --oneline
Interview angle: git log --oneline --graph --all is the command that shows you the full branching structure of a repository visually in the terminal. Being able to mention this specifically — and knowing what it shows — signals real command-line familiarity.
git diff
Shows line-by-line differences between file versions. Where git log shows the what and when, git diff shows the exactly-how-the-lines-changed.
# Unstaged changes (working directory vs staging area)
$ git diff
# Staged changes (staging area vs last commit)
$ git diff --staged
$ git diff --cached # same thing, older syntax
# Difference between two branches
$ git diff main feature/search
# Difference between two commits
$ git diff a3f9c12 f4d8a1b
# Which files changed between two branches (no line details)
$ git diff --name-only main feature/search
# Difference between working directory and a specific commit
$ git diff HEAD~3
Use this before every commit: Run git diff --staged right before git commit. You see exactly what is about to be committed. It only takes ten seconds and it catches staging mistakes — accidentally staged debug logs, leftover console.log statements, half-finished changes — before they become part of the permanent history.
git show
Shows the full details of any Git object — most commonly a commit. Displays the commit metadata and the complete diff of every file changed in that commit.
# Show the most recent commit
$ git show HEAD
# Show a specific commit
$ git show a3f9c12
# Show a commit on another branch
$ git show feature/search:src/search.js
# Show only the files changed in a commit, not the full diff
$ git show --stat a3f9c12
# Show the contents of a specific file at a specific commit
$ git show a3f9c12:src/auth/login.js
git blame
Annotates every line of a file with the last commit that modified it. Shows who changed each line, when, and in which commit.
# Blame a file
$ git blame src/auth/login.js
# Blame a specific range of lines
$ git blame -L 25,50 src/auth/login.js
# Show commits without the full hash (shorter output)
$ git blame --abbrev=8 src/auth/login.js
Real use case: You are reading code and find a line that makes no sense. Run git blame on the file, find the commit hash for that line, run git show on that hash. In ten seconds you can see who wrote it, when, what the full context of that commit was, and often find a PR link or issue number in the commit message that explains the entire decision. This is genuinely how experienced developers navigate unfamiliar codebases.
7. Undoing and Rewriting Commands
Interviewers pay close attention to how you answer undo questions because misusing these commands is how teams end up with corrupted history and frustrated colleagues. Knowing the right tool for each situation is what separates someone who is safe to work with from someone who is a liability on a shared codebase.
git restore
Discards changes to files. Introduced in Git 2.23 to replace the file-manipulation uses of git checkout.
# Discard changes to a file in the working directory
# (restores it to the staged version, or last commit if not staged)
$ git restore src/auth/login.js
# Discard all changes in the working directory
$ git restore .
# Unstage a file (move it back from staging to working directory)
$ git restore --staged src/auth/login.js
# Restore a file to how it looked in a specific commit
$ git restore --source=HEAD~3 src/auth/login.js
Warning: git restore on the working directory is irreversible. Uncommitted changes are gone permanently. There is no undo. Always run git status and git diff before restoring to make sure you know exactly what you are discarding.
git reset
Moves the branch pointer to a different commit, with different effects on the staging area and working directory depending on the mode used.
# Undo the last commit, keep changes staged
$ git reset --soft HEAD~1
# Undo the last commit, keep changes in working directory (unstaged)
# This is the default if no mode is specified
$ git reset HEAD~1
$ git reset --mixed HEAD~1
# Undo the last commit and discard all changes completely
# DESTRUCTIVE — use carefully
$ git reset --hard HEAD~1
# Reset to a specific commit
$ git reset --hard a3f9c12
# Unstage a file without changing the working directory
$ git reset HEAD src/auth/login.js
The rule that matters in interviews: Use git reset only on commits that have not been pushed to a shared remote. If you reset past a commit that teammates have already pulled, your branch history diverges from theirs and reconciling it is messy and painful. For undoing changes on shared branches, use git revert instead.
git revert
Creates a new commit that is the exact inverse of a previous commit. The original commit stays in the history. Nothing is rewritten. This is the safe way to undo changes on shared branches.
# Revert a specific commit
$ git revert a3f9c12
# Revert the most recent commit
$ git revert HEAD
# Revert without opening the editor (use default message)
$ git revert --no-edit a3f9c12
# Revert a merge commit
# -m 1 specifies the first parent (main) as the mainline to preserve
$ git revert -m 1 a3f9c12
# Stage the revert without committing immediately
$ git revert -n a3f9c12
Why revert over reset on shared branches: When you revert, the history shows that the original commit happened and then was undone. Anyone who has already pulled the original commit simply pulls the revert commit next and everything is consistent. When you reset, you are rewriting history — you are claiming the original commit never happened. Anyone who already pulled it now has a different history than yours.
git reflog
Shows a log of every position HEAD has been in for the last 90 days. It records every operation — commits, checkouts, merges, resets, rebases — regardless of whether those commits are still reachable from any branch.
# Show the full reflog
$ git reflog
# Show the reflog for a specific branch
$ git reflog show feature/search
# Recover a commit that was "lost" in a hard reset
$ git reflog
f4d8a1b HEAD@{0}: reset: moving to HEAD~3
a3f9c12 HEAD@{1}: commit: Add search functionality ← this is the lost commit
# Create a branch at the lost commit to recover it
$ git checkout -b recovered-search a3f9c12
Interview angle: Reflog is what interviewers are thinking of when they ask “is there any way to recover a commit after a hard reset?” The answer is yes — with the reflog, as long as you act within the 90-day window before garbage collection removes unreachable objects. This is the Git safety net most beginners do not know about.
8. Advanced Commands Worth Knowing
These come up in intermediate and senior-level interviews, and in DevOps roles specifically. As a beginner, understanding these conceptually and being able to describe one real use case for each is enough to make a strong impression.
git bisect
Uses binary search to find the commit that introduced a bug. You mark one commit as good (bug did not exist) and one as bad (bug exists) and Git automatically navigates to the midpoint for you to test. Each round cuts the search space in half.
$ git bisect start
$ git bisect bad HEAD # current state is broken
$ git bisect good v2.0.0 # this version was working
# Git checks out the midpoint — test your application
# If the bug exists at this commit:
$ git bisect bad
# If the bug does not exist at this commit:
$ git bisect good
# Repeat until Git identifies the first bad commit:
# "a3f9c12 is the first bad commit"
# Always clean up when done
$ git bisect reset
Why this matters: Without bisect, finding a bug introduced somewhere in the last 200 commits means manual inspection. With bisect, it takes about 8 rounds (log₂ of 200) regardless of how many commits there are. You can also automate it: if you have a script that exits 0 for good and non-zero for bad, run git bisect run ./test.sh and Git completes the search entirely automatically.
git tag
Marks specific commits with meaningful names — most commonly used for release versions. Tags do not move when new commits are made, unlike branches.
# Create a lightweight tag (just a pointer)
$ git tag v2.0.0
# Create an annotated tag (a full object with metadata)
$ git tag -a v2.0.0 -m "Release 2.0.0: adds OAuth login"
# List all tags
$ git tag
# List tags matching a pattern
$ git tag -l "v2.*"
# Tag a specific commit
$ git tag -a v1.9.5 a3f9c12 -m "Hotfix release 1.9.5"
# Push a specific tag to remote
$ git push origin v2.0.0
# Push all tags
$ git push origin --tags
# Delete a local tag
$ git tag -d v2.0.0-beta
# Delete a remote tag
$ git push origin --delete v2.0.0-beta
# Check out a tag (creates detached HEAD)
$ git checkout v2.0.0
git worktree
Lets you have multiple branches checked out simultaneously in different directories, all sharing the same .git repository. No stashing, no context switching — you literally have two branches open at the same time in separate folders.
# Add a new working tree for a hotfix
$ git worktree add ../project-hotfix hotfix/payment-bug
# List all working trees
$ git worktree list
# Remove a working tree when done
$ git worktree remove ../project-hotfix
Use case: You are in the middle of a complex feature and need to test something on main quickly without disrupting your work. Instead of stashing and switching, add a worktree for main in a separate directory, run your test there, come back to your original directory unchanged.
git archive
Creates a zip or tar archive of a tree — a branch, tag, or commit — without including the .git directory. Used for creating clean release packages.
# Create a zip of the v2.0.0 release
$ git archive --format=zip --output=release-v2.0.0.zip v2.0.0
# Create a tar.gz archive of main
$ git archive --format=tar.gz --output=project-latest.tar.gz main
9. Command Combinations Interviewers Love Asking About
Some interview questions are really about command combinations — not one command but the sequence of commands that handles a real situation. These are the scenarios where knowing individual commands is not enough. You need to know which commands to combine and in what order.
Starting a New Feature
$ git checkout main
$ git pull origin main
$ git checkout -b feature/user-notifications
# Work, work, work
$ git add .
$ git commit -m "Add notification preferences to user settings"
$ git push -u origin feature/user-notifications
# Open Pull Request
Updating a Feature Branch That Has Fallen Behind Main
$ git checkout feature/user-notifications
$ git fetch origin
$ git rebase origin/main
# Resolve any conflicts, then:
$ git rebase --continue
$ git push --force-with-lease origin feature/user-notifications
Deploying a Hotfix to Production
$ git checkout main
$ git pull origin main
$ git checkout -b hotfix/login-crash v2.3.0
# Fix the bug
$ git add auth/session.js
$ git commit -m "Fix null pointer when session token expires"
$ git checkout main
$ git merge hotfix/login-crash
$ git tag -a v2.3.1 -m "Hotfix: fix session expiry crash"
$ git push origin main --tags
$ git branch -d hotfix/login-crash
Recovering from an Accidental Hard Reset
# Find the lost commit in the reflog
$ git reflog
a3f9c12 HEAD@{1}: commit: Add user notification preferences
# Create a branch at the lost commit
$ git checkout -b recovered-notifications a3f9c12
# Verify everything is there, then merge into main
$ git checkout main
$ git merge recovered-notifications
Cleaning Up Before a Pull Request
# Interactively rebase the last 5 commits
$ git rebase -i HEAD~5
# In the editor: squash "fix typo" and "WIP" commits
# into the meaningful commits they belong to
# Update the remote branch
$ git push --force-with-lease origin feature/user-notifications
Frequently Asked Questions
Which Git commands are asked most often in interviews?
The commands that come up in almost every interview are git status, git add, git commit, git push, git pull, git branch, git checkout or git switch, git merge, and git log. After those, the next tier that frequently appears includes git stash, git revert, git reset, git rebase, git diff, and git cherry-pick. For DevOps roles specifically, git bisect, git hooks, git tag, and git config scope questions are more common than in pure developer interviews. Know the core nine cold. Know the second tier well enough to explain clearly with a real example.
What is the difference between git pull –rebase and git pull?
Regular git pull does a fetch followed by a merge. If your local branch has commits that the remote does not, pulling creates a merge commit that joins the two histories. This adds a merge commit to your log for every pull where your local branch was ahead. git pull –rebase does a fetch followed by a rebase. Instead of creating a merge commit, it replays your local commits on top of the fetched commits, keeping the history linear. Many teams configure this as the default with git config –global pull.rebase true because it results in a cleaner, more readable history without merge commits cluttering every sync.
When should I use git merge –no-ff?
Use it when you want the history to explicitly show that a feature branch was created and merged, even when a fast-forward would be possible. Without –no-ff, a fast-forward merge moves the main pointer forward and makes it look like all the commits were made directly on main — the branch structure is invisible in the history. With –no-ff, a merge commit is always created, preserving the record that this set of commits was developed on a branch and merged at a specific point. Teams that want a clear record of when features were integrated use –no-ff consistently. Teams that prefer a clean linear history prefer fast-forward merges or squash merges.
What is the difference between git reset –soft, –mixed, and –hard?
All three move the branch pointer back to a previous commit. The difference is what happens to the changes that were in the commits being undone. With –soft, the changes come back staged in the index — ready to commit again immediately. With –mixed (the default), the changes come back to the working directory but unstaged. With –hard, the changes are discarded completely. –soft is useful when you want to recommit with a different message or split one commit into multiple. –mixed is useful when you want to review and selectively restage. –hard is the destructive option — use it only when you are certain you want to permanently discard the changes, and only on commits that have not been pushed.
Is there a way to undo git add before committing?
Yes. git restore –staged filename removes a file from the staging area and puts it back in the working directory as a modified but unstaged file. The change itself is not lost — only its staged status is reverted. To unstage everything at once, run git restore –staged . with a dot. In older versions of Git before 2.23, the equivalent command was git reset HEAD filename, which still works in current Git versions. Running git status after unstaging confirms the file is back in the “Changes not staged for commit” section.
What does git push –force-with-lease do and why is it safer than –force?
Both flags allow you to push even when the remote has commits your local branch does not have — normally Git would reject this to prevent overwriting remote work. The difference is that –force overwrites the remote unconditionally. If a teammate pushed a commit after you last fetched, –force destroys their work silently. –force-with-lease checks that the remote branch is still at the same commit you last fetched. If anyone has pushed new commits since your last fetch, –force-with-lease refuses and asks you to fetch first. Use –force-with-lease whenever you need to force-push, typically after an interactive rebase on a personal feature branch. Never use –force on shared branches.
What is the quickest way to see which files changed in the last commit?
Run git show –stat HEAD. This shows the commit metadata and a summary of which files changed and how many lines were added or removed, without showing the full line-by-line diff. For a list of just the filenames with no other output, use git diff –name-only HEAD~1 HEAD or git show –name-only HEAD. If you want the full diff of the last commit including every changed line, use git show HEAD or git log -p -1.
Related Resources
For going deeper on the commands covered here:
- Git Official Documentation — The authoritative reference for every command and every option. When you need to know exactly what a flag does, start here.
- Pro Git (Free Book) — Chapters 2 and 7 cover the daily commands and advanced tools respectively. Free to read online and worth bookmarking.
- GIT
- Git Internals Explained: How Git Works Behind the Scenes (Step-by-Step Guide) [2026]
- What is Git? Beginner Guide (With Real Examples) [2026]
Final Thoughts
The gap between knowing Git commands and being good at Git is experience. Reading this guide puts you in a better position than most candidates going into interviews. Running the commands in a real terminal puts you in a better position than that. Using them on a real project — with real branches, real commits, real conflicts to resolve — puts you in the position where the answers feel natural because they actually happened to you.
A few things worth remembering going into any Git interview. The undo commands — reset, revert, restore — are where most mistakes happen. Know which ones rewrite history and which ones do not. Know which ones are safe on shared branches and which ones are not. Interviewers probe this area specifically because it is where junior developers cause problems in production.
Know --force-with-lease instead of --force. Know the difference between fetch and pull. Know when to use rebase and when to use merge. Know what the staging area actually is and why it exists. These are the conceptual foundations that make every other command make sense.
And if you get asked about a command you have never used — git worktree, git archive, git bisect — be honest about not having used it in practice and then explain what you understand about it conceptually. That answer is better than guessing. Every interviewer who has been in the industry more than three years has used commands they learned on the job, not in preparation. They respect the honest answer more than the confident wrong one.
About the Author
Kedar Salunkhe
DevOps Engineer | Seven years of fixing things that break at 2am
Kubernetes • OpenShift • AWS • Coffee
I’ve spent almost 7 years keeping production systems running, often when everyone else is asleep. These days I’m working with Kubernetes and OpenShift deployments, automating everything that can be automated, and occasionally remembering to document the things I fix. When I’m not troubleshooting clusters, I’m probably trying out new DevOps tools or explaining to someone why we can’t just “restart everything” as a debugging strategy. You can usually find me where the coffee is strong and the error logs are confusing.
Excellent blog post. I definitely appreciate this website.
Keep writing!
Someone essentially help to make seriously posts I’d state.
That is the very first time I frequented your website page and up to now?
I surprised with the research you made to make this actual post amazing.
Great process!