vc-mtn.el 14 KB

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