static void assert_patch_matches_blobs( git_patch *p, git_blob *a, git_blob *b, int hunks, int l0, int l1, int ctxt, int adds, int dels) { const git_diff_delta *delta; size_t tc, ta, td; cl_assert(p != NULL); delta = git_patch_get_delta(p); cl_assert(delta != NULL); cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); cl_assert_equal_oid(git_blob_id(a), &delta->old_file.id); cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size); cl_assert_equal_oid(git_blob_id(b), &delta->new_file.id); cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size); cl_assert_equal_i(hunks, (int)git_patch_num_hunks(p)); if (hunks > 0) cl_assert_equal_i(l0, git_patch_num_lines_in_hunk(p, 0)); if (hunks > 1) cl_assert_equal_i(l1, git_patch_num_lines_in_hunk(p, 1)); cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p)); cl_assert_equal_i(ctxt, (int)tc); cl_assert_equal_i(adds, (int)ta); cl_assert_equal_i(dels, (int)td); }
/* * call-seq: * patch.stat -> int, int * * Returns the number of additions and deletions in the patch. */ static VALUE rb_git_diff_patch_stat(VALUE self) { git_patch *patch; size_t additions, deletions; Data_Get_Struct(self, git_patch, patch); git_patch_line_stats(NULL, &additions, &deletions, patch); return rb_ary_new3(2, INT2FIX(additions), INT2FIX(deletions)); }
/** * ggit_patch_get_line_stats: * @patch: a #GgitPatch. * @total_context: (allow-none) (out): return value for the number of context lines. * @total_additions: (allow-none) (out): return value for the number of added lines. * @total_deletions: (allow-none) (out): return value for the number of deleted lines. * @error: a #GError. * * Get the line statistics of the patch. * * Returns: %TRUE if successfull, %FALSE otherwise. * **/ gboolean ggit_patch_get_line_stats (GgitPatch *patch, gsize *total_context, gsize *total_additions, gsize *total_deletions, GError **error) { size_t tc; size_t ta; size_t td; gint ret; g_return_val_if_fail (patch != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ret = git_patch_line_stats (&tc, &ta, &td, patch->patch); if (ret != GIT_OK) { _ggit_error_set (error, ret); return FALSE; } if (total_context) { *total_context = tc; } if (total_additions) { *total_additions = ta; } if (total_deletions) { *total_deletions = td; } return TRUE; }
PyObject * wrap_patch(git_patch *patch) { Patch *py_patch; if (!patch) Py_RETURN_NONE; py_patch = PyObject_New(Patch, &PatchType); if (py_patch) { size_t i, j, hunk_amounts, lines_in_hunk, additions, deletions; const git_diff_delta *delta; const git_diff_hunk *hunk; const git_diff_line *line; int err; delta = git_patch_get_delta(patch); py_patch->old_file_path = delta->old_file.path; py_patch->new_file_path = delta->new_file.path; py_patch->status = git_diff_status_char(delta->status); py_patch->similarity = delta->similarity; py_patch->flags = delta->flags; py_patch->old_id = git_oid_allocfmt(&delta->old_file.id); py_patch->new_id = git_oid_allocfmt(&delta->new_file.id); git_patch_line_stats(NULL, &additions, &deletions, patch); py_patch->additions = additions; py_patch->deletions = deletions; hunk_amounts = git_patch_num_hunks(patch); py_patch->hunks = PyList_New(hunk_amounts); for (i = 0; i < hunk_amounts; ++i) { Hunk *py_hunk = NULL; err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i); if (err < 0) return Error_set(err); py_hunk = PyObject_New(Hunk, &HunkType); if (py_hunk != NULL) { py_hunk->old_start = hunk->old_start; py_hunk->old_lines = hunk->old_lines; py_hunk->new_start = hunk->new_start; py_hunk->new_lines = hunk->new_lines; py_hunk->lines = PyList_New(lines_in_hunk); for (j = 0; j < lines_in_hunk; ++j) { PyObject *py_line_origin = NULL, *py_line = NULL; err = git_patch_get_line_in_hunk(&line, patch, i, j); if (err < 0) return Error_set(err); py_line_origin = to_unicode_n(&line->origin, 1, NULL, NULL); py_line = to_unicode_n(line->content, line->content_len, NULL, NULL); PyList_SetItem(py_hunk->lines, j, Py_BuildValue("OO", py_line_origin, py_line)); Py_DECREF(py_line_origin); Py_DECREF(py_line); } PyList_SetItem((PyObject*) py_patch->hunks, i, (PyObject*) py_hunk); } } } git_patch_free(patch); return (PyObject*) py_patch; }
int git_diff_get_stats( git_diff_stats **out, git_diff *diff) { size_t i, deltas; size_t total_insertions = 0, total_deletions = 0; git_diff_stats *stats = NULL; int error = 0; assert(out && diff); stats = git__calloc(1, sizeof(git_diff_stats)); GITERR_CHECK_ALLOC(stats); deltas = git_diff_num_deltas(diff); stats->filestats = git__calloc(deltas, sizeof(diff_file_stats)); if (!stats->filestats) { git__free(stats); return -1; } stats->diff = diff; GIT_REFCOUNT_INC(diff); for (i = 0; i < deltas && !error; ++i) { git_patch *patch = NULL; size_t add = 0, remove = 0, namelen; const git_diff_delta *delta; if ((error = git_patch_from_diff(&patch, diff, i)) < 0) break; /* keep a count of renames because it will affect formatting */ delta = patch->delta; /* TODO ugh */ namelen = strlen(delta->new_file.path); if (strcmp(delta->old_file.path, delta->new_file.path) != 0) { namelen += strlen(delta->old_file.path); stats->renames++; } /* and, of course, count the line stats */ error = git_patch_line_stats(NULL, &add, &remove, patch); git_patch_free(patch); stats->filestats[i].insertions = add; stats->filestats[i].deletions = remove; total_insertions += add; total_deletions += remove; if (stats->max_name < namelen) stats->max_name = namelen; if (stats->max_filestat < add + remove) stats->max_filestat = add + remove; } stats->files_changed = deltas; stats->insertions = total_insertions; stats->deletions = total_deletions; stats->max_digits = digits_for_value(stats->max_filestat + 1); if (error < 0) { git_diff_stats_free(stats); stats = NULL; } *out = stats; return error; }
void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) { git_patch *p; git_blob *a; git_oid a_oid; const char *a_content = "Hello from the root\n"; const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n"; size_t tc, ta, td; /* tests/resources/attr/root_test1 */ cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8)); cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 8)); /* diff from blob a to content of b */ cl_git_pass(git_patch_from_blob_and_buffer( &p, a, NULL, b_content, strlen(b_content), NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_MODIFIED, git_patch_get_delta(p)->status); cl_assert_equal_i(1, (int)git_patch_num_hunks(p)); cl_assert_equal_i(6, git_patch_num_lines_in_hunk(p, 0)); cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p)); cl_assert_equal_i(1, (int)tc); cl_assert_equal_i(5, (int)ta); cl_assert_equal_i(0, (int)td); git_patch_free(p); /* diff from blob a to content of a */ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; cl_git_pass(git_patch_from_blob_and_buffer( &p, a, NULL, a_content, strlen(a_content), NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_patch_get_delta(p)->status); cl_assert_equal_i(0, (int)git_patch_num_hunks(p)); git_patch_free(p); /* diff from NULL blob to content of a */ cl_git_pass(git_patch_from_blob_and_buffer( &p, NULL, NULL, a_content, strlen(a_content), NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status); cl_assert_equal_i(1, (int)git_patch_num_hunks(p)); cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0)); git_patch_free(p); /* diff from blob a to NULL buffer */ cl_git_pass(git_patch_from_blob_and_buffer( &p, a, NULL, NULL, 0, NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_DELETED, git_patch_get_delta(p)->status); cl_assert_equal_i(1, (int)git_patch_num_hunks(p)); cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0)); git_patch_free(p); /* diff with reverse */ opts.flags ^= GIT_DIFF_REVERSE; cl_git_pass(git_patch_from_blob_and_buffer( &p, a, NULL, NULL, 0, NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status); cl_assert_equal_i(1, (int)git_patch_num_hunks(p)); cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0)); git_patch_free(p); git_blob_free(a); }