release.c revision 81404
117721Speter/* 217721Speter * Release: "cancel" a checkout in the history log. 317721Speter * 417721Speter * - Enter a line in the history log indicating the "release". - If asked to, 517721Speter * delete the local working directory. 617721Speter */ 717721Speter 817721Speter#include "cvs.h" 954427Speter#include "savecwd.h" 1025839Speter#include "getline.h" 1117721Speter 1217721Speterstatic const char *const release_usage[] = 1317721Speter{ 1425839Speter "Usage: %s %s [-d] directories...\n", 1517721Speter "\t-d\tDelete the given directory.\n", 1632785Speter "(Specify the --help global option for a list of other help options)\n", 1717721Speter NULL 1817721Speter}; 1917721Speter 2025839Speter#ifdef SERVER_SUPPORT 2125839Speterstatic int release_server PROTO ((int argc, char **argv)); 2217721Speter 2325839Speter/* This is the server side of cvs release. */ 2425839Speterstatic int 2525839Speterrelease_server (argc, argv) 2625839Speter int argc; 2725839Speter char **argv; 2825839Speter{ 2925839Speter int i; 3017721Speter 3125839Speter /* Note that we skip argv[0]. */ 3225839Speter for (i = 1; i < argc; ++i) 3325839Speter history_write ('F', argv[i], "", argv[i], ""); 3425839Speter return 0; 3525839Speter} 3625839Speter 3725839Speter#endif /* SERVER_SUPPORT */ 3825839Speter 3925839Speter/* There are various things to improve about this implementation: 4025839Speter 4125839Speter 1. Using run_popen to run "cvs update" could be replaced by a 4217721Speter fairly simple start_recursion/classify_file loop--a win for 4325839Speter portability, performance, and cleanliness. In particular, there is 4425839Speter no particularly good way to find the right "cvs". 4517721Speter 4625839Speter 2. The fact that "cvs update" contacts the server slows things down; 4725839Speter it undermines the case for using "cvs release" rather than "rm -rf". 4825839Speter However, for correctly printing "? foo" and correctly handling 4932785Speter CVSROOTADM_IGNORE, we currently need to contact the server. (One 5032785Speter idea for how to fix this is to stash a copy of CVSROOTADM_IGNORE in 5132785Speter the working directories; see comment at base_* in entries.c for a 5232785Speter few thoughts on that). 5317721Speter 5425839Speter 3. Would be nice to take processing things on the client side one step 5525839Speter further, and making it like edit/unedit in terms of working well if 5625839Speter disconnected from the network, and then sending a delayed 5725839Speter notification. 5817721Speter 5925839Speter 4. Having separate network turnarounds for the "Notify" request 6025839Speter which we do as part of unedit, and for the "release" itself, is slow 6125839Speter and unnecessary. */ 6217721Speter 6317721Speterint 6417721Speterrelease (argc, argv) 6517721Speter int argc; 6617721Speter char **argv; 6717721Speter{ 6817721Speter FILE *fp; 6925839Speter int i, c; 7025839Speter char *repository; 7125839Speter char *line = NULL; 7225839Speter size_t line_allocated = 0; 7325839Speter char *update_cmd; 7417721Speter char *thisarg; 7517721Speter int arg_start_idx; 7617721Speter int err = 0; 7725839Speter short delete_flag = 0; 7854427Speter struct saved_cwd cwd; 7917721Speter 8017721Speter#ifdef SERVER_SUPPORT 8125839Speter if (server_active) 8225839Speter return release_server (argc, argv); 8317721Speter#endif 8425839Speter 8525839Speter /* Everything from here on is client or local. */ 8625839Speter if (argc == -1) 8725839Speter usage (release_usage); 8826065Speter optind = 0; 8925839Speter while ((c = getopt (argc, argv, "+Qdq")) != -1) 9025839Speter { 9125839Speter switch (c) 9225839Speter { 9325839Speter case 'Q': 9425839Speter case 'q': 9525839Speter error (1, 0, 9625839Speter "-q or -Q must be specified before \"%s\"", 9725839Speter command_name); 9817721Speter break; 9925839Speter case 'd': 10017721Speter delete_flag++; 10117721Speter break; 10225839Speter case '?': 10325839Speter default: 10417721Speter usage (release_usage); 10517721Speter break; 10625839Speter } 10725839Speter } 10825839Speter argc -= optind; 10925839Speter argv += optind; 11017721Speter 11117721Speter /* We're going to run "cvs -n -q update" and check its output; if 11217721Speter * the output is sufficiently unalarming, then we release with no 11317721Speter * questions asked. Else we prompt, then maybe release. 11454427Speter * (Well, actually we ask no matter what. Our notion of "sufficiently 11554427Speter * unalarming" doesn't take into account "? foo.c" files, so it is 11654427Speter * up to the user to take note of them, at least currently 11754427Speter * (ignore-193 in testsuite)). 11817721Speter */ 11917721Speter /* Construct the update command. */ 12025839Speter update_cmd = xmalloc (strlen (program_path) 12181404Speter + strlen (current_parsed_root->original) 12225839Speter + 20); 12317721Speter sprintf (update_cmd, "%s -n -q -d %s update", 12481404Speter program_path, current_parsed_root->original); 12517721Speter 12617721Speter#ifdef CLIENT_SUPPORT 12717721Speter /* Start the server; we'll close it after looping. */ 12881404Speter if (current_parsed_root->isremote) 12925839Speter { 13017721Speter start_server (); 13117721Speter ign_setup (); 13225839Speter } 13317721Speter#endif /* CLIENT_SUPPORT */ 13417721Speter 13554427Speter /* Remember the directory where "cvs release" was invoked because 13654427Speter all args are relative to this directory and we chdir around. 13754427Speter */ 13854427Speter if (save_cwd (&cwd)) 13954427Speter error_exit (); 14054427Speter 14125839Speter arg_start_idx = 0; 14217721Speter 14317721Speter for (i = arg_start_idx; i < argc; i++) 14417721Speter { 14525839Speter thisarg = argv[i]; 14625839Speter 14717721Speter if (isdir (thisarg)) 14817721Speter { 14925839Speter if (CVS_CHDIR (thisarg) < 0) 15025839Speter { 15125839Speter if (!really_quiet) 15225839Speter error (0, errno, "can't chdir to: %s", thisarg); 15325839Speter continue; 15425839Speter } 15525839Speter if (!isdir (CVSADM)) 15625839Speter { 15725839Speter if (!really_quiet) 15825839Speter error (0, 0, "no repository directory: %s", thisarg); 15954427Speter if (restore_cwd (&cwd, NULL)) 16054427Speter error_exit (); 16125839Speter continue; 16225839Speter } 16317721Speter } 16417721Speter else 16517721Speter { 16625839Speter if (!really_quiet) 16725839Speter error (0, 0, "no such directory: %s", thisarg); 16825839Speter continue; 16917721Speter } 17017721Speter 17117721Speter repository = Name_Repository ((char *) NULL, (char *) NULL); 17225839Speter 17317721Speter if (!really_quiet) 17417721Speter { 17532785Speter int line_length; 17632785Speter 17725839Speter /* The "release" command piggybacks on "update", which 17825839Speter does the real work of finding out if anything is not 17925839Speter up-to-date with the repository. Then "release" prompts 18025839Speter the user, telling her how many files have been 18125839Speter modified, and asking if she still wants to do the 18225839Speter release. */ 18325839Speter fp = run_popen (update_cmd, "r"); 18432785Speter if (fp == NULL) 18532785Speter error (1, 0, "cannot run command %s", update_cmd); 18632785Speter 18725839Speter c = 0; 18817721Speter 18932785Speter while ((line_length = getline (&line, &line_allocated, fp)) >= 0) 19025839Speter { 19125839Speter if (strchr ("MARCZ", *line)) 19225839Speter c++; 19366525Speter (void) fputs (line, stdout); 19425839Speter } 19532785Speter if (line_length < 0 && !feof (fp)) 19632785Speter error (0, errno, "cannot read from subprocess"); 19717721Speter 19825839Speter /* If the update exited with an error, then we just want to 19925839Speter complain and go on to the next arg. Especially, we do 20025839Speter not want to delete the local copy, since it's obviously 20125839Speter not what the user thinks it is. */ 20225839Speter if ((pclose (fp)) != 0) 20325839Speter { 20425839Speter error (0, 0, "unable to release `%s'", thisarg); 20554427Speter free (repository); 20654427Speter if (restore_cwd (&cwd, NULL)) 20754427Speter error_exit (); 20825839Speter continue; 20925839Speter } 21017721Speter 21125839Speter printf ("You have [%d] altered files in this repository.\n", 21225839Speter c); 21325839Speter printf ("Are you sure you want to release %sdirectory `%s': ", 21425839Speter delete_flag ? "(and delete) " : "", thisarg); 21525839Speter c = !yesno (); 21625839Speter if (c) /* "No" */ 21725839Speter { 21825839Speter (void) fprintf (stderr, "** `%s' aborted by user choice.\n", 21925839Speter command_name); 22025839Speter free (repository); 22154427Speter if (restore_cwd (&cwd, NULL)) 22254427Speter error_exit (); 22325839Speter continue; 22425839Speter } 22517721Speter } 22617721Speter 22717721Speter if (1 22817721Speter#ifdef CLIENT_SUPPORT 22981404Speter && !(current_parsed_root->isremote 23017721Speter && (!supported_request ("noop") 23117721Speter || !supported_request ("Notify"))) 23217721Speter#endif 23317721Speter ) 23417721Speter { 23525839Speter /* We are chdir'ed into the directory in question. 23625839Speter So don't pass args to unedit. */ 23725839Speter int argc = 1; 23825839Speter char *argv[3]; 23925839Speter argv[0] = "dummy"; 24025839Speter argv[1] = NULL; 24125839Speter err += unedit (argc, argv); 24217721Speter } 24317721Speter 24417721Speter#ifdef CLIENT_SUPPORT 24581404Speter if (current_parsed_root->isremote) 24617721Speter { 24725839Speter send_to_server ("Argument ", 0); 24825839Speter send_to_server (thisarg, 0); 24925839Speter send_to_server ("\012", 1); 25025839Speter send_to_server ("release\012", 0); 25125839Speter } 25217721Speter else 25325839Speter#endif /* CLIENT_SUPPORT */ 25417721Speter { 25525839Speter history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ 25625839Speter } 25725839Speter 25817721Speter free (repository); 25925839Speter 26054427Speter if (restore_cwd (&cwd, NULL)) 26154427Speter error_exit (); 26254427Speter 26354427Speter if (delete_flag) 26454427Speter { 26554427Speter /* FIXME? Shouldn't this just delete the CVS-controlled 26654427Speter files and, perhaps, the files that would normally be 26754427Speter ignored and leave everything else? */ 26854427Speter 26954427Speter if (unlink_file_dir (thisarg) < 0) 27054427Speter error (0, errno, "deletion of directory %s failed", thisarg); 27154427Speter } 27254427Speter 27317721Speter#ifdef CLIENT_SUPPORT 27481404Speter if (current_parsed_root->isremote) 27525839Speter err += get_server_responses (); 27617721Speter#endif /* CLIENT_SUPPORT */ 27725839Speter } 27825839Speter 27954427Speter if (restore_cwd (&cwd, NULL)) 28054427Speter error_exit (); 28154427Speter free_cwd (&cwd); 28254427Speter 28325839Speter#ifdef CLIENT_SUPPORT 28481404Speter if (current_parsed_root->isremote) 28525839Speter { 28625839Speter /* Unfortunately, client.c doesn't offer a way to close 28725839Speter the connection without waiting for responses. The extra 28825839Speter network turnaround here is quite unnecessary other than 28925839Speter that.... */ 29025839Speter send_to_server ("noop\012", 0); 29125839Speter err += get_responses_and_close (); 29225839Speter } 29325839Speter#endif /* CLIENT_SUPPORT */ 29425839Speter 29525839Speter free (update_cmd); 29625839Speter if (line != NULL) 29725839Speter free (line); 29817721Speter return err; 29917721Speter} 300