summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2013-06-20 11:39:31 -0700
committerRussell Belfer <rb@github.com>2013-06-20 11:39:31 -0700
commitcf300bb9e50c65e4140f7e204243b34f2898fa95 (patch)
treee3df94b8f56f09ccbce75ff36a051b344eca5af7
parent852ded96982ae70acb63c3940fae08ea29e40fee (diff)
downloadlibgit2-cf300bb9e50c65e4140f7e204243b34f2898fa95.tar.gz
Initial implementation of status example
-rw-r--r--examples/Makefile2
-rw-r--r--examples/status.c294
2 files changed, 295 insertions, 1 deletions
diff --git a/examples/Makefile b/examples/Makefile
index c5d555566..140cc4da9 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
all: $(APPS)
diff --git a/examples/status.c b/examples/status.c
new file mode 100644
index 000000000..2378c78b6
--- /dev/null
+++ b/examples/status.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2011-2012 the libgit2 contributors
+ *
+ * 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 `git_status_foreach()` to roughly
+ * simulate the output of running `git status`. It should serve as a simple
+ * example of how to get basic status information.
+ *
+ * This does not have:
+ * - Robust error handling
+ * - Any real command line parsing
+ * - Colorized or paginated output formatting
+ *
+ */
+
+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("# %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)
+{
+ (void)repo;
+ (void)status;
+}
+
+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;
+
+ 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;
+}
+