New functions for finding the administrative directory in VC.
[bpt/emacs.git] / lisp / vc / vc-mtn.el
CommitLineData
0d42eb3e 1;;; vc-mtn.el --- VC backend for Monotone -*- lexical-binding: t -*-
b1dc6d44 2
ab422c4d 3;; Copyright (C) 2007-2013 Free Software Foundation, Inc.
b1dc6d44
SM
4
5;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
9766adfb 6;; Keywords: vc
bd78fa1d 7;; Package: vc
b1dc6d44 8
eb3fa2cf
GM
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
b1dc6d44 12;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
b1dc6d44 15
eb3fa2cf 16;; GNU Emacs is distributed in the hope that it will be useful,
b1dc6d44
SM
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
eb3fa2cf 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
b1dc6d44
SM
23
24;;; Commentary:
25
f64ab8fb 26;;
b1dc6d44 27
469ca403
DN
28;;; TODO:
29
30;; - The `previous-version' VC method needs to be supported, 'D' in
31;; log-view-mode uses it.
32
b1dc6d44
SM
33;;; Code:
34
a464a6c7 35(eval-when-compile (require 'vc))
b1dc6d44 36
67b0de11
CY
37(defgroup vc-mtn nil
38 "VC Monotone (mtn) backend."
39 :version "24.1"
40 :group 'vc)
41
a857238c
GM
42(defcustom vc-mtn-diff-switches t
43 "String or list of strings specifying switches for monotone diff under VC.
633883e7 44If nil, use the value of `vc-diff-switches'. If t, use no switches."
a857238c
GM
45 :type '(choice (const :tag "Unspecified" nil)
46 (const :tag "None" t)
47 (string :tag "Argument String")
633883e7 48 (repeat :tag "Argument List" :value ("") string))
a857238c 49 :version "23.1"
67b0de11 50 :group 'vc-mtn)
a857238c
GM
51
52(define-obsolete-variable-alias 'vc-mtn-command 'vc-mtn-program "23.1")
53(defcustom vc-mtn-program "mtn"
54 "Name of the monotone executable."
55 :type 'string
67b0de11 56 :group 'vc-mtn)
a857238c 57
b1dc6d44
SM
58;; Clear up the cache to force vc-call to check again and discover
59;; new functions when we reload this file.
60(put 'Mtn 'vc-functions nil)
61
a857238c 62(unless (executable-find vc-mtn-program)
b1dc6d44
SM
63 ;; vc-mtn.el is 100% non-functional without the `mtn' executable.
64 (setq vc-handled-backends (delq 'Mtn vc-handled-backends)))
65
66;;;###autoload
3adbe224 67(defconst vc-mtn-admin-dir "_MTN" "Name of the monotone directory.")
b1dc6d44 68;;;###autoload
3adbe224
GM
69(defconst vc-mtn-admin-format (concat vc-mtn-admin-dir "/format")
70 "Name of the monotone directory's format file.")
b1dc6d44
SM
71
72;;;###autoload (defun vc-mtn-registered (file)
73;;;###autoload (if (vc-find-root file vc-mtn-admin-format)
74;;;###autoload (progn
af314ba0 75;;;###autoload (load "vc-mtn" nil t)
b1dc6d44
SM
76;;;###autoload (vc-mtn-registered file))))
77
78(defun vc-mtn-revision-granularity () 'repository)
0d42eb3e 79(defun vc-mtn-checkout-model (_files) 'implicit)
b1dc6d44
SM
80
81(defun vc-mtn-root (file)
82 (setq file (if (file-directory-p file)
83 (file-name-as-directory file)
84 (file-name-directory file)))
85 (or (vc-file-getprop file 'vc-mtn-root)
86 (vc-file-setprop file 'vc-mtn-root
87 (vc-find-root file vc-mtn-admin-format))))
88
34ca0f4c
XF
89(defun vc-mtn-find-admin-dir (file)
90 "Return the administrative directory of FILE."
91 (expand-file-name vc-mtn-admin-dir (vc-mtn-root file)))
b1dc6d44
SM
92
93(defun vc-mtn-registered (file)
94 (let ((root (vc-mtn-root file)))
95 (when root
96 (vc-mtn-state file))))
97
98(defun vc-mtn-command (buffer okstatus files &rest flags)
99 "A wrapper around `vc-do-command' for use in vc-mtn.el."
ce4025c7
SM
100 (let ((process-environment
101 ;; Avoid localization of messages so we can parse the output.
102 (cons "LC_MESSAGES=C" process-environment)))
a857238c
GM
103 (apply 'vc-do-command (or buffer "*vc*") okstatus vc-mtn-program
104 files flags)))
b1dc6d44
SM
105
106(defun vc-mtn-state (file)
107 ;; If `mtn' fails or returns status>0, or if the search files, just
108 ;; return nil.
109 (ignore-errors
110 (with-temp-buffer
111 (vc-mtn-command t 0 file "status")
112 (goto-char (point-min))
3281a821
DN
113 (re-search-forward
114 "^ \\(?:\\(patched\\)\\|\\(added\\) \\(?:.*\\)\\)\\|no changes$")
115 (cond ((match-end 1) 'edited)
116 ((match-end 2) 'added)
117 (t 'up-to-date)))))
b1dc6d44 118
f5a0b281
DN
119(defun vc-mtn-after-dir-status (update-function)
120 (let (result)
121 (goto-char (point-min))
05539fb3 122 (re-search-forward "\\(?:Current b\\|B\\)ranch: *\\(.*\\)\n?\nChanges against parent \\(.*\\)" nil t)
f5a0b281
DN
123 (while (re-search-forward
124 "^ \\(?:\\(patched \\)\\|\\(added \\)\\)\\(.*\\)$" nil t)
125 (cond ((match-end 1) (push (list (match-string 3) 'edited) result))
126 ((match-end 2) (push (list (match-string 3) 'added) result))))
127 (funcall update-function result)))
128
e658d75c
GM
129;; -dir-status called from vc-dir, which loads vc, which loads vc-dispatcher.
130(declare-function vc-exec-after "vc-dispatcher" (code))
131
f5a0b281
DN
132(defun vc-mtn-dir-status (dir update-function)
133 (vc-mtn-command (current-buffer) 'async dir "status")
9c750eba
SM
134 (vc-run-delayed
135 (vc-mtn-after-dir-status update-function)))
f5a0b281 136
ac3f4c6f 137(defun vc-mtn-working-revision (file)
b1dc6d44
SM
138 ;; If `mtn' fails or returns status>0, or if the search fails, just
139 ;; return nil.
140 (ignore-errors
141 (with-temp-buffer
142 (vc-mtn-command t 0 file "status")
143 (goto-char (point-min))
05539fb3 144 (re-search-forward "\\(?:Current b\\|B\\)ranch: *\\(.*\\)\n?\nChanges against parent \\(.*\\)")
b1dc6d44
SM
145 (match-string 2))))
146
147(defun vc-mtn-workfile-branch (file)
148 ;; If `mtn' fails or returns status>0, or if the search files, just
149 ;; return nil.
150 (ignore-errors
151 (with-temp-buffer
152 (vc-mtn-command t 0 file "status")
153 (goto-char (point-min))
05539fb3 154 (re-search-forward "\\(?:Current b\\|B\\)ranch: *\\(.*\\)\n?\nChanges against parent \\(.*\\)")
b1dc6d44
SM
155 (match-string 1))))
156
157(defun vc-mtn-workfile-unchanged-p (file)
158 (not (eq (vc-mtn-state file) 'edited)))
159
160;; Mode-line rewrite code copied from vc-arch.el.
161
162(defcustom vc-mtn-mode-line-rewrite
163 '(("\\`[^:/#]*[:/#]" . "")) ;Drop the host part.
164 "Rewrite rules to shorten Mtn's revision names on the mode-line."
165 :type '(repeat (cons regexp string))
8e788369 166 :version "22.2"
67b0de11 167 :group 'vc-mtn)
b1dc6d44
SM
168
169(defun vc-mtn-mode-line-string (file)
37269466 170 "Return a string for `vc-mode-line' to put in the mode line for FILE."
b1dc6d44
SM
171 (let ((branch (vc-mtn-workfile-branch file)))
172 (dolist (rule vc-mtn-mode-line-rewrite)
173 (if (string-match (car rule) branch)
174 (setq branch (replace-match (cdr rule) t nil branch))))
175 (format "Mtn%c%s"
a464a6c7
SM
176 (pcase (vc-state file)
177 ((or `up-to-date `needs-update) ?-)
178 (`added ?@)
179 (_ ?:))
b1dc6d44
SM
180 branch)))
181
0d42eb3e 182(defun vc-mtn-register (files &optional _rev _comment)
b1dc6d44
SM
183 (vc-mtn-command nil 0 files "add"))
184
185(defun vc-mtn-responsible-p (file) (vc-mtn-root file))
186(defun vc-mtn-could-register (file) (vc-mtn-root file))
187
f64ab8fb
JB
188(declare-function log-edit-extract-headers "log-edit" (headers string))
189
0d42eb3e 190(defun vc-mtn-checkin (files _rev comment)
fab43c76
DN
191 (apply 'vc-mtn-command nil 0 files
192 (nconc (list "commit" "-m")
193 (log-edit-extract-headers '(("Author" . "--author")
194 ("Date" . "--date"))
195 comment))))
b1dc6d44 196
ac3f4c6f 197(defun vc-mtn-find-revision (file rev buffer)
b1dc6d44
SM
198 (vc-mtn-command buffer 0 file "cat" "-r" rev))
199
200;; (defun vc-mtn-checkout (file &optional editable rev)
201;; )
202
203(defun vc-mtn-revert (file &optional contents-done)
204 (unless contents-done
205 (vc-mtn-command nil 0 file "revert")))
206
9858f6c3 207;; (defun vc-mtn-rollback (files)
b1dc6d44
SM
208;; )
209
0d42eb3e 210(defun vc-mtn-print-log (files buffer &optional _shortlog start-revision limit)
bb7cdf58
GM
211 "Print commit logs associated with FILES into specified BUFFER.
212_SHORTLOG is ignored.
213If START-REVISION is non-nil, it is the newest revision to show.
214If LIMIT is non-nil, show no more than this many entries."
6616006b 215 (apply 'vc-mtn-command buffer 0 files "log"
662c5698 216 (append
6e890faa
GM
217 (when start-revision (list "--from" (format "%s" start-revision)))
218 (when limit (list "--last" (format "%s" limit))))))
b1dc6d44 219
97546017
DN
220(defvar log-view-message-re)
221(defvar log-view-file-re)
222(defvar log-view-font-lock-keywords)
469ca403 223(defvar log-view-per-file-logs)
97546017 224
b1dc6d44 225(define-derived-mode vc-mtn-log-view-mode log-view-mode "Mtn-Log-View"
469ca403
DN
226 ;; Don't match anything.
227 (set (make-local-variable 'log-view-file-re) "\\`a\\`")
228 (set (make-local-variable 'log-view-per-file-logs) nil)
b1dc6d44
SM
229 ;; TODO: Use a more precise regexp than "[ |/]+" to avoid false positives
230 ;; in the ChangeLog text.
231 (set (make-local-variable 'log-view-message-re)
232 "^[ |/]+Revision: \\([0-9a-f]+\\)")
233 (require 'add-log) ;For change-log faces.
234 (set (make-local-variable 'log-view-font-lock-keywords)
235 (append log-view-font-lock-keywords
236 '(("^[ |]+Author: \\(.*\\)" (1 'change-log-email))
237 ("^[ |]+Date: \\(.*\\)" (1 'change-log-date-face))))))
238
5b5afd50 239;; (defun vc-mtn-show-log-entry (revision)
b1dc6d44
SM
240;; )
241
e658d75c
GM
242(autoload 'vc-switches "vc")
243
b1dc6d44 244(defun vc-mtn-diff (files &optional rev1 rev2 buffer)
a857238c 245 "Get a difference report using monotone between two revisions of FILES."
b1dc6d44 246 (apply 'vc-mtn-command (or buffer "*vc-diff*") 1 files "diff"
a857238c 247 (append
39ba78ef 248 (vc-switches 'mtn 'diff)
a857238c 249 (if rev1 (list "-r" rev1)) (if rev2 (list "-r" rev2)))))
b1dc6d44
SM
250
251(defun vc-mtn-annotate-command (file buf &optional rev)
837b0e99 252 (apply 'vc-mtn-command buf 'async file "annotate"
b1dc6d44
SM
253 (if rev (list "-r" rev))))
254
f8bd9ac6
DN
255(declare-function vc-annotate-convert-time "vc-annotate" (time))
256
b1dc6d44
SM
257(defconst vc-mtn-annotate-full-re
258 "^ *\\([0-9a-f]+\\)\\.* by [^ ]+ \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\): ")
259(defconst vc-mtn-annotate-any-re
260 (concat "^\\(?: +: \\|" vc-mtn-annotate-full-re "\\)"))
261
262(defun vc-mtn-annotate-time ()
263 (when (looking-at vc-mtn-annotate-any-re)
264 (goto-char (match-end 0))
265 (let ((year (match-string 2)))
266 (if (not year)
267 ;; Look for the date on a previous line.
268 (save-excursion
269 (get-text-property (1- (previous-single-property-change
270 (point) 'vc-mtn-time nil (point-min)))
271 'vc-mtn-time))
272 (let ((time (vc-annotate-convert-time
273 (encode-time 0 0 0
274 (string-to-number (match-string 4))
275 (string-to-number (match-string 3))
276 (string-to-number year)
277 t))))
278 (let ((inhibit-read-only t)
279 (inhibit-modification-hooks t))
280 (put-text-property (match-beginning 0) (match-end 0)
281 'vc-mtn-time time))
282 time)))))
283
284(defun vc-mtn-annotate-extract-revision-at-line ()
285 (save-excursion
286 (when (or (looking-at vc-mtn-annotate-full-re)
287 (re-search-backward vc-mtn-annotate-full-re nil t))
288 (match-string 1))))
289
290;;; Revision completion.
291
292(defun vc-mtn-list-tags ()
293 (with-temp-buffer
294 (vc-mtn-command t 0 nil "list" "tags")
295 (goto-char (point-min))
296 (let ((tags ()))
297 (while (re-search-forward "^[^ ]+" nil t)
298 (push (match-string 0) tags))
299 tags)))
300
301(defun vc-mtn-list-branches ()
302 (with-temp-buffer
303 (vc-mtn-command t 0 nil "list" "branches")
304 (goto-char (point-min))
305 (let ((branches ()))
306 (while (re-search-forward "^.+" nil t)
307 (push (match-string 0) branches))
308 branches)))
309
310(defun vc-mtn-list-revision-ids (prefix)
311 (with-temp-buffer
312 (vc-mtn-command t 0 nil "complete" "revision" prefix)
313 (goto-char (point-min))
314 (let ((ids ()))
315 (while (re-search-forward "^.+" nil t)
316 (push (match-string 0) ids))
317 ids)))
318
0d42eb3e 319(defun vc-mtn-revision-completion-table (_files)
0d42eb3e
SM
320 ;; What about using `files'?!? --Stef
321 (lambda (string pred action)
322 (cond
94c9ece1
SM
323 ;; Special chars for composite selectors.
324 ((string-match ".*[^\\]\\(\\\\\\\\\\)*[/|;(]" string)
325 (completion-table-with-context (substring string 0 (match-end 0))
326 (vc-mtn-revision-completion-table nil)
327 (substring string (match-end 0))
328 pred action))
0d42eb3e
SM
329 ;; "Tag" selectors.
330 ((string-match "\\`t:" string)
331 (complete-with-action action
332 (mapcar (lambda (tag) (concat "t:" tag))
333 (vc-mtn-list-tags))
334 string pred))
94c9ece1
SM
335 ;; "Branch" or "Head" selectors.
336 ((string-match "\\`[hb]:" string)
337 (let ((prefix (match-string 0 string)))
338 (complete-with-action action
339 (mapcar (lambda (tag) (concat prefix tag))
340 (vc-mtn-list-branches))
341 string pred)))
0d42eb3e
SM
342 ;; "ID" selectors.
343 ((string-match "\\`i:" string)
344 (complete-with-action action
345 (mapcar (lambda (tag) (concat "i:" tag))
346 (vc-mtn-list-revision-ids
347 (substring string (match-end 0))))
348 string pred))
349 (t
350 (complete-with-action action
351 '("t:" "b:" "h:" "i:"
352 ;; Completion not implemented for these.
94c9ece1
SM
353 "c:" "a:" "k:" "d:" "m:" "e:" "l:" "i:" "p:"
354 ;; These have no arg to complete.
355 "u:" "w:"
356 ;; Selector functions.
357 "difference(" "lca(" "max(" "ancestors("
358 "descendants(" "parents(" "children("
359 "pick(")
0d42eb3e 360 string pred)))))
eb3fa2cf
GM
361
362
b1dc6d44
SM
363
364(provide 'vc-mtn)
365
b1dc6d44 366;;; vc-mtn.el ends here