Compare commits

...

361 Commits

Author SHA1 Message Date
Bogdan Vasilescu
371c97cadb Update diff.rst
`old_lineno` appeared twice. I think it should be `new_lineno` instead.
2017-07-18 12:58:56 -04:00
anatoly techtonik
ab3bb01249 install.rst: Use PyPI wheels on Windows 2017-07-07 21:24:04 +03:00
J. David Ibáñez
62c70e852d Release 0.26.0 2017-07-06 17:12:44 +02:00
J. David Ibáñez
e81f45c4c6 Merge remote-tracking branch 'nhynes/br-is-co' 2017-07-05 23:20:15 +02:00
J. David Ibáñez
295166bb64 Fixing AppVeyor & Travis
AppVeyor, upgrade to 0.26
Travis, disable pypy3 for now
2017-07-03 22:55:10 +02:00
Carlos Martín Nieto
96d37e16a9 Update install docs with the v0.26 version of libgit2 2017-07-03 15:48:20 +02:00
Carlos Martín Nieto
2ce8b952c4 Update Travis CI script to use libgit2 v0.26 2017-07-03 15:47:52 +02:00
Carlos Martín Nieto
e41f71f458 Update for libgit2 v0.26
The API isn't changing as much recently so all we needed to adjust was the
proper capitalisation of the error string.
2017-07-03 15:40:29 +02:00
Mark Adams
c6305a062b revert: Add support for git_revert_commit via Repository.revert_commit()
This change adds `Repository.revert_commit()` which wraps around
`git_revert_commit` which will return an `Index` with the appropriate
changes to revert the specified commit.

Fixes #710
2017-06-05 16:08:39 -05:00
Mark Adams
87beb76dcc Modify Index.write_tree() to have more robust Repository detection
Currently, Index.write_tree() relies on either the caller passing in a
`repo=` arg or the underlying `git_index` being already bound to a
`git_repository`. This ignores the case where the caller does not pass
a `repo` argument to `Index.write_tree()` but the `Index._repo`
property is populated on the index.

This change modifies Index.write_tree() to use the passed-in `repo`
argument, falls back to using `Index._repo` and then assumes that
`git_index` must be bound to a `git_repository`. This change should make
Index.write_tree() a little more robust in the most common use-case.
2017-06-05 16:02:01 -05:00
Lukas Fleischer
f18de427bf Randomize OID object hashes
Instead of using type punning to convert the OID to a Python hash, use
_Py_HashBytes() to hash the OID again. This means we no longer make any
assumptions on the internal representation of OID values or Python
hashes (before this commit, we at least relied on the fact that OID
hases are longer than Python hashes). Moreover, the random seed stored
in PYTHONHASHSEED is now honored.

This also fixes a compiler warning seen with -Wstrict-aliasing.

Signed-off-by: Lukas Fleischer <lfleischer@lfos.de>
2017-05-07 10:03:17 +02:00
J. David Ibáñez
f841c62fa6 Drop support for Python 3.2, cffi 1.10 doesn't work with 2017-05-07 09:53:10 +02:00
Lukas Fleischer
035d4a9396 Mark index unsigned in get_pylist_from_git_strarray()
Signed-off-by: Lukas Fleischer <lfleischer@lfos.de>
2017-05-07 07:47:00 +02:00
Lukas Fleischer
21d668421f Remove unused variable from Repository_init()
Signed-off-by: Lukas Fleischer <lfleischer@lfos.de>
2017-05-07 07:44:01 +02:00
Lukas Fleischer
b88dc86842 Fix parameter passing of describe patterns
When ffi.new() is used to build a new pointer object, the returned
pointer object has ownership on the allocated memory. When it is
garbage-collected, then the memory is freed. Thus, we need to make sure
the original object survives its use, otherwise the casted pointer will
point to garbage.

This fixes one test which was failing with the latest CFFI version, see
issue #694. Thus, this commit also reverts 803b1cb (cffi 1.10 not yet
supported, 2017-03-22) where the latest CFFI version was marked as
unsupported.

Signed-off-by: Lukas Fleischer <lfleischer@lfos.de>
2017-05-06 21:49:47 +02:00
J. David Ibáñez
8559b2da20 Release 0.25.1 2017-04-25 19:08:10 +02:00
J. David Ibáñez
9325494d6f docs: fix references 2017-04-21 11:36:40 +02:00
J. David Ibáñez
74717bed55 Merge remote-tracking branch 'tmr232/references-object' 2017-04-21 11:13:09 +02:00
Tamir Bahar
3818555e14 Added Repository.references accessor. 2017-04-21 08:51:41 +00:00
J. David Ibáñez
706c60c4ab Clean some cffi warnings
"has no values explicitly defined"
2017-04-20 22:32:03 +02:00
J. David Ibáñez
0733ba4da3 docs: fix make html 2017-04-19 21:34:07 +02:00
Tamir Bahar
320ee5e733 Added GIT_BRANCH_ALL 2017-04-19 21:10:11 +03:00
Tamir Bahar
d6716e035a Updated docs to match new branches API. 2017-04-19 21:03:43 +03:00
Tamir Bahar
d14438725e Added basic Repository.branches implementation. 2017-04-17 23:02:58 +03:00
J. David Ibáñez
dd57c9b366 Fixing error on Python 2.7 / Windows 64bits 2017-04-09 13:11:50 +02:00
J. David Ibáñez
f2c89a760a (partial) review the way we handle Python int/long 2017-04-09 12:33:24 +02:00
J. David Ibáñez
f37cf25b8e Fix warning, and coding style a bit 2017-04-08 16:56:04 +02:00
J. David Ibáñez
11ff7a99eb Options, improve error messages 2017-04-08 11:16:15 +02:00
J. David Ibáñez
9335819795 Opening repos, raise more meaningful exceptions 2017-04-05 22:18:21 +02:00
Tamir Bahar
784583d21e Fixed indentation 2017-04-05 21:59:30 +03:00
Tamir Bahar
b81810e9cb Better error messages for opening repos.
Closes #645.
2017-04-05 20:59:36 +03:00
Tamir Bahar
7ee851273a Added explicit check for path = None in init_repository
Closes #688.
2017-04-05 16:37:25 +03:00
Nick Hynes
1fadc2eae0 Wrap branch_is_checked_out 2017-03-29 18:14:46 -04:00
Nick Hynes
453fd8a9a3 Add docs 2017-03-27 17:10:51 -04:00
Nick Hynes
630d905e73 Add stash tests 2017-03-27 17:10:45 -04:00
Nick Hynes
9be907983f Wrap stash_pop 2017-03-27 16:42:38 -04:00
Nick Hynes
b31ac50210 Wrap stash_drop 2017-03-27 16:33:32 -04:00
Nick Hynes
da233b16c5 Wrap git_stash_apply 2017-03-27 16:31:21 -04:00
Nick Hynes
1a842ff8bd Wrap git_stash_save 2017-03-27 16:20:33 -04:00
Nick Hynes
cbbf9f1f87 Fix Signature._pointer 2017-03-27 16:18:21 -04:00
Nick Hynes
a294655aa5 Add declarations for git-stash 2017-03-27 16:18:13 -04:00
J. David Ibáñez
5a940987cd Add support for Python 3.6 2017-03-22 21:45:12 +01:00
J. David Ibáñez
354d56a95c Update copyright years 2017-03-22 21:15:34 +01:00
Jason Ziglar
8327e1bee3 Add test for init_submodules() and update_submodules() 2017-03-22 11:23:17 -04:00
Jason Ziglar
15326b731f Add update_submodules() command 2017-03-22 11:23:17 -04:00
Jason Ziglar
f5cd6da307 Add init_submodules() command 2017-03-22 11:23:17 -04:00
J. David Ibáñez
803b1cb154 cffi 1.10 not yet supported 2017-03-22 16:01:23 +01:00
J. David Ibáñez
d622e87654 test options, avoid side effects
This makes tests in PR#692 to pass
2017-03-22 13:38:11 +01:00
J. David Ibáñez
819cbff552 Remove some tabs 2017-02-09 17:31:50 +01:00
Matthaus Woolard
4fbc1f1c05 Add support for custom backends
Signed-off-by: Matthaus Woolard <matthaus.woolard@gmail.com>
2017-02-09 15:57:56 +00:00
Tamir Bahar
5c061cbb0a Remove unused code
Removed a chunk of code from `checkout` that did nothing, but had a bug.
When checking out from a branch-less state (like the state when a repository is first initialized) the code failed.
The failure was due to trying to get some properties of the current branch, which were never used in the code.
2017-01-03 01:17:48 +02:00
J. David Ibáñez
68817aad4f Release 0.25.0 2016-12-26 12:30:36 +01:00
J. David Ibáñez
0b513d57fa Merge remote-tracking branch 'carlos/next' 2016-12-23 21:37:58 +01:00
Carlos Martín Nieto
074a726d7f Update versions to libgit2 v0.25.0 2016-12-23 20:24:37 +00:00
J. David Ibáñez
950249442e Merge remote-tracking branch 'bisho/master' 2016-12-20 20:02:47 +01:00
Guillermo Pérez
809fe33b8a Make pygit2 throw if tree of a commit is not found
Commit objects in git always have a tree_id associated, that points
to the corresponding Tree object.

When the tree object is missing, the repo is corrupted.

In those cases:
* official git cli fatals with status code 128 and message:
  fatal: unable to read tree <hash>
* libgit2 returns error GIT_ENOTFOUND when calling git_commit_tree()
* pygit2 when accessing the tree by id with repo[commit.tree_id]
raises a KeyError: <hash>

But on the other hand, on the commit object, rather than throwing
and exception, pygit2 is swallowing the error returned by libgit2
and setting the <Commit object>.tree property to None.

None is arguable the wrong choice to encode an error condition,
specially in python that is used heavily.

In particular this caused in our system to assume there was
an empty tree, and the sync service that tails git repo changes
decided to DELETE everything. The code was using None to
represent empty tree, usefull for example when we need to
compare a path between two commits (the path might be non-existant
at one of the commits you are comparing).

I think that in this case the right decision would be to raise
since is an exceptional case, caused by a corrupted repo,
is more consistent with other tools, and ensures user code
does not take the wrong decissions.

For curiosity the corrupted repository can happen more commonly
than expected. We run our repositories on a shared NFS filer,
and one of our servers didn't have the lookupcache=positive
flag. This makes NFS cache the metadata (files on a directory
for example) and use that for negative lookups (to deny existance
of files). In this case, the commit object was on a directory
not cached, so the commit was seen immediately, but the tree
object was in a folder that was cached, the cache didn't
contained the tree object, and thus for some seconds the tree
was not existing and the repo was corrupted. Our sync service
saw tree being None and decided to delete everything, causing
a lot of issues down the way.
2016-12-19 11:28:00 -08:00
Szucs Krisztian
6402302002 fixed cached memory tests 2016-12-03 14:01:39 +01:00
Szucs Krisztian
77f0585645 Added mwindow_mapped_limit, cached_memory, enable_caching, cache_max_size, cache_object_limit options 2016-12-03 13:52:19 +01:00
Carlos Martín Nieto
acdec78617 Update for v0.24+1 2016-11-14 16:40:36 +01:00
J. David Ibáñez
66280af83a Release 0.24.2 2016-11-01 20:34:50 +01:00
J. David Ibáñez
b2a34bd901 Merge remote-tracking branch 'fourplusone/master' 2016-11-01 13:41:23 +01:00
Matthias Bartelmeß
44ef9ad2e9 Remove python 3.2 from appveyor file since it is not supported 2016-10-30 11:55:36 +01:00
Matthias Bartelmeß
db213113f4 Fix/appveyor all (#1)
* Create python-33.yml

* Delete python-33.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Use weakref for conflicts caching

To prevent GC issues for python <= 3.3, use a weak reference for Index._conflicts

* Update index.py

* Update appveyor.yml
2016-10-30 12:52:25 +02:00
Robert Hölzl
ee28de65a0 Extend comment of Remote.push by note about push_update_reference.
As happened in support request https://github.com/libgit2/libgit2/issues/3963 it can be easily overseen,
that push returns True, when the remote has installed a hook that denies the commits.
2016-10-23 23:55:53 +02:00
Matthias Bartelmeß
825f3e45bd add slashes to URL if needed 2016-10-21 06:31:19 +03:00
Matthias Bartelmess
00dd78bf1b Fix windows tests 2016-10-21 06:06:40 +03:00
anatoly techtonik
e2393a5e24 appveyor.yml Fix nosetests report upload
/xunit/ endpoint in AppVeyor is made for uploading xUnit.net reports
http://help.appveyor.com/discussions/problems/5264-xunit-results-are-not-being-parsed
2016-10-18 13:51:03 +03:00
anatoly techtonik
da59cb1c92 appveyor.yml Attempt to fix test failure reporting
https://www.appveyor.com/docs/build-configuration/#script-blocks-in-build-configuration
2016-10-18 12:40:05 +03:00
anatoly techtonik
e873c6a363 appveyor.yml Attempt to fix test run
broken by 865c2f0
2016-10-15 20:17:28 +03:00
J. David Ibáñez
865c2f0e82 Revert "Update appveyor.yml"
This reverts commit faf6a63d25d9c12d7480e9b4faf4d6531b13b7c3.
2016-10-14 09:45:34 +02:00
J. David Ibáñez
6d6931cd26 Merge remote-tracking branch 'fourplusone/fix/windows-tests' 2016-10-13 12:26:19 +02:00
J. David Ibáñez
9b364dc7f3 Readme, badge to appveyor 2016-10-07 17:40:25 +02:00
Carlos Martín Nieto
578cf58cd7 Add Repository.create_reference to the automethod list 2016-10-03 20:31:56 +02:00
J. David Ibáñez
554f167353 Merge remote-tracking branch 'mrh/fix-non-ascii-errmsg' 2016-09-15 09:25:08 +02:00
mrh1997
54e4da837b Support non-english errors with non-ascii chars.
Libgit2 partially forwards OS error message texts.
On non-english Windows OSes these errors may contain non-ascii characters (i.e. umlauts).
To avoid that a UnicodeDecodeError is raised the error message is interpreted as UTF-8.
The solution should not be necessary on linux/osx as they return always ascii (as far as I know).
Thus this solution will not change the behaviour on linux/osx.
2016-09-13 22:31:49 +02:00
Matthias Bartelmeß
faf6a63d25 Update appveyor.yml 2016-07-24 22:57:05 +02:00
Matthias Bartelmeß
30980751cf Update appveyor.yml 2016-07-24 22:55:05 +02:00
Matthias Bartelmeß
dae61ded38 Update appveyor.yml 2016-07-24 22:52:41 +02:00
Matthias Bartelmeß
0e270c72bd Update appveyor.yml 2016-07-24 22:12:00 +02:00
Matthias Bartelmeß
1afbde0d7f install dependencies in init script 2016-07-24 21:56:21 +02:00
Matthias Bartelmeß
7fe7a4da8d Remove qoutes from env vars 2016-07-24 21:50:17 +02:00
Matthias Bartelmeß
fd1e9e3d35 Update appveyor.yml
upload test results
2016-07-24 15:54:54 +02:00
Matthias Bartelmeß
cdd57b2c0f make sure the repo object will be collected before the repo is removed 2016-07-22 10:46:11 +02:00
mrh1997
ffc514fa24 Ammend the doc-string of Repository.diff()
According to the old documentation, it was not clear how to compare 
working directory/index to a git object.
2016-07-11 14:41:39 +02:00
Matthias Bartelmeß
c57a3aeb22 install locally 2016-07-03 17:01:26 +02:00
Matthias Bartelmeß
ca444f3c7d Update appveyor.yml 2016-07-03 16:54:40 +02:00
Matthias Bartelmeß
96beae5c82 fix yaml error 2016-07-03 16:15:41 +02:00
Matthias Bartelmeß
68de0f8bb6 fix yaml error 2016-07-03 16:14:26 +02:00
Matthias Bartelmeß
df53551cb2 Build 32 and 64 bit version 2016-07-03 16:13:21 +02:00
Matthias Bartelmeß
5864b17b57 archive wheels 2016-07-03 16:03:05 +02:00
Matthias Bartelmeß
6c4fa88d07 using cd,cd .. instead of popd 2016-07-03 15:49:39 +02:00
Matthias Bartelmeß
e96d0286b8 Create appveyor.yml 2016-07-03 15:41:23 +02:00
J. David Ibáñez
4fa43e234e docs, update version 2016-06-21 23:15:56 +02:00
J. David Ibáñez
4416f65fe1 Release 0.24.1 2016-06-21 23:04:12 +02:00
Ondřej Nový
5dcc793aff Make build reproducible
https://wiki.debian.org/ReproducibleBuilds
2016-06-07 22:33:46 +02:00
J. David Ibáñez
30f539ff35 Merge remote-tracking branch 'seanfarley/smf/pygit2-upgrade' 2016-06-02 16:18:07 +02:00
Carlos Martín Nieto
df30f9213f Remove checks for obsolete methods
This is not how you define your callbacks, so this test isn't testing
for anything useful.
2016-04-29 13:19:32 +02:00
Ondřej Nový
50c0569cf0 Fixed typo 2016-04-18 21:58:58 +02:00
J. David Ibáñez
d8fd2e78d8 Merge remote-tracking branch 'ignatenkobrain/gh620' 2016-04-17 20:48:56 +02:00
Igor Gnatenko
fd9a39a91b repository: decode() linkname
Reference: https://github.com/libgit2/pygit2/issues/620
Signed-off-by: Igor Gnatenko <ignatenko@redhat.com>
2016-04-15 14:13:47 +02:00
Igor Gnatenko
270dad8cd3 repository: SYMTYPE is constant in module tarfile, not in any class
Reference: https://github.com/libgit2/pygit2/issues/618
Signed-off-by: Igor Gnatenko <ignatenko@redhat.com>
2016-04-15 14:07:33 +02:00
Yu Jianjian
51915ddf0e wrong order of the args in docstring of write_archive 2016-03-23 23:33:20 +08:00
Dustin Raimondi
f2864c0511 fix addition occurence of libgit2 version number 2016-03-11 10:24:53 -05:00
Dustin Raimondi
456bf59a88 bump libgit2 version number 2016-03-11 09:38:08 -05:00
J. David Ibáñez
22021c67fc Release 0.24.0 2016-03-05 23:21:05 +01:00
Carlos Martín Nieto
c1d831c98a Update to libgit2 v0.24 2016-03-04 17:08:55 +01:00
J. David Ibáñez
391a3a74e9 Merge remote-tracking branch 'thom/tox' 2016-02-28 17:22:58 +01:00
J. David Ibáñez
141f0abe62 docs: add Repository.path_is_ignored 2016-02-28 14:26:19 +01:00
J. David Ibáñez
6b926494db Merge remote-tracking branch 'ccope/gitignore' 2016-02-28 14:19:35 +01:00
Thom Wiggers
cde5b5170b
Allow testing with tox 2016-02-28 12:53:35 +01:00
Thom Wiggers
735510f14d
Fix repository crash if path passed is not a str
Tries to decode any non-string objects (such as bytes)

Introduces `six` as a dependency

Closes #588
2016-02-28 12:32:51 +01:00
Thom Wiggers
bc424e342f
Add unit tests for bytes and unicode Repositories
Add unit test for bytes repository paths
Add a unicode path test for Repositories
2016-02-28 12:32:41 +01:00
Thom Wiggers
3470fbc1c6
Add unit test for bytes repository paths 2016-02-27 23:44:05 +01:00
Carlos Martín Nieto
487fb5913e Keep the describe dirty suffix string alive 2016-02-25 08:04:27 +01:00
J. David Ibáñez
33cf1a1ca2 travis: test with Python 3.5 too 2016-01-31 14:14:07 +01:00
Cam Cope
95ad6b1b0a add method to check if a path is ignored 2016-01-25 02:54:00 -08:00
J. David Ibáñez
fa60e2233d Release 0.23.3 2016-01-01 19:18:34 +01:00
Chris Rebert
daff45f2d4 Document that Diff.patch can be None; fixes #467 2015-12-22 12:23:59 -07:00
Chason Chaffin
a5cfea21a7 Fix broken binary diff test
In a recent commit to libgit2, binary diffs were changed to have a
trailing empty line. This broke a test in test_diff because it compares
it directly against the string. I've added the extra line to the
expected output and the test now passes correctly.
2015-12-15 17:20:50 -08:00
Noah Fontes
99dfce9ab8 Add support for Repository.describe(...). 2015-12-05 23:22:37 -08:00
J. David Ibáñez
7a8474cd44 Change Signature default encoding to UTF-8
Fixes #581
2015-11-15 19:46:50 +01:00
Carlos Martín Nieto
91bb93d266 Fix create_blob_fromiobase with pypy
The file object is passed to us, so we never incremented
its refcount. We shouldn't decrement it on exit.
2015-10-31 16:57:20 +01:00
J. David Ibáñez
13f4ddec1d Fix create_blob_fromiobase with Python 2.7 2015-10-25 19:58:48 +01:00
J. David Ibáñez
f92d38e25f Merge remote-tracking branch 'frutiger/blob_from_iobase' into blob_from_io_base 2015-10-25 13:04:49 +01:00
J. David Ibáñez
70edbf256a Update copyright years 2015-10-11 18:48:59 +02:00
J. David Ibáñez
203335bd63 Trying to fix install with pip 2015-10-11 18:42:30 +02:00
J. David Ibáñez
64150d3535 Release 0.23.2 2015-10-11 17:49:17 +02:00
J. David Ibáñez
d25a0d61de Fix error introduced accidentally in previous commit 2015-10-11 12:08:53 +02:00
J. David Ibáñez
f5aa1829ac Do not use hasattr, not robust
In Python versions older than 3.2 hasattr is not robust as it masks real
errors. And usually is not efficient as a call to hasattr is often
followed by another call to getattr. It is best to avoid using it
completely.

See https://docs.python.org/3/whatsnew/3.2.html#other-language-changes
2015-10-11 11:58:07 +02:00
J. David Ibáñez
9db8737364 Update changelog 2015-10-11 11:06:22 +02:00
Nicolás Sanguinetti
cf439e4286 List OpenSSL as a dependency in the docs 2015-10-06 14:44:18 -03:00
Nicolas Dandrimont
eadc2a320f tag._message: avoid NULL pointer dereference
A tag message can be empty. In that case, git_tag_message returns
NULL. PyBytes_FromString doesn't check its argument for nullness, and
therefore accessing _message on a tag with an empty message segfaults
Python.
2015-10-05 19:08:56 +02:00
Nicolas Dandrimont
2b083a1509 Add _name attribute to TreeEntry
This mirrors the _message attribute for Tag, which gives you the raw
bytes from the entry name. Useful to parse repos where some filenames
aren't encoded as utf-8, such as https://github.com/wuts/earthquake.
2015-09-28 18:24:48 +02:00
J. David Ibáñez
681c7d4341 Fixing pip install pygit2
Regression in the latest release, "pip install pygit2" fails if cffi is
not already installed. Should be fixed with this change, not tested.
2015-09-27 14:03:32 +02:00
J. David Ibáñez
8a66da1278 docs: remove reference to old Remote.credentials 2015-09-27 13:57:30 +02:00
J. David Ibáñez
0d2bc05708 Merge remote-tracking branch 'carlos/diff-delta' 2015-09-27 13:55:13 +02:00
Carlos Martín Nieto
b8e6852d26 Add some missing fields to DiffFile's docs 2015-09-27 03:09:25 +02:00
Carlos Martín Nieto
563cb9018e Bring back the certificate check callback
This was implemented for clone, but not for fetch or push, so it was
deleted during the conversion, which shows why we need to unify these
callback structures.
2015-09-27 02:43:50 +02:00
Carlos Martín Nieto
ac2e363d04 Allow setting credentials and certificate in callback ctor
This allows for a less verbose way of setting one-liners as these
callbacks.
2015-09-27 02:43:50 +02:00
Carlos Martín Nieto
ab97c08f72 Make clone take the callbacks object
This lets use the same callbacks for fetch and clone; it also fills in
the callbacks which the clone function did not support.
2015-09-27 02:43:50 +02:00
Carlos Martín Nieto
7b97ade6ce Move remote callbacks to use a class for callbacks
This represents what's going on much better than the remnants from the
older methods. What we do is pass a list of callbacks to libgit2 for it
to call, and they are valid for a single operation, not for the remote
itself.

This should also make it easier to re-use callbacks which have already
been set up.
2015-09-27 02:43:50 +02:00
J. David Ibáñez
e4ef8ea5c2 Release 0.23.1 2015-09-26 20:49:13 +02:00
J. David Ibáñez
50f4b20e7d Update changelog 2015-09-26 13:30:24 +02:00
Guille -bisho-
802976535a Add support for GIT_DIFF_SHOW_BINARY
Adding the binary diff flag GIT_DIFF_SHOW_BINARY to pygit2.
libgit2 0.23.0 already supports this constant to be used in diff
flags and produces properly formated binary diffs.
2015-09-23 16:30:19 -07:00
J. David Ibáñez
ade211de60 tests: fix warning 2015-09-10 11:30:37 +02:00
J. David Ibáñez
8911416d4f Merge remote-tracking branch 'olasd/add-type-to-treeentry' 2015-09-10 11:15:50 +02:00
Nicolas Dandrimont
4b607b8256 Re-enable tests on PyPy 2015-09-09 23:25:15 +02:00
Nicolas Dandrimont
ec23762c09 Add support for cffi-pre-1.0 2015-09-09 23:20:08 +02:00
Nicolas Dandrimont
ac7738bbb3 Add type attribute to TreeEntry
This allows complete iteration and rebuilding of a tree without hitting
the object store for every entry.
2015-09-09 21:41:00 +02:00
J. David Ibáñez
29a8dbc6b2 Temporarily deactivate travis for PyPy and PyPy3
Until travis supports PyPy 2.6, and there is a new release of PyPy3

Update docs regarding cffi and PyPy
2015-09-03 09:24:58 +02:00
Sheeo
f28a199351 Install cffi>=1 on travis 2015-08-28 19:51:39 +02:00
Michael Sondergaard
becc265c78 Don't use the deprecated cffi.verify by default
Instead this does what is recommend in the CFFI docs here: https://cffi.readthedocs.org/en/latest/cdef.html?highlight=verify#out-of-line-api

This also means building the cffi extension is neatly handled by cffi's setuptools integration itself, so we can delete the code in setup.py that used to do this.
2015-08-28 19:36:58 +02:00
David Six
25d02259df Fix: pass push_opts to git_remote_push 2015-08-26 11:15:47 -04:00
J. David Ibáñez
27e3450232 Get ready to release v0.23.0 2015-08-14 16:56:30 +02:00
Carlos Martín Nieto
4f00dad086 Don't throw if there is no merge base
Not finding a merge base between two commits isn't an exceptional case,
it's just a different result.
2015-07-31 10:50:15 +02:00
Santiago Perez De Rosso
9dd74dd593 add doc for DiffLine 2015-07-16 11:35:46 -04:00
J. David Ibáñez
c5eae8942d Merge remote-tracking branch 'carlos/development' 2015-07-12 12:20:27 +02:00
J. David Ibáñez
2fdfdcdc4b Get ready to release v0.22.1 2015-07-12 12:04:58 +02:00
Carlos Martín Nieto
7ff6f6efb7 Add repr output for TreeEntry
When iterating over a tree, its entries show up as objects with an
address, which makes it hard to distinguish.

Add a method to handle repr and make it easier to play around with them
in the console.
2015-07-07 14:06:35 +02:00
Carlos Martín Nieto
f09bbe79a8 Update installation docs with v0.23 2015-07-06 18:49:20 +02:00
Carlos Martín Nieto
81520c9c62 Update to libgit2 v0.23 2015-07-06 18:49:20 +02:00
J. David Ibáñez
6939b9b203 Fix indent error 2015-06-16 20:06:29 +02:00
Justin Clift
cc898d29e5 Typo fix 2015-06-16 17:13:50 +01:00
J. David Ibáñez
060b3fbaec Update changelog 2015-06-05 19:26:30 +02:00
vtemian
5469f0c891 Remove items from index, based on stage 2015-06-03 17:31:59 +03:00
Carlos Martín Nieto
74b81bf180 Add support for querying attributes
Expose a method in the repository which allows querying an attribute for
a file and converts the result to the python equivalent.
2015-05-20 20:56:40 +02:00
J. David Ibáñez
e46119838b Merge remote-tracking branch 'rmoehn/cherry-pick-cleanup' 2015-05-03 11:37:57 +02:00
J. David Ibáñez
8a196f656b Merge remote-tracking branch 'cjwatson/merge-index-path-refs' 2015-05-03 11:33:28 +02:00
J. David Ibáñez
deb50536f0 docs: remove manual singatures from py files
These are only needed in C code. With Python files the signatures are
automatically generated. The only drawback is the return value is not
included in the signature, so document it in the body of the docstring.
2015-05-03 11:16:17 +02:00
J. David Ibáñez
6da3d8f8a8 Fix a couple of warnings shown by pyflakes 2015-05-03 11:15:51 +02:00
J. David Ibáñez
52dd956896 Merge remote-tracking branch 'rmoehn/optional-args' 2015-05-03 10:13:55 +02:00
J. David Ibáñez
1f98ba6495 docs: fix build errors 2015-05-03 10:07:00 +02:00
J. David Ibáñez
d63c2d4fd7 Merge remote-tracking branch 'carlos/diff-stats' 2015-04-29 10:47:21 +02:00
Carlos Martín Nieto
5b50579790 Add a recipe for git clone --mirror
It's not necessarily obvious how to perform a mirror, so add a recipe
which tells what git does as well as provide example code of how to
perform the same steps in pygit2.
2015-04-28 19:53:39 +02:00
Carlos Martín Nieto
42d81e33ec Add DiffStats
This wraps git_diff_stats and can be retrieved through a Diff. It
includes a formatting method.
2015-04-28 16:21:56 +02:00
Carlos Martín Nieto
0ce4d3b9a8 Allow creating a remote with a particular fetch refspec
This makes it a lot more convenient to create a remote and override the
fetch refspec it gets created with.
2015-04-28 13:39:15 +02:00
Carlos Martín Nieto
3091c7aa87 Remove mistaken submodule in the tree
The path test/data/testrepo was mistakenly added as a submodule and the
error was not caught before merging. Remove this path as it should not
exist on the repo.
2015-04-28 01:55:20 +02:00
Richard Möhn
654e4bf56f Cherry-pick recipe: clean up after picking
In b3025e3fe I had written that when following my recipe, the repository
remains in cherry-picking mode afterwards. In issue #516 I was told that
Repository.state_cleanup() is needed to correct that. Therefore add it
to the recipe.

Also add a note near the documentation for cherry-pick, so that nobody
will overlook this again. Apparently there are other times when you need
to do Repository.state_cleanup() as well, but it's not documented, I
don't know when and I don't want to take the time and find out. So leave
it at that for now.
2015-04-25 14:49:48 +09:00
J. David Ibáñez
c072a77e4b Merge remote-tracking branch 'rmoehn/diff-iter' 2015-04-23 12:45:44 +02:00
J. David Ibáñez
d3d60c75f8 Merge remote-tracking branch 'rmoehn/create_commit_arg' 2015-04-23 12:35:17 +02:00
Richard Möhn
1b9cb54927 Add hint to Diff.__iter__()
I'm not the guy who looks at examples in the first place and I guess
there are other people like me. When I wanted find out how to get
information out of a Diff, I looked at the documented methods and didn't
find anything. Only later @cmn showed me the [p for p in diff] example
in the documentation. Add a short piece of information that gives a hint
to those who prefer the dry API docs.
2015-04-23 16:38:55 +09:00
Colin Watson
efb49f8418 Keep path references in merge_file_from_index
IndexEntry._to_c requires its caller to hold a reference to the path it
returns until it no longer needs the C structure.
Repository.merge_file_from_index was not doing so, causing the merge
text to contain garbage from freed memory in some cases.
2015-04-23 04:29:13 +01:00
Richard Möhn
7a6465833b remote.py: Denote optional parameters as such
In the online documentation to Pygit2 it was not visible that (some of)
the parameters to Remote.fetch() and Remote.push() were optional. Fix
this. (I'm not sure if the way I did it is the idiomatic way of marking
a parameter optional in Python docstrings.)
2015-04-22 17:46:44 +09:00
Richard Möhn
b69a2f6197 Clarify docstring for Repository.create_commit
Change the argument "reference" to "reference_name", because "reference"
might lead to the assumption that one has to pass a pygit2.Reference.
2015-04-22 17:25:43 +09:00
Richard Möhn
b3025e3fe1 Add git-cherry-pick recipes
Add the way that worked for me. Not sure if it is idiomatic. When doing
the convenience-mode cherry-pick, the repo remains in cherry-picking
mode afterwards. I've already added an issue for this.
2015-04-22 17:14:45 +09:00
Patrick Steinhardt
f923e20f2d submodule: reimplement with Python CFFI.
The submodule type has been implemented as a C type. When opening
a submodule's repository this leads to the bug that instead of an
actual pygit2.Repository being instantiated we only create an
object of the C Repository type.

As this is not trivially fixed within the C code, reimplement the
submodule type as a Python interface with CFFI. As submodules
provide no functionality that is usually accessed repeatedly the
code paths should not prove performance critical. In addition,
maintainability is improved by this reimplementation.
2015-04-16 11:36:41 +02:00
Santiago Perez De Rosso
cd7e2b21be make pygit2 work with pyinstaller 2015-04-03 11:35:54 -04:00
J. David Ibáñez
1f755c601c Fix indentation style 2015-04-03 09:19:29 +02:00
Santiago Perez De Rosso
08f2956e97 diff of blob to blob in repository.diff 2015-03-31 19:30:15 -04:00
Santiago Perez De Rosso
99e1cad393 bug fix in repository.diff 2015-03-31 13:49:39 -04:00
J. David Ibáñez
da98890bd1 Add DiffHunk.header
Commes from PR #346
2015-03-30 17:38:20 +02:00
Vladimir Rutsky
ca39a65054 fix typo: "Troobleshooting" 2015-03-30 14:19:58 +03:00
Patrick Steinhardt
cf56a695f9 Fix error when merging files with unicode content.
When merging index entries where the corresponding files contain Unicode
codepoints an error is thrown. Decode the C string using UTF-8 to fix the issue
and adjust the test case for merging files to contain umlauts to catch such
errors.
2015-03-19 07:48:18 +01:00
J. David Ibáñez
8b05b296c2 New DiffDelta.status_char() 2015-03-16 19:12:20 +01:00
J. David Ibáñez
b2ffc8a8d5 New DiffFile members: size, flags and mode 2015-03-16 18:17:06 +01:00
J. David Ibáñez
e32df6a1c8 internal: add wrap_diff_hunk 2015-03-16 17:34:48 +01:00
J. David Ibáñez
fe849f659e Update changelog 2015-03-16 16:34:43 +01:00
Carlos Martín Nieto
c099655fc0 TreeEntry: compare ids when two entrie sort equally
The function we were using `git_tree_entry_cmp()` is only meant for
git-compatible sorting in a tree and thus does not take the id into
account. This is however important in order to keep value equality. In
order to avoid issues with assymetry, we compare the id any time when
two entries are equal according to their position in a tree.
2015-03-15 00:15:15 +01:00
J. David Ibáñez
f5485bb86f Merge remote-tracking branch 'pks/bare-conflicts' 2015-03-13 10:09:31 +01:00
J. David Ibáñez
318c6a8bee tests: fix warning, do not use deprecated assertEquals 2015-03-13 09:48:28 +01:00
Patrick Steinhardt
367084e3c1 Implement merging of index entries.
This allows us to generate a textual diff of conflicting files in
bare repositories by performing a merge on the index followed by
repo.merge_file_from_index on the resulting index entries.
2015-03-11 16:08:13 +01:00
J. David Ibáñez
50a70086bf Merge remote-tracking branch 'pks/submodules' 2015-03-09 19:06:44 +01:00
Patrick Steinhardt
9a4e002864 Fix build error with Python3 due to PyString_FromFormat. 2015-03-09 14:18:59 +01:00
Patrick Steinhardt
71ca619e26 Fully implement Submodule type in C. 2015-03-09 13:27:12 +01:00
Masud Rahman
38b1975991 blob_fromiobase: addresses review comments. 2015-03-08 00:09:20 -05:00
Kaarel Kitsemets
2d3f9d8e55 Added info about pkg-config being an optional requirement 2015-03-07 12:44:32 +02:00
Patrick Steinhardt
d35ecf945a Add tests for submodules. 2015-03-06 17:48:29 +01:00
Patrick Steinhardt
86c51eadbf Fix issues with submodules regarding refcounting. 2015-03-06 17:47:08 +01:00
Patrick Steinhardt
404645042b Add Submodule type. 2015-03-04 14:10:41 +01:00
kitsemets
ef67c36d8c docs/install.rst: added pkg-config as a dependency for building libgit2. Without pkg-config it is not possible to build libgit2 with ssh support. 2015-02-26 16:00:27 +02:00
J. David Ibáñez
bc668751a4 Fix type check in Diff_getitem
Fixes issue #495
2015-02-20 17:14:22 +01:00
J. David Ibáñez
adb351f7b3 Show at least git error code when no git error message is available
Should help resolving issue #494
2015-02-20 11:35:10 +01:00
J. David Ibáñez
fbb11775a3 Update changelog 2015-02-14 21:27:34 +01:00
J. David Ibáñez
c7609efc4b Merge remote-tracking branch 'rnicoll/cherrypick' 2015-02-14 20:27:17 +01:00
J. David Ibáñez
82d5214321 Merge remote-tracking branch 'rmoehn/master' 2015-02-13 16:30:54 +01:00
Ross Nicoll
c91fdf1d21
Add support for cherrypick() 2015-02-13 12:52:03 +00:00
Richard Möhn
1cb62ab578 git-show recipe: Add the easy Python 3 way
As @jdavid pointed out, Python 3 already provides a tzinfo subclass for
fixed UTC offsets. Incorporate this in the recipe. Leave the old code
with the self-made class, since many people are working with Python 2
and it is harder to find out there.
2015-02-13 13:46:41 +01:00
Richard Möhn
69f539851b Clarify comments in git-show recipe 2015-02-13 09:03:16 +01:00
J. David Ibáñez
3d896769d0 New DiffLine
Comes from PR #346

One difference, DiffLine.origin is a T_CHAR instead of T_OBJECT
2015-02-12 16:56:34 +01:00
J. David Ibáñez
7130df3a5e Get back iter(Blame)
Was lost in commit cd0842592
2015-02-12 13:02:04 +01:00
J. David Ibáñez
94be744ba6 docs: remove reference to LIBGIT2 in OS X notes
As disscussed in PR #448
2015-02-12 09:32:58 +01:00
J. David Ibáñez
40946cd795 Merge remote-tracking branch 'holgi/master' 2015-02-12 09:32:18 +01:00
Richard Möhn
2b2beb8094 Correct git-show recipe
Make the diff generation more idiomatic and fix the assembling of the
timestamp. git-show normally prints the author time, so use this instead
of the commit time. Also fix how tzinfo is obtained. Of course we have
to use the author's time zone and not some fixed one as I had written
before.
2015-02-12 09:08:24 +01:00
Richard Möhn
c87d28c9a8 Update git-show recipe
I couldn't get the diff as shown in the git-show recipe. Therefore
update it to what I think it should be. Maybe there is a better way.

Also add a section on how to assemble a git show-like message. It took
me quite some searching in the Python docs to find out how to do it,
especially the date and time part. So this might save people time. I
wanted to add something that gives me a git show --stat equivalent, but
couldn't figure it out.
2015-02-11 18:00:00 +01:00
J. David Ibáñez
d64dd15bd2 Merge remote-tracking branch 'carlos/merge-trees' 2015-02-10 09:34:49 +01:00
Masud Rahman
9cce003efe Implements 'Repository.create_blob_fromiobase'.
This commit allows blobs to be constructed from implementatons of the
'RawIOBase' ABC.  This allows any conformant stream implementation to be added
as a blob.

This is useful in the case where the contents of the blob would be too large to
create from memory (as 'create_blob_fromstring' allows) and avoids having to
write the data to disk (as 'create_blob_fromworkdir' allows).  The latter
operation is especially useful when reading data from a network socket, since
it avoids having to first commit all the data to disk.
2015-02-10 01:28:23 -05:00
Carlos Martín Nieto
d4da228c0e Add documentation for merge_commits() and merge_trees() 2015-02-09 21:25:33 +01:00
Carlos Martín Nieto
fcd4b9446b Introduce Repository.merge_trees()
This is the function which does the work for Repository.merge_commits()
and allows us more direct control over which trees we want to merge,
including trees which do not belong to commits.
2015-02-09 21:21:28 +01:00
J. David Ibáñez
fa380c0adb Merge remote-tracking branch 'gandalf/archive_enh'
Conflicts:
	pygit2/repository.py
2015-02-07 20:35:45 +01:00
J. David Ibáñez
718a2df1d5 Fix building the docs 2015-02-07 08:37:41 +01:00
J. David Ibáñez
30e57e13e3 Merge remote-tracking branch 'carlos/ahead-behind' 2015-02-07 08:27:26 +01:00
Carlos Martín Nieto
0ba17a5b46 Safer handling of string arrays
We need to keep hold of the strings which we create. We must also hold
on to the array of strings which we assing to our git_strarray.

We were not doing the latter, which meant that our strings may have been
freed too early, leaving us with with memory access errors (though often
not leading to a crash due to the custom allocator in python).

As we need to keep hold of two/three pieces of information, this looks
like a good place to introduce a context manager. This allows us to
keep these pointers alive without burdening the call sites with a return
of multiple objects they have no use for.
2015-02-06 03:51:05 +01:00
Carlos Martín Nieto
4709cae1a1 Add Repository.ahead_behind()
This lets us ask how many diverging commits each side of two histories
have.
2015-02-06 01:26:26 +01:00
Carlos Martín Nieto
1361b2cce9 Add Repository.expand_id()
As we allow users of the library to use short strings, we must expand
them. This is however only available through a function in C. Expose
that function from the repository to allow python code to get a full id
from a short hex string.
2015-02-06 01:17:19 +01:00
J. David Ibáñez
7f21f6eb63 Expose the pygit2.GIT_REPOSITORY_INIT_* constants
Fixes #483
2015-02-01 10:53:19 +01:00
Alok Singhal
f5a5dfc18a Use "prefix" instead of "root_path" in write_archive 2015-01-30 10:15:08 -08:00
J. David Ibáñez
961d007b02 Patch.line_stats replaces .additions and .deletions
Comes from PR #346
2015-01-30 17:56:46 +01:00
J. David Ibáñez
909e03d8fc New DiffDelta and DiffFile
Comes from PR #346
2015-01-29 18:34:47 +01:00
Alok Singhal
3ee1c798b2 Add an option to specify root path when creating archives 2015-01-29 08:46:50 -08:00
Alok Singhal
9771adf862 Fix handling of symlinks in write_archive(). 2015-01-29 08:35:27 -08:00
J. David Ibáñez
8881b75aaa internal: split patch.c from diff.c
Comes from PR #346
2015-01-26 19:02:28 +01:00
J. David Ibáñez
e7fdaf2510 Fix link to travis image (use master) 2015-01-23 23:19:13 +01:00
J. David Ibáñez
061961f119 Rename pygit2.Hunk to pygit2.DiffHunk
Comes from PR #346
2015-01-23 13:38:13 +01:00
J. David Ibáñez
c62a79cf81 Start changelog for future 0.22.1 release 2015-01-23 13:37:27 +01:00
J. David Ibáñez
8a6e61551c Merge remote-tracking branch 'Sheeo/master' 2015-01-19 13:56:32 +01:00
J. David Ibáñez
126308403b Get ready to release v0.22.0 2015-01-16 16:35:19 +01:00
J. David Ibáñez
9da91e554d Changelog for upcomming v0.22.0 release 2015-01-16 13:52:15 +01:00
J. David Ibáñez
beaaca7f63 Fix type of RefLogEntry.oid_old and RefLogEntry.oid_new
This was left from PR #449
2015-01-16 12:39:16 +01:00
J. David Ibáñez
b538163536 Merge remote-tracking branch 'carlos/development'
Conflicts:
	pygit2/remote.py
2015-01-16 11:16:02 +01:00
J. David Ibáñez
9c9b925da8 Revert "Mentioning libssh2 in remote's pydoc"
This reverts commit e807ad43d725da06bfc34187b387270baea74887.
2015-01-16 10:24:06 +01:00
Carlos Martín Nieto
d0b00e3124 Add support for libgit2 feature detection
This lets us ask the library whether it supports threading, https and
ssh.
2015-01-14 20:47:47 +01:00
Greg Fitzgerald
81bde5d0e7 Fix compiler warnings 2015-01-14 14:25:41 +00:00
Peter Dave Hello
e15c0d828b Use svg instead of png to get better image quality 2015-01-14 02:15:20 +08:00
Matthew Duggan
b2abfdec9e Note that Refspec constructor is internal. Fix typo. 2015-01-12 18:23:03 -08:00
Matthew Duggan
34fb1c00eb Make it explicit that respecs are added as strings. 2015-01-12 18:22:25 -08:00
Matthew Duggan
52ac41a362 Make it explicit that Refspec objects are not for construction 2015-01-12 18:20:11 -08:00
Matthew Duggan
5a06cd2688 Make it explicit what to do when no passphrase is needed 2015-01-12 18:20:07 -08:00
Carlos Martín Nieto
78695aa93a Change required version to 0.22 2015-01-12 20:00:17 +01:00
Carlos Martín Nieto
66d55aee7e Add certificate callback for clone
We do not pass anything as the certificate, as there doesn't seem to be
anything sensible for checking it.
2015-01-12 18:39:21 +01:00
Carlos Martín Nieto
f68b266e60 Remote: generalize push()
Move to use git_remote_push() instead of doing the steps ourselves. We
also change to accept a list of refspecs instead of just the one refspec
for the push method.

As part of this, we no longer error out if the server rejected any
updates, as this is a different concern from whether the push itself
failed or not. We do still error out if we attempt to push non-ff
updates.
2015-01-12 18:39:21 +01:00
Carlos Martín Nieto
1dbf94011a Migrate to 0.22
Apart from the usual API changes, we now need to introduce the concept
of whether we still own the C object underneath our Repository and
Remote objects.

When using the custom callbacks for repository and remote creation
during clone, we pass the pointer and thus ownership of the object back
to the library. We will then get the repository back at the end.

We return the object which was handed to us rather than opening the
repository again with the local path as there is now a much higher
chance that the cloned repository does not use the standard backends.
2015-01-12 18:39:21 +01:00
Lukas Fleischer
4cbfade973 Fix data type of options in init_repository()
Initializers for the char * fields of the git_repository_init_options
structure must be cdata pointers.

Signed-off-by: Lukas Fleischer <info@cryptocrack.de>
2015-01-10 21:30:26 +01:00
J. David Ibáñez
e81d395adf Merge remote-tracking branch 'rmoehn/master' 2015-01-10 21:24:17 +01:00
Richard Möhn
d341cff7d6 Fix documentation for Repository.listall_branches
Add a description of the possible flags and turn the "tuple" into a
"list", as it had already happened in code and part of the documentation
in aa5877e0.
2015-01-09 19:25:50 +01:00
Greg Fitzgerald
4f88840e93 Fix use-after-free when patch outlives diff 2015-01-05 19:20:47 +00:00
Richard Möhn
22d1aef50d Remove obsolete git-branch recipe
The git-branch recipe says: »Note that the next release will probably
allow repo.listall_branches().« Concluding from the README,
Repository.listall_branches() was included in some release prior to
0.20.0, so at least that statement is obsolete.

However, since pygit2.org brings up fairly accurate results for a search
on »list all branches«, I figured that the whole recipe isn't needed
anymore. Therefore delete it.
2015-01-05 10:27:43 +01:00
J. David Ibáñez
ceb40ecc52 Merge remote-tracking branch 'kinfoo/libssh2_docs' 2014-12-30 22:58:48 +01:00
Kevin KIN-FOO
e807ad43d7 Mentioning libssh2 in remote's pydoc 2014-12-30 16:34:15 +01:00
Kevin KIN-FOO
aff3a64e2d Mention libssh2 in installation#requirements
Fixes #456
2014-12-30 16:08:07 +01:00
Michael Sondergaard
df0e11726e Make pygit work in a frozen environment 2014-11-26 06:04:40 +01:00
J. David Ibáñez
beff871923 Minor styling 2014-11-12 10:21:47 +01:00
J. David Ibáñez
93be1f1910 Merge remote-tracking branch 'carlos/remote-collection' 2014-11-11 20:08:56 +01:00
Carlos Martín Nieto
b80103b017 Introduce RemoteCollection
This lets us look up remotes by name, which is not possible by just
returning the list of remotes.

Move remote creation to Repostiory.remotes.create() and keep the old
Repository.create_remote() for compatibility, delegating to this new
way.

Existing code should keep working, but this moves us towards what we'd
need for a better interface in 0.22 which makes remote renaming and
deleting work with a name rather than an instance and would make sense
to exist as part of an Remote.remotes object.
2014-11-11 19:57:22 +01:00
J. David Ibáñez
b6f0bb0800 setup: minor cleanup 2014-11-11 13:03:25 +01:00
Philippe Ombredanne
6484ef1e37 Make the GPL exception explicit in setup.py
I think that is best to have that explicitly set there (and it should possibly be updated in Pypi too).
Thanks for this lib!
Cordially
Philippe
2014-11-11 12:05:08 +01:00
Carlos Martín Nieto
11eea2d574 Get rid of allocfmt()
It is not possible to know how we can free the results of this
allocation, so we shouldn't be using this function.

We have a convention of returning Oid objects in pygit2, so let's keep
to that in these places.
2014-11-09 12:10:45 +01:00
Holger Frey
d4fc7010b3 Added installing on OS X 2014-11-09 10:39:07 +01:00
J. David Ibáñez
21e2102e7c Get ready for 0.21.4 release 2014-11-04 17:49:43 +01:00
J. David Ibáñez
316d5af2d1 Write changelog for upcoming 0.21.4 release 2014-11-03 18:58:15 +01:00
J. David Ibáñez
cb310316bf docs: fix build errors in the blame chapter 2014-11-03 18:53:38 +01:00
Carlos Martín Nieto
ab52904c5d Merge branch 'push-callback' 2014-11-03 13:27:53 +01:00
chengyuhang
45be961d60 set remote callback before push 2014-11-03 13:27:33 +01:00
J. David Ibáñez
1f0466fe49 Merge remote-tracking branch 'carlos/merge-commits' 2014-10-31 10:28:08 +01:00
J. David Ibáñez
8e933c8019 issue#441: change modulename to include hash of source
This should make it work both for users and developers.
2014-10-30 19:41:25 +01:00
J. David Ibáñez
94f650a41d Fix (again) the chicken-and-egg problem with cffi
Broken with previous commits :)
2014-10-30 17:59:38 +01:00
J. David Ibáñez
a9fa063de3 setup: remove old 'include' from include_dirs 2014-10-30 17:19:11 +01:00
Carlos Martín Nieto
bc8b29b4f3 Repository: allow passing a favor option to merge_commits
I've gone with taking a string and converting it because the depth of
the namespacing in the libgit2 name is rather large and we don't care
about the global namespace that C has; and this also lets us pass the
same as the '-X' option to git-merge.
2014-10-30 16:21:22 +01:00
Carlos Martín Nieto
3b27e16d08 Add Repository.merge_commits()
This allows you to merge arbitrary commits, returning an index, which is
useful when dealing with bare repos in which we want to merge.
2014-10-30 15:04:51 +01:00
J. David Ibáñez
7653d12c72 docs: review install section 2014-10-30 13:45:54 +01:00
J. David Ibáñez
01067cb77f Make cffi extension name to be constant
This should fix issue #441
2014-10-29 12:33:02 +01:00
J. David Ibáñez
93dd545069 Fix import error introduced in previous commit 2014-10-29 12:05:19 +01:00
J. David Ibáñez
e325c51203 Refactor code to get the path to libgit2 2014-10-28 16:31:53 +01:00
J. David Ibáñez
6bb2b369fa Merge remote-tracking branch 'wking/instaleld-typo' 2014-10-28 09:25:42 +01:00
J. David Ibáñez
7daa95a3a5 Merge remote-tracking branch 'wking/install-filing-typo' 2014-10-28 09:23:52 +01:00
W. Trevor King
6677de82c2 docs/install: Fix 'instaleld' -> 'installed' typo 2014-10-27 21:36:06 -07:00
W. Trevor King
8ca75e2744 docs/install: Fix 'filling un' -> 'filing a' typo 2014-10-27 21:31:55 -07:00
W. Trevor King
05c570c3fc docs/working-copy: Replace 'del index[path]' with index.remove(path)
Catch up with b12a5960 (Remove "del index[xxx]" from the API,
2013-05-02).
2014-10-27 21:03:37 -07:00
J. David Ibáñez
ecba70198f Test against libgit2 v0.21.2 2014-10-27 18:16:05 +01:00
J. David Ibáñez
54720c4f01 Merge remote-tracking branch 'wking/default-signature-attribute' 2014-10-27 13:20:10 +01:00
W. Trevor King
120fdedb5a docs/merge: default_signature is an attribute, not a method
Avoid:

  >>> repo.default_signature()
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: '_pygit2.Signature' object is not callable
2014-10-26 16:36:34 -07:00
W. Trevor King
149bb1e9e2 index: Add missing 'tree' entry to diff_to_tree docstring argspec
It's been missing since the original argspec was added with 5ed9eb4
(Add documentation for conflicts and fixup Index, 2014-07-10).
2014-10-26 15:37:15 -07:00
Carlos Martín Nieto
b8efdde626 Merge pull request #434 from carlosmn/diff-refactor
Repository: make use of peel for diff()
2014-10-10 11:57:18 +02:00
Carlos Martín Nieto
5d4c955d25 Reference: deprecate get_object() in favour of peel()
Let Reference.peel()'s argument be optional, and default to GIT_OBJ_ANY,
which mirrors the behaviour of get_object().

Since we reimplement the get_object() behaviour, change it to call
peel() which makes sure we can keep using the old tests for this aspect.
2014-10-10 11:46:02 +02:00
Carlos Martín Nieto
fa20589fe9 Merge pull request #424 from kyriakosoikonomakos/ssh-agent
authenticate using ssh agent
2014-10-10 11:44:05 +02:00
Carlos Martín Nieto
59da03476e Repository: make use of peel for diff()
Instead of trying to reimplement parts of it, make use of Object.peel()
and Reference.peel() to get to a Blob or Tree.
2014-10-07 20:27:01 +02:00
Carlos Martín Nieto
b98e9e85de Reference: implement peel()
This makes both objects and references peelable via the same interface,
simplifying how to get to the wanted type.
2014-10-07 20:27:01 +02:00
J. David Ibáñez
510f6174f1 Fix Repository.diff for references an empty trees
This should fix issue #432

By the way make the code a little more robust.
2014-10-07 18:59:04 +02:00
Ash Berlin
9aa39aafbc Use ssh_agent when pub+priv key are None, not based on the class 2014-10-07 09:48:47 +01:00
J. David Ibáñez
6831983a26 docs: now "pip install pygit2" installs cffi first
This was fixed in PR#407
2014-09-24 09:33:25 +02:00
Soasme
8bb263559d Fix typo pìp to pip 2014-09-24 09:53:39 +08:00
Alexander Bayandin
548ba1ab84 Fix typo 2014-09-16 18:37:45 +04:00
Kyriakos Oikonomakos
3e87adaccd authenticate using ssh agent 2014-09-15 18:14:20 +01:00
J. David Ibáñez
c997037c7b Get ready for 0.21.3 release 2014-09-15 12:39:27 +02:00
Carlos Martín Nieto
5bc6a98004 Merge pull request #426 from carlosmn/checkout-init-options
Properly initialize the clone options
2014-09-13 23:27:38 +02:00
Carlos Martín Nieto
7d34d2bb27 Use the initializer for repository init options 2014-09-13 18:58:36 +02:00
Carlos Martín Nieto
63377aad78 Properly initialize the clone options
libgit2 provides an initialization function to set sane defaults. Use
that instead of setting the version by hand, as that's not the only
thing it does.

Using C.git_clone_init_options() sets the checkout strategy to SAFE,
which will checkout the files after the clone, instead of the implicit
NONE which we're setting by hand.

This fixes #425,
2014-09-13 18:58:36 +02:00
J. David Ibáñez
51da3b767d Merge remote-tracking branch 'carlos/archive' 2014-09-08 10:03:57 +02:00
Carlos Martín Nieto
81104d4df2 pypy3 does not have the AttributeError/TypeError difference 2014-09-06 19:38:16 +02:00
Carlos Martín Nieto
ab730cb1d4 Provide a method to write a tree to an archive
Add Repository.write_archive() to write a given tree to an archive. As
there are many customisation options, we only provide a method to write
to an archive which is created by the user.
2014-09-06 18:59:15 +02:00
Carlos Martín Nieto
ae00fe4522 Add pypy3 to Travis 2014-09-06 18:37:08 +02:00
J. David Ibáñez
82167827bc Merge remote-tracking branch 'carlos/reference-update' 2014-09-05 17:11:32 +02:00
Carlos Martín Nieto
a3e7a115f4 Provide method for deleting a remote
Fixes #418
2014-09-04 15:57:05 +02:00
J. David Ibáñez
5d8c108fe7 Merge remote-tracking branch 'carlos/clean-handle' 2014-09-04 09:25:36 +02:00
J. David Ibáñez
7c350bdd7c Merge remote-tracking branch 'carlos/merge-checkout-safe' 2014-09-04 09:21:39 +02:00
Carlos Martín Nieto
747e7c2136 Repository: make head read-only and introduce set_head()
Following from the previous commits, make 'head' read-only and provide a
method to update head while providing a message.

The checkout() codepath which switches branches has been updated to
provide a reflog entry which mimics git's.
2014-09-04 01:58:05 +02:00
Carlos Martín Nieto
70256d1a00 remote: clear self handle only on error
When setting the callbacks fails, we want to clear self._self_handle so
we don't leak a pointer to ourselves.

The current code used a 'finally' clause which clears it
unconditionally, which means that by the time the fetch starts, we have
no guarantee that the handle will be valid.

Replace that with an except and re-raise to make sure we only clear it
here if there was an error.
2014-09-03 19:06:34 +02:00
Carlos Martín Nieto
718c7f790c merge: set the checkout strategy to SAFE_CREATE
The expectation is set with Repository.checkout()'s default strategy and
the documentation text that we will write the merged content of the
files to the workdir. We however fail to override the default checkout
strategy when calling git_merge() which results in a DRY_RUN strategy
and no files being changed.

Set the checkout strategy to SAFE_CREATE to match expectations and text.
2014-09-03 15:53:21 +02:00
Carlos Martín Nieto
cd08425927 Move blame to cffi
This requires fairly little work on the pygit2 side to kick off all the
searching on the libgit2 side, so it's a fairly good candidate.

This changes the return value for the commit ids to Oid instead of
strings, which is what we generally try to return.
2014-08-31 22:35:32 +02:00
Carlos Martín Nieto
70410349ff Update reference documentation 2014-08-28 01:25:21 +02:00
Carlos Martín Nieto
4d053cf066 Get rid of a settable Reference.target
Updating the target of a reference is not like setting a property. It
involves appending to the reflog under the same lock, so we must do it
all the same time. Thus setting the target becomes a function which
takes the new target and what the use would like to add to the reflog.
2014-08-28 01:25:21 +02:00
Carlos Martín Nieto
f35f58ca45 Get rid of Reference.append_log()
We do not want this as it is riddled with race conditions.
2014-08-28 01:25:21 +02:00
Carlos Martín Nieto
ae495444a6 Implement Reference.target= as a method
This new method is Reference.set_target() which in addition lets you
specify what to use in the reflog.
2014-08-28 01:25:21 +02:00
J. David Ibáñez
a53d8b2213 Merge remote-tracking branch 'djmattyg007/doc_fixes' 2014-08-26 10:31:53 +02:00
J. David Ibáñez
fe5c9d68c3 Merge remote-tracking branch 'djmattyg007/branches_are_references' 2014-08-26 10:15:33 +02:00
J. David Ibáñez
617dd34f5e Merge remote-tracking branch 'carlos/checkout-branch' 2014-08-26 10:09:32 +02:00
J. David Ibáñez
dad9bc3612 Merge remote-tracking branch 'mduggan/remote-refcount-fix' 2014-08-26 10:02:07 +02:00
Matthew Duggan
2f2d4005c7 Ensure self handle stays alive - keeping it in callbacks is not enough. 2014-08-25 22:39:16 +09:00
Carlos Martín Nieto
b550027234 Fix cloning a repository with a particular branch to checkout
We were missing a cast to bytes. Add a test for this option as well and
remove the old commented-out test for checkout_branch, which is
superseded by this one and whose last bit seemed confused about what the
option means.

This fixes #399
2014-08-25 13:58:41 +02:00
djmattyg007
14bcce0dcf Add test for checking out local branches with a Branch object.
This differs from the only other test which uses a Reference object.
This test only succeeds because of the previous commit.
2014-08-23 11:52:58 +10:00
djmattyg007
21efebd305 Clarify return types from Repository.{create,lookup}_branch() 2014-08-23 11:29:27 +10:00
djmattyg007
629eea01e9 Allow objects of type Branch to be passed to Repository.checkout()
Branch inherits from Reference, so there's no reason why we shouldn't be
able to pass a Branch object.
2014-08-23 11:23:17 +10:00
djmattyg007
4a53b2c8ae Add note about including remote name when looking up remote branch 2014-08-23 11:15:49 +10:00
djmattyg007
e4315aa0a5 Add note about default behaviour of Repository.listall_branches() 2014-08-23 10:56:48 +10:00
djmattyg007
9718fd8c32 Add note about checkout('HEAD') being identical to checkout_head() 2014-08-23 01:08:32 +10:00
djmattyg007
2bd73f3bdf Clear up confusion regarding accepted types for Repository.checkout() refname parameter 2014-08-23 01:06:30 +10:00
J. David Ibáñez
82b342937f Merge remote-tracking branch 'opebeat/cffi-install-fix' 2014-08-21 19:31:10 +02:00
Ron Cohen
acca2726df BuildWithDLLs now inherits from CFFIBuild to *theoretically* fix windows. 2014-08-21 18:42:27 +02:00
Ron Cohen
a459712fde Forgot the setup_requires argument. 2014-08-21 16:00:40 +02:00
Ron Cohen
4056ebf53b Fixed the chicked-and-egg problem with CFFI in setup.
This still needs some work in order to fix the problem
under windows. I have no windows machine here to test it.
2014-08-21 15:48:25 +02:00
J. David Ibáñez
99f295e2e8 docs: fix reST errors 2014-08-20 17:00:33 +02:00
J. David Ibáñez
f0631868af Merge remote-tracking branch 'djmattyg007/doc_updates' 2014-08-20 16:34:01 +02:00
Matthew Duggan
0813ec7923 Fix use of skipIf. 2014-08-18 21:17:36 +09:00
Matthew Duggan
bedd9ee315 Don't test refcounts in pypy as they don't make sense there 2014-08-18 21:13:03 +09:00
Matthew Duggan
99433ca66a Hard-code callback version because we'll need to change the defs if it changes. 2014-08-18 21:12:34 +09:00
Matthew Duggan
9ce6a26db3 Add some tests for refcounts to check for leaks 2014-08-18 18:23:10 +09:00
Matthew Duggan
794dbe7b9c set callbacks just before fetch, clear them after (fixes #403). 2014-08-18 18:22:45 +09:00
Mathieu Bridon
6c4e1d093b Fix syntax error 2014-08-12 11:25:17 +02:00
djmattyg007
d752e8550b Further clarified purpose of user field in docstring for Keypair class 2014-08-12 08:43:05 +10:00
djmattyg007
f787a52e66 Document list of parameters for the Keypair credential callback.
Includes a couple of small grammar fixes to other docstrings.
2014-08-10 12:39:27 +10:00
djmattyg007
f98dc8264a Dynamically get version component of build directory for building sphinx docs
This almost certainly isn't perfect, but it's a big step up and should
work on (at least) any 32-bit or 64-bit version of python2 or python3
running on Linux (rather than just 64-bit python2.7 on Linux).
2014-08-10 12:39:27 +10:00
Brodie Rao
78d134c016 repository: add listall_reference_objects() method
This allows for efficient reading of many references and their targets,
without incurring the overhead of lookup_reference() (which stats for
a loose ref and then reads packed-refs) which can be expensive on NFS
with thousands of refs.
2014-01-08 15:14:27 -08:00
119 changed files with 7141 additions and 2602 deletions

1
.gitattributes vendored Normal file

@ -0,0 +1 @@
*.h text eol=lf

1
.gitignore vendored

@ -3,6 +3,7 @@ build
dist
pygit2.so
_pygit2.so
.tox/
test/*.pyc
test/__pycache__
pygit2/*.pyc

@ -14,3 +14,15 @@ Xu Tao <xutao881001@gmail.com>
<petrhosek@gmail.com> <p.hosek@imperial.ac.uk>
Vlad Temian <vladtemian@gmail.com>
Matthew Gamble <git@matthewgamble.net>
Kaarel Kitsemets <kitsemets@gmail.com>
Matthias Bartelmeß <mba@fourplusone.de>
Robert Hölzl <robert.hoelzl@posteo.de>
Anatoly Techtonik <techtonik@gmail.com>
Guillermo Pérez <bisho@fb.com> <bisho@freedreams.org>
Matthew Duggan <mduggan@qti.qualcomm.com> <mgithub@guarana.org>
Alexander Bayandin <a.bayandin@gmail.com> <bayandin@users.noreply.github.com>

@ -2,7 +2,7 @@
cd ~
git clone --depth=1 -b v0.21.1 https://github.com/libgit2/libgit2.git
git clone --depth=1 -b maint/v0.26 https://github.com/libgit2/libgit2.git
cd libgit2/
mkdir build && cd build

@ -2,10 +2,12 @@ language: python
python:
- "2.7"
- "3.2"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
- "pypy"
# - "pypy3"
env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib

859
CHANGELOG.rst Normal file

@ -0,0 +1,859 @@
0.26.0 (2017-07-06)
-------------------------
- Update to libgit2 v0.26
`#713 <https://github.com/libgit2/pygit2/pull/713>`_
- Drop support for Python 3.2, add support for cffi 1.10
`#706 <https://github.com/libgit2/pygit2/pull/706>`_
`#694 <https://github.com/libgit2/pygit2/issues/694>`_
- New ``Repository.revert_commit(...)``
`#711 <https://github.com/libgit2/pygit2/pull/711>`_
`#710 <https://github.com/libgit2/pygit2/issues/710>`_
- New ``Branch.is_checked_out()``
`#696 <https://github.com/libgit2/pygit2/pull/696>`_
- Various fixes
`#706 <https://github.com/libgit2/pygit2/pull/706>`_
`#707 <https://github.com/libgit2/pygit2/pull/707>`_
`#708 <https://github.com/libgit2/pygit2/pull/708>`_
0.25.1 (2017-04-25)
-------------------------
- Add suport for Python 3.6
- New support for stash: repository methods ``stash``, ``stash_apply``,
``stash_drop`` and ``stash_pop``
`#695 <https://github.com/libgit2/pygit2/pull/695>`_
- Improved support for submodules: new repository methods ``init_submodules``
and ``update_submodules``
`#692 <https://github.com/libgit2/pygit2/pull/692>`_
- New friendlier API for branches & references: ``Repository.branches`` and
``Repository.references``
`#700 <https://github.com/libgit2/pygit2/pull/700>`_
`#701 <https://github.com/libgit2/pygit2/pull/701>`_
- New support for custom backends
`#690 <https://github.com/libgit2/pygit2/pull/690>`_
- Fix ``init_repository`` crash on None input
`#688 <https://github.com/libgit2/pygit2/issues/688>`_
`#697 <https://github.com/libgit2/pygit2/pull/697>`_
- Fix checkout with an orphan master branch
`#669 <https://github.com/libgit2/pygit2/issues/669>`_
`#685 <https://github.com/libgit2/pygit2/pull/685>`_
- Better error messages for opening repositories
`#645 <https://github.com/libgit2/pygit2/issues/645>`_
`#698 <https://github.com/libgit2/pygit2/pull/698>`_
0.25.0 (2016-12-26)
-------------------------
- Upgrade to libgit2 0.25
`#670 <https://github.com/libgit2/pygit2/pull/670>`_
- Now Commit.tree raises an error if tree is not found
`#682 <https://github.com/libgit2/pygit2/pull/682>`_
- New settings.mwindow_mapped_limit, cached_memory, enable_caching,
cache_max_size and cache_object_limit
`#677 <https://github.com/libgit2/pygit2/pull/677>`_
0.24.2 (2016-11-01)
-------------------------
- Unit tests pass on Windows, integration with AppVeyor
`#641 <https://github.com/libgit2/pygit2/pull/641>`_
`#655 <https://github.com/libgit2/pygit2/issues/655>`_
`#657 <https://github.com/libgit2/pygit2/pull/657>`_
`#659 <https://github.com/libgit2/pygit2/pull/659>`_
`#660 <https://github.com/libgit2/pygit2/pull/660>`_
`#661 <https://github.com/libgit2/pygit2/pull/661>`_
`#667 <https://github.com/libgit2/pygit2/pull/667>`_
- Fix when libgit2 error messages have non-ascii chars
`#651 <https://github.com/libgit2/pygit2/pull/651>`_
- Documentation improvements
`#643 <https://github.com/libgit2/pygit2/pull/643>`_
`#653 <https://github.com/libgit2/pygit2/pull/653>`_
`#663 <https://github.com/libgit2/pygit2/pull/663>`_
0.24.1 (2016-06-21)
-------------------------
- New ``Repository.listall_reference_objects()``
`#634 <https://github.com/libgit2/pygit2/pull/634>`_
- Fix ``Repository.write_archive(...)``
`#619 <https://github.com/libgit2/pygit2/pull/619>`_
`#621 <https://github.com/libgit2/pygit2/pull/621>`_
- Reproducible builds
`#636 <https://github.com/libgit2/pygit2/pull/636>`_
- Documentation fixes
`#606 <https://github.com/libgit2/pygit2/pull/606>`_
`#607 <https://github.com/libgit2/pygit2/pull/607>`_
`#609 <https://github.com/libgit2/pygit2/pull/609>`_
`#623 <https://github.com/libgit2/pygit2/pull/623>`_
- Test updates
`#629 <https://github.com/libgit2/pygit2/pull/629>`_
0.24.0 (2016-03-05)
-------------------------
- Update to libgit2 v0.24
`#594 <https://github.com/libgit2/pygit2/pull/594>`_
- Support Python 3.5
- New dependency, `six <https://pypi.python.org/pypi/six/>`_
- New ``Repository.path_is_ignored(path)``
`#589 <https://github.com/libgit2/pygit2/pull/589>`_
- Fix error in ``Repository(path)`` when path is a bytes string
`#588 <https://github.com/libgit2/pygit2/issues/588>`_
`#593 <https://github.com/libgit2/pygit2/pull/593>`_
- Fix memory issue in ``Repository.describe(...)``
`#592 <https://github.com/libgit2/pygit2/issues/592>`_
`#597 <https://github.com/libgit2/pygit2/issues/597>`_
`#599 <https://github.com/libgit2/pygit2/pull/599>`_
- Allow testing with `tox <https://pypi.python.org/pypi/tox/>`_
`#600 <https://github.com/libgit2/pygit2/pull/600>`_
0.23.3 (2016-01-01)
-------------------------
- New ``Repository.create_blob_fromiobase(...)``
`#490 <https://github.com/libgit2/pygit2/pull/490>`_
`#577 <https://github.com/libgit2/pygit2/pull/577>`_
- New ``Repository.describe(...)``
`#585 <https://github.com/libgit2/pygit2/pull/585>`_
- Fix ``Signature`` default encoding, UTF-8 now
`#581 <https://github.com/libgit2/pygit2/issues/581>`_
- Fixing ``pip install pygit2``, should install cffi first
- Unit tests, fix binary diff test
`#586 <https://github.com/libgit2/pygit2/pull/586>`_
- Document that ``Diff.patch`` can be ``None``
`#587 <https://github.com/libgit2/pygit2/pull/587>`_
0.23.2 (2015-10-11)
-------------------------
- Unify callbacks system for remotes and clone
`#568 <https://github.com/libgit2/pygit2/pull/568>`_
- New ``TreeEntry._name``
`#570 <https://github.com/libgit2/pygit2/pull/570>`_
- Fix segfault in ``Tag._message``
`#572 <https://github.com/libgit2/pygit2/pull/572>`_
- Documentation improvements
`#569 <https://github.com/libgit2/pygit2/pull/569>`_
`#574 <https://github.com/libgit2/pygit2/pull/574>`_
API changes to clone::
# Before
clone_repository(..., credentials, certificate)
# Now
callbacks = RemoteCallbacks(credentials, certificate)
clone_repository(..., callbacks)
API changes to remote::
# Before
def transfer_progress(stats):
...
remote.credentials = credentials
remote.transfer_progress = transfer_progress
remote.fetch()
remote.push(specs)
# Now
class MyCallbacks(RemoteCallbacks):
def transfer_progress(self, stats):
...
callbacks = MyCallbacks(credentials)
remote.fetch(callbacks=callbacks)
remote.push(specs, callbacks=callbacks)
0.23.1 (2015-09-26)
-------------------------
- Improve support for cffi 1.0+
`#529 <https://github.com/libgit2/pygit2/pull/529>`_
`#561 <https://github.com/libgit2/pygit2/pull/561>`_
- Fix ``Remote.push``
`#557 <https://github.com/libgit2/pygit2/pull/557>`_
- New ``TreeEntry.type``
`#560 <https://github.com/libgit2/pygit2/pull/560>`_
- New ``pygit2.GIT_DIFF_SHOW_BINARY``
`#566 <https://github.com/libgit2/pygit2/pull/566>`_
0.23.0 (2015-08-14)
-------------------------
- Update to libgit2 v0.23
`#540 <https://github.com/libgit2/pygit2/pull/540>`_
- Now ``Repository.merge_base(...)`` returns ``None`` if no merge base is found
`#550 <https://github.com/libgit2/pygit2/pull/550>`_
- Documentation updates
`#547 <https://github.com/libgit2/pygit2/pull/547>`_
API changes:
- How to set identity (aka signature) in a reflog has changed::
# Before
signature = Signature('foo', 'bar')
...
reference.set_target(target, signature=signature, message=message)
repo.set_head(target, signature=signature)
remote.fetch(signature=signature)
remote.push(signature=signature)
# Now
repo.set_ident('foo', 'bar')
...
reference.set_target(target, message=message)
repo.set_head(target)
remote.push()
# The current identity can be get with
repo.ident
- Some remote setters have been replaced by methods::
# Before # Now
Remote.url = url Repository.remotes.set_url(name, url)
Remote.push_url = url Repository.remotes.set_push_url(name, url)
Remote.add_fetch(refspec) Repository.remotes.add_fetch(name, refspec)
Remote.add_push(refspec) Repository.remotes.add_push(name, refspec)
Remote.fetch_refspecs = [...] removed, use the config API instead
Remote.push_refspecs = [...] removed, use the config API instead
0.22.1 (2015-07-12)
-------------------------
Diff interface refactoring
`#346 <https://github.com/libgit2/pygit2/pull/346>`_
(in progress):
- New ``iter(pygit2.Blame)``
- New ``pygit2.DiffDelta``, ``pygit2.DiffFile`` and ``pygit.DiffLine``
- API changes, translation table::
Hunk => DiffHunk
Patch.old_file_path => Patch.delta.old_file.path
Patch.new_file_path => Patch.delta.new_file.path
Patch.old_id => Patch.delta.old_file.id
Patch.new_id => Patch.delta.new_file.id
Patch.status => Patch.delta.status
Patch.similarity => Patch.delta.similarity
Patch.is_binary => Patch.delta.is_binary
Patch.additions => Patch.line_stats[1]
Patch.deletions => Patch.line_stats[2]
- ``DiffHunk.lines`` is now a list of ``DiffLine`` objects, not tuples
New features:
- New ``Repository.expand_id(...)`` and ``Repository.ahead_behind(...)``
`#448 <https://github.com/libgit2/pygit2/pull/448>`_
- New ``prefix`` parameter in ``Repository.write_archive``
`#481 <https://github.com/libgit2/pygit2/pull/481>`_
- New ``Repository.merge_trees(...)``
`#489 <https://github.com/libgit2/pygit2/pull/489>`_
- New ``Repository.cherrypick(...)``
`#436 <https://github.com/libgit2/pygit2/issues/436>`_
`#492 <https://github.com/libgit2/pygit2/pull/492>`_
- New support for submodules
`#499 <https://github.com/libgit2/pygit2/pull/499>`_
`#514 <https://github.com/libgit2/pygit2/pull/514>`_
- New ``Repository.merge_file_from_index(...)``
`#503 <https://github.com/libgit2/pygit2/pull/503>`_
- Now ``Repository.diff`` supports diffing two blobs
`#508 <https://github.com/libgit2/pygit2/pull/508>`_
- New optional ``fetch`` parameter in ``Remote.create``
`#526 <https://github.com/libgit2/pygit2/pull/526>`_
- New ``pygit2.DiffStats``
`#406 <https://github.com/libgit2/pygit2/issues/406>`_
`#525 <https://github.com/libgit2/pygit2/pull/525>`_
- New ``Repository.get_attr(...)``
`#528 <https://github.com/libgit2/pygit2/pull/528>`_
- New ``level`` optional parameter in ``Index.remove``
`#533 <https://github.com/libgit2/pygit2/pull/533>`_
- New ``repr(TreeEntry)``
`#543 <https://github.com/libgit2/pygit2/pull/543>`_
Build and install improvements:
- Make pygit work in a frozen environment
`#453 <https://github.com/libgit2/pygit2/pull/453>`_
- Make pygit2 work with pyinstaller
`#510 <https://github.com/libgit2/pygit2/pull/510>`_
Bugs fixed:
- Fix memory issues
`#477 <https://github.com/libgit2/pygit2/issues/477>`_
`#487 <https://github.com/libgit2/pygit2/pull/487>`_
`#520 <https://github.com/libgit2/pygit2/pull/520>`_
- Fix TreeEntry equality testing
`#458 <https://github.com/libgit2/pygit2/issues/458>`_
`#488 <https://github.com/libgit2/pygit2/pull/488>`_
- ``Repository.write_archive`` fix handling of symlinks
`#480 <https://github.com/libgit2/pygit2/pull/480>`_
- Fix type check in ``Diff[...]``
`#495 <https://github.com/libgit2/pygit2/issues/495>`_
- Fix error when merging files with unicode content
`#505 <https://github.com/libgit2/pygit2/pull/505>`_
Other:
- Documentation improvements and fixes
`#448 <https://github.com/libgit2/pygit2/pull/448>`_
`#491 <https://github.com/libgit2/pygit2/pull/491>`_
`#497 <https://github.com/libgit2/pygit2/pull/497>`_
`#507 <https://github.com/libgit2/pygit2/pull/507>`_
`#517 <https://github.com/libgit2/pygit2/pull/517>`_
`#518 <https://github.com/libgit2/pygit2/pull/518>`_
`#519 <https://github.com/libgit2/pygit2/pull/519>`_
`#521 <https://github.com/libgit2/pygit2/pull/521>`_
`#523 <https://github.com/libgit2/pygit2/pull/523>`_
`#527 <https://github.com/libgit2/pygit2/pull/527>`_
`#536 <https://github.com/libgit2/pygit2/pull/536>`_
- Expose the ``pygit2.GIT_REPOSITORY_INIT_*`` constants
`#483 <https://github.com/libgit2/pygit2/issues/483>`_
0.22.0 (2015-01-16)
-------------------
New:
- Update to libgit2 v0.22
`#459 <https://github.com/libgit2/pygit2/pull/459>`_
- Add support for libgit2 feature detection
(new ``pygit2.features`` and ``pygit2.GIT_FEATURE_*``)
`#475 <https://github.com/libgit2/pygit2/pull/475>`_
- New ``Repository.remotes`` (``RemoteCollection``)
`#447 <https://github.com/libgit2/pygit2/pull/447>`_
API Changes:
- Prototype of ``clone_repository`` changed, check documentation
- Removed ``clone_into``, use ``clone_repository`` with callbacks instead
- Use ``Repository.remotes.rename(name, new_name)`` instead of
``Remote.rename(new_name)``
- Use ``Repository.remotes.delete(name)`` instead of ``Remote.delete()``
- Now ``Remote.push(...)`` takes a list of refspecs instead of just one
- Change ``Patch.old_id``, ``Patch.new_id``, ``Note.annotated_id``,
``RefLogEntry.oid_old`` and ``RefLogEntry.oid_new`` to be ``Oid`` objects
instead of strings
`#449 <https://github.com/libgit2/pygit2/pull/449>`_
Other:
- Fix ``init_repository`` when passing optional parameters ``workdir_path``,
``description``, ``template_path``, ``initial_head`` or ``origin_url``
`#466 <https://github.com/libgit2/pygit2/issues/466>`_
`#471 <https://github.com/libgit2/pygit2/pull/471>`_
- Fix use-after-free when patch outlives diff
`#457 <https://github.com/libgit2/pygit2/issues/457>`_
`#461 <https://github.com/libgit2/pygit2/pull/461>`_
`#474 <https://github.com/libgit2/pygit2/pull/474>`_
- Documentation improvements
`#456 <https://github.com/libgit2/pygit2/issues/456>`_
`#462 <https://github.com/libgit2/pygit2/pull/462>`_
`#465 <https://github.com/libgit2/pygit2/pull/465>`_
`#472 <https://github.com/libgit2/pygit2/pull/472>`_
`#473 <https://github.com/libgit2/pygit2/pull/473>`_
- Make the GPL exception explicit in setup.py
`#450 <https://github.com/libgit2/pygit2/pull/450>`_
0.21.4 (2014-11-04)
-------------------
- Fix credentials callback not set when pushing
`#431 <https://github.com/libgit2/pygit2/pull/431>`_
`#435 <https://github.com/libgit2/pygit2/issues/435>`_
`#437 <https://github.com/libgit2/pygit2/issues/437>`_
`#438 <https://github.com/libgit2/pygit2/pull/438>`_
- Fix ``Repository.diff(...)`` when treeish is "empty"
`#432 <https://github.com/libgit2/pygit2/issues/432>`_
- New ``Reference.peel(...)`` renders ``Reference.get_object()`` obsolete
`#434 <https://github.com/libgit2/pygit2/pull/434>`_
- New, authenticate using ssh agent
`#424 <https://github.com/libgit2/pygit2/pull/424>`_
- New ``Repository.merge_commits(...)``
`#445 <https://github.com/libgit2/pygit2/pull/445>`_
- Make it easier to run when libgit2 not in a standard location
`#441 <https://github.com/libgit2/pygit2/issues/441>`_
- Documentation: review install chapter
- Documentation: many corrections
`#427 <https://github.com/libgit2/pygit2/pull/427>`_
`#429 <https://github.com/libgit2/pygit2/pull/429>`_
`#439 <https://github.com/libgit2/pygit2/pull/439>`_
`#440 <https://github.com/libgit2/pygit2/pull/440>`_
`#442 <https://github.com/libgit2/pygit2/pull/442>`_
`#443 <https://github.com/libgit2/pygit2/pull/443>`_
`#444 <https://github.com/libgit2/pygit2/pull/444>`_
0.21.3 (2014-09-15)
-------------------
Breaking changes:
- Now ``Repository.blame(...)`` returns ``Oid`` instead of string
`#413 <https://github.com/libgit2/pygit2/pull/413>`_
- New ``Reference.set_target(...)`` replaces the ``Reference.target`` setter
and ``Reference.log_append(...)``
`#414 <https://github.com/libgit2/pygit2/pull/414>`_
- New ``Repository.set_head(...)`` replaces the ``Repository.head`` setter
`#414 <https://github.com/libgit2/pygit2/pull/414>`_
- ``Repository.merge(...)`` now uses the ``SAFE_CREATE`` strategy by default
`#417 <https://github.com/libgit2/pygit2/pull/417>`_
Other changes:
- New ``Remote.delete()``
`#418 <https://github.com/libgit2/pygit2/issues/418>`_
`#420 <https://github.com/libgit2/pygit2/pull/420>`_
- New ``Repository.write_archive(...)``
`#421 <https://github.com/libgit2/pygit2/pull/421>`_
- Now ``Repository.checkout(...)`` accepts branch objects
`#408 <https://github.com/libgit2/pygit2/pull/408>`_
- Fix refcount leak in remotes
`#403 <https://github.com/libgit2/pygit2/issues/403>`_
`#404 <https://github.com/libgit2/pygit2/pull/404>`_
`#419 <https://github.com/libgit2/pygit2/pull/419>`_
- Various fixes to ``clone_repository(...)``
`#399 <https://github.com/libgit2/pygit2/issues/399>`_
`#411 <https://github.com/libgit2/pygit2/pull/411>`_
`#425 <https://github.com/libgit2/pygit2/issues/425>`_
`#426 <https://github.com/libgit2/pygit2/pull/426>`_
- Fix build error in Python 3
`#401 <https://github.com/libgit2/pygit2/pull/401>`_
- Now ``pip install pygit2`` installs cffi first
`#380 <https://github.com/libgit2/pygit2/issues/380>`_
`#407 <https://github.com/libgit2/pygit2/pull/407>`_
- Add support for PyPy3
`#422 <https://github.com/libgit2/pygit2/pull/422>`_
- Documentation improvements
`#398 <https://github.com/libgit2/pygit2/pull/398>`_
`#409 <https://github.com/libgit2/pygit2/pull/409>`_
0.21.2 (2014-08-09)
-------------------
- Fix regression with Python 2, ``IndexEntry.path`` returns str
(bytes in Python 2 and unicode in Python 3)
- Get back ``IndexEntry.oid`` for backwards compatibility
- Config, iterate over the keys (instead of the key/value pairs)
`#395 <https://github.com/libgit2/pygit2/pull/395>`_
- ``Diff.find_similar`` supports new threshold arguments
`#396 <https://github.com/libgit2/pygit2/pull/396>`_
- Optimization, do not load the object when expanding an oid prefix
`#397 <https://github.com/libgit2/pygit2/pull/397>`_
0.21.1 (2014-07-22)
-------------------
- Install fix
`#382 <https://github.com/libgit2/pygit2/pull/382>`_
- Documentation improved, including
`#383 <https://github.com/libgit2/pygit2/pull/383>`_
`#385 <https://github.com/libgit2/pygit2/pull/385>`_
`#388 <https://github.com/libgit2/pygit2/pull/388>`_
- Documentation, use the read-the-docs theme
`#387 <https://github.com/libgit2/pygit2/pull/387>`_
- Coding style improvements
`#392 <https://github.com/libgit2/pygit2/pull/392>`_
- New ``Repository.state_cleanup()``
`#386 <https://github.com/libgit2/pygit2/pull/386>`_
- New ``Index.conflicts``
`#345 <https://github.com/libgit2/pygit2/issues/345>`_
`#389 <https://github.com/libgit2/pygit2/pull/389>`_
- New checkout option to define the target directory
`#390 <https://github.com/libgit2/pygit2/pull/390>`_
Backward incompatible changes:
- Now the checkout strategy must be a keyword argument.
Change ``Repository.checkout(refname, strategy)`` to
``Repository.checkout(refname, strategy=strategy)``
Idem for ``checkout_head``, ``checkout_index`` and ``checkout_tree``
0.21.0 (2014-06-27)
-------------------
Highlights:
- Drop official support for Python 2.6, and add support for Python 3.4
`#376 <https://github.com/libgit2/pygit2/pull/376>`_
- Upgrade to libgit2 v0.21.0
`#374 <https://github.com/libgit2/pygit2/pull/374>`_
- Start using cffi
`#360 <https://github.com/libgit2/pygit2/pull/360>`_
`#361 <https://github.com/libgit2/pygit2/pull/361>`_
Backward incompatible changes:
- Replace ``oid`` by ``id`` through the API to follow libgit2 conventions.
- Merge API overhaul following changes in libgit2.
- New ``Remote.rename(...)`` replaces ``Remote.name = ...``
- Now ``Remote.fetch()`` returns a ``TransferProgress`` object.
- Now ``Config.get_multivar(...)`` returns an iterator instead of a list.
New features:
- New ``Config.snapshot()`` and ``Repository.config_snapshot()``
- New ``Config`` methods: ``get_bool(...)``, ``get_int(...)``,
``parse_bool(...)`` and ``parse_int(...)``
`#357 <https://github.com/libgit2/pygit2/pull/357>`_
- Blob: implement the memory buffer interface
`#362 <https://github.com/libgit2/pygit2/pull/362>`_
- New ``clone_into(...)`` function
`#368 <https://github.com/libgit2/pygit2/pull/368>`_
- Now ``Index`` can be used alone, without a repository
`#372 <https://github.com/libgit2/pygit2/pull/372>`_
- Add more options to ``init_repository``
`#347 <https://github.com/libgit2/pygit2/pull/347>`_
- Support ``Repository.workdir = ...`` and
support setting detached heads ``Repository.head = <Oid>``
`#377 <https://github.com/libgit2/pygit2/pull/377>`_
Other:
- Fix again build with VS2008
`#364 <https://github.com/libgit2/pygit2/pull/364>`_
- Fix ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)`` arguments passing
`#366 <https://github.com/libgit2/pygit2/pull/366>`_
- Fail gracefully when compiling against the wrong version of libgit2
`#365 <https://github.com/libgit2/pygit2/pull/365>`_
- Several documentation improvements and updates
`#359 <https://github.com/libgit2/pygit2/pull/359>`_
`#375 <https://github.com/libgit2/pygit2/pull/375>`_
`#378 <https://github.com/libgit2/pygit2/pull/378>`_
0.20.3 (2014-04-02)
-------------------
- A number of memory issues fixed
`#328 <https://github.com/libgit2/pygit2/pull/328>`_
`#348 <https://github.com/libgit2/pygit2/pull/348>`_
`#353 <https://github.com/libgit2/pygit2/pull/353>`_
`#355 <https://github.com/libgit2/pygit2/pull/355>`_
`#356 <https://github.com/libgit2/pygit2/pull/356>`_
- Compatibility fixes for
PyPy (`#338 <https://github.com/libgit2/pygit2/pull/338>`_),
Visual Studio 2008 (`#343 <https://github.com/libgit2/pygit2/pull/343>`_)
and Python 3.3 (`#351 <https://github.com/libgit2/pygit2/pull/351>`_)
- Make the sort mode parameter in ``Repository.walk(...)`` optional
`#337 <https://github.com/libgit2/pygit2/pull/337>`_
- New ``Object.peel(...)``
`#342 <https://github.com/libgit2/pygit2/pull/342>`_
- New ``Index.add_all(...)``
`#344 <https://github.com/libgit2/pygit2/pull/344>`_
- Introduce support for libgit2 options
`#350 <https://github.com/libgit2/pygit2/pull/350>`_
- More informative repr for ``Repository`` objects
`#352 <https://github.com/libgit2/pygit2/pull/352>`_
- Introduce support for credentials
`#354 <https://github.com/libgit2/pygit2/pull/354>`_
- Several documentation fixes
`#302 <https://github.com/libgit2/pygit2/issues/302>`_
`#336 <https://github.com/libgit2/pygit2/issues/336>`_
- Tests, remove temporary files
`#341 <https://github.com/libgit2/pygit2/pull/341>`_
0.20.2 (2014-02-04)
-------------------
- Support PyPy
`#209 <https://github.com/libgit2/pygit2/issues/209>`_
`#327 <https://github.com/libgit2/pygit2/pull/327>`_
`#333 <https://github.com/libgit2/pygit2/pull/333>`_
Repository:
- New ``Repository.default_signature``
`#310 <https://github.com/libgit2/pygit2/pull/310>`_
Oid:
- New ``str(Oid)`` deprecates ``Oid.hex``
`#322 <https://github.com/libgit2/pygit2/pull/322>`_
Object:
- New ``Object.id`` deprecates ``Object.oid``
`#322 <https://github.com/libgit2/pygit2/pull/322>`_
- New ``TreeEntry.id`` deprecates ``TreeEntry.oid``
`#322 <https://github.com/libgit2/pygit2/pull/322>`_
- New ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)``
`#307 <https://github.com/libgit2/pygit2/pull/307>`_
- New ``Commit.tree_id`` and ``Commit.parent_ids``
`#73 <https://github.com/libgit2/pygit2/issues/73>`_
`#311 <https://github.com/libgit2/pygit2/pull/311>`_
- New rich comparison between tree entries
`#305 <https://github.com/libgit2/pygit2/issues/305>`_
`#313 <https://github.com/libgit2/pygit2/pull/313>`_
- Now ``Tree.__contains__(key)`` supports paths
`#306 <https://github.com/libgit2/pygit2/issues/306>`_
`#316 <https://github.com/libgit2/pygit2/pull/316>`_
Index:
- Now possible to create ``IndexEntry(...)``
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
- Now ``IndexEntry.path``, ``IndexEntry.oid`` and ``IndexEntry.mode`` are
writable
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
- Now ``Index.add(...)`` accepts an ``IndexEntry`` too
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
- Now ``Index.write_tree(...)`` is able to write to a different repository
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
- Fix memory leak in ``IndexEntry.path`` setter
`#335 <https://github.com/libgit2/pygit2/pull/335>`_
Config:
- New ``Config`` iterator replaces ``Config.foreach``
`#183 <https://github.com/libgit2/pygit2/issues/183>`_
`#312 <https://github.com/libgit2/pygit2/pull/312>`_
Remote:
- New type ``Refspec``
`#314 <https://github.com/libgit2/pygit2/pull/314>`_
- New ``Remote.push_url``
`#315 <https://github.com/libgit2/pygit2/pull/314>`_
- New ``Remote.add_push`` and ``Remote.add_fetch``
`#255 <https://github.com/libgit2/pygit2/issues/255>`_
`#318 <https://github.com/libgit2/pygit2/pull/318>`_
- New ``Remote.fetch_refspecs`` replaces ``Remote.get_fetch_refspecs()`` and
``Remote.set_fetch_refspecs(...)``
`#319 <https://github.com/libgit2/pygit2/pull/319>`_
- New ``Remote.push_refspecs`` replaces ``Remote.get_push_refspecs()`` and
``Remote.set_push_refspecs(...)``
`#319 <https://github.com/libgit2/pygit2/pull/319>`_
- New ``Remote.progress``, ``Remote.transfer_progress`` and
``Remote.update_tips``
`#274 <https://github.com/libgit2/pygit2/issues/274>`_
`#324 <https://github.com/libgit2/pygit2/pull/324>`_
- New type ``TransferProgress``
`#274 <https://github.com/libgit2/pygit2/issues/274>`_
`#324 <https://github.com/libgit2/pygit2/pull/324>`_
- Fix refcount leak in ``Repository.remotes``
`#321 <https://github.com/libgit2/pygit2/issues/321>`_
`#332 <https://github.com/libgit2/pygit2/pull/332>`_
Other: `#331 <https://github.com/libgit2/pygit2/pull/331>`_
0.20.1 (2013-12-24)
-------------------
- New remote ref-specs API:
`#290 <https://github.com/libgit2/pygit2/pull/290>`_
- New ``Repository.reset(...)``:
`#292 <https://github.com/libgit2/pygit2/pull/292>`_,
`#294 <https://github.com/libgit2/pygit2/pull/294>`_
- Export ``GIT_DIFF_MINIMAL``:
`#293 <https://github.com/libgit2/pygit2/pull/293>`_
- New ``Repository.merge(...)``:
`#295 <https://github.com/libgit2/pygit2/pull/295>`_
- Fix ``Repository.blame`` argument handling:
`#297 <https://github.com/libgit2/pygit2/pull/297>`_
- Fix build error on Windows:
`#298 <https://github.com/libgit2/pygit2/pull/298>`_
- Fix typo in the README file, Blog → Blob:
`#301 <https://github.com/libgit2/pygit2/pull/301>`_
- Now ``Diff.patch`` returns ``None`` if no patch:
`#232 <https://github.com/libgit2/pygit2/pull/232>`_,
`#303 <https://github.com/libgit2/pygit2/pull/303>`_
- New ``Walker.simplify_first_parent()``:
`#304 <https://github.com/libgit2/pygit2/pull/304>`_
0.20.0 (2013-11-24)
-------------------
- Upgrade to libgit2 v0.20.0:
`#288 <https://github.com/libgit2/pygit2/pull/288>`_
- New ``Repository.head_is_unborn`` replaces ``Repository.head_is_orphaned``
- Changed ``pygit2.clone_repository(...)``. Drop ``push_url``, ``fetch_spec``
and ``push_spec`` parameters. Add ``ignore_cert_errors``.
- New ``Patch.additions`` and ``Patch.deletions``:
`#275 <https://github.com/libgit2/pygit2/pull/275>`_
- New ``Patch.is_binary``:
`#276 <https://github.com/libgit2/pygit2/pull/276>`_
- New ``Reference.log_append(...)``:
`#277 <https://github.com/libgit2/pygit2/pull/277>`_
- New ``Blob.is_binary``:
`#278 <https://github.com/libgit2/pygit2/pull/278>`_
- New ``len(Diff)`` shows the number of patches:
`#281 <https://github.com/libgit2/pygit2/pull/281>`_
- Rewrite ``Repository.status()``:
`#283 <https://github.com/libgit2/pygit2/pull/283>`_
- New ``Reference.shorthand``:
`#284 <https://github.com/libgit2/pygit2/pull/284>`_
- New ``Repository.blame(...)``:
`#285 <https://github.com/libgit2/pygit2/pull/285>`_
- Now ``Repository.listall_references()`` and
``Repository.listall_branches()`` return a list, not a tuple:
`#289 <https://github.com/libgit2/pygit2/pull/289>`_

@ -2,19 +2,22 @@
pygit2 - libgit2 bindings in Python
######################################################################
.. image:: https://secure.travis-ci.org/libgit2/pygit2.png
.. image:: https://travis-ci.org/libgit2/pygit2.svg?branch=master
:target: http://travis-ci.org/libgit2/pygit2
.. image:: https://ci.appveyor.com/api/projects/status/edmwc0dctk5nacx0/branch/master?svg=true
:target: https://ci.appveyor.com/project/jdavid/pygit2/branch/master
Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2
implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and
pypy.
implements Git plumbing. Pygit2 works with Python 2.7, 3.3, 3.4, 3.5, 3.6
and PyPy 2.6
Links:
- http://github.com/libgit2/pygit2 -- Source code and issue tracker
- https://github.com/libgit2/pygit2 -- Source code and issue tracker
- http://www.pygit2.org/ -- Documentation
- http://pypi.python.org/pypi/pygit2 -- Download
- https://pypi.python.org/pypi/pygit2 -- Download
- https://github.com/libgit2/pygit2/blob/master/CHANGELOG.rst -- Changelog
How to install
==============
@ -22,363 +25,50 @@ How to install
- Check http://www.pygit2.org/install.html
Changelog
==============
0.21.2 (2014-08-09)
-------------------
- Fix regression with Python 2, ``IndexEntry.path`` returns str
(bytes in Python 2 and unicode in Python 3)
- Get back ``IndexEntry.oid`` for backwards compatibility
- Config, iterate over the keys (instead of the key/value pairs)
`#395 <https://github.com/libgit2/pygit2/pull/395>`_
- ``Diff.find_similar`` supports new threshold arguments
`#396 <https://github.com/libgit2/pygit2/pull/396>`_
- Optimization, do not load the object when expanding an oid prefix
`#397 <https://github.com/libgit2/pygit2/pull/397>`_
0.21.1 (2014-07-22)
-------------------
- Install fix
`#382 <https://github.com/libgit2/pygit2/pull/382>`_
- Documentation improved, including
`#383 <https://github.com/libgit2/pygit2/pull/383>`_
`#385 <https://github.com/libgit2/pygit2/pull/385>`_
`#388 <https://github.com/libgit2/pygit2/pull/388>`_
- Documentation, use the read-the-docs theme
`#387 <https://github.com/libgit2/pygit2/pull/387>`_
- Coding style improvements
`#392 <https://github.com/libgit2/pygit2/pull/392>`_
- New ``Repository.state_cleanup()``
`#386 <https://github.com/libgit2/pygit2/pull/386>`_
- New ``Index.conflicts``
`#345 <https://github.com/libgit2/pygit2/issues/345>`_
`#389 <https://github.com/libgit2/pygit2/pull/389>`_
- New checkout option to define the target directory
`#390 <https://github.com/libgit2/pygit2/pull/390>`_
Backward incompatible changes:
- Now the checkout strategy must be a keyword argument.
Change ``Repository.checkout(refname, strategy)`` to
``Repository.checkout(refname, strategy=strategy)``
Idem for ``checkout_head``, ``checkout_index`` and ``checkout_tree``
0.21.0 (2014-06-27)
-------------------
Highlights:
- Drop official support for Python 2.6, and add support for Python 3.4
`#376 <https://github.com/libgit2/pygit2/pull/376>`_
- Upgrade to libgit2 v0.21.0
`#374 <https://github.com/libgit2/pygit2/pull/374>`_
- Start using cffi
`#360 <https://github.com/libgit2/pygit2/pull/360>`_
`#361 <https://github.com/libgit2/pygit2/pull/361>`_
Backward incompatible changes:
- Replace ``oid`` by ``id`` through the API to follow libgit2 conventions.
- Merge API overhaul following changes in libgit2.
- New ``Remote.rename(...)`` replaces ``Remote.name = ...``
- Now ``Remote.fetch()`` returns a ``TransferProgress`` object.
- Now ``Config.get_multivar(...)`` returns an iterator instead of a list.
New features:
- New ``Config.snapshot()`` and ``Repository.config_snapshot()``
- New ``Config`` methods: ``get_bool(...)``, ``get_int(...)``,
``parse_bool(...)`` and ``parse_int(...)``
`#357 <https://github.com/libgit2/pygit2/pull/357>`_
- Blob: implement the memory buffer interface
`#362 <https://github.com/libgit2/pygit2/pull/362>`_
- New ``clone_into(...)`` function
`#368 <https://github.com/libgit2/pygit2/pull/368>`_
- Now ``Index`` can be used alone, without a repository
`#372 <https://github.com/libgit2/pygit2/pull/372>`_
- Add more options to ``init_repository``
`#347 <https://github.com/libgit2/pygit2/pull/347>`_
- Support ``Repository.workdir = ...`` and
support setting detached heads ``Repository.head = <Oid>``
`#377 <https://github.com/libgit2/pygit2/pull/377>`_
Other:
- Fix again build with VS2008
`#364 <https://github.com/libgit2/pygit2/pull/364>`_
- Fix ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)`` arguments passing
`#366 <https://github.com/libgit2/pygit2/pull/366>`_
- Fail gracefully when compiling against the wrong version of libgit2
`#365 <https://github.com/libgit2/pygit2/pull/365>`_
- Several documentation improvements and updates
`#359 <https://github.com/libgit2/pygit2/pull/359>`_
`#375 <https://github.com/libgit2/pygit2/pull/375>`_
`#378 <https://github.com/libgit2/pygit2/pull/378>`_
0.20.3 (2014-04-02)
-------------------
- A number of memory issues fixed
`#328 <https://github.com/libgit2/pygit2/pull/328>`_
`#348 <https://github.com/libgit2/pygit2/pull/348>`_
`#353 <https://github.com/libgit2/pygit2/pull/353>`_
`#355 <https://github.com/libgit2/pygit2/pull/355>`_
`#356 <https://github.com/libgit2/pygit2/pull/356>`_
- Compatibility fixes for
PyPy (`#338 <https://github.com/libgit2/pygit2/pull/338>`_),
Visual Studio 2008 (`#343 <https://github.com/libgit2/pygit2/pull/343>`_)
and Python 3.3 (`#351 <https://github.com/libgit2/pygit2/pull/351>`_)
- Make the sort mode parameter in ``Repository.walk(...)`` optional
`#337 <https://github.com/libgit2/pygit2/pull/337>`_
- New ``Object.peel(...)``
`#342 <https://github.com/libgit2/pygit2/pull/342>`_
- New ``Index.add_all(...)``
`#344 <https://github.com/libgit2/pygit2/pull/344>`_
- Introduce support for libgit2 options
`#350 <https://github.com/libgit2/pygit2/pull/350>`_
- More informative repr for ``Repository`` objects
`#352 <https://github.com/libgit2/pygit2/pull/352>`_
- Introduce support for credentials
`#354 <https://github.com/libgit2/pygit2/pull/354>`_
- Several documentation fixes
`#302 <https://github.com/libgit2/pygit2/issues/302>`_
`#336 <https://github.com/libgit2/pygit2/issues/336>`_
- Tests, remove temporary files
`#341 <https://github.com/libgit2/pygit2/pull/341>`_
0.20.2 (2014-02-04)
-------------------
- Support pypy
`#209 <https://github.com/libgit2/pygit2/issues/209>`_
`#327 <https://github.com/libgit2/pygit2/pull/327>`_
`#333 <https://github.com/libgit2/pygit2/pull/333>`_
Repository:
- New ``Repository.default_signature``
`#310 <https://github.com/libgit2/pygit2/pull/310>`_
Oid:
- New ``str(Oid)`` deprecates ``Oid.hex``
`#322 <https://github.com/libgit2/pygit2/pull/322>`_
Object:
- New ``Object.id`` deprecates ``Object.oid``
`#322 <https://github.com/libgit2/pygit2/pull/322>`_
- New ``TreeEntry.id`` deprecates ``TreeEntry.oid``
`#322 <https://github.com/libgit2/pygit2/pull/322>`_
- New ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)``
`#307 <https://github.com/libgit2/pygit2/pull/307>`_
- New ``Commit.tree_id`` and ``Commit.parent_ids``
`#73 <https://github.com/libgit2/pygit2/issues/73>`_
`#311 <https://github.com/libgit2/pygit2/pull/311>`_
- New rich comparison between tree entries
`#305 <https://github.com/libgit2/pygit2/issues/305>`_
`#313 <https://github.com/libgit2/pygit2/pull/313>`_
- Now ``Tree.__contains__(key)`` supports paths
`#306 <https://github.com/libgit2/pygit2/issues/306>`_
`#316 <https://github.com/libgit2/pygit2/pull/316>`_
Index:
- Now possible to create ``IndexEntry(...)``
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
- Now ``IndexEntry.path``, ``IndexEntry.oid`` and ``IndexEntry.mode`` are
writable
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
- Now ``Index.add(...)`` accepts an ``IndexEntry`` too
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
- Now ``Index.write_tree(...)`` is able to write to a different repository
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
- Fix memory leak in ``IndexEntry.path`` setter
`#335 <https://github.com/libgit2/pygit2/pull/335>`_
Config:
- New ``Config`` iterator replaces ``Config.foreach``
`#183 <https://github.com/libgit2/pygit2/issues/183>`_
`#312 <https://github.com/libgit2/pygit2/pull/312>`_
Remote:
- New type ``Refspec``
`#314 <https://github.com/libgit2/pygit2/pull/314>`_
- New ``Remote.push_url``
`#315 <https://github.com/libgit2/pygit2/pull/314>`_
- New ``Remote.add_push`` and ``Remote.add_fetch``
`#255 <https://github.com/libgit2/pygit2/issues/255>`_
`#318 <https://github.com/libgit2/pygit2/pull/318>`_
- New ``Remote.fetch_refspecs`` replaces ``Remote.get_fetch_refspecs()`` and
``Remote.set_fetch_refspecs(...)``
`#319 <https://github.com/libgit2/pygit2/pull/319>`_
- New ``Remote.push_refspecs`` replaces ``Remote.get_push_refspecs()`` and
``Remote.set_push_refspecs(...)``
`#319 <https://github.com/libgit2/pygit2/pull/319>`_
- New ``Remote.progress``, ``Remote.transfer_progress`` and
``Remote.update_tips``
`#274 <https://github.com/libgit2/pygit2/issues/274>`_
`#324 <https://github.com/libgit2/pygit2/pull/324>`_
- New type ``TransferProgress``
`#274 <https://github.com/libgit2/pygit2/issues/274>`_
`#324 <https://github.com/libgit2/pygit2/pull/324>`_
- Fix refcount leak in ``Repository.remotes``
`#321 <https://github.com/libgit2/pygit2/issues/321>`_
`#332 <https://github.com/libgit2/pygit2/pull/332>`_
Other: `#331 <https://github.com/libgit2/pygit2/pull/331>`_
0.20.1 (2013-12-24)
-------------------
- New remote ref-specs API:
`#290 <https://github.com/libgit2/pygit2/pull/290>`_
- New ``Repository.reset(...)``:
`#292 <https://github.com/libgit2/pygit2/pull/292>`_,
`#294 <https://github.com/libgit2/pygit2/pull/294>`_
- Export ``GIT_DIFF_MINIMAL``:
`#293 <https://github.com/libgit2/pygit2/pull/293>`_
- New ``Repository.merge(...)``:
`#295 <https://github.com/libgit2/pygit2/pull/295>`_
- Fix ``Repository.blame`` argument handling:
`#297 <https://github.com/libgit2/pygit2/pull/297>`_
- Fix build error on Windows:
`#298 <https://github.com/libgit2/pygit2/pull/298>`_
- Fix typo in the README file, Blog → Blob:
`#301 <https://github.com/libgit2/pygit2/pull/301>`_
- Now ``Diff.patch`` returns ``None`` if no patch:
`#232 <https://github.com/libgit2/pygit2/pull/232>`_,
`#303 <https://github.com/libgit2/pygit2/pull/303>`_
- New ``Walker.simplify_first_parent()``:
`#304 <https://github.com/libgit2/pygit2/pull/304>`_
0.20.0 (2013-11-24)
-------------------
- Upgrade to libgit2 v0.20.0:
`#288 <https://github.com/libgit2/pygit2/pull/288>`_
- New ``Repository.head_is_unborn`` replaces ``Repository.head_is_orphaned``
- Changed ``pygit2.clone_repository(...)``. Drop ``push_url``, ``fetch_spec``
and ``push_spec`` parameters. Add ``ignore_cert_errors``.
- New ``Patch.additions`` and ``Patch.deletions``:
`#275 <https://github.com/libgit2/pygit2/pull/275>`_
- New ``Patch.is_binary``:
`#276 <https://github.com/libgit2/pygit2/pull/276>`_
- New ``Reference.log_append(...)``:
`#277 <https://github.com/libgit2/pygit2/pull/277>`_
- New ``Blob.is_binary``:
`#278 <https://github.com/libgit2/pygit2/pull/278>`_
- New ``len(Diff)`` shows the number of patches:
`#281 <https://github.com/libgit2/pygit2/pull/281>`_
- Rewrite ``Repository.status()``:
`#283 <https://github.com/libgit2/pygit2/pull/283>`_
- New ``Reference.shorthand``:
`#284 <https://github.com/libgit2/pygit2/pull/284>`_
- New ``Repository.blame(...)``:
`#285 <https://github.com/libgit2/pygit2/pull/285>`_
- Now ``Repository.listall_references()`` and
``Repository.listall_branches()`` return a list, not a tuple:
`#289 <https://github.com/libgit2/pygit2/pull/289>`_
Authors
==============
69 developers have contributed at least 1 commit to pygit2::
117 developers have contributed at least 1 commit to pygit2::
J. David Ibáñez Rémi Duraffort Adam Spiers
Nico von Geyso Sebastian Thiel Alexander Bayandin
Carlos Martín Nieto Fraser Tweedale Andrew Chin
W. Trevor King Han-Wen Nienhuys András Veres-Szentkirályi
Dave Borowitz Leonardo Rhodes Benjamin Kircher
Daniel Rodríguez Troitiño Petr Viktorin Benjamin Pollack
Richo Healey Thomas Kluyver Bryan O'Sullivan
Christian Boos Alex Chamberlain Daniel Bruce
Julien Miotte Amit Bakshi David Fischer
Xu Tao Andrey Devyatkin David Sanders
Jose Plana Arno van Lumig Devaev Maxim
Martin Lenders Ben Davis Eric Davis
Petr Hosek Eric Schrijver Erik Meusel
Victor Garcia Hervé Cauwelier Erik van Zijst
Xavier Delannoy Huang Huang Ferengee
Yonggang Luo Ian P. McCullough Gustavo Di Pietro
Valentin Haenel Jack O'Connor Hugh Cole-Baker
Michael Jones Jared Flatow Jasper Lievisse Adriaanse
Bernardo Heynemann Jiunn Haur Lim Josh Bleecher Snyder
John Szakmeister Jun Omae Óscar San José
Brodie Rao Sarath Lakshman Ridge Kennedy
Vlad Temian Vicent Marti Rui Abreu Ferreira
David Versmisse Zoran Zaric earl
J. David Ibáñez Carlos Martín Nieto Nico von Geyso
W. Trevor King Dave Borowitz Matthias Bartelmeß
Daniel Rodríguez Troitiño Richo Healey Christian Boos
Julien Miotte Richard Möhn Xu Tao
Jose Plana Matthew Duggan Matthew Gamble
Martin Lenders Nick Hynes Petr Hosek
Victor Garcia Xavier Delannoy Yonggang Luo
Patrick Steinhardt Tamir Bahar Valentin Haenel
Michael Jones Bernardo Heynemann Brodie Rao
John Szakmeister Vlad Temian Lukas Fleischer
Nicolas Dandrimont David Versmisse Rémi Duraffort
Santiago Perez De Rosso Sebastian Thiel Thom Wiggers
Alok Singhal Anatoly Techtonik Fraser Tweedale
Han-Wen Nienhuys Jason Ziglar Leonardo Rhodes
Petr Viktorin Robert Hölzl Ron Cohen
Thomas Kluyver Alex Chamberlain Alexander Bayandin
Amit Bakshi Andrey Devyatkin Arno van Lumig
Ben Davis Dustin Raimondi Eric Schrijver
Greg Fitzgerald Guillermo Pérez Hervé Cauwelier
Huang Huang Ian P. McCullough Igor Gnatenko
Jack O'Connor Jared Flatow Jiunn Haur Lim
Jun Omae Kaarel Kitsemets Kevin KIN-FOO
Mark Adams Masud Rahman Michael Sondergaard
Ondřej Nový Sarath Lakshman Szucs Krisztian
Vicent Marti Zoran Zaric Adam Spiers
Andrew Chin András Veres-Szentkirályi Ash Berlin
Benjamin Kircher Benjamin Pollack Bryan O'Sullivan
Cam Cope Chason Chaffin Chris Rebert
Colin Watson Daniel Bruce David Fischer
David Sanders David Six Devaev Maxim
Eric Davis Erik Meusel Erik van Zijst
Ferengee Gustavo Di Pietrou Holger Frey
Hugh Cole-Baker Jasper Lievisse Adriaanse Josh Bleecher Snyder
Justin Clift Kyriakos Oikonomakos Mathieu Bridon
Matthaus Woolard Nicolás Sanguinetti Noah Fontes
Óscar San José Peter Dave Hello Philippe Ombredanne
Ridge Kennedy Ross Nicoll Rui Abreu Ferreira
Sheeo Soasme Vladimir Rutsky
Yu Jianjian chengyuhang earl
License

53
appveyor.yml Normal file

@ -0,0 +1,53 @@
version: 1.0.{build}
image: Visual Studio 2015
configuration: Release
environment:
matrix:
- GENERATOR: 'Visual Studio 10'
PYTHON: 'C:\Python27\python.exe'
- GENERATOR: 'Visual Studio 10 Win64'
PYTHON: 'C:\Python27-x64\python.exe'
- GENERATOR: 'Visual Studio 10'
PYTHON: 'C:\Python33\python.exe'
- GENERATOR: 'Visual Studio 10 Win64'
PYTHON: 'C:\Python33-x64\python.exe'
- GENERATOR: 'Visual Studio 10'
PYTHON: 'C:\Python34\python.exe'
- GENERATOR: 'Visual Studio 10 Win64'
PYTHON: 'C:\Python34-x64\python.exe'
- GENERATOR: 'Visual Studio 14'
PYTHON: 'C:\Python35\python.exe'
- GENERATOR: 'Visual Studio 14 Win64'
PYTHON: 'C:\Python35-x64\python.exe'
- GENERATOR: 'Visual Studio 14'
PYTHON: 'C:\Python36\python.exe'
- GENERATOR: 'Visual Studio 14 Win64'
PYTHON: 'C:\Python36-x64\python.exe'
init:
- cmd: '%PYTHON% -m pip install -U nose wheel'
build_script:
- cmd: |
set LIBGIT2=%APPVEYOR_BUILD_FOLDER%\build\libgit2
git clone --depth=1 -b maint/v0.26 https://github.com/libgit2/libgit2.git libgit2
mkdir build
cd build
cmake -DSTDCALL=OFF -DBUILD_CLAR=OFF -DCMAKE_INSTALL_PREFIX="%LIBGIT2%" ../libgit2 -G "%GENERATOR%"
cmake --build . --config Release --target install
cd ..
IF "%GENERATOR%"=="Visual Studio 10 Win64" ( call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" )
"%PYTHON%" setup.py bdist_wheel
test_script:
- ps: |
cp build\Release\git2.dll .
&$env:PYTHON setup.py nosetests --with-xunit
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
# upload results to AppVeyor
$wc = New-Object 'System.Net.WebClient'
$wc.UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\nosetests.xml))
artifacts:
- path: dist\*.whl

8
docs/backends.rst Normal file

@ -0,0 +1,8 @@
**********************************************************************
Custom backends
**********************************************************************
There is some support for custom backends, but undocumented. See
`<https://github.com/libgit2/pygit2/pull/690/commits>`_
Documentation contributions are very welcome.

@ -11,12 +11,14 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
import sys, os, platform
from string import digits
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../build/lib.linux-x86_64-2.7'))
version_string = sys.platform.rstrip(digits) + "-" + os.uname()[4] + "-" + ".".join(platform.python_version_tuple()[0:2])
sys.path.insert(0, os.path.abspath('../build/lib.' + version_string))
# -- General configuration -----------------------------------------------------
@ -41,16 +43,16 @@ master_doc = 'index'
# General information about the project.
project = u'pygit2'
copyright = u'2010-2014 The pygit2 contributors'
copyright = u'2010-2015 The pygit2 contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.21'
version = '0.26'
# The full version, including alpha/beta/rc tags.
release = '0.21.2'
release = '0.26.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

@ -2,9 +2,12 @@
The development version
**********************************************************************
.. image:: https://secure.travis-ci.org/libgit2/pygit2.png
.. image:: https://travis-ci.org/libgit2/pygit2.svg?branch=master
:target: http://travis-ci.org/libgit2/pygit2
.. image:: https://ci.appveyor.com/api/projects/status/edmwc0dctk5nacx0/branch/master?svg=true
:target: https://ci.appveyor.com/project/jdavid/pygit2/branch/master
.. code-block:: sh
$ git clone git://github.com/libgit2/pygit2.git

@ -23,6 +23,10 @@ Examples
>>> diff = repo.diff('HEAD^', 'HEAD~3')
>>> patches = [p for p in diff]
# Get the stats for a diff
>>> diff = repo.diff('HEAD^', 'HEAD~3')
>>> diff.stats
# Diffing the empty tree
>>> tree = revparse_single('HEAD').tree
>>> tree.diff_to_tree()
@ -35,6 +39,10 @@ The Diff type
====================
.. autoattribute:: pygit2.Diff.patch
.. method:: Diff.__iter__()
Returns an iterator over the deltas/patches in this diff.
.. method:: Diff.__len__()
Returns the number of deltas/patches in this diff.
@ -48,26 +56,60 @@ The Patch type
Attributes:
.. autoattribute:: pygit2.Patch.old_file_path
.. autoattribute:: pygit2.Patch.new_file_path
.. autoattribute:: pygit2.Patch.old_id
.. autoattribute:: pygit2.Patch.new_id
.. autoattribute:: pygit2.Patch.status
.. autoattribute:: pygit2.Patch.similarity
.. autoattribute:: pygit2.Patch.delta
.. autoattribute:: pygit2.Patch.hunks
.. autoattribute:: pygit2.Patch.additions
.. autoattribute:: pygit2.Patch.deletions
.. autoattribute:: pygit2.Patch.line_stats
The DiffDelta type
====================
Attributes:
.. autoattribute:: pygit2.DiffDelta.old_file
.. autoattribute:: pygit2.DiffDelta.new_file
.. autoattribute:: pygit2.DiffDelta.status
.. autoattribute:: pygit2.DiffDelta.similarity
Getters:
.. autoattribute:: pygit2.Patch.is_binary
.. autoattribute:: pygit2.DiffDelta.is_binary
The Hunk type
The DiffFile type
====================
.. autoattribute:: pygit2.Hunk.old_start
.. autoattribute:: pygit2.Hunk.old_lines
.. autoattribute:: pygit2.Hunk.new_start
.. autoattribute:: pygit2.Hunk.new_lines
.. autoattribute:: pygit2.Hunk.lines
Attributes:
.. autoattribute:: pygit2.DiffFile.path
.. autoattribute:: pygit2.DiffFile.id
.. autoattribute:: pygit2.DiffFile.size
.. autoattribute:: pygit2.DiffFile.flags
.. autoattribute:: pygit2.DiffFile.mode
The DiffHunk type
====================
.. autoattribute:: pygit2.DiffHunk.old_start
.. autoattribute:: pygit2.DiffHunk.old_lines
.. autoattribute:: pygit2.DiffHunk.new_start
.. autoattribute:: pygit2.DiffHunk.new_lines
.. autoattribute:: pygit2.DiffHunk.lines
The DiffStats type
====================
.. autoattribute :: pygit2.DiffStats.insertions
.. autoattribute :: pygit2.DiffStats.deletions
.. autoattribute :: pygit2.DiffStats.files_changed
.. automethod :: pygit2.DiffStats.format
The DiffLine type
====================
.. autoattribute :: pygit2.DiffLine.origin
.. autoattribute :: pygit2.DiffLine.content
.. autoattribute :: pygit2.DiffLine.old_lineno
.. autoattribute :: pygit2.DiffLine.new_lineno
.. autoattribute :: pygit2.DiffLine.num_lines

8
docs/features.rst Normal file

@ -0,0 +1,8 @@
**********************************************************************
Feature detection
**********************************************************************
.. py:data:: pygit2.features
This variable contains a combination of `GIT_FEATURE_*` flags,
indicating which features a particular build of libgit2 supports.

@ -18,7 +18,7 @@ library that has been built against. The version number has a
.. py:data:: LIBGIT2_VER_MAJOR
Integer value of the major version number. For example, for the version
``0.21.1``::
``0.26.0``::
>>> print LIBGIT2_VER_MAJOR
0
@ -26,25 +26,25 @@ library that has been built against. The version number has a
.. py:data:: LIBGIT2_VER_MINOR
Integer value of the minor version number. For example, for the version
``0.21.1``::
``0.26.0``::
>>> print LIBGIT2_VER_MINOR
21
26
.. py:data:: LIBGIT2_VER_REVISION
Integer value of the revision version number. For example, for the version
``0.21.1``::
``0.26.0``::
>>> print LIBGIT2_VER_REVISION
1
0
.. py:data:: LIBGIT2_VERSION
The libgit2 version number as a string::
>>> print LIBGIT2_VERSION
'0.21.1'
'0.26.0'
Errors
======

@ -7,17 +7,18 @@ Welcome to pygit2's documentation!
==================================
Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2
implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and
pypy.
implements the core of Git. Pygit2 works with Python 2.7, 3.3, 3.4, 3.5,
3.6 and pypy.
It is likely to work with Python 2.6 and 3.1, but these versions are not
officially supported.
Pygit2 links:
Links:
- http://github.com/libgit2/pygit2 -- Source code and issue tracker
- https://github.com/libgit2/pygit2 -- Source code and issue tracker
- http://www.pygit2.org/ -- Documentation
- http://pypi.python.org/pypi/pygit2 -- Download
- https://pypi.python.org/pypi/pygit2 -- Download
- https://github.com/libgit2/pygit2/blob/master/CHANGELOG.rst -- Changelog
Start:
@ -45,8 +46,11 @@ Usage guide:
merge
config
remotes
submodule
blame
settings
features
backends
Indices and tables

@ -13,68 +13,83 @@ Installation
Requirements
============
- Python 2.7, 3.2, 3.3, 3.4 or pypy.
Including the development headers.
- Python 2.7, 3.3+ or PyPy 2.6+ (including the development headers)
- Libgit2 v0.26.x
- cffi 1.0+
- six
- tox (optional)
- Libgit2 v0.21.1
Optional libgit2 dependecies to support ssh and https:
- cffi 0.8.1+
- https: WinHTTP (Windows), SecureTransport (OS X) or OpenSSL.
- ssh: libssh2, pkg-config
It should work with older versions of cffi and PyPy, but using cffi 1.0+
(and PyPy 2.6+) is strongly encouraged.
One common mistake users do is to choose incompatible versions of libgit2 and
pygit2. Be sure to use the latest release of both, double check the versions do
match before filling un bug report.
.. warning::
.. note::
One common mistake users do is to choose incompatible versions of libgit2
and pygit2. See below for a reference table of compatible versions. Double
check the versions do match before filing a bug report.
The version of pygit2 is composed of three numbers separated by dots
|lq| *major.minor.micro* |rq|, where the first two numbers
|lq| *major.minor* |rq| match the first two numbers of the libgit2 version,
while the last number |lq| *.micro* |rq| auto-increments independently.
Version numbers
---------------
As illustration see this table of compatible releases:
The version number of pygit2 is composed of three numbers separated by dots
|lq| *major.minor.micro* |rq|, where the first two numbers
|lq| *major.minor* |rq| match the first two numbers of the libgit2 version,
while the last number |lq| *.micro* |rq| auto-increments independently.
+-----------+-----------------------+------------------------------+
|**libgit2**|0.21.1 |0.20.0 |
+-----------+-----------------------+------------------------------+
|**pygit2** |0.21.0, 0.21.1, 0.21.2 |0.20.0, 0.20.1, 0.20.2, 0.20.3|
+-----------+-----------------------+------------------------------+
As illustration see this table of compatible releases:
**Warning!** Backwards compatibility is not guaranteed even between micro
releases. Please check the release notes for incompatible changes before
upgrading to a new release.
+-----------+--------+----------------+------------------------+
|**libgit2**| 0.26.0 | 0.25.0, 0.25.1 | 0.24.0, 0.24.1, 0.24.2 |
+-----------+--------+----------------+------------------------+
|**pygit2** | 0.26.0 | 0.25.0, 0.25.1 | 0.24.0, 0.24.1, 0.24.2 |
+-----------+--------+----------------+------------------------+
.. warning::
Backwards compatibility is not guaranteed even between micro releases.
Please check the release notes for incompatible changes before upgrading to
a new release.
Quick install
=============
This works for me, it may work for you:
To install the latest version of libgit2 system wide, in the ``/usr/local``
directory, do:
.. code-block:: sh
$ wget https://github.com/libgit2/libgit2/archive/v0.21.1.tar.gz
$ tar xzf v0.21.1.tar.gz
$ cd libgit2-0.21.1/
$ wget https://github.com/libgit2/libgit2/archive/v0.26.0.tar.gz
$ tar xzf v0.26.0.tar.gz
$ cd libgit2-0.26.0/
$ cmake .
$ make
$ sudo make install
If this does not work for you, check the detailed instructions on building
libgit2 in various platforms, see
https://libgit2.github.com/docs/guides/build-and-link/
.. seealso::
Once libgit2 is instaleld, deploying pygit2 should be a snap:
For detailed instructions on building libgit2 check
https://libgit2.github.com/docs/guides/build-and-link/
Now install pygit2, and then verify it is correctly installed:
.. code-block:: sh
$ pìp install cffi
$ pip install pygit2
...
$ python -c 'import pygit2'
Troobleshooting
===============
Troubleshooting
---------------
You may get an error like this one:
The verification step may fail if the dynamic linker does not find the libgit2
library:
.. code-block:: sh
@ -85,71 +100,40 @@ You may get an error like this one:
from _pygit2 import *
ImportError: libgit2.so.0: cannot open shared object file: No such file or directory
It means the linker is not able to find the libgit2 library.
This happens for instance in Ubuntu: the libgit2 library is installed within
This happens for instance in Ubuntu, the libgit2 library is installed within
the ``/usr/local/lib`` directory, but the linker does not look for it there. To
fix this call ``ldconfig`` between the installation of libgit2 and the
installation of pygit2:
fix this call ``ldconfig``:
.. code-block:: sh
...
$ cmake .
$ make
$ sudo make install
...
$ sudo ldconfig
...
$ pip install cffi
$ pip install pygit2
$ python -c 'import pygit2'
Now it should work. If it does not...
Advanced: the runpath
---------------------
If it does not work yet, you can always instruct pygit2 to search for libraries
in some extra paths:
.. code-block:: sh
$ export LIBGIT2="/usr/local"
$ export LDFLAGS="-Wl,-rpath='$LIBGIT2/lib',--enable-new-dtags $LDFLAGS"
$ pip install pygit2
This compiles the pygit2 libraries with a ``RUNPATH``, which bakes extra
library search paths directly into the binaries (see the `ld man page`_ for
details). With ``RUNPATH`` compiled in, you won't have to use
``LD_LIBRARY_PATH``. You can check to ensure ``RUNPATH`` was set with
readelf_:
.. code-block:: sh
$ readelf --dynamic build/lib.linux-x86_64-3.2/_pygit2.cpython-32.so | grep PATH
0x000000000000000f (RPATH) Library rpath: [/usr/local/lib]
0x000000000000001d (RUNPATH) Library runpath: [/usr/local/lib]
.. _Shared libraries: http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
.. _ld man page: http://linux.die.net/man/1/ld
.. _readelf: http://www.gnu.org/software/binutils/
If it still does not work, please open an issue at
https://github.com/libgit2/pygit2/issues, I would like to know about it.
The LIBGIT2 environment variable
================================
Build options
=============
If libgit2 is installed in some non standard location, you will have to set the
``LIBGIT2`` environment variable before installing pygit2. This variables tells
pygit2 where libgit2 is installed.
``LIBGIT2`` -- If you install libgit2 in an unusual place, you will need to set
the ``LIBGIT2`` environment variable before installing pygit2. This variable
tells pygit2 where libgit2 is installed. We will see a concrete example later,
when explaining how to install libgit2 within a virtual environment.
``LIBGIT2_LIB`` -- This is a more rarely used build option, it allows to
override the library directory where libgit2 is installed, useful if different
from from ``$LIBGIT2/lib``.
Use case: libgit2 within a Virtualenv
-------------------------------------
libgit2 within a virtual environment
====================================
A use case for this is if you want to install libgit2 inside a virtualenv, so
you may have several virtualenvs with different versions of libgit2/pygit2,
isolated from each other. Or maybe you just don't have root access to install
libgit2 in the system.
This is how to install both libgit2 and pygit2 within a virtual environment.
This is useful if you don't have root acces to install libgit2 system wide.
Or if you wish to have different versions of libgit2/pygit2 installed in
different virtual environments, isolated from each other.
Create the virtualenv, activate it, and set the ``LIBGIT2`` environment
variable:
@ -164,9 +148,9 @@ Install libgit2 (see we define the installation prefix):
.. code-block:: sh
$ wget https://github.com/libgit2/libgit2/archive/v0.21.1.tar.gz
$ tar xzf v0.21.1.tar.gz
$ cd libgit2-0.21.1/
$ wget https://github.com/libgit2/libgit2/archive/v0.26.0.tar.gz
$ tar xzf v0.26.0.tar.gz
$ cd libgit2-0.26.0/
$ cmake . -DCMAKE_INSTALL_PREFIX=$LIBGIT2
$ make
$ make install
@ -175,31 +159,81 @@ Install pygit2:
.. code-block:: sh
$ pìp install cffi
$ export LDFLAGS="-Wl,-rpath='$LIBGIT2/lib',--enable-new-dtags $LDFLAGS"
$ pip install pygit2
$ python -c 'import pygit2'
Building on Windows
The run-path
------------------------------------------
Did you notice we set the `rpath <http://en.wikipedia.org/wiki/Rpath>`_ before
installing pygit2? Since libgit2 is installed in a non standard location, the
dynamic linker will not find it at run-time, and ``lddconfig`` will not help
this time.
So you need to either set ``LD_LIBRARY_PATH`` before using pygit2, like:
.. code-block:: sh
$ export LD_LIBRARY_PATH=$LIBGIT2/lib
$ python -c 'import pygit2'
Or, like we have done in the instructions above, use the `rpath
<http://en.wikipedia.org/wiki/Rpath>`_, it hard-codes extra search paths within
the pygit2 extension modules, so you don't need to set ``LD_LIBRARY_PATH``
everytime. Verify yourself if curious:
.. code-block:: sh
$ readelf --dynamic lib/python2.7/site-packages/pygit2-0.26.0-py2.7-linux-x86_64.egg/_pygit2.so | grep PATH
0x000000000000001d (RUNPATH) Library runpath: [/tmp/venv/lib]
Installing on Windows
===================================
pygit2 expects to find the libgit2 installed files in the directory specified
in the ``LIBGIT2`` environment variable.
`pygit2` for Windows is packaged into wheels and can be easily
installed with `pip`:
In addition, make sure that libgit2 is build in "__cdecl" mode.
The following recipe shows you how to do it, assuming you're working
from a bash shell:
pip install pygit2
For development it is also possible to build `pygit2` with `libgit2`
from sources. `libgit2` location is specified by the ``LIBGIT2``
environment variable. `libgit2` should be built in "__cdecl" mode.
The following recipe shows you how to do it from a bash shell:
.. code-block:: sh
$ export LIBGIT2=C:/Dev/libgit2
$ wget https://github.com/libgit2/libgit2/archive/v0.21.1.tar.gz
$ tar xzf v0.21.1.tar.gz
$ cd libgit2-0.21.1/
$ mkdir build
$ cd build
$ cmake .. -DSTDCALL=OFF -DCMAKE_INSTALL_PREFIX=$LIBGIT2 -G "Visual Studio 9 2008"
$ git clone --depth=1 -b maint/v0.26 https://github.com/libgit2/libgit2.git
$ cd libgit2
$ cmake . -DSTDCALL=OFF -DCMAKE_INSTALL_PREFIX=$LIBGIT2 -G "Visual Studio 9 2008"
$ cmake --build . --config release --target install
$ ctest -v
At this point, you're ready to execute the generic pygit2 installation
steps described above.
At this point, you're ready to execute the generic `pygit2`
installation steps described at the start of this page.
Installing on OS X
===================================
.. note::
You will need the `XCode <https://developer.apple.com/xcode/>`_ Developer
Tools from Apple. This free download from the Mac App Store will provide the
clang compiler needed for the installation of pygit2.
This section was tested on OS X 10.9 Mavericks and OS X 10.10 Yosemite with
Python 3.3 in a virtual environment.
The easiest way is to first install libgit2 with the `Homebrew <http://brew.sh>`_
package manager and then use pip3 for pygit2. The following example assumes that
XCode and Hombrew are already installed.
.. code-block:: sh
$ brew update
$ brew install libgit2
$ pip3 install pygit2

@ -1,5 +1,5 @@
**********************************************************************
Merge
Merge & Cherrypick
**********************************************************************
.. contents::
@ -29,7 +29,28 @@ You can now inspect the index file for conflicts and get back to the
user to resolve if there are. Once there are no conflicts left, you
can create a commit with these two parents.
>>> user = repo.default_signature()
>>> user = repo.default_signature
>>> tree = repo.index.write_tree()
>>> new_commit = repo.create_commit('HEAD', user, user, tree,
[repo.head.target, other_branch_tip])
Cherrypick
===================
.. automethod:: pygit2.Repository.cherrypick
Note that after a successful cherrypick you have to run
:py:meth:`.Repository.state_cleanup` in order to get the repository out
of cherrypicking mode.
Lower-level methods
===================
These methods allow more direct control over how to perform the
merging. They do not modify the working directory and return an
in-memory Index representing the result of the merge.
.. automethod:: pygit2.Repository.merge_commits
.. automethod:: pygit2.Repository.merge_trees

@ -132,6 +132,7 @@ them to the Git object database:
.. automethod:: pygit2.Repository.create_blob_fromworkdir
.. automethod:: pygit2.Repository.create_blob_fromdisk
.. automethod:: pygit2.Repository.create_blob_fromiobase
There are also some functions to calculate the id for a byte string without
creating the blob object:
@ -176,6 +177,7 @@ Tree entries
.. autoattribute:: pygit2.TreeEntry.id
.. autoattribute:: pygit2.TreeEntry.hex
.. autoattribute:: pygit2.TreeEntry.filemode
.. autoattribute:: pygit2.TreeEntry.type
.. method:: TreeEntry.__cmp__(TreeEntry)
@ -188,14 +190,14 @@ Example::
6
>>> for entry in tree: # Iteration
... print(entry.id, entry.name)
... print(entry.id, entry.type, entry.name)
...
7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore
c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING
32b30b90b062f66957d6790c3c155c289c34424e README.md
c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c
85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py
3d8985bbec338eb4d47c5b01b863ee89d044bd53 test
7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa blob .gitignore
c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 blob COPYING
32b30b90b062f66957d6790c3c155c289c34424e blob README.md
c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 blob pygit2.c
85a67270a49ef16cdd3d328f06a3e4b459f09b27 blob setup.py
3d8985bbec338eb4d47c5b01b863ee89d044bd53 tree test
>>> entry = tree['pygit2.c'] # Get an entry by name
>>> entry

@ -17,10 +17,11 @@ Main porcelain commands
.. toctree::
:maxdepth: 1
git-branch (List, create, or delete branches.) <recipes/git-branch>
git-cherry-pick (Apply the changes introduced by some existing commits.) <recipes/git-cherry-pick>
git-init (Create an empty git repository or reinitialize an existing one.) <recipes/git-init>
git-log (Show commit logs.) <recipes/git-log>
git-show (Show various types of objects.) <recipes/git-show>
git-tag (Create, list, delete or verify a tag object signed with GPG.) <recipes/git-tag>
git clone --mirror (Clone with a mirroring configuration) <recipes/git-clone-mirror>
.. _git man page: https://www.kernel.org/pub/software/scm/git/docs/git.html

@ -1,30 +0,0 @@
**********************************************************************
git-branch
**********************************************************************
----------------------------------------------------------------------
Listing branches
----------------------------------------------------------------------
======================================================================
List all branches
======================================================================
.. code-block:: bash
$> git branch
.. code-block:: python
>>> regex = re.compile('^refs/heads/')
>>> branches = filter(lambda r: regex.match(r), repo.listall_references())
`Note that the next release will probably allow` ``repo.listall_branches()``.
----------------------------------------------------------------------
References
----------------------------------------------------------------------
- git-branch_.
.. _git-branch: https://www.kernel.org/pub/software/scm/git/docs/git-branch.html

@ -0,0 +1,73 @@
**********************************************************************
git-cherry-pick
**********************************************************************
The convenient way to cherry-pick a commit is to use
:py:meth:`.Repository.cherrypick()`. It is limited to cherry-picking with a
working copy and on-disk index.
.. code-block:: bash
$> cd /path/to/repo
$> git checkout basket
$> git cherry-pick 9e044d03c
.. code-block:: python
repo = pygit2.Repository('/path/to/repo')
repo.checkout('basket')
cherry_id = pygit2.Oid('9e044d03c')
repo.cherrypick(cherry_id)
if repo.index.conflicts is None:
tree_id = repo.index.write_tree()
cherry = repo.get(cherry_id)
committer = pygit2.Signature('Archimedes', 'archy@jpl-classics.org')
repo.create_commit(basket.name, cherry.author, committer,
cherry.message, tree_id, [basket.target])
del basket # outdated, prevent from accidentally using it
repo.state_cleanup()
----------------------------------------------------------------------
Cherry-picking a commit without a working copy
----------------------------------------------------------------------
This way of cherry-picking gives you more control over the process and works
on bare repositories as well as repositories with a working copy.
:py:meth:`~.Repository.merge_trees()` can also be used for other tasks, for
example `three-argument rebases`_.
.. _`three-argument rebases`: https://www.kernel.org/pub/software/scm/git/docs/git-rebase.html
.. code-block:: python
repo = pygit2.Repository('/path/to/repo')
cherry = repo.revparse_single('9e044d03c')
basket = repo.branches.get('basket')
base = repo.merge_base(cherry.oid, basket.target)
base_tree = cherry.parents[0].tree
index = repo.merge_trees(base_tree, basket, cherry)
tree_id = index.write_tree(repo)
author = cherry.author
committer = pygit2.Signature('Archimedes', 'archy@jpl-classics.org')
repo.create_commit(basket.name, author, committer, cherry.message,
tree_id, [basket.target])
del None # outdated, prevent from accidentally using it
----------------------------------------------------------------------
References
----------------------------------------------------------------------
- git-cherry-pick_.
.. _git-cherry-pick: https://www.kernel.org/pub/software/scm/git/docs/git-cherry-pick.html

@ -0,0 +1,26 @@
**********************************************************************
git-clone --mirror
**********************************************************************
git provides an argument to set up the repository as a mirror, which
involves setting the refspec to one which copies all refs and a mirror
option for push in the remote.
.. code-block:: bash
$> git clone --mirror https://github.com/libgit2/pygit2
.. code-block:: python
def init_remote(repo, name, url):
# Create the remote with a mirroring url
remote = repo.remotes.create(name, url, "+refs/*:refs/*")
# And set the configuration option to true for the push command
mirror_var = "remote.{}.mirror".format(name)
repo.config[mirror_var] = True
# Return the remote, which pygit2 will use to perform the clone
return remote
print("Cloning pygit2 as mirror")
pygit2.clone_repository("https://github.com/libgit2/pygit2", "pygit2.git", bare=True,
remote=init_remote)

@ -31,7 +31,7 @@ Show SHA hash
Show diff
======================================================================
>>> diff = commit.tree.diff()
>>> diff = repo.diff(commit.parents[0], commit)
======================================================================
Show all files in commit
@ -40,6 +40,52 @@ Show all files in commit
>>> for e in commit.tree:
>>> print(e.name)
======================================================================
Produce something like a ``git show`` message
======================================================================
In order to display time zone information you have to create a subclass
of tzinfo. In Python 3.2+ you can do this fairly directly. In older
versions you have to make your own class as described in the `Python
datetime documentation`_::
from datetime import tzinfo, timedelta
class FixedOffset(tzinfo):
"""Fixed offset in minutes east from UTC."""
def __init__(self, offset):
self.__offset = timedelta(minutes = offset)
def utcoffset(self, dt):
return self.__offset
def tzname(self, dt):
return None # we don't know the time zone's name
def dst(self, dt):
return timedelta(0) # we don't know about DST
.. _Python datetime documentation: https://docs.python.org/2/library/datetime.html#tzinfo-objects
Then you can make your message:
>>> # Until Python 2.7.9:
>>> from __future__ import unicode_literals
>>> from datetime import datetime
>>> tzinfo = FixedOffset(commit.author.offset)
>>> # From Python 3.2:
>>> from datetime import datetime, timezone, timedelta
>>> tzinfo = timezone( timedelta(minutes=commit.author.offset) )
>>>
>>> dt = datetime.fromtimestamp(float(commit.author.time), tzinfo)
>>> timestr = dt.strftime('%c %z')
>>> msg = '\n'.join(['commit {}'.format(commit.tree_id.hex),
... 'Author: {} <{}>'.format(commit.author.name, commit.author.email),
... 'Date: {}'.format(timestr),
... '',
... commit.message])
----------------------------------------------------------------------
References
----------------------------------------------------------------------

@ -4,29 +4,42 @@ References
.. contents::
.. automethod:: pygit2.Repository.listall_references
.. automethod:: pygit2.Repository.lookup_reference
.. autoclass:: pygit2.repository.References
:members:
:undoc-members:
:special-members: __getitem__, __iter__, __contains__
Example::
>>> all_refs = repo.listall_references()
>>> all_refs = list(repo.references)
>>> master_ref = repo.lookup_reference("refs/heads/master")
>>> commit = master_ref.get_object() # or repo[master_ref.target]
# Create a reference
>>> ref = repo.references.create('refs/tags/version1', LAST_COMMIT)
# Delete a reference
>>> repo.references.delete('refs/tags/version1')
The Reference type
====================
.. autoclass:: pygit2.Reference
.. autoattribute:: pygit2.Reference.name
.. autoattribute:: pygit2.Reference.shorthand
.. autoattribute:: pygit2.Reference.target
.. autoattribute:: pygit2.Reference.type
.. automethod:: pygit2.Reference.set_target
.. automethod:: pygit2.Reference.delete
.. automethod:: pygit2.Reference.rename
.. automethod:: pygit2.Reference.resolve
.. automethod:: pygit2.Reference.peel
.. automethod:: pygit2.Reference.log
.. automethod:: pygit2.Reference.log_append
Example::
@ -65,28 +78,33 @@ Branches
Branches inherit from References, and additionally provide specialized
accessors for some unique features.
.. automethod:: pygit2.Repository.listall_branches
.. automethod:: pygit2.Repository.lookup_branch
.. automethod:: pygit2.Repository.create_branch
.. autoclass:: pygit2.repository.Branches
:members:
:undoc-members:
:special-members: __getitem__, __iter__, __contains__
Example::
>>> local_branches = repo.listall_branches()
>>> # equivalent to
>>> local_branches = repo.listall_branches(pygit2.GIT_BRANCH_LOCAL)
>>> # Listing all branches
>>> branches_list = list(repo.branches)
>>> # Local only
>>> local_branches = list(repo.branches.local)
>>> # Remote only
>>> remote_branches = list(repo.branches.remote)
>>> remote_branches = repo.listall_branches(pygit2.GIT_BRANCH_REMOTE)
>>> # Get a branch
>>> branch = repo.branches['master']
>>> other_branch = repo.branches['does-not-exist'] # Will raise a KeyError
>>> other_branch = repo.branches.get('does-not-exist') # Returns None
>>> all_branches = repo.listall_branches(pygit2.GIT_BRANCH_REMOTE |
pygit2.GIT_BRANCH_LOCAL)
>>> remote_branch = repo.branches.remote['upstream/feature']
>>> master_branch = repo.lookup_branch('master')
>>> # equivalent to
>>> master_branch = repo.lookup_branch('master',
pygit2.GIT_BRANCH_LOCAL)
>>> # Create a local branch
>>> new_branch = repo.branches.local.create('new-branch')
>>> And delete it
>>> repo.branches.delete('new-branch')
>>> remote_branch = repo.lookup_branch('upstream/feature',
pygit2.GIT_BRANCH_REMOTE)
The Branch type
====================
@ -95,17 +113,19 @@ The Branch type
.. autoattribute:: pygit2.Branch.remote_name
.. autoattribute:: pygit2.Branch.upstream
.. autoattribute:: pygit2.Branch.upstream_name
.. automethod:: pygit2.Branch.rename
.. automethod:: pygit2.Branch.delete
.. automethod:: pygit2.Branch.is_head
.. automethod:: pygit2.Branch.is_checked_out
The reference log
====================
Example::
>>> head = repo.lookup_reference('refs/heads/master')
>>> head = repo.references.get('refs/heads/master') # Returns None if not found
>>> # Almost equivalent to
>>> head = repo.references['refs/heads/master'] # Raises KeyError if not found
>>> for entry in head.log():
... print(entry.message)

@ -2,10 +2,18 @@
Remotes
**********************************************************************
.. py:attribute:: Repository.remotes
The collection of configured remotes, an instance of
:py:class:`pygit2.remote.RemoteCollection`
.. autoattribute:: pygit2.Repository.remotes
.. automethod:: pygit2.Repository.create_remote
The remote collection
==========================
.. autoclass:: pygit2.remote.RemoteCollection
:members:
The Remote type
====================
@ -13,6 +21,12 @@ The Remote type
.. autoclass:: pygit2.Remote
:members:
The RemoteCallbacks type
========================
.. autoclass:: pygit2.RemoteCallbacks
:members:
The TransferProgress type
===========================
@ -24,14 +38,16 @@ This class contains the data which is available to us during a fetch.
The Refspec type
===================
Refspecs objects are not constructed directly, but returned by
:meth:`pygit2.Remote.get_refspec`. To create a new a refspec on a Remote, use
:meth:`pygit2.Remote.add_fetch` or :meth:`pygit2.Remote.add_push`.
.. autoclass:: pygit2.refspec.Refspec
:members:
Credentials
================
.. automethod:: pygit2.Remote.credentials
There are two types of credentials: username/password and SSH key
pairs. Both :py:class:`pygit2.UserPass` and :py:class:`pygit2.Keypair`
are callable objects, with the appropriate signature for the

@ -67,5 +67,11 @@ Below there are some general attributes and methods:
.. autoattribute:: pygit2.Repository.default_signature
.. automethod:: pygit2.Repository.read
.. automethod:: pygit2.Repository.write
.. automethod:: pygit2.Repository.ahead_behind
.. automethod:: pygit2.Repository.create_reference
.. automethod:: pygit2.Repository.describe
.. automethod:: pygit2.Repository.path_is_ignored
.. automethod:: pygit2.Repository.reset
.. automethod:: pygit2.Repository.revert_commit
.. automethod:: pygit2.Repository.state_cleanup
.. automethod:: pygit2.Repository.write_archive

21
docs/submodule.rst Normal file

@ -0,0 +1,21 @@
**********************************************************************
Submodules
**********************************************************************
A submodule is a foreign repository that is embedded within a
dedicated subdirectory of the repositories tree.
.. automethod:: pygit2.Repository.init_submodules
.. automethod:: pygit2.Repository.update_submodules
.. automethod:: pygit2.Repository.lookup_submodule
.. automethod:: pygit2.Repository.listall_submodules
The Submodule type
====================
.. automethod:: pygit2.Submodule.open
.. autoattribute:: pygit2.Submodule.name
.. autoattribute:: pygit2.Submodule.path
.. autoattribute:: pygit2.Submodule.url
.. autoattribute:: pygit2.Submodule.branch

@ -19,7 +19,7 @@ Iterate over all entries of the index::
Index write::
>>> index.add('path/to/file') # git add
>>> del index['path/to/file'] # git rm
>>> index.remove('path/to/file') # git rm
>>> index.write() # don't forget to save the changes
Custom entries::
@ -71,3 +71,11 @@ Lower level API:
.. automethod:: pygit2.Repository.checkout_head
.. automethod:: pygit2.Repository.checkout_tree
.. automethod:: pygit2.Repository.checkout_index
Stash
====================
.. automethod:: pygit2.Repository.stash
.. automethod:: pygit2.Repository.stash_apply
.. automethod:: pygit2.Repository.stash_drop
.. automethod:: pygit2.Repository.stash_pop

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -32,20 +32,48 @@ from __future__ import absolute_import
from _pygit2 import *
# High level API
from .blame import Blame, BlameHunk
from .config import Config
from .credentials import *
from .errors import check_error
from .errors import check_error, Passthrough
from .ffi import ffi, C
from .index import Index, IndexEntry
from .remote import Remote, get_credentials
from .remote import Remote, RemoteCallbacks, get_credentials
from .repository import Repository
from .settings import Settings
from .utils import to_bytes
from .version import __version__
from .submodule import Submodule
from .utils import to_bytes, to_str
from ._build import __version__
# Features
features = C.git_libgit2_features()
GIT_FEATURE_THREADS = C.GIT_FEATURE_THREADS
GIT_FEATURE_HTTPS = C.GIT_FEATURE_HTTPS
GIT_FEATURE_SSH = C.GIT_FEATURE_SSH
# GIT_REPOSITORY_INIT_*
GIT_REPOSITORY_INIT_OPTIONS_VERSION = C.GIT_REPOSITORY_INIT_OPTIONS_VERSION
GIT_REPOSITORY_INIT_BARE = C.GIT_REPOSITORY_INIT_BARE
GIT_REPOSITORY_INIT_NO_REINIT = C.GIT_REPOSITORY_INIT_NO_REINIT
GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = C.GIT_REPOSITORY_INIT_NO_DOTGIT_DIR
GIT_REPOSITORY_INIT_MKDIR = C.GIT_REPOSITORY_INIT_MKDIR
GIT_REPOSITORY_INIT_MKPATH = C.GIT_REPOSITORY_INIT_MKPATH
GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = C.GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE
GIT_REPOSITORY_INIT_RELATIVE_GITLINK = C.GIT_REPOSITORY_INIT_RELATIVE_GITLINK
GIT_REPOSITORY_INIT_SHARED_UMASK = C.GIT_REPOSITORY_INIT_SHARED_UMASK
GIT_REPOSITORY_INIT_SHARED_GROUP = C.GIT_REPOSITORY_INIT_SHARED_GROUP
GIT_REPOSITORY_INIT_SHARED_ALL = C.GIT_REPOSITORY_INIT_SHARED_ALL
# GIT_ATTR_CHECK_*
GIT_ATTR_CHECK_FILE_THEN_INDEX = C.GIT_ATTR_CHECK_FILE_THEN_INDEX
GIT_ATTR_CHECK_INDEX_THEN_FILE = C.GIT_ATTR_CHECK_INDEX_THEN_FILE
GIT_ATTR_CHECK_INDEX_ONLY = C.GIT_ATTR_CHECK_INDEX_ONLY
GIT_ATTR_CHECK_NO_SYSTEM = C.GIT_ATTR_CHECK_NO_SYSTEM
def init_repository(path, bare=False,
flags=C.GIT_REPOSITORY_INIT_MKPATH,
flags=GIT_REPOSITORY_INIT_MKPATH,
mode=0,
workdir_path=None,
description=None,
@ -77,19 +105,38 @@ def init_repository(path, bare=False,
See libgit2's documentation on git_repository_init_ext for further details.
"""
# Pre-process input parameters
if path is None:
raise TypeError('Expected string type for path, found None.')
if bare:
flags |= C.GIT_REPOSITORY_INIT_BARE
flags |= GIT_REPOSITORY_INIT_BARE
# Options
options = ffi.new('git_repository_init_options *')
options.version = 1
C.git_repository_init_init_options(options,
GIT_REPOSITORY_INIT_OPTIONS_VERSION)
options.flags = flags
options.mode = mode
options.workdir_path = to_bytes(workdir_path)
options.description = to_bytes(description)
options.template_path = to_bytes(template_path)
options.initial_head = to_bytes(initial_head)
options.origin_url = to_bytes(origin_url)
if workdir_path:
workdir_path_ref = ffi.new('char []', to_bytes(workdir_path))
options.workdir_path = workdir_path_ref
if description:
description_ref = ffi.new('char []', to_bytes(description))
options.description = description_ref
if template_path:
template_path_ref = ffi.new('char []', to_bytes(template_path))
options.template_path = template_path_ref
if initial_head:
initial_head_ref = ffi.new('char []', to_bytes(initial_head))
options.initial_head = initial_head_ref
if origin_url:
origin_url_ref = ffi.new('char []', to_bytes(origin_url))
options.origin_url = origin_url_ref
# Call
crepository = ffi.new('git_repository **')
@ -97,28 +144,41 @@ def init_repository(path, bare=False,
check_error(err)
# Ok
return Repository(path)
return Repository(to_str(path))
@ffi.callback('int (*credentials)(git_cred **cred, const char *url,'
'const char *username_from_url, unsigned int allowed_types,'
'void *data)')
def _credentials_cb(cred_out, url, username_from_url, allowed, data):
@ffi.callback('int (*git_repository_create_cb)(git_repository **out,'
'const char *path, int bare, void *payload)')
def _repository_create_cb(repo_out, path, bare, data):
d = ffi.from_handle(data)
try:
ccred = get_credentials(d['callback'], url, username_from_url, allowed)
cred_out[0] = ccred[0]
repository = d['repository_cb'](ffi.string(path), bare != 0)
# we no longer own the C object
repository._disown()
repo_out[0] = repository._repo
except Exception as e:
d['exception'] = e
return C.GIT_EUSER
return 0
@ffi.callback('int (*git_remote_create_cb)(git_remote **out, git_repository *repo,'
'const char *name, const char *url, void *payload)')
def _remote_create_cb(remote_out, repo, name, url, data):
d = ffi.from_handle(data)
try:
remote = d['remote_cb'](Repository._from_c(repo, False), ffi.string(name), ffi.string(url))
remote_out[0] = remote._remote
# we no longer own the C object
remote._remote = ffi.NULL
except Exception as e:
d['exception'] = e
return C.GIT_EUSER
return 0
def clone_repository(
url, path, bare=False, ignore_cert_errors=False,
remote_name="origin", checkout_branch=None, credentials=None):
url, path, bare=False, repository=None, remote=None,
checkout_branch=None, callbacks=None):
"""Clones a new Git repository from *url* in the given *path*.
Returns a Repository class pointing to the newly cloned repository.
@ -129,16 +189,29 @@ def clone_repository(
:param bool bare: Whether the local repository should be bare
:param str remote_name: Name to give the remote at *url*.
:param callable remote: Callback for the remote to use.
:param callable repository: Callback for the repository to use.
:param str checkout_branch: Branch to checkout after the
clone. The default is to use the remote's default branch.
:param callable credentials: authentication to use if the remote
requires it
:param RemoteCallbacks callbacks: object which implements the
callbacks as methods.
:rtype: Repository
The repository callback has `(path, bare) -> Repository` as a
signature. The Repository it returns will be used instead of
creating a new one.
The remote callback has `(Repository, name, url) -> Remote` as a
signature. The Remote it returns will be used instead of the default
one.
The callbacks should be an object which inherits from
`pyclass:RemoteCallbacks`.
"""
opts = ffi.new('git_clone_options *')
@ -148,60 +221,42 @@ def clone_repository(
# Data, let's use a dict as we don't really want much more
d = {}
d['callback'] = credentials
d['repository_cb'] = repository
d['remote_cb'] = remote
d_handle = ffi.new_handle(d)
# Perform the initialization with the version we compiled
C.git_clone_init_options(opts, C.GIT_CLONE_OPTIONS_VERSION)
# We need to keep the ref alive ourselves
checkout_branch_ref = None
if branch:
checkout_branch_ref = ffi.new('char []', branch)
checkout_branch_ref = ffi.new('char []', to_bytes(branch))
opts.checkout_branch = checkout_branch_ref
remote_name_ref = ffi.new('char []', to_bytes(remote_name))
opts.remote_name = remote_name_ref
if repository:
opts.repository_cb = _repository_create_cb
opts.repository_cb_payload = d_handle
if remote:
opts.remote_cb = _remote_create_cb
opts.remote_cb_payload = d_handle
opts.version = 1
opts.ignore_cert_errors = ignore_cert_errors
opts.bare = bare
opts.remote_callbacks.version = 1
opts.checkout_opts.version = 1
if credentials:
opts.remote_callbacks.credentials = _credentials_cb
opts.remote_callbacks.payload = d_handle
if callbacks is None:
callbacks = RemoteCallbacks()
callbacks._fill_fetch_options(opts.fetch_opts)
err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts)
C.git_repository_free(crepo[0])
if 'exception' in d:
raise d['exception']
check_error(err)
return Repository(path)
def clone_into(repo, remote, branch=None):
"""Clone into an empty repository from the specified remote
:param Repository repo: The empty repository into which to clone
:param Remote remote: The remote from which to clone
:param str branch: Branch to checkout after the clone. Pass None
to use the remotes's default branch.
This allows you specify arbitrary repository and remote configurations
before performing the clone step itself. E.g. you can replicate git-clone's
'--mirror' option by setting a refspec of '+refs/*:refs/*', 'core.mirror'
to true and calling this function.
"""
err = C.git_clone_into(repo._repo, remote._remote, ffi.NULL,
to_bytes(branch), ffi.NULL)
if remote._stored_exception:
raise remote._stored_exception
check_error(err)
return Repository._from_c(crepo[0], owned=True)
settings = Settings()

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -25,20 +25,40 @@
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
"""Tests for reference log."""
"""
This is an special module, it provides stuff used by setup.py at build time.
But also used by pygit2 at run time.
"""
from __future__ import absolute_import
from __future__ import unicode_literals
# Import from the Standard Library
import os
from os import getenv
from pygit2 import Signature
from . import utils
#
# The version number of pygit2
#
__version__ = '0.26.0'
class ReflogTest(utils.RepoTestCase):
#
# Utility functions to get the paths required for bulding extensions
#
def _get_libgit2_path():
# LIBGIT2 environment variable takes precedence
libgit2_path = getenv("LIBGIT2")
if libgit2_path is not None:
return libgit2_path
def test_log_append(self):
repo = self.repo
master = repo.lookup_reference("refs/heads/master")
signature = Signature('xtao', 'xutao@douban.com')
master.log_append(None, signature, 'reflog')
self.assertTrue('reflog' in [entry.message for entry in master.log()])
# Default
if os.name == 'nt':
return '%s\libgit2' % getenv("ProgramFiles")
return '/usr/local'
def get_libgit2_paths():
libgit2_path = _get_libgit2_path()
return (
os.path.join(libgit2_path, 'bin'),
os.path.join(libgit2_path, 'include'),
getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')),
)

76
pygit2/_run.py Normal file

@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
"""
This is an special module, it provides stuff used by by pygit2 at run-time.
"""
# Import from the Standard Library
import codecs
import os
from os.path import abspath, dirname
import sys
# Import from cffi
from cffi import FFI
# Import from pygit2
from _build import get_libgit2_paths
# C_HEADER_SRC
if getattr(sys, 'frozen', False):
dir_path = getattr(sys, '_MEIPASS', None)
if dir_path is None:
dir_path = dirname(abspath(sys.executable))
else:
dir_path = dirname(abspath(__file__))
decl_path = os.path.join(dir_path, 'decl.h')
with codecs.open(decl_path, 'r', 'utf-8') as header:
C_HEADER_SRC = header.read()
# C_KEYWORDS
libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths()
C_KEYWORDS = dict(libraries=['git2'],
library_dirs=[libgit2_lib],
include_dirs=[libgit2_include])
# preamble
preamble = "#include <git2.h>"
# ffi
ffi = FFI()
set_source = getattr(ffi, 'set_source', None)
if set_source is not None:
set_source("pygit2._libgit2", preamble, **C_KEYWORDS)
ffi.cdef(C_HEADER_SRC)
if __name__ == '__main__':
ffi.compile()

146
pygit2/blame.py Normal file

@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from the future
from __future__ import absolute_import, unicode_literals
# Import from pygit2
from .errors import check_error
from .ffi import ffi, C
from .utils import to_bytes, is_string, to_str
from .utils import GenericIterator
from _pygit2 import Signature, Oid
def wrap_signature(csig):
if not csig:
return None
return Signature(ffi.string(csig.name).decode('utf-8'),
ffi.string(csig.email).decode('utf-8'),
csig.when.time, csig.when.offset, 'utf-8')
class BlameHunk(object):
@classmethod
def _from_c(cls, blame, ptr):
hunk = cls.__new__(cls)
hunk._blame = blame
hunk._hunk = ptr
return hunk
@property
def lines_in_hunk(self):
"""Number of lines"""
return self._hunk.lines_in_hunk
@property
def boundary(self):
"""Tracked to a boundary commit"""
# Casting directly to bool via cffi does not seem to work
return int(ffi.cast('int', self._hunk.boundary)) != 0
@property
def final_start_line_number(self):
"""Final start line number"""
return self._hunk.final_start_line_number
@property
def final_committer(self):
"""Final committer"""
return wrap_signature(self._hunk.final_signature)
@property
def final_commit_id(self):
return Oid(raw=bytes(ffi.buffer(ffi.addressof(self._hunk, 'final_commit_id'))[:]))
@property
def orig_start_line_number(self):
"""Origin start line number"""
return self._hunk.orig_start_line_number
@property
def orig_committer(self):
"""Original committer"""
return wrap_signature(self._hunk.orig_signature)
@property
def orig_commit_id(self):
return Oid(raw=bytes(ffi.buffer(ffi.addressof(self._hunk, 'orig_commit_id'))[:]))
@property
def orig_path(self):
"""Original path"""
path = self._hunk.orig_path
if not path:
return None
return ffi.string(path).decode()
class Blame(object):
@classmethod
def _from_c(cls, repo, ptr):
blame = cls.__new__(cls)
blame._repo = repo
blame._blame = ptr
return blame
def __del__(self):
C.git_blame_free(self._blame)
def __len__(self):
return C.git_blame_get_hunk_count(self._blame)
def __getitem__(self, index):
chunk = C.git_blame_get_hunk_byindex(self._blame, index)
if not chunk:
raise IndexError
return BlameHunk._from_c(self, chunk)
def for_line(self, line_no):
"""Returns the <BlameHunk> object for a given line given its number
in the current Blame.
Arguments:
line_no
Line number, starts at 1.
"""
if line_no < 0:
raise IndexError
chunk = C.git_blame_get_hunk_byline(self._blame, line_no)
if not chunk:
raise IndexError
return BlameHunk._from_c(self, chunk)
def __iter__(self):
return GenericIterator(self)

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -63,15 +63,12 @@ class ConfigIterator(object):
def __next__(self):
entry = self._next_entry()
name = ffi.string(entry.name).decode('utf-8')
return name
return ffi.string(entry.name).decode('utf-8')
class ConfigMultivarIterator(ConfigIterator):
def __next__(self):
entry = self._next_entry()
return ffi.string(entry.value).decode('utf-8')
@ -104,19 +101,19 @@ class Config(object):
def _get(self, key):
assert_string(key, "key")
cstr = ffi.new('char **')
err = C.git_config_get_string(cstr, self._config, to_bytes(key))
entry = ffi.new('git_config_entry **')
err = C.git_config_get_entry(entry, self._config, to_bytes(key))
return err, cstr
return err, ConfigEntry._from_c(entry[0])
def _get_string(self, key):
err, cstr = self._get(key)
def _get_entry(self, key):
err, entry = self._get(key)
if err == C.GIT_ENOTFOUND:
raise KeyError(key)
check_error(err)
return cstr[0]
return entry
def __contains__(self, key):
err, cstr = self._get(key)
@ -129,9 +126,9 @@ class Config(object):
return True
def __getitem__(self, key):
val = self._get_string(key)
entry = self._get_entry(key)
return ffi.string(val).decode('utf-8')
return ffi.string(entry.value).decode('utf-8')
def __setitem__(self, key, value):
assert_string(key, "key")
@ -161,12 +158,11 @@ class Config(object):
return ConfigIterator(self, citer[0])
def get_multivar(self, name, regex=None):
"""get_multivar(name[, regex]) -> [str, ...]
Get each value of a multivar ''name'' as a list. The optional ''regex''
parameter is expected to be a regular expression to filter the
variables we're interested in."""
"""Get each value of a multivar ''name'' as a list of strings.
The optional ''regex'' parameter is expected to be a regular expression
to filter the variables we're interested in.
"""
assert_string(name, "name")
citer = ffi.new('git_config_iterator **')
@ -178,11 +174,9 @@ class Config(object):
return ConfigMultivarIterator(self, citer[0])
def set_multivar(self, name, regex, value):
"""set_multivar(name, regex, value)
Set a multivar ''name'' to ''value''. ''regexp'' is a regular
expression to indicate which values to replace"""
"""Set a multivar ''name'' to ''value''. ''regexp'' is a regular
expression to indicate which values to replace.
"""
assert_string(name, "name")
assert_string(regex, "regex")
assert_string(value, "value")
@ -192,53 +186,48 @@ class Config(object):
check_error(err)
def get_bool(self, key):
"""get_bool(key) -> Bool
Look up *key* and parse its value as a boolean as per the git-config
rules
"""Look up *key* and parse its value as a boolean as per the git-config
rules. Return a boolean value (True or False).
Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',
0, 'off' and 'no'"""
0, 'off' and 'no'
"""
val = self._get_string(key)
entry = self._get_entry(key)
res = ffi.new('int *')
err = C.git_config_parse_bool(res, val)
err = C.git_config_parse_bool(res, entry.value)
check_error(err)
return res[0] != 0
def get_int(self, key):
"""get_int(key) -> int
Look up *key* and parse its value as an integer as per the git-config
rules.
"""Look up *key* and parse its value as an integer as per the git-config
rules. Return an integer.
A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo',
'mega' and 'giga' respectively"""
'mega' and 'giga' respectively.
"""
val = self._get_string(key)
entry = self._get_entry(key)
res = ffi.new('int64_t *')
err = C.git_config_parse_int64(res, val)
err = C.git_config_parse_int64(res, entry.value)
check_error(err)
return res[0]
def add_file(self, path, level=0, force=0):
"""add_file(path, level=0, force=0)
Add a config file instance to an existing config."""
"""Add a config file instance to an existing config."""
err = C.git_config_add_file_ondisk(self._config, to_bytes(path), level,
force)
check_error(err)
def snapshot(self):
"""Create a snapshot from this Config object
"""Create a snapshot from this Config object.
This means that looking up multiple values will use the same version
of the configuration files
of the configuration files.
"""
ccfg = ffi.new('git_config **')
err = C.git_config_snapshot(ccfg, self._config)
check_error(err)
@ -281,24 +270,35 @@ class Config(object):
@staticmethod
def get_system_config():
"""get_system_config() -> Config
Return an object representing the system configuration file."""
"""Return a <Config> object representing the system configuration file.
"""
return Config._from_found_config(C.git_config_find_system)
@staticmethod
def get_global_config():
"""get_global_config() -> Config
Return an object representing the global configuration file."""
"""Return a <Config> object representing the global configuration file.
"""
return Config._from_found_config(C.git_config_find_global)
@staticmethod
def get_xdg_config():
"""get_xdg_config() -> Config
Return an object representing the global configuration file."""
"""Return a <Config> object representing the global configuration file.
"""
return Config._from_found_config(C.git_config_find_xdg)
class ConfigEntry(object):
"""An entry in a configuation object
"""
@classmethod
def _from_c(cls, ptr):
entry = cls.__new__(cls)
entry._entry = ptr
return entry
def __del__(self):
C.git_config_entry_free(self._entry)
@property
def value(self):
return self._entry.value

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -36,7 +36,6 @@ class UserPass(object):
This is an object suitable for passing to a remote's credentials
callback and for returning from said callback.
"""
def __init__(self, username, password):
@ -61,6 +60,12 @@ class Keypair(object):
This is an object suitable for passing to a remote's credentials
callback and for returning from said callback.
:param str username: the username being used to authenticate with the
remote server
:param str pubkey: the path to the user's public key file
:param str privkey: the path to the user's private key file
:param str passphrase: the password used to decrypt the private key file,
or empty string if no passphrase is required.
"""
def __init__(self, username, pubkey, privkey, passphrase):
@ -79,3 +84,8 @@ class Keypair(object):
def __call__(self, _url, _username, _allowed):
return self
class KeypairFromAgent(Keypair):
def __init__(self, username):
super(KeypairFromAgent, self).__init__(username, None, None, None)

@ -1,11 +1,12 @@
typedef ... git_repository;
typedef ... git_submodule;
typedef ... git_remote;
typedef ... git_transport;
typedef ... git_refspec;
typedef ... git_push;
typedef ... git_cred;
typedef ... git_object;
typedef ... git_tree;
typedef ... git_signature;
typedef ... git_commit;
typedef ... git_index;
typedef ... git_diff;
typedef ... git_index_conflict_iterator;
@ -29,6 +30,14 @@ typedef struct git_strarray {
} git_strarray;
typedef int64_t git_off_t;
typedef int64_t git_time_t;
typedef enum {
GIT_REF_INVALID = 0,
GIT_REF_OID = 1,
GIT_REF_SYMBOLIC = 2,
GIT_REF_LISTALL = 3,
} git_ref_t;
typedef enum {
GIT_OK = 0,
@ -43,8 +52,18 @@ typedef enum {
GIT_EUNMERGED = -10,
GIT_ENONFASTFORWARD = -11,
GIT_EINVALIDSPEC = -12,
GIT_EMERGECONFLICT = -13,
GIT_ECONFLICT = -13,
GIT_ELOCKED = -14,
GIT_EMODIFIED = -15,
GIT_EAUTH = -16,
GIT_ECERTIFICATE = -17,
GIT_EAPPLIED = -18,
GIT_EPEEL = -19,
GIT_EEOF = -20,
GIT_EINVALID = -21,
GIT_EUNCOMMITTED = -22,
GIT_EDIRECTORY = -23,
GIT_EMERGECONFLICT = -24,
GIT_PASSTHROUGH = -30,
GIT_ITEROVER = -31,
@ -55,6 +74,23 @@ typedef struct {
int klass;
} git_error;
typedef struct git_time {
git_time_t time;
int offset;
} git_time;
typedef struct git_signature {
char *name;
char *email;
git_time when;
} git_signature;
#define GIT_FEATURE_THREADS ...
#define GIT_FEATURE_HTTPS ...
#define GIT_FEATURE_SSH ...
int git_libgit2_features(void);
const git_error * giterr_last(void);
void git_strarray_free(git_strarray *array);
@ -87,9 +123,39 @@ typedef enum {
GIT_CREDTYPE_SSH_KEY,
GIT_CREDTYPE_SSH_CUSTOM,
GIT_CREDTYPE_DEFAULT,
GIT_CREDTYPE_SSH_INTERACTIVE,
GIT_CREDTYPE_USERNAME,
...
} git_credtype_t;
typedef enum git_cert_t {
GIT_CERT_NONE,
GIT_CERT_X509,
GIT_CERT_HOSTKEY_LIBSSH2,
} git_cert_t;
typedef enum {
GIT_CERT_SSH_MD5 = 1,
GIT_CERT_SSH_SHA1 = 2,
} git_cert_ssh_t;
typedef struct {
git_cert_t cert_type;
} git_cert;
typedef struct {
git_cert parent;
git_cert_ssh_t type;
unsigned char hash_md5[16];
unsigned char hash_sha1[20];
} git_cert_hostkey;
typedef struct {
git_cert parent;
void *data;
size_t len;
} git_cert_x509;
typedef int (*git_transport_message_cb)(const char *str, int len, void *data);
typedef int (*git_cred_acquire_cb)(
git_cred **cred,
@ -98,70 +164,136 @@ typedef int (*git_cred_acquire_cb)(
unsigned int allowed_types,
void *payload);
typedef int (*git_transfer_progress_cb)(const git_transfer_progress *stats, void *payload);
typedef int (*git_transport_certificate_check_cb)(git_cert *cert, int valid, const char *host, void *payload);
typedef int (*git_packbuilder_progress)(
int stage,
unsigned int current,
unsigned int total,
void *payload);
typedef int (*git_push_transfer_progress)(
unsigned int current,
unsigned int total,
size_t bytes,
void* payload);
typedef struct {
char *src_refname;
char *dst_refname;
git_oid src;
git_oid dst;
} git_push_update;
typedef int (*git_push_negotiation)(const git_push_update **updates, size_t len, void *payload);
typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param);
struct git_remote_callbacks {
unsigned int version;
git_transport_message_cb sideband_progress;
int (*completion)(git_remote_completion_type type, void *data);
git_cred_acquire_cb credentials;
git_transport_certificate_check_cb certificate_check;
git_transfer_progress_cb transfer_progress;
int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
git_packbuilder_progress pack_progress;
git_push_transfer_progress push_transfer_progress;
int (*push_update_reference)(const char *refname, const char *status, void *data);
git_push_negotiation push_negotiation;
git_transport_cb transport;
void *payload;
};
#define GIT_REMOTE_CALLBACKS_VERSION ...
typedef struct git_remote_callbacks git_remote_callbacks;
typedef enum {
GIT_PROXY_NONE,
GIT_PROXY_AUTO,
GIT_PROXY_SPECIFIED,
} git_proxy_t;
typedef struct {
unsigned int version;
git_proxy_t type;
const char *url;
git_cred_acquire_cb credentials;
git_transport_certificate_check_cb certificate_check;
void *payload;
} git_proxy_options;
#define GIT_PROXY_OPTIONS_VERSION ...
int git_proxy_init_options(git_proxy_options *opts, unsigned int version);
typedef struct {
unsigned int version;
unsigned int pb_parallelism;
git_remote_callbacks callbacks;
git_proxy_options proxy_opts;
git_strarray custom_headers;
} git_push_options;
#define GIT_PUSH_OPTIONS_VERSION ...
int git_push_init_options(git_push_options *opts, unsigned int version);
typedef enum {
GIT_FETCH_PRUNE_UNSPECIFIED,
GIT_FETCH_PRUNE,
GIT_FETCH_NO_PRUNE,
} git_fetch_prune_t;
typedef enum {
GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED = 0,
GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
GIT_REMOTE_DOWNLOAD_TAGS_NONE,
GIT_REMOTE_DOWNLOAD_TAGS_ALL,
} git_remote_autotag_option_t;
typedef struct {
int version;
git_remote_callbacks callbacks;
git_fetch_prune_t prune;
int update_fetchhead;
git_remote_autotag_option_t download_tags;
git_proxy_options proxy_opts;
git_strarray custom_headers;
} git_fetch_options;
#define GIT_FETCH_OPTIONS_VERSION ...
int git_fetch_init_options(git_fetch_options *opts, unsigned int version);
int git_remote_list(git_strarray *out, git_repository *repo);
int git_remote_load(git_remote **out, git_repository *repo, const char *name);
int git_remote_lookup(git_remote **out, git_repository *repo, const char *name);
int git_remote_create(
git_remote **out,
git_repository *repo,
const char *name,
const char *url);
int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch);
int git_remote_delete(git_repository *repo, const char *name);
int git_repository_state_cleanup(git_repository *repo);
const char * git_remote_name(const git_remote *remote);
int git_remote_rename(
git_strarray *problems,
git_remote *remote,
const char *new_name);
int git_remote_rename(git_strarray *problems, git_repository *repo, const char *name, const char *new_name);
const char * git_remote_url(const git_remote *remote);
int git_remote_set_url(git_remote *remote, const char* url);
int git_remote_set_url(git_repository *repo, const char *remote, const char* url);
const char * git_remote_pushurl(const git_remote *remote);
int git_remote_set_pushurl(git_remote *remote, const char* url);
int git_remote_fetch(git_remote *remote, const git_signature *signature, const char *reflog_message);
int git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url);
int git_remote_fetch(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts, const char *reflog_message);
int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts);
const git_transfer_progress * git_remote_stats(git_remote *remote);
int git_remote_add_push(git_remote *remote, const char *refspec);
int git_remote_add_fetch(git_remote *remote, const char *refspec);
int git_remote_save(const git_remote *remote);
int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec);
int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec);
int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version);
size_t git_remote_refspec_count(git_remote *remote);
const git_refspec * git_remote_get_refspec(git_remote *remote, size_t n);
int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array);
int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array);
void git_remote_free(git_remote *remote);
int git_push_new(git_push **push, git_remote *remote);
int git_push_add_refspec(git_push *push, const char *refspec);
int git_push_finish(git_push *push);
int git_push_unpack_ok(git_push *push);
int git_push_status_foreach(
git_push *push,
int (*cb)(const char *ref, const char *msg, void *data),
void *data);
int git_push_update_tips(
git_push *push,
const git_signature *signature,
const char *reflog_message);
void git_push_free(git_push *push);
const char * git_refspec_src(const git_refspec *refspec);
const char * git_refspec_dst(const git_refspec *refspec);
int git_refspec_force(const git_refspec *refspec);
@ -184,19 +316,21 @@ int git_cred_ssh_key_new(
const char *publickey,
const char *privatekey,
const char *passphrase);
int git_cred_ssh_key_from_agent(
git_cred **out,
const char *username);
/*
* git_diff
*/
typedef enum {
GIT_SUBMODULE_IGNORE_RESET = -1,
GIT_SUBMODULE_IGNORE_UNSPECIFIED = -1,
GIT_SUBMODULE_IGNORE_NONE = 1,
GIT_SUBMODULE_IGNORE_UNTRACKED = 2,
GIT_SUBMODULE_IGNORE_DIRTY = 3,
GIT_SUBMODULE_IGNORE_ALL = 4,
GIT_SUBMODULE_IGNORE_DEFAULT = 0
} git_submodule_ignore_t;
typedef enum {
@ -234,23 +368,40 @@ typedef int (*git_diff_notify_cb)(
const char *matched_pathspec,
void *payload);
typedef int (*git_diff_progress_cb)(
const git_diff *diff_so_far,
const char *old_path,
const char *new_path,
void *payload);
typedef struct {
unsigned int version;
uint32_t flags;
git_submodule_ignore_t ignore_submodules;
git_strarray pathspec;
git_diff_notify_cb notify_cb;
void *notify_payload;
uint16_t context_lines;
uint16_t interhunk_lines;
git_diff_notify_cb notify_cb;
git_diff_progress_cb progress_cb;
void *payload;
uint32_t context_lines;
uint32_t interhunk_lines;
uint16_t id_abbrev;
git_off_t max_size;
const char *old_prefix;
const char *new_prefix;
} git_diff_options;
typedef struct {
int (*file_signature)(
void **out, const git_diff_file *file,
const char *fullpath, void *payload);
int (*buffer_signature)(
void **out, const git_diff_file *file,
const char *buf, size_t buflen, void *payload);
void (*free_signature)(void *sig, void *payload);
int (*similarity)(int *score, void *siga, void *sigb, void *payload);
void *payload;
} git_diff_similarity_metric;
int git_diff_init_options(git_diff_options *opts, unsigned int version);
int git_diff_index_to_workdir(git_diff **diff, git_repository *repo, git_index *index, const git_diff_options *opts);
int git_diff_tree_to_index(git_diff **diff, git_repository *repo, git_tree *old_tree, git_index *index, const git_diff_options *opts);
@ -259,7 +410,16 @@ int git_diff_tree_to_index(git_diff **diff, git_repository *repo, git_tree *old_
* git_checkout
*/
typedef enum { ... } git_checkout_notify_t;
typedef enum {
GIT_CHECKOUT_NOTIFY_NONE = 0,
GIT_CHECKOUT_NOTIFY_CONFLICT = 1,
GIT_CHECKOUT_NOTIFY_DIRTY = 2,
GIT_CHECKOUT_NOTIFY_UPDATED = 4,
GIT_CHECKOUT_NOTIFY_UNTRACKED = 8,
GIT_CHECKOUT_NOTIFY_IGNORED = 16,
GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFF
} git_checkout_notify_t;
typedef int (*git_checkout_notify_cb)(
git_checkout_notify_t why,
@ -275,41 +435,39 @@ typedef void (*git_checkout_progress_cb)(
size_t total_steps,
void *payload);
typedef struct {
size_t mkdir_calls;
size_t stat_calls;
size_t chmod_calls;
} git_checkout_perfdata;
typedef void (*git_checkout_perfdata_cb)(
const git_checkout_perfdata *perfdata,
void *payload);
typedef struct git_checkout_options {
unsigned int version;
unsigned int checkout_strategy;
int disable_filters;
unsigned int dir_mode;
unsigned int file_mode;
int file_open_flags;
unsigned int notify_flags;
git_checkout_notify_cb notify_cb;
void *notify_payload;
git_checkout_progress_cb progress_cb;
void *progress_payload;
git_strarray paths;
git_tree *baseline;
git_index *baseline_index;
const char *target_directory;
const char *ancestor_label;
const char *our_label;
const char *their_label;
git_checkout_perfdata_cb perfdata_cb;
void *perfdata_payload;
} git_checkout_options;
typedef enum {
GIT_CLONE_LOCAL_AUTO,
GIT_CLONE_LOCAL,
GIT_CLONE_NO_LOCAL,
GIT_CLONE_LOCAL_NO_LINKS,
} git_clone_local_t;
int git_checkout_init_options(git_checkout_options *opts, unsigned int version);
int git_checkout_tree(git_repository *repo, const git_object *treeish, const git_checkout_options *opts);
int git_checkout_head(git_repository *repo, const git_checkout_options *opts);
@ -319,32 +477,47 @@ int git_checkout_index(git_repository *repo, git_index *index, const git_checkou
* git_clone
*/
typedef int (*git_remote_create_cb)(
git_remote **out,
git_repository *repo,
const char *name,
const char *url,
void *payload);
typedef int (*git_repository_create_cb)(
git_repository **out,
const char *path,
int bare,
void *payload);
typedef enum {
GIT_CLONE_LOCAL_AUTO,
GIT_CLONE_LOCAL,
GIT_CLONE_NO_LOCAL,
GIT_CLONE_LOCAL_NO_LINKS,
} git_clone_local_t;
typedef struct git_clone_options {
unsigned int version;
git_checkout_options checkout_opts;
git_remote_callbacks remote_callbacks;
git_fetch_options fetch_opts;
int bare;
int ignore_cert_errors;
git_clone_local_t local;
const char *remote_name;
const char* checkout_branch;
git_signature *signature;
git_repository_create_cb repository_cb;
void *repository_cb_payload;
git_remote_create_cb remote_cb;
void *remote_cb_payload;
} git_clone_options;
#define GIT_CLONE_OPTIONS_VERSION ...
int git_clone_init_options(git_clone_options *opts, unsigned int version);
int git_clone(git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *options);
int git_clone_into(
git_repository *repo,
git_remote *remote,
const git_checkout_options *co_opts,
const char *branch,
const git_signature *signature);
/*
* git_config
*/
@ -353,24 +526,30 @@ typedef ... git_config;
typedef ... git_config_iterator;
typedef enum {
GIT_CONFIG_LEVEL_SYSTEM = 1,
GIT_CONFIG_LEVEL_XDG = 2,
GIT_CONFIG_LEVEL_GLOBAL = 3,
GIT_CONFIG_LEVEL_LOCAL = 4,
GIT_CONFIG_LEVEL_APP = 5,
GIT_CONFIG_LEVEL_PROGRAMDATA = 1,
GIT_CONFIG_LEVEL_SYSTEM = 2,
GIT_CONFIG_LEVEL_XDG = 3,
GIT_CONFIG_LEVEL_GLOBAL = 4,
GIT_CONFIG_LEVEL_LOCAL = 5,
GIT_CONFIG_LEVEL_APP = 6,
GIT_CONFIG_HIGHEST_LEVEL = -1,
} git_config_level_t;
typedef struct {
typedef struct git_config_entry {
const char *name;
const char *value;
git_config_level_t level;
void (*free)(struct git_config_entry *entry);
void *payload;
} git_config_entry;
void git_config_entry_free(git_config_entry *);
int git_repository_config(git_config **out, git_repository *repo);
int git_repository_config_snapshot(git_config **out, git_repository *repo);
void git_config_free(git_config *cfg);
int git_config_get_entry(git_config_entry **out, const git_config *cfg, const char *name);
int git_config_get_string(const char **out, const git_config *cfg, const char *name);
int git_config_set_string(git_config *cfg, const char *name, const char *value);
int git_config_set_bool(git_config *cfg, const char *name, int value);
@ -418,6 +597,7 @@ typedef enum {
GIT_REPOSITORY_INIT_MKDIR,
GIT_REPOSITORY_INIT_MKPATH,
GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE,
GIT_REPOSITORY_INIT_RELATIVE_GITLINK,
...
} git_repository_init_flag_t;
@ -439,6 +619,9 @@ typedef struct {
const char *origin_url;
} git_repository_init_options;
#define GIT_REPOSITORY_INIT_OPTIONS_VERSION ...
int git_repository_init_init_options(git_repository_init_options *opts, int version);
int git_repository_init(
git_repository **out,
const char *path,
@ -449,31 +632,49 @@ int git_repository_init_ext(
const char *repo_path,
git_repository_init_options *opts);
int git_repository_set_head(git_repository *repo, const char *refname);
int git_repository_set_head_detached(git_repository *repo, const git_oid *commitish);
int git_repository_ident(const char **name, const char **email, const git_repository *repo);
int git_repository_set_ident(git_repository *repo, const char *name, const char *email);
int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream);
/*
* git_submodule
*/
int git_submodule_lookup(git_submodule **out, git_repository *repo, char *path);
void git_submodule_free(git_submodule *subm);
int git_submodule_open(git_repository **out, git_submodule *subm);
const char *git_submodule_name(git_submodule *subm);
const char *git_submodule_path(git_submodule *subm);
const char *git_submodule_url(git_submodule *subm);
const char *git_submodule_branch(git_submodule *subm);
/*
* git_index
*/
typedef int64_t git_time_t;
typedef struct {
git_time_t seconds;
unsigned int nanoseconds;
int32_t seconds;
uint32_t nanoseconds;
} git_index_time;
typedef struct git_index_entry {
git_index_time ctime;
git_index_time mtime;
unsigned int dev;
unsigned int ino;
unsigned int mode;
unsigned int uid;
unsigned int gid;
git_off_t file_size;
uint32_t dev;
uint32_t ino;
uint32_t mode;
uint32_t uid;
uint32_t gid;
uint32_t file_size;
git_oid id;
unsigned short flags;
unsigned short flags_extended;
uint16_t flags;
uint16_t flags_extended;
const char *path;
} git_index_entry;
@ -505,3 +706,221 @@ int git_index_conflict_iterator_new(git_index_conflict_iterator **iterator_out,
int git_index_conflict_get(const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index *index, const char *path);
int git_index_conflict_next(const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index_conflict_iterator *iterator);
int git_index_conflict_remove(git_index *index, const char *path);
/*
* git_blame
*/
typedef ... git_blame;
typedef struct git_blame_options {
unsigned int version;
uint32_t flags;
uint16_t min_match_characters;
git_oid newest_commit;
git_oid oldest_commit;
size_t min_line;
size_t max_line;
} git_blame_options;
#define GIT_BLAME_OPTIONS_VERSION ...
typedef struct git_blame_hunk {
size_t lines_in_hunk;
git_oid final_commit_id;
size_t final_start_line_number;
git_signature *final_signature;
git_oid orig_commit_id;
const char *orig_path;
size_t orig_start_line_number;
git_signature *orig_signature;
char boundary;
} git_blame_hunk;
int git_blame_init_options(git_blame_options *opts, unsigned int version);
uint32_t git_blame_get_hunk_count(git_blame *blame);
const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t index);
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno);
int git_blame_file(git_blame **out, git_repository *repo, const char *path, git_blame_options *options);
void git_blame_free(git_blame *blame);
/*
* Merging
*/
typedef enum {
GIT_MERGE_FIND_RENAMES = 1,
GIT_MERGE_FAIL_ON_CONFLICT = 2,
GIT_MERGE_SKIP_REUC = 4,
GIT_MERGE_NO_RECURSIVE = 8,
} git_merge_flag_t;
typedef enum {
GIT_MERGE_FILE_FAVOR_NORMAL = 0,
GIT_MERGE_FILE_FAVOR_OURS = 1,
GIT_MERGE_FILE_FAVOR_THEIRS = 2,
GIT_MERGE_FILE_FAVOR_UNION = 3,
} git_merge_file_favor_t;
typedef struct {
unsigned int version;
git_merge_flag_t flags;
unsigned int rename_threshold;
unsigned int target_limit;
git_diff_similarity_metric *metric;
unsigned int recursion_limit;
const char *default_driver;
git_merge_file_favor_t file_favor;
unsigned int file_flags;
} git_merge_options;
#define GIT_MERGE_OPTIONS_VERSION 1
typedef struct {
unsigned int automergeable;
const char *path;
unsigned int mode;
const char *ptr;
size_t len;
} git_merge_file_result;
typedef enum {
GIT_MERGE_FILE_DEFAULT = 0,
GIT_MERGE_FILE_STYLE_MERGE = 1,
GIT_MERGE_FILE_STYLE_DIFF3 = 2,
GIT_MERGE_FILE_SIMPLIFY_ALNUM = 4,
GIT_MERGE_FILE_IGNORE_WHITESPACE = 8,
GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE = 16,
GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL = 32,
GIT_MERGE_FILE_DIFF_PATIENCE = 64,
GIT_MERGE_FILE_DIFF_MINIMAL = 128,
} git_merge_file_flag_t;
typedef struct {
unsigned int version;
const char *ancestor_label;
const char *our_label;
const char *their_label;
git_merge_file_favor_t favor;
git_merge_file_flag_t flags;
} git_merge_file_options;
#define GIT_MERGE_OPTIONS_VERSION ...
int git_merge_init_options(git_merge_options *opts, unsigned int version);
int git_merge_commits(git_index **out, git_repository *repo, const git_commit *our_commit, const git_commit *their_commit, const git_merge_options *opts);
int git_merge_trees(git_index **out, git_repository *repo, const git_tree *ancestor_tree, const git_tree *our_tree, const git_tree *their_tree, const git_merge_options *opts);
int git_merge_file_from_index(git_merge_file_result *out, git_repository *repo, const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *opts);
void git_merge_file_result_free(git_merge_file_result *result);
/*
* git_stash
*/
typedef int (*git_stash_cb)(
size_t index, const char* message, const git_oid *stash_id, void *payload);
typedef enum {
GIT_STASH_APPLY_PROGRESS_NONE = 0,
GIT_STASH_APPLY_PROGRESS_LOADING_STASH = 1,
GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX = 2,
GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED = 3,
GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED = 4,
GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED = 5,
GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED = 6,
GIT_STASH_APPLY_PROGRESS_DONE = 7,
} git_stash_apply_progress_t;
typedef int (*git_stash_apply_progress_cb)(
git_stash_apply_progress_t progress, void *payload);
typedef enum {
GIT_STASH_DEFAULT = 0,
GIT_STASH_KEEP_INDEX = 1,
GIT_STASH_INCLUDE_UNTRACKED = 2,
GIT_STASH_INCLUDE_IGNORED = 4,
} git_stash_flags;
typedef enum {
GIT_STASH_APPLY_DEFAULT = 0,
GIT_STASH_APPLY_REINSTATE_INDEX = 1,
} git_stash_apply_flags;
typedef struct git_stash_apply_options {
unsigned int version;
git_stash_apply_flags flags;
git_checkout_options checkout_options;
git_stash_apply_progress_cb progress_cb;
void *progress_payload;
} git_stash_apply_options;
#define GIT_STASH_APPLY_OPTIONS_VERSION ...
int git_stash_save(git_oid *out, git_repository *repo, const git_signature *stasher, const char *message, uint32_t flags);
int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version);
int git_stash_apply(git_repository *repo, size_t index, const git_stash_apply_options *options);
int git_stash_foreach(git_repository *repo, git_stash_cb callback, void *payload);
int git_stash_drop(git_repository *repo, size_t index);
int git_stash_pop(git_repository *repo, size_t index, const git_stash_apply_options *options);
/*
* Describe
*/
typedef enum {
GIT_DESCRIBE_DEFAULT,
GIT_DESCRIBE_TAGS,
GIT_DESCRIBE_ALL,
} git_describe_strategy_t;
typedef struct git_describe_options {
unsigned int version;
unsigned int max_candidates_tags;
unsigned int describe_strategy;
const char *pattern;
int only_follow_first_parent;
int show_commit_oid_as_fallback;
} git_describe_options;
#define GIT_DESCRIBE_OPTIONS_VERSION ...
int git_describe_init_options(git_describe_options *opts, unsigned int version);
typedef struct {
unsigned int version;
unsigned int abbreviated_size;
int always_use_long_format;
const char *dirty_suffix;
} git_describe_format_options;
#define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION ...
int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version);
typedef ... git_describe_result;
int git_describe_commit(git_describe_result **result, git_object *committish, git_describe_options *opts);
int git_describe_workdir(git_describe_result **out, git_repository *repo, git_describe_options *opts);
int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts);
void git_describe_result_free(git_describe_result *result);
#define GIT_ATTR_CHECK_FILE_THEN_INDEX ...
#define GIT_ATTR_CHECK_INDEX_THEN_FILE ...
#define GIT_ATTR_CHECK_INDEX_ONLY ...
#define GIT_ATTR_CHECK_NO_SYSTEM ...
typedef enum {
GIT_ATTR_UNSPECIFIED_T = 0,
GIT_ATTR_TRUE_T,
GIT_ATTR_FALSE_T,
GIT_ATTR_VALUE_T,
} git_attr_t;
int git_attr_get(const char **value_out, git_repository *repo, uint32_t flags, const char *path, const char *name);
git_attr_t git_attr_value(const char *attr);
int git_revert_commit(git_index **out, git_repository *repo, git_commit *revert_commit, git_commit *our_commit, unsigned int mainline, const git_merge_options *merge_options);

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -25,32 +25,45 @@
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# ffi
# Import from pygit2
from .ffi import ffi, C
from _pygit2 import GitError
value_errors = set([C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EEXISTS,
C.GIT_EAMBIGUOUS])
def check_error(err, io=False):
if err >= 0:
return
message = "(no message provided)"
# Error message
giterr = C.giterr_last()
if giterr != ffi.NULL:
message = ffi.string(giterr.message).decode()
message = ffi.string(giterr.message).decode('utf8')
else:
message = "err %d (no message provided)" % err
if err in [C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EEXISTS,
C.GIT_EAMBIGUOUS]:
# Translate to Python errors
if err in value_errors:
raise ValueError(message)
elif err == C.GIT_ENOTFOUND:
if err == C.GIT_ENOTFOUND:
if io:
raise IOError(message)
else:
raise KeyError(message)
elif err == C.GIT_EINVALIDSPEC:
raise KeyError(message)
if err == C.GIT_EINVALIDSPEC:
raise ValueError(message)
elif err == C.GIT_ITEROVER:
if err == C.GIT_ITEROVER:
raise StopIteration()
# Generic Git error
raise GitError(message)
# Indicate that we want libgit2 to pretend a function was not set
class Passthrough(Exception):
def __init__(self):
super(Passthrough, self).__init__( "The function asked for pass-through")

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -28,32 +28,9 @@
# Import from the future
from __future__ import absolute_import
# Import from the Standard Library
import inspect
import codecs
from os import path, getenv
# Import from cffi
from cffi import FFI
ffi = FFI()
dir_path = path.dirname(path.abspath(inspect.getfile(inspect.currentframe())))
decl_path = path.join(dir_path, 'decl.h')
with codecs.open(decl_path, 'r', 'utf-8') as header:
ffi.cdef(header.read())
# if LIBGIT2 exists, set build and link against that version
libgit2_path = getenv('LIBGIT2')
if not libgit2_path:
libgit2_path = '/usr/local'
include_dirs = [path.join(libgit2_path, 'include')]
library_dirs = [path.join(libgit2_path, 'lib')]
C = ffi.verify("#include <git2.h>", libraries=["git2"],
include_dirs=include_dirs, library_dirs=library_dirs)
# Import from pygit2
try:
from ._libgit2 import ffi, lib as C
except ImportError:
from ._run import ffi, preamble, C_KEYWORDS
C = ffi.verify(preamble, **C_KEYWORDS)

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -28,11 +28,14 @@
# Import from the future
from __future__ import absolute_import, unicode_literals
import weakref
# Import from pygit2
from _pygit2 import Oid, Tree, Diff
from .errors import check_error
from .ffi import ffi, C
from .utils import is_string, strings_to_strarray, to_bytes, to_str
from .utils import is_string, to_bytes, to_str
from .utils import GenericIterator, StrArray
class Index(object):
@ -47,6 +50,7 @@ class Index(object):
err = C.git_index_open(cindex, to_bytes(path))
check_error(err)
self._repo = None
self._index = cindex[0]
self._cindex = cindex
@ -92,7 +96,7 @@ class Index(object):
return IndexEntry._from_c(centry)
def __iter__(self):
return IndexIterator(self)
return GenericIterator(self)
def read(self, force=True):
"""Update the contents the Index
@ -109,8 +113,7 @@ class Index(object):
check_error(err, True)
def write(self):
"""Write the contents of the Index to disk
"""
"""Write the contents of the Index to disk."""
err = C.git_index_write(self._index)
check_error(err, True)
@ -119,21 +122,21 @@ class Index(object):
check_error(err)
def read_tree(self, tree):
"""read_tree([Tree|Oid])
Replace the contents of the Index with those of a tree
"""Replace the contents of the Index with those of the given tree,
expressed either as a <Tree> object or as an oid (string or <Oid>).
The tree will be read recursively and all its children will also be
inserted into the Index.
"""
repo = self._repo
if is_string(tree):
tree = self._repo[tree]
tree = repo[tree]
if isinstance(tree, Oid):
if not hasattr(self, '_repo'):
if repo is None:
raise TypeError("id given but no associated repository")
tree = self._repo[tree]
tree = repo[tree]
elif not isinstance(tree, Tree):
raise TypeError("argument must be Oid or Tree")
@ -143,9 +146,8 @@ class Index(object):
check_error(err)
def write_tree(self, repo=None):
"""write_tree([repo]) -> Oid
Create a tree out of the Index
"""Create a tree out of the Index. Return the <Oid> object of the
written tree.
The contents of the index will be written out to the object
database. If there is no associated repository, 'repo' must be
@ -155,6 +157,9 @@ class Index(object):
It returns the id of the resulting tree.
"""
coid = ffi.new('git_oid *')
repo = repo or self._repo
if repo:
err = C.git_index_write_tree_to(coid, self._index, repo._repo)
else:
@ -163,10 +168,10 @@ class Index(object):
check_error(err)
return Oid(raw=bytes(ffi.buffer(coid)[:]))
def remove(self, path):
def remove(self, path, level=0):
"""Remove an entry from the Index.
"""
err = C.git_index_remove(self._index, to_bytes(path), 0)
err = C.git_index_remove(self._index, to_bytes(path), level)
check_error(err, True)
def add_all(self, pathspecs=[]):
@ -175,18 +180,16 @@ class Index(object):
If pathspecs are specified, only files matching those pathspecs will
be added.
"""
arr, refs = strings_to_strarray(pathspecs)
err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL)
check_error(err, True)
with StrArray(pathspecs) as arr:
err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL)
check_error(err, True)
def add(self, path_or_entry):
"""add([path|entry])
"""Add or update an entry in the Index.
Add or update an entry in the Index
If a path is given, that file will be added. The path must be
relative to the root of the worktree and the Index must be associated
with a repository.
If a path is given, that file will be added. The path must be relative
to the root of the worktree and the Index must be associated with a
repository.
If an IndexEntry is given, that entry will be added or update in the
Index without checking for the existence of the path or id.
@ -205,12 +208,8 @@ class Index(object):
check_error(err, True)
def diff_to_workdir(self, flags=0, context_lines=3, interhunk_lines=0):
"""diff_to_workdir(flags=0, context_lines=3, interhunk_lines=0) -> Diff
Diff the index against the working directory
Return a :py:class:`~pygit2.Diff` object with the differences
between the index and the working copy.
"""Diff the index against the working directory. Return a <Diff> object
with the differences between the index and the working copy.
Arguments:
@ -222,7 +221,8 @@ class Index(object):
interhunk_lines: the maximum number of unchanged lines between hunk
boundaries before the hunks will be merged into a one
"""
if not hasattr(self, '_repo'):
repo = self._repo
if repo is None:
raise ValueError('diff needs an associated repository')
copts = ffi.new('git_diff_options *')
@ -234,19 +234,15 @@ class Index(object):
copts.interhunk_lines = interhunk_lines
cdiff = ffi.new('git_diff **')
err = C.git_diff_index_to_workdir(cdiff, self._repo._repo,
self._index, copts)
err = C.git_diff_index_to_workdir(cdiff, repo._repo, self._index,
copts)
check_error(err)
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo)
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo)
def diff_to_tree(self, tree, flags=0, context_lines=3, interhunk_lines=0):
"""diff_to_tree(flags=0, context_lines=3, interhunk_lines=0) -> Diff
Diff the index against a tree
Return a :py:class:`~pygit2.Diff` object with the differences between
the index and the given tree.
"""Diff the index against a tree. Return a <Diff> object with the
differences between the index and the given tree.
Arguments:
@ -260,8 +256,8 @@ class Index(object):
interhunk_lines: the maximum number of unchanged lines between hunk
boundaries before the hunks will be merged into a one.
"""
if not hasattr(self, '_repo'):
repo = self._repo
if repo is None:
raise ValueError('diff needs an associated repository')
if not isinstance(tree, Tree):
@ -279,11 +275,11 @@ class Index(object):
ffi.buffer(ctree)[:] = tree._pointer[:]
cdiff = ffi.new('git_diff **')
err = C.git_diff_tree_to_index(cdiff, self._repo._repo, ctree[0],
err = C.git_diff_tree_to_index(cdiff, repo._repo, ctree[0],
self._index, copts)
check_error(err)
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo)
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo)
#
@ -314,10 +310,12 @@ class Index(object):
self._conflicts = None
return None
if self._conflicts is None:
self._conflicts = ConflictCollection(self)
if self._conflicts is None or self._conflicts() is None:
conflicts = ConflictCollection(self)
self._conflicts = weakref.ref(conflicts)
return conflicts
return self._conflicts
return self._conflicts()
class IndexEntry(object):
@ -329,7 +327,7 @@ class IndexEntry(object):
self.id = object_id
"""The id of the referenced object"""
self.mode = mode
"""The mode of this entry, a GIT_FILEMODE_ value"""
"""The mode of this entry, a GIT_FILEMODE_* value"""
@property
def oid(self):
@ -369,26 +367,6 @@ class IndexEntry(object):
return entry
class IndexIterator(object):
def __init__(self, index):
self.index = index
self.n = 0
self.max = len(index)
def next(self):
return self.__next__()
def __next__(self):
if self.n >= self.max:
raise StopIteration
entry = self.index[self.n]
self.n += 1
return entry
class ConflictCollection(object):
def __init__(self, index):

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -54,4 +54,4 @@ def to_str(s):
if type(s) is unicode:
return s.encode()
raise TypeError, 'unexpected type "%s"' % repr(s)
raise TypeError('unexpected type "%s"' % repr(s))

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -35,6 +35,7 @@ from .utils import to_bytes
class Refspec(object):
"""The constructor is for internal use only"""
def __init__(self, owner, ptr):
self._owner = owner
self._refspec = ptr
@ -65,16 +66,14 @@ class Refspec(object):
return C.git_refspec_direction(self._refspec)
def src_matches(self, ref):
"""src_matches(str) -> Bool
Returns whether the given string matches the source of this refspec"""
"""Return True if the given string matches the source of this refspec,
False otherwise.
"""
return bool(C.git_refspec_src_matches(self._refspec, to_bytes(ref)))
def dst_matches(self, ref):
"""dst_matches(str) -> Bool
Returns whether the given string matches the destination of this
refspec"""
"""Return True if the given string matches the destination of this
refspec, False otherwise."""
return bool(C.git_refspec_dst_matches(self._refspec, to_bytes(ref)))
def _transform(self, ref, fn):
@ -88,15 +87,13 @@ class Refspec(object):
C.git_buf_free(buf)
def transform(self, ref):
"""transform(str) -> str
Transform a reference name according to this refspec from the lhs to
the rhs."""
"""Transform a reference name according to this refspec from the lhs to
the rhs. Return an string.
"""
return self._transform(ref, C.git_refspec_transform)
def rtransform(self, ref):
"""transform(str) -> str
Transform a reference name according to this refspec from the lhs
to the rhs"""
"""Transform a reference name according to this refspec from the lhs to
the rhs. Return an string.
"""
return self._transform(ref, C.git_refspec_rtransform)

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -30,10 +30,10 @@ from __future__ import absolute_import
# Import from pygit2
from _pygit2 import Oid
from .errors import check_error, GitError
from .errors import check_error, Passthrough
from .ffi import ffi, C
from .refspec import Refspec
from .utils import to_bytes, strarray_to_strings, strings_to_strarray
from .utils import to_bytes, strarray_to_strings, StrArray
def maybe_string(ptr):
@ -49,7 +49,7 @@ class TransferProgress(object):
def __init__(self, tp):
self.total_objects = tp.total_objects
"""Total number objects to download"""
"""Total number of objects to download"""
self.indexed_objects = tp.indexed_objects
"""Objects which have been indexed"""
@ -70,16 +70,35 @@ class TransferProgress(object):
""""Number of bytes received up to now"""
class Remote(object):
class RemoteCallbacks(object):
"""Base class for pygit2 remote callbacks.
Inherit from this class and override the callbacks which you want to use
in your class, which you can then pass to the network operations.
"""
def __init__(self, credentials=None, certificate=None):
"""Initialize some callbacks in-line
Use this constructor to provide credentials and certificate
callbacks in-line, instead of defining your own class for these ones.
You can e.g. also pass in one of the credential objects as 'credentials'
instead of creating a function which returns a hard-coded object.
"""
if credentials is not None:
self.credentials = credentials
if certificate is not None:
self.certificate = certificate
def sideband_progress(self, string):
"""Progress output callback
Override this function with your own progress reporting function
:param str string: Progress otuput from the remote
:param str string: Progress output from the remote
"""
pass
def credentials(self, url, username_from_url, allowed_types):
"""Credentials callback
@ -99,7 +118,26 @@ class Remote(object):
Return value: credential
"""
pass
raise Passthrough
def certificate_check(self, certificate, valid, host):
"""Certificate callback
Override with your own function to determine whether the accept
the server's certificate.
:param None certificate: The certificate. It is currently always None
while we figure out how to represent it cross-platform
:param bool valid: Whether the TLS/SSH library thinks the certificate
is valid
:param str host: The hostname we want to connect to
Return value: True to connect, False to abort
"""
raise Passthrough
def transfer_progress(self, stats):
"""Transfer progress callback
@ -108,7 +146,6 @@ class Remote(object):
:param TransferProgress stats: The progress up to now
"""
pass
def update_tips(self, refname, old, new):
"""Update tips callabck
@ -120,6 +157,169 @@ class Remote(object):
:param Oid new: the reference's new value
"""
def push_update_reference(self, refname, message):
"""Push update reference callback
Override with your own function to report the remote's
acceptace or rejection of reference updates.
:param str refname: the name of the reference (on the remote)
:param str messsage: rejection message from the remote. If None, the update was accepted.
"""
def _fill_fetch_options(self, fetch_opts):
fetch_opts.callbacks.sideband_progress = self._sideband_progress_cb
fetch_opts.callbacks.transfer_progress = self._transfer_progress_cb
fetch_opts.callbacks.update_tips = self._update_tips_cb
fetch_opts.callbacks.credentials = self._credentials_cb
fetch_opts.callbacks.certificate_check = self._certificate_cb
# We need to make sure that this handle stays alive
self._self_handle = ffi.new_handle(self)
fetch_opts.callbacks.payload = self._self_handle
self._stored_exception = None
def _fill_push_options(self, push_opts):
push_opts.callbacks.sideband_progress = self._sideband_progress_cb
push_opts.callbacks.transfer_progress = self._transfer_progress_cb
push_opts.callbacks.update_tips = self._update_tips_cb
push_opts.callbacks.credentials = self._credentials_cb
push_opts.callbacks.certificate_check = self._certificate_cb
push_opts.callbacks.push_update_reference = self._push_update_reference_cb
# We need to make sure that this handle stays alive
self._self_handle = ffi.new_handle(self)
push_opts.callbacks.payload = self._self_handle
# These functions exist to be called by the git_remote as
# callbacks. They proxy the call to whatever the user set
@ffi.callback('git_transfer_progress_cb')
def _transfer_progress_cb(stats_ptr, data):
self = ffi.from_handle(data)
transfer_progress = getattr(self, 'transfer_progress', None)
if not transfer_progress:
return 0
try:
transfer_progress(TransferProgress(stats_ptr))
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('git_transport_message_cb')
def _sideband_progress_cb(string, length, data):
self = ffi.from_handle(data)
progress = getattr(self, 'progress', None)
if not progress:
return 0
try:
s = ffi.string(string, length).decode()
progress(s)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('int (*update_tips)(const char *refname, const git_oid *a,'
'const git_oid *b, void *data)')
def _update_tips_cb(refname, a, b, data):
self = ffi.from_handle(data)
update_tips = getattr(self, 'update_tips', None)
if not update_tips:
return 0
try:
s = maybe_string(refname)
a = Oid(raw=bytes(ffi.buffer(a)[:]))
b = Oid(raw=bytes(ffi.buffer(b)[:]))
update_tips(s, a, b)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback("int (*push_update_reference)(const char *ref, const char *msg, void *data)")
def _push_update_reference_cb(ref, msg, data):
self = ffi.from_handle(data)
push_update_reference = getattr(self, 'push_update_reference', None)
if not push_update_reference:
return 0
try:
refname = ffi.string(ref)
message = maybe_string(msg)
push_update_reference(refname, message)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('int (*credentials)(git_cred **cred, const char *url,'
'const char *username_from_url, unsigned int allowed_types,'
'void *data)')
def _credentials_cb(cred_out, url, username, allowed, data):
self = ffi.from_handle(data)
credentials = getattr(self, 'credentials', None)
if not credentials:
return 0
try:
ccred = get_credentials(credentials, url, username, allowed)
cred_out[0] = ccred[0]
except Passthrough as e:
return C.GIT_PASSTHROUGH
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('int (*git_transport_certificate_check_cb)'
'(git_cert *cert, int valid, const char *host, void *payload)')
def _certificate_cb(cert_i, valid, host, data):
self = ffi.from_handle(data)
# We want to simulate what should happen if libgit2 supported pass-through for
# this callback. For SSH, 'valid' is always False, because it doesn't look
# at known_hosts, but we do want to let it through in order to do what libgit2 would
# if the callback were not set.
try:
is_ssh = cert_i.cert_type == C.GIT_CERT_HOSTKEY_LIBSSH2
certificate_check = getattr(self, 'certificate_check', None)
if not certificate_check:
raise Passthrough
# python's parsing is deep in the libraries and assumes an OpenSSL-owned cert
val = certificate_check(None, bool(valid), ffi.string(host))
if not val:
return C.GIT_ECERTIFICATE
except Passthrough as e:
if is_ssh:
return 0
elif valid:
return 0
else:
return C.GIT_ECERTIFICATE
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
class Remote(object):
def __init__(self, repo, ptr):
"""The constructor is for internal use only"""
@ -127,20 +327,6 @@ class Remote(object):
self._remote = ptr
self._stored_exception = None
# Build the callback structure
callbacks = ffi.new('git_remote_callbacks *')
callbacks.version = 1
callbacks.sideband_progress = self._sideband_progress_cb
callbacks.transfer_progress = self._transfer_progress_cb
callbacks.update_tips = self._update_tips_cb
callbacks.credentials = self._credentials_cb
# We need to make sure that this handle stays alive
self._self_handle = ffi.new_handle(self)
callbacks.payload = self._self_handle
err = C.git_remote_set_callbacks(self._remote, callbacks)
check_error(err)
def __del__(self):
C.git_remote_free(self._remote)
@ -150,72 +336,45 @@ class Remote(object):
return maybe_string(C.git_remote_name(self._remote))
def rename(self, new_name):
"""Rename this remote
Returns a list of fetch refspecs which were not in the standard format
and thus could not be remapped
"""
if not new_name:
raise ValueError("New remote name must be a non-empty string")
problems = ffi.new('git_strarray *')
err = C.git_remote_rename(problems, self._remote, to_bytes(new_name))
check_error(err)
ret = strarray_to_strings(problems)
C.git_strarray_free(problems)
return ret
@property
def url(self):
"""Url of the remote"""
return maybe_string(C.git_remote_url(self._remote))
@url.setter
def url(self, value):
err = C.git_remote_set_url(self._remote, to_bytes(value))
check_error(err)
@property
def push_url(self):
"""Push url of the remote"""
return maybe_string(C.git_remote_pushurl(self._remote))
@push_url.setter
def push_url(self, value):
err = C.git_remote_set_pushurl(self._remote, to_bytes(value))
check_error(err)
def save(self):
"""save()
Save a remote to its repository's configuration"""
"""Save a remote to its repository's configuration."""
err = C.git_remote_save(self._remote)
check_error(err)
def fetch(self, signature=None, message=None):
"""fetch(signature, message) -> TransferProgress
Perform a fetch against this remote.
def fetch(self, refspecs=None, message=None, callbacks=None):
"""Perform a fetch against this remote. Returns a <TransferProgress>
object.
"""
if signature:
ptr = signature._pointer[:]
else:
ptr = ffi.NULL
fetch_opts = ffi.new('git_fetch_options *')
err = C.git_fetch_init_options(fetch_opts, C.GIT_FETCH_OPTIONS_VERSION)
self._stored_exception = None
err = C.git_remote_fetch(self._remote, ptr, to_bytes(message))
if self._stored_exception:
raise self._stored_exception
if callbacks is None:
callbacks = RemoteCallbacks()
check_error(err)
callbacks._fill_fetch_options(fetch_opts)
try:
with StrArray(refspecs) as arr:
err = C.git_remote_fetch(self._remote, arr, fetch_opts, to_bytes(message))
if callbacks._stored_exception:
raise callbacks._stored_exception
check_error(err)
finally:
callbacks._self_handle = None
return TransferProgress(C.git_remote_stats(self._remote))
@ -226,10 +385,7 @@ class Remote(object):
return C.git_remote_refspec_count(self._remote)
def get_refspec(self, n):
"""get_refspec(n) -> Refspec
Return the refspec at the given position
"""
"""Return the <Refspec> object at the given position."""
spec = C.git_remote_get_refspec(self._remote, n)
return Refspec(self, spec)
@ -243,12 +399,6 @@ class Remote(object):
return strarray_to_strings(specs)
@fetch_refspecs.setter
def fetch_refspecs(self, l):
arr, refs = strings_to_strarray(l)
err = C.git_remote_set_fetch_refspecs(self._remote, arr)
check_error(err)
@property
def push_refspecs(self):
"""Refspecs that will be used for pushing"""
@ -259,153 +409,33 @@ class Remote(object):
return strarray_to_strings(specs)
@push_refspecs.setter
def push_refspecs(self, l):
arr, refs = strings_to_strarray(l)
err = C.git_remote_set_push_refspecs(self._remote, arr)
check_error(err)
def push(self, specs, callbacks=None):
"""Push the given refspec to the remote. Raises ``GitError`` on
protocol error or unpack failure.
def add_fetch(self, spec):
"""add_fetch(refspec)
When the remote has a githook installed, that denies the reference
this function will return successfully. Thus it is stronly recommended
to install a callback, that implements
:py:meth:`RemoteCallbacks.push_update_reference` and check the passed
parameters for successfull operations.
Add a fetch refspec to the remote"""
err = C.git_remote_add_fetch(self._remote, to_bytes(spec))
check_error(err)
def add_push(self, spec):
"""add_push(refspec)
Add a push refspec to the remote"""
err = C.git_remote_add_push(self._remote, to_bytes(spec))
check_error(err)
@ffi.callback("int (*cb)(const char *ref, const char *msg, void *data)")
def _push_cb(ref, msg, data):
self = ffi.from_handle(data)
if msg:
self._bad_message = ffi.string(msg).decode()
return 0
def push(self, spec, signature=None, message=None):
"""push(refspec, signature, message)
Push the given refspec to the remote. Raises ``GitError`` on error
:param str spec: push refspec to use
:param Signature signature: signature to use when updating the tips
:param str message: message to use when updating the tips
:param [str] specs: push refspecs to use
"""
push_opts = ffi.new('git_push_options *')
err = C.git_push_init_options(push_opts, C.GIT_PUSH_OPTIONS_VERSION)
cpush = ffi.new('git_push **')
err = C.git_push_new(cpush, self._remote)
check_error(err)
if callbacks is None:
callbacks = RemoteCallbacks()
push = cpush[0]
callbacks._fill_push_options(push_opts)
# Build custom callback structure
try:
err = C.git_push_add_refspec(push, to_bytes(spec))
check_error(err)
err = C.git_push_finish(push)
check_error(err)
if not C.git_push_unpack_ok(push):
raise GitError("remote failed to unpack objects")
err = C.git_push_status_foreach(push, self._push_cb,
ffi.new_handle(self))
check_error(err)
if hasattr(self, '_bad_message'):
raise GitError(self._bad_message)
if signature:
ptr = signature._pointer[:]
else:
ptr = ffi.NULL
err = C.git_push_update_tips(push, ptr, to_bytes(message))
check_error(err)
with StrArray(specs) as refspecs:
err = C.git_remote_push(self._remote, refspecs, push_opts)
check_error(err)
finally:
C.git_push_free(push)
# These functions exist to be called by the git_remote as
# callbacks. They proxy the call to whatever the user set
@ffi.callback('git_transfer_progress_cb')
def _transfer_progress_cb(stats_ptr, data):
self = ffi.from_handle(data)
if not hasattr(self, 'transfer_progress') \
or not self.transfer_progress:
return 0
try:
self.transfer_progress(TransferProgress(stats_ptr))
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('git_transport_message_cb')
def _sideband_progress_cb(string, length, data):
self = ffi.from_handle(data)
if not hasattr(self, 'progress') or not self.progress:
return 0
try:
s = ffi.string(string, length).decode()
self.progress(s)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('int (*update_tips)(const char *refname, const git_oid *a,'
'const git_oid *b, void *data)')
def _update_tips_cb(refname, a, b, data):
self = ffi.from_handle(data)
if not hasattr(self, 'update_tips') or not self.update_tips:
return 0
try:
s = maybe_string(refname)
a = Oid(raw=bytes(ffi.buffer(a)[:]))
b = Oid(raw=bytes(ffi.buffer(b)[:]))
self.update_tips(s, a, b)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('int (*credentials)(git_cred **cred, const char *url,'
'const char *username_from_url, unsigned int allowed_types,'
'void *data)')
def _credentials_cb(cred_out, url, username, allowed, data):
self = ffi.from_handle(data)
if not hasattr(self, 'credentials') or not self.credentials:
return 0
try:
ccred = get_credentials(self.credentials, url, username, allowed)
cred_out[0] = ccred[0]
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
callbacks._self_handle = None
def get_credentials(fn, url, username, allowed):
"""Call fn and return the credentials object"""
@ -415,29 +445,158 @@ def get_credentials(fn, url, username, allowed):
creds = fn(url_str, username_str, allowed)
if not hasattr(creds, 'credential_type') \
or not hasattr(creds, 'credential_tuple'):
credential_type = getattr(creds, 'credential_type', None)
credential_tuple = getattr(creds, 'credential_tuple', None)
if not credential_type or not credential_tuple:
raise TypeError("credential does not implement interface")
cred_type = creds.credential_type
cred_type = credential_type
if not (allowed & cred_type):
raise TypeError("invalid credential type")
ccred = ffi.new('git_cred **')
if cred_type == C.GIT_CREDTYPE_USERPASS_PLAINTEXT:
name, passwd = creds.credential_tuple
name, passwd = credential_tuple
err = C.git_cred_userpass_plaintext_new(ccred, to_bytes(name),
to_bytes(passwd))
elif cred_type == C.GIT_CREDTYPE_SSH_KEY:
name, pubkey, privkey, passphrase = creds.credential_tuple
err = C.git_cred_ssh_key_new(ccred, to_bytes(name), to_bytes(pubkey),
to_bytes(privkey), to_bytes(passphrase))
name, pubkey, privkey, passphrase = credential_tuple
if pubkey is None and privkey is None:
err = C.git_cred_ssh_key_from_agent(ccred, to_bytes(name))
else:
err = C.git_cred_ssh_key_new(ccred, to_bytes(name),
to_bytes(pubkey), to_bytes(privkey),
to_bytes(passphrase))
else:
raise TypeError("unsupported credential type")
check_error(err)
return ccred
class RemoteCollection(object):
"""Collection of configured remotes
You can use this class to look up and manage the remotes configured
in a repository. You can access repositories using index
access. E.g. to look up the "origin" remote, you can use
>>> repo.remotes["origin"]
"""
def __init__(self, repo):
self._repo = repo;
def __len__(self):
names = ffi.new('git_strarray *')
try:
err = C.git_remote_list(names, self._repo._repo)
check_error(err)
return names.count
finally:
C.git_strarray_free(names)
def __iter__(self):
names = ffi.new('git_strarray *')
try:
err = C.git_remote_list(names, self._repo._repo)
check_error(err)
cremote = ffi.new('git_remote **')
for i in range(names.count):
err = C.git_remote_lookup(cremote, self._repo._repo, names.strings[i])
check_error(err)
yield Remote(self._repo, cremote[0])
finally:
C.git_strarray_free(names)
def __getitem__(self, name):
if isinstance(name, int):
return list(self)[name]
cremote = ffi.new('git_remote **')
err = C.git_remote_lookup(cremote, self._repo._repo, to_bytes(name))
check_error(err)
return Remote(self._repo, cremote[0])
def create(self, name, url, fetch=None):
"""Create a new remote with the given name and url. Returns a <Remote>
object.
If 'fetch' is provided, this fetch refspec will be used instead of the default
"""
cremote = ffi.new('git_remote **')
if fetch:
err = C.git_remote_create_with_fetchspec(cremote, self._repo._repo, to_bytes(name), to_bytes(url), to_bytes(fetch))
else:
err = C.git_remote_create(cremote, self._repo._repo, to_bytes(name), to_bytes(url))
check_error(err)
return Remote(self._repo, cremote[0])
def rename(self, name, new_name):
"""Rename a remote in the configuration. The refspecs in standard
format will be renamed.
Returns a list of fetch refspecs (list of strings) which were not in
the standard format and thus could not be remapped.
"""
if not new_name:
raise ValueError("Current remote name must be a non-empty string")
if not new_name:
raise ValueError("New remote name must be a non-empty string")
problems = ffi.new('git_strarray *')
err = C.git_remote_rename(problems, self._repo._repo, to_bytes(name), to_bytes(new_name))
check_error(err)
ret = strarray_to_strings(problems)
C.git_strarray_free(problems)
return ret
def delete(self, name):
"""Remove a remote from the configuration
All remote-tracking branches and configuration settings for the remote will be removed.
"""
err = C.git_remote_delete(self._repo._repo, to_bytes(name))
check_error(err)
def set_url(self, name, url):
""" Set the URL for a remote
"""
err = C.git_remote_set_url(self._repo._repo, to_bytes(name), to_bytes(url))
check_error(err)
def set_push_url(self, name, url):
"""Set the push-URL for a remote
"""
err = C.git_remote_set_pushurl(self._repo._repo, to_bytes(name), to_bytes(url))
check_error(err)
def add_fetch(self, name, refspec):
"""Add a fetch refspec (str) to the remote
"""
err = C.git_remote_add_fetch(self._repo._repo, to_bytes(name), to_bytes(refspec))
check_error(err)
def add_push(self, name, refspec):
"""Add a push refspec (str) to the remote
"""
err = C.git_remote_add_push(self._repo._repo, to_bytes(name), to_bytes(refspec))
check_error(err)

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -28,6 +28,11 @@
from _pygit2 import option
from _pygit2 import GIT_OPT_GET_SEARCH_PATH, GIT_OPT_SET_SEARCH_PATH
from _pygit2 import GIT_OPT_GET_MWINDOW_SIZE, GIT_OPT_SET_MWINDOW_SIZE
from _pygit2 import GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, GIT_OPT_SET_MWINDOW_MAPPED_LIMIT
from _pygit2 import GIT_OPT_SET_CACHE_OBJECT_LIMIT
from _pygit2 import GIT_OPT_GET_CACHED_MEMORY
from _pygit2 import GIT_OPT_ENABLE_CACHING
from _pygit2 import GIT_OPT_SET_CACHE_MAX_SIZE
class SearchPathList(object):
@ -64,3 +69,36 @@ class Settings(object):
@mwindow_size.setter
def mwindow_size(self, value):
option(GIT_OPT_SET_MWINDOW_SIZE, value)
@property
def mwindow_mapped_limit(self):
"""Mwindow mapped limit"""
return option(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT)
@mwindow_mapped_limit.setter
def mwindow_mapped_limit(self, value):
"""Mwindow mapped limit"""
return option(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, value)
@property
def cached_memory(self):
"""Maximum mmap window size"""
return option(GIT_OPT_GET_CACHED_MEMORY)
def enable_caching(self, value=True):
return option(GIT_OPT_ENABLE_CACHING, value)
def cache_max_size(self, value):
return option(GIT_OPT_SET_CACHE_MAX_SIZE, value)
def cache_object_limit(self, object_type, value):
"""Set the maximum data size for the given type of object to be
considered eligible for caching in memory.
Setting to value to zero means that that type of object will not
be cached. Defaults to 0 for GIT_OBJ_BLOB (i.e. won't cache
blobs) and 4k for GIT_OBJ_COMMIT, GIT_OBJ_TREE, and GIT_OBJ_TAG.
"""
return option(GIT_OPT_SET_CACHE_OBJECT_LIMIT, object_type, value)

79
pygit2/submodule.py Normal file

@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from the future
from __future__ import absolute_import, unicode_literals
from .errors import check_error
from .ffi import ffi, C
class Submodule(object):
@classmethod
def _from_c(cls, repo, cptr):
subm = cls.__new__(cls)
subm._repo = repo
subm._subm = cptr
return subm
def __del__(self):
C.git_submodule_free(self._subm)
def open(self):
"""Open the repository for a submodule."""
crepo = ffi.new('git_repository **')
err = C.git_submodule_open(crepo, self._subm)
check_error(err)
return self._repo._from_c(crepo[0], True)
@property
def name(self):
"""Name of the submodule."""
name = C.git_submodule_name(self._subm)
return ffi.string(name).decode('utf-8')
@property
def path(self):
"""Path of the submodule."""
path = C.git_submodule_path(self._subm)
return ffi.string(path).decode('utf-8')
@property
def url(self):
"""URL of the submodule."""
url = C.git_submodule_url(self._subm)
return ffi.string(url).decode('utf-8')
@property
def branch(self):
"""Branch that is to be tracked by the submodule."""
branch = C.git_submodule_branch(self._subm)
return ffi.string(branch).decode('utf-8')

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -50,34 +50,62 @@ def strarray_to_strings(arr):
return l
def strings_to_strarray(l):
"""Convert a list of strings to a git_strarray
class StrArray(object):
"""A git_strarray wrapper
We return first the git_strarray* you can pass to libgit2 and a
list of references to the memory, which we must keep around for as
long as the git_strarray must live.
Use this in order to get a git_strarray* to pass to libgit2 out of a
list of strings. This has a context manager, which you should use, e.g.
with StrArray(list_of_strings) as arr:
C.git_function_that_takes_strarray(arr)
"""
if not isinstance(l, list):
raise TypeError("Value must be a list")
def __init__(self, l):
# Allow passing in None as lg2 typically considers them the same as empty
if l is None:
self.array = ffi.NULL
return
arr = ffi.new('git_strarray *')
strings = ffi.new('char *[]', len(l))
if not isinstance(l, list):
raise TypeError("Value must be a list")
# We need refs in order to keep a reference to the value returned
# by the ffi.new(). Otherwise, they will be freed and the memory
# re-used, with less than great consequences.
refs = [None] * len(l)
strings = [None] * len(l)
for i in range(len(l)):
if not is_string(l[i]):
raise TypeError("Value must be a string")
for i in range(len(l)):
if not is_string(l[i]):
raise TypeError("Value must be a string")
strings[i] = ffi.new('char []', to_bytes(l[i]))
s = ffi.new('char []', to_bytes(l[i]))
refs[i] = s
strings[i] = s
self._arr = ffi.new('char *[]', strings)
self._strings = strings
self.array = ffi.new('git_strarray *', [self._arr, len(strings)])
arr.strings = strings
arr.count = len(l)
def __enter__(self):
return self.array
return arr, refs
def __exit__(self, type, value, traceback):
pass
class GenericIterator(object):
"""Helper to easily implement an iterator.
The constructor gets a container which must implement __len__ and
__getitem__
"""
def __init__(self, container):
self.container = container
self.length = len(container)
self.idx = 0
def next(self):
return self.__next__()
def __next__(self):
idx = self.idx
if idx >= self.length:
raise StopIteration
self.idx += 1
return self.container[idx]

@ -1,26 +0,0 @@
# Copyright 2010-2014 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
__version__ = '0.21.2'

171
setup.py

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# coding: UTF-8
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -28,45 +28,43 @@
"""Setup file for pygit2."""
# Import from the future
from __future__ import print_function
# Import from the Standard Library
import codecs
from distutils.core import setup, Extension, Command
from distutils.command.build import build
from distutils.command.sdist import sdist
from distutils import log
import os
from os import getenv, listdir, pathsep
from os.path import abspath, isfile
from setuptools import setup, Extension, Command
import shlex
from subprocess import Popen, PIPE
import sys
import unittest
# Read version from local pygit2/version.py without pulling in
# pygit2/__init__.py
sys.path.insert(0, 'pygit2')
from version import __version__
# Python 2 support
# See https://github.com/libgit2/pygit2/pull/180 for a discussion about this.
if sys.version_info[0] == 2:
u = lambda s: unicode(s, 'utf-8')
# Get cffi major version
try:
import cffi
except ImportError:
cffi_major_version = None
else:
u = str
cffi_major_version = cffi.__version_info__[0]
# Import stuff from pygit2/_utils.py without loading the whole pygit2 package
sys.path.insert(0, 'pygit2')
from _build import __version__, get_libgit2_paths
if cffi_major_version == 0:
from _run import ffi, preamble, C_KEYWORDS
ffi.verify(preamble, **C_KEYWORDS)
del sys.path[0]
# Use environment variable LIBGIT2 to set your own libgit2 configuration.
libgit2_path = os.getenv("LIBGIT2")
if libgit2_path is None:
if os.name == 'nt':
program_files = os.getenv("ProgramFiles")
libgit2_path = '%s\libgit2' % program_files
else:
libgit2_path = '/usr/local'
libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths()
libgit2_bin = os.path.join(libgit2_path, 'bin')
libgit2_include = os.path.join(libgit2_path, 'include')
libgit2_lib = os.getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib'))
pygit2_exts = [os.path.join('src', name) for name in os.listdir('src')
pygit2_exts = [os.path.join('src', name) for name in sorted(listdir('src'))
if name.endswith('.c')]
@ -79,7 +77,6 @@ class TestCommand(Command):
def initialize_options(self):
self.args = ''
pass
def finalize_options(self):
pass
@ -88,7 +85,7 @@ class TestCommand(Command):
self.run_command('build')
bld = self.distribution.get_command_obj('build')
# Add build_lib in to sys.path so that unittest can found DLLs and libs
sys.path = [os.path.abspath(bld.build_lib)] + sys.path
sys.path = [abspath(bld.build_lib)] + sys.path
test_argv0 = [sys.argv[0] + ' test --args=']
# For transfering args to unittest, we have to split args by ourself,
@ -102,43 +99,10 @@ class TestCommand(Command):
unittest.main(None, defaultTest='test.test_suite', argv=test_argv)
class BuildWithDLLs(build):
# On Windows, we install the git2.dll too.
def _get_dlls(self):
# return a list of (FQ-in-name, relative-out-name) tuples.
ret = []
bld_ext = self.distribution.get_command_obj('build_ext')
compiler_type = bld_ext.compiler.compiler_type
libgit2_dlls = []
if compiler_type == 'msvc':
libgit2_dlls.append('git2.dll')
elif compiler_type == 'mingw32':
libgit2_dlls.append('libgit2.dll')
look_dirs = [libgit2_bin] + os.getenv("PATH", "").split(os.pathsep)
target = os.path.abspath(self.build_lib)
for bin in libgit2_dlls:
for look in look_dirs:
f = os.path.join(look, bin)
if os.path.isfile(f):
ret.append((f, target))
break
else:
log.warn("Could not find required DLL %r to include", bin)
log.debug("(looked in %s)", look_dirs)
return ret
def run(self):
build.run(self)
if os.name == 'nt':
# On Windows we package up the dlls with the plugin.
for s, d in self._get_dlls():
self.copy_file(s, d)
class sdist_files_from_git(sdist):
def get_file_list(self):
popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE)
popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE,
universal_newlines=True)
stdoutdata, stderrdata = popen.communicate()
if popen.returncode != 0:
print(stderrdata)
@ -156,27 +120,68 @@ class sdist_files_from_git(sdist):
self.write_manifest()
cmdclass = {
'test': TestCommand,
'sdist': sdist_files_from_git}
if os.name == 'nt':
# BuildWithDLLs can copy external DLLs into source directory.
cmdclass['build'] = BuildWithDLLs
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Topic :: Software Development :: Version Control"]
with codecs.open('README.rst', 'r', 'utf-8') as readme:
long_description = readme.read()
# This ffi is pygit2.ffi due to the path trick used in the beginning
# of the file
from ffi import ffi
ffi_ext = ffi.verifier.get_extension()
cmdclass = {
'test': TestCommand,
'sdist': sdist_files_from_git,
}
# On Windows, we install the git2.dll too.
class BuildWithDLLs(build):
def _get_dlls(self):
# return a list of (FQ-in-name, relative-out-name) tuples.
ret = []
bld_ext = self.distribution.get_command_obj('build_ext')
compiler_type = bld_ext.compiler.compiler_type
libgit2_dlls = []
if compiler_type == 'msvc':
libgit2_dlls.append('git2.dll')
elif compiler_type == 'mingw32':
libgit2_dlls.append('libgit2.dll')
look_dirs = [libgit2_bin] + getenv("PATH", "").split(pathsep)
target = abspath(self.build_lib)
for bin in libgit2_dlls:
for look in look_dirs:
f = os.path.join(look, bin)
if isfile(f):
ret.append((f, target))
break
else:
log.warn("Could not find required DLL %r to include", bin)
log.debug("(looked in %s)", look_dirs)
return ret
def run(self):
build.run(self)
for s, d in self._get_dlls():
self.copy_file(s, d)
# On Windows we package up the dlls with the plugin.
if os.name == 'nt':
cmdclass['build'] = BuildWithDLLs
extra_args = {
'ext_modules': [
Extension('_pygit2', pygit2_exts, libraries=['git2'],
include_dirs=[libgit2_include],
library_dirs=[libgit2_lib]),
# FFI is added in the build step
],
}
if cffi_major_version == 0:
extra_args['ext_modules'].append(ffi.verifier.get_extension())
else:
extra_args['cffi_modules'] = ['pygit2/_run.py:ffi']
setup(name='pygit2',
description='Python bindings for libgit2.',
@ -184,18 +189,14 @@ setup(name='pygit2',
version=__version__,
url='http://github.com/libgit2/pygit2',
classifiers=classifiers,
license='GPLv2',
maintainer=u('J. David Ibáñez'),
license='GPLv2 with linking exception',
maintainer=u'J. David Ibáñez',
maintainer_email='jdavid.ibp@gmail.com',
long_description=long_description,
packages=['pygit2'],
package_data={'pygit2': ['decl.h']},
install_requires=['cffi'],
ext_modules=[
Extension('_pygit2', pygit2_exts,
include_dirs=[libgit2_include, 'include'],
library_dirs=[libgit2_lib],
libraries=['git2']),
ffi_ext,
],
cmdclass=cmdclass)
setup_requires=['cffi'],
install_requires=['cffi', 'six'],
zip_safe=False,
cmdclass=cmdclass,
**extra_args)

@ -1,399 +0,0 @@
/*
* Copyright 2010-2014 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include "error.h"
#include "types.h"
#include "utils.h"
#include "signature.h"
#include "blame.h"
extern PyObject *GitError;
extern PyTypeObject BlameType;
extern PyTypeObject BlameIterType;
extern PyTypeObject BlameHunkType;
PyObject*
wrap_blame(git_blame *blame, Repository *repo)
{
Blame *py_blame;
py_blame = PyObject_New(Blame, &BlameType);
if (py_blame) {
Py_INCREF(repo);
py_blame->repo = repo;
py_blame->blame = blame;
}
return (PyObject*) py_blame;
}
#include <stdio.h>
PyObject*
wrap_blame_hunk(const git_blame_hunk *hunk, Blame *blame)
{
BlameHunk *py_hunk = NULL;
int err;
if (!hunk)
Py_RETURN_NONE;
py_hunk = PyObject_New(BlameHunk, &BlameHunkType);
if (py_hunk == NULL)
return NULL;
py_hunk->lines_in_hunk = hunk->lines_in_hunk;
py_hunk->final_commit_id = git_oid_allocfmt(&hunk->final_commit_id);
py_hunk->final_start_line_number = hunk->final_start_line_number;
py_hunk->final_signature = NULL;
if (hunk->final_signature) {
err = git_signature_dup(&py_hunk->final_signature,
hunk->final_signature);
if (err < 0)
return Error_set(err);
}
py_hunk->orig_commit_id = git_oid_allocfmt(&hunk->orig_commit_id);
py_hunk->orig_path = hunk->orig_path != NULL ?
strdup(hunk->orig_path) : NULL;
py_hunk->orig_start_line_number = hunk->orig_start_line_number;
py_hunk->orig_signature = NULL;
if (hunk->orig_signature) {
err = git_signature_dup(&py_hunk->orig_signature,
hunk->orig_signature);
if (err < 0)
return Error_set(err);
}
py_hunk->boundary = hunk->boundary;
return (PyObject*) py_hunk;
}
PyDoc_STRVAR(BlameHunk_final_committer__doc__, "Final committer.");
PyObject *
BlameHunk_final_committer__get__(BlameHunk *self)
{
if (!self->final_signature)
Py_RETURN_NONE;
return build_signature((Object*) self, self->final_signature, "utf-8");
}
PyDoc_STRVAR(BlameHunk_orig_committer__doc__, "Origin committer.");
PyObject *
BlameHunk_orig_committer__get__(BlameHunk *self)
{
if (!self->orig_signature)
Py_RETURN_NONE;
return build_signature((Object*) self, self->orig_signature, "utf-8");
}
static int
BlameHunk_init(BlameHunk *self, PyObject *args, PyObject *kwds)
{
self->final_commit_id = NULL;
self->final_signature = NULL;
self->orig_commit_id = NULL;
self->orig_path = NULL;
self->orig_signature = NULL;
return 0;
}
static void
BlameHunk_dealloc(BlameHunk *self)
{
free(self->final_commit_id);
if (self->final_signature)
git_signature_free(self->final_signature);
free(self->orig_commit_id);
if (self->orig_path)
free(self->orig_path);
if (self->orig_signature)
git_signature_free(self->orig_signature);
PyObject_Del(self);
}
PyMemberDef BlameHunk_members[] = {
MEMBER(BlameHunk, lines_in_hunk, T_UINT, "Number of lines."),
MEMBER(BlameHunk, final_commit_id, T_STRING, "Last changed oid."),
MEMBER(BlameHunk, final_start_line_number, T_UINT, "final start line no."),
MEMBER(BlameHunk, orig_commit_id, T_STRING, "oid where hunk was found."),
MEMBER(BlameHunk, orig_path, T_STRING, "Origin path."),
MEMBER(BlameHunk, orig_start_line_number, T_UINT, "Origin start line no."),
MEMBER(BlameHunk, boundary, T_BOOL, "Tracked to a boundary commit."),
{NULL}
};
PyGetSetDef BlameHunk_getseters[] = {
GETTER(BlameHunk, final_committer),
GETTER(BlameHunk, orig_committer),
{NULL}
};
PyDoc_STRVAR(BlameHunk__doc__, "Blame Hunk object.");
PyTypeObject BlameHunkType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.BlameHunk", /* tp_name */
sizeof(BlameHunk), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)BlameHunk_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
BlameHunk__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
BlameHunk_members, /* tp_members */
BlameHunk_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)BlameHunk_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
PyObject *
BlameIter_iternext(BlameIter *self)
{
if (self->i < self->n)
return wrap_blame_hunk(git_blame_get_hunk_byindex(
self->blame->blame, self->i++), self->blame);
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
static void
BlameIter_dealloc(BlameIter *self)
{
Py_CLEAR(self->blame);
PyObject_Del(self);
}
PyDoc_STRVAR(BlameIter__doc__, "Blame iterator object.");
PyTypeObject BlameIterType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.BlameIter", /* tp_name */
sizeof(BlameIter), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)BlameIter_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
BlameIter__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
(iternextfunc) BlameIter_iternext, /* tp_iternext */
};
PyObject *
Blame_iter(Blame *self)
{
BlameIter *iter;
iter = PyObject_New(BlameIter, &BlameIterType);
if (iter != NULL) {
Py_INCREF(self);
iter->blame = self;
iter->i = 0;
iter->n = git_blame_get_hunk_count(self->blame);
}
return (PyObject*)iter;
}
Py_ssize_t
Blame_len(Blame *self)
{
assert(self->blame);
return (Py_ssize_t)git_blame_get_hunk_count(self->blame);
}
PyObject *
Blame_getitem(Blame *self, PyObject *value)
{
size_t i;
const git_blame_hunk *hunk;
if (PyLong_Check(value) < 0) {
PyErr_SetObject(PyExc_IndexError, value);
return NULL;
}
i = PyLong_AsUnsignedLong(value);
if (PyErr_Occurred()) {
PyErr_SetObject(PyExc_IndexError, value);
return NULL;
}
hunk = git_blame_get_hunk_byindex(self->blame, i);
if (!hunk) {
PyErr_SetObject(PyExc_IndexError, value);
return NULL;
}
return wrap_blame_hunk(hunk, self);
}
PyDoc_STRVAR(Blame_for_line__doc__,
"for_line(line_no) -> hunk\n"
"\n"
"Returns the blame hunk data for the given \"line_no\" in blame.\n"
"\n"
"Arguments:\n"
"\n"
"line_no\n"
" Line number, countings starts with 1.");
PyObject *
Blame_for_line(Blame *self, PyObject *args)
{
size_t line_no;
const git_blame_hunk *hunk;
if (!PyArg_ParseTuple(args, "I", &line_no))
return NULL;
hunk = git_blame_get_hunk_byline(self->blame, line_no);
if (!hunk) {
PyErr_SetObject(PyExc_IndexError, args);
return NULL;
}
return wrap_blame_hunk(hunk, self);
}
static void
Blame_dealloc(Blame *self)
{
git_blame_free(self->blame);
Py_CLEAR(self->repo);
PyObject_Del(self);
}
PyMappingMethods Blame_as_mapping = {
(lenfunc)Blame_len, /* mp_length */
(binaryfunc)Blame_getitem, /* mp_subscript */
0, /* mp_ass_subscript */
};
static PyMethodDef Blame_methods[] = {
METHOD(Blame, for_line, METH_VARARGS),
{NULL}
};
PyDoc_STRVAR(Blame__doc__, "Blame objects.");
PyTypeObject BlameType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.Blame", /* tp_name */
sizeof(Blame), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Blame_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
&Blame_as_mapping, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
Blame__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)Blame_iter, /* tp_iter */
0, /* tp_iternext */
Blame_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -27,11 +27,12 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "blob.h"
#include "diff.h"
#include "error.h"
#include "utils.h"
#include "object.h"
#include "blob.h"
#include "patch.h"
#include "utils.h"
extern PyObject *GitError;
@ -132,7 +133,7 @@ PyDoc_STRVAR(Blob_size__doc__, "Size.");
PyObject *
Blob_size__get__(Blob *self)
{
return PyLong_FromLongLong(git_blob_rawsize(self->blob));
return PyInt_FromLongLong(git_blob_rawsize(self->blob));
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -81,6 +81,28 @@ Branch_is_head(Branch *self)
return Error_set(err);
}
PyDoc_STRVAR(Branch_is_checked_out__doc__,
"is_checked_out()\n"
"\n"
"True if branch is checked out by any repo connected to the current one, "
" False otherwise.");
PyObject *
Branch_is_checked_out(Branch *self)
{
int err;
CHECK_REFERENCE(self);
err = git_branch_is_checked_out(self->reference);
if (err == 1)
Py_RETURN_TRUE;
else if (err == 0)
Py_RETURN_FALSE;
else
return Error_set(err);
}
PyDoc_STRVAR(Branch_rename__doc__,
"rename(name, force=False)\n"
@ -101,7 +123,7 @@ Branch_rename(Branch *self, PyObject *args)
if (!PyArg_ParseTuple(args, "s|i", &c_name, &force))
return NULL;
err = git_branch_move(&c_out, self->reference, c_name, force, NULL, NULL);
err = git_branch_move(&c_out, self->reference, c_name, force);
if (err == GIT_OK)
return wrap_branch(c_out, self->repo);
else
@ -234,6 +256,7 @@ Branch_upstream_name__get__(Branch *self)
PyMethodDef Branch_methods[] = {
METHOD(Branch, delete, METH_NOARGS),
METHOD(Branch, is_head, METH_NOARGS),
METHOD(Branch, is_checked_out, METH_NOARGS),
METHOD(Branch, rename, METH_VARARGS),
{NULL}
};

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -34,6 +34,7 @@
PyObject* Branch_delete(Branch *self, PyObject *args);
PyObject* Branch_is_head(Branch *self);
PyObject* Branch_is_checked_out(Branch *self);
PyObject* Branch_move(Branch *self, PyObject *args);
PyObject* wrap_branch(git_reference *c_reference, Repository *repo);

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -35,6 +35,7 @@
#include "oid.h"
extern PyTypeObject TreeType;
extern PyObject *GitError;
PyDoc_STRVAR(Commit_message_encoding__doc__, "Message encoding.");
@ -79,7 +80,7 @@ PyDoc_STRVAR(Commit_commit_time__doc__, "Commit time.");
PyObject *
Commit_commit_time__get__(Commit *commit)
{
return PyLong_FromLongLong(git_commit_time(commit->commit));
return PyInt_FromLongLong(git_commit_time(commit->commit));
}
@ -88,7 +89,7 @@ PyDoc_STRVAR(Commit_commit_time_offset__doc__, "Commit time offset.");
PyObject *
Commit_commit_time_offset__get__(Commit *commit)
{
return PyLong_FromLong(git_commit_time_offset(commit->commit));
return PyInt_FromLong(git_commit_time_offset(commit->commit));
}
@ -131,8 +132,11 @@ Commit_tree__get__(Commit *commit)
int err;
err = git_commit_tree(&tree, commit->commit);
if (err == GIT_ENOTFOUND)
Py_RETURN_NONE;
if (err == GIT_ENOTFOUND) {
char tree_id[GIT_OID_HEXSZ + 1] = { 0 };
git_oid_fmt(tree_id, git_commit_tree_id(commit->commit));
return PyErr_Format(GitError, "Unable to read tree %s", tree_id);
}
if (err < 0)
return Error_set(err);

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -28,22 +28,26 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include "diff.h"
#include "error.h"
#include "oid.h"
#include "patch.h"
#include "types.h"
#include "utils.h"
#include "diff.h"
extern PyObject *GitError;
extern PyTypeObject TreeType;
extern PyTypeObject IndexType;
extern PyTypeObject DiffType;
extern PyTypeObject HunkType;
extern PyTypeObject DiffDeltaType;
extern PyTypeObject DiffFileType;
extern PyTypeObject DiffHunkType;
extern PyTypeObject DiffLineType;
extern PyTypeObject DiffStatsType;
extern PyTypeObject RepositoryType;
PyTypeObject PatchType;
PyObject*
PyObject *
wrap_diff(git_diff *diff, Repository *repo)
{
Diff *py_diff;
@ -52,148 +56,162 @@ wrap_diff(git_diff *diff, Repository *repo)
if (py_diff) {
Py_INCREF(repo);
py_diff->repo = repo;
py_diff->list = diff;
py_diff->diff = diff;
}
return (PyObject*) py_diff;
}
PyObject *
wrap_patch(git_patch *patch)
wrap_diff_file(const git_diff_file *file)
{
Patch *py_patch;
DiffFile *py_file;
if (!patch)
if (!file)
Py_RETURN_NONE;
py_patch = PyObject_New(Patch, &PatchType);
if (py_patch) {
size_t i, j, hunk_amounts, lines_in_hunk, additions, deletions;
const git_diff_delta *delta;
const git_diff_hunk *hunk;
const git_diff_line *line;
int err;
delta = git_patch_get_delta(patch);
py_patch->old_file_path = delta->old_file.path;
py_patch->new_file_path = delta->new_file.path;
py_patch->status = git_diff_status_char(delta->status);
py_patch->similarity = delta->similarity;
py_patch->flags = delta->flags;
py_patch->old_id = git_oid_allocfmt(&delta->old_file.id);
py_patch->new_id = git_oid_allocfmt(&delta->new_file.id);
git_patch_line_stats(NULL, &additions, &deletions, patch);
py_patch->additions = additions;
py_patch->deletions = deletions;
hunk_amounts = git_patch_num_hunks(patch);
py_patch->hunks = PyList_New(hunk_amounts);
for (i = 0; i < hunk_amounts; ++i) {
Hunk *py_hunk = NULL;
err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i);
if (err < 0)
return Error_set(err);
py_hunk = PyObject_New(Hunk, &HunkType);
if (py_hunk != NULL) {
py_hunk->old_start = hunk->old_start;
py_hunk->old_lines = hunk->old_lines;
py_hunk->new_start = hunk->new_start;
py_hunk->new_lines = hunk->new_lines;
py_hunk->lines = PyList_New(lines_in_hunk);
for (j = 0; j < lines_in_hunk; ++j) {
PyObject *py_line_origin = NULL, *py_line = NULL;
err = git_patch_get_line_in_hunk(&line, patch, i, j);
if (err < 0)
return Error_set(err);
py_line_origin = to_unicode_n(&line->origin, 1,
NULL, NULL);
py_line = to_unicode_n(line->content, line->content_len,
NULL, NULL);
PyList_SetItem(py_hunk->lines, j,
Py_BuildValue("OO", py_line_origin, py_line));
Py_DECREF(py_line_origin);
Py_DECREF(py_line);
}
PyList_SetItem((PyObject*) py_patch->hunks, i,
(PyObject*) py_hunk);
}
}
py_file = PyObject_New(DiffFile, &DiffFileType);
if (py_file) {
py_file->id = git_oid_to_python(&file->id);
py_file->path = file->path != NULL ? strdup(file->path) : NULL;
py_file->size = file->size;
py_file->flags = file->flags;
py_file->mode = file->mode;
}
git_patch_free(patch);
return (PyObject*) py_patch;
return (PyObject *) py_file;
}
PyObject*
diff_get_patch_byindex(git_diff *diff, size_t idx)
PyObject *
wrap_diff_delta(const git_diff_delta *delta)
{
git_patch *patch = NULL;
DiffDelta *py_delta;
if (!delta)
Py_RETURN_NONE;
py_delta = PyObject_New(DiffDelta, &DiffDeltaType);
if (py_delta) {
py_delta->status = delta->status;
py_delta->flags = delta->flags;
py_delta->similarity = delta->similarity;
py_delta->nfiles = delta->nfiles;
py_delta->old_file = wrap_diff_file(&delta->old_file);
py_delta->new_file = wrap_diff_file(&delta->new_file);
}
return (PyObject *) py_delta;
}
PyObject *
wrap_diff_hunk(git_patch *patch, size_t idx)
{
DiffHunk *py_hunk;
const git_diff_hunk *hunk;
const git_diff_line *line;
size_t j, lines_in_hunk;
int err;
err = git_patch_from_diff(&patch, diff, idx);
err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, idx);
if (err < 0)
return Error_set(err);
return (PyObject*) wrap_patch(patch);
py_hunk = PyObject_New(DiffHunk, &DiffHunkType);
if (py_hunk) {
py_hunk->old_start = hunk->old_start;
py_hunk->old_lines = hunk->old_lines;
py_hunk->new_start = hunk->new_start;
py_hunk->new_lines = hunk->new_lines;
py_hunk->header = to_unicode_n((const char *) &hunk->header,
hunk->header_len, NULL, NULL);
py_hunk->lines = PyList_New(lines_in_hunk);
for (j = 0; j < lines_in_hunk; ++j) {
PyObject *py_line = NULL;
err = git_patch_get_line_in_hunk(&line, patch, idx, j);
if (err < 0)
return Error_set(err);
py_line = wrap_diff_line(line);
if (py_line == NULL)
return NULL;
PyList_SetItem(py_hunk->lines, j, py_line);
}
}
return (PyObject *) py_hunk;
}
PyObject *
wrap_diff_stats(git_diff *diff)
{
git_diff_stats *stats;
DiffStats *py_stats;
int err;
err = git_diff_get_stats(&stats, diff);
if (err < 0)
return Error_set(err);
py_stats = PyObject_New(DiffStats, &DiffStatsType);
if (!py_stats) {
git_diff_stats_free(stats);
return NULL;
}
py_stats->stats = stats;
return (PyObject *) py_stats;
}
PyObject *
wrap_diff_line(const git_diff_line *line)
{
DiffLine *py_line;
py_line = PyObject_New(DiffLine, &DiffLineType);
if (py_line) {
py_line->origin = line->origin;
py_line->old_lineno = line->old_lineno;
py_line->new_lineno = line->new_lineno;
py_line->num_lines = line->num_lines;
py_line->content = to_unicode_n(line->content, line->content_len,
NULL, NULL);
py_line->content_offset = line->content_offset;
}
return (PyObject *) py_line;
}
static void
Patch_dealloc(Patch *self)
DiffFile_dealloc(DiffFile *self)
{
Py_CLEAR(self->hunks);
free(self->old_id);
free(self->new_id);
/* We do not have to free old_file_path and new_file_path, they will
* be freed by git_diff_list_free in Diff_dealloc */
Py_CLEAR(self->id);
if (self->path)
free(self->path);
PyObject_Del(self);
}
PyMemberDef Patch_members[] = {
MEMBER(Patch, old_file_path, T_STRING, "old file path"),
MEMBER(Patch, new_file_path, T_STRING, "new file path"),
MEMBER(Patch, old_id, T_STRING, "old oid"),
MEMBER(Patch, new_id, T_STRING, "new oid"),
MEMBER(Patch, status, T_CHAR, "status"),
MEMBER(Patch, similarity, T_INT, "similarity"),
MEMBER(Patch, hunks, T_OBJECT, "hunks"),
MEMBER(Patch, additions, T_INT, "additions"),
MEMBER(Patch, deletions, T_INT, "deletions"),
PyMemberDef DiffFile_members[] = {
MEMBER(DiffFile, id, T_OBJECT, "Oid of the item."),
MEMBER(DiffFile, path, T_STRING, "Path to the entry."),
MEMBER(DiffFile, size, T_LONG, "Size of the entry."),
MEMBER(DiffFile, flags, T_UINT, "Combination of GIT_DIFF_FLAG_* flags."),
MEMBER(DiffFile, mode, T_USHORT, "Mode of the entry."),
{NULL}
};
PyDoc_STRVAR(Patch_is_binary__doc__, "True if binary data, False if not.");
PyObject *
Patch_is_binary__get__(Patch *self)
{
if (!(self->flags & GIT_DIFF_FLAG_NOT_BINARY) &&
(self->flags & GIT_DIFF_FLAG_BINARY))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
PyDoc_STRVAR(DiffFile__doc__, "DiffFile object.");
PyGetSetDef Patch_getseters[] = {
GETTER(Patch, is_binary),
{NULL}
};
PyDoc_STRVAR(Patch__doc__, "Diff patch object.");
PyTypeObject PatchType = {
PyTypeObject DiffFileType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.Patch", /* tp_name */
sizeof(Patch), /* tp_basicsize */
"_pygit2.DiffFile", /* tp_name */
sizeof(DiffFile), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Patch_dealloc, /* tp_dealloc */
(destructor)DiffFile_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
@ -209,7 +227,7 @@ PyTypeObject PatchType = {
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
Patch__doc__, /* tp_doc */
DiffFile__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
@ -217,8 +235,8 @@ PyTypeObject PatchType = {
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
Patch_members, /* tp_members */
Patch_getseters, /* tp_getset */
DiffFile_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
@ -230,11 +248,188 @@ PyTypeObject PatchType = {
};
PyDoc_STRVAR(DiffDelta_status_char__doc__,
"status_char()\n"
"\n"
"Return the single character abbreviation for a delta status code."
);
PyObject *
DiffDelta_status_char(DiffDelta *self)
{
char status = git_diff_status_char(self->status);
#if PY_MAJOR_VERSION == 2
return Py_BuildValue("c", status);
#else
return Py_BuildValue("C", status);
#endif
}
PyDoc_STRVAR(DiffDelta_is_binary__doc__, "True if binary data, False if not.");
PyObject *
DiffDelta_is_binary__get__(DiffDelta *self)
{
if (!(self->flags & GIT_DIFF_FLAG_NOT_BINARY) &&
(self->flags & GIT_DIFF_FLAG_BINARY))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
static void
DiffDelta_dealloc(DiffDelta *self)
{
Py_CLEAR(self->old_file);
Py_CLEAR(self->new_file);
PyObject_Del(self);
}
static PyMethodDef DiffDelta_methods[] = {
METHOD(DiffDelta, status_char, METH_NOARGS),
{NULL}
};
PyMemberDef DiffDelta_members[] = {
MEMBER(DiffDelta, status, T_UINT, "A GIT_DELTA_* constant."),
MEMBER(DiffDelta, flags, T_UINT, "Combination of GIT_DIFF_FLAG_* flags."),
MEMBER(DiffDelta, similarity, T_USHORT, "For renamed and copied."),
MEMBER(DiffDelta, nfiles, T_USHORT, "Number of files in the delta."),
MEMBER(DiffDelta, old_file, T_OBJECT, "\"from\" side of the diff."),
MEMBER(DiffDelta, new_file, T_OBJECT, "\"to\" side of the diff."),
{NULL}
};
PyGetSetDef DiffDelta_getseters[] = {
GETTER(DiffDelta, is_binary),
{NULL}
};
PyDoc_STRVAR(DiffDelta__doc__, "DiffDelta object.");
PyTypeObject DiffDeltaType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.DiffDelta", /* tp_name */
sizeof(DiffDelta), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DiffDelta_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
DiffDelta__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
DiffDelta_methods, /* tp_methods */
DiffDelta_members, /* tp_members */
DiffDelta_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
static void
DiffLine_dealloc(DiffLine *self)
{
Py_CLEAR(self->content);
PyObject_Del(self);
}
PyMemberDef DiffLine_members[] = {
MEMBER(DiffLine, origin, T_CHAR, "Type of the diff line"),
MEMBER(DiffLine, old_lineno, T_INT,
"Line number in old file or -1 for added line"),
MEMBER(DiffLine, new_lineno, T_INT,
"Line number in new file or -1 for deleted line"),
MEMBER(DiffLine, num_lines, T_INT,
"Number of newline characters in content"),
MEMBER(DiffLine, content_offset, T_INT,
"Offset in the original file to the content"),
MEMBER(DiffLine, content, T_OBJECT, "Content of the diff line"),
{NULL}
};
PyDoc_STRVAR(DiffLine__doc__, "DiffLine object.");
PyTypeObject DiffLineType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.DiffLine", /* tp_name */
sizeof(DiffLine), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DiffLine_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
DiffLine__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
DiffLine_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
PyObject *
diff_get_patch_byindex(git_diff *diff, size_t idx)
{
git_patch *patch = NULL;
int err;
err = git_patch_from_diff(&patch, diff, idx);
if (err < 0)
return Error_set(err);
return (PyObject*) wrap_patch(patch);
}
PyObject *
DiffIter_iternext(DiffIter *self)
{
if (self->i < self->n)
return diff_get_patch_byindex(self->diff->list, self->i++);
return diff_get_patch_byindex(self->diff->diff, self->i++);
PyErr_SetNone(PyExc_StopIteration);
return NULL;
@ -283,11 +478,12 @@ PyTypeObject DiffIterType = {
Py_ssize_t
Diff_len(Diff *self)
{
assert(self->list);
return (Py_ssize_t)git_diff_num_deltas(self->list);
assert(self->diff);
return (Py_ssize_t)git_diff_num_deltas(self->diff);
}
PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string.");
PyDoc_STRVAR(Diff_patch__doc__,
"Patch diff string. Can be None in some cases, such as empty commits.");
PyObject *
Diff_patch__get__(Diff *self)
@ -295,15 +491,15 @@ Diff_patch__get__(Diff *self)
git_patch* patch;
git_buf buf = {NULL};
int err = GIT_ERROR;
size_t i, len, num;
size_t i, num;
PyObject *py_patch = NULL;
num = git_diff_num_deltas(self->list);
num = git_diff_num_deltas(self->diff);
if (num == 0)
Py_RETURN_NONE;
for (i = 0, len = 1; i < num ; ++i) {
err = git_patch_from_diff(&patch, self->list, i);
for (i = 0; i < num ; ++i) {
err = git_patch_from_diff(&patch, self->diff, i);
if (err < 0)
goto cleanup;
@ -325,30 +521,32 @@ cleanup:
static void
Hunk_dealloc(Hunk *self)
DiffHunk_dealloc(DiffHunk *self)
{
Py_CLEAR(self->header);
Py_CLEAR(self->lines);
PyObject_Del(self);
}
PyMemberDef Hunk_members[] = {
MEMBER(Hunk, old_start, T_INT, "Old start."),
MEMBER(Hunk, old_lines, T_INT, "Old lines."),
MEMBER(Hunk, new_start, T_INT, "New start."),
MEMBER(Hunk, new_lines, T_INT, "New lines."),
MEMBER(Hunk, lines, T_OBJECT, "Lines."),
PyMemberDef DiffHunk_members[] = {
MEMBER(DiffHunk, old_start, T_INT, "Old start."),
MEMBER(DiffHunk, old_lines, T_INT, "Old lines."),
MEMBER(DiffHunk, new_start, T_INT, "New start."),
MEMBER(DiffHunk, new_lines, T_INT, "New lines."),
MEMBER(DiffHunk, header, T_OBJECT, "Header."),
MEMBER(DiffHunk, lines, T_OBJECT, "Lines."),
{NULL}
};
PyDoc_STRVAR(Hunk__doc__, "Hunk object.");
PyDoc_STRVAR(DiffHunk__doc__, "DiffHunk object.");
PyTypeObject HunkType = {
PyTypeObject DiffHunkType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.Hunk", /* tp_name */
sizeof(Hunk), /* tp_basicsize */
"_pygit2.DiffHunk", /* tp_name */
sizeof(DiffHunk), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Hunk_dealloc, /* tp_dealloc */
(destructor)DiffHunk_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
@ -364,7 +562,7 @@ PyTypeObject HunkType = {
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
Hunk__doc__, /* tp_doc */
DiffHunk__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
@ -372,7 +570,7 @@ PyTypeObject HunkType = {
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
Hunk_members, /* tp_members */
DiffHunk_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
@ -384,6 +582,132 @@ PyTypeObject HunkType = {
0, /* tp_new */
};
PyDoc_STRVAR(DiffStats_insertions__doc__, "Total number of insertions");
PyObject *
DiffStats_insertions__get__(DiffStats *self)
{
return PyInt_FromSize_t(git_diff_stats_insertions(self->stats));
}
PyDoc_STRVAR(DiffStats_deletions__doc__, "Total number of deletions");
PyObject *
DiffStats_deletions__get__(DiffStats *self)
{
return PyInt_FromSize_t(git_diff_stats_deletions(self->stats));
}
PyDoc_STRVAR(DiffStats_files_changed__doc__, "Total number of files changed");
PyObject *
DiffStats_files_changed__get__(DiffStats *self)
{
return PyInt_FromSize_t(git_diff_stats_files_changed(self->stats));
}
PyDoc_STRVAR(DiffStats_format__doc__,
"format(format, width)-> str\n"
"\n"
"Format the stats as a string\n"
"\n"
"Arguments:\n"
"\n"
"format\n"
" The format to use. A pygit2.GIT_DIFF_STATS_* constant\n"
"\n"
"width\n"
" The width of the output. The output will be scaled to fit.");
PyObject *
DiffStats_format(DiffStats *self, PyObject *args, PyObject *kwds)
{
int err, format;
git_buf buf = { 0 };
Py_ssize_t width;
PyObject *str;
char *keywords[] = {"format", "width", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "in", keywords, &format, &width))
return NULL;
if (width <= 0) {
PyErr_SetString(PyExc_ValueError, "width must be positive");
return NULL;
}
err = git_diff_stats_to_buf(&buf, self->stats, format, width);
if (err < 0)
return Error_set(err);
str = to_unicode(buf.ptr, NULL, NULL);
git_buf_free(&buf);
return str;
}
static void
DiffStats_dealloc(DiffStats *self)
{
git_diff_stats_free(self->stats);
PyObject_Del(self);
}
PyMethodDef DiffStats_methods[] = {
METHOD(DiffStats, format, METH_VARARGS | METH_KEYWORDS),
{NULL}
};
PyGetSetDef DiffStats_getseters[] = {
GETTER(DiffStats, insertions),
GETTER(DiffStats, deletions),
GETTER(DiffStats, files_changed),
{NULL}
};
PyDoc_STRVAR(DiffStats__doc__, "DiffStats object.");
PyTypeObject DiffStatsType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.DiffStats", /* tp_name */
sizeof(DiffStats), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DiffStats_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
DiffStats__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
DiffStats_methods, /* tp_methods */
0, /* tp_members */
DiffStats_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
PyDoc_STRVAR(Diff_from_c__doc__, "Method exposed for Index to hook into");
PyObject *
@ -429,7 +753,7 @@ Diff_merge(Diff *self, PyObject *args)
if (py_diff->repo->repo != self->repo->repo)
return Error_set(GIT_ERROR);
err = git_diff_merge(self->list, py_diff->list);
err = git_diff_merge(self->diff, py_diff->diff);
if (err < 0)
return Error_set(err);
@ -454,7 +778,7 @@ Diff_find_similar(Diff *self, PyObject *args, PyObject *kwds)
&opts.flags, &opts.rename_threshold, &opts.copy_threshold, &opts.rename_from_rewrite_threshold, &opts.break_rewrite_threshold, &opts.rename_limit))
return NULL;
err = git_diff_find_similar(self->list, &opts);
err = git_diff_find_similar(self->diff, &opts);
if (err < 0)
return Error_set(err);
@ -471,7 +795,7 @@ Diff_iter(Diff *self)
Py_INCREF(self);
iter->diff = self;
iter->i = 0;
iter->n = git_diff_num_deltas(self->list);
iter->n = git_diff_num_deltas(self->diff);
}
return (PyObject*)iter;
}
@ -481,25 +805,32 @@ Diff_getitem(Diff *self, PyObject *value)
{
size_t i;
if (PyLong_Check(value) < 0)
return NULL;
if (!PyInt_Check(value))
return NULL; /* FIXME Raise error */
i = PyLong_AsUnsignedLong(value);
return diff_get_patch_byindex(self->list, i);
i = PyInt_AsSize_t(value);
return diff_get_patch_byindex(self->diff, i);
}
PyDoc_STRVAR(Diff_stats__doc__, "Accumulate diff statistics for all patches");
PyObject *
Diff_stats__get__(Diff *self)
{
return wrap_diff_stats(self->diff);
}
static void
Diff_dealloc(Diff *self)
{
git_diff_free(self->list);
git_diff_free(self->diff);
Py_CLEAR(self->repo);
PyObject_Del(self);
}
PyGetSetDef Diff_getseters[] = {
GETTER(Diff, patch),
GETTER(Diff, stats),
{NULL}
};

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -33,15 +33,13 @@
#include <git2.h>
#include "types.h"
#define DIFF_CHECK_TYPES(_x, _y, _type_x, _type_y) \
PyObject_TypeCheck(_x, _type_x) && \
PyObject_TypeCheck(_y, _type_y)
PyObject* Diff_changes(Diff *self);
PyObject* Diff_patch(Diff *self);
PyObject* wrap_diff(git_diff *diff, Repository *repo);
PyObject* wrap_patch(git_patch *patch);
PyObject* wrap_diff_delta(const git_diff_delta *delta);
PyObject* wrap_diff_file(const git_diff_file *file);
PyObject * wrap_diff_hunk(git_patch *patch, size_t idx);
PyObject* wrap_diff_line(const git_diff_line *line);
#endif

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -126,3 +126,10 @@ Error_set_oid(int err, const git_oid *oid, size_t len)
hex[len] = '\0';
return Error_set_str(err, hex);
}
PyObject *
Error_type_error(const char *format, PyObject *value)
{
PyErr_Format(PyExc_TypeError, format, Py_TYPE(value)->tp_name);
return NULL;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -37,5 +37,6 @@ PyObject* Error_set(int err);
PyObject* Error_set_exc(PyObject* exception);
PyObject* Error_set_str(int err, const char *str);
PyObject* Error_set_oid(int err, const git_oid *oid, size_t len);
PyObject* Error_type_error(const char *format, PyObject *value);
#endif

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -39,13 +39,13 @@ extern PyTypeObject SignatureType;
PyDoc_STRVAR(Note_remove__doc__,
"Removes a note for an annotated object");
PyObject*
PyObject *
Note_remove(Note *self, PyObject* args)
{
char *ref = "refs/notes/commits";
int err = GIT_ERROR;
git_oid annotated_id;
Signature *py_author, *py_committer;
Oid *id;
if (!PyArg_ParseTuple(args, "O!O!|s",
&SignatureType, &py_author,
@ -53,12 +53,9 @@ Note_remove(Note *self, PyObject* args)
&ref))
return NULL;
err = git_oid_fromstr(&annotated_id, self->annotated_id);
if (err < 0)
return Error_set(err);
id = (Oid *) self->annotated_id;
err = git_note_remove(self->repo->repo, ref, py_author->signature,
py_committer->signature, &annotated_id);
py_committer->signature, &id->oid);
if (err < 0)
return Error_set(err);
@ -90,7 +87,7 @@ static void
Note_dealloc(Note *self)
{
Py_CLEAR(self->repo);
free(self->annotated_id);
Py_CLEAR(self->annotated_id);
git_note_free(self->note);
PyObject_Del(self);
}
@ -102,7 +99,7 @@ PyMethodDef Note_methods[] = {
};
PyMemberDef Note_members[] = {
MEMBER(Note, annotated_id, T_STRING, "id of the annotated object."),
MEMBER(Note, annotated_id, T_OBJECT, "id of the annotated object."),
{NULL}
};
@ -211,7 +208,7 @@ PyTypeObject NoteIterType = {
};
PyObject*
PyObject *
wrap_note(Repository* repo, git_oid* annotated_id, const char* ref)
{
Note* py_note = NULL;
@ -229,7 +226,7 @@ wrap_note(Repository* repo, git_oid* annotated_id, const char* ref)
py_note->repo = repo;
Py_INCREF(repo);
py_note->annotated_id = git_oid_allocfmt(annotated_id);
py_note->annotated_id = git_oid_to_python(annotated_id);
return (PyObject*) py_note;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -97,7 +97,7 @@ PyDoc_STRVAR(Object_type__doc__,
PyObject *
Object_type__get__(Object *self)
{
return PyLong_FromLong(git_object_type(self->obj));
return PyInt_FromLong(git_object_type(self->obj));
}
PyDoc_STRVAR(Object__pointer__doc__, "Get the object's pointer. For internal use only.");
@ -135,24 +135,6 @@ Object_read_raw(Object *self)
return aux;
}
static git_otype
py_type_to_git_type(PyTypeObject *py_type)
{
git_otype type = GIT_OBJ_BAD;
if (py_type == &CommitType) {
type = GIT_OBJ_COMMIT;
} else if (py_type == &TreeType) {
type = GIT_OBJ_TREE;
} else if (py_type == &BlobType) {
type = GIT_OBJ_BLOB;
} else if (py_type == &TagType) {
type = GIT_OBJ_TAG;
}
return type;
}
PyDoc_STRVAR(Object_peel__doc__,
"peel(target_type) -> Object\n"
"\n"
@ -161,23 +143,15 @@ PyDoc_STRVAR(Object_peel__doc__,
PyObject *
Object_peel(Object *self, PyObject *py_type)
{
int type = -1, err;
int err;
git_otype otype;
git_object *peeled;
if (PyLong_Check(py_type)) {
type = PyLong_AsLong(py_type);
if (type == -1 && PyErr_Occurred())
return NULL;
} else if (PyType_Check(py_type)) {
type = py_type_to_git_type((PyTypeObject *) py_type);
}
if (type == -1) {
PyErr_SetString(PyExc_ValueError, "invalid target type");
otype = py_object_to_otype(py_type);
if (otype == GIT_OBJ_BAD)
return NULL;
}
err = git_object_peel(&peeled, self->obj, (git_otype)type);
err = git_object_peel(&peeled, self->obj, otype);
if (err < 0)
return Error_set(err);

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -209,8 +209,10 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw)
Py_hash_t
Oid_hash(PyObject *oid)
{
/* TODO Randomize (use _Py_HashSecret) to avoid collission DoS attacks? */
return *(Py_hash_t*) ((Oid*)oid)->oid.id;
PyObject *py_oid = git_oid_to_py_str(&((Oid *)oid)->oid);
Py_hash_t ret = PyObject_Hash(py_oid);
Py_DECREF(py_oid);
return ret;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -65,10 +65,11 @@ option(PyObject *self, PyObject *args)
if (!py_option)
return NULL;
if (!PyLong_Check(py_option))
goto on_non_integer;
if (!PyInt_Check(py_option))
return Error_type_error(
"option should be an integer, got %.200s", py_option);
option = PyLong_AsLong(py_option);
option = PyInt_AsLong(py_option);
switch (option) {
case GIT_OPT_GET_SEARCH_PATH:
@ -79,11 +80,11 @@ option(PyObject *self, PyObject *args)
if (!py_level)
return NULL;
if (!PyLong_Check(py_level))
goto on_non_integer;
if (!PyInt_Check(py_level))
return Error_type_error(
"level should be an integer, got %.200s", py_level);
return get_search_path(PyLong_AsLong(py_level));
break;
return get_search_path(PyInt_AsLong(py_level));
}
case GIT_OPT_SET_SEARCH_PATH:
@ -100,23 +101,22 @@ option(PyObject *self, PyObject *args)
if (!py_path)
return NULL;
if (!PyLong_Check(py_level))
goto on_non_integer;
if (!PyInt_Check(py_level))
return Error_type_error(
"level should be an integer, got %.200s", py_level);
path = py_str_borrow_c_str(&tpath, py_path, NULL);
if (!path)
return NULL;
err = git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, PyLong_AsLong(py_level), path);
err = git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, PyInt_AsLong(py_level), path);
Py_DECREF(tpath);
if (err < 0) {
Error_set(err);
return NULL;
}
if (err < 0)
return Error_set(err);
Py_RETURN_NONE;
break;
}
case GIT_OPT_GET_MWINDOW_SIZE:
@ -124,14 +124,10 @@ option(PyObject *self, PyObject *args)
size_t size;
error = git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &size);
if (error < 0) {
Error_set(error);
return NULL;
}
if (error < 0)
return Error_set(error);
return PyLong_FromSize_t(size);
break;
return PyInt_FromSize_t(size);
}
case GIT_OPT_SET_MWINDOW_SIZE:
@ -143,25 +139,141 @@ option(PyObject *self, PyObject *args)
if (!py_size)
return NULL;
if (!PyLong_Check(py_size))
goto on_non_integer;
if (!PyInt_Check(py_size))
return Error_type_error(
"size should be an integer, got %.200s", py_size);
size = PyLong_AsSize_t(py_size);
size = PyInt_AsSize_t(py_size);
error = git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, size);
if (error < 0) {
Error_set(error);
return NULL;
}
if (error < 0)
return Error_set(error);
Py_RETURN_NONE;
break;
}
case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT:
{
size_t limit;
error = git_libgit2_opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, &limit);
if (error < 0)
return Error_set(error);
return PyInt_FromSize_t(limit);
}
case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT:
{
size_t limit;
PyObject *py_limit;
py_limit = PyTuple_GetItem(args, 1);
if (!py_limit)
return NULL;
if (PyInt_Check(py_limit)) {
limit = PyInt_AsSize_t(py_limit);
} else if (PyLong_Check(py_limit)) {
limit = PyLong_AsSize_t(py_limit);
} else {
return Error_type_error(
"limit should be an integer, got %.200s", py_limit);
}
error = git_libgit2_opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, limit);
if (error < 0)
return Error_set(error);
Py_RETURN_NONE;
}
case GIT_OPT_SET_CACHE_OBJECT_LIMIT:
{
size_t limit;
int object_type;
PyObject *py_object_type, *py_limit;
py_object_type = PyTuple_GetItem(args, 1);
if (!py_object_type)
return NULL;
py_limit = PyTuple_GetItem(args, 2);
if (!py_limit)
return NULL;
if (!PyInt_Check(py_limit))
return Error_type_error(
"limit should be an integer, got %.200s", py_limit);
object_type = PyInt_AsLong(py_object_type);
limit = PyInt_AsSize_t(py_limit);
error = git_libgit2_opts(
GIT_OPT_SET_CACHE_OBJECT_LIMIT, object_type, limit);
if (error < 0)
return Error_set(error);
Py_RETURN_NONE;
}
case GIT_OPT_SET_CACHE_MAX_SIZE:
{
size_t max_size;
PyObject *py_max_size;
py_max_size = PyTuple_GetItem(args, 1);
if (!py_max_size)
return NULL;
if (!PyInt_Check(py_max_size))
return Error_type_error(
"max_size should be an integer, got %.200s", py_max_size);
max_size = PyInt_AsSize_t(py_max_size);
error = git_libgit2_opts(GIT_OPT_SET_CACHE_MAX_SIZE, max_size);
if (error < 0)
return Error_set(error);
Py_RETURN_NONE;
}
case GIT_OPT_ENABLE_CACHING:
{
int flag;
PyObject *py_flag;
py_flag = PyTuple_GetItem(args, 1);
if (!PyInt_Check(py_flag))
return Error_type_error(
"flag should be an integer, got %.200s", py_flag);
flag = PyInt_AsSize_t(py_flag);
error = git_libgit2_opts(GIT_OPT_ENABLE_CACHING, flag);
if (error < 0)
return Error_set(error);
Py_RETURN_NONE;
}
case GIT_OPT_GET_CACHED_MEMORY:
{
size_t current;
size_t allowed;
PyObject* tup = PyTuple_New(2);
error = git_libgit2_opts(GIT_OPT_GET_CACHED_MEMORY, &current, &allowed);
if (error < 0)
return Error_set(error);
PyTuple_SetItem(tup, 0, PyInt_FromLong(current));
PyTuple_SetItem(tup, 1, PyInt_FromLong(allowed));
return tup;
}
}
PyErr_SetString(PyExc_ValueError, "unknown/unsupported option value");
return NULL;
on_non_integer:
PyErr_SetString(PyExc_TypeError, "option is not an integer");
return NULL;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

158
src/patch.c Normal file

@ -0,0 +1,158 @@
/*
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include "diff.h"
#include "error.h"
#include "oid.h"
#include "types.h"
#include "utils.h"
extern PyTypeObject DiffHunkType;
PyTypeObject PatchType;
PyObject *
wrap_patch(git_patch *patch)
{
Patch *py_patch;
PyObject *py_hunk;
size_t i, hunk_amounts;
if (!patch)
Py_RETURN_NONE;
py_patch = PyObject_New(Patch, &PatchType);
if (py_patch) {
py_patch->patch = patch;
hunk_amounts = git_patch_num_hunks(patch);
py_patch->hunks = PyList_New(hunk_amounts);
for (i = 0; i < hunk_amounts; ++i) {
py_hunk = wrap_diff_hunk(patch, i);
if (py_hunk)
PyList_SetItem((PyObject*) py_patch->hunks, i, py_hunk);
}
}
return (PyObject*) py_patch;
}
static void
Patch_dealloc(Patch *self)
{
Py_CLEAR(self->hunks);
git_patch_free(self->patch);
PyObject_Del(self);
}
PyDoc_STRVAR(Patch_delta__doc__, "Get the delta associated with a patch.");
PyObject *
Patch_delta__get__(Patch *self)
{
if (!self->patch)
Py_RETURN_NONE;
return wrap_diff_delta(git_patch_get_delta(self->patch));
}
PyDoc_STRVAR(Patch_line_stats__doc__,
"Get line counts of each type in a patch.");
PyObject *
Patch_line_stats__get__(Patch *self)
{
size_t context, additions, deletions;
int err;
if (!self->patch)
Py_RETURN_NONE;
err = git_patch_line_stats(&context, &additions, &deletions,
self->patch);
if (err < 0)
return Error_set(err);
return Py_BuildValue("III", context, additions, deletions);
}
PyMemberDef Patch_members[] = {
MEMBER(Patch, hunks, T_OBJECT, "hunks"),
{NULL}
};
PyGetSetDef Patch_getseters[] = {
GETTER(Patch, delta),
GETTER(Patch, line_stats),
{NULL}
};
PyDoc_STRVAR(Patch__doc__, "Diff patch object.");
PyTypeObject PatchType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.Patch", /* tp_name */
sizeof(Patch), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Patch_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
Patch__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
Patch_members, /* tp_members */
Patch_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -25,14 +25,13 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDE_pygit2_blame_h
#define INCLUDE_pygit2_blame_h
#ifndef INCLUDE_pygit2_patch_h
#define INCLUDE_pygit2_patch_h
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <git2.h>
#include "types.h"
PyObject* wrap_blame(git_blame *blame, Repository *repo);
PyObject* wrap_patch(git_patch *patch);
#endif

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -44,8 +44,12 @@ extern PyTypeObject ObjectType;
extern PyTypeObject CommitType;
extern PyTypeObject DiffType;
extern PyTypeObject DiffIterType;
extern PyTypeObject DiffDeltaType;
extern PyTypeObject DiffFileType;
extern PyTypeObject DiffHunkType;
extern PyTypeObject DiffLineType;
extern PyTypeObject DiffStatsType;
extern PyTypeObject PatchType;
extern PyTypeObject HunkType;
extern PyTypeObject TreeType;
extern PyTypeObject TreeBuilderType;
extern PyTypeObject TreeEntryType;
@ -62,10 +66,6 @@ extern PyTypeObject RemoteType;
extern PyTypeObject RefspecType;
extern PyTypeObject NoteType;
extern PyTypeObject NoteIterType;
extern PyTypeObject BlameType;
extern PyTypeObject BlameIterType;
extern PyTypeObject BlameHunkType;
PyDoc_STRVAR(discover_repository__doc__,
@ -144,7 +144,44 @@ hash(PyObject *self, PyObject *args)
}
PyDoc_STRVAR(init_file_backend__doc__,
"init_file_backend(path) -> object\n"
"\n"
"open repo backend given path.");
PyObject *
init_file_backend(PyObject *self, PyObject *args)
{
const char* path = NULL;
int err = GIT_OK;
git_repository *repository = NULL;
if (!PyArg_ParseTuple(args, "s", &path)) {
return NULL;
}
err = git_repository_open(&repository, path);
if (err < 0) {
Error_set_str(err, path);
goto cleanup;
}
return PyCapsule_New(repository, "backend", NULL);
cleanup:
if (repository) {
git_repository_free(repository);
}
if (err == GIT_ENOTFOUND) {
PyErr_Format(GitError, "Repository not found at %s", path);
}
return NULL;
}
PyMethodDef module_methods[] = {
{"init_file_backend", init_file_backend, METH_VARARGS,
init_file_backend__doc__},
{"discover_repository", discover_repository, METH_VARARGS,
discover_repository__doc__},
{"hashfile", hashfile, METH_VARARGS, hashfile__doc__},
@ -153,7 +190,7 @@ PyMethodDef module_methods[] = {
{NULL}
};
PyObject*
PyObject *
moduleinit(PyObject* m)
{
if (m == NULL)
@ -170,6 +207,12 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_OPT_SET_SEARCH_PATH);
ADD_CONSTANT_INT(m, GIT_OPT_GET_MWINDOW_SIZE);
ADD_CONSTANT_INT(m, GIT_OPT_SET_MWINDOW_SIZE);
ADD_CONSTANT_INT(m, GIT_OPT_GET_MWINDOW_MAPPED_LIMIT);
ADD_CONSTANT_INT(m, GIT_OPT_SET_MWINDOW_MAPPED_LIMIT);
ADD_CONSTANT_INT(m, GIT_OPT_SET_CACHE_OBJECT_LIMIT);
ADD_CONSTANT_INT(m, GIT_OPT_GET_CACHED_MEMORY);
ADD_CONSTANT_INT(m, GIT_OPT_ENABLE_CACHING);
ADD_CONSTANT_INT(m, GIT_OPT_SET_CACHE_MAX_SIZE);
/* Errors */
GitError = PyErr_NewException("_pygit2.GitError", NULL, NULL);
@ -214,7 +257,6 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_OBJ_BLOB)
ADD_CONSTANT_INT(m, GIT_OBJ_TAG)
/* Valid modes for index and tree entries. */
ADD_CONSTANT_INT(m, GIT_FILEMODE_NEW)
ADD_CONSTANT_INT(m, GIT_FILEMODE_TREE)
ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB)
ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB_EXECUTABLE)
@ -261,6 +303,7 @@ moduleinit(PyObject* m)
ADD_TYPE(m, Branch)
ADD_CONSTANT_INT(m, GIT_BRANCH_LOCAL)
ADD_CONSTANT_INT(m, GIT_BRANCH_REMOTE)
ADD_CONSTANT_INT(m, GIT_BRANCH_ALL)
/*
* Index & Working copy
@ -274,10 +317,11 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_STATUS_WT_MODIFIED)
ADD_CONSTANT_INT(m, GIT_STATUS_WT_DELETED)
ADD_CONSTANT_INT(m, GIT_STATUS_IGNORED) /* Flags for ignored files */
ADD_CONSTANT_INT(m, GIT_STATUS_CONFLICTED)
/* Different checkout strategies */
ADD_CONSTANT_INT(m, GIT_CHECKOUT_NONE)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_SAFE)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_SAFE_CREATE)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_RECREATE_MISSING)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_FORCE)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_ALLOW_CONFLICTS)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_REMOVE_UNTRACKED)
@ -292,11 +336,19 @@ moduleinit(PyObject* m)
*/
INIT_TYPE(DiffType, NULL, NULL)
INIT_TYPE(DiffIterType, NULL, NULL)
INIT_TYPE(DiffDeltaType, NULL, NULL)
INIT_TYPE(DiffFileType, NULL, NULL)
INIT_TYPE(DiffHunkType, NULL, NULL)
INIT_TYPE(DiffLineType, NULL, NULL)
INIT_TYPE(DiffStatsType, NULL, NULL)
INIT_TYPE(PatchType, NULL, NULL)
INIT_TYPE(HunkType, NULL, NULL)
ADD_TYPE(m, Diff)
ADD_TYPE(m, DiffDelta)
ADD_TYPE(m, DiffFile)
ADD_TYPE(m, DiffHunk)
ADD_TYPE(m, DiffLine)
ADD_TYPE(m, DiffStats)
ADD_TYPE(m, Patch)
ADD_TYPE(m, Hunk)
ADD_CONSTANT_INT(m, GIT_DIFF_NORMAL)
ADD_CONSTANT_INT(m, GIT_DIFF_REVERSE)
ADD_CONSTANT_INT(m, GIT_DIFF_FORCE_TEXT)
@ -315,9 +367,15 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_CASE)
ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_UNTRACKED_CONTENT)
ADD_CONSTANT_INT(m, GIT_DIFF_SKIP_BINARY_CHECK)
ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_BINARY)
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE)
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE_TREES)
ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_IGNORED_DIRS)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_NONE)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_FULL)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_SHORT)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_NUMBER)
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_INCLUDE_SUMMARY)
/* Flags for diff find similar */
/* --find-renames */
ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES)
@ -330,6 +388,23 @@ moduleinit(PyObject* m)
/* --break-rewrites=/M */
ADD_CONSTANT_INT(m, GIT_DIFF_FIND_AND_BREAK_REWRITES)
/* DiffDelta and DiffFile flags */
ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_BINARY)
ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_NOT_BINARY)
ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_VALID_ID)
/* DiffDelta.status */
ADD_CONSTANT_INT(m, GIT_DELTA_UNMODIFIED)
ADD_CONSTANT_INT(m, GIT_DELTA_ADDED)
ADD_CONSTANT_INT(m, GIT_DELTA_DELETED)
ADD_CONSTANT_INT(m, GIT_DELTA_MODIFIED)
ADD_CONSTANT_INT(m, GIT_DELTA_RENAMED)
ADD_CONSTANT_INT(m, GIT_DELTA_COPIED)
ADD_CONSTANT_INT(m, GIT_DELTA_IGNORED)
ADD_CONSTANT_INT(m, GIT_DELTA_UNTRACKED)
ADD_CONSTANT_INT(m, GIT_DELTA_TYPECHANGE)
ADD_CONSTANT_INT(m, GIT_DELTA_UNREADABLE)
/* Config */
ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_LOCAL);
ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_GLOBAL);
@ -337,11 +412,6 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_SYSTEM);
/* Blame */
INIT_TYPE(BlameType, NULL, NULL)
INIT_TYPE(BlameIterType, NULL, NULL)
INIT_TYPE(BlameHunkType, NULL, NULL)
ADD_TYPE(m, Blame)
ADD_TYPE(m, BlameHunk)
ADD_CONSTANT_INT(m, GIT_BLAME_NORMAL)
ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_FILE)
ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES)
@ -355,8 +425,21 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_FASTFORWARD)
ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_UNBORN)
/* Describe */
ADD_CONSTANT_INT(m, GIT_DESCRIBE_DEFAULT);
ADD_CONSTANT_INT(m, GIT_DESCRIBE_TAGS);
ADD_CONSTANT_INT(m, GIT_DESCRIBE_ALL);
/* Stash */
ADD_CONSTANT_INT(m, GIT_STASH_DEFAULT);
ADD_CONSTANT_INT(m, GIT_STASH_KEEP_INDEX);
ADD_CONSTANT_INT(m, GIT_STASH_INCLUDE_UNTRACKED);
ADD_CONSTANT_INT(m, GIT_STASH_INCLUDE_IGNORED);
ADD_CONSTANT_INT(m, GIT_STASH_APPLY_DEFAULT);
ADD_CONSTANT_INT(m, GIT_STASH_APPLY_REINSTATE_INDEX);
/* Global initialization of libgit2 */
git_threads_init();
git_libgit2_init();
return m;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -60,8 +60,8 @@ RefLogIter_iternext(RefLogIter *self)
entry = git_reflog_entry_byindex(self->reflog, self->i);
py_entry = PyObject_New(RefLogEntry, &RefLogEntryType);
py_entry->oid_old = git_oid_allocfmt(git_reflog_entry_id_old(entry));
py_entry->oid_new = git_oid_allocfmt(git_reflog_entry_id_new(entry));
py_entry->oid_old = git_oid_to_python(git_reflog_entry_id_old(entry));
py_entry->oid_new = git_oid_to_python(git_reflog_entry_id_new(entry));
py_entry->message = strdup(git_reflog_entry_message(entry));
err = git_signature_dup(&py_entry->signature,
git_reflog_entry_committer(entry));
@ -163,7 +163,7 @@ Reference_rename(Reference *self, PyObject *py_name)
return NULL;
/* Rename */
err = git_reference_rename(&new_reference, self->reference, c_name, 0, NULL, NULL);
err = git_reference_rename(&new_reference, self->reference, c_name, 0, NULL);
git_reference_free(self->reference);
free(c_name);
if (err < 0)
@ -205,10 +205,7 @@ Reference_resolve(Reference *self, PyObject *args)
PyDoc_STRVAR(Reference_target__doc__,
"The reference target: If direct the value will be an Oid object, if it\n"
"is symbolic it will be an string with the full name of the target\n"
"reference.\n"
"\n"
"The target is writable. Setting the Reference's target to another Oid\n"
"object will direct the reference to that Oid instead.");
"reference.\n");
PyObject *
Reference_target__get__(Reference *self)
@ -230,48 +227,72 @@ Reference_target__get__(Reference *self)
return to_path(c_name);
}
int
Reference_target__set__(Reference *self, PyObject *py_target)
PyDoc_STRVAR(Reference_set_target__doc__,
"set_target(target, [message])\n"
"\n"
"Set the target of this reference.\n"
"\n"
"Update the reference using the given signature and message.\n"
"These will be used to fill the reflog entry which will be created\n"
"as a result of this update\n"
"\n"
"Arguments:\n"
"\n"
"target\n"
" The new target for this reference\n"
"message\n"
" Message to use for the reflog.\n");
PyObject *
Reference_set_target(Reference *self, PyObject *args, PyObject *kwds)
{
git_oid oid;
char *c_name;
int err;
git_reference *new_ref;
PyObject *py_target = NULL;
const char *message = NULL;
char *keywords[] = {"target", "message", NULL};
CHECK_REFERENCE_INT(self);
CHECK_REFERENCE(self);
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s", keywords,
&py_target, &message))
return NULL;
/* Case 1: Direct */
if (GIT_REF_OID == git_reference_type(self->reference)) {
err = py_oid_to_git_oid_expand(self->repo->repo, py_target, &oid);
if (err < 0)
return err;
goto error;
err = git_reference_set_target(&new_ref, self->reference, &oid, NULL, NULL);
err = git_reference_set_target(&new_ref, self->reference, &oid, message);
if (err < 0)
goto error;
git_reference_free(self->reference);
self->reference = new_ref;
return 0;
Py_RETURN_NONE;
}
/* Case 2: Symbolic */
c_name = py_path_to_c_str(py_target);
if (c_name == NULL)
return -1;
return NULL;
err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, NULL, NULL);
err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, message);
free(c_name);
if (err < 0)
goto error;
git_reference_free(self->reference);
self->reference = new_ref;
return 0;
Py_RETURN_NONE;
error:
Error_set(err);
return -1;
return NULL;
}
@ -304,7 +325,7 @@ Reference_type__get__(Reference *self)
CHECK_REFERENCE(self);
c_type = git_reference_type(self->reference);
return PyLong_FromLong(c_type);
return PyInt_FromLong(c_type);
}
@ -335,93 +356,50 @@ Reference_log(Reference *self)
return (PyObject*)iter;
}
PyDoc_STRVAR(Reference_log_append__doc__,
"log_append(oid, committer, message[, encoding])\n"
"\n"
"Append a reflog entry to the reference. If the oid is None then keep\n"
"the current reference's oid. The message parameter may be None.");
PyObject *
Reference_log_append(Reference *self, PyObject *args)
{
git_signature *committer;
const char *message = NULL;
git_reflog *reflog;
git_oid oid;
const git_oid *ref_oid;
int err;
PyObject *py_oid = NULL;
Signature *py_committer;
PyObject *py_message = NULL;
char *encoding = NULL;
git_repository *repo;
CHECK_REFERENCE(self);
/* Input parameters */
if (!PyArg_ParseTuple(args, "OO!O|s", &py_oid,
&SignatureType, &py_committer,
&py_message, &encoding))
return NULL;
if (py_oid == Py_None)
ref_oid = git_reference_target(self->reference);
else {
err = py_oid_to_git_oid_expand(self->repo->repo, py_oid, &oid);
if (err < 0)
return NULL;
ref_oid = &oid;
}
if (py_message != Py_None) {
message = py_str_to_c_str(py_message, encoding);
if (message == NULL)
return NULL;
}
/* Go */
repo = git_reference_owner(self->reference);
err = git_reflog_read(&reflog, repo, git_reference_name(self->reference));
if (err < 0) {
free((void *)message);
return NULL;
}
committer = (git_signature *)py_committer->signature;
err = git_reflog_append(reflog, ref_oid, committer, message);
if (!err)
err = git_reflog_write(reflog);
git_reflog_free(reflog);
free((void *)message);
if (err < 0)
return NULL;
Py_RETURN_NONE;
}
PyDoc_STRVAR(Reference_get_object__doc__,
"get_object() -> object\n"
"\n"
"Retrieves the object the current reference is pointing to.");
"Retrieves the object the current reference is pointing to.\n"
"\n"
"This method is deprecated, please use Reference.peel() instead.");
PyObject *
Reference_get_object(Reference *self)
{
return PyObject_CallMethod((PyObject *) self, "peel", NULL);
}
PyDoc_STRVAR(Reference_peel__doc__,
"peel(type=None) -> object\n"
"\n"
"Retrieve an object of the given type by recursive peeling.\n"
"\n"
"If no type is provided, the first non-tag object will be returned.");
PyObject *
Reference_peel(Reference *self, PyObject *args)
{
int err;
git_object* obj;
git_otype otype;
git_object *obj;
PyObject *py_type = Py_None;
CHECK_REFERENCE(self);
err = git_reference_peel(&obj, self->reference, GIT_OBJ_ANY);
if (!PyArg_ParseTuple(args, "|O", &py_type))
return NULL;
otype = py_object_to_otype(py_type);
if (otype == GIT_OBJ_BAD)
return NULL;
err = git_reference_peel(&obj, self->reference, otype);
if (err < 0)
return Error_set(err);
return wrap_object(obj, self->repo);
}
PyDoc_STRVAR(RefLogEntry_committer__doc__, "Committer.");
PyObject *
@ -446,16 +424,16 @@ RefLogEntry_init(RefLogEntry *self, PyObject *args, PyObject *kwds)
static void
RefLogEntry_dealloc(RefLogEntry *self)
{
free(self->oid_old);
free(self->oid_new);
Py_CLEAR(self->oid_old);
Py_CLEAR(self->oid_new);
free(self->message);
git_signature_free(self->signature);
PyObject_Del(self);
}
PyMemberDef RefLogEntry_members[] = {
MEMBER(RefLogEntry, oid_new, T_STRING, "New oid."),
MEMBER(RefLogEntry, oid_old, T_STRING, "Old oid."),
MEMBER(RefLogEntry, oid_new, T_OBJECT, "New oid."),
MEMBER(RefLogEntry, oid_old, T_OBJECT, "Old oid."),
MEMBER(RefLogEntry, message, T_STRING, "Message."),
{NULL}
};
@ -514,15 +492,16 @@ PyMethodDef Reference_methods[] = {
METHOD(Reference, rename, METH_O),
METHOD(Reference, resolve, METH_NOARGS),
METHOD(Reference, log, METH_NOARGS),
METHOD(Reference, log_append, METH_VARARGS),
METHOD(Reference, get_object, METH_NOARGS),
METHOD(Reference, set_target, METH_VARARGS | METH_KEYWORDS),
METHOD(Reference, peel, METH_VARARGS),
{NULL}
};
PyGetSetDef Reference_getseters[] = {
GETTER(Reference, name),
GETTER(Reference, shorthand),
GETSET(Reference, target),
GETTER(Reference, target),
GETTER(Reference, type),
{NULL}
};

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -36,7 +36,6 @@
#include "note.h"
#include "repository.h"
#include "branch.h"
#include "blame.h"
#include "signature.h"
#include <git2/odb_backend.h>
@ -56,6 +55,9 @@ extern PyTypeObject ReferenceType;
extern PyTypeObject NoteType;
extern PyTypeObject NoteIterType;
/* forward-declaration for Repsository._from_c() */
PyTypeObject RepositoryType;
git_otype
int_to_loose_object_type(int type_id)
{
@ -68,11 +70,25 @@ int_to_loose_object_type(int type_id)
}
}
PyObject *
wrap_repository(git_repository *c_repo)
{
Repository *py_repo = PyObject_GC_New(Repository, &RepositoryType);
if (py_repo) {
py_repo->repo = c_repo;
py_repo->config = NULL;
py_repo->index = NULL;
py_repo->owned = 1;
}
return (PyObject *)py_repo;
}
int
Repository_init(Repository *self, PyObject *args, PyObject *kwds)
{
char *path;
int err;
PyObject *backend;
if (kwds && PyDict_Size(kwds) > 0) {
PyErr_SetString(PyExc_TypeError,
@ -80,28 +96,72 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds)
return -1;
}
if (!PyArg_ParseTuple(args, "s", &path))
return -1;
err = git_repository_open(&self->repo, path);
if (err < 0) {
Error_set_str(err, path);
if (!PyArg_ParseTuple(args, "O", &backend)) {
return -1;
}
self->repo = PyCapsule_GetPointer(backend, "backend");
if (self->repo == NULL) {
PyErr_SetString(PyExc_TypeError,
"Repository unable to unpack backend.");
return -1;
}
self->owned = 1;
self->config = NULL;
self->index = NULL;
return 0;
}
PyDoc_STRVAR(Repository__from_c__doc__, "Init a Repository from a pointer. For internal use only.");
PyObject *
Repository__from_c(Repository *py_repo, PyObject *args)
{
PyObject *py_pointer, *py_free;
char *buffer;
Py_ssize_t len;
int err;
py_repo->repo = NULL;
py_repo->config = NULL;
py_repo->index = NULL;
if (!PyArg_ParseTuple(args, "OO!", &py_pointer, &PyBool_Type, &py_free))
return NULL;
err = PyBytes_AsStringAndSize(py_pointer, &buffer, &len);
if (err < 0)
return NULL;
if (len != sizeof(git_repository *)) {
PyErr_SetString(PyExc_TypeError, "invalid pointer length");
return NULL;
}
py_repo->repo = *((git_repository **) buffer);
py_repo->owned = py_free == Py_True;
Py_RETURN_NONE;
}
PyDoc_STRVAR(Repository__disown__doc__, "Mark the object as not-owned by us. For internal use only.");
PyObject *
Repository__disown(Repository *py_repo)
{
py_repo->owned = 0;
Py_RETURN_NONE;
}
void
Repository_dealloc(Repository *self)
{
PyObject_GC_UnTrack(self);
Py_CLEAR(self->index);
Py_CLEAR(self->config);
git_repository_free(self->repo);
if (self->owned)
git_repository_free(self->repo);
Py_TYPE(self)->tp_free(self);
}
@ -178,38 +238,6 @@ Repository_head__get__(Repository *self)
return wrap_reference(head, self);
}
int
Repository_head__set__(Repository *self, PyObject *py_val)
{
int err;
if (PyObject_TypeCheck(py_val, &OidType)) {
git_oid oid;
py_oid_to_git_oid(py_val, &oid);
err = git_repository_set_head_detached(self->repo, &oid, NULL, NULL);
if (err < 0) {
Error_set(err);
return -1;
}
} else {
const char *refname;
PyObject *trefname;
refname = py_str_borrow_c_str(&trefname, py_val, NULL);
if (refname == NULL)
return -1;
err = git_repository_set_head(self->repo, refname, NULL, NULL);
Py_DECREF(trefname);
if (err < 0) {
Error_set_str(err, refname);
return -1;
}
}
return 0;
}
PyDoc_STRVAR(Repository_head_is_detached__doc__,
"A repository's HEAD is detached when it points directly to a commit\n"
"instead of a branch.");
@ -293,9 +321,11 @@ Repository_git_object_lookup_prefix(Repository *self, PyObject *key)
PyDoc_STRVAR(Repository_lookup_branch__doc__,
"lookup_branch(branch_name, [branch_type]) -> Object\n"
"lookup_branch(branch_name, [branch_type]) -> Branch\n"
"\n"
"Returns the Git reference for the given branch name (local or remote).");
"Returns the Git reference for the given branch name (local or remote).\n"
"If branch_type is GIT_BRANCH_REMOTE, you must include the remote name\n"
"in the branch name (eg 'origin/master').");
PyObject *
Repository_lookup_branch(Repository *self, PyObject *args)
@ -319,6 +349,26 @@ Repository_lookup_branch(Repository *self, PyObject *args)
}
PyDoc_STRVAR(Repository_path_is_ignored__doc__,
"Check if a path is ignored in the repository.");
PyObject *
Repository_path_is_ignored(Repository *self, PyObject *args)
{
int ignored;
char *path;
if (!PyArg_ParseTuple(args, "s", &path))
return NULL;
git_ignore_path_is_ignored(&ignored, self->repo, path);
if (ignored == 1)
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
PyDoc_STRVAR(Repository_revparse_single__doc__,
"revparse_single(revision) -> Object\n"
"\n"
@ -511,7 +561,8 @@ Repository_workdir__set__(Repository *self, PyObject *py_workdir)
PyDoc_STRVAR(Repository_merge_base__doc__,
"merge_base(oid, oid) -> Oid\n"
"\n"
"Find as good common ancestors as possible for a merge.");
"Find as good common ancestors as possible for a merge.\n"
"Returns None if there is no merge base between the commits");
PyObject *
Repository_merge_base(Repository *self, PyObject *args)
@ -535,6 +586,10 @@ Repository_merge_base(Repository *self, PyObject *args)
return NULL;
err = git_merge_base(&oid, self->repo, &oid1, &oid2);
if (err == GIT_ENOTFOUND)
Py_RETURN_NONE;
if (err < 0)
return Error_set(err);
@ -548,7 +603,7 @@ PyDoc_STRVAR(Repository_merge_analysis__doc__,
"them into the HEAD of the repository\n"
"\n"
"The first returned value is a mixture of the GIT_MERGE_ANALYSIS_NONE, _NORMAL,\n"
" _UP_TO_DATE, _FASTFORWARD and _UNBORN flags.\n"
"_UP_TO_DATE, _FASTFORWARD and _UNBORN flags.\n"
"The second value is the user's preference from 'merge.ff'");
PyObject *
@ -557,7 +612,7 @@ Repository_merge_analysis(Repository *self, PyObject *py_id)
int err;
size_t len;
git_oid id;
git_merge_head *merge_head;
git_annotated_commit *commit;
git_merge_analysis_t analysis;
git_merge_preference_t preference;
@ -565,12 +620,12 @@ Repository_merge_analysis(Repository *self, PyObject *py_id)
if (len == 0)
return NULL;
err = git_merge_head_from_id(&merge_head, self->repo, &id);
err = git_annotated_commit_lookup(&commit, self->repo, &id);
if (err < 0)
return Error_set(err);
err = git_merge_analysis(&analysis, &preference, self->repo, (const git_merge_head **) &merge_head, 1);
git_merge_head_free(merge_head);
err = git_merge_analysis(&analysis, &preference, self->repo, (const git_annotated_commit **) &commit, 1);
git_annotated_commit_free(commit);
if (err < 0)
return Error_set(err);
@ -592,7 +647,7 @@ PyDoc_STRVAR(Repository_merge__doc__,
PyObject *
Repository_merge(Repository *self, PyObject *py_oid)
{
git_merge_head *oid_merge_head;
git_annotated_commit *commit;
git_oid oid;
int err;
size_t len;
@ -603,15 +658,56 @@ Repository_merge(Repository *self, PyObject *py_oid)
if (len == 0)
return NULL;
err = git_merge_head_from_id(&oid_merge_head, self->repo, &oid);
err = git_annotated_commit_lookup(&commit, self->repo, &oid);
if (err < 0)
return Error_set(err);
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
err = git_merge(self->repo,
(const git_merge_head **)&oid_merge_head, 1,
(const git_annotated_commit **)&commit, 1,
&merge_opts, &checkout_opts);
git_merge_head_free(oid_merge_head);
git_annotated_commit_free(commit);
if (err < 0)
return Error_set(err);
Py_RETURN_NONE;
}
PyDoc_STRVAR(Repository_cherrypick__doc__,
"cherrypick(id)\n"
"\n"
"Cherry-pick the given oid, producing changes in the index and working directory.\n"
"\n"
"Merges the given commit into HEAD as a cherrypick, writing the results into the\n"
"working directory. Any changes are staged for commit and any conflicts\n"
"are written to the index. Callers should inspect the repository's\n"
"index after this completes, resolve any conflicts and prepare a\n"
"commit.");
PyObject *
Repository_cherrypick(Repository *self, PyObject *py_oid)
{
git_commit *commit;
git_oid oid;
int err;
size_t len;
git_cherrypick_options cherrypick_opts = GIT_CHERRYPICK_OPTIONS_INIT;
len = py_oid_to_git_oid(py_oid, &oid);
if (len == 0)
return NULL;
err = git_commit_lookup(&commit, self->repo, &oid);
if (err < 0)
return Error_set(err);
cherrypick_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
err = git_cherrypick(self->repo,
commit,
(const git_cherrypick_options *)&cherrypick_opts);
git_commit_free(commit);
if (err < 0)
return Error_set(err);
@ -766,8 +862,88 @@ Repository_create_blob_fromdisk(Repository *self, PyObject *args)
}
#define BUFSIZE 4096
PyDoc_STRVAR(Repository_create_blob_fromiobase__doc__,
"create_blob_fromiobase(io.IOBase) -> Oid\n"
"\n"
"Create a new blob from an IOBase object.");
PyObject *
Repository_create_blob_fromiobase(Repository *self, PyObject *py_file)
{
git_writestream *stream;
git_oid oid;
PyObject *py_is_readable;
int is_readable;
int err;
py_is_readable = PyObject_CallMethod(py_file, "readable", NULL);
if (!py_is_readable) {
if (PyErr_ExceptionMatches(PyExc_AttributeError))
PyErr_SetObject(PyExc_TypeError, py_file);
return NULL;
}
is_readable = PyObject_IsTrue(py_is_readable);
Py_DECREF(py_is_readable);
if (!is_readable) {
Py_DECREF(py_file);
PyErr_SetString(PyExc_TypeError, "expected readable IO type");
return NULL;
}
err = git_blob_create_fromstream(&stream, self->repo, NULL);
if (err < 0)
return Error_set(err);
for (;;) {
PyObject *py_bytes;
char *bytes;
Py_ssize_t size;
py_bytes = PyObject_CallMethod(py_file, "read", "i", 4096);
if (!py_bytes)
return NULL;
if (py_bytes == Py_None) {
Py_DECREF(py_bytes);
goto cleanup;
}
if (PyBytes_AsStringAndSize(py_bytes, &bytes, &size)) {
Py_DECREF(py_bytes);
return NULL;
}
if (size == 0) {
Py_DECREF(py_bytes);
break;
}
err = stream->write(stream, bytes, size);
Py_DECREF(py_bytes);
if (err < 0)
goto cleanup;
}
cleanup:
if (err < 0) {
stream->free(stream);
return Error_set(err);
}
err = git_blob_create_fromstream_commit(&oid, stream);
if (err < 0)
return Error_set(err);
return git_oid_to_python(&oid);
}
PyDoc_STRVAR(Repository_create_commit__doc__,
"create_commit(reference, author, committer, message, tree, parents[, encoding]) -> Oid\n"
"create_commit(reference_name, author, committer, message, tree, parents[, encoding]) -> Oid\n"
"\n"
"Create a new commit object, return its oid.");
@ -893,7 +1069,7 @@ Repository_create_tag(Repository *self, PyObject *args)
PyDoc_STRVAR(Repository_create_branch__doc__,
"create_branch(name, commit, force=False) -> bytes\n"
"create_branch(name, commit, force=False) -> Branch\n"
"\n"
"Create a new branch \"name\" which points to a commit.\n"
"\n"
@ -918,7 +1094,7 @@ Repository_create_branch(Repository *self, PyObject *args)
if (!PyArg_ParseTuple(args, "sO!|i", &c_name, &CommitType, &py_commit, &force))
return NULL;
err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force, NULL, NULL);
err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force);
if (err < 0)
return Error_set(err);
@ -965,10 +1141,66 @@ out:
}
PyDoc_STRVAR(Repository_listall_branches__doc__,
"listall_branches([flags]) -> [str, ...]\n"
PyDoc_STRVAR(Repository_listall_reference_objects__doc__,
"listall_reference_objects() -> [Reference, ...]\n"
"\n"
"Return a tuple with all the branches in the repository.");
"Return a list with all the reference objects in the repository.");
PyObject *
Repository_listall_reference_objects(Repository *self, PyObject *args)
{
git_reference_iterator *iter;
git_reference *ref = NULL;
int err;
PyObject *list;
list = PyList_New(0);
if (list == NULL)
return NULL;
if ((err = git_reference_iterator_new(&iter, self->repo)) < 0)
return Error_set(err);
while ((err = git_reference_next(&ref, iter)) == 0) {
PyObject *py_ref = wrap_reference(ref, self);
if (py_ref == NULL)
goto error;
err = PyList_Append(list, py_ref);
Py_DECREF(py_ref);
if (err < 0)
goto error;
}
git_reference_iterator_free(iter);
if (err == GIT_ITEROVER)
err = 0;
if (err < 0) {
Py_CLEAR(list);
return Error_set(err);
}
return list;
error:
git_reference_iterator_free(iter);
Py_CLEAR(list);
return NULL;
}
PyDoc_STRVAR(Repository_listall_branches__doc__,
"listall_branches([flag]) -> [str, ...]\n"
"\n"
"Return a list with all the branches in the repository.\n"
"\n"
"The *flag* may be:\n"
"\n"
"- GIT_BRANCH_LOCAL - return all local branches (set by default)\n"
"- GIT_BRANCH_REMOTE - return all remote-tracking branches\n"
"- GIT_BRANCH_ALL - return local branches and remote-tracking branches");
PyObject *
Repository_listall_branches(Repository *self, PyObject *args)
@ -1022,6 +1254,173 @@ error:
return NULL;
}
PyDoc_STRVAR(Repository_listall_submodules__doc__,
"listall_submodules() -> [str, ...]\n"
"\n"
"Return a list with all submodule paths in the repository.\n");
static int foreach_path_cb(git_submodule *submodule, const char *name, void *payload)
{
PyObject *list = (PyObject *)payload;
PyObject *path = to_unicode(git_submodule_path(submodule), NULL, NULL);
return PyList_Append(list, path);
}
PyObject *
Repository_listall_submodules(Repository *self, PyObject *args)
{
int err;
PyObject *list;
list = PyList_New(0);
if (list == NULL)
return NULL;
err = git_submodule_foreach(self->repo, foreach_path_cb, list);
if (err != 0) {
Py_DECREF(list);
return Py_None;
}
return list;
}
PyDoc_STRVAR(Repository_init_submodules__doc__,
"init_submodule(submodules=None, overwrite=False)\n"
"\n"
"Initialize all submodules in repository.\n"
"submodules: List of submodules to initialize. Default argument initializes all submodules.\n"
"overwrite: Flag indicating if initialization should overwrite submodule entries.\n");
static int foreach_sub_init_cb(git_submodule *submodule, const char *name, void *payload)
{
return git_submodule_init(submodule, *(int*)payload);
}
PyObject *
Repository_init_submodules(Repository* self, PyObject *args, PyObject *kwds)
{
PyObject *list = Py_None;
PyObject *oflag = Py_False;
char *kwlist[] = {"submodules", "overwrite", NULL};
int err, fflag;
PyObject *iter, *subpath, *next;
const char *c_subpath;
git_submodule *submodule;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &list, &oflag))
return NULL;
fflag = PyObject_IsTrue(oflag);
if (fflag != 0 && fflag != 1)
fflag = 0;
//Init all submodules listed in repository
if (list == Py_None) {
err = git_submodule_foreach(self->repo, foreach_sub_init_cb, &fflag);
if (err != 0)
return Error_set(err);
Py_RETURN_NONE;
}
iter = PyObject_GetIter(list);
if (!iter)
return NULL;
while (1) {
next = PyIter_Next(iter);
if (!next)
break;
c_subpath = py_str_borrow_c_str(&subpath, next, NULL);
git_submodule_lookup(&submodule, self->repo, c_subpath);
Py_DECREF(subpath);
if (!submodule) {
PyErr_SetString(PyExc_KeyError,
"Submodule does not exist");
return NULL;
}
err = git_submodule_init(submodule, fflag);
if (err != 0) {
return Error_set(err);
}
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(Repository_update_submodules__doc__,
"update_submodules(submodules=None, init=False)\n"
"\n"
"Updates the specified submodules, or all if None are specified\n"
"init: Flag indicating if submodules should be automatically initialized if necessary.\n");
static int foreach_sub_update_cb(git_submodule *submodule, const char *name, void *payload)
{
git_submodule_update_options opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
return git_submodule_update(submodule, *(int*)payload, &opts);
}
PyObject *
Repository_update_submodules(Repository *self, PyObject *args, PyObject *kwds)
{
PyObject *list = Py_None;
PyObject *py_init = Py_False;
PyObject *iter, *next, *subpath;
int init, err;
const char *c_subpath;
git_submodule *submodule;
git_submodule_update_options opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
char *kwlist[] = {"submodules", "init", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &list, &py_init))
return NULL;
init = PyObject_IsTrue(py_init);
if (init != 0 && init != 1)
init = 0;
if (list == Py_None) {
err = git_submodule_foreach(self->repo, foreach_sub_update_cb, &init);
if (err != 0)
return Error_set(err);
Py_RETURN_NONE;
}
iter = PyObject_GetIter(list);
if (!iter)
return NULL;
while (1) {
next = PyIter_Next(iter);
if (!next)
break;
c_subpath = py_str_borrow_c_str(&subpath, next, NULL);
git_submodule_lookup(&submodule, self->repo, c_subpath);
Py_DECREF(subpath);
if (!submodule) {
PyErr_SetString(PyExc_KeyError,
"Submodule does not exist");
return NULL;
}
err = git_submodule_update(submodule, init, &opts);
if (err != 0) {
return Error_set(err);
}
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(Repository_lookup_reference__doc__,
"lookup_reference(name) -> Reference\n"
@ -1085,7 +1484,7 @@ Repository_create_reference_direct(Repository *self, PyObject *args,
if (err < 0)
return NULL;
err = git_reference_create(&c_reference, self->repo, c_name, &oid, force, NULL, NULL);
err = git_reference_create(&c_reference, self->repo, c_name, &oid, force, NULL);
if (err < 0)
return Error_set(err);
@ -1119,7 +1518,7 @@ Repository_create_reference_symbolic(Repository *self, PyObject *args,
return NULL;
err = git_reference_symbolic_create(&c_reference, self->repo, c_name,
c_target, force, NULL, NULL);
c_target, force, NULL);
if (err < 0)
return Error_set(err);
@ -1164,7 +1563,7 @@ Repository_status(Repository *self)
path = entry->head_to_index->old_file.path;
else
path = entry->index_to_workdir->old_file.path;
status = PyLong_FromLong((long) entry->status);
status = PyInt_FromLong((long) entry->status);
err = PyDict_SetItemString(dict, path, status);
Py_CLEAR(status);
@ -1206,7 +1605,7 @@ Repository_status_file(Repository *self, PyObject *value)
free(path);
return err_obj;
}
return PyLong_FromLong(status);
return PyInt_FromLong(status);
}
@ -1249,7 +1648,7 @@ Repository_TreeBuilder(Repository *self, PyObject *args)
}
}
err = git_treebuilder_create(&bld, tree);
err = git_treebuilder_new(&bld, self->repo, tree);
if (must_free != NULL)
git_tree_free(must_free);
@ -1342,8 +1741,8 @@ Repository_create_note(Repository *self, PyObject* args)
if (err < 0)
return Error_set(err);
err = git_note_create(&note_id, self->repo, py_author->signature,
py_committer->signature, ref,
err = git_note_create(&note_id, self->repo, ref, py_author->signature,
py_committer->signature,
&annotated_id, message, force);
if (err < 0)
return Error_set(err);
@ -1374,79 +1773,13 @@ Repository_lookup_note(Repository *self, PyObject* args)
return (PyObject*) wrap_note(self, &annotated_id, ref);
}
PyDoc_STRVAR(Repository_blame__doc__,
"blame(path, [flags, min_match_characters, newest_commit, oldest_commit,\n"
" min_line, max_line]) -> blame\n"
"\n"
"Get the blame for a single file.\n"
"\n"
"Arguments:\n"
"\n"
"path\n"
" A path to file to consider.\n"
"flags\n"
" A GIT_BLAME_* constant.\n"
"min_match_characters\n"
" The number of alphanum chars that must be detected as moving/copying\n"
" within a file for it to associate those lines with the parent commit.\n"
"newest_commit\n"
" The id of the newest commit to consider.\n"
"oldest_commit\n"
" The id of the oldest commit to consider.\n"
"min_line\n"
" The first line in the file to blame.\n"
"max_line\n"
" The last line in the file to blame.\n"
"\n"
"Examples::\n"
"\n"
" repo.blame('foo.c', flags=GIT_BLAME_TRACK_COPIES_SAME_FILE)");
PyObject *
Repository_blame(Repository *self, PyObject *args, PyObject *kwds)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
git_blame *blame;
char *path;
PyObject *value1 = NULL;
PyObject *value2 = NULL;
int err;
char *keywords[] = {"path", "flags", "min_match_characters", "newest_commit",
"oldest_commit", "min_line", "max_line", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|IHO!O!II", keywords,
&path, &opts.flags,
&opts.min_match_characters,
&OidType, &value1,
&OidType, &value2,
&opts.min_line, &opts.max_line))
return NULL;
if (value1) {
err = py_oid_to_git_oid_expand(self->repo, value1, &opts.newest_commit);
if (err < 0)
return NULL;
}
if (value2) {
err = py_oid_to_git_oid_expand(self->repo, value2, &opts.oldest_commit);
if (err < 0)
return NULL;
}
err = git_blame_file(&blame, self->repo, path, &opts);
if (err < 0)
return Error_set(err);
return wrap_blame(blame, self);
}
PyDoc_STRVAR(Repository_reset__doc__,
"reset(oid, reset_type)\n"
"\n"
"Resets current head to the provided oid.\n"
"reset_type:\n"
"GIT_RESET_SOFT: resets head to point to oid, but does not modfy working copy, and leaves the changes in the index.\n"
"GIT_RESET_MIXED: resets head to point to oid, but does not modfy working copy. It empties the index too.\n"
"GIT_RESET_SOFT: resets head to point to oid, but does not modify working copy, and leaves the changes in the index.\n"
"GIT_RESET_MIXED: resets head to point to oid, but does not modify working copy. It empties the index too.\n"
"GIT_RESET_HARD: resets head to point to oid, and resets too the working copy and the content of the index.\n");
PyObject *
@ -1470,17 +1803,36 @@ Repository_reset(Repository *self, PyObject* args)
err = git_object_lookup_prefix(&target, self->repo, &oid, len,
GIT_OBJ_ANY);
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL, NULL);
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL);
git_object_free(target);
if (err < 0)
return Error_set_oid(err, &oid, len);
Py_RETURN_NONE;
}
PyDoc_STRVAR(Repository_expand_id__doc__,
"expand_id(hex) -> Oid\n"
"\n"
"Expand a string into a full Oid according to the objects in this repsitory.\n");
PyObject *
Repository_expand_id(Repository *self, PyObject *py_hex)
{
git_oid oid;
int err;
err = py_oid_to_git_oid_expand(self->repo, py_hex, &oid);
if (err < 0)
return NULL;
return git_oid_to_python(&oid);
}
PyMethodDef Repository_methods[] = {
METHOD(Repository, create_blob, METH_VARARGS),
METHOD(Repository, create_blob_fromworkdir, METH_VARARGS),
METHOD(Repository, create_blob_fromdisk, METH_VARARGS),
METHOD(Repository, create_blob_fromiobase, METH_O),
METHOD(Repository, create_commit, METH_VARARGS),
METHOD(Repository, create_tag, METH_VARARGS),
METHOD(Repository, TreeBuilder, METH_VARARGS),
@ -1488,11 +1840,16 @@ PyMethodDef Repository_methods[] = {
METHOD(Repository, merge_base, METH_VARARGS),
METHOD(Repository, merge_analysis, METH_O),
METHOD(Repository, merge, METH_O),
METHOD(Repository, cherrypick, METH_O),
METHOD(Repository, read, METH_O),
METHOD(Repository, write, METH_VARARGS),
METHOD(Repository, create_reference_direct, METH_VARARGS),
METHOD(Repository, create_reference_symbolic, METH_VARARGS),
METHOD(Repository, listall_references, METH_NOARGS),
METHOD(Repository, listall_reference_objects, METH_NOARGS),
METHOD(Repository, listall_submodules, METH_NOARGS),
METHOD(Repository, init_submodules, METH_VARARGS | METH_KEYWORDS),
METHOD(Repository, update_submodules, METH_VARARGS | METH_KEYWORDS),
METHOD(Repository, lookup_reference, METH_O),
METHOD(Repository, revparse_single, METH_O),
METHOD(Repository, status, METH_NOARGS),
@ -1502,16 +1859,19 @@ PyMethodDef Repository_methods[] = {
METHOD(Repository, lookup_note, METH_VARARGS),
METHOD(Repository, git_object_lookup_prefix, METH_O),
METHOD(Repository, lookup_branch, METH_VARARGS),
METHOD(Repository, path_is_ignored, METH_VARARGS),
METHOD(Repository, listall_branches, METH_VARARGS),
METHOD(Repository, create_branch, METH_VARARGS),
METHOD(Repository, blame, METH_VARARGS | METH_KEYWORDS),
METHOD(Repository, reset, METH_VARARGS),
METHOD(Repository, expand_id, METH_O),
METHOD(Repository, _from_c, METH_VARARGS),
METHOD(Repository, _disown, METH_NOARGS),
{NULL}
};
PyGetSetDef Repository_getseters[] = {
GETTER(Repository, path),
GETSET(Repository, head),
GETTER(Repository, head),
GETTER(Repository, head_is_detached),
GETTER(Repository, head_is_unborn),
GETTER(Repository, is_empty),
@ -1524,7 +1884,7 @@ PyGetSetDef Repository_getseters[] = {
PyDoc_STRVAR(Repository__doc__,
"Repository(path) -> Repository\n"
"Repository(backend) -> Repository\n"
"\n"
"Git repository.");

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -33,6 +33,8 @@
#include <git2.h>
#include "types.h"
PyObject *wrap_repository(git_repository *c_repo);
int Repository_init(Repository *self, PyObject *args, PyObject *kwds);
int Repository_traverse(Repository *self, visitproc visit, void *arg);
int Repository_clear(Repository *self);
@ -56,6 +58,8 @@ PyObject* Repository_create_commit(Repository *self, PyObject *args);
PyObject* Repository_create_tag(Repository *self, PyObject *args);
PyObject* Repository_create_branch(Repository *self, PyObject *args);
PyObject* Repository_listall_references(Repository *self, PyObject *args);
PyObject* Repository_listall_reference_objects(Repository *self,
PyObject *args);
PyObject* Repository_listall_branches(Repository *self, PyObject *args);
PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name);
@ -70,5 +74,6 @@ PyObject* Repository_TreeBuilder(Repository *self, PyObject *args);
PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds);
PyObject* Repository_merge(Repository *self, PyObject *py_oid);
PyObject* Repository_cherrypick(Repository *self, PyObject *py_oid);
#endif

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -38,7 +38,7 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds)
{
char *keywords[] = {"name", "email", "time", "offset", "encoding", NULL};
PyObject *py_name, *tname;
char *email, *encoding = "ascii";
char *email, *encoding = "utf-8";
const char *name;
long long time = -1;
int offset = 0;
@ -96,10 +96,10 @@ Signature_dealloc(Signature *self)
PyDoc_STRVAR(Signature__pointer__doc__, "Get the signature's pointer. For internal use only.");
PyObject *
Signature__pointer__get__(Repository *self)
Signature__pointer__get__(Signature *self)
{
/* Bytes means a raw buffer */
return PyBytes_FromStringAndSize((char *) &self->repo, sizeof(git_repository *));
return PyBytes_FromStringAndSize((char *) &self->signature, sizeof(git_signature *));
}
PyDoc_STRVAR(Signature__encoding__doc__, "Encoding.");
@ -158,7 +158,7 @@ PyDoc_STRVAR(Signature_time__doc__, "Unix time.");
PyObject *
Signature_time__get__(Signature *self)
{
return PyLong_FromLongLong(self->signature->when.time);
return PyInt_FromLongLong(self->signature->when.time);
}
@ -167,7 +167,7 @@ PyDoc_STRVAR(Signature_offset__doc__, "Offset from UTC in minutes.");
PyObject *
Signature_offset__get__(Signature *self)
{
return PyLong_FromLong(self->signature->when.offset);
return PyInt_FromLong(self->signature->when.offset);
}
PyGetSetDef Signature_getseters[] = {

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -111,7 +111,11 @@ PyDoc_STRVAR(Tag__message__doc__, "Tag message (bytes).");
PyObject *
Tag__message__get__(Tag *self)
{
return PyBytes_FromString(git_tag_message(self->tag));
const char *message;
message = git_tag_message(self->tag);
if (!message)
Py_RETURN_NONE;
return PyBytes_FromString(message);
}
PyMethodDef Tag_methods[] = {

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -54,7 +54,7 @@ PyDoc_STRVAR(TreeEntry_filemode__doc__, "Filemode.");
PyObject *
TreeEntry_filemode__get__(TreeEntry *self)
{
return PyLong_FromLong(git_tree_entry_filemode(self->entry));
return PyInt_FromLong(git_tree_entry_filemode(self->entry));
}
@ -67,6 +67,24 @@ TreeEntry_name__get__(TreeEntry *self)
}
PyDoc_STRVAR(TreeEntry__name__doc__, "Name (bytes).");
PyObject *
TreeEntry__name__get__(TreeEntry *self)
{
return PyBytes_FromString(git_tree_entry_name(self->entry));
}
PyDoc_STRVAR(TreeEntry_type__doc__, "Type.");
PyObject *
TreeEntry_type__get__(TreeEntry *self)
{
return to_path(git_object_type2string(git_tree_entry_type(self->entry)));
}
PyDoc_STRVAR(TreeEntry_id__doc__, "Object id.");
PyObject *
@ -87,10 +105,20 @@ TreeEntry_oid__get__(TreeEntry *self)
return TreeEntry_id__get__(self);
}
static int
compare_ids(TreeEntry *a, TreeEntry *b)
{
const git_oid *id_a, *id_b;
id_a = git_tree_entry_id(a->entry);
id_b = git_tree_entry_id(b->entry);
return git_oid_cmp(id_a, id_b);
}
PyObject *
TreeEntry_richcompare(PyObject *a, PyObject *b, int op)
{
PyObject *res;
TreeEntry *ta, *tb;
int cmp;
/* We only support comparing to another tree entry */
@ -99,7 +127,14 @@ TreeEntry_richcompare(PyObject *a, PyObject *b, int op)
return Py_NotImplemented;
}
cmp =git_tree_entry_cmp(((TreeEntry*)a)->entry, ((TreeEntry*)b)->entry);
ta = (TreeEntry *) a;
tb = (TreeEntry *) b;
/* This is sorting order, if they sort equally, we still need to compare the ids */
cmp = git_tree_entry_cmp(ta->entry, tb->entry);
if (cmp == 0)
cmp = compare_ids(ta, tb);
switch (op) {
case Py_LT:
res = (cmp <= 0) ? Py_True: Py_False;
@ -137,17 +172,28 @@ TreeEntry_hex__get__(TreeEntry *self)
return git_oid_to_py_str(git_tree_entry_id(self->entry));
}
PyObject *
TreeEntry_repr(TreeEntry *self)
{
char str[GIT_OID_HEXSZ + 1] = { 0 };
const char *typename;
typename = git_object_type2string(git_tree_entry_type(self->entry));
git_oid_fmt(str, git_tree_entry_id(self->entry));
return PyString_FromFormat("pygit2.TreeEntry('%s', %s, %s)", git_tree_entry_name(self->entry), typename, str);
}
PyGetSetDef TreeEntry_getseters[] = {
GETTER(TreeEntry, filemode),
GETTER(TreeEntry, name),
GETTER(TreeEntry, _name),
GETTER(TreeEntry, oid),
GETTER(TreeEntry, id),
GETTER(TreeEntry, hex),
GETTER(TreeEntry, type),
{NULL}
};
PyDoc_STRVAR(TreeEntry__doc__, "TreeEntry objects.");
PyTypeObject TreeEntryType = {
@ -160,7 +206,7 @@ PyTypeObject TreeEntryType = {
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
(reprfunc)TreeEntry_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
@ -244,7 +290,7 @@ Tree_fix_index(Tree *self, PyObject *py_index)
size_t len;
long slen;
index = PyLong_AsLong(py_index);
index = PyInt_AsLong(py_index);
if (PyErr_Occurred())
return -1;
@ -313,7 +359,7 @@ Tree_getitem(Tree *self, PyObject *value)
int err;
/* Case 1: integer */
if (PyLong_Check(value))
if (PyInt_Check(value))
return Tree_getitem_by_index(self, value);
/* Case 2: byte or text string */

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -88,7 +88,7 @@ TreeBuilder_write(TreeBuilder *self)
int err;
git_oid oid;
err = git_treebuilder_write(&oid, self->repo->repo, self->bld);
err = git_treebuilder_write(&oid, self->bld);
if (err < 0)
return Error_set(err);

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -32,8 +32,8 @@
#include <Python.h>
#include <git2.h>
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 21)
#error You need a compatible libgit2 version (v0.21.x)
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 26)
#error You need a compatible libgit2 version (v0.26.x)
#endif
/*
@ -47,6 +47,7 @@ typedef struct {
git_repository *repo;
PyObject *index; /* It will be None for a bare repository */
PyObject *config; /* It will be None for a bare repository */
int owned; /* _from_c() sometimes means we don't own the C pointer */
} Repository;
@ -79,7 +80,7 @@ typedef struct {
PyObject_HEAD
Repository *repo;
git_note *note;
char* annotated_id;
PyObject* annotated_id;
} Note;
typedef struct {
@ -89,30 +90,41 @@ typedef struct {
char* ref;
} NoteIter;
/* git_patch */
typedef struct {
PyObject_HEAD
git_patch *patch;
PyObject* hunks;
} Patch;
/* git _diff */
SIMPLE_TYPE(Diff, git_diff, list)
/* git_diff */
SIMPLE_TYPE(Diff, git_diff, diff)
typedef struct {
PyObject_HEAD
Diff* diff;
Diff *diff;
size_t i;
size_t n;
} DiffIter;
typedef struct {
PyObject_HEAD
PyObject* hunks;
const char * old_file_path;
const char * new_file_path;
char* old_id;
char* new_id;
char status;
unsigned similarity;
unsigned additions;
unsigned deletions;
unsigned flags;
} Patch;
PyObject *id;
char *path;
git_off_t size;
uint32_t flags;
uint16_t mode;
} DiffFile;
typedef struct {
PyObject_HEAD
git_delta_t status;
uint32_t flags;
uint16_t similarity;
uint16_t nfiles;
PyObject *old_file;
PyObject *new_file;
} DiffDelta;
typedef struct {
PyObject_HEAD
@ -121,8 +133,20 @@ typedef struct {
int old_lines;
int new_start;
int new_lines;
} Hunk;
PyObject *header;
} DiffHunk;
typedef struct {
PyObject_HEAD
char origin;
int old_lineno;
int new_lineno;
int num_lines;
git_off_t content_offset;
PyObject *content;
} DiffLine;
SIMPLE_TYPE(DiffStats, git_diff_stats, stats);
/* git_tree_walk , git_treebuilder*/
SIMPLE_TYPE(TreeBuilder, git_treebuilder, bld)
@ -147,12 +171,6 @@ typedef struct {
git_index_entry entry;
} IndexEntry;
typedef struct {
PyObject_HEAD
Index *owner;
int i;
} IndexIter;
/* git_reference, git_reflog */
SIMPLE_TYPE(Walker, git_revwalk, walk)
@ -164,8 +182,8 @@ typedef Reference Branch;
typedef struct {
PyObject_HEAD
git_signature *signature;
char *oid_old;
char *oid_new;
PyObject *oid_old;
PyObject *oid_new;
char *message;
} RefLogEntry;
@ -185,27 +203,4 @@ typedef struct {
char *encoding;
} Signature;
/* git_blame */
SIMPLE_TYPE(Blame, git_blame, blame)
typedef struct {
PyObject_HEAD
Blame* blame;
size_t i;
size_t n;
} BlameIter;
typedef struct {
PyObject_HEAD
unsigned lines_in_hunk;
char* final_commit_id;
unsigned final_start_line_number;
git_signature* final_signature;
char* orig_commit_id;
char* orig_path;
unsigned orig_start_line_number;
git_signature* orig_signature;
char boundary;
} BlameHunk;
#endif

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -31,6 +31,10 @@
#include "utils.h"
extern PyTypeObject ReferenceType;
extern PyTypeObject TreeType;
extern PyTypeObject CommitType;
extern PyTypeObject BlobType;
extern PyTypeObject TagType;
/**
* py_str_to_c_str() returns a newly allocated C string holding the string
@ -79,8 +83,7 @@ py_str_borrow_c_str(PyObject **tvalue, PyObject *value, const char *encoding)
}
/* Type error */
PyErr_Format(PyExc_TypeError, "unexpected %.200s",
Py_TYPE(value)->tp_name);
Error_type_error("unexpected %.200s", value);
return NULL;
}
@ -90,7 +93,7 @@ py_str_borrow_c_str(PyObject **tvalue, PyObject *value, const char *encoding)
PyObject *
get_pylist_from_git_strarray(git_strarray *strarray)
{
int index;
size_t index;
PyObject *new_list;
new_list = PyList_New(strarray->count);
@ -153,3 +156,43 @@ on_error:
return -1;
}
static git_otype
py_type_to_git_type(PyTypeObject *py_type)
{
if (py_type == &CommitType)
return GIT_OBJ_COMMIT;
else if (py_type == &TreeType)
return GIT_OBJ_TREE;
else if (py_type == &BlobType)
return GIT_OBJ_BLOB;
else if (py_type == &TagType)
return GIT_OBJ_TAG;
PyErr_SetString(PyExc_ValueError, "invalid target type");
return GIT_OBJ_BAD; /* -1 */
}
git_otype
py_object_to_otype(PyObject *py_type)
{
long value;
if (py_type == Py_None)
return GIT_OBJ_ANY;
if (PyInt_Check(py_type)) {
value = PyInt_AsLong(py_type);
if (value == -1 && PyErr_Occurred())
return GIT_OBJ_BAD;
/* TODO Check whether the value is a valid value */
return (git_otype)value;
}
if (PyType_Check(py_type))
return py_type_to_git_type((PyTypeObject *) py_type);
PyErr_SetString(PyExc_ValueError, "invalid target type");
return GIT_OBJ_BAD; /* -1 */
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -40,14 +40,17 @@
#endif
/* Python 2 support */
#ifndef Py_hash_t
#define Py_hash_t long
#endif
#ifndef PyLong_AsSize_t
#define PyLong_AsSize_t (size_t)PyLong_AsSsize_t
#endif
#if PY_MAJOR_VERSION == 2
#define PyLong_FromSize_t PyInt_FromSize_t
#define PyLong_AsSize_t (size_t)PyInt_AsSsize_t
#define PyLong_AsLong PyInt_AsLong
#undef PyLong_Check
#define PyLong_Check PyInt_Check
#define PyLong_FromLong PyInt_FromLong
#define PyInteger_Type PyInt_Type
#define PyInt_AsSize_t (size_t)PyInt_AsLong
#define PyInt_FromLongLong PyInt_FromLong
#define PyBytes_AS_STRING PyString_AS_STRING
#define PyBytes_AsString PyString_AsString
#define PyBytes_AsStringAndSize PyString_AsStringAndSize
@ -58,17 +61,16 @@
#define to_path(x) to_bytes(x)
#define to_encoding(x) to_bytes(x)
#else
#define PyInteger_Type PyLong_Type
#define PyInt_Check PyLong_Check
#define PyInt_FromSize_t PyLong_FromSize_t
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromLongLong PyLong_FromLongLong
#define PyInt_AsLong PyLong_AsLong
#define PyInt_AsSize_t PyLong_AsSize_t
#define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict")
#define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict")
#endif
#ifdef PYPY_VERSION
#define PyLong_AsSize_t (size_t)PyLong_AsUnsignedLong
#endif
#ifndef Py_hash_t
#define Py_hash_t long
#define PyString_FromFormat(s, ...) PyUnicode_FromFormat(s, __VA_ARGS__)
#endif
@ -119,6 +121,8 @@ const char *py_str_borrow_c_str(PyObject **tvaue, PyObject *value, const char *e
PyObject * get_pylist_from_git_strarray(git_strarray *strarray);
int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist);
int py_object_to_otype(PyObject *py_type);
#define py_path_to_c_str(py_path) \
py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding)

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
@ -98,13 +98,13 @@ PyDoc_STRVAR(Walker_sort__doc__,
PyObject *
Walker_sort(Walker *self, PyObject *py_sort_mode)
{
int sort_mode;
long sort_mode;
sort_mode = (int)PyLong_AsLong(py_sort_mode);
sort_mode = PyInt_AsLong(py_sort_mode);
if (sort_mode == -1 && PyErr_Occurred())
return NULL;
git_revwalk_sorting(self->walk, sort_mode);
git_revwalk_sorting(self->walk, (unsigned int)sort_mode);
Py_RETURN_NONE;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 The pygit2 contributors
* Copyright 2010-2017 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,

Binary file not shown.

BIN
test/data/submodulerepo.tar Normal file

Binary file not shown.

74
test/test_archive.py Normal file

@ -0,0 +1,74 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
"""Tests for Blame objects."""
from __future__ import absolute_import
from __future__ import unicode_literals
import unittest
import pygit2
from pygit2 import Index, Oid, Tree, Object
import tarfile
import os
from . import utils
from time import time
TREE_HASH = 'fd937514cb799514d4b81bb24c5fcfeb6472b245'
COMMIT_HASH = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'
class ArchiveTest(utils.RepoTestCase):
def check_writing(self, treeish, timestamp=None):
archive = tarfile.open('foo.tar', mode='w')
self.repo.write_archive(treeish, archive)
index = Index()
if isinstance(treeish, Object):
index.read_tree(treeish.peel(Tree))
else:
index.read_tree(self.repo[treeish].peel(Tree))
self.assertEqual(len(index), len(archive.getmembers()))
if timestamp:
fileinfo = archive.getmembers()[0]
self.assertEqual(timestamp, fileinfo.mtime)
archive.close()
self.assertTrue(os.path.isfile('foo.tar'))
os.remove('foo.tar')
def test_write_tree(self):
self.check_writing(TREE_HASH)
self.check_writing(Oid(hex=TREE_HASH))
self.check_writing(self.repo[TREE_HASH])
def test_write_commit(self):
commit_timestamp = self.repo[COMMIT_HASH].committer.time
self.check_writing(COMMIT_HASH, commit_timestamp)
self.check_writing(Oid(hex=COMMIT_HASH), commit_timestamp)
self.check_writing(self.repo[COMMIT_HASH], commit_timestamp)

65
test/test_attributes.py Normal file

@ -0,0 +1,65 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from the future
from __future__ import absolute_import
from __future__ import unicode_literals, print_function
# Import from the Standard Library
import binascii
import unittest
import tempfile
import os
from os.path import join, realpath
import sys
# Import from pygit2
from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT
from pygit2 import init_repository, clone_repository, discover_repository
from pygit2 import Oid, Reference, hashfile
import pygit2
from . import utils
try:
import __pypy__
except ImportError:
__pypy__ = None
class RepositorySignatureTest(utils.RepoTestCase):
def test_no_attr(self):
self.assertIsNone(self.repo.get_attr('file', 'foo'))
with open(join(self.repo.workdir, '.gitattributes'), 'w+') as f:
print('*.py text\n', file=f)
print('*.jpg -text\n', file=f)
print('*.sh eol=lf\n', file=f)
self.assertIsNone(self.repo.get_attr('file.py', 'foo'))
self.assertTrue(self.repo.get_attr('file.py', 'text'))
self.assertFalse(self.repo.get_attr('file.jpg', 'text'))
self.assertEqual("lf", self.repo.get_attr('file.sh', 'eol'))

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -31,7 +31,7 @@ from __future__ import absolute_import
from __future__ import unicode_literals
import unittest
import pygit2
from pygit2 import Signature
from pygit2 import Signature, Oid
from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED
from pygit2 import GIT_DIFF_IGNORE_WHITESPACE, GIT_DIFF_IGNORE_WHITESPACE_EOL
from . import utils
@ -41,13 +41,13 @@ from datetime import datetime
PATH = 'hello.txt'
HUNKS = [
('acecd5ea2924a4b900e7e149496e1f4b57976e51', 1,
(Oid(hex='acecd5ea2924a4b900e7e149496e1f4b57976e51'), 1,
Signature('J. David Ibañez', 'jdavid@itaapy.com',
1297179898, 60, encoding='utf-8'), True),
('6aaa262e655dd54252e5813c8e5acd7780ed097d', 2,
(Oid(hex='6aaa262e655dd54252e5813c8e5acd7780ed097d'), 2,
Signature('J. David Ibañez', 'jdavid@itaapy.com',
1297696877, 60, encoding='utf-8'), False),
('4ec4389a8068641da2d6578db0419484972284c8', 3,
(Oid(hex='4ec4389a8068641da2d6578db0419484972284c8'), 3,
Signature('J. David Ibañez', 'jdavid@itaapy.com',
1297696908, 60, encoding='utf-8'), False)
]

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -30,6 +30,7 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from os.path import dirname, join
import io
import unittest
import pygit2
@ -112,6 +113,19 @@ class BlobTest(utils.RepoTestCase):
self.assertTrue(isinstance(blob, pygit2.Blob))
self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type)
def test_create_blob_fromiobase(self):
self.assertRaises(TypeError, self.repo.create_blob_fromiobase, 'bad type')
f = io.BytesIO(BLOB_CONTENT)
blob_oid = self.repo.create_blob_fromiobase(f)
blob = self.repo[blob_oid]
self.assertTrue(isinstance(blob, pygit2.Blob))
self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type)
self.assertEqual(blob_oid, blob.id)
self.assertEqual(BLOB_SHA, blob_oid.hex)
def test_diff_blob(self):
blob = self.repo[BLOB_SHA]
old_blob = self.repo['3b18e512dba79e4c8300dd08aeb37f8e728b8dad']

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -39,6 +39,135 @@ I18N_LAST_COMMIT = '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'
ORIGIN_MASTER_COMMIT = '784855caf26449a1914d2cf62d12b9374d76ae78'
class BranchesObjectTestCase(utils.RepoTestCase):
def test_lookup_branch_local(self):
branch = self.repo.branches['master']
self.assertEqual(branch.target.hex, LAST_COMMIT)
branch = self.repo.branches.local['i18n']
self.assertEqual(branch.target.hex, I18N_LAST_COMMIT)
self.assertTrue(self.repo.branches.get('not-exists') is None)
self.assertRaises(KeyError, lambda: self.repo.branches['not-exists'])
def test_listall_branches(self):
branches = sorted(self.repo.branches)
self.assertEqual(branches, ['i18n', 'master'])
def test_create_branch(self):
commit = self.repo[LAST_COMMIT]
reference = self.repo.branches.create('version1', commit)
self.assertTrue('version1' in self.repo.branches)
reference = self.repo.branches['version1']
self.assertEqual(reference.target.hex, LAST_COMMIT)
# try to create existing reference
self.assertRaises(ValueError,
lambda: self.repo.branches.create('version1', commit))
# try to create existing reference with force
reference = self.repo.branches.create('version1', commit, True)
self.assertEqual(reference.target.hex, LAST_COMMIT)
def test_delete(self):
self.repo.branches.delete('i18n')
self.assertTrue(self.repo.branches.get('i18n') is None)
def test_cant_delete_master(self):
self.assertRaises(pygit2.GitError, lambda: self.repo.branches.delete('master'))
def test_branch_is_head_returns_true_if_branch_is_head(self):
branch = self.repo.branches.get('master')
self.assertTrue(branch.is_head())
def test_branch_is_head_returns_false_if_branch_is_not_head(self):
branch = self.repo.branches.get('i18n')
self.assertFalse(branch.is_head())
def test_branch_rename_succeeds(self):
new_branch = self.repo.branches['i18n'].rename('new-branch')
self.assertEqual(new_branch.target.hex, I18N_LAST_COMMIT)
new_branch_2 = self.repo.branches.get('new-branch')
self.assertEqual(new_branch_2.target.hex, I18N_LAST_COMMIT)
def test_branch_rename_fails_if_destination_already_exists(self):
original_branch = self.repo.branches.get('i18n')
self.assertRaises(ValueError, lambda: original_branch.rename('master'))
def test_branch_rename_not_fails_if_force_is_true(self):
original_branch = self.repo.branches.get('master')
new_branch = original_branch.rename('i18n', True)
self.assertEqual(new_branch.target.hex, LAST_COMMIT)
def test_branch_rename_fails_with_invalid_names(self):
original_branch = self.repo.branches.get('i18n')
self.assertRaises(ValueError,
lambda: original_branch.rename('abc@{123'))
def test_branch_name(self):
branch = self.repo.branches.get('master')
self.assertEqual(branch.branch_name, 'master')
self.assertEqual(branch.name, 'refs/heads/master')
branch = self.repo.branches.get('i18n')
self.assertEqual(branch.branch_name, 'i18n')
self.assertEqual(branch.name, 'refs/heads/i18n')
class BranchesObjectEmptyRepoTestCase(utils.EmptyRepoTestCase):
def setUp(self):
super(utils.EmptyRepoTestCase, self).setUp()
remote = self.repo.remotes[0]
remote.fetch()
def test_lookup_branch_remote(self):
branch = self.repo.branches.remote.get('origin/master')
self.assertEqual(branch.target.hex, ORIGIN_MASTER_COMMIT)
self.assertTrue(
self.repo.branches.remote.get('origin/not-exists') is None)
def test_listall_branches(self):
branches = sorted(self.repo.branches.remote)
self.assertEqual(branches, ['origin/master'])
def test_branch_remote_name(self):
self.repo.remotes[0].fetch()
branch = self.repo.branches.remote['origin/master']
self.assertEqual(branch.remote_name, 'origin')
def test_branch_upstream(self):
self.repo.remotes[0].fetch()
remote_master = self.repo.branches.remote['origin/master']
master = self.repo.branches.create('master',
self.repo[remote_master.target.hex])
self.assertTrue(master.upstream is None)
master.upstream = remote_master
self.assertEqual(master.upstream.branch_name, 'origin/master')
def set_bad_upstream():
master.upstream = 2.5
self.assertRaises(TypeError, set_bad_upstream)
master.upstream = None
self.assertTrue(master.upstream is None)
def test_branch_upstream_name(self):
self.repo.remotes[0].fetch()
remote_master = self.repo.branches.remote['origin/master']
master = self.repo.branches.create('master',
self.repo[remote_master.target.hex])
master.upstream = remote_master
self.assertEqual(master.upstream_name, 'refs/remotes/origin/master')
class BranchesTestCase(utils.RepoTestCase):
def test_lookup_branch_local(self):
branch = self.repo.lookup_branch('master')
@ -88,6 +217,14 @@ class BranchesTestCase(utils.RepoTestCase):
branch = self.repo.lookup_branch('i18n')
self.assertFalse(branch.is_head())
def test_branch_is_checked_out_returns_true_if_branch_is_checked_out(self):
branch = self.repo.lookup_branch('master')
self.assertTrue(branch.is_checked_out())
def test_branch_is_checked_out_returns_false_if_branch_is_not_checked_out(self):
branch = self.repo.lookup_branch('i18n')
self.assertFalse(branch.is_checked_out())
def test_branch_rename_succeeds(self):
original_branch = self.repo.lookup_branch('i18n')
new_branch = original_branch.rename('new-branch')
@ -159,6 +296,7 @@ class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase):
def set_bad_upstream():
master.upstream = 2.5
self.assertRaises(TypeError, set_bad_upstream)
master.upstream = None

71
test/test_cherrypick.py Normal file

@ -0,0 +1,71 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
"""Tests for merging and information about it."""
# Import from the future
from __future__ import absolute_import
from __future__ import unicode_literals
import unittest
import os
from pygit2 import GIT_MERGE_ANALYSIS_NONE, GIT_MERGE_ANALYSIS_NORMAL, GIT_MERGE_ANALYSIS_UP_TO_DATE
from pygit2 import GIT_MERGE_ANALYSIS_FASTFORWARD, GIT_MERGE_ANALYSIS_UNBORN
import pygit2
from . import utils
class CherrypickTestBasic(utils.RepoTestCaseForMerging):
def test_cherrypick_none(self):
self.assertRaises(TypeError, self.repo.cherrypick, None)
def test_cherrypick_invalid_hex(self):
branch_head_hex = '12345678'
self.assertRaises(KeyError, self.repo.cherrypick, branch_head_hex)
def test_cherrypick_already_something_in_index(self):
branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1'
branch_oid = self.repo.get(branch_head_hex).id
with open(os.path.join(self.repo.workdir, 'inindex.txt'), 'w') as f:
f.write('new content')
self.repo.index.add('inindex.txt')
self.assertRaises(pygit2.GitError, self.repo.cherrypick, branch_oid)
class CherrypickTestWithConflicts(utils.RepoTestCaseForMerging):
def test_cherrypick_remove_conflicts(self):
other_branch_tip = '1b2bae55ac95a4be3f8983b86cd579226d0eb247'
self.repo.cherrypick(other_branch_tip)
idx = self.repo.index
conflicts = idx.conflicts
self.assertTrue(conflicts is not None)
conflicts['.gitignore']
del idx.conflicts['.gitignore']
self.assertRaises(KeyError, conflicts.__getitem__, '.gitignore')
self.assertTrue(idx.conflicts is None)

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -30,22 +30,36 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import unittest
import sys
from pygit2 import GIT_OBJ_COMMIT, Signature, Oid
from . import utils
# pypy raises TypeError on writing to read-only, so we need to check
# and change the test accordingly
# pypy (in python2 mode) raises TypeError on writing to read-only, so
# we need to check and change the test accordingly
try:
import __pypy__
import __pypy__, sys
pypy2 = sys.version_info[0] < 3
except ImportError:
__pypy__ = None
pypy2 = False
COMMIT_SHA = '5fe808e8953c12735680c257f56600cb0de44b10'
class CommitTest(utils.BareRepoTestCase):
@unittest.skipIf(__pypy__ is not None, "skip refcounts checks in pypy")
def test_commit_refcount(self):
commit = self.repo[COMMIT_SHA]
start = sys.getrefcount(commit)
tree = commit.tree
del tree
end = sys.getrefcount(commit)
self.assertEqual(start, end)
def test_read_commit(self):
commit = self.repo[COMMIT_SHA]
self.assertEqual(COMMIT_SHA, str(commit.id))
@ -138,7 +152,7 @@ class CommitTest(utils.BareRepoTestCase):
commit = self.repo[COMMIT_SHA]
error_type = AttributeError if not __pypy__ else TypeError
error_type = AttributeError if not pypy2 else TypeError
self.assertRaises(error_type, setattr, commit, 'message', message)
self.assertRaises(error_type, setattr, commit, 'committer', committer)
self.assertRaises(error_type, setattr, commit, 'author', author)

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -97,7 +97,7 @@ class ConfigTest(utils.RepoTestCase):
self.assertRaises(TypeError, lambda: config[()])
self.assertRaises(TypeError, lambda: config[-4])
self.assertRaisesWithArg(ValueError, "Invalid config item name 'abc'",
self.assertRaisesWithArg(ValueError, "invalid config item name 'abc'",
lambda: config['abc'])
self.assertRaisesWithArg(KeyError, 'abc.def',
lambda: config['abc.def'])
@ -153,7 +153,7 @@ class ConfigTest(utils.RepoTestCase):
new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n")
new_file.close()
config.add_file(CONFIG_FILENAME, 5)
config.add_file(CONFIG_FILENAME, 6)
self.assertTrue('this.that' in config)
l = config.get_multivar('this.that', 'foo.*')
self.assertEqual(2, len(list(l)))

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
@ -31,6 +31,7 @@
import unittest
import pygit2
from pygit2 import GIT_CREDTYPE_USERPASS_PLAINTEXT
from pygit2 import UserPass, Keypair, KeypairFromAgent
from pygit2 import UserPass, Keypair
from . import utils
@ -60,34 +61,46 @@ class CredentialCreateTest(utils.NoRepoTestCase):
cred = Keypair(username, pubkey, privkey, passphrase)
self.assertEqual((username, pubkey, privkey, passphrase), cred.credential_tuple)
def test_ssh_agent(self):
username = "git"
cred = KeypairFromAgent(username)
self.assertEqual((username, None, None, None), cred.credential_tuple)
class CredentialCallback(utils.RepoTestCase):
def test_callback(self):
def credentials_cb(url, username, allowed):
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
raise Exception("I don't know the password")
class MyCallbacks(pygit2.RemoteCallbacks):
@staticmethod
def credentials(url, username, allowed):
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
raise Exception("I don't know the password")
remote = self.repo.create_remote("github", "https://github.com/github/github")
remote.credentials = credentials_cb
url = "https://github.com/github/github"
remote = self.repo.create_remote("github", url)
self.assertRaises(Exception, remote.fetch)
self.assertRaises(Exception, lambda: remote.fetch(callbacks=MyCallbacks()))
def test_bad_cred_type(self):
def credentials_cb(url, username, allowed):
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
return Keypair("git", "foo.pub", "foo", "sekkrit")
class MyCallbacks(pygit2.RemoteCallbacks):
@staticmethod
def credentials(url, username, allowed):
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
return Keypair("git", "foo.pub", "foo", "sekkrit")
remote = self.repo.create_remote("github", "https://github.com/github/github")
remote.credentials = credentials_cb
self.assertRaises(TypeError, remote.fetch)
url = "https://github.com/github/github"
remote = self.repo.create_remote("github", url)
self.assertRaises(TypeError, lambda: remote.fetch(callbacks=MyCallbacks()))
class CallableCredentialTest(utils.RepoTestCase):
def test_user_pass(self):
remote = self.repo.create_remote("bb", "https://bitbucket.org/libgit2/testgitrepository.git")
remote.credentials = UserPass("libgit2", "libgit2")
credentials = UserPass("libgit2", "libgit2")
callbacks = pygit2.RemoteCallbacks(credentials=credentials)
remote.fetch()
url = "https://bitbucket.org/libgit2/testgitrepository.git"
remote = self.repo.create_remote("bb", url)
remote.fetch(callbacks=callbacks)
if __name__ == '__main__':
unittest.main()

106
test/test_describe.py Normal file

@ -0,0 +1,106 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
"""Tests for describing commits."""
from __future__ import absolute_import
from __future__ import unicode_literals
import unittest
from pygit2 import GIT_DESCRIBE_DEFAULT, GIT_DESCRIBE_TAGS, GIT_DESCRIBE_ALL
import pygit2
from . import utils
def add_tag(repo, name, target):
message = 'Example tag.\n'
tagger = pygit2.Signature('John Doe', 'jdoe@example.com', 12347, 0)
sha = repo.create_tag(name, target, pygit2.GIT_OBJ_COMMIT, tagger, message)
return sha
class DescribeTest(utils.RepoTestCase):
def test_describe(self):
add_tag(self.repo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8')
self.assertEqual('thetag-2-g2be5719', self.repo.describe())
def test_describe_without_ref(self):
self.assertRaises(pygit2.GitError, self.repo.describe)
def test_describe_default_oid(self):
self.assertEqual('2be5719', self.repo.describe(show_commit_oid_as_fallback=True))
def test_describe_strategies(self):
self.assertEqual('heads/master', self.repo.describe(describe_strategy=GIT_DESCRIBE_ALL))
self.repo.create_reference('refs/tags/thetag', '4ec4389a8068641da2d6578db0419484972284c8')
self.assertRaises(KeyError, self.repo.describe)
self.assertEqual('thetag-2-g2be5719', self.repo.describe(describe_strategy=GIT_DESCRIBE_TAGS))
def test_describe_pattern(self):
add_tag(self.repo, 'private/tag1', '5ebeeebb320790caf276b9fc8b24546d63316533')
add_tag(self.repo, 'public/tag2', '4ec4389a8068641da2d6578db0419484972284c8')
self.assertEqual('public/tag2-2-g2be5719', self.repo.describe(pattern='public/*'))
def test_describe_committish(self):
add_tag(self.repo, 'thetag', 'acecd5ea2924a4b900e7e149496e1f4b57976e51')
self.assertEqual('thetag-4-g2be5719', self.repo.describe(committish='HEAD'))
self.assertEqual('thetag-1-g5ebeeeb', self.repo.describe(committish='HEAD^'))
self.assertEqual('thetag-4-g2be5719', self.repo.describe(committish=self.repo.head))
self.assertEqual('thetag-1-g6aaa262', self.repo.describe(committish='6aaa262e655dd54252e5813c8e5acd7780ed097d'))
self.assertEqual('thetag-1-g6aaa262', self.repo.describe(committish='6aaa262'))
def test_describe_follows_first_branch_only(self):
add_tag(self.repo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8')
self.assertRaises(KeyError, self.repo.describe, only_follow_first_parent=True)
def test_describe_abbreviated_size(self):
add_tag(self.repo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8')
self.assertEqual('thetag-2-g2be5719152d4f82c', self.repo.describe(abbreviated_size=16))
self.assertEqual('thetag', self.repo.describe(abbreviated_size=0))
def test_describe_long_format(self):
add_tag(self.repo, 'thetag', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98')
self.assertEqual('thetag-0-g2be5719', self.repo.describe(always_use_long_format=True))
class DescribeDirtyWorkdirTest(utils.DirtyRepoTestCase):
def setUp(self):
super(utils.DirtyRepoTestCase, self).setUp()
add_tag(self.repo, 'thetag', 'a763aa560953e7cfb87ccbc2f536d665aa4dff22')
def test_describe(self):
self.assertEqual('thetag', self.repo.describe())
def test_describe_with_dirty_suffix(self):
self.assertEqual('thetag-dirty', self.repo.describe(dirty_suffix='-dirty'))

Some files were not shown because too many files have changed in this diff Show More