CVS Source Control Extension
VS Code extension that integrates CVS (Concurrent Versions System) as a native Source Control provider — bringing CVS workflows into the VS Code SCM panel with the same look and feel as the built-in Git extension.
Features
Source Control view
- ⚠ Incoming Conflicts — files where the server has a newer revision AND local is also modified (
Needs Merge); shown automatically from cvs status without requiring Check Remote; if present in Staged, auto-removed from staging; click opens 3-way merge editor; inline ↓ pulls just this file, inline ⟲ reverts local changes so the file becomes safe to pull
- Conflicts — files with real CVS conflict markers after a failed merge (
C status); click opens 3-way merge editor; if staged, auto-removed from staging; inline ⟲ reverts local changes
- Staged Changes — files explicitly staged for the next commit; state persisted to
.cvs-staging
- Changes — locally modified (
M), added (A), removed (R) files
- ↓ Incoming Changes — files where the server has a newer version and local is clean (
Needs Patch); safe to pull; inline ↓ pulls just this file
- Untracked Files — new files not yet added to CVS
Diffs
- Click to diff — M files open a side-by-side diff against the pinned base revision from
CVS/Entries (no server round-trip for clean files; content cached per revision until the next update)
- Conflict files — click opens VS Code 3-way merge editor using the CVS
.# pre-merge backup as the "Current" side and the server version as "Incoming"
- Incoming Changes — click opens diff of server version vs local clean copy
- QuickDiff gutter bars — inline change indicators on modified lines in the editor
- Conflict marker highlights — red background decoration on
<<<<<<< / ======= / >>>>>>> lines
Commands
| Command |
Where |
Description |
| CVS: Refresh |
SCM title |
Re-runs cvs status and updates all groups |
CVS: Check Remote $(cloud) |
REPOSITORIES row |
Runs cvs -qn update to detect incoming changes without modifying the working copy |
CVS: Update All $(sync) |
REPOSITORIES row |
Runs cvs update on the whole repo; warns about unsaved files; shows "Show Conflicts" button if merges fail; label shows ⚠ N when N conflict-risk files exist |
CVS: Update File $(cloud-download) |
Inline on ↓ Incoming / ⚠ Incoming Conflicts / Explorer right-click |
Pulls a single file; same unsaved-file check as Update All |
CVS: Safe Pull $(cloud-download) |
REPOSITORIES row |
Pulls only Needs Patch files (no local changes); skips conflict-risk files |
CVS: Set Auto-Check Interval $(clock) |
REPOSITORIES row |
Sets how often Check Remote runs automatically (seconds; 0 = off); value persisted per workspace |
| CVS: Commit |
SCM title / ✓ button |
Commits staged files, or all changes if nothing is staged |
| CVS: Commit File |
Right-click Changes/Staged / Explorer right-click |
Commits a single file |
| CVS: Stage File |
Right-click Changes |
Moves a file to Staged Changes |
| CVS: Unstage File |
Right-click Staged |
Moves a file back to Changes |
| CVS: Clear Staging |
SCM title |
Wipes the staging store |
| CVS: Add |
Right-click Untracked / Explorer right-click |
Schedules a new file for addition |
| CVS: Remove |
Right-click Changes / Explorer right-click |
Deletes the file and schedules it for removal (with confirmation) |
| CVS: Revert |
Right-click Changes / Conflicts / ⚠ Incoming Conflicts / Explorer right-click |
Discards local changes via cvs update -C; on Incoming Conflicts files this removes the local edit so the file becomes safe to pull |
| CVS: Resolve Incoming Conflict |
Right-click ⚠ Incoming Conflicts |
After editing in the merge editor and saving, advances the file to the pinned server revision and marks it as locally modified (M) — ready to commit |
| CVS: Show Log |
Right-click any file |
Opens "CVS History" panel in the SCM sidebar |
| CVS: Show History |
SCM title menu / Explorer right-click |
Opens the history panel. From the SCM title: prompts for subfolder (or All). From the explorer: pre-filtered to the clicked file or folder. File-targeted view disables changeset grouping (one revision per row), shows a "Compare" button, and sets the panel title to the filename. |
| CVS: Annotate |
Right-click any file / Explorer right-click |
Opens a read-only per-line revision/author annotation document |
| CVS: Diff with HEAD |
Explorer right-click |
Opens a side-by-side diff of the file against its pinned base revision (same diff as clicking the file in the SCM panel) |
| CVS: Rename File |
Explorer right-click |
Renames the file on disk, schedules the old path for removal, and schedules the new path for addition. Warns that CVS does not preserve history across renames. |
Status bar (REPOSITORIES row)
| Item |
Meaning |
$(clock) off / $(clock) 60s |
Auto-check interval; click to change |
$(cloud) Check Remote |
Manual Check Remote trigger |
$(cloud-download) ↓ N |
N files safe to pull; tooltip lists filenames; click runs Safe Pull |
$(sync) Update All / $(sync) Update All ⚠ N |
Update All; ⚠ N means N conflict-risk files — tooltip lists them |
Automatic refresh
- File system watcher — any change to the working directory (save, create, delete) triggers an immediate
cvs status refresh; subsequent changes are suppressed until the cooldown expires (cvs.fileWatchCooldown); files matching .cvsignore patterns and the CVS repository directory are never watched; set cooldown to 0 to disable auto-refresh entirely
- Remote check interval — Check Remote runs on a configurable per-workspace interval (set via the
$(clock) button); minimum 60 s; 0 disables
Settings
| Setting |
Default |
Description |
cvs.cvsRoot |
"" |
Fallback CVSROOT if CVS/Root is not found in workspace root |
cvs.showUntrackedFiles |
true |
Show untracked files (?) in the Untracked Files group |
cvs.verboseOutput |
false |
Show CVS command output in the output channel. Errors always appear regardless of this setting. |
cvs.fileWatchCooldown |
1 |
Seconds between file-watcher-triggered refreshes. The first change fires immediately; further changes are suppressed until this many seconds have passed. Set to 0 to disable auto-refresh on file changes entirely. |
cvs.logGroupingWindowSeconds |
120 |
Maximum seconds between file revisions to treat them as one logical changeset in the History panel. |
cvs.logMaxEntries |
100 |
Maximum number of logical changesets shown in the CVS History panel. |
cvs.historyDateFormat |
"YYYY-MM-DD HH:mm" |
Date format for the CVS History panel. Tokens: YYYY MM DD HH mm. Leave empty to use the system locale. |
cvs.warnOnCommitAll |
true |
Show a confirmation prompt when committing with no staged files (commits all changed files). |
History panel
The history panel (CVS: Show History) displays commit log entries as a sortable table. Features:
Path targeting — right-clicking a file in the explorer opens history pre-filtered to that file's revisions. Right-clicking a folder shows the combined log for all files in that folder. The SCM title bar entry shows a quick pick for subfolder selection.
Filter bar — always visible above the table:
- Type in the search box to filter by commit message (case-insensitive substring)
- Select an author from the dropdown (populated from loaded entries)
- Set a date range with the From/To date pickers (same-day entries are included on both boundary dates)
- "Clear filters" resets all inputs at once
Compare two revisions — available only in file-targeted view. Check any two revision rows and click "Compare". A side-by-side diff opens with the older revision on the left and the newer on the right; hidden/filtered rows are excluded from the selection count.
Changeset grouping — in folder or full-module view, revisions by the same author with the same message committed within cvs.logGroupingWindowSeconds seconds are merged into one changeset row showing all affected files. File-targeted view always shows one row per revision (grouping disabled).
CVS Session Log
The CVS Session Log panel (visible in the SCM sidebar, below CVS History) tracks every CVS operation performed during the current VS Code session: commits, updates, adds, removes, reverts, and safe-pulls.
Each entry shows: operation type (colour-coded), time, commit message (if any), and the list of affected files. Click an entry header to expand it and see per-file details. Where a revision range is available (e.g. after an update), a Diff button opens a side-by-side view of the before/after content.
Active-file filter — when you have a file open in the editor the view automatically filters to operations that touched that file. Click Show all to clear the filter.
Timeline integration — the same entries appear in VS Code's built-in Timeline panel (bottom of the Explorer sidebar) for the active file, letting you scrub through CVS operations alongside local file history.
The log is in-memory only and resets when VS Code is restarted.
How staging works
CVS has no native staging concept — every cvs commit commits all locally modified files unless you explicitly list filenames. This extension emulates Git-style staging entirely in the extension layer:
- Stage a file — right-click any file in "Changes" and choose CVS: Stage File. The file moves to the "Staged Changes" group. No CVS command is run; the extension records the selection in a local
.cvs-staging file inside the working directory.
- Commit — when you click Commit (or press ✓), the extension passes only the staged filenames to
cvs commit -m "..." file1 file2. If nothing is staged, all changed files are committed (standard CVS behaviour).
- Unstage — right-click a file in "Staged Changes" and choose CVS: Unstage File to move it back to Changes.
- Clear all — CVS: Clear Staging in the SCM title bar wipes the staging store entirely (useful when stale entries appear after renames or moves).
The .cvs-staging file is an extension artefact and should be added to .cvsignore to keep it out of cvs status output on servers that do report unknown files.
How conflict resolution works
When Check Remote detects that a file was changed on the server while you also have local edits (⚠ Incoming Conflicts):
- Click the file in
⚠ Incoming Conflicts — opens VS Code's 3-way merge editor with the common ancestor as base, your local edits as "Current", and the server revision as "Incoming".
- Resolve the conflict in the merge editor and save.
- Saving automatically triggers CVS: Resolve Incoming Conflict: the extension advances
CVS/Entries to the server revision (clearing any sticky tag) and writes your merged content back. The file now shows as M in Changes — ready to commit.
If CVS already wrote conflict markers into a file during cvs update (visible in the Conflicts group), clicking the file opens the same 3-way merge editor using the .# pre-merge backup CVS left behind.
CVSROOT detection
The extension reads CVSROOT from <workspaceRoot>/CVS/Root automatically. If that file is absent, it falls back to the cvs.cvsRoot setting.
Requirements
- VS Code 1.85+
cvs CLI available on PATH (installed in devcontainer at /usr/bin/cvs)
Development
- Unit tests:
npm run compile && npm test
- Integration tests (requires devcontainer with
cvs-server running): npm run test:integration
- Manual extension testing: open a CVS workspace in VS Code; the extension activates automatically when
CVS/Root is present
The CVS server starts with the devcontainer. If you rebuilt, check $CVSROOT is set.