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
20 changes: 18 additions & 2 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,25 @@ $ uvx --from 'vcspull' --prerelease allow vcspull

## vcspull v1.44.x (unreleased)

<!-- Maintainers, insert changes / features for the next release here -->
### Improvements

#### `vcspull add` streamlines path-first imports (#481)

- The CLI now requires a repository path as its positional argument, inferring
the name and `origin` remote automatically. Supply `--url` to record an
alternative remote and `--name` when you need a different label.
- Workspace roots default to the checkout's parent directory; use
`--workspace/--workspace-root` to override the destination while keeping the
path-first flow intact.
- CLI output contracts configuration paths to `~/.vcspull.yaml`, keeping
dry-run previews concise when configs live under the home directory.

### Development

#### Snapshot stability for CLI logs (#481)

_Upcoming changes will be written here._
- Discover command snapshots replace volatile line numbers with placeholders so
future refactors and lint rewrites do not break the test suite.

## vcspull v1.43.0 (2025-11-02)

Expand Down
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,18 @@ machines. Subsequent syncs of initialized repos will fetch the latest commits.

### Add repositories from the CLI

Register a single repository without touching YAML manually:
Register a single repository by pointing at the checkout:

```console
$ vcspull add my-lib https://github.com/example/my-lib.git --path ~/code/my-lib
$ vcspull add ~/projects/libs/my-lib
```

- Omit `--path` to default the entry under `./`.
- Use `-w/--workspace` when you want to force a specific workspace root, e.g.
`-w ~/projects/libs`.
- Pass `-f/--file` to add to an alternate YAML file.
- Use `--dry-run` to preview changes before writing.
- Point at an existing checkout (`vcspull add ~/projects/example`) to infer the
name and remote; add `--yes` to skip the confirmation prompt.
- vcspull infers the name from the directory and detects the `origin` remote.
Pass `--url` when you need to record a different remote.
- Override the derived name with `--name` and the workspace root with
`-w/--workspace`.
- `--dry-run` previews the update, while `--yes` skips the confirmation prompt.
- `-f/--file` selects an alternate configuration file.
- Append `--no-merge` if you prefer to review duplicate workspace roots
yourself instead of having vcspull merge them automatically.
- Follow with `vcspull sync my-lib` to clone or update the working tree after registration.
Expand Down
167 changes: 63 additions & 104 deletions docs/cli/add.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

# vcspull add

The `vcspull add` command adds a single repository to your vcspull configuration.
Provide a repository name and URL, and vcspull will append it to your config file
with the appropriate workspace root.
The `vcspull add` command registers a repository in your configuration by
pointing vcspull at a checkout on disk. The command inspects the directory,
merges duplicate workspace roots by default, and prompts before writing unless
you pass `--yes`.

