Using Version Control is strongly recommended for all non-trivial projects. For projects with multiple developers, Version Control is almost always a necessity. The advantages of Version Control are compelling:
- it allows groups of people to work simultaneously on a project with minimal friction.
- it allows time travel, in the sense of letting you create a snapshot of the code as it appeared at some specific time in the past.
- it lets you create parallel universes, by letting you define branches for your project. The most common case of this is having a bug-fix branch along side a main trunk.
- it acts as the definitive version of the source.
- it allows for automated daily backups.
More complete information is available from:
- Pragmatic Version Control Using Subversion by Mike Mason
- Pragmatic Version Control Using CVS by Thomas and Hunt
Getting Started With Subversion
- edit the file named %APPDATA%\Subversion\config (no file extension) You will likely want to edit global-ignores, editor-cmd, and set enable-auto-props=yes. There are also settings for pointing to your diff tool.
- consider defining an environment variable SVNROOT to point to your subversion repository
Common Commandssvn checkout %SVNROOT%/blah/trunk
Checks out the trunk of a project named blah.
svn commit -m "Some message" blah.txt
Commits a change made to a file in the current directory.
svn help <command>
Get help on a command.
svn status -uv
Shows status. Recursive by default. Talks to server for any updates that you haven't seen yet.
svn log -v
View change sets.
Occasional Commandssvnadmin create C:\my-repos
Create a local repository.
svn import -m "Importing" . %SVNROOT%/blah/trunk
Import project named blah into a repository. Imports the local directory and all of its subdirectories.
Shows high level information; shows which branch you are on.
svn ls %SVNROOT%
Lists the projects in the repository. Allows you to navigate the repository's tree structure, by the usual drill-down. '-R' is recursive, and shows each file, not just directories.
Branchingsvn mkdir -m "Making first release branch" %SVNROOT%/blah/branch
Create a directory for the blah project to hold all future branches.
svn copy -m "Making Release branch" %SVNROOT%/blah/trunk %SVNROOT%/blah/branch/RB-1.0
Create release branch 1.0. Looks like a directory copy operation.
svn co %SVNROOT%/blah/branch/RB-1.0 blah1.0
Check out release branch 1.0 to directory named blah1.0. The working directory is usually beside blah, which contains the main trunk of development.
svn merge -r11:12 %SVNROOT%/blah/branch/RB-1.0
Merging a fix on a release branch into the trunk. Picks up edits between revision 11 and revision 12. Needs a commit afterwards.
- repository - where CVS stores the source code for projects.
- CVSROOT - an environment variable which points to the repository.
- workspace or working directory - where the developer does their work. A private copy of the code, on the developer's host.
- checkout - the initial fetch of a project's source tree from the repository.
- update - a re-fetch of a file or set of files after the initial checkout.
- commit - after verifying edits are correct, the developer will use a commit to post their edits from their workspace back to the repository.
- revision - version of a file, denoted by a string of numbers, as in 1.2 (on the trunk) or 18.104.22.168 (on a branch).
- trunk or mainline - the main branch of development. Releases are often branched off the trunk.
- dead files - files that are removed from the project aren't really removed. Instead, they are moved into the 'dead' state.
- regular tag - label for a particular set of revisions in the project. Allows you to define specific snapshots of the project, which can be used later to recreate that snapshot. Tags always start with a letter, so you can always distinguish them from revisions (which are numeric, as in 1.2 or 22.214.171.124).
- branch tag - label for a project branch. Always starts with a letter. Branches allow parallel development.
- HEAD - a reserved tag name, refers to the most recent revision in the repository.
- BASE - a reserved tag name, refers to the revision checked out into the workspace. If a developer has modified a file, then the BASE is no longer the same as the workspace copy.
- sticky - many CVS commands are sticky. This means that the command is implicitly applied using the most recent tag that was specified (either regular tag or branch tag). Thus, you can specify a tag once, and then subsequent commands will assume that tag also applies as the default. The idea is to let you work without repeatedly specifying the tag over and over again.
CVS Edit Codes
- ? - an item in the local workspace has not yet been added to the repository
- U - updated (either changed or added new)
- P - updated via a 'patch' (more or less the same as U)
- M - item in the local workspace has been modified after retrieval from the repository
- C - conflicting edits found between two revisions. Such conflicts need manual intervention, since they cannot be automatically merged together.
- A - added to CVS, but not yet committed.
- R - removed from CVS, but not yet committed.
Getting Started With CVS
- to use CVS from the command line, add CVS to your PATH environment variable.
- by default, most commands apply recursively to all subdirectories.
- CVS usually outputs dates and times using Universal Time, but you can often alter the output time zone, using the '-z' option.
- CVS lets you define a simple local repository on your host's file system (see below). When getting started with CVS, it's highly recommended that you create such a 'sandbox' repository, as a safe place to experiment with CVS.
Lists all CVS commands.
cvs -H update
Displays help for the update command.
cvs -d C:\sandbox init
Creates a local 'sandbox' repository, on the local file system. Such a sandbox is very helpful for experimenting with CVS. This is a nice feature, since you don't have to set up a server in order to create the repository. For more formal development, however, your 'real' source code will be stored in a server on another host, not on the local file system.
tmp>cvs import -m "Play around with CVS" myproject myproject initial
Create a new project named 'myproject', built from the current contents of your tmp directory (and subdirectories), and place the new project into your sandbox repository.
work>cvs -d C:\sandbox checkout myproject
Retrieves from the sandbox repository a copy of all files in myproject, and places them into work/myproject. Retrieves the latest version of each file (the HEAD).
Common Commandscvs status Blah.java
Lists general information about the file, whether it has been edited, it's revision, and what branch it's on.
cvs status -q Blah.java
More concise status listing.
cvs -q update -P -d
Updates your workspace with the latest repository files. Performs a merge. Most merges will succeed, but some will fail, and will result in a conflict. Conflicts must be handled manually.
This is a 'sticky' operation (see above). If your workspace is on the trunk, then you are updated with new trunk files. If your workspace is on a branch, then you are updated with files from that branch.
cvs -nq update -P -d
Compares your workspace with the repository without actually doing the update. Does not write to your workspace. The '-n' option means "don't really do this, just show me the current differences between my workspace and the HEAD of the repository."
cvs -q update -P -d -A
Updates your workspace, and ensures you're updated with the trunk, not a branch.
cvs -q udpate -P -d -r REL_1_0
Updates your workspace with a specific release branch.
cvs commit -m "Some appropriate description"
Commits all edits to all files in and under the current directory.
cvs history -c -u myuserid -D yesterday -z AST
Displays what a specific user id committed yesterday, and displays the result in a specific time zone (AST) instead of Universal Time.
Occasional Commandswork>cvs checkout myproject
Fetches an entire project from the repository, and places it in the local directory named work/myproject.
cvs add Blah.java
cvs commit -m "My new file." Blah.java
Adds a new file to the repository. Note that you need to commit the file before the addition is complete.
cvs add -kb jstl.jar
cvs commit -m "Required library." jstl.jar
Adds a new binary file to the repository. Again, you need to commit the file before the addition is complete.
work>cvs remove Blah.java
work>cvs commit "No longer needed." Blah.java
Deletes a file. This has 3 steps.
cvs history -c -D "2010/01/01" -z AST Blah.java
Displays a history of commits on a given file, after a given date, with date-time output in a specific time zone (AST).
cvs log -d today Blah.java
Shows messages for all commits done today for a given file.
cvs -q log -wmyuserid -d"2010/04/13 13:00:00" -S
Shows messages for all commits done by a certain user, after a given date-time
cvs log -rREL_1_0 -wmyuserid Blah.java
Shows the commit messages for a given user id, performed on a specific branch.
work>rename SolarSys.java SolarSystem.java
work>cvs remove SolarSys.java
work>cvs add SolarSystem.java
work>cvs commit -m "Better name." SolarSystem.java
Renames a file. This has 4 steps.
cvs rtag -b RB_1_0 myproject
Creates a release branch named 'RB_1_0' for a project named 'myproject'. This command is applied to the repository, not to the workspace. A release branch can be created before the release, if desired. Then, each release candidate is built using the head of the release branch. When the release is final, then you apply a regular tag to the release branch, to mark the exact point of release.
cvs tag REL_1_0
Tags the repository files in your BASE -- the unmodified revisions checked out to your local workspace.
cvs diff --side-by-side Blah.java
Shows the differences between your workspace and the BASE.
- after you have initially checked out a project to a given local workspace, CVS will remember both the location of the repository, and your login credentials (as long as you don't log out). Thus, there's no need to repeatedly state the location of the repository when issuing CVS commands.
- if you try to commit a file which actually has no differences with the repository file, CVS will not create a new revision.
Merging From Branch To TrunkThere are two ways to merge from a branch into the trunk.
cvs -q update -j 126.96.36.199 -j 188.8.131.52 Blah.java
This is usually the preferred method. Note the presence of 2 '-j' options. This means that only the edits that took the file from 184.108.40.206 to 220.127.116.11 are to be merged into the trunk. This is very helpful, because it can avoid the repetition of possible conflicts that may have already occurred during previous merges.
cvs -q update -j 18.104.22.168 Blah.java
This is usually not the preferred style (see above). This merge has a single '-j' option. The implementation of this merge will need to go back to the common ancestor of the trunk and the branch. The problem with this style is how conflicts are treated. If such a merge has a conflict, it can certainly be resolved, just like any other conflict. However, the conflict will resurface again later should you need to perform another merge between the branch and the trunk.
Separate Workspaces For Each BranchAlthough there are many variations, it's typical to be working on 2 code streams (sometimes more) :
- the trunk : main development for the next release
- a branch : bug fixes for the most recent release, already in production
When working on more than a single branch, it's helpful to be able to switch rapidly between them. It's certainly possible to use a single local workspace to do so, using a 'cvs update' command which specifies the appropriate branch.
However, many find it simpler and more convenient to just use a separate workspace for each branch.
Since the update command is "sticky", and remembers the branch from which a workspace originally came from, there's usually no asymmetry between working on the branch and working on the trunk.