@@ -56,62 +56,101 @@ sync_repo() { |
| 56 | 56 | local org_path="$3" |
| 57 | 57 | local repo_path="${org_path}/${repo}" |
| 58 | 58 | |
| 59 | | - if [[ -d "$repo_path" ]]; then |
| 60 | | - # Repo exists - try to pull |
| 61 | | - if [[ -d "$repo_path/.git" ]]; then |
| 62 | | - cd "$repo_path" |
| 59 | + # Case 1: Valid git repo exists - try to update |
| 60 | + if [[ -d "$repo_path/.git" ]]; then |
| 61 | + # Use subshell to avoid cd pollution, capture result |
| 62 | + local result |
| 63 | + result=$( |
| 64 | + cd "$repo_path" 2>/dev/null || { echo "invalid"; exit 0; } |
| 65 | + |
| 66 | + # Verify it's a valid git repo with a HEAD |
| 67 | + if ! git rev-parse HEAD &>/dev/null; then |
| 68 | + echo "invalid" |
| 69 | + exit 0 |
| 70 | + fi |
| 63 | 71 | |
| 64 | | - # Check for uncommitted changes |
| 65 | | - if ! git diff-index --quiet HEAD -- 2>/dev/null; then |
| 66 | | - log_warn "$repo (uncommitted changes, skipping)" |
| 67 | | - ((total_skipped++)) || true |
| 68 | | - return 0 |
| 72 | + # Check for uncommitted changes (staged or unstaged) |
| 73 | + if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then |
| 74 | + echo "dirty" |
| 75 | + exit 0 |
| 69 | 76 | fi |
| 70 | 77 | |
| 71 | 78 | # Try fast-forward pull |
| 72 | | - if output=$(timeout 30 git pull --ff-only 2>&1); then |
| 73 | | - if [[ "$output" == *"Already up to date"* ]]; then |
| 74 | | - log_success "$repo (up to date)" |
| 79 | + if pull_output=$(timeout 30 git pull --ff-only 2>&1); then |
| 80 | + if [[ "$pull_output" == *"Already up to date"* ]]; then |
| 81 | + echo "uptodate" |
| 75 | 82 | else |
| 76 | | - log_success "$repo (updated)" |
| 77 | | - ((total_updated++)) || true |
| 83 | + echo "updated" |
| 78 | 84 | fi |
| 79 | 85 | else |
| 86 | + echo "diverged" |
| 87 | + fi |
| 88 | + ) |
| 89 | + |
| 90 | + case "$result" in |
| 91 | + invalid) |
| 92 | + log_warn "$repo (invalid git repo, skipping)" |
| 93 | + ((total_skipped++)) || true |
| 94 | + ;; |
| 95 | + dirty) |
| 96 | + log_warn "$repo (uncommitted changes, skipping)" |
| 97 | + ((total_skipped++)) || true |
| 98 | + ;; |
| 99 | + uptodate) |
| 100 | + log_success "$repo (up to date)" |
| 101 | + ;; |
| 102 | + updated) |
| 103 | + log_success "$repo (updated)" |
| 104 | + ((total_updated++)) || true |
| 105 | + ;; |
| 106 | + diverged) |
| 80 | 107 | log_warn "$repo (can't fast-forward, skipping)" |
| 81 | 108 | ((total_skipped++)) || true |
| 82 | | - fi |
| 109 | + ;; |
| 110 | + *) |
| 111 | + log_warn "$repo (unknown state: $result)" |
| 112 | + ((total_skipped++)) || true |
| 113 | + ;; |
| 114 | + esac |
| 115 | + return 0 |
| 116 | + |
| 117 | + # Case 2: Directory exists but no .git |
| 118 | + elif [[ -d "$repo_path" ]]; then |
| 119 | + if [[ -z "$(ls -A "$repo_path" 2>/dev/null)" ]]; then |
| 120 | + log_info "$repo (empty dir, removing and cloning...)" |
| 121 | + rmdir "$repo_path" |
| 83 | 122 | else |
| 84 | 123 | log_warn "$repo (exists but not a git repo)" |
| 85 | 124 | ((total_skipped++)) || true |
| 125 | + return 0 |
| 126 | + fi |
| 127 | + fi |
| 128 | + |
| 129 | + # Case 3: Clone new repo |
| 130 | + local ssh_url="git@github.com:${github_org}/${repo}.git" |
| 131 | + log_info "Cloning $repo (SSH)..." |
| 132 | + if timeout "$CLONE_TIMEOUT" git clone --progress "$ssh_url" "$repo_path" 2>&1; then |
| 133 | + if [[ -d "$repo_path/.git" ]]; then |
| 134 | + log_success "$repo (cloned)" |
| 135 | + ((total_cloned++)) || true |
| 136 | + else |
| 137 | + log_error "$repo (clone failed - no permission?)" |
| 138 | + failed_repos+=("${github_org}/${repo}") |
| 139 | + ((total_failed++)) || true |
| 86 | 140 | fi |
| 87 | 141 | else |
| 88 | | - # Clone new repo via SSH with timeout, showing progress |
| 89 | | - local ssh_url="git@github.com:${github_org}/${repo}.git" |
| 90 | | - log_info "Cloning $repo (SSH)..." |
| 91 | | - if timeout "$CLONE_TIMEOUT" git clone --progress "$ssh_url" "$repo_path" 2>&1; then |
| 92 | | - if [[ -d "$repo_path/.git" ]]; then |
| 93 | | - log_success "$repo (cloned)" |
| 94 | | - ((total_cloned++)) || true |
| 95 | | - else |
| 96 | | - log_error "$repo (clone failed - no permission?)" |
| 97 | | - failed_repos+=("${github_org}/${repo}") |
| 98 | | - ((total_failed++)) || true |
| 99 | | - fi |
| 142 | + local exit_code=$? |
| 143 | + if [[ -d "$repo_path/.git" ]]; then |
| 144 | + log_success "$repo (cloned)" |
| 145 | + ((total_cloned++)) || true |
| 146 | + elif [[ $exit_code -eq 124 ]]; then |
| 147 | + log_error "$repo (timeout after ${CLONE_TIMEOUT}s)" |
| 148 | + failed_repos+=("${github_org}/${repo}") |
| 149 | + ((total_failed++)) || true |
| 100 | 150 | else |
| 101 | | - exit_code=$? |
| 102 | | - # Check if it actually succeeded despite error |
| 103 | | - if [[ -d "$repo_path/.git" ]]; then |
| 104 | | - log_success "$repo (cloned)" |
| 105 | | - ((total_cloned++)) || true |
| 106 | | - elif [[ $exit_code -eq 124 ]]; then |
| 107 | | - log_error "$repo (timeout after ${CLONE_TIMEOUT}s)" |
| 108 | | - failed_repos+=("${github_org}/${repo}") |
| 109 | | - ((total_failed++)) || true |
| 110 | | - else |
| 111 | | - log_error "$repo (clone failed)" |
| 112 | | - failed_repos+=("${github_org}/${repo}") |
| 113 | | - ((total_failed++)) || true |
| 114 | | - fi |
| 151 | + log_error "$repo (clone failed)" |
| 152 | + failed_repos+=("${github_org}/${repo}") |
| 153 | + ((total_failed++)) || true |
| 115 | 154 | fi |
| 116 | 155 | fi |
| 117 | 156 | } |