Remove old comments about making various formats (use Makefile instead)
[bpt/emacs.git] / lisp / vc / vc-svn.el
CommitLineData
1fd3454a
SM
1;;; vc-svn.el --- non-resident support for Subversion version-control
2
ab422c4d 3;; Copyright (C) 2003-2013 Free Software Foundation, Inc.
1fd3454a
SM
4
5;; Author: FSF (see vc.el for full credits)
6;; Maintainer: Stefan Monnier <monnier@gnu.org>
bd78fa1d 7;; Package: vc
1fd3454a
SM
8
9;; This file is part of GNU Emacs.
10
eb3fa2cf 11;; GNU Emacs is free software: you can redistribute it and/or modify
1fd3454a 12;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
1fd3454a
SM
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
eb3fa2cf 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
1fd3454a
SM
23
24;;; Commentary:
25
f9914e54
ER
26;; Sync'd with Subversion's vc-svn.el as of revision 5801. but this version
27;; has been extensively modified since to handle filesets.
1fd3454a 28
1fd3454a
SM
29;;; Code:
30
31(eval-when-compile
32 (require 'vc))
33
8228a275
MH
34;; Clear up the cache to force vc-call to check again and discover
35;; new functions when we reload this file.
36(put 'SVN 'vc-functions nil)
37
1fd3454a
SM
38;;;
39;;; Customization options
40;;;
41
67b0de11
CY
42(defgroup vc-svn nil
43 "VC Subversion (svn) backend."
44 :version "24.1"
45 :group 'vc)
46
f9d5dc48
GM
47;; FIXME there is also svnadmin.
48(defcustom vc-svn-program "svn"
49 "Name of the SVN executable."
50 :type 'string
67b0de11 51 :group 'vc-svn)
f9d5dc48 52
31db8c31
GM
53;; Might be nice if svn defaulted to non-interactive if stdin not tty.
54;; http://svn.haxx.se/dev/archive-2008-05/0762.shtml
55;; http://svn.haxx.se/dev/archive-2009-04/0094.shtml
56;; Maybe newer ones do?
57(defcustom vc-svn-global-switches (unless (eq system-type 'darwin) ; bug#13513
58 '("--non-interactive"))
59 "Global switches to pass to any SVN command.
60The option \"--non-interactive\" is often needed to prevent SVN
61hanging while prompting for authorization."
1fd3454a
SM
62 :type '(choice (const :tag "None" nil)
63 (string :tag "Argument String")
64 (repeat :tag "Argument List"
65 :value ("")
66 string))
31db8c31 67 :version "24.4"
67b0de11 68 :group 'vc-svn)
1fd3454a
SM
69
70(defcustom vc-svn-register-switches nil
c8d6b4bc 71 "Switches for registering a file into SVN.
1fd3454a 72A string or list of strings passed to the checkin program by
c8d6b4bc
GM
73\\[vc-register]. If nil, use the value of `vc-register-switches'.
74If t, use no switches."
75 :type '(choice (const :tag "Unspecified" nil)
76 (const :tag "None" t)
1fd3454a 77 (string :tag "Argument String")
c8d6b4bc 78 (repeat :tag "Argument List" :value ("") string))
bf247b6e 79 :version "22.1"
67b0de11 80 :group 'vc-svn)
1fd3454a 81
8462aca5
SM
82(defcustom vc-svn-diff-switches
83 t ;`svn' doesn't support common args like -c or -b.
84 "String or list of strings specifying extra switches for svn diff under VC.
9751169a 85If nil, use the value of `vc-diff-switches' (or `diff-switches'),
ed68f651
SS
86together with \"-x --diff-cmd=\"`diff-command' (since 'svn diff'
87does not support the default \"-c\" value of `diff-switches').
88If you want to force an empty list of arguments, use t."
8462aca5
SM
89 :type '(choice (const :tag "Unspecified" nil)
90 (const :tag "None" t)
1fd3454a
SM
91 (string :tag "Argument String")
92 (repeat :tag "Argument List"
93 :value ("")
94 string))
bf247b6e 95 :version "22.1"
67b0de11 96 :group 'vc-svn)
1fd3454a 97
67141a37 98(defcustom vc-svn-header '("\$Id\$")
f9d5dc48 99 "Header keywords to be inserted by `vc-insert-headers'."
67141a37 100 :version "24.1" ; no longer consult the obsolete vc-header-alist
1fd3454a 101 :type '(repeat string)
67b0de11 102 :group 'vc-svn)
1fd3454a 103
fc2fb30c
SM
104;; We want to autoload it for use by the autoloaded version of
105;; vc-svn-registered, but we want the value to be compiled at startup, not
106;; at dump time.
107;; ;;;###autoload
108(defconst vc-svn-admin-directory
109 (cond ((and (memq system-type '(cygwin windows-nt ms-dos))
ace2fad9
CY
110 (getenv "SVN_ASP_DOT_NET_HACK"))
111 "_svn")
112 (t ".svn"))
113 "The name of the \".svn\" subdirectory or its equivalent.")
114
8cdd17b4
ER
115;;; Properties of the backend
116
70e2f6c7
ER
117(defun vc-svn-revision-granularity () 'repository)
118(defun vc-svn-checkout-model (files) 'implicit)
119
1fd3454a
SM
120;;;
121;;; State-querying functions
122;;;
123
ace2fad9
CY
124;;; vc-svn-admin-directory is generally not defined when the
125;;; autoloaded function is called.
126
1fd3454a 127;;;###autoload (defun vc-svn-registered (f)
ace2fad9 128;;;###autoload (let ((admin-dir (cond ((and (eq system-type 'windows-nt)
fc2fb30c
SM
129;;;###autoload (getenv "SVN_ASP_DOT_NET_HACK"))
130;;;###autoload "_svn")
131;;;###autoload (t ".svn"))))
b5a53795 132;;;###autoload (when (vc-find-root f admin-dir)
af314ba0 133;;;###autoload (load "vc-svn" nil t)
ace2fad9 134;;;###autoload (vc-svn-registered f))))
1fd3454a
SM
135
136(defun vc-svn-registered (file)
137 "Check if FILE is SVN registered."
b5a53795 138 (when (vc-svn-root file)
1fd3454a
SM
139 (with-temp-buffer
140 (cd (file-name-directory file))
04473f67
MA
141 (let* (process-file-side-effects
142 (status
df4da7f4
SM
143 (condition-case nil
144 ;; Ignore all errors.
145 (vc-svn-command t t file "status" "-v")
146 ;; Some problem happened. E.g. We can't find an `svn'
147 ;; executable. We used to only catch `file-error' but when
148 ;; the process is run on a remote host via Tramp, the error
149 ;; is only reported via the exit status which is turned into
150 ;; an `error' by vc-do-command.
151 (error nil))))
152 (when (eq 0 status)
b5e791bd
DN
153 (let ((parsed (vc-svn-parse-status file)))
154 (and parsed (not (memq parsed '(ignored unregistered))))))))))
1fd3454a
SM
155
156(defun vc-svn-state (file &optional localp)
157 "SVN-specific version of `vc-state'."
04473f67
MA
158 (let (process-file-side-effects)
159 (setq localp (or localp (vc-stay-local-p file 'SVN)))
160 (with-temp-buffer
161 (cd (file-name-directory file))
162 (vc-svn-command t 0 file "status" (if localp "-v" "-u"))
163 (vc-svn-parse-status file))))
1fd3454a 164
9749e2b0
GM
165;; NB this does not handle svn properties, which can be changed
166;; without changing the file timestamp.
167;; Note that unlike vc-cvs-state-heuristic, this is not called from
168;; vc-svn-state. AFAICS, it is only called from vc-state-refresh via
169;; vc-after-save (bug#7850). Therefore the fact that it ignores
170;; properties is irrelevant. If you want to make vc-svn-state call
171;; this, it should be extended to handle svn properties.
1fd3454a
SM
172(defun vc-svn-state-heuristic (file)
173 "SVN-specific state heuristic."
9749e2b0
GM
174 ;; If the file has not changed since checkout, consider it `up-to-date'.
175 ;; Otherwise consider it `edited'. Copied from vc-cvs-state-heuristic.
176 (let ((checkout-time (vc-file-getprop file 'vc-checkout-time))
177 (lastmod (nth 5 (file-attributes file))))
178 (cond
179 ((equal checkout-time lastmod) 'up-to-date)
180 ((string= (vc-working-revision file) "0") 'added)
181 ((null checkout-time) 'unregistered)
182 (t 'edited))))
1fd3454a 183
a80a6b03
GM
184;; FIXME it would be better not to have the "remote" argument,
185;; but to distinguish the two output formats based on content.
186(defun vc-svn-after-dir-status (callback &optional remote)
c222c25f 187 (let ((state-map '((?A . added)
b2ee56c9 188 (?C . conflict)
b2ee56c9
SM
189 (?I . ignored)
190 (?M . edited)
e6c01f09 191 (?D . removed)
b2ee56c9
SM
192 (?R . removed)
193 (?? . unregistered)
194 ;; This is what vc-svn-parse-status does.
195 (?~ . edited)))
5dd4f3f7
GM
196 (re (if remote "^\\(.\\)\\(.\\).....? \\([ *]\\) +\\(?:[-0-9]+\\)? \\(.*\\)$"
197 ;; Subexp 3 is a dummy in this case, so the numbers match.
198 "^\\(.\\)\\(.\\)...\\(.\\) \\(.*\\)$"))
c222c25f
DN
199 result)
200 (goto-char (point-min))
a80a6b03 201 (while (re-search-forward re nil t)
c222c25f 202 (let ((state (cdr (assq (aref (match-string 1) 0) state-map)))
5dd4f3f7 203 (propstat (cdr (assq (aref (match-string 2) 0) state-map)))
614b85f8
VJ
204 (filename (if (memq system-type '(windows-nt ms-dos))
205 (replace-regexp-in-string "\\\\" "/" (match-string 4))
206 (match-string 4))))
dbfb414e
GM
207 (and (memq propstat '(conflict edited))
208 (not (eq state 'conflict)) ; conflict always wins
209 (setq state propstat))
5dd4f3f7 210 (and remote (string-equal (match-string 3) "*")
a80a6b03
GM
211 ;; FIXME are there other possible combinations?
212 (cond ((eq state 'edited) (setq state 'needs-merge))
213 ((not state) (setq state 'needs-update))))
524a655d 214 (when (and state (not (string= "." filename)))
1b3f2d4e 215 (setq result (cons (list filename state) result)))))
b2ee56c9 216 (funcall callback result)))
c222c25f 217
c1b51374 218(defun vc-svn-dir-status (dir callback)
c222c25f
DN
219 "Run 'svn status' for DIR and update BUFFER via CALLBACK.
220CALLBACK is called as (CALLBACK RESULT BUFFER), where
221RESULT is a list of conses (FILE . STATE) for directory DIR."
a80a6b03 222 ;; FIXME should this rather be all the files in dir?
1826af5e
DN
223 ;; FIXME: the vc-stay-local-p logic below is disabled, it ends up
224 ;; calling synchronously (vc-svn-registered DIR) => calling svn status -v DIR
225 ;; which is VERY SLOW for big trees and it makes emacs
226 ;; completely unresponsive during that time.
77bf3f54 227 (let* ((local (and nil (vc-stay-local-p dir 'SVN)))
1826af5e 228 (remote (or t (not local) (eq local 'only-file))))
a80a6b03
GM
229 (vc-svn-command (current-buffer) 'async nil "status"
230 (if remote "-u"))
115c0061 231 (vc-exec-after
a80a6b03 232 `(vc-svn-after-dir-status (quote ,callback) ,remote))))
f8e89f19 233
847fb889
DN
234(defun vc-svn-dir-status-files (dir files default-state callback)
235 (apply 'vc-svn-command (current-buffer) 'async nil "status" files)
236 (vc-exec-after
237 `(vc-svn-after-dir-status (quote ,callback))))
238
13ad7457 239(defun vc-svn-dir-extra-headers (dir)
2ec0d864 240 "Generate extra status headers for a Subversion working copy."
04473f67
MA
241 (let (process-file-side-effects)
242 (vc-svn-command "*vc*" 0 nil "info"))
2ec0d864 243 (let ((repo
def61be2 244 (save-excursion
2ec0d864
ER
245 (and (progn
246 (set-buffer "*vc*")
247 (goto-char (point-min))
248 (re-search-forward "Repository Root: *\\(.*\\)" nil t))
249 (match-string 1)))))
250 (concat
251 (cond (repo
252 (concat
253 (propertize "Repository : " 'face 'font-lock-type-face)
254 (propertize repo 'face 'font-lock-variable-name-face)))
255 (t "")))))
256
ac3f4c6f
ER
257(defun vc-svn-working-revision (file)
258 "SVN-specific version of `vc-working-revision'."
1fd3454a
SM
259 ;; There is no need to consult RCS headers under SVN, because we
260 ;; get the workfile version for free when we recognize that a file
261 ;; is registered in SVN.
262 (vc-svn-registered file)
ac3f4c6f 263 (vc-file-getprop file 'vc-working-revision))
1fd3454a 264
5cc7cb96
SM
265;; vc-svn-mode-line-string doesn't exist because the default implementation
266;; works just fine.
267
5b5afd50 268(defun vc-svn-previous-revision (file rev)
cbbd2cd3
TTN
269 (let ((newrev (1- (string-to-number rev))))
270 (when (< 0 newrev)
271 (number-to-string newrev))))
272
5b5afd50 273(defun vc-svn-next-revision (file rev)
cbbd2cd3 274 (let ((newrev (1+ (string-to-number rev))))
5b5afd50 275 ;; The "working revision" is an uneasy conceptual fit under Subversion;
cbbd2cd3
TTN
276 ;; we use it as the upper bound until a better idea comes along. If the
277 ;; workfile version W coincides with the tree's latest revision R, then
278 ;; this check prevents a "no such revision: R+1" error. Otherwise, it
279 ;; inhibits showing of W+1 through R, which could be considered anywhere
280 ;; from gracious to impolite.
ac3f4c6f 281 (unless (< (string-to-number (vc-file-getprop file 'vc-working-revision))
cbbd2cd3
TTN
282 newrev)
283 (number-to-string newrev))))
284
1fd3454a
SM
285
286;;;
287;;; State-changing functions
288;;;
289
8cdd17b4
ER
290(defun vc-svn-create-repo ()
291 "Create a new SVN repository."
2888a97e 292 (vc-do-command "*vc*" 0 "svnadmin" '("create" "SVN"))
9a4de110
GM
293 (vc-svn-command "*vc*" 0 "." "checkout"
294 (concat "file://" default-directory "SVN")))
8cdd17b4
ER
295
296(defun vc-svn-register (files &optional rev comment)
297 "Register FILES into the SVN version-control system.
298The COMMENT argument is ignored This does an add but not a commit.
c8d6b4bc
GM
299Passes either `vc-svn-register-switches' or `vc-register-switches'
300to the SVN command."
8cdd17b4 301 (apply 'vc-svn-command nil 0 files "add" (vc-switches 'SVN 'register)))
1fd3454a 302
b5a53795
KP
303(defun vc-svn-root (file)
304 (vc-find-root file vc-svn-admin-directory))
1fd3454a 305
b5a53795
KP
306(defalias 'vc-svn-responsible-p 'vc-svn-root)
307
308(defalias 'vc-svn-could-register 'vc-svn-root
1fd3454a
SM
309 "Return non-nil if FILE could be registered in SVN.
310This is only possible if SVN is responsible for FILE's directory.")
311
09158997 312(defun vc-svn-checkin (files rev comment &optional extra-args-ignored)
1fd3454a 313 "SVN-specific version of `vc-backend-checkin'."
4ced8551 314 (if rev (error "Committing to a specific revision is unsupported in SVN"))
5129f10c 315 (let ((status (apply
8cdd17b4 316 'vc-svn-command nil 1 files "ci"
5129f10c 317 (nconc (list "-m" comment) (vc-switches 'SVN 'checkin)))))
1fd3454a
SM
318 (set-buffer "*vc*")
319 (goto-char (point-min))
320 (unless (equal status 0)
321 ;; Check checkin problem.
322 (cond
fd140743 323 ((search-forward "Transaction is out of date" nil t)
8cdd17b4
ER
324 (mapc (lambda (file) (vc-file-setprop file 'vc-state 'needs-merge))
325 files)
1fd3454a
SM
326 (error (substitute-command-keys
327 (concat "Up-to-date check failed: "
328 "type \\[vc-next-action] to merge in changes"))))
329 (t
330 (pop-to-buffer (current-buffer))
331 (goto-char (point-min))
332 (shrink-window-if-larger-than-buffer)
333 (error "Check-in failed"))))
334 ;; Update file properties
335 ;; (vc-file-setprop
ac3f4c6f 336 ;; file 'vc-working-revision
1fd3454a
SM
337 ;; (vc-parse-buffer "^\\(new\\|initial\\) revision: \\([0-9.]+\\)" 2))
338 ))
339
ac3f4c6f 340(defun vc-svn-find-revision (file rev buffer)
8cdd17b4 341 "SVN-specific retrieval of a specified version into a buffer."
04473f67
MA
342 (let (process-file-side-effects)
343 (apply 'vc-svn-command
344 buffer 0 file
345 "cat"
346 (and rev (not (string= rev ""))
347 (concat "-r" rev))
348 (vc-switches 'SVN 'checkout))))
1fd3454a
SM
349
350(defun vc-svn-checkout (file &optional editable rev)
351 (message "Checking out %s..." file)
352 (with-current-buffer (or (get-file-buffer file) (current-buffer))
a749e19d 353 (vc-svn-update file editable rev (vc-switches 'SVN 'checkout)))
77bf3f54 354 (vc-mode-line file 'SVN)
1fd3454a
SM
355 (message "Checking out %s...done" file))
356
357(defun vc-svn-update (file editable rev switches)
358 (if (and (file-exists-p file) (not rev))
fc2fb30c
SM
359 ;; If no revision was specified, there's nothing to do.
360 nil
1fd3454a 361 ;; Check out a particular version (or recreate the file).
ac3f4c6f 362 (vc-file-setprop file 'vc-working-revision nil)
1fd3454a 363 (apply 'vc-svn-command nil 0 file
1fd3454a 364 "update"
5cc7cb96
SM
365 (cond
366 ((null rev) "-rBASE")
367 ((or (eq rev t) (equal rev "")) nil)
368 (t (concat "-r" rev)))
1fd3454a
SM
369 switches)))
370
3bdc13e4
SM
371(defun vc-svn-delete-file (file)
372 (vc-svn-command nil 0 file "remove"))
373
2766aaaf
SM
374(defun vc-svn-rename-file (old new)
375 (vc-svn-command nil 0 new "move" (file-relative-name old)))
376
1fd3454a
SM
377(defun vc-svn-revert (file &optional contents-done)
378 "Revert FILE to the version it was based on."
379 (unless contents-done
fc2fb30c 380 (vc-svn-command nil 0 file "revert")))
1fd3454a
SM
381
382(defun vc-svn-merge (file first-version &optional second-version)
383 "Merge changes into current working copy of FILE.
384The changes are between FIRST-VERSION and SECOND-VERSION."
385 (vc-svn-command nil 0 file
c217cb04 386 "merge"
02610d0e 387 "-r" (if second-version
c217cb04
SM
388 (concat first-version ":" second-version)
389 first-version))
1fd3454a
SM
390 (vc-file-setprop file 'vc-state 'edited)
391 (with-current-buffer (get-buffer "*vc*")
392 (goto-char (point-min))
c217cb04
SM
393 (if (looking-at "C ")
394 1 ; signal conflict
1fd3454a
SM
395 0))) ; signal success
396
397(defun vc-svn-merge-news (file)
398 "Merge in any new changes made to FILE."
399 (message "Merging changes into %s..." file)
ac3f4c6f 400 ;; (vc-file-setprop file 'vc-working-revision nil)
1fd3454a 401 (vc-file-setprop file 'vc-checkout-time 0)
9a4de110 402 (vc-svn-command nil 0 file "update")
1fd3454a
SM
403 ;; Analyze the merge result reported by SVN, and set
404 ;; file properties accordingly.
405 (with-current-buffer (get-buffer "*vc*")
406 (goto-char (point-min))
5b5afd50 407 ;; get new working revision
1fd3454a 408 (if (re-search-forward
1468d754 409 "^\\(Updated to\\|At\\) revision \\([0-9]+\\)" nil t)
ac3f4c6f
ER
410 (vc-file-setprop file 'vc-working-revision (match-string 2))
411 (vc-file-setprop file 'vc-working-revision nil))
1fd3454a 412 ;; get file status
1468d754 413 (goto-char (point-min))
1fd3454a 414 (prog1
1468d754 415 (if (looking-at "At revision")
1fd3454a
SM
416 0 ;; there were no news; indicate success
417 (if (re-search-forward
459b1fe4
SM
418 ;; Newer SVN clients have 3 columns of chars (one for the
419 ;; file's contents, then second for its properties, and the
420 ;; third for lock-grabbing info), before the 2 spaces.
421 ;; We also used to match the filename in column 0 without any
422 ;; meta-info before it, but I believe this can never happen.
423 (concat "^\\(\\([ACGDU]\\)\\(.[B ]\\)? \\)"
c2d07557 424 (regexp-quote (file-relative-name file)))
1fd3454a
SM
425 nil t)
426 (cond
427 ;; Merge successful, we are in sync with repository now
459b1fe4 428 ((string= (match-string 2) "U")
1fd3454a
SM
429 (vc-file-setprop file 'vc-state 'up-to-date)
430 (vc-file-setprop file 'vc-checkout-time
431 (nth 5 (file-attributes file)))
432 0);; indicate success to the caller
433 ;; Merge successful, but our own changes are still in the file
459b1fe4 434 ((string= (match-string 2) "G")
1fd3454a
SM
435 (vc-file-setprop file 'vc-state 'edited)
436 0);; indicate success to the caller
437 ;; Conflicts detected!
438 (t
439 (vc-file-setprop file 'vc-state 'edited)
440 1);; signal the error to the caller
441 )
442 (pop-to-buffer "*vc*")
443 (error "Couldn't analyze svn update result")))
444 (message "Merging changes into %s...done" file))))
445
42a0a135
ER
446(defun vc-svn-modify-change-comment (files rev comment)
447 "Modify the change comments for a specified REV.
448You must have ssh access to the repository host, and the directory Emacs
e09deae9 449uses locally for temp files must also be writable by you on that host.
fd064451
DN
450This is only supported if the repository access method is either file://
451or svn+ssh://."
452 (let (tempfile host remotefile directory fileurl-p)
42a0a135 453 (with-temp-buffer
9a4de110 454 (vc-svn-command (current-buffer) 0 nil "info")
fd064451
DN
455 (goto-char (point-min))
456 (unless (re-search-forward "Repository Root: \\(file://\\(/.*\\)\\)\\|\\(svn\\+ssh://\\([^/]+\\)\\(/.*\\)\\)" nil t)
457 (error "Repository information is unavailable"))
458 (if (match-string 1)
459 (progn
460 (setq fileurl-p t)
461 (setq directory (match-string 2)))
462 (setq host (match-string 4))
463 (setq directory (match-string 5))
464 (setq remotefile (concat host ":" tempfile))))
465 (with-temp-file (setq tempfile (make-temp-file user-mail-address))
466 (insert comment))
467 (if fileurl-p
468 ;; Repository Root is a local file.
469 (progn
470 (unless (vc-do-command
2888a97e 471 "*vc*" 0 "svnadmin" nil
def61be2 472 "setlog" "--bypass-hooks" directory
fd064451
DN
473 "-r" rev (format "%s" tempfile))
474 (error "Log edit failed"))
475 (delete-file tempfile))
476
477 ;; Remote repository, using svn+ssh.
2888a97e 478 (unless (vc-do-command "*vc*" 0 "scp" nil "-q" tempfile remotefile)
fd064451
DN
479 (error "Copy of comment to %s failed" remotefile))
480 (unless (vc-do-command
2888a97e 481 "*vc*" 0 "ssh" nil "-q" host
fd064451
DN
482 (format "svnadmin setlog --bypass-hooks %s -r %s %s; rm %s"
483 directory rev tempfile tempfile))
484 (error "Log edit failed")))))
1fd3454a
SM
485
486;;;
487;;; History functions
488;;;
489
def61be2
JB
490(defvar log-view-per-file-logs)
491
6653c6b7
DN
492(define-derived-mode vc-svn-log-view-mode log-view-mode "SVN-Log-View"
493 (require 'add-log)
494 (set (make-local-variable 'log-view-per-file-logs) nil))
495
662c5698 496(defun vc-svn-print-log (files buffer &optional shortlog start-revision limit)
8cdd17b4 497 "Get change log(s) associated with FILES."
1fd3454a 498 (save-current-buffer
b349012b 499 (vc-setup-buffer buffer)
1fd3454a
SM
500 (let ((inhibit-read-only t))
501 (goto-char (point-min))
13b56025
ER
502 (if files
503 (dolist (file files)
504 (insert "Working file: " file "\n")
6616006b
DN
505 (apply
506 'vc-svn-command
13b56025
ER
507 buffer
508 'async
77bf3f54 509 ;; (if (and (= (length files) 1) (vc-stay-local-p file 'SVN)) 'async 0)
13b56025
ER
510 (list file)
511 "log"
662c5698
DN
512 (append
513 (list
514 (if start-revision
515 (format "-r%s" start-revision)
516 ;; By default Subversion only shows the log up to the
517 ;; working revision, whereas we also want the log of the
518 ;; subsequent commits. At least that's what the
519 ;; vc-cvs.el code does.
520 "-rHEAD:0"))
1db3226b 521 (when limit (list "--limit" (format "%s" limit))))))
13b56025 522 ;; Dump log for the entire directory.
662c5698
DN
523 (apply 'vc-svn-command buffer 0 nil "log"
524 (append
525 (list
526 (if start-revision (format "-r%s" start-revision) "-rHEAD:0"))
1db3226b 527 (when limit (list "--limit" (format "%s" limit)))))))))
8cdd17b4 528
8cdd17b4 529(defun vc-svn-diff (files &optional oldvers newvers buffer)
5b5afd50 530 "Get a difference report using SVN between two revisions of fileset FILES."
22b5692c
NR
531 (and oldvers
532 (not newvers)
533 files
534 (catch 'no
535 (dolist (f files)
536 (or (equal oldvers (vc-working-revision f))
537 (throw 'no nil)))
538 t)
539 ;; Use nil rather than the current revision because svn handles
540 ;; it better (i.e. locally). Note that if _any_ of the files
541 ;; has a different revision, we fetch the lot, which is
542 ;; obviously sub-optimal.
543 (setq oldvers nil))
8cdd17b4 544 (let* ((switches
f9d1f3be
SM
545 (if vc-svn-diff-switches
546 (vc-switches 'SVN 'diff)
ed68f651 547 (list (concat "--diff-cmd=" diff-command) "-x"
9751169a 548 (mapconcat 'identity (vc-switches nil 'diff) " "))))
2d4e93b9 549 (async (and (not vc-disable-async-diff)
77bf3f54 550 (vc-stay-local-p files 'SVN)
fe1919ab 551 (or oldvers newvers)))) ; Svn diffs those locally.
b349012b 552 (apply 'vc-svn-command buffer
2766aaaf 553 (if async 'async 0)
8cdd17b4 554 files "diff"
2766aaaf 555 (append
f9d1f3be 556 switches
2766aaaf
SM
557 (when oldvers
558 (list "-r" (if newvers (concat oldvers ":" newvers)
559 oldvers)))))
fd140743
SM
560 (if async 1 ; async diff => pessimistic assumption
561 ;; For some reason `svn diff' does not return a useful
562 ;; status w.r.t whether the diff was empty or not.
8cdd17b4 563 (buffer-size (get-buffer buffer)))))
1fd3454a 564
1fd3454a 565;;;
370fded4 566;;; Tag system
1fd3454a
SM
567;;;
568
370fded4 569(defun vc-svn-create-tag (dir name branchp)
5b5afd50 570 "Assign to DIR's current revision a given NAME.
1fd3454a 571If BRANCHP is non-nil, the name is created as a branch (and the current
5cc7cb96
SM
572workspace is immediately moved to that new branch).
573NAME is assumed to be a URL."
574 (vc-svn-command nil 0 dir "copy" name)
370fded4 575 (when branchp (vc-svn-retrieve-tag dir name nil)))
1fd3454a 576
370fded4
ER
577(defun vc-svn-retrieve-tag (dir name update)
578 "Retrieve a tag at and below DIR.
579NAME is the name of the tag; if it is empty, do a `svn update'.
5cc7cb96
SM
580If UPDATE is non-nil, then update (resynch) any affected buffers.
581NAME is assumed to be a URL."
582 (vc-svn-command nil 0 dir "switch" name)
583 ;; FIXME: parse the output and obey `update'.
584 )
1fd3454a
SM
585
586;;;
587;;; Miscellaneous
588;;;
589
590;; Subversion makes backups for us, so don't bother.
77bf3f54
DN
591;; (defun vc-svn-make-version-backups-p (file)
592;; "Return non-nil if version backups should be made for FILE."
593;; (vc-stay-local-p file 'SVN))
1fd3454a
SM
594
595(defun vc-svn-check-headers ()
596 "Check if the current file has any headers in it."
597 (save-excursion
598 (goto-char (point-min))
599 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
600\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
601
602
603;;;
604;;; Internal functions
605;;;
606
8cdd17b4 607(defun vc-svn-command (buffer okstatus file-or-list &rest flags)
1fd3454a
SM
608 "A wrapper around `vc-do-command' for use in vc-svn.el.
609The difference to vc-do-command is that this function always invokes `svn',
31db8c31 610and that it passes `vc-svn-global-switches' to it before FLAGS."
9a4de110 611 (apply 'vc-do-command (or buffer "*vc*") okstatus vc-svn-program file-or-list
31db8c31
GM
612 (if (stringp vc-svn-global-switches)
613 (cons vc-svn-global-switches flags)
614 (append vc-svn-global-switches flags))))
1fd3454a 615
7ffc77d3
SM
616(defun vc-svn-repository-hostname (dirname)
617 (with-temp-buffer
b5a53795
KP
618 (let (process-file-side-effects)
619 (vc-svn-command t t dirname "info" "--xml"))
7ffc77d3 620 (goto-char (point-min))
b5a53795 621 (when (re-search-forward "<url>\\(.*\\)</url>" nil t)
17a5a301
SM
622 ;; This is not a hostname but a URL. This may actually be considered
623 ;; as a feature since it allows vc-svn-stay-local to specify different
624 ;; behavior for different modules on the same server.
625 (match-string 1))))
1fd3454a 626
1c67a814
SM
627(defun vc-svn-resolve-when-done ()
628 "Call \"svn resolved\" if the conflict markers have been removed."
629 (save-excursion
630 (goto-char (point-min))
54648b5c
DN
631 (unless (re-search-forward "^<<<<<<< " nil t)
632 (vc-svn-command nil 0 buffer-file-name "resolved")
633 ;; Remove the hook so that it is not called multiple times.
634 (remove-hook 'after-save-hook 'vc-svn-resolve-when-done t))))
1c67a814
SM
635
636;; Inspired by vc-arch-find-file-hook.
637(defun vc-svn-find-file-hook ()
638 (when (eq ?C (vc-file-getprop buffer-file-name 'vc-svn-status))
639 ;; If the file is marked as "conflicted", then we should try and call
640 ;; "svn resolved" when applicable.
641 (if (save-excursion
642 (goto-char (point-min))
643 (re-search-forward "^<<<<<<< " nil t))
644 ;; There are conflict markers.
645 (progn
28e4e2b4 646 (smerge-start-session)
1c67a814
SM
647 (add-hook 'after-save-hook 'vc-svn-resolve-when-done nil t))
648 ;; There are no conflict markers. This is problematic: maybe it means
649 ;; the conflict has been resolved and we should immediately call "svn
650 ;; resolved", or it means that the file's type does not allow Svn to
651 ;; use conflict markers in which case we don't really know what to do.
652 ;; So let's just punt for now.
653 nil)
654 (message "There are unresolved conflicts in this file")))
655
bc8c1bb4 656(defun vc-svn-parse-status (&optional filename)
1fd3454a 657 "Parse output of \"svn status\" command in the current buffer.
bc8c1bb4
SM
658Set file properties accordingly. Unless FILENAME is non-nil, parse only
659information about FILENAME and return its status."
5dd4f3f7 660 (let (file status propstat)
1fd3454a
SM
661 (goto-char (point-min))
662 (while (re-search-forward
484c1b1f 663 ;; Ignore the files with status X.
245cacf1 664 "^\\(?:\\?\\|[ ACDGIMR!~][ MC][ L][ +][ S]..\\([ *]\\) +\\([-0-9]+\\) +\\([0-9?]+\\) +\\([^ ]+\\)\\) +" nil t)
c45b3be3
SM
665 ;; If the username contains spaces, the output format is ambiguous,
666 ;; so don't trust the output's filename unless we have to.
667 (setq file (or filename
668 (expand-file-name
669 (buffer-substring (point) (line-end-position)))))
5dd4f3f7
GM
670 (setq status (char-after (line-beginning-position))
671 ;; Status of the item's properties ([ MC]).
672 propstat (char-after (1+ (line-beginning-position))))
484c1b1f 673 (if (eq status ??)
4f07b9f2 674 (vc-file-setprop file 'vc-state 'unregistered)
fd140743
SM
675 ;; Use the last-modified revision, so that searching in vc-print-log
676 ;; output works.
4f07b9f2 677 (vc-file-setprop file 'vc-working-revision (match-string 3))
1c67a814 678 ;; Remember Svn's own status.
4f07b9f2
ER
679 (vc-file-setprop file 'vc-svn-status status)
680 (vc-file-setprop
1fd3454a
SM
681 file 'vc-state
682 (cond
5dd4f3f7 683 ((and (eq status ?\ ) (eq propstat ?\ ))
1fd3454a 684 (if (eq (char-after (match-beginning 1)) ?*)
3702367b 685 'needs-update
4f07b9f2 686 (vc-file-setprop file 'vc-checkout-time
1fd3454a
SM
687 (nth 5 (file-attributes file)))
688 'up-to-date))
689 ((eq status ?A)
fd140743 690 ;; If the file was actually copied, (match-string 2) is "-".
4f07b9f2
ER
691 (vc-file-setprop file 'vc-working-revision "0")
692 (vc-file-setprop file 'vc-checkout-time 0)
484c1b1f 693 'added)
5dd4f3f7
GM
694 ;; Conflict in contents or properties.
695 ((or (eq status ?C) (eq propstat ?C))
7fbb4797 696 (vc-file-setprop file 'vc-state 'conflict))
5dd4f3f7
GM
697 ;; Modified contents or properties.
698 ((or (eq status ?M) (eq propstat ?M))
1fd3454a
SM
699 (if (eq (char-after (match-beginning 1)) ?*)
700 'needs-merge
701 'edited))
722f037f 702 ((eq status ?I)
4f07b9f2 703 (vc-file-setprop file 'vc-state 'ignored))
e6c01f09 704 ((memq status '(?D ?R))
4f07b9f2 705 (vc-file-setprop file 'vc-state 'removed))
bc8c1bb4 706 (t 'edited)))))
245cacf1 707 (when filename (vc-file-getprop filename 'vc-state))))
1fd3454a 708
1fd3454a
SM
709(defun vc-svn-valid-symbolic-tag-name-p (tag)
710 "Return non-nil if TAG is a valid symbolic tag name."
711 ;; According to the SVN manual, a valid symbolic tag must start with
712 ;; an uppercase or lowercase letter and can contain uppercase and
713 ;; lowercase letters, digits, `-', and `_'.
714 (and (string-match "^[a-zA-Z]" tag)
715 (not (string-match "[^a-z0-9A-Z-_]" tag))))
716
5b5afd50
ER
717(defun vc-svn-valid-revision-number-p (tag)
718 "Return non-nil if TAG is a valid revision number."
1fd3454a
SM
719 (and (string-match "^[0-9]" tag)
720 (not (string-match "[^0-9]" tag))))
721
17a5a301
SM
722;; Support for `svn annotate'
723
724(defun vc-svn-annotate-command (file buf &optional rev)
837b0e99 725 (vc-svn-command buf 'async file "annotate" (if rev (concat "-r" rev))))
17a5a301
SM
726
727(defun vc-svn-annotate-time-of-rev (rev)
da6062e6 728 ;; Arbitrarily assume 10 commits per day.
17a5a301
SM
729 (/ (string-to-number rev) 10.0))
730
e53ac718
DN
731(defvar vc-annotate-parent-rev)
732
17a5a301
SM
733(defun vc-svn-annotate-current-time ()
734 (vc-svn-annotate-time-of-rev vc-annotate-parent-rev))
735
736(defconst vc-svn-annotate-re "[ \t]*\\([0-9]+\\)[ \t]+[^\t ]+ ")
737
738(defun vc-svn-annotate-time ()
739 (when (looking-at vc-svn-annotate-re)
740 (goto-char (match-end 0))
741 (vc-svn-annotate-time-of-rev (match-string 1))))
742
743(defun vc-svn-annotate-extract-revision-at-line ()
744 (save-excursion
745 (beginning-of-line)
746 (if (looking-at vc-svn-annotate-re) (match-string 1))))
747
8228a275
MH
748(defun vc-svn-revision-table (files)
749 (let ((vc-svn-revisions '()))
750 (with-current-buffer "*vc*"
751 (vc-svn-command nil 0 files "log" "-q")
752 (goto-char (point-min))
753 (forward-line)
754 (let ((start (point-min))
755 (loglines (buffer-substring-no-properties (point-min)
756 (point-max))))
757 (while (string-match "^r\\([0-9]+\\) " loglines)
758 (push (match-string 1 loglines) vc-svn-revisions)
759 (setq start (+ start (match-end 0)))
760 (setq loglines (buffer-substring-no-properties start (point-max)))))
761 vc-svn-revisions)))
762
1fd3454a
SM
763(provide 'vc-svn)
764
765;;; vc-svn.el ends here