Unverified Commit 1327dbcf by Edward Thomson Committed by GitHub

Merge pull request #6133 from libgit2/ethomson/cli_redux

CLI (redux)
parents eca9e1cf e427d0a1

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

# CMake build script for the libgit2 project
# libgit2: the cross-platform, linkable library implementation of git.
# See `README.md` for build instructions.
#
# This top-level CMakeLists.txt sets up configuration options and
# determines which subprojects to build.
cmake_minimum_required(VERSION 3.5.1)
......@@ -15,6 +18,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
# Optional subsystems
option(BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON)
option(BUILD_TESTS "Build Tests using the Clar suite" ON)
option(BUILD_CLI "Build the command-line interface" ON)
option(BUILD_EXAMPLES "Build library usage example apps" OFF)
option(BUILD_FUZZERS "Build the fuzz targets" OFF)
......
......@@ -159,10 +159,18 @@ fi
if [ -z "$SKIP_OFFLINE_TESTS" ]; then
echo ""
echo "##############################################################################"
echo "## Running (offline) tests"
echo "## Running core tests"
echo "##############################################################################"
echo ""
echo "Running libgit2 integration (offline) tests"
echo ""
run_test offline
echo ""
echo "Running utility tests"
echo ""
run_test util
fi
if [ -n "$RUN_INVASIVE_TESTS" ]; then
......@@ -186,7 +194,7 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
echo ""
echo "##############################################################################"
echo "## Running (online) tests"
echo "## Running networking (online) tests"
echo "##############################################################################"
export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository"
......@@ -198,9 +206,9 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
# Run the online tests that immutably change global state separately
# to avoid polluting the test environment.
echo ""
echo "##############################################################################"
echo "## Running (online_customcert) tests"
echo "##############################################################################"
echo "Running custom certificate (online_customcert) tests"
echo ""
run_test online_customcert
fi
......
function(ADD_CLAR_TEST project name)
if(NOT USE_LEAK_CHECKER STREQUAL "OFF")
add_test(${name} "${PROJECT_SOURCE_DIR}/script/${USE_LEAK_CHECKER}.sh" "${PROJECT_BINARY_DIR}/${project}" ${ARGN})
else()
add_test(${name} "${PROJECT_BINARY_DIR}/${project}" ${ARGN})
endif()
endfunction(ADD_CLAR_TEST)
......@@ -21,9 +21,6 @@ endif()
if(USE_SHA1 STREQUAL "CollisionDetection")
set(GIT_SHA1_COLLISIONDETECT 1)
add_definitions(-DSHA1DC_NO_STANDARD_INCLUDES=1)
add_definitions(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\")
add_definitions(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\")
elseif(USE_SHA1 STREQUAL "OpenSSL")
# OPENSSL_FOUND should already be set, we're checking USE_HTTPS
......
# examples: code usage examples of libgit2
file(GLOB SRC_EXAMPLES *.c *.h)
add_executable(lg2 ${SRC_EXAMPLES})
......@@ -10,7 +12,7 @@ target_include_directories(lg2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_
target_include_directories(lg2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
if(WIN32 OR ANDROID)
target_link_libraries(lg2 git2)
target_link_libraries(lg2 libgit2package)
else()
target_link_libraries(lg2 git2 pthread)
target_link_libraries(lg2 libgit2package pthread)
endif()
# fuzzers: libFuzzer and standalone fuzzing utilities
if(BUILD_FUZZERS AND NOT USE_STANDALONE_FUZZERS)
set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer-no-link")
add_c_flag(-fsanitize=fuzzer)
......
......@@ -130,7 +130,8 @@ GIT_EXTERN(const git_error *) git_error_last(void);
GIT_EXTERN(void) git_error_clear(void);
/**
* Set the error message string for this thread.
* Set the error message string for this thread, using `printf`-style
* formatting.
*
* This function is public so that custom ODB backends and the like can
* relay an error message through libgit2. Most regular users of libgit2
......@@ -143,7 +144,20 @@ GIT_EXTERN(void) git_error_clear(void);
*
* @param error_class One of the `git_error_t` enum above describing the
* general subsystem that is responsible for the error.
* @param string The formatted error message to keep
* @param fmt The `printf`-style format string; subsequent arguments must
* be the arguments for the format string.
*/
GIT_EXTERN(void) git_error_set(int error_class, const char *fmt, ...)
GIT_FORMAT_PRINTF(2, 3);
/**
* Set the error message string for this thread. This function is like
* `git_error_set` but takes a static string instead of a `printf`-style
* format.
*
* @param error_class One of the `git_error_t` enum above describing the
* general subsystem that is responsible for the error.
* @param string The error message to keep
* @return 0 on success or -1 on failure
*/
GIT_EXTERN(int) git_error_set_str(int error_class, const char *string);
......
# libgit2 sources
This is the source that makes up the core of libgit2 and its related
projects.
* `cli`
A git-compatible command-line interface that uses libgit2.
* `libgit2`
This is the libgit2 project, a cross-platform, linkable library
implementation of Git that you can use in your application.
* `util`
A shared utility library for these projects.
set(CLI_INCLUDES
"${libgit2_BINARY_DIR}/src"
"${libgit2_SOURCE_DIR}/src/util"
"${libgit2_SOURCE_DIR}/src/cli"
"${libgit2_SOURCE_DIR}/include")
if(WIN32 AND NOT CYGWIN)
file(GLOB CLI_SRC_OS win32/*.c)
list(SORT CLI_SRC_OS)
else()
file(GLOB CLI_SRC_OS unix/*.c)
list(SORT CLI_SRC_OS)
endif()
file(GLOB CLI_SRC_C *.c *.h)
list(SORT CLI_SRC_C)
#
# The CLI currently needs to be statically linked against libgit2 because
# the utility library uses libgit2's thread-local error buffers. TODO:
# remove this dependency and allow us to dynamically link against libgit2.
#
if(BUILD_CLI STREQUAL "dynamic")
set(CLI_LIBGIT2_LIBRARY libgit2package)
else()
set(CLI_LIBGIT2_OBJECTS $<TARGET_OBJECTS:libgit2>)
endif()
#
# Compile and link the CLI
#
add_executable(git2_cli ${CLI_SRC_C} ${CLI_SRC_OS} ${CLI_OBJECTS}
$<TARGET_OBJECTS:util>
${CLI_LIBGIT2_OBJECTS}
${LIBGIT2_DEPENDENCY_OBJECTS})
target_link_libraries(git2_cli ${CLI_LIBGIT2_LIBRARY} ${LIBGIT2_SYSTEM_LIBS})
set_target_properties(git2_cli PROPERTIES C_STANDARD 90)
set_target_properties(git2_cli PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
ide_split_sources(git2_cli)
target_include_directories(git2_cli PRIVATE ${CLI_INCLUDES})
if(MSVC_IDE)
# Precompiled headers
set_target_properties(git2_cli PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
endif()
install(TARGETS git2_cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
# cli
A git-compatible command-line interface that uses libgit2.
## Adding commands
1. Individual commands have a `main`-like top-level entrypoint. For example:
```c
int cmd_help(int argc, char **argv)
```
Although this is the same signature as `main`, commands are not built as
individual standalone executables, they'll be linked into the main cli.
(Though there may be an option for command executables to be built as
standalone executables in the future.)
2. Commands are prototyped in `cmd.h` and added to `main.c`'s list of
commands (`cli_cmds[]`). Commands should be specified with their name,
entrypoint and a brief description that can be printed in `git help`.
This is done because commands are linked into the main cli.
3. Commands should accept a `--help` option that displays their help
information. This will be shown when a user runs `<command> --help` and
when a user runs `help <command>`.
/*
* 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"
#endif /* CLI_cli_h__ */
/*
* 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 "cli.h"
#include "cmd.h"
const cli_cmd_spec *cli_cmd_spec_byname(const char *name)
{
const cli_cmd_spec *cmd;
for (cmd = cli_cmds; cmd->name; cmd++) {
if (!strcmp(cmd->name, name))
return cmd;
}
return NULL;
}
/*
* 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_cmd_h__
#define CLI_cmd_h__
/* Command definitions */
typedef struct {
const char *name;
int (*fn)(int argc, char **argv);
const char *desc;
} cli_cmd_spec;
/* Options that are common to all commands (eg --help, --git-dir) */
extern const cli_opt_spec cli_common_opts[];
/* All the commands supported by the CLI */
extern const cli_cmd_spec cli_cmds[];
/* Find a command by name */
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_hash_object(int argc, char **argv);
extern int cmd_help(int argc, char **argv);
#endif /* CLI_cmd_h__ */
/*
* 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"
#define COMMAND_NAME "cat-file"
typedef enum {
DISPLAY_CONTENT = 0,
DISPLAY_EXISTS,
DISPLAY_PRETTY,
DISPLAY_SIZE,
DISPLAY_TYPE
} display_t;
static int show_help;
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_OPT_TYPE_SWITCH, NULL, 't', &display, DISPLAY_TYPE,
CLI_OPT_USAGE_REQUIRED, NULL, "display the type of the object" },
{ CLI_OPT_TYPE_SWITCH, NULL, 's', &display, DISPLAY_SIZE,
CLI_OPT_USAGE_CHOICE, NULL, "display the size of the object" },
{ CLI_OPT_TYPE_SWITCH, NULL, 'e', &display, DISPLAY_EXISTS,
CLI_OPT_USAGE_CHOICE, NULL, "displays nothing unless the object is corrupt" },
{ CLI_OPT_TYPE_SWITCH, NULL, 'p', &display, DISPLAY_PRETTY,
CLI_OPT_USAGE_CHOICE, NULL, "pretty-print the object" },
{ CLI_OPT_TYPE_ARG, "type", 0, &type_name, 0,
CLI_OPT_USAGE_CHOICE, "type", "the type of object to display" },
{ CLI_OPT_TYPE_ARG, "object", 0, &object_spec, 0,
CLI_OPT_USAGE_REQUIRED, "object", "the object to display" },
{ 0 },
};
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
printf("\n");
printf("Display the content for the given object in the repository.\n");
printf("\n");
printf("Options:\n");
cli_opt_help_fprint(stdout, opts);
}
static int print_odb(git_object *object, display_t display)
{
git_odb *odb = NULL;
git_odb_object *odb_object = NULL;
const unsigned char *content;
git_object_size_t size;
int ret = 0;
/*
* Our parsed blobs retain the raw content; all other objects are
* parsed into a working representation. To get the raw content,
* we need to do an ODB lookup. (Thankfully, this should be cached
* in-memory from our last call.)
*/
if (git_object_type(object) == GIT_OBJECT_BLOB) {
content = git_blob_rawcontent((git_blob *)object);
size = git_blob_rawsize((git_blob *)object);
} else {
if (git_repository_odb(&odb, git_object_owner(object)) < 0 ||
git_odb_read(&odb_object, odb, git_object_id(object)) < 0) {
ret = cli_error_git();
goto done;
}
content = git_odb_object_data(odb_object);
size = git_odb_object_size(odb_object);
}
switch (display) {
case DISPLAY_SIZE:
if (printf("%" PRIu64 "\n", size) < 0)
ret = cli_error_os();
break;
case DISPLAY_CONTENT:
if (p_write(fileno(stdout), content, (size_t)size) < 0)
ret = cli_error_os();
break;
default:
GIT_ASSERT(0);
}
done:
git_odb_object_free(odb_object);
git_odb_free(odb);
return ret;
}
static int print_type(git_object *object)
{
if (printf("%s\n", git_object_type2string(git_object_type(object))) < 0)
return cli_error_os();
return 0;
}
static int print_pretty(git_object *object)
{
const git_tree_entry *entry;
size_t i, count;
/*
* Only trees are stored in an unreadable format and benefit from
* pretty-printing.
*/
if (git_object_type(object) != GIT_OBJECT_TREE)
return print_odb(object, DISPLAY_CONTENT);
for (i = 0, count = git_tree_entrycount((git_tree *)object); i < count; i++) {
entry = git_tree_entry_byindex((git_tree *)object, i);
if (printf("%06o %s %s\t%s\n",
git_tree_entry_filemode_raw(entry),
git_object_type2string(git_tree_entry_type(entry)),
git_oid_tostr_s(git_tree_entry_id(entry)),
git_tree_entry_name(entry)) < 0)
return cli_error_os();
}
return 0;
}
int cmd_cat_file(int argc, char **argv)
{
git_repository *repo = NULL;
git_object *object = NULL;
git_object_t type;
cli_opt invalid_opt;
int giterr, 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 (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0)
return cli_error_git();
if ((giterr = git_revparse_single(&object, repo, object_spec)) < 0) {
if (display == DISPLAY_EXISTS && giterr == GIT_ENOTFOUND)
ret = 1;
else
ret = cli_error_git();
goto done;
}
if (type_name) {
git_object *peeled;
if ((type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID) {
ret = cli_error_usage("invalid object type '%s'", type_name);
goto done;
}
if (git_object_peel(&peeled, object, type) < 0) {
ret = cli_error_git();
goto done;
}
git_object_free(object);
object = peeled;
}
switch (display) {
case DISPLAY_EXISTS:
ret = 0;
break;
case DISPLAY_TYPE:
ret = print_type(object);
break;
case DISPLAY_PRETTY:
ret = print_pretty(object);
break;
default:
ret = print_odb(object, display);
break;
}
done:
git_object_free(object);
git_repository_free(repo);
return ret;
}
/*
* 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 "futils.h"
#define COMMAND_NAME "hash-object"
static int show_help;
static char *type_name;
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_OPT_TYPE_VALUE, NULL, 't', &type_name, 0,
CLI_OPT_USAGE_DEFAULT, "type", "the type of object to hash (default: \"blob\")" },
{ CLI_OPT_TYPE_SWITCH, NULL, 'w', &write_object, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "write the object to the object database" },
{ CLI_OPT_TYPE_SWITCH, "literally", 0, &literally, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "do not validate the object contents" },
{ CLI_OPT_TYPE_SWITCH, "stdin", 0, &read_stdin, 1,
CLI_OPT_USAGE_REQUIRED, NULL, "read content from stdin" },
{ CLI_OPT_TYPE_ARGS, "file", 0, &filenames, 0,
CLI_OPT_USAGE_CHOICE, "file", "the file (or files) to read and hash" },
{ 0 },
};
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
printf("\n");
printf("Compute the object ID for a given file and optionally write that file\nto the object database.\n");
printf("\n");
printf("Options:\n");
cli_opt_help_fprint(stdout, opts);
}
static int hash_buf(git_odb *odb, git_str *buf, git_object_t type)
{
git_oid oid;
if (!literally) {
int valid = 0;
if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, type) < 0 || !valid)
return cli_error_git();
}
if (write_object) {
if (git_odb_write(&oid, odb, buf->ptr, buf->size, type) < 0)
return cli_error_git();
} else {
if (git_odb_hash(&oid, buf->ptr, buf->size, type) < 0)
return cli_error_git();
}
if (printf("%s\n", git_oid_tostr_s(&oid)) < 0)
return cli_error_os();
return 0;
}
int cmd_hash_object(int argc, char **argv)
{
git_repository *repo = NULL;
git_odb *odb = NULL;
git_str buf = GIT_STR_INIT;
cli_opt invalid_opt;
git_object_t type = GIT_OBJECT_BLOB;
char **filename;
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 (type_name && (type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID)
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 ||
git_repository_odb(&odb, repo) < 0)) {
ret = cli_error_git();
goto done;
}
/*
* TODO: we're reading blobs, we shouldn't pull them all into main
* memory, we should just stream them into the odb instead.
* (Or create a `git_odb_writefile` API.)
*/
if (read_stdin) {
if (git_futils_readbuffer_fd_full(&buf, fileno(stdin)) < 0) {
ret = cli_error_git();
goto done;
}
if ((ret = hash_buf(odb, &buf, type)) != 0)
goto done;
} else {
for (filename = filenames; *filename; filename++) {
if (git_futils_readbuffer(&buf, *filename) < 0) {
ret = cli_error_git();
goto done;
}
if ((ret = hash_buf(odb, &buf, type)) != 0)
goto done;
}
}
done:
git_str_dispose(&buf);
git_odb_free(odb);
git_repository_free(repo);
return ret;
}
/*
* 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 <stdio.h>
#include <git2.h>
#include "cli.h"
#include "cmd.h"
#define COMMAND_NAME "help"
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_OPT_TYPE_ARG, "command", 0, &command, 0,
CLI_OPT_USAGE_DEFAULT, "command", "the command to show help for" },
{ 0 },
};
static int print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
printf("\n");
printf("Display help information about %s. If a command is specified, help\n", PROGRAM_NAME);
printf("about that command will be shown. Otherwise, general information about\n");
printf("%s will be shown, including the commands available.\n", PROGRAM_NAME);
return 0;
}
static int print_commands(void)
{
const cli_cmd_spec *cmd;
cli_opt_usage_fprint(stdout, PROGRAM_NAME, NULL, cli_common_opts);
printf("\n");
printf("These are the %s commands available:\n\n", PROGRAM_NAME);
for (cmd = cli_cmds; cmd->name; cmd++)
printf(" %-11s %s\n", cmd->name, cmd->desc);
printf("\nSee '%s help <command>' for more information on a specific command.\n", PROGRAM_NAME);
return 0;
}
int cmd_help(int argc, char **argv)
{
char *fake_args[2];
const cli_cmd_spec *cmd;
cli_opt invalid_opt;
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);
/* Show the meta-help */
if (show_help)
return print_help();
/* We were not asked to show help for a specific command. */
if (!command)
return print_commands();
/*
* If we were asked for help for a command (eg, `help <command>`),
* delegate back to that command's `--help` option. This lets
* commands own their help. Emulate the command-line arguments
* that would invoke `<command> --help` and invoke that command.
*/
fake_args[0] = command;
fake_args[1] = "--help";
if ((cmd = cli_cmd_spec_byname(command)) == NULL)
return cli_error("'%s' is not a %s command. See '%s help'.",
command, PROGRAM_NAME, PROGRAM_NAME);
return cmd->fn(2, fake_args);
}
/*
* 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_error_h__
#define CLI_error_h__
#include "cli.h"
#include <stdio.h>
#define CLI_EXIT_OK 0
#define CLI_EXIT_ERROR 1
#define CLI_EXIT_OS 128
#define CLI_EXIT_GIT 128
#define CLI_EXIT_USAGE 129
#define cli_error__print(fmt) do { \
va_list ap; \
va_start(ap, fmt); \
fprintf(stderr, "%s: ", PROGRAM_NAME); \
vfprintf(stderr, fmt, ap); \
fprintf(stderr, "\n"); \
va_end(ap); \
} while(0)
GIT_INLINE(int) cli_error(const char *fmt, ...)
{
cli_error__print(fmt);
return CLI_EXIT_ERROR;
}
GIT_INLINE(int) cli_error_usage(const char *fmt, ...)
{
cli_error__print(fmt);
return CLI_EXIT_USAGE;
}
GIT_INLINE(int) cli_error_git(void)
{
const git_error *err = git_error_last();
fprintf(stderr, "%s: %s\n", PROGRAM_NAME,
err ? err->message : "unknown error");
return CLI_EXIT_GIT;
}
#define cli_error_os() (perror(PROGRAM_NAME), CLI_EXIT_OS)
#endif /* CLI_error_h__ */
/*
* 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 <stdio.h>
#include <git2.h>
#include "cli.h"
#include "cmd.h"
static int show_help = 0;
static int show_version = 0;
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, "version", 0, &show_version, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display the version" },
{ CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
CLI_OPT_USAGE_REQUIRED, "command", "the command to run" },
{ CLI_OPT_TYPE_ARGS, "args", 0, &args, 0,
CLI_OPT_USAGE_DEFAULT, "args", "arguments for the command" },
{ 0 }
};
const cli_cmd_spec cli_cmds[] = {
{ "cat-file", cmd_cat_file, "Display an object in the repository" },
{ "hash-object", cmd_hash_object, "Hash a raw object and product its object ID" },
{ "help", cmd_help, "Display help information" },
{ NULL }
};
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) {
cli_error("failed to initialize libgit2");
exit(CLI_EXIT_GIT);
}
cli_opt_parser_init(&optparser, cli_common_opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU);
/* Parse the top-level (common) options and command information */
while (cli_opt_parser_next(&opt, &optparser)) {
if (!opt.spec) {
cli_opt_status_fprint(stderr, PROGRAM_NAME, &opt);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, cli_common_opts);
ret = CLI_EXIT_USAGE;
goto done;
}
/*
* When we see a command, stop parsing and capture the
* remaining arguments as args for the command itself.
*/
if (command) {
args = &argv[optparser.idx];
args_len = (int)(argc - optparser.idx);
break;
}
}
if (show_version) {
printf("%s version %s\n", PROGRAM_NAME, LIBGIT2_VERSION);
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 ((cmd = cli_cmd_spec_byname(command)) == NULL) {
ret = cli_error("'%s' is not a %s command. See '%s help'.",
command, PROGRAM_NAME, PROGRAM_NAME);
goto done;
}
ret = cmd->fn(args_len, args);
done:
git_libgit2_shutdown();
return ret;
}
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
/*
* 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 "cli.h"
#include "str.h"
static int print_spec_name(git_str *out, const cli_opt_spec *spec)
{
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL) &&
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
return git_str_printf(out, "-%c <%s>", spec->alias, spec->value_name);
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
return git_str_printf(out, "-%c [<%s>]", spec->alias, spec->value_name);
if (spec->type == CLI_OPT_TYPE_VALUE &&
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
return git_str_printf(out, "--%s[=<%s>]", spec->name, spec->value_name);
if (spec->type == CLI_OPT_TYPE_VALUE)
return git_str_printf(out, "--%s=<%s>", spec->name, spec->value_name);
if (spec->type == CLI_OPT_TYPE_ARG)
return git_str_printf(out, "<%s>", spec->value_name);
if (spec->type == CLI_OPT_TYPE_ARGS)
return git_str_printf(out, "<%s>...", spec->value_name);
if (spec->type == CLI_OPT_TYPE_LITERAL)
return git_str_printf(out, "--");
if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
return git_str_printf(out, "-%c", spec->alias);
if (spec->name)
return git_str_printf(out, "--%s", spec->name);
GIT_ASSERT(0);
}
/*
* This is similar to adopt's function, but modified to understand
* that we have a command ("git") and a "subcommand" ("checkout").
* It also understands a terminal's line length and wrap appropriately,
* using a `git_str` for storage.
*/
int cli_opt_usage_fprint(
FILE *file,
const char *command,
const char *subcommand,
const cli_opt_spec specs[])
{
git_str usage = GIT_BUF_INIT, opt = GIT_BUF_INIT;
const cli_opt_spec *spec;
size_t i, prefixlen, linelen;
bool choice = false, next_choice = false, optional = false;
int error;
/* TODO: query actual console width. */
int console_width = 80;
if ((error = git_str_printf(&usage, "usage: %s", command)) < 0)
goto done;
if (subcommand &&
(error = git_str_printf(&usage, " %s", subcommand)) < 0)
goto done;
linelen = git_str_len(&usage);
prefixlen = linelen + 1;
for (spec = specs; spec->type; ++spec) {
if (!choice)
optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
next_choice = !!((spec + 1)->usage & CLI_OPT_USAGE_CHOICE);
if (spec->usage & CLI_OPT_USAGE_HIDDEN)
continue;
if (choice)
git_str_putc(&opt, '|');
else
git_str_clear(&opt);
if (optional && !choice)
git_str_putc(&opt, '[');
if (!optional && !choice && next_choice)
git_str_putc(&opt, '(');
if ((error = print_spec_name(&opt, spec)) < 0)
goto done;
if (!optional && choice && !next_choice)
git_str_putc(&opt, ')');
else if (optional && !next_choice)
git_str_putc(&opt, ']');
if ((choice = next_choice))
continue;
if (git_str_oom(&opt)) {
error = -1;
goto done;
}
if (linelen > prefixlen &&
console_width > 0 &&
linelen + git_str_len(&opt) + 1 > (size_t)console_width) {
git_str_putc(&usage, '\n');
for (i = 0; i < prefixlen; i++)
git_str_putc(&usage, ' ');
linelen = prefixlen;
} else {
git_str_putc(&usage, ' ');
linelen += git_str_len(&opt) + 1;
}
git_str_puts(&usage, git_str_cstr(&opt));
if (git_str_oom(&usage)) {
error = -1;
goto done;
}
}
error = fprintf(file, "%s\n", git_str_cstr(&usage));
done:
error = (error < 0) ? -1 : 0;
git_str_dispose(&usage);
git_str_dispose(&opt);
return error;
}
int cli_opt_usage_error(
const char *subcommand,
const cli_opt_spec specs[],
const cli_opt *invalid_opt)
{
cli_opt_status_fprint(stderr, PROGRAM_NAME, invalid_opt);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, subcommand, specs);
return CLI_EXIT_USAGE;
}
int cli_opt_help_fprint(
FILE *file,
const cli_opt_spec specs[])
{
git_str help = GIT_BUF_INIT;
const cli_opt_spec *spec;
int error;
/* Display required arguments first */
for (spec = specs; spec->type; ++spec) {
if (! (spec->usage & CLI_OPT_USAGE_REQUIRED) ||
(spec->usage & CLI_OPT_USAGE_HIDDEN))
continue;
git_str_printf(&help, " ");
if ((error = print_spec_name(&help, spec)) < 0)
goto done;
git_str_printf(&help, ": %s\n", spec->help);
}
/* Display the remaining arguments */
for (spec = specs; spec->type; ++spec) {
if ((spec->usage & CLI_OPT_USAGE_REQUIRED) ||
(spec->usage & CLI_OPT_USAGE_HIDDEN))
continue;
git_str_printf(&help, " ");
if ((error = print_spec_name(&help, spec)) < 0)
goto done;
git_str_printf(&help, ": %s\n", spec->help);
}
if (git_str_oom(&help) ||
p_write(fileno(file), help.ptr, help.size) < 0)
error = -1;
done:
error = (error < 0) ? -1 : 0;
git_str_dispose(&help);
return error;
}
/*
* 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_opt_usage_h__
#define CLI_opt_usage_h__
/**
* Prints usage information to the given file handle.
*
* @param file The file to print information to
* @param command The name of the command to use when printing
* @param subcommand The name of the subcommand (eg "checkout") to use when printing, or NULL to skip
* @param specs The specifications allowed by the command
* @return 0 on success, -1 on failure
*/
int cli_opt_usage_fprint(
FILE *file,
const char *command,
const char *subcommand,
const cli_opt_spec specs[]);
int cli_opt_usage_error(
const char *subcommand,
const cli_opt_spec specs[],
const cli_opt *invalid_opt);
int cli_opt_help_fprint(
FILE *file,
const cli_opt_spec specs[]);
#endif /* CLI_opt_usage_h__ */
#include "precompiled.h"
#include <git2.h>
#include "cli.h"
# libgit2: the shared library: this CMakeLists.txt compiles the core
# git library functionality.
add_library(libgit2 OBJECT)
set_target_properties(libgit2 PROPERTIES C_STANDARD 90)
set_target_properties(libgit2 PROPERTIES C_EXTENSIONS OFF)
include(PkgBuildConfig)
set(LIBGIT2_INCLUDES
"${PROJECT_BINARY_DIR}/src"
"${PROJECT_SOURCE_DIR}/src/libgit2"
"${PROJECT_SOURCE_DIR}/src/util"
"${PROJECT_SOURCE_DIR}/include")
if(WIN32 AND EMBED_SSH_PATH)
file(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
list(SORT SRC_SSH)
target_sources(libgit2 PRIVATE ${SRC_SSH})
list(APPEND LIBGIT2_SYSTEM_INCLUDES "${EMBED_SSH_PATH}/include")
file(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"")
set(GIT_SSH 1)
endif()
# Collect sourcefiles
file(GLOB SRC_H
"${PROJECT_SOURCE_DIR}/include/git2.h"
"${PROJECT_SOURCE_DIR}/include/git2/*.h"
"${PROJECT_SOURCE_DIR}/include/git2/sys/*.h")
list(SORT SRC_H)
target_sources(libgit2 PRIVATE ${SRC_H})
file(GLOB SRC_GIT2 *.c *.h
streams/*.c streams/*.h
transports/*.c transports/*.h
xdiff/*.c xdiff/*.h)
list(SORT SRC_GIT2)
target_sources(libgit2 PRIVATE ${SRC_GIT2})
if(WIN32 AND NOT CYGWIN)
# Add resource information on Windows
set(SRC_RC "git2.rc")
endif()
if(APPLE)
# The old Secure Transport API has been deprecated in macOS 10.15.
set_source_files_properties(streams/stransport.c PROPERTIES COMPILE_FLAGS -Wno-deprecated)
endif()
# the xdiff dependency is not (yet) warning-free, disable warnings
# as errors for the xdiff sources until we've sorted them out
if(MSVC)
set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xmerge.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS -WX-)
set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS -WX-)
else()
set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
endif()
ide_split_sources(libgit2)
list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:libgit2> ${LIBGIT2_DEPENDENCY_OBJECTS})
target_include_directories(libgit2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_include_directories(libgit2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
#
# Compile and link libgit2
#
add_library(libgit2package ${SRC_RC} ${LIBGIT2_OBJECTS})
target_link_libraries(libgit2package ${LIBGIT2_SYSTEM_LIBS})
set_target_properties(libgit2package PROPERTIES C_STANDARD 90)
set_target_properties(libgit2package PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(libgit2package PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(libgit2package PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
# Win64+MSVC+static libs = linker error
if(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
set_target_properties(libgit2package PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
endif()
ide_split_sources(libgit2package)
if(SONAME)
set_target_properties(libgit2package PROPERTIES VERSION ${libgit2_VERSION})
set_target_properties(libgit2package PROPERTIES SOVERSION "${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}")
if(LIBGIT2_FILENAME)
target_compile_definitions(libgit2package PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
set_target_properties(libgit2package PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
elseif(DEFINED LIBGIT2_PREFIX)
set_target_properties(libgit2package PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
endif()
endif()
pkg_build_config(NAME libgit2
VERSION ${libgit2_VERSION}
DESCRIPTION "The git library, take 2"
LIBS_SELF git2
PRIVATE_LIBS ${LIBGIT2_PC_LIBS}
REQUIRES ${LIBGIT2_PC_REQUIRES})
if(MSVC_IDE)
# Precompiled headers
set_target_properties(libgit2package PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
endif()
# Install
install(TARGETS libgit2package
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/git2 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${PROJECT_SOURCE_DIR}/include/git2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
......@@ -101,7 +101,7 @@ static int write_file_stream(
git_oid *id, git_odb *odb, const char *path, git_object_size_t file_size)
{
int fd, error;
char buffer[FILEIO_BUFSIZE];
char buffer[GIT_BUFSIZE_FILEIO];
git_odb_stream *stream = NULL;
ssize_t read_len = -1;
git_object_size_t written = 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 INCLUDE_common_h__
#define INCLUDE_common_h__
#include "git2_util.h"
#include "errors.h"
/*
* Include the declarations for deprecated functions; this ensures
* that they're decorated with the proper extern/visibility attributes.
*/
#include "git2/deprecated.h"
#include "posix.h"
/**
* Initialize a structure with a version.
*/
GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version)
{
memset(structure, 0, len);
*((int*)structure) = version;
}
#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V)
#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \
TYPE _tmpl = TPL; \
GIT_ERROR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \
memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0)
/**
* Check a versioned structure for validity
*/
GIT_INLINE(int) git_error__check_version(const void *structure, unsigned int expected_max, const char *name)
{
unsigned int actual;
if (!structure)
return 0;
actual = *(const unsigned int*)structure;
if (actual > 0 && actual <= expected_max)
return 0;
git_error_set(GIT_ERROR_INVALID, "invalid version %d on %s", actual, name);
return -1;
}
#define GIT_ERROR_CHECK_VERSION(S,V,N) if (git_error__check_version(S,V,N) < 0) return -1
#endif
......@@ -11,6 +11,7 @@
#include "diff.h"
#include "diff_driver.h"
#include "patch_generate.h"
#include "utf8.h"
static int git_xdiff_scan_int(const char **str, int *value)
{
......
......@@ -11,9 +11,8 @@
#include "common.h"
/*
* Set the error message for this thread, formatting as needed.
* `vprintf`-style formatting for the error message for this thread.
*/
void git_error_set(int error_class, const char *fmt, ...) GIT_FORMAT_PRINTF(2, 3);
void git_error_vset(int error_class, const char *fmt, va_list ap);
/**
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment