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