Pliant AllianceSoftware development notes & tools

Why We Switched from CVS to Subversion

For several years, Concurrent Versions System was the quiet machinery behind our software work. It stored source code, recorded who changed what, and gave us a reliable way to recover an older file when a hurried edit went wrong. CVS was familiar, widely documented, and available on nearly every Unix-like system we used. It had earned its place.

By 2005, however, our projects had grown more complicated than the repository model we had originally adopted. Developers were working across Windows and Ubuntu machines, designers were contributing images and documentation, and releases needed cleaner maintenance branches. Subversion, usually abbreviated to SVN, offered a more coherent answer to those demands. The change was less about chasing a fashionable tool than removing friction from ordinary work.

CVS had reached its practical limits

CVS treats files as the primary unit of version control. A project appears to be a collection of tracked files, each with its own history and revision number. That arrangement is workable, but it creates awkward edges around operations that developers increasingly regard as basic: moving a file, renaming a directory, deleting a group of resources, or changing several related files as one logical update.

A rename in CVS is effectively a copy followed by a delete. The repository can preserve some relationship between the two files, but the history is not as transparent as it should be. Directory handling is similarly uneven. CVS tracks directories only indirectly through their contents, which makes an empty directory difficult to represent and gives repository administrators a less precise model of the project tree.

The larger problem was the absence of a dependable atomic commit. CVS submits changes file by file. If a network interruption, authentication problem, or server error occurs in the middle of an update, the repository can briefly contain only part of a developer’s intended change. That possibility mattered when a feature touched source code, configuration, database scripts, and documentation together.

We could work around these limitations with discipline and scripts. We could make small commits, avoid risky directory operations, and repair interrupted submissions. Yet every workaround consumed attention. A version control system should help a team preserve a coherent change, rather than require everyone to remember which actions the tool handles poorly.

Subversion offered a stronger project model

Subversion represented a repository as a versioned filesystem. Instead of assigning an isolated revision number to each file, it advanced a repository-wide revision for every committed transaction. This distinction was valuable because a commit could describe a complete, meaningful change across many paths. A checkout at revision 184, for example, represented a consistent state of the tree rather than a loosely related set of file revisions.

Atomic commits were the most persuasive improvement. A developer could update source, tests, and configuration, then submit them together. Either the complete transaction appeared in the repository or none of it did. That behaviour simplified code review, release preparation, and fault recovery. It also made the log a more useful record of decisions, because one revision could correspond to one feature or defect fix.

Subversion handled copy, move, and delete operations as first-class repository actions. Its cheap-copy mechanism made branching and tagging practical without duplicating the contents of an entire project. A release tag could be created quickly, and a maintenance branch could be opened without turning storage requirements into a barrier. The repository still required good naming and process, but the software no longer fought those habits.

This aligned with the development method we were trying to establish: short changes, visible decisions, and a working tree that could be understood by someone who had not written every line. Version control did not create that method, yet better repository semantics made it much easier to practise consistently.

Branching became a usable part of daily work

CVS branching was possible, but many teams treated it as specialist territory. Branch and merge operations could be difficult to reason about, especially when a branch lived for a long time or when several developers made parallel changes. The tool did not always make the relationship between a branch and its source obvious, so teams often postponed branching until a release was already under pressure.

With Subversion, we could create a branch for a release candidate, experimental feature, or customer-specific maintenance task using a straightforward repository copy. The convention of placing projects beneath trunk, branches, and tags gave everyone a shared vocabulary. These directories were conventions rather than magical repository areas, but that was enough to make the layout predictable.

The practical benefit was a safer separation between ongoing development and stabilisation. The trunk could continue receiving new work while a release branch accepted only tested fixes. If a bug was corrected in the branch, the change could be merged back deliberately instead of being re-created from memory. This reduced the chance that a release fix would disappear from the next development build.

Branching still demanded restraint. A repository can support dozens of branches without making any of them useful. We learned to create branches for a reason, name them clearly, and delete temporary ones when their work was complete. Subversion made branching affordable; it did not remove the need for judgement.

Cross-platform work became less awkward

Our development machines were a mixture of Windows desktops and Ubuntu workstations. That was a normal arrangement for the period: Windows remained common in offices and among clients, while Ubuntu offered an increasingly approachable environment for developers and administrators. CVS worked across both platforms, but the surrounding details could become tiresome.

