Git
Aliases⌗
From my .gitconfig.
[alias]
alias = config --get-regexp '^alias.*' # list available aliases
# add and commit
a = add
aa = add --all
ac = !git add . && git commit -am
ap = add -p
c = commit --verbose
ca = commit -a --verbose
cm = commit -m
cam = commit -a -m
m = commit --amend --verbose
uncommit = reset --soft HEAD~1
# branching
ba = branch -a
bd = branch -d
bD = branch -D
branches = branch --all
branchrename = branch --move
branchesdiffed = !git branch | grep -i "indiff"
branchesundiffed = !git branch | grep -v -i "indiff"
cleanmerged = !git branch --merged | grep -v \"\\*\" | xargs -n 1 git branch -d # remove merged branches
co = checkout
cob = checkout -b
wipelocal = checkout .
# diff
d = diff
ds = diff --stat
dc = diff --cached
f = fetch -p
# remote
p = push
pr = pull --rebase
pushitgood = push -u origin --all
rao = remote add origin
# rebase
rb = rebase
rba = rebase --abort
rbc = rebase --continue
rbs = rebase --skip
# log
st = status -sb
plog = log --graph --pretty='format:%C(red)%d%C(reset) %C(yellow)%h%C(reset) %ar %C(green)%aN%C(reset) %s'
tlog = log --stat --since='1 Day Ago' --graph --pretty=oneline --abbrev-commit --date=relative
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
out = log --pretty=oneline --abbrev-commit --graph @{u}..
in = !git fetch && git log --pretty=oneline --abbrev-commit --graph ..@{u}
rank = shortlog -sn --no-merges
winning = shortlog --summary --numbered --email
totalcommits = !git log --all --pretty=oneline | wc -l
commitslastmonth = !git log --author=\"`git config user.name`\" --before={`date "+%Y-%m-01"`} --after={`date --date=\"$(date +%Y-%m-1) -1 month\" \"+%Y-%m-01\"`} --reverse --pretty=format:\"%cd %h %s\" --date=short
commitsthismonth = !git log --author=\"`git config user.name`\" --before=now --after={`date "+%Y-%m-1"`} --reverse --pretty=format:\"%cd %h %s\" --date=short
commitstoday = !git log --author=\"`git config user.name`\" --since=\"6am\"
rank = shortlog -sn --no-merges
Common tasks⌗
Undo-ing⌗
Dump all uncommitted changes and baseline the local files based on the latest committed change (HEAD):
git reset --hard feature/cool
Undo uncommitted changes in a specific file:
git reset HEAD src/contoso/settings.ini
You committed and/or pushed didn’t you?
git show some_commit_sha1 -- some_file.py | git apply -R
Diff-ing⌗
Show changes over the last 3 commits:
git diff HEAD~3
Pushing and pulling⌗
Set the upstream for first time pull or push:
git push -u origin feature/simoid
Patches⌗
Sometimes instead of managing things as a commit, its nice to be able to email or share changes as a patch. Git makes this incredibly easy:
git diff > mypatch.patch
git apply mypatch.patch
Uncommon tasks⌗
Branch name in Bash prompt (PS1)⌗
brname () {
a=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
if [ -n "$a" ]; then
echo " [$a]"
else
echo ""
fi
}
PS1="\u@\h:\w\$(brname)$ "
Discover large commits in history⌗
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
Concepts⌗
Rebase vs Merge⌗
The purpose of both is to integrate changes from one branch into another.
Merge⌗
Merges create a new merge commit onto the target branch, which ties the two branches together, including any new patches and/or conflict resolutions. For example:
git checkout master
git merge feature
Creates a new merge commit in the master branch, that includes all the changes made in feature since it diverged.
Pros/cons: Original branches are not changed, but does mean history can become quite polluted with merge commits, especially if the upstream branch is very active.
Rebase⌗
My preferred method is rebase. In essence rebase is about lifting up your branch, and recreating the base on which is sits. One would rebase the feature branch onto the master branch.
git checkout feature
git rebase master
This re-writes history (the commit chain) by replaying the commits in the source branch, as brand new commits in the upstream branch.
Pros/cons: Much cleaner history (i.e. you don’t have to unpack random merge commits to figure out what has happened). Linear history (commit chain). Can be catastrophic for collaboration workflows. Loss of context of when upstream commits were incorporated into the feature branch.
Never use rebase it on public branches.
Interactive rebasing (-i)⌗
So very useful. Presents the journal of commits, with the proposed commands (e.g. pick
, fixup
) to be used to deal with each commit.
git checkout feature
git rebase -i master
Vim will launch with:
pick 13d5b7b Message for commit 1
pick 2480b3e Message for commit 2
pick 3c67e6e Message for commit 3
By changing the pick
command or reordering entries, can make history look however you please. There are many commands available, fixup
for example squashes commits together.
pick 13d5b7b Message for commit 1
fixup 2480b3e Message for commit 2
pick 3c67e6e Message for commit 3
The result will be two commits, commit 2 will be squashed into the commit 1, and commit 3.
Submodules⌗
Embeds another Git repo within a Git repo. One handy use case is relatively referencing some common source, which is managed independently in its own Git repo. The submodule can be pinned to a particular branch by creating a .gitmodules
file.
[submodule "common"]
path = common
url = git@gitlab.dev.local:fooframework/common.git
branch = master
In each branch of the parent repo (the one that includes the submodule/s), you could commit slightly different versions of the .gitmodules
so that branch of the submodule lines up. For example, you may want the develop
branch of the parent repo, to use the develop
branch of the submodule. However when in the master
branch of the parent repo, you’d like the master
branch of the submodule to be used.
Detached Head⌗
Every now and then I end up with a detached head. WTF is a detached head again? It happens when you checkout a specific commit, instead of a branch. The HEAD
pointer will stick to the specific commit, and no longer automatically point to the latest commit in the working branch. If you don’t realise the HEAD is detached, and get to work and make a bunch of changes, these changes can easily get lost as the correct commit/branch accounting is thrown out.
My favourite remedial action, is to stash the changes, then properly chechout the branch (not a commit!), pop the stash, stage and commit.