]> git.proxmox.com Git - libgit2.git/blame - tests/diff/patch.c
New upstream version 1.3.0+dfsg.1
[libgit2.git] / tests / diff / patch.c
CommitLineData
eb3d71a5 1#include "clar_libgit2.h"
1384b688
RB
2#include "git2/sys/repository.h"
3
eb3d71a5 4#include "diff_helpers.h"
e349ed50 5#include "diff.h"
805c476c 6#include "repository.h"
eb3d71a5 7
8static git_repository *g_repo = NULL;
9
10void test_diff_patch__initialize(void)
11{
eb3d71a5 12}
13
14void test_diff_patch__cleanup(void)
15{
16 cl_git_sandbox_cleanup();
17}
18
1d2dd864 19#define EXPECTED_HEADER "diff --git a/subdir.txt b/subdir.txt\n" \
eb3d71a5 20 "deleted file mode 100644\n" \
21 "index e8ee89e..0000000\n" \
22 "--- a/subdir.txt\n" \
23 "+++ /dev/null\n"
24
1d2dd864 25#define EXPECTED_HUNK "@@ -1,2 +0,0 @@\n"
26
ac3d33df
JK
27#define UTF8_HUNK_HEADER "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\n"
28
29#define UTF8_TRUNCATED_A_HUNK_HEADER "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\n"
30
31#define UTF8_TRUNCATED_L_HUNK_HEADER "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\n"
32
eb3d71a5 33static int check_removal_cb(
bae957b9 34 const git_diff_delta *delta,
3b5f7954
RB
35 const git_diff_hunk *hunk,
36 const git_diff_line *line,
793c4385 37 void *payload)
eb3d71a5 38{
3b5f7954 39 switch (line->origin) {
1d2dd864 40 case GIT_DIFF_LINE_FILE_HDR:
3b5f7954
RB
41 cl_assert_equal_s(EXPECTED_HEADER, line->content);
42 cl_assert(hunk == NULL);
1d2dd864 43 goto check_delta;
44
45 case GIT_DIFF_LINE_HUNK_HDR:
3b5f7954 46 cl_assert_equal_s(EXPECTED_HUNK, line->content);
11bd7a03 47 goto check_hunk;
1d2dd864 48
49 case GIT_DIFF_LINE_CONTEXT:
50 case GIT_DIFF_LINE_DELETION:
11bd7a03
RB
51 if (payload != NULL)
52 return *(int *)payload;
3b5f7954 53 goto check_hunk;
1d2dd864 54
55 default:
56 /* unexpected code path */
57 return -1;
58 }
59
3b5f7954
RB
60check_hunk:
61 cl_assert(hunk != NULL);
62 cl_assert_equal_i(1, hunk->old_start);
63 cl_assert_equal_i(2, hunk->old_lines);
64 cl_assert_equal_i(0, hunk->new_start);
65 cl_assert_equal_i(0, hunk->new_lines);
eb3d71a5 66
1d2dd864 67check_delta:
16b83019
RB
68 cl_assert_equal_s("subdir.txt", delta->old_file.path);
69 cl_assert_equal_s("subdir.txt", delta->new_file.path);
1d2dd864 70 cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
eb3d71a5 71
1d2dd864 72 return 0;
eb3d71a5 73}
74
75void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
76{
77 /*
78 * $ git diff 26a125e..735b6a2
79 * diff --git a/subdir.txt b/subdir.txt
80 * deleted file mode 100644
81 * index e8ee89e..0000000
82 * --- a/subdir.txt
83 * +++ /dev/null
84 * @@ -1,2 +0,0 @@
85 * -Is it a bird?
86 * -Is it a plane?
87 */
88
89 const char *one_sha = "26a125e";
90 const char *another_sha = "735b6a2";
91 git_tree *one, *another;
3ff1d123 92 git_diff *diff;
eb3d71a5 93
805c476c
RB
94 g_repo = cl_git_sandbox_init("status");
95
eb3d71a5 96 one = resolve_commit_oid_to_tree(g_repo, one_sha);
97 another = resolve_commit_oid_to_tree(g_repo, another_sha);
98
5735bf5e 99 cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
eb3d71a5 100
10672e3e
RB
101 cl_git_pass(git_diff_print(
102 diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, NULL));
eb3d71a5 103
3ff1d123 104 git_diff_free(diff);
eb3d71a5 105
106 git_tree_free(another);
107 git_tree_free(one);
108}
93cf7bb8 109
11bd7a03
RB
110void test_diff_patch__can_cancel_diff_print(void)
111{
112 const char *one_sha = "26a125e";
113 const char *another_sha = "735b6a2";
114 git_tree *one, *another;
115 git_diff *diff;
116 int fail_with;
117
118 g_repo = cl_git_sandbox_init("status");
119
120 one = resolve_commit_oid_to_tree(g_repo, one_sha);
121 another = resolve_commit_oid_to_tree(g_repo, another_sha);
122
123 cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
124
125 fail_with = -2323;
126
127 cl_git_fail_with(git_diff_print(
128 diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with),
129 fail_with);
130
131 fail_with = 45;
132
133 cl_git_fail_with(git_diff_print(
134 diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with),
135 fail_with);
136
137 git_diff_free(diff);
138
139 git_tree_free(another);
140 git_tree_free(one);
141}
142
93cf7bb8
RB
143void test_diff_patch__to_string(void)
144{
145 const char *one_sha = "26a125e";
146 const char *another_sha = "735b6a2";
147 git_tree *one, *another;
3ff1d123
RB
148 git_diff *diff;
149 git_patch *patch;
c05cd792 150 git_buf buf = GIT_BUF_INIT;
93cf7bb8
RB
151 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";
152
805c476c
RB
153 g_repo = cl_git_sandbox_init("status");
154
93cf7bb8
RB
155 one = resolve_commit_oid_to_tree(g_repo, one_sha);
156 another = resolve_commit_oid_to_tree(g_repo, another_sha);
157
5735bf5e 158 cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
93cf7bb8 159
a8122b5d 160 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
93cf7bb8 161
10672e3e 162 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
93cf7bb8 163
c05cd792 164 cl_git_pass(git_patch_to_buf(&buf, patch));
93cf7bb8 165
c05cd792 166 cl_assert_equal_s(expected, buf.ptr);
93cf7bb8 167
3ff1d123
RB
168 cl_assert_equal_sz(31, git_patch_size(patch, 0, 0, 0));
169 cl_assert_equal_sz(31, git_patch_size(patch, 1, 0, 0));
170 cl_assert_equal_sz(31 + 16, git_patch_size(patch, 1, 1, 0));
171 cl_assert_equal_sz(strlen(expected), git_patch_size(patch, 1, 1, 1));
b4a4cf24 172
ac3d33df 173 git_buf_dispose(&buf);
3ff1d123
RB
174 git_patch_free(patch);
175 git_diff_free(diff);
93cf7bb8
RB
176 git_tree_free(another);
177 git_tree_free(one);
178}
805c476c 179
b1ff7004
RB
180void test_diff_patch__config_options(void)
181{
182 const char *one_sha = "26a125e"; /* current HEAD */
183 git_tree *one;
184 git_config *cfg;
3ff1d123
RB
185 git_diff *diff;
186 git_patch *patch;
c05cd792 187 git_buf buf = GIT_BUF_INIT;
b1ff7004
RB
188 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
189 char *onefile = "staged_changes_modified_file";
190 const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
191 const char *expected2 = "diff --git i/staged_changes_modified_file w/staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- i/staged_changes_modified_file\n+++ w/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";
192 const char *expected3 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- staged_changes_modified_file\n+++ 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";
193 const char *expected4 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 70bd9443ada0..906ee7711f4f 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
194
195 g_repo = cl_git_sandbox_init("status");
196 cl_git_pass(git_repository_config(&cfg, g_repo));
197 one = resolve_commit_oid_to_tree(g_repo, one_sha);
198 opts.pathspec.count = 1;
199 opts.pathspec.strings = &onefile;
200
201
202 cl_git_pass(git_config_set_string(cfg, "diff.mnemonicprefix", "true"));
203
204 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
205
206 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
10672e3e 207 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
c05cd792
NH
208 cl_git_pass(git_patch_to_buf(&buf, patch));
209 cl_assert_equal_s(expected1, buf.ptr);
b1ff7004 210
c05cd792 211 git_buf_clear(&buf);
3ff1d123
RB
212 git_patch_free(patch);
213 git_diff_free(diff);
b1ff7004
RB
214
215 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
216
217 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
10672e3e 218 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
c05cd792
NH
219 cl_git_pass(git_patch_to_buf(&buf, patch));
220 cl_assert_equal_s(expected2, buf.ptr);
b1ff7004 221
c05cd792 222 git_buf_clear(&buf);
3ff1d123
RB
223 git_patch_free(patch);
224 git_diff_free(diff);
b1ff7004
RB
225
226
227 cl_git_pass(git_config_set_string(cfg, "diff.noprefix", "true"));
228
229 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
230
231 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
10672e3e 232 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
c05cd792
NH
233 cl_git_pass(git_patch_to_buf(&buf, patch));
234 cl_assert_equal_s(expected3, buf.ptr);
b1ff7004 235
c05cd792 236 git_buf_clear(&buf);
3ff1d123
RB
237 git_patch_free(patch);
238 git_diff_free(diff);
b1ff7004
RB
239
240
241 cl_git_pass(git_config_set_int32(cfg, "core.abbrev", 12));
242
243 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
244
245 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
10672e3e 246 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
c05cd792
NH
247 cl_git_pass(git_patch_to_buf(&buf, patch));
248 cl_assert_equal_s(expected4, buf.ptr);
b1ff7004 249
c05cd792 250 git_buf_clear(&buf);
3ff1d123
RB
251 git_patch_free(patch);
252 git_diff_free(diff);
b1ff7004 253
ac3d33df 254 git_buf_dispose(&buf);
b1ff7004
RB
255 git_tree_free(one);
256 git_config_free(cfg);
257}
258
805c476c
RB
259void test_diff_patch__hunks_have_correct_line_numbers(void)
260{
7d46b34b 261 git_config *cfg;
805c476c 262 git_tree *head;
7d46b34b 263 git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
3ff1d123
RB
264 git_diff *diff;
265 git_patch *patch;
805c476c 266 const git_diff_delta *delta;
3b5f7954
RB
267 const git_diff_hunk *hunk;
268 const git_diff_line *line;
269 size_t hunklen;
c2f602f8 270 git_buf old_content = GIT_BUF_INIT, actual = GIT_BUF_INIT;
6f9d5ce8 271 const char *new_content = "The Song of Seven Cities\n------------------------\n\nI WAS Lord of Cities very sumptuously builded.\nSeven roaring Cities paid me tribute from afar.\nIvory their outposts were--the guardrooms of them gilded,\nAnd garrisoned with Amazons invincible in war.\n\nThis is some new text;\nNot as good as the old text;\nBut here it is.\n\nSo they warred and trafficked only yesterday, my Cities.\nTo-day there is no mark or mound of where my Cities stood.\nFor the River rose at midnight and it washed away my Cities.\nThey are evened with Atlantis and the towns before the Flood.\n\nRain on rain-gorged channels raised the water-levels round them,\nFreshet backed on freshet swelled and swept their world from sight,\nTill the emboldened floods linked arms and, flashing forward, drowned them--\nDrowned my Seven Cities and their peoples in one night!\n\nLow among the alders lie their derelict foundations,\nThe beams wherein they trusted and the plinths whereon they built--\nMy rulers and their treasure and their unborn populations,\nDead, destroyed, aborted, and defiled with mud and silt!\n\nAnother replacement;\nBreaking up the poem;\nGenerating some hunks.\n\nTo the sound of trumpets shall their seed restore my Cities\nWealthy and well-weaponed, that once more may I behold\nAll the world go softly when it walks before my Cities,\nAnd the horses and the chariots fleeing from them as of old!\n\n -- Rudyard Kipling\n";
805c476c
RB
272
273 g_repo = cl_git_sandbox_init("renames");
274
487fc724
RB
275 cl_git_pass(git_config_new(&cfg));
276 git_repository_set_config(g_repo, cfg);
5173ea92
RB
277 git_config_free(cfg);
278
867f7c9b 279 git_repository_reinit_filesystem(g_repo, false);
7d46b34b 280
c2f602f8
RB
281 cl_git_pass(
282 git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
283
6f9d5ce8 284 cl_git_rewritefile("renames/songof7cities.txt", new_content);
805c476c
RB
285
286 cl_git_pass(git_repository_head_tree(&head, g_repo));
287
7d46b34b 288 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
805c476c
RB
289
290 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
291
10672e3e
RB
292 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
293 cl_assert((delta = git_patch_get_delta(patch)) != NULL);
805c476c
RB
294
295 cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
3ff1d123 296 cl_assert_equal_i(2, (int)git_patch_num_hunks(patch));
805c476c
RB
297
298 /* check hunk 0 */
299
300 cl_git_pass(
3b5f7954 301 git_patch_get_hunk(&hunk, &hunklen, patch, 0));
805c476c
RB
302
303 cl_assert_equal_i(18, (int)hunklen);
304
3b5f7954
RB
305 cl_assert_equal_i(6, (int)hunk->old_start);
306 cl_assert_equal_i(15, (int)hunk->old_lines);
307 cl_assert_equal_i(6, (int)hunk->new_start);
308 cl_assert_equal_i(9, (int)hunk->new_lines);
805c476c 309
3ff1d123 310 cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 0));
805c476c 311
3b5f7954
RB
312 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 0));
313 cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
314 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 315 cl_assert_equal_s("Ivory their outposts were--the guardrooms of them gilded,\n", actual.ptr);
3b5f7954
RB
316 cl_assert_equal_i(6, line->old_lineno);
317 cl_assert_equal_i(6, line->new_lineno);
d8e7ffc2 318 cl_assert_equal_i(-1, line->content_offset);
805c476c 319
3b5f7954
RB
320 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
321 cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
322 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 323 cl_assert_equal_s("All the world went softly when it walked before my Cities--\n", actual.ptr);
3b5f7954
RB
324 cl_assert_equal_i(9, line->old_lineno);
325 cl_assert_equal_i(-1, line->new_lineno);
d8e7ffc2 326 cl_assert_equal_i(252, line->content_offset);
805c476c 327
3b5f7954
RB
328 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 12));
329 cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
330 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 331 cl_assert_equal_s("This is some new text;\n", actual.ptr);
3b5f7954
RB
332 cl_assert_equal_i(-1, line->old_lineno);
333 cl_assert_equal_i(9, line->new_lineno);
d8e7ffc2 334 cl_assert_equal_i(252, line->content_offset);
805c476c
RB
335
336 /* check hunk 1 */
337
3b5f7954 338 cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 1));
805c476c
RB
339
340 cl_assert_equal_i(18, (int)hunklen);
341
3b5f7954
RB
342 cl_assert_equal_i(31, (int)hunk->old_start);
343 cl_assert_equal_i(15, (int)hunk->old_lines);
344 cl_assert_equal_i(25, (int)hunk->new_start);
345 cl_assert_equal_i(9, (int)hunk->new_lines);
805c476c 346
3ff1d123 347 cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 1));
805c476c 348
3b5f7954
RB
349 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 0));
350 cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
351 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 352 cl_assert_equal_s("My rulers and their treasure and their unborn populations,\n", actual.ptr);
3b5f7954
RB
353 cl_assert_equal_i(31, line->old_lineno);
354 cl_assert_equal_i(25, line->new_lineno);
d8e7ffc2 355 cl_assert_equal_i(-1, line->content_offset);
805c476c 356
3b5f7954
RB
357 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 3));
358 cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
359 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 360 cl_assert_equal_s("The Daughters of the Palace whom they cherished in my Cities,\n", actual.ptr);
3b5f7954
RB
361 cl_assert_equal_i(34, line->old_lineno);
362 cl_assert_equal_i(-1, line->new_lineno);
d8e7ffc2 363 cl_assert_equal_i(1468, line->content_offset);
805c476c 364
3b5f7954
RB
365 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 12));
366 cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
367 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 368 cl_assert_equal_s("Another replacement;\n", actual.ptr);
3b5f7954
RB
369 cl_assert_equal_i(-1, line->old_lineno);
370 cl_assert_equal_i(28, line->new_lineno);
d8e7ffc2 371 cl_assert_equal_i(1066, line->content_offset);
805c476c 372
3ff1d123
RB
373 git_patch_free(patch);
374 git_diff_free(diff);
c2f602f8
RB
375
376 /* Let's check line numbers when there is no newline */
377
378 git_buf_rtrim(&old_content);
379 cl_git_rewritefile("renames/songof7cities.txt", old_content.ptr);
380
381 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
382
383 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
384
10672e3e
RB
385 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
386 cl_assert((delta = git_patch_get_delta(patch)) != NULL);
c2f602f8
RB
387
388 cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
3ff1d123 389 cl_assert_equal_i(1, (int)git_patch_num_hunks(patch));
c2f602f8
RB
390
391 /* check hunk 0 */
392
3b5f7954 393 cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 0));
c2f602f8
RB
394
395 cl_assert_equal_i(6, (int)hunklen);
396
3b5f7954
RB
397 cl_assert_equal_i(46, (int)hunk->old_start);
398 cl_assert_equal_i(4, (int)hunk->old_lines);
399 cl_assert_equal_i(46, (int)hunk->new_start);
400 cl_assert_equal_i(4, (int)hunk->new_lines);
c2f602f8 401
3ff1d123 402 cl_assert_equal_i(6, (int)git_patch_num_lines_in_hunk(patch, 0));
c2f602f8 403
3b5f7954
RB
404 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 1));
405 cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
406 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 407 cl_assert_equal_s("And the horses and the chariots fleeing from them as of old!\n", actual.ptr);
3b5f7954
RB
408 cl_assert_equal_i(47, line->old_lineno);
409 cl_assert_equal_i(47, line->new_lineno);
c2f602f8 410
3b5f7954
RB
411 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 2));
412 cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
413 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 414 cl_assert_equal_s("\n", actual.ptr);
3b5f7954
RB
415 cl_assert_equal_i(48, line->old_lineno);
416 cl_assert_equal_i(48, line->new_lineno);
c2f602f8 417
3b5f7954
RB
418 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
419 cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
420 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 421 cl_assert_equal_s(" -- Rudyard Kipling\n", actual.ptr);
3b5f7954
RB
422 cl_assert_equal_i(49, line->old_lineno);
423 cl_assert_equal_i(-1, line->new_lineno);
c2f602f8 424
3b5f7954
RB
425 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 4));
426 cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
427 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 428 cl_assert_equal_s(" -- Rudyard Kipling", actual.ptr);
3b5f7954
RB
429 cl_assert_equal_i(-1, line->old_lineno);
430 cl_assert_equal_i(49, line->new_lineno);
c2f602f8 431
3b5f7954
RB
432 cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 5));
433 cl_assert_equal_i(GIT_DIFF_LINE_DEL_EOFNL, (int)line->origin);
434 cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
c2f602f8 435 cl_assert_equal_s("\n\\ No newline at end of file\n", actual.ptr);
3b5f7954
RB
436 cl_assert_equal_i(-1, line->old_lineno);
437 cl_assert_equal_i(49, line->new_lineno);
c2f602f8 438
3ff1d123
RB
439 git_patch_free(patch);
440 git_diff_free(diff);
c2f602f8 441
ac3d33df
JK
442 git_buf_dispose(&actual);
443 git_buf_dispose(&old_content);
805c476c
RB
444 git_tree_free(head);
445}
f1e2735c
RB
446
447static void check_single_patch_stats(
fd96f98e 448 git_repository *repo, size_t hunks,
197b8966 449 size_t adds, size_t dels, size_t ctxt, size_t *sizes,
fd96f98e 450 const char *expected)
f1e2735c 451{
3ff1d123
RB
452 git_diff *diff;
453 git_patch *patch;
f1e2735c 454 const git_diff_delta *delta;
e35e2684 455 size_t actual_ctxt, actual_adds, actual_dels;
f1e2735c
RB
456
457 cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
458
459 cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
460
10672e3e
RB
461 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
462 cl_assert((delta = git_patch_get_delta(patch)) != NULL);
f1e2735c
RB
463 cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
464
3ff1d123 465 cl_assert_equal_i((int)hunks, (int)git_patch_num_hunks(patch));
f1e2735c 466
3ff1d123 467 cl_git_pass( git_patch_line_stats(
e35e2684 468 &actual_ctxt, &actual_adds, &actual_dels, patch) );
f1e2735c 469
e35e2684 470 cl_assert_equal_sz(ctxt, actual_ctxt);
3ad05221 471 cl_assert_equal_sz(adds, actual_adds);
472 cl_assert_equal_sz(dels, actual_dels);
f1e2735c 473
fd96f98e 474 if (expected != NULL) {
c05cd792
NH
475 git_buf buf = GIT_BUF_INIT;
476 cl_git_pass(git_patch_to_buf(&buf, patch));
477 cl_assert_equal_s(expected, buf.ptr);
ac3d33df 478 git_buf_dispose(&buf);
fd96f98e 479
b4a4cf24 480 cl_assert_equal_sz(
3ff1d123 481 strlen(expected), git_patch_size(patch, 1, 1, 1));
197b8966
RB
482 }
483
484 if (sizes) {
485 if (sizes[0])
3ff1d123 486 cl_assert_equal_sz(sizes[0], git_patch_size(patch, 0, 0, 0));
197b8966 487 if (sizes[1])
3ff1d123 488 cl_assert_equal_sz(sizes[1], git_patch_size(patch, 1, 0, 0));
197b8966 489 if (sizes[2])
3ff1d123 490 cl_assert_equal_sz(sizes[2], git_patch_size(patch, 1, 1, 0));
197b8966 491 }
b4a4cf24 492
c2f602f8
RB
493 /* walk lines in hunk with basic sanity checks */
494 for (; hunks > 0; --hunks) {
495 size_t i, max_i;
3b5f7954
RB
496 const git_diff_line *line;
497 int last_new_lineno = -1, last_old_lineno = -1;
c2f602f8 498
3ff1d123 499 max_i = git_patch_num_lines_in_hunk(patch, hunks - 1);
c2f602f8
RB
500
501 for (i = 0; i < max_i; ++i) {
502 int expected = 1;
503
3b5f7954
RB
504 cl_git_pass(
505 git_patch_get_line_in_hunk(&line, patch, hunks - 1, i));
c2f602f8 506
3b5f7954
RB
507 if (line->origin == GIT_DIFF_LINE_ADD_EOFNL ||
508 line->origin == GIT_DIFF_LINE_DEL_EOFNL ||
509 line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
c2f602f8
RB
510 expected = 0;
511
3b5f7954
RB
512 if (line->old_lineno >= 0) {
513 if (last_old_lineno >= 0)
514 cl_assert_equal_i(
515 expected, line->old_lineno - last_old_lineno);
516 last_old_lineno = line->old_lineno;
c2f602f8 517 }
3b5f7954
RB
518
519 if (line->new_lineno >= 0) {
520 if (last_new_lineno >= 0)
521 cl_assert_equal_i(
522 expected, line->new_lineno - last_new_lineno);
523 last_new_lineno = line->new_lineno;
c2f602f8
RB
524 }
525 }
526 }
527
3ff1d123
RB
528 git_patch_free(patch);
529 git_diff_free(diff);
f1e2735c
RB
530}
531
532void test_diff_patch__line_counts_with_eofnl(void)
533{
7d46b34b 534 git_config *cfg;
f1e2735c
RB
535 git_buf content = GIT_BUF_INIT;
536 const char *end;
537 git_index *index;
197b8966
RB
538 const char *expected =
539 /* below is pasted output of 'git diff' with fn context removed */
540 "diff --git a/songof7cities.txt b/songof7cities.txt\n"
541 "index 378a7d9..3d0154e 100644\n"
542 "--- a/songof7cities.txt\n"
543 "+++ b/songof7cities.txt\n"
544 "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n"
545 " \n"
546 " To the sound of trumpets shall their seed restore my Cities\n"
547 " Wealthy and well-weaponed, that once more may I behold\n"
548 "-All the world go softly when it walks before my Cities,\n"
549 "+#All the world go softly when it walks before my Cities,\n"
550 " And the horses and the chariots fleeing from them as of old!\n"
551 " \n"
552 " -- Rudyard Kipling\n"
553 "\\ No newline at end of file\n";
554 size_t expected_sizes[3] = { 115, 119 + 115 + 114, 119 + 115 + 114 + 71 };
f1e2735c
RB
555
556 g_repo = cl_git_sandbox_init("renames");
557
487fc724
RB
558 cl_git_pass(git_config_new(&cfg));
559 git_repository_set_config(g_repo, cfg);
5173ea92
RB
560 git_config_free(cfg);
561
867f7c9b 562 git_repository_reinit_filesystem(g_repo, false);
7d46b34b 563
6f9d5ce8 564 cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
f1e2735c
RB
565
566 /* remove first line */
567
568 end = git_buf_cstr(&content) + git_buf_find(&content, '\n') + 1;
569 git_buf_consume(&content, end);
6f9d5ce8 570 cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
f1e2735c 571
197b8966 572 check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL, NULL);
f1e2735c
RB
573
574 /* remove trailing whitespace */
575
576 git_buf_rtrim(&content);
6f9d5ce8 577 cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
f1e2735c 578
197b8966 579 check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL, NULL);
f1e2735c
RB
580
581 /* add trailing whitespace */
582
583 cl_git_pass(git_repository_index(&index, g_repo));
6f9d5ce8 584 cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
f1e2735c
RB
585 cl_git_pass(git_index_write(index));
586 git_index_free(index);
587
588 cl_git_pass(git_buf_putc(&content, '\n'));
6f9d5ce8 589 cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
f1e2735c 590
197b8966 591 check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL, NULL);
e35e2684
RB
592
593 /* no trailing whitespace as context line */
594
595 {
596 /* walk back a couple lines, make space and insert char */
597 char *scan = content.ptr + content.size;
598 int i;
599
600 for (i = 0; i < 5; ++i) {
601 for (--scan; scan > content.ptr && *scan != '\n'; --scan)
602 /* seek to prev \n */;
603 }
604 cl_assert(scan > content.ptr);
605
606 /* overwrite trailing \n with right-shifted content */
607 memmove(scan + 1, scan, content.size - (scan - content.ptr) - 1);
608 /* insert '#' char into space we created */
609 scan[1] = '#';
610 }
611 cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
612
fd96f98e 613 check_single_patch_stats(
197b8966 614 g_repo, 1, 1, 1, 6, expected_sizes, expected);
3bf68be4 615
ac3d33df
JK
616 git_buf_dispose(&content);
617}
618
619void test_diff_patch__can_strip_bad_utf8(void)
620{
621 const char *a = "A " UTF8_HUNK_HEADER
622 " B\n"
623 " C\n"
624 " D\n"
625 " E\n"
626 " F\n"
627 " G\n"
628 " H\n"
629 " I\n"
630 " J\n"
631 " K\n"
632 "L " UTF8_HUNK_HEADER
633 " M\n"
634 " N\n"
635 " O\n"
636 " P\n"
637 " Q\n"
638 " R\n"
639 " S\n"
640 " T\n"
641 " U\n"
642 " V\n";
643
644 const char *b = "A " UTF8_HUNK_HEADER
645 " B\n"
646 " C\n"
647 " D\n"
648 " E modified\n"
649 " F\n"
650 " G\n"
651 " H\n"
652 " I\n"
653 " J\n"
654 " K\n"
655 "L " UTF8_HUNK_HEADER
656 " M\n"
657 " N\n"
658 " O\n"
659 " P modified\n"
660 " Q\n"
661 " R\n"
662 " S\n"
663 " T\n"
664 " U\n"
665 " V\n";
666
667 const char *expected = "diff --git a/file b/file\n"
668 "index d0647c4..7827ce5 100644\n"
669 "--- a/file\n"
670 "+++ b/file\n"
671 "@@ -2,7 +2,7 @@ A " UTF8_TRUNCATED_A_HUNK_HEADER
672 " B\n"
673 " C\n"
674 " D\n"
675 "- E\n"
676 "+ E modified\n"
677 " F\n"
678 " G\n"
679 " H\n"
680 "@@ -13,7 +13,7 @@ L " UTF8_TRUNCATED_L_HUNK_HEADER
681 " M\n"
682 " N\n"
683 " O\n"
684 "- P\n"
685 "+ P modified\n"
686 " Q\n"
687 " R\n"
688 " S\n";
689
690 git_diff_options opts;
691 git_patch *patch;
692 git_buf buf = GIT_BUF_INIT;
693
22a2d3d5 694 cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
ac3d33df
JK
695
696 cl_git_pass(git_patch_from_buffers(&patch, a, strlen(a), NULL, b, strlen(b), NULL, &opts));
697 cl_git_pass(git_patch_to_buf(&buf, patch));
698
699 cl_assert_equal_s(expected, buf.ptr);
700
701 git_patch_free(patch);
702 git_buf_dispose(&buf);
f1e2735c 703}