PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/dvc/lisp/xhg.el

https://bitbucket.org/kjhealy/emacs-starter-kit
Lisp | 1036 lines | 848 code | 95 blank | 93 comment | 23 complexity | 19a9f38dfa4d848be00df5ad95f3fcee MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, GPL-2.0
  1. ;;; xhg.el --- Mercurial interface for dvc
  2. ;; Copyright (C) 2005-2008 by all contributors
  3. ;; Author: Stefan Reichoer, <stefan@xsteve.at>
  4. ;; This file is free software; you can redistribute it and/or modify
  5. ;; it under the terms of the GNU General Public License as published by
  6. ;; the Free Software Foundation; either version 2, or (at your option)
  7. ;; any later version.
  8. ;; This file is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ;; GNU General Public License for more details.
  12. ;; You should have received a copy of the GNU General Public License
  13. ;; along with GNU Emacs; see the file COPYING. If not, write to
  14. ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. ;; Boston, MA 02110-1301, USA.
  16. ;;; Commentary:
  17. ;; The mercurial interface for dvc
  18. ;;; History:
  19. ;;
  20. ;;; Code:
  21. (require 'dvc-core)
  22. (require 'dvc-diff)
  23. (require 'xhg-core)
  24. (require 'xhg-log)
  25. (require 'xhg-mq)
  26. (defvar xhg-export-git-style-patches t "Run hg export --git.")
  27. ;;;###autoload
  28. (defun xhg-init (&optional dir)
  29. "Run hg init."
  30. (interactive
  31. (list (expand-file-name (dvc-read-directory-name "Directory for hg init: "
  32. (or default-directory
  33. (getenv "HOME"))))))
  34. (dvc-run-dvc-sync 'xhg (list "init" dir)
  35. :finished (dvc-capturing-lambda
  36. (output error status arguments)
  37. (message "hg init %s finished" dir))))
  38. ;;;###autoload
  39. (defun xhg-dvc-add-files (&rest files)
  40. "Run hg add."
  41. (dvc-trace "xhg-add-files: %s" files)
  42. (let ((default-directory (xhg-tree-root)))
  43. (dvc-run-dvc-sync 'xhg (append '("add") (mapcar #'file-relative-name files))
  44. :finished (dvc-capturing-lambda
  45. (output error status arguments)
  46. (message "hg add finished")))))
  47. ;;;###autoload
  48. (defun xhg-dvc-revert-files (&rest files)
  49. "Run hg revert."
  50. (dvc-trace "xhg-revert-files: %s" files)
  51. (let ((default-directory (xhg-tree-root)))
  52. (dvc-run-dvc-sync 'xhg (append '("revert") (mapcar #'file-relative-name files))
  53. :finished (dvc-capturing-lambda
  54. (output error status arguments)
  55. (message "hg revert finished")))))
  56. (defun xhg-dry-tip ()
  57. "Extract only the revision number of tip"
  58. (let ((revision (with-temp-buffer
  59. (apply #'call-process "hg" nil t nil
  60. '("tip" "--template" "#rev#"))
  61. (buffer-string))))
  62. revision))
  63. ;;;###autoload
  64. (defun xhg-rollback (&optional revert)
  65. "Run hg rollback.
  66. if prefix-arg (C-u) run hg revert"
  67. (interactive "P")
  68. (let ((act-rev (xhg-dry-tip))
  69. (new-rev))
  70. (if (yes-or-no-p (format "Really rollback rev %s?" act-rev))
  71. (progn
  72. (dvc-run-dvc-sync 'xhg (list "rollback")
  73. :finished
  74. (lambda (output error status arguments)
  75. (setq new-rev (xhg-dry-tip))
  76. (message
  77. (when (equal act-rev new-rev)
  78. "no rollback information available"))))
  79. (if (and current-prefix-arg
  80. (not (equal act-rev new-rev)))
  81. (progn
  82. (dvc-run-dvc-sync 'xhg (list "revert" "--all")
  83. :finished
  84. (lambda (output error status arguments)
  85. (message "hg revert finished, now at rev %s" new-rev))))
  86. (when (not (equal act-rev new-rev))
  87. (message
  88. "hg rollback finished, tip is now at %s don't forget to revert" new-rev))))
  89. (message "hg rollback aborted"))))
  90. ;;;###autoload
  91. (defun xhg-dvc-remove-files (&rest files)
  92. "Run hg remove."
  93. (dvc-trace "xhg-remove-files: %s" files)
  94. (let ((default-directory (xhg-tree-root)))
  95. (dvc-run-dvc-sync 'xhg (append '("remove") (mapcar #'file-relative-name files))
  96. :finished (dvc-capturing-lambda
  97. (output error status arguments)
  98. (message "hg remove finished")))))
  99. ;;;###autoload
  100. (defun xhg-addremove ()
  101. "Run hg addremove."
  102. (interactive)
  103. (dvc-run-dvc-sync 'xhg '("addremove")
  104. :finished (dvc-capturing-lambda
  105. (output error status arguments)
  106. (message "hg addremove finished"))))
  107. ;;;###autoload
  108. (defun xhg-dvc-rename (from to &optional after force)
  109. "Run hg rename."
  110. (interactive
  111. (let* ((from-name (dvc-confirm-read-file-name "xhg rename: "))
  112. (to-name (dvc-confirm-read-file-name (concat "xhg rename '" from-name "' to: ") nil "" from-name)))
  113. (list from-name to-name nil nil)))
  114. (dvc-run-dvc-sync 'xhg (list "rename" (dvc-uniquify-file-name from) (dvc-uniquify-file-name to)
  115. (when after "--after") (when force "--force"))
  116. :finished (dvc-capturing-lambda
  117. (output error status arguments)
  118. (message "hg rename finished"))))
  119. ;;;###autoload
  120. (defun xhg-forget (&rest files)
  121. "Run hg forget."
  122. (interactive (dvc-current-file-list))
  123. (let ((multiprompt (format "Forget %%d files for hg? "))
  124. (singleprompt (format "Forget file for hg: ")))
  125. (when (dvc-confirm-read-file-name-list multiprompt files singleprompt t)
  126. (dvc-run-dvc-sync 'xhg (append '("forget") files)
  127. :finished (dvc-capturing-lambda
  128. (output error status arguments)
  129. (message "hg forget finished"))))))
  130. ;;;###autoload
  131. (defun xhg-add-all-files (arg)
  132. "Run 'hg add' to add all files to mercurial.
  133. Normally run 'hg add -n' to simulate the operation to see which files will be added.
  134. Only when called with a prefix argument, add the files."
  135. (interactive "P")
  136. (dvc-run-dvc-sync 'xhg (list "add" (unless arg "-n"))))
  137. ;;;###autoload
  138. (defun xhg-log (&optional r1 r2 show-patch file)
  139. "Run hg log.
  140. When run interactively, the prefix argument decides, which parameters are queried from the user.
  141. C-u : Show patches also, use all revisions
  142. C-u C-u : Show patches also, ask for revisions
  143. positive : Don't show patches, ask for revisions.
  144. negative : Don't show patches, limit to n revisions."
  145. (interactive "P")
  146. (when (interactive-p)
  147. (cond ((equal current-prefix-arg '(4))
  148. (setq show-patch t)
  149. (setq r1 nil))
  150. ((equal current-prefix-arg '(16))
  151. (setq show-patch t)
  152. (setq r1 1)))
  153. (when (and (numberp r1) (> r1 0))
  154. (setq r1 (read-string "hg log, R1:"))
  155. (setq r2 (read-string "hg log, R2:"))))
  156. (let ((buffer (dvc-get-buffer-create 'xhg 'log))
  157. (command-list '("log"))
  158. (cur-dir default-directory))
  159. (when r1
  160. (when (numberp r1)
  161. (setq r1 (number-to-string r1))))
  162. (when r2
  163. (when (numberp r2)
  164. (setq r2 (number-to-string r2))))
  165. (if (and (> (length r2) 0) (> (length r1) 0))
  166. (setq command-list (append command-list (list "-r" (concat r2 ":" r1))))
  167. (when (> (length r1) 0)
  168. (let ((r1-num (string-to-number r1)))
  169. (if (> r1-num 0)
  170. (setq command-list (append command-list (list "-r" r1)))
  171. (setq command-list
  172. (append command-list
  173. (list "-l" (number-to-string (abs r1-num)))))))))
  174. (when show-patch
  175. (setq command-list (append command-list (list "-p"))))
  176. (dvc-switch-to-buffer-maybe buffer)
  177. (let ((inhibit-read-only t))
  178. (erase-buffer))
  179. (xhg-log-mode)
  180. ;;(dvc-trace "xhg-log command-list: %S, default-directory: %s" command-list cur-dir)
  181. (let ((default-directory cur-dir))
  182. (dvc-run-dvc-sync 'xhg command-list
  183. :finished
  184. (dvc-capturing-lambda (output error status arguments)
  185. (progn
  186. (with-current-buffer (capture buffer)
  187. (let ((inhibit-read-only t))
  188. (erase-buffer)
  189. (insert-buffer-substring output)
  190. (goto-char (point-min))
  191. (insert (format "hg log for %s\n\n" default-directory))
  192. (toggle-read-only 1)))))))))
  193. (defun xhg-parse-diff (changes-buffer)
  194. (save-excursion
  195. (while (re-search-forward
  196. "^diff -r [^ ]+ \\(.*\\)$" nil t)
  197. (let* ((name (match-string-no-properties 1))
  198. (added (progn (forward-line 1)
  199. (looking-at "^--- /dev/null")))
  200. (removed (progn (forward-line 1)
  201. (looking-at "^\\+\\+\\+ /dev/null"))))
  202. (with-current-buffer changes-buffer
  203. (ewoc-enter-last
  204. dvc-fileinfo-ewoc
  205. (make-dvc-fileinfo-legacy
  206. :data (list 'file
  207. name
  208. (cond (added "A")
  209. (removed "D")
  210. (t " "))
  211. (cond ((or added removed) " ")
  212. (t "M"))
  213. " " ; dir. Nothing is a directory in hg.
  214. nil))))))))
  215. (defun xhg-parse-status (changes-buffer)
  216. (let ((status-list (split-string (dvc-buffer-content (current-buffer)) "\n")))
  217. (let ((inhibit-read-only t)
  218. (modif)
  219. (modif-char))
  220. (erase-buffer)
  221. (setq dvc-header (format "hg status for %s\n" default-directory))
  222. (dolist (elem status-list)
  223. (unless (string= "" elem)
  224. (setq modif-char (substring elem 0 1))
  225. (with-current-buffer changes-buffer
  226. (ewoc-enter-last
  227. dvc-fileinfo-ewoc
  228. (make-dvc-fileinfo-legacy
  229. :data (list 'file (substring elem 2) modif-char)))))))))
  230. (defun xhg-diff-1 (modified path dont-switch base-rev)
  231. "Run hg diff.
  232. If DONT-SWITCH, don't switch to the diff buffer"
  233. (interactive (list nil nil current-prefix-arg))
  234. (let* ((window-conf (current-window-configuration))
  235. (cur-dir (or path default-directory))
  236. (orig-buffer (current-buffer))
  237. (root (xhg-tree-root cur-dir))
  238. (buffer (dvc-prepare-changes-buffer
  239. `(xhg (last-revision ,root 1))
  240. `(xhg (local-tree ,root))
  241. 'diff root 'xhg))
  242. (command-list '("diff")))
  243. (dvc-switch-to-buffer-maybe buffer)
  244. (dvc-buffer-push-previous-window-config window-conf)
  245. (when dont-switch (pop-to-buffer orig-buffer))
  246. (dvc-save-some-buffers root)
  247. (when base-rev
  248. (setq command-list (append command-list (list "-r" base-rev)))
  249. (when modified
  250. (setq command-list (append command-list (list "-r" modified)))))
  251. (dvc-run-dvc-sync 'xhg command-list
  252. :finished
  253. (dvc-capturing-lambda (output error status arguments)
  254. (dvc-show-changes-buffer output 'xhg-parse-diff
  255. (capture buffer))))))
  256. ;;;###autoload
  257. (defun xhg-dvc-diff (&optional base-rev path dont-switch)
  258. "Run hg diff.
  259. If DONT-SWITCH, don't switch to the diff buffer"
  260. (interactive (list nil nil current-prefix-arg))
  261. (xhg-diff-1 nil path dont-switch
  262. (dvc-revision-to-string base-rev nil "tip")))
  263. (defun xhg-delta (base-rev modified &optional path dont-switch)
  264. ;; TODO: dvc-revision-to-string doesn't work for me.
  265. (interactive (list nil nil nil current-prefix-arg))
  266. (xhg-diff-1 (dvc-revision-to-string modified) path dont-switch
  267. (dvc-revision-to-string base-rev)))
  268. (defun xhg-dvc-status ()
  269. "Run hg status."
  270. (let* ((window-conf (current-window-configuration))
  271. (root (xhg-tree-root))
  272. (buffer (dvc-prepare-changes-buffer
  273. `(xhg (last-revision ,root 1))
  274. `(xhg (local-tree ,root))
  275. 'status root 'xhg)))
  276. (dvc-switch-to-buffer-maybe buffer)
  277. (dvc-buffer-push-previous-window-config window-conf)
  278. (dvc-save-some-buffers root)
  279. (dvc-run-dvc-sync 'xhg '("status")
  280. :finished
  281. (dvc-capturing-lambda (output error status arguments)
  282. (with-current-buffer (capture buffer)
  283. (xhg-status-extra-mode-setup)
  284. (if (> (point-max) (point-min))
  285. (dvc-show-changes-buffer output 'xhg-parse-status
  286. (capture buffer))
  287. (dvc-diff-no-changes (capture buffer)
  288. "No changes in %s"
  289. (capture root))))))))
  290. (easy-menu-define xhg-mode-menu dvc-diff-mode-map
  291. "`xhg' menu"
  292. `("hg"
  293. ,xhg-mq-submenu
  294. ["Edit project hgrc file" xhg-hgrc-edit-repository-hgrc t]
  295. ["Edit global ~/.hgrc file" xhg-hgrc-edit-global-hgrc t]
  296. ))
  297. (defun xhg-status-extra-mode-setup ()
  298. "Do some additonal setup for xhg status buffers."
  299. (dvc-trace "xhg-status-extra-mode-setup called.")
  300. (easy-menu-add xhg-mode-menu)
  301. (when (boundp 'xhg-mq-sub-mode-map)
  302. (local-set-key [?Q] xhg-mq-sub-mode-map))
  303. (setq dvc-buffer-refresh-function 'xhg-dvc-status))
  304. (defun xhg-pull-finish-function (output error status arguments)
  305. (let ((buffer (dvc-get-buffer-create 'xhg 'pull)))
  306. (with-current-buffer buffer
  307. (let ((inhibit-read-only t))
  308. (erase-buffer)
  309. (insert-buffer-substring output)
  310. (toggle-read-only 1)))
  311. (let ((dvc-switch-to-buffer-mode 'show-in-other-window))
  312. (dvc-switch-to-buffer buffer))))
  313. ;;;###autoload
  314. (defun xhg-pull (src &optional update-after-pull)
  315. "Run hg pull."
  316. (interactive (list (let* ((completions (xhg-paths 'both))
  317. (initial-input (car (member "default" completions))))
  318. (dvc-completing-read
  319. "Pull from hg repository: "
  320. completions nil nil initial-input))))
  321. (dvc-run-dvc-async 'xhg (list "pull" (when update-after-pull "--update") src)
  322. :error 'xhg-pull-finish-function
  323. :finished 'xhg-pull-finish-function))
  324. (defun xhg-push-finish-function (output error status arguments)
  325. (let ((buffer (dvc-get-buffer-create 'xhg 'push)))
  326. (with-current-buffer buffer
  327. (let ((inhibit-read-only t))
  328. (erase-buffer)
  329. (insert-buffer-substring output)
  330. (toggle-read-only 1)))
  331. (let ((dvc-switch-to-buffer-mode 'show-in-other-window))
  332. (dvc-switch-to-buffer buffer))))
  333. ;;;###autoload
  334. (defun xhg-push (src)
  335. "Run hg push."
  336. (interactive (list (let* ((completions (xhg-paths 'both))
  337. (initial-input (car (member "default" completions))))
  338. (dvc-completing-read
  339. "Push to hg repository: "
  340. completions nil nil initial-input))))
  341. (dvc-run-dvc-async 'xhg (list "push" src)
  342. :error 'xhg-push-finish-function
  343. :finished 'xhg-push-finish-function))
  344. ;;;###autoload
  345. (defun xhg-clone (src &optional dest rev noupdate pull)
  346. "Run hg clone."
  347. (interactive (list (read-string "hg clone from: ")
  348. (read-string "hg clone to: ")
  349. (if current-prefix-arg
  350. (read-string "hg revision: ") ;; rev
  351. nil)
  352. nil ;; noupdate
  353. nil ;; pull
  354. ))
  355. (if rev
  356. (dvc-run-dvc-async 'xhg (list "clone" "--rev" rev src dest))
  357. (dvc-run-dvc-async 'xhg (list "clone" src dest))))
  358. ;;;###autoload
  359. (defun xhg-incoming (&optional src show-patch no-merges)
  360. "Run hg incoming."
  361. (interactive (list (let* ((completions (xhg-paths 'both))
  362. (initial-input (car (member "default" completions))))
  363. (dvc-completing-read
  364. "Show incoming from hg repository: "
  365. completions nil nil initial-input))
  366. nil ;; show-patch
  367. nil ;; no-merges
  368. ))
  369. (let ((window-conf (current-window-configuration))
  370. (buffer (dvc-get-buffer-create 'xhg 'log)))
  371. (dvc-switch-to-buffer-maybe buffer t)
  372. (let ((inhibit-read-only t))
  373. (erase-buffer))
  374. (xhg-log-mode)
  375. (dvc-run-dvc-async 'xhg (list "incoming" (when show-patch "--patch") (when no-merges "--no-merges") src)
  376. :finished
  377. (dvc-capturing-lambda (output error status arguments)
  378. (progn
  379. (with-current-buffer (capture buffer)
  380. (let ((inhibit-read-only t))
  381. (erase-buffer)
  382. (insert-buffer-substring output)
  383. (goto-char (point-min))
  384. (insert (format "hg incoming for %s\n\n" default-directory))
  385. (toggle-read-only 1)
  386. (xhg-log-next 1)))))
  387. :error
  388. (dvc-capturing-lambda (output error status arguments)
  389. (with-current-buffer output
  390. (goto-char (point-max))
  391. (forward-line -1)
  392. (if (looking-at "no changes found")
  393. (progn
  394. (message "No changes found")
  395. (set-window-configuration (capture window-conf)))
  396. (dvc-default-error-function output error status arguments)))))))
  397. ;;;###autoload
  398. (defun xhg-outgoing (&optional src show-patch no-merges)
  399. "Run hg outgoing."
  400. (interactive (list (let* ((completions (xhg-paths 'both))
  401. (initial-input (car (member "default" completions))))
  402. (dvc-completing-read
  403. "Show outgoing to hg repository: "
  404. completions nil nil initial-input))
  405. nil ;; show-patch
  406. nil ;; no-merges
  407. ))
  408. (let ((window-conf (current-window-configuration))
  409. (buffer (dvc-get-buffer-create 'xhg 'log)))
  410. (dvc-switch-to-buffer-maybe buffer t)
  411. (let ((inhibit-read-only t))
  412. (erase-buffer))
  413. (xhg-log-mode)
  414. (dvc-run-dvc-async 'xhg (list "outgoing" (when show-patch "--patch") (when no-merges "--no-merges") src)
  415. :finished
  416. (dvc-capturing-lambda (output error status arguments)
  417. (progn
  418. (with-current-buffer (capture buffer)
  419. (let ((inhibit-read-only t))
  420. (erase-buffer)
  421. (insert-buffer-substring output)
  422. (goto-char (point-min))
  423. (insert (format "hg outgoing for %s\n\n" default-directory))
  424. (toggle-read-only 1)))))
  425. :error
  426. (dvc-capturing-lambda (output error status arguments)
  427. (with-current-buffer output
  428. (goto-char (point-max))
  429. (forward-line -1)
  430. (if (looking-at "no changes found")
  431. (progn
  432. (message "No changes found")
  433. (set-window-configuration (capture window-conf)))
  434. (dvc-default-error-function output error status arguments)))))))
  435. (defun xhg-get-all-heads-list ()
  436. "Get a list of all heads available from the output of hg heads."
  437. (let ((rev-list (with-temp-buffer
  438. (apply #'call-process "hg" nil t nil
  439. '("heads"
  440. "--template"
  441. "#rev#\n"))
  442. (buffer-string))))
  443. (setq rev-list (cons "auto"
  444. (remove "" (split-string rev-list "\n"))))
  445. rev-list))
  446. (defun xhg-changep ()
  447. (let ((change (with-temp-buffer
  448. (apply #'call-process "hg" nil t nil
  449. '("diff"))
  450. (buffer-string))))
  451. (setq change (remove "" (split-string change "\n")))
  452. (if change
  453. t
  454. nil)))
  455. ;;;###autoload
  456. (defun xhg-merge (&optional xhg-use-imerge)
  457. "Run hg merge. called with prefix argument (C-u)
  458. use extension hg imerge.
  459. Be sure to enable it in .hgrc:
  460. ,----
  461. | [extensions]
  462. | imerge =
  463. `----
  464. To merge from specific revision, choose it in completion.
  465. If `auto' is choose use default revision (last)"
  466. (interactive "P")
  467. (let* ((xhg-use-imerge (if current-prefix-arg
  468. t
  469. nil))
  470. (haschange (xhg-changep))
  471. (collection (xhg-get-all-heads-list))
  472. (revision (dvc-completing-read "Merge from hg revision: "
  473. collection nil t))
  474. (arg)
  475. (command (if xhg-use-imerge
  476. 'dvc-run-dvc-sync
  477. 'dvc-run-dvc-async)))
  478. (when (or (string= revision "")
  479. (string= revision "auto"))
  480. (setq revision nil))
  481. (setq arg (if xhg-use-imerge
  482. (if revision
  483. '("imerge" "--rev")
  484. '("imerge"))
  485. (if revision
  486. '("merge" "--rev")
  487. '("merge"))))
  488. (if (and (not haschange)
  489. (> (length collection) 2))
  490. (funcall command 'xhg `(,@arg ,revision)
  491. :finished
  492. (dvc-capturing-lambda (output error status arguments)
  493. (message "hg %s %s %s finished => %s"
  494. (nth 0 arg)
  495. (if revision
  496. (nth 1 arg)
  497. "")
  498. (if revision
  499. revision
  500. "")
  501. (concat (dvc-buffer-content error)
  502. (dvc-buffer-content output))))
  503. :error
  504. ;; avoid dvc-error buffer to appear in ediff
  505. (lambda (output error status arguments)
  506. nil))
  507. (when haschange
  508. (error "abort: outstanding uncommitted merges, Please commit before merging"))
  509. (when (<= (length collection) 2)
  510. (error "There is nothing to merge here")))))
  511. (defun xhg-command-version ()
  512. "Run hg version."
  513. (interactive)
  514. (let ((version (dvc-run-dvc-sync 'xhg '("version")
  515. :finished 'dvc-output-buffer-handler)))
  516. (when (interactive-p)
  517. (message "Mercurial version: %s" version))
  518. version))
  519. ;;;###autoload
  520. (defun xhg-branch (&optional new-name)
  521. "Run hg branch.
  522. When called with a prefix argument, ask for the new branch-name, otherwise
  523. display the current one."
  524. (interactive "P")
  525. (let ((branch (dvc-run-dvc-sync 'xhg (list "branch")
  526. :finished 'dvc-output-buffer-handler)))
  527. (if (not new-name)
  528. (progn
  529. (when (interactive-p)
  530. (message "xhg branch: %s" branch))
  531. branch)
  532. (when (interactive-p)
  533. (setq new-name (read-string (format "Change branch from '%s' to: " branch) nil nil branch)))
  534. (dvc-run-dvc-sync 'xhg (list "branch" new-name)))))
  535. ;;todo: add support to specify a rev
  536. (defun xhg-manifest ()
  537. "Run hg manifest."
  538. (interactive)
  539. (let ((buffer (dvc-get-buffer-create 'xhg 'manifest)))
  540. (dvc-run-dvc-sync 'xhg '("manifest")
  541. :finished
  542. (dvc-capturing-lambda (output error status arguments)
  543. (progn
  544. (with-current-buffer (capture buffer)
  545. (let ((inhibit-read-only t))
  546. (erase-buffer)
  547. (insert-buffer-substring output)
  548. (toggle-read-only 1)))
  549. (dvc-switch-to-buffer (capture buffer)))))))
  550. ;;;###autoload
  551. (defun xhg-tip ()
  552. "Run hg tip."
  553. (interactive)
  554. (dvc-run-dvc-display-as-info 'xhg '("tip")))
  555. ;;;###autoload
  556. (defun xhg-heads ()
  557. "Run hg heads."
  558. (interactive)
  559. (dvc-run-dvc-display-as-info 'xhg '("heads")))
  560. ;;;###autoload
  561. (defun xhg-parents ()
  562. "Run hg parents."
  563. (interactive)
  564. (dvc-run-dvc-display-as-info 'xhg '("parents")))
  565. ;;;###autoload
  566. (defun xhg-identify ()
  567. "Run hg identify."
  568. (interactive)
  569. (let ((id))
  570. (dvc-run-dvc-sync 'xhg '("identify")
  571. :finished
  572. (lambda (output error status arguments)
  573. (set-buffer output)
  574. (goto-char (point-min))
  575. (setq id
  576. (buffer-substring-no-properties
  577. (point)
  578. (line-end-position))))
  579. :error
  580. (lambda (output error status arguments)
  581. (setq id "<unknown>")))
  582. (when (interactive-p)
  583. (message "hg identity for %s: %s" default-directory id))
  584. id))
  585. ;;;###autoload
  586. (defun xhg-verify ()
  587. "Run hg verify."
  588. (interactive)
  589. (dvc-run-dvc-display-as-info 'xhg '("verify")))
  590. ;;;###autoload
  591. (defun xhg-showconfig ()
  592. "Run hg showconfig."
  593. (interactive)
  594. (dvc-run-dvc-display-as-info 'xhg '("showconfig")))
  595. ;;;###autoload
  596. (defun xhg-paths (&optional type)
  597. "Run hg paths.
  598. When called interactive, display them in an *xhg-info* buffer.
  599. Otherwise the return value depends on TYPE:
  600. 'alias: Return only alias names
  601. 'path: Return only the paths
  602. 'both Return the aliases and the paths in a flat list
  603. otherwise: Return a list of two element sublists containing alias, path"
  604. (interactive)
  605. (if (interactive-p)
  606. (dvc-run-dvc-display-as-info 'xhg '("paths"))
  607. (let* ((path-list (dvc-run-dvc-sync 'xhg (list "paths")
  608. :finished 'dvc-output-buffer-split-handler))
  609. (lisp-path-list (mapcar '(lambda(arg) (dvc-split-string arg " = " arg)) path-list))
  610. (result-list))
  611. (cond ((eq type 'alias)
  612. (setq result-list (mapcar 'car lisp-path-list)))
  613. ((eq type 'path)
  614. (setq result-list (mapcar 'cadr lisp-path-list)))
  615. ((eq type 'both)
  616. (setq result-list (append (mapcar 'car lisp-path-list) (mapcar 'cadr lisp-path-list))))
  617. (t
  618. (setq result-list lisp-path-list))))))
  619. ;;;###autoload
  620. (defun xhg-tags ()
  621. "Run hg tags."
  622. (interactive)
  623. (dvc-run-dvc-display-as-info 'xhg '("tags")))
  624. ;; hg annotate: add support to edit the parameters
  625. ;; -r --rev revision
  626. ;; -a --text treat all files as text
  627. ;; -u --user show user
  628. ;; -n --number show revision number
  629. ;; -c --changeset show changeset
  630. ;;;###autoload
  631. (defun xhg-annotate ()
  632. "Run hg annotate."
  633. (interactive)
  634. (dvc-run-dvc-display-as-info 'xhg (append '("annotate") (dvc-current-file-list))))
  635. ;;;###autoload
  636. (defun xhg-view ()
  637. "Run hg view."
  638. (interactive)
  639. (dvc-run-dvc-async 'xhg '("view")))
  640. ;;;###autoload
  641. (defun xhg-export (rev fname)
  642. "Run hg export.
  643. `xhg-export-git-style-patches' determines, if git style patches are created."
  644. (interactive (list (xhg-read-revision "Export revision: ")
  645. (read-file-name "Export hg revision to: ")))
  646. (dvc-run-dvc-sync 'xhg (list "export" (when xhg-export-git-style-patches "--git") "-o" (expand-file-name fname) rev)
  647. :finished
  648. (lambda (output error status arguments)
  649. (message "Exported revision %s to %s." rev fname))))
  650. ;;;###autoload
  651. (defun xhg-import (patch-file-name &optional force)
  652. "Run hg import."
  653. (interactive (list (read-file-name "Import hg patch: " nil nil t (when (eq major-mode 'dired-mode)
  654. (file-name-nondirectory (dired-get-filename))))))
  655. (dvc-run-dvc-sync 'xhg (delete nil (list "import" (when force "--force") (expand-file-name patch-file-name)))
  656. :finished
  657. (lambda (output error status arguments)
  658. (message "Imported hg patch from %s." patch-file-name))))
  659. ;;;###autoload
  660. (defun xhg-undo ()
  661. "Run hg undo."
  662. (interactive)
  663. (let ((undo-possible (file-exists-p (concat (xhg-tree-root) ".hg/undo"))))
  664. (if undo-possible
  665. (save-window-excursion
  666. (xhg-log "-1" nil t)
  667. (if (yes-or-no-p "Undo this transaction? ")
  668. (progn
  669. (dvc-run-dvc-sync 'xhg (list "undo")
  670. :finished
  671. (lambda (output error status arguments)
  672. (message "Finished xhg undo."))))
  673. (message "xhg undo aborted.")))
  674. (message "xhg: No undo information available."))))
  675. ;;;###autoload
  676. (defun xhg-update (&optional clean)
  677. "Run hg update.
  678. When called with prefix-arg run hg update -C (clean)"
  679. (interactive "P")
  680. (let* ((opt-list (if current-prefix-arg
  681. (list "update" "-C")
  682. (list "update")))
  683. (opt-string (mapconcat 'identity opt-list " ")))
  684. (dvc-run-dvc-sync 'xhg opt-list
  685. :finished
  686. (lambda (output error status arguments)
  687. (dvc-default-finish-function output error status arguments)
  688. (message "hg %s complete for %s" opt-string default-directory)))))
  689. (defun xhg-convert (source target)
  690. "Convert a foreign SCM repository to a Mercurial one.
  691. Accepted source formats:
  692. - Mercurial
  693. - CVS
  694. - Darcs
  695. - git
  696. - Subversion
  697. - Monotone
  698. - GNU Arch
  699. Be sure to add to your hgrc:
  700. \[extensions\]
  701. hgext.convert =
  702. Read also: hg help convert
  703. "
  704. (interactive "DSource: \nsTarget: ")
  705. (message "Started hg conversion of [%s] to [%s] ..." source target)
  706. (dvc-run-dvc-async 'xhg (list "convert"
  707. (expand-file-name source)
  708. (expand-file-name target))
  709. :finished (dvc-capturing-lambda (output error status arguments)
  710. (let ((default-directory (capture target))
  711. (xhg-update)))
  712. (message "hg: [%s] successfully converted to [%s]" (capture source) (capture target)))))
  713. ;; --------------------------------------------------------------------------------
  714. ;; hg serve functionality
  715. ;; --------------------------------------------------------------------------------
  716. (defvar xhg-serve-parameter-list (make-hash-table :test 'equal)
  717. "A hash table that holds the mapping from work directory roots to
  718. extra parameters used for hg serve.
  719. The extra parameters are given as alist. The following example shows the supported settings:
  720. '((port 8235) (name \"my-project\"))")
  721. ;;;###autoload
  722. (defun xhg-serve-register-serve-parameter-list (working-copy-root parameter-list &optional start-server)
  723. "Register a mapping from a work directory root to a parameter list for hg serve.
  724. When START-SERVER is given, start the server immediately.
  725. Example usage:
  726. (xhg-serve-register-serve-parameter-list \"~/proj/simple-counter-1/\" '((port 8100) (name \"simple-counter\")))"
  727. (puthash (dvc-uniquify-file-name working-copy-root) parameter-list xhg-serve-parameter-list)
  728. (when start-server
  729. (let ((default-directory (dvc-uniquify-file-name working-copy-root)))
  730. (xhg-serve))))
  731. (defun xhg-serve ()
  732. "Run hg serve --daemon.
  733. See `xhg-serve-register-serve-parameter-list' to register specific parameters for the server process."
  734. (interactive)
  735. (let* ((tree-root (dvc-tree-root))
  736. (server-status-dir (concat tree-root ".xhg-serve/"))
  737. (parameter-alist (gethash (dvc-uniquify-file-name tree-root) xhg-serve-parameter-list))
  738. (port (or (cadr (assoc 'port parameter-alist)) 8000))
  739. (name (cadr (assoc 'name parameter-alist)))
  740. (errorlog (concat server-status-dir "error.log"))
  741. (accesslog (concat server-status-dir "access.log"))
  742. (pid-file (concat server-status-dir "server.pid")))
  743. (when (numberp port)
  744. (setq port (number-to-string port)))
  745. (unless (file-directory-p server-status-dir)
  746. (make-directory server-status-dir))
  747. (dvc-run-dvc-sync 'xhg (list "serve" "--daemon" (when port "--port") port (when name "--name") name
  748. "--pid-file" pid-file "--accesslog" accesslog "--errorlog" errorlog)
  749. :finished (dvc-capturing-lambda (output error status arguments)
  750. (message "hg server started for %s, using port %s" tree-root port)))))
  751. (defun xhg-serve-kill ()
  752. "Kill a hg serve process started with `xhg-serve'."
  753. (interactive)
  754. (let* ((tree-root (dvc-tree-root))
  755. (server-status-dir (concat tree-root ".xhg-serve/"))
  756. (pid-file (concat server-status-dir "server.pid"))
  757. (pid)
  758. (kill-status))
  759. (if (file-readable-p pid-file)
  760. (with-current-buffer
  761. (find-file-noselect pid-file)
  762. (setq pid (buffer-substring-no-properties (point-min) (- (point-max) 1)))
  763. (kill-buffer (current-buffer)))
  764. (message "no hg serve pid file found - aborting"))
  765. (when pid
  766. (setq kill-status (call-process "kill" nil nil nil pid))
  767. (if (eq kill-status 0)
  768. (progn
  769. (delete-file pid-file)
  770. (message "hg serve process killed."))
  771. (message "kill hg serve process failed, return status: %d" kill-status)))))
  772. ;; --------------------------------------------------------------------------------
  773. ;; dvc revision support
  774. ;; --------------------------------------------------------------------------------
  775. ;;;###autoload
  776. (defun xhg-revision-get-last-revision (file last-revision)
  777. "Insert the content of FILE in LAST-REVISION, in current buffer.
  778. LAST-REVISION looks like
  779. \(\"path\" NUM)"
  780. (dvc-trace "xhg-revision-get-last-revision file:%S last-revision:%S" file last-revision)
  781. (let ((xhg-rev (int-to-string (nth 1 last-revision)))
  782. (default-directory (car last-revision)))
  783. ;; TODO: support the last-revision parameter??
  784. (insert (dvc-run-dvc-sync
  785. 'xhg (list "cat" file)
  786. :finished 'dvc-output-buffer-handler-withnewline))))
  787. ;;;###autoload
  788. (defun xhg-revision-get-last-or-num-revision (infile outfile &optional revision)
  789. "Run the command:
  790. hg cat --rev <num revision> -o outputfile inputfile"
  791. (interactive
  792. (let* ((xhg-infile (read-file-name "InputFile: "))
  793. (xhg-outfile (read-file-name "OutputFile: "))
  794. (xhg-rev (if current-prefix-arg
  795. (read-string "Revision: ")
  796. "tip")))
  797. (setq xhg-infile (expand-file-name xhg-infile)
  798. xhg-outfile (concat (expand-file-name xhg-outfile)
  799. "."
  800. xhg-rev))
  801. (list xhg-infile xhg-outfile xhg-rev)))
  802. (dvc-run-dvc-sync 'xhg (list "cat"
  803. "--rev"
  804. revision
  805. "-o"
  806. outfile
  807. infile)
  808. :finished 'dvc-output-buffer-handler-withnewline)
  809. (message "%s extracted in %s at revision %s"
  810. (file-name-nondirectory infile)
  811. (file-relative-name outfile)
  812. revision))
  813. ;; --------------------------------------------------------------------------------
  814. ;; higher level commands
  815. ;; --------------------------------------------------------------------------------
  816. (defvar xhg-submit-patch-mapping nil)
  817. ;;(add-to-list 'xhg-submit-patch-mapping '("~/data/wiki" ("joe@host.com" "my-wiki")))
  818. (defun xhg-export-via-mail (rev)
  819. (interactive (list (xhg-read-revision "Export revision: ")))
  820. (let ((file-name)
  821. (destination-email "")
  822. (base-file-name nil)
  823. (subject)
  824. (description))
  825. (dolist (m xhg-submit-patch-mapping)
  826. (when (string= (dvc-uniquify-file-name (car m)) (dvc-uniquify-file-name (xhg-tree-root)))
  827. ;;(message "%S" (cadr m))
  828. (setq destination-email (car (cadr m)))
  829. (setq base-file-name (cadr (cadr m)))))
  830. (setq file-name (concat (dvc-uniquify-file-name dvc-temp-directory) (or base-file-name "") rev ".patch"))
  831. (xhg-export rev file-name)
  832. (setq description
  833. (dvc-run-dvc-sync 'xhg (list "log" "-r" rev)
  834. :finished 'dvc-output-buffer-handler))
  835. (require 'reporter)
  836. (delete-other-windows)
  837. (reporter-submit-bug-report
  838. destination-email
  839. nil
  840. nil
  841. nil
  842. nil
  843. description)
  844. (save-excursion
  845. (re-search-backward "^summary: +\\(.+\\)")
  846. (setq subject (match-string-no-properties 1)))
  847. ;; delete emacs version - its not needed here
  848. (delete-region (point) (point-max))
  849. (mml-attach-file file-name "text/x-patch")
  850. (goto-char (point-min))
  851. (mail-position-on-field "Subject")
  852. (insert (concat "[PATCH] " subject))))
  853. ;; hg log -r $(hg identify)
  854. ;; add one to that revision number -> actual-rev+1
  855. ;; hg log -r actual-rev+1:tip, e.g. hg log -r 5:tip
  856. ;;;###autoload
  857. (defun xhg-missing-1 ()
  858. "Shows the logs of the new arrived changesets after a pull and before an update."
  859. (interactive)
  860. (let ((id (split-string (xhg-identify)))
  861. (last-log)
  862. (actual-rev))
  863. (if (= 2 (length id))
  864. (message "Nothing missing, already at tip.")
  865. (if (string= (car id) "unknown")
  866. (setq actual-rev -1)
  867. (setq last-log (dvc-run-dvc-sync 'xhg (list "log" "-r" (car id))
  868. :finished 'dvc-output-buffer-handler))
  869. (string-match "changeset: +\\([0-9]+\\)" last-log)
  870. (setq actual-rev (string-to-number (match-string-no-properties 1 last-log))))
  871. (xhg-log (concat (number-to-string (+ actual-rev 1)) ":tip")))))
  872. (defun xhg-save-diff (filename)
  873. "Save the current hg diff to a file named FILENAME."
  874. (interactive (list (read-file-name "Save the hg diff to: ")))
  875. (with-current-buffer
  876. (find-file-noselect filename)
  877. (let ((inhibit-read-only t))
  878. (erase-buffer)
  879. (insert (dvc-run-dvc-sync 'xhg (list "diff")
  880. :finished 'dvc-output-buffer-handler-withnewline))
  881. (save-buffer)
  882. (kill-buffer (current-buffer)))))
  883. ;; --------------------------------------------------------------------------------
  884. ;; hgrc-mode
  885. ;; --------------------------------------------------------------------------------
  886. (defun xhg-hgrc-open-hgrc-file (file-name)
  887. (find-file file-name)
  888. (unless (file-exists-p file-name)
  889. (insert "# -*- hgrc -*-\n\n")))
  890. (defun xhg-hgrc-edit-repository-hgrc ()
  891. "Edit the .hg/hgrc file for the current working copy"
  892. (interactive)
  893. (xhg-hgrc-open-hgrc-file (concat (xhg-tree-root) ".hg/hgrc")))
  894. (defun xhg-hgrc-edit-global-hgrc ()
  895. "Edit the ~/.hgrc file"
  896. (interactive)
  897. (xhg-hgrc-open-hgrc-file "~/.hgrc"))
  898. ;; Note: this mode is named hgrc-mode and not xhgrc-mode, because
  899. ;; a similar thing does not exist in mercurial.el yet and
  900. ;; that mode should be settable via a file local variable in .hgrc files
  901. (defvar hgrc-mode-map
  902. (let ((map (make-sparse-keymap)))
  903. map)
  904. "Keymap used in `hgrc-mode'.")
  905. (easy-menu-define hgrc-mode-menu hgrc-mode-map
  906. "`hgrc-mode' menu"
  907. `("hgrc"
  908. ["Show hgrc manpage" hgrc-mode-help t]
  909. ))
  910. (dvc-do-in-gnu-emacs
  911. ;; TODO : define-generic-mode doesn't exist in XEmacs.
  912. ;; http://list-archive.xemacs.org/xemacs-beta/200408/msg00016.html
  913. ;; world be better to use define-derived-mode below
  914. (define-generic-mode 'hgrc-mode
  915. '(?\; ?#)
  916. nil
  917. '(("^\\(\\[.*\\]\\)" 1 font-lock-constant-face)
  918. ("^\\s-*\\(.+\\)=\\([^\r\n]*\\)"
  919. (1 font-lock-variable-name-face)
  920. (2 font-lock-type-face)))
  921. '("\\.?hgrc\\'")
  922. '(hgrc-mode-setup-function)
  923. "Mode to edit mercurial configuration files.")
  924. )
  925. (dvc-do-in-xemacs
  926. (define-derived-mode hgrc-mode fundamental-mode
  927. "Hgrc-mode"
  928. "Major mode to edit hgrc files"
  929. ;; Empty mode for XEmacs users :-(
  930. ))
  931. (defun hgrc-mode-setup-function ()
  932. (use-local-map hgrc-mode-map))
  933. (defun hgrc-mode-help ()
  934. "Show the manual for the hgrc configuration file."
  935. (interactive)
  936. (split-window)
  937. (other-window 1)
  938. (apply (if (featurep 'xemacs) 'manual-entry 'woman) '("hgrc"))
  939. (other-window -1))
  940. (provide 'xhg)
  941. ;;; xhg.el ends here