* vc/vc-svn.el (vc-svn-retrieve-tag): Mark unused argument.
[bpt/emacs.git] / lisp / vc / vc-svn.el
CommitLineData
9c750eba 1;;; vc-svn.el --- non-resident support for Subversion version-control -*- lexical-binding:t -*-
1fd3454a 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 117(defun vc-svn-revision-granularity () 'repository)
9c750eba 118(defun vc-svn-checkout-model (_files) 'implicit)
70e2f6c7 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
e658d75c
GM
218;; -dir-status called from vc-dir, which loads vc, which loads vc-dispatcher.
219(declare-function vc-exec-after "vc-dispatcher" (code))
220
c1b51374 221(defun vc-svn-dir-status (dir callback)
c222c25f
DN
222 "Run 'svn status' for DIR and update BUFFER via CALLBACK.
223CALLBACK is called as (CALLBACK RESULT BUFFER), where
224RESULT is a list of conses (FILE . STATE) for directory DIR."
a80a6b03 225 ;; FIXME should this rather be all the files in dir?
1826af5e
DN
226 ;; FIXME: the vc-stay-local-p logic below is disabled, it ends up
227 ;; calling synchronously (vc-svn-registered DIR) => calling svn status -v DIR
228 ;; which is VERY SLOW for big trees and it makes emacs
229 ;; completely unresponsive during that time.
77bf3f54 230 (let* ((local (and nil (vc-stay-local-p dir 'SVN)))
1826af5e 231 (remote (or t (not local) (eq local 'only-file))))
a80a6b03
GM
232 (vc-svn-command (current-buffer) 'async nil "status"
233 (if remote "-u"))
9c750eba
SM
234 (vc-run-delayed
235 (vc-svn-after-dir-status callback remote))))
f8e89f19 236
be94d713 237(defun vc-svn-dir-status-files (_dir files _default-state callback)
847fb889 238 (apply 'vc-svn-command (current-buffer) 'async nil "status" files)
9c750eba
SM
239 (vc-run-delayed
240 (vc-svn-after-dir-status callback)))
847fb889 241
be94d713 242(defun vc-svn-dir-extra-headers (_dir)
2ec0d864 243 "Generate extra status headers for a Subversion working copy."
04473f67
MA
244 (let (process-file-side-effects)
245 (vc-svn-command "*vc*" 0 nil "info"))
2ec0d864 246 (let ((repo
def61be2 247 (save-excursion
2ec0d864
ER
248 (and (progn
249 (set-buffer "*vc*")
250 (goto-char (point-min))
251 (re-search-forward "Repository Root: *\\(.*\\)" nil t))
252 (match-string 1)))))
253 (concat
254 (cond (repo
255 (concat
256 (propertize "Repository : " 'face 'font-lock-type-face)
257 (propertize repo 'face 'font-lock-variable-name-face)))
258 (t "")))))
259
ac3f4c6f
ER
260(defun vc-svn-working-revision (file)
261 "SVN-specific version of `vc-working-revision'."
1fd3454a
SM
262 ;; There is no need to consult RCS headers under SVN, because we
263 ;; get the workfile version for free when we recognize that a file
264 ;; is registered in SVN.
265 (vc-svn-registered file)
ac3f4c6f 266 (vc-file-getprop file 'vc-working-revision))
1fd3454a 267
5cc7cb96
SM
268;; vc-svn-mode-line-string doesn't exist because the default implementation
269;; works just fine.
270
9c750eba 271(defun vc-svn-previous-revision (_file rev)
cbbd2cd3
TTN
272 (let ((newrev (1- (string-to-number rev))))
273 (when (< 0 newrev)
274 (number-to-string newrev))))
275
5b5afd50 276(defun vc-svn-next-revision (file rev)
cbbd2cd3 277 (let ((newrev (1+ (string-to-number rev))))
5b5afd50 278 ;; The "working revision" is an uneasy conceptual fit under Subversion;
cbbd2cd3
TTN
279 ;; we use it as the upper bound until a better idea comes along. If the
280 ;; workfile version W coincides with the tree's latest revision R, then
281 ;; this check prevents a "no such revision: R+1" error. Otherwise, it
282 ;; inhibits showing of W+1 through R, which could be considered anywhere
283 ;; from gracious to impolite.
ac3f4c6f 284 (unless (< (string-to-number (vc-file-getprop file 'vc-working-revision))
cbbd2cd3
TTN
285 newrev)
286 (number-to-string newrev))))
287
1fd3454a
SM
288
289;;;
290;;; State-changing functions
291;;;
292
8cdd17b4
ER
293(defun vc-svn-create-repo ()
294 "Create a new SVN repository."
2888a97e 295 (vc-do-command "*vc*" 0 "svnadmin" '("create" "SVN"))
9a4de110
GM
296 (vc-svn-command "*vc*" 0 "." "checkout"
297 (concat "file://" default-directory "SVN")))
8cdd17b4 298
e658d75c
GM
299(autoload 'vc-switches "vc")
300
9c750eba 301(defun vc-svn-register (files &optional _rev _comment)
8cdd17b4
ER
302 "Register FILES into the SVN version-control system.
303The COMMENT argument is ignored This does an add but not a commit.
c8d6b4bc
GM
304Passes either `vc-svn-register-switches' or `vc-register-switches'
305to the SVN command."
8cdd17b4 306 (apply 'vc-svn-command nil 0 files "add" (vc-switches 'SVN 'register)))
1fd3454a 307
b5a53795
KP
308(defun vc-svn-root (file)
309 (vc-find-root file vc-svn-admin-directory))
1fd3454a 310
b5a53795
KP
311(defalias 'vc-svn-responsible-p 'vc-svn-root)
312
313(defalias 'vc-svn-could-register 'vc-svn-root
1fd3454a
SM
314 "Return non-nil if FILE could be registered in SVN.
315This is only possible if SVN is responsible for FILE's directory.")
316
9c750eba 317(defun vc-svn-checkin (files rev comment &optional _extra-args-ignored)
1fd3454a 318 "SVN-specific version of `vc-backend-checkin'."
4ced8551 319 (if rev (error "Committing to a specific revision is unsupported in SVN"))
5129f10c 320 (let ((status (apply
8cdd17b4 321 'vc-svn-command nil 1 files "ci"
5129f10c 322 (nconc (list "-m" comment) (vc-switches 'SVN 'checkin)))))
1fd3454a
SM
323 (set-buffer "*vc*")
324 (goto-char (point-min))
325 (unless (equal status 0)
326 ;; Check checkin problem.
327 (cond
fd140743 328 ((search-forward "Transaction is out of date" nil t)
8cdd17b4
ER
329 (mapc (lambda (file) (vc-file-setprop file 'vc-state 'needs-merge))
330 files)
1fd3454a
SM
331 (error (substitute-command-keys
332 (concat "Up-to-date check failed: "
333 "type \\[vc-next-action] to merge in changes"))))
334 (t
335 (pop-to-buffer (current-buffer))
336 (goto-char (point-min))
337 (shrink-window-if-larger-than-buffer)
338 (error "Check-in failed"))))
339 ;; Update file properties
340 ;; (vc-file-setprop
ac3f4c6f 341 ;; file 'vc-working-revision
1fd3454a
SM
342 ;; (vc-parse-buffer "^\\(new\\|initial\\) revision: \\([0-9.]+\\)" 2))
343 ))
344
ac3f4c6f 345(defun vc-svn-find-revision (file rev buffer)
8cdd17b4 346 "SVN-specific retrieval of a specified version into a buffer."
04473f67
MA
347 (let (process-file-side-effects)
348 (apply 'vc-svn-command
349 buffer 0 file
350 "cat"
351 (and rev (not (string= rev ""))
352 (concat "-r" rev))
353 (vc-switches 'SVN 'checkout))))
1fd3454a 354
be94d713 355(defun vc-svn-ignore (file &optional _directory _remove)
ab419665 356 "Ignore FILE under Subversion.
63191d9f 357FILE is a file wildcard, relative to the root directory of DIRECTORY."
5c09de04 358 (vc-svn-command t 0 file "propedit" "svn:ignore"))
7aa7fff0 359
9c750eba 360(defun vc-svn-ignore-completion-table (_file)
ab419665
XF
361 "Return the list of ignored files."
362 )
363
1fd3454a
SM
364(defun vc-svn-checkout (file &optional editable rev)
365 (message "Checking out %s..." file)
366 (with-current-buffer (or (get-file-buffer file) (current-buffer))
a749e19d 367 (vc-svn-update file editable rev (vc-switches 'SVN 'checkout)))
77bf3f54 368 (vc-mode-line file 'SVN)
1fd3454a
SM
369 (message "Checking out %s...done" file))
370
9c750eba 371(defun vc-svn-update (file _editable rev switches)
1fd3454a 372 (if (and (file-exists-p file) (not rev))
fc2fb30c
SM
373 ;; If no revision was specified, there's nothing to do.
374 nil
1fd3454a 375 ;; Check out a particular version (or recreate the file).
ac3f4c6f 376 (vc-file-setprop file 'vc-working-revision nil)
1fd3454a 377 (apply 'vc-svn-command nil 0 file
1fd3454a 378 "update"
5cc7cb96
SM
379 (cond
380 ((null rev) "-rBASE")
381 ((or (eq rev t) (equal rev "")) nil)
382 (t (concat "-r" rev)))
1fd3454a
SM
383 switches)))
384
3bdc13e4
SM
385(defun vc-svn-delete-file (file)
386 (vc-svn-command nil 0 file "remove"))
387
2766aaaf
SM
388(defun vc-svn-rename-file (old new)
389 (vc-svn-command nil 0 new "move" (file-relative-name old)))
390
1fd3454a
SM
391(defun vc-svn-revert (file &optional contents-done)
392 "Revert FILE to the version it was based on."
393 (unless contents-done
fc2fb30c 394 (vc-svn-command nil 0 file "revert")))
1fd3454a
SM
395
396(defun vc-svn-merge (file first-version &optional second-version)
397 "Merge changes into current working copy of FILE.
398The changes are between FIRST-VERSION and SECOND-VERSION."
399 (vc-svn-command nil 0 file
c217cb04 400 "merge"
02610d0e 401 "-r" (if second-version
c217cb04
SM
402 (concat first-version ":" second-version)
403 first-version))
1fd3454a
SM
404 (vc-file-setprop file 'vc-state 'edited)
405 (with-current-buffer (get-buffer "*vc*")
406 (goto-char (point-min))
c217cb04
SM
407 (if (looking-at "C ")
408 1 ; signal conflict
1fd3454a
SM
409 0))) ; signal success
410
411(defun vc-svn-merge-news (file)
412 "Merge in any new changes made to FILE."
413 (message "Merging changes into %s..." file)
ac3f4c6f 414 ;; (vc-file-setprop file 'vc-working-revision nil)
1fd3454a 415 (vc-file-setprop file 'vc-checkout-time 0)
9a4de110 416 (vc-svn-command nil 0 file "update")
1fd3454a
SM
417 ;; Analyze the merge result reported by SVN, and set
418 ;; file properties accordingly.
419 (with-current-buffer (get-buffer "*vc*")
420 (goto-char (point-min))
5b5afd50 421 ;; get new working revision
1fd3454a 422 (if (re-search-forward
1468d754 423 "^\\(Updated to\\|At\\) revision \\([0-9]+\\)" nil t)
ac3f4c6f
ER
424 (vc-file-setprop file 'vc-working-revision (match-string 2))
425 (vc-file-setprop file 'vc-working-revision nil))
1fd3454a 426 ;; get file status
1468d754 427 (goto-char (point-min))
1fd3454a 428 (prog1
1468d754 429 (if (looking-at "At revision")
1fd3454a
SM
430 0 ;; there were no news; indicate success
431 (if (re-search-forward
459b1fe4
SM
432 ;; Newer SVN clients have 3 columns of chars (one for the
433 ;; file's contents, then second for its properties, and the
434 ;; third for lock-grabbing info), before the 2 spaces.
435 ;; We also used to match the filename in column 0 without any
436 ;; meta-info before it, but I believe this can never happen.
437 (concat "^\\(\\([ACGDU]\\)\\(.[B ]\\)? \\)"
c2d07557 438 (regexp-quote (file-relative-name file)))
1fd3454a
SM
439 nil t)
440 (cond
441 ;; Merge successful, we are in sync with repository now
459b1fe4 442 ((string= (match-string 2) "U")
1fd3454a
SM
443 (vc-file-setprop file 'vc-state 'up-to-date)
444 (vc-file-setprop file 'vc-checkout-time
445 (nth 5 (file-attributes file)))
446 0);; indicate success to the caller
447 ;; Merge successful, but our own changes are still in the file
459b1fe4 448 ((string= (match-string 2) "G")
1fd3454a
SM
449 (vc-file-setprop file 'vc-state 'edited)
450 0);; indicate success to the caller
451 ;; Conflicts detected!
452 (t
453 (vc-file-setprop file 'vc-state 'edited)
454 1);; signal the error to the caller
455 )
456 (pop-to-buffer "*vc*")
457 (error "Couldn't analyze svn update result")))
458 (message "Merging changes into %s...done" file))))
459
9c750eba 460(defun vc-svn-modify-change-comment (_files rev comment)
42a0a135
ER
461 "Modify the change comments for a specified REV.
462You must have ssh access to the repository host, and the directory Emacs
e09deae9 463uses locally for temp files must also be writable by you on that host.
fd064451
DN
464This is only supported if the repository access method is either file://
465or svn+ssh://."
466 (let (tempfile host remotefile directory fileurl-p)
42a0a135 467 (with-temp-buffer
9a4de110 468 (vc-svn-command (current-buffer) 0 nil "info")
fd064451
DN
469 (goto-char (point-min))
470 (unless (re-search-forward "Repository Root: \\(file://\\(/.*\\)\\)\\|\\(svn\\+ssh://\\([^/]+\\)\\(/.*\\)\\)" nil t)
471 (error "Repository information is unavailable"))
472 (if (match-string 1)
473 (progn
474 (setq fileurl-p t)
475 (setq directory (match-string 2)))
476 (setq host (match-string 4))
477 (setq directory (match-string 5))
478 (setq remotefile (concat host ":" tempfile))))
479 (with-temp-file (setq tempfile (make-temp-file user-mail-address))
480 (insert comment))
481 (if fileurl-p
482 ;; Repository Root is a local file.
483 (progn
484 (unless (vc-do-command
2888a97e 485 "*vc*" 0 "svnadmin" nil
def61be2 486 "setlog" "--bypass-hooks" directory
fd064451
DN
487 "-r" rev (format "%s" tempfile))
488 (error "Log edit failed"))
489 (delete-file tempfile))
490
491 ;; Remote repository, using svn+ssh.
2888a97e 492 (unless (vc-do-command "*vc*" 0 "scp" nil "-q" tempfile remotefile)
fd064451
DN
493 (error "Copy of comment to %s failed" remotefile))
494 (unless (vc-do-command
2888a97e 495 "*vc*" 0 "ssh" nil "-q" host
fd064451
DN
496 (format "svnadmin setlog --bypass-hooks %s -r %s %s; rm %s"
497 directory rev tempfile tempfile))
498 (error "Log edit failed")))))
1fd3454a
SM
499
500;;;
501;;; History functions
502;;;
503
def61be2
JB
504(defvar log-view-per-file-logs)
505
6653c6b7
DN
506(define-derived-mode vc-svn-log-view-mode log-view-mode "SVN-Log-View"
507 (require 'add-log)
508 (set (make-local-variable 'log-view-per-file-logs) nil))
509
e658d75c
GM
510(autoload 'vc-setup-buffer "vc-dispatcher")
511
9c750eba 512(defun vc-svn-print-log (files buffer &optional _shortlog start-revision limit)
bb7cdf58
GM
513 "Print commit log associated with FILES into specified BUFFER.
514SHORTLOG is ignored.
515If START-REVISION is non-nil, it is the newest revision to show.
516If LIMIT is non-nil, show no more than this many entries."
1fd3454a 517 (save-current-buffer
b349012b 518 (vc-setup-buffer buffer)
1fd3454a
SM
519 (let ((inhibit-read-only t))
520 (goto-char (point-min))
13b56025
ER
521 (if files
522 (dolist (file files)
523 (insert "Working file: " file "\n")
6616006b
DN
524 (apply
525 'vc-svn-command
13b56025
ER
526 buffer
527 'async
77bf3f54 528 ;; (if (and (= (length files) 1) (vc-stay-local-p file 'SVN)) 'async 0)
13b56025
ER
529 (list file)
530 "log"
662c5698
DN
531 (append
532 (list
533 (if start-revision
90b4237a 534 (format "-r%s:1" start-revision)
662c5698
DN
535 ;; By default Subversion only shows the log up to the
536 ;; working revision, whereas we also want the log of the
537 ;; subsequent commits. At least that's what the
538 ;; vc-cvs.el code does.
539 "-rHEAD:0"))
1db3226b 540 (when limit (list "--limit" (format "%s" limit))))))
13b56025 541 ;; Dump log for the entire directory.
662c5698
DN
542 (apply 'vc-svn-command buffer 0 nil "log"
543 (append
544 (list
545 (if start-revision (format "-r%s" start-revision) "-rHEAD:0"))
1db3226b 546 (when limit (list "--limit" (format "%s" limit)))))))))
8cdd17b4 547
8cdd17b4 548(defun vc-svn-diff (files &optional oldvers newvers buffer)
5b5afd50 549 "Get a difference report using SVN between two revisions of fileset FILES."
22b5692c
NR
550 (and oldvers
551 (not newvers)
552 files
553 (catch 'no
554 (dolist (f files)
555 (or (equal oldvers (vc-working-revision f))
556 (throw 'no nil)))
557 t)
558 ;; Use nil rather than the current revision because svn handles
559 ;; it better (i.e. locally). Note that if _any_ of the files
560 ;; has a different revision, we fetch the lot, which is
561 ;; obviously sub-optimal.
562 (setq oldvers nil))
8cdd17b4 563 (let* ((switches
f9d1f3be
SM
564 (if vc-svn-diff-switches
565 (vc-switches 'SVN 'diff)
ed68f651 566 (list (concat "--diff-cmd=" diff-command) "-x"
9751169a 567 (mapconcat 'identity (vc-switches nil 'diff) " "))))
2d4e93b9 568 (async (and (not vc-disable-async-diff)
77bf3f54 569 (vc-stay-local-p files 'SVN)
fe1919ab 570 (or oldvers newvers)))) ; Svn diffs those locally.
b349012b 571 (apply 'vc-svn-command buffer
2766aaaf 572 (if async 'async 0)
8cdd17b4 573 files "diff"
2766aaaf 574 (append
f9d1f3be 575 switches
2766aaaf
SM
576 (when oldvers
577 (list "-r" (if newvers (concat oldvers ":" newvers)
578 oldvers)))))
fd140743
SM
579 (if async 1 ; async diff => pessimistic assumption
580 ;; For some reason `svn diff' does not return a useful
581 ;; status w.r.t whether the diff was empty or not.
8cdd17b4 582 (buffer-size (get-buffer buffer)))))
1fd3454a 583
1fd3454a 584;;;
370fded4 585;;; Tag system
1fd3454a
SM
586;;;
587
370fded4 588(defun vc-svn-create-tag (dir name branchp)
5b5afd50 589 "Assign to DIR's current revision a given NAME.
1fd3454a 590If BRANCHP is non-nil, the name is created as a branch (and the current
5cc7cb96
SM
591workspace is immediately moved to that new branch).
592NAME is assumed to be a URL."
593 (vc-svn-command nil 0 dir "copy" name)
370fded4 594 (when branchp (vc-svn-retrieve-tag dir name nil)))
1fd3454a 595
aa30fa6f 596(defun vc-svn-retrieve-tag (dir name _update)
370fded4
ER
597 "Retrieve a tag at and below DIR.
598NAME is the name of the tag; if it is empty, do a `svn update'.
5cc7cb96
SM
599If UPDATE is non-nil, then update (resynch) any affected buffers.
600NAME is assumed to be a URL."
601 (vc-svn-command nil 0 dir "switch" name)
602 ;; FIXME: parse the output and obey `update'.
603 )
1fd3454a
SM
604
605;;;
606;;; Miscellaneous
607;;;
608
609;; Subversion makes backups for us, so don't bother.
77bf3f54
DN
610;; (defun vc-svn-make-version-backups-p (file)
611;; "Return non-nil if version backups should be made for FILE."
612;; (vc-stay-local-p file 'SVN))
1fd3454a
SM
613
614(defun vc-svn-check-headers ()
615 "Check if the current file has any headers in it."
616 (save-excursion
617 (goto-char (point-min))
618 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
619\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
620
621
622;;;
623;;; Internal functions
624;;;
625
8cdd17b4 626(defun vc-svn-command (buffer okstatus file-or-list &rest flags)
1fd3454a
SM
627 "A wrapper around `vc-do-command' for use in vc-svn.el.
628The difference to vc-do-command is that this function always invokes `svn',
31db8c31 629and that it passes `vc-svn-global-switches' to it before FLAGS."
9a4de110 630 (apply 'vc-do-command (or buffer "*vc*") okstatus vc-svn-program file-or-list
31db8c31
GM
631 (if (stringp vc-svn-global-switches)
632 (cons vc-svn-global-switches flags)
633 (append vc-svn-global-switches flags))))
1fd3454a 634
7ffc77d3
SM
635(defun vc-svn-repository-hostname (dirname)
636 (with-temp-buffer
b5a53795
KP
637 (let (process-file-side-effects)
638 (vc-svn-command t t dirname "info" "--xml"))
7ffc77d3 639 (goto-char (point-min))
b5a53795 640 (when (re-search-forward "<url>\\(.*\\)</url>" nil t)
17a5a301
SM
641 ;; This is not a hostname but a URL. This may actually be considered
642 ;; as a feature since it allows vc-svn-stay-local to specify different
643 ;; behavior for different modules on the same server.
644 (match-string 1))))
1fd3454a 645
1c67a814
SM
646(defun vc-svn-resolve-when-done ()
647 "Call \"svn resolved\" if the conflict markers have been removed."
648 (save-excursion
649 (goto-char (point-min))
54648b5c
DN
650 (unless (re-search-forward "^<<<<<<< " nil t)
651 (vc-svn-command nil 0 buffer-file-name "resolved")
652 ;; Remove the hook so that it is not called multiple times.
653 (remove-hook 'after-save-hook 'vc-svn-resolve-when-done t))))
1c67a814
SM
654
655;; Inspired by vc-arch-find-file-hook.
656(defun vc-svn-find-file-hook ()
657 (when (eq ?C (vc-file-getprop buffer-file-name 'vc-svn-status))
658 ;; If the file is marked as "conflicted", then we should try and call
659 ;; "svn resolved" when applicable.
660 (if (save-excursion
661 (goto-char (point-min))
662 (re-search-forward "^<<<<<<< " nil t))
663 ;; There are conflict markers.
664 (progn
28e4e2b4 665 (smerge-start-session)
1c67a814
SM
666 (add-hook 'after-save-hook 'vc-svn-resolve-when-done nil t))
667 ;; There are no conflict markers. This is problematic: maybe it means
668 ;; the conflict has been resolved and we should immediately call "svn
669 ;; resolved", or it means that the file's type does not allow Svn to
670 ;; use conflict markers in which case we don't really know what to do.
671 ;; So let's just punt for now.
672 nil)
673 (message "There are unresolved conflicts in this file")))
674
bc8c1bb4 675(defun vc-svn-parse-status (&optional filename)
1fd3454a 676 "Parse output of \"svn status\" command in the current buffer.
a6ae021f
GM
677Set file properties accordingly. If FILENAME is non-nil, return its status."
678 (let (multifile file status propstat)
1fd3454a
SM
679 (goto-char (point-min))
680 (while (re-search-forward
484c1b1f 681 ;; Ignore the files with status X.
245cacf1 682 "^\\(?:\\?\\|[ ACDGIMR!~][ MC][ L][ +][ S]..\\([ *]\\) +\\([-0-9]+\\) +\\([0-9?]+\\) +\\([^ ]+\\)\\) +" nil t)
c45b3be3
SM
683 ;; If the username contains spaces, the output format is ambiguous,
684 ;; so don't trust the output's filename unless we have to.
a6ae021f 685 (setq file (or (unless multifile filename)
c45b3be3 686 (expand-file-name
a6ae021f
GM
687 (buffer-substring (point) (line-end-position))))
688 ;; If we are parsing the result of running status on a directory,
689 ;; there could be multiple files in the output.
690 ;; We assume that filename, if supplied, applies to the first
691 ;; listed file (ie, the directory). Bug#15322.
692 multifile t
693 status (char-after (line-beginning-position))
5dd4f3f7
GM
694 ;; Status of the item's properties ([ MC]).
695 propstat (char-after (1+ (line-beginning-position))))
484c1b1f 696 (if (eq status ??)
4f07b9f2 697 (vc-file-setprop file 'vc-state 'unregistered)
fd140743
SM
698 ;; Use the last-modified revision, so that searching in vc-print-log
699 ;; output works.
4f07b9f2 700 (vc-file-setprop file 'vc-working-revision (match-string 3))
1c67a814 701 ;; Remember Svn's own status.
4f07b9f2
ER
702 (vc-file-setprop file 'vc-svn-status status)
703 (vc-file-setprop
1fd3454a
SM
704 file 'vc-state
705 (cond
5dd4f3f7 706 ((and (eq status ?\ ) (eq propstat ?\ ))
1fd3454a 707 (if (eq (char-after (match-beginning 1)) ?*)
3702367b 708 'needs-update
4f07b9f2 709 (vc-file-setprop file 'vc-checkout-time
1fd3454a
SM
710 (nth 5 (file-attributes file)))
711 'up-to-date))
712 ((eq status ?A)
fd140743 713 ;; If the file was actually copied, (match-string 2) is "-".
4f07b9f2
ER
714 (vc-file-setprop file 'vc-working-revision "0")
715 (vc-file-setprop file 'vc-checkout-time 0)
484c1b1f 716 'added)
5dd4f3f7
GM
717 ;; Conflict in contents or properties.
718 ((or (eq status ?C) (eq propstat ?C))
7fbb4797 719 (vc-file-setprop file 'vc-state 'conflict))
5dd4f3f7
GM
720 ;; Modified contents or properties.
721 ((or (eq status ?M) (eq propstat ?M))
1fd3454a
SM
722 (if (eq (char-after (match-beginning 1)) ?*)
723 'needs-merge
724 'edited))
722f037f 725 ((eq status ?I)
4f07b9f2 726 (vc-file-setprop file 'vc-state 'ignored))
e6c01f09 727 ((memq status '(?D ?R))
4f07b9f2 728 (vc-file-setprop file 'vc-state 'removed))
bc8c1bb4 729 (t 'edited)))))
245cacf1 730 (when filename (vc-file-getprop filename 'vc-state))))
1fd3454a 731
1fd3454a
SM
732(defun vc-svn-valid-symbolic-tag-name-p (tag)
733 "Return non-nil if TAG is a valid symbolic tag name."
734 ;; According to the SVN manual, a valid symbolic tag must start with
735 ;; an uppercase or lowercase letter and can contain uppercase and
736 ;; lowercase letters, digits, `-', and `_'.
737 (and (string-match "^[a-zA-Z]" tag)
738 (not (string-match "[^a-z0-9A-Z-_]" tag))))
739
5b5afd50
ER
740(defun vc-svn-valid-revision-number-p (tag)
741 "Return non-nil if TAG is a valid revision number."
1fd3454a
SM
742 (and (string-match "^[0-9]" tag)
743 (not (string-match "[^0-9]" tag))))
744
17a5a301
SM
745;; Support for `svn annotate'
746
747(defun vc-svn-annotate-command (file buf &optional rev)
837b0e99 748 (vc-svn-command buf 'async file "annotate" (if rev (concat "-r" rev))))
17a5a301
SM
749
750(defun vc-svn-annotate-time-of-rev (rev)
da6062e6 751 ;; Arbitrarily assume 10 commits per day.
17a5a301
SM
752 (/ (string-to-number rev) 10.0))
753
e53ac718
DN
754(defvar vc-annotate-parent-rev)
755
17a5a301
SM
756(defun vc-svn-annotate-current-time ()
757 (vc-svn-annotate-time-of-rev vc-annotate-parent-rev))
758
759(defconst vc-svn-annotate-re "[ \t]*\\([0-9]+\\)[ \t]+[^\t ]+ ")
760
761(defun vc-svn-annotate-time ()
762 (when (looking-at vc-svn-annotate-re)
763 (goto-char (match-end 0))
764 (vc-svn-annotate-time-of-rev (match-string 1))))
765
766(defun vc-svn-annotate-extract-revision-at-line ()
767 (save-excursion
768 (beginning-of-line)
769 (if (looking-at vc-svn-annotate-re) (match-string 1))))
770
8228a275
MH
771(defun vc-svn-revision-table (files)
772 (let ((vc-svn-revisions '()))
773 (with-current-buffer "*vc*"
774 (vc-svn-command nil 0 files "log" "-q")
775 (goto-char (point-min))
776 (forward-line)
777 (let ((start (point-min))
778 (loglines (buffer-substring-no-properties (point-min)
779 (point-max))))
780 (while (string-match "^r\\([0-9]+\\) " loglines)
781 (push (match-string 1 loglines) vc-svn-revisions)
782 (setq start (+ start (match-end 0)))
783 (setq loglines (buffer-substring-no-properties start (point-max)))))
784 vc-svn-revisions)))
785
1fd3454a
SM
786(provide 'vc-svn)
787
788;;; vc-svn.el ends here