กลับไปที่บทความ
Git Engineering Tools Productivity Workflow

Basic Git Commands Every Software Engineer Should Know

พลากร วรมงคล
17 เมษายน 2569 12 นาที

“The thirty-something git commands you actually use day-to-day — staging, branching, merging, rebasing, recovering from mistakes, and the small set of aliases worth setting up once.”

Most engineers learn git by accident — copy a snippet from Stack Overflow, run it, watch what happens. That works for the first few weeks. Then someone force-pushes over your branch, or a merge conflict resolution drops half a feature, and the gaps in your mental model start charging interest.

This post is the floor. Not every git command — just the ones you actually reach for in a normal week, organised by what you’re trying to do. The deeper bits (custom merge drivers, sparse checkouts, sub-trees) you can learn when you need them; you don’t need them yet.

TL;DR

  • Git is a content-addressable store + a small graph of commits + three “places” (working tree, index, HEAD).
  • Stop guessing at staging — git status and git diff are your two most-used commands by an order of magnitude.
  • Branching is cheap, recovering is cheap, history is permanent. git reflog will save you more than any tutorial.
  • Use rebase -i to clean up your own branch before merging; never to rewrite shared history.
  • Force-push only with --force-with-lease. Plain --force is how teams lose work.
  • Set up a handful of aliases (co, st, lg) once; the time saved compounds.

The Mental Model in Three Pieces

Before any commands, three concepts that explain almost everything else.

ConceptWhat it really isWhere it lives
Working treeThe files on disk you can edit.Your folder.
Index (a.k.a. “staging area”)A next commit being assembled. Files added to it are queued for the next git commit..git/index
HEADA pointer to the current branch tip (which is itself a pointer to a commit)..git/HEAD

Almost every command moves data between these three places. git add moves working tree → index. git commit moves index → new commit, advances HEAD. git restore moves backwards. Once you see this, the commands stop feeling random.

Setup and Inspect

The first commands of every project, and the ones you run literally every day.

CommandWhat it does
git config --global user.name "Your Name"Set your name (once per machine).
git config --global user.email you@example.comSet your email (must match GitHub for verified commits).
git config --global init.defaultBranch mainNew repos start on main instead of master.
git config --global pull.rebase truegit pull rebases by default — fewer noisy merge commits.
git config --global push.autoSetupRemote trueFirst push to a new branch auto-creates the upstream tracking.
git statusWhat’s changed, staged, untracked. Run constantly.
git diffUnstaged changes.
git diff --staged (or --cached)Staged changes.
git log --oneline -10Last 10 commits, one line each.
git show <hash>A specific commit’s diff + metadata.
# Worth doing once on a fresh machine
git config --global core.editor "code --wait"   # or vim, helix, etc.
git config --global core.autocrlf input          # macOS/Linux
git config --global core.autocrlf true           # Windows
git config --global rerere.enabled true          # remember conflict resolutions

git status and git diff are far and away the commands you’ll run most. Don’t try to remember what you changed — ask git.

Stage and Commit

Most “I broke git” stories start here.

CommandWhat it does
git add <path>Stage a specific file or directory.
git add -pPatch-add — pick which hunks to stage. Best command in the entire toolkit.
git add -uStage all modifications and deletions of tracked files (skip untracked).
git commit -m "msg"Commit staged changes with a one-line message.
git commitOpen editor for a multi-line commit message — the right way for non-trivial changes.
git commit --amendReplace the last commit (squash in new staged changes, fix the message).
git restore <path>Discard unstaged changes in <path>.
git restore --staged <path>Unstage <path> (keep the working-tree changes).
# The pattern most engineers want most of the time:
git add -p                  # interactively stage just the relevant hunks
git status                  # confirm what's about to commit
git commit                  # write a real message

git add -p is the single most under-used command. It teaches you to commit one logical change at a time, which makes review easier and history cleaner.

git commit --amend is fine only before you’ve pushed. Once a commit is on the remote, amending it rewrites history — which everyone else now has to deal with.

Branching

Branches are cheap. Make them.

CommandWhat it does
git branchList local branches; current one starred.
git branch -aList local + remote branches.
git switch <branch>Switch to an existing branch.
git switch -c <branch>Create AND switch to a new branch.
git switch -Switch to the previously-checked-out branch (like cd -).
git branch -d <branch>Delete a merged branch (safe).
git branch -D <branch>Delete a branch even if not merged (dangerous, useful).
git branch -m <new-name>Rename current branch.

Old commands you’ll see in tutorials: git checkout -b feature/x and git checkout main. They still work, but git switch (for branches) and git restore (for files) split the overloaded checkout into two clear verbs. Prefer them.

# The everyday branch dance
git switch main
git pull                         # update local main
git switch -c feature/login-fix  # branch off the latest
# ...work, commit, push...
git push                         # autoSetupRemote handles the upstream

