2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS source distribution.
10 * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
11 * Uses the checked out revision in the current directory.
17 static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo));
18 static int check_filesdoneproc PROTO ((void *callerdat, int err,
19 char *repos, char *update_dir,
21 static int pretag_proc PROTO((char *repository, char *filter));
22 static void masterlist_delproc PROTO((Node *p));
23 static void tag_delproc PROTO((Node *p));
24 static int pretag_list_proc PROTO((Node *p, void *closure));
26 static Dtype tag_dirproc PROTO ((void *callerdat, char *dir,
27 char *repos, char *update_dir,
29 static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo));
30 static int tag_filesdoneproc PROTO ((void *callerdat, int err,
31 char *repos, char *update_dir,
35 static char *date = NULL;
37 static int delete_flag; /* adding a tag by default */
38 static int branch_mode; /* make an automagic "branch" tag */
39 static int local; /* recursive by default */
40 static int force_tag_match = 1; /* force tag to match by default */
41 static int force_tag_move; /* don't force tag to move by default */
42 static int check_uptodate; /* no uptodate-check by default */
60 static const char *const tag_usage[] =
62 "Usage: %s %s [-lRF] [-b] [-d] [-c] [-r tag|-D date] tag [files...]\n",
63 "\t-l\tLocal directory only, not recursive.\n",
64 "\t-R\tProcess directories recursively.\n",
65 "\t-d\tDelete the given tag.\n",
66 "\t-r rev\tExisting revision/tag.\n",
67 "\t-D\tExisting date.\n",
68 "\t-f\tForce a head revision if specified tag not found.\n",
69 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
70 "\t-F\tMove tag if it already exists.\n",
71 "\t-c\tCheck that working files are unmodified.\n",
72 "(Specify the --help global option for a list of other help options)\n",
88 while ((c = getopt (argc, argv, "+FQqlRcdr:D:bf")) != -1)
95 /* The CVS 1.5 client sends these options (in addition to
96 Global_option requests), so we must ignore them. */
100 "-q or -Q must be specified before \"%s\"",
121 date = Make_Date (optarg);
148 error (1, 0, "-r and -D options are mutually exclusive");
149 if (delete_flag && branch_mode)
150 error (0, 0, "warning: -b ignored with -d options");
151 RCS_check_tag (symtag);
153 #ifdef CLIENT_SUPPORT
156 /* We're the client side. Fire up the remote server. */
161 if (!force_tag_match)
175 option_with_arg ("-r", numtag);
177 client_senddate (date);
181 send_file_names (argc, argv, SEND_EXPAND_WILD);
183 /* SEND_NO_CONTENTS has a mildly bizarre interaction with
184 check_uptodate; if the timestamp is modified but the file
185 is unmodified, the check will fail, only to have "cvs diff"
186 show no differences (and one must do "update" or something to
187 reset the client's notion of the timestamp). */
189 send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
190 send_to_server ("tag\012", 0);
191 return get_responses_and_close ();
196 tag_check_valid (numtag, argc, argv, local, 0, "");
198 /* check to make sure they are authorized to tag all the
199 specified files in the repository */
202 err = start_recursion (check_fileproc, check_filesdoneproc,
203 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
204 argc, argv, local, W_LOCAL, 0, 1,
209 error (1, 0, "correct the above errors first!");
212 /* start the recursion processor */
213 err = start_recursion (tag_fileproc, tag_filesdoneproc, tag_dirproc,
214 (DIRLEAVEPROC) NULL, NULL, argc, argv, local,
215 W_LOCAL, 0, 0, (char *) NULL, 1);
220 /* check file that is to be tagged */
221 /* All we do here is add it to our list */
224 check_fileproc (callerdat, finfo)
226 struct file_info *finfo;
234 Ctype status = Classify_File (finfo, (char *) NULL, (char *) NULL,
235 (char *) NULL, 1, 0, &vers, 0);
236 if ((status != T_UPTODATE) && (status != T_CHECKOUT))
238 error (0, 0, "%s is locally modified", finfo->fullname);
243 if (finfo->update_dir[0] == '\0')
246 xdir = finfo->update_dir;
247 if ((p = findnode (mtlist, xdir)) != NULL)
249 tlist = ((struct master_lists *) p->data)->tlist;
253 struct master_lists *ml;
257 p->key = xstrdup (xdir);
259 ml = (struct master_lists *)
260 xmalloc (sizeof (struct master_lists));
262 p->data = (char *) ml;
263 p->delproc = masterlist_delproc;
264 (void) addnode (mtlist, p);
268 p->key = xstrdup (finfo->file);
270 p->delproc = tag_delproc;
271 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
272 if (vers->srcfile == NULL)
275 error (0, 0, "nothing known about %s", finfo->file);
279 /* Here we duplicate the calculation in tag_fileproc about which
280 version we are going to tag. There probably are some subtle races
281 (e.g. numtag is "foo" which gets moved between here and
283 if (numtag == NULL && date == NULL)
284 p->data = xstrdup (vers->vn_user);
286 p->data = RCS_getversion (vers->srcfile, numtag, date,
287 force_tag_match, NULL);
294 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
296 if (oversion == NULL)
303 else if (strcmp(oversion, p->data) == 0)
307 else if (!force_tag_move)
311 if (oversion != NULL)
322 (void) addnode (tlist, p);
327 check_filesdoneproc (callerdat, err, repos, update_dir, entries)
337 p = findnode(mtlist, update_dir);
340 tlist = ((struct master_lists *) p->data)->tlist;
344 tlist = (List *) NULL;
346 if ((tlist == NULL) || (tlist->list->next == tlist->list))
350 if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0)
352 error (0, 0, "Pre-tag check failed");
359 pretag_proc(repository, filter)
363 if (filter[0] == '/')
368 for (cp=s; *cp; cp++)
378 error (0, errno, "cannot find pre-tag filter '%s'", s);
386 run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add");
387 run_arg (repository);
388 walklist(tlist, pretag_list_proc, NULL);
389 return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
393 masterlist_delproc(p)
396 struct master_lists *ml;
398 ml = (struct master_lists *)p->data;
417 pretag_list_proc(p, closure)
431 * Called to tag a particular file (the currently checked out version is
432 * tagged with the specified tag - or the specified tag is deleted).
436 tag_fileproc (callerdat, finfo)
438 struct file_info *finfo;
440 char *version, *oversion;
441 char *nversion = NULL;
446 /* Lock the directory if it is not already locked. We can't rely
447 on tag_dirproc because it won't handle the case where the user
448 specifies a list of files on the command line. */
449 /* We do not need to acquire a full write lock for the tag operation:
450 the revisions are obtained from the working directory, so we do not
451 require consistency across the entire repository. However, we do
452 need to prevent simultaneous tag operations from interfering with
453 each other. Therefore, we write lock each directory as we enter
454 it, and unlock it as we leave it. */
455 lock_dir_for_write (finfo->repository);
457 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
459 if ((numtag != NULL) || (date != NULL))
461 nversion = RCS_getversion(vers->srcfile,
466 if (nversion == NULL)
476 * If -d is specified, "force_tag_match" is set, so that this call to
477 * RCS_getversion() will return a NULL version string if the symbolic
478 * tag does not exist in the RCS file.
480 * This is done here because it's MUCH faster than just blindly calling
481 * "rcs" to remove the tag... trust me.
484 version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
486 if (version == NULL || vers->srcfile == NULL)
493 if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0)
496 error (0, retcode == -1 ? errno : 0,
497 "failed to remove tag %s from %s", symtag,
498 vers->srcfile->path);
502 RCS_rewrite (vers->srcfile, NULL, NULL);
507 cvs_output ("D ", 2);
508 cvs_output (finfo->fullname, 0);
509 cvs_output ("\n", 1);
517 * If we are adding a tag, we need to know which version we have checked
518 * out and we'll tag that version.
520 if (nversion == NULL)
522 version = vers->vn_user;
533 else if (strcmp (version, "0") == 0)
536 error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file);
540 else if (version[0] == '-')
543 error (0, 0, "skipping removed but un-commited file `%s'", finfo->file);
547 else if (vers->srcfile == NULL)
550 error (0, 0, "cannot find revision control file for `%s'", finfo->file);
556 * As an enhancement for the case where a tag is being re-applied to a
557 * large number of files, make one extra call to RCS_getversion to see
558 * if the tag is already set in the RCS file. If so, check to see if it
559 * needs to be moved. If not, do nothing. This will likely save a lot of
560 * time when simply moving the tag to the "current" head revisions of a
561 * module -- which I have found to be a typical tagging operation.
563 rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
564 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
566 if (oversion != NULL)
568 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
571 * if versions the same and neither old or new are branches don't have
574 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
583 /* we're NOT going to move the tag */
584 cvs_output ("W ", 2);
585 cvs_output (finfo->fullname, 0);
586 cvs_output (" : ", 0);
587 cvs_output (symtag, 0);
588 cvs_output (" already exists on ", 0);
589 cvs_output (isbranch ? "branch" : "version", 0);
591 cvs_output (oversion, 0);
592 cvs_output (" : NOT MOVING tag to ", 0);
593 cvs_output (branch_mode ? "branch" : "version", 0);
596 cvs_output ("\n", 1);
604 if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
606 error (1, retcode == -1 ? errno : 0,
607 "failed to set tag %s to revision %s in %s",
608 symtag, rev, vers->srcfile->path);
612 RCS_rewrite (vers->srcfile, NULL, NULL);
614 /* more warm fuzzies */
617 cvs_output ("T ", 2);
618 cvs_output (finfo->fullname, 0);
619 cvs_output ("\n", 1);
622 if (nversion != NULL)
630 /* Clear any lock we may hold on the current directory. */
633 tag_filesdoneproc (callerdat, err, repos, update_dir, entries)
646 * Print a warm fuzzy message
650 tag_dirproc (callerdat, dir, repos, update_dir, entries)
658 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir);
662 /* Code relating to the val-tags file. Note that this file has no way
663 of knowing when a tag has been deleted. The problem is that there
664 is no way of knowing whether a tag still exists somewhere, when we
665 delete it some places. Using per-directory val-tags files (in
666 CVSREP) might be better, but that might slow down the process of
667 verifying that a tag is correct (maybe not, for the likely cases,
668 if carefully done), and/or be harder to implement correctly. */
675 static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo));
678 val_fileproc (callerdat, finfo)
680 struct file_info *finfo;
683 struct val_args *args = (struct val_args *)callerdat;
686 if ((rcsdata = finfo->rcs) == NULL)
687 /* Not sure this can happen, after all we passed only
688 W_REPOS | W_ATTIC. */
691 tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL);
694 /* FIXME: should find out a way to stop the search at this point. */
701 static Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *));
704 val_direntproc (callerdat, dir, repository, update_dir, entries)
711 /* This is not quite right--it doesn't get right the case of "cvs
712 update -d -r foobar" where foobar is a tag which exists only in
713 files in a directory which does not exist yet, but which is
714 about to be created. */
720 /* Check to see whether NAME is a valid tag. If so, return. If not
721 print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify
722 which files we will be operating on.
724 REPOSITORY is the repository if we need to cd into it, or NULL if
725 we are already there, or "" if we should do a W_LOCAL recursion.
726 Sorry for three cases, but the "" case is needed in case the
727 working directories come from diverse parts of the repository, the
728 NULL case avoids an unneccesary chdir, and the non-NULL, non-""
729 case is needed for checkout, where we don't want to chdir if the
730 tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
733 tag_check_valid (name, argc, argv, local, aflag, repository)
742 char *valtags_filename;
745 struct val_args the_val_args;
746 struct saved_cwd cwd;
749 /* Numeric tags require only a syntactic check. */
750 if (isdigit (name[0]))
753 for (p = name; *p != '\0'; ++p)
755 if (!(isdigit (*p) || *p == '.'))
757 Numeric tag %s contains characters other than digits and '.'", name);
762 /* Special tags are always valid. */
763 if (strcmp (name, TAG_BASE) == 0
764 || strcmp (name, TAG_HEAD) == 0)
767 /* FIXME: This routine doesn't seem to do any locking whatsoever
768 (and it is called from places which don't have locks in place).
769 If two processes try to write val-tags at the same time, it would
770 seem like we are in trouble. */
773 mytag.dsize = strlen (name);
775 valtags_filename = xmalloc (strlen (CVSroot_directory)
777 + sizeof CVSROOTADM_VALTAGS + 20);
778 strcpy (valtags_filename, CVSroot_directory);
779 strcat (valtags_filename, "/");
780 strcat (valtags_filename, CVSROOTADM);
781 strcat (valtags_filename, "/");
782 strcat (valtags_filename, CVSROOTADM_VALTAGS);
783 db = dbm_open (valtags_filename, O_RDWR, 0666);
786 if (!existence_error (errno))
787 error (1, errno, "cannot read %s", valtags_filename);
789 /* If the file merely fails to exist, we just keep going and create
790 it later if need be. */
796 val = dbm_fetch (db, mytag);
797 if (val.dptr != NULL)
799 /* Found. The tag is valid. */
801 free (valtags_filename);
804 /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */
807 /* We didn't find the tag in val-tags, so look through all the RCS files
808 to see whether it exists there. Yes, this is expensive, but there
809 is no other way to cope with a tag which might have been created
810 by an old version of CVS, from before val-tags was invented.
812 Since we need this code anyway, we also use it to create
813 entries in val-tags in general (that is, the val-tags entry
814 will get created the first time the tag is used, not when the
817 the_val_args.name = name;
818 the_val_args.found = 0;
820 which = W_REPOS | W_ATTIC;
822 if (repository != NULL)
824 if (repository[0] == '\0')
830 if ( CVS_CHDIR (repository) < 0)
831 error (1, errno, "cannot change to %s directory", repository);
835 err = start_recursion (val_fileproc, (FILESDONEPROC) NULL,
836 val_direntproc, (DIRLEAVEPROC) NULL,
837 (void *)&the_val_args,
838 argc, argv, local, which, aflag,
840 if (repository != NULL && repository[0] != '\0')
842 if (restore_cwd (&cwd, NULL))
847 if (!the_val_args.found)
848 error (1, 0, "no such tag %s", name);
851 /* The tags is valid but not mentioned in val-tags. Add it. */
858 free (valtags_filename);
865 omask = umask (cvsumask);
866 db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
867 (void) umask (omask);
871 error (0, errno, "cannot create %s", valtags_filename);
872 free (valtags_filename);
878 if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
879 error (0, errno, "cannot store %s into %s", name,
883 free (valtags_filename);
887 * Check whether a join tag is valid. This is just like
888 * tag_check_valid, but we must stop before the colon if there is one.
892 tag_check_valid_join (join_tag, argc, argv, local, aflag, repository)
902 c = xstrdup (join_tag);
906 if (isdigit (join_tag[0]))
908 "Numeric join tag %s may not contain a date specifier",
914 tag_check_valid (c, argc, argv, local, aflag, repository);