summaryrefslogtreecommitdiffstats
path: root/src/cli
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-21 05:03:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-21 05:03:06 +0000
commit4ffc4372dff6595a86120ec0bd311c4cb5fb8ed6 (patch)
tree582224c3f88627b5797d1b57ccaf158c7d36118e /src/cli
parentReleasing progress-linux version 1.7.2+ds-1~progress7.99u1. (diff)
downloadlibgit2-4ffc4372dff6595a86120ec0bd311c4cb5fb8ed6.tar.xz
libgit2-4ffc4372dff6595a86120ec0bd311c4cb5fb8ed6.zip
Merging upstream version 1.8.1+ds.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cli')
-rw-r--r--src/cli/CMakeLists.txt3
-rw-r--r--src/cli/cli.h20
-rw-r--r--src/cli/cmd.c2
-rw-r--r--src/cli/cmd.h2
-rw-r--r--src/cli/cmd_cat_file.c9
-rw-r--r--src/cli/cmd_clone.c6
-rw-r--r--src/cli/cmd_config.c242
-rw-r--r--src/cli/cmd_hash_object.c9
-rw-r--r--src/cli/cmd_help.c6
-rw-r--r--src/cli/cmd_index_pack.c114
-rw-r--r--src/cli/common.c126
-rw-r--r--src/cli/common.h66
-rw-r--r--src/cli/error.h2
-rw-r--r--src/cli/main.c58
-rw-r--r--src/cli/opt.c32
-rw-r--r--src/cli/opt.h24
-rw-r--r--src/cli/opt_usage.c2
-rw-r--r--src/cli/progress.c53
-rw-r--r--src/cli/progress.h12
-rw-r--r--src/cli/unix/sighandler.c3
-rw-r--r--src/cli/win32/precompiled.h2
-rw-r--r--src/cli/win32/sighandler.c2
22 files changed, 721 insertions, 74 deletions
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
index 84b6c19..97797e3 100644
--- a/src/cli/CMakeLists.txt
+++ b/src/cli/CMakeLists.txt
@@ -4,7 +4,8 @@ set(CLI_INCLUDES
"${libgit2_SOURCE_DIR}/src/util"
"${libgit2_SOURCE_DIR}/src/cli"
"${libgit2_SOURCE_DIR}/include"
- "${LIBGIT2_DEPENDENCY_INCLUDES}")
+ "${LIBGIT2_DEPENDENCY_INCLUDES}"
+ "${LIBGIT2_SYSTEM_INCLUDES}")
if(WIN32 AND NOT CYGWIN)
file(GLOB CLI_SRC_OS win32/*.c)
diff --git a/src/cli/cli.h b/src/cli/cli.h
deleted file mode 100644
index 7dede67..0000000
--- a/src/cli/cli.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef CLI_cli_h__
-#define CLI_cli_h__
-
-#define PROGRAM_NAME "git2"
-
-#include "git2_util.h"
-
-#include "error.h"
-#include "opt.h"
-#include "opt_usage.h"
-#include "sighandler.h"
-
-#endif /* CLI_cli_h__ */
diff --git a/src/cli/cmd.c b/src/cli/cmd.c
index 2a7e71c..0b1fafb 100644
--- a/src/cli/cmd.c
+++ b/src/cli/cmd.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
const cli_cmd_spec *cli_cmd_spec_byname(const char *name)
diff --git a/src/cli/cmd.h b/src/cli/cmd.h
index 8b1a1b3..bd88122 100644
--- a/src/cli/cmd.h
+++ b/src/cli/cmd.h
@@ -27,7 +27,9 @@ 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_config(int argc, char **argv);
extern int cmd_hash_object(int argc, char **argv);
extern int cmd_help(int argc, char **argv);
+extern int cmd_index_pack(int argc, char **argv);
#endif /* CLI_cmd_h__ */
diff --git a/src/cli/cmd_cat_file.c b/src/cli/cmd_cat_file.c
index fb53a72..90ee603 100644
--- a/src/cli/cmd_cat_file.c
+++ b/src/cli/cmd_cat_file.c
@@ -6,7 +6,7 @@
*/
#include <git2.h>
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
#define COMMAND_NAME "cat-file"
@@ -24,9 +24,7 @@ static int display = DISPLAY_CONTENT;
static char *type_name, *object_spec;
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_COMMON_OPT,
{ CLI_OPT_TYPE_SWITCH, NULL, 't', &display, DISPLAY_TYPE,
CLI_OPT_USAGE_REQUIRED, NULL, "display the type of the object" },
@@ -139,6 +137,7 @@ static int print_pretty(git_object *object)
int cmd_cat_file(int argc, char **argv)
{
+ cli_repository_open_options open_opts = { argv + 1, argc - 1};
git_repository *repo = NULL;
git_object *object = NULL;
git_object_t type;
@@ -153,7 +152,7 @@ int cmd_cat_file(int argc, char **argv)
return 0;
}
- if (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0)
+ if (cli_repository_open(&repo, &open_opts) < 0)
return cli_error_git();
if ((giterr = git_revparse_single(&object, repo, object_spec)) < 0) {
diff --git a/src/cli/cmd_clone.c b/src/cli/cmd_clone.c
index e477625..7d9736f 100644
--- a/src/cli/cmd_clone.c
+++ b/src/cli/cmd_clone.c
@@ -7,7 +7,7 @@
#include <stdio.h>
#include <git2.h>
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
#include "error.h"
#include "sighandler.h"
@@ -24,9 +24,7 @@ static bool local_path_exists;
static cli_progress progress = CLI_PROGRESS_INIT;
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_COMMON_OPT,
{ CLI_OPT_TYPE_SWITCH, "quiet", 'q', &quiet, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display the type of the object" },
diff --git a/src/cli/cmd_config.c b/src/cli/cmd_config.c
new file mode 100644
index 0000000..6b9d373
--- /dev/null
+++ b/src/cli/cmd_config.c
@@ -0,0 +1,242 @@
+/*
+ * 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 "common.h"
+#include "cmd.h"
+
+#define COMMAND_NAME "config"
+
+typedef enum {
+ ACTION_NONE = 0,
+ ACTION_GET,
+ ACTION_ADD,
+ ACTION_REPLACE_ALL,
+ ACTION_LIST
+} action_t;
+
+static action_t action = ACTION_NONE;
+static int show_origin;
+static int show_scope;
+static int show_help;
+static int null_separator;
+static int config_level;
+static char *config_filename;
+static char *name, *value, *value_pattern;
+
+static const cli_opt_spec opts[] = {
+ CLI_COMMON_OPT, \
+
+ { CLI_OPT_TYPE_SWITCH, "null", 'z', &null_separator, 1,
+ 0, NULL, "use NUL as a separator" },
+
+ { CLI_OPT_TYPE_SWITCH, "system", 0, &config_level, GIT_CONFIG_LEVEL_SYSTEM,
+ 0, NULL, "read/write to system configuration" },
+ { CLI_OPT_TYPE_SWITCH, "global", 0, &config_level, GIT_CONFIG_LEVEL_GLOBAL,
+ CLI_OPT_USAGE_CHOICE, NULL, "read/write to global configuration" },
+ { CLI_OPT_TYPE_SWITCH, "local", 0, &config_level, GIT_CONFIG_LEVEL_LOCAL,
+ CLI_OPT_USAGE_CHOICE, NULL, "read/write to local configuration" },
+ { CLI_OPT_TYPE_VALUE, "file", 0, &config_filename, 0,
+ CLI_OPT_USAGE_CHOICE, "filename", "read/write to specified configuration file" },
+
+ { CLI_OPT_TYPE_SWITCH, "get", 0, &action, ACTION_GET,
+ CLI_OPT_USAGE_REQUIRED, NULL, "get a configuration value" },
+ { CLI_OPT_TYPE_SWITCH, "add", 0, &action, ACTION_ADD,
+ CLI_OPT_USAGE_CHOICE, NULL, "add a configuration value" },
+ { CLI_OPT_TYPE_SWITCH, "replace-all", 0, &action, ACTION_REPLACE_ALL,
+ CLI_OPT_USAGE_CHOICE, NULL, "add a configuration value, replacing any old values" },
+ { CLI_OPT_TYPE_SWITCH, "list", 'l', &action, ACTION_LIST,
+ CLI_OPT_USAGE_CHOICE | CLI_OPT_USAGE_SHOW_LONG,
+ NULL, "list all configuration entries" },
+ { CLI_OPT_TYPE_SWITCH, "show-origin", 0, &show_origin, 1,
+ 0, NULL, "show origin of configuration" },
+ { CLI_OPT_TYPE_SWITCH, "show-scope", 0, &show_scope, 1,
+ 0, NULL, "show scope of configuration" },
+ { CLI_OPT_TYPE_ARG, "name", 0, &name, 0,
+ 0, "name", "name of configuration entry" },
+ { CLI_OPT_TYPE_ARG, "value", 0, &value, 0,
+ 0, "value", "value of configuration entry" },
+ { CLI_OPT_TYPE_ARG, "regexp", 0, &value_pattern, 0,
+ 0, "regexp", "regular expression of values to replace" },
+ { 0 },
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Query and set configuration options.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+static int get_config(git_config *config)
+{
+ git_buf value = GIT_BUF_INIT;
+ char sep = null_separator ? '\0' : '\n';
+ int error;
+
+ error = git_config_get_string_buf(&value, config, name);
+
+ if (error && error != GIT_ENOTFOUND)
+ return cli_error_git();
+
+ else if (error == GIT_ENOTFOUND)
+ return 1;
+
+ printf("%s%c", value.ptr, sep);
+ return 0;
+}
+
+static int add_config(git_config *config)
+{
+ if (git_config_set_multivar(config, name, "$^", value) < 0)
+ return cli_error_git();
+
+ return 0;
+}
+
+static int replace_all_config(git_config *config)
+{
+ if (git_config_set_multivar(config, name, value_pattern ? value_pattern : ".*", value) < 0)
+ return cli_error_git();
+
+ return 0;
+}
+
+static const char *level_name(git_config_level_t level)
+{
+ switch (level) {
+ case GIT_CONFIG_LEVEL_PROGRAMDATA:
+ return "programdata";
+ case GIT_CONFIG_LEVEL_SYSTEM:
+ return "system";
+ case GIT_CONFIG_LEVEL_XDG:
+ return "global";
+ case GIT_CONFIG_LEVEL_GLOBAL:
+ return "global";
+ case GIT_CONFIG_LEVEL_LOCAL:
+ return "local";
+ case GIT_CONFIG_LEVEL_APP:
+ return "command";
+ default:
+ return "unknown";
+ }
+}
+
+static int list_config(git_config *config)
+{
+ git_config_iterator *iterator;
+ git_config_entry *entry;
+ char data_separator = null_separator ? '\0' : '\t';
+ char kv_separator = null_separator ? '\n' : '=';
+ char entry_separator = null_separator ? '\0' : '\n';
+ int error;
+
+ if (git_config_iterator_new(&iterator, config) < 0)
+ return cli_error_git();
+
+ while ((error = git_config_next(&entry, iterator)) == 0) {
+ if (show_scope)
+ printf("%s%c",
+ level_name(entry->level),
+ data_separator);
+
+ if (show_origin)
+ printf("%s%s%s%c",
+ entry->backend_type ? entry->backend_type : "",
+ entry->backend_type && entry->origin_path ? ":" : "",
+ entry->origin_path ? entry->origin_path : "",
+ data_separator);
+
+ printf("%s%c%s%c", entry->name, kv_separator, entry->value,
+ entry_separator);
+ }
+
+ if (error != GIT_ITEROVER)
+ return cli_error_git();
+
+ git_config_iterator_free(iterator);
+ return 0;
+}
+
+int cmd_config(int argc, char **argv)
+{
+ git_repository *repo = NULL;
+ git_config *config = NULL;
+ cli_repository_open_options open_opts = { argv + 1, argc - 1};
+ cli_opt invalid_opt;
+ int ret = 0;
+
+ 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 (config_filename) {
+ if (git_config_new(&config) < 0 ||
+ git_config_add_file_ondisk(config, config_filename,
+ GIT_CONFIG_LEVEL_APP, NULL, 0) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+ } else {
+ if (cli_repository_open(&repo, &open_opts) < 0 ||
+ git_repository_config(&config, repo) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ if (config_level &&
+ git_config_open_level(&config, config, config_level) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+ }
+
+ switch (action) {
+ case ACTION_ADD:
+ if (!name || !value || value_pattern)
+ ret = cli_error_usage("%s --add requires two arguments", COMMAND_NAME);
+ else
+ ret = add_config(config);
+ break;
+ case ACTION_REPLACE_ALL:
+ if (!name || !value)
+ ret = cli_error_usage("%s --replace-all requires two or three arguments", COMMAND_NAME);
+ else
+ ret = replace_all_config(config);
+ break;
+ case ACTION_GET:
+ if (!name)
+ ret = cli_error_usage("%s --get requires an argument", COMMAND_NAME);
+ else
+ ret = get_config(config);
+ break;
+ case ACTION_LIST:
+ if (name)
+ ret = cli_error_usage("%s --list does not take an argument", COMMAND_NAME);
+ else
+ ret = list_config(config);
+ break;
+ default:
+ ret = cli_error_usage("unknown action");
+ }
+
+done:
+ git_config_free(config);
+ git_repository_free(repo);
+ return ret;
+}
diff --git a/src/cli/cmd_hash_object.c b/src/cli/cmd_hash_object.c
index 93b980d..741debb 100644
--- a/src/cli/cmd_hash_object.c
+++ b/src/cli/cmd_hash_object.c
@@ -6,7 +6,7 @@
*/
#include <git2.h>
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
#include "futils.h"
@@ -19,9 +19,7 @@ static int write_object, read_stdin, literally;
static char **filenames;
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_COMMON_OPT,
{ CLI_OPT_TYPE_VALUE, NULL, 't', &type_name, 0,
CLI_OPT_USAGE_DEFAULT, "type", "the type of object to hash (default: \"blob\")" },
@@ -92,6 +90,7 @@ static int hash_buf(
int cmd_hash_object(int argc, char **argv)
{
+ cli_repository_open_options open_opts = { argv + 1, argc - 1};
git_repository *repo = NULL;
git_odb *odb = NULL;
git_oid_t oid_type;
@@ -113,7 +112,7 @@ int cmd_hash_object(int argc, char **argv)
return cli_error_usage("invalid object type '%s'", type_name);
if (write_object &&
- (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0 ||
+ (cli_repository_open(&repo, &open_opts) < 0 ||
git_repository_odb(&odb, repo) < 0)) {
ret = cli_error_git();
goto done;
diff --git a/src/cli/cmd_help.c b/src/cli/cmd_help.c
index 7ee9822..5e877e0 100644
--- a/src/cli/cmd_help.c
+++ b/src/cli/cmd_help.c
@@ -7,7 +7,7 @@
#include <stdio.h>
#include <git2.h>
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
#define COMMAND_NAME "help"
@@ -16,8 +16,8 @@ static char *command;
static int show_help;
static const cli_opt_spec opts[] = {
- { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
- CLI_OPT_USAGE_HIDDEN, NULL, "display help about the help command" },
+ CLI_COMMON_OPT,
+
{ CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
CLI_OPT_USAGE_DEFAULT, "command", "the command to show help for" },
{ 0 },
diff --git a/src/cli/cmd_index_pack.c b/src/cli/cmd_index_pack.c
new file mode 100644
index 0000000..09685c5
--- /dev/null
+++ b/src/cli/cmd_index_pack.c
@@ -0,0 +1,114 @@
+/*
+ * 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 "common.h"
+#include "cmd.h"
+#include "progress.h"
+
+#define COMMAND_NAME "index-pack"
+
+#define BUFFER_SIZE (1024 * 1024)
+
+static int show_help, verbose, read_stdin;
+static char *filename;
+static cli_progress progress = CLI_PROGRESS_INIT;
+
+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_SWITCH, "verbose", 'v', &verbose, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display progress output" },
+
+ { CLI_OPT_TYPE_LITERAL },
+
+ { CLI_OPT_TYPE_SWITCH, "stdin", 0, &read_stdin, 1,
+ CLI_OPT_USAGE_REQUIRED, NULL, "read from stdin" },
+ { CLI_OPT_TYPE_ARG, "pack-file", 0, &filename, 0,
+ CLI_OPT_USAGE_CHOICE, "pack-file", "packfile path" },
+
+ { 0 },
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Indexes a packfile and writes the index to disk.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+int cmd_index_pack(int argc, char **argv)
+{
+ cli_opt invalid_opt;
+ git_indexer *idx = NULL;
+ git_indexer_options idx_opts = GIT_INDEXER_OPTIONS_INIT;
+ git_indexer_progress stats = {0};
+ char buf[BUFFER_SIZE];
+ ssize_t read_len;
+ int fd, ret;
+
+ 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 (verbose) {
+ idx_opts.progress_cb = cli_progress_indexer;
+ idx_opts.progress_cb_payload = &progress;
+ }
+
+ if (read_stdin) {
+ fd = fileno(stdin);
+ } else if ((fd = p_open(filename, O_RDONLY)) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ ret = git_indexer_new(&idx, ".", GIT_OID_SHA1, &idx_opts);
+#else
+ ret = git_indexer_new(&idx, ".", 0, NULL, &idx_opts);
+#endif
+
+ if (ret < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ while ((read_len = p_read(fd, buf, sizeof(buf))) > 0) {
+ if (git_indexer_append(idx, buf, (size_t)read_len, &stats) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+ }
+
+ if (git_indexer_commit(idx, &stats) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ cli_progress_finish(&progress);
+
+done:
+ if (!read_stdin && fd >= 0)
+ p_close(fd);
+
+ cli_progress_dispose(&progress);
+ git_indexer_free(idx);
+ return ret;
+}
diff --git a/src/cli/common.c b/src/cli/common.c
new file mode 100644
index 0000000..60b0358
--- /dev/null
+++ b/src/cli/common.c
@@ -0,0 +1,126 @@
+/*
+ * 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 <git2/sys/config.h>
+
+#include "git2_util.h"
+#include "vector.h"
+
+#include "common.h"
+#include "error.h"
+
+static int parse_option(cli_opt *opt, void *data)
+{
+ git_str kv = GIT_STR_INIT, env = GIT_STR_INIT;
+ git_vector *cmdline_config = data;
+ int error = 0;
+
+ if (opt->spec && opt->spec->alias == 'c') {
+ if (git_str_puts(&kv, opt->value) < 0) {
+ error = cli_error_git();
+ goto done;
+ }
+ }
+
+ else if (opt->spec && !strcmp(opt->spec->name, "config-env")) {
+ char *val = strchr(opt->value, '=');
+
+ if (val == NULL || *(val + 1) == '\0') {
+ error = cli_error("invalid config format: '%s'", opt->value);
+ goto done;
+ }
+
+ if (git_str_put(&kv, opt->value, (val - opt->value)) < 0) {
+ error = cli_error_git();
+ goto done;
+ }
+
+ val++;
+
+ if ((error = git__getenv(&env, val)) == GIT_ENOTFOUND) {
+ error = cli_error("missing environment variable '%s' for configuration '%s'", val, kv.ptr);
+ goto done;
+ } else if (error) {
+ error = cli_error_git();
+ goto done;
+ }
+
+ if (git_str_putc(&kv, '=') < 0 ||
+ git_str_puts(&kv, env.ptr) < 0) {
+ error = cli_error_git();
+ goto done;
+ }
+ }
+
+ if (kv.size > 0 &&
+ git_vector_insert(cmdline_config, git_str_detach(&kv)) < 0)
+ error = cli_error_git();
+
+done:
+ git_str_dispose(&env);
+ git_str_dispose(&kv);
+ return error;
+}
+
+static int parse_common_options(
+ git_repository *repo,
+ cli_repository_open_options *opts)
+{
+ cli_opt_spec common_opts[] = {
+ { CLI_COMMON_OPT_CONFIG },
+ { CLI_COMMON_OPT_CONFIG_ENV },
+ { 0 }
+ };
+ git_config_backend_memory_options config_opts =
+ GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
+ git_vector cmdline = GIT_VECTOR_INIT;
+ git_config *config = NULL;
+ git_config_backend *backend = NULL;
+ int error = 0;
+
+ config_opts.backend_type = "command line";
+
+ if ((error = cli_opt_foreach(common_opts, opts->args,
+ opts->args_len, CLI_OPT_PARSE_GNU, parse_option,
+ &cmdline)) < 0)
+ goto done;
+
+ if (git_vector_length(&cmdline) == 0)
+ goto done;
+
+ if (git_repository_config(&config, repo) < 0 ||
+ git_config_backend_from_values(&backend,
+ (const char **)cmdline.contents, cmdline.length,
+ &config_opts) < 0 ||
+ git_config_add_backend(config, backend, GIT_CONFIG_LEVEL_APP,
+ repo, 0) < 0)
+ error = cli_error_git();
+
+done:
+ if (error && backend)
+ backend->free(backend);
+ git_config_free(config);
+ git_vector_free_deep(&cmdline);
+ return error;
+}
+
+int cli_repository_open(
+ git_repository **out,
+ cli_repository_open_options *opts)
+{
+ git_repository *repo;
+
+ if (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0)
+ return -1;
+
+ if (opts && parse_common_options(repo, opts) < 0)
+ return -1;
+
+ *out = repo;
+ return 0;
+}
diff --git a/src/cli/common.h b/src/cli/common.h
new file mode 100644
index 0000000..3aed8ad
--- /dev/null
+++ b/src/cli/common.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef CLI_common_h__
+#define CLI_common_h__
+
+#define PROGRAM_NAME "git2"
+
+#include "git2_util.h"
+
+#include "error.h"
+#include "opt.h"
+#include "opt_usage.h"
+
+/*
+ * Common command arguments.
+ */
+
+#define CLI_COMMON_OPT_HELP \
+ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, \
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING
+#define CLI_COMMON_OPT_CONFIG \
+ CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0, \
+ CLI_OPT_USAGE_HIDDEN
+#define CLI_COMMON_OPT_CONFIG_ENV \
+ CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0, \
+ CLI_OPT_USAGE_HIDDEN
+
+#define CLI_COMMON_OPT \
+ { CLI_COMMON_OPT_HELP }, \
+ { CLI_COMMON_OPT_CONFIG }, \
+ { CLI_COMMON_OPT_CONFIG_ENV }
+
+typedef struct {
+ char **args;
+ int args_len;
+} cli_repository_open_options;
+
+extern int cli_repository_open(
+ git_repository **out,
+ cli_repository_open_options *opts);
+
+/*
+ * Common command arguments.
+ */
+
+#define CLI_COMMON_OPT_HELP \
+ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, \
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING
+#define CLI_COMMON_OPT_CONFIG \
+ CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0, \
+ CLI_OPT_USAGE_HIDDEN
+#define CLI_COMMON_OPT_CONFIG_ENV \
+ CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0, \
+ CLI_OPT_USAGE_HIDDEN
+
+#define CLI_COMMON_OPT \
+ { CLI_COMMON_OPT_HELP }, \
+ { CLI_COMMON_OPT_CONFIG }, \
+ { CLI_COMMON_OPT_CONFIG_ENV }
+
+#endif /* CLI_common_h__ */
diff --git a/src/cli/error.h b/src/cli/error.h
index cce7a54..abf8a51 100644
--- a/src/cli/error.h
+++ b/src/cli/error.h
@@ -8,7 +8,7 @@
#ifndef CLI_error_h__
#define CLI_error_h__
-#include "cli.h"
+#include "common.h"
#include <stdio.h>
#define CLI_EXIT_OK 0
diff --git a/src/cli/main.c b/src/cli/main.c
index cbfc50e..c7a6fcf 100644
--- a/src/cli/main.c
+++ b/src/cli/main.c
@@ -7,7 +7,7 @@
#include <stdio.h>
#include <git2.h>
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
static int show_help = 0;
@@ -16,8 +16,12 @@ static char *command = NULL;
static char **args = NULL;
const cli_opt_spec cli_common_opts[] = {
- { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
- CLI_OPT_USAGE_DEFAULT, NULL, "display help information" },
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display help information" },
+ { CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0,
+ CLI_OPT_USAGE_DEFAULT, "key=value", "add configuration value" },
+ { CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0,
+ CLI_OPT_USAGE_DEFAULT, "key=value", "set configuration value to environment variable" },
{ CLI_OPT_TYPE_SWITCH, "version", 0, &show_version, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display the version" },
{ CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
@@ -30,19 +34,40 @@ 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 a repository into a new directory" },
+ { "config", cmd_config, "View or set configuration values " },
{ "hash-object", cmd_hash_object, "Hash a raw object and product its object ID" },
{ "help", cmd_help, "Display help information" },
+ { "index-pack", cmd_index_pack, "Create an index for a packfile" },
{ NULL }
};
+/*
+ * Reorder the argv as it was given, since git has the notion of global
+ * options (like `--help` or `-c key=val`) that we want to pass to the
+ * subcommand, and that can appear early in the arguments, before the
+ * command name. Put the command-name in argv[1] to allow easier parsing.
+ */
+static void reorder_args(char **argv, size_t first)
+{
+ char *tmp;
+ size_t i;
+
+ if (first == 1)
+ return;
+
+ tmp = argv[first];
+
+ for (i = first; i > 1; i--)
+ argv[i] = argv[i - 1];
+
+ argv[1] = tmp;
+}
+
int main(int argc, char **argv)
{
const cli_cmd_spec *cmd;
cli_opt_parser optparser;
cli_opt opt;
- char *help_args[3] = { NULL };
- int help_args_len;
- int args_len = 0;
int ret = 0;
if (git_libgit2_init() < 0) {
@@ -66,8 +91,7 @@ int main(int argc, char **argv)
* remaining arguments as args for the command itself.
*/
if (command) {
- args = &argv[optparser.idx];
- args_len = (int)(argc - optparser.idx);
+ reorder_args(argv, optparser.idx);
break;
}
}
@@ -77,19 +101,9 @@ int main(int argc, char **argv)
goto done;
}
- /*
- * If `--help <command>` is specified, delegate to that command's
- * `--help` option. If no command is specified, run the `help`
- * command. Do this by updating the args to emulate that behavior.
- */
- if (!command || show_help) {
- help_args[0] = command ? (char *)command : "help";
- help_args[1] = command ? "--help" : NULL;
- help_args_len = command ? 2 : 1;
-
- command = help_args[0];
- args = help_args;
- args_len = help_args_len;
+ if (!command) {
+ ret = cmd_help(argc, argv);
+ goto done;
}
if ((cmd = cli_cmd_spec_byname(command)) == NULL) {
@@ -98,7 +112,7 @@ int main(int argc, char **argv)
goto done;
}
- ret = cmd->fn(args_len, args);
+ ret = cmd->fn(argc - 1, &argv[1]);
done:
git_libgit2_shutdown();
diff --git a/src/cli/opt.c b/src/cli/opt.c
index 62a3430..9242e22 100644
--- a/src/cli/opt.c
+++ b/src/cli/opt.c
@@ -10,7 +10,7 @@
* This file was produced by using the `rename.pl` script included with
* adopt. The command-line specified was:
*
- * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
+ * ./rename.pl cli_opt --filename=opt --include=common.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
*/
#include <stdlib.h>
@@ -19,7 +19,11 @@
#include <limits.h>
#include <assert.h>
-#include "cli.h"
+#if defined(__sun) || defined(__illumos__)
+# include <alloca.h>
+#endif
+
+#include "common.h"
#include "opt.h"
#ifdef _WIN32
@@ -73,7 +77,7 @@ GIT_INLINE(const cli_opt_spec *) spec_for_long(
/* Handle --option=value arguments */
if (spec->type == CLI_OPT_TYPE_VALUE &&
- eql &&
+ spec->name && eql &&
strncmp(arg, spec->name, eql_pos) == 0 &&
spec->name[eql_pos] == '\0') {
*has_value = 1;
@@ -575,6 +579,28 @@ cli_opt_status_t cli_opt_parse(
return validate_required(opt, specs, given_specs);
}
+int cli_opt_foreach(
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags,
+ int (*callback)(cli_opt *, void *),
+ void *callback_data)
+{
+ cli_opt_parser parser;
+ cli_opt opt;
+ int ret;
+
+ cli_opt_parser_init(&parser, specs, args, args_len, flags);
+
+ while (cli_opt_parser_next(&opt, &parser)) {
+ if ((ret = callback(&opt, callback_data)) != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int spec_name_fprint(FILE *file, const cli_opt_spec *spec)
{
int error;
diff --git a/src/cli/opt.h b/src/cli/opt.h
index 6c1d460..226f74d 100644
--- a/src/cli/opt.h
+++ b/src/cli/opt.h
@@ -10,7 +10,7 @@
* This file was produced by using the `rename.pl` script included with
* adopt. The command-line specified was:
*
- * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
+ * ./rename.pl cli_opt --filename=opt --include=common.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
*/
#ifndef CLI_opt_h__
@@ -275,8 +275,8 @@ typedef struct cli_opt_parser {
size_t arg_idx;
size_t in_args;
size_t in_short;
- int needs_sort : 1,
- in_literal : 1;
+ unsigned int needs_sort : 1,
+ in_literal : 1;
} cli_opt_parser;
/**
@@ -301,6 +301,24 @@ cli_opt_status_t cli_opt_parse(
unsigned int flags);
/**
+ * Quickly executes the given callback for each argument.
+ *
+ * @param specs A NULL-terminated array of `cli_opt_spec`s that can be parsed
+ * @param args The arguments that will be parsed
+ * @param args_len The length of arguments to be parsed
+ * @param flags The `cli_opt_flag_t flags for parsing
+ * @param callback The callback to invoke for each specified option
+ * @param callback_data Data to be provided to the callback
+ */
+int cli_opt_foreach(
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags,
+ int (*callback)(cli_opt *, void *),
+ void *callback_data);
+
+/**
* Initializes a parser that parses the given arguments according to the
* given specifications.
*
diff --git a/src/cli/opt_usage.c b/src/cli/opt_usage.c
index 478b416..8374f51 100644
--- a/src/cli/opt_usage.c
+++ b/src/cli/opt_usage.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "cli.h"
+#include "common.h"
#include "str.h"
static int print_spec_name(git_str *out, const cli_opt_spec *spec)
diff --git a/src/cli/progress.c b/src/cli/progress.c
index ddfbafb..d975b09 100644
--- a/src/cli/progress.c
+++ b/src/cli/progress.c
@@ -242,7 +242,21 @@ static int fetch_receiving(
done ? ", done." : "");
}
-static int fetch_resolving(
+static int indexer_indexing(
+ cli_progress *progress,
+ const git_indexer_progress *stats)
+{
+ bool done = (stats->received_objects == stats->total_objects);
+
+ return progress_printf(progress, false,
+ "Indexing objects: %3d%% (%d/%d)%s\r",
+ percent(stats->received_objects, stats->total_objects),
+ stats->received_objects,
+ stats->total_objects,
+ done ? ", done." : "");
+}
+
+static int indexer_resolving(
cli_progress *progress,
const git_indexer_progress *stats)
{
@@ -283,7 +297,42 @@ int cli_progress_fetch_transfer(const git_indexer_progress *stats, void *payload
/* fall through */
case CLI_PROGRESS_RESOLVING:
- error = fetch_resolving(progress, stats);
+ error = indexer_resolving(progress, stats);
+ break;
+
+ default:
+ /* should not be reached */
+ GIT_ASSERT(!"unexpected progress state");
+ }
+
+ return error;
+}
+
+int cli_progress_indexer(
+ const git_indexer_progress *stats,
+ void *payload)
+{
+ cli_progress *progress = (cli_progress *)payload;
+ int error = 0;
+
+ switch (progress->action) {
+ case CLI_PROGRESS_NONE:
+ progress->action = CLI_PROGRESS_INDEXING;
+ /* fall through */
+
+ case CLI_PROGRESS_INDEXING:
+ if ((error = indexer_indexing(progress, stats)) < 0)
+ break;
+
+ if (stats->indexed_deltas == stats->total_deltas)
+ break;
+
+ progress_complete(progress);
+ progress->action = CLI_PROGRESS_RESOLVING;
+ /* fall through */
+
+ case CLI_PROGRESS_RESOLVING:
+ error = indexer_resolving(progress, stats);
break;
default:
diff --git a/src/cli/progress.h b/src/cli/progress.h
index 886fef8..f08d68f 100644
--- a/src/cli/progress.h
+++ b/src/cli/progress.h
@@ -22,6 +22,7 @@
typedef enum {
CLI_PROGRESS_NONE,
CLI_PROGRESS_RECEIVING,
+ CLI_PROGRESS_INDEXING,
CLI_PROGRESS_RESOLVING,
CLI_PROGRESS_CHECKING_OUT
} cli_progress_t;
@@ -75,6 +76,17 @@ extern int cli_progress_fetch_transfer(
void *payload);
/**
+ * Prints indexer progress to the console. Suitable for a
+ * `progress_cb` callback for `git_indexer_options`.
+ *
+ * @param stats The indexer stats
+ * @param payload A pointer to the cli_progress
+ */
+extern int cli_progress_indexer(
+ const git_indexer_progress *stats,
+ void *payload);
+
+/**
* Prints checkout progress to the console. Suitable for a
* `progress_cb` callback for `git_checkout_options`.
*
diff --git a/src/cli/unix/sighandler.c b/src/cli/unix/sighandler.c
index 6b4982d..05ac867 100644
--- a/src/cli/unix/sighandler.c
+++ b/src/cli/unix/sighandler.c
@@ -8,7 +8,8 @@
#include <stdint.h>
#include <signal.h>
#include "git2_util.h"
-#include "cli.h"
+#include "common.h"
+#include "sighandler.h"
static void (*interrupt_handler)(void) = NULL;
diff --git a/src/cli/win32/precompiled.h b/src/cli/win32/precompiled.h
index b0309b8..031370e 100644
--- a/src/cli/win32/precompiled.h
+++ b/src/cli/win32/precompiled.h
@@ -1,3 +1,3 @@
#include <git2.h>
-#include "cli.h"
+#include "common.h"
diff --git a/src/cli/win32/sighandler.c b/src/cli/win32/sighandler.c
index cc0b646..05a67fb 100644
--- a/src/cli/win32/sighandler.c
+++ b/src/cli/win32/sighandler.c
@@ -8,7 +8,7 @@
#include "git2_util.h"
#include <windows.h>
-#include "cli.h"
+#include "sighandler.h"
static void (*interrupt_handler)(void) = NULL;