bug-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Feature request/ideas - final patch


From: Frank Hemer
Subject: Re: Feature request/ideas - final patch
Date: Thu, 17 Mar 2005 20:07:26 +0100
User-agent: KMail/1.5.1

I have finally finished my patch, tests and alike are contained:

Index: ChangeLog
===================================================================
RCS file: /cvs/ccvs/ChangeLog,v
retrieving revision 1.1182
diff -u -r1.1182 ChangeLog
--- ChangeLog   17 Mar 2005 01:38:16 -0000      1.1182
+++ ChangeLog   17 Mar 2005 18:46:53 -0000
@@ -1,3 +1,7 @@
+2005-03-17  Frank Hemer <frank@hemer.org>
+
+       * NEWS: Note new tag extensions.
+
 2005-03-16  Mark Baushke  <mdb@cvshome.org>
 
        * cvs-format.el: Update to work with modern GNU Emacs versions
Index: NEWS
===================================================================
RCS file: /cvs/ccvs/NEWS,v
retrieving revision 1.307
diff -u -r1.307 NEWS
--- NEWS        16 Mar 2005 20:44:25 -0000      1.307
+++ NEWS        17 Mar 2005 18:46:53 -0000
@@ -3,6 +3,10 @@
 
 NEW FEATURES
 
+* New builtin tags are available: '.base' and '.head' as a replacement for
+  the well known HEAD and BASE tags, additional tags are: '.trunk', '.root',
+  '.origin', '.prev', '.next'.
+
 * A problem where a proxy server could fail to notice that its primary closed
   the connection has been fixed.
 
Index: doc/cvs.texinfo
===================================================================
RCS file: /cvs/ccvs/doc/cvs.texinfo,v
retrieving revision 1.637
diff -u -r1.637 cvs.texinfo
--- doc/cvs.texinfo     25 Feb 2005 17:54:32 -0000      1.637
+++ doc/cvs.texinfo     17 Mar 2005 18:47:03 -0000
@@ -3575,6 +3575,7 @@
 * Versions revisions releases::  Terminology used in this manual
 * Assigning revisions::         Assigning revisions
 * Tags::                        Tags--Symbolic revisions
+* Tags (builtin)::              Tags--builtin
 * Tagging the working directory::  The cvs tag command
 * Tagging by date/tag::         The cvs rtag command
 * Modifying tags::              Adding, renaming, and deleting tags
@@ -3883,6 +3884,154 @@
 @end group
 @end example
 
+@node Tags (builtin)
+@section Tags--builtin
+@cindex Tags (builtin)
+
+@noindent
+The following builtin tags are available:
+
+HEAD BASE .head .base .trunk .root .origin .prev .next
+
+@noindent
+The builtin tags can be separated into categories:
+
+@itemize
+@item A. Static tags:
+Static tags always mark a specific revision, no matter
+how they are used.
+Static tags are: Symbolic tags, numeric revision numbers
+
+@item B. Dynamic tags:
+Dynamic tags specify a specific revision depending
+on the the revision they are based upon.
+Dynamic tags are: HEAD, BASE, '.head', '.root', '.prev',
+'.next', '.origin', '.base'
+
+@item C. Branch tags:
+Branch tags are ambiguous regarding static/dynamic
+behavior. Taken standalone, a branch tag points onto
+the most recent revision of a branch, impersonating
+static behavior. However, branch tags are dynamic tags
+as their focus changes when a branch is updated.
+@end itemize
+
+@noindent
+The different tags may be combined. For describing
+combined behavior, another definition is required:
+
+@itemize
+@item Absolute tags:
+Absolute tag means a tag that specifies either a revision or a trunk/branch.
+'.trunk', '.base', '1.2.3.4' and symbolic tags/branch tags are absolute tags.
+
+@item Relative tags:
+Relative tag means a tag that specifies a revision
+relative to a certain starting point.
+'.prev', '.next', '.root', '.origin' and '.head'
+are relative tags.
+@end itemize
+
+@noindent
+Combination of builtin tags:
+
+The builtin tags may be combined. Combined tags are resolved from
+left to right. When using tags with directories, combined tags need
+to start with an absolute tag. Relative tags can be appended. If a
+relative tag follows an absolute tag, the result will be absolute.
+
+When using tags with files only, the tag does not need to be absolute.
+If a relative tag is used, the absolute position is taken from:
+@enumerate
+@item The sandbox revision's tag
+@item The sandbox revision's dir-tag
+@item The sandbox revisions revision number
+@item The mainline
+@end enumerate
+@noindent
+in this order, whatever is first available.
+
+If a combined tag with relative extensions is used for commands that change
+the local sandbox and set sticky tags, the resulting numeric revision is used
+for sticky tagging in case the last tag is either '.prev' or '.next'.
+
+@noindent
+Following the mainline (backwards):
+
+Starting from a specific rev. number X on the trunk or a branch, the line is
+followed backwards up to the first revision Z on the trunk. It doesn't matter
+if there are dead or missing revisions in between. If this first revision is
+dead, and its the 1.1 rev., stop here.
+If 1.1 is not dead, and 1.1 was created at the same time the first vendor
+branch rev. was created, jump to the vendor branch, and take the latest
+revision prior to the date of Y (Z is the predecessor of Y).
+Otherwise take 1.1.
+
+@noindent
+Focused branch:
+
+The 'focused branch' is used as a synonym for either the trunk or the branch,
+depending on the sticky tag of the addressed file or the workdir.
+If a relative tag is in head position, the absolute position is calculated 
from:
+1. The sandbox revision's tag
+2. The sandbox revision's dir-tag
+3. The sandbox revisions revision number
+4. The mainline
+in this order, whatever is first available.
+
+@noindent
+What the tags mean:
+
+'.trunk': Serves as a synonym for the trunk-branch. Resolves to the
+most recent revision of the mainline, and allows to be used as a
+branch tag. Combination with a date ('xxx -r ".trunk:2005-03-08 18:00:00")
+is possible.
+
+'.origin': Will always resolve to the very first revision. If a file has
+been added on a branch, .origin will resolve to the first revision on
+that branch, otherwise it will follow the mainline.
+
+'.root': Will resolv to the first revision on a branch, the revision
+which is shared with its parent.
+
+'.base': The current sandbox revision. May only be used standalone.
+
+'.head': The most recent revision on the focused branch.
+
+'.prev': The previous revision on the focused branch. Follows the mainline.
+
+'.next': The next revision on the trunk/branch.
+
+@noindent
+Warning (when using '.origin' or '.root' with directories):
+
+While file1.root.head from the branch of a branch could yield
+the head of the first branch, file2.root.head could yield the
+head of the trunk. This must be taken into account since tags
+can be spread over a project in unpredictible manner, and there
+is no guarantee that a combined tag resolves to something usefull
+under all circumstances.
+
+@noindent
+Examples:
+
+@example
+cvs diff -r .base.prev test.txt
+cvs update -r .trunk.prev.prev
+@end example
+
+@noindent
+Backward compatibility:
+
+The '.head' and '.base' are a replacement for the well known
+HEAD and BASE tags. However there is a slight difference:
+
+When '.head' is used as a relative tag, it resolves to the
+head of trunk or branch depending on the current sandbox state.
+If '.head' shall be used as an absolute tag (i.e. to ignore a
+sticky dir tag), '.trunk' or any other absolute tag must be
+prepended.
+
 @node Tagging the working directory
 @section Specifying what to tag from the working directory
 
Index: src/ChangeLog
===================================================================
RCS file: /cvs/ccvs/src/ChangeLog,v
retrieving revision 1.3159
diff -u -r1.3159 ChangeLog
--- src/ChangeLog       17 Mar 2005 17:15:19 -0000      1.3159
+++ src/ChangeLog       17 Mar 2005 18:47:11 -0000
@@ -1,3 +1,8 @@
+2005-03-17  Frank Hemer <frank@hemer.org>
+
+       * admin.c, subr.h, update.c, rcs.h, subr.c, import.c, sanity.sh,
+       cvs.h, vers_ts.c, tag.c, commit.c, rcs.c: Tag extensions added
+
 2005-03-16  Mark D. Baushke  <mdb@juniper.net>
 
        * buffer.c (fd_buffer_shutdown): Replace (int *) 0 with NULL.
Index: src/admin.c
===================================================================
RCS file: /cvs/ccvs/src/admin.c,v
retrieving revision 1.105
diff -u -r1.105 admin.c
--- src/admin.c 7 Mar 2005 20:15:58 -0000       1.105
+++ src/admin.c 17 Mar 2005 18:47:11 -0000
@@ -655,12 +655,17 @@
        char *branch = &admin_data->branch[2];
        if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
        {
-           branch = RCS_whatbranch (rcs, admin_data->branch + 2);
-           if (branch == NULL)
+           if (*branch == '.' && !strcmp (branch+1, TAG_TRUNK))
+               branch = NULL;
+           else
            {
-               error (0, 0, "%s: Symbolic name %s is undefined.",
-                               rcs->path, admin_data->branch + 2);
-               status = 1;
+               branch = RCS_whatbranch (rcs, admin_data->branch + 2);
+               if (branch == NULL)
+               {
+                   error (0, 0, "%s: Symbolic name %s is undefined.",
+                                   rcs->path, admin_data->branch + 2);
+                   status = 1;
+               }
            }
        }
        if (status == 0)
Index: src/commit.c
===================================================================
RCS file: /cvs/ccvs/src/commit.c,v
retrieving revision 1.250
diff -u -r1.250 commit.c
--- src/commit.c        16 Mar 2005 22:07:46 -0000      1.250
+++ src/commit.c        17 Mar 2005 18:47:13 -0000
@@ -700,6 +700,10 @@
     Lock_Cleanup ();
     dellist (&mulist);
 
+    char *commitid = Xasprintf ("@%s", global_session_id);
+    tag_check_valid (commitid, argc, argv, local, aflag, "", true);
+    free (commitid);
+
     /* see if we need to sleep before returning to avoid time-stamp races */
     if (
 #ifdef SERVER_SUPPORT
@@ -886,13 +890,16 @@
                           finfo->fullname);
                    goto out;
                }