Merging vs Rebasing

The two ways to integrate one branch into another. Both have their place.

git mergegit rebase
What it doesCombines two branches with a merge commit recording the join.Replays your branch’s commits on top of another.
History shapeShows the actual history (branched, merged).Linear, as if your work always sat on top of the latest.
Best forIntegrating completed features back into shared branches.Cleaning up your own branch before sharing it.
ConflictsResolved once, in a single commit.Potentially resolved per replayed commit.
Rule of thumbUse on shared/protected branches.Use on your private branch only — never rewrite shared history.
# Merge a feature branch into main
git switch main
git pull
git merge --no-ff feature/login-fix    # explicit merge commit; clear in history

# Rebase YOUR branch onto the latest main (keeps history linear)
git switch feature/login-fix
git fetch origin
git rebase origin/main
# resolve conflicts as they appear, then:
git rebase --continue

If a rebase goes sideways, git rebase --abort puts everything back exactly how it was. Stop using --continue when you’re confused; abort, breathe, try again.

Working with Remotes

Where your code goes to be other people’s problem.

CommandWhat it does
git clone <url>Copy a remote repo locally.
git remote -vList configured remotes.
git fetchDownload new commits/branches from remote without merging.
git pullgit fetch + git merge (or rebase, if you set pull.rebase).
git pushSend your local commits to the upstream branch.
git push -u origin <branch>First push of a new branch — sets upstream tracking.
git push --force-with-leaseForce-push, but only if the remote hasn’t moved since you last fetched.

Never use git push --force on a shared branch. It overwrites whatever is there, including commits a teammate just pushed. --force-with-lease is the safety-belted version: it will refuse if the remote tip isn’t what you expect. Use it.

# A safe force-push after rebase
git fetch origin
git rebase origin/main
git push --force-with-lease

Recovering From Mistakes

Almost any git mistake is recoverable as long as the commits weren’t garbage-collected (which takes ~30 days). The trick is git reflog.

CommandWhat it does
git reflogLog of every move HEAD has made — including the commits “lost” by reset/rebase.
git reset --hard <hash>Set current branch (and working tree) to a specific commit. Destructive — discards uncommitted work.
git reset --soft <hash>Move branch tip; keep working tree and index intact.
git reset --mixed <hash> (default)Move branch tip; keep working tree, unstage everything.
git revert <hash>Create a new commit that undoes <hash>. Safe on shared history.
git stashTuck away uncommitted changes; clean working tree.
git stash popRe-apply (and remove) the most recent stash.
git stash listSee all stashes.
git restore --source=<hash> <path>Pull a specific file’s contents from any commit.
# I just rebased and lost everything!
git reflog                              # find the commit hash before the rebase
git reset --hard HEAD@{12}              # restore — your branch is back

# I committed a bug; I need to revert without rewriting history
git revert <bad-commit-hash>
git push

# I'm in the middle of a feature and need to switch branches NOW
git stash
git switch hotfix-branch
# ...later...
git switch -
git stash pop

reset --hard is the most dangerous command in normal use. Always check git status first to confirm you have nothing uncommitted you care about — and remember reflog will still save you for ~30 days even if you forget.

History — Who Did What, When, Why

CommandWhat it does
git log --oneline --graph --allVisualise all branches as a graph. Set this up as an alias.
git log -p <path>Full diff of every commit that touched <path>.
git log -S "function_name"All commits that added/removed a string. Pickaxe — invaluable.
git log --grep="bug"Commits whose message contains “bug”.
git log --author="Alice"Commits by Alice.
git log --since="2 weeks ago"Commits in the last 2 weeks.
git blame <file>Who last touched each line, and when.
git bisect startBegin a binary search across history for the commit that introduced a bug.
git show <hash>:<path>Print a file’s contents at a specific commit, without checking it out.
# Find when a function was deleted
git log -S "calculateInvoice" -- src/billing.ts

# Find the commit that introduced a bug
git bisect start
git bisect bad                  # current HEAD is broken
git bisect good v1.4.2          # this older version works
# git checks out the midpoint; you test, then mark good or bad
git bisect good
git bisect bad
# ...repeat...
git bisect reset                # done; back to where you started

Bisect is magical when the bug appeared “sometime in the last two weeks” and you don’t know where. Even with hundreds of commits, you’ll find it in ≤ log₂(N) steps.

Cleanup — Before You Open the PR

Don’t open a PR with 23 commits called “wip”, “fix”, “actually fix”, “lint”. Clean up first.

