Originally published on 2009-02-02; fully revised and updated.
This guide is for engineers who still maintain code in CVS and need a reliable command-line playbook. It consolidates the original tips into a clearer workflow for inspecting changes, creating branches, tagging releases, and merging HEAD into long-lived branches with less risk.
Core CVS commands for history and status
These commands are the daily basics for understanding what changed and what your working copy contains.
| Command | What it does | Typical use |
|---|---|---|
| cvs log filename | Shows revision history and commit messages for one file | Find revision numbers and prior changes |
| cvs status filename | Shows working revision, repository revision, and sticky tags | Confirm branch/tag context before merge or commit |
| cvs -q diff -u filename | Shows local changes against repository version | Review uncommitted edits |
cvs log filename
cvs status filename
cvs -q diff -u filename
Diff recipes for branches, revisions, and files
Use these patterns when you need to compare specific branches or revisions.
# Diff two branches (entire working tree)
cvs -q diff -u -r BRANCH1 -r BRANCH2
# Diff one file across two branches
cvs -q diff -u -r BRANCH1 -r BRANCH2 filename
# Diff two explicit file revisions
cvs -q diff -u -r VERSION1 -r VERSION2 filename
# Diff working copy vs a specific revision
cvs -q diff -u -r VERSION filenameCode language: PHP (php)
Retrieve specific revisions and switch branch context
When debugging or reproducing behavior, update a file or tree to a known revision or tag. Verify context with cvs status; CVS reports branch/tag state as a sticky tag.
# Update one file to a specific revision
cvs -q update -Pd -r REVISION filename
# Move working copy to a specific tag or branch
cvs -q update -r TAG_VERSION
# Verify sticky tag / branch state
cvs -q statusCode language: PHP (php)
Tagging releases and creating branches
Run tagging and branching from the project top-level directory so the whole module is consistently tagged.
| Operation | Command pattern | Why use it |
|---|---|---|
| Release tag | cvs tag RELEASE_NAME | Create a reproducible release snapshot |
| Create branch | cvs tag -b BRANCH_NAME | Start isolated work for bugfixes or maintenance |
| Switch to branch | cvs -q update -r BRANCH_NAME | Put working copy on the intended branch |
| Branch checkpoint tag | cvs tag BRANCH_CHECKPOINT | Record a rollback/reference point before merges |
# Optional: align to a known tag before branching
cvs -q update -r TAG_VERSION
# Create branch from current context
cvs tag -b BRANCH_36_BUGFIX
# Switch working copy onto that branch
cvs -q update -r BRANCH_36_BUGFIX
cvs -q status
# Tag a branch checkpoint
cvs tag BRANCH_36_BUGFIX_2026_10_12Code language: PHP (php)
Workflow for merging HEAD into a branch safely
The original post emphasizes repeatable merges and tagging discipline. The flow below keeps a clear merge history and easier rollback points.
flowchart TD
A[Update to target branch] --> B[Verify sticky tag with status]
B --> C[Create safety tag on branch]
C --> D[Create current HEAD tag]
D --> E[Merge into branch with update -j]
E --> F{Conflicts found}
F -- yes --> G[Resolve conflicts and review diff]
F -- no --> H[Review diff]
G --> I[Commit merge]
H --> I[Commit merge]
I --> J[Create post merge branch tag]
One-time merge from HEAD into a branch
cvs -q update -r BRANCH_36_BUGFIX
cvs -q status
cvs update -j HEAD
cvs -q diff -u
Repeated merges using tags
For recurring merges, apply only the delta between two HEAD-side tags (old merge point to new merge point). The -kk option is commonly used to reduce keyword-expansion noise during merges.
# Run from branch working copy
cvs update -kk -j UPDATE_HEAD_36_BUG_2026_01_15 -j UPDATE_HEAD_36_BUG_2026_02_01Code language: PHP (php)
If prior merge tags were not created, teams sometimes use a build tag as the older join point and HEAD as the newer point.
cvs update -kk -j DAILY_BUILD_2026_02_01 -j HEAD
Conflict handling and CLI quality-of-life tips
After a merge, capture conflicted files quickly and open them as a batch.
cvs update -dP
cvs update | grep '^C ' | sed -e 's/^C //' > conflicted_files.txt
vi $(cat conflicted_files.txt)Code language: JavaScript (javascript)
The original post also suggests enabling compression for remote CVS traffic:
alias cvs='cvs -z9'Code language: JavaScript (javascript)
Best practices
- Always verify branch context with cvs status before merge or commit.
- Create tags before and after merge operations for rollback and auditability.
- Prefer incremental, tag-to-tag merges over large catch-up merges.
- Review cvs diff -u output before every commit, especially after conflict resolution.
- Use consistent, date-stamped tag naming conventions across the team.
References 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
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