Merge pull request #2772 from ethomson/case_changing_rename Case changing rename
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b01f481..1ea9618 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -145,3 +145,6 @@ v0.21 + 1
* git_treebuilder_create now takes a repository so that it can query
repository configuration. Subsequently, git_treebuilder_write no
longer takes a repository.
+
+* git_checkout now handles case-changing renames correctly on
+ case-insensitive filesystems; for example renaming "readme" to "README".
diff --git a/src/checkout.c b/src/checkout.c
index a3a4601..45e02e9 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -2474,7 +2474,7 @@ int git_checkout_tree(
if ((error = git_repository_index(&index, repo)) < 0)
return error;
- if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL)))
+ if (!(error = git_iterator_for_tree(&tree_i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)))
error = git_checkout_iterator(tree_i, index, opts);
git_iterator_free(tree_i);
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index 1d65527..da3c00d 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -645,14 +645,7 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), -5555);
cl_assert(!git_path_exists("testrepo/new.txt"));
-
- /* on case-insensitive FS = a/b.txt, branch_file.txt, new.txt */
- /* on case-sensitive FS = README, then above */
-
- if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */
- cl_assert_equal_i(3, ca.count);
- else
- cl_assert_equal_i(4, ca.count);
+ cl_assert_equal_i(4, ca.count);
/* and again with a different stopping point and return code */
ca.filename = "README";
@@ -662,11 +655,7 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), 123);
cl_assert(!git_path_exists("testrepo/new.txt"));
-
- if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */
- cl_assert_equal_i(4, ca.count);
- else
- cl_assert_equal_i(1, ca.count);
+ cl_assert_equal_i(1, ca.count);
git_object_free(obj);
}
@@ -1034,3 +1023,80 @@ void test_checkout_tree__removes_conflicts_only_by_pathscope(void)
git_commit_free(commit);
git_index_free(index);
}
+
+void test_checkout_tree__case_changing_rename(void)
+{
+ git_index *index;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid master_id, dir_commit_id, tree_id, commit_id;
+ git_commit *master_commit, *dir_commit;
+ git_tree *tree;
+ git_signature *signature;
+ const git_index_entry *index_entry;
+ bool case_sensitive;
+
+ assert_on_branch(g_repo, "master");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* Switch branches and perform a case-changing rename */
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&dir_commit_id, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_commit_lookup(&dir_commit, g_repo, &dir_commit_id));
+
+ cl_git_pass(git_checkout_tree(g_repo, (git_object *)dir_commit, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL));
+
+ cl_assert(git_path_isfile("testrepo/README"));
+ case_sensitive = !git_path_isfile("testrepo/readme");
+
+ cl_assert(index_entry = git_index_get_bypath(index, "README", 0));
+ cl_assert_equal_s("README", index_entry->path);
+
+ cl_git_pass(git_index_remove_bypath(index, "README"));
+ cl_git_pass(p_rename("testrepo/README", "testrepo/__readme__"));
+ cl_git_pass(p_rename("testrepo/__readme__", "testrepo/readme"));
+ cl_git_append2file("testrepo/readme", "An addendum...");
+ cl_git_pass(git_index_add_bypath(index, "readme"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ cl_git_pass(git_signature_new(&signature, "Renamer", "rename@contoso.com", time(NULL), 0));
+
+ cl_git_pass(git_commit_create(&commit_id, g_repo, "refs/heads/dir", signature, signature, NULL, "case-changing rename", tree, 1, (const git_commit **)&dir_commit));
+
+ cl_assert(git_path_isfile("testrepo/readme"));
+ if (case_sensitive)
+ cl_assert(!git_path_isfile("testrepo/README"));
+
+ cl_assert(index_entry = git_index_get_bypath(index, "readme", 0));
+ cl_assert_equal_s("readme", index_entry->path);
+
+ /* Switching back to master should rename readme -> README */
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_name_to_id(&master_id, g_repo, "refs/heads/master"));
+ cl_git_pass(git_commit_lookup(&master_commit, g_repo, &master_id));
+
+ cl_git_pass(git_checkout_tree(g_repo, (git_object *)master_commit, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL));
+
+ assert_on_branch(g_repo, "master");
+
+ cl_assert(git_path_isfile("testrepo/README"));
+ if (case_sensitive)
+ cl_assert(!git_path_isfile("testrepo/readme"));
+
+ cl_assert(index_entry = git_index_get_bypath(index, "README", 0));
+ cl_assert_equal_s("README", index_entry->path);
+
+ git_signature_free(signature);
+ git_tree_free(tree);
+ git_commit_free(dir_commit);
+ git_commit_free(master_commit);
+}