Last active
August 29, 2015 14:25
-
-
Save blech75/5f9c6e56a670f7e453d3 to your computer and use it in GitHub Desktop.
findgitrepos: For a given path, find all child dirs that appear to be local git repos and print their user config and a list of remotes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # findgitrepos | |
| # | |
| # For a given path, find all child dirs that appear to be local Git repos and | |
| # print some useful info (user config, list of remotes, hooks). Allows you to | |
| # review your Git config across many projects. Called without arguments, it | |
| # looks in $HOME. | |
| # | |
| # Author: justin@worksperfectly.net | |
| # | |
| # TODO | |
| # ==== | |
| # | |
| # * colorize output to make it easier to read | |
| # * add ability to pass in more $IGNORE_PATHS | |
| # * add help/usage on command line | |
| # * better error handling | |
| # * add support for bare repos | |
| # * address NOTEs and FIXMEs below | |
| # * add option to just print dirs found (for passing to other tools) | |
| # * possibly add other useful Git info/config | |
| # * add option to output only specified info (ex: name/email only) | |
| # * add option to filter list based on matching certain values for each kind of | |
| # info | |
| # default to looking in your home dir | |
| # FIXME: does this make sense? should it just be the current dir? real question | |
| # is: is it a 'global' tool? or a 'local' tool? | |
| if [[ $# -eq 0 ]]; then | |
| TARGET_PATH=$HOME | |
| elif [[ $# -eq 1 ]]; then | |
| if [[ ! -d $1 ]]; then | |
| echo "Error: '$1' is not a directory." >&2 | |
| exit 1 | |
| fi | |
| # resolve symlinks of passed dir to be root-relative. | |
| # NOTE: may want to make this optional, especially w/r/t/ it being | |
| # root-relative since it would affect $IGNORE_PATHS. (see below) | |
| TARGET_PATH="$( cd $1 ; pwd -P )" | |
| else | |
| echo "Usage: "`basename "$0"` "TARGET_PATH" >&2 | |
| exit 1 | |
| fi | |
| function indent_values { | |
| sed -e 's/^/ /' | |
| } | |
| # http://stackoverflow.com/a/17841619/2284440 | |
| function join { local IFS="$1"; shift; echo "$*"; } | |
| # space-seprarted paths to ignore | |
| IGNORE_PATHS="" | |
| # if we're looking in $HOME, we don't care about things in ~/Library | |
| if [ $TARGET_PATH = $HOME ]; then | |
| # FIXME: this is rather platform-specific | |
| IGNORE_PATHS=$HOME/Library/ | |
| fi | |
| # array of args to pass to `find` | |
| # see http://wiki.bash-hackers.org/syntax/quoting#comment_53ff2ad966e2d5e9ace47c4c025c34fb | |
| FIND_IGNORE_ARGS=() | |
| # compose list of arguments based on $IGNORE_PATHS | |
| # NOTE: does not account for spaces in paths | |
| for p in $IGNORE_PATHS; do | |
| # IGNORE_PATHS are currently root-relative because we resolve symlinks when | |
| # setting TARGET_PATH. this allows us to anchor the -path at the beginning. | |
| FIND_IGNORE_ARGS=(${FIND_IGNORE_ARGS[@]} -not -path '${p}.*') | |
| done | |
| # pull name/email from global git config for late comparison to each git repo's | |
| # local config | |
| global_name="`git config --global user.name`" | |
| global_email="`git config --global user.email`" | |
| # from `man githook` (as of 2.4.5) | |
| ALL_GIT_HOOKS=(applypatch-msg | |
| pre-applypatch | |
| post-applypatch | |
| pre-commit | |
| prepare-commit-msg | |
| commit-msg | |
| post-commit | |
| pre-rebase | |
| post-checkout | |
| post-merge | |
| pre-push | |
| pre-receive | |
| update | |
| post-receive | |
| post-update | |
| push-to-checkout | |
| pre-auto-gc | |
| post-rewrite) | |
| FIND_GITHOOK_REGEXP="(`join '|', "${ALL_GIT_HOOKS[@]}"`)" | |
| # FIXME: hardcode the regexp. bash string quoting and interpolation is killing | |
| # me. i give up. | |
| # FIND_GITHOOK_ARGS=(-regex '.*/${FIND_GITHOOK_REGEXP}\$') | |
| FIND_GITHOOK_ARGS=(-regex '.*/(applypatch-msg|pre-applypatch|post-applypatch|pre-commit|prepare-commit-msg|commit-msg|post-commit|pre-rebase|post-checkout|post-merge|pre-push|pre-receive|update|post-receive|post-update|push-to-checkout|pre-auto-gc|post-rewrite)$') | |
| # perform the find, locating all GIT_DIRs in target dir. report the parent dir | |
| # of the GIT_DIR. pipe find's output so we display results immediately and | |
| # incrementally. | |
| echo "Finding directories that appear to be Git repos in $TARGET_PATH..." | |
| echo | |
| find $TARGET_PATH -type d -path '*/.git' ${FIND_IGNORE_ARGS[@]} -exec dirname '{}' \; | \ | |
| while read g; do | |
| # TODO: option to display full path or relative path | |
| echo $g | |
| # descend into the git project via pushd to extract data. | |
| # TODO: maybe use GIT_DIR to avoid pushd/popd | |
| # | |
| # quote the dir to deal with spaces | |
| pushd "$g" > /dev/null | |
| # get our local name/email from the repo's git config | |
| local_name="`git config --local user.name`" | |
| local_email="`git config --local user.email`" | |
| echo -n " user: " | |
| # FIXME: treat name and email independently w/r/t global vs. local | |
| if [[ ("$local_name" = "") && ("$local_email" = "") ]]; then | |
| echo "$global_name <$global_email> (using global config)" | |
| elif [[ ("$global_name" = "$local_name") && ("$global_email" = "$local_email") ]]; then | |
| echo "$local_name <$local_email> (same as global config)" | |
| else | |
| echo "$local_name <$local_email>" | |
| fi | |
| # get all the unique remotes (collapsing get/push) and format them in | |
| # columns with an indent | |
| remotes="`git remote -v | awk '{ print $1" "$2 }' | sort | uniq | column -t`" | |
| echo -n " remotes: " | |
| if [[ "$remotes" =~ ^\s*$ ]]; then | |
| echo "(none)" | |
| else | |
| echo "$remotes" | wc -l | (read n; echo "($n)") | |
| echo "$remotes" | indent_values | |
| fi | |
| # get all the active hooks (files or symlinks) in the current repo. | |
| hooks="`find -E .git/hooks \( -type f -or -type l \) -perm -u+x -not -name '*.sample' ${FIND_GITHOOK_ARGS[@]} | sed -e 's/.git\/hooks\///g'`" | |
| echo -n " hooks: " | |
| if [[ "$hooks" =~ ^\s*$ ]]; then | |
| echo "(none)" | |
| else | |
| echo "$hooks" | wc -l | (read n; echo "($n)") | |
| { | |
| for h in $hooks; do | |
| link="`readlink .git/hooks/$h`" | |
| if [ "$link" != "" ]; then | |
| echo "$h (-> $link)" | |
| else | |
| echo $h | |
| fi | |
| done | |
| } | indent_values | |
| fi | |
| echo | |
| popd > /dev/null | |
| done | |
| exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment