I need to delete old and unmaintained branches from our remote repository. I\'m trying to find a way with which to list the remote branches by their last modified date, and
I did a simple alias, not sure if this is what exactly asked, but it is simple
I did this as i wanted to list all the branches not just my local branches, which above commands do only
alias git_brs="git fetch && git branch -av --format='\''%(authordate)%09%(authordate:relative)%09%(refname)'\'"
You can pipe above to grep origin
to get only origins
This lists all the branches along with the last date modified, helps me decide which one i should pull for latest version
This results in below type of display
Wed Feb 4 23:21:56 2019 +0230 8 days ago refs/heads/foo
Tue Feb 3 12:18:04 2019 +0230 10 days ago refs/heads/master
Mon Feb 9 12:19:33 2019 +0230 4 days ago refs/heads/bar
Wed Feb 11 16:34:00 2019 +0230 2 days ago refs/heads/xyz
Tue Feb 3 12:18:04 2019 +0230 10 days ago refs/remotes/origin/HEAD
Mon Feb 9 12:19:33 2019 +0230 4 days ago refs/remotes/origin/foo
Tue Feb 3 12:18:04 2019 +0230 10 days ago refs/remotes/origin/master
Tue Feb 3 12:18:04 2019 +0230 10 days ago refs/remotes/origin/bar
Tue Feb 3 12:18:04 2019 +0230 10 days ago refs/remotes/origin/xyz
Try and let me know if it helped, happy gitting
commandlinefu has 2 interesting propositions:
for k in `git branch | perl -pe s/^..//`; do echo -e `git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1`\\t$k; done | sort -r
or:
for k in `git branch | sed s/^..//`; do echo -e `git log -1 --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k --`\\t"$k";done | sort
That is for local branches, in a Unix syntax. Using git branch -r
, you can similarly show remote branches:
for k in `git branch -r | perl -pe 's/^..(.*?)( ->.*)?$/\1/'`; do echo -e `git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1`\\t$k; done | sort -r
Michael Forrest mentions in the comments that zsh requires escapes for the sed
expression:
for k in git branch | perl -pe s\/\^\.\.\/\/; do echo -e git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1\\t$k; done | sort -r
kontinuity adds in the comments:
If you want to add it your zshrc the following escape is needed.
alias gbage='for k in `git branch -r | perl -pe '\''s/^..(.*?)( ->.*)?$/\1/'\''`; do echo -e `git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1`\\t$k; done | sort -r'
In multiple lines:
alias gbage='for k in `git branch -r | \
perl -pe '\''s/^..(.*?)( ->.*)?$/\1/'\''`; \
do echo -e `git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | \
head -n 1`\\t$k; done | sort -r'
Note: n8tr's answer, based on git for-each-ref refs/heads is cleaner. And faster.
See also "Name only option for git branch --list?"
More generally, tripleee reminds us in the comments:
- Prefer modern
$(command substitution)
syntax over obsolescent backtick syntax.
(I illustrated that point in 2014 with "What is the difference between $(command) and `command` in shell programming?")
- Don't read lines with for.
- Probably switch to
git for-each-ref refs/remote
to get remote branch names in machine-readable format
I made two variants, based on VonC's answer.
My first variant:
for k in `git branch -a | sed -e s/^..// -e 's/(detached from .*)/HEAD/'`; do echo -e `git log -1 --pretty=format:"%Cgreen%ci |%Cblue%cr |%Creset$k |%s" $k --`;done | sort | column -t -s "|"
This handles local and remote branches (-a
), handles detached-head state (the longer sed command, though the solution is kind of crude -- it just replaces the detached branch info with the keyword HEAD), adds in the commit subject (%s), and puts things into columns via literal pipe characters in the format string and passing the end result to column -t -s "|"
. (You could use whatever as the separator, as long as it's something you don't expect in the rest of the output.)
My second variant is quite hacky, but I really wanted something that still has an indicator of "this is the branch you're currently on" like the branch command does.
CURRENT_BRANCH=0
for k in `git branch -a | sed -e 's/\*/CURRENT_BRANCH_MARKER/' -e 's/(detached from .*)/HEAD/'`
do
if [ "$k" == 'CURRENT_BRANCH_MARKER' ]; then
# Set flag, skip output
CURRENT_BRANCH=1
elif [ $CURRENT_BRANCH == 0 ]; then
echo -e `git log -1 --pretty=format:"%Cgreen%ci |%Cblue%cr |%Creset$k |%s" $k --`
else
echo -e `git log -1 --pretty=format:"%Cgreen%ci |%Cblue%cr |%Creset* %Cgreen$k%Creset |%s" $k --`
CURRENT_BRANCH=0
fi
done | sort | column -t -s "|"
This turns the *
that marks the current branch into a keyword, and when the loop body sees the keyword it instead sets a flag and outputs nothing. The flag is used to indicate that an alternate formatting should be used for the next line. Like I said, it is totally hacky, but it works! (Mostly. For some reason, my last column is getting outdented on the current branch line.)
In PowerShell, the following shows branches on the remote that are already merged and at least two weeks old (the author:relative
format starts displaying weeks instead of days at two weeks):
$safeBranchRegex = "origin/(HEAD|master|develop)$";
$remoteMergedBranches = git branch --remote --merged | %{$_.trim()};
git for-each-ref --sort='authordate:iso8601' --format=' %(authordate:relative)%09%(refname:short)' refs/remotes | ?{$_ -match "(weeks|months|years) ago" -and $_ -notmatch "origin/(HEAD|master|qa/)"} | %{$_.substring($_.indexof("origin/"))} | ?{$_ -in $remoteMergedBranches}
Or you can use my PHP script, https://gist.github.com/2780984
#!/usr/bin/env php
<?php
$local = exec("git branch | xargs $1");
$lines = explode(" ", $local);
$limit = strtotime("-2 week");
$exclude = array("*", "master");
foreach ($exclude as $i) {
$k = array_search($i, $lines);
unset($lines[$k]);
}
$k = 0;
foreach ($lines as $line) {
$output[$k]['name'] = $line;
$output[$k]['time'] = exec('git log '.$line.' --pretty=format:"%at" -1');
if ($limit>$output[$k]['time']) {
echo "This branch should be deleted $line\n";
exec("git branch -d $line");
}
$k++;
}
?>
Building off of Olivier Croquette, I like using a relative date and shortening the branch name like this:
git for-each-ref --sort='-authordate:iso8601' --format=' %(authordate:relative)%09%(refname:short)' refs/heads
Which gives you output:
21 minutes ago nathan/a_recent_branch
6 hours ago master
27 hours ago nathan/some_other_branch
29 hours ago branch_c
6 days ago branch_d
I recommend making a Bash file for adding all your favorite aliases and then sharing the script out to your team. Here's an example to add just this one:
#!/bin/sh
git config --global alias.branches "!echo ' ------------------------------------------------------------' && git for-each-ref --sort='-authordate:iso8601' --format=' %(authordate:relative)%09%(refname:short)' refs/heads && echo ' ------------------------------------------------------------'"
Then you can just do this to get a nicely formatted and sorted local branch list:
git branches