Menu

Git Developer Reference Version Control March 2026 ⏱ 16 min read

20 Git Commands Every Developer Should Know in 2026

The commands you will actually use every day, every week, and every time something breaks. Real syntax, real examples, common mistakes, and a full quick-reference table to bookmark.

Git is non-negotiable in professional development. Every team uses it. Every project depends on it. And yet most developers learn just enough to commit and push, then get completely stuck the first time a merge conflict appears or a commit needs to be undone. This guide covers all 20 commands that matter, explained clearly with working examples, common mistakes, and guidance on when to use each one.

First: Configure Git on Every New Machine

Before running any Git command on a new machine, run these two configuration commands. Git attaches your identity to every single commit you make. Without this configuration, your commits will have no author information, which causes problems on GitHub and in team repositories.

Run once per machine
Set your identity
git config --global user.name "Your Name" git config --global user.email "[email protected]"

Attached to every commit you make. Use the same email as your GitHub account so commits link to your profile correctly.

Set default branch name
git config --global init.defaultBranch main

New repositories will use main instead of master as the default branch name, matching GitHub's convention.

Verify your configuration
git config --list git config user.name git config user.email

Prints your current Git configuration. Use to confirm identity is set correctly before making your first commit on a machine.

Set your preferred editor
git config --global core.editor "code --wait" # or: vim, nano, etc.

Sets VS Code as the editor used for commit messages and interactive rebase. Without this, Git defaults to Vim, which surprises many developers the first time.

Daily Core Commands

These commands form the backbone of everything you do in Git. You will run most of them multiple times per day in any active project.

Commands 1–7: The everyday essentials
Status, staging, committing, logging, and comparing changes.
1
git init — Create a New Repository
Turn any folder into a Git repository instantly
As Needed

Creates a new Git repository in the current directory. Adds a hidden .git folder that tracks all version history, branch information, and configuration for the project. Nothing outside that folder is affected.

bash
git init # Init in current directory git init my-project # Init in a new folder called my-project
When to use it
  • Starting a brand new project from scratch
  • Converting an existing folder to be version-controlled
  • Setting up a local repository before connecting to GitHub
What it creates
  • A hidden .git/ directory with all tracking data
  • An initial empty commit history
  • A default branch (main if you configured it above)
2
git clone — Copy an Existing Repository
Download a complete copy of any remote repository
Often

Downloads a complete copy of a remote repository to your local machine, including the full commit history, all branches, and all tags. Not just the latest files: the entire project history comes with it.

bash
git clone https://github.com/user/repository.git git clone https://github.com/user/repository.git my-folder # custom folder name git clone --depth 1 https://github.com/user/repo.git # shallow clone, latest only git clone [email protected]:user/repository.git # SSH clone
Useful variations
  • Use HTTPS for simplicity, SSH for passwordless authentication
  • --depth 1 speeds up cloning huge repos by fetching only the latest snapshot
  • Custom folder name avoids collisions when cloning multiple forks
After cloning
  • Remote origin is automatically configured
  • All remote branches are available via git branch -a
  • Tracking is set up: git pull works immediately
3
git status — See What's Changed
The single most important command to run before anything else
Daily

Shows the current state of your working directory and staging area. This is the command you will run more than any other. Always run git status before committing, before merging, and before switching branches. It tells you exactly where you are and what is waiting to be done.

bash
git status # Full output git status -s # Short format: M = modified, A = added, ? = untracked

The output tells you: which branch you are on, which files are modified (but not staged), which files are staged (ready to commit), and which files are untracked (new files Git has never seen). Each piece of information answers a question before you take the next action.

Status output symbols (short mode)
  • M = Modified (tracked file changed)
  • A = Added (newly staged file)
  • ?? = Untracked (new file, not yet added)
  • D = Deleted
Make it a habit
  • Run before git add to see what changed
  • Run before git commit to confirm staging
  • Run after git pull to see what arrived
4
git add — Stage Changes
Choose exactly which changes go into the next commit
Daily

Staging is the step between editing files and committing. It lets you select precisely which changes belong in the next commit. This is one of Git's most powerful ideas: you can make ten changes across five files, then craft three separate commits each focused on one logical change.

