summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2022-02-26 22:17:36 -0500
committerEdward Thomson <ethomson@edwardthomson.com>2022-02-26 22:17:36 -0500
commit4014022414255de31fae5b8e04d92dc305511ad2 (patch)
treee3c2594abca2854b6912b29dfe233d2f58765475
parenta42808302cb3a942e0b27394e70abba873fc088a (diff)
downloadlibgit2-4014022414255de31fae5b8e04d92dc305511ad2.tar.gz
cli: add a clone command
-rw-r--r--src/cli/cmd.h1
-rw-r--r--src/cli/cmd_clone.c122
-rw-r--r--src/cli/main.c1
3 files changed, 124 insertions, 0 deletions
diff --git a/src/cli/cmd.h b/src/cli/cmd.h
index 664b5021a..8b1a1b38f 100644
--- a/src/cli/cmd.h
+++ b/src/cli/cmd.h
@@ -26,6 +26,7 @@ extern const cli_cmd_spec *cli_cmd_spec_byname(const char *name);
/* Commands */
extern int cmd_cat_file(int argc, char **argv);
+extern int cmd_clone(int argc, char **argv);
extern int cmd_hash_object(int argc, char **argv);
extern int cmd_help(int argc, char **argv);
diff --git a/src/cli/cmd_clone.c b/src/cli/cmd_clone.c
new file mode 100644
index 000000000..4dd5db858
--- /dev/null
+++ b/src/cli/cmd_clone.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <git2.h>
+#include "cli.h"
+#include "cmd.h"
+#include "fs_path.h"
+#include "futils.h"
+
+#define COMMAND_NAME "clone"
+
+static int show_help;
+static int quiet;
+static bool local_path_exists;
+
+static char *remote_path, *local_path;
+
+static const cli_opt_spec opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
+ "display help about the " COMMAND_NAME " command" },
+
+ { CLI_OPT_TYPE_BOOL, "quiet", 'q', &quiet, 0,
+ CLI_OPT_USAGE_DEFAULT, NULL, "do not display progress output" },
+
+ { CLI_OPT_TYPE_LITERAL },
+ { CLI_OPT_TYPE_ARG, "repository", 0, &remote_path, 0,
+ CLI_OPT_USAGE_REQUIRED, "repository", "path to repository to clone" },
+ { CLI_OPT_TYPE_ARG, "directory", 0, &local_path, 0,
+ CLI_OPT_USAGE_DEFAULT, "directory", "directory to clone into" },
+ { 0 },
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Clone an existing repository into a local directory.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+static char *compute_local_path(const char *orig_path)
+{
+ const char *slash;
+ char *local_path;
+
+ if ((slash = strrchr(orig_path, '/')) == NULL &&
+ (slash = strrchr(orig_path, '\\')) == NULL)
+ local_path = git__strdup(orig_path);
+ else
+ local_path = git__strdup(slash + 1);
+
+ return local_path;
+}
+
+static bool validate_local_path(const char *path)
+{
+ if (!git_fs_path_exists(path))
+ return false;
+
+ if (!git_fs_path_isdir(path) || !git_fs_path_is_empty_dir(path))
+ cli_die("fatal: destination path '%s' already exists and is not an empty directory.\n", path);
+
+ return true;
+}
+
+static void cleanup(void)
+{
+ int rmdir_flags = GIT_RMDIR_REMOVE_FILES;
+
+ if (local_path_exists)
+ rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
+
+ if (!git_fs_path_isdir(local_path))
+ return;
+
+ if (git_futils_rmdir_r(local_path, NULL, rmdir_flags) < 0)
+ cli_die_git();
+}
+
+int cmd_clone(int argc, char **argv)
+{
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo = NULL;
+ cli_opt invalid_opt;
+ char *computed_path = NULL;
+
+ if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
+ return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
+
+ if (show_help) {
+ print_help();
+ return 0;
+ }
+
+ if (!local_path)
+ local_path = computed_path = compute_local_path(remote_path);
+
+ local_path_exists = validate_local_path(local_path);
+
+ if (!quiet)
+ printf("Cloning into '%s'...\n", local_path);
+
+ if (git_clone(&repo, remote_path, local_path, &clone_opts) < 0) {
+ cleanup();
+ cli_die_git();
+ }
+
+ git__free(computed_path);
+ git_repository_free(repo);
+
+ return 0;
+}
diff --git a/src/cli/main.c b/src/cli/main.c
index 08abb324f..72f577c7a 100644
--- a/src/cli/main.c
+++ b/src/cli/main.c
@@ -29,6 +29,7 @@ const cli_opt_spec cli_common_opts[] = {
const cli_cmd_spec cli_cmds[] = {
{ "cat-file", cmd_cat_file, "Display an object in the repository" },
+ { "clone", cmd_clone, "Clone an existing repository into a local directory" },
{ "hash-object", cmd_hash_object, "Hash a raw object and product its object ID" },
{ "help", cmd_help, "Display help information" },
{ NULL }