Quickly look up commands based on what you want to do! Caveat: this list is grotesquely incomplete! See your man pages for more info!
In this reference section we use the following substitutions:
URL
: Some URL either SSH, HTTP, or even a local file, usually the URL you cloned from.FILE
: Path to file, e.g. foo/bar.txt
, etc.DIR
: Path to directory, e.g. foo/
, etc.PATH
: Path to directory or fileBRANCH
: Some branch name, e.g. main
, etc.REMOTE
: A remote name, e.g. origin
, upstream
, etc.HASH
: Some commit hash—you can get a commit hash from git log
or git reflog
.CMMT
: a commit hash, branch, etc. Anything that refers to a commit. Officially this is called a tree-ish, but that was more letters than I wanted to repeatedly type.VARIABLE
: a Git config variable name, usually words separated by periods.VALUE
: an arbitrary value for Git configs.TAG
: a tag nameAlso, don’t type the $
—it’s the shell prompt. And everything after a #
is a comment. A backslash \
at the end of a line indicates that it continues on the next line.
HEAD
: the commit that is currently checked out/switched to.main
: a common the first branch created.master
: another common name for the first branch created.origin
: the default name for the remote from which this repo was cloned.upstream
: the conventional name of the remote that you forked from. Not set up automatically.HEAD
.git add FILE
git rm --cached FILE
git add FILE
git restore FILE
(discards changes)git rm --cached FILE
git commit FILE
(finalize commit)git restore --staged FILE
(unstage)git checkout --merged FILE
(during merge)For all git config
commands, specify --global
for a universal setting or leave it off to set the value just for this repo.
$ git config set VARIABLE VALUE
$ git config get VARIABLE
$ git config list
$ git config unset VARIABLE $ git config --edit
Username and email:
$ git config set --global user.name "Your Name" $ git config set --global user.email "your-email@example.com"
SSH identity:
$ git config set core.sshCommand \ "ssh -i ~/.ssh/id_alterego_ed25519 -F none"
This is the first branch created when you make a new repo.
$ git config set --global init.defaultBranch BRANCH
Common names are main
, master
, trunk
, and development
. This guide uses main
.
$ git config set --global pull.rebase false # Merge $ git config set --global pull.rebase true # Rebase
Set the default editor to Vim, and the default mergetool and difftool to Vimdiff, turn off prompting for the tools, and turn off mergetool backups:
$ git config set core.editor vim
$ git config set diff.tool vimdiff
$ git config set difftool.prompt false
$ git config set difftool.vimdiff.cmd 'vimdiff "$LOCAL" "$REMOTE"'
$ git config set merge.tool=vimdiff
$ git config set mergetool.vimdiff.cmd \
'vimdiff "$LOCAL" "$REMOTE" "$MERGED"' $ git config --global set mergetool.keepBackup false
$ git config set color.ui true # Or false
Autocorrect will automatically run the command it thinks you meant. For example, if you git poush
, it will assume you meant git push
.
$ git config set help.autocorrect 0 # Ask "Did you mean...?"
$ git config set help.autocorrect 7 # Wait 0.7 seconds before run
$ git config set help.autocorrect immediate # Just guess and go
$ git config set help.autocorrect prompt # Prompt then go $ git config set help.autocorrect never # Turn autocorrect off
Handle automatic newline translation. Recommend set to true for Windows (not WSL) and false everywhere else.
$ git config set core.autocrlf true # Windows (non-WSL) $ git config set core.autocrlf false # WSL, Linux, Mac, C64, etc.
Obsolete commands for older versions:
git config user.email # Get
git config user.email "user@example.com" # Set
git config --unset user.email # Delete
git config --list # List git config --edit # Edit
Setting aliases, some examples:
$ git config set --global alias.logn 'log --name-only'
$ git config set alias.aa "add --all"
$ git config set alias.logc "log --oneline --graph --decorate"
$ git config set alias.diffs "diff --staged"
$ git config set alias.lol "log --graph"\
" --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s"\ " %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
Getting aliases:
$ git config get alias.logx
$ git config get --all --show-names --regexp '^alias\.'
$ git config set alias.aliases \ "config get --all --show-names --regexp '^alias\.'"
Tracing an alias run:
$ GIT_TRACE=1 git logx
$ git clone URL # Clone a URL
$ git clone URL DIR # Clone a URL to directory
$ git init DIR # Init repo at directory $ git init . # Init repo in the current directory
$ git add PATH # Add PATH to the repo
$ git mv FILE1 FILE2 # Rename ("Move") FILE1 to FILE2
$ git mv FILE2 FILE1 # Undo the above rename
$ git rm FILE # Delete ("Remove") FILE
$ git add -p FILE # Add file in patch mode
$ git commit # Commit files on stage $ git commit -m "message" # Commit with a message
Amending commits—don’t amend commits you have pushed unless you know what you’re getting into!
$ git commit --amend # Amend last commit
$ git commit --amend -m "message" # Amend with commit message $ git commit --amend --no-edit # Don't change commit message
To undelete a staged file, run these two commands in sequence:
$ git restore --staged FILE $ git restore FILE
To undelete a deleted file, you could manually recover it from an old commit, or revert the commit that deleted it.
$ git status # Show current file states
$ git log # Show the commit logs
$ git log --name-only # Also list changed files
$ git log CMMT # Show log from a specific branch
$ git log CMMT1 CMMT2 # Show logs from multiple branches
$ git log CMMT1..CMMT2 # Show logs from CMMT2 since it
# diverged from CMMT1
$ git log CMMT1...CMMT2 # Show logs from CMMT1 and CMMT2 # since they diverged
$ git diff # Diffs between working tree and stage
$ git diff HEAD^ # Diff from the previous commit to here
$ git diff HEAD^^ # Diff from the 2nd last commit to here
$ git diff HEAD~3 HEAD~2 # Diff from 3rd last to 2nd last commit
$ git diff CMMT # Diff between CMMT and now
$ git diff CMMT1 CMMT2 # Diff between two commits (older first)
$ git diff CMMT1...CMMT2 # Diff between CMMT2 and the common
# ancestor of CMMT1 and CMMT2
$ git diff HEAD~3^! # Diff between HEAD~3 and its parent
$ git diff -- FILE # Run a diff just for a specific file
$ git diff HEAD^ -- FILE # Run a diff just for a specific file
$ git diff -U5 # Show 5 lines of context
$ git diff -w # Ignore whitespace
$ git diff --name-only # Only show filenames of changed files
$ git diff --staged # Diffs between stage and repo $ git difftool # Diffs using the configured difftool
A local branch looks like branchname
. A remote tracking branch looks like remote/branchname
.
$ git switch BRANCH # Switch to a branch
$ git switch --detach HASH # Detach HEAD to a commit $ git switch - # Switch back to previous commit
$ git switch --detach HEAD^ # Switch to previous commit
$ git switch --detach HEAD^^ # Switch to 2 commit ago
$ git switch --detach HEAD~3 # Switch to 3 commits ago $ git switch --detach HEAD~99 # Switch to 99 commits ago
$ git switch main # Reattach HEAD to main
$ git branch -v # List all branches $ git branch -va # List all including remote tracking branches
$ git switch -c BRANCH # Create and switch to BRANCH
$ git branch BRANCH # Create BRANCH at HEAD $ git branch BRANCH1 BRANCH2 # Create BRANCH1 at BRANCH2
$ git branch -d BRANCH # Delete fully merged branch $ git branch -D BRANCH # Force delete unmerged branch
Obsolete style (use switch
if you can):
$ git checkout CMMT # Detach HEAD to a commit
$ git checkout HEAD^ # Detach HEAD to previous commit $ git checkout HEAD~2 # Detach HEAD to second previous commit
$ git pull # Pull from remote and merge or rebase
$ git pull --ff-only # Only allow fast-forward merges
$ git pull --rebase # Force a rebase on pull $ git pull --no-rebase # Force a merge on pull
$ git push # Push this branch to its remote
$ git push REMOTE BRANCH # Create remote tracking branch and
# push to remote
$ git push -u REMOTE BRANCH # Create remote tracking branch and
# push to remote, and use subsequent
# `git push` commands for this local
# branch
$ git push -u origin branch99 # Example
$ git push --tags # Push all tags to origin
$ git push REMOTE --tags # Push all tags to specific remote $ git push REMOTE tag3.14 # Push single tag
$ git fetch # Get data from remote but don't merge or rebase $ git fetch REMOTE # Same, for a specific remote
$ git merge CMMT # Merge commit or branch into HEAD
$ git merge --abort # Rollback the current merge
$ git mergetool # Run mergetool to resolve a conflict
$ git checkout --merged FILE # Unstage resolved files
In a conflict occurs, you can always --abort
. Otherwise:
$ git remote -v # List remotes
$ git remote set-url REMOTE URL # Change remote's URL
$ git remote add REMOTE URL # Add a new remote
$ git remote rename REMOTE1 REMOTE2 # Rename REMOTE1 to REMOTE2 $ git remote remove REMOTE # Delete REMOTE
Add a .gitignore
file to your repo. It applies to this directory and all non-submodule subdirectories below it. Add descriptions of files to ignore to this file. Comments behind #
are allowed. Blank lines are ignored.
Example .gitignore
:
foo.aux # Ignore specific file "foo.aux"
foo.* # Ignore all files that start with "foo."
*.tmp # Ignore all files that end with ".tmp"
frotz/ # Ignore all files in the "frotz" directory
foo[12].txt # Ignore "foo1.txt" and "foo2.txt"
foo? # Ignore "foo" followed by any single character
frotz/bar # Ignore file "bar" in directory "frotz" * # Ignore everything
Exceptions to earlier rules, also useful in .gitignore
files in subdirectories to override rules from parent directories:
*.txt # Ignore all text files !keep.txt # Except "keep.txt"
$ git rebase CMMT # Rebase changes onto commit
$ git rebase -i CMMT # Interactive rebase (squashing commits)
$ git rebase --continue # Continue processing from conflict
$ git rebase --skip # Skip a conflicting commit $ git rebase --abort # Bail out of rebasing
$ git pull --rebase # Force a rebase on pull
$ git pull --no-rebase # Force a merge on pull
Stashes are stored on a stack.
$ git stash push # Stash changed files
$ git stash # Effectively the same as "push"
$ git stash FILE # Stash a specific file
$ git stash pop # Replay stashed files on working tree
$ git stash list # List stashed files
$ git stash pop 'stash@{1}' # Pop stash at index 1
$ git stash pop --index 1 # Same thing
$ git stash drop 'stash@{1}' # Drop stash at index 1 $ git stash drop --index 1 # Same thing
$ git revert CMMT # Revert a specific commit
$ git revert -n CMMT # Revert but don't commit (yet)
$ git revert CMMT1 CMMT2 # Revert multiple commits
$ git revert CMMT1^..CMMT2 # Revert a range (oldeest first)
$ git revert --continue # Continue processing from conflict
$ git revert --skip # Skip a conflicting commit $ git revert --abort # Bail out of reverting
All resets move HEAD
and the current checked out branch to the specified commit.
$ git reset --mixed CMMT # Set stage to CMMT, don't change WT
$ git reset CMMT # Same as --mixed
$ git reset --soft CMMT # Don't change stage or working tree
$ git reset --hard CMMT # Set stage and WT to CMMT
$ git reset -p CMMT # Reset file in patch mode
Obsolete usage:
$ git reset FILE # Same as "git restore --staged FILE"
$ git reflog # Look at the reflog
…I admit this section could use a bit more information.
$ git cherry-pick CMMT # Cherry-pick a particular commit
$ git blame FILE # Who is responsible for each line $ git blame --date=short FILE # Same, shorter date format
$ git clone --recurse-submodules URL # Clone with submodules
$ git submodule update --recursive --init # If you cloned without
$ git submodule add URL # Add submodule
$ git add DIR # Add to repo
$ git pull --recurse-submodules # Pull including submodules
$ git submodule status # Submodule status
$ git ls-tree HEAD DIR # Show submod pinned commit
$ git submodule init # Set up bookeeeping
$ git submodule update # Bring in missing submods $ git submodule update --recursive # Handle submods of submods
Deleting a submodule—do these in order. In this example, DIR is the name of the submodule directory.
$ git submodule deinit DIR
$ rm -rf .git/modules/DIR
$ git config -f .gitmodules --remove-section submodule.DIR
$ git add .gitmodules
$ git rm --cached DIR $ git commit -m "remove DIR submodule"
$ git tag # List tags $ git tag -l # List tags
$ git tag TAG # Create a tag on HEAD
$ git tag TAG CMMT # Create a tag on a specific commit
$ git tag -a TAG # Create an annotated tag
$ git tag -a TAG CMMT # On a specific commit $ git tag -a TAG -m "message" # Add a message to the tag
$ git push --tags # Push all tags to origin
$ git push REMOTE --tags # Push all tags to specific remote $ git push REMOTE tag3.14 # Push specific tag
$ git tag -d tagname # Delete a tag locally $ git push REMOTE -d tagname # Delete a tag on a remote
$ git worktree list # List worktrees
$ git worktree add DIR CMMT # Add worktree at CMMT
$ git worktree add --detach DIR CMMT # Add, detach head at CMMT
$ git worktree add DIR HASH # Add, detach head at HASH
$ git worktree remove DIR # Remove worktree $ git worktree remove --force DIR # Remove, lose uncommitted mods
https://learn.microsoft.com/en-us/windows/wsl/↩︎
https://beej.us/guide/bggit/↩︎
https://github.com/↩︎
https://gitlab.com↩︎
https://docs.gitea.com/↩︎
https://learn.microsoft.com/en-us/windows/wsl/↩︎
We’re glazing over an important topic here that we’ll come back to later called remote tracking branches.↩︎
And again we’re doing some hand-waving. There are actually three branches. Two of them, main
and origin/main
are on your local clone. And there’s a third main
on the remote origin
that your origin/main
is tracking. Feel free to ignore this detail until we get to the remote tracking branch chapter.↩︎
https://www.vim.org/↩︎
https://github.com/↩︎
https://cli.github.com/↩︎
https://github.com/cli/cli#installation↩︎
https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent↩︎
https://www.baeldung.com/linux/ssh-private-key-git-command↩︎
https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens↩︎
https://en.wikipedia.org/wiki/Hexadecimal↩︎
I’m stretching it a bit, here. HEAD
looks at the commit you’ve switched to. This might not be quite the same as what’s in your project subdirectory if you’ve modified some of the files since the moving HEAD
to that commit. The commit is a snapshot, but that snapshot doesn’t include modifications to files until you make another commit that contains them.↩︎
https://learngitbranching.js.org/↩︎
All Git enthusiasts collectively, that is.↩︎
https://github.com/github/gitignore↩︎
The --staged
flag is more modern. Older versions of Git used git diff --cached
.↩︎
A dot-env (.env
) file is one that contains secret information (like login credentials) that is never committed to the repo. It’s usually in the .gitignore
just to be sure. Someone related to the project will use a side channel to tell you what to put in it so you can authenticate properly. Support for .env
files exists for a variety of languages and frameworks. I’m using my serious voice because this is a serious matter!↩︎
https://tinyurl.com/y5kzpeyk↩︎
https://git-scm.com/book/en/v2/Git-Tools-Rerere↩︎
https://en.wikipedia.org/wiki/Stack_(abstract_data_type)↩︎
https://github.com/beejjorgensen/git-example-repo↩︎
https://en.wikipedia.org/wiki/XZ_Utils_backdoor↩︎
https://en.wikipedia.org/wiki/Social_Security_number#SSNs_used_in_advertising↩︎
https://support.github.com/request?q=pull+request+removals↩︎
Git cleans up “unreachable” commits after some time has elapsed, so they won’t be instantly destroyed. But they’re on borrowed time unless you create a new branch to hold them.↩︎
Who knows what it really does under the hood, but we’re going to use this as a mental model for how things work.↩︎
You can leave off the --mixed
since it’s the default.↩︎
No guarantees. You shouldn’t rewrite commit history that is already public!! It makes a big mess!↩︎
Perhaps using git reflog
and git cherry-pick
or git cherry-pick -n
and potentially git add -p
, all of which are covered in later chapters. Along with judicious use of rebase, old commits or parts of old commits can be applied while keeping the commit history clean.↩︎
https://en.wikipedia.org/wiki/Battle_of_Mobile_Bay#“Damn_the_torpedoes”↩︎
By default it’s 90 days. You can configure this with the gc.reflogExpire
config option.↩︎
Keeping in mind to never rewrite history on anything you’ve pushed, of course.↩︎
Even if the changes were identical, the commit hash would still be different because the hash takes all kinds of other metadata into account.↩︎
https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work↩︎
https://git-scm.com/docs/git-config#_variables↩︎
https://git-scm.com/docs/git-config#_conditional_includes↩︎
https://git-scm.com/docs/git-config#_deprecated_modes↩︎
https://git-scm.com/docs/git-log#_pretty_formats↩︎
https://superuser.com/questions/232373/how-to-tell-git-which-private-key-to-use↩︎
https://www.gnupg.org/↩︎
Setting the PATH
is outside the scope of this tutorial, but the short of it is if you can run the diff tool command on the command line (e.g. by running vimdiff
), then it is in the PATH
. If it says command not found
or some such, then it is not in the PATH
. Search the Intertubes for how to add something to the PATH
in Bash. Or set the Git path config explicitly, as shown in the following paragraph.↩︎
https://www.araxis.com/merge/index.en↩︎
https://www.scootersoftware.com/↩︎
https://sourcegear.com/diffmerge/↩︎
https://kdiff3.sourceforge.net/↩︎
https://apps.kde.org/kompare/↩︎
https://meldmerge.org/↩︎
https://www.perforce.com/products/helix-core-apps/merge-diff-tool-p4merge↩︎
https://www.vim.org/↩︎
https://winmerge.org/?lang=en↩︎
https://www.araxis.com/merge/index.en↩︎
https://www.scootersoftware.com/↩︎
https://www.devart.com/codecompare/↩︎
https://invent.kde.org/sdk/kdiff3↩︎
https://meldmerge.org/↩︎
https://www.perforce.com/products/helix-core-apps/merge-diff-tool-p4merge↩︎
https://www.vim.org/↩︎
https://winmerge.org/↩︎
https://www.vim.org/↩︎
https://vimhelp.org/index.txt.html#normal-index↩︎
https://openvim.com/↩︎
https://chatgpt.com/↩︎
https://git-scm.com/book/en/v2↩︎
https://git-scm.com/docs↩︎
https://shafiul.github.io/gitbook/index.html↩︎
https://wizardzines.com/git-cheat-sheet.pdf↩︎
https://ohshitgit.com/↩︎
https://learngitbranching.js.org/↩︎
https://github.com/apps/github-learning-lab↩︎
https://www.codecademy.com/learn/learn-git↩︎
https://www.atlassian.com/git/tutorials↩︎
https://gitimmersion.com/↩︎
https://github.com/pluralsight/git-internals-pdf?tab=readme-ov-file↩︎
http://sethrobertson.github.io/GitPostProduction/gpp.html↩︎
https://wizardzines.com/zines/git/↩︎
https://wizardzines.com/zines/oh-shit-git/↩︎
https://www.manning.com/books/learn-git-in-a-month-of-lunches↩︎
https://www.amazon.com/Introducing-GitHub-Non-Technical-Peter-Bell/dp/1491949740↩︎
https://www.amazon.com/Git-Pocket-Guide-Working-Introduction/dp/1449325866↩︎
https://www.amazon.com/Version-Control-Git-collaborative-development/dp/1449316387↩︎
https://www.amazon.com/Git-Practice-Techniques-Mike-McQuaid/dp/1617291978↩︎
https://www.amazon.com/Git-Teams-User-Centered-Efficient-Workflows/dp/1491911182↩︎
https://pragprog.com/titles/tsgit/pragmatic-version-control-using-git/↩︎
https://www.amazon.com/Mastering-Git-proficiency-productivity-collaboration/dp/1783553758↩︎