Git Guide — Branching, Merging & Undo
What this covers
The everyday git operations that come up in normal development: configuring the repo, staging and committing, branching and merging, syncing with a remote, inspecting history, stashing work in progress, and — the part worth getting right — undoing changes safely. For running parallel branches in separate working directories, see Git Worktrees ; for the Gitflow branching model, see Git Flow .
When to use it
- Looking up the right command for a common git operation
- Deciding how to undo something (uncommitted changes, a bad commit, a pushed commit) without losing work
- Choosing between merge and rebase for integrating a branch
- Recovering from a mistake (detached HEAD, wrong branch, accidental reset) — see Troubleshooting
Setup & Configuration
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git init # new repo in current directory
git clone <url> # copy an existing repo
git clone <url> <dir> # into a named directory
Staging & Committing
git status # what's changed / staged
git add <file> # stage a specific file
git add -p # stage selectively, hunk by hunk
git add . # stage everything under the current directory
git commit -m "message" # commit staged changes
git commit -am "message" # stage tracked files + commit (skips new files)
git commit --amend # rewrite the last commit (only if not pushed)
git add -p is worth the habit — it lets you review and split changes into
focused commits instead of committing unrelated work together.
Branching & Merging
git branch # list local branches
git switch <branch> # change branch (modern; replaces checkout)
git switch -c <branch> # create and switch
git branch -d <branch> # delete a merged branch
git branch -D <branch> # force-delete an unmerged branch
git merge <branch> # merge <branch> into the current one
git merge --no-ff <branch> # always create a merge commit
Merge vs rebase:
git mergepreserves history exactly and creates a merge commit — safe default, especially for shared branches.git rebase <base>replays your commits on top of<base>for a linear history — cleaner, but rewrites commit hashes.- Never rebase commits that others have already pulled. Rewriting shared history forces everyone else into conflicts.
git rebase main # replay current branch on top of main
git rebase -i HEAD~3 # interactively squash/reword/reorder last 3 commits
Rebase step by step
The most common use: bring new commits from main into your feature branch without a merge commit.
git switch feature/login
git fetch origin
git rebase origin/main # replay the branch's commits on top of current main
# on a conflict: edit the files, then
git add <file>
git rebase --continue # move on to the next commit
git rebase --abort # or go back to the state before the rebase
git push --force-with-lease # the branch was already pushed → hashes changed
A rebase resolves conflicts commit by commit, so the same file can come up more
than once. When that gets tedious, git merge origin/main is an equally valid and cheaper way.
Remotes & Syncing
git remote -v # list remotes
git remote add origin <url> # add a remote
git fetch # download remote changes, don't merge
git pull # fetch + merge into current branch
git pull --rebase # fetch + rebase (avoids a merge commit)
git push # push current branch
git push -u origin <branch> # push and set upstream (first time)
git push --force-with-lease # force-push safely — refuses if remote moved unexpectedly
Prefer --force-with-lease over --force: it still refuses to overwrite work
someone else pushed after your last fetch, so you don’t clobber a teammate’s commits.
Inspecting History
git log --oneline --graph --all # compact visual history of all branches
git log -p <file> # history with diffs for one file
git show <commit> # a commit's message and diff
git diff # unstaged changes
git diff --staged # staged changes
git blame <file> # who last changed each line
Stashing
git stash # shelve uncommitted changes, clean the tree
git stash -u # include untracked files
git stash list # list stashes
git stash pop # reapply the most recent stash and drop it
git stash apply # reapply but keep it in the stash list
git stash drop # discard a stash
Stashing is for a quick context switch. For anything longer-lived, a branch (or a worktree ) is safer — stashes are easy to forget and have no branch name to remind you what they held.
Undoing Changes
The right command depends on how far the change has gone. From least to most drastic:
# Discard unstaged changes to a file (irreversible — the edits are gone)
git restore <file>
# Unstage a file but keep the edits
git restore --staged <file>
# Undo the last commit, KEEP the changes staged
git reset --soft HEAD~1
# Undo the last commit, keep changes in the working tree (unstaged)
git reset HEAD~1
# Undo the last commit AND discard its changes (destructive)
git reset --hard HEAD~1
# Revert a commit by making a new commit that undoes it (safe for shared history)
git revert <commit>
Rules of thumb:
- Not pushed yet →
resetis fine. - Already pushed / shared → use
revert, notreset --hard+ force-push. git reset --hardandgit restorediscard uncommitted work permanently — there’s no undo. Commit or stash first if unsure.
Common mistakes
git add .without looking. Logs,.envor build output ride along into the commit. Checkgit statusandgit diff --stagedbefore committing; what does not belong in the repository goes into.gitignore.git push --forceto a shared branch. It overwrites commits someone else pushed in the meantime. Use--force-with-lease, and only on your own branch.- Rebasing a shared branch. Teammates’ history diverges and they merge the old commits back in. Rebase only what is still yours alone.
git reset --hardwith work in progress. Uncommitted changes are gone and the reflog cannot bring them back. Commit orgit stashfirst.- One big “WIP” commit. It cannot be reverted in parts or reviewed sensibly. Split with
git add -pinto commits that each make sense on their own. git pullinto a dirty tree. The merge gets mixed up with your edits. Commit or stash before pulling.- Forgotten stashes. A month later
git stash listtells nobody anything. For more than a few minutes, create a branch.
Troubleshooting
| Symptom | Fix |
|---|---|
| Committed to the wrong branch | git switch <right-branch> then git cherry-pick <commit>; remove it from the wrong branch with git reset --hard HEAD~1 (if unpushed) |
| Detached HEAD (“not on a branch”) | git switch -c <new-branch> to keep the commits, or git switch main to discard them |
| Need a “deleted” commit back | git reflog lists recent HEAD positions; git reset --hard <sha> or git switch -c rescue <sha> |
Accidental reset --hard | The commits usually still exist — find them with git reflog and recover |
| Merge conflict | Edit the marked files, git add them, then git commit (or git merge --continue); git merge --abort to back out |
| Pushed a secret | Rotate the secret immediately — history rewriting doesn’t undo the exposure |
Frequently asked questions
What is the difference between git merge and git rebase?
git merge joins two branches with a merge commit and leaves history untouched. git rebase replays your commits on top of another branch, which keeps history linear but gives the commits new hashes. Rebase is fine on your own unshared branch; integrate shared branches with merge.
How do I undo the last commit in git?
If the commit is not pushed yet, git reset --soft HEAD~1 removes it and keeps the changes staged. If it is already on the remote, use git revert <commit> — it adds a new commit that undoes the change and does not rewrite shared history.
What is git stash for?
git stash shelves work in progress and gives you a clean working tree, for example when you need to switch branches quickly. git stash pop brings it back. For anything longer-lived, a branch or a worktree is safer.
How do I delete a branch locally and on the remote?
Locally git branch -d <branch> (-D for an unmerged branch), on the remote git push origin --delete <branch>. git fetch --prune cleans up stale references to deleted remote branches.
What should I do when git pull ends in a conflict?
Open the files marked with <<<<<<<, pick the right version, save, git add them and finish with git commit (git rebase --continue during a rebase). You can always back out: git merge --abort or git rebase --abort restores the state before the pull.