Commit 567649f2 by Vicent Martí

Merge pull request #1916 from libgit2/simplify-examples

Fix examples to make the important stuff more obvious
parents 948f00b4 4f62d559
...@@ -9,6 +9,8 @@ ENDIF() ...@@ -9,6 +9,8 @@ ENDIF()
FILE(GLOB SRC_EXAMPLE_APPS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c) FILE(GLOB SRC_EXAMPLE_APPS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c)
FOREACH(src_app ${SRC_EXAMPLE_APPS}) FOREACH(src_app ${SRC_EXAMPLE_APPS})
STRING(REPLACE ".c" "" app_name ${src_app}) STRING(REPLACE ".c" "" app_name ${src_app})
ADD_EXECUTABLE(${app_name} ${src_app}) IF(NOT ${app_name} STREQUAL "common")
ADD_EXECUTABLE(${app_name} ${src_app} "common.c")
TARGET_LINK_LIBRARIES(${app_name} git2) TARGET_LINK_LIBRARIES(${app_name} git2)
ENDIF()
ENDFOREACH() ENDFOREACH()
...@@ -8,7 +8,7 @@ APPS = general showindex diff rev-list cat-file status log rev-parse init ...@@ -8,7 +8,7 @@ APPS = general showindex diff rev-list cat-file status log rev-parse init
all: $(APPS) all: $(APPS)
% : %.c % : %.c
$(CC) -o $@ $(CFLAGS) $< $(LFLAGS) $(CC) -o $@ common.c $(CFLAGS) $< $(LFLAGS)
clean: clean:
$(RM) $(APPS) $(RM) $(APPS)
......
libgit2 examples libgit2 examples
================ ================
These examples are meant as thin, easy-to-read snippets for Docurium These examples are a mixture of basic emulation of core Git command line
(https://github.com/github/docurium) rather than full-blown functions and simple snippets demonstrating libgit2 API usage (for use
implementations of Git commands. They are not vetted as carefully with Docurium). As a whole, they are not vetted carefully for bugs, error
for bugs, error handling, or cross-platform compatibility as the handling, and cross-platform compatibility in the same manner as the rest
rest of the code in libgit2, so copy with some caution. 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
#include <git2.h> /*
#include <stdio.h> * Copyright (C) the libgit2 contributors. All rights reserved.
#include <string.h> *
* 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> #include <assert.h>
enum print_options { enum print_options {
...@@ -14,19 +19,49 @@ struct print_payload { ...@@ -14,19 +19,49 @@ struct print_payload {
git_repository *repo; 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; git_threads_init();
array->strings = malloc(sizeof(char*) * array->count);
assert(array->strings!=NULL);
for(i=0; i<array->count; i++) { parse_opts(&options, &count, argc, argv);
array->strings[i]=argv[i];
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) 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 ...@@ -55,36 +90,46 @@ int print_matched_cb(const char *path, const char *matched_pathspec, void *paylo
return ret; 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) void print_usage(void)
{ {
fprintf(stderr, "usage: add [options] [--] file-spec [file-spec] [...]\n\n"); fprintf(stderr, "usage: add [options] [--] file-spec [file-spec] [...]\n\n");
fprintf(stderr, "\t-n, --dry-run dry run\n"); fprintf(stderr, "\t-n, --dry-run dry run\n");
fprintf(stderr, "\t-v, --verbose be verbose\n"); fprintf(stderr, "\t-v, --verbose be verbose\n");
fprintf(stderr, "\t-u, --update update tracked files\n"); fprintf(stderr, "\t-u, --update update tracked files\n");
exit(1);
} }
static void parse_opts(int *options, int *count, int argc, char *argv[])
int main (int argc, char** argv)
{ {
git_index_matched_path_cb matched_cb = NULL; int i;
git_repository *repo = NULL;
git_index *index;
git_strarray array = {0};
int i, options = 0;
struct print_payload payload = {0};
for (i = 1; i < argc; ++i) { for (i = 1; i < argc; ++i) {
if (argv[i][0] != '-') { if (argv[i][0] != '-') {
break; break;
} }
else if(!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-v")) { 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")) { 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")) { else if(!strcmp(argv[i], "--update") || !strcmp(argv[i], "-u")) {
options |= UPDATE; *options |= UPDATE;
} }
else if(!strcmp(argv[i], "-h")) { else if(!strcmp(argv[i], "-h")) {
print_usage(); print_usage();
...@@ -97,47 +142,11 @@ int main (int argc, char** argv) ...@@ -97,47 +142,11 @@ int main (int argc, char** argv)
else { else {
fprintf(stderr, "Unsupported option %s.\n", argv[i]); fprintf(stderr, "Unsupported option %s.\n", argv[i]);
print_usage(); print_usage();
return 1;
} }
} }
if (argc<=i) { if (argc<=i)
print_usage(); print_usage();
return 1;
}
git_threads_init(); *count = i;
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;
} }
#include <stdio.h> /*
#include <git2.h> * Copyright (C) the libgit2 contributors. All rights reserved.
#include <stdlib.h> *
#include <string.h> * 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; #include "common.h"
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;
}
static void print_signature(const char *header, const git_signature *sig) 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) ...@@ -57,12 +31,14 @@ static void print_signature(const char *header, const git_signature *sig)
sign, hours, minutes); sign, hours, minutes);
} }
/** Printing out a blob is simple, get the contents and print */
static void show_blob(const git_blob *blob) static void show_blob(const git_blob *blob)
{ {
/* ? Does this need crlf filtering? */ /* ? Does this need crlf filtering? */
fwrite(git_blob_rawcontent(blob), git_blob_rawsize(blob), 1, stdout); 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) static void show_tree(const git_tree *tree)
{ {
size_t i, max_i = (int)git_tree_entrycount(tree); size_t i, max_i = (int)git_tree_entrycount(tree);
...@@ -81,6 +57,9 @@ static void show_tree(const git_tree *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) static void show_commit(const git_commit *commit)
{ {
unsigned int i, max_i; unsigned int i, max_i;
...@@ -123,53 +102,34 @@ enum { ...@@ -123,53 +102,34 @@ enum {
SHOW_PRETTY = 4 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[]) int main(int argc, char *argv[])
{ {
const char *dir = ".", *rev = NULL; git_repository *repo;
int i, action = 0, verbose = 0; struct opts o = { ".", NULL, 0, 0 };
git_object *obj = NULL; git_object *obj = NULL;
char oidstr[GIT_OID_HEXSZ + 1]; char oidstr[GIT_OID_HEXSZ + 1];
git_threads_init(); git_threads_init();
for (i = 1; i < argc; ++i) { parse_opts(&o, argc, argv);
char *a = argv[i];
if (a[0] != '-') { check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
if (rev != NULL) "Could not open repository", NULL);
usage("Only one rev should be provided", NULL); check_lg2(git_revparse_single(&obj, repo, o.rev),
else "Could not resolve", o.rev);
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);
}
if (!action || !rev) if (o.verbose) {
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) {
char oidstr[GIT_OID_HEXSZ + 1]; char oidstr[GIT_OID_HEXSZ + 1];
git_oid_tostr(oidstr, sizeof(oidstr), git_object_id(obj)); git_oid_tostr(oidstr, sizeof(oidstr), git_object_id(obj));
...@@ -177,7 +137,7 @@ int main(int argc, char *argv[]) ...@@ -177,7 +137,7 @@ int main(int argc, char *argv[])
git_object_type2string(git_object_type(obj)), oidstr); git_object_type2string(git_object_type(obj)), oidstr);
} }
switch (action) { switch (o.action) {
case SHOW_TYPE: case SHOW_TYPE:
printf("%s\n", git_object_type2string(git_object_type(obj))); printf("%s\n", git_object_type2string(git_object_type(obj)));
break; break;
...@@ -185,9 +145,9 @@ int main(int argc, char *argv[]) ...@@ -185,9 +145,9 @@ int main(int argc, char *argv[])
git_odb *odb; git_odb *odb;
git_odb_object *odbobj; git_odb_object *odbobj;
check(git_repository_odb(&odb, g_repo), "Could not open ODB"); check_lg2(git_repository_odb(&odb, repo), "Could not open ODB", NULL);
check(git_odb_read(&odbobj, odb, git_object_id(obj)), check_lg2(git_odb_read(&odbobj, odb, git_object_id(obj)),
"Could not find obj"); "Could not find obj", NULL);
printf("%ld\n", (long)git_odb_object_size(odbobj)); printf("%ld\n", (long)git_odb_object_size(odbobj));
...@@ -221,9 +181,59 @@ int main(int argc, char *argv[]) ...@@ -221,9 +181,59 @@ int main(int argc, char *argv[])
} }
git_object_free(obj); git_object_free(obj);
git_repository_free(g_repo); git_repository_free(repo);
git_threads_shutdown(); git_threads_shutdown();
return 0; 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);
}
/*
* 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);
}
/*
* 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);
#include <stdio.h> /*
#include <git2.h> * Copyright (C) the libgit2 contributors. All rights reserved.
#include <stdlib.h> *
#include <string.h> * 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 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.
*/
static const char *colors[] = {
"\033[m", /* reset */
"\033[1m", /* bold */
"\033[31m", /* red */
"\033[32m", /* green */
"\033[36m" /* cyan */
};
static void check(int error, const char *message) /** The 'opts' struct captures all the various parsed command line options. */
{ struct opts {
if (error) { git_diff_options diffopts;
fprintf(stderr, "%s (%d)\n", message, error); git_diff_find_options findopts;
exit(1); int color;
} int cached;
} git_diff_format_t format;
const char *treeish1;
const char *treeish2;
const char *dir;
};
static int resolve_to_tree( /** These functions are implemented at the end */
git_repository *repo, const char *identifier, git_tree **tree) 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[])
{ {
int err = 0; git_repository *repo = NULL;
git_object *obj = NULL; git_tree *t1 = NULL, *t2 = NULL;
git_diff *diff;
if ((err = git_revparse_single(&obj, repo, identifier)) < 0) struct opts o = {
return err; GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
-1, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
switch (git_object_type(obj)) { };
case GIT_OBJ_TREE:
*tree = (git_tree *)obj; git_threads_init();
break;
case GIT_OBJ_COMMIT: parse_opts(&o, argc, argv);
err = git_commit_tree(tree, (git_commit *)obj);
git_object_free(obj); check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
break; "Could not open repository", o.dir);
default:
err = GIT_ENOTFOUND; /**
* Possible argument patterns:
*
* * &lt;sha1&gt; &lt;sha2&gt;
* * &lt;sha1&gt; --cached
* * &lt;sha1&gt;
* * --cached
* * nothing
*
* Currently ranged arguments like &lt;sha1&gt;..&lt;sha2&gt; and &lt;sha1&gt;...&lt;sha2&gt;
* 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);
return err; 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;
} }
char *colors[] = { static void usage(const char *message, const char *arg)
"\033[m", /* reset */ {
"\033[1m", /* bold */ if (message && arg)
"\033[31m", /* red */ fprintf(stderr, "%s: %s\n", message, arg);
"\033[32m", /* green */ else if (message)
"\033[36m" /* cyan */ fprintf(stderr, "%s\n", message);
}; fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
exit(1);
}
static int printer( /** This implements very rudimentary colorized output. */
static int color_printer(
const git_diff_delta *delta, const git_diff_delta *delta,
const git_diff_hunk *hunk, const git_diff_hunk *hunk,
const git_diff_line *line, const git_diff_line *line,
...@@ -63,6 +163,7 @@ static int printer( ...@@ -63,6 +163,7 @@ static int printer(
case GIT_DIFF_LINE_HUNK_HDR: color = 4; break; case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
default: break; default: break;
} }
if (color != *last_color) { if (color != *last_color) {
if (*last_color == 1 || color == 1) if (*last_color == 1 || color == 1)
fputs(colors[0], stdout); fputs(colors[0], stdout);
...@@ -71,186 +172,79 @@ static int printer( ...@@ -71,186 +172,79 @@ static int printer(
} }
} }
if (line->origin == GIT_DIFF_LINE_CONTEXT || return diff_output(delta, hunk, line, stdout);
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 check_uint16_param(const char *arg, const char *pattern, uint16_t *val)
{
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;
} }
static int check_str_param(const char *arg, const char *pattern, const char **val) /** Parse arguments as copied from git-diff. */
static void parse_opts(struct opts *o, int argc, char *argv[])
{ {
size_t len = strlen(pattern); struct args_info args = ARGS_INFO_INIT;
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) { for (args.pos = 1; args.pos < argc; ++args.pos) {
a = argv[i]; const char *a = argv[args.pos];
if (a[0] != '-') { if (a[0] != '-') {
if (treeish1 == NULL) if (o->treeish1 == NULL)
treeish1 = a; o->treeish1 = a;
else if (treeish2 == NULL) else if (o->treeish2 == NULL)
treeish2 = a; o->treeish2 = a;
else else
usage("Only one or two tree identifiers can be provided", NULL); usage("Only one or two tree identifiers can be provided", NULL);
} }
else if (!strcmp(a, "-p") || !strcmp(a, "-u") || else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
!strcmp(a, "--patch")) !strcmp(a, "--patch"))
format = GIT_DIFF_FORMAT_PATCH; o->format = GIT_DIFF_FORMAT_PATCH;
else if (!strcmp(a, "--cached")) else if (!strcmp(a, "--cached"))
cached = 1; o->cached = 1;
else if (!strcmp(a, "--name-only")) 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")) else if (!strcmp(a, "--name-status"))
format = GIT_DIFF_FORMAT_NAME_STATUS; o->format = GIT_DIFF_FORMAT_NAME_STATUS;
else if (!strcmp(a, "--raw")) else if (!strcmp(a, "--raw"))
format = GIT_DIFF_FORMAT_RAW; o->format = GIT_DIFF_FORMAT_RAW;
else if (!strcmp(a, "--color")) else if (!strcmp(a, "--color"))
color = 0; o->color = 0;
else if (!strcmp(a, "--no-color")) else if (!strcmp(a, "--no-color"))
color = -1; o->color = -1;
else if (!strcmp(a, "-R")) else if (!strcmp(a, "-R"))
opts.flags |= GIT_DIFF_REVERSE; o->diffopts.flags |= GIT_DIFF_REVERSE;
else if (!strcmp(a, "-a") || !strcmp(a, "--text")) 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")) 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")) 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")) 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")) else if (!strcmp(a, "--ignored"))
opts.flags |= GIT_DIFF_INCLUDE_IGNORED; o->diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED;
else if (!strcmp(a, "--untracked")) else if (!strcmp(a, "--untracked"))
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; o->diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
else if (check_uint16_param(a, "-M", &findopts.rename_threshold) || else if (match_uint16_arg(
check_uint16_param(a, "--find-renames", &o->findopts.rename_threshold, &args, "-M") ||
&findopts.rename_threshold)) match_uint16_arg(
findopts.flags |= GIT_DIFF_FIND_RENAMES; &o->findopts.rename_threshold, &args, "--find-renames"))
else if (check_uint16_param(a, "-C", &findopts.copy_threshold) || o->findopts.flags |= GIT_DIFF_FIND_RENAMES;
check_uint16_param(a, "--find-copies", else if (match_uint16_arg(
&findopts.copy_threshold)) &o->findopts.copy_threshold, &args, "-C") ||
findopts.flags |= GIT_DIFF_FIND_COPIES; match_uint16_arg(
&o->findopts.copy_threshold, &args, "--find-copies"))
o->findopts.flags |= GIT_DIFF_FIND_COPIES;
else if (!strcmp(a, "--find-copies-harder")) else if (!strcmp(a, "--find-copies-harder"))
findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED; o->findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
else if (!strncmp(a, "-B", 2) || !strncmp(a, "--break-rewrites", 16)) { else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites"))
/* TODO: parse thresholds */ /* TODO: parse thresholds */
findopts.flags |= GIT_DIFF_FIND_REWRITES; o->findopts.flags |= GIT_DIFF_FIND_REWRITES;
} else if (!match_uint16_arg(
else if (!check_uint16_param(a, "-U", &opts.context_lines) && &o->diffopts.context_lines, &args, "-U") &&
!check_uint16_param(a, "--unified=", &opts.context_lines) && !match_uint16_arg(
!check_uint16_param(a, "--inter-hunk-context=", &o->diffopts.context_lines, &args, "--unified") &&
&opts.interhunk_lines) && !match_uint16_arg(
!check_str_param(a, "--src-prefix=", &opts.old_prefix) && &o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
!check_str_param(a, "--dst-prefix=", &opts.new_prefix) && !match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") &&
!check_str_param(a, "--git-dir=", &dir)) !match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") &&
usage("Unknown arg", a); !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;
} }
/* /*
* 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 * This is a sample program that is similar to "git init". See the
* documentation for that (try "git help init") to understand what this * documentation for that (try "git help init") to understand what this
* program is emulating. * program is emulating.
...@@ -8,179 +17,93 @@ ...@@ -8,179 +17,93 @@
* This also contains a special additional option that regular "git init" * This also contains a special additional option that regular "git init"
* does not support which is "--initial-commit" to make a first empty commit. * does not support which is "--initial-commit" to make a first empty commit.
* That is demonstrated in the "create_initial_commit" helper function. * 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> /** Forward declarations of helpers */
#include <git2.h> struct opts {
#include <stdlib.h> int no_options;
#include <string.h> int quiet;
#include <time.h> int bare;
int initial_commit;
/* not actually good error handling */ uint32_t shared;
static void fail(const char *msg, const char *arg) const char *template;
{ const char *gitdir;
if (arg) const char *dir;
fprintf(stderr, "%s %s\n", msg, arg); };
else
fprintf(stderr, "%s\n", msg);
exit(1);
}
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");
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 */
static uint32_t parse_shared(const char *shared)
{
if (!strcmp(shared, "false") || !strcmp(shared, "umask"))
return GIT_REPOSITORY_INIT_SHARED_UMASK;
else if (!strcmp(shared, "true") || !strcmp(shared, "group"))
return GIT_REPOSITORY_INIT_SHARED_GROUP;
else if (!strcmp(shared, "all") || !strcmp(shared, "world") ||
!strcmp(shared, "everybody"))
return GIT_REPOSITORY_INIT_SHARED_ALL;
else if (shared[0] == '0') {
long val;
char *end = NULL;
val = strtol(shared + 1, &end, 8);
if (end == shared + 1 || *end != 0)
usage("invalid octal value for --shared", shared);
return (uint32_t)val;
}
else
usage("unknown value for --shared", shared);
return 0;
}
/* forward declaration of helper to make an empty parent-less commit */
static void create_initial_commit(git_repository *repo); static void create_initial_commit(git_repository *repo);
static void parse_opts(struct opts *o, int argc, char *argv[]);
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
git_repository *repo = NULL; git_repository *repo = NULL;
int no_options = 1, quiet = 0, bare = 0, initial_commit = 0, i; struct opts o = { 1, 0, 0, 0, GIT_REPOSITORY_INIT_SHARED_UMASK, 0, 0, 0 };
uint32_t shared = GIT_REPOSITORY_INIT_SHARED_UMASK;
const char *template = NULL, *gitdir = NULL, *dir = NULL;
size_t pfxlen;
git_threads_init(); git_threads_init();
/* Process arguments */ parse_opts(&o, argc, argv);
for (i = 1; i < argc; ++i) { /* Initialize repository. */
char *a = argv[i];
if (a[0] == '-') if (o.no_options) {
no_options = 0; /**
* No options were specified, so let's demonstrate the default
if (a[0] != '-') {
if (dir != NULL)
usage("extra argument", a);
dir = a;
}
else if (!strcmp(a, "-q") || !strcmp(a, "--quiet"))
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;
else if (!strcmp(a, "--shared"))
shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
else if ((pfxlen = is_prefixed(a, "--shared=")) > 0)
shared = parse_shared(a + pfxlen);
else if (!strcmp(a, "--initial-commit"))
initial_commit = 1;
else
usage("unknown option", a);
}
if (!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... * simple case of git_repository_init() API usage...
*/ */
check_lg2(git_repository_init(&repo, o.dir, 0),
if (git_repository_init(&repo, dir, 0) < 0) "Could not initialize repository", NULL);
fail("Could not initialize repository", dir);
} }
else { else {
/* Some command line options were specified, so we'll use the /**
* Some command line options were specified, so we'll use the
* extended init API to handle them * extended init API to handle them
*/ */
git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; git_repository_init_options initopts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
initopts.flags = GIT_REPOSITORY_INIT_MKPATH;
if (bare) if (o.bare)
opts.flags |= GIT_REPOSITORY_INIT_BARE; initopts.flags |= GIT_REPOSITORY_INIT_BARE;
if (template) { if (o.template) {
opts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; initopts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
opts.template_path = template; initopts.template_path = o.template;
} }
if (gitdir) { if (o.gitdir) {
/* if you specified a separate git directory, then initialize /**
* If you specified a separate git directory, then initialize
* the repository at that path and use the second path as the * the repository at that path and use the second path as the
* working directory of the repository (with a git-link file) * working directory of the repository (with a git-link file)
*/ */
opts.workdir_path = dir; initopts.workdir_path = o.dir;
dir = gitdir; o.dir = o.gitdir;
} }
if (shared != 0) if (o.shared != 0)
opts.mode = shared; initopts.mode = o.shared;
if (git_repository_init_ext(&repo, dir, &opts) < 0) check_lg2(git_repository_init_ext(&repo, o.dir, &initopts),
fail("Could not initialize repository", dir); "Could not initialize repository", NULL);
} }
/* Print a message to stdout like "git init" does */ /** Print a message to stdout like "git init" does. */
if (!quiet) { if (!o.quiet) {
if (bare || gitdir) if (o.bare || o.gitdir)
dir = git_repository_path(repo); o.dir = git_repository_path(repo);
else else
dir = git_repository_workdir(repo); o.dir = git_repository_workdir(repo);
printf("Initialized empty Git repository in %s\n", dir); printf("Initialized empty Git repository in %s\n", o.dir);
} }
/* As an extension to the basic "git init" command, this example /**
* As an extension to the basic "git init" command, this example
* gives the option to create an empty initial commit. This is * gives the option to create an empty initial commit. This is
* mostly to demonstrate what it takes to do that, but also some * mostly to demonstrate what it takes to do that, but also some
* people like to have that empty base commit in their repo. * people like to have that empty base commit in their repo.
*/ */
if (initial_commit) { if (o.initial_commit) {
create_initial_commit(repo); create_initial_commit(repo);
printf("Created empty initial commit\n"); printf("Created empty initial commit\n");
} }
...@@ -191,7 +114,8 @@ int main(int argc, char *argv[]) ...@@ -191,7 +114,8 @@ int main(int argc, char *argv[])
return 0; return 0;
} }
/* Unlike regular "git init", this example shows how to create an initial /**
* Unlike regular "git init", this example shows how to create an initial
* empty commit in the repository. This is the helper function that does * empty commit in the repository. This is the helper function that does
* that. * that.
*/ */
...@@ -202,31 +126,33 @@ static void create_initial_commit(git_repository *repo) ...@@ -202,31 +126,33 @@ static void create_initial_commit(git_repository *repo)
git_oid tree_id, commit_id; git_oid tree_id, commit_id;
git_tree *tree; git_tree *tree;
/* First use the config to initialize a commit signature for the user */ /** First use the config to initialize a commit signature for the user. */
if (git_signature_default(&sig, repo) < 0) if (git_signature_default(&sig, repo) < 0)
fail("Unable to create a commit signature.", fatal("Unable to create a commit signature.",
"Perhaps 'user.name' and 'user.email' are not set"); "Perhaps 'user.name' and 'user.email' are not set");
/* Now let's create an empty tree for this commit */ /* Now let's create an empty tree for this commit */
if (git_repository_index(&index, repo) < 0) if (git_repository_index(&index, repo) < 0)
fail("Could not open repository index", NULL); fatal("Could not open repository index", NULL);
/* Outside of this example, you could call git_index_add_bypath() /**
* Outside of this example, you could call git_index_add_bypath()
* here to put actual files into the index. For our purposes, we'll * here to put actual files into the index. For our purposes, we'll
* leave it empty for now. * leave it empty for now.
*/ */
if (git_index_write_tree(&tree_id, index) < 0) if (git_index_write_tree(&tree_id, index) < 0)
fail("Unable to write initial tree from index", NULL); fatal("Unable to write initial tree from index", NULL);
git_index_free(index); git_index_free(index);
if (git_tree_lookup(&tree, repo, &tree_id) < 0) if (git_tree_lookup(&tree, repo, &tree_id) < 0)
fail("Could not look up initial tree", NULL); fatal("Could not look up initial tree", NULL);
/* Ready to create the initial commit /**
* Ready to create the initial commit.
* *
* Normally creating a commit would involve looking up the current * Normally creating a commit would involve looking up the current
* HEAD commit and making that be the parent of the initial commit, * HEAD commit and making that be the parent of the initial commit,
...@@ -236,10 +162,85 @@ static void create_initial_commit(git_repository *repo) ...@@ -236,10 +162,85 @@ static void create_initial_commit(git_repository *repo)
if (git_commit_create_v( if (git_commit_create_v(
&commit_id, repo, "HEAD", sig, sig, &commit_id, repo, "HEAD", sig, sig,
NULL, "Initial commit", tree, 0) < 0) NULL, "Initial commit", tree, 0) < 0)
fail("Could not create the initial commit", NULL); fatal("Could not create the initial commit", NULL);
/* Clean up so we don't leak memory */ /** Clean up so we don't leak memory. */
git_tree_free(tree); git_tree_free(tree);
git_signature_free(sig); 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>]\n"
" [--shared[=perms]] [--initial-commit]\n"
" [--separate-git-dir] <directory>\n");
exit(1);
}
/** Parse the tail of the --shared= argument. */
static uint32_t parse_shared(const char *shared)
{
if (!strcmp(shared, "false") || !strcmp(shared, "umask"))
return GIT_REPOSITORY_INIT_SHARED_UMASK;
else if (!strcmp(shared, "true") || !strcmp(shared, "group"))
return GIT_REPOSITORY_INIT_SHARED_GROUP;
else if (!strcmp(shared, "all") || !strcmp(shared, "world") ||
!strcmp(shared, "everybody"))
return GIT_REPOSITORY_INIT_SHARED_ALL;
else if (shared[0] == '0') {
long val;
char *end = NULL;
val = strtol(shared + 1, &end, 8);
if (end == shared + 1 || *end != 0)
usage("invalid octal value for --shared", shared);
return (uint32_t)val;
}
else
usage("unknown value for --shared", shared);
return 0;
}
static void parse_opts(struct opts *o, int argc, char *argv[])
{
struct args_info args = ARGS_INFO_INIT;
const char *sharedarg;
/** Process arguments. */
for (args.pos = 1; args.pos < argc; ++args.pos) {
char *a = argv[args.pos];
if (a[0] == '-')
o->no_options = 0;
if (a[0] != '-') {
if (o->dir != NULL)
usage("extra argument", a);
o->dir = a;
}
else if (!strcmp(a, "-q") || !strcmp(a, "--quiet"))
o->quiet = 1;
else if (!strcmp(a, "--bare"))
o->bare = 1;
else if (!strcmp(a, "--shared"))
o->shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
else if (!strcmp(a, "--initial-commit"))
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 (!o->dir)
usage("must specify directory to init", NULL);
}
#include <stdio.h> /*
#include <git2.h> * Copyright (C) the libgit2 contributors. All rights reserved.
#include <stdlib.h> *
#include <string.h> * 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) #include "common.h"
return;
if (arg) /**
fprintf(stderr, "%s '%s' (%d)\n", message, arg, error); * This example demonstrates the libgit2 rev walker APIs to roughly
else * simulate the output of `git log` and a few of command line arguments.
fprintf(stderr, "%s (%d)\n", message, error); * `git log` has many many options and this only shows a few of them.
exit(1); *
} * This does not have:
*
static void usage(const char *message, const char *arg) * - Robust error handling
{ * - Colorized or paginated output formatting
if (message && arg) * - Most of the `git log` options
fprintf(stderr, "%s: %s\n", message, arg); *
else if (message) * This does have:
fprintf(stderr, "%s\n", message); *
fprintf(stderr, "usage: log [<options>]\n"); * - Examples of translating command line arguments to equivalent libgit2
exit(1); * 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 { struct log_state {
git_repository *repo; git_repository *repo;
const char *repodir; const char *repodir;
git_revwalk *walker; git_revwalk *walker;
int hide; int hide;
int sorting; 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) { int i, count = 0, printed = 0, parents, last_arg;
if (!s->repodir) s->repodir = "."; struct log_state s;
check(git_repository_open_ext(&s->repo, s->repodir, 0, NULL), struct log_options opt;
"Could not open repository", s->repodir); 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 (!s->walker) if (unmatched > 0)
check(git_revwalk_new(&s->walker, s->repo), continue;
"Could not create revision walker", NULL); }
if (sort_mode == GIT_SORT_REVERSE) if (count++ < opt.skip)
s->sorting = s->sorting ^ GIT_SORT_REVERSE; continue;
else if (opt.limit != -1 && printed++ >= opt.limit) {
s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE); git_commit_free(commit);
break;
}
git_revwalk_sorting(s->walker, s->sorting); 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);
}
}
git_pathspec_free(ps);
git_revwalk_free(s.walker);
git_repository_free(s.repo);
git_threads_shutdown();
return 0;
} }
/** Push object (for hide or show) onto revwalker. */
static void push_rev(struct log_state *s, git_object *obj, int hide) static void push_rev(struct log_state *s, git_object *obj, int hide)
{ {
hide = s->hide ^ hide; hide = s->hide ^ hide;
/** Create revwalker on demand if it doesn't already exist. */
if (!s->walker) { 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); "Could not create revision walker", NULL);
git_revwalk_sorting(s->walker, s->sorting); git_revwalk_sorting(s->walker, s->sorting);
} }
if (!obj) if (!obj)
check(git_revwalk_push_head(s->walker), check_lg2(git_revwalk_push_head(s->walker),
"Could not find repository HEAD", NULL); "Could not find repository HEAD", NULL);
else if (hide) 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); "Reference does not refer to a commit", NULL);
else 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); "Reference does not refer to a commit", NULL);
git_object_free(obj); git_object_free(obj);
} }
/** Parse revision string and add revs to walker. */
static int add_revision(struct log_state *s, const char *revstr) static int add_revision(struct log_state *s, const char *revstr)
{ {
git_revspec revs; git_revspec revs;
int hide = 0; int hide = 0;
/** Open repo on demand if it isn't already open. */
if (!s->repo) { if (!s->repo) {
if (!s->repodir) s->repodir = "."; 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); "Could not open repository", s->repodir);
} }
...@@ -107,10 +224,11 @@ static int add_revision(struct log_state *s, const char *revstr) ...@@ -107,10 +224,11 @@ static int add_revision(struct log_state *s, const char *revstr)
if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) { if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
git_oid base; 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)), git_object_id(revs.from), git_object_id(revs.to)),
"Could not find merge base", revstr); "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); "Could not find merge base commit", NULL);
push_rev(s, revs.to, hide); push_rev(s, revs.to, hide);
...@@ -122,6 +240,30 @@ static int add_revision(struct log_state *s, const char *revstr) ...@@ -122,6 +240,30 @@ static int add_revision(struct log_state *s, const char *revstr)
return 0; 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) static void print_time(const git_time *intime, const char *prefix)
{ {
char sign, out[32]; char sign, out[32];
...@@ -148,6 +290,7 @@ static void print_time(const git_time *intime, const char *prefix) ...@@ -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); 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) static void print_commit(git_commit *commit)
{ {
char buf[GIT_OID_HEXSZ + 1]; char buf[GIT_OID_HEXSZ + 1];
...@@ -182,53 +325,20 @@ static void print_commit(git_commit *commit) ...@@ -182,53 +325,20 @@ static void print_commit(git_commit *commit)
printf("\n"); printf("\n");
} }
static int print_diff( /** Helper to find how many files in a commit changed from its nth parent. */
const git_diff_delta *delta, static int match_with_parent(git_commit *commit, int i, git_diff_options *opts)
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)
{ {
git_commit *parent; git_commit *parent;
git_tree *a, *b; git_tree *a, *b;
git_diff *diff; git_diff *diff;
int ndeltas; int ndeltas;
check(git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL); check_lg2(
check(git_commit_tree(&a, parent), "Tree for parent", NULL); git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
check(git_commit_tree(&b, commit), "Tree for commit", NULL); check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
check(git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts), 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); "Checking diff between parent and commit", NULL);
ndeltas = (int)git_diff_num_deltas(diff); ndeltas = (int)git_diff_num_deltas(diff);
...@@ -241,170 +351,77 @@ static int match_with_parent( ...@@ -241,170 +351,77 @@ static int match_with_parent(
return ndeltas > 0; return ndeltas > 0;
} }
struct log_options { /** Print a usage message for the program. */
int show_diff; static void usage(const char *message, const char *arg)
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[])
{ {
int i, count = 0, printed = 0, parents; if (message && arg)
char *a; fprintf(stderr, "%s: %s\n", message, arg);
struct log_state s; else if (message)
struct log_options opt; fprintf(stderr, "%s\n", message);
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; fprintf(stderr, "usage: log [<options>]\n");
git_oid oid; exit(1);
git_commit *commit = NULL; }
git_pathspec *ps = NULL;
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)); memset(s, 0, sizeof(*s));
s.sorting = GIT_SORT_TIME; s->sorting = GIT_SORT_TIME;
memset(&opt, 0, sizeof(opt)); memset(opt, 0, sizeof(*opt));
opt.max_parents = -1; opt->max_parents = -1;
opt.limit = -1; opt->limit = -1;
for (i = 1; i < argc; ++i) { for (args.pos = 1; args.pos < argc; ++args.pos) {
a = argv[i]; const char *a = argv[args.pos];
if (a[0] != '-') { if (a[0] != '-') {
if (!add_revision(&s, a)) if (!add_revision(s, a))
++count; s->revisions++;
else /* try failed revision parse as filename */ else
/** Try failed revision parse as filename. */
break; break;
} else if (!strcmp(a, "--")) { } else if (!strcmp(a, "--")) {
++i; ++args.pos;
break; break;
} }
else if (!strcmp(a, "--date-order")) else if (!strcmp(a, "--date-order"))
set_sorting(&s, GIT_SORT_TIME); set_sorting(s, GIT_SORT_TIME);
else if (!strcmp(a, "--topo-order")) else if (!strcmp(a, "--topo-order"))
set_sorting(&s, GIT_SORT_TOPOLOGICAL); set_sorting(s, GIT_SORT_TOPOLOGICAL);
else if (!strcmp(a, "--reverse")) else if (!strcmp(a, "--reverse"))
set_sorting(&s, GIT_SORT_REVERSE); set_sorting(s, GIT_SORT_REVERSE);
else if (!strncmp(a, "--git-dir=", strlen("--git-dir="))) else if (match_str_arg(&s->repodir, &args, "--git-dir"))
s.repodir = a + strlen("--git-dir="); /** Found git-dir. */;
else if (match_int_arg(&opt.skip, a, "--skip=", 0)) else if (match_int_arg(&opt->skip, &args, "--skip", 0))
/* found valid --skip */; /** Found valid --skip. */;
else if (match_int_arg(&opt.limit, a, "--max-count=", 0)) else if (match_int_arg(&opt->limit, &args, "--max-count", 0))
/* found valid --max-count */; /** Found valid --max-count. */;
else if (a[1] >= '0' && a[1] <= '9') { else if (a[1] >= '0' && a[1] <= '9')
if (!match_int(&opt.limit, a + 1, 0)) is_integer(&opt->limit, a + 1, 0);
usage("Invalid limit on number of commits", a); else if (match_int_arg(&opt->limit, &args, "-n", 0))
} else if (!strcmp(a, "-n")) { /** Found valid -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;
}
else if (!strcmp(a, "--merges")) else if (!strcmp(a, "--merges"))
opt.min_parents = 2; opt->min_parents = 2;
else if (!strcmp(a, "--no-merges")) else if (!strcmp(a, "--no-merges"))
opt.max_parents = 1; opt->max_parents = 1;
else if (!strcmp(a, "--no-min-parents")) else if (!strcmp(a, "--no-min-parents"))
opt.min_parents = 0; opt->min_parents = 0;
else if (!strcmp(a, "--no-max-parents")) else if (!strcmp(a, "--no-max-parents"))
opt.max_parents = -1; opt->max_parents = -1;
else if (match_int_arg(&opt.max_parents, a, "--max-parents=", 1)) else if (match_int_arg(&opt->max_parents, &args, "--max-parents=", 1))
/* found valid --max-parents */; /** Found valid --max-parents. */;
else if (match_int_arg(&opt.min_parents, a, "--min-parents=", 0)) else if (match_int_arg(&opt->min_parents, &args, "--min-parents=", 0))
/* found valid --min_parents */; /** Found valid --min_parents. */;
else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch")) else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
opt.show_diff = 1; opt->show_diff = 1;
else else
usage("Unsupported argument", a); usage("Unsupported argument", a);
} }
if (!count) return args.pos;
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;
} }
...@@ -47,6 +47,11 @@ exit: ...@@ -47,6 +47,11 @@ exit:
return &data->ret; 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) 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]; 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 ...@@ -66,6 +71,7 @@ static int update_cb(const char *refname, const git_oid *a, const git_oid *b, vo
return 0; return 0;
} }
/** Entry point for this command */
int fetch(git_repository *repo, int argc, char **argv) int fetch(git_repository *repo, int argc, char **argv)
{ {
git_remote *remote = NULL; git_remote *remote = NULL;
...@@ -130,6 +136,11 @@ int fetch(git_repository *repo, int argc, char **argv) ...@@ -130,6 +136,11 @@ int fetch(git_repository *repo, int argc, char **argv)
pthread_join(worker, NULL); pthread_join(worker, NULL);
#endif #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) { if (stats->local_objects > 0) {
printf("\rReceived %d/%d objects in %zu bytes (used %d local objects)\n", 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); stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include "common.h" #include "common.h"
/** Callback to show each item */
static int show_ref__cb(git_remote_head *head, void *payload) static int show_ref__cb(git_remote_head *head, void *payload)
{ {
char oid[GIT_OID_HEXSZ + 1] = {0}; char oid[GIT_OID_HEXSZ + 1] = {0};
...@@ -28,6 +29,10 @@ static int use_remote(git_repository *repo, char *name) ...@@ -28,6 +29,10 @@ static int use_remote(git_repository *repo, char *name)
goto cleanup; goto cleanup;
} }
/**
* Connect to the remote and call the printing function for
* each of the remote references.
*/
callbacks.credentials = cred_acquire_cb; callbacks.credentials = cred_acquire_cb;
git_remote_set_callbacks(remote, &callbacks); git_remote_set_callbacks(remote, &callbacks);
...@@ -42,9 +47,7 @@ cleanup: ...@@ -42,9 +47,7 @@ cleanup:
return error; return error;
} }
// This gets called to do the work. The remote can be given either as /** Entry point for this command */
// the name of a configured remote or an URL.
int ls_remote(git_repository *repo, int argc, char **argv) int ls_remote(git_repository *repo, int argc, char **argv)
{ {
int error; int error;
......
#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) git_repository *repo;
return; git_revwalk *walk;
git_oid oid;
char buf[41];
git_threads_init();
const git_error *error = giterr_last(); check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "opening repository", NULL);
fprintf(stderr, "Error %d %s: %s\n", -error_code, action, check_lg2(git_revwalk_new(&walk, repo), "allocating revwalk", NULL);
(error && error->message) ? error->message : "???"); check_lg2(revwalk_parseopts(repo, walk, argc-1, argv+1), "parsing options", NULL);
exit(1);
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) 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, ...@@ -93,27 +111,3 @@ static int revwalk_parseopts(git_repository *repo, git_revwalk *walk, int nopts,
return 0; 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;
}
#include <stdio.h> /*
#include <git2.h> * Copyright (C) the libgit2 contributors. All rights reserved.
#include <stdlib.h> *
#include <string.h> * 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) struct parse_state ps = {0};
return;
if (arg) git_threads_init();
fprintf(stderr, "%s %s (%d)\n", message, arg, error); parse_opts(&ps, argc, argv);
else
fprintf(stderr, "%s(%d)\n", message, error); check_lg2(parse_revision(&ps), "Parsing", NULL);
exit(1);
git_repository_free(ps.repo);
git_threads_shutdown();
return 0;
} }
static void usage(const char *message, const char *arg) static void usage(const char *message, const char *arg)
...@@ -24,13 +43,25 @@ static void usage(const char *message, const char *arg) ...@@ -24,13 +43,25 @@ static void usage(const char *message, const char *arg)
exit(1); exit(1);
} }
struct parse_state { static void parse_opts(struct parse_state *ps, int argc, char *argv[])
git_repository *repo; {
const char *repodir; struct args_info args = ARGS_INFO_INIT;
int not;
};
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; git_revspec rs;
char str[GIT_OID_HEXSZ + 1]; char str[GIT_OID_HEXSZ + 1];
...@@ -38,11 +69,11 @@ static int parse_revision(struct parse_state *ps, const char *revstr) ...@@ -38,11 +69,11 @@ static int parse_revision(struct parse_state *ps, const char *revstr)
if (!ps->repo) { if (!ps->repo) {
if (!ps->repodir) if (!ps->repodir)
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); "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) { if ((rs.flags & GIT_REVPARSE_SINGLE) != 0) {
git_oid_tostr(str, sizeof(str), git_object_id(rs.from)); 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) ...@@ -56,9 +87,9 @@ static int parse_revision(struct parse_state *ps, const char *revstr)
if ((rs.flags & GIT_REVPARSE_MERGE_BASE) != 0) { if ((rs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
git_oid base; git_oid base;
check(git_merge_base(&base, ps->repo, check_lg2(git_merge_base(&base, ps->repo,
git_object_id(rs.from), git_object_id(rs.to)), git_object_id(rs.from), git_object_id(rs.to)),
"Could not find merge base", revstr); "Could not find merge base", ps->spec);
git_oid_tostr(str, sizeof(str), &base); git_oid_tostr(str, sizeof(str), &base);
printf("%s\n", str); printf("%s\n", str);
...@@ -69,38 +100,9 @@ static int parse_revision(struct parse_state *ps, const char *revstr) ...@@ -69,38 +100,9 @@ static int parse_revision(struct parse_state *ps, const char *revstr)
git_object_free(rs.from); git_object_free(rs.from);
} }
else { else {
check(0, "Invalid results from git_revparse", revstr); fatal("Invalid results from git_revparse", ps->spec);
} }
return 0; 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;
}
#include <git2.h> /*
#include <stdio.h> * Copyright (C) the libgit2 contributors. All rights reserved.
#include <string.h> *
* 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) int main (int argc, char** argv)
{ {
git_repository *repo = NULL;
git_index *index; git_index *index;
unsigned int i, ecount; unsigned int i, ecount;
char *dir = "."; char *dir = ".";
...@@ -14,28 +18,19 @@ int main (int argc, char** argv) ...@@ -14,28 +18,19 @@ int main (int argc, char** argv)
git_threads_init(); git_threads_init();
if (argc > 2)
fatal("usage: showindex [<repo-dir>]", NULL);
if (argc > 1) if (argc > 1)
dir = argv[1]; dir = argv[1];
if (!dir || argc > 2) {
fprintf(stderr, "usage: showindex [<repo-dir>]\n");
return 1;
}
dirlen = strlen(dir); dirlen = strlen(dir);
if (dirlen > 5 && strcmp(dir + dirlen - 5, "index") == 0) { if (dirlen > 5 && strcmp(dir + dirlen - 5, "index") == 0) {
if (git_index_open(&index, dir) < 0) { check_lg2(git_index_open(&index, dir), "could not open index", dir);
fprintf(stderr, "could not open index: %s\n", dir);
return 1;
}
} else { } else {
if (git_repository_open_ext(&repo, dir, 0, NULL) < 0) { git_repository *repo;
fprintf(stderr, "could not open repository: %s\n", dir); check_lg2(git_repository_open_ext(&repo, dir, 0, NULL), "could not open repository", dir);
return 1; check_lg2(git_repository_index(&index, repo), "could not open repository index", NULL);
} git_repository_free(repo);
if (git_repository_index(&index, repo) < 0) {
fprintf(stderr, "could not open repository index\n");
return 1;
}
} }
git_index_read(index); git_index_read(index);
...@@ -62,10 +57,7 @@ int main (int argc, char** argv) ...@@ -62,10 +57,7 @@ int main (int argc, char** argv)
} }
git_index_free(index); git_index_free(index);
git_repository_free(repo);
git_threads_shutdown(); git_threads_shutdown();
return 0; return 0;
} }
...@@ -4,30 +4,22 @@ ...@@ -4,30 +4,22 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with * This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
#include <git2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { #include "common.h"
FORMAT_DEFAULT = 0,
FORMAT_LONG = 1,
FORMAT_SHORT = 2,
FORMAT_PORCELAIN = 3,
};
#define MAX_PATHSPEC 8
/* /**
* This example demonstrates the use of the libgit2 status APIs, * This example demonstrates the use of the libgit2 status APIs,
* particularly the `git_status_list` object, to roughly simulate the * particularly the `git_status_list` object, to roughly simulate the
* output of running `git status`. It serves as a simple example of * output of running `git status`. It serves as a simple example of
* using those APIs to get basic status information. * using those APIs to get basic status information.
* *
* This does not have: * This does not have:
*
* - Robust error handling * - Robust error handling
* - Colorized or paginated output formatting * - Colorized or paginated output formatting
* *
* This does have: * This does have:
*
* - Examples of translating command line arguments to the status * - Examples of translating command line arguments to the status
* options settings to mimic `git status` results. * options settings to mimic `git status` results.
* - A sample status formatter that matches the default "long" format * - A sample status formatter that matches the default "long" format
...@@ -35,32 +27,83 @@ enum { ...@@ -35,32 +27,83 @@ enum {
* - A sample status formatter that matches the "short" format * - 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; git_repository *repo = NULL;
const char *lg2msg = "", *lg2spacer = ""; git_status_list *status;
struct opts o = { GIT_STATUS_OPTIONS_INIT, "." };
if (!error) git_threads_init();
return;
if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) { o.statusopt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
lg2msg = lg2err->message; o.statusopt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
lg2spacer = " - "; GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
} GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
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));
if (extra) /**
fprintf(stderr, "%s '%s' [%d]%s%s\n", * Run status on the repository
message, extra, error, lg2spacer, lg2msg); *
* 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 else
fprintf(stderr, "%s [%d]%s%s\n", print_short(repo, status);
message, error, lg2spacer, lg2msg);
exit(1); git_status_list_free(status);
} git_repository_free(repo);
git_threads_shutdown();
static void fail(const char *message) return 0;
{
check(-1, message, NULL);
} }
static void show_branch(git_repository *repo, int format) static void show_branch(git_repository *repo, int format)
...@@ -78,7 +121,7 @@ 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/"))) if (!strncmp(branch, "refs/heads/", strlen("refs/heads/")))
branch += strlen("refs/heads/"); branch += strlen("refs/heads/");
} else } else
check(error, "failed to get current branch", NULL); check_lg2(error, "failed to get current branch", NULL);
if (format == FORMAT_LONG) if (format == FORMAT_LONG)
printf("# On branch %s\n", printf("# On branch %s\n",
...@@ -99,7 +142,7 @@ static void print_long(git_repository *repo, git_status_list *status) ...@@ -99,7 +142,7 @@ static void print_long(git_repository *repo, git_status_list *status)
(void)repo; (void)repo;
/* print index changes */ /** Print index changes. */
for (i = 0; i < maxi; ++i) { for (i = 0; i < maxi; ++i) {
char *istatus = NULL; char *istatus = NULL;
...@@ -148,7 +191,7 @@ static void print_long(git_repository *repo, git_status_list *status) ...@@ -148,7 +191,7 @@ static void print_long(git_repository *repo, git_status_list *status)
} }
header = 0; header = 0;
/* print workdir changes to tracked files */ /** Print workdir changes to tracked files. */
for (i = 0; i < maxi; ++i) { for (i = 0; i < maxi; ++i) {
char *wstatus = NULL; char *wstatus = NULL;
...@@ -193,7 +236,7 @@ static void print_long(git_repository *repo, git_status_list *status) ...@@ -193,7 +236,7 @@ static void print_long(git_repository *repo, git_status_list *status)
} }
header = 0; header = 0;
/* print untracked files */ /** Print untracked files. */
header = 0; header = 0;
...@@ -215,7 +258,7 @@ static void print_long(git_repository *repo, git_status_list *status) ...@@ -215,7 +258,7 @@ static void print_long(git_repository *repo, git_status_list *status)
header = 0; header = 0;
/* print ignored files */ /** Print ignored files. */
for (i = 0; i < maxi; ++i) { for (i = 0; i < maxi; ++i) {
s = git_status_byindex(status, i); s = git_status_byindex(status, i);
...@@ -341,103 +384,58 @@ static void print_short(git_repository *repo, git_status_list *status) ...@@ -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; struct args_info args = ARGS_INFO_INIT;
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];
opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; for (args.pos = 1; args.pos < argc; ++args.pos) {
opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | char *a = argv[args.pos];
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
for (i = 1; i < argc; ++i) { if (a[0] != '-') {
if (argv[i][0] != '-') { if (o->npaths < MAX_PATHSPEC)
if (npaths < MAX_PATHSPEC) o->pathspec[o->npaths++] = a;
pathspec[npaths++] = argv[i];
else 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")) else if (!strcmp(a, "-s") || !strcmp(a, "--short"))
format = FORMAT_SHORT; o->format = FORMAT_SHORT;
else if (!strcmp(argv[i], "--long")) else if (!strcmp(a, "--long"))
format = FORMAT_LONG; o->format = FORMAT_LONG;
else if (!strcmp(argv[i], "--porcelain")) else if (!strcmp(a, "--porcelain"))
format = FORMAT_PORCELAIN; o->format = FORMAT_PORCELAIN;
else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch")) else if (!strcmp(a, "-b") || !strcmp(a, "--branch"))
showbranch = 1; o->showbranch = 1;
else if (!strcmp(argv[i], "-z")) { else if (!strcmp(a, "-z")) {
zterm = 1; o->zterm = 1;
if (format == FORMAT_DEFAULT) if (o->format == FORMAT_DEFAULT)
format = FORMAT_PORCELAIN; o->format = FORMAT_PORCELAIN;
} }
else if (!strcmp(argv[i], "--ignored")) else if (!strcmp(a, "--ignored"))
opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED; o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
else if (!strcmp(argv[i], "-uno") || else if (!strcmp(a, "-uno") ||
!strcmp(argv[i], "--untracked-files=no")) !strcmp(a, "--untracked-files=no"))
opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED; o->statusopt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
else if (!strcmp(argv[i], "-unormal") || else if (!strcmp(a, "-unormal") ||
!strcmp(argv[i], "--untracked-files=normal")) !strcmp(a, "--untracked-files=normal"))
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
else if (!strcmp(argv[i], "-uall") || else if (!strcmp(a, "-uall") ||
!strcmp(argv[i], "--untracked-files=all")) !strcmp(a, "--untracked-files=all"))
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED | o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
else if (!strcmp(argv[i], "--ignore-submodules=all")) else if (!strcmp(a, "--ignore-submodules=all"))
opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES; o->statusopt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir="))) else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
repodir = argv[i] + strlen("--git-dir="); o->repodir = a + strlen("--git-dir=");
else else
check(-1, "Unsupported option", argv[i]); check_lg2(-1, "Unsupported option", a);
} }
if (format == FORMAT_DEFAULT) if (o->format == FORMAT_DEFAULT)
format = FORMAT_LONG; o->format = FORMAT_LONG;
if (format == FORMAT_LONG) if (o->format == FORMAT_LONG)
showbranch = 1; o->showbranch = 1;
if (npaths > 0) { if (o->npaths > 0) {
opt.pathspec.strings = pathspec; o->statusopt.pathspec.strings = o->pathspec;
opt.pathspec.count = npaths; 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;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment