Commit 66902d47 by Russell Belfer

Extract common example helpers and reorg examples

This reorganizes a few of the examples so that the main function
comes first with the argument parsing extracted into a helper
that can come at the end of the file (so the example focuses more
on the use of libgit2 instead of command line support).  This also
creates a shared examples/common.[ch] so that useful helper funcs
can be shared across examples instead of repeated.
parent 5c50f22a
......@@ -9,6 +9,8 @@ ENDIF()
FILE(GLOB SRC_EXAMPLE_APPS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c)
FOREACH(src_app ${SRC_EXAMPLE_APPS})
STRING(REPLACE ".c" "" app_name ${src_app})
ADD_EXECUTABLE(${app_name} ${src_app})
TARGET_LINK_LIBRARIES(${app_name} git2)
IF(NOT ${app_name} STREQUAL "common")
ADD_EXECUTABLE(${app_name} ${src_app} "common.c")
TARGET_LINK_LIBRARIES(${app_name} git2)
ENDIF()
ENDFOREACH()
......@@ -8,7 +8,7 @@ APPS = general showindex diff rev-list cat-file status log rev-parse init
all: $(APPS)
% : %.c
$(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
$(CC) -o $@ common.c $(CFLAGS) $< $(LFLAGS)
clean:
$(RM) $(APPS)
......
/*
* 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>
#include <stdlib.h>
#include <string.h>
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#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)
{
if (error) {
fprintf(stderr, "%s (%d)\n", message, error);
exit(1);
}
}
/* this implements very rudimentary colorized output */
static int color_printer(
const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
/* the 'opts' struct captures all the various parsed command line options */
struct opts {
git_diff_options diffopts;
git_diff_find_options findopts;
int color;
int cached;
git_diff_format_t format;
const char *treeish1;
const char *treeish2;
const char *dir;
};
static void parse_opts(struct opts *o, int argc, char *argv[]);
static int resolve_to_tree(
git_repository *repo, const char *identifier, git_tree **tree)
int main(int argc, char *argv[])
{
int err = 0;
git_object *obj = NULL;
if ((err = git_revparse_single(&obj, repo, identifier)) < 0)
return err;
switch (git_object_type(obj)) {
case GIT_OBJ_TREE:
*tree = (git_tree *)obj;
break;
case GIT_OBJ_COMMIT:
err = git_commit_tree(tree, (git_commit *)obj);
git_object_free(obj);
break;
default:
err = GIT_ENOTFOUND;
git_repository *repo = NULL;
git_tree *t1 = NULL, *t2 = NULL;
git_diff *diff;
struct opts o = {
GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
-1, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
};
git_threads_init();
parse_opts(&o, argc, argv);
check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
"Could not open repository", o.dir);
/* Possible argument patterns:
* <sha1> <sha2>
* <sha1> --cached
* <sha1>
* --cached
* nothing
*
* Currently ranged arguments like <sha1>..<sha2> and <sha1>...<sha2>
* are not supported in this example
*/
if (o.treeish1)
treeish_to_tree(&t1, repo, o.treeish1);
if (o.treeish2)
treeish_to_tree(&t2, repo, o.treeish2);
if (t1 && t2)
check_lg2(
git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts),
"diff trees", NULL);
else if (t1 && o.cached)
check_lg2(
git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
"diff tree to index", NULL);
else if (t1)
check_lg2(
git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts),
"diff tree to working directory", NULL);
else if (o.cached) {
treeish_to_tree(&t1, repo, "HEAD");
check_lg2(
git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
"diff tree to index", NULL);
}
else
check_lg2(
git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts),
"diff index to working directory", NULL);
/* apply rename and copy detection if requested */
if ((o.findopts.flags & GIT_DIFF_FIND_ALL) != 0)
check_lg2(
git_diff_find_similar(diff, &o.findopts),
"finding renames and copies", NULL);
/* generate simple output using libgit2 display helper */
return err;
if (o.color >= 0)
fputs(colors[0], stdout);
check_lg2(
git_diff_print(diff, o.format, color_printer, &o.color),
"displaying diff", NULL);
if (o.color >= 0)
fputs(colors[0], stdout);
/* cleanup before exiting */
git_diff_free(diff);
git_tree_free(t1);
git_tree_free(t2);
git_repository_free(repo);
git_threads_shutdown();
return 0;
}
char *colors[] = {
"\033[m", /* reset */
"\033[1m", /* bold */
"\033[31m", /* red */
"\033[32m", /* green */
"\033[36m" /* cyan */
};
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);
}
static int printer(
static int color_printer(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
......@@ -63,6 +161,7 @@ static int printer(
case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
default: break;
}
if (color != *last_color) {
if (*last_color == 1 || color == 1)
fputs(colors[0], stdout);
......@@ -71,186 +170,79 @@ static int printer(
}
}
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
line->origin == GIT_DIFF_LINE_ADDITION ||
line->origin == GIT_DIFF_LINE_DELETION)
fputc(line->origin, stdout);
fwrite(line->content, 1, line->content_len, stdout);
return 0;
}
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;
return diff_output(delta, hunk, line, stdout);
}
static int check_str_param(const char *arg, const char *pattern, const char **val)
static void parse_opts(struct opts *o, int argc, char *argv[])
{
size_t len = strlen(pattern);
if (strncmp(arg, pattern, len))
return 0;
*val = (const char *)(arg + len);
return 1;
}
static void usage(const char *message, const char *arg)
{
if (message && arg)
fprintf(stderr, "%s: %s\n", message, arg);
else if (message)
fprintf(stderr, "%s\n", message);
fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
exit(1);
}
int main(int argc, char *argv[])
{
git_repository *repo = NULL;
git_tree *t1 = NULL, *t2 = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
git_diff *diff;
int i, color = -1, cached = 0;
git_diff_format_t format = GIT_DIFF_FORMAT_PATCH;
char *a, *treeish1 = NULL, *treeish2 = NULL;
const char *dir = ".";
git_threads_init();
struct args_info args = ARGS_INFO_INIT;
/* parse arguments as copied from git-diff */
for (i = 1; i < argc; ++i) {
a = argv[i];
for (args.pos = 1; args.pos < argc; ++args.pos) {
const char *a = argv[args.pos];
if (a[0] != '-') {
if (treeish1 == NULL)
treeish1 = a;
else if (treeish2 == NULL)
treeish2 = a;
if (o->treeish1 == NULL)
o->treeish1 = a;
else if (o->treeish2 == NULL)
o->treeish2 = a;
else
usage("Only one or two tree identifiers can be provided", NULL);
}
else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
!strcmp(a, "--patch"))
format = GIT_DIFF_FORMAT_PATCH;
o->format = GIT_DIFF_FORMAT_PATCH;
else if (!strcmp(a, "--cached"))
cached = 1;
o->cached = 1;
else if (!strcmp(a, "--name-only"))
format = GIT_DIFF_FORMAT_NAME_ONLY;
o->format = GIT_DIFF_FORMAT_NAME_ONLY;
else if (!strcmp(a, "--name-status"))
format = GIT_DIFF_FORMAT_NAME_STATUS;
o->format = GIT_DIFF_FORMAT_NAME_STATUS;
else if (!strcmp(a, "--raw"))
format = GIT_DIFF_FORMAT_RAW;
o->format = GIT_DIFF_FORMAT_RAW;
else if (!strcmp(a, "--color"))
color = 0;
o->color = 0;
else if (!strcmp(a, "--no-color"))
color = -1;
o->color = -1;
else if (!strcmp(a, "-R"))
opts.flags |= GIT_DIFF_REVERSE;
o->diffopts.flags |= GIT_DIFF_REVERSE;
else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
opts.flags |= GIT_DIFF_FORCE_TEXT;
o->diffopts.flags |= GIT_DIFF_FORCE_TEXT;
else if (!strcmp(a, "--ignore-space-at-eol"))
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
else if (!strcmp(a, "--ignored"))
opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
o->diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED;
else if (!strcmp(a, "--untracked"))
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
else if (check_uint16_param(a, "-M", &findopts.rename_threshold) ||
check_uint16_param(a, "--find-renames",
&findopts.rename_threshold))
findopts.flags |= GIT_DIFF_FIND_RENAMES;
else if (check_uint16_param(a, "-C", &findopts.copy_threshold) ||
check_uint16_param(a, "--find-copies",
&findopts.copy_threshold))
findopts.flags |= GIT_DIFF_FIND_COPIES;
o->diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
else if (match_uint16_arg(
&o->findopts.rename_threshold, &args, "-M") ||
match_uint16_arg(
&o->findopts.rename_threshold, &args, "--find-renames"))
o->findopts.flags |= GIT_DIFF_FIND_RENAMES;
else if (match_uint16_arg(
&o->findopts.copy_threshold, &args, "-C") ||
match_uint16_arg(
&o->findopts.copy_threshold, &args, "--find-copies"))
o->findopts.flags |= GIT_DIFF_FIND_COPIES;
else if (!strcmp(a, "--find-copies-harder"))
findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
else if (!strncmp(a, "-B", 2) || !strncmp(a, "--break-rewrites", 16)) {
o->findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites"))
/* TODO: parse thresholds */
findopts.flags |= GIT_DIFF_FIND_REWRITES;
}
else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
!check_uint16_param(a, "--unified=", &opts.context_lines) &&
!check_uint16_param(a, "--inter-hunk-context=",
&opts.interhunk_lines) &&
!check_str_param(a, "--src-prefix=", &opts.old_prefix) &&
!check_str_param(a, "--dst-prefix=", &opts.new_prefix) &&
!check_str_param(a, "--git-dir=", &dir))
usage("Unknown arg", a);
}
/* 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);
o->findopts.flags |= GIT_DIFF_FIND_REWRITES;
else if (!match_uint16_arg(
&o->diffopts.context_lines, &args, "-U") &&
!match_uint16_arg(
&o->diffopts.context_lines, &args, "--unified") &&
!match_uint16_arg(
&o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
!match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") &&
!match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") &&
!match_str_arg(&o->dir, &args, "--git-dir"))
usage("Unknown command line argument", a);
}
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;
}
#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);
}
/*
* 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 example demonstrates the libgit2 rev walker APIs to roughly
* simulate the output of `git log` and a few of command line arguments.
* `git log` has many many options and this only shows a few of them.
*
* This does not have:
* - Robust error handling
* - Colorized or paginated output formatting
* - Most of the `git log` options
*
* This does have:
* - Examples of translating command line arguments to equivalent libgit2
* revwalker configuration calls
* - Simplified options to apply pathspec limits and to show basic diffs
*/
/* log_state represents walker being configured while handling options */
struct log_state {
git_repository *repo;
const char *repodir;
git_revwalk *walker;
int hide;
int sorting;
int revisions;
};
static void set_sorting(struct log_state *s, unsigned int sort_mode)
/* utility functions that are called to configure the walker */
static void set_sorting(struct log_state *s, unsigned int sort_mode);
static void push_rev(struct log_state *s, git_object *obj, int hide);
static int add_revision(struct log_state *s, const char *revstr);
/* log_options holds other command line options that affect log output */
struct log_options {
int show_diff;
int skip, limit;
int min_parents, max_parents;
git_time_t before;
git_time_t after;
char *author;
char *committer;
};
/* utility functions that parse options and help with log output */
static int parse_options(
struct log_state *s, struct log_options *opt, int argc, char **argv);
static void print_time(const git_time *intime, const char *prefix);
static void print_commit(git_commit *commit);
static int match_with_parent(git_commit *commit, int i, git_diff_options *);
int main(int argc, char *argv[])
{
if (!s->repo) {
if (!s->repodir) s->repodir = ".";
check(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
"Could not open repository", s->repodir);
}
int i, count = 0, printed = 0, parents, last_arg;
struct log_state s;
struct log_options opt;
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
git_oid oid;
git_commit *commit = NULL;
git_pathspec *ps = NULL;
if (!s->walker)
check(git_revwalk_new(&s->walker, s->repo),
"Could not create revision walker", NULL);
git_threads_init();
if (sort_mode == GIT_SORT_REVERSE)
s->sorting = s->sorting ^ GIT_SORT_REVERSE;
else
s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE);
/* parse arguments and set up revwalker */
git_revwalk_sorting(s->walker, s->sorting);
last_arg = parse_options(&s, &opt, argc, argv);
diffopts.pathspec.strings = &argv[last_arg];
diffopts.pathspec.count = argc - last_arg;
if (diffopts.pathspec.count > 0)
check_lg2(git_pathspec_new(&ps, &diffopts.pathspec),
"Building pathspec", NULL);
if (!s.revisions)
add_revision(&s, NULL);
/* use the revwalker to traverse the history */
printed = count = 0;
for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) {
check_lg2(git_commit_lookup(&commit, s.repo, &oid),
"Failed to look up commit", NULL);
parents = (int)git_commit_parentcount(commit);
if (parents < opt.min_parents)
continue;
if (opt.max_parents > 0 && parents > opt.max_parents)
continue;
if (diffopts.pathspec.count > 0) {
int unmatched = parents;
if (parents == 0) {
git_tree *tree;
check_lg2(git_commit_tree(&tree, commit), "Get tree", NULL);
if (git_pathspec_match_tree(
NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0)
unmatched = 1;
git_tree_free(tree);
} else if (parents == 1) {
unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1;
} else {
for (i = 0; i < parents; ++i) {
if (match_with_parent(commit, i, &diffopts))
unmatched--;
}
}
if (unmatched > 0)
continue;
}
if (count++ < opt.skip)
continue;
if (opt.limit != -1 && printed++ >= opt.limit) {
git_commit_free(commit);
break;
}
print_commit(commit);
if (opt.show_diff) {
git_tree *a = NULL, *b = NULL;
git_diff *diff = NULL;
if (parents > 1)
continue;
check_lg2(git_commit_tree(&b, commit), "Get tree", NULL);
if (parents == 1) {
git_commit *parent;
check_lg2(git_commit_parent(&parent, commit, 0), "Get parent", NULL);
check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
git_commit_free(parent);
}
check_lg2(git_diff_tree_to_tree(
&diff, git_commit_owner(commit), a, b, &diffopts),
"Diff commit with parent", NULL);
check_lg2(
git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_output, NULL),
"Displaying diff", NULL);
git_diff_free(diff);
git_tree_free(a);
git_tree_free(b);
}
}
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)
{
hide = s->hide ^ hide;
/* create revwalker on demand if it doesn't already exist */
if (!s->walker) {
check(git_revwalk_new(&s->walker, s->repo),
check_lg2(git_revwalk_new(&s->walker, s->repo),
"Could not create revision walker", NULL);
git_revwalk_sorting(s->walker, s->sorting);
}
if (!obj)
check(git_revwalk_push_head(s->walker),
check_lg2(git_revwalk_push_head(s->walker),
"Could not find repository HEAD", NULL);
else if (hide)
check(git_revwalk_hide(s->walker, git_object_id(obj)),
check_lg2(git_revwalk_hide(s->walker, git_object_id(obj)),
"Reference does not refer to a commit", NULL);
else
check(git_revwalk_push(s->walker, git_object_id(obj)),
check_lg2(git_revwalk_push(s->walker, git_object_id(obj)),
"Reference does not refer to a commit", NULL);
git_object_free(obj);
}
/* parse revision string and add revs to walker */
static int add_revision(struct log_state *s, const char *revstr)
{
git_revspec revs;
int hide = 0;
/* open repo on demand if it isn't already open */
if (!s->repo) {
if (!s->repodir) s->repodir = ".";
check(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
"Could not open repository", s->repodir);
}
......@@ -107,10 +222,11 @@ static int add_revision(struct log_state *s, const char *revstr)
if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
git_oid base;
check(git_merge_base(&base, s->repo,
check_lg2(git_merge_base(&base, s->repo,
git_object_id(revs.from), git_object_id(revs.to)),
"Could not find merge base", revstr);
check(git_object_lookup(&revs.to, s->repo, &base, GIT_OBJ_COMMIT),
check_lg2(
git_object_lookup(&revs.to, s->repo, &base, GIT_OBJ_COMMIT),
"Could not find merge base commit", NULL);
push_rev(s, revs.to, hide);
......@@ -122,6 +238,30 @@ static int add_revision(struct log_state *s, const char *revstr)
return 0;
}
/* update revwalker with sorting mode */
static void set_sorting(struct log_state *s, unsigned int sort_mode)
{
/* open repo on demand if it isn't already open */
if (!s->repo) {
if (!s->repodir) s->repodir = ".";
check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
"Could not open repository", s->repodir);
}
/* create revwalker on demand if it doesn't already exist */
if (!s->walker)
check_lg2(git_revwalk_new(&s->walker, s->repo),
"Could not create revision walker", NULL);
if (sort_mode == GIT_SORT_REVERSE)
s->sorting = s->sorting ^ GIT_SORT_REVERSE;
else
s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE);
git_revwalk_sorting(s->walker, s->sorting);
}
/* helper to format a git_time value like Git */
static void print_time(const git_time *intime, const char *prefix)
{
char sign, out[32];
......@@ -148,6 +288,7 @@ static void print_time(const git_time *intime, const char *prefix)
printf("%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
}
/* helper to print a commit object */
static void print_commit(git_commit *commit)
{
char buf[GIT_OID_HEXSZ + 1];
......@@ -182,54 +323,21 @@ static void print_commit(git_commit *commit)
printf("\n");
}
static int print_diff(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *data)
{
(void)delta; (void)hunk; (void)data;
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
line->origin == GIT_DIFF_LINE_ADDITION ||
line->origin == GIT_DIFF_LINE_DELETION)
fputc(line->origin, stdout);
fwrite(line->content, 1, line->content_len, stdout);
return 0;
}
static int match_int(int *value, const char *arg, int allow_negative)
{
char *found;
*value = (int)strtol(arg, &found, 10);
return (found && *found == '\0' && (allow_negative || *value >= 0));
}
static int match_int_arg(
int *value, const char *arg, const char *pfx, int allow_negative)
{
size_t pfxlen = strlen(pfx);
if (strncmp(arg, pfx, pfxlen) != 0)
return 0;
if (!match_int(value, arg + pfxlen, allow_negative))
usage("Invalid value after argument", arg);
return 1;
}
static int match_with_parent(
git_commit *commit, int i, git_diff_options *opts)
/* helper to find how many files in a commit changed from its nth parent */
static int match_with_parent(git_commit *commit, int i, git_diff_options *opts)
{
git_commit *parent;
git_tree *a, *b;
git_diff *diff;
int ndeltas;
check(git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
check(git_commit_tree(&a, parent), "Tree for parent", NULL);
check(git_commit_tree(&b, commit), "Tree for commit", NULL);
check(git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts),
"Checking diff between parent and commit", NULL);
check_lg2(
git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
check_lg2(git_commit_tree(&b, commit), "Tree for commit", NULL);
check_lg2(
git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts),
"Checking diff between parent and commit", NULL);
ndeltas = (int)git_diff_num_deltas(diff);
......@@ -241,170 +349,76 @@ static int match_with_parent(
return ndeltas > 0;
}
struct log_options {
int show_diff;
int skip, limit;
int min_parents, max_parents;
git_time_t before;
git_time_t after;
char *author;
char *committer;
};
int main(int argc, char *argv[])
/* print a usage message for the program */
static void usage(const char *message, const char *arg)
{
int i, count = 0, printed = 0, parents;
char *a;
struct log_state s;
struct log_options opt;
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
git_oid oid;
git_commit *commit = NULL;
git_pathspec *ps = NULL;
if (message && arg)
fprintf(stderr, "%s: %s\n", message, arg);
else if (message)
fprintf(stderr, "%s\n", message);
fprintf(stderr, "usage: log [<options>]\n");
exit(1);
}
git_threads_init();
/* parse some log command line options */
static int parse_options(
struct log_state *s, struct log_options *opt, int argc, char **argv)
{
struct args_info args = ARGS_INFO_INIT;
memset(&s, 0, sizeof(s));
s.sorting = GIT_SORT_TIME;
memset(s, 0, sizeof(*s));
s->sorting = GIT_SORT_TIME;
memset(&opt, 0, sizeof(opt));
opt.max_parents = -1;
opt.limit = -1;
memset(opt, 0, sizeof(*opt));
opt->max_parents = -1;
opt->limit = -1;
for (i = 1; i < argc; ++i) {
a = argv[i];
for (args.pos = 1; args.pos < argc; ++args.pos) {
const char *a = argv[args.pos];
if (a[0] != '-') {
if (!add_revision(&s, a))
++count;
if (!add_revision(s, a))
s->revisions++;
else /* try failed revision parse as filename */
break;
} else if (!strcmp(a, "--")) {
++i;
++args.pos;
break;
}
else if (!strcmp(a, "--date-order"))
set_sorting(&s, GIT_SORT_TIME);
set_sorting(s, GIT_SORT_TIME);
else if (!strcmp(a, "--topo-order"))
set_sorting(&s, GIT_SORT_TOPOLOGICAL);
set_sorting(s, GIT_SORT_TOPOLOGICAL);
else if (!strcmp(a, "--reverse"))
set_sorting(&s, GIT_SORT_REVERSE);
else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
s.repodir = a + strlen("--git-dir=");
else if (match_int_arg(&opt.skip, a, "--skip=", 0))
set_sorting(s, GIT_SORT_REVERSE);
else if (match_str_arg(&s->repodir, &args, "--git-dir"))
/* found git-dir */;
else if (match_int_arg(&opt->skip, &args, "--skip", 0))
/* found valid --skip */;
else if (match_int_arg(&opt.limit, a, "--max-count=", 0))
else if (match_int_arg(&opt->limit, &args, "--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 (a[1] >= '0' && a[1] <= '9')
is_integer(&opt->limit, a + 1, 0);
else if (match_int_arg(&opt->limit, &args, "-n", 0))
/* found valid -n */;
else if (!strcmp(a, "--merges"))
opt.min_parents = 2;
opt->min_parents = 2;
else if (!strcmp(a, "--no-merges"))
opt.max_parents = 1;
opt->max_parents = 1;
else if (!strcmp(a, "--no-min-parents"))
opt.min_parents = 0;
opt->min_parents = 0;
else if (!strcmp(a, "--no-max-parents"))
opt.max_parents = -1;
else if (match_int_arg(&opt.max_parents, a, "--max-parents=", 1))
opt->max_parents = -1;
else if (match_int_arg(&opt->max_parents, &args, "--max-parents=", 1))
/* found valid --max-parents */;
else if (match_int_arg(&opt.min_parents, a, "--min-parents=", 0))
else if (match_int_arg(&opt->min_parents, &args, "--min-parents=", 0))
/* found valid --min_parents */;
else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
opt.show_diff = 1;
opt->show_diff = 1;
else
usage("Unsupported argument", a);
}
if (!count)
add_revision(&s, NULL);
diffopts.pathspec.strings = &argv[i];
diffopts.pathspec.count = argc - i;
if (diffopts.pathspec.count > 0)
check(git_pathspec_new(&ps, &diffopts.pathspec),
"Building pathspec", NULL);
printed = count = 0;
for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) {
check(git_commit_lookup(&commit, s.repo, &oid),
"Failed to look up commit", NULL);
parents = (int)git_commit_parentcount(commit);
if (parents < opt.min_parents)
continue;
if (opt.max_parents > 0 && parents > opt.max_parents)
continue;
if (diffopts.pathspec.count > 0) {
int unmatched = parents;
if (parents == 0) {
git_tree *tree;
check(git_commit_tree(&tree, commit), "Get tree", NULL);
if (git_pathspec_match_tree(
NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0)
unmatched = 1;
git_tree_free(tree);
} else if (parents == 1) {
unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1;
} else {
for (i = 0; i < parents; ++i) {
if (match_with_parent(commit, i, &diffopts))
unmatched--;
}
}
if (unmatched > 0)
continue;
}
if (count++ < opt.skip)
continue;
if (opt.limit != -1 && printed++ >= opt.limit) {
git_commit_free(commit);
break;
}
print_commit(commit);
if (opt.show_diff) {
git_tree *a = NULL, *b = NULL;
git_diff *diff = NULL;
if (parents > 1)
continue;
check(git_commit_tree(&b, commit), "Get tree", NULL);
if (parents == 1) {
git_commit *parent;
check(git_commit_parent(&parent, commit, 0), "Get parent", NULL);
check(git_commit_tree(&a, parent), "Tree for parent", NULL);
git_commit_free(parent);
}
check(git_diff_tree_to_tree(
&diff, git_commit_owner(commit), a, b, &diffopts),
"Diff commit with parent", NULL);
check(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, print_diff, NULL),
"Displaying diff", NULL);
git_diff_free(diff);
git_tree_free(a);
git_tree_free(b);
}
}
git_pathspec_free(ps);
git_revwalk_free(s.walker);
git_repository_free(s.repo);
git_threads_shutdown();
return 0;
return args.pos;
}
......@@ -4,18 +4,8 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include <git2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum {
FORMAT_DEFAULT = 0,
FORMAT_LONG = 1,
FORMAT_SHORT = 2,
FORMAT_PORCELAIN = 3,
};
#define MAX_PATHSPEC 8
#include "common.h"
/*
* This example demonstrates the use of the libgit2 status APIs,
......@@ -35,32 +25,83 @@ enum {
* - A sample status formatter that matches the "short" format
*/
static void check(int error, const char *message, const char *extra)
enum {
FORMAT_DEFAULT = 0,
FORMAT_LONG = 1,
FORMAT_SHORT = 2,
FORMAT_PORCELAIN = 3,
};
#define MAX_PATHSPEC 8
struct opts {
git_status_options statusopt;
char *repodir;
char *pathspec[MAX_PATHSPEC];
int npaths;
int format;
int zterm;
int showbranch;
};
static void parse_opts(struct opts *o, int argc, char *argv[]);
static void show_branch(git_repository *repo, int format);
static void print_long(git_repository *repo, git_status_list *status);
static void print_short(git_repository *repo, git_status_list *status);
int main(int argc, char *argv[])
{
const git_error *lg2err;
const char *lg2msg = "", *lg2spacer = "";
git_repository *repo = NULL;
git_status_list *status;
struct opts o = { GIT_STATUS_OPTIONS_INIT, "." };
if (!error)
return;
git_threads_init();
if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
lg2msg = lg2err->message;
lg2spacer = " - ";
}
o.statusopt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
o.statusopt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
parse_opts(&o, argc, argv);
/*
* Try to open the repository at the given path (or at the current
* directory if none was given).
*/
check_lg2(git_repository_open_ext(&repo, o.repodir, 0, NULL),
"Could not open repository", o.repodir);
if (git_repository_is_bare(repo))
fatal("Cannot report status on bare repository",
git_repository_path(repo));
/*
* Run status on the repository
*
* Because we want to simluate a full "git status" run and want to
* support some command line options, we use `git_status_foreach_ext()`
* instead of just the plain status call. This allows (a) iterating
* over the index and then the workdir and (b) extra flags that control
* which files are included. If you just want simple status (e.g. to
* enumerate files that are modified) then you probably don't need the
* extended API.
*/
check_lg2(git_status_list_new(&status, repo, &o.statusopt),
"Could not get status", NULL);
if (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(repo, status);
else
fprintf(stderr, "%s [%d]%s%s\n",
message, error, lg2spacer, lg2msg);
print_short(repo, status);
exit(1);
}
git_status_list_free(status);
git_repository_free(repo);
git_threads_shutdown();
static void fail(const char *message)
{
check(-1, message, NULL);
return 0;
}
static void show_branch(git_repository *repo, int format)
......@@ -78,7 +119,7 @@ static void show_branch(git_repository *repo, int format)
if (!strncmp(branch, "refs/heads/", strlen("refs/heads/")))
branch += strlen("refs/heads/");
} else
check(error, "failed to get current branch", NULL);
check_lg2(error, "failed to get current branch", NULL);
if (format == FORMAT_LONG)
printf("# On branch %s\n",
......@@ -341,103 +382,58 @@ static void print_short(git_repository *repo, git_status_list *status)
}
}
int main(int argc, char *argv[])
static void parse_opts(struct opts *o, int argc, char *argv[])
{
git_repository *repo = NULL;
int i, npaths = 0, format = FORMAT_DEFAULT, zterm = 0, showbranch = 0;
git_status_options opt = GIT_STATUS_OPTIONS_INIT;
git_status_list *status;
char *repodir = ".", *pathspec[MAX_PATHSPEC];
struct args_info args = ARGS_INFO_INIT;
opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
for (args.pos = 1; args.pos < argc; ++args.pos) {
char *a = argv[args.pos];
for (i = 1; i < argc; ++i) {
if (argv[i][0] != '-') {
if (npaths < MAX_PATHSPEC)
pathspec[npaths++] = argv[i];
if (a[0] != '-') {
if (o->npaths < MAX_PATHSPEC)
o->pathspec[o->npaths++] = a;
else
fail("Example only supports a limited pathspec");
fatal("Example only supports a limited pathspec", NULL);
}
else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--short"))
format = FORMAT_SHORT;
else if (!strcmp(argv[i], "--long"))
format = FORMAT_LONG;
else if (!strcmp(argv[i], "--porcelain"))
format = FORMAT_PORCELAIN;
else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch"))
showbranch = 1;
else if (!strcmp(argv[i], "-z")) {
zterm = 1;
if (format == FORMAT_DEFAULT)
format = FORMAT_PORCELAIN;
else if (!strcmp(a, "-s") || !strcmp(a, "--short"))
o->format = FORMAT_SHORT;
else if (!strcmp(a, "--long"))
o->format = FORMAT_LONG;
else if (!strcmp(a, "--porcelain"))
o->format = FORMAT_PORCELAIN;
else if (!strcmp(a, "-b") || !strcmp(a, "--branch"))
o->showbranch = 1;
else if (!strcmp(a, "-z")) {
o->zterm = 1;
if (o->format == FORMAT_DEFAULT)
o->format = FORMAT_PORCELAIN;
}
else if (!strcmp(argv[i], "--ignored"))
opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
else if (!strcmp(argv[i], "-uno") ||
!strcmp(argv[i], "--untracked-files=no"))
opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
else if (!strcmp(argv[i], "-unormal") ||
!strcmp(argv[i], "--untracked-files=normal"))
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
else if (!strcmp(argv[i], "-uall") ||
!strcmp(argv[i], "--untracked-files=all"))
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
else if (!strcmp(a, "--ignored"))
o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
else if (!strcmp(a, "-uno") ||
!strcmp(a, "--untracked-files=no"))
o->statusopt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
else if (!strcmp(a, "-unormal") ||
!strcmp(a, "--untracked-files=normal"))
o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
else if (!strcmp(a, "-uall") ||
!strcmp(a, "--untracked-files=all"))
o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
else if (!strcmp(argv[i], "--ignore-submodules=all"))
opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir=")))
repodir = argv[i] + strlen("--git-dir=");
else if (!strcmp(a, "--ignore-submodules=all"))
o->statusopt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
o->repodir = a + strlen("--git-dir=");
else
check(-1, "Unsupported option", argv[i]);
check_lg2(-1, "Unsupported option", a);
}
if (format == FORMAT_DEFAULT)
format = FORMAT_LONG;
if (format == FORMAT_LONG)
showbranch = 1;
if (npaths > 0) {
opt.pathspec.strings = pathspec;
opt.pathspec.count = npaths;
if (o->format == FORMAT_DEFAULT)
o->format = FORMAT_LONG;
if (o->format == FORMAT_LONG)
o->showbranch = 1;
if (o->npaths > 0) {
o->statusopt.pathspec.strings = o->pathspec;
o->statusopt.pathspec.count = o->npaths;
}
/*
* Try to open the repository at the given path (or at the current
* directory if none was given).
*/
check(git_repository_open_ext(&repo, repodir, 0, NULL),
"Could not open repository", repodir);
if (git_repository_is_bare(repo))
fail("Cannot report status on bare repository");
/*
* Run status on the repository
*
* Because we want to simluate a full "git status" run and want to
* support some command line options, we use `git_status_foreach_ext()`
* instead of just the plain status call. This allows (a) iterating
* over the index and then the workdir and (b) extra flags that control
* which files are included. If you just want simple status (e.g. to
* enumerate files that are modified) then you probably don't need the
* extended API.
*/
check(git_status_list_new(&status, repo, &opt),
"Could not get status", NULL);
if (showbranch)
show_branch(repo, format);
if (format == FORMAT_LONG)
print_long(repo, status);
else
print_short(repo, status);
git_status_list_free(status);
git_repository_free(repo);
return 0;
}
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