25 Git Commands Every Developer Should Know

Published February 10, 2026 · 12 min read

Git is the backbone of modern software development. Whether you are a beginner just starting out or a seasoned developer looking to sharpen your workflow, knowing the right git commands can save you hours of frustration. This git tutorial covers 25 essential commands with practical, real-world examples you can start using today.

⚙ Try it: Keep this handy: Our Git Commands Cheat Sheet puts all essential commands on one page.

We have organized the commands from basic to advanced, so if you are new to git, start from the top. If you already know the basics, skip ahead to the branching or advanced sections.

1. Basic Git Commands

Every git workflow starts with these foundational commands. Master these first, and the rest will follow naturally.

git init — Create a New Repository

The git init command creates a brand new Git repository in your current directory. It sets up the hidden .git folder that tracks all your changes.

# Create a new project and initialize git
mkdir my-project
cd my-project
git init

# Output: Initialized empty Git repository in /home/user/my-project/.git/

Use this when starting a brand new project from scratch. For existing projects hosted on GitHub, GitLab, or Bitbucket, use git clone instead.

git clone — Copy a Remote Repository

The git clone command downloads an entire repository, including all its history, branches, and tags.

# Clone via HTTPS
git clone https://github.com/user/repo.git

# Clone via SSH (recommended for regular contributors)
git clone git@github.com:user/repo.git

# Clone into a specific folder name
git clone https://github.com/user/repo.git my-folder

# Shallow clone (only latest commit — faster for large repos)
git clone --depth 1 https://github.com/user/repo.git

git add — Stage Changes

Before you can commit changes, you need to stage them. Think of staging as selecting which changes you want to include in your next snapshot.

# Stage a specific file
git add index.html

# Stage multiple files
git add index.html style.css app.js

# Stage all changes in the current directory
git add .

# Stage all tracked files that have been modified (ignores new untracked files)
git add -u

# Interactively choose which hunks to stage
git add -p

Tip: Use git add -p when you have made multiple unrelated changes in the same file and want to split them into separate commits. This is one of the most underrated git commands for keeping a clean history.

git commit — Save a Snapshot

A commit records your staged changes as a permanent snapshot in the repository history. Write clear, descriptive commit messages.

# Commit with a message
git commit -m "Add user login form with validation"

# Stage all tracked changes and commit in one step
git commit -am "Fix header alignment on mobile screens"

# Amend the last commit (change message or add forgotten files)
git add forgotten-file.js
git commit --amend -m "Add user login form with validation and error handling"

Best practice: Write commit messages in the imperative mood ("Add feature" not "Added feature"). Keep the first line under 72 characters and add details in the body if needed.

git status — Check the Working Tree

The git status command shows you which files are staged, unstaged, and untracked. Run it often to stay oriented.

# Full status output
git status

# Short format (more compact)
git status -s

# Example short output:
#  M  src/app.js       (modified, not staged)
# M   src/index.html   (modified, staged)
# ??  src/new-file.js  (untracked)

git log — View Commit History

Browse through your project's commit history to understand what changed and when.

# Standard log
git log

# Compact one-line format
git log --oneline

# Show a visual branch graph
git log --oneline --graph --all

# Filter by author
git log --author="Jane"

# Filter by date range
git log --after="2026-01-01" --before="2026-02-01"

# Search commit messages
git log --grep="fix login"

# Show changes in each commit
git log -p

# Show last 5 commits
git log -5

The --oneline --graph --all combination is especially useful for visualizing how branches relate to each other.

2. Branching and Merging

Branches are what make Git so powerful. They let you work on features, fixes, and experiments in isolation without affecting the main codebase.

git branch — Manage Branches

Create, list, rename, and delete branches.

# List all local branches (* marks the current branch)
git branch

# List all branches including remote
git branch -a

# Create a new branch (does not switch to it)
git branch feature/user-auth

# Rename a branch
git branch -m old-name new-name

# Delete a branch (only if fully merged)
git branch -d feature/user-auth

# Force delete a branch (even if not merged — use with caution)
git branch -D experimental-branch

# Show which branches contain a specific commit
git branch --contains abc1234

git checkout — Switch Branches (Classic)

The traditional way to switch between branches and restore files.

# Switch to an existing branch
git checkout main

# Create a new branch and switch to it
git checkout -b feature/shopping-cart

# Checkout a specific file from another branch
git checkout main -- src/config.js

