Merge from emacs-24; up to 2012-05-07T21:26:08Z!rgm@gnu.org
[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
acaf905b 3;; Copyright (C) 2006-2012 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
96;; - find-file-hook () PROBABLY NOT NEEDED
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
387 (vc-hg-command t 0 nil "tip")
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
6772c8e1 467;; Modeled after the similar function in vc-bzr.el
a272e668
DN
468(defun vc-hg-workfile-unchanged-p (file)
469 (eq 'up-to-date (vc-hg-state file)))
470
6772c8e1 471;; Modeled after the similar function in vc-bzr.el
b33ac3b7
DN
472(defun vc-hg-revert (file &optional contents-done)
473 (unless contents-done
34b7fb85 474 (with-temp-buffer (vc-hg-command t 0 file "revert"))))
b33ac3b7 475
f0230324
DN
476;;; Hg specific functionality.
477
f0230324
DN
478(defvar vc-hg-extra-menu-map
479 (let ((map (make-sparse-keymap)))
f0230324
DN
480 map))
481
482(defun vc-hg-extra-menu () vc-hg-extra-menu-map)
483
25a4ea6d 484(defun vc-hg-extra-status-menu () vc-hg-extra-menu-map)
6656ecaa 485
4a827e0a 486(defvar log-view-vc-backend)
f0230324 487
a464a6c7 488(cl-defstruct (vc-hg-extra-fileinfo
7db924c0
DN
489 (:copier nil)
490 (:constructor vc-hg-create-extra-fileinfo (rename-state extra-name))
491 (:conc-name vc-hg-extra-fileinfo->))
492 rename-state ;; rename or copy state
fe4f8695 493 extra-name) ;; original name for copies and rename targets, new name for
7db924c0 494
13ad7457 495(declare-function vc-default-dir-printer "vc-dir" (backend fileentry))
f8bd9ac6 496
13ad7457 497(defun vc-hg-dir-printer (info)
7db924c0
DN
498 "Pretty-printer for the vc-dir-fileinfo structure."
499 (let ((extra (vc-dir-fileinfo->extra info)))
13ad7457 500 (vc-default-dir-printer 'Hg info)
7db924c0
DN
501 (when extra
502 (insert (propertize
72169e55 503 (format " (%s %s)"
a464a6c7
SM
504 (pcase (vc-hg-extra-fileinfo->rename-state extra)
505 (`copied "copied from")
506 (`renamed-from "renamed from")
507 (`renamed-to "renamed to"))
72169e55
SS
508 (vc-hg-extra-fileinfo->extra-name extra))
509 'face 'font-lock-comment-face)))))
7db924c0 510
c1b51374 511(defun vc-hg-after-dir-status (update-function)
0d42eb3e 512 (let ((file nil)
72169e55
SS
513 (translation '((?= . up-to-date)
514 (?C . up-to-date)
515 (?A . added)
516 (?R . removed)
517 (?M . edited)
518 (?I . ignored)
519 (?! . missing)
520 (? . copy-rename-line)
521 (?? . unregistered)))
522 (translated nil)
523 (result nil)
524 (last-added nil)
525 (last-line-copy nil))
5ab612e8 526 (goto-char (point-min))
8fcaf22f 527 (while (not (eobp))
72169e55
SS
528 (setq translated (cdr (assoc (char-after) translation)))
529 (setq file
530 (buffer-substring-no-properties (+ (point) 2)
531 (line-end-position)))
532 (cond ((not translated)
533 (setq last-line-copy nil))
534 ((eq translated 'up-to-date)
535 (setq last-line-copy nil))
536 ((eq translated 'copy-rename-line)
537 ;; For copied files the output looks like this:
538 ;; A COPIED_FILE_NAME
539 ;; ORIGINAL_FILE_NAME
540 (setf (nth 2 last-added)
541 (vc-hg-create-extra-fileinfo 'copied file))
542 (setq last-line-copy t))
543 ((and last-line-copy (eq translated 'removed))
544 ;; For renamed files the output looks like this:
545 ;; A NEW_FILE_NAME
546 ;; ORIGINAL_FILE_NAME
547 ;; R ORIGINAL_FILE_NAME
548 ;; We need to adjust the previous entry to not think it is a copy.
549 (setf (vc-hg-extra-fileinfo->rename-state (nth 2 last-added))
550 'renamed-from)
551 (push (list file translated
552 (vc-hg-create-extra-fileinfo
553 'renamed-to (nth 0 last-added))) result)
554 (setq last-line-copy nil))
555 (t
556 (setq last-added (list file translated nil))
557 (push last-added result)
558 (setq last-line-copy nil)))
559 (forward-line))
c1b51374 560 (funcall update-function result)))
5ab612e8 561
c1b51374 562(defun vc-hg-dir-status (dir update-function)
7db924c0 563 (vc-hg-command (current-buffer) 'async dir "status" "-C")
115c0061 564 (vc-exec-after
c1b51374 565 `(vc-hg-after-dir-status (quote ,update-function))))
8fcaf22f 566
0d42eb3e 567(defun vc-hg-dir-status-files (dir files _default-state update-function)
a779ddf0
DN
568 (apply 'vc-hg-command (current-buffer) 'async dir "status" "-C" files)
569 (vc-exec-after
570 `(vc-hg-after-dir-status (quote ,update-function))))
571
13ad7457 572(defun vc-hg-dir-extra-header (name &rest commands)
fe4f8695
SS
573 (concat (propertize name 'face 'font-lock-type-face)
574 (propertize
575 (with-temp-buffer
576 (apply 'vc-hg-command (current-buffer) 0 nil commands)
577 (buffer-substring-no-properties (point-min) (1- (point-max))))
578 'face 'font-lock-variable-name-face)))
579
13ad7457 580(defun vc-hg-dir-extra-headers (dir)
fe4f8695
SS
581 "Generate extra status headers for a Mercurial tree."
582 (let ((default-directory dir))
583 (concat
13ad7457
DN
584 (vc-hg-dir-extra-header "Root : " "root") "\n"
585 (vc-hg-dir-extra-header "Branch : " "id" "-b") "\n"
586 (vc-hg-dir-extra-header "Tags : " "id" "-t") ; "\n"
fe4f8695 587 ;; these change after each commit
13ad7457
DN
588 ;; (vc-hg-dir-extra-header "Local num : " "id" "-n") "\n"
589 ;; (vc-hg-dir-extra-header "Global id : " "id" "-i")
fe4f8695
SS
590 )))
591
31527c56
DN
592(defun vc-hg-log-incoming (buffer remote-location)
593 (vc-hg-command buffer 1 nil "incoming" "-n" (unless (string= remote-location "")
594 remote-location)))
f0230324 595
31527c56
DN
596(defun vc-hg-log-outgoing (buffer remote-location)
597 (vc-hg-command buffer 1 nil "outgoing" "-n" (unless (string= remote-location "")
598 remote-location)))
f0230324 599
004a00f4
DN
600(declare-function log-view-get-marked "log-view" ())
601
f0230324
DN
602;; XXX maybe also add key bindings for these functions.
603(defun vc-hg-push ()
604 (interactive)
605 (let ((marked-list (log-view-get-marked)))
606 (if marked-list
3fb87bf5
SS
607 (apply #'vc-hg-command
608 nil 0 nil
609 "push"
72169e55 610 (apply 'nconc
3fb87bf5
SS
611 (mapcar (lambda (arg) (list "-r" arg)) marked-list)))
612 (error "No log entries selected for push"))))
f0230324 613
8a4e6db8
SS
614(defvar vc-hg-error-regexp-alist nil
615 ;; 'hg pull' does not list modified files, so, for now, the only
616 ;; benefit of `vc-compilation-mode' is that one can get rid of
617 ;; *vc-hg* buffer with 'q' or 'z'.
618 ;; TODO: call 'hg incoming' before pull/merge to get the list of
619 ;; modified files
620 "Value of `compilation-error-regexp-alist' in *vc-hg* buffers.")
621
659114fd 622(defun vc-hg-pull (prompt)
a2b6e5d6
CY
623 "Issue a Mercurial pull command.
624If called interactively with a set of marked Log View buffers,
625call \"hg pull -r REVS\" to pull in the specified revisions REVS.
626
627With a prefix argument or if PROMPT is non-nil, prompt for a
628specific Mercurial pull command. The default is \"hg pull -u\",
629which fetches changesets from the default remote repository and
630then attempts to update the working directory."
659114fd
CY
631 (interactive "P")
632 (let (marked-list)
a2b6e5d6
CY
633 ;; The `vc-hg-pull' command existed before the `pull' VC action
634 ;; was implemented. Keep it for backward compatibility.
659114fd
CY
635 (if (and (called-interactively-p 'interactive)
636 (setq marked-list (log-view-get-marked)))
637 (apply #'vc-hg-command
638 nil 0 nil
639 "pull"
640 (apply 'nconc
641 (mapcar (lambda (arg) (list "-r" arg))
642 marked-list)))
643 (let* ((root (vc-hg-root default-directory))
644 (buffer (format "*vc-hg : %s*" (expand-file-name root)))
645 (command "pull")
5ceaac0c 646 (hg-program vc-hg-program)
a2b6e5d6
CY
647 ;; Fixme: before updating the working copy to the latest
648 ;; state, should check if it's visiting an old revision.
659114fd
CY
649 (args '("-u")))
650 ;; If necessary, prompt for the exact command.
651 (when prompt
652 (setq args (split-string
5ceaac0c
GM
653 (read-shell-command "Run Hg (like this): "
654 (format "%s pull -u" hg-program)
659114fd
CY
655 'vc-hg-history)
656 " " t))
657 (setq hg-program (car args)
658 command (cadr args)
659 args (cddr args)))
660 (apply 'vc-do-async-command buffer root hg-program
a2b6e5d6 661 command args)
8a4e6db8 662 (with-current-buffer buffer (vc-exec-after '(vc-compilation-mode 'hg)))
a2b6e5d6 663 (vc-set-async-update buffer)))))
659114fd
CY
664
665(defun vc-hg-merge-branch ()
a2b6e5d6
CY
666 "Merge incoming changes into the current working directory.
667This runs the command \"hg merge\"."
659114fd
CY
668 (let* ((root (vc-hg-root default-directory))
669 (buffer (format "*vc-hg : %s*" (expand-file-name root))))
5ceaac0c 670 (apply 'vc-do-async-command buffer root vc-hg-program '("merge"))
8a4e6db8 671 (with-current-buffer buffer (vc-exec-after '(vc-compilation-mode 'hg)))
a2b6e5d6 672 (vc-set-async-update buffer)))
f0230324 673
61223448
DN
674;;; Internal functions
675
8cdd17b4 676(defun vc-hg-command (buffer okstatus file-or-list &rest flags)
61223448 677 "A wrapper around `vc-do-command' for use in vc-hg.el.
219ea611
GM
678This function differs from vc-do-command in that it invokes
679`vc-hg-program', and passes `vc-hg-global-switches' to it before FLAGS."
88bf1bec 680 (apply 'vc-do-command (or buffer "*vc*") okstatus vc-hg-program file-or-list
61223448
DN
681 (if (stringp vc-hg-global-switches)
682 (cons vc-hg-global-switches flags)
683 (append vc-hg-global-switches
684 flags))))
685
a07e665b
DN
686(defun vc-hg-root (file)
687 (vc-find-root file ".hg"))
688
61223448
DN
689(provide 'vc-hg)
690
248c6645 691;;; vc-hg.el ends here