Subversion’s client choices made the transition easier. Command-line tools suited Linux users and build servers, while graphical clients gave Windows developers a more discoverable way to inspect changes, resolve conflicts, and commit selected files. The repository lived centrally, so a developer in Melbourne and another in Sydney could work from the same history without maintaining separate local conventions.

Line endings needed attention. A file edited on Windows could contain carriage-return and line-feed pairs, while a Unix environment commonly used line feeds alone. We configured Subversion properties such as svn:eol-style for text files and marked binary resources explicitly. Without that discipline, a clean checkout could still produce noisy diffs and avoidable merge conflicts.

The same applied to executable permissions and generated files. Build output, temporary IDE metadata, and local configuration did not belong in source control merely because they appeared in a working directory. Establishing ignore rules early kept the repository focused on material that another developer could check out, build, test, or understand.

The migration required more than importing files

Moving from CVS to Subversion was not a matter of copying the latest directory and declaring success. We wanted to preserve authorship, commit messages, and as much historical continuity as the available conversion tools could provide. The migration therefore began with an inventory of modules, branches, tags, dead files, generated content, and binary assets.

We first removed material that had no place in the new repository. Old build artefacts and private local settings made the CVS history noisy and would have made the SVN repository unnecessarily large. We reviewed inactive modules rather than importing every abandoned experiment. Historical preservation mattered, but preservation did not mean carrying every mistake into a cleaner system.

The conversion itself was tested against a temporary Subversion repository. Developers checked representative files, compared old and new logs, inspected branch points, and verified that tags referred to the expected source state. We also tested a fresh checkout on both Windows and Ubuntu. A migration that looks successful on the server can still fail for ordinary users because of permissions, line endings, missing tools, or an overlooked build dependency.

For the final cutover, CVS became read-only while the last conversion was performed and validated. The team received a short set of rules: where new work belonged, how to create a branch, which files required properties, and how to describe a commit. This was less dramatic than a simultaneous switch-off, but it gave us a known boundary between the old history and the new repository.

Better commits changed everyday collaboration

Subversion improved the quality of communication around a change. A commit could include a concise message, a complete diff, and all related files. When a test failed after an update, we could inspect one revision and understand the intended unit of work. When preparing a release, we could identify the changes that belonged to a branch without reconstructing them from dozens of unrelated file histories.

That mattered for pair programming and informal review. One developer could make a change while another inspected the pending diff before it reached the central repository. The process did not require a heavyweight code review platform. A clean commit, a meaningful message, and a colleague willing to look closely provided a useful baseline for quality.

The repository also became more valuable to people outside the core programming group. Documentation writers could update manuals alongside code. A tester could identify the revision under examination. An administrator could reproduce the configuration associated with a release rather than relying on a folder copied to a server months earlier. For Australian teams working across Sydney, Melbourne, Brisbane, or Perth, that shared reference reduced the ambiguity created by distance and different working hours.

Subversion was still a centralised system. A developer could edit files and inspect some local history while disconnected, but committing required access to the server. A slow office link or an unavailable repository could interrupt the day, and merges still required care. Distributed version control systems would later address some of these limitations more elegantly, but they were not yet the obvious choice for our environment.

The switch was about fewer surprises

The decision to leave CVS was ultimately practical. Subversion gave us atomic commits, clearer filesystem operations, usable branches, and a consistent project-wide revision history. It supported the mixture of languages, documentation, images, operating systems, and release work that had grown around our software. Those advantages reduced small sources of uncertainty that had accumulated over years.

The tool did not make development automatically agile, and it could not compensate for vague requirements or untested code. A team could still create sprawling branches, write unhelpful commit messages, or commit broken builds. Version control remains a social system as much as a technical service: its value depends on the habits formed around it.

Even so, the change improved the texture of ordinary work. We spent less time explaining repository peculiarities and more time discussing the software itself. A release could be tagged with confidence, a fix could be merged deliberately, and a new contributor could understand the project layout without learning a collection of historical exceptions.

CVS had been a sensible choice when our needs were smaller and centralised source control was the main goal. Subversion became the better choice when the project needed transactions, structure, and reliable collaboration across a growing team. We switched because the repository had to reflect the way we were actually building software, not the way we had built it years earlier.