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