summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2012-10-24 20:56:32 -0700
committerRussell Belfer <rb@github.com>2012-10-24 20:56:32 -0700
commit93cf7bb8e26a04d9bd4197c1b938cee352023f63 (patch)
tree34fc8c5fbf63e3962573d768c252197bcdf177e3
parent6f6b0c013c6eff2aca2a7ada1027044f2e20f578 (diff)
downloadlibgit2-93cf7bb8e26a04d9bd4197c1b938cee352023f63.tar.gz
Add git_diff_patch_to_str API
This adds an API to generate a complete single-file patch text from a git_diff_patch object.
-rw-r--r--include/git2/diff.h11
-rw-r--r--src/diff_output.c54
-rw-r--r--tests-clar/diff/diffiter.c99
-rw-r--r--tests-clar/diff/patch.c30
4 files changed, 194 insertions, 0 deletions
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 1932db029..1c2a2f83a 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -603,6 +603,17 @@ GIT_EXTERN(int) git_diff_patch_get_line_in_hunk(
size_t hunk_idx,
size_t line_of_hunk);
+/**
+ * Get the content of a patch as a single diff text.
+ *
+ * @param string Allocated string; caller must free.
+ * @param patch The patch to generate a string from.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_diff_patch_to_str(
+ char **string,
+ git_diff_patch *patch);
+
/**@}*/
diff --git a/src/diff_output.c b/src/diff_output.c
index 5f0d13c64..511e9318b 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -1502,3 +1502,57 @@ notfound:
return GIT_ENOTFOUND;
}
+static int print_to_buffer_cb(
+ void *cb_data,
+ const git_diff_delta *delta,
+ const git_diff_range *range,
+ char line_origin,
+ const char *content,
+ size_t content_len)
+{
+ git_buf *output = cb_data;
+ GIT_UNUSED(delta);
+ GIT_UNUSED(range);
+ GIT_UNUSED(line_origin);
+ git_buf_put(output, content, content_len);
+ return 0;
+}
+
+int git_diff_patch_to_str(
+ char **string,
+ git_diff_patch *patch)
+{
+ int error;
+ git_buf output = GIT_BUF_INIT, temp = GIT_BUF_INIT;
+ diff_print_info pi;
+ size_t h, l;
+
+ pi.diff = patch->diff;
+ pi.print_cb = print_to_buffer_cb;
+ pi.cb_data = &output;
+ pi.buf = &temp;
+
+ error = print_patch_file(&pi, patch->delta, 0);
+
+ for (h = 0; h < patch->hunks_size; ++h) {
+ diff_patch_hunk *hunk = &patch->hunks[h];
+
+ error = print_patch_hunk(&pi, patch->delta,
+ &hunk->range, hunk->header, hunk->header_len);
+
+ for (l = 0; l < hunk->line_count; ++l) {
+ diff_patch_line *line = &patch->lines[hunk->line_start + l];
+
+ error = print_patch_line(
+ &pi, patch->delta, &hunk->range,
+ line->origin, line->ptr, line->len);
+ }
+ }
+
+ git_buf_free(&temp);
+
+ *string = git_buf_detach(&output);
+
+ return error;
+}
+
diff --git a/tests-clar/diff/diffiter.c b/tests-clar/diff/diffiter.c
index f6d9bfc38..86e8d1f57 100644
--- a/tests-clar/diff/diffiter.c
+++ b/tests-clar/diff/diffiter.c
@@ -342,3 +342,102 @@ void test_diff_diffiter__iterate_randomly_while_saving_state(void)
cl_assert_equal_i(8, exp.hunks);
cl_assert_equal_i(14, exp.lines);
}
+
+/* This output is taken directly from `git diff` on the status test data */
+static const char *expected_patch_text[8] = {
+ /* 0 */
+ "diff --git a/file_deleted b/file_deleted\n"
+ "deleted file mode 100644\n"
+ "index 5452d32..0000000\n"
+ "--- a/file_deleted\n"
+ "+++ /dev/null\n"
+ "@@ -1 +0,0 @@\n"
+ "-file_deleted\n",
+ /* 1 */
+ "diff --git a/modified_file b/modified_file\n"
+ "index 452e424..0a53963 100644\n"
+ "--- a/modified_file\n"
+ "+++ b/modified_file\n"
+ "@@ -1 +1,2 @@\n"
+ " modified_file\n"
+ "+modified_file\n",
+ /* 2 */
+ "diff --git a/staged_changes_file_deleted b/staged_changes_file_deleted\n"
+ "deleted file mode 100644\n"
+ "index a6be623..0000000\n"
+ "--- a/staged_changes_file_deleted\n"
+ "+++ /dev/null\n"
+ "@@ -1,2 +0,0 @@\n"
+ "-staged_changes_file_deleted\n"
+ "-staged_changes_file_deleted\n",
+ /* 3 */
+ "diff --git a/staged_changes_modified_file b/staged_changes_modified_file\n"
+ "index 906ee77..011c344 100644\n"
+ "--- a/staged_changes_modified_file\n"
+ "+++ b/staged_changes_modified_file\n"
+ "@@ -1,2 +1,3 @@\n"
+ " staged_changes_modified_file\n"
+ " staged_changes_modified_file\n"
+ "+staged_changes_modified_file\n",
+ /* 4 */
+ "diff --git a/staged_new_file_deleted_file b/staged_new_file_deleted_file\n"
+ "deleted file mode 100644\n"
+ "index 90b8c29..0000000\n"
+ "--- a/staged_new_file_deleted_file\n"
+ "+++ /dev/null\n"
+ "@@ -1 +0,0 @@\n"
+ "-staged_new_file_deleted_file\n",
+ /* 5 */
+ "diff --git a/staged_new_file_modified_file b/staged_new_file_modified_file\n"
+ "index ed06290..8b090c0 100644\n"
+ "--- a/staged_new_file_modified_file\n"
+ "+++ b/staged_new_file_modified_file\n"
+ "@@ -1 +1,2 @@\n"
+ " staged_new_file_modified_file\n"
+ "+staged_new_file_modified_file\n",
+ /* 6 */
+ "diff --git a/subdir/deleted_file b/subdir/deleted_file\n"
+ "deleted file mode 100644\n"
+ "index 1888c80..0000000\n"
+ "--- a/subdir/deleted_file\n"
+ "+++ /dev/null\n"
+ "@@ -1 +0,0 @@\n"
+ "-subdir/deleted_file\n",
+ /* 7 */
+ "diff --git a/subdir/modified_file b/subdir/modified_file\n"
+ "index a619198..57274b7 100644\n"
+ "--- a/subdir/modified_file\n"
+ "+++ b/subdir/modified_file\n"
+ "@@ -1 +1,2 @@\n"
+ " subdir/modified_file\n"
+ "+subdir/modified_file\n"
+};
+
+void test_diff_diffiter__iterate_and_generate_patch_text(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_list *diff;
+ size_t d, num_d;
+
+ cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff));
+
+ num_d = git_diff_num_deltas(diff);
+ cl_assert_equal_i(8, (int)num_d);
+
+ for (d = 0; d < num_d; ++d) {
+ git_diff_patch *patch;
+ char *text;
+
+ cl_git_pass(git_diff_get_patch(&patch, NULL, diff, d));
+ cl_assert(patch != NULL);
+
+ cl_git_pass(git_diff_patch_to_str(&text, patch));
+
+ cl_assert_equal_s(expected_patch_text[d], text);
+
+ git__free(text);
+ git_diff_patch_free(patch);
+ }
+
+ git_diff_list_free(diff);
+}
diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c
index e8468386c..dce6d6da2 100644
--- a/tests-clar/diff/patch.c
+++ b/tests-clar/diff/patch.c
@@ -97,3 +97,33 @@ void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
git_tree_free(another);
git_tree_free(one);
}
+
+void test_diff_patch__to_string(void)
+{
+ const char *one_sha = "26a125e";
+ const char *another_sha = "735b6a2";
+ git_tree *one, *another;
+ git_diff_list *diff;
+ git_diff_patch *patch;
+ char *text;
+ const char *expected = "diff --git a/subdir.txt b/subdir.txt\ndeleted file mode 100644\nindex e8ee89e..0000000\n--- a/subdir.txt\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Is it a bird?\n-Is it a plane?\n";
+
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+ another = resolve_commit_oid_to_tree(g_repo, another_sha);
+
+ cl_git_pass(git_diff_tree_to_tree(g_repo, NULL, one, another, &diff));
+
+ cl_assert_equal_i(1, 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(expected, text);
+
+ git__free(text);
+ git_diff_patch_free(patch);
+ git_diff_list_free(diff);
+ git_tree_free(another);
+ git_tree_free(one);
+}