mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-04 19:50:19 +00:00

The usefulness of these helpers came up for me while debugging some of the iterator changes that I was making, so since they have also been requested (albeit indirectly) I thought I'd include them.
232 lines
5.0 KiB
C
232 lines
5.0 KiB
C
#include "clar_libgit2.h"
|
|
#include "diff_helpers.h"
|
|
#include "git2/sys/diff.h"
|
|
|
|
git_tree *resolve_commit_oid_to_tree(
|
|
git_repository *repo,
|
|
const char *partial_oid)
|
|
{
|
|
size_t len = strlen(partial_oid);
|
|
git_oid oid;
|
|
git_object *obj = NULL;
|
|
git_tree *tree = NULL;
|
|
|
|
if (git_oid_fromstrn(&oid, partial_oid, len) == 0)
|
|
git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
|
|
cl_assert(obj);
|
|
if (git_object_type(obj) == GIT_OBJ_TREE)
|
|
return (git_tree *)obj;
|
|
cl_assert(git_object_type(obj) == GIT_OBJ_COMMIT);
|
|
cl_git_pass(git_commit_tree(&tree, (git_commit *)obj));
|
|
git_object_free(obj);
|
|
return tree;
|
|
}
|
|
|
|
static char diff_pick_suffix(int mode)
|
|
{
|
|
if (S_ISDIR(mode))
|
|
return '/';
|
|
else if (GIT_PERMS_IS_EXEC(mode))
|
|
return '*';
|
|
else
|
|
return ' ';
|
|
}
|
|
|
|
static void fprintf_delta(FILE *fp, const git_diff_delta *delta, float progress)
|
|
{
|
|
char code = git_diff_status_char(delta->status);
|
|
char old_suffix = diff_pick_suffix(delta->old_file.mode);
|
|
char new_suffix = diff_pick_suffix(delta->new_file.mode);
|
|
|
|
fprintf(fp, "%c\t%s", code, delta->old_file.path);
|
|
|
|
if ((delta->old_file.path != delta->new_file.path &&
|
|
strcmp(delta->old_file.path, delta->new_file.path) != 0) ||
|
|
(delta->old_file.mode != delta->new_file.mode &&
|
|
delta->old_file.mode != 0 && delta->new_file.mode != 0))
|
|
fprintf(fp, "%c %s%c", old_suffix, delta->new_file.path, new_suffix);
|
|
else if (old_suffix != ' ')
|
|
fprintf(fp, "%c", old_suffix);
|
|
|
|
fprintf(fp, "\t[%.2f]\n", progress);
|
|
}
|
|
|
|
int diff_file_cb(
|
|
const git_diff_delta *delta,
|
|
float progress,
|
|
void *payload)
|
|
{
|
|
diff_expects *e = payload;
|
|
|
|
if (e->debug)
|
|
fprintf_delta(stderr, delta, progress);
|
|
|
|
if (e->names)
|
|
cl_assert_equal_s(e->names[e->files], delta->old_file.path);
|
|
if (e->statuses)
|
|
cl_assert_equal_i(e->statuses[e->files], (int)delta->status);
|
|
|
|
e->files++;
|
|
|
|
if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
|
e->files_binary++;
|
|
|
|
cl_assert(delta->status <= GIT_DELTA_TYPECHANGE);
|
|
|
|
e->file_status[delta->status] += 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int diff_print_file_cb(
|
|
const git_diff_delta *delta,
|
|
float progress,
|
|
void *payload)
|
|
{
|
|
if (!payload) {
|
|
fprintf_delta(stderr, delta, progress);
|
|
return 0;
|
|
}
|
|
|
|
if (!((diff_expects *)payload)->debug)
|
|
fprintf_delta(stderr, delta, progress);
|
|
|
|
return diff_file_cb(delta, progress, payload);
|
|
}
|
|
|
|
int diff_hunk_cb(
|
|
const git_diff_delta *delta,
|
|
const git_diff_hunk *hunk,
|
|
void *payload)
|
|
{
|
|
diff_expects *e = payload;
|
|
const char *scan = hunk->header, *scan_end = scan + hunk->header_len;
|
|
|
|
GIT_UNUSED(delta);
|
|
|
|
/* confirm no NUL bytes in header text */
|
|
while (scan < scan_end)
|
|
cl_assert('\0' != *scan++);
|
|
|
|
e->hunks++;
|
|
e->hunk_old_lines += hunk->old_lines;
|
|
e->hunk_new_lines += hunk->new_lines;
|
|
return 0;
|
|
}
|
|
|
|
int diff_line_cb(
|
|
const git_diff_delta *delta,
|
|
const git_diff_hunk *hunk,
|
|
const git_diff_line *line,
|
|
void *payload)
|
|
{
|
|
diff_expects *e = payload;
|
|
|
|
GIT_UNUSED(delta);
|
|
GIT_UNUSED(hunk);
|
|
|
|
e->lines++;
|
|
switch (line->origin) {
|
|
case GIT_DIFF_LINE_CONTEXT:
|
|
case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */
|
|
e->line_ctxt++;
|
|
break;
|
|
case GIT_DIFF_LINE_ADDITION:
|
|
case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */
|
|
e->line_adds++;
|
|
break;
|
|
case GIT_DIFF_LINE_DELETION:
|
|
case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */
|
|
e->line_dels++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int diff_foreach_via_iterator(
|
|
git_diff *diff,
|
|
git_diff_file_cb file_cb,
|
|
git_diff_hunk_cb hunk_cb,
|
|
git_diff_line_cb line_cb,
|
|
void *data)
|
|
{
|
|
size_t d, num_d = git_diff_num_deltas(diff);
|
|
|
|
for (d = 0; d < num_d; ++d) {
|
|
git_patch *patch;
|
|
const git_diff_delta *delta;
|
|
size_t h, num_h;
|
|
|
|
cl_git_pass(git_patch_from_diff(&patch, diff, d));
|
|
cl_assert((delta = git_patch_get_delta(patch)) != NULL);
|
|
|
|
/* call file_cb for this file */
|
|
if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) {
|
|
git_patch_free(patch);
|
|
goto abort;
|
|
}
|
|
|
|
/* if there are no changes, then the patch will be NULL */
|
|
if (!patch) {
|
|
cl_assert(delta->status == GIT_DELTA_UNMODIFIED ||
|
|
(delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
|
|
continue;
|
|
}
|
|
|
|
if (!hunk_cb && !line_cb) {
|
|
git_patch_free(patch);
|
|
continue;
|
|
}
|
|
|
|
num_h = git_patch_num_hunks(patch);
|
|
|
|
for (h = 0; h < num_h; h++) {
|
|
const git_diff_hunk *hunk;
|
|
size_t l, num_l;
|
|
|
|
cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
|
|
|
|
if (hunk_cb && hunk_cb(delta, hunk, data) != 0) {
|
|
git_patch_free(patch);
|
|
goto abort;
|
|
}
|
|
|
|
for (l = 0; l < num_l; ++l) {
|
|
const git_diff_line *line;
|
|
|
|
cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
|
|
|
|
if (line_cb &&
|
|
line_cb(delta, hunk, line, data) != 0) {
|
|
git_patch_free(patch);
|
|
goto abort;
|
|
}
|
|
}
|
|
}
|
|
|
|
git_patch_free(patch);
|
|
}
|
|
|
|
return 0;
|
|
|
|
abort:
|
|
giterr_clear();
|
|
return GIT_EUSER;
|
|
}
|
|
|
|
void diff_print(FILE *fp, git_diff *diff)
|
|
{
|
|
cl_git_pass(
|
|
git_diff_print(diff, GIT_DIFF_FORMAT_PATCH,
|
|
git_diff_print_callback__to_file_handle, fp ? fp : stderr));
|
|
}
|
|
|
|
void diff_print_raw(FILE *fp, git_diff *diff)
|
|
{
|
|
cl_git_pass(
|
|
git_diff_print(diff, GIT_DIFF_FORMAT_RAW,
|
|
git_diff_print_callback__to_file_handle, fp ? fp : stderr));
|
|
}
|