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 <mdb@gnu.org>
* 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);