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.
13 static const char *const release_usage[] =
15 "Usage: %s %s [-d] directories...\n",
16 "\t-d\tDelete the given directory.\n",
17 "(Specify the --help global option for a list of other help options)\n",
22 static int release_server (int argc, char **argv);
24 /* This is the server side of cvs release. */
26 release_server (int argc, char **argv)
30 /* Note that we skip argv[0]. */
31 for (i = 1; i < argc; ++i)
32 history_write ('F', argv[i], "", argv[i], "");
36 #endif /* SERVER_SUPPORT */
38 /* There are various things to improve about this implementation:
40 1. Using run_popen to run "cvs update" could be replaced by a
41 fairly simple start_recursion/classify_file loop--a win for
42 portability, performance, and cleanliness. In particular, there is
43 no particularly good way to find the right "cvs".
45 2. The fact that "cvs update" contacts the server slows things down;
46 it undermines the case for using "cvs release" rather than "rm -rf".
47 However, for correctly printing "? foo" and correctly handling
48 CVSROOTADM_IGNORE, we currently need to contact the server. (One
49 idea for how to fix this is to stash a copy of CVSROOTADM_IGNORE in
50 the working directories; see comment at base_* in entries.c for a
51 few thoughts on that).
53 3. Would be nice to take processing things on the client side one step
54 further, and making it like edit/unedit in terms of working well if
55 disconnected from the network, and then sending a delayed
58 4. Having separate network turnarounds for the "Notify" request
59 which we do as part of unedit, and for the "release" itself, is slow
63 release (int argc, char **argv)
69 size_t line_allocated = 0;
74 short delete_flag = 0;
79 return release_server (argc, argv);
82 /* Everything from here on is client or local. */
84 usage (release_usage);
86 while ((c = getopt (argc, argv, "+Qdq")) != -1)
93 "-q or -Q must be specified before \"%s\"",
101 usage (release_usage);
108 /* We're going to run "cvs -n -q update" and check its output; if
109 * the output is sufficiently unalarming, then we release with no
110 * questions asked. Else we prompt, then maybe release.
111 * (Well, actually we ask no matter what. Our notion of "sufficiently
112 * unalarming" doesn't take into account "? foo.c" files, so it is
113 * up to the user to take note of them, at least currently
114 * (ignore-193 in testsuite)).
116 /* Construct the update command. Be sure to add authentication and
117 encryption if we are using them currently, else our child process may
118 not be able to communicate with the server. */
119 update_cmd = Xasprintf ("%s %s%s-n -q -d %s update",
121 #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
122 cvsauthenticate ? "-a " : "",
123 cvsencrypt ? "-x " : "",
127 original_parsed_root->original);
129 #ifdef CLIENT_SUPPORT
130 /* Start the server; we'll close it after looping. */
131 if (current_parsed_root->isremote)
136 #endif /* CLIENT_SUPPORT */
138 /* Remember the directory where "cvs release" was invoked because
139 all args are relative to this directory and we chdir around.
142 error (1, errno, "Failed to save current directory.");
146 for (i = arg_start_idx; i < argc; i++)
152 if (CVS_CHDIR (thisarg) < 0)
155 error (0, errno, "can't chdir to: %s", thisarg);
161 error (0, 0, "no repository directory: %s", thisarg);
162 if (restore_cwd (&cwd))
164 "Failed to restore current directory, `%s'.",
172 error (0, 0, "no such directory: %s", thisarg);
176 repository = Name_Repository ((char *) NULL, (char *) NULL);
182 /* The "release" command piggybacks on "update", which
183 does the real work of finding out if anything is not
184 up-to-date with the repository. Then "release" prompts
185 the user, telling her how many files have been
186 modified, and asking if she still wants to do the
188 fp = run_popen (update_cmd, "r");
190 error (1, 0, "cannot run command %s", update_cmd);
194 while ((line_length = getline (&line, &line_allocated, fp)) >= 0)
196 if (strchr ("MARCZ", *line))
198 (void) fputs (line, stdout);
200 if (line_length < 0 && !feof (fp))
201 error (0, errno, "cannot read from subprocess");
203 /* If the update exited with an error, then we just want to
204 complain and go on to the next arg. Especially, we do
205 not want to delete the local copy, since it's obviously
206 not what the user thinks it is. */
207 if ((pclose (fp)) != 0)
209 error (0, 0, "unable to release `%s'", thisarg);
211 if (restore_cwd (&cwd))
213 "Failed to restore current directory, `%s'.",
218 printf ("You have [%d] altered files in this repository.\n",
220 printf ("Are you sure you want to release %sdirectory `%s': ",
221 delete_flag ? "(and delete) " : "", thisarg);
227 (void) fprintf (stderr, "** `%s' aborted by user choice.\n",
230 if (restore_cwd (&cwd))
232 "Failed to restore current directory, `%s'.",
238 /* Note: client.c doesn't like to have other code
239 changing the current directory on it. So a fair amount
240 of effort is needed to make sure it doesn't get confused
241 about the directory and (for example) overwrite
242 CVS/Entries file in the wrong directory. See release-17
243 through release-23. */
246 if (restore_cwd (&cwd))
247 error (1, errno, "Failed to restore current directory, `%s'.",
250 #ifdef CLIENT_SUPPORT
251 if (!current_parsed_root->isremote
252 || (supported_request ("noop") && supported_request ("Notify")))
260 err += unedit (argc, argv);
261 if (restore_cwd (&cwd))
262 error (1, errno, "Failed to restore current directory, `%s'.",
266 #ifdef CLIENT_SUPPORT
267 if (current_parsed_root->isremote)
269 send_to_server ("Argument ", 0);
270 send_to_server (thisarg, 0);
271 send_to_server ("\012", 1);
272 send_to_server ("release\012", 0);
275 #endif /* CLIENT_SUPPORT */
277 history_write ('F', thisarg, "", thisarg, ""); /* F == Free */
282 /* FIXME? Shouldn't this just delete the CVS-controlled
283 files and, perhaps, the files that would normally be
284 ignored and leave everything else? */
286 if (unlink_file_dir (thisarg) < 0)
287 error (0, errno, "deletion of directory %s failed", thisarg);
290 #ifdef CLIENT_SUPPORT
291 if (current_parsed_root->isremote)
294 * Is there a good reason why get_server_responses() isn't
295 * responsible for restoring its initial directory itself when
298 err += get_server_responses ();
300 if (restore_cwd (&cwd))
301 error (1, errno, "Failed to restore current directory, `%s'.",
304 #endif /* CLIENT_SUPPORT */
307 if (restore_cwd (&cwd))
308 error (1, errno, "Failed to restore current directory, `%s'.",
312 #ifdef CLIENT_SUPPORT
313 if (current_parsed_root->isremote)
315 /* Unfortunately, client.c doesn't offer a way to close
316 the connection without waiting for responses. The extra
317 network turnaround here is quite unnecessary other than
319 send_to_server ("noop\012", 0);
320 err += get_responses_and_close ();
322 #endif /* CLIENT_SUPPORT */