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