Originally published on 2009-01-29; fully revised and updated.
If you still maintain code in CVS, this guide gives you a clean, command-line-focused workflow for day-to-day tasks: checking file history, comparing revisions, tagging releases, creating branches, and merging safely. The original post contained valuable command snippets; this revision organizes them into a reference you can use during real maintenance work.

CVS at a glance: common tasks and commands
| Task | Command | When to use it |
|---|---|---|
| View commit history for a file | cvs log filename | Audit changes, revisions, and author comments. |
| Check working copy status | cvs status filename | Confirm local revision, repository revision, and sticky tags. |
| Diff workspace vs repository | cvs -q diff -u filename | See local edits before commit. |
| Diff two branches | cvs -q diff -u -r BRANCH1 -r BRANCH2 | Review branch-level changes. |
| Get a specific file revision | cvs -q update -Pd -r REVISION filename | Reproduce or inspect an older state. |
| Tag current source as release | cvs tag RELEASE_TAG | Create a named recovery or release point. |
| Repeat merge from HEAD into branch | cvs update -kk -j OLD_HEAD_TAG -j NEW_HEAD_TAG | Merge only changes since the previous merge. |
Inspect history and working-copy state
These are the two most-used inspection commands in CVS.
cvs log filename
cvs status filename
Use log to review revision history and status to verify what your local file is tracking (including sticky tags when you are on a branch/tag).
Choose the right CVS diff command
The original post listed several diff patterns. Keep this set handy:
# Diff complete branches
cvs -q diff -u -r BRANCH1 -r BRANCH2
# Diff one file between two branches
cvs -q diff -u -r BRANCH1 -r BRANCH2 filename
# Diff two versions of a file
cvs -q diff -u -r VERSION1 -r VERSION2 filename
# Diff local working file vs latest repository version
cvs -q diff -u filename
# Diff local working file vs a specific repository version
cvs -q diff -u -r VERSION filenameCode language: PHP (php)
Retrieve revisions, tag releases, and manage branches
Retrieve a file by revision
cvs -q update -Pd -r REVISION filename
Find the revision identifier first with cvs log filename.
Tag a release snapshot
cvs tag RELEASE_TAG
Run release tagging from the project top-level directory so the tag applies consistently.
Create and switch to a branch
# Optional: inspect existing tags/revisions first
cvs -q log filename
# Optional: update workspace to a known tag before branching
cvs -q update -r TAG_VERSION
# Create branch tag
cvs tag -b BRANCH_TAG
# Move workspace to that branch
cvs -q update -r BRANCH_TAG
# Verify sticky branch tag
cvs -q statusCode language: PHP (php)
The original article emphasizes branch verification via sticky tags in cvs status, which is still a useful safety check.
Merge workflow for long-lived CVS branches
When you regularly pull changes from HEAD into a maintenance branch, using tags for merge points makes repeated merges safer and easier to audit.
flowchart TD
A[Update workspace to branch tag] --> B[Verify sticky tag with cvs status]
B --> C[Tag current branch state]
C --> D[Merge head changes]
D --> E{Conflicts found}
E -- yes --> F[Resolve conflicts and retest]
E -- no --> G[Review resulting diff]
F --> G
G --> H[Commit and add new merge tag]
First merge from HEAD into current branch
# From a working copy already set to the branch
cvs update -j HEAD
cvs -q diff -u -r HEAD | less
cvs tag BRANCH_MERGE_TAGCode language: PHP (php)
Repeat merges using two tags
# Merge only the delta between old and new HEAD tags
cvs update -kk -j OLD_HEAD_TAG -j NEW_HEAD_TAGCode language: PHP (php)
The original post recommends tagging each merge point and then using those tags in later merges. It also recommends -kk during merge updates to reduce keyword-related noise in conflicts.
Fallback when previous merge tags are missing
# Legacy fallback pattern from the original article
cvs update -kk -j BASELINE_TAG -j HEADCode language: PHP (php)
This approach is less precise than tagged merge points, so validate carefully with diffs and testing before commit.
Conflict triage and connection tips
The original post included a quick way to open conflicted files in Vim:
vim $(cvs up -d | grep '^C' | sed -e 's/^C //')Code language: JavaScript (javascript)
It also suggested creating a shell alias for compressed CVS traffic:
alias cvs='cvs -z9'Code language: JavaScript (javascript)
Reference links from the original post
- http://kb.wisc.edu/middleware/page.php?id=4087
- http://datagrams.blogspot.com/2009/01/how-to-deploy-cvs-concurrent-versions.html
- http://ximbiot.com/cvs/manual/cvs-1.11.21/cvs_10.html#SEC85
- http://ximbiot.com/cvs/wiki/Special/Index
- More Help!!
Best practices for legacy CVS repositories
- Use consistent, readable tag names for releases and merge points.
- Always verify branch context with cvs status before merge or commit.
- Prefer tagged incremental merges over ad hoc merges from HEAD.
- Review diffs before committing merge results.
- Document your branch and tag naming convention in the repository README.
Original post attribution: Amaresh Das.
I’m Rajesh Kumar, a DevOps, SRE, DevSecOps, Cloud, and Platform Engineering expert passionate about sharing practical knowledge, real-world experiences, and industry best practices. I have worked at Cotocus and regularly write about technology, travel, investing, health, product reviews, and digital marketing through my various platforms.
I publish technical articles at DevOps School, travel stories at Holiday Landmark, stock market insights at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow, and SEO and digital marketing strategies at Wizbrand.
Find Trusted Cardiac Hospitals
Compare heart hospitals by city and services — all in one place.
Explore Hospitals
Really enjoyed this post — you explained how CVS can actually make life easier for realtime developers in a super clear way. I liked the practical angle you took. Short read, but genuinely helpful!