GNU Info

Info Node: (cvsbook.info)Marking A Moment In Time (Tags)

(cvsbook.info)Marking A Moment In Time (Tags)


Prev: Acceptable Date Formats Up: Other Useful CVS Commands
Enter node , (file) or (file)node

Marking A Moment In Time (Tags)
-------------------------------

Retrieving by date is useful when the mere passage of time is your main
concern.  But more often what you really want to do is retrieve the
project as it was at the time of a specific event - perhaps a public
release, a known stable point in the software's development, or the
addition or removal of some major feature.

Trying to remember the date when that event took place or deducing the
date from log messages would be a tedious process.  Presumably, the
event, because it was important, was marked as such in the formal
revision history.  The method CVS offers for making such marks is known
as "tagging".

Tags differ from commits in that they don't record any particular
textual change to files, but rather a change in the developers' attitude
about the files.  A tag gives a label to the collection of revisions
represented by one developer's working copy (usually, that working copy
is completely up to date so the tag name is attached to the "latest and
greatest" revisions in the repository).

Setting a tag is as simple as this:

     floss$ cvs -q tag Release-1999_05_01
     T README.txt
     T hello.c
     T a-subdir/whatever.c
     T a-subdir/subsubdir/fish.c
     T b-subdir/random.c
     floss$

That command associates the symbolic name "Release-1999_05_01" with the
snapshot represented by this working copy.  Defined formally, snapshot
means a set of files and associated revision numbers from the project.
Those revision numbers do not have to be the same from file to file and,
in fact, usually aren't.  For example, assuming that tag was done on the
same myproj directory that we've been using throughout this chapter and
that the working copy was completely up to date, the symbolic name
"Release-1999_05_01" will be attached to hello.c at revision 1.5, to
fish.c at revision 1.2, to random.c at revision 1.2, and to everything
else at revision 1.1.

It may help to visualize a tag as a path or string linking various
revisions of files in the project.  In Figure 2.1, an imaginary string
passes through the tagged revision number of each file in a project.


          File A      File B      File C      File D      File E
          ------      ------      ------      ------      ------
          1.1         1.1         1.1         1.1         1.1
      ----1.2-.       1.2         1.2         1.2         1.2
          1.3 |       1.3         1.3         1.3         1.3
               \      1.4       .-1.4-.       1.4         1.4
                \     1.5      /  1.5  \      1.5         1.5
                 \    1.6     /   1.6   |     1.6         1.6
                  \   1.7    /          |     1.7         1.7
                   \  1.8   /           |     1.8       .-1.8------->
                    \ 1.9  /            |     1.9      /  1.9
                     `1.10'             |     1.10    /   1.10
                      1.11              |     1.11    |
                                        |     1.12    |
                                        |     1.13    |
                                         \    1.14    |
                                          \   1.15   /
                                           \  1.16  /
                                            `-1.17-'
     
     [Figure 2.1: How a tag might stand in relation to files's revisions.]

But if you pull the string taut and sight directly along it, you'll see
a particular moment in the project's history - namely, the moment that
the tag was set (Figure 2.2).


          File A      File B      File C      File D      File E
          ------      ------      ------      ------      ------
                                              1.1
                                              1.2
                                              1.3
                                              1.4
                                              1.5
                                              1.6
                                              1.7
                      1.1                     1.8
                      1.2                     1.9
                      1.3                     1.10        1.1
                      1.4                     1.11        1.2
                      1.5                     1.12        1.3
                      1.6                     1.13        1.4
                      1.7         1.1         1.14        1.5
                      1.8         1.2         1.15        1.6
          1.1         1.9         1.3         1.16        1.7
      ----1.2---------1.10--------1.4---------1.17--------1.8------->
          1.3         1.11        1.5         1.17        1.9
                                  1.6         1.17        1.10
     
     [Figure 2.2: The same tag as a "straight sight" through the revision history.]

As you continue to edit files and commit changes, the tag will not move
along with the increasing revision numbers.  It stays fixed, "stickily",
at the revision number of each file at the time the tag was made.

Given their importance as descriptors, it's a bit unfortunate that log
messages can't be included with tags or that the tags themselves can't
be full paragraphs of prose.  In the preceding example, the tag is
fairly obviously stating that the project was in a releasable state as
of a certain date.  However, sometimes you may want to make snapshots of
a more complex state, which can result in ungainly tag names such as:

     floss$ cvs tag testing-release-3_pre-19990525-public-release

As a general rule, you should try to keep tags as terse as possible
while still including all necessary information about the event that
you're trying to record.  When in doubt, err on the side of being overly
descriptive - you'll be glad later when you're able to tell from some
verbose tag name exactly what circumstance was recorded.

You've probably noticed that no periods or spaces were used in the tag
names.  CVS is rather strict about what constitutes a valid tag name.
The rules are that it must start with a letter and contain letters,
digits, hyphens ("-"), and underscores ("_").  No spaces, periods,
colons, commas, or any other symbols may be used.

To retrieve a snapshot by tag name, the tag name is used just like a
revision number.  There are two ways to retrieve snapshots: You can
check out a new working copy with a certain tag, or you can switch an
existing working copy over to a tag.  Both result in a working copy
whose files are at the revisions specified by the tag.

Most of the time, what you're trying to do is take a look at the project
as it was at the time of the snapshot.  You may not necessarily want to
do this in your main working copy, where you presumably have uncommitted
changes and other useful states built up, so let's assume you just want
to check out a separate working copy with the tag.  Here's how (but make
sure to invoke this somewhere other than in your existing working copy
or its parent directory!):

     floss$ cvs checkout -r Release-1999_05_01 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
     cvs checkout: Updating myproj/c-subdir

