pygit2 1.19.1__tar.gz → 1.19.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. {pygit2-1.19.1 → pygit2-1.19.2}/AUTHORS.md +3 -0
  2. {pygit2-1.19.1 → pygit2-1.19.2}/CHANGELOG.md +19 -0
  3. {pygit2-1.19.1 → pygit2-1.19.2}/PKG-INFO +1 -1
  4. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/__init__.py +21 -2
  5. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/_build.py +1 -1
  6. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/_libgit2/ffi.pyi +12 -0
  7. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/_pygit2.pyi +2 -2
  8. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/_run.py +2 -0
  9. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/config.py +6 -6
  10. pygit2-1.19.2/pygit2/decl/filter.h +49 -0
  11. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/types.h +2 -0
  12. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/filter.py +99 -1
  13. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/repository.py +27 -0
  14. {pygit2-1.19.1 → pygit2-1.19.2}/pyproject.toml +2 -1
  15. {pygit2-1.19.1 → pygit2-1.19.2}/src/branch.c +1 -1
  16. {pygit2-1.19.1 → pygit2-1.19.2}/src/diff.c +2 -2
  17. {pygit2-1.19.1 → pygit2-1.19.2}/src/filter.c +2 -0
  18. {pygit2-1.19.1 → pygit2-1.19.2}/src/mailmap.c +1 -1
  19. {pygit2-1.19.1 → pygit2-1.19.2}/src/note.c +1 -1
  20. {pygit2-1.19.1 → pygit2-1.19.2}/src/object.c +1 -1
  21. {pygit2-1.19.1 → pygit2-1.19.2}/src/odb.c +51 -4
  22. {pygit2-1.19.1 → pygit2-1.19.2}/src/odb_backend.c +1 -1
  23. {pygit2-1.19.1 → pygit2-1.19.2}/src/patch.c +1 -1
  24. {pygit2-1.19.1 → pygit2-1.19.2}/src/pygit2.c +28 -45
  25. {pygit2-1.19.1 → pygit2-1.19.2}/src/refdb.c +3 -5
  26. {pygit2-1.19.1 → pygit2-1.19.2}/src/refdb_backend.c +1 -1
  27. {pygit2-1.19.1 → pygit2-1.19.2}/src/reference.c +1 -1
  28. {pygit2-1.19.1 → pygit2-1.19.2}/src/repository.c +1 -1
  29. {pygit2-1.19.1 → pygit2-1.19.2}/src/tag.c +1 -1
  30. {pygit2-1.19.1 → pygit2-1.19.2}/src/tree.c +1 -1
  31. {pygit2-1.19.1 → pygit2-1.19.2}/src/treebuilder.c +1 -1
  32. {pygit2-1.19.1 → pygit2-1.19.2}/src/utils.c +29 -0
  33. {pygit2-1.19.1 → pygit2-1.19.2}/src/utils.h +1 -0
  34. {pygit2-1.19.1 → pygit2-1.19.2}/src/walker.c +1 -1
  35. {pygit2-1.19.1 → pygit2-1.19.2}/src/worktree.c +1 -1
  36. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_config.py +29 -0
  37. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_filter.py +107 -13
  38. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_odb.py +18 -2
  39. {pygit2-1.19.1 → pygit2-1.19.2}/COPYING +0 -0
  40. {pygit2-1.19.1 → pygit2-1.19.2}/Makefile +0 -0
  41. {pygit2-1.19.1 → pygit2-1.19.2}/README.md +0 -0
  42. {pygit2-1.19.1 → pygit2-1.19.2}/SPONSORS.md +0 -0
  43. {pygit2-1.19.1 → pygit2-1.19.2}/build.ps1 +0 -0
  44. {pygit2-1.19.1 → pygit2-1.19.2}/build.sh +0 -0
  45. {pygit2-1.19.1 → pygit2-1.19.2}/build_tag.py +0 -0
  46. {pygit2-1.19.1 → pygit2-1.19.2}/mypy-stubtest.ini +0 -0
  47. {pygit2-1.19.1 → pygit2-1.19.2}/mypy.ini +0 -0
  48. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/blame.py +0 -0
  49. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/blob.py +0 -0
  50. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/branches.py +0 -0
  51. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/callbacks.py +0 -0
  52. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/credentials.py +0 -0
  53. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/attr.h +0 -0
  54. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/blame.h +0 -0
  55. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/buffer.h +0 -0
  56. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/callbacks.h +0 -0
  57. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/checkout.h +0 -0
  58. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/clone.h +0 -0
  59. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/commit.h +0 -0
  60. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/common.h +0 -0
  61. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/config.h +0 -0
  62. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/describe.h +0 -0
  63. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/diff.h +0 -0
  64. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/errors.h +0 -0
  65. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/graph.h +0 -0
  66. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/index.h +0 -0
  67. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/indexer.h +0 -0
  68. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/merge.h +0 -0
  69. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/net.h +0 -0
  70. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/oid.h +0 -0
  71. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/options.h +0 -0
  72. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/pack.h +0 -0
  73. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/proxy.h +0 -0
  74. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/refspec.h +0 -0
  75. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/remote.h +0 -0
  76. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/repository.h +0 -0
  77. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/revert.h +0 -0
  78. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/stash.h +0 -0
  79. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/strarray.h +0 -0
  80. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/submodule.h +0 -0
  81. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/transaction.h +0 -0
  82. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/decl/transport.h +0 -0
  83. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/enums.py +0 -0
  84. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/errors.py +0 -0
  85. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/ffi.py +0 -0
  86. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/index.py +0 -0
  87. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/legacyenums.py +0 -0
  88. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/options.py +0 -0
  89. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/packbuilder.py +0 -0
  90. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/py.typed +0 -0
  91. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/references.py +0 -0
  92. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/refspec.py +0 -0
  93. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/remotes.py +0 -0
  94. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/settings.py +0 -0
  95. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/submodules.py +0 -0
  96. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/transaction.py +0 -0
  97. {pygit2-1.19.1 → pygit2-1.19.2}/pygit2/utils.py +0 -0
  98. {pygit2-1.19.1 → pygit2-1.19.2}/pytest.ini +0 -0
  99. {pygit2-1.19.1 → pygit2-1.19.2}/requirements-test.txt +0 -0
  100. {pygit2-1.19.1 → pygit2-1.19.2}/requirements-typing.txt +0 -0
  101. {pygit2-1.19.1 → pygit2-1.19.2}/requirements-wheel.txt +0 -0
  102. {pygit2-1.19.1 → pygit2-1.19.2}/requirements.txt +0 -0
  103. {pygit2-1.19.1 → pygit2-1.19.2}/setup.cfg +0 -0
  104. {pygit2-1.19.1 → pygit2-1.19.2}/setup.py +0 -0
  105. {pygit2-1.19.1 → pygit2-1.19.2}/src/blob.c +0 -0
  106. {pygit2-1.19.1 → pygit2-1.19.2}/src/branch.h +0 -0
  107. {pygit2-1.19.1 → pygit2-1.19.2}/src/commit.c +0 -0
  108. {pygit2-1.19.1 → pygit2-1.19.2}/src/diff.h +0 -0
  109. {pygit2-1.19.1 → pygit2-1.19.2}/src/error.c +0 -0
  110. {pygit2-1.19.1 → pygit2-1.19.2}/src/error.h +0 -0
  111. {pygit2-1.19.1 → pygit2-1.19.2}/src/filter.h +0 -0
  112. {pygit2-1.19.1 → pygit2-1.19.2}/src/mailmap.h +0 -0
  113. {pygit2-1.19.1 → pygit2-1.19.2}/src/note.h +0 -0
  114. {pygit2-1.19.1 → pygit2-1.19.2}/src/object.h +0 -0
  115. {pygit2-1.19.1 → pygit2-1.19.2}/src/odb.h +0 -0
  116. {pygit2-1.19.1 → pygit2-1.19.2}/src/odb_backend.h +0 -0
  117. {pygit2-1.19.1 → pygit2-1.19.2}/src/oid.c +0 -0
  118. {pygit2-1.19.1 → pygit2-1.19.2}/src/oid.h +0 -0
  119. {pygit2-1.19.1 → pygit2-1.19.2}/src/patch.h +0 -0
  120. {pygit2-1.19.1 → pygit2-1.19.2}/src/refdb.h +0 -0
  121. {pygit2-1.19.1 → pygit2-1.19.2}/src/refdb_backend.h +0 -0
  122. {pygit2-1.19.1 → pygit2-1.19.2}/src/reference.h +0 -0
  123. {pygit2-1.19.1 → pygit2-1.19.2}/src/repository.h +0 -0
  124. {pygit2-1.19.1 → pygit2-1.19.2}/src/revspec.c +0 -0
  125. {pygit2-1.19.1 → pygit2-1.19.2}/src/revspec.h +0 -0
  126. {pygit2-1.19.1 → pygit2-1.19.2}/src/signature.c +0 -0
  127. {pygit2-1.19.1 → pygit2-1.19.2}/src/signature.h +0 -0
  128. {pygit2-1.19.1 → pygit2-1.19.2}/src/stash.c +0 -0
  129. {pygit2-1.19.1 → pygit2-1.19.2}/src/tree.h +0 -0
  130. {pygit2-1.19.1 → pygit2-1.19.2}/src/treebuilder.h +0 -0
  131. {pygit2-1.19.1 → pygit2-1.19.2}/src/types.h +0 -0
  132. {pygit2-1.19.1 → pygit2-1.19.2}/src/walker.h +0 -0
  133. {pygit2-1.19.1 → pygit2-1.19.2}/src/wildmatch.c +0 -0
  134. {pygit2-1.19.1 → pygit2-1.19.2}/src/wildmatch.h +0 -0
  135. {pygit2-1.19.1 → pygit2-1.19.2}/src/worktree.h +0 -0
  136. {pygit2-1.19.1 → pygit2-1.19.2}/test/__init__.py +0 -0
  137. {pygit2-1.19.1 → pygit2-1.19.2}/test/conftest.py +0 -0
  138. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/barerepo.zip +0 -0
  139. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/binaryfilerepo.zip +0 -0
  140. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/blameflagsrepo.zip +0 -0
  141. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/dirtyrepo.zip +0 -0
  142. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/emptyrepo.zip +0 -0
  143. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/encoding.zip +0 -0
  144. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/gpgsigned.zip +0 -0
  145. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/submodulerepo.zip +0 -0
  146. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/testrepo.zip +0 -0
  147. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/testrepoformerging.zip +0 -0
  148. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/testrepopacked.zip +0 -0
  149. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/trailerrepo.zip +0 -0
  150. {pygit2-1.19.1 → pygit2-1.19.2}/test/data/utf8branchrepo.zip +0 -0
  151. {pygit2-1.19.1 → pygit2-1.19.2}/test/keys/pygit2_empty +0 -0
  152. {pygit2-1.19.1 → pygit2-1.19.2}/test/keys/pygit2_empty.pub +0 -0
  153. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_apply_diff.py +0 -0
  154. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_archive.py +0 -0
  155. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_attributes.py +0 -0
  156. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_blame.py +0 -0
  157. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_blob.py +0 -0
  158. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_branch.py +0 -0
  159. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_branch_empty.py +0 -0
  160. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_cherrypick.py +0 -0
  161. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_commit.py +0 -0
  162. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_commit_gpg.py +0 -0
  163. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_commit_trailer.py +0 -0
  164. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_credentials.py +0 -0
  165. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_describe.py +0 -0
  166. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_diff.py +0 -0
  167. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_diff_binary.py +0 -0
  168. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_index.py +0 -0
  169. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_mailmap.py +0 -0
  170. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_merge.py +0 -0
  171. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_nonunicode.py +0 -0
  172. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_note.py +0 -0
  173. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_object.py +0 -0
  174. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_odb_backend.py +0 -0
  175. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_oid.py +0 -0
  176. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_options.py +0 -0
  177. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_packbuilder.py +0 -0
  178. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_patch.py +0 -0
  179. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_patch_encoding.py +0 -0
  180. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_refdb_backend.py +0 -0
  181. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_refs.py +0 -0
  182. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_remote.py +0 -0
  183. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_remote_prune.py +0 -0
  184. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_remote_utf8.py +0 -0
  185. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_repository.py +0 -0
  186. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_repository_bare.py +0 -0
  187. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_repository_custom.py +0 -0
  188. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_repository_empty.py +0 -0
  189. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_revparse.py +0 -0
  190. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_revwalk.py +0 -0
  191. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_settings.py +0 -0
  192. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_signature.py +0 -0
  193. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_status.py +0 -0
  194. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_submodule.py +0 -0
  195. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_tag.py +0 -0
  196. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_transaction.py +0 -0
  197. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_tree.py +0 -0
  198. {pygit2-1.19.1 → pygit2-1.19.2}/test/test_treebuilder.py +0 -0
  199. {pygit2-1.19.1 → pygit2-1.19.2}/test/utils.py +0 -0
