mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-03 01:58:32 +00:00

Instead of having functions with so very many parameters to pass hunk and line data, this takes the existing git_diff_hunk struct and extends it with more hunk data, plus adds a git_diff_line. Those structs are used to pass back hunk and line data instead of the old APIs that took tons of parameters. Some work that was previously only being done for git_diff_patch creation (scanning the diff content for exact line counts) is now done for all callbacks, but the performance difference should not be noticable.
257 lines
6.8 KiB
C
257 lines
6.8 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;
|
|
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;
|
|
}
|
|
|
|
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_hunk *hunk,
|
|
const git_diff_line *line,
|
|
void *data)
|
|
{
|
|
int *last_color = data, color = 0;
|
|
|
|
(void)delta; (void)hunk;
|
|
|
|
if (*last_color >= 0) {
|
|
switch (line->origin) {
|
|
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: break;
|
|
}
|
|
if (color != *last_color) {
|
|
if (*last_color == 1 || color == 1)
|
|
fputs(colors[0], stdout);
|
|
fputs(colors[color], stdout);
|
|
*last_color = color;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int check_str_param(const char *arg, const char *pattern, const char **val)
|
|
{
|
|
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();
|
|
|
|
/* 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"))
|
|
format = GIT_DIFF_FORMAT_PATCH;
|
|
else if (!strcmp(a, "--cached"))
|
|
cached = 1;
|
|
else if (!strcmp(a, "--name-only"))
|
|
format = GIT_DIFF_FORMAT_NAME_ONLY;
|
|
else if (!strcmp(a, "--name-status"))
|
|
format = GIT_DIFF_FORMAT_NAME_STATUS;
|
|
else if (!strcmp(a, "--raw"))
|
|
format = GIT_DIFF_FORMAT_RAW;
|
|
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, "-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;
|
|
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)) {
|
|
/* 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);
|
|
}
|
|
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;
|
|
}
|
|
|