hgitaly 17.8.1__tar.gz → 17.8.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 (172) hide show
  1. {hgitaly-17.8.1/hgitaly.egg-info → hgitaly-17.8.2}/PKG-INFO +1 -1
  2. hgitaly-17.8.2/hgitaly/VERSION +1 -0
  3. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/gitlab_ref.py +51 -3
  4. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/mercurial_repository.py +60 -1
  5. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_mercurial_repository.py +20 -0
  6. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/mercurial_repository_pb2.py +13 -5
  7. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/mercurial_repository_pb2_grpc.py +34 -0
  8. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_gitlab_ref.py +21 -0
  9. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_workdir.py +98 -1
  10. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/workdir.py +104 -16
  11. {hgitaly-17.8.1 → hgitaly-17.8.2/hgitaly.egg-info}/PKG-INFO +1 -1
  12. hgitaly-17.8.1/hgitaly/VERSION +0 -1
  13. {hgitaly-17.8.1 → hgitaly-17.8.2}/LICENSE +0 -0
  14. {hgitaly-17.8.1 → hgitaly-17.8.2}/MANIFEST.in +0 -0
  15. {hgitaly-17.8.1 → hgitaly-17.8.2}/README.md +0 -0
  16. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgext3rd/__init__.py +0 -0
  17. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgext3rd/hgitaly/__init__.py +0 -0
  18. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgext3rd/hgitaly/revset.py +0 -0
  19. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgext3rd/hgitaly/tests/__init__.py +0 -0
  20. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgext3rd/hgitaly/tests/test_revset.py +0 -0
  21. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgext3rd/hgitaly/tests/test_serve.py +0 -0
  22. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/__init__.py +0 -0
  23. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/branch.py +0 -0
  24. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/changelog.py +0 -0
  25. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/diff.py +0 -0
  26. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/errors.py +0 -0
  27. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/feature.py +0 -0
  28. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/file_content.py +0 -0
  29. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/file_context.py +0 -0
  30. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/git.py +0 -0
  31. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/license_detector/__init__.py +0 -0
  32. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/license_detector/spdx-licenses.json +0 -0
  33. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/linguist/__init__.py +0 -0
  34. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/linguist/languages.json +0 -0
  35. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/logging.py +0 -0
  36. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/manifest.py +0 -0
  37. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/message.py +0 -0
  38. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/oid.py +0 -0
  39. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/pagination.py +0 -0
  40. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/path.py +0 -0
  41. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/peer.py +0 -0
  42. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/procutil.py +0 -0
  43. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/repository.py +0 -0
  44. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/revision.py +0 -0
  45. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/revset.py +0 -0
  46. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/scripts.py +0 -0
  47. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/__init__.py +0 -0
  48. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/address.py +0 -0
  49. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/mono.py +0 -0
  50. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/prefork.py +0 -0
  51. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/tests/__init__.py +0 -0
  52. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/tests/test_address.py +0 -0
  53. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/tests/test_mono.py +0 -0
  54. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/tests/test_prefork.py +0 -0
  55. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/tests/test_worker.py +0 -0
  56. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/server/worker.py +0 -0
  57. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/__init__.py +0 -0
  58. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/analysis.py +0 -0
  59. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/blob.py +0 -0
  60. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/commit.py +0 -0
  61. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/diff.py +0 -0
  62. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/interceptors.py +0 -0
  63. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/mercurial_changeset.py +0 -0
  64. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/mercurial_operations.py +0 -0
  65. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/operations.py +0 -0
  66. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/ref.py +0 -0
  67. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/repository.py +0 -0
  68. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/server.py +0 -0
  69. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/__init__.py +0 -0
  70. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/fixture.py +0 -0
  71. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_analysis.py +0 -0
  72. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_blob.py +0 -0
  73. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_commit.py +0 -0
  74. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_default_branch.py +0 -0
  75. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_diff.py +0 -0
  76. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_mercurial_changeset.py +0 -0
  77. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_mercurial_operations.py +0 -0
  78. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_operations.py +0 -0
  79. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_ref.py +0 -0
  80. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_repository_service.py +0 -0
  81. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/service/tests/test_server.py +0 -0
  82. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/servicer.py +0 -0
  83. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/ssh.py +0 -0
  84. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stream.py +0 -0
  85. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/__init__.py +0 -0
  86. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/analysis_pb2.py +0 -0
  87. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/analysis_pb2_grpc.py +0 -0
  88. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/blob_pb2.py +0 -0
  89. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/blob_pb2_grpc.py +0 -0
  90. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/commit_pb2.py +0 -0
  91. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/commit_pb2_grpc.py +0 -0
  92. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/diff_pb2.py +0 -0
  93. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/diff_pb2_grpc.py +0 -0
  94. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/errors_pb2.py +0 -0
  95. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/errors_pb2_grpc.py +0 -0
  96. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/lint_pb2.py +0 -0
  97. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/lint_pb2_grpc.py +0 -0
  98. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/mercurial_aux_git_pb2.py +0 -0
  99. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/mercurial_aux_git_pb2_grpc.py +0 -0
  100. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/mercurial_changeset_pb2.py +0 -0
  101. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/mercurial_changeset_pb2_grpc.py +0 -0
  102. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/mercurial_operations_pb2.py +0 -0
  103. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/mercurial_operations_pb2_grpc.py +0 -0
  104. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/operations_pb2.py +0 -0
  105. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/operations_pb2_grpc.py +0 -0
  106. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/ref_pb2.py +0 -0
  107. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/ref_pb2_grpc.py +0 -0
  108. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/remote_pb2.py +0 -0
  109. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/remote_pb2_grpc.py +0 -0
  110. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/repository_pb2.py +0 -0
  111. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/repository_pb2_grpc.py +0 -0
  112. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/server_pb2.py +0 -0
  113. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/server_pb2_grpc.py +0 -0
  114. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/shared_pb2.py +0 -0
  115. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/stub/shared_pb2_grpc.py +0 -0
  116. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tag.py +0 -0
  117. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/testing/__init__.py +0 -0
  118. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/testing/bundle.py +0 -0
  119. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/testing/context.py +0 -0
  120. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/testing/grpc.py +0 -0
  121. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/testing/ssh.py +0 -0
  122. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/testing/sshd.py +0 -0
  123. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/testing/storage.py +0 -0
  124. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/testing/tests/__init__.py +0 -0
  125. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/testing/tests/test_sshd.py +0 -0
  126. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/__init__.py +0 -0
  127. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/common.py +0 -0
  128. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_branch.py +0 -0
  129. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_diff.py +0 -0
  130. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_errors.py +0 -0
  131. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_feature.py +0 -0
  132. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_file_context.py +0 -0
  133. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_license_detector.py +0 -0
  134. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_linguist.py +0 -0
  135. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_manifest.py +0 -0
  136. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_messages.py +0 -0
  137. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_oid.py +0 -0
  138. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_peer.py +0 -0
  139. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_repository.py +0 -0
  140. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_revision.py +0 -0
  141. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_revset.py +0 -0
  142. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_servicer.py +0 -0
  143. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_stream.py +0 -0
  144. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/tests/test_tag.py +0 -0
  145. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly/util.py +0 -0
  146. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly.egg-info/SOURCES.txt +0 -0
  147. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly.egg-info/dependency_links.txt +0 -0
  148. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly.egg-info/entry_points.txt +0 -0
  149. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly.egg-info/requires.txt +0 -0
  150. {hgitaly-17.8.1 → hgitaly-17.8.2}/hgitaly.egg-info/top_level.txt +0 -0
  151. {hgitaly-17.8.1 → hgitaly-17.8.2}/install-requirements.txt +0 -0
  152. {hgitaly-17.8.1 → hgitaly-17.8.2}/setup.cfg +0 -0
  153. {hgitaly-17.8.1 → hgitaly-17.8.2}/setup.py +0 -0
  154. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/__init__.py +0 -0
  155. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/comparison.py +0 -0
  156. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/conftest.py +0 -0
  157. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/gitaly.py +0 -0
  158. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/hgitaly_rhgitaly_comparison.py +0 -0
  159. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/rhgitaly.py +0 -0
  160. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_blob_tree.py +0 -0
  161. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_commit.py +0 -0
  162. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_comparison.py +0 -0
  163. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_diff.py +0 -0
  164. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_gitaly_server.py +0 -0
  165. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_mercurial_aux_git.py +0 -0
  166. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_mercurial_repository.py +0 -0
  167. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_operations.py +0 -0
  168. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_ref.py +0 -0
  169. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_remote.py +0 -0
  170. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_repository_service.py +0 -0
  171. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_rhgitaly_server.py +0 -0
  172. {hgitaly-17.8.1 → hgitaly-17.8.2}/tests_with_gitaly/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hgitaly
