diff --git a/README.rst b/README.rst index 8679049..b0bce57 100644 --- a/README.rst +++ b/README.rst @@ -20,9 +20,9 @@ Pygit2 links: Quick install guide =================== -1. Checkout libgi2 v0.18.0:: +1. Checkout the libgit2 stable branch:: - $ git clone git://github.com/libgit2/libgit2.git -b v0.18.0 + $ git clone git://github.com/libgit2/libgit2.git -b master 2. Build and install libgit2 https://github.com/libgit2/libgit2/#building-libgit2---using-cmake diff --git a/docs/general.rst b/docs/general.rst new file mode 100644 index 0000000..bf0b3c1 --- /dev/null +++ b/docs/general.rst @@ -0,0 +1,24 @@ +********************************************************************** +General +********************************************************************** + +.. contents:: Contents + :local: + + +Constants +========= + +.. py:data:: LIBGIT2_VER_MAJOR +.. py:data:: LIBGIT2_VER_MINOR +.. py:data:: LIBGIT2_VER_REVISION +.. py:data:: LIBGIT2_VER_VERSION + + +Errors +====== + +.. autoexception:: pygit2.GitError + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/index.rst b/docs/index.rst index daba596..dcd3ed6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,32 +22,27 @@ Pygit2 links: Start: .. toctree:: - :maxdepth: 2 + :maxdepth: 1 install Usage guide: .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + general repository + oid objects references revparse log working-copy diff + merge config remotes - errors - -More: - -.. toctree:: - :maxdepth: 1 - - utils Indices and tables diff --git a/docs/install.rst b/docs/install.rst index 6cde5b5..b18d908 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -6,8 +6,10 @@ How to Install .. contents:: -First you need to install the latest version of libgit2. You can find -platform-specific instructions to build the library in the libgit2 website: +First you need to install the latest release of libgit2. If you clone +the repository, make sure to use the ``master`` branch. You can find +platform-specific instructions to build the library in the libgit2 +website: http://libgit2.github.com @@ -55,7 +57,7 @@ instructions in the libgit2 ``README.md``): .. code-block:: sh - $ git clone git://github.com/libgit2/libgit2.git + $ git clone -b master git://github.com/libgit2/libgit2.git $ mkdir libgit2/build $ cd libgit2/build $ cmake .. @@ -105,7 +107,7 @@ from a bash shell: .. code-block:: sh $ export LIBGIT2=C:/Dev/libgit2 - $ git clone git://github.com/libgit2/libgit2.git + $ git clone -b master git://github.com/libgit2/libgit2.git $ cd libgit2 $ mkdir build $ cd build diff --git a/docs/errors.rst b/docs/merge.rst similarity index 59% rename from docs/errors.rst rename to docs/merge.rst index 6a3715a..55e411a 100644 --- a/docs/errors.rst +++ b/docs/merge.rst @@ -1,8 +1,5 @@ ********************************************************************** -Errors +Merge ********************************************************************** -.. autoexception:: pygit2.GitError - :members: - :show-inheritance: - :undoc-members: +.. automethod:: pygit2.Repository.merge_base diff --git a/docs/objects.rst b/docs/objects.rst index dd849bb..04f466e 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -1,92 +1,80 @@ ********************************************************************** -Git objects +Git Objects ********************************************************************** +There are four types of Git objects: blobs, trees, commits and tags. For each +one pygit2 has a type, and all four types inherit from the base ``Object`` +type. + + .. contents:: Contents :local: -In the first place Git is a key-value storage system. The keys are called -OIDs, for Object id, and the values stored are called Objects. -Oids +Object lookup ================= -The oid is the `SHA-1 `_ hash of an -object. It is 20 bytes long: +In the previous chapter we learnt about Object IDs. With an oid we can ask the +repository to get the associated object. To do that the ``Repository`` class +implementes a subset of the mapping interface. -- When we represent an oid as a 20 bytes Python string, we say it is a raw - oid. +.. method:: Repository.get(oid, default=None) -- When we represent an oid as a 40 chars Python string, we sayt it is a hex - oid. + Return the Git object for the given *oid*, returns the *default* value if + there's no object in the repository with that oid. The oid can be an Oid + object, or an hexadecimal string. -However, most of the time we will use the Oid type. We can explicetly create -an Oid object from its raw or hexadecimal form:: + Example:: - >>> hex = "cff3ceaefc955f0dbe1957017db181bc49913781" - >>> oid1 = Oid(hex=hex) + >>> from pygit2 import Repository + >>> repo = Repository('path/to/pygit2') + >>> obj = repo.get("101715bf37440d32291bde4f58c3142bcf7d8adb") + >>> obj + <_pygit2.Commit object at 0x7ff27a6b60f0> - >>> from binascii import unhexlify - >>> raw = unhexlify(hex) - >>> oid2 = Oid(raw=raw) +.. method:: Repository[oid] - >>> print oid1 == oid2 + Return the Git object for the given oid, raise ``KeyError`` if there's no + object in the repository with that oid. The oid can be an Oid object, or + an hexadecimal string. + +.. method:: oid in Repository + + Returns True if there is an object in the Repository with that oid, False + if there is not. The oid can be an Oid object, or an hexadecimal string. + + +The Object base type +==================== + +The Object type is a base type, it is not possible to make instances of it, in +any way. + +It is the base type of the ``Blob``, ``Tree``, ``Commit`` and ``Tag`` types, so +it is possible to check whether a Python value is an Object or not:: + + >>> from pygit2 import Object + >>> commit = repository.revparse_single('HEAD') + >>> print isinstance(commit, Object) True -And in the opposite direction, we can get the raw or hexadecimal form from -an Oid object: +All Objects are immutable, they cannot be modified once they are created:: -.. autoattribute:: pygit2.Oid.raw -.. autoattribute:: pygit2.Oid.hex + >>> commit.message = u"foobar" + Traceback (most recent call last): + File "", line 1, in + AttributeError: attribute 'message' of '_pygit2.Commit' objects is not writable -The Oid type supports: +Derived types (blobs, trees, etc.) don't have a constructor, this means they +cannot be created with the common idiom:: -- rich comparisons, not just for equality, also: lesser-than, lesser-or-equal, - etc. + >>> from pygit2 import Blob + >>> blob = Blob("data") + Traceback (most recent call last): + File "", line 1, in + TypeError: cannot create '_pygit2.Blob' instances -- hashing, so Oid objects can be used as keys in a dictionary - - -Python 2 and Python 3 ---------------------- - -There is a difference on how the library handles hex oids, depending on -whether we are using Python 2 or 3. - -- In Python 2, we can represent an hexadecimal oid using a bytes string - (``str``) or a text string (``unicode``) - -- In Python 3, hexadecimal oids can only be represented using unicode - strings. - - -Objects -================= - -There are four types (commits, trees, blobs and tags), for each type pygit2 -has a Python class:: - - >>> # Show commits and trees - >>> commit - - >>> commit.tree - - -These four classes (``Commit``, ``Tree``, ``Blob`` and ``Tag``) inherit from -the ``Object`` base class, which provides shared behaviour. A Git object is -identified by a unique *object id*, which is a binary byte string; this is -often represented as an hexadecimal text string:: - - >>> commit.oid - b'x\xde\xb5W\x8d\x01<\xdb\xdf\x08o\xa1\xd1\xa3\xe7\xd9\x82\xe8\x88\x8f' - >>> commit.hex - '78deb5578d013cdbdf086fa1d1a3e7d982e8888f' - -The API of pygit2 accepts both the raw object id and its hexadecimal -representation, the difference is done based on its type (a byte or a text -string). - -Objects can not be modified once they have been created. +New objects are created using an specific API we will see later. This is the common interface for all Git objects: @@ -96,6 +84,126 @@ This is the common interface for all Git objects: .. automethod:: pygit2.Object.read_raw +Blobs +================= + +A blob is just a raw byte string. They are the Git equivalent to files in +a filesytem. + +This is their API: + +.. autoattribute:: pygit2.Blob.data + + Example, print the contents of the ``.gitignore`` file:: + + >>> blob = repo["d8022420bf6db02e906175f64f66676df539f2fd"] + >>> print blob.data + MANIFEST + build + dist + +.. autoattribute:: pygit2.Blob.size + + Example:: + + >>> print blob.size + 130 + +Creating blobs +-------------- + +There are a number of methods in the repository to create new blobs, and add +them to the Git object database: + +.. automethod:: pygit2.Repository.create_blob + + Example: + + >>> oid = repo.create_blob('foo bar') # Creates blob from bytes string + >>> blob = repo[oid] + >>> blob.data + 'foo bar' + +.. automethod:: pygit2.Repository.create_blob_fromworkdir +.. automethod:: pygit2.Repository.create_blob_fromdisk + +There are also some functions to calculate the oid for a byte string without +creating the blob object: + +.. autofunction:: pygit2.hash +.. autofunction:: pygit2.hashfile + + +Trees +================= + +A tree is a sorted collection of tree entries. It is similar to a folder or +directory in a file system. Each entry points to another tree or a blob. A +tree can be iterated, and partially implements the sequence and mapping +interfaces. + +.. method:: Tree[name] + + Return the TreeEntry object for the given *name*. Raise ``KeyError`` if + there is not a tree entry with that name. + +.. method:: name in Tree + + Return True if there is a tree entry with the given name, False otherwise. + +.. method:: len(Tree) + + Return the number of entries in the tree. + +.. method:: iter(Tree) + + Return an iterator over the entries of the tree. + +.. automethod:: pygit2.Tree.diff + +Tree entries +------------ + +.. autoattribute:: pygit2.TreeEntry.name +.. autoattribute:: pygit2.TreeEntry.oid +.. autoattribute:: pygit2.TreeEntry.hex +.. autoattribute:: pygit2.TreeEntry.filemode + +Example:: + + >>> tree = commit.tree + >>> len(tree) # Number of entries + 6 + + >>> for entry in tree: # Iteration + ... print(entry.hex, entry.name) + ... + 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore + c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING + 32b30b90b062f66957d6790c3c155c289c34424e README.md + c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c + 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py + 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test + + >>> entry = tree['pygit2.c'] # Get an entry by name + >>> entry + + + >>> blob = repo[entry.oid] # Get the object the entry points to + >>> blob + + +Creating trees +-------------------- + +.. automethod:: pygit2.Repository.TreeBuilder + +.. automethod:: pygit2.TreeBuilder.insert +.. automethod:: pygit2.TreeBuilder.remove +.. automethod:: pygit2.TreeBuilder.clear +.. automethod:: pygit2.TreeBuilder.write + + Commits ================= @@ -147,82 +255,6 @@ repository with the following parameters:: '#\xe4>> # Number of entries - >>> tree = commit.tree - >>> len(tree) - 6 - - >>> # Iteration - >>> for entry in tree: - ... print(entry.hex, entry.name) - ... - 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore - c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING - 32b30b90b062f66957d6790c3c155c289c34424e README.md - c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c - 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py - 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test - - >>> # Get an entry by name - >>> entry = tree['pygit2.c'] - >>> entry - - - >>> # Get the object the entry points to - >>> blob = repo[entry.oid] - >>> blob - - -.. automethod:: pygit2.Tree.diff - -.. autoattribute:: pygit2.TreeEntry.name -.. autoattribute:: pygit2.TreeEntry.oid -.. autoattribute:: pygit2.TreeEntry.hex -.. autoattribute:: pygit2.TreeEntry.filemode - - -Creating trees --------------------- - -.. automethod:: pygit2.Repository.TreeBuilder - -.. automethod:: pygit2.TreeBuilder.insert -.. automethod:: pygit2.TreeBuilder.remove -.. automethod:: pygit2.TreeBuilder.clear -.. automethod:: pygit2.TreeBuilder.write - - -Blobs -================= - -A blob is equivalent to a file in a file system.:: - - >>> # create a blob out of memory - >>> oid = repo.create_blob('foo bar') - >>> blob = repo[oid] - >>> blob.data - 'foo bar' - >>> oid - '\x96\xc9\x06um{\x91\xc4S"a|\x92\x95\xe4\xa8\rR\xd1\xc5' - -.. autoattribute:: pygit2.Blob.data -.. autoattribute:: pygit2.Blob.size - -Creating blobs --------------------- - -.. automethod:: pygit2.Repository.create_blob -.. automethod:: pygit2.Repository.create_blob_fromworkdir -.. automethod:: pygit2.Repository.create_blob_fromdisk - Tags ================= diff --git a/docs/oid.rst b/docs/oid.rst new file mode 100644 index 0000000..3df5d3a --- /dev/null +++ b/docs/oid.rst @@ -0,0 +1,84 @@ +********************************************************************** +Object IDs +********************************************************************** + +In the first place Git is a key-value storage system. The keys are called +OIDs, for Object ID, and the values stored are called Objects. + +.. contents:: Contents + :local: + + +The three forms of an object id +=============================== + +The oid is the `SHA-1 `_ hash of an +object. It is 20 bytes long. + +These are the three forms of an oid in pygit2: + +Raw oid + A raw oid is represented as a Python byte string of 20 bytes length. + This form can only be used to create an Oid object. + +Hex oid + A hex oid is represented as a Python string of 40 hexadecimal chars. This + form can be used to create Oid objects, just like raw oids. Also, the pygit2 + API directly accepts hex oids everywhere. + + .. note:: + + In Python 3 hexadecimal oids are represented using the ``str`` type. + In Python 2 both ``str`` and ``unicode`` are accepted. + +Oid object + An ``Oid`` object can be built from the raw or hexadecimal representations + (see below). The pygit2 API always returns, and accepts, ``Oid`` objects. + + This is the preferred way to represent an Oid, with the hexadecimal form + being used for interaction with the user. + + +The Oid type +============ + +.. c:type:: pygit2.Oid(raw=None, hex=None) + + The constructor expects either a raw or a hex oid, but not both. + + An Oid object is created from the hexadecimal form this way:: + + >>> from pygit2 import Oid + + >>> hex = "cff3ceaefc955f0dbe1957017db181bc49913781" + >>> oid1 = Oid(hex=hex) + + An Oid object is created from the raw form this way:: + + >>> from binascii import unhexlify + >>> from pygit2 import Oid + + >>> raw = unhexlify("cff3ceaefc955f0dbe1957017db181bc49913781") + >>> oid2 = Oid(raw=raw) + +An the other way around, from an Oid object we can get the hexadecimal and raw +forms. + +.. autoattribute:: pygit2.Oid.hex +.. autoattribute:: pygit2.Oid.raw + +The Oid type supports: + +- rich comparisons, not just for equality, also: lesser-than, lesser-or-equal, + etc. + +- hashing, so Oid objects can be used as keys in a dictionary. + + +Constants +========= + +.. py:data:: GIT_OID_RAWSZ +.. py:data:: GIT_OID_HEXSZ +.. py:data:: GIT_OID_HEX_ZERO +.. py:data:: GIT_OID_MINPREFIXLEN diff --git a/docs/repository.rst b/docs/repository.rst index e04ca2d..034ed56 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -5,35 +5,55 @@ The repository Everything starts either by creating a new repository, or by opening an existing one. +.. contents:: Contents + :local: -Creating a repository + +Functions =================================== .. autofunction:: pygit2.init_repository -This is how to create non-bare repository:: + Example:: - >>> from pygit2 import init_repository - >>> repo = init_repository('test') + >>> from pygit2 import init_repository + >>> repo = init_repository('test') # Creates a non-bare repository + >>> repo = init_repository('test', bare=True) # Creates a bare repository -And this is how to create a bare repository:: +.. autofunction:: pygit2.clone_repository - >>> from pygit2 import init_repository - >>> repo = init_repository('test', bare=True) + Example:: -But one can also do:: + >>> from pygit2 import clone_repository + >>> repo_url = 'git://github.com/libgit2/pygit2.git' + >>> repo_path = '/path/to/create/repository' + >>> repo = clone_repository(repo_url, repo_path) # Clones a non-bare repository + >>> repo = clone_repository(repo_url, repo_path, bare=True) # Clones a bare repository + + +.. autofunction:: pygit2.discover_repository - >>> from pygit2 import init_repository - >>> repo = init_repository('test', True) The Repository class =================================== -To open an existing repository:: +.. py:class:: pygit2.Repository(path) - >>> from pygit2 import Repository - >>> repo = Repository('pygit2/.git') + The Repository constructor only takes one argument, the path of the + repository to open. + + Example:: + + >>> from pygit2 import Repository + >>> repo = Repository('pygit2/.git') + +The API of the Repository class is quite large. Since this documentation is +orgaized by features, the related bits are explained in the related chapters, +for instance the :py:meth:`pygit2.Repository.checkout` method are explained in +the Checkout section. + +Below there are some general attributes and methods: .. autoattribute:: pygit2.Repository.path .. autoattribute:: pygit2.Repository.workdir @@ -41,4 +61,3 @@ To open an existing repository:: .. autoattribute:: pygit2.Repository.is_empty .. automethod:: pygit2.Repository.read .. automethod:: pygit2.Repository.write -.. automethod:: pygit2.Repository.merge_base diff --git a/docs/utils.rst b/docs/utils.rst deleted file mode 100644 index c683012..0000000 --- a/docs/utils.rst +++ /dev/null @@ -1,14 +0,0 @@ -********************************************************************** -Utilities -********************************************************************** - -.. autofunction:: pygit2.discover_repository - -.. autofunction:: pygit2.hash - -.. autofunction:: pygit2.hashfile - -.. automodule:: pygit2.utils - :members: - :show-inheritance: - :undoc-members: diff --git a/pygit2/__init__.py b/pygit2/__init__.py index fbaae96..e246ff8 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -40,15 +40,50 @@ import pygit2.utils def init_repository(path, bare=False): """ - Creates a new Git repository in the given path. + Creates a new Git repository in the given *path*. - Arguments: - - path - Path where to create the repository. - - bare - Whether the repository will be bare or not. + If *bare* is True the repository will be bare, i.e. it will not have a + working copy. """ _pygit2.init_repository(path, bare) return Repository(path) + + +def clone_repository( + url, path, bare=False, remote_name="origin", + push_url=None, fetch_spec=None, + push_spec=None, checkout_branch=None): + """ + Clones a new Git repository from *url* in the given *path*. + + **bare** indicates whether a bare git repository should be created. + + **remote_name** is the name given to the "origin" remote. + The default is "origin". + + **push_url** is a URL to be used for pushing. + None means use the *url* parameter. + + **fetch_spec** defines the the default fetch spec. + None results in the same behavior as *GIT_REMOTE_DEFAULT_FETCH*. + + **push_spec** is the fetch specification to be used for pushing. + None means use the same spec as for *fetch_spec*. + + **checkout_branch** gives the name of the branch to checkout. + None means use the remote's *HEAD*. + + Returns a Repository class pointing to the newly cloned repository. + + If you wish to use the repo, you need to do a checkout for one of + the available branches, like this: + + >>> repo = repo.clone_repository("url", "path") + >>> repo.checkout(branch) # i.e.: refs/heads/master + + """ + + _pygit2.clone_repository( + url, path, bare, remote_name, push_url, + fetch_spec, push_spec, checkout_branch) + return Repository(path) diff --git a/src/blob.c b/src/blob.c index 289160c..465d8d6 100644 --- a/src/blob.c +++ b/src/blob.c @@ -42,7 +42,8 @@ Blob_size__get__(Blob *self) PyDoc_STRVAR(Blob_data__doc__, - "Raw data. This is the same as Blob.read_raw()"); + "The contents of the blob, a bytes string. This is the same as\n" + "Blob.read_raw()"); PyGetSetDef Blob_getseters[] = { GETTER(Blob, size), diff --git a/src/commit.c b/src/commit.c index d87ec57..b8e8fbf 100644 --- a/src/commit.c +++ b/src/commit.c @@ -31,6 +31,7 @@ #include "utils.h" #include "signature.h" #include "commit.h" +#include "object.h" extern PyTypeObject TreeType; @@ -149,32 +150,43 @@ Commit_tree__get__(Commit *commit) PyDoc_STRVAR(Commit_parents__doc__, "The list of parent commits."); PyObject * -Commit_parents__get__(Commit *commit) +Commit_parents__get__(Commit *self) { + git_repository *repo; unsigned int i, parent_count; const git_oid *parent_oid; - PyObject *obj; + git_commit *parent; + int err; + PyObject *py_parent; PyObject *list; - parent_count = git_commit_parentcount(commit->commit); + parent_count = git_commit_parentcount(self->commit); list = PyList_New(parent_count); if (!list) return NULL; + repo = git_object_owner((git_object*)self->commit); for (i=0; i < parent_count; i++) { - parent_oid = git_commit_parent_id(commit->commit, i); + parent_oid = git_commit_parent_id(self->commit, i); if (parent_oid == NULL) { Py_DECREF(list); Error_set(GIT_ENOTFOUND); return NULL; } - obj = lookup_object(commit->repo, parent_oid, GIT_OBJ_COMMIT); - if (obj == NULL) { + + err = git_commit_lookup(&parent, repo, parent_oid); + if (err < 0) { + Py_DECREF(list); + return Error_set_oid(err, parent_oid, GIT_OID_HEXSZ); + } + + py_parent = wrap_object((git_object*)parent, self->repo); + if (py_parent == NULL) { Py_DECREF(list); return NULL; } - PyList_SET_ITEM(list, i, obj); + PyList_SET_ITEM(list, i, py_parent); } return list; diff --git a/src/index.c b/src/index.c index 9bf1fe1..dccd2a1 100644 --- a/src/index.c +++ b/src/index.c @@ -358,10 +358,10 @@ Index_read_tree(Index *self, PyObject *value) git_oid oid; git_tree *tree; int err; - Py_ssize_t len; + size_t len; - len = py_str_to_git_oid(value, &oid); - if (len < 0) + len = py_oid_to_git_oid(value, &oid); + if (len == 0) return NULL; err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, len); @@ -378,7 +378,7 @@ Index_read_tree(Index *self, PyObject *value) PyDoc_STRVAR(Index_write_tree__doc__, - "write_tree() -> str\n" + "write_tree() -> Oid\n" "\n" "Create a tree object from the index file, return its oid."); diff --git a/src/object.c b/src/object.c index f5f9c4f..e602da1 100644 --- a/src/object.c +++ b/src/object.c @@ -50,7 +50,7 @@ Object_dealloc(Object* self) PyDoc_STRVAR(Object_oid__doc__, - "The object id, a byte string 20 bytes long."); + "The object id, an instance of the Oid type."); PyObject * Object_oid__get__(Object *self) @@ -65,7 +65,8 @@ Object_oid__get__(Object *self) PyDoc_STRVAR(Object_hex__doc__, - "Hexadecimal representation of the object id, a text string 40 chars long."); + "Hexadecimal representation of the object id. This is a shortcut for\n" + "Object.oid.hex"); PyObject * Object_hex__get__(Object *self) @@ -80,8 +81,8 @@ Object_hex__get__(Object *self) PyDoc_STRVAR(Object_type__doc__, - "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_TAG\n" - "constants."); + "One of the GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_TAG\n" + "constants."); PyObject * Object_type__get__(Object *self) @@ -91,19 +92,22 @@ Object_type__get__(Object *self) PyDoc_STRVAR(Object_read_raw__doc__, + "read_raw()\n" + "\n" "Returns the byte string with the raw contents of the of the object."); PyObject * Object_read_raw(Object *self) { + git_repository *repo; const git_oid *oid; git_odb_object *obj; PyObject *aux; + repo = git_object_owner(self->obj); oid = git_object_id(self->obj); - assert(oid); - obj = Repository_read_raw(self->repo->repo, oid, GIT_OID_HEXSZ); + obj = Repository_read_raw(repo, oid, GIT_OID_HEXSZ); if (obj == NULL) return NULL; diff --git a/src/oid.c b/src/oid.c index b9a50b8..d1db630 100644 --- a/src/oid.c +++ b/src/oid.c @@ -45,8 +45,8 @@ git_oid_to_python(const git_oid *oid) return (PyObject*)py_oid; } -Py_ssize_t -_oid_from_hex(PyObject *py_oid, git_oid *oid) +size_t +py_hex_to_git_oid (PyObject *py_oid, git_oid *oid) { PyObject *py_hex; int err; @@ -58,15 +58,15 @@ _oid_from_hex(PyObject *py_oid, git_oid *oid) if (PyBytes_Check(py_oid)) { err = PyBytes_AsStringAndSize(py_oid, &hex, &len); if (err) - return -1; + return 0; err = git_oid_fromstrn(oid, hex, len); if (err < 0) { PyErr_SetObject(Error_type(err), py_oid); - return -1; + return 0; } - return len; + return (size_t)len; } #endif @@ -74,31 +74,31 @@ _oid_from_hex(PyObject *py_oid, git_oid *oid) if (PyUnicode_Check(py_oid)) { py_hex = PyUnicode_AsASCIIString(py_oid); if (py_hex == NULL) - return -1; + return 0; err = PyBytes_AsStringAndSize(py_hex, &hex, &len); if (err) { Py_DECREF(py_hex); - return -1; + return 0; } err = git_oid_fromstrn(oid, hex, len); Py_DECREF(py_hex); if (err < 0) { PyErr_SetObject(Error_type(err), py_oid); - return -1; + return 0; } - return len; + return (size_t)len; } /* Type error */ PyErr_SetObject(PyExc_TypeError, py_oid); - return -1; + return 0; } -Py_ssize_t -py_str_to_git_oid(PyObject *py_oid, git_oid *oid) +size_t +py_oid_to_git_oid(PyObject *py_oid, git_oid *oid) { /* Oid */ if (PyObject_TypeCheck(py_oid, (PyTypeObject*)&OidType)) { @@ -107,41 +107,43 @@ py_str_to_git_oid(PyObject *py_oid, git_oid *oid) } /* Hex */ - return _oid_from_hex(py_oid, oid); + return py_hex_to_git_oid(py_oid, oid); } -Py_ssize_t -py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) +int +py_oid_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) { int err; - Py_ssize_t len; - git_odb *odb; - git_odb_object *obj; + size_t len; + git_odb *odb = NULL; + git_odb_object *obj = NULL; - len = py_str_to_git_oid(py_str, oid); - - if (len == GIT_OID_HEXSZ || len < 0) - return len; - - err = git_repository_odb(&odb, repo); - if (err < 0) { - Error_set(err); + len = py_oid_to_git_oid(py_str, oid); + if (len == 0) return -1; - } + + if (len == GIT_OID_HEXSZ) + return 0; + + /* Short oid */ + err = git_repository_odb(&odb, repo); + if (err < 0) + goto error; err = git_odb_read_prefix(&obj, odb, oid, len); - if (err < 0) { - git_odb_free(odb); - Error_set(err); - return err; - } + if (err < 0) + goto error; git_oid_cpy(oid, git_odb_object_id(obj)); - git_odb_object_free(obj); git_odb_free(odb); - return 0; + +error: + git_odb_object_free(obj); + git_odb_free(odb); + Error_set(err); + return -1; } PyObject * @@ -197,8 +199,8 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) } /* Case 2: hex */ - len = _oid_from_hex(hex, &self->oid); - if (len < 0) + len = py_hex_to_git_oid(hex, &self->oid); + if (len == 0) return -1; return 0; @@ -246,6 +248,9 @@ Oid_richcompare(PyObject *o1, PyObject *o2, int op) case Py_GE: res = (cmp >= 0) ? Py_True: Py_False; break; + default: + PyErr_Format(PyExc_RuntimeError, "Unexpected '%d' op", op); + return NULL; } Py_INCREF(res); @@ -253,7 +258,7 @@ Oid_richcompare(PyObject *o1, PyObject *o2, int op) } -PyDoc_STRVAR(Oid_raw__doc__, "Raw oid."); +PyDoc_STRVAR(Oid_raw__doc__, "Raw oid, a 20 bytes string."); PyObject * Oid_raw__get__(Oid *self) @@ -262,7 +267,7 @@ Oid_raw__get__(Oid *self) } -PyDoc_STRVAR(Oid_hex__doc__, "Hex oid."); +PyDoc_STRVAR(Oid_hex__doc__, "Hex oid, a 40 chars long string (type str)."); PyObject * Oid_hex__get__(Oid *self) diff --git a/src/oid.h b/src/oid.h index ca6567c..871f26a 100644 --- a/src/oid.h +++ b/src/oid.h @@ -32,9 +32,9 @@ #include #include -Py_ssize_t py_str_to_git_oid(PyObject *py_str, git_oid *oid); -Py_ssize_t py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, - git_oid *oid); +size_t py_oid_to_git_oid(PyObject *py_str, git_oid *oid); +int py_oid_to_git_oid_expand(git_repository *repo, PyObject *py_str, + git_oid *oid); PyObject* git_oid_to_python(const git_oid *oid); PyObject* git_oid_to_py_str(const git_oid *oid); diff --git a/src/pygit2.c b/src/pygit2.c index 0533b2a..8362891 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -97,6 +97,69 @@ init_repository(PyObject *self, PyObject *args) { Py_RETURN_NONE; }; +PyDoc_STRVAR(clone_repository__doc__, + "clone_repository(url, path, bare, remote_name, push_url," + "fetch_spec, push_spec, checkout_branch)\n" + "\n" + "Clones a Git repository in the given url to the given path " + "with the specified options.\n" + "\n" + "Arguments:\n" + "\n" + "url\n" + " Git repository remote url.\n" + "path\n" + " Path where to create the repository.\n" + "bare\n" + " If 'bare' is not 0, then a bare git repository will be created.\n" + "remote_name\n" + " The name given to the 'origin' remote. The default is 'origin'.\n" + "push_url\n" + " URL to be used for pushing.\n" + "fetch_spec\n" + " The fetch specification to be used for fetching. None results in " + "the same behavior as GIT_REMOTE_DEFAULT_FETCH.\n" + "push_spec\n" + " The fetch specification to be used for pushing. None means use the " + "same spec as for 'fetch_spec'\n" + "checkout_branch\n" + " The name of the branch to checkout. None means use the remote's " + "HEAD.\n"); + + +PyObject * +clone_repository(PyObject *self, PyObject *args) { + git_repository *repo; + const char *url; + const char *path; + unsigned int bare; + const char *remote_name, *push_url, *fetch_spec; + const char *push_spec, *checkout_branch; + int err; + + if (!PyArg_ParseTuple(args, "zzIzzzzz", + &url, &path, &bare, &remote_name, &push_url, + &fetch_spec, &push_spec, &checkout_branch)) + return NULL; + + git_clone_options opts = { + .version=1, + .bare=bare, + .remote_name=remote_name, + .pushurl=push_url, + .fetch_spec=fetch_spec, + .push_spec=push_spec, + .checkout_branch=checkout_branch + }; + + err = git_clone(&repo, url, path, &opts); + if (err < 0) + return Error_set_str(err, path); + + git_repository_free(repo); + Py_RETURN_NONE; +}; + PyDoc_STRVAR(discover_repository__doc__, "discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n" @@ -124,10 +187,10 @@ discover_repository(PyObject *self, PyObject *args) }; PyDoc_STRVAR(hashfile__doc__, - "hashfile(path) -> bytes\n" - "\n" - "Returns the oid of a new blob from a file path without actually writing \n" - "to the odb."); + "hashfile(path) -> Oid\n" + "\n" + "Returns the oid of a new blob from a file path without actually writing\n" + "to the odb."); PyObject * hashfile(PyObject *self, PyObject *args) { @@ -146,10 +209,10 @@ hashfile(PyObject *self, PyObject *args) } PyDoc_STRVAR(hash__doc__, - "hash(data) -> bytes\n" - "\n" - "Returns the oid of a new blob from a string without actually writing to \n" - "the odb."); + "hash(data) -> Oid\n" + "\n" + "Returns the oid of a new blob from a string without actually writing to\n" + "the odb."); PyObject * hash(PyObject *self, PyObject *args) { @@ -172,6 +235,8 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, + {"clone_repository", clone_repository, METH_VARARGS, + clone_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, diff --git a/src/reference.c b/src/reference.c index 17537e7..0ed45aa 100644 --- a/src/reference.c +++ b/src/reference.c @@ -107,6 +107,7 @@ PyTypeObject RefLogIterType = { void Reference_dealloc(Reference *self) { + Py_CLEAR(self->repo); git_reference_free(self->reference); PyObject_Del(self); } @@ -191,7 +192,7 @@ Reference_resolve(Reference *self, PyObject *args) if (err < 0) return Error_set(err); - return wrap_reference(c_reference); + return wrap_reference(c_reference, self->repo); } @@ -226,7 +227,6 @@ Reference_target__set__(Reference *self, PyObject *py_target) git_oid oid; char *c_name; int err; - Py_ssize_t len; git_reference *new_ref; git_repository *repo; @@ -235,11 +235,9 @@ Reference_target__set__(Reference *self, PyObject *py_target) /* Case 1: Direct */ if (GIT_REF_OID == git_reference_type(self->reference)) { repo = git_reference_owner(self->reference); - len = py_str_to_git_oid_expand(repo, py_target, &oid); - if (len < 0) { - err = (int)len; - goto error; - } + err = py_oid_to_git_oid_expand(repo, py_target, &oid); + if (err < 0) + return err; err = git_reference_set_target(&new_ref, self->reference, &oid); if (err < 0) @@ -464,13 +462,18 @@ PyTypeObject ReferenceType = { PyObject * -wrap_reference(git_reference * c_reference) +wrap_reference(git_reference * c_reference, Repository *repo) { Reference *py_reference=NULL; py_reference = PyObject_New(Reference, &ReferenceType); - if (py_reference) + if (py_reference) { py_reference->reference = c_reference; + if (repo) { + py_reference->repo = repo; + Py_INCREF(repo); + } + } return (PyObject *)py_reference; } diff --git a/src/reference.h b/src/reference.h index ff63bbf..5cbd134 100644 --- a/src/reference.h +++ b/src/reference.h @@ -41,6 +41,6 @@ PyObject* Reference_get_name(Reference *self); PyObject* Reference_get_oid(Reference *self); PyObject* Reference_get_hex(Reference *self); PyObject* Reference_get_type(Reference *self); -PyObject* wrap_reference(git_reference * c_reference); +PyObject* wrap_reference(git_reference *c_reference, Repository *repo); #endif diff --git a/src/repository.c b/src/repository.c index 5b81e5d..af388f4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -65,20 +65,6 @@ int_to_loose_object_type(int type_id) } } -PyObject * -lookup_object(Repository *repo, const git_oid *oid, git_otype type) -{ - int err; - git_object *obj; - - err = git_object_lookup_prefix(&obj, repo->repo, oid, GIT_OID_HEXSZ, - type); - if (err < 0) - return Error_set_oid(err, oid, GIT_OID_HEXSZ); - - return wrap_object(obj, repo); -} - int Repository_init(Repository *self, PyObject *args, PyObject *kwds) { @@ -182,7 +168,7 @@ Repository_head__get__(Repository *self) return NULL; } - return wrap_reference(head); + return wrap_reference(head, self); } int @@ -268,12 +254,12 @@ PyObject * Repository_git_object_lookup_prefix(Repository *self, PyObject *key) { int err; - Py_ssize_t len; + size_t len; git_oid oid; git_object *obj; - len = py_str_to_git_oid(key, &oid); - if (len < 0) + len = py_oid_to_git_oid(key, &oid); + if (len == 0) return NULL; err = git_object_lookup_prefix(&obj, self->repo, &oid, len, GIT_OBJ_ANY); @@ -353,11 +339,11 @@ Repository_read(Repository *self, PyObject *py_hex) { git_oid oid; git_odb_object *obj; - Py_ssize_t len; + size_t len; PyObject* tuple; - len = py_str_to_git_oid(py_hex, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_hex, &oid); + if (len == 0) return NULL; obj = Repository_read_raw(self->repo, &oid, len); @@ -380,11 +366,11 @@ Repository_read(Repository *self, PyObject *py_hex) PyDoc_STRVAR(Repository_write__doc__, - "write(type, data) -> oid\n" - "\n" - "Write raw object data into the repository. First arg is the object type,\n" - "the second one a buffer with data. Return the object id (sha) of of the\n" - "created object."); + "write(type, data) -> Oid\n" + "\n" + "Write raw object data into the repository. First arg is the object\n" + "type, the second one a buffer with data. Return the Oid of the created\n" + "object."); PyObject * Repository_write(Repository *self, PyObject *args) @@ -519,7 +505,7 @@ Repository_config__get__(Repository *self) } PyDoc_STRVAR(Repository_merge_base__doc__, - "merge_base(oid, oid) -> commit\n" + "merge_base(oid, oid) -> Oid\n" "\n" "Find as good common ancestors as possible for a merge."); @@ -536,11 +522,11 @@ Repository_merge_base(Repository *self, PyObject *args) if (!PyArg_ParseTuple(args, "OO", &value1, &value2)) return NULL; - err = py_str_to_git_oid_expand(self->repo, value1, &oid1); + err = py_oid_to_git_oid_expand(self->repo, value1, &oid1); if (err < 0) return NULL; - err = py_str_to_git_oid_expand(self->repo, value2, &oid2); + err = py_oid_to_git_oid_expand(self->repo, value2, &oid2); if (err < 0) return NULL; @@ -562,7 +548,6 @@ Repository_walk(Repository *self, PyObject *args) PyObject *value; unsigned int sort; int err; - Py_ssize_t len; git_oid oid; git_revwalk *walk; Walker *py_walker; @@ -579,10 +564,10 @@ Repository_walk(Repository *self, PyObject *args) /* Push */ if (value != Py_None) { - len = py_str_to_git_oid_expand(self->repo, value, &oid); - if (len < 0) { + err = py_oid_to_git_oid_expand(self->repo, value, &oid); + if (err < 0) { git_revwalk_free(walk); - return Error_set((int)len); + return NULL; } err = git_revwalk_push(walk, &oid); @@ -606,9 +591,10 @@ Repository_walk(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob__doc__, - "create_blob(data) -> bytes\n" - "\n" - "Create a new blob from memory."); + "create_blob(data) -> Oid\n" + "\n" + "Create a new blob from a bytes string. The blob is added to the Git\n" + "object database. Returns the oid of the blob."); PyObject * Repository_create_blob(Repository *self, PyObject *args) @@ -630,9 +616,11 @@ Repository_create_blob(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob_fromworkdir__doc__, - "create_blob_fromworkdir(path) -> bytes\n" - "\n" - "Create a new blob from a file within the working directory (raise an error otherwise)."); + "create_blob_fromworkdir(path) -> Oid\n" + "\n" + "Create a new blob from a file within the working directory. The given\n" + "path must be relative to the working directory, if it is not an error\n" + "is raised."); PyObject * Repository_create_blob_fromworkdir(Repository *self, PyObject *args) @@ -653,9 +641,9 @@ Repository_create_blob_fromworkdir(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_blob_fromdisk__doc__, - "create_blob_fromdisk(path) -> bytes\n" - "\n" - "Create a new blob from a file anywhere (no working directory check)."); + "create_blob_fromdisk(path) -> Oid\n" + "\n" + "Create a new blob from a file anywhere (no working directory check)."); PyObject * Repository_create_blob_fromdisk(Repository *self, PyObject *args) @@ -676,9 +664,9 @@ Repository_create_blob_fromdisk(Repository *self, PyObject *args) PyDoc_STRVAR(Repository_create_commit__doc__, - "create_commit(reference, author, committer, message, tree, parents[, encoding]) -> bytes\n" + "create_commit(reference, author, committer, message, tree, parents[, encoding]) -> Oid\n" "\n" - "Create a new commit object, return its SHA."); + "Create a new commit object, return its oid."); PyObject * Repository_create_commit(Repository *self, PyObject *args) @@ -694,7 +682,7 @@ Repository_create_commit(Repository *self, PyObject *args) int parent_count; git_commit **parents = NULL; int err = 0, i = 0; - Py_ssize_t len; + size_t len; if (!PyArg_ParseTuple(args, "zO!O!OOO!|s", &update_ref, @@ -706,8 +694,8 @@ Repository_create_commit(Repository *self, PyObject *args) &encoding)) return NULL; - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_oid, &oid); + if (len == 0) goto out; message = py_str_to_c_str(py_message, encoding); @@ -728,12 +716,14 @@ Repository_create_commit(Repository *self, PyObject *args) } for (; i < parent_count; i++) { py_parent = PyList_GET_ITEM(py_parents, i); - len = py_str_to_git_oid(py_parent, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_parent, &oid); + if (len == 0) goto out; - if (git_commit_lookup_prefix(&parents[i], self->repo, &oid, - (unsigned int)len)) + err = git_commit_lookup_prefix(&parents[i], self->repo, &oid, len); + if (err < 0) { + Error_set(err); goto out; + } } err = git_commit_create(&oid, self->repo, update_ref, @@ -760,9 +750,9 @@ out: PyDoc_STRVAR(Repository_create_tag__doc__, - "create_tag(name, oid, type, tagger, message) -> bytes\n" + "create_tag(name, oid, type, tagger, message) -> Oid\n" "\n" - "Create a new tag object, return its SHA."); + "Create a new tag object, return its oid."); PyObject * Repository_create_tag(Repository *self, PyObject *args) @@ -773,7 +763,7 @@ Repository_create_tag(Repository *self, PyObject *args) git_oid oid; git_object *target = NULL; int err, target_type; - Py_ssize_t len; + size_t len; if (!PyArg_ParseTuple(args, "sOiO!s", &tag_name, @@ -783,12 +773,12 @@ Repository_create_tag(Repository *self, PyObject *args) &message)) return NULL; - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_oid, &oid); + if (len == 0) return NULL; - err = git_object_lookup_prefix(&target, self->repo, &oid, - (unsigned int)len, target_type); + err = git_object_lookup_prefix(&target, self->repo, &oid, len, + target_type); err = err < 0 ? err : git_tag_create(&oid, self->repo, tag_name, target, py_tagger->signature, message, 0); git_object_free(target); @@ -869,7 +859,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name) free(c_name); /* 3- Make an instance of Reference and return it */ - return wrap_reference(c_reference); + return wrap_reference(c_reference, self); } PyDoc_STRVAR(Repository_create_reference_direct__doc__, @@ -896,20 +886,19 @@ Repository_create_reference_direct(Repository *self, PyObject *args, char *c_name; git_oid oid; int err, force; - Py_ssize_t len; if (!PyArg_ParseTuple(args, "sOi", &c_name, &py_obj, &force)) return NULL; - len = py_str_to_git_oid_expand(self->repo, py_obj, &oid); - if (len < 0) - return Error_set((int)len); + err = py_oid_to_git_oid_expand(self->repo, py_obj, &oid); + if (err < 0) + return NULL; err = git_reference_create(&c_reference, self->repo, c_name, &oid, force); if (err < 0) return Error_set(err); - return wrap_reference(c_reference); + return wrap_reference(c_reference, self); } PyDoc_STRVAR(Repository_create_reference_symbolic__doc__, @@ -943,7 +932,7 @@ Repository_create_reference_symbolic(Repository *self, PyObject *args, if (err < 0) return Error_set(err); - return wrap_reference(c_reference); + return wrap_reference(c_reference, self); } @@ -1024,7 +1013,6 @@ Repository_TreeBuilder(Repository *self, PyObject *args) git_tree *tree = NULL; git_tree *must_free = NULL; int err; - Py_ssize_t len; if (!PyArg_ParseTuple(args, "|O", &py_src)) return NULL; @@ -1038,8 +1026,8 @@ Repository_TreeBuilder(Repository *self, PyObject *args) } tree = py_tree->tree; } else { - len = py_str_to_git_oid_expand(self->repo, py_src, &oid); - if (len < 0) + err = py_oid_to_git_oid_expand(self->repo, py_src, &oid); + if (err < 0) return NULL; err = git_tree_lookup(&tree, self->repo, &oid); @@ -1220,12 +1208,11 @@ Repository_notes(Repository *self, PyObject *args) } return Error_set(err); - } PyDoc_STRVAR(Repository_create_note__doc__, - "create_note(message, author, committer, annotated_id [,ref, force]) -> ID\n" + "create_note(message, author, committer, annotated_id [,ref, force]) -> Oid\n" "\n" "Create a new note for an object, return its SHA-ID." "If no ref is given 'refs/notes/commits' will be used."); diff --git a/src/tree.c b/src/tree.c index c6edaab..b8dee9b 100644 --- a/src/tree.c +++ b/src/tree.c @@ -290,7 +290,7 @@ Tree_diff_to_workdir(Tree *self, PyObject *args) if (!PyArg_ParseTuple(args, "|i", &opts.flags)) return NULL; - repo = self->repo->repo; + repo = git_tree_owner(self->tree); err = git_diff_tree_to_workdir(&diff, repo, self->tree, &opts); if (err < 0) @@ -321,7 +321,7 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) if (!PyArg_ParseTuple(args, "O!|i", &IndexType, &py_idx, &opts.flags)) return NULL; - repo = self->repo->repo; + repo = git_tree_owner(self->tree); err = git_diff_tree_to_index(&diff, repo, self->tree, py_idx->index, &opts); if (err < 0) return Error_set(err); @@ -354,7 +354,7 @@ Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) &swap)) return NULL; - repo = self->repo->repo; + repo = git_tree_owner(self->tree); to = (py_tree == NULL) ? NULL : py_tree->tree; from = self->tree; if (swap > 0) { diff --git a/src/treebuilder.c b/src/treebuilder.c index 55601e5..1724f14 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -53,7 +53,7 @@ PyObject * TreeBuilder_insert(TreeBuilder *self, PyObject *args) { PyObject *py_oid; - Py_ssize_t len; + size_t len; int err, attr; git_oid oid; const char *fname; @@ -61,8 +61,8 @@ TreeBuilder_insert(TreeBuilder *self, PyObject *args) if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) return NULL; - len = py_str_to_git_oid(py_oid, &oid); - if (len < 0) + len = py_oid_to_git_oid(py_oid, &oid); + if (len == 0) return NULL; err = git_treebuilder_insert(NULL, self->bld, fname, &oid, attr); @@ -74,7 +74,7 @@ TreeBuilder_insert(TreeBuilder *self, PyObject *args) PyDoc_STRVAR(TreeBuilder_write__doc__, - "write() -> bytes\n" + "write() -> Oid\n" "\n" "Write the tree to the given repository."); diff --git a/src/types.h b/src/types.h index 3c559ea..ce27142 100644 --- a/src/types.h +++ b/src/types.h @@ -158,10 +158,7 @@ typedef struct { /* git_reference, git_reflog */ SIMPLE_TYPE(Walker, git_revwalk, walk) -typedef struct { - PyObject_HEAD - git_reference *reference; -} Reference; +SIMPLE_TYPE(Reference, git_reference, reference) typedef struct { PyObject_HEAD @@ -192,6 +189,4 @@ typedef struct { SIMPLE_TYPE(Remote, git_remote, remote) -PyObject* lookup_object(Repository *repo, const git_oid *oid, git_otype type); - #endif diff --git a/src/utils.h b/src/utils.h index 1fb2466..99a89db 100644 --- a/src/utils.h +++ b/src/utils.h @@ -79,7 +79,8 @@ /* Utilities */ -#define to_unicode(x, encoding, errors) to_unicode_n(x, strlen(x), encoding, errors) +#define to_unicode(x, encoding, errors)\ + to_unicode_n(x, strlen(x), encoding, errors) PYGIT2_FN_UNUSED Py_LOCAL_INLINE(PyObject*) @@ -150,8 +151,8 @@ char * py_str_to_c_str(PyObject *value, const char *encoding); /* Helpers to make type init shorter. */ #define INIT_TYPE(type, base, new) \ - if (base != NULL) type.tp_base = base; \ - if (new != NULL) type.tp_new = new; \ + type.tp_base = base; \ + type.tp_new = new; \ if (PyType_Ready(&type) < 0) return NULL; #define ADD_TYPE(module, type) \ diff --git a/src/walker.c b/src/walker.c index 1952fbe..a02b2f4 100644 --- a/src/walker.c +++ b/src/walker.c @@ -53,12 +53,11 @@ PyObject * Walker_hide(Walker *self, PyObject *py_hex) { int err; - Py_ssize_t len; git_oid oid; - len = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); - if (len < 0) - return Error_set((int)len); + err = py_oid_to_git_oid_expand(self->repo->repo, py_hex, &oid); + if (err < 0) + return NULL; err = git_revwalk_hide(self->walk, &oid); if (err < 0) @@ -77,12 +76,11 @@ PyObject * Walker_push(Walker *self, PyObject *py_hex) { int err; - Py_ssize_t len; git_oid oid; - len = py_str_to_git_oid_expand(self->repo->repo, py_hex, &oid); - if (len < 0) - return Error_set((int)len); + err = py_oid_to_git_oid_expand(self->repo->repo, py_hex, &oid); + if (err < 0) + return NULL; err = git_revwalk_push(self->walk, &oid); if (err < 0) diff --git a/test/test_repository.py b/test/test_repository.py index b11c804..d6e762e 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -40,7 +40,10 @@ from os.path import join, realpath # Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT -from pygit2 import init_repository, discover_repository, Reference, hashfile +from pygit2 import ( + init_repository, clone_repository, discover_repository, + Reference, hashfile +) from pygit2 import Oid import pygit2 from . import utils @@ -101,7 +104,7 @@ class RepositoryTest(utils.BareRepoTestCase): self.assertFalse('a' * 20 in self.repo) def test_iterable(self): - l = [ obj for obj in self.repo ] + l = [obj for obj in self.repo] oid = Oid(hex=BLOB_HEX) self.assertTrue(oid in l) @@ -135,9 +138,10 @@ class RepositoryTest(utils.BareRepoTestCase): commit = self.repo[commit_sha_prefix] self.assertEqual(commit_sha, commit.hex) self.assertEqual(GIT_OBJ_COMMIT, commit.type) - self.assertEqual(('Second test data commit.\n\n' - 'This commit has some additional text.\n'), - commit.message) + self.assertEqual( + ('Second test data commit.\n\n' + 'This commit has some additional text.\n'), + commit.message) self.assertRaises(ValueError, self.repo.__getitem__, too_short_prefix) def test_get_path(self): @@ -208,7 +212,7 @@ class RepositoryTest_II(utils.RepoTestCase): def test_checkout_index(self): # some changes to working dir with open(os.path.join(self.repo.workdir, 'hello.txt'), 'w') as f: - f.write('new content') + f.write('new content') # checkout index self.assertTrue('hello.txt' in self.repo.status()) @@ -218,7 +222,7 @@ class RepositoryTest_II(utils.RepoTestCase): def test_checkout_head(self): # some changes to the index with open(os.path.join(self.repo.workdir, 'bye.txt'), 'w') as f: - f.write('new content') + f.write('new content') self.repo.index.add('bye.txt') # checkout from index should not change anything @@ -272,6 +276,7 @@ class InitRepositoryTest(utils.NoRepoTestCase): repo = init_repository(self._temp_dir, bare=True) self.assertTrue(repo.is_bare) + class DiscoverRepositoryTest(utils.NoRepoTestCase): def test_discover_repo(self): repo = init_repository(self._temp_dir, False) @@ -279,6 +284,7 @@ class DiscoverRepositoryTest(utils.NoRepoTestCase): os.makedirs(subdir) self.assertEqual(repo.path, discover_repository(subdir)) + class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_is_empty(self): @@ -292,5 +298,69 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase): self.assertFalse(self.repo.head_is_detached) +class CloneRepositoryTest(utils.NoRepoTestCase): + def test_clone_repository(self): + repo_path = "./test/data/testrepo.git/" + repo = clone_repository(repo_path, self._temp_dir) + self.assertFalse(repo.is_empty) + self.assertFalse(repo.is_bare) + + def test_clone_bare_repository(self): + repo_path = "./test/data/testrepo.git/" + repo = clone_repository(repo_path, self._temp_dir, bare=True) + 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_push_url(self): + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, push_url="custom_push_url" + ) + self.assertFalse(repo.is_empty) + # FIXME: When pygit2 supports retrieving the pushurl parameter, + # enable this test + # self.assertEqual(repo.remotes[0].pushurl, "custom_push_url") + + def test_clone_fetch_spec(self): + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, fetch_spec="refs/heads/test" + ) + self.assertFalse(repo.is_empty) + # FIXME: When pygit2 retrieve the fetchspec we passed to git clone. + # fetchspec seems to be going through, but the Repository class is + # not getting it. + # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") + + def test_clone_push_spec(self): + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, push_spec="refs/heads/test" + ) + self.assertFalse(repo.is_empty) + # FIXME: When pygit2 supports retrieving the pushspec parameter, + # enable this test + # not sure how to test this either... couldn't find pushspec + # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") + + def test_clone_checkout_branch(self): + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, checkout_branch="test" + ) + self.assertFalse(repo.is_empty) + # FIXME: When pygit2 supports retrieving the current branch, + # enable this test + # self.assertEqual(repo.remotes[0].current_branch, "test") + + if __name__ == '__main__': unittest.main()