Add 2010 to copyright years.
[bpt/emacs.git] / lisp / 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
248c6645 6;; Keywords: 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
DN
170 (let ((process-environment
171 ;; Avoid localization of messages so we can parse the output.
172 (append (list "TERM=dumb" "LANGUAGE=C" "HGRC=") process-environment)))
173
174 (process-file
cc63d28f 175 "hg" nil t nil
662c5698 176 "status" "-A" (file-relative-name file)))
72169e55
SS
177 ;; Some problem happened. E.g. We can't find an `hg'
178 ;; executable.
179 (error nil)))))))
b33ac3b7 180 (when (eq 0 status)
72169e55
SS
181 (when (null (string-match ".*: No such file or directory$" out))
182 (let ((state (aref out 0)))
183 (cond
184 ((eq state ?=) 'up-to-date)
185 ((eq state ?A) 'added)
186 ((eq state ?M) 'edited)
187 ((eq state ?I) 'ignored)
188 ((eq state ?R) 'removed)
189 ((eq state ?!) 'missing)
190 ((eq state ??) 'unregistered)
191 ((eq state ?C) 'up-to-date) ;; Older mercurials use this
192 (t 'up-to-date)))))))
a6ea7ffc 193
ac3f4c6f
ER
194(defun vc-hg-working-revision (file)
195 "Hg-specific version of `vc-working-revision'."
62754d29 196 (let*
b33ac3b7 197 ((status nil)
cc63d28f 198 (default-directory (file-name-directory file))
b33ac3b7 199 (out
72169e55
SS
200 (with-output-to-string
201 (with-current-buffer
202 standard-output
203 (setq status
204 (condition-case nil
110de3bb
DN
205 (let ((process-environment
206 ;; Avoid localization of messages so we can parse the output.
207 (append (list "TERM=dumb" "LANGUAGE=C" "HGRC=")
208 process-environment)))
209 ;; Ignore all errors.
210 (process-file
211 "hg" nil t nil
ac4a9529 212 "parent" "--template" "{rev}" (file-relative-name file)))
72169e55
SS
213 ;; Some problem happened. E.g. We can't find an `hg'
214 ;; executable.
215 (error nil)))))))
ac4a9529 216 (when (eq 0 status) out)))
61223448 217
61223448
DN
218;;; History functions
219
02532fbc
SS
220(defcustom vc-hg-log-switches nil
221 "String or list of strings specifying switches for hg log under VC."
222 :type '(choice (const :tag "None" nil)
223 (string :tag "Argument String")
224 (repeat :tag "Argument List" :value ("") string))
225 :group 'vc-hg)
226
86b5e14c 227(defun vc-hg-print-log (files buffer &optional shortlog start-revision limit)
8cdd17b4 228 "Get change log associated with FILES."
7c1912af
DN
229 ;; `vc-do-command' creates the buffer, but we need it before running
230 ;; the command.
231 (vc-setup-buffer buffer)
232 ;; If the buffer exists from a previous invocation it might be
233 ;; read-only.
234 (let ((inhibit-read-only t))
6653c6b7
DN
235 (with-current-buffer
236 buffer
32ba3abc 237 (apply 'vc-hg-command buffer 0 files "log"
6616006b 238 (append
662c5698 239 (when start-revision (list (format "-r%s:" start-revision)))
6616006b
DN
240 (when limit (list "-l" (format "%s" limit)))
241 (when shortlog '("--style" "compact"))
242 vc-hg-log-switches)))))
61223448 243
d797e643
DN
244(defvar log-view-message-re)
245(defvar log-view-file-re)
246(defvar log-view-font-lock-keywords)
6653c6b7 247(defvar log-view-per-file-logs)
32ba3abc 248(defvar vc-short-log)
d797e643 249
4211679b 250(define-derived-mode vc-hg-log-view-mode log-view-mode "Hg-Log-View"
f0230324 251 (require 'add-log) ;; we need the add-log faces
6653c6b7
DN
252 (set (make-local-variable 'log-view-file-re) "\\`a\\`")
253 (set (make-local-variable 'log-view-per-file-logs) nil)
d797e643 254 (set (make-local-variable 'log-view-message-re)
32ba3abc 255 (if vc-short-log
72169e55
SS
256 "^\\([0-9]+\\)\\(?:\\[.*\\]\\)? +\\([0-9a-z]\\{12\\}\\) +\\(\\(?:[0-9]+\\)-\\(?:[0-9]+\\)-\\(?:[0-9]+\\) \\(?:[0-9]+\\):\\(?:[0-9]+\\) \\(?:[-+0-9]+\\)\\) +\\(.*\\)$"
257 "^changeset:[ \t]*\\([0-9]+\\):\\(.+\\)"))
d797e643 258 (set (make-local-variable 'log-view-font-lock-keywords)
32ba3abc 259 (if vc-short-log
72169e55
SS
260 (append `((,log-view-message-re
261 (1 'log-view-message-face)
262 (2 'log-view-message-face)
263 (3 'change-log-date)
264 (4 'change-log-name))))
11a4edc2 265 (append
72169e55
SS
266 log-view-font-lock-keywords
267 '(
268 ;; Handle the case:
269 ;; user: FirstName LastName <foo@bar>
270 ("^user:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]"
271 (1 'change-log-name)
272 (2 'change-log-email))
273 ;; Handle the cases:
274 ;; user: foo@bar
275 ;; and
276 ;; user: foo
277 ("^user:[ \t]+\\([A-Za-z0-9_.+-]+\\(?:@[A-Za-z0-9_.-]+\\)?\\)"
278 (1 'change-log-email))
279 ("^date: \\(.+\\)" (1 'change-log-date))
280 ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message)))))))
d797e643 281
8cdd17b4 282(defun vc-hg-diff (files &optional oldvers newvers buffer)
5b5afd50 283 "Get a difference report using hg between two revisions of FILES."
6653c6b7 284 (let* ((firstfile (car files))
72169e55 285 (working (and firstfile (vc-working-revision firstfile))))
ec4149ff
DN
286 (when (and (equal oldvers working) (not newvers))
287 (setq oldvers nil))
288 (when (and (not oldvers) newvers)
289 (setq oldvers working))
efa3639b 290 (apply #'vc-hg-command (or buffer "*vc-diff*") nil files "diff"
72169e55
SS
291 (append
292 (vc-switches 'hg 'diff)
293 (when oldvers
294 (if newvers
295 (list "-r" oldvers "-r" newvers)
296 (list "-r" oldvers)))))))
cdaf01cc 297
87d1a48e
SM
298(defun vc-hg-revision-table (files)
299 (let ((default-directory (file-name-directory (car files))))
34b7fb85 300 (with-temp-buffer
004a00f4 301 (vc-hg-command t nil files "log" "--template" "{rev} ")
62754d29 302 (split-string
34b7fb85
DN
303 (buffer-substring-no-properties (point-min) (point-max))))))
304
6772c8e1 305;; Modeled after the similar function in vc-cvs.el
87d1a48e
SM
306(defun vc-hg-revision-completion-table (files)
307 (lexical-let ((files files)
eff23ff3
DN
308 table)
309 (setq table (lazy-completion-table
87d1a48e 310 table (lambda () (vc-hg-revision-table files))))
eff23ff3 311 table))
34b7fb85 312
5b5afd50 313(defun vc-hg-annotate-command (file buffer &optional revision)
cdaf01cc 314 "Execute \"hg annotate\" on FILE, inserting the contents in BUFFER.
5b5afd50 315Optional arg REVISION is a revision to annotate from."
d1e4c403
DN
316 (vc-hg-command buffer 0 file "annotate" "-d" "-n" "--follow"
317 (when revision (concat "-r" revision))))
cdaf01cc 318
f8bd9ac6 319(declare-function vc-annotate-convert-time "vc-annotate" (time))
cdaf01cc 320
11a4edc2
SM
321;; The format for one line output by "hg annotate -d -n" looks like this:
322;;215 Wed Jun 20 21:22:58 2007 -0700: CONTENTS
323;; i.e: VERSION_NUMBER DATE: CONTENTS
4064ff25
DN
324;; If the user has set the "--follow" option, the output looks like:
325;;215 Wed Jun 20 21:22:58 2007 -0700 foo.c: CONTENTS
326;; i.e. VERSION_NUMBER DATE FILENAME: CONTENTS
327(defconst vc-hg-annotate-re
d1e4c403 328 "^[ \t]*\\([0-9]+\\) \\(.\\{30\\}\\)\\(?:\\(: \\)\\|\\(?: +\\(.+\\): \\)\\)")
cdaf01cc
DN
329
330(defun vc-hg-annotate-time ()
331 (when (looking-at vc-hg-annotate-re)
332 (goto-char (match-end 0))
11a4edc2 333 (vc-annotate-convert-time
cdaf01cc
DN
334 (date-to-time (match-string-no-properties 2)))))
335
336(defun vc-hg-annotate-extract-revision-at-line ()
337 (save-excursion
338 (beginning-of-line)
d1e4c403
DN
339 (when (looking-at vc-hg-annotate-re)
340 (if (match-beginning 3)
341 (match-string-no-properties 1)
342 (cons (match-string-no-properties 1)
343 (expand-file-name (match-string-no-properties 4)))))))
cdaf01cc 344
5b5afd50 345(defun vc-hg-previous-revision (file rev)
cdaf01cc
DN
346 (let ((newrev (1- (string-to-number rev))))
347 (when (>= newrev 0)
348 (number-to-string newrev))))
61223448 349
5b5afd50 350(defun vc-hg-next-revision (file rev)
a07e665b 351 (let ((newrev (1+ (string-to-number rev)))
72169e55
SS
352 (tip-revision
353 (with-temp-buffer
354 (vc-hg-command t 0 nil "tip")
355 (goto-char (point-min))
356 (re-search-forward "^changeset:[ \t]*\\([0-9]+\\):")
357 (string-to-number (match-string-no-properties 1)))))
5b5afd50
ER
358 ;; We don't want to exceed the maximum possible revision number, ie
359 ;; the tip revision.
360 (when (<= newrev tip-revision)
a07e665b
DN
361 (number-to-string newrev))))
362
6772c8e1 363;; Modeled after the similar function in vc-bzr.el
a07e665b
DN
364(defun vc-hg-delete-file (file)
365 "Delete FILE and delete it in the hg repository."
366 (condition-case ()
367 (delete-file file)
368 (file-error nil))
34b7fb85 369 (vc-hg-command nil 0 file "remove" "--after" "--force"))
a07e665b 370
6772c8e1 371;; Modeled after the similar function in vc-bzr.el
a07e665b
DN
372(defun vc-hg-rename-file (old new)
373 "Rename file from OLD to NEW using `hg mv'."
bfd57731 374 (vc-hg-command nil 0 new "mv" old))
a07e665b 375
8cdd17b4
ER
376(defun vc-hg-register (files &optional rev comment)
377 "Register FILES under hg.
248c6645
DN
378REV is ignored.
379COMMENT is ignored."
34b7fb85 380 (vc-hg-command nil 0 files "add"))
8cdd17b4
ER
381
382(defun vc-hg-create-repo ()
383 "Create a new Mercurial repository."
34b7fb85 384 (vc-hg-command nil 0 nil "init"))
248c6645 385
a07e665b
DN
386(defalias 'vc-hg-responsible-p 'vc-hg-root)
387
6772c8e1 388;; Modeled after the similar function in vc-bzr.el
a07e665b
DN
389(defun vc-hg-could-register (file)
390 "Return non-nil if FILE could be registered under hg."
391 (and (vc-hg-responsible-p file) ; shortcut
392 (condition-case ()
393 (with-temp-buffer
394 (vc-hg-command t nil file "add" "--dry-run"))
395 ;; The command succeeds with no output if file is
396 ;; registered.
397 (error))))
398
4f10da1c 399;; FIXME: This would remove the file. Is that correct?
a07e665b
DN
400;; (defun vc-hg-unregister (file)
401;; "Unregister FILE from hg."
402;; (vc-hg-command nil nil file "remove"))
403
8cdd17b4 404(defun vc-hg-checkin (files rev comment)
4211679b 405 "Hg-specific version of `vc-backend-checkin'.
cdaf01cc 406REV is ignored."
34b7fb85 407 (vc-hg-command nil 0 files "commit" "-m" comment))
cdaf01cc 408
ac3f4c6f 409(defun vc-hg-find-revision (file rev buffer)
248c6645
DN
410 (let ((coding-system-for-read 'binary)
411 (coding-system-for-write 'binary))
248c6645 412 (if rev
72169e55 413 (vc-hg-command buffer 0 file "cat" "-r" rev)
34b7fb85 414 (vc-hg-command buffer 0 file "cat"))))
a07e665b 415
6772c8e1 416;; Modeled after the similar function in vc-bzr.el
a6ea7ffc
DN
417(defun vc-hg-checkout (file &optional editable rev)
418 "Retrieve a revision of FILE.
419EDITABLE is ignored.
420REV is the revision to check out into WORKFILE."
421 (let ((coding-system-for-read 'binary)
422 (coding-system-for-write 'binary))
423 (with-current-buffer (or (get-file-buffer file) (current-buffer))
424 (if rev
34b7fb85
DN
425 (vc-hg-command t 0 file "cat" "-r" rev)
426 (vc-hg-command t 0 file "cat")))))
248c6645 427
6772c8e1 428;; Modeled after the similar function in vc-bzr.el
a272e668
DN
429(defun vc-hg-workfile-unchanged-p (file)
430 (eq 'up-to-date (vc-hg-state file)))
431
6772c8e1 432;; Modeled after the similar function in vc-bzr.el
b33ac3b7
DN
433(defun vc-hg-revert (file &optional contents-done)
434 (unless contents-done
34b7fb85 435 (with-temp-buffer (vc-hg-command t 0 file "revert"))))
b33ac3b7 436
f0230324
DN
437;;; Hg specific functionality.
438
f0230324
DN
439(defvar vc-hg-extra-menu-map
440 (let ((map (make-sparse-keymap)))
441 (define-key map [incoming] '(menu-item "Show incoming" vc-hg-incoming))
442 (define-key map [outgoing] '(menu-item "Show outgoing" vc-hg-outgoing))
443 map))
444
445(defun vc-hg-extra-menu () vc-hg-extra-menu-map)
446
25a4ea6d 447(defun vc-hg-extra-status-menu () vc-hg-extra-menu-map)
6656ecaa 448
4a827e0a 449(defvar log-view-vc-backend)
f0230324 450
4a827e0a
DN
451(define-derived-mode vc-hg-outgoing-mode vc-hg-log-view-mode "Hg-Outgoing"
452 "Mode for browsing Hg outgoing changes."
453 (set (make-local-variable 'log-view-vc-backend) 'Hg))
454
455(define-derived-mode vc-hg-incoming-mode vc-hg-log-view-mode "Hg-Incoming"
456 "Mode for browsing Hg incoming changes."
457 (set (make-local-variable 'log-view-vc-backend) 'Hg))
f0230324 458
7db924c0
DN
459(defstruct (vc-hg-extra-fileinfo
460 (:copier nil)
461 (:constructor vc-hg-create-extra-fileinfo (rename-state extra-name))
462 (:conc-name vc-hg-extra-fileinfo->))
463 rename-state ;; rename or copy state
fe4f8695 464 extra-name) ;; original name for copies and rename targets, new name for
7db924c0 465
13ad7457 466(declare-function vc-default-dir-printer "vc-dir" (backend fileentry))
f8bd9ac6 467
13ad7457 468(defun vc-hg-dir-printer (info)
7db924c0
DN
469 "Pretty-printer for the vc-dir-fileinfo structure."
470 (let ((extra (vc-dir-fileinfo->extra info)))
13ad7457 471 (vc-default-dir-printer 'Hg info)
7db924c0
DN
472 (when extra
473 (insert (propertize
72169e55
SS
474 (format " (%s %s)"
475 (case (vc-hg-extra-fileinfo->rename-state extra)
476 ('copied "copied from")
477 ('renamed-from "renamed from")
478 ('renamed-to "renamed to"))
479 (vc-hg-extra-fileinfo->extra-name extra))
480 'face 'font-lock-comment-face)))))
7db924c0 481
c1b51374 482(defun vc-hg-after-dir-status (update-function)
5ab612e8 483 (let ((status-char nil)
72169e55
SS
484 (file nil)
485 (translation '((?= . up-to-date)
486 (?C . up-to-date)
487 (?A . added)
488 (?R . removed)
489 (?M . edited)
490 (?I . ignored)
491 (?! . missing)
492 (? . copy-rename-line)
493 (?? . unregistered)))
494 (translated nil)
495 (result nil)
496 (last-added nil)
497 (last-line-copy nil))
5ab612e8 498 (goto-char (point-min))
8fcaf22f 499 (while (not (eobp))
72169e55
SS
500 (setq translated (cdr (assoc (char-after) translation)))
501 (setq file
502 (buffer-substring-no-properties (+ (point) 2)
503 (line-end-position)))
504 (cond ((not translated)
505 (setq last-line-copy nil))
506 ((eq translated 'up-to-date)
507 (setq last-line-copy nil))
508 ((eq translated 'copy-rename-line)
509 ;; For copied files the output looks like this:
510 ;; A COPIED_FILE_NAME
511 ;; ORIGINAL_FILE_NAME
512 (setf (nth 2 last-added)
513 (vc-hg-create-extra-fileinfo 'copied file))
514 (setq last-line-copy t))
515 ((and last-line-copy (eq translated 'removed))
516 ;; For renamed files the output looks like this:
517 ;; A NEW_FILE_NAME
518 ;; ORIGINAL_FILE_NAME
519 ;; R ORIGINAL_FILE_NAME
520 ;; We need to adjust the previous entry to not think it is a copy.
521 (setf (vc-hg-extra-fileinfo->rename-state (nth 2 last-added))
522 'renamed-from)
523 (push (list file translated
524 (vc-hg-create-extra-fileinfo
525 'renamed-to (nth 0 last-added))) result)
526 (setq last-line-copy nil))
527 (t
528 (setq last-added (list file translated nil))
529 (push last-added result)
530 (setq last-line-copy nil)))
531 (forward-line))
c1b51374 532 (funcall update-function result)))
5ab612e8 533
c1b51374 534(defun vc-hg-dir-status (dir update-function)
7db924c0 535 (vc-hg-command (current-buffer) 'async dir "status" "-C")
115c0061 536 (vc-exec-after
c1b51374 537 `(vc-hg-after-dir-status (quote ,update-function))))
8fcaf22f 538
a779ddf0
DN
539(defun vc-hg-dir-status-files (dir files default-state update-function)
540 (apply 'vc-hg-command (current-buffer) 'async dir "status" "-C" files)
541 (vc-exec-after
542 `(vc-hg-after-dir-status (quote ,update-function))))
543
13ad7457 544(defun vc-hg-dir-extra-header (name &rest commands)
fe4f8695
SS
545 (concat (propertize name 'face 'font-lock-type-face)
546 (propertize
547 (with-temp-buffer
548 (apply 'vc-hg-command (current-buffer) 0 nil commands)
549 (buffer-substring-no-properties (point-min) (1- (point-max))))
550 'face 'font-lock-variable-name-face)))
551
13ad7457 552(defun vc-hg-dir-extra-headers (dir)
fe4f8695
SS
553 "Generate extra status headers for a Mercurial tree."
554 (let ((default-directory dir))
555 (concat
13ad7457
DN
556 (vc-hg-dir-extra-header "Root : " "root") "\n"
557 (vc-hg-dir-extra-header "Branch : " "id" "-b") "\n"
558 (vc-hg-dir-extra-header "Tags : " "id" "-t") ; "\n"
fe4f8695 559 ;; these change after each commit
13ad7457
DN
560 ;; (vc-hg-dir-extra-header "Local num : " "id" "-n") "\n"
561 ;; (vc-hg-dir-extra-header "Global id : " "id" "-i")
fe4f8695
SS
562 )))
563
4f10da1c 564;; FIXME: this adds another top level menu, instead figure out how to
f0230324
DN
565;; replace the Log-View menu.
566(easy-menu-define log-view-mode-menu vc-hg-outgoing-mode-map
567 "Hg-outgoing Display Menu"
568 `("Hg-outgoing"
569 ["Push selected" vc-hg-push]))
570
571(easy-menu-define log-view-mode-menu vc-hg-incoming-mode-map
572 "Hg-incoming Display Menu"
573 `("Hg-incoming"
574 ["Pull selected" vc-hg-pull]))
575
576(defun vc-hg-outgoing ()
577 (interactive)
4a827e0a
DN
578 (let ((bname "*Hg outgoing*")
579 (vc-short-log nil))
71630ffe 580 (vc-hg-command bname 1 nil "outgoing" "-n")
f0230324
DN
581 (pop-to-buffer bname)
582 (vc-hg-outgoing-mode)))
583
584(defun vc-hg-incoming ()
585 (interactive)
4a827e0a
DN
586 (let ((bname "*Hg incoming*")
587 (vc-short-log nil))
71630ffe 588 (vc-hg-command bname 0 nil "incoming" "-n")
f0230324
DN
589 (pop-to-buffer bname)
590 (vc-hg-incoming-mode)))
591
004a00f4
DN
592(declare-function log-view-get-marked "log-view" ())
593
f0230324
DN
594;; XXX maybe also add key bindings for these functions.
595(defun vc-hg-push ()
596 (interactive)
597 (let ((marked-list (log-view-get-marked)))
598 (if marked-list
72169e55
SS
599 (vc-hg-command
600 nil 0 nil
601 (cons "push"
602 (apply 'nconc
603 (mapcar (lambda (arg) (list "-r" arg)) marked-list))))
604 (error "No log entries selected for push"))))
f0230324
DN
605
606(defun vc-hg-pull ()
607 (interactive)
608 (let ((marked-list (log-view-get-marked)))
609 (if marked-list
72169e55
SS
610 (vc-hg-command
611 nil 0 nil
612 (cons "pull"
613 (apply 'nconc
614 (mapcar (lambda (arg) (list "-r" arg)) marked-list))))
f0230324
DN
615 (error "No log entries selected for pull"))))
616
61223448
DN
617;;; Internal functions
618
8cdd17b4 619(defun vc-hg-command (buffer okstatus file-or-list &rest flags)
61223448 620 "A wrapper around `vc-do-command' for use in vc-hg.el.
c0203c86
DN
621The difference to vc-do-command is that this function always invokes `hg',
622and that it passes `vc-hg-global-switches' to it before FLAGS."
623 (apply 'vc-do-command (or buffer "*vc*") okstatus "hg" file-or-list
61223448
DN
624 (if (stringp vc-hg-global-switches)
625 (cons vc-hg-global-switches flags)
626 (append vc-hg-global-switches
627 flags))))
628
a07e665b
DN
629(defun vc-hg-root (file)
630 (vc-find-root file ".hg"))
631
61223448
DN
632(provide 'vc-hg)
633
eaaa2b09 634;; arch-tag: bd094dc5-715a-434f-a331-37b9fb7cd954
248c6645 635;;; vc-hg.el ends here