Skip to content
28 changes: 28 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,34 @@ $ uv add libvcs --prerelease allow
_Notes on the upcoming release will go here._
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->

### Documentation

#### Documentation examples now run as doctests (#540)

Every Python example under `docs/` executes against real temporary
repositories on each test run, so copied examples work as printed. The
conversion surfaced and fixed documented calls that had drifted from the
API: `git.branches.create()` and `remote.set_url()` shown positionally
where keyword arguments are required, `notes.add(object=...)` for what is
named `object_sha`, `reflog.ls(ref=...)` and `expire(ref=...)` parameters
that don't exist, `worktrees.add(branch=...)` for `new_branch`, and
{class}`~libvcs._internal.query_list.QueryList` filter examples using
attribute names the objects don't have.

#### Cross-references link objects on first mention (#540)

Documentation pages now link classes, methods, and exceptions to their API
reference the first time prose names them, and man-page citations link to
upstream documentation. The {mod}`libvcs.url.registry` guide untangles its
examples into narrated, runnable steps, and the module hub pages open with
what each layer does.

#### Quickstart now demonstrates URL parsing (#540)

The quickstart's "Basic Usage" adds a "Parse URLs" section that detects
and parses a repository URL with {class}`~libvcs.url.git.GitURL`. See
{ref}`url-parsing` for the full tour.

## libvcs 0.45.0 (2026-06-28)

libvcs 0.45.0 makes test behavior independent of execution order by removing shared state from the URL registry and the pytest fixtures. {class}`~libvcs.url.registry.VCSRegistry` now keeps its parsers per instance, so building a custom registry no longer mutates the module-level `registry`, and the `git_repo`, `hg_repo`, and `svn_repo` fixtures hand every test its own clone instead of a shared checkout — fixing cross-test state leakage for downstream test suites such as vcspull. Contributors also gain an opt-in parallel test mode via pytest-xdist.
Expand Down
2 changes: 1 addition & 1 deletion docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ libvcs exposes three public subsystems -- URL parsing, command execution,
and repository synchronization -- plus a pytest plugin for test fixtures.

All APIs are pre-1.0 and may change between minor versions.
Pin to a range: `libvcs>=0.39,<0.40`.
Pin to a range, e.g. `libvcs>=0.45,<0.46`.

## Subsystems

