From d6c1d49ef68f108368c597ea811f708cca5c3f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Mon, 6 May 2013 23:16:50 +0200 Subject: [PATCH 01/16] Use git_{object,tree}_owner (#228) Note that libgit2's git_commit_owner is missing from the public interface, so we cannot use it. --- src/commit.c | 26 +++++++++++++++++++------- src/object.c | 5 +++-- src/repository.c | 14 -------------- src/tree.c | 4 ++-- src/types.h | 2 -- 5 files changed, 24 insertions(+), 27 deletions(-) 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/object.c b/src/object.c index f5f9c4f..67bff88 100644 --- a/src/object.c +++ b/src/object.c @@ -96,14 +96,15 @@ PyDoc_STRVAR(Object_read_raw__doc__, 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/repository.c b/src/repository.c index 5b81e5d..eb9e2f6 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) { diff --git a/src/tree.c b/src/tree.c index f6a01cf..a3ac026 100644 --- a/src/tree.c +++ b/src/tree.c @@ -292,7 +292,7 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) git_diff_list *diff; git_tree* tree = NULL; git_index* index; - git_repository* repo; + git_repository *repo; int err, empty_tree = 0; char *keywords[] = {"obj", "flags", "empty_tree", NULL}; @@ -303,7 +303,7 @@ Tree_diff(Tree *self, PyObject *args, PyObject *kwds) &py_obj, &opts.flags, &empty_tree)) return NULL; - repo = self->repo->repo; + repo = git_tree_owner(self->tree); if (py_obj == NULL) { if (empty_tree > 0) err = git_diff_tree_to_tree(&diff, repo, self->tree, NULL, &opts); diff --git a/src/types.h b/src/types.h index a1a0105..fdc451e 100644 --- a/src/types.h +++ b/src/types.h @@ -193,6 +193,4 @@ typedef struct { SIMPLE_TYPE(Remote, git_remote, remote) -PyObject* lookup_object(Repository *repo, const git_oid *oid, git_otype type); - #endif From 101715bf37440d32291bde4f58c3142bcf7d8adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 9 May 2013 14:13:30 +0200 Subject: [PATCH 02/16] docs: review, in progress --- docs/general.rst | 24 ++++ docs/index.rst | 15 +-- docs/{errors.rst => merge.rst} | 7 +- docs/objects.rst | 228 ++++++++++++++------------------- docs/oid.rst | 84 ++++++++++++ docs/repository.rst | 43 ++++--- docs/utils.rst | 14 -- pygit2/__init__.py | 11 +- src/object.c | 11 +- src/oid.c | 4 +- src/pygit2.c | 16 +-- src/repository.c | 10 +- 12 files changed, 264 insertions(+), 203 deletions(-) create mode 100644 docs/general.rst rename docs/{errors.rst => merge.rst} (59%) create mode 100644 docs/oid.rst delete mode 100644 docs/utils.rst 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/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..a277a3a 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -2,91 +2,46 @@ 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 -================= - -The oid is the `SHA-1 `_ hash of an -object. It is 20 bytes long: - -- When we represent an oid as a 20 bytes Python string, we say it is a raw - oid. - -- When we represent an oid as a 40 chars Python string, we sayt it is a hex - oid. - -However, most of the time we will use the Oid type. We can explicetly create -an Oid object from its raw or hexadecimal form:: - - >>> hex = "cff3ceaefc955f0dbe1957017db181bc49913781" - >>> oid1 = Oid(hex=hex) - - >>> from binascii import unhexlify - >>> raw = unhexlify(hex) - >>> oid2 = Oid(raw=raw) - - >>> print oid1 == oid2 - True - -And in the opposite direction, we can get the raw or hexadecimal form from -an Oid object: - -.. autoattribute:: pygit2.Oid.raw -.. autoattribute:: pygit2.Oid.hex - -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 - - -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:: +The Object type is a base type, it is not possible to make instances of it, in +any way. - >>> # Show commits and trees - >>> commit - - >>> commit.tree - +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:: -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:: + >>> from pygit2 import Object + >>> commit = repository.revparse_single('HEAD') + >>> print isinstance(commit, Object) + True - >>> commit.oid - b'x\xde\xb5W\x8d\x01<\xdb\xdf\x08o\xa1\xd1\xa3\xe7\xd9\x82\xe8\x88\x8f' - >>> commit.hex - '78deb5578d013cdbdf086fa1d1a3e7d982e8888f' +All Objects are immutable, they cannot be modified once they are created:: -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). + >>> commit.message = u"foobar" + Traceback (most recent call last): + File "", line 1, in + AttributeError: attribute 'message' of '_pygit2.Commit' objects is not writable -Objects can not be modified once they have been created. +Derived types (blobs, trees, etc.) don't have a constructor, this means they +cannot be created with the common idiom:: + + >>> from pygit2 import Blob + >>> blob = Blob("data") + Traceback (most recent call last): + File "", line 1, in + TypeError: cannot create '_pygit2.Blob' instances + +New objects are created using an specific API we will see later. This is the common interface for all Git objects: @@ -96,55 +51,38 @@ This is the common interface for all Git objects: .. automethod:: pygit2.Object.read_raw -Commits + + + + + +Blobs ================= -A commit is a snapshot of the working dir with meta informations like author, -committer and others. +A blob is equivalent to a file in a file system.:: -.. autoattribute:: pygit2.Commit.author -.. autoattribute:: pygit2.Commit.committer -.. autoattribute:: pygit2.Commit.message -.. autoattribute:: pygit2.Commit.message_encoding -.. autoattribute:: pygit2.Commit.tree -.. autoattribute:: pygit2.Commit.parents -.. autoattribute:: pygit2.Commit.commit_time -.. autoattribute:: pygit2.Commit.commit_time_offset + >>> # 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 -Signatures -------------- +To create new blobs use the Repository API: -The author and committer attributes of commit objects are ``Signature`` -objects:: +.. automethod:: pygit2.Repository.create_blob +.. automethod:: pygit2.Repository.create_blob_fromworkdir +.. automethod:: pygit2.Repository.create_blob_fromdisk - >>> commit.author - - -.. autoattribute:: pygit2.Signature.name -.. autoattribute:: pygit2.Signature.email -.. autoattribute:: pygit2.Signature.time -.. autoattribute:: pygit2.Signature.offset - - -Creating commits ----------------- - -.. automethod:: pygit2.Repository.create_commit - -Commits can be created by calling the ``create_commit`` method of the -repository with the following parameters:: - - >>> author = Signature('Alice Author', 'alice@authors.tld') - >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') - >>> tree = repo.TreeBuilder().write() - >>> repo.create_commit( - ... 'refs/heads/master', # the name of the reference to update - ... author, committer, 'one line commit message\n\ndetailed commit message', - ... tree, # binary string representing the tree object ID - ... [] # list of binary strings representing parents of the new commit - ... ) - '#\xe4>> # 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.Commit.author +.. autoattribute:: pygit2.Commit.committer +.. autoattribute:: pygit2.Commit.message +.. autoattribute:: pygit2.Commit.message_encoding +.. autoattribute:: pygit2.Commit.tree +.. autoattribute:: pygit2.Commit.parents +.. autoattribute:: pygit2.Commit.commit_time +.. autoattribute:: pygit2.Commit.commit_time_offset -.. autoattribute:: pygit2.Blob.data -.. autoattribute:: pygit2.Blob.size -Creating blobs --------------------- +Signatures +------------- + +The author and committer attributes of commit objects are ``Signature`` +objects:: + + >>> commit.author + + +.. autoattribute:: pygit2.Signature.name +.. autoattribute:: pygit2.Signature.email +.. autoattribute:: pygit2.Signature.time +.. autoattribute:: pygit2.Signature.offset + + +Creating commits +---------------- + +.. automethod:: pygit2.Repository.create_commit + +Commits can be created by calling the ``create_commit`` method of the +repository with the following parameters:: + + >>> author = Signature('Alice Author', 'alice@authors.tld') + >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') + >>> tree = repo.TreeBuilder().write() + >>> repo.create_commit( + ... 'refs/heads/master', # the name of the reference to update + ... author, committer, 'one line commit message\n\ndetailed commit message', + ... tree, # binary string representing the tree object ID + ... [] # list of binary strings representing parents of the new commit + ... ) + '#\xe4`_ 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..2f34c14 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -5,35 +5,41 @@ The repository Everything starts either by creating a new repository, or by opening an existing one. +.. contents:: Contents + :local: + Creating a repository =================================== .. autofunction:: pygit2.init_repository -This is how to create non-bare repository:: +Example:: >>> from pygit2 import init_repository - >>> repo = init_repository('test') - -And this is how to create a bare repository:: - - >>> from pygit2 import init_repository - >>> repo = init_repository('test', bare=True) - -But one can also do:: - - >>> from pygit2 import init_repository - >>> repo = init_repository('test', True) + >>> repo = init_repository('test') # Creates a non-bare repository + >>> repo = init_repository('test', bare=True) # Creates a bare repository 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 +47,9 @@ To open an existing repository:: .. autoattribute:: pygit2.Repository.is_empty .. automethod:: pygit2.Repository.read .. automethod:: pygit2.Repository.write -.. automethod:: pygit2.Repository.merge_base + + +Utilities +========= + +.. autofunction:: pygit2.discover_repository 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..aeb7fe5 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -40,15 +40,10 @@ 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) diff --git a/src/object.c b/src/object.c index 67bff88..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,6 +92,8 @@ 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 * diff --git a/src/oid.c b/src/oid.c index b9a50b8..d1751ce 100644 --- a/src/oid.c +++ b/src/oid.c @@ -253,7 +253,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 +262,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/pygit2.c b/src/pygit2.c index 0533b2a..c68e530 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -124,10 +124,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 +146,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) { diff --git a/src/repository.c b/src/repository.c index eb9e2f6..fe63f67 100644 --- a/src/repository.c +++ b/src/repository.c @@ -366,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) From 7b7bd5edb3080c7b6e47dd4b5bcd4498927a9459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 11 May 2013 08:18:16 +0200 Subject: [PATCH 03/16] docs: a little more --- docs/objects.rst | 88 +++++++++++++++++++++++++++++++++++---------- docs/repository.rst | 19 +++++----- src/blob.c | 3 +- src/repository.c | 21 ++++++----- 4 files changed, 92 insertions(+), 39 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index a277a3a..6f778e9 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -1,5 +1,5 @@ ********************************************************************** -Git objects +Git Objects ********************************************************************** There are four types of Git objects: blobs, trees, commits and tags. For each @@ -11,9 +11,42 @@ type. :local: -Objects +Object lookup ================= +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. + +.. method:: Repository.get(oid, default=None) + + 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. + + Example:: + + >>> from pygit2 import Repository + >>> repo = Repository('path/to/pygit2') + >>> obj = repo.get("101715bf37440d32291bde4f58c3142bcf7d8adb") + >>> obj + <_pygit2.Commit object at 0x7ff27a6b60f0> + +.. method:: Repository[oid] + + 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. @@ -51,40 +84,59 @@ This is the common interface for all Git objects: .. automethod:: pygit2.Object.read_raw - - - - - Blobs ================= -A blob is equivalent to a file in a file system.:: +A blob is just a raw byte string. They are the Git equivalent to files in +a filesytem. - >>> # 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' +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 -To create new blobs use the Repository API: + 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 -It is also possible to get the oid for a blob without really adding it to -the Git object database: +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 ================= diff --git a/docs/repository.rst b/docs/repository.rst index 2f34c14..24f0674 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -9,16 +9,19 @@ existing one. :local: -Creating a repository +Functions =================================== .. autofunction:: pygit2.init_repository -Example:: + Example:: + + >>> from pygit2 import init_repository + >>> repo = init_repository('test') # Creates a non-bare repository + >>> repo = init_repository('test', bare=True) # Creates a bare repository + +.. autofunction:: pygit2.discover_repository - >>> from pygit2 import init_repository - >>> repo = init_repository('test') # Creates a non-bare repository - >>> repo = init_repository('test', bare=True) # Creates a bare repository The Repository class @@ -47,9 +50,3 @@ Below there are some general attributes and methods: .. autoattribute:: pygit2.Repository.is_empty .. automethod:: pygit2.Repository.read .. automethod:: pygit2.Repository.write - - -Utilities -========= - -.. autofunction:: pygit2.discover_repository 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/repository.c b/src/repository.c index fe63f67..59c4443 100644 --- a/src/repository.c +++ b/src/repository.c @@ -592,9 +592,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) @@ -616,9 +617,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) @@ -639,9 +642,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) From d44f7aa9a9f9528afde6280ceb6eff365aa3d09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 11 May 2013 15:22:28 +0200 Subject: [PATCH 04/16] docs: a little more --- docs/objects.rst | 54 +++++++++++++++++++++++++++++------------------ src/index.c | 2 +- src/repository.c | 13 ++++++------ src/treebuilder.c | 2 +- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index 6f778e9..04f466e 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -134,24 +134,48 @@ creating the blob object: .. 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:: +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:: - >>> # Number of entries >>> tree = commit.tree - >>> len(tree) + >>> len(tree) # Number of entries 6 - >>> # Iteration - >>> for entry in tree: + >>> for entry in tree: # Iteration ... print(entry.hex, entry.name) ... 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore @@ -161,24 +185,14 @@ interfaces:: 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test - >>> # Get an entry by name - >>> entry = tree['pygit2.c'] + >>> entry = tree['pygit2.c'] # Get an entry by name >>> entry - >>> # Get the object the entry points to - >>> blob = repo[entry.oid] + >>> blob = repo[entry.oid] # Get the object the entry points to >>> blob -.. automethod:: pygit2.Tree.diff - -.. autoattribute:: pygit2.TreeEntry.name -.. autoattribute:: pygit2.TreeEntry.oid -.. autoattribute:: pygit2.TreeEntry.hex -.. autoattribute:: pygit2.TreeEntry.filemode - - Creating trees -------------------- diff --git a/src/index.c b/src/index.c index 55fe01a..3b8447c 100644 --- a/src/index.c +++ b/src/index.c @@ -372,7 +372,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/repository.c b/src/repository.c index 59c4443..3468ffc 100644 --- a/src/repository.c +++ b/src/repository.c @@ -505,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."); @@ -665,9 +665,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) @@ -749,9 +749,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) @@ -1209,12 +1209,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/treebuilder.c b/src/treebuilder.c index 55601e5..8f52596 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -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."); From 405d5ccdfb5fab99474158c419cedc009df6ae4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 11 May 2013 19:38:17 +0200 Subject: [PATCH 05/16] Rename and review py_str_to_git_oid* --- src/index.c | 6 ++-- src/oid.c | 74 ++++++++++++++++++++++++----------------------- src/oid.h | 6 ++-- src/reference.c | 9 ++---- src/repository.c | 61 +++++++++++++++++++------------------- src/treebuilder.c | 6 ++-- src/walker.c | 14 ++++----- 7 files changed, 86 insertions(+), 90 deletions(-) diff --git a/src/index.c b/src/index.c index 3b8447c..c62e5f2 100644 --- a/src/index.c +++ b/src/index.c @@ -352,10 +352,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); diff --git a/src/oid.c b/src/oid.c index d1751ce..f96fd1e 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; 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/reference.c b/src/reference.c index 17537e7..1e302c7 100644 --- a/src/reference.c +++ b/src/reference.c @@ -226,7 +226,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 +234,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) diff --git a/src/repository.c b/src/repository.c index 3468ffc..0cc7c3b 100644 --- a/src/repository.c +++ b/src/repository.c @@ -254,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); @@ -339,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); @@ -522,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; @@ -548,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; @@ -565,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); @@ -683,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, @@ -695,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); @@ -717,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, @@ -762,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, @@ -772,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); @@ -885,14 +886,13 @@ 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) @@ -1013,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; @@ -1027,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); diff --git a/src/treebuilder.c b/src/treebuilder.c index 8f52596..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); 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) From 8c69751d4fbae6b70015d045632bb65ceae31e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 11 May 2013 21:55:28 +0200 Subject: [PATCH 06/16] Fix compilation warnings --- src/oid.c | 3 +++ src/utils.h | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/oid.c b/src/oid.c index f96fd1e..d1db630 100644 --- a/src/oid.c +++ b/src/oid.c @@ -248,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); 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) \ From 3dd998c061f769081e385ecc4065e05b1d362cd3 Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Wed, 15 May 2013 18:15:00 -0300 Subject: [PATCH 07/16] Supporting clone in pygit2 --- pygit2/__init__.py | 19 ++++++++++++++ src/pygit2.c | 56 +++++++++++++++++++++++++++++++++++++++++ test/test_repository.py | 42 ++++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index aeb7fe5..44b31e7 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -47,3 +47,22 @@ def init_repository(path, bare=False): """ _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*. + + Parameters: + * If 'bare' is True, then a bare git repository will 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 fetch url. + * 'fetch_spec' is the fetch specification to be used for fetching. 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 + """ + + _pygit2.clone_repository(url, path, bare, remote_name, push_url, fetch_spec, push_spec, checkout_branch) + return Repository(path) diff --git a/src/pygit2.c b/src/pygit2.c index c68e530..ed1ad9d 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -97,6 +97,61 @@ 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, *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" @@ -172,6 +227,7 @@ 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/test/test_repository.py b/test/test_repository.py index b11c804..f1771e9 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -40,7 +40,7 @@ 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 @@ -291,6 +291,46 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase): self.assertTrue(self.repo.head_is_orphaned) self.assertFalse(self.repo.head_is_detached) +class CloneRepositoryTest(utils.NoRepoTestCase): + def test_clone_repository(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir) + self.assertFalse(repo.is_empty) + self.assertFalse(repo.is_bare) + + def test_clone_bare_repository(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, bare=True) + self.assertFalse(repo.is_empty) + self.assertTrue(repo.is_bare) + + def test_clone_remote_name(self): + repo = clone_repository("./test/data/testrepo.git/", 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 = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_url="custom_push_url") + self.assertFalse(repo.is_empty) + # not sure how to test this... couldn't find pushurl + # self.assertEqual(repo.remotes[0].pushurl, "custom_push_url") + + def test_clone_fetch_spec(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, fetch_spec="refs/heads/test") + self.assertFalse(repo.is_empty) + # not sure how to test this either... fetchspec seems to be going through, but repo is not getting it. + # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") + + def test_clone_push_spec(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_spec="refs/heads/test") + self.assertFalse(repo.is_empty) + # 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 = clone_repository("./test/data/testrepo.git/", self._temp_dir, checkout_branch="test") + self.assertFalse(repo.is_empty) + # not sure how to test this either... couldn't find current branch + # self.assertEqual(repo.remotes[0].current_branch, "test") + if __name__ == '__main__': unittest.main() From 4070c583f14b40eec0f0bbdbb419e388ae09502a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 16 May 2013 10:25:30 +0200 Subject: [PATCH 08/16] Explain what "latest libgit2" means better pygit2 targets the master branch, and "latest" isn't well-defined in this case, as libgit2's default branch is development. --- docs/install.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 From a831b8b6f6c588a0a7e172e681523aad3f9a6cf8 Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 10:04:17 -0300 Subject: [PATCH 09/16] Since I was PEP8-ing my tests, I decided to do it for the whole file. --- test/test_repository.py | 64 ++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/test/test_repository.py b/test/test_repository.py index f1771e9..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, clone_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): @@ -291,44 +297,68 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase): self.assertTrue(self.repo.head_is_orphaned) self.assertFalse(self.repo.head_is_detached) + class CloneRepositoryTest(utils.NoRepoTestCase): def test_clone_repository(self): - repo = clone_repository("./test/data/testrepo.git/", self._temp_dir) + 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 = clone_repository("./test/data/testrepo.git/", self._temp_dir, bare=True) + 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 = clone_repository("./test/data/testrepo.git/", self._temp_dir, remote_name="custom_remote") + 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 = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_url="custom_push_url") + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, push_url="custom_push_url" + ) self.assertFalse(repo.is_empty) - # not sure how to test this... couldn't find pushurl + # 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 = clone_repository("./test/data/testrepo.git/", self._temp_dir, fetch_spec="refs/heads/test") + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, fetch_spec="refs/heads/test" + ) self.assertFalse(repo.is_empty) - # not sure how to test this either... fetchspec seems to be going through, but repo is not getting it. + # 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 = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_spec="refs/heads/test") + 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 = clone_repository("./test/data/testrepo.git/", self._temp_dir, checkout_branch="test") + repo_path = "./test/data/testrepo.git/" + repo = clone_repository( + repo_path, self._temp_dir, checkout_branch="test" + ) self.assertFalse(repo.is_empty) - # not sure how to test this either... couldn't find current branch + # FIXME: When pygit2 supports retrieving the current branch, + # enable this test # self.assertEqual(repo.remotes[0].current_branch, "test") From 57ea19e90589f506243a5e9128ca37a063b33a4f Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 10:06:46 -0300 Subject: [PATCH 10/16] Now __init__ is pep8-compliant --- pygit2/__init__.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 44b31e7..babce1a 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -50,19 +50,27 @@ def init_repository(path, bare=False): def clone_repository( - url, path, bare=False, remote_name="origin", push_url=None, fetch_spec=None, + 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*. Parameters: - * If 'bare' is True, then a bare git repository will 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 fetch url. - * 'fetch_spec' is the fetch specification to be used for fetching. 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 + * '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 fetch url. + * 'fetch_spec' is the fetch specification to be used for fetching. + 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 """ - _pygit2.clone_repository(url, path, bare, remote_name, push_url, fetch_spec, push_spec, checkout_branch) + _pygit2.clone_repository( + url, path, bare, remote_name, push_url, + fetch_spec, push_spec, checkout_branch) return Repository(path) From a377b32b603b82aaa9878679b9ae0ac583ecce34 Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 10:25:47 -0300 Subject: [PATCH 11/16] Documentation for the new clone method. Even though I am aware that the comment lines in pygit2/__init__.py are longer than 79 characters, there's a reason for that. If I break them, they'll show poorly in the documentation, and in my opinion better docs trump the 80 char requirement. If you think it's still better to have less than 80 characters in the comments, I'll gladly resubmit the changes at the expense of the documentation. --- docs/repository.rst | 11 +++++++++++ pygit2/__init__.py | 26 ++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/repository.rst b/docs/repository.rst index 24f0674..034ed56 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -20,6 +20,17 @@ Functions >>> repo = init_repository('test') # Creates a non-bare repository >>> repo = init_repository('test', bare=True) # Creates a bare repository +.. autofunction:: pygit2.clone_repository + + Example:: + + >>> 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 diff --git a/pygit2/__init__.py b/pygit2/__init__.py index babce1a..be87a49 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -56,18 +56,20 @@ def clone_repository( """ Clones a new Git repository from *url* in the given *path*. - Parameters: - * '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 fetch url. - * 'fetch_spec' is the fetch specification to be used for fetching. - 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 + * **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( From 032faf7017d9b5ac60c37f126697539e0dc8f63b Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 16:09:39 -0300 Subject: [PATCH 12/16] Fixing 80-column width for both the python and C code. --- pygit2/__init__.py | 25 ++++++++++++++++++------- src/pygit2.c | 19 +++++++++++++------ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index be87a49..e246ff8 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -56,16 +56,27 @@ def clone_repository( """ 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*. + **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: + 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 diff --git a/src/pygit2.c b/src/pygit2.c index ed1ad9d..d062a9e 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -98,9 +98,11 @@ init_repository(PyObject *self, PyObject *args) { }; PyDoc_STRVAR(clone_repository__doc__, - "clone_repository(url, path, bare, remote_name, push_url, fetch_spec, push_spec, checkout_branch)\n" + "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" + "Clones a Git repository in the given url to the given path " + "with the specified options.\n" "\n" "Arguments:\n" "\n" @@ -115,11 +117,14 @@ PyDoc_STRVAR(clone_repository__doc__, "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" + " 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" + " 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"); + " The name of the branch to checkout. None means use the remote's " + "HEAD.\n"); PyObject * @@ -131,7 +136,9 @@ clone_repository(PyObject *self, PyObject *args) { const char *remote_name, *push_url, *fetch_spec, *push_spec, *checkout_branch; int err; - if (!PyArg_ParseTuple(args, "zzIzzzzz", &url, &path, &bare, &remote_name, &push_url, &fetch_spec, &push_spec, &checkout_branch)) + if (!PyArg_ParseTuple(args, "zzIzzzzz", + &url, &path, &bare, &remote_name, &push_url, + &fetch_spec, &push_spec, &checkout_branch)) return NULL; git_clone_options opts = { From 68e3e06a88bc7b781bc029f7241cd9125a370326 Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Thu, 16 May 2013 16:13:23 -0300 Subject: [PATCH 13/16] Missed a couple lines in the C code --- src/pygit2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pygit2.c b/src/pygit2.c index d062a9e..8362891 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -133,7 +133,8 @@ clone_repository(PyObject *self, PyObject *args) { const char *url; const char *path; unsigned int bare; - const char *remote_name, *push_url, *fetch_spec, *push_spec, *checkout_branch; + const char *remote_name, *push_url, *fetch_spec; + const char *push_spec, *checkout_branch; int err; if (!PyArg_ParseTuple(args, "zzIzzzzz", @@ -234,7 +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__}, + {"clone_repository", clone_repository, METH_VARARGS, + clone_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, From c5606d94f21465d9ad0ee3d26bf835d0233d39d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Thu, 16 May 2013 23:23:57 +0200 Subject: [PATCH 14/16] Add Repository pointer to Reference. As asked in #221. --- src/reference.c | 12 +++++++++--- src/reference.h | 2 +- src/repository.c | 8 ++++---- src/types.h | 5 +---- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/reference.c b/src/reference.c index 1e302c7..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); } @@ -461,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 0cc7c3b..af388f4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -168,7 +168,7 @@ Repository_head__get__(Repository *self) return NULL; } - return wrap_reference(head); + return wrap_reference(head, self); } int @@ -859,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__, @@ -898,7 +898,7 @@ Repository_create_reference_direct(Repository *self, PyObject *args, 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__, @@ -932,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); } diff --git a/src/types.h b/src/types.h index fdc451e..a4ac5ec 100644 --- a/src/types.h +++ b/src/types.h @@ -159,10 +159,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 From e0f4bb27a9dd4429486f32a464ae0ddad5427cb6 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Fri, 17 May 2013 17:44:32 +1000 Subject: [PATCH 15/16] README: fix typo --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 8679049..7f5550d 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ Pygit2 links: Quick install guide =================== -1. Checkout libgi2 v0.18.0:: +1. Checkout libgit2 v0.18.0:: $ git clone git://github.com/libgit2/libgit2.git -b v0.18.0 From 4f83209a9ad08bb31c496d8493ce60f858e79736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 17 May 2013 10:04:25 +0200 Subject: [PATCH 16/16] Install notes, checkout from libgit2 master Some older versions of Git do not support to clone and checkout from a tag. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 7f5550d..b0bce57 100644 --- a/README.rst +++ b/README.rst @@ -20,9 +20,9 @@ Pygit2 links: Quick install guide =================== -1. Checkout libgit2 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