1 #include "clar_libgit2.h"
2 #include "diff_helpers.h"
3 #include "repository.h"
4 #include "git2/sys/diff.h"
5 #include "../checkout/checkout_helpers.h"
7 static git_repository
*g_repo
= NULL
;
9 void test_diff_workdir__cleanup(void)
11 cl_git_sandbox_cleanup();
14 void test_diff_workdir__to_index(void)
16 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
17 git_diff
*diff
= NULL
;
21 g_repo
= cl_git_sandbox_init("status");
23 opts
.context_lines
= 3;
24 opts
.interhunk_lines
= 1;
25 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
;
27 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
29 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
30 memset(&exp
, 0, sizeof(exp
));
33 cl_git_pass(diff_foreach_via_iterator(
34 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
36 cl_git_pass(git_diff_foreach(
37 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
39 /* to generate these values:
40 * - cd to tests/resources/status,
42 * - git diff --name-status
46 cl_assert_equal_i(13, exp
.files
);
47 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
48 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
49 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
50 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
51 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
53 cl_assert_equal_i(8, exp
.hunks
);
55 cl_assert_equal_i(14, exp
.lines
);
56 cl_assert_equal_i(5, exp
.line_ctxt
);
57 cl_assert_equal_i(4, exp
.line_adds
);
58 cl_assert_equal_i(5, exp
.line_dels
);
62 git_diff_perfdata perf
= GIT_DIFF_PERFDATA_INIT
;
63 cl_git_pass(git_diff_get_perfdata(&perf
, diff
));
65 13 /* in root */ + 3 /* in subdir */, perf
.stat_calls
);
66 cl_assert_equal_sz(5, perf
.oid_calculations
);
72 void test_diff_workdir__to_index_with_conflicts(void)
74 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
75 git_diff
*diff
= NULL
;
77 git_index_entry our_entry
= {{0}}, their_entry
= {{0}};
78 diff_expects exp
= {0};
80 g_repo
= cl_git_sandbox_init("status");
82 opts
.context_lines
= 3;
83 opts
.interhunk_lines
= 1;
85 /* Adding an entry that represents a rename gets two files in conflict */
86 our_entry
.path
= "subdir/modified_file";
87 our_entry
.mode
= 0100644;
89 their_entry
.path
= "subdir/rename_conflict";
90 their_entry
.mode
= 0100644;
92 cl_git_pass(git_repository_index(&index
, g_repo
));
93 cl_git_pass(git_index_conflict_add(index
, NULL
, &our_entry
, &their_entry
));
95 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, index
, &opts
));
97 cl_git_pass(diff_foreach_via_iterator(
98 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
100 cl_assert_equal_i(9, exp
.files
);
101 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
102 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
103 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_MODIFIED
]);
104 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_CONFLICTED
]);
106 cl_assert_equal_i(7, exp
.hunks
);
108 cl_assert_equal_i(12, exp
.lines
);
109 cl_assert_equal_i(4, exp
.line_ctxt
);
110 cl_assert_equal_i(3, exp
.line_adds
);
111 cl_assert_equal_i(5, exp
.line_dels
);
114 git_index_free(index
);
117 void test_diff_workdir__to_index_with_assume_unchanged(void)
119 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
120 git_diff
*diff
= NULL
;
121 git_index
*idx
= NULL
;
123 const git_index_entry
*iep
;
126 g_repo
= cl_git_sandbox_init("status");
128 /* do initial diff */
130 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
131 memset(&exp
, 0, sizeof(exp
));
132 cl_git_pass(git_diff_foreach(
133 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
134 cl_assert_equal_i(8, exp
.files
);
135 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
136 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
137 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
140 /* mark a couple of entries with ASSUME_UNCHANGED */
142 cl_git_pass(git_repository_index(&idx
, g_repo
));
144 cl_assert((iep
= git_index_get_bypath(idx
, "modified_file", 0)) != NULL
);
145 memcpy(&ie
, iep
, sizeof(ie
));
146 ie
.flags
|= GIT_IDXENTRY_VALID
;
147 cl_git_pass(git_index_add(idx
, &ie
));
149 cl_assert((iep
= git_index_get_bypath(idx
, "file_deleted", 0)) != NULL
);
150 memcpy(&ie
, iep
, sizeof(ie
));
151 ie
.flags
|= GIT_IDXENTRY_VALID
;
152 cl_git_pass(git_index_add(idx
, &ie
));
154 cl_git_pass(git_index_write(idx
));
157 /* redo diff and see that entries are skipped */
159 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
160 memset(&exp
, 0, sizeof(exp
));
161 cl_git_pass(git_diff_foreach(
162 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
163 cl_assert_equal_i(6, exp
.files
);
164 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
165 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_DELETED
]);
166 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_MODIFIED
]);
171 void test_diff_workdir__to_tree(void)
173 /* grabbed a couple of commit oids from the history of the attr repo */
174 const char *a_commit
= "26a125ee1bf"; /* the current HEAD */
175 const char *b_commit
= "0017bd4ab1ec3"; /* the start */
177 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
178 git_diff
*diff
= NULL
;
179 git_diff
*diff2
= NULL
;
183 g_repo
= cl_git_sandbox_init("status");
185 a
= resolve_commit_oid_to_tree(g_repo
, a_commit
);
186 b
= resolve_commit_oid_to_tree(g_repo
, b_commit
);
188 opts
.context_lines
= 3;
189 opts
.interhunk_lines
= 1;
190 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
;
192 /* You can't really generate the equivalent of git_diff_tree_to_workdir()
193 * using C git. It really wants to interpose the index into the diff.
195 * To validate the following results with command line git, I ran the
197 * - git ls-tree 26a125
198 * - find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
199 * The results are documented at the bottom of this file in the
200 * long comment entitled "PREPARATION OF TEST DATA".
202 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, a
, &opts
));
204 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
205 memset(&exp
, 0, sizeof(exp
));
208 cl_git_pass(diff_foreach_via_iterator(
209 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
211 cl_git_pass(git_diff_foreach(
212 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
214 cl_assert_equal_i(14, exp
.files
);
215 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
216 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
217 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
218 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
219 cl_assert_equal_i(5, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
222 /* Since there is no git diff equivalent, let's just assume that the
223 * text diffs produced by git_diff_foreach are accurate here. We will
224 * do more apples-to-apples test comparison below.
229 memset(&exp
, 0, sizeof(exp
));
231 /* This is a compatible emulation of "git diff <sha>" which looks like
232 * a workdir to tree diff (even though it is not really). This is what
233 * you would get from "git diff --name-status 26a125ee1bf"
235 cl_git_pass(git_diff_tree_to_index(&diff
, g_repo
, a
, NULL
, &opts
));
236 cl_git_pass(git_diff_index_to_workdir(&diff2
, g_repo
, NULL
, &opts
));
237 cl_git_pass(git_diff_merge(diff
, diff2
));
238 git_diff_free(diff2
);
240 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
241 memset(&exp
, 0, sizeof(exp
));
244 cl_git_pass(diff_foreach_via_iterator(
245 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
247 cl_git_pass(git_diff_foreach(
248 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
250 cl_assert_equal_i(15, exp
.files
);
251 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_ADDED
]);
252 cl_assert_equal_i(5, exp
.file_status
[GIT_DELTA_DELETED
]);
253 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
254 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
255 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
257 cl_assert_equal_i(11, exp
.hunks
);
259 cl_assert_equal_i(17, exp
.lines
);
260 cl_assert_equal_i(4, exp
.line_ctxt
);
261 cl_assert_equal_i(8, exp
.line_adds
);
262 cl_assert_equal_i(5, exp
.line_dels
);
267 memset(&exp
, 0, sizeof(exp
));
269 /* Again, emulating "git diff <sha>" for testing purposes using
270 * "git diff --name-status 0017bd4ab1ec3" instead.
272 cl_git_pass(git_diff_tree_to_index(&diff
, g_repo
, b
, NULL
, &opts
));
273 cl_git_pass(git_diff_index_to_workdir(&diff2
, g_repo
, NULL
, &opts
));
274 cl_git_pass(git_diff_merge(diff
, diff2
));
275 git_diff_free(diff2
);
277 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
278 memset(&exp
, 0, sizeof(exp
));
281 cl_git_pass(diff_foreach_via_iterator(
282 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
284 cl_git_pass(git_diff_foreach(
285 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
287 cl_assert_equal_i(16, exp
.files
);
288 cl_assert_equal_i(5, exp
.file_status
[GIT_DELTA_ADDED
]);
289 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
290 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_MODIFIED
]);
291 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
292 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
294 cl_assert_equal_i(12, exp
.hunks
);
296 cl_assert_equal_i(19, exp
.lines
);
297 cl_assert_equal_i(3, exp
.line_ctxt
);
298 cl_assert_equal_i(12, exp
.line_adds
);
299 cl_assert_equal_i(4, exp
.line_dels
);
304 /* Let's try that once more with a reversed diff */
306 opts
.flags
|= GIT_DIFF_REVERSE
;
308 cl_git_pass(git_diff_tree_to_index(&diff
, g_repo
, b
, NULL
, &opts
));
309 cl_git_pass(git_diff_index_to_workdir(&diff2
, g_repo
, NULL
, &opts
));
310 cl_git_pass(git_diff_merge(diff
, diff2
));
311 git_diff_free(diff2
);
313 memset(&exp
, 0, sizeof(exp
));
315 cl_git_pass(git_diff_foreach(
316 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
318 cl_assert_equal_i(16, exp
.files
);
319 cl_assert_equal_i(5, exp
.file_status
[GIT_DELTA_DELETED
]);
320 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_ADDED
]);
321 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_MODIFIED
]);
322 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
323 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
325 cl_assert_equal_i(12, exp
.hunks
);
327 cl_assert_equal_i(19, exp
.lines
);
328 cl_assert_equal_i(3, exp
.line_ctxt
);
329 cl_assert_equal_i(12, exp
.line_dels
);
330 cl_assert_equal_i(4, exp
.line_adds
);
340 void test_diff_workdir__to_index_with_pathspec(void)
342 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
343 git_diff
*diff
= NULL
;
345 char *pathspec
= NULL
;
348 g_repo
= cl_git_sandbox_init("status");
350 opts
.context_lines
= 3;
351 opts
.interhunk_lines
= 1;
352 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
;
353 opts
.pathspec
.strings
= &pathspec
;
354 opts
.pathspec
.count
= 1;
356 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
358 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
359 memset(&exp
, 0, sizeof(exp
));
362 cl_git_pass(diff_foreach_via_iterator(
363 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
365 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
367 cl_assert_equal_i(13, exp
.files
);
368 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
369 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
370 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
371 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
372 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
377 pathspec
= "modified_file";
379 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
381 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
382 memset(&exp
, 0, sizeof(exp
));
385 cl_git_pass(diff_foreach_via_iterator(
386 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
388 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
390 cl_assert_equal_i(1, exp
.files
);
391 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
392 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
393 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
394 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
395 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
402 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
404 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
405 memset(&exp
, 0, sizeof(exp
));
408 cl_git_pass(diff_foreach_via_iterator(
409 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
411 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
413 cl_assert_equal_i(3, exp
.files
);
414 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
415 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
416 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
417 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
418 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
423 pathspec
= "*_deleted";
425 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
427 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
428 memset(&exp
, 0, sizeof(exp
));
431 cl_git_pass(diff_foreach_via_iterator(
432 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
434 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
436 cl_assert_equal_i(2, exp
.files
);
437 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
438 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_DELETED
]);
439 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
440 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
441 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
447 void test_diff_workdir__filemode_changes(void)
449 git_diff
*diff
= NULL
;
453 if (!cl_is_chmod_supported())
456 g_repo
= cl_git_sandbox_init("issue_592");
458 cl_repo_set_bool(g_repo
, "core.filemode", true);
460 /* test once with no mods */
462 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
464 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
465 memset(&exp
, 0, sizeof(exp
));
468 cl_git_pass(diff_foreach_via_iterator(
469 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
471 cl_git_pass(git_diff_foreach(
472 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
474 cl_assert_equal_i(0, exp
.files
);
475 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
476 cl_assert_equal_i(0, exp
.hunks
);
481 /* chmod file and test again */
483 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
485 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
487 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
488 memset(&exp
, 0, sizeof(exp
));
491 cl_git_pass(diff_foreach_via_iterator(
492 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
494 cl_git_pass(git_diff_foreach(
495 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
497 cl_assert_equal_i(1, exp
.files
);
498 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
499 cl_assert_equal_i(0, exp
.hunks
);
504 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
507 void test_diff_workdir__filemode_changes_with_filemode_false(void)
509 git_diff
*diff
= NULL
;
512 if (!cl_is_chmod_supported())
515 g_repo
= cl_git_sandbox_init("issue_592");
517 cl_repo_set_bool(g_repo
, "core.filemode", false);
519 /* test once with no mods */
521 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
523 memset(&exp
, 0, sizeof(exp
));
524 cl_git_pass(git_diff_foreach(
525 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
527 cl_assert_equal_i(0, exp
.files
);
528 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
529 cl_assert_equal_i(0, exp
.hunks
);
533 /* chmod file and test again */
535 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
537 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
539 memset(&exp
, 0, sizeof(exp
));
540 cl_git_pass(git_diff_foreach(diff
,
541 diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
543 cl_assert_equal_i(0, exp
.files
);
544 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
545 cl_assert_equal_i(0, exp
.hunks
);
549 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
552 void test_diff_workdir__head_index_and_workdir_all_differ(void)
554 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
555 git_diff
*diff_i2t
= NULL
, *diff_w2i
= NULL
;
557 char *pathspec
= "staged_changes_modified_file";
562 * - head->index diff has 1 line of context, 1 line of diff
563 * - index->workdir diff has 2 lines of context, 1 line of diff
565 * - head->workdir diff has 1 line of context, 2 lines of diff
566 * Let's make sure the right one is returned from each fn.
569 g_repo
= cl_git_sandbox_init("status");
571 tree
= resolve_commit_oid_to_tree(g_repo
, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
573 opts
.pathspec
.strings
= &pathspec
;
574 opts
.pathspec
.count
= 1;
576 cl_git_pass(git_diff_tree_to_index(&diff_i2t
, g_repo
, tree
, NULL
, &opts
));
577 cl_git_pass(git_diff_index_to_workdir(&diff_w2i
, g_repo
, NULL
, &opts
));
579 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
580 memset(&exp
, 0, sizeof(exp
));
583 cl_git_pass(diff_foreach_via_iterator(
584 diff_i2t
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
586 cl_git_pass(git_diff_foreach(
587 diff_i2t
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
589 cl_assert_equal_i(1, exp
.files
);
590 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
591 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
592 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
593 cl_assert_equal_i(1, exp
.hunks
);
594 cl_assert_equal_i(2, exp
.lines
);
595 cl_assert_equal_i(1, exp
.line_ctxt
);
596 cl_assert_equal_i(1, exp
.line_adds
);
597 cl_assert_equal_i(0, exp
.line_dels
);
600 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
601 memset(&exp
, 0, sizeof(exp
));
604 cl_git_pass(diff_foreach_via_iterator(
605 diff_w2i
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
607 cl_git_pass(git_diff_foreach(
608 diff_w2i
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
610 cl_assert_equal_i(1, exp
.files
);
611 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
612 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
613 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
614 cl_assert_equal_i(1, exp
.hunks
);
615 cl_assert_equal_i(3, exp
.lines
);
616 cl_assert_equal_i(2, exp
.line_ctxt
);
617 cl_assert_equal_i(1, exp
.line_adds
);
618 cl_assert_equal_i(0, exp
.line_dels
);
621 cl_git_pass(git_diff_merge(diff_i2t
, diff_w2i
));
623 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
624 memset(&exp
, 0, sizeof(exp
));
627 cl_git_pass(diff_foreach_via_iterator(
628 diff_i2t
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
630 cl_git_pass(git_diff_foreach(
631 diff_i2t
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
633 cl_assert_equal_i(1, exp
.files
);
634 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
635 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
636 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
637 cl_assert_equal_i(1, exp
.hunks
);
638 cl_assert_equal_i(3, exp
.lines
);
639 cl_assert_equal_i(1, exp
.line_ctxt
);
640 cl_assert_equal_i(2, exp
.line_adds
);
641 cl_assert_equal_i(0, exp
.line_dels
);
644 git_diff_free(diff_i2t
);
645 git_diff_free(diff_w2i
);
650 void test_diff_workdir__eof_newline_changes(void)
652 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
653 git_diff
*diff
= NULL
;
655 char *pathspec
= "current_file";
658 g_repo
= cl_git_sandbox_init("status");
660 opts
.pathspec
.strings
= &pathspec
;
661 opts
.pathspec
.count
= 1;
663 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
665 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
666 memset(&exp
, 0, sizeof(exp
));
669 cl_git_pass(diff_foreach_via_iterator(
670 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
672 cl_git_pass(git_diff_foreach(
673 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
675 cl_assert_equal_i(0, exp
.files
);
676 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
677 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
678 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
679 cl_assert_equal_i(0, exp
.hunks
);
680 cl_assert_equal_i(0, exp
.lines
);
681 cl_assert_equal_i(0, exp
.line_ctxt
);
682 cl_assert_equal_i(0, exp
.line_adds
);
683 cl_assert_equal_i(0, exp
.line_dels
);
688 cl_git_append2file("status/current_file", "\n");
690 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
692 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
693 memset(&exp
, 0, sizeof(exp
));
696 cl_git_pass(diff_foreach_via_iterator(
697 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
699 cl_git_pass(git_diff_foreach(
700 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
702 cl_assert_equal_i(1, exp
.files
);
703 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
704 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
705 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
706 cl_assert_equal_i(1, exp
.hunks
);
707 cl_assert_equal_i(2, exp
.lines
);
708 cl_assert_equal_i(1, exp
.line_ctxt
);
709 cl_assert_equal_i(1, exp
.line_adds
);
710 cl_assert_equal_i(0, exp
.line_dels
);
715 cl_git_rewritefile("status/current_file", "current_file");
717 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
719 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
720 memset(&exp
, 0, sizeof(exp
));
723 cl_git_pass(diff_foreach_via_iterator(
724 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
726 cl_git_pass(git_diff_foreach(
727 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
729 cl_assert_equal_i(1, exp
.files
);
730 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
731 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
732 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
733 cl_assert_equal_i(1, exp
.hunks
);
734 cl_assert_equal_i(3, exp
.lines
);
735 cl_assert_equal_i(0, exp
.line_ctxt
);
736 cl_assert_equal_i(1, exp
.line_adds
);
737 cl_assert_equal_i(2, exp
.line_dels
);
743 /* PREPARATION OF TEST DATA
745 * Since there is no command line equivalent of git_diff_tree_to_workdir,
746 * it was a bit of a pain to confirm that I was getting the expected
747 * results in the first part of this tests. Here is what I ended up
748 * doing to set my expectation for the file counts and results:
750 * Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
752 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
753 * B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
754 * C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
755 * D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
756 * E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
757 * F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
758 * G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
759 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
760 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
761 * J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
762 * K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
763 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
767 * find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
769 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
770 * M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
771 * C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
772 * N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
773 * D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
774 * F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
775 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
776 * O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
777 * P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
778 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
779 * K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
780 * Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
781 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
785 * A - current_file (UNMODIFIED) -> not in results
787 * M I ignored_file (IGNORED)
789 * N U new_file (UNTRACKED)
791 * E D staged_changes_file_deleted
792 * F M staged_changes_modified_file
793 * G D staged_delete_file_deleted
794 * H - staged_delete_modified_file (UNMODIFIED) -> not in results
795 * O U staged_new_file
796 * P U staged_new_file_modified_file
797 * I - subdir/current_file (UNMODIFIED) -> not in results
798 * J D subdir/deleted_file
799 * K M subdir/modified_file
800 * Q U subdir/new_file
801 * L - subdir.txt (UNMODIFIED) -> not in results
803 * Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
807 void test_diff_workdir__larger_hunks(void)
809 const char *a_commit
= "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
810 const char *b_commit
= "7a9e0b02e63179929fed24f0a3e0f19168114d10";
812 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
813 size_t i
, d
, num_d
, h
, num_h
, l
, num_l
;
815 g_repo
= cl_git_sandbox_init("diff");
817 cl_assert((a
= resolve_commit_oid_to_tree(g_repo
, a_commit
)) != NULL
);
818 cl_assert((b
= resolve_commit_oid_to_tree(g_repo
, b_commit
)) != NULL
);
820 opts
.context_lines
= 1;
821 opts
.interhunk_lines
= 0;
823 for (i
= 0; i
<= 2; ++i
) {
824 git_diff
*diff
= NULL
;
826 const git_diff_hunk
*hunk
;
827 const git_diff_line
*line
;
829 /* okay, this is a bit silly, but oh well */
832 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
835 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, a
, &opts
));
838 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, b
, &opts
));
842 num_d
= git_diff_num_deltas(diff
);
843 cl_assert_equal_i(2, (int)num_d
);
845 for (d
= 0; d
< num_d
; ++d
) {
846 cl_git_pass(git_patch_from_diff(&patch
, diff
, d
));
849 num_h
= git_patch_num_hunks(patch
);
850 for (h
= 0; h
< num_h
; h
++) {
851 cl_git_pass(git_patch_get_hunk(&hunk
, &num_l
, patch
, h
));
853 for (l
= 0; l
< num_l
; ++l
) {
855 git_patch_get_line_in_hunk(&line
, patch
, h
, l
));
859 /* confirm fail after the last item */
861 git_patch_get_line_in_hunk(&line
, patch
, h
, num_l
));
864 /* confirm fail after the last item */
865 cl_git_fail(git_patch_get_hunk(&hunk
, &num_l
, patch
, num_h
));
867 git_patch_free(patch
);
877 /* Set up a test that exercises this code. The easiest test using existing
878 * test data is probably to create a sandbox of submod2 and then run a
879 * git_diff_tree_to_workdir against tree
880 * 873585b94bdeabccea991ea5e3ec1a277895b698. As for what you should actually
881 * test, you can start by just checking that the number of lines of diff
882 * content matches the actual output of git diff. That will at least
883 * demonstrate that the submodule content is being used to generate somewhat
884 * comparable outputs. It is a test that would fail without this code and
885 * will succeed with it.
888 #include "../submodule/submodule_helpers.h"
890 void test_diff_workdir__submodules(void)
892 const char *a_commit
= "873585b94bdeabccea991ea5e3ec1a277895b698";
894 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
895 git_diff
*diff
= NULL
;
898 g_repo
= setup_fixture_submod2();
900 a
= resolve_commit_oid_to_tree(g_repo
, a_commit
);
903 GIT_DIFF_INCLUDE_UNTRACKED
|
904 GIT_DIFF_INCLUDE_IGNORED
|
905 GIT_DIFF_RECURSE_UNTRACKED_DIRS
|
906 GIT_DIFF_SHOW_UNTRACKED_CONTENT
;
908 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, a
, &opts
));
910 /* diff_print(stderr, diff); */
912 /* essentially doing: git diff 873585b94bdeabccea991ea5e3ec1a277895b698 */
914 memset(&exp
, 0, sizeof(exp
));
916 cl_git_pass(git_diff_foreach(
917 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
919 /* so "git diff 873585" returns:
921 * A just_a_dir/contents
923 * A sm_added_and_uncommited
927 * A sm_changed_untracked_file
928 * M sm_missing_commits
930 * which is a little deceptive because of the difference between the
931 * "git diff <treeish>" results from "git_diff_tree_to_workdir". The
932 * only significant difference is that those Added items will show up
933 * as Untracked items in the pure libgit2 diff.
935 * Then add in the two extra untracked items "not" and "not-submodule"
936 * to get the 12 files reported here.
939 cl_assert_equal_i(12, exp
.files
);
941 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
942 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
943 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_MODIFIED
]);
944 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
945 cl_assert_equal_i(10, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
947 /* the following numbers match "git diff 873585" exactly */
949 cl_assert_equal_i(9, exp
.hunks
);
951 cl_assert_equal_i(33, exp
.lines
);
952 cl_assert_equal_i(2, exp
.line_ctxt
);
953 cl_assert_equal_i(30, exp
.line_adds
);
954 cl_assert_equal_i(1, exp
.line_dels
);
960 void test_diff_workdir__cannot_diff_against_a_bare_repository(void)
962 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
963 git_diff
*diff
= NULL
;
966 g_repo
= cl_git_sandbox_init("testrepo.git");
969 GIT_EBAREREPO
, git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
971 cl_git_pass(git_repository_head_tree(&tree
, g_repo
));
974 GIT_EBAREREPO
, git_diff_tree_to_workdir(&diff
, g_repo
, tree
, &opts
));
979 void test_diff_workdir__to_null_tree(void)
983 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
985 opts
.flags
= GIT_DIFF_INCLUDE_UNTRACKED
|
986 GIT_DIFF_RECURSE_UNTRACKED_DIRS
;
988 g_repo
= cl_git_sandbox_init("status");
990 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, NULL
, &opts
));
992 memset(&exp
, 0, sizeof(exp
));
994 cl_git_pass(git_diff_foreach(
995 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
997 cl_assert_equal_i(exp
.files
, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1002 void test_diff_workdir__checks_options_version(void)
1005 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1006 const git_error
*err
;
1008 g_repo
= cl_git_sandbox_init("status");
1011 cl_git_fail(git_diff_tree_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1012 err
= giterr_last();
1013 cl_assert_equal_i(GITERR_INVALID
, err
->klass
);
1016 opts
.version
= 1024;
1017 cl_git_fail(git_diff_tree_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1018 err
= giterr_last();
1019 cl_assert_equal_i(GITERR_INVALID
, err
->klass
);
1022 void test_diff_workdir__can_diff_empty_file(void)
1026 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1030 g_repo
= cl_git_sandbox_init("attr_index");
1032 tree
= resolve_commit_oid_to_tree(g_repo
, "3812cfef3661"); /* HEAD */
1034 /* baseline - make sure there are no outstanding diffs */
1036 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, tree
, &opts
));
1037 cl_assert_equal_i(2, (int)git_diff_num_deltas(diff
));
1038 git_diff_free(diff
);
1040 /* empty contents of file */
1042 cl_git_rewritefile("attr_index/README.txt", "");
1043 cl_git_pass(git_path_lstat("attr_index/README.txt", &st
));
1044 cl_assert_equal_i(0, (int)st
.st_size
);
1046 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, tree
, &opts
));
1047 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff
));
1048 /* diffs are: .gitattributes, README.txt, sub/sub/.gitattributes */
1049 cl_git_pass(git_patch_from_diff(&patch
, diff
, 1));
1050 git_patch_free(patch
);
1051 git_diff_free(diff
);
1053 /* remove a file altogether */
1055 cl_git_pass(p_unlink("attr_index/README.txt"));
1056 cl_assert(!git_path_exists("attr_index/README.txt"));
1058 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, tree
, &opts
));
1059 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff
));
1060 cl_git_pass(git_patch_from_diff(&patch
, diff
, 1));
1061 git_patch_free(patch
);
1062 git_diff_free(diff
);
1064 git_tree_free(tree
);
1067 void test_diff_workdir__to_index_issue_1397(void)
1069 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1070 git_diff
*diff
= NULL
;
1073 g_repo
= cl_git_sandbox_init("issue_1397");
1075 cl_repo_set_bool(g_repo
, "core.autocrlf", true);
1077 opts
.context_lines
= 3;
1078 opts
.interhunk_lines
= 1;
1080 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1082 memset(&exp
, 0, sizeof(exp
));
1083 cl_git_pass(git_diff_foreach(
1084 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1086 cl_assert_equal_i(0, exp
.files
);
1087 cl_assert_equal_i(0, exp
.hunks
);
1088 cl_assert_equal_i(0, exp
.lines
);
1090 git_diff_free(diff
);
1093 cl_git_rewritefile("issue_1397/crlf_file.txt",
1094 "first line\r\nsecond line modified\r\nboth with crlf");
1096 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1098 memset(&exp
, 0, sizeof(exp
));
1099 cl_git_pass(git_diff_foreach(
1100 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1102 cl_assert_equal_i(1, exp
.files
);
1103 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1105 cl_assert_equal_i(1, exp
.hunks
);
1107 cl_assert_equal_i(5, exp
.lines
);
1108 cl_assert_equal_i(3, exp
.line_ctxt
);
1109 cl_assert_equal_i(1, exp
.line_adds
);
1110 cl_assert_equal_i(1, exp
.line_dels
);
1112 git_diff_free(diff
);
1115 void test_diff_workdir__to_tree_issue_1397(void)
1117 const char *a_commit
= "7f483a738"; /* the current HEAD */
1119 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1120 git_diff
*diff
= NULL
;
1121 git_diff
*diff2
= NULL
;
1124 g_repo
= cl_git_sandbox_init("issue_1397");
1126 cl_repo_set_bool(g_repo
, "core.autocrlf", true);
1128 a
= resolve_commit_oid_to_tree(g_repo
, a_commit
);
1130 opts
.context_lines
= 3;
1131 opts
.interhunk_lines
= 1;
1133 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, a
, &opts
));
1135 memset(&exp
, 0, sizeof(exp
));
1136 cl_git_pass(git_diff_foreach(
1137 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1139 cl_assert_equal_i(0, exp
.files
);
1140 cl_assert_equal_i(0, exp
.hunks
);
1141 cl_assert_equal_i(0, exp
.lines
);
1143 git_diff_free(diff
);
1146 cl_git_pass(git_diff_tree_to_index(&diff
, g_repo
, a
, NULL
, &opts
));
1147 cl_git_pass(git_diff_index_to_workdir(&diff2
, g_repo
, NULL
, &opts
));
1148 cl_git_pass(git_diff_merge(diff
, diff2
));
1149 git_diff_free(diff2
);
1151 memset(&exp
, 0, sizeof(exp
));
1152 cl_git_pass(git_diff_foreach(
1153 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1155 cl_assert_equal_i(0, exp
.files
);
1156 cl_assert_equal_i(0, exp
.hunks
);
1157 cl_assert_equal_i(0, exp
.lines
);
1159 git_diff_free(diff
);
1163 void test_diff_workdir__untracked_directory_scenarios(void)
1165 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1166 git_diff
*diff
= NULL
;
1168 char *pathspec
= NULL
;
1169 static const char *files0
[] = {
1170 "subdir/deleted_file",
1171 "subdir/modified_file",
1175 static const char *files1
[] = {
1176 "subdir/deleted_file",
1177 "subdir/directory/",
1178 "subdir/modified_file",
1182 static const char *files2
[] = {
1183 "subdir/deleted_file",
1184 "subdir/directory/more/notignored",
1185 "subdir/modified_file",
1190 g_repo
= cl_git_sandbox_init("status");
1191 cl_git_mkfile("status/.gitignore", "ignored\n");
1193 opts
.context_lines
= 3;
1194 opts
.interhunk_lines
= 1;
1195 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
;
1196 opts
.pathspec
.strings
= &pathspec
;
1197 opts
.pathspec
.count
= 1;
1198 pathspec
= "subdir";
1200 /* baseline for "subdir" pathspec */
1202 memset(&exp
, 0, sizeof(exp
));
1205 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1207 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1209 cl_assert_equal_i(3, exp
.files
);
1210 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1211 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1212 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1213 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
1214 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1216 git_diff_free(diff
);
1218 /* empty directory */
1220 cl_git_pass(p_mkdir("status/subdir/directory", 0777));
1222 memset(&exp
, 0, sizeof(exp
));
1225 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1227 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1229 cl_assert_equal_i(4, exp
.files
);
1230 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1231 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1232 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1233 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1234 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1236 git_diff_free(diff
);
1238 /* empty directory in empty directory */
1240 cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777));
1242 memset(&exp
, 0, sizeof(exp
));
1245 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1247 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1249 cl_assert_equal_i(4, exp
.files
);
1250 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1251 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1252 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1253 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1254 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1256 git_diff_free(diff
);
1258 /* directory with only ignored files */
1260 cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
1261 cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n");
1263 cl_git_pass(p_mkdir("status/subdir/directory/another", 0777));
1264 cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n");
1266 memset(&exp
, 0, sizeof(exp
));
1269 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1271 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1273 cl_assert_equal_i(4, exp
.files
);
1274 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1275 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1276 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1277 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1278 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1280 git_diff_free(diff
);
1282 /* directory with ignored directory (contents irrelevant) */
1284 cl_git_pass(p_mkdir("status/subdir/directory/more", 0777));
1285 cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777));
1286 cl_git_mkfile("status/subdir/directory/more/ignored/notignored",
1287 "inside ignored dir\n");
1289 memset(&exp
, 0, sizeof(exp
));
1292 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1294 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1296 cl_assert_equal_i(4, exp
.files
);
1297 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1298 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1299 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1300 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1301 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1303 git_diff_free(diff
);
1305 /* quick version avoids directory scan */
1307 opts
.flags
= opts
.flags
| GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS
;
1309 memset(&exp
, 0, sizeof(exp
));
1312 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1314 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1316 cl_assert_equal_i(4, exp
.files
);
1317 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1318 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1319 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1320 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
1321 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1323 git_diff_free(diff
);
1325 /* directory with nested non-ignored content */
1327 opts
.flags
= opts
.flags
& ~GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS
;
1329 cl_git_mkfile("status/subdir/directory/more/notignored",
1330 "not ignored deep under untracked\n");
1332 memset(&exp
, 0, sizeof(exp
));
1335 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1337 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1339 cl_assert_equal_i(4, exp
.files
);
1340 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1341 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1342 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1343 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
1344 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1346 git_diff_free(diff
);
1348 /* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
1350 opts
.flags
= opts
.flags
& ~GIT_DIFF_INCLUDE_IGNORED
;
1351 opts
.flags
= opts
.flags
| GIT_DIFF_RECURSE_UNTRACKED_DIRS
;
1353 memset(&exp
, 0, sizeof(exp
));
1356 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1358 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1360 cl_assert_equal_i(4, exp
.files
);
1361 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1362 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1363 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1364 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
1365 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1367 git_diff_free(diff
);
1371 void test_diff_workdir__untracked_directory_comes_last(void)
1373 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1374 git_diff
*diff
= NULL
;
1376 g_repo
= cl_git_sandbox_init("renames");
1378 cl_git_mkfile("renames/.gitignore", "*.ign\n");
1379 cl_git_pass(p_mkdir("renames/zzz_untracked", 0777));
1380 cl_git_mkfile("renames/zzz_untracked/an.ign", "ignore me please");
1381 cl_git_mkfile("renames/zzz_untracked/skip.ign", "ignore me really");
1382 cl_git_mkfile("renames/zzz_untracked/test.ign", "ignore me now");
1384 opts
.context_lines
= 3;
1385 opts
.interhunk_lines
= 1;
1386 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
;
1388 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1390 cl_assert(diff
!= NULL
);
1392 git_diff_free(diff
);
1395 void test_diff_workdir__untracked_with_bom(void)
1397 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1398 git_diff
*diff
= NULL
;
1399 const git_diff_delta
*delta
;
1401 g_repo
= cl_git_sandbox_init("empty_standard_repo");
1402 cl_repo_set_bool(g_repo
, "core.autocrlf", true);
1404 cl_git_write2file("empty_standard_repo/bom.txt",
1405 "\xFF\xFE\x31\x00\x32\x00\x33\x00\x34\x00", 10, O_WRONLY
|O_CREAT
, 0664);
1408 GIT_DIFF_INCLUDE_UNTRACKED
| GIT_DIFF_SHOW_UNTRACKED_CONTENT
;
1410 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1412 cl_assert_equal_i(1, git_diff_num_deltas(diff
));
1413 cl_assert((delta
= git_diff_get_delta(diff
, 0)) != NULL
);
1414 cl_assert_equal_i(GIT_DELTA_UNTRACKED
, delta
->status
);
1416 /* not known at this point
1417 * cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
1420 git_diff_free(diff
);
1423 void test_diff_workdir__patience_diff(void)
1426 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1427 git_diff
*diff
= NULL
;
1428 git_patch
*patch
= NULL
;
1429 git_buf buf
= GIT_BUF_INIT
;
1430 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";
1431 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";
1433 g_repo
= cl_git_sandbox_init("empty_standard_repo");
1434 cl_repo_set_bool(g_repo
, "core.autocrlf", true);
1435 cl_git_pass(git_repository_index(&index
, g_repo
));
1438 "empty_standard_repo/test.txt",
1439 "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");
1440 cl_git_pass(git_index_add_bypath(index
, "test.txt"));
1441 cl_repo_commit_from_index(NULL
, g_repo
, NULL
, 1372350000, "Base");
1442 git_index_free(index
);
1445 "empty_standard_repo/test.txt",
1446 "When I wrote this\nI did not know\nI did not know\nhow to create\na patience diff\nanother problem\na minimal diff\n");
1448 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1449 cl_assert_equal_i(1, git_diff_num_deltas(diff
));
1450 cl_git_pass(git_patch_from_diff(&patch
, diff
, 0));
1451 cl_git_pass(git_patch_to_buf(&buf
, patch
));
1453 cl_assert_equal_s(expected_normal
, buf
.ptr
);
1454 git_buf_clear(&buf
);
1455 git_patch_free(patch
);
1456 git_diff_free(diff
);
1458 opts
.flags
|= GIT_DIFF_PATIENCE
;
1460 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1461 cl_assert_equal_i(1, git_diff_num_deltas(diff
));
1462 cl_git_pass(git_patch_from_diff(&patch
, diff
, 0));
1463 cl_git_pass(git_patch_to_buf(&buf
, patch
));
1465 cl_assert_equal_s(expected_patience
, buf
.ptr
);
1466 git_buf_clear(&buf
);
1469 git_patch_free(patch
);
1470 git_diff_free(diff
);
1473 void test_diff_workdir__with_stale_index(void)
1475 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1476 git_diff
*diff
= NULL
;
1477 git_index
*idx
= NULL
;
1480 g_repo
= cl_git_sandbox_init("status");
1481 cl_git_pass(git_repository_index(&idx
, g_repo
));
1483 /* make the in-memory index invalid */
1487 cl_git_pass(git_repository_open(&r2
, "status"));
1488 cl_git_pass(git_repository_index(&idx2
, r2
));
1489 cl_git_pass(git_index_add_bypath(idx2
, "new_file"));
1490 cl_git_pass(git_index_add_bypath(idx2
, "subdir/new_file"));
1491 cl_git_pass(git_index_remove_bypath(idx2
, "staged_new_file"));
1492 cl_git_pass(git_index_remove_bypath(idx2
, "staged_changes_file_deleted"));
1493 cl_git_pass(git_index_write(idx2
));
1494 git_index_free(idx2
);
1495 git_repository_free(r2
);
1498 opts
.context_lines
= 3;
1499 opts
.interhunk_lines
= 1;
1500 opts
.flags
|= GIT_DIFF_INCLUDE_UNTRACKED
| GIT_DIFF_INCLUDE_UNMODIFIED
;
1502 /* first try with index pointer which should prevent reload */
1504 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, idx
, &opts
));
1506 memset(&exp
, 0, sizeof(exp
));
1508 cl_git_pass(git_diff_foreach(
1509 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1511 cl_assert_equal_i(17, exp
.files
);
1512 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1513 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
1514 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1515 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1516 cl_assert_equal_i(5, exp
.file_status
[GIT_DELTA_UNMODIFIED
]);
1518 git_diff_free(diff
);
1520 /* now let's try without the index pointer which should trigger reload */
1522 /* two files that were UNTRACKED should have become UNMODIFIED */
1523 /* one file that was UNMODIFIED should now have become UNTRACKED */
1524 /* one file that was DELETED should now be gone completely */
1526 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1528 memset(&exp
, 0, sizeof(exp
));
1530 cl_git_pass(git_diff_foreach(
1531 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1533 git_diff_free(diff
);
1535 cl_assert_equal_i(16, exp
.files
);
1536 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1537 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_DELETED
]);
1538 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1539 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1540 cl_assert_equal_i(6, exp
.file_status
[GIT_DELTA_UNMODIFIED
]);
1542 git_index_free(idx
);
1545 static int touch_file(void *payload
, git_buf
*path
)
1550 GIT_UNUSED(payload
);
1551 if (git_path_isdir(path
->ptr
))
1554 cl_assert((fd
= p_open(path
->ptr
, O_RDWR
)) >= 0);
1555 cl_assert_equal_i(1, p_read(fd
, &b
, 1));
1556 cl_must_pass(p_lseek(fd
, 0, SEEK_SET
));
1557 cl_must_pass(p_write(fd
, &b
, 1));
1558 cl_must_pass(p_close(fd
));
1563 static void basic_diff_status(git_diff
**out
, const git_diff_options
*opts
)
1567 cl_git_pass(git_diff_index_to_workdir(out
, g_repo
, NULL
, opts
));
1569 memset(&exp
, 0, sizeof(exp
));
1571 cl_git_pass(git_diff_foreach(
1572 *out
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1574 cl_assert_equal_i(13, exp
.files
);
1575 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1576 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
1577 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1578 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1579 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1582 void test_diff_workdir__can_update_index(void)
1584 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1585 git_diff
*diff
= NULL
;
1586 git_diff_perfdata perf
= GIT_DIFF_PERFDATA_INIT
;
1589 g_repo
= cl_git_sandbox_init("status");
1591 /* touch all the files so stat times are different */
1593 git_buf path
= GIT_BUF_INIT
;
1594 cl_git_pass(git_buf_sets(&path
, "status"));
1595 cl_git_pass(git_path_direach(&path
, 0, touch_file
, NULL
));
1596 git_buf_free(&path
);
1599 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
;
1601 basic_diff_status(&diff
, &opts
);
1603 cl_git_pass(git_diff_get_perfdata(&perf
, diff
));
1604 cl_assert_equal_sz(13 + 3, perf
.stat_calls
);
1605 cl_assert_equal_sz(5, perf
.oid_calculations
);
1607 git_diff_free(diff
);
1609 /* now allow diff to update stat cache */
1610 opts
.flags
|= GIT_DIFF_UPDATE_INDEX
;
1612 /* advance a tick for the index so we don't re-calculate racily-clean entries */
1613 cl_git_pass(git_repository_index__weakptr(&index
, g_repo
));
1616 basic_diff_status(&diff
, &opts
);
1618 cl_git_pass(git_diff_get_perfdata(&perf
, diff
));
1619 cl_assert_equal_sz(13 + 3, perf
.stat_calls
);
1620 cl_assert_equal_sz(5, perf
.oid_calculations
);
1622 git_diff_free(diff
);
1624 /* now if we do it again, we should see fewer OID calculations */
1626 basic_diff_status(&diff
, &opts
);
1628 cl_git_pass(git_diff_get_perfdata(&perf
, diff
));
1629 cl_assert_equal_sz(13 + 3, perf
.stat_calls
);
1630 cl_assert_equal_sz(0, perf
.oid_calculations
);
1632 git_diff_free(diff
);
1635 #define STR7 "0123456"
1636 #define STR8 "01234567"
1637 #define STR40 STR8 STR8 STR8 STR8 STR8
1638 #define STR200 STR40 STR40 STR40 STR40 STR40
1639 #define STR999Z STR200 STR200 STR200 STR200 STR40 STR40 STR40 STR40 \
1640 STR8 STR8 STR8 STR8 STR7 "\0"
1641 #define STR1000 STR200 STR200 STR200 STR200 STR200
1642 #define STR3999Z STR1000 STR1000 STR1000 STR999Z
1643 #define STR4000 STR1000 STR1000 STR1000 STR1000
1645 static void assert_delta_binary(git_diff
*diff
, size_t idx
, int is_binary
)
1648 const git_diff_delta
*delta
;
1650 cl_git_pass(git_patch_from_diff(&patch
, diff
, idx
));
1651 delta
= git_patch_get_delta(patch
);
1652 cl_assert_equal_b((delta
->flags
& GIT_DIFF_FLAG_BINARY
), is_binary
);
1653 git_patch_free(patch
);
1656 void test_diff_workdir__binary_detection(void)
1659 git_diff
*diff
= NULL
;
1660 git_buf b
= GIT_BUF_INIT
;
1662 git_buf data
[10] = {
1663 { "1234567890", 0, 0 }, /* 0 - all ascii text control */
1664 { "\xC3\x85\xC3\xBC\xE2\x80\xA0\x48\xC3\xB8\xCF\x80\xCE\xA9", 0, 0 }, /* 1 - UTF-8 multibyte text */
1665 { "\xEF\xBB\xBF\xC3\x9C\xE2\xA4\x92\xC6\x92\x38\xC2\xA3\xE2\x82\xAC", 0, 0 }, /* 2 - UTF-8 with BOM */
1666 { STR999Z
, 0, 1000 }, /* 3 - ASCII with NUL at 1000 */
1667 { STR3999Z
, 0, 4000 }, /* 4 - ASCII with NUL at 4000 */
1668 { STR4000 STR3999Z
"x", 0, 8001 }, /* 5 - ASCII with NUL at 8000 */
1669 { STR4000 STR4000
"\0", 0, 8001 }, /* 6 - ASCII with NUL at 8001 */
1670 { "\x00\xDC\x00\x6E\x21\x39\xFE\x0E\x00\x63\x00\xF8"
1671 "\x00\x64\x00\x65\x20\x48", 0, 18 }, /* 7 - UTF-16 text */
1672 { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
1673 "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d",
1674 0, 26 }, /* 8 - All non-printable characters (no NUL) */
1675 { "Hello \x01\x02\x03\x04\x05\x06 World!\x01\x02\x03\x04"
1676 "\x05\x06\x07", 0, 26 }, /* 9 - 50-50 non-printable (no NUL) */
1679 g_repo
= cl_git_sandbox_init("empty_standard_repo");
1680 cl_git_pass(git_repository_index(&idx
, g_repo
));
1682 /* We start with ASCII in index and test data in workdir,
1683 * then we will try with test data in index and ASCII in workdir.
1686 cl_git_pass(git_buf_sets(&b
, "empty_standard_repo/0"));
1687 for (i
= 0; i
< 10; ++i
) {
1688 b
.ptr
[b
.size
- 1] = '0' + i
;
1689 cl_git_mkfile(b
.ptr
, "baseline");
1690 cl_git_pass(git_index_add_bypath(idx
, &b
.ptr
[b
.size
- 1]));
1692 if (data
[i
].size
== 0)
1693 data
[i
].size
= strlen(data
[i
].ptr
);
1695 b
.ptr
, data
[i
].ptr
, data
[i
].size
, O_WRONLY
|O_TRUNC
, 0664);
1697 git_index_write(idx
);
1699 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
1701 cl_assert_equal_i(10, git_diff_num_deltas(diff
));
1703 /* using diff binary detection (i.e. looking for NUL byte) */
1704 assert_delta_binary(diff
, 0, false);
1705 assert_delta_binary(diff
, 1, false);
1706 assert_delta_binary(diff
, 2, false);
1707 assert_delta_binary(diff
, 3, true);
1708 assert_delta_binary(diff
, 4, true);
1709 assert_delta_binary(diff
, 5, true);
1710 assert_delta_binary(diff
, 6, false);
1711 assert_delta_binary(diff
, 7, true);
1712 assert_delta_binary(diff
, 8, false);
1713 assert_delta_binary(diff
, 9, false);
1714 /* The above have been checked to match command-line Git */
1716 git_diff_free(diff
);
1718 cl_git_pass(git_buf_sets(&b
, "empty_standard_repo/0"));
1719 for (i
= 0; i
< 10; ++i
) {
1720 b
.ptr
[b
.size
- 1] = '0' + i
;
1721 cl_git_pass(git_index_add_bypath(idx
, &b
.ptr
[b
.size
- 1]));
1723 cl_git_write2file(b
.ptr
, "baseline\n", 9, O_WRONLY
|O_TRUNC
, 0664);
1725 git_index_write(idx
);
1727 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
1729 cl_assert_equal_i(10, git_diff_num_deltas(diff
));
1731 /* using diff binary detection (i.e. looking for NUL byte) */
1732 assert_delta_binary(diff
, 0, false);
1733 assert_delta_binary(diff
, 1, false);
1734 assert_delta_binary(diff
, 2, false);
1735 assert_delta_binary(diff
, 3, true);
1736 assert_delta_binary(diff
, 4, true);
1737 assert_delta_binary(diff
, 5, true);
1738 assert_delta_binary(diff
, 6, false);
1739 assert_delta_binary(diff
, 7, true);
1740 assert_delta_binary(diff
, 8, false);
1741 assert_delta_binary(diff
, 9, false);
1743 git_diff_free(diff
);
1745 git_index_free(idx
);
1749 void test_diff_workdir__to_index_conflicted(void) {
1750 const char *a_commit
= "26a125ee1bf"; /* the current HEAD */
1751 git_index_entry ancestor
= {{0}}, ours
= {{0}}, theirs
= {{0}};
1754 git_diff
*diff1
, *diff2
;
1755 const git_diff_delta
*delta
;
1757 g_repo
= cl_git_sandbox_init("status");
1758 a
= resolve_commit_oid_to_tree(g_repo
, a_commit
);
1760 cl_git_pass(git_repository_index(&index
, g_repo
));
1762 ancestor
.path
= ours
.path
= theirs
.path
= "_file";
1763 ancestor
.mode
= ours
.mode
= theirs
.mode
= 0100644;
1764 git_oid_fromstr(&ancestor
.id
, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
1765 git_oid_fromstr(&ours
.id
, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
1766 git_oid_fromstr(&theirs
.id
, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
1767 cl_git_pass(git_index_conflict_add(index
, &ancestor
, &ours
, &theirs
));
1769 cl_git_pass(git_diff_tree_to_index(&diff1
, g_repo
, a
, index
, NULL
));
1770 cl_git_pass(git_diff_index_to_workdir(&diff2
, g_repo
, index
, NULL
));
1771 cl_git_pass(git_diff_merge(diff1
, diff2
));
1773 cl_assert_equal_i(git_diff_num_deltas(diff1
), 12);
1774 delta
= git_diff_get_delta(diff1
, 0);
1775 cl_assert_equal_s(delta
->old_file
.path
, "_file");
1776 cl_assert_equal_i(delta
->nfiles
, 1);
1777 cl_assert_equal_i(delta
->status
, GIT_DELTA_CONFLICTED
);
1779 git_diff_free(diff2
);
1780 git_diff_free(diff1
);
1781 git_index_free(index
);