Expand Down
2 changes: 1 addition & 1 deletion docs/api/pytest-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def setup(
its own clone. The remote is built once and cached for the session, then copied
for every consumer, so a test can commit, add remotes, or rewrite history
without affecting any other test — and the fixtures stay safe under parallel
runs (`pytest-xdist`).
runs ([pytest-xdist](https://pytest-xdist.readthedocs.io/)).
:::

## Types
Expand Down
50 changes: 35 additions & 15 deletions docs/cmd/git/branch.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,52 @@
# `branch`

For `git-branch(1)`.
For [`git-branch(1)`](https://git-scm.com/docs/git-branch).

## Overview

Manage git branches using {class}`~libvcs.cmd.git.GitBranchManager` (collection-level)
and {class}`~libvcs.cmd.git.GitBranchCmd` (per-branch operations).

### Example
### Examples

```python
from libvcs.cmd.git import Git
List branches — local by default, remote with `remotes=True`:

git = Git(path='/path/to/repo')
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> branches = git.branches.ls()
>>> len(branches) >= 1
True
>>> remote_branches = git.branches.ls(remotes=True)
>>> isinstance(remote_branches, list)
True
```

# List all branches
branches = git.branches.ls()
Create a branch, then look it up:

# List remote branches only
remote_branches = git.branches.ls(remotes=True)
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.branches.create(branch='feature-branch')
''
>>> branch = git.branches.get(branch_name='feature-branch')
>>> branch.branch_name
'feature-branch'
```

# Create a new branch
git.branches.create('feature-branch')
Rename or delete a branch through its Cmd object:

# Get a specific branch and operate on it
branch = git.branches.get(branch_name='feature-branch')
branch.rename('new-feature')
branch.delete()
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.branches.create(branch='old-name')
''
>>> branch = git.branches.get(branch_name='old-name')
>>> branch.rename('new-feature')
''
>>> renamed = git.branches.get(branch_name='new-feature')
>>> renamed.delete() # doctest: +ELLIPSIS
'Deleted branch new-feature ...'
```

## API Reference
Expand Down
42 changes: 22 additions & 20 deletions docs/cmd/git/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# `libvcs.cmd.git`

For `git(1)`.
For [`git(1)`](https://git-scm.com/docs/git).

_Compare to: [`fabtools.git`](https://fabtools.readthedocs.io/en/0.19.0/api/git.html#git-module),
[`salt.modules.git`](https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.git.html),
Expand All @@ -11,9 +11,12 @@ _Compare to: [`fabtools.git`](https://fabtools.readthedocs.io/en/0.19.0/api/git.
libvcs provides **Managers** and **Commands** for git subcommands:

- **Managers** (`git.branches`, `git.tags`, etc.) let you traverse repository
entities intuitively with ORM-like filtering via QueryList
entities intuitively with ORM-like filtering via
{class}`~libvcs._internal.query_list.QueryList`
- **Commands** are contextual ways to run git commands against a specific target entity

See {ref}`traversing-git-repos` for the full guide.

```
Git instance
├── branches: GitBranchManager
Expand All @@ -31,25 +34,24 @@ Git instance

### Quick Example

```python
from libvcs.cmd.git import Git

git = Git(path='/path/to/repo')

# List all branches
branches = git.branches.ls()
List branches, rename one through its Cmd object, and manage tags:

# Filter to remote branches only
remote_branches = git.branches.ls(remotes=True)

# Get a specific branch and rename it
branch = git.branches.get(branch_name='old-name')
branch.rename('new-name')

# Create and manage tags
git.tags.create(name='v1.0.0', message='Release 1.0')
tag = git.tags.get(tag_name='v1.0.0')
tag.delete()
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> branches = git.branches.ls()
>>> len(branches) >= 1
True
>>> git.branches.create(branch='old-name')
''
>>> branch = git.branches.get(branch_name='old-name')
>>> branch.rename('new-name')
''
>>> git.tags.create(name='v1.0.0', message='Release 1.0')
''
>>> tag = git.tags.get(tag_name='v1.0.0')
>>> tag.delete() # doctest: +ELLIPSIS
"Deleted tag 'v1.0.0' ..."
```

```{toctree}
Expand Down
49 changes: 33 additions & 16 deletions docs/cmd/git/notes.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,50 @@
# `notes`

For `git-notes(1)`.
For [`git-notes(1)`](https://git-scm.com/docs/git-notes).

## Overview

Manage git notes using {class}`~libvcs.cmd.git.GitNotesManager` (collection-level)
and {class}`~libvcs.cmd.git.GitNoteCmd` (per-note operations).

### Example
### Examples

```python
from libvcs.cmd.git import Git
Add a note to the current commit (`object_sha` defaults to `HEAD`), then list
notes:

git = Git(path='/path/to/repo')
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.notes.add(message='This is a note')
''
>>> notes = git.notes.ls()
>>> len(notes) >= 1
True
```

# Add a note to a commit
git.notes.add(object='HEAD', message='This is a note')
Operate on a note through its Cmd object — show it, append to it, remove it:

# List all notes
notes = git.notes.ls()
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.notes.add(message='Reviewed by Alice')
''
>>> note = git.notes.ls()[0]
>>> note.show()
'Reviewed by Alice\n'
>>> note.append(message='Additional info')
''
>>> note.remove() # doctest: +ELLIPSIS
'...'
```

# Get a specific note and operate on it
note = git.notes.get(object='HEAD')
note.show()
note.append(message='Additional info')
note.remove()
Prune notes attached to objects that no longer exist:

# Prune notes for non-existent objects
git.notes.prune()
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.notes.prune()
''
```

## API Reference
Expand Down
39 changes: 26 additions & 13 deletions docs/cmd/git/reflog.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
# `reflog`

For `git-reflog(1)`.
For [`git-reflog(1)`](https://git-scm.com/docs/git-reflog).

## Overview

Manage git reflog using {class}`~libvcs.cmd.git.GitReflogManager` (collection-level)
and {class}`~libvcs.cmd.git.GitReflogEntryCmd` (per-entry operations).

### Example
### Examples

```python
from libvcs.cmd.git import Git
List reflog entries and look one up by refspec:

git = Git(path='/path/to/repo')
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> entries = git.reflog.ls()
>>> len(entries) >= 1
True
>>> entry = git.reflog.get(refspec='HEAD@{0}')
>>> entry.refspec
'HEAD@{0}'
```

# List reflog entries
entries = git.reflog.ls()
Check whether a ref has a reflog:

# List entries for a specific ref
head_entries = git.reflog.ls(ref='HEAD')
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.reflog.exists('HEAD')
True
```

# Check if reflog exists for a ref
git.reflog.exists(ref='main')
Expire old reflog entries:

# Expire old reflog entries
git.reflog.expire(ref='HEAD', expire='90.days.ago')
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.reflog.expire(expire='90.days.ago')
''
```

## API Reference
Expand Down
40 changes: 27 additions & 13 deletions docs/cmd/git/remote.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
# `remote`

For `git-remote(1)`.
For [`git-remote(1)`](https://git-scm.com/docs/git-remote).

## Overview

Manage git remotes using {class}`~libvcs.cmd.git.GitRemoteManager` (collection-level)
and {class}`~libvcs.cmd.git.GitRemoteCmd` (per-remote operations).

### Example
### Examples

List remotes — a freshly cloned repository has its `origin`:

```python
from libvcs.cmd.git import Git
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.remotes.ls() # doctest: +ELLIPSIS
[<GitRemoteCmd path=... remote_name=origin>]
```

git = Git(path='/path/to/repo')
Add a remote:

# List all remotes
remotes = git.remotes.ls()
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.remotes.add(name='upstream', url='https://github.com/org/repo.git')
''
```

# Add a new remote
git.remotes.add(name='upstream', url='https://github.com/org/repo.git')
Get a specific remote and operate on it through its Cmd object:

# Get a specific remote and operate on it
origin = git.remotes.get(remote_name='origin')
origin.show()
origin.prune()
origin.set_url('https://new-url.git')
```python
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> origin = git.remotes.get(remote_name='origin')
>>> 'origin' in origin.show()
True
>>> origin.prune()
''
>>> origin.set_url(url='https://example.com/new.git')
''
```

## API Reference
Expand Down
Loading