Package: rcs
Version: 5.7
Severity: wishlist
Tags: patch

Recent versions of CVS and CVSNT add a new phrase `commitid ...;' to
attach a unique commitid to the delta section of all files committed at
the same time. This allows users to better be able to identify the set
of files that were changed as a unit.

Production versions of RCS 5.7 do not know what to do with this
information and generate a message to STDERR about it such as the
following:

% rlog CVSROOT/config,v > /dev/null
rlog: CVSROOT/config,v: warning: Unknown phrases like `commitid ...;' are 
present.
%

The standard workaround is for users to use the -q switch to avoid
getting these messages on STDERR.

% rlog -q CVSROOT/config,v > /dev/null
%

However, some users may not desire to use the -q switch and instead
might even want to see the commitid information as a part of the rlog
output.

A patch against rcs-5.7 to teach it how to handle this new phrase and to
use it for its own commits is included in the CVS source repository
contrib directory and is presented here for your consideration.

The URL is

http://cvs.savannah.nongnu.org/viewcvs/*checkout*/ccvs/contrib/rcs-5.7-commitid.patch?rev=HEAD&root=cvs

The attached patch is a snapshot of version 1.9 of this file.

I hope you find the patch to be useful enough to incorporate into a future
revision of rcs.

If you would rather that the RCS ci command not actively add the new
phrase commitid to deltas, then I can resubmit this patch with the
changes to src/ci.c and src/rcsbase.h to remove the changes which
generate a unique commitid for a new checkin.

Please advise if you think there will ever be an RCS 5.8 release which
would pick up this patch. I suspect that there are some other patches
(security-related in nature) which might be submitted if you open the
door to a new release.

        Thank you,
        -- Mark

ChangeLog entry:

Thanks to Paul Eggert who suggested using better random numbers as
well as using the base62 format for compactness and provided the
sample divide_by and convert functions used here.

2005-10-04  Mark D. Baushke  <[EMAIL PROTECTED]>

        * src/rcssyn.c (getdelta): Initialize Delta->commitid.

2005-09-29  Mark D. Baushke  <[EMAIL PROTECTED]>

        * man/rcsfile.5in: Document new commitid delta phrase.
        * man/rcsfile.5: Regenerated.

        * src/ci.c (RANDOM_BYTES, COMMITID_RAW_SIZE): New constants.
        (mainProg): Add commitid to delta records. Use
        random data and represent in base62 or fall back to using the
        same basic format construction as is used by CVS and CVSNT.
        (divide_by): New function used by convert.
        (convert): New fucntion to convert to base62.
        * rcsbase.h (commitidsize): Room for base62 encoded block or
        32bit pid plus a 32bit time rendered as hex plus one
        NUL byte round up to 64.
        (struct hshentry): Add new commitid field.
        * src/rcsgen.c (putdelta): Preserve old commitid entries.
        * src/rcssyn.c (Kcommitid): New global constant keyword.
        (getdelta): Add optional parsing for it.
        * src/rlog.c (putadelta): Print it out.

Index:man/rcsfile.5
--- man/rcsfile.5~      1995-06-16 06:58:26.000000000 +0000
+++ man/rcsfile.5       2005-09-27 20:53:01.023504000 +0000
@@ -1,4 +1,4 @@
-.lf 1 ./rcsfile.5in
+.lf 1 rcsfile.5in
 .\" Set p to 1 if your formatter can handle pic output.
 .if t .nr p 1
 .de Id
@@ -69,6 +69,7 @@ nonterminal symbols are in
                \f3state\fP     {\f2id\fP}\f3;\fP
                \f3branches\fP  {\f2num\fP}*\f3;\fP
                \f3next\fP      {\f2num\fP}\f3;\fP
+               { \f3commitid\fP \f2id\fP\f3;\fP }
                { \f2newphrase\fP }*
 .LP
 \f2desc\fP     ::=     \f3desc\fP      \f2string\fP
