paraex.me/smart home ex存在多久了

Open Source Development With CVS
Open Source Development with CVS, 3rd Edition
by Karl Fogel and Moshe Bar
Copyright &
Karl Fogel &kfogel@&
This docum you can redistribute and/or modify
it under the terms of the GNU General Public License as published by
the Free Software F either version 2, or (at your option)
any later version.
This document is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
GNU General Public License for more details.
This manual describes how to use and administer CVS (Concurrent Versions
It is part of a larger work entitled Open Source
Development With CVS; please see the introduction for details.
This is version 1.21 of this manual.
Short Contents
Table of Contents
What is this book?
Basic CVS usage -- a tutorial.
How to run a CVS repository.
What the gurus know.
FAQs and real-life experiences.
A reference to CVS commands, variables, etc.
Other tools that work with CVS.
Previous:&,
Introduction
This is a set of free, online chapters about using CVS (Concurrent
Versions System) for collaboration and version control.
everything from CVS installation and basic concepts all the way to
advanced usage and administration.
It is intended for anyone who uses
or plans to use CVS.
These chapters are excerpted from a larger work called Open Source
Development With CVS (published by , ISBN 1-).
The remainder of that book -
chapters 1, 3, 5, and 7 - deals with the challenges and philosophical
issues of running an Open Source project using CVS.
While the free chapters here constitute a complete CVS book by
themselves, we certainly hope you'll like them enough to purchase a
treeware copy of the entire book!
You can order it directly from the
publisher, at
These chapters are released under the
For more information about free software in general, visit
, and particularly
To submit comments or errata regarding any of this material, please send
email to .
For news and updates, visit
Previous:&,
An Overview of CVS
I can't imagine programming without it... that would be like
parachuting without a parachute!
-Brian Fitzpatrick on CVS
This chapter introduces the fundamentals of CVS, and then provides an
in-depth guided tour of everyday CVS usage.
Concepts are presented
sequentially, so if you're new to CVS, the best way to read this is to
start at the beginning and go straight through, without skipping
How to think like CVS.
Guided tour through a sample CVS session.
Random useful things.
Splitting development into parallel streams.
Basic Concepts
If you've never used CVS (or any version control system) before, it's
easy to get tripped up by some of its underlying assumptions.
seems to cause the most initial confusion about CVS is that it is used
for two apparently unrelated purposes: record keeping and collaboration.
It turns out, however, that these two functions are closely connected.
Record keeping became necessary because people wanted to compare a
program's current state with how it was at some point in the past.
example, in the normal course of implementing a new feature, a developer
may bring the program into a thoroughly broken state, where it will
probably remain until the feature is mostly finished.
Unfortunately,
this is just the time when someone usually calls to report a bug in the
last publicly released version.
To debug the problem (which may also
exist in the current version of the sources), the program has to be
brought back to a useable state.
Restoring the state poses no difficulty if the source code history is
kept under CVS.
The developer can simply say, in effect, "Give me the
program as it was three weeks ago", or perhaps "Give me the program as
it was at the time of our last public release".
If you've never had
this kind of convenient access to historical snapshots before, you may
be surprised at how quickly you come to depend on it.
Personally, I
always use revision control on my coding projects now - it's saved me
many times.
To understand what this has to do with facilitating collaboration, we'll
need to take a closer look at the mechanism that CVS provides to help
numerous people work on the same project.
But before we do that, let's
take a look at a mechanism that CVS doesn't provide (or at least,
doesn't encourage): file locking.
If you've used other version control
systems, you may be familiar with the lock-modify-unlock development
model, wherein a developer first obtains exclusive write access (a lock)
to the file to be edited, makes the changes, and then releases the lock
to allow other developers access to the file.
If someone else already
has a lock on the file, they have to "release" it before you can lock it
and start making changes (or, in some implementations, you may "steal"
their lock, but that is often an unpleasant surprise for them and not
good practice!).
This system is workable if the developers know each other, know who's
planning to do what at any given time, and can communicate with each
other quickly if someone cannot work because of access contention.
However, if the developer group becomes too large or too spread out,
dealing with all the locking issues begins to chip
it becomes a constant hassle that can discourage people from getting
real work done.
CVS takes a more mellow approach.
Rather than requiring that developers
coordinate with each other to avoid conflicts, CVS enables developers to
edit simultaneously, assumes the burden of integrating all the changes,
and keeps track of any conflicts.
This process uses the
copy-modify-merge model, which works as follows:
Developer A requests a working copy (a directory tree containing the
files that make up the project) from CVS.
This is also known as
"checking out" a working copy, like checking a book out of the library.
Developer A edits freely in her working copy.
At the same time, other
developers may be busy in their own working copies.
Because these are
all separate copies, there is no interference - it is as though all of
the developers have their own copy of the same library book, and they're
all at work scribbling comments in the margins or rewriting certain
pages independently.
Developer A finishes her changes and commits them into CVS along with a
"log message", which is a comment explaining the nature and purpose of
the changes.
This is like informing the library of what changes she
made to the book and why.
The library then incorporates these changes
into a "master" copy, where they are recorded for all time.
Meanwhile, other developers can have CVS query the library to see if the
master copy has changed recently.
If it has, CVS automatically updates
their working copies.
(This part is magical and wonderful, and I hope
you appreciate it.
Imagine how different the world would be if real
books worked this way!)
As far as CVS is concerned, all developers on a project are equal.
Deciding when to update or when to commit is largely a matter of
personal preference or project policy.
One common strategy for coding
projects is to always update before commencing work on a major change
and to commit only when the changes are complete and tested so that the
master copy is always in a "runnable" state.
Perhaps you're wondering what happens when developers A and B, each in
their own working copy, make different changes to the same area of text
and then both commit their changes? This is called a conflict, and
CVS notices it as soon as developer B tries to commit changes.
of allowing developer B to proceed, CVS announces that it has discovered
a conflict and places conflict markers (easily recognizable textual
flags) at the conflicting location in his copy.
That location also
shows both sets of changes, arranged for easy comparison.
Developer B
must sort it all out and commit a new revision with the conflict
Perhaps the two developers will need to talk to each other to
settle the issue.
CVS only alerts the developers that there is a
it's up to human beings to actually resolve it.
What about the master copy? In official CVS terminology, it is called
the project's repository.
The repository is simply a file tree kept on
a central server.
Without going into too much detail about its
structure (but see ), let's look at what
the repository must do to meet the requirements of the
checkout-commit-update cycle.
Consider the following scenario:
Two developers, A and B, check out working copies of a project at the
same time.
The project is at its starting point - no changes have been
committed by anyone yet, so all the files are in their original,
pristine state.
Developer A gets right to work and soon commits her first batch of
Meanwhile, developer B watches television.
Developer A, hacking away like there's no tomorrow, commits her second
batch of changes.
Now, the repository's history contains the original
files, followed by A's first batch of changes, followed by this set of
Meanwhile, developer B plays video games.
Suddenly, developer C joins the project and checks out a working copy
from the repository.
Developer C's copy reflects A's first two sets of
changes, because they were already in the repository when C checked out
Developer A, continuing to code as one possessed by spirits, completes
and commits her third batch of changes.
Finally, blissfully unaware of the recent frenzy of activity, developer
B decides it's time to start work.
He doesn't bother to update his
he just commences editing files, some of which may be files that A
has worked in.
Shortly thereafter, developer B commits his first
At this point, one of two things can happen.
If none of the files
edited by developer B have been edited by A, the commit succeeds.
However, if CVS realizes that some of B's files are out of date with
respect to the repository's latest copies, and those files have also
been changed by B in his working copy, CVS informs B that he must do an
update before committing those files.
When developer B runs the update, CVS merges all of A's changes into B's
local copies of the files.
Some of A's work may conflict with B's
uncommitted changes, and some may not.
Those parts that don't are
simply applied to B's copies without further complication, but the
conflicting changes must be resolved by B before being committed.
If developer C does an update now, she'll receive various new changes
from the repository: those from A's third commit, and those from B's
first successful commit (which might really come from B's second
attempt to commit, assuming B's first attempt resulted in B being forced
to resolve conflicts).
In order for CVS to serve up changes, in the correct sequence, to
developers whose working copies may be out of sync by varying degrees,
the repository needs to store all commits since the project's beginning.
In practice, the CVS repository stores them all as successive diffs.
Thus, even for a very old working copy, CVS is able to calculate the
difference between the working copy's files and the current state of the
repository, and is thereby able to bring the working copy up to date
efficiently.
This makes it easy for developers to view the project's
history at any point and to revive even very old working copies.
Although, strictly speaking, the repository could achieve the same
results by other means, in practice, storing diffs is a simple,
intuitive means of implementing the necessary functionality.
process has the added benefit that, by using patch appropriately, CVS
can reconstruct any previous state of the file tree and thus bring any
working copy from one state to another.
It can allow someone to check
out the project as it looked at any particular time.
It can also show
the differences, in diff format, between two states of the tree without
affecting someone's working copy.
Thus, the very features necessary to give convenient access to a
project's history are also useful for providing a decentralized,
uncoordinated developer team with the ability to collaborate on the
For now, you can ignore the details of setting up a repository,
administering user access, and navigating CVS-specific file formats
(those will be covered in ).
moment, we'll concentrate on how to make changes in a working copy.
But first, here is a quick review of terms:
Revision A committed change in the history of a file or set of
A revision is one "snapshot" in a constantly changing project.
Repository The master copy where CVS stores a project's full
revision history.
Each project has exactly one repository.
Working copy The copy in which you actually make changes to a
There can be many working copie generally
each developer has his or her own copy.
Check out To request a working copy from the repository.
working copy reflects the state of the project as of the moment you
when you and other developers make changes, you must use
commit and update to "publish" your changes and view others' changes.
Commit To send changes from your working copy into the central
repository.
Also known as check-in.
Log message A comment you attach to a revision when you commit it,
describing the changes.
Others can page through the log messages to get
a summary of what's been going on in a project.
Update To bring others' changes from the repository into your
working copy and to show if your working copy has any uncommitted
Be careful not to conf they are
complementary operations.
Mnemonic: update brings your working copy up
to date with the repository copy.
Conflict The situation when two developers try to commit changes
to the same region of the same file.
CVS notices and points out
conflicts, but the developers must resolve them.
Previous:&,
A Day With CVS
This section describes some basic CVS operations, then follows with a
sample session covering typical CVS usage.
As the guided tour
progresses, we'll also start to look at how CVS works internally.
Although you don't need to understand every last detail of CVS's
implementation to use it, a basic knowledge of how it works is
invaluable in choosing the best way to achieve a given result.
more like a bicycle than an automobile, in the sense that its mechanisms
are entirely transparent to anyone who cares to look.
bicycle, you can just hop on and start riding immediately.
However, if
you take a few moments to study how the gears work, you'll be able to
ride it much more efficiently.
(In the case of CVS, I'm not sure
whether transparency was a deliberate design decision or an accident,
but it does seem to be a property shared by many free programs.
Externally visible implementations have the advantage of encouraging the
users to become contributing developers by exposing them to the system's
inner workings right from the start.)
Each part of the tour may make use of knowledge introduced in previous
Therefore, if this is your first time, I recommend that you
simply start at the beginning and take the tour sequentially, without
skipping over anything.
The menu below is merely meant as a convenience
for repeat visitors - you shouldn't use it to jump directly to a
section that interests you unless you're already familiar with the
material in the previous sections.
Conventions Used In This Tour
The tour takes place in a Unix environment.
CVS also runs on Windows
and Macintosh operating systems, and Tim Endres of Ice Engineering has
even written a Java client (see /java/jcvs/),
which can be
run anywhere Java runs.
However, I'm going to take a wild guess and
assume that the majority of CVS users - present and potential - are
most likely working in a Unix command-line environment.
If you aren't
one of these, the examples in the tour should be easy to translate to
other interfaces.
Once you understand the concepts, you can sit down at
any CVS front end and work with it (trust me, I've done it many times).
The examples in the tour are oriented toward people who will be using
CVS to keep track of programming projects.
However, CVS operations are
applicable to all text documents, not just source code.
The tour also assumes that you already have CVS installed (it's present
by default on many of the popular free Unix systems, so you might
already have it without knowing it) and that you have access to a
repository.
Even if you are not set up, you can still benefit from
reading the tour.
In , you'll learn how
to install CVS and set up repositories.
Assuming CVS is installed, you should take a moment to find the online
CVS manual.
Known familiarly as the "Cederqvist" (after Per Cederqvist,
its original author), it comes with the CVS source distribution and is
usually the most up-to-date reference available.
It's written in
Texinfo format and should be available on Unix systems in the "Info"
documentation hierarchy.
You can read it either with the command line
info program
floss$ info cvs
or by pressing Ctrl+H and then typing "i" inside Emacs.
If neither of
these works for you, consult your local Unix guru (or see
regarding installation issues).
definitely want to have the Cederqvist at your fingertips if you're
going to be using CVS regularly.
Previous:&,
Invoking CVS
CVS is one program, but it can perform many different actions: updating,
committing, branching, diffing, and so on.
When you invoke CVS, you
must specify which action you want to perform.
Thus, the format of a
CVS invocation is:
floss$ cvs command
For example, you can use
floss$ cvs update
floss$ cvs diff
floss$ cvs commit
and so on.
(Don't bother to try running any of those particular
commands yet, they won't do anything until you're in a working
copy, which we'll get to shortly.)
Both CVS and the command can take options.
Options that affect the
behavior of CVS, independently of the command being run, are called
command-specific options are just called command
Global options always go to the command
options, to its right.
floss$ cvs -Q update -p
-Q is a global option, and -p is a command option.
(If you're curious,
-Q means "quietly"-that is, suppress all diagnostic output, and print
error messages only if the command absolutely cannot be completed for
-p means to send the results of update to standard output
instead of to files.)
Previous:&,
Accessing A Repository
Before you can do anything, you must tell CVS the location of the
repository you'll be accessing.
This isn't a concern if you already
have a working copy checked out - any working copy knows what
repository it came from, so CVS can automatically deduce the repository
for a given working copy.
However, let's assume you don't have a
working copy yet, so you need to tell CVS explicitly where to go.
is done with the -d global option (the -d stands for "directory", an
abbreviation for which there is a historical justification, although -r
for "repository" might have been better), followed by the path to the
repository.
For example, assuming the repository is on the local
machine in /usr/local/cvs (a fairly standard location):
floss$ cvs -d /usr/local/cvs command
In many cases, however, the repository is on another machine and must
therefore be reached over the network.
CVS provides a choice of network
which one you'll use depends mostly on the security
needs of the repository machine (hereinafter referred to as "the
Setting up the server to allow various remote access methods
here we'll deal only with
the client side.
Fortunately, all the remote access methods share a common invocation
In general, to specify a remote repository as opposed to a
local one, you just use a longer repository path.
You first name the
access method, delimited on each side by colons, followed by the
username and the server name (joined with an @ sign), another separator
colon, and finally the path to the repository directory on the server.
Let's look at the pserver access method, which stands for
"password-authenticated server":
floss$ cvs -d :pserver:jrandom@:/usr/local/cvs login
(Logging in to jrandom@)
CVS password: (enter your CVS password here)
The long repository path following -d told CVS to use the pserver access
method, with the username jrandom, on the server , which
has a CVS repository in /usr/local/cvs.
There's no requirement that the
hostname be "" that's a common convention,
but it could just as easily have been:
floss$ cvs -d :pserver:jrandom@fish.foobar.org:/usr/local/cvs command
The command actually run was login, which verifies that you are
authorized to work with this repository.
It prompts for a password,
then contacts the server to verify the password.
Following Unix custom,
cvs login returns silently i it shows an error
message if it fails (for instance, because the password is incorrect).
You only have to log in once from your local machine to a given CVS
After a successful login, CVS stores the password in your home
directory, in a file called .cvspass.
It consults that file every time
a repository is contacted via the pserver method, so you only have to
run login the first time you access a given CVS server from a particular
client machine.
Of course, you can rerun cvs login anytime if the
password changes.
Note: pserver is currently the only access method requiring an initial
with the others, you can start running regular CVS
commands immediately.
Once you've stored the authentication information in your .cvspass file,
you can run other CVS commands using the same command-line syntax:
floss$ cvs -d :pserver:jrandom@:/usr/local/cvs command
Getting pserver to work in Windows may require an extra step.
doesn't have the Unix concept of a home directory, so CVS doesn't know
where to put the .cvspass file.
You'll have to specify a location.
It's normal to designate the root of the C: drive as the home directory:
C:\WINDOWS& set HOME=C:
C:\WINDOWS& cvs -d :pserver:jrandom@:/usr/local/cvs login
(Logging in to jrandom@)
CVS password: (enter password here)
C:\WINDOWS&
Any folder in the file system will suffice.
You may want to avoid
network drives, though, because the contents of your .cvspass file would
then be visible to anyone with access to the drive.
In addition to pserver, CVS supports the ext method (which uses an
external connection program, such as rsh or ssh), kserver (for the
Kerberos security system version 4), and gserver (which uses the GSSAPI,
or Generic Security Services API, and also handles Kerberos versions 5
and higher).
These methods are similar to pserver, but each has its own
idiosyncrasies.
Of these, the ext method is probably the most commonly used.
you can log into the server with rsh or ssh, you can use the ext
You can test it like this:
floss$ rsh -l jrandom
Password: enter your login password here
Okay, let's assume you successfully logged in and logged out of the
server with rsh, so now you're back on the original client machine:
floss$ CVS_RSH= export CVS_RSH
floss$ cvs -d :ext:jrandom@:/usr/local/cvs command
The first line sets (in Unix Bourne shell syntax) the CVS_RSH
environment variable to rsh, which tells CVS to use the rsh program to
The second line can be any CVS you will be prompted
for your password so CVS can log into the server.
If you're in C shell rather than in Bourne shell, try this:
floss% setenv CVS_RSH rsh
and for Windows, try this:
C:\WINDOWS& set CVS_RSH=rsh
The rest of the tour will use the B translate for your
environment as necessary.
To use ssh (the Secure Shell) instead of rsh, just set the CVS_RSH
variable appropriately:
floss$ CVS_RSH= export CVS_RSH
Don't get thrown by the fact that the variable's name is CVS_RSH but
you're setting its value to ssh.
There are historical reasons for this
(the catch-all Unix excuse, I know).
CVS_RSH can point to the name of
any program capable of logging you into the remote server, running
commands, and receiving their output.
After rsh, ssh is probably the
most common such program, although there are probably others.
this program must not modify its data stream in any way.
disqualifies the Windows NT rsh, because it converts (or attempts to
convert) between the DOS and Unix line-ending conventions.
You'd have
to get some other rsh for Windows or use a different access method.
The gserver and kserver methods are not used as often as the others and
are not covered here.
They're quite similar to what we've covered so
see the Cederqvist for details.
If you only use one repository and don't want to type -d repos each
time, just set the CVSROOT environment variable (which perhaps should
have been named CVSREPOS, but it's too late to change that now):
floss$ CVSROOT=/usr/local/cvs
floss$ export CVSROOT
floss$ echo $CVSROOT
/usr/local/cvs
floss$ CVSROOT=:pserver:jrandom@:/usr/local/cvs
floss$ export CVSROOT
floss$ echo $CVSROOT
:pserver:jrandom@:/usr/local/cvs
The rest of this tour assumes that you've set CVSROOT to point to your
repository, so the examples will not show the -d option.
If you need to
access many different repositories, you should not set CVSROOT and
should just use -d repos when you need to specify the repository.
Previous:&,
Starting A New Project
If you're learning CVS in order to work on a project that's already
under CVS control (that is, it is kept in a repository somewhere),
you'll probably want to skip down to the next section, "Checking Out A
Working Copy." On the other hand, if you want to take existing source
code and put it into CVS, this is the section for you.
Note that it
still assumes you have access to an see
if you need to set up a repository
Putting a new project into a CVS repository is known as importing.
The CVS command, as you may have guessed, is
floss$ cvs import
except that it needs some more options (and needs to be in the right
location) to succeed.
First, go into the top-level directory of your
project tree:
floss$ cd myproj
README.txt
This project has two files - README.txt and hello.c - in the top
level, plus two subdirectories - a-subdir and b-subdir - plus some
more files (not shown in the example) inside those subdirectories.
you import a project, CVS imports everything in the tree, starting from
the current directory and working its way down.
Therefore, you should
make sure that the only files in the tree are ones you want to be
permanent parts of the project.
Any old backup files, scratch files,
and so on should all be cleaned out.
The general syntax of an import command is
floss$ cvs import -m "log msg" projname vendortag releasetag
The -m flag (for message) is for specifying a short message describing
the import.
This will be the first log message fo
every commit thereafter will also have its own log message.
me if you don't give the -m flag, CVS automatically
starts up an editor (by consulting the EDITOR environment variable) for
you to type a log message in.
After you save the log message and exit
the editor, the import then continues.
The next argument is the project's name (we'll use "myproj").
the name under which you'll check out the project from the repository.
(What actually happens is that a directory of that name gets created in
the repository, but more on that in .)
The name you choose now does not need to be the same as the name of the
current directory, although in most cases it usually is.
The vendortag and releasetag arguments are a bit of bookkeeping for CVS.
it hardly matters what you use.
you'll learn about the rare circumstances where
they're significant.
For now, we'll use a username and "start" for
those arguments.
We're ready to run import:
floss$ cvs import -m "initial import into CVS" myproj jrandom start
N myproj/hello.c
N myproj/README.txt
cvs import: Importing /usr/local/cvs/myproj/a-subdir
N myproj/a-subdir/whatever.c
cvs import: Importing /usr/local/cvs/myproj/a-subdir/subsubdir
N myproj/a-subdir/subsubdir/fish.c
cvs import: Importing /usr/local/cvs/myproj/b-subdir
N myproj/b-subdir/random.c
No conflicts created by this import
Congratulations! If you ran that command (or something similar), you've
finally done something that affects the repository.
Reading over the output of the import command, you'll notice that CVS
precedes each filename with a single letter - in this case, "N" for
"new file". The use of a single letter on the left to indicate the
status of a file is a general pattern in CVS command output.
it later in checkout and update as well.
You might think that, having just imported the project, you can start
working in the tree immediately.
This is not the case, however.
current directory tree is still not a CVS working copy.
It was the
source for the import command, true, but it wasn't magically changed
into a CVS working copy merely by virtue of having been imported.
get a working copy, you need to check one out from the repository.
First, though, you might want to archive the current project tree.
reason is that once the sources are in CVS, you don't want to confuse
yourself by accidentally editing copies that aren't in version control
(because those changes won't become part of the project's history).
want to do all of your editing in a working copy from now on.
you also don't want to remove the imported tree entirely, because you
haven't yet verified that the repository actually has the files.
course, you can be 99.999 percent certain that it does because the
import command returned with no error, but why take chances? Paranoia
pays, as every programmer knows.
Therefore, do something like this:
README.txt
floss$ cd ..
floss$ mv myproj was_myproj
was_myproj/
You still have the original files, but they're clearly named as
an obsolete version, so they won't be in the way when you get a real
working copy.
Now you're ready to check out.
Previous:&,
Checking Out A Working Copy
The command to check out a project is exactly what you think it is:
floss$ cvs checkout myproj
cvs checkout: Updating myproj
U myproj/README.txt
U myproj/hello.c
cvs checkout: Updating myproj/a-subdir
U myproj/a-subdir/whatever.c
cvs checkout: Updating myproj/a-subdir/subsubdir
U myproj/a-subdir/subsubdir/fish.c
cvs checkout: Updating myproj/b-subdir
U myproj/b-subdir/random.c
was_myproj/
floss$ cd myproj
README.txt
Behold - your first working copy! Its contents are exactly the same as
what you imported, with the addition of a subdirectory named "CVS".
That's where CVS stores version control information.
Actually, each
directory in the project has a CVS subdirectory:
floss$ ls a-subdir
subsubdir/
whatever.c
floss$ ls a-subdir/subsubdir/
floss$ ls b-subdir
The fact that CVS keeps its revision information in subdirectories named
CVS means that your project can never contain subdirectories of its own
named CVS.
In practice, I've never heard of this being a problem.
Before editing any files, let's take a peek inside the black box:
floss$ cd CVS
Repository
floss$ cat Root
/usr/local/cvs
floss$ cat Repository
Nothing too mysterious there.
The Root file points to repository, and
the Repository file points to a project inside the repository.
that's a little confusing, let me explain.
There is a longstanding confusion about terminology in CVS.
"repository" is used to refer to two different things.
Sometimes, it
means the root directory of a repository (for example, /usr/local/cvs),
which can c this is what the Root file refers to.
But other times, it means one particular project-specific subdirectory
within a repository root (for example, /usr/local/cvs/myproj,
/usr/local/cvs/yourproj, or /usr/local/cvs/fish).
The Repository file
inside a CVS subdirectory takes the latter meaning.
In this book, "repository" generally means Root (that is, the top-level
repository), although it may occasionally be used to mean a
project-specific subdirectory.
If the intended sense can't be figured
out from the context, there will be clarifying text.
Note that the
Repository file may sometimes contain an absolute path to the project
name instead of a relative path.
This can make it slightly redundant
with the Root file:
floss$ cd CVS
floss$ cat Root
:pserver:jrandom@:/usr/local/cvs
floss$ cat Repository
/usr/local/cvs/myproj
The Entries file stores information about the individual files in the
Each line deals with one file, and there are only lines for
files or subdirectories in the immediate parent directory.
Here's the
top-level CVS/Entries file in myproj:
floss$ cat Entries
/README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//
/hello.c/1.1.1.1/Sun Apr 18 18:18:22 1999//
D/a-subdir////
D/b-subdir////
The format of each line is
/filename/revision number/last modification date//
and the directory lines are prefixed with "D". (CVS doesn't really keep
a change history for directories, so the fields for revision number and
datestamp are empty.)
The datestamps record the date and time of the last update (in Universal
Time, not local time) of the files in the working copy.
That way, CVS
can easily tell whether a file has been modified since the last
checkout, update, or commit.
If the file system timestamp differs from
the timestamp in the CVS/Entries file, CVS knows (without even having to
consult the repository) that the file was probably modified.
If you take a look at the CVS/* files in one of the subdirectories
floss$ cd a-subdir/CVS
floss$ cat Root
/usr/local/cvs
floss$ cat Repository
myproj/a-subdir
floss$ cat Entries
/whatever.c/1.1.1.1/Sun Apr 18 18:18:22 1999//
D/subsubdir////
you can see that the root repository has not changed, but the Repository
file spells out the location of this subdirectory of the project, and
the Entries file contains different lines.
Immediately after import, the revision number of every file in the
project is shown as 1.1.1.1.
This initial revision number is a bit of a
special case, so we won't examine i we'll take a
closer look at revision numbers after we've committed some changes.
Previous:&,
Version Versus Revision
The internal revision number that CVS keeps for each file is unrelated
to the version number of the software product of which the files are
For example, you may have a project composed of three files,
whose internal revision numbers on May 3, 1999, were 1.2, 1.7, and 2.48.
On that day, you package up a new release of the software and release it
as SlickoSoft Version 3.
This is purely a marketing decision and
doesn't affect the CVS revisions at all.
The CVS revision numbers are
invisible to your customers (unless you give them repository access);
the only publicly visible number is the "3" in Version 3.
have called it Version 1729 as far as CVS is concerned - the version
number (or "release" number) has nothing to do with CVS's internal
change tracking.
To avoid confusion, I'll use the word "revision" to refer exclusively to
the internal revision numbers of files under CVS control.
I may still
call CVS a "version control system", however, because "revision control
system" just sounds too awkward.
Previous:&,
Making A Change
The project as it stands doesn't do much.
Here are the contents of
floss$ cat hello.c
#include &stdio.h&
printf ("Hello, world!\n");
Let's make the first change to the projec we'll add
printf ("Goodbye, world!\n");
right after the Hello, world!.
Invoke your favorite editor and make the
floss$ emacs hello.c
This was a fairly simple change, one where you're not likely to forget
what you did.
But in a larger, more complex project, it's quite
possible you may edit a file, be interrupted by something else, and
return several days later and be unable to remember exactly what you
did, or even to remember if you changed anything at all.
Which brings
us to our first "CVS Saves Your Life" situation: comparing your working
copy against the repository.
Previous:&,
Finding Out What You (And Others) Did - update And diff
Previously, I've talked about updating as a way of bringing changes down
from the repository into your working copy - that is, as a way of
getting other people's changes.
However, update is really a bit more
it compares the overall state of the working copy with the
state of the project in the repository.
Even if nothing in the
repository has changed since checkout, something in the working copy may
have, and update will show that, too:
floss$ cvs update
cvs update: Updating .
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
The M next to hello.c means the file has been modified since it was last
checked out, and the modifications have not yet been committed to the
repository.
Sometimes, merely knowing which files you've edited is all you need.
However, if you want a more detailed look at the changes, you can get a
full report in diff format.
The diff command compares the possibly
modified files in the working copy to their counterparts in the
repository and displays any differences:
floss$ cvs diff
cvs diff: Diffing .
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 hello.c
printf ("Goodbye, world!\n");
cvs diff: Diffing a-subdir
cvs diff: Diffing a-subdir/subsubdir
cvs diff: Diffing b-subdir
That's helpful, if a bit obscure, but there's still a lot of cruft in
the output.
For starters, you can ignore most of the first few lines.
They just name the repository file and give the number of the last
checked-in revision.
These are useful pieces of information under other
circumstances (we'll look more closely at them later), but you don't
need them when you're just trying to get a sense of what changes have
been made in the working copy.
A more serious impediment to reading the diff is that CVS is announcing
its entry as it goes into each directory during the update.
This can be
useful during long updates on large projects, as it gives you a sense of
how much longer the command will take, but right now it's just getting
in the way of reading the diff.
Let's tell CVS to be quiet about where
it's working, with the -Q global option:
floss$ cvs -Q diff
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 hello.c
printf ("Goodbye, world!\n");
Better - at least some of the cruft is gone.
However, the diff is
still hard to read.
It's telling you that at line 6, a new line was
added (that is, what became line 7), whose contents were:
printf ("Goodbye, world!\n");
The preceding "&" in the diff tells you that this line is present in the
newer version of the file but not in the older one.
The format could be made even more readable, however.
Most people find
"context" diff format easier to read because it displays a few lines of
context on either side of a change.
Context diffs are generated by
passing the -c flag to diff:
floss$ cvs -Q diff -c
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 hello.c
*** hello.c
--- hello.c
***************
*** 4,7 ****
printf ("Hello, world!\n");
printf ("Goodbye, world!\n");
Now that's clarity! Even if you're not used to reading context diffs, a
glance at the preceding output will probably make it obvious what
happened: a new line was added (the + in the first column signifies an
added line) between the line that prints Hello, world! and the final
curly brace.
We don't need to be able to read context diffs perfectly (that's patch's
job), but it's worth taking the time to acquire at least a passing
familiarity with the format.
The first two lines (after the
introductory cruft) are
*** hello.c
--- hello.c
and they tell you what is being diffed against what.
In this case,
revision 1.1.1.1 of hello.c is being compared against a modified version
of the same file (thus, there's no revision number for the second line,
because only the working copy's changes haven't been committed to the
repository yet).
The lines of asterisks and dashes identify sections
farther down in the diff.
Later on, a line of asterisks, with a line
number range embedded, precedes a section from the original file.
a line of dashes, with a new and potentially different line number range
embedded, precedes a section from the modified file.
These sections are
organized into contrasting pairs (known as "hunks"), one side from the
old file and the other side from the new.
Our diff has one hunk:
***************
*** 4,7 ****
--- 4,8 --
printf ("Hello, world!\n");
printf ("Goodbye, world!\n");
The first section of the hunk is empty, meaning that no material was
removed from the original file.
The second section shows that, in the
corresponding place in the new file, one it's
marked with a "+". (When diff quotes excerpts from files, it reserves
the first two columns on the left for special codes, such as "+" so the
entire excerpt appears to be indented by two spaces.
This extra
indentation is stripped off when the diff is applied, of course.)
The line number ranges show the hunk's coverage, including context
In the original file, the hunk was in lines 4 through 7; in the
new file, it's lines 4 through 8 (because a line has been added).
that the diff didn't need to show any material from the original file
because it just showed the range and moved on to
the second half of the hunk.
Here's another context diff, from an actual project of mine:
floss$ cvs -Q diff -c
Index: cvs2cl.pl
===================================================================
RCS file: /usr/local/cvs/kfogel/code/cvs2cl/cvs2cl.pl,v
retrieving revision 1.76
diff -c -r1.76 cvs2cl.pl
*** cvs2cl.pl
--- cvs2cl.pl
***************
*** 212,218 ****
# can contain uppercase and lowercase letters, digits, '-',
# and '_'. However, it's not our place to enforce that, so
# we'll allow anything CVS hands us to be a tag:
/^\s([^:]+): ([0-9.]+)$/;
push (@{$symbolic_names{$2}}, $1);
-- 212,218 --
# can contain uppercase and lowercase letters, digits, '-',
# and '_'. However, it's not our place to enforce that, so
# we'll allow anything CVS hands us to be a tag:
/^\s([^:]+): ([\d.]+)$/;
push (@{$symbolic_names{$2}}, $1);
The exclamation point shows that the marked line differs between the old
and new files.
Since there are no "+" or "-" signs, we know that the
total number of lines in the file has remained the same.
Here's one more context diff from the same project, slightly more
complex this time:
floss$ cvs -Q diff -c
Index: cvs2cl.pl
===================================================================
RCS file: /usr/local/cvs/kfogel/code/cvs2cl/cvs2cl.pl,v
retrieving revision 1.76
diff -c -r1.76 cvs2cl.pl
*** cvs2cl.pl
--- cvs2cl.pl
***************
*** 207,217 ****
# we're looking at a tag name, so parse & store it
# According to the Cederqvist manual, in node "Tags", "Tag
# names must start with an uppercase or lowercase letter and
# can contain uppercase and lowercase letters, digits, '-',
# and '_'. However, it's not our place to enforce that, so
# we'll allow anything CVS hands us to be a tag:
/^\s([^:]+): ([0-9.]+)$/;
push (@{$symbolic_names{$2}}, $1);
- 207,212 --
***************
*** 223,228 ****
--- 218,225 --
if (/^revision (\d\.[0-9.]+)$/) {
$revision = "$1";
# This line was added, I admit, solely for the sake of a diff example.
# If have file name but not time and author, and see date or
# author, then grab them:
This diff has two hunks.
In the first, five lines were removed (these
lines are only shown in the first section of the hunk, and the second
section's line count shows that it has five fewer lines).
An unbroken
line of asterisks forms the boundary between hunks, and in the second
hunk we see that two lines have been added: a blank line and a pointless
Note how the line numbers compensate for the effect of the
previous hunk.
In the original file, the second hunk's range of the
area was lines 223 through 228; in the new file, because of the deletion
that took place in the first hunk, the range is in lines 218 through
Congratulations, you are probably now as expert as you'll ever need to
be at reading diffs.
Previous:&,
CVS And Implied Arguments
In each of the CVS commands so far, you may have noticed that no files
were specified on the command line.
floss$ cvs diff
instead of
floss$ cvs diff hello.c
floss$ cvs update
instead of
floss$ cvs update hello.c
The principle at work here is that if you don't name any files, CVS acts
on all files for which the command could possibly be appropriate.
even includes files in subdirectories beneath t CVS
automatically descends from the current directory through every
subdirectory in the tree.
For example, if you modified
b-subdir/random.c and a-subdir/subsubdir/fish.c, running update may
result in this:
floss$ cvs update
cvs update: Updating .
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
M a-subdir/subsubdir/fish.c
cvs update: Updating b-subdir
M b-subdir/random.c
or better yet:
floss$ cvs -q update
M a-subdir/subsubdir/fish.c
M b-subdir/random.c
Note: The -q flag is a less emphatic version of -Q.
Had we used -Q, the
command would have printed out nothing at all, because the modification
notices are considered nonessential informational messages.
lowercase - it suppresses the messages we probably
don't want, while allowing certain, more useful messages to pass
You can also name specific files for the update:
floss$ cvs update hello.c b-subdir/random.c
M b-subdir/random.c
and CVS will only examine those files, ignoring all others.
In truth, it's more common to run update without restricting it to
certain files.
In most situations, you'll want to update the entire
directory tree at once.
Remember, the updates we're doing here only
show that some files have been locally modified, because nothing has
changed yet in the repository.
When other people are working on the
project with you, there's always the chance that running update will
pull some new changes down from the repository and incorporate them into
your local files.
In that case, you may find it slightly more useful to
name which files you want updated.
The same principle can be applied to other CVS commands.
For example,
with diff, you can choose to view the changes one file at a time
floss$ cvs diff -c b-subdir/random.c
Index: b-subdir/random.c
===================================================================
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 random.c
*** b-subdir/random.c
--- b-subdir/random.c
***************
*** 1 ****
! /* A completely empty C file. */
--- 1,8 --
! /* Print out a random number. */
! #include &stdio.h&
! void main ()
printf ("a random number\n");
or see all the changes at once (hang on to your seat, this is going to
be a big diff):
floss$ cvs -Q diff -c
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 hello.c
*** hello.c
--- hello.c
***************
*** 4,7 ****
--- 4,8 --
printf ("Hello, world!\n");
printf ("Goodbye, world!\n");
Index: a-subdir/subsubdir/fish.c
===================================================================
RCS file: /usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 fish.c
*** a-subdir/subsubdir/fish.c
--- a-subdir/subsubdir/fish.c
***************
*** 1 ****
! /* A completely empty C file. */
--- 1,8 --
! #include &stdio.h&
! void main ()
while (1) {
printf ("fish\n");
Index: b-subdir/random.c
===================================================================
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 random.c
*** b-subdir/random.c
--- b-subdir/random.c
***************
*** 1 ****
! /* A completely empty C file. */
--- 1,8 --
! /* Print out a random number. */
! #include &stdio.h&
! void main ()
printf ("a random number\n");
Anyway, as you can see from these diffs, this project is clearly ready
for prime time.
Let's commit the changes to the repository.
Previous:&,
Committing
The commit command sends modifications to the repository.
don't name any files, a commit will send all chang
otherwise, you can pass the names of one or more files to be committed
(other files would be ignored, in that case).
Here, we commit one file by name and two by inference:
floss$ cvs commit -m "print goodbye too" hello.c
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v
new revision: 1.2; previous revision: 1.1
floss$ cvs commit -m "filled out C code"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in a-subdir/subsubdir/fish.c;
/usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v
new revision: 1.2; previous revision: 1.1
Checking in b-subdir/random.c;
/usr/local/cvs/myproj/b-subdir/random.c,v
new revision: 1.2; previous revision: 1.1
Take a moment to read over the output carefully.
Most of what it says
is pretty self-explanatory.
One thing you may notice is that revision
numbers have been incremented (as expected), but the original revisions
are listed as 1.1 instead of 1.1.1.1 as we saw in the Entries file
There is an explanation for this discrepancy, but it's not very
important.
It concerns a special meaning that CVS attaches to revision
For most purposes, we can just say that files receive a
revision number of 1.1 when imported, but the number is displayed - for
reasons known only to CVS - as 1.1.1.1 in the Entries file, until the
first commit.
Previous:&,
Revision Numbers
Each file in a project has its own revision number.
When a file is
committed, the last portion of the revision number is incremented by
Thus, at any given time, the various files comprising a project
may have very different revision numbers.
This just means that some
files have been changed (committed) more often than others.
(You may be wondering, what's the point of the part to the left of the
decimal point, if only the part on the right ever changes? Actually,
although CVS never automatically increments the number on the left, that
number can be incremented on request by a user.
This is a rarely used
feature, and we won't cover it in this tour.)
In the example project that we've been using, we just committed changes
to three files.
Each of those files is now revision 1.2, but the
remaining files in the project are still revision 1.1.
When you check
out a project, you get each file at its highest revision so far.
is what qsmith would see if he checked out myproj right now and looked
at the revision numbers for the top-level directory:
paste$ cvs -q -d :pserver:qsmith@:/usr/local/cvs co myproj
U myproj/README.txt
U myproj/hello.c
U myproj/a-subdir/whatever.c
U myproj/a-subdir/subsubdir/fish.c
U myproj/b-subdir/random.c
paste$ cd myproj/CVS
paste$ cat Entries
/README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//
/hello.c/1.2/Mon Apr 19 06:35:15 1999//
D/a-subdir////
D/b-subdir////
The file hello.c (among others) is now at revision 1.2, while README.txt
is still at the initial revision (revision 1.1.1.1, also known as 1.1).
If he adds the line
printf ("between hello and goodbye\n");
to hello.c and commit it, the file's revision number will be incremented
once more:
paste$ cvs ci -m "added new middle line"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v
new revision: 1.3; previous revision: 1.2
Now hello.c is revision 1.3, fish.c and random.c still are revision 1.2,
and every other file is revision 1.1.
Note: that the command was given as cvs ci instead of cvs commit.
CVS commands have short forms, to make typing easier.
For checkout,
update, and commit, the abbreviated versions are co, up, and ci,
respectively.
You can get a list of all of the short forms by running
the command cvs&--help-synonyms.
You can usually ignore a file's revision number.
In most situations,
the numbers are just internal bookkeeping that CVS handles
automatically.
However, being able to find and compare revision numbers
is extremely handy when you have to retrieve (or diff against) an
earlier copy of a file.
Examining the Entries file isn't the only way to discover a revision
You can also use the status command
paste$ cvs status hello.c
===================================================================
File: hello.c
Status: Up-to-date
Working revision:
Tue Apr 20 02:34:42 1999
Repository revision: 1.3
/usr/local/cvs/myproj/hello.c,v
Sticky Tag:
Sticky Date:
Sticky Options:
which, if invoked without any files being named, shows the status of
every file in the project:
paste$ cvs status
cvs status: Examining.
===================================================================
File: README.txt
Status: Up-to-date
Working revision:
1.1.1.1 Sun Apr 18 18:18:22 1999
Repository revision: 1.1.1.1 /usr/local/cvs/myproj/README.txt,v
Sticky Tag:
Sticky Date:
Sticky Options:
===================================================================
File: hello.c
Status: Up-to-date
Working revision:
Tue Apr 20 02:34:42 1999
Repository revision: 1.3
/usr/local/cvs/myproj/hello.c,v
Sticky Tag:
Sticky Date:
Sticky Options:
cvs status: Examining a-subdir
===================================================================
File: whatever.c
Status: Up-to-date
Working revision:
1.1.1.1 Sun Apr 18 18:18:22 1999
Repository revision: 1.1.1.1 /usr/local/cvs/myproj/a-subdir/whatever.c,v
Sticky Tag:
Sticky Date:
Sticky Options:
cvs status: Examining a-subdir/subsubdir
===================================================================
File: fish.c
Status: Up-to-date
Working revision:
Mon Apr 19 06:35:27 1999
Repository revision: 1.2
/usr/local/cvs/myproj/
a-subdir/subsubdir/fish.c,v
Sticky Tag:
Sticky Date:
Sticky Options:
cvs status: Examining b-subdir
===================================================================
File: random.c
Status: Up-to-date
Working revision:
Mon Apr 19 06:35:27 1999
Repository revision: 1.2
/usr/local/cvs/myproj/b-subdir/random.c,v
Sticky Tag:
Sticky Date:
Sticky Options:
Just ignore the parts of that output that you don't understand.
fact, that's generally good advice with CVS.
Often, the one little bit
of information you're looking for will be accompanied by reams of
information that you don't care about at all, and maybe don't even
understand.
This situation is normal.
Just pick out what you need, and
don't worry about the rest.
In the previous example, the parts we care about are the first three
lines (not counting the blank line) of each file's status output.
first line i it tells you the file's name, and its
status in the working copy.
All of the files are currently in sync with
the repository, so they all say Up-to-date.
However, if random.c
has been modified but not committed, it might read like this:
===================================================================
File: random.c
Status: Locally Modified
Working revision:
Mon Apr 19 06:35:27 1999
Repository revision: 1.2
/usr/local/cvs/myproj/b-subdir/random.c,v
Sticky Tag:
Sticky Date:
Sticky Options:
The Working revision and Repository revision tell you whether the file
is out of sync with the repository.
Returning to our original working
copy (jrandom's copy, which hasn't seen the new change to hello.c yet),
floss$ cvs status hello.c
===================================================================
File: hello.c
Status: Needs Patch
Working revision:
Mon Apr 19 02:17:07 1999
Repository revision: 1.3
/usr/local/cvs/myproj/hello.c,v
Sticky Tag:
Sticky Date:
Sticky Options:
This tells us that someone has committed a change to hello.c, bringing
the repository copy to revision 1.3, but that this working copy is still
on revision 1.2.
The line Status: Needs Patch means that the next update
will retrieve those changes from the repository and "patch" them into
the working copy's file.
Let's pretend for the moment that we don't know anything about qsmith's
change to hello.c, so we don't run status or update.
Instead, we just
start editing the file, making a slightly different change at the same
This brings us to our first conflict.
Previous:&,
Detecting And Resolving Conflicts
Detecting a conflict is easy enough.
When you run update, CVS tells
you, in no uncertain terms, that there's a conflict.
But first, let's
create the conflict.
We edit hello.c to insert the line
printf ("this change will conflict\n");
right where qsmith committed this:
printf ("between hello and goodbye\n");
At this point, the status of our copy of hello.c is
floss$ cvs status hello.c
===================================================================
File: hello.c
Status: Needs Merge
Working revision:
Mon Apr 19 02:17:07 1999
Repository revision: 1.3
/usr/local/cvs/myproj/hello.c,v
Sticky Tag:
Sticky Date:
Sticky Options:
meaning that there are changes both in the repository and the working
copy, and these changes need to be merged. (CVS isn't aware that the
changes will conflict, because we haven't run update yet.) When we do
the update, we see this:
floss$ cvs update hello.c
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.2
retrieving revision 1.3
Merging differences between 1.2 and 1.3 into hello.c
rcsmerge: warning: conflicts during merge
cvs update: conflicts found in hello.c
The last line of output is the giveaway.
The C in the left margin next
to the filename indicates that changes have been merged, but that they
The contents of hello.c now shows both changes:
#include &stdio.h&
printf ("Hello, world!\n");
&&&&&&& hello.c
printf ("this change will conflict\n");
printf ("between hello and goodbye\n");
&&&&&&& 1.3
printf ("Goodbye, world!\n");
Conflicts are always shown delimited by conflict markers, in the
following format:
&&&&&&& (filename)
the uncommitted changes in the working copy
blah blah blah
the new changes that came from the repository
blah blah blah
&&&&&&& (latest revision number in the repository)
The Entries file also shows that the file is in a halfway state at the
floss$ cat CVS/Entries
/README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//
D/a-subdir////
D/b-subdir////
/hello.c/1.3/Result of merge+Tue Apr 20 03:59:09 1999//
The way to resolve the conflict is to edit the file so that it contains
whatever text is appropriate, removing the conflict markers in the
process, and then to commit.
This doesn't necessarily mean choosing one
you could decide neither change is sufficient and
rewrite the conflicting section (or indeed the whole file) completely.
In this case, we'll adjust in favor of the first change, but with
capitalization and punctuation slightly different from qsmith's:
floss$ emacs hello.c
(make the edits...)
floss$ cat hello.c
#include &stdio.h&
printf ("Hello, world!\n");
printf ("BETWEEN HELLO AND GOODBYE.\n");
printf ("Goodbye, world!\n");
floss$ cvs ci -m "adjusted middle line"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v
new revision: 1.4; previous revision: 1.3
Previous:&,
Finding Out Who Did What (Browsing Log Messages)
By now, the project has undergone several changes.
If you're trying to
get an overview of what has happened so far, you don't necessarily want
to examine every diff in detail.
Browsing the log messages would be
ideal, and you can accomplish this with the log command:
floss$ cvs log
(pages upon pages of output omitted)
The log output tends to be a bit verbose.
Let's look at the log
messages for just one file:
floss$ cvs log hello.c
RCS file: /usr/local/cvs/myproj/hello.c,v
Working file: hello.c
locks: strict
access list:
symbolic names:
start: 1.1.1.1
jrandom: 1.1.1
keyword substitution: kv
total revisions: 5;
selected revisions: 5
description:
--------------
revision 1.4
lines: +1 -1
adjusted middle line
--------------
revision 1.3
lines: +1 -0
added new middle line
--------------
revision 1.2
lines: +1 -0
print goodbye too
--------------
revision 1.1
Initial revision
--------------
revision 1.1.1.1
lines: +0 -0
initial import into CVS
=========================================================================
As usual, there's a lot of information at the top that you can just
The good stuff comes after each line of dashes, in a format that
is self-explanatory.
When many files are sent in the same commit, they all share the same log
a fact that can be useful in tracing changes.
For example,
remember back when we committed fish.c and random.c simultaneously? It
was done like this:
floss$ cvs commit -m "filled out C code"
Checking in a-subdir/subsubdir/fish.c;
/usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v
new revision: 1.2; previous revision: 1.1
Checking in b-subdir/random.c;
/usr/local/cvs/myproj/b-subdir/random.c,v
new revision: 1.2; previous revision: 1.1
The effect of this was to commit both files with the same log message:
"Filled out C code."
(As it happened, both files started at revision
1.1 and went to 1.2, but that's just a coincidence.
If random.c had
been at revision 1.29, it would have moved to 1.30 with this commit, and
its revision 1.30 would have had the same log message as fish.c's
revision 1.2.)
When you run cvs log on them, you'll see the shared message:
floss$ cvs log a-subdir/subsubdir/fish.c b-subdir/random.c
RCS file: /usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v
Working file: a-subdir/subsubdir/fish.c
locks: strict
access list:
symbolic names:
start: 1.1.1.1
jrandom: 1.1.1
keyword substitution: kv
total revisions: 3;
selected revisions: 3
description:
--------------
revision 1.2
lines: +8 -1
filled out C code
--------------
revision 1.1
Initial revision
--------------
revision 1.1.1.1
lines: +0 -0
initial import into CVS
=========================================================================
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
Working file: b-subdir/random.c
locks: strict
access list:
symbolic names:
start: 1.1.1.1
jrandom: 1.1.1
keyword substitution: kv
total revisions: 3;
selected revisions: 3
description:
--------------
revision 1.2
lines: +8 -1
filled out C code
--------------
revision 1.1
Initial revision
--------------
revision 1.1.1.1
lines: +0 -0
initial import into CVS
=========================================================================
From this output, you'll know that the two revisions were part of the
same commit (the fact that the timestamps on the two revisions are the
same, or very close, is further evidence).
Browsing log messages is a good way to get a quick overview of what's
been going on in a project or to find out what happened to a specific
file at a certain time.
There are also free tools available to convert
raw cvs log output to more concise and readable formats (such as GNU
ChangeLog style); we won't cover those tools in this tour, but they'll
be introduced in .
Previous:&,
Examining And Reverting Changes
Suppose that, in the course of browsing the logs, qsmith sees that
jrandom made the most recent change to hello.c:
revision 1.4
lines: +1 -1
adjusted middle line
and wonders what jrandom did? In formal terms, the question that qsmith
is asking is, "What's the difference between my revision (1.3) of
hello.c, and jrandom's revision right after it (1.4)?" The way to find
out is with the diff command, but this time by comparing two past
revisions using the -r command option to specify both of them:
paste$ cvs diff -c -r 1.3 -r 1.4 hello.c
Index: hello.c
===========================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -c -r1.3 -r1.4
*** hello.c
--- hello.c
***************
*** 4,9 ****
printf ("Hello, world!\n");
printf ("between hello and goodbye\n");
printf ("Goodbye, world!\n");
--- 4,9 --
printf ("Hello, world!\n");
printf ("BETWEEN HELLO AND GOODBYE.\n");
printf ("Goodbye, world!\n");
The change is pretty clear, when viewed this way.
Because the revision
numbers are given in chronological order (usually a good idea), the diff
shows them in order.
If only one revision number is given, CVS uses the
revision of the current working copy for the other.
When qsmith sees this change, he instantly decides he likes his way
better and resolves to "undo"-that is, to step back by one revision.
However, this doesn't mean that he wants to lose his revision 1.4.
Although, in an absolute technical sense, it's probably possible to
achieve that effect in CVS, there's almost never any reason to do so.
It's much preferable to keep revision 1.4 in the history and make a new
revision 1.5 that looks exactly like 1.3.
That way the undo event
itself is part of the file's history.
The only question is, how can you retrieve the contents of revision 1.3
and put them into 1.5?
In this particular case, because the change is a very simple one, qsmith
can probably just edit the file by hand to mirror revision 1.3 and then
However, if the changes are more complex (as they usually are
in a real-life project), trying to re-create the old revision manually
will be hopelessly error-prone.
Therefore, we'll have qsmith use CVS to
retrieve and recommit the older revision's contents.
There are two equally good ways to do this: the slow, plodding way and
the fast, fancy way.
We'll examine the slow, plodding way first.
Previous:&,
The Slow Method Of Reverting
This method involves passing the -p flag to update, in conjunction with
The -p option sends the contents of the named revision to standard
By itself, this isn' the contents of the
file fly by on the display, leaving the working copy unchanged.
However, by redirecting the standard output into the file, the file will
now hold the contents of the older revision.
It's just as though the
file had been hand-edited into that state.
First, though, qsmith needs to get up

我要回帖

更多关于 smart home ex 的文章

 

随机推荐