3 Thanks to Paul Eggert who suggested using better random numbers as
4 well as using the base62 format for compactness and provided the
5 sample divide_by and convert functions used here.
7 2005-09-29 Mark D. Baushke <mdb@gnu.org>
9 * man/rcsfile.5in: Document new commitid delta phrase.
10 * man/rcsfile.5: Regenerated.
12 * src/ci.c (RANDOM_BYTES, COMMITID_RAW_SIZE): New constants.
13 (mainProg): Add commitid to delta records. Use
14 random data and represent in base62 or fall back to using the
15 same basic format construction as is used by CVS and CVSNT.
16 (divide_by): New function used by convert.
17 (convert): New fucntion to convert to base62.
18 * rcsbase.h (commitidsize): Room for base62 encoded block or
19 32bit pid plus a 32bit time rendered as hex plus one
20 NUL byte round up to 64.
21 (struct hshentry): Add new commitid field.
22 * src/rcsgen.c (putdelta): Preserve old commitid entries.
23 * src/rcssyn.c (Kcommitid): New global constant keyword.
24 (getdelta): Add optional parsing for it.
25 * src/rlog.c (putadelta): Print it out.
28 --- man/rcsfile.5~ 1995-06-16 06:58:26.000000000 +0000
29 +++ man/rcsfile.5 2005-09-27 20:53:01.023504000 +0000
33 .\" Set p to 1 if your formatter can handle pic output.
36 @@ -69,6 +69,7 @@ nonterminal symbols are in
37 \f3state\fP {\f2id\fP}\f3;\fP
38 \f3branches\fP {\f2num\fP}*\f3;\fP
39 \f3next\fP {\f2num\fP}\f3;\fP
40 + { \f3commitid\fP \f2id\fP\f3;\fP }
43 \f2desc\fP ::= \f3desc\fP \f2string\fP
44 @@ -128,6 +129,18 @@ and all the digits of years thereafter.
45 Dates use the Gregorian calendar; times use UTC.
51 +token. This token is intended to be unique across
52 +multiple files and is used to help group files as
53 +being a part of the same logical commit.
54 +This token must uniquely identify the commit
55 +operation that was applied to a set of RCS files.
56 +In particular, it must be unique among all the
57 +commitids in this file.
61 productions in the grammar are reserved for future extensions
62 to the format of \*r files.
63 @@ -230,7 +243,7 @@ The following diagram shows an example o
70 .\" -2.0625 -4.25 1.75 0
71 .\" 0.000i 4.250i 3.812i 0.000i
72 @@ -239,7 +252,7 @@ The following diagram shows an example o
78 \h'2.062i-(\w'Head'u/2u)'\v'0.125i-(0v/2u)+0v+0.22m'Head
80 \h'2.062i'\v'0.250i'\D'l0.000i 0.500i'
81 @@ -256,7 +269,7 @@ The following diagram shows an example o
83 \h'1.688i'\v'0.750i'\D'l0.000i 0.500i'
87 \h'2.062i-(\w'2.1'u/2u)'\v'1.000i-(0v/2u)+0v+0.22m'2.1
89 \h'2.062i'\v'1.250i'\D'l0.000i 0.500i'
90 @@ -265,7 +278,7 @@ The following diagram shows an example o
92 \h'2.062i'\v'1.750i'\D'l-0.025i -0.100i'
96 \h'2.062i-(\w'1.3'u/2u)'\v'2.000i-(1v/2u)+0v+0.22m'1.3
98 \h'2.062i'\v'2.250i'\D'l-0.375i -0.500i'
99 @@ -280,7 +293,7 @@ The following diagram shows an example o
101 \h'1.375i'\v'1.500i'\D'l0.025i 0.100i'
105 \h'1.375i-(\w'1.3.1.1'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.3.1.1
107 \h'1.375i'\v'1.000i'\D'l-0.375i 0.500i'
108 @@ -295,7 +308,7 @@ The following diagram shows an example o
110 \h'2.062i'\v'2.750i'\D'l-0.025i -0.100i'
114 \h'2.062i-(\w'1.2'u/2u)'\v'3.000i-(1v/2u)+0v+0.22m'1.2
116 \h'2.062i'\v'3.250i'\D'l-0.375i -0.500i'
117 @@ -310,7 +323,7 @@ The following diagram shows an example o
119 \h'0.375i'\v'2.500i'\D'l0.025i 0.100i'
123 \h'0.375i-(\w'1.2.1.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.1.1
125 \h'0.375i'\v'2.000i'\D'l-0.375i 0.500i'
126 @@ -325,7 +338,7 @@ The following diagram shows an example o
128 \h'0.375i'\v'1.500i'\D'l0.025i 0.100i'
132 \h'0.375i-(\w'1.2.1.3'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.1.3
134 \h'0.375i'\v'1.000i'\D'l-0.375i 0.500i'
135 @@ -340,7 +353,7 @@ The following diagram shows an example o
137 \h'2.750i'\v'2.500i'\D'l0.025i 0.100i'
141 \h'2.750i-(\w'1.2.2.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.2.1
143 \h'2.750i'\v'2.000i'\D'l-0.375i 0.500i'
144 @@ -355,7 +368,7 @@ The following diagram shows an example o
146 \h'3.438i'\v'1.250i'\D'l0.025i 0.100i'
150 \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
152 \h'3.438i'\v'0.750i'\D'l-0.375i 0.500i'
153 @@ -370,7 +383,7 @@ The following diagram shows an example o
155 \h'2.750i'\v'1.500i'\D'l0.025i 0.100i'
159 \h'2.750i-(\w'1.2.2.2'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.2.2
161 \h'2.750i'\v'1.000i'\D'l-0.375i 0.500i'
162 @@ -385,7 +398,7 @@ The following diagram shows an example o
164 \h'2.062i'\v'3.750i'\D'l-0.025i -0.100i'
168 \h'2.062i-(\w'1.1'u/2u)'\v'4.000i-(1v/2u)+0v+0.22m'1.1
170 \h'2.062i'\v'4.250i'\D'l-0.375i -0.500i'
171 @@ -398,9 +411,9 @@ The following diagram shows an example o
183 Index:man/rcsfile.5in
184 --- man/rcsfile.5in~ 1995-06-05 08:28:35.000000000 +0000
185 +++ man/rcsfile.5in 2005-09-27 20:52:46.424504000 +0000
186 @@ -68,6 +68,7 @@ nonterminal symbols are in
187 \f3state\fP {\f2id\fP}\f3;\fP
188 \f3branches\fP {\f2num\fP}*\f3;\fP
189 \f3next\fP {\f2num\fP}\f3;\fP
190 + { \f3commitid\fP \f2id\fP\f3;\fP }
193 \f2desc\fP ::= \f3desc\fP \f2string\fP
194 @@ -127,6 +128,18 @@ and all the digits of years thereafter.
195 Dates use the Gregorian calendar; times use UTC.
201 +token. This token is intended to be unique across
202 +multiple files and is used to help group files as
203 +being a part of the same logical commit.
204 +This token must uniquely identify the commit
205 +operation that was applied to a set of RCS files.
206 +In particular, it must be unique among all the
207 +commitids in this file.
211 productions in the grammar are reserved for future extensions
212 to the format of \*r files.
214 --- src/rcsbase.h~ 1995-06-16 06:19:24.000000000 +0000
215 +++ src/rcsbase.h 2005-09-28 21:47:51.490505000 +0000
216 @@ -222,6 +222,11 @@ Report problems and direct all questions
217 /* 1 sets the default locking to strict; */
218 /* used in production environments. */
220 +/* base64_encode(128 random bits) needs 24 bytes + 1 for NUL */
221 +/* time_t may be 64bits on some machines needs 16 bytes + 1 as hex */
222 +#define commitidsize 64 /* time+1+base64(128bits)+1 | pid+time+rand+1 */
223 +#define urandom_dev "/dev/urandom"
225 #define yearlength 16 /* (good through AD 9,999,999,999,999,999) */
226 #define datesize (yearlength+16) /* size of output of time2date */
227 #define RCSTMPPREFIX '_' /* prefix for temp files in working dir */
228 @@ -358,6 +363,7 @@ struct hshentry {
229 char const * lockedby; /* who locks the revision */
230 char const * state; /* state of revision (Exp by default) */
231 char const * name; /* name (if any) by which retrieved */
232 + char const * commitid; /* text string to associate commits */
233 struct cbuf log; /* log message requested at checkin */
234 struct branchhead * branches; /* list of first revisions on branches*/
235 struct cbuf ig; /* ignored phrases in admin part */
236 @@ -662,6 +668,7 @@ extern int TotalDeltas;
237 extern char const *const expand_names[];
239 Kaccess[], Kauthor[], Kbranch[], Kcomment[],
241 Kdate[], Kdesc[], Kexpand[], Khead[], Klocks[], Klog[],
242 Knext[], Kstate[], Kstrict[], Ksymbols[], Ktext[];
243 void unexpected_EOF P((void)) exiting;
245 --- src/ci.c~ 1995-06-16 06:19:24.000000000 +0000
246 +++ src/ci.c 2005-09-29 21:57:57.814504000 +0000
247 @@ -262,6 +262,10 @@ static void cleanup P((void));
248 static void incnum P((char const*,struct buf*));
249 static void addassoclst P((int,char const*));
251 +enum {RANDOM_BYTES = 8};
252 +enum {COMMITID_RAW_SIZE = (sizeof(time_t) + RANDOM_BYTES)};
253 +static void convert P((char const input[COMMITID_RAW_SIZE], char *output));
256 static RILE *workptr; /* working file pointer */
257 static struct buf newdelnum; /* new revision number */
258 @@ -285,6 +289,7 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.30 1
259 char olddate[datesize];
260 char newdatebuf[datesize + zonelenmax];
261 char targetdatebuf[datesize + zonelenmax];
262 + char commitid[commitidsize];
263 char *a, **newargv, *textfile;
264 char const *author, *krev, *rev, *state;
265 char const *diffname, *expname;
266 @@ -309,6 +314,45 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.30 1
267 suffixes = X_DEFAULT;
268 nextassoc = &assoclst;
271 + char buf[COMMITID_RAW_SIZE] = { 0, };
273 + time_t rightnow = time (NULL);
274 + char *startrand = buf + sizeof (time_t);
275 + unsigned char *p = (unsigned char *) startrand;
276 + size_t randbytes = RANDOM_BYTES;
277 + int flags = O_RDONLY;
282 + if (rightnow != (time_t)-1)
283 + while (rightnow > 0) {
284 + *--p = rightnow % (UCHAR_MAX + 1);
285 + rightnow /= UCHAR_MAX + 1;
288 + /* try to use more random data */
289 + randbytes = COMMITID_RAW_SIZE;
292 + fd = open (urandom_dev, flags);
294 + len = read (fd, startrand, randbytes);
298 + /* no random data was available so use pid */
299 + long int pid = (long int)getpid ();
300 + p = (unsigned char *) (startrand + sizeof (pid));
302 + *--p = pid % (UCHAR_MAX + 1);
303 + pid /= UCHAR_MAX + 1;
306 + convert(buf, commitid);
309 argc = getRCSINIT(argc, argv, &newargv);
311 while (a = *++argv, 0<--argc && *a++=='-') {
312 @@ -532,6 +576,8 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.30 1
314 clear_buf(&newdelta.ig);
315 clear_buf(&newdelta.igtext);
317 + newdelta.commitid=commitid;
320 newdelta.author=author; /* set author given by -w */
321 @@ -1317,3 +1363,38 @@ addassoclst(flag, sp)
323 nextassoc = &pt->nextsym;
326 +static char const alphabet[62] =
327 + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
329 +/* Divide BUF by D, returning the remainder. Replace BUF by the
330 + quotient. BUF[0] is the most significant part of BUF.
331 + D must not exceed UINT_MAX >> CHAR_BIT. */
333 +divide_by (unsigned char buf[COMMITID_RAW_SIZE], unsigned int d)
335 + unsigned int carry = 0;
337 + for (i = 0; i < COMMITID_RAW_SIZE; i++)
339 + unsigned int byte = buf[i];
340 + unsigned int dividend = (carry << CHAR_BIT) + byte;
341 + buf[i] = dividend / d;
342 + carry = dividend % d;
348 +convert (char const input[COMMITID_RAW_SIZE], char *output)
350 + static char const zero[COMMITID_RAW_SIZE] = { 0, };
351 + unsigned char buf[COMMITID_RAW_SIZE];
353 + memcpy (buf, input, COMMITID_RAW_SIZE);
354 + while (memcmp (buf, zero, COMMITID_RAW_SIZE) != 0)
355 + output[o++] = alphabet[divide_by (buf, sizeof alphabet)];
361 --- src/rcsgen.c~ 1995-06-16 06:19:24.000000000 +0000
362 +++ src/rcsgen.c 2005-09-27 22:08:47.421504000 +0000
363 @@ -547,6 +547,9 @@ putdelta(node, fout)
365 aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:"");
366 awrite(node->ig.string, node->ig.size, fout);
368 + if (node->commitid)
369 + aprintf(fout, "%s\t%s;\n", Kcommitid, node->commitid);
374 --- src/rcssyn.c~ 1995-06-16 06:19:24.000000000 +0000
375 +++ src/rcssyn.c 2005-09-27 22:08:47.429504000 +0000
376 @@ -171,6 +171,7 @@ char const
377 Kauthor[] = "author",
378 Kbranch[] = "branch",
379 Kcomment[] = "comment",
380 + Kcommitid[] = "commitid",
383 Kexpand[] = "expand",
384 @@ -433,6 +434,13 @@ getdelta()
386 Delta->log.string = 0;
387 Delta->selector = true;
389 + if (getkeyopt(Kcommitid)) {
390 + Delta->commitid = NextString;
392 + getsemi(Kcommitid);
395 Delta->ig = getphrases(Kdesc);
399 --- src/rlog.c~ 1995-06-16 06:19:24.000000000 +0000
400 +++ src/rlog.c 2005-09-26 17:23:55.257504000 +0000
401 @@ -591,6 +591,10 @@ putadelta(node,editscript,trunk)
402 aprintf(out, insDelFormat,
403 editscript->insertlns, editscript->deletelns);
405 + if ( node->commitid )
406 + aprintf(out, "%s commitid: %s", (editscript) ? ";" : "",
409 newbranch = node->branches;
411 bufautobegin(&branchnum);