gitlabber 2.1.0__tar.gz → 2.1.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 (132) hide show
  1. {gitlabber-2.1.0/gitlabber.egg-info → gitlabber-2.1.2}/PKG-INFO +5 -3
  2. {gitlabber-2.1.0 → gitlabber-2.1.2}/README.rst +2 -1
  3. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/__init__.py +1 -1
  4. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/cli.py +13 -1
  5. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/config.py +1 -0
  6. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/git.py +21 -7
  7. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/gitlab_tree.py +5 -1
  8. {gitlabber-2.1.0 → gitlabber-2.1.2/gitlabber.egg-info}/PKG-INFO +5 -3
  9. gitlabber-2.1.2/gitlabber.egg-info/SOURCES.txt +57 -0
  10. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber.egg-info/requires.txt +2 -1
  11. {gitlabber-2.1.0 → gitlabber-2.1.2}/pyproject.toml +7 -3
  12. gitlabber-2.1.2/tests/test_git.py +499 -0
  13. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_helpers.py +9 -1
  14. gitlabber-2.1.0/gitlabber.egg-info/SOURCES.txt +0 -129
  15. gitlabber-2.1.0/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  16. gitlabber-2.1.0/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  17. gitlabber-2.1.0/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  18. gitlabber-2.1.0/tests/__pycache__/conftest.cpython-311-pytest-9.0.1.pyc +0 -0
  19. gitlabber-2.1.0/tests/__pycache__/conftest.cpython-313-pytest-9.0.1.pyc +0 -0
  20. gitlabber-2.1.0/tests/__pycache__/gitlab_test_utils.cpython-311.pyc +0 -0
  21. gitlabber-2.1.0/tests/__pycache__/gitlab_test_utils.cpython-312.pyc +0 -0
  22. gitlabber-2.1.0/tests/__pycache__/gitlab_test_utils.cpython-313.pyc +0 -0
  23. gitlabber-2.1.0/tests/__pycache__/io_test_util.cpython-311.pyc +0 -0
  24. gitlabber-2.1.0/tests/__pycache__/io_test_util.cpython-312.pyc +0 -0
  25. gitlabber-2.1.0/tests/__pycache__/io_test_util.cpython-313.pyc +0 -0
  26. gitlabber-2.1.0/tests/__pycache__/test_archive.cpython-311-pytest-8.2.2.pyc +0 -0
  27. gitlabber-2.1.0/tests/__pycache__/test_archive.cpython-312-pytest-8.3.3.pyc +0 -0
  28. gitlabber-2.1.0/tests/__pycache__/test_archive.cpython-312-pytest-8.3.4.pyc +0 -0
  29. gitlabber-2.1.0/tests/__pycache__/test_archive.cpython-312-pytest-8.3.5.pyc +0 -0
  30. gitlabber-2.1.0/tests/__pycache__/test_archive.cpython-313-pytest-9.0.1.pyc +0 -0
  31. gitlabber-2.1.0/tests/__pycache__/test_auth.cpython-313-pytest-9.0.1.pyc +0 -0
  32. gitlabber-2.1.0/tests/__pycache__/test_cli.cpython-311-pytest-8.2.2.pyc +0 -0
  33. gitlabber-2.1.0/tests/__pycache__/test_cli.cpython-311-pytest-9.0.1.pyc +0 -0
  34. gitlabber-2.1.0/tests/__pycache__/test_cli.cpython-312-pytest-8.3.3.pyc +0 -0
  35. gitlabber-2.1.0/tests/__pycache__/test_cli.cpython-312-pytest-8.3.4.pyc +0 -0
  36. gitlabber-2.1.0/tests/__pycache__/test_cli.cpython-312-pytest-8.3.5.pyc +0 -0
  37. gitlabber-2.1.0/tests/__pycache__/test_cli.cpython-313-pytest-9.0.1.pyc +0 -0
  38. gitlabber-2.1.0/tests/__pycache__/test_cli_token_storage.cpython-313-pytest-9.0.1.pyc +0 -0
  39. gitlabber-2.1.0/tests/__pycache__/test_config.cpython-313-pytest-9.0.1.pyc +0 -0
  40. gitlabber-2.1.0/tests/__pycache__/test_e2e.cpython-311-pytest-8.2.2.pyc +0 -0
  41. gitlabber-2.1.0/tests/__pycache__/test_e2e.cpython-312-pytest-8.3.3.pyc +0 -0
  42. gitlabber-2.1.0/tests/__pycache__/test_e2e.cpython-312-pytest-8.3.4.pyc +0 -0
  43. gitlabber-2.1.0/tests/__pycache__/test_e2e.cpython-312-pytest-8.3.5.pyc +0 -0
  44. gitlabber-2.1.0/tests/__pycache__/test_e2e.cpython-313-pytest-9.0.1.pyc +0 -0
  45. gitlabber-2.1.0/tests/__pycache__/test_e2e.cpython-313.pyc +0 -0
  46. gitlabber-2.1.0/tests/__pycache__/test_exceptions.cpython-313-pytest-9.0.1.pyc +0 -0
  47. gitlabber-2.1.0/tests/__pycache__/test_format.cpython-311-pytest-8.2.2.pyc +0 -0
  48. gitlabber-2.1.0/tests/__pycache__/test_format.cpython-312-pytest-8.3.3.pyc +0 -0
  49. gitlabber-2.1.0/tests/__pycache__/test_format.cpython-312-pytest-8.3.4.pyc +0 -0
  50. gitlabber-2.1.0/tests/__pycache__/test_format.cpython-312-pytest-8.3.5.pyc +0 -0
  51. gitlabber-2.1.0/tests/__pycache__/test_format.cpython-313-pytest-9.0.1.pyc +0 -0
  52. gitlabber-2.1.0/tests/__pycache__/test_git.cpython-311-pytest-8.2.2.pyc +0 -0
  53. gitlabber-2.1.0/tests/__pycache__/test_git.cpython-312-pytest-8.3.3.pyc +0 -0
  54. gitlabber-2.1.0/tests/__pycache__/test_git.cpython-312-pytest-8.3.4.pyc +0 -0
  55. gitlabber-2.1.0/tests/__pycache__/test_git.cpython-312-pytest-8.3.5.pyc +0 -0
  56. gitlabber-2.1.0/tests/__pycache__/test_git.cpython-313-pytest-9.0.1.pyc +0 -0
  57. gitlabber-2.1.0/tests/__pycache__/test_gitlab_tree.cpython-311-pytest-8.2.2.pyc +0 -0
  58. gitlabber-2.1.0/tests/__pycache__/test_gitlab_tree.cpython-312-pytest-8.3.3.pyc +0 -0
  59. gitlabber-2.1.0/tests/__pycache__/test_gitlab_tree.cpython-312-pytest-8.3.4.pyc +0 -0
  60. gitlabber-2.1.0/tests/__pycache__/test_gitlab_tree.cpython-312-pytest-8.3.5.pyc +0 -0
  61. gitlabber-2.1.0/tests/__pycache__/test_gitlab_tree.cpython-313-pytest-9.0.1.pyc +0 -0
  62. gitlabber-2.1.0/tests/__pycache__/test_helpers.cpython-311-pytest-9.0.1.pyc +0 -0
  63. gitlabber-2.1.0/tests/__pycache__/test_helpers.cpython-313-pytest-9.0.1.pyc +0 -0
  64. gitlabber-2.1.0/tests/__pycache__/test_integration.cpython-311-pytest-8.2.2.pyc +0 -0
  65. gitlabber-2.1.0/tests/__pycache__/test_integration.cpython-311-pytest-9.0.1.pyc +0 -0
  66. gitlabber-2.1.0/tests/__pycache__/test_integration.cpython-312-pytest-8.3.3.pyc +0 -0
  67. gitlabber-2.1.0/tests/__pycache__/test_integration.cpython-312-pytest-8.3.4.pyc +0 -0
  68. gitlabber-2.1.0/tests/__pycache__/test_integration.cpython-312-pytest-8.3.5.pyc +0 -0
  69. gitlabber-2.1.0/tests/__pycache__/test_integration.cpython-313-pytest-9.0.1.pyc +0 -0
  70. gitlabber-2.1.0/tests/__pycache__/test_main.cpython-313-pytest-9.0.1.pyc +0 -0
  71. gitlabber-2.1.0/tests/__pycache__/test_method.cpython-311-pytest-8.2.2.pyc +0 -0
  72. gitlabber-2.1.0/tests/__pycache__/test_method.cpython-312-pytest-8.3.3.pyc +0 -0
  73. gitlabber-2.1.0/tests/__pycache__/test_method.cpython-312-pytest-8.3.4.pyc +0 -0
  74. gitlabber-2.1.0/tests/__pycache__/test_method.cpython-312-pytest-8.3.5.pyc +0 -0
  75. gitlabber-2.1.0/tests/__pycache__/test_method.cpython-313-pytest-9.0.1.pyc +0 -0
  76. gitlabber-2.1.0/tests/__pycache__/test_naming.cpython-311-pytest-8.2.2.pyc +0 -0
  77. gitlabber-2.1.0/tests/__pycache__/test_naming.cpython-312-pytest-8.3.3.pyc +0 -0
  78. gitlabber-2.1.0/tests/__pycache__/test_naming.cpython-312-pytest-8.3.4.pyc +0 -0
  79. gitlabber-2.1.0/tests/__pycache__/test_naming.cpython-312-pytest-8.3.5.pyc +0 -0
  80. gitlabber-2.1.0/tests/__pycache__/test_naming.cpython-313-pytest-9.0.1.pyc +0 -0
  81. gitlabber-2.1.0/tests/__pycache__/test_performance.cpython-313-pytest-9.0.1.pyc +0 -0
  82. gitlabber-2.1.0/tests/__pycache__/test_performance.cpython-313.pyc +0 -0
  83. gitlabber-2.1.0/tests/__pycache__/test_progress.cpython-313-pytest-9.0.1.pyc +0 -0
  84. gitlabber-2.1.0/tests/__pycache__/test_rate_limiter.cpython-313-pytest-9.0.1.pyc +0 -0
  85. gitlabber-2.1.0/tests/__pycache__/test_token_storage.cpython-313-pytest-9.0.1.pyc +0 -0
  86. gitlabber-2.1.0/tests/__pycache__/test_url_builder.cpython-313-pytest-9.0.1.pyc +0 -0
  87. gitlabber-2.1.0/tests/test_git.py +0 -219
  88. {gitlabber-2.1.0 → gitlabber-2.1.2}/LICENSE.txt +0 -0
  89. {gitlabber-2.1.0 → gitlabber-2.1.2}/MANIFEST.in +0 -0
  90. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/__main__.py +0 -0
  91. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/archive.py +0 -0
  92. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/auth.py +0 -0
  93. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/exceptions.py +0 -0
  94. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/format.py +0 -0
  95. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/method.py +0 -0
  96. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/naming.py +0 -0
  97. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/progress.py +0 -0
  98. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/rate_limiter.py +0 -0
  99. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/token_storage.py +0 -0
  100. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/tree_builder.py +0 -0
  101. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber/url_builder.py +0 -0
  102. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber.egg-info/dependency_links.txt +0 -0
  103. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber.egg-info/entry_points.txt +0 -0
  104. {gitlabber-2.1.0 → gitlabber-2.1.2}/gitlabber.egg-info/top_level.txt +0 -0
  105. {gitlabber-2.1.0 → gitlabber-2.1.2}/setup.cfg +0 -0
  106. {gitlabber-2.1.0 → gitlabber-2.1.2}/setup.py +0 -0
  107. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/__init__.py +0 -0
  108. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/conftest.py +0 -0
  109. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/gitlab_test_utils.py +0 -0
  110. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/io_test_util.py +0 -0
  111. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test-input.yaml +0 -0
  112. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test-output.json +0 -0
  113. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test-output.tree +0 -0
  114. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test-output.yaml +0 -0
  115. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_archive.py +0 -0
  116. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_auth.py +0 -0
  117. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_cli.py +0 -0
  118. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_cli_token_storage.py +0 -0
  119. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_config.py +0 -0
  120. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_e2e.py +0 -0
  121. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_exceptions.py +0 -0
  122. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_format.py +0 -0
  123. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_gitlab_tree.py +0 -0
  124. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_integration.py +0 -0
  125. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_main.py +0 -0
  126. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_method.py +0 -0
  127. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_naming.py +0 -0
  128. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_performance.py +0 -0
  129. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_progress.py +0 -0
  130. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_rate_limiter.py +0 -0
  131. {gitlabber-2.1.0 → gitlabber-2.1.2}/tests/test_token_storage.py +0 -0
  132. {gitlabber-2.1.0 → gitlabber-2.1.2}/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.2
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,8 +26,9 @@ 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.7.0
29
30
  Requires-Dist: rich
