summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorBen Straub <bs@github.com>2013-09-16 16:12:31 -0700
committerBen Straub <bs@github.com>2013-09-16 16:12:31 -0700
commit549931679a77b280eb1f36afeda8930eb31d70f7 (patch)
tree2744e3e198ad146bae72f35369bbeb4f8028c8c3 /examples
parent1a68c168a6cdbe0db6e44fb582a7026a7d536c9d (diff)
parent8821c9aa5baf31e21c21825e8c91c765e6631e7f (diff)
downloadlibgit2-549931679a77b280eb1f36afeda8930eb31d70f7.tar.gz
Merge branch 'development' into blame_rebased
Conflicts: include/git2.h
Diffstat (limited to 'examples')
-rw-r--r--examples/CMakeLists.txt14
-rw-r--r--examples/Makefile2
-rw-r--r--examples/add.c143
-rw-r--r--examples/init.c245
-rw-r--r--examples/log.c406
-rw-r--r--examples/network/Makefile3
-rw-r--r--examples/network/clone.c33
-rw-r--r--examples/network/common.c34
-rw-r--r--examples/network/common.h6
-rw-r--r--examples/network/fetch.c1
-rw-r--r--examples/network/ls-remote.c41
-rw-r--r--examples/rev-parse.c106
-rw-r--r--examples/status.c443
13 files changed, 1410 insertions, 67 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 000000000..c20a6df3b
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,14 @@
+FILE(GLOB_RECURSE SRC_EXAMPLE_GIT2 network/*.c network/*.h)
+ADD_EXECUTABLE(cgit2 ${SRC_EXAMPLE_GIT2})
+IF(WIN32 OR ANDROID)
+ TARGET_LINK_LIBRARIES(cgit2 git2)
+ELSE()
+ TARGET_LINK_LIBRARIES(cgit2 git2 pthread)
+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)
+ENDFOREACH()
diff --git a/examples/Makefile b/examples/Makefile
index c5d555566..d53ed8241 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -3,7 +3,7 @@
CC = gcc
CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
LFLAGS = -L../build -lgit2 -lz
-APPS = general showindex diff rev-list cat-file
+APPS = general showindex diff rev-list cat-file status log rev-parse init
all: $(APPS)
diff --git a/examples/add.c b/examples/add.c
new file mode 100644
index 000000000..a0edf4376
--- /dev/null
+++ b/examples/add.c
@@ -0,0 +1,143 @@
+#include <git2.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+enum print_options {
+ SKIP = 1,
+ VERBOSE = 2,
+ UPDATE = 4,
+};
+
+struct print_payload {
+ enum print_options options;
+ git_repository *repo;
+};
+
+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;
+}
+
+int print_matched_cb(const char *path, const char *matched_pathspec, void *payload)
+{
+ struct print_payload p = *(struct print_payload*)(payload);
+ int ret;
+ git_status_t status;
+ (void)matched_pathspec;
+
+ if (git_status_file(&status, p.repo, path)) {
+ return -1; //abort
+ }
+
+ if (status & GIT_STATUS_WT_MODIFIED ||
+ status & GIT_STATUS_WT_NEW) {
+ printf("add '%s'\n", path);
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+
+ if(p.options & SKIP) {
+ ret = 1;
+ }
+
+ return ret;
+}
+
+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");
+}
+
+
+int main (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};
+
+ for (i = 1; i < argc; ++i) {
+ if (argv[i][0] != '-') {
+ break;
+ }
+ else if(!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-v")) {
+ options |= VERBOSE;
+ }
+ else if(!strcmp(argv[i], "--dry-run") || !strcmp(argv[i], "-n")) {
+ options |= SKIP;
+ }
+ else if(!strcmp(argv[i], "--update") || !strcmp(argv[i], "-u")) {
+ options |= UPDATE;
+ }
+ else if(!strcmp(argv[i], "-h")) {
+ print_usage();
+ break;
+ }
+ else if(!strcmp(argv[i], "--")) {
+ i++;
+ break;
+ }
+ else {
+ fprintf(stderr, "Unsupported option %s.\n", argv[i]);
+ print_usage();
+ return 1;
+ }
+ }
+
+ 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;
+}
diff --git a/examples/init.c b/examples/init.c
new file mode 100644
index 000000000..4a379c6e3
--- /dev/null
+++ b/examples/init.c
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ *
+ * This demonstrates using the libgit2 APIs to initialize a new repository.
+ *
+ * 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>
+
+/* not actually good error handling */
+static void fail(const char *msg, const char *arg)
+{
+ if (arg)
+ 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);
+
+
+int main(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;
+
+ git_threads_init();
+
+ /* Process arguments */
+
+ for (i = 1; i < argc; ++i) {
+ char *a = argv[i];
+
+ if (a[0] == '-')
+ no_options = 0;
+
+ 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...
+ */
+
+ 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);
+}
diff --git a/examples/log.c b/examples/log.c
new file mode 100644
index 000000000..413c211e4
--- /dev/null
+++ b/examples/log.c
@@ -0,0 +1,406 @@
+#include <stdio.h>
+#include <git2.h>
+#include <stdlib.h>
+#include <string.h>
+
+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);
+}
+
+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);
+}
+
+struct log_state {
+ git_repository *repo;
+ const char *repodir;
+ git_revwalk *walker;
+ int hide;
+ int sorting;
+};
+
+static void set_sorting(struct log_state *s, unsigned int sort_mode)
+{
+ 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);
+ }
+
+ if (!s->walker)
+ check(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);
+}
+
+static void push_rev(struct log_state *s, git_object *obj, int hide)
+{
+ hide = s->hide ^ hide;
+
+ if (!s->walker) {
+ check(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),
+ "Could not find repository HEAD", NULL);
+ else if (hide)
+ check(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)),
+ "Reference does not refer to a commit", NULL);
+
+ git_object_free(obj);
+}
+
+static int add_revision(struct log_state *s, const char *revstr)
+{
+ git_revspec revs;
+ int hide = 0;
+
+ 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);
+ }
+
+ if (!revstr) {
+ push_rev(s, NULL, hide);
+ return 0;
+ }
+
+ if (*revstr == '^') {
+ revs.flags = GIT_REVPARSE_SINGLE;
+ hide = !hide;
+
+ if (git_revparse_single(&revs.from, s->repo, revstr + 1) < 0)
+ return -1;
+ } else if (git_revparse(&revs, s->repo, revstr) < 0)
+ return -1;
+
+ if ((revs.flags & GIT_REVPARSE_SINGLE) != 0)
+ push_rev(s, revs.from, hide);
+ else {
+ push_rev(s, revs.to, hide);
+
+ if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
+ git_oid base;
+ check(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),
+ "Could not find merge base commit", NULL);
+
+ push_rev(s, revs.to, hide);
+ }
+
+ push_rev(s, revs.from, !hide);
+ }
+
+ return 0;
+}
+
+static void print_time(const git_time *intime, const char *prefix)
+{
+ char sign, out[32];
+ struct tm intm;
+ int offset, hours, minutes;
+ time_t t;
+
+ offset = intime->offset;
+ if (offset < 0) {
+ sign = '-';
+ offset = -offset;
+ } else {
+ sign = '+';
+ }
+
+ hours = offset / 60;
+ minutes = offset % 60;
+
+ t = (time_t)intime->time + (intime->offset * 60);
+
+ gmtime_r(&t, &intm);
+ strftime(out, sizeof(out), "%a %b %e %T %Y", &intm);
+
+ printf("%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
+}
+
+static void print_commit(git_commit *commit)
+{
+ char buf[GIT_OID_HEXSZ + 1];
+ int i, count;
+ const git_signature *sig;
+ const char *scan, *eol;
+
+ git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
+ printf("commit %s\n", buf);
+
+ if ((count = (int)git_commit_parentcount(commit)) > 1) {
+ printf("Merge:");
+ for (i = 0; i < count; ++i) {
+ git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
+ printf(" %s", buf);
+ }
+ printf("\n");
+ }
+
+ if ((sig = git_commit_author(commit)) != NULL) {
+ printf("Author: %s <%s>\n", sig->name, sig->email);
+ print_time(&sig->when, "Date: ");
+ }
+ printf("\n");
+
+ for (scan = git_commit_message(commit); scan && *scan; ) {
+ for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */;
+
+ printf(" %.*s\n", (int)(eol - scan), scan);
+ scan = *eol ? eol + 1 : NULL;
+ }
+ printf("\n");
+}
+
+static int print_diff(
+ const git_diff_delta *delta,
+ const git_diff_range *range,
+ char usage,
+ const char *line,
+ size_t line_len,
+ void *data)
+{
+ (void)delta; (void)range; (void)usage; (void)line_len; (void)data;
+ fputs(line, 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_tree *a, *b;
+ git_diff_list *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);
+
+ ndeltas = (int)git_diff_num_deltas(diff);
+
+ git_diff_list_free(diff);
+ git_tree_free(a);
+ git_tree_free(b);
+ git_commit_free(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[])
+{
+ 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;
+
+ git_threads_init();
+
+ memset(&s, 0, sizeof(s));
+ s.sorting = GIT_SORT_TIME;
+
+ memset(&opt, 0, sizeof(opt));
+ opt.max_parents = -1;
+ opt.limit = -1;
+
+ for (i = 1; i < argc; ++i) {
+ a = argv[i];
+
+ if (a[0] != '-') {
+ if (!add_revision(&s, a))
+ ++count;
+ else /* try failed revision parse as filename */
+ break;
+ } else if (!strcmp(a, "--")) {
+ ++i;
+ break;
+ }
+ else if (!strcmp(a, "--date-order"))
+ set_sorting(&s, GIT_SORT_TIME);
+ else if (!strcmp(a, "--topo-order"))
+ 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;
+ }
+ else if (!strcmp(a, "--merges"))
+ opt.min_parents = 2;
+ else if (!strcmp(a, "--no-merges"))
+ opt.max_parents = 1;
+ else if (!strcmp(a, "--no-min-parents"))
+ 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 */;
+ else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
+ 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_list *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_patch(diff, print_diff, NULL),
+ "Displaying diff", NULL);
+
+ git_diff_list_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;
+}
diff --git a/examples/network/Makefile b/examples/network/Makefile
index 810eb705b..f65c6cb26 100644
--- a/examples/network/Makefile
+++ b/examples/network/Makefile
@@ -11,7 +11,8 @@ OBJECTS = \
ls-remote.o \
fetch.o \
clone.o \
- index-pack.o
+ index-pack.o \
+ common.o
all: $(OBJECTS)
$(CC) $(CFLAGS) $(LDFLAGS) -o git2 $(OBJECTS) $(LIBRARIES)
diff --git a/examples/network/clone.c b/examples/network/clone.c
index 00c25c1ae..a09a94728 100644
--- a/examples/network/clone.c
+++ b/examples/network/clone.c
@@ -9,19 +9,6 @@
# include <unistd.h>
#endif
-/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
- * with permission of the original author, Martin Pool.
- * http://sourcefrog.net/weblog/software/languages/C/unused.html
- */
-#ifdef UNUSED
-#elif defined(__GNUC__)
-# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
-#elif defined(__LCLINT__)
-# define UNUSED(x) /*@unused@*/ x
-#else
-# define UNUSED(x) x
-#endif
-
typedef struct progress_data {
git_transfer_progress fetch_progress;
size_t completed_steps;
@@ -63,24 +50,6 @@ static void checkout_progress(const char *path, size_t cur, size_t tot, void *pa
print_progress(pd);
}
-static int cred_acquire(git_cred **out,
- const char * UNUSED(url),
- const char * UNUSED(username_from_url),
- unsigned int UNUSED(allowed_types),
- void * UNUSED(payload))
-{
- char username[128] = {0};
- char password[128] = {0};
-
- printf("Username: ");
- scanf("%s", username);
-
- /* Yup. Right there on your terminal. Careful where you copy/paste output. */
- printf("Password: ");
- scanf("%s", password);
-
- return git_cred_userpass_plaintext_new(out, username, password);
-}
int do_clone(git_repository *repo, int argc, char **argv)
{
@@ -107,7 +76,7 @@ int do_clone(git_repository *repo, int argc, char **argv)
clone_opts.checkout_opts = checkout_opts;
clone_opts.fetch_progress_cb = &fetch_progress;
clone_opts.fetch_progress_payload = &pd;
- clone_opts.cred_acquire_cb = cred_acquire;
+ clone_opts.cred_acquire_cb = cred_acquire_cb;
// Do the clone
error = git_clone(&cloned_repo, url, path, &clone_opts);
diff --git a/examples/network/common.c b/examples/network/common.c
new file mode 100644
index 000000000..d123eedbd
--- /dev/null
+++ b/examples/network/common.c
@@ -0,0 +1,34 @@
+#include "common.h"
+#include <stdio.h>
+
+/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
+ * with permission of the original author, Martin Pool.
+ * http://sourcefrog.net/weblog/software/languages/C/unused.html
+ */
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
+
+int cred_acquire_cb(git_cred **out,
+ const char * UNUSED(url),
+ const char * UNUSED(username_from_url),
+ unsigned int UNUSED(allowed_types),
+ void * UNUSED(payload))
+{
+ char username[128] = {0};
+ char password[128] = {0};
+
+ printf("Username: ");
+ scanf("%s", username);
+
+ /* Yup. Right there on your terminal. Careful where you copy/paste output. */
+ printf("Password: ");
+ scanf("%s", password);
+
+ return git_cred_userpass_plaintext_new(out, username, password);
+}
diff --git a/examples/network/common.h b/examples/network/common.h
index a4cfa1a7e..1b09caad4 100644
--- a/examples/network/common.h
+++ b/examples/network/common.h
@@ -12,6 +12,12 @@ int fetch(git_repository *repo, int argc, char **argv);
int index_pack(git_repository *repo, int argc, char **argv);
int do_clone(git_repository *repo, int argc, char **argv);
+int cred_acquire_cb(git_cred **out,
+ const char * url,
+ const char * username_from_url,
+ unsigned int allowed_types,
+ void *payload);
+
#ifndef PRIuZ
/* Define the printf format specifer to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index 6020ec6ec..ce016ce0b 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -92,6 +92,7 @@ int fetch(git_repository *repo, int argc, char **argv)
callbacks.update_tips = &update_cb;
callbacks.progress = &progress_cb;
git_remote_set_callbacks(remote, &callbacks);
+ git_remote_set_cred_acquire_cb(remote, &cred_acquire_cb, NULL);
// Set up the information for the background worker thread
data.remote = remote;
diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c
index 252011828..b22ac47a0 100644
--- a/examples/network/ls-remote.c
+++ b/examples/network/ls-remote.c
@@ -14,31 +14,6 @@ static int show_ref__cb(git_remote_head *head, void *payload)
return 0;
}
-static int use_unnamed(git_repository *repo, const char *url)
-{
- git_remote *remote = NULL;
- int error;
-
- // Create an instance of a remote from the URL. The transport to use
- // is detected from the URL
- error = git_remote_create_inmemory(&remote, repo, NULL, url);
- if (error < 0)
- goto cleanup;
-
- // When connecting, the underlying code needs to know wether we
- // want to push or fetch
- error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
- if (error < 0)
- goto cleanup;
-
- // With git_remote_ls we can retrieve the advertised heads
- error = git_remote_ls(remote, &show_ref__cb, NULL);
-
-cleanup:
- git_remote_free(remote);
- return error;
-}
-
static int use_remote(git_repository *repo, char *name)
{
git_remote *remote = NULL;
@@ -46,8 +21,13 @@ static int use_remote(git_repository *repo, char *name)
// Find the remote by name
error = git_remote_load(&remote, repo, name);
- if (error < 0)
- goto cleanup;
+ if (error < 0) {
+ error = git_remote_create_inmemory(&remote, repo, NULL, name);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ git_remote_set_cred_acquire_cb(remote, &cred_acquire_cb, NULL);
error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
if (error < 0)
@@ -72,12 +52,7 @@ int ls_remote(git_repository *repo, int argc, char **argv)
return EXIT_FAILURE;
}
- /* If there's a ':' in the name, assume it's an URL */
- if (strchr(argv[1], ':') != NULL) {
- error = use_unnamed(repo, argv[1]);
- } else {
- error = use_remote(repo, argv[1]);
- }
+ error = use_remote(repo, argv[1]);
return error;
}
diff --git a/examples/rev-parse.c b/examples/rev-parse.c
new file mode 100644
index 000000000..cdbb61e46
--- /dev/null
+++ b/examples/rev-parse.c
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <git2.h>
+#include <stdlib.h>
+#include <string.h>
+
+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);
+}
+
+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: rev-parse [ --option ] <args>...\n");
+ exit(1);
+}
+
+struct parse_state {
+ git_repository *repo;
+ const char *repodir;
+ int not;
+};
+
+static int parse_revision(struct parse_state *ps, const char *revstr)
+{
+ git_revspec rs;
+ char str[GIT_OID_HEXSZ + 1];
+
+ if (!ps->repo) {
+ if (!ps->repodir)
+ ps->repodir = ".";
+ check(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);
+
+ if ((rs.flags & GIT_REVPARSE_SINGLE) != 0) {
+ git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
+ printf("%s\n", str);
+ git_object_free(rs.from);
+ }
+ else if ((rs.flags & GIT_REVPARSE_RANGE) != 0) {
+ git_oid_tostr(str, sizeof(str), git_object_id(rs.to));
+ printf("%s\n", str);
+ git_object_free(rs.to);
+
+ 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);
+
+ git_oid_tostr(str, sizeof(str), &base);
+ printf("%s\n", str);
+ }
+
+ git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
+ printf("^%s\n", str);
+ git_object_free(rs.from);
+ }
+ else {
+ check(0, "Invalid results from git_revparse", revstr);
+ }
+
+ 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;
+}
diff --git a/examples/status.c b/examples/status.c
new file mode 100644
index 000000000..689098415
--- /dev/null
+++ b/examples/status.c
@@ -0,0 +1,443 @@
+/*
+ * 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 <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
+
+/*
+ * 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
+ * from `git status`
+ * - A sample status formatter that matches the "short" format
+ */
+
+static void check(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);
+}
+
+static void fail(const char *message)
+{
+ check(-1, message, NULL);
+}
+
+static void show_branch(git_repository *repo, int format)
+{
+ int error = 0;
+ const char *branch = NULL;
+ git_reference *head = NULL;
+
+ error = git_repository_head(&head, repo);
+
+ if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
+ branch = NULL;
+ else if (!error) {
+ branch = git_reference_name(head);
+ if (!strncmp(branch, "refs/heads/", strlen("refs/heads/")))
+ branch += strlen("refs/heads/");
+ } else
+ check(error, "failed to get current branch", NULL);
+
+ if (format == FORMAT_LONG)
+ printf("# On branch %s\n",
+ branch ? branch : "Not currently on any branch.");
+ else
+ printf("## %s\n", branch ? branch : "HEAD (no branch)");
+
+ git_reference_free(head);
+}
+
+static void print_long(git_repository *repo, git_status_list *status)
+{
+ size_t i, maxi = git_status_list_entrycount(status);
+ const git_status_entry *s;
+ int header = 0, changes_in_index = 0;
+ int changed_in_workdir = 0, rm_in_workdir = 0;
+ const char *old_path, *new_path;
+
+ (void)repo;
+
+ /* print index changes */
+
+ for (i = 0; i < maxi; ++i) {
+ char *istatus = NULL;
+
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_CURRENT)
+ continue;
+
+ if (s->status & GIT_STATUS_WT_DELETED)
+ rm_in_workdir = 1;
+
+ if (s->status & GIT_STATUS_INDEX_NEW)
+ istatus = "new file: ";
+ if (s->status & GIT_STATUS_INDEX_MODIFIED)
+ istatus = "modified: ";
+ if (s->status & GIT_STATUS_INDEX_DELETED)
+ istatus = "deleted: ";
+ if (s->status & GIT_STATUS_INDEX_RENAMED)
+ istatus = "renamed: ";
+ if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
+ istatus = "typechange:";
+
+ if (istatus == NULL)
+ continue;
+
+ if (!header) {
+ printf("# Changes to be committed:\n");
+ printf("# (use \"git reset HEAD <file>...\" to unstage)\n");
+ printf("#\n");
+ header = 1;
+ }
+
+ old_path = s->head_to_index->old_file.path;
+ new_path = s->head_to_index->new_file.path;
+
+ if (old_path && new_path && strcmp(old_path, new_path))
+ printf("#\t%s %s -> %s\n", istatus, old_path, new_path);
+ else
+ printf("#\t%s %s\n", istatus, old_path ? old_path : new_path);
+ }
+
+ if (header) {
+ changes_in_index = 1;
+ printf("#\n");
+ }
+ header = 0;
+
+ /* print workdir changes to tracked files */
+
+ for (i = 0; i < maxi; ++i) {
+ char *wstatus = NULL;
+
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL)
+ continue;
+
+ if (s->status & GIT_STATUS_WT_MODIFIED)
+ wstatus = "modified: ";
+ if (s->status & GIT_STATUS_WT_DELETED)
+ wstatus = "deleted: ";
+ if (s->status & GIT_STATUS_WT_RENAMED)
+ wstatus = "renamed: ";
+ if (s->status & GIT_STATUS_WT_TYPECHANGE)
+ wstatus = "typechange:";
+
+ if (wstatus == NULL)
+ continue;
+
+ if (!header) {
+ printf("# Changes not staged for commit:\n");
+ printf("# (use \"git add%s <file>...\" to update what will be committed)\n", rm_in_workdir ? "/rm" : "");
+ printf("# (use \"git checkout -- <file>...\" to discard changes in working directory)\n");
+ printf("#\n");
+ header = 1;
+ }
+
+ old_path = s->index_to_workdir->old_file.path;
+ new_path = s->index_to_workdir->new_file.path;
+
+ if (old_path && new_path && strcmp(old_path, new_path))
+ printf("#\t%s %s -> %s\n", wstatus, old_path, new_path);
+ else
+ printf("#\t%s %s\n", wstatus, old_path ? old_path : new_path);
+ }
+
+ if (header) {
+ changed_in_workdir = 1;
+ printf("#\n");
+ }
+ header = 0;
+
+ /* print untracked files */
+
+ header = 0;
+
+ for (i = 0; i < maxi; ++i) {
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_WT_NEW) {
+
+ if (!header) {
+ printf("# Untracked files:\n");
+ printf("# (use \"git add <file>...\" to include in what will be committed)\n");
+ printf("#\n");
+ header = 1;
+ }
+
+ printf("#\t%s\n", s->index_to_workdir->old_file.path);
+ }
+ }
+
+ header = 0;
+
+ /* print ignored files */
+
+ for (i = 0; i < maxi; ++i) {
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_IGNORED) {
+
+ if (!header) {
+ printf("# Ignored files:\n");
+ printf("# (use \"git add -f <file>...\" to include in what will be committed)\n");
+ printf("#\n");
+ header = 1;
+ }
+
+ printf("#\t%s\n", s->index_to_workdir->old_file.path);
+ }
+ }
+
+ if (!changes_in_index && changed_in_workdir)
+ printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
+}
+
+static void print_short(git_repository *repo, git_status_list *status)
+{
+ size_t i, maxi = git_status_list_entrycount(status);
+ const git_status_entry *s;
+ char istatus, wstatus;
+ const char *extra, *a, *b, *c;
+
+ for (i = 0; i < maxi; ++i) {
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_CURRENT)
+ continue;
+
+ a = b = c = NULL;
+ istatus = wstatus = ' ';
+ extra = "";
+
+ if (s->status & GIT_STATUS_INDEX_NEW)
+ istatus = 'A';
+ if (s->status & GIT_STATUS_INDEX_MODIFIED)
+ istatus = 'M';
+ if (s->status & GIT_STATUS_INDEX_DELETED)
+ istatus = 'D';
+ if (s->status & GIT_STATUS_INDEX_RENAMED)
+ istatus = 'R';
+ if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
+ istatus = 'T';
+
+ if (s->status & GIT_STATUS_WT_NEW) {
+ if (istatus == ' ')
+ istatus = '?';
+ wstatus = '?';
+ }
+ if (s->status & GIT_STATUS_WT_MODIFIED)
+ wstatus = 'M';
+ if (s->status & GIT_STATUS_WT_DELETED)
+ wstatus = 'D';
+ if (s->status & GIT_STATUS_WT_RENAMED)
+ wstatus = 'R';
+ if (s->status & GIT_STATUS_WT_TYPECHANGE)
+ wstatus = 'T';
+
+ if (s->status & GIT_STATUS_IGNORED) {
+ istatus = '!';
+ wstatus = '!';
+ }
+
+ if (istatus == '?' && wstatus == '?')
+ continue;
+
+ if (s->index_to_workdir &&
+ s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
+ {
+ git_submodule *sm = NULL;
+ unsigned int smstatus = 0;
+
+ if (!git_submodule_lookup(
+ &sm, repo, s->index_to_workdir->new_file.path) &&
+ !git_submodule_status(&smstatus, sm))
+ {
+ if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
+ extra = " (new commits)";
+ else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
+ extra = " (modified content)";
+ else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
+ extra = " (modified content)";
+ else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
+ extra = " (untracked content)";
+ }
+ }
+
+ if (s->head_to_index) {
+ a = s->head_to_index->old_file.path;
+ b = s->head_to_index->new_file.path;
+ }
+ if (s->index_to_workdir) {
+ if (!a)
+ a = s->index_to_workdir->old_file.path;
+ if (!b)
+ b = s->index_to_workdir->old_file.path;
+ c = s->index_to_workdir->new_file.path;
+ }
+
+ if (istatus == 'R') {
+ if (wstatus == 'R')
+ printf("%c%c %s %s %s%s\n", istatus, wstatus, a, b, c, extra);
+ else
+ printf("%c%c %s %s%s\n", istatus, wstatus, a, b, extra);
+ } else {
+ if (wstatus == 'R')
+ printf("%c%c %s %s%s\n", istatus, wstatus, a, c, extra);
+ else
+ printf("%c%c %s%s\n", istatus, wstatus, a, extra);
+ }
+ }
+
+ for (i = 0; i < maxi; ++i) {
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_WT_NEW)
+ printf("?? %s\n", s->index_to_workdir->old_file.path);
+ }
+}
+
+int main(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];
+
+ 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 (i = 1; i < argc; ++i) {
+ if (argv[i][0] != '-') {
+ if (npaths < MAX_PATHSPEC)
+ pathspec[npaths++] = argv[i];
+ else
+ fail("Example only supports a limited pathspec");
+ }
+ 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(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 |
+ 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
+ check(-1, "Unsupported option", argv[i]);
+ }
+
+ if (format == FORMAT_DEFAULT)
+ format = FORMAT_LONG;
+ if (format == FORMAT_LONG)
+ showbranch = 1;
+ if (npaths > 0) {
+ opt.pathspec.strings = pathspec;
+ opt.pathspec.count = 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;
+}
+