summaryrefslogtreecommitdiffstats
path: root/emacs.d/lisp/magit/magit-svn.el
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--emacs.d/lisp/magit/magit-svn.el240
1 files changed, 240 insertions, 0 deletions
diff --git a/emacs.d/lisp/magit/magit-svn.el b/emacs.d/lisp/magit/magit-svn.el
new file mode 100644
index 0000000..d08c048
--- /dev/null
+++ b/emacs.d/lisp/magit/magit-svn.el
@@ -0,0 +1,240 @@
+;;; magit-svn.el --- git-svn plug-in for Magit
+
+;; Copyright (C) 2008 Alex Ott
+;; Copyright (C) 2009 Alexey Voinov
+;; Copyright (C) 2009 John Wiegley
+;; Copyright (C) 2008 Linh Dang
+;; Copyright (C) 2008 Marcin Bachry
+;; Copyright (C) 2008, 2009 Marius Vollmer
+;; Copyright (C) 2010 Yann Hodique
+;;
+;; Magit is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; Magit is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+;; License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with Magit. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This plug-in provides git-svn functionality as a separate component of Magit
+
+;;; Code:
+
+(require 'magit)
+(eval-when-compile
+ (require 'cl))
+
+;; git svn commands
+
+(defun magit-svn-find-rev (rev &optional branch)
+ (interactive
+ (list (read-string "SVN revision: ")
+ (if current-prefix-arg
+ (read-string "In branch: "))))
+ (let* ((sha (apply 'magit-git-string
+ `("svn"
+ "find-rev"
+ ,(concat "r" rev)
+ ,@(when branch (list branch))))))
+ (if sha
+ (magit-show-commit
+ (magit-with-section sha 'commit
+ (magit-set-section-info sha)
+ sha))
+ (error "Revision %s could not be mapped to a commit" rev))))
+
+(defun magit-svn-create-branch (name)
+ (interactive "sBranch name: ")
+ (magit-run-git "svn" "branch" name))
+
+(defun magit-svn-rebase ()
+ (interactive)
+ (magit-run-git-async "svn" "rebase"))
+
+(defun magit-svn-dcommit ()
+ (interactive)
+ (magit-run-git-async "svn" "dcommit"))
+
+(defun magit-svn-enabled ()
+ (not (null (magit-svn-get-ref-info t))))
+
+(defun magit-svn-expand-braces-in-branches (branch)
+ (if (not (string-match "\\(.+\\){\\(.+,.+\\)}\\(.*\\):\\(.*\\)\\\*" branch))
+ (list branch)
+ (let ((prefix (match-string 1 branch))
+ (suffix (match-string 3 branch))
+ (rhs (match-string 4 branch))
+ (pieces (split-string (match-string 2 branch) ",")))
+ (mapcar (lambda (p) (concat prefix p suffix ":" rhs p)) pieces))))
+
+(defun magit-svn-get-local-ref (url)
+ (let* ((branches (cons (magit-get "svn-remote" "svn" "fetch")
+ (magit-get-all "svn-remote" "svn" "branches")))
+ (branches (apply 'nconc
+ (mapcar 'magit-svn-expand-braces-in-branches
+ branches)))
+ (base-url (magit-get "svn-remote" "svn" "url"))
+ (result nil))
+ (while branches
+ (let* ((pats (split-string (pop branches) ":"))
+ (src (replace-regexp-in-string "\\*" "\\\\(.*\\\\)" (car pats)))
+ (dst (replace-regexp-in-string "\\*" "\\\\1" (cadr pats)))
+ (base-url (replace-regexp-in-string "\\+" "\\\\+" base-url))
+ (base-url (replace-regexp-in-string "//.+@" "//" base-url))
+ (pat1 (concat "^" src "$"))
+ (pat2 (cond ((equal src "") (concat "^" base-url "$"))
+ (t (concat "^" base-url "/" src "$")))))
+ (cond ((string-match pat1 url)
+ (setq result (replace-match dst nil nil url))
+ (setq branches nil))
+ ((string-match pat2 url)
+ (setq result (replace-match dst nil nil url))
+ (setq branches nil)))))
+ result))
+
+(defvar magit-svn-get-ref-info-cache nil
+ "A cache for svn-ref-info.
+As `magit-get-svn-ref-info' might be considered a quite
+expensive operation a cache is taken so that `magit-status'
+doesn't repeatedly call it.")
+
+(defun magit-svn-get-ref-info (&optional use-cache)
+ "Gather details about the current git-svn repository.
+Return nil if there isn't one. Keys of the alist are ref-path,
+trunk-ref-name and local-ref-name.
+If USE-CACHE is non-nil then return the value of `magit-get-svn-ref-info-cache'."
+ (if (and use-cache magit-svn-get-ref-info-cache)
+ magit-svn-get-ref-info-cache
+ (let* ((fetch (magit-get "svn-remote" "svn" "fetch"))
+ (url)
+ (revision))
+ (when fetch
+ (let* ((ref (cadr (split-string fetch ":")))
+ (ref-path (file-name-directory ref))
+ (trunk-ref-name (file-name-nondirectory ref)))
+ (set (make-local-variable
+ 'magit-svn-get-ref-info-cache)
+ (list
+ (cons 'ref-path ref-path)
+ (cons 'trunk-ref-name trunk-ref-name)
+ ;; get the local ref from the log. This is actually
+ ;; the way that git-svn does it.
+ (cons 'local-ref
+ (with-temp-buffer
+ (insert (or (magit-git-string "log" "--first-parent"
+ "--grep" "git-svn" "-1")
+ ""))
+ (goto-char (point-min))
+ (cond ((re-search-forward "git-svn-id: \\(.+/.+?\\)@\\([0-9]+\\)" nil t)
+ (setq url (match-string 1)
+ revision (match-string 2))
+ (magit-svn-get-local-ref url))
+ (t
+ (setq url (magit-get "svn-remote" "svn" "url"))
+ nil))))
+ (cons 'revision revision)
+ (cons 'url url))))))))
+
+(defun magit-svn-get-ref (&optional use-cache)
+ "Get the best guess remote ref for the current git-svn based branch.
+If USE-CACHE is non nil, use the cached information."
+ (let ((info (magit-svn-get-ref-info use-cache)))
+ (cdr (assoc 'local-ref info))))
+
+(magit-define-inserter svn-unpulled (&optional use-cache)
+ (when (magit-svn-enabled)
+ (apply #'magit-git-section
+ 'svn-unpulled "Unpulled commits (SVN):" 'magit-wash-log "log"
+ (append magit-git-log-options
+ (list
+ (format "HEAD..%s" (magit-svn-get-ref use-cache)))))))
+
+(magit-define-inserter svn-unpushed (&optional use-cache)
+ (when (magit-svn-enabled)
+ (apply #'magit-git-section
+ 'svn-unpushed "Unpushed commits (SVN):" 'magit-wash-log "log"
+ (append magit-git-log-options
+ (list
+ (format "%s..HEAD" (magit-svn-get-ref use-cache)))))))
+
+(magit-define-section-jumper svn-unpushed "Unpushed commits (SVN)")
+
+(defun magit-svn-remote-string ()
+ (let ((svn-info (magit-svn-get-ref-info)))
+ (when svn-info
+ (concat (cdr (assoc 'url svn-info))
+ " @ "
+ (cdr (assoc 'revision svn-info))))))
+
+(defun magit-svn-remote-update ()
+ (interactive)
+ (when (magit-svn-enabled)
+ (magit-run-git-async "svn" "fetch")))
+
+(easy-menu-define magit-svn-extension-menu
+ nil
+ "Git SVN extension menu"
+ '("Git SVN"
+ :visible magit-svn-mode
+ ["Create branch" magit-svn-create-branch (magit-svn-enabled)]
+ ["Rebase" magit-svn-rebase (magit-svn-enabled)]
+ ["Fetch" magit-svn-remote-update (magit-svn-enabled)]
+ ["Commit" magit-svn-dcommit (magit-svn-enabled)]))
+
+(easy-menu-add-item 'magit-mode-menu
+ '("Extensions")
+ magit-svn-extension-menu)
+
+;; add the group and its keys
+(progn
+ ;; (re-)create the group
+ (magit-key-mode-add-group 'svn)
+
+ (magit-key-mode-insert-action 'svn "r" "Rebase" 'magit-svn-rebase)
+ (magit-key-mode-insert-action 'svn "c" "DCommit" 'magit-svn-dcommit)
+ (magit-key-mode-insert-action 'svn "f" "Fetch" 'magit-svn-remote-update)
+ (magit-key-mode-insert-action 'svn "s" "Find rev" 'magit-svn-find-rev)
+ (magit-key-mode-insert-action 'svn "B" "Create branch" 'magit-svn-create-branch)
+
+ ;; generate and bind the menu popup function
+ (magit-key-mode-generate 'svn))
+
+(defvar magit-svn-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "N") 'magit-key-mode-popup-svn)
+ map))
+
+;;;###autoload
+(define-minor-mode magit-svn-mode "SVN support for Magit"
+ :lighter " SVN" :require 'magit-svn :keymap 'magit-svn-mode-map
+ (or (derived-mode-p 'magit-mode)
+ (error "This mode only makes sense with magit"))
+ (let ((unpulled-hook (lambda () (magit-insert-svn-unpulled t)))
+ (unpushed-hook (lambda () (magit-insert-svn-unpushed t)))
+ (remote-hook 'magit-svn-remote-string))
+ (if magit-svn-mode
+ (progn
+ (add-hook 'magit-after-insert-unpulled-commits-hook unpulled-hook nil t)
+ (add-hook 'magit-after-insert-unpushed-commits-hook unpushed-hook nil t)
+ (add-hook 'magit-remote-string-hook remote-hook nil t))
+ (progn
+ (remove-hook 'magit-after-insert-unpulled-commits-hook unpulled-hook t)
+ (remove-hook 'magit-after-insert-unpushed-commits-hook unpushed-hook t)
+ (remove-hook 'magit-remote-string-hook remote-hook t)))
+ (when (called-interactively-p 'any)
+ (magit-refresh))))
+
+;;;###autoload
+(defun turn-on-magit-svn ()
+ "Unconditionally turn on `magit-svn-mode'."
+ (magit-svn-mode 1))
+
+(provide 'magit-svn)
+;;; magit-svn.el ends here