##// END OF EJS Templates
mercurial.el: fix error on hg-read-rev() with small tip, and cleanups...
NIIMI Satoshi -
r4433:ba22e867 default
parent child Browse files
Show More
@@ -1,1268 +1,1275 b''
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, 2006 Bryan O'Sullivan
3 ;; Copyright (C) 2005, 2006 Bryan O'Sullivan
4
4
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6
6
7 ;; mercurial.el is free software; you can redistribute it and/or
7 ;; mercurial.el is free software; you can redistribute it and/or
8 ;; modify it under the terms of version 2 of the GNU General Public
8 ;; modify it under the terms of version 2 of the GNU General Public
9 ;; License as published by the Free Software Foundation.
9 ;; License as published by the Free Software Foundation.
10
10
11 ;; mercurial.el is distributed in the hope that it will be useful, but
11 ;; mercurial.el is distributed in the hope that it will be useful, but
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;; General Public License for more details.
14 ;; General Public License for more details.
15
15
16 ;; You should have received a copy of the GNU General Public License
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
17 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
18 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
18 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
19 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
20
21 ;;; Commentary:
21 ;;; Commentary:
22
22
23 ;; mercurial.el builds upon Emacs's VC mode to provide flexible
23 ;; mercurial.el builds upon Emacs's VC mode to provide flexible
24 ;; integration with the Mercurial distributed SCM tool.
24 ;; integration with the Mercurial distributed SCM tool.
25
25
26 ;; To get going as quickly as possible, load mercurial.el into Emacs and
26 ;; To get going as quickly as possible, load mercurial.el into Emacs and
27 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
27 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
28 ;; usage overview.
28 ;; usage overview.
29
29
30 ;; Much of the inspiration for mercurial.el comes from Rajesh
30 ;; Much of the inspiration for mercurial.el comes from Rajesh
31 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
31 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
32 ;; job for the commercial Perforce SCM product. In fact, substantial
32 ;; job for the commercial Perforce SCM product. In fact, substantial
33 ;; chunks of code are adapted from p4.el.
33 ;; chunks of code are adapted from p4.el.
34
34
35 ;; This code has been developed under XEmacs 21.5, and may not work as
35 ;; This code has been developed under XEmacs 21.5, and may not work as
36 ;; well under GNU Emacs (albeit tested under 21.4). Patches to
36 ;; well under GNU Emacs (albeit tested under 21.4). Patches to
37 ;; enhance the portability of this code, fix bugs, and add features
37 ;; enhance the portability of this code, fix bugs, and add features
38 ;; are most welcome. You can clone a Mercurial repository for this
38 ;; are most welcome. You can clone a Mercurial repository for this
39 ;; package from http://www.serpentine.com/hg/hg-emacs
39 ;; package from http://www.serpentine.com/hg/hg-emacs
40
40
41 ;; Please send problem reports and suggestions to bos@serpentine.com.
41 ;; Please send problem reports and suggestions to bos@serpentine.com.
42
42
43
43
44 ;;; Code:
44 ;;; Code:
45
45
46 (require 'advice)
46 (eval-when-compile (require 'cl))
47 (require 'cl)
48 (require 'diff-mode)
47 (require 'diff-mode)
49 (require 'easymenu)
48 (require 'easymenu)
50 (require 'executable)
49 (require 'executable)
51 (require 'vc)
50 (require 'vc)
52
51
52 (defmacro hg-feature-cond (&rest clauses)
53 "Test CLAUSES for feature at compile time.
54 Each clause is (FEATURE BODY...)."
55 (dolist (x clauses)
56 (let ((feature (car x))
57 (body (cdr x)))
58 (when (or (eq feature t)
59 (featurep feature))
60 (return (cons 'progn body))))))
61
53
62
54 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
63 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
55
64
56 (condition-case nil
65 (hg-feature-cond
57 (require 'view-less)
66 (xemacs (require 'view-less))
58 (error nil))
67 (t (require 'view)))
59 (condition-case nil
60 (require 'view)
61 (error nil))
62
68
63
69
64 ;;; Variables accessible through the custom system.
70 ;;; Variables accessible through the custom system.
65
71
66 (defgroup mercurial nil
72 (defgroup mercurial nil
67 "Mercurial distributed SCM."
73 "Mercurial distributed SCM."
68 :group 'tools)
74 :group 'tools)
69
75
70 (defcustom hg-binary
76 (defcustom hg-binary
71 (or (executable-find "hg")
77 (or (executable-find "hg")
72 (dolist (path '("~/bin/hg" "/usr/bin/hg" "/usr/local/bin/hg"))
78 (dolist (path '("~/bin/hg" "/usr/bin/hg" "/usr/local/bin/hg"))
73 (when (file-executable-p path)
79 (when (file-executable-p path)
74 (return path))))
80 (return path))))
75 "The path to Mercurial's hg executable."
81 "The path to Mercurial's hg executable."
76 :type '(file :must-match t)
82 :type '(file :must-match t)
77 :group 'mercurial)
83 :group 'mercurial)
78
84
79 (defcustom hg-mode-hook nil
85 (defcustom hg-mode-hook nil
80 "Hook run when a buffer enters hg-mode."
86 "Hook run when a buffer enters hg-mode."
81 :type 'sexp
87 :type 'sexp
82 :group 'mercurial)
88 :group 'mercurial)
83
89
84 (defcustom hg-commit-mode-hook nil
90 (defcustom hg-commit-mode-hook nil
85 "Hook run when a buffer is created to prepare a commit."
91 "Hook run when a buffer is created to prepare a commit."
86 :type 'sexp
92 :type 'sexp
87 :group 'mercurial)
93 :group 'mercurial)
88
94
89 (defcustom hg-pre-commit-hook nil
95 (defcustom hg-pre-commit-hook nil
90 "Hook run before a commit is performed.
96 "Hook run before a commit is performed.
91 If you want to prevent the commit from proceeding, raise an error."
97 If you want to prevent the commit from proceeding, raise an error."
92 :type 'sexp
98 :type 'sexp
93 :group 'mercurial)
99 :group 'mercurial)
94
100
95 (defcustom hg-log-mode-hook nil
101 (defcustom hg-log-mode-hook nil
96 "Hook run after a buffer is filled with log information."
102 "Hook run after a buffer is filled with log information."
97 :type 'sexp
103 :type 'sexp
98 :group 'mercurial)
104 :group 'mercurial)
99
105
100 (defcustom hg-global-prefix "\C-ch"
106 (defcustom hg-global-prefix "\C-ch"
101 "The global prefix for Mercurial keymap bindings."
107 "The global prefix for Mercurial keymap bindings."
102 :type 'sexp
108 :type 'sexp
103 :group 'mercurial)
109 :group 'mercurial)
104
110
105 (defcustom hg-commit-allow-empty-message nil
111 (defcustom hg-commit-allow-empty-message nil
106 "Whether to allow changes to be committed with empty descriptions."
112 "Whether to allow changes to be committed with empty descriptions."
107 :type 'boolean
113 :type 'boolean
108 :group 'mercurial)
114 :group 'mercurial)
109
115
110 (defcustom hg-commit-allow-empty-file-list nil
116 (defcustom hg-commit-allow-empty-file-list nil
111 "Whether to allow changes to be committed without any modified files."
117 "Whether to allow changes to be committed without any modified files."
112 :type 'boolean
118 :type 'boolean
113 :group 'mercurial)
119 :group 'mercurial)
114
120
115 (defcustom hg-rev-completion-limit 100
121 (defcustom hg-rev-completion-limit 100
116 "The maximum number of revisions that hg-read-rev will offer to complete.
122 "The maximum number of revisions that hg-read-rev will offer to complete.
117 This affects memory usage and performance when prompting for revisions
123 This affects memory usage and performance when prompting for revisions
118 in a repository with a lot of history."
124 in a repository with a lot of history."
119 :type 'integer
125 :type 'integer
120 :group 'mercurial)
126 :group 'mercurial)
121
127
122 (defcustom hg-log-limit 50
128 (defcustom hg-log-limit 50
123 "The maximum number of revisions that hg-log will display."
129 "The maximum number of revisions that hg-log will display."
124 :type 'integer
130 :type 'integer
125 :group 'mercurial)
131 :group 'mercurial)
126
132
127 (defcustom hg-update-modeline t
133 (defcustom hg-update-modeline t
128 "Whether to update the modeline with the status of a file after every save.
134 "Whether to update the modeline with the status of a file after every save.
129 Set this to nil on platforms with poor process management, such as Windows."
135 Set this to nil on platforms with poor process management, such as Windows."
130 :type 'boolean
136 :type 'boolean
131 :group 'mercurial)
137 :group 'mercurial)
132
138
133 (defcustom hg-incoming-repository "default"
139 (defcustom hg-incoming-repository "default"
134 "The repository from which changes are pulled from by default.
140 "The repository from which changes are pulled from by default.
135 This should be a symbolic repository name, since it is used for all
141 This should be a symbolic repository name, since it is used for all
136 repository-related commands."
142 repository-related commands."
137 :type 'string
143 :type 'string
138 :group 'mercurial)
144 :group 'mercurial)
139
145
140 (defcustom hg-outgoing-repository "default-push"
146 (defcustom hg-outgoing-repository "default-push"
141 "The repository to which changes are pushed to by default.
147 "The repository to which changes are pushed to by default.
142 This should be a symbolic repository name, since it is used for all
148 This should be a symbolic repository name, since it is used for all
143 repository-related commands."
149 repository-related commands."
144 :type 'string
150 :type 'string
145 :group 'mercurial)
151 :group 'mercurial)
146
152
147
153
148 ;;; Other variables.
154 ;;; Other variables.
149
155
150 (defconst hg-running-xemacs (string-match "XEmacs" emacs-version)
151 "Is mercurial.el running under XEmacs?")
152
153 (defvar hg-mode nil
156 (defvar hg-mode nil
154 "Is this file managed by Mercurial?")
157 "Is this file managed by Mercurial?")
155 (make-variable-buffer-local 'hg-mode)
158 (make-variable-buffer-local 'hg-mode)
156 (put 'hg-mode 'permanent-local t)
159 (put 'hg-mode 'permanent-local t)
157
160
158 (defvar hg-status nil)
161 (defvar hg-status nil)
159 (make-variable-buffer-local 'hg-status)
162 (make-variable-buffer-local 'hg-status)
160 (put 'hg-status 'permanent-local t)
163 (put 'hg-status 'permanent-local t)
161
164
162 (defvar hg-prev-buffer nil)
165 (defvar hg-prev-buffer nil)
163 (make-variable-buffer-local 'hg-prev-buffer)
166 (make-variable-buffer-local 'hg-prev-buffer)
164 (put 'hg-prev-buffer 'permanent-local t)
167 (put 'hg-prev-buffer 'permanent-local t)
165
168
166 (defvar hg-root nil)
169 (defvar hg-root nil)
167 (make-variable-buffer-local 'hg-root)
170 (make-variable-buffer-local 'hg-root)
168 (put 'hg-root 'permanent-local t)
171 (put 'hg-root 'permanent-local t)
169
172
173 (defvar hg-view-mode nil)
174 (make-variable-buffer-local 'hg-view-mode)
175 (put 'hg-view-mode 'permanent-local t)
176
177 (defvar hg-view-file-name nil)
178 (make-variable-buffer-local 'hg-view-file-name)
179 (put 'hg-view-file-name 'permanent-local t)
180
170 (defvar hg-output-buffer-name "*Hg*"
181 (defvar hg-output-buffer-name "*Hg*"
171 "The name to use for Mercurial output buffers.")
182 "The name to use for Mercurial output buffers.")
172
183
173 (defvar hg-file-history nil)
184 (defvar hg-file-history nil)
174 (defvar hg-repo-history nil)
185 (defvar hg-repo-history nil)
175 (defvar hg-rev-history nil)
186 (defvar hg-rev-history nil)
187 (defvar hg-repo-completion-table nil) ; shut up warnings
176
188
177
189
178 ;;; Random constants.
190 ;;; Random constants.
179
191
180 (defconst hg-commit-message-start
192 (defconst hg-commit-message-start
181 "--- Enter your commit message. Type `C-c C-c' to commit. ---\n")
193 "--- Enter your commit message. Type `C-c C-c' to commit. ---\n")
182
194
183 (defconst hg-commit-message-end
195 (defconst hg-commit-message-end
184 "--- Files in bold will be committed. Click to toggle selection. ---\n")
196 "--- Files in bold will be committed. Click to toggle selection. ---\n")
185
197
198 (defconst hg-state-alist
199 '((?M . modified)
200 (?A . added)
201 (?R . removed)
202 (?! . deleted)
203 (?C . normal)
204 (?I . ignored)
205 (?? . nil)))
186
206
187 ;;; hg-mode keymap.
207 ;;; hg-mode keymap.
188
208
189 (defvar hg-mode-map (make-sparse-keymap))
190 (define-key hg-mode-map "\C-xv" 'hg-prefix-map)
191
192 (defvar hg-prefix-map
209 (defvar hg-prefix-map
193 (let ((map (copy-keymap vc-prefix-map)))
210 (let ((map (make-sparse-keymap)))
194 (if (functionp 'set-keymap-name)
211 (hg-feature-cond (xemacs (set-keymap-name map 'hg-prefix-map))) ; XEmacs
195 (set-keymap-name map 'hg-prefix-map)); XEmacs
212 (set-keymap-parent map vc-prefix-map)
213 (define-key map "=" 'hg-diff)
214 (define-key map "c" 'hg-undo)
215 (define-key map "g" 'hg-annotate)
216 (define-key map "i" 'hg-add)
217 (define-key map "l" 'hg-log)
218 (define-key map "n" 'hg-commit-start)
219 ;; (define-key map "r" 'hg-update)
220 (define-key map "u" 'hg-revert-buffer)
221 (define-key map "~" 'hg-version-other-window)
196 map)
222 map)
197 "This keymap overrides some default vc-mode bindings.")
223 "This keymap overrides some default vc-mode bindings.")
198 (fset 'hg-prefix-map hg-prefix-map)
224
199 (define-key hg-prefix-map "=" 'hg-diff)
225 (defvar hg-mode-map
200 (define-key hg-prefix-map "c" 'hg-undo)
226 (let ((map (make-sparse-keymap)))
201 (define-key hg-prefix-map "g" 'hg-annotate)
227 (define-key map "\C-xv" hg-prefix-map)
202 (define-key hg-prefix-map "l" 'hg-log)
228 map))
203 (define-key hg-prefix-map "n" 'hg-commit-start)
204 ;; (define-key hg-prefix-map "r" 'hg-update)
205 (define-key hg-prefix-map "u" 'hg-revert-buffer)
206 (define-key hg-prefix-map "~" 'hg-version-other-window)
207
229
208 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
230 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
209
231
210
232
211 ;;; Global keymap.
233 ;;; Global keymap.
212
234
213 (global-set-key "\C-xvi" 'hg-add)
235 (defvar hg-global-map
236 (let ((map (make-sparse-keymap)))
237 (define-key map "," 'hg-incoming)
238 (define-key map "." 'hg-outgoing)
239 (define-key map "<" 'hg-pull)
240 (define-key map "=" 'hg-diff-repo)
241 (define-key map ">" 'hg-push)
242 (define-key map "?" 'hg-help-overview)
243 (define-key map "A" 'hg-addremove)
244 (define-key map "U" 'hg-revert)
245 (define-key map "a" 'hg-add)
246 (define-key map "c" 'hg-commit-start)
247 (define-key map "f" 'hg-forget)
248 (define-key map "h" 'hg-help-overview)
249 (define-key map "i" 'hg-init)
250 (define-key map "l" 'hg-log-repo)
251 (define-key map "r" 'hg-root)
252 (define-key map "s" 'hg-status)
253 (define-key map "u" 'hg-update)
254 map))
214
255
215 (defvar hg-global-map (make-sparse-keymap))
256 (global-set-key hg-global-prefix hg-global-map)
216 (fset 'hg-global-map hg-global-map)
217 (global-set-key hg-global-prefix 'hg-global-map)
218 (define-key hg-global-map "," 'hg-incoming)
219 (define-key hg-global-map "." 'hg-outgoing)
220 (define-key hg-global-map "<" 'hg-pull)
221 (define-key hg-global-map "=" 'hg-diff-repo)
222 (define-key hg-global-map ">" 'hg-push)
223 (define-key hg-global-map "?" 'hg-help-overview)
224 (define-key hg-global-map "A" 'hg-addremove)
225 (define-key hg-global-map "U" 'hg-revert)
226 (define-key hg-global-map "a" 'hg-add)
227 (define-key hg-global-map "c" 'hg-commit-start)
228 (define-key hg-global-map "f" 'hg-forget)
229 (define-key hg-global-map "h" 'hg-help-overview)
230 (define-key hg-global-map "i" 'hg-init)
231 (define-key hg-global-map "l" 'hg-log-repo)
232 (define-key hg-global-map "r" 'hg-root)
233 (define-key hg-global-map "s" 'hg-status)
234 (define-key hg-global-map "u" 'hg-update)
235
236
257
237 ;;; View mode keymap.
258 ;;; View mode keymap.
238
259
239 (defvar hg-view-mode-map
260 (defvar hg-view-mode-map
240 (let ((map (copy-keymap (if (boundp 'view-minor-mode-map)
261 (let ((map (make-sparse-keymap)))
241 view-minor-mode-map
262 (hg-feature-cond (xemacs (set-keymap-name map 'hg-view-mode-map))) ; XEmacs
242 view-mode-map))))
263 (define-key map (hg-feature-cond (xemacs [button2])
243 (if (functionp 'set-keymap-name)
264 (t [mouse-2]))
244 (set-keymap-name map 'hg-view-mode-map)); XEmacs
265 'hg-buffer-mouse-clicked)
245 map))
266 map))
246 (fset 'hg-view-mode-map hg-view-mode-map)
267
247 (define-key hg-view-mode-map
268 (add-minor-mode 'hg-view-mode "" hg-view-mode-map)
248 (if hg-running-xemacs [button2] [mouse-2])
249 'hg-buffer-mouse-clicked)
250
269
251
270
252 ;;; Commit mode keymaps.
271 ;;; Commit mode keymaps.
253
272
254 (defvar hg-commit-mode-map (make-sparse-keymap))
273 (defvar hg-commit-mode-map
255 (define-key hg-commit-mode-map "\C-c\C-c" 'hg-commit-finish)
274 (let ((map (make-sparse-keymap)))
256 (define-key hg-commit-mode-map "\C-c\C-k" 'hg-commit-kill)
275 (define-key map "\C-c\C-c" 'hg-commit-finish)
257 (define-key hg-commit-mode-map "\C-xv=" 'hg-diff-repo)
276 (define-key map "\C-c\C-k" 'hg-commit-kill)
277 (define-key map "\C-xv=" 'hg-diff-repo)
278 map))
258
279
259 (defvar hg-commit-mode-file-map (make-sparse-keymap))
280 (defvar hg-commit-mode-file-map
260 (define-key hg-commit-mode-file-map
281 (let ((map (make-sparse-keymap)))
261 (if hg-running-xemacs [button2] [mouse-2])
282 (define-key map (hg-feature-cond (xemacs [button2])
262 'hg-commit-mouse-clicked)
283 (t [mouse-2]))
263 (define-key hg-commit-mode-file-map " " 'hg-commit-toggle-file)
284 'hg-commit-mouse-clicked)
264 (define-key hg-commit-mode-file-map "\r" 'hg-commit-toggle-file)
285 (define-key map " " 'hg-commit-toggle-file)
286 (define-key map "\r" 'hg-commit-toggle-file)
287 map))
265
288
266
289
267 ;;; Convenience functions.
290 ;;; Convenience functions.
268
291
269 (defsubst hg-binary ()
292 (defsubst hg-binary ()
270 (if hg-binary
293 (if hg-binary
271 hg-binary
294 hg-binary
272 (error "No `hg' executable found!")))
295 (error "No `hg' executable found!")))
273
296
274 (defsubst hg-replace-in-string (str regexp newtext &optional literal)
297 (defsubst hg-replace-in-string (str regexp newtext &optional literal)
275 "Replace all matches in STR for REGEXP with NEWTEXT string.
298 "Replace all matches in STR for REGEXP with NEWTEXT string.
276 Return the new string. Optional LITERAL non-nil means do a literal
299 Return the new string. Optional LITERAL non-nil means do a literal
277 replacement.
300 replacement.
278
301
279 This function bridges yet another pointless impedance gap between
302 This function bridges yet another pointless impedance gap between
280 XEmacs and GNU Emacs."
303 XEmacs and GNU Emacs."
281 (if (fboundp 'replace-in-string)
304 (hg-feature-cond
282 (replace-in-string str regexp newtext literal)
305 (xemacs (replace-in-string str regexp newtext literal))
283 (replace-regexp-in-string regexp newtext str nil literal)))
306 (t (replace-regexp-in-string regexp newtext str nil literal))))
284
307
285 (defsubst hg-strip (str)
308 (defsubst hg-strip (str)
286 "Strip leading and trailing blank lines from a string."
309 "Strip leading and trailing blank lines from a string."
287 (hg-replace-in-string (hg-replace-in-string str "[\r\n][ \t\r\n]*\\'" "")
310 (hg-replace-in-string (hg-replace-in-string str "[\r\n][ \t\r\n]*\\'" "")
288 "\\`[ \t\r\n]*[\r\n]" ""))
311 "\\`[ \t\r\n]*[\r\n]" ""))
289
312
290 (defsubst hg-chomp (str)
313 (defsubst hg-chomp (str)
291 "Strip trailing newlines from a string."
314 "Strip trailing newlines from a string."
292 (hg-replace-in-string str "[\r\n]+\\'" ""))
315 (hg-replace-in-string str "[\r\n]+\\'" ""))
293
316
294 (defun hg-run-command (command &rest args)
317 (defun hg-run-command (command &rest args)
295 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
318 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
296 The list ARGS contains a list of arguments to pass to the command."
319 The list ARGS contains a list of arguments to pass to the command."
297 (let* (exit-code
320 (let* (exit-code
298 (output
321 (output
299 (with-output-to-string
322 (with-output-to-string
300 (with-current-buffer
323 (with-current-buffer
301 standard-output
324 standard-output
302 (setq exit-code
325 (setq exit-code
303 (apply 'call-process command nil t nil args))))))
326 (apply 'call-process command nil t nil args))))))
304 (cons exit-code output)))
327 (cons exit-code output)))
305
328
306 (defun hg-run (command &rest args)
329 (defun hg-run (command &rest args)
307 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
330 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
308 (apply 'hg-run-command (hg-binary) command args))
331 (apply 'hg-run-command (hg-binary) command args))
309
332
310 (defun hg-run0 (command &rest args)
333 (defun hg-run0 (command &rest args)
311 "Run the Mercurial command COMMAND, returning its output.
334 "Run the Mercurial command COMMAND, returning its output.
312 If the command does not exit with a zero status code, raise an error."
335 If the command does not exit with a zero status code, raise an error."
313 (let ((res (apply 'hg-run-command (hg-binary) command args)))
336 (let ((res (apply 'hg-run-command (hg-binary) command args)))
314 (if (not (eq (car res) 0))
337 (if (not (eq (car res) 0))
315 (error "Mercurial command failed %s - exit code %s"
338 (error "Mercurial command failed %s - exit code %s"
316 (cons command args)
339 (cons command args)
317 (car res))
340 (car res))
318 (cdr res))))
341 (cdr res))))
319
342
320 (defmacro hg-do-across-repo (path &rest body)
343 (defmacro hg-do-across-repo (path &rest body)
321 (let ((root-name (gensym "root-"))
344 (let ((root-name (make-symbol "root-"))
322 (buf-name (gensym "buf-")))
345 (buf-name (make-symbol "buf-")))
323 `(let ((,root-name (hg-root ,path)))
346 `(let ((,root-name (hg-root ,path)))
324 (save-excursion
347 (save-excursion
325 (dolist (,buf-name (buffer-list))
348 (dolist (,buf-name (buffer-list))
326 (set-buffer ,buf-name)
349 (set-buffer ,buf-name)
327 (when (and hg-status (equal (hg-root buffer-file-name) ,root-name))
350 (when (and hg-status (equal (hg-root buffer-file-name) ,root-name))
328 ,@body))))))
351 ,@body))))))
329
352
330 (put 'hg-do-across-repo 'lisp-indent-function 1)
353 (put 'hg-do-across-repo 'lisp-indent-function 1)
331
354
332 (defun hg-sync-buffers (path)
355 (defun hg-sync-buffers (path)
333 "Sync buffers visiting PATH with their on-disk copies.
356 "Sync buffers visiting PATH with their on-disk copies.
334 If PATH is not being visited, but is under the repository root, sync
357 If PATH is not being visited, but is under the repository root, sync
335 all buffers visiting files in the repository."
358 all buffers visiting files in the repository."
336 (let ((buf (find-buffer-visiting path)))
359 (let ((buf (find-buffer-visiting path)))
337 (if buf
360 (if buf
338 (with-current-buffer buf
361 (with-current-buffer buf
339 (vc-buffer-sync))
362 (vc-buffer-sync))
340 (hg-do-across-repo path
363 (hg-do-across-repo path
341 (vc-buffer-sync)))))
364 (vc-buffer-sync)))))
342
365
343 (defun hg-buffer-commands (pnt)
366 (defun hg-buffer-commands (pnt)
344 "Use the properties of a character to do something sensible."
367 "Use the properties of a character to do something sensible."
345 (interactive "d")
368 (interactive "d")
346 (let ((rev (get-char-property pnt 'rev))
369 (let ((rev (get-char-property pnt 'rev))
347 (file (get-char-property pnt 'file))
370 (file (get-char-property pnt 'file)))
348 (date (get-char-property pnt 'date))
349 (user (get-char-property pnt 'user))
350 (host (get-char-property pnt 'host))
351 (prev-buf (current-buffer)))
352 (cond
371 (cond
353 (file
372 (file
354 (find-file-other-window file))
373 (find-file-other-window file))
355 (rev
374 (rev
356 (hg-diff hg-view-file-name rev rev prev-buf))
375 (hg-diff hg-view-file-name rev rev))
357 ((message "I don't know how to do that yet")))))
376 ((message "I don't know how to do that yet")))))
358
377
359 (defsubst hg-event-point (event)
378 (defsubst hg-event-point (event)
360 "Return the character position of the mouse event EVENT."
379 "Return the character position of the mouse event EVENT."
361 (if hg-running-xemacs
380 (hg-feature-cond (xemacs (event-point event))
362 (event-point event)
381 (t (posn-point (event-start event)))))
363 (posn-point (event-start event))))
364
382
365 (defsubst hg-event-window (event)
383 (defsubst hg-event-window (event)
366 "Return the window over which mouse event EVENT occurred."
384 "Return the window over which mouse event EVENT occurred."
367 (if hg-running-xemacs
385 (hg-feature-cond (xemacs (event-window event))
368 (event-window event)
386 (t (posn-window (event-start event)))))
369 (posn-window (event-start event))))
370
387
371 (defun hg-buffer-mouse-clicked (event)
388 (defun hg-buffer-mouse-clicked (event)
372 "Translate the mouse clicks in a HG log buffer to character events.
389 "Translate the mouse clicks in a HG log buffer to character events.
373 These are then handed off to `hg-buffer-commands'.
390 These are then handed off to `hg-buffer-commands'.
374
391
375 Handle frickin' frackin' gratuitous event-related incompatibilities."
392 Handle frickin' frackin' gratuitous event-related incompatibilities."
376 (interactive "e")
393 (interactive "e")
377 (select-window (hg-event-window event))
394 (select-window (hg-event-window event))
378 (hg-buffer-commands (hg-event-point event)))
395 (hg-buffer-commands (hg-event-point event)))
379
396
380 (unless (fboundp 'view-minor-mode)
381 (defun view-minor-mode (prev-buffer exit-func)
382 (view-mode)))
383
384 (defsubst hg-abbrev-file-name (file)
397 (defsubst hg-abbrev-file-name (file)
385 "Portable wrapper around abbreviate-file-name."
398 "Portable wrapper around abbreviate-file-name."
386 (if hg-running-xemacs
399 (hg-feature-cond (xemacs (abbreviate-file-name file t))
387 (abbreviate-file-name file t)
400 (t (abbreviate-file-name file))))
388 (abbreviate-file-name file)))
389
401
390 (defun hg-read-file-name (&optional prompt default)
402 (defun hg-read-file-name (&optional prompt default)
391 "Read a file or directory name, or a pattern, to use with a command."
403 "Read a file or directory name, or a pattern, to use with a command."
392 (save-excursion
404 (save-excursion
393 (while hg-prev-buffer
405 (while hg-prev-buffer
394 (set-buffer hg-prev-buffer))
406 (set-buffer hg-prev-buffer))
395 (let ((path (or default
407 (let ((path (or default
396 (buffer-file-name)
408 (buffer-file-name)
397 (expand-file-name default-directory))))
409 (expand-file-name default-directory))))
398 (if (or (not path) current-prefix-arg)
410 (if (or (not path) current-prefix-arg)
399 (expand-file-name
411 (expand-file-name
400 (eval (list* 'read-file-name
412 (eval (list* 'read-file-name
401 (format "File, directory or pattern%s: "
413 (format "File, directory or pattern%s: "
402 (or prompt ""))
414 (or prompt ""))
403 (and path (file-name-directory path))
415 (and path (file-name-directory path))
404 nil nil
416 nil nil
405 (and path (file-name-nondirectory path))
417 (and path (file-name-nondirectory path))
406 (if hg-running-xemacs
418 (hg-feature-cond
407 (cons (quote 'hg-file-history) nil)
419 (xemacs (cons (quote 'hg-file-history) nil))
408 nil))))
420 (t nil)))))
409 path))))
421 path))))
410
422
411 (defun hg-read-number (&optional prompt default)
423 (defun hg-read-number (&optional prompt default)
412 "Read a integer value."
424 "Read a integer value."
413 (save-excursion
425 (save-excursion
414 (if (or (not default) current-prefix-arg)
426 (if (or (not default) current-prefix-arg)
415 (string-to-number
427 (string-to-number
416 (eval (list* 'read-string
428 (eval (list* 'read-string
417 (or prompt "")
429 (or prompt "")
418 (if default (cons (format "%d" default) nil) nil))))
430 (if default (cons (format "%d" default) nil) nil))))
419 default)))
431 default)))
420
432
421 (defun hg-read-config ()
433 (defun hg-read-config ()
422 "Return an alist of (key . value) pairs of Mercurial config data.
434 "Return an alist of (key . value) pairs of Mercurial config data.
423 Each key is of the form (section . name)."
435 Each key is of the form (section . name)."
424 (let (items)
436 (let (items)
425 (dolist (line (split-string (hg-chomp (hg-run0 "debugconfig")) "\n") items)
437 (dolist (line (split-string (hg-chomp (hg-run0 "debugconfig")) "\n") items)
426 (string-match "^\\([^=]*\\)=\\(.*\\)" line)
438 (string-match "^\\([^=]*\\)=\\(.*\\)" line)
427 (let* ((left (substring line (match-beginning 1) (match-end 1)))
439 (let* ((left (substring line (match-beginning 1) (match-end 1)))
428 (right (substring line (match-beginning 2) (match-end 2)))
440 (right (substring line (match-beginning 2) (match-end 2)))
429 (key (split-string left "\\."))
441 (key (split-string left "\\."))
430 (value (hg-replace-in-string right "\\\\n" "\n" t)))
442 (value (hg-replace-in-string right "\\\\n" "\n" t)))
431 (setq items (cons (cons (cons (car key) (cadr key)) value) items))))))
443 (setq items (cons (cons (cons (car key) (cadr key)) value) items))))))
432
444
433 (defun hg-config-section (section config)
445 (defun hg-config-section (section config)
434 "Return an alist of (name . value) pairs for SECTION of CONFIG."
446 "Return an alist of (name . value) pairs for SECTION of CONFIG."
435 (let (items)
447 (let (items)
436 (dolist (item config items)
448 (dolist (item config items)
437 (when (equal (caar item) section)
449 (when (equal (caar item) section)
438 (setq items (cons (cons (cdar item) (cdr item)) items))))))
450 (setq items (cons (cons (cdar item) (cdr item)) items))))))
439
451
440 (defun hg-string-starts-with (sub str)
452 (defun hg-string-starts-with (sub str)
441 "Indicate whether string STR starts with the substring or character SUB."
453 "Indicate whether string STR starts with the substring or character SUB."
442 (if (not (stringp sub))
454 (if (not (stringp sub))
443 (and (> (length str) 0) (equal (elt str 0) sub))
455 (and (> (length str) 0) (equal (elt str 0) sub))
444 (let ((sub-len (length sub)))
456 (let ((sub-len (length sub)))
445 (and (<= sub-len (length str))
457 (and (<= sub-len (length str))
446 (string= sub (substring str 0 sub-len))))))
458 (string= sub (substring str 0 sub-len))))))
447
459
448 (defun hg-complete-repo (string predicate all)
460 (defun hg-complete-repo (string predicate all)
449 "Attempt to complete a repository name.
461 "Attempt to complete a repository name.
450 We complete on either symbolic names from Mercurial's config or real
462 We complete on either symbolic names from Mercurial's config or real
451 directory names from the file system. We do not penalise URLs."
463 directory names from the file system. We do not penalise URLs."
452 (or (if all
464 (or (if all
453 (all-completions string hg-repo-completion-table predicate)
465 (all-completions string hg-repo-completion-table predicate)
454 (try-completion string hg-repo-completion-table predicate))
466 (try-completion string hg-repo-completion-table predicate))
455 (let* ((str (expand-file-name string))
467 (let* ((str (expand-file-name string))
456 (dir (file-name-directory str))
468 (dir (file-name-directory str))
457 (file (file-name-nondirectory str)))
469 (file (file-name-nondirectory str)))
458 (if all
470 (if all
459 (let (completions)
471 (let (completions)
460 (dolist (name (delete "./" (file-name-all-completions file dir))
472 (dolist (name (delete "./" (file-name-all-completions file dir))
461 completions)
473 completions)
462 (let ((path (concat dir name)))
474 (let ((path (concat dir name)))
463 (when (file-directory-p path)
475 (when (file-directory-p path)
464 (setq completions (cons name completions))))))
476 (setq completions (cons name completions))))))
465 (let ((comp (file-name-completion file dir)))
477 (let ((comp (file-name-completion file dir)))
466 (if comp
478 (if comp
467 (hg-abbrev-file-name (concat dir comp))))))))
479 (hg-abbrev-file-name (concat dir comp))))))))
468
480
469 (defun hg-read-repo-name (&optional prompt initial-contents default)
481 (defun hg-read-repo-name (&optional prompt initial-contents default)
470 "Read the location of a repository."
482 "Read the location of a repository."
471 (save-excursion
483 (save-excursion
472 (while hg-prev-buffer
484 (while hg-prev-buffer
473 (set-buffer hg-prev-buffer))
485 (set-buffer hg-prev-buffer))
474 (let (hg-repo-completion-table)
486 (let (hg-repo-completion-table)
475 (if current-prefix-arg
487 (if current-prefix-arg
476 (progn
488 (progn
477 (dolist (path (hg-config-section "paths" (hg-read-config)))
489 (dolist (path (hg-config-section "paths" (hg-read-config)))
478 (setq hg-repo-completion-table
490 (setq hg-repo-completion-table
479 (cons (cons (car path) t) hg-repo-completion-table))
491 (cons (cons (car path) t) hg-repo-completion-table))
480 (unless (hg-string-starts-with directory-sep-char (cdr path))
492 (unless (hg-string-starts-with (hg-feature-cond
493 (xemacs directory-sep-char)
494 (t ?/))
495 (cdr path))
481 (setq hg-repo-completion-table
496 (setq hg-repo-completion-table
482 (cons (cons (cdr path) t) hg-repo-completion-table))))
497 (cons (cons (cdr path) t) hg-repo-completion-table))))
483 (completing-read (format "Repository%s: " (or prompt ""))
498 (completing-read (format "Repository%s: " (or prompt ""))
484 'hg-complete-repo
499 'hg-complete-repo
485 nil
500 nil
486 nil
501 nil
487 initial-contents
502 initial-contents
488 'hg-repo-history
503 'hg-repo-history
489 default))
504 default))
490 default))))
505 default))))
491
506
492 (defun hg-read-rev (&optional prompt default)
507 (defun hg-read-rev (&optional prompt default)
493 "Read a revision or tag, offering completions."
508 "Read a revision or tag, offering completions."
494 (save-excursion
509 (save-excursion
495 (while hg-prev-buffer
510 (while hg-prev-buffer
496 (set-buffer hg-prev-buffer))
511 (set-buffer hg-prev-buffer))
497 (let ((rev (or default "tip")))
512 (let ((rev (or default "tip")))
498 (if current-prefix-arg
513 (if current-prefix-arg
499 (let ((revs (split-string
514 (let ((revs (split-string
500 (hg-chomp
515 (hg-chomp
501 (hg-run0 "-q" "log" "-r"
516 (hg-run0 "-q" "log" "-l"
502 (format "-%d:tip" hg-rev-completion-limit)))
517 (format "%d" hg-rev-completion-limit)))
503 "[\n:]")))
518 "[\n:]")))
504 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
519 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
505 (setq revs (cons (car (split-string line "\\s-")) revs)))
520 (setq revs (cons (car (split-string line "\\s-")) revs)))
506 (completing-read (format "Revision%s (%s): "
521 (completing-read (format "Revision%s (%s): "
507 (or prompt "")
522 (or prompt "")
508 (or default "tip"))
523 (or default "tip"))
509 (map 'list 'cons revs revs)
524 (map 'list 'cons revs revs)
510 nil
525 nil
511 nil
526 nil
512 nil
527 nil
513 'hg-rev-history
528 'hg-rev-history
514 (or default "tip")))
529 (or default "tip")))
515 rev))))
530 rev))))
516
531
517 (defun hg-parents-for-mode-line (root)
532 (defun hg-parents-for-mode-line (root)
518 "Format the parents of the working directory for the mode line."
533 "Format the parents of the working directory for the mode line."
519 (let ((parents (split-string (hg-chomp
534 (let ((parents (split-string (hg-chomp
520 (hg-run0 "--cwd" root "parents" "--template"
535 (hg-run0 "--cwd" root "parents" "--template"
521 "{rev}\n")) "\n")))
536 "{rev}\n")) "\n")))
522 (mapconcat 'identity parents "+")))
537 (mapconcat 'identity parents "+")))
523
538
524 (defun hg-buffers-visiting-repo (&optional path)
539 (defun hg-buffers-visiting-repo (&optional path)
525 "Return a list of buffers visiting the repository containing PATH."
540 "Return a list of buffers visiting the repository containing PATH."
526 (let ((root-name (hg-root (or path (buffer-file-name))))
541 (let ((root-name (hg-root (or path (buffer-file-name))))
527 bufs)
542 bufs)
528 (save-excursion
543 (save-excursion
529 (dolist (buf (buffer-list) bufs)
544 (dolist (buf (buffer-list) bufs)
530 (set-buffer buf)
545 (set-buffer buf)
531 (let ((name (buffer-file-name)))
546 (let ((name (buffer-file-name)))
532 (when (and hg-status name (equal (hg-root name) root-name))
547 (when (and hg-status name (equal (hg-root name) root-name))
533 (setq bufs (cons buf bufs))))))))
548 (setq bufs (cons buf bufs))))))))
534
549
535 (defun hg-update-mode-lines (path)
550 (defun hg-update-mode-lines (path)
536 "Update the mode lines of all buffers visiting the same repository as PATH."
551 "Update the mode lines of all buffers visiting the same repository as PATH."
537 (let* ((root (hg-root path))
552 (let* ((root (hg-root path))
538 (parents (hg-parents-for-mode-line root)))
553 (parents (hg-parents-for-mode-line root)))
539 (save-excursion
554 (save-excursion
540 (dolist (info (hg-path-status
555 (dolist (info (hg-path-status
541 root
556 root
542 (mapcar
557 (mapcar
543 (function
558 (function
544 (lambda (buf)
559 (lambda (buf)
545 (substring (buffer-file-name buf) (length root))))
560 (substring (buffer-file-name buf) (length root))))
546 (hg-buffers-visiting-repo root))))
561 (hg-buffers-visiting-repo root))))
547 (let* ((name (car info))
562 (let* ((name (car info))
548 (status (cdr info))
563 (status (cdr info))
549 (buf (find-buffer-visiting (concat root name))))
564 (buf (find-buffer-visiting (concat root name))))
550 (when buf
565 (when buf
551 (set-buffer buf)
566 (set-buffer buf)
552 (hg-mode-line-internal status parents)))))))
567 (hg-mode-line-internal status parents)))))))
553
568
554
569
555 ;;; View mode bits.
570 ;;; View mode bits.
556
571
557 (defun hg-exit-view-mode (buf)
572 (defun hg-exit-view-mode (buf)
558 "Exit from hg-view-mode.
573 "Exit from hg-view-mode.
559 We delete the current window if entering hg-view-mode split the
574 We delete the current window if entering hg-view-mode split the
560 current frame."
575 current frame."
561 (when (and (eq buf (current-buffer))
576 (when (and (eq buf (current-buffer))
562 (> (length (window-list)) 1))
577 (> (length (window-list)) 1))
563 (delete-window))
578 (delete-window))
564 (when (buffer-live-p buf)
579 (when (buffer-live-p buf)
565 (kill-buffer buf)))
580 (kill-buffer buf)))
566
581
567 (defun hg-view-mode (prev-buffer &optional file-name)
582 (defun hg-view-mode (prev-buffer &optional file-name)
568 (goto-char (point-min))
583 (goto-char (point-min))
569 (set-buffer-modified-p nil)
584 (set-buffer-modified-p nil)
570 (toggle-read-only t)
585 (toggle-read-only t)
571 (view-minor-mode prev-buffer 'hg-exit-view-mode)
586 (hg-feature-cond (xemacs (view-minor-mode prev-buffer 'hg-exit-view-mode))
572 (use-local-map hg-view-mode-map)
587 (t (view-mode-enter nil 'hg-exit-view-mode)))
588 (setq hg-view-mode t)
573 (setq truncate-lines t)
589 (setq truncate-lines t)
574 (when file-name
590 (when file-name
575 (set (make-local-variable 'hg-view-file-name)
591 (setq hg-view-file-name
576 (hg-abbrev-file-name file-name))))
592 (hg-abbrev-file-name file-name))))
577
593
578 (defun hg-file-status (file)
594 (defun hg-file-status (file)
579 "Return status of FILE, or nil if FILE does not exist or is unmanaged."
595 "Return status of FILE, or nil if FILE does not exist or is unmanaged."
580 (let* ((s (hg-run "status" file))
596 (let* ((s (hg-run "status" file))
581 (exit (car s))
597 (exit (car s))
582 (output (cdr s)))
598 (output (cdr s)))
583 (if (= exit 0)
599 (if (= exit 0)
584 (let ((state (assoc (substring output 0 (min (length output) 2))
600 (let ((state (and (>= (length output) 2)
585 '(("M " . modified)
601 (= (aref output 1) ? )
586 ("A " . added)
602 (assq (aref output 0) hg-state-alist))))
587 ("R " . removed)
588 ("! " . deleted)
589 ("? " . nil)))))
590 (if state
603 (if state
591 (cdr state)
604 (cdr state)
592 'normal)))))
605 'normal)))))
593
606
594 (defun hg-path-status (root paths)
607 (defun hg-path-status (root paths)
595 "Return status of PATHS in repo ROOT as an alist.
608 "Return status of PATHS in repo ROOT as an alist.
596 Each entry is a pair (FILE-NAME . STATUS)."
609 Each entry is a pair (FILE-NAME . STATUS)."
597 (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
610 (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
598 result)
611 result)
599 (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
612 (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
600 (let (state name)
613 (let (state name)
601 (if (equal (substring entry 1 2) " ")
614 (cond ((= (aref entry 1) ? )
602 (setq state (cdr (assoc (substring entry 0 2)
615 (setq state (assq (aref entry 0) hg-state-alist)
603 '(("M " . modified)
616 name (substring entry 2)))
604 ("A " . added)
617 ((string-match "\\(.*\\): " entry)
605 ("R " . removed)
618 (setq name (match-string 1 entry))))
606 ("! " . deleted)
607 ("C " . normal)
608 ("I " . ignored)
609 ("? " . nil))))
610 name (substring entry 2))
611 (setq name (substring entry 0 (search ": " entry :from-end t))))
612 (setq result (cons (cons name state) result))))))
619 (setq result (cons (cons name state) result))))))
613
620
614 (defmacro hg-view-output (args &rest body)
621 (defmacro hg-view-output (args &rest body)
615 "Execute BODY in a clean buffer, then quickly display that buffer.
622 "Execute BODY in a clean buffer, then quickly display that buffer.
616 If the buffer contains one line, its contents are displayed in the
623 If the buffer contains one line, its contents are displayed in the
617 minibuffer. Otherwise, the buffer is displayed in view-mode.
624 minibuffer. Otherwise, the buffer is displayed in view-mode.
618 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
625 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
619 the name of the buffer to create, and FILE is the name of the file
626 the name of the buffer to create, and FILE is the name of the file
620 being viewed."
627 being viewed."
621 (let ((prev-buf (gensym "prev-buf-"))
628 (let ((prev-buf (make-symbol "prev-buf-"))
622 (v-b-name (car args))
629 (v-b-name (car args))
623 (v-m-rest (cdr args)))
630 (v-m-rest (cdr args)))
624 `(let ((view-buf-name ,v-b-name)
631 `(let ((view-buf-name ,v-b-name)
625 (,prev-buf (current-buffer)))
632 (,prev-buf (current-buffer)))
626 (get-buffer-create view-buf-name)
633 (get-buffer-create view-buf-name)
627 (kill-buffer view-buf-name)
634 (kill-buffer view-buf-name)
628 (get-buffer-create view-buf-name)
635 (get-buffer-create view-buf-name)
629 (set-buffer view-buf-name)
636 (set-buffer view-buf-name)
630 (save-excursion
637 (save-excursion
631 ,@body)
638 ,@body)
632 (case (count-lines (point-min) (point-max))
639 (case (count-lines (point-min) (point-max))
633 ((0)
640 ((0)
634 (kill-buffer view-buf-name)
641 (kill-buffer view-buf-name)
635 (message "(No output)"))
642 (message "(No output)"))
636 ((1)
643 ((1)
637 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
644 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
638 (kill-buffer view-buf-name)
645 (kill-buffer view-buf-name)
639 (message "%s" msg)))
646 (message "%s" msg)))
640 (t
647 (t
641 (pop-to-buffer view-buf-name)
648 (pop-to-buffer view-buf-name)
642 (setq hg-prev-buffer ,prev-buf)
649 (setq hg-prev-buffer ,prev-buf)
643 (hg-view-mode ,prev-buf ,@v-m-rest))))))
650 (hg-view-mode ,prev-buf ,@v-m-rest))))))
644
651
645 (put 'hg-view-output 'lisp-indent-function 1)
652 (put 'hg-view-output 'lisp-indent-function 1)
646
653
647 ;;; Context save and restore across revert and other operations.
654 ;;; Context save and restore across revert and other operations.
648
655
649 (defun hg-position-context (pos)
656 (defun hg-position-context (pos)
650 "Return information to help find the given position again."
657 "Return information to help find the given position again."
651 (let* ((end (min (point-max) (+ pos 98))))
658 (let* ((end (min (point-max) (+ pos 98))))
652 (list pos
659 (list pos
653 (buffer-substring (max (point-min) (- pos 2)) end)
660 (buffer-substring (max (point-min) (- pos 2)) end)
654 (- end pos))))
661 (- end pos))))
655
662
656 (defun hg-buffer-context ()
663 (defun hg-buffer-context ()
657 "Return information to help restore a user's editing context.
664 "Return information to help restore a user's editing context.
658 This is useful across reverts and merges, where a context is likely
665 This is useful across reverts and merges, where a context is likely
659 to have moved a little, but not really changed."
666 to have moved a little, but not really changed."
660 (let ((point-context (hg-position-context (point)))
667 (let ((point-context (hg-position-context (point)))
661 (mark-context (let ((mark (mark-marker)))
668 (mark-context (let ((mark (mark-marker)))
662 (and mark (hg-position-context mark)))))
669 (and mark (hg-position-context mark)))))
663 (list point-context mark-context)))
670 (list point-context mark-context)))
664
671
665 (defun hg-find-context (ctx)
672 (defun hg-find-context (ctx)
666 "Attempt to find a context in the given buffer.
673 "Attempt to find a context in the given buffer.
667 Always returns a valid, hopefully sane, position."
674 Always returns a valid, hopefully sane, position."
668 (let ((pos (nth 0 ctx))
675 (let ((pos (nth 0 ctx))
669 (str (nth 1 ctx))
676 (str (nth 1 ctx))
670 (fixup (nth 2 ctx)))
677 (fixup (nth 2 ctx)))
671 (save-excursion
678 (save-excursion
672 (goto-char (max (point-min) (- pos 15000)))
679 (goto-char (max (point-min) (- pos 15000)))
673 (if (and (not (equal str ""))
680 (if (and (not (equal str ""))
674 (search-forward str nil t))
681 (search-forward str nil t))
675 (- (point) fixup)
682 (- (point) fixup)
676 (max pos (point-min))))))
683 (max pos (point-min))))))
677
684
678 (defun hg-restore-context (ctx)
685 (defun hg-restore-context (ctx)
679 "Attempt to restore the user's editing context."
686 "Attempt to restore the user's editing context."
680 (let ((point-context (nth 0 ctx))
687 (let ((point-context (nth 0 ctx))
681 (mark-context (nth 1 ctx)))
688 (mark-context (nth 1 ctx)))
682 (goto-char (hg-find-context point-context))
689 (goto-char (hg-find-context point-context))
683 (when mark-context
690 (when mark-context
684 (set-mark (hg-find-context mark-context)))))
691 (set-mark (hg-find-context mark-context)))))
685
692
686
693
687 ;;; Hooks.
694 ;;; Hooks.
688
695
689 (defun hg-mode-line-internal (status parents)
696 (defun hg-mode-line-internal (status parents)
690 (setq hg-status status
697 (setq hg-status status
691 hg-mode (and status (concat " Hg:"
698 hg-mode (and status (concat " Hg:"
692 parents
699 parents
693 (cdr (assq status
700 (cdr (assq status
694 '((normal . "")
701 '((normal . "")
695 (removed . "r")
702 (removed . "r")
696 (added . "a")
703 (added . "a")
697 (deleted . "!")
704 (deleted . "!")
698 (modified . "m"))))))))
705 (modified . "m"))))))))
699
706
700 (defun hg-mode-line (&optional force)
707 (defun hg-mode-line (&optional force)
701 "Update the modeline with the current status of a file.
708 "Update the modeline with the current status of a file.
702 An update occurs if optional argument FORCE is non-nil,
709 An update occurs if optional argument FORCE is non-nil,
703 hg-update-modeline is non-nil, or we have not yet checked the state of
710 hg-update-modeline is non-nil, or we have not yet checked the state of
704 the file."
711 the file."
705 (let ((root (hg-root)))
712 (let ((root (hg-root)))
706 (when (and root (or force hg-update-modeline (not hg-mode)))
713 (when (and root (or force hg-update-modeline (not hg-mode)))
707 (let ((status (hg-file-status buffer-file-name))
714 (let ((status (hg-file-status buffer-file-name))
708 (parents (hg-parents-for-mode-line root)))
715 (parents (hg-parents-for-mode-line root)))
709 (hg-mode-line-internal status parents)
716 (hg-mode-line-internal status parents)
710 status))))
717 status))))
711
718
712 (defun hg-mode (&optional toggle)
719 (defun hg-mode (&optional toggle)
713 "Minor mode for Mercurial distributed SCM integration.
720 "Minor mode for Mercurial distributed SCM integration.
714
721
715 The Mercurial mode user interface is based on that of VC mode, so if
722 The Mercurial mode user interface is based on that of VC mode, so if
716 you're already familiar with VC, the same keybindings and functions
723 you're already familiar with VC, the same keybindings and functions
717 will generally work.
724 will generally work.
718
725
719 Below is a list of many common SCM tasks. In the list, `G/L\'
726 Below is a list of many common SCM tasks. In the list, `G/L\'
720 indicates whether a key binding is global (G) to a repository or
727 indicates whether a key binding is global (G) to a repository or
721 local (L) to a file. Many commands take a prefix argument.
728 local (L) to a file. Many commands take a prefix argument.
722
729
723 SCM Task G/L Key Binding Command Name
730 SCM Task G/L Key Binding Command Name
724 -------- --- ----------- ------------
731 -------- --- ----------- ------------
725 Help overview (what you are reading) G C-c h h hg-help-overview
732 Help overview (what you are reading) G C-c h h hg-help-overview
726
733
727 Tell Mercurial to manage a file G C-c h a hg-add
734 Tell Mercurial to manage a file G C-c h a hg-add
728 Commit changes to current file only L C-x v n hg-commit-start
735 Commit changes to current file only L C-x v n hg-commit-start
729 Undo changes to file since commit L C-x v u hg-revert-buffer
736 Undo changes to file since commit L C-x v u hg-revert-buffer
730
737
731 Diff file vs last checkin L C-x v = hg-diff
738 Diff file vs last checkin L C-x v = hg-diff
732
739
733 View file change history L C-x v l hg-log
740 View file change history L C-x v l hg-log
734 View annotated file L C-x v a hg-annotate
741 View annotated file L C-x v a hg-annotate
735
742
736 Diff repo vs last checkin G C-c h = hg-diff-repo
743 Diff repo vs last checkin G C-c h = hg-diff-repo
737 View status of files in repo G C-c h s hg-status
744 View status of files in repo G C-c h s hg-status
738 Commit all changes G C-c h c hg-commit-start
745 Commit all changes G C-c h c hg-commit-start
739
746
740 Undo all changes since last commit G C-c h U hg-revert
747 Undo all changes since last commit G C-c h U hg-revert
741 View repo change history G C-c h l hg-log-repo
748 View repo change history G C-c h l hg-log-repo
742
749
743 See changes that can be pulled G C-c h , hg-incoming
750 See changes that can be pulled G C-c h , hg-incoming
744 Pull changes G C-c h < hg-pull
751 Pull changes G C-c h < hg-pull
745 Update working directory after pull G C-c h u hg-update
752 Update working directory after pull G C-c h u hg-update
746 See changes that can be pushed G C-c h . hg-outgoing
753 See changes that can be pushed G C-c h . hg-outgoing
747 Push changes G C-c h > hg-push"
754 Push changes G C-c h > hg-push"
748 (unless vc-make-backup-files
755 (unless vc-make-backup-files
749 (set (make-local-variable 'backup-inhibited) t))
756 (set (make-local-variable 'backup-inhibited) t))
750 (run-hooks 'hg-mode-hook))
757 (run-hooks 'hg-mode-hook))
751
758
752 (defun hg-find-file-hook ()
759 (defun hg-find-file-hook ()
753 (ignore-errors
760 (ignore-errors
754 (when (hg-mode-line)
761 (when (hg-mode-line)
755 (hg-mode))))
762 (hg-mode))))
756
763
757 (add-hook 'find-file-hooks 'hg-find-file-hook)
764 (add-hook 'find-file-hooks 'hg-find-file-hook)
758
765
759 (defun hg-after-save-hook ()
766 (defun hg-after-save-hook ()
760 (let ((old-status hg-status))
767 (let ((old-status hg-status))
761 (hg-mode-line)
768 (hg-mode-line)
762 (if (and (not old-status) hg-status)
769 (if (and (not old-status) hg-status)
763 (hg-mode))))
770 (hg-mode))))
764
771
765 (add-hook 'after-save-hook 'hg-after-save-hook)
772 (add-hook 'after-save-hook 'hg-after-save-hook)
766
773
767
774
768 ;;; User interface functions.
775 ;;; User interface functions.
769
776
770 (defun hg-help-overview ()
777 (defun hg-help-overview ()
771 "This is an overview of the Mercurial SCM mode for Emacs.
778 "This is an overview of the Mercurial SCM mode for Emacs.
772
779
773 You can find the source code, license (GPL v2), and credits for this
780 You can find the source code, license (GPL v2), and credits for this
774 code by typing `M-x find-library mercurial RET'."
781 code by typing `M-x find-library mercurial RET'."
775 (interactive)
782 (interactive)
776 (hg-view-output ("Mercurial Help Overview")
783 (hg-view-output ("Mercurial Help Overview")
777 (insert (documentation 'hg-help-overview))
784 (insert (documentation 'hg-help-overview))
778 (let ((pos (point)))
785 (let ((pos (point)))
779 (insert (documentation 'hg-mode))
786 (insert (documentation 'hg-mode))
780 (goto-char pos)
787 (goto-char pos)
781 (end-of-line 1)
788 (end-of-line 1)
782 (delete-region pos (point)))
789 (delete-region pos (point)))
783 (let ((hg-root-dir (hg-root)))
790 (let ((hg-root-dir (hg-root)))
784 (if (not hg-root-dir)
791 (if (not hg-root-dir)
785 (error "error: %s: directory is not part of a Mercurial repository."
792 (error "error: %s: directory is not part of a Mercurial repository."
786 default-directory)
793 default-directory)
787 (cd hg-root-dir)))))
794 (cd hg-root-dir)))))
788
795
789 (defun hg-fix-paths ()
796 (defun hg-fix-paths ()
790 "Fix paths reported by some Mercurial commands."
797 "Fix paths reported by some Mercurial commands."
791 (save-excursion
798 (save-excursion
792 (goto-char (point-min))
799 (goto-char (point-min))
793 (while (re-search-forward " \\.\\.." nil t)
800 (while (re-search-forward " \\.\\.." nil t)
794 (replace-match " " nil nil))))
801 (replace-match " " nil nil))))
795
802
796 (defun hg-add (path)
803 (defun hg-add (path)
797 "Add PATH to the Mercurial repository on the next commit.
804 "Add PATH to the Mercurial repository on the next commit.
798 With a prefix argument, prompt for the path to add."
805 With a prefix argument, prompt for the path to add."
799 (interactive (list (hg-read-file-name " to add")))
806 (interactive (list (hg-read-file-name " to add")))
800 (let ((buf (current-buffer))
807 (let ((buf (current-buffer))
801 (update (equal buffer-file-name path)))
808 (update (equal buffer-file-name path)))
802 (hg-view-output (hg-output-buffer-name)
809 (hg-view-output (hg-output-buffer-name)
803 (apply 'call-process (hg-binary) nil t nil (list "add" path))
810 (apply 'call-process (hg-binary) nil t nil (list "add" path))
804 (hg-fix-paths)
811 (hg-fix-paths)
805 (goto-char (point-min))
812 (goto-char (point-min))
806 (cd (hg-root path)))
813 (cd (hg-root path)))
807 (when update
814 (when update
808 (unless vc-make-backup-files
815 (unless vc-make-backup-files
809 (set (make-local-variable 'backup-inhibited) t))
816 (set (make-local-variable 'backup-inhibited) t))
810 (with-current-buffer buf
817 (with-current-buffer buf
811 (hg-mode-line)))))
818 (hg-mode-line)))))
812
819
813 (defun hg-addremove ()
820 (defun hg-addremove ()
814 (interactive)
821 (interactive)
815 (error "not implemented"))
822 (error "not implemented"))
816
823
817 (defun hg-annotate ()
824 (defun hg-annotate ()
818 (interactive)
825 (interactive)
819 (error "not implemented"))
826 (error "not implemented"))
820
827
821 (defun hg-commit-toggle-file (pos)
828 (defun hg-commit-toggle-file (pos)
822 "Toggle whether or not the file at POS will be committed."
829 "Toggle whether or not the file at POS will be committed."
823 (interactive "d")
830 (interactive "d")
824 (save-excursion
831 (save-excursion
825 (goto-char pos)
832 (goto-char pos)
826 (let ((face (get-text-property pos 'face))
833 (let ((face (get-text-property pos 'face))
827 (inhibit-read-only t)
834 (inhibit-read-only t)
828 bol)
835 bol)
829 (beginning-of-line)
836 (beginning-of-line)
830 (setq bol (+ (point) 4))
837 (setq bol (+ (point) 4))
831 (end-of-line)
838 (end-of-line)
832 (if (eq face 'bold)
839 (if (eq face 'bold)
833 (progn
840 (progn
834 (remove-text-properties bol (point) '(face nil))
841 (remove-text-properties bol (point) '(face nil))
835 (message "%s will not be committed"
842 (message "%s will not be committed"
836 (buffer-substring bol (point))))
843 (buffer-substring bol (point))))
837 (add-text-properties bol (point) '(face bold))
844 (add-text-properties bol (point) '(face bold))
838 (message "%s will be committed"
845 (message "%s will be committed"
839 (buffer-substring bol (point)))))))
846 (buffer-substring bol (point)))))))
840
847
841 (defun hg-commit-mouse-clicked (event)
848 (defun hg-commit-mouse-clicked (event)
842 "Toggle whether or not the file at POS will be committed."
849 "Toggle whether or not the file at POS will be committed."
843 (interactive "@e")
850 (interactive "@e")
844 (hg-commit-toggle-file (hg-event-point event)))
851 (hg-commit-toggle-file (hg-event-point event)))
845
852
846 (defun hg-commit-kill ()
853 (defun hg-commit-kill ()
847 "Kill the commit currently being prepared."
854 "Kill the commit currently being prepared."
848 (interactive)
855 (interactive)
849 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this commit? "))
856 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this commit? "))
850 (let ((buf hg-prev-buffer))
857 (let ((buf hg-prev-buffer))
851 (kill-buffer nil)
858 (kill-buffer nil)
852 (switch-to-buffer buf))))
859 (switch-to-buffer buf))))
853
860
854 (defun hg-commit-finish ()
861 (defun hg-commit-finish ()
855 "Finish preparing a commit, and perform the actual commit.
862 "Finish preparing a commit, and perform the actual commit.
856 The hook hg-pre-commit-hook is run before anything else is done. If
863 The hook hg-pre-commit-hook is run before anything else is done. If
857 the commit message is empty and hg-commit-allow-empty-message is nil,
864 the commit message is empty and hg-commit-allow-empty-message is nil,
858 an error is raised. If the list of files to commit is empty and
865 an error is raised. If the list of files to commit is empty and
859 hg-commit-allow-empty-file-list is nil, an error is raised."
866 hg-commit-allow-empty-file-list is nil, an error is raised."
860 (interactive)
867 (interactive)
861 (let ((root hg-root))
868 (let ((root hg-root))
862 (save-excursion
869 (save-excursion
863 (run-hooks 'hg-pre-commit-hook)
870 (run-hooks 'hg-pre-commit-hook)
864 (goto-char (point-min))
871 (goto-char (point-min))
865 (search-forward hg-commit-message-start)
872 (search-forward hg-commit-message-start)
866 (let (message files)
873 (let (message files)
867 (let ((start (point)))
874 (let ((start (point)))
868 (goto-char (point-max))
875 (goto-char (point-max))
869 (search-backward hg-commit-message-end)
876 (search-backward hg-commit-message-end)
870 (setq message (hg-strip (buffer-substring start (point)))))
877 (setq message (hg-strip (buffer-substring start (point)))))
871 (when (and (= (length message) 0)
878 (when (and (= (length message) 0)
872 (not hg-commit-allow-empty-message))
879 (not hg-commit-allow-empty-message))
873 (error "Cannot proceed - commit message is empty"))
880 (error "Cannot proceed - commit message is empty"))
874 (forward-line 1)
881 (forward-line 1)
875 (beginning-of-line)
882 (beginning-of-line)
876 (while (< (point) (point-max))
883 (while (< (point) (point-max))
877 (let ((pos (+ (point) 4)))
884 (let ((pos (+ (point) 4)))
878 (end-of-line)
885 (end-of-line)
879 (when (eq (get-text-property pos 'face) 'bold)
886 (when (eq (get-text-property pos 'face) 'bold)
880 (end-of-line)
887 (end-of-line)
881 (setq files (cons (buffer-substring pos (point)) files))))
888 (setq files (cons (buffer-substring pos (point)) files))))
882 (forward-line 1))
889 (forward-line 1))
883 (when (and (= (length files) 0)
890 (when (and (= (length files) 0)
884 (not hg-commit-allow-empty-file-list))
891 (not hg-commit-allow-empty-file-list))
885 (error "Cannot proceed - no files to commit"))
892 (error "Cannot proceed - no files to commit"))
886 (setq message (concat message "\n"))
893 (setq message (concat message "\n"))
887 (apply 'hg-run0 "--cwd" hg-root "commit" "-m" message files))
894 (apply 'hg-run0 "--cwd" hg-root "commit" "-m" message files))
888 (let ((buf hg-prev-buffer))
895 (let ((buf hg-prev-buffer))
889 (kill-buffer nil)
896 (kill-buffer nil)
890 (switch-to-buffer buf))
897 (switch-to-buffer buf))
891 (hg-update-mode-lines root))))
898 (hg-update-mode-lines root))))
892
899
893 (defun hg-commit-mode ()
900 (defun hg-commit-mode ()
894 "Mode for describing a commit of changes to a Mercurial repository.
901 "Mode for describing a commit of changes to a Mercurial repository.
895 This involves two actions: describing the changes with a commit
902 This involves two actions: describing the changes with a commit
896 message, and choosing the files to commit.
903 message, and choosing the files to commit.
897
904
898 To describe the commit, simply type some text in the designated area.
905 To describe the commit, simply type some text in the designated area.
899
906
900 By default, all modified, added and removed files are selected for
907 By default, all modified, added and removed files are selected for
901 committing. Files that will be committed are displayed in bold face\;
908 committing. Files that will be committed are displayed in bold face\;
902 those that will not are displayed in normal face.
909 those that will not are displayed in normal face.
903
910
904 To toggle whether a file will be committed, move the cursor over a
911 To toggle whether a file will be committed, move the cursor over a
905 particular file and hit space or return. Alternatively, middle click
912 particular file and hit space or return. Alternatively, middle click
906 on the file.
913 on the file.
907
914
908 Key bindings
915 Key bindings
909 ------------
916 ------------
910 \\[hg-commit-finish] proceed with commit
917 \\[hg-commit-finish] proceed with commit
911 \\[hg-commit-kill] kill commit
918 \\[hg-commit-kill] kill commit
912
919
913 \\[hg-diff-repo] view diff of pending changes"
920 \\[hg-diff-repo] view diff of pending changes"
914 (interactive)
921 (interactive)
915 (use-local-map hg-commit-mode-map)
922 (use-local-map hg-commit-mode-map)
916 (set-syntax-table text-mode-syntax-table)
923 (set-syntax-table text-mode-syntax-table)
917 (setq local-abbrev-table text-mode-abbrev-table
924 (setq local-abbrev-table text-mode-abbrev-table
918 major-mode 'hg-commit-mode
925 major-mode 'hg-commit-mode
919 mode-name "Hg-Commit")
926 mode-name "Hg-Commit")
920 (set-buffer-modified-p nil)
927 (set-buffer-modified-p nil)
921 (setq buffer-undo-list nil)
928 (setq buffer-undo-list nil)
922 (run-hooks 'text-mode-hook 'hg-commit-mode-hook))
929 (run-hooks 'text-mode-hook 'hg-commit-mode-hook))
923
930
924 (defun hg-commit-start ()
931 (defun hg-commit-start ()
925 "Prepare a commit of changes to the repository containing the current file."
932 "Prepare a commit of changes to the repository containing the current file."
926 (interactive)
933 (interactive)
927 (while hg-prev-buffer
934 (while hg-prev-buffer
928 (set-buffer hg-prev-buffer))
935 (set-buffer hg-prev-buffer))
929 (let ((root (hg-root))
936 (let ((root (hg-root))
930 (prev-buffer (current-buffer))
937 (prev-buffer (current-buffer))
931 modified-files)
938 modified-files)
932 (unless root
939 (unless root
933 (error "Cannot commit outside a repository!"))
940 (error "Cannot commit outside a repository!"))
934 (hg-sync-buffers root)
941 (hg-sync-buffers root)
935 (setq modified-files (hg-chomp (hg-run0 "--cwd" root "status" "-arm")))
942 (setq modified-files (hg-chomp (hg-run0 "--cwd" root "status" "-arm")))
936 (when (and (= (length modified-files) 0)
943 (when (and (= (length modified-files) 0)
937 (not hg-commit-allow-empty-file-list))
944 (not hg-commit-allow-empty-file-list))
938 (error "No pending changes to commit"))
945 (error "No pending changes to commit"))
939 (let* ((buf-name (format "*Mercurial: Commit %s*" root)))
946 (let* ((buf-name (format "*Mercurial: Commit %s*" root)))
940 (pop-to-buffer (get-buffer-create buf-name))
947 (pop-to-buffer (get-buffer-create buf-name))
941 (when (= (point-min) (point-max))
948 (when (= (point-min) (point-max))
942 (set (make-local-variable 'hg-root) root)
949 (set (make-local-variable 'hg-root) root)
943 (setq hg-prev-buffer prev-buffer)
950 (setq hg-prev-buffer prev-buffer)
944 (insert "\n")
951 (insert "\n")
945 (let ((bol (point)))
952 (let ((bol (point)))
946 (insert hg-commit-message-end)
953 (insert hg-commit-message-end)
947 (add-text-properties bol (point) '(face bold-italic)))
954 (add-text-properties bol (point) '(face bold-italic)))
948 (let ((file-area (point)))
955 (let ((file-area (point)))
949 (insert modified-files)
956 (insert modified-files)
950 (goto-char file-area)
957 (goto-char file-area)
951 (while (< (point) (point-max))
958 (while (< (point) (point-max))
952 (let ((bol (point)))
959 (let ((bol (point)))
953 (forward-char 1)
960 (forward-char 1)
954 (insert " ")
961 (insert " ")
955 (end-of-line)
962 (end-of-line)
956 (add-text-properties (+ bol 4) (point)
963 (add-text-properties (+ bol 4) (point)
957 '(face bold mouse-face highlight)))
964 '(face bold mouse-face highlight)))
958 (forward-line 1))
965 (forward-line 1))
959 (goto-char file-area)
966 (goto-char file-area)
960 (add-text-properties (point) (point-max)
967 (add-text-properties (point) (point-max)
961 `(keymap ,hg-commit-mode-file-map))
968 `(keymap ,hg-commit-mode-file-map))
962 (goto-char (point-min))
969 (goto-char (point-min))
963 (insert hg-commit-message-start)
970 (insert hg-commit-message-start)
964 (add-text-properties (point-min) (point) '(face bold-italic))
971 (add-text-properties (point-min) (point) '(face bold-italic))
965 (insert "\n\n")
972 (insert "\n\n")
966 (forward-line -1)
973 (forward-line -1)
967 (save-excursion
974 (save-excursion
968 (goto-char (point-max))
975 (goto-char (point-max))
969 (search-backward hg-commit-message-end)
976 (search-backward hg-commit-message-end)
970 (add-text-properties (match-beginning 0) (point-max)
977 (add-text-properties (match-beginning 0) (point-max)
971 '(read-only t))
978 '(read-only t))
972 (goto-char (point-min))
979 (goto-char (point-min))
973 (search-forward hg-commit-message-start)
980 (search-forward hg-commit-message-start)
974 (add-text-properties (match-beginning 0) (match-end 0)
981 (add-text-properties (match-beginning 0) (match-end 0)
975 '(read-only t)))
982 '(read-only t)))
976 (hg-commit-mode)
983 (hg-commit-mode)
977 (cd root))))))
984 (cd root))))))
978
985
979 (defun hg-diff (path &optional rev1 rev2)
986 (defun hg-diff (path &optional rev1 rev2)
980 "Show the differences between REV1 and REV2 of PATH.
987 "Show the differences between REV1 and REV2 of PATH.
981 When called interactively, the default behaviour is to treat REV1 as
988 When called interactively, the default behaviour is to treat REV1 as
982 the \"parent\" revision, REV2 as the current edited version of the file, and
989 the \"parent\" revision, REV2 as the current edited version of the file, and
983 PATH as the file edited in the current buffer.
990 PATH as the file edited in the current buffer.
984 With a prefix argument, prompt for all of these."
991 With a prefix argument, prompt for all of these."
985 (interactive (list (hg-read-file-name " to diff")
992 (interactive (list (hg-read-file-name " to diff")
986 (let ((rev1 (hg-read-rev " to start with" 'parent)))
993 (let ((rev1 (hg-read-rev " to start with" 'parent)))
987 (and (not (eq rev1 'parent)) rev1))
994 (and (not (eq rev1 'parent)) rev1))
988 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
995 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
989 (and (not (eq rev2 'working-dir)) rev2))))
996 (and (not (eq rev2 'working-dir)) rev2))))
990 (hg-sync-buffers path)
997 (hg-sync-buffers path)
991 (let ((a-path (hg-abbrev-file-name path))
998 (let ((a-path (hg-abbrev-file-name path))
992 ;; none revision is specified explicitly
999 ;; none revision is specified explicitly
993 (none (and (not rev1) (not rev2)))
1000 (none (and (not rev1) (not rev2)))
994 ;; only one revision is specified explicitly
1001 ;; only one revision is specified explicitly
995 (one (or (and (or (equal rev1 rev2) (not rev2)) rev1)
1002 (one (or (and (or (equal rev1 rev2) (not rev2)) rev1)
996 (and (not rev1) rev2)))
1003 (and (not rev1) rev2)))
997 diff)
1004 diff)
998 (hg-view-output ((cond
1005 (hg-view-output ((cond
999 (none
1006 (none
1000 (format "Mercurial: Diff against parent of %s" a-path))
1007 (format "Mercurial: Diff against parent of %s" a-path))
1001 (one
1008 (one
1002 (format "Mercurial: Diff of rev %s of %s" one a-path))
1009 (format "Mercurial: Diff of rev %s of %s" one a-path))
1003 (t
1010 (t
1004 (format "Mercurial: Diff from rev %s to %s of %s"
1011 (format "Mercurial: Diff from rev %s to %s of %s"
1005 rev1 rev2 a-path))))
1012 rev1 rev2 a-path))))
1006 (cond
1013 (cond
1007 (none
1014 (none
1008 (call-process (hg-binary) nil t nil "diff" path))
1015 (call-process (hg-binary) nil t nil "diff" path))
1009 (one
1016 (one
1010 (call-process (hg-binary) nil t nil "diff" "-r" one path))
1017 (call-process (hg-binary) nil t nil "diff" "-r" one path))
1011 (t
1018 (t
1012 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)))
1019 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)))
1013 (diff-mode)
1020 (diff-mode)
1014 (setq diff (not (= (point-min) (point-max))))
1021 (setq diff (not (= (point-min) (point-max))))
1015 (font-lock-fontify-buffer)
1022 (font-lock-fontify-buffer)
1016 (cd (hg-root path)))
1023 (cd (hg-root path)))
1017 diff))
1024 diff))
1018
1025
1019 (defun hg-diff-repo (path &optional rev1 rev2)
1026 (defun hg-diff-repo (path &optional rev1 rev2)
1020 "Show the differences between REV1 and REV2 of repository containing PATH.
1027 "Show the differences between REV1 and REV2 of repository containing PATH.
1021 When called interactively, the default behaviour is to treat REV1 as
1028 When called interactively, the default behaviour is to treat REV1 as
1022 the \"parent\" revision, REV2 as the current edited version of the file, and
1029 the \"parent\" revision, REV2 as the current edited version of the file, and
1023 PATH as the `hg-root' of the current buffer.
1030 PATH as the `hg-root' of the current buffer.
1024 With a prefix argument, prompt for all of these."
1031 With a prefix argument, prompt for all of these."
1025 (interactive (list (hg-read-file-name " to diff")
1032 (interactive (list (hg-read-file-name " to diff")
1026 (let ((rev1 (hg-read-rev " to start with" 'parent)))
1033 (let ((rev1 (hg-read-rev " to start with" 'parent)))
1027 (and (not (eq rev1 'parent)) rev1))
1034 (and (not (eq rev1 'parent)) rev1))
1028 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
1035 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
1029 (and (not (eq rev2 'working-dir)) rev2))))
1036 (and (not (eq rev2 'working-dir)) rev2))))
1030 (hg-diff (hg-root path) rev1 rev2))
1037 (hg-diff (hg-root path) rev1 rev2))
1031
1038
1032 (defun hg-forget (path)
1039 (defun hg-forget (path)
1033 "Lose track of PATH, which has been added, but not yet committed.
1040 "Lose track of PATH, which has been added, but not yet committed.
1034 This will prevent the file from being incorporated into the Mercurial
1041 This will prevent the file from being incorporated into the Mercurial
1035 repository on the next commit.
1042 repository on the next commit.
1036 With a prefix argument, prompt for the path to forget."
1043 With a prefix argument, prompt for the path to forget."
1037 (interactive (list (hg-read-file-name " to forget")))
1044 (interactive (list (hg-read-file-name " to forget")))
1038 (let ((buf (current-buffer))
1045 (let ((buf (current-buffer))
1039 (update (equal buffer-file-name path)))
1046 (update (equal buffer-file-name path)))
1040 (hg-view-output (hg-output-buffer-name)
1047 (hg-view-output (hg-output-buffer-name)
1041 (apply 'call-process (hg-binary) nil t nil (list "forget" path))
1048 (apply 'call-process (hg-binary) nil t nil (list "forget" path))
1042 ;; "hg forget" shows pathes relative NOT TO ROOT BUT TO REPOSITORY
1049 ;; "hg forget" shows pathes relative NOT TO ROOT BUT TO REPOSITORY
1043 (hg-fix-paths)
1050 (hg-fix-paths)
1044 (goto-char (point-min))
1051 (goto-char (point-min))
1045 (cd (hg-root path)))
1052 (cd (hg-root path)))
1046 (when update
1053 (when update
1047 (with-current-buffer buf
1054 (with-current-buffer buf
1048 (when (local-variable-p 'backup-inhibited)
1055 (when (local-variable-p 'backup-inhibited)
1049 (kill-local-variable 'backup-inhibited))
1056 (kill-local-variable 'backup-inhibited))
1050 (hg-mode-line)))))
1057 (hg-mode-line)))))
1051
1058
1052 (defun hg-incoming (&optional repo)
1059 (defun hg-incoming (&optional repo)
1053 "Display changesets present in REPO that are not present locally."
1060 "Display changesets present in REPO that are not present locally."
1054 (interactive (list (hg-read-repo-name " where changes would come from")))
1061 (interactive (list (hg-read-repo-name " where changes would come from")))
1055 (hg-view-output ((format "Mercurial: Incoming from %s to %s"
1062 (hg-view-output ((format "Mercurial: Incoming from %s to %s"
1056 (hg-abbrev-file-name (hg-root))
1063 (hg-abbrev-file-name (hg-root))
1057 (hg-abbrev-file-name
1064 (hg-abbrev-file-name
1058 (or repo hg-incoming-repository))))
1065 (or repo hg-incoming-repository))))
1059 (call-process (hg-binary) nil t nil "incoming"
1066 (call-process (hg-binary) nil t nil "incoming"
1060 (or repo hg-incoming-repository))
1067 (or repo hg-incoming-repository))
1061 (hg-log-mode)
1068 (hg-log-mode)
1062 (cd (hg-root))))
1069 (cd (hg-root))))
1063
1070
1064 (defun hg-init ()
1071 (defun hg-init ()
1065 (interactive)
1072 (interactive)
1066 (error "not implemented"))
1073 (error "not implemented"))
1067
1074
1068 (defun hg-log-mode ()
1075 (defun hg-log-mode ()
1069 "Mode for viewing a Mercurial change log."
1076 "Mode for viewing a Mercurial change log."
1070 (goto-char (point-min))
1077 (goto-char (point-min))
1071 (when (looking-at "^searching for changes.*$")
1078 (when (looking-at "^searching for changes.*$")
1072 (delete-region (match-beginning 0) (match-end 0)))
1079 (delete-region (match-beginning 0) (match-end 0)))
1073 (run-hooks 'hg-log-mode-hook))
1080 (run-hooks 'hg-log-mode-hook))
1074
1081
1075 (defun hg-log (path &optional rev1 rev2 log-limit)
1082 (defun hg-log (path &optional rev1 rev2 log-limit)
1076 "Display the revision history of PATH.
1083 "Display the revision history of PATH.
1077 History is displayed between REV1 and REV2.
1084 History is displayed between REV1 and REV2.
1078 Number of displayed changesets is limited to LOG-LIMIT.
1085 Number of displayed changesets is limited to LOG-LIMIT.
1079 REV1 defaults to the tip, while
1086 REV1 defaults to the tip, while
1080 REV2 defaults to `hg-rev-completion-limit' changes from the tip revision.
1087 REV2 defaults to `hg-rev-completion-limit' changes from the tip revision.
1081 LOG-LIMIT defaults to `hg-log-limit'.
1088 LOG-LIMIT defaults to `hg-log-limit'.
1082 With a prefix argument, prompt for each parameter."
1089 With a prefix argument, prompt for each parameter."
1083 (interactive (list (hg-read-file-name " to log")
1090 (interactive (list (hg-read-file-name " to log")
1084 (hg-read-rev " to start with"
1091 (hg-read-rev " to start with"
1085 "tip")
1092 "tip")
1086 (hg-read-rev " to end with"
1093 (hg-read-rev " to end with"
1087 (format "%d" (- hg-rev-completion-limit)))
1094 (format "%d" (- hg-rev-completion-limit)))
1088 (hg-read-number "Output limited to: "
1095 (hg-read-number "Output limited to: "
1089 hg-log-limit)))
1096 hg-log-limit)))
1090 (let ((a-path (hg-abbrev-file-name path))
1097 (let ((a-path (hg-abbrev-file-name path))
1091 (r1 (or rev1 (format "-%d" hg-rev-completion-limit)))
1098 (r1 (or rev1 (format "-%d" hg-rev-completion-limit)))
1092 (r2 (or rev2 rev1 "tip"))
1099 (r2 (or rev2 rev1 "tip"))
1093 (limit (format "%d" (or log-limit hg-log-limit))))
1100 (limit (format "%d" (or log-limit hg-log-limit))))
1094 (hg-view-output ((if (equal r1 r2)
1101 (hg-view-output ((if (equal r1 r2)
1095 (format "Mercurial: Log of rev %s of %s" rev1 a-path)
1102 (format "Mercurial: Log of rev %s of %s" rev1 a-path)
1096 (format
1103 (format
1097 "Mercurial: at most %s log(s) from rev %s to %s of %s"
1104 "Mercurial: at most %s log(s) from rev %s to %s of %s"
1098 limit r1 r2 a-path)))
1105 limit r1 r2 a-path)))
1099 (eval (list* 'call-process (hg-binary) nil t nil
1106 (eval (list* 'call-process (hg-binary) nil t nil
1100 "log"
1107 "log"
1101 "-r" (format "%s:%s" r1 r2)
1108 "-r" (format "%s:%s" r1 r2)
1102 "-l" limit
1109 "-l" limit
1103 (if (> (length path) (length (hg-root path)))
1110 (if (> (length path) (length (hg-root path)))
1104 (cons path nil)
1111 (cons path nil)
1105 nil)))
1112 nil)))
1106 (hg-log-mode)
1113 (hg-log-mode)
1107 (cd (hg-root path)))))
1114 (cd (hg-root path)))))
1108
1115
1109 (defun hg-log-repo (path &optional rev1 rev2 log-limit)
1116 (defun hg-log-repo (path &optional rev1 rev2 log-limit)
1110 "Display the revision history of the repository containing PATH.
1117 "Display the revision history of the repository containing PATH.
1111 History is displayed between REV1 and REV2.
1118 History is displayed between REV1 and REV2.
1112 Number of displayed changesets is limited to LOG-LIMIT,
1119 Number of displayed changesets is limited to LOG-LIMIT,
1113 REV1 defaults to the tip, while
1120 REV1 defaults to the tip, while
1114 REV2 defaults to `hg-rev-completion-limit' changes from the tip revision.
1121 REV2 defaults to `hg-rev-completion-limit' changes from the tip revision.
1115 LOG-LIMIT defaults to `hg-log-limit'.
1122 LOG-LIMIT defaults to `hg-log-limit'.
1116 With a prefix argument, prompt for each parameter."
1123 With a prefix argument, prompt for each parameter."
1117 (interactive (list (hg-read-file-name " to log")
1124 (interactive (list (hg-read-file-name " to log")
1118 (hg-read-rev " to start with"
1125 (hg-read-rev " to start with"
1119 "tip")
1126 "tip")
1120 (hg-read-rev " to end with"
1127 (hg-read-rev " to end with"
1121 (format "%d" (- hg-rev-completion-limit)))
1128 (format "%d" (- hg-rev-completion-limit)))
1122 (hg-read-number "Output limited to: "
1129 (hg-read-number "Output limited to: "
1123 hg-log-limit)))
1130 hg-log-limit)))
1124 (hg-log (hg-root path) rev1 rev2 log-limit))
1131 (hg-log (hg-root path) rev1 rev2 log-limit))
1125
1132
1126 (defun hg-outgoing (&optional repo)
1133 (defun hg-outgoing (&optional repo)
1127 "Display changesets present locally that are not present in REPO."
1134 "Display changesets present locally that are not present in REPO."
1128 (interactive (list (hg-read-repo-name " where changes would go to" nil
1135 (interactive (list (hg-read-repo-name " where changes would go to" nil
1129 hg-outgoing-repository)))
1136 hg-outgoing-repository)))
1130 (hg-view-output ((format "Mercurial: Outgoing from %s to %s"
1137 (hg-view-output ((format "Mercurial: Outgoing from %s to %s"
1131 (hg-abbrev-file-name (hg-root))
1138 (hg-abbrev-file-name (hg-root))
1132 (hg-abbrev-file-name
1139 (hg-abbrev-file-name
1133 (or repo hg-outgoing-repository))))
1140 (or repo hg-outgoing-repository))))
1134 (call-process (hg-binary) nil t nil "outgoing"
1141 (call-process (hg-binary) nil t nil "outgoing"
1135 (or repo hg-outgoing-repository))
1142 (or repo hg-outgoing-repository))
1136 (hg-log-mode)
1143 (hg-log-mode)
1137 (cd (hg-root))))
1144 (cd (hg-root))))
1138
1145
1139 (defun hg-pull (&optional repo)
1146 (defun hg-pull (&optional repo)
1140 "Pull changes from repository REPO.
1147 "Pull changes from repository REPO.
1141 This does not update the working directory."
1148 This does not update the working directory."
1142 (interactive (list (hg-read-repo-name " to pull from")))
1149 (interactive (list (hg-read-repo-name " to pull from")))
1143 (hg-view-output ((format "Mercurial: Pull to %s from %s"
1150 (hg-view-output ((format "Mercurial: Pull to %s from %s"
1144 (hg-abbrev-file-name (hg-root))
1151 (hg-abbrev-file-name (hg-root))
1145 (hg-abbrev-file-name
1152 (hg-abbrev-file-name
1146 (or repo hg-incoming-repository))))
1153 (or repo hg-incoming-repository))))
1147 (call-process (hg-binary) nil t nil "pull"
1154 (call-process (hg-binary) nil t nil "pull"
1148 (or repo hg-incoming-repository))
1155 (or repo hg-incoming-repository))
1149 (cd (hg-root))))
1156 (cd (hg-root))))
1150
1157
1151 (defun hg-push (&optional repo)
1158 (defun hg-push (&optional repo)
1152 "Push changes to repository REPO."
1159 "Push changes to repository REPO."
1153 (interactive (list (hg-read-repo-name " to push to")))
1160 (interactive (list (hg-read-repo-name " to push to")))
1154 (hg-view-output ((format "Mercurial: Push from %s to %s"
1161 (hg-view-output ((format "Mercurial: Push from %s to %s"
1155 (hg-abbrev-file-name (hg-root))
1162 (hg-abbrev-file-name (hg-root))
1156 (hg-abbrev-file-name
1163 (hg-abbrev-file-name
1157 (or repo hg-outgoing-repository))))
1164 (or repo hg-outgoing-repository))))
1158 (call-process (hg-binary) nil t nil "push"
1165 (call-process (hg-binary) nil t nil "push"
1159 (or repo hg-outgoing-repository))
1166 (or repo hg-outgoing-repository))
1160 (cd (hg-root))))
1167 (cd (hg-root))))
1161
1168
1162 (defun hg-revert-buffer-internal ()
1169 (defun hg-revert-buffer-internal ()
1163 (let ((ctx (hg-buffer-context)))
1170 (let ((ctx (hg-buffer-context)))
1164 (message "Reverting %s..." buffer-file-name)
1171 (message "Reverting %s..." buffer-file-name)
1165 (hg-run0 "revert" buffer-file-name)
1172 (hg-run0 "revert" buffer-file-name)
1166 (revert-buffer t t t)
1173 (revert-buffer t t t)
1167 (hg-restore-context ctx)
1174 (hg-restore-context ctx)
1168 (hg-mode-line)
1175 (hg-mode-line)
1169 (message "Reverting %s...done" buffer-file-name)))
1176 (message "Reverting %s...done" buffer-file-name)))
1170
1177
1171 (defun hg-revert-buffer ()
1178 (defun hg-revert-buffer ()
1172 "Revert current buffer's file back to the latest committed version.
1179 "Revert current buffer's file back to the latest committed version.
1173 If the file has not changed, nothing happens. Otherwise, this
1180 If the file has not changed, nothing happens. Otherwise, this
1174 displays a diff and asks for confirmation before reverting."
1181 displays a diff and asks for confirmation before reverting."
1175 (interactive)
1182 (interactive)
1176 (let ((vc-suppress-confirm nil)
1183 (let ((vc-suppress-confirm nil)
1177 (obuf (current-buffer))
1184 (obuf (current-buffer))
1178 diff)
1185 diff)
1179 (vc-buffer-sync)
1186 (vc-buffer-sync)
1180 (unwind-protect
1187 (unwind-protect
1181 (setq diff (hg-diff buffer-file-name))
1188 (setq diff (hg-diff buffer-file-name))
1182 (when diff
1189 (when diff
1183 (unless (yes-or-no-p "Discard changes? ")
1190 (unless (yes-or-no-p "Discard changes? ")
1184 (error "Revert cancelled")))
1191 (error "Revert cancelled")))
1185 (when diff
1192 (when diff
1186 (let ((buf (current-buffer)))
1193 (let ((buf (current-buffer)))
1187 (delete-window (selected-window))
1194 (delete-window (selected-window))
1188 (kill-buffer buf))))
1195 (kill-buffer buf))))
1189 (set-buffer obuf)
1196 (set-buffer obuf)
1190 (when diff
1197 (when diff
1191 (hg-revert-buffer-internal))))
1198 (hg-revert-buffer-internal))))
1192
1199
1193 (defun hg-root (&optional path)
1200 (defun hg-root (&optional path)
1194 "Return the root of the repository that contains the given path.
1201 "Return the root of the repository that contains the given path.
1195 If the path is outside a repository, return nil.
1202 If the path is outside a repository, return nil.
1196 When called interactively, the root is printed. A prefix argument
1203 When called interactively, the root is printed. A prefix argument
1197 prompts for a path to check."
1204 prompts for a path to check."
1198 (interactive (list (hg-read-file-name)))
1205 (interactive (list (hg-read-file-name)))
1199 (if (or path (not hg-root))
1206 (if (or path (not hg-root))
1200 (let ((root (do ((prev nil dir)
1207 (let ((root (do ((prev nil dir)
1201 (dir (file-name-directory
1208 (dir (file-name-directory
1202 (or
1209 (or
1203 path
1210 path
1204 buffer-file-name
1211 buffer-file-name
1205 (expand-file-name default-directory)))
1212 (expand-file-name default-directory)))
1206 (file-name-directory (directory-file-name dir))))
1213 (file-name-directory (directory-file-name dir))))
1207 ((equal prev dir))
1214 ((equal prev dir))
1208 (when (file-directory-p (concat dir ".hg"))
1215 (when (file-directory-p (concat dir ".hg"))
1209 (return dir)))))
1216 (return dir)))))
1210 (when (interactive-p)
1217 (when (interactive-p)
1211 (if root
1218 (if root
1212 (message "The root of this repository is `%s'." root)
1219 (message "The root of this repository is `%s'." root)
1213 (message "The path `%s' is not in a Mercurial repository."
1220 (message "The path `%s' is not in a Mercurial repository."
1214 (hg-abbrev-file-name path))))
1221 (hg-abbrev-file-name path))))
1215 root)
1222 root)
1216 hg-root))
1223 hg-root))
1217
1224
1218 (defun hg-cwd (&optional path)
1225 (defun hg-cwd (&optional path)
1219 "Return the current directory of PATH within the repository."
1226 "Return the current directory of PATH within the repository."
1220 (do ((stack nil (cons (file-name-nondirectory
1227 (do ((stack nil (cons (file-name-nondirectory
1221 (directory-file-name dir))
1228 (directory-file-name dir))
1222 stack))
1229 stack))
1223 (prev nil dir)
1230 (prev nil dir)
1224 (dir (file-name-directory (or path buffer-file-name
1231 (dir (file-name-directory (or path buffer-file-name
1225 (expand-file-name default-directory)))
1232 (expand-file-name default-directory)))
1226 (file-name-directory (directory-file-name dir))))
1233 (file-name-directory (directory-file-name dir))))
1227 ((equal prev dir))
1234 ((equal prev dir))
1228 (when (file-directory-p (concat dir ".hg"))
1235 (when (file-directory-p (concat dir ".hg"))
1229 (let ((cwd (mapconcat 'identity stack "/")))
1236 (let ((cwd (mapconcat 'identity stack "/")))
1230 (unless (equal cwd "")
1237 (unless (equal cwd "")
1231 (return (file-name-as-directory cwd)))))))
1238 (return (file-name-as-directory cwd)))))))
1232
1239
1233 (defun hg-status (path)
1240 (defun hg-status (path)
1234 "Print revision control status of a file or directory.
1241 "Print revision control status of a file or directory.
1235 With prefix argument, prompt for the path to give status for.
1242 With prefix argument, prompt for the path to give status for.
1236 Names are displayed relative to the repository root."
1243 Names are displayed relative to the repository root."
1237 (interactive (list (hg-read-file-name " for status" (hg-root))))
1244 (interactive (list (hg-read-file-name " for status" (hg-root))))
1238 (let ((root (hg-root)))
1245 (let ((root (hg-root)))
1239 (hg-view-output ((format "Mercurial: Status of %s in %s"
1246 (hg-view-output ((format "Mercurial: Status of %s in %s"
1240 (let ((name (substring (expand-file-name path)
1247 (let ((name (substring (expand-file-name path)
1241 (length root))))
1248 (length root))))
1242 (if (> (length name) 0)
1249 (if (> (length name) 0)
1243 name
1250 name
1244 "*"))
1251 "*"))
1245 (hg-abbrev-file-name root)))
1252 (hg-abbrev-file-name root)))
1246 (apply 'call-process (hg-binary) nil t nil
1253 (apply 'call-process (hg-binary) nil t nil
1247 (list "--cwd" root "status" path))
1254 (list "--cwd" root "status" path))
1248 (cd (hg-root path)))))
1255 (cd (hg-root path)))))
1249
1256
1250 (defun hg-undo ()
1257 (defun hg-undo ()
1251 (interactive)
1258 (interactive)
1252 (error "not implemented"))
1259 (error "not implemented"))
1253
1260
1254 (defun hg-update ()
1261 (defun hg-update ()
1255 (interactive)
1262 (interactive)
1256 (error "not implemented"))
1263 (error "not implemented"))
1257
1264
1258 (defun hg-version-other-window ()
1265 (defun hg-version-other-window ()
1259 (interactive)
1266 (interactive)
1260 (error "not implemented"))
1267 (error "not implemented"))
1261
1268
1262
1269
1263 (provide 'mercurial)
1270 (provide 'mercurial)
1264
1271
1265
1272
1266 ;;; Local Variables:
1273 ;;; Local Variables:
1267 ;;; prompt-to-byte-compile: nil
1274 ;;; prompt-to-byte-compile: nil
1268 ;;; end:
1275 ;;; end:
General Comments 0
You need to be logged in to leave comments. Login now