[See unoff-meta.txt for an older version of this patch and commentary (which probably still applies). -kingdon] Date: Wed, 20 Jan 1999 03:09:36 -0500 From: Ian Lance Taylor To: bug-cvs@gnu.org Subject: Re: TODO item 62 proposal . . . A version of the Cygnus patch concerning separating out some metadata is at http://www.cyclic.com/cvs/dev-metadata.html ("simple form of separated metadata"). Here is a version of the patch against CVS 1.10. Ian Index: src/admin.c =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/admin.c,v retrieving revision 1.1.1.16 retrieving revision 1.33 diff -u -p -r1.1.1.16 -r1.33 --- admin.c 1998/06/22 01:50:47 1.1.1.16 +++ admin.c 1998/06/22 03:17:58 1.33 @@ -10,6 +10,7 @@ */ #include "cvs.h" +#include "fileattr.h" #ifdef CVS_ADMIN_GROUP #include #endif @@ -503,7 +504,11 @@ admin_fileproc (callerdat, finfo) } } if (status == 0) + { RCS_setbranch (rcs, branch); + fileattr_set (finfo->file, "_branch", + branch != NULL && *branch != '\0' ? branch : NULL); + } if (branch != NULL && branch != &admin_data->branch[2]) free (branch); } @@ -581,6 +586,8 @@ admin_fileproc (callerdat, finfo) free (rev1); if (rev2) free (rev2); + + fileattr_set (finfo->file, "_head", rcs->head); } } if (admin_data->desc != NULL) @@ -609,6 +616,8 @@ admin_fileproc (callerdat, finfo) if (rcs->expand) free (rcs->expand); rcs->expand = xstrdup (kflag); + fileattr_set (finfo->file, "_expand", + strcmp (kflag, "kv") == 0 ? "" : kflag); } } Index: src/checkin.c =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/checkin.c,v retrieving revision 1.1.1.17 retrieving revision 1.33 diff -u -p -r1.1.1.17 -r1.33 --- checkin.c 1998/02/23 04:36:10 1.1.1.17 +++ checkin.c 1998/02/23 04:50:59 1.33 @@ -118,6 +118,7 @@ Checkin (type, finfo, rcs, rev, tag, opt vers->options, vers->tag, vers->date, (char *) 0); history_write (type, NULL, vers->vn_rcs, finfo->file, finfo->repository); + fileattr_set (finfo->file, "_head", vers->srcfile->head); if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) Index: src/commit.c =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/commit.c,v retrieving revision 1.1.1.29 retrieving revision 1.91 diff -u -p -r1.1.1.29 -r1.91 --- commit.c 1998/08/14 20:28:09 1.1.1.29 +++ commit.c 1998/08/14 21:03:18 1.91 @@ -1273,6 +1280,7 @@ commit_fileproc (callerdat, finfo) { unlockrcs (finfo->rcs); fixbranch (finfo->rcs, sbranch); + fileattr_set (finfo->file, "_branch", sbranch); } (void) time (&last_register_time); @@ -1316,6 +1324,7 @@ commit_fileproc (callerdat, finfo) { unlockrcs (finfo->rcs); fixbranch (finfo->rcs, sbranch); + fileattr_set (finfo->file, "_branch", sbranch); } } else if (ci->status == T_REMOVED) @@ -1403,8 +1412,11 @@ commit_filesdoneproc (callerdat, err, re got_message = 0; + /* Update any new _head attributes, in case loginfo wants to run + CVS itself. */ + fileattr_write (); Update_Logfile (repository, saved_message, (FILE *) 0, ulist); /* Build the administrative files if necessary. */ { @@ -1693,6 +1706,7 @@ remove_file (finfo, tag, message) return (1); } RCS_rewrite (finfo->rcs, NULL, NULL); + fileattr_set (finfo->file, "_branch", NULL); } #ifdef SERVER_SUPPORT @@ -1768,6 +1782,9 @@ remove_file (finfo, tag, message) /* The old value of finfo->rcs->path is in old_path, and is freed below. */ finfo->rcs->path = tmp; + + fileattr_set (finfo->file, "_head", finfo->rcs->head); + fileattr_set (finfo->file, "_attic", ""); } /* Print message that file was removed. */ @@ -1964,6 +1981,8 @@ internal error: `%s' didn't move out of free (oldfile); free (rcsfile->path); rcsfile->path = xstrdup (rcs); + + fileattr_set (file, "_attic", NULL); } rev = RCS_getversion (rcsfile, tag, NULL, 1, (int *) NULL); @@ -1991,6 +2010,8 @@ internal error: `%s' didn't move out of char *opt; + fileattr_newfile (file); + desc = NULL; descalloc = 0; desclen = 0; @@ -2079,6 +2100,9 @@ internal error: `%s' didn't move out of goto out; } + /* We don't need to set any file attributes here, because we + will wind up calling Checkin, which will handle them. */ + /* put the new file back where it was */ rename_file (fname, file); free (fname); @@ -2169,7 +2193,8 @@ internal error: `%s' didn't move out of } } - fileattr_newfile (file); + if (tag && newfile) + fileattr_set (file, "_attic", ""); /* I don't think fix_rcs_modes is needed any more. In the add_rcs_file case, the algorithms used by add_rcs_file and @@ -2224,6 +2249,8 @@ lock_RCS (user, rcs, rev, repository) free (branch); return (1); } + + fileattr_set (user, "_branch", NULL); } err = RCS_lock(rcs, NULL, 1); } @@ -2261,7 +2288,10 @@ lock_RCS (user, rcs, rev, repository) /* try to restore the branch if we can on error */ if (branch != NULL) + { fixbranch (rcs, branch); + fileattr_set (user, "_branch", branch); + } if (branch) free (branch); Index: src/fileattr.c =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/fileattr.c,v retrieving revision 1.1.1.11 retrieving revision 1.8 diff -u -p -r1.1.1.11 -r1.8 --- fileattr.c 1998/06/22 01:51:00 1.1.1.11 +++ fileattr.c 1998/06/22 03:18:08 1.8 @@ -483,6 +483,15 @@ fileattr_newfile (filename) /* No attributes existed previously. */ attrlist = getlist (); } + else + { + node = findnode (attrlist, filename); + if (node != NULL) + { + /* The file already had attributes. Don't change them. */ + return; + } + } node = getnode (); node->type = FILEATTR; Index: src/fileattr.h =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/fileattr.h,v retrieving revision 1.1.1.6 retrieving revision 1.7 diff -u -p -r1.1.1.6 -r1.7 --- fileattr.h 1997/10/10 16:07:35 1.1.1.6 +++ fileattr.h 1998/02/07 22:00:44 1.7 @@ -53,7 +53,20 @@ EDITOR > VAL { , EDITOR > VAL } where EDITOR is a username, and VAL is TIME+HOSTNAME+PATHNAME, where TIME is when the "cvs edit" command happened, - and HOSTNAME and PATHNAME are for the working directory. */ + and HOSTNAME and PATHNAME are for the working directory. + + _head: Head revision of file. This is a copy of the value of the head + keyword in the RCS file. + + _branch: Default branch of file. This is a copy of the value of the + branch keyword in the RCS file. + + _expand: RCS substition options for file. This is a copy of the + value of the expand keyword in the RCS file. If there is no expand + keyword, then this attribute is still present, with an empty value. + + _attic: If the attribute is present, the RCS file is in the Attic. + The value is unimportant. */ #define CVSREP_FILEATTR "CVS/fileattr" Index: src/import.c =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/import.c,v retrieving revision 1.1.1.28 retrieving revision 1.57 diff -u -p -r1.1.1.28 -r1.57 --- import.c 1998/06/22 01:51:08 1.1.1.28 +++ import.c 1998/06/22 03:18:14 1.57 @@ -17,6 +17,7 @@ */ #include "cvs.h" +#include "fileattr.h" #include "savecwd.h" #include @@ -355,6 +356,8 @@ import_descend (message, vtag, targc, ta int err = 0; List *dirlist = NULL; + fileattr_startdir (repository); + /* first, load up any per-directory ignore lists */ ign_add_file (CVSDOTIGNORE, 1); wrap_add_file (CVSDOTWRAPPER, 1); @@ -428,6 +431,9 @@ import_descend (message, vtag, targc, ta (void) closedir (dirp); } + fileattr_write (); + fileattr_free (); + if (dirlist != NULL) { Node *head, *p; @@ -617,6 +623,8 @@ update_rcs_file (message, vfile, vtag, t letter = 'U'; add_log (letter, vfile); + fileattr_set (vfile, "_head", vers->srcfile->head); + freevers_ts (&vers); return (0); } @@ -1327,6 +1335,11 @@ add_rcs_file (message, rcs, user, add_vh if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); + + fileattr_set (user, "_head", vhead); + fileattr_set (user, "_branch", vbranch); + fileattr_set (user, "_expand", local_opt == NULL ? "" : local_opt); + if (free_opt != NULL) free (free_opt); return (err); Index: src/mkmodules.c =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/mkmodules.c,v retrieving revision 1.1.1.22 retrieving revision 1.39 diff -u -p -r1.1.1.22 -r1.39 --- mkmodules.c 1998/08/14 20:28:20 1.1.1.22 +++ mkmodules.c 1998/08/14 21:03:19 1.39 @@ -778,6 +778,8 @@ init (argc, argv) if ( CVS_CHDIR (adm) < 0) error (1, errno, "cannot change to directory %s", adm); + fileattr_startdir ("."); + /* 80 is long enough for all the administrative file names, plus "/" and so on. */ info = xmalloc (strlen (adm) + 80); Index: src/rcs.c =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/rcs.c,v retrieving revision 1.1.1.35 retrieving revision 1.82 diff -u -p -r1.1.1.35 -r1.82 --- rcs.c 1998/08/14 20:28:24 1.1.1.35 +++ rcs.c 1998/12/16 17:12:26 1.82 @@ -11,6 +11,7 @@ #include #include "cvs.h" #include "edit.h" +#include "fileattr.h" #include "hardlink.h" int preserve_perms = 0; @@ -274,6 +275,73 @@ RCS_parse (file, repos) } /* + * Parse an RCS file if we know that we can fetch file attributes. This + * skips reading the file if the attributes are available. + */ + +RCSNode * +RCS_parse_with_attrs (file, repos) + const char *file; + const char *repos; +{ + char *expand; + RCSNode *rcs; + + expand = fileattr_get0 (file, "_expand"); + if (expand != NULL) + { + char *rcsfile; + + /* We have head and branch attributes for this file, so we + don't need to actually open it. */ + rcs = (RCSNode *) xmalloc (sizeof (RCSNode)); + memset ((char *) rcs, 0, sizeof (RCSNode)); + rcs->refcount = 1; + + rcsfile = xmalloc (strlen (repos) + strlen (file) + + sizeof (RCSEXT) + sizeof (CVSATTIC) + 10); + if (fileattr_get (file, "_attic") == NULL) + (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT); + else + { + (void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC, file, + RCSEXT); + rcs->flags |= INATTIC; + } + rcs->path = rcsfile; + + rcs->head = fileattr_get0 (file, "_head"); + rcs->branch = fileattr_get0 (file, "_branch"); + if (*expand != '\0') + rcs->expand = expand; + else + free (expand); + + rcs->flags |= PARTIAL | VALID; + + return rcs; + } + + /* For backward compatibility for repositories which don't have + head and branch attributes stored yet, just call RCS_parse, and + then store the attributes. */ + rcs = RCS_parse (file, repos); + + if (rcs != NULL) + { + fileattr_set (file, "_head", rcs->head); + if (rcs->branch != NULL) + fileattr_set (file, "_branch", rcs->branch); + if (rcs->flags & INATTIC) + fileattr_set (file, "_attic", ""); + fileattr_set (file, "_expand", + rcs->expand != NULL ? rcs->expand : ""); + } + + return rcs; +} + +/* * Parse a specific rcsfile. */ RCSNode * @@ -443,6 +511,37 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp) gotkey = 0; + if (STREQ (RCSHEAD, key) + && (value == NULL + ? rdata->head != NULL + : (rdata->head == NULL + || ! STREQ (value, rdata->head)))) + { + error (1, 0, "head attribute does not match file for `%s'", + rcsfile); + } + + if (STREQ (RCSBRANCH, key) + && (value == NULL + ? rdata->branch != NULL + : (rdata->branch == NULL + || strncmp (value, rdata->branch, + strlen (rdata->branch)) != 0))) + { + error (1, 0, "branch attribute does not match file for `%s'", + rcsfile); + } + + if (STREQ (RCSEXPAND, key) + && (value == NULL + ? rdata->expand != NULL + : (rdata->expand == NULL + || ! STREQ (value, rdata->expand)))) + { + error (1, 0, "expand attribute does not match file for `%s'", + rcsfile); + } + /* Skip head, branch and expand tags; we already have them. */ if (STREQ (key, RCSHEAD) || STREQ (key, RCSBRANCH) Index: src/rcs.h =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/rcs.h,v retrieving revision 1.1.1.16 retrieving revision 1.32 diff -u -p -r1.1.1.16 -r1.32 --- rcs.h 1998/06/22 01:51:20 1.1.1.16 +++ rcs.h 1998/06/22 03:18:21 1.32 @@ -181,6 +181,7 @@ struct rcsbuffer; * exported interfaces */ RCSNode *RCS_parse PROTO((const char *file, const char *repos)); +RCSNode *RCS_parse_with_attrs PROTO((const char *file, const char *repos)); RCSNode *RCS_parsercsfile PROTO((char *rcsfile)); void RCS_fully_parse PROTO((RCSNode *)); void RCS_reparsercsfile PROTO((RCSNode *, FILE **, struct rcsbuffer *)); Index: src/recurse.c =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/recurse.c,v retrieving revision 1.1.1.19 retrieving revision 1.45 diff -u -p -r1.1.1.19 -r1.45 --- recurse.c 1998/08/14 20:28:27 1.1.1.19 +++ recurse.c 1998/08/14 21:03:19 1.45 @@ -589,7 +589,7 @@ do_file_proc (p, closure) strcat (finfo->fullname, finfo->file); if (frfile->frame->dosrcs && repository) - finfo->rcs = RCS_parse (finfo->file, repository); + finfo->rcs = RCS_parse_with_attrs (finfo->file, repository); else finfo->rcs = (RCSNode *) NULL; ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo); Index: src/sanity.sh =================================================================== RCS file: /cvs/cvsfiles/devo/cvssrc/src/sanity.sh,v retrieving revision 1.1.1.39 retrieving revision 1.87 diff -u -p -r1.1.1.39 -r1.87 --- sanity.sh 1998/08/14 20:27:50 1.1.1.39 +++ sanity.sh 1998/08/14 21:03:20 1.87 @@ -8966,7 +8966,9 @@ U first-dir/abc' dotest devcom-b0 "${testcvs} watch off" '' dotest devcom-b1 "${testcvs} watch remove" '' # Test that CVS 1.6 and earlier can handle the repository. - dotest_fail devcom-b2 "test -d ${CVSROOT_DIRNAME}/first-dir/CVS" + # This test no longer works, because of the new _head, .etc, + # attributes. + # dotest_fail devcom-b2 "test -d ${CVSROOT_DIRNAME}/first-dir/CVS" # Now test watching just some, not all, files. dotest devcom-some0 "${testcvs} watch on abc" '' @@ -9048,10 +9050,10 @@ U first-dir/w3' dotest devcom2-16 "${testcvs} editors w4" '' # Make sure there are no droppings lying around dotest devcom2-17 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ -"Fw1 _watched= -Fw2 _watched= -Fw3 _watched= -Fnw1 _watched= +"Fw1 _watched=;_expand=;_head=1.1 +Fw2 _watched=;_expand=;_head=1.1 +Fnw1 _expand=;_head=1.1;_watched= +Fw3 _expand=;_head=1.1;_watched= D _watched=" cd .. @@ -9079,8 +9081,8 @@ D _watched=" # OK, since we are about to delve into CVS's internals, make # sure that we seem to be correct about how they work. dotest devcom3-5 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ -"Fw1 _watched= -Fw2 _watched=" +"Fw1 _watched=;_expand=;_head=1.1 +Fw2 _watched=;_expand=;_head=1.1" # Now write a few more lines, just as if we were a newer version # of CVS implementing some new feature. cat <>${CVSROOT_DIRNAME}/first-dir/CVS/fileattr @@ -9093,7 +9095,8 @@ EOF # Note that writing these lines in another order would be OK # too. dotest devcom3-7 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ -"Fw2 _watched= +"Fw1 _expand=;_head=1.1 +Fw2 _watched=;_expand=;_head=1.1 G@#..!@#=& Enew line here" @@ -9107,8 +9110,8 @@ Enew line here" echo 'Fw2 duplicate=' >>${CVSROOT_DIRNAME}/first-dir/CVS/fileattr dotest devcom3-8 "${testcvs} watch on w1" '' dotest devcom3-9 "cat ${CVSROOT_DIRNAME}/first-dir/CVS/fileattr" \ -"Fw2 _watched= -Fw1 _watched= +"Fw1 _expand=;_head=1.1;_watched= +Fw2 _watched=;_expand=;_head=1.1 Enew line here G@#..!@#=&"