Remote: generalize push()
Move to use git_remote_push() instead of doing the steps ourselves. We also change to accept a list of refspecs instead of just the one refspec for the push method. As part of this, we no longer error out if the server rejected any updates, as this is a different concern from whether the push itself failed or not. We do still error out if we attempt to push non-ff updates.
This commit is contained in:
@@ -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);
|
||||
|
@@ -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)')
|
||||
|
@@ -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()
|
||||
|
Reference in New Issue
Block a user