# Changelog

To install the unreleased vcspull version, see [developmental releases](https://vcspull.git-pull.com/quickstart.html#developmental-releases).

[pip](https://pip.pypa.io/en/stable/):

```console
$ pip install --user --upgrade --pre vcspull
```

[pipx](https://pypa.github.io/pipx/docs/):

```console
$ pipx install \
    --suffix=@next \
    --pip-args '\--pre' \
    --force \
    'vcspull'
// Usage: vcspull@next sync [config]
```

[uv](https://docs.astral.sh/uv/):

```console
$ uv add vcspull --prerelease allow
```

[uvx](https://docs.astral.sh/uv/guides/tools/):

```console
$ uvx --from 'vcspull' --prerelease allow vcspull
```

## vcspull v1.59.x (unreleased)

<!-- KEEP THIS PLACEHOLDER - DO NOT REMOVE OR MODIFY THIS LINE -->
_Notes on upcoming releases will be added here_
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->

## vcspull v1.58.1 (2026-03-29)

### Bug fixes

#### `config`: Preserve symlinks when writing config files (#538)

Config files that are symbolic links (e.g. `~/.vcspull.yaml` pointing to a
dotfiles directory) were being silently replaced by regular files on every
write, destroying the symlink. The write now goes through the link — a temp
file is created next to the real target, renamed into place, and the symlink
directory entry is preserved.

Format detection now also inspects the symlink target's extension, so a
`.yaml` symlink pointing to a `.json` file serialises correctly as JSON rather
than overwriting the target with YAML.

## vcspull v1.58.0 (2026-03-01)

### New features

#### `vcspull import`: Add `--sync` and `--prune` (#520)

`--sync` fully reconciles your config with the remote: updates changed URLs,
and prunes entries no longer on the remote. `--prune` does prune-only (no URL
updates). Both respect pinned entries and use provenance tags
(`metadata.imported_from`) to scope pruning — manually added repos are never
removed.

```console
$ vcspull import gh myorg \
    --mode org \
    --workspace ~/code/ \
    --sync
```

Available on all six import providers. Pruning is config-only — cloned
directories on disk are not deleted. See {ref}`cli-import` for details.

#### `vcspull import`: Add `--prune-untracked` flag (#520)

`--prune-untracked` expands `--sync` / `--prune` to also remove config
entries in the target workspace that lack import provenance — repos added
manually or before provenance tracking. Entries imported from other sources
and pinned entries are preserved. A confirmation prompt lists exactly what
would be removed.

```console
$ vcspull import gh myorg \
    --workspace ~/code/ \
    --sync \
    --prune-untracked \
    --dry-run
```

#### `options.pin`: Per-repo, per-operation mutation guard (#520)

Any repository entry can carry an `options.pin` block that prevents specific
vcspull operations from mutating it. This is useful for pinned forks, company
mirrors, or any repo whose config should only be changed manually.

Pin all operations:

```yaml
~/code/:
  myrepo:
    repo: git+git@github.com:me/myrepo.git
    options:
      pin: true
      pin_reason: "pinned to company fork — update manually"
```

Pin only specific operations:

```yaml
~/code/:
  other-repo:
    repo: git+git@github.com:me/other.git
    options:
      pin:
        import: true    # --sync cannot replace this URL
        fmt: true       # vcspull fmt preserves the entry verbatim
      pin_reason: "pinned for import and fmt"
```

Shorthand — equivalent to `pin: {import: true}`:

```yaml
~/code/:
  shorthand-repo:
    repo: git+git@github.com:me/pinned.git
    options:
      allow_overwrite: false
```

Pin semantics:

- `pin: true` — all five operations are blocked (`import`, `add`, `discover`,
  `fmt`, `merge`)
- `pin: {op: true}` — only the named operations are blocked; unspecified keys
  default to `false`
- `allow_overwrite: false` — concise shorthand, equivalent to
  `pin: {import: true}`
- `pin_reason` — human-readable string shown in log output when a repo is
  skipped due to a pin

Pin awareness has been added to all config-mutation commands: `import`,
`add`, `discover`, `fmt`, and the duplicate-workspace-root `merge` resolver.

### Bug fixes

#### `vcspull add`, `vcspull discover`, `vcspull fmt`: Fix JSON configs saved as YAML (#520)

All three commands called `save_config_yaml()` unconditionally, silently
corrupting `.json` config files by overwriting them with YAML content. Each
save site now checks the file extension and calls the correct serialiser.

## vcspull v1.57.0 (2026-02-22)

### Breaking changes

#### `vcspull import gitlab`: `--with-shared` now defaults to excluded

Prior to this release, GitLab's group API returned shared projects by default.
vcspull now explicitly requests `with_shared=false`, so shared repositories are
excluded unless `--with-shared` is passed.

### New features

#### `vcspull import gitlab`: Add `--with-shared` and `--skip-group` flags

- `--with-shared`: Opt-in flag to include projects shared into a group from
  another namespace. Only applies in `--mode org`; a warning is emitted when
  used in user or search modes.
- `--skip-group <name>`: Client-side filter that excludes all repositories
  whose owner path contains the specified group name segment (case-insensitive,
  segment-based). Repeat the flag to skip multiple groups.

## vcspull v1.56.1 (2026-02-18)

### Bug fixes

#### `vcspull import`: Fix silent truncation of GitLab/GitHub results (#518)

Previously, `--limit 100` (the default) would silently discard repositories
beyond the cap with no indication that more were available.

- GitLab: Read `x-total` and `x-next-page` response headers to detect
  truncation and warn users
- GitHub search: Use `total_count` from the JSON body to detect truncation
- GitHub user/org: Detect mid-page limit hit as a "more available" signal
- All providers now warn when results are capped by `--limit`

#### `vcspull import`: Fix HTTP 429 rate-limit failures on large imports (#518)

Rate-limited API requests previously failed immediately with an unrecoverable
error. Large imports that triggered rate limits had to be manually restarted.

- Add automatic retry with exponential backoff (up to 3 attempts) on HTTP 429
- Honor the `Retry-After` response header when present (capped at 120 s)
- Fall back to exponential backoff with jitter when the header is absent

### New features

#### `vcspull import`: `--limit 0` means "no limit" (#518)

`--limit 0` now fetches every repository instead of raising a validation
error. This follows the common CLI convention where 0 means unlimited.

#### `vcspull import`: GitLab rate-limit header logging (#518)

GitLab `ratelimit-remaining` / `ratelimit-limit` headers are now logged
after each API request, matching the existing GitHub rate-limit logging.
A warning is emitted when fewer than 10 requests remain.

## vcspull v1.56.0 (2026-02-15)

### New features

#### New command: `vcspull worktree` (#508)

Declarative git worktree management. Configure worktrees per-repo in YAML
targeting tags, branches, or commits, then list, sync, and prune them.

**Configuration format:**

```yaml
~/repos/:
  myproject:
    repo: "git+https://github.com/user/project"
    worktrees:
      - dir: ../myproject-v1.0
        tag: v1.0.0
      - dir: ../myproject-dev
        branch: develop
      - dir: ../myproject-abc123
        commit: abc123
      - dir: ../myproject-stable
        tag: stable
        lock: true
        lock_reason: "Production reference - do not remove"
```

Each worktree entry requires a `dir` and exactly one ref type (`tag`, `branch`,
or `commit`). Optional fields: `lock` (boolean), `lock_reason` (string, implies
`lock: true`), and `detach` (boolean, defaults to true for tag/commit).

**Subcommands:**

Preview the plan for all configured worktrees:

```console
$ vcspull worktree list
```

Create or update worktrees to match configuration:

```console
$ vcspull worktree sync
```

Preview sync without making changes:

```console
$ vcspull worktree sync --dry-run
```

Remove worktrees not present in configuration:

```console
$ vcspull worktree prune
```

Preview prune without removing anything:

```console
$ vcspull worktree prune --dry-run
```

Filter by repository name pattern:

```console
$ vcspull worktree list "myproject"
```

Machine-readable output:

```console
$ vcspull worktree list --json
```

```console
$ vcspull worktree list --ndjson
```

**Key features:**

- Three ref types: `tag` (detached HEAD), `branch` (tracks upstream, pulls on
  sync), `commit` (detached HEAD at exact SHA)
- Path resolution: relative paths resolve from workspace root, absolute and
  tilde-prefixed paths are supported
- Lock support: `lock: true` passes `--lock` to `git worktree add`;
  `lock_reason` locks with a reason after creation
- Safety: dirty worktrees are reported as BLOCKED (never force-updated),
  missing refs are reported as ERROR
- Plan display: entries show CREATE / UPDATE / UNCHANGED / BLOCKED / ERROR
  status with colored symbols (`+`, `~`, `✓`, `⚠`, `✗`)
- Output modes: human-readable (default), `--json`, `--ndjson`
- Pattern filtering: all subcommands accept glob patterns to target specific
  repositories

#### `vcspull worktree`: Integration with `vcspull sync` (#508)

Sync repositories and their worktrees in one pass with
`--include-worktrees`:

```console
$ vcspull sync --all --include-worktrees
```

Preview everything without changes:

```console
$ vcspull sync --all \
    --include-worktrees \
    --dry-run
```

Worktree results are included in the sync summary and affect the exit code
when `--exit-on-error` is set.

### Bug fixes

- `vcspull discover` now correctly distinguishes git worktrees from
  submodules by inspecting the `.git` file target path (#508)
- Config reader: fix false duplicate-key detection in nested list mappings
  inside `_DuplicateTrackingSafeLoader` (#508)

### Internal / Development

- New exception hierarchy: `WorktreeError` base with `WorktreeConfigError`,
  `WorktreeRefNotFoundError`, and `WorktreeDirtyError` subtypes (#508)
- `WorktreeCheck` audit trail attached to every plan entry for
  debuggability (#508)
- Comprehensive worktree test suite covering planning, sync, prune, CLI
  handlers, and edge cases (#508)

## vcspull v1.55.0 (2026-02-15)

### New features

#### New command: `vcspull import` (#510)

Import repositories from GitHub, GitLab, Codeberg/Gitea/Forgejo, and AWS
CodeCommit directly into your vcspull configuration.

Import a user's repositories:

```console
$ vcspull import github torvalds \
    --workspace ~/repos/linux \
    --mode user
```

Import an organization's repositories:

```console
$ vcspull import github django \
    --workspace ~/study/python \
    --mode org
```

Search and import repositories:

```console
$ vcspull import github "machine learning" \
    --workspace ~/ml-repos \
    --mode search \
    --min-stars 1000
```

Use with self-hosted GitLab:

```console
$ vcspull import gitlab myuser \
    --workspace ~/work \
    --url https://gitlab.company.com
```

Import from AWS CodeCommit:

```console
$ vcspull import codecommit \
    --workspace ~/work/aws \
    --region us-east-1
```

Preview without writing (dry run):

```console
$ vcspull import codeberg user \
    --workspace ~/oss \
    --dry-run
```

**Key features:**

- Service aliases: `gh`, `gl`, `cb`, `cc`, `aws`
- Filtering: `--language`, `--topics`, `--min-stars`, `--archived`, `--forks`
- Output modes: human-readable (default), `--json`, `--ndjson`
- Interactive confirmation before writing; use `--yes`/`-y` to skip
- Repositories already in the config are detected and skipped
- Non-zero exit code on errors (for CI/automation)
- No new dependencies (uses stdlib `urllib` for HTTP)

#### `vcspull import`: SSH clone URLs by default (#510)

Clone URLs default to SSH. Use `--https` to get HTTPS URLs instead:

SSH (default):

```console
$ vcspull import github torvalds \
    --workspace ~/repos/linux \
    --mode user
```

Use `--https` for HTTPS clone URLs:

```console
$ vcspull import github torvalds \
    --workspace ~/repos/linux \
    --mode user \
    --https
```

#### `vcspull import`: GitLab subgroups map to workspace roots (#510)

For GitLab organization/group imports, subgroup namespaces are preserved
under the workspace root by default:

```console
$ vcspull import gitlab vcs-python-group-test \
    --workspace ~/projects/python \
    --mode org
```

This writes repositories into workspace sections like:

- `~/projects/python/`
- `~/projects/python/<subgroup>/`
- `~/projects/python/<subgroup>/<subsubgroup>/`

Use `--flatten-groups` to collapse subgroup repositories into a single
workspace root:

```console
$ vcspull import gitlab vcs-python-group-test \
    --workspace ~/projects/python \
    --mode org \
    --flatten-groups
```

### Bug fixes

- Config writes now use atomic temp-file-then-rename to prevent data loss
  during interrupted writes (#510)

### Tests

- Fix `pytest-asyncio` deprecation warning in isolated `pytester` runs by
  setting `asyncio_default_fixture_loop_scope` explicitly (#515)

## vcspull v1.54.0 (2026-02-08)

### Breaking changes

- Dry-run `--json` output is now a flat array of plan entries, matching
  the schema of `sync --json` and `status --json` (#514)

### Bug fixes

- Fix false "up to date" status for repos without upstream tracking (#514)
- Fix non-git repos being counted in dirty/clean summary totals (#514)
- Fix repos matched by multiple patterns being synced multiple times (#514)
- Fix SVN and Mercurial repos showing as BLOCKED in dry-run plan (#514)
- Fix misclassified tokens in colorized `--help` output (#514)

### Documentation

- Update sync examples in README and quickstart to use `--all` (#514)

### Tests

- Add SVN and Mercurial sync tests (#514)

## vcspull v1.53.0 (2026-02-08)

### Breaking changes

#### cli(sync): Require `--all` to sync all repositories (#511)

`vcspull sync` without arguments now shows help instead of silently
exiting. To sync every configured repository, use `--all` (or `-a`):

```console
$ vcspull sync --all
```

- Passing `--all` with positional patterns is an error (mutually exclusive)
- The `"*"` glob pattern still works as an alternative to `--all`

### What's new

- Subcommands (`sync`, `search`, `add`, `discover`) now print help when
  called without arguments (#511)

## vcspull v1.52.1 (2026-02-08)

### Tests

- Fix test failures in clean build environments without a global git
  identity (e.g. Arch Linux nspawn containers) (#513)

## vcspull v1.52.0 (2026-02-07)

### Breaking changes

- Bump minimum libvcs from v0.38.6 -> v0.39.0 (#512)

### Features

#### cli(sync): Detect and report errored syncs (#512)

`vcspull sync` now reports git fetch/pull failures as errors instead of
silently succeeding. The sync summary shows errored repos alongside
successful and failed counts.

#### cli(sync): Exit non-zero on unmatched patterns with `--exit-on-error` (#512)

When `--exit-on-error` is set and sync patterns match no configured
repositories, `vcspull sync` now exits with a non-zero status code.

#### cli(sync): Improved sync summary output (#512)

- Show unmatched patterns separately from repo totals
- Omit "0 previewed" from non-dry-run summaries

### Bug fixes

- Fix path-based sync patterns showing `"None"` instead of the actual
  search term (#512)
- Fix duplicate unmatched-pattern warnings in output (#512)
- Fix missing sync summary when all patterns are unmatched (#512)

### Tests

- Add errored sync, unmatched pattern, and cross-VCS regression tests (#512)

## vcspull v1.51.1 (2026-02-01)

### Tests

- Fix snapshot test compatibility with syrupy 4.x (#509, #503)

## vcspull v1.51.0 (2026-01-27)

### Breaking changes

- Bump minimum libvcs from v0.38.3 -> v0.38.6 (#506)

### Documentation

- CLI reference arguments now have permalink anchors (#502)
- Fix doc build warnings from glob patterns in help text (#502)

### Bug fixes

- Fix snapshot test compatibility for downstream packaging (#504, #503)

## vcspull v1.50.1 (2026-01-25)

### Breaking changes

- Bump minimum libvcs from v0.38.2 -> v0.38.3 (#505)

## vcspull v1.50.0 (2026-01-24)

### Breaking changes

- Bump minimum libvcs from v0.38.1 -> v0.38.2 (#501)

### Packaging / CI

- Migrate to PyPI Trusted Publisher (#492)

### Documentation

#### Replace sphinx-argparse with internal extension (#499)

sphinx-argparse broke with Sphinx 9.x due to internal API changes. This release
replaces it with `sphinx_argparse_neo`, a local extension with better architecture.

**Changes:**

- Remove `sphinx-argparse` dependency
- Uncap `sphinx` version (now `>=8` instead of `<9`)
- Add custom Pygments lexers for CLI output highlighting
- Add syntax highlighting to argparse directive HTML output

**Directive options:**

- `:no-defaults:`, `:no-description:`, `:no-epilog:`, `:no-choices:`, `:no-types:`
- sphinx-argparse compatibility: `nosubcommands`, `nodefault`, `nodescription`, `noepilog`
- `:path:` for subparser navigation
- `:mock-modules:` for mocking imports during introspection

### Development

- Add `types-docutils` and `types-Pygments` for type checking
- Clarify CLAUDE.md import style guidelines (stdlib vs third-party)

## vcspull v1.49.0 (2026-01-03)

### New features

#### New command: `vcspull search` (#494)

Search across all configured repositories using ripgrep-inspired syntax with field-scoped queries, regex patterns, and flexible output formats.

**Basic usage:**

Search for a term across all fields:

```console
$ vcspull search django
```

Search by repository name:

```console
$ vcspull search "name:flask"
```

Search by URL:

```console
$ vcspull search "url:github.com"
```

**Key features:**

- **Field-scoped queries**: Target specific fields with `name:`, `url:`, `path:`, or `vcs:` prefixes
- **Regex patterns**: Full regex support with `-i/--ignore-case`, `-S/--smart-case`, `-F/--fixed-strings`, and `--word-regexp`
- **Boolean logic**: AND (default), OR (`--any`), and inverted matching (`-v/--invert-match`)
- **Context display**: Show matching fields with `--field` filtering
- **Output formats**: Human-readable (default), `--json`, or `--ndjson` for automation
- **Color control**: `--color {auto,always,never}` with `NO_COLOR` support

**Advanced examples:**

Case-insensitive search across all fields:

```console
$ vcspull search -i DJANGO
```

Find repos by VCS type:

```console
$ vcspull search "vcs:git"
```

Match any term (OR logic):

```console
$ vcspull search --any flask django requests
```

Invert match (exclude repos):

```console
$ vcspull search -v "url:gitlab"
```

JSON output for scripting:

```console
$ vcspull search --json "name:lib"
```

### Development

#### Makefile -> Justfile (#493)

- Migrate from `Makefile` to `justfile` for running development tasks
- Update documentation to reference `just` commands

### Documentation

- Migrate docs deployment to AWS OIDC authentication and AWS CLI

## vcspull v1.48.1 (2025-12-06)

### Breaking changes

- Bump minimum libvcs from v0.38.0 -> v0.38.1 (#491)

### Documentation

#### Fix Sphinx autodoc indentation errors (#490)

- Configure `sphinx-autodoc-typehints` to prevent RST parsing conflicts with
  Napoleon-processed docstrings by disabling type injection into docstring
  bodies (`always_document_param_types`, `typehints_document_rtype`).
- Suppress cosmetic forward reference warnings from TYPE_CHECKING imports.

_Upcoming changes will be written here._

## vcspull v1.48.0 (2025-11-30)

### Breaking changes

- Bump minimum libvcs from v0.37.0 -> v0.38.0 (#489)

### Bug fixes

#### Path privacy improvements (#488)

Path privacy added to:

- `vcspull fmt`: summary banners, success logs, and `--all` listings cache the
  `PrivatePath` display value so config paths always render as `~/.vcspull.yaml`.
- `vcspull add`: invalid-config errors reuse the redacted path, keeping failure
  logs consistent with other config operations.
- `vcspull discover`: warnings for repos without remotes and "no repos found"
  notices collapse their target directories via `PrivatePath`.
- `vcspull sync`: human "Synced …" lines now mirror the structured JSON payloads
  by showing tilde-collapsed repository paths.

## vcspull v1.47.0 (2025-11-15)

### Bug Fixes

#### `vcspull discover`: Success logs redact config paths (#487)

Writes triggered by `vcspull discover` now pass the config file through
`PrivatePath`, so confirmations like "✓ Successfully updated ~/.vcspull.yaml"
and dry-run notices no longer leak the absolute home directory.

#### `vcspull discover`: Fix another workspace dir case (#487)

Discover now inspects the config scope (user/system/project) before writing,
so user-level configs like `~/.vcspull.yaml` prefer tilde-prefixed workspace
keys while project-level configs keep their relative `./` sections. Tests
cover both behaviors to guard against regressions.

### Development

#### PrivatePath centralizes home-directory redaction (#485)

- Introduced a dedicated `PrivatePath` helper for all CLI logging and structured
  output, ensuring tilde-collapsed paths stay consistent without duplicating the
  contraction logic across commands.

## vcspull v1.46.1 (2025-11-03)

### Bug Fixes

#### Fix `vcspull.__version__`

- v1.46.0 shipped with v1.45.0

#### `vcspull add`: Fix legacy `./` heuristics for path-first flow (#484)

- Path-based imports now always record their workspaces using the tilde label
  unless callers explicitly pass `--workspace ./`, avoiding stray top-level
  `./` sections.
- Duplicate-aware writes operate directly on the ordered loader output, keeping
  repeated workspace roots intact while removing the old `config_items`
  scaffolding.
- CLI decisions no longer round-trip through `normalize_workspace_roots`
  unless duplicate merges are needed, reducing the chances of accidental YAML
  rewrites during `--no-merge` operations.

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

### Bug Fixes

#### `vcspull add` keeps tilde workspace labels in path-first mode (#482, #483)

- Running `vcspull add ~/study/python/…` from inside the workspace now logs the
  workspace as `~/study/python/` instead of `./`, preserving tilde-shortened
  labels for path-first imports.

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

### Bug Fixes

#### `vcspull add --no-merge` preserves duplicate workspace roots (#482)

- Duplicate workspace sections are no longer flattened when adding a repository
  with `--no-merge`; all previously configured repositories stay intact.
- The configuration loader now exposes ordered duplicate entries so CLI writes
  can target the correct section without data loss.

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

### 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)

- 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)

### Improvements

#### `vcspull fmt` gains duplicate root merging (#479)

- Running `vcspull fmt` now consolidates repeated workspace sections into one
  merged block so no repositories vanish during cleanup; pass `--no-merge` if
  you want the command to surface duplicates without rewriting them.

#### `vcspull add` protects workspace configs during imports (#480)

- Default behavior now merges duplicate workspace roots so prior entries stay
  intact; add `--no-merge` to keep the raw sections and handle them yourself.
- You can invoke `vcspull add ~/study/python/project`; the command inspects the
  path, auto-fills the `origin` remote, shows the tilde-shortened workspace, and
  asks for confirmation unless you supply `--yes`.
- CLI previews now contract `$HOME` to `~/…`, matching the rest of the UX.

#### `vcspull discover` honors --no-merge (#480)

- Running `vcspull discover --no-merge` only reports duplicates—it leaves the
  file untouched until you decide to act.

#### Configuration loader: Support for duplicate workspace roots (#480)

- Commands backed by `load_configs` (`list`, `status`, `sync`, etc.)
  automatically keep every repository even when workspace sections repeat; pass
  `merge_duplicates=False` to fall back to the legacy "last entry wins"
  behavior with a warning.

### Development

#### Snapshot coverage for formatter tests (#479)

- Formatter scenarios are checked against [Syrupy] JSON/YAML snapshots, making it
  obvious when future changes alter the merged output or log text.

#### CLI `add` path-mode regression tests (#480)

- Parameterized pytest scenarios cover interactive prompts, duplicate merging,
  and path inference to keep the redesigned workflow stable.

[syrupy]: https://github.com/syrupy-project/syrupy

## vcspull v1.42.0 (2025-11-01)

### Breaking changes

- Bump minimum libvcs from v0.36.0 -> v0.37.0
- Drop support for Python 3.9; the new minimum is Python 3.10 (#478)

  See also:
  - [Python 3.9 EOL timeline](https://devguide.python.org/versions/#:~:text=Release%20manager-,3.9,-PEP%20596)
  - [PEP 596](https://peps.python.org/pep-0596/)

## vcspull v1.41.0 (2025-10-25)

### Breaking Changes

#### JSON/NDJSON paths now use tilde notation

**All commands** (`list`, `status`, `sync`) now contract home directory paths in JSON/NDJSON output for consistency, privacy, and portability.

**Before:**
```json
{
  "name": "flask",
  "path": "/home/username/code/flask",
  "workspace_root": "~/code/"
}
```

**After:**
```json
{
  "name": "flask",
  "path": "~/code/flask",
  "workspace_root": "~/code/"
}
```

**Why:** The `workspace_root` field already used tilde notation in JSON output, creating an inconsistency. Full home paths (`/home/username/`) expose usernames and make output less portable between machines.

**Impact:** Automation consuming JSON/NDJSON output will need to expand `~` to absolute paths if required. Most tools handle tilde expansion automatically.

### Bug Fixes

- Defer imports of `ConfigDict` to `TYPE_CHECKING` blocks so CLI modules never import `typing_extensions` at runtime (#476).

  vcspull treats `typing-extensions` as a dev-only dependency, so avoiding those imports fixes crashes when the package isn’t installed in production environments and still keeps type information intact for static analysis.

## vcspull v1.40.0 (2025-10-19)

### Performance Improvements

#### Concurrent status checking (#474)

- **`vcspull status`**: Now checks repositories concurrently using asyncio
  - Dramatically faster when checking many repositories (5-10x speedup for 20+ repos)
  - Live progress display on TTY: `Progress: 5/10 ✓:3 ✗:2`
  - New `--max-concurrent N` flag to control concurrency limit (default: 32)
  - New `--no-concurrent`/`--sequential` flag to disable concurrent mode
  - Duration tracking included in JSON/NDJSON output (`duration_ms` field)
  - Uses same asyncio pattern as `vcspull sync --dry-run`
  - Backward compatible with sequential fallback option

## vcspull v1.39.1 (2025-10-19)

### Improvements

#### Improved home directories paths (#473)

- **Human-readable output**: Contract `$HOME` to `~/` in introspection commands
  - `vcspull list`, `vcspull status`, and `vcspull sync --dry-run` now display
    paths like `~/code/flask` instead of `/home/username/code/flask`
  - Reduces verbosity and avoids exposing usernames in terminal output
  - JSON/NDJSON output preserves full absolute paths for accuracy in automation
- **Documentation**: Updated all examples to use `~/` for consistency

## vcspull v1.39.0 (2025-10-19)

### Breaking Changes

This release modernizes the vcspull CLI to align with DevOps tool conventions (Terraform, Cargo, Ruff, Biome). **This is a breaking change release**.

#### Command Changes (#472)

- **REMOVED**: `vcspull import` command
  - Replaced by `vcspull add <name> <url>` to add a single repository
  - Replaced by `vcspull discover <path>` to scan and add multiple repositories
- **NEW**: `vcspull list` - List configured repositories with optional `--tree`, `--json`, `--ndjson` output
- **NEW**: `vcspull status` - Check repository health (clean/dirty status, ahead/behind tracking with `--detailed`)

#### Flag Changes (#472)

- **RENAMED**: `-c/--config` → `-f/--file` (all commands)
- **NEW**: `-w/--workspace/--workspace-root` - All three aliases supported for workspace root
- **NEW**: `--dry-run/-n` - Preview changes without making modifications (sync, add, discover)
- **NEW**: `--json/--ndjson` - Machine-readable output for automation (sync, list, status)
- **NEW**: `--color {auto,always,never}` - Control color output

#### Migration Guide (#472)

```
# Old → New
vcspull import NAME URL              → vcspull add NAME URL
vcspull import --scan DIR            → vcspull discover DIR
vcspull sync -c FILE                 → vcspull sync -f FILE
vcspull sync --workspace-root PATH   → vcspull sync -w PATH  # (or keep long form)
vcspull fmt -c FILE                  → vcspull fmt -f FILE
```

### Features

#### Developer Experience Improvements (#472)

- Action commands (`sync`, `add`, `discover`) support `--dry-run` for safe previewing of changes
- Structured output (`--json`, `--ndjson`) enables CI/CD integration and automation
- Semantic colors with `NO_COLOR` environment variable support
- Short `-w` flag for workspace root reduces typing
- Consistent flag naming across all commands
- `vcspull sync --dry-run` renders a Terraform-style plan (with live progress on
  TTYs) and exposes the same data via a stable JSON/NDJSON schema for automation

#### New Introspection Commands (#472)

- `vcspull list` - View all configured repositories
  - `--tree` mode groups by workspace root
  - `--json/--ndjson` for programmatic access
- `vcspull status` - Check repository health
  - Shows which repos exist, are clean/dirty, or missing
  - `--detailed` mode shows branch, ahead/behind tracking, and full paths

## vcspull v1.38.0 (2025-10-18)

### Improvements

#### Improved help output for `--help`

- Align CLI help output with CPython’s argparse theming by adding a dedicated
  formatter that colorizes example sections for top-level and subcommand help
  screens (#471).
- Expand `vcspull --help` to include additional example commands for the `sync`,
  `import`, and `fmt` subcommands, giving users clearer quick-start guidance (#471).
- Keep CLI logger discovery stable while refactoring the formatter into its own
  module, preventing additional loggers from surfacing in downstream tools (#471).

## vcspull v1.37.0 (2025-10-18)

### Breaking changes

- Rename the import CLI flag `--base-dir-key`/`--dir` and the related internal
  argument names to `--workspace-root`, aligning both user-facing options and
  implementation terminology with the workspace-root concept (#470).

### Bug fixes

- Normalize workspace roots during imports so equivalent labels (for example
  `~/foo`, `~/foo/`, `/home/user/foo`) collapse into a single canonical entry;
  avoids duplicate "already added" prompts (#470).
- Have `vcspull fmt` apply the same normalization before formatting and writing
  configs, so duplicate workspace-root sections are removed automatically (#470).

### Development

- Expand CLI tests to cover mixed workspace-root scenarios and the formatter’s
  normalization behavior (#470).

## vcspull v1.36.0 (2025-10-18)

### New features

#### New command: `vcspull import` (#465)

- **Manual import**: Register a single repository with `vcspull import <name> <url>`
  - Optional `--dir`/`--path` helpers for base-directory detection
- **Filesystem scan**: Discover and import existing repositories with `vcspull import --scan <dir>`
  - Recursively scan with `--recursive`/`-r`
  - Interactive confirmation prompt or `--yes` for unattended runs
  - Custom base directory with `--base-dir-key`

#### New command: `vcspull fmt` (#465)

- Normalize configuration files by expanding compact entries to `{repo: ...}`, sorting directories/repos, and standardizing keys; pair with `--write` to persist the formatted output.

### Improvements

- Enhanced logging system with better CLI module propagation and StreamHandler configuration for improved output visibility in tests and CLI usage (#465).

### Development

- Add Python 3.14 to test matrix, trove classifiers (#469)

## vcspull v1.35.0 (2025-09-04)

### Development

- libvcs 0.35.1 -> 0.36.0 (#467)

  Improved Git URL detection

## vcspull v1.34.1 (2025-06-21)

### Development

- libvcs 0.35.0 -> 0.35.1 (#466)

  Fixes issue with console streaming output, e.g. `git clone` progress.

## vcspull v1.34.0 (2025-02-22)

### Development

#### libvcs: 0.34.0 -> 0.35.0 (#461)

Internally, this libvcs will use `text=True` in subprocess, and deal only with
unicode. Before this change, handling bytestrings was supported.

#### chore: Implement PEP 563 deferred annotation resolution (#459)

- Add `from __future__ import annotations` to defer annotation resolution and reduce unnecessary runtime computations during type checking.
- Enable Ruff checks for PEP-compliant annotations:
  - [non-pep585-annotation (UP006)](https://docs.astral.sh/ruff/rules/non-pep585-annotation/)
  - [non-pep604-annotation (UP007)](https://docs.astral.sh/ruff/rules/non-pep604-annotation/)

For more details on PEP 563, see: https://peps.python.org/pep-0563/

## vcspull v1.33.0 (2024-11-23)

_Maintenance only, no bug fixes, or new features_

### Breaking changes

#### Project and package management: poetry to uv (#457)

[uv] is the new package and project manager for the project, replacing Poetry.

[uv]: https://github.com/astral-sh/uv

#### Build system: poetry to hatchling (#457)

[Build system] moved from [poetry] to [hatchling].

[Build system]: https://packaging.python.org/en/latest/tutorials/packaging-projects/#choosing-a-build-backend
[poetry]: https://github.com/python-poetry/poetry
[hatchling]: https://hatch.pypa.io/latest/

#### libvcs 0.33.0 -> 0.34.0 (#456)

First libvcs release built with `uv` package and project manager. No changes to libvcs API itself.

## vcspull v1.32.0 (2024-10-13)

_Maintenance only, no bug fixes or new features_

### New features

#### Python 3.13 support

Added Python 3.13 to package trove classifiers and CI tests.

#### libvcs 0.30.1 -> 0.33.0 (#453)

Faster, cached pytest fixtures. Upstream tests are 50% faster.

- https://github.com/vcs-python/libvcs/blob/v0.32.1/CHANGES#libvcs-0310-2024-10-12
- https://libvcs.git-pull.com/history.html#libvcs-0-32-1-2024-10-12

#### Faster tests fixtures (#453)

libvcs's included test fixtures beyond v0.31.0+ are 33%+ faster.

## vcspull v1.31.0 (2024-06-18)

### Breaking changes

- libvcs: 0.29.0 -> 0.30.1 (#449, #450)

  [AWS CodeCommit] support. Example:

  ```yaml
  ~/study/aws-codecommit:
  test:
    repo: "codecommit::us-east-1://test"
  ```

[AWS CodeCommit]: https://docs.aws.amazon.com/codecommit/

### Documentation

- Automatically linkify links that were previously only text.

### Development

- poetry: 1.8.1 -> 1.8.2

  See also: https://github.com/python-poetry/poetry/blob/1.8.2/CHANGELOG.md

- Code quality: Use f-strings in more places (#445)

  via [ruff 0.4.2](https://github.com/astral-sh/ruff/blob/v0.4.2/CHANGELOG.md).

## vcspull v1.30.0 (2024-03-24)

_Maintenance only, no bug fixes or new features_

### Development

- Aggressive automated lint fixes via `ruff` (#442)

  via ruff v0.3.4, all automated lint fixes, including unsafe and previews were applied:

  ```console
  $ ruff check --select ALL . --fix --unsafe-fixes --preview --show-fixes; ruff format .
  ```

  Branches were treated with:

  ```console
  $ git rebase \
      --strategy-option=theirs \
      --exec 'poetry run ruff check --select ALL . --fix --unsafe-fixes --preview --show-fixes; poetry run ruff format .; git add src tests; git commit --amend --no-edit' \
      origin/master
  ```

## vcspull v1.29.0 (2024-03-24)

_Maintenance only, no bug fixes or new features_

### Breaking changes

- libvcs: 0.28.2 -> 0.29.0 (#443)

  Internal refactorings and maintenance.

### Development

- poetry: 1.7.1 -> 1.8.1

  See also: https://github.com/python-poetry/poetry/blob/1.8.1/CHANGELOG.md

- ruff 0.2.2 -> 0.3.0 (#441)

  Related formattings. Update CI to use `ruff check .` instead of `ruff .`.

  See also: https://github.com/astral-sh/ruff/blob/v0.3.0/CHANGELOG.md

## vcspull v1.28.1 (2024-02-17)

### Fixes

- libvcs: 0.28.0 -> 0.28.1 (#440)

  Fixes `'--max-count': not an integer` issue when syncing git repos.

### Testing

- CI: Bump actions for Node 20 (#439)

## vcspull v1.28.0 (2024-02-07)

_Maintenance only, no bug fixes or new features_

### Breaking changes

- libvcs: 0.27.0 -> 0.28.0 (#437)

  QueryList generic typing improvements.

## vcspull v1.27.0 (2024-02-07)

_Maintenance only, no bug fixes, or new features_

### Breaking changes

- libvcs: v0.26.0 -> v0.27.0 (#435)

  Renamings of `dir` to `path`.

- Fix shadowing of python builtins

  - `dir` -> `path` (#435)

### Development

- Strengthen linting (#436)

  - Add flake8-commas (COM)

    - https://docs.astral.sh/ruff/rules/#flake8-commas-com
    - https://pypi.org/project/flake8-commas/

  - Add flake8-builtins (A)

    - https://docs.astral.sh/ruff/rules/#flake8-builtins-a
    - https://pypi.org/project/flake8-builtins/

  - Add flake8-errmsg (EM)

    - https://docs.astral.sh/ruff/rules/#flake8-errmsg-em
    - https://pypi.org/project/flake8-errmsg/

### Documentation

- Refactor API docs to split across multiple pages (#431)
- Remove unused reStructuredText section headers from some modules (#431)

## vcspull v1.26.0 (2023-12-16)

_Maintenance only, no bug fixes, or new features_

### CI

- ci: Add pydocstyle rule to ruff (#428)

### Documentation

- Add docstrings to functions, methods, classes, and packages (#428)

## vcspull v1.25.0 (2023-11-26)

### Packaging

- libvcs 0.25.1 -> 0.26.0: Minor bug fixes, docstring updates

### CI

- Move CodeQL from advanced configuration file to GitHub's default

## vcspull v1.24.1 (2023-11-24)

### Packaging

- libvcs 0.25.0 -> 0.25.1 (maintenance release)

## vcspull v1.24.0 (2023-11-19)

_Maintenance only, no bug fixes, or new features_

### Packaging

- libvcs 0.24.0 -> 0.25.0 (maintenance release)
- Add Python 3.12 to trove classifiers
- Poetry: 1.6.x -> 1.7.0

  See also: https://github.com/python-poetry/poetry/blob/1.7.0/CHANGELOG.md

- Packaging (poetry): Fix development dependencies

  Per [Poetry's docs on managing dependencies] and `poetry check`, we had it wrong: Instead of using extras, we should create these:

  ```toml
  [tool.poetry.group.group-name.dependencies]
  dev-dependency = "1.0.0"
  ```

  Which we now do.

  [Poetry's docs on managing dependencies]: https://python-poetry.org/docs/master/managing-dependencies/

### Development

- Move formatting from `black` to [`ruff format`] (#427)

  This retains the same formatting style of `black` while eliminating a
  dev dependency by using our existing rust-based `ruff` linter.

  [`ruff format`]: https://docs.astral.sh/ruff/formatter/

- CI: Update action packages to fix warnings

  - [dorny/paths-filter]: 2.7.0 -> 2.11.1

  [dorny/paths-filter]: https://github.com/dorny/paths-filter

## vcspull v1.23.0 (2023-10-22)

### Breaking changes

- libvcs: Bumped from 0.22.2 -> 0.24.0 (#419)

### Bug fixes

- Git Remote URLs: Fix bug that would cause git remotes with `@` to be chopped off after the
  protocol (#419, fixes #425)

### Development

- Refactor of two testsuites to used `NamedTuple` parametrization (#423):

  - test_config_variations
  - test_updating_remote

## vcspull v1.22.0 (2023-09-02)

_Maintenance only, no bug fixes, or new features_

### Development

- Code quality improved via [ruff] rules (#417)

  This includes fixes made by hand, and with ruff's automated fixes. With
  the expanded set of linting rules (which include import sorting) - ruff runs are
  still instant when checking the whole codebase.

## vcspull v1.21.1 (2023-05-28)

_Maintenance only, no bug fixes, or new features_

### Development

- Add back `black` for formatting

  This is still necessary to accompany `ruff`, until it replaces black.

## vcspull v1.21.0 (2023-05-27)

_Maintenance only, no bug fixes or features_

### Internal improvements

- Move formatting, import sorting, and linting to [ruff].

  This rust-based checker has dramatically improved performance. Linting and
  formatting can be done almost instantly.

  This change replaces black, isort, flake8 and flake8 plugins.

- libvcs: 0.21.1 -> 0.21.2 (addition of ruff)
- poetry: 1.4.0 -> 1.5.0

  See also: https://github.com/python-poetry/poetry/releases/tag/1.5.0

[ruff]: https://ruff.rs

## vcspull v1.20.3 (2023-05-14)

### Bug fix

- Refactor `.types` imports to not require `typing-extensions` on production
  builds (fixes #412 via #414, thank you for the report @Jasha10)

## vcspull v1.20.2 (2023-04-07)

_Maintenance only, no bug fixes or features_

### Development

- Update mypy to 1.2.0
- libvcs: 0.21.1 -> 0.21.2

  Typing update only

## vcspull v1.20.1 (2023-03-15)

- libvcs: 0.21.0 -> 0.21.1

  This removes even more `typing-extensions` runtime dependencies from
  production builds.

## vcspull v1.20.0 (2023-03-15)

### Bug fix

- libvcs: 0.20.0 -> 0.21.0

  This removes the `typing-extensions` requirement from production builds.

## vcspull v1.19.0 (2022-12-31)

### Internal

- mypy: Add `--strict` typings (#386)

## vcspull v1.18.0 (2022-10-31)

### Python 3.11 support (#409)

- Bump libvcs 0.19.1 -> 0.20.0

  Adds python 3.11 support

## vcspull v1.17.1 (2022-10-23)

**Maintenance release, no features or fixes**

### Internal

- Bump libvcs 0.19.0 -> 0.19.1

  Doc fixes and test improvements

## vcspull v1.17.0 (2022-10-23)

**Maintenance release, no features or fixes**

### Internal

- Bump libvcs 0.18.1 -> 0.19.0 (#408)

  Refactor of sync and commands. Syncing now uses commands instead of invoking
  directly through `run()`.

## vcspull v1.16.0 (2022-10-23)

**Maintenance release, no features or fixes**

### Internal

- `sync()`: Rename `repo_terms` to `repo_patterns` (#406)
- Bump libvcs 0.17.0 to 0.18.1

  - 0.18.0 includes support for URL Matcher weights

## vcspull v1.15.8 (2022-10-16)

### CLI

- Doc fix for sphinx-argparse CLI generation

## vcspull v1.15.7 (2022-10-16)

### CLI

- `vcspull` and `vcspull sync` Copy updates and metavar updates (#404)

## vcspull v1.15.6 (2022-10-16)

### CLI

- `vcspull`: Add usage examples to CLI output

## vcspull v1.15.5 (2022-10-16)

### CLI

- `vcspull sync`: Fix showing of help when no arguments passed (#405)

## vcspull v1.15.4 (2022-10-16)

### CLI

- File completions for `-c` / `--config` files (#403)

  After updating, you can re-run [shtab]'s setup (see [completions page]) completion of:

  ```console
  $ vcspull sync -c [tab]
  ```

  ```console
  $ vcspull sync --config [tab]
  ```

[completions page]: https://vcspull.git-pull.com/cli/completion.html
[shtab]: https://docs.iterative.ai/shtab/

### Documentation

- Fix readme example for syncing repositories

## vcspull v1.15.3 (2022-10-09)

### Documentation

- Updates for migrations page
- Add back page on completions

### CLI

- Capitalization fixes and help text updates

## vcspull v1.15.2 (2022-10-08)

### Packaging

- Add missing pyyaml dependency (needed after #397's transition to `ConfigReader`)

## vcspull v1.15.1 (2022-10-02)

### Bug fix

- Fix help text for `vcspull sync` arguments and flags

## vcspull v1.15.0 (2022-10-02)

**Maintenance release, no features or fixes**

### Internal

- Move from click to {mod}`argparse` (#400)

### Packaging

- Drop click dependency (#400)

## vcspull v1.14.0 (2022-10-01)

**Maintenance release, no features or fixes**

### Internal

- Move test fixtures over to libvcs's pytest plugin (#398)
- Move conftest.py to root directory (#399)
- Add `ConfigReader`: Our clean, typed parser for raw strings and files (#397)

  This is our shiny, new, 200-line, doctested and typed parser that replaces `kaptan`.

### Packaging

- Drop kaptan dependency (#397)

## vcspull v1.13.0 (2022-09-25)

### What's new

- Refreshed logo
- `vcspull sync`:

  - Empty command will now show help output

    ```console
    $ vcspull sync
    Usage: vcspull sync [OPTIONS] [REPO_TERMS]...

    Options:
    -c, --config PATH Specify config
    -x, --exit-on-error Exit immediately when encountering an error syncing
    multiple repos
    -h, --help Show this message and exit.
    ```

    To achieve the equivalent behavior of syncing all repos, pass `'*'`:

    ```console
    $ vcspull sync '*'
    ```

    Depending on how shell escaping works in your shell setup with [wild card / asterisk], you may not need to quote `*`.

    [wild card / asterisk]: https://tldp.org/LDP/abs/html/special-chars.html#:~:text=wild%20card%20%5Basterisk%5D.

  - Terms with no match in config will show a notice (#394)

    > No repo found in config(s) for "non_existent_repo"

  - Syncing will now skip to the next repos if an error is encountered

  - Learned `--exit-on-error` / `-x`

    Usage:

    ```console
    $ vcspull sync --exit-on-error grako django
    ```

    Print traceback for errored repos:

    ```console
    $ vcspull --log-level DEBUG sync --exit-on-error grako django
    ```

  - Syncing in git repositories with untracked files has been improved (via
    libvcs 0.17)

    See also https://github.com/vcs-python/libvcs/pull/425

### Development

- Move to `src/` directory structure (#382)
- libvcs: Update to 0.17.x (#373)
- mypy:

  - Basic mypy annotations (#373)
  - [`mypy --strict`] compliant (#386)

  [`mypy --strict`]: https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-strict

- Remove `.pre-commit-config.yaml`: Let's not automate what the contributor could /
  should do themselves.
- Add [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) (#379)
- Add [flake8-comprehensions](https://github.com/adamchainz/flake8-comprehensions) (#380)

### Testing

- Add CLI tests (#387)

### Documentation

- Render changelog in sphinx-autoissues (#378)

### Bug fixes

- Fix cloning of mercurial repositories

### Infrastructure

- CI speedups (#383)

  - Split out release to separate job so the PyPI Upload docker image isn't pulled on normal runs
  - Clean up CodeQL

- Upgraded poetry 1.1.x to 1.2.x

### Packaging

- Remove `MANIFEST.in`

  Redundant since poetry has pyproject.toml with `include`

- Remove unused `.tmuxp-before-script.sh`, which was used as a `before_script`
  in `.tmuxp.yaml`
- Move `.coveragerc` into `pyproject.toml` (#384)

## vcspull 1.12.3 (2022-06-01)

### Bug fixes

- Fix cloning of mercurial repositories (backported fix from libvcs 0.13.x)

## vcspull 1.12.2 (2022-05-15)

### Packaging

- Update tags and description

### Documentation

- Update README

## vcspull 1.12.1 (2022-05-02)

### Bug fixes

- Pin [click at
  8+](https://click.palletsprojects.com/en/8.0.x/changes/#version-8-0-0)

## vcspull 1.12.0 (2022-04-24)

Patch branch: [`v1.12.x`](https://github.com/vcs-python/vcspull/tree/v1.12.x)

### Breaking changes

- Config location uses `XDG_CONFIG_HOME` from [XDG Base Directory],
  (#367).

  Old path: `~/.vcspull`

  New path: `XDG_CONFIG_HOME`, usually `~/.config/vcspull`

  [xdg base directory]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html

- Override config directory via `VCSPULL_CONFIGDIR` (#367)
- Switch from `str` to `pathlib.Path` (#364)
- Requires click 8+

### Compatibility

- Allow click 8.1.x (#372)
- vcspull learned `-h` for help (thanks HN 2022-04-11)
- Python 3.7 and 3.8 dropped (#356)

  Branch [`v1.11.x`](https://github.com/vcs-python/vcspull/tree/v1.11.x) opened for security / bug fixes.

- `--version` learned to show libvcs version
- `--version` learned to accept the `-V` flag
- `vcspull sync` accepts config file params via `--config`. Before it was only
  `-c`. Assume myrepos.yaml in your path:

  ```console
  $ vcspull sync --config myrepos.yaml
  ```

### Development

- Publish to PyPI via CI when git tags are pushed

### Documentation

- Sidebar reorganized into sections
- Added documentation on fetching developmental releases of libvcs

## vcspull 1.11.5 (2022-04-20)

### Compatibility

- `vcspull sync` accepts config file params via `--config`. Before it was only
  `-c`. Assume myrepos.yaml in your path:

  ```console
  $ vcspull sync --config myrepos.yaml
  ```

  (backport from vcspull v1.12.0b22)

## vcspull 1.11.4 (2022-04-17)

### Compatibility

- Allow click 8.1.x (backport of #372)

## vcspull 1.11.3 (2022-04-11)

### Compatibility

- vcspull learned `-h` for help (thanks HN 2022-04-11)
- vcspull learned `-V` for version
- vcspull shows libvcs version with `-V` / `--version`

## vcspull 1.11.2 (2022-03-21)

### Fix

- Tab-completion for repository names and configurations (retracted in v1.15)

## vcspull 1.11.1 (2022-03-12)

### Security

- Update libvcs 0.11.0 -> 0.11.1 to patch [CVE-2022-21187](https://nvd.nist.gov/vuln/detail/CVE-2022-21187): mercurial URL injection
  vulnerability with URLs arguments ([libvcs#306](https://github.com/vcs-python/libvcs/pull/306))

### Development

- Run pyupgrade formatting (#353)
- Tests: Move from pytest `tmp_dir` (`py.path.local`) to `tmp_path`
  (`pathlib.Path`)

### Documentation

- Move to furo theme
- Root: `make start_docs`, `make design_docs`
- docs/: `make start`, `make design`

## vcspull 1.11.0 (2022-02-28)

### Development

- CLI moved from `cli.py` -> `cli/__init__.py`, `cli/sync.py`
- CLI: Default groups removed

### Documentation

- Updates for `vcspull sync` changed in 1.10.0

## vcspull 1.10.0 (2022-02-28)

### Breaking

- Sync through `vcspull sync`

  Syncing is no longer ran by default when `vcspull` is typed

### Improvements

- Experimental completion (retracted in v1.15):

  - Completion for sync:

    - `vcspull sync <tab>`
    - `vcspull sync -c <tab> <tab>`

### Documentation

- Added completion:

## vcspull 1.9.0 (2022-02-26)

### Compatibility

- Drop Python 3.6 (#329)
- Add Python 3.10 (#329)

### Documentation

_✨ Renewed_

- GitLab config importer example script, credit @aschleifer (#336, #337, #341)

  These are hosted on the new _config generation_ page.

- Rename `examples` page to `configuration`
- Theme has been moved to [furo]
- Broken URLs have ben fixed
- Added copy paste button next to code snippets
- Inline tab examples for configuration and config generators

[furo]: https://github.com/pradyunsg/furo

### Development

- CI: Use poetry 1.1.12 and new `install-poetry.py` install location (#329)

## vcspull 1.8.1 (2021-12-05)

- Update libvcs 0.10.0 to 0.10.1
- #313: Update poetry to 1.1
  - CI: Use poetry 1.1.7 and `install-poetry.py` installer
  - Relock poetry.lock at 1.1 (w/ 1.1.7's fix)

## vcspull 1.8.0 (2021-06-16)

- #277: Convert to markdown
- Update libvcs to 0.10.0

## vcspull 1.7.0post0 (2021-06-14)

- Fix image in README

## vcspull 1.7.0 (2021-06-14)

- #308: Update to libvcs 0.9.0 (python 3.6+ only)

## vcspull 1.6.1 (2021-06-14)

- Remove some python 2.7 compat imports

## vcspull 1.6.0 (2021-06-14)

- #307: Drop python 2.7

## vcspull 1.5.0 (2020-08-11)

- #304, #299 (tests) - Fix adding of remotes
- #303 - Overhaul docs and packaging
- #303 - Add docs for CLI via sphinx-click

## vcspull 1.4.3 (2020-08-05)

- Bump libvcs to 0.4.4

## vcspull 1.4.2 (2020-08-01)

- Bump libvcs to 0.4.3, fix issue with updating branches that have special
  characters (e.g. hyphens)

## vcspull 1.4.1 (2020-08-01)

- Bump libvcs to 0.4.2, fixes issues with checking out branches where
  branch doesn't exist upstream

## vcspull 1.4.0 (2020-08-01)

- :issue:`300` Update libvcs to 0.4.0, also fixes :issue:`298` issue on initial checkout
- :issue:`301` Move from Travis to GitHub Actions

## vcspull 1.3.1 (2020-07-26)

- :issue:`231` Add updating / merging of remote URLs (via PR :issue:`297`)
- Fix colorama constraint
- poetry lockfile: Fix (accidentally pushed lockfile via prerelease
  version of poetry)

## vcspull 1.3.0post2 (2020-07-26)

- Fix pyproject.toml libvcs pin

## vcspull 1.3.0post1 (2020-07-26)

- Fix pyproject.toml release version
- Fixes to contribution docs

## vcspull 1.3.0 (2020-07-25)

- :issue:`257` - Move from Pipfile to poetry
- Removed dependency on releases for CHANGES, use plain reStructuredText
  for now
- Loosen constraints for colorama
- Update click to 7/8
- Add `project_urls` to setup.py

## vcspull 1.2.0 (2018-07-08)

- Sort imports
- Add isort package, isort configuration in setup.cfg, and
  `make isort` task to Makefile.
- Add `make sync_pipfile` for updating _Pipfile_
  dependencies against _requirements/\*.txt_ files.
- Update Pipfile
- Package updates

  Base / Core dependencies:

  - kaptan: 0.5.9 to >=0.5.9,<1 (adds Python 3.7 compatibility)
  - pyyaml: >=3.12,<4 (adds Python 3.7 compatibility)

  Docs:

  - sphinx: 1.7.1 to 1.7.5
  - alagitpull: 0.0.19 to 0.0.21
  - releases: 1.4.0 to 1.7.1

  Testing:

  - pytest: 3.4.2 to 3.6.3

## vcspull 1.1.0 (2018-03-11)

- Move to new organization, vcs-python
- Fix a bug in libvcs that prevented syncing on Ubuntu 18.04 LTS
  beta.
- Update libvcs to 0.3.0
- Various package updates for support infrastructure,
  including pytest and sphinx

## vcspull 1.0.3 (2016-12-19)

- Update more documentation domain at vcspull.git-pull.com

## vcspull 1.0.2 (2016-12-19)

- Update to new documentation domain at vcspull.git-pull.com
- Fix travis tests via `pip install -u mock` in
  `.travis.yaml`.

## vcspull 1.0.1 (2016-11-24)

- :issue:`43` - fix missing test dependency on anaconda by updating libvcs
- README tweaks

## vcspull 1.0.0 (2016-09-13)

- :issue:`41` Transition uses `libvcs`\_ backend
- :issue:`41` Transition tests from unittest to pytest
- :issue:`40` Major API refactorings and removal of unused code, see the
  api reference for more.
- :issue:`38` Improve SVN and git support, thanks `@jcfr`!
- :issue:`35` Don't run mercurial tests if `hg(1)` missing, by `@jcfr`
- :issue:`36` , :issue:`37` Documentation updates from `@jcfr`
- :issue:`34` Fix missing mock library, thanks `@jfcr`

[libvcs]: https://github.com/vcs-python/libvcs

## vcspull 0.9.0 (2016-05-26)

- [cli] [internal]: command line arg parsing now uses [click] library.
- [cli] vcspull will use the beginning part of the argument to determine
  if its searching by vcs URL, repository name or system path.
- [config] `repo` (the longform for the vcs url) is now `url`.
- [config] [internal] repos are now passed around in a list of
  :py:mod:`dict`. :class:`vcspull.config.expand_config` has been
  simplified.
- [internal] flake8 and sort imports
- [internal] [dev] Add `Makefile` for `watch_test` (re-running
  tests on file change), `watch_flake` (re-running flake8 check
  on changes)
- [internal] [dev] Move `.tmuxp.yaml` commands to use Makefile
- [internal] [dev] split up requirements into `requirements/`
  folder.
- Drop Python 2.6 support
- [internal] `find_config` renamed to `find_config_files`.
- Rename `get_repos` to `filter_repos`, simplify argument signature.
- [cli] Experimental support for concurrently synchroniznig repos via
  :py:mod:`multiprocessing`.
- [api][internal]: Refactor :class:`vcspull.repo.Repo` factory class
  into :func:`vcspull.repo.create_repo`.
- [internal]: major internal cleanup of dead code, pep8 style updates.
- [git] [tests]: fix git+https remotes [#14], add regression tests.

## vcspull 0.2.2 (2015-07-10)

- [internal]: devendorize colorama

## vcspull 0.2.1 (2015-06-12)

- [internal] [tests]: handle `ERROR` in git cloning, add tests. Fixes
  cases like `ERROR: Repository not found`.

## vcspull 0.2.0 (2015-05-19)

- [api] Change the API of :class:`vcspull.repo.base.BaseRepo` and
  :class:`vcspull.repo.Repo` to accept arguments as `kwargs`
- [api]: Rename `parent_path` to `cwd`.
- [docs]: Pydoc documentation for `Repo` usage.
- [docs]: Python API example

## vcspull 0.1.1 (2015-05-09)

- [testing] Fix sniffer on Python 3
- [cli] remove redundant message when adding remotes
- [docs] add animated screencast preview for ReadTheDocs and README.rst

## vcspull 0.1.0 (2015-04-30)

- [cli]: support for specifying for config file path with `-c`.
- [cli]: clarify error output when no repositories found

## vcspull 0.0.10 (2015-04-08)

- [cli]: fix message when adding remotes
- [internals]: split config-related functions into `config` module.
- [tests]: consolidate tests from `testsuite.cli` into
  `testsuite.config`.
- [tests]: remove stub TestSuites insite of `cli` tests.
- [tests]: Refactor helpers. Tests should now run much faster.
- [cli]: vcspull now checks for configs in `~/.vcspull` (yaml and json
  files) as well as the standard `~/.vcspull.{yaml,json}` files.

## vcspull 0.0.9-1 (2015-03-27)

- [internals]: move to `__about__.py` convention for pulling package
  data.
- [internals]: remove extraneous url from stdout in git repositories.

## vcspull 0.0.9 (2015-03-25)

- [dev]: support for tmuxp `before_script` to set up local virtualenv +
  dependencies.
- [dev]: Clean up `__init__.py` imports
- [internals]: move :meth:`update` to :meth:`update_dict` in `utils`.
- [cli]: `-d` / `--dirmatch` for matching directories, accepts
  [fnmatch]'s.
- [cli]: `-r` / `--repomatch` for matching directories, accepts
  [fnmatch]'s.
- [cli]: `-c` / `--config` YAML / JSON file of repositories
- [config/vcs]: Exception for private / ssh GitHub repository URLs and
  message to change to correct format.
- [docs]: Update docs to reflect updates on new commands and how to clone
  private / privileged GitHub repos.

## vcspull 0.0.8-4 (2014-02-06)

- [cli] Update documentation when no config found to show `yaml` and
  `json`. Remove mentioning of `conf`.

## vcspull 0.0.8-3 (2014-02-06)

- [logging] [#6] - fix issue where log templates would not
  concatenate correctly.

[#6]: https://github.com/vcs-python/vcspull/issues/6

## vcspull 0.0.8-2 (2014-02-05)

- [packaging] Fix issue where `package_metadata.py` missing from
  `MANIFEST.in`.

## vcspull 0.0.8-1 (2014-02-05)

- [setup.py] remove history from readme

## vcspull 0.0.8 (2014-02-05)

- Use werkzeug/flask style unit test suites.
- [docs] Fix section headings.
- [internals] use conventions from [tony/cookiecutter-pypackage].

vcspull 0.0.7 (2014-01-07)

- New logging system from pypa/pip with support showing current stderr
  (checkout, pull, cloning) progress.
- `unicode_literals`, `__future__` for python 3 support.

## vcspull 0.0.6 (2013-12-03)

- Renamed to `vcspull`.

## vcspull 0.0.5 (2013-11-28)

- [tests] tests for loading multiple configs, config conflict cases
- [internal] fix recursive imports, using explicit importing
- [git] handle cli case where git is on a different branch, just fetch
  repo.
- [py3] python 3 support

## vcspull 0.0.4 (2013-11-25)

- [svn] [git] [hg] added functionality for pip's netloc and vcs
  functionality.
- [docs] update license docs for methods from salt (apache) and pip (mit).
- [cli] autocomplete
- [config] [tests] Support for expanding environment variables in syntax
  with `${envvar}` and `~`.
- [cli] Exit with `Ctrl-C` gracefully.

## vcspull 0.0.3 (2013-11-23)

- [docs] [examples] more docs and examples
- [internal] remove developer logger message that would show when
  updating.
- [cli] - fixed bug where logger would output message twice
- [`pep257`_] [`pep8`_] [logger] update `vcspull.log` module to latest
  code (from `tmuxp`\_ project).

## vcspull 0.0.2 (2013-11-23)

- [tests] Move tests for making directory paths to `test_repo`
  TestSuite.
- [tests] [git] more work on remotes with :class:`vcspull.repo.git.GitRepo`.

## vcspull 0.0.2 (2013-11-16)

- [tests] refactor tests modules
- [git] add some functions with saltstack to
  :py:class:`vcspull.repo.git.GitRepo`.
- [version] reset to 0.1.0 w/o dev tag to begin releases.

## vcspull 0.0.1 (2013-11-15)

- [cli] [tests] [#1] for making directories recursively.
- [tests] :py:mod:`tempfile`.
- [cli] [config] remove .ini support

[#1]: https://github.com/vcs-python/vcspull/issues/1

## vcspull 0.0.1 (2013-11-09)

- [pep257] to all modules.
- Add [argcomplete]
- Move in code patterns from [tmuxp]. New logger, setup.py, testsuite
  structure, etc.

## vcspull 0.0.1 (2013-10-23)

- add example

## vcspull 0.0.1 (2013-10-17)

- fix packaging issue, add MANIFEST.in
- bump to 0.1.1

## vcspull 0.0.1 (2013-10-08)

- move to semver

## vcspull 0.0.1 (2013-10-06)

- https://github.com/vcs-python/vcspull/compare/a96e723269...a5be723de5
- remove virtualenv dep [Done]
- remove pip dep [Done]
- logging for repo obtain/update/makedir/rev [Done]
- logging.notify with alternative format [Done]
- removed salt-specific code used for Popen
  (https://github.com/vcs-python/vcspull/commit/67dbb793e7)

[tmuxp]: http://tmuxp.git-pull.com
[pep257]: http://www.python.org/dev/peps/pep-0257/
[pep8]: http://www.python.org/dev/peps/pep-0008/
[sphinx-argparse]: https://github.com/tony/sphinx-argparse
[argcomplete]: https://github.com/kislyuk/argcomplete
[tony/cookiecutter-pypackage]: https://github.com/tony/cookiecutter-pypackage.
[#14]: https://github.com/vcs-python/vcspull/issues/14
[click]: https://github.com/pallets/click
[fnmatch]: http://pubs.opengroup.org/onlinepubs/009695399/functions/fnmatch.html

<!---
.. vim: set filetype=markdown:
-->
