gitlabber 2.1.0__tar.gz → 2.1.1__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 (132) hide show
  1. {gitlabber-2.1.0/gitlabber.egg-info → gitlabber-2.1.1}/PKG-INFO +4 -2
  2. {gitlabber-2.1.0 → gitlabber-2.1.1}/README.rst +2 -1
  3. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/__init__.py +1 -1
  4. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/cli.py +11 -1
  5. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/config.py +1 -0
  6. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/git.py +21 -7
  7. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/gitlab_tree.py +5 -1
  8. {gitlabber-2.1.0 → gitlabber-2.1.1/gitlabber.egg-info}/PKG-INFO +4 -2
  9. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber.egg-info/requires.txt +1 -0
  10. {gitlabber-2.1.0 → gitlabber-2.1.1}/pyproject.toml +2 -1
  11. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/conftest.cpython-313-pytest-9.0.1.pyc +0 -0
  12. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/gitlab_test_utils.cpython-313.pyc +0 -0
  13. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/io_test_util.cpython-313.pyc +0 -0
  14. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_cli.cpython-313-pytest-9.0.1.pyc +0 -0
  15. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_cli_token_storage.cpython-313-pytest-9.0.1.pyc +0 -0
  16. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_config.cpython-313-pytest-9.0.1.pyc +0 -0
  17. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_e2e.cpython-313-pytest-9.0.1.pyc +0 -0
  18. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_exceptions.cpython-313-pytest-9.0.1.pyc +0 -0
  19. gitlabber-2.1.1/tests/__pycache__/test_git.cpython-313-pytest-9.0.1.pyc +0 -0
  20. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_gitlab_tree.cpython-313-pytest-9.0.1.pyc +0 -0
  21. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_helpers.cpython-313-pytest-9.0.1.pyc +0 -0
  22. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_integration.cpython-313-pytest-9.0.1.pyc +0 -0
  23. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_main.cpython-313-pytest-9.0.1.pyc +0 -0
  24. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_performance.cpython-313-pytest-9.0.1.pyc +0 -0
  25. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_rate_limiter.cpython-313-pytest-9.0.1.pyc +0 -0
  26. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_token_storage.cpython-313-pytest-9.0.1.pyc +0 -0
  27. gitlabber-2.1.1/tests/test_git.py +499 -0
  28. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_helpers.py +9 -1
  29. gitlabber-2.1.0/tests/__pycache__/test_git.cpython-313-pytest-9.0.1.pyc +0 -0
  30. gitlabber-2.1.0/tests/test_git.py +0 -219
  31. {gitlabber-2.1.0 → gitlabber-2.1.1}/LICENSE.txt +0 -0
  32. {gitlabber-2.1.0 → gitlabber-2.1.1}/MANIFEST.in +0 -0
  33. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/__main__.py +0 -0
  34. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/archive.py +0 -0
  35. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/auth.py +0 -0
  36. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/exceptions.py +0 -0
  37. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/format.py +0 -0
  38. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/method.py +0 -0
  39. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/naming.py +0 -0
  40. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/progress.py +0 -0
  41. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/rate_limiter.py +0 -0
  42. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/token_storage.py +0 -0
  43. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/tree_builder.py +0 -0
  44. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber/url_builder.py +0 -0
  45. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber.egg-info/SOURCES.txt +0 -0
  46. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber.egg-info/dependency_links.txt +0 -0
  47. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber.egg-info/entry_points.txt +0 -0
  48. {gitlabber-2.1.0 → gitlabber-2.1.1}/gitlabber.egg-info/top_level.txt +0 -0
  49. {gitlabber-2.1.0 → gitlabber-2.1.1}/setup.cfg +0 -0
  50. {gitlabber-2.1.0 → gitlabber-2.1.1}/setup.py +0 -0
  51. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__init__.py +0 -0
  52. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  53. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  54. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  55. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/conftest.cpython-311-pytest-9.0.1.pyc +0 -0
  56. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/gitlab_test_utils.cpython-311.pyc +0 -0
  57. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/gitlab_test_utils.cpython-312.pyc +0 -0
  58. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/io_test_util.cpython-311.pyc +0 -0
  59. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/io_test_util.cpython-312.pyc +0 -0
  60. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_archive.cpython-311-pytest-8.2.2.pyc +0 -0
  61. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_archive.cpython-312-pytest-8.3.3.pyc +0 -0
  62. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_archive.cpython-312-pytest-8.3.4.pyc +0 -0
  63. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_archive.cpython-312-pytest-8.3.5.pyc +0 -0
  64. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_archive.cpython-313-pytest-9.0.1.pyc +0 -0
  65. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_auth.cpython-313-pytest-9.0.1.pyc +0 -0
  66. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_cli.cpython-311-pytest-8.2.2.pyc +0 -0
  67. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_cli.cpython-311-pytest-9.0.1.pyc +0 -0
  68. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_cli.cpython-312-pytest-8.3.3.pyc +0 -0
  69. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_cli.cpython-312-pytest-8.3.4.pyc +0 -0
  70. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_cli.cpython-312-pytest-8.3.5.pyc +0 -0
  71. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_e2e.cpython-311-pytest-8.2.2.pyc +0 -0
  72. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_e2e.cpython-312-pytest-8.3.3.pyc +0 -0
  73. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_e2e.cpython-312-pytest-8.3.4.pyc +0 -0
  74. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_e2e.cpython-312-pytest-8.3.5.pyc +0 -0
  75. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_e2e.cpython-313.pyc +0 -0
  76. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_format.cpython-311-pytest-8.2.2.pyc +0 -0
  77. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_format.cpython-312-pytest-8.3.3.pyc +0 -0
  78. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_format.cpython-312-pytest-8.3.4.pyc +0 -0
  79. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_format.cpython-312-pytest-8.3.5.pyc +0 -0
  80. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_format.cpython-313-pytest-9.0.1.pyc +0 -0
  81. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_git.cpython-311-pytest-8.2.2.pyc +0 -0
  82. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_git.cpython-312-pytest-8.3.3.pyc +0 -0
  83. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_git.cpython-312-pytest-8.3.4.pyc +0 -0
  84. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_git.cpython-312-pytest-8.3.5.pyc +0 -0
  85. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_gitlab_tree.cpython-311-pytest-8.2.2.pyc +0 -0
  86. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_gitlab_tree.cpython-312-pytest-8.3.3.pyc +0 -0
  87. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_gitlab_tree.cpython-312-pytest-8.3.4.pyc +0 -0
  88. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_gitlab_tree.cpython-312-pytest-8.3.5.pyc +0 -0
  89. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_helpers.cpython-311-pytest-9.0.1.pyc +0 -0
  90. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_integration.cpython-311-pytest-8.2.2.pyc +0 -0
  91. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_integration.cpython-311-pytest-9.0.1.pyc +0 -0
  92. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_integration.cpython-312-pytest-8.3.3.pyc +0 -0
  93. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_integration.cpython-312-pytest-8.3.4.pyc +0 -0
  94. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_integration.cpython-312-pytest-8.3.5.pyc +0 -0
  95. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_method.cpython-311-pytest-8.2.2.pyc +0 -0
  96. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_method.cpython-312-pytest-8.3.3.pyc +0 -0
  97. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_method.cpython-312-pytest-8.3.4.pyc +0 -0
  98. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_method.cpython-312-pytest-8.3.5.pyc +0 -0
  99. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_method.cpython-313-pytest-9.0.1.pyc +0 -0
  100. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_naming.cpython-311-pytest-8.2.2.pyc +0 -0
  101. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_naming.cpython-312-pytest-8.3.3.pyc +0 -0
  102. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_naming.cpython-312-pytest-8.3.4.pyc +0 -0
  103. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_naming.cpython-312-pytest-8.3.5.pyc +0 -0
  104. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_naming.cpython-313-pytest-9.0.1.pyc +0 -0
  105. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_performance.cpython-313.pyc +0 -0
  106. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_progress.cpython-313-pytest-9.0.1.pyc +0 -0
  107. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/__pycache__/test_url_builder.cpython-313-pytest-9.0.1.pyc +0 -0
  108. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/conftest.py +0 -0
  109. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/gitlab_test_utils.py +0 -0
  110. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/io_test_util.py +0 -0
  111. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test-input.yaml +0 -0
  112. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test-output.json +0 -0
  113. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test-output.tree +0 -0
  114. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test-output.yaml +0 -0
  115. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_archive.py +0 -0
  116. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_auth.py +0 -0
  117. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_cli.py +0 -0
  118. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_cli_token_storage.py +0 -0
  119. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_config.py +0 -0
  120. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_e2e.py +0 -0
  121. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_exceptions.py +0 -0
  122. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_format.py +0 -0
  123. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_gitlab_tree.py +0 -0
  124. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_integration.py +0 -0
  125. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_main.py +0 -0
  126. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_method.py +0 -0
  127. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_naming.py +0 -0
  128. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_performance.py +0 -0
  129. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_progress.py +0 -0
  130. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_rate_limiter.py +0 -0
  131. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_token_storage.py +0 -0
  132. {gitlabber-2.1.0 → gitlabber-2.1.1}/tests/test_url_builder.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitlabber
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: A Gitlab clone/pull utility for backing up or cloning Gitlab groups
5
5
  Author-email: Erez Mazor <erezmazor@gmail.com>
6
6
  License: MIT
@@ -26,6 +26,7 @@ Requires-Dist: pyyaml
26
26
  Requires-Dist: pydantic>=2.7
27
27
  Requires-Dist: pydantic-settings>=2.7
28
28
  Requires-Dist: typer>=0.12
29
+ Requires-Dist: urllib3==2.5.0
29
30
  Requires-Dist: rich
30
31
  Requires-Dist: GitPython
31
32
  Requires-Dist: python-gitlab
@@ -229,7 +230,8 @@ Usage
229
230
  number of concurrent git operations (default: 1)
230
231
  --api-concurrency N number of concurrent API calls for tree building (default: 5)
231
232
  -r, --recursive clone/pull git submodules recursively
232
- -F, --use-fetch clone/fetch git repository (mirrored repositories)
233
+ -F, --use-fetch use git fetch instead of pull for updates (normal repositories with working tree)
234
+ --mirror create bare mirror repositories (for backups, automatically uses fetch)
233
235
  -s, --include-shared include shared projects in the results
234
236
  -g term, --group-search term
235
237
  only include groups matching the search term, filtering done at the API level (useful for large projects, see: https://docs.gitlab.com/ee/api/groups.html#search-for-group works with partial names of path or name)
@@ -189,7 +189,8 @@ Usage
189
189
  number of concurrent git operations (default: 1)
190
190
  --api-concurrency N number of concurrent API calls for tree building (default: 5)
191
191
  -r, --recursive clone/pull git submodules recursively
192
- -F, --use-fetch clone/fetch git repository (mirrored repositories)
192
+ -F, --use-fetch use git fetch instead of pull for updates (normal repositories with working tree)
193
+ --mirror create bare mirror repositories (for backups, automatically uses fetch)
193
194
  -s, --include-shared include shared projects in the results
194
195
  -g term, --group-search term
195
196
  only include groups matching the search term, filtering done at the API level (useful for large projects, see: https://docs.gitlab.com/ee/api/groups.html#search-for-group works with partial names of path or name)
@@ -5,4 +5,4 @@ while maintaining the directory structure. It supports filtering, progress
5
5
  tracking, and various configuration options.
6
6
  """
7
7
 
8
- __version__ = '2.1.0'
8
+ __version__ = '2.1.1'
@@ -211,6 +211,7 @@ def run_gitlabber(
211
211
  exclude: Optional[str],
212
212
  recursive: bool,
213
213
  use_fetch: bool,
214
+ mirror: bool,
214
215
  include_shared: bool,
215
216
  group_search: Optional[str],
216
217
  user_projects: bool,
@@ -244,6 +245,7 @@ def run_gitlabber(
244
245
  exclude: Comma-separated glob patterns to exclude
245
246
  recursive: Clone submodules recursively
246
247
  use_fetch: Use git fetch instead of pull
248
+ mirror: Create bare mirror repositories (implies use_fetch)
247
249
  include_shared: Include shared projects
248
250
  group_search: Search term for filtering groups at API level
249
251
  user_projects: Fetch only user personal projects
@@ -295,6 +297,7 @@ def run_gitlabber(
295
297
  "recursive": recursive,
296
298
  "include_shared": include_shared,
297
299
  "use_fetch": use_fetch,
300
+ "mirror": mirror,
298
301
  "hide_token": hide_token,
299
302
  "user_projects": user_projects,
300
303
  "group_search": group_search,
@@ -318,6 +321,7 @@ def run_gitlabber(
318
321
  disable_progress=verbose,
319
322
  include_shared=include_shared,
320
323
  use_fetch=use_fetch,
324
+ mirror=mirror,
321
325
  hide_token=hide_token,
322
326
  user_projects=user_projects,
323
327
  group_search=group_search,
@@ -458,7 +462,7 @@ def cli(
458
462
  False,
459
463
  "-F",
460
464
  "--use-fetch",
461
- help="Use git fetch instead of pull (mirrored repositories)",
465
+ help="Use git fetch instead of pull for updates (normal repositories with working tree)",
462
466
  ),
463
467
  exclude_shared: bool = typer.Option(
464
468
  False,
@@ -495,6 +499,11 @@ def cli(
495
499
  "--store-token",
496
500
  help="Store token securely in OS keyring (requires keyring package)",
497
501
  ),
502
+ mirror: bool = typer.Option(
503
+ False,
504
+ "--mirror",
505
+ help="Create bare mirror repositories (for backups, automatically uses fetch)",
506
+ ),
498
507
  ) -> None:
499
508
  """Main CLI command for gitlabber.
500
509
 
@@ -560,6 +569,7 @@ def cli(
560
569
  exclude=exclude,
561
570
  recursive=recursive,
562
571
  use_fetch=use_fetch,
572
+ mirror=mirror,
563
573
  include_shared=include_shared_value,
564
574
  group_search=group_search,
565
575
  user_projects=user_projects,
@@ -80,6 +80,7 @@ class GitlabberConfig(BaseModel):
80
80
  disable_progress: bool = False
81
81
  include_shared: bool = True
82
82
  use_fetch: bool = False
83
+ mirror: bool = False
83
84
  hide_token: bool = False
84
85
  user_projects: bool = False
85
86
  group_search: Optional[str] = None
@@ -26,6 +26,7 @@ class GitAction:
26
26
  use_fetch: bool = False
27
27
  hide_token: bool = False
28
28
  git_options: Optional[str] = None
29
+ mirror: bool = False # New parameter
29
30
 
30
31
 
31
32
  class GitRepository:
@@ -68,7 +69,7 @@ class GitRepository:
68
69
  multi_options: list[str] = []
69
70
  if action.recursive:
70
71
  multi_options.append('--recursive')
71
- if action.use_fetch:
72
+ if action.mirror: # New parameter
72
73
  multi_options.append('--mirror')
73
74
  if action.git_options:
74
75
  multi_options += action.git_options.split(',')
@@ -132,13 +133,15 @@ class GitRepository:
132
133
  GitlabberGitError: If pull operation fails
133
134
  """
134
135
  log.debug("updating existing project %s", action.path)
135
- operation = 'fetching' if action.use_fetch else 'pulling'
136
+ # Mirror implies use_fetch, so check both
137
+ should_fetch = action.use_fetch or action.mirror
138
+ operation = 'fetching' if should_fetch else 'pulling'
136
139
  progress_bar.show_progress_detailed(action.node.name, 'project', operation)
137
140
 
138
141
  try:
139
142
  if repo is None:
140
143
  repo = git.Repo(action.path)
141
- if not action.use_fetch:
144
+ if not should_fetch:
142
145
  repo.remotes.origin.pull()
143
146
  else:
144
147
  repo.remotes.origin.fetch()
@@ -226,6 +229,7 @@ class GitActionCollector:
226
229
  dest: str,
227
230
  recursive: bool = False,
228
231
  use_fetch: bool = False,
232
+ mirror: bool = False,
229
233
  hide_token: bool = False,
230
234
  git_options: Optional[str] = None
231
235
  ):
@@ -235,12 +239,14 @@ class GitActionCollector:
235
239
  dest: Destination directory for repositories
236
240
  recursive: Whether to clone recursively
237
241
  use_fetch: Whether to use git fetch instead of pull
242
+ mirror: Whether to create bare mirror repositories
238
243
  hide_token: Whether to hide token in URLs
239
244
  git_options: Additional git options as comma-separated string
240
245
  """
241
246
  self.dest = Path(dest)
242
247
  self.recursive = recursive
243
248
  self.use_fetch = use_fetch
249
+ self.mirror = mirror
244
250
  self.hide_token = hide_token
245
251
  self.git_options = git_options
246
252
 
@@ -272,9 +278,11 @@ class GitActionCollector:
272
278
  path_str = str(path)
273
279
 
274
280
  if child.is_leaf:
281
+ # Mirror implies use_fetch (mirrors should always use fetch)
282
+ effective_use_fetch = self.use_fetch or self.mirror
275
283
  actions.append(GitAction(
276
284
  child, path_str, self.recursive,
277
- self.use_fetch, self.hide_token, self.git_options
285
+ effective_use_fetch, self.hide_token, self.git_options, self.mirror
278
286
  ))
279
287
 
280
288
  if not child.is_leaf:
@@ -307,6 +315,7 @@ class GitSyncManager:
307
315
  dest: str,
308
316
  recursive: bool = False,
309
317
  use_fetch: bool = False,
318
+ mirror: bool = False,
310
319
  hide_token: bool = False,
311
320
  git_options: Optional[str] = None
312
321
  ) -> None:
@@ -317,6 +326,7 @@ class GitSyncManager:
317
326
  dest: Destination directory
318
327
  recursive: Whether to clone recursively
319
328
  use_fetch: Whether to use git fetch instead of pull
329
+ mirror: Whether to create bare mirror repositories
320
330
  hide_token: Whether to hide token in URLs
321
331
  git_options: Additional git options as comma-separated string
322
332
  """
@@ -324,7 +334,7 @@ class GitSyncManager:
324
334
  self.progress_bar.init_progress(len(root.leaves))
325
335
 
326
336
  collector = GitActionCollector(
327
- dest, recursive, use_fetch, hide_token, git_options
337
+ dest, recursive, use_fetch, mirror, hide_token, git_options
328
338
  )
329
339
  actions = collector.collect(root)
330
340
 
@@ -343,6 +353,7 @@ def sync_tree(
343
353
  disable_progress: bool = False,
344
354
  recursive: bool = False,
345
355
  use_fetch: bool = False,
356
+ mirror: bool = False,
346
357
  hide_token: bool = False,
347
358
  git_options: Optional[str] = None
348
359
  ) -> None:
@@ -356,11 +367,12 @@ def sync_tree(
356
367
  disable_progress: Whether to disable progress reporting
357
368
  recursive: Whether to clone recursively
358
369
  use_fetch: Whether to use git fetch instead of pull
370
+ mirror: Whether to create bare mirror repositories
359
371
  hide_token: Whether to hide token in URLs
360
372
  git_options: Additional git options as comma-separated string
361
373
  """
362
374
  manager = GitSyncManager(concurrency, disable_progress)
363
- manager.sync(root, dest, recursive, use_fetch, hide_token, git_options)
375
+ manager.sync(root, dest, recursive, use_fetch, mirror, hide_token, git_options)
364
376
 
365
377
 
366
378
  def get_git_actions(
@@ -368,6 +380,7 @@ def get_git_actions(
368
380
  dest: str,
369
381
  recursive: bool,
370
382
  use_fetch: bool,
383
+ mirror: bool,
371
384
  hide_token: bool,
372
385
  git_options: Optional[str] = None
373
386
  ) -> list[GitAction]:
@@ -378,13 +391,14 @@ def get_git_actions(
378
391
  dest: Destination directory
379
392
  recursive: Whether to clone recursively
380
393
  use_fetch: Whether to use git fetch instead of pull
394
+ mirror: Whether to create bare mirror repositories
381
395
  hide_token: Whether to hide token in URLs
382
396
  git_options: Additional git options as comma-separated string
383
397
 
384
398
  Returns:
385
399
  List of GitAction objects to execute
386
400
  """
387
- collector = GitActionCollector(dest, recursive, use_fetch, hide_token, git_options)
401
+ collector = GitActionCollector(dest, recursive, use_fetch, mirror, hide_token, git_options)
388
402
  return collector.collect(root)
389
403
 
390
404
 
@@ -45,6 +45,7 @@ class GitlabTree:
45
45
  disable_progress: bool = False,
46
46
  include_shared: bool = True,
47
47
  use_fetch: bool = False,
48
+ mirror: bool = False,
48
49
  hide_token: bool = False,
49
50
  user_projects: bool = False,
50
51
  group_search: Optional[str] = None,
@@ -69,6 +70,7 @@ class GitlabTree:
69
70
  disable_progress: Whether to disable progress bar (used if config not provided)
70
71
  include_shared: Whether to include shared projects (used if config not provided)
71
72
  use_fetch: Whether to use git fetch instead of pull (used if config not provided)
73
+ mirror: Whether to create bare mirror repositories (used if config not provided)
72
74
  hide_token: Whether to hide token in URLs (used if config not provided)
73
75
  user_projects: Whether to fetch only user projects (used if config not provided)
74
76
  group_search: Search term for filtering groups (used if config not provided)
@@ -98,6 +100,7 @@ class GitlabTree:
98
100
  disable_progress = config.disable_progress
99
101
  include_shared = config.include_shared
100
102
  use_fetch = config.use_fetch
103
+ mirror = config.mirror
101
104
  hide_token = config.hide_token
102
105
  user_projects = config.user_projects
103
106
  group_search = config.group_search
@@ -175,6 +178,7 @@ class GitlabTree:
175
178
  self.token = token
176
179
  self.include_shared = include_shared
177
180
  self.use_fetch = use_fetch
181
+ self.mirror = mirror
178
182
  self.hide_token = hide_token
179
183
  self.user_projects = user_projects
180
184
  self.group_search = group_search
@@ -308,7 +312,7 @@ class GitlabTree:
308
312
  len(self.root.descendants) - len(self.root.leaves), len(self.root.leaves))
309
313
  sync_tree(self.root, dest, concurrency=self.concurrency,
310
314
  disable_progress=self.disable_progress, recursive=self.recursive,
311
- use_fetch=self.use_fetch, hide_token=self.hide_token,
315
+ use_fetch=self.use_fetch, mirror=self.mirror, hide_token=self.hide_token,
312
316
  git_options=self.git_options)
313
317
  except GitlabberGitError:
314
318
  # Re-raise git errors as-is
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitlabber
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: A Gitlab clone/pull utility for backing up or cloning Gitlab groups
5
5
  Author-email: Erez Mazor <erezmazor@gmail.com>
6
6
  License: MIT
@@ -26,6 +26,7 @@ Requires-Dist: pyyaml
26
26
  Requires-Dist: pydantic>=2.7
27
27
  Requires-Dist: pydantic-settings>=2.7
28
28
  Requires-Dist: typer>=0.12
29
+ Requires-Dist: urllib3==2.5.0
29
30
  Requires-Dist: rich
30
31
  Requires-Dist: GitPython
31
32
  Requires-Dist: python-gitlab
@@ -229,7 +230,8 @@ Usage
229
230
  number of concurrent git operations (default: 1)
230
231
  --api-concurrency N number of concurrent API calls for tree building (default: 5)
231
232
  -r, --recursive clone/pull git submodules recursively
232
- -F, --use-fetch clone/fetch git repository (mirrored repositories)
233
+ -F, --use-fetch use git fetch instead of pull for updates (normal repositories with working tree)
234
+ --mirror create bare mirror repositories (for backups, automatically uses fetch)
233
235
  -s, --include-shared include shared projects in the results
234
236
  -g term, --group-search term
235
237
  only include groups matching the search term, filtering done at the API level (useful for large projects, see: https://docs.gitlab.com/ee/api/groups.html#search-for-group works with partial names of path or name)
@@ -4,6 +4,7 @@ pyyaml
4
4
  pydantic>=2.7
5
5
  pydantic-settings>=2.7
6
6
  typer>=0.12
7
+ urllib3==2.5.0
7
8
  rich
8
9
  GitPython
9
10
  python-gitlab
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gitlabber"
7
- version = "2.1.0"
7
+ version = "2.1.1"
8
8
  description = "A Gitlab clone/pull utility for backing up or cloning Gitlab groups"
9
9
  readme = "README.rst"
10
10
  requires-python = ">=3.11"
@@ -32,6 +32,7 @@ dependencies = [
32
32
  "pydantic>=2.7",
33
33
  "pydantic-settings>=2.7",
34
34
  "typer>=0.12",
35
+ "urllib3==2.5.0",
35
36
  "rich",
36
37
  "GitPython",
37
38
  "python-gitlab",