diff --git a/pygit2/decl.h b/pygit2/decl.h index 7af2bd6..b1fd28f 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -1,7 +1,6 @@ typedef ... git_repository; typedef ... git_remote; typedef ... git_refspec; -typedef ... git_push; typedef ... git_cred; typedef ... git_object; typedef ... git_tree; @@ -175,6 +174,11 @@ struct git_remote_callbacks { typedef struct git_remote_callbacks git_remote_callbacks; +typedef struct { + unsigned int version; + unsigned int pb_parallelism; +} git_push_options; + int git_remote_list(git_strarray *out, git_repository *repo); int git_remote_lookup(git_remote **out, git_repository *repo, const char *name); int git_remote_create( @@ -193,6 +197,7 @@ int git_remote_set_url(git_remote *remote, const char* url); const char * git_remote_pushurl(const git_remote *remote); int git_remote_set_pushurl(git_remote *remote, const char* url); int git_remote_fetch(git_remote *remote, const git_strarray *refspecs, const git_signature *signature, const char *reflog_message); +int git_remote_push(git_remote *remote, git_strarray *refspecs, const git_push_options *opts, const git_signature *signature, const char *reflog_message); const git_transfer_progress * git_remote_stats(git_remote *remote); int git_remote_add_push(git_remote *remote, const char *refspec); int git_remote_add_fetch(git_remote *remote, const char *refspec); @@ -209,21 +214,6 @@ int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array); void git_remote_free(git_remote *remote); -int git_push_new(git_push **push, git_remote *remote); -int git_push_add_refspec(git_push *push, const char *refspec); -int git_push_finish(git_push *push); - -int git_push_status_foreach( - git_push *push, - int (*cb)(const char *ref, const char *msg, void *data), - void *data); - -int git_push_update_tips( - git_push *push, - const git_signature *signature, - const char *reflog_message); -void git_push_free(git_push *push); - const char * git_refspec_src(const git_refspec *refspec); const char * git_refspec_dst(const git_refspec *refspec); int git_refspec_force(const git_refspec *refspec); diff --git a/pygit2/remote.py b/pygit2/remote.py index 1385e48..af5a8b4 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -121,6 +121,16 @@ class Remote(object): :param Oid new: the reference's new value """ + def push_update_reference(self, refname, message): + """Push update reference callback + + Override with your own function to report the remote's + acceptace or rejection of reference updates. + + :param str refname: the name of the reference (on the remote) + :param str messsage: rejection message from the remote. If None, the update was accepted. + """ + def __init__(self, repo, ptr): """The constructor is for internal use only""" @@ -279,28 +289,31 @@ class Remote(object): err = C.git_remote_add_push(self._remote, to_bytes(spec)) check_error(err) - @ffi.callback("int (*cb)(const char *ref, const char *msg, void *data)") - def _push_cb(ref, msg, data): - self = ffi.from_handle(data) - if msg: - self._bad_message = ffi.string(msg).decode() - return 0 + def push(self, specs, signature=None, message=None): + """push(specs, signature, message) - def push(self, spec, signature=None, message=None): - """push(refspec, signature, message) - - Push the given refspec to the remote. Raises ``GitError`` on error. + Push the given refspec to the remote. Raises ``GitError`` on + protocol error or unpack failure. May require libssh2. - :param str spec: push refspec to use + :param [str] specs: push refspecs to use :param Signature signature: signature to use when updating the tips :param str message: message to use when updating the tips + """ # Get the default callbacks first defaultcallbacks = ffi.new('git_remote_callbacks *') err = C.git_remote_init_callbacks(defaultcallbacks, 1) check_error(err) + refspecs, refspecs_refs = strings_to_strarray(specs) + if signature: + sig_cptr = ffi.new('git_signature **') + ffi.buffer(sig_cptr)[:] = signature._pointer[:] + sig_ptr = sig_cptr[0] + else: + sig_ptr = ffi.NULL + # Build custom callback structure callbacks = ffi.new('git_remote_callbacks *') callbacks.version = 1 @@ -308,50 +321,23 @@ class Remote(object): callbacks.transfer_progress = self._transfer_progress_cb callbacks.update_tips = self._update_tips_cb callbacks.credentials = self._credentials_cb + callbacks.push_update_reference = self._push_update_reference_cb # We need to make sure that this handle stays alive self._self_handle = ffi.new_handle(self) callbacks.payload = self._self_handle - err = C.git_remote_set_callbacks(self._remote, callbacks) - try: + err = C.git_remote_set_callbacks(self._remote, callbacks) check_error(err) except: self._self_handle = None raise - - cpush = ffi.new('git_push **') - err = C.git_push_new(cpush, self._remote) - check_error(err) - - push = cpush[0] - try: - err = C.git_push_add_refspec(push, to_bytes(spec)) + err = C.git_remote_push(self._remote, refspecs, ffi.NULL, sig_ptr, to_bytes(message)) check_error(err) - - err = C.git_push_finish(push) - check_error(err) - - err = C.git_push_status_foreach(push, self._push_cb, - ffi.new_handle(self)) - check_error(err) - - if hasattr(self, '_bad_message'): - raise GitError(self._bad_message) - - if signature: - ptr = signature._pointer[:] - else: - ptr = ffi.NULL - - err = C.git_push_update_tips(push, ptr, to_bytes(message)) - check_error(err) - finally: self._self_handle = None - C.git_push_free(push) # These functions exist to be called by the git_remote as # callbacks. They proxy the call to whatever the user set @@ -408,6 +394,23 @@ class Remote(object): return 0 + @ffi.callback("int (*push_update_reference)(const char *ref, const char *msg, void *data)") + def _push_update_reference_cb(ref, msg, data): + self = ffi.from_handle(data) + + if not hasattr(self, 'push_update_reference') or not self.push_update_reference: + return 0 + + try: + refname = ffi.string(ref) + message = maybe_string(msg) + self.push_update_reference(refname, message) + except Exception as e: + self._stored_exception = e + return C.GIT_EUSER + + return 0 + @ffi.callback('int (*credentials)(git_cred **cred, const char *url,' 'const char *username_from_url, unsigned int allowed_types,' 'void *data)') diff --git a/test/test_remote.py b/test/test_remote.py index be50b77..f825cd6 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -274,11 +274,11 @@ class PushTestCase(unittest.TestCase): 'refs/heads/master', tip.author, tip.author, 'empty commit', tip.tree.id, [tip.id] ) - self.remote.push('refs/heads/master') + self.remote.push(['refs/heads/master']) self.assertEqual(self.origin[self.origin.head.target].id, oid) def test_push_when_up_to_date_succeeds(self): - self.remote.push('refs/heads/master') + self.remote.push(['refs/heads/master']) origin_tip = self.origin[self.origin.head.target].id clone_tip = self.clone[self.clone.head.target].id self.assertEqual(origin_tip, clone_tip) @@ -294,7 +294,8 @@ class PushTestCase(unittest.TestCase): 'refs/heads/master', tip.author, tip.author, 'other commit', tip.tree.id, [tip.id] ) - self.assertRaises(pygit2.GitError, self.remote.push, 'refs/heads/master') + + self.assertRaises(pygit2.GitError, self.remote.push, ['refs/heads/master']) if __name__ == '__main__': unittest.main()