@@ -128,6 +129,18 @@ and all the digits of years thereafter.
 Dates use the Gregorian calendar; times use UTC.
 .PP
 The
+.I commitid
+is followed by an
+.I id
+token. This token is intended to be unique across
+multiple files and is used to help group files as
+being a part of the same logical commit.
+This token must uniquely identify the commit
+operation that was applied to a set of RCS files.
+In particular, it must be unique among all the
+commitids in this file.
+.PP
+The
 .I newphrase
 productions in the grammar are reserved for future extensions
 to the format of \*r files.
@@ -230,7 +243,7 @@ The following diagram shows an example o
 .fi
 .\}
 .if \np \{\
-.lf 232
+.lf 245
 .PS 4.250i 3.812i
 .\" -2.0625 -4.25 1.75 0
 .\" 0.000i 4.250i 3.812i 0.000i
@@ -239,7 +252,7 @@ The following diagram shows an example o
 .nr 0x 1
 \h'3.812i'
 .sp -1
-.lf 242
+.lf 255
 \h'2.062i-(\w'Head'u/2u)'\v'0.125i-(0v/2u)+0v+0.22m'Head
 .sp -1
 \h'2.062i'\v'0.250i'\D'l0.000i 0.500i'
@@ -256,7 +269,7 @@ The following diagram shows an example o
 .sp -1
 \h'1.688i'\v'0.750i'\D'l0.000i 0.500i'
 .sp -1
-.lf 244
+.lf 257
 \h'2.062i-(\w'2.1'u/2u)'\v'1.000i-(0v/2u)+0v+0.22m'2.1
 .sp -1
 \h'2.062i'\v'1.250i'\D'l0.000i 0.500i'
@@ -265,7 +278,7 @@ The following diagram shows an example o
 .sp -1
 \h'2.062i'\v'1.750i'\D'l-0.025i -0.100i'
 .sp -1
-.lf 246
+.lf 259
 \h'2.062i-(\w'1.3'u/2u)'\v'2.000i-(1v/2u)+0v+0.22m'1.3
 .sp -1
 \h'2.062i'\v'2.250i'\D'l-0.375i -0.500i'
@@ -280,7 +293,7 @@ The following diagram shows an example o
 .sp -1
 \h'1.375i'\v'1.500i'\D'l0.025i 0.100i'
 .sp -1
-.lf 249
+.lf 262
 \h'1.375i-(\w'1.3.1.1'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.3.1.1
 .sp -1
 \h'1.375i'\v'1.000i'\D'l-0.375i 0.500i'
@@ -295,7 +308,7 @@ The following diagram shows an example o
 .sp -1
 \h'2.062i'\v'2.750i'\D'l-0.025i -0.100i'
 .sp -1
-.lf 252
+.lf 265
 \h'2.062i-(\w'1.2'u/2u)'\v'3.000i-(1v/2u)+0v+0.22m'1.2
 .sp -1
 \h'2.062i'\v'3.250i'\D'l-0.375i -0.500i'
@@ -310,7 +323,7 @@ The following diagram shows an example o
 .sp -1
 \h'0.375i'\v'2.500i'\D'l0.025i 0.100i'
 .sp -1
-.lf 255
+.lf 268
 \h'0.375i-(\w'1.2.1.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.1.1
 .sp -1
 \h'0.375i'\v'2.000i'\D'l-0.375i 0.500i'
@@ -325,7 +338,7 @@ The following diagram shows an example o
 .sp -1
 \h'0.375i'\v'1.500i'\D'l0.025i 0.100i'
 .sp -1
-.lf 257
+.lf 270
 \h'0.375i-(\w'1.2.1.3'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.1.3
 .sp -1
 \h'0.375i'\v'1.000i'\D'l-0.375i 0.500i'
@@ -340,7 +353,7 @@ The following diagram shows an example o
 .sp -1
 \h'2.750i'\v'2.500i'\D'l0.025i 0.100i'
 .sp -1
-.lf 261
+.lf 274
 \h'2.750i-(\w'1.2.2.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.2.1
 .sp -1
 \h'2.750i'\v'2.000i'\D'l-0.375i 0.500i'
@@ -355,7 +368,7 @@ The following diagram shows an example o
 .sp -1
 \h'3.438i'\v'1.250i'\D'l0.025i 0.100i'
 .sp -1
-.lf 264
+.lf 277
 
\h'3.438i-(\w'\s-21.2.2.1.1.1\s0'u/2u)'\v'1.000i-(1v/2u)+1v+0.22m'\s-21.2.2.1.1.1\s0
 .sp -1
 \h'3.438i'\v'0.750i'\D'l-0.375i 0.500i'
@@ -370,7 +383,7 @@ The following diagram shows an example o
 .sp -1
 \h'2.750i'\v'1.500i'\D'l0.025i 0.100i'
 .sp -1
-.lf 267
+.lf 280
 \h'2.750i-(\w'1.2.2.2'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.2.2
 .sp -1
 \h'2.750i'\v'1.000i'\D'l-0.375i 0.500i'
@@ -385,7 +398,7 @@ The following diagram shows an example o
 .sp -1
 \h'2.062i'\v'3.750i'\D'l-0.025i -0.100i'
 .sp -1
-.lf 270
+.lf 283
 \h'2.062i-(\w'1.1'u/2u)'\v'4.000i-(1v/2u)+0v+0.22m'1.1
 .sp -1
 \h'2.062i'\v'4.250i'\D'l-0.375i -0.500i'
@@ -398,9 +411,9 @@ The following diagram shows an example o
 .if \n(00 .fi
 .br
 .nr 0x 0
-.lf 271
+.lf 284
 .PE
-.lf 272
+.lf 285
 .\}
 .PP
 .SH IDENTIFICATION
Index:man/rcsfile.5in
--- man/rcsfile.5in~    1995-06-05 08:28:35.000000000 +0000
+++ man/rcsfile.5in     2005-09-27 20:52:46.424504000 +0000
@@ -68,6 +68,7 @@ nonterminal symbols are in
                \f3state\fP     {\f2id\fP}\f3;\fP
                \f3branches\fP  {\f2num\fP}*\f3;\fP
                \f3next\fP      {\f2num\fP}\f3;\fP
+               { \f3commitid\fP \f2id\fP\f3;\fP }
                { \f2newphrase\fP }*
 .LP
 \f2desc\fP     ::=     \f3desc\fP      \f2string\fP
@@ -127,6 +128,18 @@ and all the digits of years thereafter.
 Dates use the Gregorian calendar; times use UTC.
 .PP
 The
+.I commitid
+is followed by an
+.I id
+token. This token is intended to be unique across
+multiple files and is used to help group files as
+being a part of the same logical commit.
+This token must uniquely identify the commit
+operation that was applied to a set of RCS files.
+In particular, it must be unique among all the
+commitids in this file.
+.PP
+The
 .I newphrase
 productions in the grammar are reserved for future extensions
 to the format of \*r files.
Index:src/rcsbase.h
--- src/rcsbase.h~      1995-06-16 06:19:24.000000000 +0000
+++ src/rcsbase.h       2005-09-28 21:47:51.490505000 +0000
@@ -222,6 +222,11 @@ Report problems and direct all questions
                               /* 1 sets the default locking to strict;      */
                               /* used in production environments.           */
 
+/* base64_encode(128 random bits) needs 24 bytes + 1 for NUL */
+/* time_t may be 64bits on some machines needs 16 bytes + 1 as hex */
+#define commitidsize      64 /* time+1+base64(128bits)+1 | pid+time+rand+1 */
+#define urandom_dev "/dev/urandom"
+
 #define yearlength        16 /* (good through AD 9,999,999,999,999,999)    */
 #define datesize (yearlength+16)       /* size of output of time2date */
 #define RCSTMPPREFIX '_' /* prefix for temp files in working dir  */
@@ -358,6 +363,7 @@ struct hshentry {
        char const        * lockedby; /* who locks the revision             */
        char const        * state;    /* state of revision (Exp by default) */
        char const        * name;     /* name (if any) by which retrieved   */
+       char const        * commitid; /* text string to associate commits   */
        struct cbuf         log;      /* log message requested at checkin   */
         struct branchhead * branches; /* list of first revisions on branches*/
        struct cbuf         ig;       /* ignored phrases in admin part      */
@@ -662,6 +668,7 @@ extern int               TotalDeltas;
 extern char const *const expand_names[];
 extern char const
        Kaccess[], Kauthor[], Kbranch[], Kcomment[],
+       Kcommitid[],
        Kdate[], Kdesc[], Kexpand[], Khead[], Klocks[], Klog[],
        Knext[], Kstate[], Kstrict[], Ksymbols[], Ktext[];
 void unexpected_EOF P((void)) exiting;
Index:src/ci.c
--- src/ci.c~   1995-06-16 06:19:24.000000000 +0000
+++ src/ci.c    2005-09-29 21:57:57.814504000 +0000
@@ -262,6 +262,10 @@ static void cleanup P((void));
 static void incnum P((char const*,struct buf*));
 static void addassoclst P((int,char const*));
 
+enum {RANDOM_BYTES = 8};
+enum {COMMITID_RAW_SIZE = (sizeof(time_t) + RANDOM_BYTES)};
+static void convert P((char const input[COMMITID_RAW_SIZE], char *output));
+
 static FILE *exfile;
 static RILE *workptr;                  /* working file pointer         */
 static struct buf newdelnum;           /* new revision number          */
@@ -285,6 +289,7 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.30 1
        char olddate[datesize];
        char newdatebuf[datesize + zonelenmax];
        char targetdatebuf[datesize + zonelenmax];
+       char commitid[commitidsize];
        char *a, **newargv, *textfile;
        char const *author, *krev, *rev, *state;
        char const *diffname, *expname;
@@ -309,6 +314,45 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.30 1
        suffixes = X_DEFAULT;
        nextassoc = &assoclst;
 
+       {
+               char buf[COMMITID_RAW_SIZE] = { 0, };
+               ssize_t len = 0;
+               time_t rightnow = time (NULL);
+               char *startrand = buf + sizeof (time_t);
+               unsigned char *p = (unsigned char *) startrand;
+               size_t randbytes = RANDOM_BYTES;
+               int flags = O_RDONLY;
+               int fd;
+#ifdef O_NOCTTY
+               flags |= O_NOCTTY;
+#endif
+               if (rightnow != (time_t)-1)
+                       while (rightnow > 0) {
+                               *--p = rightnow % (UCHAR_MAX + 1);
+                               rightnow /= UCHAR_MAX + 1;
+                       }
+               else {
+                       /* try to use more random data */
+                       randbytes = COMMITID_RAW_SIZE;
+                       startrand = buf;
+               }
+               fd = open (urandom_dev, flags);
+               if (fd >= 0) {
+                       len = read (fd, startrand, randbytes);
+                       close (fd);
+               }
+               if (len <= 0) {
+                       /* no random data was available so use pid */
+                       long int pid = (long int)getpid ();
+                       p = (unsigned char *) (startrand + sizeof (pid));
+                       while (pid > 0) {
+                           *--p = pid % (UCHAR_MAX + 1);
+                           pid /= UCHAR_MAX + 1;
+                       }
+               }
+               convert(buf, commitid);
+       }
+
        argc = getRCSINIT(argc, argv, &newargv);
        argv = newargv;
        while (a = *++argv,  0<--argc && *a++=='-') {
@@ -532,6 +576,8 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.30 1
        newdelta.name = 0;
        clear_buf(&newdelta.ig);
        clear_buf(&newdelta.igtext);
+       /* set commitid */
+       newdelta.commitid=commitid;
        /* set author */
        if (author)
                newdelta.author=author;     /* set author given by -w         */
@@ -1317,3 +1363,38 @@ addassoclst(flag, sp)
        *nextassoc = pt;
        nextassoc = &pt->nextsym;
 }
+
+static char const alphabet[62] =
+  "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+/* Divide BUF by D, returning the remainder.  Replace BUF by the
+   quotient.  BUF[0] is the most significant part of BUF.
+   D must not exceed UINT_MAX >> CHAR_BIT.  */
+static unsigned int
+divide_by (unsigned char buf[COMMITID_RAW_SIZE], unsigned int d)
+{
+  unsigned int carry = 0;
+  int i;
+  for (i = 0; i < COMMITID_RAW_SIZE; i++)
+    {
+      unsigned int byte = buf[i];
+      unsigned int dividend = (carry << CHAR_BIT) + byte;
+      buf[i] = dividend / d;
+      carry = dividend % d;
+    }
+  return carry;
+}
+
+static void
+convert (char const input[COMMITID_RAW_SIZE], char *output)
+{
+  static char const zero[COMMITID_RAW_SIZE] = { 0, };
+  unsigned char buf[COMMITID_RAW_SIZE];
+  size_t o = 0;
+  memcpy (buf, input, COMMITID_RAW_SIZE);
+  while (memcmp (buf, zero, COMMITID_RAW_SIZE) != 0)
+    output[o++] = alphabet[divide_by (buf, sizeof alphabet)];
+  if (! o)
+    output[o++] = '0';
+  output[o] = '\0';
+}
Index:src/rcsgen.c
--- src/rcsgen.c~       1995-06-16 06:19:24.000000000 +0000
+++ src/rcsgen.c        2005-09-27 22:08:47.421504000 +0000
@@ -547,6 +547,9 @@ putdelta(node, fout)
 
        aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:"");
        awrite(node->ig.string, node->ig.size, fout);
+
+       if (node->commitid)
+               aprintf(fout, "%s\t%s;\n", Kcommitid, node->commitid);
 }
 
 
