Compare commits
248 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
371c97cadb | ||
![]() |
ab3bb01249 | ||
![]() |
62c70e852d | ||
![]() |
e81f45c4c6 | ||
![]() |
295166bb64 | ||
![]() |
96d37e16a9 | ||
![]() |
2ce8b952c4 | ||
![]() |
e41f71f458 | ||
![]() |
c6305a062b | ||
![]() |
87beb76dcc | ||
![]() |
f18de427bf | ||
![]() |
f841c62fa6 | ||
![]() |
035d4a9396 | ||
![]() |
21d668421f | ||
![]() |
b88dc86842 | ||
![]() |
8559b2da20 | ||
![]() |
9325494d6f | ||
![]() |
74717bed55 | ||
![]() |
3818555e14 | ||
![]() |
706c60c4ab | ||
![]() |
0733ba4da3 | ||
![]() |
320ee5e733 | ||
![]() |
d6716e035a | ||
![]() |
d14438725e | ||
![]() |
dd57c9b366 | ||
![]() |
f2c89a760a | ||
![]() |
f37cf25b8e | ||
![]() |
11ff7a99eb | ||
![]() |
9335819795 | ||
![]() |
784583d21e | ||
![]() |
b81810e9cb | ||
![]() |
7ee851273a | ||
![]() |
1fadc2eae0 | ||
![]() |
453fd8a9a3 | ||
![]() |
630d905e73 | ||
![]() |
9be907983f | ||
![]() |
b31ac50210 | ||
![]() |
da233b16c5 | ||
![]() |
1a842ff8bd | ||
![]() |
cbbf9f1f87 | ||
![]() |
a294655aa5 | ||
![]() |
5a940987cd | ||
![]() |
354d56a95c | ||
![]() |
8327e1bee3 | ||
![]() |
15326b731f | ||
![]() |
f5cd6da307 | ||
![]() |
803b1cb154 | ||
![]() |
d622e87654 | ||
![]() |
819cbff552 | ||
![]() |
4fbc1f1c05 | ||
![]() |
5c061cbb0a | ||
![]() |
68817aad4f | ||
![]() |
0b513d57fa | ||
![]() |
074a726d7f | ||
![]() |
950249442e | ||
![]() |
809fe33b8a | ||
![]() |
6402302002 | ||
![]() |
77f0585645 | ||
![]() |
acdec78617 | ||
![]() |
66280af83a | ||
![]() |
b2a34bd901 | ||
![]() |
44ef9ad2e9 | ||
![]() |
db213113f4 | ||
![]() |
ee28de65a0 | ||
![]() |
825f3e45bd | ||
![]() |
00dd78bf1b | ||
![]() |
e2393a5e24 | ||
![]() |
da59cb1c92 | ||
![]() |
e873c6a363 | ||
![]() |
865c2f0e82 | ||
![]() |
6d6931cd26 | ||
![]() |
9b364dc7f3 | ||
![]() |
578cf58cd7 | ||
![]() |
554f167353 | ||
![]() |
54e4da837b | ||
![]() |
faf6a63d25 | ||
![]() |
30980751cf | ||
![]() |
dae61ded38 | ||
![]() |
0e270c72bd | ||
![]() |
1afbde0d7f | ||
![]() |
7fe7a4da8d | ||
![]() |
fd1e9e3d35 | ||
![]() |
cdd57b2c0f | ||
![]() |
ffc514fa24 | ||
![]() |
c57a3aeb22 | ||
![]() |
ca444f3c7d | ||
![]() |
96beae5c82 | ||
![]() |
68de0f8bb6 | ||
![]() |
df53551cb2 | ||
![]() |
5864b17b57 | ||
![]() |
6c4fa88d07 | ||
![]() |
e96d0286b8 | ||
![]() |
4fa43e234e | ||
![]() |
4416f65fe1 | ||
![]() |
5dcc793aff | ||
![]() |
30f539ff35 | ||
![]() |
df30f9213f | ||
![]() |
50c0569cf0 | ||
![]() |
d8fd2e78d8 | ||
![]() |
fd9a39a91b | ||
![]() |
270dad8cd3 | ||
![]() |
51915ddf0e | ||
![]() |
f2864c0511 | ||
![]() |
456bf59a88 | ||
![]() |
22021c67fc | ||
![]() |
c1d831c98a | ||
![]() |
391a3a74e9 | ||
![]() |
141f0abe62 | ||
![]() |
6b926494db | ||
![]() |
cde5b5170b | ||
![]() |
735510f14d | ||
![]() |
bc424e342f | ||
![]() |
3470fbc1c6 | ||
![]() |
487fb5913e | ||
![]() |
33cf1a1ca2 | ||
![]() |
95ad6b1b0a | ||
![]() |
fa60e2233d | ||
![]() |
daff45f2d4 | ||
![]() |
a5cfea21a7 | ||
![]() |
99dfce9ab8 | ||
![]() |
7a8474cd44 | ||
![]() |
91bb93d266 | ||
![]() |
13f4ddec1d | ||
![]() |
f92d38e25f | ||
![]() |
70edbf256a | ||
![]() |
203335bd63 | ||
![]() |
64150d3535 | ||
![]() |
d25a0d61de | ||
![]() |
f5aa1829ac | ||
![]() |
9db8737364 | ||
![]() |
cf439e4286 | ||
![]() |
eadc2a320f | ||
![]() |
2b083a1509 | ||
![]() |
681c7d4341 | ||
![]() |
8a66da1278 | ||
![]() |
0d2bc05708 | ||
![]() |
b8e6852d26 | ||
![]() |
563cb9018e | ||
![]() |
ac2e363d04 | ||
![]() |
ab97c08f72 | ||
![]() |
7b97ade6ce | ||
![]() |
e4ef8ea5c2 | ||
![]() |
50f4b20e7d | ||
![]() |
802976535a | ||
![]() |
ade211de60 | ||
![]() |
8911416d4f | ||
![]() |
4b607b8256 | ||
![]() |
ec23762c09 | ||
![]() |
ac7738bbb3 | ||
![]() |
29a8dbc6b2 | ||
![]() |
f28a199351 | ||
![]() |
becc265c78 | ||
![]() |
25d02259df | ||
![]() |
27e3450232 | ||
![]() |
4f00dad086 | ||
![]() |
9dd74dd593 | ||
![]() |
c5eae8942d | ||
![]() |
2fdfdcdc4b | ||
![]() |
7ff6f6efb7 | ||
![]() |
f09bbe79a8 | ||
![]() |
81520c9c62 | ||
![]() |
6939b9b203 | ||
![]() |
cc898d29e5 | ||
![]() |
060b3fbaec | ||
![]() |
5469f0c891 | ||
![]() |
74b81bf180 | ||
![]() |
e46119838b | ||
![]() |
8a196f656b | ||
![]() |
deb50536f0 | ||
![]() |
6da3d8f8a8 | ||
![]() |
52dd956896 | ||
![]() |
1f98ba6495 | ||
![]() |
d63c2d4fd7 | ||
![]() |
5b50579790 | ||
![]() |
42d81e33ec | ||
![]() |
0ce4d3b9a8 | ||
![]() |
3091c7aa87 | ||
![]() |
654e4bf56f | ||
![]() |
c072a77e4b | ||
![]() |
d3d60c75f8 | ||
![]() |
1b9cb54927 | ||
![]() |
efb49f8418 | ||
![]() |
7a6465833b | ||
![]() |
b69a2f6197 | ||
![]() |
b3025e3fe1 | ||
![]() |
f923e20f2d | ||
![]() |
cd7e2b21be | ||
![]() |
1f755c601c | ||
![]() |
08f2956e97 | ||
![]() |
99e1cad393 | ||
![]() |
da98890bd1 | ||
![]() |
ca39a65054 | ||
![]() |
cf56a695f9 | ||
![]() |
8b05b296c2 | ||
![]() |
b2ffc8a8d5 | ||
![]() |
e32df6a1c8 | ||
![]() |
fe849f659e | ||
![]() |
c099655fc0 | ||
![]() |
f5485bb86f | ||
![]() |
318c6a8bee | ||
![]() |
367084e3c1 | ||
![]() |
50a70086bf | ||
![]() |
9a4e002864 | ||
![]() |
71ca619e26 | ||
![]() |
38b1975991 | ||
![]() |
2d3f9d8e55 | ||
![]() |
d35ecf945a | ||
![]() |
86c51eadbf | ||
![]() |
404645042b | ||
![]() |
ef67c36d8c | ||
![]() |
bc668751a4 | ||
![]() |
adb351f7b3 | ||
![]() |
fbb11775a3 | ||
![]() |
c7609efc4b | ||
![]() |
82d5214321 | ||
![]() |
c91fdf1d21 | ||
![]() |
1cb62ab578 | ||
![]() |
69f539851b | ||
![]() |
3d896769d0 | ||
![]() |
7130df3a5e | ||
![]() |
94be744ba6 | ||
![]() |
40946cd795 | ||
![]() |
2b2beb8094 | ||
![]() |
c87d28c9a8 | ||
![]() |
d64dd15bd2 | ||
![]() |
9cce003efe | ||
![]() |
d4da228c0e | ||
![]() |
fcd4b9446b | ||
![]() |
fa380c0adb | ||
![]() |
718a2df1d5 | ||
![]() |
30e57e13e3 | ||
![]() |
0ba17a5b46 | ||
![]() |
4709cae1a1 | ||
![]() |
1361b2cce9 | ||
![]() |
7f21f6eb63 | ||
![]() |
f5a5dfc18a | ||
![]() |
961d007b02 | ||
![]() |
909e03d8fc | ||
![]() |
3ee1c798b2 | ||
![]() |
9771adf862 | ||
![]() |
8881b75aaa | ||
![]() |
e7fdaf2510 | ||
![]() |
061961f119 | ||
![]() |
c62a79cf81 | ||
![]() |
8a6e61551c | ||
![]() |
df0e11726e | ||
![]() |
d4fc7010b3 | ||
![]() |
78d134c016 |
.gitattributes.gitignore.mailmap.travis.sh.travis.ymlCHANGELOG.rstREADME.rstappveyor.yml
docs
backends.rstconf.pydevelopment.rstdiff.rstgeneral.rstindex.rstinstall.rstmerge.rstobjects.rstrecipes.rst
recipes
references.rstremotes.rstrepository.rstsubmodule.rstworking-copy.rstpygit2
__init__.py_build.py_run.pyblame.pyconfig.pycredentials.pydecl.herrors.pyffi.pyindex.pypy2.pypy3.pyrefspec.pyremote.pyrepository.pysettings.pysubmodule.pyutils.py
setup.pysrc
blob.cblob.hbranch.cbranch.hcommit.ccommit.hdiff.cdiff.herror.cerror.hnote.cnote.hobject.cobject.hoid.coid.hoptions.coptions.hpatch.cpatch.hpygit2.creference.creference.hrepository.crepository.hsignature.csignature.htag.ctag.htree.ctree.htreebuilder.ctreebuilder.htypes.hutils.cutils.hwalker.cwalker.h
test
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.h text eol=lf
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ build
|
||||
dist
|
||||
pygit2.so
|
||||
_pygit2.so
|
||||
.tox/
|
||||
test/*.pyc
|
||||
test/__pycache__
|
||||
pygit2/*.pyc
|
||||
|
10
.mailmap
10
.mailmap
@ -16,3 +16,13 @@ Xu Tao <xutao881001@gmail.com>
|
||||
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.22 https://github.com/libgit2/libgit2.git
|
||||
git clone --depth=1 -b maint/v0.26 https://github.com/libgit2/libgit2.git
|
||||
cd libgit2/
|
||||
|
||||
mkdir build && cd build
|
||||
|
@ -2,11 +2,12 @@ language: python
|
||||
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.2"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
- "pypy"
|
||||
- "pypy3"
|
||||
# - "pypy3"
|
||||
|
||||
env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib
|
||||
|
||||
|
859
CHANGELOG.rst
Normal file
859
CHANGELOG.rst
Normal file
@ -0,0 +1,859 @@
|
||||
0.26.0 (2017-07-06)
|
||||
-------------------------
|
||||
|
||||
- Update to libgit2 v0.26
|
||||
`#713 <https://github.com/libgit2/pygit2/pull/713>`_
|
||||
|
||||
- Drop support for Python 3.2, add support for cffi 1.10
|
||||
`#706 <https://github.com/libgit2/pygit2/pull/706>`_
|
||||
`#694 <https://github.com/libgit2/pygit2/issues/694>`_
|
||||
|
||||
- New ``Repository.revert_commit(...)``
|
||||
`#711 <https://github.com/libgit2/pygit2/pull/711>`_
|
||||
`#710 <https://github.com/libgit2/pygit2/issues/710>`_
|
||||
|
||||
- New ``Branch.is_checked_out()``
|
||||
`#696 <https://github.com/libgit2/pygit2/pull/696>`_
|
||||
|
||||
- Various fixes
|
||||
`#706 <https://github.com/libgit2/pygit2/pull/706>`_
|
||||
`#707 <https://github.com/libgit2/pygit2/pull/707>`_
|
||||
`#708 <https://github.com/libgit2/pygit2/pull/708>`_
|
||||
|
||||
|
||||
0.25.1 (2017-04-25)
|
||||
-------------------------
|
||||
|
||||
- Add suport for Python 3.6
|
||||
|
||||
- New support for stash: repository methods ``stash``, ``stash_apply``,
|
||||
``stash_drop`` and ``stash_pop``
|
||||
`#695 <https://github.com/libgit2/pygit2/pull/695>`_
|
||||
|
||||
- Improved support for submodules: new repository methods ``init_submodules``
|
||||
and ``update_submodules``
|
||||
`#692 <https://github.com/libgit2/pygit2/pull/692>`_
|
||||
|
||||
- New friendlier API for branches & references: ``Repository.branches`` and
|
||||
``Repository.references``
|
||||
`#700 <https://github.com/libgit2/pygit2/pull/700>`_
|
||||
`#701 <https://github.com/libgit2/pygit2/pull/701>`_
|
||||
|
||||
- New support for custom backends
|
||||
`#690 <https://github.com/libgit2/pygit2/pull/690>`_
|
||||
|
||||
- Fix ``init_repository`` crash on None input
|
||||
`#688 <https://github.com/libgit2/pygit2/issues/688>`_
|
||||
`#697 <https://github.com/libgit2/pygit2/pull/697>`_
|
||||
|
||||
- Fix checkout with an orphan master branch
|
||||
`#669 <https://github.com/libgit2/pygit2/issues/669>`_
|
||||
`#685 <https://github.com/libgit2/pygit2/pull/685>`_
|
||||
|
||||
- Better error messages for opening repositories
|
||||
`#645 <https://github.com/libgit2/pygit2/issues/645>`_
|
||||
`#698 <https://github.com/libgit2/pygit2/pull/698>`_
|
||||
|
||||
|
||||
0.25.0 (2016-12-26)
|
||||
-------------------------
|
||||
|
||||
- Upgrade to libgit2 0.25
|
||||
`#670 <https://github.com/libgit2/pygit2/pull/670>`_
|
||||
|
||||
- Now Commit.tree raises an error if tree is not found
|
||||
`#682 <https://github.com/libgit2/pygit2/pull/682>`_
|
||||
|
||||
- New settings.mwindow_mapped_limit, cached_memory, enable_caching,
|
||||
cache_max_size and cache_object_limit
|
||||
`#677 <https://github.com/libgit2/pygit2/pull/677>`_
|
||||
|
||||
|
||||
0.24.2 (2016-11-01)
|
||||
-------------------------
|
||||
|
||||
- Unit tests pass on Windows, integration with AppVeyor
|
||||
`#641 <https://github.com/libgit2/pygit2/pull/641>`_
|
||||
`#655 <https://github.com/libgit2/pygit2/issues/655>`_
|
||||
`#657 <https://github.com/libgit2/pygit2/pull/657>`_
|
||||
`#659 <https://github.com/libgit2/pygit2/pull/659>`_
|
||||
`#660 <https://github.com/libgit2/pygit2/pull/660>`_
|
||||
`#661 <https://github.com/libgit2/pygit2/pull/661>`_
|
||||
`#667 <https://github.com/libgit2/pygit2/pull/667>`_
|
||||
|
||||
- Fix when libgit2 error messages have non-ascii chars
|
||||
`#651 <https://github.com/libgit2/pygit2/pull/651>`_
|
||||
|
||||
- Documentation improvements
|
||||
`#643 <https://github.com/libgit2/pygit2/pull/643>`_
|
||||
`#653 <https://github.com/libgit2/pygit2/pull/653>`_
|
||||
`#663 <https://github.com/libgit2/pygit2/pull/663>`_
|
||||
|
||||
|
||||
0.24.1 (2016-06-21)
|
||||
-------------------------
|
||||
|
||||
- New ``Repository.listall_reference_objects()``
|
||||
`#634 <https://github.com/libgit2/pygit2/pull/634>`_
|
||||
|
||||
- Fix ``Repository.write_archive(...)``
|
||||
`#619 <https://github.com/libgit2/pygit2/pull/619>`_
|
||||
`#621 <https://github.com/libgit2/pygit2/pull/621>`_
|
||||
|
||||
- Reproducible builds
|
||||
`#636 <https://github.com/libgit2/pygit2/pull/636>`_
|
||||
|
||||
- Documentation fixes
|
||||
`#606 <https://github.com/libgit2/pygit2/pull/606>`_
|
||||
`#607 <https://github.com/libgit2/pygit2/pull/607>`_
|
||||
`#609 <https://github.com/libgit2/pygit2/pull/609>`_
|
||||
`#623 <https://github.com/libgit2/pygit2/pull/623>`_
|
||||
|
||||
- Test updates
|
||||
`#629 <https://github.com/libgit2/pygit2/pull/629>`_
|
||||
|
||||
|
||||
0.24.0 (2016-03-05)
|
||||
-------------------------
|
||||
|
||||
- Update to libgit2 v0.24
|
||||
`#594 <https://github.com/libgit2/pygit2/pull/594>`_
|
||||
|
||||
- Support Python 3.5
|
||||
|
||||
- New dependency, `six <https://pypi.python.org/pypi/six/>`_
|
||||
|
||||
- New ``Repository.path_is_ignored(path)``
|
||||
`#589 <https://github.com/libgit2/pygit2/pull/589>`_
|
||||
|
||||
- Fix error in ``Repository(path)`` when path is a bytes string
|
||||
`#588 <https://github.com/libgit2/pygit2/issues/588>`_
|
||||
`#593 <https://github.com/libgit2/pygit2/pull/593>`_
|
||||
|
||||
- Fix memory issue in ``Repository.describe(...)``
|
||||
`#592 <https://github.com/libgit2/pygit2/issues/592>`_
|
||||
`#597 <https://github.com/libgit2/pygit2/issues/597>`_
|
||||
`#599 <https://github.com/libgit2/pygit2/pull/599>`_
|
||||
|
||||
- Allow testing with `tox <https://pypi.python.org/pypi/tox/>`_
|
||||
`#600 <https://github.com/libgit2/pygit2/pull/600>`_
|
||||
|
||||
|
||||
0.23.3 (2016-01-01)
|
||||
-------------------------
|
||||
|
||||
- New ``Repository.create_blob_fromiobase(...)``
|
||||
`#490 <https://github.com/libgit2/pygit2/pull/490>`_
|
||||
`#577 <https://github.com/libgit2/pygit2/pull/577>`_
|
||||
|
||||
- New ``Repository.describe(...)``
|
||||
`#585 <https://github.com/libgit2/pygit2/pull/585>`_
|
||||
|
||||
- Fix ``Signature`` default encoding, UTF-8 now
|
||||
`#581 <https://github.com/libgit2/pygit2/issues/581>`_
|
||||
|
||||
- Fixing ``pip install pygit2``, should install cffi first
|
||||
|
||||
- Unit tests, fix binary diff test
|
||||
`#586 <https://github.com/libgit2/pygit2/pull/586>`_
|
||||
|
||||
- Document that ``Diff.patch`` can be ``None``
|
||||
`#587 <https://github.com/libgit2/pygit2/pull/587>`_
|
||||
|
||||
|
||||
0.23.2 (2015-10-11)
|
||||
-------------------------
|
||||
|
||||
- Unify callbacks system for remotes and clone
|
||||
`#568 <https://github.com/libgit2/pygit2/pull/568>`_
|
||||
|
||||
- New ``TreeEntry._name``
|
||||
`#570 <https://github.com/libgit2/pygit2/pull/570>`_
|
||||
|
||||
- Fix segfault in ``Tag._message``
|
||||
`#572 <https://github.com/libgit2/pygit2/pull/572>`_
|
||||
|
||||
- Documentation improvements
|
||||
`#569 <https://github.com/libgit2/pygit2/pull/569>`_
|
||||
`#574 <https://github.com/libgit2/pygit2/pull/574>`_
|
||||
|
||||
API changes to clone::
|
||||
|
||||
# Before
|
||||
clone_repository(..., credentials, certificate)
|
||||
|
||||
# Now
|
||||
callbacks = RemoteCallbacks(credentials, certificate)
|
||||
clone_repository(..., callbacks)
|
||||
|
||||
API changes to remote::
|
||||
|
||||
# Before
|
||||
def transfer_progress(stats):
|
||||
...
|
||||
|
||||
remote.credentials = credentials
|
||||
remote.transfer_progress = transfer_progress
|
||||
remote.fetch()
|
||||
remote.push(specs)
|
||||
|
||||
# Now
|
||||
class MyCallbacks(RemoteCallbacks):
|
||||
def transfer_progress(self, stats):
|
||||
...
|
||||
|
||||
callbacks = MyCallbacks(credentials)
|
||||
remote.fetch(callbacks=callbacks)
|
||||
remote.push(specs, callbacks=callbacks)
|
||||
|
||||
|
||||
0.23.1 (2015-09-26)
|
||||
-------------------------
|
||||
|
||||
- Improve support for cffi 1.0+
|
||||
`#529 <https://github.com/libgit2/pygit2/pull/529>`_
|
||||
`#561 <https://github.com/libgit2/pygit2/pull/561>`_
|
||||
|
||||
- Fix ``Remote.push``
|
||||
`#557 <https://github.com/libgit2/pygit2/pull/557>`_
|
||||
|
||||
- New ``TreeEntry.type``
|
||||
`#560 <https://github.com/libgit2/pygit2/pull/560>`_
|
||||
|
||||
- New ``pygit2.GIT_DIFF_SHOW_BINARY``
|
||||
`#566 <https://github.com/libgit2/pygit2/pull/566>`_
|
||||
|
||||
|
||||
0.23.0 (2015-08-14)
|
||||
-------------------------
|
||||
|
||||
- Update to libgit2 v0.23
|
||||
`#540 <https://github.com/libgit2/pygit2/pull/540>`_
|
||||
|
||||
- Now ``Repository.merge_base(...)`` returns ``None`` if no merge base is found
|
||||
`#550 <https://github.com/libgit2/pygit2/pull/550>`_
|
||||
|
||||
- Documentation updates
|
||||
`#547 <https://github.com/libgit2/pygit2/pull/547>`_
|
||||
|
||||
API changes:
|
||||
|
||||
- How to set identity (aka signature) in a reflog has changed::
|
||||
|
||||
# Before
|
||||
signature = Signature('foo', 'bar')
|
||||
...
|
||||
reference.set_target(target, signature=signature, message=message)
|
||||
repo.set_head(target, signature=signature)
|
||||
remote.fetch(signature=signature)
|
||||
remote.push(signature=signature)
|
||||
|
||||
# Now
|
||||
repo.set_ident('foo', 'bar')
|
||||
...
|
||||
reference.set_target(target, message=message)
|
||||
repo.set_head(target)
|
||||
remote.push()
|
||||
|
||||
# The current identity can be get with
|
||||
repo.ident
|
||||
|
||||
- Some remote setters have been replaced by methods::
|
||||
|
||||
# Before # Now
|
||||
Remote.url = url Repository.remotes.set_url(name, url)
|
||||
Remote.push_url = url Repository.remotes.set_push_url(name, url)
|
||||
|
||||
Remote.add_fetch(refspec) Repository.remotes.add_fetch(name, refspec)
|
||||
Remote.add_push(refspec) Repository.remotes.add_push(name, refspec)
|
||||
|
||||
Remote.fetch_refspecs = [...] removed, use the config API instead
|
||||
Remote.push_refspecs = [...] removed, use the config API instead
|
||||
|
||||
|
||||
0.22.1 (2015-07-12)
|
||||
-------------------------
|
||||
|
||||
Diff interface refactoring
|
||||
`#346 <https://github.com/libgit2/pygit2/pull/346>`_
|
||||
(in progress):
|
||||
|
||||
- New ``iter(pygit2.Blame)``
|
||||
|
||||
- New ``pygit2.DiffDelta``, ``pygit2.DiffFile`` and ``pygit.DiffLine``
|
||||
|
||||
- API changes, translation table::
|
||||
|
||||
Hunk => DiffHunk
|
||||
Patch.old_file_path => Patch.delta.old_file.path
|
||||
Patch.new_file_path => Patch.delta.new_file.path
|
||||
Patch.old_id => Patch.delta.old_file.id
|
||||
Patch.new_id => Patch.delta.new_file.id
|
||||
Patch.status => Patch.delta.status
|
||||
Patch.similarity => Patch.delta.similarity
|
||||
Patch.is_binary => Patch.delta.is_binary
|
||||
Patch.additions => Patch.line_stats[1]
|
||||
Patch.deletions => Patch.line_stats[2]
|
||||
|
||||
- ``DiffHunk.lines`` is now a list of ``DiffLine`` objects, not tuples
|
||||
|
||||
New features:
|
||||
|
||||
- New ``Repository.expand_id(...)`` and ``Repository.ahead_behind(...)``
|
||||
`#448 <https://github.com/libgit2/pygit2/pull/448>`_
|
||||
|
||||
- New ``prefix`` parameter in ``Repository.write_archive``
|
||||
`#481 <https://github.com/libgit2/pygit2/pull/481>`_
|
||||
|
||||
- New ``Repository.merge_trees(...)``
|
||||
`#489 <https://github.com/libgit2/pygit2/pull/489>`_
|
||||
|
||||
- New ``Repository.cherrypick(...)``
|
||||
`#436 <https://github.com/libgit2/pygit2/issues/436>`_
|
||||
`#492 <https://github.com/libgit2/pygit2/pull/492>`_
|
||||
|
||||
- New support for submodules
|
||||
`#499 <https://github.com/libgit2/pygit2/pull/499>`_
|
||||
`#514 <https://github.com/libgit2/pygit2/pull/514>`_
|
||||
|
||||
- New ``Repository.merge_file_from_index(...)``
|
||||
`#503 <https://github.com/libgit2/pygit2/pull/503>`_
|
||||
|
||||
- Now ``Repository.diff`` supports diffing two blobs
|
||||
`#508 <https://github.com/libgit2/pygit2/pull/508>`_
|
||||
|
||||
- New optional ``fetch`` parameter in ``Remote.create``
|
||||
`#526 <https://github.com/libgit2/pygit2/pull/526>`_
|
||||
|
||||
- New ``pygit2.DiffStats``
|
||||
`#406 <https://github.com/libgit2/pygit2/issues/406>`_
|
||||
`#525 <https://github.com/libgit2/pygit2/pull/525>`_
|
||||
|
||||
- New ``Repository.get_attr(...)``
|
||||
`#528 <https://github.com/libgit2/pygit2/pull/528>`_
|
||||
|
||||
- New ``level`` optional parameter in ``Index.remove``
|
||||
`#533 <https://github.com/libgit2/pygit2/pull/533>`_
|
||||
|
||||
- New ``repr(TreeEntry)``
|
||||
`#543 <https://github.com/libgit2/pygit2/pull/543>`_
|
||||
|
||||
Build and install improvements:
|
||||
|
||||
- Make pygit work in a frozen environment
|
||||
`#453 <https://github.com/libgit2/pygit2/pull/453>`_
|
||||
|
||||
- Make pygit2 work with pyinstaller
|
||||
`#510 <https://github.com/libgit2/pygit2/pull/510>`_
|
||||
|
||||
Bugs fixed:
|
||||
|
||||
- Fix memory issues
|
||||
`#477 <https://github.com/libgit2/pygit2/issues/477>`_
|
||||
`#487 <https://github.com/libgit2/pygit2/pull/487>`_
|
||||
`#520 <https://github.com/libgit2/pygit2/pull/520>`_
|
||||
|
||||
- Fix TreeEntry equality testing
|
||||
`#458 <https://github.com/libgit2/pygit2/issues/458>`_
|
||||
`#488 <https://github.com/libgit2/pygit2/pull/488>`_
|
||||
|
||||
- ``Repository.write_archive`` fix handling of symlinks
|
||||
`#480 <https://github.com/libgit2/pygit2/pull/480>`_
|
||||
|
||||
- Fix type check in ``Diff[...]``
|
||||
`#495 <https://github.com/libgit2/pygit2/issues/495>`_
|
||||
|
||||
- Fix error when merging files with unicode content
|
||||
`#505 <https://github.com/libgit2/pygit2/pull/505>`_
|
||||
|
||||
Other:
|
||||
|
||||
- Documentation improvements and fixes
|
||||
`#448 <https://github.com/libgit2/pygit2/pull/448>`_
|
||||
`#491 <https://github.com/libgit2/pygit2/pull/491>`_
|
||||
`#497 <https://github.com/libgit2/pygit2/pull/497>`_
|
||||
`#507 <https://github.com/libgit2/pygit2/pull/507>`_
|
||||
`#517 <https://github.com/libgit2/pygit2/pull/517>`_
|
||||
`#518 <https://github.com/libgit2/pygit2/pull/518>`_
|
||||
`#519 <https://github.com/libgit2/pygit2/pull/519>`_
|
||||
`#521 <https://github.com/libgit2/pygit2/pull/521>`_
|
||||
`#523 <https://github.com/libgit2/pygit2/pull/523>`_
|
||||
`#527 <https://github.com/libgit2/pygit2/pull/527>`_
|
||||
`#536 <https://github.com/libgit2/pygit2/pull/536>`_
|
||||
|
||||
- Expose the ``pygit2.GIT_REPOSITORY_INIT_*`` constants
|
||||
`#483 <https://github.com/libgit2/pygit2/issues/483>`_
|
||||
|
||||
|
||||
0.22.0 (2015-01-16)
|
||||
-------------------
|
||||
|
||||
New:
|
||||
|
||||
- Update to libgit2 v0.22
|
||||
`#459 <https://github.com/libgit2/pygit2/pull/459>`_
|
||||
|
||||
- Add support for libgit2 feature detection
|
||||
(new ``pygit2.features`` and ``pygit2.GIT_FEATURE_*``)
|
||||
`#475 <https://github.com/libgit2/pygit2/pull/475>`_
|
||||
|
||||
- New ``Repository.remotes`` (``RemoteCollection``)
|
||||
`#447 <https://github.com/libgit2/pygit2/pull/447>`_
|
||||
|
||||
API Changes:
|
||||
|
||||
- Prototype of ``clone_repository`` changed, check documentation
|
||||
|
||||
- Removed ``clone_into``, use ``clone_repository`` with callbacks instead
|
||||
|
||||
- Use ``Repository.remotes.rename(name, new_name)`` instead of
|
||||
``Remote.rename(new_name)``
|
||||
|
||||
- Use ``Repository.remotes.delete(name)`` instead of ``Remote.delete()``
|
||||
|
||||
- Now ``Remote.push(...)`` takes a list of refspecs instead of just one
|
||||
|
||||
- Change ``Patch.old_id``, ``Patch.new_id``, ``Note.annotated_id``,
|
||||
``RefLogEntry.oid_old`` and ``RefLogEntry.oid_new`` to be ``Oid`` objects
|
||||
instead of strings
|
||||
`#449 <https://github.com/libgit2/pygit2/pull/449>`_
|
||||
|
||||
Other:
|
||||
|
||||
- Fix ``init_repository`` when passing optional parameters ``workdir_path``,
|
||||
``description``, ``template_path``, ``initial_head`` or ``origin_url``
|
||||
`#466 <https://github.com/libgit2/pygit2/issues/466>`_
|
||||
`#471 <https://github.com/libgit2/pygit2/pull/471>`_
|
||||
|
||||
- Fix use-after-free when patch outlives diff
|
||||
`#457 <https://github.com/libgit2/pygit2/issues/457>`_
|
||||
`#461 <https://github.com/libgit2/pygit2/pull/461>`_
|
||||
`#474 <https://github.com/libgit2/pygit2/pull/474>`_
|
||||
|
||||
- Documentation improvements
|
||||
`#456 <https://github.com/libgit2/pygit2/issues/456>`_
|
||||
`#462 <https://github.com/libgit2/pygit2/pull/462>`_
|
||||
`#465 <https://github.com/libgit2/pygit2/pull/465>`_
|
||||
`#472 <https://github.com/libgit2/pygit2/pull/472>`_
|
||||
`#473 <https://github.com/libgit2/pygit2/pull/473>`_
|
||||
|
||||
- Make the GPL exception explicit in setup.py
|
||||
`#450 <https://github.com/libgit2/pygit2/pull/450>`_
|
||||
|
||||
|
||||
0.21.4 (2014-11-04)
|
||||
-------------------
|
||||
|
||||
- Fix credentials callback not set when pushing
|
||||
`#431 <https://github.com/libgit2/pygit2/pull/431>`_
|
||||
`#435 <https://github.com/libgit2/pygit2/issues/435>`_
|
||||
`#437 <https://github.com/libgit2/pygit2/issues/437>`_
|
||||
`#438 <https://github.com/libgit2/pygit2/pull/438>`_
|
||||
|
||||
- Fix ``Repository.diff(...)`` when treeish is "empty"
|
||||
`#432 <https://github.com/libgit2/pygit2/issues/432>`_
|
||||
|
||||
- New ``Reference.peel(...)`` renders ``Reference.get_object()`` obsolete
|
||||
`#434 <https://github.com/libgit2/pygit2/pull/434>`_
|
||||
|
||||
- New, authenticate using ssh agent
|
||||
`#424 <https://github.com/libgit2/pygit2/pull/424>`_
|
||||
|
||||
- New ``Repository.merge_commits(...)``
|
||||
`#445 <https://github.com/libgit2/pygit2/pull/445>`_
|
||||
|
||||
- Make it easier to run when libgit2 not in a standard location
|
||||
`#441 <https://github.com/libgit2/pygit2/issues/441>`_
|
||||
|
||||
- Documentation: review install chapter
|
||||
|
||||
- Documentation: many corrections
|
||||
`#427 <https://github.com/libgit2/pygit2/pull/427>`_
|
||||
`#429 <https://github.com/libgit2/pygit2/pull/429>`_
|
||||
`#439 <https://github.com/libgit2/pygit2/pull/439>`_
|
||||
`#440 <https://github.com/libgit2/pygit2/pull/440>`_
|
||||
`#442 <https://github.com/libgit2/pygit2/pull/442>`_
|
||||
`#443 <https://github.com/libgit2/pygit2/pull/443>`_
|
||||
`#444 <https://github.com/libgit2/pygit2/pull/444>`_
|
||||
|
||||
|
||||
0.21.3 (2014-09-15)
|
||||
-------------------
|
||||
|
||||
Breaking changes:
|
||||
|
||||
- Now ``Repository.blame(...)`` returns ``Oid`` instead of string
|
||||
`#413 <https://github.com/libgit2/pygit2/pull/413>`_
|
||||
|
||||
- New ``Reference.set_target(...)`` replaces the ``Reference.target`` setter
|
||||
and ``Reference.log_append(...)``
|
||||
`#414 <https://github.com/libgit2/pygit2/pull/414>`_
|
||||
|
||||
- New ``Repository.set_head(...)`` replaces the ``Repository.head`` setter
|
||||
`#414 <https://github.com/libgit2/pygit2/pull/414>`_
|
||||
|
||||
- ``Repository.merge(...)`` now uses the ``SAFE_CREATE`` strategy by default
|
||||
`#417 <https://github.com/libgit2/pygit2/pull/417>`_
|
||||
|
||||
Other changes:
|
||||
|
||||
- New ``Remote.delete()``
|
||||
`#418 <https://github.com/libgit2/pygit2/issues/418>`_
|
||||
`#420 <https://github.com/libgit2/pygit2/pull/420>`_
|
||||
|
||||
- New ``Repository.write_archive(...)``
|
||||
`#421 <https://github.com/libgit2/pygit2/pull/421>`_
|
||||
|
||||
- Now ``Repository.checkout(...)`` accepts branch objects
|
||||
`#408 <https://github.com/libgit2/pygit2/pull/408>`_
|
||||
|
||||
- Fix refcount leak in remotes
|
||||
`#403 <https://github.com/libgit2/pygit2/issues/403>`_
|
||||
`#404 <https://github.com/libgit2/pygit2/pull/404>`_
|
||||
`#419 <https://github.com/libgit2/pygit2/pull/419>`_
|
||||
|
||||
- Various fixes to ``clone_repository(...)``
|
||||
`#399 <https://github.com/libgit2/pygit2/issues/399>`_
|
||||
`#411 <https://github.com/libgit2/pygit2/pull/411>`_
|
||||
`#425 <https://github.com/libgit2/pygit2/issues/425>`_
|
||||
`#426 <https://github.com/libgit2/pygit2/pull/426>`_
|
||||
|
||||
- Fix build error in Python 3
|
||||
`#401 <https://github.com/libgit2/pygit2/pull/401>`_
|
||||
|
||||
- Now ``pip install pygit2`` installs cffi first
|
||||
`#380 <https://github.com/libgit2/pygit2/issues/380>`_
|
||||
`#407 <https://github.com/libgit2/pygit2/pull/407>`_
|
||||
|
||||
- Add support for PyPy3
|
||||
`#422 <https://github.com/libgit2/pygit2/pull/422>`_
|
||||
|
||||
- Documentation improvements
|
||||
`#398 <https://github.com/libgit2/pygit2/pull/398>`_
|
||||
`#409 <https://github.com/libgit2/pygit2/pull/409>`_
|
||||
|
||||
|
||||
0.21.2 (2014-08-09)
|
||||
-------------------
|
||||
|
||||
- Fix regression with Python 2, ``IndexEntry.path`` returns str
|
||||
(bytes in Python 2 and unicode in Python 3)
|
||||
|
||||
- Get back ``IndexEntry.oid`` for backwards compatibility
|
||||
|
||||
- Config, iterate over the keys (instead of the key/value pairs)
|
||||
`#395 <https://github.com/libgit2/pygit2/pull/395>`_
|
||||
|
||||
- ``Diff.find_similar`` supports new threshold arguments
|
||||
`#396 <https://github.com/libgit2/pygit2/pull/396>`_
|
||||
|
||||
- Optimization, do not load the object when expanding an oid prefix
|
||||
`#397 <https://github.com/libgit2/pygit2/pull/397>`_
|
||||
|
||||
|
||||
0.21.1 (2014-07-22)
|
||||
-------------------
|
||||
|
||||
- Install fix
|
||||
`#382 <https://github.com/libgit2/pygit2/pull/382>`_
|
||||
|
||||
- Documentation improved, including
|
||||
`#383 <https://github.com/libgit2/pygit2/pull/383>`_
|
||||
`#385 <https://github.com/libgit2/pygit2/pull/385>`_
|
||||
`#388 <https://github.com/libgit2/pygit2/pull/388>`_
|
||||
|
||||
- Documentation, use the read-the-docs theme
|
||||
`#387 <https://github.com/libgit2/pygit2/pull/387>`_
|
||||
|
||||
- Coding style improvements
|
||||
`#392 <https://github.com/libgit2/pygit2/pull/392>`_
|
||||
|
||||
- New ``Repository.state_cleanup()``
|
||||
`#386 <https://github.com/libgit2/pygit2/pull/386>`_
|
||||
|
||||
- New ``Index.conflicts``
|
||||
`#345 <https://github.com/libgit2/pygit2/issues/345>`_
|
||||
`#389 <https://github.com/libgit2/pygit2/pull/389>`_
|
||||
|
||||
- New checkout option to define the target directory
|
||||
`#390 <https://github.com/libgit2/pygit2/pull/390>`_
|
||||
|
||||
|
||||
Backward incompatible changes:
|
||||
|
||||
- Now the checkout strategy must be a keyword argument.
|
||||
|
||||
Change ``Repository.checkout(refname, strategy)`` to
|
||||
``Repository.checkout(refname, strategy=strategy)``
|
||||
|
||||
Idem for ``checkout_head``, ``checkout_index`` and ``checkout_tree``
|
||||
|
||||
|
||||
0.21.0 (2014-06-27)
|
||||
-------------------
|
||||
|
||||
Highlights:
|
||||
|
||||
- Drop official support for Python 2.6, and add support for Python 3.4
|
||||
`#376 <https://github.com/libgit2/pygit2/pull/376>`_
|
||||
|
||||
- Upgrade to libgit2 v0.21.0
|
||||
`#374 <https://github.com/libgit2/pygit2/pull/374>`_
|
||||
|
||||
- Start using cffi
|
||||
`#360 <https://github.com/libgit2/pygit2/pull/360>`_
|
||||
`#361 <https://github.com/libgit2/pygit2/pull/361>`_
|
||||
|
||||
Backward incompatible changes:
|
||||
|
||||
- Replace ``oid`` by ``id`` through the API to follow libgit2 conventions.
|
||||
- Merge API overhaul following changes in libgit2.
|
||||
- New ``Remote.rename(...)`` replaces ``Remote.name = ...``
|
||||
- Now ``Remote.fetch()`` returns a ``TransferProgress`` object.
|
||||
- Now ``Config.get_multivar(...)`` returns an iterator instead of a list.
|
||||
|
||||
New features:
|
||||
|
||||
- New ``Config.snapshot()`` and ``Repository.config_snapshot()``
|
||||
|
||||
- New ``Config`` methods: ``get_bool(...)``, ``get_int(...)``,
|
||||
``parse_bool(...)`` and ``parse_int(...)``
|
||||
`#357 <https://github.com/libgit2/pygit2/pull/357>`_
|
||||
|
||||
- Blob: implement the memory buffer interface
|
||||
`#362 <https://github.com/libgit2/pygit2/pull/362>`_
|
||||
|
||||
- New ``clone_into(...)`` function
|
||||
`#368 <https://github.com/libgit2/pygit2/pull/368>`_
|
||||
|
||||
- Now ``Index`` can be used alone, without a repository
|
||||
`#372 <https://github.com/libgit2/pygit2/pull/372>`_
|
||||
|
||||
- Add more options to ``init_repository``
|
||||
`#347 <https://github.com/libgit2/pygit2/pull/347>`_
|
||||
|
||||
- Support ``Repository.workdir = ...`` and
|
||||
support setting detached heads ``Repository.head = <Oid>``
|
||||
`#377 <https://github.com/libgit2/pygit2/pull/377>`_
|
||||
|
||||
Other:
|
||||
|
||||
- Fix again build with VS2008
|
||||
`#364 <https://github.com/libgit2/pygit2/pull/364>`_
|
||||
|
||||
- Fix ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)`` arguments passing
|
||||
`#366 <https://github.com/libgit2/pygit2/pull/366>`_
|
||||
|
||||
- Fail gracefully when compiling against the wrong version of libgit2
|
||||
`#365 <https://github.com/libgit2/pygit2/pull/365>`_
|
||||
|
||||
- Several documentation improvements and updates
|
||||
`#359 <https://github.com/libgit2/pygit2/pull/359>`_
|
||||
`#375 <https://github.com/libgit2/pygit2/pull/375>`_
|
||||
`#378 <https://github.com/libgit2/pygit2/pull/378>`_
|
||||
|
||||
|
||||
|
||||
0.20.3 (2014-04-02)
|
||||
-------------------
|
||||
|
||||
- A number of memory issues fixed
|
||||
`#328 <https://github.com/libgit2/pygit2/pull/328>`_
|
||||
`#348 <https://github.com/libgit2/pygit2/pull/348>`_
|
||||
`#353 <https://github.com/libgit2/pygit2/pull/353>`_
|
||||
`#355 <https://github.com/libgit2/pygit2/pull/355>`_
|
||||
`#356 <https://github.com/libgit2/pygit2/pull/356>`_
|
||||
- Compatibility fixes for
|
||||
PyPy (`#338 <https://github.com/libgit2/pygit2/pull/338>`_),
|
||||
Visual Studio 2008 (`#343 <https://github.com/libgit2/pygit2/pull/343>`_)
|
||||
and Python 3.3 (`#351 <https://github.com/libgit2/pygit2/pull/351>`_)
|
||||
- Make the sort mode parameter in ``Repository.walk(...)`` optional
|
||||
`#337 <https://github.com/libgit2/pygit2/pull/337>`_
|
||||
- New ``Object.peel(...)``
|
||||
`#342 <https://github.com/libgit2/pygit2/pull/342>`_
|
||||
- New ``Index.add_all(...)``
|
||||
`#344 <https://github.com/libgit2/pygit2/pull/344>`_
|
||||
- Introduce support for libgit2 options
|
||||
`#350 <https://github.com/libgit2/pygit2/pull/350>`_
|
||||
- More informative repr for ``Repository`` objects
|
||||
`#352 <https://github.com/libgit2/pygit2/pull/352>`_
|
||||
- Introduce support for credentials
|
||||
`#354 <https://github.com/libgit2/pygit2/pull/354>`_
|
||||
- Several documentation fixes
|
||||
`#302 <https://github.com/libgit2/pygit2/issues/302>`_
|
||||
`#336 <https://github.com/libgit2/pygit2/issues/336>`_
|
||||
- Tests, remove temporary files
|
||||
`#341 <https://github.com/libgit2/pygit2/pull/341>`_
|
||||
|
||||
|
||||
0.20.2 (2014-02-04)
|
||||
-------------------
|
||||
|
||||
- Support PyPy
|
||||
`#209 <https://github.com/libgit2/pygit2/issues/209>`_
|
||||
`#327 <https://github.com/libgit2/pygit2/pull/327>`_
|
||||
`#333 <https://github.com/libgit2/pygit2/pull/333>`_
|
||||
|
||||
Repository:
|
||||
|
||||
- New ``Repository.default_signature``
|
||||
`#310 <https://github.com/libgit2/pygit2/pull/310>`_
|
||||
|
||||
Oid:
|
||||
|
||||
- New ``str(Oid)`` deprecates ``Oid.hex``
|
||||
`#322 <https://github.com/libgit2/pygit2/pull/322>`_
|
||||
|
||||
Object:
|
||||
|
||||
- New ``Object.id`` deprecates ``Object.oid``
|
||||
`#322 <https://github.com/libgit2/pygit2/pull/322>`_
|
||||
|
||||
- New ``TreeEntry.id`` deprecates ``TreeEntry.oid``
|
||||
`#322 <https://github.com/libgit2/pygit2/pull/322>`_
|
||||
|
||||
- New ``Blob.diff(...)`` and ``Blob.diff_to_buffer(...)``
|
||||
`#307 <https://github.com/libgit2/pygit2/pull/307>`_
|
||||
|
||||
- New ``Commit.tree_id`` and ``Commit.parent_ids``
|
||||
`#73 <https://github.com/libgit2/pygit2/issues/73>`_
|
||||
`#311 <https://github.com/libgit2/pygit2/pull/311>`_
|
||||
|
||||
- New rich comparison between tree entries
|
||||
`#305 <https://github.com/libgit2/pygit2/issues/305>`_
|
||||
`#313 <https://github.com/libgit2/pygit2/pull/313>`_
|
||||
|
||||
- Now ``Tree.__contains__(key)`` supports paths
|
||||
`#306 <https://github.com/libgit2/pygit2/issues/306>`_
|
||||
`#316 <https://github.com/libgit2/pygit2/pull/316>`_
|
||||
|
||||
Index:
|
||||
|
||||
- Now possible to create ``IndexEntry(...)``
|
||||
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
|
||||
|
||||
- Now ``IndexEntry.path``, ``IndexEntry.oid`` and ``IndexEntry.mode`` are
|
||||
writable
|
||||
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
|
||||
|
||||
- Now ``Index.add(...)`` accepts an ``IndexEntry`` too
|
||||
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
|
||||
|
||||
- Now ``Index.write_tree(...)`` is able to write to a different repository
|
||||
`#325 <https://github.com/libgit2/pygit2/pull/325>`_
|
||||
|
||||
- Fix memory leak in ``IndexEntry.path`` setter
|
||||
`#335 <https://github.com/libgit2/pygit2/pull/335>`_
|
||||
|
||||
Config:
|
||||
|
||||
- New ``Config`` iterator replaces ``Config.foreach``
|
||||
`#183 <https://github.com/libgit2/pygit2/issues/183>`_
|
||||
`#312 <https://github.com/libgit2/pygit2/pull/312>`_
|
||||
|
||||
Remote:
|
||||
|
||||
- New type ``Refspec``
|
||||
`#314 <https://github.com/libgit2/pygit2/pull/314>`_
|
||||
|
||||
- New ``Remote.push_url``
|
||||
`#315 <https://github.com/libgit2/pygit2/pull/314>`_
|
||||
|
||||
- New ``Remote.add_push`` and ``Remote.add_fetch``
|
||||
`#255 <https://github.com/libgit2/pygit2/issues/255>`_
|
||||
`#318 <https://github.com/libgit2/pygit2/pull/318>`_
|
||||
|
||||
- New ``Remote.fetch_refspecs`` replaces ``Remote.get_fetch_refspecs()`` and
|
||||
``Remote.set_fetch_refspecs(...)``
|
||||
`#319 <https://github.com/libgit2/pygit2/pull/319>`_
|
||||
|
||||
- New ``Remote.push_refspecs`` replaces ``Remote.get_push_refspecs()`` and
|
||||
``Remote.set_push_refspecs(...)``
|
||||
`#319 <https://github.com/libgit2/pygit2/pull/319>`_
|
||||
|
||||
- New ``Remote.progress``, ``Remote.transfer_progress`` and
|
||||
``Remote.update_tips``
|
||||
`#274 <https://github.com/libgit2/pygit2/issues/274>`_
|
||||
`#324 <https://github.com/libgit2/pygit2/pull/324>`_
|
||||
|
||||
- New type ``TransferProgress``
|
||||
`#274 <https://github.com/libgit2/pygit2/issues/274>`_
|
||||
`#324 <https://github.com/libgit2/pygit2/pull/324>`_
|
||||
|
||||
- Fix refcount leak in ``Repository.remotes``
|
||||
`#321 <https://github.com/libgit2/pygit2/issues/321>`_
|
||||
`#332 <https://github.com/libgit2/pygit2/pull/332>`_
|
||||
|
||||
Other: `#331 <https://github.com/libgit2/pygit2/pull/331>`_
|
||||
|
||||
|
||||
0.20.1 (2013-12-24)
|
||||
-------------------
|
||||
|
||||
- New remote ref-specs API:
|
||||
`#290 <https://github.com/libgit2/pygit2/pull/290>`_
|
||||
|
||||
- New ``Repository.reset(...)``:
|
||||
`#292 <https://github.com/libgit2/pygit2/pull/292>`_,
|
||||
`#294 <https://github.com/libgit2/pygit2/pull/294>`_
|
||||
|
||||
- Export ``GIT_DIFF_MINIMAL``:
|
||||
`#293 <https://github.com/libgit2/pygit2/pull/293>`_
|
||||
|
||||
- New ``Repository.merge(...)``:
|
||||
`#295 <https://github.com/libgit2/pygit2/pull/295>`_
|
||||
|
||||
- Fix ``Repository.blame`` argument handling:
|
||||
`#297 <https://github.com/libgit2/pygit2/pull/297>`_
|
||||
|
||||
- Fix build error on Windows:
|
||||
`#298 <https://github.com/libgit2/pygit2/pull/298>`_
|
||||
|
||||
- Fix typo in the README file, Blog → Blob:
|
||||
`#301 <https://github.com/libgit2/pygit2/pull/301>`_
|
||||
|
||||
- Now ``Diff.patch`` returns ``None`` if no patch:
|
||||
`#232 <https://github.com/libgit2/pygit2/pull/232>`_,
|
||||
`#303 <https://github.com/libgit2/pygit2/pull/303>`_
|
||||
|
||||
- New ``Walker.simplify_first_parent()``:
|
||||
`#304 <https://github.com/libgit2/pygit2/pull/304>`_
|
||||
|
||||
0.20.0 (2013-11-24)
|
||||
-------------------
|
||||
|
||||
- Upgrade to libgit2 v0.20.0:
|
||||
`#288 <https://github.com/libgit2/pygit2/pull/288>`_
|
||||
|
||||
- New ``Repository.head_is_unborn`` replaces ``Repository.head_is_orphaned``
|
||||
|
||||
- Changed ``pygit2.clone_repository(...)``. Drop ``push_url``, ``fetch_spec``
|
||||
and ``push_spec`` parameters. Add ``ignore_cert_errors``.
|
||||
|
||||
- New ``Patch.additions`` and ``Patch.deletions``:
|
||||
`#275 <https://github.com/libgit2/pygit2/pull/275>`_
|
||||
|
||||
- New ``Patch.is_binary``:
|
||||
`#276 <https://github.com/libgit2/pygit2/pull/276>`_
|
||||
|
||||
- New ``Reference.log_append(...)``:
|
||||
`#277 <https://github.com/libgit2/pygit2/pull/277>`_
|
||||
|
||||
- New ``Blob.is_binary``:
|
||||
`#278 <https://github.com/libgit2/pygit2/pull/278>`_
|
||||
|
||||
- New ``len(Diff)`` shows the number of patches:
|
||||
`#281 <https://github.com/libgit2/pygit2/pull/281>`_
|
||||
|
||||
- Rewrite ``Repository.status()``:
|
||||
`#283 <https://github.com/libgit2/pygit2/pull/283>`_
|
||||
|
||||
- New ``Reference.shorthand``:
|
||||
`#284 <https://github.com/libgit2/pygit2/pull/284>`_
|
||||
|
||||
- New ``Repository.blame(...)``:
|
||||
`#285 <https://github.com/libgit2/pygit2/pull/285>`_
|
||||
|
||||
- Now ``Repository.listall_references()`` and
|
||||
``Repository.listall_branches()`` return a list, not a tuple:
|
||||
`#289 <https://github.com/libgit2/pygit2/pull/289>`_
|
559
README.rst
559
README.rst
@ -2,19 +2,22 @@
|
||||
pygit2 - libgit2 bindings in Python
|
||||
######################################################################
|
||||
|
||||
.. image:: https://secure.travis-ci.org/libgit2/pygit2.svg
|
||||
.. image:: https://travis-ci.org/libgit2/pygit2.svg?branch=master
|
||||
:target: http://travis-ci.org/libgit2/pygit2
|
||||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/edmwc0dctk5nacx0/branch/master?svg=true
|
||||
:target: https://ci.appveyor.com/project/jdavid/pygit2/branch/master
|
||||
|
||||
Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2
|
||||
implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and
|
||||
pypy.
|
||||
implements Git plumbing. Pygit2 works with Python 2.7, 3.3, 3.4, 3.5, 3.6
|
||||
and PyPy 2.6
|
||||
|
||||
Links:
|
||||
|
||||
- http://github.com/libgit2/pygit2 -- Source code and issue tracker
|
||||
- https://github.com/libgit2/pygit2 -- Source code and issue tracker
|
||||
- http://www.pygit2.org/ -- Documentation
|
||||
- http://pypi.python.org/pypi/pygit2 -- Download
|
||||
|
||||
- https://pypi.python.org/pypi/pygit2 -- Download
|
||||
- https://github.com/libgit2/pygit2/blob/master/CHANGELOG.rst -- Changelog
|
||||
|
||||
How to install
|
||||
==============
|
||||
@ -22,514 +25,50 @@ How to install
|
||||
- Check http://www.pygit2.org/install.html
|
||||
|
||||
|
||||
Changelog
|
||||
==============
|
||||
|
||||
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>`_
|
||||
|
||||
|
||||
Authors
|
||||
==============
|
||||
|
||||
83 developers have contributed at least 1 commit to pygit2::
|
||||
117 developers have contributed at least 1 commit to pygit2::
|
||||
|
||||
J. David Ibáñez Sebastian Thiel András Veres-Szentkirályi
|
||||
Carlos Martín Nieto Fraser Tweedale Ash Berlin
|
||||
Nico von Geyso Han-Wen Nienhuys Benjamin Kircher
|
||||
W. Trevor King Leonardo Rhodes Benjamin Pollack
|
||||
Dave Borowitz Petr Viktorin Bryan O'Sullivan
|
||||
Daniel Rodríguez Troitiño Ron Cohen Daniel Bruce
|
||||
Richo Healey Thomas Kluyver David Fischer
|
||||
Christian Boos Alex Chamberlain David Sanders
|
||||
Julien Miotte Alexander Bayandin Devaev Maxim
|
||||
Xu Tao Amit Bakshi Eric Davis
|
||||
Jose Plana Andrey Devyatkin Erik Meusel
|
||||
Matthew Gamble Arno van Lumig Erik van Zijst
|
||||
Martin Lenders Ben Davis Ferengee
|
||||
Petr Hosek Eric Schrijver Gustavo Di Pietro
|
||||
Victor Garcia Hervé Cauwelier Hugh Cole-Baker
|
||||
Xavier Delannoy Huang Huang Jasper Lievisse Adriaanse
|
||||
Yonggang Luo Ian P. McCullough Josh Bleecher Snyder
|
||||
Valentin Haenel Jack O'Connor Kyriakos Oikonomakos
|
||||
Michael Jones Jared Flatow Mathieu Bridon
|
||||
Bernardo Heynemann Jiunn Haur Lim Óscar San José
|
||||
John Szakmeister Jun Omae Ridge Kennedy
|
||||
Matthew Duggan Sarath Lakshman Rui Abreu Ferreira
|
||||
Brodie Rao Vicent Marti Soasme
|
||||
Vlad Temian Zoran Zaric chengyuhang
|
||||
David Versmisse Adam Spiers earl
|
||||
Rémi Duraffort Andrew Chin
|
||||
J. David Ibáñez Carlos Martín Nieto Nico von Geyso
|
||||
W. Trevor King Dave Borowitz Matthias Bartelmeß
|
||||
Daniel Rodríguez Troitiño Richo Healey Christian Boos
|
||||
Julien Miotte Richard Möhn Xu Tao
|
||||
Jose Plana Matthew Duggan Matthew Gamble
|
||||
Martin Lenders Nick Hynes Petr Hosek
|
||||
Victor Garcia Xavier Delannoy Yonggang Luo
|
||||
Patrick Steinhardt Tamir Bahar Valentin Haenel
|
||||
Michael Jones Bernardo Heynemann Brodie Rao
|
||||
John Szakmeister Vlad Temian Lukas Fleischer
|
||||
Nicolas Dandrimont David Versmisse Rémi Duraffort
|
||||
Santiago Perez De Rosso Sebastian Thiel Thom Wiggers
|
||||
Alok Singhal Anatoly Techtonik Fraser Tweedale
|
||||
Han-Wen Nienhuys Jason Ziglar Leonardo Rhodes
|
||||
Petr Viktorin Robert Hölzl Ron Cohen
|
||||
Thomas Kluyver Alex Chamberlain Alexander Bayandin
|
||||
Amit Bakshi Andrey Devyatkin Arno van Lumig
|
||||
Ben Davis Dustin Raimondi Eric Schrijver
|
||||
Greg Fitzgerald Guillermo Pérez Hervé Cauwelier
|
||||
Huang Huang Ian P. McCullough Igor Gnatenko
|
||||
Jack O'Connor Jared Flatow Jiunn Haur Lim
|
||||
Jun Omae Kaarel Kitsemets Kevin KIN-FOO
|
||||
Mark Adams Masud Rahman Michael Sondergaard
|
||||
Ondřej Nový Sarath Lakshman Szucs Krisztian
|
||||
Vicent Marti Zoran Zaric Adam Spiers
|
||||
Andrew Chin András Veres-Szentkirályi Ash Berlin
|
||||
Benjamin Kircher Benjamin Pollack Bryan O'Sullivan
|
||||
Cam Cope Chason Chaffin Chris Rebert
|
||||
Colin Watson Daniel Bruce David Fischer
|
||||
David Sanders David Six Devaev Maxim
|
||||
Eric Davis Erik Meusel Erik van Zijst
|
||||
Ferengee Gustavo Di Pietrou Holger Frey
|
||||
Hugh Cole-Baker Jasper Lievisse Adriaanse Josh Bleecher Snyder
|
||||
Justin Clift Kyriakos Oikonomakos Mathieu Bridon
|
||||
Matthaus Woolard Nicolás Sanguinetti Noah Fontes
|
||||
Óscar San José Peter Dave Hello Philippe Ombredanne
|
||||
Ridge Kennedy Ross Nicoll Rui Abreu Ferreira
|
||||
Sheeo Soasme Vladimir Rutsky
|
||||
Yu Jianjian chengyuhang earl
|
||||
|
||||
|
||||
License
|
||||
|
53
appveyor.yml
Normal file
53
appveyor.yml
Normal file
@ -0,0 +1,53 @@
|
||||
version: 1.0.{build}
|
||||
image: Visual Studio 2015
|
||||
configuration: Release
|
||||
environment:
|
||||
matrix:
|
||||
- GENERATOR: 'Visual Studio 10'
|
||||
PYTHON: 'C:\Python27\python.exe'
|
||||
- GENERATOR: 'Visual Studio 10 Win64'
|
||||
PYTHON: 'C:\Python27-x64\python.exe'
|
||||
- GENERATOR: 'Visual Studio 10'
|
||||
PYTHON: 'C:\Python33\python.exe'
|
||||
- GENERATOR: 'Visual Studio 10 Win64'
|
||||
PYTHON: 'C:\Python33-x64\python.exe'
|
||||
- GENERATOR: 'Visual Studio 10'
|
||||
PYTHON: 'C:\Python34\python.exe'
|
||||
- GENERATOR: 'Visual Studio 10 Win64'
|
||||
PYTHON: 'C:\Python34-x64\python.exe'
|
||||
- GENERATOR: 'Visual Studio 14'
|
||||
PYTHON: 'C:\Python35\python.exe'
|
||||
- GENERATOR: 'Visual Studio 14 Win64'
|
||||
PYTHON: 'C:\Python35-x64\python.exe'
|
||||
- GENERATOR: 'Visual Studio 14'
|
||||
PYTHON: 'C:\Python36\python.exe'
|
||||
- GENERATOR: 'Visual Studio 14 Win64'
|
||||
PYTHON: 'C:\Python36-x64\python.exe'
|
||||
|
||||
init:
|
||||
- cmd: '%PYTHON% -m pip install -U nose wheel'
|
||||
build_script:
|
||||
- cmd: |
|
||||
set LIBGIT2=%APPVEYOR_BUILD_FOLDER%\build\libgit2
|
||||
git clone --depth=1 -b maint/v0.26 https://github.com/libgit2/libgit2.git libgit2
|
||||
mkdir build
|
||||
|
||||
cd build
|
||||
cmake -DSTDCALL=OFF -DBUILD_CLAR=OFF -DCMAKE_INSTALL_PREFIX="%LIBGIT2%" ../libgit2 -G "%GENERATOR%"
|
||||
cmake --build . --config Release --target install
|
||||
cd ..
|
||||
|
||||
IF "%GENERATOR%"=="Visual Studio 10 Win64" ( call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" )
|
||||
|
||||
"%PYTHON%" setup.py bdist_wheel
|
||||
test_script:
|
||||
- ps: |
|
||||
cp build\Release\git2.dll .
|
||||
&$env:PYTHON setup.py nosetests --with-xunit
|
||||
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
|
||||
# upload results to AppVeyor
|
||||
$wc = New-Object 'System.Net.WebClient'
|
||||
$wc.UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\nosetests.xml))
|
||||
|
||||
artifacts:
|
||||
- path: dist\*.whl
|
8
docs/backends.rst
Normal file
8
docs/backends.rst
Normal file
@ -0,0 +1,8 @@
|
||||
**********************************************************************
|
||||
Custom backends
|
||||
**********************************************************************
|
||||
|
||||
There is some support for custom backends, but undocumented. See
|
||||
`<https://github.com/libgit2/pygit2/pull/690/commits>`_
|
||||
|
||||
Documentation contributions are very welcome.
|
@ -43,16 +43,16 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'pygit2'
|
||||
copyright = u'2010-2014 The pygit2 contributors'
|
||||
copyright = u'2010-2015 The pygit2 contributors'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.22'
|
||||
version = '0.26'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.22.0'
|
||||
release = '0.26.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -2,9 +2,12 @@
|
||||
The development version
|
||||
**********************************************************************
|
||||
|
||||
.. image:: https://secure.travis-ci.org/libgit2/pygit2.png
|
||||
.. image:: https://travis-ci.org/libgit2/pygit2.svg?branch=master
|
||||
:target: http://travis-ci.org/libgit2/pygit2
|
||||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/edmwc0dctk5nacx0/branch/master?svg=true
|
||||
:target: https://ci.appveyor.com/project/jdavid/pygit2/branch/master
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ git clone git://github.com/libgit2/pygit2.git
|
||||
|
@ -23,6 +23,10 @@ Examples
|
||||
>>> diff = repo.diff('HEAD^', 'HEAD~3')
|
||||
>>> patches = [p for p in diff]
|
||||
|
||||
# Get the stats for a diff
|
||||
>>> diff = repo.diff('HEAD^', 'HEAD~3')
|
||||
>>> diff.stats
|
||||
|
||||
# Diffing the empty tree
|
||||
>>> tree = revparse_single('HEAD').tree
|
||||
>>> tree.diff_to_tree()
|
||||
@ -35,6 +39,10 @@ The Diff type
|
||||
====================
|
||||
|
||||
.. autoattribute:: pygit2.Diff.patch
|
||||
.. method:: Diff.__iter__()
|
||||
|
||||
Returns an iterator over the deltas/patches in this diff.
|
||||
|
||||
.. method:: Diff.__len__()
|
||||
|
||||
Returns the number of deltas/patches in this diff.
|
||||
@ -48,26 +56,60 @@ The Patch type
|
||||
|
||||
Attributes:
|
||||
|
||||
.. autoattribute:: pygit2.Patch.old_file_path
|
||||
.. autoattribute:: pygit2.Patch.new_file_path
|
||||
.. autoattribute:: pygit2.Patch.old_id
|
||||
.. autoattribute:: pygit2.Patch.new_id
|
||||
.. autoattribute:: pygit2.Patch.status
|
||||
.. autoattribute:: pygit2.Patch.similarity
|
||||
.. autoattribute:: pygit2.Patch.delta
|
||||
.. autoattribute:: pygit2.Patch.hunks
|
||||
.. autoattribute:: pygit2.Patch.additions
|
||||
.. autoattribute:: pygit2.Patch.deletions
|
||||
.. autoattribute:: pygit2.Patch.line_stats
|
||||
|
||||
|
||||
The DiffDelta type
|
||||
====================
|
||||
|
||||
Attributes:
|
||||
|
||||
.. autoattribute:: pygit2.DiffDelta.old_file
|
||||
.. autoattribute:: pygit2.DiffDelta.new_file
|
||||
.. autoattribute:: pygit2.DiffDelta.status
|
||||
.. autoattribute:: pygit2.DiffDelta.similarity
|
||||
|
||||
Getters:
|
||||
|
||||
.. autoattribute:: pygit2.Patch.is_binary
|
||||
.. autoattribute:: pygit2.DiffDelta.is_binary
|
||||
|
||||
|
||||
The Hunk type
|
||||
The DiffFile type
|
||||
====================
|
||||
|
||||
.. autoattribute:: pygit2.Hunk.old_start
|
||||
.. autoattribute:: pygit2.Hunk.old_lines
|
||||
.. autoattribute:: pygit2.Hunk.new_start
|
||||
.. autoattribute:: pygit2.Hunk.new_lines
|
||||
.. autoattribute:: pygit2.Hunk.lines
|
||||
Attributes:
|
||||
|
||||
.. autoattribute:: pygit2.DiffFile.path
|
||||
.. autoattribute:: pygit2.DiffFile.id
|
||||
.. autoattribute:: pygit2.DiffFile.size
|
||||
.. autoattribute:: pygit2.DiffFile.flags
|
||||
.. autoattribute:: pygit2.DiffFile.mode
|
||||
|
||||
|
||||
The DiffHunk type
|
||||
====================
|
||||
|
||||
.. autoattribute:: pygit2.DiffHunk.old_start
|
||||
.. autoattribute:: pygit2.DiffHunk.old_lines
|
||||
.. autoattribute:: pygit2.DiffHunk.new_start
|
||||
.. autoattribute:: pygit2.DiffHunk.new_lines
|
||||
.. autoattribute:: pygit2.DiffHunk.lines
|
||||
|
||||
The DiffStats type
|
||||
====================
|
||||
|
||||
.. autoattribute :: pygit2.DiffStats.insertions
|
||||
.. autoattribute :: pygit2.DiffStats.deletions
|
||||
.. autoattribute :: pygit2.DiffStats.files_changed
|
||||
.. automethod :: pygit2.DiffStats.format
|
||||
|
||||
The DiffLine type
|
||||
====================
|
||||
|
||||
.. autoattribute :: pygit2.DiffLine.origin
|
||||
.. autoattribute :: pygit2.DiffLine.content
|
||||
.. autoattribute :: pygit2.DiffLine.old_lineno
|
||||
.. autoattribute :: pygit2.DiffLine.new_lineno
|
||||
.. autoattribute :: pygit2.DiffLine.num_lines
|
||||
|
@ -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.22.0``::
|
||||
``0.26.0``::
|
||||
|
||||
>>> print LIBGIT2_VER_MAJOR
|
||||
0
|
||||
@ -26,15 +26,15 @@ 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.22.0``::
|
||||
``0.26.0``::
|
||||
|
||||
>>> print LIBGIT2_VER_MINOR
|
||||
22
|
||||
26
|
||||
|
||||
.. py:data:: LIBGIT2_VER_REVISION
|
||||
|
||||
Integer value of the revision version number. For example, for the version
|
||||
``0.22.0``::
|
||||
``0.26.0``::
|
||||
|
||||
>>> print LIBGIT2_VER_REVISION
|
||||
0
|
||||
@ -44,7 +44,7 @@ library that has been built against. The version number has a
|
||||
The libgit2 version number as a string::
|
||||
|
||||
>>> print LIBGIT2_VERSION
|
||||
'0.22.0'
|
||||
'0.26.0'
|
||||
|
||||
Errors
|
||||
======
|
||||
|
@ -7,17 +7,18 @@ Welcome to pygit2's documentation!
|
||||
==================================
|
||||
|
||||
Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2
|
||||
implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and
|
||||
pypy.
|
||||
implements the core of Git. Pygit2 works with Python 2.7, 3.3, 3.4, 3.5,
|
||||
3.6 and pypy.
|
||||
|
||||
It is likely to work with Python 2.6 and 3.1, but these versions are not
|
||||
officially supported.
|
||||
|
||||
Pygit2 links:
|
||||
Links:
|
||||
|
||||
- http://github.com/libgit2/pygit2 -- Source code and issue tracker
|
||||
- https://github.com/libgit2/pygit2 -- Source code and issue tracker
|
||||
- http://www.pygit2.org/ -- Documentation
|
||||
- http://pypi.python.org/pypi/pygit2 -- Download
|
||||
- https://pypi.python.org/pypi/pygit2 -- Download
|
||||
- https://github.com/libgit2/pygit2/blob/master/CHANGELOG.rst -- Changelog
|
||||
|
||||
Start:
|
||||
|
||||
@ -45,9 +46,11 @@ Usage guide:
|
||||
merge
|
||||
config
|
||||
remotes
|
||||
submodule
|
||||
blame
|
||||
settings
|
||||
features
|
||||
backends
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
@ -13,10 +13,19 @@ Installation
|
||||
Requirements
|
||||
============
|
||||
|
||||
- Python 2.7, 3.2+ or pypy (including the development headers)
|
||||
- Libgit2 v0.22.x
|
||||
- cffi 0.8.1+
|
||||
- Libssh2, optional, used for SSH network operations.
|
||||
- Python 2.7, 3.3+ or PyPy 2.6+ (including the development headers)
|
||||
- Libgit2 v0.26.x
|
||||
- cffi 1.0+
|
||||
- six
|
||||
- tox (optional)
|
||||
|
||||
Optional libgit2 dependecies to support ssh and https:
|
||||
|
||||
- https: WinHTTP (Windows), SecureTransport (OS X) or OpenSSL.
|
||||
- ssh: libssh2, pkg-config
|
||||
|
||||
It should work with older versions of cffi and PyPy, but using cffi 1.0+
|
||||
(and PyPy 2.6+) is strongly encouraged.
|
||||
|
||||
.. warning::
|
||||
|
||||
@ -34,11 +43,11 @@ while the last number |lq| *.micro* |rq| auto-increments independently.
|
||||
|
||||
As illustration see this table of compatible releases:
|
||||
|
||||
+-----------+--------+----------------------------------------+-------------------------------+
|
||||
|**libgit2**| 0.22.0 | 0.21.1, 0.21.2 |0.20.0 |
|
||||
+-----------+--------+----------------------------------------+-------------------------------+
|
||||
|**pygit2** | 0.22.0 | 0.21.0, 0.21.1, 0.21.2, 0.21.3, 0.21.4 | 0.20.0, 0.20.1, 0.20.2, 0.20.3|
|
||||
+-----------+--------+----------------------------------------+-------------------------------+
|
||||
+-----------+--------+----------------+------------------------+
|
||||
|**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::
|
||||
|
||||
@ -55,9 +64,9 @@ directory, do:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ wget https://github.com/libgit2/libgit2/archive/v0.22.0.tar.gz
|
||||
$ tar xzf v0.22.0.tar.gz
|
||||
$ cd libgit2-0.22.0/
|
||||
$ wget https://github.com/libgit2/libgit2/archive/v0.26.0.tar.gz
|
||||
$ tar xzf v0.26.0.tar.gz
|
||||
$ cd libgit2-0.26.0/
|
||||
$ cmake .
|
||||
$ make
|
||||
$ sudo make install
|
||||
@ -76,7 +85,7 @@ Now install pygit2, and then verify it is correctly installed:
|
||||
$ python -c 'import pygit2'
|
||||
|
||||
|
||||
Troobleshooting
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
The verification step may fail if the dynamic linker does not find the libgit2
|
||||
@ -139,9 +148,9 @@ Install libgit2 (see we define the installation prefix):
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ wget https://github.com/libgit2/libgit2/archive/v0.22.0.tar.gz
|
||||
$ tar xzf v0.22.0.tar.gz
|
||||
$ cd libgit2-0.22.0/
|
||||
$ wget https://github.com/libgit2/libgit2/archive/v0.26.0.tar.gz
|
||||
$ tar xzf v0.26.0.tar.gz
|
||||
$ cd libgit2-0.26.0/
|
||||
$ cmake . -DCMAKE_INSTALL_PREFIX=$LIBGIT2
|
||||
$ make
|
||||
$ make install
|
||||
@ -177,37 +186,54 @@ everytime. Verify yourself if curious:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ readelf --dynamic lib/python2.7/site-packages/pygit2-0.21.3-py2.7-linux-x86_64.egg/_pygit2.so | grep PATH
|
||||
$ readelf --dynamic lib/python2.7/site-packages/pygit2-0.26.0-py2.7-linux-x86_64.egg/_pygit2.so | grep PATH
|
||||
0x000000000000001d (RUNPATH) Library runpath: [/tmp/venv/lib]
|
||||
|
||||
|
||||
Installing on Windows
|
||||
===================================
|
||||
|
||||
pygit2 expects to find the libgit2 installed files in the directory specified
|
||||
in the ``LIBGIT2`` environment variable.
|
||||
`pygit2` for Windows is packaged into wheels and can be easily
|
||||
installed with `pip`:
|
||||
|
||||
In addition, make sure that libgit2 is build in "__cdecl" mode.
|
||||
The following recipe shows you how to do it, assuming you're working
|
||||
from a bash shell:
|
||||
pip install pygit2
|
||||
|
||||
For development it is also possible to build `pygit2` with `libgit2`
|
||||
from sources. `libgit2` location is specified by the ``LIBGIT2``
|
||||
environment variable. `libgit2` should be built in "__cdecl" mode.
|
||||
The following recipe shows you how to do it from a bash shell:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ export LIBGIT2=C:/Dev/libgit2
|
||||
$ wget https://github.com/libgit2/libgit2/archive/v0.22.0.tar.gz
|
||||
$ tar xzf v0.22.0.tar.gz
|
||||
$ cd libgit2-0.22.0/
|
||||
$ git clone --depth=1 -b maint/v0.26 https://github.com/libgit2/libgit2.git
|
||||
$ cd libgit2
|
||||
$ cmake . -DSTDCALL=OFF -DCMAKE_INSTALL_PREFIX=$LIBGIT2 -G "Visual Studio 9 2008"
|
||||
$ cmake --build . --config release --target install
|
||||
$ ctest -v
|
||||
|
||||
At this point, you're ready to execute the generic pygit2 installation
|
||||
steps described above.
|
||||
At this point, you're ready to execute the generic `pygit2`
|
||||
installation steps described at the start of this page.
|
||||
|
||||
|
||||
Installing on OS X
|
||||
===================================
|
||||
|
||||
.. note::
|
||||
|
||||
Help wanted to write this section, please make a pull request
|
||||
https://github.com/libgit2/pygit2/pulls
|
||||
You will need the `XCode <https://developer.apple.com/xcode/>`_ Developer
|
||||
Tools from Apple. This free download from the Mac App Store will provide the
|
||||
clang compiler needed for the installation of pygit2.
|
||||
|
||||
This section was tested on OS X 10.9 Mavericks and OS X 10.10 Yosemite with
|
||||
Python 3.3 in a virtual environment.
|
||||
|
||||
The easiest way is to first install libgit2 with the `Homebrew <http://brew.sh>`_
|
||||
package manager and then use pip3 for pygit2. The following example assumes that
|
||||
XCode and Hombrew are already installed.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ brew update
|
||||
$ brew install libgit2
|
||||
$ pip3 install pygit2
|
||||
|
@ -1,5 +1,5 @@
|
||||
**********************************************************************
|
||||
Merge
|
||||
Merge & Cherrypick
|
||||
**********************************************************************
|
||||
|
||||
.. contents::
|
||||
@ -33,3 +33,24 @@ can create a commit with these two parents.
|
||||
>>> tree = repo.index.write_tree()
|
||||
>>> new_commit = repo.create_commit('HEAD', user, user, tree,
|
||||
[repo.head.target, other_branch_tip])
|
||||
|
||||
|
||||
Cherrypick
|
||||
===================
|
||||
|
||||
.. automethod:: pygit2.Repository.cherrypick
|
||||
|
||||
Note that after a successful cherrypick you have to run
|
||||
:py:meth:`.Repository.state_cleanup` in order to get the repository out
|
||||
of cherrypicking mode.
|
||||
|
||||
|
||||
Lower-level methods
|
||||
===================
|
||||
|
||||
These methods allow more direct control over how to perform the
|
||||
merging. They do not modify the working directory and return an
|
||||
in-memory Index representing the result of the merge.
|
||||
|
||||
.. automethod:: pygit2.Repository.merge_commits
|
||||
.. automethod:: pygit2.Repository.merge_trees
|
||||
|
@ -132,6 +132,7 @@ them to the Git object database:
|
||||
|
||||
.. automethod:: pygit2.Repository.create_blob_fromworkdir
|
||||
.. automethod:: pygit2.Repository.create_blob_fromdisk
|
||||
.. automethod:: pygit2.Repository.create_blob_fromiobase
|
||||
|
||||
There are also some functions to calculate the id for a byte string without
|
||||
creating the blob object:
|
||||
@ -176,6 +177,7 @@ Tree entries
|
||||
.. autoattribute:: pygit2.TreeEntry.id
|
||||
.. autoattribute:: pygit2.TreeEntry.hex
|
||||
.. autoattribute:: pygit2.TreeEntry.filemode
|
||||
.. autoattribute:: pygit2.TreeEntry.type
|
||||
|
||||
.. method:: TreeEntry.__cmp__(TreeEntry)
|
||||
|
||||
@ -188,14 +190,14 @@ Example::
|
||||
6
|
||||
|
||||
>>> for entry in tree: # Iteration
|
||||
... print(entry.id, entry.name)
|
||||
... print(entry.id, entry.type, entry.name)
|
||||
...
|
||||
7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore
|
||||
c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING
|
||||
32b30b90b062f66957d6790c3c155c289c34424e README.md
|
||||
c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c
|
||||
85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py
|
||||
3d8985bbec338eb4d47c5b01b863ee89d044bd53 test
|
||||
7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa blob .gitignore
|
||||
c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 blob COPYING
|
||||
32b30b90b062f66957d6790c3c155c289c34424e blob README.md
|
||||
c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 blob pygit2.c
|
||||
85a67270a49ef16cdd3d328f06a3e4b459f09b27 blob setup.py
|
||||
3d8985bbec338eb4d47c5b01b863ee89d044bd53 tree test
|
||||
|
||||
>>> entry = tree['pygit2.c'] # Get an entry by name
|
||||
>>> entry
|
||||
|
@ -17,9 +17,11 @@ Main porcelain commands
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
git-cherry-pick (Apply the changes introduced by some existing commits.) <recipes/git-cherry-pick>
|
||||
git-init (Create an empty git repository or reinitialize an existing one.) <recipes/git-init>
|
||||
git-log (Show commit logs.) <recipes/git-log>
|
||||
git-show (Show various types of objects.) <recipes/git-show>
|
||||
git-tag (Create, list, delete or verify a tag object signed with GPG.) <recipes/git-tag>
|
||||
git clone --mirror (Clone with a mirroring configuration) <recipes/git-clone-mirror>
|
||||
|
||||
.. _git man page: https://www.kernel.org/pub/software/scm/git/docs/git.html
|
||||
|
73
docs/recipes/git-cherry-pick.rst
Normal file
73
docs/recipes/git-cherry-pick.rst
Normal file
@ -0,0 +1,73 @@
|
||||
**********************************************************************
|
||||
git-cherry-pick
|
||||
**********************************************************************
|
||||
|
||||
The convenient way to cherry-pick a commit is to use
|
||||
:py:meth:`.Repository.cherrypick()`. It is limited to cherry-picking with a
|
||||
working copy and on-disk index.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$> cd /path/to/repo
|
||||
$> git checkout basket
|
||||
$> git cherry-pick 9e044d03c
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
repo = pygit2.Repository('/path/to/repo')
|
||||
repo.checkout('basket')
|
||||
|
||||
cherry_id = pygit2.Oid('9e044d03c')
|
||||
repo.cherrypick(cherry_id)
|
||||
|
||||
if repo.index.conflicts is None:
|
||||
tree_id = repo.index.write_tree()
|
||||
|
||||
cherry = repo.get(cherry_id)
|
||||
committer = pygit2.Signature('Archimedes', 'archy@jpl-classics.org')
|
||||
|
||||
repo.create_commit(basket.name, cherry.author, committer,
|
||||
cherry.message, tree_id, [basket.target])
|
||||
del basket # outdated, prevent from accidentally using it
|
||||
|
||||
repo.state_cleanup()
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Cherry-picking a commit without a working copy
|
||||
----------------------------------------------------------------------
|
||||
|
||||
This way of cherry-picking gives you more control over the process and works
|
||||
on bare repositories as well as repositories with a working copy.
|
||||
:py:meth:`~.Repository.merge_trees()` can also be used for other tasks, for
|
||||
example `three-argument rebases`_.
|
||||
|
||||
.. _`three-argument rebases`: https://www.kernel.org/pub/software/scm/git/docs/git-rebase.html
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
repo = pygit2.Repository('/path/to/repo')
|
||||
|
||||
cherry = repo.revparse_single('9e044d03c')
|
||||
basket = repo.branches.get('basket')
|
||||
|
||||
base = repo.merge_base(cherry.oid, basket.target)
|
||||
base_tree = cherry.parents[0].tree
|
||||
|
||||
index = repo.merge_trees(base_tree, basket, cherry)
|
||||
tree_id = index.write_tree(repo)
|
||||
|
||||
author = cherry.author
|
||||
committer = pygit2.Signature('Archimedes', 'archy@jpl-classics.org')
|
||||
|
||||
repo.create_commit(basket.name, author, committer, cherry.message,
|
||||
tree_id, [basket.target])
|
||||
del None # outdated, prevent from accidentally using it
|
||||
|
||||
----------------------------------------------------------------------
|
||||
References
|
||||
----------------------------------------------------------------------
|
||||
|
||||
- git-cherry-pick_.
|
||||
|
||||
.. _git-cherry-pick: https://www.kernel.org/pub/software/scm/git/docs/git-cherry-pick.html
|
26
docs/recipes/git-clone-mirror.rst
Normal file
26
docs/recipes/git-clone-mirror.rst
Normal file
@ -0,0 +1,26 @@
|
||||
**********************************************************************
|
||||
git-clone --mirror
|
||||
**********************************************************************
|
||||
|
||||
git provides an argument to set up the repository as a mirror, which
|
||||
involves setting the refspec to one which copies all refs and a mirror
|
||||
option for push in the remote.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$> git clone --mirror https://github.com/libgit2/pygit2
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def init_remote(repo, name, url):
|
||||
# Create the remote with a mirroring url
|
||||
remote = repo.remotes.create(name, url, "+refs/*:refs/*")
|
||||
# And set the configuration option to true for the push command
|
||||
mirror_var = "remote.{}.mirror".format(name)
|
||||
repo.config[mirror_var] = True
|
||||
# Return the remote, which pygit2 will use to perform the clone
|
||||
return remote
|
||||
|
||||
print("Cloning pygit2 as mirror")
|
||||
pygit2.clone_repository("https://github.com/libgit2/pygit2", "pygit2.git", bare=True,
|
||||
remote=init_remote)
|
@ -31,7 +31,7 @@ Show SHA hash
|
||||
Show diff
|
||||
======================================================================
|
||||
|
||||
>>> diff = commit.tree.diff()
|
||||
>>> diff = repo.diff(commit.parents[0], commit)
|
||||
|
||||
======================================================================
|
||||
Show all files in commit
|
||||
@ -40,6 +40,52 @@ Show all files in commit
|
||||
>>> for e in commit.tree:
|
||||
>>> print(e.name)
|
||||
|
||||
======================================================================
|
||||
Produce something like a ``git show`` message
|
||||
======================================================================
|
||||
|
||||
In order to display time zone information you have to create a subclass
|
||||
of tzinfo. In Python 3.2+ you can do this fairly directly. In older
|
||||
versions you have to make your own class as described in the `Python
|
||||
datetime documentation`_::
|
||||
|
||||
from datetime import tzinfo, timedelta
|
||||
class FixedOffset(tzinfo):
|
||||
"""Fixed offset in minutes east from UTC."""
|
||||
|
||||
def __init__(self, offset):
|
||||
self.__offset = timedelta(minutes = offset)
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self.__offset
|
||||
|
||||
def tzname(self, dt):
|
||||
return None # we don't know the time zone's name
|
||||
|
||||
def dst(self, dt):
|
||||
return timedelta(0) # we don't know about DST
|
||||
|
||||
.. _Python datetime documentation: https://docs.python.org/2/library/datetime.html#tzinfo-objects
|
||||
|
||||
Then you can make your message:
|
||||
|
||||
>>> # Until Python 2.7.9:
|
||||
>>> from __future__ import unicode_literals
|
||||
>>> from datetime import datetime
|
||||
>>> tzinfo = FixedOffset(commit.author.offset)
|
||||
|
||||
>>> # From Python 3.2:
|
||||
>>> from datetime import datetime, timezone, timedelta
|
||||
>>> tzinfo = timezone( timedelta(minutes=commit.author.offset) )
|
||||
>>>
|
||||
>>> dt = datetime.fromtimestamp(float(commit.author.time), tzinfo)
|
||||
>>> timestr = dt.strftime('%c %z')
|
||||
>>> msg = '\n'.join(['commit {}'.format(commit.tree_id.hex),
|
||||
... 'Author: {} <{}>'.format(commit.author.name, commit.author.email),
|
||||
... 'Date: {}'.format(timestr),
|
||||
... '',
|
||||
... commit.message])
|
||||
|
||||
----------------------------------------------------------------------
|
||||
References
|
||||
----------------------------------------------------------------------
|
||||
|
@ -4,15 +4,25 @@ References
|
||||
|
||||
.. contents::
|
||||
|
||||
.. automethod:: pygit2.Repository.listall_references
|
||||
.. automethod:: pygit2.Repository.lookup_reference
|
||||
.. autoclass:: pygit2.repository.References
|
||||
:members:
|
||||
:undoc-members:
|
||||
:special-members: __getitem__, __iter__, __contains__
|
||||
|
||||
|
||||
Example::
|
||||
|
||||
>>> all_refs = repo.listall_references()
|
||||
>>> all_refs = list(repo.references)
|
||||
|
||||
>>> master_ref = repo.lookup_reference("refs/heads/master")
|
||||
>>> commit = master_ref.get_object() # or repo[master_ref.target]
|
||||
|
||||
# Create a reference
|
||||
>>> ref = repo.references.create('refs/tags/version1', LAST_COMMIT)
|
||||
|
||||
# Delete a reference
|
||||
>>> repo.references.delete('refs/tags/version1')
|
||||
|
||||
|
||||
The Reference type
|
||||
====================
|
||||
@ -68,28 +78,33 @@ Branches
|
||||
Branches inherit from References, and additionally provide specialized
|
||||
accessors for some unique features.
|
||||
|
||||
.. automethod:: pygit2.Repository.listall_branches
|
||||
.. automethod:: pygit2.Repository.lookup_branch
|
||||
.. automethod:: pygit2.Repository.create_branch
|
||||
.. autoclass:: pygit2.repository.Branches
|
||||
:members:
|
||||
:undoc-members:
|
||||
:special-members: __getitem__, __iter__, __contains__
|
||||
|
||||
Example::
|
||||
|
||||
>>> local_branches = repo.listall_branches()
|
||||
>>> # equivalent to
|
||||
>>> local_branches = repo.listall_branches(pygit2.GIT_BRANCH_LOCAL)
|
||||
>>> # Listing all branches
|
||||
>>> branches_list = list(repo.branches)
|
||||
>>> # Local only
|
||||
>>> local_branches = list(repo.branches.local)
|
||||
>>> # Remote only
|
||||
>>> remote_branches = list(repo.branches.remote)
|
||||
|
||||
>>> remote_branches = repo.listall_branches(pygit2.GIT_BRANCH_REMOTE)
|
||||
>>> # Get a branch
|
||||
>>> branch = repo.branches['master']
|
||||
>>> other_branch = repo.branches['does-not-exist'] # Will raise a KeyError
|
||||
>>> other_branch = repo.branches.get('does-not-exist') # Returns None
|
||||
|
||||
>>> all_branches = repo.listall_branches(pygit2.GIT_BRANCH_REMOTE |
|
||||
pygit2.GIT_BRANCH_LOCAL)
|
||||
>>> remote_branch = repo.branches.remote['upstream/feature']
|
||||
|
||||
>>> master_branch = repo.lookup_branch('master')
|
||||
>>> # equivalent to
|
||||
>>> master_branch = repo.lookup_branch('master',
|
||||
pygit2.GIT_BRANCH_LOCAL)
|
||||
>>> # Create a local branch
|
||||
>>> new_branch = repo.branches.local.create('new-branch')
|
||||
|
||||
>>> And delete it
|
||||
>>> repo.branches.delete('new-branch')
|
||||
|
||||
>>> remote_branch = repo.lookup_branch('upstream/feature',
|
||||
pygit2.GIT_BRANCH_REMOTE)
|
||||
|
||||
The Branch type
|
||||
====================
|
||||
@ -98,17 +113,19 @@ The Branch type
|
||||
.. autoattribute:: pygit2.Branch.remote_name
|
||||
.. autoattribute:: pygit2.Branch.upstream
|
||||
.. autoattribute:: pygit2.Branch.upstream_name
|
||||
|
||||
.. automethod:: pygit2.Branch.rename
|
||||
.. automethod:: pygit2.Branch.delete
|
||||
.. automethod:: pygit2.Branch.is_head
|
||||
.. automethod:: pygit2.Branch.is_checked_out
|
||||
|
||||
The reference log
|
||||
====================
|
||||
|
||||
Example::
|
||||
|
||||
>>> head = repo.lookup_reference('refs/heads/master')
|
||||
>>> head = repo.references.get('refs/heads/master') # Returns None if not found
|
||||
>>> # Almost equivalent to
|
||||
>>> head = repo.references['refs/heads/master'] # Raises KeyError if not found
|
||||
>>> for entry in head.log():
|
||||
... print(entry.message)
|
||||
|
||||
|
@ -21,6 +21,12 @@ The Remote type
|
||||
.. autoclass:: pygit2.Remote
|
||||
:members:
|
||||
|
||||
The RemoteCallbacks type
|
||||
========================
|
||||
|
||||
.. autoclass:: pygit2.RemoteCallbacks
|
||||
:members:
|
||||
|
||||
The TransferProgress type
|
||||
===========================
|
||||
|
||||
@ -42,8 +48,6 @@ Refspecs objects are not constructed directly, but returned by
|
||||
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,6 +67,11 @@ Below there are some general attributes and methods:
|
||||
.. autoattribute:: pygit2.Repository.default_signature
|
||||
.. automethod:: pygit2.Repository.read
|
||||
.. automethod:: pygit2.Repository.write
|
||||
.. automethod:: pygit2.Repository.ahead_behind
|
||||
.. automethod:: pygit2.Repository.create_reference
|
||||
.. automethod:: pygit2.Repository.describe
|
||||
.. automethod:: pygit2.Repository.path_is_ignored
|
||||
.. automethod:: pygit2.Repository.reset
|
||||
.. automethod:: pygit2.Repository.revert_commit
|
||||
.. automethod:: pygit2.Repository.state_cleanup
|
||||
.. automethod:: pygit2.Repository.write_archive
|
||||
|
21
docs/submodule.rst
Normal file
21
docs/submodule.rst
Normal file
@ -0,0 +1,21 @@
|
||||
**********************************************************************
|
||||
Submodules
|
||||
**********************************************************************
|
||||
|
||||
A submodule is a foreign repository that is embedded within a
|
||||
dedicated subdirectory of the repositories tree.
|
||||
|
||||
.. automethod:: pygit2.Repository.init_submodules
|
||||
.. automethod:: pygit2.Repository.update_submodules
|
||||
.. automethod:: pygit2.Repository.lookup_submodule
|
||||
.. automethod:: pygit2.Repository.listall_submodules
|
||||
|
||||
The Submodule type
|
||||
====================
|
||||
|
||||
.. automethod:: pygit2.Submodule.open
|
||||
|
||||
.. autoattribute:: pygit2.Submodule.name
|
||||
.. autoattribute:: pygit2.Submodule.path
|
||||
.. autoattribute:: pygit2.Submodule.url
|
||||
.. autoattribute:: pygit2.Submodule.branch
|
@ -71,3 +71,11 @@ Lower level API:
|
||||
.. automethod:: pygit2.Repository.checkout_head
|
||||
.. automethod:: pygit2.Repository.checkout_tree
|
||||
.. automethod:: pygit2.Repository.checkout_index
|
||||
|
||||
Stash
|
||||
====================
|
||||
|
||||
.. automethod:: pygit2.Repository.stash
|
||||
.. automethod:: pygit2.Repository.stash_apply
|
||||
.. automethod:: pygit2.Repository.stash_drop
|
||||
.. automethod:: pygit2.Repository.stash_pop
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -35,18 +35,45 @@ from _pygit2 import *
|
||||
from .blame import Blame, BlameHunk
|
||||
from .config import Config
|
||||
from .credentials import *
|
||||
from .errors import check_error
|
||||
from .errors import check_error, Passthrough
|
||||
from .ffi import ffi, C
|
||||
from .index import Index, IndexEntry
|
||||
from .remote import Remote, get_credentials
|
||||
from .remote import Remote, RemoteCallbacks, get_credentials
|
||||
from .repository import Repository
|
||||
from .settings import Settings
|
||||
from .submodule import Submodule
|
||||
from .utils import to_bytes, to_str
|
||||
from ._utils import __version__
|
||||
from ._build import __version__
|
||||
|
||||
|
||||
# Features
|
||||
features = C.git_libgit2_features()
|
||||
GIT_FEATURE_THREADS = C.GIT_FEATURE_THREADS
|
||||
GIT_FEATURE_HTTPS = C.GIT_FEATURE_HTTPS
|
||||
GIT_FEATURE_SSH = C.GIT_FEATURE_SSH
|
||||
|
||||
# GIT_REPOSITORY_INIT_*
|
||||
GIT_REPOSITORY_INIT_OPTIONS_VERSION = C.GIT_REPOSITORY_INIT_OPTIONS_VERSION
|
||||
GIT_REPOSITORY_INIT_BARE = C.GIT_REPOSITORY_INIT_BARE
|
||||
GIT_REPOSITORY_INIT_NO_REINIT = C.GIT_REPOSITORY_INIT_NO_REINIT
|
||||
GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = C.GIT_REPOSITORY_INIT_NO_DOTGIT_DIR
|
||||
GIT_REPOSITORY_INIT_MKDIR = C.GIT_REPOSITORY_INIT_MKDIR
|
||||
GIT_REPOSITORY_INIT_MKPATH = C.GIT_REPOSITORY_INIT_MKPATH
|
||||
GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = C.GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE
|
||||
GIT_REPOSITORY_INIT_RELATIVE_GITLINK = C.GIT_REPOSITORY_INIT_RELATIVE_GITLINK
|
||||
GIT_REPOSITORY_INIT_SHARED_UMASK = C.GIT_REPOSITORY_INIT_SHARED_UMASK
|
||||
GIT_REPOSITORY_INIT_SHARED_GROUP = C.GIT_REPOSITORY_INIT_SHARED_GROUP
|
||||
GIT_REPOSITORY_INIT_SHARED_ALL = C.GIT_REPOSITORY_INIT_SHARED_ALL
|
||||
|
||||
# GIT_ATTR_CHECK_*
|
||||
GIT_ATTR_CHECK_FILE_THEN_INDEX = C.GIT_ATTR_CHECK_FILE_THEN_INDEX
|
||||
GIT_ATTR_CHECK_INDEX_THEN_FILE = C.GIT_ATTR_CHECK_INDEX_THEN_FILE
|
||||
GIT_ATTR_CHECK_INDEX_ONLY = C.GIT_ATTR_CHECK_INDEX_ONLY
|
||||
GIT_ATTR_CHECK_NO_SYSTEM = C.GIT_ATTR_CHECK_NO_SYSTEM
|
||||
|
||||
|
||||
def init_repository(path, bare=False,
|
||||
flags=C.GIT_REPOSITORY_INIT_MKPATH,
|
||||
flags=GIT_REPOSITORY_INIT_MKPATH,
|
||||
mode=0,
|
||||
workdir_path=None,
|
||||
description=None,
|
||||
@ -78,12 +105,16 @@ def init_repository(path, bare=False,
|
||||
See libgit2's documentation on git_repository_init_ext for further details.
|
||||
"""
|
||||
# Pre-process input parameters
|
||||
if path is None:
|
||||
raise TypeError('Expected string type for path, found None.')
|
||||
|
||||
if bare:
|
||||
flags |= C.GIT_REPOSITORY_INIT_BARE
|
||||
flags |= GIT_REPOSITORY_INIT_BARE
|
||||
|
||||
# Options
|
||||
options = ffi.new('git_repository_init_options *')
|
||||
C.git_repository_init_init_options(options, C.GIT_REPOSITORY_INIT_OPTIONS_VERSION)
|
||||
C.git_repository_init_init_options(options,
|
||||
GIT_REPOSITORY_INIT_OPTIONS_VERSION)
|
||||
options.flags = flags
|
||||
options.mode = mode
|
||||
|
||||
@ -115,22 +146,6 @@ def init_repository(path, bare=False,
|
||||
# Ok
|
||||
return Repository(to_str(path))
|
||||
|
||||
|
||||
@ffi.callback('int (*credentials)(git_cred **cred, const char *url,'
|
||||
'const char *username_from_url, unsigned int allowed_types,'
|
||||
'void *data)')
|
||||
def _credentials_cb(cred_out, url, username_from_url, allowed, data):
|
||||
d = ffi.from_handle(data)
|
||||
|
||||
try:
|
||||
ccred = get_credentials(d['credentials_cb'], 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_repository_create_cb)(git_repository **out,'
|
||||
'const char *path, int bare, void *payload)')
|
||||
def _repository_create_cb(repo_out, path, bare, data):
|
||||
@ -161,24 +176,9 @@ def _remote_create_cb(remote_out, repo, name, url, data):
|
||||
|
||||
return 0
|
||||
|
||||
@ffi.callback('int (*git_transport_certificate_check_cb)'
|
||||
'(git_cert *cert, int valid, const char *host, void *payload)')
|
||||
def _certificate_cb(cert_i, valid, host, data):
|
||||
d = ffi.from_handle(data)
|
||||
try:
|
||||
# python's parting is deep in the libraries and assumes an OpenSSL-owned cert
|
||||
val = d['certificate_cb'](None, bool(valid), ffi.string(host))
|
||||
if not val:
|
||||
return C.GIT_ECERTIFICATE
|
||||
except Exception as e:
|
||||
d['exception'] = e
|
||||
return C.GIT_EUSER
|
||||
|
||||
return 0
|
||||
|
||||
def clone_repository(
|
||||
url, path, bare=False, repository=None, remote=None,
|
||||
checkout_branch=None, credentials=None, certificate=None):
|
||||
checkout_branch=None, callbacks=None):
|
||||
"""Clones a new Git repository from *url* in the given *path*.
|
||||
|
||||
Returns a Repository class pointing to the newly cloned repository.
|
||||
@ -196,11 +196,8 @@ def clone_repository(
|
||||
:param str checkout_branch: Branch to checkout after the
|
||||
clone. The default is to use the remote's default branch.
|
||||
|
||||
:param callable credentials: authentication to use if the remote
|
||||
requires it
|
||||
|
||||
:param callable certificate: callback to verify the host's
|
||||
certificate or fingerprint.
|
||||
:param RemoteCallbacks callbacks: object which implements the
|
||||
callbacks as methods.
|
||||
|
||||
:rtype: Repository
|
||||
|
||||
@ -212,8 +209,8 @@ def clone_repository(
|
||||
signature. The Remote it returns will be used instead of the default
|
||||
one.
|
||||
|
||||
The certificate callback has `(cert, valid, hostname) -> bool` as
|
||||
a signature. Return True to accept the connection, False to abort.
|
||||
The callbacks should be an object which inherits from
|
||||
`pyclass:RemoteCallbacks`.
|
||||
|
||||
"""
|
||||
|
||||
@ -224,10 +221,8 @@ def clone_repository(
|
||||
|
||||
# Data, let's use a dict as we don't really want much more
|
||||
d = {}
|
||||
d['credentials_cb'] = credentials
|
||||
d['repository_cb'] = repository
|
||||
d['remote_cb'] = remote
|
||||
d['certificate_cb'] = certificate
|
||||
d_handle = ffi.new_handle(d)
|
||||
|
||||
# Perform the initialization with the version we compiled
|
||||
@ -249,13 +244,11 @@ def clone_repository(
|
||||
|
||||
|
||||
opts.bare = bare
|
||||
if credentials:
|
||||
opts.remote_callbacks.credentials = _credentials_cb
|
||||
opts.remote_callbacks.payload = d_handle
|
||||
|
||||
if certificate:
|
||||
opts.remote_callbacks.certificate_check = _certificate_cb
|
||||
opts.remote_callbacks.payload = d_handle
|
||||
if callbacks is None:
|
||||
callbacks = RemoteCallbacks()
|
||||
|
||||
callbacks._fill_fetch_options(opts.fetch_opts)
|
||||
|
||||
err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts)
|
||||
|
||||
@ -267,8 +260,3 @@ def clone_repository(
|
||||
return Repository._from_c(crepo[0], owned=True)
|
||||
|
||||
settings = Settings()
|
||||
|
||||
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
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -26,24 +26,18 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
|
||||
"""
|
||||
This is an special module, it provides stuff used by setup.py and by
|
||||
pygit2 at run-time.
|
||||
This is an special module, it provides stuff used by setup.py at build time.
|
||||
But also used by pygit2 at run time.
|
||||
"""
|
||||
|
||||
# Import from the Standard Library
|
||||
from binascii import crc32
|
||||
import inspect
|
||||
import codecs
|
||||
import os
|
||||
from os import getenv
|
||||
from os.path import abspath, dirname
|
||||
import sys
|
||||
|
||||
|
||||
#
|
||||
# The version number of pygit2
|
||||
#
|
||||
__version__ = '0.22.0'
|
||||
__version__ = '0.26.0'
|
||||
|
||||
|
||||
#
|
||||
@ -68,37 +62,3 @@ def get_libgit2_paths():
|
||||
os.path.join(libgit2_path, 'include'),
|
||||
getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')),
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Loads the cffi extension
|
||||
#
|
||||
def get_ffi():
|
||||
import cffi
|
||||
|
||||
ffi = cffi.FFI()
|
||||
|
||||
# Load C definitions
|
||||
dir_path = dirname(abspath(inspect.getfile(inspect.currentframe())))
|
||||
decl_path = os.path.join(dir_path, 'decl.h')
|
||||
with codecs.open(decl_path, 'r', 'utf-8') as header:
|
||||
ffi.cdef(header.read())
|
||||
|
||||
# The modulename
|
||||
# Simplified version of what cffi does: remove kwargs and vengine
|
||||
preamble = "#include <git2.h>"
|
||||
key = [sys.version[:3], cffi.__version__, preamble] + ffi._cdefsources
|
||||
key = '\x00'.join(key)
|
||||
if sys.version_info >= (3,):
|
||||
key = key.encode('utf-8')
|
||||
k1 = hex(crc32(key[0::2]) & 0xffffffff).lstrip('0x').rstrip('L')
|
||||
k2 = hex(crc32(key[1::2]) & 0xffffffff).lstrip('0').rstrip('L')
|
||||
modulename = 'pygit2_cffi_%s%s' % (k1, k2)
|
||||
|
||||
# Load extension module
|
||||
libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths()
|
||||
C = ffi.verify(preamble, modulename=modulename, libraries=["git2"],
|
||||
include_dirs=[libgit2_include], library_dirs=[libgit2_lib])
|
||||
|
||||
# Ok
|
||||
return ffi, C
|
76
pygit2/_run.py
Normal file
76
pygit2/_run.py
Normal file
@ -0,0 +1,76 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# In addition to the permissions in the GNU General Public License,
|
||||
# the authors give you unlimited permission to link the compiled
|
||||
# version of this file into combinations with other programs,
|
||||
# and to distribute those combinations without any restriction
|
||||
# coming from the use of this file. (The General Public License
|
||||
# restrictions do apply in other respects; for example, they cover
|
||||
# modification of the file, and distribution when not linked into
|
||||
# a combined executable.)
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
|
||||
"""
|
||||
This is an special module, it provides stuff used by by pygit2 at run-time.
|
||||
"""
|
||||
|
||||
# Import from the Standard Library
|
||||
import codecs
|
||||
import os
|
||||
from os.path import abspath, dirname
|
||||
import sys
|
||||
|
||||
# Import from cffi
|
||||
from cffi import FFI
|
||||
|
||||
# Import from pygit2
|
||||
from _build import get_libgit2_paths
|
||||
|
||||
|
||||
# C_HEADER_SRC
|
||||
if getattr(sys, 'frozen', False):
|
||||
dir_path = getattr(sys, '_MEIPASS', None)
|
||||
if dir_path is None:
|
||||
dir_path = dirname(abspath(sys.executable))
|
||||
else:
|
||||
dir_path = dirname(abspath(__file__))
|
||||
|
||||
decl_path = os.path.join(dir_path, 'decl.h')
|
||||
with codecs.open(decl_path, 'r', 'utf-8') as header:
|
||||
C_HEADER_SRC = header.read()
|
||||
|
||||
# C_KEYWORDS
|
||||
libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths()
|
||||
C_KEYWORDS = dict(libraries=['git2'],
|
||||
library_dirs=[libgit2_lib],
|
||||
include_dirs=[libgit2_include])
|
||||
|
||||
# preamble
|
||||
preamble = "#include <git2.h>"
|
||||
|
||||
# ffi
|
||||
ffi = FFI()
|
||||
set_source = getattr(ffi, 'set_source', None)
|
||||
if set_source is not None:
|
||||
set_source("pygit2._libgit2", preamble, **C_KEYWORDS)
|
||||
|
||||
ffi.cdef(C_HEADER_SRC)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ffi.compile()
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -32,8 +32,10 @@ from __future__ import absolute_import, unicode_literals
|
||||
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
|
||||
@ -123,9 +125,7 @@ class Blame(object):
|
||||
return BlameHunk._from_c(self, chunk)
|
||||
|
||||
def for_line(self, line_no):
|
||||
"""for_line(line_no) -> BlameHunk
|
||||
|
||||
Returns the blame hunk data for a given line given its number
|
||||
"""Returns the <BlameHunk> object for a given line given its number
|
||||
in the current Blame.
|
||||
|
||||
Arguments:
|
||||
@ -142,21 +142,5 @@ class Blame(object):
|
||||
|
||||
return BlameHunk._from_c(self, chunk)
|
||||
|
||||
|
||||
class BlameIterator(object):
|
||||
def __init__(self, blame):
|
||||
self._count = len(blame)
|
||||
self._index = 0
|
||||
self._blame = blame
|
||||
|
||||
def __next__(self):
|
||||
if self._index >= self._count:
|
||||
raise StopIteration
|
||||
|
||||
hunk = self._blame[self._blame]
|
||||
self._index += 1
|
||||
|
||||
return hunk
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
def __iter__(self):
|
||||
return GenericIterator(self)
|
||||
|
110
pygit2/config.py
110
pygit2/config.py
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -63,15 +63,12 @@ class ConfigIterator(object):
|
||||
|
||||
def __next__(self):
|
||||
entry = self._next_entry()
|
||||
name = ffi.string(entry.name).decode('utf-8')
|
||||
|
||||
return name
|
||||
return ffi.string(entry.name).decode('utf-8')
|
||||
|
||||
|
||||
class ConfigMultivarIterator(ConfigIterator):
|
||||
def __next__(self):
|
||||
entry = self._next_entry()
|
||||
|
||||
return ffi.string(entry.value).decode('utf-8')
|
||||
|
||||
|
||||
@ -104,19 +101,19 @@ class Config(object):
|
||||
def _get(self, key):
|
||||
assert_string(key, "key")
|
||||
|
||||
cstr = ffi.new('char **')
|
||||
err = C.git_config_get_string(cstr, self._config, to_bytes(key))
|
||||
entry = ffi.new('git_config_entry **')
|
||||
err = C.git_config_get_entry(entry, self._config, to_bytes(key))
|
||||
|
||||
return err, cstr
|
||||
return err, ConfigEntry._from_c(entry[0])
|
||||
|
||||
def _get_string(self, key):
|
||||
err, cstr = self._get(key)
|
||||
def _get_entry(self, key):
|
||||
err, entry = self._get(key)
|
||||
|
||||
if err == C.GIT_ENOTFOUND:
|
||||
raise KeyError(key)
|
||||
|
||||
check_error(err)
|
||||
return cstr[0]
|
||||
return entry
|
||||
|
||||
def __contains__(self, key):
|
||||
err, cstr = self._get(key)
|
||||
@ -129,9 +126,9 @@ class Config(object):
|
||||
return True
|
||||
|
||||
def __getitem__(self, key):
|
||||
val = self._get_string(key)
|
||||
entry = self._get_entry(key)
|
||||
|
||||
return ffi.string(val).decode('utf-8')
|
||||
return ffi.string(entry.value).decode('utf-8')
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
assert_string(key, "key")
|
||||
@ -161,12 +158,11 @@ class Config(object):
|
||||
return ConfigIterator(self, citer[0])
|
||||
|
||||
def get_multivar(self, name, regex=None):
|
||||
"""get_multivar(name[, regex]) -> [str, ...]
|
||||
|
||||
Get each value of a multivar ''name'' as a list. The optional ''regex''
|
||||
parameter is expected to be a regular expression to filter the
|
||||
variables we're interested in."""
|
||||
"""Get each value of a multivar ''name'' as a list of strings.
|
||||
|
||||
The optional ''regex'' parameter is expected to be a regular expression
|
||||
to filter the variables we're interested in.
|
||||
"""
|
||||
assert_string(name, "name")
|
||||
|
||||
citer = ffi.new('git_config_iterator **')
|
||||
@ -178,11 +174,9 @@ class Config(object):
|
||||
return ConfigMultivarIterator(self, citer[0])
|
||||
|
||||
def set_multivar(self, name, regex, value):
|
||||
"""set_multivar(name, regex, value)
|
||||
|
||||
Set a multivar ''name'' to ''value''. ''regexp'' is a regular
|
||||
expression to indicate which values to replace"""
|
||||
|
||||
"""Set a multivar ''name'' to ''value''. ''regexp'' is a regular
|
||||
expression to indicate which values to replace.
|
||||
"""
|
||||
assert_string(name, "name")
|
||||
assert_string(regex, "regex")
|
||||
assert_string(value, "value")
|
||||
@ -192,53 +186,48 @@ class Config(object):
|
||||
check_error(err)
|
||||
|
||||
def get_bool(self, key):
|
||||
"""get_bool(key) -> Bool
|
||||
|
||||
Look up *key* and parse its value as a boolean as per the git-config
|
||||
rules
|
||||
"""Look up *key* and parse its value as a boolean as per the git-config
|
||||
rules. Return a boolean value (True or False).
|
||||
|
||||
Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',
|
||||
0, 'off' and 'no'"""
|
||||
0, 'off' and 'no'
|
||||
"""
|
||||
|
||||
val = self._get_string(key)
|
||||
entry = self._get_entry(key)
|
||||
res = ffi.new('int *')
|
||||
err = C.git_config_parse_bool(res, val)
|
||||
err = C.git_config_parse_bool(res, entry.value)
|
||||
check_error(err)
|
||||
|
||||
return res[0] != 0
|
||||
|
||||
def get_int(self, key):
|
||||
"""get_int(key) -> int
|
||||
|
||||
Look up *key* and parse its value as an integer as per the git-config
|
||||
rules.
|
||||
"""Look up *key* and parse its value as an integer as per the git-config
|
||||
rules. Return an integer.
|
||||
|
||||
A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo',
|
||||
'mega' and 'giga' respectively"""
|
||||
'mega' and 'giga' respectively.
|
||||
"""
|
||||
|
||||
val = self._get_string(key)
|
||||
entry = self._get_entry(key)
|
||||
res = ffi.new('int64_t *')
|
||||
err = C.git_config_parse_int64(res, val)
|
||||
err = C.git_config_parse_int64(res, entry.value)
|
||||
check_error(err)
|
||||
|
||||
return res[0]
|
||||
|
||||
def add_file(self, path, level=0, force=0):
|
||||
"""add_file(path, level=0, force=0)
|
||||
|
||||
Add a config file instance to an existing config."""
|
||||
"""Add a config file instance to an existing config."""
|
||||
|
||||
err = C.git_config_add_file_ondisk(self._config, to_bytes(path), level,
|
||||
force)
|
||||
check_error(err)
|
||||
|
||||
def snapshot(self):
|
||||
"""Create a snapshot from this Config object
|
||||
"""Create a snapshot from this Config object.
|
||||
|
||||
This means that looking up multiple values will use the same version
|
||||
of the configuration files
|
||||
of the configuration files.
|
||||
"""
|
||||
|
||||
ccfg = ffi.new('git_config **')
|
||||
err = C.git_config_snapshot(ccfg, self._config)
|
||||
check_error(err)
|
||||
@ -281,24 +270,35 @@ class Config(object):
|
||||
|
||||
@staticmethod
|
||||
def get_system_config():
|
||||
"""get_system_config() -> Config
|
||||
|
||||
Return an object representing the system configuration file."""
|
||||
|
||||
"""Return a <Config> object representing the system configuration file.
|
||||
"""
|
||||
return Config._from_found_config(C.git_config_find_system)
|
||||
|
||||
@staticmethod
|
||||
def get_global_config():
|
||||
"""get_global_config() -> Config
|
||||
|
||||
Return an object representing the global configuration file."""
|
||||
|
||||
"""Return a <Config> object representing the global configuration file.
|
||||
"""
|
||||
return Config._from_found_config(C.git_config_find_global)
|
||||
|
||||
@staticmethod
|
||||
def get_xdg_config():
|
||||
"""get_xdg_config() -> Config
|
||||
|
||||
Return an object representing the global configuration file."""
|
||||
|
||||
"""Return a <Config> object representing the global configuration file.
|
||||
"""
|
||||
return Config._from_found_config(C.git_config_find_xdg)
|
||||
|
||||
class ConfigEntry(object):
|
||||
"""An entry in a configuation object
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _from_c(cls, ptr):
|
||||
entry = cls.__new__(cls)
|
||||
entry._entry = ptr
|
||||
return entry
|
||||
|
||||
def __del__(self):
|
||||
C.git_config_entry_free(self._entry)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._entry.value
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -36,7 +36,6 @@ class UserPass(object):
|
||||
|
||||
This is an object suitable for passing to a remote's credentials
|
||||
callback and for returning from said callback.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, username, password):
|
||||
@ -67,7 +66,6 @@ class Keypair(object):
|
||||
: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):
|
||||
|
381
pygit2/decl.h
381
pygit2/decl.h
@ -1,5 +1,7 @@
|
||||
typedef ... git_repository;
|
||||
typedef ... git_submodule;
|
||||
typedef ... git_remote;
|
||||
typedef ... git_transport;
|
||||
typedef ... git_refspec;
|
||||
typedef ... git_cred;
|
||||
typedef ... git_object;
|
||||
@ -50,8 +52,18 @@ typedef enum {
|
||||
GIT_EUNMERGED = -10,
|
||||
GIT_ENONFASTFORWARD = -11,
|
||||
GIT_EINVALIDSPEC = -12,
|
||||
GIT_EMERGECONFLICT = -13,
|
||||
GIT_ECONFLICT = -13,
|
||||
GIT_ELOCKED = -14,
|
||||
GIT_EMODIFIED = -15,
|
||||
GIT_EAUTH = -16,
|
||||
GIT_ECERTIFICATE = -17,
|
||||
GIT_EAPPLIED = -18,
|
||||
GIT_EPEEL = -19,
|
||||
GIT_EEOF = -20,
|
||||
GIT_EINVALID = -21,
|
||||
GIT_EUNCOMMITTED = -22,
|
||||
GIT_EDIRECTORY = -23,
|
||||
GIT_EMERGECONFLICT = -24,
|
||||
|
||||
GIT_PASSTHROUGH = -30,
|
||||
GIT_ITEROVER = -31,
|
||||
@ -117,6 +129,7 @@ typedef enum {
|
||||
} git_credtype_t;
|
||||
|
||||
typedef enum git_cert_t {
|
||||
GIT_CERT_NONE,
|
||||
GIT_CERT_X509,
|
||||
GIT_CERT_HOSTKEY_LIBSSH2,
|
||||
} git_cert_t;
|
||||
@ -128,21 +141,21 @@ typedef enum {
|
||||
|
||||
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_t cert_type;
|
||||
git_cert parent;
|
||||
void *data;
|
||||
size_t len;
|
||||
} git_cert_x509;
|
||||
|
||||
typedef struct {
|
||||
git_cert_t cert_type;
|
||||
} git_cert;
|
||||
|
||||
typedef int (*git_transport_message_cb)(const char *str, int len, void *data);
|
||||
typedef int (*git_cred_acquire_cb)(
|
||||
git_cred **cred,
|
||||
@ -164,6 +177,16 @@ typedef int (*git_push_transfer_progress)(
|
||||
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;
|
||||
@ -172,19 +195,73 @@ struct git_remote_callbacks {
|
||||
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_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_create(
|
||||
@ -192,6 +269,7 @@ int git_remote_create(
|
||||
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);
|
||||
|
||||
@ -199,24 +277,20 @@ 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);
|
||||
const char * git_remote_url(const git_remote *remote);
|
||||
int git_remote_set_url(git_remote *remote, const char* url);
|
||||
int git_remote_set_url(git_repository *repo, const char *remote, const char* url);
|
||||
const char * git_remote_pushurl(const git_remote *remote);
|
||||
int git_remote_set_pushurl(git_remote *remote, const char* url);
|
||||
int git_remote_fetch(git_remote *remote, const git_strarray *refspecs, const git_signature *signature, const char *reflog_message);
|
||||
int git_remote_push(git_remote *remote, git_strarray *refspecs, const git_push_options *opts, const git_signature *signature, const char *reflog_message);
|
||||
int git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url);
|
||||
int git_remote_fetch(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts, const char *reflog_message);
|
||||
int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts);
|
||||
const git_transfer_progress * git_remote_stats(git_remote *remote);
|
||||
int git_remote_add_push(git_remote *remote, const char *refspec);
|
||||
int git_remote_add_fetch(git_remote *remote, const char *refspec);
|
||||
int git_remote_save(const git_remote *remote);
|
||||
int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
|
||||
int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec);
|
||||
int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec);
|
||||
int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version);
|
||||
size_t git_remote_refspec_count(git_remote *remote);
|
||||
const git_refspec * git_remote_get_refspec(git_remote *remote, size_t n);
|
||||
|
||||
int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
|
||||
int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array);
|
||||
int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
|
||||
int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array);
|
||||
|
||||
void git_remote_free(git_remote *remote);
|
||||
|
||||
@ -251,13 +325,12 @@ int git_cred_ssh_key_from_agent(
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_IGNORE_RESET = -1,
|
||||
GIT_SUBMODULE_IGNORE_UNSPECIFIED = -1,
|
||||
|
||||
GIT_SUBMODULE_IGNORE_NONE = 1,
|
||||
GIT_SUBMODULE_IGNORE_UNTRACKED = 2,
|
||||
GIT_SUBMODULE_IGNORE_DIRTY = 3,
|
||||
GIT_SUBMODULE_IGNORE_ALL = 4,
|
||||
GIT_SUBMODULE_IGNORE_DEFAULT = 0
|
||||
} git_submodule_ignore_t;
|
||||
|
||||
typedef enum {
|
||||
@ -295,13 +368,20 @@ typedef int (*git_diff_notify_cb)(
|
||||
const char *matched_pathspec,
|
||||
void *payload);
|
||||
|
||||
typedef int (*git_diff_progress_cb)(
|
||||
const git_diff *diff_so_far,
|
||||
const char *old_path,
|
||||
const char *new_path,
|
||||
void *payload);
|
||||
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
uint32_t flags;
|
||||
git_submodule_ignore_t ignore_submodules;
|
||||
git_strarray pathspec;
|
||||
git_diff_notify_cb notify_cb;
|
||||
void *notify_payload;
|
||||
git_diff_notify_cb notify_cb;
|
||||
git_diff_progress_cb progress_cb;
|
||||
void *payload;
|
||||
uint32_t context_lines;
|
||||
uint32_t interhunk_lines;
|
||||
uint16_t id_abbrev;
|
||||
@ -330,7 +410,16 @@ int git_diff_tree_to_index(git_diff **diff, git_repository *repo, git_tree *old_
|
||||
* git_checkout
|
||||
*/
|
||||
|
||||
typedef enum { ... } git_checkout_notify_t;
|
||||
typedef enum {
|
||||
GIT_CHECKOUT_NOTIFY_NONE = 0,
|
||||
GIT_CHECKOUT_NOTIFY_CONFLICT = 1,
|
||||
GIT_CHECKOUT_NOTIFY_DIRTY = 2,
|
||||
GIT_CHECKOUT_NOTIFY_UPDATED = 4,
|
||||
GIT_CHECKOUT_NOTIFY_UNTRACKED = 8,
|
||||
GIT_CHECKOUT_NOTIFY_IGNORED = 16,
|
||||
|
||||
GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFF
|
||||
} git_checkout_notify_t;
|
||||
|
||||
typedef int (*git_checkout_notify_cb)(
|
||||
git_checkout_notify_t why,
|
||||
@ -346,32 +435,37 @@ 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;
|
||||
|
||||
int git_checkout_init_options(git_checkout_options *opts, unsigned int version);
|
||||
@ -406,11 +500,10 @@ typedef enum {
|
||||
typedef struct git_clone_options {
|
||||
unsigned int version;
|
||||
git_checkout_options checkout_opts;
|
||||
git_remote_callbacks remote_callbacks;
|
||||
git_fetch_options fetch_opts;
|
||||
int bare;
|
||||
git_clone_local_t local;
|
||||
const char* checkout_branch;
|
||||
git_signature *signature;
|
||||
git_repository_create_cb repository_cb;
|
||||
void *repository_cb_payload;
|
||||
git_remote_create_cb remote_cb;
|
||||
@ -433,24 +526,30 @@ typedef ... git_config;
|
||||
typedef ... git_config_iterator;
|
||||
|
||||
typedef enum {
|
||||
GIT_CONFIG_LEVEL_SYSTEM = 1,
|
||||
GIT_CONFIG_LEVEL_XDG = 2,
|
||||
GIT_CONFIG_LEVEL_GLOBAL = 3,
|
||||
GIT_CONFIG_LEVEL_LOCAL = 4,
|
||||
GIT_CONFIG_LEVEL_APP = 5,
|
||||
GIT_CONFIG_LEVEL_PROGRAMDATA = 1,
|
||||
GIT_CONFIG_LEVEL_SYSTEM = 2,
|
||||
GIT_CONFIG_LEVEL_XDG = 3,
|
||||
GIT_CONFIG_LEVEL_GLOBAL = 4,
|
||||
GIT_CONFIG_LEVEL_LOCAL = 5,
|
||||
GIT_CONFIG_LEVEL_APP = 6,
|
||||
GIT_CONFIG_HIGHEST_LEVEL = -1,
|
||||
} git_config_level_t;
|
||||
|
||||
typedef struct {
|
||||
typedef struct git_config_entry {
|
||||
const char *name;
|
||||
const char *value;
|
||||
git_config_level_t level;
|
||||
void (*free)(struct git_config_entry *entry);
|
||||
void *payload;
|
||||
} git_config_entry;
|
||||
|
||||
void git_config_entry_free(git_config_entry *);
|
||||
|
||||
int git_repository_config(git_config **out, git_repository *repo);
|
||||
int git_repository_config_snapshot(git_config **out, git_repository *repo);
|
||||
void git_config_free(git_config *cfg);
|
||||
|
||||
int git_config_get_entry(git_config_entry **out, const git_config *cfg, const char *name);
|
||||
int git_config_get_string(const char **out, const git_config *cfg, const char *name);
|
||||
int git_config_set_string(git_config *cfg, const char *name, const char *value);
|
||||
int git_config_set_bool(git_config *cfg, const char *name, int value);
|
||||
@ -498,6 +597,7 @@ typedef enum {
|
||||
GIT_REPOSITORY_INIT_MKDIR,
|
||||
GIT_REPOSITORY_INIT_MKPATH,
|
||||
GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE,
|
||||
GIT_REPOSITORY_INIT_RELATIVE_GITLINK,
|
||||
...
|
||||
} git_repository_init_flag_t;
|
||||
|
||||
@ -532,8 +632,23 @@ 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, const git_signature *signature, const char *log_message);
|
||||
int git_repository_set_head_detached(git_repository *repo, const git_oid *commitish, const git_signature *signature, const char *log_message);
|
||||
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
|
||||
@ -541,25 +656,25 @@ int git_repository_set_head_detached(git_repository *repo, const git_oid *commit
|
||||
typedef int64_t git_time_t;
|
||||
|
||||
typedef struct {
|
||||
git_time_t seconds;
|
||||
unsigned int nanoseconds;
|
||||
int32_t seconds;
|
||||
uint32_t nanoseconds;
|
||||
} git_index_time;
|
||||
|
||||
typedef struct git_index_entry {
|
||||
git_index_time ctime;
|
||||
git_index_time mtime;
|
||||
|
||||
unsigned int dev;
|
||||
unsigned int ino;
|
||||
unsigned int mode;
|
||||
unsigned int uid;
|
||||
unsigned int gid;
|
||||
git_off_t file_size;
|
||||
uint32_t dev;
|
||||
uint32_t ino;
|
||||
uint32_t mode;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t file_size;
|
||||
|
||||
git_oid id;
|
||||
|
||||
unsigned short flags;
|
||||
unsigned short flags_extended;
|
||||
uint16_t flags;
|
||||
uint16_t flags_extended;
|
||||
|
||||
const char *path;
|
||||
} git_index_entry;
|
||||
@ -605,22 +720,22 @@ typedef struct git_blame_options {
|
||||
uint16_t min_match_characters;
|
||||
git_oid newest_commit;
|
||||
git_oid oldest_commit;
|
||||
uint32_t min_line;
|
||||
uint32_t max_line;
|
||||
size_t min_line;
|
||||
size_t max_line;
|
||||
} git_blame_options;
|
||||
|
||||
#define GIT_BLAME_OPTIONS_VERSION ...
|
||||
|
||||
typedef struct git_blame_hunk {
|
||||
uint16_t lines_in_hunk;
|
||||
size_t lines_in_hunk;
|
||||
|
||||
git_oid final_commit_id;
|
||||
uint16_t final_start_line_number;
|
||||
size_t final_start_line_number;
|
||||
git_signature *final_signature;
|
||||
|
||||
git_oid orig_commit_id;
|
||||
const char *orig_path;
|
||||
uint16_t orig_start_line_number;
|
||||
size_t orig_start_line_number;
|
||||
git_signature *orig_signature;
|
||||
|
||||
char boundary;
|
||||
@ -629,7 +744,7 @@ typedef struct 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, uint32_t lineno);
|
||||
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);
|
||||
|
||||
@ -637,7 +752,12 @@ void git_blame_free(git_blame *blame);
|
||||
* Merging
|
||||
*/
|
||||
|
||||
typedef enum { ... } git_merge_tree_flag_t;
|
||||
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,
|
||||
@ -648,14 +768,159 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
git_merge_tree_flag_t flags;
|
||||
git_merge_flag_t flags;
|
||||
unsigned int rename_threshold;
|
||||
unsigned int target_limit;
|
||||
git_diff_similarity_metric *metric;
|
||||
unsigned int recursion_limit;
|
||||
const char *default_driver;
|
||||
git_merge_file_favor_t file_favor;
|
||||
unsigned int file_flags;
|
||||
} git_merge_options;
|
||||
|
||||
#define GIT_MERGE_OPTIONS_VERSION 1
|
||||
|
||||
typedef struct {
|
||||
unsigned int automergeable;
|
||||
const char *path;
|
||||
unsigned int mode;
|
||||
const char *ptr;
|
||||
size_t len;
|
||||
} git_merge_file_result;
|
||||
|
||||
typedef enum {
|
||||
GIT_MERGE_FILE_DEFAULT = 0,
|
||||
GIT_MERGE_FILE_STYLE_MERGE = 1,
|
||||
GIT_MERGE_FILE_STYLE_DIFF3 = 2,
|
||||
GIT_MERGE_FILE_SIMPLIFY_ALNUM = 4,
|
||||
GIT_MERGE_FILE_IGNORE_WHITESPACE = 8,
|
||||
GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE = 16,
|
||||
GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL = 32,
|
||||
GIT_MERGE_FILE_DIFF_PATIENCE = 64,
|
||||
GIT_MERGE_FILE_DIFF_MINIMAL = 128,
|
||||
} git_merge_file_flag_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
const char *ancestor_label;
|
||||
const char *our_label;
|
||||
const char *their_label;
|
||||
git_merge_file_favor_t favor;
|
||||
git_merge_file_flag_t flags;
|
||||
} git_merge_file_options;
|
||||
|
||||
#define GIT_MERGE_OPTIONS_VERSION ...
|
||||
|
||||
int git_merge_init_options(git_merge_options *opts, unsigned int version);
|
||||
int git_merge_commits(git_index **out, git_repository *repo, const git_commit *our_commit, const git_commit *their_commit, const git_merge_options *opts);
|
||||
int git_merge_trees(git_index **out, git_repository *repo, const git_tree *ancestor_tree, const git_tree *our_tree, const git_tree *their_tree, const git_merge_options *opts);
|
||||
int git_merge_file_from_index(git_merge_file_result *out, git_repository *repo, const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *opts);
|
||||
void git_merge_file_result_free(git_merge_file_result *result);
|
||||
|
||||
/*
|
||||
* git_stash
|
||||
*/
|
||||
|
||||
typedef int (*git_stash_cb)(
|
||||
size_t index, const char* message, const git_oid *stash_id, void *payload);
|
||||
|
||||
typedef enum {
|
||||
GIT_STASH_APPLY_PROGRESS_NONE = 0,
|
||||
GIT_STASH_APPLY_PROGRESS_LOADING_STASH = 1,
|
||||
GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX = 2,
|
||||
GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED = 3,
|
||||
GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED = 4,
|
||||
GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED = 5,
|
||||
GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED = 6,
|
||||
GIT_STASH_APPLY_PROGRESS_DONE = 7,
|
||||
} git_stash_apply_progress_t;
|
||||
|
||||
typedef int (*git_stash_apply_progress_cb)(
|
||||
git_stash_apply_progress_t progress, void *payload);
|
||||
|
||||
typedef enum {
|
||||
GIT_STASH_DEFAULT = 0,
|
||||
GIT_STASH_KEEP_INDEX = 1,
|
||||
GIT_STASH_INCLUDE_UNTRACKED = 2,
|
||||
GIT_STASH_INCLUDE_IGNORED = 4,
|
||||
} git_stash_flags;
|
||||
|
||||
typedef enum {
|
||||
GIT_STASH_APPLY_DEFAULT = 0,
|
||||
GIT_STASH_APPLY_REINSTATE_INDEX = 1,
|
||||
} git_stash_apply_flags;
|
||||
|
||||
typedef struct git_stash_apply_options {
|
||||
unsigned int version;
|
||||
git_stash_apply_flags flags;
|
||||
git_checkout_options checkout_options;
|
||||
git_stash_apply_progress_cb progress_cb;
|
||||
void *progress_payload;
|
||||
} git_stash_apply_options;
|
||||
|
||||
#define GIT_STASH_APPLY_OPTIONS_VERSION ...
|
||||
|
||||
int git_stash_save(git_oid *out, git_repository *repo, const git_signature *stasher, const char *message, uint32_t flags);
|
||||
int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version);
|
||||
int git_stash_apply(git_repository *repo, size_t index, const git_stash_apply_options *options);
|
||||
int git_stash_foreach(git_repository *repo, git_stash_cb callback, void *payload);
|
||||
int git_stash_drop(git_repository *repo, size_t index);
|
||||
int git_stash_pop(git_repository *repo, size_t index, const git_stash_apply_options *options);
|
||||
|
||||
/*
|
||||
* Describe
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
GIT_DESCRIBE_DEFAULT,
|
||||
GIT_DESCRIBE_TAGS,
|
||||
GIT_DESCRIBE_ALL,
|
||||
} git_describe_strategy_t;
|
||||
|
||||
typedef struct git_describe_options {
|
||||
unsigned int version;
|
||||
unsigned int max_candidates_tags;
|
||||
unsigned int describe_strategy;
|
||||
const char *pattern;
|
||||
int only_follow_first_parent;
|
||||
int show_commit_oid_as_fallback;
|
||||
} git_describe_options;
|
||||
|
||||
#define GIT_DESCRIBE_OPTIONS_VERSION ...
|
||||
|
||||
int git_describe_init_options(git_describe_options *opts, unsigned int version);
|
||||
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
unsigned int abbreviated_size;
|
||||
int always_use_long_format;
|
||||
const char *dirty_suffix;
|
||||
} git_describe_format_options;
|
||||
|
||||
#define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION ...
|
||||
|
||||
int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version);
|
||||
|
||||
typedef ... git_describe_result;
|
||||
|
||||
int git_describe_commit(git_describe_result **result, git_object *committish, git_describe_options *opts);
|
||||
int git_describe_workdir(git_describe_result **out, git_repository *repo, git_describe_options *opts);
|
||||
int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts);
|
||||
void git_describe_result_free(git_describe_result *result);
|
||||
|
||||
#define GIT_ATTR_CHECK_FILE_THEN_INDEX ...
|
||||
#define GIT_ATTR_CHECK_INDEX_THEN_FILE ...
|
||||
#define GIT_ATTR_CHECK_INDEX_ONLY ...
|
||||
#define GIT_ATTR_CHECK_NO_SYSTEM ...
|
||||
|
||||
typedef enum {
|
||||
GIT_ATTR_UNSPECIFIED_T = 0,
|
||||
GIT_ATTR_TRUE_T,
|
||||
GIT_ATTR_FALSE_T,
|
||||
GIT_ATTR_VALUE_T,
|
||||
} git_attr_t;
|
||||
|
||||
int git_attr_get(const char **value_out, git_repository *repo, uint32_t flags, const char *path, const char *name);
|
||||
git_attr_t git_attr_value(const char *attr);
|
||||
|
||||
int git_revert_commit(git_index **out, git_repository *repo, git_commit *revert_commit, git_commit *our_commit, unsigned int mainline, const git_merge_options *merge_options);
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -25,32 +25,45 @@
|
||||
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
|
||||
# ffi
|
||||
# Import from pygit2
|
||||
from .ffi import ffi, C
|
||||
|
||||
from _pygit2 import GitError
|
||||
|
||||
|
||||
value_errors = set([C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EEXISTS,
|
||||
C.GIT_EAMBIGUOUS])
|
||||
|
||||
def check_error(err, io=False):
|
||||
if err >= 0:
|
||||
return
|
||||
|
||||
message = "(no message provided)"
|
||||
# Error message
|
||||
giterr = C.giterr_last()
|
||||
if giterr != ffi.NULL:
|
||||
message = ffi.string(giterr.message).decode()
|
||||
message = ffi.string(giterr.message).decode('utf8')
|
||||
else:
|
||||
message = "err %d (no message provided)" % err
|
||||
|
||||
if err in [C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EEXISTS,
|
||||
C.GIT_EAMBIGUOUS]:
|
||||
# Translate to Python errors
|
||||
if err in value_errors:
|
||||
raise ValueError(message)
|
||||
elif err == C.GIT_ENOTFOUND:
|
||||
|
||||
if err == C.GIT_ENOTFOUND:
|
||||
if io:
|
||||
raise IOError(message)
|
||||
else:
|
||||
raise KeyError(message)
|
||||
elif err == C.GIT_EINVALIDSPEC:
|
||||
|
||||
raise KeyError(message)
|
||||
|
||||
if err == C.GIT_EINVALIDSPEC:
|
||||
raise ValueError(message)
|
||||
elif err == C.GIT_ITEROVER:
|
||||
|
||||
if err == C.GIT_ITEROVER:
|
||||
raise StopIteration()
|
||||
|
||||
# Generic Git error
|
||||
raise GitError(message)
|
||||
|
||||
# Indicate that we want libgit2 to pretend a function was not set
|
||||
class Passthrough(Exception):
|
||||
def __init__(self):
|
||||
super(Passthrough, self).__init__( "The function asked for pass-through")
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -29,7 +29,8 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import from pygit2
|
||||
from ._utils import get_ffi
|
||||
|
||||
|
||||
ffi, C = get_ffi()
|
||||
try:
|
||||
from ._libgit2 import ffi, lib as C
|
||||
except ImportError:
|
||||
from ._run import ffi, preamble, C_KEYWORDS
|
||||
C = ffi.verify(preamble, **C_KEYWORDS)
|
||||
|
114
pygit2/index.py
114
pygit2/index.py
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -28,11 +28,14 @@
|
||||
# Import from the future
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import weakref
|
||||
|
||||
# Import from pygit2
|
||||
from _pygit2 import Oid, Tree, Diff
|
||||
from .errors import check_error
|
||||
from .ffi import ffi, C
|
||||
from .utils import is_string, strings_to_strarray, to_bytes, to_str
|
||||
from .utils import is_string, to_bytes, to_str
|
||||
from .utils import GenericIterator, StrArray
|
||||
|
||||
|
||||
class Index(object):
|
||||
@ -47,6 +50,7 @@ class Index(object):
|
||||
err = C.git_index_open(cindex, to_bytes(path))
|
||||
check_error(err)
|
||||
|
||||
self._repo = None
|
||||
self._index = cindex[0]
|
||||
self._cindex = cindex
|
||||
|
||||
@ -92,7 +96,7 @@ class Index(object):
|
||||
return IndexEntry._from_c(centry)
|
||||
|
||||
def __iter__(self):
|
||||
return IndexIterator(self)
|
||||
return GenericIterator(self)
|
||||
|
||||
def read(self, force=True):
|
||||
"""Update the contents the Index
|
||||
@ -109,8 +113,7 @@ class Index(object):
|
||||
check_error(err, True)
|
||||
|
||||
def write(self):
|
||||
"""Write the contents of the Index to disk
|
||||
"""
|
||||
"""Write the contents of the Index to disk."""
|
||||
err = C.git_index_write(self._index)
|
||||
check_error(err, True)
|
||||
|
||||
@ -119,21 +122,21 @@ class Index(object):
|
||||
check_error(err)
|
||||
|
||||
def read_tree(self, tree):
|
||||
"""read_tree([Tree|Oid])
|
||||
|
||||
Replace the contents of the Index with those of a tree
|
||||
"""Replace the contents of the Index with those of the given tree,
|
||||
expressed either as a <Tree> object or as an oid (string or <Oid>).
|
||||
|
||||
The tree will be read recursively and all its children will also be
|
||||
inserted into the Index.
|
||||
"""
|
||||
repo = self._repo
|
||||
if is_string(tree):
|
||||
tree = self._repo[tree]
|
||||
tree = repo[tree]
|
||||
|
||||
if isinstance(tree, Oid):
|
||||
if not hasattr(self, '_repo'):
|
||||
if repo is None:
|
||||
raise TypeError("id given but no associated repository")
|
||||
|
||||
tree = self._repo[tree]
|
||||
tree = repo[tree]
|
||||
elif not isinstance(tree, Tree):
|
||||
raise TypeError("argument must be Oid or Tree")
|
||||
|
||||
@ -143,9 +146,8 @@ class Index(object):
|
||||
check_error(err)
|
||||
|
||||
def write_tree(self, repo=None):
|
||||
"""write_tree([repo]) -> Oid
|
||||
|
||||
Create a tree out of the Index
|
||||
"""Create a tree out of the Index. Return the <Oid> object of the
|
||||
written tree.
|
||||
|
||||
The contents of the index will be written out to the object
|
||||
database. If there is no associated repository, 'repo' must be
|
||||
@ -155,6 +157,9 @@ class Index(object):
|
||||
It returns the id of the resulting tree.
|
||||
"""
|
||||
coid = ffi.new('git_oid *')
|
||||
|
||||
repo = repo or self._repo
|
||||
|
||||
if repo:
|
||||
err = C.git_index_write_tree_to(coid, self._index, repo._repo)
|
||||
else:
|
||||
@ -163,10 +168,10 @@ class Index(object):
|
||||
check_error(err)
|
||||
return Oid(raw=bytes(ffi.buffer(coid)[:]))
|
||||
|
||||
def remove(self, path):
|
||||
def remove(self, path, level=0):
|
||||
"""Remove an entry from the Index.
|
||||
"""
|
||||
err = C.git_index_remove(self._index, to_bytes(path), 0)
|
||||
err = C.git_index_remove(self._index, to_bytes(path), level)
|
||||
check_error(err, True)
|
||||
|
||||
def add_all(self, pathspecs=[]):
|
||||
@ -175,18 +180,16 @@ class Index(object):
|
||||
If pathspecs are specified, only files matching those pathspecs will
|
||||
be added.
|
||||
"""
|
||||
arr, refs = strings_to_strarray(pathspecs)
|
||||
err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL)
|
||||
check_error(err, True)
|
||||
with StrArray(pathspecs) as arr:
|
||||
err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL)
|
||||
check_error(err, True)
|
||||
|
||||
def add(self, path_or_entry):
|
||||
"""add([path|entry])
|
||||
"""Add or update an entry in the Index.
|
||||
|
||||
Add or update an entry in the Index
|
||||
|
||||
If a path is given, that file will be added. The path must be
|
||||
relative to the root of the worktree and the Index must be associated
|
||||
with a repository.
|
||||
If a path is given, that file will be added. The path must be relative
|
||||
to the root of the worktree and the Index must be associated with a
|
||||
repository.
|
||||
|
||||
If an IndexEntry is given, that entry will be added or update in the
|
||||
Index without checking for the existence of the path or id.
|
||||
@ -205,12 +208,8 @@ class Index(object):
|
||||
check_error(err, True)
|
||||
|
||||
def diff_to_workdir(self, flags=0, context_lines=3, interhunk_lines=0):
|
||||
"""diff_to_workdir(flags=0, context_lines=3, interhunk_lines=0) -> Diff
|
||||
|
||||
Diff the index against the working directory
|
||||
|
||||
Return a :py:class:`~pygit2.Diff` object with the differences
|
||||
between the index and the working copy.
|
||||
"""Diff the index against the working directory. Return a <Diff> object
|
||||
with the differences between the index and the working copy.
|
||||
|
||||
Arguments:
|
||||
|
||||
@ -222,7 +221,8 @@ class Index(object):
|
||||
interhunk_lines: the maximum number of unchanged lines between hunk
|
||||
boundaries before the hunks will be merged into a one
|
||||
"""
|
||||
if not hasattr(self, '_repo'):
|
||||
repo = self._repo
|
||||
if repo is None:
|
||||
raise ValueError('diff needs an associated repository')
|
||||
|
||||
copts = ffi.new('git_diff_options *')
|
||||
@ -234,19 +234,15 @@ class Index(object):
|
||||
copts.interhunk_lines = interhunk_lines
|
||||
|
||||
cdiff = ffi.new('git_diff **')
|
||||
err = C.git_diff_index_to_workdir(cdiff, self._repo._repo,
|
||||
self._index, copts)
|
||||
err = C.git_diff_index_to_workdir(cdiff, repo._repo, self._index,
|
||||
copts)
|
||||
check_error(err)
|
||||
|
||||
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo)
|
||||
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo)
|
||||
|
||||
def diff_to_tree(self, tree, flags=0, context_lines=3, interhunk_lines=0):
|
||||
"""diff_to_tree(tree, flags=0, context_lines=3, interhunk_lines=0) -> Diff
|
||||
|
||||
Diff the index against a tree
|
||||
|
||||
Return a :py:class:`~pygit2.Diff` object with the differences between
|
||||
the index and the given tree.
|
||||
"""Diff the index against a tree. Return a <Diff> object with the
|
||||
differences between the index and the given tree.
|
||||
|
||||
Arguments:
|
||||
|
||||
@ -260,8 +256,8 @@ class Index(object):
|
||||
interhunk_lines: the maximum number of unchanged lines between hunk
|
||||
boundaries before the hunks will be merged into a one.
|
||||
"""
|
||||
|
||||
if not hasattr(self, '_repo'):
|
||||
repo = self._repo
|
||||
if repo is None:
|
||||
raise ValueError('diff needs an associated repository')
|
||||
|
||||
if not isinstance(tree, Tree):
|
||||
@ -279,11 +275,11 @@ class Index(object):
|
||||
ffi.buffer(ctree)[:] = tree._pointer[:]
|
||||
|
||||
cdiff = ffi.new('git_diff **')
|
||||
err = C.git_diff_tree_to_index(cdiff, self._repo._repo, ctree[0],
|
||||
err = C.git_diff_tree_to_index(cdiff, repo._repo, ctree[0],
|
||||
self._index, copts)
|
||||
check_error(err)
|
||||
|
||||
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo)
|
||||
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo)
|
||||
|
||||
|
||||
#
|
||||
@ -314,10 +310,12 @@ class Index(object):
|
||||
self._conflicts = None
|
||||
return None
|
||||
|
||||
if self._conflicts is None:
|
||||
self._conflicts = ConflictCollection(self)
|
||||
if self._conflicts is None or self._conflicts() is None:
|
||||
conflicts = ConflictCollection(self)
|
||||
self._conflicts = weakref.ref(conflicts)
|
||||
return conflicts
|
||||
|
||||
return self._conflicts
|
||||
return self._conflicts()
|
||||
|
||||
|
||||
class IndexEntry(object):
|
||||
@ -369,26 +367,6 @@ class IndexEntry(object):
|
||||
return entry
|
||||
|
||||
|
||||
class IndexIterator(object):
|
||||
|
||||
def __init__(self, index):
|
||||
self.index = index
|
||||
self.n = 0
|
||||
self.max = len(index)
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
def __next__(self):
|
||||
if self.n >= self.max:
|
||||
raise StopIteration
|
||||
|
||||
entry = self.index[self.n]
|
||||
self.n += 1
|
||||
|
||||
return entry
|
||||
|
||||
|
||||
class ConflictCollection(object):
|
||||
|
||||
def __init__(self, index):
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -66,16 +66,14 @@ class Refspec(object):
|
||||
return C.git_refspec_direction(self._refspec)
|
||||
|
||||
def src_matches(self, ref):
|
||||
"""src_matches(str) -> Bool
|
||||
|
||||
Returns whether the given string matches the source of this refspec"""
|
||||
"""Return True if the given string matches the source of this refspec,
|
||||
False otherwise.
|
||||
"""
|
||||
return bool(C.git_refspec_src_matches(self._refspec, to_bytes(ref)))
|
||||
|
||||
def dst_matches(self, ref):
|
||||
"""dst_matches(str) -> Bool
|
||||
|
||||
Returns whether the given string matches the destination of this
|
||||
refspec"""
|
||||
"""Return True if the given string matches the destination of this
|
||||
refspec, False otherwise."""
|
||||
return bool(C.git_refspec_dst_matches(self._refspec, to_bytes(ref)))
|
||||
|
||||
def _transform(self, ref, fn):
|
||||
@ -89,15 +87,13 @@ class Refspec(object):
|
||||
C.git_buf_free(buf)
|
||||
|
||||
def transform(self, ref):
|
||||
"""transform(str) -> str
|
||||
|
||||
Transform a reference name according to this refspec from the lhs to
|
||||
the rhs."""
|
||||
"""Transform a reference name according to this refspec from the lhs to
|
||||
the rhs. Return an string.
|
||||
"""
|
||||
return self._transform(ref, C.git_refspec_transform)
|
||||
|
||||
def rtransform(self, ref):
|
||||
"""rtransform(str) -> str
|
||||
|
||||
Transform a reference name according to this refspec from the lhs
|
||||
to the rhs"""
|
||||
"""Transform a reference name according to this refspec from the lhs to
|
||||
the rhs. Return an string.
|
||||
"""
|
||||
return self._transform(ref, C.git_refspec_rtransform)
|
||||
|
511
pygit2/remote.py
511
pygit2/remote.py
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -30,11 +30,10 @@ from __future__ import absolute_import
|
||||
|
||||
# Import from pygit2
|
||||
from _pygit2 import Oid
|
||||
from .errors import check_error, GitError
|
||||
from .errors import check_error, Passthrough
|
||||
from .ffi import ffi, C
|
||||
from .credentials import KeypairFromAgent
|
||||
from .refspec import Refspec
|
||||
from .utils import to_bytes, strarray_to_strings, strings_to_strarray
|
||||
from .utils import to_bytes, strarray_to_strings, StrArray
|
||||
|
||||
|
||||
def maybe_string(ptr):
|
||||
@ -71,7 +70,27 @@ class TransferProgress(object):
|
||||
""""Number of bytes received up to now"""
|
||||
|
||||
|
||||
class Remote(object):
|
||||
class RemoteCallbacks(object):
|
||||
"""Base class for pygit2 remote callbacks.
|
||||
|
||||
Inherit from this class and override the callbacks which you want to use
|
||||
in your class, which you can then pass to the network operations.
|
||||
"""
|
||||
|
||||
def __init__(self, credentials=None, certificate=None):
|
||||
"""Initialize some callbacks in-line
|
||||
|
||||
Use this constructor to provide credentials and certificate
|
||||
callbacks in-line, instead of defining your own class for these ones.
|
||||
|
||||
You can e.g. also pass in one of the credential objects as 'credentials'
|
||||
instead of creating a function which returns a hard-coded object.
|
||||
"""
|
||||
|
||||
if credentials is not None:
|
||||
self.credentials = credentials
|
||||
if certificate is not None:
|
||||
self.certificate = certificate
|
||||
|
||||
def sideband_progress(self, string):
|
||||
"""Progress output callback
|
||||
@ -80,7 +99,6 @@ class Remote(object):
|
||||
|
||||
:param str string: Progress output from the remote
|
||||
"""
|
||||
pass
|
||||
|
||||
def credentials(self, url, username_from_url, allowed_types):
|
||||
"""Credentials callback
|
||||
@ -100,7 +118,26 @@ class Remote(object):
|
||||
|
||||
Return value: credential
|
||||
"""
|
||||
pass
|
||||
raise Passthrough
|
||||
|
||||
def certificate_check(self, certificate, valid, host):
|
||||
"""Certificate callback
|
||||
|
||||
Override with your own function to determine whether the accept
|
||||
the server's certificate.
|
||||
|
||||
:param None certificate: The certificate. It is currently always None
|
||||
while we figure out how to represent it cross-platform
|
||||
|
||||
:param bool valid: Whether the TLS/SSH library thinks the certificate
|
||||
is valid
|
||||
|
||||
:param str host: The hostname we want to connect to
|
||||
|
||||
Return value: True to connect, False to abort
|
||||
"""
|
||||
|
||||
raise Passthrough
|
||||
|
||||
def transfer_progress(self, stats):
|
||||
"""Transfer progress callback
|
||||
@ -109,7 +146,6 @@ class Remote(object):
|
||||
|
||||
:param TransferProgress stats: The progress up to now
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_tips(self, refname, old, new):
|
||||
"""Update tips callabck
|
||||
@ -131,6 +167,159 @@ class Remote(object):
|
||||
: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"""
|
||||
|
||||
@ -153,77 +342,39 @@ class Remote(object):
|
||||
|
||||
return maybe_string(C.git_remote_url(self._remote))
|
||||
|
||||
@url.setter
|
||||
def url(self, value):
|
||||
err = C.git_remote_set_url(self._remote, to_bytes(value))
|
||||
check_error(err)
|
||||
|
||||
@property
|
||||
def push_url(self):
|
||||
"""Push url of the remote"""
|
||||
|
||||
return maybe_string(C.git_remote_pushurl(self._remote))
|
||||
|
||||
@push_url.setter
|
||||
def push_url(self, value):
|
||||
err = C.git_remote_set_pushurl(self._remote, to_bytes(value))
|
||||
check_error(err)
|
||||
|
||||
def save(self):
|
||||
"""save()
|
||||
|
||||
Save a remote to its repository's configuration"""
|
||||
"""Save a remote to its repository's configuration."""
|
||||
|
||||
err = C.git_remote_save(self._remote)
|
||||
check_error(err)
|
||||
|
||||
def fetch(self, signature=None, message=None):
|
||||
"""fetch(signature, message) -> TransferProgress
|
||||
|
||||
Perform a fetch against this remote.
|
||||
def fetch(self, refspecs=None, message=None, callbacks=None):
|
||||
"""Perform a fetch against this remote. Returns a <TransferProgress>
|
||||
object.
|
||||
"""
|
||||
|
||||
# Get the default callbacks first
|
||||
defaultcallbacks = ffi.new('git_remote_callbacks *')
|
||||
err = C.git_remote_init_callbacks(defaultcallbacks, 1)
|
||||
check_error(err)
|
||||
fetch_opts = ffi.new('git_fetch_options *')
|
||||
err = C.git_fetch_init_options(fetch_opts, C.GIT_FETCH_OPTIONS_VERSION)
|
||||
|
||||
# Build custom 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
|
||||
if callbacks is None:
|
||||
callbacks = RemoteCallbacks()
|
||||
|
||||
err = C.git_remote_set_callbacks(self._remote, callbacks)
|
||||
try:
|
||||
check_error(err)
|
||||
except:
|
||||
self._self_handle = None
|
||||
raise
|
||||
|
||||
if signature:
|
||||
ptr = signature._pointer[:]
|
||||
else:
|
||||
ptr = ffi.NULL
|
||||
|
||||
self._stored_exception = None
|
||||
callbacks._fill_fetch_options(fetch_opts)
|
||||
|
||||
try:
|
||||
err = C.git_remote_fetch(self._remote, ffi.NULL, ptr, to_bytes(message))
|
||||
if self._stored_exception:
|
||||
raise self._stored_exception
|
||||
|
||||
check_error(err)
|
||||
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:
|
||||
# Even on error, clear stored callbacks and reset to default
|
||||
self._self_handle = None
|
||||
err = C.git_remote_set_callbacks(self._remote, defaultcallbacks)
|
||||
check_error(err)
|
||||
callbacks._self_handle = None
|
||||
|
||||
return TransferProgress(C.git_remote_stats(self._remote))
|
||||
|
||||
@ -234,10 +385,7 @@ class Remote(object):
|
||||
return C.git_remote_refspec_count(self._remote)
|
||||
|
||||
def get_refspec(self, n):
|
||||
"""get_refspec(n) -> Refspec
|
||||
|
||||
Return the refspec at the given position
|
||||
"""
|
||||
"""Return the <Refspec> object at the given position."""
|
||||
spec = C.git_remote_get_refspec(self._remote, n)
|
||||
return Refspec(self, spec)
|
||||
|
||||
@ -251,12 +399,6 @@ class Remote(object):
|
||||
|
||||
return strarray_to_strings(specs)
|
||||
|
||||
@fetch_refspecs.setter
|
||||
def fetch_refspecs(self, l):
|
||||
arr, refs = strings_to_strarray(l)
|
||||
err = C.git_remote_set_fetch_refspecs(self._remote, arr)
|
||||
check_error(err)
|
||||
|
||||
@property
|
||||
def push_refspecs(self):
|
||||
"""Refspecs that will be used for pushing"""
|
||||
@ -267,168 +409,33 @@ class Remote(object):
|
||||
|
||||
return strarray_to_strings(specs)
|
||||
|
||||
@push_refspecs.setter
|
||||
def push_refspecs(self, l):
|
||||
arr, refs = strings_to_strarray(l)
|
||||
err = C.git_remote_set_push_refspecs(self._remote, arr)
|
||||
check_error(err)
|
||||
|
||||
def add_fetch(self, spec):
|
||||
"""add_fetch(refspec)
|
||||
|
||||
Add a fetch refspec (str) 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 (str) to the remote"""
|
||||
|
||||
err = C.git_remote_add_push(self._remote, to_bytes(spec))
|
||||
check_error(err)
|
||||
|
||||
def push(self, specs, signature=None, message=None):
|
||||
"""push(specs, signature, message)
|
||||
|
||||
Push the given refspec to the remote. Raises ``GitError`` on
|
||||
def push(self, specs, callbacks=None):
|
||||
"""Push the given refspec to the remote. Raises ``GitError`` on
|
||||
protocol error or unpack failure.
|
||||
|
||||
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.
|
||||
|
||||
:param [str] specs: push refspecs to use
|
||||
:param Signature signature: signature to use when updating the tips
|
||||
:param str message: message to use when updating the tips
|
||||
|
||||
"""
|
||||
# Get the default callbacks first
|
||||
defaultcallbacks = ffi.new('git_remote_callbacks *')
|
||||
err = C.git_remote_init_callbacks(defaultcallbacks, 1)
|
||||
check_error(err)
|
||||
push_opts = ffi.new('git_push_options *')
|
||||
err = C.git_push_init_options(push_opts, C.GIT_PUSH_OPTIONS_VERSION)
|
||||
|
||||
refspecs, refspecs_refs = strings_to_strarray(specs)
|
||||
if signature:
|
||||
sig_cptr = ffi.new('git_signature **')
|
||||
ffi.buffer(sig_cptr)[:] = signature._pointer[:]
|
||||
sig_ptr = sig_cptr[0]
|
||||
else:
|
||||
sig_ptr = ffi.NULL
|
||||
if callbacks is None:
|
||||
callbacks = RemoteCallbacks()
|
||||
|
||||
callbacks._fill_push_options(push_opts)
|
||||
# Build custom 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
|
||||
callbacks.push_update_reference = self._push_update_reference_cb
|
||||
# We need to make sure that this handle stays alive
|
||||
self._self_handle = ffi.new_handle(self)
|
||||
callbacks.payload = self._self_handle
|
||||
|
||||
try:
|
||||
err = C.git_remote_set_callbacks(self._remote, callbacks)
|
||||
check_error(err)
|
||||
except:
|
||||
self._self_handle = None
|
||||
raise
|
||||
|
||||
try:
|
||||
err = C.git_remote_push(self._remote, refspecs, ffi.NULL, sig_ptr, to_bytes(message))
|
||||
check_error(err)
|
||||
with StrArray(specs) as refspecs:
|
||||
err = C.git_remote_push(self._remote, refspecs, push_opts)
|
||||
check_error(err)
|
||||
finally:
|
||||
self._self_handle = None
|
||||
|
||||
# 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 (*push_update_reference)(const char *ref, const char *msg, void *data)")
|
||||
def _push_update_reference_cb(ref, msg, data):
|
||||
self = ffi.from_handle(data)
|
||||
|
||||
if not hasattr(self, 'push_update_reference') or not self.push_update_reference:
|
||||
return 0
|
||||
|
||||
try:
|
||||
refname = ffi.string(ref)
|
||||
message = maybe_string(msg)
|
||||
self.push_update_reference(refname, message)
|
||||
except Exception as e:
|
||||
self._stored_exception = e
|
||||
return C.GIT_EUSER
|
||||
|
||||
return 0
|
||||
|
||||
@ffi.callback('int (*credentials)(git_cred **cred, const char *url,'
|
||||
'const char *username_from_url, unsigned int allowed_types,'
|
||||
'void *data)')
|
||||
def _credentials_cb(cred_out, url, username, allowed, data):
|
||||
self = ffi.from_handle(data)
|
||||
|
||||
if not hasattr(self, 'credentials') or not self.credentials:
|
||||
return 0
|
||||
|
||||
try:
|
||||
ccred = get_credentials(self.credentials, url, username, allowed)
|
||||
cred_out[0] = ccred[0]
|
||||
|
||||
except Exception as e:
|
||||
self._stored_exception = e
|
||||
return C.GIT_EUSER
|
||||
|
||||
return 0
|
||||
|
||||
callbacks._self_handle = None
|
||||
|
||||
def get_credentials(fn, url, username, allowed):
|
||||
"""Call fn and return the credentials object"""
|
||||
@ -438,23 +445,24 @@ def get_credentials(fn, url, username, allowed):
|
||||
|
||||
creds = fn(url_str, username_str, allowed)
|
||||
|
||||
if not hasattr(creds, 'credential_type') \
|
||||
or not hasattr(creds, 'credential_tuple'):
|
||||
credential_type = getattr(creds, 'credential_type', None)
|
||||
credential_tuple = getattr(creds, 'credential_tuple', None)
|
||||
if not credential_type or not credential_tuple:
|
||||
raise TypeError("credential does not implement interface")
|
||||
|
||||
cred_type = creds.credential_type
|
||||
cred_type = credential_type
|
||||
|
||||
if not (allowed & cred_type):
|
||||
raise TypeError("invalid credential type")
|
||||
|
||||
ccred = ffi.new('git_cred **')
|
||||
if cred_type == C.GIT_CREDTYPE_USERPASS_PLAINTEXT:
|
||||
name, passwd = creds.credential_tuple
|
||||
name, passwd = credential_tuple
|
||||
err = C.git_cred_userpass_plaintext_new(ccred, to_bytes(name),
|
||||
to_bytes(passwd))
|
||||
|
||||
elif cred_type == C.GIT_CREDTYPE_SSH_KEY:
|
||||
name, pubkey, privkey, passphrase = creds.credential_tuple
|
||||
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:
|
||||
@ -518,27 +526,30 @@ class RemoteCollection(object):
|
||||
|
||||
return Remote(self._repo, cremote[0])
|
||||
|
||||
def create(self, name, url):
|
||||
"""create(name, url) -> Remote
|
||||
def create(self, name, url, fetch=None):
|
||||
"""Create a new remote with the given name and url. Returns a <Remote>
|
||||
object.
|
||||
|
||||
Create a new remote with the given name and url.
|
||||
If 'fetch' is provided, this fetch refspec will be used instead of the default
|
||||
"""
|
||||
|
||||
cremote = ffi.new('git_remote **')
|
||||
|
||||
err = C.git_remote_create(cremote, self._repo._repo, to_bytes(name), to_bytes(url))
|
||||
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(name, new_name) -> [str]
|
||||
|
||||
Rename a remote in the configuration. The refspecs in strandard
|
||||
"""Rename a remote in the configuration. The refspecs in standard
|
||||
format will be renamed.
|
||||
|
||||
Returns a list of fetch refspecs which were not in the standard format
|
||||
and thus could not be remapped
|
||||
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:
|
||||
@ -563,3 +574,29 @@ class RemoteCollection(object):
|
||||
"""
|
||||
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)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -32,17 +32,21 @@ from __future__ import absolute_import
|
||||
from string import hexdigits
|
||||
import sys, tarfile
|
||||
from time import time
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
from cStringIO import StringIO
|
||||
else:
|
||||
from io import BytesIO as StringIO
|
||||
|
||||
import six
|
||||
|
||||
# Import from pygit2
|
||||
from _pygit2 import Repository as _Repository
|
||||
from _pygit2 import Repository as _Repository, init_file_backend
|
||||
from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN
|
||||
from _pygit2 import GIT_CHECKOUT_SAFE_CREATE, GIT_DIFF_NORMAL
|
||||
from _pygit2 import GIT_CHECKOUT_SAFE, GIT_CHECKOUT_RECREATE_MISSING, GIT_DIFF_NORMAL
|
||||
from _pygit2 import GIT_FILEMODE_LINK
|
||||
from _pygit2 import Reference, Tree, Commit, Blob
|
||||
from _pygit2 import GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE, GIT_BRANCH_ALL
|
||||
from _pygit2 import Reference, Tree, Commit, Blob, Signature
|
||||
|
||||
from .config import Config
|
||||
from .errors import check_error
|
||||
@ -51,23 +55,14 @@ from .index import Index
|
||||
from .remote import RemoteCollection
|
||||
from .blame import Blame
|
||||
from .utils import to_bytes, is_string
|
||||
from .submodule import Submodule
|
||||
|
||||
|
||||
class Repository(_Repository):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Repository, self).__init__(*args, **kwargs)
|
||||
class BaseRepository(_Repository):
|
||||
def __init__(self, backend, *args, **kwargs):
|
||||
super(BaseRepository, self).__init__(backend, *args, **kwargs)
|
||||
self._common_init()
|
||||
|
||||
@classmethod
|
||||
def _from_c(cls, ptr, owned):
|
||||
cptr = ffi.new('git_repository **')
|
||||
cptr[0] = ptr
|
||||
repo = cls.__new__(cls)
|
||||
super(cls, repo)._from_c(bytes(ffi.buffer(cptr)[:]), owned)
|
||||
repo._common_init()
|
||||
return repo
|
||||
|
||||
def _common_init(self):
|
||||
self.remotes = RemoteCollection(self)
|
||||
|
||||
@ -77,6 +72,14 @@ class Repository(_Repository):
|
||||
ffi.buffer(repo_cptr)[:] = self._pointer[:]
|
||||
self._repo = repo_cptr[0]
|
||||
|
||||
def lookup_submodule(self, path):
|
||||
csub = ffi.new('git_submodule **')
|
||||
cpath = ffi.new('char[]', to_bytes(path))
|
||||
|
||||
err = C.git_submodule_lookup(csub, self._repo, cpath)
|
||||
check_error(err)
|
||||
return Submodule._from_c(self, csub[0])
|
||||
|
||||
#
|
||||
# Mapping interface
|
||||
#
|
||||
@ -100,13 +103,10 @@ class Repository(_Repository):
|
||||
# Remotes
|
||||
#
|
||||
def create_remote(self, name, url):
|
||||
"""create_remote(name, url) -> Remote
|
||||
|
||||
Creates a new remote.
|
||||
"""Create a new remote. Return a <Remote> object.
|
||||
|
||||
This method is deprecated, please use Remote.remotes.create()
|
||||
"""
|
||||
|
||||
return self.remotes.create(name, url)
|
||||
|
||||
#
|
||||
@ -114,12 +114,12 @@ class Repository(_Repository):
|
||||
#
|
||||
@property
|
||||
def config(self):
|
||||
"""The configuration file for this repository
|
||||
"""The configuration file for this repository.
|
||||
|
||||
If a the configuration hasn't been set yet, the default config for
|
||||
repository will be returned, including global and system configurations
|
||||
(if they are available)."""
|
||||
|
||||
(if they are available).
|
||||
"""
|
||||
cconfig = ffi.new('git_config **')
|
||||
err = C.git_repository_config(cconfig, self._repo)
|
||||
check_error(err)
|
||||
@ -131,8 +131,8 @@ class Repository(_Repository):
|
||||
"""A snapshot for this repositiory's configuration
|
||||
|
||||
This allows reads over multiple values to use the same version
|
||||
of the configuration files"""
|
||||
|
||||
of the configuration files.
|
||||
"""
|
||||
cconfig = ffi.new('git_config **')
|
||||
err = C.git_repository_config_snapshot(cconfig, self._repo)
|
||||
check_error(err)
|
||||
@ -143,9 +143,8 @@ class Repository(_Repository):
|
||||
# References
|
||||
#
|
||||
def create_reference(self, name, target, force=False):
|
||||
"""
|
||||
Create a new reference "name" which points to an object or to another
|
||||
reference.
|
||||
"""Create a new reference "name" which points to an object or to
|
||||
another reference.
|
||||
|
||||
Based on the type and value of the target parameter, this method tries
|
||||
to guess whether it is a direct or a symbolic reference.
|
||||
@ -185,8 +184,8 @@ class Repository(_Repository):
|
||||
# References we need to keep to strings and so forth
|
||||
refs = []
|
||||
|
||||
# pygit2's default is SAFE_CREATE
|
||||
copts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE
|
||||
# pygit2's default is SAFE | RECREATE_MISSING
|
||||
copts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING
|
||||
# and go through the arguments to see what the user wanted
|
||||
if strategy:
|
||||
copts.checkout_strategy = strategy
|
||||
@ -230,7 +229,7 @@ class Repository(_Repository):
|
||||
Checkout the given reference using the given strategy, and update
|
||||
the HEAD.
|
||||
The reference may be a reference name or a Reference object.
|
||||
The default strategy is GIT_CHECKOUT_SAFE_CREATE.
|
||||
The default strategy is GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING.
|
||||
|
||||
To checkout from the HEAD, just pass 'HEAD'::
|
||||
|
||||
@ -246,7 +245,7 @@ class Repository(_Repository):
|
||||
the current branch will be switched to this one.
|
||||
|
||||
:param int strategy: A ``GIT_CHECKOUT_`` value. The default is
|
||||
``GIT_CHECKOUT_SAFE_CREATE``.
|
||||
``GIT_CHECKOUT_SAFE``.
|
||||
|
||||
:param str directory: Alternative checkout path to workdir.
|
||||
|
||||
@ -270,56 +269,30 @@ class Repository(_Repository):
|
||||
oid = reference.resolve().target
|
||||
treeish = self[oid]
|
||||
self.checkout_tree(treeish, **kwargs)
|
||||
head = self.lookup_reference('HEAD')
|
||||
if head.type == C.GIT_REF_SYMBOLIC:
|
||||
from_ = self.head.shorthand
|
||||
else:
|
||||
from_ = head.target.hex
|
||||
|
||||
try:
|
||||
signature = self.default_signature
|
||||
except Exception:
|
||||
signature = None
|
||||
|
||||
reflog_text = "checkout: moving from %s to %s" % (from_, reference)
|
||||
self.set_head(refname, signature, reflog_text)
|
||||
self.set_head(refname)
|
||||
|
||||
#
|
||||
# Setting HEAD
|
||||
#
|
||||
def set_head(self, target, signature=None, message=None):
|
||||
def set_head(self, target):
|
||||
"""Set HEAD to point to the given target
|
||||
|
||||
Arguments:
|
||||
|
||||
target
|
||||
The new target for HEAD. Can be a string or Oid (to detach)
|
||||
|
||||
signature
|
||||
Signature to use for the reflog. If not provided, the repository's
|
||||
default will be used
|
||||
|
||||
message
|
||||
Message to use for the reflog
|
||||
"""
|
||||
|
||||
sig_ptr = ffi.new('git_signature **')
|
||||
if signature:
|
||||
ffi.buffer(sig_ptr)[:] = signature._pointer[:]
|
||||
|
||||
message_ptr = ffi.NULL
|
||||
if message_ptr:
|
||||
message_ptr = to_bytes(message)
|
||||
|
||||
if isinstance(target, Oid):
|
||||
oid = ffi.new('git_oid *')
|
||||
ffi.buffer(oid)[:] = target.raw[:]
|
||||
err = C.git_repository_set_head_detached(self._repo, oid, sig_ptr[0], message_ptr)
|
||||
err = C.git_repository_set_head_detached(self._repo, oid)
|
||||
check_error(err)
|
||||
return
|
||||
|
||||
# if it's a string, then it's a reference name
|
||||
err = C.git_repository_set_head(self._repo, to_bytes(target), sig_ptr[0], message_ptr)
|
||||
err = C.git_repository_set_head(self._repo, to_bytes(target))
|
||||
check_error(err)
|
||||
|
||||
#
|
||||
@ -334,8 +307,23 @@ class Repository(_Repository):
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
a
|
||||
None, a str (that refers to an Object, see revparse_single()) or a
|
||||
Reference object.
|
||||
If None, b must be None, too. In this case the working directory is
|
||||
compared with the index. Otherwise the referred object is compared to
|
||||
'b'.
|
||||
|
||||
b
|
||||
None, a str (that refers to an Object, see revparse_single()) or a
|
||||
Reference object.
|
||||
If None, the working directory is compared to 'a'. (except
|
||||
'cached' is True, in which case the index is compared to 'a').
|
||||
Otherwise the referred object is compared to 'a'
|
||||
|
||||
cached
|
||||
use staged changes instead of workdir
|
||||
if 'b' is None, by default the working directory is compared to 'a'.
|
||||
If 'cached' is set to True, the index/staging area is used for comparing.
|
||||
|
||||
flag
|
||||
a GIT_DIFF_* constant
|
||||
@ -381,14 +369,12 @@ class Repository(_Repository):
|
||||
try:
|
||||
obj = obj.peel(Blob)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# And if that failed, try to get a tree, raising a type
|
||||
# error if that still doesn't work
|
||||
try:
|
||||
obj = obj.peel(Tree)
|
||||
except Exception:
|
||||
raise TypeError('unexpected "%s"' % type(obj))
|
||||
# And if that failed, try to get a tree, raising a type
|
||||
# error if that still doesn't work
|
||||
try:
|
||||
obj = obj.peel(Tree)
|
||||
except Exception:
|
||||
raise TypeError('unexpected "%s"' % type(obj))
|
||||
|
||||
return obj
|
||||
|
||||
@ -415,13 +401,12 @@ class Repository(_Repository):
|
||||
|
||||
# Case 4: Diff blob to blob
|
||||
if isinstance(a, Blob) and isinstance(b, Blob):
|
||||
raise NotImplementedError('git_diff_blob_to_blob()')
|
||||
return a.diff(b)
|
||||
|
||||
raise ValueError("Only blobs and treeish can be diffed")
|
||||
|
||||
def state_cleanup(self):
|
||||
"""
|
||||
Remove all the metadata associated with an ongoing command like
|
||||
"""Remove all the metadata associated with an ongoing command like
|
||||
merge, revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG,
|
||||
etc.
|
||||
"""
|
||||
@ -486,8 +471,7 @@ class Repository(_Repository):
|
||||
#
|
||||
@property
|
||||
def index(self):
|
||||
"""Index representing the repository's index file
|
||||
"""
|
||||
"""Index representing the repository's index file."""
|
||||
cindex = ffi.new('git_index **')
|
||||
err = C.git_repository_index(cindex, self._repo)
|
||||
check_error(err, True)
|
||||
@ -497,6 +481,68 @@ class Repository(_Repository):
|
||||
#
|
||||
# Merging
|
||||
#
|
||||
|
||||
@staticmethod
|
||||
def _merge_options(favor):
|
||||
"""Return a 'git_merge_opts *'"""
|
||||
|
||||
def favor_to_enum(favor):
|
||||
if favor == 'normal':
|
||||
return C.GIT_MERGE_FILE_FAVOR_NORMAL
|
||||
elif favor == 'ours':
|
||||
return C.GIT_MERGE_FILE_FAVOR_OURS
|
||||
elif favor == 'theirs':
|
||||
return C.GIT_MERGE_FILE_FAVOR_THEIRS
|
||||
elif favor == 'union':
|
||||
return C.GIT_MERGE_FILE_FAVOR_UNION
|
||||
else:
|
||||
return None
|
||||
|
||||
favor_val = favor_to_enum(favor)
|
||||
if favor_val is None:
|
||||
raise ValueError("unkown favor value %s" % favor)
|
||||
|
||||
opts = ffi.new('git_merge_options *')
|
||||
err = C.git_merge_init_options(opts, C.GIT_MERGE_OPTIONS_VERSION)
|
||||
check_error(err)
|
||||
|
||||
opts.file_favor = favor_val
|
||||
|
||||
return opts
|
||||
|
||||
def merge_file_from_index(self, ancestor, ours, theirs):
|
||||
"""Merge files from index. Return a string with the merge result
|
||||
containing possible conflicts.
|
||||
|
||||
ancestor
|
||||
The index entry which will be used as a common
|
||||
ancestor.
|
||||
ours
|
||||
The index entry to take as "ours" or base.
|
||||
theirs
|
||||
The index entry which will be merged into "ours"
|
||||
"""
|
||||
cmergeresult = ffi.new('git_merge_file_result *')
|
||||
|
||||
cancestor, ancestor_str_ref = (
|
||||
ancestor._to_c() if ancestor is not None else (ffi.NULL, ffi.NULL))
|
||||
cours, ours_str_ref = (
|
||||
ours._to_c() if ours is not None else (ffi.NULL, ffi.NULL))
|
||||
ctheirs, theirs_str_ref = (
|
||||
theirs._to_c() if theirs is not None else (ffi.NULL, ffi.NULL))
|
||||
|
||||
err = C.git_merge_file_from_index(
|
||||
cmergeresult, self._repo,
|
||||
cancestor, cours, ctheirs,
|
||||
ffi.NULL);
|
||||
check_error(err)
|
||||
|
||||
ret = ffi.string(cmergeresult.ptr,
|
||||
cmergeresult.len).decode('utf-8')
|
||||
C.git_merge_file_result_free(cmergeresult)
|
||||
|
||||
return ret
|
||||
|
||||
def merge_commits(self, ours, theirs, favor='normal'):
|
||||
"""Merge two arbitrary commits
|
||||
|
||||
@ -522,21 +568,9 @@ class Repository(_Repository):
|
||||
Returns an index with the result of the merge
|
||||
|
||||
"""
|
||||
def favor_to_enum(favor):
|
||||
if favor == 'normal':
|
||||
return C.GIT_MERGE_FILE_FAVOR_NORMAL
|
||||
elif favor == 'ours':
|
||||
return C.GIT_MERGE_FILE_FAVOR_OURS
|
||||
elif favor == 'theirs':
|
||||
return C.GIT_MERGE_FILE_FAVOR_THEIRS
|
||||
elif favor == 'union':
|
||||
return C.GIT_MERGE_FILE_FAVOR_UNION
|
||||
else:
|
||||
return None
|
||||
|
||||
ours_ptr = ffi.new('git_commit **')
|
||||
theirs_ptr = ffi.new('git_commit **')
|
||||
opts = ffi.new('git_merge_options *')
|
||||
cindex = ffi.new('git_index **')
|
||||
|
||||
if is_string(ours) or isinstance(ours, Oid):
|
||||
@ -547,14 +581,7 @@ class Repository(_Repository):
|
||||
ours = ours.peel(Commit)
|
||||
theirs = theirs.peel(Commit)
|
||||
|
||||
err = C.git_merge_init_options(opts, C.GIT_MERGE_OPTIONS_VERSION)
|
||||
check_error(err)
|
||||
|
||||
favor_val = favor_to_enum(favor)
|
||||
if favor_val is None:
|
||||
raise ValueError("unkown favor value %s" % favor)
|
||||
|
||||
opts.file_favor = favor_val
|
||||
opts = self._merge_options(favor)
|
||||
|
||||
ffi.buffer(ours_ptr)[:] = ours._pointer[:]
|
||||
ffi.buffer(theirs_ptr)[:] = theirs._pointer[:]
|
||||
@ -563,15 +590,260 @@ class Repository(_Repository):
|
||||
check_error(err)
|
||||
|
||||
return Index.from_c(self, cindex)
|
||||
|
||||
def merge_trees(self, ancestor, ours, theirs, favor='normal'):
|
||||
"""Merge two trees
|
||||
|
||||
Arguments:
|
||||
|
||||
ancestor
|
||||
The tree which is the common ancestor between 'ours' and 'theirs'
|
||||
ours
|
||||
The commit to take as "ours" or base.
|
||||
theirs
|
||||
The commit which will be merged into "ours"
|
||||
favor
|
||||
How to deal with file-level conflicts. Can be one of
|
||||
|
||||
* normal (default). Conflicts will be preserved.
|
||||
* ours. The "ours" side of the conflict region is used.
|
||||
* theirs. The "theirs" side of the conflict region is used.
|
||||
* union. Unique lines from each side will be used.
|
||||
|
||||
for all but NORMAL, the index will not record a conflict.
|
||||
|
||||
Returns an Index that reflects the result of the merge.
|
||||
"""
|
||||
|
||||
ancestor_ptr = ffi.new('git_tree **')
|
||||
ours_ptr = ffi.new('git_tree **')
|
||||
theirs_ptr = ffi.new('git_tree **')
|
||||
cindex = ffi.new('git_index **')
|
||||
|
||||
if is_string(ancestor) or isinstance(ancestor, Oid):
|
||||
ancestor = self[ancestor]
|
||||
if is_string(ours) or isinstance(ours, Oid):
|
||||
ours = self[ours]
|
||||
if is_string(theirs) or isinstance(theirs, Oid):
|
||||
theirs = self[theirs]
|
||||
|
||||
ancestor = ancestor.peel(Tree)
|
||||
ours = ours.peel(Tree)
|
||||
theirs = theirs.peel(Tree)
|
||||
|
||||
opts = self._merge_options(favor)
|
||||
|
||||
ffi.buffer(ancestor_ptr)[:] = ancestor._pointer[:]
|
||||
ffi.buffer(ours_ptr)[:] = ours._pointer[:]
|
||||
ffi.buffer(theirs_ptr)[:] = theirs._pointer[:]
|
||||
|
||||
err = C.git_merge_trees(cindex, self._repo, ancestor_ptr[0], ours_ptr[0], theirs_ptr[0], opts)
|
||||
check_error(err)
|
||||
|
||||
return Index.from_c(self, cindex)
|
||||
|
||||
#
|
||||
# Describe
|
||||
#
|
||||
def describe(self, committish=None, max_candidates_tags=None,
|
||||
describe_strategy=None, pattern=None,
|
||||
only_follow_first_parent=None,
|
||||
show_commit_oid_as_fallback=None, abbreviated_size=None,
|
||||
always_use_long_format=None, dirty_suffix=None):
|
||||
"""Describe a commit-ish or the current working tree.
|
||||
|
||||
:param committish: Commit-ish object or object name to describe, or
|
||||
`None` to describe the current working tree.
|
||||
:type committish: `str`, :class:`~.Reference`, or :class:`~.Commit`
|
||||
|
||||
:param int max_candidates_tags: The number of candidate tags to
|
||||
consider. Increasing above 10 will take slightly longer but may
|
||||
produce a more accurate result. A value of 0 will cause only exact
|
||||
matches to be output.
|
||||
:param int describe_strategy: A GIT_DESCRIBE_* constant.
|
||||
:param str pattern: Only consider tags matching the given `glob(7)`
|
||||
pattern, excluding the "refs/tags/" prefix.
|
||||
:param bool only_follow_first_parent: Follow only the first parent
|
||||
commit upon seeing a merge commit.
|
||||
:param bool show_commit_oid_as_fallback: Show uniquely abbreviated
|
||||
commit object as fallback.
|
||||
:param int abbreviated_size: The minimum number of hexadecimal digits
|
||||
to show for abbreviated object names. A value of 0 will suppress
|
||||
long format, only showing the closest tag.
|
||||
:param bool always_use_long_format: Always output the long format (the
|
||||
nearest tag, the number of commits, and the abbrevated commit name)
|
||||
even when the committish matches a tag.
|
||||
:param str dirty_suffix: A string to append if the working tree is
|
||||
dirty.
|
||||
|
||||
:returns: The description.
|
||||
:rtype: `str`
|
||||
|
||||
Example::
|
||||
|
||||
repo.describe(pattern='public/*', dirty_suffix='-dirty')
|
||||
"""
|
||||
|
||||
options = ffi.new('git_describe_options *')
|
||||
C.git_describe_init_options(options, C.GIT_DESCRIBE_OPTIONS_VERSION)
|
||||
|
||||
if max_candidates_tags is not None:
|
||||
options.max_candidates_tags = max_candidates_tags
|
||||
if describe_strategy is not None:
|
||||
options.describe_strategy = describe_strategy
|
||||
if pattern:
|
||||
# The returned pointer object has ownership on the allocated
|
||||
# memory. Make sure it is kept alive until git_describe_commit() or
|
||||
# git_describe_workdir() are called below.
|
||||
pattern_char = ffi.new('char[]', to_bytes(pattern))
|
||||
options.pattern = pattern_char
|
||||
if only_follow_first_parent is not None:
|
||||
options.only_follow_first_parent = only_follow_first_parent
|
||||
if show_commit_oid_as_fallback is not None:
|
||||
options.show_commit_oid_as_fallback = show_commit_oid_as_fallback
|
||||
|
||||
result = ffi.new('git_describe_result **')
|
||||
if committish:
|
||||
if is_string(committish):
|
||||
committish = self.revparse_single(committish)
|
||||
|
||||
commit = committish.peel(Commit)
|
||||
|
||||
cptr = ffi.new('git_object **')
|
||||
ffi.buffer(cptr)[:] = commit._pointer[:]
|
||||
|
||||
err = C.git_describe_commit(result, cptr[0], options)
|
||||
else:
|
||||
err = C.git_describe_workdir(result, self._repo, options)
|
||||
check_error(err)
|
||||
|
||||
try:
|
||||
format_options = ffi.new('git_describe_format_options *')
|
||||
C.git_describe_init_format_options(format_options, C.GIT_DESCRIBE_FORMAT_OPTIONS_VERSION)
|
||||
|
||||
if abbreviated_size is not None:
|
||||
format_options.abbreviated_size = abbreviated_size
|
||||
if always_use_long_format is not None:
|
||||
format_options.always_use_long_format = always_use_long_format
|
||||
dirty_ptr = None
|
||||
if dirty_suffix:
|
||||
dirty_ptr = ffi.new('char[]', to_bytes(dirty_suffix))
|
||||
format_options.dirty_suffix = dirty_ptr
|
||||
|
||||
buf = ffi.new('git_buf *', (ffi.NULL, 0))
|
||||
|
||||
err = C.git_describe_format(buf, result[0], format_options)
|
||||
check_error(err)
|
||||
|
||||
try:
|
||||
return ffi.string(buf.ptr).decode('utf-8')
|
||||
finally:
|
||||
C.git_buf_free(buf)
|
||||
finally:
|
||||
C.git_describe_result_free(result[0])
|
||||
|
||||
#
|
||||
# Stash
|
||||
#
|
||||
def stash(self, stasher, message=None, keep_index=False,
|
||||
include_untracked=False, include_ignored=False):
|
||||
"""Save changes to the working directory to the stash.
|
||||
|
||||
:param Signature stasher: The identity of the person doing the stashing.
|
||||
:param str message: An optional description of stashed state.
|
||||
:param bool keep_index: Leave changes already added to the index
|
||||
in the working directory.
|
||||
:param bool include_untracked: Also stash untracked files.
|
||||
:param bool include_ignored: Also stash ignored files.
|
||||
|
||||
:returns: The Oid of the stash merge commit.
|
||||
:rtype: Oid
|
||||
|
||||
Example::
|
||||
|
||||
>>> repo = pygit2.Repsitory('.')
|
||||
>>> repo.stash(repo.default_signature(), 'WIP: stashing')
|
||||
"""
|
||||
|
||||
if message is not None:
|
||||
stash_msg = ffi.new('char[]', to_bytes(message)) if message else ffi.NULL
|
||||
else:
|
||||
stash_msg = ffi.NULL
|
||||
|
||||
flags = 0
|
||||
flags |= keep_index * C.GIT_STASH_KEEP_INDEX
|
||||
flags |= include_untracked * C.GIT_STASH_INCLUDE_UNTRACKED
|
||||
flags |= include_ignored * C.GIT_STASH_INCLUDE_IGNORED
|
||||
|
||||
stasher_cptr = ffi.new('git_signature **')
|
||||
ffi.buffer(stasher_cptr)[:] = stasher._pointer[:]
|
||||
|
||||
coid = ffi.new('git_oid *')
|
||||
err = C.git_stash_save(coid, self._repo, stasher_cptr[0], stash_msg, flags)
|
||||
check_error(err)
|
||||
|
||||
return Oid(raw=bytes(ffi.buffer(coid)[:]))
|
||||
|
||||
@staticmethod
|
||||
def _stash_args_to_options(reinstate_index=False, **kwargs):
|
||||
stash_opts = ffi.new('git_stash_apply_options *')
|
||||
check_error(C.git_stash_apply_init_options(stash_opts, 1))
|
||||
|
||||
flags = reinstate_index * C.GIT_STASH_APPLY_REINSTATE_INDEX
|
||||
stash_opts.flags = flags
|
||||
|
||||
copts, refs = Repository._checkout_args_to_options(**kwargs)
|
||||
stash_opts.checkout_options = copts[0]
|
||||
|
||||
return stash_opts
|
||||
|
||||
def stash_apply(self, index=0, **kwargs):
|
||||
"""Apply a stashed state in the stash list to the working directory.
|
||||
|
||||
:param int index: The position within the stash list of the stash to apply.
|
||||
0 is the most recent stash.
|
||||
:param bool reinstate_index: Try to reinstate stashed changes to the index.
|
||||
|
||||
The checkout options may be customized using the same arguments taken by
|
||||
Repository.checkout().
|
||||
|
||||
Example::
|
||||
|
||||
>>> repo = pygit2.Repsitory('.')
|
||||
>>> repo.stash(repo.default_signature(), 'WIP: stashing')
|
||||
>>> repo.stash_apply(strategy=GIT_CHECKOUT_ALLOW_CONFLICTS)
|
||||
"""
|
||||
stash_opts = Repository._stash_args_to_options(**kwargs)
|
||||
check_error(C.git_stash_apply(self._repo, index, stash_opts))
|
||||
|
||||
def stash_drop(self, index=0):
|
||||
"""Remove a stashed state from the stash list.
|
||||
|
||||
:param int index: The position within the stash list of the stash to remove.
|
||||
0 is the most recent stash.
|
||||
"""
|
||||
check_error(C.git_stash_drop(self._repo, index))
|
||||
|
||||
def stash_pop(self, index=0, **kwargs):
|
||||
"""Apply a stashed state and remove it from the stash list.
|
||||
|
||||
For arguments, see Repository.stash_apply().
|
||||
"""
|
||||
stash_opts = Repository._stash_args_to_options(**kwargs)
|
||||
check_error(C.git_stash_pop(self._repo, index, stash_opts))
|
||||
|
||||
#
|
||||
# Utility for writing a tree into an archive
|
||||
#
|
||||
def write_archive(self, treeish, archive, timestamp=None):
|
||||
def write_archive(self, treeish, archive, timestamp=None, prefix=''):
|
||||
"""Write treeish into an archive
|
||||
|
||||
If no timestamp is provided and 'treeish' is a commit, its committer
|
||||
timestamp will be used. Otherwise the current time will be used.
|
||||
|
||||
All path names in the archive are added to 'prefix', which defaults to
|
||||
an empty string.
|
||||
|
||||
Arguments:
|
||||
|
||||
treeish
|
||||
@ -580,13 +852,15 @@ class Repository(_Repository):
|
||||
An archive from the 'tarfile' module
|
||||
timestamp
|
||||
Timestamp to use for the files in the archive.
|
||||
prefix
|
||||
Extra prefix to add to the path names in the archive.
|
||||
|
||||
Example::
|
||||
|
||||
>>> import tarfile, pygit2
|
||||
>>>> with tarfile.open('foo.tar', 'w') as archive:
|
||||
>>>> repo = pygit2.Repsitory('.')
|
||||
>>>> repo.write_archive(archive, repo.head.target)
|
||||
>>>> repo.write_archive(repo.head.target, archive)
|
||||
"""
|
||||
|
||||
# Try to get a tree form whatever we got
|
||||
@ -615,13 +889,241 @@ class Repository(_Repository):
|
||||
|
||||
for entry in index:
|
||||
content = self[entry.id].read_raw()
|
||||
info = tarfile.TarInfo(entry.path)
|
||||
info = tarfile.TarInfo(prefix + entry.path)
|
||||
info.size = len(content)
|
||||
info.mtime = timestamp
|
||||
info.uname = info.gname = 'root' # just because git does this
|
||||
info.uname = info.gname = 'root' # just because git does this
|
||||
if entry.mode == GIT_FILEMODE_LINK:
|
||||
info.type = archive.SYMTYPE
|
||||
info.linkname = content
|
||||
info.mode = 0o777 # symlinks get placeholder
|
||||
info.type = tarfile.SYMTYPE
|
||||
info.linkname = content.decode("utf-8")
|
||||
info.mode = 0o777 # symlinks get placeholder
|
||||
info.size = 0
|
||||
archive.addfile(info)
|
||||
else:
|
||||
archive.addfile(info, StringIO(content))
|
||||
|
||||
archive.addfile(info, StringIO(content))
|
||||
#
|
||||
# Ahead-behind, which mostly lives on its own namespace
|
||||
#
|
||||
def ahead_behind(self, local, upstream):
|
||||
"""Calculate how many different commits are in the non-common parts
|
||||
of the history between the two given ids.
|
||||
|
||||
Ahead is how many commits are in the ancestry of the 'local'
|
||||
commit which are not in the 'upstream' commit. Behind is the
|
||||
opposite.
|
||||
|
||||
Arguments
|
||||
|
||||
local
|
||||
The commit which is considered the local or current state
|
||||
upstream
|
||||
The commit which is considered the upstream
|
||||
|
||||
Returns a tuple of two integers with the number of commits ahead and
|
||||
behind respectively.
|
||||
"""
|
||||
|
||||
if not isinstance(local, Oid):
|
||||
local = self.expand_id(local)
|
||||
|
||||
if not isinstance(upstream, Oid):
|
||||
upstream = self.expand_id(upstream)
|
||||
|
||||
ahead, behind = ffi.new('size_t*'), ffi.new('size_t*')
|
||||
oid1, oid2 = ffi.new('git_oid *'), ffi.new('git_oid *')
|
||||
ffi.buffer(oid1)[:] = local.raw[:]
|
||||
ffi.buffer(oid2)[:] = upstream.raw[:]
|
||||
err = C.git_graph_ahead_behind(ahead, behind, self._repo, oid1, oid2)
|
||||
check_error(err)
|
||||
|
||||
return int(ahead[0]), int(behind[0])
|
||||
|
||||
#
|
||||
# Git attributes
|
||||
#
|
||||
def get_attr(self, path, name, flags=0):
|
||||
"""Retrieve an attribute for a file by path
|
||||
|
||||
Arguments
|
||||
|
||||
path
|
||||
The path of the file to look up attributes for, relative to the
|
||||
workdir root
|
||||
name
|
||||
The name of the attribute to look up
|
||||
flags
|
||||
A combination of GIT_ATTR_CHECK_ flags which determine the
|
||||
lookup order
|
||||
|
||||
Returns either a boolean, None (if the value is unspecified) or string
|
||||
with the value of the attribute.
|
||||
"""
|
||||
|
||||
cvalue = ffi.new('char **')
|
||||
err = C.git_attr_get(cvalue, self._repo, flags, to_bytes(path), to_bytes(name))
|
||||
check_error(err)
|
||||
|
||||
# Now let's see if we can figure out what the value is
|
||||
attr_kind = C.git_attr_value(cvalue[0])
|
||||
if attr_kind == C.GIT_ATTR_UNSPECIFIED_T:
|
||||
return None
|
||||
elif attr_kind == C.GIT_ATTR_TRUE_T:
|
||||
return True
|
||||
elif attr_kind == C.GIT_ATTR_FALSE_T:
|
||||
return False
|
||||
elif attr_kind == C.GIT_ATTR_VALUE_T:
|
||||
return ffi.string(cvalue[0]).decode('utf-8')
|
||||
|
||||
assert False, "the attribute value from libgit2 is invalid"
|
||||
|
||||
#
|
||||
# Identity for reference operations
|
||||
#
|
||||
@property
|
||||
def ident(self):
|
||||
cname = ffi.new('char **')
|
||||
cemail = ffi.new('char **')
|
||||
|
||||
err = C.git_repository_ident(cname, cemail, self._repo)
|
||||
check_error(err)
|
||||
|
||||
return (ffi.string(cname).decode('utf-8'), ffi.string(cemail).decode('utf-8'))
|
||||
|
||||
def set_ident(self, name, email):
|
||||
"""Set the identity to be used for reference operations
|
||||
|
||||
Updates to some references also append data to their
|
||||
reflog. You can use this method to set what identity will be
|
||||
used. If none is set, it will be read from the configuration.
|
||||
"""
|
||||
|
||||
err = C.git_repository_set_ident(self._repo, to_bytes(name), to_bytes(email))
|
||||
check_error(err)
|
||||
|
||||
def revert_commit(self, revert_commit, our_commit, mainline=0):
|
||||
"""Reverts the given Commit against the given "our" Commit,
|
||||
producing an Index that reflects the result of the revert.
|
||||
|
||||
Arguments
|
||||
|
||||
revert_commit
|
||||
The Commit to revert
|
||||
our_commit
|
||||
The Commit to revert against (eg, HEAD)
|
||||
mainline
|
||||
The parent of the revert Commit, if it is a merge (i.e. 1, 2)
|
||||
|
||||
Returns an Index with the result of the revert.
|
||||
"""
|
||||
cindex = ffi.new('git_index **')
|
||||
revert_commit_ptr = ffi.new('git_commit **')
|
||||
our_commit_ptr = ffi.new('git_commit **')
|
||||
|
||||
ffi.buffer(revert_commit_ptr)[:] = revert_commit._pointer[:]
|
||||
ffi.buffer(our_commit_ptr)[:] = our_commit._pointer[:]
|
||||
|
||||
opts = ffi.new('git_merge_options *')
|
||||
err = C.git_merge_init_options(opts, C.GIT_MERGE_OPTIONS_VERSION)
|
||||
check_error(err)
|
||||
|
||||
err = C.git_revert_commit(
|
||||
cindex, self._repo, revert_commit_ptr[0], our_commit_ptr[0], mainline, opts
|
||||
)
|
||||
check_error(err)
|
||||
|
||||
return Index.from_c(self, cindex)
|
||||
|
||||
|
||||
class Branches(object):
|
||||
def __init__(self, repository, flag=GIT_BRANCH_ALL):
|
||||
self._repository = repository
|
||||
self._flag = flag
|
||||
|
||||
if flag == GIT_BRANCH_ALL:
|
||||
self.local = Branches(repository, flag=GIT_BRANCH_LOCAL)
|
||||
self.remote = Branches(repository, flag=GIT_BRANCH_REMOTE)
|
||||
|
||||
def __getitem__(self, name):
|
||||
branch = None
|
||||
if self._flag & GIT_BRANCH_LOCAL:
|
||||
branch = self._repository.lookup_branch(name, GIT_BRANCH_LOCAL)
|
||||
|
||||
if branch is None and self._flag & GIT_BRANCH_REMOTE:
|
||||
branch = self._repository.lookup_branch(name, GIT_BRANCH_REMOTE)
|
||||
|
||||
if branch is None:
|
||||
raise KeyError('Branch not found: {}'.format(name))
|
||||
|
||||
return branch
|
||||
|
||||
def get(self, key):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def __iter__(self):
|
||||
for branch_name in self._repository.listall_branches(self._flag):
|
||||
yield branch_name
|
||||
|
||||
def create(self, name, commit, force=False):
|
||||
return self._repository.create_branch(name, commit, force)
|
||||
|
||||
def delete(self, name):
|
||||
self[name].delete()
|
||||
|
||||
def __contains__(self, name):
|
||||
return self.get(name) is not None
|
||||
|
||||
|
||||
class References(object):
|
||||
def __init__(self, repository):
|
||||
self._repository = repository
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self._repository.lookup_reference(name)
|
||||
|
||||
def get(self, key):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def __iter__(self):
|
||||
for ref_name in self._repository.listall_references():
|
||||
yield ref_name
|
||||
|
||||
def create(self, name, target, force=False):
|
||||
return self._repository.create_reference(name, target, force)
|
||||
|
||||
def delete(self, name):
|
||||
self[name].delete()
|
||||
|
||||
def __contains__(self, name):
|
||||
return self.get(name) is not None
|
||||
|
||||
@property
|
||||
def objects(self):
|
||||
return self._repository.listall_reference_objects()
|
||||
|
||||
|
||||
class Repository(BaseRepository):
|
||||
def __init__(self, path, *args, **kwargs):
|
||||
if not isinstance(path, six.string_types):
|
||||
path = path.decode('utf-8')
|
||||
|
||||
path_backend = init_file_backend(path)
|
||||
super(Repository, self).__init__(backend=path_backend, *args, **kwargs)
|
||||
|
||||
self.branches = Branches(self)
|
||||
self.references = References(self)
|
||||
|
||||
@classmethod
|
||||
def _from_c(cls, ptr, owned):
|
||||
cptr = ffi.new('git_repository **')
|
||||
cptr[0] = ptr
|
||||
repo = cls.__new__(cls)
|
||||
super(cls, repo)._from_c(bytes(ffi.buffer(cptr)[:]), owned)
|
||||
repo._common_init()
|
||||
return repo
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -28,6 +28,11 @@
|
||||
from _pygit2 import option
|
||||
from _pygit2 import GIT_OPT_GET_SEARCH_PATH, GIT_OPT_SET_SEARCH_PATH
|
||||
from _pygit2 import GIT_OPT_GET_MWINDOW_SIZE, GIT_OPT_SET_MWINDOW_SIZE
|
||||
from _pygit2 import GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, GIT_OPT_SET_MWINDOW_MAPPED_LIMIT
|
||||
from _pygit2 import GIT_OPT_SET_CACHE_OBJECT_LIMIT
|
||||
from _pygit2 import GIT_OPT_GET_CACHED_MEMORY
|
||||
from _pygit2 import GIT_OPT_ENABLE_CACHING
|
||||
from _pygit2 import GIT_OPT_SET_CACHE_MAX_SIZE
|
||||
|
||||
|
||||
class SearchPathList(object):
|
||||
@ -64,3 +69,36 @@ class Settings(object):
|
||||
@mwindow_size.setter
|
||||
def mwindow_size(self, value):
|
||||
option(GIT_OPT_SET_MWINDOW_SIZE, value)
|
||||
|
||||
@property
|
||||
def mwindow_mapped_limit(self):
|
||||
"""Mwindow mapped limit"""
|
||||
return option(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT)
|
||||
|
||||
@mwindow_mapped_limit.setter
|
||||
def mwindow_mapped_limit(self, value):
|
||||
"""Mwindow mapped limit"""
|
||||
return option(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, value)
|
||||
|
||||
@property
|
||||
def cached_memory(self):
|
||||
"""Maximum mmap window size"""
|
||||
return option(GIT_OPT_GET_CACHED_MEMORY)
|
||||
|
||||
def enable_caching(self, value=True):
|
||||
return option(GIT_OPT_ENABLE_CACHING, value)
|
||||
|
||||
def cache_max_size(self, value):
|
||||
return option(GIT_OPT_SET_CACHE_MAX_SIZE, value)
|
||||
|
||||
def cache_object_limit(self, object_type, value):
|
||||
"""Set the maximum data size for the given type of object to be
|
||||
considered eligible for caching in memory.
|
||||
|
||||
Setting to value to zero means that that type of object will not
|
||||
be cached. Defaults to 0 for GIT_OBJ_BLOB (i.e. won't cache
|
||||
blobs) and 4k for GIT_OBJ_COMMIT, GIT_OBJ_TREE, and GIT_OBJ_TAG.
|
||||
"""
|
||||
return option(GIT_OPT_SET_CACHE_OBJECT_LIMIT, object_type, value)
|
||||
|
||||
|
||||
|
79
pygit2/submodule.py
Normal file
79
pygit2/submodule.py
Normal file
@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# In addition to the permissions in the GNU General Public License,
|
||||
# the authors give you unlimited permission to link the compiled
|
||||
# version of this file into combinations with other programs,
|
||||
# and to distribute those combinations without any restriction
|
||||
# coming from the use of this file. (The General Public License
|
||||
# restrictions do apply in other respects; for example, they cover
|
||||
# modification of the file, and distribution when not linked into
|
||||
# a combined executable.)
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
# Import from the future
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .errors import check_error
|
||||
from .ffi import ffi, C
|
||||
|
||||
class Submodule(object):
|
||||
|
||||
@classmethod
|
||||
def _from_c(cls, repo, cptr):
|
||||
subm = cls.__new__(cls)
|
||||
|
||||
subm._repo = repo
|
||||
subm._subm = cptr
|
||||
|
||||
return subm
|
||||
|
||||
def __del__(self):
|
||||
C.git_submodule_free(self._subm)
|
||||
|
||||
def open(self):
|
||||
"""Open the repository for a submodule."""
|
||||
crepo = ffi.new('git_repository **')
|
||||
err = C.git_submodule_open(crepo, self._subm)
|
||||
check_error(err)
|
||||
|
||||
return self._repo._from_c(crepo[0], True)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Name of the submodule."""
|
||||
name = C.git_submodule_name(self._subm)
|
||||
return ffi.string(name).decode('utf-8')
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""Path of the submodule."""
|
||||
path = C.git_submodule_path(self._subm)
|
||||
return ffi.string(path).decode('utf-8')
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
"""URL of the submodule."""
|
||||
url = C.git_submodule_url(self._subm)
|
||||
return ffi.string(url).decode('utf-8')
|
||||
|
||||
@property
|
||||
def branch(self):
|
||||
"""Branch that is to be tracked by the submodule."""
|
||||
branch = C.git_submodule_branch(self._subm)
|
||||
return ffi.string(branch).decode('utf-8')
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -50,34 +50,62 @@ def strarray_to_strings(arr):
|
||||
return l
|
||||
|
||||
|
||||
def strings_to_strarray(l):
|
||||
"""Convert a list of strings to a git_strarray
|
||||
class StrArray(object):
|
||||
"""A git_strarray wrapper
|
||||
|
||||
We return first the git_strarray* you can pass to libgit2 and a
|
||||
list of references to the memory, which we must keep around for as
|
||||
long as the git_strarray must live.
|
||||
Use this in order to get a git_strarray* to pass to libgit2 out of a
|
||||
list of strings. This has a context manager, which you should use, e.g.
|
||||
|
||||
with StrArray(list_of_strings) as arr:
|
||||
C.git_function_that_takes_strarray(arr)
|
||||
"""
|
||||
|
||||
if not isinstance(l, list):
|
||||
raise TypeError("Value must be a list")
|
||||
def __init__(self, l):
|
||||
# Allow passing in None as lg2 typically considers them the same as empty
|
||||
if l is None:
|
||||
self.array = ffi.NULL
|
||||
return
|
||||
|
||||
arr = ffi.new('git_strarray *')
|
||||
strings = ffi.new('char *[]', len(l))
|
||||
if not isinstance(l, list):
|
||||
raise TypeError("Value must be a list")
|
||||
|
||||
# We need refs in order to keep a reference to the value returned
|
||||
# by the ffi.new(). Otherwise, they will be freed and the memory
|
||||
# re-used, with less than great consequences.
|
||||
refs = [None] * len(l)
|
||||
strings = [None] * len(l)
|
||||
for i in range(len(l)):
|
||||
if not is_string(l[i]):
|
||||
raise TypeError("Value must be a string")
|
||||
|
||||
for i in range(len(l)):
|
||||
if not is_string(l[i]):
|
||||
raise TypeError("Value must be a string")
|
||||
strings[i] = ffi.new('char []', to_bytes(l[i]))
|
||||
|
||||
s = ffi.new('char []', to_bytes(l[i]))
|
||||
refs[i] = s
|
||||
strings[i] = s
|
||||
self._arr = ffi.new('char *[]', strings)
|
||||
self._strings = strings
|
||||
self.array = ffi.new('git_strarray *', [self._arr, len(strings)])
|
||||
|
||||
arr.strings = strings
|
||||
arr.count = len(l)
|
||||
def __enter__(self):
|
||||
return self.array
|
||||
|
||||
return arr, refs
|
||||
def __exit__(self, type, value, traceback):
|
||||
pass
|
||||
|
||||
|
||||
class GenericIterator(object):
|
||||
"""Helper to easily implement an iterator.
|
||||
|
||||
The constructor gets a container which must implement __len__ and
|
||||
__getitem__
|
||||
"""
|
||||
|
||||
def __init__(self, container):
|
||||
self.container = container
|
||||
self.length = len(container)
|
||||
self.idx = 0
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
def __next__(self):
|
||||
idx = self.idx
|
||||
if idx >= self.length:
|
||||
raise StopIteration
|
||||
|
||||
self.idx += 1
|
||||
return self.container[idx]
|
||||
|
129
setup.py
129
setup.py
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# coding: UTF-8
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -45,22 +45,26 @@ 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
|
||||
sys.path.insert(0, 'pygit2')
|
||||
from _utils import __version__, get_libgit2_paths, get_ffi
|
||||
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]
|
||||
|
||||
# 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()
|
||||
|
||||
pygit2_exts = [os.path.join('src', name) for name in listdir('src')
|
||||
pygit2_exts = [os.path.join('src', name) for name in sorted(listdir('src'))
|
||||
if name.endswith('.c')]
|
||||
|
||||
|
||||
@ -95,19 +99,43 @@ class TestCommand(Command):
|
||||
unittest.main(None, defaultTest='test.test_suite', argv=test_argv)
|
||||
|
||||
|
||||
class CFFIBuild(build):
|
||||
"""Hack to combat the chicken and egg problem that we need cffi
|
||||
to add cffi as an extension.
|
||||
"""
|
||||
def finalize_options(self):
|
||||
ffi, C = get_ffi()
|
||||
self.distribution.ext_modules.append(ffi.verifier.get_extension())
|
||||
build.finalize_options(self)
|
||||
class sdist_files_from_git(sdist):
|
||||
def get_file_list(self):
|
||||
popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE,
|
||||
universal_newlines=True)
|
||||
stdoutdata, stderrdata = popen.communicate()
|
||||
if popen.returncode != 0:
|
||||
print(stderrdata)
|
||||
sys.exit()
|
||||
|
||||
for line in stdoutdata.splitlines():
|
||||
# Skip hidden files at the root
|
||||
if line[0] == '.':
|
||||
continue
|
||||
self.filelist.append(line)
|
||||
|
||||
# Ok
|
||||
self.filelist.sort()
|
||||
self.filelist.remove_duplicates()
|
||||
self.write_manifest()
|
||||
|
||||
|
||||
class BuildWithDLLs(CFFIBuild):
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"Topic :: Software Development :: Version Control"]
|
||||
|
||||
# On Windows, we install the git2.dll too.
|
||||
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 = []
|
||||
@ -133,45 +161,27 @@ class BuildWithDLLs(CFFIBuild):
|
||||
|
||||
def run(self):
|
||||
build.run(self)
|
||||
# On Windows we package up the dlls with the plugin.
|
||||
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
|
||||
|
||||
class sdist_files_from_git(sdist):
|
||||
def get_file_list(self):
|
||||
popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE)
|
||||
stdoutdata, stderrdata = popen.communicate()
|
||||
if popen.returncode != 0:
|
||||
print(stderrdata)
|
||||
sys.exit()
|
||||
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
|
||||
],
|
||||
}
|
||||
|
||||
for line in stdoutdata.splitlines():
|
||||
# Skip hidden files at the root
|
||||
if line[0] == '.':
|
||||
continue
|
||||
self.filelist.append(line)
|
||||
if cffi_major_version == 0:
|
||||
extra_args['ext_modules'].append(ffi.verifier.get_extension())
|
||||
else:
|
||||
extra_args['cffi_modules'] = ['pygit2/_run.py:ffi']
|
||||
|
||||
# Ok
|
||||
self.filelist.sort()
|
||||
self.filelist.remove_duplicates()
|
||||
self.write_manifest()
|
||||
|
||||
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 = {
|
||||
'build': BuildWithDLLs if os.name == 'nt' else CFFIBuild,
|
||||
'test': TestCommand,
|
||||
'sdist': sdist_files_from_git,
|
||||
}
|
||||
|
||||
setup(name='pygit2',
|
||||
description='Python bindings for libgit2.',
|
||||
@ -180,18 +190,13 @@ setup(name='pygit2',
|
||||
url='http://github.com/libgit2/pygit2',
|
||||
classifiers=classifiers,
|
||||
license='GPLv2 with linking exception',
|
||||
maintainer=u('J. David Ibáñez'),
|
||||
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'],
|
||||
install_requires=['cffi', 'six'],
|
||||
zip_safe=False,
|
||||
ext_modules=[
|
||||
Extension('_pygit2', pygit2_exts, libraries=['git2'],
|
||||
include_dirs=[libgit2_include],
|
||||
library_dirs=[libgit2_lib]),
|
||||
# FFI is added in the build step
|
||||
],
|
||||
cmdclass=cmdclass)
|
||||
cmdclass=cmdclass,
|
||||
**extra_args)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -27,11 +27,12 @@
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include "blob.h"
|
||||
#include "diff.h"
|
||||
#include "error.h"
|
||||
#include "utils.h"
|
||||
#include "object.h"
|
||||
#include "blob.h"
|
||||
#include "patch.h"
|
||||
#include "utils.h"
|
||||
|
||||
extern PyObject *GitError;
|
||||
|
||||
@ -132,7 +133,7 @@ PyDoc_STRVAR(Blob_size__doc__, "Size.");
|
||||
PyObject *
|
||||
Blob_size__get__(Blob *self)
|
||||
{
|
||||
return PyLong_FromLongLong(git_blob_rawsize(self->blob));
|
||||
return PyInt_FromLongLong(git_blob_rawsize(self->blob));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
27
src/branch.c
27
src/branch.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -81,6 +81,28 @@ Branch_is_head(Branch *self)
|
||||
return Error_set(err);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Branch_is_checked_out__doc__,
|
||||
"is_checked_out()\n"
|
||||
"\n"
|
||||
"True if branch is checked out by any repo connected to the current one, "
|
||||
" False otherwise.");
|
||||
|
||||
PyObject *
|
||||
Branch_is_checked_out(Branch *self)
|
||||
{
|
||||
int err;
|
||||
|
||||
CHECK_REFERENCE(self);
|
||||
|
||||
err = git_branch_is_checked_out(self->reference);
|
||||
if (err == 1)
|
||||
Py_RETURN_TRUE;
|
||||
else if (err == 0)
|
||||
Py_RETURN_FALSE;
|
||||
else
|
||||
return Error_set(err);
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(Branch_rename__doc__,
|
||||
"rename(name, force=False)\n"
|
||||
@ -101,7 +123,7 @@ Branch_rename(Branch *self, PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, "s|i", &c_name, &force))
|
||||
return NULL;
|
||||
|
||||
err = git_branch_move(&c_out, self->reference, c_name, force, NULL, NULL);
|
||||
err = git_branch_move(&c_out, self->reference, c_name, force);
|
||||
if (err == GIT_OK)
|
||||
return wrap_branch(c_out, self->repo);
|
||||
else
|
||||
@ -234,6 +256,7 @@ Branch_upstream_name__get__(Branch *self)
|
||||
PyMethodDef Branch_methods[] = {
|
||||
METHOD(Branch, delete, METH_NOARGS),
|
||||
METHOD(Branch, is_head, METH_NOARGS),
|
||||
METHOD(Branch, is_checked_out, METH_NOARGS),
|
||||
METHOD(Branch, rename, METH_VARARGS),
|
||||
{NULL}
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -34,6 +34,7 @@
|
||||
|
||||
PyObject* Branch_delete(Branch *self, PyObject *args);
|
||||
PyObject* Branch_is_head(Branch *self);
|
||||
PyObject* Branch_is_checked_out(Branch *self);
|
||||
PyObject* Branch_move(Branch *self, PyObject *args);
|
||||
|
||||
PyObject* wrap_branch(git_reference *c_reference, Repository *repo);
|
||||
|
14
src/commit.c
14
src/commit.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -35,6 +35,7 @@
|
||||
#include "oid.h"
|
||||
|
||||
extern PyTypeObject TreeType;
|
||||
extern PyObject *GitError;
|
||||
|
||||
|
||||
PyDoc_STRVAR(Commit_message_encoding__doc__, "Message encoding.");
|
||||
@ -79,7 +80,7 @@ PyDoc_STRVAR(Commit_commit_time__doc__, "Commit time.");
|
||||
PyObject *
|
||||
Commit_commit_time__get__(Commit *commit)
|
||||
{
|
||||
return PyLong_FromLongLong(git_commit_time(commit->commit));
|
||||
return PyInt_FromLongLong(git_commit_time(commit->commit));
|
||||
}
|
||||
|
||||
|
||||
@ -88,7 +89,7 @@ PyDoc_STRVAR(Commit_commit_time_offset__doc__, "Commit time offset.");
|
||||
PyObject *
|
||||
Commit_commit_time_offset__get__(Commit *commit)
|
||||
{
|
||||
return PyLong_FromLong(git_commit_time_offset(commit->commit));
|
||||
return PyInt_FromLong(git_commit_time_offset(commit->commit));
|
||||
}
|
||||
|
||||
|
||||
@ -131,8 +132,11 @@ Commit_tree__get__(Commit *commit)
|
||||
int err;
|
||||
|
||||
err = git_commit_tree(&tree, commit->commit);
|
||||
if (err == GIT_ENOTFOUND)
|
||||
Py_RETURN_NONE;
|
||||
if (err == GIT_ENOTFOUND) {
|
||||
char tree_id[GIT_OID_HEXSZ + 1] = { 0 };
|
||||
git_oid_fmt(tree_id, git_commit_tree_id(commit->commit));
|
||||
return PyErr_Format(GitError, "Unable to read tree %s", tree_id);
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
598
src/diff.c
598
src/diff.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -28,22 +28,25 @@
|
||||
#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 "oid.h"
|
||||
#include "diff.h"
|
||||
|
||||
extern PyObject *GitError;
|
||||
|
||||
extern PyTypeObject TreeType;
|
||||
extern PyTypeObject IndexType;
|
||||
extern PyTypeObject DiffType;
|
||||
extern PyTypeObject HunkType;
|
||||
extern PyTypeObject DiffDeltaType;
|
||||
extern PyTypeObject DiffFileType;
|
||||
extern PyTypeObject DiffHunkType;
|
||||
extern PyTypeObject DiffLineType;
|
||||
extern PyTypeObject DiffStatsType;
|
||||
extern PyTypeObject RepositoryType;
|
||||
|
||||
PyTypeObject PatchType;
|
||||
|
||||
PyObject *
|
||||
wrap_diff(git_diff *diff, Repository *repo)
|
||||
{
|
||||
@ -60,141 +63,155 @@ wrap_diff(git_diff *diff, Repository *repo)
|
||||
}
|
||||
|
||||
PyObject *
|
||||
wrap_patch(git_patch *patch)
|
||||
wrap_diff_file(const git_diff_file *file)
|
||||
{
|
||||
Patch *py_patch;
|
||||
DiffFile *py_file;
|
||||
|
||||
if (!patch)
|
||||
if (!file)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
py_patch = PyObject_New(Patch, &PatchType);
|
||||
if (py_patch) {
|
||||
size_t i, j, hunk_amounts, lines_in_hunk, additions, deletions;
|
||||
const git_diff_delta *delta;
|
||||
const git_diff_hunk *hunk;
|
||||
const git_diff_line *line;
|
||||
int err;
|
||||
|
||||
delta = git_patch_get_delta(patch);
|
||||
|
||||
py_patch->old_file_path = strdup(delta->old_file.path);
|
||||
py_patch->new_file_path = strdup(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_to_python(&delta->old_file.id);
|
||||
py_patch->new_id = git_oid_to_python(&delta->new_file.id);
|
||||
|
||||
git_patch_line_stats(NULL, &additions, &deletions, patch);
|
||||
py_patch->additions = additions;
|
||||
py_patch->deletions = deletions;
|
||||
|
||||
hunk_amounts = git_patch_num_hunks(patch);
|
||||
py_patch->hunks = PyList_New(hunk_amounts);
|
||||
for (i = 0; i < hunk_amounts; ++i) {
|
||||
Hunk *py_hunk = NULL;
|
||||
|
||||
err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, i);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
py_hunk = PyObject_New(Hunk, &HunkType);
|
||||
if (py_hunk != NULL) {
|
||||
py_hunk->old_start = hunk->old_start;
|
||||
py_hunk->old_lines = hunk->old_lines;
|
||||
py_hunk->new_start = hunk->new_start;
|
||||
py_hunk->new_lines = hunk->new_lines;
|
||||
|
||||
py_hunk->lines = PyList_New(lines_in_hunk);
|
||||
for (j = 0; j < lines_in_hunk; ++j) {
|
||||
PyObject *py_line_origin = NULL, *py_line = NULL;
|
||||
|
||||
err = git_patch_get_line_in_hunk(&line, patch, i, j);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
py_line_origin = to_unicode_n(&line->origin, 1,
|
||||
NULL, NULL);
|
||||
py_line = to_unicode_n(line->content, line->content_len,
|
||||
NULL, NULL);
|
||||
PyList_SetItem(py_hunk->lines, j,
|
||||
Py_BuildValue("OO", py_line_origin, py_line));
|
||||
|
||||
Py_DECREF(py_line_origin);
|
||||
Py_DECREF(py_line);
|
||||
}
|
||||
|
||||
PyList_SetItem((PyObject*) py_patch->hunks, i,
|
||||
(PyObject*) py_hunk);
|
||||
}
|
||||
}
|
||||
py_file = PyObject_New(DiffFile, &DiffFileType);
|
||||
if (py_file) {
|
||||
py_file->id = git_oid_to_python(&file->id);
|
||||
py_file->path = file->path != NULL ? strdup(file->path) : NULL;
|
||||
py_file->size = file->size;
|
||||
py_file->flags = file->flags;
|
||||
py_file->mode = file->mode;
|
||||
}
|
||||
git_patch_free(patch);
|
||||
|
||||
return (PyObject*) py_patch;
|
||||
return (PyObject *) py_file;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
diff_get_patch_byindex(git_diff *diff, size_t idx)
|
||||
wrap_diff_delta(const git_diff_delta *delta)
|
||||
{
|
||||
git_patch *patch = NULL;
|
||||
DiffDelta *py_delta;
|
||||
|
||||
if (!delta)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
py_delta = PyObject_New(DiffDelta, &DiffDeltaType);
|
||||
if (py_delta) {
|
||||
py_delta->status = delta->status;
|
||||
py_delta->flags = delta->flags;
|
||||
py_delta->similarity = delta->similarity;
|
||||
py_delta->nfiles = delta->nfiles;
|
||||
py_delta->old_file = wrap_diff_file(&delta->old_file);
|
||||
py_delta->new_file = wrap_diff_file(&delta->new_file);
|
||||
}
|
||||
|
||||
return (PyObject *) py_delta;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
wrap_diff_hunk(git_patch *patch, size_t idx)
|
||||
{
|
||||
DiffHunk *py_hunk;
|
||||
const git_diff_hunk *hunk;
|
||||
const git_diff_line *line;
|
||||
size_t j, lines_in_hunk;
|
||||
int err;
|
||||
|
||||
err = git_patch_from_diff(&patch, diff, idx);
|
||||
err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, idx);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
return (PyObject*) wrap_patch(patch);
|
||||
py_hunk = PyObject_New(DiffHunk, &DiffHunkType);
|
||||
if (py_hunk) {
|
||||
py_hunk->old_start = hunk->old_start;
|
||||
py_hunk->old_lines = hunk->old_lines;
|
||||
py_hunk->new_start = hunk->new_start;
|
||||
py_hunk->new_lines = hunk->new_lines;
|
||||
py_hunk->header = to_unicode_n((const char *) &hunk->header,
|
||||
hunk->header_len, NULL, NULL);
|
||||
|
||||
py_hunk->lines = PyList_New(lines_in_hunk);
|
||||
for (j = 0; j < lines_in_hunk; ++j) {
|
||||
PyObject *py_line = NULL;
|
||||
|
||||
err = git_patch_get_line_in_hunk(&line, patch, idx, j);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
py_line = wrap_diff_line(line);
|
||||
if (py_line == NULL)
|
||||
return NULL;
|
||||
|
||||
PyList_SetItem(py_hunk->lines, j, py_line);
|
||||
}
|
||||
}
|
||||
|
||||
return (PyObject *) py_hunk;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
wrap_diff_stats(git_diff *diff)
|
||||
{
|
||||
git_diff_stats *stats;
|
||||
DiffStats *py_stats;
|
||||
int err;
|
||||
|
||||
err = git_diff_get_stats(&stats, diff);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
py_stats = PyObject_New(DiffStats, &DiffStatsType);
|
||||
if (!py_stats) {
|
||||
git_diff_stats_free(stats);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
py_stats->stats = stats;
|
||||
|
||||
return (PyObject *) py_stats;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
wrap_diff_line(const git_diff_line *line)
|
||||
{
|
||||
DiffLine *py_line;
|
||||
|
||||
py_line = PyObject_New(DiffLine, &DiffLineType);
|
||||
if (py_line) {
|
||||
py_line->origin = line->origin;
|
||||
py_line->old_lineno = line->old_lineno;
|
||||
py_line->new_lineno = line->new_lineno;
|
||||
py_line->num_lines = line->num_lines;
|
||||
py_line->content = to_unicode_n(line->content, line->content_len,
|
||||
NULL, NULL);
|
||||
py_line->content_offset = line->content_offset;
|
||||
}
|
||||
|
||||
return (PyObject *) py_line;
|
||||
}
|
||||
|
||||
static void
|
||||
Patch_dealloc(Patch *self)
|
||||
DiffFile_dealloc(DiffFile *self)
|
||||
{
|
||||
Py_CLEAR(self->hunks);
|
||||
Py_CLEAR(self->old_id);
|
||||
Py_CLEAR(self->new_id);
|
||||
free(self->old_file_path);
|
||||
free(self->new_file_path);
|
||||
Py_CLEAR(self->id);
|
||||
if (self->path)
|
||||
free(self->path);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
PyMemberDef Patch_members[] = {
|
||||
MEMBER(Patch, old_file_path, T_STRING, "old file path"),
|
||||
MEMBER(Patch, new_file_path, T_STRING, "new file path"),
|
||||
MEMBER(Patch, old_id, T_OBJECT, "old oid"),
|
||||
MEMBER(Patch, new_id, T_OBJECT, "new oid"),
|
||||
MEMBER(Patch, status, T_CHAR, "status"),
|
||||
MEMBER(Patch, similarity, T_INT, "similarity"),
|
||||
MEMBER(Patch, hunks, T_OBJECT, "hunks"),
|
||||
MEMBER(Patch, additions, T_INT, "additions"),
|
||||
MEMBER(Patch, deletions, T_INT, "deletions"),
|
||||
PyMemberDef DiffFile_members[] = {
|
||||
MEMBER(DiffFile, id, T_OBJECT, "Oid of the item."),
|
||||
MEMBER(DiffFile, path, T_STRING, "Path to the entry."),
|
||||
MEMBER(DiffFile, size, T_LONG, "Size of the entry."),
|
||||
MEMBER(DiffFile, flags, T_UINT, "Combination of GIT_DIFF_FLAG_* flags."),
|
||||
MEMBER(DiffFile, mode, T_USHORT, "Mode of the entry."),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(Patch_is_binary__doc__, "True if binary data, False if not.");
|
||||
|
||||
PyObject *
|
||||
Patch_is_binary__get__(Patch *self)
|
||||
{
|
||||
if (!(self->flags & GIT_DIFF_FLAG_NOT_BINARY) &&
|
||||
(self->flags & GIT_DIFF_FLAG_BINARY))
|
||||
Py_RETURN_TRUE;
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
PyDoc_STRVAR(DiffFile__doc__, "DiffFile object.");
|
||||
|
||||
PyGetSetDef Patch_getseters[] = {
|
||||
GETTER(Patch, is_binary),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(Patch__doc__, "Diff patch object.");
|
||||
|
||||
PyTypeObject PatchType = {
|
||||
PyTypeObject DiffFileType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_pygit2.Patch", /* tp_name */
|
||||
sizeof(Patch), /* tp_basicsize */
|
||||
"_pygit2.DiffFile", /* tp_name */
|
||||
sizeof(DiffFile), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)Patch_dealloc, /* tp_dealloc */
|
||||
(destructor)DiffFile_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
@ -210,7 +227,7 @@ PyTypeObject PatchType = {
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
Patch__doc__, /* tp_doc */
|
||||
DiffFile__doc__, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
@ -218,8 +235,8 @@ PyTypeObject PatchType = {
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
Patch_members, /* tp_members */
|
||||
Patch_getseters, /* tp_getset */
|
||||
DiffFile_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
@ -231,6 +248,183 @@ PyTypeObject PatchType = {
|
||||
};
|
||||
|
||||
|
||||
PyDoc_STRVAR(DiffDelta_status_char__doc__,
|
||||
"status_char()\n"
|
||||
"\n"
|
||||
"Return the single character abbreviation for a delta status code."
|
||||
);
|
||||
|
||||
PyObject *
|
||||
DiffDelta_status_char(DiffDelta *self)
|
||||
{
|
||||
char status = git_diff_status_char(self->status);
|
||||
|
||||
#if PY_MAJOR_VERSION == 2
|
||||
return Py_BuildValue("c", status);
|
||||
#else
|
||||
return Py_BuildValue("C", status);
|
||||
#endif
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(DiffDelta_is_binary__doc__, "True if binary data, False if not.");
|
||||
|
||||
PyObject *
|
||||
DiffDelta_is_binary__get__(DiffDelta *self)
|
||||
{
|
||||
if (!(self->flags & GIT_DIFF_FLAG_NOT_BINARY) &&
|
||||
(self->flags & GIT_DIFF_FLAG_BINARY))
|
||||
Py_RETURN_TRUE;
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
DiffDelta_dealloc(DiffDelta *self)
|
||||
{
|
||||
Py_CLEAR(self->old_file);
|
||||
Py_CLEAR(self->new_file);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
static PyMethodDef DiffDelta_methods[] = {
|
||||
METHOD(DiffDelta, status_char, METH_NOARGS),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyMemberDef DiffDelta_members[] = {
|
||||
MEMBER(DiffDelta, status, T_UINT, "A GIT_DELTA_* constant."),
|
||||
MEMBER(DiffDelta, flags, T_UINT, "Combination of GIT_DIFF_FLAG_* flags."),
|
||||
MEMBER(DiffDelta, similarity, T_USHORT, "For renamed and copied."),
|
||||
MEMBER(DiffDelta, nfiles, T_USHORT, "Number of files in the delta."),
|
||||
MEMBER(DiffDelta, old_file, T_OBJECT, "\"from\" side of the diff."),
|
||||
MEMBER(DiffDelta, new_file, T_OBJECT, "\"to\" side of the diff."),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyGetSetDef DiffDelta_getseters[] = {
|
||||
GETTER(DiffDelta, is_binary),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(DiffDelta__doc__, "DiffDelta object.");
|
||||
|
||||
PyTypeObject DiffDeltaType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_pygit2.DiffDelta", /* tp_name */
|
||||
sizeof(DiffDelta), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)DiffDelta_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
DiffDelta__doc__, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
DiffDelta_methods, /* tp_methods */
|
||||
DiffDelta_members, /* tp_members */
|
||||
DiffDelta_getseters, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /* tp_new */
|
||||
};
|
||||
|
||||
static void
|
||||
DiffLine_dealloc(DiffLine *self)
|
||||
{
|
||||
Py_CLEAR(self->content);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
PyMemberDef DiffLine_members[] = {
|
||||
MEMBER(DiffLine, origin, T_CHAR, "Type of the diff line"),
|
||||
MEMBER(DiffLine, old_lineno, T_INT,
|
||||
"Line number in old file or -1 for added line"),
|
||||
MEMBER(DiffLine, new_lineno, T_INT,
|
||||
"Line number in new file or -1 for deleted line"),
|
||||
MEMBER(DiffLine, num_lines, T_INT,
|
||||
"Number of newline characters in content"),
|
||||
MEMBER(DiffLine, content_offset, T_INT,
|
||||
"Offset in the original file to the content"),
|
||||
MEMBER(DiffLine, content, T_OBJECT, "Content of the diff line"),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(DiffLine__doc__, "DiffLine object.");
|
||||
|
||||
PyTypeObject DiffLineType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_pygit2.DiffLine", /* tp_name */
|
||||
sizeof(DiffLine), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)DiffLine_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
DiffLine__doc__, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
DiffLine_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /* tp_new */
|
||||
};
|
||||
|
||||
PyObject *
|
||||
diff_get_patch_byindex(git_diff *diff, size_t idx)
|
||||
{
|
||||
git_patch *patch = NULL;
|
||||
int err;
|
||||
|
||||
err = git_patch_from_diff(&patch, diff, idx);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
return (PyObject*) wrap_patch(patch);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
DiffIter_iternext(DiffIter *self)
|
||||
{
|
||||
@ -288,7 +482,8 @@ Diff_len(Diff *self)
|
||||
return (Py_ssize_t)git_diff_num_deltas(self->diff);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string.");
|
||||
PyDoc_STRVAR(Diff_patch__doc__,
|
||||
"Patch diff string. Can be None in some cases, such as empty commits.");
|
||||
|
||||
PyObject *
|
||||
Diff_patch__get__(Diff *self)
|
||||
@ -326,30 +521,32 @@ cleanup:
|
||||
|
||||
|
||||
static void
|
||||
Hunk_dealloc(Hunk *self)
|
||||
DiffHunk_dealloc(DiffHunk *self)
|
||||
{
|
||||
Py_CLEAR(self->header);
|
||||
Py_CLEAR(self->lines);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
PyMemberDef Hunk_members[] = {
|
||||
MEMBER(Hunk, old_start, T_INT, "Old start."),
|
||||
MEMBER(Hunk, old_lines, T_INT, "Old lines."),
|
||||
MEMBER(Hunk, new_start, T_INT, "New start."),
|
||||
MEMBER(Hunk, new_lines, T_INT, "New lines."),
|
||||
MEMBER(Hunk, lines, T_OBJECT, "Lines."),
|
||||
PyMemberDef DiffHunk_members[] = {
|
||||
MEMBER(DiffHunk, old_start, T_INT, "Old start."),
|
||||
MEMBER(DiffHunk, old_lines, T_INT, "Old lines."),
|
||||
MEMBER(DiffHunk, new_start, T_INT, "New start."),
|
||||
MEMBER(DiffHunk, new_lines, T_INT, "New lines."),
|
||||
MEMBER(DiffHunk, header, T_OBJECT, "Header."),
|
||||
MEMBER(DiffHunk, lines, T_OBJECT, "Lines."),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
PyDoc_STRVAR(Hunk__doc__, "Hunk object.");
|
||||
PyDoc_STRVAR(DiffHunk__doc__, "DiffHunk object.");
|
||||
|
||||
PyTypeObject HunkType = {
|
||||
PyTypeObject DiffHunkType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_pygit2.Hunk", /* tp_name */
|
||||
sizeof(Hunk), /* tp_basicsize */
|
||||
"_pygit2.DiffHunk", /* tp_name */
|
||||
sizeof(DiffHunk), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)Hunk_dealloc, /* tp_dealloc */
|
||||
(destructor)DiffHunk_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
@ -365,7 +562,7 @@ PyTypeObject HunkType = {
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
Hunk__doc__, /* tp_doc */
|
||||
DiffHunk__doc__, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
@ -373,7 +570,7 @@ PyTypeObject HunkType = {
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
Hunk_members, /* tp_members */
|
||||
DiffHunk_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
@ -385,6 +582,132 @@ PyTypeObject HunkType = {
|
||||
0, /* tp_new */
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(DiffStats_insertions__doc__, "Total number of insertions");
|
||||
|
||||
PyObject *
|
||||
DiffStats_insertions__get__(DiffStats *self)
|
||||
{
|
||||
return PyInt_FromSize_t(git_diff_stats_insertions(self->stats));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(DiffStats_deletions__doc__, "Total number of deletions");
|
||||
|
||||
PyObject *
|
||||
DiffStats_deletions__get__(DiffStats *self)
|
||||
{
|
||||
return PyInt_FromSize_t(git_diff_stats_deletions(self->stats));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(DiffStats_files_changed__doc__, "Total number of files changed");
|
||||
|
||||
PyObject *
|
||||
DiffStats_files_changed__get__(DiffStats *self)
|
||||
{
|
||||
return PyInt_FromSize_t(git_diff_stats_files_changed(self->stats));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(DiffStats_format__doc__,
|
||||
"format(format, width)-> str\n"
|
||||
"\n"
|
||||
"Format the stats as a string\n"
|
||||
"\n"
|
||||
"Arguments:\n"
|
||||
"\n"
|
||||
"format\n"
|
||||
" The format to use. A pygit2.GIT_DIFF_STATS_* constant\n"
|
||||
"\n"
|
||||
"width\n"
|
||||
" The width of the output. The output will be scaled to fit.");
|
||||
|
||||
PyObject *
|
||||
DiffStats_format(DiffStats *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
int err, format;
|
||||
git_buf buf = { 0 };
|
||||
Py_ssize_t width;
|
||||
PyObject *str;
|
||||
char *keywords[] = {"format", "width", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "in", keywords, &format, &width))
|
||||
return NULL;
|
||||
|
||||
if (width <= 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "width must be positive");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = git_diff_stats_to_buf(&buf, self->stats, format, width);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
str = to_unicode(buf.ptr, NULL, NULL);
|
||||
git_buf_free(&buf);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void
|
||||
DiffStats_dealloc(DiffStats *self)
|
||||
{
|
||||
git_diff_stats_free(self->stats);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
PyMethodDef DiffStats_methods[] = {
|
||||
METHOD(DiffStats, format, METH_VARARGS | METH_KEYWORDS),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyGetSetDef DiffStats_getseters[] = {
|
||||
GETTER(DiffStats, insertions),
|
||||
GETTER(DiffStats, deletions),
|
||||
GETTER(DiffStats, files_changed),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(DiffStats__doc__, "DiffStats object.");
|
||||
|
||||
PyTypeObject DiffStatsType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_pygit2.DiffStats", /* tp_name */
|
||||
sizeof(DiffStats), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)DiffStats_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
DiffStats__doc__, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
DiffStats_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
DiffStats_getseters, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /* tp_new */
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(Diff_from_c__doc__, "Method exposed for Index to hook into");
|
||||
|
||||
PyObject *
|
||||
@ -482,14 +805,20 @@ Diff_getitem(Diff *self, PyObject *value)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (PyLong_Check(value) < 0)
|
||||
return NULL;
|
||||
|
||||
i = PyLong_AsUnsignedLong(value);
|
||||
if (!PyInt_Check(value))
|
||||
return NULL; /* FIXME Raise error */
|
||||
|
||||
i = PyInt_AsSize_t(value);
|
||||
return diff_get_patch_byindex(self->diff, i);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Diff_stats__doc__, "Accumulate diff statistics for all patches");
|
||||
|
||||
PyObject *
|
||||
Diff_stats__get__(Diff *self)
|
||||
{
|
||||
return wrap_diff_stats(self->diff);
|
||||
}
|
||||
|
||||
static void
|
||||
Diff_dealloc(Diff *self)
|
||||
@ -501,6 +830,7 @@ Diff_dealloc(Diff *self)
|
||||
|
||||
PyGetSetDef Diff_getseters[] = {
|
||||
GETTER(Diff, patch),
|
||||
GETTER(Diff, stats),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
12
src/diff.h
12
src/diff.h
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -33,15 +33,13 @@
|
||||
#include <git2.h>
|
||||
#include "types.h"
|
||||
|
||||
#define DIFF_CHECK_TYPES(_x, _y, _type_x, _type_y) \
|
||||
PyObject_TypeCheck(_x, _type_x) && \
|
||||
PyObject_TypeCheck(_y, _type_y)
|
||||
|
||||
|
||||
PyObject* Diff_changes(Diff *self);
|
||||
PyObject* Diff_patch(Diff *self);
|
||||
|
||||
PyObject* wrap_diff(git_diff *diff, Repository *repo);
|
||||
PyObject* wrap_patch(git_patch *patch);
|
||||
PyObject* wrap_diff_delta(const git_diff_delta *delta);
|
||||
PyObject* wrap_diff_file(const git_diff_file *file);
|
||||
PyObject * wrap_diff_hunk(git_patch *patch, size_t idx);
|
||||
PyObject* wrap_diff_line(const git_diff_line *line);
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -126,3 +126,10 @@ Error_set_oid(int err, const git_oid *oid, size_t len)
|
||||
hex[len] = '\0';
|
||||
return Error_set_str(err, hex);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
Error_type_error(const char *format, PyObject *value)
|
||||
{
|
||||
PyErr_Format(PyExc_TypeError, format, Py_TYPE(value)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -37,5 +37,6 @@ PyObject* Error_set(int err);
|
||||
PyObject* Error_set_exc(PyObject* exception);
|
||||
PyObject* Error_set_str(int err, const char *str);
|
||||
PyObject* Error_set_oid(int err, const git_oid *oid, size_t len);
|
||||
PyObject* Error_type_error(const char *format, PyObject *value);
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
13
src/object.c
13
src/object.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -97,7 +97,7 @@ PyDoc_STRVAR(Object_type__doc__,
|
||||
PyObject *
|
||||
Object_type__get__(Object *self)
|
||||
{
|
||||
return PyLong_FromLong(git_object_type(self->obj));
|
||||
return PyInt_FromLong(git_object_type(self->obj));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Object__pointer__doc__, "Get the object's pointer. For internal use only.");
|
||||
@ -143,14 +143,15 @@ PyDoc_STRVAR(Object_peel__doc__,
|
||||
PyObject *
|
||||
Object_peel(Object *self, PyObject *py_type)
|
||||
{
|
||||
int type = -1, err;
|
||||
int err;
|
||||
git_otype otype;
|
||||
git_object *peeled;
|
||||
|
||||
type = py_object_to_object_type(py_type);
|
||||
if (type == -1)
|
||||
otype = py_object_to_otype(py_type);
|
||||
if (otype == GIT_OBJ_BAD)
|
||||
return NULL;
|
||||
|
||||
err = git_object_peel(&peeled, self->obj, (git_otype)type);
|
||||
err = git_object_peel(&peeled, self->obj, otype);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -209,8 +209,10 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw)
|
||||
Py_hash_t
|
||||
Oid_hash(PyObject *oid)
|
||||
{
|
||||
/* TODO Randomize (use _Py_HashSecret) to avoid collission DoS attacks? */
|
||||
return *(Py_hash_t*) ((Oid*)oid)->oid.id;
|
||||
PyObject *py_oid = git_oid_to_py_str(&((Oid *)oid)->oid);
|
||||
Py_hash_t ret = PyObject_Hash(py_oid);
|
||||
Py_DECREF(py_oid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
182
src/options.c
182
src/options.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -65,10 +65,11 @@ option(PyObject *self, PyObject *args)
|
||||
if (!py_option)
|
||||
return NULL;
|
||||
|
||||
if (!PyLong_Check(py_option))
|
||||
goto on_non_integer;
|
||||
if (!PyInt_Check(py_option))
|
||||
return Error_type_error(
|
||||
"option should be an integer, got %.200s", py_option);
|
||||
|
||||
option = PyLong_AsLong(py_option);
|
||||
option = PyInt_AsLong(py_option);
|
||||
|
||||
switch (option) {
|
||||
case GIT_OPT_GET_SEARCH_PATH:
|
||||
@ -79,11 +80,11 @@ option(PyObject *self, PyObject *args)
|
||||
if (!py_level)
|
||||
return NULL;
|
||||
|
||||
if (!PyLong_Check(py_level))
|
||||
goto on_non_integer;
|
||||
if (!PyInt_Check(py_level))
|
||||
return Error_type_error(
|
||||
"level should be an integer, got %.200s", py_level);
|
||||
|
||||
return get_search_path(PyLong_AsLong(py_level));
|
||||
break;
|
||||
return get_search_path(PyInt_AsLong(py_level));
|
||||
}
|
||||
|
||||
case GIT_OPT_SET_SEARCH_PATH:
|
||||
@ -100,23 +101,22 @@ option(PyObject *self, PyObject *args)
|
||||
if (!py_path)
|
||||
return NULL;
|
||||
|
||||
if (!PyLong_Check(py_level))
|
||||
goto on_non_integer;
|
||||
if (!PyInt_Check(py_level))
|
||||
return Error_type_error(
|
||||
"level should be an integer, got %.200s", py_level);
|
||||
|
||||
path = py_str_borrow_c_str(&tpath, py_path, NULL);
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
err = git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, PyLong_AsLong(py_level), path);
|
||||
err = git_libgit2_opts(
|
||||
GIT_OPT_SET_SEARCH_PATH, PyInt_AsLong(py_level), path);
|
||||
Py_DECREF(tpath);
|
||||
|
||||
if (err < 0) {
|
||||
Error_set(err);
|
||||
return NULL;
|
||||
}
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case GIT_OPT_GET_MWINDOW_SIZE:
|
||||
@ -124,14 +124,10 @@ option(PyObject *self, PyObject *args)
|
||||
size_t size;
|
||||
|
||||
error = git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &size);
|
||||
if (error < 0) {
|
||||
Error_set(error);
|
||||
return NULL;
|
||||
}
|
||||
if (error < 0)
|
||||
return Error_set(error);
|
||||
|
||||
return PyLong_FromSize_t(size);
|
||||
|
||||
break;
|
||||
return PyInt_FromSize_t(size);
|
||||
}
|
||||
|
||||
case GIT_OPT_SET_MWINDOW_SIZE:
|
||||
@ -143,25 +139,141 @@ option(PyObject *self, PyObject *args)
|
||||
if (!py_size)
|
||||
return NULL;
|
||||
|
||||
if (!PyLong_Check(py_size))
|
||||
goto on_non_integer;
|
||||
if (!PyInt_Check(py_size))
|
||||
return Error_type_error(
|
||||
"size should be an integer, got %.200s", py_size);
|
||||
|
||||
size = PyLong_AsSize_t(py_size);
|
||||
size = PyInt_AsSize_t(py_size);
|
||||
error = git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, size);
|
||||
if (error < 0) {
|
||||
Error_set(error);
|
||||
return NULL;
|
||||
}
|
||||
if (error < 0)
|
||||
return Error_set(error);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT:
|
||||
{
|
||||
size_t limit;
|
||||
|
||||
error = git_libgit2_opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, &limit);
|
||||
if (error < 0)
|
||||
return Error_set(error);
|
||||
|
||||
return PyInt_FromSize_t(limit);
|
||||
}
|
||||
|
||||
case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT:
|
||||
{
|
||||
size_t limit;
|
||||
PyObject *py_limit;
|
||||
|
||||
py_limit = PyTuple_GetItem(args, 1);
|
||||
if (!py_limit)
|
||||
return NULL;
|
||||
|
||||
if (PyInt_Check(py_limit)) {
|
||||
limit = PyInt_AsSize_t(py_limit);
|
||||
} else if (PyLong_Check(py_limit)) {
|
||||
limit = PyLong_AsSize_t(py_limit);
|
||||
} else {
|
||||
return Error_type_error(
|
||||
"limit should be an integer, got %.200s", py_limit);
|
||||
}
|
||||
|
||||
error = git_libgit2_opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, limit);
|
||||
if (error < 0)
|
||||
return Error_set(error);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
case GIT_OPT_SET_CACHE_OBJECT_LIMIT:
|
||||
{
|
||||
size_t limit;
|
||||
int object_type;
|
||||
PyObject *py_object_type, *py_limit;
|
||||
|
||||
py_object_type = PyTuple_GetItem(args, 1);
|
||||
if (!py_object_type)
|
||||
return NULL;
|
||||
|
||||
py_limit = PyTuple_GetItem(args, 2);
|
||||
if (!py_limit)
|
||||
return NULL;
|
||||
|
||||
if (!PyInt_Check(py_limit))
|
||||
return Error_type_error(
|
||||
"limit should be an integer, got %.200s", py_limit);
|
||||
|
||||
object_type = PyInt_AsLong(py_object_type);
|
||||
limit = PyInt_AsSize_t(py_limit);
|
||||
error = git_libgit2_opts(
|
||||
GIT_OPT_SET_CACHE_OBJECT_LIMIT, object_type, limit);
|
||||
|
||||
if (error < 0)
|
||||
return Error_set(error);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
case GIT_OPT_SET_CACHE_MAX_SIZE:
|
||||
{
|
||||
size_t max_size;
|
||||
PyObject *py_max_size;
|
||||
|
||||
py_max_size = PyTuple_GetItem(args, 1);
|
||||
if (!py_max_size)
|
||||
return NULL;
|
||||
|
||||
if (!PyInt_Check(py_max_size))
|
||||
return Error_type_error(
|
||||
"max_size should be an integer, got %.200s", py_max_size);
|
||||
|
||||
max_size = PyInt_AsSize_t(py_max_size);
|
||||
error = git_libgit2_opts(GIT_OPT_SET_CACHE_MAX_SIZE, max_size);
|
||||
if (error < 0)
|
||||
return Error_set(error);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
case GIT_OPT_ENABLE_CACHING:
|
||||
{
|
||||
int flag;
|
||||
PyObject *py_flag;
|
||||
|
||||
py_flag = PyTuple_GetItem(args, 1);
|
||||
|
||||
if (!PyInt_Check(py_flag))
|
||||
return Error_type_error(
|
||||
"flag should be an integer, got %.200s", py_flag);
|
||||
|
||||
flag = PyInt_AsSize_t(py_flag);
|
||||
error = git_libgit2_opts(GIT_OPT_ENABLE_CACHING, flag);
|
||||
if (error < 0)
|
||||
return Error_set(error);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
case GIT_OPT_GET_CACHED_MEMORY:
|
||||
{
|
||||
size_t current;
|
||||
size_t allowed;
|
||||
PyObject* tup = PyTuple_New(2);
|
||||
|
||||
error = git_libgit2_opts(GIT_OPT_GET_CACHED_MEMORY, ¤t, &allowed);
|
||||
if (error < 0)
|
||||
return Error_set(error);
|
||||
|
||||
PyTuple_SetItem(tup, 0, PyInt_FromLong(current));
|
||||
PyTuple_SetItem(tup, 1, PyInt_FromLong(allowed));
|
||||
|
||||
return tup;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_ValueError, "unknown/unsupported option value");
|
||||
return NULL;
|
||||
|
||||
on_non_integer:
|
||||
PyErr_SetString(PyExc_TypeError, "option is not an integer");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
158
src/patch.c
Normal file
158
src/patch.c
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* In addition to the permissions in the GNU General Public License,
|
||||
* the authors give you unlimited permission to link the compiled
|
||||
* version of this file into combinations with other programs,
|
||||
* and to distribute those combinations without any restriction
|
||||
* coming from the use of this file. (The General Public License
|
||||
* restrictions do apply in other respects; for example, they cover
|
||||
* modification of the file, and distribution when not linked into
|
||||
* a combined executable.)
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
#include "diff.h"
|
||||
#include "error.h"
|
||||
#include "oid.h"
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
|
||||
extern PyTypeObject DiffHunkType;
|
||||
PyTypeObject PatchType;
|
||||
|
||||
|
||||
PyObject *
|
||||
wrap_patch(git_patch *patch)
|
||||
{
|
||||
Patch *py_patch;
|
||||
PyObject *py_hunk;
|
||||
size_t i, hunk_amounts;
|
||||
|
||||
if (!patch)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
py_patch = PyObject_New(Patch, &PatchType);
|
||||
if (py_patch) {
|
||||
py_patch->patch = patch;
|
||||
|
||||
hunk_amounts = git_patch_num_hunks(patch);
|
||||
py_patch->hunks = PyList_New(hunk_amounts);
|
||||
for (i = 0; i < hunk_amounts; ++i) {
|
||||
py_hunk = wrap_diff_hunk(patch, i);
|
||||
if (py_hunk)
|
||||
PyList_SetItem((PyObject*) py_patch->hunks, i, py_hunk);
|
||||
}
|
||||
}
|
||||
|
||||
return (PyObject*) py_patch;
|
||||
}
|
||||
|
||||
static void
|
||||
Patch_dealloc(Patch *self)
|
||||
{
|
||||
Py_CLEAR(self->hunks);
|
||||
git_patch_free(self->patch);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Patch_delta__doc__, "Get the delta associated with a patch.");
|
||||
|
||||
PyObject *
|
||||
Patch_delta__get__(Patch *self)
|
||||
{
|
||||
if (!self->patch)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
return wrap_diff_delta(git_patch_get_delta(self->patch));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Patch_line_stats__doc__,
|
||||
"Get line counts of each type in a patch.");
|
||||
|
||||
PyObject *
|
||||
Patch_line_stats__get__(Patch *self)
|
||||
{
|
||||
size_t context, additions, deletions;
|
||||
int err;
|
||||
|
||||
if (!self->patch)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
err = git_patch_line_stats(&context, &additions, &deletions,
|
||||
self->patch);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
return Py_BuildValue("III", context, additions, deletions);
|
||||
}
|
||||
|
||||
PyMemberDef Patch_members[] = {
|
||||
MEMBER(Patch, hunks, T_OBJECT, "hunks"),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyGetSetDef Patch_getseters[] = {
|
||||
GETTER(Patch, delta),
|
||||
GETTER(Patch, line_stats),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(Patch__doc__, "Diff patch object.");
|
||||
|
||||
PyTypeObject PatchType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_pygit2.Patch", /* tp_name */
|
||||
sizeof(Patch), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)Patch_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
Patch__doc__, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
Patch_members, /* tp_members */
|
||||
Patch_getseters, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /* tp_new */
|
||||
};
|
37
src/patch.h
Normal file
37
src/patch.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_pygit2_patch_h
|
||||
#define INCLUDE_pygit2_patch_h
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include <git2.h>
|
||||
|
||||
PyObject* wrap_patch(git_patch *patch);
|
||||
|
||||
#endif
|
103
src/pygit2.c
103
src/pygit2.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -44,8 +44,12 @@ extern PyTypeObject ObjectType;
|
||||
extern PyTypeObject CommitType;
|
||||
extern PyTypeObject DiffType;
|
||||
extern PyTypeObject DiffIterType;
|
||||
extern PyTypeObject DiffDeltaType;
|
||||
extern PyTypeObject DiffFileType;
|
||||
extern PyTypeObject DiffHunkType;
|
||||
extern PyTypeObject DiffLineType;
|
||||
extern PyTypeObject DiffStatsType;
|
||||
extern PyTypeObject PatchType;
|
||||
extern PyTypeObject HunkType;
|
||||
extern PyTypeObject TreeType;
|
||||
extern PyTypeObject TreeBuilderType;
|
||||
extern PyTypeObject TreeEntryType;
|
||||
@ -140,7 +144,44 @@ hash(PyObject *self, PyObject *args)
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(init_file_backend__doc__,
|
||||
"init_file_backend(path) -> object\n"
|
||||
"\n"
|
||||
"open repo backend given path.");
|
||||
PyObject *
|
||||
init_file_backend(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char* path = NULL;
|
||||
int err = GIT_OK;
|
||||
git_repository *repository = NULL;
|
||||
if (!PyArg_ParseTuple(args, "s", &path)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = git_repository_open(&repository, path);
|
||||
if (err < 0) {
|
||||
Error_set_str(err, path);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return PyCapsule_New(repository, "backend", NULL);
|
||||
|
||||
cleanup:
|
||||
if (repository) {
|
||||
git_repository_free(repository);
|
||||
}
|
||||
|
||||
if (err == GIT_ENOTFOUND) {
|
||||
PyErr_Format(GitError, "Repository not found at %s", path);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PyMethodDef module_methods[] = {
|
||||
{"init_file_backend", init_file_backend, METH_VARARGS,
|
||||
init_file_backend__doc__},
|
||||
{"discover_repository", discover_repository, METH_VARARGS,
|
||||
discover_repository__doc__},
|
||||
{"hashfile", hashfile, METH_VARARGS, hashfile__doc__},
|
||||
@ -166,6 +207,12 @@ moduleinit(PyObject* m)
|
||||
ADD_CONSTANT_INT(m, GIT_OPT_SET_SEARCH_PATH);
|
||||
ADD_CONSTANT_INT(m, GIT_OPT_GET_MWINDOW_SIZE);
|
||||
ADD_CONSTANT_INT(m, GIT_OPT_SET_MWINDOW_SIZE);
|
||||
ADD_CONSTANT_INT(m, GIT_OPT_GET_MWINDOW_MAPPED_LIMIT);
|
||||
ADD_CONSTANT_INT(m, GIT_OPT_SET_MWINDOW_MAPPED_LIMIT);
|
||||
ADD_CONSTANT_INT(m, GIT_OPT_SET_CACHE_OBJECT_LIMIT);
|
||||
ADD_CONSTANT_INT(m, GIT_OPT_GET_CACHED_MEMORY);
|
||||
ADD_CONSTANT_INT(m, GIT_OPT_ENABLE_CACHING);
|
||||
ADD_CONSTANT_INT(m, GIT_OPT_SET_CACHE_MAX_SIZE);
|
||||
|
||||
/* Errors */
|
||||
GitError = PyErr_NewException("_pygit2.GitError", NULL, NULL);
|
||||
@ -256,6 +303,7 @@ moduleinit(PyObject* m)
|
||||
ADD_TYPE(m, Branch)
|
||||
ADD_CONSTANT_INT(m, GIT_BRANCH_LOCAL)
|
||||
ADD_CONSTANT_INT(m, GIT_BRANCH_REMOTE)
|
||||
ADD_CONSTANT_INT(m, GIT_BRANCH_ALL)
|
||||
|
||||
/*
|
||||
* Index & Working copy
|
||||
@ -269,10 +317,11 @@ moduleinit(PyObject* m)
|
||||
ADD_CONSTANT_INT(m, GIT_STATUS_WT_MODIFIED)
|
||||
ADD_CONSTANT_INT(m, GIT_STATUS_WT_DELETED)
|
||||
ADD_CONSTANT_INT(m, GIT_STATUS_IGNORED) /* Flags for ignored files */
|
||||
ADD_CONSTANT_INT(m, GIT_STATUS_CONFLICTED)
|
||||
/* Different checkout strategies */
|
||||
ADD_CONSTANT_INT(m, GIT_CHECKOUT_NONE)
|
||||
ADD_CONSTANT_INT(m, GIT_CHECKOUT_SAFE)
|
||||
ADD_CONSTANT_INT(m, GIT_CHECKOUT_SAFE_CREATE)
|
||||
ADD_CONSTANT_INT(m, GIT_CHECKOUT_RECREATE_MISSING)
|
||||
ADD_CONSTANT_INT(m, GIT_CHECKOUT_FORCE)
|
||||
ADD_CONSTANT_INT(m, GIT_CHECKOUT_ALLOW_CONFLICTS)
|
||||
ADD_CONSTANT_INT(m, GIT_CHECKOUT_REMOVE_UNTRACKED)
|
||||
@ -287,11 +336,19 @@ moduleinit(PyObject* m)
|
||||
*/
|
||||
INIT_TYPE(DiffType, NULL, NULL)
|
||||
INIT_TYPE(DiffIterType, NULL, NULL)
|
||||
INIT_TYPE(DiffDeltaType, NULL, NULL)
|
||||
INIT_TYPE(DiffFileType, NULL, NULL)
|
||||
INIT_TYPE(DiffHunkType, NULL, NULL)
|
||||
INIT_TYPE(DiffLineType, NULL, NULL)
|
||||
INIT_TYPE(DiffStatsType, NULL, NULL)
|
||||
INIT_TYPE(PatchType, NULL, NULL)
|
||||
INIT_TYPE(HunkType, NULL, NULL)
|
||||
ADD_TYPE(m, Diff)
|
||||
ADD_TYPE(m, DiffDelta)
|
||||
ADD_TYPE(m, DiffFile)
|
||||
ADD_TYPE(m, DiffHunk)
|
||||
ADD_TYPE(m, DiffLine)
|
||||
ADD_TYPE(m, DiffStats)
|
||||
ADD_TYPE(m, Patch)
|
||||
ADD_TYPE(m, Hunk)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_NORMAL)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_REVERSE)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_FORCE_TEXT)
|
||||
@ -310,9 +367,15 @@ moduleinit(PyObject* m)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_CASE)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_UNTRACKED_CONTENT)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_SKIP_BINARY_CHECK)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_BINARY)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE_TREES)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_IGNORED_DIRS)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_NONE)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_FULL)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_SHORT)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_NUMBER)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_STATS_INCLUDE_SUMMARY)
|
||||
/* Flags for diff find similar */
|
||||
/* --find-renames */
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES)
|
||||
@ -325,6 +388,23 @@ moduleinit(PyObject* m)
|
||||
/* --break-rewrites=/M */
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_FIND_AND_BREAK_REWRITES)
|
||||
|
||||
/* DiffDelta and DiffFile flags */
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_BINARY)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_NOT_BINARY)
|
||||
ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_VALID_ID)
|
||||
|
||||
/* DiffDelta.status */
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_UNMODIFIED)
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_ADDED)
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_DELETED)
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_MODIFIED)
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_RENAMED)
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_COPIED)
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_IGNORED)
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_UNTRACKED)
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_TYPECHANGE)
|
||||
ADD_CONSTANT_INT(m, GIT_DELTA_UNREADABLE)
|
||||
|
||||
/* Config */
|
||||
ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_LOCAL);
|
||||
ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_GLOBAL);
|
||||
@ -345,6 +425,19 @@ 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();
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -163,7 +163,7 @@ Reference_rename(Reference *self, PyObject *py_name)
|
||||
return NULL;
|
||||
|
||||
/* Rename */
|
||||
err = git_reference_rename(&new_reference, self->reference, c_name, 0, NULL, NULL);
|
||||
err = git_reference_rename(&new_reference, self->reference, c_name, 0, NULL);
|
||||
git_reference_free(self->reference);
|
||||
free(c_name);
|
||||
if (err < 0)
|
||||
@ -228,7 +228,7 @@ Reference_target__get__(Reference *self)
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Reference_set_target__doc__,
|
||||
"set_target(target, [signature, message])\n"
|
||||
"set_target(target, [message])\n"
|
||||
"\n"
|
||||
"Set the target of this reference.\n"
|
||||
"\n"
|
||||
@ -240,9 +240,6 @@ PyDoc_STRVAR(Reference_set_target__doc__,
|
||||
"\n"
|
||||
"target\n"
|
||||
" The new target for this reference\n"
|
||||
"signature\n"
|
||||
" The signature to use for the reflog. If left out, the repository's\n"
|
||||
" default identity will be used.\n"
|
||||
"message\n"
|
||||
" Message to use for the reflog.\n");
|
||||
|
||||
@ -253,28 +250,23 @@ Reference_set_target(Reference *self, PyObject *args, PyObject *kwds)
|
||||
char *c_name;
|
||||
int err;
|
||||
git_reference *new_ref;
|
||||
const git_signature *sig = NULL;
|
||||
PyObject *py_target = NULL;
|
||||
Signature *py_signature = NULL;
|
||||
const char *message = NULL;
|
||||
char *keywords[] = {"target", "signature", "message", NULL};
|
||||
char *keywords[] = {"target", "message", NULL};
|
||||
|
||||
CHECK_REFERENCE(self);
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O!s", keywords,
|
||||
&py_target, &SignatureType, &py_signature, &message))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s", keywords,
|
||||
&py_target, &message))
|
||||
return NULL;
|
||||
|
||||
if (py_signature)
|
||||
sig = py_signature->signature;
|
||||
|
||||
/* 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;
|
||||
|
||||
err = git_reference_set_target(&new_ref, self->reference, &oid, sig, message);
|
||||
err = git_reference_set_target(&new_ref, self->reference, &oid, message);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
@ -288,7 +280,7 @@ Reference_set_target(Reference *self, PyObject *args, PyObject *kwds)
|
||||
if (c_name == NULL)
|
||||
return NULL;
|
||||
|
||||
err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, sig, message);
|
||||
err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, message);
|
||||
free(c_name);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
@ -333,7 +325,7 @@ Reference_type__get__(Reference *self)
|
||||
|
||||
CHECK_REFERENCE(self);
|
||||
c_type = git_reference_type(self->reference);
|
||||
return PyLong_FromLong(c_type);
|
||||
return PyInt_FromLong(c_type);
|
||||
}
|
||||
|
||||
|
||||
@ -387,7 +379,8 @@ PyDoc_STRVAR(Reference_peel__doc__,
|
||||
PyObject *
|
||||
Reference_peel(Reference *self, PyObject *args)
|
||||
{
|
||||
int err, type;
|
||||
int err;
|
||||
git_otype otype;
|
||||
git_object *obj;
|
||||
PyObject *py_type = Py_None;
|
||||
|
||||
@ -396,11 +389,11 @@ Reference_peel(Reference *self, PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, "|O", &py_type))
|
||||
return NULL;
|
||||
|
||||
type = py_object_to_object_type(py_type);
|
||||
if (type == -1)
|
||||
otype = py_object_to_otype(py_type);
|
||||
if (otype == GIT_OBJ_BAD)
|
||||
return NULL;
|
||||
|
||||
err = git_reference_peel(&obj, self->reference, type);
|
||||
err = git_reference_peel(&obj, self->reference, otype);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
445
src/repository.c
445
src/repository.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -70,11 +70,25 @@ int_to_loose_object_type(int type_id)
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *
|
||||
wrap_repository(git_repository *c_repo)
|
||||
{
|
||||
Repository *py_repo = PyObject_GC_New(Repository, &RepositoryType);
|
||||
|
||||
if (py_repo) {
|
||||
py_repo->repo = c_repo;
|
||||
py_repo->config = NULL;
|
||||
py_repo->index = NULL;
|
||||
py_repo->owned = 1;
|
||||
}
|
||||
|
||||
return (PyObject *)py_repo;
|
||||
}
|
||||
|
||||
int
|
||||
Repository_init(Repository *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
char *path;
|
||||
int err;
|
||||
PyObject *backend;
|
||||
|
||||
if (kwds && PyDict_Size(kwds) > 0) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
@ -82,15 +96,16 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &path))
|
||||
return -1;
|
||||
|
||||
err = git_repository_open(&self->repo, path);
|
||||
if (err < 0) {
|
||||
Error_set_str(err, path);
|
||||
if (!PyArg_ParseTuple(args, "O", &backend)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->repo = PyCapsule_GetPointer(backend, "backend");
|
||||
if (self->repo == NULL) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Repository unable to unpack backend.");
|
||||
return -1;
|
||||
}
|
||||
self->owned = 1;
|
||||
self->config = NULL;
|
||||
self->index = NULL;
|
||||
@ -334,6 +349,26 @@ Repository_lookup_branch(Repository *self, PyObject *args)
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(Repository_path_is_ignored__doc__,
|
||||
"Check if a path is ignored in the repository.");
|
||||
|
||||
PyObject *
|
||||
Repository_path_is_ignored(Repository *self, PyObject *args)
|
||||
{
|
||||
int ignored;
|
||||
char *path;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &path))
|
||||
return NULL;
|
||||
|
||||
git_ignore_path_is_ignored(&ignored, self->repo, path);
|
||||
if (ignored == 1)
|
||||
Py_RETURN_TRUE;
|
||||
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(Repository_revparse_single__doc__,
|
||||
"revparse_single(revision) -> Object\n"
|
||||
"\n"
|
||||
@ -526,7 +561,8 @@ Repository_workdir__set__(Repository *self, PyObject *py_workdir)
|
||||
PyDoc_STRVAR(Repository_merge_base__doc__,
|
||||
"merge_base(oid, oid) -> Oid\n"
|
||||
"\n"
|
||||
"Find as good common ancestors as possible for a merge.");
|
||||
"Find as good common ancestors as possible for a merge.\n"
|
||||
"Returns None if there is no merge base between the commits");
|
||||
|
||||
PyObject *
|
||||
Repository_merge_base(Repository *self, PyObject *args)
|
||||
@ -550,6 +586,10 @@ Repository_merge_base(Repository *self, PyObject *args)
|
||||
return NULL;
|
||||
|
||||
err = git_merge_base(&oid, self->repo, &oid1, &oid2);
|
||||
|
||||
if (err == GIT_ENOTFOUND)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
@ -622,7 +662,7 @@ Repository_merge(Repository *self, PyObject *py_oid)
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
|
||||
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
|
||||
err = git_merge(self->repo,
|
||||
(const git_annotated_commit **)&commit, 1,
|
||||
&merge_opts, &checkout_opts);
|
||||
@ -634,6 +674,46 @@ Repository_merge(Repository *self, PyObject *py_oid)
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Repository_cherrypick__doc__,
|
||||
"cherrypick(id)\n"
|
||||
"\n"
|
||||
"Cherry-pick the given oid, producing changes in the index and working directory.\n"
|
||||
"\n"
|
||||
"Merges the given commit into HEAD as a cherrypick, writing the results into the\n"
|
||||
"working directory. Any changes are staged for commit and any conflicts\n"
|
||||
"are written to the index. Callers should inspect the repository's\n"
|
||||
"index after this completes, resolve any conflicts and prepare a\n"
|
||||
"commit.");
|
||||
|
||||
PyObject *
|
||||
Repository_cherrypick(Repository *self, PyObject *py_oid)
|
||||
{
|
||||
git_commit *commit;
|
||||
git_oid oid;
|
||||
int err;
|
||||
size_t len;
|
||||
git_cherrypick_options cherrypick_opts = GIT_CHERRYPICK_OPTIONS_INIT;
|
||||
|
||||
len = py_oid_to_git_oid(py_oid, &oid);
|
||||
if (len == 0)
|
||||
return NULL;
|
||||
|
||||
err = git_commit_lookup(&commit, self->repo, &oid);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
cherrypick_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
|
||||
err = git_cherrypick(self->repo,
|
||||
commit,
|
||||
(const git_cherrypick_options *)&cherrypick_opts);
|
||||
|
||||
git_commit_free(commit);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Repository_walk__doc__,
|
||||
"walk(oid[, sort_mode]) -> iterator\n"
|
||||
"\n"
|
||||
@ -782,8 +862,88 @@ Repository_create_blob_fromdisk(Repository *self, PyObject *args)
|
||||
}
|
||||
|
||||
|
||||
#define BUFSIZE 4096
|
||||
|
||||
PyDoc_STRVAR(Repository_create_blob_fromiobase__doc__,
|
||||
"create_blob_fromiobase(io.IOBase) -> Oid\n"
|
||||
"\n"
|
||||
"Create a new blob from an IOBase object.");
|
||||
|
||||
PyObject *
|
||||
Repository_create_blob_fromiobase(Repository *self, PyObject *py_file)
|
||||
{
|
||||
git_writestream *stream;
|
||||
git_oid oid;
|
||||
PyObject *py_is_readable;
|
||||
int is_readable;
|
||||
int err;
|
||||
|
||||
py_is_readable = PyObject_CallMethod(py_file, "readable", NULL);
|
||||
if (!py_is_readable) {
|
||||
if (PyErr_ExceptionMatches(PyExc_AttributeError))
|
||||
PyErr_SetObject(PyExc_TypeError, py_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
is_readable = PyObject_IsTrue(py_is_readable);
|
||||
Py_DECREF(py_is_readable);
|
||||
|
||||
if (!is_readable) {
|
||||
Py_DECREF(py_file);
|
||||
PyErr_SetString(PyExc_TypeError, "expected readable IO type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = git_blob_create_fromstream(&stream, self->repo, NULL);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
for (;;) {
|
||||
PyObject *py_bytes;
|
||||
char *bytes;
|
||||
Py_ssize_t size;
|
||||
|
||||
py_bytes = PyObject_CallMethod(py_file, "read", "i", 4096);
|
||||
if (!py_bytes)
|
||||
return NULL;
|
||||
|
||||
if (py_bytes == Py_None) {
|
||||
Py_DECREF(py_bytes);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (PyBytes_AsStringAndSize(py_bytes, &bytes, &size)) {
|
||||
Py_DECREF(py_bytes);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
Py_DECREF(py_bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
err = stream->write(stream, bytes, size);
|
||||
Py_DECREF(py_bytes);
|
||||
if (err < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (err < 0) {
|
||||
stream->free(stream);
|
||||
return Error_set(err);
|
||||
}
|
||||
|
||||
err = git_blob_create_fromstream_commit(&oid, stream);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
return git_oid_to_python(&oid);
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(Repository_create_commit__doc__,
|
||||
"create_commit(reference, author, committer, message, tree, parents[, encoding]) -> Oid\n"
|
||||
"create_commit(reference_name, author, committer, message, tree, parents[, encoding]) -> Oid\n"
|
||||
"\n"
|
||||
"Create a new commit object, return its oid.");
|
||||
|
||||
@ -934,7 +1094,7 @@ Repository_create_branch(Repository *self, PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, "sO!|i", &c_name, &CommitType, &py_commit, &force))
|
||||
return NULL;
|
||||
|
||||
err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force, NULL, NULL);
|
||||
err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
@ -981,6 +1141,56 @@ 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"
|
||||
"\n"
|
||||
@ -1044,6 +1254,173 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Repository_listall_submodules__doc__,
|
||||
"listall_submodules() -> [str, ...]\n"
|
||||
"\n"
|
||||
"Return a list with all submodule paths in the repository.\n");
|
||||
|
||||
static int foreach_path_cb(git_submodule *submodule, const char *name, void *payload)
|
||||
{
|
||||
PyObject *list = (PyObject *)payload;
|
||||
PyObject *path = to_unicode(git_submodule_path(submodule), NULL, NULL);
|
||||
|
||||
return PyList_Append(list, path);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
Repository_listall_submodules(Repository *self, PyObject *args)
|
||||
{
|
||||
int err;
|
||||
PyObject *list;
|
||||
|
||||
list = PyList_New(0);
|
||||
if (list == NULL)
|
||||
return NULL;
|
||||
|
||||
err = git_submodule_foreach(self->repo, foreach_path_cb, list);
|
||||
if (err != 0) {
|
||||
Py_DECREF(list);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(Repository_init_submodules__doc__,
|
||||
"init_submodule(submodules=None, overwrite=False)\n"
|
||||
"\n"
|
||||
"Initialize all submodules in repository.\n"
|
||||
"submodules: List of submodules to initialize. Default argument initializes all submodules.\n"
|
||||
"overwrite: Flag indicating if initialization should overwrite submodule entries.\n");
|
||||
|
||||
static int foreach_sub_init_cb(git_submodule *submodule, const char *name, void *payload)
|
||||
{
|
||||
return git_submodule_init(submodule, *(int*)payload);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
Repository_init_submodules(Repository* self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *list = Py_None;
|
||||
PyObject *oflag = Py_False;
|
||||
char *kwlist[] = {"submodules", "overwrite", NULL};
|
||||
int err, fflag;
|
||||
PyObject *iter, *subpath, *next;
|
||||
const char *c_subpath;
|
||||
git_submodule *submodule;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &list, &oflag))
|
||||
return NULL;
|
||||
|
||||
fflag = PyObject_IsTrue(oflag);
|
||||
|
||||
if (fflag != 0 && fflag != 1)
|
||||
fflag = 0;
|
||||
|
||||
//Init all submodules listed in repository
|
||||
if (list == Py_None) {
|
||||
err = git_submodule_foreach(self->repo, foreach_sub_init_cb, &fflag);
|
||||
if (err != 0)
|
||||
return Error_set(err);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
iter = PyObject_GetIter(list);
|
||||
if (!iter)
|
||||
return NULL;
|
||||
|
||||
while (1) {
|
||||
next = PyIter_Next(iter);
|
||||
if (!next)
|
||||
break;
|
||||
|
||||
c_subpath = py_str_borrow_c_str(&subpath, next, NULL);
|
||||
|
||||
git_submodule_lookup(&submodule, self->repo, c_subpath);
|
||||
Py_DECREF(subpath);
|
||||
if (!submodule) {
|
||||
PyErr_SetString(PyExc_KeyError,
|
||||
"Submodule does not exist");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = git_submodule_init(submodule, fflag);
|
||||
if (err != 0) {
|
||||
return Error_set(err);
|
||||
}
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Repository_update_submodules__doc__,
|
||||
"update_submodules(submodules=None, init=False)\n"
|
||||
"\n"
|
||||
"Updates the specified submodules, or all if None are specified\n"
|
||||
"init: Flag indicating if submodules should be automatically initialized if necessary.\n");
|
||||
|
||||
static int foreach_sub_update_cb(git_submodule *submodule, const char *name, void *payload)
|
||||
{
|
||||
git_submodule_update_options opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
|
||||
return git_submodule_update(submodule, *(int*)payload, &opts);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
Repository_update_submodules(Repository *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *list = Py_None;
|
||||
PyObject *py_init = Py_False;
|
||||
PyObject *iter, *next, *subpath;
|
||||
int init, err;
|
||||
const char *c_subpath;
|
||||
git_submodule *submodule;
|
||||
git_submodule_update_options opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
|
||||
|
||||
char *kwlist[] = {"submodules", "init", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &list, &py_init))
|
||||
return NULL;
|
||||
|
||||
init = PyObject_IsTrue(py_init);
|
||||
|
||||
if (init != 0 && init != 1)
|
||||
init = 0;
|
||||
|
||||
if (list == Py_None) {
|
||||
err = git_submodule_foreach(self->repo, foreach_sub_update_cb, &init);
|
||||
if (err != 0)
|
||||
return Error_set(err);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
iter = PyObject_GetIter(list);
|
||||
if (!iter)
|
||||
return NULL;
|
||||
|
||||
while (1) {
|
||||
next = PyIter_Next(iter);
|
||||
if (!next)
|
||||
break;
|
||||
|
||||
c_subpath = py_str_borrow_c_str(&subpath, next, NULL);
|
||||
|
||||
git_submodule_lookup(&submodule, self->repo, c_subpath);
|
||||
Py_DECREF(subpath);
|
||||
if (!submodule) {
|
||||
PyErr_SetString(PyExc_KeyError,
|
||||
"Submodule does not exist");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = git_submodule_update(submodule, init, &opts);
|
||||
if (err != 0) {
|
||||
return Error_set(err);
|
||||
}
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Repository_lookup_reference__doc__,
|
||||
"lookup_reference(name) -> Reference\n"
|
||||
@ -1107,7 +1484,7 @@ Repository_create_reference_direct(Repository *self, PyObject *args,
|
||||
if (err < 0)
|
||||
return NULL;
|
||||
|
||||
err = git_reference_create(&c_reference, self->repo, c_name, &oid, force, NULL, NULL);
|
||||
err = git_reference_create(&c_reference, self->repo, c_name, &oid, force, NULL);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
@ -1141,7 +1518,7 @@ Repository_create_reference_symbolic(Repository *self, PyObject *args,
|
||||
return NULL;
|
||||
|
||||
err = git_reference_symbolic_create(&c_reference, self->repo, c_name,
|
||||
c_target, force, NULL, NULL);
|
||||
c_target, force, NULL);
|
||||
if (err < 0)
|
||||
return Error_set(err);
|
||||
|
||||
@ -1186,7 +1563,7 @@ Repository_status(Repository *self)
|
||||
path = entry->head_to_index->old_file.path;
|
||||
else
|
||||
path = entry->index_to_workdir->old_file.path;
|
||||
status = PyLong_FromLong((long) entry->status);
|
||||
status = PyInt_FromLong((long) entry->status);
|
||||
|
||||
err = PyDict_SetItemString(dict, path, status);
|
||||
Py_CLEAR(status);
|
||||
@ -1228,7 +1605,7 @@ Repository_status_file(Repository *self, PyObject *value)
|
||||
free(path);
|
||||
return err_obj;
|
||||
}
|
||||
return PyLong_FromLong(status);
|
||||
return PyInt_FromLong(status);
|
||||
}
|
||||
|
||||
|
||||
@ -1401,8 +1778,8 @@ PyDoc_STRVAR(Repository_reset__doc__,
|
||||
"\n"
|
||||
"Resets current head to the provided oid.\n"
|
||||
"reset_type:\n"
|
||||
"GIT_RESET_SOFT: resets head to point to oid, but does not modfy working copy, and leaves the changes in the index.\n"
|
||||
"GIT_RESET_MIXED: resets head to point to oid, but does not modfy working copy. It empties the index too.\n"
|
||||
"GIT_RESET_SOFT: resets head to point to oid, but does not modify working copy, and leaves the changes in the index.\n"
|
||||
"GIT_RESET_MIXED: resets head to point to oid, but does not modify working copy. It empties the index too.\n"
|
||||
"GIT_RESET_HARD: resets head to point to oid, and resets too the working copy and the content of the index.\n");
|
||||
|
||||
PyObject *
|
||||
@ -1426,17 +1803,36 @@ Repository_reset(Repository *self, PyObject* args)
|
||||
|
||||
err = git_object_lookup_prefix(&target, self->repo, &oid, len,
|
||||
GIT_OBJ_ANY);
|
||||
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL, NULL, NULL);
|
||||
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL);
|
||||
git_object_free(target);
|
||||
if (err < 0)
|
||||
return Error_set_oid(err, &oid, len);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Repository_expand_id__doc__,
|
||||
"expand_id(hex) -> Oid\n"
|
||||
"\n"
|
||||
"Expand a string into a full Oid according to the objects in this repsitory.\n");
|
||||
|
||||
PyObject *
|
||||
Repository_expand_id(Repository *self, PyObject *py_hex)
|
||||
{
|
||||
git_oid oid;
|
||||
int err;
|
||||
|
||||
err = py_oid_to_git_oid_expand(self->repo, py_hex, &oid);
|
||||
if (err < 0)
|
||||
return NULL;
|
||||
|
||||
return git_oid_to_python(&oid);
|
||||
}
|
||||
|
||||
PyMethodDef Repository_methods[] = {
|
||||
METHOD(Repository, create_blob, METH_VARARGS),
|
||||
METHOD(Repository, create_blob_fromworkdir, METH_VARARGS),
|
||||
METHOD(Repository, create_blob_fromdisk, METH_VARARGS),
|
||||
METHOD(Repository, create_blob_fromiobase, METH_O),
|
||||
METHOD(Repository, create_commit, METH_VARARGS),
|
||||
METHOD(Repository, create_tag, METH_VARARGS),
|
||||
METHOD(Repository, TreeBuilder, METH_VARARGS),
|
||||
@ -1444,11 +1840,16 @@ PyMethodDef Repository_methods[] = {
|
||||
METHOD(Repository, merge_base, METH_VARARGS),
|
||||
METHOD(Repository, merge_analysis, METH_O),
|
||||
METHOD(Repository, merge, METH_O),
|
||||
METHOD(Repository, cherrypick, METH_O),
|
||||
METHOD(Repository, read, METH_O),
|
||||
METHOD(Repository, write, METH_VARARGS),
|
||||
METHOD(Repository, create_reference_direct, METH_VARARGS),
|
||||
METHOD(Repository, create_reference_symbolic, METH_VARARGS),
|
||||
METHOD(Repository, listall_references, METH_NOARGS),
|
||||
METHOD(Repository, listall_reference_objects, METH_NOARGS),
|
||||
METHOD(Repository, listall_submodules, METH_NOARGS),
|
||||
METHOD(Repository, init_submodules, METH_VARARGS | METH_KEYWORDS),
|
||||
METHOD(Repository, update_submodules, METH_VARARGS | METH_KEYWORDS),
|
||||
METHOD(Repository, lookup_reference, METH_O),
|
||||
METHOD(Repository, revparse_single, METH_O),
|
||||
METHOD(Repository, status, METH_NOARGS),
|
||||
@ -1458,9 +1859,11 @@ 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, reset, METH_VARARGS),
|
||||
METHOD(Repository, expand_id, METH_O),
|
||||
METHOD(Repository, _from_c, METH_VARARGS),
|
||||
METHOD(Repository, _disown, METH_NOARGS),
|
||||
{NULL}
|
||||
@ -1481,7 +1884,7 @@ PyGetSetDef Repository_getseters[] = {
|
||||
|
||||
|
||||
PyDoc_STRVAR(Repository__doc__,
|
||||
"Repository(path) -> Repository\n"
|
||||
"Repository(backend) -> Repository\n"
|
||||
"\n"
|
||||
"Git repository.");
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -33,6 +33,8 @@
|
||||
#include <git2.h>
|
||||
#include "types.h"
|
||||
|
||||
PyObject *wrap_repository(git_repository *c_repo);
|
||||
|
||||
int Repository_init(Repository *self, PyObject *args, PyObject *kwds);
|
||||
int Repository_traverse(Repository *self, visitproc visit, void *arg);
|
||||
int Repository_clear(Repository *self);
|
||||
@ -56,6 +58,8 @@ PyObject* Repository_create_commit(Repository *self, PyObject *args);
|
||||
PyObject* Repository_create_tag(Repository *self, PyObject *args);
|
||||
PyObject* Repository_create_branch(Repository *self, PyObject *args);
|
||||
PyObject* Repository_listall_references(Repository *self, PyObject *args);
|
||||
PyObject* Repository_listall_reference_objects(Repository *self,
|
||||
PyObject *args);
|
||||
PyObject* Repository_listall_branches(Repository *self, PyObject *args);
|
||||
PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name);
|
||||
|
||||
@ -70,5 +74,6 @@ PyObject* Repository_TreeBuilder(Repository *self, PyObject *args);
|
||||
PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds);
|
||||
|
||||
PyObject* Repository_merge(Repository *self, PyObject *py_oid);
|
||||
PyObject* Repository_cherrypick(Repository *self, PyObject *py_oid);
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -38,7 +38,7 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
char *keywords[] = {"name", "email", "time", "offset", "encoding", NULL};
|
||||
PyObject *py_name, *tname;
|
||||
char *email, *encoding = "ascii";
|
||||
char *email, *encoding = "utf-8";
|
||||
const char *name;
|
||||
long long time = -1;
|
||||
int offset = 0;
|
||||
@ -96,10 +96,10 @@ Signature_dealloc(Signature *self)
|
||||
|
||||
PyDoc_STRVAR(Signature__pointer__doc__, "Get the signature's pointer. For internal use only.");
|
||||
PyObject *
|
||||
Signature__pointer__get__(Repository *self)
|
||||
Signature__pointer__get__(Signature *self)
|
||||
{
|
||||
/* Bytes means a raw buffer */
|
||||
return PyBytes_FromStringAndSize((char *) &self->repo, sizeof(git_repository *));
|
||||
return PyBytes_FromStringAndSize((char *) &self->signature, sizeof(git_signature *));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(Signature__encoding__doc__, "Encoding.");
|
||||
@ -158,7 +158,7 @@ PyDoc_STRVAR(Signature_time__doc__, "Unix time.");
|
||||
PyObject *
|
||||
Signature_time__get__(Signature *self)
|
||||
{
|
||||
return PyLong_FromLongLong(self->signature->when.time);
|
||||
return PyInt_FromLongLong(self->signature->when.time);
|
||||
}
|
||||
|
||||
|
||||
@ -167,7 +167,7 @@ PyDoc_STRVAR(Signature_offset__doc__, "Offset from UTC in minutes.");
|
||||
PyObject *
|
||||
Signature_offset__get__(Signature *self)
|
||||
{
|
||||
return PyLong_FromLong(self->signature->when.offset);
|
||||
return PyInt_FromLong(self->signature->when.offset);
|
||||
}
|
||||
|
||||
PyGetSetDef Signature_getseters[] = {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -111,7 +111,11 @@ PyDoc_STRVAR(Tag__message__doc__, "Tag message (bytes).");
|
||||
PyObject *
|
||||
Tag__message__get__(Tag *self)
|
||||
{
|
||||
return PyBytes_FromString(git_tag_message(self->tag));
|
||||
const char *message;
|
||||
message = git_tag_message(self->tag);
|
||||
if (!message)
|
||||
Py_RETURN_NONE;
|
||||
return PyBytes_FromString(message);
|
||||
}
|
||||
|
||||
PyMethodDef Tag_methods[] = {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
60
src/tree.c
60
src/tree.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -54,7 +54,7 @@ PyDoc_STRVAR(TreeEntry_filemode__doc__, "Filemode.");
|
||||
PyObject *
|
||||
TreeEntry_filemode__get__(TreeEntry *self)
|
||||
{
|
||||
return PyLong_FromLong(git_tree_entry_filemode(self->entry));
|
||||
return PyInt_FromLong(git_tree_entry_filemode(self->entry));
|
||||
}
|
||||
|
||||
|
||||
@ -67,6 +67,24 @@ TreeEntry_name__get__(TreeEntry *self)
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(TreeEntry__name__doc__, "Name (bytes).");
|
||||
|
||||
PyObject *
|
||||
TreeEntry__name__get__(TreeEntry *self)
|
||||
{
|
||||
return PyBytes_FromString(git_tree_entry_name(self->entry));
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(TreeEntry_type__doc__, "Type.");
|
||||
|
||||
PyObject *
|
||||
TreeEntry_type__get__(TreeEntry *self)
|
||||
{
|
||||
return to_path(git_object_type2string(git_tree_entry_type(self->entry)));
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(TreeEntry_id__doc__, "Object id.");
|
||||
|
||||
PyObject *
|
||||
@ -87,10 +105,20 @@ TreeEntry_oid__get__(TreeEntry *self)
|
||||
return TreeEntry_id__get__(self);
|
||||
}
|
||||
|
||||
static int
|
||||
compare_ids(TreeEntry *a, TreeEntry *b)
|
||||
{
|
||||
const git_oid *id_a, *id_b;
|
||||
id_a = git_tree_entry_id(a->entry);
|
||||
id_b = git_tree_entry_id(b->entry);
|
||||
return git_oid_cmp(id_a, id_b);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
TreeEntry_richcompare(PyObject *a, PyObject *b, int op)
|
||||
{
|
||||
PyObject *res;
|
||||
TreeEntry *ta, *tb;
|
||||
int cmp;
|
||||
|
||||
/* We only support comparing to another tree entry */
|
||||
@ -99,7 +127,14 @@ TreeEntry_richcompare(PyObject *a, PyObject *b, int op)
|
||||
return Py_NotImplemented;
|
||||
}
|
||||
|
||||
cmp =git_tree_entry_cmp(((TreeEntry*)a)->entry, ((TreeEntry*)b)->entry);
|
||||
ta = (TreeEntry *) a;
|
||||
tb = (TreeEntry *) b;
|
||||
|
||||
/* This is sorting order, if they sort equally, we still need to compare the ids */
|
||||
cmp = git_tree_entry_cmp(ta->entry, tb->entry);
|
||||
if (cmp == 0)
|
||||
cmp = compare_ids(ta, tb);
|
||||
|
||||
switch (op) {
|
||||
case Py_LT:
|
||||
res = (cmp <= 0) ? Py_True: Py_False;
|
||||
@ -137,17 +172,28 @@ TreeEntry_hex__get__(TreeEntry *self)
|
||||
return git_oid_to_py_str(git_tree_entry_id(self->entry));
|
||||
}
|
||||
|
||||
PyObject *
|
||||
TreeEntry_repr(TreeEntry *self)
|
||||
{
|
||||
char str[GIT_OID_HEXSZ + 1] = { 0 };
|
||||
const char *typename;
|
||||
|
||||
typename = git_object_type2string(git_tree_entry_type(self->entry));
|
||||
git_oid_fmt(str, git_tree_entry_id(self->entry));
|
||||
return PyString_FromFormat("pygit2.TreeEntry('%s', %s, %s)", git_tree_entry_name(self->entry), typename, str);
|
||||
}
|
||||
|
||||
PyGetSetDef TreeEntry_getseters[] = {
|
||||
GETTER(TreeEntry, filemode),
|
||||
GETTER(TreeEntry, name),
|
||||
GETTER(TreeEntry, _name),
|
||||
GETTER(TreeEntry, oid),
|
||||
GETTER(TreeEntry, id),
|
||||
GETTER(TreeEntry, hex),
|
||||
GETTER(TreeEntry, type),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
PyDoc_STRVAR(TreeEntry__doc__, "TreeEntry objects.");
|
||||
|
||||
PyTypeObject TreeEntryType = {
|
||||
@ -160,7 +206,7 @@ PyTypeObject TreeEntryType = {
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
(reprfunc)TreeEntry_repr, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
@ -244,7 +290,7 @@ Tree_fix_index(Tree *self, PyObject *py_index)
|
||||
size_t len;
|
||||
long slen;
|
||||
|
||||
index = PyLong_AsLong(py_index);
|
||||
index = PyInt_AsLong(py_index);
|
||||
if (PyErr_Occurred())
|
||||
return -1;
|
||||
|
||||
@ -313,7 +359,7 @@ Tree_getitem(Tree *self, PyObject *value)
|
||||
int err;
|
||||
|
||||
/* Case 1: integer */
|
||||
if (PyLong_Check(value))
|
||||
if (PyInt_Check(value))
|
||||
return Tree_getitem_by_index(self, value);
|
||||
|
||||
/* Case 2: byte or text string */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
59
src/types.h
59
src/types.h
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -32,8 +32,8 @@
|
||||
#include <Python.h>
|
||||
#include <git2.h>
|
||||
|
||||
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 22)
|
||||
#error You need a compatible libgit2 version (v0.22.x)
|
||||
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 26)
|
||||
#error You need a compatible libgit2 version (v0.26.x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -90,6 +90,12 @@ 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)
|
||||
@ -103,17 +109,22 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject* hunks;
|
||||
char * old_file_path;
|
||||
char * new_file_path;
|
||||
PyObject* old_id;
|
||||
PyObject* new_id;
|
||||
char status;
|
||||
unsigned similarity;
|
||||
unsigned additions;
|
||||
unsigned deletions;
|
||||
unsigned flags;
|
||||
} Patch;
|
||||
PyObject *id;
|
||||
char *path;
|
||||
git_off_t size;
|
||||
uint32_t flags;
|
||||
uint16_t mode;
|
||||
} DiffFile;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
git_delta_t status;
|
||||
uint32_t flags;
|
||||
uint16_t similarity;
|
||||
uint16_t nfiles;
|
||||
PyObject *old_file;
|
||||
PyObject *new_file;
|
||||
} DiffDelta;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
@ -122,8 +133,20 @@ typedef struct {
|
||||
int old_lines;
|
||||
int new_start;
|
||||
int new_lines;
|
||||
} Hunk;
|
||||
PyObject *header;
|
||||
} DiffHunk;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
char origin;
|
||||
int old_lineno;
|
||||
int new_lineno;
|
||||
int num_lines;
|
||||
git_off_t content_offset;
|
||||
PyObject *content;
|
||||
} DiffLine;
|
||||
|
||||
SIMPLE_TYPE(DiffStats, git_diff_stats, stats);
|
||||
|
||||
/* git_tree_walk , git_treebuilder*/
|
||||
SIMPLE_TYPE(TreeBuilder, git_treebuilder, bld)
|
||||
@ -148,12 +171,6 @@ typedef struct {
|
||||
git_index_entry entry;
|
||||
} IndexEntry;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
Index *owner;
|
||||
int i;
|
||||
} IndexIter;
|
||||
|
||||
|
||||
/* git_reference, git_reflog */
|
||||
SIMPLE_TYPE(Walker, git_revwalk, walk)
|
||||
|
56
src/utils.c
56
src/utils.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -83,8 +83,7 @@ py_str_borrow_c_str(PyObject **tvalue, PyObject *value, const char *encoding)
|
||||
}
|
||||
|
||||
/* Type error */
|
||||
PyErr_Format(PyExc_TypeError, "unexpected %.200s",
|
||||
Py_TYPE(value)->tp_name);
|
||||
Error_type_error("unexpected %.200s", value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -94,7 +93,7 @@ py_str_borrow_c_str(PyObject **tvalue, PyObject *value, const char *encoding)
|
||||
PyObject *
|
||||
get_pylist_from_git_strarray(git_strarray *strarray)
|
||||
{
|
||||
int index;
|
||||
size_t index;
|
||||
PyObject *new_list;
|
||||
|
||||
new_list = PyList_New(strarray->count);
|
||||
@ -161,40 +160,39 @@ on_error:
|
||||
static git_otype
|
||||
py_type_to_git_type(PyTypeObject *py_type)
|
||||
{
|
||||
git_otype type = GIT_OBJ_BAD;
|
||||
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;
|
||||
|
||||
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;
|
||||
PyErr_SetString(PyExc_ValueError, "invalid target type");
|
||||
return GIT_OBJ_BAD; /* -1 */
|
||||
}
|
||||
|
||||
int
|
||||
py_object_to_object_type(PyObject *py_type)
|
||||
git_otype
|
||||
py_object_to_otype(PyObject *py_type)
|
||||
{
|
||||
int type = -1;
|
||||
long value;
|
||||
|
||||
if (py_type == Py_None)
|
||||
return GIT_OBJ_ANY;
|
||||
|
||||
if (PyLong_Check(py_type)) {
|
||||
type = PyLong_AsLong(py_type);
|
||||
if (type == -1 && PyErr_Occurred())
|
||||
return -1;
|
||||
} else if (PyType_Check(py_type)) {
|
||||
type = py_type_to_git_type((PyTypeObject *) py_type);
|
||||
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 (type == -1) {
|
||||
PyErr_SetString(PyExc_ValueError, "invalid target type");
|
||||
}
|
||||
if (PyType_Check(py_type))
|
||||
return py_type_to_git_type((PyTypeObject *) py_type);
|
||||
|
||||
return type;
|
||||
PyErr_SetString(PyExc_ValueError, "invalid target type");
|
||||
return GIT_OBJ_BAD; /* -1 */
|
||||
}
|
||||
|
38
src/utils.h
38
src/utils.h
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -40,14 +40,17 @@
|
||||
#endif
|
||||
|
||||
/* Python 2 support */
|
||||
#ifndef Py_hash_t
|
||||
#define Py_hash_t long
|
||||
#endif
|
||||
|
||||
#ifndef PyLong_AsSize_t
|
||||
#define PyLong_AsSize_t (size_t)PyLong_AsSsize_t
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION == 2
|
||||
#define PyLong_FromSize_t PyInt_FromSize_t
|
||||
#define PyLong_AsSize_t (size_t)PyInt_AsSsize_t
|
||||
#define PyLong_AsLong PyInt_AsLong
|
||||
#undef PyLong_Check
|
||||
#define PyLong_Check PyInt_Check
|
||||
#define PyLong_FromLong PyInt_FromLong
|
||||
#define PyInteger_Type PyInt_Type
|
||||
#define PyInt_AsSize_t (size_t)PyInt_AsLong
|
||||
#define PyInt_FromLongLong PyInt_FromLong
|
||||
#define PyBytes_AS_STRING PyString_AS_STRING
|
||||
#define PyBytes_AsString PyString_AsString
|
||||
#define PyBytes_AsStringAndSize PyString_AsStringAndSize
|
||||
@ -58,17 +61,16 @@
|
||||
#define to_path(x) to_bytes(x)
|
||||
#define to_encoding(x) to_bytes(x)
|
||||
#else
|
||||
#define PyInteger_Type PyLong_Type
|
||||
#define PyInt_Check PyLong_Check
|
||||
#define PyInt_FromSize_t PyLong_FromSize_t
|
||||
#define PyInt_FromLong PyLong_FromLong
|
||||
#define PyInt_FromLongLong PyLong_FromLongLong
|
||||
#define PyInt_AsLong PyLong_AsLong
|
||||
#define PyInt_AsSize_t PyLong_AsSize_t
|
||||
|
||||
#define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict")
|
||||
#define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict")
|
||||
#endif
|
||||
|
||||
#ifdef PYPY_VERSION
|
||||
#define PyLong_AsSize_t (size_t)PyLong_AsUnsignedLong
|
||||
#endif
|
||||
|
||||
#ifndef Py_hash_t
|
||||
#define Py_hash_t long
|
||||
#define PyString_FromFormat(s, ...) PyUnicode_FromFormat(s, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
|
||||
@ -119,7 +121,7 @@ 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_object_type(PyObject *py_type);
|
||||
int py_object_to_otype(PyObject *py_type);
|
||||
|
||||
#define py_path_to_c_str(py_path) \
|
||||
py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
@ -98,13 +98,13 @@ PyDoc_STRVAR(Walker_sort__doc__,
|
||||
PyObject *
|
||||
Walker_sort(Walker *self, PyObject *py_sort_mode)
|
||||
{
|
||||
int sort_mode;
|
||||
long sort_mode;
|
||||
|
||||
sort_mode = (int)PyLong_AsLong(py_sort_mode);
|
||||
sort_mode = PyInt_AsLong(py_sort_mode);
|
||||
if (sort_mode == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
|
||||
git_revwalk_sorting(self->walk, sort_mode);
|
||||
git_revwalk_sorting(self->walk, (unsigned int)sort_mode);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 The pygit2 contributors
|
||||
* Copyright 2010-2017 The pygit2 contributors
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
|
BIN
test/data/binaryfilerepo.tar
Normal file
BIN
test/data/binaryfilerepo.tar
Normal file
Binary file not shown.
BIN
test/data/submodulerepo.tar
Normal file
BIN
test/data/submodulerepo.tar
Normal file
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
|
65
test/test_attributes.py
Normal file
65
test/test_attributes.py
Normal file
@ -0,0 +1,65 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# In addition to the permissions in the GNU General Public License,
|
||||
# the authors give you unlimited permission to link the compiled
|
||||
# version of this file into combinations with other programs,
|
||||
# and to distribute those combinations without any restriction
|
||||
# coming from the use of this file. (The General Public License
|
||||
# restrictions do apply in other respects; for example, they cover
|
||||
# modification of the file, and distribution when not linked into
|
||||
# a combined executable.)
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
|
||||
# Import from the future
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals, print_function
|
||||
|
||||
# Import from the Standard Library
|
||||
import binascii
|
||||
import unittest
|
||||
import tempfile
|
||||
import os
|
||||
from os.path import join, realpath
|
||||
import sys
|
||||
|
||||
# Import from pygit2
|
||||
from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT
|
||||
from pygit2 import init_repository, clone_repository, discover_repository
|
||||
from pygit2 import Oid, Reference, hashfile
|
||||
import pygit2
|
||||
from . import utils
|
||||
|
||||
try:
|
||||
import __pypy__
|
||||
except ImportError:
|
||||
__pypy__ = None
|
||||
|
||||
class RepositorySignatureTest(utils.RepoTestCase):
|
||||
|
||||
def test_no_attr(self):
|
||||
self.assertIsNone(self.repo.get_attr('file', 'foo'))
|
||||
|
||||
with open(join(self.repo.workdir, '.gitattributes'), 'w+') as f:
|
||||
print('*.py text\n', file=f)
|
||||
print('*.jpg -text\n', file=f)
|
||||
print('*.sh eol=lf\n', file=f)
|
||||
|
||||
self.assertIsNone(self.repo.get_attr('file.py', 'foo'))
|
||||
self.assertTrue(self.repo.get_attr('file.py', 'text'))
|
||||
self.assertFalse(self.repo.get_attr('file.jpg', 'text'))
|
||||
self.assertEqual("lf", self.repo.get_attr('file.sh', 'eol'))
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -30,6 +30,7 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from os.path import dirname, join
|
||||
import io
|
||||
import unittest
|
||||
|
||||
import pygit2
|
||||
@ -112,6 +113,19 @@ class BlobTest(utils.RepoTestCase):
|
||||
self.assertTrue(isinstance(blob, pygit2.Blob))
|
||||
self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type)
|
||||
|
||||
def test_create_blob_fromiobase(self):
|
||||
self.assertRaises(TypeError, self.repo.create_blob_fromiobase, 'bad type')
|
||||
|
||||
f = io.BytesIO(BLOB_CONTENT)
|
||||
blob_oid = self.repo.create_blob_fromiobase(f)
|
||||
blob = self.repo[blob_oid]
|
||||
|
||||
self.assertTrue(isinstance(blob, pygit2.Blob))
|
||||
self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type)
|
||||
|
||||
self.assertEqual(blob_oid, blob.id)
|
||||
self.assertEqual(BLOB_SHA, blob_oid.hex)
|
||||
|
||||
def test_diff_blob(self):
|
||||
blob = self.repo[BLOB_SHA]
|
||||
old_blob = self.repo['3b18e512dba79e4c8300dd08aeb37f8e728b8dad']
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -39,6 +39,135 @@ I18N_LAST_COMMIT = '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'
|
||||
ORIGIN_MASTER_COMMIT = '784855caf26449a1914d2cf62d12b9374d76ae78'
|
||||
|
||||
|
||||
class BranchesObjectTestCase(utils.RepoTestCase):
|
||||
def test_lookup_branch_local(self):
|
||||
branch = self.repo.branches['master']
|
||||
self.assertEqual(branch.target.hex, LAST_COMMIT)
|
||||
|
||||
branch = self.repo.branches.local['i18n']
|
||||
self.assertEqual(branch.target.hex, I18N_LAST_COMMIT)
|
||||
|
||||
self.assertTrue(self.repo.branches.get('not-exists') is None)
|
||||
|
||||
self.assertRaises(KeyError, lambda: self.repo.branches['not-exists'])
|
||||
|
||||
def test_listall_branches(self):
|
||||
branches = sorted(self.repo.branches)
|
||||
self.assertEqual(branches, ['i18n', 'master'])
|
||||
|
||||
def test_create_branch(self):
|
||||
commit = self.repo[LAST_COMMIT]
|
||||
reference = self.repo.branches.create('version1', commit)
|
||||
self.assertTrue('version1' in self.repo.branches)
|
||||
reference = self.repo.branches['version1']
|
||||
self.assertEqual(reference.target.hex, LAST_COMMIT)
|
||||
|
||||
# try to create existing reference
|
||||
self.assertRaises(ValueError,
|
||||
lambda: self.repo.branches.create('version1', commit))
|
||||
|
||||
# try to create existing reference with force
|
||||
reference = self.repo.branches.create('version1', commit, True)
|
||||
self.assertEqual(reference.target.hex, LAST_COMMIT)
|
||||
|
||||
def test_delete(self):
|
||||
self.repo.branches.delete('i18n')
|
||||
|
||||
self.assertTrue(self.repo.branches.get('i18n') is None)
|
||||
|
||||
def test_cant_delete_master(self):
|
||||
self.assertRaises(pygit2.GitError, lambda: self.repo.branches.delete('master'))
|
||||
|
||||
def test_branch_is_head_returns_true_if_branch_is_head(self):
|
||||
branch = self.repo.branches.get('master')
|
||||
self.assertTrue(branch.is_head())
|
||||
|
||||
def test_branch_is_head_returns_false_if_branch_is_not_head(self):
|
||||
branch = self.repo.branches.get('i18n')
|
||||
self.assertFalse(branch.is_head())
|
||||
|
||||
def test_branch_rename_succeeds(self):
|
||||
new_branch = self.repo.branches['i18n'].rename('new-branch')
|
||||
self.assertEqual(new_branch.target.hex, I18N_LAST_COMMIT)
|
||||
|
||||
new_branch_2 = self.repo.branches.get('new-branch')
|
||||
self.assertEqual(new_branch_2.target.hex, I18N_LAST_COMMIT)
|
||||
|
||||
def test_branch_rename_fails_if_destination_already_exists(self):
|
||||
original_branch = self.repo.branches.get('i18n')
|
||||
self.assertRaises(ValueError, lambda: original_branch.rename('master'))
|
||||
|
||||
def test_branch_rename_not_fails_if_force_is_true(self):
|
||||
original_branch = self.repo.branches.get('master')
|
||||
new_branch = original_branch.rename('i18n', True)
|
||||
self.assertEqual(new_branch.target.hex, LAST_COMMIT)
|
||||
|
||||
def test_branch_rename_fails_with_invalid_names(self):
|
||||
original_branch = self.repo.branches.get('i18n')
|
||||
self.assertRaises(ValueError,
|
||||
lambda: original_branch.rename('abc@{123'))
|
||||
|
||||
def test_branch_name(self):
|
||||
branch = self.repo.branches.get('master')
|
||||
self.assertEqual(branch.branch_name, 'master')
|
||||
self.assertEqual(branch.name, 'refs/heads/master')
|
||||
|
||||
branch = self.repo.branches.get('i18n')
|
||||
self.assertEqual(branch.branch_name, 'i18n')
|
||||
self.assertEqual(branch.name, 'refs/heads/i18n')
|
||||
|
||||
|
||||
class BranchesObjectEmptyRepoTestCase(utils.EmptyRepoTestCase):
|
||||
def setUp(self):
|
||||
super(utils.EmptyRepoTestCase, self).setUp()
|
||||
|
||||
remote = self.repo.remotes[0]
|
||||
remote.fetch()
|
||||
|
||||
def test_lookup_branch_remote(self):
|
||||
branch = self.repo.branches.remote.get('origin/master')
|
||||
self.assertEqual(branch.target.hex, ORIGIN_MASTER_COMMIT)
|
||||
|
||||
self.assertTrue(
|
||||
self.repo.branches.remote.get('origin/not-exists') is None)
|
||||
|
||||
def test_listall_branches(self):
|
||||
branches = sorted(self.repo.branches.remote)
|
||||
self.assertEqual(branches, ['origin/master'])
|
||||
|
||||
def test_branch_remote_name(self):
|
||||
self.repo.remotes[0].fetch()
|
||||
branch = self.repo.branches.remote['origin/master']
|
||||
self.assertEqual(branch.remote_name, 'origin')
|
||||
|
||||
def test_branch_upstream(self):
|
||||
self.repo.remotes[0].fetch()
|
||||
remote_master = self.repo.branches.remote['origin/master']
|
||||
master = self.repo.branches.create('master',
|
||||
self.repo[remote_master.target.hex])
|
||||
|
||||
self.assertTrue(master.upstream is None)
|
||||
master.upstream = remote_master
|
||||
self.assertEqual(master.upstream.branch_name, 'origin/master')
|
||||
|
||||
def set_bad_upstream():
|
||||
master.upstream = 2.5
|
||||
|
||||
self.assertRaises(TypeError, set_bad_upstream)
|
||||
|
||||
master.upstream = None
|
||||
self.assertTrue(master.upstream is None)
|
||||
|
||||
def test_branch_upstream_name(self):
|
||||
self.repo.remotes[0].fetch()
|
||||
remote_master = self.repo.branches.remote['origin/master']
|
||||
master = self.repo.branches.create('master',
|
||||
self.repo[remote_master.target.hex])
|
||||
|
||||
master.upstream = remote_master
|
||||
self.assertEqual(master.upstream_name, 'refs/remotes/origin/master')
|
||||
|
||||
|
||||
class BranchesTestCase(utils.RepoTestCase):
|
||||
def test_lookup_branch_local(self):
|
||||
branch = self.repo.lookup_branch('master')
|
||||
@ -88,6 +217,14 @@ class BranchesTestCase(utils.RepoTestCase):
|
||||
branch = self.repo.lookup_branch('i18n')
|
||||
self.assertFalse(branch.is_head())
|
||||
|
||||
def test_branch_is_checked_out_returns_true_if_branch_is_checked_out(self):
|
||||
branch = self.repo.lookup_branch('master')
|
||||
self.assertTrue(branch.is_checked_out())
|
||||
|
||||
def test_branch_is_checked_out_returns_false_if_branch_is_not_checked_out(self):
|
||||
branch = self.repo.lookup_branch('i18n')
|
||||
self.assertFalse(branch.is_checked_out())
|
||||
|
||||
def test_branch_rename_succeeds(self):
|
||||
original_branch = self.repo.lookup_branch('i18n')
|
||||
new_branch = original_branch.rename('new-branch')
|
||||
@ -159,6 +296,7 @@ class BranchesEmptyRepoTestCase(utils.EmptyRepoTestCase):
|
||||
|
||||
def set_bad_upstream():
|
||||
master.upstream = 2.5
|
||||
|
||||
self.assertRaises(TypeError, set_bad_upstream)
|
||||
|
||||
master.upstream = None
|
||||
|
71
test/test_cherrypick.py
Normal file
71
test/test_cherrypick.py
Normal file
@ -0,0 +1,71 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# In addition to the permissions in the GNU General Public License,
|
||||
# the authors give you unlimited permission to link the compiled
|
||||
# version of this file into combinations with other programs,
|
||||
# and to distribute those combinations without any restriction
|
||||
# coming from the use of this file. (The General Public License
|
||||
# restrictions do apply in other respects; for example, they cover
|
||||
# modification of the file, and distribution when not linked into
|
||||
# a combined executable.)
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
|
||||
"""Tests for merging and information about it."""
|
||||
|
||||
# Import from the future
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
import os
|
||||
|
||||
from pygit2 import GIT_MERGE_ANALYSIS_NONE, GIT_MERGE_ANALYSIS_NORMAL, GIT_MERGE_ANALYSIS_UP_TO_DATE
|
||||
from pygit2 import GIT_MERGE_ANALYSIS_FASTFORWARD, GIT_MERGE_ANALYSIS_UNBORN
|
||||
import pygit2
|
||||
|
||||
from . import utils
|
||||
|
||||
class CherrypickTestBasic(utils.RepoTestCaseForMerging):
|
||||
|
||||
def test_cherrypick_none(self):
|
||||
self.assertRaises(TypeError, self.repo.cherrypick, None)
|
||||
|
||||
def test_cherrypick_invalid_hex(self):
|
||||
branch_head_hex = '12345678'
|
||||
self.assertRaises(KeyError, self.repo.cherrypick, branch_head_hex)
|
||||
|
||||
def test_cherrypick_already_something_in_index(self):
|
||||
branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1'
|
||||
branch_oid = self.repo.get(branch_head_hex).id
|
||||
with open(os.path.join(self.repo.workdir, 'inindex.txt'), 'w') as f:
|
||||
f.write('new content')
|
||||
self.repo.index.add('inindex.txt')
|
||||
self.assertRaises(pygit2.GitError, self.repo.cherrypick, branch_oid)
|
||||
|
||||
class CherrypickTestWithConflicts(utils.RepoTestCaseForMerging):
|
||||
|
||||
def test_cherrypick_remove_conflicts(self):
|
||||
other_branch_tip = '1b2bae55ac95a4be3f8983b86cd579226d0eb247'
|
||||
self.repo.cherrypick(other_branch_tip)
|
||||
idx = self.repo.index
|
||||
conflicts = idx.conflicts
|
||||
self.assertTrue(conflicts is not None)
|
||||
conflicts['.gitignore']
|
||||
del idx.conflicts['.gitignore']
|
||||
self.assertRaises(KeyError, conflicts.__getitem__, '.gitignore')
|
||||
self.assertTrue(idx.conflicts is None)
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -97,7 +97,7 @@ class ConfigTest(utils.RepoTestCase):
|
||||
|
||||
self.assertRaises(TypeError, lambda: config[()])
|
||||
self.assertRaises(TypeError, lambda: config[-4])
|
||||
self.assertRaisesWithArg(ValueError, "Invalid config item name 'abc'",
|
||||
self.assertRaisesWithArg(ValueError, "invalid config item name 'abc'",
|
||||
lambda: config['abc'])
|
||||
self.assertRaisesWithArg(KeyError, 'abc.def',
|
||||
lambda: config['abc.def'])
|
||||
@ -153,7 +153,7 @@ class ConfigTest(utils.RepoTestCase):
|
||||
new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n")
|
||||
new_file.close()
|
||||
|
||||
config.add_file(CONFIG_FILENAME, 5)
|
||||
config.add_file(CONFIG_FILENAME, 6)
|
||||
self.assertTrue('this.that' in config)
|
||||
l = config.get_multivar('this.that', 'foo.*')
|
||||
self.assertEqual(2, len(list(l)))
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -70,32 +70,37 @@ class CredentialCreateTest(utils.NoRepoTestCase):
|
||||
|
||||
class CredentialCallback(utils.RepoTestCase):
|
||||
def test_callback(self):
|
||||
def credentials_cb(url, username, allowed):
|
||||
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
|
||||
raise Exception("I don't know the password")
|
||||
class MyCallbacks(pygit2.RemoteCallbacks):
|
||||
@staticmethod
|
||||
def credentials(url, username, allowed):
|
||||
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
|
||||
raise Exception("I don't know the password")
|
||||
|
||||
remote = self.repo.create_remote("github", "https://github.com/github/github")
|
||||
remote.credentials = credentials_cb
|
||||
url = "https://github.com/github/github"
|
||||
remote = self.repo.create_remote("github", url)
|
||||
|
||||
self.assertRaises(Exception, remote.fetch)
|
||||
self.assertRaises(Exception, lambda: remote.fetch(callbacks=MyCallbacks()))
|
||||
|
||||
def test_bad_cred_type(self):
|
||||
def credentials_cb(url, username, allowed):
|
||||
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
|
||||
return Keypair("git", "foo.pub", "foo", "sekkrit")
|
||||
class MyCallbacks(pygit2.RemoteCallbacks):
|
||||
@staticmethod
|
||||
def credentials(url, username, allowed):
|
||||
self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT)
|
||||
return Keypair("git", "foo.pub", "foo", "sekkrit")
|
||||
|
||||
remote = self.repo.create_remote("github", "https://github.com/github/github")
|
||||
remote.credentials = credentials_cb
|
||||
|
||||
self.assertRaises(TypeError, remote.fetch)
|
||||
url = "https://github.com/github/github"
|
||||
remote = self.repo.create_remote("github", url)
|
||||
self.assertRaises(TypeError, lambda: remote.fetch(callbacks=MyCallbacks()))
|
||||
|
||||
class CallableCredentialTest(utils.RepoTestCase):
|
||||
|
||||
def test_user_pass(self):
|
||||
remote = self.repo.create_remote("bb", "https://bitbucket.org/libgit2/testgitrepository.git")
|
||||
remote.credentials = UserPass("libgit2", "libgit2")
|
||||
credentials = UserPass("libgit2", "libgit2")
|
||||
callbacks = pygit2.RemoteCallbacks(credentials=credentials)
|
||||
|
||||
remote.fetch()
|
||||
url = "https://bitbucket.org/libgit2/testgitrepository.git"
|
||||
remote = self.repo.create_remote("bb", url)
|
||||
remote.fetch(callbacks=callbacks)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
106
test/test_describe.py
Normal file
106
test/test_describe.py
Normal file
@ -0,0 +1,106 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# In addition to the permissions in the GNU General Public License,
|
||||
# the authors give you unlimited permission to link the compiled
|
||||
# version of this file into combinations with other programs,
|
||||
# and to distribute those combinations without any restriction
|
||||
# coming from the use of this file. (The General Public License
|
||||
# restrictions do apply in other respects; for example, they cover
|
||||
# modification of the file, and distribution when not linked into
|
||||
# a combined executable.)
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
|
||||
"""Tests for describing commits."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
|
||||
from pygit2 import GIT_DESCRIBE_DEFAULT, GIT_DESCRIBE_TAGS, GIT_DESCRIBE_ALL
|
||||
import pygit2
|
||||
from . import utils
|
||||
|
||||
|
||||
def add_tag(repo, name, target):
|
||||
message = 'Example tag.\n'
|
||||
tagger = pygit2.Signature('John Doe', 'jdoe@example.com', 12347, 0)
|
||||
|
||||
sha = repo.create_tag(name, target, pygit2.GIT_OBJ_COMMIT, tagger, message)
|
||||
return sha
|
||||
|
||||
|
||||
class DescribeTest(utils.RepoTestCase):
|
||||
|
||||
def test_describe(self):
|
||||
add_tag(self.repo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8')
|
||||
self.assertEqual('thetag-2-g2be5719', self.repo.describe())
|
||||
|
||||
def test_describe_without_ref(self):
|
||||
self.assertRaises(pygit2.GitError, self.repo.describe)
|
||||
|
||||
def test_describe_default_oid(self):
|
||||
self.assertEqual('2be5719', self.repo.describe(show_commit_oid_as_fallback=True))
|
||||
|
||||
def test_describe_strategies(self):
|
||||
self.assertEqual('heads/master', self.repo.describe(describe_strategy=GIT_DESCRIBE_ALL))
|
||||
|
||||
self.repo.create_reference('refs/tags/thetag', '4ec4389a8068641da2d6578db0419484972284c8')
|
||||
self.assertRaises(KeyError, self.repo.describe)
|
||||
self.assertEqual('thetag-2-g2be5719', self.repo.describe(describe_strategy=GIT_DESCRIBE_TAGS))
|
||||
|
||||
def test_describe_pattern(self):
|
||||
add_tag(self.repo, 'private/tag1', '5ebeeebb320790caf276b9fc8b24546d63316533')
|
||||
add_tag(self.repo, 'public/tag2', '4ec4389a8068641da2d6578db0419484972284c8')
|
||||
|
||||
self.assertEqual('public/tag2-2-g2be5719', self.repo.describe(pattern='public/*'))
|
||||
|
||||
def test_describe_committish(self):
|
||||
add_tag(self.repo, 'thetag', 'acecd5ea2924a4b900e7e149496e1f4b57976e51')
|
||||
self.assertEqual('thetag-4-g2be5719', self.repo.describe(committish='HEAD'))
|
||||
self.assertEqual('thetag-1-g5ebeeeb', self.repo.describe(committish='HEAD^'))
|
||||
|
||||
self.assertEqual('thetag-4-g2be5719', self.repo.describe(committish=self.repo.head))
|
||||
|
||||
self.assertEqual('thetag-1-g6aaa262', self.repo.describe(committish='6aaa262e655dd54252e5813c8e5acd7780ed097d'))
|
||||
self.assertEqual('thetag-1-g6aaa262', self.repo.describe(committish='6aaa262'))
|
||||
|
||||
def test_describe_follows_first_branch_only(self):
|
||||
add_tag(self.repo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8')
|
||||
self.assertRaises(KeyError, self.repo.describe, only_follow_first_parent=True)
|
||||
|
||||
def test_describe_abbreviated_size(self):
|
||||
add_tag(self.repo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8')
|
||||
self.assertEqual('thetag-2-g2be5719152d4f82c', self.repo.describe(abbreviated_size=16))
|
||||
self.assertEqual('thetag', self.repo.describe(abbreviated_size=0))
|
||||
|
||||
def test_describe_long_format(self):
|
||||
add_tag(self.repo, 'thetag', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98')
|
||||
self.assertEqual('thetag-0-g2be5719', self.repo.describe(always_use_long_format=True))
|
||||
|
||||
|
||||
class DescribeDirtyWorkdirTest(utils.DirtyRepoTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(utils.DirtyRepoTestCase, self).setUp()
|
||||
add_tag(self.repo, 'thetag', 'a763aa560953e7cfb87ccbc2f536d665aa4dff22')
|
||||
|
||||
def test_describe(self):
|
||||
self.assertEqual('thetag', self.repo.describe())
|
||||
|
||||
def test_describe_with_dirty_suffix(self):
|
||||
self.assertEqual('thetag-dirty', self.repo.describe(dirty_suffix='-dirty'))
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -33,6 +33,8 @@ 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
|
||||
|
||||
@ -62,6 +64,22 @@ 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',
|
||||
@ -100,6 +118,11 @@ 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):
|
||||
@ -107,11 +130,11 @@ class DiffDirtyTest(utils.DirtyRepoTestCase):
|
||||
|
||||
head = repo[repo.lookup_reference('HEAD').resolve().target]
|
||||
diff = head.tree.diff_to_index(repo.index)
|
||||
files = [patch.new_file_path for patch in diff]
|
||||
files = [patch.delta.new_file.path for patch in diff]
|
||||
self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files)
|
||||
|
||||
diff = repo.diff('HEAD', cached=True)
|
||||
files = [patch.new_file_path for patch in diff]
|
||||
files = [patch.delta.new_file.path for patch in diff]
|
||||
self.assertEqual(DIFF_HEAD_TO_INDEX_EXPECTED, files)
|
||||
|
||||
def test_workdir_to_tree(self):
|
||||
@ -119,16 +142,16 @@ class DiffDirtyTest(utils.DirtyRepoTestCase):
|
||||
head = repo[repo.lookup_reference('HEAD').resolve().target]
|
||||
|
||||
diff = head.tree.diff_to_workdir()
|
||||
files = [patch.new_file_path for patch in diff]
|
||||
files = [patch.delta.new_file.path for patch in diff]
|
||||
self.assertEqual(DIFF_HEAD_TO_WORKDIR_EXPECTED, files)
|
||||
|
||||
diff = repo.diff('HEAD')
|
||||
files = [patch.new_file_path for patch in diff]
|
||||
files = [patch.delta.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.new_file_path for patch in diff]
|
||||
files = [patch.delta.new_file.path for patch in diff]
|
||||
self.assertEqual(DIFF_INDEX_TO_WORK_EXPECTED, files)
|
||||
|
||||
|
||||
@ -145,15 +168,15 @@ class DiffTest(utils.BareRepoTestCase):
|
||||
head = repo[repo.lookup_reference('HEAD').resolve().target]
|
||||
|
||||
diff = self.repo.index.diff_to_tree(head.tree)
|
||||
files = [patch.new_file_path.split('/')[0] for patch in diff]
|
||||
files = [patch.delta.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.new_file_path.split('/')[0] for patch in diff]
|
||||
files = [patch.delta.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.new_file_path.split('/')[0] for patch in diff]
|
||||
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
|
||||
self.assertEqual([x.name for x in head.tree], files)
|
||||
|
||||
def test_diff_tree(self):
|
||||
@ -173,9 +196,9 @@ class DiffTest(utils.BareRepoTestCase):
|
||||
self.assertEqual(hunk.new_start, 1)
|
||||
self.assertEqual(hunk.new_lines, 1)
|
||||
|
||||
self.assertEqual(patch.old_file_path, 'a')
|
||||
self.assertEqual(patch.new_file_path, 'a')
|
||||
self.assertEqual(patch.is_binary, False)
|
||||
self.assertEqual(patch.delta.old_file.path, 'a')
|
||||
self.assertEqual(patch.delta.new_file.path, 'a')
|
||||
self.assertEqual(patch.delta.is_binary, False)
|
||||
|
||||
_test(commit_a.tree.diff_to_tree(commit_b.tree))
|
||||
_test(self.repo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2))
|
||||
@ -188,14 +211,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[0], lines)
|
||||
return map(lambda x: x.origin, lines)
|
||||
|
||||
entries = [p.new_file_path for p in diff]
|
||||
entries = [p.delta.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.new_file_path for p in diff_swaped]
|
||||
entries = [p.delta.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))
|
||||
|
||||
@ -231,13 +254,13 @@ class DiffTest(utils.BareRepoTestCase):
|
||||
self.assertTrue(diff_c is not None)
|
||||
|
||||
# assertIn / assertNotIn are 2.7 only
|
||||
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])
|
||||
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])
|
||||
|
||||
diff_b.merge(diff_c)
|
||||
|
||||
# assertIn is 2.7 only
|
||||
self.assertTrue('b' in [patch.new_file_path for patch in diff_b])
|
||||
self.assertTrue('b' in [patch.delta.new_file.path for patch in diff_b])
|
||||
|
||||
patch = diff_b[0]
|
||||
hunk = patch.hunks[0]
|
||||
@ -246,8 +269,8 @@ class DiffTest(utils.BareRepoTestCase):
|
||||
self.assertEqual(hunk.new_start, 1)
|
||||
self.assertEqual(hunk.new_lines, 1)
|
||||
|
||||
self.assertEqual(patch.old_file_path, 'a')
|
||||
self.assertEqual(patch.new_file_path, 'a')
|
||||
self.assertEqual(patch.delta.old_file.path, 'a')
|
||||
self.assertEqual(patch.delta.new_file.path, 'a')
|
||||
|
||||
def test_diff_patch(self):
|
||||
commit_a = self.repo[COMMIT_SHA1_1]
|
||||
@ -261,9 +284,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.old_id.hex,
|
||||
self.assertEqual(patch.delta.old_file.id.hex,
|
||||
'7f129fd57e31e935c6d60a0c794efe4e6927664b')
|
||||
self.assertEqual(patch.new_id.hex,
|
||||
self.assertEqual(patch.delta.new_file.id.hex,
|
||||
'af431f20fc541ed6d5afede3e2dc7160f6f01f16')
|
||||
|
||||
def test_hunk_content(self):
|
||||
@ -271,7 +294,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) for x in hunk.lines)
|
||||
lines = ('{0} {1}'.format(x.origin, x.content) for x in hunk.lines)
|
||||
self.assertEqual(HUNK_EXPECTED, ''.join(lines))
|
||||
|
||||
def test_find_similar(self):
|
||||
@ -282,9 +305,35 @@ 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.status != 'R', diff)
|
||||
self.assertAll(lambda x: x.delta.status != GIT_DELTA_RENAMED, diff)
|
||||
self.assertAll(lambda x: x.delta.status_char() != 'R', diff)
|
||||
diff.find_similar()
|
||||
self.assertAny(lambda x: x.status == 'R', diff)
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
@ -82,7 +82,7 @@ class MergeTestBasic(utils.RepoTestCaseForMerging):
|
||||
|
||||
self.repo.merge(branch_id)
|
||||
self.assertTrue(self.repo.index.conflicts is not None)
|
||||
status = pygit2.GIT_STATUS_WT_NEW | pygit2.GIT_STATUS_INDEX_DELETED
|
||||
status = pygit2.GIT_STATUS_CONFLICTED
|
||||
# Asking twice to assure the reference counting is correct
|
||||
self.assertEqual({'.gitignore': status}, self.repo.status())
|
||||
self.assertEqual({'.gitignore': status}, self.repo.status())
|
||||
@ -165,3 +165,29 @@ class MergeCommitsTest(utils.RepoTestCaseForMerging):
|
||||
self.assertTrue(merge_index.conflicts is None)
|
||||
|
||||
self.assertRaises(ValueError, self.repo.merge_commits, self.repo.head.target, branch_head_hex, favor='foo')
|
||||
|
||||
class MergeTreesTest(utils.RepoTestCaseForMerging):
|
||||
|
||||
def test_merge_trees(self):
|
||||
branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1'
|
||||
branch_id = self.repo.get(branch_head_hex).id
|
||||
ancestor_id = self.repo.merge_base(self.repo.head.target, branch_id)
|
||||
|
||||
merge_index = self.repo.merge_trees(ancestor_id, self.repo.head.target, branch_head_hex)
|
||||
self.assertTrue(merge_index.conflicts is None)
|
||||
merge_commits_tree = merge_index.write_tree(self.repo)
|
||||
|
||||
self.repo.merge(branch_id)
|
||||
index = self.repo.index
|
||||
self.assertTrue(index.conflicts is None)
|
||||
merge_tree = index.write_tree()
|
||||
|
||||
self.assertEqual(merge_tree, merge_commits_tree)
|
||||
|
||||
def test_merge_commits_favor(self):
|
||||
branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247'
|
||||
ancestor_id = self.repo.merge_base(self.repo.head.target, branch_head_hex)
|
||||
merge_index = self.repo.merge_trees(ancestor_id, self.repo.head.target, branch_head_hex, favor='ours')
|
||||
self.assertTrue(merge_index.conflicts is None)
|
||||
|
||||
self.assertRaises(ValueError, self.repo.merge_trees, ancestor_id, self.repo.head.target, branch_head_hex, favor='foo')
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright 2010-2014 The pygit2 contributors
|
||||
# Copyright 2010-2017 The pygit2 contributors
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License, version 2,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user