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

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 merge feature master

Creates a branch new merge commit in the feature branch.

Pros/cons: Original branches are not changed, but does mean history can become quite polluted with merge commit, 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 master.

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.

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