bash
git add filename.js # Stage one specific file git add src/ # Stage all changes in a directory git add . # Stage every changed and new file git add *.css # Stage all CSS files git add -p # Interactive: stage chunk by chunk
git add -p is underrated
  • Shows each changed section (hunk) and asks y/n/s
  • y = stage this hunk, n = skip, s = split further
  • Lets you commit half a file's changes, leaving the rest for the next commit
  • Produces much cleaner, more focused commit history
Common mistake
  • Using git add . exclusively creates monolithic commits that mix unrelated changes
  • Unrelated changes in one commit make rollbacks painful and code reviews difficult
  • One logical change per commit is the professional standard
5
git commit — Save Changes
Create a permanent, named snapshot of your staged changes
Daily

Creates a permanent snapshot of everything you have staged. Each commit gets a unique hash identifier, a timestamp, your author information, and a message explaining the change. A good commit history is one of the most valuable things in a long-lived codebase: it tells the story of why the code looks the way it does.

bash
git commit -m "Add user authentication endpoint" git commit -am "Fix typo in README" # Stage + commit tracked files in one step git commit --amend # Edit the last commit message git commit --amend --no-edit # Add staged changes to last commit, keep message
The -am shortcut
  • -a automatically stages all modified tracked files
  • Does NOT stage new (untracked) files: still use git add for those
  • Useful for quick fixes to existing files when you are confident in what changed
--amend caution
  • Only amend commits that have NOT been pushed to a shared remote
  • Amending a pushed commit rewrites history and forces others to reconcile
  • Safe on local-only commits before a push
6
git log — View Commit History
Browse the complete history of every change ever made
Daily

Displays the commit history of the current branch. The default output is verbose: each commit shows its full hash, author, date, and message. Use flags to get the specific view you need for the situation.

bash
git log # Full history git log --oneline # One line per commit git log --oneline --graph --all # Visual branch graph git log --author="Alex" --since="2 weeks ago" # Filter by author and time git log -p filename.js # History with diffs for one file git log --grep="authentication" # Search commit messages
Most useful combinations
  • --oneline --graph --all shows all branches visually in the terminal
  • --since="yesterday" shows what the team committed today
  • -p filename shows every change ever made to a specific file
  • --grep finds commits by keyword in the message
Useful log aliases
  • Add git config --global alias.lg "log --oneline --graph --all" to your config
  • Then type git lg for the visual branch graph every time
7
git diff — See Exactly What Changed
Line-by-line comparison of any two states of your code
Daily

Shows the actual line-by-line differences between states of your files. Green lines with + are additions. Red lines with - are deletions. Running git diff --staged before every commit is one of the best habits you can build: it confirms exactly what is going into the snapshot before you create it.

bash
git diff # Changes in working dir (not staged) git diff --staged # Changes staged but not yet committed git diff HEAD # All changes since last commit git diff main feature-branch # Diff between two branches git diff abc1234 def5678 # Diff between two specific commits git diff HEAD~3 HEAD -- file.js # Changes to one file over last 3 commits
Before every commit
  • Run git diff --staged to review what is staged
  • Catches accidentally staged debug logs, console.log calls, and leftover test code
  • Takes 10 seconds and prevents low-quality commits
For comparing large outputs
  • For comparing JSON files or API responses, the Text Diff Checker on StackDevTools gives a visual side-by-side view without needing a terminal

Branching and Merging

Branches are Git's killer feature. They let you work on anything without touching stable code. Master these commands and your entire team's workflow becomes safer and more parallel.

🌿
Commands 8–11: Branches and merging
Create, switch, merge, and rebase branches.
8
git branch — Manage Branches
List, create, rename, and delete branches
Daily

Branches let you work on features, bug fixes, and experiments in isolation without affecting the main codebase. Create a new branch for every feature and every bug fix, no matter how small. It costs nothing and gives you the ability to discard, rebase, or review the work independently.

bash
git branch # List all local branches git branch -a # List local and remote branches git branch -v # List with last commit on each git branch feature/user-auth # Create a new branch (stays on current) git branch -m old-name new-name # Rename a branch git branch -d feature/done # Delete a fully merged branch git branch -D feature/abandoned # Force delete (unmerged changes discarded)
Branch naming conventions
  • feature/add-login for new features
  • fix/auth-redirect-bug for bug fixes
  • chore/update-dependencies for maintenance
  • release/v2.1.0 for release preparation
