Compare commits

..

No commits in common. "master" and "v0.21.2" have entirely different histories.

119 changed files with 2635 additions and 7174 deletions

1
.gitattributes vendored

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

1
.gitignore vendored

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

@ -14,15 +14,3 @@ 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 maint/v0.26 https://github.com/libgit2/libgit2.git
git clone --depth=1 -b v0.21.1 https://github.com/libgit2/libgit2.git
cd libgit2/
mkdir build && cd build

@ -2,12 +2,10 @@ 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

@ -1,859 +0,0 @@
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,22 +2,19 @@
pygit2 - libgit2 bindings in Python
######################################################################
.. image:: https://travis-ci.org/libgit2/pygit2.svg?branch=master
.. image:: https://secure.travis-ci.org/libgit2/pygit2.png
: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 Git plumbing. Pygit2 works with Python 2.7, 3.3, 3.4, 3.5, 3.6
and PyPy 2.6
implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and
pypy.
Links:
- https://github.com/libgit2/pygit2 -- Source code and issue tracker
- http://github.com/libgit2/pygit2 -- Source code and issue tracker
- http://www.pygit2.org/ -- Documentation
- https://pypi.python.org/pypi/pygit2 -- Download
- https://github.com/libgit2/pygit2/blob/master/CHANGELOG.rst -- Changelog
- http://pypi.python.org/pypi/pygit2 -- Download
How to install
==============
@ -25,50 +22,363 @@ 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
==============
117 developers have contributed at least 1 commit to pygit2::
69 developers have contributed at least 1 commit to pygit2::
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
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
License

@ -1,53 +0,0 @@
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

@ -1,8 +0,0 @@
**********************************************************************
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,14 +11,12 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os, platform
from string import digits
import sys, os
# 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.
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))
sys.path.insert(0, os.path.abspath('../build/lib.linux-x86_64-2.7'))
# -- General configuration -----------------------------------------------------
@ -43,16 +41,16 @@ master_doc = 'index'
# General information about the project.
project = u'pygit2'
copyright = u'2010-2015 The pygit2 contributors'
copyright = u'2010-2014 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.26'
version = '0.21'
# The full version, including alpha/beta/rc tags.
release = '0.26.0'
release = '0.21.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

@ -2,12 +2,9 @@
The development version
**********************************************************************
.. image:: https://travis-ci.org/libgit2/pygit2.svg?branch=master
.. image:: https://secure.travis-ci.org/libgit2/pygit2.png
: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,10 +23,6 @@ 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()
@ -39,10 +35,6 @@ 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.
@ -56,60 +48,26 @@ The Patch type
Attributes:
.. autoattribute:: pygit2.Patch.delta
.. 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.hunks
.. 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
.. autoattribute:: pygit2.Patch.additions
.. autoattribute:: pygit2.Patch.deletions
Getters:
.. autoattribute:: pygit2.DiffDelta.is_binary
.. autoattribute:: pygit2.Patch.is_binary
The DiffFile type
The Hunk type
====================
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
.. autoattribute:: pygit2.Hunk.old_start
.. autoattribute:: pygit2.Hunk.old_lines
.. autoattribute:: pygit2.Hunk.new_start
.. autoattribute:: pygit2.Hunk.new_lines
.. autoattribute:: pygit2.Hunk.lines

@ -1,8 +0,0 @@
**********************************************************************
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.26.0``::
``0.21.1``::
>>> 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.26.0``::
``0.21.1``::
>>> print LIBGIT2_VER_MINOR
26
21
.. py:data:: LIBGIT2_VER_REVISION
Integer value of the revision version number. For example, for the version
``0.26.0``::
``0.21.1``::
>>> print LIBGIT2_VER_REVISION
0
1
.. py:data:: LIBGIT2_VERSION
The libgit2 version number as a string::
>>> print LIBGIT2_VERSION
'0.26.0'
'0.21.1'
Errors
======

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

@ -13,83 +13,68 @@ Installation
Requirements
============
- Python 2.7, 3.3+ or PyPy 2.6+ (including the development headers)
- Libgit2 v0.26.x
- cffi 1.0+
- six
- tox (optional)
- Python 2.7, 3.2, 3.3, 3.4 or pypy.
Including the development headers.
Optional libgit2 dependecies to support ssh and https:
- Libgit2 v0.21.1
- https: WinHTTP (Windows), SecureTransport (OS X) or OpenSSL.
- ssh: libssh2, pkg-config
- cffi 0.8.1+
It should work with older versions of cffi and PyPy, but using cffi 1.0+
(and PyPy 2.6+) is strongly encouraged.
.. warning::
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.
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.
.. note::
Version numbers
---------------
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.
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.
As illustration see this table of compatible releases:
As illustration see this table of compatible releases:
+-----------+-----------------------+------------------------------+
|**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|
+-----------+-----------------------+------------------------------+
+-----------+--------+----------------+------------------------+
|**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.
**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
=============
To install the latest version of libgit2 system wide, in the ``/usr/local``
directory, do:
This works for me, it may work for you:
.. code-block:: sh
$ wget https://github.com/libgit2/libgit2/archive/v0.26.0.tar.gz
$ tar xzf v0.26.0.tar.gz
$ cd libgit2-0.26.0/
$ wget https://github.com/libgit2/libgit2/archive/v0.21.1.tar.gz
$ tar xzf v0.21.1.tar.gz
$ cd libgit2-0.21.1/
$ cmake .
$ make
$ sudo make install
.. seealso::
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/
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:
Once libgit2 is instaleld, deploying pygit2 should be a snap:
.. code-block:: sh
$ pìp install cffi
$ pip install pygit2
...
$ python -c 'import pygit2'
Troubleshooting
---------------
Troobleshooting
===============
The verification step may fail if the dynamic linker does not find the libgit2
library:
You may get an error like this one:
.. code-block:: sh
@ -100,40 +85,71 @@ library:
from _pygit2 import *
ImportError: libgit2.so.0: cannot open shared object file: No such file or directory
This happens for instance in Ubuntu, the libgit2 library is installed within
It means the linker is not able to find the libgit2 library.
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``:
fix this call ``ldconfig`` between the installation of libgit2 and the
installation of pygit2:
.. code-block:: sh
...
$ cmake .
$ make
$ sudo make install
...
$ sudo ldconfig
$ python -c 'import pygit2'
...
$ pip install cffi
$ pip install pygit2
If it still does not work, please open an issue at
https://github.com/libgit2/pygit2/issues, I would like to know about it.
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/
Build options
=============
The LIBGIT2 environment variable
================================
``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``.
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 within a virtual environment
====================================
Use case: libgit2 within a Virtualenv
-------------------------------------
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.
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.
Create the virtualenv, activate it, and set the ``LIBGIT2`` environment
variable:
@ -148,9 +164,9 @@ Install libgit2 (see we define the installation prefix):
.. code-block:: sh
$ wget https://github.com/libgit2/libgit2/archive/v0.26.0.tar.gz
$ tar xzf v0.26.0.tar.gz
$ cd libgit2-0.26.0/
$ wget https://github.com/libgit2/libgit2/archive/v0.21.1.tar.gz
$ tar xzf v0.21.1.tar.gz
$ cd libgit2-0.21.1/
$ cmake . -DCMAKE_INSTALL_PREFIX=$LIBGIT2
$ make
$ make install
@ -159,81 +175,31 @@ Install pygit2:
.. code-block:: sh
$ export LDFLAGS="-Wl,-rpath='$LIBGIT2/lib',--enable-new-dtags $LDFLAGS"
$ pìp install cffi
$ pip install pygit2
$ python -c 'import pygit2'
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
Building on Windows
===================================
`pygit2` for Windows is packaged into wheels and can be easily
installed with `pip`:
pygit2 expects to find the libgit2 installed files in the directory specified
in the ``LIBGIT2`` environment variable.
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:
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:
.. code-block:: sh
$ export LIBGIT2=C:/Dev/libgit2
$ 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"
$ 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"
$ cmake --build . --config release --target install
$ ctest -v
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
At this point, you're ready to execute the generic pygit2 installation
steps described above.

@ -1,5 +1,5 @@
**********************************************************************
Merge & Cherrypick
Merge
**********************************************************************
.. contents::
@ -29,28 +29,7 @@ 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,7 +132,6 @@ 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:
@ -177,7 +176,6 @@ Tree entries
.. autoattribute:: pygit2.TreeEntry.id
.. autoattribute:: pygit2.TreeEntry.hex
.. autoattribute:: pygit2.TreeEntry.filemode
.. autoattribute:: pygit2.TreeEntry.type
.. method:: TreeEntry.__cmp__(TreeEntry)
@ -190,14 +188,14 @@ Example::
6
>>> for entry in tree: # Iteration
... print(entry.id, entry.type, entry.name)
... print(entry.id, entry.name)
...
7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa blob .gitignore
c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 blob COPYING
32b30b90b062f66957d6790c3c155c289c34424e blob README.md
c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 blob pygit2.c
85a67270a49ef16cdd3d328f06a3e4b459f09b27 blob setup.py
3d8985bbec338eb4d47c5b01b863ee89d044bd53 tree test
7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore
c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING
32b30b90b062f66957d6790c3c155c289c34424e README.md
c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c
85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py
3d8985bbec338eb4d47c5b01b863ee89d044bd53 test
>>> entry = tree['pygit2.c'] # Get an entry by name
>>> entry

@ -17,11 +17,10 @@ Main porcelain commands
.. toctree::
:maxdepth: 1
git-cherry-pick (Apply the changes introduced by some existing commits.) <recipes/git-cherry-pick>
git-branch (List, create, or delete branches.) <recipes/git-branch>
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

@ -0,0 +1,30 @@
**********************************************************************
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

@ -1,73 +0,0 @@
**********************************************************************
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

