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