#!/bin/sh clean= next=next branch=`git symbolic-ref HEAD` while case $# in 0) break ;; esac do case "$1" in --clean) test refs/heads/master = "$branch" || { echo >&2 Not on master exit 1 } clean=t ;; --next) test 2 -le $# || { echo >&2 "Need argument" exit 1 } next="$2" git rev-parse --verify "$next" >/dev/null || exit shift ;; *) echo >&2 "$0 [--clean | --next test-next ]" exit 1 ;; esac shift done master_sha1=`git rev-parse --verify refs/heads/master` LF=' ' (cd .git/refs/heads && find -type f) | sed -n \ -e 's/^\.\///' \ -e '/^[^\/][^\/]\//p' | sort | while read topic do rebase= done= not_done= trouble= date= topic_sha1=`git rev-parse --verify "refs/heads/$topic"` is_current= if test "refs/heads/$topic" = "$branch" then is_current=" *" fi date=` git-rev-list -1 --pretty "$topic" | sed -ne 's/^Date: *\(.*\)/ (\1)/p' ` # (1) only_next_1=`git-rev-list ^master "^$topic" ${next} | sort` only_next_2=`git-rev-list ^master ${next} | sort` if test "$only_next_1" = "$only_next_2" then not_in_topic=`git-rev-list "^$topic" master` if test -z "$not_in_topic" then rebase=" (vanilla)" else rebase=" (can be rebased)" fi fi # (2) not_in_master=` git-rev-list ^master "$topic" ` test -z "$not_in_master" && done="${LF}Fully merged -- delete." # (3) not_in_next=` git-rev-list --pretty=oneline ^${next} "$topic" | sed -e 's/^[0-9a-f]* / - /' ` if test -n "$not_in_next" then if test -n "$done" then # If $topic and master are the same, # it is fine. test "$master_sha1" = "$topic_sha1" || trouble="${LF}### MODIFIED AFTER COOKED ###" fi not_done="${LF}Still not merged in ${next}$rebase.$LF$not_in_next" elif test -n "$done" then not_done= else not_done="${LF}Up to date." fi echo "*** $topic ***$date$is_current$trouble$done$not_done" if test -z "$trouble$not_done" && test -n "$done" && test t = "$clean" then git branch -d "$topic" fi done exit ################################################################ Using Topic Branches Some important disciplines first. * Once a topic branch forks from "master", never merge "master" updates into the topic branch. * Once a topic branch is fully cooked and merged into "master", delete it. If you need to build on top of it to correct earlier mistakes, create a new topic branch by forking at the tip of the "master". This is not strictly necessary, but it makes it easier to keep your history simple. * Whenever you need to test or publish your changes to topic branches, merge them into "next" branch. So, you would want to know: (1) ... if a topic branch has ever been merged to "next". Young topic branches can have stupid mistakes you would rather clean up, and things that have not been merged into other branches can be easily rebased without affecting others. (2) ... if a topic branch has been fully merged to "master". Then you can delete it. More importantly, you can tell you should not build on top of it. (3) ... if a topic branch has commits unmerged to "next". You need to merge them to test and/or publish. Let's look at this example: o---o---o---o---o---o---o---o---o---o "next" / / / / / a---a---b A / / / / / / / / c---c---c---c B / / / / \ / / / / b---b C \ / / / / / \ / ---o---o---o---o---o---o---o---o---o---o---o "master" A, B and C are topic branches. * A has one fix since it was merged up to "next". * B has finished. It has been fully merged up to "master" and "next", and is ready to be deleted. * C has not merged to "next" at all. To compute (1): git-rev-list ^master ^topic next git-rev-list ^master next if these match, topic has not merged in next at all. To compute (2): git-rev-list master..topic if this is empty, it is fully merged to "master". To compute (3): git-rev-list next..topic if this is empty, there is nothing to merge to "next".