@ -1,26 +0,0 @@
**********************************************************************
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 = repo.diff(commit.parents[0], commit)
>>> diff = commit.tree.diff()
======================================================================
Show all files in commit
@ -40,52 +40,6 @@ 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,42 +4,29 @@ References
.. contents::
.. autoclass:: pygit2.repository.References
:members:
:undoc-members:
:special-members: __getitem__, __iter__, __contains__
.. automethod:: pygit2.Repository.listall_references
.. automethod:: pygit2.Repository.lookup_reference
Example::
>>> all_refs = list(repo.references)
>>> all_refs = repo.listall_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::
@ -78,33 +65,28 @@ Branches
Branches inherit from References, and additionally provide specialized
accessors for some unique features.
.. autoclass:: pygit2.repository.Branches
:members:
:undoc-members:
:special-members: __getitem__, __iter__, __contains__
.. automethod:: pygit2.Repository.listall_branches
.. automethod:: pygit2.Repository.lookup_branch
.. automethod:: pygit2.Repository.create_branch
Example::
>>> # Listing all branches
>>> branches_list = list(repo.branches)
>>> # Local only
>>> local_branches = list(repo.branches.local)
>>> # Remote only
>>> remote_branches = list(repo.branches.remote)
>>> local_branches = repo.listall_branches()
>>> # equivalent to
>>> local_branches = repo.listall_branches(pygit2.GIT_BRANCH_LOCAL)
>>> # 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
>>> remote_branches = repo.listall_branches(pygit2.GIT_BRANCH_REMOTE)
>>> remote_branch = repo.branches.remote['upstream/feature']
>>> all_branches = repo.listall_branches(pygit2.GIT_BRANCH_REMOTE |
pygit2.GIT_BRANCH_LOCAL)
>>> # Create a local branch
>>> new_branch = repo.branches.local.create('new-branch')
>>> And delete it
>>> repo.branches.delete('new-branch')
>>> master_branch = repo.lookup_branch('master')
>>> # equivalent to
>>> master_branch = repo.lookup_branch('master',
pygit2.GIT_BRANCH_LOCAL)
>>> remote_branch = repo.lookup_branch('upstream/feature',
pygit2.GIT_BRANCH_REMOTE)
The Branch type
====================
@ -113,19 +95,17 @@ 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.references.get('refs/heads/master') # Returns None if not found
>>> # Almost equivalent to
>>> head = repo.references['refs/heads/master'] # Raises KeyError if not found
>>> head = repo.lookup_reference('refs/heads/master')
>>> for entry in head.log():
... print(entry.message)

@ -2,18 +2,10 @@
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
====================
@ -21,12 +13,6 @@ The Remote type
.. autoclass:: pygit2.Remote
:members:
The RemoteCallbacks type
========================
.. autoclass:: pygit2.RemoteCallbacks
:members:
The TransferProgress type
===========================
@ -38,16 +24,14 @@ 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,11 +67,5 @@ 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

