Here's the latest update to my ".trunk" patch. (Several people seem to prefer ".main" or various other strings to ".trunk", which is ok by me..., that's only a #define) Anyway, this one makes "cvs admin -l.trunk foo" and "cvs admin -u.trunk foo" work. It also includes sanity.sh tests for other "cvs admin" options and ".trunk". Though I'm no expert with "cvs admin", I rarely use it, as I expect is the case with most people. I also think I found one of the "corner cases" Greg Woods has vaguely referred to around the area of "cvs commit -r" updating trunk revisions: "cvs log -r.trunk" (or "cvs log -b" for that matter) doesn't seem to work quite right if the trunk contains foo version 1.1 and foo version 2.1. Only version 2.1 will show up by "cvs log -b" or "cvs log -r.trunk" For the latter, it is certainly a bug, for "cvs log -b" I'm not sure, maybe it was made that way on purpose. This also gave me insight into finding other such corner cases, places where strrchr() is used, looking for a dot (".") are prime stomping grounds for such corner cases. Not that I've started looking too hard for them yet. Attached is the latest ".trunk" patch against the current (6/27/2000) development version of CVS. It passes "make check" and "make remotecheck" for me on linux. (ftp.geociites.com was acting weird this morning, so I couldn't put it on the web) I grant permission to distribute this patch under the terms of the GNU Public License -- steve __________________________________________________ Do You Yahoo!? Get Yahoo! Mail - Free email you can access from anywhere! http://mail.yahoo.com/
Index: NEWS =================================================================== RCS file: /home2/cvsroot/ccvs/NEWS,v retrieving revision 1.79 diff -c -r1.79 NEWS *** NEWS 2000/05/05 14:48:37 1.79 --- NEWS 2000/06/27 05:26:00 *************** *** 1,5 **** --- 1,8 ---- Changes since 1.10: + * New ".trunk" pseudo-branch-tag added which acts just like a branch + tag, but means the trunk. + * The "cvs history" command output format has changed -- the date now includes the year and is given is ISO 8601 format (yyyy-mm-dd). Index: doc/ChangeLog =================================================================== RCS file: /home2/cvsroot/ccvs/doc/ChangeLog,v retrieving revision 1.622 diff -c -r1.622 ChangeLog *** ChangeLog 2000/06/14 17:41:56 1.622 --- ChangeLog 2000/06/27 05:26:23 *************** *** 1,3 **** --- 1,7 ---- + 2000-06-18 Stephen Cameron <[EMAIL PROTECTED]> + + * cvs.texinfo: Document new ".trunk" pseudo branch tag. + 2000-04-03 Pavel Roskin <[EMAIL PROTECTED]> * cvs.texinfo (Telling CVS to notify you): Remove backslashes Index: doc/cvs.texinfo =================================================================== RCS file: /home2/cvsroot/ccvs/doc/cvs.texinfo,v retrieving revision 1.489 diff -c -r1.489 cvs.texinfo *** cvs.texinfo 2000/06/14 17:41:56 1.489 --- cvs.texinfo 2000/06/27 05:27:55 *************** *** 3258,3264 **** as internal numbers that @sc{cvs} maintains, and tags provide a better way to distinguish between things like release 1 versus release 2 of your product ! (@pxref{Tags}). However, if you want to set the numeric revisions, the @samp{-r} option to @code{cvs commit} can do that. The @samp{-r} option implies the @samp{-f} option, in the sense that it causes the --- 3258,3273 ---- as internal numbers that @sc{cvs} maintains, and tags provide a better way to distinguish between things like release 1 versus release 2 of your product ! (@pxref{Tags}). ! ! In fact, let's just go ahead and say right now that you are ! STRONGLY advised against using @samp{cvs commit -r} ! to manually set revision numbers. It is STRONGLY recommended that ! you just allow CVS to assign revision numbers however it likes. ! You should use tags instead of trying to assign some meaning to ! revision numbers. ! ! However, if, with that caution in mind, you still want to set the numeric revisions, the @samp{-r} option to @code{cvs commit} can do that. The @samp{-r} option implies the @samp{-f} option, in the sense that it causes the *************** *** 3313,3331 **** @cindex Name, symbolic (tag) @cindex HEAD, as reserved tag name @cindex BASE, as reserved tag name You can use the @code{tag} command to give a symbolic name to a certain revision of a file. You can use the @samp{-v} flag to the @code{status} command to see all tags that a file has, and which revision numbers they represent. Tag names must start with an uppercase or lowercase letter and can contain uppercase and lowercase letters, digits, ! @samp{-}, and @samp{_}. The two tag names @code{BASE} ! and @code{HEAD} are reserved for use by @sc{cvs}. It is expected that future names which are special to @sc{cvs} will be specially named, for example by starting with @samp{.}, rather than being named analogously to @code{BASE} and @code{HEAD}, to avoid conflicts with ! actual tag names. @c Including a character such as % or = has also been @c suggested as the naming convention for future @c special tag names. Starting with . is nice because --- 3322,3342 ---- @cindex Name, symbolic (tag) @cindex HEAD, as reserved tag name @cindex BASE, as reserved tag name + @cindex .trunk, as reserved tag name You can use the @code{tag} command to give a symbolic name to a certain revision of a file. You can use the @samp{-v} flag to the @code{status} command to see all tags that a file has, and which revision numbers they represent. Tag names must start with an uppercase or lowercase letter and can contain uppercase and lowercase letters, digits, ! @samp{-}, and @samp{_}. The three tag names @code{BASE}, ! @code{HEAD}, and @code{.trunk} are reserved for use by @sc{cvs}. It is expected that future names which are special to @sc{cvs} will be specially named, for example by starting with @samp{.}, rather than being named analogously to @code{BASE} and @code{HEAD}, to avoid conflicts with ! actual tag names. (The previous sentence is the reason `.trunk' ! begins with a dot, since it predates `.trunk'. @c Including a character such as % or = has also been @c suggested as the naming convention for future @c special tag names. Starting with . is nice because *************** *** 3630,3636 **** If you specify the @samp{-r} option to @code{cvs rtag}, then @sc{cvs} tags the files which have been removed, and thereby avoids this problem. For example, one ! might specify @code{-r HEAD} to tag the head. On the subject of adding and removing files, the @code{cvs rtag} command has a @samp{-a} option which --- 3641,3648 ---- If you specify the @samp{-r} option to @code{cvs rtag}, then @sc{cvs} tags the files which have been removed, and thereby avoids this problem. For example, one ! might specify @code{-r HEAD} to tag the head, though ! you may prefer the pseudo branch tag @code{-r .trunk}. On the subject of adding and removing files, the @code{cvs rtag} command has a @samp{-a} option which *************** *** 3692,3698 **** you delete them with @samp{cvs update -A}. The @samp{-A} option retrieves the version of the file from the head of the trunk, and forgets any sticky tags, ! dates, or options. @cindex Sticky date The most common use of sticky tags is to identify which --- 3704,3714 ---- you delete them with @samp{cvs update -A}. The @samp{-A} option retrieves the version of the file from the head of the trunk, and forgets any sticky tags, ! dates, or options. (Note, `-A' clears ALL sticky tags, ! including -kb, etc., which you may not want, if for ! example you simply want to switch your working directory ! to the trunk. For this purpose, you may want to use ! the special `.trunk' branch tag name for the trunk.) @cindex Sticky date The most common use of sticky tags is to identify which *************** *** 8073,8078 **** --- 8089,8095 ---- @item -r @var{tag} @cindex HEAD, special tag @cindex BASE, special tag + @cindex .trunk, special tag Use the revision specified by the @var{tag} argument instead of the default @dfn{head} revision. As well as arbitrary tags defined with the @code{tag} or @code{rtag} command, two special tags are *************** *** 8102,8107 **** --- 8119,8129 ---- @c same for "diff" as for everyone else), test cases @c written (similar to the ones in "head"), new tests @c cases written for things like default branches, &c. + @c + @c Hmm. I've made ".trunk", which works as ".thead" + @c above, but additionally works like a branch tag + @c for the trunk, that is, you can commit changes to it. + @c The tag specification is sticky when you use this @c option *************** *** 8208,8213 **** --- 8230,8238 ---- @c the optional argument). Note that -bHEAD does not @c work, as of 17 Sep 1997, but probably will once "cvs @c admin" is internal to CVS. + @c + @c Hmm, I wonder if ".trunk" works with "cvs admin"? + @c @cindex Comment leader @item -c@var{string} *************** *** 8819,8825 **** either a branch, or a revision on the main trunk that is higher than any existing revision number (@pxref{Assigning revisions}). You ! cannot commit to a specific revision on a branch. @c FIXME: Need xref for branch case. @end table --- 8844,8863 ---- either a branch, or a revision on the main trunk that is higher than any existing revision number (@pxref{Assigning revisions}). You ! cannot commit to a specific revision on a branch. Note, ! `.trunk' is valid as a branch tag for the `-r' option, and ! will commit the file as the newest revision on the trunk. ! @c ! @c However, if the file in the working directory has a ! @c sticky tag other than .trunk, the sticky tag will not ! @c be changed, and even if the file differs from the repository ! @c revision, it will be marked 'up-to-date', ! @c and a subsequent 'cvs commit' will not commit those changes. ! @c no doubt this is a bug, though I'm not sure what the right ! @c fix, is, either change the sticky tag, or leave the file's ! @c status as 'locally-modified'. ! @c ! @c FIXME: Need xref for branch case. @end table *************** *** 9010,9015 **** --- 9048,9069 ---- One or both @samp{-r} options can be replaced by a @samp{-D @var{date}} option, described above. + + CAUTION: the special tag `HEAD' is interpreted by + the `cvs diff' command in a different way than it + is interpreted by any other cvs command. `cvs diff' + takes `-r HEAD' to mean the following, as nearly as + I can tell: + + For `cvs diff', `HEAD' means the most recent revision + on the `current branch' (taking into account whatever + sticky tags are active in your working directory) unless + a particular file has not had a revision committed to the + branch, in which case the head revision of the trunk is + taken. This is clearly wrong. You should use either + `.trunk' or the branch tag name rather than `HEAD'. + (The `.trunk' tag acts as a branch tag name for the trunk.) + @end table @c Conceptually, this is a disaster. There are 3 Index: src/ChangeLog =================================================================== RCS file: /home2/cvsroot/ccvs/src/ChangeLog,v retrieving revision 1.1921 diff -c -r1.1921 ChangeLog *** ChangeLog 2000/06/24 04:55:29 1.1921 --- ChangeLog 2000/06/27 05:28:26 *************** *** 1,3 **** --- 1,26 ---- + + 2000-06-19 Stephen Cameron <[EMAIL PROTECTED]> + + * checkout.c (checkout): Add ".trunk" pseudo branch tag feature + * commit.c (classify_file_internal): Ditto + (check_fileproc): Ditto + (remove_file): Ditto + * cvs.h: add definition of TAG_TRUNK (".trunk") plus comments + * diff.c: (diff_fileproc): Handle ".trunk" pseudo branch tag. + (diff_file_nodiff): Ditto + * entriex.c (WriteTag): modified to magically treat ".trunk" as + a branch tag. + * log.c (cvslog): Modified to handle ".trunk" pseudo branch tag. + * rcs.c (RCS_tag2rev): + (RCS_gettag): Modified to reject ".trunk" reserved tag + (RCS_settag): Ditto + (RCS_trunk): New function + * rcs.h (RCS_trunk): New function prototype + * sanity.sh: New suite of tests for ".trunk", called + "btrunktag" + * status.c (status_fileproc): handle ".trunk" pseudo branch tag + * tag.c (tag_check_valid): handle ".trunk" pseudo branch tag + 2000-06-23 Larry Jones <[EMAIL PROTECTED]> * client.c (send_dirent_proc): Don't allocate ignlist if you're Index: src/admin.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/admin.c,v retrieving revision 1.64 diff -c -r1.64 admin.c *** admin.c 2000/06/13 21:30:44 1.64 --- admin.c 2000/06/27 05:28:30 *************** *** 801,810 **** break; case 'l': ! status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0); break; case 'u': ! status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0); break; default: assert(0); /* can't happen */ } --- 801,826 ---- break; case 'l': ! { ! char *rev; ! ! rev = arg[2] ? arg + 2 : NULL; ! ! if (rev && strcmp(rev, TAG_TRUNK)==0) ! rev = NULL; ! status |= RCS_lock (rcs, rev, 0); ! } break; case 'u': ! { ! char *rev; ! ! rev = arg[2] ? arg + 2 : NULL; ! ! if (rev && strcmp(rev, TAG_TRUNK)==0) ! rev = NULL; ! status |= RCS_unlock (rcs, rev, 0); ! } break; default: assert(0); /* can't happen */ } Index: src/checkout.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/checkout.c,v retrieving revision 1.89 diff -c -r1.89 checkout.c *** checkout.c 2000/06/15 15:33:30 1.89 --- checkout.c 2000/06/27 05:28:36 *************** *** 197,203 **** break; case 'r': tag = optarg; ! checkout_prune_dirs = 1; break; case 'D': date = Make_Date (optarg); --- 197,204 ---- break; case 'r': tag = optarg; ! if (strcmp(optarg, TAG_TRUNK) != 0) ! checkout_prune_dirs = 1; break; case 'D': date = Make_Date (optarg); Index: src/commit.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/commit.c,v retrieving revision 1.162 diff -c -r1.162 commit.c *** commit.c 2000/06/21 22:28:36 1.162 --- commit.c 2000/06/27 05:28:50 *************** *** 694,703 **** noexec = quiet = really_quiet = 1; /* handle specified numeric revision specially */ ! if (saved_tag && isdigit ((unsigned char) *saved_tag)) { /* If the tag is for the trunk, make sure we're at the head */ ! if (numdots (saved_tag) < 2) { status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL, 1, aflag, vers, 0); --- 694,704 ---- noexec = quiet = really_quiet = 1; /* handle specified numeric revision specially */ ! if (saved_tag && (isdigit ((unsigned char) *saved_tag) || ! strcmp(saved_tag, TAG_TRUNK) == 0)) { /* If the tag is for the trunk, make sure we're at the head */ ! if (numdots (saved_tag) < 2 || strcmp(saved_tag, TAG_TRUNK) == 0 ) { status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL, 1, aflag, vers, 0); *************** *** 753,758 **** --- 754,760 ---- else status = Classify_File (finfo, saved_tag, (char *) NULL, (char *) NULL, 1, 0, vers, 0); + noexec = save_noexec; quiet = save_quiet; really_quiet = save_really_quiet; *************** *** 826,835 **** * some quick sanity checks; if no numeric -r option specified: * - can't have a sticky date * - can't have a sticky tag that is not a branch * Also, * - if status is T_REMOVED, can't have a numeric tag * - if status is T_ADDED, rcs file must not exist unless on ! * a branch * - if status is T_ADDED, can't have a non-trunk numeric rev * - if status is T_MODIFIED and a Conflict marker exists, don't * allow the commit if timestamp is identical or if we find --- 828,838 ---- * some quick sanity checks; if no numeric -r option specified: * - can't have a sticky date * - can't have a sticky tag that is not a branch + * (except if it's TAG_TRUNK) * Also, * - if status is T_REMOVED, can't have a numeric tag * - if status is T_ADDED, rcs file must not exist unless on ! * a branch (and count TAG_TRUNK as a branch) * - if status is T_ADDED, can't have a non-trunk numeric rev * - if status is T_MODIFIED and a Conflict marker exists, don't * allow the commit if timestamp is identical or if we find *************** *** 846,851 **** --- 849,855 ---- return (1); } if (status == T_MODIFIED && vers->tag && + strcmp(vers->tag, TAG_TRUNK)!=0 && !RCS_isbranch (finfo->rcs, vers->tag)) { error (0, 0, *************** *** 924,930 **** } if (status == T_ADDED) { ! if (vers->tag == NULL) { char *rcs; --- 928,934 ---- } if (status == T_ADDED) { ! if (vers->tag == NULL || strcmp(vers->tag, TAG_TRUNK) == 0) { char *rcs; *************** *** 995,1001 **** li = ((struct logfile_info *) xmalloc (sizeof (struct logfile_info))); li->type = status; ! li->tag = xstrdup (vers->tag); li->rev_old = xstrdup (vers->vn_rcs); li->rev_new = NULL; p->data = (char *) li; --- 999,1010 ---- li = ((struct logfile_info *) xmalloc (sizeof (struct logfile_info))); li->type = status; ! ! if (vers->tag != NULL && strcmp(vers->tag, TAG_TRUNK) == 0) ! li->tag = NULL; ! else ! li->tag = xstrdup (vers->tag); ! li->rev_old = xstrdup (vers->vn_rcs); li->rev_new = NULL; p->data = (char *) li; *************** *** 1007,1020 **** p->delproc = ci_delproc; ci = (struct commit_info *) xmalloc (sizeof (struct commit_info)); ci->status = status; ! if (vers->tag) if (isdigit ((unsigned char) *vers->tag)) ci->rev = xstrdup (vers->tag); else ci->rev = RCS_whatbranch (finfo->rcs, vers->tag); else ci->rev = (char *) NULL; ! ci->tag = xstrdup (vers->tag); ci->options = xstrdup(vers->options); p->data = (char *) ci; (void) addnode (cilist, p); --- 1016,1035 ---- p->delproc = ci_delproc; ci = (struct commit_info *) xmalloc (sizeof (struct commit_info)); ci->status = status; ! ! if (vers->tag && strcmp(vers->tag, TAG_TRUNK)!=0) if (isdigit ((unsigned char) *vers->tag)) ci->rev = xstrdup (vers->tag); else ci->rev = RCS_whatbranch (finfo->rcs, vers->tag); else ci->rev = (char *) NULL; ! ! if (vers->tag && strcmp(vers->tag, TAG_TRUNK) == 0) ! ci->tag = (char *) NULL; ! else ! ci->tag = xstrdup (vers->tag); ! ci->options = xstrdup(vers->options); p->data = (char *) ci; (void) addnode (cilist, p); *************** *** 1646,1652 **** error (1, 0, "internal error: no parsed RCS file"); branch = 0; ! if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag))) { /* a symbolic tag is specified; just remove the tag from the file */ if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0) --- 1661,1668 ---- error (1, 0, "internal error: no parsed RCS file"); branch = 0; ! if (tag && strcmp(tag, TAG_TRUNK)!=0 && ! !(branch = RCS_nodeisbranch (finfo->rcs, tag))) { /* 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: /home2/cvsroot/ccvs/src/cvs.h,v retrieving revision 1.202 diff -c -r1.202 cvs.h *** cvs.h 2000/06/14 19:38:15 1.202 --- cvs.h 2000/06/27 05:28:53 *************** *** 245,256 **** #endif /* USE_VMS_FILENAMES */ /* ! * Special tags. -rHEAD refers to the head of an RCS file, regardless of any ! * sticky tags. -rBASE refers to the current revision the user has checked ! * out This mimics the behaviour of RCS. */ #define TAG_HEAD "HEAD" #define TAG_BASE "BASE" /* Environment variable used by CVS */ #define CVSREAD_ENV "CVSREAD" /* make files read-only */ --- 245,278 ---- #endif /* USE_VMS_FILENAMES */ /* ! * Special tags. ! * -rHEAD refers to the tip revision on the trunk, _except_ for ! * "cvs diff". "cvs diff" interprets -rHEAD to mean the tip ! * revision of the current branch, however, that behavior is ! * broken, because if the file has not been branched, that is, ! * the revision on the branch is the same one that's on the trunk ! * then the tip revision of the trunk is reported. Also, it's ! * not clear (to me) what happens in the instance of a sticky ! * non-branch tag what -rHEAD is supposed to mean. So, -rHEAD ! * is probably a lost cause, unless you redefine what it means. ! * ! * -rBASE refers to the current revision the user has checked ! * out This mimics the behaviour of RCS. ! * ! * -r.trunk refers to the head revision on the trunk. ! * (necessary for this to exist so that the trunk is not anonymous.) ! * "cvs commit" and "cvs status" have been hacked to believe ".trunk" ! * is a "branch tag", even though, really, it's not. ! * This is more like how I think -rHEAD probably should have ! * always worked. I chose ".trunk" as the name for various ! * reasons, (lowercase is easier to type, nobody currently has ! * any real tag names that begin with dots.) ! * */ + #define TAG_HEAD "HEAD" #define TAG_BASE "BASE" + #define TAG_TRUNK ".trunk" /* Environment variable used by CVS */ #define CVSREAD_ENV "CVSREAD" /* make files read-only */ Index: src/diff.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/diff.c,v retrieving revision 1.83 diff -c -r1.83 diff.c *** diff.c 1999/04/15 00:12:26 1.83 --- diff.c 2000/06/27 05:28:59 *************** *** 461,466 **** --- 461,477 ---- if (head != NULL) free (head); } + else /* special handling for TAG_TRUNK */ + if (diff_rev1 && strcmp (diff_rev1, TAG_TRUNK) == 0) + { + char *trunk = + (vers->vn_rcs == NULL + ? NULL + : RCS_trunk (vers->srcfile)); + exists = trunk != NULL; + if (trunk != NULL) + free (trunk); + } else { Vers_TS *xvers; *************** *** 843,855 **** use_rev1 = ((vers->vn_rcs == NULL || vers->srcfile == NULL) ? NULL : RCS_branch_head (vers->srcfile, vers->vn_rcs)); ! else ! { ! xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0); ! if (xvers->vn_rcs != NULL) ! use_rev1 = xstrdup (xvers->vn_rcs); ! freevers_ts (&xvers); ! } } if (diff_rev2 || diff_date2) { --- 854,874 ---- use_rev1 = ((vers->vn_rcs == NULL || vers->srcfile == NULL) ? NULL : RCS_branch_head (vers->srcfile, vers->vn_rcs)); ! else ! { ! /* special handling for TAG_TRUNK */ ! if (diff_rev1 && strcmp (diff_rev1, TAG_TRUNK) == 0) ! use_rev1 = ((vers->vn_rcs == NULL || vers->srcfile == NULL) ! ? NULL ! : RCS_trunk (vers->srcfile)); ! else ! { ! xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0); ! if (xvers->vn_rcs != NULL) ! use_rev1 = xstrdup (xvers->vn_rcs); ! freevers_ts (&xvers); ! } ! } } if (diff_rev2 || diff_date2) { *************** *** 859,869 **** ? NULL : RCS_branch_head (vers->srcfile, vers->vn_rcs)); else ! { ! xvers = Version_TS (finfo, NULL, diff_rev2, diff_date2, 1, 0); ! if (xvers->vn_rcs != NULL) ! use_rev2 = xstrdup (xvers->vn_rcs); ! freevers_ts (&xvers); } if (use_rev1 == NULL) --- 878,896 ---- ? NULL : RCS_branch_head (vers->srcfile, vers->vn_rcs)); else ! { ! /* special handling for TAG_TRUNK */ ! if (diff_rev2 && strcmp (diff_rev2, TAG_TRUNK) == 0) ! use_rev2 = ((vers->vn_rcs == NULL || vers->srcfile == NULL) ! ? NULL ! : RCS_trunk (vers->srcfile)); ! else ! { ! xvers = Version_TS (finfo, NULL, diff_rev2, diff_date2, 1, 0); ! if (xvers->vn_rcs != NULL) ! use_rev2 = xstrdup (xvers->vn_rcs); ! freevers_ts (&xvers); ! } } if (use_rev1 == NULL) Index: src/entries.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/entries.c,v retrieving revision 1.45 diff -c -r1.45 entries.c *** entries.c 1999/09/29 19:24:15 1.45 --- entries.c 2000/06/27 05:29:04 *************** *** 662,667 **** --- 662,674 ---- fout = open_file (tmp, "w+"); if (tag) { + /* Let's imagine that the magic TAG_TRUNK + tag is a branch tag, even though it's really not. + this is so that "cvs add" will allow us to proceed */ + + if (strcmp(tag, TAG_TRUNK)==0) + nonbranch=0; + if (nonbranch) { if (fprintf (fout, "N%s\n", tag) < 0) Index: src/log.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/log.c,v retrieving revision 1.62 diff -c -r1.62 log.c *** log.c 2000/06/21 22:28:37 1.62 --- log.c 2000/06/27 05:29:11 *************** *** 232,237 **** --- 232,239 ---- break; case 'r': *prl = log_parse_revlist (optarg); + if (optarg != NULL && strcmp(optarg, TAG_TRUNK)==0) + log_data.default_branch = 1; prl = &(*prl)->next; break; case 's': Index: src/rcs.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/rcs.c,v retrieving revision 1.235 diff -c -r1.235 rcs.c *** rcs.c 2000/06/16 18:34:52 1.235 --- rcs.c 2000/06/27 05:29:52 *************** *** 2198,2203 **** --- 2198,2207 ---- if (tag && STREQ (tag, TAG_HEAD)) return (RCS_head (rcs)); + /* If tag is "TRUNK", special case to get the trunk RCS revision */ + if (tag && STREQ (tag, TAG_TRUNK)) + return (RCS_trunk (rcs)); + /* If valid tag let translate_symtag say yea or nay. */ rev = translate_symtag (rcs, tag); *************** *** 2248,2253 **** --- 2252,2261 ---- #endif return (RCS_head (rcs)); + /* If tag is "TRUNK", special case to get trunk RCS revision */ + if (tag && (STREQ (tag, TAG_TRUNK) )) + return (RCS_trunk (rcs)); + if (!isdigit ((unsigned char) tag[0])) { char *version; *************** *** 2793,2798 **** --- 2801,2822 ---- } /* + * Get the "trunk" of the RCS file. + * or, the real head. + * Returns NULL or a newly malloc'd string. + */ + + char * + RCS_trunk (rcs) + RCSNode *rcs; + { + /* make sure we have something to look at... */ + assert (rcs != NULL); + + return (xstrdup (rcs->head)); + } + + /* * Get the head of the RCS file. If branch is set, this is the head of the * branch, otherwise the real head. * Returns NULL or a newly malloc'd string. *************** *** 5561,5568 **** /* 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)) { /* Print the name of the tag might be considered redundant with the caller, which also prints it. Perhaps this helps --- 5585,5593 ---- /* 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_TRUNK)) { /* 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: /home2/cvsroot/ccvs/src/rcs.h,v retrieving revision 1.55 diff -c -r1.55 rcs.h *** rcs.h 2000/06/12 21:47:32 1.55 --- rcs.h 2000/06/27 05:29:54 *************** *** 202,207 **** --- 202,208 ---- int RCS_nodeisbranch PROTO((RCSNode *rcs, const char *tag)); char *RCS_whatbranch PROTO((RCSNode *rcs, const char *tag)); char *RCS_head PROTO((RCSNode * rcs)); + char *RCS_trunk PROTO((RCSNode * rcs)); int RCS_datecmp PROTO((char *date1, char *date2)); time_t RCS_getrevtime PROTO((RCSNode * rcs, char *rev, char *date, int fudge)); List *RCS_symbols PROTO((RCSNode *rcs)); Index: src/sanity.sh =================================================================== RCS file: /home2/cvsroot/ccvs/src/sanity.sh,v retrieving revision 1.608 diff -c -r1.608 sanity.sh *** sanity.sh 2000/06/14 20:40:53 1.608 --- sanity.sh 2000/06/27 05:32:06 *************** *** 670,675 **** --- 670,678 ---- # Multiple root directories and low-level protocol tests. tests="${tests} multiroot multiroot2 multiroot3 multiroot4" tests="${tests} rmroot reposmv pserver server server2 client" + # ".trunck" pseudo branch tag + tests="${tests} btrunktag" + else tests="$*" fi *************** *** 796,802 **** "${PROG} [a-z]*: nothing known about ssfile ${PROG} "'\[[a-z]* aborted\]: correct the above errors first!' cd ../.. ! dotest basica-5 "${testcvs} -q ci -m add-it" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v done Checking in sdir/ssdir/ssfile; --- 799,805 ---- "${PROG} [a-z]*: nothing known about ssfile ${PROG} "'\[[a-z]* aborted\]: correct the above errors first!' cd ../.. ! dotest basica-5 "${testcvs} -q ci -m add-it" \ "RCS file: ${TESTDIR}/cvsroot/first-dir/sdir/ssdir/ssfile,v done Checking in sdir/ssdir/ssfile; *************** *** 20538,20543 **** --- 20541,20972 ---- rm ${TESTDIR}/serveme CVS_SERVER=${testcvs}; export CVS_SERVER fi # skip the whole thing for local + ;; + btrunktag) + + # test operations with "-r .trunk" + + mkdir 1; cd 1 + dotest trunkbtag1 "${testcvs} -q co -l -r .trunk ." '' + mkdir my-dir + dotest btrunktag2 "${testcvs} add my-dir" \ + "Directory ${TESTDIR}/cvsroot/my-dir added to the repository + --> Using per-directory sticky tag "'`.'"trunk'" + cd .. + rm -r 1 + + dotest btrunktag3 "${testcvs} co -r .trunk my-dir" \ + "${PROG} [a-z]*: Updating my-dir" + + dotest btrunktag3 "${testcvs} co my-dir" \ + "${PROG} [a-z]*: Updating my-dir" + cd my-dir + + # add a file + + echo xyz > xyz + dotest btrunktag4 "${testcvs} add xyz" \ + "${PROG} [a-z]*: "'scheduling file `xyz'\'' for addition on branch `.trunk'\'" + ${PROG}"' [a-z]*: use '\''cvs commit'\'' to add this file permanently' + + # commit the file + + dotest btrunktag5 "${testcvs} commit -m addxyz xyz" \ + "RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + done + Checking in xyz; + ${TESTDIR}/cvsroot/my-dir/xyz,v <-- xyz + initial revision: 1.1 + done" + # status the file + # Running "cvs status" and matching output is too + # error-prone, too likely to falsely fail. Instead, we'll + # just grep the Entries lines: (I found this out the hard way.) + + dotest btrunktag6 "grep xyz ./CVS/Entries" \ + "/xyz/1.1/[A-Za-z0-9 :]*//T.trunk" + + # edit the file and commit again + echo xyz >> xyz + dotest btrunktag7 "${testcvs} commit -m editxyz xyz" \ + "Checking in xyz; + ${TESTDIR}/cvsroot/my-dir/xyz,v <-- xyz + new revision: 1.2; previous revision: 1.1 + done" + + # create a branch, commit some changes to the branch, + # commit some more changes to the trunk, check out a + # specific revision that is neither the head of the branch + # nor the trunk, and do a cvs rdiff between the head of + # the branch and the head of the trunk.A + # (that is, test some unique functionality provided by ".trunk") + # (well, ok, -r1 would work in this case.) + + dotest btrunktag8 "${testcvs} tag stickytag" \ + "${PROG} [a-z]*: Tagging . + T xyz" + dotest btrunktag9 "${testcvs} tag -r stickytag -b mybranch" \ + "${PROG} [a-z]*: Tagging . + T xyz" + dotest btrunktag9a "${testcvs} -q update -r mybranch" "" + echo mybranch >> xyz + dotest btrunktag10 "${testcvs} commit -m branchedit" \ + "${PROG} [a-z]*: Examining . + Checking in xyz; + ${TESTDIR}/cvsroot/my-dir/xyz,v <-- xyz + new revision: 1.2.2.1; previous revision: 1.2 + done" + dotest btrunktag11 "${testcvs} update -r .trunk" \ + "${PROG} [a-z]*: Updating . + [UP] xyz" + echo trunkedit >> xyz + dotest btrunktag12 "${testcvs} commit -m trunkedit" \ + "${PROG} [a-z]*: Examining . + Checking in xyz; + ${TESTDIR}/cvsroot/my-dir/xyz,v <-- xyz + new revision: 1.3; previous revision: 1.2 + done" + dotest btrunktag13 "${testcvs} update -r stickytag" \ + "${PROG} [a-z]*: Updating . + [UP] xyz" + # try cvs rdiff + + dotest btrunktag14a \ + "${testcvs} rdiff -s -r mybranch -r .trunk my-dir" \ + "${PROG} [a-z]*: Diffing my-dir + File my-dir/xyz changed from revision 1\.2\.2\.1 to 1\.3" + + # try cvs diff + + dotest_fail btrunktag14b "${testcvs} diff -u -r mybranch -r .trunk" \ + "${PROG} [a-z]*: Diffing . + Index: xyz + =================================================================== + RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + retrieving revision 1\.2\.2\.1 + retrieving revision 1\.3 + diff -u -r1\.2\.2\.1 -r1\.3 + --- xyz [0-9/]* [0-9:]* 1\.2\.2\.1 + ${PLUS}${PLUS}${PLUS} xyz [0-9/]* [0-9:]* 1\.3 + @@ -1,3 ${PLUS}1,3 @@ + xyz + xyz + -mybranch + ${PLUS}trunkedit" + + # make sure cvs diff works with just one -r + + dotest_fail btrunktag14a "${testcvs} diff -u -r .trunk" \ + "${PROG} [a-z]*: Diffing \. + Index: xyz + =================================================================== + RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + retrieving revision 1\.3 + retrieving revision 1\.2 + diff -u -r1\.3 -r1\.2 + --- xyz [0-9/]* [0-9:]* 1\.3 + +++ xyz [0-9/]* [0-9:]* 1\.2 + @@ -1,3 ${PLUS}1,2 @@ + xyz + xyz + -trunkedit" + + # make sure cvs log works + + dotest btrunktag15 "${testcvs} log -r.trunk" \ + "${PROG} [a-z]*: Logging . + + RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + Working file: xyz + head: 1\.3 + branch: + locks: strict + access list: + symbolic names: + mybranch: 1\.2\.0\.2 + stickytag: 1\.2 + keyword substitution: kv + total revisions: 4; selected revisions: 3 + description: + ---------------------------- + revision 1\.3 + date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 + trunkedit + ---------------------------- + revision 1\.2 + date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; lines: ${PLUS}1 -0 + branches: 1\.2\.2; + editxyz + ---------------------------- + revision 1\.1 + date: [0-9/]* [0-9:]*; author: ${username}; state: Exp; + addxyz + =============================================================================" + + # try cvs update with -j options involving .trunk + + dotest btrunktag16 "${testcvs} update -j mybranch -j .trunk" \ + "${PROG} [a-z]*: Updating \. + RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + retrieving revision 1\.2\.2\.1 + retrieving revision 1\.3 + Merging differences between 1\.2\.2\.1 and 1\.3 into xyz + rcsmerge: warning: conflicts during merge" + + # undo the conflicted merge, + + rm xyz + + # note, discrepancy here between client-server CVS + # and local CVS, "xyz was lost" message printed only + # by local-CVS but not by client-server CVS. + # This is not a ".trunk" related problem though. + # + + if [ "$remote" = "yes" ] + then + dotest btrunktag17 "${testcvs} -q update" \ + "[UP] xyz" + else + dotest btrunktag17 "${testcvs} -q update" \ + "${PROG} [a-z]*: warning: xyz was lost + [UP] xyz" + fi + + # try cvs annotate + dotest btrunktag18 "${testcvs} annotate -r .trunk xyz" \ + "Annotations for xyz + \*\*\*\*\*\*\*\*\*\*\*\*\*\*\* + 1\.1 (${username} [0-9]*-[A-Za-z]*-[0-9]*): xyz + 1\.2 (${username} [0-9]*-[A-Za-z]*-[0-9]*): xyz + 1\.3 (${username} [0-9]*-[A-Za-z]*-[0-9]*): trunkedit" + + # try cvs update -A + + dotest btrunktag19 "${testcvs} update -A" \ + "${PROG} [a-z]*: Updating \. + [UP] xyz" + + # status the file (just grep the Entries lines) + + dotest btrunktag20 "grep xyz ./CVS/Entries" \ + "/xyz/1\.3/[A-Za-z ]*[0-9]* [0-9:]* [0-9]*//" + + # try switching working directory back: cvs update -r .trunk + dotest btrunktag21 "${testcvs} update -r .trunk" \ + "${PROG} [a-z]*: Updating \." + + # status the file (just grep the Entries lines) + + dotest btrunktag22 "grep xyz ./CVS/Entries" \ + "/xyz/1\.3/[A-Za-z ]*[0-9]* [0-9:]* [0-9]*//T.trunk" + + # try cvs remove + rm xyz + dotest btrunktag23 "${testcvs} remove xyz" \ + "${PROG} [a-z]*: scheduling "\`"xyz"\'" for removal + ${PROG} [a-z]*: use "\'"cvs commit"\'" to remove this file permanently" + + # commit the removal + dotest btrunktag24 "${testcvs} commit -m removexyz" \ + "${PROG} [a-z]*: Examining \. + Removing xyz; + ${TESTDIR}/cvsroot/my-dir/xyz,v <-- xyz + new revision: delete; previous revision: 1\.3 + done" + + # update working directory to the branch + dotest btrunktag25 "${testcvs} update -r mybranch" \ + "${PROG} [a-z]*: Updating \. + [UP] xyz" + + # merge in the trunk just to see if the file disappears. + + dotest btrunktag26 "${testcvs} update -j stickytag -j .trunk" \ + "${PROG} [a-z]*: Updating \. + ${PROG} [a-z]*: scheduling xyz for removal" + + # reverse the merge, file would come back + # except we just "cvs removed" it with the prior merge + # so that's a conflict, (is the file removed, or isn't it?) + # At least I guess that's the logic. The file remains + # in the removed state and a commit will remove it. + # this behavior is identical if two branches are used + # instead of one branch and ".trunk", therefore ".trunk" + # is working perfectly. + + dotest btrunktag27 "${testcvs} update -j .trunk -j stickytag" \ + "${PROG} [a-z]*: Updating \. + R xyz + ${PROG} [a-z]*: file xyz exists, but has been added in revision stickytag" + + # commit, just to see what happens + + dotest btrunktag28 "${testcvs} commit -m removesticky" \ + "${PROG} [a-z]*: Examining \. + Removing xyz; + ${TESTDIR}/cvsroot/my-dir/Attic/xyz,v <-- xyz + new revision: delete; previous revision: 1\.2\.2\.1 + done" + + # try the reverse merge again, to see that the file comes back + + dotest btrunktag29 "${testcvs} update -j .trunk -j stickytag" \ + "${PROG} [a-z]*: Updating \. + [UP] xyz" + + # try to commit it directly to the trunk instead of to branch + dotest btrunktag30 "${testcvs} commit -r .trunk -m totrunk xyz" \ + "Checking in xyz; + ${TESTDIR}/cvsroot/my-dir/xyz,v <-- xyz + new revision: 1\.5; previous revision: 1\.4 + done" + + # at this point, the file still has the sticky branch tag and + # it's status is "up-to-date..." not sure if that's right... + # It might be a bug. My gut feeling is it should be + # "newly added?"? I strongly suspect exactly the same behavior + # would occur with two branches instead of ".trunk" and a + # branch, but I haven't tried that case. + + # let's see what happens with another commit. + + dotest btrunktag31 "${testcvs} commit -m tobranch" \ + "${PROG} [a-z]*: Examining ." + + # Hmm, the file didn't get added... That doesn't seem + # consistent. I think that's a bug. Had I done + # this commit before I'd done the "commit -r .trunk", + # this file would have gone onto the branch. + # The "commit -r different-branch" probably should either leave + # the file's status alone, or else change the sticky tag to + # the new branch, I think. + # + + # try to remove the ".trunk" tag. The message could be + # better, but it's not wrong in any way that's harmful. + + dotest_fail btrunktag32 "${testcvs} tag -d .trunk xyz" \ + "${PROG} .*: tag "\`"\.trunk"\'" must start with a letter" + + # note, this, above, is *not* what happens if you try to remove + # HEAD. In that case, cvs actually tries to + # remove a tag called "HEAD", but can't find it, naturally. + # Perhaps that's a bug, that it doesn't catch that you're + # trying to remove the special HEAD tag. BASE is almost + # certainly in the same boat. + # + # I'm going to go ahead and leave the following test in here + # to remind someone to look into this later, though + # it probably belongs someplace else. + + dotest_fail btrunktag33 "${testcvs} tag -d HEAD xyz" \ + "${PROG} [a-z]*: failed to remove tag HEAD from ${TESTDIR}/cvsroot/my-dir/xyz,v" + + # test the case of ".trunk" with revision + # numbers such as 2.1 + + # update to the trunk, odd behavior on the client/server front + + if [ "$remote" != "yes" ] + then + dotest btrunktag34 "${testcvs} update -r .trunk" \ + "${PROG} [a-z]*: Updating \. + [UP] xyz" + else + dotest btrunktag34 "${testcvs} update -r .trunk" \ + "${PROG} [a-z]*: Updating \. + [UP] xyz + ${PROG} [a-z]*: invalid change text in \./xyz + ${PROG} [a-z]*: refetching unpatchable files + U xyz" + # Now, what the heck is *that* about? + fi + + echo "revision 2.1" >> xyz + dotest btrunktag35 "${testcvs} commit -r2.1 -m twopointone" \ + "${PROG} [a-z]*: Examining \. + Checking in xyz; + ${TESTDIR}/cvsroot/my-dir/xyz,v <-- xyz + new revision: 2\.1; previous revision: 1\.5 + done" + + dotest btrunktag36 "${testcvs} rdiff -r stickytag \ + -r .trunk my-dir" \ + "${PROG} [a-z]*: Diffing my-dir + Index: my-dir/xyz + diff -c my-dir/xyz:1\.2 my-dir/xyz:2\.1 + \*\*\* my-dir/xyz:1\.2 [A-Za-z ]*[0-9]* [0-9:]* [0-9]* + --- my-dir/xyz [A-Za-z ]*[0-9]* [0-9:]* [0-9]* + \*\*\*\*\*\*\*\*\*\*\*\*\*\*\* + \*\*\* 1,2 \*\*\*\* + --- 1,3 ---- + xyz + xyz + ${PLUS} revision 2\.1" + + # try a few things with "cvs admin" and ".trunk" + + dotest_fail btrunktag40 "${testcvs} admin -o ::.trunk xyz" \ + "RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + deleting revision 1\.5 + deleting revision 1\.4 + deleting revision 1\.3 + ${PROG} [a-z]*: ${TESTDIR}/cvsroot/my-dir/xyz,v: can't remove branch point 1\.2 + ${PROG} [a-z]*: cannot modify RCS file for .xyz'" + + dotest btrunktag41 "${testcvs} admin -o 1.2::.trunk xyz" \ + "RCS file: /tmp/cvs-sanity/cvsroot/my-dir/xyz,v + deleting revision 1\.5 + deleting revision 1\.4 + deleting revision 1\.3 + done" + dotest_fail btrunktag42 "${testcvs} admin -o 1.2:.trunk xyz" \ + "RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + deleting revision 2\.1 + ${PROG} [a-z]*: ${TESTDIR}/cvsroot/my-dir/xyz,v: can't remove branch point 1\.2 + ${PROG} [a-z]*: cannot modify RCS file for .xyz'" + + dotest btrunktag43 "${testcvs} admin -sStab:2.1 xyz" \ + "RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + done" + + dotest btrunktag44 "${testcvs} admin -m.trunk:this_is_a_test xyz" \ + "RCS file: /tmp/cvs-sanity/cvsroot/my-dir/xyz,v + done" + + # NOTE, the following may be one of the broken corner + # cases around "cvs commit -r" updating revision numbers + # manually. It could be argued that revisions 1.1 and 1.2 + # should be printed by this "cvs log", Right now, + # "cvs log -r.trunk" is the same as "cvs log -b" + dotest btrunktag45 "${testcvs} log -N -r.trunk xyz" \ + " + RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + Working file: xyz + head: 2\.1 + branch: + locks: strict + access list: + keyword substitution: kv + total revisions: 5; selected revisions: 1 + description: + ---------------------------- + revision 2\.1 + date: [0-9/]* [0-9:]*; author: ${username}; state: Stab; lines: ${PLUS}1 -0 + this_is_a_test + =============================================================================" + + dotest btrunktag46 "${testcvs} admin -l.trunk xyz" \ + "RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + 2.1 locked + done" + dotest btrunktag46 "${testcvs} admin -u.trunk xyz" \ + "RCS file: ${TESTDIR}/cvsroot/my-dir/xyz,v + 2.1 unlocked + done" + + ;; *) Index: src/status.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/status.c,v retrieving revision 1.45 diff -c -r1.45 status.c *** status.c 1999/06/01 21:36:33 1.45 --- status.c 2000/06/27 05:32:08 *************** *** 256,261 **** --- 256,264 ---- if (RCS_nodeisbranch (finfo->rcs, edata->tag)) branch = RCS_whatbranch(finfo->rcs, edata->tag); + if ( strcmp(edata->tag, TAG_TRUNK) == 0) + branch = xstrdup(TAG_TRUNK); + cvs_output (" Sticky Tag:\t\t", 0); cvs_output (edata->tag, 0); cvs_output (" (", 0); Index: src/tag.c =================================================================== RCS file: /home2/cvsroot/ccvs/src/tag.c,v retrieving revision 1.86 diff -c -r1.86 tag.c *** tag.c 2000/06/14 19:32:51 1.86 --- tag.c 2000/06/27 05:32:12 *************** *** 778,784 **** /* Special tags are always valid. */ if (strcmp (name, TAG_BASE) == 0 ! || strcmp (name, TAG_HEAD) == 0) return; /* FIXME: This routine doesn't seem to do any locking whatsoever --- 778,785 ---- /* Special tags are always valid. */ if (strcmp (name, TAG_BASE) == 0 ! || strcmp (name, TAG_HEAD) == 0 ! || strcmp (name, TAG_TRUNK) == 0) return; /* FIXME: This routine doesn't seem to do any locking whatsoever