Command line, Tips & tricks

Cleaning up git branches

In order to keep your git environment in a good shape you need to clean up branches from time to time. It is especially difficult in case of build servers which complete pull requests on behalf of developers (like VSTS). It’s easy when you have just a few branches to review, but if you forget to do some cleaning for longer while then it’s getting painful. For sure you do not want to remove effects of your work by accident during cleanup.

Git branch on steroids

There is a very cool option for well-known git branch command: -vv (very verbose). It will enrich flat list of branches with lots of extra information (see below):

branch1
Instead of just branch names we get (in the following order):

  • Branch name (current one is marked with star)
  • Last commit hash
  • Remote references (if exist)
  • Last commit message

The coolest thing is reference to remote-tracking branches. What’s more it shows if remote-tracking branch is ahead/behing remote or remote branch is gone. You can use that information to split cleanup process into the following steps:

1. Fetch branch information

You need to run the following command in order to remove remote-tracking branches that no longer exist on remote repository and refresh information about ahead/behind commits:

git fetch --all --prune

2. Remove branches that no longer exist on remote

When VSTS completes pull-request, it removes (by default) the source feature branch. If you forget to remove corresponding local branch, then it gets orphaned and it’s a perfect candidate to clean up.

branch2

In order to do a bulk clean up, run the following command (you will find command line reference in the end of this post):
git branch -vv | grep ": gone\]" > .to-remove && vim .to-remove

This should extract all orphaned branches to a file and let you review/edit that file before real clean up. After that you should use another command to remove them:

grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git branch -D

3. Remove unpushed branches

You may also need to clean up some temporary or deprecated unpushed branches. Run the following command to find (and review) all local branches that have not been pushed to remote.

git branch -vv | grep -v "\[origin.*\]" > .to-remove && vim .to-remove

After that you can remove them all same as before:
grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git branch -D

4. Remove branches with existing remote branch

You should find them using the following command:

git branch -vv | grep "\[origin.*\]" > .to-remove && vim .to-remove

I would think twice before removing branches that are ahead of remote version, but the rest can be safely reviewed and cleaned up (all in all up-to-date version exists on remote)

2017-12-03_11-18-40

Again, in order to bulk remove branches you run the familiar command.

grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git branch -D

CAUTION: If you want, you can also remove branches on remote using the following comand (do it carefully):

grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git push origin --delete

All-in-one

If you have only a few branches to clean up you do not have to do steps from 2 to 4 one-by-one. Instead edit the whole list of branches before removing:

git branch -vv > .to-remove && vim .to-remove

And finally remove them:
grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git branch -D

Branch naming

Personally I add workitem ID to the name of my branches (see screenshots). On one hand it’s easier to connect pull-requests with corresponding work item. On the other hand it’s handy while removing branches. I can extract work item IDs of my branches and check which of them are still in-progress and I should wait witch cleaning them up.

In order to get a comma-separated list of workitem IDs behind my branches I use the following command:

git branch | cut -c 3- | grep -oP "\d{3,}" > .tmp && vim .tmp && paste -sd "," .tmp

Command line reference (Windows)

| – redirect output of one command as input to another command
&& – run another command without redirecting output/input
> – redirect output to a file
grep ": gone\]" – find lines containing given phrase “: gone]
grep -v "\[origin.*\]" – find lines not containing phrase “[origin.*]” (.* means “anything”
grep ".*" .to-remove – print all lines of file “.to-remove
cut -c 3- – substring from input, from 3rd character in line to the end of line
awk '{print $1}' – print first column of text (columns are separated by whitespaces)
xargs – use input as parameter to given command
grep -o – print only the matching part (not the whole line)
grep -P – use PERL regular expression instead of basic (allows usage of \d)
git branch -D – force delete unmerged branch (needed if build server squash commits when completing pull request)
paste -sd "," .tmp – read .tmp file and join lines using comma

grep, cut, awk, xargs, paste are linux commands but they are distributed with Git for Windows (same folder as git.exe)

Advertisements
Command line

git console: changing brach interactively

Some time ago I wrote about some helpful script that let me copy branch name without mouse. Later on I wrote about some simple console menu in Powershell. The second one seems to be a far better option here. Here’s the code:

#uncomment if you don't have ps-menu (powershellget is needed)
#install-module ps-menu -scope currentuser

$selection = menu (iex "git branch")
if ($selection -ne $null)
{
    $branch = $selection.Trim(" ", "*")
    iex "git checkout $branch"
}

Here is how it works:

branching

Command line, Tips & tricks

Investigating “git log” without mouse

As I’m a huge fun of doing the job using keyboard only, I’ve got addicted to VsVim, vim mode using CapsLock, then git console. Unfortunately there were still some actions that were “uncomfortable” like for example investigating “git log“. In order to copy a part of previous commit message or commit id I had to use both mouse and keyboard. Finally I have figured out that the solution is really simple: pipe the result to vim:

git log | vim --noplugin -

- is to tell vim to read from stdin
--noplugin is used to speed up load time and reset some strange behaviour, but you can live without it I think

As it’s not very short, best to have it as console alias:

doskey gl=git log | vim --noplugin -

Here you have some demo:

gif_im_color_dither_16_20a833df54

My journey to figure it out was not that straightforward, as I thought it should be some feature/plugin to my terminal (ConEmu at home, Console2 at work) – nothing found. Then I tried to use VIM as terminal (using ConqueTerm) but not everything worked there (i.e. Yeoman) and finally git commit opened “vim” inside of console in vim … “Vimception” ]:-)>

Finally we can use the same trick in several other places like
git branch – copy branch name for further use in git checkout
git status – copy file name to add/reset/checkout changes
and more…