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