libgit2/examples/diff.c
Russell Belfer 793c438559 Update diff callback param order
This makes the diff functions that take callbacks both take
the payload parameter after the callback function pointers and
pass the payload as the last argument to the callback function
instead of the first.  This should make them consistent with
other callbacks across the API.
2012-11-27 13:18:28 -08:00

243 lines
6.0 KiB
C

#include <stdio.h>
#include <git2.h>
#include <stdlib.h>
#include <string.h>
static void check(int error, const char *message)
{
if (error) {
fprintf(stderr, "%s (%d)\n", message, error);
exit(1);
}
}
static int resolve_to_tree(
git_repository *repo, const char *identifier, git_tree **tree)
{
int err = 0;
size_t len = strlen(identifier);
git_oid oid;
git_object *obj = NULL;
/* try to resolve as OID */
if (git_oid_fromstrn(&oid, identifier, len) == 0)
git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
/* try to resolve as reference */
if (obj == NULL) {
git_reference *ref, *resolved;
if (git_reference_lookup(&ref, repo, identifier) == 0) {
git_reference_resolve(&resolved, ref);
git_reference_free(ref);
if (resolved) {
git_object_lookup(&obj, repo, git_reference_target(resolved), GIT_OBJ_ANY);
git_reference_free(resolved);
}
}
}
if (obj == NULL)
return GIT_ENOTFOUND;
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;
}
return err;
}
char *colors[] = {
"\033[m", /* reset */
"\033[1m", /* bold */
"\033[31m", /* red */
"\033[32m", /* green */
"\033[36m" /* cyan */
};
static int printer(
const git_diff_delta *delta,
const git_diff_range *range,
char usage,
const char *line,
size_t line_len,
void *data)
{
int *last_color = data, color = 0;
(void)delta; (void)range; (void)line_len;
if (*last_color >= 0) {
switch (usage) {
case GIT_DIFF_LINE_ADDITION: color = 3; break;
case GIT_DIFF_LINE_DELETION: color = 2; break;
case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
default: color = 0;
}
if (color != *last_color) {
if (*last_color == 1 || color == 1)
fputs(colors[0], stdout);
fputs(colors[color], stdout);
*last_color = color;
}
}
fputs(line, 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;
strval = strtoul(arg + len, &endptr, 0);
if (endptr == arg)
return 0;
*val = strval;
return 1;
}
static int check_str_param(const char *arg, const char *pattern, char **val)
{
size_t len = strlen(pattern);
if (strncmp(arg, pattern, len))
return 0;
*val = (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_list *diff;
int i, color = -1, compact = 0, cached = 0;
char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL;
memset(&opts, 0, sizeof(opts));
/* parse arguments as copied from git-diff */
for (i = 1; i < argc; ++i) {
a = argv[i];
if (a[0] != '-') {
if (treeish1 == NULL)
treeish1 = a;
else if (treeish2 == NULL)
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"))
compact = 0;
else if (!strcmp(a, "--cached"))
cached = 1;
else if (!strcmp(a, "--name-status"))
compact = 1;
else if (!strcmp(a, "--color"))
color = 0;
else if (!strcmp(a, "--no-color"))
color = -1;
else if (!strcmp(a, "-R"))
opts.flags |= GIT_DIFF_REVERSE;
else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
opts.flags |= GIT_DIFF_FORCE_TEXT;
else if (!strcmp(a, "--ignore-space-at-eol"))
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
else if (!strcmp(a, "--ignored"))
opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
else if (!strcmp(a, "--untracked"))
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
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))
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_index_to_tree(&diff, repo, t1, NULL, &opts), "Diff");
else if (t1) {
git_diff_list *diff2;
check(git_diff_index_to_tree(&diff, repo, t1, NULL, &opts), "Diff");
check(git_diff_workdir_to_index(&diff2, repo, NULL, &opts), "Diff");
check(git_diff_merge(diff, diff2), "Merge diffs");
git_diff_list_free(diff2);
}
else if (cached) {
check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
check(git_diff_index_to_tree(&diff, repo, t1, NULL, &opts), "Diff");
}
else
check(git_diff_workdir_to_index(&diff, repo, NULL, &opts), "Diff");
if (color >= 0)
fputs(colors[0], stdout);
if (compact)
check(git_diff_print_compact(diff, printer, &color), "Displaying diff");
else
check(git_diff_print_patch(diff, printer, &color), "Displaying diff");
if (color >= 0)
fputs(colors[0], stdout);
git_diff_list_free(diff);
git_tree_free(t1);
git_tree_free(t2);
git_repository_free(repo);
return 0;
}