├── LICENSE ├── README.md └── git-my /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2020 davidosomething 2 | 3 | Permission is hereby granted, 4 | free of charge, to any person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to the 9 | following conditions: 10 | 11 | The above copyright notice and this permission notice 12 | (including the next paragraph) shall be included in all copies or substantial 13 | portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # git-my 2 | 3 | Lists all of a user's branches, including local and remote, and shows: 4 | 5 | - if a remote branch is checked out locally 6 | - if a local branch is tracked remotely 7 | - if a local branch is up-to-date with a specified branch 8 | 9 | | Name | Link | 10 | | ------------- | -------- | 11 | | Project Home: | [https://github.com/davidosomething/git-my](https://github.com/davidosomething/git-my) 12 | 13 | ## About 14 | 15 | Unlike `git branch -r` and `git remote show origin` this shows only branches 16 | where YOU were the last committer and it will tell you if you have a copy of 17 | that branch locally. 18 | 19 | Here's a screenshot: 20 | ![Example output](https://raw.githubusercontent.com/davidosomething/git-my/docs/screenshot.png) 21 | 22 | ## Installation 23 | 24 | - Requires BASH 4+ 25 | - Add the `git-my` file somewhere in your path, e.g. `/usr/local/bin/` 26 | 27 | ### zplug installation 28 | 29 | Super simple if you use zplug: 30 | 31 | ```sh 32 | zplug "davidosomething/git-my", as:command 33 | ``` 34 | 35 | ## Usage 36 | 37 | From command line execute: 38 | 39 | git my 40 | 41 | ### Options 42 | 43 | #### Remote comparison branch 44 | 45 | git my origin/qa 46 | 47 | will give you a list of your remote branches and tell you which ones have been 48 | merged into `origin/qa`. 49 | 50 | ## To do 51 | 52 | -v|--version 53 | -h|--help 54 | -U|--username 55 | --local 56 | --merged 57 | --tracked 58 | 59 | 60 | ---- 61 | 62 | Copyright (c) 2017 David O'Trakoun 63 | 64 | -------------------------------------------------------------------------------- /git-my: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # git-my v1.1.2 4 | # 5 | # Lists a user's remote branches and shows if it was merged 6 | # and/or available locally. 7 | # 8 | # Copyright (c) 2017 David O'Trakoun 9 | # 10 | # Requires: 11 | # bash 4+ for readarray 12 | # 13 | # Usage: 14 | # git my 15 | # 16 | 17 | # is_repository 18 | # 19 | # Exits with error if not a git repository 20 | # 21 | is_repository() { 22 | git rev-parse --git-dir >/dev/null 2>&1 23 | } 24 | 25 | # get_local_branches 26 | # 27 | # Proper way to get a porcelain list of local branches for shell script use 28 | # 29 | get_local_branches() { 30 | local fmt 31 | local cmd_get_local_branches 32 | 33 | fmt=" 34 | r=%(refname) 35 | refname_without_prefix=\${r#refs/heads/} 36 | printf \"%s\\t%s\\n\" \"\$refname_without_prefix\" 37 | " 38 | 39 | cmd_get_local_branches=$( 40 | git for-each-ref --shell --format="$fmt" refs/heads 41 | ) 42 | 43 | eval "$cmd_get_local_branches" 44 | } 45 | 46 | # get_everyones_remotes 47 | # 48 | # Get porcelain list of all remote branches 49 | # 50 | # @TODO support remote_name (currently origin) 51 | # 52 | get_everyones_remotes() { 53 | local fmt 54 | local cmd_everyones_remotes 55 | 56 | local remote_name 57 | remote_name=${1:-"origin"} 58 | 59 | # user.namebranch.name 60 | # the TAB is used as a delimiter to awk with 61 | fmt=" 62 | a=%(authorname) 63 | r=%(refname) 64 | refname_without_prefix=\${r#refs/remotes/\${remote_name}/} 65 | printf \"%s\\t%s\\n\" \"\$a\" \"\$refname_without_prefix\" 66 | " 67 | 68 | cmd_everyones_remotes=$( 69 | git for-each-ref --shell --format="$fmt" "refs/remotes/$remote_name" 70 | ) 71 | 72 | eval "$cmd_everyones_remotes" 73 | } 74 | 75 | # get_merged_remote_branches 76 | # 77 | # @param string optional remote to list merged branches of. Defaults to 78 | # "origin/master" 79 | # @output names of remote branches that are merged into given branch 80 | get_merged_remote_branches() { 81 | local remote 82 | local remote_name 83 | #local remote_refname 84 | local merged_remote_branches 85 | local stripped_branchnames 86 | 87 | remote=${1:-"origin/master"} 88 | # trim from end of string until / for the remote name 89 | remote_name=${remote%/*} 90 | # trim from beginning of string until / for the remote refname 91 | #remote_refname=${remote#*/} 92 | 93 | merged_remote_branches=$( \ 94 | git branch --no-color \ 95 | --remotes \ 96 | --merged "$remote" \ 97 | ) 98 | 99 | # remove "origin/" 100 | stripped_branchnames=$( \ 101 | echo "$merged_remote_branches" | sed "s/^\s*$remote_name\///g" \ 102 | ) 103 | 104 | echo "$stripped_branchnames" 105 | } 106 | 107 | # filter_mine 108 | # 109 | # @param git_user 110 | # @param branchnames 111 | # @output branchnames owned by current git user 112 | filter_mine() { 113 | local git_user 114 | local branchnames 115 | git_user=$1 116 | branchnames=$2 117 | 118 | # use eval to not parse the fmt string 119 | local my_remotes 120 | my_remotes=$(echo "$branchnames" | grep -i "^$git_user") 121 | 122 | # output only the branchname 123 | echo "$my_remotes" | grep -v "HEAD" | awk -F'\t' '{ print $2 }' 124 | } 125 | 126 | # merge_lists 127 | # 128 | # Convert two lists to bash arrays, merge them, sort them 129 | # 130 | # @param list1 131 | # @param list2 132 | merge_lists() { 133 | local l1 134 | local l2 135 | l1=$(echo "$1" | awk '{$1=$1};1') 136 | l2=$(echo "$2" | awk '{$1=$1};1') 137 | 138 | readarray -t a1 <<< "$l1" 139 | readarray -t a2 <<< "$l2" 140 | 141 | local merged 142 | merged=( "${a1[@]}" "${a2[@]}" ) 143 | 144 | local intersect 145 | readarray -t intersect < <(printf '%s\n' "${merged[@]}" | sort -u) 146 | echo "${intersect[@]}" 147 | } 148 | 149 | # decorate_merged 150 | # 151 | # @param string my_branches list of remote branch names owned by me 152 | # @param string local_branches list of local branch names 153 | # @param string merged_remote_branches list of all remote branch names merged 154 | # into another branch 155 | # @output table of branch names and status 156 | decorate_merged() { 157 | local my_branches 158 | readarray -t my_branches <<< "$(echo "$1" | tr ' ' '\n' )" 159 | 160 | local local_branches=$2 161 | local merged_remote_branches=$3 162 | 163 | local normal 164 | local magenta 165 | normal=$(tput sgr0) 166 | magenta=$(tput setaf 5) 167 | 168 | local clreol 169 | clreol=$'\x1B[K' 170 | 171 | # heading 172 | echo 'local | tracked | merged' 173 | echo '----- + ------- + ------' 174 | 175 | # body 176 | local zebra=0 177 | local last=$((${#my_branches[@]} - 1)) 178 | for i in "${!my_branches[@]}"; do 179 | local branchname="${my_branches[$i]}" 180 | local decorated='' 181 | local is_local=' ' 182 | local is_tracked=' ' 183 | local is_merged=' ' 184 | 185 | echo "$local_branches" | grep -q "$branchname" && \ 186 | is_local='✓' 187 | 188 | if git rev-parse --symbolic-full-name "$branchname@{u}" >/dev/null 2>&1; then 189 | is_tracked='✓' 190 | fi 191 | 192 | echo "$merged_remote_branches" | grep -q "$branchname" && \ 193 | is_merged='✓' 194 | 195 | if [ "$branchname" == "$(git symbolic-ref --short HEAD)" ]; then 196 | branchname="${magenta}${branchname}" 197 | else 198 | branchname="${branchname}" 199 | fi 200 | 201 | decorated=$(printf " %s | %s | %s %s" \ 202 | "$is_local" "$is_tracked" "$is_merged" "$branchname") 203 | 204 | if [ $zebra = 0 ]; then 205 | echo -en "${normal}" 206 | printf "\e[48;5;0m%s %s\n" "$decorated" "$clreol" 207 | zebra=1 208 | else 209 | echo -en "${normal}" 210 | printf "\e[48;5;236m%s %s" "$decorated" "$clreol" 211 | if [[ "$i" != "$last" ]]; then printf "\n"; fi 212 | zebra=0 213 | fi 214 | done 215 | 216 | printf "\e[48;5;0m\n" 217 | } 218 | 219 | main() { 220 | local decorated 221 | local everyones_remotes 222 | local git_remote 223 | local git_user 224 | local local_branches 225 | local merged_remote_branches 226 | local my_branches 227 | local my_remotes 228 | 229 | local remote=${1:-"origin/master"} 230 | 231 | # trim from end of string until / for the remote name 232 | local remote_name=${remote%/*} 233 | # trim from beginning of string until / for the remote refname 234 | local remote_ref=${remote#*/} 235 | 236 | git_user=$(git config --get user.name) 237 | if [ -z "$git_user" ]; then 238 | echo "ERROR: user.name is not set in git config" 239 | exit 1 240 | fi 241 | 242 | git_remote=$(git config --get "remote.${remote_name}.url") 243 | if [ -z "$git_remote" ]; then 244 | echo "ERROR: remote.${remote_name}.url is not set" 245 | exit 1 246 | fi 247 | 248 | everyones_remotes=$(get_everyones_remotes "$remote_name") 249 | my_remotes=$(filter_mine "$git_user" "$everyones_remotes") 250 | merged_remote_branches=$(get_merged_remote_branches "$remote_name/$remote_ref") 251 | local_branches=$(get_local_branches) 252 | my_branches=$(merge_lists "$my_remotes" "$local_branches") 253 | 254 | echo 255 | echo "branches owned by user: $git_user" 256 | echo "compare local/merge to: $remote" 257 | echo "in remote repository: $git_remote" 258 | echo 259 | 260 | decorate_merged "$my_branches" "$local_branches" "$merged_remote_branches" 261 | 262 | echo 263 | } 264 | 265 | if ! is_repository; then 266 | echo "This is not a git repository." 267 | exit 1 268 | fi 269 | 270 | main "$@" 271 | 272 | --------------------------------------------------------------------------------