]> git.proxmox.com Git - libgit2.git/blame - tests-clar/diff/workdir.c
signature: Lenient when dupping, strict when creating
[libgit2.git] / tests-clar / diff / workdir.c
CommitLineData
74fa4bfa
RB
1#include "clar_libgit2.h"
2#include "diff_helpers.h"
c2e43fb1 3#include "repository.h"
74fa4bfa
RB
4
5static git_repository *g_repo = NULL;
6
7void test_diff_workdir__initialize(void)
8{
74fa4bfa
RB
9}
10
11void test_diff_workdir__cleanup(void)
12{
854eccbb 13 cl_git_sandbox_cleanup();
74fa4bfa
RB
14}
15
16void test_diff_workdir__to_index(void)
17{
2f8d30be 18 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
74fa4bfa
RB
19 git_diff_list *diff = NULL;
20 diff_expects exp;
f335ecd6 21 int use_iterator;
74fa4bfa 22
0abd7244
RB
23 g_repo = cl_git_sandbox_init("status");
24
74fa4bfa
RB
25 opts.context_lines = 3;
26 opts.interhunk_lines = 1;
27 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
28
56c72b75 29 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
74fa4bfa 30
f335ecd6
RB
31 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
32 memset(&exp, 0, sizeof(exp));
33
34 if (use_iterator)
35 cl_git_pass(diff_foreach_via_iterator(
793c4385 36 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
37 else
38 cl_git_pass(git_diff_foreach(
793c4385 39 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
40
41 /* to generate these values:
42 * - cd to tests/resources/status,
43 * - mv .gitted .git
44 * - git diff --name-status
45 * - git diff
46 * - mv .git .gitted
47 */
48 cl_assert_equal_i(13, exp.files);
b4f5bb07
RB
49 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
50 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
51 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
52 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
53 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6
RB
54
55 cl_assert_equal_i(8, exp.hunks);
56
57 cl_assert_equal_i(14, exp.lines);
58 cl_assert_equal_i(5, exp.line_ctxt);
59 cl_assert_equal_i(4, exp.line_adds);
60 cl_assert_equal_i(5, exp.line_dels);
61 }
74fa4bfa
RB
62
63 git_diff_list_free(diff);
64}
65
66void test_diff_workdir__to_tree(void)
67{
68 /* grabbed a couple of commit oids from the history of the attr repo */
69 const char *a_commit = "26a125ee1bf"; /* the current HEAD */
70 const char *b_commit = "0017bd4ab1ec3"; /* the start */
0abd7244 71 git_tree *a, *b;
2f8d30be 72 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
74fa4bfa
RB
73 git_diff_list *diff = NULL;
74 git_diff_list *diff2 = NULL;
75 diff_expects exp;
f335ecd6 76 int use_iterator;
74fa4bfa 77
0abd7244
RB
78 g_repo = cl_git_sandbox_init("status");
79
80 a = resolve_commit_oid_to_tree(g_repo, a_commit);
81 b = resolve_commit_oid_to_tree(g_repo, b_commit);
82
74fa4bfa
RB
83 opts.context_lines = 3;
84 opts.interhunk_lines = 1;
85 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
86
56c72b75 87 /* You can't really generate the equivalent of git_diff_tree_to_workdir()
74fa4bfa
RB
88 * using C git. It really wants to interpose the index into the diff.
89 *
90 * To validate the following results with command line git, I ran the
91 * following:
92 * - git ls-tree 26a125
93 * - find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
94 * The results are documented at the bottom of this file in the
95 * long comment entitled "PREPARATION OF TEST DATA".
96 */
56c72b75 97 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
74fa4bfa 98
f335ecd6
RB
99 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
100 memset(&exp, 0, sizeof(exp));
74fa4bfa 101
f335ecd6
RB
102 if (use_iterator)
103 cl_git_pass(diff_foreach_via_iterator(
793c4385 104 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
105 else
106 cl_git_pass(git_diff_foreach(
793c4385 107 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
108
109 cl_assert_equal_i(14, exp.files);
b4f5bb07
RB
110 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
111 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
112 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
113 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
114 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 115 }
74fa4bfa
RB
116
117 /* Since there is no git diff equivalent, let's just assume that the
118 * text diffs produced by git_diff_foreach are accurate here. We will
119 * do more apples-to-apples test comparison below.
120 */
121
122 git_diff_list_free(diff);
123 diff = NULL;
124 memset(&exp, 0, sizeof(exp));
125
126 /* This is a compatible emulation of "git diff <sha>" which looks like
127 * a workdir to tree diff (even though it is not really). This is what
128 * you would get from "git diff --name-status 26a125ee1bf"
129 */
56c72b75
RB
130 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
131 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
74fa4bfa
RB
132 cl_git_pass(git_diff_merge(diff, diff2));
133 git_diff_list_free(diff2);
134
f335ecd6
RB
135 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
136 memset(&exp, 0, sizeof(exp));
137
138 if (use_iterator)
139 cl_git_pass(diff_foreach_via_iterator(
793c4385 140 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
141 else
142 cl_git_pass(git_diff_foreach(
793c4385 143 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
74fa4bfa 144
f335ecd6 145 cl_assert_equal_i(15, exp.files);
b4f5bb07
RB
146 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
147 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
148 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
149 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
150 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
74fa4bfa 151
f335ecd6 152 cl_assert_equal_i(11, exp.hunks);
74fa4bfa 153
f335ecd6
RB
154 cl_assert_equal_i(17, exp.lines);
155 cl_assert_equal_i(4, exp.line_ctxt);
156 cl_assert_equal_i(8, exp.line_adds);
157 cl_assert_equal_i(5, exp.line_dels);
158 }
74fa4bfa
RB
159
160 git_diff_list_free(diff);
161 diff = NULL;
162 memset(&exp, 0, sizeof(exp));
163
164 /* Again, emulating "git diff <sha>" for testing purposes using
165 * "git diff --name-status 0017bd4ab1ec3" instead.
166 */
56c72b75
RB
167 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
168 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
74fa4bfa
RB
169 cl_git_pass(git_diff_merge(diff, diff2));
170 git_diff_list_free(diff2);
171
f335ecd6
RB
172 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
173 memset(&exp, 0, sizeof(exp));
74fa4bfa 174
f335ecd6
RB
175 if (use_iterator)
176 cl_git_pass(diff_foreach_via_iterator(
793c4385 177 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
178 else
179 cl_git_pass(git_diff_foreach(
793c4385 180 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
74fa4bfa 181
f335ecd6 182 cl_assert_equal_i(16, exp.files);
b4f5bb07
RB
183 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_ADDED]);
184 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
185 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
186 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
187 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
74fa4bfa 188
f335ecd6
RB
189 cl_assert_equal_i(12, exp.hunks);
190
191 cl_assert_equal_i(19, exp.lines);
192 cl_assert_equal_i(3, exp.line_ctxt);
193 cl_assert_equal_i(12, exp.line_adds);
194 cl_assert_equal_i(4, exp.line_dels);
195 }
74fa4bfa 196
c19bc93c
RB
197 git_diff_list_free(diff);
198
74fa4bfa
RB
199 git_tree_free(a);
200 git_tree_free(b);
201}
202
14a513e0
RB
203void test_diff_workdir__to_index_with_pathspec(void)
204{
2f8d30be 205 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
14a513e0
RB
206 git_diff_list *diff = NULL;
207 diff_expects exp;
208 char *pathspec = NULL;
f335ecd6 209 int use_iterator;
14a513e0 210
0abd7244
RB
211 g_repo = cl_git_sandbox_init("status");
212
14a513e0
RB
213 opts.context_lines = 3;
214 opts.interhunk_lines = 1;
215 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
216 opts.pathspec.strings = &pathspec;
217 opts.pathspec.count = 1;
218
56c72b75 219 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
14a513e0 220
f335ecd6
RB
221 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
222 memset(&exp, 0, sizeof(exp));
223
224 if (use_iterator)
225 cl_git_pass(diff_foreach_via_iterator(
793c4385 226 diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6 227 else
793c4385 228 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6
RB
229
230 cl_assert_equal_i(13, exp.files);
b4f5bb07
RB
231 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
232 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
233 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
234 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
235 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 236 }
14a513e0
RB
237
238 git_diff_list_free(diff);
239
14a513e0
RB
240 pathspec = "modified_file";
241
56c72b75 242 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
14a513e0 243
f335ecd6
RB
244 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
245 memset(&exp, 0, sizeof(exp));
246
247 if (use_iterator)
248 cl_git_pass(diff_foreach_via_iterator(
793c4385 249 diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6 250 else
793c4385 251 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6
RB
252
253 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
254 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
255 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
256 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
257 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
258 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 259 }
14a513e0
RB
260
261 git_diff_list_free(diff);
262
14a513e0
RB
263 pathspec = "subdir";
264
56c72b75 265 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
14a513e0 266
f335ecd6
RB
267 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
268 memset(&exp, 0, sizeof(exp));
269
270 if (use_iterator)
271 cl_git_pass(diff_foreach_via_iterator(
793c4385 272 diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6 273 else
793c4385 274 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6
RB
275
276 cl_assert_equal_i(3, exp.files);
b4f5bb07
RB
277 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
278 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
279 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
280 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
281 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 282 }
14a513e0
RB
283
284 git_diff_list_free(diff);
285
14a513e0
RB
286 pathspec = "*_deleted";
287
56c72b75 288 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
14a513e0 289
f335ecd6
RB
290 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
291 memset(&exp, 0, sizeof(exp));
292
293 if (use_iterator)
294 cl_git_pass(diff_foreach_via_iterator(
793c4385 295 diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6 296 else
793c4385 297 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6
RB
298
299 cl_assert_equal_i(2, exp.files);
b4f5bb07
RB
300 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
301 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
302 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
303 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
304 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 305 }
14a513e0
RB
306
307 git_diff_list_free(diff);
308}
309
0abd7244
RB
310void test_diff_workdir__filemode_changes(void)
311{
0abd7244
RB
312 git_diff_list *diff = NULL;
313 diff_expects exp;
f335ecd6 314 int use_iterator;
0abd7244
RB
315
316 if (!cl_is_chmod_supported())
317 return;
318
319 g_repo = cl_git_sandbox_init("issue_592");
320
1323c6d1 321 cl_repo_set_bool(g_repo, "core.filemode", true);
0abd7244
RB
322
323 /* test once with no mods */
324
56c72b75 325 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
0abd7244 326
f335ecd6
RB
327 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
328 memset(&exp, 0, sizeof(exp));
0abd7244 329
f335ecd6
RB
330 if (use_iterator)
331 cl_git_pass(diff_foreach_via_iterator(
793c4385 332 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
333 else
334 cl_git_pass(git_diff_foreach(
793c4385 335 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
336
337 cl_assert_equal_i(0, exp.files);
b4f5bb07 338 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
339 cl_assert_equal_i(0, exp.hunks);
340 }
0abd7244
RB
341
342 git_diff_list_free(diff);
343
344 /* chmod file and test again */
345
346 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
347
56c72b75 348 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
0abd7244 349
f335ecd6
RB
350 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
351 memset(&exp, 0, sizeof(exp));
0abd7244 352
f335ecd6
RB
353 if (use_iterator)
354 cl_git_pass(diff_foreach_via_iterator(
793c4385 355 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
356 else
357 cl_git_pass(git_diff_foreach(
793c4385 358 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
359
360 cl_assert_equal_i(1, exp.files);
b4f5bb07 361 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
362 cl_assert_equal_i(0, exp.hunks);
363 }
0abd7244
RB
364
365 git_diff_list_free(diff);
366
367 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
0abd7244
RB
368}
369
370void test_diff_workdir__filemode_changes_with_filemode_false(void)
371{
0abd7244
RB
372 git_diff_list *diff = NULL;
373 diff_expects exp;
374
375 if (!cl_is_chmod_supported())
376 return;
377
378 g_repo = cl_git_sandbox_init("issue_592");
379
1323c6d1 380 cl_repo_set_bool(g_repo, "core.filemode", false);
0abd7244
RB
381
382 /* test once with no mods */
383
56c72b75 384 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
0abd7244
RB
385
386 memset(&exp, 0, sizeof(exp));
387 cl_git_pass(git_diff_foreach(
793c4385 388 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
0abd7244
RB
389
390 cl_assert_equal_i(0, exp.files);
b4f5bb07 391 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
0abd7244
RB
392 cl_assert_equal_i(0, exp.hunks);
393
394 git_diff_list_free(diff);
395
396 /* chmod file and test again */
397
398 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
399
56c72b75 400 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
0abd7244
RB
401
402 memset(&exp, 0, sizeof(exp));
403 cl_git_pass(git_diff_foreach(
793c4385 404 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
0abd7244
RB
405
406 cl_assert_equal_i(0, exp.files);
b4f5bb07 407 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
0abd7244
RB
408 cl_assert_equal_i(0, exp.hunks);
409
410 git_diff_list_free(diff);
411
412 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
0abd7244
RB
413}
414
145e696b
RB
415void test_diff_workdir__head_index_and_workdir_all_differ(void)
416{
2f8d30be 417 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
145e696b
RB
418 git_diff_list *diff_i2t = NULL, *diff_w2i = NULL;
419 diff_expects exp;
420 char *pathspec = "staged_changes_modified_file";
421 git_tree *tree;
f335ecd6 422 int use_iterator;
145e696b
RB
423
424 /* For this file,
425 * - head->index diff has 1 line of context, 1 line of diff
426 * - index->workdir diff has 2 lines of context, 1 line of diff
427 * but
428 * - head->workdir diff has 1 line of context, 2 lines of diff
429 * Let's make sure the right one is returned from each fn.
430 */
431
432 g_repo = cl_git_sandbox_init("status");
433
434 tree = resolve_commit_oid_to_tree(g_repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
435
436 opts.pathspec.strings = &pathspec;
437 opts.pathspec.count = 1;
438
56c72b75
RB
439 cl_git_pass(git_diff_tree_to_index(&diff_i2t, g_repo, tree, NULL, &opts));
440 cl_git_pass(git_diff_index_to_workdir(&diff_w2i, g_repo, NULL, &opts));
145e696b 441
f335ecd6
RB
442 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
443 memset(&exp, 0, sizeof(exp));
444
445 if (use_iterator)
446 cl_git_pass(diff_foreach_via_iterator(
793c4385 447 diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
448 else
449 cl_git_pass(git_diff_foreach(
793c4385 450 diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
451
452 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
453 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
454 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
455 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
456 cl_assert_equal_i(1, exp.hunks);
457 cl_assert_equal_i(2, exp.lines);
458 cl_assert_equal_i(1, exp.line_ctxt);
459 cl_assert_equal_i(1, exp.line_adds);
460 cl_assert_equal_i(0, exp.line_dels);
461 }
462
463 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
464 memset(&exp, 0, sizeof(exp));
465
466 if (use_iterator)
467 cl_git_pass(diff_foreach_via_iterator(
793c4385 468 diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
469 else
470 cl_git_pass(git_diff_foreach(
793c4385 471 diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
472
473 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
474 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
475 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
476 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
477 cl_assert_equal_i(1, exp.hunks);
478 cl_assert_equal_i(3, exp.lines);
479 cl_assert_equal_i(2, exp.line_ctxt);
480 cl_assert_equal_i(1, exp.line_adds);
481 cl_assert_equal_i(0, exp.line_dels);
482 }
145e696b
RB
483
484 cl_git_pass(git_diff_merge(diff_i2t, diff_w2i));
485
f335ecd6
RB
486 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
487 memset(&exp, 0, sizeof(exp));
488
489 if (use_iterator)
490 cl_git_pass(diff_foreach_via_iterator(
793c4385 491 diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
492 else
493 cl_git_pass(git_diff_foreach(
793c4385 494 diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
495
496 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
497 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
498 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
499 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
500 cl_assert_equal_i(1, exp.hunks);
501 cl_assert_equal_i(3, exp.lines);
502 cl_assert_equal_i(1, exp.line_ctxt);
503 cl_assert_equal_i(2, exp.line_adds);
504 cl_assert_equal_i(0, exp.line_dels);
505 }
145e696b
RB
506
507 git_diff_list_free(diff_i2t);
508 git_diff_list_free(diff_w2i);
cdca82c7
CMN
509
510 git_tree_free(tree);
145e696b
RB
511}
512
513void test_diff_workdir__eof_newline_changes(void)
514{
2f8d30be 515 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
145e696b
RB
516 git_diff_list *diff = NULL;
517 diff_expects exp;
518 char *pathspec = "current_file";
f335ecd6 519 int use_iterator;
145e696b
RB
520
521 g_repo = cl_git_sandbox_init("status");
522
523 opts.pathspec.strings = &pathspec;
524 opts.pathspec.count = 1;
525
56c72b75 526 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
145e696b 527
f335ecd6
RB
528 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
529 memset(&exp, 0, sizeof(exp));
530
531 if (use_iterator)
532 cl_git_pass(diff_foreach_via_iterator(
793c4385 533 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
534 else
535 cl_git_pass(git_diff_foreach(
793c4385 536 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
537
538 cl_assert_equal_i(0, exp.files);
b4f5bb07
RB
539 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
540 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
541 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
542 cl_assert_equal_i(0, exp.hunks);
543 cl_assert_equal_i(0, exp.lines);
544 cl_assert_equal_i(0, exp.line_ctxt);
545 cl_assert_equal_i(0, exp.line_adds);
546 cl_assert_equal_i(0, exp.line_dels);
547 }
145e696b
RB
548
549 git_diff_list_free(diff);
550
551 cl_git_append2file("status/current_file", "\n");
552
56c72b75 553 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
145e696b 554
f335ecd6
RB
555 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
556 memset(&exp, 0, sizeof(exp));
557
558 if (use_iterator)
559 cl_git_pass(diff_foreach_via_iterator(
793c4385 560 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
561 else
562 cl_git_pass(git_diff_foreach(
793c4385 563 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
564
565 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
566 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
567 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
568 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
569 cl_assert_equal_i(1, exp.hunks);
570 cl_assert_equal_i(2, exp.lines);
571 cl_assert_equal_i(1, exp.line_ctxt);
572 cl_assert_equal_i(1, exp.line_adds);
573 cl_assert_equal_i(0, exp.line_dels);
574 }
145e696b
RB
575
576 git_diff_list_free(diff);
577
578 cl_git_rewritefile("status/current_file", "current_file");
579
56c72b75 580 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
145e696b 581
f335ecd6
RB
582 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
583 memset(&exp, 0, sizeof(exp));
584
585 if (use_iterator)
586 cl_git_pass(diff_foreach_via_iterator(
793c4385 587 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
588 else
589 cl_git_pass(git_diff_foreach(
793c4385 590 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
591
592 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
593 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
594 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
595 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
596 cl_assert_equal_i(1, exp.hunks);
597 cl_assert_equal_i(3, exp.lines);
598 cl_assert_equal_i(0, exp.line_ctxt);
599 cl_assert_equal_i(1, exp.line_adds);
600 cl_assert_equal_i(2, exp.line_dels);
601 }
145e696b
RB
602
603 git_diff_list_free(diff);
604}
605
74fa4bfa
RB
606/* PREPARATION OF TEST DATA
607 *
56c72b75 608 * Since there is no command line equivalent of git_diff_tree_to_workdir,
74fa4bfa
RB
609 * it was a bit of a pain to confirm that I was getting the expected
610 * results in the first part of this tests. Here is what I ended up
611 * doing to set my expectation for the file counts and results:
612 *
613 * Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
614 *
615 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
616 * B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
617 * C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
618 * D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
619 * E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
620 * F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
621 * G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
622 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
623 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
624 * J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
625 * K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
626 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
627 *
628 * --------
629 *
630 * find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
631 *
632 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
633 * M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
634 * C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
635 * N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
636 * D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
637 * F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
638 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
639 * O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
640 * P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
641 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
642 * K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
643 * Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
644 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
645 *
646 * --------
647 *
648 * A - current_file (UNMODIFIED) -> not in results
649 * B D file_deleted
650 * M I ignored_file (IGNORED)
651 * C M modified_file
652 * N U new_file (UNTRACKED)
653 * D M staged_changes
654 * E D staged_changes_file_deleted
655 * F M staged_changes_modified_file
656 * G D staged_delete_file_deleted
657 * H - staged_delete_modified_file (UNMODIFIED) -> not in results
658 * O U staged_new_file
659 * P U staged_new_file_modified_file
660 * I - subdir/current_file (UNMODIFIED) -> not in results
661 * J D subdir/deleted_file
662 * K M subdir/modified_file
663 * Q U subdir/new_file
664 * L - subdir.txt (UNMODIFIED) -> not in results
665 *
666 * Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
667 */
49d34c1c
RB
668
669
670void test_diff_workdir__larger_hunks(void)
671{
672 const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
673 const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
674 git_tree *a, *b;
2f8d30be 675 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
5f69a31f 676 size_t i, d, num_d, h, num_h, l, num_l, header_len, line_len;
49d34c1c
RB
677
678 g_repo = cl_git_sandbox_init("diff");
679
680 cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
681 cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
682
683 opts.context_lines = 1;
684 opts.interhunk_lines = 0;
685
686 for (i = 0; i <= 2; ++i) {
687 git_diff_list *diff = NULL;
5f69a31f 688 git_diff_patch *patch;
bae957b9 689 const git_diff_range *range;
5f69a31f
RB
690 const char *header, *line;
691 char origin;
49d34c1c
RB
692
693 /* okay, this is a bit silly, but oh well */
694 switch (i) {
695 case 0:
56c72b75 696 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
49d34c1c
RB
697 break;
698 case 1:
56c72b75 699 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
49d34c1c
RB
700 break;
701 case 2:
56c72b75 702 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, b, &opts));
49d34c1c
RB
703 break;
704 }
705
5f69a31f
RB
706 num_d = git_diff_num_deltas(diff);
707 cl_assert_equal_i(2, (int)num_d);
49d34c1c 708
5f69a31f
RB
709 for (d = 0; d < num_d; ++d) {
710 cl_git_pass(git_diff_get_patch(&patch, NULL, diff, d));
711 cl_assert(patch);
49d34c1c 712
5f69a31f
RB
713 num_h = git_diff_patch_num_hunks(patch);
714 for (h = 0; h < num_h; h++) {
715 cl_git_pass(git_diff_patch_get_hunk(
716 &range, &header, &header_len, &num_l, patch, h));
49d34c1c 717
5f69a31f
RB
718 for (l = 0; l < num_l; ++l) {
719 cl_git_pass(git_diff_patch_get_line_in_hunk(
720 &origin, &line, &line_len, NULL, NULL, patch, h, l));
721 cl_assert(line);
49d34c1c
RB
722 }
723
5f69a31f
RB
724 /* confirm fail after the last item */
725 cl_git_fail(git_diff_patch_get_line_in_hunk(
726 &origin, &line, &line_len, NULL, NULL, patch, h, num_l));
49d34c1c
RB
727 }
728
5f69a31f
RB
729 /* confirm fail after the last item */
730 cl_git_fail(git_diff_patch_get_hunk(
731 &range, &header, &header_len, &num_l, patch, num_h));
49d34c1c 732
5f69a31f
RB
733 git_diff_patch_free(patch);
734 }
49d34c1c 735
49d34c1c
RB
736 git_diff_list_free(diff);
737 }
738
739 git_tree_free(a);
740 git_tree_free(b);
741}
5d1308f2
RB
742
743/* Set up a test that exercises this code. The easiest test using existing
744 * test data is probably to create a sandbox of submod2 and then run a
56c72b75 745 * git_diff_tree_to_workdir against tree
5d1308f2
RB
746 * 873585b94bdeabccea991ea5e3ec1a277895b698. As for what you should actually
747 * test, you can start by just checking that the number of lines of diff
748 * content matches the actual output of git diff. That will at least
749 * demonstrate that the submodule content is being used to generate somewhat
750 * comparable outputs. It is a test that would fail without this code and
751 * will succeed with it.
752 */
753
754#include "../submodule/submodule_helpers.h"
755
756void test_diff_workdir__submodules(void)
757{
758 const char *a_commit = "873585b94bdeabccea991ea5e3ec1a277895b698";
759 git_tree *a;
2f8d30be 760 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
5d1308f2
RB
761 git_diff_list *diff = NULL;
762 diff_expects exp;
763
764 g_repo = cl_git_sandbox_init("submod2");
765
766 cl_fixture_sandbox("submod2_target");
767 p_rename("submod2_target/.gitted", "submod2_target/.git");
768
769 rewrite_gitmodules(git_repository_workdir(g_repo));
65025cb8
RB
770 p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
771 p_rename("submod2/not/.gitted", "submod2/not/.git");
5d1308f2
RB
772
773 cl_fixture_cleanup("submod2_target");
774
775 a = resolve_commit_oid_to_tree(g_repo, a_commit);
776
777 opts.flags =
778 GIT_DIFF_INCLUDE_UNTRACKED |
779 GIT_DIFF_RECURSE_UNTRACKED_DIRS |
780 GIT_DIFF_INCLUDE_UNTRACKED_CONTENT;
781
56c72b75 782 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
5d1308f2
RB
783
784 /* diff_print(stderr, diff); */
785
786 /* essentially doing: git diff 873585b94bdeabccea991ea5e3ec1a277895b698 */
787
788 memset(&exp, 0, sizeof(exp));
65025cb8 789
5d1308f2 790 cl_git_pass(git_diff_foreach(
793c4385 791 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
5d1308f2 792
ccfa6805
RB
793 /* so "git diff 873585" returns:
794 * M .gitmodules
795 * A just_a_dir/contents
796 * A just_a_file
797 * A sm_added_and_uncommited
798 * A sm_changed_file
799 * A sm_changed_head
800 * A sm_changed_index
801 * A sm_changed_untracked_file
802 * M sm_missing_commits
803 * A sm_unchanged
804 * which is a little deceptive because of the difference between the
805 * "git diff <treeish>" results from "git_diff_tree_to_workdir". The
806 * only significant difference is that those Added items will show up
807 * as Untracked items in the pure libgit2 diff.
808 *
809 * Then add in the two extra untracked items "not" and "not-submodule"
810 * to get the 12 files reported here.
5d1308f2
RB
811 */
812
ccfa6805 813 cl_assert_equal_i(12, exp.files);
5d1308f2 814
b4f5bb07
RB
815 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
816 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
ccfa6805 817 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
b4f5bb07 818 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
65025cb8 819 cl_assert_equal_i(10, exp.file_status[GIT_DELTA_UNTRACKED]);
5d1308f2
RB
820
821 /* the following numbers match "git diff 873585" exactly */
822
823 cl_assert_equal_i(9, exp.hunks);
824
825 cl_assert_equal_i(33, exp.lines);
826 cl_assert_equal_i(2, exp.line_ctxt);
827 cl_assert_equal_i(30, exp.line_adds);
828 cl_assert_equal_i(1, exp.line_dels);
829
830 git_diff_list_free(diff);
831 git_tree_free(a);
832}
c2e43fb1 833
834void test_diff_workdir__cannot_diff_against_a_bare_repository(void)
835{
2f8d30be 836 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
c2e43fb1 837 git_diff_list *diff = NULL;
838 git_tree *tree;
839
840 g_repo = cl_git_sandbox_init("testrepo.git");
841
5735bf5e 842 cl_assert_equal_i(
56c72b75 843 GIT_EBAREREPO, git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
c2e43fb1 844
845 cl_git_pass(git_repository_head_tree(&tree, g_repo));
5735bf5e
RB
846
847 cl_assert_equal_i(
56c72b75 848 GIT_EBAREREPO, git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
c2e43fb1 849
850 git_tree_free(tree);
851}
59a0d772 852
853void test_diff_workdir__to_null_tree(void)
854{
855 git_diff_list *diff;
856 diff_expects exp;
2f8d30be 857 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
59a0d772 858
859 opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
860 GIT_DIFF_RECURSE_UNTRACKED_DIRS;
861
862 g_repo = cl_git_sandbox_init("status");
863
56c72b75 864 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
59a0d772 865
866 memset(&exp, 0, sizeof(exp));
867
868 cl_git_pass(git_diff_foreach(
869 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
870
871 cl_assert_equal_i(exp.files, exp.file_status[GIT_DELTA_UNTRACKED]);
872
873 git_diff_list_free(diff);
874}
2f8d30be
BS
875
876void test_diff_workdir__checks_options_version(void)
877{
878 git_diff_list *diff;
879 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
880 const git_error *err;
881
882 g_repo = cl_git_sandbox_init("status");
883
884 opts.version = 0;
56c72b75 885 cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
2f8d30be
BS
886 err = giterr_last();
887 cl_assert_equal_i(GITERR_INVALID, err->klass);
888
889 giterr_clear();
890 opts.version = 1024;
56c72b75 891 cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
2f8d30be
BS
892 err = giterr_last();
893 cl_assert_equal_i(GITERR_INVALID, err->klass);
894}
de590550
RB
895
896void test_diff_workdir__can_diff_empty_file(void)
897{
898 git_diff_list *diff;
899 git_tree *tree;
900 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
901 struct stat st;
902 git_diff_patch *patch;
903
904 g_repo = cl_git_sandbox_init("attr_index");
905
906 tree = resolve_commit_oid_to_tree(g_repo, "3812cfef3661"); /* HEAD */
907
908 /* baseline - make sure there are no outstanding diffs */
909
910 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
911 cl_assert_equal_i(2, (int)git_diff_num_deltas(diff));
912 git_diff_list_free(diff);
913
914 /* empty contents of file */
915
916 cl_git_rewritefile("attr_index/README.txt", "");
917 cl_git_pass(git_path_lstat("attr_index/README.txt", &st));
918 cl_assert_equal_i(0, (int)st.st_size);
919
920 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
921 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
922 /* diffs are: .gitattributes, README.txt, sub/sub/.gitattributes */
923 cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 1));
924 git_diff_patch_free(patch);
925 git_diff_list_free(diff);
926
927 /* remove a file altogether */
928
929 cl_git_pass(p_unlink("attr_index/README.txt"));
930 cl_assert(!git_path_exists("attr_index/README.txt"));
931
932 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
933 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
934 cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 1));
935 git_diff_patch_free(patch);
936 git_diff_list_free(diff);
8842c75f
VM
937
938 git_tree_free(tree);
de590550 939}
b8acb775 940
b8acb775
SS
941void test_diff_workdir__to_index_issue_1397(void)
942{
943 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
944 git_diff_list *diff = NULL;
945 diff_expects exp;
b8acb775
SS
946
947 g_repo = cl_git_sandbox_init("issue_1397");
948
1098cfae 949 cl_repo_set_bool(g_repo, "core.autocrlf", true);
b8acb775
SS
950
951 opts.context_lines = 3;
952 opts.interhunk_lines = 1;
953
954 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
955
1098cfae
RB
956 memset(&exp, 0, sizeof(exp));
957 cl_git_pass(git_diff_foreach(
958 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
b8acb775 959
1098cfae
RB
960 cl_assert_equal_i(0, exp.files);
961 cl_assert_equal_i(0, exp.hunks);
962 cl_assert_equal_i(0, exp.lines);
b8acb775
SS
963
964 git_diff_list_free(diff);
965 diff = NULL;
b8acb775 966
1098cfae
RB
967 cl_git_rewritefile("issue_1397/crlf_file.txt",
968 "first line\r\nsecond line modified\r\nboth with crlf");
b8acb775
SS
969
970 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
971
1098cfae
RB
972 memset(&exp, 0, sizeof(exp));
973 cl_git_pass(git_diff_foreach(
974 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
b8acb775 975
1098cfae
RB
976 cl_assert_equal_i(1, exp.files);
977 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
b8acb775 978
1098cfae 979 cl_assert_equal_i(1, exp.hunks);
b8acb775 980
1098cfae
RB
981 cl_assert_equal_i(5, exp.lines);
982 cl_assert_equal_i(3, exp.line_ctxt);
983 cl_assert_equal_i(1, exp.line_adds);
984 cl_assert_equal_i(1, exp.line_dels);
b8acb775
SS
985
986 git_diff_list_free(diff);
987}
988
989void test_diff_workdir__to_tree_issue_1397(void)
990{
1098cfae 991 const char *a_commit = "7f483a738"; /* the current HEAD */
b8acb775
SS
992 git_tree *a;
993 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
994 git_diff_list *diff = NULL;
995 git_diff_list *diff2 = NULL;
996 diff_expects exp;
b8acb775
SS
997
998 g_repo = cl_git_sandbox_init("issue_1397");
999
1098cfae 1000 cl_repo_set_bool(g_repo, "core.autocrlf", true);
b8acb775
SS
1001
1002 a = resolve_commit_oid_to_tree(g_repo, a_commit);
1003
1004 opts.context_lines = 3;
1005 opts.interhunk_lines = 1;
1006
1007 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
1008
1098cfae
RB
1009 memset(&exp, 0, sizeof(exp));
1010 cl_git_pass(git_diff_foreach(
1011 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
b8acb775 1012
1098cfae
RB
1013 cl_assert_equal_i(0, exp.files);
1014 cl_assert_equal_i(0, exp.hunks);
1015 cl_assert_equal_i(0, exp.lines);
b8acb775
SS
1016
1017 git_diff_list_free(diff);
1018 diff = NULL;
b8acb775 1019
b8acb775
SS
1020 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
1021 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
1022 cl_git_pass(git_diff_merge(diff, diff2));
1023 git_diff_list_free(diff2);
1024
1098cfae
RB
1025 memset(&exp, 0, sizeof(exp));
1026 cl_git_pass(git_diff_foreach(
1027 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
b8acb775 1028
1098cfae
RB
1029 cl_assert_equal_i(0, exp.files);
1030 cl_assert_equal_i(0, exp.hunks);
1031 cl_assert_equal_i(0, exp.lines);
b8acb775
SS
1032
1033 git_diff_list_free(diff);
1034 git_tree_free(a);
1035}
a66c4bc8
RB
1036
1037void test_diff_workdir__untracked_directory_scenarios(void)
1038{
1039 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1040 git_diff_list *diff = NULL;
1041 diff_expects exp;
1042 char *pathspec = NULL;
1043 static const char *files0[] = {
1044 "subdir/deleted_file",
1045 "subdir/modified_file",
1046 "subdir/new_file",
1047 NULL
1048 };
1049 static const char *files1[] = {
1050 "subdir/deleted_file",
1051 "subdir/directory/",
1052 "subdir/modified_file",
1053 "subdir/new_file",
1054 NULL
1055 };
1056 static const char *files2[] = {
1057 "subdir/deleted_file",
1058 "subdir/directory/more/notignored",
1059 "subdir/modified_file",
1060 "subdir/new_file",
1061 NULL
1062 };
1063
1064 g_repo = cl_git_sandbox_init("status");
1065 cl_git_mkfile("status/.gitignore", "ignored\n");
1066
1067 opts.context_lines = 3;
1068 opts.interhunk_lines = 1;
1069 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
1070 opts.pathspec.strings = &pathspec;
1071 opts.pathspec.count = 1;
1072 pathspec = "subdir";
1073
1074 /* baseline for "subdir" pathspec */
1075
1076 memset(&exp, 0, sizeof(exp));
1077 exp.names = files0;
1078
1079 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1080
1081 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1082
1083 cl_assert_equal_i(3, exp.files);
1084 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1085 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1086 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1087 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1088 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1089
1090 git_diff_list_free(diff);
1091
1092 /* empty directory */
1093
1094 cl_git_pass(p_mkdir("status/subdir/directory", 0777));
1095
1096 memset(&exp, 0, sizeof(exp));
1097 exp.names = files1;
1098
1099 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1100
1101 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1102
1103 cl_assert_equal_i(4, exp.files);
1104 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1105 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1106 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1107 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1108 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1109
1110 git_diff_list_free(diff);
1111
1112 /* directory with only ignored files */
1113
1114 cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
1115 cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n");
1116
1117 cl_git_pass(p_mkdir("status/subdir/directory/another", 0777));
1118 cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n");
1119
1120 memset(&exp, 0, sizeof(exp));
1121 exp.names = files1;
1122
1123 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1124
1125 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1126
1127 cl_assert_equal_i(4, exp.files);
1128 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1129 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1130 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1131 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1132 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1133
1134 git_diff_list_free(diff);
1135
1136 /* directory with ignored directory (contents irrelevant) */
1137
1138 cl_git_pass(p_mkdir("status/subdir/directory/more", 0777));
1139 cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777));
1140 cl_git_mkfile("status/subdir/directory/more/ignored/notignored",
1141 "inside ignored dir\n");
1142
1143 memset(&exp, 0, sizeof(exp));
1144 exp.names = files1;
1145
1146 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1147
1148 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1149
1150 cl_assert_equal_i(4, exp.files);
1151 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1152 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1153 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1154 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1155 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1156
1157 git_diff_list_free(diff);
1158
1159 /* quick version avoids directory scan */
1160
1161 opts.flags = opts.flags | GIT_DIFF_FAST_UNTRACKED_DIRS;
1162
1163 memset(&exp, 0, sizeof(exp));
1164 exp.names = files1;
1165
1166 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1167
1168 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1169
1170 cl_assert_equal_i(4, exp.files);
1171 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1172 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1173 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1174 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1175 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
1176
1177 git_diff_list_free(diff);
1178
1179 /* directory with nested non-ignored content */
1180
1181 opts.flags = opts.flags & ~GIT_DIFF_FAST_UNTRACKED_DIRS;
1182
1183 cl_git_mkfile("status/subdir/directory/more/notignored",
1184 "not ignored deep under untracked\n");
1185
1186 memset(&exp, 0, sizeof(exp));
1187 exp.names = files1;
1188
1189 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1190
1191 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1192
1193 cl_assert_equal_i(4, exp.files);
1194 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1195 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1196 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1197 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1198 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
1199
1200 git_diff_list_free(diff);
1201
1202 /* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
1203
1204 opts.flags = opts.flags & ~GIT_DIFF_INCLUDE_IGNORED;
1205 opts.flags = opts.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
1206
1207 memset(&exp, 0, sizeof(exp));
1208 exp.names = files2;
1209
1210 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1211
1212 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1213
1214 cl_assert_equal_i(4, exp.files);
1215 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1216 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1217 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1218 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1219 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
1220
1221 git_diff_list_free(diff);
1222}