summaryrefslogtreecommitdiff
path: root/examples/status.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/status.c')
-rw-r--r--examples/status.c301
1 files changed, 166 insertions, 135 deletions
diff --git a/examples/status.c b/examples/status.c
index 689098415..3adfe0d5d 100644
--- a/examples/status.c
+++ b/examples/status.c
@@ -1,33 +1,32 @@
/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
+ * libgit2 "status" example - shows how to use the status APIs
*
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
-#include <git2.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-enum {
- FORMAT_DEFAULT = 0,
- FORMAT_LONG = 1,
- FORMAT_SHORT = 2,
- FORMAT_PORCELAIN = 3,
-};
-#define MAX_PATHSPEC 8
+#include "common.h"
-/*
+/**
* This example demonstrates the use of the libgit2 status APIs,
* particularly the `git_status_list` object, to roughly simulate the
* output of running `git status`. It serves as a simple example of
* using those APIs to get basic status information.
*
* This does not have:
+ *
* - Robust error handling
* - Colorized or paginated output formatting
*
* This does have:
+ *
* - Examples of translating command line arguments to the status
* options settings to mimic `git status` results.
* - A sample status formatter that matches the default "long" format
@@ -35,34 +34,91 @@ enum {
* - A sample status formatter that matches the "short" format
*/
-static void check(int error, const char *message, const char *extra)
+enum {
+ FORMAT_DEFAULT = 0,
+ FORMAT_LONG = 1,
+ FORMAT_SHORT = 2,
+ FORMAT_PORCELAIN = 3,
+};
+
+#define MAX_PATHSPEC 8
+
+struct opts {
+ git_status_options statusopt;
+ char *repodir;
+ char *pathspec[MAX_PATHSPEC];
+ int npaths;
+ int format;
+ int zterm;
+ int showbranch;
+};
+
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+static void show_branch(git_repository *repo, int format);
+static void print_long(git_status_list *status);
+static void print_short(git_repository *repo, git_status_list *status);
+
+int main(int argc, char *argv[])
{
- const git_error *lg2err;
- const char *lg2msg = "", *lg2spacer = "";
+ git_repository *repo = NULL;
+ git_status_list *status;
+ struct opts o = { GIT_STATUS_OPTIONS_INIT, "." };
- if (!error)
- return;
+ git_threads_init();
- if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
- lg2msg = lg2err->message;
- lg2spacer = " - ";
- }
+ o.statusopt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ o.statusopt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
+ GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
+
+ parse_opts(&o, argc, argv);
+
+ /**
+ * Try to open the repository at the given path (or at the current
+ * directory if none was given).
+ */
+ check_lg2(git_repository_open_ext(&repo, o.repodir, 0, NULL),
+ "Could not open repository", o.repodir);
+
+ if (git_repository_is_bare(repo))
+ fatal("Cannot report status on bare repository",
+ git_repository_path(repo));
+
+ /**
+ * Run status on the repository
+ *
+ * We use `git_status_list_new()` to generate a list of status
+ * information which lets us iterate over it at our
+ * convenience and extract the data we want to show out of
+ * each entry.
+ *
+ * You can use `git_status_foreach()` or
+ * `git_status_foreach_ext()` if you'd prefer to execute a
+ * callback for each entry. The latter gives you more control
+ * about what results are presented.
+ */
+ check_lg2(git_status_list_new(&status, repo, &o.statusopt),
+ "Could not get status", NULL);
- if (extra)
- fprintf(stderr, "%s '%s' [%d]%s%s\n",
- message, extra, error, lg2spacer, lg2msg);
+ if (o.showbranch)
+ show_branch(repo, o.format);
+
+ if (o.format == FORMAT_LONG)
+ print_long(status);
else
- fprintf(stderr, "%s [%d]%s%s\n",
- message, error, lg2spacer, lg2msg);
+ print_short(repo, status);
- exit(1);
-}
+ git_status_list_free(status);
+ git_repository_free(repo);
+ git_threads_shutdown();
-static void fail(const char *message)
-{
- check(-1, message, NULL);
+ return 0;
}
+/**
+ * If the user asked for the branch, let's show the short name of the
+ * branch.
+ */
static void show_branch(git_repository *repo, int format)
{
int error = 0;
@@ -71,14 +127,12 @@ static void show_branch(git_repository *repo, int format)
error = git_repository_head(&head, repo);
- if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
+ if (error == GIT_EUNBORNBRANCH || 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/");
+ branch = git_reference_shorthand(head);
} else
- check(error, "failed to get current branch", NULL);
+ check_lg2(error, "failed to get current branch", NULL);
if (format == FORMAT_LONG)
printf("# On branch %s\n",
@@ -89,7 +143,11 @@ static void show_branch(git_repository *repo, int format)
git_reference_free(head);
}
-static void print_long(git_repository *repo, git_status_list *status)
+/**
+ * This function print out an output similar to git's status command
+ * in long form, including the command-line hints.
+ */
+static void print_long(git_status_list *status)
{
size_t i, maxi = git_status_list_entrycount(status);
const git_status_entry *s;
@@ -97,9 +155,7 @@ static void print_long(git_repository *repo, git_status_list *status)
int changed_in_workdir = 0, rm_in_workdir = 0;
const char *old_path, *new_path;
- (void)repo;
-
- /* print index changes */
+ /** Print index changes. */
for (i = 0; i < maxi; ++i) {
char *istatus = NULL;
@@ -148,16 +204,22 @@ static void print_long(git_repository *repo, git_status_list *status)
}
header = 0;
- /* print workdir changes to tracked files */
+ /** Print workdir changes to tracked files. */
for (i = 0; i < maxi; ++i) {
char *wstatus = NULL;
s = git_status_byindex(status, i);
+ /**
+ * With `GIT_STATUS_OPT_INCLUDE_UNMODIFIED` (not used in this example)
+ * `index_to_workdir` may not be `NULL` even if there are
+ * no differences, in which case it will be a `GIT_DELTA_UNMODIFIED`.
+ */
if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL)
continue;
+ /** Print out the output since we know the file has some changes */
if (s->status & GIT_STATUS_WT_MODIFIED)
wstatus = "modified: ";
if (s->status & GIT_STATUS_WT_DELETED)
@@ -191,9 +253,8 @@ static void print_long(git_repository *repo, git_status_list *status)
changed_in_workdir = 1;
printf("#\n");
}
- header = 0;
- /* print untracked files */
+ /** Print untracked files. */
header = 0;
@@ -215,7 +276,7 @@ static void print_long(git_repository *repo, git_status_list *status)
header = 0;
- /* print ignored files */
+ /** Print ignored files. */
for (i = 0; i < maxi; ++i) {
s = git_status_byindex(status, i);
@@ -237,6 +298,10 @@ static void print_long(git_repository *repo, git_status_list *status)
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
}
+/**
+ * This version of the output prefixes each path with two status
+ * columns and shows submodule status information.
+ */
static void print_short(git_repository *repo, git_status_list *status)
{
size_t i, maxi = git_status_list_entrycount(status);
@@ -287,6 +352,10 @@ static void print_short(git_repository *repo, git_status_list *status)
if (istatus == '?' && wstatus == '?')
continue;
+ /**
+ * A commit in a tree is how submodules are stored, so
+ * let's go take a look at its status.
+ */
if (s->index_to_workdir &&
s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
{
@@ -308,6 +377,10 @@ static void print_short(git_repository *repo, git_status_list *status)
}
}
+ /**
+ * Now that we have all the information, it's time to format the output.
+ */
+
if (s->head_to_index) {
a = s->head_to_index->old_file.path;
b = s->head_to_index->new_file.path;
@@ -341,103 +414,61 @@ static void print_short(git_repository *repo, git_status_list *status)
}
}
-int main(int argc, char *argv[])
+/**
+ * Parse options that git's status command supports.
+ */
+static void parse_opts(struct opts *o, int argc, char *argv[])
{
- git_repository *repo = NULL;
- int i, npaths = 0, format = FORMAT_DEFAULT, zterm = 0, showbranch = 0;
- git_status_options opt = GIT_STATUS_OPTIONS_INIT;
- git_status_list *status;
- char *repodir = ".", *pathspec[MAX_PATHSPEC];
+ struct args_info args = ARGS_INFO_INIT;
- opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
- opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
- GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ char *a = argv[args.pos];
- for (i = 1; i < argc; ++i) {
- if (argv[i][0] != '-') {
- if (npaths < MAX_PATHSPEC)
- pathspec[npaths++] = argv[i];
+ if (a[0] != '-') {
+ if (o->npaths < MAX_PATHSPEC)
+ o->pathspec[o->npaths++] = a;
else
- fail("Example only supports a limited pathspec");
+ fatal("Example only supports a limited pathspec", NULL);
}
- else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--short"))
- format = FORMAT_SHORT;
- else if (!strcmp(argv[i], "--long"))
- format = FORMAT_LONG;
- else if (!strcmp(argv[i], "--porcelain"))
- format = FORMAT_PORCELAIN;
- else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch"))
- showbranch = 1;
- else if (!strcmp(argv[i], "-z")) {
- zterm = 1;
- if (format == FORMAT_DEFAULT)
- format = FORMAT_PORCELAIN;
+ else if (!strcmp(a, "-s") || !strcmp(a, "--short"))
+ o->format = FORMAT_SHORT;
+ else if (!strcmp(a, "--long"))
+ o->format = FORMAT_LONG;
+ else if (!strcmp(a, "--porcelain"))
+ o->format = FORMAT_PORCELAIN;
+ else if (!strcmp(a, "-b") || !strcmp(a, "--branch"))
+ o->showbranch = 1;
+ else if (!strcmp(a, "-z")) {
+ o->zterm = 1;
+ if (o->format == FORMAT_DEFAULT)
+ o->format = FORMAT_PORCELAIN;
}
- else if (!strcmp(argv[i], "--ignored"))
- opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
- else if (!strcmp(argv[i], "-uno") ||
- !strcmp(argv[i], "--untracked-files=no"))
- opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- else if (!strcmp(argv[i], "-unormal") ||
- !strcmp(argv[i], "--untracked-files=normal"))
- opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- else if (!strcmp(argv[i], "-uall") ||
- !strcmp(argv[i], "--untracked-files=all"))
- opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ else if (!strcmp(a, "--ignored"))
+ o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
+ else if (!strcmp(a, "-uno") ||
+ !strcmp(a, "--untracked-files=no"))
+ o->statusopt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ else if (!strcmp(a, "-unormal") ||
+ !strcmp(a, "--untracked-files=normal"))
+ o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ else if (!strcmp(a, "-uall") ||
+ !strcmp(a, "--untracked-files=all"))
+ o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
- else if (!strcmp(argv[i], "--ignore-submodules=all"))
- opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
- else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir=")))
- repodir = argv[i] + strlen("--git-dir=");
+ else if (!strcmp(a, "--ignore-submodules=all"))
+ o->statusopt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+ else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
+ o->repodir = a + strlen("--git-dir=");
else
- check(-1, "Unsupported option", argv[i]);
+ check_lg2(-1, "Unsupported option", a);
}
- if (format == FORMAT_DEFAULT)
- format = FORMAT_LONG;
- if (format == FORMAT_LONG)
- showbranch = 1;
- if (npaths > 0) {
- opt.pathspec.strings = pathspec;
- opt.pathspec.count = npaths;
+ if (o->format == FORMAT_DEFAULT)
+ o->format = FORMAT_LONG;
+ if (o->format == FORMAT_LONG)
+ o->showbranch = 1;
+ if (o->npaths > 0) {
+ o->statusopt.pathspec.strings = o->pathspec;
+ o->statusopt.pathspec.count = o->npaths;
}
-
- /*
- * Try to open the repository at the given path (or at the current
- * directory if none was given).
- */
- check(git_repository_open_ext(&repo, repodir, 0, NULL),
- "Could not open repository", repodir);
-
- if (git_repository_is_bare(repo))
- fail("Cannot report status on bare repository");
-
- /*
- * Run status on the repository
- *
- * Because we want to simluate a full "git status" run and want to
- * support some command line options, we use `git_status_foreach_ext()`
- * instead of just the plain status call. This allows (a) iterating
- * over the index and then the workdir and (b) extra flags that control
- * which files are included. If you just want simple status (e.g. to
- * enumerate files that are modified) then you probably don't need the
- * extended API.
- */
- check(git_status_list_new(&status, repo, &opt),
- "Could not get status", NULL);
-
- if (showbranch)
- show_branch(repo, format);
-
- if (format == FORMAT_LONG)
- print_long(repo, status);
- else
- print_short(repo, status);
-
- git_status_list_free(status);
- git_repository_free(repo);
-
- return 0;
}
-