Git Cheatsheet

Commands

Notes:

  • origin is the default remote, you can always specify another remote name
  • ^: "parent of", e.g. git show HEAD^ means "show me the parent commit of HEAD". If followed by a number, is the n-parent, so useful for merge commits only
  • ~: "first parent of", e.g. git show HEAD~3 means "show me the parent of the parent of the parent of HEAD"
  • {x}: History position, to be used from commands like reflog
  • ..: range specification: e.g. git log A..B shows changes in B not yet in A
  • ...: exclusion range specification: commits that are in either of the references but not in both
  • --not: exclude commits not reachable by a reference, e.g. git log A B --not C shows changes either at A or B, but not present in C

Branch Management

  • git checkout -b <new-branch-name> : get a new branch and switch to it
  • git checkout -b <branch-name> : get a local copy of a remote existing branch
  • git checkout <branch-name> : switch to an existing branch
  • git checkout --orphan <branch-name> : create branch without full history
  • git fetch: update your local repo reference list (list of branches and latest commits they point to)
  • git pull origin <branch-name>: update commits info from a branch
  • git pull --rebase: Pull changes, and if both your local and the remote had changes, rewrite local history by placing server commits before, and yours after them. Does not add a merge commit
  • git merge --no-ff <source-branch> : merge into current branch source one without rebase/FF. This will create a new merge commit
  • git push origin <destination-branch> : push merges/changesets to a branch
  • git push origin <local-branch>:<remote-branch> : push merges/changesets to a different branch than local
  • git push origin <destination-branch> --force-with-lease : push merges/changesets to a branch. If remote had different commits not in local, fails and does not push. Safer than --force which would always override remote commits
  • git remote -v: show all remotes configured in the repository
  • git remote show origin : display the path of the repository
  • git remote set-url origin xxx : Change remote URI to xxx
  • git remote rm <name> / git remote remove <name> : Remove a remote
  • git remote rename <old> <new>: Rename a remote (just the name, not the URI)
  • git remote add origin git@github.com:<path>.git: replace the origin remote URI. Need to firstgit remote rm origin to remove the previous one
  • git remote add <user> git@github.com:<user>/<path>: add <user> fork to a repository as a remote. When pushing changes afterwards to that remote, Github will propose you to create a pull request directly from the fork remote towards the original remote
  • git pull <user>: fetch branches from remote <user>
  • git add xxx : add files (use . for everything, <folder>/.. for folder recursive children)
  • git add -i: interactive staging; allows to decide what to do with each local change (often with the patch option)
  • git commit : commit changes
  • git status : show status of uncommited files
  • git checkout <filename> : revert a file
  • git checkout <branch-name> <filename> : Checkout all changes to <filename> from branch <branch-name> into current
  • git checkout <revision> . : revert a full branch to specified revision if not commited
  • git revert <commit1> <commit2> ...: Reverts certain commits if commited, by creating a new commit that undoes the changes. Note that you can also do a git reset or git rebase -i, if your changes are not yet pushed/merged. Be careful with reset and rebase, as you will need to force push, and that's always dangerous.
  • git reset <revision-hash> .: revert a full branch to specified revision if commited
  • git reset --soft HEAD~1: reset to last commit (even if pushed). Changes will be staged, and can be committed again, but if pushed before, you need to push with --force-with-lease
  • git reset --hard HEAD^: forcibly resets to the branch's head, discarding any commit
  • git reset --hard origin/<branch-name>: forcibly resets your local branch to the remote version. Helpful when gets easy to just discard any local operations and revert to the old state
  • git reset --hard ORIG_HEAD: Just did a rebase and now regretting it? Then this command undoes it. Only valid if exactly the previous operation was the rebase.
  • git reset HEAD <file>: reset <file> to the version in HEAD
  • git reset --soft <new-root-hash> && git commit --amend -m "<new message>" && git push --force: squash all branch pushed commits previous to the one specified into a single commit with the desired new message
  • git reset 'HEAD@{1}': revert to the previous command's commit. Check the reflog to see which command was last. This is very useful to undo non-trivial commands like a git reset HEAD~1
  • git reset HEAD@<commit>: revert to a specific commit (often from the reflog)
  • git clean -n: dry run to check which uncommited modifications would be removed
  • git clean -fd : remove all local uncommited modifications
  • git restore --staged .: unstage all staged changes. Combined with git checkout . and git clean -fd, leaves your local checkout "clean"
  • git branch : display local branches, active one is with an *
  • git branch -vv: display additional information. Remember state is only regarding local git repo, not remote(s). Reflects state since last fetch
  • git branch --sort=-committerdate # DESC: display local branches, ordered by last commit date
  • git branch -r: list all remote branches
  • git diff : Show changes in files
  • git diff <branch-name> origin/<remote-branch-name> : show a diff between a local branch and a remote one from the origin remote
  • git --no-pager diff --name-only master...HEAD: list all modified files in commits from current branch in regards to master up to your last pull
  • git diff > <filename>: Save a diff patch file. To apply it, use git patch <filename>.
  • git diff | pbcopy: Copy the diff into the clipboard.
  • git rebase <branch-name> : rebases current branch with specified branch (fetches remote branch changes and then adds your changes at the tip)
  • git rebase -i <branch-name>: rebases current branch with specified branch, allowing you to reorder changes
  • git rebase -i HEAD~3: allows you to check and reorder, mix or remove the last 3 commits from the current branch. Very useful to rewrite local commits, squash some of them, etcetera, before preparing a branch for review
  • git rebase --onto <A-branch> <B-branch> <C-branch>: Rebase commits from C not present at B, into A; assuming that B was branched from A, and C was branched from B ("skip the intermediary").
  • git rm <filename> : delete a file from branch and filesystem
  • git branch -d <branch-name> : delete a local branch
  • git push origin --delete <branch-name> : delete a remote brach
  • gitk <filename> : show visual git log
  • git submodule update --init --recursive : Init and update all submodules
  • git submodule foreach --recursive git reset --hard: Reset all submodule changes
  • Switch from current branch having a submodule to a branch without it:
rm -Rf <submodule-folder>
git reset && git checkout 
git checkout <branch-name>
  • git pull https://github.com/<username>/<repository-name>.git <branch-name>: Merge a pull request to local branch
  • git checkout --theirs xxxx git checkout --ours xxxx: Keep changes from incoming branch or local one, respectively
  • git cherry-pick <commit>: merges and commits a specific commit to current branch. Note that SHA of the commit will change (as the date changes)
  • git commit --amend: Squash a change on previous commit and change the commit message
  • git commit --amend --author='Kartones <your@email.com>' --no-edit: Change the author of the last commit (note that the email is enclosed inside <>)
  • git rebase -i HEAD~3 -x "git commit --amend --author='Kartones <your@email.com>' --no-edit": Change the author of the last 3 commits
  • git diff --staged: Normal git diff shows only unstaged changes, with --staged you can see staged changes that will be committed
  • Undo a commit removing it from history:
git reset --hard HEAD~1
# your new commit goes here
# option a:
git push origin <branch> --force-with-lease
# option b: (more dangerous, see below)
git push origin <branch> --force
  • When pushing, --force will destroy any commit added by others to the remote. --force-with-lease tries to keep all upstream commits, and if it can't (because there is a conflict), it will abort and notify you.
  • Tag any commit of a repo (e.g. with a certain version):
git tag <label> <commit-id>
git push origin <label>
  • Apply a .diff file: git apply <filename>.diff
  • Push to a different remote branch: git push origin <localb-ranch>:<remote-branch>
  • Rename a branch: git branch -m <old-name> <new-name>
  • git commit --no-verify ...: With great power comes great responsibility. This flag disables all commit hooks, so use it only when really in need
  • git fetch origin master && git rebase origin/master && git push origin HEAD --force-with-lease: Rebase master with any new remote change, and then push, but not forcing if there is any kind conflict with the push. Also works if there's any amended commit

Repository management

  • git clone <repository-uri> <folder-name>: Clone a repository with a different folder name instead of the default repository name.

Stashing

  • git stash: Stash current changes
  • git stash -u: Stash also the untracked changes
  • git stash apply: Unstash and merge stored changes
  • git stash list: check all stashes

Logs, Search

  • git log -n X -p : show X last commits with diffs
  • git log <branch-A-name> ^<branch-B-name> : commits in branch A that aren't in branch B
  • git log -- <path>: filter to commits including modifications to <path>/file
  • git log --pretty=oneline --stat --all <folder-name> : same as previous, but better presented
  • git log --format='%H' -1 HEAD: Get last commit hash from current branch
  • git log --pretty=format:"%h %s" --graph: format the commits log as nice graph with hash & commit message
  • git rev-list origin/master...master: list all commits that exist on local master but not on origin/master
  • git ls-remote git@xxxx.git | grep <commit-sha>: search if a commit exists upstream
  • git reflog: show a history of changes in all branches
  • git show HEAD@{3}: show commit details (easiest way to combine with reflog)
  • git log origin/<branch-name>..<branch-name> : Show diff between remote commits and local commits
  • git log <branch-name>..origin/<branch-name> : Show diff between local commits and remote commits
  • git blame -M: Blames original commit, not the move commit
  • git blame -CCC: Looks at all commits in history
  • git log -S <string>: find all commits that modified occurrences of <string>, e.g. git log -S myFunction (+ info)
  • git log --grep="<string>": search for a string in git logs
  • git log -G <regex>: Search in which commits the regular expression matches
  • git tag -l "v1.*": Searching for tags supports wildcard via the -l option
  • git grep: Search patterns in tracked files (+ info)
  • git merge-base <branch-A> <branch-B>: Find common ancestor commit between two branches
  • git diff --name-only HEAD~2 HEAD: Show a list of modified files from the last 2 commits