-               if (status == T_MODIFIED && vers->tag &&
-                   !RCS_isbranch (finfo->rcs, vers->tag))
+               if (status == T_MODIFIED && vers->tag)
                {
-                   error (0, 0,
-                          "sticky tag `%s' for file `%s' is not a branch",
-                          vers->tag, finfo->fullname);
-                   goto out;
+                  if ( (*(vers->tag) != '.' || strcmp (vers->tag+1, TAG_TRUNK))
+                        && !RCS_isbranch (finfo->rcs, vers->tag) )
+                  {
+                     error (0, 0,
+                           "sticky tag `%s' for file `%s' is not a branch",
+                           vers->tag, finfo->fullname);
+                     goto out;
+                  }
                }
            }
            if (status == T_MODIFIED && !force_ci && vers->ts_conflict)
@@ -1350,6 +1357,7 @@
     {
        char *rev = RCS_getversion (finfo->rcs, write_dirtag, NULL, 1, NULL);
        if (rev != NULL
+           && !(*write_dirtag == '.' && !strcmp (write_dirtag+1, TAG_TRUNK))
            && !RCS_nodeisbranch (finfo->rcs, write_dirtag))
            write_dirnonbranch = 1;
        if (rev != NULL)
@@ -1407,6 +1415,16 @@
     }
     else if (ci->status == T_ADDED)
     {
+
+        /* prevent adding files to a branch named TAG_TRUNK as this
+        * is a synonym for the trunk itself
+        */
+        if (ci->tag && *ci->tag == '.' && !strcmp (ci->tag + 1, TAG_TRUNK))
+       {
+           free (ci->tag);
+           ci->tag = NULL;
+       }
+
        if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
                          &finfo->rcs) != 0)
        {
@@ -1425,7 +1443,7 @@
            /* If numeric, it is on the trunk; check_fileproc enforced
               this.  */
            && !isdigit ((unsigned char) ci->tag[0]))
-       {
+          {
            if (finfo->rcs == NULL)
                error (1, 0, "internal error: no parsed RCS file");
            if (ci->rev)
@@ -1751,7 +1769,8 @@
        error (1, 0, "internal error: no parsed RCS file");
 
     branch = 0;
-    if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag)))
+    if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag))
+       && !(*tag == '.' && !strcmp (tag+1, TAG_TRUNK)))
     {
        /* a symbolic tag is specified; just remove the tag from the file */
        if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0)
Index: src/cvs.h
===================================================================
RCS file: /cvs/ccvs/src/cvs.h,v
retrieving revision 1.331
diff -u -r1.331 cvs.h
--- src/cvs.h   16 Mar 2005 15:52:14 -0000      1.331
+++ src/cvs.h   17 Mar 2005 18:47:13 -0000
@@ -242,6 +242,14 @@
  */
 #define        TAG_HEAD        "HEAD"
 #define        TAG_BASE        "BASE"
+#define TAG_DOTHEAD     "head"
+#define TAG_DOTBASE     "base"
+#define TAG_COMMITID    "commitid"
+#define TAG_PREVIOUS    "prev"
+#define TAG_TRUNK       "trunk"
+#define TAG_ORIGIN      "origin"
+#define TAG_ROOT        "root"
+#define TAG_NEXT        "next"
 
 /* Environment variable used by CVS */
 #define        CVSREAD_ENV     "CVSREAD"       /* make files read-only */
Index: src/import.c
===================================================================
RCS file: /cvs/ccvs/src/import.c,v
retrieving revision 1.169
diff -u -r1.169 import.c
--- src/import.c        10 Mar 2005 20:44:28 -0000      1.169
+++ src/import.c        17 Mar 2005 18:47:14 -0000
@@ -441,6 +441,10 @@
        error (0, errno, "cannot remove %s", tmpfile);
     free (tmpfile);
 
+    char *commitid = Xasprintf ("@%s", global_session_id);
+    tag_check_valid (commitid, argc, argv, 0, 1, "", true);
+    free (commitid);
+
     if (message)
        free (message);
     free (repository);
Index: src/rcs.c
===================================================================
RCS file: /cvs/ccvs/src/rcs.c,v
retrieving revision 1.338
diff -u -r1.338 rcs.c
--- src/rcs.c   17 Mar 2005 17:15:19 -0000      1.338
+++ src/rcs.c   17 Mar 2005 18:47:18 -0000
@@ -69,6 +69,8 @@
 };
 
 static RCSNode *RCS_parsercsfile_i (FILE * fp, const char *rcsfile);
+static char *RCS_getdatetrunk (RCSNode * rcs, const char *date,
+                               int force_tag_match);
 static char *RCS_getdatebranch (RCSNode * rcs, const char *date,
                                 const char *branch);
 static void rcsbuf_open (struct rcsbuffer *, FILE *fp,
@@ -98,6 +100,13 @@
 static void free_rcsnode_contents (RCSNode *);
 static void free_rcsvers_contents (RCSVers *);
 static void rcsvers_delproc (Node * p);
+static char *RCS_getroot (RCSNode *, const char *);
+static char *RCS_getprevious (RCSNode *, const char *);
+static char *RCS_getnext (RCSNode *, const char *);
+static char *RCS_getorigin (RCSNode *, const char *);
+static char *RCS_getcommitid (RCSNode *, const char *, bool);
+static char *RCS_gethead (RCSNode *, const char *);
+static char *translate_tag (RCSNode *, const char *);
 static char *translate_symtag (RCSNode *, const char *);
 static char *RCS_addbranch (RCSNode *, const char *);
 static char *truncate_revnum_in_place (char *);
@@ -2157,23 +2166,30 @@
     {
        char *branch, *rev;
 
-       if (! RCS_nodeisbranch (rcs, tag))
+       if (*tag == '.' && !strcmp (tag+1, TAG_TRUNK))
        {
-           /* We can't get a particular date if the tag is not a
-               branch.  */
-           return NULL;
+           return RCS_getdatetrunk (rcs, date, force_tag_match);
        }
-
-       /* Work out the branch.  */
-       if (! isdigit ((unsigned char) tag[0]))
-           branch = RCS_whatbranch (rcs, tag);
        else
-           branch = xstrdup (tag);
+       {
+           if (! RCS_nodeisbranch (rcs, tag))
+           {
+               /* We can't get a particular date if the tag is not a
+                  branch.  */
+               return NULL;
+           }
 
-       /* Fetch the revision of branch as of date.  */
-       rev = RCS_getdatebranch (rcs, date, branch);
-       free (branch);
-       return rev;
+           /* Work out the branch.  */
+           if (! isdigit ((unsigned char) tag[0]))
+               branch = RCS_whatbranch (rcs, tag);
+           else
+               branch = xstrdup (tag);
+
+           /* Fetch the revision of branch as of date.  */
+           rev = RCS_getdatebranch (rcs, date, branch);
+           free (branch);
+           return rev;
+       }
     }
     else if (tag)
        return RCS_gettag (rcs, tag, force_tag_match, simple_tag);
@@ -2194,6 +2210,8 @@
  *    returns the magic branch number.
  * -- If tag is a branch tag, returns the branch number, not
  *    the revision of the head of the branch.
+ * -- An exception is made for '.trunk' as it returns the
+ *    head revision of the trunk
  * If tag or revision is not valid or does not exist in file,
  * return NULL.
  */
@@ -2259,15 +2277,15 @@
        error (1, 0, "revision `%s' does not exist", tag);
     }
 
-
-    RCS_check_tag (tag); /* exit if not a valid tag */
-
     /* If tag is "HEAD", special case to get head RCS revision */
     if (tag && STREQ (tag, TAG_HEAD))
         return RCS_head (rcs);
 
-    /* If valid tag let translate_symtag say yea or nay. */
-    rev = translate_symtag (rcs, tag);
+    /* If valid tag let translate_tag say yea or nay. */
+    char *tmp = RCS_extract_tag (tag, true);
+    if (tmp)
+        free (tmp);
+    rev = translate_tag (rcs, tag);
 
     if (rev)
         return rev;
@@ -2313,12 +2331,12 @@
 #endif
            return RCS_head (rcs);
 
