Returns true if working tree has changes (modified, untracked, etc.). * Also checks submodules via `git submodule foreach` to detect uncommitted * changes inside submodules that `git status` alone would not report. */
| 154 | * Also checks submodules via `git submodule foreach` to detect uncommitted |
| 155 | * changes inside submodules that `git status` alone would not report. */ |
| 156 | static bool git_is_dirty(const char *root_path) { |
| 157 | char cmd[CBM_SZ_1K]; |
| 158 | snprintf(cmd, sizeof(cmd), |
| 159 | "git --no-optional-locks -C \"%s\" status --porcelain " |
| 160 | "--untracked-files=normal 2>%s", |
| 161 | root_path, WATCHER_NULDEV); |
| 162 | FILE *fp = cbm_popen(cmd, "r"); |
| 163 | if (!fp) { |
| 164 | return false; |
| 165 | } |
| 166 | |
| 167 | char line[CBM_SZ_256]; |
| 168 | bool dirty = false; |
| 169 | if (fgets(line, sizeof(line), fp)) { |
| 170 | size_t len = strlen(line); |
| 171 | while (len > 0 && (line[len - SKIP_ONE] == '\n' || line[len - SKIP_ONE] == '\r')) { |
| 172 | line[--len] = '\0'; |
| 173 | } |
| 174 | if (len > 0) { |
| 175 | dirty = true; |
| 176 | } |
| 177 | } |
| 178 | cbm_pclose(fp); |
| 179 | |
| 180 | if (dirty) { |
| 181 | return true; |
| 182 | } |
| 183 | |
| 184 | #if !defined(_WIN32) |
| 185 | /* Check submodules: uncommitted changes inside a submodule are invisible |
| 186 | * to the parent's git status. Use `git submodule foreach` as a portable |
| 187 | * fallback (Apple Git lacks --recurse-submodules). POSIX-only: foreach takes |
| 188 | * an inner shell command that cmd.exe cannot pass intact; the parent-repo |
| 189 | * status check above already covers the common (non-submodule) case. */ |
| 190 | snprintf(cmd, sizeof(cmd), |
| 191 | "git --no-optional-locks -C '%s' submodule foreach --quiet --recursive " |
| 192 | "'git status --porcelain --untracked-files=normal 2>/dev/null' " |
| 193 | "2>/dev/null", |
| 194 | root_path); |
| 195 | fp = cbm_popen(cmd, "r"); |
| 196 | if (!fp) { |
| 197 | return false; |
| 198 | } |
| 199 | if (fgets(line, sizeof(line), fp)) { |
| 200 | size_t len = strlen(line); |
| 201 | while (len > 0 && (line[len - SKIP_ONE] == '\n' || line[len - SKIP_ONE] == '\r')) { |
| 202 | line[--len] = '\0'; |
| 203 | } |
| 204 | if (len > 0) { |
| 205 | dirty = true; |
| 206 | } |
| 207 | } |
| 208 | cbm_pclose(fp); |
| 209 | #endif |
| 210 | return dirty; |
| 211 | } |
| 212 | |
| 213 | /* Count tracked files via git ls-files */ |
no test coverage detected