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