3
- Version: 17.8.1
3
+ Version: 17.8.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
@@ -0,0 +1 @@
1
+ 17.8.2
@@ -13,6 +13,8 @@ utilities about refs, i.e, anything about a full ref path, such as
13
13
  or tags.
14
14
  """
15
15
  from fnmatch import fnmatch
16
+ import os
17
+ import re
16
18
 
17
19
  from heptapod.gitlab.branch import gitlab_branch_ref
18
20
  from heptapod.gitlab.tag import gitlab_tag_ref
@@ -30,10 +32,40 @@ from hgext3rd.heptapod.special_ref import (
30
32
  )
31
33
  from hgext3rd.heptapod.keep_around import (
32
34
  iter_keep_arounds,
35
+ init_keep_arounds,
33
36
  KEEP_AROUND_REF_PREFIX,
34
37
  KEEP_AROUND_REF_PREFIX_LEN,
35
38
  )
36
39
 
40
+ DOT_HG_RX = re.compile(br'\.hg$')
41
+
42
+
43
+ def has_legacy_git_repo(repo):
44
+ """Return whether there is a legacy Git repo for this Mercurial repo.
45
+
46
+ With "legacy" here is meant the Git repository for a non-native project.
47
+ Git repositories meant for mirroring are *not* included in this scope.
48
+
49
+ As of Heptapod 17.6, this is the best way to know without direct indication
50
+ (from the inside) that a repository is not legacy (hg-git based).
51
+ The reverse implication is false, as there are loopholes:
52
+
53
+ - leftover Git repositories for Projects that have migrated to native
54
+ - native projects with addition conversion for mirrors whose Git
55
+ repositories have not been moved yet (bug, time to run the migration,
56
+ etc.)
57
+
58
+ The caller must be aware of these loopholes and avoid overwriting existing
59
+ data based on the legacy Git repo. In the intended case where this is to
60
+ decide on calling the `ensure` methods, this means it is ok only if there
61
+ is no existing data.
62
+ """
63
+ # if a repository is legacy, based on hg-git conversion, is whether
64
+ # it has a `.git` repository at the same location. Git repositories
65
+ # for mirroring are in a different place
66
+ git_path = DOT_HG_RX.sub(b'', repo.root) + b'.git'
67
+ return os.path.exists(git_path)
68
+
37
69
 
38
70
  def gitlab_special_ref_target(repo, ref_path):
39
71
  """Return the changeset for a special ref.
@@ -115,7 +147,23 @@ def iter_gitlab_special_refs_as_refs(repo, deref=True, patterns=None):
115
147
 
116
148
 
117
149
  def ensure_special_refs(repo):
118
- return ensure_gitlab_special_refs(repo.ui, repo)
150
+ if has_legacy_git_repo(repo):
151
+ return ensure_gitlab_special_refs(repo.ui, repo)
152
+ return {}
153
+
154
+
155
+ def ensure_keep_arounds(repo, init_empty=False):
156
+ """Ensure keep around from Git repo if present or create.
157
+
158
+ An empty file is created so that the keep-arounds file is no
159
+ more missing, but only if `init_empty` is `True`, so that
160
+ responsibility is handed to the caller, than must use the
161
+ option only after having obtained the missing marker.
162
+ """
163
+ if has_legacy_git_repo(repo):
164
+ ensure_gitlab_keep_arounds(repo.ui, repo)
165
+ elif init_empty:
166
+ init_keep_arounds(repo, ())
119
167
 
