##// END OF EJS Templates
Emacs: document existing functions.
Bryan O'Sullivan -
r996:5ed56657 default
parent child Browse files
Show More
@@ -1,666 +1,696
1 1 ;;; mercurial.el --- Emacs support for the Mercurial distributed SCM
2 2
3 3 ;; Copyright (C) 2005 Bryan O'Sullivan
4 4
5 5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6 6
7 7 ;; $Id$
8 8
9 9 ;; mercurial.el is free software; you can redistribute it and/or
10 10 ;; modify it under the terms of version 2 of the GNU General Public
11 11 ;; License as published by the Free Software Foundation.
12 12
13 13 ;; mercurial.el is distributed in the hope that it will be useful, but
14 14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 16 ;; General Public License for more details.
17 17
18 18 ;; You should have received a copy of the GNU General Public License
19 19 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
20 20 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
21 21 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 22
23 23 ;;; Commentary:
24 24
25 25 ;; This mode builds upon Emacs's VC mode to provide flexible
26 26 ;; integration with the Mercurial distributed SCM tool.
27 27
28 28 ;; To get going as quickly as possible, load mercurial.el into Emacs and
29 29 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
30 30 ;; usage overview.
31 31
32 32 ;; Much of the inspiration for mercurial.el comes from Rajesh
33 33 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
34 34 ;; job for the commercial Perforce SCM product. In fact, substantial
35 35 ;; chunks of code are adapted from p4.el.
36 36
37 37 ;; This code has been developed under XEmacs 21.5, and may will not
38 38 ;; work as well under GNU Emacs (albeit tested under 21.2). Patches
39 39 ;; to enhance the portability of this code, fix bugs, and add features
40 40 ;; are most welcome. You can clone a Mercurial repository for this
41 41 ;; package from http://www.serpentine.com/hg/hg-emacs
42 42
43 43 ;; Please send problem reports and suggestions to bos@serpentine.com.
44 44
45 45
46 46 ;;; Code:
47 47
48 48 (require 'advice)
49 49 (require 'cl)
50 50 (require 'diff-mode)
51 51 (require 'easymenu)
52 52 (require 'vc)
53 53
54 54
55 55 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
56 56
57 57 (condition-case nil
58 58 (require 'view-less)
59 59 (error nil))
60 60 (condition-case nil
61 61 (require 'view)
62 62 (error nil))
63 63
64 64
65 65 ;;; Variables accessible through the custom system.
66 66
67 67 (defgroup mercurial nil
68 68 "Mercurial distributed SCM."
69 69 :group 'tools)
70 70
71 71 (defcustom hg-binary
72 72 (dolist (path '("~/bin/hg"
73 73 "/usr/bin/hg"
74 74 "/usr/local/bin/hg"))
75 75 (when (file-executable-p path)
76 76 (return path)))
77 77 "The path to Mercurial's hg executable."
78 78 :type '(file :must-match t)
79 79 :group 'mercurial)
80 80
81 81 (defcustom hg-mode-hook nil
82 82 "Hook run when a buffer enters hg-mode."
83 83 :type 'sexp
84 84 :group 'mercurial)
85 85
86 86 (defcustom hg-global-prefix "\C-ch"
87 87 "The global prefix for Mercurial keymap bindings."
88 88 :type 'sexp
89 89 :group 'mercurial)
90 90
91 91 (defcustom hg-rev-completion-limit 100
92 92 "The maximum number of revisions that hg-read-rev will offer to complete.
93 93 This affects memory usage and performance when prompting for revisions
94 94 in a repository with a lot of history."
95 95 :type 'integer
96 96 :group 'mercurial)
97 97
98 98 (defcustom hg-log-limit 50
99 99 "The maximum number of revisions that hg-log will display."
100 100 :type 'integer
101 101 :group 'mercurial)
102 102
103 103 (defcustom hg-update-modeline t
104 104 "Whether to update the modeline with the status of a file after every save.
105 105 Set this to nil on platforms with poor process management, such as Windows."
106 106 :type 'boolean
107 107 :group 'mercurial)
108 108
109 109
110 110 ;;; Other variables.
111 111
112 112 (defconst hg-running-xemacs (string-match "XEmacs" emacs-version)
113 113 "Is mercurial.el running under XEmacs?")
114 114
115 115 (defvar hg-mode nil
116 116 "Is this file managed by Mercurial?")
117 117 (make-variable-buffer-local 'hg-mode)
118 118 (put 'hg-mode 'permanent-local t)
119 119
120 120 (defvar hg-status nil)
121 121 (make-variable-buffer-local 'hg-status)
122 122 (put 'hg-status 'permanent-local t)
123 123
124 124 (defvar hg-output-buffer-name "*Hg*"
125 125 "The name to use for Mercurial output buffers.")
126 126
127 127 (defvar hg-file-history nil)
128 128 (defvar hg-rev-history nil)
129 129
130 130
131 131 ;;; hg-mode keymap.
132 132
133 133 (defvar hg-prefix-map
134 134 (let ((map (copy-keymap vc-prefix-map)))
135 135 (if (functionp 'set-keymap-name)
136 136 (set-keymap-name map 'hg-prefix-map)); XEmacs
137 137 map)
138 138 "This keymap overrides some default vc-mode bindings.")
139 139 (fset 'hg-prefix-map hg-prefix-map)
140 140 (define-key hg-prefix-map "=" 'hg-diff)
141 141 (define-key hg-prefix-map "c" 'hg-undo)
142 142 (define-key hg-prefix-map "g" 'hg-annotate)
143 143 (define-key hg-prefix-map "l" 'hg-log)
144 144 (define-key hg-prefix-map "n" 'hg-commit-file)
145 145 ;; (define-key hg-prefix-map "r" 'hg-update)
146 146 (define-key hg-prefix-map "u" 'hg-revert-buffer)
147 147 (define-key hg-prefix-map "~" 'hg-version-other-window)
148 148
149 149 (defvar hg-mode-map (make-sparse-keymap))
150 150 (define-key hg-mode-map "\C-xv" 'hg-prefix-map)
151 151
152 152 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
153 153
154 154
155 155 ;;; Global keymap.
156 156
157 157 (global-set-key "\C-xvi" 'hg-add)
158 158
159 159 (defvar hg-global-map (make-sparse-keymap))
160 160 (fset 'hg-global-map hg-global-map)
161 161 (global-set-key hg-global-prefix 'hg-global-map)
162 162 (define-key hg-global-map "," 'hg-incoming)
163 163 (define-key hg-global-map "." 'hg-outgoing)
164 164 (define-key hg-global-map "<" 'hg-pull)
165 165 (define-key hg-global-map "=" 'hg-diff)
166 166 (define-key hg-global-map ">" 'hg-push)
167 167 (define-key hg-global-map "?" 'hg-help-overview)
168 168 (define-key hg-global-map "A" 'hg-addremove)
169 169 (define-key hg-global-map "U" 'hg-revert)
170 170 (define-key hg-global-map "a" 'hg-add)
171 171 (define-key hg-global-map "c" 'hg-commit)
172 172 (define-key hg-global-map "f" 'hg-forget)
173 173 (define-key hg-global-map "h" 'hg-help-overview)
174 174 (define-key hg-global-map "i" 'hg-init)
175 175 (define-key hg-global-map "l" 'hg-log)
176 176 (define-key hg-global-map "r" 'hg-root)
177 177 (define-key hg-global-map "s" 'hg-status)
178 178 (define-key hg-global-map "u" 'hg-update)
179 179
180 180
181 181 ;;; View mode keymap.
182 182
183 183 (defvar hg-view-mode-map
184 184 (let ((map (copy-keymap (if (boundp 'view-minor-mode-map)
185 185 view-minor-mode-map
186 186 view-mode-map))))
187 187 (if (functionp 'set-keymap-name)
188 188 (set-keymap-name map 'hg-view-mode-map)); XEmacs
189 189 map))
190 190 (fset 'hg-view-mode-map hg-view-mode-map)
191 191 (define-key hg-view-mode-map
192 192 (if hg-running-xemacs [button2] [mouse-2])
193 193 'hg-buffer-mouse-clicked)
194 194
195 195
196 196 ;;; Convenience functions.
197 197
198 198 (defsubst hg-binary ()
199 199 (if hg-binary
200 200 hg-binary
201 201 (error "No `hg' executable found!")))
202 202
203 203 (defsubst hg-replace-in-string (str regexp newtext &optional literal)
204 204 "Replace all matches in STR for REGEXP with NEWTEXT string.
205 205 Return the new string. Optional LITERAL non-nil means do a literal
206 206 replacement.
207 207
208 208 This function bridges yet another pointless impedance gap between
209 209 XEmacs and GNU Emacs."
210 210 (if (fboundp 'replace-in-string)
211 211 (replace-in-string str regexp newtext literal)
212 212 (replace-regexp-in-string regexp newtext str nil literal)))
213 213
214 214 (defsubst hg-chomp (str)
215 215 "Strip trailing newlines from a string."
216 216 (hg-replace-in-string str "[\r\n]+$" ""))
217 217
218 218 (defun hg-run-command (command &rest args)
219 219 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
220 220 The list ARGS contains a list of arguments to pass to the command."
221 221 (let* (exit-code
222 222 (output
223 223 (with-output-to-string
224 224 (with-current-buffer
225 225 standard-output
226 226 (setq exit-code
227 227 (apply 'call-process command nil t nil args))))))
228 228 (cons exit-code output)))
229 229
230 230 (defun hg-run (command &rest args)
231 231 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
232 232 (apply 'hg-run-command (hg-binary) command args))
233 233
234 234 (defun hg-run0 (command &rest args)
235 235 "Run the Mercurial command COMMAND, returning its output.
236 236 If the command does not exit with a zero status code, raise an error."
237 237 (let ((res (apply 'hg-run-command (hg-binary) command args)))
238 238 (if (not (eq (car res) 0))
239 239 (error "Mercurial command failed %s - exit code %s"
240 240 (cons command args)
241 241 (car res))
242 242 (cdr res))))
243 243
244 244 (defun hg-buffer-commands (pnt)
245 245 "Use the properties of a character to do something sensible."
246 246 (interactive "d")
247 247 (let ((rev (get-char-property pnt 'rev))
248 248 (file (get-char-property pnt 'file))
249 249 (date (get-char-property pnt 'date))
250 250 (user (get-char-property pnt 'user))
251 251 (host (get-char-property pnt 'host))
252 252 (prev-buf (current-buffer)))
253 253 (cond
254 254 (file
255 255 (find-file-other-window file))
256 256 (rev
257 257 (hg-diff hg-view-file-name rev rev prev-buf))
258 258 ((message "I don't know how to do that yet")))))
259 259
260 260 (defun hg-buffer-mouse-clicked (event)
261 261 "Translate the mouse clicks in a HG log buffer to character events.
262 262 These are then handed off to `hg-buffer-commands'.
263 263
264 264 Handle frickin' frackin' gratuitous event-related incompatibilities."
265 265 (interactive "e")
266 266 (if hg-running-xemacs
267 267 (progn
268 268 (select-window (event-window event))
269 269 (hg-buffer-commands (event-point event)))
270 270 (select-window (posn-window (event-end event)))
271 271 (hg-buffer-commands (posn-point (event-start event)))))
272 272
273 273 (unless (fboundp 'view-minor-mode)
274 274 (defun view-minor-mode (prev-buffer exit-func)
275 275 (view-mode)))
276 276
277 277 (defsubst hg-abbrev-file-name (file)
278 278 "Portable wrapper around abbreviate-file-name."
279 279 (if hg-running-xemacs
280 280 (abbreviate-file-name file t)
281 281 (abbreviate-file-name file)))
282 282
283 283 (defun hg-read-file-name (&optional prompt default)
284 284 "Read a file or directory name, or a pattern, to use with a command."
285 285 (let ((path (or default (buffer-file-name))))
286 286 (if (or (not path) current-prefix-arg)
287 287 (expand-file-name
288 288 (read-file-name (format "File, directory or pattern%s: "
289 289 (or prompt ""))
290 290 (and path (file-name-directory path))
291 291 nil nil
292 292 (and path (file-name-nondirectory path))
293 293 'hg-file-history))
294 294 path)))
295 295
296 296 (defun hg-read-rev (&optional prompt default)
297 297 "Read a revision or tag, offering completions."
298 298 (let ((rev (or default "tip")))
299 299 (if (or (not rev) current-prefix-arg)
300 300 (let ((revs (split-string (hg-chomp
301 301 (hg-run0 "-q" "log" "-r"
302 302 (format "-%d"
303 303 hg-rev-completion-limit)
304 304 "-r" "tip"))
305 305 "[\n:]")))
306 306 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
307 307 (setq revs (cons (car (split-string line "\\s-")) revs)))
308 308 (completing-read (format "Revision%s (%s): "
309 309 (or prompt "")
310 310 (or default "tip"))
311 311 (map 'list 'cons revs revs)
312 312 nil
313 313 nil
314 314 nil
315 315 'hg-rev-history
316 316 (or default "tip")))
317 317 rev)))
318 318
319 319 ;;; View mode bits.
320 320
321 321 (defun hg-exit-view-mode (buf)
322 322 "Exit from hg-view-mode.
323 323 We delete the current window if entering hg-view-mode split the
324 324 current frame."
325 325 (when (and (eq buf (current-buffer))
326 326 (> (length (window-list)) 1))
327 327 (delete-window))
328 328 (when (buffer-live-p buf)
329 329 (kill-buffer buf)))
330 330
331 331 (defun hg-view-mode (prev-buffer &optional file-name)
332 332 (goto-char (point-min))
333 333 (set-buffer-modified-p nil)
334 334 (toggle-read-only t)
335 335 (view-minor-mode prev-buffer 'hg-exit-view-mode)
336 336 (use-local-map hg-view-mode-map)
337 337 (setq truncate-lines t)
338 338 (when file-name
339 339 (set (make-local-variable 'hg-view-file-name)
340 340 (hg-abbrev-file-name file-name))))
341 341
342 342 (defun hg-file-status (file)
343 343 "Return status of FILE, or nil if FILE does not exist or is unmanaged."
344 344 (let* ((s (hg-run "status" file))
345 345 (exit (car s))
346 346 (output (cdr s)))
347 347 (if (= exit 0)
348 348 (let ((state (assoc (substring output 0 (min (length output) 2))
349 349 '(("M " . modified)
350 350 ("A " . added)
351 351 ("R " . removed)
352 352 ("? " . nil)))))
353 353 (if state
354 354 (cdr state)
355 355 'normal)))))
356 356
357 357 (defun hg-tip ()
358 358 (split-string (hg-chomp (hg-run0 "-q" "tip")) ":"))
359 359
360 360 (defmacro hg-view-output (args &rest body)
361 361 "Execute BODY in a clean buffer, then quickly display that buffer.
362 362 If the buffer contains one line, its contents are displayed in the
363 363 minibuffer. Otherwise, the buffer is displayed in view-mode.
364 364 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
365 365 the name of the buffer to create, and FILE is the name of the file
366 366 being viewed."
367 367 (let ((prev-buf (gensym "prev-buf-"))
368 368 (v-b-name (car args))
369 369 (v-m-rest (cdr args)))
370 370 `(let ((view-buf-name ,v-b-name)
371 371 (,prev-buf (current-buffer)))
372 372 (get-buffer-create view-buf-name)
373 373 (kill-buffer view-buf-name)
374 374 (get-buffer-create view-buf-name)
375 375 (set-buffer view-buf-name)
376 376 (save-excursion
377 377 ,@body)
378 378 (case (count-lines (point-min) (point-max))
379 379 ((0)
380 380 (kill-buffer view-buf-name)
381 381 (message "(No output)"))
382 382 ((1)
383 383 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
384 384 (kill-buffer view-buf-name)
385 385 (message "%s" msg)))
386 386 (t
387 387 (pop-to-buffer view-buf-name)
388 388 (hg-view-mode ,prev-buf ,@v-m-rest))))))
389 389
390 390 (put 'hg-view-output 'lisp-indent-function 1)
391 391
392 392 ;;; Context save and restore across revert.
393 393
394 394 (defun hg-position-context (pos)
395 395 "Return information to help find the given position again."
396 396 (let* ((end (min (point-max) (+ pos 98))))
397 397 (list pos
398 398 (buffer-substring (max (point-min) (- pos 2)) end)
399 399 (- end pos))))
400 400
401 401 (defun hg-buffer-context ()
402 402 "Return information to help restore a user's editing context.
403 403 This is useful across reverts and merges, where a context is likely
404 404 to have moved a little, but not really changed."
405 405 (let ((point-context (hg-position-context (point)))
406 406 (mark-context (let ((mark (mark-marker)))
407 407 (and mark (hg-position-context mark)))))
408 408 (list point-context mark-context)))
409 409
410 410 (defun hg-find-context (ctx)
411 411 "Attempt to find a context in the given buffer.
412 412 Always returns a valid, hopefully sane, position."
413 413 (let ((pos (nth 0 ctx))
414 414 (str (nth 1 ctx))
415 415 (fixup (nth 2 ctx)))
416 416 (save-excursion
417 417 (goto-char (max (point-min) (- pos 15000)))
418 418 (if (and (not (equal str ""))
419 419 (search-forward str nil t))
420 420 (- (point) fixup)
421 421 (max pos (point-min))))))
422 422
423 423 (defun hg-restore-context (ctx)
424 424 "Attempt to restore the user's editing context."
425 425 (let ((point-context (nth 0 ctx))
426 426 (mark-context (nth 1 ctx)))
427 427 (goto-char (hg-find-context point-context))
428 428 (when mark-context
429 429 (set-mark (hg-find-context mark-context)))))
430 430
431 431
432 432 ;;; Hooks.
433 433
434 434 (defun hg-mode-line (&optional force)
435 435 "Update the modeline with the current status of a file.
436 436 An update occurs if optional argument FORCE is non-nil,
437 437 hg-update-modeline is non-nil, or we have not yet checked the state of
438 438 the file."
439 439 (when (and (hg-root) (or force hg-update-modeline (not hg-mode)))
440 440 (let ((status (hg-file-status buffer-file-name)))
441 441 (setq hg-status status
442 442 hg-mode (and status (concat " Hg:"
443 443 (car (hg-tip))
444 444 (cdr (assq status
445 445 '((normal . "")
446 446 (removed . "r")
447 447 (added . "a")
448 448 (modified . "m")))))))
449 449 status)))
450 450
451 451 (defun hg-find-file-hook ()
452 452 (when (hg-mode-line)
453 453 (run-hooks 'hg-mode-hook)))
454 454
455 455 (add-hook 'find-file-hooks 'hg-find-file-hook)
456 456
457 457 (defun hg-after-save-hook ()
458 458 (let ((old-status hg-status))
459 459 (hg-mode-line)
460 460 (if (and (not old-status) hg-status)
461 461 (run-hooks 'hg-mode-hook))))
462 462
463 463 (add-hook 'after-save-hook 'hg-after-save-hook)
464 464
465 465
466 466 ;;; User interface functions.
467 467
468 468 (defun hg-help-overview ()
469 469 "This is an overview of the Mercurial SCM mode for Emacs.
470 470
471 471 You can find the source code, license (GPL v2), and credits for this
472 472 code by typing `M-x find-library mercurial RET'.
473 473
474 474 The Mercurial mode user interface is based on that of the older VC
475 475 mode, so if you're already familiar with VC, the same keybindings and
476 476 functions will generally work.
477 477
478 478 Below is a list of common SCM tasks, with the key bindings needed to
479 479 perform them, and the command names. This list is not exhaustive.
480 480
481 481 In the list below, `G/L' indicates whether a key binding is global (G)
482 482 or local (L). Global keybindings work on any file inside a Mercurial
483 483 repository. Local keybindings only apply to files under the control
484 484 of Mercurial. Many commands take a prefix argument.
485 485
486 486
487 487 SCM Task G/L Key Binding Command Name
488 488 -------- --- ----------- ------------
489 489 Help overview (what you are reading) G C-c h h hg-help-overview
490 490
491 491 Tell Mercurial to manage a file G C-c h a hg-add
492 492 Commit changes to current file only L C-x v n hg-commit
493 493 Undo changes to file since commit L C-x v u hg-revert-buffer
494 494
495 495 Diff file vs last checkin L C-x v = hg-diff
496 496
497 497 View file change history L C-x v l hg-log
498 498 View annotated file L C-x v a hg-annotate
499 499
500 500 Diff repo vs last checkin G C-c h = hg-diff
501 501 View status of files in repo G C-c h s hg-status
502 502 Commit all changes G C-c h c hg-commit
503 503
504 504 Undo all changes since last commit G C-c h U hg-revert
505 505 View repo change history G C-c h l hg-log
506 506
507 507 See changes that can be pulled G C-c h , hg-incoming
508 508 Pull changes G C-c h < hg-pull
509 509 Update working directory after pull G C-c h u hg-update
510 510 See changes that can be pushed G C-c h . hg-outgoing
511 511 Push changes G C-c h > hg-push"
512 512 (interactive)
513 513 (hg-view-output ("Mercurial Help Overview")
514 514 (insert (documentation 'hg-help-overview))))
515 515
516 516 (defun hg-add (path)
517 "Add PATH to the Mercurial repository on the next commit.
518 With a prefix argument, prompt for the path to add."
517 519 (interactive (list (hg-read-file-name " to add")))
518 520 (let ((buf (current-buffer))
519 521 (update (equal buffer-file-name path)))
520 522 (hg-view-output (hg-output-buffer-name)
521 523 (apply 'call-process (hg-binary) nil t nil (list "add" path)))
522 524 (when update
523 525 (with-current-buffer buf
524 526 (hg-mode-line)))))
525 527
526 528 (defun hg-addremove ()
527 529 (interactive)
528 530 (error "not implemented"))
529 531
530 532 (defun hg-annotate ()
531 533 (interactive)
532 534 (error "not implemented"))
533 535
534 536 (defun hg-commit ()
535 537 (interactive)
536 538 (error "not implemented"))
537 539
538 540 (defun hg-diff (path &optional rev1 rev2)
541 "Show the differences between REV1 and REV2 of PATH.
542 When called interactively, the default behaviour is to treat REV1 as
543 the tip revision, REV2 as the current edited version of the file, and
544 PATH as the file edited in the current buffer.
545 With a prefix argument, prompt for all of these."
539 546 (interactive (list (hg-read-file-name " to diff")
540 547 (hg-read-rev " to start with")
541 548 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
542 549 (and (not (eq rev2 'working-dir)) rev2))))
543 550 (unless rev1
544 551 (setq rev1 "-1"))
545 552 (let ((a-path (hg-abbrev-file-name path))
546 553 diff)
547 554 (hg-view-output ((if (equal rev1 rev2)
548 555 (format "Mercurial: Rev %s of %s" rev1 a-path)
549 556 (format "Mercurial: Rev %s to %s of %s"
550 557 rev1 (or rev2 "Current") a-path)))
551 558 (if rev2
552 559 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)
553 560 (call-process (hg-binary) nil t nil "diff" "-r" rev1 path))
554 561 (diff-mode)
555 562 (setq diff (not (= (point-min) (point-max))))
556 563 (font-lock-fontify-buffer))
557 564 diff))
558 565
559 566 (defun hg-forget (path)
567 "Lose track of PATH, which has been added, but not yet committed.
568 This will prevent the file from being incorporated into the Mercurial
569 repository on the next commit.
570 With a prefix argument, prompt for the path to forget."
560 571 (interactive (list (hg-read-file-name " to forget")))
561 572 (let ((buf (current-buffer))
562 573 (update (equal buffer-file-name path)))
563 574 (hg-view-output (hg-output-buffer-name)
564 575 (apply 'call-process (hg-binary) nil t nil (list "forget" path)))
565 576 (when update
566 577 (with-current-buffer buf
567 578 (hg-mode-line)))))
568 579
569 580 (defun hg-incoming ()
570 581 (interactive)
571 582 (error "not implemented"))
572 583
573 584 (defun hg-init ()
574 585 (interactive)
575 586 (error "not implemented"))
576 587
577 588 (defun hg-log (path &optional rev1 rev2)
589 "Display the revision history of PATH, between REV1 and REV2.
590 REV1 defaults to the initial revision, while REV2 defaults to the tip.
591 With a prefix argument, prompt for each parameter."
578 592 (interactive (list (hg-read-file-name " to log")
579 593 (hg-read-rev " to start with" "-1")
580 594 (hg-read-rev " to end with" (format "-%d" hg-log-limit))))
581 595 (let ((a-path (hg-abbrev-file-name path)))
582 596 (hg-view-output ((if (equal rev1 rev2)
583 597 (format "Mercurial: Rev %s of %s" rev1 a-path)
584 598 (format "Mercurial: Rev %s to %s of %s"
585 599 rev1 (or rev2 "Current") a-path)))
586 600 (call-process (hg-binary) nil t nil "log" "-r" rev1 "-r" rev2 path)
587 601 (diff-mode)
588 602 (font-lock-fontify-buffer))))
589 603
590 604 (defun hg-outgoing ()
591 605 (interactive)
592 606 (error "not implemented"))
593 607
594 608 (defun hg-pull ()
595 609 (interactive)
596 610 (error "not implemented"))
597 611
598 612 (defun hg-push ()
599 613 (interactive)
600 614 (error "not implemented"))
601 615
602 616 (defun hg-revert-buffer-internal ()
603 617 (let ((ctx (hg-buffer-context)))
604 618 (message "Reverting %s..." buffer-file-name)
605 619 (hg-run0 "revert" buffer-file-name)
606 620 (revert-buffer t t t)
607 621 (hg-restore-context ctx)
608 622 (hg-mode-line)
609 623 (message "Reverting %s...done" buffer-file-name)))
610 624
611 625 (defun hg-revert-buffer ()
626 "Revert current buffer's file back to the latest committed version.
627 If the file has not changed, nothing happens. Otherwise, this
628 displays a diff and asks for confirmation before reverting."
612 629 (interactive)
613 630 (let ((vc-suppress-confirm nil)
614 631 (obuf (current-buffer))
615 632 diff)
616 633 (vc-buffer-sync)
617 634 (unwind-protect
618 635 (setq diff (hg-diff buffer-file-name))
619 636 (when diff
620 637 (unless (yes-or-no-p "Discard changes? ")
621 638 (error "Revert cancelled")))
622 639 (when diff
623 640 (let ((buf (current-buffer)))
624 641 (delete-window (selected-window))
625 642 (kill-buffer buf))))
626 643 (set-buffer obuf)
627 644 (when diff
628 645 (hg-revert-buffer-internal))))
629 646
630 647 (defun hg-root (&optional path)
648 "Return the root of the repository that contains the given path.
649 If the path is outside a repository, return nil.
650 When called interactively, the root is printed. A prefix argument
651 prompts for a path to check."
631 652 (interactive (list (hg-read-file-name)))
632 653 (let ((root (do ((prev nil dir)
633 654 (dir (file-name-directory (or path (buffer-file-name)))
634 655 (file-name-directory (directory-file-name dir))))
635 656 ((equal prev dir))
636 657 (when (file-directory-p (concat dir ".hg"))
637 658 (return dir)))))
638 659 (when (interactive-p)
639 660 (if root
640 661 (message "The root of this repository is `%s'." root)
641 662 (message "The path `%s' is not in a Mercurial repository."
642 663 (abbreviate-file-name path t))))
643 664 root))
644 665
645 666 (defun hg-status (path)
667 "Print revision control status of a file or directory.
668 With prefix argument, prompt for the path to give status for.
669 Names are displayed relative to the repository root."
646 670 (interactive (list (hg-read-file-name " for status" (hg-root))))
647 671 (let ((root (hg-root)))
648 (hg-view-output (hg-output-buffer-name)
672 (hg-view-output ((format "Mercurial: Status of %s in %s"
673 (let ((name (substring (expand-file-name path)
674 (length root))))
675 (if (> (length name) 0)
676 name
677 "*"))
678 (hg-abbrev-file-name root)))
649 679 (apply 'call-process (hg-binary) nil t nil
650 680 (list "--cwd" root "status" path)))))
651 681
652 682 (defun hg-undo ()
653 683 (interactive)
654 684 (error "not implemented"))
655 685
656 686 (defun hg-version-other-window ()
657 687 (interactive)
658 688 (error "not implemented"))
659 689
660 690
661 691 (provide 'mercurial)
662 692
663 693
664 694 ;;; Local Variables:
665 695 ;;; prompt-to-byte-compile: nil
666 696 ;;; end:
General Comments 0
You need to be logged in to leave comments. Login now