vc-mtn.el 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. ;;; vc-mtn.el --- VC backend for Monotone
  2. ;; Copyright (C) 2007-2012 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 'cl) (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. (define-obsolete-variable-alias 'vc-mtn-command 'vc-mtn-program "23.1")
  38. (defcustom vc-mtn-program "mtn"
  39. "Name of the monotone executable."
  40. :type 'string
  41. :group 'vc-mtn)
  42. ;; Clear up the cache to force vc-call to check again and discover
  43. ;; new functions when we reload this file.
  44. (put 'Mtn 'vc-functions nil)
  45. (unless (executable-find vc-mtn-program)
  46. ;; vc-mtn.el is 100% non-functional without the `mtn' executable.
  47. (setq vc-handled-backends (delq 'Mtn vc-handled-backends)))
  48. ;;;###autoload
  49. (defconst vc-mtn-admin-dir "_MTN" "Name of the monotone directory.")
  50. ;;;###autoload
  51. (defconst vc-mtn-admin-format (concat vc-mtn-admin-dir "/format")
  52. "Name of the monotone directory's format file.")
  53. ;;;###autoload (defun vc-mtn-registered (file)
  54. ;;;###autoload (if (vc-find-root file vc-mtn-admin-format)
  55. ;;;###autoload (progn
  56. ;;;###autoload (load "vc-mtn")
  57. ;;;###autoload (vc-mtn-registered file))))
  58. (defun vc-mtn-revision-granularity () 'repository)
  59. (defun vc-mtn-checkout-model (files) 'implicit)
  60. (defun vc-mtn-root (file)
  61. (setq file (if (file-directory-p file)
  62. (file-name-as-directory file)
  63. (file-name-directory file)))
  64. (or (vc-file-getprop file 'vc-mtn-root)
  65. (vc-file-setprop file 'vc-mtn-root
  66. (vc-find-root file vc-mtn-admin-format))))
  67. (defun vc-mtn-registered (file)
  68. (let ((root (vc-mtn-root file)))
  69. (when root
  70. (vc-mtn-state file))))
  71. (defun vc-mtn-command (buffer okstatus files &rest flags)
  72. "A wrapper around `vc-do-command' for use in vc-mtn.el."
  73. (let ((process-environment
  74. ;; Avoid localization of messages so we can parse the output.
  75. (cons "LC_MESSAGES=C" process-environment)))
  76. (apply 'vc-do-command (or buffer "*vc*") okstatus vc-mtn-program
  77. files flags)))
  78. (defun vc-mtn-state (file)
  79. ;; If `mtn' fails or returns status>0, or if the search files, just
  80. ;; return nil.
  81. (ignore-errors
  82. (with-temp-buffer
  83. (vc-mtn-command t 0 file "status")
  84. (goto-char (point-min))
  85. (re-search-forward
  86. "^ \\(?:\\(patched\\)\\|\\(added\\) \\(?:.*\\)\\)\\|no changes$")
  87. (cond ((match-end 1) 'edited)
  88. ((match-end 2) 'added)
  89. (t 'up-to-date)))))
  90. (defun vc-mtn-after-dir-status (update-function)
  91. (let (result)
  92. (goto-char (point-min))
  93. (re-search-forward "\\(?:Current b\\|B\\)ranch: *\\(.*\\)\n?\nChanges against parent \\(.*\\)" nil t)
  94. (while (re-search-forward
  95. "^ \\(?:\\(patched \\)\\|\\(added \\)\\)\\(.*\\)$" nil t)
  96. (cond ((match-end 1) (push (list (match-string 3) 'edited) result))
  97. ((match-end 2) (push (list (match-string 3) 'added) result))))
  98. (funcall update-function result)))
  99. (defun vc-mtn-dir-status (dir update-function)
  100. (vc-mtn-command (current-buffer) 'async dir "status")
  101. (vc-exec-after
  102. `(vc-mtn-after-dir-status (quote ,update-function))))
  103. (defun vc-mtn-working-revision (file)
  104. ;; If `mtn' fails or returns status>0, or if the search fails, just
  105. ;; return nil.
  106. (ignore-errors
  107. (with-temp-buffer
  108. (vc-mtn-command t 0 file "status")
  109. (goto-char (point-min))
  110. (re-search-forward "\\(?:Current b\\|B\\)ranch: *\\(.*\\)\n?\nChanges against parent \\(.*\\)")
  111. (match-string 2))))
  112. (defun vc-mtn-workfile-branch (file)
  113. ;; If `mtn' fails or returns status>0, or if the search files, just
  114. ;; return nil.
  115. (ignore-errors
  116. (with-temp-buffer
  117. (vc-mtn-command t 0 file "status")
  118. (goto-char (point-min))
  119. (re-search-forward "\\(?:Current b\\|B\\)ranch: *\\(.*\\)\n?\nChanges against parent \\(.*\\)")
  120. (match-string 1))))
  121. (defun vc-mtn-workfile-unchanged-p (file)
  122. (not (eq (vc-mtn-state file) 'edited)))
  123. ;; Mode-line rewrite code copied from vc-arch.el.
  124. (defcustom vc-mtn-mode-line-rewrite
  125. '(("\\`[^:/#]*[:/#]" . "")) ;Drop the host part.
  126. "Rewrite rules to shorten Mtn's revision names on the mode-line."
  127. :type '(repeat (cons regexp string))
  128. :version "22.2"
  129. :group 'vc-mtn)
  130. (defun vc-mtn-mode-line-string (file)
  131. "Return string for placement in modeline by `vc-mode-line' for FILE."
  132. (let ((branch (vc-mtn-workfile-branch file)))
  133. (dolist (rule vc-mtn-mode-line-rewrite)
  134. (if (string-match (car rule) branch)
  135. (setq branch (replace-match (cdr rule) t nil branch))))
  136. (format "Mtn%c%s"
  137. (case (vc-state file)
  138. ((up-to-date needs-update) ?-)
  139. (added ?@)
  140. (t ?:))
  141. branch)))
  142. (defun vc-mtn-register (files &optional rev comment)
  143. (vc-mtn-command nil 0 files "add"))
  144. (defun vc-mtn-responsible-p (file) (vc-mtn-root file))
  145. (defun vc-mtn-could-register (file) (vc-mtn-root file))
  146. (declare-function log-edit-extract-headers "log-edit" (headers string))
  147. (defun vc-mtn-checkin (files rev comment)
  148. (apply 'vc-mtn-command nil 0 files
  149. (nconc (list "commit" "-m")
  150. (log-edit-extract-headers '(("Author" . "--author")
  151. ("Date" . "--date"))
  152. comment))))
  153. (defun vc-mtn-find-revision (file rev buffer)
  154. (vc-mtn-command buffer 0 file "cat" "-r" rev))
  155. ;; (defun vc-mtn-checkout (file &optional editable rev)
  156. ;; )
  157. (defun vc-mtn-revert (file &optional contents-done)
  158. (unless contents-done
  159. (vc-mtn-command nil 0 file "revert")))
  160. ;; (defun vc-mtn-rollback (files)
  161. ;; )
  162. (defun vc-mtn-print-log (files buffer &optional shortlog start-revision limit)
  163. (apply 'vc-mtn-command buffer 0 files "log"
  164. (append
  165. (when start-revision (list "--from" (format "%s" start-revision)))
  166. (when limit (list "--last" (format "%s" limit))))))
  167. (defvar log-view-message-re)
  168. (defvar log-view-file-re)
  169. (defvar log-view-font-lock-keywords)
  170. (defvar log-view-per-file-logs)
  171. (define-derived-mode vc-mtn-log-view-mode log-view-mode "Mtn-Log-View"
  172. ;; Don't match anything.
  173. (set (make-local-variable 'log-view-file-re) "\\`a\\`")
  174. (set (make-local-variable 'log-view-per-file-logs) nil)
  175. ;; TODO: Use a more precise regexp than "[ |/]+" to avoid false positives
  176. ;; in the ChangeLog text.
  177. (set (make-local-variable 'log-view-message-re)
  178. "^[ |/]+Revision: \\([0-9a-f]+\\)")
  179. (require 'add-log) ;For change-log faces.
  180. (set (make-local-variable 'log-view-font-lock-keywords)
  181. (append log-view-font-lock-keywords
  182. '(("^[ |]+Author: \\(.*\\)" (1 'change-log-email))
  183. ("^[ |]+Date: \\(.*\\)" (1 'change-log-date-face))))))
  184. ;; (defun vc-mtn-show-log-entry (revision)
  185. ;; )
  186. (defun vc-mtn-diff (files &optional rev1 rev2 buffer)
  187. "Get a difference report using monotone between two revisions of FILES."
  188. (apply 'vc-mtn-command (or buffer "*vc-diff*") 1 files "diff"
  189. (append
  190. (vc-switches 'mtn 'diff)
  191. (if rev1 (list "-r" rev1)) (if rev2 (list "-r" rev2)))))
  192. (defun vc-mtn-annotate-command (file buf &optional rev)
  193. (apply 'vc-mtn-command buf 'async file "annotate"
  194. (if rev (list "-r" rev))))
  195. (declare-function vc-annotate-convert-time "vc-annotate" (time))
  196. (defconst vc-mtn-annotate-full-re
  197. "^ *\\([0-9a-f]+\\)\\.* by [^ ]+ \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\): ")
  198. (defconst vc-mtn-annotate-any-re
  199. (concat "^\\(?: +: \\|" vc-mtn-annotate-full-re "\\)"))
  200. (defun vc-mtn-annotate-time ()
  201. (when (looking-at vc-mtn-annotate-any-re)
  202. (goto-char (match-end 0))
  203. (let ((year (match-string 2)))
  204. (if (not year)
  205. ;; Look for the date on a previous line.
  206. (save-excursion
  207. (get-text-property (1- (previous-single-property-change
  208. (point) 'vc-mtn-time nil (point-min)))
  209. 'vc-mtn-time))
  210. (let ((time (vc-annotate-convert-time
  211. (encode-time 0 0 0
  212. (string-to-number (match-string 4))
  213. (string-to-number (match-string 3))
  214. (string-to-number year)
  215. t))))
  216. (let ((inhibit-read-only t)
  217. (inhibit-modification-hooks t))
  218. (put-text-property (match-beginning 0) (match-end 0)
  219. 'vc-mtn-time time))
  220. time)))))
  221. (defun vc-mtn-annotate-extract-revision-at-line ()
  222. (save-excursion
  223. (when (or (looking-at vc-mtn-annotate-full-re)
  224. (re-search-backward vc-mtn-annotate-full-re nil t))
  225. (match-string 1))))
  226. ;;; Revision completion.
  227. (defun vc-mtn-list-tags ()
  228. (with-temp-buffer
  229. (vc-mtn-command t 0 nil "list" "tags")
  230. (goto-char (point-min))
  231. (let ((tags ()))
  232. (while (re-search-forward "^[^ ]+" nil t)
  233. (push (match-string 0) tags))
  234. tags)))
  235. (defun vc-mtn-list-branches ()
  236. (with-temp-buffer
  237. (vc-mtn-command t 0 nil "list" "branches")
  238. (goto-char (point-min))
  239. (let ((branches ()))
  240. (while (re-search-forward "^.+" nil t)
  241. (push (match-string 0) branches))
  242. branches)))
  243. (defun vc-mtn-list-revision-ids (prefix)
  244. (with-temp-buffer
  245. (vc-mtn-command t 0 nil "complete" "revision" prefix)
  246. (goto-char (point-min))
  247. (let ((ids ()))
  248. (while (re-search-forward "^.+" nil t)
  249. (push (match-string 0) ids))
  250. ids)))
  251. (defun vc-mtn-revision-completion-table (files)
  252. ;; TODO: Implement completion for selectors
  253. ;; TODO: Implement completion for composite selectors.
  254. (lexical-let ((files files))
  255. ;; What about using `files'?!? --Stef
  256. (lambda (string pred action)
  257. (cond
  258. ;; "Tag" selectors.
  259. ((string-match "\\`t:" string)
  260. (complete-with-action action
  261. (mapcar (lambda (tag) (concat "t:" tag))
  262. (vc-mtn-list-tags))
  263. string pred))
  264. ;; "Branch" selectors.
  265. ((string-match "\\`b:" string)
  266. (complete-with-action action
  267. (mapcar (lambda (tag) (concat "b:" tag))
  268. (vc-mtn-list-branches))
  269. string pred))
  270. ;; "Head" selectors. Not sure how they differ from "branch" selectors.
  271. ((string-match "\\`h:" string)
  272. (complete-with-action action
  273. (mapcar (lambda (tag) (concat "h:" tag))
  274. (vc-mtn-list-branches))
  275. string pred))
  276. ;; "ID" selectors.
  277. ((string-match "\\`i:" string)
  278. (complete-with-action action
  279. (mapcar (lambda (tag) (concat "i:" tag))
  280. (vc-mtn-list-revision-ids
  281. (substring string (match-end 0))))
  282. string pred))
  283. (t
  284. (complete-with-action action
  285. '("t:" "b:" "h:" "i:"
  286. ;; Completion not implemented for these.
  287. "a:" "c:" "d:" "e:" "l:")
  288. string pred))))))
  289. (provide 'vc-mtn)
  290. ;;; vc-mtn.el ends here