]> git.proxmox.com Git - libgit2.git/blob - tests/status/renames.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / tests / status / renames.c
1 #include "clar_libgit2.h"
2 #include "path.h"
3 #include "posix.h"
4 #include "status_helpers.h"
5 #include "util.h"
6 #include "status.h"
7
8 static git_repository *g_repo = NULL;
9
10 void test_status_renames__initialize(void)
11 {
12 g_repo = cl_git_sandbox_init("renames");
13
14 cl_repo_set_bool(g_repo, "core.autocrlf", false);
15 }
16
17 void test_status_renames__cleanup(void)
18 {
19 cl_git_sandbox_cleanup();
20 }
21
22 static void _rename_helper(
23 git_repository *repo, const char *from, const char *to, const char *extra)
24 {
25 git_str oldpath = GIT_STR_INIT, newpath = GIT_STR_INIT;
26
27 cl_git_pass(git_str_joinpath(
28 &oldpath, git_repository_workdir(repo), from));
29 cl_git_pass(git_str_joinpath(
30 &newpath, git_repository_workdir(repo), to));
31
32 cl_git_pass(p_rename(oldpath.ptr, newpath.ptr));
33
34 if (extra)
35 cl_git_append2file(newpath.ptr, extra);
36
37 git_str_dispose(&oldpath);
38 git_str_dispose(&newpath);
39 }
40
41 #define rename_file(R,O,N) _rename_helper((R), (O), (N), NULL)
42 #define rename_and_edit_file(R,O,N) \
43 _rename_helper((R), (O), (N), "Added at the end to keep similarity!")
44
45 struct status_entry {
46 git_status_t status;
47 const char *oldname;
48 const char *newname;
49 };
50
51 static void check_status(
52 git_status_list *status_list,
53 struct status_entry *expected_list,
54 size_t expected_len)
55 {
56 const git_status_entry *actual;
57 const struct status_entry *expected;
58 const char *oldname, *newname;
59 size_t i, files_in_status = git_status_list_entrycount(status_list);
60
61 cl_assert_equal_sz(expected_len, files_in_status);
62
63 for (i = 0; i < expected_len; i++) {
64 actual = git_status_byindex(status_list, i);
65 expected = &expected_list[i];
66
67 oldname = actual->head_to_index ? actual->head_to_index->old_file.path :
68 actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL;
69
70 newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path :
71 actual->head_to_index ? actual->head_to_index->new_file.path : NULL;
72
73 cl_assert_equal_i_fmt(expected->status, actual->status, "%04x");
74
75 if (expected->oldname) {
76 cl_assert(oldname != NULL);
77 cl_assert_equal_s(oldname, expected->oldname);
78 } else {
79 cl_assert(oldname == NULL);
80 }
81
82 if (actual->status & (GIT_STATUS_INDEX_RENAMED|GIT_STATUS_WT_RENAMED)) {
83 if (expected->newname) {
84 cl_assert(newname != NULL);
85 cl_assert_equal_s(newname, expected->newname);
86 } else {
87 cl_assert(newname == NULL);
88 }
89 }
90 }
91 }
92
93 void test_status_renames__head2index_one(void)
94 {
95 git_index *index;
96 git_status_list *statuslist;
97 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
98 struct status_entry expected[] = {
99 { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "newname.txt" },
100 };
101
102 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
103
104 cl_git_pass(git_repository_index(&index, g_repo));
105
106 rename_file(g_repo, "ikeepsix.txt", "newname.txt");
107
108 cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
109 cl_git_pass(git_index_add_bypath(index, "newname.txt"));
110 cl_git_pass(git_index_write(index));
111
112 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
113 check_status(statuslist, expected, 1);
114 git_status_list_free(statuslist);
115
116 git_index_free(index);
117 }
118
119 void test_status_renames__head2index_two(void)
120 {
121 git_index *index;
122 git_status_list *statuslist;
123 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
124 struct status_entry expected[] = {
125 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
126 "sixserving.txt", "aaa.txt" },
127 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
128 "untimely.txt", "bbb.txt" },
129 { GIT_STATUS_INDEX_RENAMED, "songof7cities.txt", "ccc.txt" },
130 { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "ddd.txt" },
131 };
132
133 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
134
135 cl_git_pass(git_repository_index(&index, g_repo));
136
137 rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
138 rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
139 rename_file(g_repo, "songof7cities.txt", "ccc.txt");
140 rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
141
142 cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
143 cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
144 cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
145 cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
146 cl_git_pass(git_index_add_bypath(index, "ddd.txt"));
147 cl_git_pass(git_index_add_bypath(index, "aaa.txt"));
148 cl_git_pass(git_index_add_bypath(index, "ccc.txt"));
149 cl_git_pass(git_index_add_bypath(index, "bbb.txt"));
150 cl_git_pass(git_index_write(index));
151
152 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
153 check_status(statuslist, expected, 4);
154 git_status_list_free(statuslist);
155
156 git_index_free(index);
157 }
158
159 void test_status_renames__head2index_no_rename_from_rewrite(void)
160 {
161 git_index *index;
162 git_status_list *statuslist;
163 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
164 struct status_entry expected[] = {
165 { GIT_STATUS_INDEX_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
166 { GIT_STATUS_INDEX_MODIFIED, "sixserving.txt", "sixserving.txt" },
167 };
168
169 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
170
171 cl_git_pass(git_repository_index(&index, g_repo));
172
173 rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
174 rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
175 rename_file(g_repo, "_temp_.txt", "sixserving.txt");
176
177 cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
178 cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
179 cl_git_pass(git_index_write(index));
180
181 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
182 check_status(statuslist, expected, 2);
183 git_status_list_free(statuslist);
184
185 git_index_free(index);
186 }
187
188 void test_status_renames__head2index_rename_from_rewrite(void)
189 {
190 git_index *index;
191 git_status_list *statuslist;
192 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
193 struct status_entry expected[] = {
194 { GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "ikeepsix.txt" },
195 { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "sixserving.txt" },
196 };
197
198 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
199 opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
200
201 cl_git_pass(git_repository_index(&index, g_repo));
202
203 rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
204 rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
205 rename_file(g_repo, "_temp_.txt", "sixserving.txt");
206
207 cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
208 cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
209 cl_git_pass(git_index_write(index));
210
211 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
212 check_status(statuslist, expected, 2);
213 git_status_list_free(statuslist);
214
215 git_index_free(index);
216 }
217
218 void test_status_renames__index2workdir_one(void)
219 {
220 git_status_list *statuslist;
221 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
222 struct status_entry expected[] = {
223 { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname.txt" },
224 };
225
226 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
227 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
228
229 rename_file(g_repo, "ikeepsix.txt", "newname.txt");
230
231 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
232 check_status(statuslist, expected, 1);
233 git_status_list_free(statuslist);
234 }
235
236 void test_status_renames__index2workdir_two(void)
237 {
238 git_status_list *statuslist;
239 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
240 struct status_entry expected[] = {
241 { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
242 "sixserving.txt", "aaa.txt" },
243 { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
244 "untimely.txt", "bbb.txt" },
245 { GIT_STATUS_WT_RENAMED, "songof7cities.txt", "ccc.txt" },
246 { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ddd.txt" },
247 };
248
249 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
250 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
251
252 rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
253 rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
254 rename_file(g_repo, "songof7cities.txt", "ccc.txt");
255 rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
256
257 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
258 check_status(statuslist, expected, 4);
259 git_status_list_free(statuslist);
260 }
261
262 void test_status_renames__index2workdir_rename_from_rewrite(void)
263 {
264 git_index *index;
265 git_status_list *statuslist;
266 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
267 struct status_entry expected[] = {
268 { GIT_STATUS_WT_RENAMED, "sixserving.txt", "ikeepsix.txt" },
269 { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "sixserving.txt" },
270 };
271
272 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
273 opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
274
275 cl_git_pass(git_repository_index(&index, g_repo));
276
277 rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
278 rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
279 rename_file(g_repo, "_temp_.txt", "sixserving.txt");
280
281 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
282 check_status(statuslist, expected, 2);
283 git_status_list_free(statuslist);
284
285 git_index_free(index);
286 }
287
288 void test_status_renames__both_one(void)
289 {
290 git_index *index;
291 git_status_list *statuslist;
292 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
293 struct status_entry expected[] = {
294 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
295 "ikeepsix.txt", "newname-workdir.txt" },
296 };
297
298 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
299 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
300 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
301
302 cl_git_pass(git_repository_index(&index, g_repo));
303
304 rename_file(g_repo, "ikeepsix.txt", "newname-index.txt");
305
306 cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
307 cl_git_pass(git_index_add_bypath(index, "newname-index.txt"));
308 cl_git_pass(git_index_write(index));
309
310 rename_file(g_repo, "newname-index.txt", "newname-workdir.txt");
311
312 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
313 check_status(statuslist, expected, 1);
314 git_status_list_free(statuslist);
315
316 git_index_free(index);
317 }
318
319 void test_status_renames__both_two(void)
320 {
321 git_index *index;
322 git_status_list *statuslist;
323 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
324 struct status_entry expected[] = {
325 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
326 GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
327 "ikeepsix.txt", "ikeepsix-both.txt" },
328 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
329 "sixserving.txt", "sixserving-index.txt" },
330 { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
331 "songof7cities.txt", "songof7cities-workdir.txt" },
332 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
333 "untimely.txt", "untimely-both.txt" },
334 };
335
336 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
337 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
338 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
339
340 cl_git_pass(git_repository_index(&index, g_repo));
341
342 rename_and_edit_file(g_repo, "ikeepsix.txt", "ikeepsix-index.txt");
343 rename_and_edit_file(g_repo, "sixserving.txt", "sixserving-index.txt");
344 rename_file(g_repo, "untimely.txt", "untimely-index.txt");
345
346 cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
347 cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
348 cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
349 cl_git_pass(git_index_add_bypath(index, "ikeepsix-index.txt"));
350 cl_git_pass(git_index_add_bypath(index, "sixserving-index.txt"));
351 cl_git_pass(git_index_add_bypath(index, "untimely-index.txt"));
352 cl_git_pass(git_index_write(index));
353
354 rename_and_edit_file(g_repo, "ikeepsix-index.txt", "ikeepsix-both.txt");
355 rename_and_edit_file(g_repo, "songof7cities.txt", "songof7cities-workdir.txt");
356 rename_file(g_repo, "untimely-index.txt", "untimely-both.txt");
357
358 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
359 check_status(statuslist, expected, 4);
360 git_status_list_free(statuslist);
361
362 git_index_free(index);
363 }
364
365
366 void test_status_renames__both_rename_from_rewrite(void)
367 {
368 git_index *index;
369 git_status_list *statuslist;
370 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
371 struct status_entry expected[] = {
372 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
373 "songof7cities.txt", "ikeepsix.txt" },
374 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
375 "ikeepsix.txt", "sixserving.txt" },
376 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
377 "sixserving.txt", "songof7cities.txt" },
378 };
379
380 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
381 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
382 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
383 opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
384
385 cl_git_pass(git_repository_index(&index, g_repo));
386
387 rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
388 rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
389 rename_file(g_repo, "songof7cities.txt", "sixserving.txt");
390 rename_file(g_repo, "_temp_.txt", "songof7cities.txt");
391
392 cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
393 cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
394 cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
395 cl_git_pass(git_index_write(index));
396
397 rename_file(g_repo, "songof7cities.txt", "_temp_.txt");
398 rename_file(g_repo, "ikeepsix.txt", "songof7cities.txt");
399 rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
400 rename_file(g_repo, "_temp_.txt", "sixserving.txt");
401
402 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
403 check_status(statuslist, expected, 3);
404 git_status_list_free(statuslist);
405
406 git_index_free(index);
407 }
408
409 void test_status_renames__rewrites_only_for_renames(void)
410 {
411 git_index *index;
412 git_status_list *statuslist;
413 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
414 struct status_entry expected[] = {
415 { GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
416 };
417
418 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
419 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
420 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
421 opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
422
423 cl_git_pass(git_repository_index(&index, g_repo));
424
425 cl_git_rewritefile("renames/ikeepsix.txt",
426 "This is enough content for the file to be rewritten.\n" \
427 "This is enough content for the file to be rewritten.\n" \
428 "This is enough content for the file to be rewritten.\n" \
429 "This is enough content for the file to be rewritten.\n" \
430 "This is enough content for the file to be rewritten.\n" \
431 "This is enough content for the file to be rewritten.\n" \
432 "This is enough content for the file to be rewritten.\n" \
433 "This is enough content for the file to be rewritten.\n" \
434 "This is enough content for the file to be rewritten.\n" \
435 "This is enough content for the file to be rewritten.\n" \
436 "This is enough content for the file to be rewritten.\n" \
437 "This is enough content for the file to be rewritten.\n" \
438 "This is enough content for the file to be rewritten.\n" \
439 "This is enough content for the file to be rewritten.\n" \
440 "This is enough content for the file to be rewritten.\n" \
441 "This is enough content for the file to be rewritten.\n");
442
443 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
444 check_status(statuslist, expected, 1);
445 git_status_list_free(statuslist);
446
447 git_index_free(index);
448 }
449
450 void test_status_renames__both_casechange_one(void)
451 {
452 git_index *index;
453 git_status_list *statuslist;
454 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
455 int index_caps;
456 struct status_entry expected_icase[] = {
457 { GIT_STATUS_INDEX_RENAMED,
458 "ikeepsix.txt", "IKeepSix.txt" },
459 };
460 struct status_entry expected_case[] = {
461 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
462 "ikeepsix.txt", "IKEEPSIX.txt" },
463 };
464
465 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
466 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
467 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
468
469 cl_git_pass(git_repository_index(&index, g_repo));
470 index_caps = git_index_caps(index);
471
472 rename_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
473
474 cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
475 cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
476 cl_git_pass(git_index_write(index));
477
478 /* on a case-insensitive file system, this change won't matter.
479 * on a case-sensitive one, it will.
480 */
481 rename_file(g_repo, "IKeepSix.txt", "IKEEPSIX.txt");
482
483 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
484
485 check_status(statuslist, (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) ?
486 expected_icase : expected_case, 1);
487
488 git_status_list_free(statuslist);
489
490 git_index_free(index);
491 }
492
493 void test_status_renames__both_casechange_two(void)
494 {
495 git_index *index;
496 git_status_list *statuslist;
497 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
498 int index_caps;
499 struct status_entry expected_icase[] = {
500 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
501 GIT_STATUS_WT_MODIFIED,
502 "ikeepsix.txt", "IKeepSix.txt" },
503 { GIT_STATUS_INDEX_MODIFIED,
504 "sixserving.txt", "sixserving.txt" },
505 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED,
506 "songof7cities.txt", "songof7.txt" },
507 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
508 "untimely.txt", "untimeliest.txt" }
509 };
510 struct status_entry expected_case[] = {
511 { GIT_STATUS_INDEX_RENAMED |
512 GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED,
513 "songof7cities.txt", "SONGOF7.txt" },
514 { GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED,
515 "sixserving.txt", "SixServing.txt" },
516 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
517 GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
518 "ikeepsix.txt", "ikeepsix.txt" },
519 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
520 "untimely.txt", "untimeliest.txt" }
521 };
522
523 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
524 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
525 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
526
527 cl_git_pass(git_repository_index(&index, g_repo));
528 index_caps = git_index_caps(index);
529
530 rename_and_edit_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
531 rename_and_edit_file(g_repo, "sixserving.txt", "sixserving.txt");
532 rename_file(g_repo, "songof7cities.txt", "songof7.txt");
533 rename_file(g_repo, "untimely.txt", "untimelier.txt");
534
535 cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
536 cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
537 cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
538 cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
539 cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
540 cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
541 cl_git_pass(git_index_add_bypath(index, "songof7.txt"));
542 cl_git_pass(git_index_add_bypath(index, "untimelier.txt"));
543 cl_git_pass(git_index_write(index));
544
545 rename_and_edit_file(g_repo, "IKeepSix.txt", "ikeepsix.txt");
546 rename_file(g_repo, "sixserving.txt", "SixServing.txt");
547 rename_and_edit_file(g_repo, "songof7.txt", "SONGOF7.txt");
548 rename_file(g_repo, "untimelier.txt", "untimeliest.txt");
549
550 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
551
552 check_status(statuslist, (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) ?
553 expected_icase : expected_case, 4);
554
555 git_status_list_free(statuslist);
556
557 git_index_free(index);
558 }
559
560 void test_status_renames__zero_byte_file_does_not_fail(void)
561 {
562 git_status_list *statuslist;
563 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
564
565 struct status_entry expected[] = {
566 { GIT_STATUS_WT_DELETED, "ikeepsix.txt", "ikeepsix.txt" },
567 { GIT_STATUS_WT_NEW, "zerobyte.txt", "zerobyte.txt" },
568 };
569
570 opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES |
571 GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
572 GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR |
573 GIT_STATUS_OPT_INCLUDE_IGNORED |
574 GIT_STATUS_OPT_INCLUDE_UNTRACKED |
575 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
576 GIT_STATUS_SHOW_INDEX_AND_WORKDIR |
577 GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
578
579 p_unlink("renames/ikeepsix.txt");
580 cl_git_mkfile("renames/zerobyte.txt", "");
581
582 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
583 check_status(statuslist, expected, 2);
584 git_status_list_free(statuslist);
585 }
586
587 #ifdef GIT_USE_ICONV
588 static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
589 static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
590 #endif
591
592 /*
593 * Create a file in NFD (canonically decomposed) format. Ensure
594 * that when core.precomposeunicode is false that we return paths
595 * in NFD, but when core.precomposeunicode is true, then we
596 * return paths precomposed (in NFC).
597 */
598 void test_status_renames__precomposed_unicode_rename(void)
599 {
600 #ifdef GIT_USE_ICONV
601 git_status_list *statuslist;
602 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
603 struct status_entry expected0[] = {
604 { GIT_STATUS_WT_NEW, nfd, NULL },
605 { GIT_STATUS_WT_DELETED, "sixserving.txt", NULL },
606 };
607 struct status_entry expected1[] = {
608 { GIT_STATUS_WT_RENAMED, "sixserving.txt", nfd },
609 };
610 struct status_entry expected2[] = {
611 { GIT_STATUS_WT_DELETED, "sixserving.txt", NULL },
612 { GIT_STATUS_WT_NEW, nfc, NULL },
613 };
614 struct status_entry expected3[] = {
615 { GIT_STATUS_WT_RENAMED, "sixserving.txt", nfc },
616 };
617
618 rename_file(g_repo, "sixserving.txt", nfd);
619
620 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
621
622 cl_repo_set_bool(g_repo, "core.precomposeunicode", false);
623
624 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
625 check_status(statuslist, expected0, ARRAY_SIZE(expected0));
626 git_status_list_free(statuslist);
627
628 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
629
630 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
631 check_status(statuslist, expected1, ARRAY_SIZE(expected1));
632 git_status_list_free(statuslist);
633
634 cl_repo_set_bool(g_repo, "core.precomposeunicode", true);
635
636 opts.flags &= ~GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
637
638 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
639 check_status(statuslist, expected2, ARRAY_SIZE(expected2));
640 git_status_list_free(statuslist);
641
642 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
643
644 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
645 check_status(statuslist, expected3, ARRAY_SIZE(expected3));
646 git_status_list_free(statuslist);
647 #endif
648 }
649
650 void test_status_renames__precomposed_unicode_toggle_is_rename(void)
651 {
652 #ifdef GIT_USE_ICONV
653 git_status_list *statuslist;
654 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
655 struct status_entry expected0[] = {
656 { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", nfd },
657 };
658 struct status_entry expected1[] = {
659 { GIT_STATUS_WT_RENAMED, nfd, nfc },
660 };
661 struct status_entry expected2[] = {
662 { GIT_STATUS_INDEX_RENAMED, nfd, nfc },
663 };
664 struct status_entry expected3[] = {
665 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, nfd, nfd },
666 };
667
668 cl_repo_set_bool(g_repo, "core.precomposeunicode", false);
669 rename_file(g_repo, "ikeepsix.txt", nfd);
670
671 {
672 git_index *index;
673 cl_git_pass(git_repository_index(&index, g_repo));
674 cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
675 cl_git_pass(git_index_add_bypath(index, nfd));
676 cl_git_pass(git_index_write(index));
677 git_index_free(index);
678 }
679
680 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
681 GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
682 GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
683
684 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
685 check_status(statuslist, expected0, ARRAY_SIZE(expected0));
686 git_status_list_free(statuslist);
687
688 cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "commit nfd");
689
690 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
691 cl_assert_equal_sz(0, git_status_list_entrycount(statuslist));
692 git_status_list_free(statuslist);
693
694 cl_repo_set_bool(g_repo, "core.precomposeunicode", true);
695
696 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
697 check_status(statuslist, expected1, ARRAY_SIZE(expected1));
698 git_status_list_free(statuslist);
699
700 {
701 git_index *index;
702 cl_git_pass(git_repository_index(&index, g_repo));
703 cl_git_pass(git_index_remove_bypath(index, nfd));
704 cl_git_pass(git_index_add_bypath(index, nfc));
705 cl_git_pass(git_index_write(index));
706 git_index_free(index);
707 }
708
709 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
710 check_status(statuslist, expected2, ARRAY_SIZE(expected2));
711 git_status_list_free(statuslist);
712
713 cl_repo_set_bool(g_repo, "core.precomposeunicode", false);
714
715 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
716 check_status(statuslist, expected3, ARRAY_SIZE(expected3));
717 git_status_list_free(statuslist);
718 #endif
719 }
720
721 void test_status_renames__rename_threshold(void)
722 {
723 git_index *index;
724 git_status_list *statuslist;
725 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
726
727 _rename_helper(g_repo, "ikeepsix.txt", "newname.txt",
728 "Line 1\n" \
729 "Line 2\n" \
730 "Line 3\n" \
731 "Line 4\n" \
732 "Line 5\n" \
733 "Line 6\n" \
734 "Line 7\n" \
735 "Line 8\n" \
736 "Line 9\n"
737 );
738
739 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
740 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
741
742 cl_git_pass(git_repository_index(&index, g_repo));
743
744 /* Default threshold */
745 {
746 struct status_entry expected[] = {
747 { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "newname.txt" },
748 };
749
750 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
751 check_status(statuslist, expected, 1);
752 git_status_list_free(statuslist);
753 }
754
755 /* Threshold set to 90 */
756 {
757 struct status_entry expected[] = {
758 { GIT_STATUS_WT_DELETED, "ikeepsix.txt", NULL },
759 { GIT_STATUS_WT_NEW, "newname.txt", NULL }
760 };
761
762 opts.rename_threshold = 90;
763
764 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
765 check_status(statuslist, expected, 2);
766 git_status_list_free(statuslist);
767 }
768
769 /* Threshold set to 25 */
770 {
771 struct status_entry expected[] = {
772 { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "newname.txt" },
773 };
774
775 opts.rename_threshold = 25;
776
777 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
778 check_status(statuslist, expected, 1);
779 git_status_list_free(statuslist);
780 }
781
782 git_index_free(index);
783 }
784
785 void test_status_renames__case_insensitive_h2i_and_i2wc(void)
786 {
787 git_status_list *statuslist;
788 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
789 git_reference *head, *test_branch;
790 git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
791 git_str path_to_delete = GIT_STR_INIT;
792 git_str path_to_edit = GIT_STR_INIT;
793 git_index *index;
794 git_strarray paths = { NULL, 0 };
795
796 struct status_entry expected[] = {
797 { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED, "sixserving.txt", "sixserving-renamed.txt" },
798 { GIT_STATUS_INDEX_DELETED, "Wow.txt", "Wow.txt" }
799 };
800
801
802 /* Checkout the correct branch */
803 checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
804 cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
805 cl_git_pass(git_reference_symbolic_set_target(
806 &test_branch, head, "refs/heads/case-insensitive-status", NULL));
807 cl_git_pass(git_checkout_head(g_repo, &checkout_opts));
808
809 cl_git_pass(git_repository_index(&index, g_repo));
810
811
812 /* Rename sixserving.txt, delete Wow.txt, and stage those changes */
813 rename_file(g_repo, "sixserving.txt", "sixserving-renamed.txt");
814 cl_git_pass(git_str_joinpath(
815 &path_to_delete, git_repository_workdir(g_repo), "Wow.txt"));
816 cl_git_rmfile(path_to_delete.ptr);
817
818 cl_git_pass(git_index_add_all(index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL));
819 cl_git_pass(git_index_write(index));
820
821
822 /* Change content of sixserving-renamed.txt */
823 cl_git_pass(git_str_joinpath(
824 &path_to_edit, git_repository_workdir(g_repo), "sixserving-renamed.txt"));
825 cl_git_append2file(path_to_edit.ptr, "New content\n");
826
827 /* Run status */
828 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
829 opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
830 opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
831 opts.flags |= GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
832
833 cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
834 check_status(statuslist, expected, 2);
835 git_status_list_free(statuslist);
836
837 git_index_free(index);
838
839 git_str_dispose(&path_to_delete);
840 git_str_dispose(&path_to_edit);
841
842 git_reference_free(head);
843 git_reference_free(test_branch);
844 }