diff --git a/README.md b/README.md index 5f2345d..7b99727 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,23 @@ -# git-my v0.1 +# git-my v1.0.0 -> Lists (stale) (branches|tags) in the remote named (origin) (where you were -> the last committer) +Lists all of a user's branches, including local and remote, and shows: -[Homepage](https://github.com/davidosomething/git-my) +- if a remote branch is checked out locally +- if a local branch is tracked remotely +- if a local branch is up-to-date with a specified branch + +| Name | Link | +| ---- | ---- | +| Project Home: | [https://github.com/davidosomething/git-my](https://github.com/davidosomething/git-my) ## About -Unlike `git branch -r` this shows only YOUR branches. +Unlike `git branch -r` and `git remote show origin` this shows only branches +where YOU were the last committer and it will tell you if you have a copy of +that branch locally. + +Here's a screenshot: +![Example output](https://raw.githubusercontent.com/davidosomething/git-my/docs/screenshot.png) ## Installation @@ -16,19 +26,29 @@ Unlike `git branch -r` this shows only YOUR branches. ## Usage From command line execute: -``` -git my -``` -## TODO -- Add options: -``` --U|--username="gitconfig user.name" --R|--remote=origin --T|--tags --B|--branches --F|--filter=mine,stale -``` + git my -Copyright (c) 2015 David O'Trakoun +### Options + +#### Remote comparison branch + + git my origin/qa + +will give you a list of your remote branches and tell you which ones have been +merged into `origin/qa`. + +## To do + + -v|--version + -h|--help + -U|--username + --local + --merged + --tracked + + +---- + +Copyright (c) 2015 David O'Trakoun diff --git a/git-my b/git-my index d680093..95c1c36 100755 --- a/git-my +++ b/git-my @@ -1,72 +1,238 @@ #!/usr/bin/env bash -# git-my v0.1 +# git-my v0.4.0 # -# Lists (stale) (branches|tags) in the remote named (origin) (where you were -# the last committer) +# Lists a user's remote branches and shows if it was merged +# and/or available locally # # Copyright (c) 2015 David O'Trakoun # # Usage: # git my # -# Options: -# @todo NOT IMPLEMENTED -# -U|--username="gitconfig user.name" -# -R|--remote=origin -# -T|--tags -# -B|--branches -# -F|--filter=mine,stale -# set -eu +# _check_in_repository +# +# Exits with error if not a git repository +# _check_in_repository() { - git rev-parse --git-dir > /dev/null 2>&1 + git rev-parse --git-dir >/dev/null 2>&1 } -_filter_mine() { - local git_user - git_user=$(git config --get user.name) +# _get_local_branches +# +# Proper way to get a porcelain list of local branches for shell script use +# +_get_local_branches() { + local fmt + local cmd_get_local_branches - # use eval to not parse the fmt string - local my_remotes - my_remotes=$(echo "$1" | grep -i "^$git_user") - echo "$my_remotes" | awk -F'\t' '{ print $2 }' + # shellcheck disable=SC2016 + fmt=' + r=%(refname) + refname_without_prefix=${r#refs/heads/} + printf "%s\t%s\n" "$refname_without_prefix" + ' + + cmd_get_local_branches=$( + git for-each-ref --shell --format="$fmt" refs/heads + ) + + eval "$cmd_get_local_branches" } -# @TODO -_filter_stales() { - local raw_stales - raw_stales=$(git remote prune --dry-run origin) - echo "$raw_stales" -} +# _get_everyones_remotes +# +# Get porcelain list of all remote branches +# +# @TODO support remote_name (currently origin) +# +_get_everyones_remotes() { + local fmt + local cmd_everyones_remotes -_main() { - if ! _check_in_repository ; then - echo "This is not a git repository." - exit 1 - fi + local remote_name + remote_name=${1:-"origin"} # user.namebranch.name # the TAB is used as a delimiter to awk with # shellcheck disable=SC2016 - local fmt=' + fmt=' a=%(authorname) r=%(refname) - b=${r#refs/remotes/origin/} - printf "%s\t%s\n" "$a" "$b" + refname_without_prefix=${r#refs/remotes/$remote_name/} + printf "%s\t%s\n" "$a" "$refname_without_prefix" ' - local cmd_everyones_remotes cmd_everyones_remotes=$( - git for-each-ref --shell --format="$fmt" refs/remotes/origin + git for-each-ref --shell --format="$fmt" "refs/remotes/$remote_name" ) - local everyones_remotes - everyones_remotes=$(eval "$cmd_everyones_remotes") - _filter_mine "$everyones_remotes" + eval "$cmd_everyones_remotes" } -_main +# _get_merged_remote_branches +# +# @param string optional remote to list merged branches of. Defaults to +# "origin/master" +# @output names of remote branches that are merged into given branch +_get_merged_remote_branches() { + local remote + local remote_name + #local remote_refname + local merged_remote_branches + local stripped_branchnames + + remote=${1:-"origin/master"} + # trim from end of string until / for the remote name + remote_name=${remote%/*} + # trim from beginning of string until / for the remote refname + #remote_refname=${remote#*/} + + merged_remote_branches=$( \ + git branch --no-color \ + --remotes \ + --merged "$remote" \ + ) + + # remove "origin/" + # shellcheck disable=SC2001 + stripped_branchnames=$( \ + echo "$merged_remote_branches" | sed "s/^\s*$remote_name\///g" \ + ) + + echo "$stripped_branchnames" +} + +# _filter_mine +# +# @param git_user +# @param branchnames +# @output branchnames owned by current git user +_filter_mine() { + local git_user + local branchnames + git_user=$1 + branchnames=$2 + + # use eval to not parse the fmt string + local my_remotes + my_remotes=$(echo "$branchnames" | grep -i "^$git_user") + + # output only the branchname + echo "$my_remotes" | grep -v "HEAD" | awk -F'\t' '{ print $2 }' +} + +_merge_lists() { + # shellcheck disable=SC2086 + echo $1 $2 | tr ' ' '\n' | sort -u +} + +# _decorate_merged +# +# @param string my_branches list of remote branch names owned by me +# @param string local_branches list of local branch names +# @param string merged_remote_branches list of all remote branch names merged +# into another branch +# @output table of branch names and status +_decorate_merged() { + local my_branches=$1 + local local_branches=$2 + local merged_remote_branches=$3 + + local normal + local magenta + normal=$(tput sgr0) + magenta=$(tput setaf 5) + + local clreol + clreol=$'\x1B[K' + + # heading + echo 'local | tracked | merged' + echo '----- + ------- + ------' + + # body + local zebra=0 + for branchname in $my_branches + do + local decorated='' + local is_local=' ' + local is_tracked=' ' + local is_merged=' ' + + echo "$local_branches" | grep -q "$branchname" && \ + is_local='✓' + + if git rev-parse --symbolic-full-name "$branchname"@{u} >/dev/null 2>&1; then + is_tracked='✓' + fi + + echo "$merged_remote_branches" | grep -q "$branchname" && \ + is_merged='✓' + + if [ "$branchname" == "$(git symbolic-ref --short HEAD)" ]; then + branchname="${magenta}${branchname}${normal}" + else + branchname="${branchname}" + fi + + decorated=$(printf " %s | %s | %s %s" \ + "$is_local" "$is_tracked" "$is_merged" "$branchname") + + if [ $zebra = 0 ]; then + printf "\e[48;5;0m%s %s\n" "$decorated" "$clreol" + zebra=1 + else + printf "\e[48;5;236m%s %s\n" "$decorated" "$clreol" + zebra=0 + fi + done + + printf "\e[48;5;0m" +} + +_main() { + local decorated + local everyones_remotes + local git_remote + local git_user + local local_branches + local merged_remote_branches + local my_remotes + + local remote=${1:-"origin/master"} + + # trim from end of string until / for the remote name + local remote_name=${remote%/*} + # trim from beginning of string until / for the remote refname + local remote_ref=${remote#*/} + + git_user=$(git config --get user.name) + git_remote=$(git config --get "remote.${remote_name}.url") + everyones_remotes=$(_get_everyones_remotes "$remote_name") + my_remotes=$(_filter_mine "$git_user" "$everyones_remotes") + merged_remote_branches=$(_get_merged_remote_branches "$remote_name/$remote_ref") + local_branches=$(_get_local_branches) + my_branches=$(_merge_lists "$my_remotes" "$local_branches") + + echo + echo "branches owned by user: $git_user" + echo "compare local/merge to: $remote" + echo "in remote repository: $git_remote" + echo + + _decorate_merged "$my_branches" "$local_branches" "$merged_remote_branches" + + echo +} + +if ! _check_in_repository ; then + echo "This is not a git repository." + exit 1 +fi + +_main "$@"