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.
Attached to every commit you make. Use the same email as your GitHub account so commits link to your profile correctly.
New repositories will use main instead of master as the default branch name, matching GitHub's convention.
Prints your current Git configuration. Use to confirm identity is set correctly before making your first commit on a machine.
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.
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.
- Starting a brand new project from scratch
- Converting an existing folder to be version-controlled
- Setting up a local repository before connecting to GitHub
- A hidden .git/ directory with all tracking data
- An initial empty commit history
- A default branch (main if you configured it above)
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.
- 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
- Remote origin is automatically configured
- All remote branches are available via git branch -a
- Tracking is set up: git pull works immediately
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.
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.
- M = Modified (tracked file changed)
- A = Added (newly staged file)
- ?? = Untracked (new file, not yet added)
- D = Deleted
- Run before git add to see what changed
- Run before git commit to confirm staging
- Run after git pull to see what arrived
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.
- 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
- 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
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.
- -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
- 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
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.
- --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
- Add git config --global alias.lg "log --oneline --graph --all" to your config
- Then type git lg for the visual branch graph every time
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.
- 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 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.
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.
- 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 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
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.
- 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
- 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
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.
- 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
- 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
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.
- 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
- 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.
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.
- 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
- Run git config --global pull.rebase true once
- Every git pull will rebase automatically, producing cleaner history across the team
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.
- 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
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).
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.
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.
- 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)
- 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
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.
- --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
- 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
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.
- 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.
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.
- 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
- 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
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.
- 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
- 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
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.
- 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
- 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.
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.
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:
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
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
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.
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.
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.
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.
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.
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.
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.