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-09-29 Mark D. Baushke * 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-09-27 22:08:47.429504000 +0000 @@ -171,6 +171,7 @@ char const Kauthor[] = "author", Kbranch[] = "branch", Kcomment[] = "comment", + Kcommitid[] = "commitid", Kdate[] = "date", Kdesc[] = "desc", Kexpand[] = "expand", @@ -433,6 +434,13 @@ getdelta() Delta->lockedby = 0; Delta->log.string = 0; Delta->selector = true; + + if (getkeyopt(Kcommitid)) { + Delta->commitid = NextString; + nextlex(); + getsemi(Kcommitid); + } + 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);