Questions are invited at any time.
pico You should note that CVS will occasionally drop you into a full-screen editor. By default, this is vi. If you use pico, then you need to let CVS know that that is your editor of choice.
CVS follows the standard convention of using the VISUAL
environment variable to specify a full-screen editor. If you want to
use pico, append the following line to your .profile
file in your home directory:
VISUAL=pico export VISUAL
and the corresponding line to your .cshrc file:
setenv VISUAL pico
then restart your shell (logging in again will do this).
I've tried to keep this as concise and cook-booky as possible. I indicate command-line sessions using blocks of text as follows, with the > standing in for your prompt. Stuff that you type is indicated in bold. Responses are shown (where they're interesting).
> echo "hello world" hello world
CVS is organised into modules. Each cvs repository consists of one or more modules. You can think of a module as a file hierarchy with a history.
In day-to-day operation, you do your work in a local copy of a module called your working copy. Getting an initial copy of a module is called checking out the module.
In order to check out a module, you need to tell CVS where the repository is located and the name of the module. The CVS repository location is specified in your CVSROOT environment variable (which you configured as a part of the prep for this session).
I've prepared a small module for you to check out. It is called
example. You can check it out as follows:
> cvs co example cvs server: Updating example U example/README cvs server: Updating example/build U example/build/build.sh cvs server: Updating example/htdocs U example/htdocs/index.html cvs server: Updating example/htdocs/jan U example/htdocs/jan/index.html
Now is the time to yell if this doesn't work.
You should now have a small directory hierarchy starting with
example in your current directory. You can cd
into the directory and have a look around.
You'll notice that as well as normal files and subdirectories,
each directory has a subdirectory called CVS.
The CVS directory contains a number of text files that cvs uses
to do its book-keeping. You will not normally need to edit these
files.
To begin with, you're going to create a subdirectory of
example/htdocs/ to put your own files inside.
The general pattern for using CVS is to use normal file-manipulation
tools to make the changes to your working copy, then use a
cvs command to let CVS know about the changes.
> cd example/htdocs > mkdir john > cvs add john Directory /usr/local/cvsroot/example/htdocs added to the repository
There are two things worth commenting on here.
cvs add john rather
than cvs add john/. CVS won't catch this
problem here but it can get in the way later.
cvs command,
CVS coordinated with the repository to make the change. Most
CVS operations require server contact.
The next step is to create a file. You're going to make a copy of
jan/index.html under your own directory, and tell
CVS about it.
> cd john > cp ../jan/index.html . > cvs add index.html cvs add: scheduling file `index.html' for addition cvs add: use 'cvs commit' to add this file permanently
This is a slightly different message. Although CVS contacts the repository
when you issue the cvs add, it does not put a copy of the
file there. (The repository is checked to ensure there isn't already
a copy of index.html that your working copy doesn't know
about. If there were, CVS would complain to you about it.)
You can edit the file to personalise it (the contents are a purely rudimentary example) using your editor of choice. When you've finally got a version that's ready to check in, you follow this two-command step to do so:
> cvs up cvs update: Updating . A index.html > cvs ci
At this point, CVS will launch a full-screen editor and prompt you for a commit message. This message is associated with the changes you're making to the repository. Things that might be worth noting here are the reason for the change (a bug fix, a brief explanation of a new feature you're introducing, etc.); however, since CVS makes the precise nature of the change available (we'll see how later), you should avoid merely describing your changes literally. A high-level summary is better.
CVS: ---------------------------------------------------------------------- CVS: Enter Log. Lines beginning with `CVS:' are removed automatically CVS: CVS: Committing in . CVS: CVS: Added Files: CVS: index.html CVS: ----------------------------------------------------------------------
Alternatively, if you wish to specify a small commit message
(sometimes called a log message), you can do so on the
command line using the -m flag:
> cvs ci -m "A short commit message" cvs commit: Examining . RCS file: /usr/local/cvsroot/example/htdocs/john/index.html,v done Checking in index.html; /usr/local/cvsroot/example/htdocs/john/index.html,v <-- index.html initial revision: 1.1 done
After you finish giving the log message, CVS completes the operation and updates the repository.
Terminology: occasionally you will hear a checkin referred to as a
putback. They mean the same thing but CVS only knows about
cvs ci, so cvs pb will just
make it complain.
There isn't a great deal more to CVS operation than that. You can continue to add files, or make changes to existing files. When you've got a set of changes ready to put back, you simply issue the two-step sequence again:
> cvs up > cvs ci
The sequence of operations to delete a file is pretty straightforward.
First, you remove the file using normal file-manipulation commands.
Then, you tell CVS to schedule the removal of that file: here's
an example. We create and add the file my_file, then
remove it. I've elided server responses that we've already seen.
> touch my_file > cvs add my_file > cvs ci my_file
Now the file has been added. We delete it as follows:
> rm my_file > cvs rm my_file cvs remove: scheduling `foo' for removal cvs remove: use 'cvs commit' to remove this file permanently > cvs ci my_file
At this point, CVS reports a slightly different change when it asks you for a commit message:
CVS: ---------------------------------------------------------------------- CVS: Enter Log. Lines beginning with `CVS:' are removed automatically CVS: CVS: Removed Files: CVS: my_file CVS: ----------------------------------------------------------------------
Finally, CVS completes the operation:
Removing foo; /usr/local/cvsroot/example/htdocs/john/foo,v <-- foo new revision: delete; previous revision: 1.1 done
CVS knows about two kinds of files: text files (the default)
and binary files. When you check out a copy of a text file,
CVS will politely change all the end-of-line characters to
conform to your client machine's convention. It will also
expand certain special character sequences within the file
(eg, $Revision: 39 $) - which is where those odd
version numbers you may have seen on the bottom of web pages come from.
This is great unless you're trying to use CVS to store a file with a binary format, such as PNG. There are two options you can use to control CVS' behaviour when you first add a file. The first tells it to treat the file as a text one, but not to perform any keyword expansion:
> cvs add -ko my_file.txt
The second option tells CVS to store the file as a raw, binary file. If you do this it will be unable to offer you meaningful "difference" information - but since CVS' diffs are text-based (as we'll see), this isn't a great loss.
> cvs add -kb my_file.png
You can see the flags on a file by using the cvs status
command. The flags are listed as Sticky Options.
> cvs status index.html my_face.png =================================================================== File: index.html Status: Up-to-date Working revision: 1.1 Mon Jul 7 21:33:00 2003 Repository revision: 1.1 /usr/local/cvsroot/example/htdocs/john/index.html,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none) =================================================================== File: my_face.png Status: Up-to-date Working revision: 1.1 Mon Jul 7 21:34:23 2003 Repository revision: 1.1 /usr/local/cvsroot/example/htdocs/my_face.png,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: -kb
I'll explain the other fields shortly.
Unfortunately, CVS has no direct equivalent of the mv
command. To rename a file, you need to use mv src dest to move
the source file to the destination, then cvs rm src and
cvs add dest before finally committing the change with
cvs ci src dest.
This is unfortunately somewhat klunky. In addition, CVS does not know the relationship between the contents of the source and destination files; thus, this operation will effectively mean that the destination file doesn't carry any of the change history of the source file.
The situation with directories is slightly more awkward. CVS versions file contents, not file names, not file permissions (or other metadata), and not directories.
In fact, this is one of CVS' biggest niggles. You need to be aware of the issue and to plan your directory structure a bit before you begin to compensate.
Up to now, you've worked in separate directories. CVS can actually manage multiple people working on files in the same directory; it can even intelligently merge two people's changes to the same file! However, CVS is not a panacea.
For this session, we can coordinate by yelling across the room. In the wild, CVS is best used in conjunction with some other channel of communication. The value of a mailing list for the developers on your project really cannot be underestimated. If you are planning some changes to a set of files, warn people beforehand!
When you make a commit, your single log message gets attached to the
changes to every file involved in the putback. Therefore, you may want
to separate your commits into small logical parcels by specifying
a few files or directories at a time when you use the cvs ci
command.
Now that everyone's made some changes and put them back, we'd like to get a revised view from the repository. I'm going to demonstrate a common "gotcha!" when using CVS here.
If you move back into the htdocs directory, you can run
cvs up to call down updates from the repository.
However, if you do this (try it now!) you don't automatically get a copy
of the new subdirectories that the other people have added.
By default, CVS will recurse over the current directory and over subdirectories that already exist in your working copy. If you want it to check for new directories, you need to tell it like this:
> cd .. > cvs up -d [cvs output elided]
This option is easy to forget but about 98% of the time you run an update,
you're interested in new directories. You can get CVS to supply this flag
automatically by putting the following line in the .cvsrc
file in your home directory:
update -d
OK, now you should have an up-to-date copy of the htdocs directory and all the new subdirectories in your working copy.
You can explore the log messages of other people's commits now by using
the cvs log command:
> cvs log jane/index.html RCS file: /usr/local/cvsroot/example/htdocs/jane/index.html,v Working file: jane/index.html head: 1.1 branch: locks: strict access list: symbolic names: keyword substitution: kv total revisions: 1; selected revisions: 1 description: ---------------------------- revision 1.1 date: 2003/07/07 21:55:03; author: jane; state: Exp; a short commit message =============================================================================
OK, now we're going to look at what happens when multiple people make changes to the same file. Quite often, CVS will be able to do "the right thing". We'll conduct this as an experiment with multiple people gathering around keyboards (it's clearer what's going on that way).
In the top htdocs directory is another index.html
file. Make two edits to this: in the first, just add your name to the first
line ("People on the course were:") and a link to your subdirectory
similar to the one to "jan's pages". In the latter case, keep the
lines in alphabetical order. Don't check in your changes yet! When everyone's
ready, shout, and we'll gather around someone's keyboard to do the checkin.
This section should be done with everyone in the same place! Don't go past this point until I tell you.
The first person's checkin of the new top-level index.html goes
perfectly:
> cvs up cvs update: Updating . M index.html cvs update: Updating jane cvs update: Updating john > cvs ci -m "jane added to top page" index.html Checking in index.html; /usr/local/cvsroot/example/htdocs/index.html,v <-- index.html new revision: 1.2; previous revision: 1.1 done
Now it's worthwhile looking slightly more closely at the cvs up
output. Each line corresponds to a file. The character at the start of the line represents
the file's status. The M means that the repository latest version has
not changed, but the file has been modified locally in the working copy. OK so far -
so we proceed with the commit as normal.
Now, we shift to the second person's keyboard. Here, things don't quite go as before:
> cvs up cvs update: Updating . RCS file: /usr/local/cvsroot/example/htdocs/index.html,v retrieving revision 1.1 retrieving revision 1.2 Merging differences between 1.1 and 1.2 into index.html rcsmerge: warning: conflicts during merge cvs update: conflicts found in index.html C index.html cvs update: Updating jane cvs update: Updating john
What happened here? Well, normally when a file has been changed in the
repository, CVS will synchronise quietly by updating your local copy
of the file (the status letter is U or P).
Here, however, the local changes we'd made to the file were too close
to the changes made in the repository; so CVS complains with a
C status letter (standing for conflict).
Before we can go any further, we must resolve the conflicts. Let's look at the conflicting file with our editor.
<html> <head> <title> An introduction to CVS </title> </head> <body> <h1> These documents are under source-code control </h1> <<<<<<< index.html <p> People on the course were: jan, john </p> ======= <p> People on the course were: jan, jane </p> >>>>>>> 1.2 <ul> <li> <a href="jan/">jan's pages</a> </li> <<<<<<< index.html <li> <a href="john/">john's pages</a> </li> ======= <li> <a href="jane/">jane's pages</a> </li> >>>>>>> 1.2 </ul> </body> </html>
Yikes! What happened here? Well, CVS has highlighted sections that were too close together for it to merge successfully. Let's look more closely at the first section:
<<<<<<< index.html <p> People on the course were: jan, john </p> ======= <p> People on the course were: jan, jane </p> >>>>>>> 1.2
What this is saying that the section (in this case, a single line)
that corresponds to our local copy, index.html, has
the "jan, john" text. However, the corresponding section
in the latest repository copy (revision number 1.2) has text that
reads "jan, jane". CVS doesn't know what to do here -
it merges lines, but can't merge intra-line changes - so it relies
on the user to resolve the conflict. We can replace these five lines
with the single, correctly merged result:
<p> People on the course were: jan, jane, john </p>
Resolving comflicts is simply the case of repeating this procedure until no conflicts are left in the file. When that is the case, we try the update again:
> cvs up index.html M index.html
Now CVS tells us that there are no longer conflicts; simply local modifications. At this point we can finish the checkin:
> cvs ci -m "john's changes" index.html Checking in index.html; /usr/local/cvsroot/example/htdocs/index.html,v <-- index.html new revision: 1.3; previous revision: 1.2 done
Conflicts can usually be avoided by two things: firstly, ensuring
that you run a cvs up prior to a checkin and
clearing up any awkward merges it reports. Secondly, and this
really cannot be understated, communicate what you're
doing on your handy developer email list!
By carefully laying out your file tree before you start, and allocating informal "ownership" of various portions of your file hierarchy, and by good communication, multiple developers can work happily on a shared CVS module with a minimum of fuss.
Now that you've reached this point, you have sufficient know-how to make good day-to-day use of CVS. It really is that straightforward! What follows for the rest of the sessions will be a quick look at some of the other things you can get CVS to do for you.
I've called CVS a filesystem with a history. It's possible to "go back in time" to get views of your source code earlier in time. In fact, you can label (or tag) points in time and ask to get a copy of the code as it looked then.
I laid down a tag prior to the session start. Here's the command I executed in the top level directory:
> cvs tag -cR T_INITIAL cvs server: Tagging . T README cvs server: Tagging build T build/build.sh cvs server: Tagging htdocs T htdocs/index.html cvs server: Tagging htdocs/jan T htdocs/jan/index.html
In other words, I marked that moment in time with the label T_INITIAL.
Now, it's possible to get a copy of the module at that point in time like this. Which
leads to an important point: just as different developers all have their own
working copies, there is no reason why you should not check out multiple working
copies yourself. Here's how I can get a copy of the initial example directory state:
> cd > mkdir eg-initial > cd eg-initial > cvs co example cvs server: Updating example U example/README cvs server: Updating example/build U example/build/build.sh cvs server: Updating example/htdocs U example/htdocs/index.html cvs server: Updating example/htdocs/jan U example/htdocs/jan/index.html > cd example > cvs up -r T_INITIAL cvs server: Updating . cvs server: Updating build cvs server: Updating htdocs cvs server: Updating htdocs/jan
(The two-step checkout and update shouldn't strictly be necessary: I should be able
to simply use cvs co -r T_INITIAL example - however, some CVS
versions have a bug and this is how I work around it.)
Now I've got a second copy of the module, checked out as it was initially. I treat this as a read-only copy, a reference point. The working copy remembers that it is tracking a particular tag, however, as can be seen here:
> cvs status README =================================================================== File: README Status: Up-to-date Working revision: 1.2 Repository revision: 1.2 /usr/local/cvsroot/example/README,v Sticky Tag: T_INITIAL (revision: 1.2) Sticky Date: (none) Sticky Options: (none)
The "Sticky Tag" means that any updates on that file will bring it in line with the version in the repository that's marked by that tag.
Why is this useful? Well, amongst other things, it's a very simple way of
keeping a single line of "production" and "development" under CVS control.
Developers can check out the latest copy as normal. When they've produced
a stable set of files, they can tag that point in time. (A tag can be
moved by using cvs tag -cFR TAGNAME - the old position
of the tag is lost when you do this.) Meanwhile, a working copy is
kept checked out to the LAST_KNOWN_GOOD tag (say) on the production machine.
By moving this tag forwards as they make improvements, the main production
copy of the system remains stable. (This tactic is in use on the SPP project.)
If you have a developer list, it is quite handy to see the commit logs for a particular project as developers work on them. We can set you up with a commit list - this is simply a mailing list (that generally parallels the developer list) that all commit logs get send to, as checkins happen.
To get a commit list set up, you need to ask ilrt-sysadmin.
If your project lives on our main CVS server, you can browse the CVS repository directly using CVSweb. This is a web interface that lives at http://cvs.ilrt.org/cvsweb/.
(I'll demonstrate this interface briefly now.)
If you look at any open-source project, you'll probably find that they offer public access to their latest "developer snapshot". This is normally done via what's called anonymous CVS - that is, read-only access to a repository that doesn't require a login on the CVS server.
If you need it, we can offer anonymous CVS access to your projects.
The example module is also available via anonymous CVS.
Here's how I fetched it earlier:
> cd > mkdir anon > cd anon > cvs -d :pserver:anonymous@cvs.ilrt.org:/cvsroot co example cvs checkout: warning: failed to open /home/jan/.cvspass for reading: No such file or directory cvs server: Updating example U example/README cvs server: Updating example/build U example/build/build.sh cvs server: Updating example/htdocs U example/htdocs/index.html cvs server: Updating example/htdocs/jan U example/htdocs/jan/index.html
It's possible to configure our CVS repository to trigger pretty much arbitrary processing when commits are made. That's how SPP manages its commit list. It also has a developer website (managed as part of the SPP CVS module). As changes are checked into the website portion of the tree, the live website is updated to reflect those changes.
OK, so let's say you're convinced, and want to start using CVS for your own projects. The important decisions it's worth making early are: how you're going to lay out your module directory tree; what you're going to commit; when you're going to commit; and how you can use simple automation to make your life easy. I present some simple guidelines here.
If you have multiple developers working on your module, it's worthwhile trying to avoid conflicts as much as possible. If you can partition work up so that it naturally falls into separate directories, you'll find people treading on each others' toes much less frequently.
Even if you are only keeping a simple, static website under version control initially, avoid the temptation to put the top level of the htdocs hierarchy right in the top directory of your module. That way, you've room to add source code, build scripts, XSLT stylesheets, etc, in the future.
Commit source files. If those source files are processed to produce some end product, commit the programs that do the processing. Generally, however, you don't want to commit the end results. Instead, you should try to automate your module so that anyone can check out the "starting conditions" and run your build process to produce the results for themselves.
For example, you might want to commit source code (.c files) and Makefiles,
but you don't want to commit the compiled results (the .o files and a.out).
You can stop CVS complaining about these files by using a .cvsignore file,
as we'll see in a minute.
The one exception to not checking in compiled files may be where you're using a third-party library. There, it's sometimes worthwhile to check a copy of that library in. Don't forget to mark it as a binary file!
Up to now, we've discussed static layout of files. Now we move on to policy and process issues.
The more developers you have working on a project, the more some simple guidelines about what makes a good commit are necessary. These don't have to be onerous.
For example, if you're checking in source-code, you should ensure that (at least) the code doesn't "break the build" - that is, it should compile, and the system should still run.
If you're running with a level of project automation that includes automated testing, then you should endeavour to ensure that all your test code runs correctly prior to a checkin. In particular, you should be careful to avoid "regressions" where changes you make will cause other people's code to fail the tests. Automation is largely about producing a development environment that your developers can trust. If you damage that trust by making automated tests worthless, you're doing yourself (and your project) a disservice.
This brings us neatly on to automation.
A small amount of automation can work wonders. Even our trivial example project can use automation. One of the benefits of CVS is that it lets multiple developers work in parallel on their own copy of the project. In order to maximise this benefit, it'd obviously be nice if they can roll out a running version from their own working copy. Rather than have them do this by hand, we'd like to make the process as simple as possible - so the developers can concentrate on manifesting Excellence(tm) in their work!
OK, what can we do to automate the production of a website? Well, we can make it
available on the web, for a start. I've written a small script that can be used to copy
our HTML files to a subdirectory of your public_html directory. (Providing
you configure this correctly, files in your top-level directory will not be overwritten.)
You can take advantage of this small piece of automation as follows: firstly, use cd
to move into your top-level example directory. You should be able to see
the README file, and the build and htdocs
directories.
To run the deployment script, you type the following:
> sh build/build.sh deploy
This is just a Unix shell script. From here on, the details I'm giving are particular to this script (which just copies files from one place to another). Obviously your own project might require a different set of operations to build; in which case, the specifics would vary - I'm just attempting to illustrate the principle of automation here.
Initially, this script will complain. What's going on here? Well, one of the things that can differ between developers is the precise detail of how they want this project deployed. In our case, we need to specify a destination directory for our HTML files (although nothing else; this deployment is trivial).
The idea of separating out local parameters for a deployment is an important automation pattern.
how do we supply these parameters? In this case, the build script looks for a file called
build.properties that contains a single line specifying the destination directory.
My copy of the file looks like this:
DESTDIR=/space/home/cmjg/public_html/example
You should create this file (with the appropriate value) in your working copy - but do not check it in. This is important: this file is used to control the way your working copy is deployed. (Obviously, if I was running a production version of this website, I would be able to use the same script to target my production webserver.)
Once you have created the file, you should be able to run the deployment script to completion:
> sh build/build.sh deploy Deploy progress: Making destination directory /space/home/cmjg/public_html/example Copying across HTML contents mkdir: created directory `/space/home/cmjg/public_html/example/.' mkdir: created directory `/space/home/cmjg/public_html/example/./jan' `htdocs/./jan/index.html' -> `/space/home/cmjg/public_html/example/./jan/index.html' `htdocs/./index.html' -> `/space/home/cmjg/public_html/example/./index.html' Setting destination directory world-readable
At this point, I've got a deployed version of my project (and you should have too!). You can view my copy of this here: http://mail.ilrt.bris.ac.uk/~cmjg/example/ and your copy will be analogously available.
There's one final tweak we can make. If you run cvs up in your
example directory now, you'll see CVS carp about the build.properties
file - it hasn't been told about it.
> cvs up ? build.properties cvs server: Updating . cvs server: Updating build cvs server: Updating htdocs cvs server: Updating htdocs/jan
You can tell CVS to ignore files when it scans a directory. You do this by adding
filenames ("*"-patterns can be used too) to a file called .cvsignore
- this will have the desired effect. Of course, .cvsignore is just
another file, so if we want every working copy to come with it preconfigured
to ignore build.properties, we should check it in:
> echo "build.properties" >> .cvsignore > cvs add .cvsignore > cvs ci -m "Stop CVS carping about build.properties" .cvsignore
Phew! That's all the practical stuff for today. What's left are some odds and sods and the final opportunity for questions. (Although in general, CVS questions are welcomed on ilrt-tech).
If you want to version-control your project using CVS, your first port of call should be the ilrt-sysadmin mailing list. There we can deal with the generalities. You need to think about who will be working on the project (we don't need to know about everyone in advance) and whether it will need to be accessible for contributions from the non-ILRT people. It normally only takes about ten minutes to get a new project off the ground.
If you're after specific help with planning build automation (with or without CVS), then ilrt-tech is the right place to go.
Occasionally you may want to get the latest and greatest copy of a piece of open-source software. They may well offer anonymous CVS access. An example is the Apache Foundation's Jakarta project: http://jakarta.apache.org/site/cvsindex.html. They've got simple instructions there. I'm not going to demonstrate this fully because it can take a while to download some of their software, but there really is nothing more to getting hold of software via anonymous CVS than that!
Troubleshooting tip: occasionally (and particularly noticeably if you use vi) you'll
come across a text file that's full of MS-DOS linefeeds. You can tell this because
vi shows you the ^M at the end of every line.
If this is happening it's because someone is using a version of CVS that thinks it's running on Unix in conjunction with an editor that thinks it's on MS-DOS. This can arise in a number of ways: for example, by using Samba to export a filesystem from Unix (where the CVS operations are performed) to a Windows desktop (where the files are edited).
Although it's tempting to just fix the files and check them back in, be careful. CVS will consider that to be a change to every line of the file and correspondingly think the change is larger than you anticipated. If this does occur, there are two steps to correct the problem:
In general, direct manipulation of the central repository is not for the uninitiated. It can have an impact on everyone's working copy and needs to be carefully coordinated with your developers.
We mentioned the problem of lost history that arises when CVS "renames" a file. This can be mitigated somewhat be a repo-copy. This is where a CVS administrator copies the change history from one file in the CVS repository to another. The result is that the destination file "miraculously" inherits the change log of the source file.
For large-scale reorganisation of projects, it can be simpler to work directly in the repository. You should try to plan to avoid this; however, if you need to shuffle your project drastically then you should contact ilrt-sysadmin for assistance.
Here are two more things that can catch you during day-to-day operation of CVS.
> cvs add my_face.png
When you add a binary file, it's easy to forget to specify the -kb flag.
Unfortuantely, it's a pain to get CVS to change the flag for you. A quick way to correct this error
is to edit the CVS/Entries file. You'll see that this file has one line
per version-controlled file. Identify the line that's wrong:
/my_face.png/0/Initial my_face.png//
and change it so that -kb appears between the final slash marks:
/my_face.png/0/Initial my_face.png/-kb/
Now, you're a real CVS wizard! cvs status will report the
file as being marked as a binary, and CVS will be able to deal with it correctly:
> cvs status my_face.png =================================================================== File: my_face.png Status: Locally Added Working revision: New file! Repository revision: No revision control file Sticky Tag: (none) Sticky Date: (none) Sticky Options: -kb
If you've got as far as checking in the file, you need to look at the
cvs admin command. There are specific details on
this in the CVS Book.
Occasionally, you just want to wipe the slate clean. Fortunately, CVS is pretty forgiving in this regard. If things go badly wrong in a working copy, you can (generally) move (rename or delete) the offending files "out of the way" and run an update, which should restore your working copy to a reasonably sane state.
(If you use the rename rather than delete option, you still have the chance to then salvage - by hand, alas - any very recent changes you'd made to the files.)
Although it has Unix roots, CVS is available on Windows too. There are two options: Cygwin and WinCVS.
Cygwin will essentially give you a bunch of Unix shell utilities. If you select it for inclusion, you'll get CVS. However, you'll get a CVS that "knows" that it's running on Unix, and will consequently adopt the Unix line-end convention. If you are planning on going down this route, then you'd be well advised to get a Windows editor that can understand Unix line-ends (PFE or TextPad are examples).
WinCVS comes in two flavours. There are command-line utilities (which know they're running with MS-DOS line-ends!) and a graphical CVS client, about which I have some reservations. I tend to find that once you've got a crib sheet of the common CVS operations (adding, editing, updating) a graphical client isn't as convenient as the command-line tools - possibly used in conjunction with CVSWeb.
I've instructions on setting up a Windows environment to use CVS here: http://ioctl.org/unix/cvs/client-win32.
After this whistle-stop tour, you might want to take a look at the on-line CVS Book [http://cvsbook.red-bean.com/] which has more details - plus a very good FAQ section.