Mercurial > emacs
comparison lisp/net/tramp-vc.el @ 45861:7b663a89ef2a
*** empty log message ***
author | Kai Großjohann <kgrossjo@eu.uu.net> |
---|---|
date | Mon, 17 Jun 2002 11:47:23 +0000 |
parents | |
children | 09acf3f65bb5 |
comparison
equal
deleted
inserted
replaced
45860:0dcc2162a55f | 45861:7b663a89ef2a |
---|---|
1 ;;; tramp-vc.el --- Version control integration for TRAMP.el | |
2 | |
3 ;; Copyright (C) 2000 by Free Software Foundation, Inc. | |
4 | |
5 ;; Author: Daniel Pittman <daniel@danann.net> | |
6 ;; Keywords: comm, processes | |
7 | |
8 ;; This file is part of GNU Emacs. | |
9 | |
10 ;; GNU Emacs is free software; you can redistribute it and/or modify | |
11 ;; it under the terms of the GNU General Public License as published by | |
12 ;; the Free Software Foundation; either version 2, or (at your option) | |
13 ;; any later version. | |
14 | |
15 ;; GNU Emacs is distributed in the hope that it will be useful, | |
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 ;; GNU General Public License for more details. | |
19 | |
20 ;; You should have received a copy of the GNU General Public License | |
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
23 ;; Boston, MA 02111-1307, USA. | |
24 | |
25 ;;; Commentary: | |
26 | |
27 ;; See the main module, 'tramp.el' for discussion of the purpose of TRAMP. | |
28 ;; This module provides integration between remote files accessed by TRAMP and | |
29 ;; the Emacs version control system. | |
30 | |
31 ;;; Code: | |
32 | |
33 (eval-when-compile | |
34 (require 'cl)) | |
35 (require 'vc) | |
36 ;; Old VC defines vc-rcs-release in vc.el, new VC requires extra module. | |
37 (unless (boundp 'vc-rcs-release) | |
38 (require 'vc-rcs)) | |
39 (require 'tramp) | |
40 | |
41 ;; -- vc -- | |
42 | |
43 ;; This used to blow away the file-name-handler-alist and reinstall | |
44 ;; TRAMP into it. This was intended to let VC work remotely. It didn't, | |
45 ;; at least not in my XEmacs 21.2 install. | |
46 ;; | |
47 ;; In any case, tramp-run-real-handler now deals correctly with disabling | |
48 ;; the things that should be, making this a no-op. | |
49 ;; | |
50 ;; I have removed it from the tramp-file-name-handler-alist because the | |
51 ;; shortened version does nothing. This is for reference only now. | |
52 ;; | |
53 ;; Daniel Pittman <daniel@danann.net> | |
54 ;; | |
55 ;; (defun tramp-handle-vc-registered (file) | |
56 ;; "Like `vc-registered' for tramp files." | |
57 ;; (tramp-run-real-handler 'vc-registered (list file))) | |
58 | |
59 ;; `vc-do-command' | |
60 ;; This function does not deal well with remote files, so we define | |
61 ;; our own version and make a backup of the original function and | |
62 ;; call our version for tramp files and the original version for | |
63 ;; normal files. | |
64 | |
65 ;; The following function is pretty much copied from vc.el, but | |
66 ;; the part that actually executes a command is changed. | |
67 ;; CCC: this probably works for Emacs 21, too. | |
68 (defun tramp-vc-do-command (buffer okstatus command file last &rest flags) | |
69 "Like `vc-do-command' but invoked for tramp files. | |
70 See `vc-do-command' for more information." | |
71 (save-match-data | |
72 (and file (setq file (tramp-handle-expand-file-name file))) | |
73 (if (not buffer) (setq buffer "*vc*")) | |
74 (if vc-command-messages | |
75 (message "Running `%s' on `%s'..." command file)) | |
76 (let ((obuf (current-buffer)) (camefrom (current-buffer)) | |
77 (squeezed nil) | |
78 (olddir default-directory) | |
79 vc-file status) | |
80 (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name file))) | |
81 (multi-method (tramp-file-name-multi-method v)) | |
82 (method (tramp-file-name-method v)) | |
83 (user (tramp-file-name-user v)) | |
84 (host (tramp-file-name-host v)) | |
85 (path (tramp-file-name-path v))) | |
86 (set-buffer (get-buffer-create buffer)) | |
87 (set (make-local-variable 'vc-parent-buffer) camefrom) | |
88 (set (make-local-variable 'vc-parent-buffer-name) | |
89 (concat " from " (buffer-name camefrom))) | |
90 (setq default-directory olddir) | |
91 | |
92 (erase-buffer) | |
93 | |
94 (mapcar | |
95 (function | |
96 (lambda (s) (and s (setq squeezed (append squeezed (list s)))))) | |
97 flags) | |
98 (if (and (eq last 'MASTER) file | |
99 (setq vc-file (vc-name file))) | |
100 (setq squeezed | |
101 (append squeezed | |
102 (list (tramp-file-name-path | |
103 (tramp-dissect-file-name vc-file)))))) | |
104 (if (and file (eq last 'WORKFILE)) | |
105 (progn | |
106 (let* ((pwd (expand-file-name default-directory)) | |
107 (preflen (length pwd))) | |
108 (if (string= (substring file 0 preflen) pwd) | |
109 (setq file (substring file preflen)))) | |
110 (setq squeezed (append squeezed (list file))))) | |
111 ;; Unless we (save-window-excursion) the layout of windows in | |
112 ;; the current frame changes. This is painful, at best. | |
113 ;; | |
114 ;; As a point of note, (save-excursion) is still here only because | |
115 ;; it preserves (point) in the current buffer. (save-window-excursion) | |
116 ;; does not, at least under XEmacs 21.2. | |
117 ;; | |
118 ;; I trust that the FSF support this as well. I can't find useful | |
119 ;; documentation to check :( | |
120 ;; | |
121 ;; Daniel Pittman <daniel@danann.net> | |
122 (save-excursion | |
123 (save-window-excursion | |
124 ;; Actually execute remote command | |
125 (tramp-handle-shell-command | |
126 (mapconcat 'tramp-shell-quote-argument | |
127 (cons command squeezed) " ") t) | |
128 ;;(tramp-wait-for-output) | |
129 ;; Get status from command | |
130 (tramp-send-command multi-method method user host "echo $?") | |
131 (tramp-wait-for-output) | |
132 ;; Make sure to get status from last line of output. | |
133 (goto-char (point-max)) (forward-line -1) | |
134 (setq status (read (current-buffer))) | |
135 (message "Command %s returned status %d." command status))) | |
136 (goto-char (point-max)) | |
137 (set-buffer-modified-p nil) | |
138 (forward-line -1) | |
139 (if (or (not (integerp status)) (and okstatus (< okstatus status))) | |
140 (progn | |
141 (pop-to-buffer buffer) | |
142 (goto-char (point-min)) | |
143 (shrink-window-if-larger-than-buffer) | |
144 (error "Running `%s'...FAILED (%s)" command | |
145 (if (integerp status) | |
146 (format "status %d" status) | |
147 status)) | |
148 ) | |
149 (if vc-command-messages | |
150 (message "Running %s...OK" command)) | |
151 ) | |
152 (set-buffer obuf) | |
153 status)) | |
154 )) | |
155 | |
156 ;; Following code snarfed from Emacs 21 vc.el and slightly tweaked. | |
157 (defun tramp-vc-do-command-new (buffer okstatus command file &rest flags) | |
158 "Like `vc-do-command' but for TRAMP files. | |
159 This function is for the new VC which comes with Emacs 21. | |
160 Since TRAMP doesn't do async commands yet, this function doesn't, either." | |
161 (and file (setq file (expand-file-name file))) | |
162 (if vc-command-messages | |
163 (message "Running %s on %s..." command file)) | |
164 (save-current-buffer | |
165 (unless (eq buffer t) (vc-setup-buffer buffer)) | |
166 (let ((squeezed nil) | |
167 (inhibit-read-only t) | |
168 (status 0)) | |
169 (let* ((v (when file (tramp-dissect-file-name file))) | |
170 (multi-method (when file (tramp-file-name-multi-method v))) | |
171 (method (when file (tramp-file-name-method v))) | |
172 (user (when file (tramp-file-name-user v))) | |
173 (host (when file (tramp-file-name-host v))) | |
174 (path (when file (tramp-file-name-path v)))) | |
175 (setq squeezed (delq nil (copy-sequence flags))) | |
176 (when file | |
177 (setq squeezed (append squeezed (list path)))) | |
178 (let ((w32-quote-process-args t)) | |
179 (when (eq okstatus 'async) | |
180 (message "Tramp doesn't do async commands, running synchronously.")) | |
181 (setq status (tramp-handle-shell-command | |
182 (mapconcat 'tramp-shell-quote-argument | |
183 (cons command squeezed) " ") t)) | |
184 (when (or (not (integerp status)) (and okstatus (< okstatus status))) | |
185 (pop-to-buffer (current-buffer)) | |
186 (goto-char (point-min)) | |
187 (shrink-window-if-larger-than-buffer) | |
188 (error "Running %s...FAILED (%s)" command | |
189 (if (integerp status) (format "status %d" status) status)))) | |
190 (if vc-command-messages | |
191 (message "Running %s...OK" command)) | |
192 (vc-exec-after | |
193 `(run-hook-with-args | |
194 'vc-post-command-functions ',command ',path ',flags)) | |
195 status)))) | |
196 | |
197 | |
198 ;; The context for a VC command is the current buffer. | |
199 ;; That makes a test on the buffers file more reliable than a test on the | |
200 ;; arguments. | |
201 ;; This is needed to handle remote VC correctly - else we test against the | |
202 ;; local VC system and get things wrong... | |
203 ;; Daniel Pittman <daniel@danann.net> | |
204 ;;-(if (fboundp 'vc-call-backend) | |
205 ;;- () ;; This is the new VC for which we don't have an appropriate advice yet | |
206 (if (fboundp 'vc-call-backend) | |
207 (defadvice vc-do-command | |
208 (around tramp-advice-vc-do-command | |
209 (buffer okstatus command file &rest flags) | |
210 activate) | |
211 "Invoke tramp-vc-do-command for tramp files." | |
212 (let ((file (symbol-value 'file))) ;pacify byte-compiler | |
213 (if (or (and (stringp file) (tramp-tramp-file-p file)) | |
214 (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)))) | |
215 (setq ad-return-value | |
216 (apply 'tramp-vc-do-command-new buffer okstatus command | |
217 file ;(or file (buffer-file-name)) | |
218 flags)) | |
219 ad-do-it))) | |
220 (defadvice vc-do-command | |
221 (around tramp-advice-vc-do-command | |
222 (buffer okstatus command file last &rest flags) | |
223 activate) | |
224 "Invoke tramp-vc-do-command for tramp files." | |
225 (let ((file (symbol-value 'file))) ;pacify byte-compiler | |
226 (if (or (and (stringp file) (tramp-tramp-file-p file)) | |
227 (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)))) | |
228 (setq ad-return-value | |
229 (apply 'tramp-vc-do-command buffer okstatus command | |
230 (or file (buffer-file-name)) last flags)) | |
231 ad-do-it)))) | |
232 ;;-) | |
233 | |
234 | |
235 ;; XEmacs uses this to do some of its work. Like vc-do-command, we | |
236 ;; need to enhance it to make VC work via TRAMP-mode. | |
237 ;; | |
238 ;; Like the previous function, this is a cut-and-paste job from the VC | |
239 ;; file. It's based on the vc-do-command code. | |
240 ;; CCC: this isn't used in Emacs 21, so do as before. | |
241 (defun tramp-vc-simple-command (okstatus command file &rest args) | |
242 ;; Simple version of vc-do-command, for use in vc-hooks only. | |
243 ;; Don't switch to the *vc-info* buffer before running the | |
244 ;; command, because that would change its default directory | |
245 (save-match-data | |
246 (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name file))) | |
247 (multi-method (tramp-file-name-multi-method v)) | |
248 (method (tramp-file-name-method v)) | |
249 (user (tramp-file-name-user v)) | |
250 (host (tramp-file-name-host v)) | |
251 (path (tramp-file-name-path v))) | |
252 (save-excursion (set-buffer (get-buffer-create "*vc-info*")) | |
253 (erase-buffer)) | |
254 (let ((exec-path (append vc-path exec-path)) exec-status | |
255 ;; Add vc-path to PATH for the execution of this command. | |
256 (process-environment | |
257 (cons (concat "PATH=" (getenv "PATH") | |
258 path-separator | |
259 (mapconcat 'identity vc-path path-separator)) | |
260 process-environment))) | |
261 ;; Call the actual process. See tramp-vc-do-command for discussion of | |
262 ;; why this does both (save-window-excursion) and (save-excursion). | |
263 ;; | |
264 ;; As a note, I don't think that the process-environment stuff above | |
265 ;; has any effect on the remote system. This is a hard one though as | |
266 ;; there is no real reason to expect local and remote paths to be | |
267 ;; identical... | |
268 ;; | |
269 ;; Daniel Pittman <daniel@danann.net> | |
270 (save-excursion | |
271 (save-window-excursion | |
272 ;; Actually execute remote command | |
273 (tramp-handle-shell-command | |
274 (mapconcat 'tramp-shell-quote-argument | |
275 (append (list command) args (list path)) " ") | |
276 (get-buffer-create"*vc-info*")) | |
277 ;(tramp-wait-for-output) | |
278 ;; Get status from command | |
279 (tramp-send-command multi-method method user host "echo $?") | |
280 (tramp-wait-for-output) | |
281 (setq exec-status (read (current-buffer))) | |
282 (message "Command %s returned status %d." command exec-status))) | |
283 | |
284 (cond ((> exec-status okstatus) | |
285 (switch-to-buffer (get-file-buffer file)) | |
286 (shrink-window-if-larger-than-buffer | |
287 (display-buffer "*vc-info*")) | |
288 (error "Couldn't find version control information"))) | |
289 exec-status)))) | |
290 | |
291 ;; This function does not exist any more in Emacs-21's VC | |
292 (defadvice vc-simple-command | |
293 (around tramp-advice-vc-simple-command | |
294 (okstatus command file &rest args) | |
295 activate) | |
296 "Invoke tramp-vc-simple-command for tramp files." | |
297 (let ((file (symbol-value 'file))) ;pacify byte-compiler | |
298 (if (or (and (stringp file) (tramp-tramp-file-p file)) | |
299 (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)))) | |
300 (setq ad-return-value | |
301 (apply 'tramp-vc-simple-command okstatus command | |
302 (or file (buffer-file-name)) args)) | |
303 ad-do-it))) | |
304 | |
305 | |
306 ;; `vc-workfile-unchanged-p' | |
307 ;; This function does not deal well with remote files, so we do the | |
308 ;; same as for `vc-do-command'. | |
309 | |
310 ;; `vc-workfile-unchanged-p' checks the modification time, we cannot | |
311 ;; do that for remote files, so here's a version which relies on diff. | |
312 ;; CCC: this one probably works for Emacs 21, too. | |
313 (defun tramp-vc-workfile-unchanged-p | |
314 (filename &optional want-differences-if-changed) | |
315 (if (fboundp 'vc-backend-diff) | |
316 ;; Old VC. Call `vc-backend-diff'. | |
317 (let ((status (funcall (symbol-function 'vc-backend-diff) | |
318 filename nil nil | |
319 (not want-differences-if-changed)))) | |
320 (zerop status)) | |
321 ;; New VC. Call `vc-default-workfile-unchanged-p'. | |
322 (vc-default-workfile-unchanged-p (vc-backend file) filename))) | |
323 | |
324 (defadvice vc-workfile-unchanged-p | |
325 (around tramp-advice-vc-workfile-unchanged-p | |
326 (filename &optional want-differences-if-changed) | |
327 activate) | |
328 "Invoke tramp-vc-workfile-unchanged-p for tramp files." | |
329 (if (and (stringp filename) | |
330 (tramp-tramp-file-p filename) | |
331 (not | |
332 (let ((v (tramp-dissect-file-name filename))) | |
333 (tramp-get-remote-perl (tramp-file-name-multi-method v) | |
334 (tramp-file-name-method v) | |
335 (tramp-file-name-user v) | |
336 (tramp-file-name-host v))))) | |
337 (setq ad-return-value | |
338 (tramp-vc-workfile-unchanged-p filename want-differences-if-changed)) | |
339 ad-do-it)) | |
340 | |
341 | |
342 ;; Redefine a function from vc.el -- allow tramp files. | |
343 ;; `save-match-data' seems not to be required -- it isn't in | |
344 ;; the original version, either. | |
345 ;; CCC: this might need some work -- how does the Emacs 21 version | |
346 ;; work, anyway? Does it work over ange-ftp? Hm. | |
347 (if (not (fboundp 'vc-backend-checkout)) | |
348 () ;; our replacement won't work and is unnecessary anyway | |
349 (defun vc-checkout (filename &optional writable rev) | |
350 "Retrieve a copy of the latest version of the given file." | |
351 ;; If ftp is on this system and the name matches the ange-ftp format | |
352 ;; for a remote file, the user is trying something that won't work. | |
353 (funcall (symbol-function 'vc-backend-checkout) filename writable rev) | |
354 (vc-resynch-buffer filename t t)) | |
355 ) | |
356 | |
357 | |
358 ;; Do we need to advise the vc-user-login-name function anyway? | |
359 ;; This will return the correct login name for the owner of a | |
360 ;; file. It does not deal with the default remote user name... | |
361 ;; | |
362 ;; That is, when vc calls (vc-user-login-name), we return the | |
363 ;; local login name, something that may be different to the remote | |
364 ;; default. | |
365 ;; | |
366 ;; The remote VC operations will occur as the user that we logged | |
367 ;; in with however - not always the same as the local user. | |
368 ;; | |
369 ;; In the end, I did advise the function. This is because, well, | |
370 ;; the thing didn't work right otherwise ;) | |
371 ;; | |
372 ;; Daniel Pittman <daniel@danann.net> | |
373 | |
374 (defun tramp-handle-vc-user-login-name (&optional uid) | |
375 "Return the default user name on the remote machine. | |
376 Whenever VC calls this function, `file' is bound to the file name | |
377 in question. If no uid is provided or the uid is equal to the uid | |
378 owning the file, then we return the user name given in the file name. | |
379 | |
380 This should only be called when `file' is bound to the | |
381 filename we are thinking about..." | |
382 ;; Pacify byte-compiler; this symbol is bound in the calling | |
383 ;; function. CCC: Maybe it would be better to move the | |
384 ;; boundness-checking into this function? | |
385 (let ((file (symbol-value 'file))) | |
386 (if (and uid (/= uid (nth 2 (file-attributes file)))) | |
387 (error "tramp-handle-vc-user-login-name cannot map a uid to a name") | |
388 (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name file))) | |
389 (u (tramp-file-name-user v))) | |
390 (cond ((stringp u) u) | |
391 ((vectorp u) (elt u (1- (length u)))) | |
392 ((null u) (user-login-name)) | |
393 (t (error "tramp-handle-vc-user-login-name cannot cope!"))))))) | |
394 | |
395 | |
396 (defadvice vc-user-login-name | |
397 (around tramp-vc-user-login-name activate) | |
398 "Support for files on remote machines accessed by TRAMP." | |
399 ;; We rely on the fact that `file' is bound when this is called. | |
400 ;; This appears to be the case everywhere in vc.el and vc-hooks.el | |
401 ;; as of Emacs 20.5. | |
402 ;; | |
403 ;; CCC TODO there should be a real solution! Talk to Andre Spiegel | |
404 ;; about this. | |
405 (let ((file (when (boundp 'file) | |
406 (symbol-value 'file)))) ;pacify byte-compiler | |
407 (or (and (stringp file) | |
408 (tramp-tramp-file-p file) ; tramp file | |
409 (setq ad-return-value | |
410 (save-match-data | |
411 (tramp-handle-vc-user-login-name uid)))) ; get the owner name | |
412 ad-do-it))) ; else call the original | |
413 | |
414 | |
415 ;; Determine the name of the user owning a file. | |
416 (defun tramp-file-owner (filename) | |
417 "Return who owns FILE (user name, as a string)." | |
418 (let ((v (tramp-dissect-file-name | |
419 (tramp-handle-expand-file-name filename)))) | |
420 (if (not (tramp-handle-file-exists-p filename)) | |
421 nil ; file cannot be opened | |
422 ;; file exists, find out stuff | |
423 (save-excursion | |
424 (tramp-send-command | |
425 (tramp-file-name-multi-method v) (tramp-file-name-method v) | |
426 (tramp-file-name-user v) (tramp-file-name-host v) | |
427 (format "%s -Lld %s" | |
428 (tramp-get-ls-command (tramp-file-name-multi-method v) | |
429 (tramp-file-name-method v) | |
430 (tramp-file-name-user v) | |
431 (tramp-file-name-host v)) | |
432 (tramp-shell-quote-argument (tramp-file-name-path v)))) | |
433 (tramp-wait-for-output) | |
434 ;; parse `ls -l' output ... | |
435 ;; ... file mode flags | |
436 (read (current-buffer)) | |
437 ;; ... number links | |
438 (read (current-buffer)) | |
439 ;; ... uid (as a string) | |
440 (symbol-name (read (current-buffer))))))) | |
441 | |
442 ;; Wire ourselves into the VC infrastructure... | |
443 ;; This function does not exist any more in Emacs-21's VC | |
444 ;; CCC: it appears that no substitute is needed for Emacs 21. | |
445 (defadvice vc-file-owner | |
446 (around tramp-vc-file-owner activate) | |
447 "Support for files on remote machines accessed by TRAMP." | |
448 (let ((filename (ad-get-arg 0))) | |
449 (or (and (tramp-file-name-p filename) ; tramp file | |
450 (setq ad-return-value | |
451 (save-match-data | |
452 (tramp-file-owner filename)))) ; get the owner name | |
453 ad-do-it))) ; else call the original | |
454 | |
455 | |
456 ;; We need to make the version control software backend version | |
457 ;; information local to the current buffer. This is because each TRAMP | |
458 ;; buffer can (theoretically) have a different VC version and I am | |
459 ;; *way* too lazy to try and push the correct value into each new | |
460 ;; buffer. | |
461 ;; | |
462 ;; Remote VC costs will just have to be paid, at least for the moment. | |
463 ;; Well, at least, they will right until I feel guilty about doing a | |
464 ;; botch job here and fix it. :/ | |
465 ;; | |
466 ;; Daniel Pittman <daniel@danann.net> | |
467 ;; CCC: this is probably still needed for Emacs 21. | |
468 (defun tramp-vc-setup-for-remote () | |
469 "Make the backend release variables buffer local. | |
470 This makes remote VC work correctly at the cost of some processing time." | |
471 (when (and (buffer-file-name) | |
472 (tramp-tramp-file-p (buffer-file-name))) | |
473 (make-local-variable 'vc-rcs-release) | |
474 (setq vc-rcs-release nil))) | |
475 (add-hook 'find-file-hooks 'tramp-vc-setup-for-remote t) | |
476 | |
477 ;; No need to load this again if anyone asks. | |
478 (provide 'tramp-vc) | |
479 | |
480 ;;; tramp-vc.el ends here |