Last active
October 11, 2026 17:33
-
-
Save viatcheslavmogilevsky/e45bc2788f796b90220e4a0ff3be635f to your computer and use it in GitHub Desktop.
Checking locally cloned repos for smth to cleanup
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
| #!/bin/bash -e | |
| # Usage: | |
| # ./local-repos-check.sh ~/directory-which-has-cloned-repos | |
| # skipping network heavy checks: | |
| # SKIP_GETTING_DEFAULT_BRANCH_FROM_REMOTE=1 SKIP_GETTING_REMOTE_PRUNABLE_BRANCHES=1 ./local-repos-check.sh ~/directory-which-has-cloned-repos | |
| bgreen () { printf "\e[1m\e[32m" ; $@ ; printf "\e[0m"; } | |
| red () { printf "\e[31m" ; $@ ; printf "\e[0m"; } | |
| byellow () { printf "\e[33m" ; $@ ; printf "\e[0m"; } | |
| conditionalcolored() { | |
| if [[ "$@" == "0" ]]; then | |
| bgreen echo "$@" | |
| elif [[ "$@" == "N/A" ]]; then | |
| byellow echo "$@" | |
| elif [[ "$@" =~ ^main ]]; then | |
| bgreen echo "$@" | |
| else | |
| red echo "$@" | |
| fi | |
| } | |
| # # To debug particular directory/clone: | |
| # for dir in $(find "$1" -type d -depth 1 | grep -F "dir-to-debug"); do | |
| for dir in $(find "$1" -type d -depth 1); do | |
| echo "---------------------" | |
| echo "Directory: ${dir}" | |
| # To debug: | |
| # set -x | |
| GIT_NON_EMPTY_STATUS="N/A" | |
| NON_STANDARD_BRANCHES_COUNT="N/A" | |
| CURRENT_BRANCH_IS_NON_STARDARD="N/A" | |
| PRUNABLE_BRANCHES_COUNT="N/A" | |
| PRUNABLE_OBJ_COUNT="N/A" | |
| SIZE_TO_BE_CLEANED_UP="N/A" | |
| GIT_STASH_COUNT="N/A" | |
| unset UNAVAILABLE_REMOTES | |
| UNAVAILABLE_REMOTES_COUNT=0 | |
| set +e | |
| GIT_RAW_STATUS=$(git -C $dir status 2>&1) | |
| GIT_STATUS_FAILED="${?}" | |
| set -e | |
| if [ "${GIT_STATUS_FAILED}" -eq 0 ]; then | |
| # Empty/non-empty status: | |
| set +e | |
| echo $GIT_RAW_STATUS | grep -q -F "nothing to commit, working tree clean" > /dev/null 2>&1 | |
| GREP_FAILED="${?}" | |
| set -e | |
| GIT_NON_EMPTY_STATUS="0" | |
| if [ "${GREP_FAILED}" -ne 0 ]; then | |
| GIT_NON_EMPTY_STATUS="1" | |
| fi | |
| # Does it have non-standard branch: | |
| REMOTES=$(git -C $dir remote) | |
| DEFAULT_BRANCHES="" | |
| if [ -z "$SKIP_GETTING_DEFAULT_BRANCH_FROM_REMOTE" ]; then | |
| for remote in $(echo $REMOTES); do | |
| set +e | |
| def_branch_output=$(git -C $dir remote show $remote 2>&1) | |
| def_branch_status="${?}" | |
| set -e | |
| if [ "${def_branch_status}" -eq 0 ]; then | |
| def_branch=$(echo "$def_branch_output" | sed -n '/HEAD branch/s/.*: //p') | |
| DEFAULT_BRANCHES="${DEFAULT_BRANCHES}|${def_branch}" | |
| else | |
| set +e | |
| echo $def_branch_output | grep -q -F "ERROR: Repository not found" > /dev/null 2>&1 | |
| def_branch_output_grep_failed="${?}" | |
| set -e | |
| if [ "${def_branch_output_grep_failed}" -eq 0 ]; then | |
| UNAVAILABLE_REMOTES+=("${remote}") | |
| UNAVAILABLE_REMOTES_COUNT=$((UNAVAILABLE_REMOTES_COUNT+1)) | |
| # echo "Oops, repo is not available" | |
| # break | |
| else | |
| echo "Oops, something wrong" | |
| fi | |
| fi | |
| done | |
| fi | |
| DEFAULT_BRANCHES="^(main|master|develop|dev|staging${DEFAULT_BRANCHES})\$" | |
| NON_STANDARD_BRANCHES=$(git -C $dir branch | sed -E 's/^( |\*)+//g' | grep -v -E "$DEFAULT_BRANCHES" || true) | |
| if [ ! -z "$NON_STANDARD_BRANCHES" ]; then | |
| NON_STANDARD_BRANCHES_COUNT=$(echo "$NON_STANDARD_BRANCHES" | wc -l | sed 's/^ *//') | |
| else | |
| NON_STANDARD_BRANCHES_COUNT=0 | |
| fi | |
| # Whether current branch is non-standard: | |
| CURRENT_BRANCH_IS_NON_STARDARD=$(git -C $dir branch | grep -E '^\* +' | sed -E 's/^( |\*)+//g' | grep -v -E "$DEFAULT_BRANCHES" | wc -l | sed 's/^ *//') | |
| # Prunable branches: | |
| PRUNABLE_BRANCHES_COUNT=0 | |
| if [ -z "$SKIP_GETTING_REMOTE_PRUNABLE_BRANCHES" ]; then | |
| for remote in $(echo $REMOTES); do | |
| set +e | |
| printf "%s\n" "${UNAVAILABLE_REMOTES[@]}" | grep -q -E "^${remote}\$" > /dev/null 2>&1 | |
| unavailable_remotes_search_status="${?}" | |
| set -e | |
| if [ "${unavailable_remotes_search_status}" -ne 0 ]; then | |
| set +e | |
| prunable_branch_output=$(git -C $dir remote prune -n $remote 2>&1) | |
| prunable_branch_status="${?}" | |
| set -e | |
| if [ "${prunable_branch_status}" -eq 0 ]; then | |
| PRUNABLE_BRANCHES_IN_CURRENT_REMOTE=$(echo "$prunable_branch_output" | grep -F "[would prune]" | wc -l | sed 's/^ *//') | |
| PRUNABLE_BRANCHES_COUNT=$(( $PRUNABLE_BRANCHES_COUNT + $PRUNABLE_BRANCHES_IN_CURRENT_REMOTE )) | |
| else | |
| set +e | |
| echo $prunable_branch_output | grep -q -F "ERROR: Repository not found" > /dev/null 2>&1 | |
| prunable_branch_output_grep_failed="${?}" | |
| set -e | |
| if [ "${prunable_branch_output_grep_failed}" -eq 0 ]; then | |
| UNAVAILABLE_REMOTES+=("${remote}") | |
| UNAVAILABLE_REMOTES_COUNT=$((UNAVAILABLE_REMOTES_COUNT+1)) | |
| # echo "Oops, repo is not available" | |
| # break | |
| else | |
| echo "Oops, something wrong" | |
| fi | |
| fi | |
| fi | |
| done | |
| fi | |
| # Prunable objects: | |
| PRUNABLE_OBJ_COUNT=$(git -C $dir prune -n | wc -l | sed 's/^ *//') | |
| SIZE_TO_BE_CLEANED_UP="0.0" | |
| FILES_TO_BE_CLEANED_UP=$(git -C $dir clean --force --force -d -X --dry-run | sed 's|^Would remove ||g') | |
| if [[ $(echo $FILES_TO_BE_CLEANED_UP | tr -d '\n' | wc -c) -ne 0 ]]; then | |
| IGNORED_FILES_TO_KEEP=$(git -C $dir ls-files | grep -E '.example$' | sed 's|\.example$||g') | |
| SIZE_TO_BE_CLEANED_UP=$(comm -23 <(echo "$FILES_TO_BE_CLEANED_UP") <(echo "$IGNORED_FILES_TO_KEEP") | awk -v dir="$dir" '{print dir "/" $0}' | xargs du -s -k | awk '{s+=$1} END {printf "%.1f", s/1024}') | |
| fi | |
| GIT_STASH_COUNT=$(git -C $dir stash list | wc -l | sed 's/^ *//') | |
| # Worktrees: | |
| WORKTREE_STATUS="main worktree, there is no linked worktrees" | |
| WORKTREE_OUTPUT=$(git -C $dir worktree list | awk '{print $1}') | |
| WORKTREE_COUNT=$(echo "$WORKTREE_OUTPUT" | wc -l) | |
| if [ "$WORKTREE_COUNT" -ne 1 ]; then | |
| WORKTREE_MAIN_PATH=$(echo "$WORKTREE_OUTPUT" | head -n 1) | |
| set +e | |
| echo "$WORKTREE_MAIN_PATH" | grep -q -F "${dir}" > /dev/null 2>&1 | |
| worktree_output_grep_failed="${?}" | |
| set -e | |
| if [ "${worktree_output_grep_failed}" -ne 0 ]; then | |
| WORKTREE_STATUS="linked worktree of ${WORKTREE_MAIN_PATH}" | |
| else | |
| WORKTREE_COUNT=$((WORKTREE_COUNT-1)) | |
| WORKTREE_STATUS="main worktree, linked worktrees count ${WORKTREE_COUNT}" | |
| fi | |
| fi | |
| fi | |
| OVERALL_DISK_USAGE=$(du -s -k $dir | awk '{printf "%.1f\n", $1/1024}') | |
| # To debug: | |
| # set +x | |
| printf "non-empty git status: "; conditionalcolored $GIT_NON_EMPTY_STATUS | |
| printf "count of non-standard branches: "; conditionalcolored $NON_STANDARD_BRANCHES_COUNT | |
| printf "current branch is non-standard: "; conditionalcolored $CURRENT_BRANCH_IS_NON_STARDARD | |
| printf "count of prunable branches: "; conditionalcolored $PRUNABLE_BRANCHES_COUNT | |
| printf "count of prunable objects: "; conditionalcolored $PRUNABLE_OBJ_COUNT | |
| printf "size to be cleaned-up: "; echo "${SIZE_TO_BE_CLEANED_UP}" | |
| printf "overall disk usage: "; echo "${OVERALL_DISK_USAGE}" | |
| printf "git stash size: "; conditionalcolored $GIT_STASH_COUNT | |
| printf "unavaliable remotes count: "; conditionalcolored $UNAVAILABLE_REMOTES_COUNT | |
| printf "worktree status: "; conditionalcolored $WORKTREE_STATUS | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment