]> git.proxmox.com Git - libgit2.git/blob - tests/diff/workdir.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / tests / diff / workdir.c
1 #include "clar_libgit2.h"
2 #include "diff_helpers.h"
3 #include "repository.h"
4 #include "index.h"
5 #include "git2/sys/diff.h"
6 #include "../checkout/checkout_helpers.h"
7
8 static git_repository *g_repo = NULL;
9
10 void test_diff_workdir__cleanup(void)
11 {
12 cl_git_sandbox_cleanup();
13 }
14
15 void test_diff_workdir__to_index(void)
16 {
17 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
18 git_diff *diff = NULL;
19 diff_expects exp;
20 int use_iterator;
21
22 g_repo = cl_git_sandbox_init("status");
23
24 opts.context_lines = 3;
25 opts.interhunk_lines = 1;
26 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
27
28 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
29
30 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
31 memset(&exp, 0, sizeof(exp));
32
33 if (use_iterator)
34 cl_git_pass(diff_foreach_via_iterator(
35 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
36 else
37 cl_git_pass(git_diff_foreach(
38 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
39
40 /* to generate these values:
41 * - cd to tests/resources/status,
42 * - mv .gitted .git
43 * - git diff --name-status
44 * - git diff
45 * - mv .git .gitted
46 */
47 cl_assert_equal_i(13, exp.files);
48 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
49 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
50 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
51 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
52 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
53
54 cl_assert_equal_i(8, exp.hunks);
55
56 cl_assert_equal_i(14, exp.lines);
57 cl_assert_equal_i(5, exp.line_ctxt);
58 cl_assert_equal_i(4, exp.line_adds);
59 cl_assert_equal_i(5, exp.line_dels);
60 }
61
62 {
63 git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
64 cl_git_pass(git_diff_get_perfdata(&perf, diff));
65 cl_assert_equal_sz(
66 13 /* in root */ + 3 /* in subdir */, perf.stat_calls);
67 cl_assert_equal_sz(5, perf.oid_calculations);
68 }
69
70 git_diff_free(diff);
71 }
72
73 void test_diff_workdir__to_index_with_conflicts(void)
74 {
75 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
76 git_diff *diff = NULL;
77 git_index *index;
78 git_index_entry our_entry = {{0}}, their_entry = {{0}};
79 diff_expects exp = {0};
80
81 g_repo = cl_git_sandbox_init("status");
82
83 opts.context_lines = 3;
84 opts.interhunk_lines = 1;
85
86 /* Adding an entry that represents a rename gets two files in conflict */
87 our_entry.path = "subdir/modified_file";
88 our_entry.mode = 0100644;
89 git_oid_fromstr(&our_entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
90
91 their_entry.path = "subdir/rename_conflict";
92 their_entry.mode = 0100644;
93 git_oid_fromstr(&their_entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
94
95 cl_git_pass(git_repository_index(&index, g_repo));
96 cl_git_pass(git_index_conflict_add(index, NULL, &our_entry, &their_entry));
97
98 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
99
100 cl_git_pass(diff_foreach_via_iterator(
101 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
102
103 cl_assert_equal_i(9, exp.files);
104 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
105 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
106 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
107 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_CONFLICTED]);
108
109 cl_assert_equal_i(7, exp.hunks);
110
111 cl_assert_equal_i(12, exp.lines);
112 cl_assert_equal_i(4, exp.line_ctxt);
113 cl_assert_equal_i(3, exp.line_adds);
114 cl_assert_equal_i(5, exp.line_dels);
115
116 git_diff_free(diff);
117 git_index_free(index);
118 }
119
120 void test_diff_workdir__to_index_with_assume_unchanged(void)
121 {
122 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
123 git_diff *diff = NULL;
124 git_index *idx = NULL;
125 diff_expects exp;
126 const git_index_entry *iep;
127 git_index_entry ie;
128
129 g_repo = cl_git_sandbox_init("status");
130
131 /* do initial diff */
132
133 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
134 memset(&exp, 0, sizeof(exp));
135 cl_git_pass(git_diff_foreach(
136 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
137 cl_assert_equal_i(8, exp.files);
138 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
139 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
140 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
141 git_diff_free(diff);
142
143 /* mark a couple of entries with ASSUME_UNCHANGED */
144
145 cl_git_pass(git_repository_index(&idx, g_repo));
146
147 cl_assert((iep = git_index_get_bypath(idx, "modified_file", 0)) != NULL);
148 memcpy(&ie, iep, sizeof(ie));
149 ie.flags |= GIT_INDEX_ENTRY_VALID;
150 cl_git_pass(git_index_add(idx, &ie));
151
152 cl_assert((iep = git_index_get_bypath(idx, "file_deleted", 0)) != NULL);
153 memcpy(&ie, iep, sizeof(ie));
154 ie.flags |= GIT_INDEX_ENTRY_VALID;
155 cl_git_pass(git_index_add(idx, &ie));
156
157 cl_git_pass(git_index_write(idx));
158 git_index_free(idx);
159
160 /* redo diff and see that entries are skipped */
161
162 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
163 memset(&exp, 0, sizeof(exp));
164 cl_git_pass(git_diff_foreach(
165 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
166 cl_assert_equal_i(6, exp.files);
167 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
168 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
169 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
170 git_diff_free(diff);
171
172 }
173
174 void test_diff_workdir__to_tree(void)
175 {
176 /* grabbed a couple of commit oids from the history of the attr repo */
177 const char *a_commit = "26a125ee1bf"; /* the current HEAD */
178 const char *b_commit = "0017bd4ab1ec3"; /* the start */
179 git_tree *a, *b;
180 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
181 git_diff *diff = NULL;
182 git_diff *diff2 = NULL;
183 diff_expects exp;
184 int use_iterator;
185
186 g_repo = cl_git_sandbox_init("status");
187
188 a = resolve_commit_oid_to_tree(g_repo, a_commit);
189 b = resolve_commit_oid_to_tree(g_repo, b_commit);
190
191 opts.context_lines = 3;
192 opts.interhunk_lines = 1;
193 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
194
195 /* You can't really generate the equivalent of git_diff_tree_to_workdir()
196 * using C git. It really wants to interpose the index into the diff.
197 *
198 * To validate the following results with command line git, I ran the
199 * following:
200 * - git ls-tree 26a125
201 * - find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
202 * The results are documented at the bottom of this file in the
203 * long comment entitled "PREPARATION OF TEST DATA".
204 */
205 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
206
207 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
208 memset(&exp, 0, sizeof(exp));
209
210 if (use_iterator)
211 cl_git_pass(diff_foreach_via_iterator(
212 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
213 else
214 cl_git_pass(git_diff_foreach(
215 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
216
217 cl_assert_equal_i(14, exp.files);
218 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
219 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
220 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
221 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
222 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNTRACKED]);
223 }
224
225 /* Since there is no git diff equivalent, let's just assume that the
226 * text diffs produced by git_diff_foreach are accurate here. We will
227 * do more apples-to-apples test comparison below.
228 */
229
230 git_diff_free(diff);
231 diff = NULL;
232 memset(&exp, 0, sizeof(exp));
233
234 /* This is a compatible emulation of "git diff <sha>" which looks like
235 * a workdir to tree diff (even though it is not really). This is what
236 * you would get from "git diff --name-status 26a125ee1bf"
237 */
238 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
239 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
240 cl_git_pass(git_diff_merge(diff, diff2));
241 git_diff_free(diff2);
242
243 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
244 memset(&exp, 0, sizeof(exp));
245
246 if (use_iterator)
247 cl_git_pass(diff_foreach_via_iterator(
248 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
249 else
250 cl_git_pass(git_diff_foreach(
251 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
252
253 cl_assert_equal_i(15, exp.files);
254 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
255 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
256 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
257 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
258 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
259
260 cl_assert_equal_i(11, exp.hunks);
261
262 cl_assert_equal_i(17, exp.lines);
263 cl_assert_equal_i(4, exp.line_ctxt);
264 cl_assert_equal_i(8, exp.line_adds);
265 cl_assert_equal_i(5, exp.line_dels);
266 }
267
268 git_diff_free(diff);
269 diff = NULL;
270 memset(&exp, 0, sizeof(exp));
271
272 /* Again, emulating "git diff <sha>" for testing purposes using
273 * "git diff --name-status 0017bd4ab1ec3" instead.
274 */
275 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
276 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
277 cl_git_pass(git_diff_merge(diff, diff2));
278 git_diff_free(diff2);
279
280 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
281 memset(&exp, 0, sizeof(exp));
282
283 if (use_iterator)
284 cl_git_pass(diff_foreach_via_iterator(
285 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
286 else
287 cl_git_pass(git_diff_foreach(
288 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
289
290 cl_assert_equal_i(16, exp.files);
291 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_ADDED]);
292 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
293 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
294 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
295 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
296
297 cl_assert_equal_i(12, exp.hunks);
298
299 cl_assert_equal_i(19, exp.lines);
300 cl_assert_equal_i(3, exp.line_ctxt);
301 cl_assert_equal_i(12, exp.line_adds);
302 cl_assert_equal_i(4, exp.line_dels);
303 }
304
305 git_diff_free(diff);
306
307 /* Let's try that once more with a reversed diff */
308
309 opts.flags |= GIT_DIFF_REVERSE;
310
311 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
312 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
313 cl_git_pass(git_diff_merge(diff, diff2));
314 git_diff_free(diff2);
315
316 memset(&exp, 0, sizeof(exp));
317
318 cl_git_pass(git_diff_foreach(
319 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
320
321 cl_assert_equal_i(16, exp.files);
322 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
323 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_ADDED]);
324 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
325 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
326 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
327
328 cl_assert_equal_i(12, exp.hunks);
329
330 cl_assert_equal_i(19, exp.lines);
331 cl_assert_equal_i(3, exp.line_ctxt);
332 cl_assert_equal_i(12, exp.line_dels);
333 cl_assert_equal_i(4, exp.line_adds);
334
335 git_diff_free(diff);
336
337 /* all done now */
338
339 git_tree_free(a);
340 git_tree_free(b);
341 }
342
343 void test_diff_workdir__to_index_with_pathspec(void)
344 {
345 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
346 git_diff *diff = NULL;
347 diff_expects exp;
348 char *pathspec = NULL;
349 int use_iterator;
350
351 g_repo = cl_git_sandbox_init("status");
352
353 opts.context_lines = 3;
354 opts.interhunk_lines = 1;
355 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
356 opts.pathspec.strings = &pathspec;
357 opts.pathspec.count = 1;
358
359 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
360
361 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
362 memset(&exp, 0, sizeof(exp));
363
364 if (use_iterator)
365 cl_git_pass(diff_foreach_via_iterator(
366 diff, diff_file_cb, NULL, NULL, NULL, &exp));
367 else
368 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
369
370 cl_assert_equal_i(13, exp.files);
371 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
372 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
373 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
374 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
375 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
376 }
377
378 git_diff_free(diff);
379
380 pathspec = "modified_file";
381
382 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
383
384 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
385 memset(&exp, 0, sizeof(exp));
386
387 if (use_iterator)
388 cl_git_pass(diff_foreach_via_iterator(
389 diff, diff_file_cb, NULL, NULL, NULL, &exp));
390 else
391 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
392
393 cl_assert_equal_i(1, exp.files);
394 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
395 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
396 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
397 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
398 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
399 }
400
401 git_diff_free(diff);
402
403 pathspec = "subdir";
404
405 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
406
407 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
408 memset(&exp, 0, sizeof(exp));
409
410 if (use_iterator)
411 cl_git_pass(diff_foreach_via_iterator(
412 diff, diff_file_cb, NULL, NULL, NULL, &exp));
413 else
414 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
415
416 cl_assert_equal_i(3, exp.files);
417 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
418 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
419 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
420 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
421 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
422 }
423
424 git_diff_free(diff);
425
426 pathspec = "*_deleted";
427
428 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
429
430 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
431 memset(&exp, 0, sizeof(exp));
432
433 if (use_iterator)
434 cl_git_pass(diff_foreach_via_iterator(
435 diff, diff_file_cb, NULL, NULL, NULL, &exp));
436 else
437 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
438
439 cl_assert_equal_i(2, exp.files);
440 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
441 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
442 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
443 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
444 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
445 }
446
447 git_diff_free(diff);
448 }
449
450 void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void)
451 {
452 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
453 git_diff *diff = NULL;
454 diff_expects exp;
455 char *pathspec = NULL;
456 int use_iterator;
457
458 g_repo = cl_git_sandbox_init("status");
459
460 opts.context_lines = 3;
461 opts.interhunk_lines = 1;
462 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED |
463 GIT_DIFF_DISABLE_PATHSPEC_MATCH;
464 opts.pathspec.strings = &pathspec;
465 opts.pathspec.count = 0;
466
467 /* ensure that an empty pathspec list is ignored */
468 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
469
470 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
471 memset(&exp, 0, sizeof(exp));
472
473 if (use_iterator)
474 cl_git_pass(diff_foreach_via_iterator(
475 diff, diff_file_cb, NULL, NULL, NULL, &exp));
476 else
477 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
478
479 cl_assert_equal_i(13, exp.files);
480 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
481 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
482 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
483 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
484 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
485 }
486
487 git_diff_free(diff);
488
489 /* ensure that a single NULL pathspec is filtered out (like when using
490 * fnmatch filtering)
491 */
492
493 opts.pathspec.count = 1;
494
495 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
496
497 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
498 memset(&exp, 0, sizeof(exp));
499
500 if (use_iterator)
501 cl_git_pass(diff_foreach_via_iterator(
502 diff, diff_file_cb, NULL, NULL, NULL, &exp));
503 else
504 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
505
506 cl_assert_equal_i(13, exp.files);
507 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
508 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
509 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
510 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
511 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
512 }
513
514 git_diff_free(diff);
515
516 pathspec = "modified_file";
517
518 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
519
520 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
521 memset(&exp, 0, sizeof(exp));
522
523 if (use_iterator)
524 cl_git_pass(diff_foreach_via_iterator(
525 diff, diff_file_cb, NULL, NULL, NULL, &exp));
526 else
527 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
528
529 cl_assert_equal_i(1, exp.files);
530 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
531 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
532 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
533 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
534 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
535 }
536
537 git_diff_free(diff);
538
539 /* ensure that subdirs can be specified */
540 pathspec = "subdir";
541
542 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
543
544 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
545 memset(&exp, 0, sizeof(exp));
546
547 if (use_iterator)
548 cl_git_pass(diff_foreach_via_iterator(
549 diff, diff_file_cb, NULL, NULL, NULL, &exp));
550 else
551 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
552
553 cl_assert_equal_i(3, exp.files);
554 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
555 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
556 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
557 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
558 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
559 }
560
561 git_diff_free(diff);
562
563 /* ensure that subdirs can be specified with a trailing slash */
564 pathspec = "subdir/";
565
566 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
567
568 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
569 memset(&exp, 0, sizeof(exp));
570
571 if (use_iterator)
572 cl_git_pass(diff_foreach_via_iterator(
573 diff, diff_file_cb, NULL, NULL, NULL, &exp));
574 else
575 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
576
577 cl_assert_equal_i(3, exp.files);
578 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
579 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
580 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
581 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
582 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
583 }
584
585 git_diff_free(diff);
586
587 /* ensure that fnmatching is completely disabled */
588 pathspec = "subdir/*";
589
590 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
591
592 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
593 memset(&exp, 0, sizeof(exp));
594
595 if (use_iterator)
596 cl_git_pass(diff_foreach_via_iterator(
597 diff, diff_file_cb, NULL, NULL, NULL, &exp));
598 else
599 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
600
601 cl_assert_equal_i(0, exp.files);
602 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
603 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
604 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
605 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
606 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
607 }
608
609 git_diff_free(diff);
610
611 /* ensure that the prefix matching isn't completely braindead */
612 pathspec = "subdi";
613
614 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
615
616 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
617 memset(&exp, 0, sizeof(exp));
618
619 if (use_iterator)
620 cl_git_pass(diff_foreach_via_iterator(
621 diff, diff_file_cb, NULL, NULL, NULL, &exp));
622 else
623 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
624
625 cl_assert_equal_i(0, exp.files);
626 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
627 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
628 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
629 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
630 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
631 }
632
633 git_diff_free(diff);
634
635 /* ensure that fnmatching isn't working at all */
636 pathspec = "*_deleted";
637
638 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
639
640 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
641 memset(&exp, 0, sizeof(exp));
642
643 if (use_iterator)
644 cl_git_pass(diff_foreach_via_iterator(
645 diff, diff_file_cb, NULL, NULL, NULL, &exp));
646 else
647 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
648
649 cl_assert_equal_i(0, exp.files);
650 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
651 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
652 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
653 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
654 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
655 }
656
657 git_diff_free(diff);
658 }
659
660 void test_diff_workdir__filemode_changes(void)
661 {
662 git_diff *diff = NULL;
663 diff_expects exp;
664 int use_iterator;
665
666 if (!cl_is_chmod_supported())
667 return;
668
669 g_repo = cl_git_sandbox_init("issue_592");
670
671 cl_repo_set_bool(g_repo, "core.filemode", true);
672
673 /* test once with no mods */
674
675 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
676
677 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
678 memset(&exp, 0, sizeof(exp));
679
680 if (use_iterator)
681 cl_git_pass(diff_foreach_via_iterator(
682 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
683 else
684 cl_git_pass(git_diff_foreach(
685 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
686
687 cl_assert_equal_i(0, exp.files);
688 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
689 cl_assert_equal_i(0, exp.hunks);
690 }
691
692 git_diff_free(diff);
693
694 /* chmod file and test again */
695
696 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
697
698 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
699
700 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
701 memset(&exp, 0, sizeof(exp));
702
703 if (use_iterator)
704 cl_git_pass(diff_foreach_via_iterator(
705 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
706 else
707 cl_git_pass(git_diff_foreach(
708 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
709
710 cl_assert_equal_i(1, exp.files);
711 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
712 cl_assert_equal_i(0, exp.hunks);
713 }
714
715 git_diff_free(diff);
716
717 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
718 }
719
720 void test_diff_workdir__filemode_changes_with_filemode_false(void)
721 {
722 git_diff *diff = NULL;
723 diff_expects exp;
724
725 if (!cl_is_chmod_supported())
726 return;
727
728 g_repo = cl_git_sandbox_init("issue_592");
729
730 cl_repo_set_bool(g_repo, "core.filemode", false);
731
732 /* test once with no mods */
733
734 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
735
736 memset(&exp, 0, sizeof(exp));
737 cl_git_pass(git_diff_foreach(
738 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
739
740 cl_assert_equal_i(0, exp.files);
741 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
742 cl_assert_equal_i(0, exp.hunks);
743
744 git_diff_free(diff);
745
746 /* chmod file and test again */
747
748 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
749
750 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
751
752 memset(&exp, 0, sizeof(exp));
753 cl_git_pass(git_diff_foreach(diff,
754 diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
755
756 cl_assert_equal_i(0, exp.files);
757 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
758 cl_assert_equal_i(0, exp.hunks);
759
760 git_diff_free(diff);
761
762 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
763 }
764
765 void test_diff_workdir__head_index_and_workdir_all_differ(void)
766 {
767 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
768 git_diff *diff_i2t = NULL, *diff_w2i = NULL;
769 diff_expects exp;
770 char *pathspec = "staged_changes_modified_file";
771 git_tree *tree;
772 int use_iterator;
773
774 /* For this file,
775 * - head->index diff has 1 line of context, 1 line of diff
776 * - index->workdir diff has 2 lines of context, 1 line of diff
777 * but
778 * - head->workdir diff has 1 line of context, 2 lines of diff
779 * Let's make sure the right one is returned from each fn.
780 */
781
782 g_repo = cl_git_sandbox_init("status");
783
784 tree = resolve_commit_oid_to_tree(g_repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
785
786 opts.pathspec.strings = &pathspec;
787 opts.pathspec.count = 1;
788
789 cl_git_pass(git_diff_tree_to_index(&diff_i2t, g_repo, tree, NULL, &opts));
790 cl_git_pass(git_diff_index_to_workdir(&diff_w2i, g_repo, NULL, &opts));
791
792 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
793 memset(&exp, 0, sizeof(exp));
794
795 if (use_iterator)
796 cl_git_pass(diff_foreach_via_iterator(
797 diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
798 else
799 cl_git_pass(git_diff_foreach(
800 diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
801
802 cl_assert_equal_i(1, exp.files);
803 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
804 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
805 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
806 cl_assert_equal_i(1, exp.hunks);
807 cl_assert_equal_i(2, exp.lines);
808 cl_assert_equal_i(1, exp.line_ctxt);
809 cl_assert_equal_i(1, exp.line_adds);
810 cl_assert_equal_i(0, exp.line_dels);
811 }
812
813 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
814 memset(&exp, 0, sizeof(exp));
815
816 if (use_iterator)
817 cl_git_pass(diff_foreach_via_iterator(
818 diff_w2i, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
819 else
820 cl_git_pass(git_diff_foreach(
821 diff_w2i, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
822
823 cl_assert_equal_i(1, exp.files);
824 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
825 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
826 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
827 cl_assert_equal_i(1, exp.hunks);
828 cl_assert_equal_i(3, exp.lines);
829 cl_assert_equal_i(2, exp.line_ctxt);
830 cl_assert_equal_i(1, exp.line_adds);
831 cl_assert_equal_i(0, exp.line_dels);
832 }
833
834 cl_git_pass(git_diff_merge(diff_i2t, diff_w2i));
835
836 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
837 memset(&exp, 0, sizeof(exp));
838
839 if (use_iterator)
840 cl_git_pass(diff_foreach_via_iterator(
841 diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
842 else
843 cl_git_pass(git_diff_foreach(
844 diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
845
846 cl_assert_equal_i(1, exp.files);
847 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
848 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
849 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
850 cl_assert_equal_i(1, exp.hunks);
851 cl_assert_equal_i(3, exp.lines);
852 cl_assert_equal_i(1, exp.line_ctxt);
853 cl_assert_equal_i(2, exp.line_adds);
854 cl_assert_equal_i(0, exp.line_dels);
855 }
856
857 git_diff_free(diff_i2t);
858 git_diff_free(diff_w2i);
859
860 git_tree_free(tree);
861 }
862
863 void test_diff_workdir__eof_newline_changes(void)
864 {
865 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
866 git_diff *diff = NULL;
867 diff_expects exp;
868 char *pathspec = "current_file";
869 int use_iterator;
870
871 g_repo = cl_git_sandbox_init("status");
872
873 opts.pathspec.strings = &pathspec;
874 opts.pathspec.count = 1;
875
876 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
877
878 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
879 memset(&exp, 0, sizeof(exp));
880
881 if (use_iterator)
882 cl_git_pass(diff_foreach_via_iterator(
883 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
884 else
885 cl_git_pass(git_diff_foreach(
886 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
887
888 cl_assert_equal_i(0, exp.files);
889 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
890 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
891 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
892 cl_assert_equal_i(0, exp.hunks);
893 cl_assert_equal_i(0, exp.lines);
894 cl_assert_equal_i(0, exp.line_ctxt);
895 cl_assert_equal_i(0, exp.line_adds);
896 cl_assert_equal_i(0, exp.line_dels);
897 }
898
899 git_diff_free(diff);
900
901 cl_git_append2file("status/current_file", "\n");
902
903 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
904
905 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
906 memset(&exp, 0, sizeof(exp));
907
908 if (use_iterator)
909 cl_git_pass(diff_foreach_via_iterator(
910 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
911 else
912 cl_git_pass(git_diff_foreach(
913 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
914
915 cl_assert_equal_i(1, exp.files);
916 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
917 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
918 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
919 cl_assert_equal_i(1, exp.hunks);
920 cl_assert_equal_i(2, exp.lines);
921 cl_assert_equal_i(1, exp.line_ctxt);
922 cl_assert_equal_i(1, exp.line_adds);
923 cl_assert_equal_i(0, exp.line_dels);
924 }
925
926 git_diff_free(diff);
927
928 cl_git_rewritefile("status/current_file", "current_file");
929
930 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
931
932 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
933 memset(&exp, 0, sizeof(exp));
934
935 if (use_iterator)
936 cl_git_pass(diff_foreach_via_iterator(
937 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
938 else
939 cl_git_pass(git_diff_foreach(
940 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
941
942 cl_assert_equal_i(1, exp.files);
943 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
944 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
945 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
946 cl_assert_equal_i(1, exp.hunks);
947 cl_assert_equal_i(3, exp.lines);
948 cl_assert_equal_i(0, exp.line_ctxt);
949 cl_assert_equal_i(1, exp.line_adds);
950 cl_assert_equal_i(2, exp.line_dels);
951 }
952
953 git_diff_free(diff);
954 }
955
956 /* PREPARATION OF TEST DATA
957 *
958 * Since there is no command line equivalent of git_diff_tree_to_workdir,
959 * it was a bit of a pain to confirm that I was getting the expected
960 * results in the first part of this tests. Here is what I ended up
961 * doing to set my expectation for the file counts and results:
962 *
963 * Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
964 *
965 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
966 * B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
967 * C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
968 * D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
969 * E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
970 * F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
971 * G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
972 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
973 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
974 * J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
975 * K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
976 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
977 *
978 * --------
979 *
980 * find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
981 *
982 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
983 * M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
984 * C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
985 * N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
986 * D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
987 * F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
988 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
989 * O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
990 * P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
991 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
992 * K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
993 * Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
994 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
995 *
996 * --------
997 *
998 * A - current_file (UNMODIFIED) -> not in results
999 * B D file_deleted
1000 * M I ignored_file (IGNORED)
1001 * C M modified_file
1002 * N U new_file (UNTRACKED)
1003 * D M staged_changes
1004 * E D staged_changes_file_deleted
1005 * F M staged_changes_modified_file
1006 * G D staged_delete_file_deleted
1007 * H - staged_delete_modified_file (UNMODIFIED) -> not in results
1008 * O U staged_new_file
1009 * P U staged_new_file_modified_file
1010 * I - subdir/current_file (UNMODIFIED) -> not in results
1011 * J D subdir/deleted_file
1012 * K M subdir/modified_file
1013 * Q U subdir/new_file
1014 * L - subdir.txt (UNMODIFIED) -> not in results
1015 *
1016 * Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
1017 */
1018
1019
1020 void test_diff_workdir__larger_hunks(void)
1021 {
1022 const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
1023 const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
1024 git_tree *a, *b;
1025 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1026 size_t i, d, num_d, h, num_h, l, num_l;
1027
1028 g_repo = cl_git_sandbox_init("diff");
1029
1030 cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
1031 cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
1032
1033 opts.context_lines = 1;
1034 opts.interhunk_lines = 0;
1035
1036 for (i = 0; i <= 2; ++i) {
1037 git_diff *diff = NULL;
1038 git_patch *patch;
1039 const git_diff_hunk *hunk;
1040 const git_diff_line *line;
1041
1042 /* okay, this is a bit silly, but oh well */
1043 switch (i) {
1044 case 0:
1045 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1046 break;
1047 case 1:
1048 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
1049 break;
1050 case 2:
1051 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, b, &opts));
1052 break;
1053 }
1054
1055 num_d = git_diff_num_deltas(diff);
1056 cl_assert_equal_i(2, (int)num_d);
1057
1058 for (d = 0; d < num_d; ++d) {
1059 cl_git_pass(git_patch_from_diff(&patch, diff, d));
1060 cl_assert(patch);
1061
1062 num_h = git_patch_num_hunks(patch);
1063 for (h = 0; h < num_h; h++) {
1064 cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
1065
1066 for (l = 0; l < num_l; ++l) {
1067 cl_git_pass(
1068 git_patch_get_line_in_hunk(&line, patch, h, l));
1069 cl_assert(line);
1070 }
1071
1072 /* confirm fail after the last item */
1073 cl_git_fail(
1074 git_patch_get_line_in_hunk(&line, patch, h, num_l));
1075 }
1076
1077 /* confirm fail after the last item */
1078 cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h));
1079
1080 git_patch_free(patch);
1081 }
1082
1083 git_diff_free(diff);
1084 }
1085
1086 git_tree_free(a);
1087 git_tree_free(b);
1088 }
1089
1090 /* Set up a test that exercises this code. The easiest test using existing
1091 * test data is probably to create a sandbox of submod2 and then run a
1092 * git_diff_tree_to_workdir against tree
1093 * 873585b94bdeabccea991ea5e3ec1a277895b698. As for what you should actually
1094 * test, you can start by just checking that the number of lines of diff
1095 * content matches the actual output of git diff. That will at least
1096 * demonstrate that the submodule content is being used to generate somewhat
1097 * comparable outputs. It is a test that would fail without this code and
1098 * will succeed with it.
1099 */
1100
1101 #include "../submodule/submodule_helpers.h"
1102
1103 void test_diff_workdir__submodules(void)
1104 {
1105 const char *a_commit = "873585b94bdeabccea991ea5e3ec1a277895b698";
1106 git_tree *a;
1107 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1108 git_diff *diff = NULL;
1109 diff_expects exp;
1110
1111 g_repo = setup_fixture_submod2();
1112
1113 a = resolve_commit_oid_to_tree(g_repo, a_commit);
1114
1115 opts.flags =
1116 GIT_DIFF_INCLUDE_UNTRACKED |
1117 GIT_DIFF_INCLUDE_IGNORED |
1118 GIT_DIFF_RECURSE_UNTRACKED_DIRS |
1119 GIT_DIFF_SHOW_UNTRACKED_CONTENT;
1120
1121 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
1122
1123 /* diff_print(stderr, diff); */
1124
1125 /* essentially doing: git diff 873585b94bdeabccea991ea5e3ec1a277895b698 */
1126
1127 memset(&exp, 0, sizeof(exp));
1128
1129 cl_git_pass(git_diff_foreach(
1130 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
1131
1132 /* so "git diff 873585" returns:
1133 * M .gitmodules
1134 * A just_a_dir/contents
1135 * A just_a_file
1136 * A sm_added_and_uncommited
1137 * A sm_changed_file
1138 * A sm_changed_head
1139 * A sm_changed_index
1140 * A sm_changed_untracked_file
1141 * M sm_missing_commits
1142 * A sm_unchanged
1143 * which is a little deceptive because of the difference between the
1144 * "git diff <treeish>" results from "git_diff_tree_to_workdir". The
1145 * only significant difference is that those Added items will show up
1146 * as Untracked items in the pure libgit2 diff.
1147 *
1148 * Then add in the two extra untracked items "not" and "not-submodule"
1149 * to get the 12 files reported here.
1150 */
1151
1152 cl_assert_equal_i(12, exp.files);
1153
1154 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1155 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
1156 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
1157 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1158 cl_assert_equal_i(10, exp.file_status[GIT_DELTA_UNTRACKED]);
1159
1160 /* the following numbers match "git diff 873585" exactly */
1161
1162 cl_assert_equal_i(9, exp.hunks);
1163
1164 cl_assert_equal_i(33, exp.lines);
1165 cl_assert_equal_i(2, exp.line_ctxt);
1166 cl_assert_equal_i(30, exp.line_adds);
1167 cl_assert_equal_i(1, exp.line_dels);
1168
1169 git_diff_free(diff);
1170 git_tree_free(a);
1171 }
1172
1173 void test_diff_workdir__cannot_diff_against_a_bare_repository(void)
1174 {
1175 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1176 git_diff *diff = NULL;
1177 git_tree *tree;
1178
1179 g_repo = cl_git_sandbox_init("testrepo.git");
1180
1181 cl_assert_equal_i(
1182 GIT_EBAREREPO, git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1183
1184 cl_git_pass(git_repository_head_tree(&tree, g_repo));
1185
1186 cl_assert_equal_i(
1187 GIT_EBAREREPO, git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
1188
1189 git_tree_free(tree);
1190 }
1191
1192 void test_diff_workdir__to_null_tree(void)
1193 {
1194 git_diff *diff;
1195 diff_expects exp;
1196 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1197
1198 opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
1199 GIT_DIFF_RECURSE_UNTRACKED_DIRS;
1200
1201 g_repo = cl_git_sandbox_init("status");
1202
1203 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
1204
1205 memset(&exp, 0, sizeof(exp));
1206
1207 cl_git_pass(git_diff_foreach(
1208 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
1209
1210 cl_assert_equal_i(exp.files, exp.file_status[GIT_DELTA_UNTRACKED]);
1211
1212 git_diff_free(diff);
1213 }
1214
1215 void test_diff_workdir__checks_options_version(void)
1216 {
1217 git_diff *diff;
1218 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1219 const git_error *err;
1220
1221 g_repo = cl_git_sandbox_init("status");
1222
1223 opts.version = 0;
1224 cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
1225 err = git_error_last();
1226 cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
1227
1228 git_error_clear();
1229 opts.version = 1024;
1230 cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
1231 err = git_error_last();
1232 cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
1233 }
1234
1235 void test_diff_workdir__can_diff_empty_file(void)
1236 {
1237 git_diff *diff;
1238 git_tree *tree;
1239 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1240 struct stat st;
1241 git_patch *patch;
1242
1243 g_repo = cl_git_sandbox_init("attr_index");
1244
1245 tree = resolve_commit_oid_to_tree(g_repo, "3812cfef3661"); /* HEAD */
1246
1247 /* baseline - make sure there are no outstanding diffs */
1248
1249 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
1250 cl_assert_equal_i(2, (int)git_diff_num_deltas(diff));
1251 git_diff_free(diff);
1252
1253 /* empty contents of file */
1254
1255 cl_git_rewritefile("attr_index/README.txt", "");
1256 cl_git_pass(git_fs_path_lstat("attr_index/README.txt", &st));
1257 cl_assert_equal_i(0, (int)st.st_size);
1258
1259 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
1260 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
1261 /* diffs are: .gitattributes, README.txt, sub/sub/.gitattributes */
1262 cl_git_pass(git_patch_from_diff(&patch, diff, 1));
1263 git_patch_free(patch);
1264 git_diff_free(diff);
1265
1266 /* remove a file altogether */
1267
1268 cl_git_pass(p_unlink("attr_index/README.txt"));
1269 cl_assert(!git_fs_path_exists("attr_index/README.txt"));
1270
1271 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
1272 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
1273 cl_git_pass(git_patch_from_diff(&patch, diff, 1));
1274 git_patch_free(patch);
1275 git_diff_free(diff);
1276
1277 git_tree_free(tree);
1278 }
1279
1280 void test_diff_workdir__to_index_issue_1397(void)
1281 {
1282 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1283 git_diff *diff = NULL;
1284 diff_expects exp;
1285
1286 g_repo = cl_git_sandbox_init("issue_1397");
1287
1288 cl_repo_set_bool(g_repo, "core.autocrlf", true);
1289
1290 opts.context_lines = 3;
1291 opts.interhunk_lines = 1;
1292
1293 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1294
1295 memset(&exp, 0, sizeof(exp));
1296 cl_git_pass(git_diff_foreach(
1297 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
1298
1299 cl_assert_equal_i(0, exp.files);
1300 cl_assert_equal_i(0, exp.hunks);
1301 cl_assert_equal_i(0, exp.lines);
1302
1303 git_diff_free(diff);
1304 diff = NULL;
1305
1306 cl_git_rewritefile("issue_1397/crlf_file.txt",
1307 "first line\r\nsecond line modified\r\nboth with crlf");
1308
1309 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1310
1311 memset(&exp, 0, sizeof(exp));
1312 cl_git_pass(git_diff_foreach(
1313 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
1314
1315 cl_assert_equal_i(1, exp.files);
1316 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1317
1318 cl_assert_equal_i(1, exp.hunks);
1319
1320 cl_assert_equal_i(5, exp.lines);
1321 cl_assert_equal_i(3, exp.line_ctxt);
1322 cl_assert_equal_i(1, exp.line_adds);
1323 cl_assert_equal_i(1, exp.line_dels);
1324
1325 git_diff_free(diff);
1326 }
1327
1328 void test_diff_workdir__to_tree_issue_1397(void)
1329 {
1330 const char *a_commit = "7f483a738"; /* the current HEAD */
1331 git_tree *a;
1332 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1333 git_diff *diff = NULL;
1334 git_diff *diff2 = NULL;
1335 diff_expects exp;
1336
1337 g_repo = cl_git_sandbox_init("issue_1397");
1338
1339 cl_repo_set_bool(g_repo, "core.autocrlf", true);
1340
1341 a = resolve_commit_oid_to_tree(g_repo, a_commit);
1342
1343 opts.context_lines = 3;
1344 opts.interhunk_lines = 1;
1345
1346 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
1347
1348 memset(&exp, 0, sizeof(exp));
1349 cl_git_pass(git_diff_foreach(
1350 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
1351
1352 cl_assert_equal_i(0, exp.files);
1353 cl_assert_equal_i(0, exp.hunks);
1354 cl_assert_equal_i(0, exp.lines);
1355
1356 git_diff_free(diff);
1357 diff = NULL;
1358
1359 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
1360 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
1361 cl_git_pass(git_diff_merge(diff, diff2));
1362 git_diff_free(diff2);
1363
1364 memset(&exp, 0, sizeof(exp));
1365 cl_git_pass(git_diff_foreach(
1366 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
1367
1368 cl_assert_equal_i(0, exp.files);
1369 cl_assert_equal_i(0, exp.hunks);
1370 cl_assert_equal_i(0, exp.lines);
1371
1372 git_diff_free(diff);
1373 git_tree_free(a);
1374 }
1375
1376 void test_diff_workdir__untracked_directory_scenarios(void)
1377 {
1378 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1379 git_diff *diff = NULL;
1380 diff_expects exp;
1381 char *pathspec = NULL;
1382 static const char *files0[] = {
1383 "subdir/deleted_file",
1384 "subdir/modified_file",
1385 "subdir/new_file",
1386 NULL
1387 };
1388 static const char *files1[] = {
1389 "subdir/deleted_file",
1390 "subdir/directory/",
1391 "subdir/modified_file",
1392 "subdir/new_file",
1393 NULL
1394 };
1395 static const char *files2[] = {
1396 "subdir/deleted_file",
1397 "subdir/directory/more/notignored",
1398 "subdir/modified_file",
1399 "subdir/new_file",
1400 NULL
1401 };
1402
1403 g_repo = cl_git_sandbox_init("status");
1404 cl_git_mkfile("status/.gitignore", "ignored\n");
1405
1406 opts.context_lines = 3;
1407 opts.interhunk_lines = 1;
1408 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
1409 opts.pathspec.strings = &pathspec;
1410 opts.pathspec.count = 1;
1411 pathspec = "subdir";
1412
1413 /* baseline for "subdir" pathspec */
1414
1415 memset(&exp, 0, sizeof(exp));
1416 exp.names = files0;
1417
1418 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1419
1420 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
1421
1422 cl_assert_equal_i(3, exp.files);
1423 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1424 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1425 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1426 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1427 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1428
1429 git_diff_free(diff);
1430
1431 /* empty directory */
1432
1433 cl_git_pass(p_mkdir("status/subdir/directory", 0777));
1434
1435 memset(&exp, 0, sizeof(exp));
1436 exp.names = files1;
1437
1438 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1439
1440 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
1441
1442 cl_assert_equal_i(4, exp.files);
1443 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1444 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1445 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1446 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1447 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1448
1449 git_diff_free(diff);
1450
1451 /* empty directory in empty directory */
1452
1453 cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777));
1454
1455 memset(&exp, 0, sizeof(exp));
1456 exp.names = files1;
1457
1458 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1459
1460 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
1461
1462 cl_assert_equal_i(4, exp.files);
1463 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1464 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1465 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1466 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1467 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1468
1469 git_diff_free(diff);
1470
1471 /* directory with only ignored files */
1472
1473 cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
1474 cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n");
1475
1476 cl_git_pass(p_mkdir("status/subdir/directory/another", 0777));
1477 cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n");
1478
1479 memset(&exp, 0, sizeof(exp));
1480 exp.names = files1;
1481
1482 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1483
1484 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
1485
1486 cl_assert_equal_i(4, exp.files);
1487 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1488 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1489 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1490 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1491 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1492
1493 git_diff_free(diff);
1494
1495 /* directory with ignored directory (contents irrelevant) */
1496
1497 cl_git_pass(p_mkdir("status/subdir/directory/more", 0777));
1498 cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777));
1499 cl_git_mkfile("status/subdir/directory/more/ignored/notignored",
1500 "inside ignored dir\n");
1501
1502 memset(&exp, 0, sizeof(exp));
1503 exp.names = files1;
1504
1505 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1506
1507 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
1508
1509 cl_assert_equal_i(4, exp.files);
1510 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1511 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1512 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1513 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1514 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1515
1516 git_diff_free(diff);
1517
1518 /* quick version avoids directory scan */
1519
1520 opts.flags = opts.flags | GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
1521
1522 memset(&exp, 0, sizeof(exp));
1523 exp.names = files1;
1524
1525 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1526
1527 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
1528
1529 cl_assert_equal_i(4, exp.files);
1530 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1531 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1532 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1533 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1534 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
1535
1536 git_diff_free(diff);
1537
1538 /* directory with nested non-ignored content */
1539
1540 opts.flags = opts.flags & ~GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
1541
1542 cl_git_mkfile("status/subdir/directory/more/notignored",
1543 "not ignored deep under untracked\n");
1544
1545 memset(&exp, 0, sizeof(exp));
1546 exp.names = files1;
1547
1548 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1549
1550 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
1551
1552 cl_assert_equal_i(4, exp.files);
1553 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1554 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1555 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1556 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1557 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
1558
1559 git_diff_free(diff);
1560
1561 /* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
1562
1563 opts.flags = opts.flags & ~GIT_DIFF_INCLUDE_IGNORED;
1564 opts.flags = opts.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
1565
1566 memset(&exp, 0, sizeof(exp));
1567 exp.names = files2;
1568
1569 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1570
1571 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
1572
1573 cl_assert_equal_i(4, exp.files);
1574 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1575 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1576 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1577 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1578 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
1579
1580 git_diff_free(diff);
1581 }
1582
1583
1584 void test_diff_workdir__untracked_directory_comes_last(void)
1585 {
1586 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1587 git_diff *diff = NULL;
1588
1589 g_repo = cl_git_sandbox_init("renames");
1590
1591 cl_git_mkfile("renames/.gitignore", "*.ign\n");
1592 cl_git_pass(p_mkdir("renames/zzz_untracked", 0777));
1593 cl_git_mkfile("renames/zzz_untracked/an.ign", "ignore me please");
1594 cl_git_mkfile("renames/zzz_untracked/skip.ign", "ignore me really");
1595 cl_git_mkfile("renames/zzz_untracked/test.ign", "ignore me now");
1596
1597 opts.context_lines = 3;
1598 opts.interhunk_lines = 1;
1599 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
1600
1601 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1602
1603 cl_assert(diff != NULL);
1604
1605 git_diff_free(diff);
1606 }
1607
1608 void test_diff_workdir__untracked_with_bom(void)
1609 {
1610 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1611 git_diff *diff = NULL;
1612 const git_diff_delta *delta;
1613
1614 g_repo = cl_git_sandbox_init("empty_standard_repo");
1615 cl_repo_set_bool(g_repo, "core.autocrlf", true);
1616
1617 cl_git_write2file("empty_standard_repo/bom.txt",
1618 "\xFF\xFE\x31\x00\x32\x00\x33\x00\x34\x00", 10, O_WRONLY|O_CREAT, 0664);
1619
1620 opts.flags =
1621 GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT;
1622
1623 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1624
1625 cl_assert_equal_i(1, git_diff_num_deltas(diff));
1626 cl_assert((delta = git_diff_get_delta(diff, 0)) != NULL);
1627 cl_assert_equal_i(GIT_DELTA_UNTRACKED, delta->status);
1628
1629 /* not known at this point
1630 * cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
1631 */
1632
1633 git_diff_free(diff);
1634 }
1635
1636 void test_diff_workdir__patience_diff(void)
1637 {
1638 git_index *index;
1639 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1640 git_diff *diff = NULL;
1641 git_patch *patch = NULL;
1642 git_buf buf = GIT_BUF_INIT;
1643 const char *expected_normal = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n-how to create\n-a patience diff\n I did not know\n how to create\n+a patience diff\n another problem\n-I did not know\n-how to create\n a minimal diff\n";
1644 const char *expected_patience = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n+I did not know\n how to create\n a patience diff\n-I did not know\n-how to create\n another problem\n-I did not know\n-how to create\n a minimal diff\n";
1645
1646 g_repo = cl_git_sandbox_init("empty_standard_repo");
1647 cl_repo_set_bool(g_repo, "core.autocrlf", true);
1648 cl_git_pass(git_repository_index(&index, g_repo));
1649
1650 cl_git_mkfile(
1651 "empty_standard_repo/test.txt",
1652 "When I wrote this\nI did not know\nhow to create\na patience diff\nI did not know\nhow to create\nanother problem\nI did not know\nhow to create\na minimal diff\n");
1653 cl_git_pass(git_index_add_bypath(index, "test.txt"));
1654 cl_repo_commit_from_index(NULL, g_repo, NULL, 1372350000, "Base");
1655 git_index_free(index);
1656
1657 cl_git_rewritefile(
1658 "empty_standard_repo/test.txt",
1659 "When I wrote this\nI did not know\nI did not know\nhow to create\na patience diff\nanother problem\na minimal diff\n");
1660
1661 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1662 cl_assert_equal_i(1, git_diff_num_deltas(diff));
1663 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
1664 cl_git_pass(git_patch_to_buf(&buf, patch));
1665
1666 cl_assert_equal_s(expected_normal, buf.ptr);
1667 git_buf_dispose(&buf);
1668 git_patch_free(patch);
1669 git_diff_free(diff);
1670
1671 opts.flags |= GIT_DIFF_PATIENCE;
1672
1673 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1674 cl_assert_equal_i(1, git_diff_num_deltas(diff));
1675 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
1676 cl_git_pass(git_patch_to_buf(&buf, patch));
1677
1678 cl_assert_equal_s(expected_patience, buf.ptr);
1679 git_buf_dispose(&buf);
1680
1681 git_patch_free(patch);
1682 git_diff_free(diff);
1683 }
1684
1685 void test_diff_workdir__with_stale_index(void)
1686 {
1687 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1688 git_diff *diff = NULL;
1689 git_index *idx = NULL;
1690 diff_expects exp;
1691
1692 g_repo = cl_git_sandbox_init("status");
1693 cl_git_pass(git_repository_index(&idx, g_repo));
1694
1695 /* make the in-memory index invalid */
1696 {
1697 git_repository *r2;
1698 git_index *idx2;
1699 cl_git_pass(git_repository_open(&r2, "status"));
1700 cl_git_pass(git_repository_index(&idx2, r2));
1701 cl_git_pass(git_index_add_bypath(idx2, "new_file"));
1702 cl_git_pass(git_index_add_bypath(idx2, "subdir/new_file"));
1703 cl_git_pass(git_index_remove_bypath(idx2, "staged_new_file"));
1704 cl_git_pass(git_index_remove_bypath(idx2, "staged_changes_file_deleted"));
1705 cl_git_pass(git_index_write(idx2));
1706 git_index_free(idx2);
1707 git_repository_free(r2);
1708 }
1709
1710 opts.context_lines = 3;
1711 opts.interhunk_lines = 1;
1712 opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_INCLUDE_UNMODIFIED;
1713
1714 /* first try with index pointer which should prevent reload */
1715
1716 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, idx, &opts));
1717
1718 memset(&exp, 0, sizeof(exp));
1719
1720 cl_git_pass(git_diff_foreach(
1721 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
1722
1723 cl_assert_equal_i(17, exp.files);
1724 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1725 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
1726 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
1727 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
1728 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNMODIFIED]);
1729
1730 git_diff_free(diff);
1731
1732 /* now let's try without the index pointer which should trigger reload */
1733
1734 /* two files that were UNTRACKED should have become UNMODIFIED */
1735 /* one file that was UNMODIFIED should now have become UNTRACKED */
1736 /* one file that was DELETED should now be gone completely */
1737
1738 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1739
1740 memset(&exp, 0, sizeof(exp));
1741
1742 cl_git_pass(git_diff_foreach(
1743 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
1744
1745 git_diff_free(diff);
1746
1747 cl_assert_equal_i(16, exp.files);
1748 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1749 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
1750 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
1751 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
1752 cl_assert_equal_i(6, exp.file_status[GIT_DELTA_UNMODIFIED]);
1753
1754 git_index_free(idx);
1755 }
1756
1757 static int touch_file(void *payload, git_str *path)
1758 {
1759 struct stat st;
1760 struct p_timeval times[2];
1761
1762 GIT_UNUSED(payload);
1763 if (git_fs_path_isdir(path->ptr))
1764 return 0;
1765
1766 cl_must_pass(p_stat(path->ptr, &st));
1767
1768 times[0].tv_sec = st.st_mtime + 3;
1769 times[0].tv_usec = 0;
1770 times[1].tv_sec = st.st_mtime + 3;
1771 times[1].tv_usec = 0;
1772
1773 cl_must_pass(p_utimes(path->ptr, times));
1774 return 0;
1775 }
1776
1777 static void basic_diff_status(git_diff **out, const git_diff_options *opts)
1778 {
1779 diff_expects exp;
1780
1781 cl_git_pass(git_diff_index_to_workdir(out, g_repo, NULL, opts));
1782
1783 memset(&exp, 0, sizeof(exp));
1784
1785 cl_git_pass(git_diff_foreach(
1786 *out, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
1787
1788 cl_assert_equal_i(13, exp.files);
1789 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1790 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
1791 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
1792 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1793 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
1794 }
1795
1796 void test_diff_workdir__can_update_index(void)
1797 {
1798 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1799 git_diff *diff = NULL;
1800 git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
1801 git_index *index;
1802
1803 g_repo = cl_git_sandbox_init("status");
1804
1805 /* touch all the files so stat times are different */
1806 {
1807 git_str path = GIT_STR_INIT;
1808 cl_git_pass(git_str_sets(&path, "status"));
1809 cl_git_pass(git_fs_path_direach(&path, 0, touch_file, NULL));
1810 git_str_dispose(&path);
1811 }
1812
1813 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
1814
1815 basic_diff_status(&diff, &opts);
1816
1817 cl_git_pass(git_diff_get_perfdata(&perf, diff));
1818 cl_assert_equal_sz(13 + 3, perf.stat_calls);
1819 cl_assert_equal_sz(5, perf.oid_calculations);
1820
1821 git_diff_free(diff);
1822
1823 /* now allow diff to update stat cache */
1824 opts.flags |= GIT_DIFF_UPDATE_INDEX;
1825
1826 /* advance a tick for the index so we don't re-calculate racily-clean entries */
1827 cl_git_pass(git_repository_index__weakptr(&index, g_repo));
1828 tick_index(index);
1829
1830 basic_diff_status(&diff, &opts);
1831
1832 cl_git_pass(git_diff_get_perfdata(&perf, diff));
1833 cl_assert_equal_sz(13 + 3, perf.stat_calls);
1834 cl_assert_equal_sz(5, perf.oid_calculations);
1835
1836 git_diff_free(diff);
1837
1838 /* now if we do it again, we should see fewer OID calculations */
1839
1840 /* tick again as the index updating from the previous diff might have reset the timestamp */
1841 tick_index(index);
1842 basic_diff_status(&diff, &opts);
1843
1844 cl_git_pass(git_diff_get_perfdata(&perf, diff));
1845 cl_assert_equal_sz(13 + 3, perf.stat_calls);
1846 cl_assert_equal_sz(0, perf.oid_calculations);
1847
1848 git_diff_free(diff);
1849 }
1850
1851 #define STR7 "0123456"
1852 #define STR8 "01234567"
1853 #define STR40 STR8 STR8 STR8 STR8 STR8
1854 #define STR200 STR40 STR40 STR40 STR40 STR40
1855 #define STR999Z STR200 STR200 STR200 STR200 STR40 STR40 STR40 STR40 \
1856 STR8 STR8 STR8 STR8 STR7 "\0"
1857 #define STR1000 STR200 STR200 STR200 STR200 STR200
1858 #define STR3999Z STR1000 STR1000 STR1000 STR999Z
1859 #define STR4000 STR1000 STR1000 STR1000 STR1000
1860
1861 static void assert_delta_binary(git_diff *diff, size_t idx, int is_binary)
1862 {
1863 git_patch *patch;
1864 const git_diff_delta *delta;
1865
1866 cl_git_pass(git_patch_from_diff(&patch, diff, idx));
1867 delta = git_patch_get_delta(patch);
1868 cl_assert_equal_b((delta->flags & GIT_DIFF_FLAG_BINARY), is_binary);
1869 git_patch_free(patch);
1870 }
1871
1872 void test_diff_workdir__binary_detection(void)
1873 {
1874 git_index *idx;
1875 git_diff *diff = NULL;
1876 git_str b = GIT_STR_INIT;
1877 int i;
1878 git_str data[10] = {
1879 { "1234567890", 0, 10 }, /* 0 - all ascii text control */
1880 { "\xC3\x85\xC3\xBC\xE2\x80\xA0\x48\xC3\xB8\xCF\x80\xCE\xA9", 0, 14 }, /* 1 - UTF-8 multibyte text */
1881 { "\xEF\xBB\xBF\xC3\x9C\xE2\xA4\x92\xC6\x92\x38\xC2\xA3\xE2\x82\xAC", 0, 16 }, /* 2 - UTF-8 with BOM */
1882 { STR999Z, 0, 1000 }, /* 3 - ASCII with NUL at 1000 */
1883 { STR3999Z, 0, 4000 }, /* 4 - ASCII with NUL at 4000 */
1884 { STR4000 STR3999Z "x", 0, 8001 }, /* 5 - ASCII with NUL at 8000 */
1885 { STR4000 STR4000 "\0", 0, 8001 }, /* 6 - ASCII with NUL at 8001 */
1886 { "\x00\xDC\x00\x6E\x21\x39\xFE\x0E\x00\x63\x00\xF8"
1887 "\x00\x64\x00\x65\x20\x48", 0, 18 }, /* 7 - UTF-16 text */
1888 { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
1889 "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d",
1890 0, 26 }, /* 8 - All non-printable characters (no NUL) */
1891 { "Hello \x01\x02\x03\x04\x05\x06 World!\x01\x02\x03\x04"
1892 "\x05\x06\x07", 0, 26 }, /* 9 - 50-50 non-printable (no NUL) */
1893 };
1894
1895 g_repo = cl_git_sandbox_init("empty_standard_repo");
1896 cl_git_pass(git_repository_index(&idx, g_repo));
1897
1898 /* We start with ASCII in index and test data in workdir,
1899 * then we will try with test data in index and ASCII in workdir.
1900 */
1901
1902 cl_git_pass(git_str_sets(&b, "empty_standard_repo/0"));
1903 for (i = 0; i < 10; ++i) {
1904 b.ptr[b.size - 1] = '0' + i;
1905 cl_git_mkfile(b.ptr, "baseline");
1906 cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
1907
1908 if (data[i].size == 0)
1909 data[i].size = strlen(data[i].ptr);
1910 cl_git_write2file(
1911 b.ptr, data[i].ptr, data[i].size, O_WRONLY|O_TRUNC, 0664);
1912 }
1913 cl_git_pass(git_index_write(idx));
1914
1915 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
1916
1917 cl_assert_equal_i(10, git_diff_num_deltas(diff));
1918
1919 /* using diff binary detection (i.e. looking for NUL byte) */
1920 assert_delta_binary(diff, 0, false);
1921 assert_delta_binary(diff, 1, false);
1922 assert_delta_binary(diff, 2, false);
1923 assert_delta_binary(diff, 3, true);
1924 assert_delta_binary(diff, 4, true);
1925 assert_delta_binary(diff, 5, true);
1926 assert_delta_binary(diff, 6, false);
1927 assert_delta_binary(diff, 7, true);
1928 assert_delta_binary(diff, 8, false);
1929 assert_delta_binary(diff, 9, false);
1930 /* The above have been checked to match command-line Git */
1931
1932 git_diff_free(diff);
1933
1934 cl_git_pass(git_str_sets(&b, "empty_standard_repo/0"));
1935 for (i = 0; i < 10; ++i) {
1936 b.ptr[b.size - 1] = '0' + i;
1937 cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
1938
1939 cl_git_write2file(b.ptr, "baseline\n", 9, O_WRONLY|O_TRUNC, 0664);
1940 }
1941 cl_git_pass(git_index_write(idx));
1942
1943 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
1944
1945 cl_assert_equal_i(10, git_diff_num_deltas(diff));
1946
1947 /* using diff binary detection (i.e. looking for NUL byte) */
1948 assert_delta_binary(diff, 0, false);
1949 assert_delta_binary(diff, 1, false);
1950 assert_delta_binary(diff, 2, false);
1951 assert_delta_binary(diff, 3, true);
1952 assert_delta_binary(diff, 4, true);
1953 assert_delta_binary(diff, 5, true);
1954 assert_delta_binary(diff, 6, false);
1955 assert_delta_binary(diff, 7, true);
1956 assert_delta_binary(diff, 8, false);
1957 assert_delta_binary(diff, 9, false);
1958
1959 git_diff_free(diff);
1960
1961 git_index_free(idx);
1962 git_str_dispose(&b);
1963 }
1964
1965 void test_diff_workdir__to_index_conflicted(void) {
1966 const char *a_commit = "26a125ee1bf"; /* the current HEAD */
1967 git_index_entry ancestor = {{0}}, ours = {{0}}, theirs = {{0}};
1968 git_tree *a;
1969 git_index *index;
1970 git_diff *diff1, *diff2;
1971 const git_diff_delta *delta;
1972
1973 g_repo = cl_git_sandbox_init("status");
1974 a = resolve_commit_oid_to_tree(g_repo, a_commit);
1975
1976 cl_git_pass(git_repository_index(&index, g_repo));
1977
1978 ancestor.path = ours.path = theirs.path = "_file";
1979 ancestor.mode = ours.mode = theirs.mode = 0100644;
1980 git_oid_fromstr(&ancestor.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
1981 git_oid_fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
1982 git_oid_fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
1983 cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs));
1984
1985 cl_git_pass(git_diff_tree_to_index(&diff1, g_repo, a, index, NULL));
1986 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, index, NULL));
1987 cl_git_pass(git_diff_merge(diff1, diff2));
1988
1989 cl_assert_equal_i(git_diff_num_deltas(diff1), 12);
1990 delta = git_diff_get_delta(diff1, 0);
1991 cl_assert_equal_s(delta->old_file.path, "_file");
1992 cl_assert_equal_i(delta->nfiles, 1);
1993 cl_assert_equal_i(delta->status, GIT_DELTA_CONFLICTED);
1994
1995 git_diff_free(diff2);
1996 git_diff_free(diff1);
1997 git_index_free(index);
1998 git_tree_free(a);
1999 }
2000
2001 void test_diff_workdir__only_writes_index_when_necessary(void)
2002 {
2003 git_index *index;
2004 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
2005 git_diff *diff = NULL;
2006 git_reference *head;
2007 git_object *head_object;
2008 unsigned char initial[GIT_HASH_SHA1_SIZE],
2009 first[GIT_HASH_SHA1_SIZE],
2010 second[GIT_HASH_SHA1_SIZE];
2011 git_str path = GIT_STR_INIT;
2012 struct stat st;
2013 struct p_timeval times[2];
2014
2015 opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_UPDATE_INDEX;
2016
2017 g_repo = cl_git_sandbox_init("status");
2018
2019 cl_git_pass(git_repository_index(&index, g_repo));
2020 cl_git_pass(git_repository_head(&head, g_repo));
2021 cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJECT_COMMIT));
2022
2023 cl_git_pass(git_reset(g_repo, head_object, GIT_RESET_HARD, NULL));
2024
2025 memcpy(initial, git_index__checksum(index), GIT_HASH_SHA1_SIZE);
2026
2027 /* update the index timestamp to avoid raciness */
2028 cl_must_pass(p_stat("status/.git/index", &st));
2029
2030 times[0].tv_sec = st.st_mtime + 5;
2031 times[0].tv_usec = 0;
2032 times[1].tv_sec = st.st_mtime + 5;
2033 times[1].tv_usec = 0;
2034
2035 cl_must_pass(p_utimes("status/.git/index", times));
2036
2037 /* ensure diff doesn't touch the index */
2038 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
2039 git_diff_free(diff);
2040
2041 memcpy(first, git_index__checksum(index), GIT_HASH_SHA1_SIZE);
2042 cl_assert(memcmp(initial, first, GIT_HASH_SHA1_SIZE) != 0);
2043
2044 /* touch all the files so stat times are different */
2045 cl_git_pass(git_str_sets(&path, "status"));
2046 cl_git_pass(git_fs_path_direach(&path, 0, touch_file, NULL));
2047
2048 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
2049 git_diff_free(diff);
2050
2051 /* ensure the second diff did update the index */
2052 memcpy(second, git_index__checksum(index), GIT_HASH_SHA1_SIZE);
2053 cl_assert(memcmp(first, second, GIT_HASH_SHA1_SIZE) != 0);
2054
2055 git_str_dispose(&path);
2056 git_object_free(head_object);
2057 git_reference_free(head);
2058 git_index_free(index);
2059 }
2060
2061 void test_diff_workdir__to_index_pathlist(void)
2062 {
2063 git_index *index;
2064 git_diff *diff;
2065 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
2066 git_vector pathlist = GIT_VECTOR_INIT;
2067
2068 git_vector_insert(&pathlist, "foobar/asdf");
2069 git_vector_insert(&pathlist, "subdir/asdf");
2070 git_vector_insert(&pathlist, "ignored/asdf");
2071
2072 g_repo = cl_git_sandbox_init("status");
2073
2074 cl_git_mkfile("status/.gitignore", ".gitignore\n" "ignored/\n");
2075
2076 cl_must_pass(p_mkdir("status/foobar", 0777));
2077 cl_git_mkfile("status/foobar/one", "one\n");
2078
2079 cl_must_pass(p_mkdir("status/ignored", 0777));
2080 cl_git_mkfile("status/ignored/one", "one\n");
2081 cl_git_mkfile("status/ignored/two", "two\n");
2082 cl_git_mkfile("status/ignored/three", "three\n");
2083
2084 cl_git_pass(git_repository_index(&index, g_repo));
2085
2086 opts.flags = GIT_DIFF_INCLUDE_IGNORED;
2087 opts.pathspec.strings = (char **)pathlist.contents;
2088 opts.pathspec.count = pathlist.length;
2089
2090 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
2091 cl_assert_equal_i(0, git_diff_num_deltas(diff));
2092 git_diff_free(diff);
2093
2094 opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
2095
2096 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
2097 cl_assert_equal_i(0, git_diff_num_deltas(diff));
2098 git_diff_free(diff);
2099
2100 git_index_free(index);
2101 git_vector_free(&pathlist);
2102 }
2103
2104 void test_diff_workdir__symlink_changed_on_non_symlink_platform(void)
2105 {
2106 git_tree *tree;
2107 git_diff *diff;
2108 diff_expects exp = {0};
2109 const git_diff_delta *delta;
2110 const char *commit = "7fccd7";
2111 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
2112 git_vector pathlist = GIT_VECTOR_INIT;
2113 int symlinks;
2114
2115 g_repo = cl_git_sandbox_init("unsymlinked.git");
2116
2117 cl_git_pass(git_repository__configmap_lookup(&symlinks, g_repo, GIT_CONFIGMAP_SYMLINKS));
2118
2119 if (symlinks)
2120 cl_skip();
2121
2122 cl_git_pass(git_vector_insert(&pathlist, "include/Nu/Nu.h"));
2123
2124 opts.pathspec.strings = (char **)pathlist.contents;
2125 opts.pathspec.count = pathlist.length;
2126
2127 cl_must_pass(p_mkdir("symlink", 0777));
2128 cl_git_pass(git_repository_set_workdir(g_repo, "symlink", false));
2129
2130 cl_assert((tree = resolve_commit_oid_to_tree(g_repo, commit)) != NULL);
2131
2132 /* first, do the diff with the original contents */
2133
2134 cl_git_pass(git_futils_mkpath2file("symlink/include/Nu/Nu.h", 0755));
2135 cl_git_mkfile("symlink/include/Nu/Nu.h", "../../objc/Nu.h");
2136
2137 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
2138 cl_assert_equal_i(0, git_diff_num_deltas(diff));
2139 git_diff_free(diff);
2140
2141 /* now update the contents and expect a difference, but that the file
2142 * mode has persisted as a symbolic link.
2143 */
2144
2145 cl_git_rewritefile("symlink/include/Nu/Nu.h", "awesome content\n");
2146
2147 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
2148
2149 cl_git_pass(git_diff_foreach(
2150 diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
2151 cl_assert_equal_i(1, exp.files);
2152
2153 cl_assert_equal_i(1, git_diff_num_deltas(diff));
2154 delta = git_diff_get_delta(diff, 0);
2155 cl_assert_equal_i(GIT_FILEMODE_LINK, delta->old_file.mode);
2156 cl_assert_equal_i(GIT_FILEMODE_LINK, delta->new_file.mode);
2157
2158 git_diff_free(diff);
2159
2160 cl_git_pass(git_futils_rmdir_r("symlink", NULL, GIT_RMDIR_REMOVE_FILES));
2161
2162 git_tree_free(tree);
2163 git_vector_free(&pathlist);
2164 }
2165
2166 void test_diff_workdir__order(void)
2167 {
2168 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
2169 git_buf patch = GIT_BUF_INIT;
2170 git_oid tree_oid, blob_oid;
2171 git_treebuilder *builder;
2172 git_tree *tree;
2173 git_diff *diff;
2174
2175 g_repo = cl_git_sandbox_init("empty_standard_repo");
2176
2177 /* Build tree with a single file "abc.txt" */
2178 cl_git_pass(git_blob_create_from_buffer(&blob_oid, g_repo, "foo\n", 4));
2179 cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
2180 cl_git_pass(git_treebuilder_insert(NULL, builder, "abc.txt", &blob_oid, GIT_FILEMODE_BLOB));
2181 cl_git_pass(git_treebuilder_write(&tree_oid, builder));
2182 cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
2183
2184 /* Create a directory that sorts before and one that sorts after "abc.txt" */
2185 cl_git_mkfile("empty_standard_repo/abc.txt", "bar\n");
2186 cl_must_pass(p_mkdir("empty_standard_repo/abb", 0777));
2187 cl_must_pass(p_mkdir("empty_standard_repo/abd", 0777));
2188
2189 opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
2190 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
2191
2192 cl_assert_equal_i(1, git_diff_num_deltas(diff));
2193 cl_git_pass(git_diff_to_buf(&patch, diff, GIT_DIFF_FORMAT_PATCH));
2194 cl_assert_equal_s(patch.ptr,
2195 "diff --git a/abc.txt b/abc.txt\n"
2196 "index 257cc56..5716ca5 100644\n"
2197 "--- a/abc.txt\n"
2198 "+++ b/abc.txt\n"
2199 "@@ -1 +1 @@\n"
2200 "-foo\n"
2201 "+bar\n");
2202
2203 git_treebuilder_free(builder);
2204 git_buf_dispose(&patch);
2205 git_diff_free(diff);
2206 git_tree_free(tree);
2207 }
2208
2209 void test_diff_workdir__ignore_blank_lines(void)
2210 {
2211 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
2212 git_diff *diff;
2213 git_patch *patch;
2214 git_buf buf = GIT_BUF_INIT;
2215
2216 g_repo = cl_git_sandbox_init("rebase");
2217 cl_git_rewritefile("rebase/gravy.txt", "GRAVY SOUP.\n\n\nGet eight pounds of coarse lean beef--wash it clean and lay it in your\n\npot, put in the same ingredients as for the shin soup, with the same\nquantity of water, and follow the process directed for that. Strain the\nsoup through a sieve, and serve it up clear, with nothing more than\ntoasted bread in it; two table-spoonsful of mushroom catsup will add a\nfine flavour to the soup!\n");
2218
2219 /* Perform the diff normally */
2220 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
2221 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
2222 cl_git_pass(git_patch_to_buf(&buf, patch));
2223
2224 cl_assert_equal_s("diff --git a/gravy.txt b/gravy.txt\nindex c4e6cca..3c617e6 100644\n--- a/gravy.txt\n+++ b/gravy.txt\n@@ -1,8 +1,10 @@\n GRAVY SOUP.\n \n+\n Get eight pounds of coarse lean beef--wash it clean and lay it in your\n+\n pot, put in the same ingredients as for the shin soup, with the same\n quantity of water, and follow the process directed for that. Strain the\n soup through a sieve, and serve it up clear, with nothing more than\n toasted bread in it; two table-spoonsful of mushroom catsup will add a\n-fine flavour to the soup.\n+fine flavour to the soup!\n", buf.ptr);
2225
2226 git_buf_dispose(&buf);
2227 git_patch_free(patch);
2228 git_diff_free(diff);
2229
2230 /* Perform the diff ignoring blank lines */
2231 opts.flags |= GIT_DIFF_IGNORE_BLANK_LINES;
2232
2233 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
2234 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
2235 cl_git_pass(git_patch_to_buf(&buf, patch));
2236
2237 cl_assert_equal_s("diff --git a/gravy.txt b/gravy.txt\nindex c4e6cca..3c617e6 100644\n--- a/gravy.txt\n+++ b/gravy.txt\n@@ -5,4 +7,4 @@ pot, put in the same ingredients as for the shin soup, with the same\n quantity of water, and follow the process directed for that. Strain the\n soup through a sieve, and serve it up clear, with nothing more than\n toasted bread in it; two table-spoonsful of mushroom catsup will add a\n-fine flavour to the soup.\n+fine flavour to the soup!\n", buf.ptr);
2238
2239 git_buf_dispose(&buf);
2240 git_patch_free(patch);
2241 git_diff_free(diff);
2242 }