Created
June 30, 2025 10:22
-
-
Save patkepa/8a014cf9ee7d935261c27fd9f100b79b to your computer and use it in GitHub Desktop.
Shorts tree like structure for submodules in repositories.
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 | |
| # Git Submodule Dependency Tree Generator | |
| # Usage: ./git-submodule-tree.sh <github-repo-url> | |
| set -euo pipefail | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| BLUE='\033[0;34m' | |
| YELLOW='\033[1;33m' | |
| NC='\033[0m' # No Color | |
| # Function to print usage | |
| usage() { | |
| echo "Usage: $0 <github-repo-url>" | |
| echo "Example: $0 https://github.com/user/repo.git" | |
| exit 1 | |
| } | |
| # Check if URL is provided | |
| if [ $# -eq 0 ]; then | |
| usage | |
| fi | |
| REPO_URL="$1" | |
| TEMP_DIR=$(mktemp -d) | |
| VISITED_REPOS=() | |
| # Clean up on exit | |
| cleanup() { | |
| rm -rf "$TEMP_DIR" | |
| } | |
| trap cleanup EXIT | |
| # Function to check if repo was already visited | |
| is_visited() { | |
| local repo="$1" | |
| for visited in "${VISITED_REPOS[@]}"; do | |
| if [ "$visited" == "$repo" ]; then | |
| return 0 | |
| fi | |
| done | |
| return 1 | |
| } | |
| # Function to extract repo info from .gitmodules | |
| parse_gitmodules() { | |
| local gitmodules_file="$1" | |
| local indent="$2" | |
| if [ ! -f "$gitmodules_file" ]; then | |
| return | |
| fi | |
| # Parse .gitmodules file | |
| while IFS= read -r line; do | |
| if [[ $line =~ ^\[submodule[[:space:]]+\"(.+)\"\] ]]; then | |
| local name="${BASH_REMATCH[1]}" | |
| local path="" | |
| local url="" | |
| local branch="" | |
| # Read submodule details | |
| while IFS= read -r subline && ! [[ $subline =~ ^\[submodule ]]; do | |
| if [[ $subline =~ path[[:space:]]*=[[:space:]]*(.+) ]]; then | |
| path="${BASH_REMATCH[1]}" | |
| elif [[ $subline =~ url[[:space:]]*=[[:space:]]*(.+) ]]; then | |
| url="${BASH_REMATCH[1]}" | |
| elif [[ $subline =~ branch[[:space:]]*=[[:space:]]*(.+) ]]; then | |
| branch="${BASH_REMATCH[1]}" | |
| fi | |
| done | |
| if [ -n "$url" ]; then | |
| # Print submodule info | |
| echo -e "${indent}├── ${GREEN}${name}${NC}" | |
| echo -e "${indent}│ └── ${BLUE}${url}${NC}" | |
| # Check for commit hash or branch | |
| if [ -n "$path" ] && [ -d "$(dirname "$gitmodules_file")/$path" ]; then | |
| cd "$(dirname "$gitmodules_file")/$path" 2>/dev/null || true | |
| local commit=$(git rev-parse HEAD 2>/dev/null || echo "") | |
| if [ -n "$commit" ]; then | |
| echo -e "${indent}│ └── ${YELLOW}commit: ${commit:0:8}${NC}" | |
| fi | |
| if [ -n "$branch" ]; then | |
| echo -e "${indent}│ └── ${YELLOW}branch: ${branch}${NC}" | |
| fi | |
| cd - > /dev/null 2>&1 || true | |
| fi | |
| # Mark as visited | |
| VISITED_REPOS+=("$url") | |
| # Recursively process submodule if not already visited | |
| if ! is_visited "$url"; then | |
| process_repo "$url" "${indent}│ " | |
| fi | |
| fi | |
| fi | |
| done < "$gitmodules_file" | |
| } | |
| # Function to process a repository | |
| process_repo() { | |
| local repo_url="$1" | |
| local indent="${2:-}" | |
| # Skip if already visited | |
| if is_visited "$repo_url"; then | |
| return | |
| fi | |
| # Create temporary directory for this repo | |
| local repo_dir=$(mktemp -d -p "$TEMP_DIR") | |
| # Clone the repository (shallow clone for speed) | |
| echo -e "${indent}${RED}Cloning: ${repo_url}${NC}" >&2 | |
| if ! git clone --depth 1 --recurse-submodules "$repo_url" "$repo_dir" 2>/dev/null; then | |
| echo -e "${indent}${RED}Failed to clone: ${repo_url}${NC}" >&2 | |
| return | |
| fi | |
| # Process .gitmodules if it exists | |
| if [ -f "$repo_dir/.gitmodules" ]; then | |
| parse_gitmodules "$repo_dir/.gitmodules" "$indent" | |
| fi | |
| # Clean up this repo | |
| rm -rf "$repo_dir" | |
| } | |
| # Main execution | |
| echo -e "${GREEN}Git Submodule Dependency Tree${NC}" | |
| echo -e "${GREEN}==============================${NC}" | |
| echo -e "${BLUE}${REPO_URL}${NC}" | |
| # Mark main repo as visited | |
| VISITED_REPOS+=("$REPO_URL") | |
| # Process the main repository | |
| process_repo "$REPO_URL" "" | |
| echo -e "\n${GREEN}Complete!${NC}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment