Advanced Git cheatsheet
Git is the most widely used modern version control system in the world. It is a mature, actively maintained open source project originally developed by Linus Torvalds.
While every team is different, Git is the best choice for most of the software teams. The following git cheat sheet could be used to enhance the development workflow.
Configuration
There are three-level configuration in Git (the configuration precedence is decreasing in the following list):
- Repository-specific configuration settings (
--file
options). Location:.git/config
- User-specific configuration settings (
--global
options). Location:~/.gitconfig
- System-wide configuration settings (
--system
options). Location:/etc/gitconfig
List the settings
git config -l
Config user
git config user.name "Akos Szoke" git config user.email "aszoke@example.com"
Config alias
git config --global alias.<name> <complex git command>
Initial Setup
Initialize a repo
Create an empty git repo or reinitialize an existing one in specified directory. Run with no arguments to initialize the current directory as a git repository.
git init <directory>
Clone a repo
Clone the existing `foo` remote repo into a new directory called `foo`:
git clone https://<url>/<username>/foo.git foo
Making a copy of a repo
Once you clone a repository, you are able to modify the cloned version, make new commits and so on. It is a complete repository with full history.
git clone <source-repo-dir> <target-repo-dir>
Setup Remotes
Remotes are simply aliases that store the URL of repositories. See a list of the remote repositories whose branches you track in a local repository (-v or --verbose option)
git remote -v
If we haven't setup upstream (the previous command provides empty output), then we can set it up as follows:
git remote add upstream https://<url>/<upstream_username>/<repo_name>.git git fetch upstream
The remote configuration is achieved using the `remote.origin.url` and `remote.origin.fetch` configuration variables.
Every-day Workflow
Staging Changes
Adding a file to repository's index (i.e. staging files).
git add <file> # add single file git add . # add files of the current directory
Note: Git index (or staging area) is used to collect changes (i.e. the changeset) to the next commit to the local repository.
Unstaging Changes / Restoring Files
Maybe you accidentally staged some files that you don't want to commit than to restore workig directory files:
git restore foo.js git restore .
Remove file
Remove files from the working tree and from the index
git rm <file> # removes a file from both the repo and the working directory git rm -f <file> # force the removal of the file even if is is altered git rm --cached <file> # removes the file from both the index and the working directory
Move or rename a file, a directory, or a symlink
Move or rename a file, a directory, or a symlink:
git mv <file 1> <file 2>
Note: The followings are equivalent with the previous:
mv <file 1> <file 2> git rm <file 1> git add <file 2>
Viewing commits and differences
Display the commit history
Display the entire commits using the default format:
git log
Listing a given branch:
git log <branch name>
Listing only commits backwards:
git log -<number>
Details the changes that was introduced by the commit:
git log -p <commit ID>
Enumerates the changed files and changes only:
git log --stat
Printing textual graphs:
git log --graph
Show commit as a nice graph:
git log --graph --abbrev-commit --pretty=oneline
Note: Formatting information could be --pretty={short, oneline, full}
Show commits in range:
git log --after="2021-02-09" --before="2021-02-09" # commits after and before dates git log --author="aszoke" # limits the author (pattern matching is used) git log --grep=".*rofil.*" # limits text from the log messages git log <branch name> # limits branches git log --max-count=3 # limits the number of commits to output
Status
List which files are untracked, unstaged (are in the working directory), staged (with `git add`) on the branch you are on:
git status
Diff
Show what is changed but unstaged:
git diff
Show what is staged but not yet commited:
git diff --staged
Show the diff of what is in branchA that is not in branchB:
git diff branchB...branchA
Show the diff between two commits:
git diff <commit-id1> <commit-id2>
Show changes between the working directory and a given commit:
git diff <commit-id>
Show changes between the stage area and a given commit:
git diff --cached <commit-id>
Branching
List Branches
Show all the branches that exist in the local repo, in the remote repo or all repos:
git branch git branch -r git branch -a
Switching Branches
The new git switch branch command is meant to provide a better interface by having a clear separation, which helps to alleviate confusion with the usage of git checkout.
git switch
:git switch master git switch develop git switch feature_x
Switch to previous branch:
git switch @{-1}
Create a new branch
Create a new branch named <new-branch-name>. As such, you generally want to stay off the master branch and work on your own feature branches so that master is always clean and you can base new branches off of it (-c or --create option).
git switch -c <new-branch-name>
This command is equivalent to the next two commands:
git branch <new-branch-name> git switch <new-branch-name>
If upstream has a special develop branch, you can checkout that branch separately, but you could setup tracking so you can sync it up from time to time. Like the master branch, don't work directly on this one.
git switch -c <new-branch-name> --track upstream/<remote-branch-to-track>
If you made some progress on a branch at work, but now you want to continue the work at home then . In that case, you're dealing with your own fork's branch, so you'll checkout from origin.
git switch -c <new-branch-name> --track origin/<remote-branch-to-track>
If you're using the same name as a remote branch name, this can be simplified:
git switch -c <branch-name>
Use the -C
(i.e. capital c) option flag to force it.
Commits
Recording changes to the local repository. You can always squash down your commits before a push to be more condensed.
git commit -m "Updated README"
Want to automatically stage files that have been modified and deleted, but new files you haven't told git about will be unaffected? Pass the -a
or --all
option flag:
git commit -am "Updated README"
Add an empty directory to the repository (Git doesn't store directories so a little hack is needed):
mkdir foo touch foo/.gitignore git add foo
Undoing Commits
The following command will undo your most recent commit and put those changes back into staging, so you don't lose any work:
git reset --soft HEAD~1
To completely delete the commit and throw away any changes:
git reset --hard HEAD~1
Squashing Commits
Maybe you have 4 commits, but you haven't pushed anything yet and you want to put everything into one commit so the lead developer doesn't have to read a lot of unnecessary details during code review:
git rebase -i HEAD~4
Pushing
Push a local branch for the first time:
git push --set-upstream origin <branch> git push
Configure Git to always push using the current branch name:
git config --global push.default current
Push a local branch to a different remote branch:
git push origin <local_branch>:<remote_branch>
Use the -f
option flag to force it.
Undo Last Push
Some would say this is bad practice. Once you push something you shouldn't overwrite those changes. Instead, you're supposed to create a new commit that reverts the changes in the last one. So, technically, you shouldn't do this, but in rare cases you need to do it.
git reset --hard HEAD~1 && git push -f origin master
Fetching
Fetch is the command that tells your local git to retrieve the latest meta-data info from the original. It doesn’t do any file transferring just checking to see if there are any changes available.
Pull brings (copy) the changes from the remote repository to the local repository.
git fetch upstream
Fetch changes from both origin and upstream in the same shot:
git fetch --multiple origin upstream
Clearing deleted remote branches (after pull request):
git fetch --prune
Merging
Git rebase and merge both integrate changes from one branch into another. The difference is how it's done. Rebase moves a feature branch into a master, while merge adds a new commit, preserving the history.
Therefore, rebase streamlines a potentially complex history (usually applied in huge projects where the commit noise is huge - e.g. in Linux kernel development). Merge is used to maintain complete history and chronological order (usually applied in smaller projects where the history is important).
More information could be found in the Git Rebase vs. Merge: Which Is Better? article.
feature_x
branch:git fetch origin git merge origin/feature_x
Pulling
Pulling is a fetch followed by a merge. If you know that a branch is clean, go ahead and get the latest changes. There will be no merge conflicts as long as your branch is clean.
git pull origin/feature_x
Rebasing
Rebasing is a way of rewriting history. In place of merge, what this does is stacks your commits on top of commits that are already pushed up. In this case, you want to stack your commits on top of origin/feature_x
:
git fetch origin git rebase origin/feature_x
If you already have a local branch set to track feature_x
then just do:
git rebase feature_x
Would you like to fetch, merge and then stack your changes on top, all in one shot? You can! If you have tracking setup on the current branch, just do:
git pull --rebase
Another great use of rebasing is just rewriting commit messages. To get an interactive text editor for the most recent commit, do:
git rebase -i HEAD~1
Now, you can replace "pick" with "r" and just change the commit message.
Manually Set Tracking
Perhaps you forgot to setup tracking when you pulled down a remote branch. No worries:
git config branch.<local_branch>.remote origin git config branch.<local_branch>.merge refs/heads/<remote_branch>
Deleting Branches
Delete a local branch:
git branch -d <local_branch>
Use the -D
option flag to force removal of an unmerged branch.
Delete a remote branch on origin:
git push origin :<remote_branch>
Stashing
Doing a stash with a message to identify changes:
git stash -m "<message>"
See which stashes are available, you can use:
git stash list
Tags When you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else - for example fixing a bug. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the git stash command. Stashing takes the dirty state of your working directory and saves it on a stack of unfinished changes that you can reapply at any time.
More information could be found in Git Tools - Stashing and Cleaning article.
If you want to unstash those changes and bring them back into your working directory:
git stash pop
Or maybe you want to unstash your changes without popping them off the stack. In other words, you might want to apply these stashed changes multiple times. To do this:
git stash apply
For a list of stashes:
git stash list
And to apply a specific stash from that list (e.g., stash@{3}):
git stash apply stash@{3}
Viewing the content of the stashed code (e.g., stash@{3}):
git stash show -p stash@{3}
Tagging
Tags are ref's that point to specific points in Git history. Tags are generally used to capture a point in git history that is used for a marked version release (i.e. v1.0).
List
To list stored tags in a repo execute the following:
git tag
List stored tags with wildcard characters using the l option:
git tag -l <regexp>
Fetch all tags from the remote (i.e., fetch remote tags
refs/tags/*
into local tags with the same name), in addition to whatever else would otherwise be fetched.
git fetch --tags
Create
Create a new annotated tag identified with
v1.0.0 with a message.
git tag -a v1.0.0 <commit-id> -m "Initial release"
Push
All refs under
refs/tags
are pushed, in addition to refspecs explicitly listed on the command line:
git push --tags
Delete
Delete existing tags with the given names:
git tag -d v1.0.0
Grep
Look for specified patterns in the tracked files in the work tree, blobs registered in the index file, or blobs in given tree objects:
git grep <regexp> $(git rev-list --all)