Stefan Hornburg (Racke)
Git cheatsheet
Display list of files in a commit
Display file contents for a given revision
Display file contents for in a branch
Log commits for a file that was moved or removed
List files deleted in the working directory
Display list of files with the last commit date
Store empty directory in the Git repository
Show all tags and corresponding messages
Push tags to remote repository
Show all files changed in a branch
Change URL for remote repository
Checkout a new branch from a tag
Tracking branches (set upstream)
Compare files between branches
Show commits in a branch not merged into master
Just show the name of the current branch
Commits
Lists and Graphs
List just the hash and the commit message for the ten most recent commits:
git log --pretty=oneline -n 10
Show a nice graph:
git log --oneline --graph --decorate
Display and listing files
Display list of files in a commit
Last commit:
git show --pretty="format:" --name-only
Commit before that:
git show --pretty="format:" --name-only HEAD~1
Display file contents for a given revision
git show ee63c9b61122bfe7e24f8512a3359157ae10066b:components/customer_info
Display file contents for in a branch
git show master:components/customer_info
Log commits for a file that was moved or removed
git log -- components/customer_info
List untracked files
git ls-files --others --exclude-standard
List files deleted in the working directory
git ls-files -d
To restore them:
git checkout `git ls-files -d`
Display list of files with the last commit date
racke@ambas:$ for file in $(find lib/ -type f ); \ do DT=$(git log -n 1 --pretty=%ci -- $file;); echo "$file:$DT "; done lib/PerlDance.pm:2016-09-05 15:58:07 +0200 lib/PerlDance/Routes.pm:2016-08-08 14:42:54 +0200 lib/PerlDance/Filter/Wiki.pm:2015-09-15 14:39:36 +0200 lib/PerlDance/Filter/MongerGroups.pm:2016-08-19 11:09:05 +0200 lib/PerlDance/Filter/Markdown.pm:2015-08-06 15:53:25 +0200
Merges
Prevent dirty merges
$ git config --global pull.ff only $ git pull fatal: Not possible to fast-forward, aborting. $ git pull --rebase Successfully rebased and updated refs/heads/topic/clean-123
Show all commits in a merge
Given that the hashref of the merge is 6d31d51
, you can use the following command to show
all commits in that merge:
git log 6d31d51^..6d31d51
Store empty directory in the Git repository
In case you want to keep a directory for dynamic content in the Git repository, but no files in it, use the following .gitignore
file:
# Explanation about the purpose of the directory * !.gitignore
Removing and purging files
Remove dynamic files
Sometimes you accidentally add dynamically created files to the Git repository. In this case, you usually want to remove the file from the repository but not from the working directory.
You can do this as follows, e.g for the file adjtime
:
git rm --cached adjtime echo adjtime >> .gitignore git add .gitignore git commit -m "Remove dynamically generated file"
Purge untracked files
Please doublecheck first that you don't remove files with valuable content.
Purge untracked files:
git clean -f ...
Purge untracked files including directories:
git clean -df sql/
Reverting commits
Reverting multiple commits
If the commits are consecutive, you can do:
git revert 4ea5493d9ac1d6ebcdb0bd29d70275e82ef23467^..d56fe7e13df7ec26c73d744077d7b079b70cc029
Where 4ea5493
is the oldest and d56fe7e
is the newest commit to revert.
Please note that Git will still create one revert commit for each of the commits to be reverted.
Get rid of Git commits
Remove the last three commits (~3):
git reset --hard HEAD~3
Please note that you will loose uncommitted work as well.
"Root" commits
The following command shows you all parentless (root) commits accessible from current branch, useful to find the initial commit in the repository:
git rev-list --max-parents=0 HEAD
It is useful to create an empty commit as "root" commit as the "root" commit cannot be rebased:
$ git commit --allow-empty -m "Initial commit." [master (root-commit) 99dbf81] Initial commit.
Signing commits/tags
Force signing
git config --global commit.gpgsign true
Multiple keys
If you have multiple keys with the same email address, you can configure the correct one as follows:
git config --global user.signingkey FA2720F8
Signing on remote hosts
Add extra socket to your GPG agent configuration file (~/.gnupg/gpg-agent.conf
):
extra-socket /path/to/extra-socket
Stage
Stage changes for the next commit:
git add README.md
Display staged files:
git diff --name-only --cached
Unstage changes:
git reset HEAD
Tags
Show all tags and corresponding messages
git tag -l '*' -n 1
Display a tag
$ git show 6.2.70 tag 6.2.70 Tagger: Stefan Hornburg (Racke) <racke@linuxia.de> Date: Thu Nov 10 12:41:43 2022 +0100 [-release]Preparing version 6.2.70 -----BEGIN PGP SIGNATURE----- ...
Add a signed tag
git tag -a -s -m "Release 0.21" v0.21
Push tags to remote repository
Tags are not automatically pushed with git push
:
git push --tags
Diffs & Logs
Show all files changed in a branch
$ git checkout mybranch $ git diff --name-only master ...
Compare releases
git diff 2.0.0 2.0.3
The diff can be restricted to changes in a file or a directory:
git diff 2.0.0:tasks 2.0.3:tasks
Commits
Diff for the last three commits:
git diff HEAD~3 HEAD
Ignore whitespace
Options for a diff ignoring whitespaces:
- --ignore-space-at-eol
-
Changes at end of line (e.g. LF to CRLF)
- -b
-
As above plus collapses multi whitespaces into one
- -w
-
Ignores all whitespace changes (usually not what you want ...)
Reverse diff
git diff -R
This can be useful, e.g to display deleted whitespace.
Formatting
Show the short hash:
git log --abbrev-commit
Remotes
Display remotes
Short form (just the names):
~# git remote origin upstream
Long form (includes URL):
~# git remote -v origin git@github.com:racke/dancer2.git (fetch) origin git@github.com:racke/dancer2.git (push) upstream git@github.com:PerlDancer/Dancer2.git (fetch) upstream git@github.com:PerlDancer/Dancer2.git (push)
Add remote repository
~# git remote add origin git@github.com:racke/dancer2.git
Rename remote repository
~# git remote rename origin upstream
Change URL for remote repository
~# git remote set-url upstream git@github.com:racke/dancer2.git
Reset after remote rebase
~# git reset --hard origin/topic/returns-user-control-3964
Please note that you will loose uncommitted changes.
Checkout
Checkout a new branch from a tag
Creates a new branch topic/v2.1-fixes from tag v2.1 and switches to it:
$ git checkout -b topic/v2.1-fixes v2.1 Switched to a new branch 'topic/v2.1-fixes'
Stash
Add to stash
With message:
$ git stash save "Old cruft."
List stash entries
$ git stash list
Show list with creation date of the entries:
$ git stash list --date=local
Show stash contents
$ git stash show -p stash@{1}
Include untracked files
git stash -u
git stash save -u "Stash away experiments"
Stash specific file(s)
git stash push default/web_tt2/subscriber_table.tt2
Stash a directory including untracked files:
git stash push -u roles/sympa
Retrieve stash
$ git stash pop stash@{1} On branch topic/header/from-as-envelope-sender Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: src/lib/Sympa/Spindle/ToList.pm no changes added to commit (use "git add" and/or "git commit -a") Dropped stash@{1} (9579fe0d020a8e63733dff961fc3bd0865100959)
Pick a file from the stash
git checkout stash@{0} -- src/lib/Sympa/Bulk.pm
Branches
Tracking branches (set upstream)
Track remote branch pr-fix-typo:
git branch -u origin/pr-fix-typo
This effectively sets the upstream for the current branch to origin/pr-fix-typo.
Compare files between branches
git diff master:README pr/fix-typo:README
This also works for directories.
Files added in a branch
This lists all files added in the branch topic/transaction-status and not present in the master branch.
git diff --name-only --diff-filter=A master topic/transaction-status
Similar for modified files only:
git diff --name-only --diff-filter=M master topic/transaction-status
Find branch for a commit
Local repository:
$ git branch --contains c0cc8e3e185e1de308b0c86b01f8fccc98dd4485 topic/imap-search-header-2020-03-12
Remote repositories:
$ git branch -r --contains c0cc8e3e185e1de308b0c86b01f8fccc98dd4485 github/topic/imap-search-header-2020-03-12 origin/topic/imap-search-header-2020-03-12
First commit for a branch
Show starting point of the branch in relationship to the main branch.
git log --reverse main..topic/pr-foo-bar
Show commits in a branch not merged into master
git cherry -v master feature/mobile
You can also compare to a remote branch:
git cherry -v master origin/feature/mobile
If there are no unmerged commits left, you can delete the local and remote branch with the following commands:
git branch -d feature/mobile
git push origin :feature/mobile
Merge base
The merge base is the common commit where the current branch diverged from. You can determine this commit with the merge-base command:
git merge-base master topic/org-addons
It is quite a common case to only see the logs or the diff of the changes in your current branch since they diverged.
But the following command will also consider the changes in master since the diverge:
git diff master..topic/org-addons
Instead you can use
git diff $(git merge-base master topic/org-addons)..topic/org-addons
or the shorthand (three dots):
git diff master...topic/org-addons
The same applies of course to the log command as well.
Just show the name of the current branch
git symbolic-ref --short HEAD
Rename branch
This renames the current branch. It doesn't affect the remote branch.
git branch -m topic/newname
To rename a branch other than the current one:
git branch -m topic/oldname topic/newname
In order to rename the remote branch we are doing two steps.
First we delete the old remote branch and push the new local branch:
git push origin :topic/oldname topic/newname
Second we update the remote tracking information:
git branch -u origin/topic/newname
Move branch
Move from master
to main
:
git branch -M main
Merged and unmerged branches
Show merged branches:
git branch --merged
Show remote merged branches:
git branch -r --merged
Show unmerged branches:
git branch --no-merged
Sorted by age
This sorts local branches by date of the latest commit and displays the commit date and the branch name:
for C in $(git for-each-ref --sort=committerdate refs/heads --format='%(refname)'); do git show -s --format="%ci $C" "$C"; done
Same for remote branches from origin:
git fetch origin for C in $(git for-each-ref --sort=committerdate refs/remotes/origin --format='%(refname)'); do git show -s --format="%ci $C" "$C"; done
Configuration
Name and email
git config --global user.name "Stefan Hornburg (Racke)" git config --global user.email "racke@linuxia.de"
Break out part of the repository
One example is to extract common code out of a Perl module to start a separate module, but keep the history on the files involved, e.g. extract code out of Interchange6::Schema into Test::DBIC::Roo.
First step is to clone the repository into a new directory:
git clone git@github.com:interchange/interchange6-schema.git Test-DBIC-Roo cd Test-DBIC-Roo
Now we decide which files respective directories we are going to remove from the repository:
FILE_LIST=".gitignore .travis.yml bin CHANGES lib/Interchange6/Schema lib/Interchange6/Schema.pm lib/Interchange6/Test/Role/Fixtures.pm Makefile.PL MANIFEST MANIFEST.SKIP README t/inflatecolumn_datetime.t t/lib"
Go through the commit history and check if everything is fine for you. If yes, the next step is to rewrite the history of the repository:
git filter-branch \ --index-filter "git rm -r --cached --ignore-unmatch $FILE_LIST" \ --prune-empty \ HEAD
If yes, clean out Git objects which are no longer relevant to the new repository:
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin git reflog expire --expire=now --all git gc --prune=now
Another use case is the removal of a file and its history due to legal or privacy reason.
git filter-branch --tree-filter somefile.txt HEAD
Shell
It is quite helpful to show Git related information in your shell prompt.
If you are using the ~/.bashrc
from the /etc/skel
directory on Debian your shell prompts are configured as:
if [ "$color_prompt" = yes ]; then PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' else PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' fi
So a prompt could look like:
racke@linuxia:~/provisioning/ansible$
After we modified the prompts to include the Git branch information, the definitions look like:
if [ "$color_prompt" = yes ]; then PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\[\033[01;35m\]$(__git_ps1 " (%s)")\[\033[00m\]\$ ' else PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w$(__git_ps1 " (%s)")\$ ' fi
After reloading your ~/.bashrc
the prompt will change to include the Git branch information for a directory (if any):
racke@linuxia:~/provisioning/ansible (master)$
We can also add further information with setting the GIT_PS1_SHOWDIRTYSTATE environment variable.
So if you have changes in this working directory, the prompt shows an asterisk after the branch name:
racke@linuxia:~/provisioning/ansible (master *)$