* info.el (Info-next-reference, Info-prev-reference): Add numeric
[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)))
155 "Mercurial log template for `vc-print-root-log'.
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
179;;;###autoload (load "vc-hg")
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
86b5e14c 248(defun vc-hg-print-log (files buffer &optional shortlog start-revision limit)
8cdd17b4 249 "Get change log associated with FILES."
7c1912af
DN
250 ;; `vc-do-command' creates the buffer, but we need it before running
251 ;; the command.
252 (vc-setup-buffer buffer)
253 ;; If the buffer exists from a previous invocation it might be
254 ;; read-only.
255 (let ((inhibit-read-only t))
6653c6b7
DN
256 (with-current-buffer
257 buffer
32ba3abc 258 (apply 'vc-hg-command buffer 0 files "log"
3fb87bf5 259 (nconc
662c5698 260 (when start-revision (list (format "-r%s:" start-revision)))
6616006b 261 (when limit (list "-l" (format "%s" limit)))
33f6cf7b 262 (when shortlog (list "--template" (car vc-hg-root-log-format)))
6616006b 263 vc-hg-log-switches)))))
61223448 264
d797e643
DN
265(defvar log-view-message-re)
266(defvar log-view-file-re)
267(defvar log-view-font-lock-keywords)
6653c6b7 268(defvar log-view-per-file-logs)
33f6cf7b 269(defvar log-view-expanded-log-entry-function)
d797e643 270
4211679b 271(define-derived-mode vc-hg-log-view-mode log-view-mode "Hg-Log-View"
f0230324 272 (require 'add-log) ;; we need the add-log faces
6653c6b7
DN
273 (set (make-local-variable 'log-view-file-re) "\\`a\\`")
274 (set (make-local-variable 'log-view-per-file-logs) nil)
d797e643 275 (set (make-local-variable 'log-view-message-re)
31527c56 276 (if (eq vc-log-view-type 'short)
33f6cf7b 277 (cadr vc-hg-root-log-format)
72169e55 278 "^changeset:[ \t]*\\([0-9]+\\):\\(.+\\)"))
33f6cf7b
CY
279 ;; Allow expanding short log entries
280 (when (eq vc-log-view-type 'short)
281 (setq truncate-lines t)
282 (set (make-local-variable 'log-view-expanded-log-entry-function)
283 'vc-hg-expanded-log-entry))
d797e643 284 (set (make-local-variable 'log-view-font-lock-keywords)
31527c56 285 (if (eq vc-log-view-type 'short)
33f6cf7b
CY
286 (list (cons (nth 1 vc-hg-root-log-format)
287 (nth 2 vc-hg-root-log-format)))
288 (append
289 log-view-font-lock-keywords
290 '(
291 ;; Handle the case:
292 ;; user: FirstName LastName <foo@bar>
293 ("^user:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]"
294 (1 'change-log-name)
295 (2 'change-log-email))
296 ;; Handle the cases:
297 ;; user: foo@bar
298 ;; and
299 ;; user: foo
300 ("^user:[ \t]+\\([A-Za-z0-9_.+-]+\\(?:@[A-Za-z0-9_.-]+\\)?\\)"
301 (1 'change-log-email))
302 ("^date: \\(.+\\)" (1 'change-log-date))
303 ("^tag: +\\([^ ]+\\)$" (1 'highlight))
304 ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message)))))))
d797e643 305
8cdd17b4 306(defun vc-hg-diff (files &optional oldvers newvers buffer)
5b5afd50 307 "Get a difference report using hg between two revisions of FILES."
6653c6b7 308 (let* ((firstfile (car files))
72169e55 309 (working (and firstfile (vc-working-revision firstfile))))
ec4149ff
DN
310 (when (and (equal oldvers working) (not newvers))
311 (setq oldvers nil))
312 (when (and (not oldvers) newvers)
313 (setq oldvers working))
efa3639b 314 (apply #'vc-hg-command (or buffer "*vc-diff*") nil files "diff"
72169e55
SS
315 (append
316 (vc-switches 'hg 'diff)
317 (when oldvers
318 (if newvers
319 (list "-r" oldvers "-r" newvers)
320 (list "-r" oldvers)))))))
cdaf01cc 321
33f6cf7b
CY
322(defun vc-hg-expanded-log-entry (revision)
323 (with-temp-buffer
324 (vc-hg-command t nil nil "log" "-r" revision)
325 (goto-char (point-min))
326 (unless (eobp)
327 ;; Indent the expanded log entry.
328 (indent-region (point-min) (point-max) 2)
329 (goto-char (point-max))
330 (buffer-string))))
331
87d1a48e
SM
332(defun vc-hg-revision-table (files)
333 (let ((default-directory (file-name-directory (car files))))
34b7fb85 334 (with-temp-buffer
004a00f4 335 (vc-hg-command t nil files "log" "--template" "{rev} ")
62754d29 336 (split-string
34b7fb85
DN
337 (buffer-substring-no-properties (point-min) (point-max))))))
338
6772c8e1 339;; Modeled after the similar function in vc-cvs.el
87d1a48e 340(defun vc-hg-revision-completion-table (files)
0d42eb3e
SM
341 (letrec ((table (lazy-completion-table
342 table (lambda () (vc-hg-revision-table files)))))
eff23ff3 343 table))
34b7fb85 344
5b5afd50 345(defun vc-hg-annotate-command (file buffer &optional revision)
cdaf01cc 346 "Execute \"hg annotate\" on FILE, inserting the contents in BUFFER.
5b5afd50 347Optional arg REVISION is a revision to annotate from."
d1e4c403
DN
348 (vc-hg-command buffer 0 file "annotate" "-d" "-n" "--follow"
349 (when revision (concat "-r" revision))))
cdaf01cc 350
f8bd9ac6 351(declare-function vc-annotate-convert-time "vc-annotate" (time))
cdaf01cc 352
11a4edc2
SM
353;; The format for one line output by "hg annotate -d -n" looks like this:
354;;215 Wed Jun 20 21:22:58 2007 -0700: CONTENTS
355;; i.e: VERSION_NUMBER DATE: CONTENTS
4064ff25
DN
356;; If the user has set the "--follow" option, the output looks like:
357;;215 Wed Jun 20 21:22:58 2007 -0700 foo.c: CONTENTS
358;; i.e. VERSION_NUMBER DATE FILENAME: CONTENTS
359(defconst vc-hg-annotate-re
d1e4c403 360 "^[ \t]*\\([0-9]+\\) \\(.\\{30\\}\\)\\(?:\\(: \\)\\|\\(?: +\\(.+\\): \\)\\)")
cdaf01cc
DN
361
362(defun vc-hg-annotate-time ()
363 (when (looking-at vc-hg-annotate-re)
364 (goto-char (match-end 0))
11a4edc2 365 (vc-annotate-convert-time
cdaf01cc
DN
366 (date-to-time (match-string-no-properties 2)))))
367
368(defun vc-hg-annotate-extract-revision-at-line ()
369 (save-excursion
370 (beginning-of-line)
d1e4c403
DN
371 (when (looking-at vc-hg-annotate-re)
372 (if (match-beginning 3)
373 (match-string-no-properties 1)
374 (cons (match-string-no-properties 1)
8507c65c
CY
375 (expand-file-name (match-string-no-properties 4)
376 (vc-hg-root default-directory)))))))
cdaf01cc 377
0d42eb3e 378(defun vc-hg-previous-revision (_file rev)
cdaf01cc
DN
379 (let ((newrev (1- (string-to-number rev))))
380 (when (>= newrev 0)
381 (number-to-string newrev))))
61223448 382
0d42eb3e 383(defun vc-hg-next-revision (_file rev)
a07e665b 384 (let ((newrev (1+ (string-to-number rev)))
72169e55
SS
385 (tip-revision
386 (with-temp-buffer
3ddbf803 387 (vc-hg-command t 0 nil "tip" "--style=default")
72169e55
SS
388 (goto-char (point-min))
389 (re-search-forward "^changeset:[ \t]*\\([0-9]+\\):")
390 (string-to-number (match-string-no-properties 1)))))
5b5afd50
ER
391 ;; We don't want to exceed the maximum possible revision number, ie
392 ;; the tip revision.
393 (when (<= newrev tip-revision)
a07e665b
DN
394 (number-to-string newrev))))
395
6772c8e1 396;; Modeled after the similar function in vc-bzr.el
a07e665b
DN
397(defun vc-hg-delete-file (file)
398 "Delete FILE and delete it in the hg repository."
399 (condition-case ()
400 (delete-file file)
401 (file-error nil))
34b7fb85 402 (vc-hg-command nil 0 file "remove" "--after" "--force"))
a07e665b 403
6772c8e1 404;; Modeled after the similar function in vc-bzr.el
a07e665b
DN
405(defun vc-hg-rename-file (old new)
406 "Rename file from OLD to NEW using `hg mv'."
bfd57731 407 (vc-hg-command nil 0 new "mv" old))
a07e665b 408
0d42eb3e 409(defun vc-hg-register (files &optional _rev _comment)
8cdd17b4 410 "Register FILES under hg.
248c6645
DN
411REV is ignored.
412COMMENT is ignored."
34b7fb85 413 (vc-hg-command nil 0 files "add"))
8cdd17b4
ER
414
415(defun vc-hg-create-repo ()
416 "Create a new Mercurial repository."
34b7fb85 417 (vc-hg-command nil 0 nil "init"))
248c6645 418
a07e665b
DN
419(defalias 'vc-hg-responsible-p 'vc-hg-root)
420
6772c8e1 421;; Modeled after the similar function in vc-bzr.el
a07e665b
DN
422(defun vc-hg-could-register (file)
423 "Return non-nil if FILE could be registered under hg."
424 (and (vc-hg-responsible-p file) ; shortcut
425 (condition-case ()
426 (with-temp-buffer
427 (vc-hg-command t nil file "add" "--dry-run"))
428 ;; The command succeeds with no output if file is
429 ;; registered.
430 (error))))
431
4f10da1c 432;; FIXME: This would remove the file. Is that correct?
a07e665b
DN
433;; (defun vc-hg-unregister (file)
434;; "Unregister FILE from hg."
435;; (vc-hg-command nil nil file "remove"))
436
e97a42c1
SM
437(declare-function log-edit-extract-headers "log-edit" (headers string))
438
0d42eb3e 439(defun vc-hg-checkin (files _rev comment)
4211679b 440 "Hg-specific version of `vc-backend-checkin'.
cdaf01cc 441REV is ignored."
3fb87bf5 442 (apply 'vc-hg-command nil 0 files
e97a42c1 443 (nconc (list "commit" "-m")
fab43c76
DN
444 (log-edit-extract-headers '(("Author" . "--user")
445 ("Date" . "--date"))
e97a42c1 446 comment))))
cdaf01cc 447
ac3f4c6f 448(defun vc-hg-find-revision (file rev buffer)
248c6645
DN
449 (let ((coding-system-for-read 'binary)
450 (coding-system-for-write 'binary))
248c6645 451 (if rev
72169e55 452 (vc-hg-command buffer 0 file "cat" "-r" rev)
34b7fb85 453 (vc-hg-command buffer 0 file "cat"))))
a07e665b 454
6772c8e1 455;; Modeled after the similar function in vc-bzr.el
0d42eb3e 456(defun vc-hg-checkout (file &optional _editable rev)
a6ea7ffc
DN
457 "Retrieve a revision of FILE.
458EDITABLE is ignored.
459REV is the revision to check out into WORKFILE."
460 (let ((coding-system-for-read 'binary)
461 (coding-system-for-write 'binary))
462 (with-current-buffer (or (get-file-buffer file) (current-buffer))
463 (if rev
34b7fb85
DN
464 (vc-hg-command t 0 file "cat" "-r" rev)
465 (vc-hg-command t 0 file "cat")))))
248c6645 466
49596095
GM
467(defun vc-hg-resolve-when-done ()
468 "Call \"hg resolve -m\" if the conflict markers have been removed."
469 (save-excursion
470 (goto-char (point-min))
471 (unless (re-search-forward "^<<<<<<< " nil t)
472 (vc-hg-command nil 0 buffer-file-name "resolve" "-m")
473 ;; Remove the hook so that it is not called multiple times.
474 (remove-hook 'after-save-hook 'vc-hg-resolve-when-done t))))
475
476(defun vc-hg-find-file-hook ()
477 (when (and buffer-file-name
478 (file-exists-p (concat buffer-file-name ".orig"))
479 ;; Hg does not seem to have a "conflict" status, eg
480 ;; hg http://bz.selenic.com/show_bug.cgi?id=2724
481 (memq (vc-file-getprop buffer-file-name 'vc-state)
482 '(edited conflict))
483 ;; Maybe go on to check that "hg resolve -l" says "U"?
484 ;; If "hg resolve -l" says there's a conflict but there are no
485 ;; conflict markers, it's not clear what we should do.
486 (save-excursion
487 (goto-char (point-min))
488 (re-search-forward "^<<<<<<< " nil t)))
489 ;; Hg may not recognize "conflict" as a state, but we can do better.
490 (vc-file-setprop buffer-file-name 'vc-state 'conflict)
491 (smerge-start-session)
492 (add-hook 'after-save-hook 'vc-hg-resolve-when-done nil t)
493 (message "There are unresolved conflicts in this file")))
494
495
6772c8e1 496;; Modeled after the similar function in vc-bzr.el
a272e668
DN
497(defun vc-hg-workfile-unchanged-p (file)
498 (eq 'up-to-date (vc-hg-state file)))
499
6772c8e1 500;; Modeled after the similar function in vc-bzr.el
b33ac3b7
DN
501(defun vc-hg-revert (file &optional contents-done)
502 (unless contents-done
34b7fb85 503 (with-temp-buffer (vc-hg-command t 0 file "revert"))))
b33ac3b7 504
f0230324
DN
505;;; Hg specific functionality.
506
f0230324
DN
507(defvar vc-hg-extra-menu-map
508 (let ((map (make-sparse-keymap)))
f0230324
DN
509 map))
510
511(defun vc-hg-extra-menu () vc-hg-extra-menu-map)
512
25a4ea6d 513(defun vc-hg-extra-status-menu () vc-hg-extra-menu-map)
6656ecaa 514
4a827e0a 515(defvar log-view-vc-backend)
f0230324 516
a464a6c7 517(cl-defstruct (vc-hg-extra-fileinfo
7db924c0
DN
518 (:copier nil)
519 (:constructor vc-hg-create-extra-fileinfo (rename-state extra-name))
520 (:conc-name vc-hg-extra-fileinfo->))
521 rename-state ;; rename or copy state
fe4f8695 522 extra-name) ;; original name for copies and rename targets, new name for
7db924c0 523
13ad7457 524(declare-function vc-default-dir-printer "vc-dir" (backend fileentry))
f8bd9ac6 525
13ad7457 526(defun vc-hg-dir-printer (info)
7db924c0
DN
527 "Pretty-printer for the vc-dir-fileinfo structure."
528 (let ((extra (vc-dir-fileinfo->extra info)))
13ad7457 529 (vc-default-dir-printer 'Hg info)
7db924c0
DN
530 (when extra
531 (insert (propertize
72169e55 532 (format " (%s %s)"
a464a6c7
SM
533 (pcase (vc-hg-extra-fileinfo->rename-state extra)
534 (`copied "copied from")
535 (`renamed-from "renamed from")
536 (`renamed-to "renamed to"))
72169e55
SS
537 (vc-hg-extra-fileinfo->extra-name extra))
538 'face 'font-lock-comment-face)))))
7db924c0 539
c1b51374 540(defun vc-hg-after-dir-status (update-function)
0d42eb3e 541 (let ((file nil)
72169e55
SS
542 (translation '((?= . up-to-date)
543 (?C . up-to-date)
544 (?A . added)
545 (?R . removed)
546 (?M . edited)
547 (?I . ignored)
548 (?! . missing)
549 (? . copy-rename-line)
550 (?? . unregistered)))
551 (translated nil)
552 (result nil)
553 (last-added nil)
554 (last-line-copy nil))
5ab612e8 555 (goto-char (point-min))
8fcaf22f 556 (while (not (eobp))
72169e55
SS
557 (setq translated (cdr (assoc (char-after) translation)))
558 (setq file
559 (buffer-substring-no-properties (+ (point) 2)
560 (line-end-position)))
561 (cond ((not translated)
562 (setq last-line-copy nil))
563 ((eq translated 'up-to-date)
564 (setq last-line-copy nil))
565 ((eq translated 'copy-rename-line)
566 ;; For copied files the output looks like this:
567 ;; A COPIED_FILE_NAME
568 ;; ORIGINAL_FILE_NAME
569 (setf (nth 2 last-added)
570 (vc-hg-create-extra-fileinfo 'copied file))
571 (setq last-line-copy t))
572 ((and last-line-copy (eq translated 'removed))
573 ;; For renamed files the output looks like this:
574 ;; A NEW_FILE_NAME
575 ;; ORIGINAL_FILE_NAME
576 ;; R ORIGINAL_FILE_NAME
577 ;; We need to adjust the previous entry to not think it is a copy.
578 (setf (vc-hg-extra-fileinfo->rename-state (nth 2 last-added))
579 'renamed-from)
580 (push (list file translated
581 (vc-hg-create-extra-fileinfo
582 'renamed-to (nth 0 last-added))) result)
583 (setq last-line-copy nil))
584 (t
585 (setq last-added (list file translated nil))
586 (push last-added result)
587 (setq last-line-copy nil)))
588 (forward-line))
c1b51374 589 (funcall update-function result)))
5ab612e8 590
c1b51374 591(defun vc-hg-dir-status (dir update-function)
7db924c0 592 (vc-hg-command (current-buffer) 'async dir "status" "-C")
115c0061 593 (vc-exec-after
c1b51374 594 `(vc-hg-after-dir-status (quote ,update-function))))
8fcaf22f 595
0d42eb3e 596(defun vc-hg-dir-status-files (dir files _default-state update-function)
a779ddf0
DN
597 (apply 'vc-hg-command (current-buffer) 'async dir "status" "-C" files)
598 (vc-exec-after
599 `(vc-hg-after-dir-status (quote ,update-function))))
600
13ad7457 601(defun vc-hg-dir-extra-header (name &rest commands)
fe4f8695
SS
602 (concat (propertize name 'face 'font-lock-type-face)
603 (propertize
604 (with-temp-buffer
605 (apply 'vc-hg-command (current-buffer) 0 nil commands)
606 (buffer-substring-no-properties (point-min) (1- (point-max))))
607 'face 'font-lock-variable-name-face)))
608
13ad7457 609(defun vc-hg-dir-extra-headers (dir)
fe4f8695
SS
610 "Generate extra status headers for a Mercurial tree."
611 (let ((default-directory dir))
612 (concat
13ad7457
DN
613 (vc-hg-dir-extra-header "Root : " "root") "\n"
614 (vc-hg-dir-extra-header "Branch : " "id" "-b") "\n"
615 (vc-hg-dir-extra-header "Tags : " "id" "-t") ; "\n"
fe4f8695 616 ;; these change after each commit
13ad7457
DN
617 ;; (vc-hg-dir-extra-header "Local num : " "id" "-n") "\n"
618 ;; (vc-hg-dir-extra-header "Global id : " "id" "-i")
fe4f8695
SS
619 )))
620
31527c56
DN
621(defun vc-hg-log-incoming (buffer remote-location)
622 (vc-hg-command buffer 1 nil "incoming" "-n" (unless (string= remote-location "")
623 remote-location)))
f0230324 624
31527c56
DN
625(defun vc-hg-log-outgoing (buffer remote-location)
626 (vc-hg-command buffer 1 nil "outgoing" "-n" (unless (string= remote-location "")
627 remote-location)))
f0230324 628
004a00f4
DN
629(declare-function log-view-get-marked "log-view" ())
630
f0230324
DN
631;; XXX maybe also add key bindings for these functions.
632(defun vc-hg-push ()
633 (interactive)
634 (let ((marked-list (log-view-get-marked)))
635 (if marked-list
3fb87bf5
SS
636 (apply #'vc-hg-command
637 nil 0 nil
638 "push"
72169e55 639 (apply 'nconc
3fb87bf5
SS
640 (mapcar (lambda (arg) (list "-r" arg)) marked-list)))
641 (error "No log entries selected for push"))))
f0230324 642
8a4e6db8
SS
643(defvar vc-hg-error-regexp-alist nil
644 ;; 'hg pull' does not list modified files, so, for now, the only
645 ;; benefit of `vc-compilation-mode' is that one can get rid of
646 ;; *vc-hg* buffer with 'q' or 'z'.
647 ;; TODO: call 'hg incoming' before pull/merge to get the list of
648 ;; modified files
649 "Value of `compilation-error-regexp-alist' in *vc-hg* buffers.")
650
659114fd 651(defun vc-hg-pull (prompt)
a2b6e5d6
CY
652 "Issue a Mercurial pull command.
653If called interactively with a set of marked Log View buffers,
654call \"hg pull -r REVS\" to pull in the specified revisions REVS.
655
656With a prefix argument or if PROMPT is non-nil, prompt for a
657specific Mercurial pull command. The default is \"hg pull -u\",
658which fetches changesets from the default remote repository and
659then attempts to update the working directory."
659114fd
CY
660 (interactive "P")
661 (let (marked-list)
a2b6e5d6
CY
662 ;; The `vc-hg-pull' command existed before the `pull' VC action
663 ;; was implemented. Keep it for backward compatibility.
659114fd
CY
664 (if (and (called-interactively-p 'interactive)
665 (setq marked-list (log-view-get-marked)))
666 (apply #'vc-hg-command
667 nil 0 nil
668 "pull"
669 (apply 'nconc
670 (mapcar (lambda (arg) (list "-r" arg))
671 marked-list)))
672 (let* ((root (vc-hg-root default-directory))
673 (buffer (format "*vc-hg : %s*" (expand-file-name root)))
674 (command "pull")
5ceaac0c 675 (hg-program vc-hg-program)
a2b6e5d6
CY
676 ;; Fixme: before updating the working copy to the latest
677 ;; state, should check if it's visiting an old revision.
659114fd
CY
678 (args '("-u")))
679 ;; If necessary, prompt for the exact command.
680 (when prompt
681 (setq args (split-string
5ceaac0c
GM
682 (read-shell-command "Run Hg (like this): "
683 (format "%s pull -u" hg-program)
659114fd
CY
684 'vc-hg-history)
685 " " t))
686 (setq hg-program (car args)
687 command (cadr args)
688 args (cddr args)))
689 (apply 'vc-do-async-command buffer root hg-program
a2b6e5d6 690 command args)
8a4e6db8 691 (with-current-buffer buffer (vc-exec-after '(vc-compilation-mode 'hg)))
a2b6e5d6 692 (vc-set-async-update buffer)))))
659114fd
CY
693
694(defun vc-hg-merge-branch ()
a2b6e5d6
CY
695 "Merge incoming changes into the current working directory.
696This runs the command \"hg merge\"."
659114fd
CY
697 (let* ((root (vc-hg-root default-directory))
698 (buffer (format "*vc-hg : %s*" (expand-file-name root))))
5ceaac0c 699 (apply 'vc-do-async-command buffer root vc-hg-program '("merge"))
8a4e6db8 700 (with-current-buffer buffer (vc-exec-after '(vc-compilation-mode 'hg)))
a2b6e5d6 701 (vc-set-async-update buffer)))
f0230324 702
61223448
DN
703;;; Internal functions
704
8cdd17b4 705(defun vc-hg-command (buffer okstatus file-or-list &rest flags)
61223448 706 "A wrapper around `vc-do-command' for use in vc-hg.el.
219ea611
GM
707This function differs from vc-do-command in that it invokes
708`vc-hg-program', and passes `vc-hg-global-switches' to it before FLAGS."
88bf1bec 709 (apply 'vc-do-command (or buffer "*vc*") okstatus vc-hg-program file-or-list
61223448
DN
710 (if (stringp vc-hg-global-switches)
711 (cons vc-hg-global-switches flags)
712 (append vc-hg-global-switches
713 flags))))
714
a07e665b
DN
715(defun vc-hg-root (file)
716 (vc-find-root file ".hg"))
717
61223448
DN
718(provide 'vc-hg)
719
248c6645 720;;; vc-hg.el ends here