CommandWhat it does
git rebase -i HEAD~5Interactive rebase of the last 5 commits — squash, reorder, reword, drop.
git commit --fixup=<hash>Make a commit marked as a fixup of an earlier commit.
git rebase -i --autosquash <base>Rebase that auto-squashes --fixup commits into their targets.
git clean -fdDelete untracked files and directories. Irreversible — check with -n (dry run) first.
git clean -fdxSame, plus ignored files (e.g., node_modules, .env).
# Clean up your branch before requesting review
git rebase -i origin/main         # interactive: squash, reword, reorder
git push --force-with-lease

# The fixup workflow (clean as you go)
git commit -m "Add login form"
# ...later, fixing a bug in the same logical change...
git commit --fixup=HEAD~2
git rebase -i --autosquash HEAD~3

git rebase -i --autosquash is the workflow most professional engineers use: keep messy commits as you work, then squash them into clean logical units before opening the PR.

Things You Should Set Up Once

A handful of aliases pay back the time investment within a week.

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.sw switch
git config --global alias.br branch
git config --global alias.cm "commit -m"
git config --global alias.last "log -1 HEAD"
git config --global alias.lg "log --oneline --graph --decorate --all"
git config --global alias.unstage "reset HEAD --"
git config --global alias.amend "commit --amend --no-edit"
git config --global alias.fixup "commit --fixup"
git config --global alias.rb-onto "rebase -i --autosquash"

Now git st, git lg, git fixup HEAD~2, etc.

Two more global settings that earn their keep:

git config --global rerere.enabled true   # remember conflict resolutions; replay them next time
git config --global merge.conflictStyle zdiff3   # 3-way conflict markers — much easier to read

Workflow Cheat Sheet — “I want to…”

I want to…Command
Start a fresh featuregit switch main && git pull && git switch -c feature/x
See what changedgit status then git diff (and git diff --staged)
Commit just one logical changegit add -p then git commit
Pull in main without a merge bubblegit pull --rebase (or set pull.rebase=true)
Update my feature branch with maingit fetch && git rebase origin/main
Undo my last commit but keep the changesgit reset --soft HEAD~1
Throw away local work and match origin exactlygit fetch && git reset --hard origin/<branch>
Recover work I think I “lost”git reflog then git reset --hard HEAD@{n}
Save WIP and switch branchesgit stash && git switch other-branch
Find when line X was addedgit log -S "X" -- path/to/file
Find who last touched a linegit blame path/to/file
Find the commit that broke thingsgit bisect start
Squash 5 messy commits into 1 clean onegit rebase -i HEAD~5
Force-push safely after rebasegit push --force-with-lease
Revert a bug commit on shared historygit revert <hash>
Delete an old merged branchgit branch -d <branch> then git push origin --delete <branch>

Things You Should Almost Never Do

CommandWhy
git push --forceUse --force-with-lease instead. Plain --force overwrites teammates’ work.
git reset --hard without git status firstDiscards uncommitted changes. Always look first.
git rebase on a shared branchRewrites public history; everyone else has to recover. Rebase your own branch only.
git clean -fd without -n firstDeletes files that aren’t in any commit. Always do a dry run.
git commit --amend after pushingSame as rebasing public history — forces teammates to clean up.
git checkout .Deletes uncommitted work in tracked files with no warning. Prefer git restore (clearer verb) and look at git status first.

Closing Checklist

When you sit down to work on a branch:

  • git status — know the starting state
  • Branch off the latest: git switch main && git pull && git switch -c feature/x
  • Stage with git add -p to commit one logical change at a time
  • git commit with a real message (subject + body, “why” not “what”)
  • Before opening the PR: git rebase -i origin/main, squash messy commits, force-with-lease
  • On any reset/rebase: git reflog is your safety net for ~30 days
  • Delete merged branches locally and on the remote when done

If anything ever goes sideways: git status, then git reflog, then walk back. You almost never need to nuke a clone.

Further Reading

  • Pro Git (Chacon & Straub) — free at git-scm.com/book. The canonical reference; chapters 2–7 cover everything in this post in more depth.
  • git help <command> and git help --all — the official docs are surprisingly readable.
  • Atlassian’s git tutorials — good visualisations for merge vs rebase.
  • oh-shit-git.com — concise recovery recipes for every “I broke it” situation.
  • Learn Git Branching — interactive sandbox for visualising what each command actually does to the commit graph.

Git’s reputation for being hard comes mostly from learning it command-by-command instead of model-first. Hold the working tree → index → HEAD picture in your head, run git status constantly, trust git reflog to bail you out — and the rest is just vocabulary.

Comments powered by Giscus are not yet configured. Set PUBLIC_GISCUS_REPO_ID and PUBLIC_GISCUS_CATEGORY_ID in apps/web/.env to enable.

PV

เขียนโดย พลากร วรมงคล

Software Engineer Specialist ประสบการณ์กว่า 20 ปี เขียนเกี่ยวกับ Architecture, Performance และการสร้างระบบ Production

เพิ่มเติมเกี่ยวกับผม

บทความที่เกี่ยวข้อง