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 modules database, if necessary.
16 static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo));
17 static int check_filesdoneproc PROTO ((void *callerdat, int err,
18 char *repos, char *update_dir,
20 static int pretag_proc PROTO((char *repository, char *filter));
21 static void masterlist_delproc PROTO((Node *p));
22 static void tag_delproc PROTO((Node *p));
23 static int pretag_list_proc PROTO((Node *p, void *closure));
25 static Dtype rtag_dirproc PROTO ((void *callerdat, char *dir,
26 char *repos, char *update_dir,
28 static int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo));
29 static int rtag_filesdoneproc PROTO ((void *callerdat, int err,
30 char *repos, char *update_dir,
32 static int rtag_proc PROTO((int *pargc, char **argv, char *xwhere,
33 char *mwhere, char *mfile, int shorten,
34 int local_specified, char *mname, char *msg));
35 static int rtag_delete PROTO((RCSNode *rcsfile));
56 static int numtag_validated = 0;
57 static int delete_flag; /* adding a tag by default */
58 static int attic_too; /* remove tag from Attic files */
59 static int branch_mode; /* make an automagic "branch" tag */
61 static int local; /* recursive by default */
62 static int force_tag_match = 1; /* force by default */
63 static int force_tag_move; /* don't move existing tags by default */
65 static const char *const rtag_usage[] =
67 "Usage: %s %s [-aflRnF] [-b] [-d] [-r tag|-D date] tag modules...\n",
68 "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
69 "\t-f\tForce a head revision match if tag/date not found.\n",
70 "\t-l\tLocal directory only, not recursive\n",
71 "\t-R\tProcess directories recursively.\n",
72 "\t-n\tNo execution of 'tag program'\n",
73 "\t-d\tDelete the given Tag.\n",
74 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
75 "\t-r rev\tExisting revision/tag.\n",
76 "\t-D\tExisting date.\n",
77 "\t-F\tMove tag if it already exists\n",
78 "(Specify the --help global option for a list of other help options)\n",
90 int run_module_prog = 1;
97 while ((c = getopt (argc, argv, "+FanfQqlRdbr:D:")) != -1)
109 #ifdef SERVER_SUPPORT
110 /* The CVS 1.5 client sends these options (in addition to
111 Global_option requests), so we must ignore them. */
115 "-q or -Q must be specified before \"%s\"",
139 date = Make_Date (optarg);
159 error (1, 0, "-r and -D options are mutually exclusive");
160 if (delete_flag && branch_mode)
161 error (0, 0, "warning: -b ignored with -d options");
162 RCS_check_tag (symtag);
164 #ifdef CLIENT_SUPPORT
167 /* We're the client side. Fire up the remote server. */
172 if (!force_tag_match)
182 if (!run_module_prog)
188 option_with_arg ("-r", numtag);
190 client_senddate (date);
196 for (i = 0; i < argc; ++i)
200 send_to_server ("rtag\012", 0);
201 return get_responses_and_close ();
206 for (i = 0; i < argc; i++)
208 /* XXX last arg should be repository, but doesn't make sense here */
209 history_write ('T', (delete_flag ? "D" : (numtag ? numtag :
210 (date ? date : "A"))), symtag, argv[i], "");
211 err += do_module (db, argv[i], TAG,
212 delete_flag ? "Untagging" : "Tagging",
213 rtag_proc, (char *) NULL, 0, 0, run_module_prog,
221 * callback proc for doing the real work of tagging
225 rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
237 /* Begin section which is identical to patch_proc--should this
238 be abstracted out somehow? */
244 repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0])
245 + (mfile == NULL ? 0 : strlen (mfile)) + 30);
246 (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]);
247 where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile))
249 (void) strcpy (where, argv[0]);
251 /* if mfile isn't null, we need to set up to do only part of the module */
257 /* if the portion of the module is a path, put the dir part on repos */
258 if ((cp = strrchr (mfile, '/')) != NULL)
261 (void) strcat (repository, "/");
262 (void) strcat (repository, mfile);
263 (void) strcat (where, "/");
264 (void) strcat (where, mfile);
268 /* take care of the rest */
269 path = xmalloc (strlen (repository) + strlen (mfile) + 5);
270 (void) sprintf (path, "%s/%s", repository, mfile);
273 /* directory means repository gets the dir tacked on */
274 (void) strcpy (repository, path);
275 (void) strcat (where, "/");
276 (void) strcat (where, mfile);
282 /* a file means muck argv */
283 for (i = 1; i < *pargc; i++)
285 argv[1] = xstrdup (mfile);
291 /* cd to the starting repository */
292 if ( CVS_CHDIR (repository) < 0)
294 error (0, errno, "cannot chdir to %s", repository);
299 /* End section which is identical to patch_proc. */
301 if (delete_flag || attic_too || (force_tag_match && numtag))
302 which = W_REPOS | W_ATTIC;
306 if (numtag != NULL && !numtag_validated)
308 tag_check_valid (numtag, *pargc - 1, argv + 1, local, 0, NULL);
309 numtag_validated = 1;
312 /* check to make sure they are authorized to tag all the
313 specified files in the repository */
316 err = start_recursion (check_fileproc, check_filesdoneproc,
317 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
318 *pargc - 1, argv + 1, local, which, 0, 1,
323 error (1, 0, "correct the above errors first!");
326 /* start the recursion processor */
327 err = start_recursion (rtag_fileproc, rtag_filesdoneproc, rtag_dirproc,
328 (DIRLEAVEPROC) NULL, NULL,
329 *pargc - 1, argv + 1, local,
330 which, 0, 0, where, 1);
337 /* check file that is to be tagged */
338 /* All we do here is add it to our list */
341 check_fileproc (callerdat, finfo)
343 struct file_info *finfo;
349 if (finfo->update_dir[0] == '\0')
352 xdir = finfo->update_dir;
353 if ((p = findnode (mtlist, xdir)) != NULL)
355 tlist = ((struct master_lists *) p->data)->tlist;
359 struct master_lists *ml;
363 p->key = xstrdup (xdir);
365 ml = (struct master_lists *)
366 xmalloc (sizeof (struct master_lists));
368 p->data = (char *) ml;
369 p->delproc = masterlist_delproc;
370 (void) addnode (mtlist, p);
374 p->key = xstrdup (finfo->file);
376 p->delproc = tag_delproc;
377 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
378 p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match,
385 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
387 if (oversion == NULL)
394 else if (strcmp(oversion, p->data) == 0)
398 else if (!force_tag_move)
402 if (oversion != NULL)
413 (void) addnode (tlist, p);
418 check_filesdoneproc (callerdat, err, repos, update_dir, entries)
428 p = findnode(mtlist, update_dir);
431 tlist = ((struct master_lists *) p->data)->tlist;
435 tlist = (List *) NULL;
437 if ((tlist == NULL) || (tlist->list->next == tlist->list))
441 if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0)
443 error (0, 0, "Pre-tag check failed");
450 pretag_proc(repository, filter)
454 if (filter[0] == '/')
459 for (cp=s; *cp; cp++)
469 error (0, errno, "cannot find pre-tag filter '%s'", s);
477 run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add");
478 run_arg (repository);
479 walklist(tlist, pretag_list_proc, NULL);
480 return (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
484 masterlist_delproc(p)
487 struct master_lists *ml;
489 ml = (struct master_lists *)p->data;
508 pretag_list_proc(p, closure)
521 * Called to tag a particular file, as appropriate with the options that were
526 rtag_fileproc (callerdat, finfo)
528 struct file_info *finfo;
534 /* Lock the directory if it is not already locked. We might be
535 able to rely on rtag_dirproc for this. */
537 /* It would be nice to provide consistency with respect to
538 commits; however CVS lacks the infrastructure to do that (see
539 Concurrency in cvs.texinfo and comment in do_recursion). We
540 can and will prevent simultaneous tag operations from
541 interfering with each other, by write locking each directory as
542 we enter it, and unlocking it as we leave it. */
544 lock_dir_for_write (finfo->repository);
546 /* find the parsed RCS data */
547 if ((rcsfile = finfo->rcs) == NULL)
551 * For tagging an RCS file which is a symbolic link, you'd best be
552 * running with RCS 5.6, since it knows how to handle symbolic links
553 * correctly without breaking your link!
557 return (rtag_delete (rcsfile));
560 * If we get here, we are adding a tag. But, if -a was specified, we
561 * need to check to see if a -r or -D option was specified. If neither
562 * was specified and the file is in the Attic, remove the tag.
564 if (attic_too && (!numtag && !date))
566 if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
567 return (rtag_delete (rcsfile));
570 version = RCS_getversion (rcsfile, numtag, date, force_tag_match,
574 /* If -a specified, clean up any old tags */
576 (void) rtag_delete (rcsfile);
578 if (!quiet && !force_tag_match)
580 error (0, 0, "cannot find tag `%s' in `%s'",
581 numtag ? numtag : "head", rcsfile->path);
586 if (numtag && isdigit (*numtag) && strcmp (numtag, version) != 0)
590 * We didn't find a match for the numeric tag that was specified, but
591 * that's OK. just pass the numeric tag on to rcs, to be tagged as
592 * specified. Could get here if one tried to tag "1.1.1" and there
593 * was a 1.1.1 branch with some head revision. In this case, we want
594 * the tag to reference "1.1.1" and not the revision at the head of
595 * the branch. Use a symbolic tag for that.
597 rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
598 retcode = RCS_settag(rcsfile, symtag, numtag);
600 RCS_rewrite (rcsfile, NULL, NULL);
607 * As an enhancement for the case where a tag is being re-applied to
608 * a large body of a module, make one extra call to RCS_getversion to
609 * see if the tag is already set in the RCS file. If so, check to
610 * see if it needs to be moved. If not, do nothing. This will
611 * likely save a lot of time when simply moving the tag to the
612 * "current" head revisions of a module -- which I have found to be a
613 * typical tagging operation.
615 rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
616 oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1,
618 if (oversion != NULL)
620 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
623 * if versions the same and neither old or new are branches don't
624 * have to do anything
626 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
635 /* we're NOT going to move the tag */
636 (void) printf ("W %s", finfo->fullname);
638 (void) printf (" : %s already exists on %s %s",
639 symtag, isbranch ? "branch" : "version",
641 (void) printf (" : NOT MOVING tag to %s %s\n",
642 branch_mode ? "branch" : "version", rev);
649 retcode = RCS_settag(rcsfile, symtag, rev);
651 RCS_rewrite (rcsfile, NULL, NULL);
656 error (1, retcode == -1 ? errno : 0,
657 "failed to set tag `%s' to revision `%s' in `%s'",
658 symtag, rev, rcsfile->path);
671 * If -d is specified, "force_tag_match" is set, so that this call to
672 * RCS_getversion() will return a NULL version string if the symbolic
673 * tag does not exist in the RCS file.
675 * If the -r flag was used, numtag is set, and we only delete the
676 * symtag from files that have numtag.
678 * This is done here because it's MUCH faster than just blindly calling
679 * "rcs" to remove the tag... trust me.
682 rtag_delete (rcsfile)
690 version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1,
697 version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1,
703 if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
706 error (0, retcode == -1 ? errno : 0,
707 "failed to remove tag `%s' from `%s'", symtag,
711 RCS_rewrite (rcsfile, NULL, NULL);
715 /* Clear any lock we may hold on the current directory. */
718 rtag_filesdoneproc (callerdat, err, repos, update_dir, entries)
731 * Print a warm fuzzy message
735 rtag_dirproc (callerdat, dir, repos, update_dir, entries)
742 if (ignore_directory (update_dir))
744 /* print the warm fuzzy message */
746 error (0, 0, "Ignoring %s", update_dir);
751 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging",