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