hgitaly 18.7.0a1__tar.gz → 18.7.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 (175) hide show
  1. {hgitaly-18.7.0a1/hgitaly.egg-info → hgitaly-18.7.2}/PKG-INFO +2 -2
  2. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_blob_tree.py +1 -1
  3. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_commit.py +59 -8
  4. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_ref.py +14 -0
  5. hgitaly-18.7.2/hgitaly/VERSION +1 -0
  6. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/revset.py +9 -0
  7. hgitaly-18.7.2/hgitaly/scripts/__init__.py +3 -0
  8. hgitaly-18.7.2/hgitaly/scripts/load_tests.py +373 -0
  9. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/commit.py +8 -0
  10. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_commit.py +12 -0
  11. {hgitaly-18.7.0a1 → hgitaly-18.7.2/hgitaly.egg-info}/PKG-INFO +2 -2
  12. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly.egg-info/SOURCES.txt +3 -1
  13. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly.egg-info/entry_points.txt +1 -0
  14. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly.egg-info/requires.txt +1 -1
  15. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/install-requirements.txt +1 -1
  16. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/setup.py +1 -0
  17. hgitaly-18.7.0a1/hgitaly/VERSION +0 -1
  18. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/LICENSE +0 -0
  19. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/MANIFEST.in +0 -0
  20. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/README.md +0 -0
  21. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/__init__.py +0 -0
  22. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/comparison.py +0 -0
  23. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/conftest.py +0 -0
  24. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/gitaly.py +0 -0
  25. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/hgitaly_rhgitaly_comparison.py +0 -0
  26. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/rhgitaly.py +0 -0
  27. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_comparison.py +0 -0
  28. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_diff.py +0 -0
  29. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_gitaly_server.py +0 -0
  30. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_mercurial_aux_git.py +0 -0
  31. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_mercurial_operations.py +0 -0
  32. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_mercurial_repository.py +0 -0
  33. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_operations.py +0 -0
  34. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_remote.py +0 -0
  35. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_repository_service.py +0 -0
  36. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_rhgitaly_server.py +0 -0
  37. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/functional_tests/test_server.py +0 -0
  38. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgext3rd/__init__.py +0 -0
  39. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgext3rd/hgitaly/__init__.py +0 -0
  40. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgext3rd/hgitaly/revset.py +0 -0
  41. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgext3rd/hgitaly/tests/__init__.py +0 -0
  42. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgext3rd/hgitaly/tests/test_revset.py +0 -0
  43. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgext3rd/hgitaly/tests/test_serve.py +0 -0
  44. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/__init__.py +0 -0
  45. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/branch.py +0 -0
  46. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/changelog.py +0 -0
  47. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/diff.py +0 -0
  48. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/errors.py +0 -0
  49. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/feature.py +0 -0
  50. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/file_content.py +0 -0
  51. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/file_context.py +0 -0
  52. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/git.py +0 -0
  53. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/gitlab_ref.py +0 -0
  54. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/identification.py +0 -0
  55. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/logging.py +0 -0
  56. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/manifest.py +0 -0
  57. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/message.py +0 -0
  58. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/oid.py +0 -0
  59. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/pagination.py +0 -0
  60. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/path.py +0 -0
  61. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/peer.py +0 -0
  62. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/procutil.py +0 -0
  63. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/repository.py +0 -0
  64. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/revision.py +0 -0
  65. /hgitaly-18.7.0a1/hgitaly/scripts.py → /hgitaly-18.7.2/hgitaly/scripts/stats.py +0 -0
  66. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/server/__init__.py +0 -0
  67. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/server/address.py +0 -0
  68. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/server/mono.py +0 -0
  69. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/server/tests/__init__.py +0 -0
  70. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/server/tests/test_address.py +0 -0
  71. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/server/tests/test_mono.py +0 -0
  72. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/server/tests/test_worker.py +0 -0
  73. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/server/worker.py +0 -0
  74. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/__init__.py +0 -0
  75. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/analysis.py +0 -0
  76. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/blob.py +0 -0
  77. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/diff.py +0 -0
  78. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/interceptors.py +0 -0
  79. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/mercurial_changeset.py +0 -0
  80. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/mercurial_namespace.py +0 -0
  81. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/mercurial_operations.py +0 -0
  82. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/mercurial_repository.py +0 -0
  83. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/operations.py +0 -0
  84. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/ref.py +0 -0
  85. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/repository.py +0 -0
  86. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/server.py +0 -0
  87. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/__init__.py +0 -0
  88. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/fixture.py +0 -0
  89. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_analysis.py +0 -0
  90. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_blob.py +0 -0
  91. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_default_branch.py +0 -0
  92. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_diff.py +0 -0
  93. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_mercurial_changeset.py +0 -0
  94. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_mercurial_namespace.py +0 -0
  95. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_mercurial_operations.py +0 -0
  96. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_mercurial_repository.py +0 -0
  97. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_operations.py +0 -0
  98. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_ref.py +0 -0
  99. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_repository_service.py +0 -0
  100. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/service/tests/test_server.py +0 -0
  101. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/servicer.py +0 -0
  102. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/ssh.py +0 -0
  103. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stream.py +0 -0
  104. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/__init__.py +0 -0
  105. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/analysis_pb2.py +0 -0
  106. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/analysis_pb2_grpc.py +0 -0
  107. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/blob_pb2.py +0 -0
  108. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/blob_pb2_grpc.py +0 -0
  109. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/commit_pb2.py +0 -0
  110. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/commit_pb2_grpc.py +0 -0
  111. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/diff_pb2.py +0 -0
  112. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/diff_pb2_grpc.py +0 -0
  113. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/errors_pb2.py +0 -0
  114. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/errors_pb2_grpc.py +0 -0
  115. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/lint_pb2.py +0 -0
  116. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/lint_pb2_grpc.py +0 -0
  117. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_aux_git_pb2.py +0 -0
  118. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_aux_git_pb2_grpc.py +0 -0
  119. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_changeset_pb2.py +0 -0
  120. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_changeset_pb2_grpc.py +0 -0
  121. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_namespace_pb2.py +0 -0
  122. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_namespace_pb2_grpc.py +0 -0
  123. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_operations_pb2.py +0 -0
  124. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_operations_pb2_grpc.py +0 -0
  125. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_repository_pb2.py +0 -0
  126. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/mercurial_repository_pb2_grpc.py +0 -0
  127. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/operations_pb2.py +0 -0
  128. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/operations_pb2_grpc.py +0 -0
  129. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/ref_pb2.py +0 -0
  130. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/ref_pb2_grpc.py +0 -0
  131. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/remote_pb2.py +0 -0
  132. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/remote_pb2_grpc.py +0 -0
  133. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/repository_pb2.py +0 -0
  134. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/repository_pb2_grpc.py +0 -0
  135. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/server_pb2.py +0 -0
  136. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/server_pb2_grpc.py +0 -0
  137. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/shared_pb2.py +0 -0
  138. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/stub/shared_pb2_grpc.py +0 -0
  139. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tag.py +0 -0
  140. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/__init__.py +0 -0
  141. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/bundle.py +0 -0
  142. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/context.py +0 -0
  143. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/grpc.py +0 -0
  144. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/multiprocessing.py +0 -0
  145. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/repo.py +0 -0
  146. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/ssh.py +0 -0
  147. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/sshd.py +0 -0
  148. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/storage.py +0 -0
  149. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/tests/__init__.py +0 -0
  150. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/testing/tests/test_sshd.py +0 -0
  151. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/__init__.py +0 -0
  152. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/common.py +0 -0
  153. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_branch.py +0 -0
  154. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_diff.py +0 -0
  155. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_errors.py +0 -0
  156. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_feature.py +0 -0
  157. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_file_context.py +0 -0
  158. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_gitlab_ref.py +0 -0
  159. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_identification.py +0 -0
  160. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_manifest.py +0 -0
  161. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_messages.py +0 -0
  162. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_oid.py +0 -0
  163. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_peer.py +0 -0
  164. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_repository.py +0 -0
  165. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_revision.py +0 -0
  166. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_revset.py +0 -0
  167. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_servicer.py +0 -0
  168. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_stream.py +0 -0
  169. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_tag.py +0 -0
  170. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/tests/test_workdir.py +0 -0
  171. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/util.py +0 -0
  172. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly/workdir.py +0 -0
  173. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly.egg-info/dependency_links.txt +0 -0
  174. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/hgitaly.egg-info/top_level.txt +0 -0
  175. {hgitaly-18.7.0a1 → hgitaly-18.7.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hgitaly
3
- Version: 18.7.0a1
3
+ Version: 18.7.2
4
4
  Summary: Server-side implementation of Gitaly protocol for Mercurial
5
5
  Home-page: https://foss.heptapod.net/heptapod/hgitaly
6
6
  Author: Georges Racinet
@@ -10,7 +10,7 @@ Keywords: hg mercurial heptapod gitlab
10
10
  Requires-Python: >=3.8
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
- Requires-Dist: heptapod>=5.4.0
13
+ Requires-Dist: heptapod~=5.5.0
14
14
  Requires-Dist: protobuf~=6.31.1
15
15
  Requires-Dist: grpcio~=1.76.0
16
16
  Requires-Dist: grpcio-status~=1.76.0
@@ -412,7 +412,7 @@ def test_compare_get_tree_entries_pagination(gitaly_rhgitaly_comparison):
412
412
  # case of unknown revision and limit=0
413
413
  assert_compare_errors(path=b'.', revision=b'unknown',
414
414
  pagination_params=PaginationParameter(limit=0),
415
- skip_structured_errors_issue=156)
415
+ )
416
416
 
