~/ git for developers

Essential git commands
for your daily workflow.

A concise reference for developers. Copy-paste ready examples, an interactive playground, and a branch switch tool.

# git commands

git status - show working tree state

Shows the state of the working directory and the staging area: which files are modified, which are staged for the next commit, and which are untracked. The go-to command before any git add or git commit.

bash
# clean working tree
$ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
# with staged + unstaged + untracked files
$ git status
On branch feature/login
Your branch is ahead of 'origin/feature/login' by 1 commit.
Changes to be committed:
new file: auth.js
Changes not staged for commit:
modified: index.js
Untracked files:
.env.local
$ git status -s # short format
A auth.js
M index.js
?? .env.local

In the short format (-s), the first column is the staging area, the second is the working tree. A = added, M = modified, D = deleted, ?? = untracked.

git pull - fetch and merge from remote

Fetches changes from the remote and merges them into the current branch. It is shorthand for git fetch followed by git merge.

bash
$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
Unpacking objects: 100% (3/3), done.
Updating 3a1b2c4..7d8e9f0
Fast-forward
README.md | 2 ++
1 file changed, 2 insertions(+)
$ git pull origin main # explicit remote + branch
$ git pull --rebase # rebase instead of merge

Use --rebase to keep a linear history instead of creating a merge commit. Good practice for feature branches that diverged from main.

git push - upload commits to remote

Pushes local commits to the remote repository. The first push of a new branch requires setting the upstream tracking reference with -u.

bash
$ git push
Enumerating objects: 5, done.
Writing objects: 100% (3/3), 312 bytes, done.
To git@github.com:user/repo.git
3a1b2c4..7d8e9f0 main -> main
$ git push -u origin feature/my-branch # first push of new branch
$ git push --force-with-lease # safe force push

Prefer --force-with-lease over --force. It aborts if someone else has pushed in the meantime, preventing accidental overwrites.

git commit - save staged changes to history

Records staged changes as a new commit in the repository history. Always stage files first with git add unless using -a for already-tracked files.

bash
$ git add index.js
$ git commit -m "Add user authentication endpoint"
[main 7d8e9f0] Add user authentication endpoint
1 file changed, 24 insertions(+), 2 deletions(-)
$ git commit -am "Fix typo in README" # stage tracked files + commit
$ git commit --amend --no-edit # add to last commit

--amend rewrites the last commit — only use it before pushing. If already pushed, amending requires a force push.

git checkout - switch branch or restore files

Switches between branches or restores files from a commit. Git 2.23+ introduced the split commands git switch (for branches) and git restore (for files).

bash
$ git checkout main # switch to branch
Switched to branch 'main'
$ git checkout -b feature/dark-mode # create + switch
Switched to a new branch 'feature/dark-mode'
$ git checkout -- style.css # discard file changes
$ git switch main # modern alternative
$ git switch -c feature/dark-mode # modern create + switch

git switch and git restore do the same things but with clearer intent. New scripts should prefer them over the overloaded git checkout.

git revert - undo a commit non-destructively

Creates a new commit that undoes the changes from a previous commit. Non-destructive — it does not rewrite history, making it safe for shared branches.

bash
$ git log --oneline -4
7d8e9f0 Add user authentication endpoint
3a1b2c4 Update README with API docs
b5c6d7e Initial commit
$ git revert 7d8e9f0 # undo a specific commit
[main a8b9c0d] Revert "Add user authentication endpoint"
1 file changed, 2 insertions(+), 24 deletions(-)
$ git revert HEAD # undo the last commit
$ git revert HEAD --no-edit # skip editor prompt

Unlike git reset, revert preserves history and is safe to use on branches others are working on. Use git reset only for local, unpushed commits.

git merge - merge remote branch into current branch

To integrate changes from a remote branch (e.g. a colleague's feature branch), fetch the latest remote state and then merge the target branch.

bash
# approach 1: fetch then merge
$ git fetch origin
From github.com:user/repo
* [new branch] feature/dark-mode -> origin/feature/dark-mode
$ git merge origin/feature/dark-mode
Merge made by the 'ort' strategy.
style.css | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
# approach 2: pull directly from remote branch
$ git pull origin feature/dark-mode
# rebase onto remote branch instead of merge
$ git fetch origin && git rebase origin/feature/dark-mode

Merge preserves branch history with a merge commit. Rebase produces a linear history but rewrites commits — avoid rebasing shared branches.

git stash - temporarily save uncommitted changes

Temporarily saves uncommitted changes so you can switch context without losing work. Stashes are stored in a stack — you can have multiple stashes and apply them selectively.

bash
$ git stash # save current changes
Saved working directory and index state WIP on main: 7d8e9f0
$ git stash push -m "half-done login form" # with label
$ git stash list
stash@{0}: On main: half-done login form
stash@{1}: WIP on main: 7d8e9f0 Add user authentication
$ git stash pop # apply + remove last stash
On branch main
Changes not staged for commit:
modified: login.js
$ git stash apply stash@{1} # apply without removing
$ git stash drop stash@{0} # delete a stash

git stash pop = apply + delete. Use git stash apply if you want to keep the stash for other branches. Untracked files are not stashed unless you add -u.

Git Playground →
Try git commands without a real repo
Branch Switch Tool →
Interactive fzf branch switcher