Skip to content

Commit d0feac4

Browse files
derrickstoleevdye
authored andcommitted
scalar: 'register' sets recommended config and starts maintenance
Let's start implementing the `register` command. With this commit, recommended settings are configured upon `scalar register`, and Git's background maintenance is started. The recommended config settings may very well change in the future. For example, once the built-in FSMonitor is available, we will want to enable it upon `scalar register`. For that reason, we explicitly support running `scalar register` in an already-registered enlistment. Co-authored-by: Victoria Dye <vdye@github.com> Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 9187659 commit d0feac4

File tree

2 files changed

+266
-1
lines changed

2 files changed

+266
-1
lines changed

contrib/scalar/scalar.c

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,260 @@
55
#include "cache.h"
66
#include "gettext.h"
77
#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+
}
8256

9257
static struct {
10258
const char *name;
11259
int (*fn)(int, const char **);
12260
} builtins[] = {
261+
{ "register", cmd_register },
13262
{ NULL, NULL},
14263
};
15264

contrib/scalar/scalar.txt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ scalar - an opinionated repository management tool
88
SYNOPSIS
99
--------
1010
[verse]
11-
scalar <command> [<options>]
11+
scalar register [<enlistment>]
1212

1313
DESCRIPTION
1414
-----------
@@ -29,6 +29,22 @@ will be identical to the worktree.
2929
The `scalar` command implements various subcommands, and different options
3030
depending on the subcommand.
3131

32+
COMMANDS
33+
--------
34+
35+
Register
36+
~~~~~~~~
37+
38+
register [<enlistment>]::
39+
Adds the enlistment's repository to the list of registered repositories
40+
and starts background maintenance. If `<enlistment>` is not provided,
41+
then the enlistment associated with the current working directory is
42+
registered.
43+
+
44+
Note: when this subcommand is called in a worktree that is called `src/`, its
45+
parent directory is considered to be the Scalar enlistment. If the worktree is
46+
_not_ called `src/`, it itself will be considered to be the Scalar enlistment.
47+
3248
SEE ALSO
3349
--------
3450
linkgit:git-maintenance[1].

0 commit comments

Comments
 (0)