Commands
Notes:
originis the default remote, you can always specify another remote name^: "parent of", e.g.git show HEAD^means "show me the parent commit ofHEAD". If followed by a number, is the n-parent, so useful for merge commits only~: "first parent of", e.g.git show HEAD~3means "show me the parent of the parent of the parent ofHEAD"{x}: History position, to be used from commands likereflog..: range specification: e.g.git log A..Bshows changes inBnot yet inA...: 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 Cshows changes either atAorB, but not present inC
Branch Management
git checkout -b <new-branch-name>: get a new branch and switch to itgit checkout -b <branch-name>: get a local copy of a remote existing branchgit checkout <branch-name>: switch to an existing branchgit checkout --orphan <branch-name>: create branch without full historygit 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 branchgit 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 commitgit merge --no-ff <source-branch>: merge into current branch source one without rebase/FF. This will create a new merge commitgit push origin <destination-branch>: push merges/changesets to a branchgit push origin <local-branch>:<remote-branch>: push merges/changesets to a different branch than localgit 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--forcewhich would always override remote commitsgit remote -v: show all remotes configured in the repositorygit remote show origin: display the path of the repositorygit remote set-url origin xxx: Change remote URI to xxxgit remote rm <name>/git remote remove <name>: Remove a remotegit 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 originto remove the previous onegit 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 remotegit 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 thepatchoption)git commit: commit changesgit status: show status of uncommited filesgit checkout <filename>: revert a filegit checkout <branch-name> <filename>: Checkout all changes to<filename>from branch<branch-name>into currentgit checkout <revision> .: revert a full branch to specified revision if not commitedgit revert <commit1> <commit2> ...: Reverts certain commits if commited. Be very careful with this command, revert is problematic. Read this document fully but TL;DR is that, if you can't revert via GitHub's UI (it's not a PR, etc.), then go for an interactive rebase and wipe out the problematic commits.git clean -n: dry run to check which uncommited modifications would be removedgit clean -fd: remove all local uncommited modificationsgit restore --staged .: unstage all staged changes. Combined withgit checkout .andgit 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 fetchgit branch --sort=-committerdate # DESC: display local branches, ordered by last commit dategit branch -r: list all remote branchesgit diff: Show changes in filesgit diff <branch-name> origin/<remote-branch-name>: show a diff between a local branch and a remote one from theoriginremotegit --no-pager diff --name-only master...HEAD: list all modified files in commits from current branch in regards tomasterup to your last pullgit diff > <filename>: Save a diff patch file. To apply it, usegit 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 changesgit 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 reviewgit rebase --onto <A-branch> <B-branch> <C-branch>: Rebase commits fromCnot present atB, intoA; assuming thatBwas branched fromA, andCwas branched fromB("skip the intermediary").git rm <filename>: delete a file from branch and filesystemgit branch -d <branch-name>: delete a local branchgit push origin --delete <branch-name>: delete a remote brachgitk <filename>: show visual git loggit reset <revision-hash> .: revert a full branch to specified revision if commitedgit 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-leasegit reset --hard HEAD^: forcibly resets to the branch's head, discarding any commitgit 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 stategit reset HEAD <file>: reset<file>to the version inHEADgit 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 messagegit reset 'HEAD@{1}': revert to the previous command's commit. Check thereflogto see which command was last. This is very useful to undo non-trivial commands like agit reset HEAD~1git reset HEAD@<commit>: revert to a specific commit (often from thereflog)git submodule update --init --recursive: Init and update all submodulesgit 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 branchgit checkout --theirs xxxxgit checkout --ours xxxx: Keep changes from incoming branch or local one, respectivelygit 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 messagegit 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 commitsgit diff --staged: Normalgit diffshows only unstaged changes, with--stagedyou 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,
--forcewill destroy any commit added by others to the remote.--force-with-leasetries 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
.difffile: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 needgit fetch origin master && git rebase origin/master && git push origin HEAD --force-with-lease: Rebasemasterwith 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
Stashing
git stash: Stash current changesgit stash -u: Stash also the untracked changesgit stash apply: Unstash and merge stored changesgit stash list: check all stashes
Logs, Search
git log -n X -p: show X last commits with diffsgit log <branch-A-name> ^<branch-B-name>: commits in branch A that aren't in branch Bgit log -- <path>: filter to commits including modifications to<path>/filegit log --pretty=oneline --stat --all <folder-name>: same as previous, but better presentedgit log --format='%H' -1 HEAD: Get last commit hash from current branchgit log --pretty=format:"%h %s" --graph: format the commits log as nice graph with hash & commit messagegit rev-list origin/master...master: list all commits that exist on localmasterbut not onorigin/mastergit ls-remote git@xxxx.git | grep <commit-sha>: search if a commit exists upstreamgit reflog: show a history of changes in all branchesgit show HEAD@{3}: show commit details (easiest way to combine withreflog)git log origin/<branch-name>..<branch-name>: Show diff between remote commits and local commitsgit log <branch-name>..origin/<branch-name>: Show diff between local commits and remote commitsgit blame -M: Blames original commit, not the move commitgit blame -CCC: Looks at all commits in historygit 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 logsgit log -G <regex>: Search in which commits the regular expression matchesgit tag -l "v1.*": Searching for tags supports wildcard via the-loptiongit grep: Search patterns in tracked files (+ info)git merge-base <branch-A> <branch-B>: Find common ancestor commit between two branchesgit diff --name-only HEAD~2 HEAD: Show a list of modified files from the last2commits
Configuration
git config --list: List currently setup config valuesgit 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 hoursgit config --global color.ui true: Activate colors in diffs, etcgit 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~/.gitignoregit 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-highlightgit 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
- git extras: Many tools, but I use it just because I can see branches ordered by last activity via
- Show the current git branch at prompt - 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 - macOS (add to
~/.zshrc):
parse_git_branch() {
git symbolic-ref --quiet --short HEAD 2> /dev/null | sed -e 's/\(.*\)/ (\1)/'
}
autoload -Uz add-zsh-hook
function update_prompt() {
local git_branch=$(parse_git_branch)
PROMPT='%~%F{39}'"$git_branch"'%f $ '
}
add-zsh-hook chpwd update_prompt
zshaddhistory() {
if [[ $1 == git\ 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
Third-party Tools
- tig: to navigate commits & branches
- Github:
- hub: CLI tool to better interact with Github from the command-line. I now recommend to use instead
cli(the official GH tool) - 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
- hub: CLI tool to better interact with Github from the command-line. I now recommend to use instead
Tutorials


- 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:
- Clone the main upstream, not your fork:
git clone git@github.com:<user-or-org>/<repo>.git - Sync and update submodules:
git submodule sync --recursive; git submodule update --init --recursive - Setup your fork as another upstream:
git remote add $USER git@github.com:<your-github-user>/<repo>.git - Disable pushes to main upstream:
git remote set-url --push origin no_push - Now push always to your upstream:
git push $USER HEAD
- Clone the main upstream, not your fork:
- 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 processdocs: Documentation only changesstyle: Linting, formatting, etc. only changesrefactor: Change that neither fixes bugs or adds new featuresperf: Change that improves performancetest: 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 SHAgit rev-parse HEAD:<filepath>: Likels-files -s, but only returning the SHAgit 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 usinggit mv, first to a temporary new namegit mv -f MyFolder_temp myFolder: 2nd, move again, now to the definitive foldergit status: Now you should seerenamed: ...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: Enableprefetchand 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 installfrom 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 offind .git/lfs/objects).- Useful LFS-related
git configproperties:lfs.dialtimeout <seconds>lfs.tlstimeout <seconds>lfs.activitytimeout <seconds>lfs.keepalive <seconds>lfs.concurrenttransfers <number>(default is8): 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..Yis to show code differences, whileX...Yshows the differences in commits (which commits are missing). - Get PR code comments (uses
ghandjq):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
ghandjq):gh api repos/<user>/<repository>/pulls/<pull-request-number>/reviews --jq '.[] | select(.body != "") | {author: .user.login, body: .body}' | cat
Security
- Anyone can Access Deleted and Private Repository Data on GitHub: Interesting reading. Not trivial, but neither too hard to brute-force, so careful with your deleted forks, deleted repositories and even with private repositories.