|
5 | 5 | #include "cache.h" |
6 | 6 | #include "gettext.h" |
7 | 7 | #include "parse-options.h" |
| 8 | +#include "config.h" |
| 9 | +#include "run-command.h" |
| 10 | + |
| 11 | +/* |
| 12 | + * Remove the deepest subdirectory in the provided path string. Path must not |
| 13 | + * include a trailing path separator. Returns 1 if parent directory found, |
| 14 | + * otherwise 0. |
| 15 | + */ |
| 16 | +static int strbuf_parent_directory(struct strbuf *buf) |
| 17 | +{ |
| 18 | + size_t len = buf->len; |
| 19 | + size_t offset = offset_1st_component(buf->buf); |
| 20 | + char *path_sep = find_last_dir_sep(buf->buf + offset); |
| 21 | + strbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset); |
| 22 | + |
| 23 | + return buf->len < len; |
| 24 | +} |
| 25 | + |
| 26 | +static void setup_enlistment_directory(int argc, const char **argv, |
| 27 | + const char * const *usagestr, |
| 28 | + const struct option *options, |
| 29 | + struct strbuf *enlistment_root) |
| 30 | +{ |
| 31 | + struct strbuf path = STRBUF_INIT; |
| 32 | + char *root; |
| 33 | + int enlistment_found = 0; |
| 34 | + |
| 35 | + if (startup_info->have_repository) |
| 36 | + BUG("gitdir already set up?!?"); |
| 37 | + |
| 38 | + if (argc > 1) |
| 39 | + usage_with_options(usagestr, options); |
| 40 | + |
| 41 | + /* find the worktree, determine its corresponding root */ |
| 42 | + if (argc == 1) |
| 43 | + strbuf_add_absolute_path(&path, argv[0]); |
| 44 | + else if (strbuf_getcwd(&path) < 0) |
| 45 | + die(_("need a working directory")); |
| 46 | + |
| 47 | + strbuf_trim_trailing_dir_sep(&path); |
| 48 | + do { |
| 49 | + const size_t len = path.len; |
| 50 | + |
| 51 | + /* check if currently in enlistment root with src/ workdir */ |
| 52 | + strbuf_addstr(&path, "/src"); |
| 53 | + if (is_nonbare_repository_dir(&path)) { |
| 54 | + if (enlistment_root) |
| 55 | + strbuf_add(enlistment_root, path.buf, len); |
| 56 | + |
| 57 | + enlistment_found = 1; |
| 58 | + break; |
| 59 | + } |
| 60 | + |
| 61 | + /* reset to original path */ |
| 62 | + strbuf_setlen(&path, len); |
| 63 | + |
| 64 | + /* check if currently in workdir */ |
| 65 | + if (is_nonbare_repository_dir(&path)) { |
| 66 | + if (enlistment_root) { |
| 67 | + /* |
| 68 | + * If the worktree's directory's name is `src`, the enlistment is the |
| 69 | + * parent directory, otherwise it is identical to the worktree. |
| 70 | + */ |
| 71 | + root = strip_path_suffix(path.buf, "src"); |
| 72 | + strbuf_addstr(enlistment_root, root ? root : path.buf); |
| 73 | + free(root); |
| 74 | + } |
| 75 | + |
| 76 | + enlistment_found = 1; |
| 77 | + break; |
| 78 | + } |
| 79 | + } while (strbuf_parent_directory(&path)); |
| 80 | + |
| 81 | + if (!enlistment_found) |
| 82 | + die(_("could not find enlistment root")); |
| 83 | + |
| 84 | + if (chdir(path.buf) < 0) |
| 85 | + die_errno(_("could not switch to '%s'"), path.buf); |
| 86 | + |
| 87 | + strbuf_release(&path); |
| 88 | + setup_git_directory(); |
| 89 | +} |
| 90 | + |
| 91 | +static int run_git(const char *arg, ...) |
| 92 | +{ |
| 93 | + struct strvec argv = STRVEC_INIT; |
| 94 | + va_list args; |
| 95 | + const char *p; |
| 96 | + int res; |
| 97 | + |
| 98 | + va_start(args, arg); |
| 99 | + strvec_push(&argv, arg); |
| 100 | + while ((p = va_arg(args, const char *))) |
| 101 | + strvec_push(&argv, p); |
| 102 | + va_end(args); |
| 103 | + |
| 104 | + res = run_command_v_opt(argv.v, RUN_GIT_CMD); |
| 105 | + |
| 106 | + strvec_clear(&argv); |
| 107 | + return res; |
| 108 | +} |
| 109 | + |
| 110 | +static int set_recommended_config(void) |
| 111 | +{ |
| 112 | + struct { |
| 113 | + const char *key; |
| 114 | + const char *value; |
| 115 | + } config[] = { |
| 116 | + { "am.keepCR", "true" }, |
| 117 | + { "core.FSCache", "true" }, |
| 118 | + { "core.multiPackIndex", "true" }, |
| 119 | + { "core.preloadIndex", "true" }, |
| 120 | +#ifndef WIN32 |
| 121 | + { "core.untrackedCache", "true" }, |
| 122 | +#else |
| 123 | + /* |
| 124 | + * Unfortunately, Scalar's Functional Tests demonstrated |
| 125 | + * that the untracked cache feature is unreliable on Windows |
| 126 | + * (which is a bummer because that platform would benefit the |
| 127 | + * most from it). For some reason, freshly created files seem |
| 128 | + * not to update the directory's `lastModified` time |
| 129 | + * immediately, but the untracked cache would need to rely on |
| 130 | + * that. |
| 131 | + * |
| 132 | + * Therefore, with a sad heart, we disable this very useful |
| 133 | + * feature on Windows. |
| 134 | + */ |
| 135 | + { "core.untrackedCache", "false" }, |
| 136 | +#endif |
| 137 | + { "core.logAllRefUpdates", "true" }, |
| 138 | + { "credential.https://dev.azure.com.useHttpPath", "true" }, |
| 139 | + { "credential.validate", "false" }, /* GCM4W-only */ |
| 140 | + { "gc.auto", "0" }, |
| 141 | + { "gui.GCWarning", "false" }, |
| 142 | + { "index.threads", "true" }, |
| 143 | + { "index.version", "4" }, |
| 144 | + { "merge.stat", "false" }, |
| 145 | + { "merge.renames", "true" }, |
| 146 | + { "pack.useBitmaps", "false" }, |
| 147 | + { "pack.useSparse", "true" }, |
| 148 | + { "receive.autoGC", "false" }, |
| 149 | + { "reset.quiet", "true" }, |
| 150 | + { "feature.manyFiles", "false" }, |
| 151 | + { "feature.experimental", "false" }, |
| 152 | + { "fetch.unpackLimit", "1" }, |
| 153 | + { "fetch.writeCommitGraph", "false" }, |
| 154 | +#ifdef WIN32 |
| 155 | + { "http.sslBackend", "schannel" }, |
| 156 | +#endif |
| 157 | + { "status.aheadBehind", "false" }, |
| 158 | + { "commitGraph.generationVersion", "1" }, |
| 159 | + { "core.autoCRLF", "false" }, |
| 160 | + { "core.safeCRLF", "false" }, |
| 161 | + { "fetch.showForcedUpdates", "false" }, |
| 162 | + { NULL, NULL }, |
| 163 | + }; |
| 164 | + int i; |
| 165 | + char *value; |
| 166 | + |
| 167 | + for (i = 0; config[i].key; i++) { |
| 168 | + if (git_config_get_string(config[i].key, &value)) { |
| 169 | + trace2_data_string("scalar", the_repository, config[i].key, "created"); |
| 170 | + if (git_config_set_gently(config[i].key, |
| 171 | + config[i].value) < 0) |
| 172 | + return error(_("could not configure %s=%s"), |
| 173 | + config[i].key, config[i].value); |
| 174 | + } else { |
| 175 | + trace2_data_string("scalar", the_repository, config[i].key, "exists"); |
| 176 | + free(value); |
| 177 | + } |
| 178 | + } |
| 179 | + |
| 180 | + /* |
| 181 | + * The `log.excludeDecoration` setting is special because it allows |
| 182 | + * for multiple values. |
| 183 | + */ |
| 184 | + if (git_config_get_string("log.excludeDecoration", &value)) { |
| 185 | + trace2_data_string("scalar", the_repository, |
| 186 | + "log.excludeDecoration", "created"); |
| 187 | + if (git_config_set_multivar_gently("log.excludeDecoration", |
| 188 | + "refs/prefetch/*", |
| 189 | + CONFIG_REGEX_NONE, 0)) |
| 190 | + return error(_("could not configure " |
| 191 | + "log.excludeDecoration")); |
| 192 | + } else { |
| 193 | + trace2_data_string("scalar", the_repository, |
| 194 | + "log.excludeDecoration", "exists"); |
| 195 | + free(value); |
| 196 | + } |
| 197 | + |
| 198 | + return 0; |
| 199 | +} |
| 200 | + |
| 201 | +static int start_maintenance(void) |
| 202 | +{ |
| 203 | + return run_git("maintenance", "start", NULL); |
| 204 | +} |
| 205 | + |
| 206 | +static int add_enlistment(void) |
| 207 | +{ |
| 208 | + int res; |
| 209 | + |
| 210 | + if (!the_repository->worktree) |
| 211 | + die(_("Scalar enlistments require a worktree")); |
| 212 | + |
| 213 | + res = run_git("config", "--global", "--get", "--fixed-value", |
| 214 | + "scalar.repo", the_repository->worktree, NULL); |
| 215 | + |
| 216 | + /* |
| 217 | + * If the setting is already there, then do nothing. |
| 218 | + */ |
| 219 | + if (!res) |
| 220 | + return 0; |
| 221 | + |
| 222 | + return run_git("config", "--global", "--add", |
| 223 | + "scalar.repo", the_repository->worktree, NULL); |
| 224 | +} |
| 225 | + |
| 226 | +static int register_dir(void) |
| 227 | +{ |
| 228 | + int res = add_enlistment(); |
| 229 | + |
| 230 | + if (!res) |
| 231 | + res = set_recommended_config(); |
| 232 | + |
| 233 | + if (!res) |
| 234 | + res = start_maintenance(); |
| 235 | + |
| 236 | + return res; |
| 237 | +} |
| 238 | + |
| 239 | +static int cmd_register(int argc, const char **argv) |
| 240 | +{ |
| 241 | + struct option options[] = { |
| 242 | + OPT_END(), |
| 243 | + }; |
| 244 | + const char * const usage[] = { |
| 245 | + N_("scalar register [<enlistment>]"), |
| 246 | + NULL |
| 247 | + }; |
| 248 | + |
| 249 | + argc = parse_options(argc, argv, NULL, options, |
| 250 | + usage, 0); |
| 251 | + |
| 252 | + setup_enlistment_directory(argc, argv, usage, options, NULL); |
| 253 | + |
| 254 | + return register_dir(); |
| 255 | +} |
8 | 256 |
|
9 | 257 | static struct { |
10 | 258 | const char *name; |
11 | 259 | int (*fn)(int, const char **); |
12 | 260 | } builtins[] = { |
| 261 | + { "register", cmd_register }, |
13 | 262 | { NULL, NULL}, |
14 | 263 | }; |
15 | 264 |
|
|
0 commit comments