-    if (!isdigit ((unsigned char) symtag[0]))
+    if (!isrevnumonly (symtag))
     {
        char *version;
 
        /* If we got a symbolic tag, resolve it to a numeric */
-       version = translate_symtag (rcs, symtag);
+       version = translate_tag (rcs, symtag);
        if (version != NULL)
        {
            int dots;
@@ -2548,10 +2566,10 @@
     assert (rcs != NULL);
 
     /* numeric revisions are easy -- even number of dots is a branch */
-    if (isdigit ((unsigned char) *rev))
-       return (numdots (rev) & 1) == 0;
+    if (isrevnumonly (rev))
+        return (numdots (rev) & 1) == 0;
 
-    version = translate_symtag (rcs, rev);
+    version = translate_tag (rcs, rev);
     if (version == NULL)
        return 0;
     dots = numdots (version);
@@ -2602,7 +2620,7 @@
        return NULL;
 
     /* now, look for a match in the symbols list */
-    version = translate_symtag (rcs, rev);
+    version = translate_tag (rcs, rev);
     if (version == NULL)
        return NULL;
     dots = numdots (version);
@@ -2773,11 +2791,11 @@
     if (RCS_nodeisbranch (rcs, rev))
        return RCS_getbranch (rcs, rev, 1);
 
-    if (isdigit ((unsigned char) *rev))
+    if (isrevnumonly (rev))
        num = xstrdup (rev);
     else
     {
-       num = translate_symtag (rcs, rev);
+       num = translate_tag (rcs, rev);
        if (num == NULL)
            return NULL;
     }
@@ -2917,7 +2935,29 @@
            return retval;
     }
 
-    /* otherwise if we have a trunk, try it */
+    /* otherwise if we have a trunk, use it */
+    return RCS_getdatetrunk (rcs, date, force_tag_match);
+}
+
+
+
+/*
+ * Look up the last element on the trunk that was put in before or on
+ * the specified date and time (return the rev or NULL)
+ * Follow the vendor branch if not found on the trunk
+ */
+static char *
+RCS_getdatetrunk (RCSNode *rcs, const char *date, int force_tag_match)
+{
+    Node *p = NULL;
+    RCSVers *vers = NULL;
+    char *retval = NULL;
+
+    assert(rcs);
+
+    if (rcs->flags & PARTIAL)
+        RCS_reparsercsfile (rcs, NULL, NULL);
+
     if (rcs->head)
     {
        p = findnode (rcs->versions, rcs->head);
@@ -2932,8 +2972,8 @@
            vers = p->data;
            if (RCS_datecmp (vers->date, date) <= 0)
            {
-               cur_rev = vers->version;
-               break;
+                retval = vers->version;
+                break;
            }
 
            /* if there is a next version, find the node */
@@ -2953,10 +2993,10 @@
      */
 
     /* if we found what we're looking for, and it's not 1.1 return it */
-    if (cur_rev != NULL)
+    if (retval != NULL)
     {
-       if (! STREQ (cur_rev, "1.1"))
-           return xstrdup (cur_rev);
+       if (! STREQ (retval, "1.1"))
+           return xstrdup (retval);
 
        /* This is 1.1;  if the date of 1.1 is not the same as that for the
           1.1.1.1 version, then return 1.1.  This happens when the first
@@ -2973,7 +3013,6 @@
        }
     }
 
-    /* look on the vendor branch */
     retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
 
     /*
@@ -3210,15 +3249,680 @@
 
 
 /*
- * Return the version associated with a particular symbolic tag.
+ * Find the previous revision
+ *
+ * RETURN
+ *     Branch: return the HEAD-1 revision or NULL
+ *     Revision: return the previous revision or NULL
+ */
+static char *
+RCS_getprevious (RCSNode *rcs, const char *rev)
+{
+    char *retval = NULL;
+    int dots = numdots (rev);
+
+    if (!dots)
+        return NULL;
+
+    char *tmp = xstrdup (rev);
+    char *trev = NULL;
+
+    /* remove ev. magic branch num */
+    if (dots > 2 && (dots & 1))
+    {
+        int i;
+        const char *p = rev;
+        for (i = 1; i < dots; ++i)
+            p = strchr (p+1, '.');
+        if (!strncmp (p, ".0.", 3))
+        {
+            strcpy (tmp + (p - rev), p + 2);
+           --dots;
+        }
+    }
+
+    if (!(dots & 1)) /* branch handling => turn into a head revision */
+    {
+        char *p = RCS_branch_head (rcs, tmp);
+       if (p)
+           trev = p;
+       else
+       {
+           /* branch tag without any revisions ... */
+           trev = xstrdup (tmp);
+           *strrchr (trev, '.') = '\0';
+       }
+       dots = numdots (trev);
+    }
+    else
+        trev = xstrdup (tmp);
+
+    if (dots > 1) /* revision on a branch */
+    {
+        if (rcs->flags & PARTIAL)
+           RCS_reparsercsfile (rcs, NULL, NULL);
+
+        /* find the root revision */
+        *strrchr (trev, '.') = '\0';
+       int len = strlen (trev);
+        *strrchr (trev, '.') = '\0';
+
+        RCSVers *vers = NULL;
+        Node *node = findnode (rcs->versions, trev);
+        if (node && (vers = node->data) && vers->branches)
+       {
+          char *rootdate = vers->date;
+           Node *head = vers->branches->list;
+           Node *br;
+           for (br = head->next; br != head; br = br->next)
+           {
+               if (node = findnode (rcs->versions, br->key))
+               {
+                   if (strncmp (((RCSVers *)node->data)->version, tmp, len))
+                       continue;
+                      
+                   ++len;
+                   char *p = NULL;
+                   while (node != NULL)
+                   {
+                       vers = node->data;
+                       if (STREQ (vers->version + len, tmp + len))
+                       {
+                           if (p)
+                               retval = xstrdup (p);
+                           else if (RCS_datecmp (vers->date, rootdate))
+                               retval = xstrdup (trev);
+                           break;
+                       }
+
+                       /* if there is a next version, find the node */
+                       if (vers->next != NULL)
+                       {
+                           p = vers->version;
+                           node = findnode (rcs->versions, vers->next);
+                       }
+                       else
+                           node = NULL;
+                   }
+               }
+           }
+       }
+    }
+    else /* revision on trunk */
+    {
+        char *prev = NULL;
+       char *curdate = NULL;
+
+       assert (dots == 1);
+
+       if (rcs->flags & PARTIAL)
+           RCS_reparsercsfile (rcs, NULL, NULL);
+
+        if (!STREQ (trev, "1.1"))
+       {
+            Node *node = findnode (rcs->versions, trev);
+           if (node)
+           {
+               RCSVers *v = node->data;
+               curdate = v->date;
+               if (v->next && (node = findnode (rcs->versions, v->next)))
+               {
+                   free (trev);
+                   prev = ((RCSVers *)node->data)->version;
+                   trev = xstrdup (prev);
+               }
+           }
+        }
+
+        if (prev && STREQ (trev, "1.1"))
+        {
+            /* This is 1.1;  if the date of 1.1 is the same as that for
+            * the VENDOR.1 version, then return the VENDOR version with
+            * a timestamp before the 1.2 timestamp (point of merge). The
+            * date of 1.1. and VENDOR.1 differs if the first version of
+            * a file is created by a regular cvs add and commit, and there
+            * is a subsequent cvs import of the same file ==> return 1.1.
+            * If 1.1 is dead, the file was initially added on a branch
+            * ==> return NULL.
+            */
+           Node *rootnode = findnode (rcs->versions, trev);
+           if (rootnode)
+           {
+               RCSVers *vers = rootnode->data;
+               if (!vers->dead && vers->branches)
+               {
+                   char *date_1_1 = vers->date;//vers 1.1
+
+                   Node *head = vers->branches->list;
+                   Node *br;
+                   for (br = head->next; br != head; br = br->next)
+                   {
+                       Node *node = NULL;
+                       if (node = findnode (rcs->versions, br->key))
+                       {
+                           vers = node->data;
+                           if (!vers->dead && !RCS_datecmp (vers->date, 
date_1_1))
+                           {
+                               /* get head of branch */
+                               retval = vers->version;
+                               while (vers->next)
+                               {
+                                   node = findnode (rcs->versions, vers->next);
+                                   if (node)
+                                   {
+                                       vers = node->data;
+                                       if (RCS_datecmp (curdate, vers->date) > 
0)
+                                       {
+                                           vers = node->data;
+                                           retval = vers->version;
+                                           continue;
+                                       }
+                                   }
+                                   break;
+                               }
+                               retval = xstrdup (retval);
+                           }
+                       }
+                   }
+               }
+           }
+        }
+       if (!retval && prev)
+           retval = xstrdup (prev);
+    }
+    free (tmp);
+    free (trev);
+
+    return retval;
+}
+
+
+
+/*
+ * Find the next revision
+ *
+ * RETURN
+ *     Branch: NULL (There cannot be a revision)
+ *     Revision: returns the next revision or NULL
+ */
+static char *
+RCS_getnext (RCSNode *rcs, const char *rev)
+{
+    Node *node = NULL;
+    RCSVers *vers = NULL;
+    char *retval = NULL;
+    const char *cmp = rev;
+    int dots = numdots (rev);
+
+    if (rcs->flags & PARTIAL)
+       RCS_reparsercsfile (rcs, NULL, NULL);
+
+    if (dots > 1)
+    {
+        node = findnode (rcs->versions, rev);
+       if (!node)
+           return NULL;
+
+       vers = node->data;
+
+       /* if there is a next version, find the node */
+       if (vers->next != NULL)
+       {
+           node = findnode (rcs->versions, vers->next);
+           if (node)
+               return xstrdup (((RCSVers *)node->data)->version);
+       }
+    }
+    else if (rcs->head)
+    {
+       node = findnode (rcs->versions, rcs->head);
+       if (node)
+       {
+           RCSVers *next = NULL;
+           vers = NULL;
+           while (node != NULL)
+           {
+               vers = next;
+               next = node->data;
+               if (next && STREQ (next->version, cmp))
+               {
+                   if (vers)
+                       retval = xstrdup (vers->version);
+                   break;
+               }
+                  
+               /* if there is a next version, find the node */
+               if (next->next != NULL)
+                   node = findnode (rcs->versions, next->next);
+               else
+                   node = NULL;
+           }
+       }
+    }
+
+    return retval;
+}
+
+
+
+/*
+ * Find the origin revision, which is the first revision
+ * on either the trunk or a branch if added there.
+ */
+static char *
+RCS_getorigin (RCSNode *rcs, const char * rev)
+{
+    char *workrev = xstrdup (rev);
+
+    if (RCS_exist_rev (rcs, workrev))
+    {
+       Node *rootnode = NULL;
+        int dots = numdots (workrev);
+
+       if (dots > 2)
+       {
+           int i;
+           char *p = workrev;
+           for (i = 1; i < dots; ++i)
+               p = strchr (p+1, '.');
+           if (!strncmp (p, ".0.", 3))
+           {
+               memmove (p, p+2, strlen (p+2) + 1);
+               --dots;
+           }
+       }
+       
+       while (dots > 1)
+       {
+          int len;
+          char *tmp = workrev;
+          workrev = xstrdup (tmp);
+
+          if (dots & 1)
+          {
+              *strrchr (workrev, '.') = '\0';
+              len = strlen (workrev);
+              *strrchr (workrev, '.') = '\0';
+          }
+          else
+          {
+              len = strlen (workrev);
+              *strrchr (workrev, '.') = '\0';
+          }
+
+          /* If a file was added on the trunk, and it is added on
+           * a branch in a second step, the '1.1.2.1' revision is
+           * dead, and timestamp of 1.1 and 1.1.2.1 are equal.
+           * Prevent returning this as root!
+           */
+          bool found = false;
+          rootnode = findnode(rcs->versions, workrev);
+          if (rootnode)
+          {
+              RCSVers *vers = rootnode->data;
+              if (vers->branches)
+              {
+                  Node *head = vers->branches->list;
+                  Node *br;
+                  for (br = head->next; br != head; br = br->next)
+                  {
+                     /* check if br->key is on branch rev */
+                     if (!strncmp (tmp, br->key, len))
+                     {
+                         Node *bn = NULL;
+                         if (bn = findnode (rcs->versions, br->key))
+                         {
+                             RCSVers *cv = bn->data;
+                             if (cv->dead)
+                             {
+                                if (cv->next)
+                                {
+                                    free (workrev);
+                                    if (STREQ (cv->version, rev))
+                                        workrev = xstrdup (rev);
+                                    else
+                                    {
+                                        bn = findnode (rcs->versions, 
cv->next);
+                                        cv = bn->data;
+                                        workrev = xstrdup (cv->version);
+                                    }
+                                    found = true;
+                                }
+                                else
+                                {
+                                    free (workrev);
+                                    workrev = NULL;
+                                }
+                             }
+                             else if (vers->dead)
+                             {
+                                /* root dead => stay on branch */
+                                free (workrev);
+                                workrev = xstrdup (cv->version);
+                                found = true;
+                             }
+                         }
+                     }
+                  }
+              }
+          }
+          if (found || !workrev)
+              return workrev;
+          else
+              dots = numdots (workrev);
+          free (tmp);
+       }
+
+       RCSVers *vers;
+       char *prev = NULL;
+
+       if (!rootnode)
+           rootnode = findnode (rcs->versions, workrev);
+       free (workrev);
+
+       while (rootnode)
+       {
+           vers = rootnode->data;
+
+           /* if there is a next version, find the node */
+           if (vers->next != NULL)
+           {
+               prev = vers->version;
+               rootnode = findnode (rcs->versions, vers->next);
+           }
+           else if (vers->dead)
+               return xstrdup (prev);
+           else
+               return xstrdup (vers->version);
+       }
+    }
+    free (workrev);
+    return NULL;
+}
+
+
+
+/*
+ * Find the branchpoint, no matter if rev points
+ * to a branch or a revision
+ */
+static char *
+RCS_getroot (RCSNode *rcs, const char *rev)
+{
+    char *retval = NULL;
+    RCSVers *vers = NULL;
+    int dots = numdots (rev);
+    
+    if (dots > 1)
+    {
+        int len;
+        char *tmp = xstrdup (rev);
+
+       /* remove ev. magic branch num */
+       if (dots > 2 && (dots & 1))
+       {
+           int i;
+           const char *p = rev;
+           for (i = 1; i < dots; ++i)
+               p = strchr (p+1, '.');
+           if (!strncmp (p, ".0.", 3))
+           {
+               strcpy (tmp + (p - rev), p + 2);
+               --dots;
+           }
+       }
+       retval = xstrdup (tmp);
+
+        if (dots & 1)
+       {
+           *strrchr (retval, '.') = '\0';
+           len = strlen (retval);
+           *strrchr (retval, '.') = '\0';
+       }
+       else
+       {
+           len = strlen (retval);
+           *strrchr (retval, '.') = '\0';
+       }
+
+       /* If a file was added on the trunk, and it is added on
+        * a branch in a second step, the '1.1.2.1' revision is
+        * dead, and timestamp of 1.1 and 1.1.2.1 are equal.
+        * Return 1.1.2.1 in this case!
+        */
+       if (rcs->flags & PARTIAL)
+           RCS_reparsercsfile (rcs, NULL, NULL);
+
+       Node *node = findnode(rcs->versions, retval);
+       if (node)
+       {
+           vers = node->data;
+           if (vers->branches)
+           {
+               char *rootdate = vers->date;
+               Node *head = vers->branches->list;
+               Node *br;
+               for (br = head->next; br != head; br = br->next)
+               {
+                  //check if br->key is on branch rev
+                  if (!strncmp (tmp, br->key, len))
+                  {
+                      if (node = findnode (rcs->versions, br->key))
+                      {
+                          vers = node->data;
+                          if (vers->dead && !RCS_datecmp (rootdate, 
vers->date))
+                          {
+                              free (retval);
+                              free (tmp);
+                              if (STREQ (vers->version, rev))
+                                  return NULL;
+                              else
+                                  return xstrdup (vers->version);
+                          }
+                      }
+                      free (tmp);
+                      return retval;
+                  }
+               }
+           }
+       }
+       free (tmp);
+       free (retval);
+    }
+    return NULL;
+}
+
+
+
+/*
+ * Find the commitid, if prev is true return the
+ * previous revision 
+ */
+static char *
+RCS_getcommitid (RCSNode *rcs, const char *commitid, bool prev)
+{
+    Node *p;
+    RCSVers *oldvers = NULL;
+
+    if (rcs->flags & PARTIAL)
+        RCS_reparsercsfile (rcs, NULL, NULL);
+
+    Node *head = rcs->versions->list;
+    for (p = head->next; p != head; p = p->next)
+    {
+        RCSVers *vers = (RCSVers *)p->data;
+
+       Node *info = findnode (vers->other_delta, "commitid");
+       if (info != NULL)
+           if (!strcmp (info->data, commitid))
+           {
+               RCSVers *newvers = NULL;
+               if (!prev)
+                   return xstrdup (vers->version);
+               if (p->next)
+                   newvers = (RCSVers *)p->next->data;
+               else
+                   newvers = NULL;
+               if (numdots (vers->version)==1)
+                   return newvers ? xstrdup (newvers->version) : NULL;
+               else
+                   return oldvers ? xstrdup (oldvers->version) : NULL;
+           }
+       oldvers = vers;
+    }
+
+    return NULL;
+}
+
+
+
+/*
+ * Find the head of branch of current revision
+ */
+static char *
+RCS_gethead (RCSNode *rcs, const char *rev)
+{
+    char *retval = NULL;
+    int dots = numdots (rev);
+    if (!dots)
+        return NULL;
+
+    char *tmp = xstrdup (rev);
+
+    /* remove ev. magic branch num */
+    if (dots > 2 && (dots & 1))
+    {
+        int i;
+        const char *p = rev;
+        for (i = 1; i < dots; ++i)
+            p = strchr (p+1, '.');
+        assert (p);
+        if (!strncmp (p, ".0.", 3))
+        {
+            strcpy (tmp + (p - rev), p + 2);
+           --dots;
+        }
+    }
+
+    if (dots > 1)
+    {
+        if (dots & 1)
+           *(strrchr (tmp, '.')) = '\0';
+       retval = RCS_branch_head (rcs, tmp);
+    }
+    else
+        retval = xstrdup (rcs->head);
+    free (tmp);
+
+    return retval;
+}
+
+
+
+/* Translate special/symbolic tags into their revision
+ * If tag is numeric, rely on its formating.
+ * If tag is symbolic, resolv it. Then check for
+ * tag extensions. If an extension is found, resolv it.
  * Returns NULL or a newly malloc'd string.
  */
 static char *
-translate_symtag (RCSNode *rcs, const char *tag)
+translate_tag (RCSNode *rcs, const char *tag)
 {
+    char c;
+    char *tmp = NULL;
+    char *retval = NULL;
+    char *tmpval = NULL;
+    bool dotstart = false;
+
     if (rcs->flags & PARTIAL)
        RCS_reparsercsfile (rcs, NULL, NULL);
 
+    if (tag[0] == '@')
+    {
+        /* handle cvsnt compatibility stuff */
+        if ( tag[1] == '<')
+           return RCS_getcommitid (rcs, tag+2, true);
+       else
+           return RCS_getcommitid (rcs, tag+1, false);
+    }
+
+    tmpval = xstrdup (tag);
+    if (isdigit ((unsigned char) *tmpval)) {
+        /* extract initial revision num */
+        char * p = tmpval;
+        do
+           ++p;
+       while (isdigit ((unsigned char) *p) || *p == '.');
+       tmp = p;
+       *(--p) = '\0';
+       retval = xstrdup (tmpval);
+    }
+    else
+        tmp = tmpval;
+
+    char *token = strtok(tmp,".");
+    if (token)
+    {
+        bool force;
+       do
+       {
+           force = false;
+           if (!retval)
+           {
+               if (token == tmp)
+                   retval = translate_symtag (rcs, token);
+               else if (STREQ (token, TAG_TRUNK))
+                   retval = RCS_head (rcs);
+               else if (STREQ (token, TAG_COMMITID))
+               {
+                   char *commitid = strtok (NULL,".");
+                   if (token = strtok (NULL,"."))
+                      force = true;
+                   if (commitid)
+                   {
+                      bool previous = (token && STREQ (token, TAG_PREVIOUS));
+                      retval = RCS_getcommitid (rcs, commitid, previous);
+                      if (previous || !retval)
+                          force = false;
+                   }
+               }
+               else if (!STREQ (token, TAG_DOTBASE))
+                   error (1, 0, "Tag '%s': invalid head: '%s'", token, tag);
+           }
+           else
+           {
+               char *p = retval;
+               if (STREQ (token, TAG_DOTHEAD))
+                   retval = RCS_gethead (rcs, p);
+               else if (STREQ (token, TAG_PREVIOUS))
+                   retval = RCS_getprevious (rcs, p);
+               else if (STREQ (token, TAG_ORIGIN))
+                   retval = RCS_getorigin (rcs, p);
+               else if (STREQ (token, TAG_ROOT))
+                   retval = RCS_getroot (rcs, p);
+               else if (STREQ (token, TAG_NEXT))
+                   retval = RCS_getnext (rcs, p);
+               else
+                   error (1, 0, "Tag '%s': invalid extension: '%s'", token, 
tag);
+               free (p);
+           }
+       }
+       while (force || (retval && (token = strtok (NULL, "."))) );
+    }
+    free (tmpval);
+
+    return retval;
+}
+
+
+
+/*
+ * Return the version associated with a particular symbolic tag.
+ * Returns NULL or a newly malloc'd string.
+ */
+static char *
+translate_symtag (RCSNode *rcs, const char *tag)
+{
     if (rcs->symbols != NULL)
     {
        Node *p;
@@ -3360,6 +4064,168 @@
 
 
 /*
+ * Do some consistency checks ...
+ * If a symbolic tag is found, return a newly allocated string
+ * Allow only:
+ * SYMTAG | SYMTAG[.prev]* | .trunk[.prev]* | HEAD | BASE | x.x.x[.prev]*
+ *
+ * ERRORS
+ *   fatal errors are generated for illegal syntax
+ */
+char *
+RCS_extract_tag (const char *tag, bool files)
+{
+    char *retval = NULL;
+    const char *p = tag;
+    bool first = true;
+
+    assert(p);
+
+    if (*p == '@')
+    {
+        if (*(p+1) == '<')
+       {
+           retval = xstrdup (p+1);
+           *retval = '@';
+       }
+       else
+           retval = xstrdup (p);
+       return retval;
+    }
+
+    if (strstr (p,".."))
+        error (1, 0, "\
+Numeric tag %s invalid.  Numeric tags should be of the form X[.X]...", tag);
+
+    bool dot = false;
+    while (p)
+    {
+        if (*p == '.')
+           dot = true;
+        else if (!isdigit ((unsigned char) *p))
+           break;
+       else if (dot && first)
+           error (1, 0, "\
+Numeric tag '%s' invalid. Numeric tags should be of the form X[.X]...", tag);
+       else
+       {
+           dot = false;
+           first = false;
+       }
+       ++p;
+    }
+
+    if ( (*p && !first && !dot) || tag[strlen (tag)-1] == '.')
+        error (1, 0, "\
+Tag '%s' is invalid. Combined tags should be of the form X[.X]...", tag);
+
+    bool deptag = false;
+    bool multi = true;
+    char *prev = NULL;
+    char *tmp = xstrdup (p);
+    char *token = strtok(tmp,".");
+    if (token)
+    {
+       do
+       {
+           if (first)
+           {
+               if (!dot)
+               {
+                   if (STREQ (token, TAG_HEAD)
+                       || STREQ (token, TAG_BASE))
+                   {
+                       deptag = true;
+                       first = false;
+                   }
+                   else if (!STREQ (token, TAG_TRUNK)
+                       && !STREQ (token, TAG_COMMITID)
+                       && !STREQ (token, TAG_PREVIOUS)
+                       && !STREQ (token, TAG_NEXT)
+                       && !STREQ (token, TAG_DOTHEAD)
+                       && !STREQ (token, TAG_DOTBASE)
+                       && !STREQ (token, TAG_ORIGIN)
+                       && !STREQ (token, TAG_ROOT))
+                   {
+                      RCS_check_tag (token);
+                      retval = xstrdup (token);
+                      first = false;
+                   }
+                   else
+                       error (1, 0,"\
+Tag '%s' invalid. Reserved expression without leading dot: '%s'", tag, token);
+               }
+               else if (STREQ (token, TAG_TRUNK))
+               {
+                   first = false;
+               }
+               else if (STREQ (token, TAG_DOTBASE))
+               {
+                   first = false;
+                   multi = false;
+               }
+               else if (STREQ (token, TAG_COMMITID))
+               {
+                   token = strtok (NULL,".");
+                   if (token)
+                   {
+                       retval = Xasprintf ("@%s",token);
+                       first = false;
+                   }
+               }
+               else if (!files)
+                   error (1, 0,"\
+Tag '%s' invalid. Tag must not be relative: '.%s'", tag, token);
+               else if (STREQ (token, TAG_ORIGIN)
+                        || STREQ (token, TAG_DOTHEAD))
+               {
+                  first = false;
+                  prev = token;
+               }
+               else if (STREQ (token, TAG_PREVIOUS)
+                        || STREQ (token, TAG_NEXT)
+                        || STREQ (token, TAG_ROOT))
+               {
+                  first = false;
+               }
+               else
+                   error (1, 0,"\
+Tag '%s' invalid. Cannot resolve head: '.%s'", tag, token);
+           }
+           else if (deptag)
+               error (1, 0,"\
+Tag '%s' invalid. Deprecated prefix before extension: '%s'", tag, token);
+           else if (!multi)
+               error (1, 0,"\
+Tag '%s' invalid. Extension not allowed: '%s'", tag, token);
+           else if (STREQ (token, TAG_ORIGIN)
+                    || STREQ (token, TAG_DOTHEAD))
+           {
+               if (!prev || !STREQ (prev, token))
+                   prev = token;
+               else
+                   error (1, 0,"\
+Tag '%s' invalid. Duplicate extension: '%s'", tag, token);
+
+           }
+           else if ((!STREQ (token, TAG_PREVIOUS))
+                     && (!STREQ (token, TAG_NEXT))
+                     && (!STREQ (token, TAG_ROOT)))
+               error (1, 0,"\
+Tag '%s' invalid. Cannot resolve extension: '%s'", tag, token);
+           else
+               prev = NULL;
+       }
+       while (!first && (token = strtok (NULL, ".")) );
+    }
+    free (tmp);
+
+    return retval;
+}
+
+
+
+/*
  * TRUE if argument has valid syntax for an RCS revision or 
  * branch number.  All characters must be digits or dots, first 
  * and last characters must be digits, and no two consecutive 
@@ -5789,7 +6655,8 @@
     /* FIXME: This check should be moved to RCS_check_tag.  There is no
        reason for it to be here.  */
     if (STREQ (tag, TAG_BASE)
-       || STREQ (tag, TAG_HEAD))
+       || STREQ (tag, TAG_HEAD)
+       || *tag == '.')
     {
        /* Print the name of the tag might be considered redundant
           with the caller, which also prints it.  Perhaps this helps
Index: src/rcs.h
===================================================================
RCS file: /cvs/ccvs/src/rcs.h,v
retrieving revision 1.81
diff -u -r1.81 rcs.h
--- src/rcs.h   23 Feb 2005 01:24:28 -0000      1.81
+++ src/rcs.h   17 Mar 2005 18:47:18 -0000
@@ -215,6 +215,7 @@
 time_t RCS_getrevtime (RCSNode * rcs, const char *rev, char *date, int fudge);
 List *RCS_symbols (RCSNode *rcs);
 void RCS_check_tag (const char *tag);
+char *RCS_extract_tag (const char *tag, bool files);
 int RCS_valid_rev (const char *rev);
 List *RCS_getlocks (RCSNode *rcs);
 void freercsnode (RCSNode ** rnodep);
Index: src/sanity.sh
===================================================================
RCS file: /cvs/ccvs/src/sanity.sh,v
retrieving revision 1.1053
diff -u -r1.1053 sanity.sh
--- src/sanity.sh       16 Mar 2005 19:57:46 -0000      1.1053
+++ src/sanity.sh       17 Mar 2005 18:47:31 -0000
@@ -1614,6 +1614,7 @@
        tests="${tests} new newb conflicts conflicts2 conflicts3"
        tests="${tests} clean"
        tests="${tests} keywordexpand"
+       tests="${tests} tagextension"
        # Checking out various places (modules, checkout -d, &c)
        tests="${tests} modules modules2 modules3 modules4 modules5 modules6"
        tests="${tests} modules7 mkmodules co-d"
@@ -12289,6 +12290,391 @@
 
 
 
+       tagextension)
+         #
+         #
+         #
+         #
+         #
+         module=tag-ext
+         mkdir $module; cd $module
+         dotest tag-ext-init-1 "$testcvs -Q co -l ."
+         echo content >file1
+         echo different content >file2
+         dotest tag-ext-init-2 "$testcvs -Q add file1 file2"
+         dotest tag-ext-init-3 "$testcvs -Q ci -madd-em"
+         echo content2 >file1
+         echo different content2 >file2
+         dotest tag-ext-init-4 "$testcvs -Q ci -mvers2"
+         dotest tag-ext-init-5 "$testcvs -Q tag -b BRANCH1"
+         echo content3 >file1
+         echo different content3 >file2
+         dotest tag-ext-init-6 "$testcvs -Q ci -mvers3"
+         dotest tag-ext-init-7 "$testcvs -Q tag -b BRANCH2"
+         dotest tag-ext-init-8 "$testcvs -Q update -r BRANCH1"
+         echo b1content >file3
+         dotest tag-ext-init-9 "$testcvs -Q add file3"
+         dotest tag-ext-init-10 "$testcvs -Q ci -maddbranch1"
+         dotest tag-ext-init-11 "$testcvs -Q update -r BRANCH2"
+         echo b2content >file3
+         dotest tag-ext-init-12 "$testcvs -Q add file3"
+         dotest tag-ext-init-13 "$testcvs -Q ci -maddbranch2"
+         dotest tag-ext-init-14 "$testcvs -Q update -A"
+         echo content >file3
+         dotest tag-ext-init-15 "$testcvs -Q add file3"
+         dotest tag-ext-init-16 "$testcvs -Q ci -maddtrunk"
+         cd ..
+         mkdir import
+         cd import
+         echo content vicontent >file3
+         dotest tag-ext-init-17 "$testcvs -Q import -mimportvendor . VENDOR 
RELEASE"
+         cd ..
+         rm -r import
+         cd $module
+
+#        dotest tag-ext-init-9 "$testcvs "
+#        dotest tag-ext-init-10 "$testcvs "
+#        dotest tag-ext-init-11 "$testcvs "
+
+
+         dotest tag-ext-1 "$testcvs -q log" "
+RCS file: ${CVSROOT_DIRNAME}/file1,v
+Working file: file1
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+       BRANCH2: 1\.3\.0\.2
+       BRANCH1: 1\.2\.0\.2
+keyword substitution: kv
+total revisions: 3;    selected revisions: 3
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  lines: +1 -1;  
commitid: ${commitid};
+vers3
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  lines: +1 -1;  
commitid: ${commitid};
+vers2
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  commitid: 
${commitid};
+add-em
+=============================================================================
+
+RCS file: ${CVSROOT_DIRNAME}/file2,v
+Working file: file2
+head: 1\.3
+branch:
+locks: strict
+access list:
+symbolic names:
+       BRANCH2: 1\.3\.0\.2
+       BRANCH1: 1\.2\.0\.2
+keyword substitution: kv
+total revisions: 3;    selected revisions: 3
+description:
+----------------------------
+revision 1\.3
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  lines: +1 -1;  
commitid: ${commitid};
+vers3
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  lines: +1 -1;  
commitid: ${commitid};
+vers2
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  commitid: 
${commitid};
+add-em
+=============================================================================
+
+RCS file: ${CVSROOT_DIRNAME}/file3,v
+Working file: file3
+head: 1\.2
+branch:
+locks: strict
+access list:
+symbolic names:
+       RELEASE: 1\.1\.1\.1
+       VENDOR: 1\.1\.1
+       BRANCH2: 1\.1\.0\.4
+       BRANCH1: 1\.1\.0\.2
+keyword substitution: kv
+total revisions: 6;    selected revisions: 6
+description:
+----------------------------
+revision 1\.2
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  lines: +1 -0;  
commitid: ${commitid};
+addtrunk
+----------------------------
+revision 1\.1
+date: ${ISO8601DATE};  author: ${username};  state: dead;  commitid: 
${commitid};
+branches:  1\.1\.1;  1\.1\.2;  1\.1\.4;
+file file3 was initially added on branch BRANCH1\.
+----------------------------
+revision 1\.1\.4\.2
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  lines: +1 -0;  
commitid: ${commitid};
+addbranch2
+----------------------------
+revision 1\.1\.4\.1
+date: ${ISO8601DATE};  author: ${username};  state: dead;  lines: +0 -0;  
commitid: ${commitid};
+file file3 was added on branch BRANCH2 on ${ISO8601DATE}
+----------------------------
+revision 1\.1\.2\.1
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  lines: +1 -0;  
commitid: ${commitid};
+addbranch1
+----------------------------
+revision 1\.1\.1\.1
+date: ${ISO8601DATE};  author: ${username};  state: Exp;  lines: +1 -0;  
commitid: ${commitid};
+importvendor
+============================================================================="
+
+         dotest tag-ext-2 "$testcvs -q update -r.base"
+         dotest tag-ext-3 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.3/[a-zA-Z0-9 :]*//T\.base
+/file2/1\.3/[a-zA-Z0-9 :]*//T\.base
+/file3/1\.2/[a-zA-Z0-9 :]*//T\.base
+D
+T\.base"
+
+         dotest tag-ext-4 "$testcvs -q update -r.trunk"
+         dotest tag-ext-5 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.3/[a-zA-Z0-9 :]*//T\.trunk
+/file2/1\.3/[a-zA-Z0-9 :]*//T\.trunk
+/file3/1\.2/[a-zA-Z0-9 :]*//T\.trunk
+D
+T\.trunk"
+
+         dotest tag-ext-6 "$testcvs -q update -r.trunk.prev" \
+"U file1
+U file2
+cvs update: \`file3' is no longer in the repository"
+         dotest tag-ext-7 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.2/[a-zA-Z0-9 :]*//T1\.2
+/file2/1\.2/[a-zA-Z0-9 :]*//T1\.2
+D
+N\.trunk\.prev"
+
+         dotest tag-ext-8 "$testcvs -q update -rBRANCH1" \
+"U file3"
+         dotest tag-ext-9 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.2/[a-zA-Z0-9 :]*//TBRANCH1
+/file2/1\.2/[a-zA-Z0-9 :]*//TBRANCH1
+/file3/1\.1\.2\.1/[a-zA-Z0-9 :]*//TBRANCH1
+D
+TBRANCH1"
+
+         echo b1content >file1
+         echo b1content >file2
+         echo b1content >file3
+         dotest tag-ext-10 "$testcvs -Q ci -maddbranch1"
+         dotest tag-ext-12 "$testcvs -Q tag -b BRANCH1-1"
+         echo b1content2 >file1
+         echo b1content2 >file2
+         echo b1content2 >file3
+         dotest tag-ext-13 "$testcvs -Q ci -maddbranch1"
+         dotest tag-ext-14 "$testcvs -q update -rBRANCH1-1" \
+"U file1
+U file2
+U file3"
+         echo b1-1content >file1
+         echo b1-1content >file2
+         echo b1-1content >file3
+         dotest tag-ext-15 "$testcvs -Q ci -maddbranch1-1"
+         echo b1-1content2 >file1
+         echo b1-1content2 >file2
+         echo b1-1content2 >file3
+         dotest tag-ext-16 "$testcvs -Q ci -maddbranch1-1"
+         dotest tag-ext-17 "cat CVS/Entries && cat CVS/Tag" \
+"/file1/1\.2\.2\.1\.2\.2/[a-zA-Z0-9 :]*//TBRANCH1-1
+/file2/1\.2\.2\.1\.2\.2/[a-zA-Z0-9 :]*//TBRANCH1-1
+/file3/1\.1\.2\.1\.2\.2/[a-zA-Z0-9 :]*//TBRANCH1-1
+D
+TBRANCH1-1"
+
+         dotest_fail tag-ext-18 "$testcvs -Q -n diff -r \.prev" \
+"${SPROG} \[diff aborted\]: Tag .\.prev. invalid\. Tag must not be relative: 
.\.prev."
+
+         dotest_fail tag-ext-19 "$testcvs diff -r \.prev file2 file3" \
+"Index: file2
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file2,v
+retrieving revision 1\.2\.2\.1\.2\.1
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.2\.2\.1\.2\.1 -r1\.2\.2\.1\.2\.2
+1c1
+< b1-1content
+---
+> b1-1content2
+Index: file3
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file3,v
+retrieving revision 1\.1\.2\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.1\.2\.1 -r1\.1\.2\.1\.2\.2
+1c1
+< b1-1content
+---
+> b1-1content2"
+
+         dotest_fail tag-ext-20 "$testcvs diff -r \.root file2 file3" \
+"Index: file2
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file2,v
+retrieving revision 1\.2\.2\.1
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.2\.2\.1 -r1\.2\.2\.1\.2\.2
+1c1
+< b1content
+---
+> b1-1content2
+Index: file3
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.1 -r1\.1\.2\.1\.2\.2
+1c1
+< b1content
+---
+> b1-1content2"
+
+         dotest_fail tag-ext-21 "$testcvs diff -r \.root.root file2 file3" \
+"Index: file2
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file2,v
+retrieving revision 1\.2
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.2 -r1\.2\.2\.1\.2\.2
+1c1
+< different content2
+---
+> b1-1content2
+${SPROG} diff: Tag \.root\.root refers to a dead (removed) revision in file 
.file3.\.
+cvs diff: No comparison available\.  Pass .-N. to .${SPROG} diff.${QUESTION}"
+
+         dotest_fail tag-ext-22 "$testcvs diff -r \.origin file2 file3" \
+"Index: file2
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file2,v
+retrieving revision 1\.1
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.1 -r1\.2\.2\.1\.2\.2
+1c1
+< different content
+---
+> b1-1content2
+Index: file3
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.1 -r1\.1\.2\.1\.2\.2
+1c1
+< b1content
+---
+> b1-1content2"
+
+         dotest_fail tag-ext-23 "$testcvs diff -r \.origin.head file2 file3" \
+"Index: file2
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file2,v
+retrieving revision 1\.3
+retrieving revision 1\.2\.2\.1\.2\.2
+diff -r1\.3 -r1\.2\.2\.1\.2\.2
+1c1
+< different content3
+---
+> b1-1content2
+Index: file3
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file3,v
+retrieving revision 1\.1\.2\.2
+retrieving revision 1\.1\.2\.1\.2\.2
+diff -r1\.1\.2\.2 -r1\.1\.2\.1\.2\.2
+1c1
+< b1content2
+---
+> b1-1content2"
+
+         dotest tag-ext-24 "$testcvs -q update -r\.trunk" \
+"U file1
+U file2
+U file3"
+
+         echo tcontent >file3
+         dotest tag-ext-25 "$testcvs -Q ci -maddtrunk"
+
+         dotest_fail tag-ext-26 "$testcvs -Q -n diff -r \.prev" \
+"${SPROG} \[diff aborted\]: Tag .\.prev. invalid\. Tag must not be relative: 
.\.prev."
+
+         dotest_fail tag-ext-27 "$testcvs diff -r \.prev file2 file3" \
+"Index: file2
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file2,v
+retrieving revision 1\.2
+retrieving revision 1\.3
+diff -r1\.2 -r1\.3
+1c1
+< different content2
+---
+> different content3
+Index: file3
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file3,v
+retrieving revision 1\.2
+retrieving revision 1\.3
+diff -r1\.2 -r1\.3
+1c1
+< content
+---
+> tcontent"
+
+         dotest_fail tag-ext-29 "$testcvs diff -r \.root file2 file3" \
+"cvs diff: tag \.root is not in file file2
+cvs diff: tag \.root is not in file file3"
+
+         dotest tag-ext-30 "$testcvs diff -r \.origin.head file2 file3" ""
+
+         dotest_fail tag-ext-31 "$testcvs diff -r BRANCH1-1\.root file2 file3" 
\
+"Index: file2
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file2,v
+retrieving revision 1\.2\.2\.1
+retrieving revision 1\.3
+diff -r1\.2\.2\.1 -r1\.3
+1c1
+< b1content
+---
+> different content3
+Index: file3
+===================================================================
+RCS file: ${CVSROOT_DIRNAME}/file3,v
+retrieving revision 1\.1\.2\.1
+retrieving revision 1\.3
+diff -r1\.1\.2\.1 -r1\.3
+1c1
+< b1content
+---
+> tcontent"
+
+         dotest_fail tag-ext-32 "$testcvs diff -r BRANCH1-1\.root\.prev\.head 
file2 file3" \
+"cvs diff: tag BRANCH1-1\.root\.prev\.head is not in file file3"
+
+         # clean up
+         dokeep
+         restore_adm
+         cd ..
+         rm -r $module
+         modify_repo rm -rf $CVSROOT_DIRNAME/file1,v
+         modify_repo rm -rf $CVSROOT_DIRNAME/file2,v
+         modify_repo rm -rf $CVSROOT_DIRNAME/file3,v
+         ;;
+
+
+
        modules)
          # Tests of various ways to define and use modules.
          # Roadmap to various modules tests:
@@ -26197,7 +26583,7 @@
          #
          dotest_fail admin-28-4 "${testcvs} admin -ntagnine:1.a.2 file2"  \
 "RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
-${SPROG} \[admin aborted\]: tag .1\.a\.2. must start with a letter"
+${SPROG} \[admin aborted\]: Tag .1\.a\.2. invalid. Cannot resolve extension: 
'a'"
 
          # Confirm that a missing tag is not a fatal error.
          dotest admin-28-5.1 "${testcvs} -Q tag BO+GUS file1" ''
Index: src/subr.c
===================================================================
RCS file: /cvs/ccvs/src/subr.c,v
retrieving revision 1.139
diff -u -r1.139 subr.c
--- src/subr.c  17 Mar 2005 17:15:19 -0000      1.139
+++ src/subr.c  17 Mar 2005 18:47:32 -0000
@@ -217,6 +217,32 @@
 
 
 
+/*
+ * Returns 1 if this is a plain revision number without extensions
+ * A plain rev num never has a dot followed by an alpha 
+ */
+int
+isrevnumonly (const char *s)
+{
+    if (!isdigit ((unsigned char) *s))
+    {
+        return 0;
+    }
+    else
+    {
+        const char * c = s+1;
+       while (c = strchr (c,'.'))
+        {
+           ++c;
+           if (*c && isalpha ((unsigned char) *c))
+               return 0;
+       }
+    }
+    return 1;
+}
+
+
+
 /* Compare revision numbers REV1 and REV2 by consecutive fields.
    Return negative, zero, or positive in the manner of strcmp.  The
    two revision numbers must have the same number of fields, or else
Index: src/subr.h
===================================================================
RCS file: /cvs/ccvs/src/subr.h,v
retrieving revision 1.3
diff -u -r1.3 subr.h
--- src/subr.h  16 Mar 2005 15:52:14 -0000      1.3
+++ src/subr.h  17 Mar 2005 18:47:32 -0000
@@ -26,6 +26,7 @@
 void free_names (int *pargc, char *argv[]);
 void line2argv (int *pargc, char ***argv, char *line, char *sepchars);
 int numdots (const char *s);
+int isrevnumonly (const char *s);
 int compare_revnums (const char *, const char *);
 char *increment_revnum (const char *);
 char *getcaller (void);
Index: src/tag.c
===================================================================
RCS file: /cvs/ccvs/src/tag.c,v
retrieving revision 1.136
diff -u -r1.136 tag.c
--- src/tag.c   17 Mar 2005 17:15:19 -0000      1.136
+++ src/tag.c   17 Mar 2005 18:47:33 -0000
@@ -1445,11 +1445,12 @@
  *   Nothing.
  */
 void
-tag_check_valid (const char *name, int argc, char **argv, int local, int aflag,
+tag_check_valid (const char *oriname, int argc, char **argv, int local, int 
aflag,
                  char *repository, bool valid)
 {
     DBM *db;
     char *valtags_filename;
+    char *name;
     int nowrite = 0;
     datum mytag, val;
     struct val_args the_val_args;
@@ -1460,43 +1461,33 @@
     TRACE (TRACE_FUNCTION,
           "tag_check_valid (name=%s, argc=%d, argv=%p, local=%d,\n"
       "                      aflag=%d, repository=%s, valid=%s)",
-          name ? name : "(name)", argc, (void *)argv, local, aflag,
+          oriname ? oriname : "(name)", argc, (void *)argv, local, aflag,
           repository ? repository : "(null)",
           valid ? "true" : "false");
 #else
     TRACE (TRACE_FUNCTION,
           "tag_check_valid (name=%s, argc=%d, argv=%lx, local=%d,\n"
       "                      aflag=%d, repository=%s, valid=%s)",
-          name ? name : "(name)", argc, (unsigned long)argv, local, aflag,
+          oriname ? oriname : "(name)", argc, (unsigned long)argv, local, 
aflag,
           repository ? repository : "(null)",
           valid ? "true" : "false");
 #endif
 
-    /* Numeric tags require only a syntactic check.  */
-    if (isdigit ((unsigned char) name[0]))
+    /* validate tag and return symbolic tag if any */
+    bool files = (argc > 0);
+    int i;
+    for (i=0; i<argc; ++i)
+       if (isdir (argv[i]))
+       {
+          files = false;
+          break;
+       }
+    if (!(name = RCS_extract_tag (oriname, files)))
     {
-       /* insert is not possible for numeric revisions */
-       assert (!valid);
-       if (RCS_valid_rev (name)) return;
-       else
-           error (1, 0, "\
-Numeric tag %s invalid.  Numeric tags should be of the form X[.X]...", name);
-    }
-
-    /* Special tags are always valid.  */
-    if (strcmp (name, TAG_BASE) == 0
-       || strcmp (name, TAG_HEAD) == 0)
-    {
-       /* insert is not possible for numeric revisions */
        assert (!valid);
        return;
     }
 
-    /* Verify that the tag is valid syntactically.  Some later code once made
-     * assumptions about this.
-     */
-    RCS_check_tag (name);
-
     /* FIXME: This routine doesn't seem to do any locking whatsoever
        (and it is called from places which don't have locks in place).
        If two processes try to write val-tags at the same time, it would
@@ -1533,6 +1524,7 @@
             */
            dbm_close (db);
            free (valtags_filename);
+           free (name);
            return;
        }
        /* FIXME: should check errors somehow (add dbm_error to myndbm.c?).  */
@@ -1582,6 +1574,7 @@
        if (db != NULL)
            dbm_close (db);
        free (valtags_filename);
+       free (name);
        return;
     }
 
@@ -1596,6 +1589,7 @@
        {
            error (0, errno, "warning: cannot create %s", valtags_filename);
            free (valtags_filename);
+           free (name);
            return;
        }
     }
@@ -1607,4 +1601,5 @@
     dbm_close (db);
     free (mytag.dptr);
     free (valtags_filename);
+    free (name);
 }
Index: src/update.c
===================================================================
RCS file: /cvs/ccvs/src/update.c,v
retrieving revision 1.252
diff -u -r1.252 update.c
--- src/update.c        17 Mar 2005 17:15:19 -0000      1.252
+++ src/update.c        17 Mar 2005 18:47:34 -0000
@@ -612,6 +612,7 @@
     {
        char *rev = RCS_getversion (finfo->rcs, tag, NULL, 1, NULL);
        if (rev != NULL
+           && !(*tag == '.' && !strcmp (tag+1, TAG_TRUNK))
            && nonbranch != (nb = !RCS_nodeisbranch (finfo->rcs, tag)))
        {
            if (nonbranch >= 0 && !warned && !quiet)
Index: src/vers_ts.c
===================================================================
RCS file: /cvs/ccvs/src/vers_ts.c,v
retrieving revision 1.62
diff -u -r1.62 vers_ts.c
--- src/vers_ts.c       17 Mar 2005 17:15:20 -0000      1.62
+++ src/vers_ts.c       17 Mar 2005 18:47:34 -0000
@@ -46,6 +46,7 @@
     struct stickydirtag *sdtp;
     Entnode *entdata;
     char *rcsexpand = NULL;
+    bool setstickynumtag = false;
 
     /* get a new Vers_TS struct */
     vers_ts = xmalloc (sizeof (Vers_TS));
@@ -159,8 +160,138 @@
      */
     if (tag || date)
     {
-       vers_ts->tag = xstrdup (tag);
-       vers_ts->date = xstrdup (date);
+        if (tag)
+       {
+           /* If a relative tag extension is used,
+            * prepend the local revision number, and set setstickynumtag
+            * so the sticky tag gets set to the numeric revision number
+            */
+           int position = 0;
+           bool head = false;
+           bool base = false;
+           bool root = false;
+           bool origin = false;
+           bool relative = true;
+           bool prepversion = false;
+           bool dot = false;
+
+           if (*tag == '.')
+              dot = true;
+
+           char *tmp = xstrdup (tag);
+           char *token = strtok (tmp, ".");
+           while (token)
+           {
+               if (position == 0)
+               {
+                   if (dot)
+                   {
+                       if (!strcmp (token, TAG_ROOT))
+                           root = true;
+                       else if (!strcmp (token, TAG_ORIGIN))
+                           origin = true;
+                       else if (!strcmp (token, TAG_DOTHEAD))
+                           head = true;
+                       else if (!strcmp (token, TAG_DOTBASE))
+                       {
+                           base = true;
+                           relative = false;
+                       }
+                       else if (strcmp (token, TAG_TRUNK)
+                                && strcmp (token, TAG_COMMITID))
+                       {
+                           prepversion = true;
+                           setstickynumtag = true;
+                           break;
+                       }
+                       else
+                           relative = false;
+                   }
+                   else
+                       relative = false;
+               }
+               else if (!isdigit ((unsigned char) *token))
+               {
+                   if (!strcmp (token, TAG_DOTHEAD))
+                       setstickynumtag = false;
+                   else if (!strcmp (token, TAG_PREVIOUS)
+                            || !strcmp (token, TAG_NEXT))
+                       setstickynumtag = true;
+               }
+               token = strtok (NULL, ".");
+               ++position;
+           }
+           free (tmp);
+
+           char *preptag = NULL;
+           if (prepversion)
+               preptag = vers_ts->vn_user;
+           else if (head || root || origin)
+           {
+               /* make sure that extensions to static tags
+                * force usage of numeric revisions so we
+                * don't send '.base.xxx' or alike to translate_tag()
+                */
+               if (vers_ts->entdata
+                   && vers_ts->entdata->tag
+                   && !(*vers_ts->entdata->tag == '.'
+                        && !strcmp (vers_ts->entdata->tag+1, TAG_DOTBASE))
+                   && strcmp (vers_ts->entdata->tag, TAG_HEAD)
+                   && strcmp (vers_ts->entdata->tag, TAG_BASE))
+               {
+                   preptag = vers_ts->entdata->tag;
+               }
+               else if (sdtp && !sdtp->aflag && sdtp->tag
+                        && !isdigit (*sdtp->tag)
+                        && !(*sdtp->tag == '.'
+                             && !strcmp (sdtp->tag+1, TAG_DOTBASE))
+                        && strcmp (sdtp->tag, TAG_HEAD)
+                        && strcmp (sdtp->tag, TAG_BASE))
+               {
+                   preptag = sdtp->tag;
+               }
+               else
+                   preptag = vers_ts->vn_user;
+           }
+
+           if (preptag)
+           {
+               char *p = NULL;
+               if (origin
+                   && (p = strrchr (preptag, '.'))
+                   && !strcmp (p+1, TAG_ORIGIN))
+               {
+                   tmp = xstrdup (preptag);
+                   tmp[p-preptag] = '\0';
+                   vers_ts->tag = Xasprintf ("%s%s", tmp, tag);
+                   free (tmp);
+               }
+               else if (head
+                        && (p = strrchr (preptag, '.'))
+                        && !strcmp (p+1, TAG_DOTHEAD))
+               {
+                   tmp = xstrdup (preptag);
+                   tmp[p-preptag] = '\0';
+                   vers_ts->tag = Xasprintf ("%s%s", tmp, tag);
+                   free (tmp);
+               }
+               else
+                   vers_ts->tag = Xasprintf ("%s%s", preptag, tag);
+           }
+           else if (base && position > 1)
+           {
+               const char *p = strchr (tag+1, '.');
+               vers_ts->tag = Xasprintf ("%s%s", vers_ts->vn_user, p);
+           }
+           else if (relative)
+               vers_ts->tag = Xasprintf (".%s%s", TAG_TRUNK, tag);
+           else
+               vers_ts->tag = xstrdup (tag);
+       }
+       if (date)
+       {
+           vers_ts->date = xstrdup (date);
+       }
     }
     else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0))
     {
@@ -189,8 +320,11 @@
        /* squirrel away the rcsdata pointer for others */
        vers_ts->srcfile = rcsdata;
 
-       if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0)
-       {
+       if (vers_ts->tag
+           && (!strcmp (vers_ts->tag, TAG_BASE)
+               || (*(vers_ts->tag) == '.'
+                   && !strcmp (vers_ts->tag+1, TAG_DOTBASE)) ) )
+          {
            vers_ts->vn_rcs = xstrdup (vers_ts->vn_user);
            vers_ts->vn_tag = xstrdup (vers_ts->vn_user);
        }
@@ -207,6 +341,15 @@
                vers_ts->vn_tag = xstrdup (vers_ts->tag);
            else
                vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs);
+
+           if (vers_ts->vn_rcs && setstickynumtag)
+           {
+               /* for some relative tags, the sticky tag
+                * needs to be set to its numeric equivalent
+                */
+               free (vers_ts->tag);
+               vers_ts->tag = xstrdup (vers_ts->vn_rcs);
+           }
        }
 
        /*


Best regards and have fun
Frank
-- 
- The LinCVS Team -
http://www.lincvs.com





reply via email to

[Prev in Thread] Current Thread [Next in Thread]