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__to_index_with_pathlist_disabling_fnmatch(void)
449 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
450 git_diff
*diff
= NULL
;
452 char *pathspec
= NULL
;
455 g_repo
= cl_git_sandbox_init("status");
457 opts
.context_lines
= 3;
458 opts
.interhunk_lines
= 1;
459 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
|
460 GIT_DIFF_DISABLE_PATHSPEC_MATCH
;
461 opts
.pathspec
.strings
= &pathspec
;
462 opts
.pathspec
.count
= 0;
464 /* ensure that an empty pathspec list is ignored */
465 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
467 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
468 memset(&exp
, 0, sizeof(exp
));
471 cl_git_pass(diff_foreach_via_iterator(
472 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
474 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
476 cl_assert_equal_i(13, exp
.files
);
477 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
478 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
479 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
480 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
481 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
486 /* ensure that a single NULL pathspec is filtered out (like when using
490 opts
.pathspec
.count
= 1;
492 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
494 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
495 memset(&exp
, 0, sizeof(exp
));
498 cl_git_pass(diff_foreach_via_iterator(
499 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
501 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
503 cl_assert_equal_i(13, exp
.files
);
504 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
505 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
506 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
507 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
508 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
513 pathspec
= "modified_file";
515 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
517 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
518 memset(&exp
, 0, sizeof(exp
));
521 cl_git_pass(diff_foreach_via_iterator(
522 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
524 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
526 cl_assert_equal_i(1, exp
.files
);
527 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
528 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
529 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
530 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
531 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
536 /* ensure that subdirs can be specified */
539 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
541 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
542 memset(&exp
, 0, sizeof(exp
));
545 cl_git_pass(diff_foreach_via_iterator(
546 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
548 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
550 cl_assert_equal_i(3, exp
.files
);
551 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
552 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
553 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
554 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
555 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
560 /* ensure that subdirs can be specified with a trailing slash */
561 pathspec
= "subdir/";
563 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
565 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
566 memset(&exp
, 0, sizeof(exp
));
569 cl_git_pass(diff_foreach_via_iterator(
570 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
572 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
574 cl_assert_equal_i(3, exp
.files
);
575 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
576 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
577 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
578 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
579 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
584 /* ensure that fnmatching is completely disabled */
585 pathspec
= "subdir/*";
587 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
589 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
590 memset(&exp
, 0, sizeof(exp
));
593 cl_git_pass(diff_foreach_via_iterator(
594 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
596 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
598 cl_assert_equal_i(0, exp
.files
);
599 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
600 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
601 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
602 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
603 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
608 /* ensure that the prefix matching isn't completely braindead */
611 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
613 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
614 memset(&exp
, 0, sizeof(exp
));
617 cl_git_pass(diff_foreach_via_iterator(
618 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
620 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
622 cl_assert_equal_i(0, exp
.files
);
623 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
624 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
625 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
626 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
627 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
632 /* ensure that fnmatching isn't working at all */
633 pathspec
= "*_deleted";
635 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
637 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
638 memset(&exp
, 0, sizeof(exp
));
641 cl_git_pass(diff_foreach_via_iterator(
642 diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
644 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
646 cl_assert_equal_i(0, exp
.files
);
647 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
648 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
649 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
650 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
651 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
657 void test_diff_workdir__filemode_changes(void)
659 git_diff
*diff
= NULL
;
663 if (!cl_is_chmod_supported())
666 g_repo
= cl_git_sandbox_init("issue_592");
668 cl_repo_set_bool(g_repo
, "core.filemode", true);
670 /* test once with no mods */
672 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
674 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
675 memset(&exp
, 0, sizeof(exp
));
678 cl_git_pass(diff_foreach_via_iterator(
679 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
681 cl_git_pass(git_diff_foreach(
682 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
684 cl_assert_equal_i(0, exp
.files
);
685 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
686 cl_assert_equal_i(0, exp
.hunks
);
691 /* chmod file and test again */
693 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
695 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
697 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
698 memset(&exp
, 0, sizeof(exp
));
701 cl_git_pass(diff_foreach_via_iterator(
702 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
704 cl_git_pass(git_diff_foreach(
705 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
707 cl_assert_equal_i(1, exp
.files
);
708 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
709 cl_assert_equal_i(0, exp
.hunks
);
714 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
717 void test_diff_workdir__filemode_changes_with_filemode_false(void)
719 git_diff
*diff
= NULL
;
722 if (!cl_is_chmod_supported())
725 g_repo
= cl_git_sandbox_init("issue_592");
727 cl_repo_set_bool(g_repo
, "core.filemode", false);
729 /* test once with no mods */
731 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
733 memset(&exp
, 0, sizeof(exp
));
734 cl_git_pass(git_diff_foreach(
735 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
737 cl_assert_equal_i(0, exp
.files
);
738 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
739 cl_assert_equal_i(0, exp
.hunks
);
743 /* chmod file and test again */
745 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
747 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
749 memset(&exp
, 0, sizeof(exp
));
750 cl_git_pass(git_diff_foreach(diff
,
751 diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
753 cl_assert_equal_i(0, exp
.files
);
754 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
755 cl_assert_equal_i(0, exp
.hunks
);
759 cl_assert(cl_toggle_filemode("issue_592/a.txt"));
762 void test_diff_workdir__head_index_and_workdir_all_differ(void)
764 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
765 git_diff
*diff_i2t
= NULL
, *diff_w2i
= NULL
;
767 char *pathspec
= "staged_changes_modified_file";
772 * - head->index diff has 1 line of context, 1 line of diff
773 * - index->workdir diff has 2 lines of context, 1 line of diff
775 * - head->workdir diff has 1 line of context, 2 lines of diff
776 * Let's make sure the right one is returned from each fn.
779 g_repo
= cl_git_sandbox_init("status");
781 tree
= resolve_commit_oid_to_tree(g_repo
, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
783 opts
.pathspec
.strings
= &pathspec
;
784 opts
.pathspec
.count
= 1;
786 cl_git_pass(git_diff_tree_to_index(&diff_i2t
, g_repo
, tree
, NULL
, &opts
));
787 cl_git_pass(git_diff_index_to_workdir(&diff_w2i
, g_repo
, NULL
, &opts
));
789 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
790 memset(&exp
, 0, sizeof(exp
));
793 cl_git_pass(diff_foreach_via_iterator(
794 diff_i2t
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
796 cl_git_pass(git_diff_foreach(
797 diff_i2t
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
799 cl_assert_equal_i(1, exp
.files
);
800 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
801 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
802 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
803 cl_assert_equal_i(1, exp
.hunks
);
804 cl_assert_equal_i(2, exp
.lines
);
805 cl_assert_equal_i(1, exp
.line_ctxt
);
806 cl_assert_equal_i(1, exp
.line_adds
);
807 cl_assert_equal_i(0, exp
.line_dels
);
810 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
811 memset(&exp
, 0, sizeof(exp
));
814 cl_git_pass(diff_foreach_via_iterator(
815 diff_w2i
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
817 cl_git_pass(git_diff_foreach(
818 diff_w2i
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
820 cl_assert_equal_i(1, exp
.files
);
821 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
822 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
823 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
824 cl_assert_equal_i(1, exp
.hunks
);
825 cl_assert_equal_i(3, exp
.lines
);
826 cl_assert_equal_i(2, exp
.line_ctxt
);
827 cl_assert_equal_i(1, exp
.line_adds
);
828 cl_assert_equal_i(0, exp
.line_dels
);
831 cl_git_pass(git_diff_merge(diff_i2t
, diff_w2i
));
833 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
834 memset(&exp
, 0, sizeof(exp
));
837 cl_git_pass(diff_foreach_via_iterator(
838 diff_i2t
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
840 cl_git_pass(git_diff_foreach(
841 diff_i2t
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
843 cl_assert_equal_i(1, exp
.files
);
844 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
845 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
846 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
847 cl_assert_equal_i(1, exp
.hunks
);
848 cl_assert_equal_i(3, exp
.lines
);
849 cl_assert_equal_i(1, exp
.line_ctxt
);
850 cl_assert_equal_i(2, exp
.line_adds
);
851 cl_assert_equal_i(0, exp
.line_dels
);
854 git_diff_free(diff_i2t
);
855 git_diff_free(diff_w2i
);
860 void test_diff_workdir__eof_newline_changes(void)
862 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
863 git_diff
*diff
= NULL
;
865 char *pathspec
= "current_file";
868 g_repo
= cl_git_sandbox_init("status");
870 opts
.pathspec
.strings
= &pathspec
;
871 opts
.pathspec
.count
= 1;
873 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
875 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
876 memset(&exp
, 0, sizeof(exp
));
879 cl_git_pass(diff_foreach_via_iterator(
880 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
882 cl_git_pass(git_diff_foreach(
883 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
885 cl_assert_equal_i(0, exp
.files
);
886 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
887 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
888 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_MODIFIED
]);
889 cl_assert_equal_i(0, exp
.hunks
);
890 cl_assert_equal_i(0, exp
.lines
);
891 cl_assert_equal_i(0, exp
.line_ctxt
);
892 cl_assert_equal_i(0, exp
.line_adds
);
893 cl_assert_equal_i(0, exp
.line_dels
);
898 cl_git_append2file("status/current_file", "\n");
900 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
902 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
903 memset(&exp
, 0, sizeof(exp
));
906 cl_git_pass(diff_foreach_via_iterator(
907 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
909 cl_git_pass(git_diff_foreach(
910 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
912 cl_assert_equal_i(1, exp
.files
);
913 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
914 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
915 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
916 cl_assert_equal_i(1, exp
.hunks
);
917 cl_assert_equal_i(2, exp
.lines
);
918 cl_assert_equal_i(1, exp
.line_ctxt
);
919 cl_assert_equal_i(1, exp
.line_adds
);
920 cl_assert_equal_i(0, exp
.line_dels
);
925 cl_git_rewritefile("status/current_file", "current_file");
927 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
929 for (use_iterator
= 0; use_iterator
<= 1; use_iterator
++) {
930 memset(&exp
, 0, sizeof(exp
));
933 cl_git_pass(diff_foreach_via_iterator(
934 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
936 cl_git_pass(git_diff_foreach(
937 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
939 cl_assert_equal_i(1, exp
.files
);
940 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
941 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
942 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
943 cl_assert_equal_i(1, exp
.hunks
);
944 cl_assert_equal_i(3, exp
.lines
);
945 cl_assert_equal_i(0, exp
.line_ctxt
);
946 cl_assert_equal_i(1, exp
.line_adds
);
947 cl_assert_equal_i(2, exp
.line_dels
);
953 /* PREPARATION OF TEST DATA
955 * Since there is no command line equivalent of git_diff_tree_to_workdir,
956 * it was a bit of a pain to confirm that I was getting the expected
957 * results in the first part of this tests. Here is what I ended up
958 * doing to set my expectation for the file counts and results:
960 * Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
962 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
963 * B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
964 * C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
965 * D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
966 * E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
967 * F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
968 * G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
969 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
970 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
971 * J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
972 * K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
973 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
977 * find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
979 * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
980 * M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
981 * C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
982 * N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
983 * D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
984 * F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
985 * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
986 * O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
987 * P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
988 * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
989 * K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
990 * Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
991 * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
995 * A - current_file (UNMODIFIED) -> not in results
997 * M I ignored_file (IGNORED)
999 * N U new_file (UNTRACKED)
1000 * D M staged_changes
1001 * E D staged_changes_file_deleted
1002 * F M staged_changes_modified_file
1003 * G D staged_delete_file_deleted
1004 * H - staged_delete_modified_file (UNMODIFIED) -> not in results
1005 * O U staged_new_file
1006 * P U staged_new_file_modified_file
1007 * I - subdir/current_file (UNMODIFIED) -> not in results
1008 * J D subdir/deleted_file
1009 * K M subdir/modified_file
1010 * Q U subdir/new_file
1011 * L - subdir.txt (UNMODIFIED) -> not in results
1013 * Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
1017 void test_diff_workdir__larger_hunks(void)
1019 const char *a_commit
= "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
1020 const char *b_commit
= "7a9e0b02e63179929fed24f0a3e0f19168114d10";
1022 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1023 size_t i
, d
, num_d
, h
, num_h
, l
, num_l
;
1025 g_repo
= cl_git_sandbox_init("diff");
1027 cl_assert((a
= resolve_commit_oid_to_tree(g_repo
, a_commit
)) != NULL
);
1028 cl_assert((b
= resolve_commit_oid_to_tree(g_repo
, b_commit
)) != NULL
);
1030 opts
.context_lines
= 1;
1031 opts
.interhunk_lines
= 0;
1033 for (i
= 0; i
<= 2; ++i
) {
1034 git_diff
*diff
= NULL
;
1036 const git_diff_hunk
*hunk
;
1037 const git_diff_line
*line
;
1039 /* okay, this is a bit silly, but oh well */
1042 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1045 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, a
, &opts
));
1048 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, b
, &opts
));
1052 num_d
= git_diff_num_deltas(diff
);
1053 cl_assert_equal_i(2, (int)num_d
);
1055 for (d
= 0; d
< num_d
; ++d
) {
1056 cl_git_pass(git_patch_from_diff(&patch
, diff
, d
));
1059 num_h
= git_patch_num_hunks(patch
);
1060 for (h
= 0; h
< num_h
; h
++) {
1061 cl_git_pass(git_patch_get_hunk(&hunk
, &num_l
, patch
, h
));
1063 for (l
= 0; l
< num_l
; ++l
) {
1065 git_patch_get_line_in_hunk(&line
, patch
, h
, l
));
1069 /* confirm fail after the last item */
1071 git_patch_get_line_in_hunk(&line
, patch
, h
, num_l
));
1074 /* confirm fail after the last item */
1075 cl_git_fail(git_patch_get_hunk(&hunk
, &num_l
, patch
, num_h
));
1077 git_patch_free(patch
);
1080 git_diff_free(diff
);
1087 /* Set up a test that exercises this code. The easiest test using existing
1088 * test data is probably to create a sandbox of submod2 and then run a
1089 * git_diff_tree_to_workdir against tree
1090 * 873585b94bdeabccea991ea5e3ec1a277895b698. As for what you should actually
1091 * test, you can start by just checking that the number of lines of diff
1092 * content matches the actual output of git diff. That will at least
1093 * demonstrate that the submodule content is being used to generate somewhat
1094 * comparable outputs. It is a test that would fail without this code and
1095 * will succeed with it.
1098 #include "../submodule/submodule_helpers.h"
1100 void test_diff_workdir__submodules(void)
1102 const char *a_commit
= "873585b94bdeabccea991ea5e3ec1a277895b698";
1104 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1105 git_diff
*diff
= NULL
;
1108 g_repo
= setup_fixture_submod2();
1110 a
= resolve_commit_oid_to_tree(g_repo
, a_commit
);
1113 GIT_DIFF_INCLUDE_UNTRACKED
|
1114 GIT_DIFF_INCLUDE_IGNORED
|
1115 GIT_DIFF_RECURSE_UNTRACKED_DIRS
|
1116 GIT_DIFF_SHOW_UNTRACKED_CONTENT
;
1118 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, a
, &opts
));
1120 /* diff_print(stderr, diff); */
1122 /* essentially doing: git diff 873585b94bdeabccea991ea5e3ec1a277895b698 */
1124 memset(&exp
, 0, sizeof(exp
));
1126 cl_git_pass(git_diff_foreach(
1127 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1129 /* so "git diff 873585" returns:
1131 * A just_a_dir/contents
1133 * A sm_added_and_uncommited
1136 * A sm_changed_index
1137 * A sm_changed_untracked_file
1138 * M sm_missing_commits
1140 * which is a little deceptive because of the difference between the
1141 * "git diff <treeish>" results from "git_diff_tree_to_workdir". The
1142 * only significant difference is that those Added items will show up
1143 * as Untracked items in the pure libgit2 diff.
1145 * Then add in the two extra untracked items "not" and "not-submodule"
1146 * to get the 12 files reported here.
1149 cl_assert_equal_i(12, exp
.files
);
1151 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1152 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_DELETED
]);
1153 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1154 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
1155 cl_assert_equal_i(10, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1157 /* the following numbers match "git diff 873585" exactly */
1159 cl_assert_equal_i(9, exp
.hunks
);
1161 cl_assert_equal_i(33, exp
.lines
);
1162 cl_assert_equal_i(2, exp
.line_ctxt
);
1163 cl_assert_equal_i(30, exp
.line_adds
);
1164 cl_assert_equal_i(1, exp
.line_dels
);
1166 git_diff_free(diff
);
1170 void test_diff_workdir__cannot_diff_against_a_bare_repository(void)
1172 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1173 git_diff
*diff
= NULL
;
1176 g_repo
= cl_git_sandbox_init("testrepo.git");
1179 GIT_EBAREREPO
, git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1181 cl_git_pass(git_repository_head_tree(&tree
, g_repo
));
1184 GIT_EBAREREPO
, git_diff_tree_to_workdir(&diff
, g_repo
, tree
, &opts
));
1186 git_tree_free(tree
);
1189 void test_diff_workdir__to_null_tree(void)
1193 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1195 opts
.flags
= GIT_DIFF_INCLUDE_UNTRACKED
|
1196 GIT_DIFF_RECURSE_UNTRACKED_DIRS
;
1198 g_repo
= cl_git_sandbox_init("status");
1200 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1202 memset(&exp
, 0, sizeof(exp
));
1204 cl_git_pass(git_diff_foreach(
1205 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1207 cl_assert_equal_i(exp
.files
, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1209 git_diff_free(diff
);
1212 void test_diff_workdir__checks_options_version(void)
1215 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1216 const git_error
*err
;
1218 g_repo
= cl_git_sandbox_init("status");
1221 cl_git_fail(git_diff_tree_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1222 err
= giterr_last();
1223 cl_assert_equal_i(GITERR_INVALID
, err
->klass
);
1226 opts
.version
= 1024;
1227 cl_git_fail(git_diff_tree_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1228 err
= giterr_last();
1229 cl_assert_equal_i(GITERR_INVALID
, err
->klass
);
1232 void test_diff_workdir__can_diff_empty_file(void)
1236 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1240 g_repo
= cl_git_sandbox_init("attr_index");
1242 tree
= resolve_commit_oid_to_tree(g_repo
, "3812cfef3661"); /* HEAD */
1244 /* baseline - make sure there are no outstanding diffs */
1246 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, tree
, &opts
));
1247 cl_assert_equal_i(2, (int)git_diff_num_deltas(diff
));
1248 git_diff_free(diff
);
1250 /* empty contents of file */
1252 cl_git_rewritefile("attr_index/README.txt", "");
1253 cl_git_pass(git_path_lstat("attr_index/README.txt", &st
));
1254 cl_assert_equal_i(0, (int)st
.st_size
);
1256 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, tree
, &opts
));
1257 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff
));
1258 /* diffs are: .gitattributes, README.txt, sub/sub/.gitattributes */
1259 cl_git_pass(git_patch_from_diff(&patch
, diff
, 1));
1260 git_patch_free(patch
);
1261 git_diff_free(diff
);
1263 /* remove a file altogether */
1265 cl_git_pass(p_unlink("attr_index/README.txt"));
1266 cl_assert(!git_path_exists("attr_index/README.txt"));
1268 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, tree
, &opts
));
1269 cl_assert_equal_i(3, (int)git_diff_num_deltas(diff
));
1270 cl_git_pass(git_patch_from_diff(&patch
, diff
, 1));
1271 git_patch_free(patch
);
1272 git_diff_free(diff
);
1274 git_tree_free(tree
);
1277 void test_diff_workdir__to_index_issue_1397(void)
1279 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1280 git_diff
*diff
= NULL
;
1283 g_repo
= cl_git_sandbox_init("issue_1397");
1285 cl_repo_set_bool(g_repo
, "core.autocrlf", true);
1287 opts
.context_lines
= 3;
1288 opts
.interhunk_lines
= 1;
1290 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1292 memset(&exp
, 0, sizeof(exp
));
1293 cl_git_pass(git_diff_foreach(
1294 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1296 cl_assert_equal_i(0, exp
.files
);
1297 cl_assert_equal_i(0, exp
.hunks
);
1298 cl_assert_equal_i(0, exp
.lines
);
1300 git_diff_free(diff
);
1303 cl_git_rewritefile("issue_1397/crlf_file.txt",
1304 "first line\r\nsecond line modified\r\nboth with crlf");
1306 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1308 memset(&exp
, 0, sizeof(exp
));
1309 cl_git_pass(git_diff_foreach(
1310 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1312 cl_assert_equal_i(1, exp
.files
);
1313 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1315 cl_assert_equal_i(1, exp
.hunks
);
1317 cl_assert_equal_i(5, exp
.lines
);
1318 cl_assert_equal_i(3, exp
.line_ctxt
);
1319 cl_assert_equal_i(1, exp
.line_adds
);
1320 cl_assert_equal_i(1, exp
.line_dels
);
1322 git_diff_free(diff
);
1325 void test_diff_workdir__to_tree_issue_1397(void)
1327 const char *a_commit
= "7f483a738"; /* the current HEAD */
1329 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1330 git_diff
*diff
= NULL
;
1331 git_diff
*diff2
= NULL
;
1334 g_repo
= cl_git_sandbox_init("issue_1397");
1336 cl_repo_set_bool(g_repo
, "core.autocrlf", true);
1338 a
= resolve_commit_oid_to_tree(g_repo
, a_commit
);
1340 opts
.context_lines
= 3;
1341 opts
.interhunk_lines
= 1;
1343 cl_git_pass(git_diff_tree_to_workdir(&diff
, g_repo
, a
, &opts
));
1345 memset(&exp
, 0, sizeof(exp
));
1346 cl_git_pass(git_diff_foreach(
1347 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1349 cl_assert_equal_i(0, exp
.files
);
1350 cl_assert_equal_i(0, exp
.hunks
);
1351 cl_assert_equal_i(0, exp
.lines
);
1353 git_diff_free(diff
);
1356 cl_git_pass(git_diff_tree_to_index(&diff
, g_repo
, a
, NULL
, &opts
));
1357 cl_git_pass(git_diff_index_to_workdir(&diff2
, g_repo
, NULL
, &opts
));
1358 cl_git_pass(git_diff_merge(diff
, diff2
));
1359 git_diff_free(diff2
);
1361 memset(&exp
, 0, sizeof(exp
));
1362 cl_git_pass(git_diff_foreach(
1363 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1365 cl_assert_equal_i(0, exp
.files
);
1366 cl_assert_equal_i(0, exp
.hunks
);
1367 cl_assert_equal_i(0, exp
.lines
);
1369 git_diff_free(diff
);
1373 void test_diff_workdir__untracked_directory_scenarios(void)
1375 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1376 git_diff
*diff
= NULL
;
1378 char *pathspec
= NULL
;
1379 static const char *files0
[] = {
1380 "subdir/deleted_file",
1381 "subdir/modified_file",
1385 static const char *files1
[] = {
1386 "subdir/deleted_file",
1387 "subdir/directory/",
1388 "subdir/modified_file",
1392 static const char *files2
[] = {
1393 "subdir/deleted_file",
1394 "subdir/directory/more/notignored",
1395 "subdir/modified_file",
1400 g_repo
= cl_git_sandbox_init("status");
1401 cl_git_mkfile("status/.gitignore", "ignored\n");
1403 opts
.context_lines
= 3;
1404 opts
.interhunk_lines
= 1;
1405 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
;
1406 opts
.pathspec
.strings
= &pathspec
;
1407 opts
.pathspec
.count
= 1;
1408 pathspec
= "subdir";
1410 /* baseline for "subdir" pathspec */
1412 memset(&exp
, 0, sizeof(exp
));
1415 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1417 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1419 cl_assert_equal_i(3, exp
.files
);
1420 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1421 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1422 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1423 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
1424 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1426 git_diff_free(diff
);
1428 /* empty directory */
1430 cl_git_pass(p_mkdir("status/subdir/directory", 0777));
1432 memset(&exp
, 0, sizeof(exp
));
1435 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1437 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1439 cl_assert_equal_i(4, exp
.files
);
1440 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1441 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1442 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1443 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1444 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1446 git_diff_free(diff
);
1448 /* empty directory in empty directory */
1450 cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777));
1452 memset(&exp
, 0, sizeof(exp
));
1455 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1457 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1459 cl_assert_equal_i(4, exp
.files
);
1460 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1461 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1462 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1463 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1464 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1466 git_diff_free(diff
);
1468 /* directory with only ignored files */
1470 cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
1471 cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n");
1473 cl_git_pass(p_mkdir("status/subdir/directory/another", 0777));
1474 cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n");
1476 memset(&exp
, 0, sizeof(exp
));
1479 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1481 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1483 cl_assert_equal_i(4, exp
.files
);
1484 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1485 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1486 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1487 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1488 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1490 git_diff_free(diff
);
1492 /* directory with ignored directory (contents irrelevant) */
1494 cl_git_pass(p_mkdir("status/subdir/directory/more", 0777));
1495 cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777));
1496 cl_git_mkfile("status/subdir/directory/more/ignored/notignored",
1497 "inside ignored dir\n");
1499 memset(&exp
, 0, sizeof(exp
));
1502 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1504 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1506 cl_assert_equal_i(4, exp
.files
);
1507 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1508 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1509 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1510 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1511 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1513 git_diff_free(diff
);
1515 /* quick version avoids directory scan */
1517 opts
.flags
= opts
.flags
| GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS
;
1519 memset(&exp
, 0, sizeof(exp
));
1522 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1524 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1526 cl_assert_equal_i(4, exp
.files
);
1527 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1528 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1529 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1530 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
1531 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1533 git_diff_free(diff
);
1535 /* directory with nested non-ignored content */
1537 opts
.flags
= opts
.flags
& ~GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS
;
1539 cl_git_mkfile("status/subdir/directory/more/notignored",
1540 "not ignored deep under untracked\n");
1542 memset(&exp
, 0, sizeof(exp
));
1545 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1547 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1549 cl_assert_equal_i(4, exp
.files
);
1550 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1551 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1552 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1553 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
1554 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1556 git_diff_free(diff
);
1558 /* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
1560 opts
.flags
= opts
.flags
& ~GIT_DIFF_INCLUDE_IGNORED
;
1561 opts
.flags
= opts
.flags
| GIT_DIFF_RECURSE_UNTRACKED_DIRS
;
1563 memset(&exp
, 0, sizeof(exp
));
1566 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1568 cl_git_pass(git_diff_foreach(diff
, diff_file_cb
, NULL
, NULL
, NULL
, &exp
));
1570 cl_assert_equal_i(4, exp
.files
);
1571 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1572 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_DELETED
]);
1573 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1574 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_IGNORED
]);
1575 cl_assert_equal_i(2, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1577 git_diff_free(diff
);
1581 void test_diff_workdir__untracked_directory_comes_last(void)
1583 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1584 git_diff
*diff
= NULL
;
1586 g_repo
= cl_git_sandbox_init("renames");
1588 cl_git_mkfile("renames/.gitignore", "*.ign\n");
1589 cl_git_pass(p_mkdir("renames/zzz_untracked", 0777));
1590 cl_git_mkfile("renames/zzz_untracked/an.ign", "ignore me please");
1591 cl_git_mkfile("renames/zzz_untracked/skip.ign", "ignore me really");
1592 cl_git_mkfile("renames/zzz_untracked/test.ign", "ignore me now");
1594 opts
.context_lines
= 3;
1595 opts
.interhunk_lines
= 1;
1596 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
;
1598 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1600 cl_assert(diff
!= NULL
);
1602 git_diff_free(diff
);
1605 void test_diff_workdir__untracked_with_bom(void)
1607 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1608 git_diff
*diff
= NULL
;
1609 const git_diff_delta
*delta
;
1611 g_repo
= cl_git_sandbox_init("empty_standard_repo");
1612 cl_repo_set_bool(g_repo
, "core.autocrlf", true);
1614 cl_git_write2file("empty_standard_repo/bom.txt",
1615 "\xFF\xFE\x31\x00\x32\x00\x33\x00\x34\x00", 10, O_WRONLY
|O_CREAT
, 0664);
1618 GIT_DIFF_INCLUDE_UNTRACKED
| GIT_DIFF_SHOW_UNTRACKED_CONTENT
;
1620 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1622 cl_assert_equal_i(1, git_diff_num_deltas(diff
));
1623 cl_assert((delta
= git_diff_get_delta(diff
, 0)) != NULL
);
1624 cl_assert_equal_i(GIT_DELTA_UNTRACKED
, delta
->status
);
1626 /* not known at this point
1627 * cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
1630 git_diff_free(diff
);
1633 void test_diff_workdir__patience_diff(void)
1636 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1637 git_diff
*diff
= NULL
;
1638 git_patch
*patch
= NULL
;
1639 git_buf buf
= GIT_BUF_INIT
;
1640 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";
1641 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";
1643 g_repo
= cl_git_sandbox_init("empty_standard_repo");
1644 cl_repo_set_bool(g_repo
, "core.autocrlf", true);
1645 cl_git_pass(git_repository_index(&index
, g_repo
));
1648 "empty_standard_repo/test.txt",
1649 "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");
1650 cl_git_pass(git_index_add_bypath(index
, "test.txt"));
1651 cl_repo_commit_from_index(NULL
, g_repo
, NULL
, 1372350000, "Base");
1652 git_index_free(index
);
1655 "empty_standard_repo/test.txt",
1656 "When I wrote this\nI did not know\nI did not know\nhow to create\na patience diff\nanother problem\na minimal diff\n");
1658 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1659 cl_assert_equal_i(1, git_diff_num_deltas(diff
));
1660 cl_git_pass(git_patch_from_diff(&patch
, diff
, 0));
1661 cl_git_pass(git_patch_to_buf(&buf
, patch
));
1663 cl_assert_equal_s(expected_normal
, buf
.ptr
);
1664 git_buf_clear(&buf
);
1665 git_patch_free(patch
);
1666 git_diff_free(diff
);
1668 opts
.flags
|= GIT_DIFF_PATIENCE
;
1670 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1671 cl_assert_equal_i(1, git_diff_num_deltas(diff
));
1672 cl_git_pass(git_patch_from_diff(&patch
, diff
, 0));
1673 cl_git_pass(git_patch_to_buf(&buf
, patch
));
1675 cl_assert_equal_s(expected_patience
, buf
.ptr
);
1676 git_buf_clear(&buf
);
1679 git_patch_free(patch
);
1680 git_diff_free(diff
);
1683 void test_diff_workdir__with_stale_index(void)
1685 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1686 git_diff
*diff
= NULL
;
1687 git_index
*idx
= NULL
;
1690 g_repo
= cl_git_sandbox_init("status");
1691 cl_git_pass(git_repository_index(&idx
, g_repo
));
1693 /* make the in-memory index invalid */
1697 cl_git_pass(git_repository_open(&r2
, "status"));
1698 cl_git_pass(git_repository_index(&idx2
, r2
));
1699 cl_git_pass(git_index_add_bypath(idx2
, "new_file"));
1700 cl_git_pass(git_index_add_bypath(idx2
, "subdir/new_file"));
1701 cl_git_pass(git_index_remove_bypath(idx2
, "staged_new_file"));
1702 cl_git_pass(git_index_remove_bypath(idx2
, "staged_changes_file_deleted"));
1703 cl_git_pass(git_index_write(idx2
));
1704 git_index_free(idx2
);
1705 git_repository_free(r2
);
1708 opts
.context_lines
= 3;
1709 opts
.interhunk_lines
= 1;
1710 opts
.flags
|= GIT_DIFF_INCLUDE_UNTRACKED
| GIT_DIFF_INCLUDE_UNMODIFIED
;
1712 /* first try with index pointer which should prevent reload */
1714 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, idx
, &opts
));
1716 memset(&exp
, 0, sizeof(exp
));
1718 cl_git_pass(git_diff_foreach(
1719 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1721 cl_assert_equal_i(17, exp
.files
);
1722 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1723 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
1724 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1725 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1726 cl_assert_equal_i(5, exp
.file_status
[GIT_DELTA_UNMODIFIED
]);
1728 git_diff_free(diff
);
1730 /* now let's try without the index pointer which should trigger reload */
1732 /* two files that were UNTRACKED should have become UNMODIFIED */
1733 /* one file that was UNMODIFIED should now have become UNTRACKED */
1734 /* one file that was DELETED should now be gone completely */
1736 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
1738 memset(&exp
, 0, sizeof(exp
));
1740 cl_git_pass(git_diff_foreach(
1741 diff
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1743 git_diff_free(diff
);
1745 cl_assert_equal_i(16, exp
.files
);
1746 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1747 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_DELETED
]);
1748 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1749 cl_assert_equal_i(3, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1750 cl_assert_equal_i(6, exp
.file_status
[GIT_DELTA_UNMODIFIED
]);
1752 git_index_free(idx
);
1755 static int touch_file(void *payload
, git_buf
*path
)
1758 struct timeval times
[2];
1760 GIT_UNUSED(payload
);
1761 if (git_path_isdir(path
->ptr
))
1764 cl_must_pass(p_stat(path
->ptr
, &st
));
1766 times
[0].tv_sec
= st
.st_mtime
+ 3;
1767 times
[0].tv_usec
= 0;
1768 times
[1].tv_sec
= st
.st_mtime
+ 3;
1769 times
[1].tv_usec
= 0;
1771 cl_must_pass(p_utimes(path
->ptr
, times
));
1775 static void basic_diff_status(git_diff
**out
, const git_diff_options
*opts
)
1779 cl_git_pass(git_diff_index_to_workdir(out
, g_repo
, NULL
, opts
));
1781 memset(&exp
, 0, sizeof(exp
));
1783 cl_git_pass(git_diff_foreach(
1784 *out
, diff_file_cb
, diff_binary_cb
, diff_hunk_cb
, diff_line_cb
, &exp
));
1786 cl_assert_equal_i(13, exp
.files
);
1787 cl_assert_equal_i(0, exp
.file_status
[GIT_DELTA_ADDED
]);
1788 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_DELETED
]);
1789 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_MODIFIED
]);
1790 cl_assert_equal_i(1, exp
.file_status
[GIT_DELTA_IGNORED
]);
1791 cl_assert_equal_i(4, exp
.file_status
[GIT_DELTA_UNTRACKED
]);
1794 void test_diff_workdir__can_update_index(void)
1796 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
1797 git_diff
*diff
= NULL
;
1798 git_diff_perfdata perf
= GIT_DIFF_PERFDATA_INIT
;
1801 g_repo
= cl_git_sandbox_init("status");
1803 /* touch all the files so stat times are different */
1805 git_buf path
= GIT_BUF_INIT
;
1806 cl_git_pass(git_buf_sets(&path
, "status"));
1807 cl_git_pass(git_path_direach(&path
, 0, touch_file
, NULL
));
1808 git_buf_free(&path
);
1811 opts
.flags
|= GIT_DIFF_INCLUDE_IGNORED
| GIT_DIFF_INCLUDE_UNTRACKED
;
1813 basic_diff_status(&diff
, &opts
);
1815 cl_git_pass(git_diff_get_perfdata(&perf
, diff
));
1816 cl_assert_equal_sz(13 + 3, perf
.stat_calls
);
1817 cl_assert_equal_sz(5, perf
.oid_calculations
);
1819 git_diff_free(diff
);
1821 /* now allow diff to update stat cache */
1822 opts
.flags
|= GIT_DIFF_UPDATE_INDEX
;
1824 /* advance a tick for the index so we don't re-calculate racily-clean entries */
1825 cl_git_pass(git_repository_index__weakptr(&index
, g_repo
));
1828 basic_diff_status(&diff
, &opts
);
1830 cl_git_pass(git_diff_get_perfdata(&perf
, diff
));
1831 cl_assert_equal_sz(13 + 3, perf
.stat_calls
);
1832 cl_assert_equal_sz(5, perf
.oid_calculations
);
1834 git_diff_free(diff
);
1836 /* now if we do it again, we should see fewer OID calculations */
1838 /* tick again as the index updating from the previous diff might have reset the timestamp */
1840 basic_diff_status(&diff
, &opts
);
1842 cl_git_pass(git_diff_get_perfdata(&perf
, diff
));
1843 cl_assert_equal_sz(13 + 3, perf
.stat_calls
);
1844 cl_assert_equal_sz(0, perf
.oid_calculations
);
1846 git_diff_free(diff
);
1849 #define STR7 "0123456"
1850 #define STR8 "01234567"
1851 #define STR40 STR8 STR8 STR8 STR8 STR8
1852 #define STR200 STR40 STR40 STR40 STR40 STR40
1853 #define STR999Z STR200 STR200 STR200 STR200 STR40 STR40 STR40 STR40 \
1854 STR8 STR8 STR8 STR8 STR7 "\0"
1855 #define STR1000 STR200 STR200 STR200 STR200 STR200
1856 #define STR3999Z STR1000 STR1000 STR1000 STR999Z
1857 #define STR4000 STR1000 STR1000 STR1000 STR1000
1859 static void assert_delta_binary(git_diff
*diff
, size_t idx
, int is_binary
)
1862 const git_diff_delta
*delta
;
1864 cl_git_pass(git_patch_from_diff(&patch
, diff
, idx
));
1865 delta
= git_patch_get_delta(patch
);
1866 cl_assert_equal_b((delta
->flags
& GIT_DIFF_FLAG_BINARY
), is_binary
);
1867 git_patch_free(patch
);
1870 void test_diff_workdir__binary_detection(void)
1873 git_diff
*diff
= NULL
;
1874 git_buf b
= GIT_BUF_INIT
;
1876 git_buf data
[10] = {
1877 { "1234567890", 0, 0 }, /* 0 - all ascii text control */
1878 { "\xC3\x85\xC3\xBC\xE2\x80\xA0\x48\xC3\xB8\xCF\x80\xCE\xA9", 0, 0 }, /* 1 - UTF-8 multibyte text */
1879 { "\xEF\xBB\xBF\xC3\x9C\xE2\xA4\x92\xC6\x92\x38\xC2\xA3\xE2\x82\xAC", 0, 0 }, /* 2 - UTF-8 with BOM */
1880 { STR999Z
, 0, 1000 }, /* 3 - ASCII with NUL at 1000 */
1881 { STR3999Z
, 0, 4000 }, /* 4 - ASCII with NUL at 4000 */
1882 { STR4000 STR3999Z
"x", 0, 8001 }, /* 5 - ASCII with NUL at 8000 */
1883 { STR4000 STR4000
"\0", 0, 8001 }, /* 6 - ASCII with NUL at 8001 */
1884 { "\x00\xDC\x00\x6E\x21\x39\xFE\x0E\x00\x63\x00\xF8"
1885 "\x00\x64\x00\x65\x20\x48", 0, 18 }, /* 7 - UTF-16 text */
1886 { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
1887 "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d",
1888 0, 26 }, /* 8 - All non-printable characters (no NUL) */
1889 { "Hello \x01\x02\x03\x04\x05\x06 World!\x01\x02\x03\x04"
1890 "\x05\x06\x07", 0, 26 }, /* 9 - 50-50 non-printable (no NUL) */
1893 g_repo
= cl_git_sandbox_init("empty_standard_repo");
1894 cl_git_pass(git_repository_index(&idx
, g_repo
));
1896 /* We start with ASCII in index and test data in workdir,
1897 * then we will try with test data in index and ASCII in workdir.
1900 cl_git_pass(git_buf_sets(&b
, "empty_standard_repo/0"));
1901 for (i
= 0; i
< 10; ++i
) {
1902 b
.ptr
[b
.size
- 1] = '0' + i
;
1903 cl_git_mkfile(b
.ptr
, "baseline");
1904 cl_git_pass(git_index_add_bypath(idx
, &b
.ptr
[b
.size
- 1]));
1906 if (data
[i
].size
== 0)
1907 data
[i
].size
= strlen(data
[i
].ptr
);
1909 b
.ptr
, data
[i
].ptr
, data
[i
].size
, O_WRONLY
|O_TRUNC
, 0664);
1911 git_index_write(idx
);
1913 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
1915 cl_assert_equal_i(10, git_diff_num_deltas(diff
));
1917 /* using diff binary detection (i.e. looking for NUL byte) */
1918 assert_delta_binary(diff
, 0, false);
1919 assert_delta_binary(diff
, 1, false);
1920 assert_delta_binary(diff
, 2, false);
1921 assert_delta_binary(diff
, 3, true);
1922 assert_delta_binary(diff
, 4, true);
1923 assert_delta_binary(diff
, 5, true);
1924 assert_delta_binary(diff
, 6, false);
1925 assert_delta_binary(diff
, 7, true);
1926 assert_delta_binary(diff
, 8, false);
1927 assert_delta_binary(diff
, 9, false);
1928 /* The above have been checked to match command-line Git */
1930 git_diff_free(diff
);
1932 cl_git_pass(git_buf_sets(&b
, "empty_standard_repo/0"));
1933 for (i
= 0; i
< 10; ++i
) {
1934 b
.ptr
[b
.size
- 1] = '0' + i
;
1935 cl_git_pass(git_index_add_bypath(idx
, &b
.ptr
[b
.size
- 1]));
1937 cl_git_write2file(b
.ptr
, "baseline\n", 9, O_WRONLY
|O_TRUNC
, 0664);
1939 git_index_write(idx
);
1941 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, NULL
));
1943 cl_assert_equal_i(10, git_diff_num_deltas(diff
));
1945 /* using diff binary detection (i.e. looking for NUL byte) */
1946 assert_delta_binary(diff
, 0, false);
1947 assert_delta_binary(diff
, 1, false);
1948 assert_delta_binary(diff
, 2, false);
1949 assert_delta_binary(diff
, 3, true);
1950 assert_delta_binary(diff
, 4, true);
1951 assert_delta_binary(diff
, 5, true);
1952 assert_delta_binary(diff
, 6, false);
1953 assert_delta_binary(diff
, 7, true);
1954 assert_delta_binary(diff
, 8, false);
1955 assert_delta_binary(diff
, 9, false);
1957 git_diff_free(diff
);
1959 git_index_free(idx
);
1963 void test_diff_workdir__to_index_conflicted(void) {
1964 const char *a_commit
= "26a125ee1bf"; /* the current HEAD */
1965 git_index_entry ancestor
= {{0}}, ours
= {{0}}, theirs
= {{0}};
1968 git_diff
*diff1
, *diff2
;
1969 const git_diff_delta
*delta
;
1971 g_repo
= cl_git_sandbox_init("status");
1972 a
= resolve_commit_oid_to_tree(g_repo
, a_commit
);
1974 cl_git_pass(git_repository_index(&index
, g_repo
));
1976 ancestor
.path
= ours
.path
= theirs
.path
= "_file";
1977 ancestor
.mode
= ours
.mode
= theirs
.mode
= 0100644;
1978 git_oid_fromstr(&ancestor
.id
, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
1979 git_oid_fromstr(&ours
.id
, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
1980 git_oid_fromstr(&theirs
.id
, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
1981 cl_git_pass(git_index_conflict_add(index
, &ancestor
, &ours
, &theirs
));
1983 cl_git_pass(git_diff_tree_to_index(&diff1
, g_repo
, a
, index
, NULL
));
1984 cl_git_pass(git_diff_index_to_workdir(&diff2
, g_repo
, index
, NULL
));
1985 cl_git_pass(git_diff_merge(diff1
, diff2
));
1987 cl_assert_equal_i(git_diff_num_deltas(diff1
), 12);
1988 delta
= git_diff_get_delta(diff1
, 0);
1989 cl_assert_equal_s(delta
->old_file
.path
, "_file");
1990 cl_assert_equal_i(delta
->nfiles
, 1);
1991 cl_assert_equal_i(delta
->status
, GIT_DELTA_CONFLICTED
);
1993 git_diff_free(diff2
);
1994 git_diff_free(diff1
);
1995 git_index_free(index
);
1999 void test_diff_workdir__only_writes_index_when_necessary(void)
2002 git_diff_options opts
= GIT_DIFF_OPTIONS_INIT
;
2003 git_diff
*diff
= NULL
;
2004 git_reference
*head
;
2005 git_object
*head_object
;
2006 git_oid initial
, first
, second
;
2007 git_buf path
= GIT_BUF_INIT
;
2009 struct timeval times
[2];
2011 opts
.flags
|= GIT_DIFF_INCLUDE_UNTRACKED
| GIT_DIFF_UPDATE_INDEX
;
2013 g_repo
= cl_git_sandbox_init("status");
2015 cl_git_pass(git_repository_index(&index
, g_repo
));
2016 cl_git_pass(git_repository_head(&head
, g_repo
));
2017 cl_git_pass(git_reference_peel(&head_object
, head
, GIT_OBJ_COMMIT
));
2019 cl_git_pass(git_reset(g_repo
, head_object
, GIT_RESET_HARD
, NULL
));
2021 git_oid_cpy(&initial
, git_index_checksum(index
));
2023 /* update the index timestamp to avoid raciness */
2024 cl_must_pass(p_stat("status/.git/index", &st
));
2026 times
[0].tv_sec
= st
.st_mtime
+ 5;
2027 times
[0].tv_usec
= 0;
2028 times
[1].tv_sec
= st
.st_mtime
+ 5;
2029 times
[1].tv_usec
= 0;
2031 cl_must_pass(p_utimes("status/.git/index", times
));
2033 /* ensure diff doesn't touch the index */
2034 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
2035 git_diff_free(diff
);
2037 git_oid_cpy(&first
, git_index_checksum(index
));
2038 cl_assert(!git_oid_equal(&initial
, &first
));
2040 /* touch all the files so stat times are different */
2041 cl_git_pass(git_buf_sets(&path
, "status"));
2042 cl_git_pass(git_path_direach(&path
, 0, touch_file
, NULL
));
2044 cl_git_pass(git_diff_index_to_workdir(&diff
, g_repo
, NULL
, &opts
));
2045 git_diff_free(diff
);
2047 /* ensure the second diff did update the index */
2048 git_oid_cpy(&second
, git_index_checksum(index
));
2049 cl_assert(!git_oid_equal(&first
, &second
));
2051 git_buf_free(&path
);
2052 git_object_free(head_object
);
2053 git_reference_free(head
);
2054 git_index_free(index
);