X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/2766aaafdc18c926fe1d3f82f558ec26fdeabb14..1b74c4346e92c9ac1ae0575c2ad69f8d81126d7e:/lisp/vc-svn.el diff --git a/lisp/vc-svn.el b/lisp/vc-svn.el index eadea67d2e..de34fa847e 100644 --- a/lisp/vc-svn.el +++ b/lisp/vc-svn.el @@ -1,6 +1,6 @@ ;;; vc-svn.el --- non-resident support for Subversion version-control -;; Copyright (C) 1995,98,99,2000,2001,2002 Free Software Foundation, Inc. +;; Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. ;; Author: FSF (see vc.el for full credits) ;; Maintainer: Stefan Monnier @@ -19,8 +19,8 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, -;; Boston, MA 02111-1307, USA. +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. ;;; Commentary: @@ -50,7 +50,7 @@ (repeat :tag "Argument List" :value ("") string)) - :version "21.4" + :version "22.1" :group 'vc) (defcustom vc-svn-register-switches nil @@ -62,52 +62,38 @@ A string or list of strings passed to the checkin program by (repeat :tag "Argument List" :value ("") string)) - :version "21.1" + :version "22.1" :group 'vc) -(defcustom vc-svn-diff-switches nil - "*A string or list of strings specifying extra switches for svn diff under VC." - :type '(choice (const :tag "None" nil) +(defcustom vc-svn-diff-switches + t ;`svn' doesn't support common args like -c or -b. + "String or list of strings specifying extra switches for svn diff under VC. +If nil, use the value of `vc-diff-switches'. +If you want to force an empty list of arguments, use t." + :type '(choice (const :tag "Unspecified" nil) + (const :tag "None" t) (string :tag "Argument String") (repeat :tag "Argument List" :value ("") string)) - :version "21.1" + :version "22.1" :group 'vc) (defcustom vc-svn-header (or (cdr (assoc 'SVN vc-header-alist)) '("\$Id\$")) "*Header keywords to be inserted by `vc-insert-headers'." - :version "21.1" + :version "22.1" :type '(repeat string) :group 'vc) -(defcustom vc-svn-use-edit nil +(defconst vc-svn-use-edit nil + ;; Subversion does not provide this feature (yet). "*Non-nil means to use `svn edit' to \"check out\" a file. This is only meaningful if you don't use the implicit checkout model \(i.e. if you have $SVNREAD set)." - :type 'boolean - :version "21.1" - :group 'vc) - -(defcustom vc-svn-stay-local t - "*Non-nil means use local operations when possible for remote repositories. -This avoids slow queries over the network and instead uses heuristics -and past information to determine the current status of a file. - -The value can also be a regular expression or list of regular -expressions to match against the host name of a repository; then VC -only stays local for hosts that match it. Alternatively, the value -can be a list of regular expressions where the first element is the -symbol `except'; then VC always stays local except for hosts matched -by these regular expressions." - :type '(choice (const :tag "Always stay local" t) - (const :tag "Don't stay local" nil) - (list :format "\nExamine hostname and %v" :tag "Examine hostname ..." - (set :format "%v" :inline t (const :format "%t" :tag "don't" except)) - (regexp :format " stay local,\n%t: %v" :tag "if it matches") - (repeat :format "%v%i\n" :inline t (regexp :tag "or")))) - :version "21.1" - :group 'vc) + ;; :type 'boolean + ;; :version "22.1" + ;; :group 'vc + ) ;;; ;;; State-querying functions @@ -119,6 +105,9 @@ by these regular expressions." ;;;###autoload (load "vc-svn") ;;;###autoload (vc-svn-registered f))) +;;;###autoload +(add-to-list 'completion-ignored-extensions ".svn/") + (defun vc-svn-registered (file) "Check if FILE is SVN registered." (when (file-readable-p (expand-file-name ".svn/entries" @@ -134,7 +123,7 @@ by these regular expressions." (defun vc-svn-state (file &optional localp) "SVN-specific version of `vc-state'." - (setq localp (or localp (vc-svn-stay-local-p file))) + (setq localp (or localp (vc-stay-local-p file))) (with-temp-buffer (cd (file-name-directory file)) (vc-svn-command t 0 file "status" (if localp "-v" "-u")) @@ -147,7 +136,7 @@ by these regular expressions." (defun vc-svn-dir-state (dir &optional localp) "Find the SVN state of all files in DIR." - (setq localp (or localp (vc-svn-stay-local-p dir))) + (setq localp (or localp (vc-stay-local-p dir))) (let ((default-directory dir)) ;; Don't specify DIR in this command, the default-directory is ;; enough. Otherwise it might fail with remote repositories. @@ -168,30 +157,8 @@ by these regular expressions." ;; It looks like Subversion has no equivalent of CVSREAD. 'implicit) -(defun vc-svn-mode-line-string (file) - "Return string for placement into the modeline for FILE. -Compared to the default implementation, this function does two things: -Handle the special case of a SVN file that is added but not yet -committed and support display of sticky tags." - (let* ((state (vc-state file)) - (rev (vc-workfile-version file)) - (sticky-tag (vc-file-getprop file 'vc-svn-sticky-tag)) - (sticky-tag-printable (and sticky-tag - (not (string= sticky-tag "")) - (concat "[" sticky-tag "]")))) - (cond ((string= rev "0") - ;; A file that is added but not yet committed. - "SVN @@") - ((or (eq state 'up-to-date) - (eq state 'needs-patch)) - (concat "SVN-" rev sticky-tag-printable)) - ((stringp state) - (concat "SVN:" state ":" rev sticky-tag-printable)) - (t - ;; Not just for the 'edited state, but also a fallback - ;; for all other states. Think about different symbols - ;; for 'needs-patch and 'needs-merge. - (concat "SVN:" rev sticky-tag-printable))))) +;; vc-svn-mode-line-string doesn't exist because the default implementation +;; works just fine. (defun vc-svn-dired-state-info (file) "SVN-specific version of `vc-dired-state-info'." @@ -213,11 +180,7 @@ COMMENT can be used to provide an initial description of FILE. `vc-register-switches' and `vc-svn-register-switches' are passed to the SVN command (in that order)." - (apply 'vc-svn-command nil 0 file - "add" - ;; (and comment (string-match "[^\t\n ]" comment) - ;; (concat "-m" comment)) - (vc-switches 'SVN 'register))) + (apply 'vc-svn-command nil 0 file "add" (vc-switches 'SVN 'register))) (defun vc-svn-responsible-p (file) "Return non-nil if SVN thinks it is responsible for FILE." @@ -232,8 +195,9 @@ This is only possible if SVN is responsible for FILE's directory.") (defun vc-svn-checkin (file rev comment) "SVN-specific version of `vc-backend-checkin'." - (let ((status (apply 'vc-svn-command nil 1 file - "ci" (list* "-m" comment (vc-switches 'SVN 'checkin))))) + (let ((status (apply + 'vc-svn-command nil 1 file "ci" + (nconc (list "-m" comment) (vc-switches 'SVN 'checkin))))) (set-buffer "*vc*") (goto-char (point-min)) (unless (equal status 0) @@ -282,15 +246,18 @@ This is only possible if SVN is responsible for FILE's directory.") ;; Check out a particular version (or recreate the file). (vc-file-setprop file 'vc-workfile-version nil) (apply 'vc-svn-command nil 0 file - "-w" "update" ;; default for verbose checkout: clear the sticky tag so ;; that the actual update will get the head of the trunk - (if (or (not rev) (string= rev "")) - "-A" - (concat "-r" rev)) + (cond + ((null rev) "-rBASE") + ((or (eq rev t) (equal rev "")) nil) + (t (concat "-r" rev))) switches))) +(defun vc-svn-delete-file (file) + (vc-svn-command nil 0 file "remove")) + (defun vc-svn-rename-file (old new) (vc-svn-command nil 0 new "move" (file-relative-name old))) @@ -368,21 +335,26 @@ The changes are between FIRST-VERSION and SECOND-VERSION." ;;; History functions ;;; -(defun vc-svn-print-log (file) +(defun vc-svn-print-log (file &optional buffer) "Get change log associated with FILE." (save-current-buffer - (vc-setup-buffer nil) + (vc-setup-buffer buffer) (let ((inhibit-read-only t)) (goto-char (point-min)) ;; Add a line to tell log-view-mode what file this is. (insert "Working file: " (file-relative-name file) "\n")) (vc-svn-command - t - (if (and (vc-svn-stay-local-p file) (fboundp 'start-process)) 'async 0) + buffer + (if (and (vc-stay-local-p file) (fboundp 'start-process)) 'async 0) file "log"))) -(defun vc-svn-diff (file &optional oldvers newvers) +(defun vc-svn-diff (file &optional oldvers newvers buffer) "Get a difference report using SVN between two versions of FILE." + (unless buffer (setq buffer "*vc-diff*")) + (if (and oldvers (equal oldvers (vc-workfile-version file))) + ;; Use nil rather than the current revision because svn handles it + ;; better (i.e. locally). + (setq oldvers nil)) (if (string= (vc-workfile-version file) "0") ;; This file is added but not yet committed; there is no master file. (if (or oldvers newvers) @@ -390,54 +362,35 @@ The changes are between FIRST-VERSION and SECOND-VERSION." ;; We regard this as "changed". ;; Diff it against /dev/null. ;; Note: this is NOT a "svn diff". - (apply 'vc-do-command "*vc-diff*" + (apply 'vc-do-command buffer 1 "diff" file (append (vc-switches nil 'diff) '("/dev/null"))) ;; Even if it's empty, it's locally modified. 1) - (let* ((switches (vc-switches 'SVN 'diff)) - (async (and (vc-svn-stay-local-p file) + (let* ((switches + (if vc-svn-diff-switches + (vc-switches 'SVN 'diff) + (list "-x" (mapconcat 'identity (vc-switches nil 'diff) " ")))) + (async (and (not vc-disable-async-diff) + (vc-stay-local-p file) (or oldvers newvers) ; Svn diffs those locally. (fboundp 'start-process)))) - (apply 'vc-svn-command "*vc-diff*" + (apply 'vc-svn-command buffer (if async 'async 0) file "diff" (append - (when switches - (list "-x" (mapconcat 'identity switches " "))) + switches (when oldvers (list "-r" (if newvers (concat oldvers ":" newvers) oldvers))))) (if async 1 ; async diff => pessimistic assumption ;; For some reason `svn diff' does not return a useful ;; status w.r.t whether the diff was empty or not. - (buffer-size (get-buffer "*vc-diff*")))))) + (buffer-size (get-buffer buffer)))))) (defun vc-svn-diff-tree (dir &optional rev1 rev2) "Diff all files at and below DIR." - (with-current-buffer "*vc-diff*" - (setq default-directory dir) - (if (vc-svn-stay-local-p dir) - ;; local diff: do it filewise, and only for files that are modified - (vc-file-tree-walk - dir - (lambda (f) - (vc-exec-after - `(let ((coding-system-for-read (vc-coding-system-for-diff ',f))) - ;; possible optimization: fetch the state of all files - ;; in the tree via vc-svn-dir-state-heuristic - (unless (vc-up-to-date-p ',f) - (message "Looking at %s" ',f) - (vc-diff-internal ',f ',rev1 ',rev2)))))) - ;; svn diff: use a single call for the entire tree - (let ((coding-system-for-read (or coding-system-for-read 'undecided)) - (switches (vc-switches 'SVN 'diff))) - (apply 'vc-svn-command "*vc-diff*" 1 nil "diff" - (append - (when rev1 - (list "-r" (if rev2 (concat rev1 ":" rev2) rev1))) - (when switches - (list "-x" (mapconcat 'identity switches " "))))))))) + (vc-svn-diff (file-name-as-directory dir) rev1 rev2)) ;;; ;;; Snapshot system @@ -446,53 +399,26 @@ The changes are between FIRST-VERSION and SECOND-VERSION." (defun vc-svn-create-snapshot (dir name branchp) "Assign to DIR's current version a given NAME. If BRANCHP is non-nil, the name is created as a branch (and the current -workspace is immediately moved to that new branch)." - (vc-svn-command nil 0 dir "tag" "-c" (if branchp "-b") name) - (when branchp (vc-svn-command nil 0 dir "update" "-r" name))) +workspace is immediately moved to that new branch). +NAME is assumed to be a URL." + (vc-svn-command nil 0 dir "copy" name) + (when branchp (vc-svn-retrieve-snapshot dir name nil))) (defun vc-svn-retrieve-snapshot (dir name update) "Retrieve a snapshot at and below DIR. NAME is the name of the snapshot; if it is empty, do a `svn update'. -If UPDATE is non-nil, then update (resynch) any affected buffers." - (with-current-buffer (get-buffer-create "*vc*") - (let ((default-directory dir) - (sticky-tag)) - (erase-buffer) - (if (or (not name) (string= name "")) - (vc-svn-command t 0 nil "update") - (vc-svn-command t 0 nil "update" "-r" name) - (setq sticky-tag name)) - (when update - (goto-char (point-min)) - (while (not (eobp)) - (if (looking-at "\\([CMUP]\\) \\(.*\\)") - (let* ((file (expand-file-name (match-string 2) dir)) - (state (match-string 1)) - (buffer (find-buffer-visiting file))) - (when buffer - (cond - ((or (string= state "U") - (string= state "P")) - (vc-file-setprop file 'vc-state 'up-to-date) - (vc-file-setprop file 'vc-workfile-version nil) - (vc-file-setprop file 'vc-checkout-time - (nth 5 (file-attributes file)))) - ((or (string= state "M") - (string= state "C")) - (vc-file-setprop file 'vc-state 'edited) - (vc-file-setprop file 'vc-workfile-version nil) - (vc-file-setprop file 'vc-checkout-time 0))) - (vc-file-setprop file 'vc-svn-sticky-tag sticky-tag) - (vc-resynch-buffer file t t)))) - (forward-line 1)))))) - +If UPDATE is non-nil, then update (resynch) any affected buffers. +NAME is assumed to be a URL." + (vc-svn-command nil 0 dir "switch" name) + ;; FIXME: parse the output and obey `update'. + ) ;;; ;;; Miscellaneous ;;; ;; Subversion makes backups for us, so don't bother. -;; (defalias 'vc-svn-make-version-backups-p 'vc-svn-stay-local-p +;; (defalias 'vc-svn-make-version-backups-p 'vc-stay-local-p ;; "Return non-nil if version backups should be made for FILE.") (defun vc-svn-check-headers () @@ -517,59 +443,22 @@ and that it passes `vc-svn-global-switches' to it before FLAGS." (append vc-svn-global-switches flags)))) -(defun vc-svn-stay-local-p (file) - "Return non-nil if VC should stay local when handling FILE. -See `vc-svn-stay-local'." - (when vc-svn-stay-local - (let* ((dirname (if (file-directory-p file) - (directory-file-name file) - (file-name-directory file))) - (prop - (or (vc-file-getprop dirname 'vc-svn-stay-local-p) - (vc-file-setprop - dirname 'vc-svn-stay-local-p - (let ((rootname (expand-file-name ".svn/entries" dirname))) - (cond - ((not (file-readable-p rootname)) 'no) - ((stringp vc-svn-stay-local) - (with-temp-buffer - (let ((coding-system-for-read - (or file-name-coding-system - default-file-name-coding-system))) - (vc-insert-file rootname)) - (goto-char (point-min)) - (when (re-search-forward - (concat "name=\"svn:this_dir\"[\n\t ]*" - "url=\"\\([^\"]+\\)\"") nil t) - (let ((hostname (match-string 1))) - (if (not hostname) - 'no - (let* ((stay-local t) - (rx - (cond - ;; vc-svn-stay-local: rx - ((stringp vc-svn-stay-local) - vc-svn-stay-local) - ;; vc-svn-stay-local: '( [except] rx ... ) - ((consp vc-svn-stay-local) - (mapconcat - 'identity - (if (not (eq (car vc-svn-stay-local) - 'except)) - vc-svn-stay-local - (setq stay-local nil) - (cdr vc-svn-stay-local)) - "\\|"))))) - (if (not rx) - 'yes - (if (not (string-match rx hostname)) - (setq stay-local (not stay-local))) - (if stay-local - 'yes - 'no)))))))) - ;; vc-svn-stay-local is neither nil nor list nor string. - (t 'yes))))))) - (if (eq prop 'yes) t nil)))) +(defun vc-svn-repository-hostname (dirname) + (with-temp-buffer + (let ((coding-system-for-read + (or file-name-coding-system + default-file-name-coding-system))) + (vc-insert-file (expand-file-name ".svn/entries" dirname))) + (goto-char (point-min)) + (when (re-search-forward + ;; Old `svn' used name="svn:dir", newer use just name="". + (concat "name=\"\\(?:svn:this_dir\\)?\"[\n\t ]*" + "\\(?:[-a-z]+=\"[^\"]*\"[\n\t ]*\\)*?" + "url=\"\\([^\"]+\\)\"") nil t) + ;; This is not a hostname but a URL. This may actually be considered + ;; as a feature since it allows vc-svn-stay-local to specify different + ;; behavior for different modules on the same server. + (match-string 1)))) (defun vc-svn-parse-status (localp) "Parse output of \"svn status\" command in the current buffer. @@ -624,6 +513,31 @@ essential information." (and (string-match "^[0-9]" tag) (not (string-match "[^0-9]" tag)))) +;; Support for `svn annotate' + +(defun vc-svn-annotate-command (file buf &optional rev) + (vc-svn-command buf 0 file "annotate" (if rev (concat "-r" rev)))) + +(defun vc-svn-annotate-time-of-rev (rev) + ;; Arbitrarily assume 10 commmits per day. + (/ (string-to-number rev) 10.0)) + +(defun vc-svn-annotate-current-time () + (vc-svn-annotate-time-of-rev vc-annotate-parent-rev)) + +(defconst vc-svn-annotate-re "[ \t]*\\([0-9]+\\)[ \t]+[^\t ]+ ") + +(defun vc-svn-annotate-time () + (when (looking-at vc-svn-annotate-re) + (goto-char (match-end 0)) + (vc-svn-annotate-time-of-rev (match-string 1)))) + +(defun vc-svn-annotate-extract-revision-at-line () + (save-excursion + (beginning-of-line) + (if (looking-at vc-svn-annotate-re) (match-string 1)))) + (provide 'vc-svn) +;; arch-tag: 02f10c68-2b4d-453a-90fc-1eee6cfb268d ;;; vc-svn.el ends here