mirror of
https://git.proxmox.com/git/libgit2
synced 2026-01-02 18:10:10 +00:00
Merge pull request #1916 from libgit2/simplify-examples
Fix examples to make the important stuff more obvious
This commit is contained in:
commit
567649f2ad
@ -9,6 +9,8 @@ ENDIF()
|
||||
FILE(GLOB SRC_EXAMPLE_APPS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c)
|
||||
FOREACH(src_app ${SRC_EXAMPLE_APPS})
|
||||
STRING(REPLACE ".c" "" app_name ${src_app})
|
||||
ADD_EXECUTABLE(${app_name} ${src_app})
|
||||
TARGET_LINK_LIBRARIES(${app_name} git2)
|
||||
IF(NOT ${app_name} STREQUAL "common")
|
||||
ADD_EXECUTABLE(${app_name} ${src_app} "common.c")
|
||||
TARGET_LINK_LIBRARIES(${app_name} git2)
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
|
||||
@ -8,7 +8,7 @@ APPS = general showindex diff rev-list cat-file status log rev-parse init
|
||||
all: $(APPS)
|
||||
|
||||
% : %.c
|
||||
$(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
|
||||
$(CC) -o $@ common.c $(CFLAGS) $< $(LFLAGS)
|
||||
|
||||
clean:
|
||||
$(RM) $(APPS)
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
libgit2 examples
|
||||
================
|
||||
|
||||
These examples are meant as thin, easy-to-read snippets for Docurium
|
||||
(https://github.com/github/docurium) rather than full-blown
|
||||
implementations of Git commands. They are not vetted as carefully
|
||||
for bugs, error handling, or cross-platform compatibility as the
|
||||
rest of the code in libgit2, so copy with some caution.
|
||||
These examples are a mixture of basic emulation of core Git command line
|
||||
functions and simple snippets demonstrating libgit2 API usage (for use
|
||||
with Docurium). As a whole, they are not vetted carefully for bugs, error
|
||||
handling, and cross-platform compatibility in the same manner as the rest
|
||||
of the code in libgit2, so copy with caution.
|
||||
|
||||
For HTML versions, check "Examples" at http://libgit2.github.com/libgit2
|
||||
That being said, you are welcome to copy code from these examples as
|
||||
desired when using libgit2.
|
||||
|
||||
For annotated HTML versions, see the "Examples" section of:
|
||||
|
||||
http://libgit2.github.com/libgit2
|
||||
|
||||
such as:
|
||||
|
||||
http://libgit2.github.com/libgit2/ex/HEAD/general.html
|
||||
|
||||
129
examples/add.c
129
examples/add.c
@ -1,6 +1,11 @@
|
||||
#include <git2.h>
|
||||
#include <stdio.h>
|
||||
#include <string.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 "common.h"
|
||||
#include <assert.h>
|
||||
|
||||
enum print_options {
|
||||
@ -14,19 +19,49 @@ struct print_payload {
|
||||
git_repository *repo;
|
||||
};
|
||||
|
||||
void init_array(git_strarray *array, int argc, char **argv)
|
||||
/* Forward declarations for helpers */
|
||||
static void parse_opts(int *options, int *count, int argc, char *argv[]);
|
||||
void init_array(git_strarray *array, int argc, char **argv);
|
||||
int print_matched_cb(const char *path, const char *matched_pathspec, void *payload);
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
unsigned int i;
|
||||
git_index_matched_path_cb matched_cb = NULL;
|
||||
git_repository *repo = NULL;
|
||||
git_index *index;
|
||||
git_strarray array = {0};
|
||||
int options = 0, count = 0;
|
||||
struct print_payload payload = {0};
|
||||
|
||||
array->count = argc;
|
||||
array->strings = malloc(sizeof(char*) * array->count);
|
||||
assert(array->strings!=NULL);
|
||||
git_threads_init();
|
||||
|
||||
for(i=0; i<array->count; i++) {
|
||||
array->strings[i]=argv[i];
|
||||
parse_opts(&options, &count, argc, argv);
|
||||
|
||||
init_array(&array, argc-count, argv+count);
|
||||
|
||||
check_lg2(git_repository_open(&repo, "."), "No git repository", NULL);
|
||||
check_lg2(git_repository_index(&index, repo), "Could not open repository index", NULL);
|
||||
|
||||
if (options&VERBOSE || options&SKIP) {
|
||||
matched_cb = &print_matched_cb;
|
||||
}
|
||||
|
||||
return;
|
||||
payload.options = options;
|
||||
payload.repo = repo;
|
||||
|
||||
if (options&UPDATE) {
|
||||
git_index_update_all(index, &array, matched_cb, &payload);
|
||||
} else {
|
||||
git_index_add_all(index, &array, 0, matched_cb, &payload);
|
||||
}
|
||||
|
||||
git_index_write(index);
|
||||
git_index_free(index);
|
||||
git_repository_free(repo);
|
||||
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int print_matched_cb(const char *path, const char *matched_pathspec, void *payload)
|
||||
@ -55,36 +90,46 @@ int print_matched_cb(const char *path, const char *matched_pathspec, void *paylo
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_array(git_strarray *array, int argc, char **argv)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
array->count = argc;
|
||||
array->strings = malloc(sizeof(char*) * array->count);
|
||||
assert(array->strings!=NULL);
|
||||
|
||||
for(i=0; i<array->count; i++) {
|
||||
array->strings[i]=argv[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void print_usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: add [options] [--] file-spec [file-spec] [...]\n\n");
|
||||
fprintf(stderr, "\t-n, --dry-run dry run\n");
|
||||
fprintf(stderr, "\t-v, --verbose be verbose\n");
|
||||
fprintf(stderr, "\t-u, --update update tracked files\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, char** argv)
|
||||
static void parse_opts(int *options, int *count, int argc, char *argv[])
|
||||
{
|
||||
git_index_matched_path_cb matched_cb = NULL;
|
||||
git_repository *repo = NULL;
|
||||
git_index *index;
|
||||
git_strarray array = {0};
|
||||
int i, options = 0;
|
||||
struct print_payload payload = {0};
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (argv[i][0] != '-') {
|
||||
break;
|
||||
}
|
||||
else if(!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-v")) {
|
||||
options |= VERBOSE;
|
||||
*options |= VERBOSE;
|
||||
}
|
||||
else if(!strcmp(argv[i], "--dry-run") || !strcmp(argv[i], "-n")) {
|
||||
options |= SKIP;
|
||||
*options |= SKIP;
|
||||
}
|
||||
else if(!strcmp(argv[i], "--update") || !strcmp(argv[i], "-u")) {
|
||||
options |= UPDATE;
|
||||
*options |= UPDATE;
|
||||
}
|
||||
else if(!strcmp(argv[i], "-h")) {
|
||||
print_usage();
|
||||
@ -97,47 +142,11 @@ int main (int argc, char** argv)
|
||||
else {
|
||||
fprintf(stderr, "Unsupported option %s.\n", argv[i]);
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc<=i) {
|
||||
if (argc<=i)
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
git_threads_init();
|
||||
|
||||
init_array(&array, argc-i, argv+i);
|
||||
|
||||
if (git_repository_open(&repo, ".") < 0) {
|
||||
fprintf(stderr, "No git repository\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (git_repository_index(&index, repo) < 0) {
|
||||
fprintf(stderr, "Could not open repository index\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (options&VERBOSE || options&SKIP) {
|
||||
matched_cb = &print_matched_cb;
|
||||
}
|
||||
|
||||
payload.options = options;
|
||||
payload.repo = repo;
|
||||
|
||||
if (options&UPDATE) {
|
||||
git_index_update_all(index, &array, matched_cb, &payload);
|
||||
} else {
|
||||
git_index_add_all(index, &array, 0, matched_cb, &payload);
|
||||
}
|
||||
|
||||
git_index_write(index);
|
||||
git_index_free(index);
|
||||
git_repository_free(repo);
|
||||
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
*count = i;
|
||||
}
|
||||
|
||||
@ -1,37 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <git2.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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.
|
||||
*/
|
||||
|
||||
static git_repository *g_repo;
|
||||
|
||||
static void check(int error, const char *message)
|
||||
{
|
||||
if (error) {
|
||||
fprintf(stderr, "%s (%d)\n", message, error);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(const char *message, const char *arg)
|
||||
{
|
||||
if (message && arg)
|
||||
fprintf(stderr, "%s: %s\n", message, arg);
|
||||
else if (message)
|
||||
fprintf(stderr, "%s\n", message);
|
||||
fprintf(stderr, "usage: cat-file (-t | -s | -e | -p) [<options>] <object>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int check_str_param(
|
||||
const char *arg, const char *pattern, const char **val)
|
||||
{
|
||||
size_t len = strlen(pattern);
|
||||
if (strncmp(arg, pattern, len))
|
||||
return 0;
|
||||
*val = (const char *)(arg + len);
|
||||
return 1;
|
||||
}
|
||||
#include "common.h"
|
||||
|
||||
static void print_signature(const char *header, const git_signature *sig)
|
||||
{
|
||||
@ -57,12 +31,14 @@ static void print_signature(const char *header, const git_signature *sig)
|
||||
sign, hours, minutes);
|
||||
}
|
||||
|
||||
/** Printing out a blob is simple, get the contents and print */
|
||||
static void show_blob(const git_blob *blob)
|
||||
{
|
||||
/* ? Does this need crlf filtering? */
|
||||
fwrite(git_blob_rawcontent(blob), git_blob_rawsize(blob), 1, stdout);
|
||||
}
|
||||
|
||||
/** Show each entry with its type, id and attributes */
|
||||
static void show_tree(const git_tree *tree)
|
||||
{
|
||||
size_t i, max_i = (int)git_tree_entrycount(tree);
|
||||
@ -81,6 +57,9 @@ static void show_tree(const git_tree *tree)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits and tags have a few interesting fields in their header.
|
||||
*/
|
||||
static void show_commit(const git_commit *commit)
|
||||
{
|
||||
unsigned int i, max_i;
|
||||
@ -123,53 +102,34 @@ enum {
|
||||
SHOW_PRETTY = 4
|
||||
};
|
||||
|
||||
/* Forward declarations for option-parsing helper */
|
||||
struct opts {
|
||||
const char *dir;
|
||||
const char *rev;
|
||||
int action;
|
||||
int verbose;
|
||||
};
|
||||
static void parse_opts(struct opts *o, int argc, char *argv[]);
|
||||
|
||||
|
||||
/** Entry point for this command */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *dir = ".", *rev = NULL;
|
||||
int i, action = 0, verbose = 0;
|
||||
git_repository *repo;
|
||||
struct opts o = { ".", NULL, 0, 0 };
|
||||
git_object *obj = NULL;
|
||||
char oidstr[GIT_OID_HEXSZ + 1];
|
||||
|
||||
git_threads_init();
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
char *a = argv[i];
|
||||
parse_opts(&o, argc, argv);
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (rev != NULL)
|
||||
usage("Only one rev should be provided", NULL);
|
||||
else
|
||||
rev = a;
|
||||
}
|
||||
else if (!strcmp(a, "-t"))
|
||||
action = SHOW_TYPE;
|
||||
else if (!strcmp(a, "-s"))
|
||||
action = SHOW_SIZE;
|
||||
else if (!strcmp(a, "-e"))
|
||||
action = SHOW_NONE;
|
||||
else if (!strcmp(a, "-p"))
|
||||
action = SHOW_PRETTY;
|
||||
else if (!strcmp(a, "-q"))
|
||||
verbose = 0;
|
||||
else if (!strcmp(a, "-v"))
|
||||
verbose = 1;
|
||||
else if (!strcmp(a, "--help") || !strcmp(a, "-h"))
|
||||
usage(NULL, NULL);
|
||||
else if (!check_str_param(a, "--git-dir=", &dir))
|
||||
usage("Unknown option", a);
|
||||
}
|
||||
check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
|
||||
"Could not open repository", NULL);
|
||||
check_lg2(git_revparse_single(&obj, repo, o.rev),
|
||||
"Could not resolve", o.rev);
|
||||
|
||||
if (!action || !rev)
|
||||
usage(NULL, NULL);
|
||||
|
||||
check(git_repository_open_ext(&g_repo, dir, 0, NULL),
|
||||
"Could not open repository");
|
||||
|
||||
if (git_revparse_single(&obj, g_repo, rev) < 0) {
|
||||
fprintf(stderr, "Could not resolve '%s'\n", rev);
|
||||
exit(1);
|
||||
}
|
||||
if (verbose) {
|
||||
if (o.verbose) {
|
||||
char oidstr[GIT_OID_HEXSZ + 1];
|
||||
git_oid_tostr(oidstr, sizeof(oidstr), git_object_id(obj));
|
||||
|
||||
@ -177,7 +137,7 @@ int main(int argc, char *argv[])
|
||||
git_object_type2string(git_object_type(obj)), oidstr);
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
switch (o.action) {
|
||||
case SHOW_TYPE:
|
||||
printf("%s\n", git_object_type2string(git_object_type(obj)));
|
||||
break;
|
||||
@ -185,9 +145,9 @@ int main(int argc, char *argv[])
|
||||
git_odb *odb;
|
||||
git_odb_object *odbobj;
|
||||
|
||||
check(git_repository_odb(&odb, g_repo), "Could not open ODB");
|
||||
check(git_odb_read(&odbobj, odb, git_object_id(obj)),
|
||||
"Could not find obj");
|
||||
check_lg2(git_repository_odb(&odb, repo), "Could not open ODB", NULL);
|
||||
check_lg2(git_odb_read(&odbobj, odb, git_object_id(obj)),
|
||||
"Could not find obj", NULL);
|
||||
|
||||
printf("%ld\n", (long)git_odb_object_size(odbobj));
|
||||
|
||||
@ -221,9 +181,59 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
git_object_free(obj);
|
||||
git_repository_free(g_repo);
|
||||
git_repository_free(repo);
|
||||
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Print out usage information */
|
||||
static void usage(const char *message, const char *arg)
|
||||
{
|
||||
if (message && arg)
|
||||
fprintf(stderr, "%s: %s\n", message, arg);
|
||||
else if (message)
|
||||
fprintf(stderr, "%s\n", message);
|
||||
fprintf(stderr,
|
||||
"usage: cat-file (-t | -s | -e | -p) [-v] [-q] "
|
||||
"[-h|--help] [--git-dir=<dir>] <object>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** Parse the command-line options taken from git */
|
||||
static void parse_opts(struct opts *o, int argc, char *argv[])
|
||||
{
|
||||
struct args_info args = ARGS_INFO_INIT;
|
||||
|
||||
for (args.pos = 1; args.pos < argc; ++args.pos) {
|
||||
char *a = argv[args.pos];
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (o->rev != NULL)
|
||||
usage("Only one rev should be provided", NULL);
|
||||
else
|
||||
o->rev = a;
|
||||
}
|
||||
else if (!strcmp(a, "-t"))
|
||||
o->action = SHOW_TYPE;
|
||||
else if (!strcmp(a, "-s"))
|
||||
o->action = SHOW_SIZE;
|
||||
else if (!strcmp(a, "-e"))
|
||||
o->action = SHOW_NONE;
|
||||
else if (!strcmp(a, "-p"))
|
||||
o->action = SHOW_PRETTY;
|
||||
else if (!strcmp(a, "-q"))
|
||||
o->verbose = 0;
|
||||
else if (!strcmp(a, "-v"))
|
||||
o->verbose = 1;
|
||||
else if (!strcmp(a, "--help") || !strcmp(a, "-h"))
|
||||
usage(NULL, NULL);
|
||||
else if (!match_str_arg(&o->dir, &args, "--git-dir"))
|
||||
usage("Unknown option", a);
|
||||
}
|
||||
|
||||
if (!o->action || !o->rev)
|
||||
usage(NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
184
examples/common.c
Normal file
184
examples/common.c
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
void check_lg2(int error, const char *message, const char *extra)
|
||||
{
|
||||
const git_error *lg2err;
|
||||
const char *lg2msg = "", *lg2spacer = "";
|
||||
|
||||
if (!error)
|
||||
return;
|
||||
|
||||
if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
|
||||
lg2msg = lg2err->message;
|
||||
lg2spacer = " - ";
|
||||
}
|
||||
|
||||
if (extra)
|
||||
fprintf(stderr, "%s '%s' [%d]%s%s\n",
|
||||
message, extra, error, lg2spacer, lg2msg);
|
||||
else
|
||||
fprintf(stderr, "%s [%d]%s%s\n",
|
||||
message, error, lg2spacer, lg2msg);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void fatal(const char *message, const char *extra)
|
||||
{
|
||||
if (extra)
|
||||
fprintf(stderr, "%s %s\n", message, extra);
|
||||
else
|
||||
fprintf(stderr, "%s\n", message);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_t is_prefixed(const char *str, const char *pfx)
|
||||
{
|
||||
size_t len = strlen(pfx);
|
||||
return strncmp(str, pfx, len) ? 0 : len;
|
||||
}
|
||||
|
||||
int match_str_arg(
|
||||
const char **out, struct args_info *args, const char *opt)
|
||||
{
|
||||
const char *found = args->argv[args->pos];
|
||||
size_t len = is_prefixed(found, opt);
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (!found[len]) {
|
||||
if (args->pos + 1 == args->argc)
|
||||
fatal("expected value following argument", opt);
|
||||
args->pos += 1;
|
||||
*out = args->argv[args->pos];
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (found[len] == '=') {
|
||||
*out = found + len + 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *match_numeric_arg(struct args_info *args, const char *opt)
|
||||
{
|
||||
const char *found = args->argv[args->pos];
|
||||
size_t len = is_prefixed(found, opt);
|
||||
|
||||
if (!len)
|
||||
return NULL;
|
||||
|
||||
if (!found[len]) {
|
||||
if (args->pos + 1 == args->argc)
|
||||
fatal("expected numeric value following argument", opt);
|
||||
args->pos += 1;
|
||||
found = args->argv[args->pos];
|
||||
} else {
|
||||
found = found + len;
|
||||
if (*found == '=')
|
||||
found++;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
int match_uint16_arg(
|
||||
uint16_t *out, struct args_info *args, const char *opt)
|
||||
{
|
||||
const char *found = match_numeric_arg(args, opt);
|
||||
uint16_t val;
|
||||
char *endptr = NULL;
|
||||
|
||||
if (!found)
|
||||
return 0;
|
||||
|
||||
val = (uint16_t)strtoul(found, &endptr, 0);
|
||||
if (!endptr || *endptr != '\0')
|
||||
fatal("expected number after argument", opt);
|
||||
|
||||
if (out)
|
||||
*out = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int match_int_internal(
|
||||
int *out, const char *str, int allow_negative, const char *opt)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
int val = (int)strtol(str, &endptr, 10);
|
||||
|
||||
if (!endptr || *endptr != '\0')
|
||||
fatal("expected number", opt);
|
||||
else if (val < 0 && !allow_negative)
|
||||
fatal("negative values are not allowed", opt);
|
||||
|
||||
if (out)
|
||||
*out = val;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int is_integer(int *out, const char *str, int allow_negative)
|
||||
{
|
||||
return match_int_internal(out, str, allow_negative, NULL);
|
||||
}
|
||||
|
||||
int match_int_arg(
|
||||
int *out, struct args_info *args, const char *opt, int allow_negative)
|
||||
{
|
||||
const char *found = match_numeric_arg(args, opt);
|
||||
if (!found)
|
||||
return 0;
|
||||
return match_int_internal(out, found, allow_negative, opt);
|
||||
}
|
||||
|
||||
int diff_output(
|
||||
const git_diff_delta *d,
|
||||
const git_diff_hunk *h,
|
||||
const git_diff_line *l,
|
||||
void *p)
|
||||
{
|
||||
FILE *fp = p;
|
||||
|
||||
(void)d; (void)h;
|
||||
|
||||
if (!fp)
|
||||
fp = stdout;
|
||||
|
||||
if (l->origin == GIT_DIFF_LINE_CONTEXT ||
|
||||
l->origin == GIT_DIFF_LINE_ADDITION ||
|
||||
l->origin == GIT_DIFF_LINE_DELETION)
|
||||
fputc(l->origin, fp);
|
||||
|
||||
fwrite(l->content, 1, l->content_len, fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void treeish_to_tree(
|
||||
git_tree **out, git_repository *repo, const char *treeish)
|
||||
{
|
||||
git_object *obj = NULL;
|
||||
|
||||
check_lg2(
|
||||
git_revparse_single(&obj, repo, treeish),
|
||||
"looking up object", treeish);
|
||||
|
||||
check_lg2(
|
||||
git_object_peel((git_object **)out, obj, GIT_OBJ_TREE),
|
||||
"resolving object to tree", treeish);
|
||||
|
||||
git_object_free(obj);
|
||||
}
|
||||
|
||||
80
examples/common.h
Normal file
80
examples/common.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <git2.h>
|
||||
|
||||
/**
|
||||
* Check libgit2 error code, printing error to stderr on failure and
|
||||
* exiting the program.
|
||||
*/
|
||||
extern void check_lg2(int error, const char *message, const char *extra);
|
||||
|
||||
/**
|
||||
* Exit the program, printing error to stderr
|
||||
*/
|
||||
extern void fatal(const char *message, const char *extra);
|
||||
|
||||
/**
|
||||
* Check if a string has the given prefix. Returns 0 if not prefixed
|
||||
* or the length of the prefix if it is.
|
||||
*/
|
||||
extern size_t is_prefixed(const char *str, const char *pfx);
|
||||
|
||||
/**
|
||||
* Match an integer string, returning 1 if matched, 0 if not.
|
||||
*/
|
||||
extern int is_integer(int *out, const char *str, int allow_negative);
|
||||
|
||||
struct args_info {
|
||||
int argc;
|
||||
char **argv;
|
||||
int pos;
|
||||
};
|
||||
#define ARGS_INFO_INIT { argc, argv, 0 }
|
||||
|
||||
/**
|
||||
* Check current `args` entry against `opt` string. If it matches
|
||||
* exactly, take the next arg as a string; if it matches as a prefix with
|
||||
* an equal sign, take the remainder as a string; otherwise return 0.
|
||||
*/
|
||||
extern int match_str_arg(
|
||||
const char **out, struct args_info *args, const char *opt);
|
||||
|
||||
/**
|
||||
* Check current `args` entry against `opt` string parsing as uint16. If
|
||||
* `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
|
||||
* is a prefix (equal sign optional), take the remainder of the arg as a
|
||||
* uint16_t value; otherwise return 0.
|
||||
*/
|
||||
extern int match_uint16_arg(
|
||||
uint16_t *out, struct args_info *args, const char *opt);
|
||||
|
||||
/**
|
||||
* Check current `args` entry against `opt` string parsing as int. If
|
||||
* `opt` matches exactly, take the next arg as an int value; if it matches
|
||||
* as a prefix (equal sign optional), take the remainder of the arg as a
|
||||
* int value; otherwise return 0.
|
||||
*/
|
||||
extern int match_int_arg(
|
||||
int *out, struct args_info *args, const char *opt, int allow_negative);
|
||||
|
||||
/**
|
||||
* Basic output function for plain text diff output
|
||||
* Pass `FILE*` such as `stdout` or `stderr` as payload (or NULL == `stdout`)
|
||||
*/
|
||||
extern int diff_output(
|
||||
const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
|
||||
|
||||
/**
|
||||
* Convert a treeish argument to an actual tree; this will call check_lg2
|
||||
* and exit the program if `treeish` cannot be resolved to a tree
|
||||
*/
|
||||
extern void treeish_to_tree(
|
||||
git_tree **out, git_repository *repo, const char *treeish);
|
||||
374
examples/diff.c
374
examples/diff.c
@ -1,41 +1,24 @@
|
||||
#include <stdio.h>
|
||||
#include <git2.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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.
|
||||
*/
|
||||
|
||||
static void check(int error, const char *message)
|
||||
{
|
||||
if (error) {
|
||||
fprintf(stderr, "%s (%d)\n", message, error);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#include "common.h"
|
||||
|
||||
static int resolve_to_tree(
|
||||
git_repository *repo, const char *identifier, git_tree **tree)
|
||||
{
|
||||
int err = 0;
|
||||
git_object *obj = NULL;
|
||||
/**
|
||||
* This example demonstrates the use of the libgit2 diff APIs to
|
||||
* create `git_diff` objects and display them, emulating a number of
|
||||
* core Git `diff` command line options.
|
||||
*
|
||||
* This covers on a portion of the core Git diff options and doesn't
|
||||
* have particularly good error handling, but it should show most of
|
||||
* the core libgit2 diff APIs, including various types of diffs and
|
||||
* how to do renaming detection and patch formatting.
|
||||
*/
|
||||
|
||||
if ((err = git_revparse_single(&obj, repo, identifier)) < 0)
|
||||
return err;
|
||||
|
||||
switch (git_object_type(obj)) {
|
||||
case GIT_OBJ_TREE:
|
||||
*tree = (git_tree *)obj;
|
||||
break;
|
||||
case GIT_OBJ_COMMIT:
|
||||
err = git_commit_tree(tree, (git_commit *)obj);
|
||||
git_object_free(obj);
|
||||
break;
|
||||
default:
|
||||
err = GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
char *colors[] = {
|
||||
static const char *colors[] = {
|
||||
"\033[m", /* reset */
|
||||
"\033[1m", /* bold */
|
||||
"\033[31m", /* red */
|
||||
@ -43,7 +26,124 @@ char *colors[] = {
|
||||
"\033[36m" /* cyan */
|
||||
};
|
||||
|
||||
static int printer(
|
||||
/** The 'opts' struct captures all the various parsed command line options. */
|
||||
struct opts {
|
||||
git_diff_options diffopts;
|
||||
git_diff_find_options findopts;
|
||||
int color;
|
||||
int cached;
|
||||
git_diff_format_t format;
|
||||
const char *treeish1;
|
||||
const char *treeish2;
|
||||
const char *dir;
|
||||
};
|
||||
|
||||
/** These functions are implemented at the end */
|
||||
static void parse_opts(struct opts *o, int argc, char *argv[]);
|
||||
static int color_printer(
|
||||
const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
git_repository *repo = NULL;
|
||||
git_tree *t1 = NULL, *t2 = NULL;
|
||||
git_diff *diff;
|
||||
struct opts o = {
|
||||
GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
|
||||
-1, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
|
||||
};
|
||||
|
||||
git_threads_init();
|
||||
|
||||
parse_opts(&o, argc, argv);
|
||||
|
||||
check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
|
||||
"Could not open repository", o.dir);
|
||||
|
||||
/**
|
||||
* Possible argument patterns:
|
||||
*
|
||||
* * <sha1> <sha2>
|
||||
* * <sha1> --cached
|
||||
* * <sha1>
|
||||
* * --cached
|
||||
* * nothing
|
||||
*
|
||||
* Currently ranged arguments like <sha1>..<sha2> and <sha1>...<sha2>
|
||||
* are not supported in this example
|
||||
*/
|
||||
|
||||
if (o.treeish1)
|
||||
treeish_to_tree(&t1, repo, o.treeish1);
|
||||
if (o.treeish2)
|
||||
treeish_to_tree(&t2, repo, o.treeish2);
|
||||
|
||||
if (t1 && t2)
|
||||
check_lg2(
|
||||
git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts),
|
||||
"diff trees", NULL);
|
||||
else if (t1 && o.cached)
|
||||
check_lg2(
|
||||
git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
|
||||
"diff tree to index", NULL);
|
||||
else if (t1)
|
||||
check_lg2(
|
||||
git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts),
|
||||
"diff tree to working directory", NULL);
|
||||
else if (o.cached) {
|
||||
treeish_to_tree(&t1, repo, "HEAD");
|
||||
check_lg2(
|
||||
git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
|
||||
"diff tree to index", NULL);
|
||||
}
|
||||
else
|
||||
check_lg2(
|
||||
git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts),
|
||||
"diff index to working directory", NULL);
|
||||
|
||||
/** Apply rename and copy detection if requested. */
|
||||
|
||||
if ((o.findopts.flags & GIT_DIFF_FIND_ALL) != 0)
|
||||
check_lg2(
|
||||
git_diff_find_similar(diff, &o.findopts),
|
||||
"finding renames and copies", NULL);
|
||||
|
||||
/** Generate simple output using libgit2 display helper. */
|
||||
|
||||
if (o.color >= 0)
|
||||
fputs(colors[0], stdout);
|
||||
|
||||
check_lg2(
|
||||
git_diff_print(diff, o.format, color_printer, &o.color),
|
||||
"displaying diff", NULL);
|
||||
|
||||
if (o.color >= 0)
|
||||
fputs(colors[0], stdout);
|
||||
|
||||
/** Cleanup before exiting. */
|
||||
|
||||
git_diff_free(diff);
|
||||
git_tree_free(t1);
|
||||
git_tree_free(t2);
|
||||
git_repository_free(repo);
|
||||
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(const char *message, const char *arg)
|
||||
{
|
||||
if (message && arg)
|
||||
fprintf(stderr, "%s: %s\n", message, arg);
|
||||
else if (message)
|
||||
fprintf(stderr, "%s\n", message);
|
||||
fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** This implements very rudimentary colorized output. */
|
||||
static int color_printer(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_hunk *hunk,
|
||||
const git_diff_line *line,
|
||||
@ -63,6 +163,7 @@ static int printer(
|
||||
case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (color != *last_color) {
|
||||
if (*last_color == 1 || color == 1)
|
||||
fputs(colors[0], stdout);
|
||||
@ -71,186 +172,79 @@ static int printer(
|
||||
}
|
||||
}
|
||||
|
||||
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
|
||||
line->origin == GIT_DIFF_LINE_ADDITION ||
|
||||
line->origin == GIT_DIFF_LINE_DELETION)
|
||||
fputc(line->origin, stdout);
|
||||
|
||||
fwrite(line->content, 1, line->content_len, stdout);
|
||||
|
||||
return 0;
|
||||
return diff_output(delta, hunk, line, stdout);
|
||||
}
|
||||
|
||||
static int check_uint16_param(const char *arg, const char *pattern, uint16_t *val)
|
||||
/** Parse arguments as copied from git-diff. */
|
||||
static void parse_opts(struct opts *o, int argc, char *argv[])
|
||||
{
|
||||
size_t len = strlen(pattern);
|
||||
uint16_t strval;
|
||||
char *endptr = NULL;
|
||||
if (strncmp(arg, pattern, len))
|
||||
return 0;
|
||||
if (arg[len] == '\0' && pattern[len - 1] != '=')
|
||||
return 1;
|
||||
if (arg[len] == '=')
|
||||
len++;
|
||||
strval = strtoul(arg + len, &endptr, 0);
|
||||
if (endptr == arg)
|
||||
return 0;
|
||||
*val = strval;
|
||||
return 1;
|
||||
}
|
||||
struct args_info args = ARGS_INFO_INIT;
|
||||
|
||||
static int check_str_param(const char *arg, const char *pattern, const char **val)
|
||||
{
|
||||
size_t len = strlen(pattern);
|
||||
if (strncmp(arg, pattern, len))
|
||||
return 0;
|
||||
*val = (const char *)(arg + len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void usage(const char *message, const char *arg)
|
||||
{
|
||||
if (message && arg)
|
||||
fprintf(stderr, "%s: %s\n", message, arg);
|
||||
else if (message)
|
||||
fprintf(stderr, "%s\n", message);
|
||||
fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
git_repository *repo = NULL;
|
||||
git_tree *t1 = NULL, *t2 = NULL;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||
git_diff *diff;
|
||||
int i, color = -1, cached = 0;
|
||||
git_diff_format_t format = GIT_DIFF_FORMAT_PATCH;
|
||||
char *a, *treeish1 = NULL, *treeish2 = NULL;
|
||||
const char *dir = ".";
|
||||
|
||||
git_threads_init();
|
||||
|
||||
/* parse arguments as copied from git-diff */
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
a = argv[i];
|
||||
for (args.pos = 1; args.pos < argc; ++args.pos) {
|
||||
const char *a = argv[args.pos];
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (treeish1 == NULL)
|
||||
treeish1 = a;
|
||||
else if (treeish2 == NULL)
|
||||
treeish2 = a;
|
||||
if (o->treeish1 == NULL)
|
||||
o->treeish1 = a;
|
||||
else if (o->treeish2 == NULL)
|
||||
o->treeish2 = a;
|
||||
else
|
||||
usage("Only one or two tree identifiers can be provided", NULL);
|
||||
}
|
||||
else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
|
||||
!strcmp(a, "--patch"))
|
||||
format = GIT_DIFF_FORMAT_PATCH;
|
||||
o->format = GIT_DIFF_FORMAT_PATCH;
|
||||
else if (!strcmp(a, "--cached"))
|
||||
cached = 1;
|
||||
o->cached = 1;
|
||||
else if (!strcmp(a, "--name-only"))
|
||||
format = GIT_DIFF_FORMAT_NAME_ONLY;
|
||||
o->format = GIT_DIFF_FORMAT_NAME_ONLY;
|
||||
else if (!strcmp(a, "--name-status"))
|
||||
format = GIT_DIFF_FORMAT_NAME_STATUS;
|
||||
o->format = GIT_DIFF_FORMAT_NAME_STATUS;
|
||||
else if (!strcmp(a, "--raw"))
|
||||
format = GIT_DIFF_FORMAT_RAW;
|
||||
o->format = GIT_DIFF_FORMAT_RAW;
|
||||
else if (!strcmp(a, "--color"))
|
||||
color = 0;
|
||||
o->color = 0;
|
||||
else if (!strcmp(a, "--no-color"))
|
||||
color = -1;
|
||||
o->color = -1;
|
||||
else if (!strcmp(a, "-R"))
|
||||
opts.flags |= GIT_DIFF_REVERSE;
|
||||
o->diffopts.flags |= GIT_DIFF_REVERSE;
|
||||
else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
|
||||
opts.flags |= GIT_DIFF_FORCE_TEXT;
|
||||
o->diffopts.flags |= GIT_DIFF_FORCE_TEXT;
|
||||
else if (!strcmp(a, "--ignore-space-at-eol"))
|
||||
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
|
||||
o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
|
||||
else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
|
||||
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
|
||||
o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
|
||||
else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
|
||||
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
|
||||
o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
|
||||
else if (!strcmp(a, "--ignored"))
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
|
||||
o->diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED;
|
||||
else if (!strcmp(a, "--untracked"))
|
||||
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
else if (check_uint16_param(a, "-M", &findopts.rename_threshold) ||
|
||||
check_uint16_param(a, "--find-renames",
|
||||
&findopts.rename_threshold))
|
||||
findopts.flags |= GIT_DIFF_FIND_RENAMES;
|
||||
else if (check_uint16_param(a, "-C", &findopts.copy_threshold) ||
|
||||
check_uint16_param(a, "--find-copies",
|
||||
&findopts.copy_threshold))
|
||||
findopts.flags |= GIT_DIFF_FIND_COPIES;
|
||||
o->diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
else if (match_uint16_arg(
|
||||
&o->findopts.rename_threshold, &args, "-M") ||
|
||||
match_uint16_arg(
|
||||
&o->findopts.rename_threshold, &args, "--find-renames"))
|
||||
o->findopts.flags |= GIT_DIFF_FIND_RENAMES;
|
||||
else if (match_uint16_arg(
|
||||
&o->findopts.copy_threshold, &args, "-C") ||
|
||||
match_uint16_arg(
|
||||
&o->findopts.copy_threshold, &args, "--find-copies"))
|
||||
o->findopts.flags |= GIT_DIFF_FIND_COPIES;
|
||||
else if (!strcmp(a, "--find-copies-harder"))
|
||||
findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
|
||||
else if (!strncmp(a, "-B", 2) || !strncmp(a, "--break-rewrites", 16)) {
|
||||
o->findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
|
||||
else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites"))
|
||||
/* TODO: parse thresholds */
|
||||
findopts.flags |= GIT_DIFF_FIND_REWRITES;
|
||||
}
|
||||
else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
|
||||
!check_uint16_param(a, "--unified=", &opts.context_lines) &&
|
||||
!check_uint16_param(a, "--inter-hunk-context=",
|
||||
&opts.interhunk_lines) &&
|
||||
!check_str_param(a, "--src-prefix=", &opts.old_prefix) &&
|
||||
!check_str_param(a, "--dst-prefix=", &opts.new_prefix) &&
|
||||
!check_str_param(a, "--git-dir=", &dir))
|
||||
usage("Unknown arg", a);
|
||||
o->findopts.flags |= GIT_DIFF_FIND_REWRITES;
|
||||
else if (!match_uint16_arg(
|
||||
&o->diffopts.context_lines, &args, "-U") &&
|
||||
!match_uint16_arg(
|
||||
&o->diffopts.context_lines, &args, "--unified") &&
|
||||
!match_uint16_arg(
|
||||
&o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
|
||||
!match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") &&
|
||||
!match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") &&
|
||||
!match_str_arg(&o->dir, &args, "--git-dir"))
|
||||
usage("Unknown command line argument", a);
|
||||
}
|
||||
|
||||
/* open repo */
|
||||
|
||||
check(git_repository_open_ext(&repo, dir, 0, NULL),
|
||||
"Could not open repository");
|
||||
|
||||
if (treeish1)
|
||||
check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree");
|
||||
if (treeish2)
|
||||
check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree");
|
||||
|
||||
/* <sha1> <sha2> */
|
||||
/* <sha1> --cached */
|
||||
/* <sha1> */
|
||||
/* --cached */
|
||||
/* nothing */
|
||||
|
||||
if (t1 && t2)
|
||||
check(git_diff_tree_to_tree(&diff, repo, t1, t2, &opts), "Diff");
|
||||
else if (t1 && cached)
|
||||
check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
|
||||
else if (t1) {
|
||||
git_diff *diff2;
|
||||
check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
|
||||
check(git_diff_index_to_workdir(&diff2, repo, NULL, &opts), "Diff");
|
||||
check(git_diff_merge(diff, diff2), "Merge diffs");
|
||||
git_diff_free(diff2);
|
||||
}
|
||||
else if (cached) {
|
||||
check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
|
||||
check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
|
||||
}
|
||||
else
|
||||
check(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Diff");
|
||||
|
||||
if ((findopts.flags & GIT_DIFF_FIND_ALL) != 0)
|
||||
check(git_diff_find_similar(diff, &findopts),
|
||||
"finding renames and copies ");
|
||||
|
||||
if (color >= 0)
|
||||
fputs(colors[0], stdout);
|
||||
|
||||
check(git_diff_print(diff, format, printer, &color), "Displaying diff");
|
||||
|
||||
if (color >= 0)
|
||||
fputs(colors[0], stdout);
|
||||
|
||||
git_diff_free(diff);
|
||||
git_tree_free(t1);
|
||||
git_tree_free(t2);
|
||||
git_repository_free(repo);
|
||||
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
357
examples/init.c
357
examples/init.c
@ -1,4 +1,13 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
/**
|
||||
* This is a sample program that is similar to "git init". See the
|
||||
* documentation for that (try "git help init") to understand what this
|
||||
* program is emulating.
|
||||
@ -8,45 +17,170 @@
|
||||
* This also contains a special additional option that regular "git init"
|
||||
* does not support which is "--initial-commit" to make a first empty commit.
|
||||
* That is demonstrated in the "create_initial_commit" helper function.
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
/** Forward declarations of helpers */
|
||||
struct opts {
|
||||
int no_options;
|
||||
int quiet;
|
||||
int bare;
|
||||
int initial_commit;
|
||||
uint32_t shared;
|
||||
const char *template;
|
||||
const char *gitdir;
|
||||
const char *dir;
|
||||
};
|
||||
static void create_initial_commit(git_repository *repo);
|
||||
static void parse_opts(struct opts *o, int argc, char *argv[]);
|
||||
|
||||
/* not actually good error handling */
|
||||
static void fail(const char *msg, const char *arg)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (arg)
|
||||
fprintf(stderr, "%s %s\n", msg, arg);
|
||||
else
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
exit(1);
|
||||
git_repository *repo = NULL;
|
||||
struct opts o = { 1, 0, 0, 0, GIT_REPOSITORY_INIT_SHARED_UMASK, 0, 0, 0 };
|
||||
|
||||
git_threads_init();
|
||||
|
||||
parse_opts(&o, argc, argv);
|
||||
|
||||
/* Initialize repository. */
|
||||
|
||||
if (o.no_options) {
|
||||
/**
|
||||
* No options were specified, so let's demonstrate the default
|
||||
* simple case of git_repository_init() API usage...
|
||||
*/
|
||||
check_lg2(git_repository_init(&repo, o.dir, 0),
|
||||
"Could not initialize repository", NULL);
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Some command line options were specified, so we'll use the
|
||||
* extended init API to handle them
|
||||
*/
|
||||
git_repository_init_options initopts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
|
||||
initopts.flags = GIT_REPOSITORY_INIT_MKPATH;
|
||||
|
||||
if (o.bare)
|
||||
initopts.flags |= GIT_REPOSITORY_INIT_BARE;
|
||||
|
||||
if (o.template) {
|
||||
initopts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
|
||||
initopts.template_path = o.template;
|
||||
}
|
||||
|
||||
if (o.gitdir) {
|
||||
/**
|
||||
* If you specified a separate git directory, then initialize
|
||||
* the repository at that path and use the second path as the
|
||||
* working directory of the repository (with a git-link file)
|
||||
*/
|
||||
initopts.workdir_path = o.dir;
|
||||
o.dir = o.gitdir;
|
||||
}
|
||||
|
||||
if (o.shared != 0)
|
||||
initopts.mode = o.shared;
|
||||
|
||||
check_lg2(git_repository_init_ext(&repo, o.dir, &initopts),
|
||||
"Could not initialize repository", NULL);
|
||||
}
|
||||
|
||||
/** Print a message to stdout like "git init" does. */
|
||||
|
||||
if (!o.quiet) {
|
||||
if (o.bare || o.gitdir)
|
||||
o.dir = git_repository_path(repo);
|
||||
else
|
||||
o.dir = git_repository_workdir(repo);
|
||||
|
||||
printf("Initialized empty Git repository in %s\n", o.dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* As an extension to the basic "git init" command, this example
|
||||
* gives the option to create an empty initial commit. This is
|
||||
* mostly to demonstrate what it takes to do that, but also some
|
||||
* people like to have that empty base commit in their repo.
|
||||
*/
|
||||
if (o.initial_commit) {
|
||||
create_initial_commit(repo);
|
||||
printf("Created empty initial commit\n");
|
||||
}
|
||||
|
||||
git_repository_free(repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlike regular "git init", this example shows how to create an initial
|
||||
* empty commit in the repository. This is the helper function that does
|
||||
* that.
|
||||
*/
|
||||
static void create_initial_commit(git_repository *repo)
|
||||
{
|
||||
git_signature *sig;
|
||||
git_index *index;
|
||||
git_oid tree_id, commit_id;
|
||||
git_tree *tree;
|
||||
|
||||
/** First use the config to initialize a commit signature for the user. */
|
||||
|
||||
if (git_signature_default(&sig, repo) < 0)
|
||||
fatal("Unable to create a commit signature.",
|
||||
"Perhaps 'user.name' and 'user.email' are not set");
|
||||
|
||||
/* Now let's create an empty tree for this commit */
|
||||
|
||||
if (git_repository_index(&index, repo) < 0)
|
||||
fatal("Could not open repository index", NULL);
|
||||
|
||||
/**
|
||||
* Outside of this example, you could call git_index_add_bypath()
|
||||
* here to put actual files into the index. For our purposes, we'll
|
||||
* leave it empty for now.
|
||||
*/
|
||||
|
||||
if (git_index_write_tree(&tree_id, index) < 0)
|
||||
fatal("Unable to write initial tree from index", NULL);
|
||||
|
||||
git_index_free(index);
|
||||
|
||||
if (git_tree_lookup(&tree, repo, &tree_id) < 0)
|
||||
fatal("Could not look up initial tree", NULL);
|
||||
|
||||
/**
|
||||
* Ready to create the initial commit.
|
||||
*
|
||||
* Normally creating a commit would involve looking up the current
|
||||
* HEAD commit and making that be the parent of the initial commit,
|
||||
* but here this is the first commit so there will be no parent.
|
||||
*/
|
||||
|
||||
if (git_commit_create_v(
|
||||
&commit_id, repo, "HEAD", sig, sig,
|
||||
NULL, "Initial commit", tree, 0) < 0)
|
||||
fatal("Could not create the initial commit", NULL);
|
||||
|
||||
/** Clean up so we don't leak memory. */
|
||||
|
||||
git_tree_free(tree);
|
||||
git_signature_free(sig);
|
||||
}
|
||||
|
||||
static void usage(const char *error, const char *arg)
|
||||
{
|
||||
fprintf(stderr, "error: %s '%s'\n", error, arg);
|
||||
fprintf(stderr, "usage: init [-q | --quiet] [--bare] "
|
||||
"[--template=<dir>] [--shared[=perms]] <directory>\n");
|
||||
fprintf(stderr,
|
||||
"usage: init [-q | --quiet] [--bare] [--template=<dir>]\n"
|
||||
" [--shared[=perms]] [--initial-commit]\n"
|
||||
" [--separate-git-dir] <directory>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* simple string prefix test used in argument parsing */
|
||||
static size_t is_prefixed(const char *arg, const char *pfx)
|
||||
{
|
||||
size_t len = strlen(pfx);
|
||||
return !strncmp(arg, pfx, len) ? len : 0;
|
||||
}
|
||||
|
||||
/* parse the tail of the --shared= argument */
|
||||
/** Parse the tail of the --shared= argument. */
|
||||
static uint32_t parse_shared(const char *shared)
|
||||
{
|
||||
if (!strcmp(shared, "false") || !strcmp(shared, "umask"))
|
||||
@ -74,172 +208,39 @@ static uint32_t parse_shared(const char *shared)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* forward declaration of helper to make an empty parent-less commit */
|
||||
static void create_initial_commit(git_repository *repo);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
static void parse_opts(struct opts *o, int argc, char *argv[])
|
||||
{
|
||||
git_repository *repo = NULL;
|
||||
int no_options = 1, quiet = 0, bare = 0, initial_commit = 0, i;
|
||||
uint32_t shared = GIT_REPOSITORY_INIT_SHARED_UMASK;
|
||||
const char *template = NULL, *gitdir = NULL, *dir = NULL;
|
||||
size_t pfxlen;
|
||||
struct args_info args = ARGS_INFO_INIT;
|
||||
const char *sharedarg;
|
||||
|
||||
git_threads_init();
|
||||
/** Process arguments. */
|
||||
|
||||
/* Process arguments */
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
char *a = argv[i];
|
||||
for (args.pos = 1; args.pos < argc; ++args.pos) {
|
||||
char *a = argv[args.pos];
|
||||
|
||||
if (a[0] == '-')
|
||||
no_options = 0;
|
||||
o->no_options = 0;
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (dir != NULL)
|
||||
if (o->dir != NULL)
|
||||
usage("extra argument", a);
|
||||
dir = a;
|
||||
o->dir = a;
|
||||
}
|
||||
else if (!strcmp(a, "-q") || !strcmp(a, "--quiet"))
|
||||
quiet = 1;
|
||||
o->quiet = 1;
|
||||
else if (!strcmp(a, "--bare"))
|
||||
bare = 1;
|
||||
else if ((pfxlen = is_prefixed(a, "--template=")) > 0)
|
||||
template = a + pfxlen;
|
||||
else if (!strcmp(a, "--separate-git-dir"))
|
||||
gitdir = argv[++i];
|
||||
else if ((pfxlen = is_prefixed(a, "--separate-git-dir=")) > 0)
|
||||
gitdir = a + pfxlen;
|
||||
o->bare = 1;
|
||||
else if (!strcmp(a, "--shared"))
|
||||
shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
|
||||
else if ((pfxlen = is_prefixed(a, "--shared=")) > 0)
|
||||
shared = parse_shared(a + pfxlen);
|
||||
o->shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
|
||||
else if (!strcmp(a, "--initial-commit"))
|
||||
initial_commit = 1;
|
||||
else
|
||||
o->initial_commit = 1;
|
||||
else if (match_str_arg(&sharedarg, &args, "--shared"))
|
||||
o->shared = parse_shared(sharedarg);
|
||||
else if (!match_str_arg(&o->template, &args, "--template") ||
|
||||
!match_str_arg(&o->gitdir, &args, "--separate-git-dir"))
|
||||
usage("unknown option", a);
|
||||
}
|
||||
|
||||
if (!dir)
|
||||
if (!o->dir)
|
||||
usage("must specify directory to init", NULL);
|
||||
|
||||
/* Initialize repository */
|
||||
|
||||
if (no_options) {
|
||||
/* No options were specified, so let's demonstrate the default
|
||||
* simple case of git_repository_init() API usage...
|
||||
*/
|
||||
|
||||
if (git_repository_init(&repo, dir, 0) < 0)
|
||||
fail("Could not initialize repository", dir);
|
||||
}
|
||||
else {
|
||||
/* Some command line options were specified, so we'll use the
|
||||
* extended init API to handle them
|
||||
*/
|
||||
git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
|
||||
|
||||
if (bare)
|
||||
opts.flags |= GIT_REPOSITORY_INIT_BARE;
|
||||
|
||||
if (template) {
|
||||
opts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
|
||||
opts.template_path = template;
|
||||
}
|
||||
|
||||
if (gitdir) {
|
||||
/* if you specified a separate git directory, then initialize
|
||||
* the repository at that path and use the second path as the
|
||||
* working directory of the repository (with a git-link file)
|
||||
*/
|
||||
opts.workdir_path = dir;
|
||||
dir = gitdir;
|
||||
}
|
||||
|
||||
if (shared != 0)
|
||||
opts.mode = shared;
|
||||
|
||||
if (git_repository_init_ext(&repo, dir, &opts) < 0)
|
||||
fail("Could not initialize repository", dir);
|
||||
}
|
||||
|
||||
/* Print a message to stdout like "git init" does */
|
||||
|
||||
if (!quiet) {
|
||||
if (bare || gitdir)
|
||||
dir = git_repository_path(repo);
|
||||
else
|
||||
dir = git_repository_workdir(repo);
|
||||
|
||||
printf("Initialized empty Git repository in %s\n", dir);
|
||||
}
|
||||
|
||||
/* As an extension to the basic "git init" command, this example
|
||||
* gives the option to create an empty initial commit. This is
|
||||
* mostly to demonstrate what it takes to do that, but also some
|
||||
* people like to have that empty base commit in their repo.
|
||||
*/
|
||||
if (initial_commit) {
|
||||
create_initial_commit(repo);
|
||||
printf("Created empty initial commit\n");
|
||||
}
|
||||
|
||||
git_repository_free(repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlike regular "git init", this example shows how to create an initial
|
||||
* empty commit in the repository. This is the helper function that does
|
||||
* that.
|
||||
*/
|
||||
static void create_initial_commit(git_repository *repo)
|
||||
{
|
||||
git_signature *sig;
|
||||
git_index *index;
|
||||
git_oid tree_id, commit_id;
|
||||
git_tree *tree;
|
||||
|
||||
/* First use the config to initialize a commit signature for the user */
|
||||
|
||||
if (git_signature_default(&sig, repo) < 0)
|
||||
fail("Unable to create a commit signature.",
|
||||
"Perhaps 'user.name' and 'user.email' are not set");
|
||||
|
||||
/* Now let's create an empty tree for this commit */
|
||||
|
||||
if (git_repository_index(&index, repo) < 0)
|
||||
fail("Could not open repository index", NULL);
|
||||
|
||||
/* Outside of this example, you could call git_index_add_bypath()
|
||||
* here to put actual files into the index. For our purposes, we'll
|
||||
* leave it empty for now.
|
||||
*/
|
||||
|
||||
if (git_index_write_tree(&tree_id, index) < 0)
|
||||
fail("Unable to write initial tree from index", NULL);
|
||||
|
||||
git_index_free(index);
|
||||
|
||||
if (git_tree_lookup(&tree, repo, &tree_id) < 0)
|
||||
fail("Could not look up initial tree", NULL);
|
||||
|
||||
/* Ready to create the initial commit
|
||||
*
|
||||
* Normally creating a commit would involve looking up the current
|
||||
* HEAD commit and making that be the parent of the initial commit,
|
||||
* but here this is the first commit so there will be no parent.
|
||||
*/
|
||||
|
||||
if (git_commit_create_v(
|
||||
&commit_id, repo, "HEAD", sig, sig,
|
||||
NULL, "Initial commit", tree, 0) < 0)
|
||||
fail("Could not create the initial commit", NULL);
|
||||
|
||||
/* Clean up so we don't leak memory */
|
||||
|
||||
git_tree_free(tree);
|
||||
git_signature_free(sig);
|
||||
}
|
||||
|
||||
475
examples/log.c
475
examples/log.c
@ -1,88 +1,205 @@
|
||||
#include <stdio.h>
|
||||
#include <git2.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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.
|
||||
*/
|
||||
|
||||
static void check(int error, const char *message, const char *arg)
|
||||
{
|
||||
if (!error)
|
||||
return;
|
||||
if (arg)
|
||||
fprintf(stderr, "%s '%s' (%d)\n", message, arg, error);
|
||||
else
|
||||
fprintf(stderr, "%s (%d)\n", message, error);
|
||||
exit(1);
|
||||
}
|
||||
#include "common.h"
|
||||
|
||||
static void usage(const char *message, const char *arg)
|
||||
{
|
||||
if (message && arg)
|
||||
fprintf(stderr, "%s: %s\n", message, arg);
|
||||
else if (message)
|
||||
fprintf(stderr, "%s\n", message);
|
||||
fprintf(stderr, "usage: log [<options>]\n");
|
||||
exit(1);
|
||||
}
|
||||
/**
|
||||
* This example demonstrates the libgit2 rev walker APIs to roughly
|
||||
* simulate the output of `git log` and a few of command line arguments.
|
||||
* `git log` has many many options and this only shows a few of them.
|
||||
*
|
||||
* This does not have:
|
||||
*
|
||||
* - Robust error handling
|
||||
* - Colorized or paginated output formatting
|
||||
* - Most of the `git log` options
|
||||
*
|
||||
* This does have:
|
||||
*
|
||||
* - Examples of translating command line arguments to equivalent libgit2
|
||||
* revwalker configuration calls
|
||||
* - Simplified options to apply pathspec limits and to show basic diffs
|
||||
*/
|
||||
|
||||
/** log_state represents walker being configured while handling options */
|
||||
struct log_state {
|
||||
git_repository *repo;
|
||||
const char *repodir;
|
||||
git_revwalk *walker;
|
||||
int hide;
|
||||
int sorting;
|
||||
int revisions;
|
||||
};
|
||||
|
||||
static void set_sorting(struct log_state *s, unsigned int sort_mode)
|
||||
/** utility functions that are called to configure the walker */
|
||||
static void set_sorting(struct log_state *s, unsigned int sort_mode);
|
||||
static void push_rev(struct log_state *s, git_object *obj, int hide);
|
||||
static int add_revision(struct log_state *s, const char *revstr);
|
||||
|
||||
/** log_options holds other command line options that affect log output */
|
||||
struct log_options {
|
||||
int show_diff;
|
||||
int skip, limit;
|
||||
int min_parents, max_parents;
|
||||
git_time_t before;
|
||||
git_time_t after;
|
||||
char *author;
|
||||
char *committer;
|
||||
};
|
||||
|
||||
/** utility functions that parse options and help with log output */
|
||||
static int parse_options(
|
||||
struct log_state *s, struct log_options *opt, int argc, char **argv);
|
||||
static void print_time(const git_time *intime, const char *prefix);
|
||||
static void print_commit(git_commit *commit);
|
||||
static int match_with_parent(git_commit *commit, int i, git_diff_options *);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (!s->repo) {
|
||||
if (!s->repodir) s->repodir = ".";
|
||||
check(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
|
||||
"Could not open repository", s->repodir);
|
||||
int i, count = 0, printed = 0, parents, last_arg;
|
||||
struct log_state s;
|
||||
struct log_options opt;
|
||||
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_oid oid;
|
||||
git_commit *commit = NULL;
|
||||
git_pathspec *ps = NULL;
|
||||
|
||||
git_threads_init();
|
||||
|
||||
/** Parse arguments and set up revwalker. */
|
||||
|
||||
last_arg = parse_options(&s, &opt, argc, argv);
|
||||
|
||||
diffopts.pathspec.strings = &argv[last_arg];
|
||||
diffopts.pathspec.count = argc - last_arg;
|
||||
if (diffopts.pathspec.count > 0)
|
||||
check_lg2(git_pathspec_new(&ps, &diffopts.pathspec),
|
||||
"Building pathspec", NULL);
|
||||
|
||||
if (!s.revisions)
|
||||
add_revision(&s, NULL);
|
||||
|
||||
/** Use the revwalker to traverse the history. */
|
||||
|
||||
printed = count = 0;
|
||||
|
||||
for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) {
|
||||
check_lg2(git_commit_lookup(&commit, s.repo, &oid),
|
||||
"Failed to look up commit", NULL);
|
||||
|
||||
parents = (int)git_commit_parentcount(commit);
|
||||
if (parents < opt.min_parents)
|
||||
continue;
|
||||
if (opt.max_parents > 0 && parents > opt.max_parents)
|
||||
continue;
|
||||
|
||||
if (diffopts.pathspec.count > 0) {
|
||||
int unmatched = parents;
|
||||
|
||||
if (parents == 0) {
|
||||
git_tree *tree;
|
||||
check_lg2(git_commit_tree(&tree, commit), "Get tree", NULL);
|
||||
if (git_pathspec_match_tree(
|
||||
NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0)
|
||||
unmatched = 1;
|
||||
git_tree_free(tree);
|
||||
} else if (parents == 1) {
|
||||
unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1;
|
||||
} else {
|
||||
for (i = 0; i < parents; ++i) {
|
||||
if (match_with_parent(commit, i, &diffopts))
|
||||
unmatched--;
|
||||
}
|
||||
}
|
||||
|
||||
if (unmatched > 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count++ < opt.skip)
|
||||
continue;
|
||||
if (opt.limit != -1 && printed++ >= opt.limit) {
|
||||
git_commit_free(commit);
|
||||
break;
|
||||
}
|
||||
|
||||
print_commit(commit);
|
||||
|
||||
if (opt.show_diff) {
|
||||
git_tree *a = NULL, *b = NULL;
|
||||
git_diff *diff = NULL;
|
||||
|
||||
if (parents > 1)
|
||||
continue;
|
||||
check_lg2(git_commit_tree(&b, commit), "Get tree", NULL);
|
||||
if (parents == 1) {
|
||||
git_commit *parent;
|
||||
check_lg2(git_commit_parent(&parent, commit, 0), "Get parent", NULL);
|
||||
check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
|
||||
git_commit_free(parent);
|
||||
}
|
||||
|
||||
check_lg2(git_diff_tree_to_tree(
|
||||
&diff, git_commit_owner(commit), a, b, &diffopts),
|
||||
"Diff commit with parent", NULL);
|
||||
check_lg2(
|
||||
git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_output, NULL),
|
||||
"Displaying diff", NULL);
|
||||
|
||||
git_diff_free(diff);
|
||||
git_tree_free(a);
|
||||
git_tree_free(b);
|
||||
}
|
||||
}
|
||||
|
||||
if (!s->walker)
|
||||
check(git_revwalk_new(&s->walker, s->repo),
|
||||
"Could not create revision walker", NULL);
|
||||
git_pathspec_free(ps);
|
||||
git_revwalk_free(s.walker);
|
||||
git_repository_free(s.repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
if (sort_mode == GIT_SORT_REVERSE)
|
||||
s->sorting = s->sorting ^ GIT_SORT_REVERSE;
|
||||
else
|
||||
s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE);
|
||||
|
||||
git_revwalk_sorting(s->walker, s->sorting);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Push object (for hide or show) onto revwalker. */
|
||||
static void push_rev(struct log_state *s, git_object *obj, int hide)
|
||||
{
|
||||
hide = s->hide ^ hide;
|
||||
|
||||
/** Create revwalker on demand if it doesn't already exist. */
|
||||
if (!s->walker) {
|
||||
check(git_revwalk_new(&s->walker, s->repo),
|
||||
check_lg2(git_revwalk_new(&s->walker, s->repo),
|
||||
"Could not create revision walker", NULL);
|
||||
git_revwalk_sorting(s->walker, s->sorting);
|
||||
}
|
||||
|
||||
if (!obj)
|
||||
check(git_revwalk_push_head(s->walker),
|
||||
check_lg2(git_revwalk_push_head(s->walker),
|
||||
"Could not find repository HEAD", NULL);
|
||||
else if (hide)
|
||||
check(git_revwalk_hide(s->walker, git_object_id(obj)),
|
||||
check_lg2(git_revwalk_hide(s->walker, git_object_id(obj)),
|
||||
"Reference does not refer to a commit", NULL);
|
||||
else
|
||||
check(git_revwalk_push(s->walker, git_object_id(obj)),
|
||||
check_lg2(git_revwalk_push(s->walker, git_object_id(obj)),
|
||||
"Reference does not refer to a commit", NULL);
|
||||
|
||||
git_object_free(obj);
|
||||
}
|
||||
|
||||
/** Parse revision string and add revs to walker. */
|
||||
static int add_revision(struct log_state *s, const char *revstr)
|
||||
{
|
||||
git_revspec revs;
|
||||
int hide = 0;
|
||||
|
||||
/** Open repo on demand if it isn't already open. */
|
||||
if (!s->repo) {
|
||||
if (!s->repodir) s->repodir = ".";
|
||||
check(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
|
||||
check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
|
||||
"Could not open repository", s->repodir);
|
||||
}
|
||||
|
||||
@ -107,10 +224,11 @@ static int add_revision(struct log_state *s, const char *revstr)
|
||||
|
||||
if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
|
||||
git_oid base;
|
||||
check(git_merge_base(&base, s->repo,
|
||||
check_lg2(git_merge_base(&base, s->repo,
|
||||
git_object_id(revs.from), git_object_id(revs.to)),
|
||||
"Could not find merge base", revstr);
|
||||
check(git_object_lookup(&revs.to, s->repo, &base, GIT_OBJ_COMMIT),
|
||||
check_lg2(
|
||||
git_object_lookup(&revs.to, s->repo, &base, GIT_OBJ_COMMIT),
|
||||
"Could not find merge base commit", NULL);
|
||||
|
||||
push_rev(s, revs.to, hide);
|
||||
@ -122,6 +240,30 @@ static int add_revision(struct log_state *s, const char *revstr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Update revwalker with sorting mode. */
|
||||
static void set_sorting(struct log_state *s, unsigned int sort_mode)
|
||||
{
|
||||
/** Open repo on demand if it isn't already open. */
|
||||
if (!s->repo) {
|
||||
if (!s->repodir) s->repodir = ".";
|
||||
check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
|
||||
"Could not open repository", s->repodir);
|
||||
}
|
||||
|
||||
/** Create revwalker on demand if it doesn't already exist. */
|
||||
if (!s->walker)
|
||||
check_lg2(git_revwalk_new(&s->walker, s->repo),
|
||||
"Could not create revision walker", NULL);
|
||||
|
||||
if (sort_mode == GIT_SORT_REVERSE)
|
||||
s->sorting = s->sorting ^ GIT_SORT_REVERSE;
|
||||
else
|
||||
s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE);
|
||||
|
||||
git_revwalk_sorting(s->walker, s->sorting);
|
||||
}
|
||||
|
||||
/** Helper to format a git_time value like Git. */
|
||||
static void print_time(const git_time *intime, const char *prefix)
|
||||
{
|
||||
char sign, out[32];
|
||||
@ -148,6 +290,7 @@ static void print_time(const git_time *intime, const char *prefix)
|
||||
printf("%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
|
||||
}
|
||||
|
||||
/** Helper to print a commit object. */
|
||||
static void print_commit(git_commit *commit)
|
||||
{
|
||||
char buf[GIT_OID_HEXSZ + 1];
|
||||
@ -182,54 +325,21 @@ static void print_commit(git_commit *commit)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int print_diff(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_hunk *hunk,
|
||||
const git_diff_line *line,
|
||||
void *data)
|
||||
{
|
||||
(void)delta; (void)hunk; (void)data;
|
||||
|
||||
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
|
||||
line->origin == GIT_DIFF_LINE_ADDITION ||
|
||||
line->origin == GIT_DIFF_LINE_DELETION)
|
||||
fputc(line->origin, stdout);
|
||||
|
||||
fwrite(line->content, 1, line->content_len, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_int(int *value, const char *arg, int allow_negative)
|
||||
{
|
||||
char *found;
|
||||
*value = (int)strtol(arg, &found, 10);
|
||||
return (found && *found == '\0' && (allow_negative || *value >= 0));
|
||||
}
|
||||
|
||||
static int match_int_arg(
|
||||
int *value, const char *arg, const char *pfx, int allow_negative)
|
||||
{
|
||||
size_t pfxlen = strlen(pfx);
|
||||
if (strncmp(arg, pfx, pfxlen) != 0)
|
||||
return 0;
|
||||
if (!match_int(value, arg + pfxlen, allow_negative))
|
||||
usage("Invalid value after argument", arg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int match_with_parent(
|
||||
git_commit *commit, int i, git_diff_options *opts)
|
||||
/** Helper to find how many files in a commit changed from its nth parent. */
|
||||
static int match_with_parent(git_commit *commit, int i, git_diff_options *opts)
|
||||
{
|
||||
git_commit *parent;
|
||||
git_tree *a, *b;
|
||||
git_diff *diff;
|
||||
int ndeltas;
|
||||
|
||||
check(git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
|
||||
check(git_commit_tree(&a, parent), "Tree for parent", NULL);
|
||||
check(git_commit_tree(&b, commit), "Tree for commit", NULL);
|
||||
check(git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts),
|
||||
"Checking diff between parent and commit", NULL);
|
||||
check_lg2(
|
||||
git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
|
||||
check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
|
||||
check_lg2(git_commit_tree(&b, commit), "Tree for commit", NULL);
|
||||
check_lg2(
|
||||
git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts),
|
||||
"Checking diff between parent and commit", NULL);
|
||||
|
||||
ndeltas = (int)git_diff_num_deltas(diff);
|
||||
|
||||
@ -241,170 +351,77 @@ static int match_with_parent(
|
||||
return ndeltas > 0;
|
||||
}
|
||||
|
||||
struct log_options {
|
||||
int show_diff;
|
||||
int skip, limit;
|
||||
int min_parents, max_parents;
|
||||
git_time_t before;
|
||||
git_time_t after;
|
||||
char *author;
|
||||
char *committer;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
/** Print a usage message for the program. */
|
||||
static void usage(const char *message, const char *arg)
|
||||
{
|
||||
int i, count = 0, printed = 0, parents;
|
||||
char *a;
|
||||
struct log_state s;
|
||||
struct log_options opt;
|
||||
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_oid oid;
|
||||
git_commit *commit = NULL;
|
||||
git_pathspec *ps = NULL;
|
||||
if (message && arg)
|
||||
fprintf(stderr, "%s: %s\n", message, arg);
|
||||
else if (message)
|
||||
fprintf(stderr, "%s\n", message);
|
||||
fprintf(stderr, "usage: log [<options>]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
git_threads_init();
|
||||
/** Parse some log command line options. */
|
||||
static int parse_options(
|
||||
struct log_state *s, struct log_options *opt, int argc, char **argv)
|
||||
{
|
||||
struct args_info args = ARGS_INFO_INIT;
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.sorting = GIT_SORT_TIME;
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->sorting = GIT_SORT_TIME;
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
opt.max_parents = -1;
|
||||
opt.limit = -1;
|
||||
memset(opt, 0, sizeof(*opt));
|
||||
opt->max_parents = -1;
|
||||
opt->limit = -1;
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
a = argv[i];
|
||||
for (args.pos = 1; args.pos < argc; ++args.pos) {
|
||||
const char *a = argv[args.pos];
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (!add_revision(&s, a))
|
||||
++count;
|
||||
else /* try failed revision parse as filename */
|
||||
if (!add_revision(s, a))
|
||||
s->revisions++;
|
||||
else
|
||||
/** Try failed revision parse as filename. */
|
||||
break;
|
||||
} else if (!strcmp(a, "--")) {
|
||||
++i;
|
||||
++args.pos;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(a, "--date-order"))
|
||||
set_sorting(&s, GIT_SORT_TIME);
|
||||
set_sorting(s, GIT_SORT_TIME);
|
||||
else if (!strcmp(a, "--topo-order"))
|
||||
set_sorting(&s, GIT_SORT_TOPOLOGICAL);
|
||||
set_sorting(s, GIT_SORT_TOPOLOGICAL);
|
||||
else if (!strcmp(a, "--reverse"))
|
||||
set_sorting(&s, GIT_SORT_REVERSE);
|
||||
else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
|
||||
s.repodir = a + strlen("--git-dir=");
|
||||
else if (match_int_arg(&opt.skip, a, "--skip=", 0))
|
||||
/* found valid --skip */;
|
||||
else if (match_int_arg(&opt.limit, a, "--max-count=", 0))
|
||||
/* found valid --max-count */;
|
||||
else if (a[1] >= '0' && a[1] <= '9') {
|
||||
if (!match_int(&opt.limit, a + 1, 0))
|
||||
usage("Invalid limit on number of commits", a);
|
||||
} else if (!strcmp(a, "-n")) {
|
||||
if (i + 1 == argc || !match_int(&opt.limit, argv[i + 1], 0))
|
||||
usage("Argument -n not followed by valid count", argv[i + 1]);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
set_sorting(s, GIT_SORT_REVERSE);
|
||||
else if (match_str_arg(&s->repodir, &args, "--git-dir"))
|
||||
/** Found git-dir. */;
|
||||
else if (match_int_arg(&opt->skip, &args, "--skip", 0))
|
||||
/** Found valid --skip. */;
|
||||
else if (match_int_arg(&opt->limit, &args, "--max-count", 0))
|
||||
/** Found valid --max-count. */;
|
||||
else if (a[1] >= '0' && a[1] <= '9')
|
||||
is_integer(&opt->limit, a + 1, 0);
|
||||
else if (match_int_arg(&opt->limit, &args, "-n", 0))
|
||||
/** Found valid -n. */;
|
||||
else if (!strcmp(a, "--merges"))
|
||||
opt.min_parents = 2;
|
||||
opt->min_parents = 2;
|
||||
else if (!strcmp(a, "--no-merges"))
|
||||
opt.max_parents = 1;
|
||||
opt->max_parents = 1;
|
||||
else if (!strcmp(a, "--no-min-parents"))
|
||||
opt.min_parents = 0;
|
||||
opt->min_parents = 0;
|
||||
else if (!strcmp(a, "--no-max-parents"))
|
||||
opt.max_parents = -1;
|
||||
else if (match_int_arg(&opt.max_parents, a, "--max-parents=", 1))
|
||||
/* found valid --max-parents */;
|
||||
else if (match_int_arg(&opt.min_parents, a, "--min-parents=", 0))
|
||||
/* found valid --min_parents */;
|
||||
opt->max_parents = -1;
|
||||
else if (match_int_arg(&opt->max_parents, &args, "--max-parents=", 1))
|
||||
/** Found valid --max-parents. */;
|
||||
else if (match_int_arg(&opt->min_parents, &args, "--min-parents=", 0))
|
||||
/** Found valid --min_parents. */;
|
||||
else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
|
||||
opt.show_diff = 1;
|
||||
opt->show_diff = 1;
|
||||
else
|
||||
usage("Unsupported argument", a);
|
||||
}
|
||||
|
||||
if (!count)
|
||||
add_revision(&s, NULL);
|
||||
|
||||
diffopts.pathspec.strings = &argv[i];
|
||||
diffopts.pathspec.count = argc - i;
|
||||
if (diffopts.pathspec.count > 0)
|
||||
check(git_pathspec_new(&ps, &diffopts.pathspec),
|
||||
"Building pathspec", NULL);
|
||||
|
||||
printed = count = 0;
|
||||
|
||||
for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) {
|
||||
check(git_commit_lookup(&commit, s.repo, &oid),
|
||||
"Failed to look up commit", NULL);
|
||||
|
||||
parents = (int)git_commit_parentcount(commit);
|
||||
if (parents < opt.min_parents)
|
||||
continue;
|
||||
if (opt.max_parents > 0 && parents > opt.max_parents)
|
||||
continue;
|
||||
|
||||
if (diffopts.pathspec.count > 0) {
|
||||
int unmatched = parents;
|
||||
|
||||
if (parents == 0) {
|
||||
git_tree *tree;
|
||||
check(git_commit_tree(&tree, commit), "Get tree", NULL);
|
||||
if (git_pathspec_match_tree(
|
||||
NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0)
|
||||
unmatched = 1;
|
||||
git_tree_free(tree);
|
||||
} else if (parents == 1) {
|
||||
unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1;
|
||||
} else {
|
||||
for (i = 0; i < parents; ++i) {
|
||||
if (match_with_parent(commit, i, &diffopts))
|
||||
unmatched--;
|
||||
}
|
||||
}
|
||||
|
||||
if (unmatched > 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count++ < opt.skip)
|
||||
continue;
|
||||
if (opt.limit != -1 && printed++ >= opt.limit) {
|
||||
git_commit_free(commit);
|
||||
break;
|
||||
}
|
||||
|
||||
print_commit(commit);
|
||||
|
||||
if (opt.show_diff) {
|
||||
git_tree *a = NULL, *b = NULL;
|
||||
git_diff *diff = NULL;
|
||||
|
||||
if (parents > 1)
|
||||
continue;
|
||||
check(git_commit_tree(&b, commit), "Get tree", NULL);
|
||||
if (parents == 1) {
|
||||
git_commit *parent;
|
||||
check(git_commit_parent(&parent, commit, 0), "Get parent", NULL);
|
||||
check(git_commit_tree(&a, parent), "Tree for parent", NULL);
|
||||
git_commit_free(parent);
|
||||
}
|
||||
|
||||
check(git_diff_tree_to_tree(
|
||||
&diff, git_commit_owner(commit), a, b, &diffopts),
|
||||
"Diff commit with parent", NULL);
|
||||
check(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, print_diff, NULL),
|
||||
"Displaying diff", NULL);
|
||||
|
||||
git_diff_free(diff);
|
||||
git_tree_free(a);
|
||||
git_tree_free(b);
|
||||
}
|
||||
}
|
||||
|
||||
git_pathspec_free(ps);
|
||||
git_revwalk_free(s.walker);
|
||||
git_repository_free(s.repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
return args.pos;
|
||||
}
|
||||
|
||||
|
||||
@ -47,6 +47,11 @@ exit:
|
||||
return &data->ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function gets called for each remote-tracking branch that gets
|
||||
* updated. The message we output depends on whether it's a new one or
|
||||
* an update.
|
||||
*/
|
||||
static int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
|
||||
{
|
||||
char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1];
|
||||
@ -66,6 +71,7 @@ static int update_cb(const char *refname, const git_oid *a, const git_oid *b, vo
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Entry point for this command */
|
||||
int fetch(git_repository *repo, int argc, char **argv)
|
||||
{
|
||||
git_remote *remote = NULL;
|
||||
@ -130,6 +136,11 @@ int fetch(git_repository *repo, int argc, char **argv)
|
||||
pthread_join(worker, NULL);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* If there are local objects (we got a thin pack), then tell
|
||||
* the user how many objects we saved from having to cross the
|
||||
* network.
|
||||
*/
|
||||
if (stats->local_objects > 0) {
|
||||
printf("\rReceived %d/%d objects in %zu bytes (used %d local objects)\n",
|
||||
stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <string.h>
|
||||
#include "common.h"
|
||||
|
||||
/** Callback to show each item */
|
||||
static int show_ref__cb(git_remote_head *head, void *payload)
|
||||
{
|
||||
char oid[GIT_OID_HEXSZ + 1] = {0};
|
||||
@ -28,6 +29,10 @@ static int use_remote(git_repository *repo, char *name)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the remote and call the printing function for
|
||||
* each of the remote references.
|
||||
*/
|
||||
callbacks.credentials = cred_acquire_cb;
|
||||
git_remote_set_callbacks(remote, &callbacks);
|
||||
|
||||
@ -42,9 +47,7 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
// This gets called to do the work. The remote can be given either as
|
||||
// the name of a configured remote or an URL.
|
||||
|
||||
/** Entry point for this command */
|
||||
int ls_remote(git_repository *repo, int argc, char **argv)
|
||||
{
|
||||
int error;
|
||||
|
||||
@ -1,17 +1,35 @@
|
||||
#include <stdio.h>
|
||||
#include <string.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 "common.h"
|
||||
|
||||
static void check_error(int error_code, const char *action)
|
||||
static int revwalk_parseopts(git_repository *repo, git_revwalk *walk, int nopts, char **opts);
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
if (!error_code)
|
||||
return;
|
||||
git_repository *repo;
|
||||
git_revwalk *walk;
|
||||
git_oid oid;
|
||||
char buf[41];
|
||||
|
||||
const git_error *error = giterr_last();
|
||||
fprintf(stderr, "Error %d %s: %s\n", -error_code, action,
|
||||
(error && error->message) ? error->message : "???");
|
||||
exit(1);
|
||||
git_threads_init();
|
||||
|
||||
check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "opening repository", NULL);
|
||||
check_lg2(git_revwalk_new(&walk, repo), "allocating revwalk", NULL);
|
||||
check_lg2(revwalk_parseopts(repo, walk, argc-1, argv+1), "parsing options", NULL);
|
||||
|
||||
while (!git_revwalk_next(&oid, walk)) {
|
||||
git_oid_fmt(buf, &oid);
|
||||
buf[40] = '\0';
|
||||
printf("%s\n", buf);
|
||||
}
|
||||
|
||||
git_threads_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int push_commit(git_revwalk *walk, const git_oid *oid, int hide)
|
||||
@ -93,27 +111,3 @@ static int revwalk_parseopts(git_repository *repo, git_revwalk *walk, int nopts,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
int error;
|
||||
git_repository *repo;
|
||||
git_revwalk *walk;
|
||||
git_oid oid;
|
||||
char buf[41];
|
||||
|
||||
error = git_repository_open_ext(&repo, ".", 0, NULL);
|
||||
check_error(error, "opening repository");
|
||||
|
||||
error = git_revwalk_new(&walk, repo);
|
||||
check_error(error, "allocating revwalk");
|
||||
error = revwalk_parseopts(repo, walk, argc-1, argv+1);
|
||||
check_error(error, "parsing options");
|
||||
|
||||
while (!git_revwalk_next(&oid, walk)) {
|
||||
git_oid_fmt(buf, &oid);
|
||||
buf[40] = '\0';
|
||||
printf("%s\n", buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,17 +1,36 @@
|
||||
#include <stdio.h>
|
||||
#include <git2.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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.
|
||||
*/
|
||||
|
||||
static void check(int error, const char *message, const char *arg)
|
||||
#include "common.h"
|
||||
|
||||
/** Forward declarations for helpers. */
|
||||
struct parse_state {
|
||||
git_repository *repo;
|
||||
const char *repodir;
|
||||
const char *spec;
|
||||
int not;
|
||||
};
|
||||
static void parse_opts(struct parse_state *ps, int argc, char *argv[]);
|
||||
static int parse_revision(struct parse_state *ps);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (!error)
|
||||
return;
|
||||
if (arg)
|
||||
fprintf(stderr, "%s %s (%d)\n", message, arg, error);
|
||||
else
|
||||
fprintf(stderr, "%s(%d)\n", message, error);
|
||||
exit(1);
|
||||
struct parse_state ps = {0};
|
||||
|
||||
git_threads_init();
|
||||
parse_opts(&ps, argc, argv);
|
||||
|
||||
check_lg2(parse_revision(&ps), "Parsing", NULL);
|
||||
|
||||
git_repository_free(ps.repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(const char *message, const char *arg)
|
||||
@ -24,13 +43,25 @@ static void usage(const char *message, const char *arg)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct parse_state {
|
||||
git_repository *repo;
|
||||
const char *repodir;
|
||||
int not;
|
||||
};
|
||||
static void parse_opts(struct parse_state *ps, int argc, char *argv[])
|
||||
{
|
||||
struct args_info args = ARGS_INFO_INIT;
|
||||
|
||||
static int parse_revision(struct parse_state *ps, const char *revstr)
|
||||
for (args.pos=1; args.pos < argc; ++args.pos) {
|
||||
const char *a = argv[args.pos];
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (ps->spec)
|
||||
usage("Too many specs", a);
|
||||
ps->spec = a;
|
||||
} else if (!strcmp(a, "--not"))
|
||||
ps->not = !ps->not;
|
||||
else if (!match_str_arg(&ps->repodir, &args, "--git-dir"))
|
||||
usage("Cannot handle argument", a);
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_revision(struct parse_state *ps)
|
||||
{
|
||||
git_revspec rs;
|
||||
char str[GIT_OID_HEXSZ + 1];
|
||||
@ -38,11 +69,11 @@ static int parse_revision(struct parse_state *ps, const char *revstr)
|
||||
if (!ps->repo) {
|
||||
if (!ps->repodir)
|
||||
ps->repodir = ".";
|
||||
check(git_repository_open_ext(&ps->repo, ps->repodir, 0, NULL),
|
||||
check_lg2(git_repository_open_ext(&ps->repo, ps->repodir, 0, NULL),
|
||||
"Could not open repository from", ps->repodir);
|
||||
}
|
||||
|
||||
check(git_revparse(&rs, ps->repo, revstr), "Could not parse", revstr);
|
||||
check_lg2(git_revparse(&rs, ps->repo, ps->spec), "Could not parse", ps->spec);
|
||||
|
||||
if ((rs.flags & GIT_REVPARSE_SINGLE) != 0) {
|
||||
git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
|
||||
@ -56,9 +87,9 @@ static int parse_revision(struct parse_state *ps, const char *revstr)
|
||||
|
||||
if ((rs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
|
||||
git_oid base;
|
||||
check(git_merge_base(&base, ps->repo,
|
||||
git_object_id(rs.from), git_object_id(rs.to)),
|
||||
"Could not find merge base", revstr);
|
||||
check_lg2(git_merge_base(&base, ps->repo,
|
||||
git_object_id(rs.from), git_object_id(rs.to)),
|
||||
"Could not find merge base", ps->spec);
|
||||
|
||||
git_oid_tostr(str, sizeof(str), &base);
|
||||
printf("%s\n", str);
|
||||
@ -69,38 +100,9 @@ static int parse_revision(struct parse_state *ps, const char *revstr)
|
||||
git_object_free(rs.from);
|
||||
}
|
||||
else {
|
||||
check(0, "Invalid results from git_revparse", revstr);
|
||||
fatal("Invalid results from git_revparse", ps->spec);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
char *a;
|
||||
struct parse_state ps;
|
||||
|
||||
git_threads_init();
|
||||
|
||||
memset(&ps, 0, sizeof(ps));
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
a = argv[i];
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (parse_revision(&ps, a) != 0)
|
||||
break;
|
||||
} else if (!strcmp(a, "--not"))
|
||||
ps.not = !ps.not;
|
||||
else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
|
||||
ps.repodir = a + strlen("--git-dir=");
|
||||
else
|
||||
usage("Cannot handle argument", a);
|
||||
}
|
||||
|
||||
git_repository_free(ps.repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
#include <git2.h>
|
||||
#include <stdio.h>
|
||||
#include <string.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 "common.h"
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
git_repository *repo = NULL;
|
||||
git_index *index;
|
||||
unsigned int i, ecount;
|
||||
char *dir = ".";
|
||||
@ -14,28 +18,19 @@ int main (int argc, char** argv)
|
||||
|
||||
git_threads_init();
|
||||
|
||||
if (argc > 2)
|
||||
fatal("usage: showindex [<repo-dir>]", NULL);
|
||||
if (argc > 1)
|
||||
dir = argv[1];
|
||||
if (!dir || argc > 2) {
|
||||
fprintf(stderr, "usage: showindex [<repo-dir>]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dirlen = strlen(dir);
|
||||
if (dirlen > 5 && strcmp(dir + dirlen - 5, "index") == 0) {
|
||||
if (git_index_open(&index, dir) < 0) {
|
||||
fprintf(stderr, "could not open index: %s\n", dir);
|
||||
return 1;
|
||||
}
|
||||
check_lg2(git_index_open(&index, dir), "could not open index", dir);
|
||||
} else {
|
||||
if (git_repository_open_ext(&repo, dir, 0, NULL) < 0) {
|
||||
fprintf(stderr, "could not open repository: %s\n", dir);
|
||||
return 1;
|
||||
}
|
||||
if (git_repository_index(&index, repo) < 0) {
|
||||
fprintf(stderr, "could not open repository index\n");
|
||||
return 1;
|
||||
}
|
||||
git_repository *repo;
|
||||
check_lg2(git_repository_open_ext(&repo, dir, 0, NULL), "could not open repository", dir);
|
||||
check_lg2(git_repository_index(&index, repo), "could not open repository index", NULL);
|
||||
git_repository_free(repo);
|
||||
}
|
||||
|
||||
git_index_read(index);
|
||||
@ -62,10 +57,7 @@ int main (int argc, char** argv)
|
||||
}
|
||||
|
||||
git_index_free(index);
|
||||
git_repository_free(repo);
|
||||
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -4,30 +4,22 @@
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum {
|
||||
FORMAT_DEFAULT = 0,
|
||||
FORMAT_LONG = 1,
|
||||
FORMAT_SHORT = 2,
|
||||
FORMAT_PORCELAIN = 3,
|
||||
};
|
||||
#define MAX_PATHSPEC 8
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
* This example demonstrates the use of the libgit2 status APIs,
|
||||
* particularly the `git_status_list` object, to roughly simulate the
|
||||
* output of running `git status`. It serves as a simple example of
|
||||
* using those APIs to get basic status information.
|
||||
*
|
||||
* This does not have:
|
||||
*
|
||||
* - Robust error handling
|
||||
* - Colorized or paginated output formatting
|
||||
*
|
||||
* This does have:
|
||||
*
|
||||
* - Examples of translating command line arguments to the status
|
||||
* options settings to mimic `git status` results.
|
||||
* - A sample status formatter that matches the default "long" format
|
||||
@ -35,32 +27,83 @@ enum {
|
||||
* - A sample status formatter that matches the "short" format
|
||||
*/
|
||||
|
||||
static void check(int error, const char *message, const char *extra)
|
||||
enum {
|
||||
FORMAT_DEFAULT = 0,
|
||||
FORMAT_LONG = 1,
|
||||
FORMAT_SHORT = 2,
|
||||
FORMAT_PORCELAIN = 3,
|
||||
};
|
||||
|
||||
#define MAX_PATHSPEC 8
|
||||
|
||||
struct opts {
|
||||
git_status_options statusopt;
|
||||
char *repodir;
|
||||
char *pathspec[MAX_PATHSPEC];
|
||||
int npaths;
|
||||
int format;
|
||||
int zterm;
|
||||
int showbranch;
|
||||
};
|
||||
|
||||
static void parse_opts(struct opts *o, int argc, char *argv[]);
|
||||
static void show_branch(git_repository *repo, int format);
|
||||
static void print_long(git_repository *repo, git_status_list *status);
|
||||
static void print_short(git_repository *repo, git_status_list *status);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const git_error *lg2err;
|
||||
const char *lg2msg = "", *lg2spacer = "";
|
||||
git_repository *repo = NULL;
|
||||
git_status_list *status;
|
||||
struct opts o = { GIT_STATUS_OPTIONS_INIT, "." };
|
||||
|
||||
if (!error)
|
||||
return;
|
||||
git_threads_init();
|
||||
|
||||
if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
|
||||
lg2msg = lg2err->message;
|
||||
lg2spacer = " - ";
|
||||
}
|
||||
o.statusopt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
o.statusopt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
|
||||
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
|
||||
|
||||
if (extra)
|
||||
fprintf(stderr, "%s '%s' [%d]%s%s\n",
|
||||
message, extra, error, lg2spacer, lg2msg);
|
||||
parse_opts(&o, argc, argv);
|
||||
|
||||
/**
|
||||
* Try to open the repository at the given path (or at the current
|
||||
* directory if none was given).
|
||||
*/
|
||||
check_lg2(git_repository_open_ext(&repo, o.repodir, 0, NULL),
|
||||
"Could not open repository", o.repodir);
|
||||
|
||||
if (git_repository_is_bare(repo))
|
||||
fatal("Cannot report status on bare repository",
|
||||
git_repository_path(repo));
|
||||
|
||||
/**
|
||||
* Run status on the repository
|
||||
*
|
||||
* Because we want to simluate a full "git status" run and want to
|
||||
* support some command line options, we use `git_status_foreach_ext()`
|
||||
* instead of just the plain status call. This allows (a) iterating
|
||||
* over the index and then the workdir and (b) extra flags that control
|
||||
* which files are included. If you just want simple status (e.g. to
|
||||
* enumerate files that are modified) then you probably don't need the
|
||||
* extended API.
|
||||
*/
|
||||
check_lg2(git_status_list_new(&status, repo, &o.statusopt),
|
||||
"Could not get status", NULL);
|
||||
|
||||
if (o.showbranch)
|
||||
show_branch(repo, o.format);
|
||||
|
||||
if (o.format == FORMAT_LONG)
|
||||
print_long(repo, status);
|
||||
else
|
||||
fprintf(stderr, "%s [%d]%s%s\n",
|
||||
message, error, lg2spacer, lg2msg);
|
||||
print_short(repo, status);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
git_status_list_free(status);
|
||||
git_repository_free(repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
static void fail(const char *message)
|
||||
{
|
||||
check(-1, message, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void show_branch(git_repository *repo, int format)
|
||||
@ -78,7 +121,7 @@ static void show_branch(git_repository *repo, int format)
|
||||
if (!strncmp(branch, "refs/heads/", strlen("refs/heads/")))
|
||||
branch += strlen("refs/heads/");
|
||||
} else
|
||||
check(error, "failed to get current branch", NULL);
|
||||
check_lg2(error, "failed to get current branch", NULL);
|
||||
|
||||
if (format == FORMAT_LONG)
|
||||
printf("# On branch %s\n",
|
||||
@ -99,7 +142,7 @@ static void print_long(git_repository *repo, git_status_list *status)
|
||||
|
||||
(void)repo;
|
||||
|
||||
/* print index changes */
|
||||
/** Print index changes. */
|
||||
|
||||
for (i = 0; i < maxi; ++i) {
|
||||
char *istatus = NULL;
|
||||
@ -148,7 +191,7 @@ static void print_long(git_repository *repo, git_status_list *status)
|
||||
}
|
||||
header = 0;
|
||||
|
||||
/* print workdir changes to tracked files */
|
||||
/** Print workdir changes to tracked files. */
|
||||
|
||||
for (i = 0; i < maxi; ++i) {
|
||||
char *wstatus = NULL;
|
||||
@ -193,7 +236,7 @@ static void print_long(git_repository *repo, git_status_list *status)
|
||||
}
|
||||
header = 0;
|
||||
|
||||
/* print untracked files */
|
||||
/** Print untracked files. */
|
||||
|
||||
header = 0;
|
||||
|
||||
@ -215,7 +258,7 @@ static void print_long(git_repository *repo, git_status_list *status)
|
||||
|
||||
header = 0;
|
||||
|
||||
/* print ignored files */
|
||||
/** Print ignored files. */
|
||||
|
||||
for (i = 0; i < maxi; ++i) {
|
||||
s = git_status_byindex(status, i);
|
||||
@ -341,103 +384,58 @@ static void print_short(git_repository *repo, git_status_list *status)
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
static void parse_opts(struct opts *o, int argc, char *argv[])
|
||||
{
|
||||
git_repository *repo = NULL;
|
||||
int i, npaths = 0, format = FORMAT_DEFAULT, zterm = 0, showbranch = 0;
|
||||
git_status_options opt = GIT_STATUS_OPTIONS_INIT;
|
||||
git_status_list *status;
|
||||
char *repodir = ".", *pathspec[MAX_PATHSPEC];
|
||||
struct args_info args = ARGS_INFO_INIT;
|
||||
|
||||
opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
|
||||
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
|
||||
for (args.pos = 1; args.pos < argc; ++args.pos) {
|
||||
char *a = argv[args.pos];
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (argv[i][0] != '-') {
|
||||
if (npaths < MAX_PATHSPEC)
|
||||
pathspec[npaths++] = argv[i];
|
||||
if (a[0] != '-') {
|
||||
if (o->npaths < MAX_PATHSPEC)
|
||||
o->pathspec[o->npaths++] = a;
|
||||
else
|
||||
fail("Example only supports a limited pathspec");
|
||||
fatal("Example only supports a limited pathspec", NULL);
|
||||
}
|
||||
else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--short"))
|
||||
format = FORMAT_SHORT;
|
||||
else if (!strcmp(argv[i], "--long"))
|
||||
format = FORMAT_LONG;
|
||||
else if (!strcmp(argv[i], "--porcelain"))
|
||||
format = FORMAT_PORCELAIN;
|
||||
else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch"))
|
||||
showbranch = 1;
|
||||
else if (!strcmp(argv[i], "-z")) {
|
||||
zterm = 1;
|
||||
if (format == FORMAT_DEFAULT)
|
||||
format = FORMAT_PORCELAIN;
|
||||
else if (!strcmp(a, "-s") || !strcmp(a, "--short"))
|
||||
o->format = FORMAT_SHORT;
|
||||
else if (!strcmp(a, "--long"))
|
||||
o->format = FORMAT_LONG;
|
||||
else if (!strcmp(a, "--porcelain"))
|
||||
o->format = FORMAT_PORCELAIN;
|
||||
else if (!strcmp(a, "-b") || !strcmp(a, "--branch"))
|
||||
o->showbranch = 1;
|
||||
else if (!strcmp(a, "-z")) {
|
||||
o->zterm = 1;
|
||||
if (o->format == FORMAT_DEFAULT)
|
||||
o->format = FORMAT_PORCELAIN;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--ignored"))
|
||||
opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
|
||||
else if (!strcmp(argv[i], "-uno") ||
|
||||
!strcmp(argv[i], "--untracked-files=no"))
|
||||
opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||
else if (!strcmp(argv[i], "-unormal") ||
|
||||
!strcmp(argv[i], "--untracked-files=normal"))
|
||||
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||
else if (!strcmp(argv[i], "-uall") ||
|
||||
!strcmp(argv[i], "--untracked-files=all"))
|
||||
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||
else if (!strcmp(a, "--ignored"))
|
||||
o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
|
||||
else if (!strcmp(a, "-uno") ||
|
||||
!strcmp(a, "--untracked-files=no"))
|
||||
o->statusopt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||
else if (!strcmp(a, "-unormal") ||
|
||||
!strcmp(a, "--untracked-files=normal"))
|
||||
o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||
else if (!strcmp(a, "-uall") ||
|
||||
!strcmp(a, "--untracked-files=all"))
|
||||
o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
||||
else if (!strcmp(argv[i], "--ignore-submodules=all"))
|
||||
opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
|
||||
else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir=")))
|
||||
repodir = argv[i] + strlen("--git-dir=");
|
||||
else if (!strcmp(a, "--ignore-submodules=all"))
|
||||
o->statusopt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
|
||||
else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
|
||||
o->repodir = a + strlen("--git-dir=");
|
||||
else
|
||||
check(-1, "Unsupported option", argv[i]);
|
||||
check_lg2(-1, "Unsupported option", a);
|
||||
}
|
||||
|
||||
if (format == FORMAT_DEFAULT)
|
||||
format = FORMAT_LONG;
|
||||
if (format == FORMAT_LONG)
|
||||
showbranch = 1;
|
||||
if (npaths > 0) {
|
||||
opt.pathspec.strings = pathspec;
|
||||
opt.pathspec.count = npaths;
|
||||
if (o->format == FORMAT_DEFAULT)
|
||||
o->format = FORMAT_LONG;
|
||||
if (o->format == FORMAT_LONG)
|
||||
o->showbranch = 1;
|
||||
if (o->npaths > 0) {
|
||||
o->statusopt.pathspec.strings = o->pathspec;
|
||||
o->statusopt.pathspec.count = o->npaths;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to open the repository at the given path (or at the current
|
||||
* directory if none was given).
|
||||
*/
|
||||
check(git_repository_open_ext(&repo, repodir, 0, NULL),
|
||||
"Could not open repository", repodir);
|
||||
|
||||
if (git_repository_is_bare(repo))
|
||||
fail("Cannot report status on bare repository");
|
||||
|
||||
/*
|
||||
* Run status on the repository
|
||||
*
|
||||
* Because we want to simluate a full "git status" run and want to
|
||||
* support some command line options, we use `git_status_foreach_ext()`
|
||||
* instead of just the plain status call. This allows (a) iterating
|
||||
* over the index and then the workdir and (b) extra flags that control
|
||||
* which files are included. If you just want simple status (e.g. to
|
||||
* enumerate files that are modified) then you probably don't need the
|
||||
* extended API.
|
||||
*/
|
||||
check(git_status_list_new(&status, repo, &opt),
|
||||
"Could not get status", NULL);
|
||||
|
||||
if (showbranch)
|
||||
show_branch(repo, format);
|
||||
|
||||
if (format == FORMAT_LONG)
|
||||
print_long(repo, status);
|
||||
else
|
||||
print_short(repo, status);
|
||||
|
||||
git_status_list_free(status);
|
||||
git_repository_free(repo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user