30
- Requires-Dist: GitPython
31
+ Requires-Dist: GitPython>=3.1.59
31
32
  Requires-Dist: python-gitlab
32
33
  Provides-Extra: test
33
34
  Requires-Dist: coverage; extra == "test"
@@ -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.2'
@@ -118,6 +118,8 @@ def config_logging(verbose: bool, print_mode: bool) -> None:
118
118
  os.environ["GIT_PYTHON_TRACE"] = "full"
119
119
  else:
120
120
  logging.getLogger().setLevel(logging.INFO)
121
+ # Suppress urllib3 connection pool warnings unless verbose mode is enabled
122
+ logging.getLogger("urllib3.connectionpool").setLevel(logging.ERROR)
121
123
 
122
124
 
123
125
  def _version_callback(value: bool) -> None:
@@ -211,6 +213,7 @@ def run_gitlabber(
211
213
  exclude: Optional[str],
212
214
  recursive: bool,
213
215
  use_fetch: bool,
216
+ mirror: bool,
214
217
  include_shared: bool,
215
218
  group_search: Optional[str],
216
219
  user_projects: bool,
@@ -244,6 +247,7 @@ def run_gitlabber(
244
247
  exclude: Comma-separated glob patterns to exclude
245
248
  recursive: Clone submodules recursively
246
249
  use_fetch: Use git fetch instead of pull
250
+ mirror: Create bare mirror repositories (implies use_fetch)
247
251
  include_shared: Include shared projects
248
252
  group_search: Search term for filtering groups at API level
249
253
  user_projects: Fetch only user personal projects
@@ -295,6 +299,7 @@ def run_gitlabber(
295
299
  "recursive": recursive,
296
300
  "include_shared": include_shared,
297
301
  "use_fetch": use_fetch,
302
+ "mirror": mirror,
298
303
  "hide_token": hide_token,
299
304
  "user_projects": user_projects,
300
305
  "group_search": group_search,
@@ -318,6 +323,7 @@ def run_gitlabber(
318
323
  disable_progress=verbose,
319
324
  include_shared=include_shared,
320
325
  use_fetch=use_fetch,
326
+ mirror=mirror,
321
327
  hide_token=hide_token,
322
328
  user_projects=user_projects,
323
329
  group_search=group_search,
@@ -458,7 +464,7 @@ def cli(
458
464
  False,
459
465
  "-F",
460
466
  "--use-fetch",
461
- help="Use git fetch instead of pull (mirrored repositories)",
467
+ help="Use git fetch instead of pull for updates (normal repositories with working tree)",
462
468
  ),
463
469
  exclude_shared: bool = typer.Option(
464
470
  False,
@@ -495,6 +501,11 @@ def cli(
495
501
  "--store-token",
496
502
  help="Store token securely in OS keyring (requires keyring package)",
497
503
  ),
504
+ mirror: bool = typer.Option(
505
+ False,
506
+ "--mirror",
507
+ help="Create bare mirror repositories (for backups, automatically uses fetch)",
508
+ ),
498
509
  ) -> None:
499
510
  """Main CLI command for gitlabber.
500
511
 
@@ -560,6 +571,7 @@ def cli(
560
571
  exclude=exclude,
561
572
  recursive=recursive,
562
573
  use_fetch=use_fetch,
574
+ mirror=mirror,
563
575
  include_shared=include_shared_value,
564
576
  group_search=group_search,
565
577
  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.2
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,8 +26,9 @@ 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.7.0
29
30
  Requires-Dist: rich
30
- Requires-Dist: GitPython
31
+ Requires-Dist: GitPython>=3.1.59
31
32
  Requires-Dist: python-gitlab
32
33
  Provides-Extra: test
33
34
  Requires-Dist: coverage; extra == "test"
@@ -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)
@@ -0,0 +1,57 @@
1
+ LICENSE.txt
2
+ MANIFEST.in
3
+ README.rst
4
+ pyproject.toml
5
+ setup.cfg
6
+ setup.py
7
+ gitlabber/__init__.py
8
+ gitlabber/__main__.py
9
+ gitlabber/archive.py
10
+ gitlabber/auth.py
11
+ gitlabber/cli.py
12
+ gitlabber/config.py
13
+ gitlabber/exceptions.py
14
+ gitlabber/format.py
15
+ gitlabber/git.py
16
+ gitlabber/gitlab_tree.py
17
+ gitlabber/method.py
18
+ gitlabber/naming.py
19
+ gitlabber/progress.py
20
+ gitlabber/rate_limiter.py
21
+ gitlabber/token_storage.py
22
+ gitlabber/tree_builder.py
23
+ gitlabber/url_builder.py
24
+ gitlabber.egg-info/PKG-INFO
25
+ gitlabber.egg-info/SOURCES.txt
26
+ gitlabber.egg-info/dependency_links.txt
27
+ gitlabber.egg-info/entry_points.txt
28
+ gitlabber.egg-info/requires.txt
29
+ gitlabber.egg-info/top_level.txt
30
+ tests/__init__.py
31
+ tests/conftest.py
32
+ tests/gitlab_test_utils.py
33
+ tests/io_test_util.py
34
+ tests/test-input.yaml
35
+ tests/test-output.json
36
+ tests/test-output.tree
37
+ tests/test-output.yaml
38
+ tests/test_archive.py
39
+ tests/test_auth.py
40
+ tests/test_cli.py
41
+ tests/test_cli_token_storage.py
42
+ tests/test_config.py
43
+ tests/test_e2e.py
44
+ tests/test_exceptions.py
45
+ tests/test_format.py
46
+ tests/test_git.py
47
+ tests/test_gitlab_tree.py
48
+ tests/test_helpers.py
49
+ tests/test_integration.py
50
+ tests/test_main.py
51
+ tests/test_method.py
52
+ tests/test_naming.py
53
+ tests/test_performance.py
54
+ tests/test_progress.py
55
+ tests/test_rate_limiter.py
56
+ tests/test_token_storage.py
57
+ tests/test_url_builder.py
@@ -4,8 +4,9 @@ pyyaml
4
4
  pydantic>=2.7
5
5
  pydantic-settings>=2.7
6
6
  typer>=0.12
7
+ urllib3>=2.7.0
7
8
  rich
8
- GitPython
9
+ GitPython>=3.1.59
9
10
  python-gitlab
10
11
 
11
12
  [keyring]
@@ -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.2"
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,8 +32,9 @@ dependencies = [
32
32
  "pydantic>=2.7",
33
33
  "pydantic-settings>=2.7",
34
34
  "typer>=0.12",
35
+ "urllib3>=2.7.0",
35
36
  "rich",
36
- "GitPython",
37
+ "GitPython>=3.1.59",
37
38
  "python-gitlab",
38
39
  ]
39
40
 
@@ -99,4 +100,7 @@ exclude_lines = [
99
100
  "if TYPE_CHECKING:",
100
101
  "class .*\\bProtocol\\):",
101
102
  "@(abc\\.)?abstractmethod",
102
- ]
103
+ ]
104
+ [tool.ruff.lint]
105
+ # Pin to ruff's classic default rule set; newer ruff releases expand the defaults
106
+ select = ["E4", "E7", "E9", "F"]