diff --git a/psvn.el b/psvn.el index e00a1f5..b840b59 100644 --- a/psvn.el +++ b/psvn.el @@ -1,8 +1,8 @@ ;;; psvn.el --- Subversion interface for emacs -;; Copyright (C) 2002-2006 by Stefan Reichoer +;; Copyright (C) 2002-2007 by Stefan Reichoer -;; Author: Stefan Reichoer, -;; $Id: psvn.el 19857 2006-05-30 20:36:48Z xsteve $ +;; Author: Stefan Reichoer +;; $Id: psvn.el 26383 2007-08-29 19:04:04Z xsteve $ ;; psvn.el is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -22,7 +22,10 @@ ;;; Commentary ;; psvn.el is tested with GNU Emacs 21.3 on windows, debian linux, -;; freebsd5, red hat el3 with svn 1.2.3 +;; freebsd5, red hat el4, ubuntu edgy with svn 1.4.0 + +;; psvn.el needs at least svn 1.1.0 +;; if you upgrade to a higher version, you need to do a fresh checkout ;; psvn.el is an interface for the revision control tool subversion ;; (see http://subversion.tigris.org) @@ -51,8 +54,11 @@ ;; A - svn-status-add-file-recursively run 'svn add' ;; + - svn-status-make-directory run 'svn mkdir' ;; R - svn-status-mv run 'svn mv' +;; C - svn-status-cp run 'svn cp' ;; D - svn-status-rm run 'svn rm' ;; M-c - svn-status-cleanup run 'svn cleanup' +;; k - svn-status-lock run 'svn lock' +;; K - svn-status-unlock run 'svn unlock' ;; b - svn-status-blame run 'svn blame' ;; X e - svn-status-export run 'svn export' ;; RET - svn-status-find-file-or-examine-directory @@ -61,13 +67,14 @@ ;; E - svn-status-ediff-with-revision ;; X X - svn-status-resolve-conflicts ;; s - svn-status-show-process-buffer +;; h - svn-status-pop-to-partner-buffer ;; e - svn-status-toggle-edit-cmd-flag ;; ? - svn-status-toggle-hide-unknown ;; _ - svn-status-toggle-hide-unmodified ;; m - svn-status-set-user-mark ;; u - svn-status-unset-user-mark ;; $ - svn-status-toggle-elide -;; w - svn-status-copy-filename-as-kill +;; w - svn-status-copy-current-line-info ;; DEL - svn-status-unset-user-mark-backwards ;; * ! - svn-status-unset-all-usermarks ;; * ? - svn-status-mark-unknown @@ -75,9 +82,12 @@ ;; * M - svn-status-mark-modified ;; * D - svn-status-mark-deleted ;; * * - svn-status-mark-changed +;; * . - svn-status-mark-by-file-ext +;; * % - svn-status-mark-filename-regexp ;; . - svn-status-goto-root-or-return ;; f - svn-status-find-file ;; o - svn-status-find-file-other-window +;; C-o - svn-status-find-file-other-window-noselect ;; v - svn-status-view-file-other-window ;; I - svn-status-parse-info ;; V - svn-status-svnversion @@ -89,9 +99,13 @@ ;; P I - svn-status-property-ignore-file-extension ;; P C-i - svn-status-property-edit-svn-ignore ;; P k - svn-status-property-set-keyword-list +;; P K i - svn-status-property-set-keyword-id +;; P K d - svn-status-property-set-keyword-date ;; P y - svn-status-property-set-eol-style ;; P x - svn-status-property-set-executable -;; h - svn-status-use-history +;; P m - svn-status-property-set-mime-type +;; H - svn-status-use-history +;; x - svn-status-update-buffer ;; q - svn-status-bury-buffer ;; C-x C-j - svn-status-dired-jump @@ -117,7 +131,7 @@ ;; The latest version of psvn.el can be found at: ;; http://www.xsteve.at/prg/emacs/psvn.el ;; Or you can check it out from the subversion repository: -;; svn co http://svn.collab.net/repos/svn/trunk/contrib/client-side/psvn psvn +;; svn co http://svn.collab.net/repos/svn/trunk/contrib/client-side/emacs emacs-svn ;; TODO: ;; * shortcut for svn propset svn:keywords "Date" psvn.el @@ -126,7 +140,6 @@ ;; * when editing the command line - offer help from the svn client ;; * finish svn-status-property-set ;; * Add repository browser -;; * Improve support for svn blame ;; * Get rid of all byte-compiler warnings ;; * SVK working copy support ;; * multiple independent buffers in svn-status-mode @@ -136,38 +149,42 @@ ;; * add implemented ;; * blame implemented ;; * cat implemented -;; * checkout (co) +;; * checkout (co) implemented ;; * cleanup implemented ;; * commit (ci) implemented -;; * copy (cp) +;; * copy (cp) implemented ;; * delete (del, remove, rm) implemented ;; * diff (di) implemented ;; * export implemented ;; * help (?, h) -;; * import +;; * import used (in svn-admin-create-trunk-directory) ;; * info implemented ;; * list (ls) implemented -;; * lock +;; * lock implemented ;; * log implemented ;; * merge ;; * mkdir implemented ;; * move (mv, rename, ren) implemented ;; * propdel (pdel) implemented ;; * propedit (pedit, pe) not needed -;; * propget (pget, pg) used +;; * propget (pget, pg) used (in svn-status-property-edit) ;; * proplist (plist, pl) implemented -;; * propset (pset, ps) used +;; * propset (pset, ps) used (in svn-prop-edit-do-it) ;; * resolved implemented ;; * revert implemented ;; * status (stat, st) implemented ;; * switch (sw) -;; * unlock +;; * unlock implemented ;; * update (up) implemented ;; For the not yet implemented commands you should use the command line ;; svn client. If there are user requests for any missing commands I will ;; probably implement them. +;; There is also limited support for the web-based software project management and bug/issue tracking system trac +;; Trac ticket links can be enabled in the *svn-log* buffers when using the following: +;; (setq svn-log-link-handlers '(trac-ticket-short)) + ;; Comments / suggestions and bug reports are welcome! ;; Development notes @@ -191,23 +208,29 @@ (require 'easymenu) +(eval-when-compile (require 'dired)) +(eval-when-compile (require 'ediff-util)) +(eval-when-compile (require 'elp)) +(eval-when-compile (require 'pp)) + (condition-case nil (progn (require 'diff-mode)) (error nil)) -(defconst svn-psvn-revision "$Id: psvn.el 19857 2006-05-30 20:36:48Z xsteve $" +(defconst svn-psvn-revision "$Id: psvn.el 26383 2007-08-29 19:04:04Z xsteve $" "The revision number of psvn.") ;;; user setable variables (defcustom svn-status-verbose t - "*Add '-v' to svn status call." + "*Add '-v' to svn status call. +This can be toggled with \\[svn-status-toggle-svn-verbose-flag]." :type 'boolean :group 'psvn) (defcustom svn-log-edit-file-name "++svn-log++" "*Name of a saved log file. This can be either absolute, or relative to the default directory -of the *svn-log-edit* buffer." +of the `svn-log-edit-buffer-name' buffer." :type 'file :group 'psvn) (put 'svn-log-edit-file-name 'risky-local-variable t) @@ -221,6 +244,15 @@ of the *svn-log-edit* buffer." This variable takes effect only when psvn.el is being loaded." :type 'boolean :group 'psvn) +(defcustom svn-log-edit-paragraph-start + "$\\|[ \t]*$\\|##.*$\\|\\*.*:.*$\\|[ \t]+(.+):.*$" + "*Value used for `paragraph-start' in `svn-log-edit-buffer-name' buffer." + :type 'regexp + :group 'psvn) +(defcustom svn-log-edit-paragraph-separate "$\\|##.*$" + "*Value used for `paragraph-separate' in `svn-log-edit-buffer-name' buffer." + :type 'regexp + :group 'psvn) (defcustom svn-status-hide-unknown nil "*Hide unknown files in `svn-status-buffer-name' buffer. This can be toggled with \\[svn-status-toggle-hide-unknown]." @@ -234,13 +266,31 @@ This can be toggled with \\[svn-status-toggle-hide-unmodified]." (defcustom svn-status-sort-status-buffer t "*Whether to sort the `svn-status-buffer-name' buffer. -Setting this variable to nil speeds up \[M-x svn-status], however the +Setting this variable to nil speeds up \\[M-x svn-status], however the listing may then become incorrect. This can be toggled with \\[svn-status-toggle-sort-status-buffer]." :type 'boolean :group 'psvn) +(defcustom svn-status-ediff-delete-temporary-files nil + "*Whether to delete temporary ediff files. If set to ask, ask the user" + :type '(choice (const t) + (const nil) + (const ask)) + :group 'psvn) + +(defcustom svn-status-changelog-style 'changelog + "*The changelog style that is used for `svn-file-add-to-changelog'. +Possible values are: + 'changelog: use `add-change-log-entry-other-window' + 'svn-dev: use commit messages that are used by the svn developers + a function: This function is called to add a new entry to the changelog file. +" + :type '(set (const changelog) + (const svn-dev)) + :group 'psvn) + (defcustom svn-status-unmark-files-after-list '(commit revert) "*List of operations after which all user marks will be removed. Possible values are: commit, revert." @@ -248,11 +298,16 @@ Possible values are: commit, revert." (const revert)) :group 'psvn) -(defcustom svn-status-preserve-window-configuration nil +(defcustom svn-status-preserve-window-configuration t "*Try to preserve the window configuration." :type 'boolean :group 'psvn) +(defcustom svn-status-auto-revert-buffers t + "*Auto revert buffers that have changed on disk." + :type 'boolean + :group 'psvn) + (defcustom svn-status-negate-meaning-of-arg-commands '() "*List of operations that should use a negated meaning of the prefix argument. The supported functions are `svn-status' and `svn-status-set-user-mark'." @@ -272,13 +327,15 @@ This can be either absolute or looked up on `exec-path'." :type 'file :group 'psvn) -(defcustom svn-status-svn-environment-var-list '() +(defcustom svn-status-svn-environment-var-list '("LC_MESSAGES=C" "LC_ALL=") "*A list of environment variables that should be set for that svn process. Each element is either a string \"VARIABLE=VALUE\" which will be added to the environment when svn is run, or just \"VARIABLE\" which causes that variable to be entirely removed from the environment. -You could set this for example to '(\"LANG=C\")" +The default setting is '(\"LC_MESSAGES=C\" \"LC_ALL=\"). This ensures that the svn command +line client does not output localized strings. psvn.el relies on the english +messages." :type '(repeat string) :group 'psvn) (put 'svn-status-svn-environment-var-list 'risky-local-variable t) @@ -318,7 +375,7 @@ Any non-nil value overrides that variable, with the same syntax." "An alist to specify which windows should be used for svn command outputs. The following keys are supported: diff, log, info, blame, proplist, update. The following values can be given: -nil ... show in *svn-process* buffer +nil ... show in `svn-process-buffer-name' buffer t ... show in dedicated *svn-info* buffer invisible ... don't show the buffer (eventually useful for update) a string ... show in a buffer named string" @@ -355,7 +412,13 @@ The higher the number, the more debug messages are shown. See `svn-status-message' for the meaning of values for that variable.") +(defvar svn-bookmark-list nil "A list of locations for a quick access via `svn-status-via-bookmark'") +;;(setq svn-bookmark-list '(("proj1" . "~/work/proj1") +;; ("doc1" . "~/docs/doc1"))) + (defvar svn-status-buffer-name "*svn-status*" "Name for the svn status buffer") +(defvar svn-process-buffer-name " *svn-process*" "Name for the svn process buffer") +(defvar svn-log-edit-buffer-name "*svn-log-edit*" "Name for the svn log-edit buffer") (defcustom svn-status-use-header-line (if (boundp 'header-line-format) t 'inline) @@ -370,7 +433,7 @@ Otherwise: Don't display a header line" ;;; default arguments to pass to svn commands ;; TODO: When customizing, an option menu or completion might be nice.... -(defcustom svn-status-default-log-arguments '() +(defcustom svn-status-default-log-arguments '("-v") "*List of arguments to pass to svn log. \(used in `svn-status-show-svn-log'; override these by giving prefixes\)." :type '(repeat string) @@ -388,14 +451,28 @@ equivalent to \".\", so you would commit more than you intended." :group 'psvn) (put 'svn-status-default-commit-arguments 'risky-local-variable t) -(defcustom svn-status-default-diff-arguments '() +(defcustom svn-status-default-diff-arguments '("-x" "--ignore-eol-style") "*A list of arguments that is passed to the svn diff command. -If you'd like to suppress whitespace changes use the following value: -'(\"--diff-cmd\" \"diff\" \"-x\" \"-wbBu\")" +When the built in diff command is used, +the following options are available: --ignore-eol-style, --ignore-space-change, +--ignore-all-space, --ignore-eol-style. +The following setting ignores eol style changes and all white space changes: +'(\"-x\" \"--ignore-eol-style --ignore-all-space\") + +If you'd like to suppress whitespace changes using the external diff command +use the following value: +'(\"--diff-cmd\" \"diff\" \"-x\" \"-wbBu\") + +" :type '(repeat string) :group 'psvn) (put 'svn-status-default-diff-arguments 'risky-local-variable t) +(defcustom svn-status-default-blame-arguments '("-x" "--ignore-eol-style") + "*A list of arguments that is passed to the svn blame command. +See `svn-status-default-diff-arguments' for some examples.") +(put 'svn-status-default-blame-arguments 'risky-local-variable t) + (defvar svn-trac-project-root nil "Path for an eventual existing trac issue tracker. This can be set with \\[svn-status-set-trac-project-root].") @@ -404,31 +481,63 @@ This can be set with \\[svn-status-set-trac-project-root].") "*A short name for the actual project. This can be set with \\[svn-status-set-module-name].") +(defvar svn-status-branch-list nil + "*A list of known branches for the actual project +This can be set with \\[svn-status-set-branch-list]. + +The list contains full repository paths or shortcuts starting with \# +\# at the beginning is replaced by the repository url. +\#1\# has the special meaning that all paths below the given directory +will be considered for interactive selections. + +A useful setting might be: '\(\"\#trunk\" \"\#1\#tags\" \"\#1\#branches\")") + (defvar svn-status-load-state-before-svn-status t "*Whether to automatically restore state from ++psvn.state file before running svn-status.") +(defvar svn-log-link-handlers nil "A list of link handlers in *svn-log* buffers. +These link handlers must be registered via `svn-log-register-link-handler'") + ;;; hooks +(defvar svn-status-mode-hook nil "Hook run when entering `svn-status-mode'.") (defvar svn-log-edit-mode-hook nil "Hook run when entering `svn-log-edit-mode'.") (defvar svn-log-edit-done-hook nil "Hook run after commiting files via svn.") ;; (put 'svn-log-edit-mode-hook 'risky-local-variable t) ;; (put 'svn-log-edit-done-hook 'risky-local-variable t) ;; already implied by "-hook" suffix -(defvar svn-status-coding-system nil - "A special coding system is needed for the output of svn. -svn-status-coding-system is used in svn-run, if it is not nil.") +(defvar svn-post-process-svn-output-hook nil "Hook that can be used to preprocess the output from svn. +The function `svn-status-remove-control-M' can be useful for that hook") + +(when (eq system-type 'windows-nt) + (add-hook 'svn-post-process-svn-output-hook 'svn-status-remove-control-M)) + +(defvar svn-status-svn-process-coding-system (when (boundp 'locale-coding-system) locale-coding-system) + "The coding system that is used for the svn command line client. +It is used in svn-run, if it is not nil.") -(defcustom svn-status-wash-control-M-in-process-buffers - (eq system-type 'windows-nt) - "*Remove any trailing ^M from the *svn-process* buffer." +(defvar svn-status-svn-file-coding-system 'undecided-unix + "The coding system that is used to save files that are loaded as +parameter or data files via the svn command line client. +It is used in the following functions: `svn-prop-edit-do-it', `svn-log-edit-done'. +You could set it to 'utf-8") + +(defcustom svn-status-use-ido-completion + (fboundp 'ido-completing-read) + "*Use ido completion functionality." :type 'boolean :group 'psvn) +(defvar svn-status-completing-read-function + (if svn-status-use-ido-completion 'ido-completing-read 'completing-read)) + ;;; experimental features (defvar svn-status-track-user-input nil "Track user/password queries. This feature is implemented via a process filter. It is an experimental feature.") +(defvar svn-status-refresh-info nil "Whether `svn-status-update-buffer' should call `svn-status-parse-info'.") + ;;; Customize group (defgroup psvn nil "Subversion interface for Emacs." @@ -467,6 +576,18 @@ If t, their full path name will be displayed, else only the filename." (set var value) (global-set-key (symbol-value var) 'svn-global-keymap))) +(defcustom svn-admin-default-create-directory "~/" + "*The default directory that is suggested for `svn-admin-create'." + :type 'string + :group 'psvn) + +(defvar svn-status-custom-hide-function nil + "A function that receives a line-info and decides whether to hide that line. +See psvn.el for an example function.") +;; (put 'svn-status-custom-hide-function 'risky-local-variable t) +;; already implied by "-function" suffix + + ;; Use the normally used mode for files ending in .~HEAD~, .~BASE~, ... (add-to-list 'auto-mode-alist '("\\.~?\\(HEAD\\|BASE\\|PREV\\)~?\\'" ignore t)) @@ -496,7 +617,7 @@ This is nil if the log entry is for a new commit.") (defvar svn-status-head-revision nil) (defvar svn-status-root-return-info nil) (defvar svn-status-property-edit-must-match-flag nil) -(defvar svn-status-propedit-property-name nil) +(defvar svn-status-propedit-property-name nil "The property name for the actual svn propset command") (defvar svn-status-propedit-file-list nil) (defvar svn-status-mode-line-process "") (defvar svn-status-mode-line-process-status "") @@ -504,6 +625,7 @@ This is nil if the log entry is for a new commit.") (defvar svn-status-edit-svn-command nil) (defvar svn-status-update-previous-process-output nil) (defvar svn-pre-run-asynch-recent-keys nil) +(defvar svn-pre-run-mode-line-process nil) (defvar svn-status-temp-dir (expand-file-name (or @@ -523,18 +645,28 @@ This is nil if the log entry is for a new commit.") (defvar svn-status-options nil) (defvar svn-status-remote) (defvar svn-status-commit-rev-number nil) +(defvar svn-status-update-rev-number nil) (defvar svn-status-operated-on-dot nil) +(defvar svn-status-last-commit-author nil) (defvar svn-status-elided-list nil) -(defvar svn-status-custom-hide-function nil) -;; (put 'svn-status-custom-hide-function 'risky-local-variable t) -;; already implied by "-function" suffix -(defvar svn-status-get-specific-revision-file-info) -(defvar svn-status-last-output-buffer-name) +(defvar svn-status-last-output-buffer-name nil "The buffer name for the buffer that holds the output from the last executed svn command") (defvar svn-status-pre-run-svn-buffer nil) (defvar svn-status-update-list nil) (defvar svn-transient-buffers) (defvar svn-ediff-windows) (defvar svn-ediff-result) +(defvar svn-status-last-diff-options nil) +(defvar svn-status-blame-file-name nil) +(defvar svn-admin-last-repository-dir nil "The last repository url for various operations.") +(defvar svn-last-cmd-ring (make-ring 30) "Ring that holds the last executed svn commands (for debugging purposes)") +(defvar svn-status-cached-version-string nil) +(defvar svn-client-version nil "The version number of the used svn client") +(defvar svn-status-get-line-information-for-file nil) +(defvar svn-status-base-dir-cache (make-hash-table :test 'equal :weakness nil)) +(defvar svn-log-registered-link-handlers (make-hash-table :test 'eql :weakness nil)) + +(defvar svn-status-partner-buffer nil "The partner buffer for this svn related buffer") +(make-variable-buffer-local 'svn-status-partner-buffer) ;; Emacs 21 defines these in ediff-init.el but it seems more robust ;; to just declare the variables here than try to load that file. @@ -555,6 +687,8 @@ This is nil if the log entry is for a new commit.") (defvar ediff-after-quit-destination-buffer) ;; That is an example for the svn-status-custom-hide-function: +;; Note: For many cases it is a better solution to ignore files or +;; file extensions via the svn-ignore properties (on P i, P I) ;; (setq svn-status-custom-hide-function 'svn-status-hide-pyc-files) ;; (defun svn-status-hide-pyc-files (info) ;; "Hide all pyc files in the `svn-status-buffer-name' buffer." @@ -609,6 +743,17 @@ See `svn-status--line-info->directory-p' for what counts as a directory." See `svn-status--line-info->directory-p' for what counts as a directory." :group 'psvn-faces) +;not based on anything, may be horribly ugly! +(defface svn-status-symlink-face + '((((class color) (background light)) (:foreground "cornflower blue")) + (((class color) (background dark)) (:foreground "cyan"))) + "Face for symlinks in *svn-status* buffers. + +This is the face given to the actual link (i.e., the versioned item), +the target of the link gets either `svn-status-filename-face' or +`svn-status-directory-face'." + :group 'psvn-faces) + ;based on font-lock-warning-face (defface svn-status-locked-face '((t @@ -629,6 +774,26 @@ See `svn-status--line-info->directory-p' for what counts as a directory." "Face for the phrase \"(switched)\" non-directories in svn status buffers." :group 'psvn-faces) +(if svn-xemacsp + (defface svn-status-blame-highlight-face + '((((type tty) (class color)) (:foreground "green" :weight light)) + (((class color) (background light)) (:foreground "green3")) + (((class color) (background dark)) (:foreground "palegreen2")) + (t (:weight bold))) + "Default face for highlighting a line in svn status blame mode." + :group 'psvn-faces) + (defface svn-status-blame-highlight-face + '((t :inherit highlight)) + "Default face for highlighting a line in svn status blame mode." + :group 'psvn-faces)) + +(defface svn-status-blame-rev-number-face + '((((class color) (background light)) (:foreground "DarkGoldenrod")) + (((class color) (background dark)) (:foreground "LightGoldenrod")) + (t (:weight bold :slant italic))) + "Face to highlight revision numbers in the svn-blame mode." + :group 'psvn-faces) + (defvar svn-highlight t) ;; stolen from PCL-CVS (defun svn-add-face (str face &optional keymap) @@ -667,8 +832,8 @@ Otherwise, return \"\"." (svn-add-face string face) "")) -; compatibility -; emacs 20 +;; compatibility +;; emacs 20 (defalias 'svn-point-at-eol (if (fboundp 'point-at-eol) 'point-at-eol 'line-end-position)) (defalias 'svn-point-at-bol @@ -681,6 +846,20 @@ Otherwise, return \"\"." (require 'cl-macs))) (defalias 'svn-puthash (if (fboundp 'puthash) 'puthash 'cl-puthash)) +;; emacs 21 +(if (fboundp 'line-number-at-pos) + (defalias 'svn-line-number-at-pos 'line-number-at-pos) + (defun svn-line-number-at-pos (&optional pos) + "Return (narrowed) buffer line number at position POS. +If POS is nil, use current buffer location." + (let ((opoint (or pos (point))) start) + (save-excursion + (goto-char (point-min)) + (setq start (point)) + (goto-char opoint) + (forward-line 0) + (1+ (count-lines start (point))))))) + ; xemacs ;; Evaluate the defsubst at compile time, so that the byte compiler ;; knows the definition and can inline calls. It cannot detect the @@ -735,11 +914,17 @@ To bind this to a different key, customize `svn-status-prefix-key'.") (put 'svn-global-keymap 'risky-local-variable t) (when (not svn-global-keymap) (setq svn-global-keymap (make-sparse-keymap)) + (define-key svn-global-keymap (kbd "v") 'svn-status-version) (define-key svn-global-keymap (kbd "s") 'svn-status-this-directory) - (define-key svn-global-keymap (kbd "l") 'svn-status-show-svn-log) + (define-key svn-global-keymap (kbd "b") 'svn-status-via-bookmark) + (define-key svn-global-keymap (kbd "h") 'svn-status-use-history) (define-key svn-global-keymap (kbd "u") 'svn-status-update-cmd) (define-key svn-global-keymap (kbd "=") 'svn-status-show-svn-diff) - (define-key svn-global-keymap (kbd "b") 'svn-status-blame) + (define-key svn-global-keymap (kbd "f =") 'svn-file-show-svn-diff) + (define-key svn-global-keymap (kbd "f e") 'svn-file-show-svn-ediff) + (define-key svn-global-keymap (kbd "f l") 'svn-status-show-svn-log) + (define-key svn-global-keymap (kbd "f b") 'svn-status-blame) + (define-key svn-global-keymap (kbd "f a") 'svn-file-add-to-changelog) (define-key svn-global-keymap (kbd "c") 'svn-status-commit) (define-key svn-global-keymap (kbd "S") 'svn-status-switch-to-status-buffer) (define-key svn-global-keymap (kbd "o") 'svn-status-pop-to-status-buffer)) @@ -750,15 +935,21 @@ To bind this to a different key, customize `svn-status-prefix-key'.") (when (not svn-status-diff-mode-map) (setq svn-status-diff-mode-map (copy-keymap diff-mode-shared-map)) + (define-key svn-status-diff-mode-map [?g] 'revert-buffer) + (define-key svn-status-diff-mode-map [?s] 'svn-status-pop-to-status-buffer) + (define-key svn-status-diff-mode-map [?c] 'svn-status-diff-pop-to-commit-buffer) (define-key svn-status-diff-mode-map [?w] 'svn-status-diff-save-current-defun-as-kill)) - (defvar svn-global-trac-map () "Subkeymap used in `svn-global-keymap' for trac issue tracker commands.") (put 'svn-global-trac-map 'risky-local-variable t) ;for Emacs 20.7 (when (not svn-global-trac-map) (setq svn-global-trac-map (make-sparse-keymap)) + (define-key svn-global-trac-map (kbd "w") 'svn-trac-browse-wiki) (define-key svn-global-trac-map (kbd "t") 'svn-trac-browse-timeline) + (define-key svn-global-trac-map (kbd "m") 'svn-trac-browse-roadmap) + (define-key svn-global-trac-map (kbd "s") 'svn-trac-browse-source) + (define-key svn-global-trac-map (kbd "r") 'svn-trac-browse-report) (define-key svn-global-trac-map (kbd "i") 'svn-trac-browse-ticket) (define-key svn-global-trac-map (kbd "c") 'svn-trac-browse-changeset) (define-key svn-global-keymap (kbd "t") svn-global-trac-map)) @@ -782,6 +973,14 @@ To bind this to a different key, customize `svn-status-prefix-key'.") "_svn" ".svn")) +(defun svn-log-edit-file-name (&optional curdir) + "Get the name of the saved log edit file +If curdir, return `svn-log-edit-file-name' +Otherwise position svn-log-edit-file-name in the root directory of this working copy" + (if curdir + svn-log-edit-file-name + (concat (svn-status-base-dir) svn-log-edit-file-name))) + (defun svn-status-message (level &rest args) "If LEVEL is lower than `svn-status-debug-level' print ARGS using `message'. @@ -798,7 +997,17 @@ inside loops." if (listp item) nconc (svn-status-flatten-list item) else collect item)) +(defun svn-status-window-line-position (w) + "Return the window line at point for window W, or nil if W is nil." + (svn-status-message 3 "About to count lines; selected window is %s" (selected-window)) + (and w (count-lines (window-start w) (point)))) +;;;###autoload +(defun svn-checkout (repos-url path) + "Run svn checkout REPOS-URL PATH." + (interactive (list (read-string "Checkout from repository Url: ") + (svn-read-directory-name "Checkout to directory: "))) + (svn-run t t 'checkout "checkout" repos-url (expand-file-name path))) ;;;###autoload (defalias 'svn-examine 'svn-status) (defalias 'svn-examine 'svn-status) @@ -808,9 +1017,11 @@ inside loops." "Examine the status of Subversion working copy in directory DIR. If ARG is -, allow editing of the parameters. One could add -N to run svn status non recursively to make it faster. -For every other non nil ARG pass the -u argument to `svn status'. +For every other non nil ARG pass the -u argument to `svn status', which +asks svn to connect to the repository and check to see if there are updates +there. -If there is no .svn directory, examine if there is SVN and run +If there is no .svn directory, examine if there is CVS and run `cvs-examine'. Otherwise ask if to run `dired'." (interactive (list (svn-read-directory-name "SVN status directory: " nil default-directory nil) @@ -846,7 +1057,8 @@ If there is no .svn directory, examine if there is SVN and run (setq dir (file-name-as-directory dir)) (when svn-status-load-state-before-svn-status (unless (string= dir (car svn-status-directory-history)) - (svn-status-load-state t))) + (let ((default-directory dir)) ;otherwise svn-status-base-dir looks in the wrong place + (svn-status-load-state t)))) (setq svn-status-directory-history (delete dir svn-status-directory-history)) (add-to-list 'svn-status-directory-history dir) (if (string= (buffer-name) svn-status-buffer-name) @@ -855,8 +1067,9 @@ If there is no .svn directory, examine if there is SVN and run ;;(message "psvn: Saving initial window configuration") (setq svn-status-initial-window-configuration (current-window-configuration))) - (let* ((status-buf (get-buffer-create svn-status-buffer-name)) - (proc-buf (get-buffer-create "*svn-process*")) + (let* ((cur-buf (current-buffer)) + (status-buf (get-buffer-create svn-status-buffer-name)) + (proc-buf (get-buffer-create svn-process-buffer-name)) (want-edit (eq arg '-)) (status-option (if want-edit (if svn-status-verbose "-v" "") @@ -871,6 +1084,7 @@ If there is no .svn directory, examine if there is SVN and run (set-buffer proc-buf) (setq default-directory dir svn-status-remote (when arg t)) + (set-buffer cur-buf) (svn-run t t 'status "status" status-option)))) (defun svn-status-this-directory (arg) @@ -879,13 +1093,18 @@ If there is no .svn directory, examine if there is SVN and run (svn-status default-directory arg)) (defun svn-status-use-history () + "Interactively select a different directory from `svn-status-directory-history'." (interactive) - (let* ((hist svn-status-directory-history) - (dir (read-from-minibuffer "svn-status on directory: " - (cadr svn-status-directory-history) - nil nil 'hist))) + (let* ((in-status-buffer (eq major-mode 'svn-status-mode)) + (hist (if in-status-buffer (cdr svn-status-directory-history) svn-status-directory-history)) + (dir (funcall svn-status-completing-read-function "svn-status on directory: " hist)) + (svn-buffer-available (with-current-buffer (get-buffer svn-status-buffer-name) (string= default-directory dir)))) (if (file-directory-p dir) - (svn-status dir) + (if svn-buffer-available + (svn-status-switch-to-status-buffer) + (unless svn-status-refresh-info + (setq svn-status-refresh-info 'once)) + (svn-status dir)) (error "%s is not a directory" dir)))) (defun svn-had-user-input-since-asynch-run () @@ -916,7 +1135,7 @@ the usual `process-environment'." If RUN-ASYNCHRON is t then run svn asynchronously. If CLEAR-PROCESS-BUFFER is t then erase the contents of the -*svn-process* buffer before commencing. +`svn-process-buffer-name' buffer before commencing. CMDTYPE is a symbol such as 'mv, 'revert, or 'add, representing the command to run. @@ -926,7 +1145,9 @@ for example: '(\"revert\" \"file1\"\) ARGLIST is flattened and any every nil value is discarded. If the variable `svn-status-edit-svn-command' is non-nil then the user -can edit ARGLIST before running svn." +can edit ARGLIST before running svn. + +The hook svn-pre-run-hook allows to monitor/modify the ARGLIST." (setq arglist (svn-status-flatten-list arglist)) (if (eq (process-status "svn") nil) (progn @@ -940,24 +1161,33 @@ can edit ARGLIST before running svn." (when (eq svn-status-edit-svn-command t) (svn-status-toggle-edit-cmd-flag t)) (message "svn-run %s: %S" cmdtype arglist)) - (let* ((proc-buf (get-buffer-create "*svn-process*")) + (run-hooks 'svn-pre-run-hook) + (unless (eq mode-line-process 'svn-status-mode-line-process) + (setq svn-pre-run-mode-line-process mode-line-process) + (setq mode-line-process 'svn-status-mode-line-process)) + (setq svn-status-pre-run-svn-buffer (current-buffer)) + (let* ((proc-buf (get-buffer-create svn-process-buffer-name)) (svn-exe svn-status-svn-executable) (svn-proc)) (when (listp (car arglist)) (setq arglist (car arglist))) (save-excursion (set-buffer proc-buf) - (when svn-status-coding-system - (setq buffer-file-coding-system svn-status-coding-system)) + (unless (file-executable-p default-directory) + (message "psvn: workaround in %s needed: %s no longer exists" (current-buffer) default-directory) + (cd (expand-file-name "~"))) (setq buffer-read-only nil) + (buffer-disable-undo) (fundamental-mode) (if clear-process-buffer (delete-region (point-min) (point-max)) (goto-char (point-max))) (setq svn-process-cmd cmdtype) + (setq svn-status-last-commit-author nil) (setq svn-status-mode-line-process-status (format " running %s" cmdtype)) (svn-status-update-mode-line) (sit-for 0.1) + (ring-insert svn-last-cmd-ring (list (current-time-string) arglist default-directory)) (if run-asynchron (progn ;;(message "running asynchron: %s %S" svn-exe arglist) @@ -974,6 +1204,9 @@ can edit ARGLIST before running svn." ;; such cases, the user should start ssh-agent and ;; then run ssh-add explicitly. (setq svn-proc (apply 'start-process "svn" proc-buf svn-exe arglist))) + (when svn-status-svn-process-coding-system + (set-process-coding-system svn-proc svn-status-svn-process-coding-system + svn-status-svn-process-coding-system)) (set-process-sentinel svn-proc 'svn-process-sentinel) (when svn-status-track-user-input (set-process-filter svn-proc 'svn-process-filter))) @@ -982,9 +1215,13 @@ can edit ARGLIST before running svn." ;; `call-process' ignores `process-connection-type' and ;; never opens a pseudoterminal. (apply 'call-process svn-exe nil proc-buf nil arglist)) + (setq svn-status-last-output-buffer-name svn-process-buffer-name) + (run-hooks 'svn-post-process-svn-output-hook) (setq svn-status-mode-line-process-status "") - (svn-status-update-mode-line))) - (setq svn-status-pre-run-svn-buffer (current-buffer)))) + (svn-status-update-mode-line) + (when svn-pre-run-mode-line-process + (setq mode-line-process svn-pre-run-mode-line-process) + (setq svn-pre-run-mode-line-process nil)))))) (error "You can only run one svn process at once!"))) (defun svn-process-sentinel-fixup-path-seperators () @@ -1000,16 +1237,20 @@ can edit ARGLIST before running svn." ;;(princ (format "Process: %s had the event `%s'" process event))) ;;(save-excursion (let ((act-buf (current-buffer))) + (when svn-pre-run-mode-line-process + (with-current-buffer svn-status-pre-run-svn-buffer + (setq mode-line-process svn-pre-run-mode-line-process)) + (setq svn-pre-run-mode-line-process nil)) (set-buffer (process-buffer process)) (setq svn-status-mode-line-process-status "") (svn-status-update-mode-line) (cond ((string= event "finished\n") + (run-hooks 'svn-post-process-svn-output-hook) (cond ((eq svn-process-cmd 'status) ;;(message "svn status finished") (svn-process-sentinel-fixup-path-seperators) (svn-parse-status-result) - (set-buffer act-buf) - (svn-status-update-buffer) + (svn-status-apply-elide-list) (when svn-status-update-previous-process-output (set-buffer (process-buffer process)) (delete-region (point-min) (point-max)) @@ -1031,7 +1272,9 @@ can edit ARGLIST before running svn." (svn-status-show-process-output 'log t) (pop-to-buffer svn-status-last-output-buffer-name) (svn-log-view-mode) - (forward-line 3) + (forward-line 2) + (unless (looking-at "Changed paths:") + (forward-line 1)) (font-lock-fontify-buffer) (message "svn log finished")) ((eq svn-process-cmd 'info) @@ -1040,6 +1283,9 @@ can edit ARGLIST before running svn." ((eq svn-process-cmd 'ls) (svn-status-show-process-output 'info t) (message "svn ls finished")) + ((eq svn-process-cmd 'diff) + (svn-status-activate-diff-mode) + (message "svn diff finished")) ((eq svn-process-cmd 'parse-info) (svn-status-parse-info-result)) ((eq svn-process-cmd 'blame) @@ -1047,7 +1293,11 @@ can edit ARGLIST before running svn." (when svn-status-pre-run-svn-buffer (with-current-buffer svn-status-pre-run-svn-buffer (unless (eq major-mode 'svn-status-mode) - (goto-line (line-number-at-pos) (get-buffer svn-status-last-output-buffer-name))))) + (let ((src-line-number (svn-line-number-at-pos))) + (pop-to-buffer (get-buffer svn-status-last-output-buffer-name)) + (goto-line src-line-number))))) + (with-current-buffer (get-buffer svn-status-last-output-buffer-name) + (svn-status-activate-blame-mode)) (message "svn blame finished")) ((eq svn-process-cmd 'commit) (svn-process-sentinel-fixup-path-seperators) @@ -1055,18 +1305,28 @@ can edit ARGLIST before running svn." (when (member 'commit svn-status-unmark-files-after-list) (svn-status-unset-all-usermarks)) (svn-status-update-with-command-list (svn-status-parse-commit-output)) + (svn-revert-some-buffers) (run-hooks 'svn-log-edit-done-hook) (setq svn-status-files-to-commit nil svn-status-recursive-commit nil) - (message "svn commit finished")) + (message "svn: Committed revision %s." svn-status-commit-rev-number)) ((eq svn-process-cmd 'update) (svn-status-show-process-output 'update t) (setq svn-status-update-list (svn-status-parse-update-output)) + (svn-revert-some-buffers) (svn-status-update) - (message "svn update finished")) + (if (car svn-status-update-rev-number) + (message "svn: Updated to revision %s." (cadr svn-status-update-rev-number)) + (message "svn: At revision %s." (cadr svn-status-update-rev-number)))) ((eq svn-process-cmd 'add) (svn-status-update-with-command-list (svn-status-parse-ar-output)) (message "svn add finished")) + ((eq svn-process-cmd 'lock) + (svn-status-update) + (message "svn lock finished")) + ((eq svn-process-cmd 'unlock) + (svn-status-update) + (message "svn unlock finished")) ((eq svn-process-cmd 'mkdir) (svn-status-update) (message "svn mkdir finished")) @@ -1078,9 +1338,6 @@ can edit ARGLIST before running svn." ((eq svn-process-cmd 'resolved) (svn-status-update) (message "svn resolved finished")) - ((eq svn-process-cmd 'mv) - (svn-status-update) - (message "svn mv finished")) ((eq svn-process-cmd 'rm) (svn-status-update-with-command-list (svn-status-parse-ar-output)) (message "svn rm finished")) @@ -1089,6 +1346,8 @@ can edit ARGLIST before running svn." ((eq svn-process-cmd 'proplist) (svn-status-show-process-output 'proplist t) (message "svn proplist finished")) + ((eq svn-process-cmd 'checkout) + (svn-status default-directory)) ((eq svn-process-cmd 'proplist-parse) (svn-status-property-parse-property-names)) ((eq svn-process-cmd 'propset) @@ -1104,17 +1363,33 @@ can edit ARGLIST before running svn." (while (accept-process-output process 0 100)) ;; find last error message and show it. (goto-char (point-max)) - (message "svn failed: %s" - (if (re-search-backward "^svn: \\(.*\\)" nil t) - (match-string 1) - event))) + (if (re-search-backward "^svn: \\(.*\\)" nil t) + (svn-process-handle-error (match-string 1)) + (message "svn failed: %s" event))) (t (message "svn process had unknown event: %s" event)) (svn-status-show-process-output nil t)))) +(defun svn-process-handle-error (error-msg) + (let ((svn-process-handle-error-msg error-msg)) + (electric-helpify 'svn-process-help-with-error-msg))) + +(defun svn-process-help-with-error-msg () + (interactive) + (let ((help-msg (cadr (assoc svn-process-handle-error-msg + '(("Cannot non-recursively commit a directory deletion" + "Please unmark all files and position point at the directory you would like to remove.\nThen run commit again.")))))) + (if help-msg + (save-excursion + (with-output-to-temp-buffer (help-buffer) + (princ (format "svn failed: %s\n\n%s" svn-process-handle-error-msg help-msg)))) + (message "svn failed: %s" svn-process-handle-error-msg)))) + + (defun svn-process-filter (process str) + "Track the svn process output and ask user questions in the minibuffer when appropriate." (save-window-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) ;;(message "svn-process-filter: %s" str) (goto-char (point-max)) (insert str) @@ -1124,10 +1399,36 @@ can edit ARGLIST before running svn." ;(svn-status-show-process-buffer) (let ((passwd (read-passwd (format "Enter svn password for %s: " (match-string 1))))) - (svn-process-send-string (concat passwd "\n") t))) + (svn-process-send-string-and-newline passwd t))) (when (looking-at "Username: ") (let ((user-name (read-string "Username for svn operation: "))) - (svn-process-send-string (concat user-name "\n"))))))) + (svn-process-send-string-and-newline user-name))) + (when (looking-at "(R)eject, accept (t)emporarily or accept (p)ermanently") + (svn-status-show-process-buffer) + (let ((answer (read-string "(R)eject, accept (t)emporarily or accept (p)ermanently? "))) + (svn-process-send-string (substring answer 0 1))))))) + +(defun svn-revert-some-buffers (&optional tree) + "Reverts all buffers visiting a file in TREE that aren't modified. +To be run after a commit, an update or a merge." + (interactive) + (let ((tree (or (svn-status-base-dir) tree))) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (not (buffer-modified-p)) + (let ((file (buffer-file-name))) + (when file + (let ((root (svn-status-base-dir (file-name-directory file))) + (point-pos (point))) + (when (and root + (string= root tree) + ;; buffer is modified and in the tree TREE. + svn-status-auto-revert-buffers) + ;; (message "svn-revert-some-buffers: %s %s" (buffer-file-name) (verify-visited-file-modtime (current-buffer))) + ;; Keep the buffer if the file doesn't exist + (when (and (file-exists-p file) (not (verify-visited-file-modtime (current-buffer)))) + (revert-buffer t t) + (goto-char point-pos))))))))))) (defun svn-parse-rev-num (str) (if (and str (stringp str) @@ -1159,16 +1460,77 @@ structure." (list nil nil)) (defun svn-status-make-dummy-dirs (dir-list old-ui-information) + "Calculate additionally necessary directories that were not shown in the output +of 'svn status'" + ;; (message "svn-status-make-dummy-dirs %S" dir-list) + (let ((candidate) + (base-dir)) + (dolist (dir dir-list) + (setq base-dir (file-name-directory dir)) + (while base-dir + ;;(message "dir: %S dir-list: %S, base-dir: %S" dir dir-list base-dir) + (setq candidate (replace-regexp-in-string "/+$" "" base-dir)) + (setq base-dir (file-name-directory candidate)) + ;; (message "dir: %S, candidate: %S" dir candidate) + (add-to-list 'dir-list candidate)))) + ;; (message "svn-status-make-dummy-dirs %S" dir-list) (append (mapcar (lambda (dir) - (list (or (gethash dir old-ui-information) - (svn-status-make-ui-status)) - 32 nil dir -1 -1 "?" nil nil nil nil)) + (svn-status-make-line-info + dir + (gethash dir old-ui-information))) dir-list) svn-status-info)) +(defun svn-status-make-line-info (&optional + path + ui + file-mark prop-mark + local-rev last-change-rev + author + update-mark + locked-mark + with-history-mark + switched-mark + locked-repo-mark + psvn-extra-info) + "Create a new line-info from the given arguments +Anything left nil gets a sensible default. +nb: LOCKED-MARK refers to the kind of locks you get after an error, + LOCKED-REPO-MARK is the kind managed with `svn lock'" + (list (or ui (svn-status-make-ui-status)) + (or file-mark ? ) + (or prop-mark ? ) + (or path "") + (or local-rev ? ) + (or last-change-rev ? ) + (or author "") + update-mark + locked-mark + with-history-mark + switched-mark + locked-repo-mark + psvn-extra-info)) + +(defvar svn-user-names-including-blanks nil "A list of svn user names that include blanks.") +;;(setq svn-user-names-including-blanks '("feng shui" "mister blank")) +;;(add-hook 'svn-pre-parse-status-hook 'svn-status-parse-fixup-user-names-including-blanks) + +(defun svn-status-parse-fixup-user-names-including-blanks () + "Helper function to allow user names that include blanks. +Add this function to the `svn-pre-parse-status-hook'. The variable +`svn-user-names-including-blanks' must be configured to hold all user names that contain +blanks. This function replaces the blanks with '-' to allow further processing with +the usual parsing functionality in `svn-parse-status-result'." + (when svn-user-names-including-blanks + (goto-char (point-min)) + (let ((search-string (concat " \\(" (mapconcat 'concat svn-user-names-including-blanks "\\|") "\\) "))) + (save-match-data + (save-excursion + (while (re-search-forward search-string (point-max) t) + (replace-match (replace-regexp-in-string " " "-" (match-string 1)) nil nil nil 1))))))) (defun svn-parse-status-result () - "Parse the *svn-process* buffer. + "Parse the `svn-process-buffer-name' buffer. The results are used to build the `svn-status-info' variable." (setq svn-status-head-revision nil) (save-excursion @@ -1176,7 +1538,8 @@ The results are used to build the `svn-status-info' variable." (svn-marks) (svn-file-mark) (svn-property-mark) - (svn-locked-mark) + (svn-wc-locked-mark) + (svn-repo-locked-mark) (svn-with-history-mark) (svn-switched-mark) (svn-update-mark) @@ -1187,11 +1550,18 @@ The results are used to build the `svn-status-info' variable." (dir) (revision-width svn-status-default-revision-width) (author-width svn-status-default-author-width) - (svn-marks-length (if (and svn-status-verbose svn-status-remote) - 8 5)) - (dir-set '("."))) - (set-buffer "*svn-process*") + (svn-marks-length (if svn-status-verbose + (if svn-status-remote + 8 6) + (if svn-status-remote + ;; not verbose + 8 7))) + (dir-set '(".")) + (externals-map (make-hash-table :test 'equal)) + (skip-double-external-dir-entry-name nil)) + (set-buffer svn-process-buffer-name) (setq svn-status-info nil) + (run-hooks 'svn-pre-parse-status-hook) (goto-char (point-min)) (while (< (point) (point-max)) (cond @@ -1206,24 +1576,40 @@ The results are used to build the `svn-status-info' variable." ;; [ie the directory in (match-string 1)] ;; we should parse it, and merge the info with what we have already know ;; but for now just ignore the line completely - (forward-line) - ) + ; (forward-line) + ;; Actually, this seems to not always be the case + ;; I have an example where we are in an svn:external which + ;; is itself inside a svn:external, this need not be true: + ;; the next line is not 'X dir' but just 'dir', so we + ;; actually need to parse that line, or the results will + ;; not contain dir! + ;; so we should merge lines 'X dir' with ' dir', but for now + ;; we just leave both in the results + + ;; My attempt to merge the lines uses skip-double-external-dir-entry-name + ;; and externals-map + (setq skip-double-external-dir-entry-name (match-string-no-properties 1)) + ;; (message "Going to skip %s" skip-double-external-dir-entry-name) + nil) (t (setq svn-marks (buffer-substring (point) (+ (point) svn-marks-length)) svn-file-mark (elt svn-marks 0) ; 1st column - M,A,C,D,G,? etc svn-property-mark (elt svn-marks 1) ; 2nd column - M,C (properties) - svn-locked-mark (elt svn-marks 2) ; 3rd column - L or blank + svn-wc-locked-mark (elt svn-marks 2) ; 3rd column - L or blank svn-with-history-mark (elt svn-marks 3) ; 4th column - + or blank - svn-switched-mark (elt svn-marks 4)) ; 5th column - S or blank - (if (and svn-status-verbose svn-status-remote) + svn-switched-mark (elt svn-marks 4) ; 5th column - S or blank + svn-repo-locked-mark (elt svn-marks 5)) ; 6th column - K,O,T,B or blank + (when svn-status-remote (setq svn-update-mark (elt svn-marks 7))) ; 8th column - * or blank (when (eq svn-property-mark ?\ ) (setq svn-property-mark nil)) - (when (eq svn-locked-mark ?\ ) (setq svn-locked-mark nil)) + (when (eq svn-wc-locked-mark ?\ ) (setq svn-wc-locked-mark nil)) (when (eq svn-with-history-mark ?\ ) (setq svn-with-history-mark nil)) (when (eq svn-switched-mark ?\ ) (setq svn-switched-mark nil)) (when (eq svn-update-mark ?\ ) (setq svn-update-mark nil)) + (when (eq svn-repo-locked-mark ?\ ) (setq svn-repo-locked-mark nil)) (forward-char svn-marks-length) (skip-chars-forward " ") + ;; (message "after marks: '%s'" (buffer-substring (point) (line-end-position))) (cond ((looking-at "\\([-?]\\|[0-9]+\\) +\\([-?]\\|[0-9]+\\) +\\([^ ]+\\) *\\(.+\\)$") (setq local-rev (svn-parse-rev-num (match-string 1)) @@ -1239,7 +1625,7 @@ The results are used to build the `svn-status-info' variable." (setq path (match-string 1) local-rev -1 last-change-rev -1 - author (if (eq svn-file-mark 88) "" "?"))) ;clear author of svn:externals dirs + author (if (eq svn-file-mark ?X) "" "?"))) ;clear author of svn:externals dirs (t (error "Unknown status line format"))) (unless path (setq path ".")) @@ -1248,19 +1634,36 @@ The results are used to build the `svn-status-info' variable." (let ((dirname (directory-file-name dir))) (if (not (member dirname dir-set)) (setq dir-set (cons dirname dir-set))))) - (setq svn-status-info (cons (list (or (gethash path old-ui-information) - (svn-status-make-ui-status)) - svn-file-mark - svn-property-mark - path - local-rev - last-change-rev - author - svn-update-mark - svn-locked-mark - svn-with-history-mark - svn-switched-mark) - svn-status-info)) + (if (and skip-double-external-dir-entry-name (string= skip-double-external-dir-entry-name path)) + ;; merge this entry to a previous saved one + (let ((info (gethash path externals-map))) + ;; (message "skip-double-external-dir-entry-name: %s - path: %s" skip-double-external-dir-entry-name path) + (if info + (progn + (svn-status-line-info->set-localrev info local-rev) + (svn-status-line-info->set-lastchangerev info last-change-rev) + (svn-status-line-info->set-author info author) + (svn-status-message 3 "merging entry for %s to %s" path info) + (setq skip-double-external-dir-entry-name nil)) + (message "psvn: %s not handled correct, please report this case." path))) + (setq svn-status-info + (cons (svn-status-make-line-info path + (gethash path old-ui-information) + svn-file-mark + svn-property-mark + local-rev + last-change-rev + author + svn-update-mark + svn-wc-locked-mark + svn-with-history-mark + svn-switched-mark + svn-repo-locked-mark + nil) ;;psvn-extra-info + svn-status-info))) + (when (eq svn-file-mark ?X) + (svn-puthash (match-string 1) (car svn-status-info) externals-map) + (svn-status-message 3 "found external: %s %S" (match-string 1) (car svn-status-info))) (setq revision-width (max revision-width (length (number-to-string local-rev)) (length (number-to-string last-change-rev)))) @@ -1329,6 +1732,9 @@ A and B must be line-info's." (defvar svn-status-mode-extension-map () "Subkeymap used in `svn-status-mode' for some seldom used commands.") (put 'svn-status-mode-extension-map 'risky-local-variable t) ;for Emacs 20.7 +(defvar svn-status-mode-branch-map () + "Subkeymap used in `svn-status-mode' for branching commands.") +(put 'svn-status-mode-extension-map 'risky-local-variable t) ;for Emacs 20.7 (when (not svn-status-mode-map) (setq svn-status-mode-map (make-sparse-keymap)) @@ -1338,14 +1744,17 @@ A and B must be line-info's." (define-key svn-status-mode-map (kbd "") 'svn-status-mouse-find-file-or-examine-directory) (define-key svn-status-mode-map (kbd "^") 'svn-status-examine-parent) (define-key svn-status-mode-map (kbd "s") 'svn-status-show-process-buffer) + (define-key svn-status-mode-map (kbd "h") 'svn-status-pop-to-partner-buffer) (define-key svn-status-mode-map (kbd "f") 'svn-status-find-files) (define-key svn-status-mode-map (kbd "o") 'svn-status-find-file-other-window) + (define-key svn-status-mode-map (kbd "C-o") 'svn-status-find-file-other-window-noselect) (define-key svn-status-mode-map (kbd "v") 'svn-status-view-file-other-window) (define-key svn-status-mode-map (kbd "e") 'svn-status-toggle-edit-cmd-flag) (define-key svn-status-mode-map (kbd "g") 'svn-status-update) (define-key svn-status-mode-map (kbd "M-s") 'svn-status-update) ;; PCL-CVS compatibility (define-key svn-status-mode-map (kbd "q") 'svn-status-bury-buffer) - (define-key svn-status-mode-map (kbd "h") 'svn-status-use-history) + (define-key svn-status-mode-map (kbd "x") 'svn-status-redraw-status-buffer) + (define-key svn-status-mode-map (kbd "H") 'svn-status-use-history) (define-key svn-status-mode-map (kbd "m") 'svn-status-set-user-mark) (define-key svn-status-mode-map (kbd "u") 'svn-status-unset-user-mark) ;; This matches a binding of `dired-unmark-all-files' in `dired-mode-map' @@ -1371,7 +1780,7 @@ A and B must be line-info's." (kbd "DEL")) ; GNU Emacs 'svn-status-unset-user-mark-backwards) (define-key svn-status-mode-map (kbd "$") 'svn-status-toggle-elide) - (define-key svn-status-mode-map (kbd "w") 'svn-status-copy-filename-as-kill) + (define-key svn-status-mode-map (kbd "w") 'svn-status-copy-current-line-info) (define-key svn-status-mode-map (kbd ".") 'svn-status-goto-root-or-return) (define-key svn-status-mode-map (kbd "I") 'svn-status-parse-info) (define-key svn-status-mode-map (kbd "V") 'svn-status-svnversion) @@ -1381,9 +1790,12 @@ A and B must be line-info's." (define-key svn-status-mode-map (kbd "A") 'svn-status-add-file-recursively) (define-key svn-status-mode-map (kbd "+") 'svn-status-make-directory) (define-key svn-status-mode-map (kbd "R") 'svn-status-mv) + (define-key svn-status-mode-map (kbd "C") 'svn-status-cp) (define-key svn-status-mode-map (kbd "D") 'svn-status-rm) (define-key svn-status-mode-map (kbd "c") 'svn-status-commit) (define-key svn-status-mode-map (kbd "M-c") 'svn-status-cleanup) + (define-key svn-status-mode-map (kbd "k") 'svn-status-lock) + (define-key svn-status-mode-map (kbd "K") 'svn-status-unlock) (define-key svn-status-mode-map (kbd "U") 'svn-status-update-cmd) (define-key svn-status-mode-map (kbd "M-u") 'svn-status-update-cmd) (define-key svn-status-mode-map (kbd "r") 'svn-status-revert) @@ -1397,8 +1809,6 @@ A and B must be line-info's." (define-key svn-status-mode-map (kbd "~") 'svn-status-get-specific-revision) (define-key svn-status-mode-map (kbd "E") 'svn-status-ediff-with-revision) - (define-key svn-status-mode-map (kbd "C-n") 'svn-status-next-line) - (define-key svn-status-mode-map (kbd "C-p") 'svn-status-previous-line) (define-key svn-status-mode-map (kbd "n") 'svn-status-next-line) (define-key svn-status-mode-map (kbd "p") 'svn-status-previous-line) (define-key svn-status-mode-map (kbd "") 'svn-status-next-line) @@ -1413,6 +1823,8 @@ A and B must be line-info's." (define-key svn-status-mode-mark-map (kbd "M") 'svn-status-mark-modified) (define-key svn-status-mode-mark-map (kbd "D") 'svn-status-mark-deleted) (define-key svn-status-mode-mark-map (kbd "*") 'svn-status-mark-changed) + (define-key svn-status-mode-mark-map (kbd ".") 'svn-status-mark-by-file-ext) + (define-key svn-status-mode-mark-map (kbd "%") 'svn-status-mark-filename-regexp) (define-key svn-status-mode-mark-map (kbd "u") 'svn-status-show-svn-diff-for-marked-files)) (when (not svn-status-mode-property-map) (setq svn-status-mode-property-map (make-sparse-keymap)) @@ -1429,8 +1841,11 @@ A and B must be line-info's." (define-key svn-status-mode-property-map [(control ?i)] 'svn-status-property-edit-svn-ignore) (define-key svn-status-mode-property-map (kbd "TAB") 'svn-status-property-edit-svn-ignore) (define-key svn-status-mode-property-map (kbd "k") 'svn-status-property-set-keyword-list) + (define-key svn-status-mode-property-map (kbd "Ki") 'svn-status-property-set-keyword-id) + (define-key svn-status-mode-property-map (kbd "Kd") 'svn-status-property-set-keyword-date) (define-key svn-status-mode-property-map (kbd "y") 'svn-status-property-set-eol-style) (define-key svn-status-mode-property-map (kbd "x") 'svn-status-property-set-executable) + (define-key svn-status-mode-property-map (kbd "m") 'svn-status-property-set-mime-type) ;; TODO: Why is `svn-status-select-line' in `svn-status-mode-property-map'? (define-key svn-status-mode-property-map (kbd "RET") 'svn-status-select-line) (define-key svn-status-mode-map (kbd "P") svn-status-mode-property-map)) @@ -1445,14 +1860,27 @@ A and B must be line-info's." (define-key svn-status-mode-options-map (kbd "s") 'svn-status-save-state) (define-key svn-status-mode-options-map (kbd "l") 'svn-status-load-state) (define-key svn-status-mode-options-map (kbd "x") 'svn-status-toggle-sort-status-buffer) + (define-key svn-status-mode-options-map (kbd "v") 'svn-status-toggle-svn-verbose-flag) (define-key svn-status-mode-options-map (kbd "f") 'svn-status-toggle-display-full-path) (define-key svn-status-mode-options-map (kbd "t") 'svn-status-set-trac-project-root) (define-key svn-status-mode-options-map (kbd "n") 'svn-status-set-module-name) + (define-key svn-status-mode-options-map (kbd "c") 'svn-status-set-changelog-style) + (define-key svn-status-mode-options-map (kbd "b") 'svn-status-set-branch-list) (define-key svn-status-mode-map (kbd "O") svn-status-mode-options-map)) (when (not svn-status-mode-trac-map) (setq svn-status-mode-trac-map (make-sparse-keymap)) + (define-key svn-status-mode-trac-map (kbd "w") 'svn-trac-browse-wiki) (define-key svn-status-mode-trac-map (kbd "t") 'svn-trac-browse-timeline) + (define-key svn-status-mode-trac-map (kbd "m") 'svn-trac-browse-roadmap) + (define-key svn-status-mode-trac-map (kbd "r") 'svn-trac-browse-report) + (define-key svn-status-mode-trac-map (kbd "s") 'svn-trac-browse-source) + (define-key svn-status-mode-trac-map (kbd "i") 'svn-trac-browse-ticket) + (define-key svn-status-mode-trac-map (kbd "c") 'svn-trac-browse-changeset) (define-key svn-status-mode-map (kbd "T") svn-status-mode-trac-map)) +(when (not svn-status-mode-branch-map) + (setq svn-status-mode-branch-map (make-sparse-keymap)) + (define-key svn-status-mode-branch-map (kbd "d") 'svn-branch-diff) + (define-key svn-status-mode-map (kbd "B") svn-status-mode-branch-map)) (easy-menu-define svn-status-mode-menu svn-status-mode-map "'svn-status-mode' menu" @@ -1474,6 +1902,7 @@ A and B must be line-info's." ["svn add recursively" svn-status-add-file-recursively t] ["svn mkdir..." svn-status-make-directory t] ["svn mv..." svn-status-mv t] + ["svn cp..." svn-status-cp t] ["svn rm..." svn-status-rm t] ["svn export..." svn-status-export t] ["Up Directory" svn-status-examine-parent t] @@ -1481,7 +1910,13 @@ A and B must be line-info's." ["svn revert" svn-status-revert t] ["svn resolved" svn-status-resolved t] ["svn cleanup" svn-status-cleanup t] + ["svn lock" svn-status-lock t] + ["svn unlock" svn-status-unlock t] ["Show Process Buffer" svn-status-show-process-buffer t] + ("Branch" + ["diff" svn-branch-diff t] + ["Set Branch list" svn-status-set-branch-list t] + ) ("Property" ["svn proplist" svn-status-property-list t] ["Set Multiple Properties..." svn-status-property-set t] @@ -1493,21 +1928,35 @@ A and B must be line-info's." ["Edit svn:ignore Property" svn-status-property-edit-svn-ignore t] "---" ["Edit svn:keywords List" svn-status-property-set-keyword-list t] + ["Add/Remove Id to/from svn:keywords" svn-status-property-set-keyword-id t] + ["Add/Remove Date to/from svn:keywords" svn-status-property-set-keyword-date t] + "---" ["Select svn:eol-style" svn-status-property-set-eol-style t] ["Set svn:executable" svn-status-property-set-executable t] + ["Set svn:mime-type" svn-status-property-set-mime-type t] ) ("Options" ["Save Options" svn-status-save-state t] ["Load Options" svn-status-load-state t] ["Set Trac project root" svn-status-set-trac-project-root t] ["Set Short module name" svn-status-set-module-name t] - ["Toggle sorting of *svn-status* buffer" svn-status-toggle-sort-status-buffer + ["Set Changelog style" svn-status-set-changelog-style t] + ["Set Branch list" svn-status-set-branch-list t] + ["Sort the *svn-status* buffer" svn-status-toggle-sort-status-buffer :style toggle :selected svn-status-sort-status-buffer] - ["Toggle display of full path names" svn-status-toggle-display-full-path + ["Use -v for svn status calls" svn-status-toggle-svn-verbose-flag + :style toggle :selected svn-status-verbose] + ["Display full path names" svn-status-toggle-display-full-path :style toggle :selected svn-status-display-full-path] ) ("Trac" + ["Browse wiki" svn-trac-browse-wiki t] ["Browse timeline" svn-trac-browse-timeline t] + ["Browse roadmap" svn-trac-browse-roadmap t] + ["Browse source" svn-trac-browse-source t] + ["Browse report" svn-trac-browse-report t] + ["Browse ticket" svn-trac-browse-ticket t] + ["Browse changeset" svn-trac-browse-changeset t] ["Set Trac project root" svn-status-set-trac-project-root t] ) "---" @@ -1523,27 +1972,46 @@ A and B must be line-info's." ["Mark/Unmark modified" svn-status-mark-modified t] ["Mark/Unmark deleted" svn-status-mark-deleted t] ["Mark/Unmark modified/added/deleted" svn-status-mark-changed t] + ["Mark/Unmark filename by extension" svn-status-mark-by-file-ext t] + ["Mark/Unmark filename by regexp" svn-status-mark-filename-regexp t] ) ["Hide Unknown" svn-status-toggle-hide-unknown :style toggle :selected svn-status-hide-unknown] ["Hide Unmodified" svn-status-toggle-hide-unmodified :style toggle :selected svn-status-hide-unmodified] + ["Show Client versions" svn-status-version t] + ["Prepare bug report" svn-prepare-bug-report t] )) +(defvar svn-status-file-popup-menu-list + '(["open" svn-status-find-file-other-window t] + ["svn diff" svn-status-show-svn-diff t] + ["svn commit" svn-status-commit t] + ["svn log" svn-status-show-svn-log t] + ["svn blame" svn-status-blame t] + ["mark" svn-status-set-user-mark t] + ["unmark" svn-status-unset-user-mark t] + ["svn add" svn-status-add-file t] + ["svn add recursively" svn-status-add-file-recursively t] + ["svn mv..." svn-status-mv t] + ["svn rm..." svn-status-rm t] + ["svn lock" svn-status-lock t] + ["svn unlock" svn-status-unlock t] + ["svn info" svn-status-info t] + ) "A list of menu entries for `svn-status-popup-menu'") + +;; extend svn-status-file-popup-menu-list via: +;; (add-to-list 'svn-status-file-popup-menu-list ["commit" svn-status-commit t]) (defun svn-status-popup-menu (event) + "Display a file specific popup menu" (interactive "e") (mouse-set-point event) (let* ((line-info (svn-status-get-line-information)) (name (svn-status-line-info->filename line-info))) (when line-info (easy-menu-define svn-status-actual-popup-menu nil nil - (list name - ["svn diff" svn-status-show-svn-diff t] - ["svn commit" svn-status-commit t] - ["svn log" svn-status-show-svn-log t] - ["svn info" svn-status-info t] - ["svn blame" svn-status-blame t])) + (append (list name) svn-status-file-popup-menu-list)) (svn-status-face-set-temporary-during-popup 'svn-status-marked-popup-face (svn-point-at-bol) (svn-point-at-eol) svn-status-actual-popup-menu)))) @@ -1586,6 +2054,7 @@ The following keys are defined: (setq major-mode 'svn-status-mode) (setq mode-name "svn-status") (setq mode-line-process 'svn-status-mode-line-process) + (run-hooks 'svn-status-mode-hook) (let ((view-read-only nil)) (toggle-read-only 1))) @@ -1598,10 +2067,11 @@ The following keys are defined: "Bury the buffers used by psvn.el Currently this is: `svn-status-buffer-name' - *svn-log-edit* + `svn-process-buffer-name' + `svn-log-edit-buffer-name' *svn-property-edit* *svn-log* - *svn-process* + *svn-info* When called with a prefix argument, ARG, switch back to the window configuration that was in use before `svn-status' was called." (interactive "P") @@ -1609,7 +2079,7 @@ in use before `svn-status' was called." (when svn-status-initial-window-configuration (set-window-configuration svn-status-initial-window-configuration))) (t - (let ((bl '("*svn-log-edit*" "*svn-property-edit*" "*svn-log*" "*svn-process*"))) + (let ((bl `(,svn-log-edit-buffer-name "*svn-property-edit*" "*svn-log*" "*svn-info*" ,svn-process-buffer-name))) (while bl (when (get-buffer (car bl)) (bury-buffer (car bl))) @@ -1621,9 +2091,11 @@ in use before `svn-status' was called." "Save all buffers visiting a file in TREE. If TREE is not given, try `svn-status-base-dir' as TREE." (interactive) + ;; (message "svn-status-save-some-buffers: tree1: %s" tree) (let ((ok t) (tree (or (svn-status-base-dir) tree))) + ;; (message "svn-status-save-some-buffers: tree2: %s" tree) (unless tree (error "Not in a svn project tree")) (dolist (buffer (buffer-list)) @@ -1632,6 +2104,7 @@ If TREE is not given, try `svn-status-base-dir' as TREE." (let ((file (buffer-file-name))) (when file (let ((root (svn-status-base-dir (file-name-directory file)))) + ;; (message "svn-status-save-some-buffers: file: %s, root: %s" file root) (when (and root (string= root tree) ;; buffer is modified and in the tree TREE. @@ -1655,6 +2128,14 @@ See `svn-status-marked-files' for what counts as selected." (find-file-other-window (svn-status-line-info->filename (svn-status-get-line-information)))) +(defun svn-status-find-file-other-window-noselect () + "Open the file in the other window for editing, but don't select it." + (interactive) + (svn-status-ensure-cursor-on-file) + (display-buffer + (find-file-noselect (svn-status-line-info->filename + (svn-status-get-line-information))))) + (defun svn-status-view-file-other-window () "Open the file in the other window for viewing." (interactive) @@ -1707,7 +2188,9 @@ See `svn-status-make-ui-status' for information about the ui-status." (if (and l (>= l 0)) l nil))) -(defun svn-status-line-info->author (line-info) (nth 6 line-info)) +(defun svn-status-line-info->author (line-info) + "Return the last author that changed the item that is represented in LINE-INFO." + (nth 6 line-info)) (defun svn-status-line-info->update-available (line-info) "Return whether LINE-INFO is out of date. In other words, whether there is a newer version available in the @@ -1730,16 +2213,35 @@ history, when it will be \"+\"." This is column five of the output from `svn status'. The result will be nil or \"S\"." (nth 10 line-info)) +(defun svn-status-line-info->repo-locked (line-info) + "Return whether LINE-INFO contains some locking information. +This is column six of the output from `svn status'. +The result will be \"K\", \"O\", \"T\", \"B\" or nil." + (nth 11 line-info)) +(defun svn-status-line-info->psvn-extra-info (line-info) + "Return a list of extra information for psvn associated with LINE-INFO. +This list holds currently only one element: +* The action after a commit or update." + (nth 12 line-info)) (defun svn-status-line-info->is-visiblep (line-info) - (not (or (svn-status-line-info->hide-because-unknown line-info) - (svn-status-line-info->hide-because-unmodified line-info) - (svn-status-line-info->hide-because-user-elide line-info)))) + "Return whether the line is visible or not" + (or (not (or (svn-status-line-info->hide-because-unknown line-info) + (svn-status-line-info->hide-because-unmodified line-info) + (svn-status-line-info->hide-because-custom-hide-function line-info) + (svn-status-line-info->hide-because-user-elide line-info))) + (svn-status-line-info->update-available line-info) ;; show the line, if an update is available + (svn-status-line-info->psvn-extra-info line-info) ;; show the line, if there is some extra info displayed on this line + )) (defun svn-status-line-info->hide-because-unknown (line-info) (and svn-status-hide-unknown (eq (svn-status-line-info->filemark line-info) ??))) +(defun svn-status-line-info->hide-because-custom-hide-function (line-info) + (and svn-status-custom-hide-function + (apply svn-status-custom-hide-function (list line-info)))) + (defun svn-status-line-info->hide-because-unmodified (line-info) ;;(message " %S %S %S %S - %s" svn-status-hide-unmodified (svn-status-line-info->propmark line-info) ?_ ;; (svn-status-line-info->filemark line-info) (svn-status-line-info->filename line-info)) @@ -1766,9 +2268,30 @@ The result will be nil or \"S\"." (defun svn-status-line-info->set-localrev (line-info value) (setcar (nthcdr 4 line-info) value)) +(defun svn-status-line-info->set-author (line-info value) + (setcar (nthcdr 6 line-info) value)) + (defun svn-status-line-info->set-lastchangerev (line-info value) (setcar (nthcdr 5 line-info) value)) +(defun svn-status-line-info->set-repo-locked (line-info value) + (setcar (nthcdr 11 line-info) value)) + +(defun svn-status-line-info->set-psvn-extra-info (line-info value) + (setcar (nthcdr 12 line-info) value)) + +(defun svn-status-copy-current-line-info (arg) + "Copy the current file name at point, using `svn-status-copy-filename-as-kill'. +If no file is at point, copy everything starting from ':' to the end of line." + (interactive "P") + (if (svn-status-get-line-information) + (svn-status-copy-filename-as-kill arg) + (save-excursion + (goto-char (svn-point-at-bol)) + (when (looking-at ".+?: *\\(.+\\)$") + (kill-new (svn-match-string-no-properties 1)) + (message "Copied: %s" (svn-match-string-no-properties 1)))))) + (defun svn-status-copy-filename-as-kill (arg) "Copy the actual file name to the kill-ring. When called with the prefix argument 0, use the full path name." @@ -1779,39 +2302,63 @@ When called with the prefix argument 0, use the full path name." (kill-new str) (message "Copied %s" str))) -(defun svn-status-toggle-elide () +(defun svn-status-get-child-directories (&optional dir) + "Return a list of subdirectories for DIR" (interactive) - (let ((st-info svn-status-info) - (fname) - (test (svn-status-line-info->filename (svn-status-get-line-information))) - (len-test) - (len-fname) - (new-elide-mark t) - (elide-mark)) - (if (member test svn-status-elided-list) - (setq svn-status-elided-list (delete test svn-status-elided-list)) - (add-to-list 'svn-status-elided-list test)) - (when (string= test ".") - (setq test "")) - (setq len-test (length test)) - (while st-info - (setq fname (svn-status-line-info->filename (car st-info))) - (setq len-fname (length fname)) - (when (and (>= len-fname len-test) - (string= (substring fname 0 len-test) test)) - (setq elide-mark new-elide-mark) - (when (or (string= fname ".") - (and (= len-fname len-test) (svn-status-line-info->directory-p (car st-info)))) - (message "Elided directory %s and all its files." fname) - (setq new-elide-mark (not (svn-status-line-info->user-elide (car st-info)))) - (setq elide-mark (if new-elide-mark 'directory nil))) - ;;(message "elide-mark: %S member: %S" elide-mark (member fname svn-status-elided-list)) - (when (and (member fname svn-status-elided-list) (not elide-mark)) - (setq svn-status-elided-list (delete fname svn-status-elided-list))) - (setcar (nthcdr 1 (svn-status-line-info->ui-status (car st-info))) elide-mark)) - (setq st-info (cdr st-info)))) - ;;(message "svn-status-elided-list: %S" svn-status-elided-list) - (svn-status-update-buffer)) + (let ((this-dir (concat (expand-file-name (or dir (svn-status-line-info->filename (svn-status-get-line-information)))) "/")) + (test-dir) + (sub-dir-list)) + ;;(message "this-dir %S" this-dir) + (dolist (line-info svn-status-info) + (when (svn-status-line-info->directory-p line-info) + (setq test-dir (svn-status-line-info->full-path line-info)) + (when (string= (file-name-directory test-dir) this-dir) + (add-to-list 'sub-dir-list (file-relative-name (svn-status-line-info->full-path line-info)) t)))) + sub-dir-list)) + +(defun svn-status-toggle-elide (arg) + "Toggle eliding of the current file or directory. +When called with a prefix argument, toggle the hiding of all subdirectories for the current directory." + (interactive "P") + (if arg + (let ((cur-line (svn-status-line-info->filename (svn-status-get-line-information)))) + (when (svn-status-line-info->user-elide (svn-status-get-line-information)) + (svn-status-toggle-elide nil)) + (dolist (dir-name (svn-status-get-child-directories)) + (svn-status-goto-file-name dir-name) + (svn-status-toggle-elide nil)) + (svn-status-goto-file-name cur-line)) + (let ((st-info svn-status-info) + (fname) + (test (svn-status-line-info->filename (svn-status-get-line-information))) + (len-test) + (len-fname) + (new-elide-mark t) + (elide-mark)) + (if (member test svn-status-elided-list) + (setq svn-status-elided-list (delete test svn-status-elided-list)) + (add-to-list 'svn-status-elided-list test)) + (when (string= test ".") + (setq test "")) + (setq len-test (length test)) + (while st-info + (setq fname (svn-status-line-info->filename (car st-info))) + (setq len-fname (length fname)) + (when (and (>= len-fname len-test) + (string= (substring fname 0 len-test) test)) + (setq elide-mark new-elide-mark) + (when (or (string= fname ".") + (and (= len-fname len-test) (svn-status-line-info->directory-p (car st-info)))) + (message "Elided directory %s and all its files." fname) + (setq new-elide-mark (not (svn-status-line-info->user-elide (car st-info)))) + (setq elide-mark (if new-elide-mark 'directory nil))) + ;;(message "elide-mark: %S member: %S" elide-mark (member fname svn-status-elided-list)) + (when (and (member fname svn-status-elided-list) (not elide-mark)) + (setq svn-status-elided-list (delete fname svn-status-elided-list))) + (setcar (nthcdr 1 (svn-status-line-info->ui-status (car st-info))) elide-mark)) + (setq st-info (cdr st-info)))) + ;;(message "svn-status-elided-list: %S" svn-status-elided-list) + (svn-status-update-buffer))) (defun svn-status-apply-elide-list () "Elide files/directories according to `svn-status-elided-list'." @@ -1891,8 +2438,13 @@ When called with the prefix argument 0, use the full path name." svn-status-commit-rev-number) (svn-status-line-info->set-localrev line-info svn-status-commit-rev-number) (svn-status-line-info->set-lastchangerev line-info svn-status-commit-rev-number)) + (when svn-status-last-commit-author + (svn-status-line-info->set-author line-info svn-status-last-commit-author)) + (svn-status-line-info->set-psvn-extra-info line-info (list action)) (cond ((equal action 'committed) - (setq tag-string " ")) + (setq tag-string " ") + (when (member (svn-status-line-info->repo-locked line-info) '(?K)) + (svn-status-line-info->set-repo-locked line-info nil))) ((equal action 'added) (setq tag-string " ")) ((equal action 'deleted) @@ -1901,6 +2453,13 @@ When called with the prefix argument 0, use the full path name." (setq tag-string " ")) ((equal action 'updated) (setq tag-string " ")) + ((equal action 'updated-props) + (setq tag-string " ")) + ((equal action 'conflicted) + (setq tag-string " ") + (svn-status-line-info->set-filemark line-info ?C)) + ((equal action 'merged) + (setq tag-string " ")) ((equal action 'propset) ;;(setq tag-string " ") (svn-status-line-info->set-propmark line-info svn-status-file-modified-after-save-flag)) @@ -1911,7 +2470,7 @@ When called with the prefix argument 0, use the full path name." (svn-status-line-info->set-filemark line-info ?D)) (t (error "Unknown action '%s for %s" action (svn-status-line-info->filename line-info)))) - (when tag-string + (when (and tag-string (not (member action '(conflicted merged)))) (svn-status-line-info->set-filemark line-info ? ) (svn-status-line-info->set-propmark line-info ? )) (let ((buffer-read-only nil)) @@ -1931,9 +2490,9 @@ When called with the prefix argument 0, use the full path name." "Parse the output of svn commit. Return a list that is suitable for `svn-status-update-with-command-list'" (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (let ((action) - (name) + (file-name) (skip) (result)) (goto-char (point-min)) @@ -1964,8 +2523,10 @@ Return a list that is suitable for `svn-status-update-with-command-list'" ;; when the commit used . as argument, delete the trailing directory ;; from the svn output (search-forward "/" nil t)) - (setq name (buffer-substring-no-properties (point) (svn-point-at-eol))) - (setq result (cons (list name action) + (setq file-name (buffer-substring-no-properties (point) (svn-point-at-eol))) + (unless svn-status-last-commit-author + (setq svn-status-last-commit-author (car (svn-status-info-for-path (expand-file-name (concat default-directory file-name)))))) + (setq result (cons (list file-name action) result)) (setq skip nil)) (forward-line 1)) @@ -1977,7 +2538,7 @@ Return a list that is suitable for `svn-status-update-with-command-list'" "Parse the output of svn add|remove. Return a list that is suitable for `svn-status-update-with-command-list'" (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (let ((action) (name) (skip) @@ -2007,7 +2568,8 @@ Return a list that is suitable for `svn-status-update-with-command-list'" "Parse the output of svn update. Return a list that is suitable for `svn-status-update-with-command-list'" (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) + (setq svn-status-update-rev-number nil) (let ((action) (name) (skip) @@ -2016,9 +2578,13 @@ Return a list that is suitable for `svn-status-update-with-command-list'" (while (< (point) (point-max)) (cond ((= (svn-point-at-eol) (svn-point-at-bol)) ;skip blank lines (setq skip t)) - ((looking-at "Updated to") + ((looking-at "Updated to revision \\([0-9]+\\)") + (setq svn-status-update-rev-number + (list t (string-to-number (svn-match-string-no-properties 1)))) (setq skip t)) - ((looking-at "At revision") + ((looking-at "At revision \\([0-9]+\\)") + (setq svn-status-update-rev-number + (list nil (string-to-number (svn-match-string-no-properties 1)))) (setq skip t)) ((looking-at "U") (setq action 'updated)) @@ -2027,8 +2593,17 @@ Return a list that is suitable for `svn-status-update-with-command-list'" ((looking-at "D") (setq skip t)) ;;(setq action 'deleted)) ;;deleted files are not displayed in the svn status output. + ((looking-at "C") + (setq action 'conflicted)) + ((looking-at "G") + (setq action 'merged)) + + ((looking-at " U") + (setq action 'updated-props)) + (t ;; this should never be needed(?) - (setq action 'unknown))) + (setq action (concat "parse-update: '" + (buffer-substring-no-properties (point) (+ 2 (point))) "'")))) (unless skip ;found an interesting line (forward-char 3) (setq name (buffer-substring-no-properties (point) (svn-point-at-eol))) @@ -2044,7 +2619,7 @@ Return a list that is suitable for `svn-status-update-with-command-list'" "Parse the output of svn propset. Return a list that is suitable for `svn-status-update-with-command-list'" (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (let ((result)) (dolist (line (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")) (message "%s" line) @@ -2113,27 +2688,28 @@ Symbolic links to directories count as directories (see `file-directory-p')." (make-string (* 2 (svn-status-count-/ (svn-status-line-info->filename line-info))) 32)) - (svn-status-choose-face-to-add - (svn-status-line-info->directory-p line-info) - (svn-status-line-info->filename-nondirectory line-info) - 'svn-status-directory-face - 'svn-status-filename-face) - ;; if it's a symlkink, add '-> target' + ;;symlinks get a different face (let ((target (svn-status-line-info->symlink-p line-info))) - (when target - (concat " -> " - ;; add face to target: could maybe - ;; use different faces for - ;; unversioned targets? - (svn-status-choose-face-to-add - (file-directory-p target) - (file-relative-name - target - (svn-status-line-info->directory-containing-line-info - line-info t)); name relative to dir of line-info, not '.' - 'svn-status-directory-face - 'svn-status-filename-face) - ))) + (if target + ;; name -> trget + ;; name gets symlink-face, target gets file/directory face + (concat + (svn-add-face (svn-status-line-info->filename-nondirectory line-info) + 'svn-status-symlink-face) + " -> " + (svn-status-choose-face-to-add + ;; TODO: could use different faces for + ;; unversioned targets and broken symlinks? + (svn-status-line-info->directory-p line-info) + target + 'svn-status-directory-face + 'svn-status-filename-face)) + ;; else target is not a link + (svn-status-choose-face-to-add + (svn-status-line-info->directory-p line-info) + (svn-status-line-info->filename-nondirectory line-info) + 'svn-status-directory-face + 'svn-status-filename-face))) )) (elide-hint (if (svn-status-line-info->show-user-elide-continuation line-info) " ..." ""))) (svn-puthash (svn-status-line-info->filename line-info) @@ -2149,23 +2725,45 @@ Symbolic links to directories count as directories (see `file-directory-p')." (or (svn-status-line-info->localrev line-info) "") (or (svn-status-line-info->lastchangerev line-info) "") (svn-status-line-info->author line-info)) - (if svn-status-short-mod-flag-p update-available filename) - (if svn-status-short-mod-flag-p filename update-available) + (when svn-status-short-mod-flag-p update-available) + filename + (unless svn-status-short-mod-flag-p update-available) (svn-status-maybe-add-string (svn-status-line-info->locked line-info) " [ LOCKED ]" 'svn-status-locked-face) + (svn-status-maybe-add-string (svn-status-line-info->repo-locked line-info) + (let ((flag (svn-status-line-info->repo-locked line-info))) + (cond ((eq flag ?K) " [ REPO-LOCK-HERE ]") + ((eq flag ?O) " [ REPO-LOCK-OTHER ]") + ((eq flag ?T) " [ REPO-LOCK-STOLEN ]") + ((eq flag ?B) " [ REPO-LOCK-BROKEN ]") + (t " [ REPO-LOCK-UNKNOWN ]"))) + 'svn-status-locked-face) (svn-status-maybe-add-string (svn-status-line-info->switched line-info) " (switched)" 'svn-status-switched-face) elide-hint) 'svn-status-marked-face) "\n"))) +(defun svn-status-redraw-status-buffer () + "Redraw the `svn-status-buffer-name' buffer. +Additionally clear the psvn-extra-info field in all line-info lists." + (interactive) + (dolist (line-info svn-status-info) + (svn-status-line-info->set-psvn-extra-info line-info nil)) + (svn-status-update-buffer)) + (defun svn-status-update-buffer () - "Update the `svn-status-buffer-name' buffer, using `svn-status-info'." + "Update the `svn-status-buffer-name' buffer, using `svn-status-info'. + This function does not access the repository." (interactive) ;(message "buffer-name: %s" (buffer-name)) (unless (string= (buffer-name) svn-status-buffer-name) (set-buffer svn-status-buffer-name)) (svn-status-mode) + (when svn-status-refresh-info + (when (eq svn-status-refresh-info 'once) + (setq svn-status-refresh-info nil)) + (svn-status-parse-info t)) (let ((st-info svn-status-info) (buffer-read-only nil) (start-pos) @@ -2178,6 +2776,7 @@ Symbolic links to directories count as directories (see `file-directory-p')." (first-line t) (fname (svn-status-line-info->filename (svn-status-get-line-information))) (fname-pos (point)) + (window-line-pos (svn-status-window-line-position (get-buffer-window (current-buffer)))) (header-line-string) (column (current-column))) (delete-region (point-min) (point-max)) @@ -2216,8 +2815,11 @@ Symbolic links to directories count as directories (see `file-directory-p')." ""))) (when svn-status-module-name (insert (format "Project name: %s\n" svn-status-module-name))) + (when svn-status-branch-list + (insert (format "Branches: %s\n" svn-status-branch-list))) (when svn-status-base-info - (insert (concat "Repository: " (svn-status-base-info->url) "\n"))) + (insert (concat "Repository Root: " (svn-status-base-info->repository-root) "\n")) + (insert (concat "Repository Url: " (svn-status-base-info->url) "\n"))) (when svn-status-hide-unknown (insert (format "%d Unknown file(s) are hidden - press `?' to toggle hiding\n" @@ -2246,7 +2848,9 @@ Symbolic links to directories count as directories (see `file-directory-p')." (progn (goto-char fname-pos) (svn-status-goto-file-name fname) - (goto-char (+ column (svn-point-at-bol)))) + (goto-char (+ column (svn-point-at-bol))) + (when window-line-pos + (recenter window-line-pos))) (goto-char (+ (next-overlay-change (point-min)) svn-status-default-column))))) (defun svn-status-parse-info (arg) @@ -2260,8 +2864,11 @@ non-interactive use." (interactive "P") (if (eq arg 0) (setq svn-status-base-info nil) - (svn-run nil t 'parse-info "info" ".") - (svn-status-parse-info-result)) + (let ((svn-process-buffer-name "*svn-info-output*")) + (when (get-buffer svn-process-buffer-name) + (kill-buffer svn-process-buffer-name)) + (svn-run nil t 'parse-info "info" ".") + (svn-status-parse-info-result))) (unless (eq arg t) (svn-status-update-buffer))) @@ -2269,16 +2876,19 @@ non-interactive use." "Parse the result from the svn info command. Put the found values in `svn-status-base-info'." (let ((url) - (repository-root)) + (repository-root) + (last-changed-author)) (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (goto-char (point-min)) (let ((case-fold-search t)) (search-forward "url: ") (setq url (buffer-substring-no-properties (point) (svn-point-at-eol))) - (search-forward "repository root: ") - (setq repository-root (buffer-substring-no-properties (point) (svn-point-at-eol))))) - (setq svn-status-base-info `((url ,url) (repository-root ,repository-root))))) + (when (search-forward "repository root: " nil t) + (setq repository-root (buffer-substring-no-properties (point) (svn-point-at-eol)))) + (when (search-forward "last changed author: " nil t) + (setq last-changed-author (buffer-substring-no-properties (point) (svn-point-at-eol)))))) + (setq svn-status-base-info `((url ,url) (repository-root ,repository-root) (last-changed-author ,last-changed-author))))) (defun svn-status-base-info->url () "Extract the url part from `svn-status-base-info'." @@ -2292,10 +2902,32 @@ Put the found values in `svn-status-base-info'." (cadr (assoc 'repository-root svn-status-base-info)) "")) -(defun svn-status-ls (path) +(defun svn-status-checkout-prefix-path () + "When only a part of the svn repository is checked out, return the file path for this checkout." + (interactive) + (svn-status-parse-info t) + (let ((root (svn-status-base-info->repository-root)) + (url (svn-status-base-info->url)) + (p) + (base-dir (svn-status-base-dir)) + (wc-checkout-prefix)) + (setq p (substring url (length root))) + (setq wc-checkout-prefix (file-relative-name default-directory base-dir)) + (when (string= wc-checkout-prefix "./") + (setq wc-checkout-prefix "")) + ;; (message "svn-status-checkout-prefix-path: wc-checkout-prefix: '%s' p: '%s' base-dir: %s" wc-checkout-prefix p base-dir) + (setq p (substring p 0 (- (length p) (length wc-checkout-prefix)))) + (when (interactive-p) + (message "svn-status-checkout-prefix-path: '%s'" p)) + p)) + +(defun svn-status-ls (path &optional synchron) "Run svn ls PATH." (interactive "sPath for svn ls: ") - (svn-run t t 'ls "ls" path)) + (svn-run (not synchron) t 'ls "ls" path) + (when synchron + (split-string (with-current-buffer svn-process-buffer-name + (buffer-substring-no-properties (point-min) (point-max)))))) (defun svn-status-ls-branches () "Show, which branches exist for the actual working copy. @@ -2304,6 +2936,13 @@ Note: this command assumes the proposed standard svn repository layout." (svn-status-parse-info t) (svn-status-ls (concat (svn-status-base-info->repository-root) "/branches"))) +(defun svn-status-ls-tags () + "Show, which tags exist for the actual working copy. +Note: this command assumes the proposed standard svn repository layout." + (interactive) + (svn-status-parse-info t) + (svn-status-ls (concat (svn-status-base-info->repository-root) "/tags"))) + (defun svn-status-toggle-edit-cmd-flag (&optional reset) "Allow the user to edit the parameters for the next svn command. This command toggles between @@ -2337,14 +2976,24 @@ The string in parentheses is shown in the status line to show the state." (svn-status-goto-file-name "."))) (defun svn-status-next-line (nr-of-lines) + "Go to the next line that holds a file information. +When called with a prefix argument advance the given number of lines." (interactive "p") - (next-line nr-of-lines) + (while (progn + (next-line nr-of-lines) + (and (not (eobp)) + (not (svn-status-get-line-information))))) (when (svn-status-get-line-information) (goto-char (+ (svn-point-at-bol) svn-status-default-column)))) (defun svn-status-previous-line (nr-of-lines) + "Go to the previous line that holds a file information. +When called with a prefix argument go back the given number of lines." (interactive "p") - (previous-line nr-of-lines) + (while (progn + (previous-line nr-of-lines) + (and (not (bobp)) + (not (svn-status-get-line-information))))) (when (svn-status-get-line-information) (goto-char (+ (svn-point-at-bol) svn-status-default-column)))) @@ -2356,7 +3005,7 @@ The string in parentheses is shown in the status line to show the state." (let ((default-directory (file-name-as-directory (expand-file-name (svn-status-line-info->directory-containing-line-info line-info t))))) - (dired-jump)) + (if (fboundp 'dired-jump-back) (dired-jump-back) (dired-jump))) ;; Xemacs uses dired-jump-back (dired-goto-file file-full-path))) (defun svn-status-possibly-negate-meaning-of-arg (arg &optional command) @@ -2373,7 +3022,7 @@ When called with a prefix argument run 'svn status -vu'." (interactive "P") (unless (interactive-p) (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (setq svn-status-update-previous-process-output (buffer-substring (point-min) (point-max))))) (svn-status default-directory arg)) @@ -2388,7 +3037,11 @@ The result may be parsed with the various `svn-status-line-info->...' functions. (overlay-get overlay 'svn-info)))) svn-info) ;; different mode, means called not from the *svn-status* buffer - '((nil nil) 32 nil "." 0 0 "" nil nil nil nil))) + (if svn-status-get-line-information-for-file + (svn-status-make-line-info (if (eq svn-status-get-line-information-for-file 'relative) + (file-relative-name (buffer-file-name) (svn-status-base-dir)) + (buffer-file-name))) + (svn-status-make-line-info ".")))) (defun svn-status-get-file-list (use-marked-files) @@ -2405,13 +3058,13 @@ See `svn-status-marked-files' for what counts as selected." (defun svn-status-get-file-information () "Find out about the file under point. The result may be parsed with the various `svn-status-line-info->...' functions. -When called from a *svn-status* buffer, do the same as `svn-status-get-file-information'. +When called from a *svn-status* buffer, do the same as `svn-status-get-line-information'. When called from a file buffer provide a structure that contains the filename." (cond ((eq major-mode 'svn-status-mode) - (svn-status-get-file-information)) + (svn-status-get-line-information)) (t ;; a fake strukture that contains the buffername for the current buffer - (list '(nil nil) 32 nil (buffer-file-name (current-buffer)) 0 0 "" nil nil nil nil)))) + (svn-status-make-line-info (buffer-file-name (current-buffer)))))) (defun svn-status-select-line () "Return information about the file under point. @@ -2419,8 +3072,9 @@ When called from a file buffer provide a structure that contains the filename." (interactive) (let ((info (svn-status-get-line-information))) (if info - (message "%S %S %S" info (svn-status-line-info->hide-because-unknown info) - (svn-status-line-info->hide-because-unmodified info)) + (message "%S hide-because-unknown: %S hide-because-unmodified: %S" info + (svn-status-line-info->hide-because-unknown info) + (svn-status-line-info->hide-because-unmodified info)) (message "No file on this line")))) (defun svn-status-ensure-cursor-on-file () @@ -2437,7 +3091,7 @@ otherwise return the directory containing the file under point." ;;point was outside the file list, but we need ;;s-s-l-i->f to return a string to add to `default-directory'. (let ((line-info (or (svn-status-get-line-information) - '(nil nil nil "")))) + (svn-status-make-line-info)))) (file-name-as-directory (expand-file-name (svn-status-line-info->directory-containing-line-info line-info allow-self))))) @@ -2506,8 +3160,10 @@ Then move to that line." (mark-count 0) (line-info (svn-status-get-line-information)) (file-name (svn-status-line-info->filename line-info)) - (sub-file-regexp (concat "^" (regexp-quote - (file-name-as-directory file-name)))) + (sub-file-regexp (if (file-directory-p file-name) + (concat "^" (regexp-quote + (file-name-as-directory file-name))) + nil)) (newcursorpos-fname) (i-fname) (first-line t) @@ -2518,7 +3174,8 @@ Then move to that line." (setq first-line nil)) (setq i-fname (svn-status-line-info->filename (car st-info))) (when (or (string= file-name i-fname) - (string-match sub-file-regexp i-fname)) + (when sub-file-regexp + (string-match sub-file-regexp i-fname))) (when (svn-status-line-info->is-visiblep (car st-info)) (when (or (not only-this-line) (string= file-name i-fname)) (setq newcursorpos-fname i-fname) @@ -2610,6 +3267,33 @@ If called with a prefix ARG, unmark all such files." (interactive) (svn-status-apply-usermark-checked '(lambda (info) t) nil)) +(defvar svn-status-regexp-history nil + "History list of regular expressions used in svn status commands.") + +(defun svn-status-read-regexp (prompt) + (read-from-minibuffer prompt nil nil nil 'svn-status-regexp-history)) + +(defun svn-status-mark-filename-regexp (regexp &optional unmark) + "Mark all files matching REGEXP. +If the function is called with a prefix arg, unmark all these files." + (interactive + (list (svn-status-read-regexp (concat (if current-prefix-arg "Unmark" "Mark") + " files (regexp): ")) + (if current-prefix-arg t nil))) + (svn-status-apply-usermark-checked + '(lambda (info) (string-match regexp (svn-status-line-info->filename-nondirectory info))) (not unmark))) + +(defun svn-status-mark-by-file-ext (ext &optional unmark) + "Mark all files matching the given file extension EXT. +If the function is called with a prefix arg, unmark all these files." + (interactive + (list (read-string (concat (if current-prefix-arg "Unmark" "Mark") + " files with extensions: ")) + (if current-prefix-arg t nil))) + (svn-status-apply-usermark-checked + '(lambda (info) (let ((case-fold-search nil)) + (string-match (concat "\\." ext "$") (svn-status-line-info->filename-nondirectory info)))) (not unmark))) + (defun svn-status-toggle-hide-unknown () (interactive) (setq svn-status-hide-unknown (not svn-status-hide-unknown)) @@ -2666,16 +3350,23 @@ If the file is not found, return nil." (defun svn-status-marked-files () "Return all files marked by `svn-status-set-user-mark', or (if no files were marked) the file under point." - (let* ((st-info svn-status-info) - (file-list)) - (while st-info - (when (svn-status-line-info->has-usermark (car st-info)) - (setq file-list (append file-list (list (car st-info))))) - (setq st-info (cdr st-info))) - (or file-list - (if (svn-status-get-line-information) - (list (svn-status-get-line-information)) - nil)))) + (if (eq major-mode 'svn-status-mode) + (let* ((st-info svn-status-info) + (file-list)) + (while st-info + (when (svn-status-line-info->has-usermark (car st-info)) + (setq file-list (append file-list (list (car st-info))))) + (setq st-info (cdr st-info))) + (or file-list + (if (svn-status-get-line-information) + (list (svn-status-get-line-information)) + nil))) + ;; different mode, means called not from the *svn-status* buffer + (if svn-status-get-line-information-for-file + (list (svn-status-make-line-info (if (eq svn-status-get-line-information-for-file 'relative) + (file-relative-name (buffer-file-name) (svn-status-base-dir)) + (buffer-file-name)))) + (list (svn-status-make-line-info "."))))) (defun svn-status-marked-file-names () (mapcar 'svn-status-line-info->filename (svn-status-marked-files))) @@ -2686,8 +3377,16 @@ Unlike `svn-status-marked-files', this does not select the file under point if no files have been marked." ;; `some' would be shorter but requires cl-seq at runtime. ;; (Because it accepts both lists and vectors, it is difficult to inline.) - (loop for file in svn-status-info - thereis (svn-status-line-info->has-usermark file))) + (loop for line-info in svn-status-info + thereis (svn-status-line-info->has-usermark line-info))) + +(defun svn-status-only-dirs-or-nothing-marked-p () + "Return non-nil iff only dirs has been marked by `svn-status-set-user-mark'." + ;; `some' would be shorter but requires cl-seq at runtime. + ;; (Because it accepts both lists and vectors, it is difficult to inline.) + (loop for line-info in svn-status-info + thereis (and (not (svn-status-line-info->directory-p line-info)) + (svn-status-line-info->has-usermark line-info)))) (defun svn-status-ui-information-hash-table () (let ((st-info svn-status-info) @@ -2712,22 +3411,23 @@ if no files have been marked." (insert postfix)))) (defun svn-status-show-process-buffer-internal (&optional scroll-to-top) - (when (string= (buffer-name) svn-status-buffer-name) - (delete-other-windows)) - (pop-to-buffer "*svn-process*") - (svn-process-mode) - (when svn-status-wash-control-M-in-process-buffers - (svn-status-remove-control-M)) - (when scroll-to-top - (goto-char (point-min))) - (other-window 1)) + (let ((cur-buff (current-buffer))) + (unless svn-status-preserve-window-configuration + (when (string= (buffer-name) svn-status-buffer-name) + (delete-other-windows))) + (pop-to-buffer svn-process-buffer-name) + (svn-process-mode) + (when scroll-to-top + (goto-char (point-min))) + (pop-to-buffer cur-buff))) (defun svn-status-show-process-output (cmd &optional scroll-to-top) "Display the result of a svn command. Consider svn-status-window-alist to choose the buffer name." - (let ((window-mode (cadr (assoc cmd svn-status-window-alist)))) + (let ((window-mode (cadr (assoc cmd svn-status-window-alist))) + (process-default-directory)) (cond ((eq window-mode nil) ;; use *svn-process* buffer - (setq svn-status-last-output-buffer-name "*svn-process*")) + (setq svn-status-last-output-buffer-name svn-process-buffer-name)) ((eq window-mode t) ;; use *svn-info* buffer (setq svn-status-last-output-buffer-name "*svn-info*")) ((eq window-mode 'invisible) ;; don't display the buffer @@ -2740,11 +3440,13 @@ Consider svn-status-window-alist to choose the buffer name." (unless svn-status-preserve-window-configuration (when (string= (buffer-name) svn-status-buffer-name) (delete-other-windows))) - (pop-to-buffer "*svn-process*") + (pop-to-buffer svn-process-buffer-name) + (setq process-default-directory default-directory) (switch-to-buffer (get-buffer-create svn-status-last-output-buffer-name)) + (setq default-directory process-default-directory) (let ((buffer-read-only nil)) (delete-region (point-min) (point-max)) - (insert-buffer-substring "*svn-process*") + (insert-buffer-substring svn-process-buffer-name) (when scroll-to-top (goto-char (point-min)))) (when (eq window-mode t) ;; *svn-info* buffer @@ -2752,26 +3454,54 @@ Consider svn-status-window-alist to choose the buffer name." (other-window 1)) (svn-status-show-process-buffer-internal scroll-to-top))))) +(defun svn-status-svn-log-switches (arg) + (cond ((eq arg 0) '()) + ((or (eq arg -1) (eq arg '-)) '("-q")) + (arg '("-v")) + (t svn-status-default-log-arguments))) + (defun svn-status-show-svn-log (arg) "Run `svn log' on selected files. The output is put into the *svn-log* buffer The optional prefix argument ARG determines which switches are passed to `svn log': no prefix --- use whatever is in the list `svn-status-default-log-arguments' - prefix argument of -1 --- use no arguments - prefix argument of 0: --- use the -q switch (quiet) + prefix argument of -1: --- use the -q switch (quiet) + prefix argument of 0 --- use no arguments other prefix arguments: --- use the -v switch (verbose) See `svn-status-marked-files' for what counts as selected." (interactive "P") - (let ((switches (cond ((eq arg 0) '("-q")) - ((eq arg -1) '()) - (arg '("-v")) - (t svn-status-default-log-arguments)))) + (let ((switches (svn-status-svn-log-switches arg)) + (svn-status-get-line-information-for-file t)) + ;; (message "svn-status-show-svn-log %S" arg) (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "") - (svn-run t t 'log "log" "--targets" svn-status-temp-arg-file switches) - (save-excursion - (set-buffer "*svn-process*") - (svn-log-view-mode)))) + (svn-run t t 'log "log" "--targets" svn-status-temp-arg-file switches))) + +(defun svn-status-version () + "Show the version numbers for psvn.el and the svn command line client. +The version number of the client is cached in `svn-client-version'." + (interactive) + (let ((window-conf (current-window-configuration)) + (version-string)) + (if (or (interactive-p) (not svn-status-cached-version-string)) + (progn + (svn-run nil t 'version "--version") + (when (interactive-p) + (svn-status-show-process-output 'info t)) + (with-current-buffer svn-status-last-output-buffer-name + (goto-char (point-min)) + (setq svn-client-version + (when (re-search-forward "svn, version \\([0-9\.]+\\) " nil t) + (mapcar 'string-to-number (split-string (match-string 1) "\\.")))) + (let ((buffer-read-only nil)) + (goto-char (point-min)) + (insert (format "psvn.el revision: %s\n\n" svn-psvn-revision))) + (setq version-string (buffer-substring-no-properties (point-min) (point-max)))) + (setq svn-status-cached-version-string version-string)) + (setq version-string svn-status-cached-version-string) + (unless (interactive-p) + (set-window-configuration window-conf) + version-string)))) (defun svn-status-info () "Run `svn info' on all selected files. @@ -2780,6 +3510,20 @@ See `svn-status-marked-files' for what counts as selected." (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "") (svn-run t t 'info "info" "--targets" svn-status-temp-arg-file)) +(defun svn-status-info-for-path (path) + "Run svn info on the given PATH. +Return some interesting parts of the resulting output. +At the moment a list containing the last changed author is returned." + (let ((svn-process-buffer-name "*svn-info-output*") + (last-changed-author)) + (svn-run nil t 'info "info" path) + (with-current-buffer svn-process-buffer-name + (goto-char (point-min)) + (when (search-forward "last changed author: " nil t) + (setq last-changed-author (buffer-substring-no-properties (point) (svn-point-at-eol))))) + (svn-status-message 7 "last-changed-author for '%s': %s" path last-changed-author) + (list last-changed-author))) + (defun svn-status-blame (revision) "Run `svn blame' on the current file. When called with a prefix argument, ask the user for the REVISION to use. @@ -2788,7 +3532,8 @@ When called from a file buffer, go to the current line in the resulting blame ou (when current-prefix-arg (setq revision (svn-status-read-revision-string "Blame for version: " "BASE"))) (unless revision (setq revision "BASE")) - (svn-run t t 'blame "blame" "-r" revision (svn-status-line-info->filename (svn-status-get-file-information)))) + (setq svn-status-blame-file-name (svn-status-line-info->filename (svn-status-get-file-information))) + (svn-run t t 'blame "blame" svn-status-default-blame-arguments "-r" revision svn-status-blame-file-name)) (defun svn-status-show-svn-diff (arg) "Run `svn diff' on the current file. @@ -2801,6 +3546,15 @@ If ARG then prompt for revision to diff against." (svn-status-show-svn-diff-internal (list (svn-status-get-line-information)) t (if arg :ask :auto))) +(defun svn-file-show-svn-diff (arg) + "Run `svn diff' on the current file. +If there is a newer revision in the repository, the diff is done against HEAD, +otherwise compare the working copy with BASE. +If ARG then prompt for revision to diff against." + (interactive "P") + (svn-status-show-svn-diff-internal (list (svn-status-make-line-info buffer-file-name)) nil + (if arg :ask :auto))) + (defun svn-status-show-svn-diff-for-marked-files (arg) "Run `svn diff' on all selected files. If some files have been marked, compare those non-recursively; @@ -2813,6 +3567,17 @@ If ARG then prompt for revision to diff against, else compare working copy with (not (svn-status-some-files-marked-p)) (if arg :ask "BASE"))) +(defun svn-status-diff-show-changeset (rev &optional user-confirmation) + "Show the changeset for a given log entry. +When called with a prefix argument, ask the user for the revision." + (let* ((upper-rev rev) + (lower-rev (number-to-string (- (string-to-number upper-rev) 1))) + (rev-arg (concat lower-rev ":" upper-rev))) + (when user-confirmation + (setq rev-arg (read-string "Revision for changeset: " rev-arg))) + (svn-run nil t 'diff "diff" (concat "-r" rev-arg)) + (svn-status-activate-diff-mode))) + (defun svn-status-show-svn-diff-internal (line-infos recursive revision) ;; REVISION must be one of: ;; - a string: whatever the -r option allows. @@ -2825,6 +3590,8 @@ If ARG then prompt for revision to diff against, else compare working copy with (setq revision (svn-status-read-revision-string "Diff with files for version: " "PREV"))) + (setq svn-status-last-diff-options (list line-infos recursive revision)) + (let ((clear-buf t) (beginning nil)) (dolist (line-info line-infos) @@ -2846,9 +3613,9 @@ If ARG then prompt for revision to diff against, else compare working copy with ;; expects the output to be left in the *svn-process* buffer. (unless recursive ;; Check `directory-p' relative to the `default-directory' of the - ;; "*svn-status*" buffer, not that of the "*svn-process*" buffer. + ;; "*svn-status*" buffer, not that of the svn-process-buffer-name buffer. (let ((directory-p (svn-status-line-info->directory-p line-info))) - (with-current-buffer "*svn-process*" + (with-current-buffer svn-process-buffer-name (when directory-p (goto-char (or beginning (point-min))) (when (re-search-forward "^Index: " nil t) @@ -2867,9 +3634,18 @@ That function uses `add-log-current-defun'" (message "Copied %S" func-name)) (message "No current defun detected.")))) +(defun svn-status-diff-pop-to-commit-buffer () + "Temporary switch to the `svn-status-buffer-name' buffer and start a commit from there." + (interactive) + (let ((window-conf (current-window-configuration))) + (svn-status-switch-to-status-buffer) + (svn-status-commit) + (set-window-configuration window-conf) + (setq svn-status-pre-commit-window-configuration window-conf) + (pop-to-buffer svn-log-edit-buffer-name))) (defun svn-status-activate-diff-mode () - "Show the *svn-process* buffer, using the diff-mode." + "Show the `svn-process-buffer-name' buffer, using the diff-mode." (svn-status-show-process-output 'diff t) (let ((working-directory default-directory)) (save-excursion @@ -2886,13 +3662,35 @@ Commands: " (let ((diff-mode-shared-map (copy-keymap svn-status-diff-mode-map)) major-mode mode-name) - (diff-mode))) + (diff-mode) + (set (make-local-variable 'revert-buffer-function) 'svn-status-diff-update))) + +(defun svn-status-diff-update (arg noconfirm) + "Rerun the last svn diff command and update the *svn-diff* buffer." + (interactive) + (svn-status-save-some-buffers) + (save-window-excursion + (apply 'svn-status-show-svn-diff-internal svn-status-last-diff-options))) (defun svn-status-show-process-buffer () - "Show the content of the *svn-process* buffer" + "Show the content of the `svn-process-buffer-name' buffer" (interactive) (svn-status-show-process-output nil)) +(defun svn-status-pop-to-partner-buffer () + "Pop to the `svn-status-partner-buffer' if that variable is set." + (interactive) + (when svn-status-partner-buffer + (let ((cur-buf (current-buffer))) + (pop-to-buffer svn-status-partner-buffer) + (setq svn-status-partner-buffer cur-buf)))) + +(defun svn-status-pop-to-new-partner-buffer (buffer) + "Call `pop-to-buffer' and register the current buffer as partner buffer for BUFFER." + (let ((cur-buf (current-buffer))) + (pop-to-buffer buffer) + (setq svn-status-partner-buffer cur-buf))) + (defun svn-status-add-file-recursively (arg) "Run `svn add' on all selected files. When a directory is added, add files recursively. @@ -2914,6 +3712,22 @@ When this function is called with a prefix argument, use the actual file instead (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-get-file-list (not arg)) "") (svn-run t t 'add "add" "--non-recursive" "--targets" svn-status-temp-arg-file)) +(defun svn-status-lock (arg) + "Run `svn lock' on all selected files. +See `svn-status-marked-files' for what counts as selected." + (interactive "P") + (message "locking: %S" (svn-status-get-file-list-names t)) + (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-get-file-list t) "") + (svn-run t t 'lock "lock" "--targets" svn-status-temp-arg-file)) + +(defun svn-status-unlock (arg) + "Run `svn unlock' on all selected files. +See `svn-status-marked-files' for what counts as selected." + (interactive "P") + (message "unlocking: %S" (svn-status-get-file-list-names t)) + (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-get-file-list t) "") + (svn-run t t 'unlock "unlock" "--targets" svn-status-temp-arg-file)) + (defun svn-status-make-directory (dir) "Run `svn mkdir DIR'." ;; TODO: Allow entering a URI interactively. @@ -2924,8 +3738,6 @@ When this function is called with a prefix argument, use the actual file instead (setq dir (file-relative-name dir))) (svn-run t t 'mkdir "mkdir" "--" dir)) -;;TODO: write a svn-status-cp similar to this---maybe a common -;;function to do both? (defun svn-status-mv () "Prompt for a destination, and `svn mv' selected files there. See `svn-status-marked-files' for what counts as `selected'. @@ -2943,26 +3755,44 @@ doesn't check for that. SOLUTION: for each dir, umark all its contents (but not the dir itself) before running mv." (interactive) + (svn-status-mv-cp "mv" "Rename" "Move" "mv")) + +(defun svn-status-cp () + "See `svn-status-mv'" + (interactive) + (svn-status-mv-cp "cp" "Copy" "Copy" "cp")) + +(defun svn-status-mv-cp (command singleprompt manyprompt fallback) + "Run svn COMMAND on marked files, prompting for destination + +This function acts on `svn-status-marked-files': at the prompt the +user can enter a new file name, or an existing directory: this is used as the argument for svn COMMAND. + COMMAND --- string saying what to do: \"mv\" or \"cp\" + SINGLEPROMPT --- string at start of prompt when one file marked + MANYPROMPT --- string at start of prompt when multiple files marked + FALLBACK --- If any marked file is unversioned, use this instead of 'svn COMMAND'" (let* ((marked-files (svn-status-marked-files)) (num-of-files (length marked-files)) dest) (if (= 1 num-of-files) - ;; one file to rename, prompt for new name, or directory to move the - ;; file into. - (setq dest (read-file-name (format "Rename %s to: " - (svn-status-line-info->filename (car marked-files))) - (svn-status-directory-containing-point t) - (svn-status-line-info->full-path (car marked-files)))) + ;; one file to act on: new name, or directory to hold results + (setq dest (read-file-name + (format "%s %s to: " singleprompt + (svn-status-line-info->filename (car marked-files))) + (svn-status-directory-containing-point t) + (svn-status-line-info->full-path (car marked-files)))) + ;;TODO: (when file-exists-p but-no-dir-p dest (error "%s already exists" dest)) ;;multiple files selected, so prompt for existing directory to mv them into. - (setq dest (svn-read-directory-name (format "Move %d files to directory: " num-of-files) - (svn-status-directory-containing-point t) nil t)) + (setq dest (svn-read-directory-name + (format "%s %d files to directory: " manyprompt num-of-files) + (svn-status-directory-containing-point t) nil t)) (unless (file-directory-p dest) (error "%s is not a directory" dest))) (when (string= dest "") - (error "No destination entered; no files moved")) + (error "No destination entered")) (unless (string-match "^[^:/]+://" dest) ; Is it a URI? (setq dest (file-relative-name dest))) -; + ;;do the move: svn mv only lets us move things once at a time, so ;;we need to run svn mv once for each file (hence second arg to ;;svn-run is nil.) @@ -2970,7 +3800,7 @@ itself) before running mv." ;;TODO: before doing any moving, For every marked directory, ;;ensure none of its contents are also marked, since we dont want ;;to move both file *and* its parent... - ;; what about hidden files?? what if user marks a dir+contents, then presses `_' ?? + ;; what about elided files? what if user marks a dir+contents, then presses `_' ? ;; ;one solution: ;; (dolist (original marked-files) ;; (when (svn-status-line-info->directory-p original) @@ -2982,32 +3812,72 @@ itself) before running mv." (dolist (original marked-files) (let ((original-name (svn-status-line-info->filename original)) (original-filemarks (svn-status-line-info->filemark original)) - (original-propmarks (svn-status-line-info->propmark original))) + (original-propmarks (svn-status-line-info->propmark original)) + (moved nil)) (cond - ((or (eq original-filemarks 77) ;;original has local mods: maybe do `svn mv --force' - (eq original-propmarks 77)) ;;original has local prop mods: maybe do `svn mv --force' - (if (yes-or-no-p (format "%s has local modifications; use `--force' to really move it? " - original-name)) - (svn-run nil t 'mv "mv" "--force" "--" original-name dest) - (message "Not moving %s" original-name))) - ((eq original-filemarks 63) ;;original is unversioned: maybe do plain `mv' - (if (yes-or-no-p (format "%s is unversioned. Use plain `mv -i %s %s'? " - original-name original-name dest)) - (call-process "mv" nil (get-buffer-create "*svn-process*") nil "-i" original-name dest) - (message "Not moving %s" original-name))) - - ((eq original-filemarks 65) ;;original has `A' mark (eg it was `svn add'ed, but not committed) - (message "Not moving %s (try committing it first)" original-name)) - - ((eq original-filemarks 32) ;;original is unmodified: can use `svn mv' - (svn-run nil t 'mv "mv" "--" original-name dest)) - - ;;file is conflicted in some way? + ((or (eq original-filemarks ?M) ;local mods: maybe do `svn mv --force' + (eq original-propmarks ?M)) ;local prop mods: maybe do `svn mv --force' + (if (yes-or-no-p + (format "%s has local modifications; use `--force' to really move it? " original-name)) + (progn + (svn-status-run-mv-cp command original-name dest t) + (setq moved t)) + (message "Not acting on %s" original-name))) + ((eq original-filemarks ??) ;original is unversioned: use fallback + (if (yes-or-no-p (format "%s is unversioned. Use `%s -i -- %s %s'? " + original-name fallback original-name dest)) + (progn (call-process fallback nil (get-buffer-create svn-process-buffer-name) nil + "-i" "--" original-name dest) + (setq moved t)) + ;;new files created by fallback are not in *svn-status* now, + ;;TODO: so call (svn-status-update) here? + (message "Not acting on %s" original-name))) + + ((eq original-filemarks ?A) ;;`A' (`svn add'ed, but not committed) + (message "Not acting on %s (commit it first)" original-name)) + + ((eq original-filemarks ? ) ;original is unmodified: can proceed + (svn-status-run-mv-cp command original-name dest) + (setq moved t)) + + ;;file has some other mark (eg conflicted) (t - (if (yes-or-no-p (format "The status of %s looks scary. Risk moving it anyway? " original-name)) - (svn-run nil t 'mv "mv" "--" original-name dest) - (message "Not moving %s" original-name)))))) - (svn-status-update))) + (if (yes-or-no-p + (format "The status of %s looks scary. Risk moving it anyway? " + original-name)) + (progn + (svn-status-run-mv-cp command original-name dest) + (setq moved t)) + (message "Not acting on %s" original-name)))) + (when moved + (message "psvn: did '%s' from %s to %s" command original-name dest) + ;; Silently rename the visited file of any buffer visiting this file. + (when (get-file-buffer original-name) + (with-current-buffer (get-file-buffer original-name) + (set-visited-file-name dest nil t)))))) + (svn-status-update))) + +(defun svn-status-run-mv-cp (command original destination &optional force) + "Actually run svn mv or svn cp. +This is just to prevent duplication in `svn-status-prompt-and-act-on-files'" + (if force + (svn-run nil t (intern command) command "--force" "--" original destination) + (svn-run nil t (intern command) command "--" original destination)) +;;;TODO: use something like the following instead of calling svn-status-update +;;; at the end of svn-status-mv-cp. +;; (let ((output (svn-status-parse-ar-output)) +;; newfile +;; buffer-read-only) ; otherwise insert-line-in-status-buffer fails +;; (dolist (new-file output) +;; (when (eq (cadr new-file) 'added-wc) +;; ;; files with 'wc-added action do not exist in *svn-status* +;; ;; buffer yet, so give each of them their own line-info +;; ;; TODO: need to insert the new line-info in a sensible place, ie in the correct directory! [svn-status-filename-to-buffer-position-cache might help?] + +;; (svn-insert-line-in-status-buffer +;; (svn-status-make-line-info (car new-file))))) +;; (svn-status-update-with-command-list output)) + ) (defun svn-status-revert () "Run `svn revert' on all selected files. @@ -3026,7 +3896,9 @@ See `svn-status-marked-files' for what counts as selected." (defun svn-status-rm (force) "Run `svn rm' on all selected files. See `svn-status-marked-files' for what counts as selected. -When called with a prefix argument add the command line switch --force." +When called with a prefix argument add the command line switch --force. + +Forcing the deletion can also be used to delete files not under svn control." (interactive "P") (let* ((marked-files (svn-status-marked-files)) (num-of-files (length marked-files))) @@ -3037,17 +3909,38 @@ When called with a prefix argument add the command line switch --force." (message "removing: %S" (svn-status-marked-file-names)) (svn-status-create-arg-file svn-status-temp-arg-file "" (svn-status-marked-files) "") (if force - (svn-run t t 'rm "rm" "--force" "--targets" svn-status-temp-arg-file) + (save-excursion + (svn-run t t 'rm "rm" "--force" "--targets" svn-status-temp-arg-file) + (dolist (to-delete (svn-status-marked-files)) + (when (eq (svn-status-line-info->filemark to-delete) ??) + (svn-status-goto-file-name (svn-status-line-info->filename to-delete)) + (let ((buffer-read-only nil)) + (delete-region (svn-point-at-bol) (+ 1 (svn-point-at-eol))) + (delete to-delete svn-status-info))))) (svn-run t t 'rm "rm" "--targets" svn-status-temp-arg-file))))) (defun svn-status-update-cmd (arg) "Run svn update. -When called with a prefix argument, ask the user for the revision to update to." +When called with a prefix argument, ask the user for the revision to update to. +When called with a negative prefix argument, only update the selected files." (interactive "P") - (let ((rev (when arg (svn-status-read-revision-string (format "Directory: %s: Run svn update -r " default-directory))))) - (message "Running svn-update for %s" default-directory) - ;;TODO: use file names also?? - (svn-run t t 'update "update" (when rev (list "-r" rev))))) + (let* ((selective-update (or (and (numberp arg) (< arg 0)) (eq arg '-))) + (rev (when arg (svn-status-read-revision-string + (if selective-update + (format "Selected entries: Run svn update -r ") + (format "Directory: %s: Run svn update -r " default-directory)) + (if selective-update "HEAD" nil))))) + (if selective-update + (progn + (message "Running svn-update for %s" (svn-status-marked-file-names)) + (svn-run t t 'update "update" + (when rev (list "-r" rev)) + (list "--non-interactive") + (svn-status-marked-file-names))) + (message "Running svn-update for %s" default-directory) + (svn-run t t 'update "update" + (when rev (list "-r" rev)) + (list "--non-interactive"))))) (defun svn-status-commit () "Commit selected files. @@ -3057,26 +3950,29 @@ normally marks all of its files as well. If no files have been marked, commit recursively the file at point." (interactive) (svn-status-save-some-buffers) - (let* ((selected-files (svn-status-marked-files)) - (marked-files-p (svn-status-some-files-marked-p))) + (let* ((selected-files (svn-status-marked-files))) (setq svn-status-files-to-commit selected-files - svn-status-recursive-commit (not marked-files-p)) + svn-status-recursive-commit (not (svn-status-only-dirs-or-nothing-marked-p))) (svn-log-edit-show-files-to-commit) (svn-status-pop-to-commit-buffer) (when svn-log-edit-insert-files-to-commit (svn-log-edit-insert-files-to-commit)))) (defun svn-status-pop-to-commit-buffer () + "Pop to the svn commit buffer. +If a saved log message exists in `svn-log-edit-file-name' insert it in the buffer." (interactive) (setq svn-status-pre-commit-window-configuration (current-window-configuration)) - (let* ((use-existing-buffer (get-buffer "*svn-log-edit*")) - (commit-buffer (get-buffer-create "*svn-log-edit*")) - (dir default-directory)) + (let* ((use-existing-buffer (get-buffer svn-log-edit-buffer-name)) + (commit-buffer (get-buffer-create svn-log-edit-buffer-name)) + (dir default-directory) + (log-edit-file-name)) (pop-to-buffer commit-buffer) (setq default-directory dir) + (setq log-edit-file-name (svn-log-edit-file-name)) (unless use-existing-buffer - (when (and svn-log-edit-file-name (file-readable-p svn-log-edit-file-name)) - (insert-file-contents svn-log-edit-file-name))) + (when (and log-edit-file-name (file-readable-p log-edit-file-name)) + (insert-file-contents log-edit-file-name))) (svn-log-edit-mode))) (defun svn-status-switch-to-status-buffer () @@ -3089,6 +3985,20 @@ If no files have been marked, commit recursively the file at point." (interactive) (pop-to-buffer svn-status-buffer-name)) +(defun svn-status-via-bookmark (bookmark) + "Allows a quick selection of a bookmark in `svn-bookmark-list'. +Run `svn-status' on the selected bookmark." + (interactive + (list + (let ((completion-ignore-case t)) + (funcall svn-status-completing-read-function "SVN status bookmark: " svn-bookmark-list)))) + (unless bookmark + (error "No bookmark specified")) + (let ((directory (cdr (assoc bookmark svn-bookmark-list)))) + (if (file-directory-p directory) + (svn-status directory) + (error "%s is not a directory" directory)))) + (defun svn-status-export () "Run `svn export' for the current working copy. Ask the user for the destination path. @@ -3222,8 +4132,8 @@ names are relative to the directory where `svn-status' was run." ;; In `svn-status-show-svn-diff-internal', there is a comment ;; that REVISION `nil' might mean omitting the -r option entirely. ;; That doesn't seem like a good idea with svn cat. - ;; - ;; TODO: Return the alist, instead of storing it in a variable. + + ;; (message "svn-status-get-specific-revision-internal: %S %S" line-infos revision) (when (eq revision :ask) (setq revision (svn-status-read-revision-string @@ -3244,80 +4154,102 @@ names are relative to the directory where `svn-status' was run." (if (eq revision :auto) "HEAD or BASE" revision) count))) - (setq svn-status-get-specific-revision-file-info '()) - (dolist (line-info line-infos) - (let* ((revision (if (eq revision :auto) - (if (svn-status-line-info->update-available line-info) - "HEAD" "BASE") - revision)) ;must be a string by this point - (file-name (svn-status-line-info->filename line-info)) - ;; If REVISION is e.g. "HEAD", should we find out the actual - ;; revision number and save "foo.~123~" rather than "foo.~HEAD~"? - ;; OTOH, `auto-mode-alist' already ignores ".~HEAD~" suffixes, - ;; and if users often want to know the revision numbers of such - ;; files, they can use svn:keywords. - (file-name-with-revision (concat file-name ".~" revision "~"))) - ;; `add-to-list' would unnecessarily check for duplicates. - (push (cons file-name file-name-with-revision) - svn-status-get-specific-revision-file-info) - (save-excursion - (let ((content - (with-temp-buffer - (if (string= revision "BASE") - (insert-file-contents (concat (file-name-directory file-name) - (svn-wc-adm-dir-name) - "/text-base/" - (file-name-nondirectory file-name) - ".svn-base")) - (progn - (svn-run nil t 'cat "cat" "-r" revision file-name) - ;;todo: error processing - ;;svn: Filesystem has no item - ;;svn: file not found: revision `15', path `/trunk/file.txt' - (insert-buffer-substring "*svn-process*"))) - (buffer-string)))) - (find-file file-name-with-revision) - (setq buffer-read-only nil) - (erase-buffer) ;Widen, because we'll save the whole buffer. - (insert content) - (save-buffer))))) - (setq svn-status-get-specific-revision-file-info - (nreverse svn-status-get-specific-revision-file-info)) - (message "svn-status-get-specific-revision-file-info: %S" - svn-status-get-specific-revision-file-info)) - + (let ((svn-status-get-specific-revision-file-info '())) + (dolist (line-info line-infos) + (let* ((revision (if (eq revision :auto) + (if (svn-status-line-info->update-available line-info) + "HEAD" "BASE") + revision)) ;must be a string by this point + (file-name (svn-status-line-info->filename line-info)) + ;; If REVISION is e.g. "HEAD", should we find out the actual + ;; revision number and save "foo.~123~" rather than "foo.~HEAD~"? + ;; OTOH, `auto-mode-alist' already ignores ".~HEAD~" suffixes, + ;; and if users often want to know the revision numbers of such + ;; files, they can use svn:keywords. + (file-name-with-revision (concat (file-name-nondirectory file-name) ".~" revision "~")) + (default-directory (concat (svn-status-base-dir) (file-name-directory file-name)))) + ;; `add-to-list' would unnecessarily check for duplicates. + (push (cons file-name (concat (file-name-directory file-name) file-name-with-revision)) svn-status-get-specific-revision-file-info) + ;; (message "file-name-with-revision: %s %S" file-name-with-revision (file-exists-p file-name-with-revision)) + (save-excursion + (if (or (not (file-exists-p file-name-with-revision)) ;; file does not exist + (not (string= (number-to-string (string-to-number revision)) revision))) ;; revision is not a number + (progn + (message "getting revision %s for %s" revision file-name) + (let ((content + (with-temp-buffer + (if (string= revision "BASE") + (insert-file-contents (concat (svn-wc-adm-dir-name) + "/text-base/" + (file-name-nondirectory file-name) + ".svn-base")) + (progn + (svn-run nil t 'cat "cat" "-r" revision (file-name-nondirectory file-name)) + ;;todo: error processing + ;;svn: Filesystem has no item + ;;svn: file not found: revision `15', path `/trunk/file.txt' + (insert-buffer-substring svn-process-buffer-name))) + (buffer-string)))) + (find-file file-name-with-revision) + (setq buffer-read-only nil) + (erase-buffer) ;Widen, because we'll save the whole buffer. + (insert content) + (goto-char (point-min)) + (save-buffer))) + (find-file file-name-with-revision))))) + ;;(message "default-directory: %s revision-file-info: %S" default-directory svn-status-get-specific-revision-file-info) + (nreverse svn-status-get-specific-revision-file-info))) (defun svn-status-ediff-with-revision (arg) - "Run ediff on the current file with a previous revision. + "Run ediff on the current file with a different revision. +If there is a newer revision in the repository, the diff is done against HEAD, +otherwise compare the working copy with BASE. If ARG then prompt for revision to diff against." (interactive "P") - (svn-status-get-specific-revision-internal - (list (svn-status-get-line-information)) - (if arg :ask :auto)) - (let* ((ediff-after-quit-destination-buffer (current-buffer)) + (let* ((svn-status-get-specific-revision-file-info + (svn-status-get-specific-revision-internal + (list (svn-status-make-line-info + (file-relative-name + (svn-status-line-info->full-path (svn-status-get-line-information)) + (svn-status-base-dir)) + nil nil nil nil nil nil + (svn-status-line-info->update-available (svn-status-get-line-information)))) + (if arg :ask :auto))) + (ediff-after-quit-destination-buffer (current-buffer)) + (default-directory (svn-status-base-dir)) (my-buffer (find-file-noselect (caar svn-status-get-specific-revision-file-info))) (base-buff (find-file-noselect (cdar svn-status-get-specific-revision-file-info))) - (svn-transient-buffers (list base-buff )) + (svn-transient-buffers (list my-buffer base-buff)) (startup-hook '(svn-ediff-startup-hook))) (ediff-buffers base-buff my-buffer startup-hook))) (defun svn-ediff-startup-hook () + ;; (message "svn-ediff-startup-hook: ediff-after-quit-hook-internal: %S" ediff-after-quit-hook-internal) (add-hook 'ediff-after-quit-hook-internal - `(lambda () - (svn-ediff-exit-hook - ',ediff-after-quit-destination-buffer ',svn-transient-buffers)) - nil 'local)) + `(lambda () + (svn-ediff-exit-hook + ',ediff-after-quit-destination-buffer ',svn-transient-buffers)) + nil 'local)) (defun svn-ediff-exit-hook (svn-buf tmp-bufs) + ;; (message "svn-ediff-exit-hook: svn-buf: %s, tmp-bufs: %s" svn-buf tmp-bufs) ;; kill the temp buffers (and their associated windows) (dolist (tb tmp-bufs) (when (and tb (buffer-live-p tb) (not (buffer-modified-p tb))) - (let ((win (get-buffer-window tb t))) - (when win (delete-window win)) - (kill-buffer tb)))) + (let* ((win (get-buffer-window tb t)) + (file-name (buffer-file-name tb)) + (is-temp-file (numberp (string-match "~\\([0-9]+\\|BASE\\)~" file-name)))) + ;; (message "svn-ediff-exit-hook - is-temp-file: %s, temp-buf:: %s - %s " is-temp-file (current-buffer) file-name) + (when (and win (> (count-windows) 1) + (delete-window win))) + (kill-buffer tb) + (when (and is-temp-file svn-status-ediff-delete-temporary-files) + (when (or (eq svn-status-ediff-delete-temporary-files t) + (y-or-n-p (format "Delete File '%s' ? " file-name))) + (delete-file file-name)))))) ;; switch back to the *svn* buffer (when (and svn-buf (buffer-live-p svn-buf) - (not (get-buffer-window svn-buf t))) + (not (get-buffer-window svn-buf t))) (ignore-errors (switch-to-buffer svn-buf)))) @@ -3326,6 +4258,14 @@ If ARG then prompt for revision to diff against." (interactive) (read-string prompt default-value)) +(defun svn-file-show-svn-ediff (arg) + "Run ediff on the current file with a previous revision. +If ARG then prompt for revision to diff against." + (interactive "P") + (let ((svn-status-get-line-information-for-file 'relative) + (default-directory (svn-status-base-dir))) + (svn-status-ediff-with-revision arg))) + ;; -------------------------------------------------------------------------------- ;; SVN process handling ;; -------------------------------------------------------------------------------- @@ -3344,13 +4284,24 @@ This is useful, if the running svn process asks the user a question. Note: use C-q C-j to send a line termination character." (interactive "sSend string to svn process: ") (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (goto-char (point-max)) (let ((buffer-read-only nil)) (insert (if send-passwd (make-string (length string) ?.) string))) (set-marker (process-mark (get-process "svn")) (point))) (process-send-string "svn" string)) +(defun svn-process-send-string-and-newline (string &optional send-passwd) + "Send a string to the running svn process. +Just call `svn-process-send-string' with STRING and an end of line termination. +When called with a prefix argument, read the data from user as password." + (interactive (let* ((use-passwd current-prefix-arg) + (s (if use-passwd + (read-passwd "Send secret line to svn process: ") + (read-string "Send line to svn process: ")))) + (list s use-passwd))) + (svn-process-send-string (concat string "\n") send-passwd)) + ;; -------------------------------------------------------------------------------- ;; Property List stuff ;; -------------------------------------------------------------------------------- @@ -3391,7 +4342,7 @@ When called with a prefix argument, it is possible to enter a new property." (prop-name) (prop-value)) (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (goto-char (point-min)) (forward-line 1) (while (looking-at " \\(.+\\)") @@ -3438,7 +4389,7 @@ When called with a prefix argument, it is possible to enter a new property." (svn-run t t 'propdel (append (list "propdel" prop-name) file-names)))))))))) -(defun svn-status-property-edit (file-info-list prop-name &optional new-prop-value) +(defun svn-status-property-edit (file-info-list prop-name &optional new-prop-value remove-values) (let* ((commit-buffer (get-buffer-create "*svn-property-edit*")) (dir default-directory) ;; now only one file is implemented ... @@ -3447,7 +4398,7 @@ When called with a prefix argument, it is possible to enter a new property." (message "Edit property %s for file %s" prop-name file-name) (svn-run nil t 'propget-parse "propget" prop-name file-name) (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (setq prop-value (if (> (point-max) 1) (buffer-substring (point-min) (- (point-max) 1)) ""))) @@ -3463,14 +4414,18 @@ When called with a prefix argument, it is possible to enter a new property." (svn-status-remove-control-M) (when new-prop-value (when (listp new-prop-value) - (message "Adding new prop values %S " new-prop-value) + (if remove-values + (message "Remove prop values %S " new-prop-value) + (message "Adding new prop values %S " new-prop-value)) (while new-prop-value (goto-char (point-min)) - (unless (re-search-forward - (concat "^" (regexp-quote (car new-prop-value)) "$") nil t) - (goto-char (point-max)) - (when (> (current-column) 0) (insert "\n")) - (insert (car new-prop-value))) + (if (re-search-forward (concat "^" (regexp-quote (car new-prop-value)) "$") nil t) + (when remove-values + (kill-whole-line 1)) + (unless remove-values + (goto-char (point-max)) + (when (> (current-column) 0) (insert "\n")) + (insert (car new-prop-value)))) (setq new-prop-value (cdr new-prop-value))))) (svn-prop-edit-mode))) @@ -3577,6 +4532,25 @@ When called with a prefix argument, it is possible to enter a new property." ;;(message "Set svn:keywords for %S" (svn-status-marked-file-names)) (svn-status-property-edit (svn-status-marked-files) "svn:keywords")) +(defun svn-status-property-set-keyword-id (arg) + "Set/Remove Id from the svn:keywords property. +Normally Id is added to the svn:keywords property. + +When called with the prefix arg -, remove Id from the svn:keywords property." + (interactive "P") + (svn-status-property-edit (svn-status-marked-files) "svn:keywords" '("Id") (eq arg '-)) + (svn-prop-edit-do-it nil)) + +(defun svn-status-property-set-keyword-date (arg) + "Set/Remove Date from the svn:keywords property. +Normally Date is added to the svn:keywords property. + +When called with the prefix arg -, remove Date from the svn:keywords property." + (interactive "P") + (svn-status-property-edit (svn-status-marked-files) "svn:keywords" '("Date") (eq arg '-)) + (svn-prop-edit-do-it nil)) + + (defun svn-status-property-set-eol-style () "Edit the svn:eol-style property on the marked files." (interactive) @@ -3591,6 +4565,19 @@ When called with a prefix argument, it is possible to enter a new property." (interactive) (svn-status-property-set-property (svn-status-marked-files) "svn:executable" "*")) +(defun svn-status-property-set-mime-type () + "Set the svn:mime-type property on the marked files." + (interactive) + (require 'mailcap nil t) + (let ((completion-ignore-case t) + (mime-types (when (fboundp 'mailcap-mime-types) + (mailcap-mime-types)))) + (svn-status-property-set-property + (svn-status-marked-files) "svn:mime-type" + (funcall svn-status-completing-read-function "Set svn:mime-type for the marked files: " + (mapcar (lambda (x) (cons x x)) ; for Emacs 21 + (sort mime-types 'string<)))))) + ;; -------------------------------------------------------------------------------- ;; svn-prop-edit-mode: ;; -------------------------------------------------------------------------------- @@ -3636,13 +4623,15 @@ Commands: (svn-prop-edit-do-it t)) (defun svn-prop-edit-do-it (async) + "Run svn propset `svn-status-propedit-property-name' with the content of the +*svn-property-edit* buffer." (message "svn propset %s on %s" svn-status-propedit-property-name (mapcar 'svn-status-line-info->filename svn-status-propedit-file-list)) (save-excursion (set-buffer (get-buffer "*svn-property-edit*")) (when (fboundp 'set-buffer-file-coding-system) - (set-buffer-file-coding-system 'undecided-unix nil)) + (set-buffer-file-coding-system svn-status-svn-file-coding-system nil)) (setq svn-status-temp-file-to-remove (concat svn-status-temp-dir "svn-prop-edit.txt" svn-temp-suffix)) (write-region (point-min) (point-max) svn-status-temp-file-to-remove nil 1)) @@ -3651,9 +4640,11 @@ Commands: svn-status-propedit-file-list "") (setq svn-status-propedit-file-list nil) (svn-run async t 'propset "propset" - svn-status-propedit-property-name - "--targets" svn-status-temp-arg-file - "-F" (concat svn-status-temp-dir "svn-prop-edit.txt" svn-temp-suffix)) + svn-status-propedit-property-name + "--targets" svn-status-temp-arg-file + (when (eq svn-status-svn-file-coding-system 'utf-8) + '("--encoding" "UTF-8")) + "-F" (concat svn-status-temp-dir "svn-prop-edit.txt" svn-temp-suffix)) (unless async (svn-status-remove-temp-file-maybe))) (when svn-status-pre-propedit-window-configuration (set-window-configuration svn-status-pre-propedit-window-configuration))) @@ -3684,6 +4675,10 @@ Commands: (defvar svn-log-edit-mode-menu) ;really defined with `easy-menu-define' below. +(defun svn-log-edit-common-setup () + (set (make-local-variable 'paragraph-start) svn-log-edit-paragraph-start) + (set (make-local-variable 'paragraph-separate) svn-log-edit-paragraph-separate)) + (if svn-log-edit-use-log-edit-mode (define-derived-mode svn-log-edit-mode log-edit-mode "svn-log-edit" "Wrapper around `log-edit-mode' for psvn.el" @@ -3692,6 +4687,7 @@ Commands: (set (make-local-variable 'log-edit-callback) 'svn-log-edit-done) (set (make-local-variable 'log-edit-listfun) 'svn-log-edit-files-to-commit) (set (make-local-variable 'log-edit-initial-files) (log-edit-files)) + (svn-log-edit-common-setup) (message "Press %s when you are done editing." (substitute-command-keys "\\[log-edit-done]")) ) @@ -3706,6 +4702,7 @@ Commands: (setq major-mode 'svn-log-edit-mode) (setq mode-name "svn-log-edit") (setq svn-log-edit-update-log-entry nil) + (svn-log-edit-common-setup) (run-hooks 'svn-log-edit-mode-hook))) (when (not svn-log-edit-mode-map) @@ -3739,14 +4736,15 @@ Commands: (set-window-configuration svn-status-pre-commit-window-configuration)) (defun svn-log-edit-done () + "Finish editing the log message and run svn commit." (interactive) (svn-status-save-some-buffers) (save-excursion - (set-buffer (get-buffer "*svn-log-edit*")) + (set-buffer (get-buffer svn-log-edit-buffer-name)) (when svn-log-edit-insert-files-to-commit (svn-log-edit-remove-comment-lines)) (when (fboundp 'set-buffer-file-coding-system) - (set-buffer-file-coding-system 'undecided-unix nil)) + (set-buffer-file-coding-system svn-status-svn-file-coding-system nil)) (when (or svn-log-edit-update-log-entry svn-status-files-to-commit) (setq svn-status-temp-file-to-remove (concat svn-status-temp-dir "svn-log-edit.txt" svn-temp-suffix)) @@ -3759,7 +4757,7 @@ Commands: (concat "-r" svn-log-edit-update-log-entry) "-F" svn-status-temp-file-to-remove) (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (message "%s" (buffer-substring (point-min) (- (point-max) 1))))) (when svn-status-files-to-commit ; there are files to commit (setq svn-status-operated-on-dot @@ -3771,6 +4769,8 @@ Commands: (unless svn-status-recursive-commit "--non-recursive") "--targets" svn-status-temp-arg-file "-F" svn-status-temp-file-to-remove + (when (eq svn-status-svn-file-coding-system 'utf-8) + '("--encoding" "UTF-8")) svn-status-default-commit-arguments)) (set-window-configuration svn-status-pre-commit-window-configuration) (message "svn-log editing done"))) @@ -3808,12 +4808,15 @@ If ARG then show diff between some other version of the selected files." (defun svn-log-edit-save-message () "Save the current log message to the file `svn-log-edit-file-name'." (interactive) - (write-region (point-min) (point-max) svn-log-edit-file-name)) + (let ((log-edit-file-name (svn-log-edit-file-name))) + (if (string= buffer-file-name log-edit-file-name) + (save-buffer) + (write-region (point-min) (point-max) log-edit-file-name)))) (defun svn-log-edit-erase-edit-buffer () - "Delete everything in the *svn-log-edit* buffer." + "Delete everything in the `svn-log-edit-buffer-name' buffer." (interactive) - (set-buffer "*svn-log-edit*") + (set-buffer svn-log-edit-buffer-name) (erase-buffer)) (defun svn-log-edit-insert-files-to-commit () @@ -3838,6 +4841,123 @@ If ARG then show diff between some other version of the selected files." (goto-char (point-min)) (flush-lines "^## .*"))) +(defun svn-file-add-to-changelog (prefix-arg) + "Create a changelog entry for the function at point. +The variable `svn-status-changelog-style' allows to select the used changlog style" + (interactive "P") + (cond ((eq svn-status-changelog-style 'changelog) + (svn-file-add-to-log-changelog-style prefix-arg)) + ((eq svn-status-changelog-style 'svn-dev) + (svn-file-add-to-log-svn-dev-style prefix-arg)) + ((fboundp svn-status-changelog-style) + (funcall svn-status-changelog-style prefix-arg)) + (t + (error "Invalid setting for `svn-status-changelog-style'")))) + +(defun svn-file-add-to-log-changelog-style (curdir) + "Create a changelog entry for the function at point. +`add-change-log-entry-other-window' creates the header information. +If CURDIR, save the log file in the current directory, otherwise in the base directory of this working copy." + (interactive "P") + (add-change-log-entry-other-window nil (svn-log-edit-file-name curdir)) + (svn-log-edit-mode)) + +;; taken from svn-dev.el: svn-log-path-derive +(defun svn-dev-log-path-derive (path) + "Derive a relative directory path for absolute PATH, for a log entry." + (save-match-data + (let ((base (file-name-nondirectory path)) + (chop-spot (string-match + "\\(code/\\)\\|\\(src/\\)\\|\\(projects/\\)" + path))) + (if chop-spot + (progn + (setq path (substring path (match-end 0))) + ;; Kluge for Subversion developers. + (if (string-match "subversion/" path) + (substring path (+ (match-beginning 0) 11)) + path)) + (string-match (expand-file-name "~/") path) + (substring path (match-end 0)))))) + +;; taken from svn-dev.el: svn-log-message +(defun svn-file-add-to-log-svn-dev-style (prefix-arg) + "Add to an in-progress log message, based on context around point. +If PREFIX-ARG is negative, then use basenames only in +log messages, otherwise use full paths. The current defun name is +always used. + +If PREFIX-ARG is a list (e.g. by using C-u), save the log file in +the current directory, otherwise in the base directory of this +working copy. + +If the log message already contains material about this defun, then put +point there, so adding to that material is easy. + +Else if the log message already contains material about this file, put +point there, and push onto the kill ring the defun name with log +message dressing around it, plus the raw defun name, so yank and +yank-next are both useful. + +Else if there is no material about this defun nor file anywhere in the +log message, then put point at the end of the message and insert a new +entry for file with defun. +" + (interactive "P") + (let* ((short-file-names (and (numberp prefix-arg) (< prefix-arg 0))) + (curdir (listp prefix-arg)) + (this-file (if short-file-names + (file-name-nondirectory buffer-file-name) + (svn-dev-log-path-derive buffer-file-name))) + (this-defun (or (add-log-current-defun) + (save-excursion + (save-match-data + (if (eq major-mode 'c-mode) + (progn + (if (fboundp 'c-beginning-of-statement-1) + (c-beginning-of-statement-1) + (c-beginning-of-statement)) + (search-forward "(" nil t) + (forward-char -1) + (forward-sexp -1) + (buffer-substring + (point) + (progn (forward-sexp 1) (point))))))))) + (log-file (svn-log-edit-file-name curdir))) + (find-file log-file) + (goto-char (point-min)) + ;; Strip text properties from strings + (set-text-properties 0 (length this-file) nil this-file) + (set-text-properties 0 (length this-defun) nil this-defun) + ;; If log message for defun already in progress, add to it + (if (and + this-defun ;; we have a defun to work with + (search-forward this-defun nil t) ;; it's in the log msg already + (save-excursion ;; and it's about the same file + (save-match-data + (if (re-search-backward ; Ick, I want a real filename regexp! + "^\\*\\s-+\\([a-zA-Z0-9-_.@=+^$/%!?(){}<>]+\\)" nil t) + (string-equal (match-string 1) this-file) + t)))) + (if (re-search-forward ":" nil t) + (if (looking-at " ") (forward-char 1))) + ;; Else no log message for this defun in progress... + (goto-char (point-min)) + ;; But if log message for file already in progress, add to it. + (if (search-forward this-file nil t) + (progn + (if this-defun (progn + (kill-new (format "(%s): " this-defun)) + (kill-new this-defun))) + (search-forward ")" nil t) + (if (looking-at " ") (forward-char 1))) + ;; Found neither defun nor its file, so create new entry. + (goto-char (point-max)) + (if (not (bolp)) (insert "\n")) + (insert (format "\n* %s (%s): " this-file (or this-defun ""))) + ;; Finally, if no derived defun, put point where the user can + ;; type it themselves. + (if (not this-defun) (forward-char -3)))))) ;; -------------------------------------------------------------------------------- ;; svn-log-view-mode: @@ -3851,7 +4971,12 @@ If ARG then show diff between some other version of the selected files." (suppress-keymap svn-log-view-mode-map) (define-key svn-log-view-mode-map (kbd "p") 'svn-log-view-prev) (define-key svn-log-view-mode-map (kbd "n") 'svn-log-view-next) + (define-key svn-log-view-mode-map (kbd "~") 'svn-log-get-specific-revision) + (define-key svn-log-view-mode-map (kbd "E") 'svn-log-ediff-specific-revision) (define-key svn-log-view-mode-map (kbd "=") 'svn-log-view-diff) + (define-key svn-log-view-mode-map (kbd "TAB") 'svn-log-next-link) + (define-key svn-log-view-mode-map [backtab] 'svn-log-prev-link) + (define-key svn-log-view-mode-map (kbd "RET") 'svn-log-find-file-at-point) (define-key svn-log-view-mode-map (kbd "e") 'svn-log-edit-log-entry) (define-key svn-log-view-mode-map (kbd "q") 'bury-buffer)) @@ -3867,6 +4992,8 @@ If ARG then show diff between some other version of the selected files." "'svn-log-view-mode' menu" '("SVN-LogView" ["Show Changeset" svn-log-view-diff t] + ["Ediff file at point" svn-log-ediff-specific-revision t] + ["Find file at point" svn-log-find-file-at-point t] ["Edit log message" svn-log-edit-log-entry t])) (defun svn-log-view-popup-menu (event) @@ -3878,14 +5005,12 @@ If ARG then show diff between some other version of the selected files." 'svn-status-marked-popup-face (svn-point-at-bol) (svn-point-at-eol) svn-log-view-mode-menu)))) -(defvar svn-log-view-font-lock-keywords - '(("^r[0-9]+ .+" (0 `(face - font-lock-keyword-face - mouse-face - highlight +(defvar svn-log-view-font-lock-basic-keywords + '(("^r[0-9]+ .+" (0 `(face font-lock-keyword-face + mouse-face highlight keymap ,svn-log-view-popup-menu-map)))) - "Keywords in svn-log-view-mode.") -(put 'svn-log-view-font-lock-keywords 'risky-local-variable t) ;for Emacs 20.7 + "Basic keywords in `svn-log-view-mode'.") +(put 'svn-log-view-font-basic-lock-keywords 'risky-local-variable t) ;for Emacs 20.7 (define-derived-mode svn-log-view-mode fundamental-mode "svn-log-view" "Major Mode to show the output from svn log. @@ -3894,34 +5019,111 @@ Commands: " (use-local-map svn-log-view-mode-map) (easy-menu-add svn-log-view-mode-menu) + (set (make-local-variable 'svn-log-view-font-lock-keywords) svn-log-view-font-lock-basic-keywords) + (dolist (lh svn-log-link-handlers) + (add-to-list 'svn-log-view-font-lock-keywords (gethash lh svn-log-registered-link-handlers))) (set (make-local-variable 'font-lock-defaults) '(svn-log-view-font-lock-keywords t))) (defun svn-log-view-next () (interactive) (when (re-search-forward "^r[0-9]+" nil t) - (beginning-of-line 3))) + (beginning-of-line 2) + (unless (looking-at "Changed paths:") + (beginning-of-line 1)))) (defun svn-log-view-prev () (interactive) (when (re-search-backward "^r[0-9]+" nil t 2) - (beginning-of-line 3))) + (beginning-of-line 2) + (unless (looking-at "Changed paths:") + (beginning-of-line 1)))) (defun svn-log-revision-at-point () (save-excursion + (end-of-line) (re-search-backward "^r\\([0-9]+\\)") (svn-match-string-no-properties 1))) +(defun svn-log-file-name-at-point (respect-checkout-prefix-path) + (let ((full-file-name) + (file-name) + (checkout-prefix-path (when respect-checkout-prefix-path (svn-status-checkout-prefix-path)))) + (save-excursion + (beginning-of-line) + (when (looking-at " [MA] /\\(.+\\)$") + (setq full-file-name (svn-match-string-no-properties 1)))) + (when (string= checkout-prefix-path "") + (setq checkout-prefix-path "/")) + (setq file-name + (if (eq (string-match (regexp-quote (substring checkout-prefix-path 1)) full-file-name) 0) + (substring full-file-name (- (length checkout-prefix-path) (if (string= checkout-prefix-path "/") 1 0))) + full-file-name)) + ;; (message "svn-log-file-name-at-point %s prefix: '%s', full-file-name: %s" file-name checkout-prefix-path full-file-name) + file-name)) + +(defun svn-log-find-file-at-point () + (interactive) + (let ((file-name (svn-log-file-name-at-point t))) + (when file-name + (let ((default-directory (svn-status-base-dir))) + ;;(message "svn-log-file-name-at-point: %s, default-directory: %s" file-name default-directory) + (find-file file-name))))) + +(defun svn-log-next-link () + "Jump to the next external link in this buffer" + (interactive) + (let ((start-pos (if (get-text-property (point) 'link-handler) + (next-single-property-change (point) 'link-handler) + (point)))) + (goto-char (or (next-single-property-change start-pos 'link-handler) (point))))) + +(defun svn-log-prev-link () + "Jump to the previous external link in this buffer" + (interactive) + (let ((start-pos (if (get-text-property (point) 'link-handler) + (previous-single-property-change (point) 'link-handler) + (point)))) + (goto-char (or (previous-single-property-change (or start-pos (point)) 'link-handler) (point))))) + (defun svn-log-view-diff (arg) "Show the changeset for a given log entry. When called with a prefix argument, ask the user for the revision." (interactive "P") - (let* ((upper-rev (svn-log-revision-at-point)) - (lower-rev (number-to-string (- (string-to-number upper-rev) 1))) - (rev-arg (concat lower-rev ":" upper-rev))) - (when arg - (setq rev-arg (read-string "Revision for changeset: " rev-arg))) - (svn-run nil t 'diff "diff" (concat "-r" rev-arg)) - (svn-status-activate-diff-mode))) + (svn-status-diff-show-changeset (svn-log-revision-at-point) arg)) + +(defun svn-log-get-specific-revision () + "Get an older revision of the file at point via svn cat." + (interactive) + ;; (message "%S" (svn-status-make-line-info (svn-log-file-name-at-point t))) + (let ((default-directory (svn-status-base-dir))) + (svn-status-get-specific-revision-internal + (list (svn-status-make-line-info (svn-log-file-name-at-point nil))) + (svn-log-revision-at-point)))) + +(defun svn-log-ediff-specific-revision () + "Call ediff for the file at point to view a changeset" + (interactive) + ;; (message "svn-log-ediff-specific-revision: %s" (svn-log-file-name-at-point t)) + (let* ((cur-buf (current-buffer)) + (upper-rev (svn-log-revision-at-point)) + (lower-rev (number-to-string (- (string-to-number upper-rev) 1))) + (file-name (svn-log-file-name-at-point t)) + (default-directory (svn-status-base-dir)) + (upper-rev-file-name (when file-name + (cdar (svn-status-get-specific-revision-internal + (list (svn-status-make-line-info file-name)) upper-rev)))) + (lower-rev-file-name (when file-name + (cdar (svn-status-get-specific-revision-internal + (list (svn-status-make-line-info file-name)) lower-rev))))) + ;;(message "%S %S" upper-rev-file-name lower-rev-file-name) + (if file-name + (let* ((ediff-after-quit-destination-buffer cur-buf) + (newer-buffer (find-file-noselect upper-rev-file-name)) + (base-buff (find-file-noselect lower-rev-file-name)) + (svn-transient-buffers (list base-buff newer-buffer)) + (startup-hook '(svn-ediff-startup-hook))) + (ediff-buffers base-buff newer-buffer startup-hook)) + (message "No file at point")))) (defun svn-log-edit-log-entry () "Edit the given log entry." @@ -3930,7 +5132,7 @@ When called with a prefix argument, ask the user for the revision." (log-message)) (svn-run nil t 'propget-parse "propget" "--revprop" (concat "-r" rev) "svn:log") (save-excursion - (set-buffer "*svn-process*") + (set-buffer svn-process-buffer-name) (setq log-message (if (> (point-max) 1) (buffer-substring (point-min) (- (point-max) 1)) ""))) @@ -3942,6 +5144,63 @@ When called with a prefix argument, ask the user for the revision." (goto-char (point-min)) (setq svn-log-edit-update-log-entry rev))) + +;; allow additional hyperlinks in log view buffers +(defvar svn-log-link-keymap () + "Keymap used to resolve links `svn-log-view-mode' buffers.") +(put 'svn-log-link-keymap 'risky-local-variable t) ;for Emacs 20.7 +(when (not svn-log-link-keymap) + (setq svn-log-link-keymap (make-sparse-keymap)) + (suppress-keymap svn-log-link-keymap) + (define-key svn-log-link-keymap [mouse-2] 'svn-log-resolve-mouse-link) + (define-key svn-log-link-keymap (kbd "RET") 'svn-log-resolve-link)) + +(defun svn-log-resolve-mouse-link (event) + (interactive "e") + (mouse-set-point event) + (svn-log-resolve-link)) + +(defun svn-log-resolve-link () + (interactive) + (let* ((point-adjustment (if (not (get-text-property (- (point) 1) 'link-handler)) 1 + (if (not (get-text-property (+ (point) 1) 'link-handler)) -1 0))) + (link-name (buffer-substring-no-properties (previous-single-property-change (+ (point) point-adjustment) 'link-handler) + (next-single-property-change (+ (point) point-adjustment) 'link-handler)))) + ;; (message "svn-log-resolve-link '%s'" link-name) + (funcall (get-text-property (point) 'link-handler) link-name))) + +(defun svn-log-register-link-handler (handler-id link-regexp handler-function) + "Register a link handler for external links in *svn-log* buffers +HANDLER-ID is a symbolic name for this handler. The link handler is active when HANDLER-ID +is registered in `svn-log-link-handlers'. +LINK-REGEXP specifies a regular expression that matches the external link. +HANDLER-FUNCTION is called with the match of LINK-REGEXP when the user clicks at the external link." + (let ((font-lock-desc (list link-regexp '(0 `(face font-lock-function-name-face + mouse-face highlight + link-handler invalid-handler-function + keymap ,svn-log-link-keymap))))) + ;; no idea, how to use handler-function in invalid-handler-function above, so set it here + (setcar (nthcdr 5 (nth 1 (nth 1 (nth 1 font-lock-desc)))) handler-function) + (svn-puthash handler-id font-lock-desc svn-log-registered-link-handlers))) + +;; example: add support for ditrack links and handle them via svn-log-resolve-ditrack +;;(svn-log-register-link-handler 'ditrack-issue "i#[0-9]+" 'svn-log-resolve-ditrack) +;;(defun svn-log-resolve-ditrack (link-name) +;; (interactive) +;; (message "svn-log-resolve-ditrack %s" link-name)) + + +(defun svn-log-resolve-trac-ticket-short (link-name) + "Show the trac ticket specified by LINK-NAME via `svn-trac-browse-ticket'." + (interactive) + (let ((ticket-nr (string-to-number (substring-no-properties link-name 1)))) + (svn-trac-browse-ticket ticket-nr))) + +;; register the out of the box provided link handlers +(svn-log-register-link-handler 'trac-ticket-short "#[0-9]+" 'svn-log-resolve-trac-ticket-short) + +;; the actually used link handlers are specified in svn-log-link-handlers + ;; -------------------------------------------------------------------------------- ;; svn-info-mode ;; -------------------------------------------------------------------------------- @@ -3950,6 +5209,11 @@ When called with a prefix argument, ask the user for the revision." (when (not svn-info-mode-map) (setq svn-info-mode-map (make-sparse-keymap)) + (define-key svn-info-mode-map [?s] 'svn-status-pop-to-status-buffer) + (define-key svn-info-mode-map (kbd "h") 'svn-status-pop-to-partner-buffer) + (define-key svn-info-mode-map (kbd "n") 'next-line) + (define-key svn-info-mode-map (kbd "p") 'previous-line) + (define-key svn-info-mode-map (kbd "RET") 'svn-info-show-context) (define-key svn-info-mode-map [?q] 'bury-buffer)) (defun svn-info-mode () @@ -3958,7 +5222,204 @@ When called with a prefix argument, ask the user for the revision." (kill-all-local-variables) (use-local-map svn-info-mode-map) (setq major-mode 'svn-info-mode) - (setq mode-name "svn-info")) + (setq mode-name "svn-info") + (toggle-read-only 1)) + +(defun svn-info-show-context () + "Show the context for a line in the info buffer. +Currently is the output from the svn update command known." + (interactive) + (cond ((save-excursion + (goto-char (point-max)) + (forward-line -1) + (beginning-of-line) + (looking-at "Updated to revision")) + ;; svn-info contains info from an svn update + (let ((cur-pos (point)) + (file-name (buffer-substring-no-properties + (progn (beginning-of-line) (re-search-forward ".. +") (point)) + (line-end-position))) + (pos)) + (goto-char cur-pos) + (with-current-buffer svn-status-buffer-name + (setq pos (svn-status-get-file-name-buffer-position file-name))) + (when pos + (svn-status-pop-to-new-partner-buffer svn-status-buffer-name) + (goto-char pos)))))) + +;; -------------------------------------------------------------------------------- +;; svn blame minor mode +;; -------------------------------------------------------------------------------- + +(unless (assq 'svn-blame-mode minor-mode-alist) + (setq minor-mode-alist + (cons (list 'svn-blame-mode " SvnBlame") + minor-mode-alist))) + +(defvar svn-blame-mode-map () "Keymap used in `svn-blame-mode' buffers.") +(put 'svn-blame-mode-map 'risky-local-variable t) ;for Emacs 20.7 + +(when (not svn-blame-mode-map) + (setq svn-blame-mode-map (make-sparse-keymap)) + (define-key svn-blame-mode-map [?s] 'svn-status-pop-to-status-buffer) + (define-key svn-blame-mode-map (kbd "n") 'next-line) + (define-key svn-blame-mode-map (kbd "p") 'previous-line) + (define-key svn-blame-mode-map (kbd "RET") 'svn-blame-open-source-file) + (define-key svn-blame-mode-map (kbd "a") 'svn-blame-highlight-author) + (define-key svn-blame-mode-map (kbd "r") 'svn-blame-highlight-revision) + (define-key svn-blame-mode-map (kbd "=") 'svn-blame-show-changeset) + (define-key svn-blame-mode-map (kbd "l") 'svn-blame-show-log) + (define-key svn-blame-mode-map [?q] 'bury-buffer)) + +(easy-menu-define svn-blame-mode-menu svn-blame-mode-map +"svn blame minor mode menu" + '("SvnBlame" + ["Jump to source location" svn-blame-open-source-file t] + ["Show changeset" svn-blame-show-changeset t] + ["Show log" svn-blame-show-log t] + ["Highlight by author" svn-blame-highlight-author t] + ["Highlight by revision" svn-blame-highlight-revision t])) + +(or (assq 'svn-blame-mode minor-mode-map-alist) + (setq minor-mode-map-alist + (cons (cons 'svn-blame-mode svn-blame-mode-map) minor-mode-map-alist))) + +(make-variable-buffer-local 'svn-blame-mode) + +(defun svn-blame-mode (&optional arg) + "Toggle svn blame minor mode. +With ARG, turn svn blame minor mode on if ARG is positive, off otherwise. + +Note: This mode does not yet work on XEmacs... +It is probably because the revisions are in 'before-string properties of overlays + +Key bindings: +\\{svn-blame-mode-map}" + (interactive "P") + (setq svn-blame-mode (if (null arg) + (not svn-blame-mode) + (> (prefix-numeric-value arg) 0))) + (if svn-blame-mode + (progn + (easy-menu-add svn-blame-mode-menu) + (toggle-read-only 1)) + (easy-menu-remove svn-blame-mode-menu)) + (force-mode-line-update)) + +(defun svn-status-activate-blame-mode () + "Activate the svn blame minor in the current buffer. +The current buffer must contain a valid output from svn blame" + (save-excursion + (goto-char (point-min)) + (let ((buffer-read-only nil) + (line (svn-line-number-at-pos)) + (limit (point-max)) + (info-end-col (save-excursion (forward-word 2) (+ (current-column) 1))) + (s) + ov) + ;; remove the old overlays (only for testing) + ;; (dolist (ov (overlays-in (point) limit)) + ;; (when (overlay-get ov 'svn-blame-line-info) + ;; (delete-overlay ov))) + (while (and (not (eobp)) (< (point) limit)) + (setq ov (make-overlay (point) (point))) + (overlay-put ov 'svn-blame-line-info t) + (setq s (buffer-substring-no-properties (svn-point-at-bol) (+ (svn-point-at-bol) info-end-col))) + (overlay-put ov 'before-string (propertize s 'face 'svn-status-blame-rev-number-face)) + (overlay-put ov 'rev-info (delete "" (split-string s " "))) + (delete-region (svn-point-at-bol) (+ (svn-point-at-bol) info-end-col)) + (forward-line) + (setq line (1+ line))))) + (let* ((buf-name (format "*svn-blame: %s*" (file-relative-name svn-status-blame-file-name))) + (buffer (get-buffer buf-name))) + (when buffer + (kill-buffer buffer)) + (rename-buffer buf-name)) + ;; use the correct mode for the displayed blame output + (let ((buffer-file-name svn-status-blame-file-name)) + (normal-mode) + (set (make-local-variable 'svn-status-blame-file-name) svn-status-blame-file-name)) + (font-lock-fontify-buffer) + (svn-blame-mode 1)) + +(defun svn-blame-open-source-file () + "Jump to the source file location for the current position in the svn blame buffer" + (interactive) + (let ((src-line-number (svn-line-number-at-pos)) + (src-line-col (current-column))) + (find-file-other-window svn-status-blame-file-name) + (goto-line src-line-number) + (forward-char src-line-col))) + +(defun svn-blame-rev-at-point () + (let ((rev)) + (dolist (ov (overlays-in (svn-point-at-bol) (line-end-position))) + (when (overlay-get ov 'svn-blame-line-info) + (setq rev (car (overlay-get ov 'rev-info))))) + rev)) + +(defun svn-blame-show-changeset (arg) + "Show a diff for the revision at point. +When called with a prefix argument, allow the user to edit the revision." + (interactive "P") + (svn-status-diff-show-changeset (svn-blame-rev-at-point) arg)) + +(defun svn-blame-show-log (arg) + "Show the log for the revision at point. +The output is put into the *svn-log* buffer +The optional prefix argument ARG determines which switches are passed to `svn log': + no prefix --- use whatever is in the list `svn-status-default-log-arguments' + prefix argument of -1: --- use the -q switch (quiet) + prefix argument of 0 --- use no arguments + other prefix arguments: --- use the -v switch (verbose)" + (interactive "P") + (let ((switches (svn-status-svn-log-switches arg)) + (rev (svn-blame-rev-at-point))) + (svn-run t t 'log "log" "--revision" rev switches))) + +(defun svn-blame-highlight-line-maybe (compare-func) + (let ((reference-value) + (is-highlighted) + (consider-this-line) + (hl-ov)) + (dolist (ov (overlays-in (svn-point-at-bol) (line-end-position))) + (when (overlay-get ov 'svn-blame-line-info) + (setq reference-value (funcall compare-func ov))) + (when (overlay-get ov 'svn-blame-highlighted) + (setq is-highlighted t))) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (setq consider-this-line nil) + (dolist (ov (overlays-in (svn-point-at-bol) (line-end-position))) + (when (overlay-get ov 'svn-blame-line-info) + (when (string= reference-value (funcall compare-func ov)) + (setq consider-this-line t)))) + (when consider-this-line + (dolist (ov (overlays-in (svn-point-at-bol) (line-end-position))) + (when (and (overlay-get ov 'svn-blame-highlighted) is-highlighted) + (delete-overlay ov)) + (unless is-highlighted + (setq hl-ov (make-overlay (svn-point-at-bol) (line-end-position))) + (overlay-put hl-ov 'svn-blame-highlighted t) + (overlay-put hl-ov 'face 'svn-status-blame-highlight-face)))) + (forward-line))))) + +(defun svn-blame-highlight-author-field (ov) + (cadr (overlay-get ov 'rev-info))) + +(defun svn-blame-highlight-author () + "(Un)Highlight all lines with the same author." + (interactive) + (svn-blame-highlight-line-maybe 'svn-blame-highlight-author-field)) + +(defun svn-blame-highlight-revision-field (ov) + (car (overlay-get ov 'rev-info))) + +(defun svn-blame-highlight-revision () + "(Un)Highlight all lines with the same revision." + (interactive) + (svn-blame-highlight-line-maybe 'svn-blame-highlight-revision-field)) ;; -------------------------------------------------------------------------------- ;; svn-process-mode @@ -3968,13 +5429,26 @@ When called with a prefix argument, ask the user for the revision." (when (not svn-process-mode-map) (setq svn-process-mode-map (make-sparse-keymap)) + (define-key svn-process-mode-map (kbd "RET") 'svn-process-send-string-and-newline) + (define-key svn-process-mode-map [?s] 'svn-process-send-string) (define-key svn-process-mode-map [?q] 'bury-buffer)) +(easy-menu-define svn-process-mode-menu svn-process-mode-map +"'svn-process-mode' menu" + '("SvnProcess" + ["Send line to process" svn-process-send-string-and-newline t] + ["Send raw string to process" svn-process-send-string t] + ["Bury process buffer" bury-buffer t])) + (defun svn-process-mode () - "Major Mode to view process output from svn." + "Major Mode to view process output from svn. + +You can send a new line terminated string to the process via \\[svn-process-send-string-and-newline] +You can send raw data to the process via \\[svn-process-send-string]." (interactive) (kill-all-local-variables) (use-local-map svn-process-mode-map) + (easy-menu-add svn-log-view-mode-menu) (setq major-mode 'svn-process-mode) (setq mode-name "svn-process")) @@ -3982,9 +5456,64 @@ When called with a prefix argument, ask the user for the revision." ;; svn status persistent options ;; -------------------------------------------------------------------------------- +(defun svn-status-repo-for-path (directory) + "Find the repository root for DIRECTORY." + (let ((old-process-default-dir)) + (with-current-buffer (get-buffer-create svn-process-buffer-name) + (setq old-process-default-dir default-directory) + (setq default-directory directory)) ;; update the default-directory for the *svn-process* buffer + (svn-run nil t 'parse-info "info" ".") + (with-current-buffer svn-process-buffer-name + ;; (message "svn-status-repo-for-path: %s: default-directory: %s directory: %s old-process-default-dir: %s" svn-process-buffer-name default-directory directory old-process-default-dir) + (setq default-directory old-process-default-dir) + (goto-char (point-min)) + (let ((case-fold-search t)) + (if (search-forward "repository root: " nil t) + (buffer-substring-no-properties (point) (svn-point-at-eol)) + (when (search-forward "repository uuid: " nil t) + (message "psvn.el: Detected an old svn working copy in '%s'. Please check it out again to get a 'Repository Root' entry in the svn info output." + default-directory) + (concat "Svn Repo UUID: " (buffer-substring-no-properties (point) (svn-point-at-eol))))))))) + (defun svn-status-base-dir (&optional start-directory) "Find the svn root directory for the current working copy. Return nil, if not in a svn working copy." + (let* ((start-dir (expand-file-name (or start-directory default-directory))) + (base-dir (gethash start-dir svn-status-base-dir-cache 'not-found))) + ;;(message "svn-status-base-dir: %S %S" start-dir base-dir) + (if (not (eq base-dir 'not-found)) + base-dir + ;; (message "calculating base-dir for %s" start-dir) + (unless svn-client-version + (svn-status-version)) + (let* ((base-dir start-dir) + (repository-root (svn-status-repo-for-path base-dir)) + (dot-svn-dir (concat base-dir (svn-wc-adm-dir-name))) + (in-tree (and repository-root (file-exists-p dot-svn-dir))) + (dir-below (expand-file-name base-dir))) + ;; (message "repository-root: %s start-dir: %s" repository-root start-dir) + (if (and (<= (car svn-client-version) 1) (< (cadr svn-client-version) 3)) + (setq base-dir (svn-status-base-dir-for-ancient-svn-client start-dir)) ;; svn version < 1.3 + (while (when (and dir-below (file-exists-p dot-svn-dir)) + (setq base-dir (file-name-directory dot-svn-dir)) + (string-match "\\(.+/\\).+/" dir-below) + (setq dir-below + (and (string-match "\\(.*/\\)[^/]+/" dir-below) + (match-string 1 dir-below))) + ;; (message "base-dir: %s, dir-below: %s, dot-svn-dir: %s in-tree: %s" base-dir dir-below dot-svn-dir in-tree) + (when dir-below + (if (string= (svn-status-repo-for-path dir-below) repository-root) + (setq dot-svn-dir (concat dir-below (svn-wc-adm-dir-name))) + (setq dir-below nil))))) + (setq base-dir (and in-tree base-dir))) + (svn-puthash start-dir base-dir svn-status-base-dir-cache) + (svn-status-message 7 "svn-status-base-dir %s => %s" start-dir base-dir) + base-dir)))) + +(defun svn-status-base-dir-for-ancient-svn-client (&optional start-directory) + "Find the svn root directory for the current working copy. +Return nil, if not in a svn working copy. +This function is used for svn clients version 1.2 and below." (let* ((base-dir (expand-file-name (or start-directory default-directory))) (dot-svn-dir (concat base-dir (svn-wc-adm-dir-name))) (in-tree (file-exists-p dot-svn-dir)) @@ -3993,7 +5522,7 @@ Return nil, if not in a svn working copy." (setq base-dir (file-name-directory dot-svn-dir)) (string-match "\\(.+/\\).+/" dir-below) (setq dir-below - (and (string-match "\(.*/\)[^/]+/" dir-below) + (and (string-match "\\(.*/\\)[^/]+/" dir-below) (match-string 1 dir-below))) (setq dot-svn-dir (concat dir-below (svn-wc-adm-dir-name))))) (and in-tree base-dir))) @@ -4003,12 +5532,16 @@ Return nil, if not in a svn working copy." (interactive) (let ((buf (find-file (concat (svn-status-base-dir) "++psvn.state")))) (erase-buffer) ;Widen, because we'll save the whole buffer. + ;; TO CHECK: why is svn-status-options a global variable?? (setq svn-status-options (list (list "svn-trac-project-root" svn-trac-project-root) (list "sort-status-buffer" svn-status-sort-status-buffer) (list "elide-list" svn-status-elided-list) - (list "module-name" svn-status-module-name))) + (list "module-name" svn-status-module-name) + (list "branch-list" svn-status-branch-list) + (list "changelog-style" svn-status-changelog-style) + )) (insert (pp-to-string svn-status-options)) (save-buffer) (kill-buffer buf))) @@ -4029,14 +5562,24 @@ Return nil, if not in a svn working copy." (nth 1 (assoc "elide-list" svn-status-options))) (setq svn-status-module-name (nth 1 (assoc "module-name" svn-status-options))) + (setq svn-status-branch-list + (nth 1 (assoc "branch-list" svn-status-options))) + (setq svn-status-changelog-style + (nth 1 (assoc "changelog-style" svn-status-options))) (when (and (interactive-p) svn-status-elided-list (svn-status-apply-elide-list))) (message "psvn.el: loaded %s" file)) - (unless no-error (error "psvn.el: %s is not readable." file))))) + (if no-error + (setq svn-trac-project-root nil + svn-status-elided-list nil + svn-status-module-name nil + svn-status-branch-list nil + svn-status-changelog-style 'changelog) + (error "psvn.el: %s is not readable." file))))) (defun svn-status-toggle-sort-status-buffer () "Toggle sorting of the *svn-status* buffer. -If you turn off sorting, you can speed up \[svn-status]. However, +If you turn off sorting, you can speed up \\[svn-status]. However, the buffer is not correctly sorted then. This function will be removed again, when a faster parsing and display routine for `svn-status' is available." @@ -4045,13 +5588,19 @@ removed again, when a faster parsing and display routine for (message "The %s buffer will %sbe sorted." svn-status-buffer-name (if svn-status-sort-status-buffer "" "not "))) +(defun svn-status-toggle-svn-verbose-flag () + "Toggle `svn-status-verbose'. " + (interactive) + (setq svn-status-verbose (not svn-status-verbose)) + (message "svn status calls will %suse the -v flag." (if svn-status-verbose "" "not "))) + (defun svn-status-toggle-display-full-path () "Toggle displaying the full path in the `svn-status-buffer-name' buffer" (interactive) (setq svn-status-display-full-path (not svn-status-display-full-path)) (message "The %s buffer will%s use full path names." svn-status-buffer-name (if svn-status-display-full-path "" " not")) - (svn-status-update)) + (svn-status-update-buffer)) (defun svn-status-set-trac-project-root () (interactive) @@ -4062,7 +5611,7 @@ removed again, when a faster parsing and display routine for (svn-status-save-state))) (defun svn-status-set-module-name () - "Interactively set svn-status-module-name." + "Interactively set `svn-status-module-name'." (interactive) (setq svn-status-module-name (read-string "Short Unit Name (e.g.: MyProject): " @@ -4070,6 +5619,25 @@ removed again, when a faster parsing and display routine for (when (yes-or-no-p "Save the new setting for svn-status-module-name to disk? ") (svn-status-save-state))) +(defun svn-status-set-changelog-style () + "Interactively set `svn-status-changelog-style'." + (interactive) + (setq svn-status-changelog-style + (intern (funcall svn-status-completing-read-function "svn-status on directory: " '("changelog" "svn-dev" "other")))) + (when (string= svn-status-changelog-style 'other) + (setq svn-status-changelog-style (car (find-function-read)))) + (when (yes-or-no-p "Save the new setting for svn-status-changelog-style to disk? ") + (svn-status-save-state))) + +(defun svn-status-set-branch-list () + "Interactively set `svn-status-branch-list'." + (interactive) + (setq svn-status-branch-list + (split-string (read-string "Branch list: " + (mapconcat 'identity svn-status-branch-list " ")))) + (when (yes-or-no-p "Save the new setting for svn-status-branch-list to disk? ") + (svn-status-save-state))) + (defun svn-browse-url (url) "Call `browse-url', using `svn-browse-url-function'." (let ((browse-url-browser-function (or svn-browse-url-function @@ -4079,6 +5647,13 @@ removed again, when a faster parsing and display routine for ;; -------------------------------------------------------------------------------- ;; svn status trac integration ;; -------------------------------------------------------------------------------- +(defun svn-trac-browse-wiki () + "Open the trac wiki view for the current svn repository." + (interactive) + (unless svn-trac-project-root + (svn-status-set-trac-project-root)) + (svn-browse-url (concat svn-trac-project-root "wiki"))) + (defun svn-trac-browse-timeline () "Open the trac timeline view for the current svn repository." (interactive) @@ -4086,6 +5661,28 @@ removed again, when a faster parsing and display routine for (svn-status-set-trac-project-root)) (svn-browse-url (concat svn-trac-project-root "timeline"))) +(defun svn-trac-browse-roadmap () + "Open the trac roadmap view for the current svn repository." + (interactive) + (unless svn-trac-project-root + (svn-status-set-trac-project-root)) + (svn-browse-url (concat svn-trac-project-root "roadmap"))) + +(defun svn-trac-browse-source () + "Open the trac source browser for the current svn repository." + (interactive) + (unless svn-trac-project-root + (svn-status-set-trac-project-root)) + (svn-browse-url (concat svn-trac-project-root "browser"))) + +(defun svn-trac-browse-report (arg) + "Open the trac report view for the current svn repository. +When called with a prefix argument, display the given report number." + (interactive "P") + (unless svn-trac-project-root + (svn-status-set-trac-project-root)) + (svn-browse-url (concat svn-trac-project-root "report" (if (numberp arg) (format "/%s" arg) "")))) + (defun svn-trac-browse-changeset (changeset-nr) "Show a changeset in the trac issue tracker." (interactive (list (read-number "Browse changeset number: " (number-at-point)))) @@ -4121,14 +5718,14 @@ The conflicts must be marked with rcsmerge conflict markers." (erase-buffer) (insert-buffer-substring result-buffer) (goto-char (point-min)) - (while (re-search-forward "^<<<<<<< .mine\n" nil t) + (while (re-search-forward "^<<<<<<< .\\(mine\\|working\\)\n" nil t) (setq found t) (replace-match "") (if (not (re-search-forward "^=======\n" nil t)) (error "Malformed conflict marker")) (replace-match "") (let ((start (point))) - (if (not (re-search-forward "^>>>>>>> .r[0-9]+\n" nil t)) + (if (not (re-search-forward "^>>>>>>> .\\(r[0-9]+\\|merge.*\\)\n" nil t)) (error "Malformed conflict marker")) (delete-region start (point)))) (if (not found) @@ -4140,12 +5737,12 @@ The conflicts must be marked with rcsmerge conflict markers." (erase-buffer) (insert-buffer-substring result-buffer) (goto-char (point-min)) - (while (re-search-forward "^<<<<<<< .mine\n" nil t) + (while (re-search-forward "^<<<<<<< .\\(mine\\|working\\)\n" nil t) (let ((start (match-beginning 0))) (if (not (re-search-forward "^=======\n" nil t)) (error "Malformed conflict marker")) (delete-region start (point)) - (if (not (re-search-forward "^>>>>>>> .r[0-9]+\n" nil t)) + (if (not (re-search-forward "^>>>>>>> .\\(r[0-9]+\\|merge.*\\)\n" nil t)) (error "Malformed conflict marker")) (replace-match ""))) (let ((config (current-window-configuration)) @@ -4199,6 +5796,86 @@ The conflicts must be marked with rcsmerge conflict markers." (svn-status-line-info->full-path file-info))) (error "can not resolve conflicts at this point")))) + +;; -------------------------------------------------------------------------------- +;; Working with branches +;; -------------------------------------------------------------------------------- + +(defun svn-branch-select (&optional prompt) + "Select a branch interactively from `svn-status-branch-list'" + (interactive) + (unless prompt + (setq prompt "Select branch: ")) + (let* ((branch (funcall svn-status-completing-read-function prompt svn-status-branch-list)) + (directory) + (base-url)) + (when (string-match "#\\(1#\\)?\\(.+\\)" branch) + (setq directory (match-string 2 branch)) + (setq base-url (concat (svn-status-base-info->repository-root) "/" directory)) + (save-match-data + (svn-status-parse-info t)) + (if (eq (length (match-string 1 branch)) 0) + (setq branch base-url) + (let ((svn-status-branch-list (svn-status-ls base-url t))) + (setq branch (concat (svn-status-base-info->repository-root) "/" + directory "/" + (svn-branch-select (format "Select branch from '%s': " directory))))))) + branch)) + +(defun svn-branch-diff (branch1 branch2) + "Show the diff between two svn repository urls. +When called interactively, use `svn-branch-select' to choose two branches from `svn-status-branch-list'." + (interactive + (let* ((branch1 (svn-branch-select "svn diff branch1: ")) + (branch2 (svn-branch-select (format "svn diff %s against: " branch1)))) + (list branch1 branch2))) + (svn-run t t 'diff "diff" svn-status-default-diff-arguments branch1 branch2)) + +;; -------------------------------------------------------------------------------- +;; svnadmin interface +;; -------------------------------------------------------------------------------- +(defun svn-admin-create (dir) + "Run svnadmin create DIR." + (interactive (list (expand-file-name + (svn-read-directory-name "Create a svn repository at: " + svn-admin-default-create-directory nil nil)))) + (shell-command-to-string (concat "svnadmin create " dir)) + (setq svn-admin-last-repository-dir (concat "file://" dir)) + (message "Svn repository created at %s" dir) + (run-hooks 'svn-admin-create-hook)) + +;; - Import an empty directory +;; cd to an empty directory +;; svn import -m "Initial import" . file:///home/stefan/svn_repos/WaldiConfig/trunk +(defun svn-admin-create-trunk-directory () + "Import an empty trunk directory to `svn-admin-last-repository-dir'. +Set `svn-admin-last-repository-dir' to the new created trunk url." + (interactive) + (let ((empty-temp-dir-name (make-temp-name svn-status-temp-dir))) + (make-directory empty-temp-dir-name t) + (setq svn-admin-last-repository-dir (concat svn-admin-last-repository-dir "/trunk")) + (svn-run nil t 'import "import" "-m" "Created trunk directory" + empty-temp-dir-name svn-admin-last-repository-dir) + (delete-directory empty-temp-dir-name))) + +(defun svn-admin-start-import () + "Start to import the current working directory in a subversion repository. +The user is asked to perform the following two steps: +1. Create a local repository +2. Add a trunk directory to that repository + +After that step the empty base directory (either the root directory or +the trunk directory of the selected repository) is checked out in the current +working directory." + (interactive) + (if (y-or-n-p "Create local repository? ") + (progn + (call-interactively 'svn-admin-create) + (when (y-or-n-p "Add a trunk directory? ") + (svn-admin-create-trunk-directory))) + (setq svn-admin-last-repository-dir (read-string "Repository Url: "))) + (svn-checkout svn-admin-last-repository-dir ".")) + ;; -------------------------------------------------------------------------------- ;; svn status profiling ;; -------------------------------------------------------------------------------- @@ -4216,6 +5893,80 @@ The conflicts must be marked with rcsmerge conflict markers." (elp-instrument-package "svn-") (message "Run the desired svn command (e.g. M-x svn-status), then use M-x elp-results.")) +(defun svn-status-last-commands (&optional string-prefix) + "Return a string with the last executed svn commands" + (interactive) + (unless string-prefix + (setq string-prefix "")) + (with-output-to-string + (dolist (e (ring-elements svn-last-cmd-ring)) + (princ (format "%s%s: svn %s <%s>\n" string-prefix (nth 0 e) (mapconcat 'concat (nth 1 e) " ") (nth 2 e)))))) + +;; -------------------------------------------------------------------------------- +;; reporting bugs +;; -------------------------------------------------------------------------------- +(defun svn-insert-indented-lines (text) + "Helper function to insert TEXT, indented by two characters." + (dolist (line (split-string text "\n")) + (insert (format " %s\n" line)))) + +(defun svn-prepare-bug-report () + "Create the buffer *psvn-bug-report*. This buffer can be useful to debug problems with psvn.el" + (interactive) + (let* ((last-output-buffer-name (or svn-status-last-output-buffer-name svn-process-buffer-name)) + (last-svn-cmd-output (with-current-buffer last-output-buffer-name + (buffer-substring-no-properties (point-min) (point-max))))) + (switch-to-buffer "*psvn-bug-report*") + (delete-region (point-min) (point-max)) + (insert "This buffer holds some debug informations for psvn.el\n") + (insert "Please enter a description of the observed and the wanted behaviour\n") + (insert "and send it to the author (stefan@xsteve.at) to allow easier debugging\n\n") + (insert "Revisions:\n") + (svn-insert-indented-lines (svn-status-version)) + (insert "Language environment:\n") + (dolist (elem (svn-process-environment)) + (when (member (car (split-string elem "=")) '("LC_MESSAGES" "LC_ALL" "LANG")) + (insert (format " %s\n" elem)))) + (insert "\nLast svn commands:\n") + (svn-insert-indented-lines (svn-status-last-commands)) + (insert (format "\nContent of the <%s> buffer:\n" last-output-buffer-name)) + (svn-insert-indented-lines last-svn-cmd-output) + (goto-char (point-min)))) + +;; -------------------------------------------------------------------------------- +;; Make it easier to reload psvn, if a distribution has an older version +;; Just add the following to your .emacs: +;; (svn-prepare-for-reload) +;; (load "/path/to/psvn.el") + +;; Note the above will only work, if the loaded psvn.el has already the +;; function svn-prepare-for-reload +;; If this is not the case, do the following: +;; (load "/path/to/psvn.el");;make svn-prepare-for-reload available +;; (svn-prepare-for-reload) +;; (load "/path/to/psvn.el");; update the keybindings +;; -------------------------------------------------------------------------------- + +(defvar svn-prepare-for-reload-dont-touch-list '() "A list of variables that should not be touched by `svn-prepare-for-reload'") +(defvar svn-prepare-for-reload-variables-list '(svn-global-keymap svn-status-diff-mode-map svn-global-trac-map svn-status-mode-map + svn-status-mode-property-map svn-status-mode-extension-map + svn-status-mode-options-map svn-status-mode-trac-map svn-status-mode-branch-map + svn-log-edit-mode-map svn-log-view-mode-map + svn-log-view-popup-menu-map svn-info-mode-map svn-blame-mode-map svn-process-mode-map) + "A list of variables that should be set to nil via M-x `svn-prepare-for-reload'") +(defun svn-prepare-for-reload () + "This function resets some psvn.el variables to nil. +It makes reloading a newer version of psvn.el easier, if for example the used +GNU/Linux distribution uses an older version. + +The variables specified in `svn-prepare-for-reload-variables-list' will be reseted by this function. + +A variable will keep its value, if it is specified in `svn-prepare-for-reload-dont-touch-list'." + (interactive) + (dolist (var svn-prepare-for-reload-variables-list) + (unless (member var svn-prepare-for-reload-dont-touch-list) + (message (format "Resetting value of %s to nil" var))) + (set var nil))) (provide 'psvn) diff --git a/sources b/sources index 423dc83..f647e98 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -32786fe2f322982c0567346de18f6460 subversion-1.4.6.tar.gz +c40c1ebc1f228d8ea17dd0e7997a60c1 subversion-1.5.0.tar.gz diff --git a/subversion-1.0.3-pie.patch b/subversion-1.0.3-pie.patch deleted file mode 100644 index 4fea220..0000000 --- a/subversion-1.0.3-pie.patch +++ /dev/null @@ -1,72 +0,0 @@ - -Build subversion executables as PIEs. Requires a slight jig to -ensure that executables in the test suite are *not* built using --pie, since that doesn't work when main() is not itself an -object built with -pie. - ---- subversion-1.4.0/build.conf.pie -+++ subversion-1.4.0/build.conf -@@ -416,7 +416,7 @@ - lang = python - path = subversion/bindings/swig/python/libsvn_swig_py - libs = libsvn_client libsvn_subr libsvn_delta libsvn_wc aprutil apriconv apr --link-cmd = $(LINK) $(SWIG_PY_LIBS) -+link-cmd = $(LINK_LIB) $(SWIG_PY_LIBS) - install = swig-py-lib - # need special build rule to include -DSWIGPYTHON - compile-cmd = $(COMPILE_SWIG_PY) ---- subversion-1.4.0/Makefile.in.pie -+++ subversion-1.4.0/Makefile.in -@@ -139,9 +139,10 @@ - CPPFLAGS = @CPPFLAGS@ $(EXTRA_CPPFLAGS) - LDFLAGS = @LDFLAGS@ $(EXTRA_LDFLAGS) - --COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -+BASE_COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -+COMPILE = $(BASE_COMPILE) -fpie - COMPILE_CXX = $(CXX) $(CPPFLAGS) $(CFLAGS) $(CXXFLAGS) $(INCLUDES) --LT_COMPILE = $(LIBTOOL) $(LTFLAGS) --mode=compile $(COMPILE) -+LT_COMPILE = $(LIBTOOL) $(LTFLAGS) --mode=compile $(BASE_COMPILE) - - # special compilation for files destined for mod_dav_svn - COMPILE_APACHE_MOD = $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) $(APACHE_INCLUDES) $(INCLUDES) -o $@ -c -@@ -156,8 +157,11 @@ - COMPILE_JAVAHL_JAVAC = $(JAVAC) $(JAVAC_FLAGS) - COMPILE_JAVAHL_JAVAH = $(JAVAH) - --LINK = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) --LINK_LIB = $(LINK) -rpath $(libdir) -+BASE_LINK = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) -+LINK = $(BASE_LINK) -pie -+LINK_TEST = $(BASE_LINK) -no-install -+LINK_LIB = $(BASE_LINK) -rpath $(libdir) -+LINK_TEST_LIB = $(BASE_LINK) - - # special link rule for mod_dav_svn - LINK_APACHE_MOD = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) -rpath $(APACHE_LIBEXECDIR) -avoid-version -module $(APACHE_LDFLAGS) ---- subversion-1.4.0/build/generator/gen_base.py.pie -+++ subversion-1.4.0/build/generator/gen_base.py -@@ -411,6 +411,9 @@ - self.manpages = options.get('manpages', '') - self.testing = options.get('testing') - -+ if self.install == 'test' or self.install == 'bdb-test': -+ self.link_cmd = '$(LINK_TEST)' -+ - def add_dependencies(self): - TargetLinked.add_dependencies(self) - -@@ -453,8 +456,11 @@ - self.msvc_fake = options.get('msvc-fake') == 'yes' # has fake target - self.msvc_export = string.split(options.get('msvc-export', '')) - -- ### hmm. this is Makefile-specific -- self.link_cmd = '$(LINK_LIB)' -+ ### more Makefile-specific stuff: -+ if self.install == 'test': -+ self.link_cmd = '$(LINK_TEST_LIB)' -+ elif self.link_cmd == '$(LINK)': -+ self.link_cmd = '$(LINK_LIB)' - - class TargetApacheMod(TargetLib): - diff --git a/subversion-1.4.4-macropen.patch b/subversion-1.4.4-macropen.patch deleted file mode 100644 index 5802b02..0000000 --- a/subversion-1.4.4-macropen.patch +++ /dev/null @@ -1,184 +0,0 @@ ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/uuids-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/uuids-table.c -@@ -47,7 +47,7 @@ - BDB_ERR(db_create(&uuids, env, 0)); - BDB_ERR(uuids->set_re_len(uuids, APR_UUID_FORMATTED_LENGTH)); - -- error = uuids->open(SVN_BDB_OPEN_PARAMS(uuids, NULL), -+ error = (uuids->open)(SVN_BDB_OPEN_PARAMS(uuids, NULL), - "uuids", 0, DB_RECNO, - open_flags, 0666); - ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/strings-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/strings-table.c -@@ -44,10 +44,10 @@ - BDB_ERR(db_create(&strings, env, 0)); - - /* Enable duplicate keys. This allows the data to be spread out across -- multiple records. Note: this must occur before ->open(). */ -+ multiple records. Note: this must occur before (->open)(). */ - BDB_ERR(strings->set_flags(strings, DB_DUP)); - -- BDB_ERR(strings->open(SVN_BDB_OPEN_PARAMS(strings, NULL), -+ BDB_ERR((strings->open)(SVN_BDB_OPEN_PARAMS(strings, NULL), - "strings", 0, DB_BTREE, - open_flags, 0666)); - ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/changes-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/changes-table.c -@@ -51,10 +51,10 @@ - BDB_ERR(db_create(&changes, env, 0)); - - /* Enable duplicate keys. This allows us to store the changes -- one-per-row. Note: this must occur before ->open(). */ -+ one-per-row. Note: this must occur before (->open)(). */ - BDB_ERR(changes->set_flags(changes, DB_DUP)); - -- BDB_ERR(changes->open(SVN_BDB_OPEN_PARAMS(changes, NULL), -+ BDB_ERR((changes->open)(SVN_BDB_OPEN_PARAMS(changes, NULL), - "changes", 0, DB_BTREE, - open_flags, 0666)); - ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/lock-tokens-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/lock-tokens-table.c -@@ -43,7 +43,7 @@ - - BDB_ERR(svn_fs_bdb__check_version()); - BDB_ERR(db_create(&lock_tokens, env, 0)); -- error = lock_tokens->open(SVN_BDB_OPEN_PARAMS(lock_tokens, NULL), -+ error = (lock_tokens->open)(SVN_BDB_OPEN_PARAMS(lock_tokens, NULL), - "lock-tokens", 0, DB_BTREE, - open_flags, 0666); - ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/env.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/env.c -@@ -626,7 +626,7 @@ - flags |= DB_THREAD; - #endif - SVN_ERR(convert_bdb_error -- (bdb, bdb->env->open(bdb->env, bdb->path_bdb, flags, mode))); -+ (bdb, (bdb->env->open)(bdb->env, bdb->path_bdb, flags, mode))); - - #if SVN_BDB_AUTO_COMMIT - /* Assert the BDB_AUTO_COMMIT flag on the opened environment. This ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/copies-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/copies-table.c -@@ -43,7 +43,7 @@ - - BDB_ERR(svn_fs_bdb__check_version()); - BDB_ERR(db_create(&copies, env, 0)); -- BDB_ERR(copies->open(SVN_BDB_OPEN_PARAMS(copies, NULL), -+ BDB_ERR((copies->open)(SVN_BDB_OPEN_PARAMS(copies, NULL), - "copies", 0, DB_BTREE, - open_flags, 0666)); - ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/nodes-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/nodes-table.c -@@ -50,7 +50,7 @@ - - BDB_ERR(svn_fs_bdb__check_version()); - BDB_ERR(db_create(&nodes, env, 0)); -- BDB_ERR(nodes->open(SVN_BDB_OPEN_PARAMS(nodes, NULL), -+ BDB_ERR((nodes->open)(SVN_BDB_OPEN_PARAMS(nodes, NULL), - "nodes", 0, DB_BTREE, - open_flags, 0666)); - ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/locks-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/locks-table.c -@@ -44,7 +44,7 @@ - - BDB_ERR(svn_fs_bdb__check_version()); - BDB_ERR(db_create(&locks, env, 0)); -- error = locks->open(SVN_BDB_OPEN_PARAMS(locks, NULL), -+ error = (locks->open)(SVN_BDB_OPEN_PARAMS(locks, NULL), - "locks", 0, DB_BTREE, - open_flags, 0666); - ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/txn-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/txn-table.c -@@ -51,7 +51,7 @@ - - BDB_ERR(svn_fs_bdb__check_version()); - BDB_ERR(db_create(&txns, env, 0)); -- BDB_ERR(txns->open(SVN_BDB_OPEN_PARAMS(txns, NULL), -+ BDB_ERR((txns->open)(SVN_BDB_OPEN_PARAMS(txns, NULL), - "transactions", 0, DB_BTREE, - open_flags, 0666)); - ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/reps-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/reps-table.c -@@ -44,7 +44,7 @@ - - BDB_ERR(svn_fs_bdb__check_version()); - BDB_ERR(db_create(&reps, env, 0)); -- BDB_ERR(reps->open(SVN_BDB_OPEN_PARAMS(reps, NULL), -+ BDB_ERR((reps->open)(SVN_BDB_OPEN_PARAMS(reps, NULL), - "representations", 0, DB_BTREE, - open_flags, 0666)); - ---- subversion-1.4.4/subversion/libsvn_fs_base/bdb/rev-table.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs_base/bdb/rev-table.c -@@ -40,7 +40,7 @@ - - BDB_ERR(svn_fs_bdb__check_version()); - BDB_ERR(db_create(&revisions, env, 0)); -- BDB_ERR(revisions->open(SVN_BDB_OPEN_PARAMS(revisions, NULL), -+ BDB_ERR((revisions->open)(SVN_BDB_OPEN_PARAMS(revisions, NULL), - "revisions", 0, DB_RECNO, - open_flags, 0666)); - ---- subversion-1.4.4/subversion/libsvn_ra/ra_loader.c.macropen -+++ subversion-1.4.4/subversion/libsvn_ra/ra_loader.c -@@ -291,8 +291,8 @@ - session->pool = pool; - - /* Ask the library to open the session. */ -- SVN_ERR(vtable->open(session, repos_URL, callbacks, callback_baton, -- config, pool)); -+ SVN_ERR((vtable->open)(session, repos_URL, callbacks, callback_baton, -+ config, pool)); - - *session_p = session; - return SVN_NO_ERROR; ---- subversion-1.4.4/subversion/tests/libsvn_ra_local/ra-local-test.c.macropen -+++ subversion-1.4.4/subversion/tests/libsvn_ra_local/ra-local-test.c -@@ -95,7 +95,7 @@ - SVN_ERR(current_directory_url(&url, repos_name, pool)); - - /* Open an RA session into this repository. */ -- SVN_ERR((*plugin)->open(session, url, cbtable, NULL, NULL, pool)); -+ SVN_ERR(((*plugin)->open)(session, url, cbtable, NULL, NULL, pool)); - - return SVN_NO_ERROR; - } ---- subversion-1.4.4/subversion/libsvn_fs/fs-loader.c.macropen -+++ subversion-1.4.4/subversion/libsvn_fs/fs-loader.c -@@ -364,7 +364,7 @@ - - SVN_ERR(fs_library_vtable(&vtable, path, pool)); - *fs_p = svn_fs_new(fs_config, pool); -- SVN_ERR(vtable->open(*fs_p, path, pool)); -+ SVN_ERR((vtable->open)(*fs_p, path, pool)); - return serialized_init(*fs_p, pool); - } - -@@ -423,7 +423,7 @@ - fs_library_vtable_t *vtable; - - SVN_ERR(fs_library_vtable(&vtable, path, fs->pool)); -- SVN_ERR(vtable->open(fs, path, fs->pool)); -+ SVN_ERR((vtable->open)(fs, path, fs->pool)); - return serialized_init(fs, fs->pool); - } - ---- subversion-1.4.4/build/generator/swig/header_wrappers.py.macropen -+++ subversion-1.4.4/build/generator/swig/header_wrappers.py -@@ -103,7 +103,7 @@ - self.ofile.write( - "static svn_error_t *%s_invoke_%s(\n" % (struct[:-2], name) + - " %s *_obj, %s) {\n" % (struct, params) + -- " return _obj->%s(%s);\n" % (name, param_names) + -+ " return (_obj->%s)(%s);\n" % (name, param_names) + - "}\n\n") - - self.ofile.write("%}\n") diff --git a/subversion-1.4.4-swig1333.patch b/subversion-1.4.4-swig1333.patch deleted file mode 100644 index 022ad3a..0000000 --- a/subversion-1.4.4-swig1333.patch +++ /dev/null @@ -1,21 +0,0 @@ - -http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=453166 - -# For some reason, rubyhead.swg is included without being needed (at least -# for the current swig 1.3.33 version). This leads to a redefinition of -# _mSWIG. The documentation of SWIG only talks about including just -# the external_runtime file, not any additional includes. Also, the other -# bindings do not include anything from SWIG apart from the external -# runtime. So I think this patch should suffice. -# -# -- Torsten Landschoff - -Index: subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c -=================================================================== ---- subversion-1.4.4/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c.swig133 -+++ subversion-1.4.4/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c -@@ -1,4 +1,3 @@ --#include "rubyhead.swg" - #include "swig_ruby_external_runtime.swg" - #include "swigutil_rb.h" - #include diff --git a/subversion-1.5.0-perl510.patch b/subversion-1.5.0-perl510.patch new file mode 100644 index 0000000..54bc3c7 --- /dev/null +++ b/subversion-1.5.0-perl510.patch @@ -0,0 +1,15 @@ + +upstream r31546 + +--- subversion-1.5.0/subversion/bindings/swig/perl/native/t/6ra.t.perl510 ++++ subversion-1.5.0/subversion/bindings/swig/perl/native/t/6ra.t +@@ -221,8 +221,7 @@ sub add_file { + + sub apply_textdelta { + my ($self, $baton, $base_checksum, $pool) = @_; +- my $data = $baton->{data} = \''; +- open my $out_fh, '>', $data ++ open my $out_fh, '>', \$baton->{data} + or die "error opening in-memory file to store Subversion update: $!"; + open my $in_fh, '<', \'' + or die "error opening in-memory file for delta source: $!"; diff --git a/subversion-1.5.0-pie.patch b/subversion-1.5.0-pie.patch new file mode 100644 index 0000000..43a7eca --- /dev/null +++ b/subversion-1.5.0-pie.patch @@ -0,0 +1,84 @@ +--- subversion-1.5.0/build.conf.pie ++++ subversion-1.5.0/build.conf +@@ -452,7 +452,7 @@ type = swig_lib + lang = python + path = subversion/bindings/swig/python/libsvn_swig_py + libs = libsvn_client libsvn_wc libsvn_ra libsvn_delta libsvn_subr apriconv apr +-link-cmd = $(LINK) $(SWIG_PY_LIBS) ++link-cmd = $(LINK_LIB) $(SWIG_PY_LIBS) + install = swig-py-lib + # need special build rule to include -DSWIGPYTHON + compile-cmd = $(COMPILE_SWIG_PY) +@@ -475,7 +475,7 @@ type = swig_lib + lang = ruby + path = subversion/bindings/swig/ruby/libsvn_swig_ruby + libs = libsvn_client libsvn_wc libsvn_delta libsvn_subr apriconv apr +-link-cmd = $(LINK) $(SWIG_RB_LIBS) ++link-cmd = $(LINK_LIB) $(SWIG_RB_LIBS) + install = swig-rb-lib + # need special build rule to include + compile-cmd = $(COMPILE_SWIG_RB) +--- subversion-1.5.0/build/generator/gen_base.py.pie ++++ subversion-1.5.0/build/generator/gen_base.py +@@ -411,6 +411,9 @@ class TargetExe(TargetLinked): + self.manpages = options.get('manpages', '') + self.testing = options.get('testing') + ++ if self.install == 'test' or self.install == 'bdb-test': ++ self.link_cmd = '$(LINK_TEST)' ++ + def add_dependencies(self): + TargetLinked.add_dependencies(self) + +@@ -453,8 +456,11 @@ class TargetLib(TargetLinked): + self.msvc_fake = options.get('msvc-fake') == 'yes' # has fake target + self.msvc_export = string.split(options.get('msvc-export', '')) + +- ### hmm. this is Makefile-specific +- self.link_cmd = '$(LINK_LIB)' ++ ### more Makefile-specific stuff: ++ if self.install == 'test': ++ self.link_cmd = '$(LINK_TEST_LIB)' ++ elif self.link_cmd == '$(LINK)': ++ self.link_cmd = '$(LINK_LIB)' + + class TargetApacheMod(TargetLib): + +--- subversion-1.5.0/Makefile.in.pie ++++ subversion-1.5.0/Makefile.in +@@ -2,7 +2,7 @@ + # Makefile.in: template Makefile for Subversion + # + # ==================================================================== +-# Copyright (c) 2000-2006 CollabNet. All rights reserved. ++# Copyright (c) 2000-2006, 2008 CollabNet. All rights reserved. + # + # This software is licensed as described in the file COPYING, which + # you should have received as part of this distribution. The terms +@@ -150,9 +150,10 @@ CXXFLAGS = @CXXFLAGS@ $(EXTRA_CXXFLAGS) + CPPFLAGS = @CPPFLAGS@ $(EXTRA_CPPFLAGS) + LDFLAGS = @LDFLAGS@ $(EXTRA_LDFLAGS) + +-COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) ++BASE_COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) ++COMPILE = $(BASE_COMPILE) -fpie + COMPILE_CXX = $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) +-LT_COMPILE = $(LIBTOOL) $(LTFLAGS) --mode=compile $(COMPILE) ++LT_COMPILE = $(LIBTOOL) $(LTFLAGS) --mode=compile $(BASE_COMPILE) + + # special compilation for files destined for mod_dav_svn + COMPILE_APACHE_MOD = $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) $(APACHE_INCLUDES) $(INCLUDES) -o $@ -c +@@ -167,8 +168,11 @@ COMPILE_JAVAHL_CXX = $(LIBTOOL) $(LTCXXF + COMPILE_JAVAHL_JAVAC = $(JAVAC) $(JAVAC_FLAGS) + COMPILE_JAVAHL_JAVAH = $(JAVAH) + +-LINK = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) +-LINK_LIB = $(LINK) -rpath $(libdir) ++BASE_LINK = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) ++LINK = $(BASE_LINK) -pie ++LINK_TEST = $(BASE_LINK) -no-install ++LINK_LIB = $(BASE_LINK) -rpath $(libdir) ++LINK_TEST_LIB = $(BASE_LINK) + + # special link rule for mod_dav_svn + LINK_APACHE_MOD = $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) -rpath $(APACHE_LIBEXECDIR) -avoid-version -module $(APACHE_LDFLAGS) diff --git a/subversion.spec b/subversion.spec index 169ef11..a87f2d9 100644 --- a/subversion.spec +++ b/subversion.spec @@ -14,8 +14,8 @@ Summary: Modern Version Control System designed to replace CVS Name: subversion -Version: 1.4.6 -Release: 2%{?dist} +Version: 1.5.0 +Release: 8.1%{?dist} License: ASL 1.1 Group: Development/Tools URL: http://subversion.tigris.org/ @@ -25,10 +25,9 @@ Source3: filter-requires.sh Source4: http://www.xsteve.at/prg/emacs/psvn.el Patch2: subversion-0.20.1-deplibs.patch Patch3: subversion-0.31.0-rpath.patch -Patch6: subversion-1.0.3-pie.patch +Patch6: subversion-1.5.0-pie.patch Patch7: subversion-1.1.3-java.patch -Patch8: subversion-1.4.4-macropen.patch -Patch9: subversion-1.4.4-swig1333.patch +Patch8: subversion-1.5.0-perl510.patch BuildRequires: autoconf, libtool, python, python-devel, texinfo, which BuildRequires: db4-devel >= 4.1.25, swig >= 1.3.24, gettext BuildRequires: apr-devel >= 0.9.7, apr-util-devel >= 0.9.7 @@ -74,7 +73,7 @@ using HTTP, via the Apache httpd server. Group: Development/Libraries Summary: Perl bindings to the Subversion libraries BuildRequires: perl-devel >= 2:5.8.0, perl(ExtUtils::MakeMaker) -BuildRequires: perl(Test::More) +BuildRequires: perl(Test::More), perl(ExtUtils::Embed) Requires: %(eval `perl -V:version`; echo "perl(:MODULE_COMPAT_$version)") Requires: subversion = %{version}-%{release} @@ -110,11 +109,11 @@ This package includes the Ruby bindings to the Subversion libraries. %if %{with_java} %patch7 -p1 -b .java %endif -%patch8 -p1 -b .macropen -%patch9 -p1 -b .swig1333 +%patch8 -p1 -b .perl510 %build -./autogen.sh +# Regenerate after patches to build.conf et al +autoheader && autoconf && ./gen-make.py --reload # fix shebang lines, #111498 perl -pi -e 's|/usr/bin/env perl -w|/usr/bin/perl -w|' tools/hook-scripts/*.pl.in @@ -125,19 +124,17 @@ export svn_cv_ruby_sitedir_libsuffix="" export svn_cv_ruby_sitedir_archsuffix="" export CC=gcc CXX=g++ JAVA_HOME=%{jdk_path} -export CPPFLAGS="-DSVN_NEON_0_26 -DSVN_NEON_0_25" %configure --with-apr=%{_prefix} --with-apr-util=%{_prefix} \ --with-swig --with-neon=%{_prefix} \ --with-ruby-sitedir=%{ruby_sitearch} \ --with-apxs=%{_sbindir}/apxs --disable-mod-activation \ - --disable-static --disable-neon-version-check -# 1.3.0 tarball ships with generated swig sources -make extraclean-swig-headers + --disable-static make %{?_smp_mflags} all make swig-py swig-py-lib %{swigdirs} make swig-pl swig-pl-lib swig-rb swig-rb-lib %if %{with_java} -make %{?_smp_mflags} javahl +# javahl does not parallel-make +make javahl %endif %install @@ -145,7 +142,7 @@ rm -rf ${RPM_BUILD_ROOT} make install install-swig-py install-swig-pl-lib install-swig-rb \ DESTDIR=$RPM_BUILD_ROOT %{swigdirs} %if %{with_java} -make install-javahl DESTDIR=$RPM_BUILD_ROOT +make install-javahl-java install-javahl-lib javahl_javadir=%{_javadir} DESTDIR=$RPM_BUILD_ROOT %endif make pure_vendor_install -C subversion/bindings/swig/perl/native \ @@ -227,7 +224,9 @@ rm -rf ${RPM_BUILD_ROOT} %defattr(-,root,root) %doc BUGS COMMITTERS COPYING HACKING INSTALL README CHANGES %doc tools subversion/LICENSE mod_authz_svn-INSTALL -%doc contrib/client-side/svn_load_dirs{.pl,_*,.README} +%doc contrib/client-side/svn_load_dirs/{*.pl,*.example,*.README} +%doc contrib/client-side/svnmerge/*.{README,py} +%doc contrib/client-side/wcgrep %{_bindir}/* %{_libdir}/libsvn_*.so.* %{_mandir}/man*/* @@ -272,16 +271,65 @@ rm -rf ${RPM_BUILD_ROOT} %files javahl %defattr(-,root,root,-) %{_libdir}/libsvnjavahl-1.* -%{_libdir}/svn-javahl +%{_javadir}/svn-javahl.jar %endif %changelog -* Wed Feb 13 2008 Joe Orton 1.4.6-2.fc8 -- fix build of swig bindings +* Thu Jul 17 2008 Joe Orton 1.5.0-8.1 +- rebuild for F-8 -* Thu Jan 31 2008 Joe Orton 1.4.6-1.fc8 +* Thu Jul 3 2008 Joe Orton 1.5.0-8 +- require suitable APR + +* Thu Jul 3 2008 Joe Orton 1.5.0-7 +- add svnmerge and wcgrep to docdir (Edward Rudd, #451932) +- drop neon version overrides + +* Wed Jul 2 2008 Joe Orton 1.5.0-6 +- build with OpenJDK + +* Wed Jul 2 2008 Joe Orton 1.5.0-5 +- fix files list + +* Wed Jul 2 2008 Joe Orton 1.5.0-4 +- swig-perl test suite fix for Perl 5.10 (upstream r31546) + +* Tue Jul 1 2008 Joe Orton 1.5.0-3 +- attempt build without java bits + +* Thu Jun 26 2008 Joe Orton 1.5.0-2 +- update to 1.5.0 + +* Mon Mar 3 2008 Tom "spot" Callaway 1.4.6-7 +- tests are randomly failing, unrelated to new perl, disabled tests + +* Mon Mar 3 2008 Tom "spot" Callaway 1.4.6-6 +- rebuild for new perl (again) + +* Thu Feb 21 2008 Lubomir Kundrak 1.4.6-5 +- Correct install location of java stuff (#433295) + +* Wed Feb 6 2008 Tom "spot" Callaway 1.4.6-4 +- BR perl(ExtUtils::Embed) + +* Tue Feb 5 2008 Tom "spot" Callaway 1.4.6-3 +- rebuild for new perl + +* Fri Dec 21 2007 Joe Orton 1.4.6-2 - update to 1.4.6 +* Mon Dec 10 2007 Warren Togami 1.4.4-11 +- temporarily disable test suite + +* Thu Dec 6 2007 Joe Orton 1.4.4-10 +- fix build with swig 1.3.33 (patch by Torsten Landschoff) + +* Wed Dec 5 2007 Joe Orton 1.4.4-9 +- rebuild for OpenLDAP soname bump + +* Tue Sep 4 2007 Joe Orton 1.4.4-8 +- update to psvn.el r26383 from upstream + * Sun Sep 2 2007 Joe Orton 1.4.4-7 - rebuild for fixed 32-bit APR diff --git a/upstream b/upstream index 67f48df..e89fa06 100644 --- a/upstream +++ b/upstream @@ -1 +1 @@ -subversion-1.4.4.tar.gz +subversion-1.5.0.tar.gz