7
#include "parse-options.h"
8
#include "config.h"
9
#include "run-command.h"
10
+#include "refs.h"
11
12
/*
13
* Remove the deepest subdirectory in the provided path string. Path must not
252
return res;
253
}
254
255
+/* printf-style interface, expects `<key>=<value>` argument */
256
+static int set_config(const char *fmt, ...)
257
+{
258
+ struct strbuf buf = STRBUF_INIT;
259
+ char *value;
260
+ int res;
261
+ va_list args;
262
+
263
+ va_start(args, fmt);
264
+ strbuf_vaddf(&buf, fmt, args);
265
+ va_end(args);
266
+
267
+ value = strchr(buf.buf, '=');
268
+ if (value)
269
+ *(value++) = '\0';
270
+ res = git_config_set_gently(buf.buf, value);
271
+ strbuf_release(&buf);
272
+
273
+ return res;
274
+}
275
+
276
+static char *remote_default_branch(const char *url)
277
+{
278
+ struct child_process cp = CHILD_PROCESS_INIT;
279
+ struct strbuf out = STRBUF_INIT;
280
+
281
+ cp.git_cmd = 1;
282
+ strvec_pushl(&cp.args, "ls-remote", "--symref", url, "HEAD", NULL);
283
+ if (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {
284
+ const char *line = out.buf;
285
+
286
+ while (*line) {
287
+ const char *eol = strchrnul(line, '\n'), *p;
288
+ size_t len = eol - line;
289
+ char *branch;
290
+
291
+ if (!skip_prefix(line, "ref: ", &p) ||
292
+ !strip_suffix_mem(line, &len, "\tHEAD")) {
293
+ line = eol + (*eol == '\n');
294
+ continue;
295
+ }
296
+
297
+ eol = line + len;
298
+ if (skip_prefix(p, "refs/heads/", &p)) {
299
+ branch = xstrndup(p, eol - p);
300
+ strbuf_release(&out);
301
+ return branch;
302
+ }
303
+
304
+ error(_("remote HEAD is not a branch: '%.*s'"),
305
+ (int)(eol - p), p);
306
+ strbuf_release(&out);
307
+ return NULL;
308
+ }
309
+ }
310
+ warning(_("failed to get default branch name from remote; "
311
+ "using local default"));
312
+ strbuf_reset(&out);
313
+
314
+ child_process_init(&cp);
315
+ cp.git_cmd = 1;
316
+ strvec_pushl(&cp.args, "symbolic-ref", "--short", "HEAD", NULL);
317
+ if (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {
318
+ strbuf_trim(&out);
319
+ return strbuf_detach(&out, NULL);
320
+ }
321
+
322
+ strbuf_release(&out);
323
+ error(_("failed to get default branch name"));
324
+ return NULL;
325
+}
326
+
327
+static int cmd_clone(int argc, const char **argv)
328
+{
329
+ const char *branch = NULL;
330
+ int full_clone = 0;
331
+ struct option clone_options[] = {
332
+ OPT_STRING('b', "branch", &branch, N_("<branch>"),
333
+ N_("branch to checkout after clone")),
334
+ OPT_BOOL(0, "full-clone", &full_clone,
335
+ N_("when cloning, create full working directory")),
336
+ OPT_END(),
337
+ };
338
+ const char * const clone_usage[] = {
339
+ N_("scalar clone [<options>] [--] <repo> [<dir>]"),
340
+ NULL
341
+ };
342
+ const char *url;
343
+ char *enlistment = NULL, *dir = NULL;
344
+ struct strbuf buf = STRBUF_INIT;
345
+ int res;
346
+
347
+ argc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);
348
+
349
+ if (argc == 2) {
350
+ url = argv[0];
351
+ enlistment = xstrdup(argv[1]);
352
+ } else if (argc == 1) {
353
+ url = argv[0];
354
+
355
+ strbuf_addstr(&buf, url);
356
+ /* Strip trailing slashes, if any */
357
+ while (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))
358
+ strbuf_setlen(&buf, buf.len - 1);
359
+ /* Strip suffix `.git`, if any */
360
+ strbuf_strip_suffix(&buf, ".git");
361
+
362
+ enlistment = find_last_dir_sep(buf.buf);
363
+ if (!enlistment) {
364
+ die(_("cannot deduce worktree name from '%s'"), url);
365
+ }
366
+ enlistment = xstrdup(enlistment + 1);
367
+ } else {
368
+ usage_msg_opt(_("You must specify a repository to clone."),
369
+ clone_usage, clone_options);
370
+ }
371
+
372
+ if (is_directory(enlistment))
373
+ die(_("directory '%s' exists already"), enlistment);
374
+
375
+ dir = xstrfmt("%s/src", enlistment);
376
+
377
+ strbuf_reset(&buf);
378
+ if (branch)
379
+ strbuf_addf(&buf, "init.defaultBranch=%s", branch);
380
+ else {
381
+ char *b = repo_default_branch_name(the_repository, 1);
382
+ strbuf_addf(&buf, "init.defaultBranch=%s", b);
383
+ free(b);
384
+ }
385
+
386
+ if ((res = run_git("-c", buf.buf, "init", "--", dir, NULL)))
387
+ goto cleanup;
388
+
389
+ if (chdir(dir) < 0) {
390
+ res = error_errno(_("could not switch to '%s'"), dir);
391
+ goto cleanup;
392
+ }
393
+
394
+ setup_git_directory();
395
+
396
+ /* common-main already logs `argv` */
397
+ trace2_def_repo(the_repository);
398
+
399
+ if (!branch && !(branch = remote_default_branch(url))) {
400
+ res = error(_("failed to get default branch for '%s'"), url);
401
+ goto cleanup;
402
+ }
403
+
404
+ if (set_config("remote.origin.url=%s", url) ||
405
+ set_config("remote.origin.fetch="
406
+ "+refs/heads/*:refs/remotes/origin/*") ||
407
+ set_config("remote.origin.promisor=true") ||
408
+ set_config("remote.origin.partialCloneFilter=blob:none")) {
409
+ res = error(_("could not configure remote in '%s'"), dir);
410
+ goto cleanup;
411
+ }
412
+
413
+ if (!full_clone &&
414
+ (res = run_git("sparse-checkout", "init", "--cone", NULL)))
415
+ goto cleanup;
416
+
417
+ if (set_recommended_config())
418
+ return error(_("could not configure '%s'"), dir);
419
+
420
+ if ((res = run_git("fetch", "--quiet", "origin", NULL))) {
421
+ warning(_("partial clone failed; attempting full clone"));
422
+
423
+ if (set_config("remote.origin.promisor") ||
424
+ set_config("remote.origin.partialCloneFilter")) {
425
+ res = error(_("could not configure for full clone"));
426
+ goto cleanup;
427
+ }
428
+
429
+ if ((res = run_git("fetch", "--quiet", "origin", NULL)))
430
+ goto cleanup;
431
+ }
432
+
433
+ if ((res = set_config("branch.%s.remote=origin", branch)))
434
+ goto cleanup;
435
+ if ((res = set_config("branch.%s.merge=refs/heads/%s",
436
+ branch, branch)))
437
+ goto cleanup;
438
+
439
+ strbuf_reset(&buf);
440
+ strbuf_addf(&buf, "origin/%s", branch);
441
+ res = run_git("checkout", "-f", "-t", buf.buf, NULL);
442
+ if (res)
443
+ goto cleanup;
444
+
445
+ res = register_dir();
446
+
447
+cleanup:
448
+ free(enlistment);
449
+ free(dir);
450
+ strbuf_release(&buf);
451
+ return res;
452
+}
453
+
454
static int cmd_list(int argc, const char **argv)
455
{
456
if (argc != 1)
547
const char *name;
548
int (*fn)(int, const char **);
549
} builtins[] = {
550
+ { "clone", cmd_clone },
551
{ "list", cmd_list },
552
{ "register", cmd_register },
553
{ "unregister", cmd_unregister },