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