Skip to content

Instantly share code, notes, and snippets.

@spyesx
Last active February 24, 2025 13:29
Show Gist options
  • Select an option

  • Save spyesx/a068c93e0dabe731d293c3e7af31e62d to your computer and use it in GitHub Desktop.

Select an option

Save spyesx/a068c93e0dabe731d293c3e7af31e62d to your computer and use it in GitHub Desktop.

Revisions

  1. spyesx revised this gist Feb 24, 2025. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,6 @@
    **Features:**
    Standalone Bash script to recursively scan directories and identify Git repositories with unpushed changes or local modifications.

    ## Features

    * **Recursive Search:** Traverses directories from a configurable root path to find `.git` directories.
    * **Checks for Unpushed Commits:** Detects repositories where the local branch is ahead of the `origin` remote.
  2. spyesx revised this gist Feb 24, 2025. 1 changed file with 19 additions and 0 deletions.
    19 changes: 19 additions & 0 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    **Features:**

    * **Recursive Search:** Traverses directories from a configurable root path to find `.git` directories.
    * **Checks for Unpushed Commits:** Detects repositories where the local branch is ahead of the `origin` remote.
    * **Detects Local Changes:** Reports repositories with uncommitted modifications (staged, unstaged, untracked).
    * **Standalone:** No external dependencies beyond `bash` and `git`.
    * **Configurable Search Path:** Easily modify the `DEFAULT_GIT_CLONE_PATH` variable within the script to change the root directory for searching.
    * **Summary Report:** Provides a summary at the end indicating the total repositories scanned and those with pending changes.
    * **Exit Code:** Returns a non-zero exit code if any repositories with pending changes are found, useful for scripting and automation.

    ## Configuration

    Modify the `DEFAULT_GIT_CLONE_PATH="$HOME/git"` variable at the top of the script to change the default directory to search for Git repositories.

    ## Usage

    1. Save the script to a file (e.g., `git-report.sh`).
    2. Make it executable: `chmod +x git-report.sh`.
    3. Run it: `./git-report.sh`.
  3. spyesx created this gist Feb 24, 2025.
    120 changes: 120 additions & 0 deletions git-report.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,120 @@
    #!/usr/bin/env bash

    # Standalone Git Repository Report Script

    # Configuration (can be modified here directly)
    DEFAULT_GIT_CLONE_PATH="$HOME/repositories" # Default path to search for git repos

    # --- Helper Functions (Standalone implementations) ---

    # For standalone, let's use simple echo with prefixes instead of log_warning/log_info
    log_warning() {
    echo "[WARNING] $@"
    }

    log_info() {
    echo "[INFO] $@"
    }


    # --- Script Functions (Modified for standalone) ---

    fn_git_report__look_for_git_repo_recursively() {
    # inspired by https://stackoverflow.com/a/23344072
    local folder
    local git_repositories
    folder="$1"

    # Find command that handle spaces in paths
    git_repositories=$(find "$folder" -type d \( -exec test -e {}/.ignore \; -prune \) -o \( -exec test -d {}/.git \; -prune -print0 \) | tr '\0' '\n')

    # Filter out empty lines from find output (if any)
    git_repositories=$(echo "$git_repositories" | grep -v '^$')

    echo "$git_repositories"
    }

    fn_git_report__process_repo() {
    local repo_path
    local status
    repo_path="$1"

    if ! cd "$repo_path"; then
    log_warning "Could not change directory to: $repo_path. Skipping."
    return 1 # Indicate failure to cd, but continue script
    fi

    status="$(git status -s)"

    if [ -n "$status" ]; then
    log_warning "Git repo with pending changes: $repo_path"
    echo ""
    echo " Path: $repo_path"
    remote_url=$(git remote get-url origin 2>/dev/null) # Capture remote URL or handle error if no remote
    if [ -n "$remote_url" ]; then
    echo " Remote: $remote_url"
    else
    echo " Remote: No remote configured (origin)"
    fi
    branch_name=$(git branch --show-current 2>/dev/null) # Capture branch name or handle error if detached HEAD
    if [ -n "$branch_name" ]; then
    echo " Branch: $branch_name"
    else
    echo " Branch: Detached HEAD"
    fi
    while IFS= read -r line; do
    printf " %s\n" "$line"
    done <<<"$status"
    echo ""
    else
    log_info "Git repo clean: $repo_path"
    fi
    return 0 # Indicate successful processing of repo
    }

    fn_git_report() {

    local git_default_clone_path
    git_default_clone_path="${DEFAULT_GIT_CLONE_PATH}" # Use the configured default

    if [ ! -d "$git_default_clone_path" ]; then
    log_warning "Default git clone path '$git_default_clone_path' does not exist or is not a directory."
    return 1 # Indicate failure, path is invalid
    fi


    local repos_found
    repos_found=0
    local repos_with_pending_changes=0

    while IFS= read -r repo; do
    repos_found=$((repos_found + 1))
    if fn_git_report__process_repo "$repo"; then
    : # Processed successfully, no need to increment pending changes count here as it's already logged in process_repo
    : # if process_repo returns 0, it's considered processed. if it returned 1, it failed to cd and was skipped.
    status_check=$(git status -s 2>/dev/null) # Re-check status within process_repo context, capture status again
    if [ -n "$status_check" ]; then
    repos_with_pending_changes=$((repos_with_pending_changes + 1))
    fi

    else
    log_warning "Failed to process repo at '$repo'. Skipping."
    fi
    done <<<"$(fn_git_report__look_for_git_repo_recursively "$git_default_clone_path")"

    echo ""
    echo "--- Summary ---"
    echo "Total git repositories scanned: $repos_found"
    echo "Repositories with pending changes: $repos_with_pending_changes"

    if [ "$repos_with_pending_changes" -gt 0 ]; then
    return 1 # Indicate that there were repos with pending changes (non-zero exit code)
    else
    return 0 # Indicate success, no repos with pending changes (zero exit code)
    fi

    }


    # --- Main execution ---
    fn_git_report