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