2 * The Scalar command-line interface.
5 #define USE_THE_REPOSITORY_VARIABLE
7 #include "git-compat-util.h"
10 #include "parse-options.h"
12 #include "run-command.h"
13 #include "simple-ipc.h"
14 #include "fsmonitor-ipc.h"
15 #include "fsmonitor-settings.h"
23 static void setup_enlistment_directory(int argc
, const char **argv
,
24 const char * const *usagestr
,
25 const struct option
*options
,
26 struct strbuf
*enlistment_root
)
28 struct strbuf path
= STRBUF_INIT
;
29 int enlistment_is_repo_parent
= 0;
32 if (startup_info
->have_repository
)
33 BUG("gitdir already set up?!?");
36 usage_with_options(usagestr
, options
);
38 /* find the worktree, determine its corresponding root */
40 strbuf_add_absolute_path(&path
, argv
[0]);
41 if (!is_directory(path
.buf
))
42 die(_("'%s' does not exist"), path
.buf
);
43 if (chdir(path
.buf
) < 0)
44 die_errno(_("could not switch to '%s'"), path
.buf
);
45 } else if (strbuf_getcwd(&path
) < 0)
46 die(_("need a working directory"));
48 strbuf_trim_trailing_dir_sep(&path
);
50 /* check if currently in enlistment root with src/ workdir */
52 strbuf_addstr(&path
, "/src");
53 if (is_nonbare_repository_dir(&path
)) {
54 enlistment_is_repo_parent
= 1;
55 if (chdir(path
.buf
) < 0)
56 die_errno(_("could not switch to '%s'"), path
.buf
);
58 strbuf_setlen(&path
, len
);
60 setup_git_directory();
62 if (!the_repository
->worktree
)
63 die(_("Scalar enlistments require a worktree"));
65 if (enlistment_root
) {
66 if (enlistment_is_repo_parent
)
67 strbuf_addbuf(enlistment_root
, &path
);
69 strbuf_addstr(enlistment_root
, the_repository
->worktree
);
72 strbuf_release(&path
);
76 static int run_git(const char *arg
, ...)
78 struct child_process cmd
= CHILD_PROCESS_INIT
;
83 strvec_push(&cmd
.args
, arg
);
84 while ((p
= va_arg(args
, const char *)))
85 strvec_push(&cmd
.args
, p
);
89 return run_command(&cmd
);
92 struct scalar_config
{
95 int overwrite_on_reconfigure
;
98 static int set_scalar_config(const struct scalar_config
*config
, int reconfigure
)
103 if ((reconfigure
&& config
->overwrite_on_reconfigure
) ||
104 git_config_get_string(config
->key
, &value
)) {
105 trace2_data_string("scalar", the_repository
, config
->key
, "created");
106 res
= git_config_set_gently(config
->key
, config
->value
);
108 trace2_data_string("scalar", the_repository
, config
->key
, "exists");
116 static int have_fsmonitor_support(void)
118 return fsmonitor_ipc__is_supported() &&
119 fsm_settings__get_reason(the_repository
) == FSMONITOR_REASON_OK
;
122 static int set_recommended_config(int reconfigure
)
124 struct scalar_config config
[] = {
126 { "am.keepCR", "true", 1 },
127 { "core.FSCache", "true", 1 },
128 { "core.multiPackIndex", "true", 1 },
129 { "core.preloadIndex", "true", 1 },
131 { "core.untrackedCache", "true", 1 },
134 * Unfortunately, Scalar's Functional Tests demonstrated
135 * that the untracked cache feature is unreliable on Windows
136 * (which is a bummer because that platform would benefit the
137 * most from it). For some reason, freshly created files seem
138 * not to update the directory's `lastModified` time
139 * immediately, but the untracked cache would need to rely on
142 * Therefore, with a sad heart, we disable this very useful
143 * feature on Windows.
145 { "core.untrackedCache", "false", 1 },
147 { "core.logAllRefUpdates", "true", 1 },
148 { "credential.https://dev.azure.com.useHttpPath", "true", 1 },
149 { "credential.validate", "false", 1 }, /* GCM4W-only */
150 { "gc.auto", "0", 1 },
151 { "gui.GCWarning", "false", 1 },
152 { "index.skipHash", "false", 1 },
153 { "index.threads", "true", 1 },
154 { "index.version", "4", 1 },
155 { "merge.stat", "false", 1 },
156 { "merge.renames", "true", 1 },
157 { "pack.useBitmaps", "false", 1 },
158 { "pack.useSparse", "true", 1 },
159 { "receive.autoGC", "false", 1 },
160 { "feature.manyFiles", "false", 1 },
161 { "feature.experimental", "false", 1 },
162 { "fetch.unpackLimit", "1", 1 },
163 { "fetch.writeCommitGraph", "false", 1 },
165 { "http.sslBackend", "schannel", 1 },
168 { "status.aheadBehind", "false" },
169 { "commitGraph.generationVersion", "1" },
170 { "core.autoCRLF", "false" },
171 { "core.safeCRLF", "false" },
172 { "fetch.showForcedUpdates", "false" },
178 for (i
= 0; config
[i
].key
; i
++) {
179 if (set_scalar_config(config
+ i
, reconfigure
))
180 return error(_("could not configure %s=%s"),
181 config
[i
].key
, config
[i
].value
);
184 if (have_fsmonitor_support()) {
185 struct scalar_config fsmonitor
= { "core.fsmonitor", "true" };
186 if (set_scalar_config(&fsmonitor
, reconfigure
))
187 return error(_("could not configure %s=%s"),
188 fsmonitor
.key
, fsmonitor
.value
);
192 * The `log.excludeDecoration` setting is special because it allows
193 * for multiple values.
195 if (git_config_get_string("log.excludeDecoration", &value
)) {
196 trace2_data_string("scalar", the_repository
,
197 "log.excludeDecoration", "created");
198 if (git_config_set_multivar_gently("log.excludeDecoration",
200 CONFIG_REGEX_NONE
, 0))
201 return error(_("could not configure "
202 "log.excludeDecoration"));
204 trace2_data_string("scalar", the_repository
,
205 "log.excludeDecoration", "exists");
212 static int toggle_maintenance(int enable
)
214 return run_git("maintenance",
215 enable
? "start" : "unregister",
216 enable
? NULL
: "--force",
220 static int add_or_remove_enlistment(int add
)
224 if (!the_repository
->worktree
)
225 die(_("Scalar enlistments require a worktree"));
227 res
= run_git("config", "--global", "--get", "--fixed-value",
228 "scalar.repo", the_repository
->worktree
, NULL
);
231 * If we want to add and the setting is already there, then do nothing.
232 * If we want to remove and the setting is not there, then do nothing.
234 if ((add
&& !res
) || (!add
&& res
))
237 return run_git("config", "--global", add
? "--add" : "--unset",
238 add
? "--no-fixed-value" : "--fixed-value",
239 "scalar.repo", the_repository
->worktree
, NULL
);
242 static int start_fsmonitor_daemon(void)
244 assert(have_fsmonitor_support());
246 if (fsmonitor_ipc__get_state() != IPC_STATE__LISTENING
)
247 return run_git("fsmonitor--daemon", "start", NULL
);
252 static int stop_fsmonitor_daemon(void)
254 assert(have_fsmonitor_support());
256 if (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING
)
257 return run_git("fsmonitor--daemon", "stop", NULL
);
262 static int register_dir(void)
264 if (add_or_remove_enlistment(1))
265 return error(_("could not add enlistment"));
267 if (set_recommended_config(0))
268 return error(_("could not set recommended config"));
270 if (toggle_maintenance(1))
271 warning(_("could not turn on maintenance"));
273 if (have_fsmonitor_support() && start_fsmonitor_daemon()) {
274 return error(_("could not start the FSMonitor daemon"));
280 static int unregister_dir(void)
284 if (toggle_maintenance(0))
285 res
= error(_("could not turn off maintenance"));
287 if (add_or_remove_enlistment(0))
288 res
= error(_("could not remove enlistment"));
293 /* printf-style interface, expects `<key>=<value>` argument */
294 __attribute__((format (printf
, 1, 2)))
295 static int set_config(const char *fmt
, ...)
297 struct strbuf buf
= STRBUF_INIT
;
303 strbuf_vaddf(&buf
, fmt
, args
);
306 value
= strchr(buf
.buf
, '=');
309 res
= git_config_set_gently(buf
.buf
, value
);
310 strbuf_release(&buf
);
315 static char *remote_default_branch(const char *url
)
317 struct child_process cp
= CHILD_PROCESS_INIT
;
318 struct strbuf out
= STRBUF_INIT
;
321 strvec_pushl(&cp
.args
, "ls-remote", "--symref", url
, "HEAD", NULL
);
322 if (!pipe_command(&cp
, NULL
, 0, &out
, 0, NULL
, 0)) {
323 const char *line
= out
.buf
;
326 const char *eol
= strchrnul(line
, '\n'), *p
;
327 size_t len
= eol
- line
;
330 if (!skip_prefix(line
, "ref: ", &p
) ||
331 !strip_suffix_mem(line
, &len
, "\tHEAD")) {
332 line
= eol
+ (*eol
== '\n');
337 if (skip_prefix(p
, "refs/heads/", &p
)) {
338 branch
= xstrndup(p
, eol
- p
);
339 strbuf_release(&out
);
343 error(_("remote HEAD is not a branch: '%.*s'"),
345 strbuf_release(&out
);
349 warning(_("failed to get default branch name from remote; "
350 "using local default"));
353 child_process_init(&cp
);
355 strvec_pushl(&cp
.args
, "symbolic-ref", "--short", "HEAD", NULL
);
356 if (!pipe_command(&cp
, NULL
, 0, &out
, 0, NULL
, 0)) {
358 return strbuf_detach(&out
, NULL
);
361 strbuf_release(&out
);
362 error(_("failed to get default branch name"));
366 static int delete_enlistment(struct strbuf
*enlistment
)
368 struct strbuf parent
= STRBUF_INIT
;
372 if (unregister_dir())
373 return error(_("failed to unregister repository"));
376 * Change the current directory to one outside of the enlistment so
377 * that we may delete everything underneath it.
379 offset
= offset_1st_component(enlistment
->buf
);
380 path_sep
= find_last_dir_sep(enlistment
->buf
+ offset
);
381 strbuf_add(&parent
, enlistment
->buf
,
382 path_sep
? path_sep
- enlistment
->buf
: offset
);
383 if (chdir(parent
.buf
) < 0) {
384 int res
= error_errno(_("could not switch to '%s'"), parent
.buf
);
385 strbuf_release(&parent
);
388 strbuf_release(&parent
);
390 if (have_fsmonitor_support() && stop_fsmonitor_daemon())
391 return error(_("failed to stop the FSMonitor daemon"));
393 if (remove_dir_recursively(enlistment
, 0))
394 return error(_("failed to delete enlistment directory"));
400 * Dummy implementation; Using `get_version_info()` would cause a link error
403 void load_builtin_commands(const char *prefix UNUSED
,
404 struct cmdnames
*cmds UNUSED
)
406 die("not implemented");
409 static int cmd_clone(int argc
, const char **argv
)
411 const char *branch
= NULL
;
412 int full_clone
= 0, single_branch
= 0, show_progress
= isatty(2);
413 int src
= 1, tags
= 1;
414 struct option clone_options
[] = {
415 OPT_STRING('b', "branch", &branch
, N_("<branch>"),
416 N_("branch to checkout after clone")),
417 OPT_BOOL(0, "full-clone", &full_clone
,
418 N_("when cloning, create full working directory")),
419 OPT_BOOL(0, "single-branch", &single_branch
,
420 N_("only download metadata for the branch that will "
422 OPT_BOOL(0, "src", &src
,
423 N_("create repository within 'src' directory")),
424 OPT_BOOL(0, "tags", &tags
,
425 N_("specify if tags should be fetched during clone")),
428 const char * const clone_usage
[] = {
429 N_("scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
430 "\t[--[no-]src] [--[no-]tags] <url> [<enlistment>]"),
434 char *enlistment
= NULL
, *dir
= NULL
;
435 struct strbuf buf
= STRBUF_INIT
;
438 argc
= parse_options(argc
, argv
, NULL
, clone_options
, clone_usage
, 0);
442 enlistment
= xstrdup(argv
[1]);
443 } else if (argc
== 1) {
446 strbuf_addstr(&buf
, url
);
447 /* Strip trailing slashes, if any */
448 while (buf
.len
> 0 && is_dir_sep(buf
.buf
[buf
.len
- 1]))
449 strbuf_setlen(&buf
, buf
.len
- 1);
450 /* Strip suffix `.git`, if any */
451 strbuf_strip_suffix(&buf
, ".git");
453 enlistment
= find_last_dir_sep(buf
.buf
);
455 die(_("cannot deduce worktree name from '%s'"), url
);
457 enlistment
= xstrdup(enlistment
+ 1);
459 usage_msg_opt(_("You must specify a repository to clone."),
460 clone_usage
, clone_options
);
463 if (is_directory(enlistment
))
464 die(_("directory '%s' exists already"), enlistment
);
467 dir
= xstrfmt("%s/src", enlistment
);
469 dir
= xstrdup(enlistment
);
473 strbuf_addf(&buf
, "init.defaultBranch=%s", branch
);
475 char *b
= repo_default_branch_name(the_repository
, 1);
476 strbuf_addf(&buf
, "init.defaultBranch=%s", b
);
480 if ((res
= run_git("-c", buf
.buf
, "init", "--", dir
, NULL
)))
483 if (chdir(dir
) < 0) {
484 res
= error_errno(_("could not switch to '%s'"), dir
);
488 setup_git_directory();
490 /* common-main already logs `argv` */
491 trace2_def_repo(the_repository
);
493 if (!branch
&& !(branch
= remote_default_branch(url
))) {
494 res
= error(_("failed to get default branch for '%s'"), url
);
498 if (set_config("remote.origin.url=%s", url
) ||
499 set_config("remote.origin.fetch="
500 "+refs/heads/%s:refs/remotes/origin/%s",
501 single_branch
? branch
: "*",
502 single_branch
? branch
: "*") ||
503 set_config("remote.origin.promisor=true") ||
504 set_config("remote.origin.partialCloneFilter=blob:none")) {
505 res
= error(_("could not configure remote in '%s'"), dir
);
509 if (!tags
&& set_config("remote.origin.tagOpt=--no-tags")) {
510 res
= error(_("could not disable tags in '%s'"), dir
);
515 (res
= run_git("sparse-checkout", "init", "--cone", NULL
)))
518 if (set_recommended_config(0))
519 return error(_("could not configure '%s'"), dir
);
521 if ((res
= run_git("fetch", "--quiet",
522 show_progress
? "--progress" : "--no-progress",
524 (tags
? NULL
: "--no-tags"),
526 warning(_("partial clone failed; attempting full clone"));
528 if (set_config("remote.origin.promisor") ||
529 set_config("remote.origin.partialCloneFilter")) {
530 res
= error(_("could not configure for full clone"));
534 if ((res
= run_git("fetch", "--quiet",
535 show_progress
? "--progress" : "--no-progress",
540 if ((res
= set_config("branch.%s.remote=origin", branch
)))
542 if ((res
= set_config("branch.%s.merge=refs/heads/%s",
547 strbuf_addf(&buf
, "origin/%s", branch
);
548 res
= run_git("checkout", "-f", "-t", buf
.buf
, NULL
);
552 res
= register_dir();
557 strbuf_release(&buf
);
561 static int cmd_diagnose(int argc
, const char **argv
)
563 struct option options
[] = {
566 const char * const usage
[] = {
567 N_("scalar diagnose [<enlistment>]"),
570 struct strbuf diagnostics_root
= STRBUF_INIT
;
573 argc
= parse_options(argc
, argv
, NULL
, options
,
576 setup_enlistment_directory(argc
, argv
, usage
, options
, &diagnostics_root
);
577 strbuf_addstr(&diagnostics_root
, "/.scalarDiagnostics");
579 res
= run_git("diagnose", "--mode=all", "-s", "%Y%m%d_%H%M%S",
580 "-o", diagnostics_root
.buf
, NULL
);
582 strbuf_release(&diagnostics_root
);
586 static int cmd_list(int argc
, const char **argv UNUSED
)
589 die(_("`scalar list` does not take arguments"));
591 if (run_git("config", "--global", "--get-all", "scalar.repo", NULL
) < 0)
596 static int cmd_register(int argc
, const char **argv
)
598 struct option options
[] = {
601 const char * const usage
[] = {
602 N_("scalar register [<enlistment>]"),
606 argc
= parse_options(argc
, argv
, NULL
, options
,
609 setup_enlistment_directory(argc
, argv
, usage
, options
, NULL
);
611 return register_dir();
614 static int get_scalar_repos(const char *key
, const char *value
,
615 const struct config_context
*ctx UNUSED
,
618 struct string_list
*list
= data
;
620 if (!strcmp(key
, "scalar.repo"))
621 string_list_append(list
, value
);
626 static int remove_deleted_enlistment(struct strbuf
*path
)
629 strbuf_realpath_forgiving(path
, path
->buf
, 1);
631 if (run_git("config", "--global",
632 "--unset", "--fixed-value",
633 "scalar.repo", path
->buf
, NULL
) < 0)
636 if (run_git("config", "--global",
637 "--unset", "--fixed-value",
638 "maintenance.repo", path
->buf
, NULL
) < 0)
644 static int cmd_reconfigure(int argc
, const char **argv
)
647 struct option options
[] = {
648 OPT_BOOL('a', "all", &all
,
649 N_("reconfigure all registered enlistments")),
652 const char * const usage
[] = {
653 N_("scalar reconfigure [--all | <enlistment>]"),
656 struct string_list scalar_repos
= STRING_LIST_INIT_DUP
;
658 struct strbuf commondir
= STRBUF_INIT
, gitdir
= STRBUF_INIT
;
660 argc
= parse_options(argc
, argv
, NULL
, options
,
664 setup_enlistment_directory(argc
, argv
, usage
, options
, NULL
);
666 return set_recommended_config(1);
670 usage_msg_opt(_("--all or <enlistment>, but not both"),
673 git_config(get_scalar_repos
, &scalar_repos
);
675 for (i
= 0; i
< scalar_repos
.nr
; i
++) {
677 struct repository
*old_repo
, r
= { NULL
};
678 const char *dir
= scalar_repos
.items
[i
].string
;
680 strbuf_reset(&commondir
);
681 strbuf_reset(&gitdir
);
683 if (chdir(dir
) < 0) {
684 struct strbuf buf
= STRBUF_INIT
;
686 if (errno
!= ENOENT
) {
687 warning_errno(_("could not switch to '%s'"), dir
);
691 strbuf_addstr(&buf
, dir
);
692 if (remove_deleted_enlistment(&buf
))
693 error(_("could not remove stale "
694 "scalar.repo '%s'"), dir
);
696 warning(_("removed stale scalar.repo '%s'"),
700 strbuf_release(&buf
);
704 switch (discover_git_directory_reason(&commondir
, &gitdir
)) {
705 case GIT_DIR_INVALID_OWNERSHIP
:
706 warning(_("repository at '%s' has different owner"), dir
);
709 case GIT_DIR_INVALID_GITFILE
:
710 case GIT_DIR_INVALID_FORMAT
:
711 warning(_("repository at '%s' has a format issue"), dir
);
714 case GIT_DIR_DISCOVERED
:
719 warning(_("repository not found in '%s'"), dir
);
725 if (repo_init(&r
, gitdir
.buf
, commondir
.buf
))
728 old_repo
= the_repository
;
731 if (set_recommended_config(1) >= 0)
734 the_repository
= old_repo
;
736 if (toggle_maintenance(1) >= 0)
742 warning(_("to unregister this repository from Scalar, run\n"
743 "\tgit config --global --unset --fixed-value scalar.repo \"%s\""),
748 string_list_clear(&scalar_repos
, 1);
749 strbuf_release(&commondir
);
750 strbuf_release(&gitdir
);
755 static int cmd_run(int argc
, const char **argv
)
757 struct option options
[] = {
761 const char *arg
, *task
;
764 { "commit-graph", "commit-graph" },
765 { "fetch", "prefetch" },
766 { "loose-objects", "loose-objects" },
767 { "pack-files", "incremental-repack" },
770 struct strbuf buf
= STRBUF_INIT
;
771 const char *usagestr
[] = { NULL
, NULL
};
774 strbuf_addstr(&buf
, N_("scalar run <task> [<enlistment>]\nTasks:\n"));
775 for (i
= 0; tasks
[i
].arg
; i
++)
776 strbuf_addf(&buf
, "\t%s\n", tasks
[i
].arg
);
777 usagestr
[0] = buf
.buf
;
779 argc
= parse_options(argc
, argv
, NULL
, options
,
783 usage_with_options(usagestr
, options
);
785 if (!strcmp("all", argv
[0])) {
788 for (i
= 0; tasks
[i
].arg
&& strcmp(tasks
[i
].arg
, argv
[0]); i
++)
789 ; /* keep looking for the task */
791 if (i
> 0 && !tasks
[i
].arg
) {
792 error(_("no such task: '%s'"), argv
[0]);
793 usage_with_options(usagestr
, options
);
799 setup_enlistment_directory(argc
, argv
, usagestr
, options
, NULL
);
800 strbuf_release(&buf
);
803 return register_dir();
806 return run_git("maintenance", "run",
807 "--task", tasks
[i
].task
, NULL
);
811 for (i
= 1; tasks
[i
].arg
; i
++)
812 if (run_git("maintenance", "run",
813 "--task", tasks
[i
].task
, NULL
))
818 static int cmd_unregister(int argc
, const char **argv
)
820 struct option options
[] = {
823 const char * const usage
[] = {
824 N_("scalar unregister [<enlistment>]"),
828 argc
= parse_options(argc
, argv
, NULL
, options
,
832 * Be forgiving when the enlistment or worktree does not even exist any
833 * longer; This can be the case if a user deleted the worktree by
834 * mistake and _still_ wants to unregister the thing.
837 struct strbuf src_path
= STRBUF_INIT
, workdir_path
= STRBUF_INIT
;
839 strbuf_addf(&src_path
, "%s/src/.git", argv
[0]);
840 strbuf_addf(&workdir_path
, "%s/.git", argv
[0]);
841 if (!is_directory(src_path
.buf
) && !is_directory(workdir_path
.buf
)) {
842 /* remove possible matching registrations */
845 strbuf_strip_suffix(&src_path
, "/.git");
846 res
= remove_deleted_enlistment(&src_path
) && res
;
848 strbuf_strip_suffix(&workdir_path
, "/.git");
849 res
= remove_deleted_enlistment(&workdir_path
) && res
;
851 strbuf_release(&src_path
);
852 strbuf_release(&workdir_path
);
855 strbuf_release(&src_path
);
856 strbuf_release(&workdir_path
);
859 setup_enlistment_directory(argc
, argv
, usage
, options
, NULL
);
861 return unregister_dir();
864 static int cmd_delete(int argc
, const char **argv
)
866 char *cwd
= xgetcwd();
867 struct option options
[] = {
870 const char * const usage
[] = {
871 N_("scalar delete <enlistment>"),
874 struct strbuf enlistment
= STRBUF_INIT
;
877 argc
= parse_options(argc
, argv
, NULL
, options
,
881 usage_with_options(usage
, options
);
883 setup_enlistment_directory(argc
, argv
, usage
, options
, &enlistment
);
885 if (dir_inside_of(cwd
, enlistment
.buf
) >= 0)
886 res
= error(_("refusing to delete current working directory"));
888 close_object_store(the_repository
->objects
);
889 res
= delete_enlistment(&enlistment
);
891 strbuf_release(&enlistment
);
897 static int cmd_help(int argc
, const char **argv
)
899 struct option options
[] = {
902 const char * const usage
[] = {
907 argc
= parse_options(argc
, argv
, NULL
, options
,
911 usage_with_options(usage
, options
);
913 return run_git("help", "scalar", NULL
);
916 static int cmd_version(int argc
, const char **argv
)
918 int verbose
= 0, build_options
= 0;
919 struct option options
[] = {
920 OPT__VERBOSE(&verbose
, N_("include Git version")),
921 OPT_BOOL(0, "build-options", &build_options
,
922 N_("include Git's build options")),
925 const char * const usage
[] = {
926 N_("scalar verbose [-v | --verbose] [--build-options]"),
929 struct strbuf buf
= STRBUF_INIT
;
931 argc
= parse_options(argc
, argv
, NULL
, options
,
935 usage_with_options(usage
, options
);
937 get_version_info(&buf
, build_options
);
938 fprintf(stderr
, "%s\n", buf
.buf
);
939 strbuf_release(&buf
);
946 int (*fn
)(int, const char **);
948 { "clone", cmd_clone
},
949 { "list", cmd_list
},
950 { "register", cmd_register
},
951 { "unregister", cmd_unregister
},
953 { "reconfigure", cmd_reconfigure
},
954 { "delete", cmd_delete
},
955 { "help", cmd_help
},
956 { "version", cmd_version
},
957 { "diagnose", cmd_diagnose
},
961 int cmd_main(int argc
, const char **argv
)
963 struct strbuf scalar_usage
= STRBUF_INIT
;
966 while (argc
> 1 && *argv
[1] == '-') {
967 if (!strcmp(argv
[1], "-C")) {
969 die(_("-C requires a <directory>"));
970 if (chdir(argv
[2]) < 0)
971 die_errno(_("could not change to '%s'"),
975 } else if (!strcmp(argv
[1], "-c")) {
977 die(_("-c requires a <key>=<value> argument"));
978 git_config_push_parameter(argv
[2]);
989 for (i
= 0; builtins
[i
].name
; i
++)
990 if (!strcmp(builtins
[i
].name
, argv
[0]))
991 return !!builtins
[i
].fn(argc
, argv
);
994 strbuf_addstr(&scalar_usage
,
995 N_("scalar [-C <directory>] [-c <key>=<value>] "
996 "<command> [<options>]\n\nCommands:\n"));
997 for (i
= 0; builtins
[i
].name
; i
++)
998 strbuf_addf(&scalar_usage
, "\t%s\n", builtins
[i
].name
);
1000 usage(scalar_usage
.buf
);