Skip to content

Commit 22760f2

Browse files
authored
docs: Run examples as doctests, fix drift, link APIs (#540)
Every runnable Python example under docs/ now executes as a pytest doctest against temporary repositories, so copied examples work as printed and cannot silently drift from the API again. Executing them surfaced several documented calls whose signatures had drifted; those are corrected here alongside a first-mention cross-reference pass and a voice cleanup of the URL and command-reference pages. - Executable examples: docs code blocks run as fixture-backed doctests, asserting stable shapes (lengths, types, ellipsis) so they stay robust across git versions while still exercising every call. - API drift fixes: corrected documented calls that raised TypeError or returned nothing when copied — positional arguments where keywords are required, a renamed notes parameter (object -> object_sha), a nonexistent reflog.expire(ref=...) parameter, worktrees.add's new_branch, and QueryList filters using attributes the objects have. - Cross-references: first prose mentions of classes, methods, and exceptions link to their API reference; man-page citations and external projects (Django, pip, Mercurial, Subversion) link upstream. - Page voice: url/registry narration moves out of a single code fence into narrated doctest stories, and the url index plus the cmd and sync hub and stub pages open with what each layer does. - Quickstart and contributor docs: the quickstart gains a runnable Parse URLs section; workflow.md's setup and lint commands and its stale Releasing section are corrected to match current tooling and the release page. See CHANGES for the user-facing documentation entries.
2 parents 36eb81d + 663c940 commit 22760f2

33 files changed

Lines changed: 560 additions & 393 deletions

CHANGES

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,34 @@ $ uv add libvcs --prerelease allow
2020
_Notes on the upcoming release will go here._
2121
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->
2222

23+
### Documentation
24+
25+
#### Documentation examples now run as doctests (#540)
26+
27+
Every Python example under `docs/` executes against real temporary
28+
repositories on each test run, so copied examples work as printed. The
29+
conversion surfaced and fixed documented calls that had drifted from the
30+
API: `git.branches.create()` and `remote.set_url()` shown positionally
31+
where keyword arguments are required, `notes.add(object=...)` for what is
32+
named `object_sha`, `reflog.ls(ref=...)` and `expire(ref=...)` parameters
33+
that don't exist, `worktrees.add(branch=...)` for `new_branch`, and
34+
{class}`~libvcs._internal.query_list.QueryList` filter examples using
35+
attribute names the objects don't have.
36+
37+
#### Cross-references link objects on first mention (#540)
38+
39+
Documentation pages now link classes, methods, and exceptions to their API
40+
reference the first time prose names them, and man-page citations link to
41+
upstream documentation. The {mod}`libvcs.url.registry` guide untangles its
42+
examples into narrated, runnable steps, and the module hub pages open with
43+
what each layer does.
44+
45+
#### Quickstart now demonstrates URL parsing (#540)
46+
47+
The quickstart's "Basic Usage" adds a "Parse URLs" section that detects
48+
and parses a repository URL with {class}`~libvcs.url.git.GitURL`. See
49+
{ref}`url-parsing` for the full tour.
50+
2351
## libvcs 0.45.0 (2026-06-28)
2452

2553
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.

docs/api/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ libvcs exposes three public subsystems -- URL parsing, command execution,
88
and repository synchronization -- plus a pytest plugin for test fixtures.
99

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

1313
## Subsystems
1414

docs/api/pytest-plugin.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def setup(
4747
its own clone. The remote is built once and cached for the session, then copied
4848
for every consumer, so a test can commit, add remotes, or rewrite history
4949
without affecting any other test — and the fixtures stay safe under parallel
50-
runs (`pytest-xdist`).
50+
runs ([pytest-xdist](https://pytest-xdist.readthedocs.io/)).
5151
:::
5252

5353
## Types

docs/cmd/git/branch.md

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,52 @@
11
# `branch`
22

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

55
## Overview
66

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

10-
### Example
10+
### Examples
1111

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

15-
git = Git(path='/path/to/repo')
14+
```python
15+
>>> from libvcs.cmd.git import Git
16+
>>> git = Git(path=example_git_repo.path)
17+
>>> branches = git.branches.ls()
18+
>>> len(branches) >= 1
19+
True
20+
>>> remote_branches = git.branches.ls(remotes=True)
21+
>>> isinstance(remote_branches, list)
22+
True
23+
```
1624

17-
# List all branches
18-
branches = git.branches.ls()
25+
Create a branch, then look it up:
1926

20-
# List remote branches only
21-
remote_branches = git.branches.ls(remotes=True)
27+
```python
28+
>>> from libvcs.cmd.git import Git
29+
>>> git = Git(path=example_git_repo.path)
30+
>>> git.branches.create(branch='feature-branch')
31+
''
32+
>>> branch = git.branches.get(branch_name='feature-branch')
33+
>>> branch.branch_name
34+
'feature-branch'
35+
```
2236

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

26-
# Get a specific branch and operate on it
27-
branch = git.branches.get(branch_name='feature-branch')
28-
branch.rename('new-feature')
29-
branch.delete()
39+
```python
40+
>>> from libvcs.cmd.git import Git
41+
>>> git = Git(path=example_git_repo.path)
42+
>>> git.branches.create(branch='old-name')
43+
''
44+
>>> branch = git.branches.get(branch_name='old-name')
45+
>>> branch.rename('new-feature')
46+
''
47+
>>> renamed = git.branches.get(branch_name='new-feature')
48+
>>> renamed.delete() # doctest: +ELLIPSIS
49+
'Deleted branch new-feature ...'
3050
```
3151

3252
## API Reference

docs/cmd/git/index.md

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# `libvcs.cmd.git`
22

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

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

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

18+
See {ref}`traversing-git-repos` for the full guide.
19+
1720
```
1821
Git instance
1922
├── branches: GitBranchManager
@@ -31,25 +34,24 @@ Git instance
3134

3235
### Quick Example
3336

34-
```python
35-
from libvcs.cmd.git import Git
36-
37-
git = Git(path='/path/to/repo')
38-
39-
# List all branches
40-
branches = git.branches.ls()
37+
List branches, rename one through its Cmd object, and manage tags:
4138

42-
# Filter to remote branches only
43-
remote_branches = git.branches.ls(remotes=True)
44-
45-
# Get a specific branch and rename it
46-
branch = git.branches.get(branch_name='old-name')
47-
branch.rename('new-name')
48-
49-
# Create and manage tags
50-
git.tags.create(name='v1.0.0', message='Release 1.0')
51-
tag = git.tags.get(tag_name='v1.0.0')
52-
tag.delete()
39+
```python
40+
>>> from libvcs.cmd.git import Git
41+
>>> git = Git(path=example_git_repo.path)
42+
>>> branches = git.branches.ls()
43+
>>> len(branches) >= 1
44+
True
45+
>>> git.branches.create(branch='old-name')
46+
''
47+
>>> branch = git.branches.get(branch_name='old-name')
48+
>>> branch.rename('new-name')
49+
''
50+
>>> git.tags.create(name='v1.0.0', message='Release 1.0')
51+
''
52+
>>> tag = git.tags.get(tag_name='v1.0.0')
53+
>>> tag.delete() # doctest: +ELLIPSIS
54+
"Deleted tag 'v1.0.0' ..."
5355
```
5456

5557
```{toctree}

docs/cmd/git/notes.md

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,50 @@
11
# `notes`
22

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

55
## Overview
66

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

10-
### Example
10+
### Examples
1111

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

15-
git = Git(path='/path/to/repo')
15+
```python
16+
>>> from libvcs.cmd.git import Git
17+
>>> git = Git(path=example_git_repo.path)
18+
>>> git.notes.add(message='This is a note')
19+
''
20+
>>> notes = git.notes.ls()
21+
>>> len(notes) >= 1
22+
True
23+
```
1624

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

20-
# List all notes
21-
notes = git.notes.ls()
27+
```python
28+
>>> from libvcs.cmd.git import Git
29+
>>> git = Git(path=example_git_repo.path)
30+
>>> git.notes.add(message='Reviewed by Alice')
31+
''
32+
>>> note = git.notes.ls()[0]
33+
>>> note.show()
34+
'Reviewed by Alice\n'
35+
>>> note.append(message='Additional info')
36+
''
37+
>>> note.remove() # doctest: +ELLIPSIS
38+
'...'
39+
```
2240

23-
# Get a specific note and operate on it
24-
note = git.notes.get(object='HEAD')
25-
note.show()
26-
note.append(message='Additional info')
27-
note.remove()
41+
Prune notes attached to objects that no longer exist:
2842

29-
# Prune notes for non-existent objects
30-
git.notes.prune()
43+
```python
44+
>>> from libvcs.cmd.git import Git
45+
>>> git = Git(path=example_git_repo.path)
46+
>>> git.notes.prune()
47+
''
3148
```
3249

3350
## API Reference

docs/cmd/git/reflog.md

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,43 @@
11
# `reflog`
22

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

55
## Overview
66

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

10-
### Example
10+
### Examples
1111

12-
```python
13-
from libvcs.cmd.git import Git
12+
List reflog entries and look one up by refspec:
1413

15-
git = Git(path='/path/to/repo')
14+
```python
15+
>>> from libvcs.cmd.git import Git
16+
>>> git = Git(path=example_git_repo.path)
17+
>>> entries = git.reflog.ls()
18+
>>> len(entries) >= 1
19+
True
20+
>>> entry = git.reflog.get(refspec='HEAD@{0}')
21+
>>> entry.refspec
22+
'HEAD@{0}'
23+
```
1624

17-
# List reflog entries
18-
entries = git.reflog.ls()
25+
Check whether a ref has a reflog:
1926

20-
# List entries for a specific ref
21-
head_entries = git.reflog.ls(ref='HEAD')
27+
```python
28+
>>> from libvcs.cmd.git import Git
29+
>>> git = Git(path=example_git_repo.path)
30+
>>> git.reflog.exists('HEAD')
31+
True
32+
```
2233

23-
# Check if reflog exists for a ref
24-
git.reflog.exists(ref='main')
34+
Expire old reflog entries:
2535

26-
# Expire old reflog entries
27-
git.reflog.expire(ref='HEAD', expire='90.days.ago')
36+
```python
37+
>>> from libvcs.cmd.git import Git
38+
>>> git = Git(path=example_git_repo.path)
39+
>>> git.reflog.expire(expire='90.days.ago')
40+
''
2841
```
2942

3043
## API Reference

docs/cmd/git/remote.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,44 @@
11
# `remote`
22

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

55
## Overview
66

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

10-
### Example
10+
### Examples
11+
12+
List remotes — a freshly cloned repository has its `origin`:
1113

1214
```python
13-
from libvcs.cmd.git import Git
15+
>>> from libvcs.cmd.git import Git
16+
>>> git = Git(path=example_git_repo.path)
17+
>>> git.remotes.ls() # doctest: +ELLIPSIS
18+
[<GitRemoteCmd path=... remote_name=origin>]
19+
```
1420

15-
git = Git(path='/path/to/repo')
21+
Add a remote:
1622

17-
# List all remotes
18-
remotes = git.remotes.ls()
23+
```python
24+
>>> from libvcs.cmd.git import Git
25+
>>> git = Git(path=example_git_repo.path)
26+
>>> git.remotes.add(name='upstream', url='https://github.com/org/repo.git')
27+
''
28+
```
1929

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

23-
# Get a specific remote and operate on it
24-
origin = git.remotes.get(remote_name='origin')
25-
origin.show()
26-
origin.prune()
27-
origin.set_url('https://new-url.git')
32+
```python
33+
>>> from libvcs.cmd.git import Git
34+
>>> git = Git(path=example_git_repo.path)
35+
>>> origin = git.remotes.get(remote_name='origin')
36+
>>> 'origin' in origin.show()
37+
True
38+
>>> origin.prune()
39+
''
40+
>>> origin.set_url(url='https://example.com/new.git')
41+
''
2842
```
2943

3044
## API Reference

0 commit comments

Comments
 (0)