| @ -1,18 +0,0 @@ | |||||
| This is the file .../info/dir, which contains the | |||||
| topmost node of the Info hierarchy, called (dir)Top. | |||||
| The first time you invoke Info you start off looking at this node. | |||||
| File: dir, Node: Top This is the top of the INFO tree | |||||
| This (the Directory node) gives a menu of major topics. | |||||
| Typing "q" exits, "?" lists all Info commands, "d" returns here, | |||||
| "h" gives a primer for first-timers, | |||||
| "mEmacs<Return>" visits the Emacs manual, etc. | |||||
| In Emacs, you can click mouse button 2 on a menu item or cross reference | |||||
| to select it. | |||||
| * Menu: | |||||
| Emacs | |||||
| * Magit: (magit). Using Git from Emacs with Magit. | |||||
| @ -1,167 +0,0 @@ | |||||
| ;;; magit-autoloads.el --- automatically extracted autoloads | |||||
| ;; | |||||
| ;;; Code: | |||||
| (add-to-list 'load-path (or (file-name-directory #$) (car load-path))) | |||||
| ;;;### (autoloads nil "magit" "../../../../.emacs.d/elpa/magit-1.2.1/magit.el" | |||||
| ;;;;;; "99a76f87e1f4d97ede141ae69ef58d75") | |||||
| ;;; Generated autoloads from ../../../../.emacs.d/elpa/magit-1.2.1/magit.el | |||||
| (autoload 'magit-status "magit" "\ | |||||
| Open a Magit status buffer for the Git repository containing | |||||
| DIR. If DIR is not within a Git repository, offer to create a | |||||
| Git repository in DIR. | |||||
| Interactively, a prefix argument means to ask the user which Git | |||||
| repository to use even if `default-directory' is under Git control. | |||||
| Two prefix arguments means to ignore `magit-repo-dirs' when asking for | |||||
| user input. | |||||
| \(fn DIR)" t nil) | |||||
| ;;;*** | |||||
| ;;;### (autoloads nil "magit-blame" "../../../../.emacs.d/elpa/magit-1.2.1/magit-blame.el" | |||||
| ;;;;;; "df3b98696a2827704979850c7f9e037d") | |||||
| ;;; Generated autoloads from ../../../../.emacs.d/elpa/magit-1.2.1/magit-blame.el | |||||
| (autoload 'magit-blame-mode "magit-blame" "\ | |||||
| Display blame information inline. | |||||
| \(fn &optional ARG)" t nil) | |||||
| ;;;*** | |||||
| ;;;### (autoloads nil "magit-stgit" "../../../../.emacs.d/elpa/magit-1.2.1/magit-stgit.el" | |||||
| ;;;;;; "3a799c21b480259ac5942c12153a0e2f") | |||||
| ;;; Generated autoloads from ../../../../.emacs.d/elpa/magit-1.2.1/magit-stgit.el | |||||
| (autoload 'magit-stgit-mode "magit-stgit" "\ | |||||
| StGit support for Magit | |||||
| \(fn &optional ARG)" t nil) | |||||
| (autoload 'turn-on-magit-stgit "magit-stgit" "\ | |||||
| Unconditionally turn on `magit-stgit-mode'. | |||||
| \(fn)" nil nil) | |||||
| ;;;*** | |||||
| ;;;### (autoloads nil "magit-svn" "../../../../.emacs.d/elpa/magit-1.2.1/magit-svn.el" | |||||
| ;;;;;; "1165865405f67b53f4b888c4b4a07ed4") | |||||
| ;;; Generated autoloads from ../../../../.emacs.d/elpa/magit-1.2.1/magit-svn.el | |||||
| (autoload 'magit-svn-mode "magit-svn" "\ | |||||
| SVN support for Magit | |||||
| \(fn &optional ARG)" t nil) | |||||
| (autoload 'turn-on-magit-svn "magit-svn" "\ | |||||
| Unconditionally turn on `magit-svn-mode'. | |||||
| \(fn)" nil nil) | |||||
| ;;;*** | |||||
| ;;;### (autoloads nil "magit-topgit" "../../../../.emacs.d/elpa/magit-1.2.1/magit-topgit.el" | |||||
| ;;;;;; "4ab21c607308ffc9dd96dba3f5d06e85") | |||||
| ;;; Generated autoloads from ../../../../.emacs.d/elpa/magit-1.2.1/magit-topgit.el | |||||
| (autoload 'magit-topgit-mode "magit-topgit" "\ | |||||
| Topgit support for Magit | |||||
| \(fn &optional ARG)" t nil) | |||||
| (autoload 'turn-on-magit-topgit "magit-topgit" "\ | |||||
| Unconditionally turn on `magit-topgit-mode'. | |||||
| \(fn)" nil nil) | |||||
| ;;;*** | |||||
| ;;;### (autoloads nil "magit-wip" "../../../../.emacs.d/elpa/magit-1.2.1/magit-wip.el" | |||||
| ;;;;;; "ffe9e1c421911a9366218c10f341fa52") | |||||
| ;;; Generated autoloads from ../../../../.emacs.d/elpa/magit-1.2.1/magit-wip.el | |||||
| (defvar magit-wip-mode nil "\ | |||||
| Non-nil if Magit-Wip mode is enabled. | |||||
| See the command `magit-wip-mode' for a description of this minor mode. | |||||
| Setting this variable directly does not take effect; | |||||
| either customize it (see the info node `Easy Customization') | |||||
| or call the function `magit-wip-mode'.") | |||||
| (custom-autoload 'magit-wip-mode "magit-wip" nil) | |||||
| (autoload 'magit-wip-mode "magit-wip" "\ | |||||
| In Magit log buffers; give wip refs a special appearance. | |||||
| \(fn &optional ARG)" t nil) | |||||
| (autoload 'magit-wip-save-mode "magit-wip" "\ | |||||
| Magit support for committing to a work-in-progress ref. | |||||
| When this minor mode is turned on and a file is saved inside a writable | |||||
| git repository then it is also committed to a special work-in-progress | |||||
| ref. | |||||
| \(fn &optional ARG)" t nil) | |||||
| (defvar global-magit-wip-save-mode nil "\ | |||||
| Non-nil if Global-Magit-Wip-Save mode is enabled. | |||||
| See the command `global-magit-wip-save-mode' for a description of this minor mode. | |||||
| Setting this variable directly does not take effect; | |||||
| either customize it (see the info node `Easy Customization') | |||||
| or call the function `global-magit-wip-save-mode'.") | |||||
| (custom-autoload 'global-magit-wip-save-mode "magit-wip" nil) | |||||
| (autoload 'global-magit-wip-save-mode "magit-wip" "\ | |||||
| Toggle Magit-Wip-Save mode in all buffers. | |||||
| With prefix ARG, enable Global-Magit-Wip-Save mode if ARG is positive; | |||||
| otherwise, disable it. If called from Lisp, enable the mode if | |||||
| ARG is omitted or nil. | |||||
| Magit-Wip-Save mode is enabled in all buffers where | |||||
| `turn-on-magit-wip-save' would do it. | |||||
| See `magit-wip-save-mode' for more information on Magit-Wip-Save mode. | |||||
| \(fn &optional ARG)" t nil) | |||||
| ;;;*** | |||||
| ;;;### (autoloads nil "rebase-mode" "../../../../.emacs.d/elpa/magit-1.2.1/rebase-mode.el" | |||||
| ;;;;;; "475429652819edec7004bbe0b89f98dc") | |||||
| ;;; Generated autoloads from ../../../../.emacs.d/elpa/magit-1.2.1/rebase-mode.el | |||||
| (autoload 'rebase-mode "rebase-mode" "\ | |||||
| Major mode for editing of a Git rebase file. | |||||
| Rebase files are generated when you run 'git rebase -i' or run | |||||
| `magit-interactive-rebase'. They describe how Git should perform | |||||
| the rebase. See the documentation for git-rebase (e.g., by | |||||
| running 'man git-rebase' at the command line) for details. | |||||
| \(fn)" t nil) | |||||
| (add-to-list 'auto-mode-alist '("git-rebase-todo" . rebase-mode)) | |||||
| ;;;*** | |||||
| ;;;### (autoloads nil nil ("../../../../.emacs.d/elpa/magit-1.2.1/magit-autoloads.el" | |||||
| ;;;;;; "../../../../.emacs.d/elpa/magit-1.2.1/magit-bisect.el" "../../../../.emacs.d/elpa/magit-1.2.1/magit-blame.el" | |||||
| ;;;;;; "../../../../.emacs.d/elpa/magit-1.2.1/magit-key-mode.el" | |||||
| ;;;;;; "../../../../.emacs.d/elpa/magit-1.2.1/magit-pkg.el" "../../../../.emacs.d/elpa/magit-1.2.1/magit-stgit.el" | |||||
| ;;;;;; "../../../../.emacs.d/elpa/magit-1.2.1/magit-svn.el" "../../../../.emacs.d/elpa/magit-1.2.1/magit-topgit.el" | |||||
| ;;;;;; "../../../../.emacs.d/elpa/magit-1.2.1/magit-wip.el" "../../../../.emacs.d/elpa/magit-1.2.1/magit.el" | |||||
| ;;;;;; "../../../../.emacs.d/elpa/magit-1.2.1/rebase-mode.el") (21570 | |||||
| ;;;;;; 22947 221714 0)) | |||||
| ;;;*** | |||||
| ;; Local Variables: | |||||
| ;; version-control: never | |||||
| ;; no-byte-compile: t | |||||
| ;; no-update-autoloads: t | |||||
| ;; End: | |||||
| ;;; magit-autoloads.el ends here | |||||
| @ -1,195 +0,0 @@ | |||||
| (require 'magit) | |||||
| (defvar magit--bisect-last-pos) | |||||
| (defvar magit--bisect-tmp-file) | |||||
| (defvar magit--bisect-info nil) | |||||
| (make-variable-buffer-local 'magit--bisect-info) | |||||
| (put 'magit--bisect-info 'permanent-local t) | |||||
| (defun magit--bisecting-p (&optional required-status) | |||||
| "Return t if a bisect session is running. | |||||
| If REQUIRED-STATUS is not nil then the current status must also | |||||
| match REQUIRED-STATUS." | |||||
| (and (file-exists-p (concat (magit-git-dir) "BISECT_LOG")) | |||||
| (or (not required-status) | |||||
| (eq (plist-get (magit--bisect-info) :status) | |||||
| required-status)))) | |||||
| (defun magit--bisect-info () | |||||
| (with-current-buffer (magit-find-status-buffer) | |||||
| (or (if (local-variable-p 'magit--bisect-info) magit--bisect-info) | |||||
| (list :status (if (magit--bisecting-p) 'running 'not-running))))) | |||||
| (defun magit--bisect-cmd (&rest args) | |||||
| "Run `git bisect ...' and update the status buffer" | |||||
| (with-current-buffer (magit-find-status-buffer) | |||||
| (let* ((output (apply 'magit-git-lines (append '("bisect") args))) | |||||
| (cmd (car args)) | |||||
| (first-line (car output))) | |||||
| (save-match-data | |||||
| (setq magit--bisect-info | |||||
| (cond ((string= cmd "reset") | |||||
| (list :status 'not-running)) | |||||
| ;; Bisecting: 78 revisions left to test after this (roughly 6 steps) | |||||
| ((string-match "^Bisecting:\\s-+\\([0-9]+\\).+roughly\\s-+\\([0-9]+\\)" first-line) | |||||
| (list :status 'running | |||||
| :revs (match-string 1 first-line) | |||||
| :steps (match-string 2 first-line))) | |||||
| ;; e2596955d9253a80aec9071c18079705597fa102 is the first bad commit | |||||
| ((string-match "^\\([a-f0-9]+\\)\\s-.*first bad commit" first-line) | |||||
| (list :status 'finished | |||||
| :bad (match-string 1 first-line))) | |||||
| (t | |||||
| (list :status 'error))))))) | |||||
| (magit-refresh)) | |||||
| (defun magit--bisect-info-for-status (branch) | |||||
| "Return bisect info suitable for display in the status buffer" | |||||
| (let* ((info (magit--bisect-info)) | |||||
| (status (plist-get info :status))) | |||||
| (cond ((eq status 'not-running) | |||||
| (or branch "(detached)")) | |||||
| ((eq status 'running) | |||||
| (format "(bisecting; %s revisions & %s steps left)" | |||||
| (or (plist-get info :revs) "unknown number of") | |||||
| (or (plist-get info :steps) "unknown number of"))) | |||||
| ((eq status 'finished) | |||||
| (format "(bisected: first bad revision is %s)" (plist-get info :bad))) | |||||
| (t | |||||
| "(bisecting; unknown error occured)")))) | |||||
| (defun magit-bisect-start () | |||||
| "Start a bisect session" | |||||
| (interactive) | |||||
| (if (magit--bisecting-p) | |||||
| (error "Already bisecting")) | |||||
| (let ((bad (magit-read-rev "Start bisect with known bad revision" "HEAD")) | |||||
| (good (magit-read-rev "Good revision" (magit-default-rev)))) | |||||
| (magit--bisect-cmd "start" bad good))) | |||||
| (defun magit-bisect-reset () | |||||
| "Quit a bisect session" | |||||
| (interactive) | |||||
| (unless (magit--bisecting-p) | |||||
| (error "Not bisecting")) | |||||
| (magit--bisect-cmd "reset")) | |||||
| (defun magit-bisect-good () | |||||
| "Tell git that the current revision is good during a bisect session" | |||||
| (interactive) | |||||
| (unless (magit--bisecting-p 'running) | |||||
| (error "Not bisecting")) | |||||
| (magit--bisect-cmd "good")) | |||||
| (defun magit-bisect-bad () | |||||
| "Tell git that the current revision is bad during a bisect session" | |||||
| (interactive) | |||||
| (unless (magit--bisecting-p 'running) | |||||
| (error "Not bisecting")) | |||||
| (magit--bisect-cmd "bad")) | |||||
| (defun magit-bisect-skip () | |||||
| "Tell git to skip the current revision during a bisect session." | |||||
| (interactive) | |||||
| (unless (magit--bisecting-p 'running) | |||||
| (error "Not bisecting")) | |||||
| (magit--bisect-cmd "skip")) | |||||
| (defun magit-bisect-log () | |||||
| "Show the bisect log" | |||||
| (interactive) | |||||
| (unless (magit--bisecting-p) | |||||
| (error "Not bisecting")) | |||||
| (magit-run-git "bisect" "log") | |||||
| (magit-display-process)) | |||||
| (defun magit-bisect-visualize () | |||||
| "Show the remaining suspects with gitk" | |||||
| (interactive) | |||||
| (unless (magit--bisecting-p) | |||||
| (error "Not bisecting")) | |||||
| (magit-run-git "bisect" "visualize") | |||||
| (unless (getenv "DISPLAY") | |||||
| (magit-display-process))) | |||||
| (easy-mmode-defmap magit-bisect-minibuffer-local-map | |||||
| '(("\C-i" . comint-dynamic-complete-filename)) | |||||
| "Keymap for minibuffer prompting of rebase command." | |||||
| :inherit minibuffer-local-map) | |||||
| (defvar magit-bisect-mode-history nil | |||||
| "Previously run bisect commands.") | |||||
| (defun magit-bisect-run (command) | |||||
| "Bisect automatically by running commands after each step" | |||||
| (interactive | |||||
| (list | |||||
| (read-from-minibuffer "Run command (like this): " | |||||
| "" | |||||
| magit-bisect-minibuffer-local-map | |||||
| nil | |||||
| 'magit-bisect-mode-history))) | |||||
| (unless (magit--bisecting-p) | |||||
| (error "Not bisecting")) | |||||
| (let ((file (make-temp-file "magit-bisect-run")) | |||||
| buffer) | |||||
| (with-temp-buffer | |||||
| (insert "#!/bin/sh\n" command "\n") | |||||
| (write-region (point-min) (point-max) file)) | |||||
| (set-file-modes file #o755) | |||||
| (magit-run-git-async "bisect" "run" file) | |||||
| (magit-display-process) | |||||
| (setq buffer (get-buffer magit-process-buffer-name)) | |||||
| (with-current-buffer buffer | |||||
| (set (make-local-variable 'magit--bisect-last-pos) 0) | |||||
| (set (make-local-variable 'magit--bisect-tmp-file) file)) | |||||
| (set-process-filter (get-buffer-process buffer) 'magit--bisect-run-filter) | |||||
| (set-process-sentinel (get-buffer-process buffer) 'magit--bisect-run-sentinel))) | |||||
| (defun magit--bisect-run-filter (process output) | |||||
| (with-current-buffer (process-buffer process) | |||||
| (save-match-data | |||||
| (let ((inhibit-read-only t) | |||||
| line new-info) | |||||
| (insert output) | |||||
| (goto-char magit--bisect-last-pos) | |||||
| (beginning-of-line) | |||||
| (while (< (point) (point-max)) | |||||
| (cond ( ;; Bisecting: 78 revisions left to test after this (roughly 6 steps) | |||||
| (looking-at "^Bisecting:\\s-+\\([0-9]+\\).+roughly\\s-+\\([0-9]+\\)") | |||||
| (setq new-info (list :status 'running | |||||
| :revs (match-string 1) | |||||
| :steps (match-string 2)))) | |||||
| ( ;; e2596955d9253a80aec9071c18079705597fa102 is the first bad commit | |||||
| (looking-at "^\\([a-f0-9]+\\)\\s-.*first bad commit") | |||||
| (setq new-info (list :status 'finished | |||||
| :bad (match-string 1))))) | |||||
| (forward-line 1)) | |||||
| (goto-char (point-max)) | |||||
| (setq magit--bisect-last-pos (point)) | |||||
| (if new-info | |||||
| (with-current-buffer (magit-find-status-buffer) | |||||
| (setq magit--bisect-info new-info) | |||||
| (magit--bisect-update-status-buffer))))))) | |||||
| (defun magit--bisect-run-sentinel (process event) | |||||
| (if (string-match-p "^finish" event) | |||||
| (with-current-buffer (process-buffer process) | |||||
| (delete-file magit--bisect-tmp-file))) | |||||
| (magit-process-sentinel process event)) | |||||
| (defun magit--bisect-update-status-buffer () | |||||
| (with-current-buffer (magit-find-status-buffer) | |||||
| (save-excursion | |||||
| (save-match-data | |||||
| (let ((inhibit-read-only t)) | |||||
| (goto-char (point-min)) | |||||
| (when (search-forward-regexp "Local:" nil t) | |||||
| (beginning-of-line) | |||||
| (kill-line) | |||||
| (insert (format "Local: %s %s" | |||||
| (propertize (magit--bisect-info-for-status (magit-get-current-branch)) | |||||
| 'face 'magit-branch) | |||||
| (abbreviate-file-name default-directory))))))))) | |||||
| (provide 'magit-bisect) | |||||
| @ -1,303 +0,0 @@ | |||||
| ;;; magit-blame.el --- blame support for magit | |||||
| ;; Copyright (C) 2012 Rüdiger Sonderfeld | |||||
| ;; Copyright (C) 2012 Yann Hodique | |||||
| ;; Copyright (C) 2011 byplayer | |||||
| ;; Copyright (C) 2010 Alexander Prusov | |||||
| ;; Copyright (C) 2009 Tim Moore | |||||
| ;; Copyright (C) 2008 Linh Dang | |||||
| ;; Copyright (C) 2008 Marius Vollmer | |||||
| ;; Author: Yann Hodique <yann.hodique@gmail.com> | |||||
| ;; Keywords: | |||||
| ;; 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 code has been backported from Egg (Magit fork) to Magit | |||||
| ;;; Code: | |||||
| (eval-when-compile (require 'cl)) | |||||
| (require 'magit) | |||||
| (defface magit-blame-header | |||||
| '((t :inherit magit-header)) | |||||
| "Face for blame header." | |||||
| :group 'magit-faces) | |||||
| (defface magit-blame-sha1 | |||||
| '((t :inherit (magit-log-sha1 | |||||
| magit-blame-header))) | |||||
| "Face for blame sha1." | |||||
| :group 'magit-faces) | |||||
| (defface magit-blame-culprit | |||||
| '((t :inherit magit-blame-header)) | |||||
| "Face for blame culprit." | |||||
| :group 'magit-faces) | |||||
| (defface magit-blame-time | |||||
| '((t :inherit magit-blame-header)) | |||||
| "Face for blame time." | |||||
| :group 'magit-faces) | |||||
| (defface magit-blame-subject | |||||
| '((t :inherit (magit-log-message magit-blame-header))) | |||||
| "Face for blame tag line." | |||||
| :group 'magit-faces) | |||||
| (defconst magit-blame-map | |||||
| (let ((map (make-sparse-keymap "Magit:Blame"))) | |||||
| (define-key map (kbd "l") 'magit-blame-locate-commit) | |||||
| (define-key map (kbd "RET") 'magit-blame-locate-commit) | |||||
| (define-key map (kbd "q") 'magit-blame-mode) | |||||
| (define-key map (kbd "n") 'magit-blame-next-chunk) | |||||
| (define-key map (kbd "p") 'magit-blame-previous-chunk) | |||||
| map) | |||||
| "Keymap for an annotated section.\\{magit-blame-map}") | |||||
| (defvar magit-blame-buffer-read-only) | |||||
| (make-variable-buffer-local 'magit-blame-buffer-read-only) | |||||
| ;;;###autoload | |||||
| (define-minor-mode magit-blame-mode | |||||
| "Display blame information inline." | |||||
| :keymap magit-blame-map | |||||
| :lighter " blame" | |||||
| (unless (buffer-file-name) | |||||
| (error "Current buffer has no associated file!")) | |||||
| (when (and (buffer-modified-p) | |||||
| (y-or-n-p (format "save %s first? " (buffer-file-name)))) | |||||
| (save-buffer)) | |||||
| (if magit-blame-mode | |||||
| (progn | |||||
| (setq magit-blame-buffer-read-only buffer-read-only) | |||||
| (magit-blame-file-on (current-buffer)) | |||||
| (set-buffer-modified-p nil) | |||||
| (setq buffer-read-only t)) | |||||
| (magit-blame-file-off (current-buffer)) | |||||
| (set-buffer-modified-p nil) | |||||
| (setq buffer-read-only magit-blame-buffer-read-only))) | |||||
| (defun magit-blame-file-off (buffer) | |||||
| (save-excursion | |||||
| (save-restriction | |||||
| (with-current-buffer buffer | |||||
| (widen) | |||||
| (mapc (lambda (ov) | |||||
| (if (overlay-get ov :blame) | |||||
| (delete-overlay ov))) | |||||
| (overlays-in (point-min) (point-max))))))) | |||||
| (defun magit-blame-file-on (buffer) | |||||
| (magit-blame-file-off buffer) | |||||
| (save-excursion | |||||
| (with-current-buffer buffer | |||||
| (save-restriction | |||||
| (with-temp-buffer | |||||
| (magit-git-insert (list "blame" "--porcelain" "--" | |||||
| (file-name-nondirectory | |||||
| (buffer-file-name buffer)))) | |||||
| (magit-blame-parse buffer (current-buffer))))))) | |||||
| (defun magit-blame-locate-commit (pos) | |||||
| "Jump to a commit in the branch history from an annotated blame section." | |||||
| (interactive "d") | |||||
| (let ((overlays (overlays-at pos)) | |||||
| sha1) | |||||
| (dolist (ov overlays) | |||||
| (if (overlay-get ov :blame) | |||||
| (setq sha1 (plist-get (nth 3 (overlay-get ov :blame)) :sha1)))) | |||||
| (if sha1 | |||||
| (magit-show-commit sha1)))) | |||||
| (defun magit-find-next-overlay-change (BEG END PROP) | |||||
| "Return the next position after BEG where an overlay matching a | |||||
| property PROP starts or ends. If there are no matching overlay | |||||
| boundaries from BEG to END, the return value is nil." | |||||
| (save-excursion | |||||
| (goto-char BEG) | |||||
| (catch 'found | |||||
| (flet ((overlay-change (pos) | |||||
| (if (< BEG END) (next-overlay-change pos) | |||||
| (previous-overlay-change pos))) | |||||
| (within-bounds-p (pos) | |||||
| (if (< BEG END) (< pos END) | |||||
| (> pos END)))) | |||||
| (let ((ov-pos BEG)) | |||||
| ;; iterate through overlay changes from BEG to END | |||||
| (while (within-bounds-p ov-pos) | |||||
| (let* ((next-ov-pos (overlay-change ov-pos)) | |||||
| ;; search for an overlay with a PROP property | |||||
| (next-ov | |||||
| (let ((overlays (overlays-at next-ov-pos))) | |||||
| (while (and overlays | |||||
| (not (overlay-get (car overlays) PROP))) | |||||
| (setq overlays (cdr overlays))) | |||||
| (car overlays)))) | |||||
| (if next-ov | |||||
| ;; found the next overlay with prop PROP at next-ov-pos | |||||
| (throw 'found next-ov-pos) | |||||
| ;; no matching overlay found, keep looking | |||||
| (setq ov-pos next-ov-pos))))))))) | |||||
| (defun magit-blame-next-chunk (pos) | |||||
| "Go to the next blame chunk." | |||||
| (interactive "d") | |||||
| (let ((next-chunk-pos (magit-find-next-overlay-change pos (point-max) :blame))) | |||||
| (when next-chunk-pos | |||||
| (goto-char next-chunk-pos)))) | |||||
| (defun magit-blame-previous-chunk (pos) | |||||
| "Go to the previous blame chunk." | |||||
| (interactive "d") | |||||
| (let ((prev-chunk-pos (magit-find-next-overlay-change pos (point-min) :blame))) | |||||
| (when prev-chunk-pos | |||||
| (goto-char prev-chunk-pos)))) | |||||
| (defcustom magit-time-format-string "%Y-%m-%dT%T%z" | |||||
| "How to format time in magit-blame header." | |||||
| :group 'magit | |||||
| :type 'string) | |||||
| (defun magit-blame-decode-time (unixtime &optional tz) | |||||
| "Decode UNIXTIME into (HIGH LOW) format. | |||||
| The second argument TZ can be used to add the timezone in (-)HHMM | |||||
| format to UNIXTIME. UNIXTIME should be either a number | |||||
| containing seconds since epoch or Emacs's (HIGH LOW | |||||
| . IGNORED) format." | |||||
| (when (numberp tz) | |||||
| (unless (numberp unixtime) | |||||
| (setq unixtime (float-time unixtime))) | |||||
| (let* ((ptz (abs tz)) | |||||
| (min (+ (* (/ ptz 100) 60) | |||||
| (mod ptz 100)))) | |||||
| (setq unixtime (+ (* (if (< tz 0) (- min) min) 60) unixtime)))) | |||||
| (when (numberp unixtime) | |||||
| (setq unixtime (seconds-to-time unixtime))) | |||||
| unixtime) | |||||
| (defun magit-blame-format-time-string (format &optional unixtime tz) | |||||
| "Use FORMAT to format the time UNIXTIME, or now if omitted. | |||||
| UNIXTIME is specified as a number containing seconds since epoch | |||||
| or Emacs's (HIGH LOW . IGNORED) format. The optional argument TZ | |||||
| can be used to set the time zone. If TZ is a number it is | |||||
| treated as a (-)HHMM offset to Universal Time. If TZ is not | |||||
| a number and non-nil the time is printed in UTC. If TZ is nil | |||||
| the local zime zone is used. The format of the function is | |||||
| similar to `format-time-string' except for %Z which is not | |||||
| officially supported at the moment." | |||||
| (unless unixtime | |||||
| (setq unixtime (current-time))) | |||||
| (when (numberp tz) ;; TODO add support for %Z | |||||
| (setq format (replace-regexp-in-string "%z" (format "%+05d" tz) format))) | |||||
| (format-time-string format (magit-blame-decode-time unixtime tz) tz)) | |||||
| (defun magit-blame-parse (target-buf blame-buf) | |||||
| "Parse blame-info in buffer BLAME-BUF and decorate TARGET-BUF buffer." | |||||
| (save-match-data | |||||
| (let ((blank (propertize " " 'face 'magit-blame-header)) | |||||
| (nl (propertize "\n" 'face 'magit-blame-header)) | |||||
| (commit-hash (make-hash-table :test 'equal :size 577)) | |||||
| commit commit-info old-line new-line num old-file subject author | |||||
| author-time author-timezone info ov beg end blame) | |||||
| (with-current-buffer blame-buf | |||||
| (goto-char (point-min)) | |||||
| ;; search for a ful commit info | |||||
| (while (re-search-forward "^\\([0-9a-f]\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)$" nil t) | |||||
| (setq commit (match-string-no-properties 1) | |||||
| old-line (string-to-number | |||||
| (match-string-no-properties 2)) | |||||
| new-line (string-to-number | |||||
| (match-string-no-properties 3)) | |||||
| num (string-to-number | |||||
| (match-string-no-properties 4))) | |||||
| ;; was this commit already seen (and stored in the hash)? | |||||
| (setq commit-info (gethash commit commit-hash)) | |||||
| ;; Nope, this is the 1st time, the full commit-info follow. | |||||
| (unless commit-info | |||||
| (re-search-forward "^author \\(.+\\)$") | |||||
| (setq author (match-string-no-properties 1)) | |||||
| (re-search-forward "^author-time \\(.+\\)$") | |||||
| (setq author-time (string-to-number | |||||
| (match-string-no-properties 1))) | |||||
| (re-search-forward "^author-tz \\(.+\\)$") | |||||
| (setq author-timezone (string-to-number | |||||
| (match-string-no-properties 1))) | |||||
| (re-search-forward "^summary \\(.+\\)$") | |||||
| (setq subject (match-string-no-properties 1)) | |||||
| (re-search-forward "^filename \\(.+\\)$") | |||||
| (setq old-file (match-string-no-properties 1)) | |||||
| (setq commit-info (list :sha1 commit :author author | |||||
| :author-time author-time | |||||
| :author-timezone author-timezone | |||||
| :subject subject :file old-file)) | |||||
| ;; save it in the hash | |||||
| (puthash commit commit-info commit-hash)) | |||||
| ;; add the current blame-block into the list INFO. | |||||
| (setq info (cons (list old-line new-line num commit-info) | |||||
| info)))) | |||||
| ;; now do from beginning | |||||
| (setq info (nreverse info)) | |||||
| (with-current-buffer target-buf | |||||
| ;; for every blame chunk | |||||
| (dolist (chunk info) | |||||
| (setq commit-info (nth 3 chunk) | |||||
| old-line (nth 0 chunk) | |||||
| new-line (nth 1 chunk) | |||||
| num (nth 2 chunk) | |||||
| commit (plist-get commit-info :sha1) | |||||
| author (plist-get commit-info :author) | |||||
| author-time (plist-get commit-info :author-time) | |||||
| author-timezone (plist-get commit-info :author-timezone) | |||||
| subject (plist-get commit-info :subject)) | |||||
| (goto-char (point-min)) | |||||
| (forward-line (1- new-line)) | |||||
| (setq beg (line-beginning-position) | |||||
| end (save-excursion | |||||
| (forward-line num) | |||||
| (line-beginning-position))) | |||||
| ;; mark the blame chunk | |||||
| (put-text-property beg end :blame chunk) | |||||
| ;; make an overlay with blame info as 'before-string | |||||
| ;; on the current chunk. | |||||
| (setq ov (make-overlay beg end)) | |||||
| (overlay-put ov :blame chunk) | |||||
| (setq blame (concat | |||||
| (propertize (substring-no-properties commit 0 8) | |||||
| 'face 'magit-blame-sha1) | |||||
| blank | |||||
| (propertize (format "%-20s" author) | |||||
| 'face 'magit-blame-culprit) | |||||
| blank | |||||
| (propertize (magit-blame-format-time-string | |||||
| magit-time-format-string | |||||
| author-time author-timezone) | |||||
| 'face 'magit-blame-time) | |||||
| blank | |||||
| (propertize subject 'face 'magit-blame-subject) | |||||
| blank nl)) | |||||
| (overlay-put ov 'before-string blame)))))) | |||||
| (provide 'magit-blame) | |||||
| ;;; magit-blame.el ends here | |||||
| @ -1,521 +0,0 @@ | |||||
| (require 'magit) | |||||
| (eval-when-compile (require 'cl)) | |||||
| (defvar magit-key-mode-key-maps '() | |||||
| "This will be filled lazily with proper `define-key' built | |||||
| keymaps as they're requested.") | |||||
| (defvar magit-key-mode-buf-name "*magit-key*" | |||||
| "Name of the buffer.") | |||||
| (defvar magit-key-mode-current-args '() | |||||
| "Will contain the arguments to be passed to git.") | |||||
| (defvar magit-key-mode-current-options '() | |||||
| "Will contain the arguments to be passed to git.") | |||||
| (defvar magit-log-mode-window-conf nil | |||||
| "Will hold the pre-menu configuration of magit.") | |||||
| (defvar magit-key-mode-groups | |||||
| '((logging | |||||
| (man-page "git-log") | |||||
| (actions | |||||
| ("l" "Short" magit-log) | |||||
| ("L" "Long" magit-log-long) | |||||
| ("h" "Reflog" magit-reflog) | |||||
| ("rl" "Ranged short" magit-log-ranged) | |||||
| ("rL" "Ranged long" magit-log-long-ranged) | |||||
| ("rh" "Ranged reflog" magit-reflog-ranged)) | |||||
| (switches | |||||
| ("-m" "Only merge commits" "--merges") | |||||
| ("-f" "First parent" "--first-parent") | |||||
| ("-i" "Case insensitive patterns" "-i") | |||||
| ("-pr" "Pickaxe regex" "--pickaxe-regex") | |||||
| ("-n" "Name only" "--name-only") | |||||
| ("-am" "All match" "--all-match") | |||||
| ("-al" "All" "--all")) | |||||
| (arguments | |||||
| ("=r" "Relative" "--relative=" read-directory-name) | |||||
| ("=c" "Committer" "--committer=" read-from-minibuffer) | |||||
| ("=>" "Since" "--since=" read-from-minibuffer) | |||||
| ("=<" "Before" "--before=" read-from-minibuffer) | |||||
| ("=s" "Pickaxe search" "-S" read-from-minibuffer) | |||||
| ("=a" "Author" "--author=" read-from-minibuffer) | |||||
| ("=g" "Grep" "--grep=" read-from-minibuffer))) | |||||
| (running | |||||
| (actions | |||||
| ("!" "Command from root" magit-shell-command) | |||||
| (":" "Git command" magit-git-command) | |||||
| ("g" "git gui" magit-run-git-gui) | |||||
| ("k" "gitk" magit-run-gitk))) | |||||
| (fetching | |||||
| (man-page "git-fetch") | |||||
| (actions | |||||
| ("f" "Current" magit-fetch-current) | |||||
| ("a" "All" magit-remote-update) | |||||
| ("o" "Other" magit-fetch)) | |||||
| (switches | |||||
| ("-p" "Prune" "--prune"))) | |||||
| (pushing | |||||
| (man-page "git-push") | |||||
| (actions | |||||
| ("P" "Push" magit-push) | |||||
| ("t" "Push tags" magit-push-tags)) | |||||
| (switches | |||||
| ("-f" "Force" "--force") | |||||
| ("-d" "Dry run" "-n") | |||||
| ("-u" "Set upstream" "-u"))) | |||||
| (pulling | |||||
| (man-page "git-pull") | |||||
| (actions | |||||
| ("F" "Pull" magit-pull)) | |||||
| (switches | |||||
| ("-r" "Rebase" "--rebase"))) | |||||
| (branching | |||||
| (man-page "git-branch") | |||||
| (actions | |||||
| ("v" "Branch manager" magit-branch-manager) | |||||
| ("c" "Create" magit-create-branch) | |||||
| ("r" "Rename" magit-move-branch) | |||||
| ("k" "Delete" magit-delete-branch) | |||||
| ("b" "Checkout" magit-checkout))) | |||||
| (remoting | |||||
| (man-page "git-remote") | |||||
| (actions | |||||
| ("v" "Branch manager" magit-branch-manager) | |||||
| ("a" "Add" magit-add-remote) | |||||
| ("r" "Rename" magit-rename-remote) | |||||
| ("k" "Remove" magit-remove-remote))) | |||||
| (tagging | |||||
| (man-page "git-tag") | |||||
| (actions | |||||
| ("t" "Lightweight" magit-tag) | |||||
| ("a" "Annotated" magit-annotated-tag)) | |||||
| (switches | |||||
| ("-f" "Force" "-f"))) | |||||
| (stashing | |||||
| (man-page "git-stash") | |||||
| (actions | |||||
| ("z" "Save" magit-stash) | |||||
| ("s" "Snapshot" magit-stash-snapshot)) | |||||
| (switches | |||||
| ("-k" "Keep index" "--keep-index") | |||||
| ("-u" "Include untracked files" "--include-untracked") | |||||
| ("-a" "Include all files" "--all"))) | |||||
| (merging | |||||
| (man-page "git-merge") | |||||
| (actions | |||||
| ("m" "Merge" magit-manual-merge)) | |||||
| (switches | |||||
| ("-ff" "Fast-forward only" "--ff-only") | |||||
| ("-nf" "No fast-forward" "--no-ff") | |||||
| ("-sq" "Squash" "--squash")) | |||||
| (arguments | |||||
| ("-st" "Strategy" "--strategy=" read-from-minibuffer))) | |||||
| (rewriting | |||||
| (actions | |||||
| ("b" "Begin" magit-rewrite-start) | |||||
| ("s" "Stop" magit-rewrite-stop) | |||||
| ("a" "Abort" magit-rewrite-abort) | |||||
| ("f" "Finish" magit-rewrite-finish) | |||||
| ("*" "Set unused" magit-rewrite-set-unused) | |||||
| ("." "Set used" magit-rewrite-set-used))) | |||||
| (submodule | |||||
| (man-page "git-submodule") | |||||
| (actions | |||||
| ("u" "Update" magit-submodule-update) | |||||
| ("b" "Both update and init" magit-submodule-update-init) | |||||
| ("i" "Init" magit-submodule-init) | |||||
| ("s" "Sync" magit-submodule-sync))) | |||||
| (bisecting | |||||
| (man-page "git-bisect") | |||||
| (actions | |||||
| ("b" "Bad" magit-bisect-bad) | |||||
| ("g" "Good" magit-bisect-good) | |||||
| ("k" "Skip" magit-bisect-skip) | |||||
| ("l" "Log" magit-bisect-log) | |||||
| ("r" "Reset" magit-bisect-reset) | |||||
| ("s" "Start" magit-bisect-start) | |||||
| ("u" "Run" magit-bisect-run) | |||||
| ("v" "Visualize" magit-bisect-visualize)))) | |||||
| "Holds the key, help, function mapping for the log-mode. If you | |||||
| modify this make sure you reset `magit-key-mode-key-maps' to | |||||
| nil.") | |||||
| (defun magit-key-mode-delete-group (group) | |||||
| "Delete a group from `magit-key-mode-key-maps'." | |||||
| (let ((items (assoc group magit-key-mode-groups))) | |||||
| (when items | |||||
| ;; reset the cache | |||||
| (setq magit-key-mode-key-maps nil) | |||||
| ;; delete the whole group | |||||
| (setq magit-key-mode-groups | |||||
| (delq items magit-key-mode-groups)) | |||||
| ;; unbind the defun | |||||
| (magit-key-mode-de-generate group)) | |||||
| magit-key-mode-groups)) | |||||
| (defun magit-key-mode-add-group (group) | |||||
| "Add a new group to `magit-key-mode-key-maps'. If there's | |||||
| already a group of that name then this will completely remove it | |||||
| and put in its place an empty one of the same name." | |||||
| (when (assoc group magit-key-mode-groups) | |||||
| (magit-key-mode-delete-group group)) | |||||
| (setq magit-key-mode-groups | |||||
| (cons (list group (list 'actions)) magit-key-mode-groups))) | |||||
| (defun magit-key-mode-key-defined-p (for-group key) | |||||
| "If KEY is defined as any of switch, argument or action within | |||||
| FOR-GROUP then return t" | |||||
| (catch 'result | |||||
| (let ((options (magit-key-mode-options-for-group for-group))) | |||||
| (dolist (type '(actions switches arguments)) | |||||
| (when (assoc key (assoc type options)) | |||||
| (throw 'result t)))))) | |||||
| (defun magit-key-mode-update-group (for-group thing &rest args) | |||||
| "Abstraction for setting values in `magit-key-mode-key-maps'." | |||||
| (let* ((options (magit-key-mode-options-for-group for-group)) | |||||
| (things (assoc thing options)) | |||||
| (key (car args))) | |||||
| (if (cdr things) | |||||
| (if (magit-key-mode-key-defined-p for-group key) | |||||
| (error "%s is already defined in the %s group." key for-group) | |||||
| (setcdr (cdr things) (cons args (cddr things)))) | |||||
| (setcdr things (list args))) | |||||
| (setq magit-key-mode-key-maps nil) | |||||
| things)) | |||||
| (defun magit-key-mode-insert-argument (for-group key desc arg read-func) | |||||
| "Add a new binding (KEY) in FOR-GROUP which will use READ-FUNC | |||||
| to receive input to apply to argument ARG git is run. DESC should | |||||
| be a brief description of the binding." | |||||
| (magit-key-mode-update-group for-group 'arguments key desc arg read-func)) | |||||
| (defun magit-key-mode-insert-switch (for-group key desc switch) | |||||
| "Add a new binding (KEY) in FOR-GROUP which will add SWITCH to git's | |||||
| command line when it runs. DESC should be a brief description of | |||||
| the binding." | |||||
| (magit-key-mode-update-group for-group 'switches key desc switch)) | |||||
| (defun magit-key-mode-insert-action (for-group key desc func) | |||||
| "Add a new binding (KEY) in FOR-GROUP which will run command | |||||
| FUNC. DESC should be a brief description of the binding." | |||||
| (magit-key-mode-update-group for-group 'actions key desc func)) | |||||
| (defun magit-key-mode-options-for-group (for-group) | |||||
| "Retrieve the options (switches, commands and arguments) for | |||||
| the group FOR-GROUP." | |||||
| (or (cdr (assoc for-group magit-key-mode-groups)) | |||||
| (error "Unknown group '%s'" for-group))) | |||||
| (defun magit-key-mode-help (for-group) | |||||
| "Provide help for a key (which the user is prompted for) within | |||||
| FOR-GROUP." | |||||
| (let* ((opts (magit-key-mode-options-for-group for-group)) | |||||
| (man-page (cadr (assoc 'man-page opts))) | |||||
| (seq (read-key-sequence | |||||
| (format "Enter command prefix%s: " | |||||
| (if man-page | |||||
| (format ", `?' for man `%s'" man-page) | |||||
| "")))) | |||||
| (actions (cdr (assoc 'actions opts)))) | |||||
| (cond | |||||
| ;; if it is an action popup the help for the to-be-run function | |||||
| ((assoc seq actions) (describe-function (nth 2 (assoc seq actions)))) | |||||
| ;; if there is "?" show a man page if there is one | |||||
| ((equal seq "?") | |||||
| (if man-page | |||||
| (man man-page) | |||||
| (error "No man page associated with `%s'" for-group))) | |||||
| (t (error "No help associated with `%s'" seq))))) | |||||
| (defun magit-key-mode-exec-at-point () | |||||
| "Run action/args/option at point." | |||||
| (interactive) | |||||
| (let* ((key (or (get-text-property (point) 'key-group-executor) | |||||
| (error "Nothing at point to do."))) | |||||
| (def (lookup-key (current-local-map) key))) | |||||
| (call-interactively def))) | |||||
| (defun magit-key-mode-jump-to-next-exec () | |||||
| "Jump to the next action/args/option point." | |||||
| (interactive) | |||||
| (let* ((oldp (point)) | |||||
| (old (get-text-property oldp 'key-group-executor)) | |||||
| (p (if (= oldp (point-max)) (point-min) (1+ oldp)))) | |||||
| (while (let ((new (get-text-property p 'key-group-executor))) | |||||
| (and (not (= p oldp)) (or (not new) (eq new old)))) | |||||
| (setq p (if (= p (point-max)) (point-min) (1+ p)))) | |||||
| (goto-char p) | |||||
| (skip-chars-forward " "))) | |||||
| (defun magit-key-mode-build-keymap (for-group) | |||||
| "Construct a normal looking keymap for the key mode to use and | |||||
| put it in magit-key-mode-key-maps for fast lookup." | |||||
| (let* ((options (magit-key-mode-options-for-group for-group)) | |||||
| (actions (cdr (assoc 'actions options))) | |||||
| (switches (cdr (assoc 'switches options))) | |||||
| (arguments (cdr (assoc 'arguments options))) | |||||
| (map (make-sparse-keymap))) | |||||
| (suppress-keymap map 'nodigits) | |||||
| ;; ret dwim | |||||
| (define-key map (kbd "RET") 'magit-key-mode-exec-at-point) | |||||
| ;; tab jumps to the next "button" | |||||
| (define-key map (kbd "TAB") 'magit-key-mode-jump-to-next-exec) | |||||
| ;; all maps should `quit' with `C-g' or `q' | |||||
| (define-key map (kbd "C-g") `(lambda () | |||||
| (interactive) | |||||
| (magit-key-mode-command nil))) | |||||
| (define-key map (kbd "q") `(lambda () | |||||
| (interactive) | |||||
| (magit-key-mode-command nil))) | |||||
| ;; run help | |||||
| (define-key map (kbd "?") `(lambda () | |||||
| (interactive) | |||||
| (magit-key-mode-help ',for-group))) | |||||
| (flet ((defkey (k action) | |||||
| (when (and (lookup-key map (car k)) | |||||
| (not (numberp (lookup-key map (car k))))) | |||||
| (message "Warning: overriding binding for `%s' in %S" | |||||
| (car k) for-group) | |||||
| (ding) | |||||
| (sit-for 2)) | |||||
| (define-key map (car k) | |||||
| `(lambda () (interactive) ,action)))) | |||||
| (when actions | |||||
| (dolist (k actions) | |||||
| (defkey k `(magit-key-mode-command ',(nth 2 k))))) | |||||
| (when switches | |||||
| (dolist (k switches) | |||||
| (defkey k `(magit-key-mode-add-option ',for-group ,(nth 2 k))))) | |||||
| (when arguments | |||||
| (dolist (k arguments) | |||||
| (defkey k `(magit-key-mode-add-argument | |||||
| ',for-group ,(nth 2 k) ',(nth 3 k)))))) | |||||
| (push (cons for-group map) magit-key-mode-key-maps) | |||||
| map)) | |||||
| (defvar magit-key-mode-prefix nil | |||||
| "For internal use. Holds the prefix argument to the command | |||||
| that brought up the key-mode window, so it can be used by the | |||||
| command that's eventually invoked.") | |||||
| (defun magit-key-mode-command (func) | |||||
| (let ((args '())) | |||||
| ;; why can't maphash return a list?! | |||||
| (maphash (lambda (k v) | |||||
| (push (concat k (shell-quote-argument v)) args)) | |||||
| magit-key-mode-current-args) | |||||
| (let ((magit-custom-options (append args magit-key-mode-current-options)) | |||||
| (current-prefix-arg (or current-prefix-arg magit-key-mode-prefix))) | |||||
| (set-window-configuration magit-log-mode-window-conf) | |||||
| (when func | |||||
| (call-interactively func)) | |||||
| (magit-key-mode-kill-buffer)))) | |||||
| (defvar magit-key-mode-current-args nil | |||||
| "A hash-table of current argument set (which will eventually | |||||
| make it to the git command-line).") | |||||
| (defun magit-key-mode-add-argument (for-group arg-name input-func) | |||||
| (let ((input (funcall input-func (concat arg-name ": ")))) | |||||
| (puthash arg-name input magit-key-mode-current-args) | |||||
| (magit-key-mode-redraw for-group))) | |||||
| (defvar magit-key-mode-current-options '() | |||||
| "Current option set (which will eventually make it to the git | |||||
| command-line).") | |||||
| (defun magit-key-mode-add-option (for-group option-name) | |||||
| "Toggles the appearance of OPTION-NAME in | |||||
| `magit-key-mode-current-options'." | |||||
| (if (not (member option-name magit-key-mode-current-options)) | |||||
| (add-to-list 'magit-key-mode-current-options option-name) | |||||
| (setq magit-key-mode-current-options | |||||
| (delete option-name magit-key-mode-current-options))) | |||||
| (magit-key-mode-redraw for-group)) | |||||
| (defun magit-key-mode-kill-buffer () | |||||
| (interactive) | |||||
| (kill-buffer magit-key-mode-buf-name)) | |||||
| (defvar magit-log-mode-window-conf nil | |||||
| "Pre-popup window configuration.") | |||||
| (defun magit-key-mode (for-group &optional original-opts) | |||||
| "Mode for magit key selection. All commands, switches and | |||||
| options can be toggled/actioned with the key combination | |||||
| highlighted before the description." | |||||
| (interactive) | |||||
| ;; save the window config to restore it as was (no need to make this | |||||
| ;; buffer local) | |||||
| (setq magit-log-mode-window-conf | |||||
| (current-window-configuration)) | |||||
| ;; setup the mode, draw the buffer | |||||
| (let ((buf (get-buffer-create magit-key-mode-buf-name))) | |||||
| (delete-other-windows) | |||||
| (split-window-vertically) | |||||
| (other-window 1) | |||||
| (switch-to-buffer buf) | |||||
| (kill-all-local-variables) | |||||
| (set (make-local-variable | |||||
| 'magit-key-mode-current-options) | |||||
| original-opts) | |||||
| (set (make-local-variable | |||||
| 'magit-key-mode-current-args) | |||||
| (make-hash-table)) | |||||
| (set (make-local-variable 'magit-key-mode-prefix) current-prefix-arg) | |||||
| (magit-key-mode-redraw for-group)) | |||||
| (message | |||||
| (concat | |||||
| "Type a prefix key to toggle it. Run 'actions' with their prefixes. " | |||||
| "'?' for more help."))) | |||||
| (defun magit-key-mode-get-key-map (for-group) | |||||
| "Get or build the keymap for FOR-GROUP." | |||||
| (or (cdr (assoc for-group magit-key-mode-key-maps)) | |||||
| (magit-key-mode-build-keymap for-group))) | |||||
| (defun magit-key-mode-redraw (for-group) | |||||
| "(re)draw the magit key buffer." | |||||
| (let ((buffer-read-only nil) | |||||
| (old-point (point)) | |||||
| (is-first (zerop (buffer-size))) | |||||
| (actions-p nil)) | |||||
| (erase-buffer) | |||||
| (make-local-variable 'font-lock-defaults) | |||||
| (use-local-map (magit-key-mode-get-key-map for-group)) | |||||
| (setq actions-p (magit-key-mode-draw for-group)) | |||||
| (delete-trailing-whitespace) | |||||
| (setq mode-name "magit-key-mode" major-mode 'magit-key-mode) | |||||
| (if (and is-first actions-p) | |||||
| (progn (goto-char actions-p) | |||||
| (magit-key-mode-jump-to-next-exec)) | |||||
| (goto-char old-point))) | |||||
| (setq buffer-read-only t) | |||||
| (fit-window-to-buffer)) | |||||
| (defun magit-key-mode-draw-header (header) | |||||
| "Draw a header with the correct face." | |||||
| (insert (propertize header 'face 'font-lock-keyword-face) "\n")) | |||||
| (defvar magit-key-mode-args-in-cols nil | |||||
| "When true, draw arguments in columns as with switches and | |||||
| options.") | |||||
| (defun magit-key-mode-draw-args (args) | |||||
| "Draw the args part of the menu." | |||||
| (magit-key-mode-draw-buttons | |||||
| "Args" | |||||
| args | |||||
| (lambda (x) | |||||
| (format "(%s) %s" | |||||
| (nth 2 x) | |||||
| (propertize (gethash (nth 2 x) magit-key-mode-current-args "") | |||||
| 'face 'widget-field))) | |||||
| (not magit-key-mode-args-in-cols))) | |||||
| (defun magit-key-mode-draw-switches (switches) | |||||
| "Draw the switches part of the menu." | |||||
| (magit-key-mode-draw-buttons | |||||
| "Switches" | |||||
| switches | |||||
| (lambda (x) | |||||
| (format "(%s)" (let ((s (nth 2 x))) | |||||
| (if (member s magit-key-mode-current-options) | |||||
| (propertize s 'face 'font-lock-warning-face) | |||||
| s)))))) | |||||
| (defun magit-key-mode-draw-actions (actions) | |||||
| "Draw the actions part of the menu." | |||||
| (magit-key-mode-draw-buttons "Actions" actions nil)) | |||||
| (defun magit-key-mode-draw-buttons (section xs maker | |||||
| &optional one-col-each) | |||||
| (when xs | |||||
| (magit-key-mode-draw-header section) | |||||
| (magit-key-mode-draw-in-cols | |||||
| (mapcar (lambda (x) | |||||
| (let* ((head (propertize (car x) 'face 'font-lock-builtin-face)) | |||||
| (desc (nth 1 x)) | |||||
| (more (and maker (funcall maker x))) | |||||
| (text (format " %s: %s%s%s" | |||||
| head desc (if more " " "") (or more "")))) | |||||
| (propertize text 'key-group-executor (car x)))) | |||||
| xs) | |||||
| one-col-each))) | |||||
| (defun magit-key-mode-draw-in-cols (strings one-col-each) | |||||
| "Given a list of strings, print in columns (using `insert'). If | |||||
| ONE-COL-EACH is true then don't columify, but rather, draw each | |||||
| item on one line." | |||||
| (let ((longest-act (apply 'max (mapcar 'length strings)))) | |||||
| (while strings | |||||
| (let ((str (car strings))) | |||||
| (let ((padding (make-string (- (+ longest-act 3) (length str)) ? ))) | |||||
| (insert str) | |||||
| (if (or one-col-each | |||||
| (and (> (+ (length padding) ; | |||||
| (current-column) | |||||
| longest-act) | |||||
| (window-width)) | |||||
| (cdr strings))) | |||||
| (insert "\n") | |||||
| (insert padding)))) | |||||
| (setq strings (cdr strings)))) | |||||
| (insert "\n")) | |||||
| (defun magit-key-mode-draw (for-group) | |||||
| "Function used to draw actions, switches and parameters. | |||||
| Returns the point before the actions part, if any." | |||||
| (let* ((options (magit-key-mode-options-for-group for-group)) | |||||
| (switches (cdr (assoc 'switches options))) | |||||
| (arguments (cdr (assoc 'arguments options))) | |||||
| (actions (cdr (assoc 'actions options))) | |||||
| (p nil)) | |||||
| (magit-key-mode-draw-switches switches) | |||||
| (magit-key-mode-draw-args arguments) | |||||
| (when actions (setq p (point-marker))) | |||||
| (magit-key-mode-draw-actions actions) | |||||
| (insert "\n") | |||||
| p)) | |||||
| (defun magit-key-mode-de-generate (group) | |||||
| "Unbind the function for GROUP." | |||||
| (fmakunbound | |||||
| (intern (concat "magit-key-mode-popup-" (symbol-name group))))) | |||||
| (defun magit-key-mode-generate (group) | |||||
| "Generate the key-group menu for GROUP" | |||||
| (let ((opts (magit-key-mode-options-for-group group))) | |||||
| (eval | |||||
| `(defun ,(intern (concat "magit-key-mode-popup-" (symbol-name group))) nil | |||||
| ,(concat "Key menu for " (symbol-name group)) | |||||
| (interactive) | |||||
| (magit-key-mode (quote ,group)))))) | |||||
| ;; create the interactive functions for the key mode popups (which are | |||||
| ;; applied in the top-level key maps) | |||||
| (mapc (lambda (g) | |||||
| (magit-key-mode-generate (car g))) | |||||
| magit-key-mode-groups) | |||||
| (provide 'magit-key-mode) | |||||
| @ -1 +0,0 @@ | |||||
| (define-package "magit" "1.2.1" "Control Git from Emacs.") | |||||
| @ -1,288 +0,0 @@ | |||||
| ;;; magit-stgit.el --- StGit plug-in for Magit | |||||
| ;; Copyright (C) 2011 Lluis Vilanova | |||||
| ;; | |||||
| ;; 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 StGit functionality as a separate component of Magit. | |||||
| ;; Available actions: | |||||
| ;; - visit: Shows the patch at point in the series (stg show) | |||||
| ;; - apply: Goes to the patch at point in the series (stg goto) | |||||
| ;; - discard: Deletes the marked/at point patch in the series (stg delete) | |||||
| ;; Available commands: | |||||
| ;; - `magit-stgit-refresh': Refresh the marked/at point patch in the series | |||||
| ;; (stg refresh) | |||||
| ;; - `magit-stgit-repair': Repair the StGit metadata (stg repair) | |||||
| ;; - `magit-stgit-rebase': Rebase the whole series (stg rebase) | |||||
| ;; TODO: | |||||
| ;; - Let the user select which files must be included in a refresh. | |||||
| ;; - Missing actions for `magit-show-item-or-scroll-up' and | |||||
| ;; `magit-show-item-or-scroll-down'. | |||||
| ;; - Marking a patch is slow and refreshes all buffers, which resets their | |||||
| ;; position (i.e., the buffer is shown from its first line). | |||||
| ;;; Code: | |||||
| (require 'magit) | |||||
| (eval-when-compile | |||||
| (require 'cl)) | |||||
| ;;; Customizables: | |||||
| (defcustom magit-stgit-executable "stg" | |||||
| "The name of the StGit executable." | |||||
| :group 'magit | |||||
| :type 'string) | |||||
| (defface magit-stgit-applied | |||||
| '((t :inherit magit-diff-add)) | |||||
| "Face for an applied stgit patch." | |||||
| :group 'magit-faces) | |||||
| (defface magit-stgit-current | |||||
| '((t :inherit magit-item-highlight)) | |||||
| "Face for the current stgit patch." | |||||
| :group 'magit-faces) | |||||
| (defface magit-stgit-other | |||||
| '((t :inherit magit-diff-del)) | |||||
| "Face for a non-applied stgit patch." | |||||
| :group 'magit-faces) | |||||
| (defface magit-stgit-marked | |||||
| '((t :inherit magit-item-mark)) | |||||
| "Face for a marked stgit patch." | |||||
| :group 'magit-faces) | |||||
| (defface magit-stgit-empty | |||||
| '((t :inherit magit-item-mark)) | |||||
| "Face for an empty stgit patch." | |||||
| :group 'magit-faces) | |||||
| ;;; Common code: | |||||
| (defvar magit-stgit--enabled nil | |||||
| "Whether this buffer has StGit support.") | |||||
| (make-variable-buffer-local 'magit-stgit--enabled) | |||||
| (defvar magit-stgit-mode) | |||||
| (defun magit-stgit--enabled () | |||||
| "Whether this buffer has StGit support enabled." | |||||
| (if (assoc 'magit-stgit--enabled (buffer-local-variables)) | |||||
| magit-stgit--enabled | |||||
| (setq magit-stgit--enabled | |||||
| (and magit-stgit-mode | |||||
| (not (null | |||||
| (member (concat (magit-get-current-branch) ".stgit") | |||||
| (mapcar #'(lambda (line) | |||||
| (string-match "^\\*?\s*\\([^\s]*\\)" | |||||
| line) | |||||
| (match-string 1 line)) | |||||
| (magit-git-lines "branch"))))))))) | |||||
| (defun magit-stgit--enabled-reset () | |||||
| "Reset the StGit enabled state." | |||||
| (kill-local-variable 'magit-stgit--enabled)) | |||||
| (defvar magit-stgit--marked-patch nil | |||||
| "The (per-buffer) currently marked patch in an StGit series.") | |||||
| (make-variable-buffer-local 'magit-stgit--marked-patch) | |||||
| ;;; Menu: | |||||
| (easy-menu-define magit-stgit-extension-menu | |||||
| nil | |||||
| "StGit extension menu" | |||||
| '("StGit" | |||||
| :active (magit-stgit--enabled) | |||||
| ["Refresh patch" magit-stgit-refresh | |||||
| :help "Refresh the contents of a patch in an StGit series"] | |||||
| ["Repair" magit-stgit-repair | |||||
| :help "Repair StGit metadata if branch was modified with git commands"] | |||||
| ["Rebase series" magit-stgit-rebase | |||||
| :help "Rebase an StGit patch series"] | |||||
| )) | |||||
| (easy-menu-add-item 'magit-mode-menu | |||||
| '("Extensions") | |||||
| magit-stgit-extension-menu) | |||||
| ;;; Series section: | |||||
| (defun magit-stgit--wash-patch () | |||||
| (if (search-forward-regexp "^\\(.\\)\\(.\\) \\([^\s]*\\)\\(\s*# ?\\)\\(.*\\)" | |||||
| (line-end-position) t) | |||||
| (let* ((empty-str "[empty] ") | |||||
| (indent-str (make-string (string-bytes empty-str) ?\ )) | |||||
| (empty (match-string 1)) | |||||
| (state (match-string 2)) | |||||
| (patch (match-string 3)) | |||||
| (descr (match-string 5))) | |||||
| (delete-region (line-beginning-position) (line-end-position)) | |||||
| (insert | |||||
| (cond ((string= empty "0") | |||||
| (propertize (concat empty-str " " state " " descr) 'face 'magit-stgit-empty)) | |||||
| ((string= magit-stgit--marked-patch patch) | |||||
| (propertize (concat indent-str " " state " " descr) 'face 'magit-stgit-marked)) | |||||
| ((string= state "+") | |||||
| (concat indent-str " " (propertize state 'face 'magit-stgit-applied) " " descr)) | |||||
| ((string= state ">") | |||||
| (propertize (concat indent-str " " state " " descr) 'face 'magit-stgit-current)) | |||||
| ((string= state "-") | |||||
| (concat indent-str " " (propertize state 'face 'magit-stgit-other) " " descr)))) | |||||
| (goto-char (line-beginning-position)) | |||||
| (magit-with-section patch 'series | |||||
| (magit-set-section-info patch) | |||||
| (goto-char (line-end-position))) | |||||
| (forward-line)) | |||||
| (delete-region (line-beginning-position) (1+ (line-end-position)))) | |||||
| t) | |||||
| (defun magit-stgit--wash-series () | |||||
| (let ((magit-old-top-section nil)) | |||||
| (magit-wash-sequence #'magit-stgit--wash-patch))) | |||||
| (magit-define-inserter series () | |||||
| (when (executable-find magit-stgit-executable) | |||||
| (magit-insert-section 'series | |||||
| "Series:" 'magit-stgit--wash-series | |||||
| magit-stgit-executable "series" "-a" "-d" "-e"))) | |||||
| ;;; Actions: | |||||
| ;; Copy of `magit-refresh-commit-buffer' (version 1.0.0) | |||||
| (defun magit-stgit--refresh-patch-buffer (patch) | |||||
| (magit-create-buffer-sections | |||||
| (magit-insert-section nil nil | |||||
| 'magit-wash-commit | |||||
| magit-stgit-executable | |||||
| "show" | |||||
| patch))) | |||||
| ;; Copy of `magit-show-commit' (version 1.0.0) | |||||
| (defun magit-stgit--show-patch (patch &optional scroll) | |||||
| (when (magit-section-p patch) | |||||
| (setq patch (magit-section-info patch))) | |||||
| (let ((dir default-directory) | |||||
| (buf (get-buffer-create magit-commit-buffer-name))) | |||||
| (cond ((and (equal magit-currently-shown-commit patch) | |||||
| ;; if it's empty then the buffer was killed | |||||
| (with-current-buffer buf | |||||
| (> (length (buffer-string)) 1))) | |||||
| (let ((win (get-buffer-window buf))) | |||||
| (cond ((not win) | |||||
| (display-buffer buf)) | |||||
| (scroll | |||||
| (with-selected-window win | |||||
| (funcall scroll)))))) | |||||
| (t | |||||
| (setq magit-currently-shown-commit patch) | |||||
| (display-buffer buf) | |||||
| (with-current-buffer buf | |||||
| (set-buffer buf) | |||||
| (goto-char (point-min)) | |||||
| (magit-mode-init dir 'magit-commit-mode | |||||
| #'magit-stgit--refresh-patch-buffer patch)))))) | |||||
| (magit-add-action (item info "visit") | |||||
| ((series) | |||||
| (magit-stgit--show-patch info) | |||||
| (pop-to-buffer magit-commit-buffer-name))) | |||||
| (magit-add-action (item info "apply") | |||||
| ((series) | |||||
| (magit-run magit-stgit-executable "goto" info))) | |||||
| (magit-add-action (item info "discard") | |||||
| ((series) | |||||
| (let ((patch (or magit-stgit--marked-patch info))) | |||||
| (if (yes-or-no-p (format "Delete patch '%s' in series? " patch)) | |||||
| (progn | |||||
| (if (string= magit-stgit--marked-patch patch) | |||||
| (setq magit-stgit--marked-patch nil)) | |||||
| (magit-run magit-stgit-executable "delete" patch)))))) | |||||
| (defun magit-stgit--set-marked-patch (patch) | |||||
| (setq magit-stgit--marked-patch | |||||
| (if (string= magit-stgit--marked-patch patch) | |||||
| nil | |||||
| patch))) | |||||
| (magit-add-action (item info "mark") | |||||
| ((series) | |||||
| (magit-stgit--set-marked-patch info) | |||||
| (magit-refresh-all))) | |||||
| ;;; Commands: | |||||
| (defun magit-stgit-refresh () | |||||
| "Refresh the contents of a patch in an StGit series. | |||||
| If there is no marked patch in the series, refreshes the current | |||||
| patch. | |||||
| Otherwise, refreshes the marked patch." | |||||
| (interactive) | |||||
| (if magit-stgit--marked-patch | |||||
| (magit-run magit-stgit-executable "refresh" "-p" magit-stgit--marked-patch) | |||||
| (magit-run magit-stgit-executable "refresh"))) | |||||
| (defun magit-stgit-repair () | |||||
| "Repair StGit metadata if branch was modified with git commands. | |||||
| In the case of Git commits these will be imported as new patches | |||||
| into the series." | |||||
| (interactive) | |||||
| (message "Repairing series...") | |||||
| (magit-run magit-stgit-executable "repair") | |||||
| (message "")) | |||||
| (defun magit-stgit-rebase () | |||||
| "Rebase an StGit patch series." | |||||
| (interactive) | |||||
| (if (magit-get-current-remote) | |||||
| (progn | |||||
| (if (yes-or-no-p "Update remotes? ") | |||||
| (progn | |||||
| (message "Updating remotes...") | |||||
| (magit-run-git-async "remote" "update"))) | |||||
| (magit-run magit-stgit-executable "rebase" | |||||
| (format "remotes/%s/%s" | |||||
| (magit-get-current-remote) | |||||
| (magit-get-current-branch)))))) | |||||
| ;;;###autoload | |||||
| (define-minor-mode magit-stgit-mode "StGit support for Magit" | |||||
| :lighter " Stg" :require 'magit-stgit | |||||
| (or (derived-mode-p 'magit-mode) | |||||
| (error "This mode only makes sense with magit")) | |||||
| (if magit-stgit-mode | |||||
| (progn | |||||
| (add-hook 'magit-after-insert-stashes-hook 'magit-insert-series nil t)) | |||||
| (progn | |||||
| (remove-hook 'magit-after-insert-stashes-hook 'magit-insert-series t))) | |||||
| (when (called-interactively-p 'any) | |||||
| (magit-refresh))) | |||||
| ;;;###autoload | |||||
| (defun turn-on-magit-stgit () | |||||
| "Unconditionally turn on `magit-stgit-mode'." | |||||
| (magit-stgit-mode 1)) | |||||
| (provide 'magit-stgit) | |||||
| ;;; magit-stgit.el ends here | |||||
| @ -1,240 +0,0 @@ | |||||
| ;;; 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 | |||||
| @ -1,191 +0,0 @@ | |||||
| ;;; magit-topgit.el --- topgit plug-in for Magit | |||||
| ;; Copyright (C) 2010 Nathan Weizenbaum | |||||
| ;; 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 topgit functionality as a separate component of Magit | |||||
| ;;; Code: | |||||
| (require 'magit) | |||||
| (eval-when-compile | |||||
| (require 'cl)) | |||||
| (defcustom magit-topgit-executable "tg" | |||||
| "The name of the TopGit executable." | |||||
| :group 'magit | |||||
| :type 'string) | |||||
| (defcustom magit-topgit-branch-prefix "t/" | |||||
| "Convention prefix for topic branch creation." | |||||
| :group 'magit | |||||
| :type 'string) | |||||
| (defface magit-topgit-current | |||||
| '((t :weight bold :inherit magit-branch)) | |||||
| "Face for section titles." | |||||
| :group 'magit-faces) | |||||
| ;;; Topic branches (using topgit) | |||||
| (defun magit-topgit-in-topic-p () | |||||
| (and (file-exists-p ".topdeps") | |||||
| (executable-find magit-topgit-executable))) | |||||
| (defun magit-topgit-create-branch (branch parent) | |||||
| (when (zerop (or (string-match magit-topgit-branch-prefix branch) -1)) | |||||
| (magit-run* (list magit-topgit-executable "create" | |||||
| branch (magit-rev-to-git parent)) | |||||
| nil nil nil t) | |||||
| t)) | |||||
| (defun magit-topgit-pull () | |||||
| (when (magit-topgit-in-topic-p) | |||||
| (magit-run* (list magit-topgit-executable "update") | |||||
| nil nil nil t) | |||||
| t)) | |||||
| (defun magit-topgit-push () | |||||
| (when (magit-topgit-in-topic-p) | |||||
| (let* ((branch (or (magit-get-current-branch) | |||||
| (error "Don't push a detached head. That's gross"))) | |||||
| (remote (magit-get "topgit" "remote")) | |||||
| (push-remote (if (or current-prefix-arg (not remote)) | |||||
| (magit-read-remote (format "Push %s to" branch)) | |||||
| remote))) | |||||
| (when (and (not remote) | |||||
| (not current-prefix-arg)) | |||||
| (magit-set push-remote "topgit" "remote")) | |||||
| (magit-run magit-topgit-executable "push" "-r" push-remote)) | |||||
| t)) | |||||
| (defun magit-topgit-remote-update (&optional remote) | |||||
| (when (magit-topgit-in-topic-p) | |||||
| (let* ((remote (magit-get "topgit" "remote")) | |||||
| (remote-update (if (or current-prefix-arg (not remote)) | |||||
| (magit-read-remote) | |||||
| remote))) | |||||
| (if (and (not remote) | |||||
| (not current-prefix-arg)) | |||||
| (progn | |||||
| (magit-set remote-update "topgit" "remote") | |||||
| (magit-run magit-topgit-executable "remote" | |||||
| "--populate" remote-update))) | |||||
| (magit-run magit-topgit-executable "remote" remote-update)) | |||||
| ;; We return nil anyway, as we also want regular "git remote update" to | |||||
| ;; happen | |||||
| nil)) | |||||
| (defun magit-topgit-parse-flags (flags-string) | |||||
| (let ((flags (string-to-list flags-string)) | |||||
| (void-flag ?\ )) | |||||
| (list :current (not (eq (nth 0 flags) void-flag)) | |||||
| :empty (not (eq (nth 1 flags) void-flag))))) | |||||
| (defun magit-topgit-wash-topic () | |||||
| (let ((fmt "^\\(.\\{7\\}\\)\\s-\\(\\S-+\\)\\s-+\\(.*\\)")) | |||||
| (if (search-forward-regexp fmt (line-end-position) t) | |||||
| (let ((flags (magit-topgit-parse-flags (match-string 1))) | |||||
| (topic (match-string 2))) | |||||
| (goto-char (line-beginning-position)) | |||||
| (delete-char 8) | |||||
| (insert "\t") | |||||
| (goto-char (line-beginning-position)) | |||||
| (magit-with-section topic 'topic | |||||
| (magit-set-section-info topic) | |||||
| (let ((beg (1+ (line-beginning-position))) | |||||
| (end (line-end-position))) | |||||
| (when (plist-get flags :current) | |||||
| (put-text-property beg end 'face 'magit-topgit-current)) | |||||
| (when (plist-get flags :empty) | |||||
| (put-text-property beg end 'face `(:strike-through t :inherit ,(get-text-property beg 'face))))) | |||||
| (forward-line))) | |||||
| (delete-region (line-beginning-position) (1+ (line-end-position)))) | |||||
| t)) | |||||
| (defun magit-topgit-wash-topics () | |||||
| (let ((magit-old-top-section nil)) | |||||
| (magit-wash-sequence #'magit-topgit-wash-topic))) | |||||
| (defun magit-topgit-section (section title washer &rest args) | |||||
| (when (executable-find magit-topgit-executable) | |||||
| (let ((magit-git-executable magit-topgit-executable) | |||||
| (magit-git-standard-options nil)) | |||||
| (apply 'magit-git-section section title washer args)))) | |||||
| (magit-define-inserter topics () | |||||
| (magit-topgit-section 'topics | |||||
| "Topics:" 'magit-topgit-wash-topics | |||||
| "summary")) | |||||
| (magit-add-action (item info "discard") | |||||
| ((topic) | |||||
| (when (yes-or-no-p "Discard topic? ") | |||||
| (magit-run* (list magit-topgit-executable "delete" "-f" info) | |||||
| nil nil nil t)))) | |||||
| (magit-add-action (item info "visit") | |||||
| ((topic) | |||||
| (magit-checkout info))) | |||||
| (defun magit-topgit-get-top-bases-color (suffix) | |||||
| (list nil nil)) | |||||
| (defun magit-topgit-get-remote-top-bases-color (suffix) | |||||
| (when (string-match "^\\(?:[^/]+\\)/top-bases" suffix) | |||||
| (list nil nil))) | |||||
| (defconst magit-topgit-ignored-namespace | |||||
| '("top-bases" magit-topgit-get-top-bases-color)) | |||||
| ;;;###autoload | |||||
| (define-minor-mode magit-topgit-mode "Topgit support for Magit" | |||||
| :lighter " Topgit" :require 'magit-topgit | |||||
| (or (derived-mode-p 'magit-mode) | |||||
| (error "This mode only makes sense with magit")) | |||||
| (if magit-topgit-mode | |||||
| (progn | |||||
| (add-hook 'magit-after-insert-stashes-hook 'magit-insert-topics nil t) | |||||
| (add-hook 'magit-create-branch-command-hook 'magit-topgit-create-branch nil t) | |||||
| (add-hook 'magit-pull-command-hook 'magit-topgit-pull nil t) | |||||
| (add-hook 'magit-remote-update-command-hook 'magit-topgit-remote-update nil t) | |||||
| (add-hook 'magit-push-command-hook 'magit-topgit-push nil t) | |||||
| ;; hide refs for top-bases namespace in any remote | |||||
| (add-hook 'magit-log-remotes-color-hook | |||||
| 'magit-topgit-get-remote-top-bases-color) | |||||
| ;; hide refs in the top-bases namespace, as they're not meant for the user | |||||
| (add-to-list 'magit-refs-namespaces magit-topgit-ignored-namespace)) | |||||
| (progn | |||||
| (remove-hook 'magit-after-insert-stashes-hook 'magit-insert-topics t) | |||||
| (remove-hook 'magit-create-branch-command-hook 'magit-topgit-create-branch t) | |||||
| (remove-hook 'magit-pull-command-hook 'magit-topgit-pull t) | |||||
| (remove-hook 'magit-remote-update-command-hook 'magit-topgit-remote-update t) | |||||
| (remove-hook 'magit-push-command-hook 'magit-topgit-push t) | |||||
| (remove-hook 'magit-log-remotes-color-hook | |||||
| 'magit-topgit-get-remote-top-bases-color) | |||||
| (delete magit-topgit-ignored-namespace magit-refs-namespaces))) | |||||
| (when (called-interactively-p 'any) | |||||
| (magit-refresh))) | |||||
| ;;;###autoload | |||||
| (defun turn-on-magit-topgit () | |||||
| "Unconditionally turn on `magit-topgit-mode'." | |||||
| (magit-topgit-mode 1)) | |||||
| (provide 'magit-topgit) | |||||
| ;;; magit-topgit.el ends here | |||||
| @ -1,153 +0,0 @@ | |||||
| ;;; magit-wip.el --- git-wip plug-in for Magit | |||||
| ;; Copyright (C) 2012 Jonas Bernoulli | |||||
| ;; Copyright (C) 2012 Ryan C. Thompson | |||||
| ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li> | |||||
| ;; 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 support for special work-in-progress refs. | |||||
| ;; This requires the third-party git command "git wip" which is available | |||||
| ;; from https://github.com/bartman/git-wip. | |||||
| ;; The global mode `magit-wip-mode' provides highlighting of wip refs in | |||||
| ;; Magit buffers while the local mode `magit-wip-save-mode' commits to | |||||
| ;; such a ref when saving a file-visiting buffer. | |||||
| ;; To enable `magit-wip-save-mode' enable `global-magit-wip-save-mode' | |||||
| ;; and use the Magit extension mechanism to select the repositories in | |||||
| ;; which you want to use a work-in-progress ref. Usually you also want | |||||
| ;; to enable `magit-wip-mode'. | |||||
| ;; | |||||
| ;; (magit-wip-mode 1) | |||||
| ;; (global-magit-wip-save-mode 1) | |||||
| ;; | |||||
| ;; $ git config --add magit.extension wip-save # or | |||||
| ;; $ git config --global --add magit.extension wip-save | |||||
| ;; Note that `global-magit-wip-save-mode' is the only mode that uses the | |||||
| ;; extension mechanism for file-visiting buffers all other global modes | |||||
| ;; making use of it to turn on local modes in Magit buffers. | |||||
| ;;; Code: | |||||
| (require 'magit) | |||||
| (require 'format-spec) | |||||
| ;;; Magit Wip Mode. | |||||
| (defface magit-log-head-label-wip | |||||
| '((((class color) (background light)) | |||||
| :box t | |||||
| :background "Grey95" | |||||
| :foreground "LightSkyBlue3") | |||||
| (((class color) (background dark)) | |||||
| :box t | |||||
| :background "Grey07" | |||||
| :foreground "LightSkyBlue4")) | |||||
| "Face for git-wip labels shown in log buffer." | |||||
| :group 'magit-faces) | |||||
| (defun magit-log-get-wip-color (suffix) | |||||
| (list (concat "(WIP) " suffix) | |||||
| 'magit-log-head-label-wip)) | |||||
| (defconst magit-wip-refs-namespace | |||||
| '("wip" magit-log-get-wip-color)) | |||||
| ;;;###autoload | |||||
| (define-minor-mode magit-wip-mode | |||||
| "In Magit log buffers; give wip refs a special appearance." | |||||
| :group 'magit | |||||
| :global t | |||||
| (if magit-wip-mode | |||||
| (add-to-list 'magit-refs-namespaces magit-wip-refs-namespace 'append) | |||||
| (setq magit-refs-namespaces | |||||
| (delete magit-wip-refs-namespace magit-refs-namespaces)))) | |||||
| ;;; Magit Wip Save Mode. | |||||
| (defcustom magit-wip-commit-message "WIP %r" | |||||
| "Commit message for git-wip commits. | |||||
| The following `format'-like specs are supported: | |||||
| %f the full name of the file being saved, and | |||||
| %r the name of the file being saved, relative to the repository root | |||||
| %g the root of the git repository." | |||||
| :group 'magit | |||||
| :type 'string) | |||||
| (defcustom magit-wip-echo-area-message "Wrote %f (wip)" | |||||
| "Message shown in the echo area after creating a git-wip commit. | |||||
| The following `format'-like specs are supported: | |||||
| %f the full name of the file being saved, and | |||||
| %r the name of the file being saved, relative to the repository root. | |||||
| %g the root of the git repository." | |||||
| :group 'magit | |||||
| :type '(choice (const :tag "No message" nil) string)) | |||||
| (defvar magit-wip-save-mode-lighter " Wip") | |||||
| ;;;###autoload | |||||
| (define-minor-mode magit-wip-save-mode | |||||
| "Magit support for committing to a work-in-progress ref. | |||||
| When this minor mode is turned on and a file is saved inside a writable | |||||
| git repository then it is also committed to a special work-in-progress | |||||
| ref." | |||||
| :lighter magit-wip-save-mode-lighter | |||||
| (if magit-wip-save-mode | |||||
| (add-hook 'after-save-hook 'magit-wip-save-safe t t) | |||||
| (remove-hook 'after-save-hook 'magit-wip-save-safe t))) | |||||
| ;;;###autoload | |||||
| (define-globalized-minor-mode global-magit-wip-save-mode | |||||
| magit-wip-save-mode turn-on-magit-wip-save | |||||
| :group 'magit) | |||||
| (defun turn-on-magit-wip-save () | |||||
| (when (and (buffer-file-name) | |||||
| (magit-get-top-dir default-directory) | |||||
| (member "wip-save" (magit-get-all "magit.extension"))) | |||||
| (if (= (magit-git-exit-code "wip" "-h") 0) | |||||
| (magit-wip-save-mode 1) | |||||
| (message "Git command 'git wip' cannot be found")))) | |||||
| (defun magit-wip-save-safe () | |||||
| (condition-case err | |||||
| (magit-wip-save) | |||||
| (error | |||||
| (message "Magit WIP got an error: %S" err)))) | |||||
| (defun magit-wip-save () | |||||
| (let* ((top-dir (magit-get-top-dir default-directory)) | |||||
| (name (file-truename (buffer-file-name))) | |||||
| (spec `((?r . ,(file-relative-name name top-dir)) | |||||
| (?f . ,(buffer-file-name)) | |||||
| (?g . ,top-dir)))) | |||||
| (when (and top-dir (file-writable-p top-dir)) | |||||
| (save-excursion ; kludge see https://github.com/magit/magit/issues/441 | |||||
| (magit-run-git "wip" "save" | |||||
| (format-spec magit-wip-commit-message spec) | |||||
| "--editor" "--" name)) | |||||
| (when magit-wip-echo-area-message | |||||
| (message (format-spec magit-wip-echo-area-message spec)))))) | |||||
| (provide 'magit-wip) | |||||
| ;;; magit-wip.el ends here | |||||
| @ -1,322 +0,0 @@ | |||||
| ;;; rebase-mode -- edit git rebase files. | |||||
| ;; Copyright (C) 2010 Phil Jackson | |||||
| ;; Copyright (C) 2011 Peter J Weisberg | |||||
| ;; | |||||
| ;; 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: | |||||
| ;; Allows the editing of a git rebase file (which you might get when | |||||
| ;; using 'git rebase -i' or hitting 'E' in Magit). Assumes editing is | |||||
| ;; happening in a server. | |||||
| ;;; Code: | |||||
| (require 'server) | |||||
| (defgroup rebase-mode nil | |||||
| "Customize Rebase Mode" | |||||
| :group 'faces) | |||||
| (defface rebase-mode-killed-action-face | |||||
| '((((class color)) | |||||
| :inherit font-lock-comment-face | |||||
| :strike-through t)) | |||||
| "Action lines in the rebase TODO list that have been commented out." | |||||
| :group 'rebase-mode) | |||||
| (defface rebase-mode-description-face | |||||
| '((t :inherit font-lock-comment-face)) | |||||
| "Face for one-line commit descriptions" | |||||
| :group 'rebase-mode) | |||||
| (defconst rebase-mode-action-line-re | |||||
| (rx | |||||
| line-start | |||||
| (? "#") | |||||
| (group | |||||
| (| | |||||
| (any "presf") | |||||
| "pick" | |||||
| "reword" | |||||
| "edit" | |||||
| "squash" | |||||
| "fixup")) | |||||
| (char space) | |||||
| (group | |||||
| (** 4 40 hex-digit)) ;sha1 | |||||
| (char space) | |||||
| (group | |||||
| (* not-newline))) | |||||
| "Regexp that matches an action line in a rebase buffer.") | |||||
| (defconst rebase-mode-exec-line-re | |||||
| (rx | |||||
| line-start | |||||
| (? "#") | |||||
| (group | |||||
| (| "x" | |||||
| "exec")) | |||||
| (char space) | |||||
| (group | |||||
| (* not-newline))) | |||||
| "Regexp that matches an exec line in a rebase buffer.") | |||||
| (defconst rebase-mode-dead-line-re | |||||
| (rx-to-string `(and line-start | |||||
| (char ?#) | |||||
| (or (regexp ,(substring rebase-mode-action-line-re 1)) | |||||
| (regexp ,(substring rebase-mode-exec-line-re 1)))) t) | |||||
| "Regexp that matches a commented-out exec or action line in a rebase buffer.") | |||||
| (defvar rebase-mode-font-lock-keywords | |||||
| (list | |||||
| (list rebase-mode-action-line-re | |||||
| '(1 font-lock-keyword-face) | |||||
| '(2 font-lock-builtin-face) | |||||
| '(3 'rebase-mode-description-face)) | |||||
| (list rebase-mode-exec-line-re | |||||
| '(1 font-lock-keyword-face)) | |||||
| (list (rx line-start (char "#") (* not-newline)) 0 font-lock-comment-face) | |||||
| (list rebase-mode-dead-line-re 0 ''rebase-mode-killed-action-face t)) | |||||
| "Font lock keywords for `rebase-mode'.") | |||||
| (defvar key-to-action-map | |||||
| '(("c" . "pick") | |||||
| ("r" . "reword") | |||||
| ("e" . "edit") | |||||
| ("s" . "squash") | |||||
| ("f" . "fixup")) | |||||
| "Mapping from key to action.") | |||||
| (defvar rebase-mode-map | |||||
| (let ((map (make-sparse-keymap))) | |||||
| (define-key map (kbd "q") 'server-edit) | |||||
| (define-key map (kbd "C-c C-c") 'server-edit) | |||||
| (define-key map (kbd "a") 'rebase-mode-abort) | |||||
| (define-key map (kbd "C-c C-k") 'rebase-mode-abort) | |||||
| (define-key map (kbd "M-p") 'rebase-mode-move-line-up) | |||||
| (define-key map (kbd "M-n") 'rebase-mode-move-line-down) | |||||
| (define-key map (kbd "k") 'rebase-mode-kill-line) | |||||
| (define-key map (kbd "x") 'rebase-mode-exec) | |||||
| (define-key map (kbd "n") 'forward-line) | |||||
| (define-key map (kbd "p") '(lambda(n) | |||||
| (interactive "p") | |||||
| (forward-line (* n -1)))) | |||||
| (define-key map [remap undo] 'rebase-mode-undo) | |||||
| map) | |||||
| "Keymap for rebase-mode. Note this will be added to by the | |||||
| top-level code which defines the edit functions.") | |||||
| (require 'easymenu) | |||||
| (easy-menu-define rebase-mode-menu rebase-mode-map | |||||
| "Rebase-mode menu" | |||||
| '("Rebase" | |||||
| ["Pick" rebase-mode-pick t] | |||||
| ["Reword" rebase-mode-reword t] | |||||
| ["Edit" rebase-mode-edit t] | |||||
| ["Squash" rebase-mode-squash t] | |||||
| ["Fixup" rebase-mode-fixup t] | |||||
| ["Kill" rebase-mode-kill-line t] | |||||
| ["Move Down" rebase-mode-move-line-down t] | |||||
| ["Move Up" rebase-mode-move-line-up t] | |||||
| ["Execute" rebase-mode-exec t] | |||||
| "---" | |||||
| ["Abort" rebase-mode-abort t] | |||||
| ["Done" server-edit t])) | |||||
| ;; create the functions which edit the action lines themselves (based | |||||
| ;; on `key-to-action-map' above) | |||||
| (mapc (lambda (key-action) | |||||
| (let ((fun-name (intern (concat "rebase-mode-" (cdr key-action))))) | |||||
| ;; define the function | |||||
| (eval `(defun ,fun-name () | |||||
| (interactive) | |||||
| (rebase-mode-edit-line ,(cdr key-action)))) | |||||
| ;; bind the function in `rebase-mode-map' | |||||
| (define-key rebase-mode-map (car key-action) fun-name))) | |||||
| key-to-action-map) | |||||
| (defun rebase-mode-edit-line (change-to) | |||||
| "Change the keyword at the start of the current action line to | |||||
| that of CHANGE-TO." | |||||
| (when (rebase-mode-looking-at-action) | |||||
| (let ((buffer-read-only nil) | |||||
| (start (point))) | |||||
| (goto-char (point-at-bol)) | |||||
| (delete-region (point) (progn (forward-word 1) (point))) | |||||
| (insert change-to) | |||||
| (goto-char start)))) | |||||
| (defun rebase-mode-looking-at-action () | |||||
| "Return non-nil if looking at an action line." | |||||
| (save-excursion | |||||
| (goto-char (point-at-bol)) | |||||
| (looking-at rebase-mode-action-line-re))) | |||||
| (defun rebase-mode-looking-at-action-or-exec () | |||||
| "Return non-nil if looking at an action line or exec line." | |||||
| (save-excursion | |||||
| (goto-char (point-at-bol)) | |||||
| (or (looking-at rebase-mode-action-line-re) | |||||
| (looking-at rebase-mode-exec-line-re)))) | |||||
| (defun rebase-mode-looking-at-exec () | |||||
| "Return non-nil if cursor is on an exec line." | |||||
| (string-match rebase-mode-exec-line-re (thing-at-point 'line))) | |||||
| (defun rebase-mode-looking-at-killed-exec () | |||||
| "Return non-nil if looking at an exec line that has been commented out" | |||||
| (let ((line (thing-at-point 'line))) | |||||
| (and (eq (aref line 0) ?#) | |||||
| (string-match rebase-mode-exec-line-re line)))) | |||||
| (defun rebase-mode-move-line-up () | |||||
| "Move the current action line up." | |||||
| (interactive) | |||||
| (when (rebase-mode-looking-at-action-or-exec) | |||||
| (let ((buffer-read-only nil) | |||||
| (col (current-column))) | |||||
| (transpose-lines 1) | |||||
| (forward-line -2) | |||||
| (move-to-column col)))) | |||||
| (defun rebase-mode-move-line-down () | |||||
| "Assuming the next line is also an action line, move the current line down." | |||||
| (interactive) | |||||
| ;; if we're on an action and the next line is also an action | |||||
| (when (and (rebase-mode-looking-at-action-or-exec) | |||||
| (save-excursion | |||||
| (forward-line) | |||||
| (rebase-mode-looking-at-action-or-exec))) | |||||
| (let ((buffer-read-only nil) | |||||
| (col (current-column))) | |||||
| (forward-line 1) | |||||
| (transpose-lines 1) | |||||
| (forward-line -1) | |||||
| (move-to-column col)))) | |||||
| (defun rebase-mode-abort () | |||||
| "Abort this rebase (by emptying the buffer, saving and closing | |||||
| server connection)." | |||||
| (interactive) | |||||
| (when (or (not (buffer-modified-p)) | |||||
| (y-or-n-p "Abort this rebase? ")) | |||||
| (let ((buffer-read-only nil)) | |||||
| (delete-region (point-min) (point-max)) | |||||
| (save-buffer) | |||||
| (server-edit)))) | |||||
| (defun rebase-mode-kill-line () | |||||
| "Kill the current action line." | |||||
| (interactive) | |||||
| (when (and (not (eq (char-after (point-at-bol)) ?#)) | |||||
| (rebase-mode-looking-at-action-or-exec)) | |||||
| (beginning-of-line) | |||||
| (let ((buffer-read-only nil)) | |||||
| (insert "#")) | |||||
| (forward-line))) | |||||
| (defun rebase-mode-exec (edit) | |||||
| "Prompt the user for a shell command to be executed, and add it to | |||||
| the todo list. | |||||
| If the cursor is on a commented-out exec line, uncomment the | |||||
| current line instead of prompting. | |||||
| When the prefix argument EDIT is non-nil and the cursor is on an | |||||
| exec line, edit that line instead of inserting a new one. If the | |||||
| exec line was commented out, also uncomment it." | |||||
| (interactive "P") | |||||
| (cond | |||||
| ((and edit (rebase-mode-looking-at-exec)) | |||||
| (let ((new-line (rebase-mode-read-exec-line | |||||
| (match-string-no-properties 2 (thing-at-point 'line)))) | |||||
| (inhibit-read-only t)) | |||||
| (delete-region (point-at-bol) (point-at-eol)) | |||||
| (if (not (equal "" new-line)) | |||||
| (insert "exec " new-line) | |||||
| (delete-char -1) | |||||
| (forward-line)) | |||||
| (move-beginning-of-line nil))) | |||||
| ((rebase-mode-looking-at-killed-exec) | |||||
| (save-excursion | |||||
| (beginning-of-line) | |||||
| (let ((buffer-read-only nil)) | |||||
| (delete-char 1)))) | |||||
| (t | |||||
| (let ((inhibit-read-only t) | |||||
| (line (rebase-mode-read-exec-line))) | |||||
| (unless (equal "" line) | |||||
| (move-end-of-line nil) | |||||
| (newline) | |||||
| (insert (concat "exec " line)))) | |||||
| (move-beginning-of-line nil)))) | |||||
| (defun rebase-mode-read-exec-line (&optional initial-line) | |||||
| (read-shell-command "Execute: " initial-line)) | |||||
| (defun rebase-mode-undo (&optional arg) | |||||
| "A thin wrapper around `undo', which allows undoing in | |||||
| read-only buffers." | |||||
| (interactive "P") | |||||
| (let ((inhibit-read-only t)) | |||||
| (undo arg))) | |||||
| ;;;###autoload | |||||
| (define-derived-mode rebase-mode special-mode "Rebase" | |||||
| "Major mode for editing of a Git rebase file. | |||||
| Rebase files are generated when you run 'git rebase -i' or run | |||||
| `magit-interactive-rebase'. They describe how Git should perform | |||||
| the rebase. See the documentation for git-rebase (e.g., by | |||||
| running 'man git-rebase' at the command line) for details." | |||||
| (setq font-lock-defaults '(rebase-mode-font-lock-keywords t t))) | |||||
| (defun rebase-mode-show-keybindings () | |||||
| "Modify the \"Commands:\" section of the comment Git generates | |||||
| at the bottom of the file so that in place of the one-letter | |||||
| abbreviation for the command, it shows the command's keybinding. | |||||
| By default, this is the same except for the \"pick\" command." | |||||
| (save-excursion | |||||
| (goto-char (point-min)) | |||||
| (while (search-forward-regexp "^# \\(.\\), \\([[:alpha:]]+\\) = " nil t) | |||||
| (let ((start (match-beginning 1)) | |||||
| (end (match-end 1)) | |||||
| (command (intern (concat "rebase-mode-" (match-string 2))))) | |||||
| (when (fboundp command) | |||||
| (let ((overlay (make-overlay start end))) | |||||
| (overlay-put overlay | |||||
| 'display | |||||
| (key-description (where-is-internal command nil t))))))))) | |||||
| (add-hook 'rebase-mode-hook 'rebase-mode-show-keybindings t) | |||||
| (defun rebase-mode-disable-before-save-hook () | |||||
| (set (make-local-variable 'before-save-hook) nil)) | |||||
| (add-hook 'rebase-mode-hook 'rebase-mode-disable-before-save-hook) | |||||
| ;;;###autoload | |||||
| (add-to-list 'auto-mode-alist | |||||
| '("git-rebase-todo" . rebase-mode)) | |||||
| (provide 'rebase-mode) | |||||
| ;;; rebase-mode.el ends here | |||||