Index:src/rcssyn.c
--- src/rcssyn.c~       1995-06-16 06:19:24.000000000 +0000
+++ src/rcssyn.c        2005-10-04 08:05:21.000000000 +0000
@@ -171,6 +171,7 @@ char const
        Kauthor[]   = "author",
        Kbranch[]   = "branch",
        Kcomment[]  = "comment",
+       Kcommitid[] = "commitid",
        Kdate[]     = "date",
        Kdesc[]     = "desc",
        Kexpand[]   = "expand",
@@ -433,6 +434,14 @@ getdelta()
        Delta->lockedby = 0;
        Delta->log.string = 0;
        Delta->selector = true;
+
+       if (getkeyopt(Kcommitid)) {
+               Delta->commitid = NextString;
+               nextlex();
+               getsemi(Kcommitid);
+        } else
+               Delta->commitid = NULL;
+
        Delta->ig = getphrases(Kdesc);
         TotalDeltas++;
         return (true);
Index:src/rlog.c
--- src/rlog.c~ 1995-06-16 06:19:24.000000000 +0000
+++ src/rlog.c  2005-09-26 17:23:55.257504000 +0000
@@ -591,6 +591,10 @@ putadelta(node,editscript,trunk)
              aprintf(out, insDelFormat,
                              editscript->insertlns, editscript->deletelns);
 
+       if ( node->commitid )
+          aprintf(out, "%s commitid: %s", (editscript) ? ";" : "",
+                  node->commitid);
+
         newbranch = node->branches;
         if ( newbranch ) {
           bufautobegin(&branchnum);

Attachment: pgpCALvnqpsAx.pgp
Description: PGP signature

Reply via email to