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