@ -1,21 +0,0 @@
**********************************************************************
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
>>> index.remove('path/to/file') # git rm
>>> del index['path/to/file'] # git rm
>>> index.write() # don't forget to save the changes
Custom entries::
@ -71,11 +71,3 @@ 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-2017 The pygit2 contributors
# 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,
@ -32,48 +32,20 @@ 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, Passthrough
from .errors import check_error
from .ffi import ffi, C
from .index import Index, IndexEntry
from .remote import Remote, RemoteCallbacks, get_credentials
from .remote import Remote, get_credentials
from .repository import Repository
from .settings import Settings
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
from .utils import to_bytes
from .version import __version__
def init_repository(path, bare=False,
flags=GIT_REPOSITORY_INIT_MKPATH,
flags=C.GIT_REPOSITORY_INIT_MKPATH,
mode=0,
workdir_path=None,
description=None,
@ -105,38 +77,19 @@ 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 |= GIT_REPOSITORY_INIT_BARE
flags |= C.GIT_REPOSITORY_INIT_BARE
# Options
options = ffi.new('git_repository_init_options *')
C.git_repository_init_init_options(options,
GIT_REPOSITORY_INIT_OPTIONS_VERSION)
options.version = 1
options.flags = flags
options.mode = mode
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
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)
# Call
crepository = ffi.new('git_repository **')
@ -144,41 +97,28 @@ def init_repository(path, bare=False,
check_error(err)
# Ok
return Repository(to_str(path))
return Repository(path)
@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):
@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):
d = ffi.from_handle(data)
try:
repository = d['repository_cb'](ffi.string(path), bare != 0)
# we no longer own the C object
repository._disown()
repo_out[0] = repository._repo
ccred = get_credentials(d['callback'], url, username_from_url, allowed)
cred_out[0] = ccred[0]
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, repository=None, remote=None,
checkout_branch=None, callbacks=None):
url, path, bare=False, ignore_cert_errors=False,
remote_name="origin", checkout_branch=None, credentials=None):
"""Clones a new Git repository from *url* in the given *path*.
Returns a Repository class pointing to the newly cloned repository.
@ -189,29 +129,16 @@ def clone_repository(
:param bool bare: Whether the local repository should be bare
:param callable remote: Callback for the remote to use.
:param callable repository: Callback for the repository to use.
:param str remote_name: Name to give the remote at *url*.
:param str checkout_branch: Branch to checkout after the
clone. The default is to use the remote's default branch.
:param RemoteCallbacks callbacks: object which implements the
callbacks as methods.
:param callable credentials: authentication to use if the remote
requires it
: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 *')
@ -221,42 +148,60 @@ def clone_repository(
# Data, let's use a dict as we don't really want much more
d = {}
d['repository_cb'] = repository
d['remote_cb'] = remote
d['callback'] = credentials
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 []', to_bytes(branch))
checkout_branch_ref = ffi.new('char []', branch)
opts.checkout_branch = checkout_branch_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
remote_name_ref = ffi.new('char []', to_bytes(remote_name))
opts.remote_name = remote_name_ref
opts.version = 1
opts.ignore_cert_errors = ignore_cert_errors
opts.bare = bare
if callbacks is None:
callbacks = RemoteCallbacks()
callbacks._fill_fetch_options(opts.fetch_opts)
opts.remote_callbacks.version = 1
opts.checkout_opts.version = 1
if credentials:
opts.remote_callbacks.credentials = _credentials_cb
opts.remote_callbacks.payload = d_handle
err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts)
C.git_repository_free(crepo[0])
if 'exception' in d:
raise d['exception']
check_error(err)
return Repository._from_c(crepo[0], owned=True)
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)
settings = Settings()

@ -1,76 +0,0 @@
# -*- 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()

@ -1,146 +0,0 @@
# -*- 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-2017 The pygit2 contributors
# 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,
@ -63,12 +63,15 @@ class ConfigIterator(object):
def __next__(self):
entry = self._next_entry()
return ffi.string(entry.name).decode('utf-8')
name = ffi.string(entry.name).decode('utf-8')
return name
class ConfigMultivarIterator(ConfigIterator):
def __next__(self):
entry = self._next_entry()
return ffi.string(entry.value).decode('utf-8')
@ -101,19 +104,19 @@ class Config(object):
def _get(self, key):
assert_string(key, "key")
entry = ffi.new('git_config_entry **')
err = C.git_config_get_entry(entry, self._config, to_bytes(key))
cstr = ffi.new('char **')
err = C.git_config_get_string(cstr, self._config, to_bytes(key))
return err, ConfigEntry._from_c(entry[0])
return err, cstr
def _get_entry(self, key):
err, entry = self._get(key)
def _get_string(self, key):
err, cstr = self._get(key)
if err == C.GIT_ENOTFOUND:
raise KeyError(key)
check_error(err)
return entry
return cstr[0]
def __contains__(self, key):
err, cstr = self._get(key)
@ -126,9 +129,9 @@ class Config(object):
return True
def __getitem__(self, key):
entry = self._get_entry(key)
val = self._get_string(key)
return ffi.string(entry.value).decode('utf-8')
return ffi.string(val).decode('utf-8')
def __setitem__(self, key, value):
assert_string(key, "key")
@ -158,11 +161,12 @@ class Config(object):
return ConfigIterator(self, citer[0])
def get_multivar(self, name, regex=None):
"""Get each value of a multivar ''name'' as a list of strings.
"""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."""
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 **')
@ -174,9 +178,11 @@ class Config(object):
return ConfigMultivarIterator(self, citer[0])
def set_multivar(self, name, regex, value):
"""Set a multivar ''name'' to ''value''. ''regexp'' is a regular
expression to indicate which values to replace.
"""
"""set_multivar(name, regex, value)
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")
@ -186,48 +192,53 @@ class Config(object):
check_error(err)
def get_bool(self, key):
"""Look up *key* and parse its value as a boolean as per the git-config
rules. Return a boolean value (True or False).
"""get_bool(key) -> Bool
Look up *key* and parse its value as a boolean as per the git-config
rules
Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',
0, 'off' and 'no'
"""
0, 'off' and 'no'"""
entry = self._get_entry(key)
val = self._get_string(key)
res = ffi.new('int *')
err = C.git_config_parse_bool(res, entry.value)
err = C.git_config_parse_bool(res, val)
check_error(err)
return res[0] != 0
def get_int(self, key):
"""Look up *key* and parse its value as an integer as per the git-config
rules. Return an integer.
"""get_int(key) -> int
Look up *key* and parse its value as an integer as per the git-config
rules.
A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo',
'mega' and 'giga' respectively.
"""
'mega' and 'giga' respectively"""
entry = self._get_entry(key)
val = self._get_string(key)
res = ffi.new('int64_t *')
err = C.git_config_parse_int64(res, entry.value)
err = C.git_config_parse_int64(res, val)
check_error(err)
return res[0]
def add_file(self, path, level=0, force=0):
"""Add a config file instance to an existing config."""
"""add_file(path, level=0, force=0)
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)
@ -270,35 +281,24 @@ class Config(object):
@staticmethod
def get_system_config():
"""Return a <Config> object representing the system configuration file.
"""
"""get_system_config() -> Config
Return an object representing the system configuration file."""
return Config._from_found_config(C.git_config_find_system)
@staticmethod
def get_global_config():
"""Return a <Config> object representing the global configuration file.
"""
"""get_global_config() -> Config
Return an object representing the global configuration file."""
return Config._from_found_config(C.git_config_find_global)
@staticmethod
def get_xdg_config():
"""Return a <Config> object representing the global configuration file.
"""
"""get_xdg_config() -> Config
Return an 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-2017 The pygit2 contributors
# 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,
@ -36,6 +36,7 @@ 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):
@ -60,12 +61,6 @@ 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):
@ -84,8 +79,3 @@ 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,12 +1,11 @@
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_commit;
typedef ... git_signature;
typedef ... git_index;
typedef ... git_diff;
typedef ... git_index_conflict_iterator;
@ -30,14 +29,6 @@ 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,
@ -52,18 +43,8 @@ typedef enum {
GIT_EUNMERGED = -10,
GIT_ENONFASTFORWARD = -11,
GIT_EINVALIDSPEC = -12,
GIT_ECONFLICT = -13,
GIT_EMERGECONFLICT = -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,
@ -74,23 +55,6 @@ 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);
@ -123,39 +87,9 @@ 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,
@ -164,136 +98,70 @@ 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_lookup(git_remote **out, git_repository *repo, const char *name);
int git_remote_load(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_repository *repo, const char *name, const char *new_name);
int git_remote_rename(
git_strarray *problems,
git_remote *remote,
const char *new_name);
const char * git_remote_url(const git_remote *remote);
int git_remote_set_url(git_repository *repo, const char *remote, const char* url);
int git_remote_set_url(git_remote *remote, const char* url);
const char * git_remote_pushurl(const git_remote *remote);
int git_remote_set_pushurl(git_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);
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);
const git_transfer_progress * git_remote_stats(git_remote *remote);
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);
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);
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);
@ -316,21 +184,19 @@ 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_UNSPECIFIED = -1,
GIT_SUBMODULE_IGNORE_RESET = -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 {
@ -368,40 +234,23 @@ 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;
git_diff_progress_cb progress_cb;
void *payload;
uint32_t context_lines;
uint32_t interhunk_lines;
git_diff_notify_cb notify_cb;
void *notify_payload;
uint16_t context_lines;
uint16_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);
@ -410,16 +259,7 @@ int git_diff_tree_to_index(git_diff **diff, git_repository *repo, git_tree *old_
* git_checkout
*/
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 enum { ... } git_checkout_notify_t;
typedef int (*git_checkout_notify_cb)(
git_checkout_notify_t why,
@ -435,39 +275,41 @@ 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);
@ -477,47 +319,32 @@ 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_fetch_options fetch_opts;
int bare;
git_clone_local_t local;
const char* checkout_branch;
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);
git_checkout_options checkout_opts;
git_remote_callbacks remote_callbacks;
int bare;
int ignore_cert_errors;
git_clone_local_t local;
const char *remote_name;
const char* checkout_branch;
git_signature *signature;
} git_clone_options;
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
*/
@ -526,30 +353,24 @@ typedef ... git_config;
typedef ... git_config_iterator;
typedef enum {
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_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_HIGHEST_LEVEL = -1,
} git_config_level_t;
typedef struct git_config_entry {
typedef struct {
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);
@ -597,7 +418,6 @@ 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;
@ -619,9 +439,6 @@ 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,
@ -632,49 +449,31 @@ 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 {
int32_t seconds;
uint32_t nanoseconds;
git_time_t seconds;
unsigned int nanoseconds;
} git_index_time;
typedef struct git_index_entry {
git_index_time ctime;
git_index_time mtime;
uint32_t dev;
uint32_t ino;
uint32_t mode;
uint32_t uid;
uint32_t gid;
uint32_t file_size;
unsigned int dev;
unsigned int ino;
unsigned int mode;
unsigned int uid;
unsigned int gid;
git_off_t file_size;
git_oid id;
uint16_t flags;
uint16_t flags_extended;
unsigned short flags;
unsigned short flags_extended;
const char *path;
} git_index_entry;
@ -706,221 +505,3 @@ 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-2017 The pygit2 contributors
# 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,
@ -25,45 +25,32 @@
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from pygit2
# ffi
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
# Error message
message = "(no message provided)"
giterr = C.giterr_last()
if giterr != ffi.NULL:
message = ffi.string(giterr.message).decode('utf8')
else:
message = "err %d (no message provided)" % err
message = ffi.string(giterr.message).decode()
# Translate to Python errors
if err in value_errors:
if err in [C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EEXISTS,
C.GIT_EAMBIGUOUS]:
raise ValueError(message)
if err == C.GIT_ENOTFOUND:
elif err == C.GIT_ENOTFOUND:
if io:
raise IOError(message)
raise KeyError(message)
if err == C.GIT_EINVALIDSPEC:
else:
raise KeyError(message)
elif err == C.GIT_EINVALIDSPEC:
raise ValueError(message)
if err == C.GIT_ITEROVER:
elif 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-2017 The pygit2 contributors
# 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,
@ -28,9 +28,32 @@
# Import from the future
from __future__ import absolute_import
# 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)
# 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)

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
# 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,
@ -28,14 +28,11 @@
# 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, to_bytes, to_str
from .utils import GenericIterator, StrArray
from .utils import is_string, strings_to_strarray, to_bytes, to_str
class Index(object):
@ -50,7 +47,6 @@ 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
@ -96,7 +92,7 @@ class Index(object):
return IndexEntry._from_c(centry)
def __iter__(self):
return GenericIterator(self)
return IndexIterator(self)
def read(self, force=True):
"""Update the contents the Index
@ -113,7 +109,8 @@ 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)
@ -122,21 +119,21 @@ class Index(object):
check_error(err)
def read_tree(self, 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>).
"""read_tree([Tree|Oid])
Replace the contents of the Index with those of a tree
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 = repo[tree]
tree = self._repo[tree]
if isinstance(tree, Oid):
if repo is None:
if not hasattr(self, '_repo'):
raise TypeError("id given but no associated repository")
tree = repo[tree]
tree = self._repo[tree]
elif not isinstance(tree, Tree):
raise TypeError("argument must be Oid or Tree")
@ -146,8 +143,9 @@ class Index(object):
check_error(err)
def write_tree(self, repo=None):
"""Create a tree out of the Index. Return the <Oid> object of the
written tree.
"""write_tree([repo]) -> Oid
Create a tree out of the Index
The contents of the index will be written out to the object
database. If there is no associated repository, 'repo' must be
@ -157,9 +155,6 @@ 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:
@ -168,10 +163,10 @@ class Index(object):
check_error(err)
return Oid(raw=bytes(ffi.buffer(coid)[:]))
def remove(self, path, level=0):
def remove(self, path):
"""Remove an entry from the Index.
"""
err = C.git_index_remove(self._index, to_bytes(path), level)
err = C.git_index_remove(self._index, to_bytes(path), 0)
check_error(err, True)
def add_all(self, pathspecs=[]):
@ -180,16 +175,18 @@ class Index(object):
If pathspecs are specified, only files matching those pathspecs will
be added.
"""
with StrArray(pathspecs) as arr:
err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL)
check_error(err, True)
arr, refs = strings_to_strarray(pathspecs)
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 or update an entry in the Index.
"""add([path|entry])
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.
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 an IndexEntry is given, that entry will be added or update in the
Index without checking for the existence of the path or id.
@ -208,8 +205,12 @@ class Index(object):
check_error(err, True)
def diff_to_workdir(self, flags=0, context_lines=3, interhunk_lines=0):
"""Diff the index against the working directory. Return a <Diff> object
with the differences between the index and the working copy.
"""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.
Arguments:
@ -221,8 +222,7 @@ class Index(object):
interhunk_lines: the maximum number of unchanged lines between hunk
boundaries before the hunks will be merged into a one
"""
repo = self._repo
if repo is None:
if not hasattr(self, '_repo'):
raise ValueError('diff needs an associated repository')
copts = ffi.new('git_diff_options *')
@ -234,15 +234,19 @@ class Index(object):
copts.interhunk_lines = interhunk_lines
cdiff = ffi.new('git_diff **')
err = C.git_diff_index_to_workdir(cdiff, repo._repo, self._index,
copts)
err = C.git_diff_index_to_workdir(cdiff, self._repo._repo,
self._index, copts)
check_error(err)
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo)
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo)
def diff_to_tree(self, tree, flags=0, context_lines=3, interhunk_lines=0):
"""Diff the index against a tree. Return a <Diff> object with the
differences between the index and the given tree.
"""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.
Arguments:
@ -256,8 +260,8 @@ class Index(object):
interhunk_lines: the maximum number of unchanged lines between hunk
boundaries before the hunks will be merged into a one.
"""
repo = self._repo
if repo is None:
if not hasattr(self, '_repo'):
raise ValueError('diff needs an associated repository')
if not isinstance(tree, Tree):
@ -275,11 +279,11 @@ class Index(object):
ffi.buffer(ctree)[:] = tree._pointer[:]
cdiff = ffi.new('git_diff **')
err = C.git_diff_tree_to_index(cdiff, repo._repo, ctree[0],
err = C.git_diff_tree_to_index(cdiff, self._repo._repo, ctree[0],
self._index, copts)
check_error(err)
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo)
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo)
#
@ -310,12 +314,10 @@ class Index(object):
self._conflicts = None
return None
if self._conflicts is None or self._conflicts() is None:
conflicts = ConflictCollection(self)
self._conflicts = weakref.ref(conflicts)
return conflicts
if self._conflicts is None:
self._conflicts = ConflictCollection(self)
return self._conflicts()
return self._conflicts
class IndexEntry(object):
@ -327,7 +329,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):
@ -367,6 +369,26 @@ 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-2017 The pygit2 contributors
# 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,
@ -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-2017 The pygit2 contributors
# 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,

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
# 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,
@ -35,7 +35,6 @@ 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
@ -66,14 +65,16 @@ class Refspec(object):
return C.git_refspec_direction(self._refspec)
def src_matches(self, ref):
"""Return True if the given string matches the source of this refspec,
False otherwise.
"""
"""src_matches(str) -> Bool
Returns whether the given string matches the source of this refspec"""
return bool(C.git_refspec_src_matches(self._refspec, to_bytes(ref)))
def dst_matches(self, ref):
"""Return True if the given string matches the destination of this
refspec, False otherwise."""
"""dst_matches(str) -> Bool
Returns whether the given string matches the destination of this
refspec"""
return bool(C.git_refspec_dst_matches(self._refspec, to_bytes(ref)))
def _transform(self, ref, fn):
@ -87,13 +88,15 @@ class Refspec(object):
C.git_buf_free(buf)
def transform(self, ref):
"""Transform a reference name according to this refspec from the lhs to
the rhs. Return an string.
"""
"""transform(str) -> str
Transform a reference name according to this refspec from the lhs to
the rhs."""
return self._transform(ref, C.git_refspec_transform)
def rtransform(self, ref):
"""Transform a reference name according to this refspec from the lhs to
the rhs. Return an string.
"""
"""transform(str) -> str
Transform a reference name according to this refspec from the lhs
to the rhs"""
return self._transform(ref, C.git_refspec_rtransform)

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
# 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,
@ -30,10 +30,10 @@ from __future__ import absolute_import
# Import from pygit2
from _pygit2 import Oid
from .errors import check_error, Passthrough
from .errors import check_error, GitError
from .ffi import ffi, C
from .refspec import Refspec
from .utils import to_bytes, strarray_to_strings, StrArray
from .utils import to_bytes, strarray_to_strings, strings_to_strarray
def maybe_string(ptr):
@ -49,7 +49,7 @@ class TransferProgress(object):
def __init__(self, tp):
self.total_objects = tp.total_objects
"""Total number of objects to download"""
"""Total number objects to download"""
self.indexed_objects = tp.indexed_objects
"""Objects which have been indexed"""
@ -70,35 +70,16 @@ class TransferProgress(object):
""""Number of bytes received up to now"""
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
class Remote(object):
def sideband_progress(self, string):
"""Progress output callback
Override this function with your own progress reporting function
:param str string: Progress output from the remote
:param str string: Progress otuput from the remote
"""
pass
def credentials(self, url, username_from_url, allowed_types):
"""Credentials callback
@ -118,26 +99,7 @@ class RemoteCallbacks(object):
Return value: credential
"""
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
pass
def transfer_progress(self, stats):
"""Transfer progress callback
@ -146,6 +108,7 @@ class RemoteCallbacks(object):
:param TransferProgress stats: The progress up to now
"""
pass
def update_tips(self, refname, old, new):
"""Update tips callabck
@ -157,169 +120,6 @@ class RemoteCallbacks(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"""
@ -327,6 +127,20 @@ 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)
@ -336,45 +150,72 @@ 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 a remote to its repository's configuration."""
"""save()
Save a remote to its repository's configuration"""
err = C.git_remote_save(self._remote)
check_error(err)
def fetch(self, refspecs=None, message=None, callbacks=None):
"""Perform a fetch against this remote. Returns a <TransferProgress>
object.
def fetch(self, signature=None, message=None):
"""fetch(signature, message) -> TransferProgress
Perform a fetch against this remote.
"""
fetch_opts = ffi.new('git_fetch_options *')
err = C.git_fetch_init_options(fetch_opts, C.GIT_FETCH_OPTIONS_VERSION)
if signature:
ptr = signature._pointer[:]
else:
ptr = ffi.NULL
if callbacks is None:
callbacks = RemoteCallbacks()
self._stored_exception = None
err = C.git_remote_fetch(self._remote, ptr, to_bytes(message))
if self._stored_exception:
raise self._stored_exception
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
check_error(err)
return TransferProgress(C.git_remote_stats(self._remote))
@ -385,7 +226,10 @@ class Remote(object):
return C.git_remote_refspec_count(self._remote)
def get_refspec(self, n):
"""Return the <Refspec> object at the given position."""
"""get_refspec(n) -> Refspec
Return the refspec at the given position
"""
spec = C.git_remote_get_refspec(self._remote, n)
return Refspec(self, spec)
@ -399,6 +243,12 @@ 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"""
@ -409,33 +259,153 @@ class Remote(object):
return strarray_to_strings(specs)
def push(self, specs, callbacks=None):
"""Push the given refspec to the remote. Raises ``GitError`` on
protocol error or unpack failure.
@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)
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.
def add_fetch(self, spec):
"""add_fetch(refspec)
:param [str] specs: push refspecs to use
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
"""
push_opts = ffi.new('git_push_options *')
err = C.git_push_init_options(push_opts, C.GIT_PUSH_OPTIONS_VERSION)
if callbacks is None:
callbacks = RemoteCallbacks()
cpush = ffi.new('git_push **')
err = C.git_push_new(cpush, self._remote)
check_error(err)
callbacks._fill_push_options(push_opts)
# Build custom callback structure
push = cpush[0]
try:
with StrArray(specs) as refspecs:
err = C.git_remote_push(self._remote, refspecs, push_opts)
check_error(err)
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)
finally:
callbacks._self_handle = None
C.git_push_free(push)
# These functions exist to be called by the git_remote as
# callbacks. They proxy the call to whatever the user set
@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
def get_credentials(fn, url, username, allowed):
"""Call fn and return the credentials object"""
@ -445,158 +415,29 @@ def get_credentials(fn, url, username, allowed):
creds = fn(url_str, username_str, allowed)
credential_type = getattr(creds, 'credential_type', None)
credential_tuple = getattr(creds, 'credential_tuple', None)
if not credential_type or not credential_tuple:
if not hasattr(creds, 'credential_type') \
or not hasattr(creds, 'credential_tuple'):
raise TypeError("credential does not implement interface")
cred_type = credential_type
cred_type = creds.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 = credential_tuple
name, passwd = creds.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 = 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))
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))
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-2017 The pygit2 contributors
# 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,
@ -28,11 +28,6 @@
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):
@ -69,36 +64,3 @@ 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)

@ -1,79 +0,0 @@
# -*- 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-2017 The pygit2 contributors
# 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,
@ -50,62 +50,34 @@ def strarray_to_strings(arr):
return l
class StrArray(object):
"""A git_strarray wrapper
def strings_to_strarray(l):
"""Convert a list of strings to a git_strarray
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)
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.
"""
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
if not isinstance(l, list):
raise TypeError("Value must be a list")
if not isinstance(l, list):
raise TypeError("Value must be a list")
arr = ffi.new('git_strarray *')
strings = ffi.new('char *[]', 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")
# 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[i] = ffi.new('char []', to_bytes(l[i]))
for i in range(len(l)):
if not is_string(l[i]):
raise TypeError("Value must be a string")
self._arr = ffi.new('char *[]', strings)
self._strings = strings
self.array = ffi.new('git_strarray *', [self._arr, len(strings)])
s = ffi.new('char []', to_bytes(l[i]))
refs[i] = s
strings[i] = s
def __enter__(self):
return self.array
arr.strings = strings
arr.count = len(l)
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]
return arr, refs

