PyObject* diff_get_patch_byindex(git_diff_list* list, size_t idx) { const git_diff_delta* delta; const git_diff_range* range; git_diff_patch* patch = NULL; size_t i, j, hunk_amounts, lines_in_hunk, line_len, header_len; const char* line, *header; int err; Hunk *py_hunk = NULL; Patch *py_patch = NULL; err = git_diff_get_patch(&patch, &delta, list, idx); if (err < 0) return Error_set(err); py_patch = PyObject_New(Patch, &PatchType); if (py_patch != NULL) { 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->old_oid = git_oid_allocfmt(&delta->old_file.oid); py_patch->new_oid = git_oid_allocfmt(&delta->new_file.oid); hunk_amounts = git_diff_patch_num_hunks(patch); py_patch->hunks = PyList_New(hunk_amounts); for (i=0; i < hunk_amounts; ++i) { err = git_diff_patch_get_hunk(&range, &header, &header_len, &lines_in_hunk, patch, i); if (err < 0) goto cleanup; py_hunk = PyObject_New(Hunk, &HunkType); if (py_hunk != NULL) { py_hunk->old_start = range->old_start; py_hunk->old_lines = range->old_lines; py_hunk->new_start = range->new_start; py_hunk->new_lines = range->new_lines; py_hunk->lines = PyList_New(lines_in_hunk + 1); PyList_SetItem(py_hunk->lines, 0, to_unicode_n(header, header_len, NULL, NULL)); for (j=1; j < lines_in_hunk + 1; ++j) { err = git_diff_patch_get_line_in_hunk(&py_hunk->origin, &line, &line_len, NULL, NULL, patch, i, j - 1); if (err < 0) goto cleanup; PyList_SetItem(py_hunk->lines, j, to_unicode_n(line, line_len, NULL, NULL)); } PyList_SetItem((PyObject*) py_patch->hunks, i, (PyObject*) py_hunk); } } } cleanup: git_diff_patch_free(patch); return (err < 0) ? Error_set(err) : (PyObject*) py_patch; }
void test_diff_drivers__patterns(void) { git_config *cfg; const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13"; git_tree *one; git_diff_list *diff; git_diff_patch *patch; char *text; const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n"; const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n"; const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n"; g_repo = cl_git_sandbox_init("renames"); one = resolve_commit_oid_to_tree(g_repo, one_sha); /* no diff */ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(0, (int)git_diff_num_deltas(diff)); git_diff_list_free(diff); /* default diff */ cl_git_append2file("renames/untimely.txt", "\r\nSome new stuff\r\n"); cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); cl_git_pass(git_diff_patch_to_str(&text, patch)); cl_assert_equal_s(expected0, text); git__free(text); git_diff_patch_free(patch); git_diff_list_free(diff); /* attribute diff set to false */ cl_git_rewritefile("renames/.gitattributes", "untimely.txt -diff\n"); cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); cl_git_pass(git_diff_patch_to_str(&text, patch)); cl_assert_equal_s(expected1, text); git__free(text); git_diff_patch_free(patch); git_diff_list_free(diff); /* attribute diff set to unconfigured value (should use default) */ cl_git_rewritefile("renames/.gitattributes", "untimely.txt diff=kipling0\n"); cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); cl_git_pass(git_diff_patch_to_str(&text, patch)); cl_assert_equal_s(expected0, text); git__free(text); git_diff_patch_free(patch); git_diff_list_free(diff); /* let's define that driver */ cl_git_pass(git_repository_config(&cfg, g_repo)); cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 1)); git_config_free(cfg); cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); cl_git_pass(git_diff_patch_to_str(&text, patch)); cl_assert_equal_s(expected1, text); git__free(text); git_diff_patch_free(patch); git_diff_list_free(diff); /* let's use a real driver with some regular expressions */ git_diff_driver_registry_free(g_repo->diff_drivers); g_repo->diff_drivers = NULL; cl_git_pass(git_repository_config(&cfg, g_repo)); cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0)); cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H")); git_config_free(cfg); cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); cl_git_pass(git_diff_patch_to_str(&text, patch)); cl_assert_equal_s(expected2, text); git__free(text); git_diff_patch_free(patch); git_diff_list_free(diff); git_tree_free(one); }
int diff_foreach_via_iterator( git_diff_list *diff, git_diff_file_cb file_cb, git_diff_hunk_cb hunk_cb, git_diff_data_cb line_cb, void *data) { size_t d, num_d = git_diff_num_deltas(diff); for (d = 0; d < num_d; ++d) { git_diff_patch *patch; const git_diff_delta *delta; size_t h, num_h; cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d)); cl_assert(delta); /* call file_cb for this file */ if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) { git_diff_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_diff_patch_free(patch); continue; } num_h = git_diff_patch_num_hunks(patch); for (h = 0; h < num_h; h++) { const git_diff_range *range; const char *hdr; size_t hdr_len, l, num_l; cl_git_pass(git_diff_patch_get_hunk( &range, &hdr, &hdr_len, &num_l, patch, h)); if (hunk_cb && hunk_cb(delta, range, hdr, hdr_len, data) != 0) { git_diff_patch_free(patch); goto abort; } for (l = 0; l < num_l; ++l) { char origin; const char *line; size_t line_len; int old_lineno, new_lineno; cl_git_pass(git_diff_patch_get_line_in_hunk( &origin, &line, &line_len, &old_lineno, &new_lineno, patch, h, l)); if (line_cb && line_cb(delta, range, origin, line, line_len, data) != 0) { git_diff_patch_free(patch); goto abort; } } } git_diff_patch_free(patch); } return 0; abort: giterr_clear(); return GIT_EUSER; }
void test_diff_diffiter__iterate_randomly_while_saving_state(void) { git_repository *repo = cl_git_sandbox_init("status"); git_diff_options opts = {0}; git_diff_list *diff = NULL; diff_expects exp = {0}; git_diff_patch *patches[PATCH_CACHE]; size_t p, d, num_d; memset(patches, 0, sizeof(patches)); opts.context_lines = 3; opts.interhunk_lines = 1; opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff)); num_d = git_diff_num_deltas(diff); /* To make sure that references counts work for diff and patch objects, * this generates patches and randomly caches them. Only when the patch * is removed from the cache are hunks and lines counted. At the end, * there are still patches in the cache, so free the diff and try to * process remaining patches after the diff is freed. */ srand(121212); p = rand() % PATCH_CACHE; for (d = 0; d < num_d; ++d) { /* take old patch */ git_diff_patch *patch = patches[p]; patches[p] = NULL; /* cache new patch */ cl_git_pass(git_diff_get_patch(&patches[p], NULL, diff, d)); cl_assert(patches[p] != NULL); /* process old patch if non-NULL */ if (patch != NULL) { iterate_over_patch(patch, &exp); git_diff_patch_free(patch); } p = rand() % PATCH_CACHE; } /* free diff list now - refcounts should keep things safe */ git_diff_list_free(diff); /* process remaining unprocessed patches */ for (p = 0; p < PATCH_CACHE; p++) { git_diff_patch *patch = patches[p]; if (patch != NULL) { iterate_over_patch(patch, &exp); git_diff_patch_free(patch); } } /* hopefully it all still added up right */ cl_assert_equal_i(13, exp.files); cl_assert_equal_i(8, exp.hunks); cl_assert_equal_i(14, exp.lines); }
void test_diff_diffiter__max_size_threshold(void) { git_repository *repo = cl_git_sandbox_init("status"); git_diff_options opts = {0}; git_diff_list *diff = NULL; int file_count = 0, binary_count = 0, hunk_count = 0; size_t d, num_d; opts.context_lines = 3; opts.interhunk_lines = 1; opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff)); num_d = git_diff_num_deltas(diff); for (d = 0; d < num_d; ++d) { git_diff_patch *patch; const git_diff_delta *delta; cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d)); cl_assert(delta); cl_assert(patch); file_count++; hunk_count += (int)git_diff_patch_num_hunks(patch); assert(delta->binary == 0 || delta->binary == 1); binary_count += delta->binary; git_diff_patch_free(patch); } cl_assert_equal_i(13, file_count); cl_assert_equal_i(0, binary_count); cl_assert_equal_i(8, hunk_count); git_diff_list_free(diff); /* try again with low file size threshold */ file_count = binary_count = hunk_count = 0; opts.context_lines = 3; opts.interhunk_lines = 1; opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; opts.max_size = 50; /* treat anything over 50 bytes as binary! */ cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff)); num_d = git_diff_num_deltas(diff); for (d = 0; d < num_d; ++d) { git_diff_patch *patch; const git_diff_delta *delta; cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d)); file_count++; hunk_count += (int)git_diff_patch_num_hunks(patch); assert(delta->binary == 0 || delta->binary == 1); binary_count += delta->binary; git_diff_patch_free(patch); } cl_assert_equal_i(13, file_count); /* Three files are over the 50 byte threshold: * - staged_changes_file_deleted * - staged_changes_modified_file * - staged_new_file_modified_file */ cl_assert_equal_i(3, binary_count); cl_assert_equal_i(5, hunk_count); git_diff_list_free(diff); }
void test_diff_workdir__larger_hunks(void) { const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69"; const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10"; git_tree *a, *b; git_diff_options opts = {0}; size_t i, d, num_d, h, num_h, l, num_l, header_len, line_len; g_repo = cl_git_sandbox_init("diff"); cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL); cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL); opts.context_lines = 1; opts.interhunk_lines = 0; for (i = 0; i <= 2; ++i) { git_diff_list *diff = NULL; git_diff_patch *patch; const git_diff_range *range; const char *header, *line; char origin; /* okay, this is a bit silly, but oh well */ switch (i) { case 0: cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff)); break; case 1: cl_git_pass(git_diff_workdir_to_tree(g_repo, &opts, a, &diff)); break; case 2: cl_git_pass(git_diff_workdir_to_tree(g_repo, &opts, b, &diff)); break; } num_d = git_diff_num_deltas(diff); cl_assert_equal_i(2, (int)num_d); for (d = 0; d < num_d; ++d) { cl_git_pass(git_diff_get_patch(&patch, NULL, diff, d)); cl_assert(patch); num_h = git_diff_patch_num_hunks(patch); for (h = 0; h < num_h; h++) { cl_git_pass(git_diff_patch_get_hunk( &range, &header, &header_len, &num_l, patch, h)); for (l = 0; l < num_l; ++l) { cl_git_pass(git_diff_patch_get_line_in_hunk( &origin, &line, &line_len, NULL, NULL, patch, h, l)); cl_assert(line); } /* confirm fail after the last item */ cl_git_fail(git_diff_patch_get_line_in_hunk( &origin, &line, &line_len, NULL, NULL, patch, h, num_l)); } /* confirm fail after the last item */ cl_git_fail(git_diff_patch_get_hunk( &range, &header, &header_len, &num_l, patch, num_h)); git_diff_patch_free(patch); } git_diff_list_free(diff); } git_tree_free(a); git_tree_free(b); }