#include "git2/patch.h" #include "diff.h" #include "patch.h" int git_patch__invoke_callbacks( git_patch *patch, git_diff_file_cb file_cb, git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload) { int error = 0; uint32_t i, j; if (file_cb) error = file_cb(patch->delta, 0, payload); if (error) return error; if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) { if (binary_cb) error = binary_cb(patch->delta, &patch->binary, payload); return error; } if (!hunk_cb && !line_cb) return error; for (i = 0; !error && i < git_array_size(patch->hunks); ++i) { git_patch_hunk *h = git_array_get(patch->hunks, i); if (hunk_cb) error = hunk_cb(patch->delta, &h->hunk, payload); if (!line_cb) continue; for (j = 0; !error && j < h->line_count; ++j) { git_diff_line *l = git_array_get(patch->lines, h->line_start + j); error = line_cb(patch->delta, &h->hunk, l, payload); } } return error; } size_t git_patch_size( git_patch *patch, int include_context, int include_hunk_headers, int include_file_headers) { size_t out; assert(patch); out = patch->content_size; if (!include_context) out -= patch->context_size; if (include_hunk_headers) out += patch->header_size; if (include_file_headers) { git_buf file_header = GIT_BUF_INIT; if (git_diff_delta__format_file_header( &file_header, patch->delta, NULL, NULL, 0) < 0) giterr_clear(); else out += git_buf_len(&file_header); git_buf_free(&file_header); } return out; } int git_patch_line_stats( size_t *total_ctxt, size_t *total_adds, size_t *total_dels, const git_patch *patch) { size_t totals[3], idx; memset(totals, 0, sizeof(totals)); for (idx = 0; idx < git_array_size(patch->lines); ++idx) { git_diff_line *line = git_array_get(patch->lines, idx); if (!line) continue; switch (line->origin) { case GIT_DIFF_LINE_CONTEXT: totals[0]++; break; case GIT_DIFF_LINE_ADDITION: totals[1]++; break; case GIT_DIFF_LINE_DELETION: totals[2]++; break; default: /* diff --stat and --numstat don't count EOFNL marks because * they will always be paired with a ADDITION or DELETION line. */ break; } } if (total_ctxt) *total_ctxt = totals[0]; if (total_adds) *total_adds = totals[1]; if (total_dels) *total_dels = totals[2]; return 0; } const git_diff_delta *git_patch_get_delta(const git_patch *patch) { assert(patch); return patch->delta; } size_t git_patch_num_hunks(const git_patch *patch) { assert(patch); return git_array_size(patch->hunks); } static int patch_error_outofrange(const char *thing) { giterr_set(GITERR_INVALID, "patch %s index out of range", thing); return GIT_ENOTFOUND; } int git_patch_get_hunk( const git_diff_hunk **out, size_t *lines_in_hunk, git_patch *patch, size_t hunk_idx) { git_patch_hunk *hunk; assert(patch); hunk = git_array_get(patch->hunks, hunk_idx); if (!hunk) { if (out) *out = NULL; if (lines_in_hunk) *lines_in_hunk = 0; return patch_error_outofrange("hunk"); } if (out) *out = &hunk->hunk; if (lines_in_hunk) *lines_in_hunk = hunk->line_count; return 0; } int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx) { git_patch_hunk *hunk; assert(patch); if (!(hunk = git_array_get(patch->hunks, hunk_idx))) return patch_error_outofrange("hunk"); return (int)hunk->line_count; } int git_patch_get_line_in_hunk( const git_diff_line **out, git_patch *patch, size_t hunk_idx, size_t line_of_hunk) { git_patch_hunk *hunk; git_diff_line *line; assert(patch); if (!(hunk = git_array_get(patch->hunks, hunk_idx))) { if (out) *out = NULL; return patch_error_outofrange("hunk"); } if (line_of_hunk >= hunk->line_count || !(line = git_array_get( patch->lines, hunk->line_start + line_of_hunk))) { if (out) *out = NULL; return patch_error_outofrange("line"); } if (out) *out = line; return 0; } int git_patch_from_diff(git_patch **out, git_diff *diff, size_t idx) { assert(out && diff && diff->patch_fn); return diff->patch_fn(out, diff, idx); } static void git_patch__free(git_patch *patch) { if (patch->free_fn) patch->free_fn(patch); } void git_patch_free(git_patch *patch) { if (patch) GIT_REFCOUNT_DEC(patch, git_patch__free); }