```{note}
This command replaces the manual import functionality from `vcspull import`.
Expand All @@ -24,154 +25,112 @@ For bulk scanning of existing repositories, see {ref}`cli-discover`.

## Basic usage

Add a repository by name and URL:
Point to an existing checkout to add it under its parent workspace:

```console
$ vcspull add flask https://github.com/pallets/flask.git
Successfully added 'flask' to ./.vcspull.yaml under './'
$ vcspull add ~/study/python/pytest-docker
Found new repository to import:
+ pytest-docker (https://github.com/avast/pytest-docker)
• workspace: ~/study/python/
↳ path: ~/study/python/pytest-docker
? Import this repository? [y/N]: y
Successfully added 'pytest-docker' (git+https://github.com/avast/pytest-docker) to ~/.vcspull.yaml under '~/study/python/'.
```

By default, the repository is added to the current directory's workspace root (`./`).
The parent directory (`~/study/python/` in this example) becomes the workspace
root. vcspull shortens paths under `$HOME` to `~/...` in its log output so the
preview stays readable.

## Specifying workspace root
## Overriding detected information

Use `-w/--workspace` or `--workspace-root` to control where the repository will be checked out:
### Choose a different name

```console
$ vcspull add flask https://github.com/pallets/flask.git -w ~/code/
Successfully added 'flask' to ~/.vcspull.yaml under '~/code/'
```

All three flag names work identically:
Override the derived repository name with `--name` when the directory name
isn't the label you want stored in the configuration:

```console
$ vcspull add django https://github.com/django/django.git --workspace ~/code/
$ vcspull add requests https://github.com/psf/requests.git --workspace-root ~/code/
$ vcspull add ~/study/python/pytest-docker --name docker-pytest
```

## Custom repository path
### Override the remote URL

Override the inferred path with `--path` when the repository already exists on disk:
vcspull reads the Git `origin` remote automatically. Supply `--url` when you
need to register a different remote or when the checkout does not have one yet:

```console
$ vcspull add my-lib https://github.com/example/my-lib.git \
--path ~/code/libraries/my-lib
$ vcspull add ~/study/python/example --url https://github.com/org/example
```

The `--path` flag is useful when:
- Migrating existing local repositories
- Using non-standard directory layouts
- The repository name doesn't match the desired directory name

You can also point `vcspull add` at an existing checkout. Supplying a path such
as `vcspull add ~/projects/example` infers the repository name, inspects its
`origin` remote, and prompts before writing. Add `--yes` when you need to skip
the confirmation in scripts.

## Choosing configuration files

By default, vcspull looks for the first YAML configuration file in:
1. Current directory (`.vcspull.yaml`)
2. Home directory (`~/.vcspull.yaml`)
3. XDG config directory (`~/.config/vcspull/`)
URLs follow [pip's VCS format][pip vcs url]; vcspull inserts the `git+` prefix
for HTTPS URLs so the resulting configuration matches `vcspull fmt` output.

If no config exists, a new `.vcspull.yaml` is created in the current directory.
### Select a workspace explicitly

Specify a custom config file with `-f/--file`:
The workspace defaults to the checkout's parent directory. Pass
`--workspace`/`--workspace-root` to store the repository under a different
section:

```console
$ vcspull add vcspull https://github.com/vcs-python/vcspull.git \
-f ~/projects/.vcspull.yaml
$ vcspull add ~/scratch/tmp-project --workspace ~/projects/python/
```

## Dry run mode

Preview changes without modifying your configuration with `--dry-run` or `-n`:

```console
$ vcspull add flask https://github.com/pallets/flask.git -w ~/code/ --dry-run
Would add 'flask' (https://github.com/pallets/flask.git) to ~/.vcspull.yaml under '~/code/'
```

This is useful for:
- Verifying the workspace root is correct
- Checking which config file will be modified
- Testing path inference

## URL formats

Repositories use [pip VCS URL][pip vcs url] format with a scheme prefix:
## Confirmation and dry runs

- Git: `git+https://github.com/user/repo.git`
- Mercurial: `hg+https://bitbucket.org/user/repo`
- Subversion: `svn+http://svn.example.org/repo/trunk`

The URL scheme determines the VCS type. For Git, the `git+` prefix is required.

## Examples

Add to default location:

```console
$ vcspull add myproject https://github.com/myuser/myproject.git
```

Add to specific workspace:
`vcspull add` asks for confirmation before writing. Use `--yes` to skip the
prompt in automation, or `--dry-run`/`-n` to preview the changes without
modifying any files:

```console
$ vcspull add django-blog https://github.com/example/django-blog.git \
-w ~/code/django/
$ vcspull add ~/study/python/pytest-docker --dry-run
```

Add with custom path:
Dry runs still show duplicate merge diagnostics so you can see what would
change.

```console
$ vcspull add dotfiles https://github.com/myuser/dotfiles.git \
--path ~/.dotfiles
```
## Choosing configuration files

Preview before adding:
vcspull searches for configuration files in this order:

```console
$ vcspull add flask https://github.com/pallets/flask.git \
-w ~/code/ --dry-run
```
1. `./.vcspull.yaml`
2. `~/.vcspull.yaml`
3. `~/.config/vcspull/*.yaml`

Add to specific config file:
Specify a file explicitly with `-f/--file`:

```console
$ vcspull add tooling https://github.com/company/tooling.git \
-f ~/company/.vcspull.yaml \
-w ~/work/
$ vcspull add ~/study/python/pytest-docker -f ~/configs/python.yaml
```

## Handling duplicates

vcspull merges duplicate workspace sections by default so existing repositories
stay intact. When conflicts appear, the command logs what it kept. Prefer to
resolve duplicates yourself? Pass `--no-merge` to leave every section untouched
while still surfacing warnings.
vcspull merges duplicate workspace sections before writing so existing
repositories stay intact. When it collapses multiple sections, the command logs
a summary of the merge. Prefer to inspect duplicates yourself? Add
`--no-merge` to keep every section untouched.

## After adding repositories

After adding repositories, consider:

1. Running `vcspull fmt --write` to normalize and sort your configuration (see {ref}`cli-fmt`)
2. Running `vcspull list` to verify the repository was added correctly (see {ref}`cli-list`)
3. Running `vcspull sync` to clone the repository (see {ref}`cli-sync`)
1. Run `vcspull fmt --write` to normalize your configuration (see
{ref}`cli-fmt`).
2. Run `vcspull list` to verify the new entry (see {ref}`cli-list`).
3. Run `vcspull sync` to clone or update the working tree (see {ref}`cli-sync`).

## Migration from vcspull import

If you previously used `vcspull import <name> <url>`:
If you previously used `vcspull import <name> <url>`, switch to the path-first
workflow:

```diff
- $ vcspull import flask https://github.com/pallets/flask.git -c ~/.vcspull.yaml
+ $ vcspull add flask https://github.com/pallets/flask.git -f ~/.vcspull.yaml
+ $ vcspull add ~/code/flask --url https://github.com/pallets/flask.git -f ~/.vcspull.yaml
```

Changes:
- Command name: `import` → `add`
- Config flag: `-c` → `-f`
- Same functionality otherwise
Key differences:

- `vcspull add` now derives the name from the filesystem unless you pass
`--name`.
- The parent directory becomes the workspace automatically; use `--workspace`
to override.
- Use `--url` to record a remote when the checkout does not have one.

[pip vcs url]: https://pip.pypa.io/en/stable/topics/vcs-support/
6 changes: 3 additions & 3 deletions src/vcspull/_internal/config_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def _duplicate_tracking_construct_mapping(

for key_node, value_node in node.value:
construct = t.cast(
t.Callable[[yaml.nodes.Node], t.Any],
"t.Callable[[yaml.nodes.Node], t.Any]",
loader.construct_object,
)
key = construct(key_node)
Expand Down Expand Up @@ -289,7 +289,7 @@ def _load_yaml_with_duplicates(
try:
data = loader.get_single_data()
finally:
dispose = t.cast(t.Callable[[], None], loader.dispose)
dispose = t.cast("t.Callable[[], None]", loader.dispose)
dispose()

if data is None:
Expand All @@ -301,7 +301,7 @@ def _load_yaml_with_duplicates(
loaded = t.cast("dict[str, t.Any]", data)

duplicate_sections = {
t.cast(str, key): values
t.cast("str", key): values
for key, values in loader.top_level_key_values.items()
if len(values) > 1
}
Expand Down
Loading