]> git.proxmox.com Git - libgit2.git/blame - tests/diff/workdir.c
Include git2/transaction.h
[libgit2.git] / tests / diff / workdir.c
CommitLineData
74fa4bfa
RB
1#include "clar_libgit2.h"
2#include "diff_helpers.h"
c2e43fb1 3#include "repository.h"
9c8ed499 4#include "git2/sys/diff.h"
240f4af3 5
74fa4bfa
RB
6static git_repository *g_repo = NULL;
7
74fa4bfa
RB
8void test_diff_workdir__cleanup(void)
9{
854eccbb 10 cl_git_sandbox_cleanup();
74fa4bfa
RB
11}
12
13void test_diff_workdir__to_index(void)
14{
2f8d30be 15 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 16 git_diff *diff = NULL;
74fa4bfa 17 diff_expects exp;
f335ecd6 18 int use_iterator;
74fa4bfa 19
0abd7244
RB
20 g_repo = cl_git_sandbox_init("status");
21
74fa4bfa
RB
22 opts.context_lines = 3;
23 opts.interhunk_lines = 1;
24 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
25
56c72b75 26 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
74fa4bfa 27
f335ecd6
RB
28 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
29 memset(&exp, 0, sizeof(exp));
30
31 if (use_iterator)
32 cl_git_pass(diff_foreach_via_iterator(
793c4385 33 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
34 else
35 cl_git_pass(git_diff_foreach(
793c4385 36 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
37
38 /* to generate these values:
39 * - cd to tests/resources/status,
40 * - mv .gitted .git
41 * - git diff --name-status
42 * - git diff
43 * - mv .git .gitted
44 */
45 cl_assert_equal_i(13, exp.files);
b4f5bb07
RB
46 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
47 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
48 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
49 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
50 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6
RB
51
52 cl_assert_equal_i(8, exp.hunks);
53
54 cl_assert_equal_i(14, exp.lines);
55 cl_assert_equal_i(5, exp.line_ctxt);
56 cl_assert_equal_i(4, exp.line_adds);
57 cl_assert_equal_i(5, exp.line_dels);
9c8ed499 58 }
240f4af3 59
9c8ed499
RB
60 {
61 git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
62 cl_git_pass(git_diff_get_perfdata(&perf, diff));
240f4af3 63 cl_assert_equal_sz(
9c8ed499
RB
64 13 /* in root */ + 3 /* in subdir */, perf.stat_calls);
65 cl_assert_equal_sz(5, perf.oid_calculations);
f335ecd6 66 }
74fa4bfa 67
3ff1d123 68 git_diff_free(diff);
74fa4bfa
RB
69}
70
b22369ef
ET
71void test_diff_workdir__to_index_with_conflicts(void)
72{
73 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
74 git_diff *diff = NULL;
75 git_index *index;
76 git_index_entry our_entry = {0}, their_entry = {0};
77 diff_expects exp = {0};
78
79 g_repo = cl_git_sandbox_init("status");
80
81 opts.context_lines = 3;
82 opts.interhunk_lines = 1;
83
84 /* Adding an entry that represents a rename gets two files in conflict */
85 our_entry.path = "subdir/modified_file";
86 our_entry.mode = 0100644;
87
88 their_entry.path = "subdir/rename_conflict";
89 their_entry.mode = 0100644;
90
91 cl_git_pass(git_repository_index(&index, g_repo));
92 cl_git_pass(git_index_conflict_add(index, NULL, &our_entry, &their_entry));
93
94 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
95
96 cl_git_pass(diff_foreach_via_iterator(
97 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
98
99 cl_assert_equal_i(9, exp.files);
100 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
101 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
102 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
103 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_CONFLICTED]);
104
105 cl_assert_equal_i(7, exp.hunks);
106
107 cl_assert_equal_i(12, exp.lines);
108 cl_assert_equal_i(4, exp.line_ctxt);
109 cl_assert_equal_i(3, exp.line_adds);
110 cl_assert_equal_i(5, exp.line_dels);
111
112 git_diff_free(diff);
113 git_index_free(index);
114}
115
3e57069e
RB
116void test_diff_workdir__to_index_with_assume_unchanged(void)
117{
118 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
119 git_diff *diff = NULL;
120 git_index *idx = NULL;
121 diff_expects exp;
122 const git_index_entry *iep;
123 git_index_entry ie;
124
125 g_repo = cl_git_sandbox_init("status");
126
127 /* do initial diff */
128
129 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
130 memset(&exp, 0, sizeof(exp));
131 cl_git_pass(git_diff_foreach(
132 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
133 cl_assert_equal_i(8, exp.files);
134 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
135 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
136 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
137 git_diff_free(diff);
138
139 /* mark a couple of entries with ASSUME_UNCHANGED */
140
141 cl_git_pass(git_repository_index(&idx, g_repo));
142
143 cl_assert((iep = git_index_get_bypath(idx, "modified_file", 0)) != NULL);
144 memcpy(&ie, iep, sizeof(ie));
145 ie.flags |= GIT_IDXENTRY_VALID;
146 cl_git_pass(git_index_add(idx, &ie));
147
148 cl_assert((iep = git_index_get_bypath(idx, "file_deleted", 0)) != NULL);
149 memcpy(&ie, iep, sizeof(ie));
150 ie.flags |= GIT_IDXENTRY_VALID;
151 cl_git_pass(git_index_add(idx, &ie));
152
153 cl_git_pass(git_index_write(idx));
154 git_index_free(idx);
155
156 /* redo diff and see that entries are skipped */
157
158 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
159 memset(&exp, 0, sizeof(exp));
160 cl_git_pass(git_diff_foreach(
161 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
162 cl_assert_equal_i(6, exp.files);
163 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
164 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
165 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
166 git_diff_free(diff);
167
168}
169
74fa4bfa
RB
170void test_diff_workdir__to_tree(void)
171{
172 /* grabbed a couple of commit oids from the history of the attr repo */
173 const char *a_commit = "26a125ee1bf"; /* the current HEAD */
174 const char *b_commit = "0017bd4ab1ec3"; /* the start */
0abd7244 175 git_tree *a, *b;
2f8d30be 176 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123
RB
177 git_diff *diff = NULL;
178 git_diff *diff2 = NULL;
74fa4bfa 179 diff_expects exp;
f335ecd6 180 int use_iterator;
74fa4bfa 181
0abd7244
RB
182 g_repo = cl_git_sandbox_init("status");
183
184 a = resolve_commit_oid_to_tree(g_repo, a_commit);
185 b = resolve_commit_oid_to_tree(g_repo, b_commit);
186
74fa4bfa
RB
187 opts.context_lines = 3;
188 opts.interhunk_lines = 1;
189 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
190
56c72b75 191 /* You can't really generate the equivalent of git_diff_tree_to_workdir()
74fa4bfa
RB
192 * using C git. It really wants to interpose the index into the diff.
193 *
194 * To validate the following results with command line git, I ran the
195 * following:
196 * - git ls-tree 26a125
197 * - find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
198 * The results are documented at the bottom of this file in the
199 * long comment entitled "PREPARATION OF TEST DATA".
200 */
56c72b75 201 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
74fa4bfa 202
f335ecd6
RB
203 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
204 memset(&exp, 0, sizeof(exp));
74fa4bfa 205
f335ecd6
RB
206 if (use_iterator)
207 cl_git_pass(diff_foreach_via_iterator(
793c4385 208 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
209 else
210 cl_git_pass(git_diff_foreach(
793c4385 211 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
212
213 cl_assert_equal_i(14, exp.files);
b4f5bb07
RB
214 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
215 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
216 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
217 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
218 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 219 }
74fa4bfa
RB
220
221 /* Since there is no git diff equivalent, let's just assume that the
222 * text diffs produced by git_diff_foreach are accurate here. We will
223 * do more apples-to-apples test comparison below.
224 */
225
3ff1d123 226 git_diff_free(diff);
74fa4bfa
RB
227 diff = NULL;
228 memset(&exp, 0, sizeof(exp));
229
230 /* This is a compatible emulation of "git diff <sha>" which looks like
231 * a workdir to tree diff (even though it is not really). This is what
232 * you would get from "git diff --name-status 26a125ee1bf"
233 */
56c72b75
RB
234 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
235 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
74fa4bfa 236 cl_git_pass(git_diff_merge(diff, diff2));
3ff1d123 237 git_diff_free(diff2);
74fa4bfa 238
f335ecd6
RB
239 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
240 memset(&exp, 0, sizeof(exp));
241
242 if (use_iterator)
243 cl_git_pass(diff_foreach_via_iterator(
793c4385 244 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
245 else
246 cl_git_pass(git_diff_foreach(
793c4385 247 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
74fa4bfa 248
f335ecd6 249 cl_assert_equal_i(15, exp.files);
b4f5bb07
RB
250 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
251 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
252 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
253 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
254 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
74fa4bfa 255
f335ecd6 256 cl_assert_equal_i(11, exp.hunks);
74fa4bfa 257
f335ecd6
RB
258 cl_assert_equal_i(17, exp.lines);
259 cl_assert_equal_i(4, exp.line_ctxt);
260 cl_assert_equal_i(8, exp.line_adds);
261 cl_assert_equal_i(5, exp.line_dels);
262 }
74fa4bfa 263
3ff1d123 264 git_diff_free(diff);
74fa4bfa
RB
265 diff = NULL;
266 memset(&exp, 0, sizeof(exp));
267
268 /* Again, emulating "git diff <sha>" for testing purposes using
269 * "git diff --name-status 0017bd4ab1ec3" instead.
270 */
56c72b75
RB
271 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
272 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
74fa4bfa 273 cl_git_pass(git_diff_merge(diff, diff2));
3ff1d123 274 git_diff_free(diff2);
74fa4bfa 275
f335ecd6
RB
276 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
277 memset(&exp, 0, sizeof(exp));
74fa4bfa 278
f335ecd6
RB
279 if (use_iterator)
280 cl_git_pass(diff_foreach_via_iterator(
793c4385 281 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
282 else
283 cl_git_pass(git_diff_foreach(
793c4385 284 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
74fa4bfa 285
f335ecd6 286 cl_assert_equal_i(16, exp.files);
b4f5bb07
RB
287 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_ADDED]);
288 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
289 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
290 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
291 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
74fa4bfa 292
f335ecd6
RB
293 cl_assert_equal_i(12, exp.hunks);
294
295 cl_assert_equal_i(19, exp.lines);
296 cl_assert_equal_i(3, exp.line_ctxt);
297 cl_assert_equal_i(12, exp.line_adds);
298 cl_assert_equal_i(4, exp.line_dels);
299 }
74fa4bfa 300
3ff1d123 301 git_diff_free(diff);
c19bc93c 302
e7c85120
RB
303 /* Let's try that once more with a reversed diff */
304
305 opts.flags |= GIT_DIFF_REVERSE;
306
307 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
308 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
309 cl_git_pass(git_diff_merge(diff, diff2));
310 git_diff_free(diff2);
311
312 memset(&exp, 0, sizeof(exp));
313
314 cl_git_pass(git_diff_foreach(
315 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
316
317 cl_assert_equal_i(16, exp.files);
318 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
319 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_ADDED]);
320 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
321 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
322 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
323
324 cl_assert_equal_i(12, exp.hunks);
325
326 cl_assert_equal_i(19, exp.lines);
327 cl_assert_equal_i(3, exp.line_ctxt);
328 cl_assert_equal_i(12, exp.line_dels);
329 cl_assert_equal_i(4, exp.line_adds);
330
331 git_diff_free(diff);
332
333 /* all done now */
334
74fa4bfa
RB
335 git_tree_free(a);
336 git_tree_free(b);
337}
338
14a513e0
RB
339void test_diff_workdir__to_index_with_pathspec(void)
340{
2f8d30be 341 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 342 git_diff *diff = NULL;
14a513e0
RB
343 diff_expects exp;
344 char *pathspec = NULL;
f335ecd6 345 int use_iterator;
14a513e0 346
0abd7244
RB
347 g_repo = cl_git_sandbox_init("status");
348
14a513e0
RB
349 opts.context_lines = 3;
350 opts.interhunk_lines = 1;
351 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
352 opts.pathspec.strings = &pathspec;
353 opts.pathspec.count = 1;
354
56c72b75 355 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
14a513e0 356
f335ecd6
RB
357 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
358 memset(&exp, 0, sizeof(exp));
359
360 if (use_iterator)
361 cl_git_pass(diff_foreach_via_iterator(
793c4385 362 diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6 363 else
793c4385 364 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6
RB
365
366 cl_assert_equal_i(13, exp.files);
b4f5bb07
RB
367 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
368 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
369 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
370 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
371 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 372 }
14a513e0 373
3ff1d123 374 git_diff_free(diff);
14a513e0 375
14a513e0
RB
376 pathspec = "modified_file";
377
56c72b75 378 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
14a513e0 379
f335ecd6
RB
380 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
381 memset(&exp, 0, sizeof(exp));
382
383 if (use_iterator)
384 cl_git_pass(diff_foreach_via_iterator(
793c4385 385 diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6 386 else
793c4385 387 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6
RB
388
389 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
390 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
391 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
392 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
393 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
394 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 395 }
14a513e0 396
3ff1d123 397 git_diff_free(diff);
14a513e0 398
14a513e0
RB
399 pathspec = "subdir";
400
56c72b75 401 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
14a513e0 402
f335ecd6
RB
403 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
404 memset(&exp, 0, sizeof(exp));
405
406 if (use_iterator)
407 cl_git_pass(diff_foreach_via_iterator(
793c4385 408 diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6 409 else
793c4385 410 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6
RB
411
412 cl_assert_equal_i(3, exp.files);
b4f5bb07
RB
413 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
414 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
415 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
416 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
417 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 418 }
14a513e0 419
3ff1d123 420 git_diff_free(diff);
14a513e0 421
14a513e0
RB
422 pathspec = "*_deleted";
423
56c72b75 424 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
14a513e0 425
f335ecd6
RB
426 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
427 memset(&exp, 0, sizeof(exp));
428
429 if (use_iterator)
430 cl_git_pass(diff_foreach_via_iterator(
793c4385 431 diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6 432 else
793c4385 433 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
f335ecd6
RB
434
435 cl_assert_equal_i(2, exp.files);
b4f5bb07
RB
436 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
437 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
438 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
439 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
440 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
f335ecd6 441 }
14a513e0 442
3ff1d123 443 git_diff_free(diff);
14a513e0
RB
444}
445
0abd7244
RB
446void test_diff_workdir__filemode_changes(void)
447{
3ff1d123 448 git_diff *diff = NULL;
0abd7244 449 diff_expects exp;
f335ecd6 450 int use_iterator;
0abd7244
RB
451
452 if (!cl_is_chmod_supported())
453 return;
454
455 g_repo = cl_git_sandbox_init("issue_592");
456
1323c6d1 457 cl_repo_set_bool(g_repo, "core.filemode", true);
0abd7244
RB
458
459 /* test once with no mods */
460
56c72b75 461 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
0abd7244 462
f335ecd6
RB
463 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
464 memset(&exp, 0, sizeof(exp));
0abd7244 465
f335ecd6
RB
466 if (use_iterator)
467 cl_git_pass(diff_foreach_via_iterator(
793c4385 468 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
469 else
470 cl_git_pass(git_diff_foreach(
793c4385 471 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
472
473 cl_assert_equal_i(0, exp.files);
b4f5bb07 474 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
475 cl_assert_equal_i(0, exp.hunks);
476 }
0abd7244 477
3ff1d123 478 git_diff_free(diff);
0abd7244
RB
479
480 /* chmod file and test again */
481
482 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
483
56c72b75 484 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
0abd7244 485
f335ecd6
RB
486 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
487 memset(&exp, 0, sizeof(exp));
0abd7244 488
f335ecd6
RB
489 if (use_iterator)
490 cl_git_pass(diff_foreach_via_iterator(
793c4385 491 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
492 else
493 cl_git_pass(git_diff_foreach(
793c4385 494 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
495
496 cl_assert_equal_i(1, exp.files);
b4f5bb07 497 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
498 cl_assert_equal_i(0, exp.hunks);
499 }
0abd7244 500
3ff1d123 501 git_diff_free(diff);
0abd7244
RB
502
503 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
0abd7244
RB
504}
505
506void test_diff_workdir__filemode_changes_with_filemode_false(void)
507{
3ff1d123 508 git_diff *diff = NULL;
0abd7244
RB
509 diff_expects exp;
510
511 if (!cl_is_chmod_supported())
512 return;
513
514 g_repo = cl_git_sandbox_init("issue_592");
515
1323c6d1 516 cl_repo_set_bool(g_repo, "core.filemode", false);
0abd7244
RB
517
518 /* test once with no mods */
519
56c72b75 520 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
0abd7244
RB
521
522 memset(&exp, 0, sizeof(exp));
523 cl_git_pass(git_diff_foreach(
793c4385 524 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
0abd7244
RB
525
526 cl_assert_equal_i(0, exp.files);
b4f5bb07 527 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
0abd7244
RB
528 cl_assert_equal_i(0, exp.hunks);
529
3ff1d123 530 git_diff_free(diff);
0abd7244
RB
531
532 /* chmod file and test again */
533
534 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
535
56c72b75 536 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
0abd7244
RB
537
538 memset(&exp, 0, sizeof(exp));
539 cl_git_pass(git_diff_foreach(
793c4385 540 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
0abd7244
RB
541
542 cl_assert_equal_i(0, exp.files);
b4f5bb07 543 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
0abd7244
RB
544 cl_assert_equal_i(0, exp.hunks);
545
3ff1d123 546 git_diff_free(diff);
0abd7244
RB
547
548 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
0abd7244
RB
549}
550
145e696b
RB
551void test_diff_workdir__head_index_and_workdir_all_differ(void)
552{
2f8d30be 553 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 554 git_diff *diff_i2t = NULL, *diff_w2i = NULL;
145e696b
RB
555 diff_expects exp;
556 char *pathspec = "staged_changes_modified_file";
557 git_tree *tree;
f335ecd6 558 int use_iterator;
145e696b
RB
559
560 /* For this file,
561 * - head->index diff has 1 line of context, 1 line of diff
562 * - index->workdir diff has 2 lines of context, 1 line of diff
563 * but
564 * - head->workdir diff has 1 line of context, 2 lines of diff
565 * Let's make sure the right one is returned from each fn.
566 */
567
568 g_repo = cl_git_sandbox_init("status");
569
570 tree = resolve_commit_oid_to_tree(g_repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
571
572 opts.pathspec.strings = &pathspec;
573 opts.pathspec.count = 1;
574
56c72b75
RB
575 cl_git_pass(git_diff_tree_to_index(&diff_i2t, g_repo, tree, NULL, &opts));
576 cl_git_pass(git_diff_index_to_workdir(&diff_w2i, g_repo, NULL, &opts));
145e696b 577
f335ecd6
RB
578 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
579 memset(&exp, 0, sizeof(exp));
580
581 if (use_iterator)
582 cl_git_pass(diff_foreach_via_iterator(
793c4385 583 diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
584 else
585 cl_git_pass(git_diff_foreach(
793c4385 586 diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
587
588 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
589 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
590 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
591 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
592 cl_assert_equal_i(1, exp.hunks);
593 cl_assert_equal_i(2, exp.lines);
594 cl_assert_equal_i(1, exp.line_ctxt);
595 cl_assert_equal_i(1, exp.line_adds);
596 cl_assert_equal_i(0, exp.line_dels);
597 }
598
599 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
600 memset(&exp, 0, sizeof(exp));
601
602 if (use_iterator)
603 cl_git_pass(diff_foreach_via_iterator(
793c4385 604 diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
605 else
606 cl_git_pass(git_diff_foreach(
793c4385 607 diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
608
609 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
610 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
611 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
612 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
613 cl_assert_equal_i(1, exp.hunks);
614 cl_assert_equal_i(3, exp.lines);
615 cl_assert_equal_i(2, exp.line_ctxt);
616 cl_assert_equal_i(1, exp.line_adds);
617 cl_assert_equal_i(0, exp.line_dels);
618 }
145e696b
RB
619
620 cl_git_pass(git_diff_merge(diff_i2t, diff_w2i));
621
f335ecd6
RB
622 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
623 memset(&exp, 0, sizeof(exp));
624
625 if (use_iterator)
626 cl_git_pass(diff_foreach_via_iterator(
793c4385 627 diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
628 else
629 cl_git_pass(git_diff_foreach(
793c4385 630 diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
631
632 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
633 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
634 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
635 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
636 cl_assert_equal_i(1, exp.hunks);
637 cl_assert_equal_i(3, exp.lines);
638 cl_assert_equal_i(1, exp.line_ctxt);
639 cl_assert_equal_i(2, exp.line_adds);
640 cl_assert_equal_i(0, exp.line_dels);
641 }
145e696b 642
3ff1d123
RB
643 git_diff_free(diff_i2t);
644 git_diff_free(diff_w2i);
cdca82c7
CMN
645
646 git_tree_free(tree);
145e696b
RB
647}
648
649void test_diff_workdir__eof_newline_changes(void)
650{
2f8d30be 651 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 652 git_diff *diff = NULL;
145e696b
RB
653 diff_expects exp;
654 char *pathspec = "current_file";
f335ecd6 655 int use_iterator;
145e696b
RB
656
657 g_repo = cl_git_sandbox_init("status");
658
659 opts.pathspec.strings = &pathspec;
660 opts.pathspec.count = 1;
661
56c72b75 662 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
145e696b 663
f335ecd6
RB
664 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
665 memset(&exp, 0, sizeof(exp));
666
667 if (use_iterator)
668 cl_git_pass(diff_foreach_via_iterator(
793c4385 669 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
670 else
671 cl_git_pass(git_diff_foreach(
793c4385 672 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
673
674 cl_assert_equal_i(0, exp.files);
b4f5bb07
RB
675 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
676 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
677 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
678 cl_assert_equal_i(0, exp.hunks);
679 cl_assert_equal_i(0, exp.lines);
680 cl_assert_equal_i(0, exp.line_ctxt);
681 cl_assert_equal_i(0, exp.line_adds);
682 cl_assert_equal_i(0, exp.line_dels);
683 }
145e696b 684
3ff1d123 685 git_diff_free(diff);
145e696b
RB
686
687 cl_git_append2file("status/current_file", "\n");
688
56c72b75 689 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
145e696b 690
f335ecd6
RB
691 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
692 memset(&exp, 0, sizeof(exp));
693
694 if (use_iterator)
695 cl_git_pass(diff_foreach_via_iterator(
793c4385 696 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
697 else
698 cl_git_pass(git_diff_foreach(
793c4385 699 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
700
701 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
702 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
703 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
704 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
705 cl_assert_equal_i(1, exp.hunks);
706 cl_assert_equal_i(2, exp.lines);
707 cl_assert_equal_i(1, exp.line_ctxt);
708 cl_assert_equal_i(1, exp.line_adds);
709 cl_assert_equal_i(0, exp.line_dels);
710 }
145e696b 711
3ff1d123 712 git_diff_free(diff);
145e696b
RB
713
714 cl_git_rewritefile("status/current_file", "current_file");
715
56c72b75 716 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
145e696b 717
f335ecd6
RB
718 for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
719 memset(&exp, 0, sizeof(exp));
720
721 if (use_iterator)
722 cl_git_pass(diff_foreach_via_iterator(
793c4385 723 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
724 else
725 cl_git_pass(git_diff_foreach(
793c4385 726 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
f335ecd6
RB
727
728 cl_assert_equal_i(1, exp.files);
b4f5bb07
RB
729 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
730 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
731 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
f335ecd6
RB
732 cl_assert_equal_i(1, exp.hunks);
733 cl_assert_equal_i(3, exp.lines);
734 cl_assert_equal_i(0, exp.line_ctxt);
735 cl_assert_equal_i(1, exp.line_adds);
736 cl_assert_equal_i(2, exp.line_dels);
737 }
145e696b 738
3ff1d123 739 git_diff_free(diff);
145e696b
RB
740}
741
74fa4bfa
RB
742/* PREPARATION OF TEST DATA
743 *
56c72b75 744 * Since there is no command line equivalent of git_diff_tree_to_workdir,
74fa4bfa
RB
745 * it was a bit of a pain to confirm that I was getting the expected
746 * results in the first part of this tests. Here is what I ended up
747 * doing to set my expectation for the file counts and results:
748 *
749 * Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
750 *
751 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
752 * B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
753 * C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
754 * D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
755 * E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
756 * F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
757 * G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
758 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
759 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
760 * J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
761 * K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
762 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
763 *
764 * --------
765 *
766 * find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
767 *
768 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
769 * M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
770 * C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
771 * N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
772 * D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
773 * F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
774 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
775 * O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
776 * P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
777 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
778 * K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
779 * Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
780 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
781 *
782 * --------
783 *
784 * A - current_file (UNMODIFIED) -> not in results
785 * B D file_deleted
786 * M I ignored_file (IGNORED)
787 * C M modified_file
788 * N U new_file (UNTRACKED)
789 * D M staged_changes
790 * E D staged_changes_file_deleted
791 * F M staged_changes_modified_file
792 * G D staged_delete_file_deleted
793 * H - staged_delete_modified_file (UNMODIFIED) -> not in results
794 * O U staged_new_file
795 * P U staged_new_file_modified_file
796 * I - subdir/current_file (UNMODIFIED) -> not in results
797 * J D subdir/deleted_file
798 * K M subdir/modified_file
799 * Q U subdir/new_file
800 * L - subdir.txt (UNMODIFIED) -> not in results
801 *
802 * Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
803 */
49d34c1c
RB
804
805
806void test_diff_workdir__larger_hunks(void)
807{
808 const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
809 const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
810 git_tree *a, *b;
2f8d30be 811 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3b5f7954 812 size_t i, d, num_d, h, num_h, l, num_l;
49d34c1c
RB
813
814 g_repo = cl_git_sandbox_init("diff");
815
816 cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
817 cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
818
819 opts.context_lines = 1;
820 opts.interhunk_lines = 0;
821
822 for (i = 0; i <= 2; ++i) {
3ff1d123
RB
823 git_diff *diff = NULL;
824 git_patch *patch;
3b5f7954
RB
825 const git_diff_hunk *hunk;
826 const git_diff_line *line;
49d34c1c
RB
827
828 /* okay, this is a bit silly, but oh well */
829 switch (i) {
830 case 0:
56c72b75 831 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
49d34c1c
RB
832 break;
833 case 1:
56c72b75 834 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
49d34c1c
RB
835 break;
836 case 2:
56c72b75 837 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, b, &opts));
49d34c1c
RB
838 break;
839 }
840
5f69a31f
RB
841 num_d = git_diff_num_deltas(diff);
842 cl_assert_equal_i(2, (int)num_d);
49d34c1c 843
5f69a31f 844 for (d = 0; d < num_d; ++d) {
10672e3e 845 cl_git_pass(git_patch_from_diff(&patch, diff, d));
5f69a31f 846 cl_assert(patch);
49d34c1c 847
3ff1d123 848 num_h = git_patch_num_hunks(patch);
5f69a31f 849 for (h = 0; h < num_h; h++) {
3b5f7954 850 cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
49d34c1c 851
5f69a31f 852 for (l = 0; l < num_l; ++l) {
3b5f7954
RB
853 cl_git_pass(
854 git_patch_get_line_in_hunk(&line, patch, h, l));
5f69a31f 855 cl_assert(line);
49d34c1c
RB
856 }
857
5f69a31f 858 /* confirm fail after the last item */
3b5f7954
RB
859 cl_git_fail(
860 git_patch_get_line_in_hunk(&line, patch, h, num_l));
49d34c1c
RB
861 }
862
5f69a31f 863 /* confirm fail after the last item */
3b5f7954 864 cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h));
49d34c1c 865
3ff1d123 866 git_patch_free(patch);
5f69a31f 867 }
49d34c1c 868
3ff1d123 869 git_diff_free(diff);
49d34c1c
RB
870 }
871
872 git_tree_free(a);
873 git_tree_free(b);
874}
5d1308f2
RB
875
876/* Set up a test that exercises this code. The easiest test using existing
877 * test data is probably to create a sandbox of submod2 and then run a
56c72b75 878 * git_diff_tree_to_workdir against tree
5d1308f2
RB
879 * 873585b94bdeabccea991ea5e3ec1a277895b698. As for what you should actually
880 * test, you can start by just checking that the number of lines of diff
881 * content matches the actual output of git diff. That will at least
882 * demonstrate that the submodule content is being used to generate somewhat
883 * comparable outputs. It is a test that would fail without this code and
884 * will succeed with it.
885 */
886
887#include "../submodule/submodule_helpers.h"
888
889void test_diff_workdir__submodules(void)
890{
891 const char *a_commit = "873585b94bdeabccea991ea5e3ec1a277895b698";
892 git_tree *a;
2f8d30be 893 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 894 git_diff *diff = NULL;
5d1308f2
RB
895 diff_expects exp;
896
14997dc5 897 g_repo = setup_fixture_submod2();
5d1308f2
RB
898
899 a = resolve_commit_oid_to_tree(g_repo, a_commit);
900
901 opts.flags =
902 GIT_DIFF_INCLUDE_UNTRACKED |
125655fe 903 GIT_DIFF_INCLUDE_IGNORED |
5d1308f2 904 GIT_DIFF_RECURSE_UNTRACKED_DIRS |
10672e3e 905 GIT_DIFF_SHOW_UNTRACKED_CONTENT;
5d1308f2 906
56c72b75 907 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
5d1308f2
RB
908
909 /* diff_print(stderr, diff); */
910
911 /* essentially doing: git diff 873585b94bdeabccea991ea5e3ec1a277895b698 */
912
913 memset(&exp, 0, sizeof(exp));
65025cb8 914
5d1308f2 915 cl_git_pass(git_diff_foreach(
793c4385 916 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
5d1308f2 917
ccfa6805
RB
918 /* so "git diff 873585" returns:
919 * M .gitmodules
920 * A just_a_dir/contents
921 * A just_a_file
922 * A sm_added_and_uncommited
923 * A sm_changed_file
924 * A sm_changed_head
925 * A sm_changed_index
926 * A sm_changed_untracked_file
927 * M sm_missing_commits
928 * A sm_unchanged
929 * which is a little deceptive because of the difference between the
930 * "git diff <treeish>" results from "git_diff_tree_to_workdir". The
931 * only significant difference is that those Added items will show up
932 * as Untracked items in the pure libgit2 diff.
933 *
d3bc95fd 934 * Then add in the two extra untracked items "not" and "not-submodule"
ccfa6805 935 * to get the 12 files reported here.
5d1308f2
RB
936 */
937
ccfa6805 938 cl_assert_equal_i(12, exp.files);
5d1308f2 939
b4f5bb07
RB
940 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
941 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
ccfa6805 942 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
d3bc95fd
RB
943 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
944 cl_assert_equal_i(10, exp.file_status[GIT_DELTA_UNTRACKED]);
5d1308f2
RB
945
946 /* the following numbers match "git diff 873585" exactly */
947
948 cl_assert_equal_i(9, exp.hunks);
949
950 cl_assert_equal_i(33, exp.lines);
951 cl_assert_equal_i(2, exp.line_ctxt);
952 cl_assert_equal_i(30, exp.line_adds);
953 cl_assert_equal_i(1, exp.line_dels);
954
3ff1d123 955 git_diff_free(diff);
5d1308f2
RB
956 git_tree_free(a);
957}
c2e43fb1 958
959void test_diff_workdir__cannot_diff_against_a_bare_repository(void)
960{
2f8d30be 961 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 962 git_diff *diff = NULL;
c2e43fb1 963 git_tree *tree;
964
965 g_repo = cl_git_sandbox_init("testrepo.git");
966
5735bf5e 967 cl_assert_equal_i(
56c72b75 968 GIT_EBAREREPO, git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
c2e43fb1 969
970 cl_git_pass(git_repository_head_tree(&tree, g_repo));
5735bf5e
RB
971
972 cl_assert_equal_i(
56c72b75 973 GIT_EBAREREPO, git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
c2e43fb1 974
975 git_tree_free(tree);
976}
59a0d772 977
978void test_diff_workdir__to_null_tree(void)
979{
3ff1d123 980 git_diff *diff;
59a0d772 981 diff_expects exp;
2f8d30be 982 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
59a0d772 983
984 opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
985 GIT_DIFF_RECURSE_UNTRACKED_DIRS;
986
987 g_repo = cl_git_sandbox_init("status");
988
56c72b75 989 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
59a0d772 990
991 memset(&exp, 0, sizeof(exp));
992
993 cl_git_pass(git_diff_foreach(
994 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
995
996 cl_assert_equal_i(exp.files, exp.file_status[GIT_DELTA_UNTRACKED]);
997
3ff1d123 998 git_diff_free(diff);
59a0d772 999}
2f8d30be
BS
1000
1001void test_diff_workdir__checks_options_version(void)
1002{
3ff1d123 1003 git_diff *diff;
2f8d30be
BS
1004 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1005 const git_error *err;
1006
1007 g_repo = cl_git_sandbox_init("status");
1008
1009 opts.version = 0;
56c72b75 1010 cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
2f8d30be
BS
1011 err = giterr_last();
1012 cl_assert_equal_i(GITERR_INVALID, err->klass);
1013
1014 giterr_clear();
1015 opts.version = 1024;
56c72b75 1016 cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
2f8d30be
BS
1017 err = giterr_last();
1018 cl_assert_equal_i(GITERR_INVALID, err->klass);
1019}
de590550
RB
1020
1021void test_diff_workdir__can_diff_empty_file(void)
1022{
3ff1d123 1023 git_diff *diff;
de590550
RB
1024 git_tree *tree;
1025 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1026 struct stat st;
3ff1d123 1027 git_patch *patch;
de590550
RB
1028
1029 g_repo = cl_git_sandbox_init("attr_index");
1030
1031 tree = resolve_commit_oid_to_tree(g_repo, "3812cfef3661"); /* HEAD */
1032
1033 /* baseline - make sure there are no outstanding diffs */
1034
1035 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
1036 cl_assert_equal_i(2, (int)git_diff_num_deltas(diff));
3ff1d123 1037 git_diff_free(diff);
de590550
RB
1038
1039 /* empty contents of file */
1040
1041 cl_git_rewritefile("attr_index/README.txt", "");
1042 cl_git_pass(git_path_lstat("attr_index/README.txt", &st));
1043 cl_assert_equal_i(0, (int)st.st_size);
1044
1045 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
1046 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
1047 /* diffs are: .gitattributes, README.txt, sub/sub/.gitattributes */
10672e3e 1048 cl_git_pass(git_patch_from_diff(&patch, diff, 1));
3ff1d123
RB
1049 git_patch_free(patch);
1050 git_diff_free(diff);
de590550
RB
1051
1052 /* remove a file altogether */
1053
1054 cl_git_pass(p_unlink("attr_index/README.txt"));
1055 cl_assert(!git_path_exists("attr_index/README.txt"));
1056
1057 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
1058 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
10672e3e 1059 cl_git_pass(git_patch_from_diff(&patch, diff, 1));
3ff1d123
RB
1060 git_patch_free(patch);
1061 git_diff_free(diff);
8842c75f
VM
1062
1063 git_tree_free(tree);
de590550 1064}
b8acb775 1065
b8acb775
SS
1066void test_diff_workdir__to_index_issue_1397(void)
1067{
1068 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 1069 git_diff *diff = NULL;
b8acb775 1070 diff_expects exp;
b8acb775
SS
1071
1072 g_repo = cl_git_sandbox_init("issue_1397");
1073
1098cfae 1074 cl_repo_set_bool(g_repo, "core.autocrlf", true);
b8acb775
SS
1075
1076 opts.context_lines = 3;
1077 opts.interhunk_lines = 1;
1078
1079 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1080
1098cfae
RB
1081 memset(&exp, 0, sizeof(exp));
1082 cl_git_pass(git_diff_foreach(
1083 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
b8acb775 1084
1098cfae
RB
1085 cl_assert_equal_i(0, exp.files);
1086 cl_assert_equal_i(0, exp.hunks);
1087 cl_assert_equal_i(0, exp.lines);
b8acb775 1088
3ff1d123 1089 git_diff_free(diff);
b8acb775 1090 diff = NULL;
b8acb775 1091
1098cfae
RB
1092 cl_git_rewritefile("issue_1397/crlf_file.txt",
1093 "first line\r\nsecond line modified\r\nboth with crlf");
b8acb775
SS
1094
1095 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1096
1098cfae
RB
1097 memset(&exp, 0, sizeof(exp));
1098 cl_git_pass(git_diff_foreach(
1099 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
b8acb775 1100
1098cfae
RB
1101 cl_assert_equal_i(1, exp.files);
1102 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
b8acb775 1103
1098cfae 1104 cl_assert_equal_i(1, exp.hunks);
b8acb775 1105
1098cfae
RB
1106 cl_assert_equal_i(5, exp.lines);
1107 cl_assert_equal_i(3, exp.line_ctxt);
1108 cl_assert_equal_i(1, exp.line_adds);
1109 cl_assert_equal_i(1, exp.line_dels);
b8acb775 1110
3ff1d123 1111 git_diff_free(diff);
b8acb775
SS
1112}
1113
1114void test_diff_workdir__to_tree_issue_1397(void)
1115{
1098cfae 1116 const char *a_commit = "7f483a738"; /* the current HEAD */
b8acb775
SS
1117 git_tree *a;
1118 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123
RB
1119 git_diff *diff = NULL;
1120 git_diff *diff2 = NULL;
b8acb775 1121 diff_expects exp;
b8acb775
SS
1122
1123 g_repo = cl_git_sandbox_init("issue_1397");
1124
1098cfae 1125 cl_repo_set_bool(g_repo, "core.autocrlf", true);
b8acb775
SS
1126
1127 a = resolve_commit_oid_to_tree(g_repo, a_commit);
1128
1129 opts.context_lines = 3;
1130 opts.interhunk_lines = 1;
1131
1132 cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
1133
1098cfae
RB
1134 memset(&exp, 0, sizeof(exp));
1135 cl_git_pass(git_diff_foreach(
1136 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
b8acb775 1137
1098cfae
RB
1138 cl_assert_equal_i(0, exp.files);
1139 cl_assert_equal_i(0, exp.hunks);
1140 cl_assert_equal_i(0, exp.lines);
b8acb775 1141
3ff1d123 1142 git_diff_free(diff);
b8acb775 1143 diff = NULL;
b8acb775 1144
b8acb775
SS
1145 cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
1146 cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
1147 cl_git_pass(git_diff_merge(diff, diff2));
3ff1d123 1148 git_diff_free(diff2);
b8acb775 1149
1098cfae
RB
1150 memset(&exp, 0, sizeof(exp));
1151 cl_git_pass(git_diff_foreach(
1152 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
b8acb775 1153
1098cfae
RB
1154 cl_assert_equal_i(0, exp.files);
1155 cl_assert_equal_i(0, exp.hunks);
1156 cl_assert_equal_i(0, exp.lines);
b8acb775 1157
3ff1d123 1158 git_diff_free(diff);
b8acb775
SS
1159 git_tree_free(a);
1160}
a66c4bc8
RB
1161
1162void test_diff_workdir__untracked_directory_scenarios(void)
1163{
1164 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 1165 git_diff *diff = NULL;
a66c4bc8
RB
1166 diff_expects exp;
1167 char *pathspec = NULL;
1168 static const char *files0[] = {
1169 "subdir/deleted_file",
1170 "subdir/modified_file",
1171 "subdir/new_file",
1172 NULL
1173 };
1174 static const char *files1[] = {
1175 "subdir/deleted_file",
1176 "subdir/directory/",
1177 "subdir/modified_file",
1178 "subdir/new_file",
1179 NULL
1180 };
1181 static const char *files2[] = {
1182 "subdir/deleted_file",
1183 "subdir/directory/more/notignored",
1184 "subdir/modified_file",
1185 "subdir/new_file",
1186 NULL
1187 };
1188
1189 g_repo = cl_git_sandbox_init("status");
1190 cl_git_mkfile("status/.gitignore", "ignored\n");
1191
1192 opts.context_lines = 3;
1193 opts.interhunk_lines = 1;
1194 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
1195 opts.pathspec.strings = &pathspec;
1196 opts.pathspec.count = 1;
1197 pathspec = "subdir";
1198
1199 /* baseline for "subdir" pathspec */
1200
1201 memset(&exp, 0, sizeof(exp));
1202 exp.names = files0;
1203
1204 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1205
1206 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1207
1208 cl_assert_equal_i(3, exp.files);
1209 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1210 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1211 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1212 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1213 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1214
3ff1d123 1215 git_diff_free(diff);
a66c4bc8
RB
1216
1217 /* empty directory */
1218
1219 cl_git_pass(p_mkdir("status/subdir/directory", 0777));
1220
1221 memset(&exp, 0, sizeof(exp));
1222 exp.names = files1;
1223
94ef2a35
RB
1224 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1225
1226 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1227
1228 cl_assert_equal_i(4, exp.files);
1229 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1230 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1231 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1232 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1233 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1234
3ff1d123 1235 git_diff_free(diff);
94ef2a35
RB
1236
1237 /* empty directory in empty directory */
1238
1239 cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777));
1240
1241 memset(&exp, 0, sizeof(exp));
1242 exp.names = files1;
1243
a66c4bc8
RB
1244 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1245
1246 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1247
1248 cl_assert_equal_i(4, exp.files);
1249 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1250 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1251 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1252 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1253 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1254
3ff1d123 1255 git_diff_free(diff);
a66c4bc8
RB
1256
1257 /* directory with only ignored files */
1258
1259 cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
1260 cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n");
1261
1262 cl_git_pass(p_mkdir("status/subdir/directory/another", 0777));
1263 cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n");
1264
1265 memset(&exp, 0, sizeof(exp));
1266 exp.names = files1;
1267
1268 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1269
1270 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1271
1272 cl_assert_equal_i(4, exp.files);
1273 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1274 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1275 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1276 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1277 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1278
3ff1d123 1279 git_diff_free(diff);
a66c4bc8
RB
1280
1281 /* directory with ignored directory (contents irrelevant) */
1282
1283 cl_git_pass(p_mkdir("status/subdir/directory/more", 0777));
1284 cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777));
1285 cl_git_mkfile("status/subdir/directory/more/ignored/notignored",
1286 "inside ignored dir\n");
1287
1288 memset(&exp, 0, sizeof(exp));
1289 exp.names = files1;
1290
1291 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1292
1293 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1294
1295 cl_assert_equal_i(4, exp.files);
1296 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1297 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1298 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1299 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1300 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
1301
3ff1d123 1302 git_diff_free(diff);
a66c4bc8
RB
1303
1304 /* quick version avoids directory scan */
1305
10672e3e 1306 opts.flags = opts.flags | GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
a66c4bc8
RB
1307
1308 memset(&exp, 0, sizeof(exp));
1309 exp.names = files1;
1310
1311 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1312
1313 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1314
1315 cl_assert_equal_i(4, exp.files);
1316 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1317 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1318 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1319 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1320 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
1321
3ff1d123 1322 git_diff_free(diff);
a66c4bc8
RB
1323
1324 /* directory with nested non-ignored content */
1325
10672e3e 1326 opts.flags = opts.flags & ~GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
a66c4bc8
RB
1327
1328 cl_git_mkfile("status/subdir/directory/more/notignored",
1329 "not ignored deep under untracked\n");
1330
1331 memset(&exp, 0, sizeof(exp));
1332 exp.names = files1;
1333
1334 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1335
1336 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1337
1338 cl_assert_equal_i(4, exp.files);
1339 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1340 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1341 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1342 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1343 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
1344
3ff1d123 1345 git_diff_free(diff);
a66c4bc8
RB
1346
1347 /* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
1348
1349 opts.flags = opts.flags & ~GIT_DIFF_INCLUDE_IGNORED;
1350 opts.flags = opts.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
1351
1352 memset(&exp, 0, sizeof(exp));
1353 exp.names = files2;
1354
1355 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1356
1357 cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
1358
1359 cl_assert_equal_i(4, exp.files);
1360 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1361 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
1362 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
1363 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
1364 cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
1365
3ff1d123 1366 git_diff_free(diff);
a66c4bc8 1367}
79ef3be4
RB
1368
1369
1370void test_diff_workdir__untracked_directory_comes_last(void)
1371{
1372 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 1373 git_diff *diff = NULL;
79ef3be4
RB
1374
1375 g_repo = cl_git_sandbox_init("renames");
1376
1377 cl_git_mkfile("renames/.gitignore", "*.ign\n");
1378 cl_git_pass(p_mkdir("renames/zzz_untracked", 0777));
1379 cl_git_mkfile("renames/zzz_untracked/an.ign", "ignore me please");
1380 cl_git_mkfile("renames/zzz_untracked/skip.ign", "ignore me really");
1381 cl_git_mkfile("renames/zzz_untracked/test.ign", "ignore me now");
1382
1383 opts.context_lines = 3;
1384 opts.interhunk_lines = 1;
1385 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
1386
1387 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1388
1389 cl_assert(diff != NULL);
1390
3ff1d123 1391 git_diff_free(diff);
79ef3be4 1392}
634f10f6
RB
1393
1394void test_diff_workdir__untracked_with_bom(void)
1395{
1396 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
3ff1d123 1397 git_diff *diff = NULL;
634f10f6
RB
1398 const git_diff_delta *delta;
1399
1400 g_repo = cl_git_sandbox_init("empty_standard_repo");
1401 cl_repo_set_bool(g_repo, "core.autocrlf", true);
1402
1403 cl_git_write2file("empty_standard_repo/bom.txt",
1404 "\xFF\xFE\x31\x00\x32\x00\x33\x00\x34\x00", 10, O_WRONLY|O_CREAT, 0664);
1405
1406 opts.flags =
10672e3e 1407 GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT;
634f10f6
RB
1408
1409 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1410
1411 cl_assert_equal_i(1, git_diff_num_deltas(diff));
10672e3e 1412 cl_assert((delta = git_diff_get_delta(diff, 0)) != NULL);
634f10f6 1413 cl_assert_equal_i(GIT_DELTA_UNTRACKED, delta->status);
10672e3e
RB
1414
1415 /* not known at this point
1416 * cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
1417 */
634f10f6 1418
3ff1d123 1419 git_diff_free(diff);
634f10f6 1420}
5de4ec81
RB
1421
1422void test_diff_workdir__patience_diff(void)
1423{
1424 git_index *index;
1425 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1426 git_diff *diff = NULL;
1427 git_patch *patch = NULL;
c05cd792 1428 git_buf buf = GIT_BUF_INIT;
5de4ec81
RB
1429 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";
1430 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";
1431
1432 g_repo = cl_git_sandbox_init("empty_standard_repo");
1433 cl_repo_set_bool(g_repo, "core.autocrlf", true);
1434 cl_git_pass(git_repository_index(&index, g_repo));
1435
1436 cl_git_mkfile(
1437 "empty_standard_repo/test.txt",
1438 "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");
1439 cl_git_pass(git_index_add_bypath(index, "test.txt"));
1440 cl_repo_commit_from_index(NULL, g_repo, NULL, 1372350000, "Base");
187009e2 1441 git_index_free(index);
5de4ec81
RB
1442
1443 cl_git_rewritefile(
1444 "empty_standard_repo/test.txt",
1445 "When I wrote this\nI did not know\nI did not know\nhow to create\na patience diff\nanother problem\na minimal diff\n");
1446
1447 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1448 cl_assert_equal_i(1, git_diff_num_deltas(diff));
1449 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
c05cd792 1450 cl_git_pass(git_patch_to_buf(&buf, patch));
5de4ec81 1451
c05cd792
NH
1452 cl_assert_equal_s(expected_normal, buf.ptr);
1453 git_buf_clear(&buf);
5de4ec81
RB
1454 git_patch_free(patch);
1455 git_diff_free(diff);
1456
1457 opts.flags |= GIT_DIFF_PATIENCE;
1458
1459 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1460 cl_assert_equal_i(1, git_diff_num_deltas(diff));
1461 cl_git_pass(git_patch_from_diff(&patch, diff, 0));
c05cd792 1462 cl_git_pass(git_patch_to_buf(&buf, patch));
5de4ec81 1463
c05cd792
NH
1464 cl_assert_equal_s(expected_patience, buf.ptr);
1465 git_buf_clear(&buf);
1466
1467 git_buf_free(&buf);
5de4ec81
RB
1468 git_patch_free(patch);
1469 git_diff_free(diff);
1470}
4bf630b6
RB
1471
1472void test_diff_workdir__with_stale_index(void)
1473{
1474 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1475 git_diff *diff = NULL;
1476 git_index *idx = NULL;
1477 diff_expects exp;
1478
1479 g_repo = cl_git_sandbox_init("status");
1480 cl_git_pass(git_repository_index(&idx, g_repo));
1481
1482 /* make the in-memory index invalid */
1483 {
1484 git_repository *r2;
1485 git_index *idx2;
1486 cl_git_pass(git_repository_open(&r2, "status"));
1487 cl_git_pass(git_repository_index(&idx2, r2));
1488 cl_git_pass(git_index_add_bypath(idx2, "new_file"));
1489 cl_git_pass(git_index_add_bypath(idx2, "subdir/new_file"));
1490 cl_git_pass(git_index_remove_bypath(idx2, "staged_new_file"));
1491 cl_git_pass(git_index_remove_bypath(idx2, "staged_changes_file_deleted"));
1492 cl_git_pass(git_index_write(idx2));
1493 git_index_free(idx2);
1494 git_repository_free(r2);
1495 }
1496
1497 opts.context_lines = 3;
1498 opts.interhunk_lines = 1;
1499 opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_INCLUDE_UNMODIFIED;
1500
1501 /* first try with index pointer which should prevent reload */
1502
1503 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, idx, &opts));
1504
1505 memset(&exp, 0, sizeof(exp));
1506
1507 cl_git_pass(git_diff_foreach(
1508 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
1509
1510 cl_assert_equal_i(17, exp.files);
1511 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1512 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
1513 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
1514 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
1515 cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNMODIFIED]);
1516
1517 git_diff_free(diff);
1518
1519 /* now let's try without the index pointer which should trigger reload */
1520
1521 /* two files that were UNTRACKED should have become UNMODIFIED */
1522 /* one file that was UNMODIFIED should now have become UNTRACKED */
1523 /* one file that was DELETED should now be gone completely */
1524
1525 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
1526
1527 memset(&exp, 0, sizeof(exp));
1528
1529 cl_git_pass(git_diff_foreach(
1530 diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
1531
61080a95 1532 git_diff_free(diff);
1533
4bf630b6
RB
1534 cl_assert_equal_i(16, exp.files);
1535 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1536 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
1537 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
1538 cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
1539 cl_assert_equal_i(6, exp.file_status[GIT_DELTA_UNMODIFIED]);
1540
1541 git_index_free(idx);
1542}
94fb4aad
RB
1543
1544static int touch_file(void *payload, git_buf *path)
1545{
1546 int fd;
1547 char b;
1548
1549 GIT_UNUSED(payload);
1550 if (git_path_isdir(path->ptr))
1551 return 0;
1552
1553 cl_assert((fd = p_open(path->ptr, O_RDWR)) >= 0);
1554 cl_assert_equal_i(1, p_read(fd, &b, 1));
1555 cl_must_pass(p_lseek(fd, 0, SEEK_SET));
1556 cl_must_pass(p_write(fd, &b, 1));
1557 cl_must_pass(p_close(fd));
1558
1559 return 0;
1560}
1561
1562static void basic_diff_status(git_diff **out, const git_diff_options *opts)
1563{
1564 diff_expects exp;
1565
1566 cl_git_pass(git_diff_index_to_workdir(out, g_repo, NULL, opts));
1567
1568 memset(&exp, 0, sizeof(exp));
1569
1570 cl_git_pass(git_diff_foreach(
1571 *out, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
1572
1573 cl_assert_equal_i(13, exp.files);
1574 cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
1575 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
1576 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
1577 cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
1578 cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
1579}
1580
1581void test_diff_workdir__can_update_index(void)
1582{
1583 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
1584 git_diff *diff = NULL;
9c8ed499 1585 git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
94fb4aad
RB
1586
1587 g_repo = cl_git_sandbox_init("status");
1588
1589 /* touch all the files so stat times are different */
1590 {
1591 git_buf path = GIT_BUF_INIT;
1592 cl_git_pass(git_buf_sets(&path, "status"));
1593 cl_git_pass(git_path_direach(&path, 0, touch_file, NULL));
1594 git_buf_free(&path);
1595 }
1596
1597 opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
1598
1599 basic_diff_status(&diff, &opts);
9c8ed499
RB
1600
1601 cl_git_pass(git_diff_get_perfdata(&perf, diff));
1602 cl_assert_equal_sz(13 + 3, perf.stat_calls);
1603 cl_assert_equal_sz(5, perf.oid_calculations);
94fb4aad
RB
1604
1605 git_diff_free(diff);
1606
1607 /* now allow diff to update stat cache */
1608 opts.flags |= GIT_DIFF_UPDATE_INDEX;
1609
1610 basic_diff_status(&diff, &opts);
9c8ed499
RB
1611
1612 cl_git_pass(git_diff_get_perfdata(&perf, diff));
1613 cl_assert_equal_sz(13 + 3, perf.stat_calls);
1614 cl_assert_equal_sz(5, perf.oid_calculations);
94fb4aad
RB
1615
1616 git_diff_free(diff);
1617
1618 /* now if we do it again, we should see fewer OID calculations */
1619
1620 basic_diff_status(&diff, &opts);
9c8ed499
RB
1621
1622 cl_git_pass(git_diff_get_perfdata(&perf, diff));
1623 cl_assert_equal_sz(13 + 3, perf.stat_calls);
1624 cl_assert_equal_sz(0, perf.oid_calculations);
94fb4aad
RB
1625
1626 git_diff_free(diff);
1627}
8af4966d
RB
1628
1629#define STR7 "0123456"
1630#define STR8 "01234567"
1631#define STR40 STR8 STR8 STR8 STR8 STR8
1632#define STR200 STR40 STR40 STR40 STR40 STR40
1633#define STR999Z STR200 STR200 STR200 STR200 STR40 STR40 STR40 STR40 \
1634 STR8 STR8 STR8 STR8 STR7 "\0"
1635#define STR1000 STR200 STR200 STR200 STR200 STR200
1636#define STR3999Z STR1000 STR1000 STR1000 STR999Z
1637#define STR4000 STR1000 STR1000 STR1000 STR1000
1638
1639static void assert_delta_binary(git_diff *diff, size_t idx, int is_binary)
1640{
1641 git_patch *patch;
1642 const git_diff_delta *delta;
1643
1644 cl_git_pass(git_patch_from_diff(&patch, diff, idx));
1645 delta = git_patch_get_delta(patch);
1646 cl_assert_equal_b((delta->flags & GIT_DIFF_FLAG_BINARY), is_binary);
1647 git_patch_free(patch);
1648}
1649
1650void test_diff_workdir__binary_detection(void)
1651{
1652 git_index *idx;
1653 git_diff *diff = NULL;
1654 git_buf b = GIT_BUF_INIT;
1655 int i;
1656 git_buf data[10] = {
1657 { "1234567890", 0, 0 }, /* 0 - all ascii text control */
3ac1ff42
CH
1658 { "\xC3\x85\xC3\xBC\xE2\x80\xA0\x48\xC3\xB8\xCF\x80\xCE\xA9", 0, 0 }, /* 1 - UTF-8 multibyte text */
1659 { "\xEF\xBB\xBF\xC3\x9C\xE2\xA4\x92\xC6\x92\x38\xC2\xA3\xE2\x82\xAC", 0, 0 }, /* 2 - UTF-8 with BOM */
8af4966d
RB
1660 { STR999Z, 0, 1000 }, /* 3 - ASCII with NUL at 1000 */
1661 { STR3999Z, 0, 4000 }, /* 4 - ASCII with NUL at 4000 */
1662 { STR4000 STR3999Z "x", 0, 8001 }, /* 5 - ASCII with NUL at 8000 */
1663 { STR4000 STR4000 "\0", 0, 8001 }, /* 6 - ASCII with NUL at 8001 */
1664 { "\x00\xDC\x00\x6E\x21\x39\xFE\x0E\x00\x63\x00\xF8"
1665 "\x00\x64\x00\x65\x20\x48", 0, 18 }, /* 7 - UTF-16 text */
1666 { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
1667 "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d",
1668 0, 26 }, /* 8 - All non-printable characters (no NUL) */
1669 { "Hello \x01\x02\x03\x04\x05\x06 World!\x01\x02\x03\x04"
1670 "\x05\x06\x07", 0, 26 }, /* 9 - 50-50 non-printable (no NUL) */
1671 };
1672
1673 g_repo = cl_git_sandbox_init("empty_standard_repo");
1674 cl_git_pass(git_repository_index(&idx, g_repo));
1675
1676 /* We start with ASCII in index and test data in workdir,
1677 * then we will try with test data in index and ASCII in workdir.
1678 */
1679
1680 cl_git_pass(git_buf_sets(&b, "empty_standard_repo/0"));
1681 for (i = 0; i < 10; ++i) {
1682 b.ptr[b.size - 1] = '0' + i;
1683 cl_git_mkfile(b.ptr, "baseline");
1684 cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
1685
1686 if (data[i].size == 0)
1687 data[i].size = strlen(data[i].ptr);
1688 cl_git_write2file(
1689 b.ptr, data[i].ptr, data[i].size, O_WRONLY|O_TRUNC, 0664);
1690 }
1691 git_index_write(idx);
1692
1693 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
1694
1695 cl_assert_equal_i(10, git_diff_num_deltas(diff));
1696
1697 /* using diff binary detection (i.e. looking for NUL byte) */
1698 assert_delta_binary(diff, 0, false);
1699 assert_delta_binary(diff, 1, false);
1700 assert_delta_binary(diff, 2, false);
1701 assert_delta_binary(diff, 3, true);
1702 assert_delta_binary(diff, 4, true);
1703 assert_delta_binary(diff, 5, true);
1704 assert_delta_binary(diff, 6, false);
1705 assert_delta_binary(diff, 7, true);
1706 assert_delta_binary(diff, 8, false);
1707 assert_delta_binary(diff, 9, false);
1708 /* The above have been checked to match command-line Git */
1709
1710 git_diff_free(diff);
1711
1712 cl_git_pass(git_buf_sets(&b, "empty_standard_repo/0"));
1713 for (i = 0; i < 10; ++i) {
1714 b.ptr[b.size - 1] = '0' + i;
1715 cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
1716
1717 cl_git_write2file(b.ptr, "baseline\n", 9, O_WRONLY|O_TRUNC, 0664);
1718 }
1719 git_index_write(idx);
1720
1721 cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
1722
1723 cl_assert_equal_i(10, git_diff_num_deltas(diff));
1724
1725 /* using diff binary detection (i.e. looking for NUL byte) */
1726 assert_delta_binary(diff, 0, false);
1727 assert_delta_binary(diff, 1, false);
1728 assert_delta_binary(diff, 2, false);
1729 assert_delta_binary(diff, 3, true);
1730 assert_delta_binary(diff, 4, true);
1731 assert_delta_binary(diff, 5, true);
1732 assert_delta_binary(diff, 6, false);
1733 assert_delta_binary(diff, 7, true);
1734 assert_delta_binary(diff, 8, false);
1735 assert_delta_binary(diff, 9, false);
1736
1737 git_diff_free(diff);
1738
1739 git_index_free(idx);
1740 git_buf_free(&b);
1741}