417
417
  # case of no pagination params
418
418
  assert_compare_aggregated(path=b'sub',
@@ -17,6 +17,7 @@ from hgitaly.stub.shared_pb2 import (
17
17
  from hgitaly.stub.commit_pb2 import (
18
18
  CommitLanguagesRequest,
19
19
  CommitIsAncestorRequest,
20
+ CountCommitsRequest,
20
21
  FindCommitRequest,
21
22
  FindCommitsRequest,
22
23
  LastCommitForPathRequest,
@@ -40,6 +41,8 @@ if skip_comparison_tests(): # pragma no cover
40
41
 
41
42
  parametrize = pytest.mark.parametrize
42
43
 
44
+ PSEUDO_RANGE_SEPARATORS = (b'..', b'...')
45
+
43
46
 
44
47
  def test_compare_last_commit_for(gitaly_rhgitaly_comparison):
45
48
  fixture = gitaly_rhgitaly_comparison
@@ -583,14 +586,20 @@ def test_compare_count_find_commits(gitaly_rhgitaly_comparison, hg_server):
583
586
  wrapper.update(2)
584
587
  ctx4 = wrapper.merge_commit(ctx3, message=b'merge with stable',
585
588
  utc_timestamp=ts+20)
589
+ unknown = b'unknown'
590
+ unknown_sha = b'cade12fe' * 5
591
+ range_with_unknown = b'..'.join((unknown_sha, ctx2.hex()))
592
+ assert_compare_errors(revision=unknown)
593
+ assert_compare_errors(revision=unknown_sha)
594
+ assert_compare_errors(revision=range_with_unknown)
586
595
 
587
596
  # when `revision` is provided as <revspec>
588
597
  with sorted_comparison():
589
598
  all_revs = [ctx0.hex(), ctx1.hex(), ctx2.hex(), ctx3.hex(), ctx4.hex()]
590
- for range_str in (b'..', b'...'):
599
+ for range_sep in PSEUDO_RANGE_SEPARATORS:
591
600
  for r1 in all_revs:
592
601
  for r2 in all_revs:
593
- assert_compare(revision=r1 + range_str + r2)
602
+ assert_compare(revision=r1 + range_sep + r2)
594
603
 
595
604
  # with message_regex
596
605
  rx = 'FOO.*zoO' # tests case insensitivity
@@ -688,7 +697,7 @@ def test_compare_count_find_commits(gitaly_rhgitaly_comparison, hg_server):
688
697
  hg_server=hg_server,
689
698
  stub_cls=CommitServiceStub,
690
699
  method_name='CountCommits',
691
- request_cls=FindCommitsRequest,
700
+ request_cls=CountCommitsRequest,
692
701
  request_sha_attrs=['revision'],
693
702
  )
694
703
 
@@ -697,7 +706,10 @@ def test_compare_count_find_commits(gitaly_rhgitaly_comparison, hg_server):
697
706
 
698
707
  # when `revision` is provided as <revspec>
699
708
  all_revs = [ctx0.hex(), ctx1.hex(), ctx2.hex(), ctx3.hex(), ctx4.hex()]
700
- for range_str in (b'..', ): # TODO symdiff not implemented
709
+ range_ops = [b'..']
710
+ if hg_server == 'rhgitaly':
711
+ range_ops.append(b'...')
712
+ for range_str in range_ops:
701
713
  for r1 in all_revs:
702
714
  for r2 in all_revs:
703
715
  assert_compare(revision=r1 + range_str + r2)
@@ -708,11 +720,27 @@ def test_compare_count_find_commits(gitaly_rhgitaly_comparison, hg_server):
708
720
  for ref in refs:
709
721
  rpc_helper.assert_compare(revision=ref)
710
722
 
723
+ for mc in (0, 1, 2, 3):
724
+ assert_compare(revision=ctx2.hex(), max_count=mc)
725
+
726
+ assert_compare(all=True)
727
+
728
+ if hg_server == 'rhgitaly':
729
+ for date in [date1, date2]:
730
+ assert_compare(revision=ctx4.hex(), after=date)
731
+ assert_compare(revision=ctx4.hex(), before=date)
732
+ assert_compare(revision=ctx4.hex(), before=date, after=date)
733
+ assert_compare(revision=ctx4.hex(), before=date1, after=date2)
734
+
711
735
  assert_compare_errors()
736
+ assert_compare(revision=unknown)
737
+ assert_compare(revision=unknown_sha)
738
+ assert_compare(revision=range_with_unknown)
712
739
 
713
740
 
714
- def test_compare_list_commits(gitaly_comparison):
715
- fixture = gitaly_comparison
741
+ @parametrize('hg_server', ('hgitaly', 'rhgitaly'))
742
+ def test_compare_list_commits(gitaly_rhgitaly_comparison, hg_server):
743
+ fixture = gitaly_rhgitaly_comparison
716
744
 
717
745
  wrapper = fixture.hg_repo_wrapper
718
746
  # set_default_gitlab_branch(wrapper.repo, b'branch/default')
@@ -720,7 +748,7 @@ def test_compare_list_commits(gitaly_comparison):
720
748
  #
721
749
  # @ 4 (branch/default) merge with stable
722
750
  # |\
723
- # | o 3 creates 'animal' (branch/stable)
751
+ # | o 3 creates 'animals' (branch/stable)
724
752
  # | |
725
753
  # o | 2 rename 'foo' to 'zoo' (user: testuser)
726
754
  # |/
@@ -741,9 +769,12 @@ def test_compare_list_commits(gitaly_comparison):
741
769
  ctx2 = wrapper.commit([b'foo', b'zoo'],
742
770
  message=b"rename foo to zoo",
743
771
  utc_timestamp=ts - 10)
772
+ # TODO the converted email by hg-git is more liberal, and would
773
+ # parse the email correctly from 'testuser <testuser@heptapod.test'.
774
+ # Same with HGitaly, compared to RHGitaly.
744
775
  ctx3 = wrapper.write_commit('animals', branch='stable', parent=ctx0,
745
776
  utc_timestamp=ts + 10,
746
- user='testuser <testuser@heptapod.test')
777
+ user='testuser <testuser@heptapod.test>')
747
778
  wrapper.update(2)
748
779
  ctx4 = wrapper.merge_commit(ctx3, message=b'merge with stable',
749
780
  utc_timestamp=ts+20)
@@ -754,6 +785,7 @@ def test_compare_list_commits(gitaly_comparison):
754
785
  normalize_commit_message(commit)
755
786
 
756
787
  rpc_helper = fixture.rpc_helper(
788
+ hg_server=hg_server,
757
789
  stub_cls=CommitServiceStub,
758
790
  method_name='ListCommits',
759
791
  request_cls=ListCommitsRequest,
@@ -805,12 +837,31 @@ def test_compare_list_commits(gitaly_comparison):
805
837
 
806
838
  assert_compare(revisions=[ctx4.hex(), caret(ctx1)])
807
839
  assert_compare(revisions=[ctx4.hex(), caret(ctx1)], reverse=True)
840
+ # symmetric difference (HGitaly does not accept pseudo range revisions)
841
+ # this is the only actual testing of `reverse` for RHGitaly many_commits
842
+ symdiff_revspec = b'...'.join((ctx2.hex(), ctx4.hex()))
843
+ if hg_server == 'rhgitaly':
844
+ assert_compare(revisions=[symdiff_revspec])
845
+ assert_compare(revisions=[symdiff_revspec], reverse=True)
846
+
808
847
  # interpretation of several exclusions
809
848
  assert_compare(revisions=[ctx4.hex(), caret(ctx1), caret(ctx2)])
810
849
 
811
850
  # no result
812
851
  assert_compare(revisions=[ctx4.hex(), caret(ctx4)])
813
852
 
853
+ # with paths
854
+ assert_compare(revisions=[ctx4.hex()], paths=[b'animals'])
855
+ assert_compare(revisions=[ctx4.hex()], paths=[b'foo'])
856
+ assert_compare(revisions=[ctx4.hex()], paths=[b'zoo'])
857
+ assert_compare(revisions=[ctx3.hex(), ctx2.hex()],
858
+ paths=[b'animals', b'zoo'])
859
+ assert_compare(revisions=[ctx4.hex()], paths=[b'anim*'])
860
+ # with two paths, Git starts returning the merge
861
+ # but Mercurial does not. This seems more consistent on the Mercurial
862
+ # side, so we won't compare. It would be:
863
+ # assert_compare(revisions=[ctx4.hex()], paths=[b'anim*', b'foo'])
864
+
814
865
  # orderings
815
866
  #
816
867
  # Comparison is limited because Mercurial orderings don't exactly
@@ -585,6 +585,9 @@ def test_list_refs(gitaly_rhgitaly_comparison, hg_server):
585
585
  ref_msg.name[len(prefix):])
586
586
  if all_pseudo_ref_idx is not None:
587
587
  del resp.references[all_pseudo_ref_idx]
588
+ if resp.HasField('pagination_cursor'):
589
+ pc = resp.pagination_cursor
590
+ pc.next_cursor = b64decode(pc.next_cursor).decode()
588
591
 
589
592
  rpc_helper = fixture.rpc_helper(
590
593
  hg_server=hg_server,
@@ -631,6 +634,8 @@ def test_list_refs(gitaly_rhgitaly_comparison, hg_server):
631
634
  fixture.write_special_ref(b'pipeline/13', hg_sha)
632
635
  rpc_helper.assert_compare()
633
636
  for head in False, True:
637
+ rpc_helper.assert_compare(patterns=[b'refs/'], head=head)
638
+ rpc_helper.assert_compare(patterns=[b'refs/pipeline/'], head=head)
634
639
  rpc_helper.assert_compare(patterns=[b'refs/heads/'], head=head)
635
640
  rpc_helper.assert_compare(patterns=[b'refs/tags/'], head=head)
636
641
 
@@ -692,6 +697,15 @@ def test_list_refs(gitaly_rhgitaly_comparison, hg_server):
692
697
  head=False,
693
698
  )
694
699
 
700
+ # with pagination parameters (RHGitaly only, HGitaly no longer used)
701
+ paginated = dict(head=False,
702
+ patterns=[b'refs/heads/'], # avoid ALL
703
+ pagination_params=PaginationParameter(limit=2))
704
+ if hg_server == 'rhgitaly':
705
+ rpc_helper.assert_compare(**paginated)
706
+ rpc_helper.assert_compare(sort_by=SortBy(key=SortBy.AUTHORDATE),
707
+ **paginated)
708
+
695
709
  # FindRefsByOID is almost a subset of ListRefs, the only stated
696
710
  # thing that ListRefs would not do is accepting oids by prefix.
697
711
  # So we'll use the same setup
@@ -0,0 +1 @@
1
+ 18.7.2
@@ -60,6 +60,9 @@ def revset_from_git_revspec(repo, revspec, for_follow=False):
60
60
  return None
61
61
  return ctx.hex() if for_follow else b"reverse(::%s)" % ctx
62
62
 
63
+ if revset is None:
64
+ return None
65
+
63
66
  return b"sort(%s, -rev)" % revset
64
67
 
65
68
 
@@ -90,6 +93,9 @@ def revset_git_range(repo, r1, r2, for_follow=False):
90
93
  raise FollowNotImplemented()
91
94
 
92
95
  ctx_start, ctx_end = resolve_git_range(repo, r1, r2)
96
+ if ctx_start is None or ctx_end is None:
97
+ return None
98
+
93
99
  return b"::%s - ::%s" % (ctx_end, ctx_start)
94
100
 
95
101
 
@@ -114,6 +120,9 @@ def revset_symmetric_difference(repo, r1, r2, for_follow=False):
114
120
  raise FollowNotImplemented()
115
121
 
116
122
  ctx_start, ctx_end = resolve_git_range(repo, r1, r2)
123
+ if ctx_start is None or ctx_end is None:
124
+ return None
125
+
117
126
  left = ctx_start.rev()
118
127
  right = ctx_end.rev()
119
128
  branchpoint = repo.revs(b"ancestor(%d, %d)" % (left, right)).first()
@@ -0,0 +1,3 @@
1
+ # flake8: noqa F401 (reexports)
2
+ from .stats import stats
3
+ from .load_tests import load_test
@@ -0,0 +1,373 @@
1
+ """Load tests for HGitaly.
2
+
3
+ This simple script assumes that the HGitaly server has a given set of
4
+ repositories:
5
+
6
+ They must absolutely be dedicated to the task, because the load tests
7
+ may involve mutation calls. Hence the script expects the HGitaly server
8
+ to operate on its own separate repositories root and will clone everything
9
+ if needed.
10
+
11
+ Future facilities:
12
+
13
+ - configuring and launching RHGitaly
14
+ """
15
+ import argparse
16
+ import json
17
+ import logging
18
+ import random
19
+ import statistics
20
+ import threading
21
+ import multiprocessing as mp
22
+ import time
23
+ from urllib.parse import urlparse
24
+
25
+ import grpc
26
+
27
+ from hgitaly import feature
28
+ from hgitaly.servicer import (
29
+ SKIP_HOOKS_MD_KEY,
30
+ NATIVE_PROJECT_MD_KEY,
31
+ )
32
+
33
+ from hgitaly.stub.commit_pb2_grpc import CommitServiceStub
34
+ from hgitaly.stub.commit_pb2 import (
35
+ CountCommitsRequest,
36
+ FindCommitsRequest,
37
+ )
38
+ from hgitaly.stub.mercurial_operations_pb2 import (
39
+ InvalidateMergeAnalysisRequest,
40
+ MergeAnalysisRequest,
41
+ )
42
+ from hgitaly.stub.mercurial_operations_pb2_grpc import (
43
+ MercurialOperationsServiceStub,
44
+ )
45
+ from hgitaly.stub.mercurial_repository_pb2_grpc import (
46
+ MercurialRepositoryServiceStub,
47
+ )
48
+ from hgitaly.stub.mercurial_repository_pb2 import (
49
+ HgCallRequest,
50
+ )
51
+ from hgitaly.stub.repository_pb2_grpc import RepositoryServiceStub
52
+ from hgitaly.stub.repository_pb2 import (
53
+ CreateRepositoryRequest,
54
+ RepositoryExistsRequest,
55
+ )
56
+ from hgitaly.stub.shared_pb2 import (
57
+ Repository,
58
+ )
59
+
60
+ FOSS_HEPTAPOD = 'https://foss.heptapod.net'
61
+
62
+ logger = logging.getLogger(__name__)
63
+
64
+
65
+ def gl_repo(relpath):
66
+ return Repository(storage_name='default', relative_path=relpath)
67
+
68
+
69
+ class LoadWorker:
70
+
71
+ weighted_methods = (
72
+ ('find_commits_mercurial_devel', 9),
73
+ ('sleep_a_bit', 1),
74
+ ('merge_analysis_mercurial_devel', 1),
75
+ )
76
+ repositories = {
77
+ 'mercurial-devel': FOSS_HEPTAPOD + '/mercurial/mercurial-devel',
78
+ }
79
+ feature_flags = ()
80
+
81
+ @classmethod
82
+ def compute_random_thresholds(cls):
83
+ total_weight = sum(wm[1] for wm in cls.weighted_methods)
84
+
85
+ thresholds = []
86
+ current = 0
87
+ for meth, weight in cls.weighted_methods:
88
+ thresholds.append((current, meth))
89
+ current += weight / total_weight
90
+ cls.random_thresholds = thresholds
91
+ logger.warning("Random thresholds: %r", thresholds)
92
+
93
+ @classmethod
94
+ def random_method(cls):
95
+ rand = random.random()
96
+ candidate = cls.random_thresholds[0][0]
97
+ for thr, name in cls.random_thresholds:
98
+ if thr > rand:
99
+ break
100
+ candidate = name
101
+ return candidate
102
+
103
+ def __init__(self, url, queue, wid=None):
104
+ self.url = url = urlparse(url)
105
+ self.queue = queue
106
+
107
+ if url.scheme != 'tcp':
108
+ raise RuntimeError("Unsupported URL scheme: %r" % url.scheme)
109
+ if wid is not None:
110
+ logger.info("Worker %d starting", wid)
111
+ self.channel = grpc.insecure_channel(url.netloc)
112
+ self.hg_operations_service = MercurialOperationsServiceStub(
113
+ self.channel)
114
+ self.hg_repository_service = MercurialRepositoryServiceStub(
115
+ self.channel)
116
+ self.repository_service = RepositoryServiceStub(self.channel)
117
+ self.commit_service = CommitServiceStub(self.channel)
118
+
119
+ def close(self):
120
+ self.channel.close()
121
+
122
+ def grpc_metadata(self):
123
+ mds = feature.as_grpc_metadata(self.feature_flags)
124
+ mds.append((SKIP_HOOKS_MD_KEY, 'true'))
125
+ mds.append((NATIVE_PROJECT_MD_KEY, 'true'))
126
+ return mds
127
+
128
+ def repo_exists(self, relpath):
129
+ return self.repository_service.RepositoryExists(
130
+ RepositoryExistsRequest(repository=gl_repo(relpath))
131
+ ).exists
132
+
133
+ def ensure_repos(self):
134
+ for relpath in self.repositories:
135
+ if self.repo_exists(relpath):
136
+ logger.info("Repository %r is present", relpath)
137
+ else:
138
+ self.import_repo(relpath)
139
+
140
+ def import_repo(self, relpath):
141
+ logger.warning("Importing repository %r", relpath)
142
+ self.repository_service.CreateRepository(
143
+ CreateRepositoryRequest(repository=gl_repo(relpath))
144
+ )
145
+
146
+ src_url = self.repositories[relpath]
147
+ self.hg_call(relpath,
148
+ (b"pull", b"-q",
149
+ b"--config", b"heptapod.initial-import=yes",
150
+ src_url.encode('ascii')),
151
+ timeout=3600)
152
+
153
+ def hg_call(self, relpath, args, timeout=30):
154
+ exit_code = -1
155
+ for resp in self.hg_repository_service.HgCall(
156
+ HgCallRequest(repository=gl_repo(relpath),
157
+ args=args),
158
+ metadata=self.grpc_metadata(),
159
+ timeout=timeout,
160
+ ):
161
+ exit_code = resp.exit_code
162
+ if exit_code != 0:
163
+ raise RuntimeError(f"HgCall exit code {exit_code}")
164
+
165
+ def run_one(self, name):
166
+ start = time.time()
167
+ try:
168
+ getattr(self, name)()
169
+ code = 'OK'
170
+ except grpc.RpcError as exc:
171
+ code = exc.code().name
172
+ if self.queue is None:
173
+ logger.error(
174
+ "Failed request when checking before actual load test"
175
+ )
176
+ raise
177
+
178
+ elapsed = time.time() - start
179
+
180
+ if self.queue is None:
181
+ logger.info("Ran %r in %d ms", name, elapsed * 1000)
182
+ else:
183
+ self.queue.put((name, code, elapsed), block=False, timeout=1)
184
+
185
+ def run_test(self, iterations):
186
+ for i in range(iterations):
187
+ meth = self.random_method()
188
+ logger.info("Iteration %d, running %r", i + 1, meth)
189
+ self.run_one(meth)
190
+
191
+ def sleep_a_bit(self):
192
+ # this can make RHGitaly find there are too many idle workers
193
+ # and recycle them, leading to interesting problems because of
194
+ # long HGitaly startup time.
195
+ time.sleep(2)
196
+
197
+ def find_commits_mercurial_devel(self):
198
+ count = 0
199
+ future = self.commit_service.FindCommits(FindCommitsRequest(
200
+ repository=gl_repo('mercurial-devel'),
201
+ revision=b'branch/default',
202
+ limit=100000,
203
+ paths=[b'rust/hg-core/Cargo.toml'],
204
+ ))
205
+
206
+ # see also `streaming_future_with_cancellation()`
207
+ for resp in future:
208
+ count += len(resp.commits)
209
+
210
+ logger.debug("Got FindCommits response with %d commits", count)
211
+
212
+ def count_commits_mercurial_devel(self):
213
+ resp = self.commit_service.CountCommits(CountCommitsRequest(
214
+ repository=gl_repo('mercurial-devel'),
215
+ revision=b'branch/default',
216
+ ))
217
+ logger.debug("Got FindCommits response with %d commits", resp.count)
218
+
219
+ def merge_analysis_mercurial_devel(self):
220
+ repo = gl_repo('mercurial-devel')
221
+ self.hg_operations_service.InvalidateMergeAnalysis(
222
+ InvalidateMergeAnalysisRequest(repository=repo)
223
+ )
224
+ # an actual merge, without conflicts
225
+ future = self.hg_operations_service.MergeAnalysis.future(
226
+ MergeAnalysisRequest(
227
+ repository=repo,
228
+ source_revision=b'412fd1f5d8bc',
229
+ target_revision=b'ff85442d08d7',
230
+ ),
231
+ timeout=120)
232
+ future.result() # see also unary_future_with_cancellation()
233
+
234
+ def assert_requests(self):
235
+ """Run all requiests exactly once.
236
+
237
+ If one of them fails (typically due to a programming mistake),
238
+ better to know it before running thousands of them
239
+ """
240
+ for (name, _w) in self.weighted_methods:
241
+ if name != 'sleep_a_bit':
242
+ self.run_one(name) # TODO check exit code
243
+
244
+
245
+ def cancel_in(future, ms):
246
+ time.sleep(ms / 1000)
247
+ logger.info("Client-side cancellation")
248
+ future.cancel()
249
+
250
+
251
+ def spawn_cancel_thread(*args):
252
+ t = threading.Thread(target=cancel_in, args=args)
253
+ t.start()
254
+ return t
255
+
256
+
257
+ def unary_future_with_cancellation(future, cancel_in_ms):
258
+ """Actually send the request, but cancel it first."""
259
+ t = spawn_cancel_thread(future, cancel_in_ms)
260
+ try:
261
+ return future.result()
262
+ finally:
263
+ t.join()
264
+
265
+
266
+ def streaming_future_with_cancellation(future, cancel_in_ms):
267
+ """Actually send the request, but cancel it first."""
268
+ t = spawn_cancel_thread(future, cancel_in_ms)
269
+ try:
270
+ for resp in future:
271
+ yield resp
272
+ finally:
273
+ t.join()
274
+
275
+
276
+ LoadWorker.compute_random_thresholds()
277
+ x = LoadWorker.random_method()
278
+ x = LoadWorker.random_method()
279
+ x = LoadWorker.random_method()
280
+
281
+
282
+ def worker(wid, url, queue, iterations):
283
+ worker = LoadWorker(url, queue, wid=wid)
284
+ worker.run_test(iterations)
285
+ queue.put(('DONE', wid))
286
+
287
+
288
+ def load_test():
289
+ parser = argparse.ArgumentParser(
290
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
291
+ description=__doc__)
292
+
293
+ parser.add_argument('--url', '--rhgitaly-url',
294
+ help="URL of the RHGitaly server",
295
+ default='tcp://127.0.0.1:9237')
296
+ parser.add_argument('-c', '--concurrency', type=int, default=6)
297
+ parser.add_argument('--iterations', type=int, default=1000,
298
+ help="Number of iteration per client worker")
299
+ parser.add_argument('-l', '--logging-level', default='info')
300
+ cl_args = parser.parse_args()
301
+
302
+ logging.basicConfig(level=getattr(logging,
303
+ cl_args.logging_level.upper()))
304
+
305
+ concurrency = cl_args.concurrency
306
+
307
+ prepare_worker = LoadWorker(cl_args.url, queue=None)
308
+ prepare_worker.ensure_repos()
309
+ prepare_worker.assert_requests()
310
+ prepare_worker.close()
311
+
312
+ logger.warning("Preflight check of all requests passed. Proceeding to "
313
+ "actual load test")
314
+
315
+ queue = mp.Queue()
316
+
317
+ ctx = mp.get_context('fork')
318
+ processes = [ctx.Process(target=worker,
319
+ args=(i, cl_args.url, queue, cl_args.iterations))
320
+ for i in range(concurrency)]
321
+
322
+ for p in processes:
323
+ p.start()
324
+
325
+ done_process_ids = []
326
+ results = {}
327
+ while len(done_process_ids) < len(processes):
328
+ res = queue.get()
329
+ if res[0] == 'DONE':
330
+ wid = res[1]
331
+ processes[wid].join()
332
+ done_process_ids.append(wid)
333
+ logger.warning("Worker %d is done", wid)
334
+ else:
335
+ (results.setdefault(res[1], {})
336
+ .setdefault(res[0], [])
337
+ .append(res[2])
338
+ )
339
+
340
+ error_stats = {}
341
+ for code, details in results.items():
342
+ if code != 'OK':
343
+ for name, times in details.items():
344
+ error_stats.setdefault(name, {})[code] = dict(
345
+ count=len(times),
346
+ mean_time=statistics.mean(times)
347
+ )
348
+
349
+ successes = results.get('OK', {})
350
+ stats = {}
351
+ for name, times in successes.items():
352
+ mstats = stats[name] = dict(
353
+ count=len(times),
354
+ median=statistics.mean(times),
355
+ mean=statistics.median(times),
356
+ standard_deviation=statistics.pstdev(times),
357
+ )
358
+ if len(times) > 1:
359
+
360
+ deciles = statistics.quantiles(times, method='inclusive', n=10)
361
+ centiles = statistics.quantiles(times, method='inclusive',
362
+ n=100)
363
+ mstats["best_centile"] = centiles[0]
364
+ mstats["best_decile"] = deciles[0],
365
+ mstats["worst_centile"] = centiles[-1],
366
+ mstats["worst_decile"] = deciles[-1],
367
+
368
+ print(f"\n\nRESULTS for concurrency={concurrency}: ")
369
+ print(json.dumps(stats, indent=2))
370
+
371
+ if error_stats:
372
+ print(f"\n\nERRORS for concurrency={concurrency}: ")
373
+ print(json.dumps(error_stats, indent=2))
@@ -559,6 +559,14 @@ class CommitServicer(CommitServiceServicer, HGitalyServicer):
559
559
  if request.max_parents == 1:
560
560
  revset += b" and not merge()"
561
561
 
562
+ if request.paths:
563
+ revset += b" and ("
564
+ revset += b" or ".join(
565
+ b'file("%s/%s")' % (repo.root, p)
566
+ for p in request.paths
567
+ )
568
+ revset += b")"
569
+
562
570
  Order = ListCommitsRequest.Order
563
571
  reverse = request.reverse
564
572
 
@@ -223,6 +223,11 @@ def test_find_commits(commit_fixture_empty_repo):
223
223
  # when revision does not exists (including special Node IDs)
224
224
  for rev in (b'does_not_exists',
225
225
  b'1234deadbeaf',
226
+ b'1234dead' * 5,
227
+ b'unknown..%s' % ctx1.hex(),
228
+ b'%s..unknown' % ctx1.hex(),
229
+ b'unknown...%s' % ctx1.hex(),
230
+ b'%s...unknown' % ctx1.hex(),
226
231
  b'ffffffff' * 5,
227
232
  b'ffffffff'):
228
233
  with pytest.raises(grpc.RpcError) as exc_info:
@@ -805,6 +810,13 @@ def test_list_commits(commit_fixture_empty_repo):
805
810
  # author regexp
806
811
  assert list_commits(sha4, author=b't.stuser') == [sha3]
807
812
 
813
+ # paths
814
+ assert list_commits(sha4, paths=[b'animals']) == [sha3]
815
+ assert list_commits(sha4, paths=[b'anim*']) == [sha3]
816
+ # Mercurial does not count the merge (see also comment in Comp test)
817
+ assert list_commits(sha4,
818
+ paths=[b'foo', b'animals']) == [sha3, sha2, sha0]
819
+
808
820
  # unknown revision, including special cases:
809
821
  for unknown in ('branch/unknown',
810
822
  'f' * 39, # wdir node id, prefix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hgitaly
3
- Version: 18.7.0a1
3
+ Version: 18.7.2
4
4
  Summary: Server-side implementation of Gitaly protocol for Mercurial
5
5
  Home-page: https://foss.heptapod.net/heptapod/hgitaly
6
6
  Author: Georges Racinet
@@ -10,7 +10,7 @@ Keywords: hg mercurial heptapod gitlab
10
10
  Requires-Python: >=3.8
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
- Requires-Dist: heptapod>=5.4.0
13
+ Requires-Dist: heptapod~=5.5.0
14
14
  Requires-Dist: protobuf~=6.31.1
15
15
  Requires-Dist: grpcio~=1.76.0
16
16
  Requires-Dist: grpcio-status~=1.76.0