git switch — Switch Branches (Modern)

Introduced in Git 2.23, git switch is a cleaner alternative to git checkout for branch operations. It does one thing and does it well.

# Switch to an existing branch
git switch main

# Create and switch to a new branch
git switch -c feature/notifications

# Switch back to the previous branch
git switch -

Recommendation: Use git switch for changing branches and git restore for restoring files. These newer commands replace the overloaded git checkout with clearer intent.

git merge — Combine Branches

Merge integrates changes from one branch into another. It creates a merge commit that ties the two histories together.

# Merge a feature branch into main
git switch main
git merge feature/user-auth

# Merge with a custom commit message
git merge feature/user-auth -m "Merge user authentication feature"

# Abort a merge if there are conflicts you cannot resolve right now
git merge --abort

# After resolving conflicts manually, complete the merge
git add .
git commit

Resolving Merge Conflicts

When Git cannot automatically merge changes, it marks the conflicting sections in your files:

<<<<<<< HEAD
const apiUrl = "https://api.example.com/v2";
=======
const apiUrl = "https://api.example.com/v3";
>>>>>>> feature/api-upgrade

Edit the file to keep the version you want (or combine both), remove the conflict markers, then stage and commit.

3. Remote Operations

Working with remote repositories is essential for collaboration. These commands let you sync your local work with GitHub, GitLab, Bitbucket, or any other Git hosting service.

git remote — Manage Remote Connections

# List configured remotes
git remote -v

# Add a new remote
git remote add origin https://github.com/user/repo.git

# Add an upstream remote (common in fork workflows)
git remote add upstream https://github.com/original/repo.git

# Change a remote's URL
git remote set-url origin git@github.com:user/repo.git

# Remove a remote
git remote remove old-remote

git fetch — Download Remote Changes

Fetch downloads changes from a remote repository but does not merge them into your working branch. This is the safe way to see what others have been doing.

# Fetch from the default remote (origin)
git fetch

# Fetch from a specific remote
git fetch upstream

# Fetch all remotes
git fetch --all

# Fetch and prune deleted remote branches
git fetch --prune

Tip: Get in the habit of running git fetch before starting new work. It updates your view of the remote without touching your local branches.

git pull — Fetch and Merge

Pull is essentially git fetch followed by git merge. It downloads remote changes and immediately integrates them.

# Pull from the tracked upstream branch
git pull

# Pull with rebase instead of merge (cleaner history)
git pull --rebase

# Pull from a specific remote and branch
git pull origin main

# Set pull to rebase by default
git config --global pull.rebase true

Many teams prefer git pull --rebase to avoid unnecessary merge commits that clutter the history.

git push — Upload Local Changes

Push sends your committed changes to a remote repository so others can access them.

# Push to the tracked upstream branch
git push

# Push and set upstream tracking for a new branch
git push -u origin feature/user-auth

# Push all branches
git push --all

# Push tags
git push --tags

# Delete a remote branch
git push origin --delete feature/old-branch

Warning: Avoid git push --force on shared branches. If you must force push, use git push --force-with-lease which checks that no one else has pushed since your last fetch.

4. Advanced Git Commands

These commands unlock Git's full potential. They take some practice, but once you are comfortable with them, you will handle complex workflows with ease.

git rebase — Rewrite Commit History

Rebase moves or combines a sequence of commits to a new base commit. It is commonly used to keep feature branches up to date with main and to create a linear history.

# Rebase your feature branch onto the latest main
git switch feature/dashboard
git rebase main

# Interactive rebase: edit, squash, or reorder the last 5 commits
git rebase -i HEAD~5

# During interactive rebase, you can:
# pick   — keep the commit as-is
# reword — change the commit message
# squash — combine with the previous commit
# fixup  — like squash but discard the commit message
# drop   — remove the commit entirely

# Continue after resolving conflicts
git rebase --continue

# Abort and go back to the state before the rebase
git rebase --abort

Golden rule: Never rebase commits that have been pushed to a shared branch. Rebase rewrites history, and this will cause problems for anyone who has based their work on those commits.

git cherry-pick — Apply Specific Commits

Cherry-pick lets you take one or more commits from any branch and apply them to your current branch. This is perfect for pulling a specific bug fix without merging an entire branch.

# Apply a single commit by its hash
git cherry-pick abc1234

