PageRenderTime 56ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/dvc/mode/lisp/dvc-core.el

https://bitbucket.org/atollena/tidyconfig-antoine-packs
Emacs Lisp | 1205 lines | 952 code | 149 blank | 104 comment | 41 complexity | 2121e17d30cf9c07615a6c314efca73a MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, JSON
  1. ;;; dvc-core.el --- Core functions for distributed version control
  2. ;; Copyright (C) 2005-2009 by all contributors
  3. ;; Author: Stefan Reichoer, <stefan@xsteve.at>
  4. ;; Contributions From:
  5. ;; Matthieu Moy <Matthieu.Moy@imag.fr>
  6. ;; This file is free software; you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation; either version 3, or (at your option)
  9. ;; any later version.
  10. ;; This file is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs; see the file COPYING. If not, write to
  16. ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17. ;; Boston, MA 02110-1301, USA.
  18. ;;; Commentary:
  19. ;; This file provides the low-level functions used by the DVC interfaces
  20. ;; to distributed revison control systems.
  21. ;;; History:
  22. ;; This file holds general useful functions, previously only used for tla.
  23. ;;; Code:
  24. (require 'dvc-defs)
  25. (require 'dvc-register)
  26. (eval-and-compile (require 'dvc-utils))
  27. (require 'dvc-buffers)
  28. (eval-when-compile (require 'cl))
  29. (eval-when-compile (require 'dired))
  30. (eval-and-compile (require 'dvc-lisp))
  31. (defvar dvc-sh-executable "sh" "The shell that is used for dvc interaction.")
  32. ;; --------------------------------------------------------------------------------
  33. ;; Various constants
  34. ;; --------------------------------------------------------------------------------
  35. (defconst dvc-mark (dvc-face-add "*" 'dvc-mark) "Fontified string used for marking.")
  36. (defconst dvc-exclude (dvc-face-add "E" 'dvc-mark) "Fontified string used for excluded files.")
  37. ;; --------------------------------------------------------------------------------
  38. ;; Internal variables
  39. ;; --------------------------------------------------------------------------------
  40. (defvar dvc-memorized-log-header nil)
  41. (defvar dvc-memorized-log-message nil)
  42. (defvar dvc-memorized-version nil)
  43. (defvar dvc-memorized-patch-sender nil)
  44. ;; --------------------------------------------------------------------------------
  45. ;; Various helper functions
  46. ;; --------------------------------------------------------------------------------
  47. ;; list-buffers-directory is used by uniquify to get the directory for
  48. ;; the buffer when buffer-file-name is nil, as it is for many dvc
  49. ;; buffers (dvc-diff-mode, etc). It needs to survive
  50. ;; kill-all-local-variables, so we declare it permanent local.
  51. (make-variable-buffer-local 'list-buffers-directory)
  52. (put 'list-buffers-directory 'permanent-local t)
  53. (defun dvc-find-tree-root-file-first (file-or-dir &optional location)
  54. "Find FILE-OR-DIR upward in the file system from LOCATION.
  55. Finding is continued upward to \"/\" until FILE-OR-DIR can be found.
  56. Once FILE-OR-DIR is found, the finding is broken off.
  57. A directory which holds FILE-OR-DIR is returned. If no such directory
  58. `nil' is returned. `default-directory' is used instead if LOCATION is not
  59. given,
  60. The resulting directory is guaranteed to end in a \"/\" character.
  61. This function may be useful to find \{arch\} and/or _darcs directories."
  62. (let ((pwd (or location default-directory))
  63. (pwd-stack nil)
  64. new-pwd)
  65. (while (not (or (string= pwd "/")
  66. (member pwd pwd-stack)
  67. (file-exists-p (concat (file-name-as-directory pwd)
  68. file-or-dir))))
  69. (setq pwd-stack (cons pwd pwd-stack))
  70. (setq new-pwd
  71. (dvc-expand-file-name (concat (file-name-as-directory pwd) "..")))
  72. ;; detect MS-Windows roots (c:/, d:/, ...)
  73. (setq pwd (if (string= new-pwd pwd) "/" new-pwd)))
  74. (unless (string= pwd "/")
  75. (setq pwd (replace-regexp-in-string "\\([^:]\\)/*$" "\\1" pwd))
  76. (setq pwd (file-name-as-directory pwd))
  77. (if (memq system-type '(ms-dos windows-nt))
  78. (expand-file-name pwd)
  79. pwd))))
  80. (defun dvc-tree-root-helper (file-or-dir interactivep msg
  81. &optional location no-error)
  82. "Find FILE-OR-DIR upward in the file system from LOCATION.
  83. Calls `dvc-find-tree-root-file-first', shows a message when
  84. called interactively, and manages no-error.
  85. If LOCATION is nil, the tree root is returned, and it is
  86. guaranteed to end in a \"/\" character.
  87. MSG must be of the form \"%S is not a ...-managed tree\"."
  88. (let ((location (dvc-uniquify-file-name location)))
  89. (let ((pwd (dvc-find-tree-root-file-first
  90. file-or-dir location)))
  91. (when (and interactivep pwd)
  92. (dvc-trace "%s" pwd))
  93. (or pwd
  94. (if no-error
  95. nil
  96. (error msg
  97. (or location default-directory)))))))
  98. (defun dvc-find-tree-root-file-last (file-or-dir &optional location)
  99. "Like `dvc-find-tree-root-file-upward' but recursively if FILE-OR-DIR is found.
  100. Finding is started from LOCATION but is stoped when FILE-OR-DIR cannot be found.
  101. Fiddled is continued upward while FILE-OR-DIR can be found.
  102. The last found directory which holds FILE-OR-DIR is returned. `nil' is returned
  103. if finding failed.
  104. `default-directory' is used instead if LOCATION is not given,
  105. This function may be useful to find CVS or .svn directories"
  106. (let ((pwd (or location default-directory))
  107. old-pwd)
  108. (while (and pwd (not (string= pwd "/")))
  109. (if (file-exists-p (concat (file-name-as-directory pwd)
  110. file-or-dir))
  111. (setq old-pwd pwd
  112. pwd (expand-file-name (concat (file-name-as-directory pwd)
  113. "..")))
  114. (setq pwd nil)))
  115. (when old-pwd
  116. (expand-file-name
  117. (replace-regexp-in-string "/+$" "/" old-pwd)))))
  118. (defmacro dvc-make-bymouse-function (function)
  119. "Create a new function by adding mouse interface to FUNCTION.
  120. The new function is named FUNCTION-by-mouse; and takes one argument,
  121. a mouse click event.
  122. Thew new function moves the point to the place where mouse is clicked
  123. then invoke FUNCTION."
  124. (declare (debug (&define name :name -by-mouse)))
  125. `(defun ,(intern (concat (symbol-name function) "-by-mouse")) (event)
  126. ,(concat "`" (symbol-name function) "'" " with mouse interface.")
  127. (interactive "e")
  128. (mouse-set-point event)
  129. (,function)))
  130. ;; Adapted from `dired-delete-file' in Emacs 22
  131. (defun dvc-delete-recursively (file)
  132. "Delete FILE or directory recursively."
  133. (let (files)
  134. (if (not (eq t (car (file-attributes file))))
  135. (delete-file file)
  136. (when (setq files
  137. (directory-files
  138. file t "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*"))
  139. (while files
  140. (dvc-delete-recursively (car files))
  141. (setq files (cdr files))))
  142. (delete-directory file))))
  143. ;; --------------------------------------------------------------------------------
  144. ;; File selection helpers
  145. ;; --------------------------------------------------------------------------------
  146. (defvar dvc-get-file-info-at-point-function nil
  147. "Function used to get the file at point, anywhere.")
  148. (defun dvc-get-file-info-at-point ()
  149. "Gets the filename at point, according to mode.
  150. Calls the function `dvc-get-file-info-at-point-function' if defined.
  151. When in dired mode, return the file where point is.
  152. Otherwise return the buffer file name."
  153. (cond (dvc-get-file-info-at-point-function
  154. (funcall dvc-get-file-info-at-point-function))
  155. ((eq major-mode 'dired-mode)
  156. (dired-get-filename))
  157. (t (buffer-file-name))))
  158. ;;;###autoload
  159. (defun dvc-current-file-list (&optional selection-mode)
  160. "Return a list of currently active files.
  161. When in dired mode, return the marked files or the file under point.
  162. In a legacy DVC mode, return `dvc-buffer-marked-file-list' if non-nil.
  163. In a fileinfo DVC mode, return `dvc-fileinfo-marked-files'.
  164. otherwise the result depends on SELECTION-MODE:
  165. * When 'nil-if-none-marked, return nil.
  166. * When 'all-if-none-marked, return all files.
  167. * Otherwise return result of calling `dvc-get-file-info-at-point'."
  168. (cond
  169. ((eq major-mode 'dired-mode)
  170. (dired-get-marked-files))
  171. ((dvc-derived-mode-p 'dvc-diff-mode 'dvc-status-mode)
  172. (or (remove nil dvc-buffer-marked-file-list)
  173. (dvc-fileinfo-marked-files)
  174. (cond
  175. ((eq selection-mode 'nil-if-none-marked)
  176. nil)
  177. ((eq selection-mode 'all-if-none-marked)
  178. (dvc-fileinfo-all-files))
  179. (t (list (dvc-get-file-info-at-point))))))
  180. ((eq major-mode 'dvc-bookmark-mode)
  181. (cond
  182. ((eq selection-mode 'nil-if-none-marked)
  183. nil)
  184. (t
  185. (error "selection-mode %s not implemented for dvc bookmark buffer" selection-mode))))
  186. ;; If other modes are added here, dvc-log-edit must be updated to
  187. ;; support them as well.
  188. (t
  189. ;; Some other mode. We assume it has no notion of "marked files",
  190. ;; so there are none marked. The only file name available is
  191. ;; buffer-file-name, so we could just return that. But some DVC
  192. ;; mode might set dvc-get-file-info-at-point-function without
  193. ;; updating this function, so support that.
  194. (if (eq selection-mode 'nil-if-none-marked)
  195. nil
  196. (list (dvc-get-file-info-at-point))))))
  197. (defun dvc-confirm-read-file-name (prompt &optional mustmatch file-name default-filename)
  198. "A wrapper around `read-file-name' that provides some useful defaults."
  199. (unless file-name
  200. (setq file-name (dvc-get-file-info-at-point)))
  201. (read-file-name prompt
  202. (file-name-directory (or file-name ""))
  203. default-filename
  204. mustmatch
  205. (file-name-nondirectory (or file-name ""))))
  206. (defun dvc-confirm-read-file-name-list (prompt &optional files single-prompt mustmatch)
  207. (or
  208. (if dvc-test-mode files)
  209. (let ((num-files (length files)))
  210. (if (= num-files 1)
  211. (let ((confirmed-file-name
  212. (dvc-confirm-read-file-name single-prompt mustmatch (car files))))
  213. ;; I don't think `dvc-confirm-read-file-name' can return nil.
  214. (assert confirmed-file-name)
  215. (list confirmed-file-name))
  216. (and (y-or-n-p (format prompt num-files))
  217. files)))))
  218. (defcustom dvc-confirm-file-op-method 'y-or-n-p
  219. "Function to use for confirming file-based DVC operations.
  220. Some valid options are:
  221. y-or-n-p: Prompt for 'y' or 'n' keystroke.
  222. yes-or-no-p: Prompt for \"yes\" or \"no\" string.
  223. dvc-always-true: Do not display a prompt."
  224. :type 'function
  225. :group 'dvc)
  226. (defun dvc-always-true (&rest ignore)
  227. "Do nothing and return t.
  228. This function accepts any number of arguments, but ignores them."
  229. (interactive)
  230. t)
  231. (defun dvc-confirm-file-op (operation files confirm)
  232. "Confirm OPERATION (a string, used in prompt) on FILE (list of strings).
  233. If CONFIRM is nil, just return FILES (no prompt).
  234. Returns FILES, or nil if not confirmed.
  235. If you want to adjust the function called to confirm the
  236. operation, then customize the `dvc-confirm-file-op-method' function."
  237. (or
  238. ;; Allow bypassing confirmation with `dvc-test-mode'. See
  239. ;; tests/xmtn-tests.el dvc-status-add.
  240. (if dvc-test-mode files)
  241. ;; Abstracted from pcvs.el cvs-do-removal
  242. (if (not confirm)
  243. files
  244. (let ((nfiles (length files)))
  245. (if (funcall (or (and (functionp dvc-confirm-file-op-method)
  246. dvc-confirm-file-op-method)
  247. 'y-or-n-p)
  248. (if (= 1 nfiles)
  249. (format "%s file: \"%s\" ? "
  250. operation
  251. (car files))
  252. (format "%s %d files? "
  253. operation
  254. nfiles)))
  255. files
  256. nil)))))
  257. (defun dvc-dvc-files-to-commit ()
  258. ;;todo: set the correct modifier, one of dvc-modified, dvc-added, dvc-move, now use only nil
  259. ;; FIXME: this is only used by dvc-log-insert-commit-file-list; should just merge this code there.
  260. (let ((files
  261. (with-current-buffer dvc-partner-buffer (dvc-current-file-list 'all-if-none-marked))))
  262. (mapcar (lambda (arg) (cons nil arg)) files)))
  263. (defun dvc-find-file-at-point ()
  264. "Opens the file at point.
  265. The filename is obtained with `dvc-get-file-info-at-point'."
  266. (interactive)
  267. (let* ((file (dvc-get-file-info-at-point)))
  268. (cond
  269. ((not file)
  270. (error "No file at point"))
  271. (t
  272. (find-file file)))))
  273. (dvc-make-bymouse-function dvc-find-file-at-point)
  274. (defun dvc-find-file-other-window ()
  275. "Visit the current file in the other window.
  276. The filename is obtained with `dvc-get-file-info-at-point'."
  277. (interactive)
  278. (let ((file (dvc-get-file-info-at-point)))
  279. (if file
  280. (progn
  281. (find-file-other-window file))
  282. (error "No file at point"))))
  283. (defun dvc-view-file ()
  284. "Visit the current file in `view-mode'.
  285. The filename is obtained with `dvc-get-file-info-at-point'."
  286. (interactive)
  287. (let ((file (dvc-get-file-info-at-point)))
  288. (if file
  289. (view-file-other-window file)
  290. (error "No file at point"))))
  291. (defun dvc-dired-jump ()
  292. "Jump to a dired buffer, containing the file at point."
  293. (interactive)
  294. (let ((file-full-path (expand-file-name (or (dvc-get-file-info-at-point) ""))))
  295. (let ((default-directory (file-name-directory file-full-path)))
  296. (dvc-funcall-if-exists dired-jump))
  297. (dired-goto-file file-full-path)))
  298. (defun dvc-purge-files (&rest files)
  299. "Delete FILES from the harddisk. No backup is created for these FILES.
  300. These function bypasses the used revision control system."
  301. (interactive (dvc-current-file-list))
  302. (let ((multiprompt (format "Are you sure to purge %%d files? "))
  303. (singleprompt (format "Purge file: ")))
  304. (when (dvc-confirm-read-file-name-list multiprompt files singleprompt nil)
  305. (mapcar #'delete-file files)
  306. (message "Purged %S" files))))
  307. (defun dvc-current-executable ()
  308. "Return the name of the binary associated with the current dvc backend.
  309. This uses `dvc-current-active-dvc'.
  310. \"DVC\" is returned if `dvc-current-active-dvc' returns nil."
  311. (let ((dvc (dvc-current-active-dvc)))
  312. (if (not dvc)
  313. "DVC"
  314. (dvc-variable dvc "executable"))))
  315. ;; partner buffer stuff
  316. (defvar dvc-partner-buffer nil
  317. "DVC Partner buffer; stores diff buffer for log-edit, etc.
  318. Local to each buffer, not killed by kill-all-local-variables.")
  319. (make-variable-buffer-local 'dvc-partner-buffer)
  320. (put 'dvc-partner-buffer 'permanent-local t)
  321. (defun dvc-buffer-pop-to-partner-buffer ()
  322. "Pop to dvc-partner-buffer, if available."
  323. (interactive)
  324. (if (and (boundp 'dvc-partner-buffer) dvc-partner-buffer)
  325. (if (buffer-live-p dvc-partner-buffer)
  326. (pop-to-buffer dvc-partner-buffer)
  327. (message "Partner buffer has been killed"))
  328. (message "No partner buffer set for this buffer.")))
  329. (defmacro dvc-with-keywords (keywords plist &rest body)
  330. "Execute a body of code with keywords bound.
  331. Each keyword listed in KEYWORDS is bound to its value from PLIST, then
  332. BODY is evaluated."
  333. (declare (indent 1) (debug (sexp form body)))
  334. (flet ((keyword-to-symbol (keyword)
  335. (intern (substring (symbol-name keyword) 1))))
  336. (let ((keyword (make-symbol "keyword"))
  337. (default (make-symbol "default")))
  338. `(let ,(mapcar (lambda (keyword-entry)
  339. (keyword-to-symbol (if (consp keyword-entry)
  340. (car keyword-entry)
  341. keyword-entry)))
  342. keywords)
  343. (dolist (keyword-entry ',keywords)
  344. (let ((,keyword (if (consp keyword-entry)
  345. (car keyword-entry)
  346. keyword-entry))
  347. (,default (if (consp keyword-entry)
  348. (cadr keyword-entry)
  349. nil)))
  350. (set (intern (substring (symbol-name ,keyword) 1))
  351. (or (cadr (member ,keyword ,plist))
  352. ,default))))
  353. ,@body))))
  354. ;; ----------------------------------------------------------------------------
  355. ;; Process management
  356. ;; ----------------------------------------------------------------------------
  357. ;; Candidates for process handlers
  358. (defun dvc-default-error-function (output error status arguments)
  359. "Default function called when a DVC process ends with a non-zero status.
  360. OUTPUT is the buffer containing process standard output.
  361. ERROR is the buffer containing process error output.
  362. STATUS indicates the return status of the program.
  363. ARGUMENTS is a list of the arguments that the process was called with."
  364. (if (> (with-current-buffer error (point-max)) 1)
  365. (dvc-show-error-buffer error)
  366. (if (> (with-current-buffer output (point-max)) 1)
  367. (dvc-show-error-buffer output)
  368. (error "`%s %s' failed with code %d and no output!"
  369. (dvc-current-executable)
  370. (mapconcat 'identity arguments " ")
  371. status)))
  372. (error "`%s %s' failed with code %d"
  373. (dvc-current-executable)
  374. (mapconcat 'identity arguments " ")
  375. status))
  376. (defvar dvc-default-killed-function-noerror 0
  377. "The number of killed processes we will ignore until throwing an error.
  378. If the value is 0, `dvc-default-killed-function' will throw an error.
  379. See `dvc-default-killed-function'.")
  380. (defun dvc-default-killed-function (output error status arguments)
  381. "Default function called when a DVC process is killed.
  382. OUTPUT is the buffer containing process standard output.
  383. ERROR is the buffer containing process error output.
  384. STATUS indicates the return status of the program.
  385. ARGUMENTS is a list of the arguments that the process was called with."
  386. (if (> dvc-default-killed-function-noerror 0)
  387. (setq dvc-default-killed-function-noerror
  388. (- dvc-default-killed-function-noerror 1))
  389. (dvc-switch-to-buffer error)
  390. (error "`%s %s' process killed !"
  391. (dvc-current-executable)
  392. (mapconcat 'identity arguments " "))))
  393. (defun dvc-null-handler (output error status arguments)
  394. "Handle a finished process without doing anything.
  395. Candidate as an argument for one of the keywords :finished, :error or :killed
  396. in `dvc-run-dvc-sync' or `dvc-run-dvc-async'.
  397. OUTPUT is the buffer containing process standard output.
  398. ERROR is the buffer containing process error output.
  399. STATUS indicates the return status of the program.
  400. ARGUMENTS is a list of the arguments that the process was called with."
  401. nil)
  402. (defun dvc-status-handler (output error status arguments)
  403. "Return an integer value that reflects the process status.
  404. Candidate as an argument for one of the keywords :finished, :error or :killed
  405. in `dvc-run-dvc-sync' or `dvc-run-dvc-async'.
  406. OUTPUT is the buffer containing process standard output.
  407. ERROR is the buffer containing process error output.
  408. STATUS indicates the return status of the program.
  409. ARGUMENTS is a list of the arguments that the process was called with."
  410. (cond ((numberp status) status)
  411. ((string-match "^exited abnormally with code \\(.*\\)" status)
  412. (string-to-number (match-string 1)))
  413. (t (error status))))
  414. (defun dvc-output-buffer-handler (output error status arguments)
  415. "Return the output of a finished process, stripping any trailing newline.
  416. OUTPUT is the buffer containing process standard output.
  417. ERROR is the buffer containing process error output.
  418. STATUS indicates the return status of the program.
  419. ARGUMENTS is a list of the arguments that the process was called with."
  420. (dvc-buffer-content output))
  421. (defun dvc-output-buffer-handler-withnewline (output error status arguments)
  422. "Same as dvc-output-buffer-handler, but keep potential final newline."
  423. (with-current-buffer output (buffer-string)))
  424. (defun dvc-output-and-error-buffer-handler (output error status arguments)
  425. "Return the output of a finished process, stripping any trailing newline.
  426. OUTPUT is the buffer containing process standard output.
  427. ERROR is the buffer containing process error output.
  428. STATUS indicates the return status of the program.
  429. ARGUMENTS is a list of the arguments that the process was called with."
  430. (concat (dvc-buffer-content output)
  431. (dvc-buffer-content error)))
  432. (defun dvc-output-buffer-split-handler (output error status arguments)
  433. "Return the output of a finished process as a list of lines.
  434. OUTPUT is the buffer containing process standard output.
  435. ERROR is the buffer containing process error output.
  436. STATUS indicates the return status of the program.
  437. ARGUMENTS is a list of the arguments that the process was called with."
  438. (split-string (dvc-buffer-content output) "\n"))
  439. (defun dvc-default-finish-function (output error status arguments)
  440. "Default function called when a DVC process terminates.
  441. OUTPUT is the buffer containing process standard output.
  442. ERROR is the buffer containing process error output.
  443. STATUS indicates the return status of the program.
  444. ARGUMENTS is a list of the arguments that the process was called with."
  445. (let ((has-output))
  446. (with-current-buffer output
  447. (dvc-process-buffer-mode)
  448. (setq has-output (> (point-max) 1)))
  449. (when has-output
  450. (dvc-switch-to-buffer output))
  451. (when (or dvc-debug has-output)
  452. (message "Process `%s %s' finished"
  453. (dvc-current-executable)
  454. (mapconcat 'identity arguments " ")))
  455. status))
  456. (defun dvc-finish-function-without-buffer-switch (output error status arguments)
  457. "Similar to `dvc-default-finish-function' but no buffer switch.
  458. OUTPUT is the buffer containing process standard output.
  459. ERROR is the buffer containing process error output.
  460. STATUS indicates the return status of the program.
  461. ARGUMENTS is a list of the arguments that the process was called
  462. with."
  463. (with-current-buffer output
  464. (dvc-trace "Process `%s %s' finished"
  465. (dvc-current-executable)
  466. (mapconcat 'identity arguments " "))
  467. status))
  468. (defvar dvc-process-running nil
  469. "List of DVC processes running.
  470. A value of nil indicates no processes are running.
  471. The list is a list of pairs (process event) where EVENT is the event
  472. corresponding to the beginning of the execution of process. It can be
  473. used to get more info about the process.")
  474. (defun dvc-build-dvc-command (dvc list-args)
  475. "Build a shell command to run DVC with args LIST-ARGS.
  476. DVC can be one of 'baz, 'xhg, ..."
  477. (let ((executable (executable-find (dvc-variable dvc "executable"))))
  478. ;; 'executable-find' allows leading ~
  479. (if (not executable)
  480. (error "executable for %s not found" (symbol-name dvc)))
  481. (mapconcat 'shell-quote-argument
  482. (cons executable
  483. (remq nil list-args))
  484. " ")))
  485. (defcustom dvc-password-prompt-regexp
  486. "[Pp]ass\\(word\\|phrase\\).*:\\s *\\'"
  487. "*Regexp matching prompts for passwords in the inferior process."
  488. :type 'regexp
  489. :group 'dvc)
  490. (defun dvc-process-filter (proc string &optional no-insert)
  491. "Filter PROC's STRING.
  492. Prompt for password with `read-passwd' if the output of PROC matches
  493. `dvc-password-prompt-regexp'.
  494. If NO-INSERT is non-nil, do not insert the string.
  495. In all cases, a new string is returned after normalizing newlines."
  496. (with-current-buffer (process-buffer proc)
  497. (setq string (replace-regexp-in-string "\015" "\n" string))
  498. (unless no-insert
  499. (goto-char (process-mark proc))
  500. (insert string)
  501. (set-marker (process-mark proc) (point)))
  502. (when (string-match dvc-password-prompt-regexp string)
  503. (string-match "^\\([^\n]+\\)\n*\\'" string)
  504. (let ((passwd (read-passwd (match-string 1 string))))
  505. (process-send-string proc (concat passwd "\n"))))
  506. string))
  507. (defun dvc-prepare-environment (env)
  508. "By default, do not touch the environment"
  509. env)
  510. (defun dvc-default-global-argument ()
  511. "By default, no global argument."
  512. nil)
  513. (defun dvc-run-dvc-async (dvc arguments &rest keys)
  514. "Run a process asynchronously.
  515. Current directory for the process is the current `default-directory'.
  516. ARGUMENTS is a list of arguments. nil values in this list are removed.
  517. KEYS is a list of keywords and values. Possible keywords are:
  518. :finished ....... Function run when the process finishes. If none
  519. specified, `dvc-default-finish-function' is run.
  520. :killed ......... Function run when the process is killed. If none
  521. specified, `dvc-default-killed-function' is run.
  522. :error .......... Function run when the process exits with a non 0
  523. status. If none specified,
  524. `dvc-default-error-function' is run.
  525. All these functions take 4 arguments : output, error, status, and
  526. arguments.
  527. - \"output\" is the output buffer
  528. - \"error\" is the buffer where standard error is redirected
  529. - \"status\" is the numeric exit-status or the signal number
  530. - \"arguments\" is the list of arguments, as a list of strings,
  531. like '(\"changes\" \"--diffs\")
  532. `dvc-null-handler' can be used here if there's nothing to do.
  533. :filter Function to call every time we receive output from
  534. the process. It should take arguments proc and string.
  535. The string will have been run through
  536. `dvc-process-filter' to deal with password prompts and
  537. newlines.
  538. :output-buffer .. Buffer where the output of the process should be
  539. redirected. If none specified, a new one is
  540. created, and will be entered in
  541. `dvc-dead-process-buffer-queue' to be killed
  542. later.
  543. :error-buffer ... Buffer where the standard error of the process
  544. should be redirected.
  545. :related-buffer . Defaults to `current-buffer'. This is the buffer
  546. where the result of the process will be used. If
  547. this buffer is killed before the end of the
  548. execution, the user is prompted if he wants to kill
  549. the process."
  550. (dvc-with-keywords
  551. (:finished :killed :error :filter
  552. :output-buffer :error-buffer :related-buffer)
  553. keys
  554. (let* ((output-buf (or (and output-buffer
  555. (get-buffer-create output-buffer))
  556. (dvc-new-process-buffer nil dvc)))
  557. (error-buf (or (and error-buffer (get-buffer-create error-buffer))
  558. (dvc-new-error-buffer nil dvc)))
  559. (error-file (dvc-make-temp-name "dvc-errors"))
  560. (global-arg (funcall (dvc-function dvc "default-global-argument")))
  561. (command (dvc-build-dvc-command
  562. dvc (append global-arg arguments)))
  563. ;; Make the `default-directory' unique. The trailing slash
  564. ;; may be necessary in some cases.
  565. (default-directory (dvc-uniquify-file-name default-directory))
  566. (process
  567. (let ((process-environment
  568. (funcall (dvc-function dvc "prepare-environment")
  569. process-environment)))
  570. (with-current-buffer output-buf
  571. ;; process filter will need to know which dvc to run
  572. ;; if there is a choice
  573. (setq dvc-buffer-current-active-dvc dvc))
  574. ;; `start-process' sends both stderr and stdout to
  575. ;; `output-buf'. But we want to keep stderr separate. So
  576. ;; we use a shell to redirect stderr before Emacs sees
  577. ;; it. Note that this means we require "sh" even on
  578. ;; MS Windows.
  579. (start-process
  580. (dvc-variable dvc "executable") output-buf
  581. dvc-sh-executable "-c"
  582. (format "%s 2> %s"
  583. command error-file))))
  584. (process-event
  585. (list process
  586. (dvc-log-event output-buf
  587. error-buf
  588. command
  589. default-directory "started"))))
  590. (with-current-buffer (or related-buffer (current-buffer))
  591. (dvc-trace "Running process `%s' in `%s'" command default-directory)
  592. (add-to-list 'dvc-process-running process-event)
  593. (set-process-filter
  594. process
  595. (if (not filter)
  596. 'dvc-process-filter
  597. (dvc-capturing-lambda (proc string)
  598. (funcall (capture filter)
  599. proc
  600. (dvc-process-filter proc string t)))))
  601. (set-process-sentinel
  602. process
  603. (dvc-capturing-lambda (process event)
  604. (let ((default-directory (capture default-directory)))
  605. (dvc-log-event (capture output-buf) (capture error-buf)
  606. (capture command)
  607. (capture default-directory)
  608. (dvc-strip-final-newline event))
  609. (setq dvc-process-running
  610. (delq (capture process-event) dvc-process-running))
  611. (when (file-exists-p (capture error-file))
  612. (with-current-buffer (capture error-buf)
  613. (insert-file-contents (capture error-file)))
  614. (delete-file (capture error-file)))
  615. (let ((state (process-status process))
  616. (status (process-exit-status process))
  617. (dvc-temp-current-active-dvc (capture dvc)))
  618. (unwind-protect
  619. (cond ((and (eq state 'exit) (= status 0))
  620. (funcall (or (capture finished)
  621. 'dvc-default-finish-function)
  622. (capture output-buf) (capture error-buf)
  623. status (capture arguments)))
  624. ((eq state 'signal)
  625. (funcall (or (capture killed)
  626. 'dvc-default-killed-function)
  627. (capture output-buf) (capture error-buf)
  628. status (capture arguments)))
  629. ((eq state 'exit) ;; status != 0
  630. (funcall (or (capture error)
  631. 'dvc-default-error-function)
  632. (capture output-buf) (capture error-buf)
  633. status (capture arguments)))))
  634. ;; Schedule any buffers we created for killing
  635. (unless (capture output-buffer)
  636. (dvc-kill-process-buffer (capture output-buf)))
  637. (unless (capture error-buffer)
  638. (dvc-kill-process-buffer (capture error-buf)))))))
  639. process))))
  640. (defun dvc-run-dvc-sync (dvc arguments &rest keys)
  641. "Run DVC synchronously.
  642. See `dvc-run-dvc-async' for details on possible ARGUMENTS and KEYS."
  643. (dvc-with-keywords
  644. (:finished :killed :error :output-buffer :error-buffer :related-buffer)
  645. keys
  646. (let* ((output-buf (or (and output-buffer
  647. (get-buffer-create output-buffer))
  648. (dvc-new-process-buffer t dvc)))
  649. (error-buf (or (and error-buffer (get-buffer-create error-buffer))
  650. (dvc-new-error-buffer t dvc)))
  651. (global-arg (funcall (dvc-function dvc "default-global-argument")))
  652. (command (dvc-build-dvc-command
  653. dvc (append global-arg arguments)))
  654. (arguments (remq nil arguments))
  655. (error-file (dvc-make-temp-name "arch-errors"))
  656. ;; Make the `default-directory' unique. The trailing slash
  657. ;; may be necessary in some cases.
  658. (default-directory (dvc-uniquify-file-name default-directory)))
  659. (with-current-buffer (or related-buffer (current-buffer))
  660. (dvc-log-event output-buf error-buf command default-directory
  661. "started")
  662. (let ((status (let ((process-environment
  663. (funcall (dvc-function dvc "prepare-environment")
  664. process-environment)))
  665. (call-process dvc-sh-executable nil output-buf nil "-c"
  666. (format "%s 2> %s"
  667. command
  668. error-file)))))
  669. (when (file-exists-p error-file)
  670. (with-current-buffer error-buf
  671. (insert-file-contents error-file))
  672. (delete-file error-file))
  673. (unwind-protect
  674. (let ((dvc-temp-current-active-dvc dvc))
  675. (cond ((stringp status)
  676. (when (string= status "Terminated")
  677. (funcall (or killed 'dvc-default-killed-function)
  678. output-buf error-buf status arguments)))
  679. ((numberp status)
  680. (if (zerop status)
  681. (funcall (or finished 'dvc-default-finish-function)
  682. output-buf error-buf status arguments)
  683. (funcall (or error 'dvc-default-error-function)
  684. output-buf error-buf status arguments)))
  685. (t (message "Unknown status - %s" status))))
  686. ;; Schedule any buffers we created for killing
  687. (unless output-buffer (dvc-kill-process-buffer output-buf))
  688. (unless error-buffer (dvc-kill-process-buffer error-buf))))))))
  689. (defun dvc-processes-related-to-buffer (buffer)
  690. "Returns a list of DVC process whose related buffer is BUFFER."
  691. (let ((accu nil))
  692. (dolist (entry dvc-process-running)
  693. (when (eq (dvc-event-related-buffer (cadr entry)) buffer)
  694. (push (car entry) accu)))
  695. (setq accu (nreverse accu))
  696. accu))
  697. (defun dvc-kill-process-maybe (buffer)
  698. "Prompts and possibly kill process whose related buffer is BUFFER."
  699. ;; FIXME: It would be reasonable to run this here, to give any
  700. ;; process one last chance to run. But somehow this screws up
  701. ;; package-maint-clean-some-elc. (accept-process-output)
  702. (let* ((processes (dvc-processes-related-to-buffer buffer))
  703. (l (length processes)))
  704. (when (and processes
  705. (y-or-n-p (format "%s process%s running in buffer %s. Kill %s? "
  706. l (if (= l 1) "" "es")
  707. (buffer-name buffer)
  708. (if (= l 1) "it" "them"))))
  709. (dolist (process processes)
  710. (when (eq (process-status process) 'run)
  711. (incf dvc-default-killed-function-noerror)
  712. (kill-process process)))))
  713. ;; make sure it worked
  714. (let ((processes (dvc-processes-related-to-buffer buffer)))
  715. (when processes
  716. (error "Process still running in buffer %s" buffer))))
  717. (add-hook 'kill-buffer-hook 'dvc-kill-buffer-function)
  718. (defun dvc-kill-buffer-function ()
  719. "Function run when a buffer is killed."
  720. (dvc-buffers-tree-remove (current-buffer))
  721. (dvc-kill-process-maybe (current-buffer)))
  722. (defun dvc-run-dvc-display-as-info (dvc arg-list &optional show-error-buffer info-string asynchron)
  723. "Call either `dvc-run-dvc-async' or `dvc-run-dvc-sync' and display the result in an info buffer.
  724. When INFO-STRING is given, insert it at the buffer beginning."
  725. (let ((buffer (dvc-get-buffer-create dvc 'info)))
  726. (funcall (if asynchron 'dvc-run-dvc-async 'dvc-run-dvc-sync) dvc arg-list
  727. :finished
  728. (dvc-capturing-lambda (output error status arguments)
  729. (progn
  730. (with-current-buffer (capture buffer)
  731. (let ((inhibit-read-only t))
  732. (erase-buffer)
  733. (dvc-info-buffer-mode)
  734. (when (capture info-string)
  735. (insert (capture info-string)))
  736. (insert-buffer-substring output)
  737. (when (capture show-error-buffer)
  738. (insert-buffer-substring error))
  739. (toggle-read-only 1)))
  740. (dvc-switch-to-buffer (capture buffer)))))))
  741. (defvar dvc-info-buffer-mode-map
  742. (let ((map (make-sparse-keymap)))
  743. (define-key map (dvc-prefix-buffer ?L) 'dvc-open-internal-log-buffer)
  744. (define-key map dvc-keyvec-quit 'dvc-buffer-quit)
  745. map)
  746. "Keymap used in a dvc info buffer.")
  747. (define-derived-mode dvc-info-buffer-mode fundamental-mode
  748. "DVC info mode"
  749. "Major mode for dvc info buffers"
  750. (dvc-install-buffer-menu)
  751. (toggle-read-only 1))
  752. (defvar dvc-log-cookie nil)
  753. (defstruct (dvc-event) output-buffer error-buffer related-buffer
  754. command tree event time)
  755. (defsubst dvc-log-printer-print-buffer (buffer function)
  756. "Helper function for `dvc-log-printer'.
  757. Print a buffer filed for BUFFER during printing a log event.
  758. The printed name of BUFFER is mouse sensitive. If the user
  759. clicks it, FUNCTION is invoked."
  760. (let ((alive-p (buffer-live-p buffer))
  761. map)
  762. (dvc-face-add
  763. (or
  764. ;; pp-to-string is very costly.
  765. ;; Handle the typical case with hard-coding.
  766. (unless alive-p "#<killed buffer>")
  767. ;; Normal case.
  768. (buffer-name buffer)
  769. ;; Extra case.
  770. (pp-to-string buffer))
  771. 'dvc-buffer
  772. (when alive-p
  773. (setq map (make-sparse-keymap))
  774. (define-key map [mouse-2] function)
  775. map)
  776. nil
  777. "Show the buffer")))
  778. (defun dvc-log-recently-p (elem limit-minute)
  779. "Check ELEM recorded a recent event or not.
  780. Return nil If ELEM recorded an event older than LIMIT-MINUTE.
  781. Else return t."
  782. (let* ((recorded (dvc-event-time elem))
  783. (cur (current-time))
  784. (diff-minute (/ (+ (* 65536 (- (nth 0 cur)
  785. (nth 0 recorded)))
  786. (- (nth 1 cur)
  787. (nth 1 recorded)))
  788. 60)))
  789. (if (> limit-minute diff-minute)
  790. t
  791. nil)))
  792. (defun dvc-log-printer (elem)
  793. "Arch event printer which prints ELEM."
  794. (let ((event (dvc-event-event elem))
  795. (p (point)))
  796. (insert
  797. "Command: " (dvc-event-command elem)
  798. "\nDirectory: " (dvc-face-add (or (dvc-event-tree elem) "(nil)")
  799. 'dvc-local-directory)
  800. "\nDate: " (format-time-string "%c" (dvc-event-time elem))
  801. "\nRelated Buffer: " (dvc-log-printer-print-buffer
  802. (dvc-event-related-buffer elem)
  803. 'dvc-switch-to-related-buffer-by-mouse)
  804. "\nOutput Buffer: " (dvc-log-printer-print-buffer
  805. (dvc-event-output-buffer elem)
  806. 'dvc-switch-to-output-buffer-by-mouse)
  807. "\nError Buffer: " (dvc-log-printer-print-buffer
  808. (dvc-event-error-buffer elem)
  809. 'dvc-switch-to-error-buffer-by-mouse)
  810. (if (not (string= event "started"))
  811. (concat "\nEvent: " event)
  812. "")
  813. "\n")
  814. ;; Reflect the point to `default-directory'.
  815. ;; NOTE: XEmacs doesn't have `point-entered' special text property.
  816. (put-text-property
  817. p (point)
  818. 'point-entered (lambda (old new)
  819. (setq default-directory
  820. (dvc-event-tree
  821. (ewoc-data
  822. (ewoc-locate dvc-log-cookie))))))))
  823. (defmacro dvc-switch-to-buffer-macro (function accessor)
  824. "Define a FUNCTION for switching to the buffer associated with some event.
  825. ACCESSOR is a function for retrieving the appropriate buffer from a
  826. `dvc-event' structure."
  827. (declare (debug (&define name symbolp)))
  828. `(defun ,function ()
  829. "In a log buffer, pops to the output or error buffer corresponding to the
  830. process at point"
  831. (interactive)
  832. (let ((buffer (,accessor
  833. (ewoc-data (ewoc-locate dvc-log-cookie)))))
  834. (cond ((buffer-live-p buffer)
  835. (dvc-switch-to-buffer buffer)
  836. (unless (member buffer
  837. (mapcar (lambda (p)
  838. (process-buffer (car p)))
  839. dvc-process-running))
  840. (dvc-process-buffer-mode)))
  841. (t (error "Buffer has been killed"))))))
  842. (dvc-switch-to-buffer-macro dvc-switch-to-output-buffer
  843. dvc-event-output-buffer)
  844. (dvc-switch-to-buffer-macro dvc-switch-to-error-buffer
  845. dvc-event-error-buffer)
  846. (dvc-switch-to-buffer-macro dvc-switch-to-related-buffer
  847. dvc-event-related-buffer)
  848. (dvc-make-bymouse-function dvc-switch-to-output-buffer)
  849. (dvc-make-bymouse-function dvc-switch-to-error-buffer)
  850. (dvc-make-bymouse-function dvc-switch-to-related-buffer)
  851. (defun dvc-log-event (output error command tree event)
  852. "Log an event in the `dvc-log-buffer' buffer.
  853. OUTPUT is the buffer containing process standard output.
  854. ERROR is the buffer containing process error output.
  855. COMMAND is the command that was executed.
  856. TREE is the process's working directory.
  857. EVENT is the event that occurred.
  858. Returns that event."
  859. (unless (and dvc-log-cookie
  860. (buffer-live-p (ewoc-buffer dvc-log-cookie)))
  861. (with-current-buffer (get-buffer-create dvc-log-buffer)
  862. (setq dvc-log-cookie
  863. (ewoc-create (dvc-ewoc-create-api-select
  864. #'dvc-log-printer)))
  865. (dvc-log-buffer-mode)))
  866. (let ((related-buffer (current-buffer)))
  867. (with-current-buffer (ewoc-buffer dvc-log-cookie)
  868. (let ((elem (make-dvc-event :output-buffer output
  869. :error-buffer error
  870. :related-buffer related-buffer
  871. :command command
  872. :tree tree
  873. :event event
  874. :time (current-time)))
  875. buffer-read-only)
  876. (ewoc-enter-last dvc-log-cookie elem)
  877. ;; If an event is too old (30 minutes after it has been
  878. ;; recorded), throw it away.
  879. (ewoc-filter dvc-log-cookie 'dvc-log-recently-p 30)
  880. (ewoc-refresh dvc-log-cookie)
  881. elem))))
  882. (defun dvc-log-next ()
  883. "Move to the next log entry."
  884. (interactive)
  885. (let ((next (ewoc-next dvc-log-cookie
  886. (ewoc-locate dvc-log-cookie))))
  887. (when next (goto-char (ewoc-location next)))))
  888. (defun dvc-log-prev ()
  889. "Move to the previous log entry."
  890. (interactive)
  891. (let ((prev (ewoc-prev dvc-log-cookie
  892. (ewoc-locate dvc-log-cookie))))
  893. (when prev (goto-char (ewoc-location prev)))))
  894. ;;
  895. ;; Log buffer mode section
  896. ;;
  897. (defvar dvc-log-buffer-mode-map
  898. (let ((map (make-sparse-keymap)))
  899. (define-key map dvc-keyvec-help 'describe-mode)
  900. (define-key map [?o] 'dvc-switch-to-output-buffer)
  901. (define-key map "\C-m" 'dvc-switch-to-output-buffer)
  902. (define-key map [?e] 'dvc-switch-to-error-buffer)
  903. (define-key map [?r] 'dvc-switch-to-related-buffer)
  904. (define-key map [?n] 'dvc-log-next)
  905. (define-key map [?p] 'dvc-log-prev)
  906. (define-key map dvc-keyvec-quit 'dvc-buffer-quit)
  907. map)
  908. "Keymap used in DVC's log buffer.")
  909. (define-derived-mode dvc-log-buffer-mode fundamental-mode "DVC Log"
  910. "Major mode for DVC's internal log buffer. You can open this buffer
  911. with `dvc-open-internal-log-buffer'."
  912. (toggle-read-only 1))
  913. (defun dvc-open-internal-log-buffer ()
  914. "Switch to the DVC's internal log buffer.
  915. This buffer contains a list of all the DVC commands previously executed.
  916. The buffer uses the mode `dvc-log-buffer-mode'"
  917. (interactive)
  918. (let ((buffer-name (buffer-name)))
  919. (dvc-switch-to-buffer dvc-log-buffer)
  920. (goto-char (point-max))
  921. (when (re-search-backward (concat " Buffer: "
  922. (regexp-quote buffer-name)
  923. "$")
  924. nil t)
  925. (dvc-flash-line))))
  926. (defun dvc-clear-log-buffer ()
  927. "Kill the log buffer."
  928. (when (bufferp (get-buffer dvc-log-buffer))
  929. (kill-buffer dvc-log-buffer)))
  930. (defun dvc-get-process-output ()
  931. "Return the content of the last process buffer.
  932. Strips the final newline if there is one."
  933. (dvc-buffer-content dvc-last-process-buffer))
  934. (defun dvc-get-error-output ()
  935. "Return the content of the last error buffer.
  936. Strips the final newline if there is one."
  937. (dvc-buffer-content dvc-last-error-buffer))
  938. ;; TODO: per backend cound.
  939. (add-to-list 'minor-mode-alist
  940. '(dvc-process-running
  941. (:eval (if (equal (length dvc-process-running) 1)
  942. " DVC running"
  943. (concat " DVC running("
  944. (int-to-string (length dvc-process-running))
  945. ")")))))
  946. (defun dvc-log-edit-file-name ()
  947. "Return a suitable file name to edit the commit message"
  948. ;; FIXME: replace this with define-dvc-unified-command
  949. (dvc-call "dvc-log-edit-file-name-func"))
  950. (defun dvc-dvc-log-edit-file-name-func ()
  951. (concat (file-name-as-directory (dvc-tree-root))
  952. (dvc-variable (dvc-current-active-dvc)
  953. "log-edit-file-name")))
  954. ;;
  955. ;; Revision manipulation
  956. ;;
  957. ;; revision grammar is specified in ../docs/DVC-API
  958. ;; accessors
  959. (defun dvc-revision-get-dvc (revision-id)
  960. (car revision-id))
  961. (defun dvc-revision-get-type (revision-id)
  962. (car (nth 1 revision-id)))
  963. (defun dvc-revision-get-data (revision-id)
  964. (cdr (nth 1 revision-id)))
  965. (defun dvc-revision-to-string (revision-id &optional prev-format orig-str)
  966. "Return a string representation for REVISION-ID.
  967. If PREV-FORMAT is specified, it is the format string to use for
  968. entries that are before the given revision ID. The format string
  969. should take two parameters. The first is the revision ID, and
  970. the second is a number which indicates how many generations back
  971. to travel.
  972. If ORIG-STR is specified, it is the string that indicates the
  973. current revision of the working tree."
  974. (let* ((type (dvc-revision-get-type revision-id))
  975. (data (dvc-revision-get-data revision-id)))
  976. ;;(dvc-trace "dvc-revision-to-string: type: %s, data: %s, orig-str: %s" type data orig-str)
  977. (case type
  978. (revision (dvc-name-construct (nth 0 data)))
  979. (local-tree (car data))
  980. (last-revision (or orig-str "original"))
  981. (previous-revision
  982. (format (or prev-format "%s:-%s")
  983. (dvc-revision-to-string
  984. (list (dvc-revision-get-dvc revision-id) (nth 0 data)))
  985. (int-to-string (nth 1 data))))
  986. (t "UNKNOWN"))))
  987. (defun dvc-revision-get-buffer (file revision-id)
  988. "Return an empty buffer suitable for viewing FILE in REVISION-ID.
  989. The name of the buffer is chosen according to FILE and REVISION-ID.
  990. REVISION-ID may have the values described in docs/DVC-API."
  991. (let* ((type (dvc-revision-get-type revision-id))
  992. (name (concat
  993. (file-name-nondirectory file)
  994. "(" (dvc-revision-to-string revision-id) ")")))
  995. ;; replace / by | to work around uniquify
  996. (setq name (replace-regexp-in-string "\\/" "|" name))
  997. (let ((buffer (generate-new-buffer name)))
  998. (with-current-buffer buffer
  999. (let ((buffer-file-name file))
  1000. (set-auto-mode t)))
  1001. (dvc-buffers-tree-add (dvc-revision-get-dvc revision-id) type file buffer)
  1002. buffer)))
  1003. (defun dvc-revision-get-file-in-buffer (file revision-id)
  1004. "Return a buffer with the content of FILE at REVISION-ID.
  1005. REVISION-ID is as specified in docs/DVC-API."
  1006. (dvc-trace "dvc-revision-get-file-in-buffer. revision-id=%S" revision-id)
  1007. (let* ((type (dvc-revision-get-type revision-id))
  1008. (inhibit-read-only t)
  1009. ;; find-file-noselect will call dvc-current-active-dvc in a
  1010. ;; hook; specify dvc for dvc-call
  1011. (dvc-temp-current-active-dvc (dvc-revision-get-dvc revision-id))
  1012. (buffer (unless (eq type 'local-tree) (dvc-revision-get-buffer file revision-id))))
  1013. (case type
  1014. (local-tree (find-file-noselect file))
  1015. (revision
  1016. (with-current-buffer buffer
  1017. (dvc-call "revision-get-file-revision"
  1018. file (dvc-revision-get-data revision-id))
  1019. (set-buffer-modified-p nil)
  1020. (toggle-read-only 1)
  1021. buffer))
  1022. (previous-revision
  1023. (with-current-buffer buffer
  1024. (let* ((dvc (dvc-revision-get-dvc revision-id))
  1025. (data (nth 0 (dvc-revision-get-data revision-id)))
  1026. (rev-id (list dvc data)))
  1027. (dvc-call "revision-get-previous-revision" file rev-id))
  1028. (set-buffer-modified-p nil)
  1029. (toggle-read-only 1)
  1030. buffer))
  1031. (last-revision
  1032. (with-current-buffer buffer
  1033. (dvc-call "revision-get-last-revision"
  1034. file (dvc-revision-get-data revision-id))
  1035. (set-buffer-modified-p nil)
  1036. (toggle-read-only 1)
  1037. buffer))
  1038. (t (error "TODO: dvc-revision-get-file-in-buffer type %S" type)))))
  1039. (defun dvc-dvc-revision-nth-ancestor (revision n)
  1040. "Default function to get the n-th ancestor of REVISION."
  1041. (let ((count n)
  1042. (res revision))
  1043. (while (> count 0)
  1044. (setq res (dvc-revision-direct-ancestor res)
  1045. count (- count 1)))
  1046. res))
  1047. ;;
  1048. ;; DVC command version
  1049. ;;
  1050. (defun dvc-dvc-command-version ()
  1051. "Fallback for `dvc-command-vesion'. Returns just `nil'.
  1052. This function is called only if the current backend doesn't
  1053. implement `command-version' function."
  1054. nil)
  1055. (provide 'dvc-core)
  1056. ;;; dvc-core.el ends here