-D vs -d
  • -d refuses to delete if the branch has unmerged commits, protecting your work
  • -D deletes regardless. Use only when you are certain you want to discard unmerged work
9
git switch — Change Branches
The modern, cleaner way to move between branches
Daily

Switches to a different branch. git switch was introduced in Git 2.23 as a cleaner, more focused replacement for the branch-switching part of git checkout. Both commands work, but switch has clearer intent and better error messages. Use switch for branches and checkout for restoring individual files.

bash
git switch main # Switch to main git switch feature/user-auth # Switch to an existing branch git switch -c feature/payment-api # Create and switch in one step git switch -c hotfix/login-bug main # Create from a specific base branch # Old syntax (still works, both are valid) git checkout main git checkout -b feature/payment-api
The -c flag is your most-used combo
  • git switch -c feature/name creates and switches in one command
  • This is the most common branching workflow: branch from main, build the feature, merge back
  • Starting a feature? Run git switch main first, then git switch -c feature/name
Uncommitted changes when switching
  • Git will refuse to switch if you have uncommitted changes that conflict with the target branch
  • Options: commit the changes, stash them with git stash, or discard them
10
git merge — Combine Branches
Bring a feature branch's changes into the main branch
Weekly

Merges the commits from another branch into your current branch. Always switch to the receiving branch first (the branch you want to bring changes into), then run git merge with the name of the branch to merge from.

bash
git switch main git merge feature/user-auth # Merge feature into main git merge --no-ff feature/user-auth # Force a merge commit even if fast-forward possible git merge --squash feature/user-auth # Squash all feature commits into one git merge --abort # Abort a merge in progress (during conflict)
Handling merge conflicts
  • Git marks conflict areas in files with <<<<<<< and >>>>>>> markers
  • Edit the file to keep what you want, remove the markers
  • Then git add the resolved file and git commit to complete the merge
  • VS Code and IntelliJ both have visual conflict resolution tools
--no-ff explained
  • Without --no-ff, a fast-forward merge just moves the pointer: no merge commit is created
  • With --no-ff, a merge commit is always created, preserving the record that the feature branch existed
  • Teams that value clear history use --no-ff by convention
11
git rebase — Rewrite History
Replay your commits on top of another branch for a cleaner history
Weekly

Rebase replays your branch's commits on top of another branch, as if you had branched off from that point in time. The result is a clean, linear history with no merge commits. Use it to keep feature branches up to date with the main branch, or to clean up local commits before sharing them.

bash
git switch feature/my-feature git rebase main # Replay feature commits on top of main git rebase -i HEAD~3 # Interactive rebase: edit last 3 commits # In interactive mode: pick, squash, reword, drop, fixup # squash = combine this commit with the previous one # drop = remove the commit entirely
The golden rule of rebase
  • Never rebase a branch that other developers are also working on
  • Rebase rewrites commit hashes: everyone else's history becomes incompatible
  • Safe to rebase your own local feature branch before merging
  • Never rebase main, develop, or any shared branch
Interactive rebase is powerful
  • Squash multiple WIP commits into one clean commit before merging
  • Reword confusing commit messages before they go public
  • Drop accidental debug commits cleanly

Remote and Sync Commands

Remote commands connect your local repository to the outside world: GitHub, GitLab, Bitbucket, or any other host. These are what make collaboration across teams and locations possible.

🌐
Commands 12–14: Pull, push, and remote management
Sync changes with remote repositories and manage connections.
12
git pull — Get Remote Changes
Fetch what the team pushed and integrate it into your branch
Daily

Fetches changes from the remote repository and integrates them into your current local branch. git pull is actually two commands in one: git fetch (download changes) followed by git merge (integrate them). Using --rebase substitutes the merge with a rebase, keeping history cleaner.

bash
git pull # Pull from tracked upstream branch git pull origin main # Explicitly pull from origin/main git pull --rebase origin main # Rebase instead of merge on pull git fetch origin # Download changes without integrating (safer)
fetch vs pull
  • git fetch downloads remote changes but does not integrate them: safe to run any time
  • git pull downloads AND merges: can create merge commits or conflicts
  • When unsure what the remote contains, use git fetch then review with git log origin/main before merging
