Git Quick Reference¶
Common diff options¶
--numstat Shows number of added and deleted lines
--name-only Show only names of changed files.
--name-status Show only names and status of changed files.
See the description of the --diff-filter option
on what the status letters mean.
--diff-filter=[(A|C|D|M|R|T|U|X|B)…[*]]
Select only files that are Added (A), Copied (C),
Deleted (D), Modified (M), Renamed (R), have their
type (i.e. regular file, symlink, submodule, …)
changed (T), are Unmerged (U), are Unknown (X), or
have had their pairing Broken (B).
-S<string> Look for differences that introduce or remove an
instance of <string>.
-G<regex> Look for differences whose added or removed line
matches the given <regex>.
-b
--ignore-space-change
Ignore changes in amount of whitespace.
-w
--ignore-all-space
Ignore whitespace when comparing lines.
Git Log Formatting¶
git log --pretty=<format>
List of the valid <format>
:
oneline
short
medium
full
fuller
email
raw
format:<string>
tformat:<string>
format:<string>
and tformat:<string>
work a bit like printf
. User composes <string>
to specify how the information should be shown. See Git-log: Pretty Format for the valid placeholders can be used in <string>
.
The difference between format:<string>
and tformat:<string>
is that format:<string>
uses a separator between commits while tformat:<string>
uses a terminator between commits.
Git Log Searching¶
Show the commits that that contain/add/remove <string>
or <pattern>
git log -S<string> -i -p
git log -G<pattern> -i -p
Note:
- Option
-i
for case-insensitive search - Option
-p
to show the file diff - The results are printed by Unix command
LESS
, which supports the following commands/operations-i
: toggle case-insensitive search/
: search1G
,j
,k
: movement commands like Vim
Exclude directories or files
git log -- . ':!path/to/ignore'
Some Branch Operations¶
Rebase branch ABC
onto branch master
git rebase master ABC
git rebase master ABC -i
git rebase --onto master ABCParent ABC
The option -i
(--interactive
) allows user to edit the list of commits to be rebased.
NOTE: git merge
only merges the specified branches to the current branch. To merge branch ABC
into branch master
, user has to first change to master
and perform git merge
with the other branch.
Given the following repo graph:
C1 <-- C2 <-- C5 <-- C6 (master)
\
\-- C3 <-- C4 <-- C9 (ABCParent)
\
\-- C7 <-- C8 (ABC)
Rebase commands:
git rebase master ABC
"replays"C3
,C7
, andC8
ontoC6
git rebase --onto master ABCParent ABC
"replays"C7
, andC8
ontoC6
Create a new branch ABC
at the commit XYZ
git branch ABC XYZ
Reset the branch ABC
to the commit XYZ
git branch -f ABC XYZ
Rename a branch ABC
to new name XYZ
git branch -m ABC XYZ
Git Bisect¶
git bisect start <bad_commit> <good_commit>
# Do bisect using the script. Note that the initial <bad_commit> and <good_commit>
# are assumed to be correctly labeled and are NOT checked.
git bisect run my_script my_script_arguments
git bisect reset
Submodule¶
Ref: https://git-scm.com/book/en/v2/Git-Tools-Submodules
# Clone a repo with submodules
git clone --recurse-submodules <repo> <dir>
# Initialize the (nested) submodules in an already cloned repo:
git submodule update --init --recursive
# Check out a commit and sync all the submodules to the state associated
# to that commit
git checkout --recurse-submodules <commit>
# Configure git to automatically check out with the --recurse-submodules flag
git config submodule.recurse true
Commit Only Part of a File (Interactive Staging)¶
Use git add -p file
(-p
or --patch
) to determine whether to stage each hunk of the changes.
Use git add -i
(-i
or --interactive
) to enter a more verbose mode to allow user to select the hunks of the changes from individual files.
Git will show a hunk of the change and ask user to select an action upon the shown hunk:
- y - stage this hunk
- n - do not stage this hunk
- a - stage this and all the remaining hunks in the file
- d - do not stage this hunk nor any of the remaining hunks in the file
- g - select a hunk to go to
- / - search for a hunk matching the given regex
- j - leave this hunk undecided, see next undecided hunk
- J - leave this hunk undecided, see next hunk
- k - leave this hunk undecided, see previous undecided hunk
- K - leave this hunk undecided, see previous hunk
- s - split the current hunk into smaller hunks
- e - manually edit the current hunk
- ? - print help
Git sparse-checkout¶
Enable the sparse-checkout
git config core.sparsecheckout true
The filter file is located at .git/info/sparse-checkout
. Each line of .git/info/sparse-checkout
defines a filter, and the latter definition overwrites the former ones. For example, the following .git/info/sparse-checkout
will check out all files except those in the folder /Tools/
/* # check out all files
!/Tools/ # Do not check out the folder /Tools/, this filter
# overwrites the filter defined in the above lines.
Update the working tree:
git read-tree -mu HEAD
Creating patch and applying to a different repository¶
git format-patch -k <revision range>
git am -3 -k --keep-cr <patches>
git format-patch
prepares the commits as patches for e-mail submission
- Use
-k
to instruct git not to add the label[PATCH]
in the subject line. This is useful when the commit header also uses the format of[TAG_NAME]
to categorize the commit (e.g.[Bug]
,[Refactor]
).
NOTE:
git format-patch
generates the patches by specifying the file path with respect to the root of the current git repo. In other words, with the samegit format-patch
command under a git repo, the patches generated under<GitRepoRoot>/a/b/c
are identical with those generated under<GitRepoRoot>
.
git am
applies the "patch emails" (those generated by git format-patch
) to git repository
- Use
-3
to 3-way merge the patch. This option makesgit am
to try merging when conflict is found. - Use
-k
to instruct git to not remove the email cruft in the subject line. This is useful when the commit header also uses the format of[TAG_NAME]
, for git considers those[TAG_NAME]
as some crufts to be removed. - Use
--keep-cr
to instruct git to keep the CR when applying the patch. This option will resolve some problems that the patch for a DOS file cannot be applied. - Use
--ignore-whitespace
to workaround some cases thatgit diff
cannot find the context lines in the files to be patched. WARNING: This option may mess the line-endings in the file of DOS format. Use with caution.
NOTE:
git am
applies the patches by recognizing the file path with respect to the root of the current git repo. In other words, with the samegit am
command under a git repo, the result ofgit am
will be the same regardless of the current working directory and/or the location of the patch files.
While it is undocumented, git format-patch
seems to work with the following syntax
git format-patch <revision range> <files>
So the following command generates the patches only related to the files in path/of/interest/
and the sub directories:
git format-patch -k <revision range> path/of/interest/**
Applying Patches to a different repository with different directory structure¶
If there are directory changes for the files of interest between the repo to generate the patch and the repo to apply the patch, use -p<N>
and --directory=<root>
to tweak the path where the patch will be applied.
Example 1: We want to apply the change made in <Repo1>/a/b/c/MyFILE
to <Repo2>/x/y/z/MyFILE
. The corresponding git commands for creating and applying patches will be
# In <Repo1>
git format-patch -k <revision range> a/b/c/MyFILE
# In <Repo2>
git am -k -p4 --directory=x/y/z <patches>
The option -p<N>
is to "remove <N>
leading slashes from traditional diff paths." Effectively, given the file specified in the patch is at ./a/b/c/MyFILE
with respect to the root of the git repo:
-p1
:git am
expects to find the file at ./a/b/c/MyFILE (with respect to the root of the git repo)-p2
:git am
expects to find the file at ./b/c/MyFILE-p4
:git am
expects to find the file at ./MyFILE
The option --directory=<root>
is to "prepend <root>
to all filenames", and the prepending takes place after -p<N>
. In other words:
-p1 --directory=x/y/z
:git am
expects to find the file at ./x/y/z/a/b/c/MyFILE-p4 --directory=x/y/z
:git am
expects to find the file at ./x/y/z/MyFILE
Controlling Gitk Color¶
$ diff ~/.config/git/gitk.bak ~/.config/git/gitk
21,22c21,22
< set bgcolor SystemWindow
< set fgcolor SystemWindowText
---
> set bgcolor black
> set fgcolor grey
26c26
< set diffcolors {red "#00a000" blue}
---
> set diffcolors {red "#00a000" cyan}
30c30
< set selectbgcolor SystemHighlight
---
> set selectbgcolor blue
52c52
< set linkfgcolor blue
---
> set linkfgcolor cyan
Remote¶
Ref: * https://git-scm.com/docs/git-remote * http://stackoverflow.com/questions/16408300/what-are-the-differences-between-local-branch-local-tracking-branch-remote-bra
Terminology:
- Remote repository: A git repo on a remote machine
- Remote branch: A git branch on the remote repository
- Remote-tracking branch: A git branch on the local repository set up for tracking a remote branch. This branch usually named in the format of
remotes/<RepoName>/<BranchName>
- Local non-tracking branch: A git branch on the local repository that is not set to track any other branch.
- Local tracking branch: A git branch on the local repository that is set to track another branch (usually a remote-tracking branch).
Start tracking branches in a remote repository
git remote add -t <BranchName> -m <MasterBranchName> -f -- <RepoName> <url>
-t <BranchName>
: Specify the branch in the remote repo to be tracked. There can be more than one-t
. If no-t
is specified, all branches in the remote repo will be tracked.-m <MasterBranchName>
: Specify the "master" branch in the remote repo. Practically, a symbolic-refrefs/remotes/<RepoName>/HEAD
will be set up to point at remote’s<MasterBranchName>
branch.-f
: fetch the remote repo after the remote information is set up.<RepoName>
: The name of the remote repo appearing in the git log.- In gitk it appears as
remotes/<RepoName>/<BranchName>
. <url>
: The path to the remote repo. It can be a HTTP path or a file path.
Push a new local branch to a remote repository and track it too
git push -u <RepoName> <BranchName>
Create a local tracking branch to track a remote-tracking branch:
git checkout -b <LocalBranchName> <RepoName>/<BranchName>
<LocalBranchName>
can be different from <BranchName>
, but conventionally (and for convenience) <LocalBranchName>
tends to be the same as <BranchName>
.
Stop tracking a remote branch (done by deleting the corresponding remote-tracking branch)
git branch -d -r <RepoName>/<BranchName>
git push <RepoName> --delete <BranchName>
git remote prune <RepoName>
git fetch -p <RepoName>
git pull -p <RepoName>
Bare Repository¶
Change active branch in a bare repository
git symbolic-ref HEAD refs/heads/<BranchName>
File Listing¶
List the untracked files:
git ls-files --others --exclude-standard
List the staged changes:
git diff --cached --name-only
List the unstaged changes:
git diff --name-only
List the staged/unstaged changes and untracked files (good for checking if there is any uncommitted local changes)
git status --porcelain
List the files in the git repo:
# List the files under the current directory (recursively)
git ls-tree -r HEAD
# List all files regardless of the current directory.
git ls-tree -r HEAD --full-tree
# List the files and only print the file path
git ls-tree -r HEAD --name-only
Status Reporting¶
Get the name of the current branch (when not on a detached HEAD)
git rev-parse --abbrev-ref HEAD
Show git root directory
git rev-parse --show-toplevel
List all branches that contain commit <commit>
git branch --contain <commit> # search local branches
git branch -r --contain <commit> # search remote-tracking branches
Find the common ancestor of <commit1>
and <commit2>
:
git merge-base <commit1> <commit2>
Find merge commit where <commit>
is merged into <branch>
-
Option 1:
git rev-list <SHA-1_for_c>..master --ancestry-path > a.txt git rev-list <SHA-1_for_c>..master --first-parent > f.txt comm -1 -2 a.txt f.txt # The last common line shows the desired merge commit.
-
Option 2: use the git alias (adding it to
.gitconfig
) in bash[alias] find-merge = "!sh -c 'commit=$0 && branch=${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2'" show-merge = "!sh -c 'merge=$(git find-merge $0 $1) && [ -n \"$merge\" ] && git show $merge'"
Misc¶
Allow empty commit:
git commit --allow-empty
Rediscover lost commits (ref: restoring lost commits)
git fsck --lost-found
git reflog
Check out the files modified in COMMIT_X
:
git checkout -- $(git diff COMMIT_X^ COMMIT_X --name-only --relative)
Unstage a staged file:
git reset FILEPATH
Detect whether a string is a valid commit
git cat-file -t master
git cat-file -t 29a8ed626f
Force to create a merge commit
git merge --no-ff
Overwrite the current commit's timestamp
git commit --amend --no-edit --date "TIMESTAMP"
Check if a local branch exists
git show-ref --verify --quiet refs/heads/"<BranchName>"
- Recommend to quote
<BranchName>
in case it contains special characters - With
--quiet
, the command prints nothing regardless of the existence of<BranchName>
. Use the returned error code to determine the result- Error code 0: branch exists
- Error code 256: branch does not exist
- Without
--quiet
, the command prints different message based on the existence of<BranchName>
:- branch exists: "
<CommitHash> refs/heads/<BranchName>
" - branch does not exist: "
fatal: 'refs/heads/<BranchName>' - not a valid ref
"
- branch exists: "
Find large objects in the git repo (Ref: Stackoverflow)
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| sed -n 's/^blob //p' \
| sort --numeric-sort --key=2 \
| cut -c 1-12,41- \
| $(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Replicate the repo at /path/to/new/repo/
with an undesired file (Unwanted.dat
) removed: (Ref: Stackoverflow)
git filter-branch --prune-empty -d /path/to/new/repo \
--index-filter "git rm --cached -f --ignore-unmatch Unwanted.dat" \
--tag-name-filter cat -- --all