/* * call-seq: * patch.each_hunk { |hunk| } -> self * patch.each_hunk -> enumerator * * If given a block, yields each hunk that is part of the patch. * * If no block is given, an enumerator is returned instead. */ static VALUE rb_git_diff_patch_each_hunk(VALUE self) { git_patch *patch; const git_diff_hunk *hunk; size_t lines_in_hunk; int error = 0; size_t hunks_count, h; if (!rb_block_given_p()) { return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_hunk"), self); } Data_Get_Struct(self, git_patch, patch); hunks_count = git_patch_num_hunks(patch); for (h = 0; h < hunks_count; ++h) { error = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, h); if (error) break; rb_yield(rugged_diff_hunk_new(self, h, hunk, lines_in_hunk)); } rugged_exception_check(error); return self; }
void test_diff_tree__larger_hunks(void) { const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69"; const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10"; size_t d, num_d, h, num_h, l, num_l; git_patch *patch; const git_diff_hunk *hunk; const git_diff_line *line; 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; cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts)); num_d = git_diff_num_deltas(diff); for (d = 0; d < num_d; ++d) { cl_git_pass(git_patch_from_diff(&patch, diff, d)); cl_assert(patch); num_h = git_patch_num_hunks(patch); for (h = 0; h < num_h; h++) { cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h)); for (l = 0; l < num_l; ++l) { cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l)); cl_assert(line); } cl_git_fail(git_patch_get_line_in_hunk(&line, patch, h, num_l)); } cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h)); git_patch_free(patch); } cl_git_fail(git_patch_from_diff(&patch, diff, num_d)); cl_assert_equal_i(2, (int)num_d); }
/** * ggit_patch_get_hunk: * @patch: a #GgitPatch * @idx: the hunk index. * @error: a #GError * * Get the @idx'th hunk in the patch. * * Returns: (transfer full): a new #GgitDiffHunk or %NULL on error. */ GgitDiffHunk * ggit_patch_get_hunk (GgitPatch *patch, gsize idx, GError **error) { const git_diff_hunk *hunk; size_t tlines; gint ret; g_return_val_if_fail (patch != NULL, NULL); ret = git_patch_get_hunk (&hunk, &tlines, patch->patch, idx); if (ret != GIT_OK) { _ggit_error_set (error, ret); return NULL; } return _ggit_diff_hunk_wrap (hunk); }
PyObject * wrap_diff_hunk(git_patch *patch, size_t idx) { DiffHunk *py_hunk; const git_diff_hunk *hunk; const git_diff_line *line; size_t j, lines_in_hunk; int err; err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, idx); if (err < 0) return Error_set(err); py_hunk = PyObject_New(DiffHunk, &DiffHunkType); if (py_hunk) { 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->header = to_unicode_n((const char *) &hunk->header, hunk->header_len, NULL, NULL); py_hunk->lines = PyList_New(lines_in_hunk); for (j = 0; j < lines_in_hunk; ++j) { PyObject *py_line = NULL; err = git_patch_get_line_in_hunk(&line, patch, idx, j); if (err < 0) return Error_set(err); py_line = wrap_diff_line(line); if (py_line == NULL) return NULL; PyList_SetItem(py_hunk->lines, j, py_line); } } return (PyObject *) py_hunk; }
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 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 QGit::commitDiffContent(QString first, QString second, QList<QString> files) { git_commit *first_commit = nullptr, *second_commit = nullptr; git_object *first_obj = nullptr, *second_obj = nullptr; git_tree *first_tree = nullptr, *second_tree = nullptr; const git_diff_delta *delta = nullptr; git_repository *repo = nullptr; git_patch *patch = nullptr; QList<QGitDiffFile> items; git_diff *diff = nullptr; QGitError error; int res = 0; try { res = git_repository_open(&repo, m_path.absolutePath().toUtf8().constData()); if (res) { throw QGitError("git_repository_open", res); } if (!first.isEmpty()) { res = git_revparse_single(&first_obj, repo, first.toLatin1()); if (res) { throw QGitError("git_revparse_single(first)", res); } res = git_commit_lookup(&first_commit, repo, git_object_id(first_obj)); if (res) { throw QGitError("git_commit_lookup(first)", res); } res = git_commit_tree(&first_tree, first_commit); if (res) { throw QGitError("git_commit_tree(first)", res); } } if (second == "staged") { git_diff_options options; res = git_revparse_single(&first_obj, repo, "HEAD^{tree}"); if (res) { throw QGitError("git_revparse_single(staged)", res); } res = git_tree_lookup(&first_tree, repo, git_object_id(first_obj)); if (res) { throw QGitError("git_tree_lookup(staged)", res); } memset(&options, 0, sizeof(options)); options.version = GIT_DIFF_OPTIONS_VERSION; options.flags = GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT; options.context_lines = 3; res = git_diff_tree_to_index(&diff, repo, first_tree, nullptr, &options); if (res) { throw QGitError("git_diff_tree_to_index(staged)", res); } } else if (second == "unstaged") { git_diff_options options; memset(&options, 0, sizeof(options)); options.version = GIT_DIFF_OPTIONS_VERSION; options.flags = GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT; options.context_lines = 3; res = git_diff_index_to_workdir(&diff, repo, nullptr, &options); if (res) { throw QGitError("git_diff_index_to_workdir", res); } } else { res = git_revparse_single(&second_obj, repo, second.toLatin1()); if (res) { throw QGitError("git_revparse_single(second)", res); } res = git_commit_lookup(&second_commit, repo, git_object_id(second_obj)); if (res) { throw QGitError("git_commit_lookup(second)", res); } res = git_commit_tree(&second_tree, second_commit); if (res) { throw QGitError("git_commit_tree(second)", res); } res = git_diff_tree_to_tree(&diff, repo, first_tree, second_tree, nullptr); if (res) { throw QGitError("git_diff_tree_to_tree", res); } } size_t _count = git_diff_num_deltas(diff); for(size_t c = 0; c < _count; c++) { delta = git_diff_get_delta(diff, c); QGitDiffFile item(delta); if (files.contains(item.new_file().path())) { res = git_patch_from_diff(&patch, diff, c); if (res) { throw QGitError("git_patch_from_diff", res); } size_t hunks = git_patch_num_hunks(patch); for(size_t hunk_cnt = 0; hunk_cnt < hunks; hunk_cnt++) { const git_diff_hunk *hunk = nullptr; res = git_patch_get_hunk(&hunk, nullptr, patch, hunk_cnt); if (res) { throw QGitError("git_patch_get_hunk", res); } QGitDiffHunk hunkObj(hunk); const git_diff_line *line = nullptr; int lines = git_patch_num_lines_in_hunk(patch, hunk_cnt); if (lines > 0) { for(int line_cnt = 0; line_cnt < lines; line_cnt++) { res = git_patch_get_line_in_hunk(&line, patch, hunk_cnt, static_cast<size_t>(line_cnt)); if (res) { throw QGitError("git_patch_get_line_in_hunk", res); } hunkObj.addLine(line); } } item.addHunk(hunkObj); } items.append(item); } } } catch(const QGitError &ex) { error = ex; } emit commitDiffContentReply(first, second, items, error); }