@@ -138,6 +138,7 @@ Authors:
138
138
  Adam Spiers
139
139
  Adrien Nader
140
140
  Albin Söderström
141
+ Alexander Shadchin
141
142
  Alexandru Fikl
142
143
  Andrew Chin
143
144
  Andrew McNulty
@@ -182,6 +183,7 @@ Authors:
182
183
  Hugh Cole-Baker
183
184
  Isabella Stephens
184
185
  Jacob Swanson
186
+ Jah-yee
185
187
  Jasper Lievisse Adriaanse
186
188
  Jimisola Laursen
187
189
  Jiri Benc
@@ -237,6 +239,7 @@ Authors:
237
239
  Timo Röhling
238
240
  Victor Florea
239
241
  Vladimir Rutsky
242
+ Vruyr Gyolchanyan
240
243
  William Schueller
241
244
  Wim Jeantine-Glenn
242
245
  Yu Jianjian
@@ -1,3 +1,22 @@
1
+ # 1.19.2 (2026-03-29)
2
+
3
+ - Fix refcount and error handling issues in `filter_register(...)`
4
+
5
+ - Fix config with valueless keys
6
+ [#1457](https://github.com/libgit2/pygit2/pull/1457)
7
+
8
+ - New `Repository.load_filter_list(...)` and `FilterList`
9
+ [#1444](https://github.com/libgit2/pygit2/pull/1444)
10
+
11
+ - New `Odb.read_header(...)` and now `Odb.read(...)` returns `enums.ObjectType` instead of int
12
+ [#1450](https://github.com/libgit2/pygit2/pull/1450)
13
+
14
+ - Build and CI fixes
15
+ [#1446](https://github.com/libgit2/pygit2/pull/1446)
16
+ [#1448](https://github.com/libgit2/pygit2/pull/1448)
17
+ [#1455](https://github.com/libgit2/pygit2/pull/1455)
18
+
19
+
1
20
  # 1.19.1 (2025-12-29)
2
21
 
3
22
  - Update wheels to libgit2 1.9.2 and OpenSSL 3.5
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygit2
3
- Version: 1.19.1
3
+ Version: 1.19.2
4
4
  Summary: Python bindings for libgit2.
5
5
  Home-page: https://github.com/libgit2/pygit2
6
6
  Maintainer: J. David Ibáñez
@@ -286,7 +286,6 @@ from ._pygit2 import (
286
286
  _cache_enums,
287
287
  discover_repository,
288
288
  filter_register,
289
- filter_unregister,
290
289
  hash,
291
290
  hashfile,
292
291
  init_file_backend,
@@ -545,6 +544,27 @@ def clone_repository(
545
544
  return Repository._from_c(crepo[0], owned=True)
546
545
 
547
546
 
547
+ def filter_unregister(name: str) -> None:
548
+ """
549
+ Unregister the given filter.
550
+
551
+ Note that the filter registry is not thread safe. Any registering or
552
+ deregistering of filters should be done outside of any possible usage
553
+ of the filters.
554
+
555
+ In particular, any FilterLists that use the filter must have been garbage
556
+ collected before you can unregister the filter.
557
+ """
558
+ from .filter import FilterList
559
+
560
+ if FilterList._is_filter_in_use(name):
561
+ raise RuntimeError(f"filter still in use: '{name}'")
562
+
563
+ c_name = to_bytes(name)
564
+ err = C.git_filter_unregister(c_name)
565
+ check_error(err)
566
+
567
+
548
568
  tree_entry_key = functools.cmp_to_key(tree_entry_cmp)
549
569
 
550
570
  settings = Settings()
@@ -610,7 +630,6 @@ __all__ = (
610
630
  # Low Level API (not present in .pyi)
611
631
  'FilterSource',
612
632
  'filter_register',
613
- 'filter_unregister',
614
633
  'GIT_APPLY_LOCATION_BOTH',
615
634
  'GIT_APPLY_LOCATION_INDEX',
616
635
  'GIT_APPLY_LOCATION_WORKDIR',
@@ -34,7 +34,7 @@ from pathlib import Path
34
34
  #
35
35
  # The version number of pygit2
36
36
  #
37
- __version__ = '1.19.1'
37
+ __version__ = '1.19.2'
38
38
 
39
39
 
40
40
  #
@@ -123,6 +123,10 @@ class GitBlameC:
123
123
  # incomplete
124
124
  pass
125
125
 
126
+ class GitBlobC:
127
+ # incomplete
128
+ pass
129
+
126
130
  class GitMergeOptionsC:
127
131
  file_favor: int
128
132
  flags: int
@@ -177,6 +181,10 @@ class GitDescribeOptionsC:
177
181
  class GitDescribeResultC:
178
182
  pass
179
183
 
184
+ class GitFilterListC:
185
+ # opaque struct
186
+ pass
187
+
180
188
  class GitIndexC:
181
189
  pass
182
190
 
@@ -264,6 +272,8 @@ def new(a: Literal['git_oid *']) -> GitOidC: ...
264
272
  @overload
265
273
  def new(a: Literal['git_blame **']) -> _Pointer[GitBlameC]: ...
266
274
  @overload
275
+ def new(a: Literal['git_blob **']) -> _Pointer[GitBlobC]: ...
276
+ @overload
267
277
  def new(a: Literal['git_clone_options *']) -> GitCloneOptionsC: ...
268
278
  @overload
269
279
  def new(a: Literal['git_merge_options *']) -> GitMergeOptionsC: ...
@@ -318,6 +328,8 @@ def new(a: Literal['git_signature *']) -> GitSignatureC: ...
318
328
  @overload
319
329
  def new(a: Literal['git_signature **']) -> _Pointer[GitSignatureC]: ...
320
330
  @overload
331
+ def new(a: Literal['git_filter_list **']) -> _Pointer[GitFilterListC]: ...
332
+ @overload
321
333
  def new(a: Literal['int *']) -> int_c: ...
322
334
  @overload
323
335
  def new(a: Literal['int64_t *']) -> int64_t: ...
@@ -518,7 +518,8 @@ class Odb:
518
518
  def add_backend(self, backend: OdbBackend, priority: int) -> None: ...
519
519
  def add_disk_alternate(self, path: str | Path) -> None: ...
520
520
  def exists(self, oid: _OidArg) -> bool: ...
521
- def read(self, oid: _OidArg) -> tuple[int, bytes]: ...
521
+ def read(self, oid: _OidArg) -> tuple[ObjectType, bytes]: ...
522
+ def read_header(self, oid: _OidArg) -> tuple[ObjectType, int]: ...
522
523
  def write(self, type: int, data: bytes | str) -> Oid: ...
523
524
  def __contains__(self, other: _OidArg) -> bool: ...
524
525
  def __iter__(self) -> Iterator[Oid]: ... # Odb_as_iter
@@ -857,6 +858,5 @@ def reference_is_valid_name(refname: str) -> bool: ...
857
858
  def tree_entry_cmp(a: Object, b: Object) -> int: ...
858
859
  def _cache_enums() -> None: ...
859
860
  def filter_register(name: str, filter: type[Filter]) -> None: ...
860
- def filter_unregister(name: str) -> None: ...
861
861
 
862
862
  _OidArg = str | Oid
@@ -77,6 +77,7 @@ h_files = [
77
77
  'net.h',
78
78
  'refspec.h',
79
79
  'repository.h',
80
+ 'filter.h',
80
81
  'commit.h',
81
82
  'revert.h',
82
83
  'stash.h',
@@ -96,6 +97,7 @@ C_HEADER_SRC = '\n'.join(h_source)
96
97
  C_PREAMBLE = """\
97
98
  #include <git2.h>
98
99
  #include <git2/sys/repository.h>
100
+ #include <git2/sys/filter.h>
99
101
  """
100
102
 
101
103
  # ffi
@@ -73,7 +73,7 @@ class ConfigIterator:
73
73
 
74
74
 
75
75
  class ConfigMultivarIterator(ConfigIterator):
76
- def __next__(self) -> str: # type: ignore[override]
76
+ def __next__(self) -> str | None: # type: ignore[override]
77
77
  entry = self._next_entry()
78
78
  return entry.value
79
79
 
@@ -137,7 +137,7 @@ class Config:
137
137
 
138
138
  return True
139
139
 
140
- def __getitem__(self, key: str | bytes) -> str:
140
+ def __getitem__(self, key: str | bytes) -> str | None:
141
141
  """
142
142
  When using the mapping interface, the value is returned as a string. In
143
143
  order to apply the git-config parsing rules, you can use
@@ -365,8 +365,8 @@ class ConfigEntry:
365
365
  return ffi.string(self._entry.name)
366
366
 
367
367
  @cached_property
368
- def raw_value(self) -> bytes:
369
- return ffi.string(self.c_value)
368
+ def raw_value(self) -> bytes | None:
369
+ return ffi.string(self.c_value) if self.c_value != ffi.NULL else None
370
370
 
371
371
  @cached_property
372
372
  def level(self) -> int:
@@ -379,6 +379,6 @@ class ConfigEntry:
379
379
  return self.raw_name.decode('utf-8')
380
380
 
381
381
  @property
382
- def value(self) -> str:
382
+ def value(self) -> str | None:
383
383
  """The entry's value as a string."""
384
- return self.raw_value.decode('utf-8')
384
+ return self.raw_value.decode('utf-8') if self.raw_value is not None else None
@@ -0,0 +1,49 @@
1
+ typedef enum {
2
+ GIT_FILTER_TO_WORKTREE = ...,
3
+ GIT_FILTER_TO_ODB = ...,
4
+ } git_filter_mode_t;
5
+
6
+ typedef enum {
7
+ GIT_FILTER_DEFAULT = ...,
8
+ GIT_FILTER_ALLOW_UNSAFE = ...,
9
+ GIT_FILTER_NO_SYSTEM_ATTRIBUTES = ...,
10
+ GIT_FILTER_ATTRIBUTES_FROM_HEAD = ...,
11
+ GIT_FILTER_ATTRIBUTES_FROM_COMMIT = ...,
12
+ } git_filter_flag_t;
13
+
14
+ int git_filter_unregister(
15
+ const char *name);
16
+
17
+ int git_filter_list_load(
18
+ git_filter_list **filters,
19
+ git_repository *repo,
20
+ git_blob *blob,
21
+ const char *path,
22
+ git_filter_mode_t mode,
23
+ uint32_t flags);
24
+
25
+ int git_filter_list_contains(
26
+ git_filter_list *filters,
27
+ const char *name);
28
+
29
+ int git_filter_list_apply_to_buffer(
30
+ git_buf *out,
31
+ git_filter_list *filters,
32
+ const char* in,
33
+ size_t in_len);
34
+
35
+ int git_filter_list_apply_to_file(
36
+ git_buf *out,
37
+ git_filter_list *filters,
38
+ git_repository *repo,
39
+ const char *path);
40
+
41
+ int git_filter_list_apply_to_blob(
42
+ git_buf *out,
43
+ git_filter_list *filters,
44
+ git_blob *blob);
45
+
46
+ size_t git_filter_list_length(
47
+ const git_filter_list *fl);
48
+
49
+ void git_filter_list_free(git_filter_list *filters);
@@ -1,6 +1,8 @@
1
+ typedef struct git_blob git_blob;
1
2
  typedef struct git_commit git_commit;
2
3
  typedef struct git_annotated_commit git_annotated_commit;
3
4
  typedef struct git_config git_config;
5
+ typedef struct git_filter_list git_filter_list;
4
6
  typedef struct git_index git_index;
5
7
  typedef struct git_index_conflict_iterator git_index_conflict_iterator;
6
8
  typedef struct git_object git_object;
@@ -23,9 +23,20 @@
23
23
  # the Free Software Foundation, 51 Franklin Street, Fifth Floor,
24
24
  # Boston, MA 02110-1301, USA.
25
25
 
26
+ from __future__ import annotations
27
+
28
+ import weakref
26
29
  from collections.abc import Callable
30
+ from typing import TYPE_CHECKING
31
+
32
+ from ._pygit2 import Blob, FilterSource
33
+ from .errors import check_error
34
+ from .ffi import C, ffi
35
+ from .utils import to_bytes
27
36
 
28
- from ._pygit2 import FilterSource
37
+ if TYPE_CHECKING:
38
+ from ._libgit2.ffi import GitFilterListC
39
+ from .repository import BaseRepository
29
40
 
30
41
 
31
42
  class Filter:
@@ -107,3 +118,90 @@ class Filter:
107
118
  Any remaining filtered output data must be written to
108
119
  `write_next` before returning.
109
120
  """
121
+
122
+
123
+ class FilterList:
124
+ _all_filter_lists: set[weakref.ReferenceType[FilterList]] = set()
125
+
126
+ _pointer: GitFilterListC
127
+
128
+ @classmethod
129
+ def _from_c(cls, ptr: GitFilterListC):
130
+ if ptr == ffi.NULL:
131
+ return None
132
+
133
+ fl = cls.__new__(cls)
134
+ fl._pointer = ptr
135
+
136
+ # Keep track of this FilterList until it's garbage collected. This lets
137
+ # `filter_unregister` ensure the user isn't trying to delete a filter
138
+ # that's still in use.
139
+ ref = weakref.ref(fl, FilterList._all_filter_lists.remove)
140
+ FilterList._all_filter_lists.add(ref)
141
+
142
+ return fl
143
+
144
+ @classmethod
145
+ def _is_filter_in_use(cls, name: str) -> bool:
146
+ for ref in cls._all_filter_lists:
147
+ fl = ref()
148
+ if fl is not None and name in fl:
149
+ return True
150
+ return False
151
+
152
+ def __contains__(self, name: str) -> bool:
153
+ if not isinstance(name, str):
154
+ raise TypeError('argument must be str')
155
+ c_name = to_bytes(name)
156
+ result = C.git_filter_list_contains(self._pointer, c_name)
157
+ return bool(result)
158
+
159
+ def __len__(self) -> int:
160
+ return C.git_filter_list_length(self._pointer)
161
+
162
+ def apply_to_buffer(self, data: bytes) -> bytes:
163
+ """
164
+ Apply a filter list to a data buffer.
165
+ Return the filtered contents.
166
+ """
167
+ buf = ffi.new('git_buf *')
168
+ err = C.git_filter_list_apply_to_buffer(buf, self._pointer, data, len(data))
169
+ check_error(err)
170
+ try:
171
+ return ffi.string(buf.ptr)
172
+ finally:
173
+ C.git_buf_dispose(buf)
174
+
175
+ def apply_to_file(self, repo: BaseRepository, path: str) -> bytes:
176
+ """
177
+ Apply a filter list to the contents of a file on disk.
178
+ Return the filtered contents.
179
+ """
180
+ buf = ffi.new('git_buf *')
181
+ c_path = to_bytes(path)
182
+ err = C.git_filter_list_apply_to_file(buf, self._pointer, repo._repo, c_path)
183
+ check_error(err)
184
+ try:
185
+ return ffi.string(buf.ptr)
186
+ finally:
187
+ C.git_buf_dispose(buf)
188
+
189
+ def apply_to_blob(self, blob: Blob) -> bytes:
190
+ """
191
+ Apply a filter list to a data buffer.
192
+ Return the filtered contents.
193
+ """
194
+ buf = ffi.new('git_buf *')
195
+
196
+ c_blob = ffi.new('git_blob **')
197
+ ffi.buffer(c_blob)[:] = blob._pointer[:]
198
+
199
+ err = C.git_filter_list_apply_to_blob(buf, self._pointer, c_blob[0])
200
+ check_error(err)
201
+ try:
202
+ return ffi.string(buf.ptr)
203
+ finally:
204
+ C.git_buf_dispose(buf)
205
+
206
+ def __del__(self):
207
+ C.git_filter_list_free(self._pointer)
@@ -64,6 +64,7 @@ from .enums import (
64
64
  DescribeStrategy,
65
65
  DiffOption,
66
66
  FileMode,
67
+ FilterMode,
67
68
  MergeFavor,
68
69
  MergeFileFlag,
69
70
  MergeFlag,
@@ -73,6 +74,7 @@ from .enums import (
73
74
  )
74
75
  from .errors import check_error
75
76
  from .ffi import C, ffi
77
+ from .filter import FilterList
76
78
  from .index import Index, IndexEntry, MergeFileResult
77
79
  from .packbuilder import PackBuilder
78
80
  from .references import References
@@ -235,6 +237,31 @@ class BaseRepository(_Repository):
235
237
  oid = Oid(raw=bytes(ffi.buffer(c_oid.id)[:]))
236
238
  return oid
237
239
 
240
+ def load_filter_list(
241
+ self, path: str, mode: FilterMode = FilterMode.TO_ODB
242
+ ) -> FilterList | None:
243
+ """
244
+ Load the filter list for a given path.
245
+ May return None if there are no filters to apply to this path.
246
+
247
+ Parameters:
248
+
249
+ path
250
+ Relative path of the file to be filtered
251
+
252
+ mode
253
+ Filtering direction: ODB to worktree (SMUDGE), or worktree to ODB
254
+ (CLEAN).
255
+ """
256
+ c_filters = ffi.new('git_filter_list **')
257
+ c_path = to_bytes(path)
258
+ c_mode = int(mode)
259
+
260
+ err = C.git_filter_list_load(c_filters, self._repo, ffi.NULL, c_path, c_mode, 0)
261
+ check_error(err)
262
+ fl = FilterList._from_c(c_filters[0])
263
+ return fl
264
+
238
265
  def __iter__(self) -> Iterator[Oid]:
239
266
  return iter(self.odb)
240
267
 
@@ -1,5 +1,6 @@
1
1
  [build-system]
2
- requires = ["setuptools", "wheel"]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
3
4
 
4
5
  [tool.cibuildwheel]
5
6
  enable = ["pypy"]
@@ -275,7 +275,7 @@ Branch_upstream_name__get__(Branch *self)
275
275
  }
276
276
 
277
277
 
278
- PyMethodDef Branch_methods[] = {
278
+ static PyMethodDef Branch_methods[] = {
279
279
  METHOD(Branch, delete, METH_NOARGS),
280
280
  METHOD(Branch, is_head, METH_NOARGS),
281
281
  METHOD(Branch, is_checked_out, METH_NOARGS),
@@ -236,7 +236,7 @@ PyMemberDef DiffFile_members[] = {
236
236
  {NULL}
237
237
  };
238
238
 
239
- PyMethodDef DiffFile_methods[] = {
239
+ static PyMethodDef DiffFile_methods[] = {
240
240
  METHOD(DiffFile, from_c, METH_STATIC | METH_O),
241
241
  {NULL},
242
242
  };
@@ -914,7 +914,7 @@ DiffStats_dealloc(DiffStats *self)
914
914
  PyObject_Del(self);
915
915
  }
916
916
 
917
- PyMethodDef DiffStats_methods[] = {
917
+ static PyMethodDef DiffStats_methods[] = {
918
918
  METHOD(DiffStats, format, METH_VARARGS | METH_KEYWORDS),
919
919
  {NULL}
920
920
  };
@@ -551,7 +551,9 @@ void pygit2_filter_cleanup(git_filter *self, void *payload)
551
551
  void pygit2_filter_shutdown(git_filter *self)
552
552
  {
553
553
  pygit2_filter *filter = (pygit2_filter *)self;
554
+
554
555
  PyGILState_STATE gil = PyGILState_Ensure();
556
+ free((void*)filter->filter.attributes);
555
557
  Py_DECREF(filter->py_filter_cls);
556
558
  free(filter);
557
559
  PyGILState_Release(gil);
@@ -188,7 +188,7 @@ Mailmap_dealloc(Mailmap *self)
188
188
  }
189
189
 
190
190
 
191
- PyMethodDef Mailmap_methods[] = {
191
+ static PyMethodDef Mailmap_methods[] = {
192
192
  METHOD(Mailmap, add_entry, METH_VARARGS | METH_KEYWORDS),
193
193
  METHOD(Mailmap, resolve, METH_VARARGS),
194
194
  METHOD(Mailmap, resolve_signature, METH_VARARGS),
@@ -99,7 +99,7 @@ Note_dealloc(Note *self)
99
99
  }
100
100
 
101
101
 
102
- PyMethodDef Note_methods[] = {
102
+ static PyMethodDef Note_methods[] = {
103
103
  METHOD(Note, remove, METH_VARARGS),
104
104
  {NULL}
105
105
  };
@@ -302,7 +302,7 @@ PyGetSetDef Object_getseters[] = {
302
302
  {NULL}
303
303
  };
304
304
 
305
- PyMethodDef Object_methods[] = {
305
+ static PyMethodDef Object_methods[] = {
306
306
  METHOD(Object, read_raw, METH_NOARGS),
307
307
  METHOD(Object, peel, METH_O),
308
308
  {NULL}
@@ -37,6 +37,8 @@
37
37
 
38
38
  extern PyTypeObject OdbBackendType;
39
39
 
40
+ extern PyObject *ObjectTypeEnum;
41
+
40
42
  static git_otype
41
43
  int_to_loose_object_type(int type_id)
42
44
  {
@@ -170,7 +172,7 @@ Odb_read_raw(git_odb *odb, const git_oid *oid, size_t len)
170
172
  }
171
173
 
172
174
  PyDoc_STRVAR(Odb_read__doc__,
173
- "read(oid) -> type, data, size\n"
175
+ "read(oid: Oid) -> tuple[enums.ObjectType, bytes]\n"
174
176
  "\n"
175
177
  "Read raw object data from the object db.");
176
178
 
@@ -180,6 +182,8 @@ Odb_read(Odb *self, PyObject *py_hex)
180
182
  git_oid oid;
181
183
  git_odb_object *obj;
182
184
  size_t len;
185
+ git_object_t type;
186
+ PyObject* type_enum;
183
187
  PyObject* tuple;
184
188
 
185
189
  len = py_oid_to_git_oid(py_hex, &oid);
@@ -190,9 +194,13 @@ Odb_read(Odb *self, PyObject *py_hex)
190
194
  if (obj == NULL)
191
195
  return NULL;
192
196
 
197
+ // Convert type to ObjectType enum
198
+ type = git_odb_object_type(obj);
199
+ type_enum = pygit2_enum(ObjectTypeEnum, type);
200
+
193
201
  tuple = Py_BuildValue(
194
- "(ny#)",
195
- git_odb_object_type(obj),
202
+ "(Oy#)",
203
+ type_enum,
196
204
  git_odb_object_data(obj),
197
205
  git_odb_object_size(obj));
198
206
 
@@ -200,6 +208,44 @@ Odb_read(Odb *self, PyObject *py_hex)
200
208
  return tuple;
201
209
  }
202
210
 
211
+ PyDoc_STRVAR(Odb_read_header__doc__,
212
+ "read_header(oid: Oid) -> tuple[enums.ObjectType, size\n"
213
+ "\n"
214
+ "Read the header of an object from the database, without reading its full\n"
215
+ "contents.\n"
216
+ "\n"
217
+ "The header includes the type and the length of an object.\n"
218
+ "\n"
219
+ "Note that most backends do not support reading only the header of an object,\n"
220
+ "so the whole object may be read and then the header will be returned.");
221
+
222
+ PyObject *
223
+ Odb_read_header(Odb *self, PyObject *py_hex)
224
+ {
225
+ git_oid oid;
226
+ int err;
227
+ size_t len;
228
+ git_object_t type;
229
+ PyObject* type_enum;
230
+ PyObject* tuple;
231
+
232
+ len = py_oid_to_git_oid(py_hex, &oid);
233
+ if (len == 0)
234
+ return NULL;
235
+
236
+ err = git_odb_read_header(&len, &type, self->odb, &oid);
237
+ if (err != 0) {
238
+ Error_set_oid(err, &oid, len);
239
+ return NULL;
240
+ }
241
+
242
+ // Convert type to ObjectType enum
243
+ type_enum = pygit2_enum(ObjectTypeEnum, type);
244
+
245
+ tuple = Py_BuildValue("(On)", type_enum, len);
246
+ return tuple;
247
+ }
248
+
203
249
  PyDoc_STRVAR(Odb_write__doc__,
204
250
  "write(type: int, data: bytes) -> Oid\n"
205
251
  "\n"
@@ -298,9 +344,10 @@ Odb_add_backend(Odb *self, PyObject *args)
298
344
  }
299
345
 
300
346
 
301
- PyMethodDef Odb_methods[] = {
347
+ static PyMethodDef Odb_methods[] = {
302
348
  METHOD(Odb, add_disk_alternate, METH_O),
303
349
  METHOD(Odb, read, METH_O),
350
+ METHOD(Odb, read_header, METH_O),
304
351
  METHOD(Odb, write, METH_VARARGS),
305
352
  METHOD(Odb, exists, METH_O),
306
353
  METHOD(Odb, add_backend, METH_VARARGS),
@@ -518,7 +518,7 @@ OdbBackend_refresh(OdbBackend *self)
518
518
  * - readstream
519
519
  * - freshen
520
520
  */
521
- PyMethodDef OdbBackend_methods[] = {
521
+ static PyMethodDef OdbBackend_methods[] = {
522
522
  METHOD(OdbBackend, read, METH_O),
523
523
  METHOD(OdbBackend, read_prefix, METH_O),
524
524
  METHOD(OdbBackend, read_header, METH_O),
@@ -235,7 +235,7 @@ Patch_hunks__get__(Patch *self)
235
235
  }
236
236
 
237
237
 
238
- PyMethodDef Patch_methods[] = {
238
+ static PyMethodDef Patch_methods[] = {
239
239
  {"create_from", (PyCFunction) Patch_create_from,
240
240
  METH_KEYWORDS | METH_VARARGS | METH_STATIC, Patch_create_from__doc__},
241
241
  {NULL}