Add command internals¶
vcspull.cli.add is the implementation behind
vcspull add — it resolves a checkout on disk into a
configuration entry and writes it back.
Add single repository functionality for vcspull.
-
class vcspull.cli.add.AddAction¶class vcspull.cli.add.AddAction¶
Bases:
EnumAction resolved for a single repo during
vcspull add.
-
vcspull.cli.add._classify_add_action(existing_entry)¶vcspull.cli.add._classify_add_action(existing_entry)¶
Classify the add action for a single repository.
- Parameters:
existing_entry (
Any) – Current config entry for this repo name, orNoneif absent.- Return type:
Examples
Not in config:
>>> _classify_add_action(None) <AddAction.ADD: 'add'>
Already exists (unpinned):
>>> _classify_add_action({"repo": "git+ssh://x"}) <AddAction.SKIP_EXISTING: 'skip_existing'>
Pinned for add:
>>> _classify_add_action({"repo": "git+ssh://x", "options": {"pin": True}}) <AddAction.SKIP_PINNED: 'skip_pinned'> >>> entry = {"repo": "git+ssh://x", "options": {"pin": {"add": True}}} >>> _classify_add_action(entry) <AddAction.SKIP_PINNED: 'skip_pinned'>
Pinned for import only — not pinned for add:
>>> entry = {"repo": "git+ssh://x", "options": {"pin": {"import": True}}} >>> _classify_add_action(entry) <AddAction.SKIP_EXISTING: 'skip_existing'>
-
vcspull.cli.add.create_add_subparser(parser)¶vcspull.cli.add.create_add_subparser(parser)¶
Create
vcspull addargument subparser.- Parameters:
parser (
argparse.ArgumentParser) – The parser to configure- Return type:
-
vcspull.cli.add._resolve_workspace_path(workspace_root, repo_path_str, *, cwd)¶vcspull.cli.add._resolve_workspace_path(workspace_root, repo_path_str, *, cwd)¶
Resolve workspace path from arguments.
- Parameters:
cwd (
pathlib.Path) – Current working directory
- Returns:
Resolved workspace path
- Return type:
-
vcspull.cli.add._detect_git_remote(repo_path)¶vcspull.cli.add._detect_git_remote(repo_path)¶
Return the
originremote URL for a Git repository if available.
-
vcspull.cli.add._normalize_detected_url(remote)¶vcspull.cli.add._normalize_detected_url(remote)¶
Return display and config URLs derived from a detected remote.
-
class vcspull.cli.add.ParsedRepoUrl¶class vcspull.cli.add.ParsedRepoUrl¶
Bases:
NamedTupleWhat
addneeds from a repository URL, as libvcs parses it.
-
vcspull.cli.add._parse_repo_url(url)¶vcspull.cli.add._parse_repo_url(url)¶
Split a repository URL into a name, a rev-free URL, and a revision.
addaccepts a URL argument onGitURL.is_valid(), so it derives the name and revision from that same parse rather than splitting strings itself. Two libvcs details shape this:GitURL.to_url()re-appends a revision the input already carried, so the rev-free URL drops the parsed revision from the tail instead.The pip
file://rule leaves.gitonpathinstead of moving it tosuffix, so the final segment is trimmed unconditionally.
Only pip-style URLs (
git+…) carry a revision; libvcs parses@revon a barehttps://URL as part of the path.- Parameters:
url (
str) – Repository URL, already accepted byGitURL.is_valid().- Returns:
nameisNonewhen the URL has no path segment to name.- Return type:
Examples
>>> _parse_repo_url("https://github.com/pallets/flask.git").name 'flask'
A pip-style revision moves out of the URL:
>>> parsed = _parse_repo_url("git+https://github.com/pallets/[email protected]") >>> parsed.name, parsed.rev ('flask', 'v1.0') >>> parsed.url 'git+https://github.com/pallets/flask.git'
Scp-style remotes, trailing slashes, and a missing
.gitall resolve:>>> _parse_repo_url("[email protected]:pallets/flask.git").name 'flask' >>> _parse_repo_url("https://github.com/pallets/flask/").name 'flask' >>> _parse_repo_url("git+file:///srv/git/flask.git").name 'flask'
A URL with no path segment yields no name:
>>> _parse_repo_url("https://host/.git").name is None True
A revision on a URL libvcs parses no revision for is reported, not kept:
>>> _parse_repo_url("https://github.com/pallets/[email protected]").unparsed_rev 'v1.0' >>> _parse_repo_url("https://user@host/pallets/flask.git").unparsed_rev is None True
-
class vcspull.cli.add.RepoPlacement¶class vcspull.cli.add.RepoPlacement¶
Bases:
NamedTupleWhere a repository lands, for the preview and the write to agree on.
-
vcspull.cli.add._resolve_placement(workspace_root_input, repo_name, repo_path, *, url_mode, cwd)¶vcspull.cli.add._resolve_placement(workspace_root_input, repo_name, repo_path, *, url_mode, cwd)¶
Derive the workspace label and destination path for a workspace root.
Called once the workspace root is settled — after the prompt, which may swap in a different declared root — so the preview cannot announce a workspace the entry is not written under.
- Parameters:
workspace_root_input (
str) – Workspace root as the user, the config, or the prompt supplied it.repo_name (
str) – Repository name that becomes the config key.repo_path (
pathlib.Path) – Existing checkout. Unused in URL mode, where nothing is on disk.url_mode (
bool) –Truewhen declaring from a URL rather than importing a checkout.cwd (
pathlib.Path) – Current working directory, for resolving relative roots.
- Returns:
Label and destination path to preview and to write under.
- Return type:
Examples
In URL mode the destination is the workspace root joined with the name:
>>> placement = _resolve_placement( ... "~/code/", ... "flask", ... pathlib.Path("/nonexistent"), ... url_mode=True, ... cwd=pathlib.Path.cwd(), ... ) >>> placement.workspace_label '~/code/' >>> placement.display_path '~/code/flask'
In path mode the destination is the checkout itself:
>>> checkout = tmp_path / "workspace" / "flask" >>> placement = _resolve_placement( ... str(tmp_path / "workspace"), ... "flask", ... checkout, ... url_mode=False, ... cwd=pathlib.Path.cwd(), ... ) >>> placement.display_path == str(checkout) True
-
class vcspull.cli.add.ConfigFileResolution¶class vcspull.cli.add.ConfigFileResolution¶
Bases:
NamedTupleOutcome of deciding which config file
addshould write to.
-
vcspull.cli.add._resolve_config_file(config_file_path_str)¶vcspull.cli.add._resolve_config_file(config_file_path_str)¶
Resolve which config file
addshould write to.Reports discovery outcomes rather than logging them, so callers that only need the path (to read declared workspace roots, say) do not emit the discovery messages a second time.
Examples
An explicit
-f/--filepath is taken as given:>>> resolution = _resolve_config_file(str(tmp_path / "custom.yaml")) >>> resolution.path.name 'custom.yaml' >>> resolution.creates_new_default, resolution.ambiguous (False, False)
With no explicit path and no config in the home directory,
addfalls back to creating one in the current directory:>>> resolution = _resolve_config_file(None) >>> resolution.path.name '.vcspull.yaml' >>> resolution.creates_new_default True
-
vcspull.cli.add._declared_workspace_labels(config_file_path)¶vcspull.cli.add._declared_workspace_labels(config_file_path)¶
Return workspace root labels already declared in a config file.
Used to offer the workspace roots a user already keeps repositories under when adding by URL, where there is no parent directory to infer from. Duplicate labels collapse to their first occurrence, preserving file order.
- Parameters:
config_file_path (
pathlib.Path) – Config file to inspect. A missing or unreadable file yields[].- Returns:
Workspace root labels in the order they appear in the file.
- Return type:
Examples
A missing file has no declared roots:
>>> _declared_workspace_labels(tmp_path / "absent.yaml") []
Labels come back in file order, without duplicates:
>>> config_file = tmp_path / "declared.yaml" >>> _ = config_file.write_text( ... "~/code/:\n a: git+https://example.com/a.git\n" ... "~/study/:\n b: git+https://example.com/b.git\n", ... encoding="utf-8", ... ) >>> _declared_workspace_labels(config_file) ['~/code/', '~/study/']
-
vcspull.cli.add._build_ordered_items(top_level_items, raw_config)¶vcspull.cli.add._build_ordered_items(top_level_items, raw_config)¶
Return deep-copied top-level items preserving original ordering.
-
vcspull.cli.add._aggregate_from_ordered_items(items)¶vcspull.cli.add._aggregate_from_ordered_items(items)¶
Collapse ordered top-level items into a mapping grouped by label.
-
vcspull.cli.add._collapse_ordered_items_to_dict(ordered_items)¶vcspull.cli.add._collapse_ordered_items_to_dict(ordered_items)¶
Collapse ordered items into a flat dict for JSON serialization.
JSON does not support duplicate keys, so sections with the same workspace label are merged at the repo level via
dict.update()(last occurrence of a repo name wins).Examples
Distinct labels pass through unchanged:
>>> _collapse_ordered_items_to_dict([ ... {"label": "~/code/", "section": {"repo1": {"repo": "git+x"}}}, ... {"label": "~/work/", "section": {"repo2": {"repo": "git+y"}}}, ... ]) {'~/code/': {'repo1': {'repo': 'git+x'}}, '~/work/': {'repo2': {'repo': 'git+y'}}}
Duplicate labels are merged (repos from both sections appear):
>>> result = _collapse_ordered_items_to_dict([ ... {"label": "~/code/", "section": {"repo1": {"repo": "git+a"}}}, ... {"label": "~/code/", "section": {"repo2": {"repo": "git+b"}}}, ... ]) >>> sorted(result["~/code/"].keys()) ['repo1', 'repo2']
-
vcspull.cli.add._collect_duplicate_sections(items)¶vcspull.cli.add._collect_duplicate_sections(items)¶
Return mapping of labels to their repeated sections (>= 2 occurrences).
-
vcspull.cli.add._save_ordered_items(config_file_path, ordered_items)¶vcspull.cli.add._save_ordered_items(config_file_path, ordered_items)¶
Persist ordered items in the format matching the config file extension.
- Parameters:
config_file_path (
pathlib.Path) – Path to config file (.yaml or .json).ordered_items (
list of dict) – Each dict has"label"and"section"keys.
- Return type:
Examples
YAML output:
>>> import pathlib >>> config_file = tmp_path / "test.yaml" >>> items = [{"label": "~/code/", "section": {"myrepo": "git+https://example.com/r.git"}}] >>> _save_ordered_items(config_file, items) >>> config_file.read_text().strip() '~/code/...'
JSON output:
>>> config_file = tmp_path / "test.json" >>> _save_ordered_items(config_file, items) >>> import json >>> data = json.loads(config_file.read_text()) >>> "~/code/" in data True
-
vcspull.cli.add.handle_add_command(args)¶vcspull.cli.add.handle_add_command(args)¶
Entry point for the
vcspull addCLI command.
-
vcspull.cli.add.add_repo(name, url, config_file_path_str, path, workspace_root_path, dry_run, *, merge_duplicates=True, rev=None, shallow=False, depth=None)¶vcspull.cli.add.add_repo(name, url, config_file_path_str, path, workspace_root_path, dry_run, *, merge_duplicates=True, rev=None, shallow=False, depth=None)¶
Add a repository to the vcspull configuration.
- Parameters:
name (
str) – Repository name for the configurl (
str) – Repository URLconfig_file_path_str (
str|None) – Path to config file, or None to use defaultworkspace_root_path (
str|None) – Workspace root to use in configdry_run (
bool) – If True, preview changes without writingrev (
str|None) – Commit, tag, or branch to record asoptions.rev.shallow (
bool) – IfTrue, recordoptions.shallow: truefor the repository.depth (
int|None) – If set, recordoptions.depth: Nfor the repository.merge_duplicates (
bool)
- Return type: