]> git.proxmox.com Git - libgit2.git/blob - tests/submodule/update.c
4aa95985227517c9bc35843cb289369bd1266bab
[libgit2.git] / tests / submodule / update.c
1 #include "clar_libgit2.h"
2 #include "posix.h"
3 #include "path.h"
4 #include "submodule_helpers.h"
5 #include "futils.h"
6
7 static git_repository *g_repo = NULL;
8
9 void test_submodule_update__cleanup(void)
10 {
11 cl_git_sandbox_cleanup();
12 }
13
14 void test_submodule_update__uninitialized_submodule_no_init(void)
15 {
16 git_submodule *sm;
17 git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
18
19 g_repo = setup_fixture_submodule_simple();
20
21 /* get the submodule */
22 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
23
24 /* updating an uninitialized repository throws */
25 cl_git_fail_with(
26 GIT_ERROR,
27 git_submodule_update(sm, 0, &update_options));
28
29 git_submodule_free(sm);
30 }
31
32 struct update_submodule_cb_payload {
33 int update_tips_called;
34 int checkout_progress_called;
35 int checkout_notify_called;
36 };
37
38 static void checkout_progress_cb(
39 const char *path,
40 size_t completed_steps,
41 size_t total_steps,
42 void *payload)
43 {
44 struct update_submodule_cb_payload *update_payload = payload;
45
46 GIT_UNUSED(path);
47 GIT_UNUSED(completed_steps);
48 GIT_UNUSED(total_steps);
49
50 update_payload->checkout_progress_called = 1;
51 }
52
53 static int checkout_notify_cb(
54 git_checkout_notify_t why,
55 const char *path,
56 const git_diff_file *baseline,
57 const git_diff_file *target,
58 const git_diff_file *workdir,
59 void *payload)
60 {
61 struct update_submodule_cb_payload *update_payload = payload;
62
63 GIT_UNUSED(why);
64 GIT_UNUSED(path);
65 GIT_UNUSED(baseline);
66 GIT_UNUSED(target);
67 GIT_UNUSED(workdir);
68
69 update_payload->checkout_notify_called = 1;
70
71 return 0;
72 }
73
74 static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
75 {
76 struct update_submodule_cb_payload *update_payload = data;
77
78 GIT_UNUSED(refname);
79 GIT_UNUSED(a);
80 GIT_UNUSED(b);
81
82 update_payload->update_tips_called = 1;
83
84 return 1;
85 }
86
87 void test_submodule_update__update_submodule(void)
88 {
89 git_submodule *sm;
90 git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
91 unsigned int submodule_status = 0;
92 struct update_submodule_cb_payload update_payload = { 0 };
93
94 g_repo = setup_fixture_submodule_simple();
95
96 update_options.checkout_opts.progress_cb = checkout_progress_cb;
97 update_options.checkout_opts.progress_payload = &update_payload;
98
99 update_options.fetch_opts.callbacks.update_tips = update_tips;
100 update_options.fetch_opts.callbacks.payload = &update_payload;
101
102 /* get the submodule */
103 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
104
105 /* verify the initial state of the submodule */
106 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
107 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
108 GIT_SUBMODULE_STATUS_IN_INDEX |
109 GIT_SUBMODULE_STATUS_IN_CONFIG |
110 GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
111
112 /* initialize and update the submodule */
113 cl_git_pass(git_submodule_init(sm, 0));
114 cl_git_pass(git_submodule_update(sm, 0, &update_options));
115
116 /* verify state */
117 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
118 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
119 GIT_SUBMODULE_STATUS_IN_INDEX |
120 GIT_SUBMODULE_STATUS_IN_CONFIG |
121 GIT_SUBMODULE_STATUS_IN_WD);
122
123 cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
124 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
125 cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
126
127 /* verify that the expected callbacks have been called. */
128 cl_assert_equal_i(1, update_payload.checkout_progress_called);
129 cl_assert_equal_i(1, update_payload.update_tips_called);
130
131 git_submodule_free(sm);
132 }
133
134 void test_submodule_update__update_submodule_with_path(void)
135 {
136 git_submodule *sm;
137 git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
138 unsigned int submodule_status = 0;
139 struct update_submodule_cb_payload update_payload = { 0 };
140
141 g_repo = setup_fixture_submodule_with_path();
142
143 update_options.checkout_opts.progress_cb = checkout_progress_cb;
144 update_options.checkout_opts.progress_payload = &update_payload;
145
146 update_options.fetch_opts.callbacks.update_tips = update_tips;
147 update_options.fetch_opts.callbacks.payload = &update_payload;
148
149 /* get the submodule */
150 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
151
152 /* verify the initial state of the submodule */
153 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
154 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
155 GIT_SUBMODULE_STATUS_IN_INDEX |
156 GIT_SUBMODULE_STATUS_IN_CONFIG |
157 GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
158
159 /* initialize and update the submodule */
160 cl_git_pass(git_submodule_init(sm, 0));
161 cl_git_pass(git_submodule_update(sm, 0, &update_options));
162
163 /* verify state */
164 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
165 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
166 GIT_SUBMODULE_STATUS_IN_INDEX |
167 GIT_SUBMODULE_STATUS_IN_CONFIG |
168 GIT_SUBMODULE_STATUS_IN_WD);
169
170 cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
171 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
172 cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
173
174 /* verify that the expected callbacks have been called. */
175 cl_assert_equal_i(1, update_payload.checkout_progress_called);
176 cl_assert_equal_i(1, update_payload.update_tips_called);
177
178 git_submodule_free(sm);
179 }
180
181 void test_submodule_update__update_and_init_submodule(void)
182 {
183 git_submodule *sm;
184 git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
185 unsigned int submodule_status = 0;
186
187 g_repo = setup_fixture_submodule_simple();
188
189 /* get the submodule */
190 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
191
192 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
193 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
194 GIT_SUBMODULE_STATUS_IN_INDEX |
195 GIT_SUBMODULE_STATUS_IN_CONFIG |
196 GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
197
198 /* update (with option to initialize sub repo) */
199 cl_git_pass(git_submodule_update(sm, 1, &update_options));
200
201 /* verify expected state */
202 cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
203 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
204 cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
205
206 git_submodule_free(sm);
207 }
208
209 void test_submodule_update__update_already_checked_out_submodule(void)
210 {
211 git_submodule *sm = NULL;
212 git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
213 git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
214 unsigned int submodule_status = 0;
215 git_reference *branch_reference = NULL;
216 git_object *branch_commit = NULL;
217 struct update_submodule_cb_payload update_payload = { 0 };
218
219 g_repo = setup_fixture_submodule_simple();
220
221 update_options.checkout_opts.progress_cb = checkout_progress_cb;
222 update_options.checkout_opts.progress_payload = &update_payload;
223
224 /* Initialize and update the sub repository */
225 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
226
227 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
228 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
229 GIT_SUBMODULE_STATUS_IN_INDEX |
230 GIT_SUBMODULE_STATUS_IN_CONFIG |
231 GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
232
233 cl_git_pass(git_submodule_update(sm, 1, &update_options));
234
235 /* verify expected state */
236 cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
237 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
238 cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
239
240 /* checkout the alternate_1 branch */
241 checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
242
243 cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1"));
244 cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJECT_COMMIT));
245 cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options));
246 cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference)));
247
248 /*
249 * Verify state after checkout of parent repository. The submodule ID in the
250 * HEAD commit and index should be updated, but not the workdir.
251 */
252
253 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
254
255 git_submodule_free(sm);
256 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
257
258 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
259 GIT_SUBMODULE_STATUS_IN_INDEX |
260 GIT_SUBMODULE_STATUS_IN_CONFIG |
261 GIT_SUBMODULE_STATUS_IN_WD |
262 GIT_SUBMODULE_STATUS_WD_MODIFIED);
263
264 cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
265 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
266 cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
267
268 /*
269 * Update the submodule and verify the state.
270 * Now, the HEAD, index, and Workdir commits should all be updated to
271 * the new commit.
272 */
273 cl_git_pass(git_submodule_update(sm, 0, &update_options));
274 cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
275 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
276 cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
277
278 /* verify that the expected callbacks have been called. */
279 cl_assert_equal_i(1, update_payload.checkout_progress_called);
280
281 git_submodule_free(sm);
282 git_object_free(branch_commit);
283 git_reference_free(branch_reference);
284 }
285
286 void test_submodule_update__update_blocks_on_dirty_wd(void)
287 {
288 git_submodule *sm = NULL;
289 git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
290 git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
291 unsigned int submodule_status = 0;
292 git_reference *branch_reference = NULL;
293 git_object *branch_commit = NULL;
294 struct update_submodule_cb_payload update_payload = { 0 };
295
296 g_repo = setup_fixture_submodule_simple();
297
298 update_options.checkout_opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
299 update_options.checkout_opts.notify_cb = checkout_notify_cb;
300 update_options.checkout_opts.notify_payload = &update_payload;
301
302 /* Initialize and update the sub repository */
303 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
304
305 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
306 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
307 GIT_SUBMODULE_STATUS_IN_INDEX |
308 GIT_SUBMODULE_STATUS_IN_CONFIG |
309 GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
310
311 cl_git_pass(git_submodule_update(sm, 1, &update_options));
312
313 /* verify expected state */
314 cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
315 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
316 cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
317
318 /* checkout the alternate_1 branch */
319 checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
320
321 cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1"));
322 cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJECT_COMMIT));
323 cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options));
324 cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference)));
325
326 /*
327 * Verify state after checkout of parent repository. The submodule ID in the
328 * HEAD commit and index should be updated, but not the workdir.
329 */
330
331 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
332
333 git_submodule_free(sm);
334 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
335
336 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
337 GIT_SUBMODULE_STATUS_IN_INDEX |
338 GIT_SUBMODULE_STATUS_IN_CONFIG |
339 GIT_SUBMODULE_STATUS_IN_WD |
340 GIT_SUBMODULE_STATUS_WD_MODIFIED);
341
342 cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
343 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
344 cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
345
346 /*
347 * Create a conflicting edit in the subrepository to verify that
348 * the submodule update action is blocked.
349 */
350 cl_git_write2file("submodule_simple/testrepo/branch_file.txt", "a conflicting edit", 0,
351 O_WRONLY | O_CREAT | O_TRUNC, 0755);
352
353 cl_git_fail(git_submodule_update(sm, 0, &update_options));
354
355 /* verify that the expected callbacks have been called. */
356 cl_assert_equal_i(1, update_payload.checkout_notify_called);
357
358 /* verify that the submodule state has not changed. */
359 cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
360 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
361 cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
362
363 git_submodule_free(sm);
364 git_object_free(branch_commit);
365 git_reference_free(branch_reference);
366 }
367
368 void test_submodule_update__can_force_update(void)
369 {
370 git_submodule *sm = NULL;
371 git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
372 git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
373 unsigned int submodule_status = 0;
374 git_reference *branch_reference = NULL;
375 git_object *branch_commit = NULL;
376
377 g_repo = setup_fixture_submodule_simple();
378
379 /* Initialize and update the sub repository */
380 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
381
382 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
383 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
384 GIT_SUBMODULE_STATUS_IN_INDEX |
385 GIT_SUBMODULE_STATUS_IN_CONFIG |
386 GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
387
388 cl_git_pass(git_submodule_update(sm, 1, &update_options));
389
390 /* verify expected state */
391 cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
392 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
393 cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
394
395 /* checkout the alternate_1 branch */
396 checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
397
398 cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1"));
399 cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJECT_COMMIT));
400 cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options));
401 cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference)));
402
403 /*
404 * Verify state after checkout of parent repository. The submodule ID in the
405 * HEAD commit and index should be updated, but not the workdir.
406 */
407 cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
408
409 git_submodule_free(sm);
410 cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
411
412 cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
413 GIT_SUBMODULE_STATUS_IN_INDEX |
414 GIT_SUBMODULE_STATUS_IN_CONFIG |
415 GIT_SUBMODULE_STATUS_IN_WD |
416 GIT_SUBMODULE_STATUS_WD_MODIFIED);
417
418 cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
419 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
420 cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
421
422 /*
423 * Create a conflicting edit in the subrepository to verify that
424 * the submodule update action is blocked.
425 */
426 cl_git_write2file("submodule_simple/testrepo/branch_file.txt", "a conflicting edit", 0,
427 O_WRONLY | O_CREAT | O_TRUNC, 0777);
428
429 /* forcefully checkout and verify the submodule state was updated. */
430 update_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
431 cl_git_pass(git_submodule_update(sm, 0, &update_options));
432 cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
433 cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
434 cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
435
436 git_submodule_free(sm);
437 git_object_free(branch_commit);
438 git_reference_free(branch_reference);
439 }
440