Configuration

  • git config --list : List currently setup config values
  • git config --global push.default simple: Makes git refuse a push if the upstream branch's name is different from the local branch's name, to prevent accidental pushes to the wrong branch.
  • git config --global user.name "<username>" : Setup global user name to <username>
  • git config --global user.email "<email>" : Setup global user email to <email>
  • git config --global credential.helper 'cache --timeout=28800' : Make git cache credentials for 8 hours
  • git config --global color.ui true : Activate colors in diffs, etc
  • git config --global core.autocrlf input : Fix Convert newlines to Unix-style ones (Unix)*
  • git config --global core.autocrlf true : Fix Convert newlines to Unix-style ones (Windows)
  • git config --global core.excludesfile ~/.gitignore : Instruct git to always ignore patterns defined at ~/.gitignore
  • git config --global pager.log 'diff-highlight | less': Better diff highlighting (same for 3 following options)
  • git config --global pager.show 'diff-highlight | less'
  • git config --global pager.diff 'diff-highlight | less'
  • git config --global interactive.diffFilter diff-highlight
  • git config --global core.editor <editor-name>: change default editor (for commit messages, etcetera)

Advanced Branch & Repository Management

  • git grafting: Technique to pick part of a git repository and add it to another repository.
  • git-filter-repo: A tool to rewrite a repository's history. Also used when merging multiple repositories into one. Destructive operation, allows to rewrite almost everything.

Tools

  • git -C <path> <command> ...: Run git commands as if current folder was <path>
  • When you push changes, the format of the push output's last line is: <oldref>..<newref> <from-branch> -> <to-branch>
  • cli: GitHub’s official command line tool. Augment with the following (third party):
    • git extras: Many tools, but I use it just because I can see branches ordered by last activity via git brv
    • poi: Tool to delete merged branches (checks PRs) via gh poi
  • Show the current git branch at prompt in Linux (add to ~/.bashrc):
