vc-hg.el 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. ;;; vc-hg.el --- VC backend for the mercurial version control system -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2006-2015 Free Software Foundation, Inc.
  3. ;; Author: Ivan Kanis
  4. ;; Maintainer: emacs-devel@gnu.org
  5. ;; Keywords: vc tools
  6. ;; Package: vc
  7. ;; This file is part of GNU Emacs.
  8. ;; GNU Emacs is free software: you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation, either version 3 of the License, or
  11. ;; (at your option) any later version.
  12. ;; GNU Emacs is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;; This is a mercurial version control backend
  20. ;;; Thanks:
  21. ;;; Bugs:
  22. ;;; Installation:
  23. ;;; Todo:
  24. ;; 1) Implement the rest of the vc interface. See the comment at the
  25. ;; beginning of vc.el. The current status is:
  26. ;; FUNCTION NAME STATUS
  27. ;; BACKEND PROPERTIES
  28. ;; * revision-granularity OK
  29. ;; STATE-QUERYING FUNCTIONS
  30. ;; * registered (file) OK
  31. ;; * state (file) OK
  32. ;; - dir-status-files (dir files uf) OK
  33. ;; - dir-extra-headers (dir) OK
  34. ;; - dir-printer (fileinfo) OK
  35. ;; * working-revision (file) OK
  36. ;; * checkout-model (files) OK
  37. ;; - mode-line-string (file) NOT NEEDED
  38. ;; STATE-CHANGING FUNCTIONS
  39. ;; * register (files &optional rev comment) OK
  40. ;; * create-repo () OK
  41. ;; - responsible-p (file) OK
  42. ;; - receive-file (file rev) ?? PROBABLY NOT NEEDED
  43. ;; - unregister (file) OK
  44. ;; * checkin (files rev comment) OK
  45. ;; * find-revision (file rev buffer) OK
  46. ;; * checkout (file &optional rev) OK
  47. ;; * revert (file &optional contents-done) OK
  48. ;; - merge (file rev1 rev2) NEEDED
  49. ;; - merge-news (file) NEEDED
  50. ;; - steal-lock (file &optional revision) NOT NEEDED
  51. ;; HISTORY FUNCTIONS
  52. ;; * print-log (files buffer &optional shortlog start-revision limit) OK
  53. ;; - log-view-mode () OK
  54. ;; - show-log-entry (revision) NOT NEEDED, DEFAULT IS GOOD
  55. ;; - comment-history (file) NOT NEEDED
  56. ;; - update-changelog (files) NOT NEEDED
  57. ;; * diff (files &optional rev1 rev2 buffer) OK
  58. ;; - revision-completion-table (files) OK?
  59. ;; - annotate-command (file buf &optional rev) OK
  60. ;; - annotate-time () OK
  61. ;; - annotate-current-time () NOT NEEDED
  62. ;; - annotate-extract-revision-at-line () OK
  63. ;; TAG SYSTEM
  64. ;; - create-tag (dir name branchp) OK
  65. ;; - retrieve-tag (dir name update) OK FIXME UPDATE BUFFERS
  66. ;; MISCELLANEOUS
  67. ;; - make-version-backups-p (file) ??
  68. ;; - previous-revision (file rev) OK
  69. ;; - next-revision (file rev) OK
  70. ;; - check-headers () ??
  71. ;; - delete-file (file) TEST IT
  72. ;; - rename-file (old new) OK
  73. ;; - find-file-hook () added for bug#10709
  74. ;; 2) Implement Stefan Monnier's advice:
  75. ;; vc-hg-registered and vc-hg-state
  76. ;; Both of those functions should be super extra careful to fail gracefully in
  77. ;; unexpected circumstances. The reason this is important is that any error
  78. ;; there will prevent the user from even looking at the file :-(
  79. ;; Ideally, just like in vc-arch and vc-cvs, checking that the file is under
  80. ;; mercurial's control and extracting the current revision should be done
  81. ;; without even using `hg' (this way even if you don't have `hg' installed,
  82. ;; Emacs is able to tell you this file is under mercurial's control).
  83. ;;; History:
  84. ;;
  85. ;;; Code:
  86. (eval-when-compile
  87. (require 'cl-lib)
  88. (require 'vc)
  89. (require 'vc-dir))
  90. ;;; Customization options
  91. (defgroup vc-hg nil
  92. "VC Mercurial (hg) backend."
  93. :version "24.1"
  94. :group 'vc)
  95. (defcustom vc-hg-global-switches nil
  96. "Global switches to pass to any Hg command."
  97. :type '(choice (const :tag "None" nil)
  98. (string :tag "Argument String")
  99. (repeat :tag "Argument List" :value ("") string))
  100. :version "22.2"
  101. :group 'vc-hg)
  102. (defcustom vc-hg-diff-switches t ; Hg doesn't support common args like -u
  103. "String or list of strings specifying switches for Hg diff under VC.
  104. If nil, use the value of `vc-diff-switches'. If t, use no switches."
  105. :type '(choice (const :tag "Unspecified" nil)
  106. (const :tag "None" t)
  107. (string :tag "Argument String")
  108. (repeat :tag "Argument List" :value ("") string))
  109. :version "23.1"
  110. :group 'vc-hg)
  111. (defcustom vc-hg-annotate-switches nil
  112. "String or list of strings specifying switches for hg annotate under VC.
  113. If nil, use the value of `vc-annotate-switches'. If t, use no
  114. switches."
  115. :type '(choice (const :tag "Unspecified" nil)
  116. (const :tag "None" t)
  117. (string :tag "Argument String")
  118. (repeat :tag "Argument List" :value ("") string))
  119. :version "25.1"
  120. :group 'vc-hg)
  121. (defcustom vc-hg-program "hg"
  122. "Name of the Mercurial executable (excluding any arguments)."
  123. :type 'string
  124. :group 'vc-hg)
  125. (defcustom vc-hg-root-log-format
  126. `(,(concat "{rev}:{ifeq(branch, 'default','', '{branch}')}"
  127. ":{bookmarks}:{tags}:{author|person}"
  128. " {date|shortdate} {desc|firstline}\\n")
  129. ,(concat "^\\(?:[+@o x|-]*\\)" ;Graph data.
  130. "\\([0-9]+\\):\\([^:]*\\)"
  131. ":\\([^:]*\\):\\([^:]*\\):\\(.*?\\)"
  132. "[ \t]+\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)")
  133. ((1 'log-view-message-face)
  134. (2 'change-log-file)
  135. (3 'change-log-list)
  136. (4 'change-log-conditionals)
  137. (5 'change-log-name)
  138. (6 'change-log-date)))
  139. "Mercurial log template for `vc-hg-print-log' short format.
  140. This should be a list (TEMPLATE REGEXP KEYWORDS), where TEMPLATE
  141. is the \"--template\" argument string to pass to Mercurial,
  142. REGEXP is a regular expression matching the resulting Mercurial
  143. output, and KEYWORDS is a list of `font-lock-keywords' for
  144. highlighting the Log View buffer."
  145. :type '(list string string (repeat sexp))
  146. :group 'vc-hg
  147. :version "24.5")
  148. ;;; Properties of the backend
  149. (defvar vc-hg-history nil)
  150. (defun vc-hg-revision-granularity () 'repository)
  151. (defun vc-hg-checkout-model (_files) 'implicit)
  152. ;;; State querying functions
  153. ;;;###autoload (defun vc-hg-registered (file)
  154. ;;;###autoload "Return non-nil if FILE is registered with hg."
  155. ;;;###autoload (if (vc-find-root file ".hg") ; short cut
  156. ;;;###autoload (progn
  157. ;;;###autoload (load "vc-hg" nil t)
  158. ;;;###autoload (vc-hg-registered file))))
  159. ;; Modeled after the similar function in vc-bzr.el
  160. (defun vc-hg-registered (file)
  161. "Return non-nil if FILE is registered with hg."
  162. (when (vc-hg-root file) ; short cut
  163. (let ((state (vc-hg-state file))) ; expensive
  164. (and state (not (memq state '(ignored unregistered)))))))
  165. (defun vc-hg-state (file)
  166. "Hg-specific version of `vc-state'."
  167. (setq file (expand-file-name file))
  168. (let*
  169. ((status nil)
  170. (default-directory (file-name-directory file))
  171. (out
  172. (with-output-to-string
  173. (with-current-buffer
  174. standard-output
  175. (setq status
  176. (condition-case nil
  177. ;; Ignore all errors.
  178. (let ((process-environment
  179. ;; Avoid localization of messages so we
  180. ;; can parse the output. Disable pager.
  181. (append
  182. (list "TERM=dumb" "LANGUAGE=C" "HGPLAIN=1")
  183. process-environment)))
  184. (process-file
  185. vc-hg-program nil t nil
  186. "--config" "alias.status=status"
  187. "--config" "defaults.status="
  188. "status" "-A" (file-relative-name file)))
  189. ;; Some problem happened. E.g. We can't find an `hg'
  190. ;; executable.
  191. (error nil)))))))
  192. (when (and (eq 0 status)
  193. (> (length out) 0)
  194. (null (string-match ".*: No such file or directory$" out)))
  195. (let ((state (aref out 0)))
  196. (cond
  197. ((eq state ?=) 'up-to-date)
  198. ((eq state ?A) 'added)
  199. ((eq state ?M) 'edited)
  200. ((eq state ?I) 'ignored)
  201. ((eq state ?R) 'removed)
  202. ((eq state ?!) 'missing)
  203. ((eq state ??) 'unregistered)
  204. ((eq state ?C) 'up-to-date) ;; Older mercurial versions use this.
  205. (t 'up-to-date))))))
  206. (defun vc-hg-working-revision (file)
  207. "Hg-specific version of `vc-working-revision'."
  208. (or (ignore-errors
  209. (with-output-to-string
  210. (vc-hg-command standard-output 0 file
  211. "parent" "--template" "{rev}")))
  212. "0"))
  213. ;;; History functions
  214. (defcustom vc-hg-log-switches nil
  215. "String or list of strings specifying switches for hg log under VC."
  216. :type '(choice (const :tag "None" nil)
  217. (string :tag "Argument String")
  218. (repeat :tag "Argument List" :value ("") string))
  219. :group 'vc-hg)
  220. (autoload 'vc-setup-buffer "vc-dispatcher")
  221. (defvar vc-hg-log-graph nil
  222. "If non-nil, use `--graph' in the short log output.")
  223. (defvar vc-hg-log-format (concat "changeset: {rev}:{node|short}\n"
  224. "{tags % 'tag: {tag}\n'}"
  225. "{if(parents, 'parents: {parents}\n')}"
  226. "user: {author}\n"
  227. "Date: {date|date}\n"
  228. "summary: {desc|tabindent}\n\n")
  229. "Mercurial log template for `vc-hg-print-log' long format.")
  230. (defun vc-hg-print-log (files buffer &optional shortlog start-revision limit)
  231. "Print commit log associated with FILES into specified BUFFER.
  232. If SHORTLOG is non-nil, use a short format based on `vc-hg-root-log-format'.
  233. If START-REVISION is non-nil, it is the newest revision to show.
  234. If LIMIT is non-nil, show no more than this many entries."
  235. ;; `vc-do-command' creates the buffer, but we need it before running
  236. ;; the command.
  237. (vc-setup-buffer buffer)
  238. ;; If the buffer exists from a previous invocation it might be
  239. ;; read-only.
  240. (let ((inhibit-read-only t))
  241. (with-current-buffer
  242. buffer
  243. (apply 'vc-hg-command buffer 'async files "log"
  244. (nconc
  245. (when start-revision (list (format "-r%s:0" start-revision)))
  246. (when limit (list "-l" (format "%s" limit)))
  247. (if shortlog
  248. `(,@(if vc-hg-log-graph '("--graph"))
  249. "--template"
  250. ,(car vc-hg-root-log-format))
  251. `("--template" ,vc-hg-log-format))
  252. vc-hg-log-switches)))))
  253. (defvar log-view-message-re)
  254. (defvar log-view-file-re)
  255. (defvar log-view-font-lock-keywords)
  256. (defvar log-view-per-file-logs)
  257. (defvar log-view-expanded-log-entry-function)
  258. (define-derived-mode vc-hg-log-view-mode log-view-mode "Hg-Log-View"
  259. (require 'add-log) ;; we need the add-log faces
  260. (set (make-local-variable 'log-view-file-re) "\\`a\\`")
  261. (set (make-local-variable 'log-view-per-file-logs) nil)
  262. (set (make-local-variable 'log-view-message-re)
  263. (if (eq vc-log-view-type 'short)
  264. (cadr vc-hg-root-log-format)
  265. "^changeset:[ \t]*\\([0-9]+\\):\\(.+\\)"))
  266. (set (make-local-variable 'tab-width) 2)
  267. ;; Allow expanding short log entries
  268. (when (eq vc-log-view-type 'short)
  269. (setq truncate-lines t)
  270. (set (make-local-variable 'log-view-expanded-log-entry-function)
  271. 'vc-hg-expanded-log-entry))
  272. (set (make-local-variable 'log-view-font-lock-keywords)
  273. (if (eq vc-log-view-type 'short)
  274. (list (cons (nth 1 vc-hg-root-log-format)
  275. (nth 2 vc-hg-root-log-format)))
  276. (append
  277. log-view-font-lock-keywords
  278. '(
  279. ;; Handle the case:
  280. ;; user: FirstName LastName <foo@bar>
  281. ("^user:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]"
  282. (1 'change-log-name)
  283. (2 'change-log-email))
  284. ;; Handle the cases:
  285. ;; user: foo@bar
  286. ;; and
  287. ;; user: foo
  288. ("^user:[ \t]+\\([A-Za-z0-9_.+-]+\\(?:@[A-Za-z0-9_.-]+\\)?\\)"
  289. (1 'change-log-email))
  290. ("^date: \\(.+\\)" (1 'change-log-date))
  291. ("^tag: +\\([^ ]+\\)$" (1 'highlight))
  292. ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message)))))))
  293. (autoload 'vc-switches "vc")
  294. (defun vc-hg-diff (files &optional oldvers newvers buffer async)
  295. "Get a difference report using hg between two revisions of FILES."
  296. (let* ((firstfile (car files))
  297. (working (and firstfile (vc-working-revision firstfile))))
  298. (when (and (equal oldvers working) (not newvers))
  299. (setq oldvers nil))
  300. (when (and (not oldvers) newvers)
  301. (setq oldvers working))
  302. (apply #'vc-hg-command
  303. (or buffer "*vc-diff*")
  304. (if async 'async nil)
  305. files "diff"
  306. (append
  307. (vc-switches 'hg 'diff)
  308. (when oldvers
  309. (if newvers
  310. (list "-r" oldvers "-r" newvers)
  311. (list "-r" oldvers)))))))
  312. (defun vc-hg-expanded-log-entry (revision)
  313. (with-temp-buffer
  314. (vc-hg-command t nil nil "log" "-r" revision "--template" vc-hg-log-format)
  315. (goto-char (point-min))
  316. (unless (eobp)
  317. ;; Indent the expanded log entry.
  318. (indent-region (point-min) (point-max) 2)
  319. (goto-char (point-max))
  320. (buffer-string))))
  321. (defun vc-hg-revision-table (files)
  322. (let ((default-directory (file-name-directory (car files))))
  323. (with-temp-buffer
  324. (vc-hg-command t nil files "log" "--template" "{rev} ")
  325. (split-string
  326. (buffer-substring-no-properties (point-min) (point-max))))))
  327. ;; Modeled after the similar function in vc-cvs.el
  328. (defun vc-hg-revision-completion-table (files)
  329. (letrec ((table (lazy-completion-table
  330. table (lambda () (vc-hg-revision-table files)))))
  331. table))
  332. (defun vc-hg-annotate-command (file buffer &optional revision)
  333. "Execute \"hg annotate\" on FILE, inserting the contents in BUFFER.
  334. Optional arg REVISION is a revision to annotate from."
  335. (apply #'vc-hg-command buffer 0 file "annotate" "-d" "-n" "--follow"
  336. (append (vc-switches 'hg 'annotate)
  337. (if revision (list (concat "-r" revision))))))
  338. (declare-function vc-annotate-convert-time "vc-annotate" (&optional time))
  339. ;; The format for one line output by "hg annotate -d -n" looks like this:
  340. ;;215 Wed Jun 20 21:22:58 2007 -0700: CONTENTS
  341. ;; i.e: VERSION_NUMBER DATE: CONTENTS
  342. ;; If the user has set the "--follow" option, the output looks like:
  343. ;;215 Wed Jun 20 21:22:58 2007 -0700 foo.c: CONTENTS
  344. ;; i.e. VERSION_NUMBER DATE FILENAME: CONTENTS
  345. (defconst vc-hg-annotate-re
  346. "^[ \t]*\\([0-9]+\\) \\(.\\{30\\}\\)\\(?:\\(: \\)\\|\\(?: +\\([^:\n]+\\(?::\\(?:[^: \n][^:\n]*\\)?\\)*\\): \\)\\)")
  347. (defun vc-hg-annotate-time ()
  348. (when (looking-at vc-hg-annotate-re)
  349. (goto-char (match-end 0))
  350. (vc-annotate-convert-time
  351. (date-to-time (match-string-no-properties 2)))))
  352. (defun vc-hg-annotate-extract-revision-at-line ()
  353. (save-excursion
  354. (beginning-of-line)
  355. (when (looking-at vc-hg-annotate-re)
  356. (if (match-beginning 3)
  357. (match-string-no-properties 1)
  358. (cons (match-string-no-properties 1)
  359. (expand-file-name (match-string-no-properties 4)
  360. (vc-hg-root default-directory)))))))
  361. ;;; Tag system
  362. (defun vc-hg-create-tag (dir name branchp)
  363. "Attach the tag NAME to the state of the working copy."
  364. (let ((default-directory dir))
  365. (and (vc-hg-command nil 0 nil "status")
  366. (vc-hg-command nil 0 nil (if branchp "bookmark" "tag") name))))
  367. (defun vc-hg-retrieve-tag (dir name _update)
  368. "Retrieve the version tagged by NAME of all registered files at or below DIR."
  369. (let ((default-directory dir))
  370. (vc-hg-command nil 0 nil "update" name)
  371. ;; FIXME: update buffers if `update' is true
  372. ;; TODO: update *vc-change-log* buffer so can see @ if --graph
  373. ))
  374. ;;; Miscellaneous
  375. (defun vc-hg-previous-revision (_file rev)
  376. (let ((newrev (1- (string-to-number rev))))
  377. (when (>= newrev 0)
  378. (number-to-string newrev))))
  379. (defun vc-hg-next-revision (_file rev)
  380. (let ((newrev (1+ (string-to-number rev)))
  381. (tip-revision
  382. (with-temp-buffer
  383. (vc-hg-command t 0 nil "tip" "--style=default")
  384. (goto-char (point-min))
  385. (re-search-forward "^changeset:[ \t]*\\([0-9]+\\):")
  386. (string-to-number (match-string-no-properties 1)))))
  387. ;; We don't want to exceed the maximum possible revision number, ie
  388. ;; the tip revision.
  389. (when (<= newrev tip-revision)
  390. (number-to-string newrev))))
  391. ;; Modeled after the similar function in vc-bzr.el
  392. (defun vc-hg-delete-file (file)
  393. "Delete FILE and delete it in the hg repository."
  394. (condition-case ()
  395. (delete-file file)
  396. (file-error nil))
  397. (vc-hg-command nil 0 file "remove" "--after" "--force"))
  398. ;; Modeled after the similar function in vc-bzr.el
  399. (defun vc-hg-rename-file (old new)
  400. "Rename file from OLD to NEW using `hg mv'."
  401. (vc-hg-command nil 0 new "mv" old))
  402. (defun vc-hg-register (files &optional _comment)
  403. "Register FILES under hg. COMMENT is ignored."
  404. (vc-hg-command nil 0 files "add"))
  405. (defun vc-hg-create-repo ()
  406. "Create a new Mercurial repository."
  407. (vc-hg-command nil 0 nil "init"))
  408. (defalias 'vc-hg-responsible-p 'vc-hg-root)
  409. (defun vc-hg-unregister (file)
  410. "Unregister FILE from hg."
  411. (vc-hg-command nil 0 file "forget"))
  412. (declare-function log-edit-extract-headers "log-edit" (headers string))
  413. (defun vc-hg-checkin (files comment &optional _rev)
  414. "Hg-specific version of `vc-backend-checkin'.
  415. REV is ignored."
  416. (apply 'vc-hg-command nil 0 files
  417. (nconc (list "commit" "-m")
  418. (log-edit-extract-headers '(("Author" . "--user")
  419. ("Date" . "--date"))
  420. comment))))
  421. (defun vc-hg-find-revision (file rev buffer)
  422. (let ((coding-system-for-read 'binary)
  423. (coding-system-for-write 'binary))
  424. (if rev
  425. (vc-hg-command buffer 0 file "cat" "-r" rev)
  426. (vc-hg-command buffer 0 file "cat"))))
  427. (defun vc-hg-find-ignore-file (file)
  428. "Return the root directory of the repository of FILE."
  429. (expand-file-name ".hgignore"
  430. (vc-hg-root file)))
  431. ;; Modeled after the similar function in vc-bzr.el
  432. (defun vc-hg-checkout (file &optional rev)
  433. "Retrieve a revision of FILE.
  434. EDITABLE is ignored.
  435. REV is the revision to check out into WORKFILE."
  436. (let ((coding-system-for-read 'binary)
  437. (coding-system-for-write 'binary))
  438. (with-current-buffer (or (get-file-buffer file) (current-buffer))
  439. (if rev
  440. (vc-hg-command t 0 file "cat" "-r" rev)
  441. (vc-hg-command t 0 file "cat")))))
  442. (defun vc-hg-resolve-when-done ()
  443. "Call \"hg resolve -m\" if the conflict markers have been removed."
  444. (save-excursion
  445. (goto-char (point-min))
  446. (unless (re-search-forward "^<<<<<<< " nil t)
  447. (vc-hg-command nil 0 buffer-file-name "resolve" "-m")
  448. ;; Remove the hook so that it is not called multiple times.
  449. (remove-hook 'after-save-hook 'vc-hg-resolve-when-done t))))
  450. (defun vc-hg-find-file-hook ()
  451. (when (and buffer-file-name
  452. (file-exists-p (concat buffer-file-name ".orig"))
  453. ;; Hg does not seem to have a "conflict" status, eg
  454. ;; hg http://bz.selenic.com/show_bug.cgi?id=2724
  455. (memq (vc-file-getprop buffer-file-name 'vc-state)
  456. '(edited conflict))
  457. ;; Maybe go on to check that "hg resolve -l" says "U"?
  458. ;; If "hg resolve -l" says there's a conflict but there are no
  459. ;; conflict markers, it's not clear what we should do.
  460. (save-excursion
  461. (goto-char (point-min))
  462. (re-search-forward "^<<<<<<< " nil t)))
  463. ;; Hg may not recognize "conflict" as a state, but we can do better.
  464. (vc-file-setprop buffer-file-name 'vc-state 'conflict)
  465. (smerge-start-session)
  466. (add-hook 'after-save-hook 'vc-hg-resolve-when-done nil t)
  467. (vc-message-unresolved-conflicts buffer-file-name)))
  468. ;; Modeled after the similar function in vc-bzr.el
  469. (defun vc-hg-revert (file &optional contents-done)
  470. (unless contents-done
  471. (with-temp-buffer (vc-hg-command t 0 file "revert"))))
  472. ;;; Hg specific functionality.
  473. (defvar vc-hg-extra-menu-map
  474. (let ((map (make-sparse-keymap)))
  475. map))
  476. (defun vc-hg-extra-menu () vc-hg-extra-menu-map)
  477. (defun vc-hg-extra-status-menu () vc-hg-extra-menu-map)
  478. (defvar log-view-vc-backend)
  479. (cl-defstruct (vc-hg-extra-fileinfo
  480. (:copier nil)
  481. (:constructor vc-hg-create-extra-fileinfo (rename-state extra-name))
  482. (:conc-name vc-hg-extra-fileinfo->))
  483. rename-state ;; rename or copy state
  484. extra-name) ;; original name for copies and rename targets, new name for
  485. (declare-function vc-default-dir-printer "vc-dir" (backend fileentry))
  486. (defun vc-hg-dir-printer (info)
  487. "Pretty-printer for the vc-dir-fileinfo structure."
  488. (let ((extra (vc-dir-fileinfo->extra info)))
  489. (vc-default-dir-printer 'Hg info)
  490. (when extra
  491. (insert (propertize
  492. (format " (%s %s)"
  493. (pcase (vc-hg-extra-fileinfo->rename-state extra)
  494. (`copied "copied from")
  495. (`renamed-from "renamed from")
  496. (`renamed-to "renamed to"))
  497. (vc-hg-extra-fileinfo->extra-name extra))
  498. 'face 'font-lock-comment-face)))))
  499. (defun vc-hg-after-dir-status (update-function)
  500. (let ((file nil)
  501. (translation '((?= . up-to-date)
  502. (?C . up-to-date)
  503. (?A . added)
  504. (?R . removed)
  505. (?M . edited)
  506. (?I . ignored)
  507. (?! . missing)
  508. (? . copy-rename-line)
  509. (?? . unregistered)))
  510. (translated nil)
  511. (result nil)
  512. (last-added nil)
  513. (last-line-copy nil))
  514. (goto-char (point-min))
  515. (while (not (eobp))
  516. (setq translated (cdr (assoc (char-after) translation)))
  517. (setq file
  518. (buffer-substring-no-properties (+ (point) 2)
  519. (line-end-position)))
  520. (cond ((not translated)
  521. (setq last-line-copy nil))
  522. ((eq translated 'up-to-date)
  523. (setq last-line-copy nil))
  524. ((eq translated 'copy-rename-line)
  525. ;; For copied files the output looks like this:
  526. ;; A COPIED_FILE_NAME
  527. ;; ORIGINAL_FILE_NAME
  528. (setf (nth 2 last-added)
  529. (vc-hg-create-extra-fileinfo 'copied file))
  530. (setq last-line-copy t))
  531. ((and last-line-copy (eq translated 'removed))
  532. ;; For renamed files the output looks like this:
  533. ;; A NEW_FILE_NAME
  534. ;; ORIGINAL_FILE_NAME
  535. ;; R ORIGINAL_FILE_NAME
  536. ;; We need to adjust the previous entry to not think it is a copy.
  537. (setf (vc-hg-extra-fileinfo->rename-state (nth 2 last-added))
  538. 'renamed-from)
  539. (push (list file translated
  540. (vc-hg-create-extra-fileinfo
  541. 'renamed-to (nth 0 last-added))) result)
  542. (setq last-line-copy nil))
  543. (t
  544. (setq last-added (list file translated nil))
  545. (push last-added result)
  546. (setq last-line-copy nil)))
  547. (forward-line))
  548. (funcall update-function result)))
  549. ;; Follows vc-hg-command (or vc-do-async-command), which uses vc-do-command
  550. ;; from vc-dispatcher.
  551. (declare-function vc-exec-after "vc-dispatcher" (code))
  552. ;; Follows vc-exec-after.
  553. (declare-function vc-set-async-update "vc-dispatcher" (process-buffer))
  554. (defun vc-hg-dir-status-files (dir files update-function)
  555. (apply 'vc-hg-command (current-buffer) 'async dir "status"
  556. (concat "-mardu" (if files "i"))
  557. "-C" files)
  558. (vc-run-delayed
  559. (vc-hg-after-dir-status update-function)))
  560. (defun vc-hg-dir-extra-header (name &rest commands)
  561. (concat (propertize name 'face 'font-lock-type-face)
  562. (propertize
  563. (with-temp-buffer
  564. (apply 'vc-hg-command (current-buffer) 0 nil commands)
  565. (buffer-substring-no-properties (point-min) (1- (point-max))))
  566. 'face 'font-lock-variable-name-face)))
  567. (defun vc-hg-dir-extra-headers (dir)
  568. "Generate extra status headers for a Mercurial tree."
  569. (let ((default-directory dir))
  570. (concat
  571. (vc-hg-dir-extra-header "Root : " "root") "\n"
  572. (vc-hg-dir-extra-header "Branch : " "id" "-b") "\n"
  573. (vc-hg-dir-extra-header "Tags : " "id" "-t") ; "\n"
  574. ;; these change after each commit
  575. ;; (vc-hg-dir-extra-header "Local num : " "id" "-n") "\n"
  576. ;; (vc-hg-dir-extra-header "Global id : " "id" "-i")
  577. )))
  578. (defun vc-hg-log-incoming (buffer remote-location)
  579. (vc-hg-command buffer 1 nil "incoming" "-n" (unless (string= remote-location "")
  580. remote-location)))
  581. (defun vc-hg-log-outgoing (buffer remote-location)
  582. (vc-hg-command buffer 1 nil "outgoing" "-n" (unless (string= remote-location "")
  583. remote-location)))
  584. (defvar vc-hg-error-regexp-alist nil
  585. ;; 'hg pull' does not list modified files, so, for now, the only
  586. ;; benefit of `vc-compilation-mode' is that one can get rid of
  587. ;; *vc-hg* buffer with 'q' or 'z'.
  588. ;; TODO: call 'hg incoming' before pull/merge to get the list of
  589. ;; modified files
  590. "Value of `compilation-error-regexp-alist' in *vc-hg* buffers.")
  591. (autoload 'vc-do-async-command "vc-dispatcher")
  592. (autoload 'log-view-get-marked "log-view")
  593. (defun vc-hg--pushpull (command prompt &optional obsolete)
  594. "Run COMMAND (a string; either push or pull) on the current Hg branch.
  595. If PROMPT is non-nil, prompt for the Hg command to run.
  596. If OBSOLETE is non-nil, behave like the old versions of the Hg push/pull
  597. commands, which only operated on marked files."
  598. (let (marked-list)
  599. ;; The `vc-hg-pull' and `vc-hg-push' commands existed before the
  600. ;; `pull'/`push' VC actions were implemented.
  601. ;; The following is for backwards compatibility.
  602. (if (and obsolete (setq marked-list (log-view-get-marked)))
  603. (apply #'vc-hg-command
  604. nil 0 nil
  605. command
  606. (apply 'nconc
  607. (mapcar (lambda (arg) (list "-r" arg)) marked-list)))
  608. (let* ((root (vc-hg-root default-directory))
  609. (buffer (format "*vc-hg : %s*" (expand-file-name root)))
  610. (hg-program vc-hg-program)
  611. ;; Fixme: before updating the working copy to the latest
  612. ;; state, should check if it's visiting an old revision.
  613. (args (if (equal command "pull") '("-u"))))
  614. ;; If necessary, prompt for the exact command.
  615. ;; TODO if pushing, prompt if no default push location - cf bzr.
  616. (when prompt
  617. (setq args (split-string
  618. (read-shell-command
  619. (format "Hg %s command: " command)
  620. (format "%s %s%s" hg-program command
  621. (if (not args) ""
  622. (concat " " (mapconcat 'identity args " "))))
  623. 'vc-hg-history)
  624. " " t))
  625. (setq hg-program (car args)
  626. command (cadr args)
  627. args (cddr args)))
  628. (apply 'vc-do-async-command buffer root hg-program command args)
  629. (with-current-buffer buffer
  630. (vc-run-delayed (vc-compilation-mode 'hg)))
  631. (vc-set-async-update buffer)))))
  632. (defun vc-hg-pull (prompt)
  633. "Issue a Mercurial pull command.
  634. If called interactively with a set of marked Log View buffers,
  635. call \"hg pull -r REVS\" to pull in the specified revisions REVS.
  636. With a prefix argument or if PROMPT is non-nil, prompt for a
  637. specific Mercurial pull command. The default is \"hg pull -u\",
  638. which fetches changesets from the default remote repository and
  639. then attempts to update the working directory."
  640. (interactive "P")
  641. (vc-hg--pushpull "pull" prompt (called-interactively-p 'interactive)))
  642. (defun vc-hg-push (prompt)
  643. "Push changes from the current Mercurial branch.
  644. Normally, this runs \"hg push\". If PROMPT is non-nil, prompt
  645. for the Hg command to run.
  646. If called interactively with a set of marked Log View buffers,
  647. call \"hg push -r REVS\" to push the specified revisions REVS."
  648. (interactive "P")
  649. (vc-hg--pushpull "push" prompt (called-interactively-p 'interactive)))
  650. (defun vc-hg-merge-branch ()
  651. "Merge incoming changes into the current working directory.
  652. This runs the command \"hg merge\"."
  653. (let* ((root (vc-hg-root default-directory))
  654. (buffer (format "*vc-hg : %s*" (expand-file-name root))))
  655. (apply 'vc-do-async-command buffer root vc-hg-program '("merge"))
  656. (with-current-buffer buffer (vc-run-delayed (vc-compilation-mode 'hg)))
  657. (vc-set-async-update buffer)))
  658. ;;; Internal functions
  659. (defun vc-hg-command (buffer okstatus file-or-list &rest flags)
  660. "A wrapper around `vc-do-command' for use in vc-hg.el.
  661. This function differs from vc-do-command in that it invokes
  662. `vc-hg-program', and passes `vc-hg-global-switches' to it before FLAGS."
  663. (apply 'vc-do-command (or buffer "*vc*") okstatus vc-hg-program file-or-list
  664. (if (stringp vc-hg-global-switches)
  665. (cons vc-hg-global-switches flags)
  666. (append vc-hg-global-switches
  667. flags))))
  668. (defun vc-hg-root (file)
  669. (vc-find-root file ".hg"))
  670. (provide 'vc-hg)
  671. ;;; vc-hg.el ends here