Git Cheatsheet: Commands, Tips and Tricks 📝#

This is compilation of useful git commands, tips and tricks I created for myself since I kept forgetting some commands related to configuring, searching and managing git repositories. The format is simple, just a list with short descriptions for some common, and other less common, commands which I often find myself looking up online.

For those looking for a more beginner friendly introduction to git, I’ve written one in a previous blog post. Feedback via social media or email is always welcome. You can subscribe to my newsletter by entering your email on the sidebar if you are interested in receiving updates whenever I write something new.

The Git Workflow#

../../_images/gitworkflow.png

Git Workflow#

http://files.zeroturnaround.com/pdf/zt_git_cheat_sheet.pdf

../../_images/gittimeworkflow.png

Git in Time Workflow#

https://patrickzahnd.ch

Configuration#

 1# show current values for all global configuration parameters 
 2git config --list --global
 3
 4# let git automatically correct typos such as "comit" and "pussh." 
 5git config --global help.autocorrect 1
 6
 7# set a username globally 
 8git config --global user.name "username"`
 9
10# set an email address globally
11git config --global user.email "email@provider.com"
12
13# always --prune for git fetch and git pull
14git config --global fetch.prune true
15
16# remove the previously set username globally
17git config --global --unset user.name
18
19# color the git console
20git config color.ui true
21
22# set the tool used by git for diffing globally
23git config --global diff.tool mytool
24
25# set the tool used by git for merging globally
26git config --global merge.tool mytool
27
28# remove the previously set configuration value globally
29git config --global --unset myparameter
30
31# allows populating the working directory sparsely, that is,
32# cloning only certain directories from a repository
33git config core.sparseCheckout true
34
35# instruct git to retrieve only some directory in addition to
36# those listed in `.git/info/sparse-checkout
37echo "some/directory/inside/the/repository" >> .git/info/sparse-checkout
38
39# define which whitespace problems git should recognize (any whitespace
40# at the end of a line, mixed spaces or tabs)
41git config --global core.whitespace trailing-space,space-before-tab
42
43# tells Git to detect renames. If set to any boolean value, it will
44# enable basic rename detection. If set to "copies" or "copy", it will
45# detect copies, as well.
46git config --global diff.renames copies
47
48# if set, git diff uses a prefix pair that is different from the standard "a/"
49# and "/b" depending on what is being compared.
50git config --global diff.mnemonicprefix true
51
52# always show a diffstat at the end of a merge
53git config --global merge.stat true
54
55# no CRLF to LF output conversion will be performed
56# especially important in the case of MacOS users
57git config --global core.autocrlf input
58
59# whenever pushing, also push local tags
60git config --global push.followTags true
61
62# show also individual files in untracked directories in status queries
63git config --global status.showUntrackedFiles all
64
65# always decorate the output of git log
66git config --global log.decorate full
67
68# the git stash show command without an option will show the stash in patch form
69git config --global stash.showPatch true
70
71# always set the upstream branch of the current branch as the branch to be
72# pushed to when no refspec is given
73git config --global push.default tracking
74
75# ignore the executable bit of files in the working tree
76git config core.fileMode false

Git .ignore Syntax#

file

match a particular file

.file

match a hidden file

directory/

match a directory

directory/directory/

match a subdirectory

directory/directory/*.extension

match all files with a certain extension in a subdirectory

directory/directory/**/*.extension

recursively match all files with a certain extension in a subdirectory

