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