Merge from emacs-23
[bpt/emacs.git] / lisp / vc / pcvs-info.el
CommitLineData
3afbc435 1;;; pcvs-info.el --- internal representation of a fileinfo entry
5b467bf4 2
45ce5942 3;; Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
5df4f04c 4;; 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
cd6ef82d 5;; Free Software Foundation, Inc.
5b467bf4 6
cc1eecfd 7;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
5b467bf4 8;; Keywords: pcl-cvs
bd78fa1d 9;; Package: pcvs
5b467bf4
SM
10
11;; This file is part of GNU Emacs.
12
eb3fa2cf 13;; GNU Emacs is free software: you can redistribute it and/or modify
5b467bf4 14;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
5b467bf4
SM
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
eb3fa2cf 24;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
5b467bf4
SM
25
26;;; Commentary:
27
28;; The cvs-fileinfo data structure:
29;;
30;; When the `cvs update' is ready we parse the output. Every file
31;; that is affected in some way is added to the cookie collection as
32;; a "fileinfo" (as defined below in cvs-create-fileinfo).
33
34;;; Code:
35
36(eval-when-compile (require 'cl))
37(require 'pcvs-util)
38;;(require 'pcvs-defs)
39
40;;;;
41;;;; config variables
42;;;;
43
82061ebf
JB
44(define-obsolete-variable-alias 'cvs-display-full-path
45 'cvs-display-full-name "22.1")
cd6ef82d 46
03de06da 47(defcustom cvs-display-full-name t
9201cc28 48 "Specifies how the filenames should be displayed in the listing.
03de06da
SM
49If non-nil, their full filename name will be displayed, else only the
50non-directory part."
5b467bf4
SM
51 :group 'pcl-cvs
52 :type '(boolean))
53
5b467bf4 54(defcustom cvs-allow-dir-commit nil
9201cc28 55 "Allow `cvs-mode-commit' on directories.
5b467bf4
SM
56If you commit without any marked file and with the cursor positioned
57on a directory entry, cvs would commit the whole directory. This seems
58to confuse some users sometimes."
59 :group 'pcl-cvs
60 :type '(boolean))
61
5b467bf4
SM
62;;;;
63;;;; Faces for fontification
64;;;;
65
2058218e 66(defface cvs-header
5b467bf4 67 '((((class color) (background dark))
58b64ac7 68 (:foreground "lightyellow" :weight bold))
5b467bf4 69 (((class color) (background light))
58b64ac7
RS
70 (:foreground "blue4" :weight bold))
71 (t (:weight bold)))
5b467bf4
SM
72 "PCL-CVS face used to highlight directory changes."
73 :group 'pcl-cvs)
c4f6e489 74(define-obsolete-face-alias 'cvs-header-face 'cvs-header "22.1")
5b467bf4 75
2058218e 76(defface cvs-filename
5b467bf4
SM
77 '((((class color) (background dark))
78 (:foreground "lightblue"))
79 (((class color) (background light))
80 (:foreground "blue4"))
81 (t ()))
82 "PCL-CVS face used to highlight file names."
83 :group 'pcl-cvs)
c4f6e489 84(define-obsolete-face-alias 'cvs-filename-face 'cvs-filename "22.1")
5b467bf4 85
2058218e 86(defface cvs-unknown
5b467bf4 87 '((((class color) (background dark))
5183d4c9 88 (:foreground "red1"))
5b467bf4 89 (((class color) (background light))
5183d4c9 90 (:foreground "red1"))
58b64ac7 91 (t (:slant italic)))
5b467bf4
SM
92 "PCL-CVS face used to highlight unknown file status."
93 :group 'pcl-cvs)
c4f6e489 94(define-obsolete-face-alias 'cvs-unknown-face 'cvs-unknown "22.1")
5b467bf4 95
2058218e 96(defface cvs-handled
5b467bf4
SM
97 '((((class color) (background dark))
98 (:foreground "pink"))
99 (((class color) (background light))
100 (:foreground "pink"))
101 (t ()))
102 "PCL-CVS face used to highlight handled file status."
103 :group 'pcl-cvs)
c4f6e489 104(define-obsolete-face-alias 'cvs-handled-face 'cvs-handled "22.1")
5b467bf4 105
2058218e 106(defface cvs-need-action
5b467bf4
SM
107 '((((class color) (background dark))
108 (:foreground "orange"))
109 (((class color) (background light))
110 (:foreground "orange"))
58b64ac7 111 (t (:slant italic)))
5b467bf4
SM
112 "PCL-CVS face used to highlight status of files needing action."
113 :group 'pcl-cvs)
c4f6e489 114(define-obsolete-face-alias 'cvs-need-action-face 'cvs-need-action "22.1")
5b467bf4 115
2058218e 116(defface cvs-marked
ea81d57e
DN
117 '((((min-colors 88) (class color) (background dark))
118 (:foreground "green1" :weight bold))
119 (((class color) (background dark))
58b64ac7 120 (:foreground "green" :weight bold))
5b467bf4 121 (((class color) (background light))
58b64ac7
RS
122 (:foreground "green3" :weight bold))
123 (t (:weight bold)))
5b467bf4
SM
124 "PCL-CVS face used to highlight marked file indicator."
125 :group 'pcl-cvs)
c4f6e489 126(define-obsolete-face-alias 'cvs-marked-face 'cvs-marked "22.1")
5b467bf4 127
2058218e 128(defface cvs-msg
58b64ac7 129 '((t (:slant italic)))
5b467bf4
SM
130 "PCL-CVS face used to highlight CVS messages."
131 :group 'pcl-cvs)
c4f6e489 132(define-obsolete-face-alias 'cvs-msg-face 'cvs-msg "22.1")
5b467bf4 133
2058218e
MB
134(defvar cvs-fi-up-to-date-face 'cvs-handled)
135(defvar cvs-fi-unknown-face 'cvs-unknown)
53921158 136(defvar cvs-fi-conflict-face 'font-lock-warning-face)
5b467bf4
SM
137
138;; There is normally no need to alter the following variable, but if
139;; your site has installed CVS in a non-standard way you might have
140;; to change it.
141
142(defvar cvs-bakprefix ".#"
143 "The prefix that CVS prepends to files when rcsmerge'ing.")
144
5b467bf4 145(easy-mmode-defmap cvs-status-map
1a8bd90a 146 '(([(mouse-2)] . cvs-mode-toggle-mark))
1fe28d30 147 "Local keymap for text properties of status")
5b467bf4
SM
148
149;; Constructor:
150
151(defstruct (cvs-fileinfo
152 (:constructor nil)
153 (:copier nil)
154 (:constructor -cvs-create-fileinfo (type dir file full-log
155 &key marked subtype
156 merge
157 base-rev
158 head-rev))
159 (:conc-name cvs-fileinfo->))
160 marked ;; t/nil.
161 type ;; See below
162 subtype ;; See below
163 dir ;; Relative directory the file resides in.
164 ;; (concat dir file) should give a valid path.
165 file ;; The file name sans the directory.
166 base-rev ;; During status: This is the revision that the
167 ;; working file is based on.
168 head-rev ;; During status: This is the highest revision in
169 ;; the repository.
170 merge ;; A cons cell containing the (ancestor . head) revisions
171 ;; of the merge that resulted in the current file.
172 ;;removed ;; t if the file no longer exists.
173 full-log ;; The output from cvs, unparsed.
174 ;;mod-time ;; Not used.
175
176 ;; In addition to the above, the following values can be extracted:
177
178 ;; handled ;; t if this file doesn't require further action.
03de06da 179 ;; full-name ;; The complete relative filename.
5b467bf4
SM
180 ;; pp-name ;; The printed file name
181 ;; backup-file;; For MERGED and CONFLICT files after a \"cvs update\",
182 ;; this is a full path to the backup file where the
183 ;; untouched version resides.
184
185 ;; The meaning of the type field:
f1180544 186
5b467bf4
SM
187 ;; Value ---Used by--- Explanation
188 ;; update status
189 ;; NEED-UPDATE x file needs update
190 ;; MODIFIED x x modified by you, unchanged in repository
191 ;; MERGED x x successful merge
192 ;; ADDED x x added by you, not yet committed
193 ;; MISSING x rm'd, but not yet `cvs remove'd
194 ;; REMOVED x x removed by you, not yet committed
195 ;; NEED-MERGE x need merge
196 ;; CONFLICT x conflict when merging
197 ;; ;;MOD-CONFLICT x removed locally, changed in repository.
198 ;; DIRCHANGE x x A change of directory.
199 ;; UNKNOWN x An unknown file.
200 ;; UP-TO-DATE x The file is up-to-date.
201 ;; UPDATED x x file copied from repository
202 ;; PATCHED x x diff applied from repository
203 ;; COMMITTED x x cvs commit'd
204 ;; DEAD An entry that should be removed
205 ;; MESSAGE x x This is a special fileinfo that is used
206 ;; to display a text that should be in
207 ;; full-log."
208 ;; TEMP A temporary message that should be removed
5b467bf4
SM
209 )
210(defun cvs-create-fileinfo (type dir file msg &rest keys)
211 (cvs-check-fileinfo (apply #'-cvs-create-fileinfo type dir file msg keys)))
212
213;; Fake selectors:
214
03de06da 215(defun cvs-fileinfo->full-name (fileinfo)
5b467bf4
SM
216 "Return the full path for the file that is described in FILEINFO."
217 (let ((dir (cvs-fileinfo->dir fileinfo)))
218 (if (eq (cvs-fileinfo->type fileinfo) 'DIRCHANGE)
219 (if (string= dir "") "." (directory-file-name dir))
220 ;; Here, I use `concat' rather than `expand-file-name' because I want
221 ;; the resulting path to stay relative if `dir' is relative.
5b467bf4 222 (concat dir (cvs-fileinfo->file fileinfo)))))
82061ebf
JB
223(define-obsolete-function-alias 'cvs-fileinfo->full-path
224 'cvs-fileinfo->full-name "22.1")
5b467bf4
SM
225
226(defun cvs-fileinfo->pp-name (fi)
227 "Return the filename of FI as it should be displayed."
03de06da
SM
228 (if cvs-display-full-name
229 (cvs-fileinfo->full-name fi)
5b467bf4
SM
230 (cvs-fileinfo->file fi)))
231
232(defun cvs-fileinfo->backup-file (fileinfo)
233 "Construct the file name of the backup file for FILEINFO."
234 (let* ((dir (cvs-fileinfo->dir fileinfo))
235 (file (cvs-fileinfo->file fileinfo))
236 (default-directory (file-name-as-directory (expand-file-name dir)))
237 (files (directory-files "." nil
449b6104
SM
238 (concat "\\`" (regexp-quote cvs-bakprefix)
239 (regexp-quote file) "\\(\\.[0-9]+\\.[0-9]+\\)+\\'")))
5b467bf4 240 bf)
03de06da 241 (dolist (f files)
5b467bf4
SM
242 (when (and (file-readable-p f)
243 (or (null bf) (file-newer-than-file-p f bf)))
03de06da
SM
244 (setq bf f)))
245 (concat dir bf)))
5b467bf4
SM
246
247;; (defun cvs-fileinfo->handled (fileinfo)
248;; "Tell if this requires further action"
249;; (memq (cvs-fileinfo->type fileinfo) '(UP-TO-DATE DEAD)))
250
251\f
252;; Predicate:
253
5b467bf4
SM
254(defun cvs-check-fileinfo (fi)
255 "Check FI's conformance to some conventions."
256 (let ((check 'none)
257 (type (cvs-fileinfo->type fi))
258 (subtype (cvs-fileinfo->subtype fi))
259 (marked (cvs-fileinfo->marked fi))
260 (dir (cvs-fileinfo->dir fi))
261 (file (cvs-fileinfo->file fi))
262 (base-rev (cvs-fileinfo->base-rev fi))
263 (head-rev (cvs-fileinfo->head-rev fi))
264 (full-log (cvs-fileinfo->full-log fi)))
9d4b3027 265 (if (and (setq check 'marked) (memq marked '(t nil))
5b467bf4
SM
266 (setq check 'base-rev) (or (null base-rev) (stringp base-rev))
267 (setq check 'head-rev) (or (null head-rev) (stringp head-rev))
268 (setq check 'full-log) (stringp full-log)
269 (setq check 'dir)
270 (and (stringp dir)
271 (not (file-name-absolute-p dir))
272 (or (string= dir "")
273 (string= dir (file-name-as-directory dir))))
274 (setq check 'file)
275 (and (stringp file)
276 (string= file (file-name-nondirectory file)))
277 (setq check 'type) (symbolp type)
278 (setq check 'consistency)
279 (case type
280 (DIRCHANGE (and (null subtype) (string= "." file)))
281 ((NEED-UPDATE ADDED MISSING DEAD MODIFIED MESSAGE UP-TO-DATE
282 REMOVED NEED-MERGE CONFLICT UNKNOWN MESSAGE)
283 t)))
284 fi
285 (error "Invalid :%s in cvs-fileinfo %s" check fi))))
286
287\f
f1180544 288;;;;
5b467bf4 289;;;; State table to indicate what you can do when.
f1180544 290;;;;
5b467bf4
SM
291
292(defconst cvs-states
45ce5942 293 `((NEED-UPDATE update diff ignore)
5b467bf4
SM
294 (UP-TO-DATE update nil remove diff safe-rm revert)
295 (MODIFIED update commit undo remove diff merge diff-base)
296 (ADDED update commit remove)
297 (MISSING remove undo update safe-rm revert)
298 (REMOVED commit add undo safe-rm)
299 (NEED-MERGE update undo diff diff-base)
300 (CONFLICT merge remove undo commit diff diff-base)
301 (DIRCHANGE remove update diff ,(if cvs-allow-dir-commit 'commit) tag)
302 (UNKNOWN ignore add remove)
303 (DEAD )
304 (MESSAGE))
305 "Fileinfo state descriptions for pcl-cvs.
306This is an assoc list. Each element consists of (STATE . FUNS)
307- STATE (described in `cvs-create-fileinfo') is the key
308- FUNS is the list of applicable operations.
309 The first one (if any) should be the \"default\" action.
310Most of the actions have the obvious meaning.
311`safe-rm' indicates that the file can be removed without losing
312 any information.")
313
314;;;;
315;;;; Utility functions
316;;;;
317
5b467bf4
SM
318(defun cvs-applicable-p (fi-or-type func)
319 "Check if FUNC is applicable to FI-OR-TYPE.
320If FUNC is nil, always return t.
321FI-OR-TYPE can either be a symbol (a fileinfo-type) or a fileinfo."
322 (let ((type (if (symbolp fi-or-type) fi-or-type
323 (cvs-fileinfo->type fi-or-type))))
324 (and (not (eq type 'MESSAGE))
325 (eq (car (memq func (cdr (assq type cvs-states)))) func))))
326
db1f981d 327(defun cvs-add-face (str face &optional keymap &rest props)
c8bc0b5f
SM
328 (when keymap
329 (when (keymapp keymap)
330 (setq props (list* 'keymap keymap props)))
331 (setq props (list* 'mouse-face 'highlight props)))
332 (add-text-properties 0 (length str) (list* 'font-lock-face face props) str)
5b467bf4
SM
333 str)
334
5b467bf4
SM
335(defun cvs-fileinfo-pp (fileinfo)
336 "Pretty print FILEINFO. Insert a printed representation in current buffer.
337For use by the cookie package."
338 (cvs-check-fileinfo fileinfo)
339 (let ((type (cvs-fileinfo->type fileinfo))
340 (subtype (cvs-fileinfo->subtype fileinfo)))
341 (insert
342 (case type
343 (DIRCHANGE (concat "In directory "
03de06da 344 (cvs-add-face (cvs-fileinfo->full-name fileinfo)
2058218e 345 'cvs-header t 'cvs-goal-column t)
5b467bf4
SM
346 ":"))
347 (MESSAGE
cb3430a1 348 (cvs-add-face (format "Message: %s" (cvs-fileinfo->full-log fileinfo))
2058218e 349 'cvs-msg))
5b467bf4
SM
350 (t
351 (let* ((status (if (cvs-fileinfo->marked fileinfo)
2058218e 352 (cvs-add-face "*" 'cvs-marked)
5b467bf4
SM
353 " "))
354 (file (cvs-add-face (cvs-fileinfo->pp-name fileinfo)
2058218e 355 'cvs-filename t 'cvs-goal-column t))
5b467bf4
SM
356 (base (or (cvs-fileinfo->base-rev fileinfo) ""))
357 (head (cvs-fileinfo->head-rev fileinfo))
358 (type
359 (let ((str (case type
360 ;;(MOD-CONFLICT "Not Removed")
361 (DEAD "")
362 (t (capitalize (symbol-name type)))))
1fe28d30
SM
363 (face (let ((sym (intern
364 (concat "cvs-fi-"
365 (downcase (symbol-name type))
366 "-face"))))
367 (or (and (boundp sym) (symbol-value sym))
2058218e 368 'cvs-need-action))))
5b467bf4
SM
369 (cvs-add-face str face cvs-status-map)))
370 (side (or
371 ;; maybe a subtype
372 (when subtype (downcase (symbol-name subtype)))
373 ;; or the head-rev
374 (when (and head (not (string= head base))) head)
375 ;; or nothing
cb3430a1 376 "")))
6efa25a1 377 (format "%-11s %s %-11s %-11s %s"
438dd27d
TTN
378 side status type base file))))
379 "\n")))
5b467bf4
SM
380
381
382(defun cvs-fileinfo-update (fi fi-new)
383 "Update FI with the information provided in FI-NEW."
384 (let ((type (cvs-fileinfo->type fi-new))
385 (merge (cvs-fileinfo->merge fi-new)))
386 (setf (cvs-fileinfo->type fi) type)
387 (setf (cvs-fileinfo->subtype fi) (cvs-fileinfo->subtype fi-new))
388 (setf (cvs-fileinfo->full-log fi) (cvs-fileinfo->full-log fi-new))
389 (setf (cvs-fileinfo->base-rev fi) (cvs-fileinfo->base-rev fi-new))
390 (setf (cvs-fileinfo->head-rev fi) (cvs-fileinfo->head-rev fi-new))
391 (cond
392 (merge (setf (cvs-fileinfo->merge fi) merge))
393 ((memq type '(UP-TO-DATE NEED-UPDATE))
394 (setf (cvs-fileinfo->merge fi) nil)))))
395
5b467bf4
SM
396(defun cvs-fileinfo< (a b)
397 "Compare fileinfo A with fileinfo B and return t if A is `less'.
398The ordering defined by this function is such that directories are
399sorted alphabetically, and inside every directory the DIRCHANGE
400fileinfo will appear first, followed by all files (alphabetically)."
401 (let ((subtypea (cvs-fileinfo->subtype a))
402 (subtypeb (cvs-fileinfo->subtype b)))
403 (cond
5b467bf4
SM
404 ;; Sort according to directories.
405 ((string< (cvs-fileinfo->dir a) (cvs-fileinfo->dir b)) t)
406 ((not (string= (cvs-fileinfo->dir a) (cvs-fileinfo->dir b))) nil)
407
408 ;; The DIRCHANGE entry is always first within the directory.
409 ((eq (cvs-fileinfo->type b) 'DIRCHANGE) nil)
410 ((eq (cvs-fileinfo->type a) 'DIRCHANGE) t)
411
412 ;; All files are sorted by file name.
413 ((string< (cvs-fileinfo->file a) (cvs-fileinfo->file b))))))
414
1fe28d30
SM
415;;;
416;;; Look at CVS/Entries to quickly find a first approximation of the status
417;;;
418
419(defun cvs-fileinfo-from-entries (dir &optional all)
420 "List of fileinfos for DIR, extracted from CVS/Entries.
421Unless ALL is optional, returns only the files that are not up-to-date.
422DIR can also be a file."
423 (let* ((singlefile
424 (cond
425 ((equal dir "") nil)
426 ((file-directory-p dir) (setq dir (file-name-as-directory dir)) nil)
427 (t (prog1 (file-name-nondirectory dir)
428 (setq dir (or (file-name-directory dir) ""))))))
429 (file (expand-file-name "CVS/Entries" dir))
430 (fis nil))
431 (if (not (file-readable-p file))
432 (push (cvs-create-fileinfo (if singlefile 'UNKNOWN 'DIRCHANGE)
433 dir (or singlefile ".") "") fis)
434 (with-temp-buffer
435 (insert-file-contents file)
436 (goto-char (point-min))
437 ;; Select the single file entry in case we're only interested in a file.
438 (cond
439 ((not singlefile)
440 (push (cvs-create-fileinfo 'DIRCHANGE dir "." "") fis))
441 ((re-search-forward
442 (concat "^[^/]*/" (regexp-quote singlefile) "/.*") nil t)
443 (setq all t)
444 (goto-char (match-beginning 0))
445 (narrow-to-region (point) (match-end 0)))
446 (t
447 (push (cvs-create-fileinfo 'UNKNOWN dir singlefile "") fis)
448 (narrow-to-region (point-min) (point-min))))
449 (while (looking-at "\\([^/]*\\)/\\([^/]*\\)/\\([^/]*\\)/\\([^/]*\\)/")
450 (if (/= (match-beginning 1) (match-end 1))
451 (setq fis (append (cvs-fileinfo-from-entries
452 (concat dir (file-name-as-directory
453 (match-string 2)))
454 all)
455 fis))
456 (let ((f (match-string 2))
457 (rev (match-string 3))
458 (date (match-string 4))
459 timestamp
460 (type 'MODIFIED)
461 (subtype nil))
462 (cond
463 ((equal (substring rev 0 1) "-")
464 (setq type 'REMOVED rev (substring rev 1)))
465 ((not (file-exists-p (concat dir f))) (setq type 'MISSING))
466 ((equal rev "0") (setq type 'ADDED rev nil))
467 ((equal date "Result of merge") (setq subtype 'MERGED))
468 ((let ((mtime (nth 5 (file-attributes (concat dir f))))
469 (system-time-locale "C"))
38fdf6aa
SM
470 (setq timestamp (format-time-string "%c" mtime 'utc))
471 ;; Solaris sometimes uses "Wed Sep 05", not "Wed Sep 5".
472 ;; See "grep '[^a-z_]ctime' cvs/src/*.c" for reference.
473 (if (= (aref timestamp 8) ?0)
474 (setq timestamp (concat (substring timestamp 0 8)
475 " " (substring timestamp 9))))
476 (equal timestamp date))
1fe28d30
SM
477 (setq type (if all 'UP-TO-DATE)))
478 ((equal date (concat "Result of merge+" timestamp))
479 (setq type 'CONFLICT)))
480 (when type
481 (push (cvs-create-fileinfo type dir f ""
482 :base-rev rev :subtype subtype)
483 fis))))
484 (forward-line 1))))
485 fis))
486
5b467bf4
SM
487(provide 'pcvs-info)
488
45ce5942 489;; arch-tag: d85dde07-bdc2-400a-882f-92f398c7b0ba
3afbc435 490;;; pcvs-info.el ends here