Set rebase as default pull strategy
  • Run git config --global pull.rebase true once
  • Every git pull will rebase automatically, producing cleaner history across the team
13
git push — Send Changes to Remote
Share your local commits with the rest of the team
Daily

Uploads your local commits to the remote repository, making them available to everyone else. The first push of a new branch requires the -u flag to set up upstream tracking, after which plain git push works for subsequent pushes on that branch.

bash
git push # Push current branch to its upstream git push origin feature/my-feature # Push to a specific remote branch git push -u origin feature/my-feature # Set upstream tracking (first push only) git push --force-with-lease # Safe force push git push origin --delete old-branch # Delete a remote branch git push origin --tags # Push all tags
--force vs --force-with-lease
  • Never use --force on a shared branch: it overwrites whatever is on the remote, including work others pushed
  • --force-with-lease checks that nobody else has pushed to the branch since you last fetched
  • If someone else pushed, --force-with-lease will refuse and tell you, preventing data loss
  • Always use --force-with-lease when you must force push after a rebase
14
git remote — Manage Remote Connections
Add, rename, and update connections to remote repositories
Often

Manages the saved connections (remotes) between your local repository and remote ones. origin is the conventional name for the primary remote and is set automatically when you clone. You can have multiple remotes, which is common when contributing to open-source projects where you have both an origin (your fork) and an upstream (the original repo).

bash
git remote -v # List remotes with URLs git remote add origin https://github.com/user/repo.git # Add a remote git remote add upstream https://github.com/org/repo.git # Add upstream (for forks) git remote set-url origin https://new-url.git # Change a remote URL git remote remove origin # Remove a remote git remote rename origin old-origin # Rename a remote

Undo and Recovery Commands

These are the commands developers wish they had learned earlier. Knowing how to safely undo changes is what separates confident Git users from developers who fear the tool.

↩️
Commands 15–17: Stash, reset, and revert
Save work temporarily, undo commits, and reverse changes safely.
15
git stash — Save Unfinished Work Temporarily
Park your work-in-progress and come back to it later
Often

