Resolve the shared "common" git directory for repo_path. * Handles three layouts: * 1. /.git is a directory - ordinary repo; common_dir == /.git * 2. /.git is a regular file - linked worktree gitlink "gitdir: "; * the common dir is read from /commondir (git stores info/exclude + * config there, shared across worktrees). Falls back to git_dir w
| 1021 | * .git/info/exclude and core.excludesfile were silently dropped because the old |
| 1022 | * check required .git to be a directory (issue #489 only covered ordinary repos). */ |
| 1023 | static bool resolve_git_common_dir(const char *repo_path, char *common_dir, size_t cd_sz) { |
| 1024 | char dot_git[CBM_SZ_4K]; |
| 1025 | snprintf(dot_git, sizeof(dot_git), "%s/.git", repo_path); |
| 1026 | struct stat st; |
| 1027 | if (wide_stat(dot_git, &st) != 0) { |
| 1028 | return false; |
| 1029 | } |
| 1030 | if (S_ISDIR(st.st_mode)) { |
| 1031 | snprintf(common_dir, cd_sz, "%s", dot_git); |
| 1032 | cbm_normalize_path_sep(common_dir); |
| 1033 | return true; |
| 1034 | } |
| 1035 | if (!S_ISREG(st.st_mode)) { |
| 1036 | return false; |
| 1037 | } |
| 1038 | |
| 1039 | /* Linked worktree: parse "gitdir: <path>" from the gitlink file. |
| 1040 | * cbm_fopen (not raw fopen) so non-ASCII repo paths open on Windows. */ |
| 1041 | FILE *f = cbm_fopen(dot_git, "r"); |
| 1042 | if (!f) { |
| 1043 | return false; |
| 1044 | } |
| 1045 | char git_dir[CBM_SZ_4K]; |
| 1046 | bool got_git_dir = false; |
| 1047 | char line[CBM_SZ_4K]; |
| 1048 | while (fgets(line, sizeof(line), f)) { |
| 1049 | char *gs = trim_ws(line); |
| 1050 | if (strncmp(gs, "gitdir:", 7) == 0) { |
| 1051 | char *val = trim_ws(gs + 7); |
| 1052 | if (val[0] != '\0') { |
| 1053 | if (discover_path_is_absolute(val)) { |
| 1054 | snprintf(git_dir, sizeof(git_dir), "%s", val); |
| 1055 | cbm_normalize_path_sep(git_dir); |
| 1056 | } else { |
| 1057 | path_join(git_dir, sizeof(git_dir), repo_path, val); |
| 1058 | } |
| 1059 | got_git_dir = true; |
| 1060 | } |
| 1061 | break; |
| 1062 | } |
| 1063 | } |
| 1064 | fclose(f); |
| 1065 | if (!got_git_dir) { |
| 1066 | return false; |
| 1067 | } |
| 1068 | |
| 1069 | /* The shared dir holding info/exclude + config is named in <git_dir>/commondir |
| 1070 | * (typically a relative path like "../.."). Absent in single-worktree gitdirs. */ |
| 1071 | char commondir_path[CBM_SZ_4K]; |
| 1072 | path_join(commondir_path, sizeof(commondir_path), git_dir, "commondir"); |
| 1073 | FILE *cf = cbm_fopen(commondir_path, "r"); |
| 1074 | if (cf) { |
| 1075 | char cbuf[CBM_SZ_4K]; |
| 1076 | bool resolved = false; |
| 1077 | if (fgets(cbuf, sizeof(cbuf), cf)) { |
| 1078 | char *cs = trim_ws(cbuf); |
| 1079 | if (cs[0] != '\0') { |
| 1080 | if (discover_path_is_absolute(cs)) { |
no test coverage detected