We've seen the -r option before in the update command, where it preceded
a revision number.  In many ways a tag is just like a revision number
because, for any file, a given tag corresponds to exactly one revision
number (it's illegal, and generally impossible, to have two tags of the
same name in the same project).  In fact, anywhere you can use a
revision number as part of a CVS command, you can use a tag name instead
(as long as the tag has been set previously).  If you want to diff a
file's current state against its state at the time of the last release,
you can do this:

     floss$ cvs diff -c -r Release-1999_05_01 hello.c

And if you want to revert it temporarily to that revision, you can do
this:

     floss$ cvs update -r Release-1999_05_01 hello.c

The interchangeability of tags and revision numbers explains some of the
strict rules about valid tag names.  Imagine if periods were legal in
tag names; you could have a tag named "1.3" attached to an actual
revision number of "1.47".  If you then issued the command

     floss$ cvs update -r 1.3 hello.c

how would CVS know whether you were referring to the tag named "1.3", or
the much earlier revision 1.3 of hello.c? Thus, restrictions are placed
on tag names so that they can always be easily distinguished from
revision numbers.  A revision number has a period; a tag name doesn't.
(There are reasons for the other restrictions, too, mostly having to do
with making tag names easy for CVS to parse.)

As you've probably guessed by this point, the second method of
retrieving a snapshot - that is, switching an existing working
directory over to the tagged revisions-is also done by updating:

     floss$ cvs update -r Release-1999_05_01
     cvs update: Updating .
     cvs update: Updating a-subdir
     cvs update: Updating a-subdir/subsubdir
     cvs update: Updating b-subdir
     cvs update: Updating c-subdir
     floss$

The preceding command is just like the one we used to revert hello.c to
`Release-1999_05_01', except that the filename is omitted because we
want to revert the entire project over.  (You can, if you want, revert
just one subtree of the project to the tag by invoking the preceding
command in that subtree instead of from the top level, although you
hardly ever would want to do that.)

Note that no files appear to have changed when we updated.  The working
copy was completely up to date when we tagged, and no changes had been
committed since the tagging.

However, this does not mean that nothing changed at all.  The working
copy now knows that it's at a tagged revision.  When you make a change
and try to commit it (let's assume we modified hello.c):

     floss$ cvs -q update
     M hello.c
     floss$ cvs -q ci -m "trying to commit from a working copy on a tag"
     cvs commit: sticky tag 'Release-1999_05_01' for file 'hello.c' is not a branch
     cvs [commit aborted]: correct above errors first!
     floss$

CVS does not permit the commit to happen. (Don't worry about the exact
meaning of that error message yet - we'll cover branches next in this
chapter.)  It doesn't matter whether the working copy got to be on a tag
via a checkout or an update.  Once it is on a tag, CVS views the working
copy as a static snapshot of a moment in history, and CVS won't let you
change history, at least not easily.  If you run cvs status or look at
the CVS/Entries files, you'll see that there is a sticky tag set on each
file.  Here's the top level Entries file, for example:

     floss$ cat CVS/Entries
     D/a-subdir////
     D/b-subdir////
     D/c-subdir////
     /README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//TRelease-1999_05_01
     /hello.c/1.5/Tue Apr 20 07:24:10 1999//TRelease-1999_05_01
     floss$

Tags, like other sticky properties, are removed with the -A flag to
update:

     floss$ cvs -q update -A
     M hello.c
     floss$

The modification to hello.c did not go away, however; CVS is still aware
that the file changed with respect to the repository:

     floss$ cvs -q diff -c hello.c
     Index: hello.c
     ===================================================================
     RCS file: /usr/local/cvs/myproj/hello.c,v
     retrieving revision 1.5
     diff -c -r1.5 hello.c
     *** hello.c   1999/04/20 06:12:56     1.5
     --- hello.c   1999/05/04 20:09:17
     ***************
     *** 6,9 ****
     --- 6,10 --
         printf ("Hello, world!\n");
         printf ("between hello and goodbye\n");
         printf ("Goodbye, world!\n");
     +   /* a comment on the last line */
       }
     floss$

Now that you've reset with update, CVS will accept a commit:

     floss$ cvs ci -m "added comment to end of main function"
     cvs commit: Examining .
     cvs commit: Examining a-subdir
     cvs commit: Examining a-subdir/subsubdir
     cvs commit: Examining b-subdir
     cvs commit: Examining c-subdir
     Checking in hello.c;
     /usr/local/cvs/myproj/hello.c,v  <-  hello.c
     new revision: 1.6; previous revision: 1.5
     done
     floss$

The tag `Release-1999_05_01' is still attached to revision 1.5, of
course.  Compare the file's status before and after a reversion to the
tag:

     floss$ cvs -q status hello.c
     ===================================================================
     File: hello.c                 Status: Up-to-date
        Working revision:  1.6     Tue May  4 20:09:17 1999
        Repository revision:       1.6     /usr/local/cvs/myproj/hello.c,v
        Sticky Tag:                (none)
        Sticky Date:               (none)
        Sticky Options:            (none)
     floss$ cvs -q update -r Release-1999_05_01
     U hello.c
     floss$ cvs -q status hello.c
     ===================================================================
     File: hello.c                 Status: Up-to-date
        Working revision:  1.5     Tue May  4 20:21:12 1999
        Repository revision:       1.5     /usr/local/cvs/myproj/hello.c,v
        Sticky Tag:                Release-1999_05_01 (revision: 1.5)
        Sticky Date:               (none)
        Sticky Options:            (none)
     floss$

Now, having just told you that CVS doesn't let you change history, I'll
show you how to change history.


automatically generated by info2www version 1.2.2.9