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