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