Merge remote-tracking branch 'carlos/development'
Conflicts: pygit2/remote.py
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
cd ~
|
||||
|
||||
git clone --depth=1 -b v0.21.2 https://github.com/libgit2/libgit2.git
|
||||
git clone --depth=1 -b maint/v0.22 https://github.com/libgit2/libgit2.git
|
||||
cd libgit2/
|
||||
|
||||
mkdir build && cd build
|
||||
|
@@ -14,7 +14,7 @@ Requirements
|
||||
============
|
||||
|
||||
- Python 2.7, 3.2+ or pypy (including the development headers)
|
||||
- Libgit2 v0.21.1+
|
||||
- Libgit2 v0.22.x
|
||||
- cffi 0.8.1+
|
||||
- Libssh2, optional, used for SSH network operations.
|
||||
|
||||
@@ -34,11 +34,11 @@ while the last number |lq| *.micro* |rq| auto-increments independently.
|
||||
|
||||
As illustration see this table of compatible releases:
|
||||
|
||||
+-----------+---------------------------------------+------------------------------+--------------+
|
||||
|**libgit2**|0.21.1, 0.21.2 |0.20.0 |0.19.0 |
|
||||
+-----------+---------------------------------------+------------------------------+--------------+
|
||||
|**pygit2** |0.21.0, 0.21.1, 0.21.2, 0.21.3, 0.21.4 |0.20.0, 0.20.1, 0.20.2, 0.20.3|0.19.0, 0.19.1|
|
||||
+-----------+---------------------------------------+------------------------------+--------------+
|
||||
+-----------+--------+----------------------------------------+-------------------------------+
|
||||
|**libgit2**| 0.22.0 | 0.21.1, 0.21.2 |0.20.0 |
|
||||
+-----------+--------+----------------------------------------+-------------------------------+
|
||||
|**pygit2** | 0.22.0 | 0.21.0, 0.21.1, 0.21.2, 0.21.3, 0.21.4 | 0.20.0, 0.20.1, 0.20.2, 0.20.3|
|
||||
+-----------+--------+----------------------------------------+-------------------------------+
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -55,9 +55,9 @@ directory, do:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ wget https://github.com/libgit2/libgit2/archive/v0.21.2.tar.gz
|
||||
$ tar xzf v0.21.2.tar.gz
|
||||
$ cd libgit2-0.21.2/
|
||||
$ wget https://github.com/libgit2/libgit2/archive/v0.22.0.tar.gz
|
||||
$ tar xzf v0.22.0.tar.gz
|
||||
$ cd libgit2-0.22.0/
|
||||
$ cmake .
|
||||
$ make
|
||||
$ sudo make install
|
||||
|
@@ -41,7 +41,7 @@ from .index import Index, IndexEntry
|
||||
from .remote import Remote, get_credentials
|
||||
from .repository import Repository
|
||||
from .settings import Settings
|
||||
from .utils import to_bytes
|
||||
from .utils import to_bytes, to_str
|
||||
from ._utils import __version__
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ def init_repository(path, bare=False,
|
||||
check_error(err)
|
||||
|
||||
# Ok
|
||||
return Repository(path)
|
||||
return Repository(to_str(path))
|
||||
|
||||
|
||||
@ffi.callback('int (*credentials)(git_cred **cred, const char *url,'
|
||||
@@ -123,7 +123,7 @@ def _credentials_cb(cred_out, url, username_from_url, allowed, data):
|
||||
d = ffi.from_handle(data)
|
||||
|
||||
try:
|
||||
ccred = get_credentials(d['callback'], url, username_from_url, allowed)
|
||||
ccred = get_credentials(d['credentials_cb'], url, username_from_url, allowed)
|
||||
cred_out[0] = ccred[0]
|
||||
except Exception as e:
|
||||
d['exception'] = e
|
||||
@@ -131,10 +131,54 @@ def _credentials_cb(cred_out, url, username_from_url, allowed, data):
|
||||
|
||||
return 0
|
||||
|
||||
@ffi.callback('int (*git_repository_create_cb)(git_repository **out,'
|
||||
'const char *path, int bare, void *payload)')
|
||||
def _repository_create_cb(repo_out, path, bare, data):
|
||||
d = ffi.from_handle(data)
|
||||
try:
|
||||
repository = d['repository_cb'](ffi.string(path), bare != 0)
|
||||
# we no longer own the C object
|
||||
repository._disown()
|
||||
repo_out[0] = repository._repo
|
||||
except Exception as e:
|
||||
d['exception'] = e
|
||||
return C.GIT_EUSER
|
||||
|
||||
return 0
|
||||
|
||||
@ffi.callback('int (*git_remote_create_cb)(git_remote **out, git_repository *repo,'
|
||||
'const char *name, const char *url, void *payload)')
|
||||
def _remote_create_cb(remote_out, repo, name, url, data):
|
||||
d = ffi.from_handle(data)
|
||||
try:
|
||||
remote = d['remote_cb'](Repository._from_c(repo, False), ffi.string(name), ffi.string(url))
|
||||
remote_out[0] = remote._remote
|
||||
# we no longer own the C object
|
||||
remote._remote = ffi.NULL
|
||||
except Exception as e:
|
||||
d['exception'] = e
|
||||
return C.GIT_EUSER
|
||||
|
||||
return 0
|
||||
|
||||
@ffi.callback('int (*git_transport_certificate_check_cb)'
|
||||
'(git_cert *cert, int valid, const char *host, void *payload)')
|
||||
def _certificate_cb(cert_i, valid, host, data):
|
||||
d = ffi.from_handle(data)
|
||||
try:
|
||||
# python's parting is deep in the libraries and assumes an OpenSSL-owned cert
|
||||
val = d['certificate_cb'](None, bool(valid), ffi.string(host))
|
||||
if not val:
|
||||
return C.GIT_ECERTIFICATE
|
||||
except Exception as e:
|
||||
d['exception'] = e
|
||||
return C.GIT_EUSER
|
||||
|
||||
return 0
|
||||
|
||||
def clone_repository(
|
||||
url, path, bare=False, ignore_cert_errors=False,
|
||||
remote_name="origin", checkout_branch=None, credentials=None):
|
||||
url, path, bare=False, repository=None, remote=None,
|
||||
checkout_branch=None, credentials=None, certificate=None):
|
||||
"""Clones a new Git repository from *url* in the given *path*.
|
||||
|
||||
Returns a Repository class pointing to the newly cloned repository.
|
||||
@@ -145,7 +189,9 @@ def clone_repository(
|
||||
|
||||
:param bool bare: Whether the local repository should be bare
|
||||
|
||||
:param str remote_name: Name to give the remote at *url*.
|
||||
:param callable remote: Callback for the remote to use.
|
||||
|
||||
:param callable repository: Callback for the repository to use.
|
||||
|
||||
:param str checkout_branch: Branch to checkout after the
|
||||
clone. The default is to use the remote's default branch.
|
||||
@@ -153,8 +199,22 @@ def clone_repository(
|
||||
:param callable credentials: authentication to use if the remote
|
||||
requires it
|
||||
|
||||
:param callable certificate: callback to verify the host's
|
||||
certificate or fingerprint.
|
||||
|
||||
:rtype: Repository
|
||||
|
||||
The repository callback has `(path, bare) -> Repository` as a
|
||||
signature. The Repository it returns will be used instead of
|
||||
creating a new one.
|
||||
|
||||
The remote callback has `(Repository, name, url) -> Remote` as a
|
||||
signature. The Remote it returns will be used instead of the default
|
||||
one.
|
||||
|
||||
The certificate callback has `(cert, valid, hostname) -> bool` as
|
||||
a signature. Return True to accept the connection, False to abort.
|
||||
|
||||
"""
|
||||
|
||||
opts = ffi.new('git_clone_options *')
|
||||
@@ -164,7 +224,10 @@ def clone_repository(
|
||||
|
||||
# Data, let's use a dict as we don't really want much more
|
||||
d = {}
|
||||
d['callback'] = credentials
|
||||
d['credentials_cb'] = credentials
|
||||
d['repository_cb'] = repository
|
||||
d['remote_cb'] = remote
|
||||
d['certificate_cb'] = certificate
|
||||
d_handle = ffi.new_handle(d)
|
||||
|
||||
# Perform the initialization with the version we compiled
|
||||
@@ -176,49 +239,32 @@ def clone_repository(
|
||||
checkout_branch_ref = ffi.new('char []', to_bytes(branch))
|
||||
opts.checkout_branch = checkout_branch_ref
|
||||
|
||||
remote_name_ref = ffi.new('char []', to_bytes(remote_name))
|
||||
opts.remote_name = remote_name_ref
|
||||
if repository:
|
||||
opts.repository_cb = _repository_create_cb
|
||||
opts.repository_cb_payload = d_handle
|
||||
|
||||
if remote:
|
||||
opts.remote_cb = _remote_create_cb
|
||||
opts.remote_cb_payload = d_handle
|
||||
|
||||
|
||||
opts.ignore_cert_errors = ignore_cert_errors
|
||||
opts.bare = bare
|
||||
if credentials:
|
||||
opts.remote_callbacks.credentials = _credentials_cb
|
||||
opts.remote_callbacks.payload = d_handle
|
||||
|
||||
if certificate:
|
||||
opts.remote_callbacks.certificate_check = _certificate_cb
|
||||
opts.remote_callbacks.payload = d_handle
|
||||
|
||||
err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts)
|
||||
C.git_repository_free(crepo[0])
|
||||
|
||||
if 'exception' in d:
|
||||
raise d['exception']
|
||||
|
||||
check_error(err)
|
||||
|
||||
return Repository(path)
|
||||
|
||||
|
||||
def clone_into(repo, remote, branch=None):
|
||||
"""Clone into an empty repository from the specified remote
|
||||
|
||||
:param Repository repo: The empty repository into which to clone
|
||||
|
||||
:param Remote remote: The remote from which to clone
|
||||
|
||||
:param str branch: Branch to checkout after the clone. Pass None
|
||||
to use the remotes's default branch.
|
||||
|
||||
This allows you specify arbitrary repository and remote configurations
|
||||
before performing the clone step itself. E.g. you can replicate git-clone's
|
||||
'--mirror' option by setting a refspec of '+refs/*:refs/*', 'core.mirror'
|
||||
to true and calling this function.
|
||||
"""
|
||||
|
||||
err = C.git_clone_into(repo._repo, remote._remote, ffi.NULL,
|
||||
to_bytes(branch), ffi.NULL)
|
||||
|
||||
if remote._stored_exception:
|
||||
raise remote._stored_exception
|
||||
|
||||
check_error(err)
|
||||
return Repository._from_c(crepo[0], owned=True)
|
||||
|
||||
settings = Settings()
|
||||
|
||||
|
127
pygit2/decl.h
127
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;
|
||||
@@ -112,9 +111,38 @@ typedef enum {
|
||||
GIT_CREDTYPE_SSH_KEY,
|
||||
GIT_CREDTYPE_SSH_CUSTOM,
|
||||
GIT_CREDTYPE_DEFAULT,
|
||||
GIT_CREDTYPE_SSH_INTERACTIVE,
|
||||
GIT_CREDTYPE_USERNAME,
|
||||
...
|
||||
} git_credtype_t;
|
||||
|
||||
typedef enum git_cert_t {
|
||||
GIT_CERT_X509,
|
||||
GIT_CERT_HOSTKEY_LIBSSH2,
|
||||
} git_cert_t;
|
||||
|
||||
typedef enum {
|
||||
GIT_CERT_SSH_MD5 = 1,
|
||||
GIT_CERT_SSH_SHA1 = 2,
|
||||
} git_cert_ssh_t;
|
||||
|
||||
typedef struct {
|
||||
git_cert_t cert_type;
|
||||
git_cert_ssh_t type;
|
||||
unsigned char hash_md5[16];
|
||||
unsigned char hash_sha1[20];
|
||||
} git_cert_hostkey;
|
||||
|
||||
typedef struct {
|
||||
git_cert_t cert_type;
|
||||
void *data;
|
||||
size_t len;
|
||||
} git_cert_x509;
|
||||
|
||||
typedef struct {
|
||||
git_cert_t cert_type;
|
||||
} git_cert;
|
||||
|
||||
typedef int (*git_transport_message_cb)(const char *str, int len, void *data);
|
||||
typedef int (*git_cred_acquire_cb)(
|
||||
git_cred **cred,
|
||||
@@ -123,40 +151,59 @@ typedef int (*git_cred_acquire_cb)(
|
||||
unsigned int allowed_types,
|
||||
void *payload);
|
||||
typedef int (*git_transfer_progress_cb)(const git_transfer_progress *stats, void *payload);
|
||||
typedef int (*git_transport_certificate_check_cb)(git_cert *cert, int valid, const char *host, void *payload);
|
||||
|
||||
typedef int (*git_packbuilder_progress)(
|
||||
int stage,
|
||||
unsigned int current,
|
||||
unsigned int total,
|
||||
void *payload);
|
||||
typedef int (*git_push_transfer_progress)(
|
||||
unsigned int current,
|
||||
unsigned int total,
|
||||
size_t bytes,
|
||||
void* payload);
|
||||
|
||||
struct git_remote_callbacks {
|
||||
unsigned int version;
|
||||
git_transport_message_cb sideband_progress;
|
||||
int (*completion)(git_remote_completion_type type, void *data);
|
||||
git_cred_acquire_cb credentials;
|
||||
git_transport_certificate_check_cb certificate_check;
|
||||
git_transfer_progress_cb transfer_progress;
|
||||
int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
|
||||
git_packbuilder_progress pack_progress;
|
||||
git_push_transfer_progress push_transfer_progress;
|
||||
int (*push_update_reference)(const char *refname, const char *status, void *data);
|
||||
void *payload;
|
||||
};
|
||||
|
||||
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_load(git_remote **out, git_repository *repo, const char *name);
|
||||
int git_remote_lookup(git_remote **out, git_repository *repo, const char *name);
|
||||
int git_remote_create(
|
||||
git_remote **out,
|
||||
git_repository *repo,
|
||||
const char *name,
|
||||
const char *url);
|
||||
int git_remote_delete(git_remote *remote);
|
||||
int git_remote_delete(git_repository *repo, const char *name);
|
||||
int git_repository_state_cleanup(git_repository *repo);
|
||||
|
||||
const char * git_remote_name(const git_remote *remote);
|
||||
|
||||
int git_remote_rename(
|
||||
git_strarray *problems,
|
||||
git_remote *remote,
|
||||
const char *new_name);
|
||||
int git_remote_rename(git_strarray *problems, git_repository *repo, const char *name, const char *new_name);
|
||||
const char * git_remote_url(const git_remote *remote);
|
||||
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_signature *signature, const char *reflog_message);
|
||||
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);
|
||||
@@ -173,22 +220,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_unpack_ok(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);
|
||||
@@ -267,14 +298,12 @@ typedef int (*git_diff_notify_cb)(
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
uint32_t flags;
|
||||
|
||||
git_submodule_ignore_t ignore_submodules;
|
||||
git_strarray pathspec;
|
||||
git_diff_notify_cb notify_cb;
|
||||
void *notify_payload;
|
||||
|
||||
uint16_t context_lines;
|
||||
uint16_t interhunk_lines;
|
||||
uint32_t context_lines;
|
||||
uint32_t interhunk_lines;
|
||||
uint16_t id_abbrev;
|
||||
git_off_t max_size;
|
||||
const char *old_prefix;
|
||||
@@ -345,13 +374,6 @@ typedef struct git_checkout_options {
|
||||
const char *their_label;
|
||||
} git_checkout_options;
|
||||
|
||||
typedef enum {
|
||||
GIT_CLONE_LOCAL_AUTO,
|
||||
GIT_CLONE_LOCAL,
|
||||
GIT_CLONE_NO_LOCAL,
|
||||
GIT_CLONE_LOCAL_NO_LINKS,
|
||||
} git_clone_local_t;
|
||||
|
||||
int git_checkout_init_options(git_checkout_options *opts, unsigned int version);
|
||||
int git_checkout_tree(git_repository *repo, const git_object *treeish, const git_checkout_options *opts);
|
||||
int git_checkout_head(git_repository *repo, const git_checkout_options *opts);
|
||||
@@ -361,18 +383,38 @@ int git_checkout_index(git_repository *repo, git_index *index, const git_checkou
|
||||
* git_clone
|
||||
*/
|
||||
|
||||
typedef int (*git_remote_create_cb)(
|
||||
git_remote **out,
|
||||
git_repository *repo,
|
||||
const char *name,
|
||||
const char *url,
|
||||
void *payload);
|
||||
|
||||
typedef int (*git_repository_create_cb)(
|
||||
git_repository **out,
|
||||
const char *path,
|
||||
int bare,
|
||||
void *payload);
|
||||
|
||||
typedef enum {
|
||||
GIT_CLONE_LOCAL_AUTO,
|
||||
GIT_CLONE_LOCAL,
|
||||
GIT_CLONE_NO_LOCAL,
|
||||
GIT_CLONE_LOCAL_NO_LINKS,
|
||||
} git_clone_local_t;
|
||||
|
||||
typedef struct git_clone_options {
|
||||
unsigned int version;
|
||||
|
||||
git_checkout_options checkout_opts;
|
||||
git_remote_callbacks remote_callbacks;
|
||||
|
||||
int bare;
|
||||
int ignore_cert_errors;
|
||||
git_clone_local_t local;
|
||||
const char *remote_name;
|
||||
const char* checkout_branch;
|
||||
git_signature *signature;
|
||||
git_repository_create_cb repository_cb;
|
||||
void *repository_cb_payload;
|
||||
git_remote_create_cb remote_cb;
|
||||
void *remote_cb_payload;
|
||||
} git_clone_options;
|
||||
|
||||
#define GIT_CLONE_OPTIONS_VERSION ...
|
||||
@@ -383,13 +425,6 @@ int git_clone(git_repository **out,
|
||||
const char *local_path,
|
||||
const git_clone_options *options);
|
||||
|
||||
int git_clone_into(
|
||||
git_repository *repo,
|
||||
git_remote *remote,
|
||||
const git_checkout_options *co_opts,
|
||||
const char *branch,
|
||||
const git_signature *signature);
|
||||
|
||||
/*
|
||||
* git_config
|
||||
*/
|
||||
|
154
pygit2/remote.py
154
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"""
|
||||
|
||||
@@ -137,25 +147,6 @@ class Remote(object):
|
||||
|
||||
return maybe_string(C.git_remote_name(self._remote))
|
||||
|
||||
def rename(self, new_name):
|
||||
"""Rename this remote
|
||||
|
||||
Returns a list of fetch refspecs which were not in the standard format
|
||||
and thus could not be remapped
|
||||
"""
|
||||
|
||||
if not new_name:
|
||||
raise ValueError("New remote name must be a non-empty string")
|
||||
|
||||
problems = ffi.new('git_strarray *')
|
||||
err = C.git_remote_rename(problems, self._remote, to_bytes(new_name))
|
||||
check_error(err)
|
||||
|
||||
ret = strarray_to_strings(problems)
|
||||
C.git_strarray_free(problems)
|
||||
|
||||
return ret
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
"""Url of the remote"""
|
||||
@@ -178,14 +169,6 @@ class Remote(object):
|
||||
err = C.git_remote_set_pushurl(self._remote, to_bytes(value))
|
||||
check_error(err)
|
||||
|
||||
def delete(self):
|
||||
"""Remove this remote
|
||||
|
||||
All remote-tracking branches and configuration settings for the remote will be removed.
|
||||
"""
|
||||
err = C.git_remote_delete(self._remote)
|
||||
check_error(err)
|
||||
|
||||
def save(self):
|
||||
"""save()
|
||||
|
||||
@@ -231,7 +214,7 @@ class Remote(object):
|
||||
self._stored_exception = None
|
||||
|
||||
try:
|
||||
err = C.git_remote_fetch(self._remote, ptr, to_bytes(message))
|
||||
err = C.git_remote_fetch(self._remote, ffi.NULL, ptr, to_bytes(message))
|
||||
if self._stored_exception:
|
||||
raise self._stored_exception
|
||||
|
||||
@@ -306,27 +289,30 @@ 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
|
||||
protocol error or unpack failure.
|
||||
|
||||
Push the given refspec to the remote. Raises ``GitError`` on error.
|
||||
|
||||
: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
|
||||
@@ -334,53 +320,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)
|
||||
|
||||
if not C.git_push_unpack_ok(push):
|
||||
raise GitError("remote failed to unpack objects")
|
||||
|
||||
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
|
||||
@@ -437,6 +393,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)')
|
||||
@@ -528,7 +501,7 @@ class RemoteCollection(object):
|
||||
|
||||
cremote = ffi.new('git_remote **')
|
||||
for i in range(names.count):
|
||||
err = C.git_remote_load(cremote, self._repo._repo, names.strings[i])
|
||||
err = C.git_remote_lookup(cremote, self._repo._repo, names.strings[i])
|
||||
check_error(err)
|
||||
|
||||
yield Remote(self._repo, cremote[0])
|
||||
@@ -540,7 +513,7 @@ class RemoteCollection(object):
|
||||
return list(self)[name]
|
||||
|
||||
cremote = ffi.new('git_remote **')
|
||||
err = C.git_remote_load(cremote, self._repo._repo, to_bytes(name))
|
||||
err = C.git_remote_lookup(cremote, self._repo._repo, to_bytes(name))
|
||||
check_error(err)
|
||||
|
||||
return Remote(self._repo, cremote[0])
|
||||
@@ -557,3 +530,36 @@ class RemoteCollection(object):
|
||||
check_error(err)
|
||||
|
||||
return Remote(self._repo, cremote[0])
|
||||
|
||||
def rename(self, name, new_name):
|
||||
"""rename(name, new_name) -> [str]
|
||||
|
||||
Rename a remote in the configuration. The refspecs in strandard
|
||||
format will be renamed.
|
||||
|
||||
Returns a list of fetch refspecs which were not in the standard format
|
||||
and thus could not be remapped
|
||||
"""
|
||||
|
||||
if not new_name:
|
||||
raise ValueError("Current remote name must be a non-empty string")
|
||||
|
||||
if not new_name:
|
||||
raise ValueError("New remote name must be a non-empty string")
|
||||
|
||||
problems = ffi.new('git_strarray *')
|
||||
err = C.git_remote_rename(problems, self._repo._repo, to_bytes(name), to_bytes(new_name))
|
||||
check_error(err)
|
||||
|
||||
ret = strarray_to_strings(problems)
|
||||
C.git_strarray_free(problems)
|
||||
|
||||
return ret
|
||||
|
||||
def delete(self, name):
|
||||
"""Remove a remote from the configuration
|
||||
|
||||
All remote-tracking branches and configuration settings for the remote will be removed.
|
||||
"""
|
||||
err = C.git_remote_delete(self._repo._repo, to_bytes(name))
|
||||
check_error(err)
|
||||
|
@@ -57,7 +57,18 @@ class Repository(_Repository):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Repository, self).__init__(*args, **kwargs)
|
||||
self._common_init()
|
||||
|
||||
@classmethod
|
||||
def _from_c(cls, ptr, owned):
|
||||
cptr = ffi.new('git_repository **')
|
||||
cptr[0] = ptr
|
||||
repo = cls.__new__(cls)
|
||||
super(cls, repo)._from_c(bytes(ffi.buffer(cptr)[:]), owned)
|
||||
repo._common_init()
|
||||
return repo
|
||||
|
||||
def _common_init(self):
|
||||
self.remotes = RemoteCollection(self)
|
||||
|
||||
# Get the pointer as the contents of a buffer and store it for
|
||||
|
@@ -210,7 +210,6 @@ moduleinit(PyObject* m)
|
||||
ADD_CONSTANT_INT(m, GIT_OBJ_BLOB)
|
||||
ADD_CONSTANT_INT(m, GIT_OBJ_TAG)
|
||||
/* Valid modes for index and tree entries. */
|
||||
ADD_CONSTANT_INT(m, GIT_FILEMODE_NEW)
|
||||
ADD_CONSTANT_INT(m, GIT_FILEMODE_TREE)
|
||||
ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB)
|
||||
ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB_EXECUTABLE)
|
||||
@@ -347,7 +346,7 @@ moduleinit(PyObject* m)
|
||||
ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_UNBORN)
|
||||
|
||||
/* Global initialization of libgit2 */
|
||||
git_threads_init();
|
||||
git_libgit2_init();
|
||||
|
||||
return m;
|
||||
}
|
||||
|
@@ -55,6 +55,9 @@ extern PyTypeObject ReferenceType;
|
||||
extern PyTypeObject NoteType;
|
||||
extern PyTypeObject NoteIterType;
|
||||
|
||||
/* forward-declaration for Repsository._from_c() */
|
||||
PyTypeObject RepositoryType;
|
||||
|
||||
git_otype
|
||||
int_to_loose_object_type(int type_id)
|
||||
{
|
||||
@@ -88,19 +91,62 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds)
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->owned = 1;
|
||||
self->config = NULL;
|
||||
self->index = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Repository__from_c__doc__, "Init a Repository from a pointer. For internal use only.");
|
||||
PyObject *
|
||||
Repository__from_c(Repository *py_repo, PyObject *args)
|
||||
{
|
||||
PyObject *py_pointer, *py_free;
|
||||
char *buffer;
|
||||
Py_ssize_t len;
|
||||
int err;
|
||||
|
||||
py_repo->repo = NULL;
|
||||
py_repo->config = NULL;
|
||||
py_repo->index = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OO!", &py_pointer, &PyBool_Type, &py_free))
|
||||
return NULL;
|
||||
|
||||
err = PyBytes_AsStringAndSize(py_pointer, &buffer, &len);
|
||||
if (err < 0)
|
||||
return NULL;
|
||||
|
||||
if (len != sizeof(git_repository *)) {
|
||||
PyErr_SetString(PyExc_TypeError, "invalid pointer length");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
py_repo->repo = *((git_repository **) buffer);
|
||||
py_repo->owned = py_free == Py_True;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Repository__disown__doc__, "Mark the object as not-owned by us. For internal use only.");
|
||||
PyObject *
|
||||
Repository__disown(Repository *py_repo)
|
||||
{
|
||||
py_repo->owned = 0;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
Repository_dealloc(Repository *self)
|
||||
{
|
||||
PyObject_GC_UnTrack(self);
|
||||
Py_CLEAR(self->index);
|
||||
Py_CLEAR(self->config);
|
||||
git_repository_free(self->repo);
|
||||
|
||||
if (self->owned)
|
||||
git_repository_free(self->repo);
|
||||
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
@@ -526,7 +572,7 @@ Repository_merge_analysis(Repository *self, PyObject *py_id)
|
||||
int err;
|
||||
size_t len;
|
||||
git_oid id;
|
||||
git_merge_head *merge_head;
|
||||
git_annotated_commit *commit;
|
||||
git_merge_analysis_t analysis;
|
||||
git_merge_preference_t preference;
|
||||
|
||||
@@ -534,12 +580,12 @@ Repository_merge_analysis(Repository *self, PyObject *py_id)
|
||||
if (len == 0)
|
||||
return NULL;
|
||||
|
||||
err = git_merge_head_from_id(&merge_head, self->repo, &id);
|
||||
err = git_annotated_commit_lookup(&commit, self->repo, &id);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
err = git_merge_analysis(&analysis, &preference, self->repo, (const git_merge_head **) &merge_head, 1);
|
||||
git_merge_head_free(merge_head);
|
||||
err = git_merge_analysis(&analysis, &preference, self->repo, (const git_annotated_commit **) &commit, 1);
|
||||
git_annotated_commit_free(commit);
|
||||
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
@@ -561,7 +607,7 @@ PyDoc_STRVAR(Repository_merge__doc__,
|
||||
PyObject *
|
||||
Repository_merge(Repository *self, PyObject *py_oid)
|
||||
{
|
||||
git_merge_head *oid_merge_head;
|
||||
git_annotated_commit *commit;
|
||||
git_oid oid;
|
||||
int err;
|
||||
size_t len;
|
||||
@@ -572,16 +618,16 @@ Repository_merge(Repository *self, PyObject *py_oid)
|
||||
if (len == 0)
|
||||
return NULL;
|
||||
|
||||
err = git_merge_head_from_id(&oid_merge_head, self->repo, &oid);
|
||||
err = git_annotated_commit_lookup(&commit, self->repo, &oid);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
|
||||
err = git_merge(self->repo,
|
||||
(const git_merge_head **)&oid_merge_head, 1,
|
||||
(const git_annotated_commit **)&commit, 1,
|
||||
&merge_opts, &checkout_opts);
|
||||
|
||||
git_merge_head_free(oid_merge_head);
|
||||
git_annotated_commit_free(commit);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
@@ -1225,7 +1271,7 @@ Repository_TreeBuilder(Repository *self, PyObject *args)
|
||||
}
|
||||
}
|
||||
|
||||
err = git_treebuilder_create(&bld, tree);
|
||||
err = git_treebuilder_new(&bld, self->repo, tree);
|
||||
if (must_free != NULL)
|
||||
git_tree_free(must_free);
|
||||
|
||||
@@ -1318,8 +1364,8 @@ Repository_create_note(Repository *self, PyObject* args)
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
err = git_note_create(¬e_id, self->repo, py_author->signature,
|
||||
py_committer->signature, ref,
|
||||
err = git_note_create(¬e_id, self->repo, ref, py_author->signature,
|
||||
py_committer->signature,
|
||||
&annotated_id, message, force);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
@@ -1380,7 +1426,7 @@ Repository_reset(Repository *self, PyObject* args)
|
||||
|
||||
err = git_object_lookup_prefix(&target, self->repo, &oid, len,
|
||||
GIT_OBJ_ANY);
|
||||
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL, NULL);
|
||||
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL, NULL, NULL);
|
||||
git_object_free(target);
|
||||
if (err < 0)
|
||||
return Error_set_oid(err, &oid, len);
|
||||
@@ -1415,6 +1461,8 @@ PyMethodDef Repository_methods[] = {
|
||||
METHOD(Repository, listall_branches, METH_VARARGS),
|
||||
METHOD(Repository, create_branch, METH_VARARGS),
|
||||
METHOD(Repository, reset, METH_VARARGS),
|
||||
METHOD(Repository, _from_c, METH_VARARGS),
|
||||
METHOD(Repository, _disown, METH_NOARGS),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
@@ -88,7 +88,7 @@ TreeBuilder_write(TreeBuilder *self)
|
||||
int err;
|
||||
git_oid oid;
|
||||
|
||||
err = git_treebuilder_write(&oid, self->repo->repo, self->bld);
|
||||
err = git_treebuilder_write(&oid, self->bld);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
|
@@ -32,8 +32,8 @@
|
||||
#include <Python.h>
|
||||
#include <git2.h>
|
||||
|
||||
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 21)
|
||||
#error You need a compatible libgit2 version (v0.21.x)
|
||||
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 22)
|
||||
#error You need a compatible libgit2 version (v0.22.x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -47,6 +47,7 @@ typedef struct {
|
||||
git_repository *repo;
|
||||
PyObject *index; /* It will be None for a bare repository */
|
||||
PyObject *config; /* It will be None for a bare repository */
|
||||
int owned; /* _from_c() sometimes means we don't own the C pointer */
|
||||
} Repository;
|
||||
|
||||
|
||||
|
@@ -71,19 +71,19 @@ class RepositoryTest(utils.RepoTestCase):
|
||||
remote = self.repo.remotes[1]
|
||||
|
||||
self.assertEqual(name, remote.name)
|
||||
remote.delete()
|
||||
self.repo.remotes.delete(remote.name)
|
||||
self.assertEqual(1, len(self.repo.remotes))
|
||||
|
||||
def test_remote_rename(self):
|
||||
remote = self.repo.remotes[0]
|
||||
|
||||
self.assertEqual(REMOTE_NAME, remote.name)
|
||||
problems = remote.rename('new')
|
||||
problems = self.repo.remotes.rename(remote.name, "new")
|
||||
self.assertEqual([], problems)
|
||||
self.assertEqual('new', remote.name)
|
||||
self.assertNotEqual('new', remote.name)
|
||||
|
||||
self.assertRaises(ValueError, remote.rename, '')
|
||||
self.assertRaises(ValueError, remote.rename, None)
|
||||
self.assertRaises(ValueError, self.repo.remotes.rename, '', '')
|
||||
self.assertRaises(ValueError, self.repo.remotes.rename, None, None)
|
||||
|
||||
|
||||
def test_remote_set_url(self):
|
||||
@@ -183,13 +183,9 @@ class RepositoryTest(utils.RepoTestCase):
|
||||
|
||||
def test_remote_save(self):
|
||||
remote = self.repo.remotes[0]
|
||||
|
||||
remote.rename('new-name')
|
||||
remote.url = 'http://example.com/test.git'
|
||||
|
||||
remote.save()
|
||||
|
||||
self.assertEqual('new-name', self.repo.remotes[0].name)
|
||||
self.assertEqual('http://example.com/test.git',
|
||||
self.repo.remotes[0].url)
|
||||
|
||||
@@ -278,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)
|
||||
@@ -298,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()
|
||||
|
@@ -41,7 +41,7 @@ import sys
|
||||
|
||||
# Import from pygit2
|
||||
from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT
|
||||
from pygit2 import init_repository, clone_repository, clone_into, discover_repository
|
||||
from pygit2 import init_repository, clone_repository, discover_repository
|
||||
from pygit2 import Oid, Reference, hashfile
|
||||
import pygit2
|
||||
from . import utils
|
||||
@@ -440,19 +440,22 @@ class CloneRepositoryTest(utils.NoRepoTestCase):
|
||||
self.assertFalse(repo.is_empty)
|
||||
self.assertTrue(repo.is_bare)
|
||||
|
||||
def test_clone_remote_name(self):
|
||||
repo_path = "./test/data/testrepo.git/"
|
||||
repo = clone_repository(
|
||||
repo_path, self._temp_dir, remote_name="custom_remote")
|
||||
self.assertFalse(repo.is_empty)
|
||||
self.assertEqual(repo.remotes[0].name, "custom_remote")
|
||||
def test_clone_repository_and_remote_callbacks(self):
|
||||
src_repo_relpath = "./test/data/testrepo.git/"
|
||||
repo_path = os.path.join(self._temp_dir, "clone-into")
|
||||
url = 'file://' + os.path.realpath(src_repo_relpath)
|
||||
|
||||
def test_clone_into(self):
|
||||
repo_path = "./test/data/testrepo.git/"
|
||||
repo = init_repository(os.path.join(self._temp_dir, "clone-into"))
|
||||
remote = repo.create_remote("origin", 'file://' + os.path.realpath(repo_path))
|
||||
clone_into(repo, remote)
|
||||
self.assertTrue('refs/remotes/origin/master' in repo.listall_references())
|
||||
def create_repository(path, bare):
|
||||
return init_repository(path, bare)
|
||||
|
||||
# here we override the name
|
||||
def create_remote(repo, name, url):
|
||||
return repo.remotes.create("custom_remote", url)
|
||||
|
||||
repo = clone_repository(url, repo_path, repository=create_repository, remote=create_remote)
|
||||
self.assertFalse(repo.is_empty)
|
||||
self.assertTrue('refs/remotes/custom_remote/master' in repo.listall_references())
|
||||
self.assertIsNotNone(repo.remotes["custom_remote"])
|
||||
|
||||
def test_clone_with_credentials(self):
|
||||
credentials = pygit2.UserPass("libgit2", "libgit2")
|
||||
|
Reference in New Issue
Block a user