Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ options:
number of concurrent git operations (default: 1)
--api-concurrency N number of concurrent API calls for tree building (default: 5)
-r, --recursive clone/pull git submodules recursively
-F, --use-fetch clone/fetch git repository (mirrored repositories)
-F, --use-fetch use git fetch instead of pull for updates (normal repositories with working tree)
--mirror create bare mirror repositories (for backups, automatically uses fetch)
-s, --include-shared include shared projects in the results
-g term, --group-search term
only include groups matching the search term, filtering done at the API level
Expand Down Expand Up @@ -202,8 +203,6 @@ gitlabber --store-token -u https://gitlab.com
# Use stored token (no -t flag needed)
gitlabber -u https://gitlab.com .
```
<|tool▁call▁begin|>
run_terminal_cmd

## Common Use Cases

Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ Usage
number of concurrent git operations (default: 1)
--api-concurrency N number of concurrent API calls for tree building (default: 5)
-r, --recursive clone/pull git submodules recursively
-F, --use-fetch clone/fetch git repository (mirrored repositories)
-F, --use-fetch use git fetch instead of pull for updates (normal repositories with working tree)
--mirror create bare mirror repositories (for backups, automatically uses fetch)
-s, --include-shared include shared projects in the results
-g term, --group-search term
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)
Expand Down
3 changes: 2 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ Usage
number of concurrent git operations (default: 1)
--api-concurrency N number of concurrent API calls for tree building (default: 5)
-r, --recursive clone/pull git submodules recursively
-F, --use-fetch clone/fetch git repository (mirrored repositories)
-F, --use-fetch use git fetch instead of pull for updates (normal repositories with working tree)
--mirror create bare mirror repositories (for backups, automatically uses fetch)
-s, --include-shared include shared projects in the results
-g term, --group-search term
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)
Expand Down
12 changes: 11 additions & 1 deletion gitlabber/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ def run_gitlabber(
exclude: Optional[str],
recursive: bool,
use_fetch: bool,
mirror: bool,
include_shared: bool,
group_search: Optional[str],
user_projects: bool,
Expand Down Expand Up @@ -244,6 +245,7 @@ def run_gitlabber(
exclude: Comma-separated glob patterns to exclude
recursive: Clone submodules recursively
use_fetch: Use git fetch instead of pull
mirror: Create bare mirror repositories (implies use_fetch)
include_shared: Include shared projects
group_search: Search term for filtering groups at API level
user_projects: Fetch only user personal projects
Expand Down Expand Up @@ -295,6 +297,7 @@ def run_gitlabber(
"recursive": recursive,
"include_shared": include_shared,
"use_fetch": use_fetch,
"mirror": mirror,
"hide_token": hide_token,
"user_projects": user_projects,
"group_search": group_search,
Expand All @@ -318,6 +321,7 @@ def run_gitlabber(
disable_progress=verbose,
include_shared=include_shared,
use_fetch=use_fetch,
mirror=mirror,
hide_token=hide_token,
user_projects=user_projects,
group_search=group_search,
Expand Down Expand Up @@ -458,7 +462,7 @@ def cli(
False,
"-F",
"--use-fetch",
help="Use git fetch instead of pull (mirrored repositories)",
help="Use git fetch instead of pull for updates (normal repositories with working tree)",
),
exclude_shared: bool = typer.Option(
False,
Expand Down Expand Up @@ -495,6 +499,11 @@ def cli(
"--store-token",
help="Store token securely in OS keyring (requires keyring package)",
),
mirror: bool = typer.Option(
False,
"--mirror",
help="Create bare mirror repositories (for backups, automatically uses fetch)",
),
) -> None:
"""Main CLI command for gitlabber.

Expand Down Expand Up @@ -560,6 +569,7 @@ def cli(
exclude=exclude,
recursive=recursive,
use_fetch=use_fetch,
mirror=mirror,
include_shared=include_shared_value,
group_search=group_search,
user_projects=user_projects,
Expand Down
1 change: 1 addition & 0 deletions gitlabber/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class GitlabberConfig(BaseModel):
disable_progress: bool = False
include_shared: bool = True
use_fetch: bool = False
mirror: bool = False
hide_token: bool = False
user_projects: bool = False
group_search: Optional[str] = None
Expand Down
28 changes: 21 additions & 7 deletions gitlabber/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class GitAction:
use_fetch: bool = False
hide_token: bool = False
git_options: Optional[str] = None
mirror: bool = False # New parameter


class GitRepository:
Expand Down Expand Up @@ -68,7 +69,7 @@ def clone(action: GitAction, progress_bar: ProgressBar) -> None:
multi_options: list[str] = []
if action.recursive:
multi_options.append('--recursive')
if action.use_fetch:
if action.mirror: # New parameter
multi_options.append('--mirror')
if action.git_options:
multi_options += action.git_options.split(',')
Expand Down Expand Up @@ -132,13 +133,15 @@ def pull(action: GitAction, progress_bar: ProgressBar, repo=None) -> None:
GitlabberGitError: If pull operation fails
"""
log.debug("updating existing project %s", action.path)
operation = 'fetching' if action.use_fetch else 'pulling'
# Mirror implies use_fetch, so check both
should_fetch = action.use_fetch or action.mirror
operation = 'fetching' if should_fetch else 'pulling'
progress_bar.show_progress_detailed(action.node.name, 'project', operation)

try:
if repo is None:
repo = git.Repo(action.path)
if not action.use_fetch:
if not should_fetch:
repo.remotes.origin.pull()
else:
repo.remotes.origin.fetch()
Expand Down Expand Up @@ -226,6 +229,7 @@ def __init__(
dest: str,
recursive: bool = False,
use_fetch: bool = False,
mirror: bool = False,
hide_token: bool = False,
git_options: Optional[str] = None
):
Expand All @@ -235,12 +239,14 @@ def __init__(
dest: Destination directory for repositories
recursive: Whether to clone recursively
use_fetch: Whether to use git fetch instead of pull
mirror: Whether to create bare mirror repositories
hide_token: Whether to hide token in URLs
git_options: Additional git options as comma-separated string
"""
self.dest = Path(dest)
self.recursive = recursive
self.use_fetch = use_fetch
self.mirror = mirror
self.hide_token = hide_token
self.git_options = git_options

Expand Down Expand Up @@ -272,9 +278,11 @@ def _collect_from_node(self, node: Node, actions: list[GitAction]) -> None:
path_str = str(path)

if child.is_leaf:
# Mirror implies use_fetch (mirrors should always use fetch)
effective_use_fetch = self.use_fetch or self.mirror
actions.append(GitAction(
child, path_str, self.recursive,
self.use_fetch, self.hide_token, self.git_options
effective_use_fetch, self.hide_token, self.git_options, self.mirror
))

if not child.is_leaf:
Expand Down Expand Up @@ -307,6 +315,7 @@ def sync(
dest: str,
recursive: bool = False,
use_fetch: bool = False,
mirror: bool = False,
hide_token: bool = False,
git_options: Optional[str] = None
) -> None:
Expand All @@ -317,14 +326,15 @@ def sync(
dest: Destination directory
recursive: Whether to clone recursively
use_fetch: Whether to use git fetch instead of pull
mirror: Whether to create bare mirror repositories
hide_token: Whether to hide token in URLs
git_options: Additional git options as comma-separated string
"""
if not self.disable_progress:
self.progress_bar.init_progress(len(root.leaves))

collector = GitActionCollector(
dest, recursive, use_fetch, hide_token, git_options
dest, recursive, use_fetch, mirror, hide_token, git_options
)
actions = collector.collect(root)

Expand All @@ -343,6 +353,7 @@ def sync_tree(
disable_progress: bool = False,
recursive: bool = False,
use_fetch: bool = False,
mirror: bool = False,
hide_token: bool = False,
git_options: Optional[str] = None
) -> None:
Expand All @@ -356,18 +367,20 @@ def sync_tree(
disable_progress: Whether to disable progress reporting
recursive: Whether to clone recursively
use_fetch: Whether to use git fetch instead of pull
mirror: Whether to create bare mirror repositories
hide_token: Whether to hide token in URLs
git_options: Additional git options as comma-separated string
"""
manager = GitSyncManager(concurrency, disable_progress)
manager.sync(root, dest, recursive, use_fetch, hide_token, git_options)
manager.sync(root, dest, recursive, use_fetch, mirror, hide_token, git_options)


def get_git_actions(
root: Node,
dest: str,
recursive: bool,
use_fetch: bool,
mirror: bool,
hide_token: bool,
git_options: Optional[str] = None
) -> list[GitAction]:
Expand All @@ -378,13 +391,14 @@ def get_git_actions(
dest: Destination directory
recursive: Whether to clone recursively
use_fetch: Whether to use git fetch instead of pull
mirror: Whether to create bare mirror repositories
hide_token: Whether to hide token in URLs
git_options: Additional git options as comma-separated string

Returns:
List of GitAction objects to execute
"""
collector = GitActionCollector(dest, recursive, use_fetch, hide_token, git_options)
collector = GitActionCollector(dest, recursive, use_fetch, mirror, hide_token, git_options)
return collector.collect(root)


Expand Down
6 changes: 5 additions & 1 deletion gitlabber/gitlab_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(self,
disable_progress: bool = False,
include_shared: bool = True,
use_fetch: bool = False,
mirror: bool = False,
hide_token: bool = False,
user_projects: bool = False,
group_search: Optional[str] = None,
Expand All @@ -69,6 +70,7 @@ def __init__(self,
disable_progress: Whether to disable progress bar (used if config not provided)
include_shared: Whether to include shared projects (used if config not provided)
use_fetch: Whether to use git fetch instead of pull (used if config not provided)
mirror: Whether to create bare mirror repositories (used if config not provided)
hide_token: Whether to hide token in URLs (used if config not provided)
user_projects: Whether to fetch only user projects (used if config not provided)
group_search: Search term for filtering groups (used if config not provided)
Expand Down Expand Up @@ -98,6 +100,7 @@ def __init__(self,
disable_progress = config.disable_progress
include_shared = config.include_shared
use_fetch = config.use_fetch
mirror = config.mirror
hide_token = config.hide_token
user_projects = config.user_projects
group_search = config.group_search
Expand Down Expand Up @@ -175,6 +178,7 @@ def __init__(self,
self.token = token
self.include_shared = include_shared
self.use_fetch = use_fetch
self.mirror = mirror
self.hide_token = hide_token
self.user_projects = user_projects
self.group_search = group_search
Expand Down Expand Up @@ -308,7 +312,7 @@ def sync_tree(self, dest: str) -> None:
len(self.root.descendants) - len(self.root.leaves), len(self.root.leaves))
sync_tree(self.root, dest, concurrency=self.concurrency,
disable_progress=self.disable_progress, recursive=self.recursive,
use_fetch=self.use_fetch, hide_token=self.hide_token,
use_fetch=self.use_fetch, mirror=self.mirror, hide_token=self.hide_token,
git_options=self.git_options)
except GitlabberGitError:
# Re-raise git errors as-is
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "gitlabber"
version = "2.1.0"
version = "2.1.1"
description = "A Gitlab clone/pull utility for backing up or cloning Gitlab groups"
readme = "README.rst"
requires-python = ">=3.11"
Expand Down
Loading