26
pygit2/version.py Normal file

@ -0,0 +1,26 @@
# 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-2017 The pygit2 contributors
# 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,
@ -28,43 +28,45 @@
"""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
# Get cffi major version
try:
import cffi
except ImportError:
cffi_major_version = None
else:
cffi_major_version = cffi.__version_info__[0]
# Import stuff from pygit2/_utils.py without loading the whole pygit2 package
# Read version from local pygit2/version.py without pulling in
# pygit2/__init__.py
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]
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')
else:
u = str
libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths()
# 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'
pygit2_exts = [os.path.join('src', name) for name in sorted(listdir('src'))
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')
if name.endswith('.c')]
@ -77,6 +79,7 @@ class TestCommand(Command):
def initialize_options(self):
self.args = ''
pass
def finalize_options(self):
pass
@ -85,7 +88,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 = [abspath(bld.build_lib)] + sys.path
sys.path = [os.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,
@ -99,10 +102,43 @@ 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,
universal_newlines=True)
popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE)
stdoutdata, stderrdata = popen.communicate()
if popen.returncode != 0:
print(stderrdata)
@ -120,68 +156,27 @@ 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()
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']
# 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()
setup(name='pygit2',
description='Python bindings for libgit2.',
@ -189,14 +184,18 @@ setup(name='pygit2',
version=__version__,
url='http://github.com/libgit2/pygit2',
classifiers=classifiers,
license='GPLv2 with linking exception',
maintainer=u'J. David Ibáñez',
license='GPLv2',
maintainer=u('J. David Ibáñez'),
maintainer_email='jdavid.ibp@gmail.com',
long_description=long_description,
packages=['pygit2'],
package_data={'pygit2': ['decl.h']},
setup_requires=['cffi'],
install_requires=['cffi', 'six'],
zip_safe=False,
cmdclass=cmdclass,
**extra_args)
install_requires=['cffi'],
ext_modules=[
Extension('_pygit2', pygit2_exts,
include_dirs=[libgit2_include, 'include'],
library_dirs=[libgit2_lib],
libraries=['git2']),
ffi_ext,
],
cmdclass=cmdclass)

399
src/blame.c Normal file

@ -0,0 +1,399 @@
/*
* 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-2017 The pygit2 contributors
* 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,
@ -25,13 +25,14 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDE_pygit2_patch_h
#define INCLUDE_pygit2_patch_h
#ifndef INCLUDE_pygit2_blame_h
#define INCLUDE_pygit2_blame_h
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <git2.h>
#include "types.h"
PyObject* wrap_patch(git_patch *patch);
PyObject* wrap_blame(git_blame *blame, Repository *repo);
#endif

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -27,12 +27,11 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "blob.h"
#include "diff.h"
#include "error.h"
#include "object.h"
#include "patch.h"
#include "utils.h"
#include "object.h"
#include "blob.h"
extern PyObject *GitError;
@ -133,7 +132,7 @@ PyDoc_STRVAR(Blob_size__doc__, "Size.");
PyObject *
Blob_size__get__(Blob *self)
{
return PyInt_FromLongLong(git_blob_rawsize(self->blob));
return PyLong_FromLongLong(git_blob_rawsize(self->blob));
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -81,28 +81,6 @@ 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"
@ -123,7 +101,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);
err = git_branch_move(&c_out, self->reference, c_name, force, NULL, NULL);
if (err == GIT_OK)
return wrap_branch(c_out, self->repo);
else
@ -256,7 +234,6 @@ 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-2017 The pygit2 contributors
* 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,
@ -34,7 +34,6 @@
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-2017 The pygit2 contributors
* 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,
@ -35,7 +35,6 @@
#include "oid.h"
extern PyTypeObject TreeType;
extern PyObject *GitError;
PyDoc_STRVAR(Commit_message_encoding__doc__, "Message encoding.");
@ -80,7 +79,7 @@ PyDoc_STRVAR(Commit_commit_time__doc__, "Commit time.");
PyObject *
Commit_commit_time__get__(Commit *commit)
{
return PyInt_FromLongLong(git_commit_time(commit->commit));
return PyLong_FromLongLong(git_commit_time(commit->commit));
}
@ -89,7 +88,7 @@ PyDoc_STRVAR(Commit_commit_time_offset__doc__, "Commit time offset.");
PyObject *
Commit_commit_time_offset__get__(Commit *commit)
{
return PyInt_FromLong(git_commit_time_offset(commit->commit));
return PyLong_FromLong(git_commit_time_offset(commit->commit));
}
@ -132,11 +131,8 @@ Commit_tree__get__(Commit *commit)
int err;
err = git_commit_tree(&tree, commit->commit);
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 == GIT_ENOTFOUND)
Py_RETURN_NONE;
if (err < 0)
return Error_set(err);

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -28,26 +28,22 @@
#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 DiffDeltaType;
extern PyTypeObject DiffFileType;
extern PyTypeObject DiffHunkType;
extern PyTypeObject DiffLineType;
extern PyTypeObject DiffStatsType;
extern PyTypeObject HunkType;
extern PyTypeObject RepositoryType;
PyObject *
PyTypeObject PatchType;
PyObject*
wrap_diff(git_diff *diff, Repository *repo)
{
Diff *py_diff;
@ -56,363 +52,88 @@ wrap_diff(git_diff *diff, Repository *repo)
if (py_diff) {
Py_INCREF(repo);
py_diff->repo = repo;
py_diff->diff = diff;
py_diff->list = diff;
}
return (PyObject*) py_diff;
}
PyObject *
wrap_diff_file(const git_diff_file *file)
wrap_patch(git_patch *patch)
{
DiffFile *py_file;
Patch *py_patch;
if (!file)
if (!patch)
Py_RETURN_NONE;
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;
}
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;
return (PyObject *) py_file;
}
delta = git_patch_get_delta(patch);
PyObject *
wrap_diff_delta(const git_diff_delta *delta)
{
DiffDelta *py_delta;
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);
if (!delta)
Py_RETURN_NONE;
git_patch_line_stats(NULL, &additions, &deletions, patch);
py_patch->additions = additions;
py_patch->deletions = deletions;
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);
}
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;
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_get_hunk(&hunk, &lines_in_hunk, patch, idx);
if (err < 0)
return Error_set(err);
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);
err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i);
if (err < 0)
return Error_set(err);
py_line = wrap_diff_line(line);
if (py_line == NULL)
return NULL;
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;
PyList_SetItem(py_hunk->lines, j, py_line);
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);
}
}
}
git_patch_free(patch);
return (PyObject *) py_hunk;
return (PyObject*) py_patch;
}
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
DiffFile_dealloc(DiffFile *self)
{
Py_CLEAR(self->id);
if (self->path)
free(self->path);
PyObject_Del(self);
}
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(DiffFile__doc__, "DiffFile object.");
PyTypeObject DiffFileType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.DiffFile", /* tp_name */
sizeof(DiffFile), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DiffFile_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 */
DiffFile__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
DiffFile_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 */
};
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 *
PyObject*
diff_get_patch_byindex(git_diff *diff, size_t idx)
{
git_patch *patch = NULL;
@ -425,11 +146,95 @@ diff_get_patch_byindex(git_diff *diff, size_t idx)
return (PyObject*) wrap_patch(patch);
}
static void
Patch_dealloc(Patch *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 */
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"),
{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;
}
PyGetSetDef Patch_getseters[] = {
GETTER(Patch, is_binary),
{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 */
};
PyObject *
DiffIter_iternext(DiffIter *self)
{
if (self->i < self->n)
return diff_get_patch_byindex(self->diff->diff, self->i++);
return diff_get_patch_byindex(self->diff->list, self->i++);
PyErr_SetNone(PyExc_StopIteration);
return NULL;
@ -478,12 +283,11 @@ PyTypeObject DiffIterType = {
Py_ssize_t
Diff_len(Diff *self)
{
assert(self->diff);
return (Py_ssize_t)git_diff_num_deltas(self->diff);
assert(self->list);
return (Py_ssize_t)git_diff_num_deltas(self->list);
}
PyDoc_STRVAR(Diff_patch__doc__,
"Patch diff string. Can be None in some cases, such as empty commits.");
PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string.");
PyObject *
Diff_patch__get__(Diff *self)
@ -491,15 +295,15 @@ Diff_patch__get__(Diff *self)
git_patch* patch;
git_buf buf = {NULL};
int err = GIT_ERROR;
size_t i, num;
size_t i, len, num;
PyObject *py_patch = NULL;
num = git_diff_num_deltas(self->diff);
num = git_diff_num_deltas(self->list);
if (num == 0)
Py_RETURN_NONE;
for (i = 0; i < num ; ++i) {
err = git_patch_from_diff(&patch, self->diff, i);
for (i = 0, len = 1; i < num ; ++i) {
err = git_patch_from_diff(&patch, self->list, i);
if (err < 0)
goto cleanup;
@ -521,32 +325,30 @@ cleanup:
static void
DiffHunk_dealloc(DiffHunk *self)
Hunk_dealloc(Hunk *self)
{
Py_CLEAR(self->header);
Py_CLEAR(self->lines);
PyObject_Del(self);
}
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."),
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."),
{NULL}
};
PyDoc_STRVAR(DiffHunk__doc__, "DiffHunk object.");
PyDoc_STRVAR(Hunk__doc__, "Hunk object.");
PyTypeObject DiffHunkType = {
PyTypeObject HunkType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.DiffHunk", /* tp_name */
sizeof(DiffHunk), /* tp_basicsize */
"_pygit2.Hunk", /* tp_name */
sizeof(Hunk), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DiffHunk_dealloc, /* tp_dealloc */
(destructor)Hunk_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
@ -562,7 +364,7 @@ PyTypeObject DiffHunkType = {
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
DiffHunk__doc__, /* tp_doc */
Hunk__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
@ -570,7 +372,7 @@ PyTypeObject DiffHunkType = {
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
DiffHunk_members, /* tp_members */
Hunk_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
@ -582,132 +384,6 @@ PyTypeObject DiffHunkType = {
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 *
@ -753,7 +429,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->diff, py_diff->diff);
err = git_diff_merge(self->list, py_diff->list);
if (err < 0)
return Error_set(err);
@ -778,7 +454,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->diff, &opts);
err = git_diff_find_similar(self->list, &opts);
if (err < 0)
return Error_set(err);
@ -795,7 +471,7 @@ Diff_iter(Diff *self)
Py_INCREF(self);
iter->diff = self;
iter->i = 0;
iter->n = git_diff_num_deltas(self->diff);
iter->n = git_diff_num_deltas(self->list);
}
return (PyObject*)iter;
}
@ -805,32 +481,25 @@ Diff_getitem(Diff *self, PyObject *value)
{
size_t i;
if (!PyInt_Check(value))
return NULL; /* FIXME Raise error */
if (PyLong_Check(value) < 0)
return NULL;
i = PyInt_AsSize_t(value);
return diff_get_patch_byindex(self->diff, i);
i = PyLong_AsUnsignedLong(value);
return diff_get_patch_byindex(self->list, 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->diff);
git_diff_free(self->list);
Py_CLEAR(self->repo);
PyObject_Del(self);
}
PyGetSetDef Diff_getseters[] = {
GETTER(Diff, patch),
GETTER(Diff, stats),
{NULL}
};

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -33,13 +33,15 @@
#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_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);
PyObject* wrap_patch(git_patch *patch);
#endif

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -126,10 +126,3 @@ 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-2017 The pygit2 contributors
* 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,
@ -37,6 +37,5 @@ 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-2017 The pygit2 contributors
* 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,
@ -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,9 +53,12 @@ Note_remove(Note *self, PyObject* args)
&ref))
return NULL;
id = (Oid *) self->annotated_id;
err = git_oid_fromstr(&annotated_id, self->annotated_id);
if (err < 0)
return Error_set(err);
err = git_note_remove(self->repo->repo, ref, py_author->signature,
py_committer->signature, &id->oid);
py_committer->signature, &annotated_id);
if (err < 0)
return Error_set(err);
@ -87,7 +90,7 @@ static void
Note_dealloc(Note *self)
{
Py_CLEAR(self->repo);
Py_CLEAR(self->annotated_id);
free(self->annotated_id);
git_note_free(self->note);
PyObject_Del(self);
}
@ -99,7 +102,7 @@ PyMethodDef Note_methods[] = {
};
PyMemberDef Note_members[] = {
MEMBER(Note, annotated_id, T_OBJECT, "id of the annotated object."),
MEMBER(Note, annotated_id, T_STRING, "id of the annotated object."),
{NULL}
};
@ -208,7 +211,7 @@ PyTypeObject NoteIterType = {
};
PyObject *
PyObject*
wrap_note(Repository* repo, git_oid* annotated_id, const char* ref)
{
Note* py_note = NULL;
@ -226,7 +229,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_to_python(annotated_id);
py_note->annotated_id = git_oid_allocfmt(annotated_id);
return (PyObject*) py_note;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -97,7 +97,7 @@ PyDoc_STRVAR(Object_type__doc__,
PyObject *
Object_type__get__(Object *self)
{
return PyInt_FromLong(git_object_type(self->obj));
return PyLong_FromLong(git_object_type(self->obj));
}
PyDoc_STRVAR(Object__pointer__doc__, "Get the object's pointer. For internal use only.");
@ -135,6 +135,24 @@ 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"
@ -143,15 +161,23 @@ PyDoc_STRVAR(Object_peel__doc__,
PyObject *
Object_peel(Object *self, PyObject *py_type)
{
int err;
git_otype otype;
int type = -1, err;
git_object *peeled;
otype = py_object_to_otype(py_type);
if (otype == GIT_OBJ_BAD)
return NULL;
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);
}
err = git_object_peel(&peeled, self->obj, otype);
if (type == -1) {
PyErr_SetString(PyExc_ValueError, "invalid target type");
return NULL;
}
err = git_object_peel(&peeled, self->obj, (git_otype)type);
if (err < 0)
return Error_set(err);

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -209,10 +209,8 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw)
Py_hash_t
Oid_hash(PyObject *oid)
{
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;
/* TODO Randomize (use _Py_HashSecret) to avoid collission DoS attacks? */
return *(Py_hash_t*) ((Oid*)oid)->oid.id;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -65,11 +65,10 @@ option(PyObject *self, PyObject *args)
if (!py_option)
return NULL;
if (!PyInt_Check(py_option))
return Error_type_error(
"option should be an integer, got %.200s", py_option);
if (!PyLong_Check(py_option))
goto on_non_integer;
option = PyInt_AsLong(py_option);
option = PyLong_AsLong(py_option);
switch (option) {
case GIT_OPT_GET_SEARCH_PATH:
@ -80,11 +79,11 @@ option(PyObject *self, PyObject *args)
if (!py_level)
return NULL;
if (!PyInt_Check(py_level))
return Error_type_error(
"level should be an integer, got %.200s", py_level);
if (!PyLong_Check(py_level))
goto on_non_integer;
return get_search_path(PyInt_AsLong(py_level));
return get_search_path(PyLong_AsLong(py_level));
break;
}
case GIT_OPT_SET_SEARCH_PATH:
@ -101,22 +100,23 @@ option(PyObject *self, PyObject *args)
if (!py_path)
return NULL;
if (!PyInt_Check(py_level))
return Error_type_error(
"level should be an integer, got %.200s", py_level);
if (!PyLong_Check(py_level))
goto on_non_integer;
path = py_str_borrow_c_str(&tpath, py_path, NULL);
if (!path)
return NULL;
err = git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, PyInt_AsLong(py_level), path);
err = git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, PyLong_AsLong(py_level), path);
Py_DECREF(tpath);
if (err < 0)
return Error_set(err);
if (err < 0) {
Error_set(err);
return NULL;
}
Py_RETURN_NONE;
break;
}
case GIT_OPT_GET_MWINDOW_SIZE:
@ -124,10 +124,14 @@ option(PyObject *self, PyObject *args)
size_t size;
error = git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &size);
if (error < 0)
return Error_set(error);
if (error < 0) {
Error_set(error);
return NULL;
}
return PyInt_FromSize_t(size);
return PyLong_FromSize_t(size);
break;
}
case GIT_OPT_SET_MWINDOW_SIZE:
@ -139,141 +143,25 @@ option(PyObject *self, PyObject *args)
if (!py_size)
return NULL;
if (!PyInt_Check(py_size))
return Error_type_error(
"size should be an integer, got %.200s", py_size);
if (!PyLong_Check(py_size))
goto on_non_integer;
size = PyInt_AsSize_t(py_size);
size = PyLong_AsSize_t(py_size);
error = git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, size);
if (error < 0)
return Error_set(error);
Py_RETURN_NONE;
}
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)
if (error < 0) {
Error_set(error);
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;
break;
}
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-2017 The pygit2 contributors
* 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,

@ -1,158 +0,0 @@
/*
* 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-2017 The pygit2 contributors
* 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,
@ -44,12 +44,8 @@ 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;
@ -66,6 +62,10 @@ 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,44 +144,7 @@ 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__},
@ -190,7 +153,7 @@ PyMethodDef module_methods[] = {
{NULL}
};
PyObject *
PyObject*
moduleinit(PyObject* m)
{
if (m == NULL)
@ -207,12 +170,6 @@ 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);
@ -257,6 +214,7 @@ 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)
@ -303,7 +261,6 @@ 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
@ -317,11 +274,10 @@ 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_RECREATE_MISSING)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_SAFE_CREATE)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_FORCE)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_ALLOW_CONFLICTS)
ADD_CONSTANT_INT(m, GIT_CHECKOUT_REMOVE_UNTRACKED)
@ -336,19 +292,11 @@ 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)
@ -367,15 +315,9 @@ 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)
@ -388,23 +330,6 @@ 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);
@ -412,6 +337,11 @@ 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)
@ -425,21 +355,8 @@ 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_libgit2_init();
git_threads_init();
return m;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -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_to_python(git_reflog_entry_id_old(entry));
py_entry->oid_new = git_oid_to_python(git_reflog_entry_id_new(entry));
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->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);
err = git_reference_rename(&new_reference, self->reference, c_name, 0, NULL, NULL);
git_reference_free(self->reference);
free(c_name);
if (err < 0)
@ -205,7 +205,10 @@ 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");
"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.");
PyObject *
Reference_target__get__(Reference *self)
@ -227,72 +230,48 @@ Reference_target__get__(Reference *self)
return to_path(c_name);
}
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)
int
Reference_target__set__(Reference *self, PyObject *py_target)
{
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(self);
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s", keywords,
&py_target, &message))
return NULL;
CHECK_REFERENCE_INT(self);
/* 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)
goto error;
return err;
err = git_reference_set_target(&new_ref, self->reference, &oid, message);
err = git_reference_set_target(&new_ref, self->reference, &oid, NULL, NULL);
if (err < 0)
goto error;
git_reference_free(self->reference);
self->reference = new_ref;
Py_RETURN_NONE;
return 0;
}
/* Case 2: Symbolic */
c_name = py_path_to_c_str(py_target);
if (c_name == NULL)
return NULL;
return -1;
err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, message);
err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, NULL, NULL);
free(c_name);
if (err < 0)
goto error;
git_reference_free(self->reference);
self->reference = new_ref;
Py_RETURN_NONE;
return 0;
error:
Error_set(err);
return NULL;
return -1;
}
@ -325,7 +304,7 @@ Reference_type__get__(Reference *self)
CHECK_REFERENCE(self);
c_type = git_reference_type(self->reference);
return PyInt_FromLong(c_type);
return PyLong_FromLong(c_type);
}
@ -356,50 +335,93 @@ 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.\n"
"\n"
"This method is deprecated, please use Reference.peel() instead.");
"Retrieves the object the current reference is pointing to.");
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_otype otype;
git_object *obj;
PyObject *py_type = Py_None;
git_object* obj;
CHECK_REFERENCE(self);
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);
err = git_reference_peel(&obj, self->reference, GIT_OBJ_ANY);
if (err < 0)
return Error_set(err);
return wrap_object(obj, self->repo);
}
PyDoc_STRVAR(RefLogEntry_committer__doc__, "Committer.");
PyObject *
@ -424,16 +446,16 @@ RefLogEntry_init(RefLogEntry *self, PyObject *args, PyObject *kwds)
static void
RefLogEntry_dealloc(RefLogEntry *self)
{
Py_CLEAR(self->oid_old);
Py_CLEAR(self->oid_new);
free(self->oid_old);
free(self->oid_new);
free(self->message);
git_signature_free(self->signature);
PyObject_Del(self);
}
PyMemberDef RefLogEntry_members[] = {
MEMBER(RefLogEntry, oid_new, T_OBJECT, "New oid."),
MEMBER(RefLogEntry, oid_old, T_OBJECT, "Old oid."),
MEMBER(RefLogEntry, oid_new, T_STRING, "New oid."),
MEMBER(RefLogEntry, oid_old, T_STRING, "Old oid."),
MEMBER(RefLogEntry, message, T_STRING, "Message."),
{NULL}
};
@ -492,16 +514,15 @@ 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),
GETTER(Reference, target),
GETSET(Reference, target),
GETTER(Reference, type),
{NULL}
};

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -36,6 +36,7 @@
#include "note.h"
#include "repository.h"
#include "branch.h"
#include "blame.h"
#include "signature.h"
#include <git2/odb_backend.h>
@ -55,9 +56,6 @@ 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)
{
@ -70,25 +68,11 @@ 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)
{
PyObject *backend;
char *path;
int err;
if (kwds && PyDict_Size(kwds) > 0) {
PyErr_SetString(PyExc_TypeError,
@ -96,72 +80,28 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds)
return -1;
}
if (!PyArg_ParseTuple(args, "O", &backend)) {
if (!PyArg_ParseTuple(args, "s", &path))
return -1;
err = git_repository_open(&self->repo, path);
if (err < 0) {
Error_set_str(err, path);
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);
if (self->owned)
git_repository_free(self->repo);
git_repository_free(self->repo);
Py_TYPE(self)->tp_free(self);
}
@ -238,6 +178,38 @@ 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.");
@ -321,11 +293,9 @@ Repository_git_object_lookup_prefix(Repository *self, PyObject *key)
PyDoc_STRVAR(Repository_lookup_branch__doc__,
"lookup_branch(branch_name, [branch_type]) -> Branch\n"
"lookup_branch(branch_name, [branch_type]) -> Object\n"
"\n"
"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').");
"Returns the Git reference for the given branch name (local or remote).");
PyObject *
Repository_lookup_branch(Repository *self, PyObject *args)
@ -349,26 +319,6 @@ 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"
@ -561,8 +511,7 @@ 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.\n"
"Returns None if there is no merge base between the commits");
"Find as good common ancestors as possible for a merge.");
PyObject *
Repository_merge_base(Repository *self, PyObject *args)
@ -586,10 +535,6 @@ 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);
@ -603,7 +548,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 *
@ -612,7 +557,7 @@ Repository_merge_analysis(Repository *self, PyObject *py_id)
int err;
size_t len;
git_oid id;
git_annotated_commit *commit;
git_merge_head *merge_head;
git_merge_analysis_t analysis;
git_merge_preference_t preference;
@ -620,12 +565,12 @@ Repository_merge_analysis(Repository *self, PyObject *py_id)
if (len == 0)
return NULL;
err = git_annotated_commit_lookup(&commit, self->repo, &id);
err = git_merge_head_from_id(&merge_head, self->repo, &id);
if (err < 0)
return Error_set(err);
err = git_merge_analysis(&analysis, &preference, self->repo, (const git_annotated_commit **) &commit, 1);
git_annotated_commit_free(commit);
err = git_merge_analysis(&analysis, &preference, self->repo, (const git_merge_head **) &merge_head, 1);
git_merge_head_free(merge_head);
if (err < 0)
return Error_set(err);
@ -647,7 +592,7 @@ PyDoc_STRVAR(Repository_merge__doc__,
PyObject *
Repository_merge(Repository *self, PyObject *py_oid)
{
git_annotated_commit *commit;
git_merge_head *oid_merge_head;
git_oid oid;
int err;
size_t len;
@ -658,56 +603,15 @@ Repository_merge(Repository *self, PyObject *py_oid)
if (len == 0)
return NULL;
err = git_annotated_commit_lookup(&commit, self->repo, &oid);
err = git_merge_head_from_id(&oid_merge_head, 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_annotated_commit **)&commit, 1,
(const git_merge_head **)&oid_merge_head, 1,
&merge_opts, &checkout_opts);
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);
git_merge_head_free(oid_merge_head);
if (err < 0)
return Error_set(err);
@ -862,88 +766,8 @@ 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_name, author, committer, message, tree, parents[, encoding]) -> Oid\n"
"create_commit(reference, author, committer, message, tree, parents[, encoding]) -> Oid\n"
"\n"
"Create a new commit object, return its oid.");
@ -1069,7 +893,7 @@ Repository_create_tag(Repository *self, PyObject *args)
PyDoc_STRVAR(Repository_create_branch__doc__,
"create_branch(name, commit, force=False) -> Branch\n"
"create_branch(name, commit, force=False) -> bytes\n"
"\n"
"Create a new branch \"name\" which points to a commit.\n"
"\n"
@ -1094,7 +918,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);
err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force, NULL, NULL);
if (err < 0)
return Error_set(err);
@ -1141,66 +965,10 @@ out:
}
PyDoc_STRVAR(Repository_listall_reference_objects__doc__,
"listall_reference_objects() -> [Reference, ...]\n"
"\n"
"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"
"listall_branches([flags]) -> [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");
"Return a tuple with all the branches in the repository.");
PyObject *
Repository_listall_branches(Repository *self, PyObject *args)
@ -1254,173 +1022,6 @@ 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"
@ -1484,7 +1085,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);
err = git_reference_create(&c_reference, self->repo, c_name, &oid, force, NULL, NULL);
if (err < 0)
return Error_set(err);
@ -1518,7 +1119,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);
c_target, force, NULL, NULL);
if (err < 0)
return Error_set(err);
@ -1563,7 +1164,7 @@ Repository_status(Repository *self)
path = entry->head_to_index->old_file.path;
else
path = entry->index_to_workdir->old_file.path;
status = PyInt_FromLong((long) entry->status);
status = PyLong_FromLong((long) entry->status);
err = PyDict_SetItemString(dict, path, status);
Py_CLEAR(status);
@ -1605,7 +1206,7 @@ Repository_status_file(Repository *self, PyObject *value)
free(path);
return err_obj;
}
return PyInt_FromLong(status);
return PyLong_FromLong(status);
}
@ -1648,7 +1249,7 @@ Repository_TreeBuilder(Repository *self, PyObject *args)
}
}
err = git_treebuilder_new(&bld, self->repo, tree);
err = git_treebuilder_create(&bld, tree);
if (must_free != NULL)
git_tree_free(must_free);
@ -1741,8 +1342,8 @@ Repository_create_note(Repository *self, PyObject* args)
if (err < 0)
return Error_set(err);
err = git_note_create(&note_id, self->repo, ref, py_author->signature,
py_committer->signature,
err = git_note_create(&note_id, self->repo, py_author->signature,
py_committer->signature, ref,
&annotated_id, message, force);
if (err < 0)
return Error_set(err);
@ -1773,13 +1374,79 @@ 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 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_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_HARD: resets head to point to oid, and resets too the working copy and the content of the index.\n");
PyObject *
@ -1803,36 +1470,17 @@ 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);
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL, 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),
@ -1840,16 +1488,11 @@ 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),
@ -1859,19 +1502,16 @@ 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),
GETTER(Repository, head),
GETSET(Repository, head),
GETTER(Repository, head_is_detached),
GETTER(Repository, head_is_unborn),
GETTER(Repository, is_empty),
@ -1884,7 +1524,7 @@ PyGetSetDef Repository_getseters[] = {
PyDoc_STRVAR(Repository__doc__,
"Repository(backend) -> Repository\n"
"Repository(path) -> Repository\n"
"\n"
"Git repository.");

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -33,8 +33,6 @@
#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);
@ -58,8 +56,6 @@ 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);
@ -74,6 +70,5 @@ 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-2017 The pygit2 contributors
* 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,
@ -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 = "utf-8";
char *email, *encoding = "ascii";
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__(Signature *self)
Signature__pointer__get__(Repository *self)
{
/* Bytes means a raw buffer */
return PyBytes_FromStringAndSize((char *) &self->signature, sizeof(git_signature *));
return PyBytes_FromStringAndSize((char *) &self->repo, sizeof(git_repository *));
}
PyDoc_STRVAR(Signature__encoding__doc__, "Encoding.");
@ -158,7 +158,7 @@ PyDoc_STRVAR(Signature_time__doc__, "Unix time.");
PyObject *
Signature_time__get__(Signature *self)
{
return PyInt_FromLongLong(self->signature->when.time);
return PyLong_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 PyInt_FromLong(self->signature->when.offset);
return PyLong_FromLong(self->signature->when.offset);
}
PyGetSetDef Signature_getseters[] = {

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -111,11 +111,7 @@ PyDoc_STRVAR(Tag__message__doc__, "Tag message (bytes).");
PyObject *
Tag__message__get__(Tag *self)
{
const char *message;
message = git_tag_message(self->tag);
if (!message)
Py_RETURN_NONE;
return PyBytes_FromString(message);
return PyBytes_FromString(git_tag_message(self->tag));
}
PyMethodDef Tag_methods[] = {

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -54,7 +54,7 @@ PyDoc_STRVAR(TreeEntry_filemode__doc__, "Filemode.");
PyObject *
TreeEntry_filemode__get__(TreeEntry *self)
{
return PyInt_FromLong(git_tree_entry_filemode(self->entry));
return PyLong_FromLong(git_tree_entry_filemode(self->entry));
}
@ -67,24 +67,6 @@ 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 *
@ -105,20 +87,10 @@ 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 */
@ -127,14 +99,7 @@ TreeEntry_richcompare(PyObject *a, PyObject *b, int op)
return Py_NotImplemented;
}
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);
cmp =git_tree_entry_cmp(((TreeEntry*)a)->entry, ((TreeEntry*)b)->entry);
switch (op) {
case Py_LT:
res = (cmp <= 0) ? Py_True: Py_False;
@ -172,28 +137,17 @@ 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 = {
@ -206,7 +160,7 @@ PyTypeObject TreeEntryType = {
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
(reprfunc)TreeEntry_repr, /* tp_repr */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
@ -290,7 +244,7 @@ Tree_fix_index(Tree *self, PyObject *py_index)
size_t len;
long slen;
index = PyInt_AsLong(py_index);
index = PyLong_AsLong(py_index);
if (PyErr_Occurred())
return -1;
@ -359,7 +313,7 @@ Tree_getitem(Tree *self, PyObject *value)
int err;
/* Case 1: integer */
if (PyInt_Check(value))
if (PyLong_Check(value))
return Tree_getitem_by_index(self, value);
/* Case 2: byte or text string */

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -88,7 +88,7 @@ TreeBuilder_write(TreeBuilder *self)
int err;
git_oid oid;
err = git_treebuilder_write(&oid, self->bld);
err = git_treebuilder_write(&oid, self->repo->repo, self->bld);
if (err < 0)
return Error_set(err);

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,
@ -32,8 +32,8 @@
#include <Python.h>
#include <git2.h>
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 26)
#error You need a compatible libgit2 version (v0.26.x)
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 21)
#error You need a compatible libgit2 version (v0.21.x)
#endif
/*
@ -47,7 +47,6 @@ 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;
@ -80,7 +79,7 @@ typedef struct {
PyObject_HEAD
Repository *repo;
git_note *note;
PyObject* annotated_id;
char* annotated_id;
} Note;
typedef struct {
@ -90,41 +89,30 @@ typedef struct {
char* ref;
} NoteIter;
/* git_patch */
typedef struct {
PyObject_HEAD
git_patch *patch;
PyObject* hunks;
} Patch;
/* git_diff */
SIMPLE_TYPE(Diff, git_diff, diff)
/* git _diff */
SIMPLE_TYPE(Diff, git_diff, list)
typedef struct {
PyObject_HEAD
Diff *diff;
Diff* diff;
size_t i;
size_t n;
} DiffIter;
typedef struct {
PyObject_HEAD
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;
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;
typedef struct {
PyObject_HEAD
@ -133,20 +121,8 @@ typedef struct {
int old_lines;
int new_start;
int new_lines;
PyObject *header;
} DiffHunk;
} Hunk;
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)
@ -171,6 +147,12 @@ 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)
@ -182,8 +164,8 @@ typedef Reference Branch;
typedef struct {
PyObject_HEAD
git_signature *signature;
PyObject *oid_old;
PyObject *oid_new;
char *oid_old;
char *oid_new;
char *message;
} RefLogEntry;
@ -203,4 +185,27 @@ 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-2017 The pygit2 contributors
* 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,
@ -31,10 +31,6 @@
#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
@ -83,7 +79,8 @@ py_str_borrow_c_str(PyObject **tvalue, PyObject *value, const char *encoding)
}
/* Type error */
Error_type_error("unexpected %.200s", value);
PyErr_Format(PyExc_TypeError, "unexpected %.200s",
Py_TYPE(value)->tp_name);
return NULL;
}
@ -93,7 +90,7 @@ py_str_borrow_c_str(PyObject **tvalue, PyObject *value, const char *encoding)
PyObject *
get_pylist_from_git_strarray(git_strarray *strarray)
{
size_t index;
int index;
PyObject *new_list;
new_list = PyList_New(strarray->count);
@ -156,43 +153,3 @@ 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-2017 The pygit2 contributors
* 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,
@ -40,17 +40,14 @@
#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 PyInt_AsSize_t (size_t)PyInt_AsLong
#define PyInt_FromLongLong PyInt_FromLong
#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 PyBytes_AS_STRING PyString_AS_STRING
#define PyBytes_AsString PyString_AsString
#define PyBytes_AsStringAndSize PyString_AsStringAndSize
@ -61,16 +58,17 @@
#define to_path(x) to_bytes(x)
#define to_encoding(x) to_bytes(x)
#else
#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 PyInteger_Type PyLong_Type
#define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict")
#define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict")
#define PyString_FromFormat(s, ...) PyUnicode_FromFormat(s, __VA_ARGS__)
#endif
#ifdef PYPY_VERSION
#define PyLong_AsSize_t (size_t)PyLong_AsUnsignedLong
#endif
#ifndef Py_hash_t
#define Py_hash_t long
#endif
@ -121,8 +119,6 @@ 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-2017 The pygit2 contributors
* 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,
@ -98,13 +98,13 @@ PyDoc_STRVAR(Walker_sort__doc__,
PyObject *
Walker_sort(Walker *self, PyObject *py_sort_mode)
{
long sort_mode;
int sort_mode;
sort_mode = PyInt_AsLong(py_sort_mode);
sort_mode = (int)PyLong_AsLong(py_sort_mode);
if (sort_mode == -1 && PyErr_Occurred())
return NULL;
git_revwalk_sorting(self->walk, (unsigned int)sort_mode);
git_revwalk_sorting(self->walk, sort_mode);
Py_RETURN_NONE;
}

@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 The pygit2 contributors
* 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,

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
# 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,

Binary file not shown.

Binary file not shown.

@ -1,74 +0,0 @@
# -*- 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)

@ -1,65 +0,0 @@
# -*- 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-2017 The pygit2 contributors
# 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,
@ -31,7 +31,7 @@ from __future__ import absolute_import
from __future__ import unicode_literals
import unittest
import pygit2
from pygit2 import Signature, Oid
from pygit2 import Signature
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 = [
(Oid(hex='acecd5ea2924a4b900e7e149496e1f4b57976e51'), 1,
('acecd5ea2924a4b900e7e149496e1f4b57976e51', 1,
Signature('J. David Ibañez', 'jdavid@itaapy.com',
1297179898, 60, encoding='utf-8'), True),
(Oid(hex='6aaa262e655dd54252e5813c8e5acd7780ed097d'), 2,
('6aaa262e655dd54252e5813c8e5acd7780ed097d', 2,
Signature('J. David Ibañez', 'jdavid@itaapy.com',
1297696877, 60, encoding='utf-8'), False),
(Oid(hex='4ec4389a8068641da2d6578db0419484972284c8'), 3,
('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-2017 The pygit2 contributors
# 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,
@ -30,7 +30,6 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from os.path import dirname, join
import io
import unittest
import pygit2
@ -113,19 +112,6 @@ 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-2017 The pygit2 contributors
# 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,
@ -39,135 +39,6 @@ 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')
@ -217,14 +88,6 @@ 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')
@ -296,7 +159,6 @@ class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase):
def set_bad_upstream():
master.upstream = 2.5
self.assertRaises(TypeError, set_bad_upstream)
master.upstream = None

@ -1,71 +0,0 @@
# -*- 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-2017 The pygit2 contributors
# 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,
@ -30,36 +30,22 @@
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 (in python2 mode) raises TypeError on writing to read-only, so
# we need to check and change the test accordingly
# pypy 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))
@ -152,7 +138,7 @@ class CommitTest(utils.BareRepoTestCase):
commit = self.repo[COMMIT_SHA]
error_type = AttributeError if not pypy2 else TypeError
error_type = AttributeError if not __pypy__ 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-2017 The pygit2 contributors
# 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,
@ -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, 6)
config.add_file(CONFIG_FILENAME, 5)
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-2017 The pygit2 contributors
# 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,
@ -31,7 +31,6 @@
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
@ -61,46 +60,34 @@ 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):
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")
def credentials_cb(url, username, allowed):
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
raise Exception("I don't know the password")
url = "https://github.com/github/github"
remote = self.repo.create_remote("github", url)
remote = self.repo.create_remote("github", "https://github.com/github/github")
remote.credentials = credentials_cb
self.assertRaises(Exception, lambda: remote.fetch(callbacks=MyCallbacks()))
self.assertRaises(Exception, remote.fetch)
def test_bad_cred_type(self):
class MyCallbacks(pygit2.RemoteCallbacks):
@staticmethod
def credentials(url, username, allowed):
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
return Keypair("git", "foo.pub", "foo", "sekkrit")
def credentials_cb(url, username, allowed):
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
return Keypair("git", "foo.pub", "foo", "sekkrit")
url = "https://github.com/github/github"
remote = self.repo.create_remote("github", url)
self.assertRaises(TypeError, lambda: remote.fetch(callbacks=MyCallbacks()))
remote = self.repo.create_remote("github", "https://github.com/github/github")
remote.credentials = credentials_cb
self.assertRaises(TypeError, remote.fetch)
class CallableCredentialTest(utils.RepoTestCase):
def test_user_pass(self):
credentials = UserPass("libgit2", "libgit2")
callbacks = pygit2.RemoteCallbacks(credentials=credentials)
remote = self.repo.create_remote("bb", "https://bitbucket.org/libgit2/testgitrepository.git")
remote.credentials = UserPass("libgit2", "libgit2")
url = "https://bitbucket.org/libgit2/testgitrepository.git"
remote = self.repo.create_remote("bb", url)
remote.fetch(callbacks=callbacks)
remote.fetch()
if __name__ == '__main__':
unittest.main()

@ -1,106 +0,0 @@
# -*- 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'))

@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2017 The pygit2 contributors
# 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,
@ -33,8 +33,6 @@ import unittest
import pygit2
from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED
from pygit2 import GIT_DIFF_IGNORE_WHITESPACE, GIT_DIFF_IGNORE_WHITESPACE_EOL
from pygit2 import GIT_DIFF_SHOW_BINARY
from pygit2 import GIT_DELTA_RENAMED
from . import utils
from itertools import chain
@ -64,22 +62,6 @@ index 297efb8..0000000
-c/d contents
"""
PATCH_BINARY = """diff --git a/binary_file b/binary_file
index 86e5c10..b835d73 100644
Binary files a/binary_file and b/binary_file differ
"""
PATCH_BINARY_SHOW = """diff --git a/binary_file b/binary_file
index 86e5c1008b5ce635d3e3fffa4434c5eccd8f00b6..b835d73543244b6694f36a8c5dfdffb71b153db7 100644
GIT binary patch
literal 8
Pc${NM%FIhFs^kIy3n&7R
literal 8
Pc${NM&PdElPvrst3ey5{
"""
DIFF_HEAD_TO_INDEX_EXPECTED = [
'staged_changes',
'staged_changes_file_deleted',
@ -118,11 +100,6 @@ HUNK_EXPECTED = """- a contents 2
+ a contents
"""
STATS_EXPECTED = """ a | 2 +-
c/d | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
delete mode 100644 c/d
"""
class DiffDirtyTest(utils.DirtyRepoTestCase):
def test_diff_empty_index(self):
@ -130,11 +107,11 @@ class DiffDirtyTest(utils.DirtyRepoTestCase):
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = head.tree.diff_to_index(repo.index)
files = [patch.delta.new_file.path for patch in diff]
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files)
diff = repo.diff('HEAD', cached=True)
files = [patch.delta.new_file.path for patch in diff]
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files)
def test_workdir_to_tree(self):
@ -142,16 +119,16 @@ class DiffDirtyTest(utils.DirtyRepoTestCase):
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = head.tree.diff_to_workdir()
files = [patch.delta.new_file.path for patch in diff]
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files)
diff = repo.diff('HEAD')
files = [patch.delta.new_file.path for patch in diff]
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files)
def test_index_to_workdir(self):
diff = self.repo.diff()
files = [patch.delta.new_file.path for patch in diff]
files = [patch.new_file_path for patch in diff]
self.assertEqual(DIFF_INDEX_TO_WORK_EXPECTED, files)
@ -168,15 +145,15 @@ class DiffTest(utils.BareRepoTestCase):
head = repo[repo.lookup_reference('HEAD').resolve().target]
diff = self.repo.index.diff_to_tree(head.tree)
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
files = [patch.new_file_path.split('/')[0] for patch in diff]
self.assertEqual([x.name for x in head.tree], files)
diff = head.tree.diff_to_index(repo.index)
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
files = [patch.new_file_path.split('/')[0] for patch in diff]
self.assertEqual([x.name for x in head.tree], files)
diff = repo.diff('HEAD', cached=True)
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
files = [patch.new_file_path.split('/')[0] for patch in diff]
self.assertEqual([x.name for x in head.tree], files)
def test_diff_tree(self):
@ -196,9 +173,9 @@ class DiffTest(utils.BareRepoTestCase):
self.assertEqual(hunk.new_start, 1)
self.assertEqual(hunk.new_lines, 1)
self.assertEqual(patch.delta.old_file.path, 'a')
self.assertEqual(patch.delta.new_file.path, 'a')
self.assertEqual(patch.delta.is_binary, False)
self.assertEqual(patch.old_file_path, 'a')
self.assertEqual(patch.new_file_path, 'a')
self.assertEqual(patch.is_binary, False)
_test(commit_a.tree.diff_to_tree(commit_b.tree))
_test(self.repo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2))
@ -211,14 +188,14 @@ class DiffTest(utils.BareRepoTestCase):
def get_context_for_lines(diff):
hunks = chain(*map(lambda x: x.hunks, [p for p in diff]))
lines = chain(*map(lambda x: x.lines, hunks))
return map(lambda x: x.origin, lines)
return map(lambda x: x[0], lines)
entries = [p.delta.new_file.path for p in diff]
entries = [p.new_file_path for p in diff]
self.assertAll(lambda x: commit_a.tree[x], entries)
self.assertAll(lambda x: '-' == x, get_context_for_lines(diff))
diff_swaped = commit_a.tree.diff_to_tree(swap=True)
entries = [p.delta.new_file.path for p in diff_swaped]
entries = [p.new_file_path for p in diff_swaped]
self.assertAll(lambda x: commit_a.tree[x], entries)
self.assertAll(lambda x: '+' == x, get_context_for_lines(diff_swaped))
@ -254,13 +231,13 @@ class DiffTest(utils.BareRepoTestCase):
self.assertTrue(diff_c is not None)
# assertIn / assertNotIn are 2.7 only
self.assertFalse('b' in [patch.delta.new_file.path for patch in diff_b])
self.assertTrue('b' in [patch.delta.new_file.path for patch in diff_c])
self.assertFalse('b' in [patch.new_file_path for patch in diff_b])
self.assertTrue('b' in [patch.new_file_path for patch in diff_c])
diff_b.merge(diff_c)
# assertIn is 2.7 only
self.assertTrue('b' in [patch.delta.new_file.path for patch in diff_b])
self.assertTrue('b' in [patch.new_file_path for patch in diff_b])
patch = diff_b[0]
hunk = patch.hunks[0]
@ -269,8 +246,8 @@ class DiffTest(utils.BareRepoTestCase):
self.assertEqual(hunk.new_start, 1)
self.assertEqual(hunk.new_lines, 1)
self.assertEqual(patch.delta.old_file.path, 'a')
self.assertEqual(patch.delta.new_file.path, 'a')
self.assertEqual(patch.old_file_path, 'a')
self.assertEqual(patch.new_file_path, 'a')
def test_diff_patch(self):
commit_a = self.repo[COMMIT_SHA1_1]
@ -284,9 +261,9 @@ class DiffTest(utils.BareRepoTestCase):
commit_a = self.repo[COMMIT_SHA1_1]
commit_b = self.repo[COMMIT_SHA1_2]
patch = commit_a.tree.diff_to_tree(commit_b.tree)[0]
self.assertEqual(patch.delta.old_file.id.hex,
self.assertEqual(patch.old_id,
'7f129fd57e31e935c6d60a0c794efe4e6927664b')
self.assertEqual(patch.delta.new_file.id.hex,
self.assertEqual(patch.new_id,
'af431f20fc541ed6d5afede3e2dc7160f6f01f16')
def test_hunk_content(self):
@ -294,7 +271,7 @@ class DiffTest(utils.BareRepoTestCase):
commit_b = self.repo[COMMIT_SHA1_2]
patch = commit_a.tree.diff_to_tree(commit_b.tree)[0]
hunk = patch.hunks[0]
lines = ('{0} {1}'.format(x.origin, x.content) for x in hunk.lines)
lines = ('{0} {1}'.format(*x) for x in hunk.lines)
self.assertEqual(HUNK_EXPECTED, ''.join(lines))
def test_find_similar(self):
@ -305,35 +282,9 @@ class DiffTest(utils.BareRepoTestCase):
#~ --find-copies-harder during rename transformion...
diff = commit_a.tree.diff_to_tree(commit_b.tree,
GIT_DIFF_INCLUDE_UNMODIFIED)
self.assertAll(lambda x: x.delta.status != GIT_DELTA_RENAMED, diff)
self.assertAll(lambda x: x.delta.status_char() != 'R', diff)
self.assertAll(lambda x: x.status != 'R', diff)
diff.find_similar()
self.assertAny(lambda x: x.delta.status == GIT_DELTA_RENAMED, diff)
self.assertAny(lambda x: x.delta.status_char() == 'R', diff)
def test_diff_stats(self):
commit_a = self.repo[COMMIT_SHA1_1]
commit_b = self.repo[COMMIT_SHA1_2]
diff = commit_a.tree.diff_to_tree(commit_b.tree)
stats = diff.stats
self.assertEqual(1, stats.insertions)
self.assertEqual(2, stats.deletions)
self.assertEqual(2, stats.files_changed)
formatted = stats.format(format=pygit2.GIT_DIFF_STATS_FULL |
pygit2.GIT_DIFF_STATS_INCLUDE_SUMMARY,
width=80)
self.assertEqual(STATS_EXPECTED, formatted)
class BinaryDiffTest(utils.BinaryFileRepoTestCase):
def test_binary_diff(self):
repo = self.repo
diff = repo.diff('HEAD', 'HEAD^')
self.assertEqual(PATCH_BINARY, diff.patch)
diff = repo.diff('HEAD', 'HEAD^', flags=GIT_DIFF_SHOW_BINARY)
self.assertEqual(PATCH_BINARY_SHOW, diff.patch)
self.assertAny(lambda x: x.status == 'R', diff)
if __name__ == '__main__':
unittest.main()

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