Mercurial > hg > xemacs-beta
comparison lisp/pcl-cvs/pcl-cvs.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 49a24b4fd526 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 ;;; | |
2 ;;;#ident "@(#)OrigId: pcl-cvs.el,v 1.93 1993/05/31 22:44:00 ceder Exp " | |
3 ;;; | |
4 ;;;#ident "@(#)cvs/contrib/pcl-cvs:$Name: r19-14 $:$Id: pcl-cvs.el,v 1.1.1.1 1996/12/18 03:32:27 steve Exp $" | |
5 ;;; | |
6 ;;; pcl-cvs.el -- A Front-end to CVS 1.3 or later. | |
7 ;;; Release 1.05-CVS-$Name: r19-14 $. | |
8 ;;; Copyright (C) 1991, 1992, 1993 Per Cederqvist | |
9 | |
10 ;;; This program is free software; you can redistribute it and/or modify | |
11 ;;; it under the terms of the GNU General Public License as published by | |
12 ;;; the Free Software Foundation; either version 2 of the License, or | |
13 ;;; (at your option) any later version. | |
14 ;;; | |
15 ;;; This program is distributed in the hope that it will be useful, | |
16 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 ;;; GNU General Public License for more details. | |
19 ;;; | |
20 ;;; You should have received a copy of the GNU General Public License | |
21 ;;; along with this program; if not, write to the Free Software | |
22 ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
23 | |
24 ;;; See below for installation instructions. | |
25 | |
26 ;;; There is an TeXinfo file that describes this package. You should read it | |
27 ;;; to get the most from this package. | |
28 | |
29 ;;; Mail questions and bug reports regarding this version (as included in | |
30 ;;; CVS-1.7 or newer) to the pcl-cvs support team at <pcl-cvs@cyclic.com>. | |
31 | |
32 ;;; Don't try to use this with CVS 1.2 or earlier. It won't work. Get CVS 1.7 | |
33 ;;; or newer. Use the version of RCS best suited for the version of CVS you're | |
34 ;;; using. | |
35 | |
36 ; (require 'cookie) ; from ELIB-1.0 | |
37 (load "cookie.el") | |
38 (require 'add-log) ; for all the ChangeLog goodies | |
39 | |
40 (provide 'pcl-cvs) | |
41 | |
42 ;;; ------------------------------------------------------- | |
43 ;;; START OF THINGS TO CHECK WHEN INSTALLING | |
44 | |
45 ;; also use $GNU here, since may folks might install CVS as a GNU package | |
46 ;; | |
47 (defvar local-path (cond | |
48 ((getenv "LOCAL") | |
49 (getenv "LOCAL")) | |
50 ((getenv "GNU") | |
51 (getenv "GNU")) | |
52 (t | |
53 "/usr/local")) | |
54 "*Path prefix for most locally installed things.") | |
55 | |
56 ;; this isn't likely to be right all the time.... | |
57 ;; | |
58 (defvar local-gnu-path (cond | |
59 ((getenv "GNU") | |
60 (getenv "GNU")) | |
61 (t | |
62 "/usr/local")) ; or "/usr/gnu"? | |
63 "*Path prefix for locally installed GNU software.") | |
64 | |
65 (defvar cvs-program (concat local-path "/bin/cvs") | |
66 "*Full path to the cvs executable.") | |
67 | |
68 ;; SunOS-4.1.1_U1 has "diff.c 1.12 88/08/04 SMI; from UCB 4.6 86/04/03" | |
69 ;; | |
70 (defvar cvs-diff-program (concat local-gnu-path "/bin/diff") | |
71 "*Full path to the best diff program you've got. | |
72 NOTE: there are some nasty bugs in the context diff variants of some vendor | |
73 versions, such as the one in SunOS-4.1.1_U1") | |
74 | |
75 (defvar cvs-rmdir-program "/bin/rmdir" | |
76 "*Full path to the rmdir program. Typically /bin/rmdir.") | |
77 | |
78 (defvar cvs-shell "/bin/sh" | |
79 "*Full path to a shell that can do redirection on stdout.") | |
80 | |
81 ;;; Options to control various features: | |
82 | |
83 (defvar cvs-changelog-full-paragraphs t | |
84 "If non-nil, include full ChangeLog paragraphs in the CVS log. | |
85 This may be set in the ``local variables'' section of a ChangeLog, to | |
86 indicate the policy for that ChangeLog. | |
87 | |
88 A ChangeLog paragraph is a bunch of log text containing no blank lines; | |
89 a paragraph usually describes a set of changes with a single purpose, | |
90 but perhaps spanning several functions in several files. Changes in | |
91 different paragraphs are unrelated. | |
92 | |
93 You could argue that the CVS log entry for a file should contain the | |
94 full ChangeLog paragraph mentioning the change to the file, even though | |
95 it may mention other files, because that gives you the full context you | |
96 need to understand the change. This is the behaviour you get when this | |
97 variable is set to t. | |
98 | |
99 On the other hand, you could argue that the CVS log entry for a change | |
100 should contain only the text for the changes which occurred in that | |
101 file, because the CVS log is per-file. This is the behaviour you get | |
102 when this variable is set to nil.") | |
103 | |
104 (defvar cvs-cvsroot-required nil | |
105 "*Specifies whether CVS needs to be told where the repository is. | |
106 | |
107 In CVS 1.3, if your CVSROOT environment variable is not set, and you | |
108 do not set the `cvs-cvsroot' lisp variable, CVS will have no idea | |
109 where to find the repository, and refuse to run. CVS 1.4 and later | |
110 store the repository path with the working directories, so most | |
111 operations don't need to be told where the repository is. | |
112 | |
113 If you work with multiple repositories with CVS 1.4, it's probably | |
114 advisable to leave your CVSROOT environment variable unset, set this | |
115 variable to nil, and let CVS figure out where the repository is for | |
116 itself.") | |
117 | |
118 (defvar cvs-cvsroot nil | |
119 "*Specifies where the (current) cvs master repository is. | |
120 Overrides the $CVSROOT variable by sending \" -d dir\" to all cvs commands. | |
121 This switch is useful if you have multiple CVS repositories, and are not using | |
122 a modern version of CVS that stores the current repository in CVS/Root.") | |
123 | |
124 ;; Uncomment the following line if you are running on 18.57 or earlier. | |
125 ;(setq delete-exited-processes nil) | |
126 ;; Emacs version 18.57 and earlier is likely to crash if | |
127 ;; delete-exited-processes is t, since the sentinel uses lots of | |
128 ;; memory, and 18.57 forgets to GCPROT a variable if | |
129 ;; delete-exited-processes is t. | |
130 | |
131 ;;; END OF THINGS TO CHECK WHEN INSTALLING | |
132 ;;; -------------------------------------------------------- | |
133 | |
134 (defconst pcl-cvs-version "1.05-CVS-$Name: r19-14 $" | |
135 "A string denoting the current release version of pcl-cvs.") | |
136 | |
137 ;; You are NOT allowed to disable this message by default. However, you | |
138 ;; are encouraged to inform your users that by adding | |
139 ;; (setq cvs-inhibit-copyright-message t) | |
140 ;; to their .emacs they can get rid of it. Just don't add that line | |
141 ;; to your default.el! | |
142 (defvar cvs-inhibit-copyright-message nil | |
143 "*Non-nil means don't display a Copyright message in the ``*cvs*'' buffer.") | |
144 | |
145 (defconst cvs-startup-message | |
146 (if cvs-inhibit-copyright-message | |
147 "PCL-CVS release 1.05-CVS-$Name: r19-14 $" | |
148 "PCL-CVS release 1.05 from CVS release $Name: r19-14 $. | |
149 Copyright (C) 1992, 1993 Per Cederqvist | |
150 Pcl-cvs comes with absolutely no warranty; for details consult the manual. | |
151 This is free software, and you are welcome to redistribute it under certain | |
152 conditions; again, consult the TeXinfo manual for details.") | |
153 "*Startup message for CVS.") | |
154 | |
155 (defconst pcl-cvs-bugs-address "pcl-cvs-auto-bugs@cyclic.com" | |
156 "The destination address used for the default bug report form.") | |
157 | |
158 (defvar cvs-stdout-file nil | |
159 "Name of the file that holds the output that CVS sends to stdout. | |
160 This variable is buffer local.") | |
161 | |
162 (defvar cvs-lock-file nil | |
163 "Full path to a lock file that CVS is waiting for (or was waiting for).") | |
164 | |
165 (defvar cvs-bakprefix ".#" | |
166 "The prefix that CVS prepends to files when rcsmerge'ing.") | |
167 | |
168 (defvar cvs-erase-input-buffer nil | |
169 "*Non-nil if input buffers should be cleared before asking for new info.") | |
170 | |
171 (defvar cvs-auto-remove-handled nil | |
172 "*Non-nil if cvs-mode-remove-handled should be called automatically. | |
173 If this is set to any non-nil value, entries that do not need to be checked in | |
174 will be removed from the *cvs* buffer after every cvs-mode-commit command.") | |
175 | |
176 (defvar cvs-auto-remove-handled-directories nil | |
177 "*Non-nil if cvs-mode-remove-handled and cvs-update should automatically | |
178 remove empty directories. | |
179 If this is set to any non-nil value, directories that do not contain any files | |
180 to be checked in will be removed from the *cvs* buffer.") | |
181 | |
182 (defvar cvs-sort-ignore-file t | |
183 "*Non-nil if cvs-mode-ignore should sort the .cvsignore automatically.") | |
184 | |
185 (defvar cvs-auto-revert-after-commit t | |
186 "*Non-nil if committed buffers should be automatically reverted.") | |
187 | |
188 (defconst cvs-cursor-column 14 | |
189 "Column to position cursor in in cvs-mode. | |
190 Column 0 is left-most column.") | |
191 | |
192 (defvar cvs-mode-map nil | |
193 "Keymap for the cvs mode.") | |
194 | |
195 (defvar cvs-edit-mode-map nil | |
196 "Keymap for the cvs edit mode (used when editing cvs log messages).") | |
197 | |
198 (defvar cvs-buffer-name "*cvs*" | |
199 "Name of the cvs buffer.") | |
200 | |
201 (defvar cvs-commit-prompt-buffer "*cvs-commit-message*" | |
202 "Name of buffer in which the user is prompted for a log message when | |
203 committing files.") | |
204 | |
205 (defvar cvs-commit-buffer-require-final-newline t | |
206 "*t says silently put a newline at the end of commit log messages. | |
207 Non-nil but not t says ask user whether to add a newline in each such case. | |
208 nil means don't add newlines.") | |
209 | |
210 (defvar cvs-temp-buffer-name "*cvs-tmp*" | |
211 "*Name of the cvs temporary buffer. | |
212 Output from cvs is placed here by synchronous commands.") | |
213 | |
214 (defvar cvs-diff-ignore-marks nil | |
215 "*Non-nil if cvs-diff and cvs-mode-diff-backup should ignore any marked files. | |
216 Normally they run diff on the files that are marked (with cvs-mode-mark), | |
217 or the file under the cursor if no files are marked. If this variable | |
218 is set to a non-nil value they will always run diff on the file on the | |
219 current line.") | |
220 | |
221 ;;; (setq cvs-status-flags '("-v")) | |
222 (defvar cvs-status-flags '("-v") | |
223 "*List of flags to pass to ``cvs status''. Default is \"-v\".") | |
224 | |
225 ;;; (setq cvs-log-flags nil) | |
226 (defvar cvs-log-flags nil | |
227 "*List of flags to pass to ``cvs log''. Default is none.") | |
228 | |
229 ;;; (setq cvs-tag-flags nil) | |
230 (defvar cvs-tag-flags nil | |
231 "*List of extra flags to pass to ``cvs tag''. Default is none.") | |
232 | |
233 ;;; (setq cvs-rtag-flags nil) | |
234 (defvar cvs-rtag-flags nil | |
235 "*List of extra flags to pass to ``cvs rtag''. Default is none.") | |
236 | |
237 ;;; (setq cvs-diff-flags '("-u")) | |
238 (defvar cvs-diff-flags '("-u") | |
239 "*List of flags to use as flags to pass to ``diff'' and ``cvs diff''. | |
240 Used by cvs-mode-diff-cvs and cvs-mode-diff-backup. Default is \"-u\". | |
241 | |
242 Set this to \"-u\" to get a Unidiff format, or \"-c\" to get context diffs.") | |
243 | |
244 ;;; (setq cvs-update-optional-flags nil) | |
245 (defvar cvs-update-optional-flags nil | |
246 "*List of strings to use as optional flags to pass to ``cvs update''. Used | |
247 by cvs-do-update, called by cvs-update, cvs-update-other-window, | |
248 cvs-mode-update-no-prompt, and cvs-examine. Default is none. | |
249 | |
250 For example set this to \"-j VENDOR_PREV_RELEASE -j VENDOR_TOP_RELEASE\" to | |
251 perform an update after a new vendor release has been imported. | |
252 | |
253 To restrict the update to the current working directory, set this to \"-l\".") | |
254 | |
255 (defvar cvs-update-prog-output-skip-regexp "$" | |
256 "*A regexp that matches the end of the output from all cvs update programs. | |
257 That is, output from any programs that are run by CVS (by the flag -u in the | |
258 `modules' file - see cvs(5)) when `cvs update' is performed should terminate | |
259 with a line that this regexp matches. It is enough that some part of the line | |
260 is matched. | |
261 | |
262 The default (a single $) fits programs without output.") | |
263 | |
264 ;;; -------------------------------------------------------- | |
265 ;;; The variables below are used internally by pcl-cvs. You should | |
266 ;;; never change them. | |
267 | |
268 (defvar cvs-buffers-to-delete nil | |
269 "List of temporary buffers that should be discarded as soon as possible. | |
270 Due to a bug in emacs 18.57 the sentinel can't discard them reliably.") | |
271 | |
272 (defvar cvs-update-running nil | |
273 "This is set to nil when no process is running, and to | |
274 the process when a cvs update process is running.") | |
275 | |
276 (defvar cvs-cookie-handle nil | |
277 "Handle for the cookie structure that is displayed in the *cvs* buffer.") | |
278 | |
279 (defvar cvs-commit-list nil | |
280 "Used internally by pcl-cvs.") | |
281 | |
282 ;;; The cvs data structure: | |
283 ;;; | |
284 ;;; When the `cvs update' is ready we parse the output. Every file | |
285 ;;; that is affected in some way is added as a cookie of fileinfo | |
286 ;;; (as defined below). | |
287 ;;; | |
288 | |
289 ;;; cvs-fileinfo | |
290 | |
291 ;;; Constructor: | |
292 | |
293 (defun cvs-create-fileinfo (type | |
294 dir | |
295 file-name | |
296 full-log) | |
297 "Create a fileinfo from all parameters. | |
298 Arguments: TYPE DIR FILE-NAME FULL-LOG. | |
299 A fileinfo is a vector with the following fields: | |
300 | |
301 [0] handled True if this file doesn't require further action. | |
302 [1] marked t/nil | |
303 [2] type One of | |
304 UPDATED - file copied from repository | |
305 PATCHED - file update with patch from repository | |
306 MODIFIED - modified by you, unchanged in | |
307 repository | |
308 ADDED - added by you, not yet committed | |
309 REMOVED - removed by you, not yet committed | |
310 CVS-REMOVED- removed, since file no longer exists | |
311 in the repository. | |
312 MERGED - successful merge | |
313 CONFLICT - conflict when merging (if pcl-cvs did it) | |
314 REM-CONFLICT-removed in repository, but altered | |
315 locally. | |
316 MOD-CONFLICT-removed locally, changed in repository. | |
317 REM-EXIST - removed locally, but still exists. | |
318 DIRCHANGE - A change of directory. | |
319 UNKNOWN - An unknown file. | |
320 UNKNOWN-DIR- An unknown directory. | |
321 MOVE-AWAY - A file that is in the way. | |
322 REPOS-MISSING- The directory has vanished from the | |
323 repository. | |
324 MESSAGE - This is a special fileinfo that is used | |
325 to display a text that should be in | |
326 full-log. | |
327 [3] dir Directory the file resides in. Should not end with slash. | |
328 [4] file-name The file name. | |
329 [5] backup-file The name of a backup file created during a merge. | |
330 Only valid for MERGED and CONFLICT files. | |
331 [6] base-revision The revision that the working file was based on. | |
332 Only valid for MERGED and CONFLICT files. | |
333 [7] head-revision The revision that the newly merged changes came from | |
334 Only valid for MERGED and CONFLICT files. | |
335 [8] backup-revision The revision of the cvs backup file (original working rev.) | |
336 Only valid for MERGED and CONFLICT files. | |
337 [9] cvs-diff-buffer A buffer that contains a 'cvs diff file'. | |
338 [10] vendor-diff-buffer A buffer that contains a 'diff base-file head-file'. | |
339 [11] backup-diff-buffer A buffer that contains a 'diff file backup-file'. | |
340 [12] full-log The output from cvs, unparsed. | |
341 [13] mod-time Modification time of file used for *-diff-buffer." | |
342 | |
343 (cons | |
344 'CVS-FILEINFO | |
345 (vector nil nil type dir file-name nil nil nil nil nil nil nil full-log nil nil))) | |
346 | |
347 ;;; Selectors: | |
348 | |
349 (defun cvs-fileinfo->handled (cvs-fileinfo) | |
350 "Get the `handled' field from CVS-FILEINFO." | |
351 (elt (cdr cvs-fileinfo) 0)) | |
352 | |
353 (defun cvs-fileinfo->marked (cvs-fileinfo) | |
354 "Check if CVS-FILEINFO is marked." | |
355 (elt (cdr cvs-fileinfo) 1)) | |
356 | |
357 (defun cvs-fileinfo->type (cvs-fileinfo) | |
358 "Get type from CVS-FILEINFO. | |
359 Type is one of UPDATED, PATCHED, MODIFIED, ADDED, REMOVED, CVS-REMOVED, MERGED, | |
360 CONFLICT, REM-CONFLICT, MOD-CONFLICT, REM-EXIST, DIRCHANGE, UNKNOWN, | |
361 UNKNOWN-DIR, MOVE-AWAY, REPOS-MISSING or MESSAGE." | |
362 (elt (cdr cvs-fileinfo) 2)) | |
363 | |
364 (defun cvs-fileinfo->dir (cvs-fileinfo) | |
365 "Get dir from CVS-FILEINFO. | |
366 The directory name does not end with a slash." | |
367 (elt (cdr cvs-fileinfo) 3)) | |
368 | |
369 (defun cvs-fileinfo->file-name (cvs-fileinfo) | |
370 "Get file-name from CVS-FILEINFO." | |
371 (elt (cdr cvs-fileinfo) 4)) | |
372 | |
373 (defun cvs-fileinfo->backup-file (cvs-fileinfo) | |
374 "Get backup-file from CVS-FILEINFO." | |
375 (elt (cdr cvs-fileinfo) 5)) | |
376 | |
377 (defun cvs-fileinfo->base-revision (cvs-fileinfo) | |
378 "Get the base revision from CVS-FILEINFO." | |
379 (elt (cdr cvs-fileinfo) 6)) | |
380 | |
381 (defun cvs-fileinfo->head-revision (cvs-fileinfo) | |
382 "Get the head revision from CVS-FILEINFO." | |
383 (elt (cdr cvs-fileinfo) 7)) | |
384 | |
385 (defun cvs-fileinfo->backup-revision (cvs-fileinfo) | |
386 "Get the backup revision from CVS-FILEINFO." | |
387 (elt (cdr cvs-fileinfo) 8)) | |
388 | |
389 (defun cvs-fileinfo->cvs-diff-buffer (cvs-fileinfo) | |
390 "Get cvs-diff-buffer from CVS-FILEINFO." | |
391 (elt (cdr cvs-fileinfo) 9)) | |
392 | |
393 (defun cvs-fileinfo->vendor-diff-buffer (cvs-fileinfo) | |
394 "Get backup-diff-buffer from CVS-FILEINFO." | |
395 (elt (cdr cvs-fileinfo) 10)) | |
396 | |
397 (defun cvs-fileinfo->backup-diff-buffer (cvs-fileinfo) | |
398 "Get backup-diff-buffer from CVS-FILEINFO." | |
399 (elt (cdr cvs-fileinfo) 11)) | |
400 | |
401 (defun cvs-fileinfo->full-log (cvs-fileinfo) | |
402 "Get full-log from CVS-FILEINFO." | |
403 (elt (cdr cvs-fileinfo) 12)) | |
404 | |
405 (defun cvs-fileinfo->mod-time (cvs-fileinfo) | |
406 "Get mod-time from CVS-FILEINFO." | |
407 (elt (cdr cvs-fileinfo) 13)) | |
408 | |
409 ;;; Modifiers: | |
410 | |
411 (defun cvs-set-fileinfo->handled (cvs-fileinfo newval) | |
412 "Set handled in CVS-FILEINFO to NEWVAL." | |
413 (aset (cdr cvs-fileinfo) 0 newval)) | |
414 | |
415 (defun cvs-set-fileinfo->marked (cvs-fileinfo newval) | |
416 "Set marked in CVS-FILEINFO to NEWVAL." | |
417 (aset (cdr cvs-fileinfo) 1 newval)) | |
418 | |
419 (defun cvs-set-fileinfo->type (cvs-fileinfo newval) | |
420 "Set type in CVS-FILEINFO to NEWVAL." | |
421 (aset (cdr cvs-fileinfo) 2 newval)) | |
422 | |
423 (defun cvs-set-fileinfo->dir (cvs-fileinfo newval) | |
424 "Set dir in CVS-FILEINFO to NEWVAL. | |
425 The directory should now end with a slash." | |
426 (aset (cdr cvs-fileinfo) 3 newval)) | |
427 | |
428 (defun cvs-set-fileinfo->file-name (cvs-fileinfo newval) | |
429 "Set file-name in CVS-FILEINFO to NEWVAL." | |
430 (aset (cdr cvs-fileinfo) 4 newval)) | |
431 | |
432 (defun cvs-set-fileinfo->backup-file (cvs-fileinfo newval) | |
433 "Set backup-file in CVS-FILEINFO to NEWVAL." | |
434 (aset (cdr cvs-fileinfo) 5 newval)) | |
435 | |
436 (defun cvs-set-fileinfo->base-revision (cvs-fileinfo newval) | |
437 "Set base-revision in CVS-FILEINFO to NEWVAL." | |
438 (aset (cdr cvs-fileinfo) 6 newval)) | |
439 | |
440 (defun cvs-set-fileinfo->head-revision (cvs-fileinfo newval) | |
441 "Set head-revision in CVS-FILEINFO to NEWVAL." | |
442 (aset (cdr cvs-fileinfo) 7 newval)) | |
443 | |
444 (defun cvs-set-fileinfo->backup-revision (cvs-fileinfo newval) | |
445 "Set backup-revision in CVS-FILEINFO to NEWVAL." | |
446 (aset (cdr cvs-fileinfo) 8 newval)) | |
447 | |
448 (defun cvs-set-fileinfo->cvs-diff-buffer (cvs-fileinfo newval) | |
449 "Set cvs-diff-buffer in CVS-FILEINFO to NEWVAL." | |
450 (aset (cdr cvs-fileinfo) 9 newval)) | |
451 | |
452 (defun cvs-set-fileinfo->vendor-diff-buffer (cvs-fileinfo newval) | |
453 "Set vendor-diff-buffer in CVS-FILEINFO to NEWVAL." | |
454 (aset (cdr cvs-fileinfo) 10 newval)) | |
455 | |
456 (defun cvs-set-fileinfo->backup-diff-buffer (cvs-fileinfo newval) | |
457 "Set backup-diff-buffer in CVS-FILEINFO to NEWVAL." | |
458 (aset (cdr cvs-fileinfo) 11 newval)) | |
459 | |
460 (defun cvs-set-fileinfo->full-log (cvs-fileinfo newval) | |
461 "Set full-log in CVS-FILEINFO to NEWVAL." | |
462 (aset (cdr cvs-fileinfo) 12 newval)) | |
463 | |
464 (defun cvs-set-fileinfo->mod-time (cvs-fileinfo newval) | |
465 "Set full-log in CVS-FILEINFO to NEWVAL." | |
466 (aset (cdr cvs-fileinfo) 13 newval)) | |
467 | |
468 ;;; Predicate: | |
469 | |
470 (defun cvs-fileinfo-p (object) | |
471 "Return t if OBJECT is a cvs-fileinfo." | |
472 (eq (car-safe object) 'CVS-FILEINFO)) | |
473 | |
474 ;;;; End of types. | |
475 | |
476 ;;---------- | |
477 (defun cvs-use-temp-buffer () | |
478 "Display a temporary buffer in another window and select it. | |
479 The selected window will not be changed. The temporary buffer will | |
480 be erased and writable." | |
481 | |
482 (let ((dir default-directory)) | |
483 (display-buffer (get-buffer-create cvs-temp-buffer-name)) | |
484 (set-buffer cvs-temp-buffer-name) | |
485 (setq buffer-read-only nil) | |
486 (setq default-directory dir) | |
487 (erase-buffer))) | |
488 | |
489 ;;---------- | |
490 (defun cvs-examine (directory &optional local) | |
491 "Run a 'cvs -n update' in the current working directory. | |
492 That is, check what needs to be done, but don't change the disc. | |
493 Feed the output to a *cvs* buffer and run cvs-mode on it. | |
494 If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run. | |
495 WARNING: this doesn't work very well yet...." | |
496 | |
497 ;; TODO: this should do everything cvs-update does... | |
498 ;; for example, for CONFLICT files, it should setup fileinfo appropriately | |
499 | |
500 (interactive (list (read-file-name "CVS Update (directory): " | |
501 nil default-directory nil) | |
502 current-prefix-arg)) | |
503 (cvs-do-update directory local 'noupdate)) | |
504 | |
505 ;;---------- | |
506 ;;;###autoload | |
507 (defun cvs-update (directory &optional local) | |
508 "Run a 'cvs update' in the current working directory. Feed the | |
509 output to a *cvs* buffer and run cvs-mode on it. | |
510 If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run." | |
511 | |
512 (interactive (list (read-file-name "CVS Update (directory): " | |
513 nil default-directory nil) | |
514 current-prefix-arg)) | |
515 (cvs-do-update directory local nil) | |
516 (switch-to-buffer cvs-buffer-name)) | |
517 | |
518 ;;---------- | |
519 ;;;###autoload | |
520 (defun cvs-update-other-window (directory &optional local) | |
521 "Run a 'cvs update' in the current working directory. Feed the | |
522 output to a *cvs* buffer, display it in the other window, and run | |
523 cvs-mode on it. | |
524 | |
525 If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run." | |
526 | |
527 (interactive (list (read-file-name "CVS Update other window (directory): " | |
528 nil default-directory nil) | |
529 current-prefix-arg)) | |
530 (cvs-do-update directory local nil) | |
531 (switch-to-buffer-other-window cvs-buffer-name)) | |
532 | |
533 ;;---------- | |
534 (defun cvs-filter (predicate list &rest extra-args) | |
535 "Apply PREDICATE to each element on LIST. | |
536 Args: PREDICATE LIST &rest EXTRA-ARGS. | |
537 | |
538 Return a new list consisting of those elements that PREDICATE | |
539 returns non-nil for. | |
540 | |
541 If more than two arguments are given the remaining args are | |
542 passed to PREDICATE." | |
543 | |
544 ;; Avoid recursion - this should work for LONG lists also! | |
545 (let* ((head (cons 'dummy-header nil)) | |
546 (tail head)) | |
547 (while list | |
548 (if (apply predicate (car list) extra-args) | |
549 (setq tail (setcdr tail (list (car list))))) | |
550 (setq list (cdr list))) | |
551 (cdr head))) | |
552 | |
553 ;;---------- | |
554 (defun cvs-mode-update-no-prompt () | |
555 "Run cvs update in current directory." | |
556 | |
557 (interactive) | |
558 (cvs-do-update default-directory nil nil)) | |
559 | |
560 ;;---------- | |
561 (defun cvs-do-update (directory local dont-change-disc) | |
562 "Do a 'cvs update' in DIRECTORY. | |
563 Args: DIRECTORY LOCAL DONT-CHANGE-DISC. | |
564 | |
565 If LOCAL is non-nil 'cvs update -l' is executed. | |
566 If DONT-CHANGE-DISC is non-nil 'cvs -n update' is executed. | |
567 Both LOCAL and DONT-CHANGE-DISC may be non-nil simultaneously. | |
568 | |
569 *Note*: DONT-CHANGE-DISC does not yet work. The parser gets confused." | |
570 | |
571 (save-some-buffers) | |
572 ;; Ensure that it is safe to do an update. If not, ask user | |
573 ;; for confirmation. | |
574 (if (and (boundp 'cvs-cookie-handle) (collection-buffer cvs-cookie-handle)) | |
575 (if (collection-collect-tin | |
576 cvs-cookie-handle | |
577 '(lambda (cookie) (eq (cvs-fileinfo->type cookie) 'CONFLICT))) | |
578 (if (not | |
579 (yes-or-no-p | |
580 "Only update if conflicts have been resolved. Continue? ")) | |
581 (error "Update aborted by user request.")))) | |
582 (if (not (file-exists-p cvs-program)) | |
583 (error "%s: file not found (check setting of cvs-program)" | |
584 cvs-program)) | |
585 (let* ((this-dir (file-name-as-directory (expand-file-name directory))) | |
586 (update-buffer (generate-new-buffer | |
587 (concat " " (file-name-nondirectory | |
588 (substring this-dir 0 -1)) | |
589 "-update"))) | |
590 (temp-name (make-temp-name | |
591 (concat (file-name-as-directory | |
592 (or (getenv "TMPDIR") "/tmp")) | |
593 "pcl-cvs."))) | |
594 (args nil)) | |
595 | |
596 ;; Check that this-dir exists and is a directory that is under CVS contr. | |
597 | |
598 (if (not (file-directory-p this-dir)) | |
599 (error "%s is not a directory." this-dir)) | |
600 (if (not (file-directory-p (concat this-dir "CVS"))) | |
601 (error "%s does not contain CVS controlled files." this-dir)) | |
602 (if (file-readable-p (concat this-dir "CVS/Root")) | |
603 (save-excursion ; read CVS/Root into cvs-cvsroot | |
604 (find-file (concat this-dir "CVS/Root")) | |
605 (goto-char (point-min)) | |
606 (setq cvs-cvsroot (buffer-substring (point) | |
607 (progn (end-of-line) (point)))) | |
608 (if (not cvs-cvsroot) | |
609 (error "Invalid contents of %sCVS/Root" this-dir)) | |
610 (kill-buffer (current-buffer))) | |
611 (if (and cvs-cvsroot-required | |
612 (not (or (getenv "CVSROOT") cvs-cvsroot))) | |
613 (error "Both cvs-cvsroot and environment variable CVSROOT are unset, and no CVS/Root."))) | |
614 | |
615 ;; Check that at most one `cvs update' is run at any time. | |
616 | |
617 (if (and cvs-update-running (process-status cvs-update-running) | |
618 (or (eq (process-status cvs-update-running) 'run) | |
619 (eq (process-status cvs-update-running) 'stop))) | |
620 (error "Can't run two `cvs update' simultaneously.")) | |
621 | |
622 (if (not (listp cvs-update-optional-flags)) | |
623 (error "cvs-update-optional-flags should be set using cvs-set-update-optional-flags")) | |
624 | |
625 ;; Generate "-d /master -n update -l". | |
626 (setq args (concat (if cvs-cvsroot (concat " -d " cvs-cvsroot)) | |
627 (if dont-change-disc " -n ") | |
628 " update " | |
629 (if local " -l ") | |
630 (if cvs-update-optional-flags | |
631 (mapconcat 'identity | |
632 (copy-sequence cvs-update-optional-flags) | |
633 " ")))) | |
634 | |
635 ;; Set up the buffer that receives the stderr output from "cvs update". | |
636 (set-buffer update-buffer) | |
637 (setq default-directory this-dir) | |
638 (make-local-variable 'cvs-stdout-file) | |
639 (setq cvs-stdout-file temp-name) | |
640 | |
641 (setq cvs-update-running | |
642 (let ((process-connection-type nil)) ; Use a pipe, not a pty. | |
643 (start-process "cvs" update-buffer cvs-shell "-c" | |
644 (concat cvs-program " " args " > " temp-name)))) | |
645 | |
646 (setq mode-line-process | |
647 (concat ": " | |
648 (symbol-name (process-status cvs-update-running)))) | |
649 ; XEmacs change | |
650 (redraw-modeline) ; Update the mode line. | |
651 (set-process-sentinel cvs-update-running 'cvs-sentinel) | |
652 (set-process-filter cvs-update-running 'cvs-update-filter) | |
653 (set-marker (process-mark cvs-update-running) (point-min)) | |
654 | |
655 (save-excursion | |
656 (set-buffer (get-buffer-create cvs-buffer-name)) | |
657 (setq buffer-read-only nil) | |
658 (erase-buffer) | |
659 (cvs-mode)) | |
660 | |
661 (setq cvs-cookie-handle | |
662 (collection-create | |
663 cvs-buffer-name 'cvs-pp | |
664 cvs-startup-message ;See comment above cvs-startup-message. | |
665 "---------- End -----")) | |
666 | |
667 (cookie-enter-first | |
668 cvs-cookie-handle | |
669 (cvs-create-fileinfo | |
670 'MESSAGE nil nil (concat "\n Running `cvs " args "' in " this-dir | |
671 "...\n"))) | |
672 | |
673 (save-excursion | |
674 (set-buffer cvs-buffer-name) | |
675 (setq mode-line-process | |
676 (concat ": " | |
677 (symbol-name (process-status cvs-update-running)))) | |
678 ; XEmacs change | |
679 (redraw-modeline) ; Update the mode line. | |
680 (setq buffer-read-only t)) | |
681 | |
682 ;; Work around a bug in emacs 18.57 and earlier. | |
683 (setq cvs-buffers-to-delete | |
684 (cvs-delete-unused-temporary-buffers cvs-buffers-to-delete))) | |
685 | |
686 ;; The following line is said to improve display updates on some | |
687 ;; emacses. It shouldn't be needed, but it does no harm. | |
688 (sit-for 0)) | |
689 | |
690 ;;---------- | |
691 (defun cvs-delete-unused-temporary-buffers (list) | |
692 "Delete all buffers on LIST that is not visible. | |
693 Return a list of all buffers that still is alive." | |
694 | |
695 (cond | |
696 ((null list) nil) | |
697 ((get-buffer-window (car list)) | |
698 (cons (car list) | |
699 (cvs-delete-unused-temporary-buffers (cdr list)))) | |
700 (t | |
701 (kill-buffer (car list)) | |
702 (cvs-delete-unused-temporary-buffers (cdr list))))) | |
703 | |
704 ;;---------- | |
705 (put 'cvs-mode 'mode-class 'special) | |
706 | |
707 ;;---------- | |
708 (defun cvs-mode () | |
709 "\\<cvs-mode-map>Mode used for pcl-cvs, a front-end to CVS. | |
710 | |
711 To get to the \"*cvs*\" buffer you should use ``\\[execute-extended-command] cvs-update''. | |
712 | |
713 Full documentation is in the Texinfo file. Here are the most useful commands: | |
714 | |
715 \\[cvs-mode-previous-line] Move up. \\[cvs-mode-next-line] Move down. | |
716 \\[cvs-mode-commit] Commit file. \\[cvs-mode-update-no-prompt] Re-update directory. | |
717 \\[cvs-mode-mark] Mark file/dir. \\[cvs-mode-unmark] Unmark file/dir. | |
718 \\[cvs-mode-mark-all-files] Mark all files. \\[cvs-mode-unmark-all-files] Unmark all files. | |
719 \\[cvs-mode-find-file] Edit file/run Dired. \\[cvs-mode-find-file-other-window] Find file or run Dired in other window. | |
720 \\[cvs-mode-ignore] Add file to ./.cvsignore. \\[cvs-mode-add-change-log-entry-other-window] Write ChangeLog in other window. | |
721 \\[cvs-mode-add] Add to repository. \\[cvs-mode-remove-file] Remove file. | |
722 \\[cvs-mode-diff-cvs] Diff with base revision. \\[cvs-mode-diff-backup] Diff backup file. | |
723 \\[cvs-mode-ediff] Ediff base rev & backup. \\[cvs-mode-diff-vendor] Show merge from vendor branch. | |
724 \\[cvs-mode-emerge] Emerge base rev & backup. \\[cvs-mode-diff-backup] Diff backup file. | |
725 \\[cvs-mode-acknowledge] Delete line from buffer. \\[cvs-mode-remove-handled] Remove processed entries. | |
726 \\[cvs-mode-log] Run ``cvs log''. \\[cvs-mode-status] Run ``cvs status''. | |
727 \\[cvs-mode-tag] Run ``cvs tag''. \\[cvs-mode-rtag] Run ``cvs rtag''. | |
728 \\[cvs-mode-changelog-commit] Like \\[cvs-mode-commit], but get default log text from ChangeLog. | |
729 \\[cvs-mode-undo-local-changes] Revert the last checked in version - discard your changes to the file. | |
730 | |
731 Entry to this mode runs cvs-mode-hook. | |
732 This description is updated for release 1.05-CVS-$Name: r19-14 $ of pcl-cvs. | |
733 | |
734 All bindings: | |
735 \\{cvs-mode-map}" | |
736 | |
737 (interactive) | |
738 (setq major-mode 'cvs-mode) | |
739 (setq mode-name "CVS") | |
740 (setq mode-line-process nil) | |
741 ;; for older v18 emacs | |
742 ;;(buffer-flush-undo (current-buffer)) | |
743 (buffer-disable-undo (current-buffer)) | |
744 (make-local-variable 'goal-column) | |
745 (setq goal-column cvs-cursor-column) | |
746 (use-local-map cvs-mode-map) | |
747 (run-hooks 'cvs-mode-hook)) | |
748 | |
749 ;;---------- | |
750 (defun cvs-sentinel (proc msg) | |
751 "Sentinel for the cvs update process. | |
752 This is responsible for parsing the output from the cvs update when | |
753 it is finished." | |
754 | |
755 (cond | |
756 ((null (buffer-name (process-buffer proc))) | |
757 ;; buffer killed | |
758 (set-process-buffer proc nil)) | |
759 ((memq (process-status proc) '(signal exit)) | |
760 (let* ((obuf (current-buffer)) | |
761 (omax (point-max)) | |
762 (opoint (point))) | |
763 ;; save-excursion isn't the right thing if | |
764 ;; process-buffer is current-buffer | |
765 (unwind-protect | |
766 (progn | |
767 (set-buffer (process-buffer proc)) | |
768 (setq mode-line-process | |
769 (concat ": " | |
770 (symbol-name (process-status proc)))) | |
771 (let* ((out-file cvs-stdout-file) | |
772 (stdout-buffer (find-file-noselect out-file))) | |
773 (save-excursion | |
774 (set-buffer stdout-buffer) | |
775 (rename-buffer (concat " " | |
776 (file-name-nondirectory out-file)) t)) | |
777 (cvs-parse-update stdout-buffer (process-buffer proc)) | |
778 (setq cvs-buffers-to-delete | |
779 (cons (process-buffer proc) | |
780 (cons stdout-buffer | |
781 cvs-buffers-to-delete))) | |
782 (delete-file out-file))) | |
783 (message "CVS update process has completed.") ; XEmacs | |
784 (set-buffer-modified-p (buffer-modified-p)) | |
785 (setq cvs-update-running nil)) | |
786 (if (equal obuf (process-buffer proc)) | |
787 nil | |
788 (set-buffer (process-buffer proc)) | |
789 (if (< opoint omax) | |
790 (goto-char opoint)) | |
791 (set-buffer obuf)))))) | |
792 | |
793 ;;---------- | |
794 (defun cvs-update-filter (proc string) | |
795 "Filter function for pcl-cvs. | |
796 This function gets the output that CVS sends to stderr. It inserts it | |
797 into (process-buffer proc) but it also checks if CVS is waiting for a | |
798 lock file. If so, it inserts a message cookie in the *cvs* buffer." | |
799 | |
800 (let ((old-buffer (current-buffer)) | |
801 (data (match-data))) | |
802 (unwind-protect | |
803 (progn | |
804 (set-buffer (process-buffer proc)) | |
805 (save-excursion | |
806 ;; Insert the text, moving the process-marker. | |
807 (goto-char (process-mark proc)) | |
808 (insert string) | |
809 (set-marker (process-mark proc) (point)) | |
810 ;; Delete any old lock message | |
811 (if (tin-nth cvs-cookie-handle 1) | |
812 (tin-delete cvs-cookie-handle | |
813 (tin-nth cvs-cookie-handle 1))) | |
814 ;; Check if CVS is waiting for a lock. | |
815 (beginning-of-line 0) ;Move to beginning of last | |
816 ;complete line. | |
817 (cond | |
818 ((looking-at | |
819 "^cvs \\(update\\|server\\): \\[..:..:..\\] waiting for \\(.*\\)lock in \\(.*\\)$") | |
820 (setq cvs-lock-file (buffer-substring (match-beginning 3) | |
821 (match-end 3))) | |
822 (cookie-enter-last | |
823 cvs-cookie-handle | |
824 (cvs-create-fileinfo | |
825 'MESSAGE nil nil | |
826 (concat "\tWaiting for " | |
827 (buffer-substring (match-beginning 2) | |
828 (match-end 2)) | |
829 "lock in " cvs-lock-file | |
830 ".\n\t (type M-x cvs-delete-lock to delete it)"))))))) | |
831 (store-match-data data) | |
832 (set-buffer old-buffer)))) | |
833 | |
834 ;;---------- | |
835 (defun cvs-delete-lock () | |
836 "Delete the lock file that CVS is waiting for. | |
837 Note that this can be dangerous. You should only do this | |
838 if you are convinced that the process that created the lock is dead." | |
839 | |
840 (interactive) | |
841 (cond | |
842 ((not (or (file-exists-p | |
843 (concat (file-name-as-directory cvs-lock-file) "#cvs.lock")) | |
844 (cvs-filter (function cvs-lock-file-p) | |
845 (directory-files cvs-lock-file)))) | |
846 (error "No lock files found.")) | |
847 ((yes-or-no-p (concat "Really delete locks in " cvs-lock-file "? ")) | |
848 ;; Re-read the directory -- the locks might have disappeared. | |
849 (let ((locks (cvs-filter (function cvs-lock-file-p) | |
850 (directory-files cvs-lock-file)))) | |
851 (while locks | |
852 (delete-file (concat (file-name-as-directory cvs-lock-file) | |
853 (car locks))) | |
854 (setq locks (cdr locks))) | |
855 (cvs-remove-directory | |
856 (concat (file-name-as-directory cvs-lock-file) "#cvs.lock")))))) | |
857 | |
858 ;;---------- | |
859 (defun cvs-remove-directory (dir) | |
860 "Remove a directory." | |
861 | |
862 (if (file-directory-p dir) | |
863 (call-process cvs-rmdir-program nil nil nil dir) | |
864 (error "Not a directory: %s" dir)) | |
865 (if (file-exists-p dir) | |
866 (error "Could not remove directory %s" dir))) | |
867 | |
868 ;;---------- | |
869 (defun cvs-lock-file-p (file) | |
870 "Return true if FILE looks like a CVS lock file." | |
871 | |
872 (or | |
873 (string-match "^#cvs.tfl.[0-9]+$" file) | |
874 (string-match "^#cvs.rfl.[0-9]+$" file) | |
875 (string-match "^#cvs.wfl.[0-9]+$" file))) | |
876 | |
877 ;;---------- | |
878 (defun cvs-quote-multiword-string (str) | |
879 "Return STR surrounded in single quotes if it contains whitespace." | |
880 (cond ((string-match "[ \t\n]" str) | |
881 (concat "'" str "'")) | |
882 (t | |
883 str))) | |
884 | |
885 ;;---------- | |
886 ;; this should be in subr.el or some similar place.... | |
887 (defun parse-string (str &optional regexp) | |
888 "Explode the string STR into a list of words ala strtok(3). Optional REGEXP | |
889 defines regexp matching word separator, which defaults to \"[ \\t\\n]+\"." | |
890 (let (str-list ; new list | |
891 str-token ; "index" of next token | |
892 (str-start 0) ; "index" of current token | |
893 (str-sep (if regexp | |
894 regexp | |
895 "[ \t\n]+"))) | |
896 (while (setq str-token (string-match str-sep str str-start)) | |
897 (setq str-list | |
898 (nconc str-list | |
899 (list (substring str str-start str-token)))) | |
900 (setq str-start (match-end 0))) | |
901 ;; tag on the remainder as the final item | |
902 (if (not (>= str-start (length str))) | |
903 (setq str-list | |
904 (nconc str-list | |
905 (list (substring str str-start))))) | |
906 str-list)) | |
907 | |
908 ;;---------- | |
909 (defun cvs-make-list (str) | |
910 "Return list of words made from the string STR." | |
911 (cond ((string-match "[ \t\n]+" str) | |
912 (let ((new-str (parse-string str "[ \t\n]+"))) | |
913 ;; this is ugly, but assume if the first element is empty, there are | |
914 ;; no more elements. | |
915 (cond ((string= (car new-str) "") | |
916 nil) | |
917 (t | |
918 new-str)))) | |
919 ((string= str "") | |
920 nil) | |
921 (t | |
922 (list str)))) | |
923 | |
924 ;;---------- | |
925 (defun cvs-skip-line (stdout stderr regexp &optional arg) | |
926 "Like forward-line, but check that the skipped line matches REGEXP. | |
927 Args: STDOUT STDERR REGEXP &optional ARG. | |
928 | |
929 If it doesn't match REGEXP a bug report is generated and displayed. | |
930 STDOUT and STDERR is only used to do that. | |
931 | |
932 If optional ARG, a number, is given the ARGth parenthesized expression | |
933 in the REGEXP is returned as a string. | |
934 Point should be in column 1 when this function is called." | |
935 | |
936 (cond | |
937 ((looking-at regexp) | |
938 (forward-line 1) | |
939 (if arg | |
940 (buffer-substring (match-beginning arg) | |
941 (match-end arg)))) | |
942 (t | |
943 (cvs-parse-error stdout | |
944 stderr | |
945 (if (eq (current-buffer) stdout) | |
946 'STDOUT | |
947 'STDERR) | |
948 (point) | |
949 regexp)))) | |
950 | |
951 ;;---------- | |
952 (defun cvs-get-current-dir (root-dir dirname) | |
953 "Return current working directory, suitable for cvs-parse-update. | |
954 Args: ROOT-DIR DIRNAME. | |
955 | |
956 Concatenates ROOT-DIR and DIRNAME to form an absolute path." | |
957 | |
958 (if (string= "." dirname) | |
959 (substring root-dir 0 -1) | |
960 (concat root-dir dirname))) | |
961 | |
962 ;;---------- | |
963 (defun cvs-compare-fileinfos (a b) | |
964 "Compare fileinfo A with fileinfo B and return t if A is `less'." | |
965 | |
966 (cond | |
967 ;; Sort acording to directories. | |
968 ((string< (cvs-fileinfo->dir a) (cvs-fileinfo->dir b)) t) | |
969 ((not (string= (cvs-fileinfo->dir a) (cvs-fileinfo->dir b))) nil) | |
970 ;; The DIRCHANGE entry is always first within the directory. | |
971 ((and (eq (cvs-fileinfo->type a) 'DIRCHANGE) | |
972 (not (eq (cvs-fileinfo->type b) 'DIRCHANGE))) t) | |
973 ((and (eq (cvs-fileinfo->type b) 'DIRCHANGE) | |
974 (not (eq (cvs-fileinfo->type a) 'DIRCHANGE))) nil) | |
975 ;; All files are sorted by file name. | |
976 ((string< (cvs-fileinfo->file-name a) (cvs-fileinfo->file-name b))))) | |
977 | |
978 ;;---------- | |
979 (defun cvs-parse-error (stdout-buffer stderr-buffer err-buf pos &optional indicator) | |
980 "Handle a parse error when parsing the output from cvs. | |
981 Args: STDOUT-BUFFER STDERR-BUFFER ERR-BUF POS &optional INDICATOR. | |
982 | |
983 ERR-BUF should be 'STDOUT or 'STDERR." | |
984 | |
985 (setq pos (1- pos)) | |
986 (set-buffer cvs-buffer-name) | |
987 (setq buffer-read-only nil) | |
988 (erase-buffer) | |
989 (insert "To: " pcl-cvs-bugs-address "\n") | |
990 (insert "Subject: pcl-cvs release" pcl-cvs-version " parse error.\n") | |
991 (insert (concat mail-header-separator "\n")) | |
992 (insert "This bug report is automatically generated by pcl-cvs\n") | |
993 (insert "because it doesn't understand some output from CVS. Below\n") | |
994 (insert "is detailed information about the error. Please send\n") | |
995 (insert "this, together with any information you think might be\n") | |
996 (insert "useful for me to fix the bug, to the address above. But\n") | |
997 (insert "please check the \"known problems\" section of the\n") | |
998 (insert "documentation first. Note that this buffer contains\n") | |
999 (insert "information that you might consider confidential. You\n") | |
1000 (insert "are encouraged to read through it before sending it.\n") | |
1001 (insert "\n") | |
1002 (insert "Press C-c C-c to send this email.\n\n") | |
1003 (insert "Please state the version of these programs you are using:\n\n") | |
1004 (insert "RCS: \ndiff: \n\n") | |
1005 | |
1006 (let* ((stdout (save-excursion (set-buffer stdout-buffer) (buffer-string))) | |
1007 (stderr (save-excursion (set-buffer stderr-buffer) (buffer-string))) | |
1008 (errstr (if (eq err-buf 'STDOUT) stdout stderr)) | |
1009 (errline-end (string-match "\n" errstr pos)) | |
1010 (errline (substring errstr pos errline-end))) | |
1011 (insert (format "Offending line (%d chars): >" (- errline-end pos))) | |
1012 (insert errline) | |
1013 (insert "<\n") | |
1014 (insert "Sent to " (symbol-name err-buf) " at pos " (format "%d\n" pos)) | |
1015 (if indicator | |
1016 (insert "Optional args: \"" indicator "\".\n")) | |
1017 (insert "\nEmacs-version: " (emacs-version) "\n") | |
1018 (insert "Pcl-cvs Version: " | |
1019 "@(#)OrigId: pcl-cvs.el,v 1.93 1993/05/31 22:44:00 ceder Exp\n") | |
1020 (insert "CVS Version: " | |
1021 "@(#)cvs/contrib/pcl-cvs:$Name: r19-14 $:$Id: pcl-cvs.el,v 1.1.1.1 1996/12/18 03:32:27 steve Exp $\n\n") | |
1022 (insert (format "--- Contents of stdout buffer (%d chars) ---\n" | |
1023 (length stdout))) | |
1024 (insert stdout) | |
1025 (insert "--- End of stdout buffer ---\n") | |
1026 (insert (format "--- Contents of stderr buffer (%d chars) ---\n" | |
1027 (length stderr))) | |
1028 (insert stderr) | |
1029 (insert "--- End of stderr buffer ---\n") | |
1030 (insert "\nEnd of bug report.\n") | |
1031 (require 'sendmail) | |
1032 (mail-mode) | |
1033 (error "CVS parse error - please report this bug."))) | |
1034 | |
1035 ;;---------- | |
1036 (defun cvs-parse-update (stdout-buffer stderr-buffer) | |
1037 "Parse the output from `cvs update'. | |
1038 | |
1039 Args: STDOUT-BUFFER STDERR-BUFFER. | |
1040 | |
1041 This functions parses the from `cvs update' (which should be | |
1042 separated in its stdout- and stderr-components) and prints a | |
1043 pretty representation of it in the *cvs* buffer. | |
1044 | |
1045 Signals an error if unexpected output was detected in the buffer." | |
1046 | |
1047 (let* ((head (cons 'dummy nil)) | |
1048 (tail (cvs-parse-stderr stdout-buffer stderr-buffer | |
1049 head default-directory)) | |
1050 (root-dir default-directory)) | |
1051 (cvs-parse-stdout stdout-buffer stderr-buffer tail root-dir) | |
1052 (setq head (sort (cdr head) (function cvs-compare-fileinfos))) | |
1053 (collection-clear cvs-cookie-handle) | |
1054 (collection-append-cookies cvs-cookie-handle head) | |
1055 (cvs-remove-stdout-shadows) | |
1056 (if cvs-auto-remove-handled-directories | |
1057 (cvs-remove-empty-directories)) | |
1058 (set-buffer cvs-buffer-name) | |
1059 (cvs-mode) | |
1060 ;; XEmacs - tedium should let you know when it's ended... | |
1061 (if (pos-visible-in-window-p (point-min)) | |
1062 nil ; assume that the user will see it... | |
1063 (ding t 'ready) | |
1064 (message "CVS update is ready.")) | |
1065 (goto-char (point-min)) | |
1066 (tin-goto-previous cvs-cookie-handle (point-min) 1) | |
1067 (setq default-directory root-dir))) | |
1068 | |
1069 ;;---------- | |
1070 (defun cvs-remove-stdout-shadows () | |
1071 "Remove entries in the *cvs* buffer that comes from both stdout and stderr. | |
1072 If there is two entries for a single file the second one should be | |
1073 deleted. (Remember that sort uses a stable sort algorithm, so one can | |
1074 be sure that the stderr entry is always first)." | |
1075 | |
1076 (collection-filter-tins cvs-cookie-handle | |
1077 (function | |
1078 (lambda (tin) | |
1079 (not (cvs-shadow-entry-p tin)))))) | |
1080 | |
1081 ;;---------- | |
1082 (defun cvs-shadow-entry-p (tin) | |
1083 "Return non-nil if TIN is a shadow entry. | |
1084 Args: TIN. | |
1085 | |
1086 A TIN is a shadow entry if the previous tin contains the same file." | |
1087 | |
1088 (let* ((previous-tin (tin-previous cvs-cookie-handle tin)) | |
1089 (curr (tin-cookie cvs-cookie-handle tin)) | |
1090 (prev (and previous-tin | |
1091 (tin-cookie cvs-cookie-handle previous-tin)))) | |
1092 (and | |
1093 prev curr | |
1094 (string= (cvs-fileinfo->file-name prev) | |
1095 (cvs-fileinfo->file-name curr)) | |
1096 (string= (cvs-fileinfo->dir prev) | |
1097 (cvs-fileinfo->dir curr)) | |
1098 (or | |
1099 (and (eq (cvs-fileinfo->type prev) 'CONFLICT) | |
1100 (eq (cvs-fileinfo->type curr) 'CONFLICT)) | |
1101 (and (eq (cvs-fileinfo->type prev) 'MERGED) | |
1102 (eq (cvs-fileinfo->type curr) 'MODIFIED)) | |
1103 (and (eq (cvs-fileinfo->type prev) 'REM-EXIST) | |
1104 (eq (cvs-fileinfo->type curr) 'REMOVED)))))) | |
1105 | |
1106 ;;---------- | |
1107 (defun cvs-find-backup-file (filename &optional dirname) | |
1108 "Look for a backup file for FILENAME, optionally in directory DIRNAME, and if | |
1109 there is one, return the name of the first file found as a string." | |
1110 | |
1111 (if (eq dirname nil) | |
1112 (setq dirname default-directory)) | |
1113 (car (directory-files dirname nil (concat "^\\" cvs-bakprefix filename | |
1114 "\\.")))) | |
1115 | |
1116 ;;---------- | |
1117 (defun cvs-find-backup-revision (filename) | |
1118 "Take FILENAME as the name of a cvs backup file and return the revision of | |
1119 that file as a string." | |
1120 | |
1121 (substring filename | |
1122 (+ 1 (string-match "\\.\\([0-9.]+\\)$" filename)))) | |
1123 | |
1124 ;;---------- | |
1125 (defun cvs-parse-stderr (stdout-buffer stderr-buffer head dir) | |
1126 "Parse the output from CVS that is written to stderr. | |
1127 Args: STDOUT-BUFFER STDERR-BUFFER HEAD DIR | |
1128 | |
1129 STDOUT-BUFFER holds the output that cvs sent to stdout. It is only | |
1130 used to create a bug report in case there is a parse error. | |
1131 STDERR-BUFFER is the buffer that holds the output to parse. | |
1132 HEAD is a cons-cell, the head of the list that is built. | |
1133 DIR is the directory the `cvs update' was run in. | |
1134 | |
1135 This function returns the last cons-cell in the list that is built." | |
1136 | |
1137 (save-window-excursion | |
1138 (set-buffer stderr-buffer) | |
1139 (goto-char (point-min)) | |
1140 (let ((current-dir dir) | |
1141 (root-dir dir)) | |
1142 | |
1143 (while (< (point) (point-max)) | |
1144 (cond | |
1145 | |
1146 ;; CVS is descending a subdirectory. | |
1147 | |
1148 ((looking-at | |
1149 "^cvs \\(server\\|update\\): Updating \\(.*\\)$") | |
1150 (setq current-dir | |
1151 (cvs-get-current-dir | |
1152 root-dir | |
1153 (buffer-substring (match-beginning 2) (match-end 2)))) | |
1154 (setcdr head (list (cvs-create-fileinfo | |
1155 'DIRCHANGE | |
1156 current-dir | |
1157 "." ; the old version had nil here??? | |
1158 (buffer-substring (match-beginning 0) | |
1159 (match-end 0))))) | |
1160 (setq head (cdr head)) | |
1161 (forward-line 1)) | |
1162 | |
1163 ;; File removed, since it is removed (by third party) in repository. | |
1164 | |
1165 ((or (looking-at | |
1166 "^cvs \\(update\\|server\\): warning: \\(.*\\) is not (any longer) pertinent") | |
1167 (looking-at | |
1168 "^cvs \\(update\\|server\\): \\(.*\\) is no longer in the repository")) | |
1169 | |
1170 (setcdr head (list (cvs-create-fileinfo | |
1171 'CVS-REMOVED | |
1172 current-dir | |
1173 (file-name-nondirectory | |
1174 (buffer-substring (match-beginning 2) | |
1175 (match-end 2))) | |
1176 (buffer-substring (match-beginning 0) | |
1177 (match-end 0))))) | |
1178 (setq head (cdr head)) | |
1179 (forward-line 1)) | |
1180 | |
1181 ;; File removed by you, but recreated by cvs. Ignored. Will say | |
1182 ;; "Updated" on the next line. | |
1183 | |
1184 ((looking-at | |
1185 "^cvs \\(update\\|server\\): warning: .* was lost$") | |
1186 (forward-line 1)) | |
1187 | |
1188 ;; File unknown for some reason. | |
1189 ;; FIXME: is it really a good idea to add this as unknown here? | |
1190 | |
1191 ((looking-at | |
1192 "cvs \\(update\\|server\\): nothing known about \\(.*\\)$") | |
1193 (let ((filename (buffer-substring (match-beginning 2) | |
1194 (match-end 2)))) | |
1195 (if (file-directory-p filename) | |
1196 (setcdr head (list (cvs-create-fileinfo | |
1197 'UNKNOWN-DIR | |
1198 current-dir | |
1199 "." | |
1200 (buffer-substring (match-beginning 0) | |
1201 (match-end 0))))) | |
1202 (setcdr head (list (cvs-create-fileinfo | |
1203 'UNKNOWN | |
1204 current-dir | |
1205 (file-name-nondirectory filename) | |
1206 (buffer-substring (match-beginning 0) | |
1207 (match-end 0))))))) | |
1208 (setq head (cdr head)) | |
1209 (forward-line 1)) | |
1210 | |
1211 ;; A file that has been created by you, but added to the cvs | |
1212 ;; repository by another. | |
1213 | |
1214 ((looking-at | |
1215 "^cvs \\(update\\|server\\): move away \\(.*\\); it is in the way$") | |
1216 (setcdr head (list (cvs-create-fileinfo | |
1217 'MOVE-AWAY | |
1218 current-dir | |
1219 (file-name-nondirectory | |
1220 (buffer-substring (match-beginning 2) | |
1221 (match-end 2))) | |
1222 (buffer-substring (match-beginning 0) | |
1223 (match-end 0))))) | |
1224 (setq head (cdr head)) | |
1225 (forward-line 1)) | |
1226 | |
1227 ;; Cvs waits for a lock. Ignore. | |
1228 | |
1229 ((looking-at | |
1230 "^cvs \\(update\\|server\\): \\[..:..:..\\] waiting for .*lock in ") | |
1231 (forward-line 1)) | |
1232 | |
1233 ;; File removed in repository, but edited by you. | |
1234 | |
1235 ((looking-at | |
1236 "^cvs \\(update\\|server\\): conflict: \\(.*\\) is modified but no longer in the repository$") | |
1237 (setcdr head (list | |
1238 (cvs-create-fileinfo | |
1239 'REM-CONFLICT | |
1240 current-dir | |
1241 (file-name-nondirectory | |
1242 (buffer-substring (match-beginning 2) | |
1243 (match-end 2))) | |
1244 (buffer-substring (match-beginning 0) | |
1245 (match-end 0))))) | |
1246 (setq head (cdr head)) | |
1247 (forward-line 1)) | |
1248 | |
1249 ;; File removed in repository, but edited by someone else. | |
1250 | |
1251 ((looking-at | |
1252 "^cvs \\(update\\|server\\): conflict: removed \\(.*\\) was modified by second party") | |
1253 (setcdr head | |
1254 (list | |
1255 (cvs-create-fileinfo | |
1256 'MOD-CONFLICT | |
1257 current-dir | |
1258 (buffer-substring (match-beginning 1) | |
1259 (match-end 1)) | |
1260 (buffer-substring (match-beginning 0) | |
1261 (match-end 0))))) | |
1262 (setq head (cdr head)) | |
1263 (forward-line 1)) | |
1264 | |
1265 ;; File removed in repository, but not in local directory. | |
1266 | |
1267 ((looking-at | |
1268 "^cvs \\(update\\|server\\): \\(.*\\) should be removed and is still there") | |
1269 (setcdr head | |
1270 (list | |
1271 (cvs-create-fileinfo | |
1272 'REM-EXIST | |
1273 current-dir | |
1274 (buffer-substring (match-beginning 2) | |
1275 (match-end 2)) | |
1276 (buffer-substring (match-beginning 0) | |
1277 (match-end 0))))) | |
1278 (setq head (cdr head)) | |
1279 (forward-line 1)) | |
1280 | |
1281 ;; Error searching for repository | |
1282 | |
1283 ((looking-at | |
1284 "^cvs \\(update\\|server\\): in directory ") | |
1285 (let ((start (point))) | |
1286 (forward-line 1) | |
1287 (cvs-skip-line stdout-buffer stderr-buffer | |
1288 (regexp-quote "cvs [update aborted]: there is no repository ")) | |
1289 (setcdr head (list (cvs-create-fileinfo | |
1290 'REPOS-MISSING | |
1291 current-dir | |
1292 nil | |
1293 (buffer-substring start (point))))) | |
1294 (setq head (cdr head)))) | |
1295 | |
1296 ;; Silly warning from attempted conflict resolution. Ignored. | |
1297 ;; FIXME: Should it be? | |
1298 ;; eg.: "cvs update: cannot find revision APC-web-update in file .cvsignore" | |
1299 ;; | |
1300 ((looking-at | |
1301 "^cvs \\(update\\|server\\): cannot find revision \\(.*\\) in file \\(.*\\)$") | |
1302 (forward-line 1) | |
1303 (message "%s" (buffer-substring (match-beginning 0) (match-end 0)))) | |
1304 | |
1305 ;; CVS has decided to merge someone elses changes into this document. | |
1306 ;; About to start an rcsmerge operation... | |
1307 ;; | |
1308 ((looking-at | |
1309 "^RCS file: ") | |
1310 | |
1311 ;; skip the "RCS file:" line... | |
1312 (forward-line 1) | |
1313 | |
1314 (let ((complex-start (point)) | |
1315 base-revision ; the first revision retrieved to merge from | |
1316 head-revision ; the second revision retrieved to merge from | |
1317 filename ; the name of the file being merged | |
1318 backup-file ; the name of the backup of the working file | |
1319 backup-revision) ; the revision of the original working file | |
1320 | |
1321 (setq base-revision | |
1322 (cvs-skip-line stdout-buffer stderr-buffer | |
1323 "^retrieving revision \\(.*\\)$" | |
1324 1)) | |
1325 (setq head-revision | |
1326 (cvs-skip-line stdout-buffer stderr-buffer | |
1327 "^retrieving revision \\(.*\\)$" | |
1328 1)) | |
1329 (setq filename | |
1330 (cvs-skip-line stdout-buffer stderr-buffer | |
1331 "^Merging differences between [0-9.]+ and [0-9.]+ into \\(.*\\)$" | |
1332 1)) | |
1333 (setq backup-file | |
1334 (cvs-find-backup-file filename current-dir)) | |
1335 (setq backup-revision | |
1336 (cvs-find-backup-revision backup-file)) | |
1337 | |
1338 ;; Was there a conflict during the merge? | |
1339 | |
1340 (cond | |
1341 | |
1342 ;;;; From CVS-1.3 & RCS-5.6.0.1 with GNU-Diffutils-2.5: | |
1343 ;;;; "cvs update -j OLD-REV -j NEW-REV ." | |
1344 ;; | |
1345 ;; RCS file: /big/web-CVS/apc/cmd/Main/logout.sh,v | |
1346 ;; retrieving revision 1.1.1.1 | |
1347 ;; retrieving revision 1.1.1.2 | |
1348 ;; Merging differences between 1.1.1.1 and 1.1.1.2 into logout.sh | |
1349 ;; rcsmerge warning: overlaps during merge | |
1350 | |
1351 ((looking-at | |
1352 ;; Allow both RCS 5.5 and 5.6. (5.6 prints "rcs" and " warning"). | |
1353 "^\\(rcs\\)?merge[:]*\\( warning\\)?: \\((overlaps\\|conflicts\\) during merge$") | |
1354 | |
1355 ;; Yes, this is a conflict. | |
1356 (cvs-skip-line stdout-buffer stderr-buffer | |
1357 "^\\(rcs\\)?merge[:]*\\( warning\\)?: \\(overlaps\\|conflicts\\) during merge$") | |
1358 | |
1359 ;; this line doesn't seem to appear in all cases -- perhaps only | |
1360 ;; in "-j A -j B" usage, in which case this indicates ???? | |
1361 (cvs-skip-line stdout-buffer stderr-buffer | |
1362 "^cvs \\(update\\|server\\): conflicts found in ") | |
1363 | |
1364 (let ((fileinfo | |
1365 (cvs-create-fileinfo | |
1366 'CONFLICT current-dir | |
1367 filename | |
1368 (buffer-substring complex-start (point))))) | |
1369 | |
1370 ;; squirrel away info about the files that were retrieved for merging | |
1371 (cvs-set-fileinfo->base-revision fileinfo base-revision) | |
1372 (cvs-set-fileinfo->head-revision fileinfo head-revision) | |
1373 (cvs-set-fileinfo->backup-revision fileinfo backup-revision) | |
1374 (cvs-set-fileinfo->backup-file fileinfo backup-file) | |
1375 | |
1376 (setcdr head (list fileinfo)) | |
1377 (setq head (cdr head)))) | |
1378 | |
1379 ;; Was it a conflict, and was RCS compiled without DIFF3_BIN, in | |
1380 ;; which case this is a failed conflict resolution? | |
1381 | |
1382 ((looking-at | |
1383 ;; Allow both RCS 5.5 and 5.6. (5.6 prints "rcs" and " warning"). | |
1384 "^\\(rcs\\)?merge\\( warning\\)?: overlaps or other problems during merge$") | |
1385 | |
1386 (cvs-skip-line stdout-buffer stderr-buffer | |
1387 "^\\(rcs\\)?merge\\( warning\\)?: overlaps or other problems during merge$") | |
1388 (cvs-skip-line stdout-buffer stderr-buffer | |
1389 "^cvs update: could not merge ") | |
1390 (cvs-skip-line stdout-buffer stderr-buffer | |
1391 "^cvs update: restoring .* from backup file ") | |
1392 (let ((fileinfo | |
1393 (cvs-create-fileinfo | |
1394 'CONFLICT current-dir | |
1395 filename | |
1396 (buffer-substring complex-start (point))))) | |
1397 (setcdr head (list fileinfo)) | |
1398 (setq head (cdr head)))) | |
1399 | |
1400 ;; Not a conflict; it must be a succesful merge. | |
1401 | |
1402 (t | |
1403 (let ((fileinfo | |
1404 (cvs-create-fileinfo | |
1405 'MERGED current-dir | |
1406 filename | |
1407 (buffer-substring complex-start (point))))) | |
1408 (cvs-set-fileinfo->base-revision fileinfo base-revision) | |
1409 (cvs-set-fileinfo->head-revision fileinfo head-revision) | |
1410 (cvs-set-fileinfo->backup-revision fileinfo backup-revision) | |
1411 (cvs-set-fileinfo->backup-file fileinfo backup-file) | |
1412 (setcdr head (list fileinfo)) | |
1413 (setq head (cdr head))))))) | |
1414 | |
1415 ;; Error messages from CVS (incomplete) | |
1416 | |
1417 ((looking-at | |
1418 "^cvs \\(update\\|server\\): \\(invalid option .*\\)$") | |
1419 (error "Interface problem with CVS: %s" | |
1420 (buffer-substring (match-beginning 2) (match-end 2)))) | |
1421 | |
1422 ;; network errors | |
1423 | |
1424 ;; Kerberos connection attempted but failed. This is not | |
1425 ;; really an error, as CVS will automatically fall back to | |
1426 ;; rsh. Plus it tries kerberos, if available, even when rsh | |
1427 ;; is what you really wanted. | |
1428 | |
1429 ((looking-at | |
1430 "^cvs update: kerberos connect:.*$") | |
1431 (forward-line 1) | |
1432 (message "Remote CVS: %s" | |
1433 (buffer-substring (match-beginning 0) (match-end 0)))) | |
1434 | |
1435 ;; And when kerberos *does* fail, cvs prints out some stuff | |
1436 ;; as it tries rsh. Ignore that stuff too. | |
1437 | |
1438 ((looking-at | |
1439 "^cvs update: trying to start server using rsh$") | |
1440 (forward-line 1)) | |
1441 | |
1442 ((looking-at | |
1443 "^\\([^:]*\\) Connection timed out") | |
1444 (error "Remote CVS: %s" | |
1445 (buffer-substring (match-beginning 0) (match-end 0)))) | |
1446 | |
1447 ((looking-at | |
1448 "^Permission denied.") | |
1449 (error "Remote CVS: %s" | |
1450 (buffer-substring (match-beginning 0) (match-end 0)))) | |
1451 | |
1452 ((looking-at | |
1453 "^cvs \\[update aborted\\]: premature end of file from server") | |
1454 (error "Remote CVS: %s" | |
1455 (buffer-substring (match-beginning 0) (match-end 0)))) | |
1456 | |
1457 ;; Empty line. Probably inserted by mistake by user (or developer :-) | |
1458 ;; Ignore. | |
1459 | |
1460 ((looking-at | |
1461 "^$") | |
1462 (forward-line 1)) | |
1463 | |
1464 ;; top-level parser (cond) default clause | |
1465 | |
1466 (t | |
1467 (cvs-skip-line stdout-buffer stderr-buffer | |
1468 "^UN-MATCHABLE-OUTPUT")))))) | |
1469 | |
1470 ;; cause this function to return the head of the parser output list | |
1471 head) | |
1472 | |
1473 ;;---------- | |
1474 (defun cvs-parse-stdout (stdout-buffer stderr-buffer head root-dir) | |
1475 "Parse the output from CVS that is written to stderr. | |
1476 Args: STDOUT-BUFFER STDERR-BUFFER HEAD ROOT-DIR | |
1477 | |
1478 STDOUT-BUFFER is the buffer that holds the output to parse. | |
1479 STDERR-BUFFER holds the output that cvs sent to stderr. It is only | |
1480 used to create a bug report in case there is a parse error. | |
1481 | |
1482 HEAD is a cons-cell, the head of the list that is built. | |
1483 ROOT-DIR is the directory the `cvs update' was run in. | |
1484 | |
1485 This function doesn't return anything particular." | |
1486 | |
1487 (save-window-excursion | |
1488 (set-buffer stdout-buffer) | |
1489 (goto-char (point-min)) | |
1490 (while (< (point) (point-max)) | |
1491 (cond | |
1492 | |
1493 ;; M: The file is modified by the user, and untouched in the repository. | |
1494 ;; A: The file is "cvs add"ed, but not "cvs ci"ed. | |
1495 ;; R: The file is "cvs remove"ed, but not "cvs ci"ed. | |
1496 ;; C: Conflict (only useful if a join was done and stderr has info...) | |
1497 ;; U: The file is copied from the repository. | |
1498 ;; ?: Unknown file or directory. | |
1499 | |
1500 ((looking-at | |
1501 "^\\([MARCUP?]\\) \\(.*\\)$") | |
1502 (let* | |
1503 ((c (char-after (match-beginning 1))) | |
1504 (full-path (concat (file-name-as-directory root-dir) | |
1505 (buffer-substring (match-beginning 2) | |
1506 (match-end 2)))) | |
1507 (isdir (file-directory-p full-path)) | |
1508 (fileinfo (cvs-create-fileinfo | |
1509 (cond ((eq c ?M) 'MODIFIED) | |
1510 ((eq c ?A) 'ADDED) | |
1511 ((eq c ?R) 'REMOVED) | |
1512 ((eq c ?C) 'CONFLICT) | |
1513 ((eq c ?U) 'UPDATED) | |
1514 ((eq c ?P) 'PATCHED) | |
1515 ((eq c ??) (if isdir | |
1516 'UNKNOWN-DIR | |
1517 'UNKNOWN))) | |
1518 (substring (file-name-directory full-path) 0 -1) | |
1519 (file-name-nondirectory full-path) | |
1520 (buffer-substring (match-beginning 0) (match-end 0))))) | |
1521 ;; Updated and Patched files require no further action. | |
1522 (if (memq c '(?U ?P)) | |
1523 (cvs-set-fileinfo->handled fileinfo t)) | |
1524 | |
1525 ;; Link this last on the list. | |
1526 (setcdr head (list fileinfo)) | |
1527 (setq head (cdr head)) | |
1528 (forward-line 1))) | |
1529 | |
1530 ;; Executing a program because of the -u option in modules. | |
1531 ((looking-at | |
1532 "^cvs \\(update\\|server\\): Executing") | |
1533 ;; Skip by any output the program may generate to stdout. | |
1534 ;; Note that pcl-cvs will get seriously confused if the | |
1535 ;; program prints anything to stderr. | |
1536 (re-search-forward cvs-update-prog-output-skip-regexp) | |
1537 (forward-line 1)) | |
1538 | |
1539 (t | |
1540 (cvs-parse-error stdout-buffer stderr-buffer 'STDOUT (point) | |
1541 "cvs-parse-stdout")))))) | |
1542 | |
1543 ;;---------- | |
1544 (defun cvs-pp (fileinfo) | |
1545 "Pretty print FILEINFO. Insert a printed representation in current buffer. | |
1546 For use by the cookie package." | |
1547 | |
1548 (let ((a (cvs-fileinfo->type fileinfo)) | |
1549 (s (if (cvs-fileinfo->marked fileinfo) | |
1550 "*" " ")) | |
1551 (f (cvs-fileinfo->file-name fileinfo)) | |
1552 (ci (if (cvs-fileinfo->handled fileinfo) | |
1553 " " "ci"))) | |
1554 (insert | |
1555 (cond | |
1556 ((eq a 'UPDATED) | |
1557 (format "%s Updated %s" s f)) | |
1558 ((eq a 'PATCHED) | |
1559 (format "%s Patched %s" s f)) | |
1560 ((eq a 'MODIFIED) | |
1561 (format "%s Modified %s %s" s ci f)) | |
1562 ((eq a 'MERGED) | |
1563 (format "%s Merged %s %s" s ci f)) | |
1564 ((eq a 'CONFLICT) | |
1565 (format "%s Conflict %s" s f)) | |
1566 ((eq a 'ADDED) | |
1567 (format "%s Added %s %s" s ci f)) | |
1568 ((eq a 'REMOVED) | |
1569 (format "%s Removed %s %s" s ci f)) | |
1570 ((eq a 'UNKNOWN) | |
1571 (format "%s Unknown %s" s f)) | |
1572 ((eq a 'UNKNOWN-DIR) | |
1573 (format "%s Unknown dir %s" s f)) | |
1574 ((eq a 'CVS-REMOVED) | |
1575 (format "%s Removed from repository: %s" s f)) | |
1576 ((eq a 'REM-CONFLICT) | |
1577 (format "%s Conflict: Removed from repository, changed by you: %s" s f)) | |
1578 ((eq a 'MOD-CONFLICT) | |
1579 (format "%s Conflict: Removed by you, changed in repository: %s" s f)) | |
1580 ((eq a 'REM-EXIST) | |
1581 (format "%s Conflict: Removed by you, but still exists: %s" s f)) | |
1582 ((eq a 'DIRCHANGE) | |
1583 (format "\nIn directory %s:" (cvs-fileinfo->dir fileinfo))) | |
1584 ((eq a 'MOVE-AWAY) | |
1585 (format "%s Move away %s - it is in the way" s f)) | |
1586 ((eq a 'REPOS-MISSING) | |
1587 (format " This repository directory is missing! Remove this directory manually.")) | |
1588 ((eq a 'MESSAGE) | |
1589 (cvs-fileinfo->full-log fileinfo)) | |
1590 (t | |
1591 (format "%s Internal error! %s" s f)))))) | |
1592 | |
1593 | |
1594 ;;; You can define your own keymap in .emacs. pcl-cvs.el won't overwrite it. | |
1595 | |
1596 (if cvs-mode-map | |
1597 nil | |
1598 (setq cvs-mode-map (make-keymap)) | |
1599 (suppress-keymap cvs-mode-map) | |
1600 (define-prefix-command 'cvs-mode-map-control-c-prefix) | |
1601 (define-key cvs-mode-map "\C-?" 'cvs-mode-unmark-up) | |
1602 (define-key cvs-mode-map "\C-k" 'cvs-mode-acknowledge) | |
1603 (define-key cvs-mode-map "\C-n" 'cvs-mode-next-line) | |
1604 (define-key cvs-mode-map "\C-p" 'cvs-mode-previous-line) | |
1605 ;; ^C- keys are used to set various flags to control CVS features | |
1606 (define-key cvs-mode-map "\C-c" 'cvs-mode-map-control-c-prefix) | |
1607 (define-key cvs-mode-map "\C-cc" 'cvs-change-cvsroot) | |
1608 (define-key cvs-mode-map "\C-cd" 'cvs-set-diff-flags) | |
1609 (define-key cvs-mode-map "\C-cl" 'cvs-set-log-flags) | |
1610 (define-key cvs-mode-map "\C-cs" 'cvs-set-status-flags) | |
1611 (define-key cvs-mode-map "\C-cu" 'cvs-set-update-optional-flags) | |
1612 ;; M- keys are usually those that operate on modules | |
1613 (define-key cvs-mode-map "\M-\C-?" 'cvs-mode-unmark-all-files) | |
1614 (define-key cvs-mode-map "\M-C" 'cvs-mode-rcs2log) ; i.e. "Create a ChangeLog" | |
1615 (define-key cvs-mode-map "\M-a" 'cvs-mode-admin) | |
1616 (define-key cvs-mode-map "\M-c" 'cvs-mode-checkout) | |
1617 (define-key cvs-mode-map "\M-o" 'cvs-mode-checkout-other-window) | |
1618 (define-key cvs-mode-map "\M-p" 'cvs-mode-rdiff) ; i.e. "create a Patch" | |
1619 (define-key cvs-mode-map "\M-r" 'cvs-mode-release) | |
1620 (define-key cvs-mode-map "\M-t" 'cvs-mode-rtag) | |
1621 ;; keys that operate on files | |
1622 (define-key cvs-mode-map " " 'cvs-mode-next-line) | |
1623 (define-key cvs-mode-map "?" 'describe-mode) | |
1624 (define-key cvs-mode-map "A" 'cvs-mode-add-change-log-entry-other-window) | |
1625 (define-key cvs-mode-map "B" 'cvs-mode-byte-compile-files) | |
1626 (define-key cvs-mode-map "C" 'cvs-mode-changelog-commit) | |
1627 (define-key cvs-mode-map "E" 'cvs-mode-emerge) | |
1628 (define-key cvs-mode-map "G" 'cvs-update) | |
1629 (define-key cvs-mode-map "M" 'cvs-mode-mark-all-files) | |
1630 (define-key cvs-mode-map "Q" 'cvs-examine) | |
1631 (define-key cvs-mode-map "R" 'cvs-mode-revert-updated-buffers) | |
1632 (define-key cvs-mode-map "U" 'cvs-mode-undo-local-changes) | |
1633 (define-key cvs-mode-map "a" 'cvs-mode-add) | |
1634 (define-key cvs-mode-map "b" 'cvs-mode-diff-backup) | |
1635 (define-key cvs-mode-map "c" 'cvs-mode-commit) | |
1636 (define-key cvs-mode-map "d" 'cvs-mode-diff-cvs) | |
1637 (define-key cvs-mode-map "e" 'cvs-mode-ediff) | |
1638 (define-key cvs-mode-map "f" 'cvs-mode-find-file) | |
1639 (define-key cvs-mode-map "g" 'cvs-mode-update-no-prompt) | |
1640 (define-key cvs-mode-map "i" 'cvs-mode-ignore) | |
1641 (define-key cvs-mode-map "l" 'cvs-mode-log) | |
1642 (define-key cvs-mode-map "m" 'cvs-mode-mark) | |
1643 (define-key cvs-mode-map "n" 'cvs-mode-next-line) | |
1644 (define-key cvs-mode-map "o" 'cvs-mode-find-file-other-window) | |
1645 (define-key cvs-mode-map "p" 'cvs-mode-previous-line) | |
1646 (define-key cvs-mode-map "q" 'bury-buffer) | |
1647 (define-key cvs-mode-map "r" 'cvs-mode-remove-file) | |
1648 (define-key cvs-mode-map "s" 'cvs-mode-status) | |
1649 (define-key cvs-mode-map "t" 'cvs-mode-tag) | |
1650 (define-key cvs-mode-map "u" 'cvs-mode-unmark) | |
1651 (define-key cvs-mode-map "v" 'cvs-mode-diff-vendor) | |
1652 (define-key cvs-mode-map "x" 'cvs-mode-remove-handled)) | |
1653 | |
1654 ;;---------- | |
1655 (defun cvs-get-marked (&optional ignore-marks ignore-contents) | |
1656 "Return a list of all selected tins. | |
1657 Args: &optional IGNORE-MARKS IGNORE-CONTENTS. | |
1658 | |
1659 If there are any marked tins, and IGNORE-MARKS is nil, return them. Otherwise, | |
1660 if the cursor selects a directory, return all files in it, unless there are | |
1661 none, in which case just return the directory; or unless IGNORE-CONTENTS is not | |
1662 nil, in which case also just return the directory. Otherwise return (a list | |
1663 containing) the file the cursor points to, or an empty list if it doesn't point | |
1664 to a file at all." | |
1665 | |
1666 (cond | |
1667 ;; Any marked cookies? | |
1668 ((and (not ignore-marks) | |
1669 (collection-collect-tin cvs-cookie-handle 'cvs-fileinfo->marked))) | |
1670 ;; Nope. | |
1671 ((and (not ignore-contents) | |
1672 (let ((sel (tin-locate cvs-cookie-handle (point)))) | |
1673 (cond | |
1674 ;; If a directory is selected, all it members are returned. | |
1675 ((and sel (eq (cvs-fileinfo->type (tin-cookie cvs-cookie-handle | |
1676 sel)) | |
1677 'DIRCHANGE)) | |
1678 (let ((retsel | |
1679 (collection-collect-tin cvs-cookie-handle | |
1680 'cvs-dir-member-p | |
1681 (cvs-fileinfo->dir (tin-cookie | |
1682 cvs-cookie-handle sel))))) | |
1683 (if retsel | |
1684 retsel | |
1685 (list sel)))) | |
1686 (t | |
1687 (list sel)))))) | |
1688 (t | |
1689 (list (tin-locate cvs-cookie-handle (point)))))) | |
1690 | |
1691 ;;---------- | |
1692 (defun cvs-dir-member-p (fileinfo dir) | |
1693 "Return true if FILEINFO represents a file in directory DIR." | |
1694 | |
1695 (and (not (eq (cvs-fileinfo->type fileinfo) 'DIRCHANGE)) | |
1696 (string= (cvs-fileinfo->dir fileinfo) dir))) | |
1697 | |
1698 ;;---------- | |
1699 (defun cvs-dir-empty-p (tin) | |
1700 "Return non-nil if TIN is a directory that is empty. | |
1701 Args: CVS-BUF TIN." | |
1702 | |
1703 (and (eq (cvs-fileinfo->type (tin-cookie cvs-cookie-handle tin)) 'DIRCHANGE) | |
1704 (or (not (tin-next cvs-cookie-handle tin)) | |
1705 (eq (cvs-fileinfo->type | |
1706 (tin-cookie cvs-cookie-handle | |
1707 (tin-next cvs-cookie-handle tin))) | |
1708 'DIRCHANGE)))) | |
1709 | |
1710 ;;---------- | |
1711 (defun cvs-mode-revert-updated-buffers () | |
1712 "Revert any buffers that are UPDATED, PATCHED, MERGED or CONFLICT." | |
1713 | |
1714 (interactive) | |
1715 (cookie-map (function cvs-revert-fileinfo) cvs-cookie-handle)) | |
1716 | |
1717 ;;---------- | |
1718 (defun cvs-revert-fileinfo (fileinfo) | |
1719 "Revert the buffer that holds the file in FILEINFO if it has changed, | |
1720 and if the type is UPDATED, PATCHED, MERGED or CONFLICT." | |
1721 | |
1722 (let* ((type (cvs-fileinfo->type fileinfo)) | |
1723 (file (cvs-fileinfo->full-path fileinfo)) | |
1724 (buffer (get-file-buffer file))) | |
1725 ;; For a revert to happen... | |
1726 (cond | |
1727 ((and | |
1728 ;; ...the type must be one that justifies a revert... | |
1729 (or (eq type 'UPDATED) | |
1730 (eq type 'PATCHED) | |
1731 (eq type 'MERGED) | |
1732 (eq type 'CONFLICT)) | |
1733 ;; ...and the user must be editing the file... | |
1734 buffer) | |
1735 (save-excursion | |
1736 (set-buffer buffer) | |
1737 (cond | |
1738 ((buffer-modified-p) | |
1739 (error "%s: edited since last cvs-update." | |
1740 (buffer-file-name))) | |
1741 ;; Go ahead and revert the file. | |
1742 (t (revert-buffer 'dont-use-auto-save-file 'dont-ask)))))))) | |
1743 | |
1744 ;;---------- | |
1745 (defun cvs-mode-remove-handled () | |
1746 "Remove all lines that are handled. | |
1747 Empty directories are removed." | |
1748 | |
1749 (interactive) | |
1750 ;; Pass one: remove files that are handled. | |
1751 (collection-filter-cookies cvs-cookie-handle | |
1752 (function | |
1753 (lambda (fileinfo) | |
1754 (not (cvs-fileinfo->handled fileinfo))))) | |
1755 ;; Pass two: remove empty directories. | |
1756 (if cvs-auto-remove-handled-directories | |
1757 (cvs-remove-empty-directories))) | |
1758 | |
1759 ;;---------- | |
1760 (defun cvs-remove-empty-directories () | |
1761 "Remove empty directories in the *cvs* buffer." | |
1762 | |
1763 (collection-filter-tins cvs-cookie-handle | |
1764 (function | |
1765 (lambda (tin) | |
1766 (not (cvs-dir-empty-p tin)))))) | |
1767 | |
1768 ;;---------- | |
1769 (defun cvs-mode-mark (pos) | |
1770 "Mark a fileinfo. | |
1771 Args: POS. | |
1772 | |
1773 If the fileinfo is a directory, all the contents of that directory are marked | |
1774 instead. A directory can never be marked. POS is a buffer position." | |
1775 | |
1776 (interactive "d") | |
1777 (let* ((tin (tin-locate cvs-cookie-handle pos)) | |
1778 (sel (tin-cookie cvs-cookie-handle tin))) | |
1779 (cond | |
1780 ;; Does POS point to a directory? If so, mark all files in that directory. | |
1781 ((eq (cvs-fileinfo->type sel) 'DIRCHANGE) | |
1782 (cookie-map | |
1783 (function (lambda (f dir) | |
1784 (cond | |
1785 ((cvs-dir-member-p f dir) | |
1786 (cvs-set-fileinfo->marked f t) | |
1787 t)))) ; Tell cookie to redisplay this cookie. | |
1788 cvs-cookie-handle | |
1789 (cvs-fileinfo->dir sel))) | |
1790 (t | |
1791 (cvs-set-fileinfo->marked sel t) | |
1792 (tin-invalidate cvs-cookie-handle tin) | |
1793 (tin-goto-next cvs-cookie-handle pos 1))))) | |
1794 | |
1795 ;;---------- | |
1796 (defun cvs-committable (tin) | |
1797 "Check if the TIN is committable. | |
1798 It is committable if it | |
1799 a) is not handled and | |
1800 b) is either MODIFIED, ADDED, REMOVED, MERGED or CONFLICT." | |
1801 | |
1802 (let* ((fileinfo (tin-cookie cvs-cookie-handle tin)) | |
1803 (type (cvs-fileinfo->type fileinfo))) | |
1804 (and (not (cvs-fileinfo->handled fileinfo)) | |
1805 (or (eq type 'MODIFIED) | |
1806 (eq type 'ADDED) | |
1807 (eq type 'REMOVED) | |
1808 (eq type 'MERGED) | |
1809 (eq type 'CONFLICT))))) | |
1810 | |
1811 ;;---------- | |
1812 (defun cvs-mode-commit () | |
1813 "Check in all marked files, or the current file. | |
1814 The user will be asked for a log message in a buffer. | |
1815 If cvs-erase-input-buffer is non-nil that buffer will be erased. | |
1816 Otherwise mark and point will be set around the entire contents of the | |
1817 buffer so that it is easy to kill the contents of the buffer with \\[kill-region]." | |
1818 | |
1819 (interactive) | |
1820 (let* ((cvs-buf (current-buffer)) | |
1821 (marked (cvs-filter (function cvs-committable) | |
1822 (cvs-get-marked)))) | |
1823 (if (null marked) | |
1824 (error "Nothing to commit!") | |
1825 (pop-to-buffer (get-buffer-create cvs-commit-prompt-buffer)) | |
1826 (goto-char (point-min)) | |
1827 | |
1828 (if cvs-erase-input-buffer | |
1829 (erase-buffer) | |
1830 (push-mark (point-max))) | |
1831 (cvs-edit-mode) | |
1832 (make-local-variable 'cvs-commit-list) | |
1833 (setq cvs-commit-list marked) | |
1834 (message "Press C-c C-c when you are done editing.")))) | |
1835 | |
1836 ;;---------- | |
1837 (defun cvs-edit-done () | |
1838 "Commit the files to the repository." | |
1839 | |
1840 (interactive) | |
1841 (if (null cvs-commit-list) | |
1842 (error "You have already committed the files")) | |
1843 (if (and (> (point-max) 1) | |
1844 (/= (char-after (1- (point-max))) ?\n) | |
1845 (or (eq cvs-commit-buffer-require-final-newline t) | |
1846 (and cvs-commit-buffer-require-final-newline | |
1847 (yes-or-no-p | |
1848 (format "Buffer %s does not end in newline. Add one? " | |
1849 (buffer-name)))))) | |
1850 (save-excursion | |
1851 (goto-char (point-max)) | |
1852 (insert ?\n))) | |
1853 (save-some-buffers) | |
1854 (let ((cc-list cvs-commit-list) | |
1855 (cc-buffer (get-buffer cvs-buffer-name)) | |
1856 (msg-buffer (current-buffer)) | |
1857 (msg (buffer-substring (point-min) (point-max)))) | |
1858 (pop-to-buffer cc-buffer) | |
1859 (bury-buffer msg-buffer) | |
1860 (cvs-use-temp-buffer) | |
1861 (message "Committing...") | |
1862 (if (cvs-execute-list cc-list cvs-program | |
1863 (if cvs-cvsroot | |
1864 (list "-d" cvs-cvsroot "commit" "-m" msg) | |
1865 (list "commit" "-m" msg)) | |
1866 "Committing %s...") | |
1867 (error "Something went wrong. Check the %s buffer carefully." | |
1868 cvs-temp-buffer-name)) | |
1869 ;; FIXME: don't do any of this if the commit fails. | |
1870 (let ((ccl cc-list)) | |
1871 (while ccl | |
1872 (cvs-after-commit-function (tin-cookie cvs-cookie-handle (car ccl))) | |
1873 (setq ccl (cdr ccl)))) | |
1874 (apply 'tin-invalidate cvs-cookie-handle cc-list) | |
1875 (set-buffer msg-buffer) | |
1876 (setq cvs-commit-list nil) | |
1877 (set-buffer cc-buffer) | |
1878 (if cvs-auto-remove-handled | |
1879 (cvs-mode-remove-handled))) | |
1880 | |
1881 (message "Committing... Done.")) | |
1882 | |
1883 ;;---------- | |
1884 (defun cvs-after-commit-function (fileinfo) | |
1885 "Do everything that needs to be done when FILEINFO has been committed. | |
1886 The fileinfo->handle is set, and if the buffer is present it is reverted." | |
1887 | |
1888 (cvs-set-fileinfo->handled fileinfo t) | |
1889 (if cvs-auto-revert-after-commit | |
1890 (let* ((file (cvs-fileinfo->full-path fileinfo)) | |
1891 (buffer (get-file-buffer file))) | |
1892 ;; For a revert to happen... | |
1893 (if buffer | |
1894 ;; ...the user must be editing the file... | |
1895 (save-excursion | |
1896 (set-buffer buffer) | |
1897 (if (not (buffer-modified-p)) | |
1898 ;; ...but it must be unmodified. | |
1899 (revert-buffer 'dont-use-auto-save-file 'dont-ask))))))) | |
1900 | |
1901 ;;---------- | |
1902 (defun cvs-execute-list (tin-list program constant-args &optional message-fmt) | |
1903 "Run PROGRAM on all elements on TIN-LIST. | |
1904 Args: TIN-LIST PROGRAM CONSTANT-ARGS. | |
1905 | |
1906 The PROGRAM will be called with pwd set to the directory the files reside | |
1907 in. CONSTANT-ARGS should be a list of strings. The arguments given to the | |
1908 program will be CONSTANT-ARGS followed by all the files (from TIN-LIST) that | |
1909 resides in that directory. If the files in TIN-LIST resides in different | |
1910 directories the PROGRAM will be run once for each directory (if all files in | |
1911 the same directory appears after each other). | |
1912 | |
1913 Any output from PROGRAM will be inserted in the current buffer. | |
1914 | |
1915 This function return nil if all went well, or the numerical exit status or a | |
1916 signal name as a string. Note that PROGRAM might be called several times. This | |
1917 will return non-nil if something goes wrong, but there is no way to know which | |
1918 process that failed. | |
1919 | |
1920 If MESSAGE-FMT is not nil, then message is called to display progress with | |
1921 MESSAGE-FMT as the string. MESSAGE-FMT should contain one %s for the arg-list | |
1922 being passed to PROGRAM." | |
1923 | |
1924 ;; FIXME: something seems wrong with the error checking here.... | |
1925 | |
1926 (let ((exitstatus nil)) | |
1927 (while tin-list | |
1928 (let ((current-dir (cvs-fileinfo->dir (tin-cookie cvs-cookie-handle | |
1929 (car tin-list)))) | |
1930 arg-list | |
1931 arg-str) | |
1932 | |
1933 ;; Collect all marked files in this directory. | |
1934 | |
1935 (while (and tin-list | |
1936 (string= current-dir | |
1937 (cvs-fileinfo->dir (tin-cookie cvs-cookie-handle | |
1938 (car tin-list))))) | |
1939 (setq arg-list | |
1940 (cons (cvs-fileinfo->file-name | |
1941 (tin-cookie cvs-cookie-handle (car tin-list))) | |
1942 arg-list)) | |
1943 (setq tin-list (cdr tin-list))) | |
1944 | |
1945 (setq arg-list (nreverse arg-list)) | |
1946 | |
1947 ;; Execute the command on all the files that were collected. | |
1948 | |
1949 (if message-fmt | |
1950 (message message-fmt | |
1951 (mapconcat 'cvs-quote-multiword-string | |
1952 arg-list | |
1953 " "))) | |
1954 (setq default-directory (file-name-as-directory current-dir)) | |
1955 (insert (format "=== cd %s\n" default-directory)) | |
1956 (insert (format "=== %s %s\n\n" | |
1957 program | |
1958 (mapconcat 'cvs-quote-multiword-string | |
1959 (nconc (copy-sequence constant-args) | |
1960 arg-list) | |
1961 " "))) | |
1962 (let ((res (apply 'call-process program nil t t | |
1963 (nconc (copy-sequence constant-args) arg-list)))) | |
1964 ;; Remember the first, or highest, exitstatus. | |
1965 (if (and (not (and (integerp res) (zerop res))) | |
1966 (or (null exitstatus) | |
1967 (and (integerp exitstatus) (= 1 exitstatus)))) | |
1968 (setq exitstatus res))) | |
1969 (goto-char (point-max)) | |
1970 (if message-fmt | |
1971 (message message-fmt | |
1972 (mapconcat 'cvs-quote-multiword-string | |
1973 (nconc (copy-sequence arg-list) '("Done.")) | |
1974 " "))) | |
1975 exitstatus)))) | |
1976 | |
1977 ;;---------- | |
1978 ;;;; +++ not currently used! | |
1979 (defun cvs-execute-single-file-list (tin-list extractor program constant-args | |
1980 &optional cleanup message-fmt) | |
1981 "Run PROGRAM on all elements on TIN-LIST. | |
1982 Args: TIN-LIST EXTRACTOR PROGRAM CONSTANT-ARGS &optional CLEANUP. | |
1983 | |
1984 The PROGRAM will be called with pwd set to the directory the files | |
1985 reside in. CONSTANT-ARGS is a list of strings to pass as arguments to | |
1986 PROGRAM. The arguments given to the program will be CONSTANT-ARGS | |
1987 followed by the list that EXTRACTOR returns. | |
1988 | |
1989 EXTRACTOR will be called once for each file on TIN-LIST. It is given | |
1990 one argument, the cvs-fileinfo. It can return t, which means ignore | |
1991 this file, or a list of arguments to send to the program. | |
1992 | |
1993 If CLEANUP is not nil, the filenames returned by EXTRACTOR are deleted. | |
1994 | |
1995 If MESSAGE-FMT is not nil, then message is called to display progress with | |
1996 MESSAGE-FMT as the string. MESSAGE-FMT should contain one %s for the arg-list | |
1997 being passed to PROGRAM." | |
1998 | |
1999 (while tin-list | |
2000 (let ((current-dir (file-name-as-directory | |
2001 (cvs-fileinfo->dir | |
2002 (tin-cookie cvs-cookie-handle | |
2003 (car tin-list))))) | |
2004 (arg-list | |
2005 (funcall extractor | |
2006 (tin-cookie cvs-cookie-handle (car tin-list))))) | |
2007 | |
2008 ;; Execute the command unless extractor returned t. | |
2009 | |
2010 (if (eq arg-list t) | |
2011 nil | |
2012 (setq default-directory current-dir) | |
2013 (insert (format "=== cd %s\n" default-directory)) | |
2014 (insert (format "=== %s %s\n\n" | |
2015 program | |
2016 (mapconcat 'cvs-quote-multiword-string | |
2017 (nconc (copy-sequence constant-args) | |
2018 arg-list) | |
2019 " "))) | |
2020 (if message-fmt | |
2021 (message message-fmt (mapconcat 'cvs-quote-multiword-string | |
2022 arg-list | |
2023 " "))) | |
2024 (apply 'call-process program nil t t | |
2025 (nconc (copy-sequence constant-args) arg-list)) | |
2026 (goto-char (point-max)) | |
2027 (if message-fmt | |
2028 (message message-fmt (mapconcat 'cvs-quote-multiword-string | |
2029 (nconc arg-list '("Done.")) | |
2030 " "))) | |
2031 (if cleanup | |
2032 (while arg-list | |
2033 ;;;; (kill-buffer ?????) | |
2034 (delete-file (car arg-list)) | |
2035 (setq arg-list (cdr arg-list)))))) | |
2036 (setq tin-list (cdr tin-list)))) | |
2037 | |
2038 ;;---------- | |
2039 (defun cvs-edit-mode () | |
2040 "\\<cvs-edit-mode-map>Mode for editing cvs log messages. | |
2041 Commands: | |
2042 \\[cvs-edit-done] checks in the file when you are ready. | |
2043 This mode is based on fundamental mode." | |
2044 | |
2045 (interactive) | |
2046 (use-local-map cvs-edit-mode-map) | |
2047 (setq major-mode 'cvs-edit-mode) | |
2048 (setq mode-name "CVS Log") | |
2049 (auto-fill-mode 1)) | |
2050 | |
2051 ;;---------- | |
2052 (if cvs-edit-mode-map | |
2053 nil | |
2054 (setq cvs-edit-mode-map (make-sparse-keymap)) | |
2055 (define-prefix-command 'cvs-edit-mode-control-c-prefix) | |
2056 (define-key cvs-edit-mode-map "\C-c" 'cvs-edit-mode-control-c-prefix) | |
2057 (define-key cvs-edit-mode-map "\C-c\C-c" 'cvs-edit-done)) | |
2058 | |
2059 ;;---------- | |
2060 (defun cvs-diffable (tins) | |
2061 "Return a list of all tins on TINS that it makes sense to run | |
2062 ``cvs diff'' on." | |
2063 | |
2064 ;; +++ There is an unnecessary (nreverse) here. Get the list the | |
2065 ;; other way around instead! | |
2066 (let ((result nil)) | |
2067 (while tins | |
2068 (let ((type (cvs-fileinfo->type | |
2069 (tin-cookie cvs-cookie-handle (car tins))))) | |
2070 (if (or (eq type 'MODIFIED) | |
2071 (eq type 'UPDATED) | |
2072 (eq type 'PATCHED) | |
2073 (eq type 'MERGED) | |
2074 (eq type 'CONFLICT) | |
2075 (eq type 'REMOVED) ;+++Does this line make sense? | |
2076 (eq type 'ADDED)) ;+++Does this line make sense? | |
2077 (setq result (cons (car tins) result))) | |
2078 (setq tins (cdr tins)))) | |
2079 (nreverse result))) | |
2080 | |
2081 ;;---------- | |
2082 (defun cvs-mode-diff-cvs (&optional ignore-marks) | |
2083 "Diff the selected files against the head revisions in the repository. | |
2084 | |
2085 If the variable cvs-diff-ignore-marks is non-nil any marked files will not be | |
2086 considered to be selected. An optional prefix argument will invert the | |
2087 influence from cvs-diff-ignore-marks. | |
2088 | |
2089 The flags in the variable cvs-diff-flags will be passed to ``cvs diff''. | |
2090 | |
2091 The resulting diffs are placed in the cvs-fileinfo->cvs-diff-buffer." | |
2092 | |
2093 (interactive "P") | |
2094 (if (not (listp cvs-diff-flags)) | |
2095 (error "cvs-diff-flags should be set using cvs-set-diff-flags.")) | |
2096 (save-some-buffers) | |
2097 (message "cvsdiffing...") | |
2098 (let ((marked-file-list (cvs-diffable | |
2099 (cvs-get-marked | |
2100 (or (and ignore-marks (not cvs-diff-ignore-marks)) | |
2101 (and (not ignore-marks) cvs-diff-ignore-marks)))))) | |
2102 (while marked-file-list | |
2103 (let ((fileinfo-to-diff (tin-cookie cvs-cookie-handle | |
2104 (car marked-file-list))) | |
2105 (local-def-directory (file-name-as-directory | |
2106 (cvs-fileinfo->dir | |
2107 (tin-cookie cvs-cookie-handle | |
2108 (car marked-file-list)))))) | |
2109 (message "cvsdiffing %s..." | |
2110 (cvs-fileinfo->file-name fileinfo-to-diff)) | |
2111 | |
2112 ;; FIXME: this seems messy to test and set buffer name at this point.... | |
2113 (if (not (cvs-fileinfo->cvs-diff-buffer fileinfo-to-diff)) | |
2114 (cvs-set-fileinfo->cvs-diff-buffer fileinfo-to-diff | |
2115 (concat "*cvs-diff-" | |
2116 (cvs-fileinfo->file-name | |
2117 fileinfo-to-diff) | |
2118 "-in-" | |
2119 local-def-directory | |
2120 "*"))) | |
2121 (display-buffer (get-buffer-create | |
2122 (cvs-fileinfo->cvs-diff-buffer fileinfo-to-diff))) | |
2123 (set-buffer (cvs-fileinfo->cvs-diff-buffer fileinfo-to-diff)) | |
2124 (setq buffer-read-only nil) | |
2125 (setq default-directory local-def-directory) | |
2126 (erase-buffer) | |
2127 (insert (format "=== cd %s\n" default-directory)) | |
2128 (insert (format "=== cvs %s\n\n" | |
2129 (mapconcat 'cvs-quote-multiword-string | |
2130 (nconc (if cvs-cvsroot | |
2131 (list "-d" cvs-cvsroot "diff") | |
2132 '("diff")) | |
2133 (copy-sequence cvs-diff-flags) | |
2134 (list (cvs-fileinfo->file-name | |
2135 fileinfo-to-diff))) | |
2136 " "))) | |
2137 (if (apply 'call-process cvs-program nil t t | |
2138 (nconc (if cvs-cvsroot | |
2139 (list "-d" cvs-cvsroot "diff") | |
2140 '("diff")) | |
2141 (copy-sequence cvs-diff-flags) | |
2142 (list (cvs-fileinfo->file-name fileinfo-to-diff)))) | |
2143 (message "cvsdiffing %s... Done." | |
2144 (cvs-fileinfo->file-name fileinfo-to-diff)) | |
2145 (message "cvsdiffing %s... No differences found." | |
2146 (cvs-fileinfo->file-name fileinfo-to-diff))) | |
2147 (goto-char (point-max)) | |
2148 (setq marked-file-list (cdr marked-file-list))))) | |
2149 (message "cvsdiffing... Done.")) | |
2150 | |
2151 ;;---------- | |
2152 (defun cvs-mode-diff-backup (&optional ignore-marks) | |
2153 "Diff the files against the backup file. | |
2154 This command can be used on files that are marked with \"Merged\" | |
2155 or \"Conflict\" in the *cvs* buffer. | |
2156 | |
2157 If the variable cvs-diff-ignore-marks is non-nil any marked files will | |
2158 not be considered to be selected. An optional prefix argument will | |
2159 invert the influence from cvs-diff-ignore-marks. | |
2160 | |
2161 The flags in cvs-diff-flags will be passed to ``diff''. | |
2162 | |
2163 The resulting diffs are placed in the cvs-fileinfo->backup-diff-buffer." | |
2164 | |
2165 (interactive "P") | |
2166 (if (not (listp cvs-diff-flags)) | |
2167 (error "cvs-diff-flags should be set using cvs-set-diff-flags.")) | |
2168 (save-some-buffers) | |
2169 (let ((marked-file-list (cvs-filter | |
2170 (function cvs-backup-diffable) | |
2171 (cvs-get-marked | |
2172 (or | |
2173 (and ignore-marks (not cvs-diff-ignore-marks)) | |
2174 (and (not ignore-marks) cvs-diff-ignore-marks)))))) | |
2175 (if (null marked-file-list) | |
2176 (error "No ``Conflict'' or ``Merged'' file selected!")) | |
2177 (message "backup diff...") | |
2178 (while marked-file-list | |
2179 (let ((fileinfo-to-diff (tin-cookie cvs-cookie-handle | |
2180 (car marked-file-list))) | |
2181 (local-def-directory (file-name-as-directory | |
2182 (cvs-fileinfo->dir | |
2183 (tin-cookie cvs-cookie-handle | |
2184 (car marked-file-list))))) | |
2185 (backup-temp-files (cvs-diff-backup-extractor | |
2186 (tin-cookie cvs-cookie-handle | |
2187 (car marked-file-list))))) | |
2188 (message "backup diff %s..." | |
2189 (cvs-fileinfo->file-name fileinfo-to-diff)) | |
2190 | |
2191 ;; FIXME: this seems messy to test and set buffer name at this point.... | |
2192 (if (not (cvs-fileinfo->backup-diff-buffer fileinfo-to-diff)) | |
2193 (cvs-set-fileinfo->backup-diff-buffer fileinfo-to-diff | |
2194 (concat "*cvs-diff-" | |
2195 (cvs-fileinfo->backup-file | |
2196 fileinfo-to-diff) | |
2197 "-to-" | |
2198 (cvs-fileinfo->file-name | |
2199 fileinfo-to-diff) | |
2200 "-in" | |
2201 local-def-directory | |
2202 "*"))) | |
2203 (display-buffer (get-buffer-create | |
2204 (cvs-fileinfo->backup-diff-buffer fileinfo-to-diff))) | |
2205 (set-buffer (cvs-fileinfo->backup-diff-buffer fileinfo-to-diff)) | |
2206 (setq buffer-read-only nil) | |
2207 (setq default-directory local-def-directory) | |
2208 (erase-buffer) | |
2209 (insert (format "=== cd %s\n" default-directory)) | |
2210 (insert (format "=== %s %s\n\n" | |
2211 cvs-diff-program | |
2212 (mapconcat 'cvs-quote-multiword-string | |
2213 (nconc (copy-sequence cvs-diff-flags) | |
2214 backup-temp-files) | |
2215 " "))) | |
2216 (apply 'call-process cvs-diff-program nil t t | |
2217 (nconc (copy-sequence cvs-diff-flags) backup-temp-files)) | |
2218 (goto-char (point-max)) | |
2219 (message "backup diff %s... Done." | |
2220 (cvs-fileinfo->file-name fileinfo-to-diff)) | |
2221 (setq marked-file-list (cdr marked-file-list))))) | |
2222 (message "backup diff... Done.")) | |
2223 | |
2224 ;;---------- | |
2225 (defun cvs-mode-diff-vendor (&optional ignore-marks) | |
2226 "Diff the revisions merged into the current file. I.e. show what changes | |
2227 were merged in. | |
2228 | |
2229 This command can be used on files that are marked with \"Merged\" | |
2230 or \"Conflict\" in the *cvs* buffer. | |
2231 | |
2232 If the variable cvs-diff-ignore-marks is non-nil any marked files will | |
2233 not be considered to be selected. An optional prefix argument will | |
2234 invert the influence from cvs-diff-ignore-marks. | |
2235 | |
2236 The flags in cvs-diff-flags will be passed to ``diff''. | |
2237 | |
2238 The resulting diffs are placed in the cvs-fileinfo->vendor-diff-buffer." | |
2239 | |
2240 (interactive "P") | |
2241 (if (not (listp cvs-diff-flags)) | |
2242 (error "cvs-diff-flags should be set using cvs-set-diff-flags.")) | |
2243 (save-some-buffers) | |
2244 (let ((marked-file-list (cvs-filter | |
2245 (function cvs-vendor-diffable) | |
2246 (cvs-get-marked | |
2247 (or | |
2248 (and ignore-marks (not cvs-diff-ignore-marks)) | |
2249 (and (not ignore-marks) cvs-diff-ignore-marks)))))) | |
2250 (if (null marked-file-list) | |
2251 (error "No ``Conflict'' or ``Merged'' file selected!")) | |
2252 (message "vendor diff...") | |
2253 (while marked-file-list | |
2254 (let ((fileinfo-to-diff (tin-cookie cvs-cookie-handle | |
2255 (car marked-file-list))) | |
2256 (local-def-directory (file-name-as-directory | |
2257 (cvs-fileinfo->dir | |
2258 (tin-cookie cvs-cookie-handle | |
2259 (car marked-file-list))))) | |
2260 (vendor-temp-files (cvs-diff-vendor-extractor | |
2261 (tin-cookie cvs-cookie-handle | |
2262 (car marked-file-list))))) | |
2263 (message "vendor diff %s..." | |
2264 (cvs-fileinfo->file-name fileinfo-to-diff)) | |
2265 (if (not (cvs-fileinfo->vendor-diff-buffer fileinfo-to-diff)) | |
2266 (cvs-set-fileinfo->vendor-diff-buffer fileinfo-to-diff | |
2267 (concat "*cvs-diff-" | |
2268 (cvs-fileinfo->file-name | |
2269 fileinfo-to-diff) | |
2270 "-of-" | |
2271 (cvs-fileinfo->base-revision | |
2272 fileinfo-to-diff) | |
2273 "-to-" | |
2274 (cvs-fileinfo->head-revision | |
2275 fileinfo-to-diff) | |
2276 "-in-" | |
2277 local-def-directory | |
2278 "*"))) | |
2279 (display-buffer (get-buffer-create | |
2280 (cvs-fileinfo->vendor-diff-buffer fileinfo-to-diff))) | |
2281 (set-buffer (cvs-fileinfo->vendor-diff-buffer fileinfo-to-diff)) | |
2282 (setq buffer-read-only nil) | |
2283 (setq default-directory local-def-directory) | |
2284 (erase-buffer) | |
2285 (insert (format "=== cd %s\n" default-directory)) | |
2286 (insert (format "=== %s %s\n\n" | |
2287 cvs-diff-program | |
2288 (mapconcat 'cvs-quote-multiword-string | |
2289 (nconc (copy-sequence cvs-diff-flags) | |
2290 vendor-temp-files) | |
2291 " "))) | |
2292 (apply 'call-process cvs-diff-program nil t t | |
2293 (nconc (copy-sequence cvs-diff-flags) vendor-temp-files)) | |
2294 (goto-char (point-max)) | |
2295 (message "vendor diff %s... Done." | |
2296 (cvs-fileinfo->file-name fileinfo-to-diff)) | |
2297 (while vendor-temp-files | |
2298 (cvs-kill-buffer-visiting (car vendor-temp-files)) | |
2299 (delete-file (car vendor-temp-files)) | |
2300 (setq vendor-temp-files (cdr vendor-temp-files))) | |
2301 (setq marked-file-list (cdr marked-file-list))))) | |
2302 (message "vendor diff... Done.")) | |
2303 | |
2304 ;;---------- | |
2305 (defun cvs-backup-diffable (tin) | |
2306 "Check if the TIN is backup-diffable. | |
2307 It must have a backup file to be diffable." | |
2308 | |
2309 (file-readable-p | |
2310 (cvs-fileinfo->backup-file (tin-cookie cvs-cookie-handle tin)))) | |
2311 | |
2312 ;;---------- | |
2313 (defun cvs-vendor-diffable (tin) | |
2314 "Check if the TIN is vendor-diffable. | |
2315 It must have head and base revision info to be diffable." | |
2316 | |
2317 (and | |
2318 (cvs-fileinfo->base-revision (tin-cookie cvs-cookie-handle tin)) | |
2319 (cvs-fileinfo->head-revision (tin-cookie cvs-cookie-handle tin)))) | |
2320 | |
2321 ;;---------- | |
2322 (defun cvs-diff-backup-extractor (fileinfo) | |
2323 "Return the filename and the name of the backup file as a list. | |
2324 Signal an error if there is no backup file." | |
2325 | |
2326 (if (not (file-readable-p (cvs-fileinfo->backup-file fileinfo))) | |
2327 (error "%s has no backup file." | |
2328 (concat | |
2329 (file-name-as-directory (cvs-fileinfo->dir fileinfo)) | |
2330 (cvs-fileinfo->file-name fileinfo)))) | |
2331 (list (cvs-fileinfo->backup-file fileinfo) | |
2332 (cvs-fileinfo->file-name fileinfo))) | |
2333 | |
2334 ;;---------- | |
2335 (defun cvs-diff-vendor-extractor (fileinfo) | |
2336 "Retrieve and return the filenames of the vendor branch revisions as a list. | |
2337 Signal an error if there is no info for the vendor revisions." | |
2338 | |
2339 (list (cvs-retrieve-revision-to-tmpfile fileinfo | |
2340 (cvs-fileinfo->base-revision | |
2341 fileinfo)) | |
2342 (cvs-retrieve-revision-to-tmpfile fileinfo | |
2343 (cvs-fileinfo->head-revision | |
2344 fileinfo)))) | |
2345 | |
2346 ;;---------- | |
2347 (defun cvs-mode-find-file-other-window (pos) | |
2348 "Select a buffer containing the file in another window. | |
2349 Args: POS." | |
2350 | |
2351 (interactive "d") | |
2352 (let ((tin (tin-locate cvs-cookie-handle pos))) | |
2353 (if tin | |
2354 (let ((type (cvs-fileinfo->type (tin-cookie cvs-cookie-handle tin)))) | |
2355 (cond | |
2356 ((or (eq type 'REMOVED) | |
2357 (eq type 'CVS-REMOVED)) | |
2358 (error "Can't visit a removed file.")) | |
2359 ((eq type 'DIRCHANGE) | |
2360 (let ((obuf (current-buffer)) | |
2361 (odir default-directory)) | |
2362 (setq default-directory | |
2363 (file-name-as-directory | |
2364 (cvs-fileinfo->dir | |
2365 (tin-cookie cvs-cookie-handle tin)))) | |
2366 (dired-other-window default-directory) | |
2367 (set-buffer obuf) | |
2368 (setq default-directory odir))) | |
2369 (t | |
2370 (find-file-other-window (cvs-full-path tin))))) | |
2371 (error "There is no file to find.")))) | |
2372 | |
2373 ;;---------- | |
2374 (defun cvs-fileinfo->full-path (fileinfo) | |
2375 "Return the full path for the file that is described in FILEINFO." | |
2376 | |
2377 (concat | |
2378 (file-name-as-directory | |
2379 (cvs-fileinfo->dir fileinfo)) | |
2380 (cvs-fileinfo->file-name fileinfo))) | |
2381 | |
2382 ;;---------- | |
2383 (defun cvs-full-path (tin) | |
2384 "Return the full path for the file that is described in TIN." | |
2385 | |
2386 (cvs-fileinfo->full-path (tin-cookie cvs-cookie-handle tin))) | |
2387 | |
2388 ;;---------- | |
2389 (defun cvs-mode-find-file (pos) | |
2390 "Select a buffer containing the file in another window. | |
2391 Args: POS." | |
2392 | |
2393 (interactive "d") | |
2394 (let* ((cvs-buf (current-buffer)) | |
2395 (tin (tin-locate cvs-cookie-handle pos))) | |
2396 (if tin | |
2397 (let* ((fileinfo (tin-cookie cvs-cookie-handle tin)) | |
2398 (type (cvs-fileinfo->type fileinfo))) | |
2399 (cond | |
2400 ((or (eq type 'REMOVED) | |
2401 (eq type 'CVS-REMOVED)) | |
2402 (error "Can't visit a removed file.")) | |
2403 ((eq type 'DIRCHANGE) | |
2404 (let ((odir default-directory)) | |
2405 (setq default-directory | |
2406 (file-name-as-directory (cvs-fileinfo->dir fileinfo))) | |
2407 (dired default-directory) | |
2408 (set-buffer cvs-buf) | |
2409 (setq default-directory odir))) | |
2410 (t | |
2411 (find-file (cvs-full-path tin))))) | |
2412 (error "There is no file to find.")))) | |
2413 | |
2414 ;;---------- | |
2415 (defun cvs-mode-mark-all-files () | |
2416 "Mark all files. | |
2417 Directories are not marked." | |
2418 | |
2419 (interactive) | |
2420 (cookie-map (function (lambda (cookie) | |
2421 (cond | |
2422 ((not (eq (cvs-fileinfo->type cookie) 'DIRCHANGE)) | |
2423 (cvs-set-fileinfo->marked cookie t) | |
2424 t)))) | |
2425 cvs-cookie-handle)) | |
2426 | |
2427 ;;---------- | |
2428 (defun cvs-mode-unmark (pos) | |
2429 "Unmark a fileinfo. | |
2430 Args: POS." | |
2431 | |
2432 (interactive "d") | |
2433 (let* ((tin (tin-locate cvs-cookie-handle pos)) | |
2434 (sel (tin-cookie cvs-cookie-handle tin))) | |
2435 (cond | |
2436 ((eq (cvs-fileinfo->type sel) 'DIRCHANGE) | |
2437 (cookie-map | |
2438 (function (lambda (f dir) | |
2439 (cond | |
2440 ((cvs-dir-member-p f dir) | |
2441 (cvs-set-fileinfo->marked f nil) | |
2442 t)))) | |
2443 cvs-cookie-handle | |
2444 (cvs-fileinfo->dir sel))) | |
2445 (t | |
2446 (cvs-set-fileinfo->marked sel nil) | |
2447 (tin-invalidate cvs-cookie-handle tin) | |
2448 (tin-goto-next cvs-cookie-handle pos 1))))) | |
2449 | |
2450 ;;---------- | |
2451 (defun cvs-mode-unmark-all-files () | |
2452 "Unmark all files. | |
2453 Directories are also unmarked, but that doesn't matter, since | |
2454 they should always be unmarked." | |
2455 | |
2456 (interactive) | |
2457 (cookie-map (function (lambda (cookie) | |
2458 (cvs-set-fileinfo->marked cookie nil) | |
2459 t)) | |
2460 cvs-cookie-handle)) | |
2461 | |
2462 ;;---------- | |
2463 (defun cvs-do-removal (tins) | |
2464 "Remove files. | |
2465 Args: TINS. | |
2466 | |
2467 TINS is a list of tins that the user wants to delete. The files are deleted. | |
2468 If the type of the tin is 'UNKNOWN or 'UNKNOWN-DIR the tin is removed from the | |
2469 buffer. If it is anything else the file is added to a list that should be `cvs | |
2470 remove'd and the tin is changed to be of type 'REMOVED. | |
2471 | |
2472 Returns a list of tins files that should be `cvs remove'd." | |
2473 | |
2474 (cvs-use-temp-buffer) | |
2475 (mapcar 'cvs-insert-full-path tins) | |
2476 (cond | |
2477 ((and tins (yes-or-no-p (format "Delete %d files? " (length tins)))) | |
2478 (let (files-to-remove) | |
2479 (while tins | |
2480 (let* ((tin (car tins)) | |
2481 (fileinfo (tin-cookie cvs-cookie-handle tin)) | |
2482 (filepath (cvs-full-path tin)) | |
2483 (type (cvs-fileinfo->type fileinfo))) | |
2484 (if (or (eq type 'REMOVED) | |
2485 (eq type 'CVS-REMOVED)) | |
2486 nil | |
2487 ;; if it doesn't exist, as a file or directory, ignore it | |
2488 (cond ((file-directory-p filepath) | |
2489 (call-process cvs-rmdir-program nil nil nil filepath)) | |
2490 ((file-exists-p filepath) | |
2491 (delete-file filepath))) | |
2492 (if (or (eq type 'UNKNOWN) | |
2493 (eq type 'UNKNOWN-DIR) | |
2494 (eq type 'MOVE-AWAY)) | |
2495 (tin-delete cvs-cookie-handle tin) | |
2496 (setq files-to-remove (cons tin files-to-remove)) | |
2497 (cvs-set-fileinfo->type fileinfo 'REMOVED) | |
2498 (cvs-set-fileinfo->handled fileinfo nil) | |
2499 (tin-invalidate cvs-cookie-handle tin)))) | |
2500 (setq tins (cdr tins))) | |
2501 files-to-remove)) | |
2502 (t nil))) | |
2503 | |
2504 ;;---------- | |
2505 (defun cvs-mode-remove-file () | |
2506 "Remove all marked files." | |
2507 | |
2508 (interactive) | |
2509 (let ((files-to-remove (cvs-do-removal (cvs-get-marked)))) | |
2510 (if (null files-to-remove) | |
2511 nil | |
2512 (cvs-use-temp-buffer) | |
2513 (message "removing from repository...") | |
2514 (if (cvs-execute-list files-to-remove cvs-program | |
2515 (if cvs-cvsroot | |
2516 (list "-d" cvs-cvsroot "remove") | |
2517 '("remove")) | |
2518 "removing %s from repository...") | |
2519 (error "CVS exited with non-zero exit status.") | |
2520 (message "removing from repository... Done."))))) | |
2521 | |
2522 ;;---------- | |
2523 (defun cvs-mode-undo-local-changes () | |
2524 "Undo local changes to all marked files. | |
2525 The file is removed and `cvs update FILE' is run." | |
2526 | |
2527 (interactive) | |
2528 (let ((tins-to-undo (cvs-get-marked))) | |
2529 (cvs-use-temp-buffer) | |
2530 (mapcar 'cvs-insert-full-path tins-to-undo) | |
2531 (cond | |
2532 ((and tins-to-undo (yes-or-no-p (format "Undo changes to %d files? " | |
2533 (length tins-to-undo)))) | |
2534 (let (files-to-update) | |
2535 (while tins-to-undo | |
2536 (let* ((tin (car tins-to-undo)) | |
2537 (fileinfo (tin-cookie cvs-cookie-handle tin)) | |
2538 (type (cvs-fileinfo->type fileinfo))) | |
2539 (cond | |
2540 ((or | |
2541 (eq type 'UPDATED) | |
2542 (eq type 'PATCHED) | |
2543 (eq type 'MODIFIED) | |
2544 (eq type 'MERGED) | |
2545 (eq type 'CONFLICT) | |
2546 (eq type 'CVS-REMOVED) | |
2547 (eq type 'REM-CONFLICT) | |
2548 (eq type 'MOVE-AWAY) | |
2549 (eq type 'REMOVED)) | |
2550 (if (not (eq type 'REMOVED)) | |
2551 (delete-file (cvs-full-path tin))) | |
2552 (setq files-to-update (cons tin files-to-update)) | |
2553 (cvs-set-fileinfo->type fileinfo 'UPDATED) | |
2554 (cvs-set-fileinfo->handled fileinfo t) | |
2555 (tin-invalidate cvs-cookie-handle tin)) | |
2556 | |
2557 ((eq type 'MOD-CONFLICT) | |
2558 (error "Use cvs-mode-add instead on %s." | |
2559 (cvs-fileinfo->file-name fileinfo))) | |
2560 | |
2561 ((eq type 'REM-CONFLICT) | |
2562 (error "Can't deal with a file you have removed and recreated.")) | |
2563 | |
2564 ((eq type 'DIRCHANGE) | |
2565 (error "Undo on directories not supported (yet).")) | |
2566 | |
2567 ((eq type 'ADDED) | |
2568 (error "There is no old revision to get for %s" | |
2569 (cvs-fileinfo->file-name fileinfo))) | |
2570 (t (error "cvs-mode-undo-local-changes: can't handle an %s" | |
2571 type))) | |
2572 | |
2573 (setq tins-to-undo (cdr tins-to-undo)))) | |
2574 (cvs-use-temp-buffer) | |
2575 (message "Re-getting files from repository...") | |
2576 (if (cvs-execute-list files-to-update cvs-program | |
2577 (if cvs-cvsroot | |
2578 (list "-d" cvs-cvsroot "update") | |
2579 '("update")) | |
2580 "Re-getting %s from repository...") | |
2581 (error "CVS exited with non-zero exit status.") | |
2582 (message "Re-getting files from repository... Done."))))))) | |
2583 | |
2584 ;;---------- | |
2585 (defun cvs-mode-acknowledge () | |
2586 "Remove all marked files from the buffer." | |
2587 | |
2588 (interactive) | |
2589 (mapcar (function (lambda (tin) | |
2590 (tin-delete cvs-cookie-handle tin))) | |
2591 (cvs-get-marked))) | |
2592 | |
2593 ;;---------- | |
2594 (defun cvs-mode-unmark-up (pos) | |
2595 "Unmark the file on the previous line. | |
2596 Takes one argument POS, a buffer position." | |
2597 | |
2598 (interactive "d") | |
2599 (let ((tin (tin-goto-previous cvs-cookie-handle pos 1))) | |
2600 (cond | |
2601 (tin | |
2602 (cvs-set-fileinfo->marked (tin-cookie cvs-cookie-handle tin) | |
2603 nil) | |
2604 (tin-invalidate cvs-cookie-handle tin))))) | |
2605 | |
2606 ;;---------- | |
2607 (defun cvs-mode-previous-line (arg) | |
2608 "Go to the previous line. | |
2609 If a prefix argument is given, move by that many lines." | |
2610 | |
2611 (interactive "p") | |
2612 (tin-goto-previous cvs-cookie-handle (point) arg)) | |
2613 | |
2614 ;;---------- | |
2615 (defun cvs-mode-next-line (arg) | |
2616 "Go to the next line. | |
2617 If a prefix argument is given, move by that many lines." | |
2618 | |
2619 (interactive "p") | |
2620 (tin-goto-next cvs-cookie-handle (point) arg)) | |
2621 | |
2622 ;;---------- | |
2623 (defun cvs-add-file-update-buffer (tin) | |
2624 "Sub-function to cvs-mode-add. Internal use only. Update the display. Return | |
2625 non-nil if `cvs add' should be called on this file. | |
2626 Args: TIN. | |
2627 | |
2628 Returns 'DIR, 'ADD, 'ADD-DIR, or 'RESURRECT." | |
2629 | |
2630 (let ((fileinfo (tin-cookie cvs-cookie-handle tin))) | |
2631 (cond | |
2632 ((eq (cvs-fileinfo->type fileinfo) 'UNKNOWN-DIR) | |
2633 (cvs-set-fileinfo->full-log fileinfo "new directory added with cvs-mode-add") | |
2634 'ADD-DIR) | |
2635 ((eq (cvs-fileinfo->type fileinfo) 'UNKNOWN) | |
2636 (cvs-set-fileinfo->type fileinfo 'ADDED) | |
2637 (cvs-set-fileinfo->full-log fileinfo "new file added with cvs-mode-add") | |
2638 (tin-invalidate cvs-cookie-handle tin) | |
2639 'ADD) | |
2640 ((eq (cvs-fileinfo->type fileinfo) 'REMOVED) | |
2641 (cvs-set-fileinfo->type fileinfo 'UPDATED) | |
2642 (cvs-set-fileinfo->full-log fileinfo "file resurrected with cvs-mode-add") | |
2643 (cvs-set-fileinfo->handled fileinfo t) | |
2644 (tin-invalidate cvs-cookie-handle tin) | |
2645 'RESURRECT)))) | |
2646 | |
2647 ;;---------- | |
2648 (defun cvs-add-sub (cvs-buf candidates) | |
2649 "Internal use only. | |
2650 Args: CVS-BUF CANDIDATES. | |
2651 | |
2652 CANDIDATES is a list of tins. Updates the CVS-BUF and returns a list of lists. | |
2653 The first list is unknown tins that shall be `cvs add -m msg'ed. | |
2654 The second list is unknown directory tins that shall be `cvs add -m msg'ed. | |
2655 The third list is removed files that shall be `cvs add'ed (resurrected)." | |
2656 | |
2657 (let (add add-dir resurrect) | |
2658 (while candidates | |
2659 (let ((type (cvs-add-file-update-buffer (car candidates)))) | |
2660 (cond ((eq type 'ADD) | |
2661 (setq add (cons (car candidates) add))) | |
2662 ((eq type 'ADD-DIR) | |
2663 (setq add-dir (cons (car candidates) add-dir))) | |
2664 ((eq type 'RESURRECT) | |
2665 (setq resurrect (cons (car candidates) resurrect))))) | |
2666 (setq candidates (cdr candidates))) | |
2667 (list add add-dir resurrect))) | |
2668 | |
2669 ;;---------- | |
2670 (defun cvs-mode-add () | |
2671 "Add marked files to the cvs repository." | |
2672 | |
2673 (interactive) | |
2674 (let* ((buf (current-buffer)) | |
2675 (marked (cvs-get-marked)) | |
2676 (result (cvs-add-sub buf marked)) | |
2677 (added (car result)) | |
2678 (newdirs (car (cdr result))) | |
2679 (resurrect (car (cdr (cdr result)))) | |
2680 (msg (if (or added newdirs) | |
2681 (read-from-minibuffer "Enter description: ")))) | |
2682 | |
2683 (if (or resurrect (or added newdirs)) | |
2684 (cvs-use-temp-buffer)) | |
2685 | |
2686 (cond (resurrect | |
2687 (message "Resurrecting files from repository...") | |
2688 (if (cvs-execute-list resurrect | |
2689 cvs-program | |
2690 (if cvs-cvsroot | |
2691 (list "-d" cvs-cvsroot "add") | |
2692 '("add")) | |
2693 "Resurrecting %s from repository...") | |
2694 (error "CVS exited with non-zero exit status.") | |
2695 (message "Resurrecting files from repository... Done.")))) | |
2696 | |
2697 (cond (added | |
2698 (message "Adding new files to repository...") | |
2699 (if (cvs-execute-list added | |
2700 cvs-program | |
2701 (if cvs-cvsroot | |
2702 (list "-d" cvs-cvsroot "add" "-m" msg) | |
2703 (list "add" "-m" msg)) | |
2704 "Adding %s to repository...") | |
2705 (error "CVS exited with non-zero exit status.") | |
2706 (message "Adding new files to repository... Done.")))) | |
2707 | |
2708 (cond (newdirs | |
2709 (message "Adding new directories to repository...") | |
2710 (if (cvs-execute-list newdirs | |
2711 cvs-program | |
2712 (if cvs-cvsroot | |
2713 (list "-d" cvs-cvsroot "add" "-m" msg) | |
2714 (list "add" "-m" msg)) | |
2715 "Adding %s to repository...") | |
2716 (error "CVS exited with non-zero exit status.") | |
2717 (while newdirs | |
2718 (let* ((tin (car newdirs)) | |
2719 (fileinfo (tin-cookie cvs-cookie-handle tin)) | |
2720 (newdir (cvs-fileinfo->file-name fileinfo))) | |
2721 (cvs-set-fileinfo->dir fileinfo | |
2722 (concat (cvs-fileinfo->dir fileinfo) | |
2723 "/" | |
2724 newdir)) | |
2725 (cvs-set-fileinfo->type fileinfo 'DIRCHANGE) | |
2726 (cvs-set-fileinfo->file-name fileinfo ".") | |
2727 (tin-invalidate cvs-cookie-handle tin) | |
2728 (setq newdirs (cdr newdirs)))) | |
2729 ;; FIXME: this should really run cvs-update-no-prompt on the | |
2730 ;; subdir and insert everthing in the current list. | |
2731 (message "You must re-update to visit the new directories.")))))) | |
2732 | |
2733 ;;---------- | |
2734 (defun cvs-mode-ignore () | |
2735 "Arrange so that CVS ignores the selected files and directories. | |
2736 This command ignores files/dirs that are flagged as `Unknown'." | |
2737 | |
2738 (interactive) | |
2739 (mapcar (function (lambda (tin) | |
2740 (let* ((fileinfo (tin-cookie cvs-cookie-handle tin)) | |
2741 (type (cvs-fileinfo->type fileinfo))) | |
2742 (cond ((or (eq type 'UNKNOWN) | |
2743 (eq type 'UNKNOWN-DIR)) | |
2744 (cvs-append-to-ignore fileinfo) | |
2745 (tin-delete cvs-cookie-handle tin)))))) | |
2746 (cvs-get-marked))) | |
2747 | |
2748 ;;---------- | |
2749 (defun cvs-append-to-ignore (fileinfo) | |
2750 "Append the file in fileinfo to the .cvsignore file" | |
2751 | |
2752 (save-window-excursion | |
2753 (set-buffer (find-file-noselect (concat (file-name-as-directory | |
2754 (cvs-fileinfo->dir fileinfo)) | |
2755 ".cvsignore"))) | |
2756 (goto-char (point-max)) | |
2757 (if (not (zerop (current-column))) | |
2758 (insert "\n")) | |
2759 (insert (cvs-fileinfo->file-name fileinfo) "\n") | |
2760 (if cvs-sort-ignore-file | |
2761 (sort-lines nil (point-min) (point-max))) | |
2762 (save-buffer))) | |
2763 | |
2764 ;;---------- | |
2765 (defun cvs-mode-status () | |
2766 "Show cvs status for all marked files." | |
2767 | |
2768 (interactive) | |
2769 (save-some-buffers) | |
2770 (if (not (listp cvs-status-flags)) | |
2771 (error "cvs-status-flags should be set using cvs-set-status-flags.")) | |
2772 (let ((marked (cvs-get-marked nil t))) | |
2773 (cvs-use-temp-buffer) | |
2774 (message "Running cvs status ...") | |
2775 (if (cvs-execute-list marked | |
2776 cvs-program | |
2777 (append (if cvs-cvsroot (list "-d" cvs-cvsroot)) | |
2778 (list "-Q" "status") | |
2779 cvs-status-flags) | |
2780 "Running cvs -Q status %s...") | |
2781 (error "CVS exited with non-zero exit status.") | |
2782 (message "Running cvs -Q status ... Done.")))) | |
2783 | |
2784 ;;---------- | |
2785 (defun cvs-mode-log () | |
2786 "Display the cvs log of all selected files." | |
2787 | |
2788 (interactive) | |
2789 (if (not (listp cvs-log-flags)) | |
2790 (error "cvs-log-flags should be set using cvs-set-log-flags.")) | |
2791 (let ((marked (cvs-get-marked nil t))) | |
2792 (cvs-use-temp-buffer) | |
2793 (message "Running cvs log ...") | |
2794 (if (cvs-execute-list marked | |
2795 cvs-program | |
2796 (append (if cvs-cvsroot (list "-d" cvs-cvsroot)) | |
2797 (list "log") | |
2798 cvs-log-flags) | |
2799 "Running cvs log %s...") | |
2800 (error "CVS exited with non-zero exit status.") | |
2801 (message "Running cvs log ... Done.")))) | |
2802 | |
2803 ;;---------- | |
2804 (defun cvs-mode-tag () | |
2805 "Run 'cvs tag' on all selected files." | |
2806 | |
2807 (interactive) | |
2808 (if (not (listp cvs-tag-flags)) | |
2809 (error "cvs-tag-flags should be set using cvs-set-tag-flags.")) | |
2810 (let ((marked (cvs-get-marked nil t)) | |
2811 (tag-args (cvs-make-list (read-string "Tag name (and flags): ")))) | |
2812 (cvs-use-temp-buffer) | |
2813 (message "Running cvs tag ...") | |
2814 (if (cvs-execute-list marked | |
2815 cvs-program | |
2816 (append (if cvs-cvsroot (list "-d" cvs-cvsroot)) | |
2817 (list "tag") | |
2818 cvs-tag-flags | |
2819 tag-args) | |
2820 "Running cvs tag %s...") | |
2821 (error "CVS exited with non-zero exit status.") | |
2822 (message "Running cvs tag ... Done.")))) | |
2823 | |
2824 ;;---------- | |
2825 (defun cvs-mode-rtag () | |
2826 "Run 'cvs rtag' on all selected files." | |
2827 | |
2828 (interactive) | |
2829 (if (not (listp cvs-rtag-flags)) | |
2830 (error "cvs-rtag-flags should be set using cvs-set-rtag-flags.")) | |
2831 (let ((marked (cvs-get-marked nil t)) | |
2832 ;; FIXME: should give selection from the modules file | |
2833 (module-name (read-string "Module name: ")) | |
2834 ;; FIXME: should also ask for an existing tag *or* date | |
2835 (rtag-args (cvs-make-list (read-string "Tag name (and flags): ")))) | |
2836 (cvs-use-temp-buffer) | |
2837 (message "Running cvs rtag ...") | |
2838 (if (cvs-execute-list marked | |
2839 cvs-program | |
2840 (append (if cvs-cvsroot (list "-d" cvs-cvsroot)) | |
2841 (list "rtag") | |
2842 cvs-rtag-flags | |
2843 rtag-args | |
2844 (list module-name)) | |
2845 "Running cvs rtag %s...") | |
2846 (error "CVS rtag exited with non-zero exit status.") | |
2847 (message "Running cvs rtag ... Done.")))) | |
2848 | |
2849 ;;---------- | |
2850 (defun cvs-mode-byte-compile-files () | |
2851 "Run byte-compile-file on all selected files that end in '.el'." | |
2852 | |
2853 (interactive) | |
2854 (let ((marked (cvs-get-marked))) | |
2855 (while marked | |
2856 (let ((filename (cvs-full-path (car marked)))) | |
2857 (if (string-match "\\.el$" filename) | |
2858 (byte-compile-file filename))) | |
2859 (setq marked (cdr marked))))) | |
2860 | |
2861 ;;---------- | |
2862 (defun cvs-insert-full-path (tin) | |
2863 "Insert full path to the file described in TIN in the current buffer." | |
2864 | |
2865 (insert (format "%s\n" (cvs-full-path tin)))) | |
2866 | |
2867 ;;---------- | |
2868 (defun cvs-mode-add-change-log-entry-other-window (pos) | |
2869 "Add a ChangeLog entry in the ChangeLog of the current directory. | |
2870 Args: POS." | |
2871 | |
2872 (interactive "d") | |
2873 (let* ((cvs-buf (current-buffer)) | |
2874 (odir default-directory) | |
2875 (obfname buffer-file-name) | |
2876 (tin (tin-locate cvs-cookie-handle pos)) | |
2877 (fileinfo (tin-cookie cvs-cookie-handle tin)) | |
2878 (fname (cvs-fileinfo->file-name fileinfo)) | |
2879 (dname (file-name-as-directory (cvs-fileinfo->dir fileinfo)))) | |
2880 (setq change-log-default-name nil) ; this rarely correct in 19.28 | |
2881 (setq buffer-file-name (cond (fname | |
2882 fname) | |
2883 (t | |
2884 nil))) | |
2885 (setq default-directory (cond (dname | |
2886 dname) | |
2887 (t | |
2888 odir))) | |
2889 (add-change-log-entry-other-window) | |
2890 (set-buffer cvs-buf) | |
2891 (setq default-directory odir) | |
2892 (setq buffer-file-name obfname))) | |
2893 | |
2894 ;;---------- | |
2895 (defun print-cvs-tin (foo) | |
2896 "Debug utility." | |
2897 | |
2898 (let ((cookie (tin-cookie cvs-cookie-handle foo)) | |
2899 (stream (get-buffer-create "pcl-cvs-debug"))) | |
2900 (princ "==============\n" stream) | |
2901 (princ (cvs-fileinfo->file-name cookie) stream) | |
2902 (princ "\n" stream) | |
2903 (princ (cvs-fileinfo->dir cookie) stream) | |
2904 (princ "\n" stream) | |
2905 (princ (cvs-fileinfo->full-log cookie) stream) | |
2906 (princ "\n" stream) | |
2907 (princ (cvs-fileinfo->marked cookie) stream) | |
2908 (princ "\n" stream))) | |
2909 | |
2910 ;;---------- | |
2911 ;; NOTE: the variable cvs-emerge-tmp-head-file will be "free" when compiling | |
2912 (defun cvs-mode-emerge (pos) | |
2913 "Emerge appropriate revisions of the selected file. | |
2914 Args: POS." | |
2915 | |
2916 (interactive "d") | |
2917 (let* ((cvs-buf (current-buffer)) | |
2918 (tin (tin-locate cvs-cookie-handle pos))) | |
2919 (if (boundp 'cvs-emerge-tmp-head-file) | |
2920 (error "There can only be one emerge session active at a time.")) | |
2921 (if tin | |
2922 (let* ((fileinfo (tin-cookie cvs-cookie-handle tin)) | |
2923 (type (cvs-fileinfo->type fileinfo))) | |
2924 (cond | |
2925 ((eq type 'MODIFIED) ; merge repository head rev. with working file | |
2926 (require 'emerge) | |
2927 (setq cvs-emerge-tmp-head-file ; trick to prevent multiple runs | |
2928 (cvs-retrieve-revision-to-tmpfile fileinfo)) | |
2929 (unwind-protect | |
2930 (if (not (emerge-files | |
2931 t ; arg | |
2932 (cvs-fileinfo->full-path fileinfo) ; file-A | |
2933 ;; this is an un-avoidable compiler reference to a free variable | |
2934 cvs-emerge-tmp-head-file ; file-B | |
2935 (cvs-fileinfo->full-path fileinfo) ; file-out | |
2936 nil ; start-hooks | |
2937 '(lambda () ; quit-hooks | |
2938 (delete-file cvs-emerge-tmp-head-file) | |
2939 (makunbound 'cvs-emerge-tmp-head-file)))) | |
2940 (error "Emerge session failed")))) | |
2941 | |
2942 ;; re-do the same merge rcsmerge supposedly just did.... | |
2943 ((or (eq type 'MERGED) | |
2944 (eq type 'CONFLICT)) ; merge backup-working=A, head=B, base=ancestor | |
2945 (require 'emerge) | |
2946 (setq cvs-emerge-tmp-head-file ; trick to prevent multiple runs | |
2947 (cvs-retrieve-revision-to-tmpfile fileinfo | |
2948 (cvs-fileinfo->head-revision | |
2949 fileinfo))) | |
2950 (let ((cvs-emerge-tmp-backup-working-file | |
2951 (cvs-fileinfo->backup-file fileinfo)) | |
2952 (cvs-emerge-tmp-ancestor-file | |
2953 (cvs-retrieve-revision-to-tmpfile fileinfo | |
2954 (cvs-fileinfo->base-revision | |
2955 fileinfo)))) | |
2956 (unwind-protect | |
2957 (if (not (emerge-files-with-ancestor | |
2958 t ; arg | |
2959 cvs-emerge-tmp-backup-working-file ; file-A | |
2960 ;; this is an un-avoidable compiler reference to a free variable | |
2961 cvs-emerge-tmp-head-file ; file-B | |
2962 cvs-emerge-tmp-ancestor-file ; file-ancestor | |
2963 (cvs-fileinfo->full-path fileinfo) ; file-out | |
2964 nil ; start-hooks | |
2965 '(lambda () ; quit-hooks | |
2966 (delete-file cvs-emerge-tmp-backup-file) | |
2967 (delete-file cvs-emerge-tmp-ancestor-file) | |
2968 (delete-file cvs-emerge-tmp-head-file) | |
2969 (makunbound 'cvs-emerge-tmp-head-file)))) | |
2970 (error "Emerge session failed"))))) | |
2971 (t | |
2972 (error "Can only e-merge \"Modified\", \"Merged\" or \"Conflict\" files")))) | |
2973 (error "There is no file to e-merge.")))) | |
2974 | |
2975 ;;---------- | |
2976 ;; NOTE: the variable ediff-version may be "free" when compiling | |
2977 (defun cvs-mode-ediff (pos) | |
2978 "Ediff appropriate revisions of the selected file. | |
2979 Args: POS." | |
2980 | |
2981 (interactive "d") | |
2982 (if (boundp 'cvs-ediff-tmp-head-file) | |
2983 (error "There can only be one ediff session active at a time.")) | |
2984 (require 'ediff) | |
2985 (if (and (boundp 'ediff-version) | |
2986 (>= (string-to-number ediff-version) 2.0)) ; FIXME real number? | |
2987 (run-ediff-from-cvs-buffer pos) | |
2988 (cvs-old-ediff-interface pos))) | |
2989 | |
2990 (defun cvs-old-ediff-interface (pos) | |
2991 "Emerge like interface for older ediffs. | |
2992 Args: POS" | |
2993 | |
2994 (let* ((cvs-buf (current-buffer)) | |
2995 (tin (tin-locate cvs-cookie-handle pos))) | |
2996 (if tin | |
2997 (let* ((fileinfo (tin-cookie cvs-cookie-handle tin)) | |
2998 (type (cvs-fileinfo->type fileinfo))) | |
2999 (cond | |
3000 ((eq type 'MODIFIED) ; diff repository head rev. with working file | |
3001 ;; should this be inside the unwind-protect, and should the | |
3002 ;; makeunbound be an unwindform? | |
3003 (setq cvs-ediff-tmp-head-file ; trick to prevent multiple runs | |
3004 (cvs-retrieve-revision-to-tmpfile fileinfo)) | |
3005 (unwind-protect | |
3006 (if (not (ediff-files ; check correct ordering of args | |
3007 (cvs-fileinfo->full-path fileinfo) ; file-A | |
3008 ;; this is an un-avoidable compiler reference to a free variable | |
3009 cvs-ediff-tmp-head-file ; file-B | |
3010 '(lambda () ; startup-hooks | |
3011 (make-local-hook 'ediff-cleanup-hooks) | |
3012 (add-hook 'ediff-cleanup-hooks | |
3013 '(lambda () | |
3014 (ediff-janitor) | |
3015 (delete-file cvs-ediff-tmp-head-file) | |
3016 (makunbound 'cvs-ediff-tmp-head-file)) | |
3017 nil t)))) | |
3018 (error "Ediff session failed")))) | |
3019 | |
3020 ;; look at the merge rcsmerge supposedly just did.... | |
3021 ((or (eq type 'MERGED) | |
3022 (eq type 'CONFLICT)) ; diff backup-working=A, head=B, base=ancestor | |
3023 (if (not (boundp 'ediff-version)) | |
3024 (error "ediff version way too old for 3-way diff")) | |
3025 (if (<= (string-to-number ediff-version) 1.9) ; FIXME real number? | |
3026 (error "ediff version %s too old for 3-way diff" ediff-version)) | |
3027 (setq cvs-ediff-tmp-head-file ; trick to prevent multiple runs | |
3028 (cvs-retrieve-revision-to-tmpfile fileinfo | |
3029 (cvs-fileinfo->head-revision | |
3030 fileinfo))) | |
3031 (let ((cvs-ediff-tmp-backup-working-file | |
3032 (cvs-fileinfo->backup-file fileinfo)) | |
3033 (cvs-ediff-tmp-ancestor-file | |
3034 (cvs-retrieve-revision-to-tmpfile fileinfo | |
3035 (cvs-fileinfo->base-revision | |
3036 fileinfo)))) | |
3037 (unwind-protect | |
3038 (if (not (ediff-files3 ; check correct ordering of args | |
3039 cvs-ediff-tmp-backup-working-file ; file-A | |
3040 ;; this is an un-avoidable compiler reference to a free variable | |
3041 cvs-ediff-tmp-head-file ; file-B | |
3042 cvs-ediff-tmp-ancestor-file ; file-ancestor | |
3043 '(lambda () ; start-hooks | |
3044 (make-local-hook 'ediff-cleanup-hooks) | |
3045 (add-hook 'ediff-cleanup-hooks | |
3046 '(lambda () | |
3047 (ediff-janitor) | |
3048 (delete-file cvs-ediff-tmp-backup-file) | |
3049 (delete-file cvs-ediff-tmp-ancestor-file) | |
3050 (delete-file cvs-ediff-tmp-head-file) | |
3051 (makunbound 'cvs-ediff-tmp-head-file)) | |
3052 nil t)))) | |
3053 (error "Ediff session failed"))))) | |
3054 | |
3055 ((not (or (eq type 'UNKNOWN) | |
3056 (eq type 'UNKNOWN-DIR))) ; i.e. UPDATED or PATCHED ???? | |
3057 ;; this should really diff the current working file with the previous | |
3058 ;; rev. on the current branch (i.e. not the head, since that's what | |
3059 ;; the current file should be) | |
3060 (setq cvs-ediff-tmp-head-file ; trick to prevent multiple runs | |
3061 (cvs-retrieve-revision-to-tmpfile fileinfo | |
3062 (read-string "Rev #/tag to diff against: " | |
3063 (cvs-fileinfo->head-revision | |
3064 fileinfo)))) | |
3065 (unwind-protect | |
3066 (if (not (ediff-files ; check correct ordering of args | |
3067 (cvs-fileinfo->full-path fileinfo) ; file-A | |
3068 ;; this is an un-avoidable compiler reference to a free variable | |
3069 cvs-ediff-tmp-head-file ; file-B | |
3070 '(lambda () ; startup-hooks | |
3071 (make-local-hook 'ediff-cleanup-hooks) | |
3072 (add-hook 'ediff-cleanup-hooks | |
3073 '(lambda () | |
3074 (ediff-janitor) | |
3075 (delete-file cvs-ediff-tmp-head-file) | |
3076 (makunbound 'cvs-ediff-tmp-head-file)) | |
3077 nil t)))) | |
3078 (error "Ediff session failed")))) | |
3079 (t | |
3080 (error "Can not ediff \"Unknown\" files")))) | |
3081 (error "There is no file to ediff.")))) | |
3082 | |
3083 ;;---------- | |
3084 (defun cvs-retrieve-revision-to-tmpfile (fileinfo &optional revision) | |
3085 "Retrieve the latest revision of the file in FILEINFO to a temporary file. | |
3086 If second optional argument REVISION is given, retrieve that revision instead." | |
3087 | |
3088 (let | |
3089 ((temp-name (make-temp-name | |
3090 (concat (file-name-as-directory | |
3091 (or (getenv "TMPDIR") "/tmp")) | |
3092 "pcl-cvs." revision)))) | |
3093 (cvs-kill-buffer-visiting temp-name) | |
3094 (if (and revision | |
3095 (stringp revision) | |
3096 (not (string= revision ""))) | |
3097 (message "Retrieving revision %s..." revision) | |
3098 (message "Retrieving latest revision...")) | |
3099 (let ((res (call-process cvs-shell nil nil nil "-c" | |
3100 (concat cvs-program " update -p " | |
3101 (if (and revision | |
3102 (stringp revision) | |
3103 (not (string= revision ""))) | |
3104 (concat "-r " revision " ") | |
3105 "") | |
3106 (cvs-fileinfo->full-path fileinfo) | |
3107 " > " temp-name)))) | |
3108 (if (and res (not (and (integerp res) (zerop res)))) | |
3109 (error "Something went wrong retrieving revision %s: %s" | |
3110 revision res)) | |
3111 | |
3112 (if revision | |
3113 (message "Retrieving revision %s... Done." revision) | |
3114 (message "Retrieving latest revision... Done.")) | |
3115 (save-excursion | |
3116 (set-buffer (find-file-noselect temp-name)) | |
3117 (rename-buffer (concat " " (file-name-nondirectory temp-name)) t)) | |
3118 temp-name))) | |
3119 | |
3120 ;;---------- | |
3121 (defun cvs-kill-buffer-visiting (filename) | |
3122 "If there is any buffer visiting FILENAME, kill it (without confirmation)." | |
3123 | |
3124 (let ((l (buffer-list))) | |
3125 (while l | |
3126 (if (string= (buffer-file-name (car l)) filename) | |
3127 (kill-buffer (car l))) | |
3128 (setq l (cdr l))))) | |
3129 | |
3130 ;;---------- | |
3131 (defun cvs-change-cvsroot () | |
3132 "Ask for a new cvsroot." | |
3133 | |
3134 (interactive) | |
3135 (cvs-set-cvsroot (read-file-name "New CVSROOT: " cvs-cvsroot))) | |
3136 | |
3137 ;;---------- | |
3138 (defun cvs-set-cvsroot (newroot) | |
3139 "Change the cvsroot." | |
3140 | |
3141 (if (or (file-directory-p (expand-file-name "CVSROOT" newroot)) | |
3142 (y-or-n-p (concat "Warning: no CVSROOT found inside repository." | |
3143 " Change cvs-cvsroot anyhow?"))) | |
3144 (setq cvs-cvsroot newroot))) | |
3145 | |
3146 ;;---------- | |
3147 (defun cvs-set-diff-flags () | |
3148 "Ask for new setting of cvs-diff-flags." | |
3149 | |
3150 (interactive) | |
3151 (let ((old-value (mapconcat 'identity | |
3152 (copy-sequence cvs-diff-flags) " "))) | |
3153 (setq cvs-diff-flags | |
3154 (cvs-make-list (read-string "Diff flags: " old-value))))) | |
3155 | |
3156 ;;---------- | |
3157 (defun cvs-set-update-optional-flags () | |
3158 "Ask for new setting of cvs-update-optional-flags." | |
3159 | |
3160 (interactive) | |
3161 (let ((old-value (mapconcat 'identity | |
3162 (copy-sequence cvs-update-optional-flags) " "))) | |
3163 (setq cvs-update-optional-flags | |
3164 (cvs-make-list (read-string "Update optional flags: " old-value))))) | |
3165 | |
3166 ;;---------- | |
3167 (defun cvs-set-status-flags () | |
3168 "Ask for new setting of cvs-status-flags." | |
3169 | |
3170 (interactive) | |
3171 (let ((old-value (mapconcat 'identity | |
3172 (copy-sequence cvs-status-flags) " "))) | |
3173 (setq cvs-status-flags | |
3174 (cvs-make-list (read-string "Status flags: " old-value))))) | |
3175 | |
3176 ;;---------- | |
3177 (defun cvs-set-log-flags () | |
3178 "Ask for new setting of cvs-log-flags." | |
3179 | |
3180 (interactive) | |
3181 (let ((old-value (mapconcat 'identity | |
3182 (copy-sequence cvs-log-flags) " "))) | |
3183 (setq cvs-log-flags | |
3184 (cvs-make-list (read-string "Log flags: " old-value))))) | |
3185 | |
3186 ;;---------- | |
3187 (defun cvs-set-tag-flags () | |
3188 "Ask for new setting of cvs-tag-flags." | |
3189 | |
3190 (interactive) | |
3191 (let ((old-value (mapconcat 'identity | |
3192 (copy-sequence cvs-tag-flags) " "))) | |
3193 (setq cvs-tag-flags | |
3194 (cvs-make-list (read-string "Tag flags: " old-value))))) | |
3195 | |
3196 ;;---------- | |
3197 (defun cvs-set-rtag-flags () | |
3198 "Ask for new setting of cvs-rtag-flags." | |
3199 | |
3200 (interactive) | |
3201 (let ((old-value (mapconcat 'identity | |
3202 (copy-sequence cvs-rtag-flags) " "))) | |
3203 (setq cvs-rtag-flags | |
3204 (cvs-make-list (read-string "Rtag flags: " old-value))))) | |
3205 | |
3206 ;;---------- | |
3207 (if (string-match "XEmacs" emacs-version) | |
3208 (progn | |
3209 ;; now marked to autload in pcl-cvs-xemacs | |
3210 ;(autoload 'pcl-cvs-fontify "pcl-cvs-xemacs") | |
3211 (add-hook 'cvs-mode-hook 'pcl-cvs-fontify))) | |
3212 | |
3213 (defun cvs-changelog-name (directory) | |
3214 "Return the name of the ChangeLog file that handles DIRECTORY. | |
3215 This is in DIRECTORY or one of its parents. | |
3216 Signal an error if we can't find an appropriate ChangeLog file." | |
3217 (let ((dir (file-name-as-directory directory)) | |
3218 file) | |
3219 (while (and dir | |
3220 (not (file-exists-p | |
3221 (setq file (expand-file-name "ChangeLog" dir))))) | |
3222 (let ((last dir)) | |
3223 (setq dir (file-name-directory (directory-file-name dir))) | |
3224 (if (equal last dir) | |
3225 (setq dir nil)))) | |
3226 (or dir | |
3227 (error "Can't find ChangeLog for %s" directory)) | |
3228 file)) | |
3229 | |
3230 (defun cvs-narrow-changelog () | |
3231 "Narrow to the top page of the current buffer, a ChangeLog file. | |
3232 Actually, the narrowed region doesn't include the date line. | |
3233 A \"page\" in a ChangeLog file is the area between two dates." | |
3234 (or (eq major-mode 'change-log-mode) | |
3235 (error "cvs-narrow-changelog: current buffer isn't a ChangeLog")) | |
3236 | |
3237 (goto-char (point-min)) | |
3238 | |
3239 ;; Skip date line and subsequent blank lines. | |
3240 (forward-line 1) | |
3241 (if (looking-at "[ \t\n]*\n") | |
3242 (goto-char (match-end 0))) | |
3243 | |
3244 (let ((start (point))) | |
3245 (forward-page 1) | |
3246 (narrow-to-region start (point)) | |
3247 (goto-char (point-min)))) | |
3248 | |
3249 (defun cvs-changelog-paragraph () | |
3250 "Return the bounds of the ChangeLog paragraph containing point. | |
3251 If we are between paragraphs, return the previous paragraph." | |
3252 (save-excursion | |
3253 (beginning-of-line) | |
3254 (if (looking-at "^[ \t]*$") | |
3255 (skip-chars-backward " \t\n" (point-min))) | |
3256 (list (progn | |
3257 (if (re-search-backward "^[ \t]*\n" nil 'or-to-limit) | |
3258 (goto-char (match-end 0))) | |
3259 (point)) | |
3260 (if (re-search-forward "^[ \t\n]*$" nil t) | |
3261 (match-beginning 0) | |
3262 (point))))) | |
3263 | |
3264 (defun cvs-changelog-subparagraph () | |
3265 "Return the bounds of the ChangeLog subparagraph containing point. | |
3266 A subparagraph is a block of non-blank lines beginning with an asterisk. | |
3267 If we are between sub-paragraphs, return the previous subparagraph." | |
3268 (save-excursion | |
3269 (end-of-line) | |
3270 (if (search-backward "*" nil t) | |
3271 (list (progn (beginning-of-line) (point)) | |
3272 (progn | |
3273 (forward-line 1) | |
3274 (if (re-search-forward "^[ \t]*[\n*]" nil t) | |
3275 (match-beginning 0) | |
3276 (point-max)))) | |
3277 (list (point) (point))))) | |
3278 | |
3279 (defun cvs-changelog-entry () | |
3280 "Return the bounds of the ChangeLog entry containing point. | |
3281 The variable `cvs-changelog-full-paragraphs' decides whether an | |
3282 \"entry\" is a paragraph or a subparagraph; see its documentation string | |
3283 for more details." | |
3284 (if cvs-changelog-full-paragraphs | |
3285 (cvs-changelog-paragraph) | |
3286 (cvs-changelog-subparagraph))) | |
3287 | |
3288 ;; NOTE: the variable user-full-name may be "free" when compiling | |
3289 (defun cvs-changelog-ours-p () | |
3290 "See if ChangeLog entry at point is for the current user, today. | |
3291 Return non-nil iff it is." | |
3292 ;; Code adapted from add-change-log-entry. | |
3293 (looking-at (concat (regexp-quote (substring (current-time-string) | |
3294 0 10)) | |
3295 ".* " | |
3296 (regexp-quote (substring (current-time-string) -4)) | |
3297 "[ \t]+" | |
3298 (regexp-quote (if (and (boundp 'add-log-full-name) | |
3299 add-log-full-name) | |
3300 add-log-full-name | |
3301 user-full-name)) | |
3302 " <" | |
3303 (regexp-quote (if (and (boundp 'add-log-mailing-address) | |
3304 add-log-mailing-address) | |
3305 add-log-mailing-address | |
3306 user-mail-address))))) | |
3307 | |
3308 (defun cvs-relative-path (base child) | |
3309 "Return a directory path relative to BASE for CHILD. | |
3310 If CHILD doesn't seem to be in a subdirectory of BASE, just return | |
3311 the full path to CHILD." | |
3312 (let ((base (file-name-as-directory (expand-file-name base))) | |
3313 (child (expand-file-name child))) | |
3314 (or (string= base (substring child 0 (length base))) | |
3315 (error "cvs-relative-path: %s isn't in %s" child base)) | |
3316 (substring child (length base)))) | |
3317 | |
3318 (defun cvs-changelog-entries (file) | |
3319 "Return the ChangeLog entries for FILE, and the ChangeLog they came from. | |
3320 The return value looks like this: | |
3321 (LOGBUFFER (ENTRYSTART . ENTRYEND) ...) | |
3322 where LOGBUFFER is the name of the ChangeLog buffer, and each | |
3323 \(ENTRYSTART . ENTRYEND\) pair is a buffer region." | |
3324 (save-excursion | |
3325 (set-buffer (find-file-noselect | |
3326 (cvs-changelog-name | |
3327 (file-name-directory | |
3328 (expand-file-name file))))) | |
3329 (or (eq major-mode 'change-log-mode) | |
3330 (change-log-mode)) | |
3331 (goto-char (point-min)) | |
3332 (if (looking-at "[ \t\n]*\n") | |
3333 (goto-char (match-end 0))) | |
3334 (if (not (cvs-changelog-ours-p)) | |
3335 (list (current-buffer)) | |
3336 (save-restriction | |
3337 (cvs-narrow-changelog) | |
3338 (goto-char (point-min)) | |
3339 | |
3340 ;; Search for the name of FILE relative to the ChangeLog. If that | |
3341 ;; doesn't occur anywhere, they're not using full relative | |
3342 ;; filenames in the ChangeLog, so just look for FILE; we'll accept | |
3343 ;; some false positives. | |
3344 (let ((pattern (cvs-relative-path | |
3345 (file-name-directory buffer-file-name) file))) | |
3346 (if (or (string= pattern "") | |
3347 (not (save-excursion | |
3348 (search-forward pattern nil t)))) | |
3349 (setq pattern file)) | |
3350 | |
3351 (let (texts) | |
3352 (while (search-forward pattern nil t) | |
3353 (let ((entry (cvs-changelog-entry))) | |
3354 (setq texts (cons entry texts)) | |
3355 (goto-char (elt entry 1)))) | |
3356 | |
3357 (cons (current-buffer) texts))))))) | |
3358 | |
3359 (defun cvs-changelog-insert-entries (buffer regions) | |
3360 "Insert those regions in BUFFER specified in REGIONS. | |
3361 Sort REGIONS front-to-back first." | |
3362 (let ((regions (sort regions 'car-less-than-car)) | |
3363 (last)) | |
3364 (while regions | |
3365 (if (and last (< last (car (car regions)))) | |
3366 (newline)) | |
3367 (setq last (elt (car regions) 1)) | |
3368 (apply 'insert-buffer-substring buffer (car regions)) | |
3369 (setq regions (cdr regions))))) | |
3370 | |
3371 (defun cvs-union (set1 set2) | |
3372 "Return the union of SET1 and SET2, according to `equal'." | |
3373 (while set2 | |
3374 (or (member (car set2) set1) | |
3375 (setq set1 (cons (car set2) set1))) | |
3376 (setq set2 (cdr set2))) | |
3377 set1) | |
3378 | |
3379 (defun cvs-insert-changelog-entries (files) | |
3380 "Given a list of files FILES, insert the ChangeLog entries for them." | |
3381 (let ((buffer-entries nil)) | |
3382 | |
3383 ;; Add each buffer to buffer-entries, and associate it with the list | |
3384 ;; of entries we want from that file. | |
3385 (while files | |
3386 (let* ((entries (cvs-changelog-entries (car files))) | |
3387 (pair (assq (car entries) buffer-entries))) | |
3388 (if pair | |
3389 (setcdr pair (cvs-union (cdr pair) (cdr entries))) | |
3390 (setq buffer-entries (cons entries buffer-entries)))) | |
3391 (setq files (cdr files))) | |
3392 | |
3393 ;; Now map over each buffer in buffer-entries, sort the entries for | |
3394 ;; each buffer, and extract them as strings. | |
3395 (while buffer-entries | |
3396 (cvs-changelog-insert-entries (car (car buffer-entries)) | |
3397 (cdr (car buffer-entries))) | |
3398 (if (and (cdr buffer-entries) (cdr (car buffer-entries))) | |
3399 (newline)) | |
3400 (setq buffer-entries (cdr buffer-entries))))) | |
3401 | |
3402 (defun cvs-edit-delete-common-indentation () | |
3403 "Unindent the current buffer rigidly until at least one line is flush left." | |
3404 (save-excursion | |
3405 (let ((common 100000)) | |
3406 (goto-char (point-min)) | |
3407 (while (< (point) (point-max)) | |
3408 (if (not (looking-at "^[ \t]*$")) | |
3409 (setq common (min common (current-indentation)))) | |
3410 (forward-line 1)) | |
3411 (indent-rigidly (point-min) (point-max) (- common))))) | |
3412 | |
3413 (defun cvs-mode-changelog-commit () | |
3414 "Check in all marked files, or the current file. | |
3415 Ask the user for a log message in a buffer. | |
3416 | |
3417 This is just like `\\[cvs-mode-commit]', except that it tries to provide | |
3418 appropriate default log messages by looking at the ChangeLog. The | |
3419 idea is to write your ChangeLog entries first, and then use this | |
3420 command to commit your changes. | |
3421 | |
3422 To select default log text, we: | |
3423 - find the ChangeLog entries for the files to be checked in, | |
3424 - verify that the top entry in the ChangeLog is on the current date | |
3425 and by the current user; if not, we don't provide any default text, | |
3426 - search the ChangeLog entry for paragraphs containing the names of | |
3427 the files we're checking in, and finally | |
3428 - use those paragraphs as the log text." | |
3429 | |
3430 (interactive) | |
3431 | |
3432 (let* ((cvs-buf (current-buffer)) | |
3433 (marked (cvs-filter (function cvs-committable) | |
3434 (cvs-get-marked)))) | |
3435 (if (null marked) | |
3436 (error "Nothing to commit!") | |
3437 (pop-to-buffer (get-buffer-create cvs-commit-prompt-buffer)) | |
3438 (goto-char (point-min)) | |
3439 | |
3440 (erase-buffer) | |
3441 (cvs-insert-changelog-entries | |
3442 (mapcar (lambda (tin) | |
3443 (let ((cookie (tin-cookie cvs-cookie-handle tin))) | |
3444 (expand-file-name | |
3445 (cvs-fileinfo->file-name cookie) | |
3446 (cvs-fileinfo->dir cookie)))) | |
3447 marked)) | |
3448 (cvs-edit-delete-common-indentation) | |
3449 | |
3450 (cvs-edit-mode) | |
3451 (make-local-variable 'cvs-commit-list) | |
3452 (setq cvs-commit-list marked) | |
3453 (message "Press C-c C-c when you are done editing.")))) | |
3454 | |
3455 ;;;; end of file pcl-cvs.el |