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 rev|-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_files (argc, argv, local, 0,
183 /* I think the -c case is like "cvs status", in
184 which we really better be correct rather than
185 being fast; it is just too confusing otherwise. */
186 check_uptodate ? 0 : SEND_NO_CONTENTS);
187 send_file_names (argc, argv, SEND_EXPAND_WILD);
188 send_to_server ("tag\012", 0);
189 return get_responses_and_close ();
194 tag_check_valid (numtag, argc, argv, local, 0, "");
196 /* check to make sure they are authorized to tag all the
197 specified files in the repository */
200 err = start_recursion (check_fileproc, check_filesdoneproc,
201 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
202 argc, argv, local, W_LOCAL, 0, 1,
207 error (1, 0, "correct the above errors first!");
210 /* start the recursion processor */
211 err = start_recursion (tag_fileproc, tag_filesdoneproc, tag_dirproc,
212 (DIRLEAVEPROC) NULL, NULL, argc, argv, local,
213 W_LOCAL, 0, 0, (char *) NULL, 1);
218 /* check file that is to be tagged */
219 /* All we do here is add it to our list */
222 check_fileproc (callerdat, finfo)
224 struct file_info *finfo;
232 Ctype status = Classify_File (finfo, (char *) NULL, (char *) NULL,
233 (char *) NULL, 1, 0, &vers, 0);
234 if ((status != T_UPTODATE) && (status != T_CHECKOUT))
236 error (0, 0, "%s is locally modified", finfo->fullname);
241 if (finfo->update_dir[0] == '\0')
244 xdir = finfo->update_dir;
245 if ((p = findnode (mtlist, xdir)) != NULL)
247 tlist = ((struct master_lists *) p->data)->tlist;
251 struct master_lists *ml;
255 p->key = xstrdup (xdir);
257 ml = (struct master_lists *)
258 xmalloc (sizeof (struct master_lists));
260 p->data = (char *) ml;
261 p->delproc = masterlist_delproc;
262 (void) addnode (mtlist, p);
266 p->key = xstrdup (finfo->file);
268 p->delproc = tag_delproc;
269 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
270 if (vers->srcfile == NULL)
273 error (0, 0, "nothing known about %s", finfo->file);
277 /* Here we duplicate the calculation in tag_fileproc about which
278 version we are going to tag. There probably are some subtle races
279 (e.g. numtag is "foo" which gets moved between here and
281 if (numtag == NULL && date == NULL)
282 p->data = xstrdup (vers->vn_user);
284 p->data = RCS_getversion (vers->srcfile, numtag, date,
285 force_tag_match, NULL);
292 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
294 if (oversion == NULL)
298 /* Deleting a tag which did not exist is a noop and
299 should not be logged. */
303 else if (delete_flag)
306 p->data = xstrdup (oversion);
308 else if (strcmp(oversion, p->data) == 0)
312 else if (!force_tag_move)
316 if (oversion != NULL)
327 (void) addnode (tlist, p);
332 check_filesdoneproc (callerdat, err, repos, update_dir, entries)
342 p = findnode(mtlist, update_dir);
345 tlist = ((struct master_lists *) p->data)->tlist;
349 tlist = (List *) NULL;
351 if ((tlist == NULL) || (tlist->list->next == tlist->list))
355 if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0)
357 error (0, 0, "Pre-tag check failed");
364 pretag_proc(repository, filter)
368 if (filter[0] == '/')
373 for (cp=s; *cp; cp++)
375 if (isspace ((unsigned char) *cp))
383 error (0, errno, "cannot find pre-tag filter '%s'", s);
391 run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add");
392 run_arg (repository);
393 walklist(tlist, pretag_list_proc, NULL);
394 return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
398 masterlist_delproc(p)
401 struct master_lists *ml;
403 ml = (struct master_lists *)p->data;
422 pretag_list_proc(p, closure)
436 * Called to tag a particular file (the currently checked out version is
437 * tagged with the specified tag - or the specified tag is deleted).
441 tag_fileproc (callerdat, finfo)
443 struct file_info *finfo;
445 char *version, *oversion;
446 char *nversion = NULL;
451 /* Lock the directory if it is not already locked. We can't rely
452 on tag_dirproc because it won't handle the case where the user
453 specifies a list of files on the command line. */
454 /* We do not need to acquire a full write lock for the tag operation:
455 the revisions are obtained from the working directory, so we do not
456 require consistency across the entire repository. However, we do
457 need to prevent simultaneous tag operations from interfering with
458 each other. Therefore, we write lock each directory as we enter
459 it, and unlock it as we leave it. */
460 lock_dir_for_write (finfo->repository);
462 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
464 if ((numtag != NULL) || (date != NULL))
466 nversion = RCS_getversion(vers->srcfile,
471 if (nversion == NULL)
481 * If -d is specified, "force_tag_match" is set, so that this call to
482 * RCS_getversion() will return a NULL version string if the symbolic
483 * tag does not exist in the RCS file.
485 * This is done here because it's MUCH faster than just blindly calling
486 * "rcs" to remove the tag... trust me.
489 version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
491 if (version == NULL || vers->srcfile == NULL)
498 if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0)
501 error (0, retcode == -1 ? errno : 0,
502 "failed to remove tag %s from %s", symtag,
503 vers->srcfile->path);
507 RCS_rewrite (vers->srcfile, NULL, NULL);
512 cvs_output ("D ", 2);
513 cvs_output (finfo->fullname, 0);
514 cvs_output ("\n", 1);
522 * If we are adding a tag, we need to know which version we have checked
523 * out and we'll tag that version.
525 if (nversion == NULL)
527 version = vers->vn_user;
538 else if (strcmp (version, "0") == 0)
541 error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file);
545 else if (version[0] == '-')
548 error (0, 0, "skipping removed but un-commited file `%s'", finfo->file);
552 else if (vers->srcfile == NULL)
555 error (0, 0, "cannot find revision control file for `%s'", finfo->file);
561 * As an enhancement for the case where a tag is being re-applied to a
562 * large number of files, make one extra call to RCS_getversion to see
563 * if the tag is already set in the RCS file. If so, check to see if it
564 * needs to be moved. If not, do nothing. This will likely save a lot of
565 * time when simply moving the tag to the "current" head revisions of a
566 * module -- which I have found to be a typical tagging operation.
568 rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
569 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
571 if (oversion != NULL)
573 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
576 * if versions the same and neither old or new are branches don't have
579 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
588 /* we're NOT going to move the tag */
589 cvs_output ("W ", 2);
590 cvs_output (finfo->fullname, 0);
591 cvs_output (" : ", 0);
592 cvs_output (symtag, 0);
593 cvs_output (" already exists on ", 0);
594 cvs_output (isbranch ? "branch" : "version", 0);
596 cvs_output (oversion, 0);
597 cvs_output (" : NOT MOVING tag to ", 0);
598 cvs_output (branch_mode ? "branch" : "version", 0);
601 cvs_output ("\n", 1);
609 if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
611 error (1, retcode == -1 ? errno : 0,
612 "failed to set tag %s to revision %s in %s",
613 symtag, rev, vers->srcfile->path);
617 RCS_rewrite (vers->srcfile, NULL, NULL);
619 /* more warm fuzzies */
622 cvs_output ("T ", 2);
623 cvs_output (finfo->fullname, 0);
624 cvs_output ("\n", 1);
627 if (nversion != NULL)
635 /* Clear any lock we may hold on the current directory. */
638 tag_filesdoneproc (callerdat, err, repos, update_dir, entries)
651 * Print a warm fuzzy message
655 tag_dirproc (callerdat, dir, repos, update_dir, entries)
663 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir);
667 /* Code relating to the val-tags file. Note that this file has no way
668 of knowing when a tag has been deleted. The problem is that there
669 is no way of knowing whether a tag still exists somewhere, when we
670 delete it some places. Using per-directory val-tags files (in
671 CVSREP) might be better, but that might slow down the process of
672 verifying that a tag is correct (maybe not, for the likely cases,
673 if carefully done), and/or be harder to implement correctly. */
680 static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo));
683 val_fileproc (callerdat, finfo)
685 struct file_info *finfo;
688 struct val_args *args = (struct val_args *)callerdat;
691 if ((rcsdata = finfo->rcs) == NULL)
692 /* Not sure this can happen, after all we passed only
693 W_REPOS | W_ATTIC. */
696 tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL);
699 /* FIXME: should find out a way to stop the search at this point. */
706 static Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *));
709 val_direntproc (callerdat, dir, repository, update_dir, entries)
716 /* This is not quite right--it doesn't get right the case of "cvs
717 update -d -r foobar" where foobar is a tag which exists only in
718 files in a directory which does not exist yet, but which is
719 about to be created. */
725 /* Check to see whether NAME is a valid tag. If so, return. If not
726 print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify
727 which files we will be operating on.
729 REPOSITORY is the repository if we need to cd into it, or NULL if
730 we are already there, or "" if we should do a W_LOCAL recursion.
731 Sorry for three cases, but the "" case is needed in case the
732 working directories come from diverse parts of the repository, the
733 NULL case avoids an unneccesary chdir, and the non-NULL, non-""
734 case is needed for checkout, where we don't want to chdir if the
735 tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
738 tag_check_valid (name, argc, argv, local, aflag, repository)
747 char *valtags_filename;
750 struct val_args the_val_args;
751 struct saved_cwd cwd;
754 /* Numeric tags require only a syntactic check. */
755 if (isdigit ((unsigned char) name[0]))
758 for (p = name; *p != '\0'; ++p)
760 if (!(isdigit ((unsigned char) *p) || *p == '.'))
762 Numeric tag %s contains characters other than digits and '.'", name);
767 /* Special tags are always valid. */
768 if (strcmp (name, TAG_BASE) == 0
769 || strcmp (name, TAG_HEAD) == 0)
772 /* FIXME: This routine doesn't seem to do any locking whatsoever
773 (and it is called from places which don't have locks in place).
774 If two processes try to write val-tags at the same time, it would
775 seem like we are in trouble. */
778 mytag.dsize = strlen (name);
780 valtags_filename = xmalloc (strlen (CVSroot_directory)
782 + sizeof CVSROOTADM_VALTAGS + 20);
783 strcpy (valtags_filename, CVSroot_directory);
784 strcat (valtags_filename, "/");
785 strcat (valtags_filename, CVSROOTADM);
786 strcat (valtags_filename, "/");
787 strcat (valtags_filename, CVSROOTADM_VALTAGS);
788 db = dbm_open (valtags_filename, O_RDWR, 0666);
791 if (!existence_error (errno))
792 error (1, errno, "cannot read %s", valtags_filename);
794 /* If the file merely fails to exist, we just keep going and create
795 it later if need be. */
801 val = dbm_fetch (db, mytag);
802 if (val.dptr != NULL)
804 /* Found. The tag is valid. */
806 free (valtags_filename);
809 /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */
812 /* We didn't find the tag in val-tags, so look through all the RCS files
813 to see whether it exists there. Yes, this is expensive, but there
814 is no other way to cope with a tag which might have been created
815 by an old version of CVS, from before val-tags was invented.
817 Since we need this code anyway, we also use it to create
818 entries in val-tags in general (that is, the val-tags entry
819 will get created the first time the tag is used, not when the
822 the_val_args.name = name;
823 the_val_args.found = 0;
825 which = W_REPOS | W_ATTIC;
827 if (repository != NULL)
829 if (repository[0] == '\0')
835 if ( CVS_CHDIR (repository) < 0)
836 error (1, errno, "cannot change to %s directory", repository);
840 err = start_recursion (val_fileproc, (FILESDONEPROC) NULL,
841 val_direntproc, (DIRLEAVEPROC) NULL,
842 (void *)&the_val_args,
843 argc, argv, local, which, aflag,
845 if (repository != NULL && repository[0] != '\0')
847 if (restore_cwd (&cwd, NULL))
852 if (!the_val_args.found)
853 error (1, 0, "no such tag %s", name);
856 /* The tags is valid but not mentioned in val-tags. Add it. */
863 free (valtags_filename);
870 omask = umask (cvsumask);
871 db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
872 (void) umask (omask);
876 error (0, errno, "cannot create %s", valtags_filename);
877 free (valtags_filename);
883 if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
884 error (0, errno, "cannot store %s into %s", name,
888 free (valtags_filename);
892 * Check whether a join tag is valid. This is just like
893 * tag_check_valid, but we must stop before the colon if there is one.
897 tag_check_valid_join (join_tag, argc, argv, local, aflag, repository)
907 c = xstrdup (join_tag);
911 if (isdigit ((unsigned char) join_tag[0]))
913 "Numeric join tag %s may not contain a date specifier",
917 /* hmmm... I think it makes sense to allow -j:<date>, but
918 * for now this fixes a bug where CVS just spins and spins (I
919 * think in the RCS code) looking for a zero length tag.
923 "argument to join may not contain a date specifier without a tag");
926 tag_check_valid (c, argc, argv, local, aflag, repository);