# Cherry-pick multiple commits
git cherry-pick abc1234 def5678

# Cherry-pick without committing (stage the changes only)
git cherry-pick --no-commit abc1234

# Abort if there are conflicts
git cherry-pick --abort

Use case: A critical bug fix is on a feature branch that is not ready to merge. Cherry-pick that specific fix commit onto your release branch.

git stash — Temporarily Shelve Changes

Stash saves your uncommitted changes so you can switch context without committing half-done work.

# Stash current changes
git stash

# Stash with a descriptive message
git stash push -m "WIP: user profile redesign"

# Stash including untracked files
git stash -u

# List all stashes
git stash list
# stash@{0}: On feature/profile: WIP: user profile redesign
# stash@{1}: WIP on main: abc1234 Fix header

# Apply the most recent stash (keeps it in the stash list)
git stash apply

# Apply and remove the most recent stash
git stash pop

# Apply a specific stash
git stash apply stash@{1}

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

# Clear all stashes
git stash clear

Common scenario: You are halfway through a feature when a critical production bug comes in. Stash your work, switch to main, fix the bug, then pop your stash to resume.

git bisect — Find the Commit That Introduced a Bug

Bisect uses binary search to efficiently find which commit introduced a problem. Instead of checking every commit, it halves the search space each time.

# Start the bisect session
git bisect start

# Mark the current commit as bad (the bug exists here)
git bisect bad

# Mark a known good commit (the bug did not exist here)
git bisect good abc1234

# Git checks out a commit halfway between good and bad
# Test the code, then tell Git the result:
git bisect good   # if this commit is fine
git bisect bad    # if this commit has the bug

# Repeat until Git identifies the exact commit
# Output: abc5678 is the first bad commit

# End the bisect session and return to your branch
git bisect reset

For automated bisecting, pass a test script: git bisect run npm test. Git will run the script at each step and use the exit code to determine good/bad.

git reflog — Your Safety Net

The reflog records every change to the tip of branches and HEAD in your local repository. It is the ultimate undo mechanism — even for operations that rewrite history.

# View the reflog
git reflog

# Example output:
# abc1234 HEAD@{0}: commit: Add payment integration
# def5678 HEAD@{1}: checkout: moving from main to feature/payments
# 789abcd HEAD@{2}: commit: Update README
# 012efgh HEAD@{3}: rebase finished: returning to refs/heads/main

# Recover a lost commit after a bad reset
git reflog
# Find the commit hash you want to recover
git checkout abc1234
# Or create a new branch from it
git branch recovery-branch abc1234

Lifesaver tip: If you accidentally run git reset --hard or a rebase goes wrong, your commits are not truly gone. Use git reflog to find the commit hash and restore it. Reflog entries are kept for 90 days by default.

5. Undoing Changes

Everyone makes mistakes. Git provides several ways to undo changes, each suited to a different situation.

git reset — Move HEAD and Optionally Change Files

Reset moves the branch pointer backward and can optionally modify the staging area and working directory.

# Soft reset: undo the commit but keep changes staged
git reset --soft HEAD~1

# Mixed reset (default): undo the commit and unstage changes
git reset HEAD~1

# Hard reset: undo the commit and discard all changes (DESTRUCTIVE)
git reset --hard HEAD~1

# Unstage a specific file (keep changes in working directory)
git reset HEAD src/app.js

# Reset to a specific commit
git reset --soft abc1234

When to use each mode:

git revert — Undo a Commit Safely

Unlike reset, revert creates a new commit that undoes the changes from a previous commit. This is the safe way to undo changes on shared branches because it does not rewrite history.

# Revert the most recent commit
git revert HEAD

# Revert a specific commit
git revert abc1234

# Revert without auto-committing (useful for reverting multiple commits)
git revert --no-commit abc1234
git revert --no-commit def5678
git commit -m "Revert broken payment feature"

# Revert a merge commit (specify which parent to keep)
git revert -m 1 abc1234

Rule of thumb: Use git revert on shared branches (main, develop) and git reset on your own local branches.

git restore — Discard Working Directory Changes

The modern replacement for git checkout -- when it comes to restoring files. Clear intent and hard to misuse.

# Discard changes to a specific file (restore from staging area)
git restore src/app.js

# Discard changes to all files
git restore .

# Unstage a file (move from staged back to modified)
git restore --staged src/app.js

# Restore a file from a specific commit
git restore --source=abc1234 src/app.js