/*

match everything

!file

do not match file

Git Workflow#

Initialize and Clone#

 1# initialize a git repository in the current working directory
 2git init
 3
 4# clone a remote repository over https
 5git clone https://remote.com/repo.git
 6
 7# clone a remote repository over ssh
 8git clone ssh://git@remote.com:/repo.git
 9
10# recursively clone a repository over https
11git clone --recursive https://remote.com/repo.git
12
13# recursively clone a repository over ssh
14git clone --recursive ssh://git@remote.com:/repo.git

Track, Add and Commit#

 1# start tracking a file or add its current state to the index
 2git add file
 3
 4# add everything which is tracked and has been changed to the index
 5git add -u
 6
 7# add everything which is untracked or has been changed to the index
 8git add .
 9
10# commit to local history with a given message
11git commit -m "message"
12
13# add all changes to already tracked files and commit with a given 
14# message, non-tracked files are excluded
15git commit -am "message"
16
17# modify the last commit including both new modifications and given message
18git commit --amend -m "message"
19
20# perform a commit with an empty message
21git commit --allow-empty-message -m

Status and Diagnostics#

1git status			# show status of the working directory 
2git status -s			# show short version status
3
4git show HEAD			# show commit at the head of current branch 
5git show mycommit		# show commit with object ID mycommit 
6git show HEAD:folder/file	# show version of folder/file at HEAD

Checking Out#

 1# replace file-name with the latest version from the current branch
 2git checkout -- filename
 3
 4# in case fileorbranch is a file, replace fileorbranch with the latest version
 5# of the file on the current branch. In case fileorbranch is a branch, replace
 6# the working tree with the head of said branch.
 7git checkout fileorbranch
 8
 9# replace the current working tree with commit 05c5fa
10git checkout 05c5fa
11
12# replace the current working tree with the head of the main branch
13git checkout main

Remotes#

 1# show the remote branches and their associated urls
 2git remote -v 
 3
 4# adds an https url as remote branch under the name origin
 5git remote add -f origin https://remote.com/repo.git
 6
 7# adds an ssh url as remote branch under the name origin
 8git remote add -f origin ssh://git@remote.com:/repo.git
 9
10# remove the remote with ID origin
11git remote remove -f origin
12
13# set an https url for the remote with ID origin
14git remote set-url origin https://remote.com/repo.git
15
16# set an ssh url for the remote with ID origin
17git remote set-url origin ssh://git@remote.com:/repo.git
18
19# clean up remote non-existent branches
20git remote prune origin 
21
22# set the upstream branch, to which changes will be pushed, to origin/main
23git branch --set-upstream-to=origin/main
24
25# set foo as the tracking branch for origin/bar
26git branch –track foo origin/bar
27
28# update local tracking branches with changes from their respective remote ones
29git fetch
30
31# update local tracking branches and remove local references to
32# non-existent remote branches
33git fetch -p
34
35# delete remote tracking branch origin/branch
36git branch -r -d origin/branch
37
38# update local tracking branches and merge changes with local working directory
39git pull
40
41# given one or more existing commits, apply the change each one introduces,
42# recording a new commit for each. This requires your working tree to be clean 
43git cherry-pick commitid
44
45# push HEAD to the upstream url
46git push
47
48# push HEAD to the remote named origin
49git push origin 
50
51# push HEAD to the branch main on the remote origin 
52git push origin main
53
54# push and set origin main as upstream
55git push -u origin main
56
57# delete previous commits and push your current one
58# WARNING: never use force in repositories from which other have pulled [1]
59# https://stackoverflow.com/a/16702355
60git push --force all origin/main
61
62# a safer option to force-push that will not overwrite work on the remote
63# branch if more commits were added ensuring someone else's work is not overwritten
64git push --force-with-lease
65
66# turn the head of a branch into a commit in the currently checked out branch and merge it
67git merge --squash mybranch

Revert and Reset#

 1# figures out the changes introduced by commitid and introduces a new commit undoing them. 
 2git revert commitid
 3
 4# does the same but doesn't automatically commit
 5git revert -n commitid
 6
 7# updates the index and the HEAD to match the state of commit id. 
 8# changes made after this commit are moved to “not yet staged for commit” 
 9git reset commitid
10
11# sets only the HEAD to commitid
12git reset --soft commitid
13
14# sets the HEAD, index and working directory to commitid
15git reset --hard commitid
16
17# sets the HEAD, index and working directory to origin/main 
18git reset --hard origin/main

The Stash#

 1# take all changes made to working tree and stash them in a new dangling commit, putting 
 2# the working tree in a clean state
 3# DISCLAIMER: this does not include untracked files
 4git stash
 5
 6# stash everything into a dangling commit, including untracked files
 7stash save --include-untracked
 8
 9git stash push file # push individual or multiple files to the stash 
10
11# apply the changes which were last stashed to the current working tree
12git pop
13
14# show the stash of commits
15git stash list
16
17# apply a particular commit in the stash
18git stash apply
19
20# apply the second-to-last commit in the stash
21git stash apply stash@{2}
22
23# drop the second-to-last commit in the stash
24git stash drop stash@{2}
25
26# stash only the changes made to the working directory but keep the index unmodified
27git stash --keep-index
28
29# clear the stash
30git stash clear

Working with Submodules#

 1# add a submodule to a repository and clone it
 2git submodule add https://domain.com/user/repository.git submodules/repository
 3
 4# add "ignore = all" to .gitmodules in order to ignore all changes in submodules
 5cat <<eof >> .gitmodules
 6[submodule "mysubmodule"]
 7	path = myrepo
 8	url = git@gitlab.com:jdsalaro/myrepo.git
 9	ignore = all
10eof
11
12# while in a repository which contains submodules, they can be recursively
13# updated by issuing the following command
14git submodule init
15git submodule update
16
17# this an faster way of updating all submodules
18git submodule update --init --recursive
19
20# clone a repository which contains references to other repositories as submodules
21git clone --recursive 
22
23# remove completely a submodule
24submodule='mysubmodule';\
25git submodule deinit $submodule;\
26rm -rf .git/modules/$submodule;\
27git config --remove-section $submodule;\
28git rm --cached $submodule

Searching#

 1#list the latest tagged revision which includes a given commit
 2git name-rev --name-only commitid
 3
 4# find the branch containing a given commit
 5git branch --contains commitid
 6
 7# show commits which have been cherry-picked and applied to main already
 8git cherry -v main
 9
10# look for a regular expression in the log of the repository
11git show :/regex

Other Tips and Tricks#

ls-files and ls-tree#

 1# list files contained in the current HEAD or in the head of the
 2# main branch respectively
 3git ls-tree --full-tree -r HEAD
 4git ls-tree -r main --name-only
 5git ls-tree -r HEAD --name-only
 6
 7# list files with a certain extension
 8git ls-files '*.rb'
 9
10# list ignored files
11git ls-files -i

Diffing#

 1# diff two branches
 2git diff branch1..branch2
 3
 4# perform a word-diff instead of a line-diff
 5git diff --word-diff
 6
 7git diff --name-status main..branchname
 8git diff --stat --color main..branchname
 9git diff > changes.patch
10git apply -v changes.patch

Cleaning#

 1# perform a dry run and list untracked files or directories that
 2# would be removed without actually doing so
 3git clean -n
 4
 5#remove untracked files from the working tree
 6git clean -f
 7
 8# removes untracked files and directories
 9git clean -f -d 
10
11# same as above but also removes ignored files
12git clean -f -x -d 
13
14# same as above but does so through the entire repo
15git clean -fxd :/ 

Git log#

 1git whatchanged myfile
 2git log --after="MMM DD YYYY"
 3git log --pretty=oneline
 4git log --graph --oneline --decorate --all
 5git log --name-status
 6
 7git log --pretty=oneline --max-count=2
 8git log --pretty=oneline --since='5 minutes ago'
 9git log --pretty=oneline --until='5 minutes ago'
10git log --pretty=oneline --author=<name>
11git log --pretty=oneline --all
12
13git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
14
15git log --grep regexp1 --and --grep regexp2
16git log --grep regexp1 --grep regexp2
17git grep -e regexp1 --or -e regexp2

Set an SSH key for git access#

1ssh-keygen -t rsa -C "user@server.com"
2cat id_rsa.pub 
3
4#remote of the repository must point to ssh url
5git remote set-url origin ssh://git@server.com:/repo.git
6
7#don't forget to upload your public key to the respective server

Now the following can be put inside ~/.ssh/config.

1host server.com
2 HostName server.com
3 IdentityFile ~/.ssh/id_rsa_server
4 User git

List all dangling commits#

1git fsck --no-reflog | awk '/dangling commit/ {print $3}'

Leave the current commit as the only commit in the repository#

1git checkout --orphan new
2git add -A
3git commit -am "Initial commit"
4git branch -D main
5git branch -m main

Remove a file from the repository#

1git filter-branch -f --prune-empty --index-filter \
2  'git rm --cached -r -q -- . ; git reset -q $GIT_COMMIT -- myfile' -- --all

Set up a Git Repository using Git LFS#

In GitLab, projects you must enable git LFS as described in this HOWTO.

The following initializes, sets up a repository with an already created remote, configures git lfs for popular extensions and pushes everything as the repository’s initial version.

 1git init
 2git remote add origin git@domain.com:user/repository.git
 3
 4git lfs track '*.7zip'
 5git lfs track "*.avi"
 6git lfs track "*.bak"
 7git lfs track "*.bin"
 8git lfs track '*.bin'
 9git lfs track "*.bk"
10git lfs track "*.bmp"
11git lfs track "*.csv"
12git lfs track "*.dat"
13git lfs track "*.data"
14git lfs track "*.db"
15git lfs track "*.dll"
16git lfs track "*.doc"
17git lfs track "*.docx"
18git lfs track "*.exe"
19git lfs track "*.gif"
20git lfs track '*.gz'
21git lfs track "*.ico"
22git lfs track '*.iso'
23git lfs track "*.jar"
24git lfs track "*.jpg"
25git lfs track "*.list "
26git lfs track "*.mp4"
27git lfs track "*.mpg"
28git lfs track "*.msi"
29git lfs track "*.o"
30git lfs track "*.obj"
31git lfs track "*.odt"
32git lfs track "*.odp"
33git lfs track "*.pcap"
34git lfs track "*.pcapng"
35git lfs track "*.pdf"
36git lfs track "*.pickle"
37git lfs track "*.png"
38git lfs track "*.ppt"
39git lfs track "*.pptx"
40git lfs track "*.pyc"
41git lfs track '*.rar'
42git lfs track "*.tar"
43git lfs track '*.tar.gz'
44git lfs track "*.wmv"
45git lfs track "*.webp"
46git lfs track "*.xcf"
47git lfs track "*.xls"
48git lfs track "*.xlsx"
49git lfs track '*.zip'
50
51git add .
52git commit -m "Initial commit"
53git push -u origin main

Git Worktrees#

git worktree add --track -b local-branch wt-directory remote-branch
git worktree remove wt-directory

Git Navigation#

# determine the top-level path of the current repository
git rev-parse --show-toplevel 

# set up `git root` as global alias for navigation to a repository's top-level path
git config --global alias.root 'rev-parse --show-toplevel'

Change Author and Committer#

git filter-branch --force --env-filter '
    if [ "$GIT_COMMITTER_NAME" = "OLD_NAME" ];
    then
        GIT_COMMITTER_NAME="NEW_NAME";
        GIT_COMMITTER_EMAIL="NEW_EMAIL";
        GIT_AUTHOR_NAME="NEW_NAME";
        GIT_AUTHOR_EMAIL="NEW_EMAIL";
    fi' -- --all