2 * Release: "cancel" a checkout in the history log.
4 * - Enter a line in the history log indicating the "release". - If asked to,
5 * delete the local working directory.
12 static const char *const release_usage[] =
14 "Usage: %s %s [-d] directories...\n",
15 "\t-d\tDelete the given directory.\n",
16 "(Specify the --help global option for a list of other help options)\n",
21 static int release_server (int argc, char **argv);
23 /* This is the server side of cvs release. */
25 release_server (int argc, char **argv)
29 /* Note that we skip argv[0]. */
30 for (i = 1; i < argc; ++i)
31 history_write ('F', argv[i], "", argv[i], "");
35 #endif /* SERVER_SUPPORT */
37 /* There are various things to improve about this implementation:
39 1. Using run_popen to run "cvs update" could be replaced by a
40 fairly simple start_recursion/classify_file loop--a win for
41 portability, performance, and cleanliness. In particular, there is
42 no particularly good way to find the right "cvs".
44 2. The fact that "cvs update" contacts the server slows things down;
45 it undermines the case for using "cvs release" rather than "rm -rf".
46 However, for correctly printing "? foo" and correctly handling
47 CVSROOTADM_IGNORE, we currently need to contact the server. (One
48 idea for how to fix this is to stash a copy of CVSROOTADM_IGNORE in
49 the working directories; see comment at base_* in entries.c for a
50 few thoughts on that).
52 3. Would be nice to take processing things on the client side one step
53 further, and making it like edit/unedit in terms of working well if
54 disconnected from the network, and then sending a delayed
57 4. Having separate network turnarounds for the "Notify" request
58 which we do as part of unedit, and for the "release" itself, is slow
62 release (int argc, char **argv)
68 size_t line_allocated = 0;
73 short delete_flag = 0;
78 return release_server (argc, argv);
81 /* Everything from here on is client or local. */
83 usage (release_usage);
85 while ((c = getopt (argc, argv, "+Qdq")) != -1)
92 "-q or -Q must be specified before \"%s\"",
100 usage (release_usage);
107 /* We're going to run "cvs -n -q update" and check its output; if
108 * the output is sufficiently unalarming, then we release with no
109 * questions asked. Else we prompt, then maybe release.
110 * (Well, actually we ask no matter what. Our notion of "sufficiently
111 * unalarming" doesn't take into account "? foo.c" files, so it is
112 * up to the user to take note of them, at least currently
113 * (ignore-193 in testsuite)).
115 /* Construct the update command. Be sure to add authentication and
116 encryption if we are using them currently, else our child process may
117 not be able to communicate with the server. */
118 update_cmd = xmalloc (strlen (program_path)
119 + strlen (current_parsed_root->original)
120 + 1 + 3 + 3 + 16 + 1);
121 sprintf (update_cmd, "%s %s%s-n -q -d %s update",
123 cvsauthenticate ? "-a " : "",
124 cvsencrypt ? "-x " : "",
125 current_parsed_root->original);
127 #ifdef CLIENT_SUPPORT
128 /* Start the server; we'll close it after looping. */
129 if (current_parsed_root->isremote)
134 #endif /* CLIENT_SUPPORT */
136 /* Remember the directory where "cvs release" was invoked because
137 all args are relative to this directory and we chdir around.
144 for (i = arg_start_idx; i < argc; i++)
150 if (CVS_CHDIR (thisarg) < 0)
153 error (0, errno, "can't chdir to: %s", thisarg);
159 error (0, 0, "no repository directory: %s", thisarg);
160 if (restore_cwd (&cwd, NULL))
168 error (0, 0, "no such directory: %s", thisarg);
172 repository = Name_Repository ((char *) NULL, (char *) NULL);
178 /* The "release" command piggybacks on "update", which
179 does the real work of finding out if anything is not
180 up-to-date with the repository. Then "release" prompts
181 the user, telling her how many files have been
182 modified, and asking if she still wants to do the
184 fp = run_popen (update_cmd, "r");
186 error (1, 0, "cannot run command %s", update_cmd);
190 while ((line_length = getline (&line, &line_allocated, fp)) >= 0)
192 if (strchr ("MARCZ", *line))
194 (void) fputs (line, stdout);
196 if (line_length < 0 && !feof (fp))
197 error (0, errno, "cannot read from subprocess");
199 /* If the update exited with an error, then we just want to
200 complain and go on to the next arg. Especially, we do
201 not want to delete the local copy, since it's obviously
202 not what the user thinks it is. */
203 if ((pclose (fp)) != 0)
205 error (0, 0, "unable to release `%s'", thisarg);
207 if (restore_cwd (&cwd, NULL))
212 printf ("You have [%d] altered files in this repository.\n",
214 printf ("Are you sure you want to release %sdirectory `%s': ",
215 delete_flag ? "(and delete) " : "", thisarg);
219 (void) fprintf (stderr, "** `%s' aborted by user choice.\n",
222 if (restore_cwd (&cwd, NULL))
228 /* Note: client.c doesn't like to have other code
229 changing the current directory on it. So a fair amount
230 of effort is needed to make sure it doesn't get confused
231 about the directory and (for example) overwrite
232 CVS/Entries file in the wrong directory. See release-17
233 through release-23. */
236 if (restore_cwd (&cwd, NULL))
240 #ifdef CLIENT_SUPPORT
241 && !(current_parsed_root->isremote
242 && (!supported_request ("noop")
243 || !supported_request ("Notify")))
252 err += unedit (argc, argv);
253 if (restore_cwd (&cwd, NULL))
257 #ifdef CLIENT_SUPPORT
258 if (current_parsed_root->isremote)
260 send_to_server ("Argument ", 0);
261 send_to_server (thisarg, 0);
262 send_to_server ("\012", 1);
263 send_to_server ("release\012", 0);
266 #endif /* CLIENT_SUPPORT */
268 history_write ('F', thisarg, "", thisarg, ""); /* F == Free */
273 /* FIXME? Shouldn't this just delete the CVS-controlled
274 files and, perhaps, the files that would normally be
275 ignored and leave everything else? */
277 if (unlink_file_dir (thisarg) < 0)
278 error (0, errno, "deletion of directory %s failed", thisarg);
281 #ifdef CLIENT_SUPPORT
282 if (current_parsed_root->isremote)
285 * Is there a good reason why get_server_responses() isn't
286 * responsible for restoring its initial directory itself when
289 err += get_server_responses ();
291 if (restore_cwd (&cwd, NULL))
294 #endif /* CLIENT_SUPPORT */
297 if (restore_cwd (&cwd, NULL))
301 #ifdef CLIENT_SUPPORT
302 if (current_parsed_root->isremote)
304 /* Unfortunately, client.c doesn't offer a way to close
305 the connection without waiting for responses. The extra
306 network turnaround here is quite unnecessary other than
308 send_to_server ("noop\012", 0);
309 err += get_responses_and_close ();
311 #endif /* CLIENT_SUPPORT */