A practical, no-fluff reference of Git commands for open-source contributors — from your first git clone to rescuing a broken history with reflog.
- Quick Reference
- Open Source Contributor Workflow
- Setup & Init
- Staging & Committing
- Branches
- Syncing with Remote
- Keeping Your Fork in Sync
- Merging & Rebasing
- Viewing History
- Stashing
- Tags
- Advanced Git Commands
- Undoing Mistakes
- Configuration & .gitignore
- Sources
| Goal | Command |
|---|---|
| Initialize a repo | git init |
| View remotes | git remote -v |
| List branches | git branch |
| Switch branches | git switch <branch> |
| Fetch changes | git fetch |
| Clone a repo | git clone <url> |
| Check status | git status |
| Stage all files | git add -A |
| Commit | git commit -m "message" |
| Push to remote | git push |
| Pull from remote | git pull |
| Create & switch branch | git checkout -b <branch> |
| Merge a branch | git merge <branch> |
| View history (compact) | git log --oneline |
| View visual branch graph | git log --oneline --graph --decorate --all |
| Undo last commit (keep changes) | git reset --soft HEAD~1 |
| Temporarily save work | git stash |
| See what changed | git diff |
| Search commit messages | git log --grep=<keyword> |
| Who last changed a line | git blame <file> |
The typical flow for contributing to someone else's open-source project on GitHub.
# 1. Fork the repository on GitHub, then clone your fork
git clone <your-fork-url>
cd <repo>
# 2. Add the original repository as "upstream"
git remote add upstream <original-repo-url>
# 3. Start a feature branch from the latest main
git switch main
git pull upstream main
git switch -c feature/my-fix
# 4. Make your changes, then stage and commit
git status
git add <files>
git commit -m "Add validation for empty usernames"
# 5. Update your branch with the latest upstream changes
git fetch upstream
git rebase upstream/main
# 6. Push your branch to your fork
git push -u origin feature/my-fix
# 7. Open a Pull Request from your fork to the original repository
# 8. If maintainers request changes, make the changes,
# then commit and push again
git add <files>
git commit -m "Address review feedback"
git push💡 If your fork has fallen behind, sync
mainbefore creating a new branch:git switch main git pull upstream main git push origin main
Set up Git and create or connect repositories.
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global core.editor "code --wait" # set VS Code as default editor
git config --global init.defaultBranch main # set default branch name to "main"On a new machine, set up SSH to authenticate with GitHub without using HTTPS credentials.
# 1. Generate an SSH key
ssh-keygen -t ed25519 -C "you@example.com"
# 2. Start the SSH agent
eval "$(ssh-agent -s)"
# 3. Add your SSH key to the agent
ssh-add ~/.ssh/id_ed25519
# 4. Copy your public key
cat ~/.ssh/id_ed25519.pub
# 5. Test the connection
ssh -T git@github.comAdd the public key to GitHub → Settings → SSH and GPG keys → New SSH key.
💡 Never share your private key (
~/.ssh/id_ed25519). Only add the public key (~/.ssh/id_ed25519.pub) to GitHub.
git config --list
git config --global --list # global only
git config user.name # check a specific keygit clone <url> [folder-name]
git clone --depth 1 <url> # shallow clone (latest snapshot only, faster)
git clone --branch <branch> <url> # clone a specific branchGit supports both SSH and HTTPS for remote URLs.
git init [folder]Omit folder to init in the current directory.
git remote add origin <url>
git push -u origin main
-usets the upstream branch, so later you can usegit pushdirectly.
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.lg "log --oneline --graph --decorate --all"
git config --global alias.undo "reset --soft HEAD~1"Stage changes and save them as commits.
- Working directory — files you're editing right now
- Stage (index) — files earmarked for the next commit
- Commit — a saved snapshot with a message and ID
- HEAD — pointer to the latest commit on the current branch
git status
git status -s # short/compact status view
git diff # unstaged changes vs HEAD
git diff --cached # staged changes vs HEAD
git diff HEAD # all changes vs HEAD
git diff <file> # diff for a specific file only
git diff <branch-A> <branch-B> # diff between branchesgit add <file> # stage a specific file
git add -A # stage everything (new, modified, deleted)
git add . # stage all changes in current directory
git add *.js # stage all JS files
git add -p <file> # interactively pick which chunks to stageInteractive staging prompts:
yyes ·nno ·ssplit ·eedit ·dskip rest of file
git commit -m "Short summary (imperative tense: Fix bug, not Fixed bug)"
git commit -a # stage all tracked files and commit in one step
git commit --allow-empty -m "Empty commit" # trigger CI/CD without code changesgit commit --amend -m "Corrected message"
git commit --amend -a # also include any new staged changes
git commit --amend --no-edit # amend without changing the message
⚠️ Don't amend commits already pushed to a shared branch.
[TYPE] Short summary (≤ 72 chars, imperative tense)
Optional longer explanation. What changed and why?
Wrap lines at 74 chars.
Types: [FEATURE] [BUGFIX] [TASK] [SECURITY]
Flags: [!!] breaking change [DB] schema change [CONF] config change
Ref #42
Create and manage branches for different changes.
git branch # list local branches
git branch -r # list remote branches
git branch -a # list all branches
git branch -v # show branches and latest commits
git branch -a --merged # show merged branches
git branch -a --no-merged # show unmerged branchesgit branch <name> # create a branch
git checkout <name> # switch to a branch
git checkout -b <name> # create and switch to a branch
git switch <name> # switch to a branch
git switch -c <name> # create and switch to a branch
git branch -m <old> <new> # rename a branchgit branch -d <name> # delete merged branch (safe)
git branch -D <name> # force-delete (even if unmerged) ⚠️
git push origin --delete <branch> # delete a remote branchgit checkout -t origin/<branch> # checkout + track remote branch
git branch --track <local> origin/<remote> # create tracked branch
git branch -u origin/<remote> # set upstream for current branchgit branch --contains <commit-id> # which branches include a commit
git branch --merged main # what's already in mainPush, pull, and fetch.
git remote -v # list configured remotes
git remote add <name> <url> # add a remote repository
git remote rename origin upstream # rename a remote
git remote remove <name> # remove a remote
git remote show origin # show remote details
git remote prune origin # remove stale remote-tracking branchesgit fetch [remote] # fetch changes without merging
git fetch --all # fetch from all remotes
git pull # fetch and merge changes
git pull --rebase # fetch and rebase changes
git pull origin <branch> # pull a specific branchgit push # push the current branch
git push -u origin <branch> # push and set the upstream branch
git push --all origin # push all local branches
git push --tags origin # push all tags
git push --force-with-lease # safely force-push if remote is unchanged
git push --force # force-push and overwrite remote history ⚠️💡 Prefer
--force-with-leasebecause it helps prevent overwriting newer remote changes.
Keep your fork up to date with the original repository.
git switch main
git fetch upstream
git pull upstream maingit switch main
git fetch upstream
git pull upstream main
git push origin maingit switch <branch>
git fetch upstream
git rebase upstream/maingit fetch upstream
git status
git log HEAD..upstream/main --oneline💡 Keep your fork's
mainbranch synchronized with the original repository before creating new branches.
Combine branches.
git merge <branch> # merge a branch into the current branch
git merge --no-ff <branch> # create a merge commit
git merge --squash <branch> # combine changes into one commit
git merge --abort # cancel the current merge# 1. Edit the conflicted files
# 2. Stage the resolved files
git add <resolved-file>
# 3. Complete the merge
git commit -m "Resolve merge conflicts"
# Or open a merge tool
git mergetoolgit rebase <branch> # replay commits on top of another branch
git rebase --abort # cancel the current rebase
git rebase --continue # continue after resolving a conflict
git rebase --skip # skip the current commit
git rebase -i HEAD~3 # edit the last 3 commits
git rebase -i <commit-id> # start an interactive rebaseInteractive rebase keywords:
pickkeep ·rewordedit message ·squashcombine ·fixupsquash silently ·editpause ·dropdelete
⚠️ Avoid rebasing commits already pushed to a shared branch.
If a rebase causes a conflict, Git pauses the rebase and marks the conflicted files.
# 1. Check which files have conflicts
git status
# 2. Resolve the conflicts in the files
# 3. Stage the resolved files
git add <resolved-file>
# 4. Continue the rebase
git rebase --continue💡 Repeat the process if Git reports another conflict.
⚠️ To cancel the rebase and return to the state before it started:git rebase --abort
git pull --rebaseView commits, changes, and repository history.
git log # show commit history
git log --oneline # show commits in one line
git log --oneline --graph --decorate --all # show the branch history graph
git log -n 10 # show the last 10 commits
git log -p <file> # show changes made to a file
git log --grep=<keyword> # search commit messages
git log --author="Name" # show commits by an author
git log --since="2 weeks ago" # show recent commits
git log --after="2024-01-01" # show commits after a date
git log <branch>.. --oneline # show commits in HEAD but not in <branch>
git log <branch-A>..<branch-B> # show commits in branch B but not A
git shortlog -sn # count commits by authorgit diff <branch-A> <branch-B> # compare two branches
git diff <branch> --name-status # list changed files
git diff <commit-A> <commit-B> # compare two commits
git show <commit-id> # show a commit and its changes
git show <commit-id>:<file> # show a file from a commit
git blame <file> # show who changed each line
git blame -L 10,25 <file> # show changes for specific lines
git branch --contains <commit-id> # show branches containing a commitgit reflog # show HEAD movement history
git reflog show <branch> # show movement history for a branchIf you lose a commit,
git reflogcan often help you recover it.
git grep "search term" # search files for a string
git log -S "search term" # find commits that added or removed a string
git log -G "regex pattern" # find commits matching a patternTemporarily save changes without committing them.
git stash # save tracked changes
git stash -u # also save untracked files
git stash push -m "optional message" # save changes with a message
git stash list # list saved stashes
git stash show stash@{0} # show stash contents
git stash show -p stash@{0} # show the full stash diff
git stash apply [stash@{0}] # restore a stash and keep it
git stash pop [stash@{0}] # restore a stash and remove it
git stash drop [stash@{0}] # delete a stash
git stash clear # delete all stashes
git stash branch <branch> stash@{0} # create a branch from a stash
stash@{0}is the newest stash. Higher numbers refer to older stashes.
Create and manage tags for specific commits and releases.
git tag -n # list tags with annotations
git tag -l "v1.*" # list tags matching a pattern
git tag <tag-name> # create a lightweight tag
git tag <tag-name> -m "<annotation>" # create an annotated tag
git tag <tag-name> <commit-id> # tag a specific commit
git tag -d <tag-name> # delete a local tag
git push origin <tag-name> # push a tag to the remote
git push --tags origin # push all tags to the remote
git push origin --delete <tag-name> # delete a remote tag
git show <tag-name> # show tag details💡 Use semantic versioning for releases, such as
v1.0.0,v1.2.3.
For more complex workflows.
git cherry-pick <commit-id> # apply one commit to the current branch
git cherry-pick <commit-A>..<commit-B> # apply a range of commits
git cherry-pick --no-commit <commit-id> # apply changes without committing
git cherry-pick --abort # cancel the cherry-pickgit bisect start
git bisect bad # mark the current commit as bad
git bisect good <commit-id> # mark a known working commit
# Test the commit Git selects, then mark it:
git bisect good # if the commit works
git bisect bad # if the commit is broken
git bisect reset # finish the bisectgit submodule add <url> [folder] # add another repository as a submodule
git submodule update --init --recursive # initialize submodules after cloning
git submodule update --remote # update submodulesgit worktree add <path> <branch> # create a worktree for a branch
git worktree list # list existing worktrees
git worktree remove <path> # remove a worktreegit clean -n # preview files that would be removed
git clean -f # remove untracked files ⚠️
git clean -fd # remove untracked files and directories ⚠️
git clean -fdx # also remove ignored files ⚠️git sparse-checkout init --cone
git sparse-checkout set <folder>Recover from errors safely.
git reset HEAD -- [file] # unstage files and keep the changes
git restore --staged <file> # unstage a file
git restore <file> # discard working directory changes ⚠️
git checkout -- [file] # older syntax for restoring a filegit reset --soft HEAD~1 # undo the last commit and keep changes staged
git reset HEAD~1 # undo the last commit and keep changes unstaged
git reset --hard HEAD~1 # undo the last commit and discard changes ⚠️
git reset --hard HEAD # discard all uncommitted changes ⚠️
git revert <commit-id> # create a new commit that reverses another commit
git revert HEAD # reverse the latest commitgit reset --hard <commit-id-before-merge> # remove an unpushed merge
git revert -m 1 <merge-commit-id> # reverse a pushed mergegit rm --cached <file>
git rm --cached -r <folder> # recursively (e.g. accidentally committed node_modules)Then add the file to
.gitignore.
git rm --cached <file>
echo "<file>" >> .gitignore
git add .gitignore
git commit -m "Remove <file> and add to gitignore"git reflog # find previous HEAD positions
git checkout <commit-id> # inspect a previous commit
git checkout -b recovery-branch <commit-id> # create a branch from a commit
git reset --hard <commit-id> # move the branch back to a commitFine-tune Git's behavior.
git config --local # apply settings to this repository
git config --global # apply settings to your user account
git config --system # apply settings to all usersgit config --global core.autocrlf input # handle line endings on Mac/Linux
git config --global core.autocrlf true # handle line endings on Windows
git config --global pull.rebase true # use rebase for pulls by default
git config --global push.default current # push the current branch by default
git config --global merge.conflictstyle diff3 # show the common ancestor in conflicts*.log # ignore all .log files
/build # ignore the top-level build folder
node_modules/ # ignore node_modules folders
!important.log # keep this file from being ignored💡 Use Git ignore templates for common languages and frameworks.
git rm --cached <file>Found a command that's missing, outdated, or explained badly? Contributions are welcome! See CONTRIBUTING.md for guidelines on adding new commands or fixing existing entries.
This project is licensed under the MIT License.