6. Bonus Commands and Configuration

git diff — Compare Changes

See exactly what has changed in your working directory, staging area, or between commits.

# Show unstaged changes
git diff

# Show staged changes (ready to be committed)
git diff --staged

# Compare two branches
git diff main..feature/dashboard

# Compare a specific file between branches
git diff main..feature/dashboard -- src/app.js

# Show only file names that changed
git diff --name-only main..feature/dashboard

# Show a summary of changes (insertions/deletions per file)
git diff --stat

git tag — Mark Important Points

# Create a lightweight tag
git tag v1.0.0

# Create an annotated tag (recommended for releases)
git tag -a v1.0.0 -m "Release version 1.0.0"

# List all tags
git tag -l

# Push tags to remote
git push origin v1.0.0
git push --tags

Useful Git Aliases

Speed up your workflow by setting up aliases for commands you use frequently:

# Set up aliases
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.lg "log --oneline --graph --all"
git config --global alias.unstage "reset HEAD --"
git config --global alias.last "log -1 HEAD"

# Now you can use:
git co main         # instead of git checkout main
git st              # instead of git status
git lg              # pretty log graph

7. Tips and Best Practices

Knowing the commands is only half the battle. Here are practices that separate proficient Git users from the rest:

Commit Early and Often

Small, focused commits are easier to review, easier to revert, and easier to understand months later. Each commit should represent one logical change.

# Bad: one giant commit
git commit -m "Add user auth, fix bugs, update styles, refactor database"

# Good: separate, focused commits
git commit -m "Add user registration form"
git commit -m "Add password validation logic"
git commit -m "Add login endpoint with JWT tokens"
git commit -m "Add session management middleware"

Write Good Commit Messages

A well-structured commit message follows this format:

Short summary in imperative mood (max 72 chars)

Longer description if needed. Explain the "why" behind the change,
not the "what" (the diff shows the what). Wrap at 72 characters.

- Bullet points are fine
- Reference issue numbers: Fixes #123

Use .gitignore Effectively

Keep your repository clean by ignoring files that should not be tracked:

# .gitignore example for a Node.js project
node_modules/
dist/
.env
.env.local
*.log
.DS_Store
coverage/
.vscode/settings.json

Use gitignore.io to generate .gitignore templates for your tech stack.

Branching Strategy

Adopt a consistent branching convention across your team:

# Common branch naming patterns
feature/user-authentication
bugfix/login-redirect-loop
hotfix/payment-null-check
release/v2.1.0
chore/update-dependencies

Review Before You Push

Always check what you are about to push:

# See what commits will be pushed
git log origin/main..HEAD

# See a diff of what will be pushed
git diff origin/main..HEAD

# Do a dry run of the push
git push --dry-run

Keep Your Branches Up to Date

# Regularly sync your feature branch with main
git switch feature/my-feature
git fetch origin
git rebase origin/main

# Or if your team prefers merge
git merge origin/main

Quick Reference Summary

Command Purpose
git init Create a new repository
git clone Copy a remote repository
git add Stage changes for commit
git commit Save a snapshot
git status Check working tree state
git log View commit history
git branch Manage branches
git switch Change branches
git merge Combine branches
git fetch Download remote changes
git pull Fetch and merge
git push Upload local changes
git rebase Reapply commits on a new base
git cherry-pick Apply specific commits
git stash Shelve changes temporarily
git bisect Binary search for bugs
git reflog View reference log (safety net)
git reset Move HEAD / undo commits
git revert Undo a commit safely
git restore Discard working changes
git diff Compare changes
git tag Mark releases
git remote Manage remote connections
git checkout Switch branches (classic)
git config Configure Git settings

Conclusion

Git does not have to be intimidating. Start with the basics — init, add, commit, push, pull — and gradually add more commands to your repertoire. The advanced commands like rebase, bisect, and reflog become invaluable as your projects grow in complexity.

The most important thing is to practice. Create a test repository and experiment. Break things on purpose so you learn how to fix them. That is exactly how every experienced developer got comfortable with Git.

Related: Check out our Git Commands Cheat Sheet for a quick reference, and our Diff Checker tool.

Related Resources

Git Commands Cheat Sheet
Quick reference for essential Git commands
Diff Checker
Compare text changes side by side
Bash Shortcuts
Terminal shortcuts for faster Git workflow