multi-workspace 3.2.16__tar.gz → 3.2.18__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.
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/PKG-INFO +1 -1
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/_version.py +2 -2
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/app_api.py +22 -1
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/worktree.py +31 -9
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/.gitignore +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/LICENSE +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/README.md +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/__init__.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/__main__.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/add.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/api.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/bootstrap.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/branch.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/cli.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/cli_helpers.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/collaborator.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/convert_monorepo.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/doctor.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/errors.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/git_helpers.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/git_run.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/git_set_branch.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/ignore_files.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/init.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/logging.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/managed_blocks.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/open.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/paths.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/registry.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/remove.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/repo_urls.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/repos.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/resources/init_readme.md +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/service.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/settings.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync_agents.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync_github.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync_vscode.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync_vscode_devcontainer.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync_vscode_extensions.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync_vscode_helpers.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync_vscode_launch.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync_vscode_settings.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/sync_vscode_tasks.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/multi/utils.py +0 -0
- {multi_workspace-3.2.16 → multi_workspace-3.2.18}/pyproject.toml +0 -0
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '3.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (3, 2,
|
|
21
|
+
__version__ = version = '3.2.18'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 2, 18)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -126,6 +126,21 @@ def _subrepo_payloads(repo_path: Path) -> list[SubRepoPayload] | None:
|
|
|
126
126
|
]
|
|
127
127
|
|
|
128
128
|
|
|
129
|
+
def get_project_subrepos(repo_path: str | Path) -> list[SubRepoPayload]:
|
|
130
|
+
"""Public multi.json sub-repo listing for library consumers ([] when not a workspace)."""
|
|
131
|
+
return _subrepo_payloads(Path(repo_path)) or []
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def list_project_repo_names(repo_path: str | Path) -> list[str]:
|
|
135
|
+
"""Names of repos declared in repo_path's own multi.json ([] when absent/invalid).
|
|
136
|
+
|
|
137
|
+
Only reads a multi.json directly inside repo_path — never searches parent
|
|
138
|
+
directories — so callers can safely probe arbitrary directories.
|
|
139
|
+
"""
|
|
140
|
+
subrepos = _load_subrepos(Path(repo_path))
|
|
141
|
+
return [repo.name for repo in subrepos] if subrepos else []
|
|
142
|
+
|
|
143
|
+
|
|
129
144
|
def _describe_git_head(repo_path: Path) -> str | None:
|
|
130
145
|
if not repo_path.exists():
|
|
131
146
|
return None
|
|
@@ -212,7 +227,13 @@ def _get_git_status_internal(repo_path: Path, ignore_untracked: bool) -> RepoSta
|
|
|
212
227
|
if ignore_untracked:
|
|
213
228
|
status_args.append("--untracked-files=no")
|
|
214
229
|
|
|
215
|
-
|
|
230
|
+
try:
|
|
231
|
+
porcelain = _run_git(status_args, cwd=repo_path, timeout=10).stdout
|
|
232
|
+
except (OSError, subprocess.SubprocessError):
|
|
233
|
+
# A directory with a .git entry that git itself rejects (corrupt or
|
|
234
|
+
# locked repo) is neither clean nor dirty; report it as unknown
|
|
235
|
+
# instead of surfacing an exception to status callers.
|
|
236
|
+
return "unknown"
|
|
216
237
|
if porcelain.strip():
|
|
217
238
|
return "dirty"
|
|
218
239
|
|
|
@@ -11,7 +11,6 @@ from multi.cli_helpers import common_command_wrapper
|
|
|
11
11
|
from multi.errors import GitError
|
|
12
12
|
from multi.git_helpers import (
|
|
13
13
|
check_all_on_same_branch,
|
|
14
|
-
check_all_repos_are_clean,
|
|
15
14
|
check_branch_existence,
|
|
16
15
|
expected_branch_for_repo,
|
|
17
16
|
repo_uses_fixed_branch,
|
|
@@ -93,14 +92,23 @@ def _resolve_relative_path(entry: str) -> Path:
|
|
|
93
92
|
return relative_path
|
|
94
93
|
|
|
95
94
|
|
|
95
|
+
def _worktrees_dir(root_dir: Path) -> Path:
|
|
96
|
+
"""Return the sibling directory that holds all worktrees for a workspace.
|
|
97
|
+
|
|
98
|
+
For a workspace at ``<parent>/my-workspace``, worktrees live under
|
|
99
|
+
``<parent>/my-workspace-worktrees/``.
|
|
100
|
+
"""
|
|
101
|
+
return root_dir.parent / f"{root_dir.name}-worktrees"
|
|
102
|
+
|
|
103
|
+
|
|
96
104
|
def _destination_for_name(root_dir: Path, name: str) -> Path:
|
|
97
105
|
name_path = Path(name)
|
|
98
106
|
if name_path.is_absolute() or len(name_path.parts) != 1 or name in {"", ".", ".."}:
|
|
99
107
|
raise GitError(
|
|
100
|
-
"Worktree NAME must be a single
|
|
108
|
+
"Worktree NAME must be a single directory name. "
|
|
101
109
|
"Use --branch for branch names that contain path separators."
|
|
102
110
|
)
|
|
103
|
-
return root_dir
|
|
111
|
+
return _worktrees_dir(root_dir) / name
|
|
104
112
|
|
|
105
113
|
|
|
106
114
|
def _repo_for_path(paths: Paths, relative_path: Path) -> tuple[Path, Path]:
|
|
@@ -197,7 +205,9 @@ def _checkout_subrepos(
|
|
|
197
205
|
try:
|
|
198
206
|
_run_git(repo.path, ["fetch", str(source_repo.path), base_ref])
|
|
199
207
|
_run_git(repo.path, ["checkout", "-b", repo_branch_name, "FETCH_HEAD"])
|
|
200
|
-
logger.info(
|
|
208
|
+
logger.info(
|
|
209
|
+
f"✅ Switched to branch '{repo_branch_name}' in {repo.path}"
|
|
210
|
+
)
|
|
201
211
|
continue
|
|
202
212
|
except GitError as exc:
|
|
203
213
|
logger.warning(
|
|
@@ -234,7 +244,8 @@ def add_worktree(
|
|
|
234
244
|
if destination.exists():
|
|
235
245
|
raise GitError(f"Worktree destination already exists: {destination}")
|
|
236
246
|
|
|
237
|
-
|
|
247
|
+
# No clean-working-tree requirement: worktree creation branches from a
|
|
248
|
+
# commit (HEAD or --base-ref) and never mutates the source working trees.
|
|
238
249
|
all_on_same_branch = check_all_on_same_branch(paths=paths, raise_error=False)
|
|
239
250
|
if not all_on_same_branch:
|
|
240
251
|
logger.warning(
|
|
@@ -242,8 +253,12 @@ def add_worktree(
|
|
|
242
253
|
"already exist for all repos, this command will fix the situation."
|
|
243
254
|
)
|
|
244
255
|
|
|
256
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
245
257
|
_create_root_worktree(paths.root_dir, destination, branch_name, base_ref)
|
|
246
|
-
sync
|
|
258
|
+
# Skip branch matching during the initial sync: the worktree branch is
|
|
259
|
+
# created in each sub-repo right after, so warning about it being missing
|
|
260
|
+
# here would only be confusing.
|
|
261
|
+
sync(root_dir=destination, ensure_on_same_branch=False, install_set=install_set)
|
|
247
262
|
|
|
248
263
|
destination_paths = Paths(destination, install_set=install_set)
|
|
249
264
|
_checkout_subrepos(
|
|
@@ -259,7 +274,11 @@ def add_worktree(
|
|
|
259
274
|
|
|
260
275
|
@click.group(name="worktree")
|
|
261
276
|
def worktree_cmd() -> None:
|
|
262
|
-
"""Manage
|
|
277
|
+
"""Manage git worktrees for a multi workspace.
|
|
278
|
+
|
|
279
|
+
Worktrees live in a sibling directory named after the workspace with
|
|
280
|
+
'-worktrees' appended (e.g. my-workspace-worktrees/).
|
|
281
|
+
"""
|
|
263
282
|
|
|
264
283
|
|
|
265
284
|
@click.command(name="add")
|
|
@@ -288,9 +307,12 @@ def worktree_add_cmd(
|
|
|
288
307
|
install_set: str | None = None,
|
|
289
308
|
base_ref: str = "HEAD",
|
|
290
309
|
) -> None:
|
|
291
|
-
"""Create
|
|
310
|
+
"""Create an isolated worktree of the whole workspace.
|
|
311
|
+
|
|
312
|
+
The worktree is created at <parent>/<workspace-dirname>-worktrees/NAME
|
|
313
|
+
(the -worktrees directory is created if missing).
|
|
292
314
|
|
|
293
|
-
NAME: Directory name for the
|
|
315
|
+
NAME: Directory name for the worktree. Used as the branch name when
|
|
294
316
|
--branch is omitted.
|
|
295
317
|
"""
|
|
296
318
|
add_worktree(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|