120
168
 
121
169
  def has_keep_around(repo, sha):
@@ -125,7 +173,7 @@ def has_keep_around(repo, sha):
125
173
  """
126
174
  for ka in iter_keep_arounds(repo):
127
175
  if ka is GITLAB_TYPED_REFS_MISSING:
128
- ensure_gitlab_keep_arounds(repo.ui, repo)
176
+ ensure_keep_arounds(repo, init_empty=True)
129
177
  return has_keep_around(repo, sha)
130
178
  if ka == sha:
131
179
  return True
@@ -146,7 +194,7 @@ def parse_keep_around_ref_path(ref):
146
194
  def iter_keep_arounds_as_refs(repo, deref=True, patterns=None):
147
195
  for sha in iter_keep_arounds(repo):
148
196
  if sha is GITLAB_TYPED_REFS_MISSING:
149
- ensure_gitlab_keep_arounds(repo.ui, repo)
197
+ ensure_keep_arounds(repo, init_empty=True)
150
198
  yield from iter_keep_arounds_as_refs(repo, deref=deref)
151
199
  return
152
200
  ref_path = keep_around_ref_path(sha)
@@ -16,6 +16,7 @@ from mercurial import (
16
16
  )
17
17
 
18
18
  from ..branch import iter_gitlab_branches_matching
19
+ from ..logging import LoggerAdapter
19
20
  from ..peer import (
20
21
  FileURLOutsidePath,
21
22
  InvalidURLScheme,
@@ -30,6 +31,10 @@ from ..repository import (
30
31
  set_config_inheritance,
31
32
  set_managed_config,
32
33
  )
34
+ from ..workdir import (
35
+ remove_unlisted_workdirs,
36
+ workdirs_gc,
37
+ )
33
38
  from ..errors import (
34
39
  not_implemented,
35
40
  )
@@ -39,6 +44,8 @@ from ..stub.mercurial_repository_pb2 import (
39
44
  GetConfigItemResponse,
40
45
  GetManagedConfigRequest,
41
46
  GetManagedConfigResponse,
47
+ HousekeepingRequest,
48
+ HousekeepingResponse,
42
49
  PushRequest,
43
50
  PushResponse,
44
51
  SetManagedConfigRequest,
@@ -49,7 +56,7 @@ from ..stub.mercurial_repository_pb2_grpc import (
49
56
  )
50
57
  from ..servicer import HGitalyServicer
51
58
 
52
- logger = logging.getLogger(__name__)
59
+ base_logger = logging.getLogger(__name__)
53
60
 
54
61
 
55
62
  class MercurialRepositoryServicer(MercurialRepositoryServiceServicer,
@@ -186,3 +193,55 @@ class MercurialRepositoryServicer(MercurialRepositoryServiceServicer,
186
193
  context.abort(StatusCode.INTERNAL,
187
194
  "Unexpected error, not in the actual "
188
195
  "push to %r: %s" % (remote_url, exc))
196
+
197
+ def Housekeeping(self,
198
+ request: HousekeepingRequest,
199
+ context) -> HousekeepingResponse:
200
+ logger = LoggerAdapter(base_logger, context)
201
+ gl_repo = request.repository
202
+ repo = self.load_repo(gl_repo, context)
203
+ tasks = []
204
+ resp_attrs = {}
205
+ if request.fail:
206
+ tasks.append('fail')
207
+ if request.working_directories_age_threshold_seconds:
208
+ tasks.append('working_dirs_gc')
209
+ if request.working_directories_remove_unlisted:
210
+ tasks.append('remove_unlisted_working_dirs')
211
+ if request.recover:
212
+ tasks.append('recover')
213
+ for task in tasks:
214
+ try:
215
+ resp_attrs.update(getattr(self, 'housekeeping_' + task)(
216
+ repo=repo,
217
+ request=request,
218
+ context=context
219
+ ))
220
+ except Exception as exc:
221
+ logger.error("Housekeeping task %s failed: %r" % (task, exc))
222
+
223
+ return HousekeepingResponse(**resp_attrs)
224
+
225
+ def housekeeping_fail(self, *a, **kw):
226
+ raise RuntimeError("client-required failure")
227
+
228
+ def housekeeping_working_dirs_gc(self, repo, request, context):
229
+ workdirs_gc(
230
+ workdirs_root=self.repo_workdirs_root(request.repository, context),
231
+ repo=repo,
232
+ max_age_seconds=request.working_directories_age_threshold_seconds
233
+ )
234
+ return [('working_directories_gc', True)]
235
+
236
+ def housekeeping_remove_unlisted_working_dirs(self, repo,
237
+ request, context):
238
+ remove_unlisted_workdirs(
239
+ repo=repo,
240
+ workdirs_root=self.repo_workdirs_root(request.repository, context),
241
+ )
242
+ return [('working_directories_remove_unlisted', True)]
243
+
244
+ def housekeeping_recover(self, repo, request, context):
245
+ return (('recover_run', True),
246
+ ('recovered_interrupted_transaction', repo.recover()),
247
+ )
@@ -26,6 +26,7 @@ from hgitaly.stub.mercurial_repository_pb2 import (
26
26
  ConfigItemType,
27
27
  GetConfigItemRequest,
28
28
  GetManagedConfigRequest,
29
+ HousekeepingRequest,
29
30
  SetManagedConfigRequest,
30
31
  HeptapodConfigSection,
31
32
  MercurialPeer,
@@ -86,6 +87,10 @@ class ConfigFixture(ServiceFixture):
86
87
  def include_managed_hgrc(self):
87
88
  self.append_main_hgrc('', '%include hgrc.managed', '')
88
89
 
90
+ def housekeeping(self, **kw):
91
+ kw.setdefault('repository', self.grpc_repo)
92
+ return self.stub.Housekeeping(HousekeepingRequest(**kw))
93
+
89
94
 
90
95
  @pytest.fixture
91
96
  def config_fixture(grpc_channel, server_repos_root):
@@ -511,3 +516,18 @@ def test_push_invalid_url(push_fixture, url):
511
516
  with pytest.raises(grpc.RpcError) as exc_info:
512
517
  push(remote_url=url)
513
518
  assert exc_info.value.code() == grpc.StatusCode.INVALID_ARGUMENT
519
+
520
+
521
+ def test_housekeeping(config_fixture):
522
+ fixture = config_fixture
523
+
524
+ fixture.housekeeping(fail=True)
525
+
526
+ assert fixture.housekeeping(
527
+ working_directories_age_threshold_seconds=1).working_directories_gc
528
+
529
+ fixture.housekeeping(working_directories_remove_unlisted=True)
530
+
531
+ resp = fixture.housekeeping(recover=True)
532
+ assert resp.recover_run
533
+ assert not resp.recovered_interrupted_transaction
@@ -15,7 +15,7 @@ from . import lint_pb2 as lint__pb2
15
15
  from . import shared_pb2 as shared__pb2
16
16
 
17
17
 
18
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1amercurial-repository.proto\x12\x07hgitaly\x1a\nlint.proto\x1a\x0cshared.proto\"Y\n\x11InitConfigRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\x16\n\x0enamespace_path\x18\x02 \x01(\t\"\x14\n\x12InitConfigResponse\"\x95\x02\n\x15HeptapodConfigSection\x12!\n\x14\x61llow_multiple_heads\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x1c\n\x0f\x61llow_bookmarks\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x45\n\x0c\x61uto_publish\x18\x04 \x01(\x0e\x32*.hgitaly.HeptapodConfigSection.AutoPublishH\x02\x88\x01\x01\"6\n\x0b\x41utoPublish\x12\x11\n\rWITHOUT_TOPIC\x10\x00\x12\x0b\n\x07NOTHING\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x17\n\x15_allow_multiple_headsB\x12\n\x10_allow_bookmarksB\x0f\n\r_auto_publish\"V\n\x17GetManagedConfigRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\r\n\x05local\x18\x02 \x01(\x08\"]\n\x18GetManagedConfigResponse\x12\x0f\n\x07inherit\x18\x01 \x01(\x08\x12\x30\n\x08heptapod\x18\x02 \x01(\x0b\x32\x1e.hgitaly.HeptapodConfigSection\"\xc2\x01\n\x17SetManagedConfigRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\x14\n\x07inherit\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x30\n\x08heptapod\x18\x03 \x01(\x0b\x32\x1e.hgitaly.HeptapodConfigSection\x12\x14\n\x0cremove_items\x18\x04 \x03(\t\x12\x0f\n\x07\x62y_line\x18\x05 \x01(\tB\n\n\x08_inherit\"\x1a\n\x18SetManagedConfigResponse\"\x8d\x01\n\x14GetConfigItemRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12(\n\x07\x61s_type\x18\x02 \x01(\x0e\x32\x17.hgitaly.ConfigItemType\x12\x0f\n\x07section\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\"H\n\x15GetConfigItemResponse\x12\x13\n\tas_string\x18\x01 \x01(\tH\x00\x12\x11\n\x07\x61s_bool\x18\x02 \x01(\x08H\x00\x42\x07\n\x05value\">\n\x0eRecoverRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\"%\n\x0fRecoverResponse\x12\x12\n\nnot_needed\x18\x01 \x01(\x08\"?\n\x0fOptimizeRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\"\x12\n\x10OptimizeResponse\"\x88\x01\n\x12ResetCachesRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\x31\n\x06\x63\x61\x63hes\x18\x02 \x03(\x0e\x32!.hgitaly.ResetCachesRequest.Cache\"\x11\n\x05\x43\x61\x63he\x12\x08\n\x04TAGS\x10\x00\"\x15\n\x13ResetCachesResponse\"b\n\rMercurialPeer\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0f\n\x07ssh_key\x18\x05 \x01(\t\x12\x17\n\x0fssh_known_hosts\x18\x06 \x01(\t\x12\x1a\n\x12ssh_remote_command\x18\x07 \x01(\x0c\"\xa7\x01\n\x0bPushRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12+\n\x0bremote_peer\x18\x02 \x01(\x0b\x32\x16.hgitaly.MercurialPeer\x12%\n\x1donly_gitlab_branches_matching\x18\x03 \x03(\x0c\x12\x16\n\x0einclude_drafts\x18\x04 \x01(\x08\"&\n\x0cPushResponse\x12\x16\n\x0enew_changesets\x18\x01 \x01(\x08\"\xb8\x01\n\x0bPullRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12+\n\x0bremote_peer\x18\x02 \x01(\x0b\x32\x16.hgitaly.MercurialPeer\x12\x1a\n\x04user\x18\x04 \x01(\x0b\x32\x0c.gitaly.User\x12\x1b\n\x13mercurial_revisions\x18\x05 \x03(\x0cJ\x04\x08\x03\x10\x04R\x0fgitlab_branches\"&\n\x0cPullResponse\x12\x16\n\x0enew_changesets\x18\x01 \x01(\x08\"g\n\rHgCallRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\x1a\n\x04user\x18\x02 \x01(\x0b\x32\x0c.gitaly.User\x12\x0c\n\x04\x61rgs\x18\x03 \x03(\x0c\"3\n\x0eHgCallResponse\x12\x11\n\texit_code\x18\x01 \x01(\x05\x12\x0e\n\x06stdout\x18\x02 \x03(\x0c*&\n\x0e\x43onfigItemType\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x42OOL\x10\x01\x32\xa5\x06\n\x1aMercurialRepositoryService\x12M\n\nInitConfig\x12\x1a.hgitaly.InitConfigRequest\x1a\x1b.hgitaly.InitConfigResponse\"\x06\xfa\x97(\x02\x08\x01\x12V\n\rGetConfigItem\x12\x1d.hgitaly.GetConfigItemRequest\x1a\x1e.hgitaly.GetConfigItemResponse\"\x06\xfa\x97(\x02\x08\x02\x12\x44\n\x07Recover\x12\x17.hgitaly.RecoverRequest\x1a\x18.hgitaly.RecoverResponse\"\x06\xfa\x97(\x02\x08\x02\x12G\n\x08Optimize\x12\x18.hgitaly.OptimizeRequest\x1a\x19.hgitaly.OptimizeResponse\"\x06\xfa\x97(\x02\x08\x01\x12P\n\x0bResetCaches\x12\x1b.hgitaly.ResetCachesRequest\x1a\x1c.hgitaly.ResetCachesResponse\"\x06\xfa\x97(\x02\x08\x01\x12\x43\n\x06HgCall\x12\x16.hgitaly.HgCallRequest\x1a\x17.hgitaly.HgCallResponse\"\x06\xfa\x97(\x02\x08\x01\x30\x01\x12_\n\x10GetManagedConfig\x12 .hgitaly.GetManagedConfigRequest\x1a!.hgitaly.GetManagedConfigResponse\"\x06\xfa\x97(\x02\x08\x02\x12_\n\x10SetManagedConfig\x12 .hgitaly.SetManagedConfigRequest\x1a!.hgitaly.SetManagedConfigResponse\"\x06\xfa\x97(\x02\x08\x01\x12;\n\x04Push\x12\x14.hgitaly.PushRequest\x1a\x15.hgitaly.PushResponse\"\x06\xfa\x97(\x02\x08\x01\x12;\n\x04Pull\x12\x14.hgitaly.PullRequest\x1a\x15.hgitaly.PullResponse\"\x06\xfa\x97(\x02\x08\x01\x62\x06proto3')
18
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1amercurial-repository.proto\x12\x07hgitaly\x1a\nlint.proto\x1a\x0cshared.proto\"Y\n\x11InitConfigRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\x16\n\x0enamespace_path\x18\x02 \x01(\t\"\x14\n\x12InitConfigResponse\"\x95\x02\n\x15HeptapodConfigSection\x12!\n\x14\x61llow_multiple_heads\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x1c\n\x0f\x61llow_bookmarks\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x45\n\x0c\x61uto_publish\x18\x04 \x01(\x0e\x32*.hgitaly.HeptapodConfigSection.AutoPublishH\x02\x88\x01\x01\"6\n\x0b\x41utoPublish\x12\x11\n\rWITHOUT_TOPIC\x10\x00\x12\x0b\n\x07NOTHING\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x17\n\x15_allow_multiple_headsB\x12\n\x10_allow_bookmarksB\x0f\n\r_auto_publish\"V\n\x17GetManagedConfigRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\r\n\x05local\x18\x02 \x01(\x08\"]\n\x18GetManagedConfigResponse\x12\x0f\n\x07inherit\x18\x01 \x01(\x08\x12\x30\n\x08heptapod\x18\x02 \x01(\x0b\x32\x1e.hgitaly.HeptapodConfigSection\"\xc2\x01\n\x17SetManagedConfigRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\x14\n\x07inherit\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x30\n\x08heptapod\x18\x03 \x01(\x0b\x32\x1e.hgitaly.HeptapodConfigSection\x12\x14\n\x0cremove_items\x18\x04 \x03(\t\x12\x0f\n\x07\x62y_line\x18\x05 \x01(\tB\n\n\x08_inherit\"\x1a\n\x18SetManagedConfigResponse\"\x8d\x01\n\x14GetConfigItemRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12(\n\x07\x61s_type\x18\x02 \x01(\x0e\x32\x17.hgitaly.ConfigItemType\x12\x0f\n\x07section\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\"H\n\x15GetConfigItemResponse\x12\x13\n\tas_string\x18\x01 \x01(\tH\x00\x12\x11\n\x07\x61s_bool\x18\x02 \x01(\x08H\x00\x42\x07\n\x05value\">\n\x0eRecoverRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\"%\n\x0fRecoverResponse\x12\x12\n\nnot_needed\x18\x01 \x01(\x08\"?\n\x0fOptimizeRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\"\x12\n\x10OptimizeResponse\"\x88\x01\n\x12ResetCachesRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\x31\n\x06\x63\x61\x63hes\x18\x02 \x03(\x0e\x32!.hgitaly.ResetCachesRequest.Cache\"\x11\n\x05\x43\x61\x63he\x12\x08\n\x04TAGS\x10\x00\"\x15\n\x13ResetCachesResponse\"b\n\rMercurialPeer\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0f\n\x07ssh_key\x18\x05 \x01(\t\x12\x17\n\x0fssh_known_hosts\x18\x06 \x01(\t\x12\x1a\n\x12ssh_remote_command\x18\x07 \x01(\x0c\"\xa7\x01\n\x0bPushRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12+\n\x0bremote_peer\x18\x02 \x01(\x0b\x32\x16.hgitaly.MercurialPeer\x12%\n\x1donly_gitlab_branches_matching\x18\x03 \x03(\x0c\x12\x16\n\x0einclude_drafts\x18\x04 \x01(\x08\"&\n\x0cPushResponse\x12\x16\n\x0enew_changesets\x18\x01 \x01(\x08\"\xb8\x01\n\x0bPullRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12+\n\x0bremote_peer\x18\x02 \x01(\x0b\x32\x16.hgitaly.MercurialPeer\x12\x1a\n\x04user\x18\x04 \x01(\x0b\x32\x0c.gitaly.User\x12\x1b\n\x13mercurial_revisions\x18\x05 \x03(\x0cJ\x04\x08\x03\x10\x04R\x0fgitlab_branches\"&\n\x0cPullResponse\x12\x16\n\x0enew_changesets\x18\x01 \x01(\x08\"g\n\rHgCallRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\x1a\n\x04user\x18\x02 \x01(\x0b\x32\x0c.gitaly.User\x12\x0c\n\x04\x61rgs\x18\x03 \x03(\x0c\"3\n\x0eHgCallResponse\x12\x11\n\texit_code\x18\x01 \x01(\x05\x12\x0e\n\x06stdout\x18\x02 \x03(\x0c\"\xc2\x01\n\x13HousekeepingRequest\x12,\n\nrepository\x18\x01 \x01(\x0b\x32\x12.gitaly.RepositoryB\x04\x98\xc6,\x01\x12\x0c\n\x04\x66\x61il\x18\x02 \x01(\x08\x12\x0f\n\x07recover\x18\x03 \x01(\x08\x12\x31\n)working_directories_age_threshold_seconds\x18\x04 \x01(\r\x12+\n#working_directories_remove_unlisted\x18\x05 \x01(\x08\"\xa3\x01\n\x14HousekeepingResponse\x12\x13\n\x0brecover_run\x18\x01 \x01(\x08\x12)\n!recovered_interrupted_transaction\x18\x02 \x01(\x08\x12\x1e\n\x16working_directories_gc\x18\x03 \x01(\x08\x12+\n#working_directories_remove_unlisted\x18\x04 \x01(\x08*&\n\x0e\x43onfigItemType\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x42OOL\x10\x01\x32\xfa\x06\n\x1aMercurialRepositoryService\x12M\n\nInitConfig\x12\x1a.hgitaly.InitConfigRequest\x1a\x1b.hgitaly.InitConfigResponse\"\x06\xfa\x97(\x02\x08\x01\x12V\n\rGetConfigItem\x12\x1d.hgitaly.GetConfigItemRequest\x1a\x1e.hgitaly.GetConfigItemResponse\"\x06\xfa\x97(\x02\x08\x02\x12\x44\n\x07Recover\x12\x17.hgitaly.RecoverRequest\x1a\x18.hgitaly.RecoverResponse\"\x06\xfa\x97(\x02\x08\x02\x12G\n\x08Optimize\x12\x18.hgitaly.OptimizeRequest\x1a\x19.hgitaly.OptimizeResponse\"\x06\xfa\x97(\x02\x08\x01\x12P\n\x0bResetCaches\x12\x1b.hgitaly.ResetCachesRequest\x1a\x1c.hgitaly.ResetCachesResponse\"\x06\xfa\x97(\x02\x08\x01\x12\x43\n\x06HgCall\x12\x16.hgitaly.HgCallRequest\x1a\x17.hgitaly.HgCallResponse\"\x06\xfa\x97(\x02\x08\x01\x30\x01\x12_\n\x10GetManagedConfig\x12 .hgitaly.GetManagedConfigRequest\x1a!.hgitaly.GetManagedConfigResponse\"\x06\xfa\x97(\x02\x08\x02\x12_\n\x10SetManagedConfig\x12 .hgitaly.SetManagedConfigRequest\x1a!.hgitaly.SetManagedConfigResponse\"\x06\xfa\x97(\x02\x08\x01\x12;\n\x04Push\x12\x14.hgitaly.PushRequest\x1a\x15.hgitaly.PushResponse\"\x06\xfa\x97(\x02\x08\x01\x12;\n\x04Pull\x12\x14.hgitaly.PullRequest\x1a\x15.hgitaly.PullResponse\"\x06\xfa\x97(\x02\x08\x01\x12S\n\x0cHousekeeping\x12\x1c.hgitaly.HousekeepingRequest\x1a\x1d.hgitaly.HousekeepingResponse\"\x06\xfa\x97(\x02\x08\x01\x62\x06proto3')
19
19
 
20
20
  _globals = globals()
21
21
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -43,6 +43,8 @@ if _descriptor._USE_C_DESCRIPTORS == False:
43
43
  _PULLREQUEST.fields_by_name['repository']._serialized_options = b'\230\306,\001'
44
44
  _HGCALLREQUEST.fields_by_name['repository']._options = None
45
45
  _HGCALLREQUEST.fields_by_name['repository']._serialized_options = b'\230\306,\001'
46
+ _HOUSEKEEPINGREQUEST.fields_by_name['repository']._options = None
47
+ _HOUSEKEEPINGREQUEST.fields_by_name['repository']._serialized_options = b'\230\306,\001'
46
48
  _MERCURIALREPOSITORYSERVICE.methods_by_name['InitConfig']._options = None
47
49
  _MERCURIALREPOSITORYSERVICE.methods_by_name['InitConfig']._serialized_options = b'\372\227(\002\010\001'
48
50
  _MERCURIALREPOSITORYSERVICE.methods_by_name['GetConfigItem']._options = None
@@ -63,8 +65,10 @@ if _descriptor._USE_C_DESCRIPTORS == False:
63
65
  _MERCURIALREPOSITORYSERVICE.methods_by_name['Push']._serialized_options = b'\372\227(\002\010\001'
64
66
  _MERCURIALREPOSITORYSERVICE.methods_by_name['Pull']._options = None
65
67
  _MERCURIALREPOSITORYSERVICE.methods_by_name['Pull']._serialized_options = b'\372\227(\002\010\001'
66
- _globals['_CONFIGITEMTYPE']._serialized_start=2129
67
- _globals['_CONFIGITEMTYPE']._serialized_end=2167
68
+ _MERCURIALREPOSITORYSERVICE.methods_by_name['Housekeeping']._options = None
69
+ _MERCURIALREPOSITORYSERVICE.methods_by_name['Housekeeping']._serialized_options = b'\372\227(\002\010\001'
70
+ _globals['_CONFIGITEMTYPE']._serialized_start=2492
71
+ _globals['_CONFIGITEMTYPE']._serialized_end=2530
68
72
  _globals['_INITCONFIGREQUEST']._serialized_start=65
69
73
  _globals['_INITCONFIGREQUEST']._serialized_end=154
70
74
  _globals['_INITCONFIGRESPONSE']._serialized_start=156
@@ -113,6 +117,10 @@ if _descriptor._USE_C_DESCRIPTORS == False:
113
117
  _globals['_HGCALLREQUEST']._serialized_end=2074
114
118
  _globals['_HGCALLRESPONSE']._serialized_start=2076
115
119
  _globals['_HGCALLRESPONSE']._serialized_end=2127
116
- _globals['_MERCURIALREPOSITORYSERVICE']._serialized_start=2170
117
- _globals['_MERCURIALREPOSITORYSERVICE']._serialized_end=2975
120
+ _globals['_HOUSEKEEPINGREQUEST']._serialized_start=2130
121
+ _globals['_HOUSEKEEPINGREQUEST']._serialized_end=2324
122
+ _globals['_HOUSEKEEPINGRESPONSE']._serialized_start=2327
123
+ _globals['_HOUSEKEEPINGRESPONSE']._serialized_end=2490
124
+ _globals['_MERCURIALREPOSITORYSERVICE']._serialized_start=2533
125
+ _globals['_MERCURIALREPOSITORYSERVICE']._serialized_end=3423
118
126
  # @@protoc_insertion_point(module_scope)
@@ -64,6 +64,11 @@ class MercurialRepositoryServiceStub(object):
64
64
  request_serializer=mercurial__repository__pb2.PullRequest.SerializeToString,
65
65
  response_deserializer=mercurial__repository__pb2.PullResponse.FromString,
66
66
  )
67
+ self.Housekeeping = channel.unary_unary(
68
+ '/hgitaly.MercurialRepositoryService/Housekeeping',
69
+ request_serializer=mercurial__repository__pb2.HousekeepingRequest.SerializeToString,
70
+ response_deserializer=mercurial__repository__pb2.HousekeepingResponse.FromString,
71
+ )
67
72
 
68
73
 
69
74
  class MercurialRepositoryServiceServicer(object):
@@ -175,6 +180,13 @@ class MercurialRepositoryServiceServicer(object):
175
180
  context.set_details('Method not implemented!')
176
181
  raise NotImplementedError('Method not implemented!')
177
182
 
183
+ def Housekeeping(self, request, context):
184
+ """/ General tidying and optimization for this repository
185
+ """
186
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
187
+ context.set_details('Method not implemented!')
188
+ raise NotImplementedError('Method not implemented!')
189
+
178
190
 
179
191
  def add_MercurialRepositoryServiceServicer_to_server(servicer, server):
180
192
  rpc_method_handlers = {
@@ -228,6 +240,11 @@ def add_MercurialRepositoryServiceServicer_to_server(servicer, server):
228
240
  request_deserializer=mercurial__repository__pb2.PullRequest.FromString,
229
241
  response_serializer=mercurial__repository__pb2.PullResponse.SerializeToString,
230
242
  ),
243
+ 'Housekeeping': grpc.unary_unary_rpc_method_handler(
244
+ servicer.Housekeeping,
245
+ request_deserializer=mercurial__repository__pb2.HousekeepingRequest.FromString,
246
+ response_serializer=mercurial__repository__pb2.HousekeepingResponse.SerializeToString,
247
+ ),
231
248
  }
232
249
  generic_handler = grpc.method_handlers_generic_handler(
233
250
  'hgitaly.MercurialRepositoryService', rpc_method_handlers)
@@ -407,3 +424,20 @@ class MercurialRepositoryService(object):
407
424
  mercurial__repository__pb2.PullResponse.FromString,
408
425
  options, channel_credentials,
409
426
  insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
427
+
428
+ @staticmethod
429
+ def Housekeeping(request,
430
+ target,
431
+ options=(),
432
+ channel_credentials=None,
433
+ call_credentials=None,
434
+ insecure=False,
435
+ compression=None,
436
+ wait_for_ready=None,
437
+ timeout=None,
438
+ metadata=None):
439
+ return grpc.experimental.unary_unary(request, target, '/hgitaly.MercurialRepositoryService/Housekeeping',
440
+ mercurial__repository__pb2.HousekeepingRequest.SerializeToString,
441
+ mercurial__repository__pb2.HousekeepingResponse.FromString,
442
+ options, channel_credentials,
443
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@@ -5,6 +5,7 @@
5
5
  #
6
6
  # SPDX-License-Identifier: GPL-2.0-or-later
7
7
  from copy import deepcopy
8
+ import shutil
8
9
 
9
10
  import pytest
10
11
  from heptapod.testhelpers import (
@@ -29,6 +30,7 @@ from ..gitlab_ref import (
29
30
  keep_around_ref_path,
30
31
  parse_keep_around_ref_path,
31
32
  )
33
+ from .. import gitlab_ref as glref_mod # for monkey-patching
32
34
 
33
35
 
34
36
  @pytest.fixture
@@ -97,6 +99,25 @@ def test_gitlab_special_ref_target_ensure(mirror):
97
99
  assert gitlab_special_ref_target(wrapper.repo, ref_path) == ctx
98
100
 
99
101
 
102
+ def should_not_be_called(*a, **kw): # pragma: no cover
103
+ raise AssertionError("should not have been called")
104
+
105
+
106
+ def test_special_ref_not_ensure(mirror, monkeypatch):
107
+ wrapper = mirror.hg_repo_wrapper
108
+ git_repo = mirror.git_repo
109
+
110
+ shutil.rmtree(git_repo.path)
111
+ monkeypatch.setattr(glref_mod, 'ensure_gitlab_special_refs',
112
+ should_not_be_called)
113
+ monkeypatch.setattr(glref_mod, 'ensure_gitlab_keep_arounds',
114
+ should_not_be_called)
115
+
116
+ ref_path = b'refs/environments/654'
117
+ assert gitlab_special_ref_target(wrapper.repo, ref_path) is None
118
+ assert tuple(iter_keep_arounds_as_refs(wrapper.repo)) == ()
119
+
120
+
100
121
  def test_write_special_ref(repo_wrapper):
101
122
  repo, wrapper = repo_wrapper
102
123
 
@@ -37,6 +37,7 @@ from ..workdir import (
37
37
  reserve_workdir,
38
38
  working_directory,
39
39
  workdirs_gc,
40
+ remove_unlisted_workdirs,
40
41
  )
41
42
 
42
43
  parametrize = pytest.mark.parametrize
@@ -58,6 +59,11 @@ def wd_fixture(tmpdir):
58
59
  yield workdirs_root, wrapper
59
60
 
60
61
 
62
+ def assert_roster_length(repo, n):
63
+ with repo.vfs(ROSTER_FILE_NAME, b'rb') as rosterf:
64
+ assert len(rosterf.readlines()) == n
65
+
66
+
61
67
  def test_working_directory_basic(wd_fixture):
62
68
  wds_root, wrapper = wd_fixture
63
69
  src_repo = wrapper.repo
@@ -106,11 +112,17 @@ def test_working_directory_basic(wd_fixture):
106
112
  for (wd_id, _, _, branch), _l in roster_iter(rosterf)}
107
113
  assert wds == {wd_id: b'default', wd2_id: b'default'}
108
114
 
115
+ # And the roster file has the expected size
116
+ assert_roster_length(src_repo, 2)
117
+
109
118
  # Both can be reused
110
119
  with working_directory(wds_root, src_repo, changeset=ctx) as wd1:
111
120
  with working_directory(wds_root, src_repo, changeset=ctx) as wd2:
112
121
  assert set((wd1.path, wd2.path)) == {wd_path, wd2_path}
113
122
 
123
+ # with no undue growth of the roster file (hgitaly#226)
124
+ assert_roster_length(src_repo, 2)
125
+
114
126
 
115
127
  def test_working_directory_subrepos(wd_fixture):
116
128
  wds_root, wrapper = wd_fixture
@@ -354,6 +366,32 @@ def test_workdirs_gc_stale_default(purge_fixture):
354
366
  assert lines[0][3] == b'mydefault'
355
367
 
356
368
 
369
+ def test_workdirs_gc_duplicate_lines(purge_fixture):
370
+ wds_root, wrapper = purge_fixture
371
+ repo = wrapper.repo
372
+
373
+ # no error if there is no working directory
374
+ workdirs_gc(wds_root, repo, 1)
375
+
376
+ wd_path(wds_root, 0).mkdir(parents=True)
377
+ wd_path(wds_root, 1).mkdir()
378
+ with locked_roster(repo) as (inf, outf):
379
+ # See hgitaly#226
380
+ outf.writelines((b"0 - 10000 mydefault\n",
381
+ b"1 - 11000 mydefault\n",
382
+ b"0 - 12000 mydefault\n",
383
+ b"1 - 13000 mydefault\n",
384
+ ))
385
+
386
+ workdirs_gc(wds_root, repo, max_age_seconds=100, now=20000)
387
+
388
+ # The active work dir for default branch is kept, considered to be enough
389
+ with locked_roster(repo) as (inf, outf):
390
+ lines = [parsed for parsed, _line in roster_iter(inf)]
391
+ assert len(lines) == 1
392
+ assert lines[0][3] == b'mydefault'
393
+
394
+
357
395
  def test_workdirs_gc_no_default(purge_fixture):
358
396
  wds_root, wrapper = purge_fixture
359
397
  repo = wrapper.repo
@@ -375,7 +413,7 @@ def test_workdirs_gc_no_default(purge_fixture):
375
413
  assert len(inf.read()) == 0
376
414
 
377
415
 
378
- def test_workdirs_gc_rmerror(purge_fixture):
416
+ def test_workdirs_gc_missing_workdir(purge_fixture):
379
417
  wds_root, wrapper = purge_fixture
380
418
  repo = wrapper.repo
381
419
 
@@ -389,11 +427,70 @@ def test_workdirs_gc_rmerror(purge_fixture):
389
427
 
390
428
  workdirs_gc(wds_root, repo, max_age_seconds=100, now=20000)
391
429
 
430
+ with locked_roster(repo) as (inf, outf):
431
+ wds = {parsed[0]: parsed[3] for parsed, _line in roster_iter(inf)}
432
+ assert wds == {1: b'mydefault'}
433
+
434
+
435
+ def test_workdirs_gc_rmerror(purge_fixture, monkeypatch):
436
+ wds_root, wrapper = purge_fixture
437
+ repo = wrapper.repo
438
+
439
+ wd_path(wds_root, 0).mkdir(parents=True)
440
+ wd_path(wds_root, 1).mkdir()
441
+ with locked_roster(repo) as (inf, outf):
442
+ outf.writelines((b"0 - 10000 other\n",
443
+ b"1 - 15000 mydefault\n",
444
+ ))
445
+
446
+ def raiser(path):
447
+ raise RuntimeError(f"Cannot remove {path}")
448
+
449
+ monkeypatch.setattr(shutil, 'rmtree', raiser)
450
+ workdirs_gc(wds_root, repo, max_age_seconds=100, now=20000)
451
+
392
452
  with locked_roster(repo) as (inf, outf):
393
453
  wds = {parsed[0]: parsed[3] for parsed, _line in roster_iter(inf)}
394
454
  assert wds == {0: b'other', 1: b'mydefault'}
395
455
 
396
456
 
457
+ def test_remove_unlisted_workdirs(purge_fixture, monkeypatch):
458
+ wds_root, wrapper = purge_fixture
459
+ repo = wrapper.repo
460
+
461
+ # does not fail if working dirs root does not even exist
462
+ remove_unlisted_workdirs(wds_root, repo)
463
+
464
+ wd_path(wds_root, 0).mkdir(parents=True)
465
+ wd_path(wds_root, 1).mkdir()
466
+ wd_path(wds_root, 2).mkdir()
467
+ (wds_root / 'README').write_binary(b"innocent bystander")
468
+ (wds_root / '4').write_binary(b"innocent asking for trouble")
469
+
470
+ with locked_roster(repo) as (inf, outf):
471
+ outf.writelines((b"0 - 10000 mydefault\n",
472
+ ))
473
+ rmtree = shutil.rmtree
474
+
475
+ def raiser(path):
476
+ if path.name == '1':
477
+ rmtree(path)
478
+ else:
479
+ raise RuntimeError(f"Cannot remove {path}")
480
+
481
+ monkeypatch.setattr(shutil, 'rmtree', raiser)
482
+ remove_unlisted_workdirs(wds_root, repo)
483
+
484
+ with locked_roster(repo) as (inf, outf):
485
+ wds = {parsed[0]: parsed[3] for parsed, _line in roster_iter(inf)}
486
+ assert wds == {0: b'mydefault'}
487
+
488
+ assert not wd_path(wds_root, 1).exists()
489
+ assert wd_path(wds_root, 2).exists() # our raiser did its job
490
+ assert (wds_root / 'README').exists()
491
+ assert (wds_root / '4').exists()
492
+
493
+
397
494
  @parametrize('inner_success', ('ok', 'rm-error'))
398
495
  def test_remove_all_workdirs(purge_fixture, monkeypatch, inner_success):
399
496
  wds_root, wrapper = purge_fixture