]> git.proxmox.com Git - libgit2.git/blame - tests/online/push.c
remote: introduce git_remote_push()
[libgit2.git] / tests / online / push.c
CommitLineData
613d5eb9
PK
1#include "clar_libgit2.h"
2#include "buffer.h"
3#include "posix.h"
4#include "vector.h"
5#include "../submodule/submodule_helpers.h"
6#include "push_util.h"
1d645aab
JM
7#include "refspec.h"
8#include "remote.h"
613d5eb9 9
613d5eb9
PK
10static git_repository *_repo;
11
84efffc3
ET
12static char *_remote_url;
13
5be622fb
CMN
14static char *_remote_ssh_key;
15static char *_remote_ssh_pubkey;
16static char *_remote_ssh_passphrase;
17
613d5eb9
PK
18static char *_remote_user;
19static char *_remote_pass;
20
84efffc3
ET
21static char *_remote_default;
22
e3c131c5
CMN
23static int cred_acquire_cb(git_cred **, const char *, const char *, unsigned int, void *);
24
613d5eb9
PK
25static git_remote *_remote;
26static record_callbacks_data _record_cbs_data = {{ 0 }};
27static git_remote_callbacks _record_cbs = RECORD_CALLBACKS_INIT(&_record_cbs_data);
28
29static git_oid _oid_b6;
30static git_oid _oid_b5;
31static git_oid _oid_b4;
32static git_oid _oid_b3;
33static git_oid _oid_b2;
34static git_oid _oid_b1;
35
abeefbbe
MS
36static git_oid _tag_commit;
37static git_oid _tag_tree;
38static git_oid _tag_blob;
39static git_oid _tag_lightweight;
1c13b0bf 40static git_oid _tag_tag;
abeefbbe 41
7602cb7c 42static int cred_acquire_cb(
2648dc1a
ET
43 git_cred **cred,
44 const char *url,
45 const char *user_from_url,
46 unsigned int allowed_types,
47 void *payload)
613d5eb9
PK
48{
49 GIT_UNUSED(url);
7602cb7c 50 GIT_UNUSED(user_from_url);
2648dc1a 51 GIT_UNUSED(payload);
613d5eb9 52
e26b08d3
CMN
53 if (GIT_CREDTYPE_USERNAME & allowed_types) {
54 if (!_remote_user) {
55 printf("GITTEST_REMOTE_USER must be set\n");
56 return -1;
57 }
58
59 return git_cred_username_new(cred, _remote_user);
60 }
61
84efffc3
ET
62 if (GIT_CREDTYPE_DEFAULT & allowed_types) {
63 if (!_remote_default) {
64 printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n");
65 return -1;
66 }
67
68 return git_cred_default_new(cred);
69 }
70
70a8c78f 71 if (GIT_CREDTYPE_SSH_KEY & allowed_types) {
2648dc1a
ET
72 if (!_remote_user || !_remote_ssh_pubkey || !_remote_ssh_key || !_remote_ssh_passphrase) {
73 printf("GITTEST_REMOTE_USER, GITTEST_REMOTE_SSH_PUBKEY, GITTEST_REMOTE_SSH_KEY and GITTEST_REMOTE_SSH_PASSPHRASE must be set\n");
74 return -1;
75 }
84efffc3 76
70a8c78f 77 return git_cred_ssh_key_new(cred, _remote_user, _remote_ssh_pubkey, _remote_ssh_key, _remote_ssh_passphrase);
2648dc1a 78 }
5be622fb 79
2648dc1a
ET
80 if (GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) {
81 if (!_remote_user || !_remote_pass) {
82 printf("GITTEST_REMOTE_USER and GITTEST_REMOTE_PASS must be set\n");
83 return -1;
84 }
613d5eb9 85
2648dc1a
ET
86 return git_cred_userpass_plaintext_new(cred, _remote_user, _remote_pass);
87 }
88
89 return -1;
613d5eb9
PK
90}
91
9d41984c
ET
92/* the results of a push status. when used for expected values, msg may be NULL
93 * to indicate that it should not be matched. */
613d5eb9
PK
94typedef struct {
95 const char *ref;
9d41984c 96 int success;
613d5eb9
PK
97 const char *msg;
98} push_status;
99
100/**
101 * git_push_status_foreach callback that records status entries.
102 * @param data (git_vector *) of push_status instances
103 */
104static int record_push_status_cb(const char *ref, const char *msg, void *data)
105{
106 git_vector *statuses = (git_vector *)data;
107 push_status *s;
108
109 cl_assert(s = git__malloc(sizeof(*s)));
110 s->ref = ref;
9d41984c 111 s->success = (msg == NULL);
613d5eb9
PK
112 s->msg = msg;
113
114 git_vector_insert(statuses, s);
115
116 return 0;
117}
118
119static void do_verify_push_status(git_push *push, const push_status expected[], const size_t expected_len)
120{
121 git_vector actual = GIT_VECTOR_INIT;
122 push_status *iter;
123 bool failed = false;
124 size_t i;
125
126 git_push_status_foreach(push, record_push_status_cb, &actual);
127
128 if (expected_len != actual.length)
129 failed = true;
130 else
131 git_vector_foreach(&actual, i, iter)
132 if (strcmp(expected[i].ref, iter->ref) ||
9d41984c
ET
133 (expected[i].success != iter->success) ||
134 (expected[i].msg && (!iter->msg || strcmp(expected[i].msg, iter->msg)))) {
613d5eb9
PK
135 failed = true;
136 break;
137 }
138
139 if (failed) {
140 git_buf msg = GIT_BUF_INIT;
141
142 git_buf_puts(&msg, "Expected and actual push statuses differ:\nEXPECTED:\n");
143
144 for(i = 0; i < expected_len; i++) {
145 git_buf_printf(&msg, "%s: %s\n",
146 expected[i].ref,
9d41984c 147 expected[i].success ? "success" : "failed");
613d5eb9
PK
148 }
149
150 git_buf_puts(&msg, "\nACTUAL:\n");
151
9d41984c
ET
152 git_vector_foreach(&actual, i, iter) {
153 if (iter->success)
154 git_buf_printf(&msg, "%s: success\n", iter->ref);
155 else
156 git_buf_printf(&msg, "%s: failed with message: %s", iter->ref, iter->msg);
157 }
613d5eb9
PK
158
159 cl_fail(git_buf_cstr(&msg));
160
161 git_buf_free(&msg);
162 }
163
164 git_vector_foreach(&actual, i, iter)
165 git__free(iter);
166
167 git_vector_free(&actual);
168}
169
170/**
171 * Verifies that after git_push_finish(), refs on a remote have the expected
172 * names, oids, and order.
156cfec0 173 *
613d5eb9
PK
174 * @param remote remote to verify
175 * @param expected_refs expected remote refs after push
176 * @param expected_refs_len length of expected_refs
177 */
178static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
179{
359dce72
CMN
180 const git_remote_head **actual_refs;
181 size_t actual_refs_len;
613d5eb9 182
359dce72
CMN
183 git_remote_ls(&actual_refs, &actual_refs_len, remote);
184 verify_remote_refs(actual_refs, actual_refs_len, expected_refs, expected_refs_len);
613d5eb9
PK
185}
186
1d645aab
JM
187/**
188 * Verifies that after git_push_update_tips(), remote tracking branches have the expected
189 * names and oids.
190 *
191 * @param remote remote to verify
192 * @param expected_refs expected remote refs after push
193 * @param expected_refs_len length of expected_refs
194 */
195static void verify_tracking_branches(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
196{
4330ab26 197 git_refspec *fetch_spec;
1d645aab
JM
198 size_t i, j;
199 git_buf msg = GIT_BUF_INIT;
200 git_buf ref_name = GIT_BUF_INIT;
1d645aab 201 git_vector actual_refs = GIT_VECTOR_INIT;
8ec889a4 202 git_branch_iterator *iter;
1d645aab
JM
203 char *actual_ref;
204 git_oid oid;
8ec889a4 205 int failed = 0, error;
a667ca82 206 git_branch_t branch_type;
8ec889a4 207 git_reference *ref;
1d645aab
JM
208
209 /* Get current remote branches */
8ec889a4
CMN
210 cl_git_pass(git_branch_iterator_new(&iter, remote->repo, GIT_BRANCH_REMOTE));
211
212 while ((error = git_branch_next(&ref, &branch_type, iter)) == 0) {
213 cl_assert_equal_i(branch_type, GIT_BRANCH_REMOTE);
214
215 cl_git_pass(git_vector_insert(&actual_refs, git__strdup(git_reference_name(ref))));
f96e7e6c
ET
216
217 git_reference_free(ref);
8ec889a4
CMN
218 }
219
220 cl_assert_equal_i(error, GIT_ITEROVER);
11bd7a03 221 git_branch_iterator_free(iter);
1d645aab
JM
222
223 /* Loop through expected refs, make sure they exist */
224 for (i = 0; i < expected_refs_len; i++) {
225
226 /* Convert remote reference name into tracking branch name.
227 * If the spec is not under refs/heads/, then skip.
228 */
4330ab26
CMN
229 fetch_spec = git_remote__matching_refspec(remote, expected_refs[i].name);
230 if (!fetch_spec)
1d645aab
JM
231 continue;
232
bf522e08 233 cl_git_pass(git_refspec_transform(&ref_name, fetch_spec, expected_refs[i].name));
1d645aab
JM
234
235 /* Find matching remote branch */
236 git_vector_foreach(&actual_refs, j, actual_ref) {
8ec889a4 237 if (!strcmp(git_buf_cstr(&ref_name), actual_ref))
1d645aab
JM
238 break;
239 }
240
241 if (j == actual_refs.length) {
242 git_buf_printf(&msg, "Did not find expected tracking branch '%s'.", git_buf_cstr(&ref_name));
243 failed = 1;
244 goto failed;
245 }
246
247 /* Make sure tracking branch is at expected commit ID */
8ec889a4 248 cl_git_pass(git_reference_name_to_id(&oid, remote->repo, actual_ref));
1d645aab
JM
249
250 if (git_oid_cmp(expected_refs[i].oid, &oid) != 0) {
251 git_buf_puts(&msg, "Tracking branch commit does not match expected ID.");
252 failed = 1;
253 goto failed;
254 }
255
2ff4469a 256 git__free(actual_ref);
1d645aab
JM
257 cl_git_pass(git_vector_remove(&actual_refs, j));
258 }
259
260 /* Make sure there are no extra branches */
261 if (actual_refs.length > 0) {
262 git_buf_puts(&msg, "Unexpected remote tracking branches exist.");
263 failed = 1;
264 goto failed;
265 }
266
267failed:
f70cfd34 268 if (failed)
1d645aab
JM
269 cl_fail(git_buf_cstr(&msg));
270
271 git_vector_foreach(&actual_refs, i, actual_ref)
272 git__free(actual_ref);
273
274 git_vector_free(&actual_refs);
275 git_buf_free(&msg);
2ff4469a 276 git_buf_free(&ref_name);
f70cfd34
JG
277}
278
279static void verify_update_tips_callback(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
280{
281 git_refspec *fetch_spec;
282 git_buf msg = GIT_BUF_INIT;
283 git_buf ref_name = GIT_BUF_INIT;
284 updated_tip *tip = NULL;
285 size_t i, j;
286 int failed = 0;
287
288 for (i = 0; i < expected_refs_len; ++i) {
289 /* Convert remote reference name into tracking branch name.
290 * If the spec is not under refs/heads/, then skip.
291 */
292 fetch_spec = git_remote__matching_refspec(remote, expected_refs[i].name);
293 if (!fetch_spec)
294 continue;
295
296 cl_git_pass(git_refspec_transform(&ref_name, fetch_spec, expected_refs[i].name));
297
298 /* Find matching update_tip entry */
299 git_vector_foreach(&_record_cbs_data.updated_tips, j, tip) {
300 if (!strcmp(git_buf_cstr(&ref_name), tip->name))
301 break;
302 }
303
304 if (j == _record_cbs_data.updated_tips.length) {
305 git_buf_printf(&msg, "Did not find expected updated tip entry for branch '%s'.", git_buf_cstr(&ref_name));
306 failed = 1;
307 goto failed;
308 }
309
310 if (git_oid_cmp(expected_refs[i].oid, tip->new_oid) != 0) {
311 git_buf_printf(&msg, "Updated tip ID does not match expected ID");
312 failed = 1;
313 goto failed;
314 }
315 }
316
317failed:
318 if (failed)
319 cl_fail(git_buf_cstr(&msg));
320
321 git_buf_free(&ref_name);
322 git_buf_free(&msg);
1d645aab
JM
323}
324
6443eaf2 325void test_online_push__initialize(void)
613d5eb9
PK
326{
327 git_vector delete_specs = GIT_VECTOR_INIT;
ae297212
CMN
328 const git_remote_head **heads;
329 size_t i, heads_len;
613d5eb9
PK
330 char *curr_del_spec;
331
332 _repo = cl_git_sandbox_init("push_src");
333
334 cl_fixture_sandbox("testrepo.git");
335 cl_rename("push_src/submodule/.gitted", "push_src/submodule/.git");
336
337 rewrite_gitmodules(git_repository_workdir(_repo));
338
339 /* git log --format=oneline --decorate --graph
340 * *-. 951bbbb90e2259a4c8950db78946784fb53fcbce (HEAD, b6) merge b3, b4, and b5 to b6
341 * |\ \
342 * | | * fa38b91f199934685819bea316186d8b008c52a2 (b5) added submodule named 'submodule' pointing to '../testrepo.git'
343 * | * | 27b7ce66243eb1403862d05f958c002312df173d (b4) edited fold\b.txt
344 * | |/
345 * * | d9b63a88223d8367516f50bd131a5f7349b7f3e4 (b3) edited a.txt
346 * |/
347 * * a78705c3b2725f931d3ee05348d83cc26700f247 (b2, b1) added fold and fold/b.txt
348 * * 5c0bb3d1b9449d1cc69d7519fd05166f01840915 added a.txt
349 */
350 git_oid_fromstr(&_oid_b6, "951bbbb90e2259a4c8950db78946784fb53fcbce");
351 git_oid_fromstr(&_oid_b5, "fa38b91f199934685819bea316186d8b008c52a2");
352 git_oid_fromstr(&_oid_b4, "27b7ce66243eb1403862d05f958c002312df173d");
353 git_oid_fromstr(&_oid_b3, "d9b63a88223d8367516f50bd131a5f7349b7f3e4");
354 git_oid_fromstr(&_oid_b2, "a78705c3b2725f931d3ee05348d83cc26700f247");
355 git_oid_fromstr(&_oid_b1, "a78705c3b2725f931d3ee05348d83cc26700f247");
356
abeefbbe
MS
357 git_oid_fromstr(&_tag_commit, "805c54522e614f29f70d2413a0470247d8b424ac");
358 git_oid_fromstr(&_tag_tree, "ff83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e");
359 git_oid_fromstr(&_tag_blob, "b483ae7ba66decee9aee971f501221dea84b1498");
360 git_oid_fromstr(&_tag_lightweight, "951bbbb90e2259a4c8950db78946784fb53fcbce");
1c13b0bf 361 git_oid_fromstr(&_tag_tag, "eea4f2705eeec2db3813f2430829afce99cd00b5");
abeefbbe 362
613d5eb9
PK
363 /* Remote URL environment variable must be set. User and password are optional. */
364 _remote_url = cl_getenv("GITTEST_REMOTE_URL");
365 _remote_user = cl_getenv("GITTEST_REMOTE_USER");
366 _remote_pass = cl_getenv("GITTEST_REMOTE_PASS");
5be622fb
CMN
367 _remote_ssh_key = cl_getenv("GITTEST_REMOTE_SSH_KEY");
368 _remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
369 _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
84efffc3 370 _remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
613d5eb9
PK
371 _remote = NULL;
372
0f65733b
VM
373 /* Skip the test if we're missing the remote URL */
374 if (!_remote_url)
375 cl_skip();
613d5eb9 376
0f65733b 377 cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url));
613d5eb9 378
0f65733b
VM
379 record_callbacks_data_clear(&_record_cbs_data);
380 git_remote_set_callbacks(_remote, &_record_cbs);
613d5eb9 381
0f65733b 382 cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
613d5eb9 383
0f65733b
VM
384 /* Clean up previously pushed branches. Fails if receive.denyDeletes is
385 * set on the remote. Also, on Git 1.7.0 and newer, you must run
386 * 'git config receive.denyDeleteCurrent ignore' in the remote repo in
387 * order to delete the remote branch pointed to by HEAD (usually master).
388 * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt
389 */
390 cl_git_pass(git_remote_ls(&heads, &heads_len, _remote));
391 cl_git_pass(create_deletion_refspecs(&delete_specs, heads, heads_len));
392 if (delete_specs.length) {
393 git_push *push;
613d5eb9 394
0f65733b 395 cl_git_pass(git_push_new(&push, _remote));
613d5eb9 396
0f65733b
VM
397 git_vector_foreach(&delete_specs, i, curr_del_spec) {
398 git_push_add_refspec(push, curr_del_spec);
399 git__free(curr_del_spec);
613d5eb9
PK
400 }
401
0f65733b
VM
402 cl_git_pass(git_push_finish(push));
403 git_push_free(push);
404 }
405
406 git_remote_disconnect(_remote);
407 git_vector_free(&delete_specs);
613d5eb9 408
0f65733b
VM
409 /* Now that we've deleted everything, fetch from the remote */
410 cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH));
3f894205 411 cl_git_pass(git_remote_download(_remote, NULL));
0f65733b
VM
412 cl_git_pass(git_remote_update_tips(_remote, NULL, NULL));
413 git_remote_disconnect(_remote);
613d5eb9
PK
414}
415
6443eaf2 416void test_online_push__cleanup(void)
613d5eb9
PK
417{
418 if (_remote)
419 git_remote_free(_remote);
c4e3e797
BS
420 _remote = NULL;
421
422 /* Freed by cl_git_sandbox_cleanup */
423 _repo = NULL;
613d5eb9
PK
424
425 record_callbacks_data_clear(&_record_cbs_data);
426
427 cl_fixture_cleanup("testrepo.git");
428 cl_git_sandbox_cleanup();
429}
430
11bd7a03
RB
431static int push_pack_progress_cb(
432 int stage, unsigned int current, unsigned int total, void* payload)
b176eded 433{
11bd7a03 434 int *calls = (int *)payload;
7baa7631 435 GIT_UNUSED(stage); GIT_UNUSED(current); GIT_UNUSED(total);
11bd7a03
RB
436 if (*calls < 0)
437 return *calls;
438 (*calls)++;
5b188225 439 return 0;
b176eded
JM
440}
441
11bd7a03
RB
442static int push_transfer_progress_cb(
443 unsigned int current, unsigned int total, size_t bytes, void* payload)
b176eded 444{
11bd7a03 445 int *calls = (int *)payload;
af302aca 446 GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes);
11bd7a03
RB
447 if (*calls < 0)
448 return *calls;
449 (*calls)++;
5b188225 450 return 0;
b176eded
JM
451}
452
613d5eb9
PK
453/**
454 * Calls push and relists refs on remote to verify success.
156cfec0 455 *
613d5eb9
PK
456 * @param refspecs refspecs to push
457 * @param refspecs_len length of refspecs
458 * @param expected_refs expected remote refs after push
459 * @param expected_refs_len length of expected_refs
460 * @param expected_ret expected return value from git_push_finish()
b176eded 461 * @param check_progress_cb Check that the push progress callbacks are called
613d5eb9 462 */
11bd7a03
RB
463static void do_push(
464 const char *refspecs[], size_t refspecs_len,
613d5eb9 465 push_status expected_statuses[], size_t expected_statuses_len,
11bd7a03 466 expected_ref expected_refs[], size_t expected_refs_len,
f70cfd34 467 int expected_ret, int check_progress_cb, int check_update_tips_cb)
613d5eb9
PK
468{
469 git_push *push;
b8b897bb 470 git_push_options opts = GIT_PUSH_OPTIONS_INIT;
613d5eb9 471 size_t i;
11bd7a03 472 int pack_progress_calls = 0, transfer_progress_calls = 0;
491cecfe 473 git_signature *pusher;
613d5eb9
PK
474
475 if (_remote) {
b8b897bb
PK
476 /* Auto-detect the number of threads to use */
477 opts.pb_parallelism = 0;
478
491cecfe 479 cl_git_pass(git_signature_now(&pusher, "Foo Bar", "foo@example.com"));
613d5eb9
PK
480 cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
481
482 cl_git_pass(git_push_new(&push, _remote));
b8b897bb 483 cl_git_pass(git_push_set_options(push, &opts));
613d5eb9 484
11bd7a03
RB
485 if (check_progress_cb) {
486 /* if EUSER, then abort in transfer */
487 if (expected_ret == GIT_EUSER)
488 transfer_progress_calls = GIT_EUSER;
489
490 cl_git_pass(
491 git_push_set_callbacks(
492 push, push_pack_progress_cb, &pack_progress_calls,
493 push_transfer_progress_cb, &transfer_progress_calls));
494 }
b176eded 495
613d5eb9
PK
496 for (i = 0; i < refspecs_len; i++)
497 cl_git_pass(git_push_add_refspec(push, refspecs[i]));
498
499 if (expected_ret < 0) {
11bd7a03 500 cl_git_fail_with(git_push_finish(push), expected_ret);
613d5eb9 501 cl_assert_equal_i(0, git_push_unpack_ok(push));
11bd7a03
RB
502 } else {
503 cl_git_pass(git_push_finish(push));
613d5eb9
PK
504 cl_assert_equal_i(1, git_push_unpack_ok(push));
505 }
506
11bd7a03
RB
507 if (check_progress_cb && !expected_ret) {
508 cl_assert(pack_progress_calls > 0);
509 cl_assert(transfer_progress_calls > 0);
b176eded
JM
510 }
511
613d5eb9
PK
512 do_verify_push_status(push, expected_statuses, expected_statuses_len);
513
613d5eb9
PK
514 verify_refs(_remote, expected_refs, expected_refs_len);
515
491cecfe 516 cl_git_pass(git_push_update_tips(push, pusher, "test push"));
1d645aab
JM
517 verify_tracking_branches(_remote, expected_refs, expected_refs_len);
518
f70cfd34
JG
519 if (check_update_tips_cb)
520 verify_update_tips_callback(_remote, expected_refs, expected_refs_len);
521
1d645aab 522 git_push_free(push);
613d5eb9
PK
523
524 git_remote_disconnect(_remote);
491cecfe 525 git_signature_free(pusher);
613d5eb9 526 }
491cecfe 527
613d5eb9
PK
528}
529
530/* Call push_finish() without ever calling git_push_add_refspec() */
6443eaf2 531void test_online_push__noop(void)
613d5eb9 532{
f70cfd34 533 do_push(NULL, 0, NULL, 0, NULL, 0, 0, 0, 1);
613d5eb9
PK
534}
535
6443eaf2 536void test_online_push__b1(void)
613d5eb9
PK
537{
538 const char *specs[] = { "refs/heads/b1:refs/heads/b1" };
9d41984c 539 push_status exp_stats[] = { { "refs/heads/b1", 1 } };
613d5eb9
PK
540 expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
541 do_push(specs, ARRAY_SIZE(specs),
542 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 543 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
613d5eb9
PK
544}
545
6443eaf2 546void test_online_push__b2(void)
613d5eb9
PK
547{
548 const char *specs[] = { "refs/heads/b2:refs/heads/b2" };
9d41984c 549 push_status exp_stats[] = { { "refs/heads/b2", 1 } };
613d5eb9
PK
550 expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } };
551 do_push(specs, ARRAY_SIZE(specs),
552 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 553 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
613d5eb9
PK
554}
555
6443eaf2 556void test_online_push__b3(void)
613d5eb9
PK
557{
558 const char *specs[] = { "refs/heads/b3:refs/heads/b3" };
9d41984c 559 push_status exp_stats[] = { { "refs/heads/b3", 1 } };
613d5eb9
PK
560 expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } };
561 do_push(specs, ARRAY_SIZE(specs),
562 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 563 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
613d5eb9
PK
564}
565
6443eaf2 566void test_online_push__b4(void)
613d5eb9
PK
567{
568 const char *specs[] = { "refs/heads/b4:refs/heads/b4" };
9d41984c 569 push_status exp_stats[] = { { "refs/heads/b4", 1 } };
613d5eb9
PK
570 expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } };
571 do_push(specs, ARRAY_SIZE(specs),
572 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 573 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
613d5eb9
PK
574}
575
6443eaf2 576void test_online_push__b5(void)
613d5eb9
PK
577{
578 const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
9d41984c 579 push_status exp_stats[] = { { "refs/heads/b5", 1 } };
613d5eb9
PK
580 expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } };
581 do_push(specs, ARRAY_SIZE(specs),
582 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 583 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
613d5eb9
PK
584}
585
11bd7a03
RB
586void test_online_push__b5_cancel(void)
587{
588 const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
f70cfd34 589 do_push(specs, ARRAY_SIZE(specs), NULL, 0, NULL, 0, GIT_EUSER, 1, 1);
11bd7a03
RB
590}
591
6443eaf2 592void test_online_push__multi(void)
613d5eb9 593{
491cecfe
BS
594 git_reflog *log;
595 const git_reflog_entry *entry;
596
613d5eb9
PK
597 const char *specs[] = {
598 "refs/heads/b1:refs/heads/b1",
599 "refs/heads/b2:refs/heads/b2",
600 "refs/heads/b3:refs/heads/b3",
601 "refs/heads/b4:refs/heads/b4",
602 "refs/heads/b5:refs/heads/b5"
603 };
604 push_status exp_stats[] = {
9d41984c
ET
605 { "refs/heads/b1", 1 },
606 { "refs/heads/b2", 1 },
607 { "refs/heads/b3", 1 },
608 { "refs/heads/b4", 1 },
609 { "refs/heads/b5", 1 }
613d5eb9
PK
610 };
611 expected_ref exp_refs[] = {
612 { "refs/heads/b1", &_oid_b1 },
613 { "refs/heads/b2", &_oid_b2 },
614 { "refs/heads/b3", &_oid_b3 },
615 { "refs/heads/b4", &_oid_b4 },
616 { "refs/heads/b5", &_oid_b5 }
617 };
618 do_push(specs, ARRAY_SIZE(specs),
619 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 620 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
491cecfe
BS
621
622 cl_git_pass(git_reflog_read(&log, _repo, "refs/remotes/test/b1"));
623 entry = git_reflog_entry_byindex(log, 0);
3094102f
BS
624 if (entry) {
625 cl_assert_equal_s("test push", git_reflog_entry_message(entry));
626 cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email);
627 }
491cecfe
BS
628
629 git_reflog_free(log);
613d5eb9
PK
630}
631
6443eaf2 632void test_online_push__implicit_tgt(void)
613d5eb9
PK
633{
634 const char *specs1[] = { "refs/heads/b1:" };
9d41984c 635 push_status exp_stats1[] = { { "refs/heads/b1", 1 } };
613d5eb9
PK
636 expected_ref exp_refs1[] = { { "refs/heads/b1", &_oid_b1 } };
637
638 const char *specs2[] = { "refs/heads/b2:" };
9d41984c 639 push_status exp_stats2[] = { { "refs/heads/b2", 1 } };
613d5eb9
PK
640 expected_ref exp_refs2[] = {
641 { "refs/heads/b1", &_oid_b1 },
642 { "refs/heads/b2", &_oid_b2 }
643 };
644
645 do_push(specs1, ARRAY_SIZE(specs1),
646 exp_stats1, ARRAY_SIZE(exp_stats1),
f70cfd34 647 exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1);
613d5eb9
PK
648 do_push(specs2, ARRAY_SIZE(specs2),
649 exp_stats2, ARRAY_SIZE(exp_stats2),
f70cfd34 650 exp_refs2, ARRAY_SIZE(exp_refs2), 0, 0, 0);
613d5eb9
PK
651}
652
6443eaf2 653void test_online_push__fast_fwd(void)
613d5eb9
PK
654{
655 /* Fast forward b1 in tgt from _oid_b1 to _oid_b6. */
656
657 const char *specs_init[] = { "refs/heads/b1:refs/heads/b1" };
9d41984c 658 push_status exp_stats_init[] = { { "refs/heads/b1", 1 } };
613d5eb9
PK
659 expected_ref exp_refs_init[] = { { "refs/heads/b1", &_oid_b1 } };
660
661 const char *specs_ff[] = { "refs/heads/b6:refs/heads/b1" };
9d41984c 662 push_status exp_stats_ff[] = { { "refs/heads/b1", 1 } };
613d5eb9
PK
663 expected_ref exp_refs_ff[] = { { "refs/heads/b1", &_oid_b6 } };
664
665 /* Do a force push to reset b1 in target back to _oid_b1 */
666 const char *specs_reset[] = { "+refs/heads/b1:refs/heads/b1" };
667 /* Force should have no effect on a fast forward push */
668 const char *specs_ff_force[] = { "+refs/heads/b6:refs/heads/b1" };
669
670 do_push(specs_init, ARRAY_SIZE(specs_init),
671 exp_stats_init, ARRAY_SIZE(exp_stats_init),
f70cfd34 672 exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 1, 1);
613d5eb9
PK
673
674 do_push(specs_ff, ARRAY_SIZE(specs_ff),
675 exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
f70cfd34 676 exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0, 0);
613d5eb9
PK
677
678 do_push(specs_reset, ARRAY_SIZE(specs_reset),
679 exp_stats_init, ARRAY_SIZE(exp_stats_init),
f70cfd34 680 exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 0, 0);
613d5eb9
PK
681
682 do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force),
683 exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
f70cfd34 684 exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0, 0);
613d5eb9
PK
685}
686
abeefbbe
MS
687void test_online_push__tag_commit(void)
688{
689 const char *specs[] = { "refs/tags/tag-commit:refs/tags/tag-commit" };
9d41984c 690 push_status exp_stats[] = { { "refs/tags/tag-commit", 1 } };
abeefbbe
MS
691 expected_ref exp_refs[] = { { "refs/tags/tag-commit", &_tag_commit } };
692 do_push(specs, ARRAY_SIZE(specs),
693 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 694 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
abeefbbe
MS
695}
696
697void test_online_push__tag_tree(void)
698{
699 const char *specs[] = { "refs/tags/tag-tree:refs/tags/tag-tree" };
9d41984c 700 push_status exp_stats[] = { { "refs/tags/tag-tree", 1 } };
abeefbbe
MS
701 expected_ref exp_refs[] = { { "refs/tags/tag-tree", &_tag_tree } };
702 do_push(specs, ARRAY_SIZE(specs),
703 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 704 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
abeefbbe
MS
705}
706
707void test_online_push__tag_blob(void)
708{
709 const char *specs[] = { "refs/tags/tag-blob:refs/tags/tag-blob" };
9d41984c 710 push_status exp_stats[] = { { "refs/tags/tag-blob", 1 } };
abeefbbe
MS
711 expected_ref exp_refs[] = { { "refs/tags/tag-blob", &_tag_blob } };
712 do_push(specs, ARRAY_SIZE(specs),
713 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 714 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
abeefbbe
MS
715}
716
717void test_online_push__tag_lightweight(void)
718{
719 const char *specs[] = { "refs/tags/tag-lightweight:refs/tags/tag-lightweight" };
9d41984c 720 push_status exp_stats[] = { { "refs/tags/tag-lightweight", 1 } };
abeefbbe
MS
721 expected_ref exp_refs[] = { { "refs/tags/tag-lightweight", &_tag_lightweight } };
722 do_push(specs, ARRAY_SIZE(specs),
723 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 724 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
abeefbbe
MS
725}
726
1c13b0bf
ET
727void test_online_push__tag_to_tag(void)
728{
729 const char *specs[] = { "refs/tags/tag-tag:refs/tags/tag-tag" };
9d41984c 730 push_status exp_stats[] = { { "refs/tags/tag-tag", 1 } };
1c13b0bf
ET
731 expected_ref exp_refs[] = { { "refs/tags/tag-tag", &_tag_tag } };
732 do_push(specs, ARRAY_SIZE(specs),
733 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 734 exp_refs, ARRAY_SIZE(exp_refs), 0, 0, 0);
1c13b0bf
ET
735}
736
6443eaf2 737void test_online_push__force(void)
613d5eb9
PK
738{
739 const char *specs1[] = {"refs/heads/b3:refs/heads/tgt"};
9d41984c 740 push_status exp_stats1[] = { { "refs/heads/tgt", 1 } };
613d5eb9
PK
741 expected_ref exp_refs1[] = { { "refs/heads/tgt", &_oid_b3 } };
742
743 const char *specs2[] = {"refs/heads/b4:refs/heads/tgt"};
744
745 const char *specs2_force[] = {"+refs/heads/b4:refs/heads/tgt"};
9d41984c 746 push_status exp_stats2_force[] = { { "refs/heads/tgt", 1 } };
613d5eb9
PK
747 expected_ref exp_refs2_force[] = { { "refs/heads/tgt", &_oid_b4 } };
748
749 do_push(specs1, ARRAY_SIZE(specs1),
750 exp_stats1, ARRAY_SIZE(exp_stats1),
f70cfd34 751 exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1);
613d5eb9
PK
752
753 do_push(specs2, ARRAY_SIZE(specs2),
754 NULL, 0,
f70cfd34 755 exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD, 0, 0);
613d5eb9
PK
756
757 /* Non-fast-forward update with force should pass. */
f70cfd34 758 record_callbacks_data_clear(&_record_cbs_data);
613d5eb9
PK
759 do_push(specs2_force, ARRAY_SIZE(specs2_force),
760 exp_stats2_force, ARRAY_SIZE(exp_stats2_force),
f70cfd34 761 exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0, 1, 1);
613d5eb9
PK
762}
763
6443eaf2 764void test_online_push__delete(void)
613d5eb9
PK
765{
766 const char *specs1[] = {
767 "refs/heads/b1:refs/heads/tgt1",
768 "refs/heads/b1:refs/heads/tgt2"
769 };
770 push_status exp_stats1[] = {
9d41984c
ET
771 { "refs/heads/tgt1", 1 },
772 { "refs/heads/tgt2", 1 }
613d5eb9
PK
773 };
774 expected_ref exp_refs1[] = {
775 { "refs/heads/tgt1", &_oid_b1 },
776 { "refs/heads/tgt2", &_oid_b1 }
777 };
778
779 const char *specs_del_fake[] = { ":refs/heads/fake" };
780 /* Force has no effect for delete. */
781 const char *specs_del_fake_force[] = { "+:refs/heads/fake" };
9d41984c 782 push_status exp_stats_fake[] = { { "refs/heads/fake", 1 } };
613d5eb9
PK
783
784 const char *specs_delete[] = { ":refs/heads/tgt1" };
9d41984c 785 push_status exp_stats_delete[] = { { "refs/heads/tgt1", 1 } };
613d5eb9
PK
786 expected_ref exp_refs_delete[] = { { "refs/heads/tgt2", &_oid_b1 } };
787 /* Force has no effect for delete. */
788 const char *specs_delete_force[] = { "+:refs/heads/tgt1" };
789
790 do_push(specs1, ARRAY_SIZE(specs1),
791 exp_stats1, ARRAY_SIZE(exp_stats1),
f70cfd34 792 exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1);
613d5eb9 793
d73d52df
CW
794 /* When deleting a non-existent branch, the git client sends zero for both
795 * the old and new commit id. This should succeed on the server with the
796 * same status report as if the branch were actually deleted. The server
797 * returns a warning on the side-band iff the side-band is supported.
798 * Since libgit2 doesn't support the side-band yet, there are no warnings.
613d5eb9
PK
799 */
800 do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake),
d73d52df 801 exp_stats_fake, 1,
f70cfd34 802 exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0);
613d5eb9 803 do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force),
d73d52df 804 exp_stats_fake, 1,
f70cfd34 805 exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0);
613d5eb9
PK
806
807 /* Delete one of the pushed branches. */
808 do_push(specs_delete, ARRAY_SIZE(specs_delete),
809 exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
f70cfd34 810 exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0, 0);
613d5eb9
PK
811
812 /* Re-push branches and retry delete with force. */
813 do_push(specs1, ARRAY_SIZE(specs1),
814 exp_stats1, ARRAY_SIZE(exp_stats1),
f70cfd34 815 exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0);
613d5eb9
PK
816 do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force),
817 exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
f70cfd34 818 exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0, 0);
613d5eb9
PK
819}
820
6443eaf2 821void test_online_push__bad_refspecs(void)
613d5eb9
PK
822{
823 /* All classes of refspecs that should be rejected by
824 * git_push_add_refspec() should go in this test.
825 */
826 git_push *push;
827
828 if (_remote) {
11bd7a03 829/* cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); */
613d5eb9
PK
830 cl_git_pass(git_push_new(&push, _remote));
831
832 /* Unexpanded branch names not supported */
833 cl_git_fail(git_push_add_refspec(push, "b6:b6"));
834
835 git_push_free(push);
836 }
837}
838
6443eaf2 839void test_online_push__expressions(void)
613d5eb9
PK
840{
841 /* TODO: Expressions in refspecs doesn't actually work yet */
842 const char *specs_left_expr[] = { "refs/heads/b2~1:refs/heads/b2" };
843
9d41984c
ET
844 /* expect not NULL to indicate failure (core git replies "funny refname",
845 * other servers may be less pithy. */
613d5eb9 846 const char *specs_right_expr[] = { "refs/heads/b2:refs/heads/b2~1" };
9d41984c 847 push_status exp_stats_right_expr[] = { { "refs/heads/b2~1", 0 } };
613d5eb9
PK
848
849 /* TODO: Find a more precise way of checking errors than a exit code of -1. */
850 do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr),
851 NULL, 0,
f70cfd34 852 NULL, 0, -1, 0, 0);
613d5eb9
PK
853
854 do_push(specs_right_expr, ARRAY_SIZE(specs_right_expr),
855 exp_stats_right_expr, ARRAY_SIZE(exp_stats_right_expr),
f70cfd34 856 NULL, 0, 0, 1, 1);
613d5eb9 857}
087f64d3 858
abeefbbe 859void test_online_push__notes(void)
087f64d3
JM
860{
861 git_oid note_oid, *target_oid, expected_oid;
862 git_signature *signature;
863 const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
9d41984c 864 push_status exp_stats[] = { { "refs/notes/commits", 1 } };
087f64d3 865 expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
0f29e967 866 const char *specs_del[] = { ":refs/notes/commits" };
0f29e967 867
087f64d3
JM
868 git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb");
869
870 target_oid = &_oid_b6;
871
872 /* Create note to push */
873 cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
abeefbbe 874 cl_git_pass(git_note_create(&note_oid, _repo, signature, signature, NULL, target_oid, "hello world\n", 0));
087f64d3
JM
875
876 do_push(specs, ARRAY_SIZE(specs),
877 exp_stats, ARRAY_SIZE(exp_stats),
f70cfd34 878 exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
087f64d3 879
0f29e967
ET
880 /* And make sure to delete the note */
881
882 do_push(specs_del, ARRAY_SIZE(specs_del),
883 exp_stats, 1,
aff70018 884 NULL, 0, 0, 0, 0);
0f29e967 885
087f64d3
JM
886 git_signature_free(signature);
887}