]>
Commit | Line | Data |
---|---|---|
f30fff45 RB |
1 | #include "clar_libgit2.h" |
2 | #include "../status/status_helpers.h" | |
3 | #include "posix.h" | |
c97d407d | 4 | #include "fileops.h" |
f30fff45 RB |
5 | |
6 | git_repository *g_repo = NULL; | |
7 | ||
8 | void test_index_addall__initialize(void) | |
9 | { | |
10 | } | |
11 | ||
12 | void test_index_addall__cleanup(void) | |
13 | { | |
14 | git_repository_free(g_repo); | |
15 | g_repo = NULL; | |
16 | } | |
17 | ||
18 | #define STATUS_INDEX_FLAGS \ | |
19 | (GIT_STATUS_INDEX_NEW | GIT_STATUS_INDEX_MODIFIED | \ | |
20 | GIT_STATUS_INDEX_DELETED | GIT_STATUS_INDEX_RENAMED | \ | |
21 | GIT_STATUS_INDEX_TYPECHANGE) | |
22 | ||
23 | #define STATUS_WT_FLAGS \ | |
24 | (GIT_STATUS_WT_NEW | GIT_STATUS_WT_MODIFIED | \ | |
25 | GIT_STATUS_WT_DELETED | GIT_STATUS_WT_TYPECHANGE | \ | |
26 | GIT_STATUS_WT_RENAMED) | |
27 | ||
28 | typedef struct { | |
29 | size_t index_adds; | |
30 | size_t index_dels; | |
31 | size_t index_mods; | |
32 | size_t wt_adds; | |
33 | size_t wt_dels; | |
34 | size_t wt_mods; | |
35 | size_t ignores; | |
36 | } index_status_counts; | |
37 | ||
38 | static int index_status_cb( | |
39 | const char *path, unsigned int status_flags, void *payload) | |
40 | { | |
41 | index_status_counts *vals = payload; | |
42 | ||
43 | /* cb_status__print(path, status_flags, NULL); */ | |
44 | ||
45 | GIT_UNUSED(path); | |
46 | ||
47 | if (status_flags & GIT_STATUS_INDEX_NEW) | |
48 | vals->index_adds++; | |
49 | if (status_flags & GIT_STATUS_INDEX_MODIFIED) | |
50 | vals->index_mods++; | |
51 | if (status_flags & GIT_STATUS_INDEX_DELETED) | |
52 | vals->index_dels++; | |
53 | if (status_flags & GIT_STATUS_INDEX_TYPECHANGE) | |
54 | vals->index_mods++; | |
55 | ||
56 | if (status_flags & GIT_STATUS_WT_NEW) | |
57 | vals->wt_adds++; | |
58 | if (status_flags & GIT_STATUS_WT_MODIFIED) | |
59 | vals->wt_mods++; | |
60 | if (status_flags & GIT_STATUS_WT_DELETED) | |
61 | vals->wt_dels++; | |
62 | if (status_flags & GIT_STATUS_WT_TYPECHANGE) | |
63 | vals->wt_mods++; | |
64 | ||
65 | if (status_flags & GIT_STATUS_IGNORED) | |
66 | vals->ignores++; | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
71 | static void check_status( | |
72 | git_repository *repo, | |
73 | size_t index_adds, size_t index_dels, size_t index_mods, | |
74 | size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores) | |
75 | { | |
76 | index_status_counts vals; | |
77 | ||
78 | memset(&vals, 0, sizeof(vals)); | |
79 | ||
80 | cl_git_pass(git_status_foreach(repo, index_status_cb, &vals)); | |
81 | ||
82 | cl_assert_equal_sz(index_adds, vals.index_adds); | |
83 | cl_assert_equal_sz(index_dels, vals.index_dels); | |
84 | cl_assert_equal_sz(index_mods, vals.index_mods); | |
85 | cl_assert_equal_sz(wt_adds, vals.wt_adds); | |
86 | cl_assert_equal_sz(wt_dels, vals.wt_dels); | |
87 | cl_assert_equal_sz(wt_mods, vals.wt_mods); | |
88 | cl_assert_equal_sz(ignores, vals.ignores); | |
89 | } | |
90 | ||
91 | static void check_stat_data(git_index *index, const char *path, bool match) | |
92 | { | |
93 | const git_index_entry *entry; | |
94 | struct stat st; | |
95 | ||
96 | cl_must_pass(p_lstat(path, &st)); | |
97 | ||
98 | /* skip repo base dir name */ | |
99 | while (*path != '/') | |
100 | ++path; | |
101 | ++path; | |
102 | ||
103 | entry = git_index_get_bypath(index, path, 0); | |
104 | cl_assert(entry); | |
105 | ||
106 | if (match) { | |
107 | cl_assert(st.st_ctime == entry->ctime.seconds); | |
108 | cl_assert(st.st_mtime == entry->mtime.seconds); | |
109 | cl_assert(st.st_size == entry->file_size); | |
110 | cl_assert(st.st_uid == entry->uid); | |
111 | cl_assert(st.st_gid == entry->gid); | |
c97d407d RB |
112 | cl_assert_equal_i_fmt( |
113 | GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o"); | |
114 | cl_assert_equal_b( | |
a7fcc44d | 115 | GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode)); |
f30fff45 RB |
116 | } else { |
117 | /* most things will still match */ | |
118 | cl_assert(st.st_size != entry->file_size); | |
119 | /* would check mtime, but with second resolution it won't work :( */ | |
120 | } | |
121 | } | |
122 | ||
f30fff45 RB |
123 | void test_index_addall__repo_lifecycle(void) |
124 | { | |
125 | int error; | |
126 | git_index *index; | |
127 | git_strarray paths = { NULL, 0 }; | |
128 | char *strs[1]; | |
129 | ||
130 | cl_git_pass(git_repository_init(&g_repo, "addall", false)); | |
131 | check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); | |
132 | ||
133 | cl_git_pass(git_repository_index(&index, g_repo)); | |
134 | ||
135 | cl_git_mkfile("addall/file.foo", "a file"); | |
136 | check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); | |
137 | ||
138 | cl_git_mkfile("addall/.gitignore", "*.foo\n"); | |
139 | check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); | |
140 | ||
141 | cl_git_mkfile("addall/file.bar", "another file"); | |
142 | check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); | |
143 | ||
144 | strs[0] = "file.*"; | |
145 | paths.strings = strs; | |
146 | paths.count = 1; | |
147 | ||
148 | cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); | |
149 | check_stat_data(index, "addall/file.bar", true); | |
150 | check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); | |
151 | ||
152 | cl_git_rewritefile("addall/file.bar", "new content for file"); | |
153 | check_stat_data(index, "addall/file.bar", false); | |
154 | check_status(g_repo, 1, 0, 0, 1, 0, 1, 1); | |
155 | ||
156 | cl_git_mkfile("addall/file.zzz", "yet another one"); | |
157 | cl_git_mkfile("addall/other.zzz", "yet another one"); | |
158 | cl_git_mkfile("addall/more.zzz", "yet another one"); | |
159 | check_status(g_repo, 1, 0, 0, 4, 0, 1, 1); | |
160 | ||
161 | cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); | |
162 | check_stat_data(index, "addall/file.bar", true); | |
163 | check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); | |
164 | ||
165 | cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); | |
166 | check_stat_data(index, "addall/file.zzz", true); | |
167 | check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); | |
168 | ||
155fa234 | 169 | cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "first commit"); |
f30fff45 RB |
170 | check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); |
171 | ||
172 | /* attempt to add an ignored file - does nothing */ | |
173 | strs[0] = "file.foo"; | |
174 | cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); | |
175 | check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); | |
176 | ||
177 | /* add with check - should generate error */ | |
178 | error = git_index_add_all( | |
179 | index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL); | |
180 | cl_assert_equal_i(GIT_EINVALIDSPEC, error); | |
181 | check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); | |
182 | ||
183 | /* add with force - should allow */ | |
184 | cl_git_pass(git_index_add_all( | |
185 | index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL)); | |
186 | check_stat_data(index, "addall/file.foo", true); | |
187 | check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); | |
188 | ||
189 | /* now it's in the index, so regular add should work */ | |
190 | cl_git_rewritefile("addall/file.foo", "new content for file"); | |
191 | check_stat_data(index, "addall/file.foo", false); | |
192 | check_status(g_repo, 1, 0, 0, 3, 0, 1, 0); | |
193 | ||
194 | cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); | |
195 | check_stat_data(index, "addall/file.foo", true); | |
196 | check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); | |
197 | ||
198 | cl_git_pass(git_index_add_bypath(index, "more.zzz")); | |
199 | check_stat_data(index, "addall/more.zzz", true); | |
200 | check_status(g_repo, 2, 0, 0, 2, 0, 0, 0); | |
201 | ||
202 | cl_git_rewritefile("addall/file.zzz", "new content for file"); | |
203 | check_status(g_repo, 2, 0, 0, 2, 0, 1, 0); | |
204 | ||
205 | cl_git_pass(git_index_add_bypath(index, "file.zzz")); | |
206 | check_stat_data(index, "addall/file.zzz", true); | |
207 | check_status(g_repo, 2, 0, 1, 2, 0, 0, 0); | |
208 | ||
209 | strs[0] = "*.zzz"; | |
210 | cl_git_pass(git_index_remove_all(index, &paths, NULL, NULL)); | |
211 | check_status(g_repo, 1, 1, 0, 4, 0, 0, 0); | |
212 | ||
213 | cl_git_pass(git_index_add_bypath(index, "file.zzz")); | |
214 | check_status(g_repo, 1, 0, 1, 3, 0, 0, 0); | |
215 | ||
155fa234 | 216 | cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "second commit"); |
f30fff45 RB |
217 | check_status(g_repo, 0, 0, 0, 3, 0, 0, 0); |
218 | ||
219 | cl_must_pass(p_unlink("addall/file.zzz")); | |
220 | check_status(g_repo, 0, 0, 0, 3, 1, 0, 0); | |
221 | ||
222 | /* update_all should be able to remove entries */ | |
223 | cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); | |
224 | check_status(g_repo, 0, 1, 0, 3, 0, 0, 0); | |
225 | ||
7863523a RB |
226 | strs[0] = "*"; |
227 | cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); | |
228 | check_status(g_repo, 3, 1, 0, 0, 0, 0, 0); | |
229 | ||
230 | /* must be able to remove at any position while still updating other files */ | |
231 | cl_must_pass(p_unlink("addall/.gitignore")); | |
232 | cl_git_rewritefile("addall/file.zzz", "reconstructed file"); | |
233 | cl_git_rewritefile("addall/more.zzz", "altered file reality"); | |
234 | check_status(g_repo, 3, 1, 0, 1, 1, 1, 0); | |
235 | ||
236 | cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); | |
237 | check_status(g_repo, 2, 1, 0, 1, 0, 0, 0); | |
238 | /* this behavior actually matches 'git add -u' where "file.zzz" has | |
239 | * been removed from the index, so when you go to update, even though | |
240 | * it exists in the HEAD, it is not re-added to the index, leaving it | |
241 | * as a DELETE when comparing HEAD to index and as an ADD comparing | |
242 | * index to worktree | |
243 | */ | |
244 | ||
f30fff45 RB |
245 | git_index_free(index); |
246 | } |