diff options
author | Russell Belfer <arrbee@arrbee.com> | 2012-02-04 04:53:01 +0400 |
---|---|---|
committer | Russell Belfer <arrbee@arrbee.com> | 2012-03-03 03:49:28 +0400 |
commit | 3a4375901a92efdc641c714ec9fd07b53f2f781e (patch) | |
tree | a0aed2d5e7ee9a6c2af317ae44e1f8bdce0a1446 /examples/diff.c | |
parent | 65b09b1deddec64fa5639e9fea10c048d31901fa (diff) |
Clean up diff implementation for review
This fixes several bugs, updates tests and docs, eliminates the
FILE* assumption in favor of printing callbacks for the diff patch
formatter helpers, and adds a "diff" example function that can
perform a diff from the command line.
Diffstat (limited to 'examples/diff.c')
-rw-r--r-- | examples/diff.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/examples/diff.c b/examples/diff.c new file mode 100644 index 000000000..9b696dad5 --- /dev/null +++ b/examples/diff.c @@ -0,0 +1,134 @@ +#include <stdio.h> +#include <git2.h> +#include <stdlib.h> +#include <string.h> + +void check(int error, const char *message) +{ + if (error) { + fprintf(stderr, "%s (%d)\n", message, error); + exit(1); + } +} + +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_oid(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 */ +}; + +int printer(void *data, char usage, const char *line) +{ + int *last_color = data, color = 0; + + 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; +} + +int main(int argc, char *argv[]) +{ + char path[GIT_PATH_MAX]; + git_repository *repo = NULL; + git_tree *a, *b; + git_diff_options opts = {0}; + git_diff_list *diff; + char *dir = "."; + int color = -1; + + if (argc != 3) { + fprintf(stderr, "usage: diff <tree-oid> <tree-oid>\n"); + exit(1); + } + + check(git_repository_discover(path, sizeof(path), dir, 0, "/"), + "Could not discover repository"); + check(git_repository_open(&repo, path), + "Could not open repository"); + + check(resolve_to_tree(repo, argv[1], &a), "Looking up first tree"); + check(resolve_to_tree(repo, argv[2], &b), "Looking up second tree"); + + check(git_diff_tree_to_tree(repo, &opts, a, b, &diff), "Generating diff"); + + fputs(colors[0], stdout); + + check(git_diff_print_compact(diff, &color, printer), "Displaying diff summary"); + + fprintf(stdout, "--\n"); + + color = 0; + + check(git_diff_print_patch(diff, &color, printer), "Displaying diff"); + + fputs(colors[0], stdout); + + git_diff_list_free(diff); + git_tree_free(a); + git_tree_free(b); + git_repository_free(repo); + + return 0; +} + |