Saves your uncommitted changes to a temporary stack and reverts your working directory to the last commit, leaving it clean. Essential when you need to urgently switch to another branch (for a hotfix or to review someone's PR) without losing your half-finished work.

bash
git stash # Stash all uncommitted changes git stash push -m "WIP: login form" # Stash with a descriptive name git stash pop # Restore most recent stash and remove it from stack git stash apply # Restore most recent stash but keep it in stack git stash list # See all stashes git stash apply stash@{2} # Apply a specific stash by index git stash drop # Delete the most recent stash git stash clear # Delete all stashes
pop vs apply
  • pop = restore and remove from stash stack (you are done with it)
  • apply = restore but keep it in the stack (you might want to apply it to another branch too)
Untracked files
  • By default, git stash only stashes tracked modified files
  • Use git stash -u to also stash new untracked files
  • Use git stash --all to include ignored files too
16
git reset — Undo Commits
Move the branch pointer back and choose what happens to the changes
Often

Moves the current branch pointer back to a previous commit. The key is understanding what happens to the changes from the undone commits. Three modes control this: --soft keeps them staged, --mixed (default) keeps them unstaged, and --hard discards them entirely.

bash
git reset HEAD~1 # Undo last commit, keep changes unstaged (mixed) git reset --soft HEAD~1 # Undo last commit, keep changes staged git reset --hard HEAD~1 # Undo last commit, PERMANENTLY DISCARD changes git reset HEAD~3 # Undo last 3 commits git reset filename.js # Unstage a specific file (keep the changes)
--hard is permanent
  • --hard discards the changes from the undone commits with no recovery
  • Only use it when you are absolutely certain you do not need those changes
  • If you just want to undo a pushed commit safely, use git revert instead
--soft for squashing manually
  • git reset --soft HEAD~3 undoes 3 commits but leaves all their changes staged
  • Then git commit -m "Single clean commit" squashes them into one
  • Useful alternative to interactive rebase for simple squashing
17
git revert — Undo Safely on Shared Branches
Create a new commit that reverses a previous one, without rewriting history
Often

Creates a brand new commit that reverses the effects of a previous commit. Unlike git reset, revert does not rewrite history: it adds to it. This makes it completely safe to use on shared branches where other developers have already pulled the history you would be changing.

bash
git revert abc1234 # Revert a specific commit by hash git revert HEAD # Revert the most recent commit git revert HEAD~3..HEAD # Revert the last 3 commits git revert abc1234 --no-commit # Stage the revert without committing yet
reset vs revert: which to use
  • Commit not yet pushed: use git reset (rewrite local history freely)
  • Commit already pushed to shared branch: use git revert (adds to history safely)
  • On main or production branches: always git revert, never git reset

Power Commands

These three commands handle specific high-value situations. They are not needed every day, but when you need them, nothing else does the job.

⚙️
Commands 18–20: Cherry-pick, tag, and bisect
Apply specific commits, mark releases, and hunt bugs in history.
18
git cherry-pick — Apply Specific Commits
Take one commit from any branch and apply it to another
As Needed

Applies the changes introduced by specific commits from one branch onto your current branch. Useful when a feature branch contains a critical bug fix that needs to go to production immediately, without merging the entire unfinished feature.

bash
git cherry-pick abc1234 # Apply one commit git cherry-pick abc1234 def5678 # Apply multiple commits in order git cherry-pick abc1234..def5678 # Apply a range of commits git cherry-pick abc1234 --no-commit # Apply without committing (review first)
Classic use case
  • Fix is committed on a feature branch but main needs it now
  • Switch to main, run git cherry-pick with the fix's commit hash
  • The fix is now on main without merging the whole feature branch
Use sparingly
  • Cherry-picking creates duplicate commits with different hashes
  • When the feature branch eventually merges, the cherry-picked fix will appear twice
  • Prefer merging or rebasing over cherry-picking for entire features
19
git tag — Mark Releases
Permanently label a specific commit, usually for release versions
As Needed

Tags mark a specific point in commit history with a human-readable name. The most common use is version releases: v1.0.0, v2.3.1. Unlike branches, tags do not move when new commits are added: they stay permanently attached to the commit they were created on.

bash
git tag v1.0.0 # Lightweight tag (just a label) git tag -a v1.0.0 -m "Version 1.0.0 release" # Annotated tag (with message, author, date) git tag -a v1.0.0 abc1234 -m "Tagging past" # Tag a specific past commit git tag # List all tags git push origin --tags # Push all tags to remote git push origin v1.0.0 # Push a single tag git tag -d v1.0.0 # Delete a local tag
Annotated vs lightweight
  • Annotated tags (-a) store the tagger's name, email, date, and a message. Use for releases.
  • Lightweight tags are just a pointer to a commit hash. Use for local convenience only.
  • GitHub Releases are built from annotated tags
Tags are not pushed automatically
  • git push does not push tags by default
  • Always run git push origin --tags or git push origin v1.0.0 after creating a release tag
20
git bisect — Find Which Commit Introduced a Bug
Binary search through history to pinpoint the exact breaking commit
As Needed

When a bug exists now but did not exist in a previous version, git bisect performs an automated binary search through commit history. You mark one commit as bad (has the bug) and one as good (bug-free). Git checks out commits in the middle, you test each one, and in about log2(N) steps it identifies the exact commit that introduced the bug. For 1,000 commits, that is about 10 steps.

bash
git bisect start git bisect bad # Current commit has the bug git bisect good v1.0.0 # v1.0.0 was clean # Git checks out a middle commit automatically # Test whether the bug is present, then: git bisect good # This commit is fine git bisect bad # This commit has the bug # Repeat 6-10 times, then Git prints: # abc1234 is the first bad commit git bisect reset # Return to original HEAD when done
Why bisect is underused
  • Most developers manually check commits one by one: O(n) time
  • Bisect is O(log n): 10 checks finds the culprit in 1,024 commits
  • Even 5 minutes learning bisect saves hours on complex regressions
Automation
  • If you have a test script, git bisect run ./test.sh automates the entire binary search
  • Git runs the script at each step: exit 0 = good, exit non-zero = bad

How to Write Good Commit Messages

A commit message is a note to your future self and your teammates about why a change was made. The code shows what changed. The commit message should explain why. A well-written commit history is one of the most valuable things in a long-lived codebase.

Good Add rate limiting to the authentication endpoint Imperative mood, specific, explains what
Good Fix infinite loop in cart total calculation when discount is 100% Explains the bug scenario
Good Refactor user service to use repository pattern Clear scope and intent
Avoid fixed stuff No information
Avoid WIP Never push WIP commits to shared branches
Avoid changes Not searchable, not readable

Use the imperative mood for the subject line: "Add feature" not "Added feature." Keep the subject under 72 characters. If more explanation is needed, add a blank line after the subject and write a body paragraph explaining the why, any trade-offs made, or links to relevant issues.

The 6 Commands That Cover 90% of Daily Work

You do not need to memorise all 20 commands from day one. In practice, these six cover the vast majority of everything you do in a typical working day. Master these first before exploring the rest.

The core six
Start here. These six commands handle 90% of daily Git work.
git status
Where am I? What changed? Run before everything else.
git add .
Stage all changes. Use -p for selective staging.
git commit -m
Save a snapshot. Write a meaningful message.
git pull
Get the team's latest changes before starting work.
git push
Share your commits. Use -u on first push of a branch.
git switch -c
Create and switch to a new branch for every feature.

Complete Quick Reference Table

Bookmark this table. All 20 commands with their most useful flags and when to reach for each one:

# Command What It Does Frequency
1 git init Create a new repository in the current directory As needed
2 git clone [url] Download a full copy of a remote repository Often
3 git status Show modified, staged, and untracked files Daily
4 git add [file] Stage changes for the next commit; use -p for interactive Daily
5 git commit -m Save staged changes as a permanent snapshot Daily
6 git log --oneline View commit history in compact form Daily
7 git diff --staged See exactly what is staged and about to be committed Daily
8 git branch List, create, rename, or delete branches Daily
9 git switch -c Create and switch to a new branch in one step Daily
10 git merge [branch] Merge another branch into the current one Weekly
11 git rebase [branch] Replay commits on top of another branch; -i for interactive Weekly
12 git pull Fetch and integrate remote changes into current branch Daily
13 git push Upload local commits to the remote repository Daily
14 git remote -v List and manage remote repository connections Often
15 git stash Park uncommitted changes temporarily to switch context Often
16 git reset HEAD~1 Undo commits; --soft keeps staged, --hard discards Often
17 git revert [hash] Undo a commit safely by creating a new counter-commit Often
18 git cherry-pick Apply one specific commit from any branch to current branch As needed
19 git tag -a v1.0.0 Mark a release point with an annotated tag As needed
20 git bisect Binary search history to find the commit that introduced a bug As needed

7-Step Git Workflow for Every Feature

This is the standard workflow used in professional teams. Every feature, every bug fix, and every documentation update follows the same pattern. Internalise this and your contributions will always be clean, reviewable, and safe to merge:

  1. Pull the latest main before starting anything. Run git switch main then git pull. Never start a new feature branch from a stale base: you will immediately have a harder merge when you finish. This 5-second step prevents hours of conflict resolution later.
  2. Create a focused branch for the work. Run git switch -c feature/descriptive-name. The branch name should communicate what the branch does: fix/login-redirect-loop or feature/add-stripe-checkout. One branch per unit of work: do not mix feature work and bug fixes in the same branch.
  3. Work in small, frequent commits. Commit every logical step: after writing a test, after making it pass, after cleaning up. Do not save everything into one giant commit at the end. Small commits are easier to understand, easier to review, and easier to revert if something goes wrong. Use git add -p to stage only what belongs in each commit.
  4. Review staged changes before every commit. Run git diff --staged and read through every line before committing. This catches accidentally staged files, debug statements (console.log, print()), leftover commented-out code, and incorrect file inclusions. It takes 30 seconds and catches the majority of low-quality commits before they are created.
  5. Keep the branch up to date with main. On long-running branches, periodically run git fetch origin and then git rebase origin/main to incorporate the latest changes. This keeps the diff small and merge conflicts manageable. A branch that diverges from main for two weeks will be significantly harder to merge than one rebased daily.
  6. Use the Text Diff Checker for non-code reviews. When reviewing changes to configuration files, JSON payloads, YAML files, or documentation, paste the before and after versions into the Text Diff Checker for a clear visual comparison. It is faster than reading raw git diff output for structured data formats and helps catch subtle changes that are easy to miss in terminal output.
  7. Clean up before merging. Use git rebase -i main to squash any WIP commits, fix any confusing commit messages, and drop any accidental debug commits. The goal is a clean, logical set of commits that tells the story of the feature clearly. Then push, open a pull request, and let the team review. After merging, delete the branch with git branch -d feature/name.
⚠ One habit to avoid: committing directly to main

Even on personal projects, always use feature branches. The discipline of creating a branch, committing your work, and merging it back builds the muscle memory that makes you productive on any team. Committing directly to main also makes it impossible to use pull requests for self-review before deploying, which is one of the best quality checks available.

Frequently Asked Questions About Git

What is the difference between git merge and git rebase?

Both integrate changes from one branch into another, but they produce different history. Merge creates a new "merge commit" that joins the two branches, preserving the exact history of both: you can see that a feature branch existed and was merged at a specific point. Rebase replays your branch's commits on top of the target branch one by one, producing a straight linear history with no merge commit. Use merge when you want to preserve the full history of development. Use rebase to produce a clean linear history before merging a feature, or to keep a long-running feature branch up to date with main. Never rebase shared branches that other people have pulled.

What is the difference between git reset and git revert?

Both undo commits, but in fundamentally different ways. git reset moves the branch pointer backward, effectively removing commits from the history. It rewrites history, which makes it unsafe on any branch others have already pulled. git revert creates a new commit that reverses the changes of a previous commit, leaving the original commit in place. History is never rewritten. The rule: use reset for local commits you have not pushed yet. Use revert for anything on a shared or remote branch. On production branches, always revert.

How do I undo a git add before committing?

Use git restore --staged filename.js to unstage a specific file (moves it back from staged to modified, but keeps your changes). To unstage everything at once, run git restore --staged .. The older syntax git reset HEAD filename.js does the same thing and still works, but git restore --staged is the modern, clearer command introduced in Git 2.23. Neither command discards your actual changes: the files remain modified in your working directory, just no longer staged for the next commit.

What should I put in a .gitignore file?

The .gitignore file tells Git which files and folders to never track. Every project should ignore: dependency directories (node_modules/, .venv/), build output (dist/, build/, *.class), environment files with secrets (.env, .env.local), editor config (.vscode/, .idea/), and operating system files (.DS_Store, Thumbs.db). The site gitignore.io generates a complete .gitignore file for any combination of languages, frameworks, and editors. Commit your .gitignore to the repository so the whole team benefits from the same exclusion rules.

How do I resolve a merge conflict?

When Git cannot automatically merge changes, it marks the conflicting sections in the affected files. Open each conflicted file: you will see <<<<<<< HEAD marking your changes, ======= as the separator, and >>>>>>> branch-name marking the incoming changes. Edit the file to keep what you want, removing all three marker lines in the process. You can keep your version, their version, both, or write something new that combines them. After editing all conflicted files, run git add on each resolved file and then git commit to complete the merge. VS Code's "Accept Current", "Accept Incoming", and "Accept Both" buttons make this visually intuitive.

How do I compare two branches or two versions of a file?

In the terminal, git diff main feature-branch shows all differences between two branches. For a specific file, git diff main feature-branch -- filename.js narrows it to just that file. For comparing JSON payloads, configuration files, or any text output that is easier to read outside the terminal, the Text Diff Checker on StackDevTools gives you a side-by-side visual comparison with additions highlighted in green and deletions in red, making it significantly easier to spot changes in structured data.

Free browser-based tools

Tools that pair with your Git workflow

Compare file versions, validate JSON in commits, convert configs between formats, and more. All free, all in your browser, no login required.

Git Gets Easier With Repetition

The 20 commands in this guide cover every situation you will encounter in professional development. You do not need to memorise all of them from the start: begin with the daily six (status, add, commit, pull, push, switch), use them until they are automatic, then add the branching and undo commands as you encounter the situations that need them.

The commands that matter most to learn deeply are the undo commands: git reset, git revert, and git stash. Knowing how to safely recover from mistakes is what makes developers confident in Git rather than afraid of it. Understanding the difference between reset and revert alone prevents the most common team-wide Git disasters.

Keep this reference bookmarked alongside the Text Diff Checker for comparing file versions without a terminal, and the JSON Formatter for validating any JSON in your commits and API responses. The combination covers the majority of day-to-day development tasks you will face working in a Git-based team.