parse_git_branch() {
    git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\w\[\033[32m\]\$(parse_git_branch)\[\033[00m\] $ "

# user not shown in normal prompt, so I like to show it when starting a session
echo "User: $USER"
  • Show the current git branch at prompt in macOS (add to ~/.zshrc):
parse_git_branch() {
    git symbolic-ref --quiet --short HEAD 2> /dev/null | sed -e 's/\(.*\)/ %F{240}(%f%F{39}\1%f%F{240})%f/'
}

autoload -Uz add-zsh-hook

function update_prompt() {
    local git_branch=$(parse_git_branch)
    PROMPT='%~'"$git_branch"' $ '
}

add-zsh-hook chpwd update_prompt

zshaddhistory() {
  if [[ $1 == git\ checkout* ]] || [[ $1 == gh\ pr\ checkout* ]]; then
    NEEDS_PROMPT_UPDATE=true
  fi
  return 0  # Ensure the function returns 0 so the history system works correctly
}

precmd() {
  if [[ $NEEDS_PROMPT_UPDATE == true ]]; then
    update_prompt
    NEEDS_PROMPT_UPDATE=false
  fi
}

# user not shown in normal prompt, so I like to show it when starting a session
echo "User: $USER          "

update_prompt

# disable zsh git branch name autocompletion
# uncomment if you have autocompletions installed, else not needed
# compdef -d git
  • Show the current git branch at prompt in macOS, more complete solution: Pure (fork)

Third-party Tools

  • tig: to navigate commits & branches
  • Github:
    • gh: Official GitHub CLI tool. Very powerful if you interact with github.com. Example usage to fetch and checkout a branch: gh pr checkout 123.
    • Commands for automatically closing tickets when merged to default branch
    • Search to display all issues/PRs of an organization: https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+org%3Athemotion+sort%3Aupdated-desc
    • Additionally, filter to things assigned to me or involving me: https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+org%3Athemotion+sort%3Aupdated-desc+involves%3Akartones

Tutorials

xkcd: Git https://xkcd.com/1597/Git conceptual model - https://blog.acolyer.org/2016/10/24/whats-wrong-with-git-a-conceptual-design-analysis/

  • official documentation + free book (really good!)
  • Spark Code Hub Git Tutorial: Compact and direct to the point, covers all the relevant parts
  • Mental model: Git keeps three trees (note this is local, not factoring in remotes):
    • HEAD: Branch status, last commit reference
    • Stage: staged files that will be sent to HEAD
    • Working directory: local file-system files (unstaged and/or untracked content)
  • Commit messages should be in imperative form, e.g. "Fix bug"
  • 9 useful tricks of git branch
  • Most Common Git tips & tricks (awesome list)
  • Syncing a fork (first configure remote upstream)
  • supersedes: when a branch replaces a previous branch that has been closed without merging.
  • My preferred approach to working with forks:
    1. Clone the main upstream, not your fork: git clone git@github.com:<user-or-org>/<repo>.git
    2. Sync and update submodules: git submodule sync --recursive; git submodule update --init --recursive
    3. Setup your fork as another upstream: git remote add $USER git@github.com:<your-github-user>/<repo>.git
    4. Disable pushes to main upstream: git remote set-url --push origin no_push
    5. Now push always to your upstream: git push $USER HEAD
  • Commitizen and Conventional Commits commit messages syntax (good practice in general):
    • feat: New feature (~ X.y.z)
    • fix: Bug fix (~ x.Y.z)
    • chore: Changes to auxiliary tools and libraries, tiny changes (~ x.y.Z)
    • build: Changes to the build/CI process
    • docs: Documentation only changes
    • style: Linting, formatting, etc. only changes
    • refactor: Change that neither fixes bugs or adds new features
    • perf: Change that improves performance
    • test: Change that adds or modifies tests
  • How packfiles work: Advanced topic.
  • Obtaining the current tip commit is quick via git rev-parse HEAD, but you can also ask for shorter versions, e.g. git rev-parse --short=9 HEAD
  • You can reproduce git file and folder hashes easily:
    • git ls-files -s <filepath>: returns, among other data, <filepath>'s SHA
    • git rev-parse HEAD:<filepath>: Like ls-files -s, but only returning the SHA
    • git ls-tree HEAD <folder>: returns, among other data, a SHA of inspecting all of <folder>'s contents
  • Perform a folder case rename on a case-insensitive OS (Windows or macOS):
    • git mv -f MyFolder MyFolder_temp: 1st, move using git mv, first to a temporary new name
    • git mv -f MyFolder_temp myFolder: 2nd, move again, now to the definitive folder
    • git status: Now you should see renamed: ... items (one per file in the renamed folder)

Optimizations

Important scalability note: Some operations depend on the size of the repository history (e.g. git clone), while others depend on the size and amount of repository files (e.g. git status).

  • git config --global checkout.workers 0: Disable git's serial mode by enabling it to use one thread per CPU core. Should speed up operations in big or long-living repositories.
  • git maintenance start: Enable prefetch and other git operations to happen in background (~hourly) for a repository.
  • sparse checkouts + sparse index: Fetch only a subset of files/folders. More info
  • shallow clone: Truncate git history to a specified number of commits.
  • partial clone: Get only a subset of files given a filter. More info about partial and shallow clones.
  • git scalar: Tool for large repositories.

Git Large File Storage (LFS)

  • git-lfs + install guide: Supports large files. Recommended for non-text files, and for non-source code big text files (e.g. big language resource files).
  • Linux TL;DR: Follow instructions at git-lfs.com + sudo apt install git-lfs + git lfs install from the target repo.
  • git lfs fetch --all <remote-name>: Fetches LFS objects from all branches from a given remote. <remote-name> is optional (else picks your default remote).
  • find .git/lfs/objects -type f | wc -l: When run inside a git repository, it'll count and display the number of LFS objects downloaded.
  • git lfs push origin --object-id <hash-or-hashes>: Manually push one or more local-available git LFS objects, given their hashes (filename of find .git/lfs/objects).
  • Useful LFS-related git config properties:
    • lfs.dialtimeout <seconds>
    • lfs.tlstimeout <seconds>
    • lfs.activitytimeout <seconds>
    • lfs.keepalive <seconds>
    • lfs.concurrenttransfers <number> (default is 8): This one is good if you need to throttle LFS usage (e.g. for a big upload)
  • git lfs env: Display all details regarding LFS configuration
  • Git LFS Man: Official Documentation .
  • Anchorpoint tutorial about basic Git LFS usage.
  • There are quite a few Git LFS server implementations.

GitHub

  • You can go to a commit or pull request, and append a .diff, to get a text diff view of it.

Searches

  • Issues in which you're mentioned
  • Pull requests in which you're mentioned
  • All open issues and pull requests of organization TEST: Change to proper organization name, and for example can filter to all assigned to you
  • When comparing tags/branches, in the URL X..Y is to show code differences, while X...Y shows the differences in commits (which commits are missing).
  • Get PR code comments (uses gh and jq): gh api repos/<user>/<repository>/pulls/<pull-request-number>/comments --jq '.[] | select(.body != "") | {author: .user.login, body: .body, path: .path, line: .line}' | cat
  • Get PR review comments (uses gh and jq): gh api repos/<user>/<repository>/pulls/<pull-request-number>/reviews --jq '.[] | select(.body != "") | {author: .user.login, body: .body}' | cat

Security

Git Cheatsheet page, written by Kartones