diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-21 05:03:06 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-21 05:03:06 +0000 |
commit | 4ffc4372dff6595a86120ec0bd311c4cb5fb8ed6 (patch) | |
tree | 582224c3f88627b5797d1b57ccaf158c7d36118e /src/cli | |
parent | Releasing progress-linux version 1.7.2+ds-1~progress7.99u1. (diff) | |
download | libgit2-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.txt | 3 | ||||
-rw-r--r-- | src/cli/cli.h | 20 | ||||
-rw-r--r-- | src/cli/cmd.c | 2 | ||||
-rw-r--r-- | src/cli/cmd.h | 2 | ||||
-rw-r--r-- | src/cli/cmd_cat_file.c | 9 | ||||
-rw-r--r-- | src/cli/cmd_clone.c | 6 | ||||
-rw-r--r-- | src/cli/cmd_config.c | 242 | ||||
-rw-r--r-- | src/cli/cmd_hash_object.c | 9 | ||||
-rw-r--r-- | src/cli/cmd_help.c | 6 | ||||
-rw-r--r-- | src/cli/cmd_index_pack.c | 114 | ||||
-rw-r--r-- | src/cli/common.c | 126 | ||||
-rw-r--r-- | src/cli/common.h | 66 | ||||
-rw-r--r-- | src/cli/error.h | 2 | ||||
-rw-r--r-- | src/cli/main.c | 58 | ||||
-rw-r--r-- | src/cli/opt.c | 32 | ||||
-rw-r--r-- | src/cli/opt.h | 24 | ||||
-rw-r--r-- | src/cli/opt_usage.c | 2 | ||||
-rw-r--r-- | src/cli/progress.c | 53 | ||||
-rw-r--r-- | src/cli/progress.h | 12 | ||||
-rw-r--r-- | src/cli/unix/sighandler.c | 3 | ||||
-rw-r--r-- | src/cli/win32/precompiled.h | 2 | ||||
-rw-r--r-- | src/cli/win32/sighandler.c | 2 |
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; |