diff --git a/elpa/async-20160223.146/async-autoloads.el b/elpa/async-20160223.146/async-autoloads.el new file mode 100644 index 0000000..0d67ddb --- /dev/null +++ b/elpa/async-20160223.146/async-autoloads.el @@ -0,0 +1,129 @@ +;;; async-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "async" "async.el" (22221 60697 422000 0)) +;;; Generated autoloads from async.el + +(autoload 'async-start-process "async" "\ +Start the executable PROGRAM asynchronously. See `async-start'. +PROGRAM is passed PROGRAM-ARGS, calling FINISH-FUNC with the +process object when done. If FINISH-FUNC is nil, the future +object will return the process object when the program is +finished. Set DEFAULT-DIRECTORY to change PROGRAM's current +working directory. + +\(fn NAME PROGRAM FINISH-FUNC &rest PROGRAM-ARGS)" nil nil) + +(autoload 'async-start "async" "\ +Execute START-FUNC (often a lambda) in a subordinate Emacs process. +When done, the return value is passed to FINISH-FUNC. Example: + + (async-start + ;; What to do in the child process + (lambda () + (message \"This is a test\") + (sleep-for 3) + 222) + + ;; What to do when it finishes + (lambda (result) + (message \"Async process done, result should be 222: %s\" + result))) + +If FINISH-FUNC is nil or missing, a future is returned that can +be inspected using `async-get', blocking until the value is +ready. Example: + + (let ((proc (async-start + ;; What to do in the child process + (lambda () + (message \"This is a test\") + (sleep-for 3) + 222)))) + + (message \"I'm going to do some work here\") ;; .... + + (message \"Waiting on async process, result should be 222: %s\" + (async-get proc))) + +If you don't want to use a callback, and you don't care about any +return value from the child process, pass the `ignore' symbol as +the second argument (if you don't, and never call `async-get', it +will leave *emacs* process buffers hanging around): + + (async-start + (lambda () + (delete-file \"a remote file on a slow link\" nil)) + 'ignore) + +Note: Even when FINISH-FUNC is present, a future is still +returned except that it yields no value (since the value is +passed to FINISH-FUNC). Call `async-get' on such a future always +returns nil. It can still be useful, however, as an argument to +`async-ready' or `async-wait'. + +\(fn START-FUNC &optional FINISH-FUNC)" nil nil) + +;;;*** + +;;;### (autoloads nil "async-bytecomp" "async-bytecomp.el" (22221 +;;;;;; 60697 419000 0)) +;;; Generated autoloads from async-bytecomp.el + +(autoload 'async-byte-recompile-directory "async-bytecomp" "\ +Compile all *.el files in DIRECTORY asynchronously. +All *.elc files are systematically deleted before proceeding. + +\(fn DIRECTORY &optional QUIET)" nil nil) + +(defvar async-bytecomp-package-mode nil "\ +Non-nil if Async-Bytecomp-Package mode is enabled. +See the command `async-bytecomp-package-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 `async-bytecomp-package-mode'.") + +(custom-autoload 'async-bytecomp-package-mode "async-bytecomp" nil) + +(autoload 'async-bytecomp-package-mode "async-bytecomp" "\ +Byte compile asynchronously packages installed with package.el. +Async compilation of packages can be controlled by +`async-bytecomp-allowed-packages'. + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil "dired-async" "dired-async.el" (22221 60697 +;;;;;; 412000 0)) +;;; Generated autoloads from dired-async.el + +(defvar dired-async-mode nil "\ +Non-nil if Dired-Async mode is enabled. +See the command `dired-async-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 `dired-async-mode'.") + +(custom-autoload 'dired-async-mode "dired-async" nil) + +(autoload 'dired-async-mode "dired-async" "\ +Do dired actions asynchronously. + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil nil ("async-pkg.el" "smtpmail-async.el") (22221 +;;;;;; 60697 432884 878000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; async-autoloads.el ends here diff --git a/elpa/async-20160223.146/async-bytecomp.el b/elpa/async-20160223.146/async-bytecomp.el new file mode 100644 index 0000000..54313c0 --- /dev/null +++ b/elpa/async-20160223.146/async-bytecomp.el @@ -0,0 +1,177 @@ +;;; async-bytecomp.el --- Async functions to compile elisp files async + +;; Copyright (C) 2014-2016 Free Software Foundation, Inc. + +;; Authors: John Wiegley +;; Thierry Volpiatto + +;; Keywords: dired async byte-compile +;; X-URL: https://github.com/jwiegley/dired-async + +;; This program 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 2, or (at +;; your option) any later version. + +;; This program 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: +;; +;; This package provide the `async-byte-recompile-directory' function +;; which allows, as the name says to recompile a directory outside of +;; your running emacs. +;; The benefit is your files will be compiled in a clean environment without +;; the old *.el files loaded. +;; Among other things, this fix a bug in package.el which recompile +;; the new files in the current environment with the old files loaded, creating +;; errors in most packages after upgrades. +;; +;; NB: This package is advicing the function `package--compile'. + +;;; Code: + +(require 'cl-lib) +(require 'async) + +(defcustom async-bytecomp-allowed-packages + '(async helm helm-core helm-ls-git helm-ls-hg magit) + "Packages in this list will be compiled asynchronously by `package--compile'. +All the dependencies of these packages will be compiled async too, +so no need to add dependencies to this list. +The value of this variable can also be a list with a single element, +the symbol `all', in this case packages are always compiled asynchronously." + :group 'async + :type '(repeat (choice symbol))) + +(defvar async-byte-compile-log-file "~/.emacs.d/async-bytecomp.log") + +;;;###autoload +(defun async-byte-recompile-directory (directory &optional quiet) + "Compile all *.el files in DIRECTORY asynchronously. +All *.elc files are systematically deleted before proceeding." + (cl-loop with dir = (directory-files directory t "\\.elc\\'") + unless dir return nil + for f in dir + when (file-exists-p f) do (delete-file f)) + ;; Ensure async is reloaded when async.elc is deleted. + ;; This happen when recompiling its own directory. + (load "async") + (let ((call-back + `(lambda (&optional ignore) + (if (file-exists-p async-byte-compile-log-file) + (let ((buf (get-buffer-create byte-compile-log-buffer)) + (n 0)) + (with-current-buffer buf + (goto-char (point-max)) + (let ((inhibit-read-only t)) + (insert-file-contents async-byte-compile-log-file) + (compilation-mode)) + (display-buffer buf) + (delete-file async-byte-compile-log-file) + (unless ,quiet + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "^.*:Error:" nil t) + (cl-incf n))) + (if (> n 0) + (message "Failed to compile %d files in directory `%s'" n ,directory) + (message "Directory `%s' compiled asynchronously with warnings" ,directory))))) + (unless ,quiet + (message "Directory `%s' compiled asynchronously with success" ,directory)))))) + (async-start + `(lambda () + (require 'bytecomp) + ,(async-inject-variables "\\`\\(load-path\\)\\|byte\\'") + (let ((default-directory (file-name-as-directory ,directory)) + error-data) + (add-to-list 'load-path default-directory) + (byte-recompile-directory ,directory 0 t) + (when (get-buffer byte-compile-log-buffer) + (setq error-data (with-current-buffer byte-compile-log-buffer + (buffer-substring-no-properties (point-min) (point-max)))) + (unless (string= error-data "") + (with-temp-file ,async-byte-compile-log-file + (erase-buffer) + (insert error-data)))))) + call-back) + (unless quiet (message "Started compiling asynchronously directory %s" directory)))) + +(defvar package-archive-contents) +(defvar package-alist) +(declare-function package-desc-reqs "package.el" (cl-x)) + +(defun async-bytecomp--get-package-deps (pkg &optional only) + ;; Same as `package--get-deps' but parse instead `package-archive-contents' + ;; because PKG is not already installed and not present in `package-alist'. + ;; However fallback to `package-alist' in case PKG no more present + ;; in `package-archive-contents' due to modification to `package-archives'. + ;; See issue #58. + (let* ((pkg-desc (cadr (or (assq pkg package-archive-contents) + (assq pkg package-alist)))) + (direct-deps (cl-loop for p in (package-desc-reqs pkg-desc) + for name = (car p) + when (or (assq name package-archive-contents) + (assq name package-alist)) + collect name)) + (indirect-deps (unless (eq only 'direct) + (delete-dups + (cl-loop for p in direct-deps append + (async-bytecomp--get-package-deps p)))))) + (cl-case only + (direct direct-deps) + (separate (list direct-deps indirect-deps)) + (indirect indirect-deps) + (t (delete-dups (append direct-deps indirect-deps)))))) + +(defun async-bytecomp-get-allowed-pkgs () + (when (and async-bytecomp-allowed-packages + (listp async-bytecomp-allowed-packages)) + (if package-archive-contents + (cl-loop for p in async-bytecomp-allowed-packages + when (assq p package-archive-contents) + append (async-bytecomp--get-package-deps p) into reqs + finally return + (delete-dups + (append async-bytecomp-allowed-packages reqs))) + async-bytecomp-allowed-packages))) + +(defadvice package--compile (around byte-compile-async) + (let ((cur-package (package-desc-name pkg-desc)) + (pkg-dir (package-desc-dir pkg-desc))) + (if (or (equal async-bytecomp-allowed-packages '(all)) + (memq cur-package (async-bytecomp-get-allowed-pkgs))) + (progn + (when (eq cur-package 'async) + (fmakunbound 'async-byte-recompile-directory)) + ;; Add to `load-path' the latest version of async and + ;; reload it when reinstalling async. + (when (string= cur-package "async") + (cl-pushnew pkg-dir load-path) + (load "async-bytecomp")) + ;; `async-byte-recompile-directory' will add directory + ;; as needed to `load-path'. + (async-byte-recompile-directory (package-desc-dir pkg-desc) t)) + ad-do-it))) + +;;;###autoload +(define-minor-mode async-bytecomp-package-mode + "Byte compile asynchronously packages installed with package.el. +Async compilation of packages can be controlled by +`async-bytecomp-allowed-packages'." + :group 'async + :global t + (if async-bytecomp-package-mode + (ad-activate 'package--compile) + (ad-deactivate 'package--compile))) + +(provide 'async-bytecomp) + +;;; async-bytecomp.el ends here diff --git a/elpa/async-20160223.146/async-pkg.el b/elpa/async-20160223.146/async-pkg.el new file mode 100644 index 0000000..2610c83 --- /dev/null +++ b/elpa/async-20160223.146/async-pkg.el @@ -0,0 +1,6 @@ +(define-package "async" "20160223.146" "Asynchronous processing in Emacs" 'nil :keywords + '("async") + :url "http://elpa.gnu.org/packages/async.html") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/async-20160223.146/async.el b/elpa/async-20160223.146/async.el new file mode 100644 index 0000000..080fd34 --- /dev/null +++ b/elpa/async-20160223.146/async.el @@ -0,0 +1,303 @@ +;;; async.el --- Asynchronous processing in Emacs + +;; Copyright (C) 2012-2016 Free Software Foundation, Inc. + +;; Author: John Wiegley +;; Created: 18 Jun 2012 +;; Version: 1.6 + +;; Keywords: async +;; X-URL: https://github.com/jwiegley/emacs-async + +;; This program 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 2, or (at +;; your option) any later version. + +;; This program 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Adds the ability to call asynchronous functions and process with ease. See +;; the documentation for `async-start' and `async-start-process'. + +;;; Code: + +(defgroup async nil + "Simple asynchronous processing in Emacs" + :group 'emacs) + +(defvar async-debug nil) +(defvar async-send-over-pipe t) +(defvar async-in-child-emacs nil) +(defvar async-callback nil) +(defvar async-callback-for-process nil) +(defvar async-callback-value nil) +(defvar async-callback-value-set nil) +(defvar async-current-process nil) +(defvar async--procvar nil) + +(defun async-inject-variables + (include-regexp &optional predicate exclude-regexp) + "Return a `setq' form that replicates part of the calling environment. +It sets the value for every variable matching INCLUDE-REGEXP and +also PREDICATE. It will not perform injection for any variable +matching EXCLUDE-REGEXP (if present). It is intended to be used +as follows: + + (async-start + `(lambda () + (require 'smtpmail) + (with-temp-buffer + (insert ,(buffer-substring-no-properties (point-min) (point-max))) + ;; Pass in the variable environment for smtpmail + ,(async-inject-variables \"\\`\\(smtpmail\\|\\(user-\\)?mail\\)-\") + (smtpmail-send-it))) + 'ignore)" + `(setq + ,@(let (bindings) + (mapatoms + (lambda (sym) + (if (and (boundp sym) + (or (null include-regexp) + (string-match include-regexp (symbol-name sym))) + (not (string-match + (or exclude-regexp "-syntax-table\\'") + (symbol-name sym)))) + (let ((value (symbol-value sym))) + (when (or (null predicate) + (funcall predicate sym)) + (setq bindings (cons `(quote ,value) bindings) + bindings (cons sym bindings))))))) + bindings))) + +(defalias 'async-inject-environment 'async-inject-variables) + +(defun async-handle-result (func result buf) + (if (null func) + (progn + (set (make-local-variable 'async-callback-value) result) + (set (make-local-variable 'async-callback-value-set) t)) + (unwind-protect + (if (and (listp result) + (eq 'async-signal (nth 0 result))) + (signal (car (nth 1 result)) + (cdr (nth 1 result))) + (funcall func result)) + (unless async-debug + (kill-buffer buf))))) + +(defun async-when-done (proc &optional change) + "Process sentinal used to retrieve the value from the child process." + (when (eq 'exit (process-status proc)) + (with-current-buffer (process-buffer proc) + (let ((async-current-process proc)) + (if (= 0 (process-exit-status proc)) + (if async-callback-for-process + (if async-callback + (prog1 + (funcall async-callback proc) + (unless async-debug + (kill-buffer (current-buffer)))) + (set (make-local-variable 'async-callback-value) proc) + (set (make-local-variable 'async-callback-value-set) t)) + (goto-char (point-max)) + (backward-sexp) + (async-handle-result async-callback (read (current-buffer)) + (current-buffer))) + (set (make-local-variable 'async-callback-value) + (list 'error + (format "Async process '%s' failed with exit code %d" + (process-name proc) (process-exit-status proc)))) + (set (make-local-variable 'async-callback-value-set) t)))))) + +(defun async--receive-sexp (&optional stream) + (let ((sexp (decode-coding-string (base64-decode-string + (read stream)) 'utf-8-unix)) + ;; Parent expects UTF-8 encoded text. + (coding-system-for-write 'utf-8-unix)) + (if async-debug + (message "Received sexp {{{%s}}}" (pp-to-string sexp))) + (setq sexp (read sexp)) + (if async-debug + (message "Read sexp {{{%s}}}" (pp-to-string sexp))) + (eval sexp))) + +(defun async--insert-sexp (sexp) + (let (print-level + print-length + (print-escape-nonascii t) + (print-circle t)) + (prin1 sexp (current-buffer)) + ;; Just in case the string we're sending might contain EOF + (encode-coding-region (point-min) (point-max) 'utf-8-unix) + (base64-encode-region (point-min) (point-max) t) + (goto-char (point-min)) (insert ?\") + (goto-char (point-max)) (insert ?\" ?\n))) + +(defun async--transmit-sexp (process sexp) + (with-temp-buffer + (if async-debug + (message "Transmitting sexp {{{%s}}}" (pp-to-string sexp))) + (async--insert-sexp sexp) + (process-send-region process (point-min) (point-max)))) + +(defun async-batch-invoke () + "Called from the child Emacs process' command-line." + ;; Make sure 'message' and 'prin1' encode stuff in UTF-8, as parent + ;; process expects. + (let ((coding-system-for-write 'utf-8-unix)) + (setq async-in-child-emacs t + debug-on-error async-debug) + (if debug-on-error + (prin1 (funcall + (async--receive-sexp (unless async-send-over-pipe + command-line-args-left)))) + (condition-case err + (prin1 (funcall + (async--receive-sexp (unless async-send-over-pipe + command-line-args-left)))) + (error + (prin1 (list 'async-signal err))))))) + +(defun async-ready (future) + "Query a FUTURE to see if the ready is ready -- i.e., if no blocking +would result from a call to `async-get' on that FUTURE." + (and (memq (process-status future) '(exit signal)) + (with-current-buffer (process-buffer future) + async-callback-value-set))) + +(defun async-wait (future) + "Wait for FUTURE to become ready." + (while (not (async-ready future)) + (sit-for 0.05))) + +(defun async-get (future) + "Get the value from an asynchronously function when it is ready. +FUTURE is returned by `async-start' or `async-start-process' when +its FINISH-FUNC is nil." + (async-wait future) + (with-current-buffer (process-buffer future) + (async-handle-result #'identity async-callback-value (current-buffer)))) + +(defun async-message-p (value) + "Return true of VALUE is an async.el message packet." + (and (listp value) + (plist-get value :async-message))) + +(defun async-send (&rest args) + "Send the given messages to the asychronous Emacs PROCESS." + (let ((args (append args '(:async-message t)))) + (if async-in-child-emacs + (if async-callback + (funcall async-callback args)) + (async--transmit-sexp (car args) (list 'quote (cdr args)))))) + +(defun async-receive (&rest args) + "Send the given messages to the asychronous Emacs PROCESS." + (async--receive-sexp)) + +;;;###autoload +(defun async-start-process (name program finish-func &rest program-args) + "Start the executable PROGRAM asynchronously. See `async-start'. +PROGRAM is passed PROGRAM-ARGS, calling FINISH-FUNC with the +process object when done. If FINISH-FUNC is nil, the future +object will return the process object when the program is +finished. Set DEFAULT-DIRECTORY to change PROGRAM's current +working directory." + (let* ((buf (generate-new-buffer (concat "*" name "*"))) + (proc (let ((process-connection-type nil)) + (apply #'start-process name buf program program-args)))) + (with-current-buffer buf + (set (make-local-variable 'async-callback) finish-func) + (set-process-sentinel proc #'async-when-done) + (unless (string= name "emacs") + (set (make-local-variable 'async-callback-for-process) t)) + proc))) + +;;;###autoload +(defun async-start (start-func &optional finish-func) + "Execute START-FUNC (often a lambda) in a subordinate Emacs process. +When done, the return value is passed to FINISH-FUNC. Example: + + (async-start + ;; What to do in the child process + (lambda () + (message \"This is a test\") + (sleep-for 3) + 222) + + ;; What to do when it finishes + (lambda (result) + (message \"Async process done, result should be 222: %s\" + result))) + +If FINISH-FUNC is nil or missing, a future is returned that can +be inspected using `async-get', blocking until the value is +ready. Example: + + (let ((proc (async-start + ;; What to do in the child process + (lambda () + (message \"This is a test\") + (sleep-for 3) + 222)))) + + (message \"I'm going to do some work here\") ;; .... + + (message \"Waiting on async process, result should be 222: %s\" + (async-get proc))) + +If you don't want to use a callback, and you don't care about any +return value from the child process, pass the `ignore' symbol as +the second argument (if you don't, and never call `async-get', it +will leave *emacs* process buffers hanging around): + + (async-start + (lambda () + (delete-file \"a remote file on a slow link\" nil)) + 'ignore) + +Note: Even when FINISH-FUNC is present, a future is still +returned except that it yields no value (since the value is +passed to FINISH-FUNC). Call `async-get' on such a future always +returns nil. It can still be useful, however, as an argument to +`async-ready' or `async-wait'." + (let ((sexp start-func) + ;; Subordinate Emacs will send text encoded in UTF-8. + (coding-system-for-read 'utf-8-unix)) + (setq async--procvar + (async-start-process + "emacs" (file-truename + (expand-file-name invocation-name + invocation-directory)) + finish-func + "-Q" "-l" + ;; Using `locate-library' ensure we use the right file + ;; when the .elc have been deleted. + (locate-library "async") + "-batch" "-f" "async-batch-invoke" + (if async-send-over-pipe + "" + (with-temp-buffer + (async--insert-sexp (list 'quote sexp)) + (buffer-string))))) + (if async-send-over-pipe + (async--transmit-sexp async--procvar (list 'quote sexp))) + async--procvar)) + +(defmacro async-sandbox(func) + "Evaluate FUNC in a separate Emacs process, synchronously." + `(async-get (async-start ,func))) + +(provide 'async) + +;;; async.el ends here diff --git a/elpa/async-20160223.146/dired-async.el b/elpa/async-20160223.146/dired-async.el new file mode 100644 index 0000000..ecab9cb --- /dev/null +++ b/elpa/async-20160223.146/dired-async.el @@ -0,0 +1,290 @@ +;;; dired-async.el --- Copy/move/delete asynchronously in dired. + +;; Copyright (C) 2012-2016 Free Software Foundation, Inc. + +;; Authors: John Wiegley +;; Thierry Volpiatto + +;; Keywords: dired async network +;; X-URL: https://github.com/jwiegley/dired-async + +;; This program 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 2, or (at +;; your option) any later version. + +;; This program 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; This file provide a redefinition of `dired-create-file' function, +;; performs copies, moves and all what is handled by `dired-create-file' +;; in the background using a slave Emacs process, +;; by means of the async.el module. +;; To use it, put this in your .emacs: + +;; (dired-async-mode 1) + +;; This will enable async copy/rename etc... +;; in dired and helm. + +;;; Code: + +(require 'cl-lib) +(require 'dired-aux) +(require 'async) + +(eval-when-compile + (defvar async-callback)) +(defvar dired-async-operation nil) + +(defgroup dired-async nil + "Copy rename files asynchronously from dired." + :group 'dired) + +(defcustom dired-async-env-variables-regexp + "\\`\\(tramp-\\(default\\|connection\\|remote\\)\\|ange-ftp\\)-.*" + "Variables matching this regexp will be loaded on Child Emacs." + :type 'regexp + :group 'dired-async) + +(defcustom dired-async-message-function 'dired-async-mode-line-message + "Function to use to notify result when operation finish. +Should take same args as `message'." + :group 'dired-async + :type 'function) + +(defcustom dired-async-log-file "/tmp/dired-async.log" + "File use to communicate errors from Child Emacs to host Emacs." + :group 'dired-async + :type 'string) + +(defface dired-async-message + '((t (:foreground "yellow"))) + "Face used for mode-line message." + :group 'dired-async) + +(defface dired-async-mode-message + '((t (:foreground "Gold"))) + "Face used for `dired-async--modeline-mode' lighter." + :group 'dired-async) + +(define-minor-mode dired-async--modeline-mode + "Notify mode-line that an async process run." + :group 'dired-async + :global t + :lighter (:eval (propertize (format " [%s Async job(s) running]" + (length (dired-async-processes))) + 'face 'dired-async-mode-message)) + (unless dired-async--modeline-mode + (let ((visible-bell t)) (ding)))) + +(defun dired-async-mode-line-message (text &rest args) + "Notify end of operation in `mode-line'." + (message nil) + (let ((mode-line-format (concat + " " (propertize + (if args + (apply #'format text args) + text) + 'face 'dired-async-message)))) + (force-mode-line-update) + (sit-for 3) + (force-mode-line-update))) + +(defun dired-async-processes () + (cl-loop for p in (process-list) + when (cl-loop for c in (process-command p) thereis + (string= "async-batch-invoke" c)) + collect p)) + +(defun dired-async-kill-process () + (interactive) + (let* ((processes (dired-async-processes)) + (proc (car (last processes)))) + (delete-process proc) + (unless (> (length processes) 1) + (dired-async--modeline-mode -1)))) + +(defun dired-async-after-file-create (len-flist) + "Callback function used for operation handled by `dired-create-file'." + (unless (dired-async-processes) + ;; Turn off mode-line notification + ;; only when last process end. + (dired-async--modeline-mode -1)) + (when dired-async-operation + (if (file-exists-p dired-async-log-file) + (progn + (pop-to-buffer (get-buffer-create "*dired async*")) + (erase-buffer) + (insert "Error: ") + (insert-file-contents dired-async-log-file) + (delete-file dired-async-log-file)) + (run-with-timer + 0.1 nil + dired-async-message-function "Asynchronous %s of %s file(s) on %s file(s) done" + (car dired-async-operation) (cadr dired-async-operation) len-flist)))) + +(defun dired-async-maybe-kill-ftp () + "Return a form to kill ftp process in child emacs." + (quote + (progn + (require 'cl-lib) + (let ((buf (cl-loop for b in (buffer-list) + thereis (and (string-match + "\\`\\*ftp.*" + (buffer-name b)) b)))) + (when buf (kill-buffer buf)))))) + +(defun dired-async-create-files (file-creator operation fn-list name-constructor + &optional marker-char) + "Same as `dired-create-files' but asynchronous. + +See `dired-create-files' for the behavior of arguments." + (setq dired-async-operation nil) + (let (dired-create-files-failures + failures async-fn-list + skipped (success-count 0) + (total (length fn-list)) + callback) + (let (to overwrite-query + overwrite-backup-query) ; for dired-handle-overwrite + (dolist (from fn-list) + (setq to (funcall name-constructor from)) + (if (equal to from) + (progn + (setq to nil) + (dired-log "Cannot %s to same file: %s\n" + (downcase operation) from))) + (if (not to) + (setq skipped (cons (dired-make-relative from) skipped)) + (let* ((overwrite (file-exists-p to)) + (dired-overwrite-confirmed ; for dired-handle-overwrite + (and overwrite + (let ((help-form '(format "\ +Type SPC or `y' to overwrite file `%s', +DEL or `n' to skip to next, +ESC or `q' to not overwrite any of the remaining files, +`!' to overwrite all remaining files with no more questions." to))) + (dired-query 'overwrite-query + "Overwrite `%s'?" to)))) + ;; must determine if FROM is marked before file-creator + ;; gets a chance to delete it (in case of a move). + (actual-marker-char + (cond ((integerp marker-char) marker-char) + (marker-char (dired-file-marker from)) ; slow + (t nil)))) + ;; Handle the `dired-copy-file' file-creator specially + ;; When copying a directory to another directory or + ;; possibly to itself or one of its subdirectories. + ;; e.g "~/foo/" => "~/test/" + ;; or "~/foo/" =>"~/foo/" + ;; or "~/foo/ => ~/foo/bar/") + ;; In this case the 'name-constructor' have set the destination + ;; TO to "~/test/foo" because the old emacs23 behavior + ;; of `copy-directory' was to not create the subdirectory + ;; and instead copy the contents. + ;; With the new behavior of `copy-directory' + ;; (similar to the `cp' shell command) we don't + ;; need such a construction of the target directory, + ;; so modify the destination TO to "~/test/" instead of "~/test/foo/". + (let ((destname (file-name-directory to))) + (when (and (file-directory-p from) + (file-directory-p to) + (eq file-creator 'dired-copy-file)) + (setq to destname)) + ;; If DESTNAME is a subdirectory of FROM, not a symlink, + ;; and the method in use is copying, signal an error. + (and (eq t (car (file-attributes destname))) + (eq file-creator 'dired-copy-file) + (file-in-directory-p destname from) + (error "Cannot copy `%s' into its subdirectory `%s'" + from to))) + (if overwrite + (or (and dired-overwrite-confirmed + (push (cons from to) async-fn-list)) + (progn + (push (dired-make-relative from) failures) + (dired-log "%s `%s' to `%s' failed" + operation from to))) + (push (cons from to) async-fn-list))))) + (setq callback + `(lambda (&optional ignore) + (dired-async-after-file-create ,total) + (when (string= ,(downcase operation) "rename") + (cl-loop for (file . to) in ',async-fn-list + do (and (get-file-buffer file) + (with-current-buffer (get-file-buffer file) + (set-visited-file-name to nil t)))))))) + ;; Handle error happening in host emacs. + (cond + (dired-create-files-failures + (setq failures (nconc failures dired-create-files-failures)) + (dired-log-summary + (format "%s failed for %d file%s in %d requests" + operation (length failures) + (dired-plural-s (length failures)) + total) + failures)) + (failures + (dired-log-summary + (format "%s failed for %d of %d file%s" + operation (length failures) + total (dired-plural-s total)) + failures)) + (skipped + (dired-log-summary + (format "%s: %d of %d file%s skipped" + operation (length skipped) total + (dired-plural-s total)) + skipped)) + (t (message "%s: %s file%s" + operation success-count (dired-plural-s success-count)))) + ;; Start async process. + (when async-fn-list + (async-start `(lambda () + (require 'cl-lib) (require 'dired-aux) (require 'dired-x) + ,(async-inject-variables dired-async-env-variables-regexp) + (condition-case err + (let ((dired-recursive-copies (quote always))) + (cl-loop for (f . d) in (quote ,async-fn-list) + do (funcall (quote ,file-creator) f d t))) + (file-error + (with-temp-file ,dired-async-log-file + (insert (format "%S" err))))) + ,(dired-async-maybe-kill-ftp)) + callback) + ;; Run mode-line notifications while process running. + (dired-async--modeline-mode 1) + (setq dired-async-operation (list operation (length async-fn-list))) + (message "%s proceeding asynchronously..." operation)))) + +(defadvice dired-create-files (around dired-async) + (dired-async-create-files file-creator operation fn-list + name-constructor marker-char)) + +;;;###autoload +(define-minor-mode dired-async-mode + "Do dired actions asynchronously." + :group 'dired-async + :global t + (if dired-async-mode + (if (fboundp 'advice-add) + (advice-add 'dired-create-files :override #'dired-async-create-files) + (ad-activate 'dired-create-files)) + (if (fboundp 'advice-remove) + (advice-remove 'dired-create-files #'dired-async-create-files) + (ad-deactivate 'dired-create-files)))) + + +(provide 'dired-async) + +;;; dired-async.el ends here diff --git a/elpa/async-20160223.146/smtpmail-async.el b/elpa/async-20160223.146/smtpmail-async.el new file mode 100644 index 0000000..5ac426d --- /dev/null +++ b/elpa/async-20160223.146/smtpmail-async.el @@ -0,0 +1,73 @@ +;;; smtpmail-async.el --- Send e-mail with smtpmail.el asynchronously + +;; Copyright (C) 2012-2016 Free Software Foundation, Inc. + +;; Author: John Wiegley +;; Created: 18 Jun 2012 + +;; Keywords: email async +;; X-URL: https://github.com/jwiegley/emacs-async + +;; This program 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 2, or (at +;; your option) any later version. + +;; This program 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Send e-mail with smtpmail.el asynchronously. To use: +;; +;; (require 'smtpmail-async) +;; +;; (setq send-mail-function 'async-smtpmail-send-it +;; message-send-mail-function 'async-smtpmail-send-it) +;; +;; This assumes you already have smtpmail.el working. + +;;; Code: + +(defgroup smtpmail-async nil + "Send e-mail with smtpmail.el asynchronously" + :group 'smptmail) + +(require 'async) +(require 'smtpmail) +(require 'message) + +(defvar async-smtpmail-before-send-hook nil + "Hook running in the child emacs in `async-smtpmail-send-it'. +It is called just before calling `smtpmail-send-it'.") + +(defun async-smtpmail-send-it () + (let ((to (message-field-value "To")) + (buf-content (buffer-substring-no-properties + (point-min) (point-max)))) + (message "Delivering message to %s..." to) + (async-start + `(lambda () + (require 'smtpmail) + (with-temp-buffer + (insert ,buf-content) + (set-buffer-multibyte nil) + ;; Pass in the variable environment for smtpmail + ,(async-inject-variables + "\\`\\(smtpmail\\|async-smtpmail\\|\\(user-\\)?mail\\)-\\|auth-sources\\|epg" + nil "\\`\\(mail-header-format-function\\|smtpmail-address-buffer\\|mail-mode-abbrev-table\\)") + (run-hooks 'async-smtpmail-before-send-hook) + (smtpmail-send-it))) + `(lambda (&optional ignore) + (message "Delivering message to %s...done" ,to))))) + +(provide 'smtpmail-async) + +;;; smtpmail-async.el ends here diff --git a/elpa/dash-20160223.1028/dash-autoloads.el b/elpa/dash-20160223.1028/dash-autoloads.el new file mode 100644 index 0000000..46014cc --- /dev/null +++ b/elpa/dash-20160223.1028/dash-autoloads.el @@ -0,0 +1,15 @@ +;;; dash-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil nil ("dash.el") (22221 60697 93676 700000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; dash-autoloads.el ends here diff --git a/elpa/dash-20160223.1028/dash-pkg.el b/elpa/dash-20160223.1028/dash-pkg.el new file mode 100644 index 0000000..6e0f516 --- /dev/null +++ b/elpa/dash-20160223.1028/dash-pkg.el @@ -0,0 +1 @@ +(define-package "dash" "20160223.1028" "A modern list library for Emacs" 'nil :keywords '("lists")) diff --git a/elpa/dash-20160223.1028/dash.el b/elpa/dash-20160223.1028/dash.el new file mode 100644 index 0000000..f9cae39 --- /dev/null +++ b/elpa/dash-20160223.1028/dash.el @@ -0,0 +1,2441 @@ +;;; dash.el --- A modern list library for Emacs -*- lexical-binding: t -*- + +;; Copyright (C) 2012-2015 Free Software Foundation, Inc. + +;; Author: Magnar Sveen +;; Version: 2.12.1 +;; Package-Version: 20160223.1028 +;; Keywords: lists + +;; This program 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 of the License, or +;; (at your option) any later version. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; A modern list api for Emacs. +;; +;; See documentation on https://github.com/magnars/dash.el#functions +;; +;; **Please note** The lexical binding in this file is not utilised at the +;; moment. We will take full advantage of lexical binding in an upcoming 3.0 +;; release of Dash. In the meantime, we've added the pragma to avoid a bug that +;; you can read more about in https://github.com/magnars/dash.el/issues/130. +;; + +;;; Code: + +(defgroup dash () + "Customize group for dash.el" + :group 'lisp + :prefix "dash-") + +(defun dash--enable-fontlock (symbol value) + (when value + (dash-enable-font-lock)) + (set-default symbol value)) + +(defcustom dash-enable-fontlock nil + "If non-nil, enable fontification of dash functions, macros and +special values." + :type 'boolean + :set 'dash--enable-fontlock + :group 'dash) + +(defmacro !cons (car cdr) + "Destructive: Set CDR to the cons of CAR and CDR." + `(setq ,cdr (cons ,car ,cdr))) + +(defmacro !cdr (list) + "Destructive: Set LIST to the cdr of LIST." + `(setq ,list (cdr ,list))) + +(defmacro --each (list &rest body) + "Anaphoric form of `-each'." + (declare (debug (form body)) + (indent 1)) + (let ((l (make-symbol "list"))) + `(let ((,l ,list) + (it-index 0)) + (while ,l + (let ((it (car ,l))) + ,@body) + (setq it-index (1+ it-index)) + (!cdr ,l))))) + +(defun -each (list fn) + "Call FN with every item in LIST. Return nil, used for side-effects only." + (--each list (funcall fn it))) + +(put '-each 'lisp-indent-function 1) + +(defmacro --each-while (list pred &rest body) + "Anaphoric form of `-each-while'." + (declare (debug (form form body)) + (indent 2)) + (let ((l (make-symbol "list")) + (c (make-symbol "continue"))) + `(let ((,l ,list) + (,c t) + (it-index 0)) + (while (and ,l ,c) + (let ((it (car ,l))) + (if (not ,pred) (setq ,c nil) ,@body)) + (setq it-index (1+ it-index)) + (!cdr ,l))))) + +(defun -each-while (list pred fn) + "Call FN with every item in LIST while (PRED item) is non-nil. +Return nil, used for side-effects only." + (--each-while list (funcall pred it) (funcall fn it))) + +(put '-each-while 'lisp-indent-function 2) + +(defmacro --dotimes (num &rest body) + "Repeatedly executes BODY (presumably for side-effects) with `it` bound to integers from 0 through NUM-1." + (declare (debug (form body)) + (indent 1)) + (let ((n (make-symbol "num"))) + `(let ((,n ,num) + (it 0)) + (while (< it ,n) + ,@body + (setq it (1+ it)))))) + +(defun -dotimes (num fn) + "Repeatedly calls FN (presumably for side-effects) passing in integers from 0 through NUM-1." + (--dotimes num (funcall fn it))) + +(put '-dotimes 'lisp-indent-function 1) + +(defun -map (fn list) + "Return a new list consisting of the result of applying FN to the items in LIST." + (mapcar fn list)) + +(defmacro --map (form list) + "Anaphoric form of `-map'." + (declare (debug (form form))) + `(mapcar (lambda (it) ,form) ,list)) + +(defmacro --reduce-from (form initial-value list) + "Anaphoric form of `-reduce-from'." + (declare (debug (form form form))) + `(let ((acc ,initial-value)) + (--each ,list (setq acc ,form)) + acc)) + +(defun -reduce-from (fn initial-value list) + "Return the result of applying FN to INITIAL-VALUE and the +first item in LIST, then applying FN to that result and the 2nd +item, etc. If LIST contains no items, return INITIAL-VALUE and +FN is not called. + +In the anaphoric form `--reduce-from', the accumulated value is +exposed as `acc`. + +See also: `-reduce', `-reduce-r'" + (--reduce-from (funcall fn acc it) initial-value list)) + +(defmacro --reduce (form list) + "Anaphoric form of `-reduce'." + (declare (debug (form form))) + (let ((lv (make-symbol "list-value"))) + `(let ((,lv ,list)) + (if ,lv + (--reduce-from ,form (car ,lv) (cdr ,lv)) + (let (acc it) ,form))))) + +(defun -reduce (fn list) + "Return the result of applying FN to the first 2 items in LIST, +then applying FN to that result and the 3rd item, etc. If LIST +contains no items, FN must accept no arguments as well, and +reduce return the result of calling FN with no arguments. If +LIST has only 1 item, it is returned and FN is not called. + +In the anaphoric form `--reduce', the accumulated value is +exposed as `acc`. + +See also: `-reduce-from', `-reduce-r'" + (if list + (-reduce-from fn (car list) (cdr list)) + (funcall fn))) + +(defun -reduce-r-from (fn initial-value list) + "Replace conses with FN, nil with INITIAL-VALUE and evaluate +the resulting expression. If LIST is empty, INITIAL-VALUE is +returned and FN is not called. + +Note: this function works the same as `-reduce-from' but the +operation associates from right instead of from left. + +See also: `-reduce-r', `-reduce'" + (if (not list) initial-value + (funcall fn (car list) (-reduce-r-from fn initial-value (cdr list))))) + +(defmacro --reduce-r-from (form initial-value list) + "Anaphoric version of `-reduce-r-from'." + (declare (debug (form form form))) + `(-reduce-r-from (lambda (&optional it acc) ,form) ,initial-value ,list)) + +(defun -reduce-r (fn list) + "Replace conses with FN and evaluate the resulting expression. +The final nil is ignored. If LIST contains no items, FN must +accept no arguments as well, and reduce return the result of +calling FN with no arguments. If LIST has only 1 item, it is +returned and FN is not called. + +The first argument of FN is the new item, the second is the +accumulated value. + +Note: this function works the same as `-reduce' but the operation +associates from right instead of from left. + +See also: `-reduce-r-from', `-reduce'" + (cond + ((not list) (funcall fn)) + ((not (cdr list)) (car list)) + (t (funcall fn (car list) (-reduce-r fn (cdr list)))))) + +(defmacro --reduce-r (form list) + "Anaphoric version of `-reduce-r'." + (declare (debug (form form))) + `(-reduce-r (lambda (&optional it acc) ,form) ,list)) + +(defmacro --filter (form list) + "Anaphoric form of `-filter'." + (declare (debug (form form))) + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list (when ,form (!cons it ,r))) + (nreverse ,r)))) + +(defun -filter (pred list) + "Return a new list of the items in LIST for which PRED returns a non-nil value. + +Alias: `-select' + +See also: `-keep'" + (--filter (funcall pred it) list)) + +(defalias '-select '-filter) +(defalias '--select '--filter) + +(defmacro --remove (form list) + "Anaphoric form of `-remove'." + (declare (debug (form form))) + `(--filter (not ,form) ,list)) + +(defun -remove (pred list) + "Return a new list of the items in LIST for which PRED returns nil. + +Alias: `-reject'" + (--remove (funcall pred it) list)) + +(defalias '-reject '-remove) +(defalias '--reject '--remove) + +(defun -remove-first (pred list) + "Return a new list with the first item matching PRED removed. + +Alias: `-reject-first' + +See also: `-remove', `-map-first'" + (let (front) + (while (and list (not (funcall pred (car list)))) + (push (car list) front) + (!cdr list)) + (if list + (-concat (nreverse front) (cdr list)) + (nreverse front)))) + +(defmacro --remove-first (form list) + "Anaphoric form of `-remove-first'." + (declare (debug (form form))) + `(-remove-first (lambda (it) ,form) ,list)) + +(defalias '-reject-first '-remove-first) +(defalias '--reject-first '--remove-first) + +(defun -remove-last (pred list) + "Return a new list with the last item matching PRED removed. + +Alias: `-reject-last' + +See also: `-remove', `-map-last'" + (nreverse (-remove-first pred (nreverse list)))) + +(defmacro --remove-last (form list) + "Anaphoric form of `-remove-last'." + (declare (debug (form form))) + `(-remove-last (lambda (it) ,form) ,list)) + +(defalias '-reject-last '-remove-last) +(defalias '--reject-last '--remove-last) + +(defun -remove-item (item list) + "Remove all occurences of ITEM from LIST. + +Comparison is done with `equal'." + (--remove (equal it item) list)) + +(defmacro --keep (form list) + "Anaphoric form of `-keep'." + (declare (debug (form form))) + (let ((r (make-symbol "result")) + (m (make-symbol "mapped"))) + `(let (,r) + (--each ,list (let ((,m ,form)) (when ,m (!cons ,m ,r)))) + (nreverse ,r)))) + +(defun -keep (fn list) + "Return a new list of the non-nil results of applying FN to the items in LIST. + +If you want to select the original items satisfying a predicate use `-filter'." + (--keep (funcall fn it) list)) + +(defun -non-nil (list) + "Return all non-nil elements of LIST." + (-remove 'null list)) + +(defmacro --map-indexed (form list) + "Anaphoric form of `-map-indexed'." + (declare (debug (form form))) + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list + (!cons ,form ,r)) + (nreverse ,r)))) + +(defun -map-indexed (fn list) + "Return a new list consisting of the result of (FN index item) for each item in LIST. + +In the anaphoric form `--map-indexed', the index is exposed as `it-index`." + (--map-indexed (funcall fn it-index it) list)) + +(defmacro --map-when (pred rep list) + "Anaphoric form of `-map-when'." + (declare (debug (form form form))) + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list (!cons (if ,pred ,rep it) ,r)) + (nreverse ,r)))) + +(defun -map-when (pred rep list) + "Return a new list where the elements in LIST that does not match the PRED function +are unchanged, and where the elements in LIST that do match the PRED function are mapped +through the REP function. + +Alias: `-replace-where' + +See also: `-update-at'" + (--map-when (funcall pred it) (funcall rep it) list)) + +(defalias '-replace-where '-map-when) +(defalias '--replace-where '--map-when) + +(defun -map-first (pred rep list) + "Replace first item in LIST satisfying PRED with result of REP called on this item. + +See also: `-map-when', `-replace-first'" + (let (front) + (while (and list (not (funcall pred (car list)))) + (push (car list) front) + (!cdr list)) + (if list + (-concat (nreverse front) (cons (funcall rep (car list)) (cdr list))) + (nreverse front)))) + +(defmacro --map-first (pred rep list) + "Anaphoric form of `-map-first'." + `(-map-first (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) + +(defun -map-last (pred rep list) + "Replace first item in LIST satisfying PRED with result of REP called on this item. + +See also: `-map-when', `-replace-last'" + (nreverse (-map-first pred rep (nreverse list)))) + +(defmacro --map-last (pred rep list) + "Anaphoric form of `-map-last'." + `(-map-last (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) + +(defun -replace (old new list) + "Replace all OLD items in LIST with NEW. + +Elements are compared using `equal'. + +See also: `-replace-at'" + (--map-when (equal it old) new list)) + +(defun -replace-first (old new list) + "Replace the first occurence of OLD with NEW in LIST. + +Elements are compared using `equal'. + +See also: `-map-first'" + (--map-first (equal old it) new list)) + +(defun -replace-last (old new list) + "Replace the last occurence of OLD with NEW in LIST. + +Elements are compared using `equal'. + +See also: `-map-last'" + (--map-last (equal old it) new list)) + +(defmacro --mapcat (form list) + "Anaphoric form of `-mapcat'." + (declare (debug (form form))) + `(apply 'append (--map ,form ,list))) + +(defun -mapcat (fn list) + "Return the concatenation of the result of mapping FN over LIST. +Thus function FN should return a list." + (--mapcat (funcall fn it) list)) + +(defun -flatten (l) + "Take a nested list L and return its contents as a single, flat list. + +Note that because `nil' represents a list of zero elements (an +empty list), any mention of nil in L will disappear after +flattening. If you need to preserve nils, consider `-flatten-n' +or map them to some unique symbol and then map them back. + +Conses of two atoms are considered \"terminals\", that is, they +aren't flattened further. + +See also: `-flatten-n'" + (if (and (listp l) (listp (cdr l))) + (-mapcat '-flatten l) + (list l))) + +(defmacro --iterate (form init n) + "Anaphoric version of `-iterate'." + (declare (debug (form form form))) + `(-iterate (lambda (it) ,form) ,init ,n)) + +(defun -flatten-n (num list) + "Flatten NUM levels of a nested LIST. + +See also: `-flatten'" + (-last-item (--iterate (--mapcat (-list it) it) list (1+ num)))) + +(defun -concat (&rest lists) + "Return a new list with the concatenation of the elements in the supplied LISTS." + (apply 'append lists)) + +(defalias '-copy 'copy-sequence + "Create a shallow copy of LIST.") + +(defun -splice (pred fun list) + "Splice lists generated by FUN in place of elements matching PRED in LIST. + +FUN takes the element matching PRED as input. + +This function can be used as replacement for `,@' in case you +need to splice several lists at marked positions (for example +with keywords). + +See also: `-splice-list', `-insert-at'" + (let (r) + (--each list + (if (funcall pred it) + (let ((new (funcall fun it))) + (--each new (!cons it r))) + (!cons it r))) + (nreverse r))) + +(defmacro --splice (pred form list) + "Anaphoric form of `-splice'." + `(-splice (lambda (it) ,pred) (lambda (it) ,form) ,list)) + +(defun -splice-list (pred new-list list) + "Splice NEW-LIST in place of elements matching PRED in LIST. + +See also: `-splice', `-insert-at'" + (-splice pred (lambda (_) new-list) list)) + +(defmacro --splice-list (pred new-list list) + "Anaphoric form of `-splice-list'." + `(-splice-list (lambda (it) ,pred) ,new-list ,list)) + +(defun -cons* (&rest args) + "Make a new list from the elements of ARGS. + +The last 2 members of ARGS are used as the final cons of the +result so if the final member of ARGS is not a list the result is +a dotted list." + (-reduce-r 'cons args)) + +(defun -snoc (list elem &rest elements) + "Append ELEM to the end of the list. + +This is like `cons', but operates on the end of list. + +If ELEMENTS is non nil, append these to the list as well." + (-concat list (list elem) elements)) + +(defmacro --first (form list) + "Anaphoric form of `-first'." + (declare (debug (form form))) + (let ((n (make-symbol "needle"))) + `(let (,n) + (--each-while ,list (not ,n) + (when ,form (setq ,n it))) + ,n))) + +(defun -first (pred list) + "Return the first x in LIST where (PRED x) is non-nil, else nil. + +To get the first item in the list no questions asked, use `car'. + +Alias: `-find'" + (--first (funcall pred it) list)) + +(defalias '-find '-first) +(defalias '--find '--first) + +(defmacro --some (form list) + "Anaphoric form of `-some'." + (declare (debug (form form))) + (let ((n (make-symbol "needle"))) + `(let (,n) + (--each-while ,list (not ,n) + (setq ,n ,form)) + ,n))) + +(defun -some (pred list) + "Return (PRED x) for the first LIST item where (PRED x) is non-nil, else nil. + +Alias: `-any'" + (--some (funcall pred it) list)) + +(defalias '-any '-some) +(defalias '--any '--some) + +(defmacro --last (form list) + "Anaphoric form of `-last'." + (declare (debug (form form))) + (let ((n (make-symbol "needle"))) + `(let (,n) + (--each ,list + (when ,form (setq ,n it))) + ,n))) + +(defun -last (pred list) + "Return the last x in LIST where (PRED x) is non-nil, else nil." + (--last (funcall pred it) list)) + +(defalias '-first-item 'car + "Return the first item of LIST, or nil on an empty list.") + +(defun -last-item (list) + "Return the last item of LIST, or nil on an empty list." + (car (last list))) + +(defun -butlast (list) + "Return a list of all items in list except for the last." + (let (result) + (while (cdr list) + (!cons (car list) result) + (!cdr list)) + (nreverse result))) + +(defmacro --count (pred list) + "Anaphoric form of `-count'." + (declare (debug (form form))) + (let ((r (make-symbol "result"))) + `(let ((,r 0)) + (--each ,list (when ,pred (setq ,r (1+ ,r)))) + ,r))) + +(defun -count (pred list) + "Counts the number of items in LIST where (PRED item) is non-nil." + (--count (funcall pred it) list)) + +(defun ---truthy? (val) + (not (null val))) + +(defmacro --any? (form list) + "Anaphoric form of `-any?'." + (declare (debug (form form))) + `(---truthy? (--first ,form ,list))) + +(defun -any? (pred list) + "Return t if (PRED x) is non-nil for any x in LIST, else nil. + +Alias: `-any-p', `-some?', `-some-p'" + (--any? (funcall pred it) list)) + +(defalias '-some? '-any?) +(defalias '--some? '--any?) +(defalias '-any-p '-any?) +(defalias '--any-p '--any?) +(defalias '-some-p '-any?) +(defalias '--some-p '--any?) + +(defmacro --all? (form list) + "Anaphoric form of `-all?'." + (declare (debug (form form))) + (let ((a (make-symbol "all"))) + `(let ((,a t)) + (--each-while ,list ,a (setq ,a ,form)) + (---truthy? ,a)))) + +(defun -all? (pred list) + "Return t if (PRED x) is non-nil for all x in LIST, else nil. + +Alias: `-all-p', `-every?', `-every-p'" + (--all? (funcall pred it) list)) + +(defalias '-every? '-all?) +(defalias '--every? '--all?) +(defalias '-all-p '-all?) +(defalias '--all-p '--all?) +(defalias '-every-p '-all?) +(defalias '--every-p '--all?) + +(defmacro --none? (form list) + "Anaphoric form of `-none?'." + (declare (debug (form form))) + `(--all? (not ,form) ,list)) + +(defun -none? (pred list) + "Return t if (PRED x) is nil for all x in LIST, else nil. + +Alias: `-none-p'" + (--none? (funcall pred it) list)) + +(defalias '-none-p '-none?) +(defalias '--none-p '--none?) + +(defmacro --only-some? (form list) + "Anaphoric form of `-only-some?'." + (declare (debug (form form))) + (let ((y (make-symbol "yes")) + (n (make-symbol "no"))) + `(let (,y ,n) + (--each-while ,list (not (and ,y ,n)) + (if ,form (setq ,y t) (setq ,n t))) + (---truthy? (and ,y ,n))))) + +(defun -only-some? (pred list) + "Return `t` if at least one item of LIST matches PRED and at least one item of LIST does not match PRED. +Return `nil` both if all items match the predicate or if none of the items match the predicate. + +Alias: `-only-some-p'" + (--only-some? (funcall pred it) list)) + +(defalias '-only-some-p '-only-some?) +(defalias '--only-some-p '--only-some?) + +(defun -slice (list from &optional to step) + "Return copy of LIST, starting from index FROM to index TO. + +FROM or TO may be negative. These values are then interpreted +modulo the length of the list. + +If STEP is a number, only each STEPth item in the resulting +section is returned. Defaults to 1." + (let ((length (length list)) + (new-list nil)) + ;; to defaults to the end of the list + (setq to (or to length)) + (setq step (or step 1)) + ;; handle negative indices + (when (< from 0) + (setq from (mod from length))) + (when (< to 0) + (setq to (mod to length))) + + ;; iterate through the list, keeping the elements we want + (--each-while list (< it-index to) + (when (and (>= it-index from) + (= (mod (- from it-index) step) 0)) + (push it new-list))) + (nreverse new-list))) + +(defun -take (n list) + "Return a new list of the first N items in LIST, or all items if there are fewer than N." + (let (result) + (--dotimes n + (when list + (!cons (car list) result) + (!cdr list))) + (nreverse result))) + +(defalias '-drop 'nthcdr "Return the tail of LIST without the first N items.") + +(defmacro --take-while (form list) + "Anaphoric form of `-take-while'." + (declare (debug (form form))) + (let ((r (make-symbol "result"))) + `(let (,r) + (--each-while ,list ,form (!cons it ,r)) + (nreverse ,r)))) + +(defun -take-while (pred list) + "Return a new list of successive items from LIST while (PRED item) returns a non-nil value." + (--take-while (funcall pred it) list)) + +(defmacro --drop-while (form list) + "Anaphoric form of `-drop-while'." + (declare (debug (form form))) + (let ((l (make-symbol "list"))) + `(let ((,l ,list)) + (while (and ,l (let ((it (car ,l))) ,form)) + (!cdr ,l)) + ,l))) + +(defun -drop-while (pred list) + "Return the tail of LIST starting from the first item for which (PRED item) returns nil." + (--drop-while (funcall pred it) list)) + +(defun -split-at (n list) + "Return a list of ((-take N LIST) (-drop N LIST)), in no more than one pass through the list." + (let (result) + (--dotimes n + (when list + (!cons (car list) result) + (!cdr list))) + (list (nreverse result) list))) + +(defun -rotate (n list) + "Rotate LIST N places to the right. With N negative, rotate to the left. +The time complexity is O(n)." + (if (> n 0) + (append (last list n) (butlast list n)) + (append (-drop (- n) list) (-take (- n) list)))) + +(defun -insert-at (n x list) + "Return a list with X inserted into LIST at position N. + +See also: `-splice', `-splice-list'" + (let ((split-list (-split-at n list))) + (nconc (car split-list) (cons x (cadr split-list))))) + +(defun -replace-at (n x list) + "Return a list with element at Nth position in LIST replaced with X. + +See also: `-replace'" + (let ((split-list (-split-at n list))) + (nconc (car split-list) (cons x (cdr (cadr split-list)))))) + +(defun -update-at (n func list) + "Return a list with element at Nth position in LIST replaced with `(func (nth n list))`. + +See also: `-map-when'" + (let ((split-list (-split-at n list))) + (nconc (car split-list) (cons (funcall func (car (cadr split-list))) (cdr (cadr split-list)))))) + +(defmacro --update-at (n form list) + "Anaphoric version of `-update-at'." + (declare (debug (form form form))) + `(-update-at ,n (lambda (it) ,form) ,list)) + +(defun -remove-at (n list) + "Return a list with element at Nth position in LIST removed. + +See also: `-remove-at-indices', `-remove'" + (-remove-at-indices (list n) list)) + +(defun -remove-at-indices (indices list) + "Return a list whose elements are elements from LIST without +elements selected as `(nth i list)` for all i +from INDICES. + +See also: `-remove-at', `-remove'" + (let* ((indices (-sort '< indices)) + (diffs (cons (car indices) (-map '1- (-zip-with '- (cdr indices) indices)))) + r) + (--each diffs + (let ((split (-split-at it list))) + (!cons (car split) r) + (setq list (cdr (cadr split))))) + (!cons list r) + (apply '-concat (nreverse r)))) + +(defmacro --split-with (pred list) + "Anaphoric form of `-split-with'." + (declare (debug (form form))) + (let ((l (make-symbol "list")) + (r (make-symbol "result")) + (c (make-symbol "continue"))) + `(let ((,l ,list) + (,r nil) + (,c t)) + (while (and ,l ,c) + (let ((it (car ,l))) + (if (not ,pred) + (setq ,c nil) + (!cons it ,r) + (!cdr ,l)))) + (list (nreverse ,r) ,l)))) + +(defun -split-with (pred list) + "Return a list of ((-take-while PRED LIST) (-drop-while PRED LIST)), in no more than one pass through the list." + (--split-with (funcall pred it) list)) + +(defmacro -split-on (item list) + "Split the LIST each time ITEM is found. + +Unlike `-partition-by', the ITEM is discarded from the results. +Empty lists are also removed from the result. + +Comparison is done by `equal'. + +See also `-split-when'" + (declare (debug (form form))) + `(-split-when (lambda (it) (equal it ,item)) ,list)) + +(defmacro --split-when (form list) + "Anaphoric version of `-split-when'." + (declare (debug (form form))) + `(-split-when (lambda (it) ,form) ,list)) + +(defun -split-when (fn list) + "Split the LIST on each element where FN returns non-nil. + +Unlike `-partition-by', the \"matched\" element is discarded from +the results. Empty lists are also removed from the result. + +This function can be thought of as a generalization of +`split-string'." + (let (r s) + (while list + (if (not (funcall fn (car list))) + (push (car list) s) + (when s (push (nreverse s) r)) + (setq s nil)) + (!cdr list)) + (when s (push (nreverse s) r)) + (nreverse r))) + +(defmacro --separate (form list) + "Anaphoric form of `-separate'." + (declare (debug (form form))) + (let ((y (make-symbol "yes")) + (n (make-symbol "no"))) + `(let (,y ,n) + (--each ,list (if ,form (!cons it ,y) (!cons it ,n))) + (list (nreverse ,y) (nreverse ,n))))) + +(defun -separate (pred list) + "Return a list of ((-filter PRED LIST) (-remove PRED LIST)), in one pass through the list." + (--separate (funcall pred it) list)) + +(defun ---partition-all-in-steps-reversed (n step list) + "Private: Used by -partition-all-in-steps and -partition-in-steps." + (when (< step 1) + (error "Step must be a positive number, or you're looking at some juicy infinite loops.")) + (let ((result nil)) + (while list + (!cons (-take n list) result) + (setq list (-drop step list))) + result)) + +(defun -partition-all-in-steps (n step list) + "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. +The last groups may contain less than N items." + (nreverse (---partition-all-in-steps-reversed n step list))) + +(defun -partition-in-steps (n step list) + "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. +If there are not enough items to make the last group N-sized, +those items are discarded." + (let ((result (---partition-all-in-steps-reversed n step list))) + (while (and result (< (length (car result)) n)) + (!cdr result)) + (nreverse result))) + +(defun -partition-all (n list) + "Return a new list with the items in LIST grouped into N-sized sublists. +The last group may contain less than N items." + (-partition-all-in-steps n n list)) + +(defun -partition (n list) + "Return a new list with the items in LIST grouped into N-sized sublists. +If there are not enough items to make the last group N-sized, +those items are discarded." + (-partition-in-steps n n list)) + +(defmacro --partition-by (form list) + "Anaphoric form of `-partition-by'." + (declare (debug (form form))) + (let ((r (make-symbol "result")) + (s (make-symbol "sublist")) + (v (make-symbol "value")) + (n (make-symbol "new-value")) + (l (make-symbol "list"))) + `(let ((,l ,list)) + (when ,l + (let* ((,r nil) + (it (car ,l)) + (,s (list it)) + (,v ,form) + (,l (cdr ,l))) + (while ,l + (let* ((it (car ,l)) + (,n ,form)) + (unless (equal ,v ,n) + (!cons (nreverse ,s) ,r) + (setq ,s nil) + (setq ,v ,n)) + (!cons it ,s) + (!cdr ,l))) + (!cons (nreverse ,s) ,r) + (nreverse ,r)))))) + +(defun -partition-by (fn list) + "Apply FN to each item in LIST, splitting it each time FN returns a new value." + (--partition-by (funcall fn it) list)) + +(defmacro --partition-by-header (form list) + "Anaphoric form of `-partition-by-header'." + (declare (debug (form form))) + (let ((r (make-symbol "result")) + (s (make-symbol "sublist")) + (h (make-symbol "header-value")) + (b (make-symbol "seen-body?")) + (n (make-symbol "new-value")) + (l (make-symbol "list"))) + `(let ((,l ,list)) + (when ,l + (let* ((,r nil) + (it (car ,l)) + (,s (list it)) + (,h ,form) + (,b nil) + (,l (cdr ,l))) + (while ,l + (let* ((it (car ,l)) + (,n ,form)) + (if (equal ,h ,n) + (when ,b + (!cons (nreverse ,s) ,r) + (setq ,s nil) + (setq ,b nil)) + (setq ,b t)) + (!cons it ,s) + (!cdr ,l))) + (!cons (nreverse ,s) ,r) + (nreverse ,r)))))) + +(defun -partition-by-header (fn list) + "Apply FN to the first item in LIST. That is the header +value. Apply FN to each item in LIST, splitting it each time FN +returns the header value, but only after seeing at least one +other value (the body)." + (--partition-by-header (funcall fn it) list)) + +(defmacro --group-by (form list) + "Anaphoric form of `-group-by'." + (declare (debug t)) + (let ((n (make-symbol "n")) + (k (make-symbol "k")) + (grp (make-symbol "grp"))) + `(nreverse + (-map + (lambda (,n) + (cons (car ,n) + (nreverse (cdr ,n)))) + (--reduce-from + (let* ((,k (,@form)) + (,grp (assoc ,k acc))) + (if ,grp + (setcdr ,grp (cons it (cdr ,grp))) + (push + (list ,k it) + acc)) + acc) + nil ,list))))) + +(defun -group-by (fn list) + "Separate LIST into an alist whose keys are FN applied to the +elements of LIST. Keys are compared by `equal'." + (--group-by (funcall fn it) list)) + +(defun -interpose (sep list) + "Return a new list of all elements in LIST separated by SEP." + (let (result) + (when list + (!cons (car list) result) + (!cdr list)) + (while list + (setq result (cons (car list) (cons sep result))) + (!cdr list)) + (nreverse result))) + +(defun -interleave (&rest lists) + "Return a new list of the first item in each list, then the second etc." + (let (result) + (while (-none? 'null lists) + (--each lists (!cons (car it) result)) + (setq lists (-map 'cdr lists))) + (nreverse result))) + +(defmacro --zip-with (form list1 list2) + "Anaphoric form of `-zip-with'. + +The elements in list1 is bound as `it`, the elements in list2 as `other`." + (declare (debug (form form form))) + (let ((r (make-symbol "result")) + (l1 (make-symbol "list1")) + (l2 (make-symbol "list2"))) + `(let ((,r nil) + (,l1 ,list1) + (,l2 ,list2)) + (while (and ,l1 ,l2) + (let ((it (car ,l1)) + (other (car ,l2))) + (!cons ,form ,r) + (!cdr ,l1) + (!cdr ,l2))) + (nreverse ,r)))) + +(defun -zip-with (fn list1 list2) + "Zip the two lists LIST1 and LIST2 using a function FN. This +function is applied pairwise taking as first argument element of +LIST1 and as second argument element of LIST2 at corresponding +position. + +The anaphoric form `--zip-with' binds the elements from LIST1 as `it`, +and the elements from LIST2 as `other`." + (--zip-with (funcall fn it other) list1 list2)) + +(defun -zip (&rest lists) + "Zip LISTS together. Group the head of each list, followed by the +second elements of each list, and so on. The lengths of the returned +groupings are equal to the length of the shortest input list. + +If two lists are provided as arguments, return the groupings as a list +of cons cells. Otherwise, return the groupings as a list of lists. + +Please note! This distinction is being removed in an upcoming 2.0 +release of Dash. If you rely on this behavior, use -zip-pair instead." + (let (results) + (while (-none? 'null lists) + (setq results (cons (mapcar 'car lists) results)) + (setq lists (mapcar 'cdr lists))) + (setq results (nreverse results)) + (if (= (length lists) 2) + ;; to support backward compatability, return + ;; a cons cell if two lists were provided + (--map (cons (car it) (cadr it)) results) + results))) + +(defalias '-zip-pair '-zip) + +(defun -zip-fill (fill-value &rest lists) + "Zip LISTS, with FILL-VALUE padded onto the shorter lists. The +lengths of the returned groupings are equal to the length of the +longest input list." + (apply '-zip (apply '-pad (cons fill-value lists)))) + +(defun -cycle (list) + "Return an infinite copy of LIST that will cycle through the +elements and repeat from the beginning." + (let ((newlist (-map 'identity list))) + (nconc newlist newlist))) + +(defun -pad (fill-value &rest lists) + "Appends FILL-VALUE to the end of each list in LISTS such that they +will all have the same length." + (let* ((annotations (-annotate 'length lists)) + (n (-max (-map 'car annotations)))) + (--map (append (cdr it) (-repeat (- n (car it)) fill-value)) annotations))) + +(defun -annotate (fn list) + "Return a list of cons cells where each cell is FN applied to each +element of LIST paired with the unmodified element of LIST." + (-zip (-map fn list) list)) + +(defmacro --annotate (form list) + "Anaphoric version of `-annotate'." + (declare (debug (form form))) + `(-annotate (lambda (it) ,form) ,list)) + +(defun dash--table-carry (lists restore-lists &optional re) + "Helper for `-table' and `-table-flat'. + +If a list overflows, carry to the right and reset the list." + (while (not (or (car lists) + (equal lists '(nil)))) + (setcar lists (car restore-lists)) + (pop (cadr lists)) + (!cdr lists) + (!cdr restore-lists) + (when re + (push (nreverse (car re)) (cadr re)) + (setcar re nil) + (!cdr re)))) + +(defun -table (fn &rest lists) + "Compute outer product of LISTS using function FN. + +The function FN should have the same arity as the number of +supplied lists. + +The outer product is computed by applying fn to all possible +combinations created by taking one element from each list in +order. The dimension of the result is (length lists). + +See also: `-table-flat'" + (let ((restore-lists (copy-sequence lists)) + (last-list (last lists)) + (re (make-list (length lists) nil))) + (while (car last-list) + (let ((item (apply fn (-map 'car lists)))) + (push item (car re)) + (setcar lists (cdar lists)) ;; silence byte compiler + (dash--table-carry lists restore-lists re))) + (nreverse (car (last re))))) + +(defun -table-flat (fn &rest lists) + "Compute flat outer product of LISTS using function FN. + +The function FN should have the same arity as the number of +supplied lists. + +The outer product is computed by applying fn to all possible +combinations created by taking one element from each list in +order. The results are flattened, ignoring the tensor structure +of the result. This is equivalent to calling: + + (-flatten-n (1- (length lists)) (-table fn lists)) + +but the implementation here is much more efficient. + +See also: `-flatten-n', `-table'" + (when lists ;Just in case. + (let* ((list1 (pop lists)) + (restore-lists (copy-sequence lists)) + (last-list (last lists)) + re) + (while (car last-list) + (let ((tail (-map #'car lists))) + (dolist (head list1) + (push (apply fn head tail) re))) + (pop (car lists)) + (dash--table-carry lists restore-lists)) + (nreverse re)))) + +(defun -partial (fn &rest args) + "Take a function FN and fewer than the normal arguments to FN, +and return a fn that takes a variable number of additional ARGS. +When called, the returned function calls FN with ARGS first and +then additional args." + (apply 'apply-partially fn args)) + +(defun -elem-index (elem list) + "Return the index of the first element in the given LIST which +is equal to the query element ELEM, or nil if there is no +such element." + (car (-elem-indices elem list))) + +(defun -elem-indices (elem list) + "Return the indices of all elements in LIST equal to the query +element ELEM, in ascending order." + (-find-indices (-partial 'equal elem) list)) + +(defun -find-indices (pred list) + "Return the indices of all elements in LIST satisfying the +predicate PRED, in ascending order." + (apply 'append (--map-indexed (when (funcall pred it) (list it-index)) list))) + +(defmacro --find-indices (form list) + "Anaphoric version of `-find-indices'." + (declare (debug (form form))) + `(-find-indices (lambda (it) ,form) ,list)) + +(defun -find-index (pred list) + "Take a predicate PRED and a LIST and return the index of the +first element in the list satisfying the predicate, or nil if +there is no such element. + +See also `-first'." + (car (-find-indices pred list))) + +(defmacro --find-index (form list) + "Anaphoric version of `-find-index'." + (declare (debug (form form))) + `(-find-index (lambda (it) ,form) ,list)) + +(defun -find-last-index (pred list) + "Take a predicate PRED and a LIST and return the index of the +last element in the list satisfying the predicate, or nil if +there is no such element. + +See also `-last'." + (-last-item (-find-indices pred list))) + +(defmacro --find-last-index (form list) + "Anaphoric version of `-find-last-index'." + `(-find-last-index (lambda (it) ,form) ,list)) + +(defun -select-by-indices (indices list) + "Return a list whose elements are elements from LIST selected +as `(nth i list)` for all i from INDICES." + (let (r) + (--each indices + (!cons (nth it list) r)) + (nreverse r))) + +(defmacro -> (x &optional form &rest more) + "Thread the expr through the forms. Insert X as the second item +in the first form, making a list of it if it is not a list +already. If there are more forms, insert the first form as the +second item in second form, etc." + (cond + ((null form) x) + ((null more) (if (listp form) + `(,(car form) ,x ,@(cdr form)) + (list form x))) + (:else `(-> (-> ,x ,form) ,@more)))) + +(defmacro ->> (x &optional form &rest more) + "Thread the expr through the forms. Insert X as the last item +in the first form, making a list of it if it is not a list +already. If there are more forms, insert the first form as the +last item in second form, etc." + (cond + ((null form) x) + ((null more) (if (listp form) + `(,@form ,x) + (list form x))) + (:else `(->> (->> ,x ,form) ,@more)))) + +(defmacro --> (x form &rest more) + "Thread the expr through the forms. Insert X at the position +signified by the token `it' in the first form. If there are more +forms, insert the first form at the position signified by `it' in +in second form, etc." + (if (null more) + (if (listp form) + (--map-when (eq it 'it) x form) + (list form x)) + `(--> (--> ,x ,form) ,@more))) + +(defmacro -some-> (x &optional form &rest more) + "When expr is non-nil, thread it through the first form (via `->'), +and when that result is non-nil, through the next form, etc." + (if (null form) x + (let ((result (make-symbol "result"))) + `(-some-> (-when-let (,result ,x) + (-> ,result ,form)) + ,@more)))) + +(defmacro -some->> (x &optional form &rest more) + "When expr is non-nil, thread it through the first form (via `->>'), +and when that result is non-nil, through the next form, etc." + (if (null form) x + (let ((result (make-symbol "result"))) + `(-some->> (-when-let (,result ,x) + (->> ,result ,form)) + ,@more)))) + +(defmacro -some--> (x &optional form &rest more) + "When expr in non-nil, thread it through the first form (via `-->'), +and when that result is non-nil, through the next form, etc." + (if (null form) x + (let ((result (make-symbol "result"))) + `(-some--> (-when-let (,result ,x) + (--> ,result ,form)) + ,@more)))) + +(defun -grade-up (comparator list) + "Grade elements of LIST using COMPARATOR relation, yielding a +permutation vector such that applying this permutation to LIST +sorts it in ascending order." + ;; ugly hack to "fix" lack of lexical scope + (let ((comp `(lambda (it other) (funcall ',comparator (car it) (car other))))) + (->> (--map-indexed (cons it it-index) list) + (-sort comp) + (-map 'cdr)))) + +(defun -grade-down (comparator list) + "Grade elements of LIST using COMPARATOR relation, yielding a +permutation vector such that applying this permutation to LIST +sorts it in descending order." + ;; ugly hack to "fix" lack of lexical scope + (let ((comp `(lambda (it other) (funcall ',comparator (car other) (car it))))) + (->> (--map-indexed (cons it it-index) list) + (-sort comp) + (-map 'cdr)))) + +(defvar dash--source-counter 0 + "Monotonic counter for generated symbols.") + +(defun dash--match-make-source-symbol () + "Generate a new dash-source symbol. + +All returned symbols are guaranteed to be unique." + (prog1 (make-symbol (format "--dash-source-%d--" dash--source-counter)) + (setq dash--source-counter (1+ dash--source-counter)))) + +(defun dash--match-ignore-place-p (symbol) + "Return non-nil if SYMBOL is a symbol and starts with _." + (and (symbolp symbol) + (eq (aref (symbol-name symbol) 0) ?_))) + +(defun dash--match-cons-skip-cdr (skip-cdr source) + "Helper function generating idiomatic shifting code." + (cond + ((= skip-cdr 0) + `(pop ,source)) + (t + `(prog1 ,(dash--match-cons-get-car skip-cdr source) + (setq ,source ,(dash--match-cons-get-cdr (1+ skip-cdr) source)))))) + +(defun dash--match-cons-get-car (skip-cdr source) + "Helper function generating idiomatic code to get nth car." + (cond + ((= skip-cdr 0) + `(car ,source)) + ((= skip-cdr 1) + `(cadr ,source)) + (t + `(nth ,skip-cdr ,source)))) + +(defun dash--match-cons-get-cdr (skip-cdr source) + "Helper function generating idiomatic code to get nth cdr." + (cond + ((= skip-cdr 0) + source) + ((= skip-cdr 1) + `(cdr ,source)) + (t + `(nthcdr ,skip-cdr ,source)))) + +(defun dash--match-cons (match-form source) + "Setup a cons matching environment and call the real matcher." + (let ((s (dash--match-make-source-symbol)) + (n 0) + (m match-form)) + (while (and (consp m) + (dash--match-ignore-place-p (car m))) + (setq n (1+ n)) (!cdr m)) + (cond + ;; when we only have one pattern in the list, we don't have to + ;; create a temporary binding (--dash-source--) for the source + ;; and just use the input directly + ((and (consp m) + (not (cdr m))) + (dash--match (car m) (dash--match-cons-get-car n source))) + ;; handle other special types + ((> n 0) + (dash--match m (dash--match-cons-get-cdr n source))) + ;; this is the only entry-point for dash--match-cons-1, that's + ;; why we can't simply use the above branch, it would produce + ;; infinite recursion + (t + (cons (list s source) (dash--match-cons-1 match-form s)))))) + +(defun dash--match-cons-1 (match-form source &optional props) + "Match MATCH-FORM against SOURCE. + +MATCH-FORM is a proper or improper list. Each element of +MATCH-FORM is either a symbol, which gets bound to the respective +value in source or another match form which gets destructured +recursively. + +If the cdr of last cons cell in the list is `nil', matching stops +there. + +SOURCE is a proper or improper list." + (let ((skip-cdr (or (plist-get props :skip-cdr) 0))) + (cond + ((consp match-form) + (cond + ((cdr match-form) + (cond + ((and (symbolp (car match-form)) + (memq (car match-form) '(&keys &plist &alist &hash))) + (dash--match-kv match-form (dash--match-cons-get-cdr skip-cdr source))) + ((dash--match-ignore-place-p (car match-form)) + (dash--match-cons-1 (cdr match-form) source + (plist-put props :skip-cdr (1+ skip-cdr)))) + (t + (-concat (dash--match (car match-form) (dash--match-cons-skip-cdr skip-cdr source)) + (dash--match-cons-1 (cdr match-form) source))))) + (t ;; Last matching place, no need for shift + (dash--match (car match-form) (dash--match-cons-get-car skip-cdr source))))) + ((eq match-form nil) + nil) + (t ;; Handle improper lists. Last matching place, no need for shift + (dash--match match-form (dash--match-cons-get-cdr skip-cdr source)))))) + +(defun dash--vector-tail (seq start) + "Return the tail of SEQ starting at START." + (cond + ((vectorp seq) + (let* ((re-length (- (length seq) start)) + (re (make-vector re-length 0))) + (--dotimes re-length (aset re it (aref seq (+ it start)))) + re)) + ((stringp seq) + (substring seq start)))) + +(defun dash--match-vector (match-form source) + "Setup a vector matching environment and call the real matcher." + (let ((s (dash--match-make-source-symbol))) + (cond + ;; don't bind `s' if we only have one sub-pattern + ((= (length match-form) 1) + (dash--match (aref match-form 0) `(aref ,source 0))) + ;; if the source is a symbol, we don't need to re-bind it + ((symbolp source) + (dash--match-vector-1 match-form source)) + ;; don't bind `s' if we only have one sub-pattern which is not ignored + ((let* ((ignored-places (mapcar 'dash--match-ignore-place-p match-form)) + (ignored-places-n (length (-remove 'null ignored-places)))) + (when (= ignored-places-n (1- (length match-form))) + (let ((n (-find-index 'null ignored-places))) + (dash--match (aref match-form n) `(aref ,source ,n)))))) + (t + (cons (list s source) (dash--match-vector-1 match-form s)))))) + +(defun dash--match-vector-1 (match-form source) + "Match MATCH-FORM against SOURCE. + +MATCH-FORM is a vector. Each element of MATCH-FORM is either a +symbol, which gets bound to the respective value in source or +another match form which gets destructured recursively. + +If second-from-last place in MATCH-FORM is the symbol &rest, the +next element of the MATCH-FORM is matched against the tail of +SOURCE, starting at index of the &rest symbol. This is +conceptually the same as the (head . tail) match for improper +lists, where dot plays the role of &rest. + +SOURCE is a vector. + +If the MATCH-FORM vector is shorter than SOURCE vector, only +the (length MATCH-FORM) places are bound, the rest of the SOURCE +is discarded." + (let ((i 0) + (l (length match-form)) + (re)) + (while (< i l) + (let ((m (aref match-form i))) + (push (cond + ((and (symbolp m) + (eq m '&rest)) + (prog1 (dash--match + (aref match-form (1+ i)) + `(dash--vector-tail ,source ,i)) + (setq i l))) + ((and (symbolp m) + ;; do not match symbols starting with _ + (not (eq (aref (symbol-name m) 0) ?_))) + (list (list m `(aref ,source ,i)))) + ((not (symbolp m)) + (dash--match m `(aref ,source ,i)))) + re) + (setq i (1+ i)))) + (-flatten-n 1 (nreverse re)))) + +(defun dash--match-kv (match-form source) + "Setup a kv matching environment and call the real matcher. + +kv can be any key-value store, such as plist, alist or hash-table." + (let ((s (dash--match-make-source-symbol))) + (cond + ;; don't bind `s' if we only have one sub-pattern (&type key val) + ((= (length match-form) 3) + (dash--match-kv-1 (cdr match-form) source (car match-form))) + ;; if the source is a symbol, we don't need to re-bind it + ((symbolp source) + (dash--match-kv-1 (cdr match-form) source (car match-form))) + (t + (cons (list s source) (dash--match-kv-1 (cdr match-form) s (car match-form))))))) + +(defun dash--match-kv-1 (match-form source type) + "Match MATCH-FORM against SOURCE of type TYPE. + +MATCH-FORM is a proper list of the form (key1 place1 ... keyN +placeN). Each placeK is either a symbol, which gets bound to the +value of keyK retrieved from the key-value store, or another +match form which gets destructured recursively. + +SOURCE is a key-value store of type TYPE, which can be a plist, +an alist or a hash table. + +TYPE is a token specifying the type of the key-value store. +Valid values are &plist, &alist and &hash." + (-flatten-n 1 (-map + (lambda (kv) + (let* ((k (car kv)) + (v (cadr kv)) + (getter (cond + ((or (eq type '&plist) (eq type '&keys)) + `(plist-get ,source ,k)) + ((eq type '&alist) + `(cdr (assoc ,k ,source))) + ((eq type '&hash) + `(gethash ,k ,source))))) + (cond + ((symbolp v) + (list (list v getter))) + (t (dash--match v getter))))) + (-partition 2 match-form)))) + +(defun dash--match-symbol (match-form source) + "Bind a symbol. + +This works just like `let', there is no destructuring." + (list (list match-form source))) + +(defun dash--match (match-form source) + "Match MATCH-FORM against SOURCE. + +This function tests the MATCH-FORM and dispatches to specific +matchers based on the type of the expression. + +Key-value stores are disambiguated by placing a token &plist, +&alist or &hash as a first item in the MATCH-FORM." + (cond + ((symbolp match-form) + (dash--match-symbol match-form source)) + ((consp match-form) + (cond + ;; Handle the "x &as" bindings first. + ((and (consp (cdr match-form)) + (symbolp (car match-form)) + (eq '&as (cadr match-form))) + (let ((s (car match-form))) + (cons (list s source) + (dash--match (cddr match-form) s)))) + ((memq (car match-form) '(&keys &plist &alist &hash)) + (dash--match-kv match-form source)) + (t (dash--match-cons match-form source)))) + ((vectorp match-form) + ;; We support the &as binding in vectors too + (cond + ((and (> (length match-form) 2) + (symbolp (aref match-form 0)) + (eq '&as (aref match-form 1))) + (let ((s (aref match-form 0))) + (cons (list s source) + (dash--match (dash--vector-tail match-form 2) s)))) + (t (dash--match-vector match-form source)))))) + +(defmacro -let* (varlist &rest body) + "Bind variables according to VARLIST then eval BODY. + +VARLIST is a list of lists of the form (PATTERN SOURCE). Each +PATTERN is matched against the SOURCE structurally. SOURCE is +only evaluated once for each PATTERN. + +Each SOURCE can refer to the symbols already bound by this +VARLIST. This is useful if you want to destructure SOURCE +recursively but also want to name the intermediate structures. + +See `-let' for the list of all possible patterns." + (declare (debug ((&rest (sexp form)) body)) + (indent 1)) + (let ((bindings (--mapcat (dash--match (car it) (cadr it)) varlist))) + `(let* ,bindings + ,@body))) + +(defmacro -let (varlist &rest body) + "Bind variables according to VARLIST then eval BODY. + +VARLIST is a list of lists of the form (PATTERN SOURCE). Each +PATTERN is matched against the SOURCE \"structurally\". SOURCE +is only evaluated once for each PATTERN. Each PATTERN is matched +recursively, and can therefore contain sub-patterns which are +matched against corresponding sub-expressions of SOURCE. + +All the SOURCEs are evalled before any symbols are +bound (i.e. \"in parallel\"). + +If VARLIST only contains one (PATTERN SOURCE) element, you can +optionally specify it using a vector and discarding the +outer-most parens. Thus + + (-let ((PATTERN SOURCE)) ..) + +becomes + + (-let [PATTERN SOURCE] ..). + +`-let' uses a convention of not binding places (symbols) starting +with _ whenever it's possible. You can use this to skip over +entries you don't care about. However, this is not *always* +possible (as a result of implementation) and these symbols might +get bound to undefined values. + +Following is the overview of supported patterns. Remember that +patterns can be matched recursively, so every a, b, aK in the +following can be a matching construct and not necessarily a +symbol/variable. + +Symbol: + + a - bind the SOURCE to A. This is just like regular `let'. + +Conses and lists: + + (a) - bind `car' of cons/list to A + + (a . b) - bind car of cons to A and `cdr' to B + + (a b) - bind car of list to A and `cadr' to B + + (a1 a2 a3 ...) - bind 0th car of list to A1, 1st to A2, 2nd to A3 ... + + (a1 a2 a3 ... aN . rest) - as above, but bind the Nth cdr to REST. + +Vectors: + + [a] - bind 0th element of a non-list sequence to A (works with + vectors, strings, bit arrays...) + + [a1 a2 a3 ...] - bind 0th element of non-list sequence to A0, 1st to + A1, 2nd to A2, ... + If the PATTERN is shorter than SOURCE, the values at + places not in PATTERN are ignored. + If the PATTERN is longer than SOURCE, an `error' is + thrown. + + [a1 a2 a3 ... &rest rest] - as above, but bind the rest of + the sequence to REST. This is + conceptually the same as improper list + matching (a1 a2 ... aN . rest) + +Key/value stores: + + (&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the + SOURCE plist to aK. If the + value is not found, aK is nil. + + (&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the + SOURCE alist to aK. If the + value is not found, aK is nil. + + (&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the + SOURCE hash table to aK. If the + value is not found, aK is nil. + +Further, special keyword &keys supports \"inline\" matching of +plist-like key-value pairs, similarly to &keys keyword of +`cl-defun'. + + (a1 a2 ... aN &keys key1 b1 ... keyN bK) + +This binds N values from the list to a1 ... aN, then interprets +the cdr as a plist (see key/value matching above). + +You can name the source using the syntax SYMBOL &as PATTERN. +This syntax works with lists (proper or improper), vectors and +all types of maps. + + (list &as a b c) (list 1 2 3) + +binds A to 1, B to 2, C to 3 and LIST to (1 2 3). + +Similarly: + + (bounds &as beg . end) (cons 1 2) + +binds BEG to 1, END to 2 and BOUNDS to (1 . 2). + + (items &as first . rest) (list 1 2 3) + +binds FIRST to 1, REST to (2 3) and ITEMS to (1 2 3) + + [vect &as _ b c] [1 2 3] + +binds B to 2, C to 3 and VECT to [1 2 3] (_ avoids binding as usual). + + (plist &as &plist :b b) (list :a 1 :b 2 :c 3) + +binds B to 2 and PLIST to (:a 1 :b 2 :c 3). Same for &alist and &hash. + +This is especially useful when we want to capture the result of a +computation and destructure at the same time. Consider the +form (function-returning-complex-structure) returning a list of +two vectors with two items each. We want to capture this entire +result and pass it to another computation, but at the same time +we want to get the second item from each vector. We can achieve +it with pattern + + (result &as [_ a] [_ b]) (function-returning-complex-structure) + +Note: Clojure programmers may know this feature as the \":as +binding\". The difference is that we put the &as at the front +because we need to support improper list binding." + (declare (debug ([&or (&rest (sexp form)) + (vector [&rest [sexp form]])] + body)) + (indent 1)) + (if (vectorp varlist) + `(let* ,(dash--match (aref varlist 0) (aref varlist 1)) + ,@body) + (let* ((inputs (--map-indexed (list (make-symbol (format "input%d" it-index)) (cadr it)) varlist)) + (new-varlist (--map (list (caar it) (cadr it)) (-zip varlist inputs)))) + `(let ,inputs + (-let* ,new-varlist ,@body))))) + +(defmacro -lambda (match-form &rest body) + "Return a lambda which destructures its input as MATCH-FORM and executes BODY. + +Note that you have to enclose the MATCH-FORM in a pair of parens, +such that: + + (-lambda (x) body) + (-lambda (x y ...) body) + +has the usual semantics of `lambda'. Furthermore, these get +translated into normal lambda, so there is no performance +penalty. + +See `-let' for the description of destructuring mechanism." + (declare (doc-string 2) (indent defun) + (debug (&define sexp + [&optional stringp] + [&optional ("interactive" interactive)] + def-body))) + (cond + ((not (consp match-form)) + (signal 'wrong-type-argument "match-form must be a list")) + ;; no destructuring, so just return regular lambda to make things faster + ((-all? 'symbolp match-form) + `(lambda ,match-form ,@body)) + (t + (let* ((inputs (--map-indexed (list it (make-symbol (format "input%d" it-index))) match-form))) + ;; TODO: because inputs to the lambda are evaluated only once, + ;; -let* need not to create the extra bindings to ensure that. + ;; We should find a way to optimize that. Not critical however. + `(lambda ,(--map (cadr it) inputs) + (-let* ,inputs ,@body)))))) + +(defmacro -if-let* (vars-vals then &rest else) + "If all VALS evaluate to true, bind them to their corresponding +VARS and do THEN, otherwise do ELSE. VARS-VALS should be a list +of (VAR VAL) pairs. + +Note: binding is done according to `-let*'. VALS are evaluated +sequentially, and evaluation stops after the first nil VAL is +encountered." + (declare (debug ((&rest (sexp form)) form body)) + (indent 2)) + (->> vars-vals + (--mapcat (dash--match (car it) (cadr it))) + (--reduce-r-from + (let ((var (car it)) + (val (cadr it))) + `(let ((,var ,val)) + (if ,var ,acc ,@else))) + then))) + +(defmacro -if-let (var-val then &rest else) + "If VAL evaluates to non-nil, bind it to VAR and do THEN, +otherwise do ELSE. VAR-VAL should be a (VAR VAL) pair. + +Note: binding is done according to `-let'." + (declare (debug ((sexp form) form body)) + (indent 2)) + `(-if-let* (,var-val) ,then ,@else)) + +(defmacro --if-let (val then &rest else) + "If VAL evaluates to non-nil, bind it to `it' and do THEN, +otherwise do ELSE." + (declare (debug (form form body)) + (indent 2)) + `(-if-let (it ,val) ,then ,@else)) + +(defmacro -when-let* (vars-vals &rest body) + "If all VALS evaluate to true, bind them to their corresponding +VARS and execute body. VARS-VALS should be a list of (VAR VAL) +pairs. + +Note: binding is done according to `-let*'. VALS are evaluated +sequentially, and evaluation stops after the first nil VAL is +encountered." + (declare (debug ((&rest (sexp form)) body)) + (indent 1)) + `(-if-let* ,vars-vals (progn ,@body))) + +(defmacro -when-let (var-val &rest body) + "If VAL evaluates to non-nil, bind it to VAR and execute body. +VAR-VAL should be a (VAR VAL) pair. + +Note: binding is done according to `-let'." + (declare (debug ((sexp form) body)) + (indent 1)) + `(-if-let ,var-val (progn ,@body))) + +(defmacro --when-let (val &rest body) + "If VAL evaluates to non-nil, bind it to `it' and execute +body." + (declare (debug (form body)) + (indent 1)) + `(--if-let ,val (progn ,@body))) + +(defvar -compare-fn nil + "Tests for equality use this function or `equal' if this is nil. +It should only be set using dynamic scope with a let, like: + + (let ((-compare-fn #'=)) (-union numbers1 numbers2 numbers3)") + +(defun -distinct (list) + "Return a new list with all duplicates removed. +The test for equality is done with `equal', +or with `-compare-fn' if that's non-nil. + +Alias: `-uniq'" + (let (result) + (--each list (unless (-contains? result it) (!cons it result))) + (nreverse result))) + +(defalias '-uniq '-distinct) + +(defun -union (list list2) + "Return a new list containing the elements of LIST1 and elements of LIST2 that are not in LIST1. +The test for equality is done with `equal', +or with `-compare-fn' if that's non-nil." + ;; We fall back to iteration implementation if the comparison + ;; function isn't one of `eq', `eql' or `equal'. + (let* ((result (reverse list)) + ;; TODO: get rid of this dynamic variable, pass it as an + ;; argument instead. + (-compare-fn (if (bound-and-true-p -compare-fn) + -compare-fn + 'equal))) + (if (memq -compare-fn '(eq eql equal)) + (let ((ht (make-hash-table :test -compare-fn))) + (--each list (puthash it t ht)) + (--each list2 (unless (gethash it ht) (!cons it result)))) + (--each list2 (unless (-contains? result it) (!cons it result)))) + (nreverse result))) + +(defun -intersection (list list2) + "Return a new list containing only the elements that are members of both LIST and LIST2. +The test for equality is done with `equal', +or with `-compare-fn' if that's non-nil." + (--filter (-contains? list2 it) list)) + +(defun -difference (list list2) + "Return a new list with only the members of LIST that are not in LIST2. +The test for equality is done with `equal', +or with `-compare-fn' if that's non-nil." + (--filter (not (-contains? list2 it)) list)) + +(defun -contains? (list element) + "Return non-nil if LIST contains ELEMENT. + +The test for equality is done with `equal', or with `-compare-fn' +if that's non-nil. + +Alias: `-contains-p'" + (not + (null + (cond + ((null -compare-fn) (member element list)) + ((eq -compare-fn 'eq) (memq element list)) + ((eq -compare-fn 'eql) (memql element list)) + (t + (let ((lst list)) + (while (and lst + (not (funcall -compare-fn element (car lst)))) + (setq lst (cdr lst))) + lst)))))) + +(defalias '-contains-p '-contains?) + +(defun -same-items? (list list2) + "Return true if LIST and LIST2 has the same items. + +The order of the elements in the lists does not matter. + +Alias: `-same-items-p'" + (let ((length-a (length list)) + (length-b (length list2))) + (and + (= length-a length-b) + (= length-a (length (-intersection list list2)))))) + +(defalias '-same-items-p '-same-items?) + +(defun -is-prefix? (prefix list) + "Return non-nil if PREFIX is prefix of LIST. + +Alias: `-is-prefix-p'" + (--each-while list (equal (car prefix) it) + (!cdr prefix)) + (not prefix)) + +(defun -is-suffix? (suffix list) + "Return non-nil if SUFFIX is suffix of LIST. + +Alias: `-is-suffix-p'" + (-is-prefix? (reverse suffix) (reverse list))) + +(defun -is-infix? (infix list) + "Return non-nil if INFIX is infix of LIST. + +This operation runs in O(n^2) time + +Alias: `-is-infix-p'" + (let (done) + (while (and (not done) list) + (setq done (-is-prefix? infix list)) + (!cdr list)) + done)) + +(defalias '-is-prefix-p '-is-prefix?) +(defalias '-is-suffix-p '-is-suffix?) +(defalias '-is-infix-p '-is-infix?) + +(defun -sort (comparator list) + "Sort LIST, stably, comparing elements using COMPARATOR. +Return the sorted list. LIST is NOT modified by side effects. +COMPARATOR is called with two elements of LIST, and should return non-nil +if the first element should sort before the second." + (sort (copy-sequence list) comparator)) + +(defmacro --sort (form list) + "Anaphoric form of `-sort'." + (declare (debug (form form))) + `(-sort (lambda (it other) ,form) ,list)) + +(defun -list (&rest args) + "Return a list with ARGS. + +If first item of ARGS is already a list, simply return ARGS. If +not, return a list with ARGS as elements." + (let ((arg (car args))) + (if (listp arg) arg args))) + +(defun -repeat (n x) + "Return a list with X repeated N times. +Return nil if N is less than 1." + (let (ret) + (--dotimes n (!cons x ret)) + ret)) + +(defun -sum (list) + "Return the sum of LIST." + (apply '+ list)) + +(defun -product (list) + "Return the product of LIST." + (apply '* list)) + +(defun -max (list) + "Return the largest value from LIST of numbers or markers." + (apply 'max list)) + +(defun -min (list) + "Return the smallest value from LIST of numbers or markers." + (apply 'min list)) + +(defun -max-by (comparator list) + "Take a comparison function COMPARATOR and a LIST and return +the greatest element of the list by the comparison function. + +See also combinator `-on' which can transform the values before +comparing them." + (--reduce (if (funcall comparator it acc) it acc) list)) + +(defun -min-by (comparator list) + "Take a comparison function COMPARATOR and a LIST and return +the least element of the list by the comparison function. + +See also combinator `-on' which can transform the values before +comparing them." + (--reduce (if (funcall comparator it acc) acc it) list)) + +(defmacro --max-by (form list) + "Anaphoric version of `-max-by'. + +The items for the comparator form are exposed as \"it\" and \"other\"." + (declare (debug (form form))) + `(-max-by (lambda (it other) ,form) ,list)) + +(defmacro --min-by (form list) + "Anaphoric version of `-min-by'. + +The items for the comparator form are exposed as \"it\" and \"other\"." + (declare (debug (form form))) + `(-min-by (lambda (it other) ,form) ,list)) + +(defun -iterate (fun init n) + "Return a list of iterated applications of FUN to INIT. + +This means a list of form: + + (init (fun init) (fun (fun init)) ...) + +N is the length of the returned list." + (if (= n 0) nil + (let ((r (list init))) + (--dotimes (1- n) + (push (funcall fun (car r)) r)) + (nreverse r)))) + +(defun -fix (fn list) + "Compute the (least) fixpoint of FN with initial input LIST. + +FN is called at least once, results are compared with `equal'." + (let ((re (funcall fn list))) + (while (not (equal list re)) + (setq list re) + (setq re (funcall fn re))) + re)) + +(defmacro --fix (form list) + "Anaphoric form of `-fix'." + `(-fix (lambda (it) ,form) ,list)) + +(defun -unfold (fun seed) + "Build a list from SEED using FUN. + +This is \"dual\" operation to `-reduce-r': while -reduce-r +consumes a list to produce a single value, `-unfold' takes a +seed value and builds a (potentially infinite!) list. + +FUN should return `nil' to stop the generating process, or a +cons (A . B), where A will be prepended to the result and B is +the new seed." + (let ((last (funcall fun seed)) r) + (while last + (push (car last) r) + (setq last (funcall fun (cdr last)))) + (nreverse r))) + +(defmacro --unfold (form seed) + "Anaphoric version of `-unfold'." + (declare (debug (form form))) + `(-unfold (lambda (it) ,form) ,seed)) + +(defun -cons-pair? (con) + "Return non-nil if CON is true cons pair. +That is (A . B) where B is not a list." + (and (listp con) + (not (listp (cdr con))))) + +(defun -cons-to-list (con) + "Convert a cons pair to a list with `car' and `cdr' of the pair respectively." + (list (car con) (cdr con))) + +(defun -value-to-list (val) + "Convert a value to a list. + +If the value is a cons pair, make a list with two elements, `car' +and `cdr' of the pair respectively. + +If the value is anything else, wrap it in a list." + (cond + ((-cons-pair? val) (-cons-to-list val)) + (t (list val)))) + +(defun -tree-mapreduce-from (fn folder init-value tree) + "Apply FN to each element of TREE, and make a list of the results. +If elements of TREE are lists themselves, apply FN recursively to +elements of these nested lists. + +Then reduce the resulting lists using FOLDER and initial value +INIT-VALUE. See `-reduce-r-from'. + +This is the same as calling `-tree-reduce-from' after `-tree-map' +but is twice as fast as it only traverse the structure once." + (cond + ((not tree) nil) + ((-cons-pair? tree) (funcall fn tree)) + ((listp tree) + (-reduce-r-from folder init-value (mapcar (lambda (x) (-tree-mapreduce-from fn folder init-value x)) tree))) + (t (funcall fn tree)))) + +(defmacro --tree-mapreduce-from (form folder init-value tree) + "Anaphoric form of `-tree-mapreduce-from'." + (declare (debug (form form form form))) + `(-tree-mapreduce-from (lambda (it) ,form) (lambda (it acc) ,folder) ,init-value ,tree)) + +(defun -tree-mapreduce (fn folder tree) + "Apply FN to each element of TREE, and make a list of the results. +If elements of TREE are lists themselves, apply FN recursively to +elements of these nested lists. + +Then reduce the resulting lists using FOLDER and initial value +INIT-VALUE. See `-reduce-r-from'. + +This is the same as calling `-tree-reduce' after `-tree-map' +but is twice as fast as it only traverse the structure once." + (cond + ((not tree) nil) + ((-cons-pair? tree) (funcall fn tree)) + ((listp tree) + (-reduce-r folder (mapcar (lambda (x) (-tree-mapreduce fn folder x)) tree))) + (t (funcall fn tree)))) + +(defmacro --tree-mapreduce (form folder tree) + "Anaphoric form of `-tree-mapreduce'." + (declare (debug (form form form))) + `(-tree-mapreduce (lambda (it) ,form) (lambda (it acc) ,folder) ,tree)) + +(defun -tree-map (fn tree) + "Apply FN to each element of TREE while preserving the tree structure." + (cond + ((not tree) nil) + ((-cons-pair? tree) (funcall fn tree)) + ((listp tree) + (mapcar (lambda (x) (-tree-map fn x)) tree)) + (t (funcall fn tree)))) + +(defmacro --tree-map (form tree) + "Anaphoric form of `-tree-map'." + (declare (debug (form form))) + `(-tree-map (lambda (it) ,form) ,tree)) + +(defun -tree-reduce-from (fn init-value tree) + "Use FN to reduce elements of list TREE. +If elements of TREE are lists themselves, apply the reduction recursively. + +FN is first applied to INIT-VALUE and first element of the list, +then on this result and second element from the list etc. + +The initial value is ignored on cons pairs as they always contain +two elements." + (cond + ((not tree) nil) + ((-cons-pair? tree) tree) + ((listp tree) + (-reduce-r-from fn init-value (mapcar (lambda (x) (-tree-reduce-from fn init-value x)) tree))) + (t tree))) + +(defmacro --tree-reduce-from (form init-value tree) + "Anaphoric form of `-tree-reduce-from'." + (declare (debug (form form form))) + `(-tree-reduce-from (lambda (it acc) ,form) ,init-value ,tree)) + +(defun -tree-reduce (fn tree) + "Use FN to reduce elements of list TREE. +If elements of TREE are lists themselves, apply the reduction recursively. + +FN is first applied to first element of the list and second +element, then on this result and third element from the list etc. + +See `-reduce-r' for how exactly are lists of zero or one element handled." + (cond + ((not tree) nil) + ((-cons-pair? tree) tree) + ((listp tree) + (-reduce-r fn (mapcar (lambda (x) (-tree-reduce fn x)) tree))) + (t tree))) + +(defmacro --tree-reduce (form tree) + "Anaphoric form of `-tree-reduce'." + (declare (debug (form form))) + `(-tree-reduce (lambda (it acc) ,form) ,tree)) + +(defun -tree-map-nodes (pred fun tree) + "Call FUN on each node of TREE that satisfies PRED. + +If PRED returns nil, continue descending down this node. If PRED +returns non-nil, apply FUN to this node and do not descend +further." + (if (funcall pred tree) + (funcall fun tree) + (if (and (listp tree) + (not (-cons-pair? tree))) + (-map (lambda (x) (-tree-map-nodes pred fun x)) tree) + tree))) + +(defmacro --tree-map-nodes (pred form tree) + "Anaphoric form of `-tree-map-nodes'." + `(-tree-map-nodes (lambda (it) ,pred) (lambda (it) ,form) ,tree)) + +(defun -tree-seq (branch children tree) + "Return a sequence of the nodes in TREE, in depth-first search order. + +BRANCH is a predicate of one argument that returns non-nil if the +passed argument is a branch, that is, a node that can have children. + +CHILDREN is a function of one argument that returns the children +of the passed branch node. + +Non-branch nodes are simply copied." + (cons tree + (when (funcall branch tree) + (-mapcat (lambda (x) (-tree-seq branch children x)) + (funcall children tree))))) + +(defmacro --tree-seq (branch children tree) + "Anaphoric form of `-tree-seq'." + `(-tree-seq (lambda (it) ,branch) (lambda (it) ,children) ,tree)) + +(defun -clone (list) + "Create a deep copy of LIST. +The new list has the same elements and structure but all cons are +replaced with new ones. This is useful when you need to clone a +structure such as plist or alist." + (-tree-map 'identity list)) + +(defun dash-enable-font-lock () + "Add syntax highlighting to dash functions, macros and magic values." + (eval-after-load "lisp-mode" + '(progn + (let ((new-keywords '( + "-each" + "--each" + "-each-while" + "--each-while" + "-dotimes" + "--dotimes" + "-map" + "--map" + "-reduce-from" + "--reduce-from" + "-reduce" + "--reduce" + "-reduce-r-from" + "--reduce-r-from" + "-reduce-r" + "--reduce-r" + "-filter" + "--filter" + "-select" + "--select" + "-remove" + "--remove" + "-reject" + "--reject" + "-remove-first" + "--remove-first" + "-reject-first" + "--reject-first" + "-remove-last" + "--remove-last" + "-reject-last" + "--reject-last" + "-remove-item" + "-non-nil" + "-keep" + "--keep" + "-map-indexed" + "--map-indexed" + "-splice" + "--splice" + "-splice-list" + "--splice-list" + "-map-when" + "--map-when" + "-replace-where" + "--replace-where" + "-map-first" + "--map-first" + "-map-last" + "--map-last" + "-replace" + "-replace-first" + "-replace-last" + "-flatten" + "-flatten-n" + "-concat" + "-mapcat" + "--mapcat" + "-copy" + "-cons*" + "-snoc" + "-first" + "--first" + "-find" + "--find" + "-some" + "--some" + "-any" + "--any" + "-last" + "--last" + "-first-item" + "-last-item" + "-butlast" + "-count" + "--count" + "-any?" + "--any?" + "-some?" + "--some?" + "-any-p" + "--any-p" + "-some-p" + "--some-p" + "-all?" + "--all?" + "-every?" + "--every?" + "-all-p" + "--all-p" + "-every-p" + "--every-p" + "-none?" + "--none?" + "-none-p" + "--none-p" + "-only-some?" + "--only-some?" + "-only-some-p" + "--only-some-p" + "-slice" + "-take" + "-drop" + "-take-while" + "--take-while" + "-drop-while" + "--drop-while" + "-split-at" + "-rotate" + "-insert-at" + "-replace-at" + "-update-at" + "--update-at" + "-remove-at" + "-remove-at-indices" + "-split-with" + "--split-with" + "-split-on" + "-split-when" + "--split-when" + "-separate" + "--separate" + "-partition-all-in-steps" + "-partition-in-steps" + "-partition-all" + "-partition" + "-partition-by" + "--partition-by" + "-partition-by-header" + "--partition-by-header" + "-group-by" + "--group-by" + "-interpose" + "-interleave" + "-zip-with" + "--zip-with" + "-zip" + "-zip-fill" + "-cycle" + "-pad" + "-annotate" + "--annotate" + "-table" + "-table-flat" + "-partial" + "-elem-index" + "-elem-indices" + "-find-indices" + "--find-indices" + "-find-index" + "--find-index" + "-find-last-index" + "--find-last-index" + "-select-by-indices" + "-grade-up" + "-grade-down" + "->" + "->>" + "-->" + "-when-let" + "-when-let*" + "--when-let" + "-if-let" + "-if-let*" + "--if-let" + "-let*" + "-let" + "-lambda" + "-distinct" + "-uniq" + "-union" + "-intersection" + "-difference" + "-contains?" + "-contains-p" + "-same-items?" + "-same-items-p" + "-is-prefix-p" + "-is-prefix?" + "-is-suffix-p" + "-is-suffix?" + "-is-infix-p" + "-is-infix?" + "-sort" + "--sort" + "-list" + "-repeat" + "-sum" + "-product" + "-max" + "-min" + "-max-by" + "--max-by" + "-min-by" + "--min-by" + "-iterate" + "--iterate" + "-fix" + "--fix" + "-unfold" + "--unfold" + "-cons-pair?" + "-cons-to-list" + "-value-to-list" + "-tree-mapreduce-from" + "--tree-mapreduce-from" + "-tree-mapreduce" + "--tree-mapreduce" + "-tree-map" + "--tree-map" + "-tree-reduce-from" + "--tree-reduce-from" + "-tree-reduce" + "--tree-reduce" + "-tree-seq" + "--tree-seq" + "-tree-map-nodes" + "--tree-map-nodes" + "-clone" + "-rpartial" + "-juxt" + "-applify" + "-on" + "-flip" + "-const" + "-cut" + "-orfn" + "-andfn" + "-iteratefn" + "-fixfn" + "-prodfn" + )) + (special-variables '( + "it" + "it-index" + "acc" + "other" + ))) + (font-lock-add-keywords 'emacs-lisp-mode `((,(concat "\\_<" (regexp-opt special-variables 'paren) "\\_>") + 1 font-lock-variable-name-face)) 'append) + (font-lock-add-keywords 'emacs-lisp-mode `((,(concat "(\\s-*" (regexp-opt new-keywords 'paren) "\\_>") + 1 font-lock-keyword-face)) 'append)) + (--each (buffer-list) + (with-current-buffer it + (when (and (eq major-mode 'emacs-lisp-mode) + (boundp 'font-lock-mode) + font-lock-mode) + (font-lock-refresh-defaults))))))) + +(provide 'dash) +;;; dash.el ends here diff --git a/elpa/gh-20160222.1811/gh-api.el b/elpa/gh-20160222.1811/gh-api.el new file mode 100644 index 0000000..ce6b8b3 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-api.el @@ -0,0 +1,249 @@ +;;; gh-api.el --- api definition for gh.el + +;; Copyright (C) 2011 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'json) + +(require 'gh-profile) +(require 'gh-url) +(require 'gh-auth) +(require 'gh-cache) + +(require 'logito) + +(defgroup gh-api nil + "Github API." + :group 'gh) + +(defcustom gh-api-username-filter 'gh-api-enterprise-username-filter + "Filter to apply to usernames to build URL components" + :type 'function + :group 'gh-api) + +;;;###autoload +(defclass gh-api () + ((sync :initarg :sync :initform t) + (cache :initarg :cache :initform nil) + (base :initarg :base :type string) + (profile :initarg :profile :type string) + (auth :initarg :auth :initform nil) + (data-format :initarg :data-format) + (num-retries :initarg :num-retries :initform 0) + (log :initarg :log :initform nil) + (cache-cls :initform gh-cache :allocation :class)) + "Github API") + +(defmethod logito-log ((api gh-api) level tag string &rest objects) + (apply 'logito-log (oref api :log) level tag string objects)) + +(defmethod constructor :static ((api gh-api) &rest args) + (call-next-method)) + +(defmethod gh-api-set-default-auth ((api gh-api) auth) + (let ((auth (or (oref api :auth) auth)) + (cache (oref api :cache)) + (classname (symbol-name (funcall (if (fboundp 'eieio-object-class) + 'eieio-object-class + 'object-class) + api)))) + (oset api :auth auth) + (unless (or (null cache) + (and (eieio-object-p cache) + (object-of-class-p cache 'gh-cache))) + (oset api :cache (funcall (oref api cache-cls) + (format "gh/%s/%s" + classname + (gh-api-get-username api))))))) + +(defmethod gh-api-expand-resource ((api gh-api) + resource) + resource) + +(defun gh-api-enterprise-username-filter (username) + (replace-regexp-in-string (regexp-quote ".") "-" username)) + +(defmethod gh-api-get-username ((api gh-api)) + (let ((username (oref (oref api :auth) :username))) + (funcall gh-api-username-filter username))) + +;;;###autoload +(defclass gh-api-v3 (gh-api) + ((data-format :initarg :data-format :initform :json)) + "Github API v3") + +(defcustom gh-api-v3-authenticator 'gh-oauth-authenticator + "Authenticator for Github API v3" + :type '(choice (const :tag "Password" gh-password-authenticator) + (const :tag "OAuth" gh-oauth-authenticator)) + :group 'gh-api) + +(defmethod constructor :static ((api gh-api-v3) &rest args) + (let ((obj (call-next-method)) + (gh-profile-current-profile (gh-profile-current-profile))) + (oset obj :profile (gh-profile-current-profile)) + (oset obj :base (gh-profile-url)) + (gh-api-set-default-auth obj + (or (oref obj :auth) + (funcall gh-api-v3-authenticator "auth"))) + obj)) + +(defclass gh-api-request (gh-url-request) + ((default-response-cls :allocation :class :initform gh-api-response))) + +(defclass gh-api-response (gh-url-response) + ()) + +(defun gh-api-json-decode (repr) + (if (or (null repr) (string= repr "")) + 'empty + (let ((json-array-type 'list)) + (json-read-from-string repr)))) + +(defun gh-api-json-encode (json) + (json-encode-list json)) + +(defmethod gh-url-response-set-data ((resp gh-api-response) data) + (call-next-method resp (gh-api-json-decode data))) + +(defclass gh-api-paged-request (gh-api-request) + ((default-response-cls :allocation :class :initform gh-api-paged-response))) + +(defclass gh-api-paged-response (gh-api-response) + ()) + +(defmethod gh-api-paging-links ((resp gh-api-paged-response)) + (let ((links-header (cdr (assoc "Link" (oref resp :headers))))) + (when links-header + (loop for item in (split-string links-header ", ") + when (string-match "^<\\(.*\\)>; rel=\"\\(.*\\)\"" item) + collect (cons (match-string 2 item) + (match-string 1 item)))))) + +(defmethod gh-url-response-set-data ((resp gh-api-paged-response) data) + (let ((previous-data (oref resp :data)) + (next (cdr (assoc "next" (gh-api-paging-links resp))))) + (call-next-method) + (oset resp :data (append previous-data (oref resp :data))) + (when (and next (not (equal 304 (oref resp :http-status)))) + (let ((req (oref resp :-req))) + (oset resp :data-received nil) + (oset req :url next) + (gh-url-run-request req resp))))) + +(defmethod gh-api-authenticated-request + ((api gh-api) transformer method resource &optional data params) + (let* ((fmt (oref api :data-format)) + (headers (cond ((eq fmt :form) + '(("Content-Type" . + "application/x-www-form-urlencoded"))) + ((eq fmt :json) + '(("Content-Type" . + "application/json"))))) + (cache (oref api :cache)) + (key (list resource + method + (sha1 (format "%s" transformer)))) + (cache-key (and cache + (member method (oref cache safe-methods)) + key)) + (has-value (and cache-key (pcache-has cache cache-key))) + (value (and has-value (pcache-get cache cache-key))) + (is-outdated (and has-value (gh-cache-outdated-p cache cache-key))) + (etag (and is-outdated (gh-cache-etag cache cache-key))) + (req + (and (or (not has-value) + is-outdated) + (gh-auth-modify-request + (oref api :auth) + ;; TODO: use gh-api-paged-request only when needed + (make-instance 'gh-api-paged-request + :method method + :url (concat (oref api :base) + (gh-api-expand-resource + api resource)) + :query params + :headers (if etag + (cons (cons "If-None-Match" etag) + headers) + headers) + :data (or (and (eq fmt :json) + (gh-api-json-encode data)) + (and (eq fmt :form) + (gh-url-form-encode data)) + "")))))) + (cond ((and has-value ;; got value from cache + (not is-outdated)) + (gh-api-response "cached" :data-received t :data value)) + (cache-key ;; no value, but cache exists and method is safe + (let ((resp (make-instance (oref req default-response-cls) + :transform transformer))) + (gh-url-run-request req resp) + (gh-url-add-response-callback + resp (make-instance 'gh-api-callback :cache cache :key cache-key + :revive etag)) + resp)) + (cache ;; unsafe method, cache exists + (pcache-invalidate cache key) + (gh-url-run-request req (make-instance + (oref req default-response-cls) + :transform transformer))) + (t ;; no cache involved + (gh-url-run-request req (make-instance + (oref req default-response-cls) + :transform transformer)))))) + +(defclass gh-api-callback (gh-url-callback) + ((cache :initarg :cache) + (key :initarg :key) + (revive :initarg :revive))) + +(defmethod gh-url-callback-run ((cb gh-api-callback) resp) + (let ((cache (oref cb :cache)) + (key (oref cb :key))) + (if (and (oref cb :revive) (equal (oref resp :http-status) 304)) + (progn + (gh-cache-revive cache key) + (oset resp :data (pcache-get cache key))) + (pcache-put cache key (oref resp :data)) + (gh-cache-set-etag cache key + (cdr (assoc "ETag" (oref resp :headers))))))) + +(define-obsolete-function-alias 'gh-api-add-response-callback + 'gh-url-add-response-callback "0.6.0") + +(provide 'gh-api) +;;; gh-api.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-auth.el b/elpa/gh-20160222.1811/gh-auth.el new file mode 100644 index 0000000..39112f6 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-auth.el @@ -0,0 +1,176 @@ +;;; gh-auth.el --- authentication for gh.el + +;; Copyright (C) 2011 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-profile) +(require 'gh-common) +(require 'gh-url) + +(defgroup gh-auth nil + "Github authentication." + :group 'gh) + +(defvar gh-auth-alist nil) + +(defun gh-auth-remember (profile key value) + (let ((cell (assoc profile gh-auth-alist))) + (when (not cell) + (setq cell (cons profile nil)) + (setq gh-auth-alist (append gh-auth-alist (list cell)))) + (setcdr cell (plist-put (cdr cell) key value)))) + +(defun gh-auth-get-username () + (let* ((profile (gh-profile-current-profile)) + (user (or (plist-get (cdr (assoc profile gh-auth-alist)) :username) + (plist-get (cdr (assoc profile gh-profile-alist)) :username) + (gh-config "user")))) + (when (not user) + (setq user (read-string "GitHub username: ")) + (gh-set-config "user" user)) + (gh-auth-remember profile :username user) + user)) + +(defun gh-auth-get-password (&optional remember) + (let* ((profile (gh-profile-current-profile)) + (pass (or (plist-get (cdr (assoc profile gh-auth-alist)) :password) + (plist-get (cdr (assoc profile gh-profile-alist)) :password) + (gh-config "password")))) + (when (not pass) + (setq pass (read-passwd "GitHub password: ")) + (gh-set-config "password" pass)) + (when remember + (gh-auth-remember profile :password pass)) + pass)) + +(declare-function 'gh-oauth-auth-new "gh-oauth") + +(defun gh-auth-get-oauth-token () + (let* ((profile (gh-profile-current-profile)) + (token (or (plist-get (cdr (assoc profile gh-auth-alist)) :token) + (plist-get (cdr (assoc profile gh-profile-alist)) :token) + (gh-config "oauth-token")))) + (when (not token) + (let* ((api (make-instance 'gh-oauth-api)) + (tok (and (fboundp 'gh-oauth-auth-new) + (oref (oref (funcall 'gh-oauth-auth-new api + '(user repo gist)) :data) + :token)))) + (setq token (or tok (read-string "GitHub OAuth token: "))) + (gh-set-config "oauth-token" token))) + (gh-auth-remember profile :token token) + token)) + +;;;###autoload +(defclass gh-authenticator () + ((username :initarg :username :initform nil)) + "Abstract authenticator") + +(defmethod constructor :static ((auth gh-authenticator) &rest args) + (let ((obj (call-next-method))) + (or (oref obj :username) + (oset obj :username (gh-auth-get-username))) + obj)) + +(defmethod gh-auth-modify-request ((auth gh-authenticator) req) + req) + +(defclass gh-auth-2fa-callback (gh-url-callback) + ((req :initarg :req :initform nil)) + "2-factor callback") + +(defmethod gh-url-callback-run ((cb gh-auth-2fa-callback) resp) + (when (equal (oref resp :http-status) 401) + (let* ((otp-header "X-GitHub-OTP") + (h (assoc otp-header (oref resp :headers)))) + (when (and h (string-prefix-p "required;" (cdr h))) + (let ((otp (read-from-minibuffer "Enter dual-factor auth code: ")) + (req (oref cb :req))) + ;; reset resp + (oset resp :data nil) + (oset resp :data-received nil) + + (object-add-to-list req :headers + (cons otp-header otp)) + (gh-url-run-request req resp)))))) + +;;;###autoload +(defclass gh-password-authenticator (gh-authenticator) + ((password :initarg :password :protection :private :initform nil) + (remember :allocation :class :initform t) + + (2fa-cls :initform gh-auth-2fa-callback :allocation :class)) + "Password-based authenticator") + +(defmethod constructor :static ((auth gh-password-authenticator) &rest args) + (let ((obj (call-next-method))) + (or (oref obj :password) + (oset obj :password (gh-auth-get-password (oref obj remember)))) + obj)) + +(defmethod gh-auth-modify-request ((auth gh-password-authenticator) req) + (object-add-to-list req :headers + (cons "Authorization" + (concat "Basic " + (base64-encode-string + (format "%s:%s" (oref auth :username) + (encode-coding-string + (oref auth :password) 'utf-8)))))) + (object-add-to-list req :install-callbacks + (make-instance (oref auth 2fa-cls) :req req)) + req) + +;;;###autoload +(defclass gh-oauth-authenticator (gh-authenticator) + ((token :initarg :token :protection :private :initform nil)) + "Oauth-based authenticator") + +(defmethod constructor :static ((auth gh-oauth-authenticator) &rest args) + (let ((obj (call-next-method))) + (or (oref obj :token) + (oset obj :token (gh-auth-get-oauth-token))) + obj)) + +(defmethod gh-auth-modify-request ((auth gh-oauth-authenticator) req) + (object-add-to-list req :headers + (cons "Authorization" + (format "token %s" (oref auth :token)))) + req) + +(provide 'gh-auth) +;; to avoid circular dependencies... +(require 'gh-oauth) +;;; gh-auth.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-autoloads.el b/elpa/gh-20160222.1811/gh-autoloads.el new file mode 100644 index 0000000..892a1ae --- /dev/null +++ b/elpa/gh-20160222.1811/gh-autoloads.el @@ -0,0 +1,162 @@ +;;; gh-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "gh-api" "gh-api.el" (22221 60701 548000 0)) +;;; Generated autoloads from gh-api.el + +(require 'eieio) + +(eieio-defclass-autoload 'gh-api 'nil "gh-api" "Github API") + +(eieio-defclass-autoload 'gh-api-v3 '(gh-api) "gh-api" "Github API v3") + +;;;*** + +;;;### (autoloads nil "gh-auth" "gh-auth.el" (22221 60701 635000 +;;;;;; 0)) +;;; Generated autoloads from gh-auth.el + +(require 'eieio) + +(eieio-defclass-autoload 'gh-authenticator 'nil "gh-auth" "Abstract authenticator") + +(eieio-defclass-autoload 'gh-password-authenticator '(gh-authenticator) "gh-auth" "Password-based authenticator") + +(eieio-defclass-autoload 'gh-oauth-authenticator '(gh-authenticator) "gh-auth" "Oauth-based authenticator") + +;;;*** + +;;;### (autoloads nil "gh-cache" "gh-cache.el" (22221 60701 606000 +;;;;;; 0)) +;;; Generated autoloads from gh-cache.el + +(require 'eieio) + +;;;*** + +;;;### (autoloads nil "gh-common" "gh-common.el" (22221 60701 578000 +;;;;;; 0)) +;;; Generated autoloads from gh-common.el + +(require 'eieio) + +;;;*** + +;;;### (autoloads nil "gh-gist" "gh-gist.el" (22221 60701 536000 +;;;;;; 0)) +;;; Generated autoloads from gh-gist.el + +(require 'eieio) + +(eieio-defclass-autoload 'gh-gist-api '(gh-api-v3) "gh-gist" "Gist API") + +(eieio-defclass-autoload 'gh-gist-gist-stub '(gh-object) "gh-gist" "Class for user-created gist objects") + +(eieio-defclass-autoload 'gh-gist-gist '(gh-gist-gist-stub) "gh-gist" "Gist object") + +;;;*** + +;;;### (autoloads nil "gh-issue-comments" "gh-issue-comments.el" +;;;;;; (22221 60701 591000 0)) +;;; Generated autoloads from gh-issue-comments.el + +(require 'eieio) + +;;;*** + +;;;### (autoloads nil "gh-issues" "gh-issues.el" (22221 60701 615000 +;;;;;; 0)) +;;; Generated autoloads from gh-issues.el + +(require 'eieio) + +;;;*** + +;;;### (autoloads nil "gh-oauth" "gh-oauth.el" (22221 60701 531000 +;;;;;; 0)) +;;; Generated autoloads from gh-oauth.el + +(require 'eieio) + +(eieio-defclass-autoload 'gh-oauth-api '(gh-api-v3) "gh-oauth" "OAuth API") + +;;;*** + +;;;### (autoloads nil "gh-orgs" "gh-orgs.el" (22221 60701 586000 +;;;;;; 0)) +;;; Generated autoloads from gh-orgs.el + +(require 'eieio) + +(eieio-defclass-autoload 'gh-orgs-api '(gh-api-v3) "gh-orgs" "Orgs API") + +(eieio-defclass-autoload 'gh-orgs-org-stub '(gh-object) "gh-orgs" nil) + +;;;*** + +;;;### (autoloads nil "gh-pull-comments" "gh-pull-comments.el" (22221 +;;;;;; 60701 627000 0)) +;;; Generated autoloads from gh-pull-comments.el + +(require 'eieio) + +;;;*** + +;;;### (autoloads nil "gh-pulls" "gh-pulls.el" (22221 60701 621000 +;;;;;; 0)) +;;; Generated autoloads from gh-pulls.el + +(require 'eieio) + +(eieio-defclass-autoload 'gh-pulls-api '(gh-api-v3) "gh-pulls" "Git pull requests API") + +(eieio-defclass-autoload 'gh-pulls-request '(gh-pulls-request-stub) "gh-pulls" "Git pull requests API") + +;;;*** + +;;;### (autoloads nil "gh-repos" "gh-repos.el" (22221 60701 598000 +;;;;;; 0)) +;;; Generated autoloads from gh-repos.el + +(require 'eieio) + +(eieio-defclass-autoload 'gh-repos-api '(gh-api-v3) "gh-repos" "Repos API") + +(eieio-defclass-autoload 'gh-repos-repo-stub '(gh-object) "gh-repos" "Class for user-created repository objects") + +(eieio-defclass-autoload 'gh-repos-repo '(gh-repos-repo-stub) "gh-repos" "Class for GitHub repositories") + +;;;*** + +;;;### (autoloads nil "gh-url" "gh-url.el" (22221 60701 497000 0)) +;;; Generated autoloads from gh-url.el + +(require 'eieio) + +;;;*** + +;;;### (autoloads nil "gh-users" "gh-users.el" (22221 60701 569000 +;;;;;; 0)) +;;; Generated autoloads from gh-users.el + +(require 'eieio) + +(eieio-defclass-autoload 'gh-users-api '(gh-api-v3) "gh-users" "Users API") + +(eieio-defclass-autoload 'gh-users-user '(gh-user) "gh-users" nil) + +;;;*** + +;;;### (autoloads nil nil ("gh-pkg.el" "gh-profile.el" "gh.el") (22221 +;;;;;; 60701 645865 81000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; gh-autoloads.el ends here diff --git a/elpa/gh-20160222.1811/gh-cache.el b/elpa/gh-20160222.1811/gh-cache.el new file mode 100644 index 0000000..124bf61 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-cache.el @@ -0,0 +1,136 @@ +;;; gh-cache.el --- caching for gh.el + +;; Copyright (C) 2011 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'pcache) + +(defconst gh-cache-outdated-expiration-delay (* 60 60 24)) + +(defconst gh-cache-internal-version-constant 3) + +(defconst gh-cache-version-constant + (format "%s/gh-%s" pcache-version-constant gh-cache-internal-version-constant)) + +(defclass gh-cache (pcache-repository) + ((version-constant :allocation :class) + (entries :initarg :entries :initform (make-hash-table :test 'equal)) + (safe-methods :allocation :class :initform ("HEAD" "GET" "OPTIONS" "TRACE")) + (invalidation-chain :allocation :class :initform nil) + + (entry-cls :initarg :entry-cls :initform gh-cache-entry))) + +(oset-default 'gh-cache version-constant gh-cache-version-constant) + +(defclass gh-cache-entry (pcache-entry) + ((etag :initarg :etag :initform nil) + (outdated :initarg :outdated :initform nil) + ;; (ttl :initarg :ttl :initform 0) + )) + +(defmethod pcache-invalidate :after ((cache gh-cache) key) + (let ((resource (car key))) + (pcache-map cache #'(lambda (k v) + (when (equal (car k) resource) + (pcache-invalidate cache k)))) + (dolist (next (oref cache invalidation-chain)) + (let ((nextresource + (replace-regexp-in-string (car next) (cdr next) resource))) + (when (not (equal nextresource resource)) + (pcache-map cache #'(lambda (k v) + (when (equal (car k) nextresource) + (pcache-invalidate cache k))))))))) + +(defmethod pcache-get ((cache gh-cache) key &optional default) + (let* ((table (oref cache :entries)) + (entry (gethash key table))) + (if (not entry) + default + (unless (pcache-entry-valid-p entry) + (oset entry :outdated t)) + (oref entry :value)))) + +(defmethod pcache-has ((cache pcache-repository) key) + (let* ((default (make-symbol ":nil")) + (table (oref cache :entries)) + (entry (gethash key table default))) + (not (eq entry default)))) + +(defmethod pcache-purge-invalid ((cache gh-cache)) + (let ((table (oref cache :entries))) + (maphash #'(lambda (k e) + (unless (gh-cache-expired-p e) + (remhash k table))) + table) + (pcache-save cache))) + +(defmethod gh-cache-outdated-p ((cache gh-cache) key) + (let* ((table (oref cache :entries)) + (entry (gethash key table))) + (and entry + (oref entry :outdated)))) + +(defmethod gh-cache-expired-p ((cache gh-cache) key) + (let* ((table (oref cache :entries)) + (entry (gethash key table))) + (and (gh-cache-outdated-p cache key) + (not + (let ((time (float-time (current-time)))) + (< time (+ gh-cache-outdated-expiration-delay + (oref entry :timestamp)))))))) + +(defmethod gh-cache-revive ((cache gh-cache) key) + (let* ((table (oref cache :entries)) + (entry (gethash key table))) + (and entry + (oset entry :outdated nil) + (oset entry :timestamp (float-time (current-time))) + t))) + +(defmethod gh-cache-etag ((cache gh-cache) key) + (let* ((table (oref cache :entries)) + (entry (gethash key table))) + (and entry + (oref entry :etag)))) + +(defmethod gh-cache-set-etag ((cache gh-cache) key etag) + (let* ((table (oref cache :entries)) + (entry (gethash key table))) + (and entry + (oset entry :etag etag)))) + +(provide 'gh-cache) +;;; gh-cache.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-common.el b/elpa/gh-20160222.1811/gh-common.el new file mode 100644 index 0000000..09d8d6a --- /dev/null +++ b/elpa/gh-20160222.1811/gh-common.el @@ -0,0 +1,116 @@ +;;; gh-common.el --- common objects for gh.el + +;; Copyright (C) 2011 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-profile) + +(defgroup gh nil + "Github API client libraries." + :group 'applications) + +(defclass gh-object () + ()) + +(defmethod gh-object-read :static ((obj gh-object) data) + (let ((target (if (object-p obj) obj + (make-instance obj)))) + (when data + (gh-object-read-into target data)) + target)) + +(defmethod gh-object-reader :static ((obj gh-object)) + (apply-partially 'gh-object-read obj)) + +(defmethod gh-object-list-read :static ((obj gh-object) data) + (mapcar (gh-object-reader obj) data)) + +(defmethod gh-object-list-reader :static ((obj gh-object)) + (apply-partially 'gh-object-list-read obj)) + +(defmethod gh-object-read-into ((obj gh-object) data)) + +(defmethod slot-unbound ((obj gh-object) cls slot-name fn) + (if (eq fn 'oref) nil + (call-next-method))) + +(defclass gh-user (gh-object) + ((login :initarg :login) + (id :initarg :id) + (avatar-url :initarg :avatar-url) + (gravatar-url :initarg :gravatar-url) + (url :initarg :url)) + "Github user object") + +(defmethod gh-object-read-into ((user gh-user) data) + (call-next-method) + (with-slots (login id avatar-url gravatar-url url) + user + (setq login (gh-read data 'login) + id (gh-read data 'id) + avatar-url (gh-read data 'avatar_url) + gravatar-url (gh-read data 'gravatar_url) + url (gh-read data 'url)))) + +(defun gh-read (obj field) + (cdr (assoc field obj))) + +(defun gh-namespaced-key (key) + (let ((profile (gh-profile-current-profile))) + (concat "github." + (if (string= profile gh-profile-default-profile) + "" + (concat profile ".")) + key))) + +(defun gh-config (key) + "Returns a GitHub specific value from the global Git config." + (let ((strip (lambda (string) + (if (> (length string) 0) + (substring string 0 (- (length string) 1)))))) + (funcall strip (gh-command-to-string "config" (gh-namespaced-key key))))) + +(defun gh-set-config (key value) + "Sets a GitHub specific value to the global Git config." + (gh-command-to-string "config" "--global" (gh-namespaced-key key) value)) + +(defun gh-command-to-string (&rest args) + (let ((git (executable-find "git"))) + (with-output-to-string + (apply 'process-file git nil standard-output nil args)))) + +(provide 'gh-common) +;;; gh-common.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-gist.el b/elpa/gh-20160222.1811/gh-gist.el new file mode 100644 index 0000000..90c52eb --- /dev/null +++ b/elpa/gh-20160222.1811/gh-gist.el @@ -0,0 +1,191 @@ +;;; gh-gist.el --- gist module for gh.el + +;; Copyright (C) 2011 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-api) +(require 'gh-auth) +(require 'gh-common) + +;;;###autoload +(defclass gh-gist-api (gh-api-v3) + ((gist-cls :allocation :class :initform gh-gist-gist)) + "Gist API") + +;;;###autoload +(defclass gh-gist-gist-stub (gh-object) + ((files :initarg :files :type list :initform nil) + (public :initarg :public) + (description :initarg :description) + + (file-cls :allocation :class :initform gh-gist-gist-file)) + "Class for user-created gist objects") + +(defmethod gh-object-read-into ((stub gh-gist-gist-stub) data) + (call-next-method) + (with-slots (files public description) + stub + (setq files (gh-object-list-read (oref stub file-cls) + (gh-read data 'files)) + public (gh-read data 'public) + description (gh-read data 'description)))) + +;;;###autoload +(defclass gh-gist-gist (gh-gist-gist-stub) + ((date :initarg :date) + (update :initarg :update) + (push-url :initarg :push-url) + (pull-url :initarg :pull-url) + (html-url :initarg :html-url) + (comments :initarg :comments) + (user :initarg :user :initform nil) + (id :initarg :id :type string) + (url :initarg :url :type string) + (forks :initarg :forks :initform nil) + + (user-cls :allocation :class :initform gh-user)) + "Gist object") + +(defmethod gh-object-read-into ((gist gh-gist-gist) data) + (call-next-method) + (with-slots (date update push-url pull-url html-url comments user + id url forks) + gist + (setq date (gh-read data 'created_at) + update (gh-read data 'updated_at) + push-url (gh-read data 'git_push_url) + pull-url (gh-read data 'git_pull_url) + html-url (gh-read data 'html_url) + comments (gh-read data 'comments) + user (gh-object-read (or (oref gist :user) + (oref gist user-cls)) + (gh-read data 'user)) + id (gh-read data 'id) + url (gh-read data 'url) + forks (gh-read data 'forks)))) + +(defclass gh-gist-gist-file (gh-object) + ((filename :initarg :filename) + (size :initarg :size) + (url :initarg :url) + (content :initarg :content))) + +(defmethod gh-object-read-into ((file gh-gist-gist-file) data) + (call-next-method) + (with-slots (filename size url content) + file + (setq + filename (gh-read data 'filename) + size (gh-read data 'size) + url (gh-read data 'raw_url) + content (gh-read data 'content)))) + +(defmethod gh-gist-gist-to-obj ((gist gh-gist-gist-stub)) + `(("description" . ,(oref gist :description)) + ("public" . ,(oref gist :public)) + ("files" . ,(mapcar 'gh-gist-gist-file-to-obj (oref gist :files))))) + +(defmethod gh-gist-gist-has-files ((gist gh-gist-gist-stub)) + (not (memq nil (mapcar (lambda (f) + (oref f :content)) (oref gist :files))))) + +(defmethod gh-gist-gist-file-to-obj ((file gh-gist-gist-file)) + `(,(oref file :filename) . (("filename" . ,(oref file :filename)) + ("content" . ,(oref file :content))))) + +(defmethod gh-gist-list ((api gh-gist-api) &optional username) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api gist-cls)) "GET" + (format "/users/%s/gists" (or username (gh-api-get-username api))))) + +(defmethod gh-gist-list-public ((api gh-gist-api)) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api gist-cls)) "GET" "/gists/public")) + +(defmethod gh-gist-list-starred ((api gh-gist-api)) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api gist-cls)) "GET" "/gists/starred")) + +(defmethod gh-gist-get ((api gh-gist-api) gist-or-id) + (let (id transformer) + (if (stringp gist-or-id) + (setq id gist-or-id + transformer (gh-object-reader (oref api gist-cls))) + (setq id (oref gist-or-id :id) + transformer (gh-object-reader gist-or-id))) + (gh-api-authenticated-request + api transformer "GET" (format "/gists/%s" id)))) + +(defmethod gh-gist-new ((api gh-gist-api) gist-stub) + (gh-api-authenticated-request + api (gh-object-reader (oref api gist-cls)) "POST" "/gists" + (gh-gist-gist-to-obj gist-stub))) + +(defmethod gh-gist-edit ((api gh-gist-api) gist) + (gh-api-authenticated-request + api (gh-object-reader (oref api gist-cls)) "PATCH" + (format "/gists/%s" + (oref gist :id)) + (gh-gist-gist-to-obj gist))) + +(defmethod gh-gist-set-star ((api gh-gist-api) gist-or-id star) + (let ((id (if (stringp gist-or-id) gist-or-id + (oref gist-or-id :id)))) + (gh-api-authenticated-request + api 'ignore (if star "PUT" "DELETE") + (format "/gists/%s/star" id)))) + +(defmethod gh-gist-get-star ((api gh-gist-api) gist-or-id) + (let ((id (if (stringp gist-or-id) gist-or-id + (oref gist-or-id :id)))) + (gh-api-authenticated-request + api 'ignore "GET" (format "/gists/%s/star" id)))) + +(defmethod gh-gist-fork ((api gh-gist-api) gist-or-id) + (let ((id (if (stringp gist-or-id) gist-or-id + (oref gist-or-id :id)))) + (gh-api-authenticated-request + api (gh-object-reader (oref api gist-cls)) "POST" + (format "/gists/%s/forks" id)))) + +(defmethod gh-gist-delete ((api gh-gist-api) gist-or-id) + (let ((id (if (stringp gist-or-id) gist-or-id + (oref gist-or-id :id)))) + (gh-api-authenticated-request + api 'ignore "DELETE" (format "/gists/%s" id)))) + +(provide 'gh-gist) +;;; gh-gist.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-issue-comments.el b/elpa/gh-20160222.1811/gh-issue-comments.el new file mode 100644 index 0000000..1ab8974 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-issue-comments.el @@ -0,0 +1,110 @@ +;;; gh-issue-comments.el --- issue comments api for github + +;; Copyright (C) 2014 Travis Thieman + +;; Author: Travis Thieman +;; Keywords: + +;; This program 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 of the License, or +;; (at your option) any later version. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; TODOS: +;; * Support listing all comments in a repository + +;; Basic usage: + +;; (setf api (gh-issue-comments-api "api" :sync nil :cache nil :num-retries 1)) +;; (setf comments (gh-issue-comments-list api "user" "repo" "issue id")) +;; (setq my-comment (make-instance 'gh-issue-comments-comment :body "This is great!")) +;; (gh-issue-comments-new api "user" "repo" "issue id" my-comment) + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-api) +(require 'gh-auth) +(require 'gh-common) + +(require 'gh-issues) + +(defclass gh-issue-comments-api (gh-api-v3) + ((comment-cls :allocation :class :initform gh-issue-comments-comment)) + "GitHub Issue Comments api") + +(defclass gh-issue-comments-comment (gh-object) + ((url :initarg :url) + (html-url :initarg :html-url) + (body :initarg :body) + (user :initarg :user :initform nil) + (created-at :initarg :created_at) + (updated-at :initarg :updated_at) + + (user-cls :allocation :class :initform gh-user)) + "issues comment") + +(defmethod gh-object-read-into ((comment gh-issue-comments-comment) data) + (call-next-method) + (with-slots (url html-url body user created-at updated-at) + comment + (setq url (gh-read data 'url) + html-url (gh-read data 'html-url) + body (gh-read data 'body) + user (gh-object-read (or (oref comment :user) + (oref comment user-cls)) + (gh-read data 'user)) + created-at (gh-read data 'created_at) + updated-at (gh-read data 'updated_at)))) + +(defmethod gh-issue-comments-list ((api gh-issue-comments-api) user repo issue-id) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api comment-cls)) "GET" + (format "/repos/%s/%s/issues/%s/comments" user repo issue-id))) + +(defmethod gh-issue-comments-get ((api gh-issue-comments-api) user repo comment-id) + (gh-api-authenticated-request + api (gh-object-reader (oref api comment-cls)) "GET" + (format "/repos/%s/%s/issues/comments/%s" user repo comment-id))) + +(defmethod gh-issue-comments-req-to-update ((req gh-issue-comments-comment)) + `(("body" . ,(oref req body)))) + +(defmethod gh-issue-comments-update ((api gh-issue-comments-api) user repo comment-id comment) + (gh-api-authenticated-request + api (gh-object-reader (oref api comment-cls)) "PATCH" + (format "/repos/%s/%s/issues/comments/%s" user repo comment-id) + (gh-issue-comments-req-to-update comment))) + +(defmethod gh-issue-comments-new ((api gh-issue-comments-api) user repo issue-id comment) + (gh-api-authenticated-request + api (gh-object-reader (oref api comment-cls)) "POST" + (format "/repos/%s/%s/issues/%s/comments" user repo issue-id) + (gh-issue-comments-req-to-update comment))) + +(defmethod gh-issue-comments-delete ((api gh-issue-comments-api) user repo comment-id) + (gh-api-authenticated-request + api nil "DELETE" + (format "/repos/%s/%s/issues/comments/%s" user repo comment-id))) + +(provide 'gh-issue-comments) +;;; gh-issue-comments.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-issues.el b/elpa/gh-20160222.1811/gh-issues.el new file mode 100644 index 0000000..c566271 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-issues.el @@ -0,0 +1,308 @@ +;;; gh-issues.el --- issues api for github + +;; Copyright (C) 2012 Raimon Grau + +;; Author: Raimon Grau +;; Keywords: + +;; This program 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 of the License, or +;; (at your option) any later version. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; Basic usage: + +;; (setf api (gh-issues-api "api" :sync nil :cache nil :num-retries 1)) +;; (setf issues (gh-issues-list api "user" "repo")) +;; (last (oref issues data)) ; get one issue +;; (setq mi (make-instance 'gh-issues-issue :body "issue body" :title "issue title")) +;; (gh-issues-issue-new api "user" "repo" mi) + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-api) +(require 'gh-auth) +(require 'gh-common) + +(require 'gh-repos) + +(defclass gh-issues-api (gh-api-v3) + ((issue-cls :allocation :class :initform gh-issues-issue) + (milestone-cls :allocation :class :initform gh-issues-milestone) + (label-cls :allocation :class :initform gh-issues-label)) + "Github Issues api") + +(defclass gh-issues-issue (gh-object) + ((url :initarg :url) + (html-url :initarg :html-url) + (number :initarg :number) + (state :initarg :state) + (title :initarg :title) + (body :initarg :body) + (user :initarg :user :initform nil) + (labels :initarg :labels :initform nil) + (assignee :initarg :assignee :initform nil) + (milestone :initarg :milestone :initform nil) + (open_issues :initarg :open_issues) + (closed_issues :initarg :closed_issues) + (created_at :initarg :created_at) + (due_on :initarg :due_on) + + (user-cls :allocation :class :initform gh-user) + (milestone-cls :allocation :class :initform gh-issues-milestone)) + "issues request") + +(defclass gh-issues-label (gh-object) + ((url :initarg :url) + (name :initarg :name) + (color :initarg :color))) + +(defclass gh-issues-milestone (gh-object) + ((url :initarg :url) + (number :initarg :number) + (state :initarg :state) + (title :initarg :title) + (description :initarg :description) + (creator :initarg :creator :initform nil) + (open_issues :initarg :open_issues) + (closed_issues :initarg :closed_issues) + (created_at :initarg :created_at) + (due_on :initarg :due_on) + + (user-cls :allocation :class :initform gh-user)) + "github milestone") + +(defmethod gh-object-read-into ((issue gh-issues-issue) data) + (call-next-method) + (with-slots (url html-url number state title body + user labels assignee milestone open_issues + closed_issues created_at due_on) + issue + (setq url (gh-read data 'url) + html-url (gh-read data 'html_url) + number (gh-read data 'number) + state (gh-read data 'state) + title (gh-read data 'title) + body (gh-read data 'body) + user (gh-object-read (or (oref issue :user) + (oref issue user-cls)) + (gh-read data 'user)) + labels (gh-read data 'labels) + assignee (gh-object-read (or (oref issue :assignee) + (oref issue user-cls)) + (gh-read data 'assignee)) + milestone (gh-object-read (or (oref issue :milestone) + (oref issue milestone-cls)) + (gh-read data 'milestone)) + open_issues (gh-read data 'open_issues) + closed_issues (gh-read data 'closed_issues) + created_at (gh-read data 'created_at) + due_on (gh-read data 'due_on)))) + + +(defmethod gh-object-read-into ((milestone gh-issues-milestone) data) + (call-next-method) + (with-slots (url number state title description creator + open_issues closed_issues + created_at due_on) + milestone + (setq url (gh-read data 'url) + number (gh-read data 'number) + state (gh-read data 'state) + title (gh-read data 'title) + description (gh-read data 'description) + creator (gh-object-read (or (oref milestone :creator) + (oref milestone user-cls)) + (gh-read data 'creator)) + + open_issues (gh-read data 'open_issues) + closed_issues (gh-read data 'closed_issues) + created_at (gh-read data 'created_at) + due_on (gh-read data 'due_on)))) + +(defmethod gh-issues-issue-list ((api gh-issues-api) user repo) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api issue-cls)) "GET" + (format "/repos/%s/%s/issues" user repo))) + +(defmethod gh-issues-milestone-list ((api gh-issues-api) user repo) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api milestone-cls)) "GET" + (format "/repos/%s/%s/milestones" user repo))) + +(defmethod gh-issues-milestone-get ((api gh-issues-api) user repo id) + (gh-api-authenticated-request + api (gh-object-reader (oref api milestone-cls)) "GET" + (format "/repos/%s/%s/milestones/%s" user repo id))) + +(defmethod gh-issues-milestone-new ((api gh-issues-api) user repo milestone) + (gh-api-authenticated-request + api (gh-object-reader (oref api milestone-cls)) "POST" + (format "/repos/%s/%s/milestones" user repo) + (gh-issues-milestone-req-to-update milestone))) + +(defmethod gh-issues-milestone-update ((api gh-issues-api) user repo + id milestone) + (gh-api-authenticated-request + api (gh-object-reader (oref api milestone-cls)) "PATCH" + (format "/repos/%s/%s/milestones/%s" user repo id) + (gh-issues-milestone-req-to-update milestone))) + +(defmethod gh-issues-milestone-req-to-update ((milestone gh-issues-milestone)) + (let ((state (oref milestone state) ) + (description (oref milestone description)) + (due_on (oref milestone due_on)) + (to-update `(("title" . ,(oref milestone title))))) + (when state (nconc to-update `(("state" . ,state)))) + (when description (nconc to-update `(("description" . ,description)))) + (when due_on (nconc to-update `(("due_on" . ,due_on)))) + to-update)) + +(defmethod gh-issues-issue-get ((api gh-issues-api) user repo id) + (gh-api-authenticated-request + api (gh-object-reader (oref api issue-cls)) "GET" + (format "/repos/%s/%s/issues/%s" user repo id))) + +(defmethod gh-issues-issue-req-to-update ((req gh-issues-issue)) + (let ((assignee (oref req assignee)) + ;; (labels (oref req labels)) + (milestone (oref req milestone)) + (to-update `(("title" . ,(oref req title)) + ("state" . ,(oref req state)) + ("body" . ,(oref req body))))) + + ;; (when labels (nconc to-update `(("labels" . ,(oref req labels) )))) + (when milestone + (nconc to-update `(("milestone" . ,(oref milestone number))))) + (when assignee + (nconc to-update `(("assignee" . ,(oref assignee login) )))) + to-update)) + +(defmethod gh-issues-issue-update ((api gh-issues-api) user repo id req) + (gh-api-authenticated-request + api (gh-object-reader (oref api issue-cls)) "PATCH" + (format "/repos/%s/%s/issues/%s" user repo id) + (gh-issues-issue-req-to-update req))) + +(defmethod gh-issues-issue-new ((api gh-issues-api) user repo issue) + (gh-api-authenticated-request + api (gh-object-reader (oref api issue-cls)) "POST" + (format "/repos/%s/%s/issues" user repo) + (gh-issues-issue-req-to-update issue))) + +;;; labels +(defclass gh-issues-label (gh-object) + ((url :initarg :url) + (name :initarg :name) + (color :initarg :color))) + +(defmethod gh-object-read-into ((label gh-issues-label) data) + (call-next-method) + (with-slots (url name color) + label + (setq url (gh-read data 'url) + name (gh-read data 'name) + color (gh-read data 'color)))) + +(defmethod gh-issues-label-req-to-update ((label gh-issues-label)) + `(("name" . ,(oref label name)) + ("color" . ,(oref label color)))) + +(defmethod gh-issues-label-get ((api gh-issues-api) user repo name) + (gh-api-authenticated-request + api (gh-object-reader (oref api label-cls)) "GET" + (format "/repos/%s/%s/labels/%s" user repo name))) + +(defmethod gh-issues-label-list ((api gh-issues-api) user repo) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api label-cls)) "GET" + (format "/repos/%s/%s/labels" user repo ))) + +(defmethod gh-issues-label-new ((api gh-issues-api) user repo req) + (gh-api-authenticated-request + api (gh-object-reader (oref api label-cls)) "POST" + (format "/repos/%s/%s/labels" user repo) + (gh-issues-label-req-to-update req))) + +(defmethod gh-issues-label-update ((api gh-issues-api) user repo req) + (gh-api-authenticated-request + api (gh-object-reader (oref api label-cls)) "POST" + (format "/repos/%s/%s/labels/%s" user repo (oref req name)) + (gh-issues-label-req-to-update req))) + +(defmethod gh-issues-label-delete ((api gh-issues-api) user repo name) + (gh-api-authenticated-request + api (gh-object-reader (oref api label-cls)) "DELETE" + (format "/repos/%s/%s/labels/%s" user repo name))) + + +(defmethod gh-issues-labels-in-issue ((api gh-issues-api) user repo + issue-or-issue-id) + (let ((issue-id (gh-issues--issue-id issue-or-issue-id))) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api label-cls)) "GET" + (format "/repos/%s/%s/issues/%s/labels" user repo issue-id)))) + +(defmethod gh-issues-labels-add-to-issue ((api gh-issues-api) user repo + issue-or-issue-id labels) + (let ((issue-id (gh-issues--issue-id issue-or-issue-id))) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api label-cls)) "PUT" + (format "/repos/%s/%s/issues/%s/labels" user repo issue-id) + (mapcar #'gh-issues--label-name labels)))) + +(defmethod gh-issues-labels-remove-all-from-issue ((api gh-issues-api) user repo + issue-or-issue-id ) + (let ((issue-id (gh-issues--issue-id issue-or-issue-id))) + (gh-api-authenticated-request + api (lambda (x) x) "DELETE" + (format "/repos/%s/%s/issues/%s/labels" user repo issue-id)))) + +(defmethod gh-issues-labels-in-milestone ((api gh-issues-api) user repo + milestone-or-milestone-id) + (let ((milestone-id (gh-issues--milestone-id milestone-or-milestone-id))) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api label-cls)) "GET" + (format "/repos/%s/%s/milestones/%s/labels" user repo milestone-id)))) + + +;;; helpers + +(defun gh-issues--issue-id (issue-or-issue-id) + (if (eieio-object-p issue-or-issue-id) + (oref issue-or-issue-id id) + issue-or-issue-id)) + +(defun gh-issues--milestone-id (milestone-or-milestone-id) + (if (eieio-object-p milestone-or-milestone-id) + (oref milestone-or-milestone-id id) + milestone-or-milestone-id)) + +(defun gh-issues--label-name (label-or-label-name) + (if (eieio-object-p label-or-label-name) + (oref label-or-label-name name) + label-or-label-name)) + + +(provide 'gh-issues) +;;; gh-issues.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-oauth.el b/elpa/gh-20160222.1811/gh-oauth.el new file mode 100644 index 0000000..e41b6df --- /dev/null +++ b/elpa/gh-20160222.1811/gh-oauth.el @@ -0,0 +1,119 @@ +;;; gh-oauth.el --- oauth module for gh.el + +;; Copyright (C) 2012 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-api) +(require 'gh-auth) +(require 'gh-common) + +;;;###autoload +(defclass gh-oauth-api (gh-api-v3) + ((auth-cls :allocation :class :initform gh-oauth-authorization)) + "OAuth API") + +(defclass gh-oauth-password-authenticator (gh-password-authenticator) + ((remember :allocation :class :initform nil))) + +(defmethod constructor :static ((api gh-oauth-api) &rest args) + ;; force password authentication for this API + (let ((gh-api-v3-authenticator 'gh-oauth-password-authenticator)) + (call-next-method))) + +(defclass gh-oauth-authorization (gh-object) + ((id :initarg :id) + (url :initarg :url) + (scopes :initarg :scopes) + (token :initarg :token) + (app :initarg :app :initform nil) + (updated-at :initarg :updated-at) + (created-at :initarg :created-at) + + (app-cls :allocation :class :initform gh-oauth-app))) + +(defmethod gh-object-read-into ((auth gh-oauth-authorization) data) + (call-next-method) + (with-slots (id url scopes token app updated-at created-at) + auth + (setq id (gh-read data 'id) + url (gh-read data 'url) + scopes (gh-read data 'scopes) + token (gh-read data 'token) + app (gh-object-read (or (oref auth :app) + (oref auth app-cls)) + (gh-read data 'app)) + updated-at (gh-read data 'updated_at) + created-at (gh-read data 'created_at)))) + +(defclass gh-oauth-app (gh-object) + ((url :initarg :url) + (name :initarg :name))) + +(defmethod gh-object-read-into ((app gh-oauth-app) data) + (call-next-method) + (with-slots (url name) + app + (setq url (gh-read data 'url) + name (gh-read data 'name)))) + +(defmethod gh-oauth-auth-list ((api gh-oauth-api)) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api auth-cls)) "GET" + (format "/authorizations"))) + +(defmethod gh-oauth-auth-get ((api gh-oauth-api) id) + (gh-api-authenticated-request + api (gh-object-reader (oref api auth-cls)) "GET" + (format "/authorizations/%s" id))) + +(defmethod gh-oauth-auth-new ((api gh-oauth-api) &optional scopes) + (gh-api-authenticated-request + api (gh-object-reader (oref api auth-cls)) "POST" + (format "/authorizations") (list (cons 'scopes scopes) + (cons 'note (format "gh.el - %s" + (system-name)))))) + +(defmethod gh-oauth-auth-update ((api gh-oauth-api) id &optional scopes) + (gh-api-authenticated-request + api (gh-object-reader (oref api auth-cls)) "PATCH" + (format "/authorizations/%s" id) (list (cons 'scopes scopes)))) + +(defmethod gh-oauth-auth-delete ((api gh-oauth-api) id) + (gh-api-authenticated-request + api nil "DELETE" (format "/authorizations/%s" id))) + +(provide 'gh-oauth) +;;; gh-oauth.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-orgs.el b/elpa/gh-20160222.1811/gh-orgs.el new file mode 100644 index 0000000..d4121b8 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-orgs.el @@ -0,0 +1,163 @@ +;;; gh-org.el --- orgs module for gh.el + +;; Copyright (C) 2012 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-api) +(require 'gh-auth) +(require 'gh-common) + +;;;###autoload +(defclass gh-orgs-api (gh-api-v3) + ((org-cls :allocation :class :initform gh-orgs-org)) + "Orgs API") + +;;;###autoload +(defclass gh-orgs-org-stub (gh-object) + ((login :initarg :login) + (id :initarg :id) + (url :initarg :url) + (avatar-url :initarg :avatar-url))) + +(defmethod gh-object-read-into ((stub gh-orgs-org-stub) data) + (call-next-method) + (with-slots (login id url avatar-url) + stub + (setq login (gh-read data 'login) + id (gh-read data 'id) + url (gh-read data 'url) + avatar-url (gh-read data 'avatar_url)))) + +(defclass gh-orgs-plan (gh-object) + ((name :initarg :name) + (space :initarg :space) + (private-repos :initarg :private-repos))) + +(defmethod gh-object-read-into ((plan gh-orgs-plan) data) + (call-next-method) + (with-slots (name space private-repos) + plan + (setq name (gh-read data 'name) + space (gh-read data 'space) + private-repos (gh-read data 'private_repos)))) + +(defclass gh-orgs-org (gh-orgs-org-stub) + ((name :initarg :name) + (company :initarg :company) + (blog :initarg :blog) + (location :initarg :location) + (email :initarg :email) + (public-repos :initarg :public-repos) + (public-gists :initarg :public-gists) + (followers :initarg :followers) + (following :initarg :following) + (html-url :initarg :html-url) + (created-at :initarg :created-at) + (type :initarg :type) + (total-private-repos :initarg :total-private-repos) + (owned-private-repos :initarg :owned-private-repos) + (private-gists :initarg :private-gists) + (disk-usage :initarg :disk-usage) + (collaborators :initarg :collaborators) + (billing-email :initarg :billing-email) + (plan :initarg :plan :initform nil) + + (plan-cls :allocation :class :initform gh-orgs-plan)) + "Class for GitHub organizations") + +(defmethod gh-object-read-into ((org gh-orgs-org) data) + (call-next-method) + (with-slots (name company blog location email + public-repos public-gists followers following + html-url created-at type + total-private-repos owned-private-repos + private-gists disk-usage collaborators + billing-email plan) + org + (setq name (gh-read data 'name) + company (gh-read data 'company) + blog (gh-read data 'blog) + location (gh-read data 'location) + email (gh-read data 'email) + public-repos (gh-read data 'public_repos) + public-gists (gh-read data 'public_gists) + followers (gh-read data 'followers) + following (gh-read data 'following) + html-url (gh-read data 'html_url) + created-at (gh-read data 'created_at) + type (gh-read data 'type) + total-private-repos (gh-read data 'total_private_repos) + owned-private-repos (gh-read data 'owned_private_repos) + private-gists (gh-read data 'private_gists) + disk-usage (gh-read data 'disk_usage) + collaborators (gh-read data 'collaborators) + billing-email (gh-read data 'billing_email) + plan (gh-object-read (or (oref org :plan) + (oref org plan-cls)) + (gh-read data 'plan))))) + +(defmethod gh-orgs-org-to-obj ((org gh-orgs-org)) + `(,@(when (slot-boundp org :billing-email) + (list (cons "billing_email" (oref org :billing-email)))) + ,@(when (slot-boundp org :blog) + (list (cons "blog" (oref org :blog)))) + ,@(when (slot-boundp org :company) + (list (cons "company" (oref org :company)))) + ,@(when (slot-boundp org :email) + (list (cons "email" (oref org :email)))) + ,@(when (slot-boundp org :location) + (list (cons "location" (oref org :location)))) + ,@(when (slot-boundp org :name) + (list (cons "name" (oref org :name)))))) + +(defmethod gh-orgs-list ((api gh-orgs-api) &optional username) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api org-cls)) "GET" + (format "/users/%s/orgs" (or username (gh-api-get-username api))))) + +(defmethod gh-orgs-get ((api gh-orgs-api) org) + (gh-api-authenticated-request + api (gh-object-reader (oref api org-cls)) "GET" + (format "/orgs/%s" org))) + +(defmethod gh-orgs-update ((api gh-orgs-api) org-obj) + (gh-api-authenticated-request + api (gh-object-reader (oref api org-cls)) "PATCH" + (format "/orgs/%s" (oref org-obj :login)) + (apply 'gh-orgs-org-to-obj org-obj nil))) + +(provide 'gh-orgs) +;;; gh-org.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-pkg.el b/elpa/gh-20160222.1811/gh-pkg.el new file mode 100644 index 0000000..86a87b6 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-pkg.el @@ -0,0 +1,7 @@ +(define-package "gh" "20160222.1811" "A GitHub library for Emacs" + '((emacs "24.4") + (pcache "0.3.1") + (logito "0.1"))) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/gh-20160222.1811/gh-profile.el b/elpa/gh-20160222.1811/gh-profile.el new file mode 100644 index 0000000..d2550c7 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-profile.el @@ -0,0 +1,103 @@ +;;; gh-profile.el --- profile support for gh.el + +;; Copyright (C) 2013 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +(require 'rx) +(require 'url-parse) + +(defgroup gh-profile nil + "Github profile." + :group 'gh) + +(defun gh-profile-remote-regexp (domain) + (eval + `(rx bol (or ,(concat "git@" domain ":") + (and (or "git" "ssh" "http" "https") "://" + (* nonl) (? "@") ,domain "/")) + (and (group (* nonl)) "/" (group (* nonl))) (? ".git")))) + +(defcustom gh-profile-alist `(("github" + :url "https://api.github.com" + :remote-regexp + ,(gh-profile-remote-regexp "github.com"))) + "List of profiles for Github access. List every Github +Enterprise server and/or Github accounts you have access +to here." + :type '(alist :key-type string + :value-type (plist :key-type (choice (const :url) + (const :username) + (const :password) + (const :token) + (const :remote-regexp)) + :value-type string)) + :group 'gh-profile) + +(defun gh-profile-get-remote-regexp (profile) + (let* ((profile-plist (cdr (assoc profile gh-profile-alist))) + (regexp (plist-get profile-plist :remote-regexp))) + (if regexp + regexp + ;; try to guess remote format (just use the hostname) + (let* ((url (url-generic-parse-url (plist-get profile-plist :url))) + (host (url-host url))) + (gh-profile-remote-regexp host))))) + +(defcustom gh-profile-default-profile "github" + "Default profile. This needs to be a key present in + `gh-profile-alist'" + :type 'string + :group 'gh-profile) + +(defvar gh-profile-current-profile nil) +(make-variable-buffer-local 'gh-profile-current-profile) + +(defun gh-profile-current-profile () + (or gh-profile-current-profile + gh-profile-default-profile)) + +(defun gh-profile-url () + (plist-get (cdr (assoc (or gh-profile-current-profile + gh-profile-default-profile) + gh-profile-alist)) :url)) + +(defun gh-profile-completing-read () + (let ((profiles (mapcar #'car gh-profile-alist))) + (if (> (length profiles) 1) + (completing-read "Github profile: " profiles nil t nil nil (first profiles)) + (car profiles)))) + +(defun gh-profile-get-remote-profile (remote-url) + (loop for (id . props) in gh-profile-alist + if (string-match (gh-profile-get-remote-regexp id) + remote-url) + return id)) + +(provide 'gh-profile) +;;; gh-profile.el ends here diff --git a/elpa/gh-20160222.1811/gh-pull-comments.el b/elpa/gh-20160222.1811/gh-pull-comments.el new file mode 100644 index 0000000..b00daa4 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-pull-comments.el @@ -0,0 +1,139 @@ +;;; gh-pull-comments.el --- pull request comments api for github + +;; Copyright (C) 2014 Toni Reina + +;; Author: Toni Reina +;; Keywords: + +;; This program 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 of the License, or +;; (at your option) any later version. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; TODOS: +;; * Support listing all comments in a repository + +;; Basic usage: + +;; (setf api (gh-pull-comments-api "api" :sync nil :cache nil :num-retries 1)) +;; (setf comments (gh-pull-comments-list api "user" "repo" "pull request id")) +;; (setq my-comment (make-instance 'gh-pull-comments-comment +;; :body "This is great!" +;; :path "README.md" +;; :position 2 +;; :commit-id "commit sha")) +;; (gh-pull-comments-new api "user" "repo" "pull request id" my-comment) + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-api) +(require 'gh-auth) +(require 'gh-common) + +(defclass gh-pull-comments-api (gh-api-v3) + ((pull-comment-cls :allocation :class :initform gh-pull-comments-comment)) + "GitHub Pull Request Comments API") + +(defclass gh-pull-comments-comment (gh-object) + ((url :initarg :url) + (html-url :initarg :html-url) + (id :initarg :id) + (body :initarg :body) + (user :initarg :user :initform nil) + (path :initarg :path) + (diff-hunk :initarg :diff-hunk) + (position :initarg :position) + (original-position :initarg :original-position) + (commit-id :initarg :commit-id) + (original-commit-id :initarg :original-commit-id) + (in-reply-to :initarg :in-reply-to :initform nil) + (created-at :initarg :created_at) + (updated-at :initarg :updated_at) + (user-cls :allocation :class :initform gh-user)) + "Class for Pull Requests comments") + +(defmethod gh-object-read-into ((comment gh-pull-comments-comment) data) + (call-next-method) + (with-slots (url html-url id body user path diff-hunk position + original-position commit-id original-commit-id in-reply-to + created-at updated-at) + comment + (setq url (gh-read data 'url) + html-url (gh-read data 'html-url) + id (gh-read data 'id) + body (gh-read data 'body) + user (gh-object-read (or (oref comment :user) + (oref comment user-cls)) + (gh-read data 'user)) + path (gh-read data 'path) + diff-hunk (gh-read data 'diff_hunk) + position (gh-read data 'position) + original-position (gh-read data 'original_position) + commit-id (gh-read data 'commit_id) + original-commit-id (gh-read data 'original_commit_id) + in-reply-to (gh-read data 'in_reply_to) + created-at (gh-read data 'created_at) + updated-at (gh-read data 'updated_at)))) + +(defmethod gh-pull-comments-list ((api gh-pull-comments-api) user repo pull-id) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api pull-comment-cls)) "GET" + (format "/repos/%s/%s/pulls/%s/comments" user repo pull-id))) + +(defmethod gh-pull-comments-get ((api gh-pull-comments-api) user repo pull-id) + (gh-api-authenticated-request + api (gh-object-reader (oref api pull-comment-cls)) "GET" + (format "/repos/%s/%s/pulls/comments/%s" user repo pull-id))) + +(defmethod gh-pull-comments-req-to-create ((req gh-pull-comments-comment)) + (let ((in-reply-to (oref req in-reply-to)) + (to-update `(("body" . ,(oref req body))))) + (if in-reply-to + (nconc to-update `(("in_reply_to" . ,in-reply-to))) + (nconc to-update `(("commit_id" . ,(oref req commit-id)) + ("path" . ,(oref req path)) + ("position" . ,(oref req position))))) + to-update)) + +(defmethod gh-pull-comments-req-to-update ((req gh-pull-comments-comment)) + `(("body" . ,(oref req body)))) + +(defmethod gh-pull-comments-update ((api gh-pull-comments-api) user repo comment-id comment) + (gh-api-authenticated-request + api (gh-object-reader (oref api pull-comment-cls)) "PATCH" + (format "/repos/%s/%s/pulls/comments/%s" user repo comment-id) + (gh-pull-comments-req-to-update comment))) + +(defmethod gh-pull-comments-new ((api gh-pull-comments-api) user repo pull-id comment) + (gh-api-authenticated-request + api (gh-object-reader (oref api pull-comment-cls)) "POST" + (format "/repos/%s/%s/pulls/%s/comments" user repo pull-id) + (gh-pull-comments-req-to-create comment))) + +(defmethod gh-pull-comments-delete ((api gh-pull-comments-api) user repo comment-id) + (gh-api-authenticated-request + api nil "DELETE" + (format "/repos/%s/%s/pulls/comments/%s" user repo comment-id))) + +(provide 'gh-pull-comments) +;;; gh-pull-comments.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-pulls.el b/elpa/gh-20160222.1811/gh-pulls.el new file mode 100644 index 0000000..99e391e --- /dev/null +++ b/elpa/gh-20160222.1811/gh-pulls.el @@ -0,0 +1,172 @@ +;;; gh-pulls.el --- pull requests module for gh.el + +;; Copyright (C) 2011 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-api) +(require 'gh-auth) +(require 'gh-common) + +(require 'gh-repos) + +(defclass gh-pulls-cache (gh-cache) + ((invalidation-chain :allocation :class + :initform '(("^/repos/.*/.*/pulls$" . "\0") + ("^/repos/.*/.*/pulls/.*$" . "\0"))))) + +;;;###autoload +(defclass gh-pulls-api (gh-api-v3) + ((cache-cls :allocation :class :initform gh-pulls-cache) + + (req-cls :allocation :class :initform gh-pulls-request)) + "Git pull requests API") + +(defclass gh-pulls-request-stub (gh-object) + ((url :initarg :url) + (html-url :initarg :html-url) + (diff-url :initarg :diff-url) + (patch-url :initarg :patch-url) + (issue-url :initarg :issue-url) + (number :initarg :number) + (state :initarg :state) + (title :initarg :title) + (body :initarg :body) + (created-at :initarg :created-at) + (updated-at :initarg :updated-at) + (closed-at :initarg :closed-at) + (merged-at :initarg :merged-at) + (head :initarg :head :initform nil) + (base :initarg :base :initform nil) + + (ref-cls :allocation :class :initform gh-repos-ref))) + +(defmethod gh-object-read-into ((stub gh-pulls-request-stub) data) + (call-next-method) + (with-slots (url html-url diff-url patch-url issue-url number + state title body created-at updated-at + closed-at merged-at head base) + stub + (setq url (gh-read data 'url) + html-url (gh-read data 'html_url) + diff-url (gh-read data 'diff_url) + patch-url (gh-read data 'patch_url) + issue-url (gh-read data 'issue_url) + number (gh-read data 'number) + state (gh-read data 'state) + title (gh-read data 'title) + body (gh-read data 'body) + created-at (gh-read data 'created_at) + updated-at (gh-read data 'updated_at) + closed-at (gh-read data 'closed_at) + merged-at (gh-read data 'merged_at) + head (gh-object-read (or (oref stub :head) + (oref stub ref-cls)) + (gh-read data 'head)) + base (gh-object-read (or (oref stub :base) + (oref stub ref-cls)) + (gh-read data 'base))))) + +;;;###autoload +(defclass gh-pulls-request (gh-pulls-request-stub) + ((merged :initarg :merged) + (mergeable :initarg :mergeable) + (merged-by :initarg :merged-by) + (comments :initarg :comments) + (user :initarg :user :initform nil) + (commits :initarg :commits) + (additions :initarg :additions) + (deletions :initarg :deletions) + (changed-files :initarg :changed-files) + + (ref-cls :allocation :class :initform gh-repos-ref) + (user-cls :allocation :class :initform gh-user)) + "Git pull requests API") + +(defmethod gh-object-read-into ((req gh-pulls-request) data) + (call-next-method) + (with-slots (merged mergeable + merged-by comments user commits additions + deletions changed-files) + req + (setq merged (gh-read data 'merged) + mergeable (gh-read data 'mergeable) + merged-by (gh-read data 'merged_by) + comments (gh-read data 'comments) + user (gh-object-read (or (oref req :user) + (oref req user-cls)) + (gh-read data 'user)) + commits (gh-read data 'commits) + additions (gh-read data 'additions) + deletions (gh-read data 'deletions) + changed-files (gh-read data 'changed_files)))) + +(defmethod gh-pulls-req-to-new ((req gh-pulls-request)) + (let ((head (oref req :head)) + (base (oref req :base))) + `(("title" . ,(oref req :title)) + ("body" . ,(oref req :body)) + ("head" . ,(or (oref head :ref) (oref head :sha))) + ("base" . ,(or (oref base :ref) (oref base :sha)))))) + +(defmethod gh-pulls-req-to-update ((req gh-pulls-request-stub)) + `(("title" . ,(oref req :title)) + ("body" . ,(oref req :body)) + ("state" . ,(oref req :state)))) + +(defmethod gh-pulls-list ((api gh-pulls-api) user repo) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api req-cls)) "GET" + (format "/repos/%s/%s/pulls" user repo))) + +(defmethod gh-pulls-get ((api gh-pulls-api) user repo id) + (gh-api-authenticated-request + api (gh-object-reader (oref api req-cls)) "GET" + (format "/repos/%s/%s/pulls/%s" user repo id))) + +(defmethod gh-pulls-new ((api gh-pulls-api) user repo req) + (gh-api-authenticated-request + api (gh-object-reader (oref api req-cls)) "POST" + (format "/repos/%s/%s/pulls" user repo) + (gh-pulls-req-to-new req))) + +(defmethod gh-pulls-update ((api gh-pulls-api) user repo id req) + (gh-api-authenticated-request + api (gh-object-reader (oref api req-cls)) "PATCH" + (format "/repos/%s/%s/pulls/%s" user repo id) + (gh-pulls-req-to-update req))) + +(provide 'gh-pulls) +;;; gh-pulls.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-repos.el b/elpa/gh-20160222.1811/gh-repos.el new file mode 100644 index 0000000..7204ea3 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-repos.el @@ -0,0 +1,390 @@ +;;; gh-repos.el --- repos module for gh.el + +;; Copyright (C) 2011 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-api) +(require 'gh-auth) +(require 'gh-common) + +;;;###autoload +(defclass gh-repos-api (gh-api-v3) + ((repo-cls :allocation :class :initform gh-repos-repo) + (user-cls :allocation :class :initform gh-user)) + "Repos API") + +;;;###autoload +(defclass gh-repos-repo-stub (gh-object) + ((name :initarg :name) + (description :initarg :description) + (homepage :initarg :homepage) + (private :initarg :private)) + "Class for user-created repository objects") + +(defmethod gh-object-read-into ((stub gh-repos-repo-stub) data) + (call-next-method) + (with-slots (name description homepage private) + stub + (setq name (gh-read data 'name) + description (gh-read data 'description) + homepage (gh-read data 'homepage) + private (gh-read data 'private)))) + +;;;###autoload +(defclass gh-repos-repo (gh-repos-repo-stub) + ((url :initarg :url) + (html-url :initarg :html-url) + (clone-url :initarg :clone-url) + (git-url :initarg :git-url) + (ssh-url :initarg :ssh-url) + (svn-url :initarg :svn-url) + (mirror-url :initarg :mirror-url) + (owner :initarg :owner :initform nil) + (id :initarg :id) + (full-name :initarg full-name) + (language :initarg :language) + (fork :initarg :fork) + (forks :initarg :forks) + (forks-count :initarg forks-count) + (watchers :initarg :watchers) + (watchers-count :initarg watchers-count) + (size :initarg :size) + (master-branch :initarg :master-branch) + (open-issues :initarg :open-issues) + (pushed-at :initarg :pushed-at) + (created-at :initarg :created-at) + (updated-at :initarg :updated-at) + (organisation :initarg :organisation :initform nil) + (parent :initarg :parent) + (source :initarg :source) + (has-issues :initarg :has-issues) + (has-wiki :initarg :has-wiki) + (has-downloads :initarg :has-downloads) + + (owner-cls :allocation :class :initform gh-user) + (organisation-cls :allocation :class :initform gh-user) + (parent-cls :allocation :class :initform gh-repos-repo) + (source-cls :allocation :class :initform gh-repos-repo)) + "Class for GitHub repositories") + +(defmethod gh-object-read-into ((repo gh-repos-repo) data) + (call-next-method) + (with-slots (url html-url clone-url git-url ssh-url svn-url mirror-url + id owner full-name language fork forks forks-count + watchers watchers-count size master-branch open-issues + pushed-at created-at organisation parent source + has-issues has-wiki has-downloads) + repo + (setq url (gh-read data 'url) + html-url (gh-read data 'html_url) + clone-url (gh-read data 'clone_url) + git-url (gh-read data 'git_url) + ssh-url (gh-read data 'ssh_url) + svn-url (gh-read data 'svn_url) + mirror-url (gh-read data 'mirror_url) + id (gh-read data 'id) + owner (gh-object-read (or (oref repo :owner) + (oref repo owner-cls)) + (gh-read data 'owner)) + full-name (gh-read data 'full_name) + language (gh-read data 'language) + fork (gh-read data 'fork) + forks (gh-read data 'forks) + forks-count (gh-read data 'forks_count) + watchers (gh-read data 'watchers) + watchers-count (gh-read data 'watchers_count) + size (gh-read data 'size) + master-branch (gh-read data 'master_branch) + open-issues (gh-read data 'open_issues) + pushed-at (gh-read data 'pushed_at) + created-at (gh-read data 'created_at) + organisation (gh-object-read (or (oref repo :organisation) + (oref repo organisation-cls)) + (gh-read data 'organisation)) + parent (gh-object-read (oref repo parent-cls) + (gh-read data 'parent)) + source (gh-object-read (oref repo source-cls) + (gh-read data 'source)) + has-issues (gh-read data 'has_issues) + has-wiki (gh-read data 'has_wiki) + has-downloads (gh-read data 'has_downloads)))) + +(defclass gh-repos-ref (gh-object) + ((label :initarg :label) + (ref :initarg :ref :initform nil) + (sha :initarg :sha :initform nil) + (user :initarg :user :initform nil) + (repo :initarg :repo :initform nil) + + (user-cls :allocation :class :initform gh-user) + (repo-cls :allocation :class :initform gh-repos-repo))) + +(defmethod gh-object-read-into ((r gh-repos-ref) data) + (call-next-method) + (with-slots (label ref sha user repo) + r + (setq label (gh-read data 'label) + ref (gh-read data 'ref) + sha (gh-read data 'sha) + user (gh-object-read (or (oref r :user) + (oref r user-cls)) + (gh-read data 'user)) + repo (gh-object-read (or (oref r :repo) + (oref r repo-cls)) + (gh-read data 'repo))))) + +(defmethod gh-repos-user-list ((api gh-repos-api) &optional username) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api repo-cls)) "GET" + (format "/users/%s/repos" (or username (gh-api-get-username api))))) + +(defmethod gh-repos-org-list ((api gh-repos-api) org) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api repo-cls)) "GET" + (format "/orgs/%s/repos" org))) + +(defmethod gh-repos-repo-to-obj ((repo gh-repos-repo-stub) + &rest caps) + (let ((has_issues (plist-member caps :issues)) + (has_wiki (plist-member caps :wiki)) + (has_downloads (plist-member caps :downloads))) + `(("name" . ,(oref repo :name)) + ,@(when (slot-boundp repo :homepage) + (list (cons "homepage" (oref repo :homepage)))) + ,@(when (slot-boundp repo :description) + (list (cons "description" (oref repo :description)))) + ,@(when (slot-boundp repo :private) + (list (cons "public" (not (oref repo :private))))) + ,@(when has_issues + (list (cons "has_issues" (plist-get caps :issues)))) + ,@(when has_wiki + (list (cons "has_wiki" (plist-get caps :wiki)))) + ,@(when has_downloads + (list (cons "has_downloads" (plist-get caps :downloads))))))) + +(defmethod gh-repos-repo-new ((api gh-repos-api) repo-stub + &optional org &rest caps) + (gh-api-authenticated-request + api (gh-object-reader (oref api repo-cls)) "POST" + (if org (format "/orgs/%s/repos" org) + "/user/repos") + (apply 'gh-repos-repo-to-obj repo-stub caps))) + +(defmethod gh-repos-repo-get ((api gh-repos-api) repo-id &optional user) + (gh-api-authenticated-request + api (gh-object-reader (oref api repo-cls)) "GET" + (format "/repos/%s/%s" + (or user (gh-api-get-username api)) + repo-id))) + +(defmethod gh-repos-repo-update ((api gh-repos-api) repo-stub + &optional user &rest caps) + (gh-api-authenticated-request + api (gh-object-reader (oref api repo-cls)) "PATCH" + (format "/repos/%s/%s" + (or user (gh-api-get-username api)) + (oref repo-stub :name)) + (apply 'gh-repos-repo-to-obj repo-stub caps))) + +(defmethod gh-repos-repo-rename ((api gh-repos-api) repo-stub new-name + &optional user) + (let ((new-stub (gh-repos-repo-stub "repo" :name new-name))) + (gh-api-authenticated-request + api (gh-object-reader (oref api repo-cls)) "PATCH" + (format "/repos/%s/%s" + (or user (gh-api-get-username api)) + (oref repo-stub :name)) + (gh-repos-repo-to-obj new-stub)))) + +(defmethod gh-repos-repo-delete ((api gh-repos-api) repo-id + &optional user) + (gh-api-authenticated-request + api (gh-object-reader (oref api repo-cls)) "DELETE" + (format "/repos/%s/%s" + (or user (gh-api-get-username api)) + repo-id))) + +;; TODO gh-repos-repo-move + +(defmethod gh-repos-repo-contributors ((api gh-repos-api) repo) + (gh-api-authenticated-request + api (gh-object-reader (oref api repo-cls)) "GET" + (format "/repos/%s/%s/contributors" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +;;; TODO: generate some useful objects with the return values + +(defmethod gh-repos-repo-languages ((api gh-repos-api) repo) + (gh-api-authenticated-request + api nil "GET" (format "/repos/%s/%s/languages" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +(defmethod gh-repos-repo-teams ((api gh-repos-api) repo) + (gh-api-authenticated-request + api nil "GET" (format "/repos/%s/%s/teams" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +(defmethod gh-repos-repo-tags ((api gh-repos-api) repo) + (gh-api-authenticated-request + api nil "GET" (format "/repos/%s/%s/tags" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +(defmethod gh-repos-repo-branches ((api gh-repos-api) repo) + (gh-api-authenticated-request + api nil "GET" (format "/repos/%s/%s/branches" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +;;; TODO gh-repos-repo-branch-commits +;;; TODO Collaborators sub-API +;;; TODO Comments sub-API +;;; TODO Commits sub-API +;;; TODO Contents sub-API +;;; TODO Downloads sub-API + +;;; Forks sub-API + +(defmethod gh-repos-forks-list ((api gh-repos-api) repo &optional recursive) + (let ((resp (gh-api-authenticated-request + api (gh-object-list-reader (oref api repo-cls)) "GET" + (format "/repos/%s/%s/forks" + (oref (oref repo :owner) :login) + (oref repo :name))))) + (when recursive + (let ((forks (oref resp :data))) + (oset resp :data + (apply 'nconc forks + (mapcar + (lambda (f) + (oref (gh-repos-forks-list api f t) data)) + forks))))) + resp)) + +(defmethod gh-repos-fork ((api gh-repos-api) repo &optional org) + (gh-api-authenticated-request + api (gh-object-reader (oref api repo-cls)) "POST" + (format "/repos/%s/%s/forks" + (oref (oref repo :owner) :login) + (oref repo :name)) + nil (when org `(("org" . ,org))))) + +;;; TODO Keys sub-API +;;; TODO Hooks sub-API +;;; TODO Merging sub-API + +;;; Starring sub-API + +(defmethod gh-repos-stargazers ((api gh-repos-api) repo) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api user-cls)) "GET" + (format "/repos/%s/%s/stargazers" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +(defmethod gh-repos-starred-list ((api gh-repos-api) &optional username) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api repo-cls)) "GET" + (format "/users/%s/starred" (or username (gh-api-get-username api))))) + +(defmethod gh-repos-starred-p ((api gh-repos-api) repo) + (eq (oref (gh-api-authenticated-request + api nil "GET" + (format "/user/starred/%s/%s" + (oref (oref repo :owner) :login) + (oref repo :name))) + :http-status) + 204)) + +(defmethod gh-repos-star ((api gh-repos-api) repo) + (gh-api-authenticated-request + api nil "PUT" + (format "/user/starred/%s/%s" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +(defmethod gh-repos-unstar ((api gh-repos-api) repo) + (gh-api-authenticated-request + api nil "DELETE" + (format "/user/starred/%s/%s" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +;;; TODO Statuses sub-API + +;;; Watching sub-API + +(defmethod gh-repos-watchers ((api gh-repos-api) repo) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api user-cls)) "GET" + (format "/repos/%s/%s/subscribers" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +(defmethod gh-repos-watched-list ((api gh-repos-api) &optional username) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api repo-cls)) "GET" + (format "/users/%s/subscriptions" + (or username (gh-api-get-username api))))) + +(defmethod gh-repos-watched-p ((api gh-repos-api) repo) + (eq (oref (gh-api-authenticated-request + api nil "GET" + (format "/user/subscriptions/%s/%s" + (oref (oref repo :owner) :login) + (oref repo :name))) + :http-status) + 204)) + +(defmethod gh-repos-watch ((api gh-repos-api) repo) + (gh-api-authenticated-request + api nil "PUT" + (format "/user/subscriptions/%s/%s" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +(defmethod gh-repos-unwatch ((api gh-repos-api) repo) + (gh-api-authenticated-request + api nil "DELETE" + (format "/user/subscriptions/%s/%s" + (oref (oref repo :owner) :login) + (oref repo :name)))) + +(provide 'gh-repos) +;;; gh-repos.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/gh-20160222.1811/gh-url.el b/elpa/gh-20160222.1811/gh-url.el new file mode 100644 index 0000000..885aba8 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-url.el @@ -0,0 +1,190 @@ +;;; gh-url.el --- url wrapper for gh.el + +;; Copyright (C) 2012 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'url-http) + +(defclass gh-url-request () + ((method :initarg :method :type string) + (url :initarg :url :type string) + (query :initarg :query :initform nil) + (headers :initarg :headers :initform nil) + (data :initarg :data :initform "" :type string) + (async :initarg :async :initform nil) + (num-retries :initarg :num-retries :initform 0) + (install-callbacks :initarg :install-callbacks :initform nil) + + (default-response-cls :allocation :class :initform gh-url-response))) + +(defclass gh-url-response () + ((data-received :initarg :data-received :initform nil) + (data :initarg :data :initform nil) + (headers :initarg :headers :initform nil) + (http-status :initarg :http-status :initform nil) + (callbacks :initarg :callbacks :initform nil) + (transform :initarg :transform :initform nil) + (-req :initarg :-req :initform nil))) + +(defmethod gh-url-response-set-data ((resp gh-url-response) data) + (let ((transform (oref resp :transform))) + (oset resp :data + (if transform + (funcall transform data) + data)) + (oset resp :data-received t))) + +(defclass gh-url-callback () + nil) + +(defmethod gh-url-callback-run ((cb gh-url-callback) resp) + nil) + +(defmethod gh-url-response-run-callbacks ((resp gh-url-response)) + (let ((copy-list (lambda (list) + (if (consp list) + (let ((res nil)) + (while (consp list) (push (pop list) res)) + (prog1 (nreverse res) (setcdr res list))) + (car list))))) + (let ((data (oref resp :data))) + (dolist (cb (funcall copy-list (oref resp :callbacks))) + (cond ((and (object-p cb) + (object-of-class-p cb 'gh-url-callback)) + (gh-url-callback-run cb resp)) + ((or (functionp cb) (symbolp cb)) + (funcall cb data)) + (t (apply (car cb) data (cdr cb)))) + (object-remove-from-list resp :callbacks cb)))) + resp) + +(defmethod gh-url-add-response-callback ((resp gh-url-response) callback) + (object-add-to-list resp :callbacks callback t) + (if (oref resp :data-received) + (gh-url-response-run-callbacks resp) + resp)) + +;;; code borrowed from nicferrier's web.el +(defun gh-url-parse-headers (data) + (let* ((headers nil) + (header-lines (split-string data "\n")) + (status-line (car header-lines))) + (when (string-match + "HTTP/\\([0-9.]+\\) \\([0-9]\\{3\\}\\)\\( \\(.*\\)\\)*" + status-line) + (push (cons 'status-version (match-string 1 status-line)) headers) + (push (cons 'status-code (match-string 2 status-line)) headers) + (push (cons 'status-string + (or (match-string 4 status-line) "")) + headers)) + (loop for line in (cdr header-lines) + if (string-match + "^\\([A-Za-z0-9.-]+\\):[ ]*\\(.*\\)" + line) + do + (let ((name (match-string 1 line)) + (value (match-string 2 line))) + (push (cons name value) headers))) + headers)) + +(defmethod gh-url-response-finalize ((resp gh-url-response)) + (when (oref resp :data-received) + (gh-url-response-run-callbacks resp))) + +(defmethod gh-url-response-init ((resp gh-url-response) + buffer) + (declare (special url-http-end-of-headers)) + (unwind-protect + (with-current-buffer buffer + (let ((headers (gh-url-parse-headers + (buffer-substring + (point-min) (1+ url-http-end-of-headers))))) + (oset resp :headers headers) + (oset resp :http-status (read (cdr (assoc 'status-code headers))))) + (goto-char (1+ url-http-end-of-headers)) + (let ((raw (buffer-substring (point) (point-max)))) + (gh-url-response-set-data resp raw))) + (kill-buffer buffer)) + (gh-url-response-finalize resp) + resp) + +(defun gh-url-set-response (status req-resp) + (set-buffer-multibyte t) + (destructuring-bind (req resp) req-resp + (condition-case err + (progn + (oset resp :-req req) + (gh-url-response-init resp (current-buffer))) + (error + (let ((num (oref req :num-retries))) + (if (or (null num) (zerop num)) + (signal (car err) (cdr err)) + (oset req :num-retries (1- num)) + (gh-url-run-request req resp))))))) + +(defun gh-url-form-encode (form) + (mapconcat (lambda (x) (format "%s=%s" (car x) (cdr x))) + form "&")) + +(defun gh-url-params-encode (form) + (concat "?" (gh-url-form-encode form))) + +(defmethod gh-url-run-request ((req gh-url-request) &optional resp) + (let ((url-registered-auth-schemes + '(("basic" ignore . 4))) ;; don't let default handlers kick in + (url-privacy-level 'high) + (url-request-method (oref req :method)) + (url-request-data (oref req :data)) + (url-request-extra-headers (oref req :headers)) + (url (concat (oref req :url) + (let ((params (oref req :query))) + (if params + (gh-url-params-encode params) + ""))))) + (if (oref req :async) + (let* ((resp (or resp (make-instance (oref req default-response-cls)))) + (req-resp (list req resp))) + (with-current-buffer + (url-retrieve url 'gh-url-set-response (list req-resp)) + (set (make-local-variable 'url-registered-auth-schemes) + url-registered-auth-schemes))) + (let* ((resp (or resp (make-instance (oref req default-response-cls)))) + (req-resp (list req resp))) + (with-current-buffer (url-retrieve-synchronously url) + (gh-url-set-response nil req-resp))))) + (mapc (lambda (cb) + (gh-url-add-response-callback resp cb)) + (oref req :install-callbacks)) + resp) + +(provide 'gh-url) +;;; gh-url.el ends here diff --git a/elpa/gh-20160222.1811/gh-users.el b/elpa/gh-20160222.1811/gh-users.el new file mode 100644 index 0000000..dbc8956 --- /dev/null +++ b/elpa/gh-20160222.1811/gh-users.el @@ -0,0 +1,120 @@ +;;; gh-users.el --- users module for gh.el + +;; Copyright (C) 2013 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(eval-when-compile + (require 'cl)) + +;;;###autoload +(require 'eieio) + +(require 'gh-api) +(require 'gh-auth) +(require 'gh-common) + +;;;###autoload +(defclass gh-users-api (gh-api-v3) + ((users-cls :allocation :class :initform gh-users-user)) + "Users API") + +;;;###autoload +(defclass gh-users-user (gh-user) + ((gravatar-id :initarg :gravatar-id) + (html-url :initarg :html-url) + (followers-url :initarg :followers-url) + (following-url :initarg :following-url) + (gists-url :initarg :gists-url) + (starred-url :initarg :starred-url) + (subscriptions-url :initarg :subscriptions-url) + (organizations-url :initarg :organizations-url) + (repos-url :initarg :repos-url) + (events-url :initarg :events-url) + (received-events-url :initarg :received-events-url) + (type :initarg :type) + (site-admin :initarg :site-admin) + (name :initarg :name) + (company :initarg :company) + (blog :initarg :blog) + (location :initarg :location) + (email :initarg :email) + (hireable :initarg :hireable) + (bio :initarg :bio) + (public-repos :initarg :public-repos) + (public-gists :initarg :public-gists) + (followers :initarg :followers) + (following :initarg :following) + (created-at :initarg :created-at) + (update-at :initarg :update-at))) + +(defmethod gh-object-read-into ((user gh-users-user) data) + (call-next-method) + (with-slots (gravatar-id html-url followers-url following-url + gists-url starred-url subscriptions-url organizations-url + repos-url events-url received-events-url type site-admin name + company blog location email hireable bio public-repos + public-gists followers following created-at update-at) + user + (setq gravatar-id (gh-read data 'gravatar_id) + html-url (gh-read data 'html_url) + following-url (gh-read data 'following_url) + gists-url (gh-read data 'gists_url) + starred-url (gh-read data 'starred_url) + subscriptions-url (gh-read data 'subscriptions_url) + organizations-url (gh-read data 'organizations_url) + repos-url (gh-read data 'repos_url) + events-url (gh-read data 'events_url) + received-events-url (gh-read data 'received_events_url) + type (gh-read data 'type) + site-admin (gh-read data 'site_admin) + name (gh-read data 'name) + company (gh-read data 'company) + blog (gh-read data 'blog) + location (gh-read data 'location) + email (gh-read data 'email) + hireable (gh-read data 'hireable) + bio (gh-read data 'bio) + public-repos (gh-read data 'public_repos) + public-gists (gh-read data 'public_gists) + followers (gh-read data 'followers) + following (gh-read data 'following) + created-at (gh-read data 'created_at) + update-at (gh-read data 'update_at)))) + +(defmethod gh-users-get ((api gh-users-api) &optional username) + (gh-api-authenticated-request + api (gh-object-reader (oref api users-cls)) "GET" + (if username + (format "/users/%s" username) + "/user"))) + +(defmethod gh-users-list ((api gh-users-api)) + (gh-api-authenticated-request + api (gh-object-list-reader (oref api users-cls)) "GET" + "/users")) + +(provide 'gh-users) +;;; gh-users.el ends here diff --git a/elpa/gh-20160222.1811/gh.el b/elpa/gh-20160222.1811/gh.el new file mode 100644 index 0000000..3ed640e --- /dev/null +++ b/elpa/gh-20160222.1811/gh.el @@ -0,0 +1,39 @@ +;;; gh.el --- Github API client libraries + +;; Copyright (C) 2011 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; + +;;; Code: + +(require 'gh-gist) +(require 'gh-pulls) +(require 'gh-issues) +(require 'gh-users) + +(provide 'gh) +;;; gh.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: diff --git a/elpa/git-commit-20160130.649/git-commit-autoloads.el b/elpa/git-commit-20160130.649/git-commit-autoloads.el new file mode 100644 index 0000000..02b0b8d --- /dev/null +++ b/elpa/git-commit-20160130.649/git-commit-autoloads.el @@ -0,0 +1,35 @@ +;;; git-commit-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "git-commit" "git-commit.el" (22221 60698 575000 +;;;;;; 0)) +;;; Generated autoloads from git-commit.el + +(defvar global-git-commit-mode t "\ +Non-nil if Global-Git-Commit mode is enabled. +See the command `global-git-commit-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-git-commit-mode'.") + +(custom-autoload 'global-git-commit-mode "git-commit" nil) + +(autoload 'global-git-commit-mode "git-commit" "\ +Edit Git commit messages. +This global mode arranges for `git-commit-setup' to be called +when a Git commit message file is opened. That usually happens +when Git uses the Emacsclient as $GIT_EDITOR to have the user +provide such a commit message. + +\(fn &optional ARG)" t nil) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; git-commit-autoloads.el ends here diff --git a/elpa/git-commit-20160130.649/git-commit-pkg.el b/elpa/git-commit-20160130.649/git-commit-pkg.el new file mode 100644 index 0000000..448d55f --- /dev/null +++ b/elpa/git-commit-20160130.649/git-commit-pkg.el @@ -0,0 +1 @@ +(define-package "git-commit" "20160130.649" "Edit Git commit messages" '((emacs "24.4") (dash "20151021.113") (with-editor "20160128.1201")) :url "https://github.com/magit/magit" :keywords '("git" "tools" "vc")) diff --git a/elpa/git-commit-20160130.649/git-commit.el b/elpa/git-commit-20160130.649/git-commit.el new file mode 100644 index 0000000..4a61575 --- /dev/null +++ b/elpa/git-commit-20160130.649/git-commit.el @@ -0,0 +1,676 @@ +;;; git-commit.el --- Edit Git commit messages -*- lexical-binding: t; -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Authors: Jonas Bernoulli +;; Sebastian Wiesner +;; Florian Ragwitz +;; Marius Vollmer +;; Maintainer: Jonas Bernoulli + +;; Package-Requires: ((emacs "24.4") (dash "20151021.113") (with-editor "20160128.1201")) +;; Keywords: git tools vc +;; Package-Version: 20160130.649 +;; Homepage: https://github.com/magit/magit + +;; This file is not part of GNU Emacs. + +;; This file 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. + +;; This file 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 this file. If not, see . + +;;; Commentary: + +;; This package assists the user in writing good Git commit messages. + +;; While Git allows for the message to be provided on the command +;; line, it is preferable to tell Git to create the commit without +;; actually passing it a message. Git then invokes the `$GIT_EDITOR' +;; (or if that is undefined `$EDITOR') asking the user to provide the +;; message by editing the file ".git/COMMIT_EDITMSG" (or another file +;; in that directory, e.g. ".git/MERGE_MSG" for merge commits). + +;; When `global-git-commit-mode' is enabled, which it is by default, +;; then opening such a file causes the features described below, to +;; be enabled in that buffer. Normally this would be done using a +;; major-mode but to allow the use of any major-mode, as the user sees +;; fit, it is done here by running a setup function, which among other +;; things turns on the preferred major-mode, by default `text-mode'. + +;; Git waits for the `$EDITOR' to finish and then either creates the +;; commit using the contents of the file as commit message, or, if the +;; editor process exited with a non-zero exit status, aborts without +;; creating a commit. Unfortunately Emacsclient (which is what Emacs +;; users should be using as `$EDITOR' or at least as `$GIT_EDITOR') +;; does not differentiate between "successfully" editing a file and +;; aborting; not out of the box that is. + +;; By making use of the `with-editor' package this package provides +;; both ways of finish an editing session. In either case the file +;; is saved, but Emacseditor's exit code differs. +;; +;; C-c C-c Finish the editing session successfully by returning +;; with exit code 0. Git then creates the commit using +;; the message it finds in the file. +;; +;; C-c C-k Aborts the edit editing session by returning with exit +;; code 1. Git then aborts the commit. + +;; Aborting the commit does not cause the message to be lost, but +;; relying solely on the file not being tampered with is risky. This +;; package additionally stores all aborted messages for the duration +;; of the current session (i.e. until you close Emacs). To get back +;; an aborted message use M-p and M-n while editing a message. +;; +;; M-p Replace the buffer contents with the previous message +;; from the message ring. Of course only after storing +;; the current content there too. +;; +;; M-n Replace the buffer contents with the next message from +;; the message ring, after storing the current content. + +;; Some support for pseudo headers as used in some projects is +;; provided by these commands: +;; +;; C-c C-s Insert a Signed-off-by header. +;; C-C C-a Insert a Acked-by header. +;; C-c C-t Insert a Tested-by header. +;; C-c C-r Insert a Reviewed-by header. +;; C-c C-o Insert a Cc header. +;; C-c C-p Insert a Reported-by header. +;; C-c M-s Insert a Suggested-by header. + +;; When Git requests a commit message from the user, it does so by +;; having her edit a file which initially contains some comments, +;; instructing her what to do, and providing useful information, such +;; as which files were modified. These comments, even when left +;; intact by the user, do not become part of the commit message. This +;; package ensures these comments are propertizes as such and further +;; prettifies them by using different faces for various parts, such as +;; files. + +;; Finally this package highlights style errors, like lines that are +;; too long, or when the second line is not empty. It may even nag you +;; when you attempt to finish the commit without having fixed these +;; issues. Some people like that nagging, I don't, so you'll have to +;; enable it. Which brings me to the last point. Like any +;; respectable Emacs package, this one too is highly customizable: +;; +;; M-x customize-group RET git-commit RET + +;;; Code: +;;;; Dependencies + +(require 'dash) +(require 'log-edit) +(require 'ring) +(require 'server) +(require 'with-editor) + +(eval-when-compile (require 'recentf)) + +;;;; Declarations + +(defvar flyspell-generic-check-word-predicate) + +(declare-function magit-expand-git-file-name 'magit-git) + +;;; Options +;;;; Variables + +(defgroup git-commit nil + "Edit Git commit messages." + :prefix "git-commit-" + :group 'tools) + +;;;###autoload +(define-minor-mode global-git-commit-mode + "Edit Git commit messages. +This global mode arranges for `git-commit-setup' to be called +when a Git commit message file is opened. That usually happens +when Git uses the Emacsclient as $GIT_EDITOR to have the user +provide such a commit message." + :group 'git-commit + :type 'boolean + :global t + :init-value t + :initialize (lambda (symbol exp) + (custom-initialize-default symbol exp) + (when global-git-commit-mode + (add-hook 'find-file-hook 'git-commit-setup-check-buffer))) + (if global-git-commit-mode + (add-hook 'find-file-hook 'git-commit-setup-check-buffer) + (remove-hook 'find-file-hook 'git-commit-setup-check-buffer))) + +(defcustom git-commit-major-mode 'text-mode + "Major mode used to edit Git commit messages. +The major mode configured here is turned on by the minor mode +`git-commit-mode'." + :group 'git-commit + :type '(choice (function-item text-mode) + (const :tag "No major mode"))) + +(unless (find-lisp-object-file-name 'git-commit-setup-hook 'defvar) + (add-hook 'git-commit-setup-hook 'with-editor-usage-message) + (add-hook 'git-commit-setup-hook 'git-commit-propertize-diff) + (add-hook 'git-commit-setup-hook 'git-commit-turn-on-auto-fill) + (add-hook 'git-commit-setup-hook 'git-commit-setup-changelog-support) + (add-hook 'git-commit-setup-hook 'git-commit-save-message)) +(defcustom git-commit-setup-hook + '(git-commit-save-message + git-commit-setup-changelog-support + git-commit-turn-on-auto-fill + git-commit-propertize-diff + with-editor-usage-message) + "Hook run at the end of `git-commit-setup'." + :group 'git-commit + :type 'hook + :options '( + git-commit-save-message + git-commit-setup-changelog-support + git-commit-turn-on-auto-fill + git-commit-turn-on-flyspell + git-commit-propertize-diff + with-editor-usage-message)) + +(defcustom git-commit-finish-query-functions + '(git-commit-check-style-conventions) + "List of functions called to query before performing commit. + +The commit message buffer is current while the functions are +called. If any of them returns nil, then the commit is not +performed and the buffer is not killed. The user should then +fix the issue and try again. + +The functions are called with one argument. If it is non-nil +then that indicates that the user used a prefix argument to +force finishing the session despite issues. Functions should +usually honor this wish and return non-nil." + :options '(git-commit-check-style-conventions) + :type 'hook + :group 'git-commit) + +(defcustom git-commit-summary-max-length 50 + "Fontify characters beyond this column in summary lines as errors." + :group 'git-commit + :safe 'numberp + :type 'number) + +(defcustom git-commit-fill-column 72 + "Automatically wrap commit message lines beyond this column." + :group 'git-commit + :safe 'numberp + :type 'number) + +(defcustom git-commit-known-pseudo-headers + '("Signed-off-by" "Acked-by" "Cc" + "Suggested-by" "Reported-by" "Tested-by" "Reviewed-by") + "A list of Git pseudo headers to be highlighted." + :group 'git-commit + :safe (lambda (val) (and (listp val) (-all-p 'stringp val))) + :type '(repeat string)) + +;;;; Faces + +(defgroup git-commit-faces nil + "Faces for highlighting Git commit messages." + :prefix "git-commit-" + :group 'git-commit + :group 'faces) + +(defface git-commit-summary + '((t :inherit font-lock-type-face)) + "Face used for the summary in commit messages." + :group 'git-commit-faces) + +(defface git-commit-overlong-summary + '((t :inherit font-lock-warning-face)) + "Face used for the tail of overlong commit message summaries." + :group 'git-commit-faces) + +(defface git-commit-nonempty-second-line + '((t :inherit font-lock-warning-face)) + "Face used for non-whitespace on the second line of commit messages." + :group 'git-commit-faces) + +(defface git-commit-note + '((t :inherit font-lock-string-face)) + "Face used for notes in commit messages." + :group 'git-commit-faces) + +(defface git-commit-pseudo-header + '((t :inherit font-lock-string-face)) + "Font used for pseudo headers in commit messages." + :group 'git-commit-faces) + +(defface git-commit-known-pseudo-header + '((t :inherit font-lock-keyword-face)) + "Face used for the keywords of known pseudo headers in commit messages." + :group 'git-commit-faces) + +(defface git-commit-comment-branch + '((t :inherit font-lock-variable-name-face)) + "Face used for branch names in commit message comments." + :group 'git-commit-faces) + +(defface git-commit-comment-detached + '((t :inherit git-commit-comment-branch)) + "Face used for detached `HEAD' in commit message comments." + :group 'git-commit-faces) + +(defface git-commit-comment-heading + '((t :inherit git-commit-known-pseudo-header)) + "Face used for headings in commit message comments." + :group 'git-commit-faces) + +(defface git-commit-comment-file + '((t :inherit git-commit-pseudo-header)) + "Face used for file names in commit message comments." + :group 'git-commit-faces) + +(defface git-commit-comment-action + '((t :inherit git-commit-comment-branch)) + "Face used for actions in commit message comments." + :group 'git-commit-faces) + +;;; Keymap + +(defvar git-commit-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-s") 'git-commit-signoff) + (define-key map (kbd "C-c C-a") 'git-commit-ack) + (define-key map (kbd "C-c C-t") 'git-commit-test) + (define-key map (kbd "C-c C-r") 'git-commit-review) + (define-key map (kbd "C-c C-o") 'git-commit-cc) + (define-key map (kbd "C-c C-p") 'git-commit-reported) + (define-key map (kbd "C-c C-i") 'git-commit-suggested) + (define-key map (kbd "C-c M-s") 'git-commit-save-message) + (define-key map (kbd "M-p") 'git-commit-prev-message) + (define-key map (kbd "M-n") 'git-commit-next-message) + ;; Old bindings to avoid confusion + (define-key map (kbd "C-c C-x s") 'git-commit-signoff) + (define-key map (kbd "C-c C-x a") 'git-commit-ack) + (define-key map (kbd "C-c C-x t") 'git-commit-test) + (define-key map (kbd "C-c C-x r") 'git-commit-review) + (define-key map (kbd "C-c C-x o") 'git-commit-cc) + (define-key map (kbd "C-c C-x p") 'git-commit-reported) + map) + "Key map used by `git-commit-mode'.") + +;;; Menu + +(require 'easymenu) +(easy-menu-define git-commit-mode-menu git-commit-mode-map + "Git Commit Mode Menu" + '("Commit" + ["Previous" git-commit-prev-message t] + ["Next" git-commit-next-message t] + "-" + ["Ack" git-commit-ack :active t + :help "Insert an 'Acked-by' header"] + ["Sign-Off" git-commit-signoff :active t + :help "Insert a 'Signed-off-by' header"] + ["Tested-by" git-commit-test :active t + :help "Insert a 'Tested-by' header"] + ["Reviewed-by" git-commit-review :active t + :help "Insert a 'Reviewed-by' header"] + ["CC" git-commit-cc t + :help "Insert a 'Cc' header"] + ["Reported" git-commit-reported :active t + :help "Insert a 'Reported-by' header"] + ["Suggested" git-commit-suggested t + :help "Insert a 'Suggested-by' header"] + "-" + ["Save" git-commit-save-message t] + ["Cancel" with-editor-cancel t] + ["Commit" with-editor-finish t])) + +;;; Hooks + +(defconst git-commit-filename-regexp "/\\(\ +\\(\\(COMMIT\\|NOTES\\|PULLREQ\\|TAG\\)_EDIT\\|MERGE_\\|\\)MSG\ +\\|BRANCH_DESCRIPTION\\)\\'") + +(eval-after-load 'recentf + '(add-to-list 'recentf-exclude git-commit-filename-regexp)) + +(defun git-commit-setup-font-lock-in-buffer () + (and buffer-file-name + (string-match-p git-commit-filename-regexp buffer-file-name) + (git-commit-setup-font-lock))) + +(add-hook 'after-change-major-mode-hook 'git-commit-setup-font-lock-in-buffer) + +(defun git-commit-setup-check-buffer () + (and buffer-file-name + (string-match-p git-commit-filename-regexp buffer-file-name) + (git-commit-setup))) + +(defun git-commit-setup () + ;; cygwin git will pass a cygwin path (/cygdrive/c/foo/.git/...), + ;; try to handle this in window-nt Emacs. + (--when-let + (and (eq system-type 'windows-nt) + (not (file-accessible-directory-p default-directory)) + (if (require 'magit-git nil t) + ;; Emacs prepends a "c:". + (magit-expand-git-file-name (substring buffer-file-name 2)) + ;; Fallback if we can't load `magit-git'. + (and (string-match "\\`[a-z]:/\\(cygdrive/\\)?\\([a-z]\\)/\\(.*\\)" + buffer-file-name) + (concat (match-string 2 buffer-file-name) ":/" + (match-string 3 buffer-file-name))))) + (when (file-accessible-directory-p (file-name-directory it)) + (find-alternate-file it))) + (when git-commit-major-mode + (funcall git-commit-major-mode)) + (setq with-editor-show-usage nil) + (with-editor-mode 1) + (add-hook 'with-editor-finish-query-functions + 'git-commit-finish-query-functions nil t) + (add-hook 'with-editor-pre-finish-hook + 'git-commit-save-message nil t) + (add-hook 'with-editor-pre-cancel-hook + 'git-commit-save-message nil t) + (setq with-editor-cancel-message + 'git-commit-cancel-message) + (make-local-variable 'log-edit-comment-ring-index) + (git-commit-mode 1) + (git-commit-setup-font-lock) + (when (boundp 'save-place) + (setq save-place nil)) + (save-excursion + (goto-char (point-min)) + (when (= (line-beginning-position) + (line-end-position)) + (open-line 1))) + (run-hooks 'git-commit-setup-hook) + (set-buffer-modified-p nil)) + +(defun git-commit-setup-font-lock () + (let ((table (make-syntax-table (syntax-table)))) + (when comment-start + (modify-syntax-entry (string-to-char comment-start) "." table)) + (modify-syntax-entry ?# "." table) + (modify-syntax-entry ?\" "." table) + (modify-syntax-entry ?\' "." table) + (modify-syntax-entry ?` "." table) + (set-syntax-table table)) + (setq-local comment-start + (or (ignore-errors + (car (process-lines "git" "config" "core.commentchar"))) + "#")) + (setq-local comment-start-skip (format "^%s+[\s\t]*" comment-start)) + (setq-local comment-end-skip "\n") + (setq-local comment-use-syntax nil) + (setq-local font-lock-multiline t) + (font-lock-add-keywords nil (git-commit-mode-font-lock-keywords) t)) + +(define-minor-mode git-commit-mode + "Auxiliary minor mode used when editing Git commit messages. +This mode is only responsible for setting up some key bindings. +Don't use it directly, instead enable `global-git-commit-mode'." + :lighter "") + +(put 'git-commit-mode 'permanent-local t) + +(defun git-commit-setup-changelog-support () + "Treat ChangeLog entries as paragraphs." + (setq-local paragraph-start (concat paragraph-start "\\|\\*\\|("))) + +(defun git-commit-turn-on-auto-fill () + "Unconditionally turn on Auto Fill mode. +And set `fill-column' to `git-commit-fill-column'." + (setq fill-column git-commit-fill-column) + (turn-on-auto-fill)) + +(defun git-commit-turn-on-flyspell () + "Unconditionally turn on Flyspell mode. +Also prevent comments from being checked and +finally check current non-comment text." + (require 'flyspell) + (turn-on-flyspell) + (setq flyspell-generic-check-word-predicate + 'git-commit-flyspell-verify) + (flyspell-buffer)) + +(defun git-commit-flyspell-verify () + (not (= (char-after (line-beginning-position)) ?#))) + +(defun git-commit-finish-query-functions (force) + (run-hook-with-args-until-failure + 'git-commit-finish-query-functions force)) + +(defun git-commit-check-style-conventions (force) + "Check for violations of certain basic style conventions. +For each violation ask the user if she wants to proceed anyway. +This makes sure the summary line isn't too long and that the +second line is empty." + (or force + (save-excursion + (goto-char (point-min)) + (re-search-forward (git-commit-summary-regexp) nil t) + (if (equal (match-string 1) "") + t ; Just try; we don't know whether --allow-empty-message was used. + (and (or (equal (match-string 2) "") + (y-or-n-p "Summary line is too long. Commit anyway? ")) + (or (equal (match-string 3) "") + (y-or-n-p "Second line is not empty. Commit anyway? "))))))) + +(defun git-commit-cancel-message () + (message + (concat "Commit canceled" + (and (memq 'git-commit-save-message with-editor-pre-cancel-hook) + ". Message saved to `log-edit-comment-ring'")))) + +;;; History + +(defun git-commit-prev-message (arg) + "Cycle backward through message history, after saving current message. +With a numeric prefix ARG, go back ARG comments." + (interactive "*p") + (when (and (git-commit-save-message) (> arg 0)) + (setq log-edit-comment-ring-index + (log-edit-new-comment-index + arg (ring-length log-edit-comment-ring)))) + (save-restriction + (goto-char (point-min)) + (narrow-to-region (point) + (if (re-search-forward (concat "^" comment-start)) + (max 1 (- (point) 2)) + (point-max))) + (log-edit-previous-comment arg))) + +(defun git-commit-next-message (arg) + "Cycle forward through message history, after saving current message. +With a numeric prefix ARG, go forward ARG comments." + (interactive "*p") + (git-commit-prev-message (- arg))) + +(defun git-commit-save-message () + "Save current message to `log-edit-comment-ring'." + (interactive) + (--when-let (git-commit-buffer-message) + (unless (ring-member log-edit-comment-ring it) + (ring-insert log-edit-comment-ring it)))) + +(defun git-commit-buffer-message () + (let ((flush (concat "^" comment-start)) + (str (buffer-substring-no-properties (point-min) (point-max)))) + (with-temp-buffer + (insert str) + (goto-char (point-min)) + (flush-lines flush) + (goto-char (point-max)) + (unless (eq (char-before) ?\n) + (insert ?\n)) + (setq str (buffer-string))) + (unless (string-match "\\`[ \t\n\r]*\\'" str) + (when (string-match "\\`\n\\{2,\\}" str) + (setq str (replace-match "\n" t t str))) + (when (string-match "\n\\{2,\\}\\'" str) + (setq str (replace-match "\n" t t str))) + str))) + +;;; Headers + +(defun git-commit-ack (name mail) + "Insert a header acknowledging that you have looked at the commit." + (interactive (git-commit-self-ident)) + (git-commit-insert-header "Acked-by" name mail)) + +(defun git-commit-review (name mail) + "Insert a header acknowledging that you have reviewed the commit." + (interactive (git-commit-self-ident)) + (git-commit-insert-header "Reviewed-by" name mail)) + +(defun git-commit-signoff (name mail) + "Insert a header to sign off the commit." + (interactive (git-commit-self-ident)) + (git-commit-insert-header "Signed-off-by" name mail)) + +(defun git-commit-test (name mail) + "Insert a header acknowledging that you have tested the commit." + (interactive (git-commit-self-ident)) + (git-commit-insert-header "Tested-by" name mail)) + +(defun git-commit-cc (name mail) + "Insert a header mentioning someone who might be interested." + (interactive (git-commit-read-ident)) + (git-commit-insert-header "Cc" name mail)) + +(defun git-commit-reported (name mail) + "Insert a header mentioning the person who reported the issue." + (interactive (git-commit-read-ident)) + (git-commit-insert-header "Reported-by" name mail)) + +(defun git-commit-suggested (name mail) + "Insert a header mentioning the person who suggested the change." + (interactive (git-commit-read-ident)) + (git-commit-insert-header "Suggested-by" name mail)) + +(defun git-commit-self-ident () + (list (or (getenv "GIT_AUTHOR_NAME") + (getenv "GIT_COMMITTER_NAME") + (ignore-errors (car (process-lines "git" "config" "user.name"))) + user-full-name + (read-string "Name: ")) + (or (getenv "GIT_AUTHOR_EMAIL") + (getenv "GIT_COMMITTER_EMAIL") + (getenv "EMAIL") + (ignore-errors (car (process-lines "git" "config" "user.email"))) + (read-string "Email: ")))) + +(defun git-commit-read-ident () + (list (read-string "Name: ") + (read-string "Email: "))) + +(defun git-commit-insert-header (header name email) + (setq header (format "%s: %s <%s>" header name email)) + (save-excursion + (goto-char (point-max)) + (cond ((re-search-backward "^[-a-zA-Z]+: [^<]+? <[^>]+>" nil t) + (end-of-line) + (insert ?\n header) + (unless (= (char-after) ?\n) + (insert ?\n))) + (t + (while (re-search-backward (concat "^" comment-start) nil t)) + (unless (looking-back "\n\n" nil) + (insert ?\n)) + (insert header ?\n))) + (unless (or (eobp) (= (char-after) ?\n)) + (insert ?\n)))) + +;;; Font-Lock + +(defconst git-commit-comment-headings + '("Changes to be committed:" + "Untracked files:" + "Changed but not updated:" + "Changes not staged for commit:" + "Unmerged paths:")) + +(defun git-commit-summary-regexp () + (concat + ;; Leading empty lines and comments + (format "\\`\\(?:^\\(?:\\s-*\\|%s.*\\)\n\\)*" comment-start) + ;; Summary line + (format "\\(.\\{0,%d\\}\\)\\(.*\\)" git-commit-summary-max-length) + ;; Non-empty non-comment second line + (format "\\(?:\n%s\\|\n\\(.*\\)\\)?" comment-start))) + +(defun git-commit-mode-font-lock-keywords () + `(;; Comments + (,(format "^%s.*" comment-start) + (0 'font-lock-comment-face)) + (,(format "^%s On branch \\(.*\\)" comment-start) + (1 'git-commit-comment-branch t)) + (,(format "^%s Not currently on any branch." comment-start) + (1 'git-commit-comment-detached t)) + (,(format "^%s %s" comment-start + (regexp-opt git-commit-comment-headings t)) + (1 'git-commit-comment-heading t)) + (,(format "^%s\t\\(?:\\([^:\n]+\\):\\s-+\\)?\\(.*\\)" comment-start) + (1 'git-commit-comment-action t t) + (2 'git-commit-comment-file t)) + ;; Pseudo headers + (,(format "^\\(%s:\\)\\( .*\\)" + (regexp-opt git-commit-known-pseudo-headers)) + (1 'git-commit-known-pseudo-header) + (2 'git-commit-pseudo-header)) + ("^[-a-zA-Z]+: [^<]+? <[^>]+>" + (0 'git-commit-pseudo-header)) + ;; Summary + (,(git-commit-summary-regexp) + (1 'git-commit-summary t)) + ;; - Note (overrides summary) + ("\\[.+?\\]" + (0 'git-commit-note t)) + ;; - Non-empty second line (overrides summary and note) + (,(git-commit-summary-regexp) + (2 'git-commit-overlong-summary t t) + (3 'git-commit-nonempty-second-line t t)))) + +(defun git-commit-propertize-diff () + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "^diff --git" nil t) + (let ((buffer (current-buffer))) + (insert + (with-temp-buffer + (insert + (with-current-buffer buffer + (prog1 (buffer-substring-no-properties (point) (point-max)) + (delete-region (point) (point-max))))) + (diff-mode) + (let (font-lock-verbose font-lock-support-mode) + (if (fboundp 'font-lock-flush) + (font-lock-flush) + (with-no-warnings + (font-lock-fontify-buffer)))) + (let (next (pos (point-min))) + (while (setq next (next-single-property-change pos 'face)) + (put-text-property pos next 'font-lock-face + (get-text-property pos 'face)) + (setq pos next))) + (buffer-string))))))) + +;;; git-commit.el ends soon +(provide 'git-commit) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; git-commit.el ends here diff --git a/elpa/git-commit-mode-1.0.0/git-commit-mode-autoloads.el b/elpa/git-commit-mode-1.0.0/git-commit-mode-autoloads.el deleted file mode 100644 index 33b0320..0000000 --- a/elpa/git-commit-mode-1.0.0/git-commit-mode-autoloads.el +++ /dev/null @@ -1,35 +0,0 @@ -;;; git-commit-mode-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) - -;;;### (autoloads nil "git-commit-mode" "git-commit-mode.el" (21831 -;;;;;; 16636 620188 15000)) -;;; Generated autoloads from git-commit-mode.el - -(autoload 'git-commit-mode "git-commit-mode" "\ -Major mode for editing git commit messages. - -This mode helps with editing git commit messages both by -providing commands to do common tasks, and by highlighting the -basic structure of and errors in git commit messages. - -\(fn)" t nil) - -(add-to-list 'auto-mode-alist '("/MERGE_MSG\\'" . git-commit-mode)) - -(add-to-list 'auto-mode-alist '("/\\(?:COMMIT\\|NOTES\\|TAG\\|PULLREQ\\)_EDITMSG\\'" . git-commit-mode)) - -;;;*** - -;;;### (autoloads nil nil ("git-commit-mode-pkg.el") (21831 16636 -;;;;;; 639156 530000)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; git-commit-mode-autoloads.el ends here diff --git a/elpa/git-commit-mode-1.0.0/git-commit-mode-pkg.el b/elpa/git-commit-mode-1.0.0/git-commit-mode-pkg.el deleted file mode 100644 index 4a8e4c6..0000000 --- a/elpa/git-commit-mode-1.0.0/git-commit-mode-pkg.el +++ /dev/null @@ -1 +0,0 @@ -(define-package "git-commit-mode" "1.0.0" "Major mode for editing git commit messages" 'nil) diff --git a/elpa/git-commit-mode-1.0.0/git-commit-mode.el b/elpa/git-commit-mode-1.0.0/git-commit-mode.el deleted file mode 100644 index 1cedd79..0000000 --- a/elpa/git-commit-mode-1.0.0/git-commit-mode.el +++ /dev/null @@ -1,668 +0,0 @@ -;;; git-commit-mode.el --- Major mode for editing git commit messages -*- lexical-binding: t; -*- - -;; Copyright (c) 2010-2012 Florian Ragwitz -;; Copyright (c) 2012-2013 Sebastian Wiesner -;; Copyright (C) 2010-2015 The Magit Project Developers - -;; Authors: Jonas Bernoulli -;; Sebastian Wiesner -;; Florian Ragwitz -;; Maintainer: Jonas Bernoulli -;; Homepage: https://github.com/magit/git-modes -;; Keywords: convenience vc git -;; Package-Version: 1.0.0 - -;; This file is not part of GNU Emacs. - -;; This file 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. - -;; This file 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 this file. If not, see . - -;;; Commentary: - -;; A major mode for editing Git commit messages. - -;;;; Formatting - -;; Highlight the formatting of git commit messages and indicate errors according -;; to the guidelines for commit messages (see -;; http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). -;; -;; Highlight the first line (aka "summary") specially if it exceeds 50 -;; characters (configurable using `git-commit-summary-max-length'). -;; -;; Enable `auto-fill-mode' and set the `fill-column' to 72 according to the -;; aforementioned guidelines (configurable using `git-commit-fill-column'). - -;;;; Headers - -;; Provide commands to insert standard headers into commit messages. -;; -;; - C-c C-s inserts Signed-off-by (`git-commit-signoff'). -;; - C-C C-a inserts Acked-by (`git-commit-ack'). -;; - C-c C-t inserts Tested-by (`git-commit-test'). -;; - C-c C-r inserts Reviewed-by (`git-commit-review'). -;; - C-c C-o inserts Cc (`git-commit-cc'). -;; - C-c C-p inserts Reported-by (`git-commit-reported'). - -;;;; Committing - -;; C-c C-c finishes a commit. -;; -;; Check a buffer for stylistic errors before committing, and ask for -;; confirmation before committing with style errors. - -;;; Code: - -(require 'log-edit) -(require 'ring) -(require 'server) - -;;; Options -;;;; Variables - -(defgroup git-commit nil - "Edit Git commit messages." - :prefix "git-commit-" - :group 'tools) - -(defcustom git-commit-confirm-commit nil - "Whether to ask for confirmation before committing. - -If t, ask for confirmation before creating a commit with style -errors, unless the commit is forced. If nil, never ask for -confirmation before committing." - :group 'git-commit - :type '(choice (const :tag "On style errors" t) - (const :tag "Never" nil))) - -(defcustom git-commit-mode-hook '(turn-on-auto-fill) - "Hook run when entering Git Commit mode." - :options '(turn-on-auto-fill flyspell-mode git-commit-save-message) - :type 'hook - :group 'git-commit) - -(defcustom git-commit-kill-buffer-hook '(git-commit-save-message) - "Hook run when killing a Git Commit mode buffer. -This hook is run by both `git-commit-commit' -and `git-commit-abort'." - :options '(git-commit-save-message) - :type 'hook - :group 'git-commit) - -(defcustom git-commit-summary-max-length 50 - "Fontify characters beyond this column in summary lines as errors." - :group 'git-commit - :type 'number) - -(defcustom git-commit-fill-column 72 - "Automatically wrap commit message lines beyond this column." - :group 'git-commit - :type 'number) - -(defcustom git-commit-known-pseudo-headers - '("Signed-off-by" "Acked-by" "Cc" - "Suggested-by" "Reported-by" "Tested-by" "Reviewed-by") - "A list of git pseudo headers to be highlighted." - :group 'git-commit - :type '(repeat string)) - -;;;; Faces - -(defgroup git-commit-faces nil - "Faces for highlighting Git commit messages." - :prefix "git-commit-" - :group 'git-commit - :group 'faces) - -(defface git-commit-summary-face - '((t :inherit font-lock-type-face)) - "Face used to highlight the summary in git commit messages" - :group 'git-commit-faces) - -(defface git-commit-overlong-summary-face - '((t :inherit font-lock-warning-face)) - "Face used to highlight overlong parts of git commit message summaries" - :group 'git-commit-faces) - -(defface git-commit-nonempty-second-line-face - '((t :inherit font-lock-warning-face)) - "Face used to highlight text on the second line of git commit messages" - :group 'git-commit-faces) - -(defface git-commit-note-face - '((t :inherit font-lock-string-face)) - "Face used to highlight notes in git commit messages" - :group 'git-commit-faces) - -(defface git-commit-pseudo-header-face - '((t :inherit font-lock-string-face)) - "Font used to hightlight pseudo headers in git commit messages" - :group 'git-commit-faces) - -(defface git-commit-known-pseudo-header-face - '((t :inherit font-lock-keyword-face)) - "Face used to hightlight common pseudo headers in git commit messages" - :group 'git-commit-faces) - -(defface git-commit-branch-face - '((t :inherit font-lock-variable-name-face)) - "Face used to highlight the branch name in comments in git commit messages" - :group 'git-commit-faces) - -(defface git-commit-no-branch-face - '((t :inherit git-commit-branch-face)) - "Face used when a commit is going to be made outside of any branches" - :group 'git-commit-faces) - -(defface git-commit-comment-heading-face - '((t :inherit git-commit-known-pseudo-header-face)) - "Face used to highlight section headings in the default -comments in git commit messages" - :group 'git-commit-faces) - -(defface git-commit-comment-file-face - '((t :inherit git-commit-pseudo-header-face)) - "Face used to highlight file names in the default comments in -git commit messages" - :group 'git-commit-faces) - -(defface git-commit-comment-action-face - '((t :inherit git-commit-branch-face)) - "Face used to highlight what has happened to files in the -default comments in git commit messages" - :group 'git-commit-faces) - -;;; Keymap - -(defvar git-commit-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-c") 'git-commit-commit) - (define-key map (kbd "C-c C-k") 'git-commit-abort) - (define-key map (kbd "C-c C-s") 'git-commit-signoff) - (define-key map (kbd "C-c C-a") 'git-commit-ack) - (define-key map (kbd "C-c C-t") 'git-commit-test) - (define-key map (kbd "C-c C-r") 'git-commit-review) - (define-key map (kbd "C-c C-o") 'git-commit-cc) - (define-key map (kbd "C-c C-p") 'git-commit-reported) - (define-key map (kbd "C-c C-i") 'git-commit-suggested) - (define-key map (kbd "C-c M-s") 'git-commit-save-message) - (define-key map (kbd "M-p") 'git-commit-prev-message) - (define-key map (kbd "M-n") 'git-commit-next-message) - (define-key map [remap server-edit] 'git-commit-commit) - (define-key map [remap kill-buffer] 'git-commit-abort) - (define-key map [remap ido-kill-buffer] 'git-commit-abort) - (define-key map [remap iswitchb-kill-buffer] 'git-commit-abort) - ;; Old bindings to avoid confusion - (define-key map (kbd "C-c C-x s") 'git-commit-signoff) - (define-key map (kbd "C-c C-x a") 'git-commit-ack) - (define-key map (kbd "C-c C-x t") 'git-commit-test) - (define-key map (kbd "C-c C-x r") 'git-commit-review) - (define-key map (kbd "C-c C-x o") 'git-commit-cc) - (define-key map (kbd "C-c C-x p") 'git-commit-reported) - map) - "Key map used by `git-commit-mode'.") - -;;; Menu - -(require 'easymenu) -(easy-menu-define git-commit-mode-menu git-commit-mode-map - "Git Commit Mode Menu" - '("Commit" - ["Previous" git-commit-prev-message t] - ["Next" git-commit-next-message t] - "-" - ["Ack" git-commit-ack :active t - :help "Insert an 'Acked-by' header"] - ["Sign-Off" git-commit-signoff :active t - :help "Insert a 'Signed-off-by' header"] - ["Tested-by" git-commit-test :active t - :help "Insert a 'Tested-by' header"] - ["Reviewed-by" git-commit-review :active t - :help "Insert a 'Reviewed-by' header"] - ["CC" git-commit-cc t - :help "Insert a 'Cc' header"] - ["Reported" git-commit-reported :active t - :help "Insert a 'Reported-by' header"] - ["Suggested" git-commit-suggested t - :help "Insert a 'Suggested-by' header"] - "-" - ["Save" git-commit-save-message t] - ["Cancel" git-commit-abort t] - ["Commit" git-commit-commit t])) - -;;; Committing - -(defvar git-commit-commit-hook nil - "Hook run by `git-commit-commit' unless clients exist. -Only use this if you know what you are doing.") - -(defvar git-commit-previous-winconf nil) - -(defmacro git-commit-restore-previous-winconf (&rest body) - "Run BODY and then restore `git-commit-previous-winconf'. -When `git-commit-previous-winconf' is nil or was created from -another frame do nothing." - (declare (indent 0)) - (let ((winconf (make-symbol "winconf")) - (frame (make-symbol "frame"))) - `(let ((,winconf git-commit-previous-winconf) - (,frame (selected-frame))) - ,@body - (when (and ,winconf - (equal ,frame (window-configuration-frame ,winconf))) - (set-window-configuration ,winconf) - (setq git-commit-previous-winconf nil))))) - -(defun git-commit-commit (&optional force) - "Finish editing the commit message and commit. - -Check for stylistic errors in the current commit, and ask the -user for confirmation depending on `git-commit-confirm-commit'. -If FORCE is non-nil or if a raw prefix arg is given, commit -immediately without asking. - -Return t, if the commit was successful, or nil otherwise." - (interactive "P") - (if (and git-commit-confirm-commit - (git-commit-has-style-errors-p) - (not force) - (not (y-or-n-p "Commit despite stylistic errors?"))) - (message "Commit canceled due to stylistic errors.") - (save-buffer) - (run-hooks 'git-commit-kill-buffer-hook) - (remove-hook 'kill-buffer-query-functions - 'git-commit-kill-buffer-noop t) - (git-commit-restore-previous-winconf - (if (git-commit-buffer-clients) - (server-edit) - (run-hook-with-args 'git-commit-commit-hook) - (kill-buffer))))) - -(defun git-commit-abort () - "Abort the commit. -The commit message is saved to the kill ring." - (interactive) - (when (< emacs-major-version 24) - ;; Emacsclient doesn't exit with non-zero when -error is used. - ;; Instead cause Git to error out by feeding it an empty file. - (erase-buffer)) - (save-buffer) - (run-hooks 'git-commit-kill-buffer-hook) - (remove-hook 'kill-buffer-hook 'server-kill-buffer t) - (remove-hook 'kill-buffer-query-functions 'git-commit-kill-buffer-noop t) - (git-commit-restore-previous-winconf - (let ((buffer (current-buffer)) - (clients (git-commit-buffer-clients))) - (if clients - (progn - (dolist (client clients) - (ignore-errors - (server-send-string client "-error Commit aborted by user")) - (delete-process client)) - (when (buffer-live-p buffer) - (kill-buffer buffer))) - (kill-buffer)))) - (accept-process-output nil 0.1) - (message (concat "Commit aborted." - (when (memq 'git-commit-save-message - git-commit-kill-buffer-hook) - " Message saved to `log-edit-comment-ring'.")))) - -(defun git-commit-buffer-clients () - (and (fboundp 'server-edit) - (boundp 'server-buffer-clients) - server-buffer-clients)) - -;;; History - -(defun git-commit-save-message () - "Save current message to `log-edit-comment-ring'." - (interactive) - (let ((message (buffer-substring - (point-min) - (git-commit-find-pseudo-header-position)))) - (when (and (string-match "^\\s-*\\sw" message) - (or (ring-empty-p log-edit-comment-ring) - (not (ring-member log-edit-comment-ring message)))) - ;; if index is nil, we end up cycling back to message we just saved! - (unless log-edit-comment-ring-index - (setq log-edit-comment-ring-index 0)) - (ring-insert log-edit-comment-ring message)))) - -(defun git-commit-prev-message (arg) - "Cycle backward through message history, after saving current message. -With a numeric prefix ARG, go back ARG comments." - (interactive "*p") - (when (and (git-commit-save-message) (> arg 0)) - (setq log-edit-comment-ring-index - (log-edit-new-comment-index - arg (ring-length log-edit-comment-ring)))) - (save-restriction - (narrow-to-region (point-min) (git-commit-find-pseudo-header-position)) - (log-edit-previous-comment arg))) - -(defun git-commit-next-message (arg) - "Cycle forward through message history, after saving current message. -With a numeric prefix ARG, go forward ARG comments." - (interactive "*p") - (git-commit-prev-message (- arg))) - -;;; Headers - -(defun git-commit-find-pseudo-header-position () - "Find the position at which commit pseudo headers should be inserted. - -Those headers usually live at the end of a commit message, but -before any trailing comments git or the user might have -inserted." - (save-excursion - (goto-char (point-max)) - (if (re-search-backward "^[^#\n]" nil t) - ;; we found last non-empty non-comment line, headers go after - (forward-line 1) - ;; there's only blanks & comments, headers go before comments - (goto-char (point-min)) - (and (re-search-forward "^#" nil t) (forward-line 0))) - (skip-chars-forward "\n") - (point))) - -(defun git-commit-determine-pre-for-pseudo-header () - "Find the characters to insert before the pseudo header. -Returns either zero, one or two newlines after computation. - -`point' either points to an empty line (with a non-empty previous -line) or the end of a non-empty line." - (let ((pre "") - (prev-line nil)) - (if (not (eq (point) (point-at-bol))) - (progn - (setq pre (concat pre "\n")) - (setq prev-line (thing-at-point 'line))) - ;; else: (point) is at an empty line - (when (not (eq (point) (point-min))) - (setq prev-line - (save-excursion - (forward-line -1) - (thing-at-point 'line))))) - - ;; we have prev-line now; if it doesn't match any known pseudo - ;; header, add a newline - (when prev-line - (if (not (delq nil (mapcar (lambda (pseudo-header) - (string-match pseudo-header prev-line)) - git-commit-known-pseudo-headers))) - (setq pre (concat pre "\n")))) - pre)) - -(defun git-commit-insert-header (type name email) - "Insert a header into the commit message. -The inserted header has the format 'TYPE: NAME '. - -The header is inserted at the position returned by -`git-commit-find-pseudo-header-position'. When this position -isn't after an existing header or a newline, an extra newline is -inserted before the header." - (let ((header-at (git-commit-find-pseudo-header-position))) - (save-excursion - (goto-char header-at) - (let ((pre (git-commit-determine-pre-for-pseudo-header))) - (insert (format "%s%s: %s <%s>\n" pre type name email)))))) - -(defun git-commit-insert-header-as-self (type) - "Insert a header with the name and email of the current user. -The inserted header has the format 'TYPE: NAME '. -Also see `git-commit-insert-header'." - (git-commit-insert-header - type - (or (getenv "GIT_AUTHOR_NAME") - (getenv "GIT_COMMITTER_NAME") - (ignore-errors (car (process-lines "git" "config" "user.name"))) - user-full-name) - (or (getenv "GIT_AUTHOR_EMAIL") - (getenv "GIT_COMMITTER_EMAIL") - (getenv "EMAIL") - (ignore-errors (car (process-lines "git" "config" "user.email"))) - user-mail-address))) - -(defmacro git-define-git-commit-self (action header) - "Create function git-commit-ACTION. -ACTION will be part of the function name. -HEADER is the actual header to be inserted into the comment." - (let ((func-name (intern (concat "git-commit-" action)))) - `(defun ,func-name () - ,(format "Insert a '%s' header at the end of the commit message. - -The author name and email address used for the header are -retrieved automatically with the same mechanism git uses." - header) - (interactive) - (git-commit-insert-header-as-self ,header)))) - -(git-define-git-commit-self "ack" "Acked-by") -(git-define-git-commit-self "review" "Reviewed-by") -(git-define-git-commit-self "signoff" "Signed-off-by") -(git-define-git-commit-self "test" "Tested-by") - -(defmacro git-define-git-commit (action header) - "Create interactive function git-commit-ACTION. -ACTION will be part of the function name. -HEADER is the actual header to be inserted into the comment." - (let ((func-name (intern (concat "git-commit-" action)))) - `(defun ,func-name (name email) - ,(format "Insert a '%s' header at the end of the commit message. -The value of the header is determined by NAME and EMAIL. - -When called interactively, both NAME and EMAIL are read from the -minibuffer." - header) - (interactive - (list (read-string "Name: ") - (read-string "Email: "))) - (git-commit-insert-header ,header name email)))) - -(git-define-git-commit "cc" "Cc") -(git-define-git-commit "reported" "Reported-by") -(git-define-git-commit "suggested" "Suggested-by") - -(defconst git-commit-comment-headings-alist - '(("Not currently on any branch." . git-commit-no-branch-face) - ("Changes to be committed:" . git-commit-comment-heading-face) - ("Untracked files:" . git-commit-comment-heading-face) - ("Changed but not updated:" . git-commit-comment-heading-face) - ("Changes not staged for commit:" . git-commit-comment-heading-face) - ("Unmerged paths:" . git-commit-comment-heading-face)) - "Headings in message comments. - -The `car' of each cell is the heading text, the `cdr' the face to -use for fontification.") - -(defun git-commit-summary-regexp () - (concat - ;; Skip empty lines or comments before the summary - "\\`\\(?:^\\(?:\\s-*\\|\\s<.*\\)\n\\)*" - ;; The summary line - (format "\\(.\\{0,%d\\}\\)\\(.*\\)" git-commit-summary-max-length) - ;; Non-empty non-comment second line - ;; - ;; For instant highlighting of non-empty second lines in font-lock, - ;; the last capturing group must capture the empty string ("") in - ;; "summary line\n". - ;; That's why the simpler regex "\\(?:\n\\([^\n#].*\\)\\)?", - ;; which captures 'nil', can't be used. - "\\(?:\n\\#\\|\n\\(.*\\)\\)?")) - -(defun git-commit-has-style-errors-p () - "Check whether the current buffer has style errors. - -Return t, if the current buffer has style errors, or nil -otherwise." - (save-excursion - (goto-char (point-min)) - (when (re-search-forward (git-commit-summary-regexp) nil t) - (or (string-match-p ".+" (or (match-string 2) "")) - (string-match-p "^.+$" (or (match-string 3) "")))))) - -;;; Font-Lock - -(defun git-commit-mode-summary-font-lock-keywords (&optional errors) - "Create font lock keywords to fontify the Git summary. - -If ERRORS is non-nil create keywords that highlight errors in the -summary line, not the summary line itself." - (if errors - `(,(git-commit-summary-regexp) - (2 'git-commit-overlong-summary-face t t) - (3 'git-commit-nonempty-second-line-face t t)) - `(,(git-commit-summary-regexp) - (1 'git-commit-summary-face t)))) - -(defun git-commit-mode-heading-keywords () - "Create font lock keywords to fontify comment headings. - -Known comment headings are provided by `git-commit-comment-headings'." - (mapcar (lambda (cell) `(,(format "^\\s<\\s-+\\(%s\\)$" - (regexp-quote (car cell))) - (1 ',(cdr cell) t))) - git-commit-comment-headings-alist)) - -(defun git-commit-mode-font-lock-keywords () - (append - `(("^\\s<.*$" . 'font-lock-comment-face) - ("^\\s<\\s-On branch \\(.*\\)$" (1 'git-commit-branch-face t)) - ("^\\s<\t\\(?:\\([^:\n]+\\):\\s-+\\)?\\(.*\\)$" - (1 'git-commit-comment-action-face t t) - (2 'git-commit-comment-file-face t)) - (,(concat "^\\(" - (regexp-opt git-commit-known-pseudo-headers) - ":\\)\\(\s.*\\)$") - (1 'git-commit-known-pseudo-header-face) - (2 'git-commit-pseudo-header-face)) - ("^\\<\\S-+:\\s-.*$" . 'git-commit-pseudo-header-face) - (eval . (git-commit-mode-summary-font-lock-keywords)) - ("\\[[^\n]+?\\]" (0 'git-commit-note-face t)) ; Notes override summary line - ;; Warnings from overlong lines and nonempty second line override - ;; everything - (eval . (git-commit-mode-summary-font-lock-keywords t))) - (git-commit-mode-heading-keywords))) - -(defun git-commit-font-lock-diff () - "Add font lock on diff." - (save-excursion - (goto-char (point-min)) - (when (re-search-forward "^diff --git" nil t) - (let ((beg (match-beginning 0))) - (let* ((buffer (current-buffer)) - (font-lock-verbose nil) - (font-lock-support-mode nil) - (text (with-temp-buffer - (insert - (with-current-buffer buffer - (buffer-substring-no-properties beg (point-max)))) - (diff-mode) - (font-lock-fontify-buffer) - (let ((pos (point-min)) - next) - (while (setq next (next-single-property-change pos 'face)) - (put-text-property pos next 'font-lock-face - (get-text-property pos 'face)) - (setq pos next))) - (buffer-string)))) - (delete-region beg (point-max)) - (insert text)))))) - -;;; Mode - -(defvar git-commit-mode-syntax-table - (let ((table (make-syntax-table text-mode-syntax-table))) - (modify-syntax-entry ?# "<" table) - (modify-syntax-entry ?\n ">" table) - (modify-syntax-entry ?\r ">" table) - table) - "Syntax table used by `git-commit-mode'.") - -;;;###autoload -(define-derived-mode git-commit-mode text-mode "Git Commit" - "Major mode for editing git commit messages. - -This mode helps with editing git commit messages both by -providing commands to do common tasks, and by highlighting the -basic structure of and errors in git commit messages." - ;; Font locking - (setq font-lock-defaults (list (git-commit-mode-font-lock-keywords) t)) - (set (make-local-variable 'font-lock-multiline) t) - (git-commit-font-lock-diff) - ;; Filling according to the guidelines - (setq fill-column git-commit-fill-column) - ;; Recognize changelog-style paragraphs - (set (make-local-variable 'paragraph-start) - (concat paragraph-start "\\|*\\|(")) - ;; Treat lines starting with a hash/pound as comments - (set (make-local-variable 'comment-start) "#") - (set (make-local-variable 'comment-start-skip) - (concat "^" (regexp-quote comment-start) "+" - "\\s-*")) - (set (make-local-variable 'comment-use-syntax) nil) - ;; Do not remember point location in commit messages - (when (boundp 'save-place) - (setq save-place nil)) - ;; If the commit summary is empty, insert a newline after point - (when (string= "" (buffer-substring-no-properties - (line-beginning-position) - (line-end-position))) - (open-line 1)) - ;; That's what happens when every little detail is commented - (make-local-variable 'log-edit-comment-ring-index) - ;; Make sure `git-commit-abort' cannot be by-passed - (add-hook 'kill-buffer-query-functions - 'git-commit-kill-buffer-noop nil t) - ;; Make the wrong usage info from `server-execute' go way - (run-with-timer 0.01 nil (lambda (m) (message "%s" m)) - (substitute-command-keys - (concat "Type \\[git-commit-commit] " - (let ((n (buffer-file-name))) - (cond ((equal n "TAG_EDITMSG") "to tag") - ((or (equal n "NOTES_EDITMSG") - (equal n "PULLREQ_EDITMSG")) - "when done") - (t "to commit"))) - " (\\[git-commit-abort] to abort).")))) - -(defun git-commit-kill-buffer-noop () - (message - (substitute-command-keys - "Don't kill this buffer. Instead abort using \\[git-commit-abort].")) - nil) - -(defun git-commit-mode-flyspell-verify () - (not (nth 4 (syntax-ppss)))) ; not inside a comment - -(eval-after-load 'flyspell - '(put 'git-commit-mode 'flyspell-mode-predicate - 'git-commit-mode-flyspell-verify)) - -;;;###autoload -(add-to-list 'auto-mode-alist '("/MERGE_MSG\\'" . git-commit-mode)) -;;;###autoload -(add-to-list 'auto-mode-alist - '("/\\(?:COMMIT\\|NOTES\\|TAG\\|PULLREQ\\)_EDITMSG\\'" - . git-commit-mode)) - -(defun git-commit-auto-mode-enable () - (message "git-commit-auto-mode-enable is obsolete and doesn't do anything")) -(make-obsolete 'git-commit-auto-mode-enable "This mode is a noop now" "") - -(provide 'git-commit-mode) -;; Local Variables: -;; indent-tabs-mode: nil -;; End: -;;; git-commit-mode.el ends here diff --git a/elpa/git-rebase-mode-1.0.0/git-rebase-mode-autoloads.el b/elpa/git-rebase-mode-1.0.0/git-rebase-mode-autoloads.el deleted file mode 100644 index cd6b8f3..0000000 --- a/elpa/git-rebase-mode-1.0.0/git-rebase-mode-autoloads.el +++ /dev/null @@ -1,29 +0,0 @@ -;;; git-rebase-mode-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) - -;;;### (autoloads nil "git-rebase-mode" "git-rebase-mode.el" (21831 -;;;;;; 16621 351189 81000)) -;;; Generated autoloads from git-rebase-mode.el - -(autoload 'git-rebase-mode "git-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\\'" . git-rebase-mode)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; git-rebase-mode-autoloads.el ends here diff --git a/elpa/git-rebase-mode-1.0.0/git-rebase-mode-pkg.el b/elpa/git-rebase-mode-1.0.0/git-rebase-mode-pkg.el deleted file mode 100644 index 52e517a..0000000 --- a/elpa/git-rebase-mode-1.0.0/git-rebase-mode-pkg.el +++ /dev/null @@ -1 +0,0 @@ -(define-package "git-rebase-mode" "1.0.0" "Major mode for editing git rebase files" 'nil) diff --git a/elpa/git-rebase-mode-1.0.0/git-rebase-mode.el b/elpa/git-rebase-mode-1.0.0/git-rebase-mode.el deleted file mode 100644 index 75ca2a4..0000000 --- a/elpa/git-rebase-mode-1.0.0/git-rebase-mode.el +++ /dev/null @@ -1,393 +0,0 @@ -;;; git-rebase-mode.el --- Major mode for editing git rebase files - -;; Copyright (C) 2010-2015 The Magit Project Developers - -;; Author: Phil Jackson -;; Maintainer: Jonas Bernoulli -;; Homepage: https://github.com/magit/git-modes -;; Keywords: convenience vc git -;; Package-Version: 1.0.0 - -;; This file is not part of GNU Emacs. - -;; This file 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. - -;; This file 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 this file. If not, see . - -;;; 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 'easymenu) -(require 'server) -(require 'thingatpt) - -;;; Options -;;;; Variables - -(defgroup git-rebase nil - "Edit Git rebase sequences." - :group 'tools) - -(defcustom git-rebase-auto-advance nil - "If non-nil, moves point forward a line after running an action." - :group 'git-rebase - :type 'boolean) - -(defcustom git-rebase-remove-instructions nil - "Whether to remove the instructions from the rebase buffer. -Because you have seen them before and can still remember." - :group 'git-rebase - :type 'boolean) - -;;;; Faces - -(defgroup git-rebase-faces nil - "Faces used by Git-Rebase mode." - :group 'faces - :group 'git-rebase) - -(defface git-rebase-hash - '((((class color) (background light)) - :foreground "firebrick") - (((class color) (background dark)) - :foreground "tomato")) - "Face for commit hashes." - :group 'git-rebase-faces) - -(defface git-rebase-description nil - "Face for commit descriptions." - :group 'git-rebase-faces) - -(defface git-rebase-killed-action - '((((class color)) - :inherit font-lock-comment-face - :strike-through t)) - "Face for commented action and exec lines." - :group 'git-rebase-faces) - -(define-obsolete-face-alias 'git-rebase-description-face - 'git-rebase-description "1.0.0") -(define-obsolete-face-alias 'git-rebase-killed-action-face - 'git-rebase-killed-action "1.0.0") - -;;; Regexps - -(defconst git-rebase-action-line-re - (concat "^#?" - "\\([efprs]\\|pick\\|reword\\|edit\\|squash\\|fixup\\) " - "\\([a-z0-9]\\{4,40\\}\\) " - "\\(.*\\)") - "Regexp matching action lines in rebase buffers.") - -(defconst git-rebase-exec-line-re - "^#?\\(x\\|exec\\)[[:space:]]\\(.*\\)" - "Regexp matching exec lines in rebase buffer.") - -(defconst git-rebase-dead-line-re - (format "^#\\(?:%s\\|%s\\)" - (substring git-rebase-action-line-re 1) - (substring git-rebase-exec-line-re 1)) - "Regexp matching commented action and exex lines in rebase buffers.") - -;;; Keymaps - -(defvar git-rebase-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map special-mode-map) - (define-key map (kbd "q") 'git-rebase-server-edit) - (define-key map (kbd "C-c C-c") 'git-rebase-server-edit) - (define-key map (kbd "a") 'git-rebase-abort) - (define-key map (kbd "C-c C-k") 'git-rebase-abort) - (define-key map [remap undo] 'git-rebase-undo) - (define-key map (kbd "RET") 'git-rebase-show-commit) - (define-key map (kbd "x") 'git-rebase-exec) - (define-key map (kbd "c") 'git-rebase-pick) - (define-key map (kbd "r") 'git-rebase-reword) - (define-key map (kbd "e") 'git-rebase-edit) - (define-key map (kbd "s") 'git-rebase-squash) - (define-key map (kbd "f") 'git-rebase-fixup) - (define-key map (kbd "y") 'git-rebase-insert) - (define-key map (kbd "k") 'git-rebase-kill-line) - (define-key map (kbd "C-k") 'git-rebase-kill-line) - (define-key map (kbd "p") 'git-rebase-backward-line) - (define-key map (kbd "n") 'forward-line) - (define-key map (kbd "M-p") 'git-rebase-move-line-up) - (define-key map (kbd "M-n") 'git-rebase-move-line-down) - (define-key map (kbd "M-") 'git-rebase-move-line-up) - (define-key map (kbd "M-") 'git-rebase-move-line-down) - map) - "Keymap for Git-Rebase mode.") - -(easy-menu-define git-rebase-mode-menu git-rebase-mode-map - "Git-Rebase mode menu" - '("Rebase" - ["Pick" git-rebase-pick t] - ["Reword" git-rebase-reword t] - ["Edit" git-rebase-edit t] - ["Squash" git-rebase-squash t] - ["Fixup" git-rebase-fixup t] - ["Kill" git-rebase-kill-line t] - ["Move Down" git-rebase-move-line-down t] - ["Move Up" git-rebase-move-line-up t] - ["Execute" git-rebase-exec t] - "---" - ["Abort" git-rebase-abort t] - ["Done" git-rebase-server-edit t])) - -;;; Utilities - -(defun git-rebase-edit-line (change-to) - (when (git-rebase-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) - (when git-rebase-auto-advance - (forward-line))))) - -(defmacro git-rebase-define-action (sym) - (declare (indent defun)) - (let ((fn (intern (format "git-rebase-%s" sym)))) - `(progn - (defun ,fn () - (interactive) - (git-rebase-edit-line ,(symbol-name sym))) - (put ',fn 'definition-name ',sym)))) - -(defun git-rebase-looking-at-action () - "Return non-nil if looking at an action line." - (save-excursion - (goto-char (point-at-bol)) - (looking-at git-rebase-action-line-re))) - -(defun git-rebase-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 git-rebase-action-line-re) - (looking-at git-rebase-exec-line-re)))) - -(defun git-rebase-looking-at-exec () - "Return non-nil if cursor is on an exec line." - (string-match git-rebase-exec-line-re (thing-at-point 'line))) - -(defun git-rebase-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 git-rebase-exec-line-re line)))) - -;;; Commands - -(git-rebase-define-action pick) -(git-rebase-define-action reword) -(git-rebase-define-action edit) -(git-rebase-define-action squash) -(git-rebase-define-action fixup) - -(defun git-rebase-move-line-up () - "Move the current action line up." - (interactive) - (when (git-rebase-looking-at-action-or-exec) - (let ((buffer-read-only nil) - (col (current-column))) - (goto-char (point-at-bol)) - (unless (bobp) - (transpose-lines 1) - (forward-line -2)) - (move-to-column col)))) - -(defun git-rebase-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 (git-rebase-looking-at-action-or-exec) - (save-excursion - (forward-line) - (git-rebase-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 git-rebase-server-edit () - "Save the action buffer and end the session." - (interactive) - (save-buffer) - (server-edit)) - -(defun git-rebase-abort () - "Abort this rebase. -This is dune 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)) - (erase-buffer) - (save-buffer) - (server-edit)))) - -(defun git-rebase-kill-line () - "Kill the current action line." - (interactive) - (when (and (not (eq (char-after (point-at-bol)) ?#)) - (git-rebase-looking-at-action-or-exec)) - (beginning-of-line) - (let ((inhibit-read-only t)) - (insert "#")) - (forward-line))) - -(defun git-rebase-insert (rev) - "Read an arbitrary commit and insert it below current line." - (interactive - (list (if (fboundp 'magit-read-branch-or-commit) - (magit-read-branch-or-commit "Insert revision") - (read-string "Insert revision: ")))) - (forward-line) - (let ((summary (if (fboundp 'magit-rev-format) - (magit-rev-format "%h %s" rev) - (process-lines "git" "show" "-s" "--format=%h %s" rev)))) - (if summary - (let ((inhibit-read-only t)) - (insert "pick " summary ?\n)) - (user-error "Unknown revision")))) - -(defun git-rebase-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 (git-rebase-looking-at-exec)) - (let ((new-line (git-rebase-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))) - ((git-rebase-looking-at-killed-exec) - (save-excursion - (beginning-of-line) - (let ((buffer-read-only nil)) - (delete-char 1)))) - (t - (let ((inhibit-read-only t) - (line (git-rebase-read-exec-line))) - (unless (equal "" line) - (move-end-of-line nil) - (newline) - (insert (concat "exec " line)))) - (move-beginning-of-line nil)))) - -(defun git-rebase-read-exec-line (&optional initial-line) - (read-shell-command "Execute: " initial-line)) - -(defun git-rebase-undo (&optional arg) - "A thin wrapper around `undo', which allows undoing in read-only buffers." - (interactive "P") - (let ((inhibit-read-only t)) - (undo arg))) - -(defun git-rebase-show-commit (&optional arg) - "Show the commit on the current line if any." - (interactive "P") - (save-excursion - (goto-char (point-at-bol)) - (when (looking-at git-rebase-action-line-re) - (let ((commit (match-string 2))) - (if (fboundp 'magit-show-commit) - (magit-show-commit commit) - (shell-command (concat "git show " commit))))))) - -(defun git-rebase-backward-line (&optional n) - "Move N lines backward (forward if N is negative). -Like `forward-line' but go into the opposite direction." - (interactive "p") - (forward-line (* n -1))) - -;;; Mode - -;;;###autoload -(define-derived-mode git-rebase-mode special-mode "Git 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 '(git-rebase-mode-font-lock-keywords t t)) - (when git-rebase-remove-instructions - (let ((inhibit-read-only t)) - (flush-lines "^\\($\\|#\\)")))) - -(defvar git-rebase-mode-font-lock-keywords - `((,git-rebase-action-line-re - (1 font-lock-keyword-face) - (2 'git-rebase-hash) - (3 'git-rebase-description)) - (,git-rebase-exec-line-re 1 font-lock-keyword-face) - ("^#.*" 0 font-lock-comment-face) - (,git-rebase-dead-line-re 0 'git-rebase-killed-action t)) - "Font lock keywords for Git-Rebase mode.") - -(defun git-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 "git-rebase-" (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 'git-rebase-mode-hook 'git-rebase-mode-show-keybindings t) - -(defun git-rebase-mode-disable-before-save-hook () - (set (make-local-variable 'before-save-hook) nil)) - -(add-hook 'git-rebase-mode-hook 'git-rebase-mode-disable-before-save-hook) - -;;;###autoload -(add-to-list 'auto-mode-alist - '("/git-rebase-todo\\'" . git-rebase-mode)) - -(provide 'git-rebase-mode) -;; Local Variables: -;; indent-tabs-mode: nil -;; End: -;;; git-rebase-mode.el ends here diff --git a/elpa/logito-20120225.1255/logito-autoloads.el b/elpa/logito-20120225.1255/logito-autoloads.el new file mode 100644 index 0000000..70a0911 --- /dev/null +++ b/elpa/logito-20120225.1255/logito-autoloads.el @@ -0,0 +1,15 @@ +;;; logito-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil nil ("logito.el") (22221 60700 987613 744000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; logito-autoloads.el ends here diff --git a/elpa/logito-20120225.1255/logito-pkg.el b/elpa/logito-20120225.1255/logito-pkg.el new file mode 100644 index 0000000..5507668 --- /dev/null +++ b/elpa/logito-20120225.1255/logito-pkg.el @@ -0,0 +1 @@ +(define-package "logito" "20120225.1255" "logging library for Emacs" '((eieio "1.3")) :keywords '("lisp" "tool")) diff --git a/elpa/logito-20120225.1255/logito.el b/elpa/logito-20120225.1255/logito.el new file mode 100644 index 0000000..e7ce9e5 --- /dev/null +++ b/elpa/logito-20120225.1255/logito.el @@ -0,0 +1,98 @@ +;;; logito.el --- logging library for Emacs + +;; Copyright (C) 2012 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: lisp, tool +;; Package-Version: 20120225.1255 +;; Version: 0.1 +;; Package-Requires: ((eieio "1.3")) + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; This module provides logging facility for Emacs + +;;; Code: + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(defclass logito-object () + ((level :initarg :level :initform nil))) + +(defmethod logito-insert-log ((log logito-object) format &rest objects) + "Base implementation, do nothing") + +(defmethod logito-should-log ((log logito-object) level) + (let ((l (oref log :level))) + (and (integerp l) + (<= level l)))) + +(defmethod logito-log ((log logito-object) level tag string &rest objects) + (when (logito-should-log log level) + (apply 'logito-insert-log log (format "[%s] %s" tag string) objects))) + +(defmethod logito-log (log level tag string &rest objects) + "Fallback implementation, do nothing. This allows in particular + to pass nil as the log object." + nil) + +(defclass logito-message-object (logito-object) + ()) + +(defmethod logito-insert-log ((log logito-message-object) format &rest objects) + (apply 'message format objects)) + +(defclass logito-buffer-object (logito-object) + ((buffer :initarg :buffer :initform nil))) + +(defmethod logito-should-log ((log logito-buffer-object) level) + (and (oref log :buffer) + (call-next-method))) + +(defmethod logito-insert-log ((log logito-buffer-object) format &rest objects) + (let ((buffer (get-buffer-create (oref log :buffer)))) + (with-current-buffer buffer + (goto-char (point-max)) + (insert (apply 'format format objects) "\n\n")))) + +(defmacro logito-def-level (sym val &optional pkg) + "Define a constant logito--level and a macro logito: +associated with this level." + (let* ((pkg (or pkg 'logito)) + (const (intern (format "%s:%s-level" + (symbol-name pkg) (symbol-name sym)))) + (mac (intern (format "%s:%s" + (symbol-name pkg) (symbol-name sym))))) + `(progn + (defconst ,const ,val) + (defmacro ,mac (log string &rest objects) + (append + (list 'logito-log log ,const '',sym string) + objects))))) + +;; built-in log levels +(logito-def-level error 0) +(logito-def-level info 5) +(logito-def-level verbose 10) +(logito-def-level debug 15) + +(provide 'logito) +;;; logito.el ends here diff --git a/elpa/magit-1.4.1/magit-autoloads.el b/elpa/magit-1.4.1/magit-autoloads.el deleted file mode 100644 index cd1c0a4..0000000 --- a/elpa/magit-1.4.1/magit-autoloads.el +++ /dev/null @@ -1,579 +0,0 @@ -;;; magit-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) - -;;;### (autoloads nil "magit" "magit.el" (21831 16623 612188 923000)) -;;; Generated autoloads from magit.el - -(autoload 'magit-git-command "magit" "\ -Execute a Git subcommand asynchronously, displaying the output. -With a prefix argument run Git in the root of the current -repository. Non-interactively run Git in DIRECTORY with ARGS. - -\(fn ARGS DIRECTORY)" t nil) - -(autoload 'magit-show-commit "magit" "\ -Show information about COMMIT. - -\(fn COMMIT &optional NOSELECT)" t nil) - -(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. - -Depending on option `magit-status-buffer-switch-function' the -status buffer is shown in another window (the default) or the -current window. Non-interactively optional SWITCH-FUNCTION -can be used to override this. - -\(fn DIR &optional SWITCH-FUNCTION)" t nil) - -(autoload 'magit-stage-all "magit" "\ -Add all remaining changes in tracked files to staging area. -With a prefix argument, add remaining untracked files as well. -\('git add [-u] .'). - -\(fn &optional INCLUDING-UNTRACKED)" t nil) - -(autoload 'magit-unstage-all "magit" "\ -Remove all changes from staging area. -\('git reset --mixed HEAD'). - -\(fn)" t nil) - -(autoload 'magit-dired-jump "magit" "\ -Visit current item in dired. -With a prefix argument, visit in other window. - -\(fn &optional OTHER-WINDOW)" t nil) - -(autoload 'magit-show "magit" "\ -Display and select a buffer containing FILE as stored in REV. - -Insert the contents of FILE as stored in the revision REV into a -buffer. Then select the buffer using `pop-to-buffer' or with a -prefix argument using `switch-to-buffer'. Non-interactivity use -SWITCH-FUNCTION to switch to the buffer, if that is nil simply -return the buffer, without displaying it. - -\(fn REV FILE &optional SWITCH-FUNCTION)" t nil) - -(autoload 'magit-merge "magit" "\ -Merge REVISION into the current 'HEAD', leaving changes uncommitted. -With a prefix argument, skip editing the log message and commit. -\('git merge [--no-commit] REVISION'). - -\(fn REVISION &optional DO-COMMIT)" t nil) - -(autoload 'magit-merge-abort "magit" "\ -Abort the current merge operation. - -\(fn)" t nil) - -(autoload 'magit-checkout "magit" "\ -Switch 'HEAD' to REVISION and update working tree. -Fails if working tree or staging area contain uncommitted changes. -If REVISION is a remote branch, offer to create a local branch. -\('git checkout [-b] REVISION'). - -\(fn REVISION)" t nil) - -(autoload 'magit-create-branch "magit" "\ -Switch 'HEAD' to new BRANCH at revision PARENT and update working tree. -Fails if working tree or staging area contain uncommitted changes. -\('git checkout -b BRANCH REVISION'). - -\(fn BRANCH PARENT)" t nil) - -(autoload 'magit-delete-branch "magit" "\ -Delete the BRANCH. -If the branch is the current one, offers to switch to `master' first. -With prefix, forces the removal even if it hasn't been merged. -Works with local or remote branches. -\('git branch [-d|-D] BRANCH' or 'git push :refs/heads/BRANCH'). - -\(fn BRANCH &optional FORCE)" t nil) - -(autoload 'magit-rename-branch "magit" "\ -Rename branch OLD to NEW. -With prefix, forces the rename even if NEW already exists. -\('git branch [-m|-M] OLD NEW'). - -\(fn OLD NEW &optional FORCE)" t nil) - -(autoload 'magit-add-remote "magit" "\ -Add the REMOTE and fetch it. -\('git remote add REMOTE URL'). - -\(fn REMOTE URL)" t nil) - -(autoload 'magit-remove-remote "magit" "\ -Delete the REMOTE. -\('git remote rm REMOTE'). - -\(fn REMOTE)" t nil) - -(autoload 'magit-rename-remote "magit" "\ -Rename remote OLD to NEW. -\('git remote rename OLD NEW'). - -\(fn OLD NEW)" t nil) - -(autoload 'magit-interactive-rebase "magit" "\ -Start a git rebase -i session, old school-style. - -\(fn COMMIT)" t nil) - -(autoload 'magit-reset-head "magit" "\ -Switch 'HEAD' to REVISION, keeping prior working tree and staging area. -Any differences from REVISION become new changes to be committed. -With prefix argument, all uncommitted changes in working tree -and staging area are lost. -\('git reset [--soft|--hard] REVISION'). - -\(fn REVISION &optional HARD)" t nil) - -(autoload 'magit-reset-head-hard "magit" "\ -Switch 'HEAD' to REVISION, losing all changes. -Uncomitted changes in both working tree and staging area are lost. -\('git reset --hard REVISION'). - -\(fn REVISION)" t nil) - -(autoload 'magit-reset-working-tree "magit" "\ -Revert working tree and clear changes from staging area. -\('git reset --hard HEAD'). - -With a prefix arg, also remove untracked files. -With two prefix args, remove ignored files as well. - -\(fn &optional ARG)" t nil) - -(autoload 'magit-fetch "magit" "\ -Fetch from REMOTE. - -\(fn REMOTE)" t nil) - -(autoload 'magit-fetch-current "magit" "\ -Run fetch for default remote. - -If there is no default remote, ask for one. - -\(fn)" t nil) - -(autoload 'magit-remote-update "magit" "\ -Update all remotes. - -\(fn)" t nil) - -(autoload 'magit-pull "magit" "\ -Run git pull. - -If there is no default remote, the user is prompted for one and -its values is saved with git config. If there is no default -merge branch, the user is prompted for one and its values is -saved with git config. With a prefix argument, the default -remote is not used and the user is prompted for a remote. With -two prefix arguments, the default merge branch is not used and -the user is prompted for a merge branch. Values entered by the -user because of prefix arguments are not saved with git config. - -\(fn)" t nil) - -(autoload 'magit-push-tags "magit" "\ -Push tags to a remote repository. - -Push tags to the current branch's remote. If that isn't set push -to \"origin\" or if that remote doesn't exit but only a single -remote is defined use that. Otherwise or with a prefix argument -ask the user what remote to use. - -\(fn)" t nil) - -(autoload 'magit-push "magit" "\ -Push the current branch to a remote repository. - -This command runs the `magit-push-remote' hook. By default that -means running `magit-push-dwim'. So unless you have customized -the hook this command behaves like this: - -With a single prefix argument ask the user what branch to push -to. With two or more prefix arguments also ask the user what -remote to push to. Otherwise use the remote and branch as -configured using the Git variables `branch..remote' and -`branch..merge'. If the former is undefined ask the user. -If the latter is undefined push without specifing the remote -branch explicitly. - -Also see option `magit-set-upstream-on-push'. - -\(fn)" t nil) - -(autoload 'magit-commit "magit" "\ -Create a new commit on HEAD. -With a prefix argument amend to the commit at HEAD instead. -\('git commit [--amend]'). - -\(fn &optional AMENDP)" t nil) - -(autoload 'magit-commit-amend "magit" "\ -Amend the last commit. -\('git commit --amend'). - -\(fn)" t nil) - -(autoload 'magit-commit-extend "magit" "\ -Amend the last commit, without editing the message. -With a prefix argument do change the committer date, otherwise -don't. The option `magit-commit-extend-override-date' can be -used to inverse the meaning of the prefix argument. -\('git commit --no-edit --amend [--keep-date]'). - -\(fn &optional OVERRIDE-DATE)" t nil) - -(autoload 'magit-commit-reword "magit" "\ -Reword the last commit, ignoring staged changes. - -With a prefix argument do change the committer date, otherwise -don't. The option `magit-commit-rewrite-override-date' can be -used to inverse the meaning of the prefix argument. - -Non-interactively respect the optional OVERRIDE-DATE argument -and ignore the option. - -\('git commit --only --amend'). - -\(fn &optional OVERRIDE-DATE)" t nil) - -(autoload 'magit-commit-fixup "magit" "\ -Create a fixup commit. -With a prefix argument the user is always queried for the commit -to be fixed. Otherwise the current or marked commit may be used -depending on the value of option `magit-commit-squash-commit'. -\('git commit [--no-edit] --fixup=COMMIT'). - -\(fn &optional COMMIT)" t nil) - -(autoload 'magit-commit-squash "magit" "\ -Create a squash commit. -With a prefix argument the user is always queried for the commit -to be fixed. Otherwise the current or marked commit may be used -depending on the value of option `magit-commit-squash-commit'. -\('git commit [--no-edit] --fixup=COMMIT'). - -\(fn &optional COMMIT FIXUP)" t nil) - -(autoload 'magit-tag "magit" "\ -Create a new tag with the given NAME at REV. -With a prefix argument annotate the tag. -\('git tag [--annotate] NAME REV'). - -\(fn NAME REV &optional ANNOTATE)" t nil) - -(autoload 'magit-delete-tag "magit" "\ -Delete the tag with the given NAME. -\('git tag -d NAME'). - -\(fn NAME)" t nil) - -(autoload 'magit-stash "magit" "\ -Create new stash of working tree and staging area named DESCRIPTION. -Working tree and staging area revert to the current 'HEAD'. -With prefix argument, changes in staging area are kept. -\('git stash save [--keep-index] DESCRIPTION') - -\(fn DESCRIPTION)" t nil) - -(autoload 'magit-stash-snapshot "magit" "\ -Create new stash of working tree and staging area; keep changes in place. -\('git stash save \"Snapshot...\"; git stash apply stash@{0}') - -\(fn)" t nil) - -(autoload 'magit-submodule-update "magit" "\ -Update the submodule of the current git repository. -With a prefix arg, do a submodule update --init. - -\(fn &optional INIT)" t nil) - -(autoload 'magit-submodule-update-init "magit" "\ -Update and init the submodule of the current git repository. - -\(fn)" t nil) - -(autoload 'magit-submodule-init "magit" "\ -Initialize the submodules. - -\(fn)" t nil) - -(autoload 'magit-submodule-sync "magit" "\ -Synchronizes submodule's remote URL configuration. - -\(fn)" t nil) - -(autoload 'magit-bisect-start "magit" "\ -Start a bisect session. - -Bisecting a bug means to find the commit that introduced it. -This command starts such a bisect session by asking for a know -good and a bad commit. To move the session forward use the -other actions from the bisect popup (\\\\[magit-key-mode-popup-bisecting]). - -\(fn BAD GOOD)" t nil) - -(autoload 'magit-bisect-reset "magit" "\ -After bisecting cleanup bisection state and return to original HEAD. - -\(fn)" t nil) - -(autoload 'magit-bisect-good "magit" "\ -While bisecting, mark the current commit as good. -Use this after you have asserted that the commit does not contain -the bug in question. - -\(fn)" t nil) - -(autoload 'magit-bisect-bad "magit" "\ -While bisecting, mark the current commit as bad. -Use this after you have asserted that the commit does contain the -bug in question. - -\(fn)" t nil) - -(autoload 'magit-bisect-skip "magit" "\ -While bisecting, skip the current commit. -Use this if for some reason the current commit is not a good one -to test. This command lets Git choose a different one. - -\(fn)" t nil) - -(autoload 'magit-bisect-run "magit" "\ -Bisect automatically by running commands after each step. - -\(fn CMDLINE)" t nil) - -(autoload 'magit-log "magit" "\ - - -\(fn &optional RANGE)" t nil) - -(autoload 'magit-log-ranged "magit" "\ - - -\(fn RANGE)" t nil) - -(autoload 'magit-log-long "magit" "\ - - -\(fn &optional RANGE)" t nil) - -(autoload 'magit-log-long-ranged "magit" "\ - - -\(fn RANGE)" t nil) - -(autoload 'magit-file-log "magit" "\ -Display the log for the currently visited file or another one. -With a prefix argument show the log graph. - -\(fn FILE &optional USE-GRAPH)" t nil) - -(autoload 'magit-reflog "magit" "\ -Display the reflog of the current branch. -With a prefix argument another branch can be chosen. - -\(fn REF)" t nil) - -(autoload 'magit-reflog-head "magit" "\ -Display the HEAD reflog. - -\(fn)" t nil) - -(autoload 'magit-cherry "magit" "\ -Show commits in a branch that are not merged in the upstream branch. - -\(fn HEAD UPSTREAM)" t nil) - -(autoload 'magit-save-index "magit" "\ -Add the content of current file as if it was the index. - -\(fn)" t nil) - -(autoload 'magit-interactive-resolve "magit" "\ -Resolve a merge conflict using Ediff. - -\(fn FILE)" t nil) - -(autoload 'magit-diff "magit" "\ -Show differences between two commits. -RANGE should be a range (A..B or A...B) but can also be a single -commit. If one side of the range is omitted, then it defaults -to HEAD. If just a commit is given, then changes in the working -tree relative to that commit are shown. - -\(fn RANGE &optional WORKING ARGS)" t nil) - -(autoload 'magit-diff-working-tree "magit" "\ -Show differences between a commit and the current working tree. - -\(fn REV)" t nil) - -(autoload 'magit-diff-staged "magit" "\ -Show differences between the index and the HEAD commit. - -\(fn)" t nil) - -(autoload 'magit-diff-unstaged "magit" "\ -Show differences between the current working tree and index. - -\(fn)" t nil) - -(autoload 'magit-diff-stash "magit" "\ -Show changes in a stash. -A Stash consist of more than just one commit. This command uses -a special diff range so that the stashed changes actually were a -single commit. - -\(fn STASH &optional NOSELECT)" t nil) - -(autoload 'magit-wazzup "magit" "\ -Show a list of branches in a dedicated buffer. -Unlike in the buffer created by `magit-branch-manager' each -branch can be expanded to show a list of commits not merged -into the selected branch. - -\(fn BRANCH)" t nil) - -(autoload 'magit-branch-manager "magit" "\ -Show a list of branches in a dedicated buffer. - -\(fn)" t nil) - -(autoload 'magit-init "magit" "\ -Create or reinitialize a Git repository. -Read directory name and initialize it as new Git repository. - -If the directory is below an existing repository, then the user -has to confirm that a new one should be created inside; or when -the directory is the root of the existing repository, whether -it should be reinitialized. - -Non-interactively DIRECTORY is always (re-)initialized. - -\(fn DIRECTORY)" t nil) - -(autoload 'magit-add-change-log-entry "magit" "\ -Find change log file and add date entry and item for current change. -This differs from `add-change-log-entry' (which see) in that -it acts on the current hunk in a Magit buffer instead of on -a position in a file-visiting buffer. - -\(fn &optional WHOAMI FILE-NAME OTHER-WINDOW)" t nil) - -(autoload 'magit-add-change-log-entry-other-window "magit" "\ -Find change log file in other window and add entry and item. -This differs from `add-change-log-entry-other-window' (which see) -in that it acts on the current hunk in a Magit buffer instead of -on a position in a file-visiting buffer. - -\(fn &optional WHOAMI FILE-NAME)" t nil) - -(autoload 'magit-run-git-gui "magit" "\ -Run `git gui' for the current git repository. - -\(fn)" t nil) - -(autoload 'magit-run-git-gui-blame "magit" "\ -Run `git gui blame' on the given FILENAME and COMMIT. -Interactively run it for the current file and the HEAD, with a -prefix or when the current file cannot be determined let the user -choose. When the current buffer is visiting FILENAME instruct -blame to center around the line point is on. - -\(fn COMMIT FILENAME &optional LINENUM)" t nil) - -(autoload 'magit-run-gitk "magit" "\ -Run Gitk for the current git repository. -Without a prefix argument run `gitk --all', with -a prefix argument run gitk without any arguments. - -\(fn ARG)" t nil) - -;;;*** - -;;;### (autoloads nil "magit-blame" "magit-blame.el" (21831 16623 -;;;;;; 579188 925000)) -;;; Generated autoloads from magit-blame.el - -(autoload 'magit-blame-mode "magit-blame" "\ -Display blame information inline. - -\(fn &optional ARG)" t nil) - -;;;*** - -;;;### (autoloads nil "magit-key-mode" "magit-key-mode.el" (21831 -;;;;;; 16623 594188 924000)) -;;; Generated autoloads from magit-key-mode.el - -(defvar magit-key-mode-groups '((dispatch (actions ("b" "Branching" magit-key-mode-popup-branching) ("B" "Bisecting" magit-key-mode-popup-bisecting) ("c" "Committing" magit-key-mode-popup-committing) ("d" "Diff worktree" magit-diff-working-tree) ("D" "Diff" magit-diff) ("f" "Fetching" magit-key-mode-popup-fetching) ("F" "Pulling" magit-key-mode-popup-pulling) ("g" "Refresh Buffers" magit-refresh-all) ("l" "Logging" magit-key-mode-popup-logging) ("m" "Merging" magit-key-mode-popup-merging) ("M" "Remoting" magit-key-mode-popup-remoting) ("P" "Pushing" magit-key-mode-popup-pushing) ("o" "Submoduling" magit-key-mode-popup-submodule) ("r" "Rewriting" magit-key-mode-popup-rewriting) ("R" "Rebasing" magit-rebase-step) ("s" "Show Status" magit-status) ("S" "Stage all" magit-stage-all) ("t" "Tagging" magit-key-mode-popup-tagging) ("U" "Unstage all" magit-unstage-all) ("v" "Show Commit" magit-show-commit) ("V" "Show File" magit-show) ("w" "Wazzup" magit-wazzup) ("X" "Reset worktree" magit-reset-working-tree) ("y" "Cherry" magit-cherry) ("z" "Stashing" magit-key-mode-popup-stashing) ("!" "Running" magit-key-mode-popup-running) ("$" "Show Process" magit-process))) (logging (man-page "git-log") (actions ("l" "Short" magit-log) ("L" "Long" magit-log-long) ("h" "Head Reflog" magit-reflog-head) ("f" "File log" magit-file-log) ("rl" "Ranged short" magit-log-ranged) ("rL" "Ranged long" magit-log-long-ranged) ("rh" "Reflog" magit-reflog)) (switches ("-m" "Only merge commits" "--merges") ("-s" "No merge commits" "--no-merges") ("-do" "Date Order" "--date-order") ("-f" "First parent" "--first-parent") ("-i" "Case insensitive patterns" "-i") ("-pr" "Pickaxe regex" "--pickaxe-regex") ("-g" "Show Graph" "--graph") ("-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) ("=a" "Author" "--author=" read-from-minibuffer) ("=g" "Grep messages" "--grep=" read-from-minibuffer) ("=G" "Grep patches" "-G" read-from-minibuffer) ("=L" "Trace evolution of line range [long log only]" "-L" magit-read-file-trace) ("=s" "Pickaxe search" "-S" read-from-minibuffer) ("=b" "Branches" "--branches=" read-from-minibuffer) ("=R" "Remotes" "--remotes=" read-from-minibuffer))) (running (actions ("!" "Git Subcommand (from root)" magit-git-command-topdir) (":" "Git Subcommand (from pwd)" 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 ("-f" "Force" "--force") ("-r" "Rebase" "--rebase"))) (branching (man-page "git-branch") (actions ("v" "Branch manager" magit-branch-manager) ("b" "Checkout" magit-checkout) ("c" "Create" magit-create-branch) ("r" "Rename" magit-rename-branch) ("k" "Delete" magit-delete-branch)) (switches ("-t" "Set upstream configuration" "--track") ("-m" "Merged to HEAD" "--merged") ("-M" "Merged to master" "--merged=master") ("-n" "Not merged to HEAD" "--no-merged") ("-N" "Not merged to master" "--no-merged=master")) (arguments ("=c" "Contains" "--contains=" magit-read-rev-with-default) ("=m" "Merged" "--merged=" magit-read-rev-with-default) ("=n" "Not merged" "--no-merged=" magit-read-rev-with-default))) (remoting (man-page "git-remote") (actions ("v" "Remote 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" "Create" magit-tag) ("k" "Delete" magit-delete-tag)) (switches ("-a" "Annotate" "--annotate") ("-f" "Force" "--force") ("-s" "Sign" "--sign"))) (stashing (man-page "git-stash") (actions ("v" "View" magit-diff-stash) ("z" "Save" magit-stash) ("s" "Snapshot" magit-stash-snapshot) ("a" "Apply" magit-stash-apply) ("p" "Pop" magit-stash-pop) ("k" "Drop" magit-stash-drop)) (switches ("-k" "Keep index" "--keep-index") ("-u" "Include untracked files" "--include-untracked") ("-a" "Include all files" "--all"))) (committing (man-page "git-commit") (actions ("c" "Commit" magit-commit) ("a" "Amend" magit-commit-amend) ("e" "Extend" magit-commit-extend) ("r" "Reword" magit-commit-reword) ("f" "Fixup" magit-commit-fixup) ("s" "Squash" magit-commit-squash)) (switches ("-a" "Stage all modified and deleted files" "--all") ("-e" "Allow empty commit" "--allow-empty") ("-v" "Show diff of changes to be committed" "--verbose") ("-n" "Bypass git hooks" "--no-verify") ("-s" "Add Signed-off-by line" "--signoff") ("-R" "Claim authorship and reset author date" "--reset-author")) (arguments ("=A" "Override the author" "--author=" read-from-minibuffer) ("=S" "Sign using gpg" "--gpg-sign=" magit-read-gpg-secret-key))) (merging (man-page "git-merge") (actions ("m" "Merge" magit-merge) ("A" "Abort" magit-merge-abort)) (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) ("d" "Diff pending" magit-rewrite-diff-pending) ("*" "Set unused" magit-rewrite-set-unused) ("." "Set used" magit-rewrite-set-used))) (apply-mailbox (man-page "git-am") (actions ("J" "Apply Mailbox" magit-apply-mailbox)) (switches ("-s" "add a Signed-off-by line to the commit message" "--signoff") ("-3" "allow fall back on 3way merging if needed" "--3way") ("-k" "pass -k flag to git-mailinfo" "--keep") ("-c" "strip everything before a scissors line" "--scissors") ("-p" "pass it through git-apply" "-p") ("-r" "override error message when patch failure occurs" "--resolvemsg") ("-d" "lie about committer date" "--committer-date-is-author-date") ("-D" "use current timestamp for author date" "--ignore-date") ("-b" "pass -b flag to git-mailinfo" "--keep-non-patch")) (arguments ("=p" "format the patch(es) are in" "--patch-format=" read-from-minibuffer))) (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) ("r" "Reset" magit-bisect-reset) ("s" "Start" magit-bisect-start) ("u" "Run" magit-bisect-run))) (diff-options (actions ("s" "Set" magit-set-diff-options) ("d" "Set default" magit-set-default-diff-options) ("c" "Save default" magit-save-default-diff-options) ("r" "Reset to default" magit-reset-diff-options) ("h" "Toggle Hunk Refinement" magit-diff-toggle-refine-hunk)) (switches ("-m" "Show smallest possible diff" "--minimal") ("-p" "Use patience diff algorithm" "--patience") ("-h" "Use histogram diff algorithm" "--histogram") ("-b" "Ignore whitespace changes" "--ignore-space-change") ("-w" "Ignore all whitespace" "--ignore-all-space") ("-W" "Show surrounding functions" "--function-context")))) "\ -Holds the key, help, function mapping for the log-mode. -If you modify this make sure you reset `magit-key-mode-keymaps' -to nil.") - (mapc (lambda (g) (eval `(autoload ',(intern (concat "magit-key-mode-popup-" (symbol-name (car g)))) "magit-key-mode" ,(concat "Key menu for " (symbol-name (car g))) t))) magit-key-mode-groups) - -;;;*** - -;;;### (autoloads nil "magit-wip" "magit-wip.el" (21831 16623 587188 -;;;;;; 925000)) -;;; Generated autoloads from magit-wip.el - -(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 nil ("magit-pkg.el") (21831 16623 655620 175000)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; magit-autoloads.el ends here diff --git a/elpa/magit-1.4.1/magit-blame.el b/elpa/magit-1.4.1/magit-blame.el deleted file mode 100644 index 550df5a..0000000 --- a/elpa/magit-1.4.1/magit-blame.el +++ /dev/null @@ -1,307 +0,0 @@ -;;; magit-blame.el --- blame support for Magit - -;; Copyright (C) 2012-2015 The Magit Project Developers -;; -;; For a full list of contributors, see the AUTHORS.md file -;; at the top-level directory of this distribution and at -;; https://raw.github.com/magit/magit/master/AUTHORS.md - -;; Author: Yann Hodique -;; Package: magit - -;; Contains code from Egg (Emacs Got Git) , -;; released under the GNU General Public License version 3 or later. - -;; 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 . - -;;; Commentary: - -;; Control git-blame from Magit. -;; This code has been backported from Egg (Magit fork) to Magit. - -;;; Code: - -(eval-when-compile (require 'cl-lib)) -(require 'magit) -(require 'easymenu) - -;;; Options - -(defgroup magit-blame nil - "Git-blame support for Magit." - :group 'magit-extensions) - -(defcustom magit-blame-ignore-whitespace t - "Ignore whitespace when determining blame information." - :group 'magit-blame - :type 'boolean) - -(defcustom magit-time-format-string "%Y-%m-%dT%T%z" - "How to format time in magit-blame header." - :group 'magit-blame - :type 'string) - -(defface magit-blame-header - '((t :inherit magit-section-title)) - "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) - -;;; Keymaps - -(defvar magit-blame-map - (let ((map (make-sparse-keymap))) - (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}") - -(easy-menu-define magit-blame-mode-menu magit-blame-map - "Magit blame menu" - '("Blame" - ["Locate Commit" magit-blame-locate-commit t] - ["Next" magit-blame-next-chunk t] - ["Previous" magit-blame-previous-chunk t] - "---" - ["Quit" magit-blame-mode t])) - -;;; Mode - -(defvar-local magit-blame-buffer-read-only nil) - -;;;###autoload -(define-minor-mode magit-blame-mode - "Display blame information inline." - :keymap magit-blame-map - :lighter " blame" - (unless (buffer-file-name) - (user-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)) - - (cond (magit-blame-mode - (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)) - (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) - (when (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 - (apply 'magit-git-insert "blame" "--porcelain" - `(,@(and magit-blame-ignore-whitespace (list "-w")) "--" - ,(file-name-nondirectory (buffer-file-name buffer)))) - (magit-blame-parse buffer (current-buffer))))))) - -;;; Commands - -(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) - (when (overlay-get ov :blame) - (setq sha1 (plist-get (nth 3 (overlay-get ov :blame)) :sha1)))) - (when sha1 - (magit-show-commit sha1)))) - -(defun magit-blame-next-chunk () - "Go to the next blame chunk." - (interactive) - (let ((next (next-single-property-change (point) :blame))) - (when next - (goto-char next)))) - -(defun magit-blame-previous-chunk () - "Go to the previous blame chunk." - (interactive) - (let ((prev (previous-single-property-change (point) :blame))) - (when prev - (goto-char prev)))) - -;;; Parse - -(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) -;; Local Variables: -;; indent-tabs-mode: nil -;; End: -;;; magit-blame.el ends here diff --git a/elpa/magit-1.4.1/magit-key-mode.el b/elpa/magit-1.4.1/magit-key-mode.el deleted file mode 100644 index 7dd7dc2..0000000 --- a/elpa/magit-1.4.1/magit-key-mode.el +++ /dev/null @@ -1,735 +0,0 @@ -;;; magit-key-mode.el --- interactively tune git invocation - -;; Copyright (C) 2010-2015 The Magit Project Developers -;; -;; For a full list of contributors, see the AUTHORS.md file -;; at the top-level directory of this distribution and at -;; https://raw.github.com/magit/magit/master/AUTHORS.md - -;; Author: Phil Jackson -;; Package: magit - -;; 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 . - -;;; Commentary: - -;; This library implements `magit-key-mode' which is used throughout -;; Magit to let the user interactively select the command, switches -;; and options to call Git with. It can be though of as a way to -;; provide "postfix" arguments. - -;;; Code: - -(require 'magit) - -(eval-when-compile (require 'cl-lib)) - -(defvar magit-key-mode-keymaps) -(defvar magit-key-mode-last-buffer) -(defvar magit-pre-key-mode-window-conf) - -;;; Options - -(defcustom magit-key-mode-show-usage t - "Whether to show usage information when entering a popup." - :group 'magit - :type 'boolean) - -;;; Faces - -(defface magit-key-mode-header-face - '((t :inherit font-lock-keyword-face)) - "Face for key mode header lines." - :group 'magit-faces) - -(defface magit-key-mode-button-face - '((t :inherit font-lock-builtin-face)) - "Face for key mode buttons." - :group 'magit-faces) - -(defface magit-key-mode-switch-face - '((t :inherit font-lock-warning-face)) - "Face for key mode switches." - :group 'magit-faces) - -(defface magit-key-mode-args-face - '((t :inherit widget-field)) - "Face for key mode switch arguments." - :group 'magit-faces) - -;;; Keygroups -;;;###autoload -(defvar magit-key-mode-groups - '((dispatch - (actions - ("b" "Branching" magit-key-mode-popup-branching) - ("B" "Bisecting" magit-key-mode-popup-bisecting) - ("c" "Committing" magit-key-mode-popup-committing) - ("d" "Diff worktree" magit-diff-working-tree) - ("D" "Diff" magit-diff) - ("f" "Fetching" magit-key-mode-popup-fetching) - ("F" "Pulling" magit-key-mode-popup-pulling) - ("g" "Refresh Buffers" magit-refresh-all) - ("l" "Logging" magit-key-mode-popup-logging) - ("m" "Merging" magit-key-mode-popup-merging) - ("M" "Remoting" magit-key-mode-popup-remoting) - ("P" "Pushing" magit-key-mode-popup-pushing) - ("o" "Submoduling" magit-key-mode-popup-submodule) - ("r" "Rewriting" magit-key-mode-popup-rewriting) - ("R" "Rebasing" magit-rebase-step) - ("s" "Show Status" magit-status) - ("S" "Stage all" magit-stage-all) - ("t" "Tagging" magit-key-mode-popup-tagging) - ("U" "Unstage all" magit-unstage-all) - ("v" "Show Commit" magit-show-commit) - ("V" "Show File" magit-show) - ("w" "Wazzup" magit-wazzup) - ("X" "Reset worktree" magit-reset-working-tree) - ("y" "Cherry" magit-cherry) - ("z" "Stashing" magit-key-mode-popup-stashing) - ("!" "Running" magit-key-mode-popup-running) - ("$" "Show Process" magit-process))) - - (logging - (man-page "git-log") - (actions - ("l" "Short" magit-log) - ("L" "Long" magit-log-long) - ("h" "Head Reflog" magit-reflog-head) - ("f" "File log" magit-file-log) - ("rl" "Ranged short" magit-log-ranged) - ("rL" "Ranged long" magit-log-long-ranged) - ("rh" "Reflog" magit-reflog)) - (switches - ("-m" "Only merge commits" "--merges") - ("-s" "No merge commits" "--no-merges") - ("-do" "Date Order" "--date-order") - ("-f" "First parent" "--first-parent") - ("-i" "Case insensitive patterns" "-i") - ("-pr" "Pickaxe regex" "--pickaxe-regex") - ("-g" "Show Graph" "--graph") - ("-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) - ("=a" "Author" "--author=" read-from-minibuffer) - ("=g" "Grep messages" "--grep=" read-from-minibuffer) - ("=G" "Grep patches" "-G" read-from-minibuffer) - ("=L" "Trace evolution of line range [long log only]" - "-L" magit-read-file-trace) - ("=s" "Pickaxe search" "-S" read-from-minibuffer) - ("=b" "Branches" "--branches=" read-from-minibuffer) - ("=R" "Remotes" "--remotes=" read-from-minibuffer))) - - (running - (actions - ("!" "Git Subcommand (from root)" magit-git-command-topdir) - (":" "Git Subcommand (from pwd)" 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 - ("-f" "Force" "--force") - ("-r" "Rebase" "--rebase"))) - - (branching - (man-page "git-branch") - (actions - ("v" "Branch manager" magit-branch-manager) - ("b" "Checkout" magit-checkout) - ("c" "Create" magit-create-branch) - ("r" "Rename" magit-rename-branch) - ("k" "Delete" magit-delete-branch)) - (switches - ("-t" "Set upstream configuration" "--track") - ("-m" "Merged to HEAD" "--merged") - ("-M" "Merged to master" "--merged=master") - ("-n" "Not merged to HEAD" "--no-merged") - ("-N" "Not merged to master" "--no-merged=master")) - (arguments - ("=c" "Contains" "--contains=" magit-read-rev-with-default) - ("=m" "Merged" "--merged=" magit-read-rev-with-default) - ("=n" "Not merged" "--no-merged=" magit-read-rev-with-default))) - - (remoting - (man-page "git-remote") - (actions - ("v" "Remote 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" "Create" magit-tag) - ("k" "Delete" magit-delete-tag)) - (switches - ("-a" "Annotate" "--annotate") - ("-f" "Force" "--force") - ("-s" "Sign" "--sign"))) - - (stashing - (man-page "git-stash") - (actions - ("v" "View" magit-diff-stash) - ("z" "Save" magit-stash) - ("s" "Snapshot" magit-stash-snapshot) - ("a" "Apply" magit-stash-apply) - ("p" "Pop" magit-stash-pop) - ("k" "Drop" magit-stash-drop)) - (switches - ("-k" "Keep index" "--keep-index") - ("-u" "Include untracked files" "--include-untracked") - ("-a" "Include all files" "--all"))) - - (committing - (man-page "git-commit") - (actions - ("c" "Commit" magit-commit) - ("a" "Amend" magit-commit-amend) - ("e" "Extend" magit-commit-extend) - ("r" "Reword" magit-commit-reword) - ("f" "Fixup" magit-commit-fixup) - ("s" "Squash" magit-commit-squash)) - (switches - ("-a" "Stage all modified and deleted files" "--all") - ("-e" "Allow empty commit" "--allow-empty") - ("-v" "Show diff of changes to be committed" "--verbose") - ("-n" "Bypass git hooks" "--no-verify") - ("-s" "Add Signed-off-by line" "--signoff") - ("-R" "Claim authorship and reset author date" "--reset-author")) - (arguments - ("=A" "Override the author" "--author=" read-from-minibuffer) - ("=S" "Sign using gpg" "--gpg-sign=" magit-read-gpg-secret-key))) - - (merging - (man-page "git-merge") - (actions - ("m" "Merge" magit-merge) - ("A" "Abort" magit-merge-abort)) - (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) - ("d" "Diff pending" magit-rewrite-diff-pending) - ("*" "Set unused" magit-rewrite-set-unused) - ("." "Set used" magit-rewrite-set-used))) - - (apply-mailbox - (man-page "git-am") - (actions - ("J" "Apply Mailbox" magit-apply-mailbox)) - (switches - ("-s" "add a Signed-off-by line to the commit message" "--signoff") - ("-3" "allow fall back on 3way merging if needed" "--3way") - ("-k" "pass -k flag to git-mailinfo" "--keep") - ("-c" "strip everything before a scissors line" "--scissors") - ("-p" "pass it through git-apply" "-p") - ("-r" "override error message when patch failure occurs" "--resolvemsg") - ("-d" "lie about committer date" "--committer-date-is-author-date") - ("-D" "use current timestamp for author date" "--ignore-date") - ("-b" "pass -b flag to git-mailinfo" "--keep-non-patch")) - (arguments - ("=p" "format the patch(es) are in" "--patch-format=" read-from-minibuffer))) - - (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) - ("r" "Reset" magit-bisect-reset) - ("s" "Start" magit-bisect-start) - ("u" "Run" magit-bisect-run))) - - (diff-options - (actions - ("s" "Set" magit-set-diff-options) - ("d" "Set default" magit-set-default-diff-options) - ("c" "Save default" magit-save-default-diff-options) - ("r" "Reset to default" magit-reset-diff-options) - ("h" "Toggle Hunk Refinement" magit-diff-toggle-refine-hunk)) - (switches - ("-m" "Show smallest possible diff" "--minimal") - ("-p" "Use patience diff algorithm" "--patience") - ("-h" "Use histogram diff algorithm" "--histogram") - ("-b" "Ignore whitespace changes" "--ignore-space-change") - ("-w" "Ignore all whitespace" "--ignore-all-space") - ("-W" "Show surrounding functions" "--function-context")) - )) - "Holds the key, help, function mapping for the log-mode. -If you modify this make sure you reset `magit-key-mode-keymaps' -to nil.") - -(defun magit-key-mode-delete-group (group) - "Delete a group from `magit-key-mode-keymaps'." - (let ((items (assoc group magit-key-mode-groups))) - (when items - ;; reset the cache - (setq magit-key-mode-keymaps 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-keymaps'. -If there already is 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) (list 'switches) (list 'arguments)) - magit-key-mode-groups))) - -(defun magit-key-mode-key-defined-p (for-group key) - "Return t if KEY is defined as any option within FOR-GROUP. -The option may be a switch, argument or action." - (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-keymaps'." - (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-keymaps 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 for the group FOR-GROUP. -This includes switches, commands and arguments." - (or (cdr (assoc for-group magit-key-mode-groups)) - (error "Unknown group '%s'" for-group))) - -;;; Commands - -(defun magit-key-mode-help (for-group) - "Provide help for a key within FOR-GROUP. -The user is prompted for the key." - (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.")))) - (call-interactively (lookup-key (current-local-map) key)))) - -(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 " "))) - -;;; Keymaps - -(defvar magit-key-mode-keymaps nil - "This will be filled lazily with proper keymaps. -These keymaps are created using `define-key' as they're requested.") - -(defun magit-key-mode-build-keymap (for-group) - "Construct a normal looking keymap for the key mode to use. -Put it in `magit-key-mode-keymaps' 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))) - - (let ((defkey (lambda (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))))) - (dolist (k actions) - (funcall defkey k `(magit-key-mode-command ',(nth 2 k)))) - (dolist (k switches) - (funcall defkey k `(magit-key-mode-toggle-option ',for-group ,(nth 2 k)))) - (dolist (k arguments) - (funcall defkey k `(magit-key-mode-add-argument - ',for-group ,(nth 2 k) ',(nth 3 k))))) - - (push (cons for-group map) magit-key-mode-keymaps) - map)) - -;;; Toggling and Running - -(defvar magit-key-mode-prefix nil - "Prefix argument to the command that brought up the key-mode window. -For internal use. Used by the command that's eventually invoked.") - -(defvar magit-key-mode-current-args nil - "A hash-table of current argument set. -These will eventually make it to the git command-line.") - -(defvar magit-key-mode-current-options nil - "Current option set. -These will eventually make it to the git command-line.") - -(defvar magit-custom-options nil - "List of custom options to pass to Git. -Do not customize this (used in the `magit-key-mode' implementation).") - -(defun magit-key-mode-command (func) - (let ((current-prefix-arg (or current-prefix-arg magit-key-mode-prefix)) - (magit-custom-options magit-key-mode-current-options)) - (maphash (lambda (k v) - (push (concat k v) magit-custom-options)) - magit-key-mode-current-args) - (set-window-configuration magit-pre-key-mode-window-conf) - (kill-buffer magit-key-mode-last-buffer) - (when func - (setq this-command func) - (call-interactively this-command)))) - -(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))) - -(defun magit-key-mode-toggle-option (for-group option-name) - "Toggles the appearance of OPTION-NAME in `magit-key-mode-current-options'." - (if (member option-name magit-key-mode-current-options) - (setq magit-key-mode-current-options - (delete option-name magit-key-mode-current-options)) - (add-to-list 'magit-key-mode-current-options option-name)) - (magit-key-mode-redraw for-group)) - -;;; Mode - -(defvar magit-key-mode-buf-name "*magit-key: %s*" - "Format string to create the name of the magit-key buffer.") - -(defvar magit-key-mode-last-buffer nil - "Store the last magit-key buffer used.") - -(defvar magit-pre-key-mode-window-conf nil - "Will hold the pre-menu configuration of magit.") - -(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-pre-key-mode-window-conf - (current-window-configuration)) - ;; setup the mode, draw the buffer - (let ((buf (get-buffer-create (format magit-key-mode-buf-name - (symbol-name for-group))))) - (setq magit-key-mode-last-buffer buf) - (split-window-vertically) - (other-window 1) - (switch-to-buffer buf) - (kill-all-local-variables) - (set (make-local-variable 'scroll-margin) 0) - (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)) - (when magit-key-mode-show-usage - (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-keymaps)) - (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) - (current-exec (get-text-property (point) 'key-group-executor)) - (new-exec-pos) - (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) - (when current-exec - (setq new-exec-pos - (cdr (assoc current-exec - (magit-key-mode-build-exec-point-alist))))) - (cond ((and is-first actions-p) - (goto-char actions-p) - (magit-key-mode-jump-to-next-exec)) - (new-exec-pos - (goto-char new-exec-pos) - (skip-chars-forward " ")) - (t - (goto-char old-point)))) - (setq buffer-read-only t) - (fit-window-to-buffer)) - -(defun magit-key-mode-build-exec-point-alist () - (save-excursion - (goto-char (point-min)) - (let* ((exec (get-text-property (point) 'key-group-executor)) - (exec-alist (and exec `((,exec . ,(point)))))) - (cl-do nil ((eobp) (nreverse exec-alist)) - (when (not (eq exec (get-text-property (point) 'key-group-executor))) - (setq exec (get-text-property (point) 'key-group-executor)) - (when exec (push (cons exec (point)) exec-alist))) - (forward-char))))) - -;;; Draw Buffer - -(defun magit-key-mode-draw-header (header) - "Draw a header with the correct face." - (insert (propertize header 'face 'magit-key-mode-header-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 'magit-key-mode-args-face))) - (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 'magit-key-mode-switch-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 'magit-key-mode-button-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) - "Draw actions, switches and parameters. -Return the point before the actions part, if any, nil otherwise." - (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)) - -;;; Generate Groups - -(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) - ;; As a tempory kludge it is okay to do this here. - ,(cl-case group - (logging - '(list "--graph")) - (diff-options - '(when (local-variable-p 'magit-diff-options) - magit-diff-options)))))))) - -;; 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) - -;;;###autoload (mapc (lambda (g) (eval `(autoload ',(intern (concat "magit-key-mode-popup-" (symbol-name (car g)))) "magit-key-mode" ,(concat "Key menu for " (symbol-name (car g))) t))) magit-key-mode-groups) - -(provide 'magit-key-mode) -;; Local Variables: -;; indent-tabs-mode: nil -;; End: -;;; magit-key-mode.el ends here diff --git a/elpa/magit-1.4.1/magit-pkg.el b/elpa/magit-1.4.1/magit-pkg.el deleted file mode 100644 index 47ed9ff..0000000 --- a/elpa/magit-1.4.1/magit-pkg.el +++ /dev/null @@ -1,5 +0,0 @@ -(define-package "magit" "1.4.1" - "Control Git from Emacs." - '((cl-lib "0.5") - (git-commit-mode "1.0.0") - (git-rebase-mode "1.0.0"))) diff --git a/elpa/magit-1.4.1/magit-wip.el b/elpa/magit-1.4.1/magit-wip.el deleted file mode 100644 index ccb71b1..0000000 --- a/elpa/magit-1.4.1/magit-wip.el +++ /dev/null @@ -1,143 +0,0 @@ -;;; magit-wip.el --- git-wip plug-in for Magit - -;; Copyright (C) 2012-2015 The Magit Project Developers -;; -;; For a full list of contributors, see the AUTHORS.md file -;; at the top-level directory of this distribution and at -;; https://raw.github.com/magit/magit/master/AUTHORS.md - -;; Author: Jonas Bernoulli -;; Keywords: vc tools -;; Package: magit - -;; 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 . - -;;; 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. - -;; 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. -;; -;; (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) - -(defun magit-wip-mode (&rest ignore) - (message "magit-wip-mode is obsolete and doesn't do anything")) -(make-obsolete 'magit-wip-mode "This mode is a noop now" "1.4.0") - -;;; Options - -(defgroup magit-wip nil - "Git-Wip support for Magit." - :group 'magit-extensions) - -(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 -%g the root of the git repository -%r the name of the file being saved, - relative to the repository root." - :group 'magit-wip - :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 -%g the root of the git repository -%r the name of the file being saved, - relative to the repository root." - :group 'magit-wip - :type '(choice (const :tag "No message" nil) string)) - -(defvar magit-wip-save-mode-lighter " Wip") - -;;; Mode - -;;;###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 t t) - (remove-hook 'after-save-hook 'magit-wip-save t))) - -;;;###autoload -(define-globalized-minor-mode global-magit-wip-save-mode - magit-wip-save-mode turn-on-magit-wip-save - :group 'magit-wip) - -(defun turn-on-magit-wip-save () - "Conditionally turn on magit-wip-save-mode. - -Turn on magit-wip-save-mode if the buffer is a file in a git -repository where wip-save is enabled in git config. - -You can activate it with git config magit.extension wip-save." - (when (and (buffer-file-name) - (magit-get-top-dir) - (magit-git-true "rev-parse" "--is-inside-work-tree") - (member "wip-save" (magit-get-all "magit.extension"))) - (if (magit-git-success "wip" "-h") - (magit-wip-save-mode 1) - (message "Git command 'git wip' cannot be found")))) - -(defun magit-wip-save () - (let* ((filename (expand-file-name (file-truename (buffer-file-name)))) - (filedir (file-name-directory filename)) - (toplevel (magit-get-top-dir filedir)) - (blobname (file-relative-name filename toplevel)) - (spec `((?f . ,filename) - (?r . ,blobname) - (?g . ,toplevel)))) - (when (and toplevel (file-writable-p toplevel) - (not (member blobname - (let ((default-directory filedir)) - (magit-git-lines - "ls-files" "--other" "--ignored" - "--exclude-standard" "--full-name"))))) - (magit-run-git "wip" "save" - (format-spec magit-wip-commit-message spec) - "--editor" "--" filename) - (when magit-wip-echo-area-message - (message (format-spec magit-wip-echo-area-message spec)))))) - -(provide 'magit-wip) -;; Local Variables: -;; indent-tabs-mode: nil -;; End: -;;; magit-wip.el ends here diff --git a/elpa/magit-1.4.1/magit.el b/elpa/magit-1.4.1/magit.el deleted file mode 100644 index 769d402..0000000 --- a/elpa/magit-1.4.1/magit.el +++ /dev/null @@ -1,7838 +0,0 @@ -;;; magit.el --- control Git from Emacs - -;; Copyright (C) 2008-2015 The Magit Project Developers -;; -;; For a full list of contributors, see the AUTHORS.md file -;; at the top-level directory of this distribution and at -;; https://raw.github.com/magit/magit/master/AUTHORS.md - -;; Author: Marius Vollmer -;; Maintainer: Jonas Bernoulli -;; Former-Maintainers: -;; Nicolas Dudebout -;; Peter J. Weisberg -;; Phil Jackson -;; Rémi Vanicat -;; Yann Hodique - -;; Keywords: vc tools -;; Package: magit -;; Package-Requires: ((cl-lib "0.5") (git-commit-mode "1.0.0") (git-rebase-mode "1.0.0")) - -;; Magit requires at least GNU Emacs 23.2 and Git 1.7.2.5. -;; These are the versions shipped by Debian oldstable (6.0, Squeeze). - -;; Contains code from GNU Emacs , -;; released under the GNU General Public License version 3 or later. - -;; 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 . - -;;; Commentary: - -;; Invoking the magit-status function will show a buffer with the -;; status of the current git repository and its working tree. That -;; buffer offers key bindings for manipulating the status in simple -;; ways. -;; -;; The status buffer mainly shows the difference between the working -;; tree and the index, and the difference between the index and the -;; current HEAD. You can add individual hunks from the working tree -;; to the index, and you can commit the index. -;; -;; See the Magit User Manual for more information. - -;;; Code: - -(defvar magit-version 'undefined - "The version of Magit that you're using. -Use the function by the same name instead of this variable.") -;; The value is set at the end of this file, using the -;; function `magit-version' which is also defined there. - -;;;; Dependencies - -(when (version< emacs-version "23.2") - (error "Magit requires at least GNU Emacs 23.2")) - -(require 'git-commit-mode) -(require 'git-rebase-mode) - -(require 'ansi-color) -(require 'autorevert) -(require 'cl-lib) -(require 'diff-mode) -(require 'easymenu) -(require 'epa) -(require 'format-spec) -(require 'grep) -(require 'help-mode) -(require 'ring) -(require 'server) -(require 'tramp) -(require 'view) - -(eval-when-compile - (require 'dired) - (require 'dired-x) - (require 'ediff) - (require 'eshell) - (require 'ido) - (require 'package nil t) - (require 'view)) - -;;;; Declarations - -(if (featurep 'vc-git) - (defalias 'magit-grep 'vc-git-grep) - (defalias 'magit-grep 'lgrep)) - -(declare-function dired-jump 'dired-x) -(declare-function dired-uncache 'dired) -(declare-function ediff-cleanup-mess 'ediff) -(declare-function eshell-parse-arguments 'eshell) -(declare-function ido-completing-read 'ido) -(declare-function package-desc-vers 'package) -(declare-function package-desc-version 'package) -(declare-function package-version-join 'package) -(declare-function view-mode 'view) - -(defvar git-commit-previous-winconf) -(defvar magit-commit-buffer-name) -(defvar magit-custom-options) -(defvar magit-log-buffer-name) -(defvar magit-marked-commit) -(defvar magit-process-buffer-name) -(defvar magit-reflog-buffer-name) -(defvar magit-refresh-args) -(defvar magit-stash-buffer-name) -(defvar magit-status-buffer-name) -(defvar magit-this-process) -(defvar package-alist) - -;;;; Compatibility - -(eval-and-compile - - ;; Added in Emacs 24.3 - (unless (fboundp 'user-error) - (defalias 'user-error 'error)) - - ;; Added in Emacs 24.3 (mirrors/emacs@b335efc3). - (unless (fboundp 'setq-local) - (defmacro setq-local (var val) - "Set variable VAR to value VAL in current buffer." - (list 'set (list 'make-local-variable (list 'quote var)) val))) - - ;; Added in Emacs 24.3 (mirrors/emacs@b335efc3). - (unless (fboundp 'defvar-local) - (defmacro defvar-local (var val &optional docstring) - "Define VAR as a buffer-local variable with default value VAL. -Like `defvar' but additionally marks the variable as being automatically -buffer-local wherever it is set." - (declare (debug defvar) (doc-string 3)) - (list 'progn (list 'defvar var val docstring) - (list 'make-variable-buffer-local (list 'quote var))))) - - ;; Added in Emacs 24.1 - (unless (fboundp 'run-hook-wrapped) - (defun run-hook-wrapped-1 (hook fns wrap-function &rest args) - (cl-loop for fn in fns - if (and (eq fn t) - (local-variable-p hook) - (default-boundp hook) - (apply 'run-hook-wrapped-1 nil - (default-value hook) wrap-function args)) - return it - else if (and (functionp fn) (apply wrap-function fn args)) - return it)) - - (defun run-hook-wrapped (hook wrap-function &rest args) - "Run HOOK, passing each function through WRAP-FUNCTION. -I.e. instead of calling each function FUN directly with arguments ARGS, -it calls WRAP-FUNCTION with arguments FUN and ARGS. -As soon as a call to WRAP-FUNCTION returns non-nil, `run-hook-wrapped' -aborts and returns that value." - (when (boundp hook) - (let ((fns (symbol-value hook))) - (apply 'run-hook-wrapped-1 hook - (if (functionp fns) (list fns) fns) - wrap-function args))))) - ) - - -;;; Settings -;;;; Custom Groups - -(defgroup magit nil - "Controlling Git from Emacs." - :group 'tools) - -(defgroup magit-process nil - "Git and other external processes used by Magit." - :group 'magit) - -(defgroup magit-modes nil - "Modes provided by Magit." - :group 'magit) - -(defgroup magit-status nil - "Inspect and manipulate Git repositories." - :group 'magit-modes) - -(defgroup magit-diff nil - "Inspect and manipulate Git diffs." - :group 'magit-modes) - -(defgroup magit-commit nil - "Inspect and manipulate Git commits." - :group 'magit-modes) - -(defgroup magit-log nil - "Inspect and manipulate Git history." - :group 'magit-modes) - -(defgroup magit-extensions nil - "Extensions to Magit." - :group 'magit) - -(defgroup magit-faces nil - "Faces used by Magit." - :group 'magit - :group 'faces) - -(when (featurep 'gitattributes-mode) - (custom-add-to-group 'magit-modes 'gitattributes-mode 'custom-group)) - -(when (featurep 'git-commit-mode) - (custom-add-to-group 'magit-modes 'git-commit 'custom-group) - (custom-add-to-group 'magit-faces 'git-commit-faces 'custom-group)) - -(when (featurep 'git-rebase-mode) - (custom-add-to-group 'magit-modes 'git-rebase 'custom-group) - (custom-add-to-group 'magit-faces 'git-rebase-faces 'custom-group)) - -(custom-add-to-group 'magit 'vc-follow-symlinks 'custom-variable) - -;;;; Custom Options -;;;;; Processes - -(defcustom magit-git-executable - (or (and (eq system-type 'windows-nt) - ;; On Windows asking for "git" from $PATH might also return - ;; a "git.exe" or "git.cmd". Using "bin/git.exe" directly - ;; is faster than using one of the wrappers "cmd/git.exe" - ;; or "cmd/git.cmd". The wrappers are likely to come - ;; earlier on $PATH, and so we have to exlicitly use - ;; the former. - (let ((exe (executable-find "git.exe"))) - (when exe - (let ((alt (directory-file-name (file-name-directory exe)))) - (if (and (equal (file-name-nondirectory alt) "cmd") - (setq alt (expand-file-name - (convert-standard-filename "bin/git.exe") - (file-name-directory alt))) - (file-executable-p alt)) - alt - exe))))) - ;; When the only cost is finding the executable, then it it - ;; better not to cache the full path. It might not be installed - ;; in the same location on machines whose repositories are - ;; accessed using Tramp. - "git") - "The Git executable used by Magit." - :group 'magit-process - :type 'string) - -(defcustom magit-git-standard-options - '("--no-pager" "-c" "core.preloadindex=true") - "Standard options when running Git. -Be careful what you add here, especially if you are using -tramp to connect to servers with ancient Git versions." - :group 'magit-process - :type '(repeat string)) - -(defcustom magit-gitk-executable - (or (and (eq system-type 'windows-nt) - (let ((exe (expand-file-name - "gitk" (file-name-nondirectory magit-git-executable)))) - (and (file-executable-p exe) exe))) - (executable-find "gitk") "gitk") - "The Gitk executable." - :group 'magit-process - :set-after '(magit-git-executable) - :type 'string) - -(defun magit-locate-emacsclient () - "Search for a suitable Emacsclient executable." - (let ((path (cl-copy-list exec-path)) fixup client) - (when invocation-directory - (setq path (cons (directory-file-name invocation-directory) path)) - (when (eq system-type 'darwin) - (setq fixup (expand-file-name "bin" invocation-directory)) - (when (file-directory-p fixup) - (push fixup path)) - (when (string-match "^Emacs-\\(powerpc\\|i386\\|x86_64\\)-\\(.+\\)" - invocation-name) - (setq fixup (expand-file-name - (format "bin-%s-%s" - (match-string 1 invocation-name) - (match-string 2 invocation-name)) - invocation-directory)) - (when (file-directory-p fixup) - (push fixup path))) - (when (string-match-p "Cellar" invocation-directory) - (setq fixup (expand-file-name "../../../bin" invocation-directory)) - (when (file-directory-p fixup) - (push fixup path)))) - (setq path (delete-dups path))) - (setq client (magit-locate-emacsclient-1 path 3)) - (if client - (shell-quote-argument client) - (display-warning 'magit (format "\ -Cannot determine a suitable Emacsclient - -Determining an Emacsclient executable suitable for the -current Emacs instance failed. For more information -please see https://github.com/magit/magit/wiki/Emacsclient.")) - nil))) - -(defun magit-take (n l) ; until we get to use `-take' from dash - (let (r) (dotimes (_ n) (and l (push (pop l) r))) (nreverse r))) - -(defun magit-locate-emacsclient-1 (path depth) - (let* ((version-lst (magit-take depth (split-string emacs-version "\\."))) - (version-reg (concat "^" (mapconcat #'identity version-lst "\\.")))) - (or (locate-file-internal - "emacsclient" path - (cl-mapcan - (lambda (v) (cl-mapcar (lambda (e) (concat v e)) exec-suffixes)) - (nconc (cl-mapcon (lambda (v) - (setq v (mapconcat #'identity (reverse v) ".")) - (list v (concat "-" v))) - (reverse version-lst)) - (list ""))) - (lambda (exec) - (ignore-errors - (string-match-p version-reg (magit-emacsclient-version exec))))) - (and (> depth 1) - (magit-locate-emacsclient-1 path (1- depth)))))) - -(defun magit-emacsclient-version (exec) - (cadr (split-string (car (process-lines exec "--version"))))) - -(defcustom magit-emacsclient-executable (magit-locate-emacsclient) - "The Emacsclient executable. -If the default is nil, or commiting or rebasing is somehow broken, -please see https://github.com/magit/magit/wiki/Emacsclient." - :package-version '(magit . "1.4.0") - :group 'magit-process - :type '(choice (string :tag "Executable") - (const :tag "Don't use Emacsclient" nil))) - -(defcustom magit-process-connection-type (not (eq system-type 'cygwin)) - "Connection type used for the git process. - -If nil, use pipes: this is usually more efficient, and works on Cygwin. -If t, use ptys: this enables magit to prompt for passphrases when needed." - :group 'magit-process - :type '(choice (const :tag "pipe" nil) - (const :tag "pty" t))) - -(defcustom magit-process-popup-time -1 - "Popup the process buffer if a command takes longer than this many seconds." - :group 'magit-process - :type '(choice (const :tag "Never" -1) - (const :tag "Immediately" 0) - (integer :tag "After this many seconds"))) - -(defcustom magit-process-log-max 32 - "Maximum number of sections to keep in a process log buffer. -When adding a new section would go beyond the limit set here, -then the older half of the sections are remove. Sections that -belong to processes that are still running are never removed." - :package-version '(magit . "1.4.0") - :group 'magit-process - :type 'integer) - -(defcustom magit-process-quote-curly-braces - (and (eq system-type 'windows-nt) - (let ((case-fold-search t)) - (string-match-p "cygwin" magit-git-executable)) - t) - "Whether curly braces should be quoted when calling git. -This may be necessary when using Windows. On all other system -types this must always be nil. - -We are not certain when quoting is needed, but it appears it is -needed when using Cygwin Git but not when using stand-alone Git. -The default value is set based on that assumptions. If this -turns out to be wrong you can customize this option but please -also comment on issue #816." - :package-version '(magit . "1.4.0") - :group 'magit-process - :set-after '(magit-git-executable) - :type 'boolean) - -(defcustom magit-process-yes-or-no-prompt-regexp - " [\[(]\\([Yy]\\(?:es\\)?\\)[/|]\\([Nn]o?\\)[\])] ?[?:] ?$" - "Regexp matching Yes-or-No prompts of git and its subprocesses." - :package-version '(magit . "1.4.0") - :group 'magit-process - :type 'regexp) - -(defcustom magit-process-password-prompt-regexps - '("^\\(Enter \\)?[Pp]assphrase\\( for \\(RSA \\)?key '.*'\\)?: ?$" - "^\\(Enter \\)?[Pp]assword\\( for '.*'\\)?: ?$" - "^.*'s password: ?$" - "^Yubikey for .*: ?$") - "List of regexps matching password prompts of git and its subprocesses." - :package-version '(magit . "1.4.0") - :group 'magit-process - :type '(repeat (regexp))) - -(defcustom magit-process-username-prompt-regexps - '("^Username for '.*': ?$") - "List of regexps matching username prompts of git and its subprocesses." - :package-version '(magit . "1.4.0") - :group 'magit-process - :type '(repeat (regexp))) - -(defconst magit-server-window-type - '(choice - (const :tag "Use selected window" - :match (lambda (widget value) - (not (functionp value))) - nil) - (function-item :tag "Display in new frame" switch-to-buffer-other-frame) - (function-item :tag "Use pop-to-buffer" pop-to-buffer) - (function :tag "Other function"))) - -(defcustom magit-server-window-for-commit 'pop-to-buffer - "Function used to select a window for displaying commit message buffers. -It should take one argument (a buffer) and display and select it. -A common value is `pop-to-buffer'. It can also be nil in which -case the selected window is used." - :package-version '(magit . "1.4.0") - :group 'magit-process - :type magit-server-window-type) - -(defcustom magit-server-window-for-rebase server-window - "Function used to select a window for displaying interactive rebase buffers. -It should take one argument (a buffer) and display and select it. -A common value is `pop-to-buffer'. It can also be nil in which -case the selected window is used." - :package-version '(magit . "1.4.0") - :group 'magit-process - :set-after '(server-window) - :type magit-server-window-type) - -;;;;; Staging - -(defcustom magit-stage-all-confirm t - "Whether to require confirmation before staging all changes. -This reduces the risk of accidentally losing the index. If -nothing at all is staged yet, then always stage without requiring -confirmation, because it can be undone without the risk of losing -a carefully crafted index." - :package-version '(magit . "1.4.0") - :group 'magit - :type 'boolean) - -(defcustom magit-unstage-all-confirm t - "Whether to require confirmation before unstaging all changes. -This reduces the risk of accidentally losing of the index. If -there are no staged changes at all, then always unstage without -confirmation, because it can be undone without the risk of losing -a carefully crafted index." - :package-version '(magit . "1.4.0") - :group 'magit - :type 'boolean) - -(defcustom magit-revert-item-confirm t - "Whether to require confirmation before reverting hunks. -If you disable this, consider enabling `magit-revert-backup' -instead." - :group 'magit - :type 'boolean) - -(defcustom magit-revert-backup nil - "Whether to backup a hunk before reverting it. -The hunk is stored in \".git/magit/reverted.diff\" and can be -applied using `magit-revert-undo'. Older hunks are available -in the same directory as numbered backup files and have to be -applied manually. Only individual hunks are backed up; when -a complete file is reverted (which requires confirmation) no -backup is created." - :package-version '(magit . "1.4.0") - :group 'magit - :type 'boolean) - -(defcustom magit-save-some-buffers t - "Whether certain commands save modified buffers before running. - -nil don't save buffers. -t ask which buffers to save. -`dontask' save all buffers without asking." - :group 'magit - :type '(choice (const :tag "Never" nil) - (const :tag "Ask" t) - (const :tag "Save without asking" dontask))) - -(defcustom magit-save-some-buffers-predicate - 'magit-save-buffers-predicate-tree-only - "A predicate function to decide whether to save a buffer. - -Used by function `magit-save-some-buffers' when the variable of -the same name is non-nil." - :group 'magit - :type '(radio (function-item magit-save-buffers-predicate-tree-only) - (function-item magit-save-buffers-predicate-all) - (function :tag "Other"))) - -(defcustom magit-rewrite-inclusive t - "Whether magit includes the selected base commit in a rewrite operation. - -t means both the selected commit as well as any subsequent -commits will be rewritten. This is magit's default behaviour, -equivalent to 'git rebase -i ${REV}~1' - - A'---B'---C'---D' - ^ - -nil means the selected commit will be literally used as 'base', -so only subsequent commits will be rewritten. This is consistent -with git-rebase, equivalent to 'git rebase -i ${REV}', yet more -cumbersome to use from the status buffer. - - A---B'---C'---D' - ^" - :group 'magit - :type '(choice (const :tag "Always" t) - (const :tag "Never" nil) - (const :tag "Ask" ask))) - -;;;;; Highlighting - -(defun magit-set-variable-and-refresh (symbol value) - "Set SYMBOL to VALUE and call `magit-refresh-all'." - (set-default symbol value) - ;; If magit isn't fully loaded yet no buffer that might - ;; need refreshing can exist and we can take a shortcut. - ;; We also don't want everything to repeatedly refresh - ;; when evaluating this file. - (when (and (featurep 'magit) (not buffer-file-name)) - (magit-refresh-all))) - -(defcustom magit-highlight-whitespace t - "Specify where to highlight whitespace errors. -See `magit-highlight-trailing-whitespace', -`magit-highlight-indentation'. The symbol t means in all diffs, -`status' means only in the status buffer, and nil means nowhere." - :group 'magit - :set 'magit-set-variable-and-refresh - :type '(choice (const :tag "Always" t) - (const :tag "Never" nil) - (const :tag "In status buffer" status))) - -(defcustom magit-highlight-trailing-whitespace t - "Whether to highlight whitespace at the end of a line in diffs. -Used only when `magit-highlight-whitespace' is non-nil." - :group 'magit - :set 'magit-set-variable-and-refresh - :type 'boolean) - -(defcustom magit-highlight-indentation nil - "Highlight the \"wrong\" indentation style. -Used only when `magit-highlight-whitespace' is non-nil. - -The value is a list of cons cells. The car is a regular -expression, and the cdr is the value that applies to repositories -whose directory matches the regular expression. If more than one -item matches, then the *last* item in the list applies. So, the -default value should come first in the list. - -If the value is `tabs', highlight indentation with tabs. If the -value is an integer, highlight indentation with at least that -many spaces. Otherwise, highlight neither." - :group 'magit - :set 'magit-set-variable-and-refresh - :type `(repeat (cons (string :tag "Directory regexp") - (choice (const :tag "Tabs" tabs) - (integer :tag "Spaces" :value ,tab-width) - (const :tag "Neither" nil))))) ;^FIXME - -(defcustom magit-item-highlight-face 'magit-item-highlight - "The face used to highlight the current section. - -By default the highlighting of the current section is done using -the background color specified by face `magit-item-highlight'. - -If you don't want to use the background to do the highlighting, -this *might* by as easy as customizing that face. However if you -are using a theme, which in turn sets the background color of -that face then, due to limitations in face inheritance when using -themes, you might be forced to use another face. - -Unfortunately it is only possible to override a face attribute, -set by a theme, but not to drop it entirely. This means that one -has to explicitly use the `default' background color, to make it -appear *as if* the background wasn't used. - -One reason you might want to *not* use the background, is that -doing so forces the use of overlays for parts of diffs and for -refnames. Using overlays potentially degrades performance when -generating large diffs. Also see option `magit-use-overlays'." - :package-version '(magit . "1.4.0") - :group 'magit - :group 'magit-faces - :type '(choice (const magit-item-highlight) - (const bold) - (face :tag "Other face") - (const :tag "Don't highlight" nil))) - -(defcustom magit-use-overlays - (not (eq magit-item-highlight-face 'bold)) - "Whether to use overlays to highlight various diff components. - -This has to be non-nil if the current section is highlighted by -changing the background color. Otherwise background colors that -hold semantic meaning, like that of the added and removed lines -in diffs, as well as section headings, would be shadowed by the -highlighting. - -To select the face used for highlighting customize the option -`magit-item-highlight-face'. If you set that to `bold' or some -other face that does not use the background then you can set this -option to nil. Doing so could potentially improve performance -when generating large diffs." - :package-version '(magit . "1.4.0") - :group 'magit - :group 'magit-faces - :set-after '(magit-item-highlight-face) - :type 'boolean) - -(define-obsolete-variable-alias 'magit-diff-use-overlays - 'magit-use-overlays "1.4.0") - -;;;;; Completion - -(defcustom magit-completing-read-function 'magit-builtin-completing-read - "Function to be called when requesting input from the user." - :group 'magit - :type '(radio (function-item magit-ido-completing-read) - (function-item magit-builtin-completing-read) - (function :tag "Other"))) - -(defcustom magit-remote-ref-format 'remote-slash-branch - "How to format refs when autocompleting, in particular for remotes. - -Autocompletion is used by functions like `magit-checkout', -`magit-interactive-rebase' and others which offer branch name -completion. - -`remote-slash-branch' Format refs as \"remote/branch\". -`branch-then-remote' Format refs as \"branch (remote)\"." - :package-version '(magit . "1.4.0") - :group 'magit - :type '(choice (const :tag "branch (remote)" branch-then-remote) - (const :tag "remote/branch" remote-slash-branch))) - -(defcustom magit-repo-dirs nil - "Directories containing Git repositories. -Magit will look into these directories for Git repositories and -offer them as choices for `magit-status'." - :group 'magit - :type '(repeat directory)) - -(defcustom magit-repo-dirs-depth 3 - "The maximum depth to look for Git repos. -When looking for a Git repository below the directories in -`magit-repo-dirs', Magit will only descend this many levels -deep." - :group 'magit - :type 'integer) - -;;;;; Modes -;;;;;; Common - -(defcustom magit-mode-hook nil - "Hook run when entering a Magit mode derived mode." - :options '(magit-load-config-extensions) - :group 'magit-modes - :type 'hook) - -(defcustom magit-show-xref-buttons '(magit-diff-mode magit-commit-mode) - "List of modes whose buffers should contain history buttons. -Currently only `magit-diff-mode' and `magit-commit-mode' are -supported." - :package-version '(magit . "1.4.0") - :group 'magit-modes - :type '(repeat (choice (const magit-diff-mode) - (const magit-commit-mode)))) - -(defcustom magit-show-child-count nil - "Whether to append the number of childen to section headings." - :package-version '(magit . "1.4.0") - :group 'magit-modes - :type 'boolean) - -(defvar magit-status-line-align-to 9) - -(defcustom magit-restore-window-configuration nil - "Whether quitting a Magit buffer restores previous window configuration. - -Function `magit-mode-display-buffer' is used to display and -select Magit buffers. Unless the buffer was already displayed in -a window of the selected frame it also stores the previous window -configuration. If this option is non-nil that configuration will -later be restored by `magit-mode-quit-window', provided the -buffer has not since been displayed in another frame. - -This works best when only two windows are usually displayed in a -frame. If this isn't the case setting this to t might often lead -to undesirable behaviour. Also quitting a Magit buffer while -another Magit buffer that was created earlier is still displayed -will cause that buffer to be hidden, which might or might not be -what you want." - :package-version '(magit . "1.4.0") - :group 'magit-modes - :type 'boolean) - -(defcustom magit-refs-namespaces - '(("^\\(HEAD\\)$" magit-log-head-label-head nil) - ("^refs/tags/\\(.+\\)" magit-log-head-label-tags nil) - ("^refs/heads/\\(.+\\)" magit-log-head-label-local nil) - ("^refs/remotes/\\(.+\\)" magit-log-head-label-remote nil) - ("^refs/bisect/\\(bad\\)" magit-log-head-label-bisect-bad nil) - ("^refs/bisect/\\(skip.*\\)" magit-log-head-label-bisect-skip nil) - ("^refs/bisect/\\(good.*\\)" magit-log-head-label-bisect-good nil) - ("^refs/wip/\\(.+\\)" magit-log-head-label-wip nil) - ("^refs/patches/\\(.+\\)" magit-log-head-label-patches nil) - ("^\\(bad\\):" magit-log-head-label-bisect-bad nil) - ("^\\(skip\\):" magit-log-head-label-bisect-skip nil) - ("^\\(good\\):" magit-log-head-label-bisect-good nil) - ("\\(.+\\)" magit-log-head-label-default nil)) - "How different refs should be formatted for display. - -Each entry controls how a certain type of ref is displayed, and -has the form (REGEXP FACE FORMATTER). REGEXP is a regular -expression used to match full refs. The first entry whose REGEXP -matches the reference is used. The first regexp submatch becomes -the \"label\" that represents the ref and is propertized with -font FONT. If FORMATTER is non-nil it should be a function that -takes two arguments, the full ref and the face. It is supposed -to return a propertized label that represents the ref. - -Currently this variable is only used in logs and the branch -manager but it will be used in more places in the future." - :package-version '(magit . "1.4.0") - :group 'magit-modes - :type '(repeat - (list regexp - face - (choice (const :tag "first submatch is label" nil) - (function :tag "format using function"))))) - -;;;;;; Status - -(defcustom magit-status-mode-hook nil - "Hook run when the `magit-status' buffer is created." - :group 'magit-status - :type 'hook) - -(defcustom magit-status-sections-hook - '(magit-insert-status-local-line - magit-insert-status-remote-line - magit-insert-status-head-line - magit-insert-status-tags-line - magit-insert-status-merge-line - magit-insert-status-rebase-lines - magit-insert-empty-line - magit-insert-rebase-sequence - magit-insert-bisect-output - magit-insert-bisect-rest - magit-insert-bisect-log - magit-insert-stashes - magit-insert-untracked-files - magit-insert-pending-commits - magit-insert-unstaged-changes - magit-insert-staged-changes - magit-insert-unpulled-commits - magit-insert-unpushed-commits) - "Hook run to insert sections into the status buffer. - -This option allows reordering the sections and adding sections -that are by default displayed in other Magit buffers. Doing the -latter is currently not recommended because not all functions -that insert sections have been adapted yet. Only inserters that -take no argument can be used and some functions exist that begin -with the `magit-insert-' prefix but do not insert a section. - -Note that there are already plans to improve this and to add -similar hooks for other Magit modes." - :package-version '(magit . "1.4.0") - :group 'magit-status - :type 'hook) - -(defcustom magit-status-buffer-switch-function 'pop-to-buffer - "Function for `magit-status' to use for switching to the status buffer. - -The function is given one argument, the status buffer." - :group 'magit-status - :type '(radio (function-item switch-to-buffer) - (function-item pop-to-buffer) - (function :tag "Other"))) - -(defcustom magit-status-show-sequence-help t - "Whether to show instructions on how to proceed a stopped action. -When this is non-nil and a commit failed to apply during a merge -or rebase, then show instructions on how to continue." - :package-version '(magit . "1.4.0") - :group 'magit-status - :type 'boolean) - -(defcustom magit-status-tags-line-subject 'head - "Whether tag or head is the subject on tags line in status buffer. - -This controls how the words \"ahead\" and \"behind\" are used on -the tags line in the status buffer. The tags line does not -actually display complete sentences, but when thinking about when -to use which term, it helps imagining it did. This option -controls whether the tag names should be considered the subjects -or objects in these sentences. - -`tag' The previous tag is *behind* HEAD by N commits. - The next tag is *ahead* of HEAD by N commits. -`head' HEAD is *ahead* of the previous tag by N commits. - HEAD is *behind* the next tag by N commits. - -If the value is `tag' the commit counts are fontified; otherwise -they are not (due to semantic considerations)." - :package-version '(magit . "1.4.0") - :group 'magit-status - :type '(choice (const :tag "tags are the subjects" tag) - (const :tag "head is the subject" head))) - -;;;;;; Diff - -(defun magit-set-default-diff-options (symbol value) - "Set the default for `magit-diff-options' based on popup value. -Also set the local value in all Magit buffers and refresh them. -\n(fn)" ; The arguments are an internal implementation detail. - (interactive (list 'magit-diff-options magit-custom-options)) - (set-default symbol value) - (when (and (featurep 'magit) (not buffer-file-name)) - (dolist (buffer (buffer-list)) - (when (derived-mode-p 'magit-mode) - (with-current-buffer buffer - (with-no-warnings - (setq-local magit-diff-options value)) - (magit-mode-refresh-buffer)))))) - -(defcustom magit-diff-options nil - "Git options used to display diffs. - -For more information about the options see man:git-diff. -This variable can be conveniently set in Magit buffers -using `magit-key-mode-popup-diff-options' (bound to \ -\\\\[magit-key-mode-popup-diff-options]). - -Please note that not all of these options are supported by older -versions of Git, which could become a problem if you use tramp to -access repositories on a system with such a version. If you see -whitespace where you would have expected a diff, this likely is -the cause, and the only (currently) workaround is to not make the -problematic option a member of the default value." - :package-version '(magit . "1.4.0") - :group 'magit-diff - :set 'magit-set-default-diff-options - :type '(set :greedy t - (const :tag - "--minimal Show smallest possible diff" - "--minimal") - (const :tag - "--patience Use patience diff algorithm" - "--patience") - (const :tag - "--histogram Use histogram diff algorithm" - "--histogram") - (const :tag - "--ignore-space-change Ignore whitespace changes" - "--ignore-space-change") - (const :tag - "--ignore-all-space Ignore all whitespace" - "--ignore-all-space") - (const :tag - "--function-context Show surrounding functions" - "--function-context"))) - -(put 'magit-diff-options 'permanent-local t) - -(defcustom magit-show-diffstat t - "Whether to show diffstat in diff and commit buffers." - :package-version '(magit . "1.4.0") - :group 'magit-diff - :group 'magit-commit - :type 'boolean) - -(defcustom magit-diff-refine-hunk nil - "Show fine (word-granularity) differences within diff hunks. - -There are three possible settings: - -nil never show fine differences -t show fine differences for the selected diff hunk only -`all' show fine differences for all displayed diff hunks" - :group 'magit-diff - :type '(choice (const :tag "Never" nil) - (const :tag "Selected only" t) - (const :tag "All" all)) - :set 'magit-set-variable-and-refresh) - -;;;;;; Commit - -(defcustom magit-commit-ask-to-stage t - "Whether to ask to stage everything when committing and nothing is staged." - :package-version '(magit . "1.4.0") - :group 'magit-commit - :type 'boolean) - -(defcustom magit-commit-extend-override-date nil - "Whether using `magit-commit-extend' changes the committer date." - :package-version '(magit . "1.4.0") - :group 'magit-commit - :type 'boolean) - -(defcustom magit-commit-reword-override-date nil - "Whether using `magit-commit-reword' changes the committer date." - :package-version '(magit . "1.4.0") - :group 'magit-commit - :type 'boolean) - -(defcustom magit-commit-squash-commit nil - "Whether to target the marked or current commit when squashing. - -When this is nil then the command `magit-commit-fixup' and -`magit-commit-squash' always require that the user explicitly -selects a commit. This is also the case when these commands are -used with a prefix argument, in which case this option is ignored. - -Otherwise this controls which commit to target, either the -current or marked commit. Or if both can be used, which should -be preferred." - :package-version '(magit . "1.4.0") - :group 'magit-commit - :type - '(choice - (const :tag "Always prompt" nil) - (const :tag "Prefer current commit, else use marked" current-or-marked) - (const :tag "Prefer marked commit, else use current" marked-or-current) - (const :tag "Use current commit, if any" current) - (const :tag "Use marked commit, if any" marked))) - -(defcustom magit-expand-staged-on-commit nil - "Whether to expand staged changes when creating a commit. -When this is non-nil and the current buffer is the status buffer -expand the section containing staged changes. If this is `full' -always expand all subsections; if it is t subsections that were -previously hidden remain hidden. - -In the event that expanding very large patches takes a long time -\\\\[keyboard-quit] can be used to abort that step. -This is especially useful when you would normally not look at the -changes, e.g. because you are committing some binary files." - :package-version '(magit . "1.4.0") - :group 'magit-commit - :type '(choice (const :tag "Expand all subsections" full) - (const :tag "Expand top section" t) - (const :tag "Don't expand" nil))) - -;;;;;; Log - -(defcustom magit-log-auto-more nil - "Insert more log entries automatically when moving past the last entry. - -Only considered when moving past the last entry with -`magit-goto-*-section' commands." - :group 'magit-log - :type 'boolean) - -(defcustom magit-log-cutoff-length 100 - "The maximum number of commits to show in the log and whazzup buffers." - :group 'magit-log - :type 'integer) - -(defcustom magit-log-infinite-length 99999 - "Number of log used to show as maximum for `magit-log-cutoff-length'." - :group 'magit-log - :type 'integer) - -(defcustom magit-log-format-graph-function nil - "Function used to format graphs in log buffers. -The function is called with one argument, the propertized graph -of a single line in as a string. It has to return the formatted -string. This option can also be nil, in which case the graph is -inserted as is." - :package-version '(magit . "1.4.0") - :group 'magit-log - :type '(choice (const :tag "insert as is" nil) - (function-item magit-log-format-unicode-graph) - function)) - -(defcustom magit-log-format-unicode-graph-alist - '((?/ . ?╱) (?| . ?│) (?\\ . ?╲) (?* . ?◆) (?o . ?◇)) - "Alist used by `magit-log-format-unicode-graph' to translate chars." - :package-version '(magit . "1.4.0") - :group 'magit-log - :type '(repeat (cons :format "%v\n" - (character :format "replace %v ") - (character :format "with %v")))) - -(defcustom magit-log-show-gpg-status nil - "Display signature verification information as part of the log." - :package-version '(magit . "1.4.0") - :group 'magit-log - :type 'boolean) - -(defcustom magit-log-show-margin t - "Whether to use a margin when showing `oneline' logs. -When non-nil the author name and date are displayed in the margin -of the log buffer if that contains a `oneline' log. This can be -toggled temporarily using the command `magit-log-toggle-margin'." - :package-version '(magit . "1.4.0") - :group 'magit-log - :type 'boolean) - -(put 'magit-log-show-margin 'permanent-local t) - -(defcustom magit-log-margin-spec '(25 nil magit-duration-spec) - "How to format the margin for `oneline' logs. - -When the log buffer contains a `oneline' log, then it optionally -uses the right margin to display the author name and author date. -This is also supported in the reflog buffer. - -Logs that are shown together with other non-log information (e.g. -in the status buffer) are never accompanied by a margin. The -same applies to `long' logs, in this case because that would be -redundant. - -This option controls how that margin is formatted, the other -option affecting this is `magit-log-show-margin'; if that is nil -then no margin is displayed at all. To toggle this temporarily -use the command `magit-log-show-margin'. - -The value has the form (WIDTH CHARACTERP DURATION-SPEC). The -width of the margin is controlled using WIDTH, an integer. When -CHARACTERP is non-nil time units are shown as single characters, -otherwise the full name of the unit is displayed. DURATION-SPEC -has to be a variable, its value controls which time units are -used, how many seconds they contain, and what their names are." - :package-version '(magit . "1.4.0") - :group 'magit-log - :type '(list (integer :tag "Margin width") - (choice :tag "Time unit style" - (const :tag "Character" t) - (const :tag "Word" nil)) - (variable :tag "Duration spec variable"))) - -(defcustom magit-duration-spec - `((?Y "year" "years" ,(round (* 60 60 24 365.2425))) - (?M "month" "months" ,(round (* 60 60 24 30.436875))) - (?w "week" "weeks" ,(* 60 60 24 7)) - (?d "day" "days" ,(* 60 60 24)) - (?h "hour" "hours" ,(* 60 60)) - (?m "minute" "minutes" 60) - (?s "second" "seconds" 1)) - "Units used to display durations in a human format. -The value is a list of time units, beginning with the longest. -Each element has the form ((CHAR UNIT UNITS SECONDS)..). UNIT -is the time unit, UNITS is the plural of that unit. CHAR is a -character that can be used as abbreviation and must be unique -amoung all elements. SECONDS is the number of seconds in one -UNIT. Also see option `magit-log-margin-spec'." - :package-version '(magit . "1.4.0") - :group 'magit-log - :type '(repeat (list (character :tag "Unit character") - (string :tag "Unit singular string") - (string :tag "Unit plural string") - (integer :tag "Seconds in unit")))) - -(defcustom magit-ellipsis #x2026 ; "horizontal ellipsis" - "Character appended to abreviated text. -Currently this is used only in the log margin, but might later -be used elsewhere too. Filenames that were abbreviated by Git -are left as-is." - :package-version '(magit . "1.4.0") - :group 'magit-log - :type 'character) - -;;;;;; Others - -(defcustom magit-auto-revert-mode-lighter " MRev" - "String to display when Magit-Auto-Revert mode is active." - :group 'magit-modes) - -(define-minor-mode magit-auto-revert-mode - "Toggle global Magit-Auto-Revert mode. -With prefix ARG, enable Magit-Auto-Revert mode if ARG is positive; -otherwise, disable it. If called from Lisp, enable the mode if -ARG is omitted or nil. - -Magit-Auto-Revert mode is a global minor mode that, after Magit -has run a Git command, reverts buffers associated with files that -have changed on disk and are tracked in the current Git repository." - :group 'magit-modes - :lighter magit-auto-revert-mode-lighter - :global t - :init-value t) - -(defcustom magit-merge-warn-dirty-worktree t - "Whether to issue a warning when attempting to start a merge in a dirty worktree." - :package-version '(magit . "1.4.0") - :group 'magit-modes - :type 'boolean) - -(defcustom magit-push-hook '(magit-push-dwim) - "Hook run by `magit-push' to actually do the work. -See `magit-push' and `magit-push-dwim' for more information." - :package-version '(magit . "1.4.0") - :group 'magit-modes - :type 'hook) - -(defcustom magit-set-upstream-on-push nil - "Whether `magit-push' may set upstream when pushing a branch. -This only applies if the branch does not have an upstream set yet. - -nil don't use --set-upstream. -t ask if --set-upstream should be used. -`dontask' always use --set-upstream. -`refuse' refuse to push unless a remote branch has already been set." - :group 'magit-modes - :type '(choice (const :tag "Never" nil) - (const :tag "Ask" t) - (const :tag "Ask if not set" askifnotset) - (const :tag "Refuse" refuse) - (const :tag "Always" dontask))) - -(defcustom magit-wazzup-sections-hook - '(magit-insert-wazzup-head-line - magit-insert-empty-line - magit-insert-wazzup-branches) - "Hook run to insert sections into the wazzup buffer." - :package-version '(magit . "1.4.0") - :group 'magit-modes - :type 'hook) - -(defcustom magit-cherry-sections-hook - '(magit-insert-cherry-head-line - magit-insert-cherry-upstream-line - magit-insert-cherry-help-lines - magit-insert-empty-line - magit-insert-cherry-commits) - "Hook run to insert sections into the cherry buffer." - :package-version '(magit . "1.4.0") - :group 'magit-modes - :type 'hook) - -;;;; Custom Faces - -(defface magit-section-title - '((t :inherit header-line)) - "Face for section titles." - :group 'magit-faces) - -(defface magit-branch - '((((class color) (background light)) - :background "Grey85" - :foreground "LightSkyBlue4") - (((class color) (background dark)) - :background "Grey13" - :foreground "LightSkyBlue1")) - "Face for branches." - :group 'magit-faces) - -(defface magit-tag - '((((class color) (background light)) - :background "LemonChiffon1" - :foreground "goldenrod4") - (((class color) (background dark)) - :background "LemonChiffon1" - :foreground "goldenrod4")) - "Face for tags." - :group 'magit-faces) - -(defface magit-diff-file-header - '((t :inherit diff-file-header)) - "Face for diff file header lines." - :group 'magit-faces) - -(defface magit-diff-hunk-header - '((t :inherit diff-hunk-header)) - "Face for diff hunk header lines." - :group 'magit-faces) - -(defface magit-diff-add - '((t :inherit diff-added)) - "Face for lines in a diff that have been added." - :group 'magit-faces) - -(defface magit-diff-del - '((t :inherit diff-removed)) - "Face for lines in a diff that have been deleted." - :group 'magit-faces) - -(defface magit-diff-none - '((t :inherit diff-context)) - "Face for lines in a diff that are unchanged." - :group 'magit-faces) - -(defface magit-diff-merge-current - '((t :inherit font-lock-preprocessor-face)) - "Face for merge conflict marker 'current' line." - :group 'magit-faces) - -(defface magit-diff-merge-separator - '((t :inherit font-lock-preprocessor-face)) - "Face for merge conflict marker seperator." - :group 'magit-faces) - -(defface magit-diff-merge-diff3-separator - '((t :inherit font-lock-preprocessor-face)) - "Face for merge conflict marker seperator." - :group 'magit-faces) - -(defface magit-diff-merge-proposed - '((t :inherit font-lock-preprocessor-face)) - "Face for merge conflict marker 'proposed' line." - :group 'magit-faces) - -(defface magit-log-graph - '((((class color) (background light)) - :foreground "grey11") - (((class color) (background dark)) - :foreground "grey80")) - "Face for the graph element of the log output." - :group 'magit-faces) - -(defface magit-log-sha1 - '((((class color) (background light)) - :foreground "firebrick") - (((class color) (background dark)) - :foreground "tomato")) - "Face for the sha1 element of the log output." - :group 'magit-faces) - -(defface magit-log-author - '((((class color) (background light)) - :foreground "firebrick") - (((class color) (background dark)) - :foreground "tomato")) - "Face for the author element of the log output." - :group 'magit-faces) - -(defface magit-log-date - '((t)) - "Face for the date element of the log output." - :group 'magit-faces) - -(defface magit-log-message - '((t)) - "Face for the message element of the log output." - :group 'magit-faces) - -(defface magit-cherry-unmatched - '((t :foreground "cyan")) - "Face for unmatched cherry commits.") - -(defface magit-cherry-equivalent - '((t :foreground "magenta")) - "Face for equivalent cherry commits.") - -(defface magit-item-highlight - '((t :inherit secondary-selection)) - "Face for highlighting the current item." - :group 'magit-faces) - -(defface magit-item-mark - '((t :inherit highlight)) - "Face for highlighting marked item." - :group 'magit-faces) - -(defface magit-log-head-label-bisect-good - '((((class color) (background light)) - :box t - :background "light green" - :foreground "dark olive green") - (((class color) (background dark)) - :box t - :background "light green" - :foreground "dark olive green")) - "Face for good bisect refs." - :group 'magit-faces) - -(defface magit-log-head-label-bisect-skip - '((((class color) (background light)) - :box t - :background "light goldenrod" - :foreground "dark goldenrod") - (((class color) (background dark)) - :box t - :background "light goldenrod" - :foreground "dark goldenrod")) - "Face for skipped bisect refs." - :group 'magit-faces) - -(defface magit-log-head-label-bisect-bad - '((((class color) (background light)) - :box t - :background "IndianRed1" - :foreground "IndianRed4") - (((class color) (background dark)) - :box t - :background "IndianRed1" - :foreground "IndianRed4")) - "Face for bad bisect refs." - :group 'magit-faces) - -(defface magit-log-head-label-remote - '((((class color) (background light)) - :box t - :background "Grey85" - :foreground "OliveDrab4") - (((class color) (background dark)) - :box t - :background "Grey11" - :foreground "DarkSeaGreen2")) - "Face for remote branch head labels shown in log buffer." - :group 'magit-faces) - -(defface magit-log-head-label-tags - '((((class color) (background light)) - :box t - :background "LemonChiffon1" - :foreground "goldenrod4") - (((class color) (background dark)) - :box t - :background "LemonChiffon1" - :foreground "goldenrod4")) - "Face for tag labels shown in log buffer." - :group 'magit-faces) - -(defface magit-log-head-label-patches - '((((class color) (background light)) - :box t - :background "IndianRed1" - :foreground "IndianRed4") - (((class color) (background dark)) - :box t - :background "IndianRed1" - :foreground "IndianRed4")) - "Face for Stacked Git patches." - :group 'magit-faces) - -(defface magit-whitespace-warning-face - '((t :inherit trailing-whitespace)) - "Face for highlighting whitespace errors in Magit diffs." - :group 'magit-faces) - -(defface magit-log-head-label-local - '((((class color) (background light)) - :box t - :background "Grey85" - :foreground "LightSkyBlue4") - (((class color) (background dark)) - :box t - :background "Grey13" - :foreground "LightSkyBlue1")) - "Face for local branch head labels shown in log buffer." - :group 'magit-faces) - -(defface magit-log-head-label-head - '((((class color) (background light)) - :box t - :background "Grey70" - :foreground "Black") - (((class color) (background dark)) - :box t - :background "Grey20" - :foreground "White")) - "Face for working branch head labels shown in log buffer." - :group 'magit-faces) - -(defface magit-log-head-label-default - '((((class color) (background light)) - :box t - :background "Grey50") - (((class color) (background dark)) - :box t - :background "Grey50")) - "Face for unknown ref labels shown in log buffer." - :group 'magit-faces) - -(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) - -(defface magit-signature-good - '((t :foreground "green")) - "Face for good signatures." - :group 'magit-faces) - -(defface magit-signature-bad - '((t :foreground "red")) - "Face for bad signatures." - :group 'magit-faces) - -(defface magit-signature-untrusted - '((t :foreground "cyan")) - "Face for good untrusted signatures." - :group 'magit-faces) - -(defface magit-signature-none - '((t :inherit magit-log-message)) - "Face for unsigned commits." - :group 'magit-faces) - - -(defface magit-log-reflog-label-commit - '((((class color) (background light)) - :box t - :background "LemonChiffon1" - :foreground "goldenrod4") - (((class color) (background dark)) - :box t - :background "LemonChiffon1" - :foreground "goldenrod4")) - "Face for reflog subject labels shown in reflog buffer." - :group 'magit-faces) - -(defface magit-log-reflog-label-amend - '((t :inherit magit-log-reflog-label-commit)) - "Face for reflog subject labels shown in reflog buffer." - :group 'magit-faces) - -(defface magit-log-reflog-label-merge - '((t :inherit magit-log-reflog-label-commit)) - "Face for reflog subject labels shown in reflog buffer." - :group 'magit-faces) - -(defface magit-log-reflog-label-checkout - '((((class color) (background light)) - :box t - :background "Grey85" - :foreground "LightSkyBlue4") - (((class color) (background dark)) - :box t - :background "Grey13" - :foreground "LightSkyBlue1")) - "Face for reflog subject labels shown in reflog buffer." - :group 'magit-faces) - -(defface magit-log-reflog-label-reset - '((((class color) (background light)) - :box t - :background "IndianRed1" - :foreground "IndianRed4") - (((class color) (background dark)) - :box t - :background "IndianRed1" - :foreground "IndianRed4")) - "Face for reflog subject labels shown in reflog buffer." - :group 'magit-faces) - -(defface magit-log-reflog-label-rebase - '((((class color) (background light)) - :box t - :background "Grey85" - :foreground "OliveDrab4") - (((class color) (background dark)) - :box t - :background "Grey11" - :foreground "DarkSeaGreen2")) - "Face for reflog subject labels shown in reflog buffer." - :group 'magit-faces) - -(defface magit-log-reflog-label-cherry-pick -'((((class color) (background light)) - :box t - :background "light green" - :foreground "dark olive green") - (((class color) (background dark)) - :box t - :background "light green" - :foreground "dark olive green")) - "Face for reflog subject labels shown in reflog buffer." - :group 'magit-faces) - -(defface magit-log-reflog-label-remote - '((((class color) (background light)) - :box t - :background "Grey50") - (((class color) (background dark)) - :box t - :background "Grey50")) - "Face for reflog subject labels shown in reflog buffer." - :group 'magit-faces) - -(defface magit-log-reflog-label-other - '((((class color) (background light)) - :box t - :background "Grey50") - (((class color) (background dark)) - :box t - :background "Grey50")) - "Face for reflog subject labels shown in reflog buffer." - :group 'magit-faces) - -(defface magit-process-ok - '((t :inherit magit-section-title - :foreground "green")) - "Face for zero exit-status." - :group 'magit-faces) - -(defface magit-process-ng - '((t :inherit magit-section-title - :foreground "red")) - "Face for non-zero exit-status." - :group 'magit-faces) - -;;;; Keymaps - -;; Not an option to avoid advertising it. -(defvar magit-rigid-key-bindings nil - "Use rigid key bindings instead of thematic key popups. -If you enable this a lot of functionality is lost. You most -likely don't want that. This variable only has an effect if -set before loading libary `magit'.") - -(when (boundp 'git-commit-mode-map) - (define-key git-commit-mode-map (kbd "C-c C-d") 'magit-diff-staged)) - -(defvar magit-mode-map - (let ((map (make-keymap))) - (suppress-keymap map t) - (define-key map (kbd "n") 'magit-goto-next-section) - (define-key map (kbd "p") 'magit-goto-previous-section) - (define-key map (kbd "^") 'magit-goto-parent-section) - (define-key map (kbd "M-n") 'magit-goto-next-sibling-section) - (define-key map (kbd "M-p") 'magit-goto-previous-sibling-section) - (define-key map (kbd "TAB") 'magit-toggle-section) - (define-key map (kbd "") 'magit-expand-collapse-section) - (define-key map (kbd "1") 'magit-show-level-1) - (define-key map (kbd "2") 'magit-show-level-2) - (define-key map (kbd "3") 'magit-show-level-3) - (define-key map (kbd "4") 'magit-show-level-4) - (define-key map (kbd "M-1") 'magit-show-level-1-all) - (define-key map (kbd "M-2") 'magit-show-level-2-all) - (define-key map (kbd "M-3") 'magit-show-level-3-all) - (define-key map (kbd "M-4") 'magit-show-level-4-all) - (define-key map (kbd "M-h") 'magit-show-only-files) - (define-key map (kbd "M-H") 'magit-show-only-files-all) - (define-key map (kbd "M-s") 'magit-show-level-4) - (define-key map (kbd "M-S") 'magit-show-level-4-all) - (define-key map (kbd "g") 'magit-refresh) - (define-key map (kbd "G") 'magit-refresh-all) - (define-key map (kbd "?") 'magit-key-mode-popup-dispatch) - (define-key map (kbd ":") 'magit-git-command) - (define-key map (kbd "C-x 4 a") 'magit-add-change-log-entry-other-window) - (define-key map (kbd "L") 'magit-add-change-log-entry) - (define-key map (kbd "RET") 'magit-visit-item) - (define-key map (kbd "C-") 'magit-dired-jump) - (define-key map (kbd "SPC") 'magit-show-item-or-scroll-up) - (define-key map (kbd "DEL") 'magit-show-item-or-scroll-down) - (define-key map (kbd "C-w") 'magit-copy-item-as-kill) - (cond (magit-rigid-key-bindings - (define-key map (kbd "c") 'magit-commit) - (define-key map (kbd "m") 'magit-merge) - (define-key map (kbd "b") 'magit-checkout) - (define-key map (kbd "M") 'magit-branch-manager) - (define-key map (kbd "r") 'undefined) - (define-key map (kbd "f") 'magit-fetch-current) - (define-key map (kbd "F") 'magit-pull) - (define-key map (kbd "J") 'magit-apply-mailbox) - (define-key map (kbd "!") 'magit-git-command-topdir) - (define-key map (kbd "P") 'magit-push) - (define-key map (kbd "t") 'magit-tag) - (define-key map (kbd "l") 'magit-log) - (define-key map (kbd "o") 'magit-submodule-update) - (define-key map (kbd "B") 'undefined) - (define-key map (kbd "z") 'magit-stash)) - (t - (define-key map (kbd "c") 'magit-key-mode-popup-committing) - (define-key map (kbd "m") 'magit-key-mode-popup-merging) - (define-key map (kbd "b") 'magit-key-mode-popup-branching) - (define-key map (kbd "M") 'magit-key-mode-popup-remoting) - (define-key map (kbd "r") 'magit-key-mode-popup-rewriting) - (define-key map (kbd "f") 'magit-key-mode-popup-fetching) - (define-key map (kbd "F") 'magit-key-mode-popup-pulling) - (define-key map (kbd "J") 'magit-key-mode-popup-apply-mailbox) - (define-key map (kbd "!") 'magit-key-mode-popup-running) - (define-key map (kbd "P") 'magit-key-mode-popup-pushing) - (define-key map (kbd "t") 'magit-key-mode-popup-tagging) - (define-key map (kbd "l") 'magit-key-mode-popup-logging) - (define-key map (kbd "o") 'magit-key-mode-popup-submodule) - (define-key map (kbd "B") 'magit-key-mode-popup-bisecting) - (define-key map (kbd "z") 'magit-key-mode-popup-stashing))) - (define-key map (kbd "$") 'magit-process) - (define-key map (kbd "E") 'magit-interactive-rebase) - (define-key map (kbd "R") 'magit-rebase-step) - (define-key map (kbd "e") 'magit-ediff) - (define-key map (kbd "w") 'magit-wazzup) - (define-key map (kbd "y") 'magit-cherry) - (define-key map (kbd "q") 'magit-mode-quit-window) - (define-key map (kbd "x") 'magit-reset-head) - (define-key map (kbd "v") 'magit-revert-item) - (define-key map (kbd "a") 'magit-apply-item) - (define-key map (kbd "A") 'magit-cherry-pick-item) - (define-key map (kbd "d") 'magit-diff-working-tree) - (define-key map (kbd "D") 'magit-diff) - (define-key map (kbd "-") 'magit-diff-smaller-hunks) - (define-key map (kbd "+") 'magit-diff-larger-hunks) - (define-key map (kbd "0") 'magit-diff-default-hunks) - (define-key map (kbd "h") 'magit-key-mode-popup-diff-options) - (define-key map (kbd "H") 'magit-diff-toggle-refine-hunk) - (define-key map (kbd "S") 'magit-stage-all) - (define-key map (kbd "U") 'magit-unstage-all) - (define-key map (kbd "X") 'magit-reset-working-tree) - (define-key map (kbd "C-c C-c") 'magit-key-mode-popup-dispatch) - (define-key map (kbd "C-c C-e") 'magit-key-mode-popup-dispatch) - map) - "Parent keymap for all keymaps of modes derived from `magit-mode'.") - -(defvar magit-commit-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map magit-mode-map) - (define-key map (kbd "C-c C-b") 'magit-go-backward) - (define-key map (kbd "C-c C-f") 'magit-go-forward) - (define-key map (kbd "SPC") 'scroll-up) - (define-key map (kbd "DEL") 'scroll-down) - (define-key map (kbd "j") 'magit-jump-to-diffstats) - map) - "Keymap for `magit-commit-mode'.") - -(defvar magit-status-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map magit-mode-map) - (define-key map (kbd "s") 'magit-stage-item) - (define-key map (kbd "u") 'magit-unstage-item) - (define-key map (kbd "i") 'magit-ignore-item) - (define-key map (kbd "I") 'magit-ignore-item-locally) - (define-key map (kbd "j") 'magit-section-jump-map) - (define-key map (kbd ".") 'magit-mark-item) - (define-key map (kbd "=") 'magit-diff-with-mark) - (define-key map (kbd "k") 'magit-discard-item) - (define-key map (kbd "C") 'magit-commit-add-log) - map) - "Keymap for `magit-status-mode'.") - -(eval-after-load 'dired-x - '(define-key magit-status-mode-map [remap dired-jump] 'magit-dired-jump)) - -(defvar magit-log-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map magit-mode-map) - (define-key map (kbd ".") 'magit-mark-item) - (define-key map (kbd "=") 'magit-diff-with-mark) - (define-key map (kbd "e") 'magit-log-show-more-entries) - (define-key map (kbd "h") 'magit-log-toggle-margin) - map) - "Keymap for `magit-log-mode'.") - -(defvar magit-cherry-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map magit-mode-map) - map) - "Keymap for `magit-cherry-mode'.") - -(defvar magit-reflog-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map magit-log-mode-map) - map) - "Keymap for `magit-reflog-mode'.") - -(defvar magit-diff-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map magit-mode-map) - (define-key map (kbd "C-c C-b") 'magit-go-backward) - (define-key map (kbd "C-c C-f") 'magit-go-forward) - (define-key map (kbd "SPC") 'scroll-up) - (define-key map (kbd "DEL") 'scroll-down) - (define-key map (kbd "j") 'magit-jump-to-diffstats) - map) - "Keymap for `magit-diff-mode'.") - -(defvar magit-wazzup-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map magit-mode-map) - (define-key map (kbd ".") 'magit-mark-item) - (define-key map (kbd "=") 'magit-diff-with-mark) - (define-key map (kbd "i") 'magit-ignore-item) - map) - "Keymap for `magit-wazzup-mode'.") - -(defvar magit-branch-manager-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map magit-mode-map) - (define-key map (kbd "c") 'magit-create-branch) - (define-key map (kbd "a") 'magit-add-remote) - (define-key map (kbd "r") 'magit-rename-item) - (define-key map (kbd "k") 'magit-discard-item) - (define-key map (kbd "T") 'magit-change-what-branch-tracks) - map) - "Keymap for `magit-branch-manager-mode'.") - -(defvar magit-process-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map magit-mode-map) - map) - "Keymap for `magit-process-mode'.") - -(defvar magit-section-jump-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "z") 'magit-jump-to-stashes) - (define-key map (kbd "n") 'magit-jump-to-untracked) - (define-key map (kbd "u") 'magit-jump-to-unstaged) - (define-key map (kbd "s") 'magit-jump-to-staged) - (define-key map (kbd "f") 'magit-jump-to-unpulled) - (define-key map (kbd "p") 'magit-jump-to-unpushed) - (define-key map (kbd "r") 'magit-jump-to-pending) - map) - "Submap for jumping to sections in `magit-status-mode'.") -(fset 'magit-section-jump-map magit-section-jump-map) - -(easy-menu-define magit-mode-menu magit-mode-map - "Magit menu" - '("Magit" - ["Refresh" magit-refresh t] - ["Refresh all" magit-refresh-all t] - "---" - ["Stage" magit-stage-item t] - ["Stage all" magit-stage-all t] - ["Unstage" magit-unstage-item t] - ["Unstage all" magit-unstage-all t] - ["Commit" magit-key-mode-popup-committing t] - ["Add log entry" magit-commit-add-log t] - ["Tag" magit-tag t] - "---" - ["Diff working tree" magit-diff-working-tree t] - ["Diff" magit-diff t] - ("Log" - ["Short Log" magit-log t] - ["Long Log" magit-log-long t] - ["Reflog" magit-reflog t] - ["Extended..." magit-key-mode-popup-logging t]) - "---" - ["Cherry pick" magit-cherry-pick-item t] - ["Apply" magit-apply-item t] - ["Revert" magit-revert-item t] - "---" - ["Ignore" magit-ignore-item t] - ["Ignore locally" magit-ignore-item-locally t] - ["Discard" magit-discard-item t] - ["Reset head" magit-reset-head t] - ["Reset working tree" magit-reset-working-tree t] - ["Stash" magit-stash t] - ["Snapshot" magit-stash-snapshot t] - "---" - ["Branch..." magit-checkout t] - ["Merge" magit-merge t] - ["Interactive resolve" magit-interactive-resolve t] - ["Rebase" magit-rebase-step t] - ("Rewrite" - ["Start" magit-rewrite-start t] - ["Stop" magit-rewrite-stop t] - ["Finish" magit-rewrite-finish t] - ["Abort" magit-rewrite-abort t] - ["Set used" magit-rewrite-set-used t] - ["Set unused" magit-rewrite-set-unused t]) - "---" - ["Push" magit-push t] - ["Pull" magit-pull t] - ["Remote update" magit-remote-update t] - ("Submodule" - ["Submodule update" magit-submodule-update t] - ["Submodule update and init" magit-submodule-update-init t] - ["Submodule init" magit-submodule-init t] - ["Submodule sync" magit-submodule-sync t]) - "---" - ("Extensions") - "---" - ["Display Git output" magit-process t] - ["Quit Magit" magit-mode-quit-window t])) - - -;;; Utilities (1) -;;;; Minibuffer Input - -(defun magit-ido-completing-read - (prompt choices &optional predicate require-match initial-input hist def) - "ido-based completing-read almost-replacement." - (require 'ido) - (let ((reply (ido-completing-read - prompt - (if (consp (car choices)) - (mapcar #'car choices) - choices) - predicate require-match initial-input hist def))) - (or (and (consp (car choices)) - (cdr (assoc reply choices))) - reply))) - -(defun magit-builtin-completing-read - (prompt choices &optional predicate require-match initial-input hist def) - "Magit wrapper for standard `completing-read' function." - (let ((reply (completing-read - (if (and def (> (length prompt) 2) - (string-equal ": " (substring prompt -2))) - (format "%s (default %s): " (substring prompt 0 -2) def) - prompt) - choices predicate require-match initial-input hist def))) - (if (string= reply "") - (if require-match - (user-error "Nothing selected") - nil) - reply))) - -(defun magit-completing-read - (prompt collection &optional predicate require-match initial-input hist def) - "Call function in `magit-completing-read-function' to read user input. - -Read `completing-read' documentation for the meaning of the argument." - (funcall magit-completing-read-function - (concat prompt ": ") collection predicate - require-match initial-input hist def)) - -(defvar magit-gpg-secret-key-hist nil) - -(defun magit-read-gpg-secret-key (prompt) - (let ((keys (mapcar - (lambda (key) - (list (epg-sub-key-id (car (epg-key-sub-key-list key))) - (let ((id-obj (car (epg-key-user-id-list key))) - (id-str nil)) - (when id-obj - (setq id-str (epg-user-id-string id-obj)) - (if (stringp id-str) - id-str - (epg-decode-dn id-obj)))))) - (epg-list-keys (epg-make-context epa-protocol) nil t)))) - (magit-completing-read prompt keys nil nil nil 'magit-gpg-secret-key-hist - (car (or magit-gpg-secret-key-hist keys))))) - -;;;; Various Utilities - -(defmacro magit-bind-match-strings (varlist &rest body) - (declare (indent 1)) - (let ((i 0)) - `(let ,(mapcar (lambda (var) - (list var (list 'match-string (cl-incf i)))) - varlist) - ,@body))) - -(defun magit-file-line (file) - "Return the first line of FILE as a string." - (when (file-regular-p file) - (with-temp-buffer - (insert-file-contents file) - (buffer-substring-no-properties (point-min) - (line-end-position))))) - -(defun magit-file-lines (file &optional keep-empty-lines) - "Return a list of strings containing one element per line in FILE. -Unless optional argument KEEP-EMPTY-LINES is t, trim all empty lines." - (when (file-regular-p file) - (with-temp-buffer - (insert-file-contents file) - (split-string (buffer-string) "\n" (not keep-empty-lines))))) - -(defvar-local magit-file-name () - "Name of file the buffer shows a different version of.") - -(defun magit-buffer-file-name (&optional relative) - (let* ((topdir (magit-get-top-dir)) - (filename (or buffer-file-name - (when (buffer-base-buffer) - (with-current-buffer (buffer-base-buffer) - buffer-file-name)) - (when magit-file-name - (expand-file-name magit-file-name topdir))))) - (when filename - (setq filename (file-truename filename)) - (if relative - (file-relative-name filename topdir) - filename)))) - -(defun magit-format-duration (duration spec width) - (cl-destructuring-bind (char unit units weight) - (car spec) - (let ((cnt (round (/ duration weight 1.0)))) - (if (or (not (cdr spec)) - (>= (/ duration weight) 1)) - (if (= width 1) - (format "%3i%c" cnt char) - (format (format "%%3i %%-%is" width) cnt - (if (= cnt 1) unit units))) - (magit-format-duration duration (cdr spec) width))))) - -(defun magit-flatten-onelevel (list) - (cl-mapcan (lambda (elt) - (cond ((consp elt) (copy-sequence elt)) - (elt (list elt)))) - list)) - -(defun magit-insert (string face &rest args) - (if magit-use-overlays - (let ((start (point))) - (insert string) - (let ((ov (make-overlay start (point) nil t))) - (overlay-put ov 'face face) - ;; (overlay-put ov 'priority 10) - (overlay-put ov 'evaporate t))) - (insert (propertize string 'face face))) - (apply #'insert args)) - -(defun magit-put-face-property (start end face) - (if magit-use-overlays - (let ((ov (make-overlay start end nil t))) - (overlay-put ov 'face face) - ;; (overlay-put ov 'priority 10) - (overlay-put ov 'evaporate t)) - (put-text-property start end 'face face))) - -;;;; Buffer Margins - -(defun magit-set-buffer-margin (width enable) - (let ((window (get-buffer-window))) - (when window - (with-selected-window window - (set-window-margins nil (car (window-margins)) (if enable width 0)) - (let ((fn (apply-partially - (lambda (width) - (let ((window (get-buffer-window))) - (when window - (with-selected-window window - (set-window-margins nil (car (window-margins)) - width))))) - width))) - (if enable - (add-hook 'window-configuration-change-hook fn nil t) - (remove-hook 'window-configuration-change-hook fn t))))))) - -(defun magit-make-margin-overlay (&rest strings) - (let ((o (make-overlay (point) (line-end-position) nil t))) - (overlay-put o 'evaporate t) - (overlay-put o 'before-string - (propertize "o" 'display - (list '(margin right-margin) - (apply #'concat strings)))))) - -(defvar-local magit-log-margin-timeunit-width nil) - -(defun magit-log-margin-set-timeunit-width () - (cl-destructuring-bind (width characterp duration-spec) - magit-log-margin-spec - (setq magit-log-margin-timeunit-width - (if characterp - 1 - (apply 'max (mapcar (lambda (e) - (max (length (nth 1 e)) - (length (nth 2 e)))) - (symbol-value duration-spec))))))) - -;;;; Emacsclient Support - -(defmacro magit-with-emacsclient (server-window &rest body) - "Arrange for Git to use Emacsclient as \"the git editor\". - -Git processes that use \"the editor\" have to be asynchronous. -The use of this macro ensures that such processes inside BODY use -Emacsclient as \"the editor\" by setting the environment variable -$GIT_EDITOR accordingly around calls to Git and starting the -server if necessary." - (declare (indent 1)) - `(let* ((process-environment process-environment) - (magit-process-popup-time -1)) - ;; Make sure the client is usable. - (magit-assert-emacsclient "use `magit-with-emacsclient'") - ;; Make sure server-use-tcp's value is valid. - (unless (featurep 'make-network-process '(:family local)) - (setq server-use-tcp t)) - ;; Make sure the server is running. - (unless server-process - (when (server-running-p server-name) - (setq server-name (format "server%s" (emacs-pid))) - (when (server-running-p server-name) - (server-force-delete server-name))) - (server-start)) - ;; Tell Git to use the client. - (setenv "GIT_EDITOR" - (concat magit-emacsclient-executable - ;; Tell the client where the server file is. - (and (not server-use-tcp) - (concat " --socket-name=" - (expand-file-name server-name - server-socket-dir))))) - (when server-use-tcp - (setenv "EMACS_SERVER_FILE" - (expand-file-name server-name server-auth-dir))) - ;; As last resort fallback to a new Emacs instance. - (setenv "ALTERNATE_EDITOR" - (expand-file-name invocation-name invocation-directory)) - ;; Git has to be called asynchronously in BODY or we create a - ;; dead lock. By the time Emacsclient is called the dynamic - ;; binding is no longer in effect and our primitives don't - ;; support callbacks. Temporarily set the default value and - ;; restore the old value using a timer. - (let ((window ,server-window)) - (unless (equal window server-window) - (run-at-time "1 sec" nil - (apply-partially (lambda (value) - (setq server-window value)) - server-window)) - (setq-default server-window window))) - ,@body)) - -(defun magit-use-emacsclient-p () - (and magit-emacsclient-executable - (not (tramp-tramp-file-p default-directory)))) - -(defun magit-assert-emacsclient (action) - (unless magit-emacsclient-executable - (user-error "Cannot %s when `magit-emacsclient-executable' is nil" action)) - (when (tramp-tramp-file-p default-directory) - (user-error "Cannot %s when accessing repository using tramp" action))) - -;;;; Git Config - -(defun magit-get (&rest keys) - "Return the value of Git config entry specified by KEYS." - (magit-git-string "config" (mapconcat 'identity keys "."))) - -(defun magit-get-all (&rest keys) - "Return all values of the Git config entry specified by KEYS." - (magit-git-lines "config" "--get-all" (mapconcat 'identity keys "."))) - -(defun magit-get-boolean (&rest keys) - "Return the boolean value of Git config entry specified by KEYS." - (magit-git-true "config" "--bool" (mapconcat 'identity keys "."))) - -(defun magit-set (val &rest keys) - "Set Git config settings specified by KEYS to VAL." - (if val - (magit-git-string "config" (mapconcat 'identity keys ".") val) - (magit-git-string "config" "--unset" (mapconcat 'identity keys ".")))) - -;;;; Git Low-Level - -(defun magit-git-repo-p (dir) - (file-exists-p (expand-file-name ".git" dir))) - -(defun magit-git-dir (&optional path) - "Return absolute path to the GIT_DIR for the current repository. -If optional PATH is non-nil it has to be a path relative to the -GIT_DIR and its absolute path is returned" - (let ((gitdir (magit-git-string "rev-parse" "--git-dir"))) - (when gitdir - (setq gitdir (file-name-as-directory - (magit-expand-git-file-name gitdir))) - (if path - (expand-file-name (convert-standard-filename path) gitdir) - gitdir)))) - -(defun magit-no-commit-p () - "Return non-nil if there is no commit in the current git repository." - (not (magit-git-string "rev-list" "-1" "HEAD"))) - -(defun magit-get-top-dir (&optional directory) - "Return the top directory for the current repository. - -Determine the repository which contains `default-directory' in -either its work tree or git control directory and return its top -directory. If there is no top directory, because the repository -is bare, return the control directory instead. - -If optional DIRECTORY is non-nil then return the top directory of -the repository that contains that instead. DIRECTORY has to be -an existing directory." - (setq directory (if directory - (file-name-as-directory - (expand-file-name directory)) - default-directory)) - (unless (file-directory-p directory) - (error "%s isn't an existing directory" directory)) - (let* ((default-directory directory) - (top (magit-git-string "rev-parse" "--show-toplevel"))) - (if top - (file-name-as-directory (magit-expand-git-file-name top)) - (let ((gitdir (magit-git-dir))) - (when gitdir - (if (magit-bare-repo-p) - gitdir - (file-name-directory (directory-file-name gitdir)))))))) - -(defun magit-expand-git-file-name (filename) - (when (tramp-tramp-file-p default-directory) - (setq filename (file-relative-name filename - (with-parsed-tramp-file-name - default-directory nil - localname)))) - (expand-file-name filename)) - -(defun magit-file-relative-name (file) - "Return the path of FILE relative to the repository root. -If FILE isn't inside a Git repository then return nil." - (setq file (file-truename file)) - (let ((topdir (magit-get-top-dir (file-name-directory file)))) - (and topdir (substring file (length topdir))))) - -(defun magit-bare-repo-p () - "Return t if the current repository is bare." - (magit-git-true "rev-parse" "--is-bare-repository")) - -(defun magit-get-ref (ref) - (magit-git-string "symbolic-ref" "-q" ref)) - -(defun magit-get-current-branch () - (let ((head (magit-get-ref "HEAD"))) - (when (and head (string-match "^refs/heads/" head)) - (substring head 11)))) - -(defun magit-get-remote/branch (&optional branch verify) - "Return the remote-tracking branch of BRANCH used for pulling. -Return a string of the form \"REMOTE/BRANCH\". - -If optional BRANCH is nil return the remote-tracking branch of -the current branch. If optional VERIFY is non-nil verify that -the remote branch exists; else return nil." - (save-match-data - (let (remote remote-branch remote/branch) - (and (or branch (setq branch (magit-get-current-branch))) - (setq remote (magit-get "branch" branch "remote")) - (setq remote-branch (magit-get "branch" branch "merge")) - (string-match "^refs/heads/\\(.+\\)" remote-branch) - (setq remote/branch - (concat remote "/" (match-string 1 remote-branch))) - (or (not verify) - (magit-git-success "rev-parse" "--verify" remote/branch)) - remote/branch)))) - -(defun magit-get-tracked-branch (&optional branch qualified pretty) - "Return the name of the tracking branch the local branch name BRANCH. - -If optional QUALIFIED is non-nil return the full branch path, -otherwise try to shorten it to a name (which may fail). If -optional PRETTY is non-nil additionally format the branch name -according to option `magit-remote-ref-format'." - (unless branch - (setq branch (magit-get-current-branch))) - (when branch - (let ((remote (magit-get "branch" branch "remote")) - (merge (magit-get "branch" branch "merge"))) - (when (and (not merge) - (not (equal remote "."))) - (setq merge branch)) - (when (and remote merge) - (if (string= remote ".") - (cond (qualified merge) - ((string-match "^refs/heads/" merge) - (substring merge 11)) - ((string-match "^refs/" merge) - merge)) - (let* ((fetch (mapcar (lambda (f) (split-string f "[+:]" t)) - (magit-get-all "remote" remote "fetch"))) - (match (cadr (assoc merge fetch)))) - (unless match - (let* ((prefix (nreverse (split-string merge "/"))) - (unique (list (car prefix)))) - (setq prefix (cdr prefix)) - (setq fetch - (cl-mapcan - (lambda (f) - (cl-destructuring-bind (from to) f - (setq from (nreverse (split-string from "/"))) - (when (equal (car from) "*") - (list (list (cdr from) to))))) - fetch)) - (while (and prefix (not match)) - (if (setq match (cadr (assoc prefix fetch))) - (setq match (concat (substring match 0 -1) - (mapconcat 'identity unique "/"))) - (push (car prefix) unique) - (setq prefix (cdr prefix)))))) - (cond ((not match) nil) - (qualified match) - ((string-match "^refs/remotes/" match) - (if pretty - (magit-format-ref match) - (substring match 13))) - (t match)))))))) - -(defun magit-get-previous-branch () - "Return the refname of the previously checked out branch. -Return nil if the previously checked out branch no longer exists." - (magit-name-rev (magit-git-string "rev-parse" "--verify" "@{-1}"))) - -(defun magit-get-current-tag (&optional with-distance-p) - "Return the closest tag reachable from \"HEAD\". - -If optional WITH-DISTANCE-P is non-nil then return (TAG COMMITS), -if it is `dirty' return (TAG COMMIT DIRTY). COMMITS is the number -of commits in \"HEAD\" but not in TAG and DIRTY is t if there are -uncommitted changes, nil otherwise." - (let ((tag (magit-git-string "describe" "--long" "--tags" - (and (eq with-distance-p 'dirty) "--dirty")))) - (save-match-data - (when tag - (string-match - "\\(.+\\)-\\(?:0[0-9]*\\|\\([0-9]+\\)\\)-g[0-9a-z]+\\(-dirty\\)?$" tag) - (if with-distance-p - (list (match-string 1 tag) - (string-to-number (or (match-string 2 tag) "0")) - (and (match-string 3 tag) t)) - (match-string 1 tag)))))) - -(defun magit-get-next-tag (&optional with-distance-p) - "Return the closest tag from which \"HEAD\" is reachable. - -If no such tag can be found or if the distance is 0 (in which -case it is the current tag, not the next) return nil instead. - -If optional WITH-DISTANCE-P is non-nil then return (TAG COMMITS) -where COMMITS is the number of commits in TAG but not in \"HEAD\"." - (let ((rev (magit-git-string "describe" "--contains" "HEAD"))) - (save-match-data - (when (and rev (string-match "^[^^~]+" rev)) - (let ((tag (match-string 0 rev))) - (unless (equal tag (magit-get-current-tag)) - (if with-distance-p - (list tag (car (magit-rev-diff-count tag "HEAD"))) - tag))))))) - -(defun magit-get-remote (branch) - "Return the name of the remote for BRANCH. -If branch is nil or it has no remote, but a remote named -\"origin\" exists, return that. Otherwise, return nil." - (let ((remote (or (and branch (magit-get "branch" branch "remote")) - (and (magit-get "remote" "origin" "url") "origin")))) - (unless (string= remote "") - remote))) - -(defun magit-get-current-remote () - "Return the name of the remote for the current branch. -If there is no current branch, or no remote for that branch, -but a remote named \"origin\" is configured, return that. -Otherwise, return nil." - (magit-get-remote (magit-get-current-branch))) - -(defun magit-ref-exists-p (ref) - (magit-git-success "show-ref" "--verify" ref)) - -(defun magit-rev-parse (&rest args) - "Execute `git rev-parse ARGS', returning first line of output. -If there is no output return nil." - (apply #'magit-git-string "rev-parse" args)) - -(defun magit-ref-ambiguous-p (ref) - "Return whether or not REF is ambiguous." - ;; An ambiguous ref does not cause `git rev-parse --abbrev-ref' - ;; to exits with a non-zero status. But there is nothing on - ;; stdout in that case. - (not (magit-git-string "rev-parse" "--abbrev-ref" ref))) - -(defun magit-rev-diff-count (a b) - "Return the commits in A but not B and vice versa. -Return a list of two integers: (A>B B>A)." - (mapcar 'string-to-number - (split-string (magit-git-string "rev-list" - "--count" "--left-right" - (concat a "..." b)) - "\t"))) - -(defun magit-name-rev (rev &optional no-trim) - "Return a human-readable name for REV. -Unlike `git name-rev', this will remove \"tags/\" and \"remotes/\" -prefixes if that can be done unambiguously (unless optional arg -NO-TRIM is non-nil). In addition, it will filter out revs -involving HEAD." - (when rev - (let ((name (magit-git-string "name-rev" "--no-undefined" "--name-only" rev))) - ;; There doesn't seem to be a way of filtering HEAD out from name-rev, - ;; so we have to do it manually. - ;; HEAD-based names are too transient to allow. - (when (and (stringp name) - (string-match "^\\(.*\\" name)) - (setq name (magit-rev-parse rev))))) - (setq rev (or name rev)) - (when (string-match "^\\(?:tags\\|remotes\\)/\\(.*\\)" rev) - (let ((plain-name (match-string 1 rev))) - (unless (or no-trim (magit-ref-ambiguous-p plain-name)) - (setq rev plain-name)))) - rev))) - -(defun magit-file-uptodate-p (file) - (magit-git-success "diff" "--quiet" "--" file)) - -(defun magit-anything-staged-p () - (magit-git-failure "diff" "--quiet" "--cached")) - -(defun magit-anything-unstaged-p () - (magit-git-failure "diff" "--quiet")) - -(defun magit-anything-modified-p () - (or (magit-anything-staged-p) - (magit-anything-unstaged-p))) - -(defun magit-commit-parents (commit) - (cdr (split-string (magit-git-string "rev-list" "-1" "--parents" commit)))) - -(defun magit-assert-one-parent (commit command) - (when (> (length (magit-commit-parents commit)) 1) - (user-error "Cannot %s a merge commit" command))) - -(defun magit-decode-git-path (path) - (if (eq (aref path 0) ?\") - (string-as-multibyte (read path)) - path)) - -(defun magit-abbrev-length () - (string-to-number (or (magit-get "core.abbrev") "7"))) - -(defun magit-abbrev-arg () - (format "--abbrev=%d" (magit-abbrev-length))) - -;;;; Git Revisions - -(defvar magit-uninteresting-refs - '("^refs/stash$" - "^refs/remotes/[^/]+/HEAD$" - "^refs/remotes/[^/]+/top-bases$" - "^refs/top-bases$")) - -(cl-defun magit-list-interesting-refs (&optional uninteresting - (refs nil srefs)) - (cl-loop for ref in (if srefs - refs - (mapcar (lambda (l) - (cadr (split-string l " "))) - (magit-git-lines "show-ref"))) - with label - unless (or (cl-loop for i in - (cl-typecase uninteresting - (null magit-uninteresting-refs) - (list uninteresting) - (string (cons (format "^refs/heads/%s$" - uninteresting) - magit-uninteresting-refs))) - thereis (string-match i ref)) - (not (setq label (magit-format-ref ref)))) - collect (cons label ref))) - -(defun magit-format-ref (ref) - (cond ((string-match "refs/heads/\\(.*\\)" ref) - (match-string 1 ref)) - ((string-match "refs/tags/\\(.*\\)" ref) - (format (if (eq magit-remote-ref-format 'branch-then-remote) - "%s (tag)" - "%s") - (match-string 1 ref))) - ((string-match "refs/remotes/\\([^/]+\\)/\\(.+\\)" ref) - (if (eq magit-remote-ref-format 'branch-then-remote) - (format "%s (%s)" - (match-string 2 ref) - (match-string 1 ref)) - (substring ref 13))) - (t ref))) - -(defvar magit-read-file-hist nil) - -(defun magit-read-file-from-rev (revision &optional default) - (unless revision - (setq revision "HEAD")) - (let ((default-directory (magit-get-top-dir))) - (magit-completing-read - (format "Retrieve file from %s" revision) - (magit-git-lines "ls-tree" "-r" "-t" "--name-only" revision) - nil 'require-match - nil 'magit-read-file-hist - (or default (magit-buffer-file-name t))))) - -(defun magit-read-file-trace (ignored) - (let ((file (magit-read-file-from-rev "HEAD")) - (trace (read-string "Trace: "))) - (if (string-match - "^\\(/.+/\\|:[^:]+\\|[0-9]+,[-+]?[0-9]+\\)\\(:\\)?$" trace) - (concat trace (or (match-string 2 trace) ":") file) - (user-error "Trace is invalid, see man git-log")))) - -(defvar magit-read-rev-history nil - "The history of inputs to `magit-read-rev' and `magit-read-tag'.") - -(defun magit-read-tag (prompt &optional require-match) - (magit-completing-read prompt (magit-git-lines "tag") nil - require-match nil 'magit-read-rev-history)) - -(defun magit-read-rev (prompt &optional default uninteresting noselection) - (let* ((interesting-refs - (mapcar (lambda (elt) - (setcdr elt (replace-regexp-in-string - "^refs/heads/" "" (cdr elt))) - elt) - (magit-list-interesting-refs uninteresting))) - (reply (magit-completing-read prompt interesting-refs nil nil nil - 'magit-read-rev-history default)) - (rev (or (cdr (assoc reply interesting-refs)) reply))) - (when (equal rev ".") - (setq rev magit-marked-commit)) - (unless (or rev noselection) - (user-error "No rev selected")) - rev)) - -(defun magit-read-rev-with-default (prompt) - (magit-read-rev prompt - (let ((branch (or (magit-guess-branch) "HEAD"))) - (when branch - (if (string-match "^refs/\\(.*\\)" branch) - (match-string 1 branch) - branch))))) - -(defun magit-read-rev-range (op &optional def-beg def-end) - (let ((beg (magit-read-rev (format "%s range or start" op) def-beg))) - (save-match-data - (if (string-match "^\\(.+\\)\\.\\.\\(.+\\)$" beg) - (match-string 0 beg) - (let ((end (magit-read-rev (format "%s end" op) def-end nil t))) - (if end (concat beg ".." end) beg)))))) - -(defun magit-read-stash (prompt) - (let ((n (read-number prompt 0)) - (l (1- (length (magit-git-lines "stash" "list"))))) - (if (> n l) - (user-error "No stash older than stash@{%i}" l) - (format "stash@{%i}" n)))) - -(defun magit-read-remote (prompt &optional default require-match) - (magit-completing-read prompt (magit-git-lines "remote") - nil require-match nil nil - (or default (magit-guess-remote)))) - -(defun magit-read-remote-branch (prompt remote &optional default) - (let ((branch (magit-completing-read - prompt - (cl-mapcan - (lambda (b) - (and (not (string-match " -> " b)) - (string-match (format "^ *%s/\\(.*\\)$" - (regexp-quote remote)) b) - (list (match-string 1 b)))) - (magit-git-lines "branch" "-r")) - nil nil nil nil default))) - (unless (string= branch "") - branch))) - -(defun magit-format-ref-label (ref) - (cl-destructuring-bind (re face fn) - (cl-find-if (lambda (ns) - (string-match (car ns) ref)) - magit-refs-namespaces) - (if fn - (funcall fn ref face) - (propertize (or (match-string 1 ref) ref) 'face face)))) - -(defun magit-format-ref-labels (string) - (save-match-data - (mapconcat 'magit-format-ref-label - (mapcar 'cdr - (magit-list-interesting-refs - nil (split-string string "\\(tag: \\|[(), ]\\)" t))) - " "))) - -(defun magit-insert-ref-labels (string) - (save-match-data - (dolist (ref (split-string string "\\(tag: \\|[(), ]\\)" t) " ") - (cl-destructuring-bind (re face fn) - (cl-find-if (lambda (elt) (string-match (car elt) ref)) - magit-refs-namespaces) - (if fn - (let ((text (funcall fn ref face))) - (magit-insert text (get-text-property 1 'face text) ?\s)) - (magit-insert (or (match-string 1 ref) ref) face ?\s)))))) - -(defun magit-format-rev-summary (rev) - (let ((s (magit-git-string "log" "-1" - (concat "--pretty=format:%h %s") rev))) - (when s - (string-match " " s) - (put-text-property 0 (match-beginning 0) 'face 'magit-log-sha1 s) - s))) - -;;; Magit Api -;;;; Section Api -;;;;; Section Core - -(cl-defstruct magit-section - type info - beginning content-beginning end - hidden needs-refresh-on-show highlight - diff-status diff-file2 diff-range - process - parent children) - -(defvar-local magit-root-section nil - "The root section in the current buffer. -All other sections are descendants of this section. The value -of this variable is set by `magit-with-section' and you should -never modify it.") -(put 'magit-root-section 'permanent-local t) - -;;;;; Section Creation - -(defvar magit-with-section--parent nil - "For use by `magit-with-section' only.") - -(defvar magit-with-section--oldroot nil - "For use by `magit-with-section' only.") - -(defmacro magit-with-section (arglist &rest body) - "\n\n(fn (NAME TYPE &optional INFO HEADING NOHIGHLIGHT COLLAPSE) &rest ARGS)" - (declare (indent 1) (debug ((form form &optional form form form) body))) - (let ((s (car arglist))) - `(let ((,s (make-magit-section - :type ',(nth 1 arglist) - :info ,(nth 2 arglist) - :highlight (not ,(nth 4 arglist)) - :beginning (point-marker) - :content-beginning (point-marker) - :parent magit-with-section--parent))) - (setf (magit-section-hidden ,s) - (let ((old (and magit-with-section--oldroot - (magit-find-section (magit-section-path ,s) - magit-with-section--oldroot)))) - (if old - (magit-section-hidden old) - ,(nth 5 arglist)))) - (let ((magit-with-section--parent ,s) - (magit-with-section--oldroot - (or magit-with-section--oldroot - (unless magit-with-section--parent - (prog1 magit-root-section - (setq magit-root-section ,s)))))) - ,@body) - (when ,s - (set-marker-insertion-type (magit-section-content-beginning ,s) t) - (let ((heading ,(nth 3 arglist))) - (when heading - (save-excursion - (goto-char (magit-section-beginning ,s)) - (insert - (if (string-match-p "\n$" heading) - (substring heading 0 -1) - (propertize - (let (c) - (if (and magit-show-child-count - (string-match-p ":$" heading) - (> (setq c (length (magit-section-children ,s))) 0)) - (format "%s (%s):" (substring heading 0 -1) c) - heading)) - 'face 'magit-section-title))) - (insert "\n")))) - (set-marker-insertion-type (magit-section-beginning ,s) t) - (goto-char (max (point) ; smaller if there is no content - (magit-section-content-beginning ,s))) - (setf (magit-section-end ,s) (point-marker)) - (save-excursion - (goto-char (magit-section-beginning ,s)) - (let ((end (magit-section-end ,s))) - (while (< (point) end) - (let ((next (or (next-single-property-change - (point) 'magit-section) - end))) - (unless (get-text-property (point) 'magit-section) - (put-text-property (point) next 'magit-section ,s)) - (goto-char next))))) - (if (eq ,s magit-root-section) - (magit-section-set-hidden magit-root-section nil) - (setf (magit-section-children (magit-section-parent ,s)) - (nconc (magit-section-children (magit-section-parent ,s)) - (list ,s))))) - ,s))) - -(defmacro magit-cmd-insert-section (arglist washer program &rest args) - "\n\n(fn (TYPE &optional HEADING) WASHER PROGRAM &rest ARGS)" - (declare (indent 2)) - `(magit-with-section (section ,(car arglist) - ',(car arglist) - ,(cadr arglist) t) - (apply #'process-file ,program nil (list t nil) nil - (magit-flatten-onelevel (list ,@args))) - (unless (eq (char-before) ?\n) - (insert "\n")) - (save-restriction - (narrow-to-region (magit-section-content-beginning section) (point)) - (goto-char (point-min)) - (funcall ,washer) - (goto-char (point-max))) - (let ((parent (magit-section-parent section)) - (head-beg (magit-section-beginning section)) - (body-beg (magit-section-content-beginning section))) - (if (= (point) body-beg) - (if (not parent) - (insert "(empty)\n") - (delete-region head-beg body-beg) - (setq section nil)) - (insert "\n"))))) - -(defmacro magit-git-insert-section (arglist washer &rest args) - "\n\n(fn (TYPE &optional HEADING) WASHER &rest ARGS)" - (declare (indent 2)) - `(magit-cmd-insert-section ,arglist - ,washer - magit-git-executable - magit-git-standard-options ,@args)) - -(defmacro magit-insert-line-section (arglist line) - "\n\n(fn (TYPE &optional INFO) line)" - (declare (indent 1)) - (let ((l (cl-gensym "line"))) - `(let ((,l (concat ,line "\n"))) - (when (string-match "^\\([^:]+\\):\\( \\)" ,l) - (setq ,l (replace-match - (make-string (max 1 (- magit-status-line-align-to - (length (match-string 1 ,l)))) - ?\s) - t t ,l 2))) - (magit-with-section (section ,(car arglist) ',(car arglist) ,l t) - (setf (magit-section-info section) ,(cadr arglist)))))) - -;;;;; Section Searching - -(defun magit-find-section (path top) - "Find the section at the path PATH in subsection of section TOP." - (if (null path) - top - (let ((secs (magit-section-children top))) - (while (and secs (not (equal (car path) - (magit-section-info (car secs))))) - (setq secs (cdr secs))) - (when (car secs) - (magit-find-section (cdr path) (car secs)))))) - -(defun magit-section-path (section) - "Return the path of SECTION." - (let ((parent (magit-section-parent section))) - (when parent - (append (magit-section-path parent) - (list (magit-section-info section)))))) - -(defun magit-find-section-after (pos) - "Find the first section that begins after POS." - (magit-find-section-after* pos (list magit-root-section))) - -(defun magit-find-section-after* (pos secs) - "Find the first section that begins after POS in the list SECS -\(including children of sections in SECS)." - (while (and secs - (<= (magit-section-beginning (car secs)) pos)) - (setq secs (if (magit-section-hidden (car secs)) - (cdr secs) - (append (magit-section-children (car secs)) - (cdr secs))))) - (car secs)) - -(defun magit-find-section-before (pos) - "Return the last section that begins before POS." - (let ((section (magit-find-section-at pos))) - (cl-do* ((current (or (magit-section-parent section) - section) - next) - (next (unless (magit-section-hidden current) - (magit-find-section-before* - pos (magit-section-children current))) - (unless (magit-section-hidden current) - (magit-find-section-before* - pos (magit-section-children current))))) - ((null next) current)))) - -(defun magit-find-section-before* (pos secs) - "Find the last section that begins before POS in the list SECS." - (let ((prev nil)) - (while (and secs - (< (magit-section-beginning (car secs)) pos)) - (setq prev (car secs)) - (setq secs (cdr secs))) - prev)) - -(defun magit-current-section () - "Return the Magit section at point." - (magit-find-section-at (point))) - -(defun magit-find-section-at (pos) - "Return the Magit section at POS." - (or (get-text-property pos 'magit-section) - magit-root-section)) - -;;;;; Section Jumping - -(defun magit-goto-next-section () - "Go to the next section." - (interactive) - (let ((next (magit-find-section-after (point)))) - (if next - (magit-goto-section next) - (message "No next section")))) - -(defun magit-goto-previous-section () - "Go to the previous section." - (interactive) - (if (eq (point) 1) - (message "No previous section") - (magit-goto-section (magit-find-section-before (point))))) - -(defun magit-goto-parent-section () - "Go to the parent section." - (interactive) - (let ((parent (magit-section-parent (magit-current-section)))) - (when parent - (goto-char (magit-section-beginning parent))))) - -(defun magit-goto-next-sibling-section () - "Go to the next sibling section." - (interactive) - (let* ((section (magit-current-section)) - (parent (magit-section-parent section)) - (next (and parent (magit-find-section-after* - (1- (magit-section-end section)) - (magit-section-children parent))))) - (if next - (magit-goto-section next) - (magit-goto-next-section)))) - -(defun magit-goto-previous-sibling-section () - "Go to the previous sibling section." - (interactive) - (let* ((section (magit-current-section)) - (parent (magit-section-parent section)) - (prev (and parent (magit-find-section-before* - (magit-section-beginning section) - (magit-section-children parent))))) - (if prev - (magit-goto-section prev) - (magit-goto-previous-section)))) - -(defun magit-goto-section (section) - (goto-char (magit-section-beginning section)) - (cond - ((and magit-log-auto-more - (eq (magit-section-type section) 'longer)) - (magit-log-show-more-entries) - (forward-line -1) - (magit-goto-next-section)) - ((and (eq (magit-section-type section) 'commit) - (derived-mode-p 'magit-log-mode) - (or (eq (car magit-refresh-args) 'oneline) - (get-buffer-window magit-commit-buffer-name))) - (magit-show-commit (magit-section-info section) t)))) - -(defun magit-goto-section-at-path (path) - "Go to the section described by PATH." - (let ((sec (magit-find-section path magit-root-section))) - (if sec - (goto-char (magit-section-beginning sec)) - (message "No such section")))) - -(defmacro magit-define-section-jumper (sym title) - "Define an interactive function to go to section SYM. -TITLE is the displayed title of the section." - (let ((fun (intern (format "magit-jump-to-%s" sym)))) - `(progn - (defun ,fun (&optional expand) ,(format "\ -Jump to section '%s'. -With a prefix argument also expand it." title) - (interactive "P") - (if (magit-goto-section-at-path '(,sym)) - (when expand - (with-local-quit - (if (eq magit-expand-staged-on-commit 'full) - (magit-show-level 4 nil) - (magit-expand-section))) - (recenter 0)) - (message ,(format "Section '%s' wasn't found" title)))) - (put ',fun 'definition-name ',sym)))) - -(magit-define-section-jumper stashes "Stashes") -(magit-define-section-jumper untracked "Untracked files") -(magit-define-section-jumper unstaged "Unstaged changes") -(magit-define-section-jumper staged "Staged changes") -(magit-define-section-jumper unpulled "Unpulled commits") -(magit-define-section-jumper unpushed "Unpushed commits") -(magit-define-section-jumper diffstats "Diffstats") - -;;;;; Section Hooks - -(defun magit-add-section-hook (hook function &optional at append local) - "Add to the value of section hook HOOK the function FUNCTION. - -Add FUNCTION at the beginning of the hook list unless optional -APPEND is non-nil, in which case FUNCTION is added at the end. -If FUNCTION already is a member then move it to the new location. - -If optional AT is non-nil and a member of the hook list, then add -FUNCTION next to that instead. Add before or after AT depending -on APPEND. If only FUNCTION is a member of the list, then leave -it where ever it already is. - -If optional LOCAL is non-nil, then modify the hook's buffer-local -value rather than its global value. This makes the hook local by -copying the default value. That copy is then modified. - -HOOK should be a symbol. If HOOK is void, it is first set to nil. -HOOK's value must not be a single hook function. FUNCTION should -be a function that takes no arguments and inserts one or multiple -sections at point, moving point forward. FUNCTION may choose not -to insert its section(s), when doing so would not make sense. It -should not be abused for other side-effects. To remove FUNCTION -again use `remove-hook'." - (or (boundp hook) (set hook nil)) - (or (default-boundp hook) (set-default hook nil)) - (let ((value (if local - (if (local-variable-p hook) - (symbol-value hook) - (unless (local-variable-if-set-p hook) - (make-local-variable hook)) - (copy-sequence (default-value hook))) - (default-value hook)))) - (if at - (when (setq at (member at value)) - (setq value (delq function value)) - (if append - (push function (cdr at)) - (push (car at) (cdr at)) - (setcar at function))) - (setq value (delq function value))) - (unless (member function value) - (setq value (if append - (append value (list function)) - (cons function value)))) - (if local - (set hook value) - (set-default hook value)))) - -;;;;; Section Utilities - -(defun magit-map-sections (function section) - "Apply FUNCTION to SECTION and recursively its subsections." - (funcall function section) - (mapc (apply-partially 'magit-map-sections function) - (magit-section-children section))) - -(defun magit-wash-sequence (function) - "Repeatedly call FUNCTION until it returns nil or eob is reached. -FUNCTION has to move point forward or return nil." - (while (and (not (eobp)) (funcall function)))) - -(defun magit-section-parent-info (section) - (setq section (magit-section-parent section)) - (when section (magit-section-info section))) - -(defun magit-section-siblings (section &optional direction) - (let ((parent (magit-section-parent section))) - (when parent - (let ((siblings (magit-section-children parent))) - (cl-ecase direction - (prev (member section (reverse siblings))) - (next (member section siblings)) - (nil siblings)))))) - -(defun magit-section-region-siblings (&optional key) - (let ((beg (magit-find-section-at (region-beginning))) - (end (magit-find-section-at (region-end)))) - (if (eq beg end) - (list (if key (funcall key beg) beg)) - (goto-char (region-end)) - (when (bolp) - (setq end (magit-find-section-at (1- (point))))) - (while (> (length (magit-section-path beg)) - (length (magit-section-path end))) - (setq beg (magit-section-parent beg))) - (while (> (length (magit-section-path end)) - (length (magit-section-path beg))) - (setq end (magit-section-parent end))) - (let* ((parent (magit-section-parent beg)) - (siblings (magit-section-children parent))) - (if (eq parent (magit-section-parent end)) - (mapcar (or key #'identity) - (cl-intersection (memq beg siblings) - (memq end (reverse siblings)))) - (user-error "Ambitious cross-section region")))))) - -(defun magit-diff-section-for-diffstat (section) - (let ((file (magit-section-info section))) - (cl-find-if (lambda (s) - (and (eq (magit-section-type s) 'diff) - (string-equal (magit-section-info s) file))) - (magit-section-children magit-root-section)))) - -;;;;; Section Visibility - -(defun magit-section-set-hidden (section hidden) - "Hide SECTION if HIDDEN is not nil, show it otherwise." - (setf (magit-section-hidden section) hidden) - (if (and (not hidden) - (magit-section-needs-refresh-on-show section)) - (magit-refresh) - (let ((inhibit-read-only t) - (beg (save-excursion - (goto-char (magit-section-beginning section)) - (forward-line) - (point))) - (end (magit-section-end section))) - (when (< beg end) - (put-text-property beg end 'invisible hidden))) - (unless hidden - (dolist (c (magit-section-children section)) - (magit-section-set-hidden c (magit-section-hidden c)))))) - -(defun magit-section-any-hidden (section) - "Return true if SECTION or any of its children is hidden." - (or (magit-section-hidden section) - (let ((kids (magit-section-children section))) - (while (and kids (not (magit-section-any-hidden (car kids)))) - (setq kids (cdr kids))) - kids))) - -(defun magit-section-collapse (section) - "Show SECTION and hide all its children." - (dolist (c (magit-section-children section)) - (setf (magit-section-hidden c) t)) - (magit-section-set-hidden section nil)) - -(defun magit-section-expand (section) - "Show SECTION and all its children." - (dolist (c (magit-section-children section)) - (setf (magit-section-hidden c) nil)) - (magit-section-set-hidden section nil)) - -(defun magit-section-expand-all-aux (section) - "Show recursively all SECTION's children." - (dolist (c (magit-section-children section)) - (setf (magit-section-hidden c) nil) - (magit-section-expand-all-aux c))) - -(defun magit-section-expand-all (section) - "Show SECTION and all its children." - (magit-section-expand-all-aux section) - (magit-section-set-hidden section nil)) - -(defun magit-section-hideshow (flag-or-func) - "Show or hide current section depending on FLAG-OR-FUNC. - -If FLAG-OR-FUNC is a function, it will be ran on current section. -IF FLAG-OR-FUNC is a boolean, the section will be hidden if it is -true, shown otherwise." - (let ((section (magit-current-section))) - (when (magit-section-parent section) - (goto-char (magit-section-beginning section)) - (if (functionp flag-or-func) - (funcall flag-or-func section) - (magit-section-set-hidden section flag-or-func))))) - -(defun magit-show-section () - "Show current section." - (interactive) - (magit-section-hideshow nil)) - -(defun magit-hide-section () - "Hide current section." - (interactive) - (magit-section-hideshow t)) - -(defun magit-collapse-section () - "Hide all subsection of current section." - (interactive) - (magit-section-hideshow #'magit-section-collapse)) - -(defun magit-expand-section () - "Show all subsection of current section." - (interactive) - (magit-section-hideshow #'magit-section-expand)) - -(defun magit-toggle-file-section () - "Like `magit-toggle-section' but toggle at file granularity." - (interactive) - (when (eq (magit-section-type (magit-current-section)) 'hunk) - (magit-goto-parent-section)) - (magit-toggle-section)) - -(defun magit-toggle-section () - "Toggle hidden status of current section." - (interactive) - (magit-section-hideshow - (lambda (s) - (magit-section-set-hidden s (not (magit-section-hidden s)))))) - -(defun magit-expand-collapse-section () - "Toggle hidden status of subsections of current section." - (interactive) - (magit-section-hideshow - (lambda (s) - (cond ((magit-section-any-hidden s) - (magit-section-expand-all s)) - (t - (magit-section-collapse s)))))) - -(defun magit-cycle-section () - "Cycle between expanded, hidden and collapsed state for current section. - -Hidden: only the first line of the section is shown -Collapsed: only the first line of the subsection is shown -Expanded: everything is shown." - (interactive) - (magit-section-hideshow - (lambda (s) - (cond ((magit-section-hidden s) - (magit-section-collapse s)) - ((with-no-warnings - (cl-notany #'magit-section-hidden (magit-section-children s))) - (magit-section-set-hidden s t)) - (t - (magit-section-expand s)))))) - -(defun magit-section-lineage (section) - "Return list of parent, grand-parents... for SECTION." - (when section - (cons section (magit-section-lineage (magit-section-parent section))))) - -(defun magit-section-show-level (section level threshold path) - (magit-section-set-hidden section (>= level threshold)) - (when (and (< level threshold) - (not (magit-no-commit-p))) - (if path - (magit-section-show-level (car path) (1+ level) threshold (cdr path)) - (dolist (c (magit-section-children section)) - (magit-section-show-level c (1+ level) threshold nil))))) - -(defun magit-show-level (level all) - "Show section whose level is less than LEVEL, hide the others. -If ALL is non nil, do this in all sections, otherwise do it only -on ancestors and descendants of current section." - (if all - (magit-section-show-level magit-root-section 0 level nil) - (let ((path (reverse (magit-section-lineage (magit-current-section))))) - (magit-section-show-level (car path) 0 level (cdr path))))) - -(defun magit-show-only-files () - "Show section that are files, but not their subsection. - -Do this in on ancestors and descendants of current section." - (interactive) - (if (derived-mode-p 'magit-status-mode) - (call-interactively 'magit-show-level-2) - (call-interactively 'magit-show-level-1))) - -(defun magit-show-only-files-all () - "Show section that are files, but not their subsection. -Do this for all sections" - (interactive) - (if (derived-mode-p 'magit-status-mode) - (call-interactively 'magit-show-level-2-all) - (call-interactively 'magit-show-level-1-all))) - -(defmacro magit-define-level-shower-1 (level all) - "Define an interactive function to show function of level LEVEL. - -If ALL is non nil, this function will affect all section, -otherwise it will affect only ancestors and descendants of -current section." - (let ((fun (intern (format "magit-show-level-%s%s" - level (if all "-all" "")))) - (doc (format "Show sections on level %s." level))) - `(defun ,fun () - ,doc - (interactive) - (magit-show-level ,level ,all)))) - -(defmacro magit-define-level-shower (level) - "Define two interactive function to show function of level LEVEL. -One for all, one for current lineage." - `(progn - (magit-define-level-shower-1 ,level nil) - (magit-define-level-shower-1 ,level t))) - -(magit-define-level-shower 1) -(magit-define-level-shower 2) -(magit-define-level-shower 3) -(magit-define-level-shower 4) - -;;;;; Section Highlighting - -(defvar-local magit-highlighted-section nil) -(defvar-local magit-highlight-overlay nil) - -(defun magit-highlight-section () - "Highlight current section. -If its HIGHLIGHT slot is nil, then don't highlight it." - (let ((section (magit-current-section)) - (refinep (lambda () - (and magit-highlighted-section - (eq magit-diff-refine-hunk t) - (eq (magit-section-type magit-highlighted-section) - 'hunk))))) - (unless (eq section magit-highlighted-section) - (when (funcall refinep) - (magit-diff-unrefine-hunk magit-highlighted-section)) - (setq magit-highlighted-section section) - (unless magit-highlight-overlay - (overlay-put (setq magit-highlight-overlay (make-overlay 1 1)) - 'face magit-item-highlight-face)) - (cond ((and section (magit-section-highlight section)) - (when (funcall refinep) - (magit-diff-refine-hunk section)) - (move-overlay magit-highlight-overlay - (magit-section-beginning section) - (magit-section-end section) - (current-buffer))) - (t - (delete-overlay magit-highlight-overlay)))))) - -;;;;; Section Actions - -(defun magit-section-context-type (section) - (cons (magit-section-type section) - (let ((parent (magit-section-parent section))) - (when parent - (magit-section-context-type parent))))) - -(defun magit-section-match-1 (l1 l2) - (or (null l1) - (if (eq (car l1) '*) - (or (magit-section-match-1 (cdr l1) l2) - (and l2 - (magit-section-match-1 l1 (cdr l2)))) - (and l2 - (equal (car l1) (car l2)) - (magit-section-match-1 (cdr l1) (cdr l2)))))) - -(defun magit-section-match (condition &optional section) - (unless section - (setq section (magit-current-section))) - (cond ((eq condition t) t) - ((not section) nil) - ((listp condition) - (cl-find t condition :test - (lambda (_ condition) - (magit-section-match condition section)))) - (t - (magit-section-match-1 (if (symbolp condition) - (list condition) - (append condition nil)) - (magit-section-context-type section))))) - -(defmacro magit-section-case (slots &rest clauses) - (declare (indent 1)) - `(let* ((it (magit-current-section)) - ,@(mapcar - (lambda (slot) - `(,slot - (and it (,(intern (format "magit-section-%s" slot)) it)))) - slots)) - (cond ,@(mapcar (lambda (clause) - `((magit-section-match ',(car clause) it) - ,@(cdr clause))) - clauses)))) - -(defconst magit-section-action-success - (make-symbol "magit-section-action-success")) - -(defmacro magit-section-action (opname slots &rest clauses) - (declare (indent 2) (debug (sexp &rest (sexp body)))) - (let ((value (cl-gensym "value"))) - `(let ((,value - (or (run-hook-wrapped - ',(intern (format "magit-%s-hook" opname)) - (lambda (fn section) - (when (magit-section-match - (or (get fn 'magit-section-action-context) - (error "%s undefined for %s" - 'magit-section-action-context fn)) - section) - (funcall fn (magit-section-info section)))) - (magit-current-section)) - (magit-section-case ,slots - ,@clauses - (t (user-error - (if (magit-current-section) - ,(format "Cannot %s this section" opname) - ,(format "Nothing to %s here" opname)))))))) - (unless (eq ,value magit-section-action-success) - ,value)))) - -;;;; Process Api -;;;;; Process Commands - -(defun magit-process () - "Display Magit process buffer." - (interactive) - (let ((buf (magit-process-buffer))) - (if (buffer-live-p buf) - (pop-to-buffer buf) - (user-error "Process buffer doesn't exist")))) - -(defun magit-process-kill () - "Kill the process at point." - (interactive) - (magit-section-case (info) - (process (if (eq (process-status info) 'run) - (when (yes-or-no-p "Kill this process? ") - (kill-process info)) - (user-error "Process isn't running"))))) - -(defvar magit-git-command-history nil) - -;;;###autoload -(defun magit-git-command (args directory) - "Execute a Git subcommand asynchronously, displaying the output. -With a prefix argument run Git in the root of the current -repository. Non-interactively run Git in DIRECTORY with ARGS." - (interactive (magit-git-command-read-args)) - (require 'eshell) - (magit-mode-display-buffer (magit-process-buffer nil t) - 'magit-process-mode 'pop-to-buffer) - (goto-char (point-max)) - (let ((default-directory directory)) - (magit-run-git-async - (with-temp-buffer - (insert args) - (mapcar 'eval (eshell-parse-arguments (point-min) - (point-max))))))) - -(defun magit-git-command-topdir (args directory) - "Execute a Git subcommand asynchronously, displaying the output. -Run Git in the root of the current repository. -\n(fn)" ; arguments are for internal use - (interactive (magit-git-command-read-args t)) - (magit-git-command args directory)) - -(defun magit-git-command-read-args (&optional root) - (let ((dir (if (or root current-prefix-arg) - (or (magit-get-top-dir) - (user-error "Not inside a Git repository")) - default-directory))) - (list (read-string (format "Git subcommand (in %s): " - (abbreviate-file-name dir)) - nil 'magit-git-command-history) - dir))) - -;;;;; Process Mode - -(define-derived-mode magit-process-mode magit-mode "Magit Process" - "Mode for looking at git process output.") - -(defvar magit-process-buffer-name "*magit-process*" - "Name of buffer where output of processes is put.") - -(defun magit-process-buffer (&optional topdir create) - (or (magit-mode-get-buffer magit-process-buffer-name - 'magit-process-mode topdir) - (with-current-buffer (magit-mode-get-buffer-create - magit-process-buffer-name - 'magit-process-mode topdir) - (magit-process-mode) - (let* ((inhibit-read-only t) - (s (magit-with-section (section processbuf nil nil t) - (insert "\n")))) - (set-marker-insertion-type (magit-section-beginning s) nil) - (set-marker-insertion-type (magit-section-content-beginning s) nil) - (current-buffer))))) - -;;;;; Synchronous Processes - -(defun magit-git-exit-code (&rest args) - "Execute Git with ARGS, returning its exit code." - (apply #'process-file magit-git-executable nil nil nil - (append magit-git-standard-options - (magit-flatten-onelevel args)))) - -(defun magit-git-success (&rest args) - "Execute Git with ARGS, returning t if its exit code is 0." - (= (apply #'magit-git-exit-code args) 0)) - -(defun magit-git-failure (&rest args) - "Execute Git with ARGS, returning t if its exit code is 1." - (= (apply #'magit-git-exit-code args) 1)) - -(defun magit-git-string (&rest args) - "Execute Git with ARGS, returning the first line of its output. -If there is no output return nil. If the output begins with a -newline return an empty string." - (with-temp-buffer - (apply #'process-file magit-git-executable nil (list t nil) nil - (append magit-git-standard-options - (magit-flatten-onelevel args))) - (unless (= (point-min) (point-max)) - (goto-char (point-min)) - (buffer-substring-no-properties - (line-beginning-position) - (line-end-position))))) - -(defun magit-git-true (&rest args) - "Execute Git with ARGS, returning t if it prints \"true\". -Return t if the first (and usually only) output line is the -string \"true\", otherwise return nil." - (equal (apply #'magit-git-string args) "true")) - -(defun magit-git-false (&rest args) - "Execute Git with ARGS, returning t if it prints \"false\". -Return t if the first (and usually only) output line is the -string \"false\", otherwise return nil." - (equal (apply #'magit-git-string args) "false")) - -(defun magit-git-insert (&rest args) - "Execute Git with ARGS, inserting its output at point." - (apply #'process-file magit-git-executable nil (list t nil) nil - (append magit-git-standard-options - (magit-flatten-onelevel args)))) - -(defun magit-git-lines (&rest args) - "Execute Git with ARGS, returning its output as a list of lines. -Empty lines anywhere in the output are omitted." - (with-temp-buffer - (apply #'process-file magit-git-executable nil (list t nil) nil - (append magit-git-standard-options - (magit-flatten-onelevel args))) - (split-string (buffer-string) "\n" 'omit-nulls))) - -(defun magit-run-git (&rest args) - "Call Git synchronously in a separate process, and refresh. - -The arguments ARGS specify command line arguments. The first -level of ARGS is flattened, so each member of ARGS has to be a -string or a list of strings. - -Option `magit-git-executable' specifies which Git executable is -used. The arguments in option `magit-git-standard-options' are -prepended to ARGS. - -After Git returns, the current buffer (if it is a Magit buffer) -as well as the current repository's status buffer are refreshed. -Unmodified buffers visiting files that are tracked in the current -repository are reverted if `magit-auto-revert-mode' is active. - -Process output goes into a new section in a buffer specified by -variable `magit-process-buffer-name'." - (apply #'magit-call-git (magit-process-quote-arguments args)) - (magit-refresh)) - -(defun magit-call-git (&rest args) - "Call Git synchronously in a separate process. - -The arguments ARGS specify command line arguments. The first -level of ARGS is flattened, so each member of ARGS has to be a -string or a list of strings. - -Option `magit-git-executable' specifies which Git executable is -used. The arguments in option `magit-git-standard-options' are -prepended to ARGS. - -Process output goes into a new section in a buffer specified by -variable `magit-process-buffer-name'." - (apply #'magit-call-process magit-git-executable - (append magit-git-standard-options args))) - -(defun magit-call-process (program &rest args) - "Call PROGRAM synchronously in a separate process. - -The arguments ARGS specify command line arguments. The first -level of ARGS is flattened, so each member of ARGS has to be a -string or a list of strings. - -Process output goes into a new section in a buffer specified by -variable `magit-process-buffer-name'." - (setq args (magit-flatten-onelevel args)) - (cl-destructuring-bind (process-buf . section) - (magit-process-setup program args) - (magit-process-finish - (let ((inhibit-read-only t)) - (apply #'process-file program nil process-buf nil args)) - process-buf (current-buffer) default-directory section))) - -(defun magit-run-git-with-input (input &rest args) - "Call Git in a separate process. - -The first argument, INPUT, has to be a buffer or the name of an -existing buffer. The content of that buffer is used as the -process' standard input. - -The remaining arguments, ARGS, specify command line arguments. -The first level of ARGS is flattened, so each member of ARGS has -to be a string or a list of strings. - -Option `magit-git-executable' specifies which Git executable is -used. The arguments in option `magit-git-standard-options' are -prepended to ARGS. - -After Git returns, the current buffer (if it is a Magit buffer) -as well as the current repository's status buffer are refreshed. -Unmodified buffers visiting files that are tracked in the current -repository are reverted if `magit-auto-revert-mode' is active. - -This function actually starts a asynchronous process, but it then -waits for that process to return." - (apply #'magit-start-git input args) - (magit-process-wait) - (magit-refresh)) - -(defun magit-run-git-with-logfile (file &rest args) - "Call Git in a separate process and log its output. -And log the output to FILE. This function might have a -short halflive. See `magit-run-git' for more information." - (apply #'magit-start-git nil args) - (process-put magit-this-process 'logfile file) - (set-process-filter magit-this-process 'magit-process-logfile-filter) - (magit-process-wait) - (magit-refresh)) - -;;;;; Asynchronous Processes - -(defvar magit-this-process nil) - -(defun magit-run-git-async (&rest args) - "Start Git, prepare for refresh, and return the process object. - -If optional argument INPUT is non-nil, it has to be a buffer or -the name of an existing buffer. The content of that buffer is -used as the process' standard input. - -The remaining arguments, ARGS, specify command line arguments. -The first level of ARGS is flattened, so each member of ARGS has -to be a string or a list of strings. - -Display the command line arguments in the echo area. - -After Git returns some buffers are refreshed: the buffer that was -current when `magit-start-process' was called (if it is a Magit -buffer and still alive), as well as the respective Magit status -buffer. Unmodified buffers visiting files that are tracked in -the current repository are reverted if `magit-auto-revert-mode' -is active. - -See `magit-start-process' for more information." - (message "Running %s %s" magit-git-executable - (mapconcat 'identity (magit-flatten-onelevel args) " ")) - (apply #'magit-start-git nil args)) - -(defun magit-start-git (&optional input &rest args) - "Start Git, prepare for refresh, and return the process object. - -If optional argument INPUT is non-nil, it has to be a buffer or -the name of an existing buffer. The buffer content becomes the -processes standard input. - -The remaining arguments, ARGS, specify command line arguments. -The first level of ARGS is flattened, so each member of ARGS has -to be a string or a list of strings. - -After Git returns some buffers are refreshed: the buffer that was -current when `magit-start-process' was called (if it is a Magit -buffer and still alive), as well as the respective Magit status -buffer. Unmodified buffers visiting files that are tracked in -the current repository are reverted if `magit-auto-revert-mode' -is active. - -See `magit-start-process' for more information." - (apply #'magit-start-process magit-git-executable input - (append magit-git-standard-options - (magit-process-quote-arguments args)))) - -(defun magit-start-process (program &optional input &rest args) - "Start PROGRAM, prepare for refresh, and return the process object. - -If optional argument INPUT is non-nil, it has to be a buffer or -the name of an existing buffer. The buffer content becomes the -processes standard input. - -The remaining arguments, ARGS, specify command line arguments. -The first level of ARGS is flattened, so each member of ARGS has -to be a string or a list of strings. - -The process is started using `start-file-process' and then setup -to use the sentinel `magit-process-sentinel' and the filter -`magit-process-filter'. Information required by these functions -is stored in the process object. When this function returns the -process has not started to run yet so it is possible to override -the sentinel and filter. - -After the process returns, `magit-process-sentinel' refreshes the -buffer that was current when `magit-start-process' was called (if -it is a Magit buffer and still alive), as well as the respective -Magit status buffer. Unmodified buffers visiting files that are -tracked in the current repository are reverted if -`magit-auto-revert-mode' is active." - (setq args (magit-flatten-onelevel args)) - (cl-destructuring-bind (process-buf . section) - (magit-process-setup program args) - (let* ((process-connection-type - ;; Don't use a pty, because it would set icrnl - ;; which would modify the input (issue #20). - (and (not input) magit-process-connection-type)) - (process (apply 'start-file-process - (file-name-nondirectory program) - process-buf program args))) - (set-process-sentinel process #'magit-process-sentinel) - (set-process-filter process #'magit-process-filter) - (set-process-buffer process process-buf) - (process-put process 'section section) - (process-put process 'command-buf (current-buffer)) - (process-put process 'default-dir default-directory) - (setf (magit-section-process section) process) - (with-current-buffer process-buf - (set-marker (process-mark process) (point))) - (when input - (with-current-buffer input - (process-send-region process (point-min) (point-max)) - (process-send-eof process))) - (setq magit-this-process process) - (setf (magit-section-info section) process) - (magit-process-display-buffer process) - process))) - -;;;;; Process Internals - -(defun magit-process-setup (program args) - (magit-process-set-mode-line program args) - (let ((buf (magit-process-buffer))) - (if buf - (magit-process-truncate-log buf) - (setq buf (magit-process-buffer nil t))) - (with-current-buffer buf - (goto-char (1- (point-max))) - (let* ((inhibit-read-only t) - (magit-with-section--parent magit-root-section) - ;; Kids, don't do this ^^^^ at home. - (s (magit-with-section - (section process nil - (mapconcat 'identity (cons program args) " ")) - (insert "\n")))) - (set-marker-insertion-type (magit-section-content-beginning s) nil) - (insert "\n") - (backward-char 2) - (cons (current-buffer) s))))) - -(defun magit-process-truncate-log (buffer) - (with-current-buffer buffer - (let* ((head nil) - (tail (magit-section-children magit-root-section)) - (count (length tail))) - (when (> (1+ count) magit-process-log-max) - (while (and (cdr tail) - (> count (/ magit-process-log-max 2))) - (let* ((inhibit-read-only t) - (section (car tail)) - (process (magit-section-process section))) - (cond ((not process)) - ((memq (process-status process) '(exit signal)) - (delete-region (magit-section-beginning section) - (1+ (magit-section-end section))) - (cl-decf count)) - (t - (push section head)))) - (pop tail)) - (setf (magit-section-children magit-root-section) - (nconc (reverse head) tail)))))) - -(defun magit-process-sentinel (process event) - "Default sentinel used by `magit-start-process'." - (when (memq (process-status process) '(exit signal)) - (setq event (substring event 0 -1)) - (magit-process-unset-mode-line) - (when (string-match "^finished" event) - (message (concat (capitalize (process-name process)) " finished"))) - (magit-process-finish process) - (when (eq process magit-this-process) - (setq magit-this-process nil)) - (magit-refresh (and (buffer-live-p (process-get process 'command-buf)) - (process-get process 'command-buf))))) - -(defun magit-process-filter (proc string) - "Default filter used by `magit-start-process'." - (with-current-buffer (process-buffer proc) - (let ((inhibit-read-only t)) - (magit-process-yes-or-no-prompt proc string) - (magit-process-username-prompt proc string) - (magit-process-password-prompt proc string) - (goto-char (process-mark proc)) - (setq string (propertize string 'invisible - (magit-section-hidden - (process-get proc 'section)))) - ;; Find last ^M in string. If one was found, ignore everything - ;; before it and delete the current line. - (let ((ret-pos (length string))) - (while (and (>= (setq ret-pos (1- ret-pos)) 0) - (/= ?\r (aref string ret-pos)))) - (cond ((>= ret-pos 0) - (goto-char (line-beginning-position)) - (delete-region (point) (line-end-position)) - (insert-and-inherit (substring string (+ ret-pos 1)))) - (t - (insert-and-inherit string)))) - (set-marker (process-mark proc) (point))))) - -(defun magit-process-logfile-filter (process string) - "Special filter used by `magit-run-git-with-logfile'." - (magit-process-filter process string) - (let ((file (process-get process 'logfile))) - (with-temp-file file - (when (file-exists-p file) - (insert-file-contents file) - (goto-char (point-max))) - (insert string) - (write-region (point-min) (point-max) file)))) - -(defun magit-process-yes-or-no-prompt (proc string) - "Forward yes-or-no prompts to the user." - (let ((beg (string-match magit-process-yes-or-no-prompt-regexp string)) - (max-mini-window-height 30)) - (when beg - (process-send-string - proc - (downcase - (concat (match-string (if (yes-or-no-p (substring string 0 beg)) 1 2) - string) - "\n")))))) - -(defun magit-process-password-prompt (proc string) - "Forward password prompts to the user." - (let ((prompt (magit-process-match-prompt - magit-process-password-prompt-regexps string))) - (when prompt - (process-send-string proc (concat (read-passwd prompt) "\n"))))) - -(defun magit-process-username-prompt (proc string) - "Forward username prompts to the user." - (let ((prompt (magit-process-match-prompt - magit-process-username-prompt-regexps string))) - (when prompt - (process-send-string proc - (concat (read-string prompt nil nil - (user-login-name)) - "\n"))))) - -(defun magit-process-match-prompt (prompts string) - (when (cl-find-if (lambda (regex) - (string-match regex string)) - prompts) - (let ((prompt (match-string 0 string))) - (cond ((string-match ": $" prompt) prompt) - ((string-match ":$" prompt) (concat prompt " ")) - (t (concat prompt ": ")))))) - -(defun magit-process-wait () - (while (and magit-this-process - (eq (process-status magit-this-process) 'run)) - (sit-for 0.1 t))) - -(defun magit-process-set-mode-line (program args) - (when (equal program magit-git-executable) - (setq args (nthcdr (1+ (length magit-git-standard-options)) args))) - (magit-map-magit-buffers - (apply-partially (lambda (s) - (setq mode-line-process s)) - (concat " " program - (and args (concat " " (car args))))))) - -(defun magit-process-unset-mode-line () - (magit-map-magit-buffers (lambda () (setq mode-line-process nil)))) - -(defvar magit-process-error-message-re - (concat "^\\(?:error\\|fatal\\|git\\): \\(.*\\)" paragraph-separate)) - -(defun magit-process-finish (arg &optional process-buf command-buf - default-dir section) - (unless (integerp arg) - (setq process-buf (process-buffer arg) - command-buf (process-get arg 'command-buf) - default-dir (process-get arg 'default-dir) - section (process-get arg 'section) - arg (process-exit-status arg))) - (when (featurep 'dired) - (dired-uncache default-dir)) - (when (buffer-live-p process-buf) - (with-current-buffer process-buf - (let ((inhibit-read-only t) - (mark (magit-section-beginning section))) - (set-marker-insertion-type mark nil) - (goto-char mark) - (insert (propertize (format "%3s " arg) - 'magit-section section - 'face (if (= arg 0) - 'magit-process-ok - 'magit-process-ng)))))) - (unless (= arg 0) - (message ; `error' would prevent refresh - "%s ... [%s buffer %s for details]" - (or (and (buffer-live-p process-buf) - (with-current-buffer process-buf - (save-excursion - (goto-char (magit-section-end section)) - (when (re-search-backward - magit-process-error-message-re - (magit-section-content-beginning section) - t) - (match-string 1))))) - "Git failed") - (let ((key (and (buffer-live-p command-buf) - (with-current-buffer command-buf - (car (where-is-internal - 'magit-process-display-buffer)))))) - (if key (format "Hit %s to see" (key-description key)) "See")) - (buffer-name process-buf))) - arg) - -(defun magit-process-display-buffer (process) - (when (process-live-p process) - (let ((buf (process-buffer process))) - (cond ((not (buffer-live-p buf))) - ((= magit-process-popup-time 0) - (pop-to-buffer buf)) - ((> magit-process-popup-time 0) - (run-with-timer magit-process-popup-time nil - (lambda (p) - (when (eq (process-status p) 'run) - (let ((buf (process-buffer p))) - (when (buffer-live-p buf) - (pop-to-buffer buf))))) - process)))))) - -(defun magit-process-quote-arguments (args) - "Quote each argument in list ARGS as an argument to Git. -Except when `magit-process-quote-curly-braces' is non-nil ARGS is -returned unchanged. This is required to works around strangeness -of the Windows \"Powershell\"." - (if magit-process-quote-curly-braces - (mapcar (apply-partially 'replace-regexp-in-string - "{\\([0-9]+\\)}" "\\\\{\\1\\\\}") - (magit-flatten-onelevel args)) - args)) - -;;;; Mode Api -;;;;; Mode Foundation - -(define-derived-mode magit-mode special-mode "Magit" - "Parent major mode from which Magit major modes inherit. - -Please see the manual for a complete description of Magit. - -\\{magit-mode-map}" - (buffer-disable-undo) - (setq truncate-lines t) - (add-hook 'pre-command-hook #'magit-remember-point nil t) - (add-hook 'post-command-hook #'magit-correct-point-after-command t t) - (add-hook 'post-command-hook #'magit-highlight-section t t) - ;; Emacs' normal method of showing trailing whitespace gives weird - ;; results when `magit-whitespace-warning-face' is different from - ;; `trailing-whitespace'. - (when (and magit-highlight-whitespace - magit-highlight-trailing-whitespace) - (setq show-trailing-whitespace nil))) - -(defvar-local magit-refresh-function nil) -(put 'magit-refresh-function 'permanent-local t) - -(defvar-local magit-refresh-args nil) -(put 'magit-refresh-args 'permanent-local t) - -(defmacro magit-mode-setup - (buffer switch-func mode refresh-func &rest refresh-args) - "Display and select BUFFER, turn on MODE, and refresh a first time. -Display BUFFER using `magit-mode-display-buffer', then turn on -MODE in BUFFER, set the local value of `magit-refresh-function' -to REFRESH-FUNC and that of `magit-refresh-args' to REFRESH-ARGS -and finally \"refresh\" a first time. All arguments are -evaluated before switching to BUFFER." - (let ((mode-symb (cl-gensym "mode-symb")) - (toplevel (cl-gensym "toplevel")) - (init-args (cl-gensym "init-args")) - (buf-symb (cl-gensym "buf-symb"))) - `(let* ((,mode-symb ,mode) - (,toplevel (magit-get-top-dir)) - (,init-args (list ,mode-symb ,refresh-func ,@refresh-args)) - (,buf-symb (magit-mode-display-buffer - ,buffer ,mode-symb ,switch-func))) - (if ,toplevel - (with-current-buffer ,buf-symb - (apply #'magit-mode-init ,toplevel ,init-args)) - (user-error "Not inside a Git repository"))))) - -(defun magit-mode-init (dir mode refresh-func &rest refresh-args) - "Turn on MODE and refresh in the current buffer. -Turn on MODE, set the local value of `magit-refresh-function' to -REFRESH-FUNC and that of `magit-refresh-args' to REFRESH-ARGS and -finally \"refresh\" a first time. - -Also see `magit-mode-setup', a more convenient variant." - (cl-case mode - (magit-commit-mode - (magit-setup-xref (cons #'magit-show-commit refresh-args)) - (goto-char (point-min))) - (magit-diff-mode - (magit-setup-xref (cons #'magit-diff refresh-args)) - (goto-char (point-min)))) - (setq default-directory dir - magit-refresh-function refresh-func - magit-refresh-args refresh-args) - (funcall mode) - (magit-mode-refresh-buffer)) - -(defvar-local magit-previous-window-configuration nil) -(put 'magit-previous-window-configuration 'permanent-local t) - -(defun magit-mode-display-buffer (buffer mode &optional switch-function) - "Display BUFFER in some window and select it. -BUFFER may be a buffer or a string, the name of a buffer. Return -the buffer. - -Unless BUFFER is already displayed in the selected frame store the -previous window configuration as a buffer local value, so that it -can later be restored by `magit-mode-quit-window'. - -Then display and select BUFFER using SWITCH-FUNCTION. If that is -nil either use `pop-to-buffer' if the current buffer's major mode -derives from Magit mode; or else use `switch-to-buffer'. - -This is only intended for buffers whose major modes derive from -Magit mode." - (cond ((stringp buffer) - (setq buffer (magit-mode-get-buffer-create buffer mode))) - ((not (bufferp buffer)) - (signal 'wrong-type-argument (list 'bufferp nil)))) - (unless (get-buffer-window buffer (selected-frame)) - (with-current-buffer (get-buffer-create buffer) - (setq magit-previous-window-configuration - (current-window-configuration)))) - (funcall (or switch-function - (if (derived-mode-p 'magit-mode) - 'switch-to-buffer - 'pop-to-buffer)) - buffer) - buffer) - -(defun magit-mode-get-buffer (format mode &optional topdir create) - (if (not (string-match-p "%[Tt]" format)) - (funcall (if create #'get-buffer-create #'get-buffer) format) - (unless topdir - (setq topdir (magit-get-top-dir))) - (let ((name (format-spec - format `((?T . ,topdir) - (?t . ,(file-name-nondirectory - (directory-file-name topdir))))))) - (or (cl-find-if - (lambda (buf) - (with-current-buffer buf - (and (or (not mode) (eq major-mode mode)) - (equal (expand-file-name default-directory) topdir) - (string-match-p (format "^%s\\(?:<[0-9]+>\\)?$" - (regexp-quote name)) - (buffer-name))))) - (buffer-list)) - (and create (generate-new-buffer name)))))) - -(defun magit-mode-get-buffer-create (format mode &optional topdir) - (magit-mode-get-buffer format mode topdir t)) - -(cl-defun magit-mode-refresh-buffer (&optional (buffer (current-buffer))) - (with-current-buffer buffer - (when magit-refresh-function - (let* ((old-line (line-number-at-pos)) - (old-point (point)) - (old-section (magit-current-section)) - (old-path (and old-section - (magit-section-path (magit-current-section))))) - (beginning-of-line) - (let ((inhibit-read-only t) - (section-line (and old-section - (count-lines - (magit-section-beginning old-section) - (point)))) - (line-char (- old-point (point)))) - (erase-buffer) - (apply magit-refresh-function - magit-refresh-args) - (let ((s (and old-path (magit-find-section old-path magit-root-section)))) - (cond (s - (goto-char (magit-section-beginning s)) - (forward-line section-line) - (forward-char line-char)) - (t - (save-restriction - (widen) - (goto-char (point-min)) - (forward-line (1- old-line))))))) - (magit-highlight-section) - (magit-refresh-marked-commits-in-buffer))))) - -(defun magit-mode-quit-window (&optional kill-buffer) - "Bury the current buffer and delete its window. -With a prefix argument, kill the buffer instead. - -If `magit-restore-window-configuration' is non-nil and the last -configuration stored by `magit-mode-display-buffer' originates -from the selected frame then restore it after burrying/killing -the buffer. Finally reset the window configuration to nil." - (interactive "P") - (let ((winconf magit-previous-window-configuration) - (buffer (current-buffer)) - (frame (selected-frame))) - (quit-window kill-buffer (selected-window)) - (when winconf - (when (and magit-restore-window-configuration - (equal frame (window-configuration-frame winconf))) - (set-window-configuration winconf) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (setq magit-previous-window-configuration nil))))) - (run-hook-with-args 'magit-mode-quit-window-hook buffer))) - -;;;;; Mode Utilities - -(defun magit-map-magit-buffers (func &optional dir) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (and (derived-mode-p 'magit-mode) - (or (null dir) - (equal default-directory dir))) - (funcall func))))) - -;;;;; (section kludges) - -(defvar-local magit-last-point nil) -(put 'magit-last-point 'permanent-local t) - -(defun magit-remember-point () - (setq magit-last-point (point))) - -(defun magit-invisible-region-end (pos) - (while (and (not (= pos (point-max))) (invisible-p pos)) - (setq pos (next-char-property-change pos))) - pos) - -(defun magit-invisible-region-start (pos) - (while (and (not (= pos (point-min))) (invisible-p pos)) - (setq pos (1- (previous-char-property-change pos)))) - pos) - -(defun magit-correct-point-after-command () - "Move point outside of invisible regions. - -Emacs often leaves point in invisible regions, it seems. To fix -this, we move point ourselves and never let Emacs do its own -adjustments. - -When point has to be moved out of an invisible region, it can be -moved to its end or its beginning. We usually move it to its -end, except when that would move point back to where it was -before the last command." - (when (invisible-p (point)) - (let ((end (magit-invisible-region-end (point)))) - (goto-char (if (= end magit-last-point) - (magit-invisible-region-start (point)) - end)))) - (setq disable-point-adjustment t)) - -;;;;; Buffer History - -(defun magit-go-backward () - "Move backward in current buffer's history." - (interactive) - (if help-xref-stack - (help-xref-go-back (current-buffer)) - (user-error "No previous entry in buffer's history"))) - -(defun magit-go-forward () - "Move forward in current buffer's history." - (interactive) - (if help-xref-forward-stack - (help-xref-go-forward (current-buffer)) - (user-error "No next entry in buffer's history"))) - -(defun magit-xref-insert-buttons () - (when (and (or (eq magit-show-xref-buttons t) - (apply 'derived-mode-p magit-show-xref-buttons)) - (or help-xref-stack help-xref-forward-stack)) - (insert "\n") - (when help-xref-stack - (magit-xref-insert-button help-back-label - 'magit-xref-backward)) - (when help-xref-forward-stack - (when help-xref-stack - (insert " ")) - (magit-xref-insert-button help-forward-label - 'magit-xref-forward)))) - -(defun magit-xref-insert-button (label type) - (magit-with-section (section button label) - (insert-text-button label 'type type - 'help-args (list (current-buffer))))) - -(define-button-type 'magit-xref-backward - :supertype 'help-back - 'mouse-face magit-item-highlight-face - 'help-echo (purecopy "mouse-2, RET: go back to previous history entry")) - -(define-button-type 'magit-xref-forward - :supertype 'help-forward - 'mouse-face magit-item-highlight-face - 'help-echo (purecopy "mouse-2, RET: go back to next history entry")) - -(defun magit-setup-xref (item) - (when help-xref-stack-item - (push (cons (point) help-xref-stack-item) help-xref-stack) - (setq help-xref-forward-stack nil)) - (when (called-interactively-p 'interactive) - (let ((tail (nthcdr 10 help-xref-stack))) - (if tail (setcdr tail nil)))) - (setq help-xref-stack-item item)) - -;;;;; Refresh Machinery - -(defun magit-refresh (&optional buffer) - "Refresh some buffers belonging to the current repository. - -Refresh the current buffer if its major mode derives from -`magit-mode', and refresh the corresponding status buffer. -If the global `magit-auto-revert-mode' is turned on, then -also revert all unmodified buffers that visit files being -tracked in the current repository." - (interactive (list (current-buffer))) - (unless buffer - (setq buffer (current-buffer))) - (with-current-buffer buffer - (when (derived-mode-p 'magit-mode) - (magit-mode-refresh-buffer buffer)) - (let (status) - (when (and (not (eq major-mode 'magit-status-mode)) - (setq status (magit-mode-get-buffer - magit-status-buffer-name - 'magit-status-mode))) - (magit-mode-refresh-buffer status)))) - (when magit-auto-revert-mode - (magit-revert-buffers))) - -(defun magit-refresh-all () - "Refresh all buffers belonging to the current repository. - -Refresh all Magit buffers belonging to the current repository. -If the global `magit-auto-revert-mode' is turned on, then also -revert all unmodified buffers that visit files being tracked in -the current repository." - (interactive) - (magit-map-magit-buffers #'magit-mode-refresh-buffer default-directory) - (magit-revert-buffers)) - -(defun magit-revert-buffers () - (let ((topdir (magit-get-top-dir))) - (when topdir - (let ((gitdir (magit-git-dir)) - (tracked (magit-git-lines "ls-tree" "-r" "--name-only" "HEAD"))) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (let ((file (buffer-file-name))) - (and file (string-prefix-p topdir file) - (not (string-prefix-p gitdir file)) - (member (file-relative-name file topdir) tracked) - (let ((auto-revert-mode t)) - (auto-revert-handler) - (run-hooks 'magit-revert-buffer-hook)))))))))) - -;;; (misplaced) -;;;; Diff Options - -(defvar magit-diff-context-lines 3) - -(defun magit-diff-U-arg () - (format "-U%d" magit-diff-context-lines)) - -(defun magit-diff-smaller-hunks (&optional count) - "Decrease the context for diff hunks by COUNT." - (interactive "p") - (setq magit-diff-context-lines (max 0 (- magit-diff-context-lines count))) - (magit-refresh)) - -(defun magit-diff-larger-hunks (&optional count) - "Increase the context for diff hunks by COUNT." - (interactive "p") - (setq magit-diff-context-lines (+ magit-diff-context-lines count)) - (magit-refresh)) - -(defun magit-diff-default-hunks () - "Reset context for diff hunks to the default size." - (interactive) - (setq magit-diff-context-lines 3) - (magit-refresh)) - -(defun magit-set-diff-options () - "Set local `magit-diff-options' based on popup state. -And refresh the current Magit buffer." - (interactive) - (setq-local magit-diff-options magit-custom-options) - (magit-refresh)) - -;; `magit-set-default-diff-options' is defined in "Options"/"Setters". - -(defun magit-save-default-diff-options () - "Set and save the default for `magit-diff-options' based on popup value. -Also set the local value in all Magit buffers and refresh them." - (interactive) - (customize-save-variable 'magit-diff-options magit-custom-options)) - -(defun magit-reset-diff-options () - "Reset local `magit-diff-options' to default value. -And refresh the current Magit buffer." - (interactive) - (setq-local magit-diff-options (default-value 'magit-diff-options)) - (magit-refresh)) - -;;;; Raw Diff Washing - -(defun magit-insert-diff (section file status) - (let ((beg (point))) - (apply 'magit-git-insert "-c" "diff.submodule=short" "diff" - `(,(magit-diff-U-arg) ,@magit-diff-options "--" ,file)) - (unless (eq (char-before) ?\n) - (insert "\n")) - (save-restriction - (narrow-to-region beg (point)) - (goto-char beg) - (magit-wash-diff-section section) - (goto-char (point-max))))) - -(defun magit-wash-raw-diffs (&optional staged) - (let (previous) - (magit-wash-sequence - (lambda () - (setq previous (magit-wash-raw-diff previous staged)))))) - -(defun magit-wash-raw-diff (previous staged) - (when (looking-at - ":\\([0-7]+\\) \\([0-7]+\\) [0-9a-f]+ [0-9a-f]+ \\(.\\)[0-9]*\t\\([^\t\n]+\\)$") - (let ((file (magit-decode-git-path (match-string-no-properties 4))) - (status (cl-ecase (string-to-char (match-string-no-properties 3)) - (?A 'new) - (?C 'copy) - (?D 'deleted) - (?M 'modified) - (?T 'typechange) - (?U 'unmerged) - (?X 'unknown)))) - (delete-region (point) (1+ (line-end-position))) - (unless (or ;; Unmerged files get two entries; we ignore the second. - (equal file previous) - ;; Ignore staged, unmerged files. - (and staged (eq status 'unmerged))) - (magit-with-section (section diff file nil nil - (not (derived-mode-p - 'magit-diff-mode - 'magit-commit-mode))) - (if (not (magit-section-hidden section)) - (magit-insert-diff section file status) - (setf (magit-section-diff-status section) status) - (setf (magit-section-needs-refresh-on-show section) t) - (magit-insert-diff-title status file nil)))) - file))) - -;;; Modes (1) -;;;; Commit Mode -;;;;; Commit Core - -(define-derived-mode magit-commit-mode magit-mode "Magit" - "Mode for looking at a git commit. - -\\Type `\\[magit-visit-item]` to visit the changed file, \ -`\\[magit-toggle-section]` to hide or show a hunk, -`\\[magit-diff-larger-hunks]` and `\\[magit-diff-smaller-hunks]` to change the \ -size of the hunks. -Type `\\[magit-apply-item]` to apply a change to your worktree and \ -`\\[magit-revert-item]` to reverse it. - -\\{magit-commit-mode-map} -Unless shadowed by the mode specific bindings above, bindings -from the parent keymap `magit-mode-map' are also available." - :group 'magit) - -(defvar magit-commit-buffer-name "*magit-commit*" - "Name of buffer used to display a commit.") - -;;;###autoload -(defun magit-show-commit (commit &optional noselect) - "Show information about COMMIT." - (interactive (list (magit-read-rev-with-default - "Show commit (hash or ref)"))) - (when (magit-git-failure "cat-file" "commit" commit) - (user-error "%s is not a commit" commit)) - (magit-mode-setup magit-commit-buffer-name - (if noselect 'display-buffer 'pop-to-buffer) - #'magit-commit-mode - #'magit-refresh-commit-buffer - commit)) - -(defun magit-show-item-or-scroll-up () - "Update commit or status buffer for item at point. - -Either show the commit or stash at point in another buffer, -or if that buffer is already displayed in the current frame -and contains information about that commit or stash, then -instead scroll the buffer up. If there is no commit or -stash at point, then prompt for a commit." - (interactive) - (magit-show-item-or-scroll 'scroll-up)) - -(defun magit-show-item-or-scroll-down () - "Update commit or status buffer for item at point. - -Either show the commit or stash at point in another buffer, -or if that buffer is already displayed in the current frame -and contains information about that commit or stash, then -instead scroll the buffer down. If there is no commit or -stash at point, then prompt for a commit." - (interactive) - (magit-show-item-or-scroll 'scroll-down)) - -(defun magit-show-item-or-scroll (fn) - (let (rev cmd buf win) - (magit-section-case (info) - (commit (setq rev info - cmd 'magit-show-commit - buf magit-commit-buffer-name)) - (stash (setq rev info - cmd 'magit-diff-stash - buf magit-stash-buffer-name))) - (if rev - (if (and (setq buf (get-buffer buf)) - (setq win (get-buffer-window buf)) - (with-current-buffer buf - (equal rev (car magit-refresh-args)))) - (with-selected-window win - (condition-case err - (funcall fn) - (error - (goto-char (cl-case fn - (scroll-up (point-min)) - (scroll-down (point-max))))))) - (funcall cmd rev t)) - (call-interactively 'magit-show-commit)))) - -(defun magit-refresh-commit-buffer (commit) - (magit-git-insert-section (commitbuf nil) - #'magit-wash-commit - "log" "-1" "--decorate=full" - "--pretty=medium" (magit-diff-U-arg) - "--cc" "-p" (and magit-show-diffstat "--stat") - magit-diff-options commit)) - -;;;;; Commit Washing - -(defun magit-wash-commit () - (looking-at "^commit \\([a-z0-9]+\\)\\(?: \\(.+\\)\\)?$") - (let ((rev (match-string 1)) - (refs (match-string 2))) - (delete-region (point) (1+ (line-end-position))) - (magit-with-section - (section headers 'headers - (concat (propertize rev 'face 'magit-log-sha1) - (and refs (concat " "(magit-format-ref-labels refs))) - "\n")) - (while (re-search-forward "^\\([a-z]+\\): +\\(.+\\)$" nil t) - (when (string-match-p (match-string 1) "Merge") - (let ((revs (match-string 2))) - (delete-region (match-beginning 2) (match-end 2)) - (dolist (rev (split-string revs)) - (magit-insert-commit-button rev) - (insert ?\s))))) - (forward-line))) - (forward-line) - (let ((bound (save-excursion - (when (re-search-forward "^diff" nil t) - (copy-marker (match-beginning 0))))) - (summary (buffer-substring-no-properties - (point) (line-end-position)))) - (delete-region (point) (1+ (line-end-position))) - (magit-with-section (section message 'message (concat summary "\n")) - (cond ((re-search-forward "^---" bound t) - (goto-char (match-beginning 0)) - (delete-region (match-beginning 0) (match-end 0))) - ((re-search-forward "^.[^ ]" bound t) - (goto-char (1- (match-beginning 0))))))) - (forward-line) - (when magit-show-diffstat - (magit-wash-diffstats)) - (forward-line) - (magit-wash-diffs)) - -(defun magit-insert-commit-button (hash) - (magit-with-section (section commit hash) - (insert-text-button hash - 'help-echo "Visit commit" - 'action (lambda (button) - (save-excursion - (goto-char button) - (magit-visit-item))) - 'follow-link t - 'mouse-face magit-item-highlight-face - 'face 'magit-log-sha1))) - -;;;; Status Mode - -(define-derived-mode magit-status-mode magit-mode "Magit" - "Mode for looking at git status. - -\\Type `\\[magit-stage-item]` to stage (add) an item, \ -`\\[magit-unstage-item]` to unstage it. -Type `\\[magit-key-mode-popup-committing]` to have a popup to commit, type \ -`\\[magit-key-mode-popup-dispatch]` to see others -available popup. -Type `\\[magit-visit-item]` to visit something, and \ -`\\[magit-toggle-section]` to show or hide section. - -More information can be found in Info node `(magit)Status' - -Other key binding: -\\{magit-status-mode-map}" - :group 'magit) - -(defvar magit-status-buffer-name "*magit: %t*" - "Name of buffer used to display a repository's status.") - -;;;###autoload -(defun magit-status (dir &optional switch-function) - "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. - -Depending on option `magit-status-buffer-switch-function' the -status buffer is shown in another window (the default) or the -current window. Non-interactively optional SWITCH-FUNCTION -can be used to override this." - (interactive (list (if current-prefix-arg - (magit-read-top-dir - (> (prefix-numeric-value current-prefix-arg) - 4)) - (or (magit-get-top-dir) - (magit-read-top-dir nil))))) - (magit-save-some-buffers) - (let ((topdir (magit-get-top-dir dir))) - (when (or topdir - (and (yes-or-no-p - (format "There is no Git repository in %s. Create one? " - dir)) - (progn - (magit-init dir) - (setq topdir (magit-get-top-dir dir))))) - (let ((default-directory topdir)) - (magit-mode-setup magit-status-buffer-name - (or switch-function - magit-status-buffer-switch-function) - #'magit-status-mode - #'magit-refresh-status))))) - -(defalias 'magit-status-internal 'magit-status) ; forward compatibility - -(defun magit-refresh-status () - (magit-git-exit-code "update-index" "--refresh") - (magit-with-section (section status 'status nil t) - (run-hooks 'magit-status-sections-hook)) - (run-hooks 'magit-refresh-status-hook)) - -;;; Sections -;;;; Real Sections - -(defun magit-insert-stashes () - ;; #1427 Set log.date to work around an issue in Git <1.7.10.3. - (let ((stashes (magit-git-lines "-c" "log.date=default" "stash" "list"))) - (when stashes - (magit-with-section (section stashes 'stashes "Stashes:" t) - (dolist (stash stashes) - (string-match "^\\(stash@{\\([0-9]+\\)}\\): \\(.+\\)$" stash) - (let ((stash (match-string 1 stash)) - (number (match-string 2 stash)) - (message (match-string 3 stash))) - (magit-with-section (section stash stash) - (insert number ": " message "\n")))) - (insert "\n"))))) - -(defun magit-insert-untracked-files () - (magit-with-section (section untracked 'untracked "Untracked files:" t) - (let ((files (cl-mapcan - (lambda (f) - (when (eq (aref f 0) ??) (list f))) - (magit-git-lines - "status" "--porcelain")))) - (if (not files) - (setq section nil) - (dolist (file files) - (setq file (magit-decode-git-path (substring file 3))) - (magit-with-section (section file file) - (insert "\t" file "\n"))) - (insert "\n"))))) - -(defun magit-insert-pending-commits () - (let* ((info (magit-read-rewrite-info)) - (pending (cdr (assq 'pending info)))) - (when pending - (magit-with-section (section pending 'pending "Pending commits:" t) - (dolist (p pending) - (let* ((commit (car p)) - (properties (cdr p)) - (used (plist-get properties 'used))) - (magit-with-section (section commit commit) - (insert (magit-git-string - "log" "-1" - (if used - "--pretty=format:. %s" - "--pretty=format:* %s") - commit "--") - "\n"))))) - (insert "\n")))) - -(defun magit-insert-unstaged-changes () - (let ((magit-current-diff-range (cons 'index 'working)) - (magit-diff-options (copy-sequence magit-diff-options))) - (magit-git-insert-section (unstaged "Unstaged changes:") - #'magit-wash-raw-diffs - "diff-files"))) - -(defun magit-insert-staged-changes () - (let ((no-commit (not (magit-git-success "log" "-1" "HEAD")))) - (when (or no-commit (magit-anything-staged-p)) - (let ((magit-current-diff-range (cons "HEAD" 'index)) - (base (if no-commit - (magit-git-string "mktree") - "HEAD")) - (magit-diff-options (append '("--cached") magit-diff-options))) - (magit-git-insert-section (staged "Staged changes:") - (apply-partially #'magit-wash-raw-diffs t) - "diff-index" "--cached" base))))) - -(defun magit-insert-unpulled-or-recent-commits () - (let ((tracked (magit-get-tracked-branch nil t))) - (if (and tracked - (not (equal (magit-git-string "rev-parse" "HEAD") - (magit-git-string "rev-parse" tracked)))) - (magit-insert-unpulled-commits) - (magit-git-insert-section (recent "Recent commits:") - (apply-partially 'magit-wash-log 'unique) - "log" "--format=format:%h %s" "-n" "10")))) - -(defun magit-insert-unpulled-commits () - (let ((tracked (magit-get-tracked-branch nil t))) - (when tracked - (magit-git-insert-section (unpulled "Unpulled commits:") - (apply-partially 'magit-wash-log 'unique) - "log" "--format=format:%h %s" (concat "HEAD.." tracked))))) - -(defun magit-insert-unpushed-commits () - (let ((tracked (magit-get-tracked-branch nil t))) - (when tracked - (magit-git-insert-section (unpushed "Unpushed commits:") - (apply-partially 'magit-wash-log 'unique) - "log" "--format=format:%h %s" (concat tracked "..HEAD"))))) - -(defun magit-insert-unpulled-cherries () - (let ((tracked (magit-get-tracked-branch nil t))) - (when tracked - (magit-git-insert-section (unpulled "Unpulled commits:") - (apply-partially 'magit-wash-log 'cherry) - "cherry" "-v" (magit-abbrev-arg) (magit-get-current-branch) tracked)))) - -(defun magit-insert-unpushed-cherries () - (let ((tracked (magit-get-tracked-branch nil t))) - (when tracked - (magit-git-insert-section (unpushed "Unpushed commits:") - (apply-partially 'magit-wash-log 'cherry) - "cherry" "-v" (magit-abbrev-arg) tracked)))) - -;;;; Line Sections - -(defun magit-insert-empty-line () - (insert "\n")) - -(defun magit-insert-status-local-line () - (let ((branch (or (magit-get-current-branch) "(detached)"))) - (magit-insert-line-section (branch branch) - (concat "Local: " - (propertize branch 'face 'magit-branch) - " " (abbreviate-file-name default-directory))))) - -(defun magit-insert-status-remote-line () - (let* ((branch (magit-get-current-branch)) - (tracked (magit-get-tracked-branch branch))) - (when tracked - (magit-insert-line-section (branch tracked) - (concat "Remote: " - (and (magit-get-boolean "branch" branch "rebase") "onto ") - (magit-format-tracked-line tracked branch)))))) - -(defun magit-format-tracked-line (tracked branch) - (when tracked - (setq tracked (propertize tracked 'face 'magit-branch)) - (let ((remote (magit-get "branch" branch "remote"))) - (concat (if (string= "." remote) - (concat "branch " tracked) - (when (string-match (concat "^" remote) tracked) - (setq tracked (substring tracked (1+ (length remote))))) - (concat tracked " @ " remote - " (" (magit-get "remote" remote "url") ")")))))) - -(defun magit-insert-status-head-line () - (let ((hash (magit-git-string "rev-parse" "--verify" "HEAD"))) - (if hash - (magit-insert-line-section (commit hash) - (concat "Head: " (magit-format-rev-summary "HEAD"))) - (magit-insert-line-section (no-commit) - "Head: nothing committed yet")))) - -(defun magit-insert-status-tags-line () - (let* ((current-tag (magit-get-current-tag t)) - (next-tag (magit-get-next-tag t)) - (both-tags (and current-tag next-tag t)) - (tag-subject (eq magit-status-tags-line-subject 'tag))) - (when (or current-tag next-tag) - (magit-insert-line-section (line) - (concat - (if both-tags "Tags: " "Tag: ") - (and current-tag (apply 'magit-format-status-tag-sentence - tag-subject current-tag)) - (and both-tags ", ") - (and next-tag (apply 'magit-format-status-tag-sentence - (not tag-subject) next-tag))))))) - -(defun magit-format-status-tag-sentence (behindp tag cnt &rest ignored) - (concat (propertize tag 'face 'magit-tag) - (and (> cnt 0) - (concat (if (eq magit-status-tags-line-subject 'tag) - (concat " (" (propertize (format "%s" cnt) - 'face 'magit-branch)) - (format " (%i" cnt)) - " " (if behindp "behind" "ahead") ")")))) - -;;;; Progress Sections - -(defun magit-insert-status-merge-line () - (let ((heads (magit-file-lines (magit-git-dir "MERGE_HEAD")))) - (when heads - (magit-insert-line-section (line) - (concat - "Merging: " - (mapconcat 'identity (mapcar 'magit-name-rev heads) ", ") - (and magit-status-show-sequence-help - "; Resolve conflicts, or press \"m A\" to Abort")))))) - -(defun magit-insert-status-rebase-lines () - (let ((rebase (magit-rebase-info))) - (when rebase - (magit-insert-line-section (line) - (concat (if (nth 4 rebase) "Applying" "Rebasing") - (apply 'format ": onto %s (%s of %s)" rebase) - (and magit-status-show-sequence-help - "; Press \"R\" to Abort, Skip, or Continue"))) - (when (and (null (nth 4 rebase)) (nth 3 rebase)) - (magit-insert-line-section (line) - (concat "Stopped: " - (magit-format-rev-summary (nth 3 rebase)))))))) - -(defun magit-insert-rebase-sequence () - (let ((f (magit-git-dir "rebase-merge/git-rebase-todo"))) - (when (file-exists-p f) - (magit-with-section (section rebase-todo 'rebase-todo "Rebasing:" t) - (cl-loop - for line in (magit-file-lines f) - when (string-match - "^\\(pick\\|reword\\|edit\\|squash\\|fixup\\) \\([^ ]+\\) \\(.*\\)$" - line) - do (let ((cmd (match-string 1 line)) - (hash (match-string 2 line)) - (msg (match-string 3 line))) - (magit-with-section (section commit hash) - (insert cmd " ") - (insert (propertize - (magit-git-string "rev-parse" "--short" hash) - 'face 'magit-log-sha1)) - (insert " " msg "\n")))) - (insert "\n"))))) - -(defun magit-insert-bisect-output () - (when (magit-bisecting-p) - (let ((lines - (or (magit-file-lines (magit-git-dir "BISECT_CMD_OUTPUT")) - (list "Bisecting: (no saved bisect output)" - "It appears you have invoked `git bisect' from a shell." - "There is nothing wrong with that, we just cannot display" - "anything useful here. Consult the shell output instead."))) - (done-re "^[a-z0-9]\\{40\\} is the first bad commit$")) - (magit-with-section - (section bisect-output 'bisect-output - (propertize - (or (and (string-match done-re (car lines)) (pop lines)) - (cl-find-if (apply-partially 'string-match done-re) - lines) - (pop lines)) - 'face 'magit-section-title) - t t) - (dolist (line lines) - (insert line "\n")))) - (insert "\n"))) - -(defun magit-insert-bisect-rest () - (when (magit-bisecting-p) - (magit-git-insert-section (bisect-view "Bisect Rest:") - (apply-partially 'magit-wash-log 'bisect-vis) - "bisect" "visualize" "git" "log" - "--pretty=format:%h%d %s" "--decorate=full"))) - -(defun magit-insert-bisect-log () - (when (magit-bisecting-p) - (magit-git-insert-section (bisect-log "Bisect Log:") - #'magit-wash-bisect-log - "bisect" "log"))) - -(defun magit-wash-bisect-log () - (let (beg) - (while (progn (setq beg (point-marker)) - (re-search-forward "^\\(git bisect [^\n]+\n\\)" nil t)) - (let ((heading (match-string-no-properties 1))) - (delete-region (match-beginning 0) (match-end 0)) - (save-restriction - (narrow-to-region beg (point)) - (goto-char (point-min)) - (magit-with-section (section bisect-log 'bisect-log heading nil t) - (magit-wash-sequence - (apply-partially 'magit-wash-log-line 'bisect-log - (magit-abbrev-length))))))) - (when (re-search-forward - "# first bad commit: \\[\\([a-z0-9]\\{40\\}\\)\\] [^\n]+\n" nil t) - (let ((hash (match-string-no-properties 1))) - (delete-region (match-beginning 0) (match-end 0)) - (magit-with-section - (section 'bisect-log 'bisect-log - (concat hash " is the first bad commit\n"))))))) - -(defun magit-bisecting-p () - (file-exists-p (magit-git-dir "BISECT_LOG"))) - -;;; Utilities (2) -;;;; Save Buffers - -(defvar magit-default-directory nil) - -(defun magit-save-some-buffers (&optional msg pred topdir) - "Save some buffers if variable `magit-save-some-buffers' is non-nil. -If variable `magit-save-some-buffers' is set to `dontask' then -don't ask the user before saving the buffers, just go ahead and -do it. - -Optional argument MSG is displayed in the minibuffer if variable -`magit-save-some-buffers' is nil. - -Optional second argument PRED determines which buffers are considered: -If PRED is nil, all the file-visiting buffers are considered. -If PRED is t, then certain non-file buffers will also be considered. -If PRED is a zero-argument function, it indicates for each buffer whether -to consider it or not when called with that buffer current." - (interactive) - (let ((predicate-function (or pred magit-save-some-buffers-predicate)) - (magit-default-directory (or topdir default-directory))) - (if magit-save-some-buffers - (save-some-buffers - (eq magit-save-some-buffers 'dontask) - predicate-function) - (when msg - (message msg))))) - -(defun magit-save-buffers-predicate-all () - "Prompt to save all buffers with unsaved changes." - t) - -(defun magit-save-buffers-predicate-tree-only () - "Only prompt to save buffers which are within the current git project. -As determined by the directory passed to `magit-status'." - (and buffer-file-name - (let ((topdir (magit-get-top-dir magit-default-directory))) - (and topdir - (equal (file-remote-p topdir) (file-remote-p buffer-file-name)) - ;; ^ Avoid needlessly connecting to unrelated tramp remotes. - (string= topdir (magit-get-top-dir - (file-name-directory buffer-file-name))))))) - -;;; Porcelain -;;;; Apply -;;;;; Apply Commands -;;;;;; Apply - -(defun magit-apply-item () - "Apply the item at point to the current working tree." - (interactive) - (magit-section-action apply (info) - (([* unstaged] [* staged]) - (user-error "Change is already in your working tree")) - (hunk (magit-apply-hunk-item it)) - (diff (magit-apply-diff-item it)) - (stash (magit-stash-apply info)) - (commit (magit-apply-commit info)))) - -;;;;;; Stage - -(defun magit-stage-item (&optional file) - "Add the item at point to the staging area. -With a prefix argument, prompt for a file to be staged instead." - (interactive - (when current-prefix-arg - (list (file-relative-name (read-file-name "File to stage: " nil nil t) - (magit-get-top-dir))))) - (if file - (magit-run-git "add" file) - (magit-section-action stage (info) - ([file untracked] - (magit-run-git - (cond - ((use-region-p) - (cons "add" (magit-section-region-siblings #'magit-section-info))) - ((and (string-match-p "/$" info) - (file-exists-p (expand-file-name ".git" info))) - (let ((repo (read-string - "Add submodule tracking remote repo (empty to abort): " - (let ((default-directory - (file-name-as-directory - (expand-file-name info default-directory)))) - (magit-get "remote.origin.url"))))) - (if (equal repo "") - (user-error "Abort") - (list "submodule" "add" repo (substring info 0 -1))))) - (t - (list "add" info))))) - (untracked - (magit-run-git "add" "--" (magit-git-lines "ls-files" "--other" - "--exclude-standard"))) - ([hunk diff unstaged] - (magit-apply-hunk-item it "--cached")) - ([diff unstaged] - (magit-run-git "add" "-u" - (if (use-region-p) - (magit-section-region-siblings #'magit-section-info) - info))) - (unstaged - (magit-stage-all)) - ([* staged] - (user-error "Already staged")) - (hunk (user-error "Can't stage this hunk")) - (diff (user-error "Can't stage this diff"))))) - -;;;###autoload -(defun magit-stage-all (&optional including-untracked) - "Add all remaining changes in tracked files to staging area. -With a prefix argument, add remaining untracked files as well. -\('git add [-u] .')." - (interactive "P") - (when (or (not magit-stage-all-confirm) - (not (magit-anything-staged-p)) - (yes-or-no-p "Stage all changes? ")) - (if including-untracked - (magit-run-git "add" ".") - (magit-run-git "add" "-u" ".")))) - -;;;;;; Unstage - -(defun magit-unstage-item () - "Remove the item at point from the staging area." - (interactive) - (magit-section-action unstage (info) - ([hunk diff staged] - (magit-apply-hunk-item it "--reverse" "--cached")) - ([diff staged] - (when (eq info 'unmerged) - (user-error "Can't unstage an unmerged file. Resolve it first")) - (let ((files (if (use-region-p) - (magit-section-region-siblings #'magit-section-info) - (list info)))) - (if (magit-no-commit-p) - (magit-run-git "rm" "--cached" "--" files) - (magit-run-git "reset" "-q" "HEAD" "--" files)))) - (staged - (magit-unstage-all)) - ([* unstaged] - (user-error "Already unstaged")) - (hunk (user-error "Can't unstage this hunk")) - (diff (user-error "Can't unstage this diff")))) - -;;;###autoload -(defun magit-unstage-all () - "Remove all changes from staging area. -\('git reset --mixed HEAD')." - (interactive) - (when (or (not magit-unstage-all-confirm) - (and (not (magit-anything-unstaged-p)) - (not (magit-git-lines "ls-files" "--others" "-t" - "--exclude-standard"))) - (yes-or-no-p "Unstage all changes? ")) - (magit-run-git "reset" "HEAD" "--"))) - -;;;;;; Discard - -(defun magit-discard-item () - "Remove the change introduced by the item at point." - (interactive) - (magit-section-action discard (info parent-info) - ([file untracked] - (when (yes-or-no-p (format "Delete %s? " info)) - (if (and (file-directory-p info) - (not (file-symlink-p info))) - (delete-directory info 'recursive) - (delete-file info)) - (magit-refresh))) - (untracked - (when (yes-or-no-p "Delete all untracked files and directories? ") - (magit-run-git "clean" "-df"))) - ([hunk diff unstaged] - (when (yes-or-no-p (if (use-region-p) - "Discard changes in region? " - "Discard hunk? ")) - (magit-apply-hunk-item it "--reverse"))) - ([hunk diff staged] - (if (magit-file-uptodate-p parent-info) - (when (yes-or-no-p (if (use-region-p) - "Discard changes in region? " - "Discard hunk? ")) - (magit-apply-hunk-item it "--reverse" "--index")) - (user-error "Can't discard this hunk. Please unstage it first"))) - ([diff unstaged] - (magit-discard-diff it nil)) - ([diff staged] - (if (magit-file-uptodate-p (magit-section-info it)) - (magit-discard-diff it t) - (user-error "Can't discard staged changes to this file. \ -Please unstage it first"))) - (hunk (user-error "Can't discard this hunk")) - (diff (user-error "Can't discard this diff")) - (stash (when (yes-or-no-p "Discard stash? ") - (magit-stash-drop info))) - (branch (when (yes-or-no-p - (if current-prefix-arg - (concat "Force delete branch [" info "]? ") - (concat "Delete branch [" info "]? "))) - (magit-delete-branch info current-prefix-arg))) - (remote (when (yes-or-no-p "Remove remote? ") - (magit-remove-remote info))))) - -;;;;;; Revert - -(defun magit-revert-item () - "Revert the item at point. -The change introduced by the item is reversed in the current -working tree." - (interactive) - (magit-section-action revert (info) - ([* unstaged] (magit-discard-item)) - (commit (when (or (not magit-revert-item-confirm) - (yes-or-no-p "Revert this commit? ")) - (magit-revert-commit info))) - (diff (when (or (not magit-revert-item-confirm) - (yes-or-no-p "Revert this diff? ")) - (magit-apply-diff-item it "--reverse"))) - (hunk (when (or (not magit-revert-item-confirm) - (yes-or-no-p "Revert this hunk? ")) - (magit-apply-hunk-item it "--reverse"))))) - -(defun magit-revert-commit (commit) - (magit-assert-one-parent commit "revert") - (magit-run-git "revert" "--no-commit" commit)) - -(defconst magit-revert-backup-file "magit/reverted.diff") - -(defun magit-revert-undo () - "Re-apply the previously reverted hunk. -Also see option `magit-revert-backup'." - (interactive) - (let ((file (magit-git-dir magit-revert-backup-file))) - (if (file-readable-p file) - (magit-run-git "apply" file) - (user-error "No backups exist")) - (magit-refresh))) - -;;;;; Apply Core - -(defun magit-discard-diff (diff stagedp) - (let ((file (magit-section-info diff))) - (cl-case (magit-section-diff-status diff) - (deleted - (when (yes-or-no-p (format "Resurrect %s? " file)) - (when stagedp - (magit-run-git "reset" "-q" "--" file)) - (magit-run-git "checkout" "--" file))) - (new - (when (yes-or-no-p (format "Delete %s? " file)) - (magit-run-git "rm" "-f" "--" file))) - (t - (when (yes-or-no-p (format "Discard changes to %s? " file)) - (if stagedp - (magit-run-git "checkout" "HEAD" "--" file) - (magit-run-git "checkout" "--" file))))))) - -(defun magit-apply-commit (commit) - (magit-assert-one-parent commit "cherry-pick") - (magit-run-git "cherry-pick" "--no-commit" commit)) - -(defun magit-apply-diff-item (diff &rest args) - (when (zerop magit-diff-context-lines) - (setq args (cons "--unidiff-zero" args))) - (let ((buf (generate-new-buffer " *magit-input*"))) - (unwind-protect - (progn (magit-insert-diff-item-patch diff buf) - (magit-run-git-with-input - buf "apply" args "--ignore-space-change" "-")) - (kill-buffer buf)))) - -(defun magit-apply-hunk-item (hunk &rest args) - "Apply single hunk or part of a hunk to the index or working file. - -This function is the core of magit's stage, unstage, apply, and -revert operations. HUNK (or the portion of it selected by the -region) will be applied to either the index, if \"--cached\" is a -member of ARGS, or to the working file otherwise." - (when (string-match "^diff --cc" (magit-section-parent-info hunk)) - (user-error (concat "Cannot un-/stage individual resolution hunks. " - "Please stage the whole file."))) - (let ((use-region (use-region-p))) - (when (zerop magit-diff-context-lines) - (setq args (cons "--unidiff-zero" args)) - (when use-region - (user-error (concat "Not enough context to partially apply hunk. " - "Use `+' to increase context.")))) - (let ((buf (generate-new-buffer " *magit-input*"))) - (unwind-protect - (progn (if use-region - (magit-insert-hunk-item-region-patch - hunk (member "--reverse" args) - (region-beginning) (region-end) buf) - (magit-insert-hunk-item-patch hunk buf)) - (magit-revert-backup buf args) - (magit-run-git-with-input - buf "apply" args "--ignore-space-change" "-")) - (kill-buffer buf))))) - -(defun magit-insert-diff-item-patch (diff buf) - (magit-insert-region (magit-section-content-beginning diff) - (magit-section-end diff) - buf)) - -(defun magit-insert-hunk-item-patch (hunk buf) - (magit-diff-item-insert-header (magit-section-parent hunk) buf) - (magit-insert-region (magit-section-beginning hunk) - (magit-section-end hunk) - buf)) - -(defun magit-insert-hunk-item-region-patch (hunk reverse beg end buf) - (magit-diff-item-insert-header (magit-section-parent hunk) buf) - (save-excursion - (goto-char (magit-section-beginning hunk)) - (magit-insert-current-line buf) - (forward-line) - (let ((copy-op (if reverse "+" "-"))) - (while (< (point) (magit-section-end hunk)) - (cond ((and (<= beg (point)) (< (point) end)) - (magit-insert-current-line buf)) - ((looking-at " ") - (magit-insert-current-line buf)) - ((looking-at copy-op) - (let ((text (buffer-substring-no-properties - (+ (point) 1) (line-beginning-position 2)))) - (with-current-buffer buf - (insert " " text))))) - (forward-line)))) - (with-current-buffer buf - (diff-fixup-modifs (point-min) (point-max)))) - -(defun magit-diff-item-insert-header (diff buf) - (magit-insert-region (magit-section-content-beginning diff) - (if (magit-section-children diff) - (magit-section-beginning - (car (magit-section-children diff))) - (magit-section-end diff)) - buf)) - -(defun magit-insert-region (beg end buf) - (let ((text (buffer-substring-no-properties beg end))) - (with-current-buffer buf - (insert text)))) - -(defun magit-insert-current-line (buf) - (let ((text (buffer-substring-no-properties - (line-beginning-position) (line-beginning-position 2)))) - (with-current-buffer buf - (insert text)))) - -(defun magit-revert-backup (buffer args) - (when (and magit-revert-backup (member "--reverse" args)) - (with-current-buffer buffer - (let ((buffer-file-name (magit-git-dir magit-revert-backup-file)) - (make-backup-files t) - (backup-directory-alist nil) - (version-control t) - (kept-old-versions 0) - (kept-new-versions 10)) - (make-directory (file-name-directory buffer-file-name) t) - (save-buffer 16))))) - -;;;; Visit - -(defun magit-visit-item (&optional other-window) - "Visit current item. -With a prefix argument, visit in other window." - (interactive "P") - (magit-section-action visit (info parent-info) - ((diff diffstat [file untracked]) - (magit-visit-file-item info other-window)) - (hunk (magit-visit-file-item parent-info other-window - (magit-hunk-item-target-line it) - (current-column))) - (commit (magit-show-commit info)) - (stash (magit-diff-stash info)) - (branch (magit-checkout info)))) - -(defun magit-visit-file-item (file &optional other-window line column) - (unless file - (user-error "Can't get pathname for this file")) - (unless (file-exists-p file) - (user-error "Can't visit deleted file: %s" file)) - (if (file-directory-p file) - (progn - (setq file (file-name-as-directory (expand-file-name file))) - (if (equal (magit-get-top-dir (file-name-directory file)) - (magit-get-top-dir)) - (magit-dired-jump other-window) - (magit-status file (if other-window - 'pop-to-buffer - 'switch-to-buffer)))) - (if other-window - (find-file-other-window file) - (find-file file)) - (when line - (goto-char (point-min)) - (forward-line (1- line)) - (when (> column 0) - (move-to-column (1- column)))))) - -(defun magit-hunk-item-target-line (hunk) - (save-excursion - (beginning-of-line) - (let ((line (line-number-at-pos))) - (goto-char (magit-section-beginning hunk)) - (unless (looking-at "@@+ .* \\+\\([0-9]+\\)\\(,[0-9]+\\)? @@+") - (user-error "Hunk header not found")) - (let ((target (string-to-number (match-string 1)))) - (forward-line) - (while (< (line-number-at-pos) line) - ;; XXX - deal with combined diffs - (unless (looking-at "-") - (setq target (+ target 1))) - (forward-line)) - target)))) - -;;;###autoload -(defun magit-dired-jump (&optional other-window) - "Visit current item in dired. -With a prefix argument, visit in other window." - (interactive "P") - (require 'dired-x) - (dired-jump other-window - (file-truename - (magit-section-action dired-jump (info parent-info) - ([file untracked] info) - ((diff diffstat) info) - (hunk parent-info) - (t default-directory))))) - -(defvar-local magit-file-log-file nil) -(defvar-local magit-show-current-version nil) - -;;;###autoload -(defun magit-show (rev file &optional switch-function) - "Display and select a buffer containing FILE as stored in REV. - -Insert the contents of FILE as stored in the revision REV into a -buffer. Then select the buffer using `pop-to-buffer' or with a -prefix argument using `switch-to-buffer'. Non-interactivity use -SWITCH-FUNCTION to switch to the buffer, if that is nil simply -return the buffer, without displaying it." - ;; REV may also be one of the symbols `index' or `working' but - ;; that is only intended for use by `magit-ediff'. - (interactive - (let (rev file section) - (magit-section-case (info parent) - (commit (setq file magit-file-log-file rev info)) - (hunk (setq section parent)) - (diff (setq section it))) - (if section - (setq rev (car (magit-diff-range section)) - file (magit-section-info section)) - (unless rev - (setq rev (magit-get-current-branch)))) - (setq rev (magit-read-rev "Retrieve file from revision" rev) - file (cl-case rev - (working (read-file-name "Find file: ")) - (index (magit-read-file-from-rev "HEAD" file)) - (t (magit-read-file-from-rev rev file)))) - (list rev file (if current-prefix-arg - 'switch-to-buffer - 'pop-to-buffer)))) - (let (buffer) - (if (eq rev 'working) - (setq buffer (find-file-noselect file)) - (let ((name (format "%s.%s" file - (if (symbolp rev) - (format "@{%s}" rev) - (replace-regexp-in-string "/" ":" rev))))) - (setq buffer (get-buffer name)) - (when buffer - (with-current-buffer buffer - (if (and (equal file magit-file-name) - (equal rev magit-show-current-version)) - (let ((inhibit-read-only t)) - (erase-buffer)) - (setq buffer nil)))) - (with-current-buffer - (or buffer (setq buffer (create-file-buffer name))) - (setq buffer-read-only t) - (with-silent-modifications - (if (eq rev 'index) - (let ((temp (car (split-string - (magit-git-string "checkout-index" - "--temp" file) - "\t"))) - (inhibit-read-only t)) - (insert-file-contents temp nil nil nil t) - (delete-file temp)) - (magit-git-insert "cat-file" "-p" (concat rev ":" file)))) - (let ((buffer-file-name (expand-file-name file (magit-get-top-dir)))) - (normal-mode t)) - (setq magit-file-name file) - (setq magit-show-current-version rev) - (goto-char (point-min))))) - (when switch-function - (with-current-buffer buffer - (funcall switch-function (current-buffer)))) - buffer)) - -;;;; Act -;;;;; Merging - -;;;###autoload -(defun magit-merge (revision &optional do-commit) - "Merge REVISION into the current 'HEAD', leaving changes uncommitted. -With a prefix argument, skip editing the log message and commit. -\('git merge [--no-commit] REVISION')." - (interactive (list (magit-read-rev "Merge" - (or (magit-guess-branch) - (magit-get-previous-branch))) - current-prefix-arg)) - (when (or (not (magit-anything-modified-p)) - (not magit-merge-warn-dirty-worktree) - (yes-or-no-p - "Running merge in a dirty worktree could cause data loss. Continue?")) - (magit-run-git "merge" revision magit-custom-options - (unless do-commit "--no-commit")) - (when (file-exists-p ".git/MERGE_MSG") - (let ((magit-custom-options nil)) - (magit-commit))))) - -;;;###autoload -(defun magit-merge-abort () - "Abort the current merge operation." - (interactive) - (if (file-exists-p (magit-git-dir "MERGE_HEAD")) - (when (yes-or-no-p "Abort merge? ") - (magit-run-git-async "merge" "--abort")) - (user-error "No merge in progress"))) - -;;;;; Branching - -;;;###autoload -(defun magit-checkout (revision) - "Switch 'HEAD' to REVISION and update working tree. -Fails if working tree or staging area contain uncommitted changes. -If REVISION is a remote branch, offer to create a local branch. -\('git checkout [-b] REVISION')." - (interactive - (list (let ((current-branch (magit-get-current-branch)) - (default (or (magit-guess-branch) - (magit-get-previous-branch)))) - (magit-read-rev (format "Switch from '%s' to" current-branch) - (unless (string= current-branch default) - default) - current-branch)))) - (magit-save-some-buffers) - (or (and (string-match "^\\(?:refs/\\)?remotes/\\([^/]+\\)/\\(.+\\)" revision) - (let ((remote (match-string 1 revision)) - (branch (match-string 2 revision))) - (and (yes-or-no-p (format "Create local branch for %s? " branch)) - (let ((local (read-string - (format "Call local branch (%s): " branch) - nil nil branch))) - (if (magit-ref-exists-p (concat "refs/heads/" local)) - (user-error "'%s' already exists" local) - (magit-run-git "checkout" "-b" local revision) - t))))) - (magit-run-git "checkout" revision))) - -;;;###autoload -(defun magit-create-branch (branch parent) - "Switch 'HEAD' to new BRANCH at revision PARENT and update working tree. -Fails if working tree or staging area contain uncommitted changes. -\('git checkout -b BRANCH REVISION')." - (interactive - (list (read-string "Create branch: ") - (magit-read-rev "Parent" (or (magit-guess-branch) - (magit-get-current-branch))))) - (cond ((run-hook-with-args-until-success - 'magit-create-branch-hook branch parent)) - ((and branch (not (string= branch ""))) - (magit-save-some-buffers) - (magit-run-git "checkout" magit-custom-options - "-b" branch parent)))) - -;;;###autoload -(defun magit-delete-branch (branch &optional force) - "Delete the BRANCH. -If the branch is the current one, offers to switch to `master' first. -With prefix, forces the removal even if it hasn't been merged. -Works with local or remote branches. -\('git branch [-d|-D] BRANCH' or 'git push :refs/heads/BRANCH')." - (interactive (list (magit-read-rev "Branch to delete" - (or (magit-guess-branch) - (magit-get-previous-branch))) - current-prefix-arg)) - (if (string-match "^\\(?:refs/\\)?remotes/\\([^/]+\\)/\\(.+\\)" branch) - (magit-run-git-async "push" - (match-string 1 branch) - (concat ":" (match-string 2 branch))) - (let* ((current (magit-get-current-branch)) - (is-current (string= branch current)) - (is-master (string= branch "master")) - (args (list "branch" - (if force "-D" "-d") - branch))) - (cond - ((and is-current is-master) - (message "Cannot delete master branch while it's checked out.")) - (is-current - (if (and (magit-ref-exists-p "refs/heads/master") - (y-or-n-p "Cannot delete current branch. Switch to master first? ")) - (progn - (magit-checkout "master") - (magit-run-git args)) - (message "The current branch was not deleted."))) - (t - (magit-run-git args)))))) - -;;;###autoload -(defun magit-rename-branch (old new &optional force) - "Rename branch OLD to NEW. -With prefix, forces the rename even if NEW already exists. -\('git branch [-m|-M] OLD NEW')." - (interactive - (let* ((old (magit-read-rev-with-default "Old name")) - (new (read-string "New name: " old))) - (list old new current-prefix-arg))) - (if (or (null new) (string= new "") - (string= old new)) - (message "Cannot rename branch \"%s\" to \"%s\"." old new) - (magit-run-git "branch" (if force "-M" "-m") old new))) - -(defun magit-guess-branch () - "Return a branch name depending on the context of cursor. -If no branch is found near the cursor return nil." - (magit-section-case (info parent-info) - (branch info) - ([commit wazzup] parent-info) - ([commit ] (magit-name-rev info)) - ([ wazzup] info))) - -;;;;; Remoting - -;;;###autoload -(defun magit-add-remote (remote url) - "Add the REMOTE and fetch it. -\('git remote add REMOTE URL')." - (interactive (list (read-string "Remote name: ") - (read-string "Remote url: "))) - (magit-run-git-async "remote" "add" "-f" remote url)) - -;;;###autoload -(defun magit-remove-remote (remote) - "Delete the REMOTE. -\('git remote rm REMOTE')." - (interactive (list (magit-read-remote "Delete remote"))) - (magit-run-git "remote" "rm" remote)) - -;;;###autoload -(defun magit-rename-remote (old new) - "Rename remote OLD to NEW. -\('git remote rename OLD NEW')." - (interactive - (let* ((old (magit-read-remote "Old name")) - (new (read-string "New name: " old))) - (list old new))) - (if (or (null old) (string= old "") - (null new) (string= new "") - (string= old new)) - (message "Cannot rename remote \"%s\" to \"%s\"." old new) - (magit-run-git "remote" "rename" old new))) - -(defun magit-guess-remote () - (magit-section-case (info parent-info) - (remote info) - (branch parent-info) - (t (if (string= info ".") info (magit-get-current-remote))))) - -;;;;; Rebase - -(defun magit-rebase-info () - "Return a list indicating the state of an in-progress rebase. - -The returned list has the form (ONTO DONE TOTAL STOPPED AM). -ONTO is the commit being rebased onto. -DONE and TOTAL are integers with obvious meanings. -STOPPED is the SHA-1 of the commit at which rebase stopped. -AM is non-nil if the current rebase is actually a git-am. - -Return nil if there is no rebase in progress." - (let ((m (magit-git-dir "rebase-merge")) - (a (magit-git-dir "rebase-apply"))) - (cond - ((file-directory-p m) ; interactive - (list - (magit-name-rev (magit-file-line (expand-file-name "onto" m))) - (length (magit-file-lines (expand-file-name "done" m))) - (cl-loop for line in (magit-file-lines - (expand-file-name "git-rebase-todo.backup" m)) - count (string-match "^[^#\n]" line)) - (magit-file-line (expand-file-name "stopped-sha" m)) - nil)) - - ((file-regular-p (expand-file-name "onto" a)) ; non-interactive - (list - (magit-name-rev (magit-file-line (expand-file-name "onto" a))) - (1- (string-to-number (magit-file-line (expand-file-name "next" a)))) - (string-to-number (magit-file-line (expand-file-name "last" a))) - (let ((patch-header (magit-file-line - (car (directory-files a t "^[0-9]\\{4\\}$"))))) - (when (string-match "^From \\([a-z0-9]\\{40\\}\\) " patch-header) - (match-string 1 patch-header))) - nil)) - - ((file-regular-p (expand-file-name "applying" a)) ; am - (list - (magit-name-rev "HEAD") - (1- (string-to-number (magit-file-line (expand-file-name "next" a)))) - (string-to-number (magit-file-line (expand-file-name "last" a))) - (let ((patch-header (magit-file-line - (car (directory-files a t "^[0-9]\\{4\\}$"))))) - (when (string-match "^From \\([a-z0-9]\\{40\\}\\) " patch-header) - (match-string 1 patch-header))) - t))))) - -(defun magit-rebase-step () - "Initiate or continue a rebase." - (interactive) - (let ((rebase (magit-rebase-info))) - (if rebase - (let ((cursor-in-echo-area t) - (message-log-max nil) - (am (nth 4 rebase))) - (message "%s in progress. [A]bort, [S]kip, or [C]ontinue? " - (if am "Apply mailbox" "Rebase")) - (cl-case (read-event) - ((?A ?a) (magit-run-git-async (if am "am" "rebase") "--abort")) - ((?S ?s) (magit-run-git-async (if am "am" "rebase") "--skip")) - ((?C ?c) (magit-with-emacsclient magit-server-window-for-commit - (magit-run-git-async (if am "am" "rebase") "--continue"))))) - (let* ((branch (magit-get-current-branch)) - (rev (magit-read-rev - "Rebase to" - (magit-get-tracked-branch branch) - branch))) - (magit-run-git "rebase" rev))))) - -;;;###autoload -(defun magit-interactive-rebase (commit) - "Start a git rebase -i session, old school-style." - (interactive (let ((commit (magit-section-case (info) (commit info)))) - (list (if commit - (concat commit "^") - (magit-read-rev "Interactively rebase to" - (magit-guess-branch)))))) - (magit-assert-emacsclient "rebase interactively") - (magit-with-emacsclient magit-server-window-for-rebase - (magit-run-git-async "rebase" "-i" commit))) - -;;;;; AM - -(defun magit-apply-mailbox (&optional file-or-dir) - "Apply a series of patches from a mailbox." - (interactive "fmbox or Maildir file or directory: ") - (magit-with-emacsclient magit-server-window-for-rebase - (magit-run-git-async "am" file-or-dir))) - -;;;;; Reset - -;;;###autoload -(defun magit-reset-head (revision &optional hard) - "Switch 'HEAD' to REVISION, keeping prior working tree and staging area. -Any differences from REVISION become new changes to be committed. -With prefix argument, all uncommitted changes in working tree -and staging area are lost. -\('git reset [--soft|--hard] REVISION')." - (interactive (list (magit-read-rev (format "%s head to" - (if current-prefix-arg - "Hard reset" - "Reset")) - (or (magit-guess-branch) "HEAD")) - current-prefix-arg)) - (magit-run-git "reset" (if hard "--hard" "--soft") revision "--")) - -;;;###autoload -(defun magit-reset-head-hard (revision) - "Switch 'HEAD' to REVISION, losing all changes. -Uncomitted changes in both working tree and staging area are lost. -\('git reset --hard REVISION')." - (interactive (list (magit-read-rev (format "Hard reset head to") - (or (magit-guess-branch) "HEAD")))) - (magit-reset-head revision t)) - -;;;###autoload -(defun magit-reset-working-tree (&optional arg) - "Revert working tree and clear changes from staging area. -\('git reset --hard HEAD'). - -With a prefix arg, also remove untracked files. -With two prefix args, remove ignored files as well." - (interactive "p") - (let ((include-untracked (>= arg 4)) - (include-ignored (>= arg 16))) - (when (yes-or-no-p (format "Discard all uncommitted changes%s%s? " - (if include-untracked - ", untracked files" - "") - (if include-ignored - ", ignored files" - ""))) - (magit-reset-head-hard "HEAD") - (when include-untracked - (magit-run-git "clean" "-fd" (if include-ignored "-x" "")))))) - -;;;;; Rewriting - -(defun magit-read-rewrite-info () - (when (file-exists-p (magit-git-dir "magit-rewrite-info")) - (with-temp-buffer - (insert-file-contents (magit-git-dir "magit-rewrite-info")) - (goto-char (point-min)) - (read (current-buffer))))) - -(defun magit-write-rewrite-info (info) - (with-temp-file (magit-git-dir "magit-rewrite-info") - (prin1 info (current-buffer)) - (princ "\n" (current-buffer)))) - -(defun magit-rewrite-set-commit-property (commit prop value) - (let* ((info (magit-read-rewrite-info)) - (pending (cdr (assq 'pending info))) - (p (assoc commit pending))) - (when p - (setf (cdr p) (plist-put (cdr p) prop value)) - (magit-write-rewrite-info info) - (magit-refresh)) - t)) - -(add-hook 'magit-apply-hook 'magit-rewrite-apply) -(put 'magit-rewrite-apply 'magit-section-action-context [commit pending]) -(defun magit-rewrite-apply (commit) - (magit-apply-commit commit) - (magit-rewrite-set-commit-property commit 'used t)) - -(add-hook 'magit-cherry-pick-hook 'magit-rewrite-pick) -(put 'magit-rewrite-pick 'magit-section-action-context [commit pending]) -(defun magit-rewrite-pick (commit) - (magit-cherry-pick-commit commit) - (magit-rewrite-set-commit-property commit 'used t)) - -(add-hook 'magit-revert-hook 'magit-rewrite-revert) -(put 'magit-rewrite-revert 'magit-section-action-context [commit pending]) -(defun magit-rewrite-revert (commit) - (when (or (not magit-revert-item-confirm) - (yes-or-no-p "Revert this commit? ")) - (magit-revert-commit commit) - (magit-rewrite-set-commit-property commit 'used nil))) - -(defun magit-rewrite-set-used () - (interactive) - (magit-section-case (info) - ([commit pending] - (magit-rewrite-set-commit-property info 'used t) - (magit-refresh)))) - -(defun magit-rewrite-set-unused () - (interactive) - (magit-section-case (info) - ([commit pending] - (magit-rewrite-set-commit-property info 'used nil) - (magit-refresh)))) - -(defun magit-rewrite-start (from &optional onto) - (interactive (list (magit-read-rev-with-default "Rewrite from"))) - (when (magit-anything-modified-p) - (user-error "You have uncommitted changes")) - (or (not (magit-read-rewrite-info)) - (user-error "Rewrite in progress")) - (let* ((orig (magit-rev-parse "HEAD")) - (base (if (or (eq magit-rewrite-inclusive t) - (and (eq magit-rewrite-inclusive 'ask) - (y-or-n-p "Include selected revision in rewrite? "))) - (or (car (magit-commit-parents from)) - (user-error "Can't rewrite a parentless commit")) - from)) - (pending (magit-git-lines "rev-list" (concat base "..")))) - (magit-write-rewrite-info `((orig ,orig) - (pending ,@(mapcar #'list pending)))) - (magit-run-git "reset" "--hard" base "--"))) - -(defun magit-rewrite-stop (&optional noconfirm) - (interactive) - (let* ((info (magit-read-rewrite-info))) - (or info - (user-error "No rewrite in progress")) - (when (or noconfirm - (yes-or-no-p "Stop rewrite? ")) - (magit-write-rewrite-info nil) - (magit-refresh)))) - -(defun magit-rewrite-abort () - (interactive) - (let* ((info (magit-read-rewrite-info)) - (orig (cadr (assq 'orig info)))) - (or info - (user-error "No rewrite in progress")) - (when (magit-anything-modified-p) - (user-error "You have uncommitted changes")) - (when (yes-or-no-p "Abort rewrite? ") - (magit-write-rewrite-info nil) - (magit-run-git "reset" "--hard" orig "--")))) - -(defun magit-rewrite-finish () - (interactive) - (magit-rewrite-finish-step) - (magit-refresh)) - -(defun magit-rewrite-finish-step () - (let ((info (magit-read-rewrite-info))) - (or info - (user-error "No rewrite in progress")) - (let* ((pending (cdr (assq 'pending info))) - (first-unused - (let ((rpend (reverse pending))) - (while (and rpend (plist-get (cdr (car rpend)) 'used)) - (setq rpend (cdr rpend))) - (car rpend))) - (commit (car first-unused))) - (cond ((not first-unused) - (magit-rewrite-stop t)) - ((magit-git-success "cherry-pick" commit) - (magit-rewrite-set-commit-property commit 'used t) - (magit-rewrite-finish-step)) - (t - (magit-refresh) - (error "Could not apply %s" commit)))))) - -(defun magit-rewrite-diff-pending () - (interactive) - (let* ((info (magit-read-rewrite-info)) - (orig (cadr (assq 'orig info)))) - (if orig - (magit-diff orig nil "-R") - (user-error "No rewrite in progress")))) - -(defun magit-rewrite-diff-pending-transition () - (interactive) - (message "Please remove magit-insert-pending-changes from your magit-status-sections-hook, or move to magit-rewrite-diff-pending")) - -(define-obsolete-function-alias - 'magit-insert-pending-changes 'magit-rewrite-diff-pending-transition - "382351845e" - "https://github.com/magit/magit/commit/382351845eca2425713f640f9bb55756c9ddf723") - -;;;;; Fetching - -;;;###autoload -(defun magit-fetch (remote) - "Fetch from REMOTE." - (interactive (list (magit-read-remote "Fetch remote"))) - (magit-run-git-async "fetch" remote magit-custom-options)) - -;;;###autoload -(defun magit-fetch-current () - "Run fetch for default remote. - -If there is no default remote, ask for one." - (interactive) - (magit-fetch (or (magit-get-current-remote) - (magit-read-remote "Fetch remote")))) - -;;;###autoload -(defun magit-remote-update () - "Update all remotes." - (interactive) - (or (run-hook-with-args-until-success 'magit-remote-update-hook) - (magit-run-git-async "remote" "update" magit-custom-options))) - -;;;;; Pulling - -;;;###autoload -(defun magit-pull () - "Run git pull. - -If there is no default remote, the user is prompted for one and -its values is saved with git config. If there is no default -merge branch, the user is prompted for one and its values is -saved with git config. With a prefix argument, the default -remote is not used and the user is prompted for a remote. With -two prefix arguments, the default merge branch is not used and -the user is prompted for a merge branch. Values entered by the -user because of prefix arguments are not saved with git config." - (interactive) - (or (run-hook-with-args-until-success 'magit-pull-hook) - (let* ((branch (magit-get-current-branch)) - (branch-remote (magit-get-remote branch)) - (branch-merge (magit-get "branch" branch "merge")) - (branch-merge-name (and branch-merge - (save-match-data - (string-match "^refs/heads/\\(.+\\)" branch-merge) - (match-string 1 branch-merge)))) - (choose-remote (>= (prefix-numeric-value current-prefix-arg) 4)) - (choose-branch (>= (prefix-numeric-value current-prefix-arg) 16)) - (remote-needed (or choose-remote - (not branch-remote))) - (branch-needed (or choose-branch - (not branch-merge-name))) - (chosen-branch-remote - (if remote-needed - (magit-read-remote "Pull from remote" branch-remote) - branch-remote)) - (chosen-branch-merge-name - (if branch-needed - (magit-read-remote-branch (format "Pull branch from remote %s" - chosen-branch-remote) - chosen-branch-remote) - branch-merge-name))) - (when (and (not branch-remote) - (not choose-remote)) - (magit-set chosen-branch-remote "branch" branch "remote")) - (when (and (not branch-merge-name) - (not choose-branch)) - (magit-set (format "%s" chosen-branch-merge-name) - "branch" branch "merge")) - (magit-run-git-async - "pull" magit-custom-options - (and choose-remote chosen-branch-remote) - (and (or choose-remote choose-branch) - (list (format "refs/heads/%s:refs/remotes/%s/%s" - chosen-branch-merge-name - chosen-branch-remote - chosen-branch-merge-name))))))) - -;;;;; Pushing - -;;;###autoload -(defun magit-push-tags () - "Push tags to a remote repository. - -Push tags to the current branch's remote. If that isn't set push -to \"origin\" or if that remote doesn't exit but only a single -remote is defined use that. Otherwise or with a prefix argument -ask the user what remote to use." - (interactive) - (let* ((branch (magit-get-current-branch)) - (remotes (magit-git-lines "remote")) - (remote (or (and branch (magit-get-remote branch)) - (car (member "origin" remotes)) - (and (= (length remotes) 1) - (car remotes))))) - (when (or current-prefix-arg (not remote)) - (setq remote (magit-read-remote "Push to remote"))) - (magit-run-git-async "push" remote "--tags" magit-custom-options))) - -;;;###autoload -(defun magit-push () - "Push the current branch to a remote repository. - -This command runs the `magit-push-remote' hook. By default that -means running `magit-push-dwim'. So unless you have customized -the hook this command behaves like this: - -With a single prefix argument ask the user what branch to push -to. With two or more prefix arguments also ask the user what -remote to push to. Otherwise use the remote and branch as -configured using the Git variables `branch..remote' and -`branch..merge'. If the former is undefined ask the user. -If the latter is undefined push without specifing the remote -branch explicitly. - -Also see option `magit-set-upstream-on-push'." - (interactive) - (run-hook-with-args-until-success 'magit-push-hook current-prefix-arg)) - -(defun magit-push-dwim (arg) - "Push the current branch to a remote repository. - -With a single prefix argument ask the user what remote to push -to. With two or more prefix arguments also ask the user the -name of the remote branch to push to. - -Otherwise use the remote and branch as configured using the -Git variables `branch..remote' and `branch..merge'. -If the former is undefined ask the user. If the latter is -undefined push without specifing the remote branch explicitly. - -Also see option `magit-set-upstream-on-push'." - (interactive "P") - (let* ((branch (or (magit-get-current-branch) - (user-error "Don't push a detached head. That's gross"))) - (auto-remote (magit-get-remote branch)) - (used-remote (if (or arg (not auto-remote)) - (magit-read-remote - (format "Push %s to remote" branch) auto-remote) - auto-remote)) - (auto-branch (and (equal used-remote auto-remote) - (magit-get "branch" branch "merge"))) - (used-branch (if (>= (prefix-numeric-value arg) 16) - (magit-read-remote-branch - (format "Push %s as branch" branch) - used-remote auto-branch) - auto-branch))) - (cond ;; Pushing to what's already configured. - ((and auto-branch - (equal auto-branch used-branch) - (equal auto-remote used-remote))) - ;; Setting upstream because of magit-custom-options. - ((member "-u" magit-custom-options)) - ;; Two prefix arguments; ignore magit-set-upstream-on-push. - ((>= (prefix-numeric-value arg) 16) - (and (yes-or-no-p "Set upstream while pushing? ") - (setq magit-custom-options - (cons "-u" magit-custom-options)))) - ;; Else honor magit-set-upstream-on-push. - ((eq magit-set-upstream-on-push 'refuse) - (user-error "Not pushing since no upstream has been set.")) - ((or (eq magit-set-upstream-on-push 'dontask) - (and (eq magit-set-upstream-on-push t) - (yes-or-no-p "Set upstream while pushing? "))) - (setq magit-custom-options (cons "-u" magit-custom-options)))) - (magit-run-git-async - "push" "-v" used-remote - (if used-branch (format "%s:%s" branch used-branch) branch) - magit-custom-options))) - -;;;;; Committing - -;;;###autoload -(defun magit-commit (&optional amendp) - "Create a new commit on HEAD. -With a prefix argument amend to the commit at HEAD instead. -\('git commit [--amend]')." - (interactive "P") - (let ((args magit-custom-options)) - (when amendp - (setq args (cons "--amend" args))) - (when (setq args (magit-commit-assert args)) - (magit-commit-maybe-expand) - (magit-commit-internal "commit" args)))) - -;;;###autoload -(defun magit-commit-amend () - "Amend the last commit. -\('git commit --amend')." - (interactive) - (magit-commit-maybe-expand) - (magit-commit-internal "commit" (cons "--amend" magit-custom-options))) - -;;;###autoload -(defun magit-commit-extend (&optional override-date) - "Amend the last commit, without editing the message. -With a prefix argument do change the committer date, otherwise -don't. The option `magit-commit-extend-override-date' can be -used to inverse the meaning of the prefix argument. -\('git commit --no-edit --amend [--keep-date]')." - (interactive (list (if current-prefix-arg - (not magit-commit-reword-override-date) - magit-commit-reword-override-date))) - (magit-commit-maybe-expand) - (let ((process-environment process-environment)) - (unless override-date - (setenv "GIT_COMMITTER_DATE" - (magit-git-string "log" "-1" "--format:format=%cd"))) - (magit-commit-internal "commit" (nconc (list "--no-edit" "--amend") - magit-custom-options)))) - -;;;###autoload -(defun magit-commit-reword (&optional override-date) - "Reword the last commit, ignoring staged changes. - -With a prefix argument do change the committer date, otherwise -don't. The option `magit-commit-rewrite-override-date' can be -used to inverse the meaning of the prefix argument. - -Non-interactively respect the optional OVERRIDE-DATE argument -and ignore the option. - -\('git commit --only --amend')." - (interactive (list (if current-prefix-arg - (not magit-commit-reword-override-date) - magit-commit-reword-override-date))) - (let ((process-environment process-environment)) - (unless override-date - (setenv "GIT_COMMITTER_DATE" - (magit-git-string "log" "-1" "--format:format=%cd"))) - (magit-commit-internal "commit" (nconc (list "--only" "--amend") - magit-custom-options)))) - -(defvar-local magit-commit-squash-args nil) -(defvar-local magit-commit-squash-fixup nil) - -(defvar magit-commit-unmark-after-squash t) - -;;;###autoload -(defun magit-commit-fixup (&optional commit) - "Create a fixup commit. -With a prefix argument the user is always queried for the commit -to be fixed. Otherwise the current or marked commit may be used -depending on the value of option `magit-commit-squash-commit'. -\('git commit [--no-edit] --fixup=COMMIT')." - (interactive (list (magit-commit-squash-commit))) - (magit-commit-squash commit t)) - -;;;###autoload -(defun magit-commit-squash (&optional commit fixup) - "Create a squash commit. -With a prefix argument the user is always queried for the commit -to be fixed. Otherwise the current or marked commit may be used -depending on the value of option `magit-commit-squash-commit'. -\('git commit [--no-edit] --fixup=COMMIT')." - (interactive (list (magit-commit-squash-commit))) - (let ((args magit-custom-options)) - (cond - ((not commit) - (magit-commit-assert args) - (magit-log) - (setq magit-commit-squash-args args - magit-commit-squash-fixup fixup) - (add-hook 'magit-mark-commit-hook 'magit-commit-squash-marked t t) - (add-hook 'magit-mode-quit-window-hook 'magit-commit-squash-abort t t) - (message "Select commit using \".\", or abort using \"q\"")) - ((setq args (magit-commit-assert args)) - (when (eq args t) (setq args nil)) - (magit-commit-internal - "commit" - (nconc (list "--no-edit" - (concat (if fixup "--fixup=" "--squash=") commit)) - args)))))) - -(defun magit-commit-squash-commit () - (unless (or current-prefix-arg - (eq magit-commit-squash-commit nil)) - (let ((current (magit-section-case (info) (commit info)))) - (cl-ecase magit-commit-squash-commit - (current-or-marked (or current magit-marked-commit)) - (marked-or-current (or magit-marked-commit current)) - (current current) - (marked magit-marked-commit))))) - -(defun magit-commit-squash-marked () - (when magit-marked-commit - (magit-commit-squash magit-marked-commit magit-commit-squash-fixup)) - (when magit-commit-unmark-after-squash - (setq magit-marked-commit nil)) - (kill-local-variable 'magit-commit-squash-fixup) - (remove-hook 'magit-mark-commit-hook 'magit-commit-squash-marked t) - (remove-hook 'magit-mode-quit-window-hook 'magit-commit-squash-abort t) - (magit-mode-quit-window)) - -(defun magit-commit-squash-abort (buffer) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (remove-hook 'magit-mark-commit-hook 'magit-commit-squash-marked t) - (remove-hook 'magit-mode-quit-window-hook 'magit-commit-squash-abort t)))) - -(defun magit-commit-assert (args) - (cond - ((or (magit-anything-staged-p) - (member "--allow-empty" args) - (member "--all" args) - (member "--amend" args)) - (or args (list "--"))) - ((and (magit-rebase-info) - (y-or-n-p "Nothing staged. Continue in-progress rebase? ")) - (magit-run-git-async "rebase" "--continue") - nil) - (magit-commit-ask-to-stage - (magit-commit-maybe-expand t) - (when (y-or-n-p "Nothing staged. Stage and commit everything? ") - (magit-run-git "add" "-u" ".") - (or args (list "--")))) - (t - (user-error "Nothing staged. Set --allow-empty, --all, or --amend in popup")))) - -(defun magit-commit-maybe-expand (&optional unstaged) - (when (and magit-expand-staged-on-commit - (derived-mode-p 'magit-status-mode)) - (if unstaged - (magit-jump-to-unstaged t) - (magit-jump-to-staged t)))) - -(defun magit-commit-internal (subcmd args) - (setq git-commit-previous-winconf (current-window-configuration)) - (if (magit-use-emacsclient-p) - (magit-with-emacsclient magit-server-window-for-commit - (magit-run-git-async subcmd args)) - (let ((topdir (magit-get-top-dir)) - (editmsg (magit-git-dir (if (equal subcmd "tag") - "TAG_EDITMSG" - "COMMIT_EDITMSG")))) - (when (and (member "--amend" args) - (not (file-exists-p editmsg))) - (with-temp-file editmsg - (magit-git-insert "log" "-1" "--format=format:%B" "HEAD"))) - (with-current-buffer (find-file-noselect editmsg) - (funcall (if (functionp magit-server-window-for-commit) - magit-server-window-for-commit - 'switch-to-buffer) - (current-buffer)) - (add-hook 'git-commit-commit-hook - (apply-partially - (lambda (default-directory editmsg args) - (magit-run-git args) - (ignore-errors (delete-file editmsg))) - topdir editmsg - `(,subcmd - ,"--cleanup=strip" - ,(concat "--file=" (file-relative-name - (buffer-file-name) - topdir)) - ,@args)) - nil t))))) - -(defun magit-commit-add-log () - "Add a template for the current hunk to the commit message buffer." - (interactive) - (let* ((section (magit-current-section)) - (fun (if (eq (magit-section-type section) 'hunk) - (save-window-excursion - (save-excursion - (magit-visit-item) - (add-log-current-defun))) - nil)) - (file (magit-section-info - (cl-case (magit-section-type section) - (hunk (magit-section-parent section)) - (diff section) - (t (user-error "No change at point"))))) - (locate-buffer (lambda () - (cl-find-if - (lambda (buf) - (with-current-buffer buf - (derived-mode-p 'git-commit-mode))) - (append (buffer-list (selected-frame)) - (buffer-list))))) - (buffer (funcall locate-buffer))) - (unless buffer - (unless (magit-commit-assert nil) - (user-error "Abort")) - (magit-commit) - (while (not (setq buffer (funcall locate-buffer))) - (sit-for 0.01))) - (pop-to-buffer buffer) - (goto-char (point-min)) - (cond ((not (re-search-forward (format "^\\* %s" (regexp-quote file)) - nil t)) - ;; No entry for file, create it. - (goto-char (point-max)) - (forward-comment -1000) - (unless (or (bobp) (looking-back "\\(\\*[^\n]+\\|\n\\)")) - (insert "\n")) - (insert (format "\n* %s" file)) - (when fun - (insert (format " (%s)" fun))) - (insert ": ")) - (fun - ;; found entry for file, look for fun - (let ((limit (save-excursion - (or (and (re-search-forward "^\\* " nil t) - (match-beginning 0)) - (progn (goto-char (point-max)) - (forward-comment -1000) - (point)))))) - (cond ((re-search-forward - (format "(.*\\_<%s\\_>.*):" (regexp-quote fun)) - limit t) - ;; found it, goto end of current entry - (if (re-search-forward "^(" limit t) - (backward-char 2) - (goto-char limit))) - (t - ;; not found, insert new entry - (goto-char limit) - (if (bolp) - (open-line 1) - (newline)) - (insert (format "(%s): " fun)))))) - (t - ;; found entry for file, look for beginning it - (when (looking-at ":") - (forward-char 2)))))) - -;;;;; Tagging - -;;;###autoload -(defun magit-tag (name rev &optional annotate) - "Create a new tag with the given NAME at REV. -With a prefix argument annotate the tag. -\('git tag [--annotate] NAME REV')." - (interactive (list (magit-read-tag "Tag name") - (magit-read-rev "Place tag on" - (or (magit-guess-branch) "HEAD")) - current-prefix-arg)) - (let ((args (append magit-custom-options (list name rev)))) - (if (or (member "--sign" args) - (member "--annotate" args) - (and annotate (setq args (cons "--annotate" args)))) - (magit-commit-internal "tag" args) - (magit-run-git "tag" args)))) - -;;;###autoload -(defun magit-delete-tag (name) - "Delete the tag with the given NAME. -\('git tag -d NAME')." - (interactive (list (magit-read-tag "Delete Tag" t))) - (magit-run-git "tag" "-d" magit-custom-options name)) - -;;;;; Stashing - -(defvar magit-read-stash-history nil - "The history of inputs to `magit-stash'.") - -;;;###autoload -(defun magit-stash (description) - "Create new stash of working tree and staging area named DESCRIPTION. -Working tree and staging area revert to the current 'HEAD'. -With prefix argument, changes in staging area are kept. -\('git stash save [--keep-index] DESCRIPTION')" - (interactive (list (read-string "Stash description: " nil - 'magit-read-stash-history))) - (magit-run-git-async "stash" "save" magit-custom-options "--" description)) - -;;;###autoload -(defun magit-stash-snapshot () - "Create new stash of working tree and staging area; keep changes in place. -\('git stash save \"Snapshot...\"; git stash apply stash@{0}')" - (interactive) - (magit-call-git "stash" "save" magit-custom-options - (format-time-string - "Snapshot taken at %Y-%m-%d %H:%M:%S" - (current-time))) - (magit-run-git "stash" "apply" "stash@{0}")) - -(defun magit-stash-apply (stash) - "Apply a stash on top of the current working tree state. -\('git stash apply stash@{N}')" - (interactive (list (magit-read-stash "Apply stash (number): "))) - (magit-run-git "stash" "apply" stash)) - -(defun magit-stash-pop (stash) - "Apply a stash on top of working tree state and remove from stash list. -\('git stash pop stash@{N}')" - (interactive (list (magit-read-stash "Pop stash (number): "))) - (magit-run-git "stash" "pop" stash)) - -(defun magit-stash-drop (stash) - "Remove a stash from the stash list. -\('git stash drop stash@{N}')" - (interactive (list (magit-read-stash "Drop stash (number): "))) - (magit-run-git "stash" "drop" stash)) - -;;;;; Cherry-Pick - -(defun magit-cherry-pick-item () - "Cherry-pick them item at point." - (interactive) - (magit-section-action cherry-pick (info) - (commit (magit-cherry-pick-commit info)) - (stash (magit-stash-pop info)))) - -(defun magit-cherry-pick-commit (commit) - (magit-assert-one-parent commit "cherry-pick") - (magit-run-git "cherry-pick" commit)) - -;;;;; Submoduling - -;;;###autoload -(defun magit-submodule-update (&optional init) - "Update the submodule of the current git repository. -With a prefix arg, do a submodule update --init." - (interactive "P") - (let ((default-directory (magit-get-top-dir))) - (magit-run-git-async "submodule" "update" (and init "--init")))) - -;;;###autoload -(defun magit-submodule-update-init () - "Update and init the submodule of the current git repository." - (interactive) - (magit-submodule-update t)) - -;;;###autoload -(defun magit-submodule-init () - "Initialize the submodules." - (interactive) - (let ((default-directory (magit-get-top-dir))) - (magit-run-git-async "submodule" "init"))) - -;;;###autoload -(defun magit-submodule-sync () - "Synchronizes submodule's remote URL configuration." - (interactive) - (let ((default-directory (magit-get-top-dir))) - (magit-run-git-async "submodule" "sync"))) - -;;;;; Bisecting - -;;;###autoload -(defun magit-bisect-start (bad good) - "Start a bisect session. - -Bisecting a bug means to find the commit that introduced it. -This command starts such a bisect session by asking for a know -good and a bad commit. To move the session forward use the -other actions from the bisect popup (\ -\\\\[magit-key-mode-popup-bisecting])." - (interactive - (if (magit-bisecting-p) - (user-error "Already bisecting") - (list (magit-read-rev "Start bisect with known bad revision" "HEAD") - (magit-read-rev "Good revision" (magit-guess-branch))))) - (magit-run-git-bisect "start" (list bad good) t)) - -;;;###autoload -(defun magit-bisect-reset () - "After bisecting cleanup bisection state and return to original HEAD." - (interactive) - (when (yes-or-no-p "Reset bisect?") - (magit-run-git "bisect" "reset") - (ignore-errors (delete-file (magit-git-dir "BISECT_CMD_OUTPUT"))))) - -;;;###autoload -(defun magit-bisect-good () - "While bisecting, mark the current commit as good. -Use this after you have asserted that the commit does not contain -the bug in question." - (interactive) - (magit-run-git-bisect "good")) - -;;;###autoload -(defun magit-bisect-bad () - "While bisecting, mark the current commit as bad. -Use this after you have asserted that the commit does contain the -bug in question." - (interactive) - (magit-run-git-bisect "bad")) - -;;;###autoload -(defun magit-bisect-skip () - "While bisecting, skip the current commit. -Use this if for some reason the current commit is not a good one -to test. This command lets Git choose a different one." - (interactive) - (magit-run-git-bisect "skip")) - -;;;###autoload -(defun magit-bisect-run (cmdline) - "Bisect automatically by running commands after each step." - (interactive (list (read-shell-command "Bisect shell command: "))) - (magit-run-git-bisect "run" (list cmdline))) - -(defun magit-run-git-bisect (subcommand &optional args no-assert) - (unless (or no-assert (magit-bisecting-p)) - (user-error "Not bisecting")) - (let ((file (magit-git-dir "BISECT_CMD_OUTPUT")) - (default-directory (magit-get-top-dir))) - (ignore-errors (delete-file file)) - (magit-run-git-with-logfile file "bisect" subcommand args) - (magit-process-wait) - (magit-refresh))) - -;;;;; Logging - -;;;###autoload -(defun magit-log (&optional range) - (interactive) - (magit-mode-setup magit-log-buffer-name nil - #'magit-log-mode - #'magit-refresh-log-buffer - 'oneline (or range "HEAD") magit-custom-options)) - -;;;###autoload -(defun magit-log-ranged (range) - (interactive (list (magit-read-rev-range "Log" "HEAD"))) - (magit-log range)) - -;;;###autoload -(defun magit-log-long (&optional range) - (interactive) - (magit-mode-setup magit-log-buffer-name nil - #'magit-log-mode - #'magit-refresh-log-buffer - 'long (or range "HEAD") magit-custom-options)) - -;;;###autoload -(defun magit-log-long-ranged (range) - (interactive (list (magit-read-rev-range "Long Log" "HEAD"))) - (magit-log-long range)) - -;;;###autoload -(defun magit-file-log (file &optional use-graph) - "Display the log for the currently visited file or another one. -With a prefix argument show the log graph." - (interactive - (list (magit-read-file-from-rev (magit-get-current-branch) - (magit-buffer-file-name t)) - current-prefix-arg)) - (magit-mode-setup magit-log-buffer-name nil - #'magit-log-mode - #'magit-refresh-log-buffer - 'oneline "HEAD" - `(,@(and use-graph (list "--graph")) - ,@magit-custom-options - "--follow") - file)) - -;;;###autoload -(defun magit-reflog (ref) - "Display the reflog of the current branch. -With a prefix argument another branch can be chosen." - (interactive (let ((branch (magit-get-current-branch))) - (if (and branch (not current-prefix-arg)) - (list branch) - (list (magit-read-rev "Reflog of" branch))))) - (magit-mode-setup magit-reflog-buffer-name nil - #'magit-reflog-mode - #'magit-refresh-reflog-buffer ref)) - -;;;###autoload -(defun magit-reflog-head () - "Display the HEAD reflog." - (interactive) - (magit-reflog "HEAD")) - -;;; Modes (2) -;;;; Log Mode -;;;;; Log Core - -(define-derived-mode magit-log-mode magit-mode "Magit Log" - "Mode for looking at git log. - -\\Type `\\[magit-visit-item]` to visit a commit, and \ -`\\[magit-show-item-or-scroll-up]` to just show it. -Type `\\[magit-log-show-more-entries]` to show more commits, \ -and `\\[magit-refresh]` to refresh the log. -Type `\\[magit-diff-working-tree]` to see the diff between current commit and your working tree, -Type `\\[magit-diff]` to see diff between any two version -Type `\\[magit-apply-item]` to apply the change of the current commit to your wortree, -and `\\[magit-cherry-pick-item]` to apply and commit the result. -Type `\\[magit-revert-item]` to revert a commit, and `\\[magit-reset-head]` reset your current head to a commit, - -More information can be found in Info node `(magit)History' - -Other key binding: -\\{magit-log-mode-map}" - :group 'magit) - -(defvar magit-log-buffer-name "*magit-log*" - "Name of buffer used to display log entries.") - -(defun magit-refresh-log-buffer (style range args &optional file) - (magit-set-buffer-margin (car magit-log-margin-spec) - (and magit-log-show-margin - (eq (car magit-refresh-args) 'oneline))) - (magit-log-margin-set-timeunit-width) - (setq magit-file-log-file file) - (when (consp range) - (setq range (concat (car range) ".." (cdr range)))) - (magit-git-insert-section - (logbuf (concat "Commits" - (and file (concat " for file " file)) - (and range (concat " in " range)))) - (apply-partially 'magit-wash-log style 'color t) - "log" - (format "--max-count=%d" magit-log-cutoff-length) - "--decorate=full" "--color" - (cl-case style - (long (if magit-log-show-gpg-status - (list "--stat" "--show-signature") - "--stat")) - (oneline (concat "--pretty=format:%h%d " - (and magit-log-show-gpg-status "%G?") - "[%an][%at]%s"))) - args range "--" file) - (save-excursion - (goto-char (point-min)) - (magit-format-log-margin))) - -;;;;; Log Washing - -(defconst magit-log-oneline-re - (concat "^" - "\\(?4:\\(?: *[-_/|\\*o.] *\\)+ *\\)?" ; graph - "\\(?:" - "\\(?1:[0-9a-fA-F]+\\) " ; sha1 - "\\(?:\\(?3:([^()]+)\\) \\)?" ; refs - "\\(?7:[BGUN]\\)?" ; gpg - "\\[\\(?5:[^]]*\\)\\]" ; author - "\\[\\(?6:[^]]*\\)\\]" ; date - "\\(?2:.*\\)" ; msg - "\\)?$")) - -(defconst magit-log-long-re - (concat "^" - "\\(?4:\\(?:[-_/|\\*o.] *\\)+ *\\)?" ; graph - "\\(?:" - "\\(?:commit \\(?1:[0-9a-fA-F]+\\)" ; sha1 - "\\(?: \\(?3:([^()]+)\\)\\)?\\)" ; refs - "\\|" - "\\(?2:.+\\)\\)$")) ; "msg" - -(defconst magit-log-unique-re - (concat "^" - "\\(?1:[0-9a-fA-F]+\\) " ; sha1 - "\\(?2:.*\\)$")) ; msg - -(defconst magit-log-cherry-re - (concat "^" - "\\(?8:[-+]\\) " ; cherry - "\\(?1:[0-9a-fA-F]+\\) " ; sha1 - "\\(?2:.*\\)$")) ; msg - -(defconst magit-log-bisect-vis-re - (concat "^" - "\\(?1:[0-9a-fA-F]+\\) " ; sha1 - "\\(?:\\(?3:([^()]+)\\) \\)?" ; refs - "\\(?2:.+\\)$")) ; msg - -(defconst magit-log-bisect-log-re - (concat "^# " - "\\(?3:bad:\\|skip:\\|good:\\) " ; "refs" - "\\[\\(?1:[^]]+\\)\\] " ; sha1 - "\\(?2:.+\\)$")) ; msg - -(defconst magit-log-reflog-re - (concat "^" - "\\(?1:[^ ]+\\) " ; sha1 - "\\[\\(?5:[^]]*\\)\\] " ; author - "\\(?6:[^ ]*\\) " ; date - "[^@]+@{\\(?9:[^}]+\\)} " ; refsel - "\\(?10:merge\\|[^:]+\\)?:? ?" ; refsub - "\\(?2:.+\\)?$")) ; msg - -(defconst magit-reflog-subject-re - (concat "\\([^ ]+\\) ?" ; command (1) - "\\(\\(?: ?-[^ ]+\\)+\\)?" ; option (2) - "\\(?: ?(\\([^)]+\\))\\)?")) ; type (3) - -(defvar magit-log-count nil) - -(defun magit-wash-log (style &optional color longer) - (when color - (let ((ansi-color-apply-face-function - (lambda (beg end face) - (when face - (put-text-property beg end 'font-lock-face face))))) - (ansi-color-apply-on-region (point-min) (point-max)))) - (when (eq style 'cherry) - (reverse-region (point-min) (point-max))) - (let ((magit-log-count 0)) - (magit-wash-sequence (apply-partially 'magit-wash-log-line style - (magit-abbrev-length))) - (when (and longer - (= magit-log-count magit-log-cutoff-length)) - (magit-with-section (section longer 'longer) - (insert-text-button "type \"e\" to show more history" - 'action (lambda (button) - (magit-log-show-more-entries)) - 'follow-link t - 'mouse-face magit-item-highlight-face))))) - -(defun magit-wash-log-line (style abbrev) - (looking-at (cl-ecase style - (oneline magit-log-oneline-re) - (long magit-log-long-re) - (unique magit-log-unique-re) - (cherry magit-log-cherry-re) - (reflog magit-log-reflog-re) - (bisect-vis magit-log-bisect-vis-re) - (bisect-log magit-log-bisect-log-re))) - (magit-bind-match-strings - (hash msg refs graph author date gpg cherry refsel refsub) - (delete-region (point) (point-at-eol)) - (when cherry - (magit-insert cherry (if (string= cherry "-") - 'magit-cherry-equivalent - 'magit-cherry-unmatched) ?\s)) - (unless (eq style 'long) - (when (eq style 'bisect-log) - (setq hash (magit-git-string "rev-parse" "--short" hash))) - (if hash - (insert (propertize hash 'face 'magit-log-sha1) ?\s) - (insert (make-string (1+ abbrev) ? )))) - (when graph - (if magit-log-format-graph-function - (insert (funcall magit-log-format-graph-function graph)) - (insert graph))) - (when (and hash (eq style 'long)) - (magit-insert (if refs hash (magit-rev-parse hash)) 'magit-log-sha1 ?\s)) - (when refs - (magit-insert-ref-labels refs)) - (when refsub - (insert (format "%-2s " refsel)) - (insert (magit-log-format-reflog refsub))) - (when msg - (magit-insert msg (cl-case (and gpg (aref gpg 0)) - (?G 'magit-signature-good) - (?B 'magit-signature-bad) - (?U 'magit-signature-untrusted) - (?N 'magit-signature-none) - (t 'magit-log-message)))) - (goto-char (line-beginning-position)) - (magit-format-log-margin author date) - (if hash - (magit-with-section (section commit hash) - (when (derived-mode-p 'magit-log-mode) - (cl-incf magit-log-count)) - (forward-line) - (when (eq style 'long) - (magit-wash-sequence - (lambda () - (looking-at magit-log-long-re) - (when (match-string 2) - (magit-wash-log-line 'long abbrev)))))) - (forward-line))) - t) - -(defun magit-log-format-unicode-graph (string) - "Translate ascii characters to unicode characters. -Whether that actually is an improvment depends on the unicode -support of the font in use. The translation is done using the -alist in `magit-log-format-unicode-graph-alist'." - (replace-regexp-in-string - "[/|\\*o ]" - (lambda (str) - (propertize - (string (or (cdr (assq (aref str 0) - magit-log-format-unicode-graph-alist)) - (aref str 0))) - 'face (get-text-property 0 'face str))) - string)) - -(defun magit-format-log-margin (&optional author date) - (when magit-log-show-margin - (cl-destructuring-bind (width characterp duration-spec) - magit-log-margin-spec - (if author - (magit-make-margin-overlay - (propertize (truncate-string-to-width - author (- width 1 3 (if characterp 0 1) - magit-log-margin-timeunit-width 1) - nil ?\s (make-string 1 magit-ellipsis)) - 'face 'magit-log-author) - " " - (propertize (magit-format-duration - (abs (truncate (- (float-time) - (string-to-number date)))) - (symbol-value duration-spec) - magit-log-margin-timeunit-width) - 'face 'magit-log-date) - (propertize " " 'face 'fringe)) - (magit-make-margin-overlay - (propertize (make-string (1- width) ?\s) 'face 'default) - (propertize " " 'face 'fringe)))))) - -;;;;; Log Commands - -(defun magit-log-toggle-margin () - "Show or hide the log margin. -This command can only be used inside log buffers (usually -*magit-log*) and only if that displays a `oneline' log. -Also see option `magit-log-show-margin'." - (interactive) - (unless (derived-mode-p 'magit-log-mode) - (user-error "The log margin cannot be used outside of log buffers")) - (when (eq (car magit-refresh-args) 'long) - (user-error "The log margin cannot be used with verbose logs")) - (if magit-log-show-margin - (magit-set-buffer-margin (car magit-log-margin-spec) - (not (cdr (window-margins)))) - (setq-local magit-log-show-margin t) - (magit-refresh))) - -(defun magit-log-show-more-entries (&optional arg) - "Grow the number of log entries shown. - -With no prefix optional ARG, show twice as many log entries. -With a numerical prefix ARG, add this number to the number of shown log entries. -With a non numeric prefix ARG, show all entries" - (interactive "P") - (setq-local magit-log-cutoff-length - (cond ((numberp arg) (+ magit-log-cutoff-length arg)) - (arg magit-log-infinite-length) - (t (* magit-log-cutoff-length 2)))) - (let ((old-point (point))) - (magit-refresh) - (goto-char old-point))) - -;;;; Cherry Mode - -(define-derived-mode magit-cherry-mode magit-mode "Magit Cherry" - "Mode for looking at commits not merged upstream. - -\\Type `\\[magit-toggle-section]` to show or hide \ -section, `\\[magit-visit-item]` to visit an item and \ -`\\[magit-show-item-or-scroll-up]` to show it. -Type `\\[magit-diff-working-tree]` to display change with your working tree, \ -when `\\[magit-diff]` to display change -between any two commit. -Type `\\[magit-cherry-pick-item]` to cherry-pick a commit, and \ -`\\[magit-apply-item]` to apply its change to your -working tree, without committing, and `\\[magit-key-mode-popup-merging]` to \ -merge. -`\\[magit-refresh]` will refresh current buffer. - - -Other key binding: -\\{magit-cherry-mode-map}") - -(defvar magit-cherry-buffer-name "*magit-cherry*" - "Name of buffer used to display commits not merged upstream.") - -;;;###autoload -(defun magit-cherry (head upstream) - "Show commits in a branch that are not merged in the upstream branch." - (interactive - (let ((head (magit-read-rev "Cherry head" (magit-get-current-branch)))) - (list head (magit-read-rev "Cherry upstream" - (magit-get-tracked-branch head nil t))))) - (magit-mode-setup magit-cherry-buffer-name nil - #'magit-cherry-mode - #'magit-refresh-cherry-buffer upstream head)) - -(defun magit-refresh-cherry-buffer (upstream head) - (magit-with-section (section cherry 'cherry nil t) - (run-hooks 'magit-cherry-sections-hook))) - -(defun magit-insert-cherry-head-line () - (magit-insert-line-section (line) - (concat "Head: " - (propertize (cadr magit-refresh-args) 'face 'magit-branch) " " - (abbreviate-file-name default-directory)))) - -(defun magit-insert-cherry-upstream-line () - (magit-insert-line-section (line) - (concat "Upstream: " - (propertize (car magit-refresh-args) 'face 'magit-branch)))) - -(defun magit-insert-cherry-help-lines () - (when (derived-mode-p 'magit-cherry-mode) - (insert "\n") - (magit-insert-line-section (line) - (concat (propertize "-" 'face 'magit-cherry-equivalent) - " equivalent exists in both refs")) - (magit-insert-line-section (line) - (concat (propertize "+" 'face 'magit-cherry-unmatched) - " unmatched commit tree")))) - -(defun magit-insert-cherry-commits () - (magit-git-insert-section (cherries "Cherry commits:") - (apply-partially 'magit-wash-log 'cherry) - "cherry" "-v" (magit-abbrev-arg) magit-refresh-args)) - -;;;; Reflog Mode - -(defvar magit-reflog-buffer-name "*magit-reflog*" - "Name of buffer used to display reflog entries.") - -(define-derived-mode magit-reflog-mode magit-log-mode "Magit Reflog" - "Mode for looking at git reflog. - -\\Type `\\[magit-visit-item]` to visit a commit, and \ -`\\[magit-show-item-or-scroll-up]` to just show it. -Type `\\[magit-diff-working-tree]` to see the diff between current commit and \ -your working tree, -Type `\\[magit-diff]` to see the between any two version. -Type `\\[magit-reset-head]` to reset your head to the current commit, and \ -`\\[magit-apply-item]` to apply its change -to your working tree and `\\[magit-cherry-pick-item]` to cherry pick it. - -More information can be found in Info node `(magit)Reflogs' - -Other key binding: -\\{magit-reflog-mode-map}" - :group 'magit) - -(defun magit-refresh-reflog-buffer (ref) - (magit-log-margin-set-timeunit-width) - (magit-git-insert-section - (reflogbuf (format "Local history of branch %s" ref)) - (apply-partially 'magit-wash-log 'reflog t) - "reflog" "show" "--format=format:%h [%an] %ct %gd %gs" - (format "--max-count=%d" magit-log-cutoff-length) ref)) - -(defvar magit-reflog-labels - '(("commit" . magit-log-reflog-label-commit) - ("amend" . magit-log-reflog-label-amend) - ("merge" . magit-log-reflog-label-merge) - ("checkout" . magit-log-reflog-label-checkout) - ("branch" . magit-log-reflog-label-checkout) - ("reset" . magit-log-reflog-label-reset) - ("rebase" . magit-log-reflog-label-rebase) - ("cherry-pick" . magit-log-reflog-label-cherry-pick) - ("initial" . magit-log-reflog-label-commit) - ("pull" . magit-log-reflog-label-remote) - ("clone" . magit-log-reflog-label-remote))) - -(defun magit-log-format-reflog (subject) - (let* ((match (string-match magit-reflog-subject-re subject)) - (command (and match (match-string 1 subject))) - (option (and match (match-string 2 subject))) - (type (and match (match-string 3 subject))) - (label (if (string= command "commit") - (or type command) - command)) - (text (if (string= command "commit") - label - (mapconcat #'identity - (delq nil (list command option type)) - " ")))) - (format "%-16s " - (propertize text 'face - (or (cdr (assoc label magit-reflog-labels)) - 'magit-log-reflog-label-other))))) - -;;;; Ediff Support - -(defvar magit-ediff-buffers nil - "List of buffers that may be killed by `magit-ediff-restore'.") - -(defvar magit-ediff-windows nil - "The window configuration that will be restored when Ediff is finished.") - -(defun magit-ediff () - "View the current DIFF section in ediff." - (interactive) - (let ((diff (magit-current-section))) - (when (eq (magit-section-type (magit-current-section)) 'diffstat) - (setq diff (magit-diff-section-for-diffstat diff))) - (when (magit-section-hidden diff) - ;; Range is not set until the first time the diff is visible. - ;; This somewhat hackish code makes sure it's been visible at - ;; least once. - (magit-toggle-section) - (magit-toggle-section) - (setq diff (magit-current-section))) - (when (eq 'hunk (magit-section-type diff)) - (setq diff (magit-section-parent diff))) - (unless (eq 'diff (magit-section-type diff)) - (user-error "No diff at this location")) - (let* ((status (magit-section-diff-status diff)) - (file1 (magit-section-info diff)) - (file2 (magit-section-diff-file2 diff)) - (range (magit-diff-range diff))) - (cond - ((memq status '(new deleted typechange)) - (message "Why ediff a %s file?" status)) - ((and (eq status 'unmerged) - (eq (cdr range) 'working)) - (magit-interactive-resolve file1)) - ((consp (car range)) - (magit-ediff-buffers3 (magit-show (caar range) file2) - (magit-show (cdar range) file2) - (magit-show (cdr range) file1))) - (t - (magit-ediff-buffers (magit-show (car range) file2) - (magit-show (cdr range) file1))))))) - -(defun magit-ediff-buffers (a b) - (setq magit-ediff-buffers (list a b)) - (setq magit-ediff-windows (current-window-configuration)) - (ediff-buffers a b '(magit-ediff-add-cleanup))) - -(defun magit-ediff-buffers3 (a b c) - (setq magit-ediff-buffers (list a b c)) - (setq magit-ediff-windows (current-window-configuration)) - (ediff-buffers3 a b c '(magit-ediff-add-cleanup))) - -(defun magit-diff-range (diff) - (if (eq major-mode 'magit-commit-mode) - (let ((revs (split-string - (magit-git-string "rev-list" "-1" "--parents" - (car (last magit-refresh-args)))))) - (when (= (length revs) 2) - (cons (cadr revs) (car revs)))) - (magit-section-diff-range diff))) - -(defun magit-ediff-add-cleanup () - (make-local-variable 'magit-ediff-buffers) - (setq-default magit-ediff-buffers ()) - - (make-local-variable 'magit-ediff-windows) - (setq-default magit-ediff-windows ()) - - (add-hook 'ediff-cleanup-hook 'magit-ediff-restore 'append 'local)) - -(defun magit-ediff-restore () - "Kill any buffers in `magit-ediff-buffers' that are not visiting files and -restore the window state that was saved before ediff was called." - (dolist (buffer magit-ediff-buffers) - (when (and (null (buffer-file-name buffer)) - (buffer-live-p buffer)) - (with-current-buffer buffer - (when (and (eq magit-show-current-version 'index) - (buffer-modified-p)) - (magit-save-index))) - (kill-buffer buffer))) - (let ((buf (current-buffer))) - (set-window-configuration magit-ediff-windows) - (set-buffer buf))) - -;;;###autoload -(defun magit-save-index () - "Add the content of current file as if it was the index." - (interactive) - (unless (eq magit-show-current-version 'index) - (user-error "Current buffer doesn't visit the index version of a file")) - (when (y-or-n-p (format "Stage current version of %s? " magit-file-name)) - (let ((buf (current-buffer)) - (name (magit-git-dir "magit-add-index"))) - (with-temp-file name - (insert-buffer-substring buf)) - (let ((hash (magit-git-string "hash-object" "-t" "blob" "-w" - (concat "--path=" magit-file-name) - "--" name)) - (perm (substring (magit-git-string "ls-files" "-s" - magit-file-name) - 0 6))) - (magit-run-git "update-index" "--cacheinfo" - perm hash magit-file-name))))) - -;;;###autoload -(defun magit-interactive-resolve (file) - "Resolve a merge conflict using Ediff." - (interactive (list (magit-section-case (info) (diff (cadr info))))) - (require 'ediff) - (let ((merge-status (magit-git-lines "ls-files" "-u" "--" file)) - (base-buffer) - (our-buffer (generate-new-buffer (concat file ".current"))) - (their-buffer (generate-new-buffer (concat file ".merged"))) - (merger-buffer) - (windows (current-window-configuration))) - (unless merge-status - (user-error "Cannot resolve %s" file)) - (when (string-match "^[0-9]+ [0-9a-f]+ 1" (car merge-status)) - (pop merge-status) - (setq base-buffer (generate-new-buffer (concat file ".base"))) - (with-current-buffer base-buffer - (magit-git-insert "cat-file" "blob" (concat ":1:" file)))) - ;; If the second or third version do not exit, we use an empty buffer for the deleted file - (with-current-buffer our-buffer - (when (string-match "^[0-9]+ [0-9a-f]+ 2" (car merge-status)) - (pop merge-status) - (magit-git-insert "cat-file" "blob" (concat ":2:" file))) - (let ((buffer-file-name file)) - (normal-mode t))) - (with-current-buffer their-buffer - (when (string-match "^[0-9]+ [0-9a-f]+ 3" (car merge-status)) - (magit-git-insert "cat-file" "blob" (concat ":3:" file))) - (let ((buffer-file-name file)) - (normal-mode t))) - ;; We have now created the 3 buffer with ours, theirs and the ancestor files - (if base-buffer - (setq merger-buffer (ediff-merge-buffers-with-ancestor - our-buffer their-buffer base-buffer nil nil file)) - (setq merger-buffer (ediff-merge-buffers our-buffer their-buffer nil nil file))) - (with-current-buffer merger-buffer - (setq ediff-show-clashes-only t) - (setq-local magit-ediff-windows windows) - (make-local-variable 'ediff-quit-hook) - (add-hook 'ediff-quit-hook - (lambda () - (let ((buffer-A ediff-buffer-A) - (buffer-B ediff-buffer-B) - (buffer-C ediff-buffer-C) - (buffer-Ancestor ediff-ancestor-buffer) - (windows magit-ediff-windows)) - (ediff-cleanup-mess) - (kill-buffer buffer-A) - (kill-buffer buffer-B) - (when (bufferp buffer-Ancestor) - (kill-buffer buffer-Ancestor)) - (set-window-configuration windows))))))) - -;;;; Diff Mode -;;;;; Diff Core - -(define-derived-mode magit-diff-mode magit-mode "Magit Diff" - "Mode for looking at a git diff. - -\\Type `\\[magit-visit-item]` to visit the changed file, \ -`\\[magit-toggle-section]` to hide or show a hunk, -`\\[magit-diff-larger-hunks]` and `\\[magit-diff-smaller-hunks]` to change \ -the size of the hunks. -Type `\\[magit-apply-item]` to apply a change to your worktree and \ -`\\[magit-revert-item]` to reverse it. -You can also use `\\[magit-ediff]` to see the current change with ediff. - -More information can be found in Info node `(magit)Diffing' - -\\{magit-diff-mode-map}" - :group 'magit) - -(defvar magit-diff-buffer-name "*magit-diff*" - "Name of buffer used to display a diff.") - -(defvar magit-stash-buffer-name "*magit-stash*" - "Name of buffer used to display a stash.") - -;;;;; Diff Entry Commands - -;;;###autoload -(defun magit-diff (range &optional working args) - "Show differences between two commits. -RANGE should be a range (A..B or A...B) but can also be a single -commit. If one side of the range is omitted, then it defaults -to HEAD. If just a commit is given, then changes in the working -tree relative to that commit are shown." - (interactive (list (magit-read-rev-range "Diff"))) - (magit-mode-setup magit-diff-buffer-name - #'pop-to-buffer - #'magit-diff-mode - #'magit-refresh-diff-buffer range working args)) - -;;;###autoload -(defun magit-diff-working-tree (rev) - "Show differences between a commit and the current working tree." - (interactive (list (magit-read-rev-with-default "Diff working tree with"))) - (magit-diff (or rev "HEAD") t)) - -;;;###autoload -(defun magit-diff-staged () - "Show differences between the index and the HEAD commit." - (interactive) - (magit-diff nil nil (list "--cached"))) - -;;;###autoload -(defun magit-diff-unstaged () - "Show differences between the current working tree and index." - (interactive) - (magit-diff nil)) - -;;;###autoload -(defun magit-diff-stash (stash &optional noselect) - "Show changes in a stash. -A Stash consist of more than just one commit. This command uses -a special diff range so that the stashed changes actually were a -single commit." - (interactive (list (magit-read-stash "Show stash (number): "))) - (magit-mode-setup magit-commit-buffer-name - (if noselect 'display-buffer 'pop-to-buffer) - #'magit-diff-mode - #'magit-refresh-diff-buffer - (concat stash "^2^.." stash))) - -(defun magit-diff-with-mark (range) - "Show difference between the marked commit and the one at point. -If there is no commit at point, then prompt for one." - (interactive - (let* ((marked (or magit-marked-commit (user-error "No commit marked"))) - (current (magit-get-current-branch)) - (is-current (string= (magit-name-rev marked) current)) - (commit (or (magit-guess-branch) - (magit-read-rev - (format "Diff marked commit %s with" marked) - (unless is-current current) - current)))) - (list (concat marked ".." commit)))) - (magit-diff range)) - -(defun magit-refresh-diff-buffer (range &optional working args) - (let ((magit-current-diff-range - (cond (working (cons range 'working)) - ((null range) nil) - ((consp range) - (prog1 range - (setq range (concat (car range) ".." (cdr range))))) - ((string-match "^\\([^.]+\\)\\.\\.\\([^.]\\)$" range) - (cons (match-string 1 range) - (match-string 2 range)))))) - (magit-git-insert-section - (diffbuf (cond (working - (format "Changes from %s to working tree" range)) - ((not range) - (if (member "--cached" args) - "Staged changes" - "Unstaged changes")) - (t - (format "Changes in %s" range)))) - #'magit-wash-diffs - "diff" (magit-diff-U-arg) - (and magit-show-diffstat "--patch-with-stat") - range args "--"))) - -;;;;; Diff Washing - -(defconst magit-diff-statline-re - (concat "^ ?" - "\\(.*\\)" ; file - "\\( +| +\\)" ; separator - "\\([0-9]+\\|Bin\\(?: +[0-9]+ -> [0-9]+ bytes\\)?$\\) ?" - "\\(\\+*\\)" ; add - "\\(-*\\)$")) ; del - -(defvar magit-current-diff-range nil - "Used internally when setting up magit diff sections.") - -(defvar-local magit-diffstat-cached-sections nil) -(put 'magit-diffstat-cached-sections 'permanent-local t) - -(defun magit-wash-diffs () - (magit-wash-diffstats) - (when (re-search-forward "^diff" nil t) - (goto-char (line-beginning-position)) - (magit-wash-sequence #'magit-wash-diff)) - (goto-char (point-max)) - (magit-xref-insert-buttons)) - -(defun magit-wash-diffstats () - (let ((beg (point))) - (when (re-search-forward "^ ?\\([0-9]+ +files? change[^\n]*\n\\)" nil t) - (let ((heading (match-string-no-properties 1))) - (delete-region (match-beginning 0) (match-end 0)) - (goto-char beg) - (magit-with-section (section diffstats 'diffstats heading) - (magit-wash-sequence #'magit-wash-diffstat))) - (setq magit-diffstat-cached-sections - (nreverse magit-diffstat-cached-sections))))) - -(defun magit-wash-diffstat () - (when (looking-at magit-diff-statline-re) - (magit-bind-match-strings (file sep cnt add del) - (delete-region (point) (1+ (line-end-position))) - (magit-with-section (section diffstat 'diffstat) - (insert " " file sep cnt " ") - (when add (insert (propertize add 'face 'magit-diff-add))) - (when del (insert (propertize del 'face 'magit-diff-del))) - (insert "\n") - (push section magit-diffstat-cached-sections))))) - -(defun magit-wash-diff () - (magit-with-section (section diff (buffer-substring-no-properties - (line-beginning-position) - (line-end-position))) - (setq section (magit-wash-diff-section section)))) - -(defun magit-wash-diff-section (section) - (cond ((re-search-forward "^\\* Unmerged path \\(.*\\)" nil t) - (forward-line 0) - (let ((file (magit-decode-git-path (match-string-no-properties 1)))) - (delete-region (point) (line-end-position)) - (insert "\tUnmerged " file "\n") - (setf (magit-section-diff-status section) 'unmerged) - (setf (magit-section-info section) file) - section)) - ((re-search-forward "^diff" nil t) - (forward-line 0) - (let ((file (cond - ((looking-at "^diff --git \\(\".*\"\\) \\(\".*\"\\)$") - (substring (magit-decode-git-path - (match-string-no-properties 2)) 2)) - ((looking-at "^diff --git ./\\(.*\\) ./\\(.*\\)$") - (match-string-no-properties 2)) - ((looking-at "^diff --cc +\\(.*\\)$") - (match-string-no-properties 1)))) - (end (save-excursion - (forward-line) ;; skip over "diff" line - (if (re-search-forward "^diff\\|^@@" nil t) - (goto-char (match-beginning 0)) - (goto-char (point-max))) - (point-marker)))) - (when magit-diffstat-cached-sections - (setf (magit-section-info (pop magit-diffstat-cached-sections)) - file)) - (let ((status (cond - ((looking-at "^diff --cc") - 'unmerged) - ((save-excursion - (re-search-forward "^new file" end t)) - 'new) - ((save-excursion - (re-search-forward "^deleted" end t)) - (setf (magit-section-hidden section) t) - 'deleted) - ((save-excursion - (re-search-forward "^rename" end t)) - 'renamed) - (t - 'modified))) - (file2 (cond - ((save-excursion - (re-search-forward "^rename from \\(.*\\)" - end t)) - (match-string-no-properties 1))))) - (setf (magit-section-diff-status section) status) - (setf (magit-section-info section) file) - (setf (magit-section-diff-file2 section) (or file2 file)) - (setf (magit-section-diff-range section) magit-current-diff-range) - (magit-insert-diff-title status file file2) - (when (re-search-forward - "\\(--- \\(.*\\)\n\\+\\+\\+ \\(.*\\)\n\\)" nil t) - (magit-put-face-property (match-beginning 1) (match-end 1) - 'magit-diff-hunk-header) - (magit-put-face-property (match-beginning 2) (match-end 2) - 'magit-diff-file-header) - (magit-put-face-property (match-beginning 3) (match-end 3) - 'magit-diff-file-header)) - (goto-char end) - (magit-wash-sequence #'magit-wash-hunk))) - section))) - -(defun magit-insert-diff-title (status file file2) - (insert (format "\t%-10s " (capitalize (symbol-name status))) - file - (if (eq status 'renamed) (format " (from %s)" file2) "") - "\n")) - -(defun magit-wash-hunk () - (when (looking-at "^@@\\(@\\)?.+") - (let ((merging (match-beginning 1))) - (magit-with-section (section hunk (match-string 0)) - (magit-put-face-property (point) (line-end-position) - 'magit-diff-hunk-header) - (forward-line) - (while (not (or (eobp) (looking-at "^diff\\|^@@"))) - (magit-put-face-property - (point) (line-end-position) - (cond - ((looking-at "^\\+\\+<<<<<<<") 'magit-diff-merge-current) - ((looking-at "^\\+\\+=======") 'magit-diff-merge-separator) - ((looking-at "^\\+\\+|||||||") 'magit-diff-merge-diff3-separator) - ((looking-at "^\\+\\+>>>>>>>") 'magit-diff-merge-proposed) - ((looking-at (if merging "^\\(\\+\\| \\+\\)" "^\\+")) - (magit-diff-highlight-whitespace merging) - 'magit-diff-add) - ((looking-at (if merging "^\\(-\\| \\-\\)" "^-")) - (magit-diff-highlight-whitespace merging) - 'magit-diff-del) - (t - 'magit-diff-none))) - (forward-line)) - (when (eq magit-diff-refine-hunk 'all) - (magit-diff-refine-hunk section)))) - t)) - -(defun magit-diff-highlight-whitespace (merging) - (when (and magit-highlight-whitespace - (or (derived-mode-p 'magit-status-mode) - (not (eq magit-highlight-whitespace 'status)))) - (let ((prefix (if merging "^[-\\+\s]\\{2\\}" "^[-\\+]")) - (indent - (if (local-variable-p 'magit-highlight-indentation) - magit-highlight-indentation - (setq-local - magit-highlight-indentation - (cdr (cl-find-if (lambda (pair) - (string-match-p (car pair) default-directory)) - (default-value 'magit-highlight-indentation) - :from-end t)))))) - (when (and magit-highlight-trailing-whitespace - (looking-at (concat prefix ".*?\\([ \t]+\\)$"))) - (magit-put-face-property (match-beginning 1) (match-end 1) - 'magit-whitespace-warning-face)) - (when (or (and (eq indent 'tabs) - (looking-at (concat prefix "\\( *\t[ \t]*\\)"))) - (and (integerp indent) - (looking-at (format "%s\\([ \t]* \\{%s,\\}[ \t]*\\)" - prefix indent)))) - (magit-put-face-property (match-beginning 1) (match-end 1) - 'magit-whitespace-warning-face))))) - -;;;;; Diff Mode Commands - -(defun magit-diff-toggle-refine-hunk (&optional other) - "Turn diff-hunk refining on or off. - -If hunk refining is currently on, then hunk refining is turned off. -If hunk refining is off, then hunk refining is turned on, in -`selected' mode (only the currently selected hunk is refined). - -With a prefix argument, the \"third choice\" is used instead: -If hunk refining is currently on, then refining is kept on, but -the refining mode (`selected' or `all') is switched. -If hunk refining is off, then hunk refining is turned on, in -`all' mode (all hunks refined). - -Customize variable `magit-diff-refine-hunk' to change the default mode." - (interactive "P") - (let ((hunk (and magit-highlighted-section - (eq (magit-section-type magit-highlighted-section) 'hunk) - magit-highlighted-section)) - (old magit-diff-refine-hunk)) - (setq-local magit-diff-refine-hunk - (if other - (if (eq old 'all) t 'all) - (not old))) - (cond ((or (eq old 'all) - (eq magit-diff-refine-hunk 'all)) - (magit-refresh)) - ((not hunk)) - (magit-diff-refine-hunk - (magit-diff-refine-hunk hunk)) - (t - (magit-diff-unrefine-hunk hunk))) - (message "magit-diff-refine-hunk: %s" magit-diff-refine-hunk))) - -(defun magit-diff-refine-hunk (hunk) - (save-excursion - (goto-char (magit-section-beginning hunk)) - ;; `diff-refine-hunk' does not handle combined diffs. - (unless (looking-at "@@@") - (diff-refine-hunk)))) - -(defun magit-diff-unrefine-hunk (hunk) - (remove-overlays (magit-section-beginning hunk) - (magit-section-end hunk) - 'diff-mode 'fine)) - -;;;; Wazzup Mode - -(define-derived-mode magit-wazzup-mode magit-mode "Magit Wazzup" - "Mode for looking at git commits not merged into current HEAD. - -\\Type `\\[magit-toggle-section]` to show or hide \ -section, `\\[magit-visit-item]` to visit an item \ -`\\[magit-show-item-or-scroll-up]` to show it. -Type `\\[magit-diff-working-tree]` to display change with your working tree, \ -and `\\[magit-diff]` to display change -between any two commit. -Type `\\[magit-cherry-pick-item]` to cherry-pick a commit, and \ -`\\[magit-apply-item]` to apply its change to your -working tree, without committing, and `\\[magit-key-mode-popup-merging]` \ -to merge those change. -Type `\\[magit-refresh]` to refresh current buffer. - -More information can be found in Info node `(magit)Wazzup' - -\\{magit-wazzup-mode-map}" - :group 'magit) - -(defvar magit-wazzup-buffer-name "*magit-wazzup*" - "Name of buffer used to display commits not merged into current HEAD.") - -;;;###autoload -(defun magit-wazzup (branch) - "Show a list of branches in a dedicated buffer. -Unlike in the buffer created by `magit-branch-manager' each -branch can be expanded to show a list of commits not merged -into the selected branch." - (interactive - (let ((branch (magit-get-current-branch))) - (list (if current-prefix-arg - (magit-read-rev "Wazzup branch" branch) - branch)))) - (magit-mode-setup magit-wazzup-buffer-name nil - #'magit-wazzup-mode - #'magit-refresh-wazzup-buffer branch)) - -(defun magit-refresh-wazzup-buffer (head) - (magit-with-section (section wazzupbuf 'wazzupbuf nil t) - (run-hooks 'magit-wazzup-sections-hook))) - -(defun magit-insert-wazzup-head-line () - (magit-insert-line-section (line) - (concat "Head: " - (propertize (car magit-refresh-args) 'face 'magit-branch) " " - (abbreviate-file-name default-directory)))) - -(defun magit-insert-wazzup-branches () - (dolist (upstream (magit-git-lines "show-ref")) - (setq upstream (cadr (split-string upstream " "))) - (when (and (not (string-match-p "HEAD$" upstream)) - (string-match-p "^refs/\\(heads\\|remotes\\)/" upstream)) - (magit-insert-wazzup-commits upstream (car magit-refresh-args))))) - -(defun magit-insert-wazzup-commits (upstream head) - (let ((count (string-to-number - (magit-git-string "rev-list" "--count" "--right-only" - (concat head "..." upstream))))) - (when (> count 0) - (magit-with-section - (section wazzup upstream - (format "%3s %s\n" count (magit-format-ref-label upstream)) - nil t) - (cond - ((magit-section-hidden section) - (setf (magit-section-hidden section) t) - (setf (magit-section-needs-refresh-on-show section) t)) - (t - (let ((beg (point))) - (magit-git-insert "cherry" "-v" "--abbrev" head upstream) - (save-restriction - (narrow-to-region beg (point)) - (goto-char (point-min)) - (magit-wash-log 'cherry))))))))) - -;;;; Branch Manager Mode -;;;;; (core) - -(define-derived-mode magit-branch-manager-mode magit-mode "Magit Branch" - "Mode for looking at git branches. - -\\Type `\\[magit-visit-item]` to checkout a branch, `\\[magit-reset-head]' to reset current branch, -you can also merge the branch with `\\[magit-key-mode-popup-merging]` - -Type `\\[magit-discard-item]' to delete a branch, or `\\[universal-argument] \\[magit-discard-item]' to force the deletion. -Type `\\[magit-rename-item]' to Rename a branch. - -More information can be found in Info node `(magit)The branch list' - -\\{magit-branch-manager-mode-map} -Unless shadowed by the mode specific bindings above, bindings -from the parent keymap `magit-mode-map' are also available.") - -(defvar magit-branches-buffer-name "*magit-branches*" - "Name of buffer used to display and manage branches.") - -;;;###autoload -(defun magit-branch-manager () - "Show a list of branches in a dedicated buffer." - (interactive) - (if (magit-get-top-dir) ; Kludge for #1215 - (magit-mode-setup magit-branches-buffer-name nil - #'magit-branch-manager-mode - #'magit-refresh-branch-manager) - (user-error "There is no Git repository here"))) - -(defun magit-refresh-branch-manager () - (magit-git-insert-section (branchbuf nil) - #'magit-wash-branches - "branch" "-vva" magit-custom-options)) - -;;;;; Branch List Washing - -(defconst magit-wash-branch-line-re - (concat "^\\([ *] \\)" ; 1: current branch marker - "\\(.+?\\) +" ; 2: branch name - "\\(?:" - "\\([0-9a-fA-F]+\\)" ; 3: sha1 - " " - "\\(?:\\[" - "\\([^:\n]+?\\)" ; 4: tracking - "\\(?:: \\)?" - "\\(?:ahead \\([0-9]+\\)\\)?" ; 5: ahead - "\\(?:, \\)?" - "\\(?:behind \\([0-9]+\\)\\)?" ; 6: behind - "\\] \\)?" - "\\(?:.*\\)" ; message - "\\|" ; or - "-> " ; the pointer to - "\\(.+\\)" ; 7: a ref - "\\)\n")) - -(defun magit-wash-branch-line (&optional remote-name) - (when (looking-at magit-wash-branch-line-re) - ;; ^ Kludge for #1162. v Don't reindent for now. - (let* ((marker (match-string 1)) - (branch (match-string 2)) - (sha1 (match-string 3)) - (tracking (match-string 4)) - (ahead (match-string 5)) - (behind (match-string 6)) - (other-ref (match-string 7)) - (branch-face (and (equal marker "* ") 'magit-branch))) - (delete-region (point) (line-beginning-position 2)) - (magit-with-section (section branch branch) - (insert (propertize (or sha1 (make-string 7 ? )) - 'face 'magit-log-sha1) - " " marker - (propertize (if (string-match-p "^remotes/" branch) - (substring branch 8) - branch) - 'face branch-face)) - (when other-ref - (insert " -> " (substring other-ref (+ 1 (length remote-name))))) - (when (and tracking - (equal (magit-get-tracked-branch branch t) - (concat "refs/remotes/" tracking))) - (insert " [") - ;; getting rid of the tracking branch name if it is - ;; the same as the branch name - (let* ((remote (magit-get "branch" branch "remote")) - (merge (substring tracking (+ 1 (length remote))))) - (insert (propertize (if (string= branch merge) - (concat "@ " remote) - (concat merge " @ " remote)) - 'face 'magit-log-head-label-remote))) - (when (or ahead behind) - (insert ":") - (and ahead (insert "ahead " (propertize ahead 'face branch-face))) - (and ahead behind (insert ", ")) - (and behind (insert "behind " - (propertize behind 'face - 'magit-log-head-label-remote)))) - (insert "]")) - (insert "\n"))))) - -(defun magit-wash-remote-branches-group (group) - (let* ((remote (car group)) - (url (magit-get "remote" remote "url")) - (push-url (magit-get "remote" remote "pushurl")) - (urls (concat url (and push-url (concat ", " push-url)))) - (marker (cadr group))) - (magit-with-section - (section remote remote (format "%s (%s):" remote urls) t) - (magit-wash-branches-between-point-and-marker marker remote) - (insert "\n")))) - -(defun magit-wash-branches-between-point-and-marker (marker &optional remote-name) - (save-restriction - (narrow-to-region (point) marker) - (magit-wash-sequence - (apply-partially 'magit-wash-branch-line remote-name)))) - -(defun magit-wash-branches () - ;; get the names of the remotes - (let* ((remotes (magit-git-lines "remote")) - ;; get the location of remotes in the buffer - (markers - (append (mapcar (lambda (remote) - (save-excursion - (when (re-search-forward - (format "^ remotes/%s/" remote) nil t) - (beginning-of-line) - (point-marker)))) - remotes) - (list (save-excursion - (goto-char (point-max)) - (point-marker))))) - ;; list of remote elements to display in the buffer - (remote-groups - (cl-loop for remote in remotes - for end-markers on (cdr markers) - for marker = (cl-loop for x in end-markers thereis x) - collect (list remote marker)))) - ;; actual displaying of information - (magit-with-section (section local "." "Local:" t) - (magit-wash-branches-between-point-and-marker - (cl-loop for x in markers thereis x)) - (insert "\n")) - (mapc 'magit-wash-remote-branches-group remote-groups) - ;; make sure markers point to nil so that they can be garbage collected - (mapc (lambda (marker) - (when marker - (set-marker marker nil))) - markers))) - -;;;;; (commands) - -(defun magit-rename-item () - "Rename the item at point." - (interactive) - (magit-section-action rename () - (branch (call-interactively 'magit-rename-branch)) - (remote (call-interactively 'magit-rename-remote)))) - -(defun magit-change-what-branch-tracks () - "Change which remote branch the current branch tracks." - (interactive) - (let* ((branch (magit-guess-branch)) - (track (magit-read-rev "Track branch")) - (track- - (cond ((string-match "^\\([^ ]+\\) +(\\(.+\\))$" track) - (cons (match-string 2 track) - (concat "refs/heads/" (match-string 1 track)))) - ((string-match "^\\(?:refs/remotes/\\)?\\([^/]+\\)/\\(.+\\)" - track) - (cons (match-string 1 track) - (concat "refs/heads/" (match-string 2 track)))) - (t - (user-error "Cannot parse the remote and branch name"))))) - (magit-set (car track-) "branch" branch "remote") - (magit-set (cdr track-) "branch" branch "merge") - (magit-refresh))) - -;;; Miscellaneous -;;;; Miscellaneous Commands - -;;;###autoload -(defun magit-init (directory) - "Create or reinitialize a Git repository. -Read directory name and initialize it as new Git repository. - -If the directory is below an existing repository, then the user -has to confirm that a new one should be created inside; or when -the directory is the root of the existing repository, whether -it should be reinitialized. - -Non-interactively DIRECTORY is always (re-)initialized." - (interactive - (let* ((dir (file-name-as-directory - (expand-file-name - (read-directory-name "Create repository in: ")))) - (top (magit-get-top-dir dir))) - (if (and top - (not (yes-or-no-p - (if (string-equal top dir) - (format "Reinitialize existing repository %s? " dir) - (format "%s is a repository. Create another in %s? " - top dir))))) - (user-error "Abort") - (list dir)))) - (magit-run-git "init" (expand-file-name directory))) - -(defun magit-copy-item-as-kill () - "Copy sha1 of commit at point into kill ring." - (interactive) - (magit-section-action copy (info) - ((branch commit file diff) - (kill-new info) - (message "%s" info)))) - -(defun magit-ignore-item (edit &optional local) - "Ignore the item at point. -With a prefix argument edit the ignore string." - (interactive "P") - (magit-section-action ignore (info) - ([file untracked] - (magit-ignore-file (concat "/" info) edit local) - (magit-refresh)) - (diff - (when (yes-or-no-p (format "%s is tracked. Untrack and ignore? " info)) - (magit-ignore-file (concat "/" info) edit local) - (magit-run-git "rm" "--cached" info))))) - -(defun magit-ignore-item-locally (edit) - "Ignore the item at point locally only. -With a prefix argument edit the ignore string." - (interactive "P") - (magit-ignore-item edit t)) - -(defun magit-ignore-file (file &optional edit local) - "Add FILE to the list of files to ignore. -If EDIT is non-nil, prompt the user for the string to be ignored -instead of using FILE. The changes are written to .gitignore -except if LOCAL is non-nil in which case they are written to -.git/info/exclude." - (let* ((local-ignore-dir (magit-git-dir "info/")) - (ignore-file (if local - (concat local-ignore-dir "exclude") - ".gitignore"))) - (when edit - (setq file (magit-ignore-edit-string file))) - (when (and local (not (file-exists-p local-ignore-dir))) - (make-directory local-ignore-dir t)) - (with-temp-buffer - (when (file-exists-p ignore-file) - (insert-file-contents ignore-file)) - (goto-char (point-max)) - (unless (bolp) - (insert "\n")) - (insert file "\n") - (write-region nil nil ignore-file)))) - -(defun magit-ignore-edit-string (file) - "Prompt the user for the string to be ignored. -A list of predefined values with wildcards is derived from the -filename FILE." - (let* ((extension (concat "*." (file-name-extension file))) - (extension-in-dir (concat (file-name-directory file) extension)) - (filename (file-name-nondirectory file)) - (completions (list extension extension-in-dir filename file))) - (magit-completing-read "File/pattern to ignore" - completions nil nil nil nil file))) - -;;;; Commit Mark - -(defvar magit-marked-commit nil) - -(defvar-local magit-mark-overlay nil) -(put 'magit-mark-overlay 'permanent-local t) - -(defun magit-mark-item (&optional unmark) - "Mark the commit at point. -Some commands act on the marked commit by default or use it as -default when prompting for a commit." - (interactive "P") - (if unmark - (setq magit-marked-commit nil) - (magit-section-action mark (info) - (commit (setq magit-marked-commit - (if (equal magit-marked-commit info) nil info))))) - (magit-refresh-marked-commits) - (run-hooks 'magit-mark-commit-hook)) - -(defun magit-refresh-marked-commits () - (magit-map-magit-buffers #'magit-refresh-marked-commits-in-buffer)) - -(defun magit-refresh-marked-commits-in-buffer () - (unless magit-mark-overlay - (setq magit-mark-overlay (make-overlay 1 1)) - (overlay-put magit-mark-overlay 'face 'magit-item-mark)) - (delete-overlay magit-mark-overlay) - (magit-map-sections - (lambda (section) - (when (and (eq (magit-section-type section) 'commit) - (equal (magit-section-info section) - magit-marked-commit)) - (move-overlay magit-mark-overlay - (magit-section-beginning section) - (magit-section-end section) - (current-buffer)))) - magit-root-section)) - -;;;; ChangeLog - -;;;###autoload -(defun magit-add-change-log-entry (&optional whoami file-name other-window) - "Find change log file and add date entry and item for current change. -This differs from `add-change-log-entry' (which see) in that -it acts on the current hunk in a Magit buffer instead of on -a position in a file-visiting buffer." - (interactive (list current-prefix-arg - (prompt-for-change-log-name))) - (let (buf pos) - (save-window-excursion - (magit-visit-item) - (setq buf (current-buffer) - pos (point))) - (save-excursion - (with-current-buffer buf - (goto-char pos) - (add-change-log-entry whoami file-name other-window))))) - -;;;###autoload -(defun magit-add-change-log-entry-other-window (&optional whoami file-name) - "Find change log file in other window and add entry and item. -This differs from `add-change-log-entry-other-window' (which see) -in that it acts on the current hunk in a Magit buffer instead of -on a position in a file-visiting buffer." - (interactive (and current-prefix-arg - (list current-prefix-arg - (prompt-for-change-log-name)))) - (magit-add-change-log-entry whoami file-name t)) - -;;;; Read Repository - -(defun magit-read-top-dir (dir) - "Ask the user for a Git repository. -The choices offered by auto-completion will be the repositories -under `magit-repo-dirs'. If `magit-repo-dirs' is nil or DIR is -non-nil, then autocompletion will offer directory names." - (if (and (not dir) magit-repo-dirs) - (let* ((repos (magit-list-repos magit-repo-dirs)) - (reply (magit-completing-read "Git repository" repos))) - (file-name-as-directory - (or (cdr (assoc reply repos)) - (if (file-directory-p reply) - (expand-file-name reply) - (user-error "Not a repository or a directory: %s" reply))))) - (file-name-as-directory - (read-directory-name "Git repository: " - (or (magit-get-top-dir) default-directory))))) - -(defun magit-list-repos (dirs) - (magit-list-repos-remove-conflicts - (cl-loop for dir in dirs - append (cl-loop for repo in - (magit-list-repos* dir magit-repo-dirs-depth) - collect (cons (file-name-nondirectory repo) repo))))) - -(defun magit-list-repos* (dir depth) - "Return a list of repos found in DIR, recursing up to DEPTH levels deep." - (if (file-readable-p (expand-file-name ".git" dir)) - (list (expand-file-name dir)) - (and (> depth 0) - (file-accessible-directory-p dir) - (not (member (file-name-nondirectory dir) - '(".." "."))) - (cl-loop for entry in (directory-files dir t nil t) - append (magit-list-repos* entry (1- depth)))))) - -(defun magit-list-repos-remove-conflicts (alist) - (let ((dict (make-hash-table :test 'equal)) - (alist (delete-dups alist)) - (result nil)) - (dolist (a alist) - (puthash (car a) (cons (cdr a) (gethash (car a) dict)) - dict)) - (maphash - (lambda (key value) - (if (= (length value) 1) - (push (cons key (car value)) result) - (let ((sub (magit-list-repos-remove-conflicts - (mapcar - (lambda (entry) - (let ((dir (directory-file-name - (substring entry 0 (- (length key)))))) - (cons (concat (file-name-nondirectory dir) "/" key) - entry))) - value)))) - (setq result (append result sub))))) - dict) - result)) - -;;;; External Tools - -;;;###autoload -(defun magit-run-git-gui () - "Run `git gui' for the current git repository." - (interactive) - (let* ((default-directory (magit-get-top-dir))) - (call-process magit-git-executable nil 0 nil "gui"))) - -;;;###autoload -(defun magit-run-git-gui-blame (commit filename &optional linenum) - "Run `git gui blame' on the given FILENAME and COMMIT. -Interactively run it for the current file and the HEAD, with a -prefix or when the current file cannot be determined let the user -choose. When the current buffer is visiting FILENAME instruct -blame to center around the line point is on." - (interactive - (let (revision filename) - (when (or current-prefix-arg - (not (setq revision "HEAD" - filename (magit-buffer-file-name t)))) - (setq revision (magit-read-rev "Retrieve from revision" "HEAD") - filename (magit-read-file-from-rev revision))) - (list revision filename - (and (equal filename - (ignore-errors - (magit-file-relative-name (buffer-file-name)))) - (line-number-at-pos))))) - (let ((default-directory (magit-get-top-dir))) - (apply #'call-process magit-git-executable nil 0 nil "gui" "blame" - `(,@(and linenum (list (format "--line=%d" linenum))) - ,commit - ,filename)))) - -;;;###autoload -(defun magit-run-gitk (arg) - "Run Gitk for the current git repository. -Without a prefix argument run `gitk --all', with -a prefix argument run gitk without any arguments." - (interactive "P") - (apply #'call-process magit-gitk-executable nil 0 nil - (if arg nil (list "--all")))) - -;;;; Maintenance Tools - -(defun magit-describe-item () - "Show information about the section at point. -This command is intended for debugging purposes." - (interactive) - (let* ((section (magit-current-section)) - (head-beg (magit-section-beginning section)) - (body-beg (magit-section-content-beginning section))) - (message "Section: %s %s%s-%s %S %S" - (magit-section-type section) - (marker-position (magit-section-beginning section)) - (if (and body-beg (not (= body-beg head-beg)) - (< body-beg (magit-section-end section))) - (format "-%s" (marker-position body-beg)) - "") - (marker-position (magit-section-end section)) - (magit-section-info section) - (magit-section-context-type section)))) - -;;;; Magit Extensions - -(defun magit-load-config-extensions () - "Try to load magit extensions that are defined at git config layer. -This can be added to `magit-mode-hook' for example" - (dolist (ext (magit-get-all "magit.extension")) - (let ((sym (intern (format "magit-%s-mode" ext)))) - (when (and (fboundp sym) - (not (eq sym 'magit-wip-save-mode))) - (funcall sym 1))))) - -;;; magit.el ends soon - -(defconst magit-font-lock-keywords - (eval-when-compile - `((,(concat "(\\(" (regexp-opt - '("magit-define-level-shower" - "magit-define-section-jumper")) - "\\)\\>[ \t'\(]*\\(\\sw+\\)?") - (1 font-lock-keyword-face) - (2 font-lock-function-name-face nil t)) - (,(concat "(" (regexp-opt - '("magit-with-section" - "magit-cmd-insert-section" - "magit-git-insert-section" - "magit-insert-line-section" - "magit-section-action" - "magit-section-case" - "magit-add-action-clauses" - "magit-bind-match-strings" - "magit-visiting-file-item" - "magit-tests--with-temp-dir" - "magit-tests--with-temp-repo" - "magit-tests--with-temp-clone") t) - "\\>") - . 1))) - "Magit expressions to highlight in Emacs-Lisp mode. -To highlight Magit expressions add something like this to your -init file: - - (require 'magit) - (font-lock-add-keywords 'emacs-lisp-mode - magit-font-lock-keywords)") - -(defun magit-version (&optional noerror) - "The version of Magit that you're using.\n\n\(fn)" - (interactive) - (let ((toplib (or load-file-name buffer-file-name))) - (unless (and toplib - (equal (file-name-nondirectory toplib) "magit.el")) - (setq toplib (locate-library "magit.el"))) - (when toplib - (let* ((dir (file-name-directory toplib)) - (static (expand-file-name "magit-version.el" dir)) - (gitdir (expand-file-name ".git" dir))) - (cond ((file-exists-p gitdir) - (setq magit-version - (let ((default-directory dir)) - (magit-git-string "describe" "--tags" "--dirty"))) - (ignore-errors (delete-file static))) - ((file-exists-p static) - (load-file static)) - ((featurep 'package) - (setq magit-version - (or (ignore-errors ; < 24.3.50 - (package-version-join - (package-desc-vers - (cdr (assq 'magit package-alist))))) - (ignore-errors ; >= 24.3.50 - (package-version-join - (package-desc-version - (cadr (assq 'magit package-alist))))))))))) - (if (stringp magit-version) - (when (called-interactively-p 'any) - (message "magit-%s" magit-version)) - (if noerror - (progn (setq magit-version 'error) - (message "Cannot determine Magit's version")) - (user-error "Cannot determine Magit's version"))) - magit-version)) - -(defvar magit-last-seen-setup-instructions "0") - -(defun magit-maybe-show-setup-instructions () - (when (version< magit-last-seen-setup-instructions "1.4.0") - (require 'warnings) - (display-warning :warning "for magit-1.4.0 - -You have just updated to version 1.4.0 of Magit, and have to -make a choice. - -Before running Git, Magit by default reverts all unmodified -buffers which visit files tracked in the current repository. -This can potentially lead to dataloss so you might want to -disable this by adding the following line to your init file: - - (setq magit-auto-revert-mode nil) - -The risk is not as high as it might seem. If snapshots on Melpa -and Melpa-Stable had this enabled for a long time, so if you did -not experience any dataloss in the past, then you should probably -keep this enabled. - -Keeping this mode enabled is only problematic if you, for -example, use `git reset --hard REV' or `magit-reset-head-hard', -and expect Emacs to preserve the old state of some file in a -buffer. If you turn of this mode then file-visiting buffers and -Magit buffer will no longer by in sync, which can be confusing -and complicates many operations. Also note that it is possible -to undo a buffer revert using `C-x u' (`undo'). - -Then you also have to add the following line to your init file -to prevent this message from being shown again when you restart -Emacs: - - (setq magit-last-seen-setup-instructions \"1.4.0\") - -You might also want to read the release notes: -https://raw.githubusercontent.com/magit/magit/next/Documentation/RelNotes/1.4.0.txt")) - (when (featurep 'magit-log-edit) - (display-warning :error "magit-log-edit has to be removed - -Magit is no longer compatible with the library `magit-log-edit', -which was used in earlier releases. Please remove it, so that -Magit can use the successor `git-commit-mode' without the -obsolete library getting in the way. Then restart Emacs. - -You might also want to read: -https://github.com/magit/magit/wiki/Emacsclient"))) - -(add-hook 'after-init-hook #'magit-maybe-show-setup-instructions) - -(provide 'magit) - -(cl-eval-when (load eval) - (magit-version t) - (when after-init-time - (magit-maybe-show-setup-instructions))) - -(require 'magit-key-mode) - -;; Local Variables: -;; coding: utf-8 -;; indent-tabs-mode: nil -;; End: -;;; magit.el ends here diff --git a/elpa/magit-1.4.1/magit.info b/elpa/magit-1.4.1/magit.info deleted file mode 100644 index 46be711..0000000 --- a/elpa/magit-1.4.1/magit.info +++ /dev/null @@ -1,1596 +0,0 @@ -This is magit.info, produced by makeinfo version 4.13 from magit.texi. - -Magit is an interface to the version control system Git, implemented as -an Emacs package. - - Unlike Emacs's native Version Control package which strives to -provide a unified interface to various version control systems, Magit -only supports Git and can therefor better take advantage of its native -features. - - You are looking at the manual for the `1.4.0' release. - - Magit supports GNU Emacs 23.2 or later; 24.1 or later is recommended. -Magit supports Git 1.7.2.5 or later; 1.8.2 or later is recommended. - - When something breaks please see the curated list of known issues -(https://github.com/magit/magit/wiki/Known-Issues) and the FAQ -(https://github.com/magit/magit/wiki/FAQ). If that doesn't help check -the list of all open issues issues -(https://github.com/magit/magit/issues). - - If everything else fails please open a new issue or ask for help on -the mailing list -(https://groups.google.com/forum/?fromgroups#!forum/magit). - - Copyright (C) 2008-2015 The Magit Project Developers - - Permission is granted to copy, distribute and/or modify this - document under the terms of the GNU Free Documentation License, - Version 1.2 or any later version published by the Free Software - Foundation; with no Invariant Sections, with no Front-Cover Texts, - and with no Back-Cover Texts. A copy of the license is included - in the section entitled "GNU Free Documentation License". - -INFO-DIR-SECTION Emacs -START-INFO-DIR-ENTRY -* Magit (1.4.0): (magit). Using Git from Emacs with Magit. (1.4.0) -END-INFO-DIR-ENTRY - - -File: magit.info, Node: Top, Next: Introduction, Up: (dir) - -Magit User Manual (1.4.0) -************************* - -Magit is an interface to the version control system Git, implemented as -an Emacs package. - - Unlike Emacs's native Version Control package which strives to -provide a unified interface to various version control systems, Magit -only supports Git and can therefor better take advantage of its native -features. - - You are looking at the manual for the `1.4.0' release. - - Magit supports GNU Emacs 23.2 or later; 24.1 or later is recommended. -Magit supports Git 1.7.2.5 or later; 1.8.2 or later is recommended. - - When something breaks please see the curated list of known issues -(https://github.com/magit/magit/wiki/Known-Issues) and the FAQ -(https://github.com/magit/magit/wiki/FAQ). If that doesn't help check -the list of all open issues issues -(https://github.com/magit/magit/issues). - - If everything else fails please open a new issue or ask for help on -the mailing list -(https://groups.google.com/forum/?fromgroups#!forum/magit). - - Copyright (C) 2008-2015 The Magit Project Developers - - Permission is granted to copy, distribute and/or modify this - document under the terms of the GNU Free Documentation License, - Version 1.2 or any later version published by the Free Software - Foundation; with no Invariant Sections, with no Front-Cover Texts, - and with no Back-Cover Texts. A copy of the license is included - in the section entitled "GNU Free Documentation License". - -* Menu: - -* Introduction:: -* Acknowledgments:: -* Sections:: -* Status:: -* Untracked files:: -* Staging and Committing:: -* History:: -* Reflogs:: -* Commit Buffer:: -* Diffing:: -* Tagging:: -* Resetting:: -* Stashing:: -* Branches and Remotes:: -* Wazzup:: -* Merging:: -* Rebasing:: -* Interactive Rebasing:: -* Rewriting:: -* Pushing and Pulling:: -* Submodules:: -* Bisecting:: -* Finding commits not merged upstream:: -* Using Magit Extensions:: -* Using Git Directly:: -* GNU Free Documentation License:: - - -File: magit.info, Node: Introduction, Next: Acknowledgments, Prev: Top, Up: Top - -1 Introduction -************** - -With Magit, you can inspect and modify your Git repositories with -Emacs. You can review and commit the changes you have made to the -tracked files, for example, and you can browse the history of past -changes. There is support for cherry picking, reverting, merging, -rebasing, and other common Git operations. - - Magit is not a complete interface to Git; it just aims to make the -most common Git operations convenient. Thus, Magit will likely not -save you from learning Git itself. - - This manual provides a tour of many Magit features. It isn't an -introduction to version control in general, or to Git in particular. - - The main entry point to Magit is `M-x magit-status', which puts you -in Magit's status buffer. You will be using it frequently, so it is -probably a good idea to globally bind `magit-status' to a key of your -choice. - - In addition to the status buffer, Magit will also create buffers that -show lists of commits, buffers with diffs, and other kinds of buffers. -All these buffers are in a mode derived from `magit-mode' and have the -similar key bindings. Not all commands make sense in all contexts, but -a given key will do the same thing in different Magit buffers. - - Naturally, Magit runs the `git' command to do most of the work. The -`*magit-process*' buffer contains the transcript of the most recent -command. You can switch to it with `$'. - - -File: magit.info, Node: Acknowledgments, Next: Sections, Prev: Introduction, Up: Top - -2 Acknowledgments -***************** - -Our thank goes to all current and past contributors, Marius Vollmer who -started the project, and all retired and current maintainers, Phil -Jackson, Peter J. Weisberg, Rémi Vanicat, Nicolas Dudebout, Yann -Hodique, and Jonas Bernoulli. - - For a full list of contributors, see the AUTHORS.md file at the -top-level directory of this distribution or at AUTHORS.md -(https://github.com/magit/magit/tree/master/AUTHORS.md). - - -File: magit.info, Node: Sections, Next: Status, Prev: Acknowledgments, Up: Top - -3 Sections -********** - -All Magit buffers are structured into nested 'sections'. These -sections can be hidden and shown individually. When a section is -hidden, only its first line is shown and all its children are -completely invisible. - - The most fine-grained way to control the visibility of sections is -the `TAB' key. It will to toggle the current section (the section that -contains point) between being hidden and being shown. - - Typing `S-TAB' toggles the visibility of the children of the current -section. When all of them are shown, they will all be hidden. -Otherwise, when some or all are hidden, they will all be shown. - - The digit keys `1', `2', `3', and `4' control the visibility of -sections based on levels. Hitting `2', for example, will show sections -on levels one and two, and will hide sections on level 3. However, -only sections that are a parent or child of the current section are -affected. - - For example, when the current section is on level 3 and you hit `1', -the grand-parent of the current section (which is on level one) will be -shown, and the parent of the current section (level 2) will be hidden. -The visibility of no other section will be changed. - - This sounds a bit complicated, but you'll figure it out. - - Using `M-1', `M-2', `M-3', and `M-4' is similar to the unmodified -digits, but now all sections on the respective level are affected, -regardless of whether or not they are related to the current section. - - For example, `M-1' will only show the first lines of the top-level -sections and will hide everything else. Typing `M-4' on the other hand -will show everything. - - Because of the way the status buffer is set up, some changes to -section visibility are more common than others. Files are on level 2 -and diff hunks are on level 4. Thus, you can type `2' to collapse the -diff of the current file, and `M-2' to collapse all files. This -returns the status buffer to its default setup and is a quick way to -unclutter it after drilling down into the modified files. - - Because `2' and `M-2' are so common in the status buffer, they are -bound to additional, more mnemonic keys: `M-h' (hide) and `M-H' (hide -all). Likewise `4' and `M-4' are also available as `M-s' (show) and -`M-S' (show all). - - In other buffers than the status buffer, `M-h', `M-H', `M-s', and -`M-S' might work on different levels than on 2 and 4, but they keep -their general meaning: `M-H' hides all detail, and `M-S' shows -everything. - - -File: magit.info, Node: Status, Next: Untracked files, Prev: Sections, Up: Top - -4 Status -******** - -Running `M-x magit-status' displays the main interface of Magit, the -status buffer. You can have multiple status buffers active at the same -time, each associated with its own Git repository. - - When invoking `M-x magit-status' from within a Git repository, it -will switch to the status buffer of that repository. Otherwise, it -will prompt for a directory. With a prefix argument, it will always -prompt. - - You can set `magit-repo-dirs' to customize how `magit-status' asks -for the repository to work on. When `magit-repo-dirs' is nil, -`magit-status' will simply ask for a directory. - - If you specify a directory that is not a Git repository, `M-x -magit-status' will offer to initialize it as one. - - When `magit-repo-dirs' is not nil, it is treated as a list of -directory names, and `magit-status' will find all Git repositories in -those directories and offer them for completion. (Magit will only look -`magit-repo-dirs-depth' levels deep, however.) - - With two prefix arguments, `magit-status' will always prompt for a -raw directory. - - Thus, you would normally set `magit-repo-dirs' to the places where -you keep most of your Git repositories and switch between them with -`C-u M-x magit-status'. If you want to go to a repository outside of -your normal working areas, or if you want to create a new repository, -you would use `C-u C-u M-x magit-status'. - - You need to explicitly refresh the status buffer when you have made -changes to the repository from outside of Emacs. You can type `g' in -the status buffer itself, or just use `M-x magit-status' instead of -`C-x b' when switching to it. You also need to refresh the status -buffer in this way after saving a file in Emacs. - - The header at the top of the status buffer shows a short summary of -the repository state: where it is located, which branch is checked out, -etc. Below the header are a number of sections that show details about -the working tree and the staging area. You can hide and show them as -described in the previous section. - - The first section shows _Untracked files_, if there are any. See -*note Untracked files:: for more details. - - The next two sections show your local changes. They are explained -fully in the next chapter, *note Staging and Committing::. - - If the current branch is associated with a remote tracking branch, -the status buffer shows the differences between the current branch and -the tracking branch. See *note Pushing and Pulling:: for more -information. - - During a history rewriting session, the status buffer shows the -_Pending changes_ and _Pending commits_ sections. See *note -Rewriting:: for more details. - - -File: magit.info, Node: Untracked files, Next: Staging and Committing, Prev: Status, Up: Top - -5 Untracked files -***************** - -Untracked files are shown in the _Untracked files_ section. - - You can add an untracked file to the staging area with `s'. If -point is on the _Untracked files_ section title when you hit `s', all -untracked files are staged. - - Typing `C-u S' anywhere will also stage all untracked files, -together with all changes to the tracked files. - - You can instruct Git to ignore them by typing `i'. This will add -the filename to the `.gitignore' file. Typing `C-u i' will ask you for -the name of the file to ignore. This is useful to ignore whole -directories, for example. In this case, the minibuffer's future history -(accessible with `M-n') contains predefined values (such as wildcards) -that might be of interest. If prefix argument is negative (for example -after typing `C-- i'), the prompt proposes wildcard by default. The -`I' command is similar to `i' but will add the file to -`.git/info/exclude' instead. - - To delete an untracked file forever, use `k'. If point is on the -_Untracked files_ section title when you hit `k', all untracked files -are deleted. - - -File: magit.info, Node: Staging and Committing, Next: History, Prev: Untracked files, Up: Top - -6 Staging and Committing -************************ - -Committing with Git is a two step process: first you add the changes -you want to commit to a 'staging area' or 'index', and then you commit -them to the repository. This allows you to only commit a subset of the -changes in the working tree. If you are not familiar with this concept -yet, then you should change that as soon as possible using one of the -fine Git tutorials. If you don't, then Git and by extension Magit will -seem rather strange. - - Magit shows uncommitted changes in two sections, depending on whether -the changes have been staged yet. The _Staged changes_ section shows -the changes that will be included in the next commit, while the -_Unstaged changes_ section shows the changes that will be left out. - - To move an unstaged hunk into the staging area, move point into the -hunk and type `s'. Likewise, to unstage a hunk, move point into it and -type `u'. If point is in a diff header when you type `s' or `u', all -hunks belonging to that diff are moved at the same time. - - Currently it is only possible to stage from the status buffer. -Staging and unstaging from diff buffers that show unstaged and staged -changes is not possible yet. - - If the region is active when you type `s' or `u', only the changes -in the region are staged or unstaged. (This works line by line: if the -beginning of a line is in the region it is included in the changes, -otherwise it is not.) - - To change the size of the hunks, you can type `+' or `-' to increase -and decrease, respectively. Typing `0' will reset the hunk size to the -default. - - Typing `C-u s' will ask you for a name of a file to be staged, for -example to stage files that are hidden. - - To move all hunks of all diffs into the staging area in one go, type -`S'. To unstage everything, type `U'. - - Typing `C-u S' will stage all untracked files in addition to the -changes to tracked files. - - You can discard uncommitted changes by moving point into a hunk and -typing `k'. The changes to discard are selected as with `s' and `u'. - - Before committing, you should write a short description of the -changes. - - Type `c c' to pop up a buffer where you can write your change -description. Once you are happy with the description, type `C-c C-c' -in that buffer to perform the commit. - - If you want to write changes in a `ChangeLog' file, you can use `C-x -4 a' on a diff hunk. - - Typing `c c' when the staging area is unused is a special situation. -Normally, the next commit would be empty, but you can configure Magit -to do something more useful by customizing the -`magit-commit-all-when-nothing-staged' variable. One choice is to -instruct the subsequent `C-c C-c' to commit all changes. Another -choice is stage everything at the time of hitting `c c'. - - Typing `M-n' or `M-p' will cycle through the -`log-edit-comment-ring', which will have your previous log messages. -This is particularly useful if you have a hook that occasionally causes -git to refuse your commit. - - To abort a commit use `C-c C-k'. The commit message is saved and -can later be retrieved in the commit message buffer using `M-n' and -`M-p'. - - Typing `C' will also pop up the change description buffer, but in -addition, it will try to insert a ChangeLog-style entry for the change -that point is in. - - -File: magit.info, Node: History, Next: Reflogs, Prev: Staging and Committing, Up: Top - -7 History -********* - -To show the repository history of your current head, type `l l'. A new -buffer will be shown that displays the history in a terse form. The -first paragraph of each commit message is displayed, next to a -representation of the relationships between commits. - - To show the repository history between two branches or between any -two points of the history, type `l r l'. You will be prompted to enter -references for starting point and ending point of the history range; you -can use auto-completion to specify them. A typical use case for ranged -history log display would be `l r l master RET new-feature RET' that -will display commits on the new-feature branch that are not in master; -these commits can then be inspected and cherry-picked, for example. - - More thorough filtering can be done by supplying `l' with one or -more suffix arguments, as displayed in its popup. `=g' ('Grep') for -example, limits the output to commits of which the log message matches -a specific string/regex. - - Typing `l L' (or `l C-u L') will show the log in a more verbose form. - - Magit will show only `magit-log-cutoff-length' entries. `e' will -show twice as many entries. `C-u e' will show all entries, and given a -numeric prefix argument, `e' will add this number of entries. - - You can move point to a commit and then cause various things to -happen with it. (The following commands work in any list of commits, -such as the one shown in the _Unpushed commits_ section.) - - Typing `RET' will pop up more information about the current commit -and move point into the new buffer. *Note Commit Buffer::. Typing -`SPC' and `DEL' will also show the information, but will scroll the new -buffer up or down (respectively) when typed again. - - Typing `a' will apply the current commit to your current branch. -This is useful when you are browsing the history of some other branch -and you want to `cherry-pick' some changes from it. A typical -situation is applying selected bug fixes from the development version -of a program to a release branch. The cherry-picked changes will not -be committed automatically; you need to do that explicitly. - - Typing `A' will cherry-pick the current commit and will also commit -the changes automatically when there have not been any conflicts. - - Typing `v' will revert the current commit. Thus, it will apply the -changes made by that commit in reverse. This is obviously useful to -cleanly undo changes that turned out to be wrong. As with `a', you -need to commit the changes explicitly. - - Typing `C-w' will copy the sha1 of the current commit into the kill -ring. - - Typing `=' will show the differences from the current commit to the -"marked" commit. - - You can mark the current commit by typing `.'. When the current -commit is already marked, typing `.' will unmark it. To unmark the -marked commit no matter where point is, use `C-u .'. - - Some commands, such as `=', will use the current commit and the -marked commit as implicit arguments. Other commands will offer the -marked commit as a default when prompting for their arguments. - - -File: magit.info, Node: Reflogs, Next: Commit Buffer, Prev: History, Up: Top - -8 Reflogs -********* - -You can use `l h' and `l H' to browse your _reflog_, the local history -of changes made to your repository heads. Typing `H' will ask for a -head, while `l h' will show the reflog of `HEAD'. - - The resulting buffer is just like the buffer produced by `l l' and -`l L' that shows the commit history. - - -File: magit.info, Node: Commit Buffer, Next: Diffing, Prev: Reflogs, Up: Top - -9 Commit Buffer -*************** - -When you view a commit (perhaps by selecting it in the log buffer, -*note History::), the "commit buffer" is displayed, showing you -information about the commit and letting you interact with it. - - By placing your cursor within the diff or hunk and typing `a', you -can apply the same patch to your working copy. This is useful when you -want to copy a change from another branch, but don't necessarily want -to cherry-pick the whole commit. - - By typing `v' you can apply the patch in reverse, removing all the -lines that were added and adding all the lines that were removed. This -is a convenient way to remove a change after determining that it -introduced a bug. - - If the commit message refers to any other commits in the repository -by their unique hash, the hash will be highlighted and you will be able -to visit the referenced commit either by clicking on it or by moving -your cursor onto it and pressing `RET'. - - The commit buffer maintains a history of the commits it has shown. -After visiting a referenced commit you can type `C-c C-b' to get back -to where you came from. To go forward in the history, type `C-c C-f'. -There are also `[back]' and `[forward]' buttons at the bottom of the -buffer. - - -File: magit.info, Node: Diffing, Next: Tagging, Prev: Commit Buffer, Up: Top - -10 Diffing -********** - -Magit typically shows diffs in the "unified" format. - - In any buffer that shows a diff, you can type `e' anywhere within -the diff to show the two versions of the file in Ediff. If the diff is -of a file in the status buffer that needs to be merged, you will be -able to use Ediff as an interactive merge tool. Otherwise, Ediff will -simply show the two versions of the file. - - To show the changes from your working tree to another revision, type -`d'. To show the changes between two arbitrary revisions, type `D'. - - You can use `a' within the diff output to apply the changes to your -working tree. As usual when point is in a diff header for a file, all -changes for that file are applied, and when it is in a hunk, only that -hunk is. When the region is active, the applied changes are restricted -to that region. - - Typing `v' will apply the selected changes in reverse. - - -File: magit.info, Node: Tagging, Next: Resetting, Prev: Diffing, Up: Top - -11 Tagging -********** - -Typing `t t' will make a lightweight tag. Typing `t a' will make an -annotated tag. It will put you in the normal `*magit-log-edit' buffer -for writing commit messages, but typing `C-c C-c' in it will make the -tag instead. This is controlled by the `Tag' field that will be added -to the `*magit-log-edit*' buffer. You can edit it, if you like. - - -File: magit.info, Node: Resetting, Next: Stashing, Prev: Tagging, Up: Top - -12 Resetting -************ - -Once you have added a commit to your local repository, you can not -change that commit anymore in any way. But you can reset your current -head to an earlier commit and start over. - - If you have published your history already, rewriting it in this way -can be confusing and should be avoided. However, rewriting your local -history is fine and it is often cleaner to fix mistakes this way than -by reverting commits (with `v', for example). - - Typing `x' will ask for a revision and reset your current head to -it. No changes will be made to your working tree and staging area. -Thus, the _Staged changes_ section in the status buffer will show the -changes that you have removed from your commit history. You can commit -the changes again as if you had just made them, thus rewriting history. - - Typing `x' while point is in a line that describes a commit will -offer this commit as the default revision to reset to. Thus, you can -move point to one of the commits in the _Unpushed commits_ section and -hit `x RET' to reset your current head to it. - - Type `X' to reset your working tree and staging area to the most -recently committed state. This will discard your local modifications, -so be careful. - - You can give a prefix to `x' if you want to reset both the current -head and your working tree to a given commit. This is the same as -first using an unprefixed `x' to reset only the head, and then using -`X'. - - -File: magit.info, Node: Stashing, Next: Branches and Remotes, Prev: Resetting, Up: Top - -13 Stashing -*********** - -You can create a new stash with `z z'. Your stashes will be listed in -the status buffer, and you can apply them with `a' and pop them with -`A'. To drop a stash, use `k'. - - With a prefix argument, both `a' and `A' will attempt to reinstate -the index as well as the working tree from the stash. - - Typing `z -k z' will create a stash just like `z z', but will leave -the changes in your working tree and index. This makes it easier to, -for example, test multiple variations of the same change. - - If you just want to make quick snapshots in between edits, you can -use `z s', which automatically enters a timestamp as description, and -keeps your working tree and index intact by default. - - You can visit and show stashes in the usual way: Typing `SPC' and -`DEL' will pop up a buffer with the description of the stash and scroll -it, typing `RET' will move point into that buffer. Using `C-u RET' -will move point into that buffer in other window. - - -File: magit.info, Node: Branches and Remotes, Next: Wazzup, Prev: Stashing, Up: Top - -14 Branches and Remotes -*********************** - -The current branch is indicated in the header of the status buffer. If -this branch is tracking a remote branch, the latter is also indicated. - - Branches and remotes can be manipulated directly with a popup menu or -through the branch manager. Using the popup menu allows you to quickly -make changes from any magit buffer. The branch manager is a separate -buffer called `*magit-branches*'. It displays information about -branches and remotes and offers a local key map for shorter key -bindings. The two interaction methods are described in more details -below. - -* Menu: - -* Branches Popup:: -* Remotes Popup:: -* Branches in the Branch Manager:: -* Remotes in the Branch Manager:: - - -File: magit.info, Node: Branches Popup, Next: Remotes Popup, Up: Branches and Remotes - -14.1 Branches Popup -=================== - -Typing `b' will display a popup menu to manipulate branches. - - You can switch to a different branch by typing `b b'. This will -immediately checkout the branch into your working copy, so you -shouldn't have any local modifications when switching branches. - - If you try to switch to a remote branch, Magit will offer to create a -local tracking branch for it instead. This way, you can easily start -working on new branches that have appeared in a remote repository. - - Typing `b b' while point is at a commit description will offer that -commit as the default to switch to. This will result in a detached -head. - - To create a new branch and switch to it immediately, type `b c'. - - To delete a branch, type `b k'. If you're currently on that branch, -Magit will offer to switch to the 'master' branch. - - Typing `b r' will let you rename a branch. Unless a branch with -the same name already exists, obviously... - - Deleting a branch is only possible if it's already fully merged into -HEAD or its upstream branch. Unless you type `b C-u k', that is. Here -be dragons... - - Typing `b v' will launch the branch manager. - - -File: magit.info, Node: Remotes Popup, Next: Branches in the Branch Manager, Prev: Branches Popup, Up: Branches and Remotes - -14.2 Remotes Popup -================== - -Typing `M' will display a popup menu to manipulate remotes. - - To add a new remote, type `M a'. - - To delete a remote type `M k'. - - Typing `M r' will let you rename a remote. - - -File: magit.info, Node: Branches in the Branch Manager, Next: Remotes in the Branch Manager, Prev: Remotes Popup, Up: Branches and Remotes - -14.3 Branches in the Branch Manager -=================================== - -In the branch manager, each branch is displayed on a separate line. The -current local branch is marked by a "*" in front of the name. Remote -branches are grouped by the remote they come from. - - If a local branch tracks a remote branch some extra information is -printed on the branch line. The format is the following: " -[ : ahead , behind ]". "" -is omitted if it is identical to "". "ahead" and "behind" -information are only displayed if necessary. - - To check out a branch, move your cursor to the desired branch and -press `RET'. - - Typing `c' will create a new branch. - - Typing `k' will delete the branch in the current line, and `C-u k' -deletes it even if it hasn't been merged into the current local branch. -Deleting works for both local and remote branches. - - Typing `r' on a branch will rename it. - - Typing `T' on a local branch, changes which remote branch it tracks. - - -File: magit.info, Node: Remotes in the Branch Manager, Prev: Branches in the Branch Manager, Up: Branches and Remotes - -14.4 Remotes in the Branch Manager -================================== - -In the branch manager, each remote is displayed on a separate line. The -format is the following " (, )". "" -is omitted if it is not set. The associated branches are listed under -this line. - - Typing `a' will add a new remote. - - Typing `k' will delete the remote in the current line. - - Typing `r' on a remote will rename it. - - -File: magit.info, Node: Wazzup, Next: Merging, Prev: Branches and Remotes, Up: Top - -15 Wazzup -********* - -Typing `w' will show a summary of how your other branches relate to the -current branch. - - For each branch, you will get a section that lists the commits in -that branch that are not in the current branch. The sections are -initially collapsed; you need to explicitly open them with `TAB' (or -similar) to show the lists of commits. - - When point is on a _N unmerged commits in ..._ title, the -corresponding branch will be offered as the default for a merge. - - Hitting `i' on a branch title will ignore this branch in the wazzup -view. You can use `C-u w' to show all branches, including the ignored -ones. Hitting `i' on an already ignored branch in that view will -unignore it. - - -File: magit.info, Node: Merging, Next: Rebasing, Prev: Wazzup, Up: Top - -16 Merging -********** - -Magit offers two ways to merge branches: manual and automatic. A -manual merge will apply all changes to your working tree and staging -area, but will not commit them, while an automatic merge will go ahead -and commit them immediately. - - Type `m m' to initiate merge. - - After initiating a merge, the header of the status buffer might -remind you that the next commit will be a merge commit (with more than -one parent). If you want to abort a manual merge, just do a hard reset -to HEAD with `X'. - - Merges can fail if the two branches you want to merge introduce -conflicting changes. In that case, the automatic merge stops before the -commit, essentially falling back to a manual merge. You need to resolve -the conflicts for example with `e' and stage the resolved files, for -example with `S'. - - You can not stage individual hunks one by one as you resolve them, -you can only stage whole files once all conflicts in them have been -resolved. - - -File: magit.info, Node: Rebasing, Next: Interactive Rebasing, Prev: Merging, Up: Top - -17 Rebasing -*********** - -Typing `R' in the status buffer will initiate a rebase or, if one is -already in progress, ask you how to continue. - - When a rebase is stopped in the middle because of a conflict, the -header of the status buffer will indicate how far along you are in the -series of commits that are being replayed. When that happens, you -should resolve the conflicts and stage everything and hit `R c' to -continue the rebase. Alternatively, hitting `c' or `C' while in the -middle of a rebase will also ask you whether to continue the rebase. - - Of course, you can initiate a rebase in any number of ways, by -configuring `git pull' to rebase instead of merge, for example. Such a -rebase can be finished with `R' as well. - - -File: magit.info, Node: Interactive Rebasing, Next: Rewriting, Prev: Rebasing, Up: Top - -18 Interactive Rebasing -*********************** - -Typing `E' in the status buffer will initiate an interactive rebase. -This is equivalent to running `git rebase --interactive' at the command -line. The `git-rebase-todo' file will be opened in an Emacs buffer for -you to edit. This file is opened using `emacsclient', so just edit -this file as you normally would, then call the `server-edit' function -(typically bound to `C-x #') to tell Emacs you are finished editing, -and the rebase will proceed as usual. - - If you have loaded `rebase-mode.el' (which is included in the Magit -distribution), the `git-rebase-todo' buffer will be in `rebase-mode'. -This mode disables normal text editing but instead provides single-key -commands (shown in the buffer) to perform all the edits that you would -normally do manually, including changing the operation to be performed -each commit ("pick", "squash", etc.), deleting (commenting out) commits -from the list, and reordering commits. You can finish editing the -buffer and proceed with the rebase by pressing `C-c C-c', which is -bound to `server-edit' in this mode, and you can abort the rebase with -`C-c C-k', just like when editing a commit message in Magit. - - -File: magit.info, Node: Rewriting, Next: Pushing and Pulling, Prev: Interactive Rebasing, Up: Top - -19 Rewriting -************ - -As hinted at earlier, you can rewrite your commit history. For -example, you can reset the current head to an earlier commit with `x'. -This leaves the working tree unchanged, and the status buffer will show -all the changes that have been made since that new value of the current -head. You can commit these changes again, possibly splitting them into -multiple commits as you go along. - - Amending your last commit is a common special case of rewriting -history like this. - - Another common way to rewrite history is to reset the head to an -earlier commit, and then to cherry pick the previous commits in a -different order. You could pick them from the reflog, for example. - - Magit has several commands that can simplify the book keeping -associated with rewriting. These commands all start with the `r' -prefix key. - - (Unless you already do so, we recommend that you don't use the -functionality described here. It is semi-deprecated and will be -removed once its unique features have been ported to the `git rebase ---interactive' workflow. Even now the latter is almost always the -better option.) - - Typing `r b' will start a rewrite operation. You will be prompted -for a _base_ commit. This commit and all subsequent commits up until -the current head are then put in a list of _Pending commits_, after -which the current head will be reset to the _parent_ of the base -commit. This can be configured to behave like `git rebase', i.e. -exclude the selected base commit from the rewrite operation, with the -`magit-rewrite-inclusive' variable. - - You would then typically use `a' and `A' to cherry pick commits from -the list of pending commits in the desired order, until all have been -applied. Magit shows which commits have been applied by changing their -marker from `*' to `.'. - - Using `A' will immediately commit the commit (as usual). If you -want to combine multiple previous commits into a single new one, use -`a' to apply them all to your working tree, and then commit them -together. - - Magit has no explicit support for rewriting merge commits. It will -happily include merge commits in the list of pending commits, but there -is no way of replaying them automatically. You have to redo the merge -explicitly. - - You can also use `v' to revert a commit when you have changed your -mind. This will change the `.' mark back to `*'. - - Once you are done with the rewrite, type `r s' to remove the book -keeping information from the status buffer. - - If you rather wish to start over, type `r a'. This will abort the -rewriting, resetting the current head back to the value it had before -the rewrite was started with `r b'. - - Typing `r f' will _finish_ the rewrite: it will apply all unused -commits one after the other, as if you would use `A' with all of them. - - You can change the `*' and `.' marks of a pending commit explicitly -with `r *' and `r .'. - - In addition to a list of pending commits, the status buffer will show -the _Pending changes_. This section shows the diff between the -original head and the current head. You can use it to review the -changes that you still need to rewrite, and you can apply hunks from -it, like from any other diff. - - -File: magit.info, Node: Pushing and Pulling, Next: Submodules, Prev: Rewriting, Up: Top - -20 Pushing and Pulling -********************** - -Magit will run `git push' when you type `P P'. If you give a prefix -argument to `P P', you will be prompted for the repository to push to. -When no default remote repository has been configured yet for the -current branch, you will be prompted as well. Typing `P P' will only -push the current branch to the remote. In other words, it will run -`git push '. The branch will be created in the remote -if it doesn't exist already. The local branch will be configured so -that it pulls from the new remote branch. If you give a double prefix -argument to `P P', you will be prompted in addition for the target -branch to push to. In other words, it will run `git push -:'. - - Typing `f f' will run `git fetch'. It will prompt for the name of -the remote to update if there is no default one. Typing `f o' will -always prompt for the remote. Typing `F F' will run `git pull'. When -you don't have a default branch configured to be pulled into the -current one, you will be asked for it. - - If there is a default remote repository for the current branch, Magit -will show that repository in the status buffer header. - - In this case, the status buffer will also have a _Unpushed commits_ -section that shows the commits on your current head that are not in the -branch named `/'. This section works just like the -history buffer: you can see details about a commit with `RET', compare -two of them with `.' and `=', and you can reset your current head to -one of them with `x', for example. If you want to push the changes -then type `P P'. - - When the remote branch has changes that are not in the current -branch, Magit shows them in a section called _Unpulled changes_. Typing -`F F' will fetch and merge them into the current branch. - - -File: magit.info, Node: Submodules, Next: Bisecting, Prev: Pushing and Pulling, Up: Top - -21 Submodules -************* - -`o u' - Update the submodules, with a prefix argument it will also - initialize them. - -`o i' - Initialize the submodules. - -`o b' - Update and initialize the submodules in one go (same as C-u o u). - -`o s' - Synchronizes submodules' remote URL configuration setting to the - value specified in .gitmodules. - - -File: magit.info, Node: Bisecting, Next: Finding commits not merged upstream, Prev: Submodules, Up: Top - -22 Bisecting -************ - -Magit supports bisecting by showing how many revisions and steps are -left to be tested in the status buffer. You can control the bisect -session from both the status and from log buffers with the `B' key menu. - - Typing `B s' will start a bisect session. You will be prompted for -a revision that is known to be bad (defaults to _HEAD_) and for a -revision that is known to be good (defaults to the revision at point if -there is one). git will select a revision for you to test, and Magit -will update its status buffer accordingly. - - You can tell git that the current revision is good with `B g', that -it is bad with `B b' or that git should skip it with `B k'. You can -also tell git to go into full automatic mode by giving it the name of a -script to run for each revision to test with `B u'. - - The current status can be shown as a log with `B l'. It contains -the revisions that have already been tested and your decisions about -their state. - - The revisions left to test can be visualized in gitk with `B v'. - - When you're finished bisecting you have to reset the session with `B -r'. - - -File: magit.info, Node: Finding commits not merged upstream, Next: Using Magit Extensions, Prev: Bisecting, Up: Top - -23 Finding commits not merged upstream -************************************** - -One of the comforts of git is that it can tell you which commits have -been merged upstream but not locally and vice versa. Git's sub-command -for this is `cherry' (not to be confused with `cherry-pick'). Magit -has support for this by invoking `magit-cherry' which is bound to `y' -by default. - - Magit will then ask you first for the upstream revision (which -defaults to the currently tracked remote branch if any) and the head -revision (which defaults to the current branch) to use in the -comparison. You will then see a new buffer in which all commits are -listed with a directional marker, their revision and the commit -message's first line. The directional marker is either `+' indicating -a commit that's present in upstream but not in head or `-' which -indicates a commit present in head but not in upstream. - - From this list you can use the usual key bindings for cherry-picking -individual commits (`a' for cherry-picking without committing and `A' -for the same plus the automatic commit). The buffer is refreshed -automatically after each cherry-pick. - - -File: magit.info, Node: Using Magit Extensions, Next: Using Git Directly, Prev: Finding commits not merged upstream, Up: Top - -24 Magit Extensions -******************* - -* Menu: - -* Activating extensions:: -* Interfacing with Subversion:: -* Interfacing with Topgit:: -* Interfacing with StGit:: - - -File: magit.info, Node: Activating extensions, Next: Interfacing with Subversion, Up: Using Magit Extensions - -24.1 Activating extensions -========================== - -Magit comes with a couple of shipped extensions that allow interaction -with `git-svn', `topgit' and `stgit'. See following sections for -specific details on how to use them. - - Extensions can be activated globally or on a per-repository basis. -Since those extensions are implemented as minor modes, one can use for -example `M-x magit-topgit-mode' to toggle the `topgit' extension, -making the corresponding section and commands (un)available. - - In order to do that automatically (and for every repository), one can -use for example: - - (add-hook 'magit-mode-hook 'turn-on-magit-topgit) - - Magit also allows configuring different extensions, based on the git -repository configuration. - - (add-hook 'magit-mode-hook 'magit-load-config-extensions) - - This will read git configuration variables and activate the relevant -extensions. - - For example, after running the following commands, the `topgit' -extension will be loaded for every repository, while the `svn' one will -be loaded only for the current one. - - $ git config --global --add magit.extension topgit - $ git config --add magit.extension svn - - Note the `--add' flag, which means that each extension gets its own -line in the `config' file. - - -File: magit.info, Node: Interfacing with Subversion, Next: Interfacing with Topgit, Prev: Activating extensions, Up: Using Magit Extensions - -24.2 Interfacing with Subversion -================================ - -Typing `N r' runs `git svn rebase', typing `N c' runs `git svn dcommit' -and typing `N f' runs `git svn fetch'. - - `N s' will prompt you for a (numeric, Subversion) revision and then -search for a corresponding Git sha1 for the commit. This is limited to -the path of the remote Subversion repository. With a prefix (`C-u N s' -the user will also be prompted for a branch to search in. - - -File: magit.info, Node: Interfacing with Topgit, Next: Interfacing with StGit, Prev: Interfacing with Subversion, Up: Using Magit Extensions - -24.3 Interfacing with Topgit -============================ - -Topgit (http://repo.or.cz/r/topgit.git) is a patch queue manager that -aims at being close as possible to raw Git, which makes it easy to use -with Magit. In particular, it does not require to use a different set -of commands for "commit", "update", and other operations. - - `magit-topgit.el' provides basic integration with Magit, mostly by -providing a "Topics" section. - - Topgit branches can be created the regular way, by using a "t/" -prefix by convention. So, creating a "t/foo" branch will actually -populate the "Topics" section with one more branch after committing -`.topdeps' and `.topmsg'. - - Also, the way we pull (see *note Pushing and Pulling::) such a -branch is slightly different, since it requires updating the various -dependencies of that branch. This should be mostly transparent, except -in case of conflicts. - - -File: magit.info, Node: Interfacing with StGit, Prev: Interfacing with Topgit, Up: Using Magit Extensions - -24.4 Interfacing with StGit -=========================== - -StGit (http://www.procode.org/stgit) is a Python application providing -similar functionality to Quilt (i.e. pushing/popping patches to/from a -stack) on top of Git. These operations are performed using Git -commands and the patches are stored as Git commit objects, allowing -easy merging of the StGit patches into other repositories using -standard Git functionality. - - `magit-stgit.el' provides basic integration with Magit, mostly by -providing a "Series" section, whose patches can be seen as regular -commits through the "visit" action. - - You can change the current patch in a series with the "apply" action, -as well as you can delete them using the "discard" action. - - Additionally, the `magit-stgit-refresh' and `magit-stgit-rebase' -commands let you perform the respective StGit operations. - - -File: magit.info, Node: Using Git Directly, Next: GNU Free Documentation License, Prev: Using Magit Extensions, Up: Top - -25 Using Git Directly -********************* - -For situations when Magit doesn't do everything you need, you can run -raw Git commands using `:'. This will prompt for a Git command, run -it, and refresh the status buffer. The output can be viewed by typing -`$'. - - -File: magit.info, Node: GNU Free Documentation License, Prev: Using Git Directly, Up: Top - -Appendix A GNU Free Documentation License -***************************************** - - Version 1.2, November 2002 - - Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - 0. PREAMBLE - - The purpose of this License is to make a manual, textbook, or other - functional and useful document "free" in the sense of freedom: to - assure everyone the effective freedom to copy and redistribute it, - with or without modifying it, either commercially or - noncommercially. Secondarily, this License preserves for the - author and publisher a way to get credit for their work, while not - being considered responsible for modifications made by others. - - This License is a kind of "copyleft", which means that derivative - works of the document must themselves be free in the same sense. - It complements the GNU General Public License, which is a copyleft - license designed for free software. - - We have designed this License in order to use it for manuals for - free software, because free software needs free documentation: a - free program should come with manuals providing the same freedoms - that the software does. But this License is not limited to - software manuals; it can be used for any textual work, regardless - of subject matter or whether it is published as a printed book. - We recommend this License principally for works whose purpose is - instruction or reference. - - 1. APPLICABILITY AND DEFINITIONS - - This License applies to any manual or other work, in any medium, - that contains a notice placed by the copyright holder saying it - can be distributed under the terms of this License. Such a notice - grants a world-wide, royalty-free license, unlimited in duration, - to use that work under the conditions stated herein. The - "Document", below, refers to any such manual or work. Any member - of the public is a licensee, and is addressed as "you". You - accept the license if you copy, modify or distribute the work in a - way requiring permission under copyright law. - - A "Modified Version" of the Document means any work containing the - Document or a portion of it, either copied verbatim, or with - modifications and/or translated into another language. - - A "Secondary Section" is a named appendix or a front-matter section - of the Document that deals exclusively with the relationship of the - publishers or authors of the Document to the Document's overall - subject (or to related matters) and contains nothing that could - fall directly within that overall subject. (Thus, if the Document - is in part a textbook of mathematics, a Secondary Section may not - explain any mathematics.) The relationship could be a matter of - historical connection with the subject or with related matters, or - of legal, commercial, philosophical, ethical or political position - regarding them. - - The "Invariant Sections" are certain Secondary Sections whose - titles are designated, as being those of Invariant Sections, in - the notice that says that the Document is released under this - License. If a section does not fit the above definition of - Secondary then it is not allowed to be designated as Invariant. - The Document may contain zero Invariant Sections. If the Document - does not identify any Invariant Sections then there are none. - - The "Cover Texts" are certain short passages of text that are - listed, as Front-Cover Texts or Back-Cover Texts, in the notice - that says that the Document is released under this License. A - Front-Cover Text may be at most 5 words, and a Back-Cover Text may - be at most 25 words. - - A "Transparent" copy of the Document means a machine-readable copy, - represented in a format whose specification is available to the - general public, that is suitable for revising the document - straightforwardly with generic text editors or (for images - composed of pixels) generic paint programs or (for drawings) some - widely available drawing editor, and that is suitable for input to - text formatters or for automatic translation to a variety of - formats suitable for input to text formatters. A copy made in an - otherwise Transparent file format whose markup, or absence of - markup, has been arranged to thwart or discourage subsequent - modification by readers is not Transparent. An image format is - not Transparent if used for any substantial amount of text. A - copy that is not "Transparent" is called "Opaque". - - Examples of suitable formats for Transparent copies include plain - ASCII without markup, Texinfo input format, LaTeX input format, - SGML or XML using a publicly available DTD, and - standard-conforming simple HTML, PostScript or PDF designed for - human modification. Examples of transparent image formats include - PNG, XCF and JPG. Opaque formats include proprietary formats that - can be read and edited only by proprietary word processors, SGML or - XML for which the DTD and/or processing tools are not generally - available, and the machine-generated HTML, PostScript or PDF - produced by some word processors for output purposes only. - - The "Title Page" means, for a printed book, the title page itself, - plus such following pages as are needed to hold, legibly, the - material this License requires to appear in the title page. For - works in formats which do not have any title page as such, "Title - Page" means the text near the most prominent appearance of the - work's title, preceding the beginning of the body of the text. - - A section "Entitled XYZ" means a named subunit of the Document - whose title either is precisely XYZ or contains XYZ in parentheses - following text that translates XYZ in another language. (Here XYZ - stands for a specific section name mentioned below, such as - "Acknowledgements", "Dedications", "Endorsements", or "History".) - To "Preserve the Title" of such a section when you modify the - Document means that it remains a section "Entitled XYZ" according - to this definition. - - The Document may include Warranty Disclaimers next to the notice - which states that this License applies to the Document. These - Warranty Disclaimers are considered to be included by reference in - this License, but only as regards disclaiming warranties: any other - implication that these Warranty Disclaimers may have is void and - has no effect on the meaning of this License. - - 2. VERBATIM COPYING - - You may copy and distribute the Document in any medium, either - commercially or noncommercially, provided that this License, the - copyright notices, and the license notice saying this License - applies to the Document are reproduced in all copies, and that you - add no other conditions whatsoever to those of this License. You - may not use technical measures to obstruct or control the reading - or further copying of the copies you make or distribute. However, - you may accept compensation in exchange for copies. If you - distribute a large enough number of copies you must also follow - the conditions in section 3. - - You may also lend copies, under the same conditions stated above, - and you may publicly display copies. - - 3. COPYING IN QUANTITY - - If you publish printed copies (or copies in media that commonly - have printed covers) of the Document, numbering more than 100, and - the Document's license notice requires Cover Texts, you must - enclose the copies in covers that carry, clearly and legibly, all - these Cover Texts: Front-Cover Texts on the front cover, and - Back-Cover Texts on the back cover. Both covers must also clearly - and legibly identify you as the publisher of these copies. The - front cover must present the full title with all words of the - title equally prominent and visible. You may add other material - on the covers in addition. Copying with changes limited to the - covers, as long as they preserve the title of the Document and - satisfy these conditions, can be treated as verbatim copying in - other respects. - - If the required texts for either cover are too voluminous to fit - legibly, you should put the first ones listed (as many as fit - reasonably) on the actual cover, and continue the rest onto - adjacent pages. - - If you publish or distribute Opaque copies of the Document - numbering more than 100, you must either include a - machine-readable Transparent copy along with each Opaque copy, or - state in or with each Opaque copy a computer-network location from - which the general network-using public has access to download - using public-standard network protocols a complete Transparent - copy of the Document, free of added material. If you use the - latter option, you must take reasonably prudent steps, when you - begin distribution of Opaque copies in quantity, to ensure that - this Transparent copy will remain thus accessible at the stated - location until at least one year after the last time you - distribute an Opaque copy (directly or through your agents or - retailers) of that edition to the public. - - It is requested, but not required, that you contact the authors of - the Document well before redistributing any large number of - copies, to give them a chance to provide you with an updated - version of the Document. - - 4. MODIFICATIONS - - You may copy and distribute a Modified Version of the Document - under the conditions of sections 2 and 3 above, provided that you - release the Modified Version under precisely this License, with - the Modified Version filling the role of the Document, thus - licensing distribution and modification of the Modified Version to - whoever possesses a copy of it. In addition, you must do these - things in the Modified Version: - - A. Use in the Title Page (and on the covers, if any) a title - distinct from that of the Document, and from those of - previous versions (which should, if there were any, be listed - in the History section of the Document). You may use the - same title as a previous version if the original publisher of - that version gives permission. - - B. List on the Title Page, as authors, one or more persons or - entities responsible for authorship of the modifications in - the Modified Version, together with at least five of the - principal authors of the Document (all of its principal - authors, if it has fewer than five), unless they release you - from this requirement. - - C. State on the Title page the name of the publisher of the - Modified Version, as the publisher. - - D. Preserve all the copyright notices of the Document. - - E. Add an appropriate copyright notice for your modifications - adjacent to the other copyright notices. - - F. Include, immediately after the copyright notices, a license - notice giving the public permission to use the Modified - Version under the terms of this License, in the form shown in - the Addendum below. - - G. Preserve in that license notice the full lists of Invariant - Sections and required Cover Texts given in the Document's - license notice. - - H. Include an unaltered copy of this License. - - I. Preserve the section Entitled "History", Preserve its Title, - and add to it an item stating at least the title, year, new - authors, and publisher of the Modified Version as given on - the Title Page. If there is no section Entitled "History" in - the Document, create one stating the title, year, authors, - and publisher of the Document as given on its Title Page, - then add an item describing the Modified Version as stated in - the previous sentence. - - J. Preserve the network location, if any, given in the Document - for public access to a Transparent copy of the Document, and - likewise the network locations given in the Document for - previous versions it was based on. These may be placed in - the "History" section. You may omit a network location for a - work that was published at least four years before the - Document itself, or if the original publisher of the version - it refers to gives permission. - - K. For any section Entitled "Acknowledgements" or "Dedications", - Preserve the Title of the section, and preserve in the - section all the substance and tone of each of the contributor - acknowledgements and/or dedications given therein. - - L. Preserve all the Invariant Sections of the Document, - unaltered in their text and in their titles. Section numbers - or the equivalent are not considered part of the section - titles. - - M. Delete any section Entitled "Endorsements". Such a section - may not be included in the Modified Version. - - N. Do not retitle any existing section to be Entitled - "Endorsements" or to conflict in title with any Invariant - Section. - - O. Preserve any Warranty Disclaimers. - - If the Modified Version includes new front-matter sections or - appendices that qualify as Secondary Sections and contain no - material copied from the Document, you may at your option - designate some or all of these sections as invariant. To do this, - add their titles to the list of Invariant Sections in the Modified - Version's license notice. These titles must be distinct from any - other section titles. - - You may add a section Entitled "Endorsements", provided it contains - nothing but endorsements of your Modified Version by various - parties--for example, statements of peer review or that the text - has been approved by an organization as the authoritative - definition of a standard. - - You may add a passage of up to five words as a Front-Cover Text, - and a passage of up to 25 words as a Back-Cover Text, to the end - of the list of Cover Texts in the Modified Version. Only one - passage of Front-Cover Text and one of Back-Cover Text may be - added by (or through arrangements made by) any one entity. If the - Document already includes a cover text for the same cover, - previously added by you or by arrangement made by the same entity - you are acting on behalf of, you may not add another; but you may - replace the old one, on explicit permission from the previous - publisher that added the old one. - - The author(s) and publisher(s) of the Document do not by this - License give permission to use their names for publicity for or to - assert or imply endorsement of any Modified Version. - - 5. COMBINING DOCUMENTS - - You may combine the Document with other documents released under - this License, under the terms defined in section 4 above for - modified versions, provided that you include in the combination - all of the Invariant Sections of all of the original documents, - unmodified, and list them all as Invariant Sections of your - combined work in its license notice, and that you preserve all - their Warranty Disclaimers. - - The combined work need only contain one copy of this License, and - multiple identical Invariant Sections may be replaced with a single - copy. If there are multiple Invariant Sections with the same name - but different contents, make the title of each such section unique - by adding at the end of it, in parentheses, the name of the - original author or publisher of that section if known, or else a - unique number. Make the same adjustment to the section titles in - the list of Invariant Sections in the license notice of the - combined work. - - In the combination, you must combine any sections Entitled - "History" in the various original documents, forming one section - Entitled "History"; likewise combine any sections Entitled - "Acknowledgements", and any sections Entitled "Dedications". You - must delete all sections Entitled "Endorsements." - - 6. COLLECTIONS OF DOCUMENTS - - You may make a collection consisting of the Document and other - documents released under this License, and replace the individual - copies of this License in the various documents with a single copy - that is included in the collection, provided that you follow the - rules of this License for verbatim copying of each of the - documents in all other respects. - - You may extract a single document from such a collection, and - distribute it individually under this License, provided you insert - a copy of this License into the extracted document, and follow - this License in all other respects regarding verbatim copying of - that document. - - 7. AGGREGATION WITH INDEPENDENT WORKS - - A compilation of the Document or its derivatives with other - separate and independent documents or works, in or on a volume of - a storage or distribution medium, is called an "aggregate" if the - copyright resulting from the compilation is not used to limit the - legal rights of the compilation's users beyond what the individual - works permit. When the Document is included in an aggregate, this - License does not apply to the other works in the aggregate which - are not themselves derivative works of the Document. - - If the Cover Text requirement of section 3 is applicable to these - copies of the Document, then if the Document is less than one half - of the entire aggregate, the Document's Cover Texts may be placed - on covers that bracket the Document within the aggregate, or the - electronic equivalent of covers if the Document is in electronic - form. Otherwise they must appear on printed covers that bracket - the whole aggregate. - - 8. TRANSLATION - - Translation is considered a kind of modification, so you may - distribute translations of the Document under the terms of section - 4. Replacing Invariant Sections with translations requires special - permission from their copyright holders, but you may include - translations of some or all Invariant Sections in addition to the - original versions of these Invariant Sections. You may include a - translation of this License, and all the license notices in the - Document, and any Warranty Disclaimers, provided that you also - include the original English version of this License and the - original versions of those notices and disclaimers. In case of a - disagreement between the translation and the original version of - this License or a notice or disclaimer, the original version will - prevail. - - If a section in the Document is Entitled "Acknowledgements", - "Dedications", or "History", the requirement (section 4) to - Preserve its Title (section 1) will typically require changing the - actual title. - - 9. TERMINATION - - You may not copy, modify, sublicense, or distribute the Document - except as expressly provided for under this License. Any other - attempt to copy, modify, sublicense or distribute the Document is - void, and will automatically terminate your rights under this - License. However, parties who have received copies, or rights, - from you under this License will not have their licenses - terminated so long as such parties remain in full compliance. - - 10. FUTURE REVISIONS OF THIS LICENSE - - The Free Software Foundation may publish new, revised versions of - the GNU Free Documentation License from time to time. Such new - versions will be similar in spirit to the present version, but may - differ in detail to address new problems or concerns. See - `http://www.gnu.org/copyleft/'. - - Each version of the License is given a distinguishing version - number. If the Document specifies that a particular numbered - version of this License "or any later version" applies to it, you - have the option of following the terms and conditions either of - that specified version or of any later version that has been - published (not as a draft) by the Free Software Foundation. If - the Document does not specify a version number of this License, - you may choose any version ever published (not as a draft) by the - Free Software Foundation. - -ADDENDUM: How to use this License for your documents -==================================================== - -To use this License in a document you have written, include a copy of -the License in the document and put the following copyright and license -notices just after the title page: - - Copyright (C) YEAR YOUR NAME. - Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.2 - or any later version published by the Free Software Foundation; - with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - Texts. A copy of the license is included in the section entitled ``GNU - Free Documentation License''. - - If you have Invariant Sections, Front-Cover Texts and Back-Cover -Texts, replace the "with...Texts." line with this: - - with the Invariant Sections being LIST THEIR TITLES, with - the Front-Cover Texts being LIST, and with the Back-Cover Texts - being LIST. - - If you have Invariant Sections without Cover Texts, or some other -combination of the three, merge those two alternatives to suit the -situation. - - If your document contains nontrivial examples of program code, we -recommend releasing these examples in parallel under your choice of -free software license, such as the GNU General Public License, to -permit their use in free software. - - - -Tag Table: -Node: Top1609 -Node: Introduction3622 -Node: Acknowledgments5115 -Node: Sections5663 -Node: Status8213 -Node: Untracked files10952 -Node: Staging and Committing12157 -Node: History15562 -Node: Reflogs18744 -Node: Commit Buffer19148 -Node: Diffing20474 -Node: Tagging21461 -Node: Resetting21911 -Node: Stashing23435 -Node: Branches and Remotes24506 -Node: Branches Popup25327 -Node: Remotes Popup26589 -Node: Branches in the Branch Manager26940 -Node: Remotes in the Branch Manager28112 -Node: Wazzup28673 -Node: Merging29466 -Node: Rebasing30516 -Node: Interactive Rebasing31342 -Node: Rewriting32637 -Node: Pushing and Pulling35947 -Node: Submodules37877 -Node: Bisecting38325 -Node: Finding commits not merged upstream39560 -Node: Using Magit Extensions40826 -Node: Activating extensions41122 -Node: Interfacing with Subversion42507 -Node: Interfacing with Topgit43107 -Node: Interfacing with StGit44145 -Node: Using Git Directly45114 -Node: GNU Free Documentation License45502 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: diff --git a/elpa/magit-1.4.1/AUTHORS.md b/elpa/magit-20160223.828/AUTHORS.md similarity index 73% rename from elpa/magit-1.4.1/AUTHORS.md rename to elpa/magit-20160223.828/AUTHORS.md index 36e4ffe..9dff861 100644 --- a/elpa/magit-1.4.1/AUTHORS.md +++ b/elpa/magit-20160223.828/AUTHORS.md @@ -1,7 +1,12 @@ Authors ======= -Also see https://github.com/magit/magit/graphs/contributors. +The following people have contributed to Magit, including the +libraries `git-commit.el`, `magit-popup.el`, and `with-editor.el` +which are distributed as separate Elpa packages. + +For statistics see http://magit.vc/stats/authors.html. + Names below are sorted alphabetically. Author @@ -14,11 +19,18 @@ Maintainer - Jonas Bernoulli -Retired Maintainers -------------------- +Developers +---------- + +- Kyle Meyer +- Noam Postavsky + +Retired Maintainers and Developers +---------------------------------- - Nicolas Dudebout - Peter J. Weisberg +- Pieter Praet - Phil Jackson - Rémi Vanicat - Yann Hodique @@ -26,14 +38,16 @@ Retired Maintainers Contributors ------------ -- aaa707 - Aaron Culich - Abdo Roig-Maranges - acple - Adam Spiers +- Adeodato Simó - Ævar Arnfjörð Bjarmason - Alan Falloon +- Alex Dunn - Alexey Voinov +- Alex Kost - Alex Ott - Andreas Fuchs - Andreas Liljeqvist @@ -42,12 +56,16 @@ Contributors - Andrew Kirkpatrick - Andrew Schwartzmeyer - Andrey Smirnov +- Andriy Kmit' +- Andy Sawyer +- Barak A. Pearlmutter - Bastian Beischer - Ben Walton - Bradley Wright - Brandon W Maister - Brian Warner - Bryan Shell +- Carl Lieberman - Chris Bernard - Chris Done - Chris Moore @@ -58,13 +76,14 @@ Contributors - Cornelius Mika - Craig Andera - Dale Hagglund -- Damien Cassou +- Damien Cassou - Daniel Brockman - Daniel Farina - Daniel Hackney - Dan LaManna - David Abrahams - David Hull +- David L. Rager - David Wallin - Dennis Paskorz - Divye Kapoor @@ -75,25 +94,34 @@ Contributors - Evgkeni Sampelnikof - Felix Geller - Feng Li +- Florian Ragwitz - Geoff Shannon - George Kadianakis - Graham Clark - Greg A. Woods +- Greg Lucas - Greg Sexton +- Guillaume Martres - Hannu Koivisto - Hans-Peter Deifel - Ian Eure +- Ingo Lohmar - Jan Tatarik - Jasper St. Pierre - Jeff Bellegarde - Jeff Dairiki - Jesse Alama +- Johann Klähn +- John Mastro - John Wiegley - Jonas Bernoulli - Jonathan Roes - Jordan Greenberg +- Josiah Schwab - Julien Danjou - Justin Caratzas +- Kan-Ru Chen +- Kan-Ru Chen - Kimberly Wolk - Kyle Meyer - Laurent Laffont @@ -101,6 +129,7 @@ Contributors - Lele Gaifax - Leo Liu - Leonardo Etcheverry +- Lingchao Xin - Lluís Vilanova - Loic Dachary - Luís Borges de Oliveira @@ -113,45 +142,60 @@ Contributors - Marian Schubert - Marius Vollmer - Mark Hepburn +- Mark Karpov +- Mark Oteiza - Matus Goljer +- Michael Fogleman +- Michael Griffiths +- Michael Heerdegen +- Michal Sojka - Miles Bader +- Miloš Mošić - Mitchel Humpherys - Moritz Bunkus -- Nathan Weizenbaum +- Natalie Weizenbaum - Nguyễn Tuấn Anh - Nic Ferier - Nick Alcock - Nick Alexander - Nick Dimiduk +- Nicklas Lindgren - Nicolas Dudebout +- Nicolas Petton - Nicolas Richard +- Nikolay Martynov - Noam Postavsky - Ole Arndt +- Oleh Krehel - Óscar Fuentes - Paul Stadig - Pavel Holejsovsky - Pekka Pessi +- Peter Eisentraut +- Peter Jaros - Peter J. Weisberg +- Peter Vasil - Philippe Vaucher - Philipp Haselwarter - Philip Weaver - Phil Jackson +- Phil Sainty - Pieter Praet - Prathamesh Sonpatki - rabio - Rafael Laboissiere -- Raimon Grau +- Raimon Grau - Ramkumar Ramachandra - Remco van 't Veer - Rémi Vanicat - René Stadler +- Richard Kim - Robert Boone - Robin Green - Roger Crew - Romain Francoise - Ron Parker - Roy Crihfield -- Rüdiger Sonderfeld - Rüdiger Sonderfeld - Ryan C. Thompson - Samuel Bronson @@ -165,20 +209,33 @@ Contributors - Servilio Afre Puentes - Štěpán Němec - Steven Chow +- Steven E. Harris - Steven Thomas +- Steven Vancoillie - Steve Purcell - Suhail Shergill +- Sylvain Rousseau +- Syohei Yoshida - Takafumi Arakaki +- Teemu Likonen - Teruki Shigitani - Thierry Volpiatto +- Thomas A Caswell - Thomas Frössman - Thomas Jost - Thomas Riccardi - Tibor Simko - Timo Juhani Lindfors +- Tim Perkins +- Tim Wraight - Ting-Yu Lin - Tom Feist +- Vineet Naik +- Wei Huang - Wilfred Hughes - Win Treese +- Xavier Noria - Yann Hodique - York Zhao +- Yuichi Higashi +- Zach Latta diff --git a/elpa/magit-20160223.828/COPYING b/elpa/magit-20160223.828/COPYING new file mode 100644 index 0000000..4432540 --- /dev/null +++ b/elpa/magit-20160223.828/COPYING @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/elpa/magit-1.4.1/dir b/elpa/magit-20160223.828/dir similarity index 88% rename from elpa/magit-1.4.1/dir rename to elpa/magit-20160223.828/dir index d389e6e..5fec543 100644 --- a/elpa/magit-1.4.1/dir +++ b/elpa/magit-20160223.828/dir @@ -15,4 +15,4 @@ File: dir, Node: Top This is the top of the INFO tree * Menu: Emacs -* Magit (1.4.0): (magit). Using Git from Emacs with Magit. (1.4.0) +* Magit: (magit). Using Git from Emacs with Magit. diff --git a/elpa/magit-20160223.828/git-rebase.el b/elpa/magit-20160223.828/git-rebase.el new file mode 100644 index 0000000..fd60718 --- /dev/null +++ b/elpa/magit-20160223.828/git-rebase.el @@ -0,0 +1,461 @@ +;;; git-rebase.el --- Edit Git rebase files -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Phil Jackson +;; Maintainer: Jonas Bernoulli + +;; This file is not part of GNU Emacs. + +;; This file 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. + +;; This file 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 this file. If not, see . + +;;; Commentary: + +;; This package assists the user in editing the list of commits to be +;; rewritten during an interactive rebase. + +;; When the user initiates an interactive rebase, e.g. using "r e" in +;; a Magit buffer or on the command line using "git rebase -i REV", +;; Git invokes the `$GIT_SEQUENCE_EDITOR' (or if that is undefined +;; `$GIT_EDITOR' or even `$EDITOR') letting the user rearrange, drop, +;; reword, edit, and squash commits. + +;; This package provides the major-mode `git-rebase-mode' which makes +;; doing so much more fun, by making the buffer more colorful and +;; providing the following commands: +;; +;; C-c C-c Tell Git to make it happen. +;; C-c C-k Tell Git that you changed your mind, i.e. abort. +;; +;; p Move point to previous line. +;; n Move point to next line. +;; +;; M-p Move the commit at point up. +;; M-n Move the commit at point down. +;; +;; k Drop the commit at point. +;; c Don't drop the commit at point. +;; r Change the message of the commit at point. +;; e Edit the commit at point. +;; s Squash the commit at point, into the one above. +;; f Like "s" but don't also edit the commit message. +;; x Add a script to be run with the commit at point +;; being checked out. +;; +;; RET Show the commit at point in another buffer. +;; C-/ Undo last change. + +;; You should probably also read the `git-rebase' manpage. + +;;; Code: + +(require 'dash) +(require 'easymenu) +(require 'server) +(require 'with-editor) +(require 'magit) + +(eval-when-compile (require 'recentf)) + +;;; Options +;;;; Variables + +(defgroup git-rebase nil + "Edit Git rebase sequences." + :group 'tools) + +(defcustom git-rebase-auto-advance t + "Whether to move to next line after changing a line." + :group 'git-rebase + :type 'boolean) + +(defcustom git-rebase-show-instructions t + "Whether to show usage instructions inside the rebase buffer." + :group 'git-rebase + :type 'boolean) + +(defcustom git-rebase-confirm-cancel t + "Whether confirmation is required to cancel." + :group 'git-rebase + :type 'boolean) + +;;;; Faces + +(defgroup git-rebase-faces nil + "Faces used by Git-Rebase mode." + :group 'faces + :group 'git-rebase) + +(defface git-rebase-hash + '((((class color) (background light)) :foreground "grey60") + (((class color) (background dark)) :foreground "grey40")) + "Face for commit hashes." + :group 'git-rebase-faces) + +(defface git-rebase-description nil + "Face for commit descriptions." + :group 'git-rebase-faces) + +(defface git-rebase-killed-action + '((t (:inherit font-lock-comment-face :strike-through t))) + "Face for commented action and exec lines." + :group 'git-rebase-faces) + +;;; Keymaps + +(defvar git-rebase-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map special-mode-map) + (define-key map (kbd "q") 'undefined) + (define-key map [remap undo] 'git-rebase-undo) + (define-key map (kbd "RET") 'git-rebase-show-commit) + (define-key map (kbd "SPC") 'magit-diff-show-or-scroll-up) + (define-key map (kbd "x") 'git-rebase-exec) + (define-key map (kbd "c") 'git-rebase-pick) + (define-key map (kbd "r") 'git-rebase-reword) + (define-key map (kbd "w") 'git-rebase-reword) + (define-key map (kbd "e") 'git-rebase-edit) + (define-key map (kbd "s") 'git-rebase-squash) + (define-key map (kbd "f") 'git-rebase-fixup) + (define-key map (kbd "y") 'git-rebase-insert) + (define-key map (kbd "k") 'git-rebase-kill-line) + (define-key map (kbd "C-k") 'git-rebase-kill-line) + (define-key map (kbd "p") 'git-rebase-backward-line) + (define-key map (kbd "n") 'forward-line) + (define-key map (kbd "M-p") 'git-rebase-move-line-up) + (define-key map (kbd "M-n") 'git-rebase-move-line-down) + (define-key map (kbd "M-") 'git-rebase-move-line-up) + (define-key map (kbd "M-") 'git-rebase-move-line-down) + (define-key map (kbd "C-x C-t") 'git-rebase-move-line-up) + map) + "Keymap for Git-Rebase mode.") + +(put 'git-rebase-reword :advertised-binding "r") +(put 'git-rebase-move-line-up :advertised-binding (kbd "M-p")) + +(easy-menu-define git-rebase-mode-menu git-rebase-mode-map + "Git-Rebase mode menu" + '("Rebase" + ["Pick" git-rebase-pick t] + ["Reword" git-rebase-reword t] + ["Edit" git-rebase-edit t] + ["Squash" git-rebase-squash t] + ["Fixup" git-rebase-fixup t] + ["Kill" git-rebase-kill-line t] + ["Execute" git-rebase-exec t] + ["Move Down" git-rebase-move-line-down t] + ["Move Up" git-rebase-move-line-up t] + "---" + ["Cancel" with-editor-cancel t] + ["Finish" with-editor-finish t])) + +(defvar git-rebase-command-descriptions + '((with-editor-finish . "tell Git to make it happen") + (with-editor-cancel . "tell Git that you changed your mind, i.e. abort") + (previous-line . "move point to previous line") + (next-line . "move point to next line") + (git-rebase-move-line-up . "move the commit at point up") + (git-rebase-move-line-down . "move the commit at point down") + (git-rebase-show-commit . "show the commit at point in another buffer") + (undo . "undo last change") + (git-rebase-kill-line . "drop the commit at point"))) + +;;; Commands + +(defun git-rebase-pick () + "Use commit on current line." + (interactive) + (git-rebase-set-action "pick")) + +(defun git-rebase-reword () + "Edit message of commit on current line." + (interactive) + (git-rebase-set-action "reword")) + +(defun git-rebase-edit () + "Stop at the commit on the current line." + (interactive) + (git-rebase-set-action "edit")) + +(defun git-rebase-squash () + "Meld commit on current line into previous commit, edit message." + (interactive) + (git-rebase-set-action "squash")) + +(defun git-rebase-fixup () + "Meld commit on current line into previous commit, discard its message." + (interactive) + (git-rebase-set-action "fixup")) + +(defconst git-rebase-line + "^\\(#?\\(?:[fprse]\\|pick\\|reword\\|edit\\|squash\\|fixup\\|exec\\)\\) \ +\\(?:\\([^ \n]+\\) \\(.*\\)\\)?") + +(defun git-rebase-set-action (action) + (goto-char (line-beginning-position)) + (if (and (looking-at git-rebase-line) + (not (string-match-p "\\(e\\|exec\\)$" (match-string 1)))) + (let ((inhibit-read-only t)) + (replace-match action t t nil 1) + (when git-rebase-auto-advance + (forward-line))) + (ding))) + +(defun git-rebase-line-p (&optional pos) + (save-excursion + (when pos (goto-char pos)) + (goto-char (line-beginning-position)) + (looking-at-p git-rebase-line))) + +(defun git-rebase-region-bounds () + (when (use-region-p) + (let ((beg (save-excursion (goto-char (region-beginning)) + (line-beginning-position))) + (end (save-excursion (goto-char (region-end)) + (line-end-position)))) + (when (and (git-rebase-line-p beg) + (git-rebase-line-p end)) + (list beg (1+ end)))))) + +(defun git-rebase-move-line-down (n) + "Move the current commit (or command) N lines down. +If N is negative, move the commit up instead. With an active +region, move all the lines that the region touches, not just the +current line." + (interactive "p") + (-let* (((beg end) (or (git-rebase-region-bounds) + (list (line-beginning-position) + (1+ (line-end-position))))) + (pt-offset (- (point) beg)) + (mark-offset (and mark-active (- (mark) beg)))) + (save-restriction + (narrow-to-region + (point-min) + (1+ (save-excursion + (goto-char (point-min)) + (while (re-search-forward git-rebase-line nil t)) + (point)))) + (if (or (and (< n 0) (= beg (point-min))) + (and (> n 0) (= end (point-max))) + (> end (point-max))) + (ding) + (goto-char (if (< n 0) beg end)) + (forward-line n) + (atomic-change-group + (let ((inhibit-read-only t)) + (insert (delete-and-extract-region beg end))) + (let ((new-beg (- (point) (- end beg)))) + (when (use-region-p) + (setq deactivate-mark nil) + (set-mark (+ new-beg mark-offset))) + (goto-char (+ new-beg pt-offset)))))))) + +(defun git-rebase-move-line-up (n) + "Move the current commit (or command) N lines up. +If N is negative, move the commit down instead. With an active +region, move all the lines that the region touches, not just the +current line." + (interactive "p") + (git-rebase-move-line-down (- n))) + +(defun git-rebase-highlight-region (start end window rol) + (let ((inhibit-read-only t) + (deactivate-mark nil) + (bounds (git-rebase-region-bounds))) + (mapc #'delete-overlay magit-section-highlight-overlays) + (when bounds + (magit-section-make-overlay (car bounds) (cadr bounds) + 'magit-section-heading-selection)) + (if (and bounds (not magit-keep-region-overlay)) + (funcall (default-value 'redisplay-unhighlight-region-function) rol) + (funcall (default-value 'redisplay-highlight-region-function) + start end window rol)))) + +(defun git-rebase-unhighlight-region (rol) + (mapc #'delete-overlay magit-section-highlight-overlays) + (funcall (default-value 'redisplay-unhighlight-region-function) rol)) + +(defun git-rebase-kill-line () + "Kill the current action line." + (interactive) + (goto-char (line-beginning-position)) + (when (and (looking-at git-rebase-line) + (not (eq (char-after) ?#))) + (let ((inhibit-read-only t)) + (insert ?#)) + (when git-rebase-auto-advance + (forward-line)))) + +(defun git-rebase-insert (rev) + "Read an arbitrary commit and insert it below current line." + (interactive (list (magit-read-branch-or-commit "Insert revision"))) + (forward-line) + (--if-let (magit-rev-format "%h %s" rev) + (let ((inhibit-read-only t)) + (insert "pick " it ?\n)) + (user-error "Unknown revision"))) + +(defun git-rebase-exec (arg) + "Insert a shell command to be run after the proceeding commit. + +If there already is such a command on the current line, then edit +that instead. With a prefix argument insert a new command even +when there already is one on the current line. With empty input +remove the command on the current line, if any." + (interactive "P") + (let ((inhibit-read-only t) initial command) + (unless arg + (goto-char (line-beginning-position)) + (when (looking-at "^#?\\(e\\|exec\\) \\(.*\\)") + (setq initial (match-string-no-properties 2)))) + (setq command (read-shell-command "Execute: " initial)) + (pcase (list command initial) + (`("" nil) (ding)) + (`("" ,_) + (delete-region (match-beginning 0) (1+ (match-end 0)))) + (`(,_ nil) + (forward-line) + (insert (concat "exec " command "\n")) + (unless git-rebase-auto-advance + (forward-line -1))) + (_ + (replace-match (concat "exec " command) t t) + (if git-rebase-auto-advance + (forward-line) + (goto-char (line-beginning-position))))))) + +(defun git-rebase-undo (&optional arg) + "Undo some previous changes. +Like `undo' but works in read-only buffers." + (interactive "P") + (let ((inhibit-read-only t)) + (undo arg))) + +(defun git-rebase-show-commit () + "Show the commit on the current line if any." + (interactive) + (save-excursion + (goto-char (line-beginning-position)) + (--if-let (and (looking-at git-rebase-line) + (match-string 2)) + (apply #'magit-show-commit it (magit-diff-arguments)) + (ding)))) + +(defun git-rebase-backward-line (&optional n) + "Move N lines backward (forward if N is negative). +Like `forward-line' but go into the opposite direction." + (interactive "p") + (forward-line (- n))) + +;;; Mode + +;;;###autoload +(define-derived-mode git-rebase-mode special-mode "Git 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." + :group 'git-rebase + (setq font-lock-defaults '(git-rebase-mode-font-lock-keywords t t)) + (unless git-rebase-show-instructions + (let ((inhibit-read-only t)) + (flush-lines "^\\($\\|#\\)"))) + (with-editor-mode 1) + (when git-rebase-confirm-cancel + (add-hook 'with-editor-cancel-query-functions + 'git-rebase-cancel-confirm nil t)) + (setq-local redisplay-highlight-region-function 'git-rebase-highlight-region) + (setq-local redisplay-unhighlight-region-function 'git-rebase-unhighlight-region) + (add-hook 'with-editor-pre-cancel-hook 'git-rebase-autostash-save nil t) + (add-hook 'with-editor-post-cancel-hook 'git-rebase-autostash-apply nil t)) + +(defun git-rebase-cancel-confirm (force) + (or (not (buffer-modified-p)) force (y-or-n-p "Abort this rebase? "))) + +(defun git-rebase-autostash-save () + (--when-let (magit-file-line (magit-git-dir "rebase-merge/autostash")) + (push (cons 'stash it) with-editor-cancel-alist))) + +(defun git-rebase-autostash-apply () + (--when-let (cdr (assq 'stash with-editor-cancel-alist)) + (magit-stash-apply it))) + +(defconst git-rebase-mode-font-lock-keywords + `(("^\\([efprs]\\|pick\\|reword\\|edit\\|squash\\|fixup\\) \\([^ \n]+\\) \\(.*\\)" + (1 'font-lock-keyword-face) + (2 'git-rebase-hash) + (3 'git-rebase-description)) + ("^\\(exec\\) \\(.*\\)" + (1 'font-lock-keyword-face) + (2 'git-rebase-description)) + ("^#.*" 0 'font-lock-comment-face) + ("^#[^ \n].*" 0 'git-rebase-killed-action t)) + "Font lock keywords for Git-Rebase mode.") + +(defun git-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." + (let ((inhibit-read-only t)) + (save-excursion + (goto-char (point-min)) + (when (and git-rebase-show-instructions + (re-search-forward "^# Commands:\n" nil t)) + (--each git-rebase-command-descriptions + (insert (format "# %-8s %s\n" + (substitute-command-keys (format "\\[%s]" (car it))) + (cdr it)))) + (while (re-search-forward "^#\\( ?\\)\\([^,],\\) \\([^ ]+\\) = " nil t) + (let ((cmd (intern (concat "git-rebase-" (match-string 3))))) + (if (not (fboundp cmd)) + (delete-region (line-beginning-position) (1+ (line-end-position))) + (replace-match " " t t nil 1) + (replace-match + (format "%-8s" + (mapconcat #'key-description + (--filter (not (eq (elt it 0) 'menu-bar)) + (reverse (where-is-internal cmd))) + ", ")) + t t nil 2)))))))) + +(add-hook 'git-rebase-mode-hook 'git-rebase-mode-show-keybindings t) + +(defun git-rebase-mode-disable-before-save-hook () + (set (make-local-variable 'before-save-hook) nil)) + +(add-hook 'git-rebase-mode-hook 'git-rebase-mode-disable-before-save-hook) + +;;;###autoload +(defconst git-rebase-filename-regexp "/git-rebase-todo\\'") +;;;###autoload +(add-to-list 'auto-mode-alist + (cons git-rebase-filename-regexp 'git-rebase-mode)) + +(add-to-list 'with-editor-server-window-alist + (cons git-rebase-filename-regexp 'switch-to-buffer)) + +(eval-after-load 'recentf + '(add-to-list 'recentf-exclude git-rebase-filename-regexp)) + +(provide 'git-rebase) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; git-rebase.el ends here diff --git a/elpa/magit-20160223.828/magit-apply.el b/elpa/magit-20160223.828/magit-apply.el new file mode 100644 index 0000000..996ee8a --- /dev/null +++ b/elpa/magit-20160223.828/magit-apply.el @@ -0,0 +1,559 @@ +;;; magit-apply.el --- apply Git diffs -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library implements commands for applying Git diffs or parts +;; of such a diff. The supported "apply variants" are apply, stage, +;; unstage, discard, and reverse - more than Git itself knows about, +;; at least at the porcelain level. + +;;; Code: + +(require 'magit-core) +(require 'magit-diff) +(require 'magit-wip) + +;; For `magit-apply' +(declare-function magit-anti-stage 'magit-rockstar) +(declare-function magit-am-popup 'magit-sequence) +;; For `magit-discard-files' +(declare-function magit-checkout-stage 'magit) +(declare-function magit-checkout-read-stage 'magit) +(defvar auto-revert-verbose) + +(require 'dired) + +;;; Options + +(defcustom magit-delete-by-moving-to-trash t + "Whether Magit uses the system's trash can." + :package-version '(magit . "2.1.0") + :group 'magit + :type 'boolean) + +(defcustom magit-unstage-committed t + "Whether unstaging a committed change reverts it instead. + +A committed change cannot be unstaged, because staging and +unstaging are actions that are concern with the differences +between the index and the working tree, not with committed +changes. + +If this option is non-nil (the default), then typing \"u\" +(`magit-unstage') on a committed change, causes it to be +reversed in the index but not the working tree. For more +information see command `magit-reverse-in-index'." + :package-version '(magit . "2.4.1") + :group 'magit-commands + :type 'boolean) + +;;; Commands +;;;; Apply + +(defun magit-apply (&rest args) + "Apply the change at point. +With a prefix argument and if necessary, attempt a 3-way merge." + (interactive (and current-prefix-arg (list "--3way"))) + (--when-let (magit-apply--get-selection) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(,(or `unstaged `staged) ,_) + (user-error "Change is already in the working tree")) + (`(untracked ,(or `file `files)) + (magit-am-popup)) + (`(,_ region) (magit-apply-region it args)) + (`(,_ hunk) (magit-apply-hunk it args)) + (`(,_ hunks) (magit-apply-hunks it args)) + (`(,_ file) (magit-apply-diff it args)) + (`(,_ files) (magit-apply-diffs it args))))) + +(defun magit-apply-diffs (sections &rest args) + (setq sections (magit-apply--get-diffs sections)) + (magit-apply-patch sections args + (mapconcat + (lambda (s) + (concat (magit-diff-file-header s) + (buffer-substring (magit-section-content s) + (magit-section-end s)))) + sections ""))) + +(defun magit-apply-diff (section &rest args) + (setq section (car (magit-apply--get-diffs (list section)))) + (magit-apply-patch section args + (concat (magit-diff-file-header section) + (buffer-substring (magit-section-content section) + (magit-section-end section))))) + +(defun magit-apply-hunks (sections &rest args) + (let ((section (magit-section-parent (car sections)))) + (when (string-match "^diff --cc" (magit-section-value section)) + (user-error "Cannot un-/stage resolution hunks. Stage the whole file")) + (magit-apply-patch section args + (concat (magit-section-diff-header section) + (mapconcat + (lambda (s) + (buffer-substring (magit-section-start s) + (magit-section-end s))) + sections ""))))) + +(defun magit-apply-hunk (section &rest args) + (when (string-match "^diff --cc" (magit-section-parent-value section)) + (user-error "Cannot un-/stage resolution hunks. Stage the whole file")) + (magit-apply-patch (magit-section-parent section) args + (concat (magit-diff-file-header section) + (buffer-substring (magit-section-start section) + (magit-section-end section))))) + +(defun magit-apply-region (section &rest args) + (unless (magit-diff-context-p) + (user-error "Not enough context to apply region. Increase the context")) + (when (string-match "^diff --cc" (magit-section-parent-value section)) + (user-error "Cannot un-/stage resolution hunks. Stage the whole file")) + (magit-apply-patch (magit-section-parent section) args + (concat (magit-diff-file-header section) + (magit-diff-hunk-region-patch section args)))) + +(defun magit-apply-patch (section:s args patch) + (let* ((files (if (atom section:s) + (list (magit-section-value section:s)) + (mapcar 'magit-section-value section:s))) + (command (symbol-name this-command)) + (command (if (and command (string-match "^magit-\\([^-]+\\)" command)) + (match-string 1 command) + "apply"))) + (when (and magit-wip-before-change-mode (not inhibit-magit-refresh)) + (magit-wip-commit-before-change files (concat " before " command))) + (with-temp-buffer + (insert patch) + (magit-run-git-with-input + "apply" args "-p0" + (unless (magit-diff-context-p) "--unidiff-zero") + "--ignore-space-change" "-")) + (unless inhibit-magit-refresh + (when magit-wip-after-apply-mode + (magit-wip-commit-after-apply files (concat " after " command))) + (magit-refresh)))) + +(defun magit-apply--get-selection () + (or (magit-region-sections 'hunk 'file) + (let ((section (magit-current-section))) + (pcase (magit-section-type section) + ((or `hunk `file) section) + ((or `staged `unstaged `untracked + `stashed-index `stashed-worktree `stashed-untracked) + (magit-section-children section)) + (_ (user-error "Cannot apply this, it's not a change")))))) + +(defun magit-apply--get-diffs (sections) + (magit-section-case + ([file diffstat] + (--map (or (magit-get-section + (append `((file . ,(magit-section-value it))) + (magit-section-ident magit-root-section))) + (error "Cannot get required diff headers")) + sections)) + (t sections))) + +;;;; Stage + +(defun magit-stage () + "Add the change at point to the staging area." + (interactive) + (--when-let (magit-apply--get-selection) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(untracked ,_) (magit-stage-untracked)) + (`(unstaged region) (magit-apply-region it "--cached")) + (`(unstaged hunk) (magit-apply-hunk it "--cached")) + (`(unstaged hunks) (magit-apply-hunks it "--cached")) + (`(unstaged file) (magit-stage-1 "-u" (list (magit-section-value it)))) + (`(unstaged files) (magit-stage-1 "-u" (magit-region-values))) + (`(unstaged list) (magit-stage-1 "-u")) + (`(staged ,_) (user-error "Already staged")) + (`(committed ,_) (user-error "Cannot stage committed changes")) + (`(undefined ,_) (user-error "Cannot stage this change"))))) + +;;;###autoload +(defun magit-stage-file (file) + "Stage all changes to FILE. +With a prefix argument or when there is no file at point ask for +the file to be staged. Otherwise stage the file at point without +requiring confirmation." + (interactive + (let* ((atpoint (magit-section-when (file))) + (current (magit-file-relative-name)) + (choices (nconc (magit-modified-files) + (magit-untracked-files))) + (default (car (member (or atpoint current) choices)))) + (list (if (or current-prefix-arg (not default)) + (magit-completing-read "Stage file" choices + nil t nil nil default) + default)))) + (magit-with-toplevel + (magit-stage-1 nil (list file)))) + +;;;###autoload +(defun magit-stage-modified (&optional all) + "Stage all changes to files modified in the worktree. +Stage all new content of tracked files and remove tracked files +that no longer exist in the working tree from the index also. +With a prefix argument also stage previously untracked (but not +ignored) files. +\('git add --update|--all .')." + (interactive (progn (unless (or (not (magit-anything-staged-p)) + (magit-confirm 'stage-all-changes)) + (user-error "Abort")) + (list current-prefix-arg))) + (magit-with-toplevel + (magit-stage-1 (if all "--all" "-u")))) + +(defun magit-stage-1 (arg &optional files) + (magit-wip-commit-before-change files " before stage") + (magit-run-git "add" arg (if files (cons "--" files) ".")) + (when magit-auto-revert-mode + (mapc #'magit-turn-on-auto-revert-mode-if-desired files)) + (magit-wip-commit-after-apply files " after stage")) + +(defun magit-stage-untracked () + (let* ((section (magit-current-section)) + (files (pcase (magit-diff-scope) + (`file (list (magit-section-value section))) + (`files (magit-region-values)) + (`list (magit-untracked-files)))) + plain repos) + (dolist (file files) + (if (magit-git-repo-p file t) + (push file repos) + (push file plain))) + (magit-wip-commit-before-change files " before stage") + (when plain + (magit-run-git "add" "--" plain) + (when magit-auto-revert-mode + (mapc #'magit-turn-on-auto-revert-mode-if-desired plain))) + (dolist (repo repos) + (save-excursion + (goto-char (magit-section-start + (magit-get-section + `((file . ,repo) (untracked) (status))))) + (call-interactively 'magit-submodule-add))) + (magit-wip-commit-after-apply files " after stage"))) + +;;;; Unstage + +(defun magit-unstage () + "Remove the change at point from the staging area." + (interactive) + (--when-let (magit-apply--get-selection) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(untracked ,_) (user-error "Cannot unstage untracked changes")) + (`(unstaged ,_) (user-error "Already unstaged")) + (`(staged region) (magit-apply-region it "--reverse" "--cached")) + (`(staged hunk) (magit-apply-hunk it "--reverse" "--cached")) + (`(staged hunks) (magit-apply-hunks it "--reverse" "--cached")) + (`(staged file) (magit-unstage-1 (list (magit-section-value it)))) + (`(staged files) (magit-unstage-1 (magit-region-values))) + (`(staged list) (magit-unstage-all)) + (`(committed ,_) (if magit-unstage-committed + (magit-reverse-in-index) + (user-error "Cannot unstage committed changes"))) + (`(undefined ,_) (user-error "Cannot unstage this change"))))) + +;;;###autoload +(defun magit-unstage-file (file) + "Unstage all changes to FILE. +With a prefix argument or when there is no file at point ask for +the file to be unstaged. Otherwise unstage the file at point +without requiring confirmation." + (interactive + (let* ((atpoint (magit-section-when (file))) + (current (magit-file-relative-name)) + (choices (magit-staged-files)) + (default (car (member (or atpoint current) choices)))) + (list (if (or current-prefix-arg (not default)) + (magit-completing-read "Unstage file" choices + nil t nil nil default) + default)))) + (magit-with-toplevel + (magit-unstage-1 (list file)))) + +(defun magit-unstage-1 (files) + (magit-wip-commit-before-change files " before unstage") + (if (magit-no-commit-p) + (magit-run-git "rm" "--cached" "--" files) + (magit-run-git "reset" "HEAD" "--" files)) + (magit-wip-commit-after-apply files " after unstage")) + +;;;###autoload +(defun magit-unstage-all () + "Remove all changes from the staging area." + (interactive) + (when (or (and (not (magit-anything-unstaged-p)) + (not (magit-untracked-files))) + (magit-confirm 'unstage-all-changes)) + (magit-wip-commit-before-change nil " before unstage") + (magit-run-git "reset" "HEAD" "--") + (magit-wip-commit-after-apply nil " after unstage"))) + +;;;; Discard + +(defun magit-discard () + "Remove the change at point." + (interactive) + (--when-let (magit-apply--get-selection) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(committed ,_) (user-error "Cannot discard committed changes")) + (`(undefined ,_) (user-error "Cannot discard this change")) + (`(,_ region) (magit-discard-region it)) + (`(,_ hunk) (magit-discard-hunk it)) + (`(,_ hunks) (magit-discard-hunks it)) + (`(,_ file) (magit-discard-file it)) + (`(,_ files) (magit-discard-files it)) + (`(,_ list) (magit-discard-files it))))) + +(defun magit-discard-region (section) + (when (magit-confirm 'discard "Discard region") + (magit-discard-apply section 'magit-apply-region))) + +(defun magit-discard-hunk (section) + (when (magit-confirm 'discard "Discard hunk") + (magit-discard-apply section 'magit-apply-hunk))) + +(defun magit-discard-apply (section apply) + (if (eq (magit-diff-type section) 'unstaged) + (funcall apply section "--reverse") + (if (magit-anything-unstaged-p + nil (if (eq (magit-section-type section) 'file) + (magit-section-value section) + (magit-section-parent-value section))) + (progn (let ((inhibit-magit-refresh t)) + (funcall apply section "--reverse" "--cached") + (funcall apply section "--reverse")) + (magit-refresh)) + (funcall apply section "--reverse" "--index")))) + +(defun magit-discard-hunks (sections) + (when (magit-confirm 'discard + (format "Discard %s hunks from %s" + (length sections) + (magit-section-parent-value (car sections)))) + (magit-discard-apply-n sections 'magit-apply-hunks))) + +(defun magit-discard-apply-n (sections apply) + (let ((section (car sections))) + (if (eq (magit-diff-type section) 'unstaged) + (funcall apply sections "--reverse") + (if (magit-anything-unstaged-p + nil (if (eq (magit-section-type section) 'file) + (magit-section-value section) + (magit-section-parent-value section))) + (progn (let ((inhibit-magit-refresh t)) + (funcall apply sections "--reverse" "--cached") + (funcall apply sections "--reverse")) + (magit-refresh)) + (funcall apply sections "--reverse" "--index"))))) + +(defun magit-discard-file (section) + (magit-discard-files (list section))) + +(defun magit-discard-files (sections) + (let ((auto-revert-verbose nil) + (type (magit-diff-type (car sections))) + (status (magit-file-status)) + files delete resurrect rename discard discard-new resolve) + (dolist (section sections) + (let ((file (magit-section-value section))) + (push file files) + (pcase (cons (pcase type + (`staged ?X) + (`unstaged ?Y) + (`untracked ?Z)) + (cddr (assoc file status))) + (`(?Z) (--each (magit-untracked-files nil file) + (push it delete))) + ((or `(?Z ?? ??) `(?Z ?! ?!)) (push file delete)) + ((or `(?Z ?D ? ) `(,_ ?D ?D)) (push file delete)) + ((or `(,_ ?U ,_) `(,_ ,_ ?U)) (push file resolve)) + (`(,_ ?A ?A) (push file resolve)) + (`(?X ?M ,(or ? ?M ?D)) (push section discard)) + (`(?Y ,_ ?M ) (push section discard)) + (`(?X ?A ?M ) (push file discard-new)) + (`(?X ?C ?M ) (push file discard-new)) + (`(?X ?A ,(or ? ?D)) (push file delete)) + (`(?X ?C ,(or ? ?D)) (push file delete)) + (`(?X ?D ,(or ? ?M )) (push file resurrect)) + (`(?Y ,_ ?D ) (push file resurrect)) + (`(?X ?R ,(or ? ?M ?D)) (push file rename))))) + (unwind-protect + (let ((inhibit-magit-refresh t)) + (magit-wip-commit-before-change files " before discard") + (when resolve + (dolist (file (nreverse resolve)) + (magit-checkout-stage file (magit-checkout-read-stage file)))) + (magit-discard-files--resurrect (nreverse resurrect)) + (magit-discard-files--delete (nreverse delete) status) + (magit-discard-files--rename (nreverse rename) status) + (magit-discard-files--discard (nreverse discard) + (nreverse discard-new)) + (magit-wip-commit-after-apply files " after discard")) + (magit-refresh)))) + +(defun magit-discard-files--resurrect (files) + (when (magit-confirm-files 'resurrect files) + (if (eq (magit-diff-type) 'staged) + (magit-call-git "reset" "--" files) + (magit-call-git "checkout" "--" files)))) + +(defun magit-discard-files--delete (files status) + (when (if magit-delete-by-moving-to-trash + (magit-confirm-files 'trash files) + (magit-confirm-files 'delete files)) + (let ((delete-by-moving-to-trash magit-delete-by-moving-to-trash)) + (dolist (file files) + (if (memq (magit-diff-type) '(unstaged untracked)) + (dired-delete-file file dired-recursive-deletes + magit-delete-by-moving-to-trash) + (pcase (nth 3 (assoc file status)) + (? (delete-file file t) + (magit-call-git "rm" "--cached" "--" file)) + (?M (let ((temp (magit-git-string "checkout-index" "--temp" file))) + (string-match + (format "\\(.+?\\)\t%s" (regexp-quote file)) temp) + (rename-file (match-string 1 temp) + (setq temp (concat file ".~{index}~"))) + (delete-file temp t)) + (magit-call-git "rm" "--cached" "--force" "--" file)) + (?D (magit-call-git "checkout" "--" file) + (delete-file file t) + (magit-call-git "rm" "--cached" "--force" "--" file)))))))) + +(defun magit-discard-files--rename (files status) + (when (magit-confirm 'rename "Undo rename %s" "Undo %i renames" + (mapcar (lambda (file) + (setq file (assoc file status)) + (format "%s -> %s" (cadr file) (car file))) + files)) + (dolist (file files) + (let ((orig (cadr (assoc file status)))) + (if (file-exists-p file) + (magit-call-git "mv" file orig) + (magit-call-git "rm" "--cached" "--" file) + (magit-call-git "reset" "--" orig)))))) + +(defun magit-discard-files--discard (sections new-files) + (let ((files (mapcar #'magit-section-value sections))) + (when (magit-confirm-files + 'discard (append files new-files) + (format "Discard %s changes in" (magit-diff-type))) + (if (eq (magit-diff-type (car sections)) 'unstaged) + (magit-call-git "checkout" "--" files) + (when new-files + (magit-call-git "add" "--" new-files) + (magit-call-git "reset" "--" new-files)) + (let ((binaries (magit-staged-binary-files))) + (when binaries + (setq sections + (--filter (not (member (magit-section-value it) binaries)) + sections))) + (if (= (length sections) 1) + (magit-discard-apply (car sections) 'magit-apply-diff) + (magit-discard-apply-n sections 'magit-apply-diffs)) + (when binaries + (let ((modified (magit-modified-files t))) + (setq binaries (--separate (member it modified) binaries))) + (when (cadr binaries) + (magit-call-git "reset" "--" (cadr binaries))) + (when (car binaries) + (user-error + (concat + "Cannot discard staged changes to binary files, " + "which also have unstaged changes. Unstage instead."))))))))) + +;;;; Reverse + +(defun magit-reverse (&rest args) + "Reverse the change at point in the working tree." + (interactive (and current-prefix-arg (list "--3way"))) + (--when-let (magit-apply--get-selection) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(untracked ,_) (user-error "Cannot reverse untracked changes")) + (`(unstaged ,_) (user-error "Cannot reverse unstaged changes")) + (`(,_ region) (magit-reverse-region it args)) + (`(,_ hunk) (magit-reverse-hunk it args)) + (`(,_ hunks) (magit-reverse-hunks it args)) + (`(,_ file) (magit-reverse-file it args)) + (`(,_ files) (magit-reverse-files it args)) + (`(,_ list) (magit-reverse-files it args))))) + +(defun magit-reverse-region (section args) + (when (magit-confirm 'reverse "Reverse region") + (apply 'magit-apply-region section "--reverse" args))) + +(defun magit-reverse-hunk (section args) + (when (magit-confirm 'reverse "Reverse hunk") + (apply 'magit-apply-hunk section "--reverse" args))) + +(defun magit-reverse-hunks (sections args) + (when (magit-confirm 'reverse + (format "Reverse %s hunks from %s" + (length sections) + (magit-section-parent-value (car sections)))) + (magit-apply-hunks sections "--reverse" args))) + +(defun magit-reverse-file (section args) + (magit-reverse-files (list section) args)) + +(defun magit-reverse-files (sections args) + (-let [(binaries sections) + (let ((bs (magit-staged-binary-files))) + (--separate (member (magit-section-value it) bs) sections))] + (when (magit-confirm-files 'reverse (mapcar #'magit-section-value sections)) + (if (= (length sections) 1) + (magit-apply-diff (car sections) "--reverse" args) + (magit-apply-diffs sections "--reverse" args))) + (when binaries + (user-error "Cannot reverse binary files")))) + +(defun magit-reverse-in-index (&rest args) + "Reverse the change at point in the index but not the working tree. + +Use this command to extract a change from `HEAD', while leaving +it in the working tree, so that it can later be committed using +a separate commit. A typical workflow would be: + +0. Optionally make sure that there are no uncommitted changes. +1. Visit the `HEAD' commit and navigate to the change that should + not have been included in that commit. +2. Type \"u\" (`magit-unstage') to reverse it in the index. + This assumes that `magit-unstage-committed-changes' is non-nil. +3. Type \"c e\" to extend `HEAD' with the staged changes, + including those that were already staged before. +4. Optionally stage the remaining changes using \"s\" or \"S\" + and then type \"c c\" to create a new commit." + (interactive) + (magit-reverse (cons "--cached" args))) + +;;; magit-apply.el ends soon +(provide 'magit-apply) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-apply.el ends here diff --git a/elpa/magit-20160223.828/magit-autoloads.el b/elpa/magit-20160223.828/magit-autoloads.el new file mode 100644 index 0000000..5fe1160 --- /dev/null +++ b/elpa/magit-20160223.828/magit-autoloads.el @@ -0,0 +1,1585 @@ +;;; magit-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "git-rebase" "git-rebase.el" (22221 60708 733000 +;;;;;; 0)) +;;; Generated autoloads from git-rebase.el + +(autoload 'git-rebase-mode "git-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. + +\(fn)" t nil) + +(defconst git-rebase-filename-regexp "/git-rebase-todo\\'") + +(add-to-list 'auto-mode-alist (cons git-rebase-filename-regexp 'git-rebase-mode)) + +;;;*** + +;;;### (autoloads nil "magit" "magit.el" (22221 60708 792000 0)) +;;; Generated autoloads from magit.el + +(autoload 'magit-status "magit" "\ +Show the status of the current Git repository in a buffer. +With a prefix argument prompt for a repository to be shown. +With two prefix arguments prompt for an arbitrary directory. +If that directory isn't the root of an existing repository +then offer to initialize it as a new repository. + +\(fn &optional DIRECTORY)" t nil) + +(autoload 'magit-status-internal "magit" "\ + + +\(fn DIRECTORY)" nil nil) + (autoload 'magit-show-refs-popup "magit" nil t) + +(autoload 'magit-show-refs-head "magit" "\ +List and compare references in a dedicated buffer. +Refs are compared with `HEAD'. + +\(fn &optional ARGS)" t nil) + +(autoload 'magit-show-refs-current "magit" "\ +List and compare references in a dedicated buffer. +Refs are compared with the current branch or `HEAD' if +it is detached. + +\(fn &optional ARGS)" t nil) + +(autoload 'magit-show-refs "magit" "\ +List and compare references in a dedicated buffer. +Refs are compared with a branch read form the user. + +\(fn &optional REF ARGS)" t nil) + +(autoload 'magit-find-file "magit" "\ +View FILE from REV. +Switch to a buffer visiting blob REV:FILE, +creating one if none already exists. + +\(fn REV FILE)" t nil) + +(autoload 'magit-find-file-other-window "magit" "\ +View FILE from REV, in another window. +Like `magit-find-file', but create a new window or reuse an +existing one. + +\(fn REV FILE)" t nil) + +(autoload 'magit-dired-jump "magit" "\ +Visit file at point using Dired. +With a prefix argument, visit in other window. If there +is no file at point then instead visit `default-directory'. + +\(fn &optional OTHER-WINDOW)" t nil) + +(autoload 'magit-checkout-file "magit" "\ +Checkout FILE from REV. + +\(fn REV FILE)" t nil) + +(autoload 'magit-init "magit" "\ +Initialize a Git repository, then show its status. + +If the directory is below an existing repository, then the user +has to confirm that a new one should be created inside. If the +directory is the root of the existing repository, then the user +has to confirm that it should be reinitialized. + +Non-interactively DIRECTORY is (re-)initialized unconditionally. + +\(fn DIRECTORY)" t nil) + (autoload 'magit-branch-popup "magit" nil t) + +(autoload 'magit-checkout "magit" "\ +Checkout REVISION, updating the index and the working tree. +If REVISION is a local branch then that becomes the current +branch. If it is something else then `HEAD' becomes detached. +Checkout fails if the working tree or the staging area contain +changes. + +\(git checkout REVISION). + +\(fn REVISION)" t nil) + +(autoload 'magit-branch "magit" "\ +Create BRANCH at branch or revision START-POINT. + +\(git branch [ARGS] BRANCH START-POINT). + +\(fn BRANCH START-POINT &optional ARGS)" t nil) + +(autoload 'magit-branch-and-checkout "magit" "\ +Create and checkout BRANCH at branch or revision START-POINT. + +\(git checkout [ARGS] -b BRANCH START-POINT). + +\(fn BRANCH START-POINT &optional ARGS)" t nil) + +(autoload 'magit-branch-spinoff "magit" "\ +Create new branch from the unpushed commits. + +Create and checkout a new branch starting at and tracking the +current branch. That branch in turn is reset to the last commit +it shares with its upstream. If the current branch has no +upstream or no unpushed commits, then the new branch is created +anyway and the previously current branch is not touched. + +This is useful to create a feature branch after work has already +began on the old branch (likely but not necessarily \"master\"). + +If the current branch is a member of the value of option +`magit-branch-prefer-remote-upstream' (which see), then the +current branch will be used as the starting point as usual, but +the upstream of the starting-point may be used as the upstream +of the new branch, instead of the starting-point itself. + +\(fn BRANCH &rest ARGS)" t nil) + +(autoload 'magit-branch-reset "magit" "\ +Reset a branch to the tip of another branch or any other commit. + +When the branch being reset is the current branch, then do a +hard reset. If there are any uncommitted changes, then the user +has to confirming the reset because those changes would be lost. + +This is useful when you have started work on a feature branch but +realize it's all crap and want to start over. + +When resetting to another branch and a prefix argument is used, +then also set the target branch as the upstream of the branch +that is being reset. + +\(fn BRANCH TO &optional ARGS SET-UPSTREAM)" t nil) + +(autoload 'magit-branch-delete "magit" "\ +Delete one or multiple branches. +If the region marks multiple branches, then offer to delete +those, otherwise prompt for a single branch to be deleted, +defaulting to the branch at point. + +\(fn BRANCHES &optional FORCE)" t nil) + +(autoload 'magit-branch-rename "magit" "\ +Rename branch OLD to NEW. +With prefix, forces the rename even if NEW already exists. + +\(git branch -m|-M OLD NEW). + +\(fn OLD NEW &optional FORCE)" t nil) + +(autoload 'magit-edit-branch*description "magit" "\ +Edit the description of the current branch. +With a prefix argument edit the description of another branch. + +The description for the branch named NAME is stored in the Git +variable `branch..description'. + +\(fn BRANCH)" t nil) + +(autoload 'magit-set-branch*merge/remote "magit" "\ +Set or unset the upstream of the current branch. +With a prefix argument do so for another branch. + +When the branch in question already has an upstream then simply +unsets it. Invoke this command again to set another upstream. + +Together the Git variables `branch..remote' and +`branch..merge' define the upstream branch of the local +branch named NAME. The value of `branch..remote' is the +name of the upstream remote. The value of `branch..merge' +is the full reference of the upstream branch, on the remote. + +Non-interactively, when UPSTREAM is non-nil, then always set it +as the new upstream, regardless of whether another upstream was +already set. When nil, then always unset. + +\(fn BRANCH UPSTREAM)" t nil) + +(autoload 'magit-cycle-branch*rebase "magit" "\ +Cycle the value of `branch..rebase' for the current branch. +With a prefix argument cycle the value for another branch. + +The Git variables `branch..rebase' controls whether pulling +into the branch named NAME is done by rebasing that branch onto +the fetched branch or by merging that branch. + +When `true' then pulling is done by rebasing. +When `false' then pulling is done by merging. + +When that variable is undefined then the value of `pull.rebase' +is used instead. It defaults to `false'. + +\(fn BRANCH)" t nil) + +(autoload 'magit-cycle-branch*pushRemote "magit" "\ +Cycle the value of `branch..pushRemote' for the current branch. +With a prefix argument cycle the value for another branch. + +The Git variable `branch..pushRemote' specifies the remote +that the branch named NAME is usually pushed to. The value has +to be the name of an existing remote. + +If that variable is undefined, then the value of the Git variable +`remote.pushDefault' is used instead, provided that it is defined, +which by default it is not. + +\(fn BRANCH)" t nil) + +(autoload 'magit-cycle-pull\.rebase "magit" "\ +Cycle the repository-local value of `pull.rebase'. + +The Git variable `pull.rebase' specifies whether pulling is done +by rebasing or by merging. It can be overwritten using the Git +variable `branch..rebase'. + +When `true' then pulling is done by rebasing. +When `false' (the default) then pulling is done by merging. + +\(fn)" t nil) + +(autoload 'magit-cycle-remote\.pushDefault "magit" "\ +Cycle the repository-local value of `remote.pushDefault'. + +The Git variable `remote.pushDefault' specifies the remote that +local branches are usually pushed to. It can be overwritten +using the Git variable `branch..pushRemote'. + +\(fn)" t nil) + +(autoload 'magit-cycle-branch*autoSetupMerge "magit" "\ +Cycle the repository-local value of `branch.autoSetupMerge'. + +The Git variable `branch.autoSetupMerge' under what circumstances +creating a branch (named NAME) should result in the variables +`branch..merge' and `branch..remote' being set +according to the starting point used to create the branch. If +the starting point isn't a branch, then these variables are never +set. + +When `always' then the variables are set regardless of whether +the starting point is a local or a remote branch. + +When `true' (the default) then the variable are set when the +starting point is a remote branch, but not when it is a local +branch. + +When `false' then the variables are never set. + +\(fn)" t nil) + +(autoload 'magit-cycle-branch*autoSetupRebase "magit" "\ +Cycle the repository-local value of `branch.autoSetupRebase'. + +The Git variable `branch.autoSetupRebase' specifies whether +creating a branch (named NAME) should result in the variable +`branch..rebase' being set to `true'. + +When `always' then the variable is set regardless of whether the +starting point is a local or a remote branch. + +When `local' then the variable are set when the starting point +is a local branch, but not when it is a remote branch. + +When `remote' then the variable are set when the starting point +is a remote branch, but not when it is a local branch. + +When `never' (the default) then the variable is never set. + +\(fn)" t nil) + (autoload 'magit-merge-popup "magit" nil t) + +(autoload 'magit-merge "magit" "\ +Merge commit REV into the current branch; using default message. + +Unless there are conflicts or a prefix argument is used create a +merge commit using a generic commit message and without letting +the user inspect the result. With a prefix argument pretend the +merge failed to give the user the opportunity to inspect the +merge. + +\(git merge --no-edit|--no-commit [ARGS] REV) + +\(fn REV &optional ARGS NOCOMMIT)" t nil) + +(autoload 'magit-merge-editmsg "magit" "\ +Merge commit REV into the current branch; and edit message. +Perform the merge and prepare a commit message but let the user +edit it. + +\(git merge --edit [ARGS] rev) + +\(fn REV &optional ARGS)" t nil) + +(autoload 'magit-merge-nocommit "magit" "\ +Merge commit REV into the current branch; pretending it failed. +Pretend the merge failed to give the user the opportunity to +inspect the merge and change the commit message. + +\(git merge --no-commit [ARGS] rev) + +\(fn REV &optional ARGS)" t nil) + +(autoload 'magit-merge-preview "magit" "\ +Preview result of merging REV into the current branch. + +\(fn REV)" t nil) + +(autoload 'magit-merge-abort "magit" "\ +Abort the current merge operation. + +\(git merge --abort) + +\(fn)" t nil) + +(autoload 'magit-reset-index "magit" "\ +Reset the index to COMMIT. +Keep the head and working tree as-is, so if COMMIT refers to the +head this effectively unstages all changes. + +\(git reset COMMIT) + +\(fn COMMIT)" t nil) + +(autoload 'magit-reset "magit" "\ +Reset the head and index to COMMIT, but not the working tree. +With a prefix argument also reset the working tree. + +\(git reset --mixed|--hard COMMIT) + +\(fn COMMIT &optional HARD)" t nil) + +(autoload 'magit-reset-head "magit" "\ +Reset the head and index to COMMIT, but not the working tree. + +\(git reset --mixed COMMIT) + +\(fn COMMIT)" t nil) + +(autoload 'magit-reset-soft "magit" "\ +Reset the head to COMMIT, but not the index and working tree. + +\(git reset --soft REVISION) + +\(fn COMMIT)" t nil) + +(autoload 'magit-reset-hard "magit" "\ +Reset the head, index, and working tree to COMMIT. + +\(git reset --hard REVISION) + +\(fn COMMIT)" t nil) + (autoload 'magit-tag-popup "magit" nil t) + +(autoload 'magit-tag "magit" "\ +Create a new tag with the given NAME at REV. +With a prefix argument annotate the tag. + +\(git tag [--annotate] NAME REV) + +\(fn NAME REV &optional ARGS)" t nil) + +(autoload 'magit-tag-delete "magit" "\ +Delete one or more tags. +If the region marks multiple tags (and nothing else), then offer +to delete those, otherwise prompt for a single tag to be deleted, +defaulting to the tag at point. + +\(git tag -d TAGS) + +\(fn TAGS)" t nil) + (autoload 'magit-notes-popup "magit" nil t) + +(defvar global-magit-file-mode nil "\ +Non-nil if Global-Magit-File mode is enabled. +See the command `global-magit-file-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-file-mode'.") + +(custom-autoload 'global-magit-file-mode "magit" nil) + +(autoload 'global-magit-file-mode "magit" "\ +Toggle Magit-File mode in all buffers. +With prefix ARG, enable Global-Magit-File mode if ARG is positive; +otherwise, disable it. If called from Lisp, enable the mode if +ARG is omitted or nil. + +Magit-File mode is enabled in all buffers where +`magit-file-mode-turn-on' would do it. +See `magit-file-mode' for more information on Magit-File mode. + +\(fn &optional ARG)" t nil) + (autoload 'magit-dispatch-popup "magit" nil t) + (autoload 'magit-run-popup "magit" nil t) + +(autoload 'magit-git-command "magit" "\ +Execute a Git subcommand asynchronously, displaying the output. +With a prefix argument run Git in the root of the current +repository, otherwise in `default-directory'. + +\(fn ARGS DIRECTORY)" t nil) + +(autoload 'magit-git-command-topdir "magit" "\ +Execute a Git subcommand asynchronously, displaying the output. +Run Git in the top-level directory of the current repository. + +\(fn)" t nil) + +(autoload 'magit-shell-command "magit" "\ +Execute a shell command asynchronously, displaying the output. +With a prefix argument run the command in the root of the current +repository, otherwise in `default-directory'. + +\(fn ARGS DIRECTORY)" t nil) + +(autoload 'magit-shell-command-topdir "magit" "\ +Execute a shell command asynchronously, displaying the output. +Run the command in the top-level directory of the current repository. + +\(fn)" t nil) + +(autoload 'magit-version "magit" "\ +Return the version of Magit currently in use. +When called interactive also show the used versions of Magit, +Git, and Emacs in the echo area. + +\(fn)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-apply" "magit-apply.el" (22221 60708 +;;;;;; 751000 0)) +;;; Generated autoloads from magit-apply.el + +(autoload 'magit-stage-file "magit-apply" "\ +Stage all changes to FILE. +With a prefix argument or when there is no file at point ask for +the file to be staged. Otherwise stage the file at point without +requiring confirmation. + +\(fn FILE)" t nil) + +(autoload 'magit-stage-modified "magit-apply" "\ +Stage all changes to files modified in the worktree. +Stage all new content of tracked files and remove tracked files +that no longer exist in the working tree from the index also. +With a prefix argument also stage previously untracked (but not +ignored) files. +\('git add --update|--all .'). + +\(fn &optional ALL)" t nil) + +(autoload 'magit-unstage-file "magit-apply" "\ +Unstage all changes to FILE. +With a prefix argument or when there is no file at point ask for +the file to be unstaged. Otherwise unstage the file at point +without requiring confirmation. + +\(fn FILE)" t nil) + +(autoload 'magit-unstage-all "magit-apply" "\ +Remove all changes from the staging area. + +\(fn)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-autorevert" "magit-autorevert.el" (22221 +;;;;;; 60708 705000 0)) +;;; Generated autoloads from magit-autorevert.el + +(defvar magit-revert-buffers t) + +(defvar magit-auto-revert-mode (and magit-revert-buffers (not global-auto-revert-mode) (not noninteractive)) "\ +Non-nil if Magit-Auto-Revert mode is enabled. +See the command `magit-auto-revert-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-auto-revert-mode'.") + +(custom-autoload 'magit-auto-revert-mode "magit-autorevert" nil) + +(autoload 'magit-auto-revert-mode "magit-autorevert" "\ +Toggle Auto-Revert mode in all buffers. +With prefix ARG, enable Magit-Auto-Revert mode if ARG is positive; +otherwise, disable it. If called from Lisp, enable the mode if +ARG is omitted or nil. + +Auto-Revert mode is enabled in all buffers where +`magit-turn-on-auto-revert-mode-if-desired' would do it. +See `auto-revert-mode' for more information on Auto-Revert mode. + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-bisect" "magit-bisect.el" (22221 60708 +;;;;;; 745000 0)) +;;; Generated autoloads from magit-bisect.el + (autoload 'magit-bisect-popup "magit-bisect" nil t) + +(autoload 'magit-bisect-start "magit-bisect" "\ +Start a bisect session. + +Bisecting a bug means to find the commit that introduced it. +This command starts such a bisect session by asking for a know +good and a bad commit. To move the session forward use the +other actions from the bisect popup (\\\\[magit-bisect-popup]). + +\(fn BAD GOOD)" t nil) + +(autoload 'magit-bisect-reset "magit-bisect" "\ +After bisecting, cleanup bisection state and return to original `HEAD'. + +\(fn)" t nil) + +(autoload 'magit-bisect-good "magit-bisect" "\ +While bisecting, mark the current commit as good. +Use this after you have asserted that the commit does not contain +the bug in question. + +\(fn)" t nil) + +(autoload 'magit-bisect-bad "magit-bisect" "\ +While bisecting, mark the current commit as bad. +Use this after you have asserted that the commit does contain the +bug in question. + +\(fn)" t nil) + +(autoload 'magit-bisect-skip "magit-bisect" "\ +While bisecting, skip the current commit. +Use this if for some reason the current commit is not a good one +to test. This command lets Git choose a different one. + +\(fn)" t nil) + +(autoload 'magit-bisect-run "magit-bisect" "\ +Bisect automatically by running commands after each step. + +Unlike `git bisect run' this can be used before bisecting has +begun. In that case it behaves like `git bisect start; git +bisect run'. + +\(fn CMDLINE &optional BAD GOOD)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-blame" "magit-blame.el" (22221 60708 +;;;;;; 783000 0)) +;;; Generated autoloads from magit-blame.el + (autoload 'magit-blame-popup "magit-blame" nil t) + +(autoload 'magit-blame "magit-blame" "\ +Display edit history of FILE up to REVISION. + +Interactively blame the file being visited in the current buffer. +If the buffer visits a revision of that file, then blame up to +that revision, otherwise blame the file's full history, including +uncommitted changes. + +If Magit-Blame mode is already turned on then blame recursively, by +visiting REVISION:FILE (using `magit-find-file'), where revision +is the revision before the revision that added the lines at +point. + +ARGS is a list of additional arguments to pass to `git blame'; +only arguments available from `magit-blame-popup' should be used. + +\(fn REVISION FILE &optional ARGS)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-commit" "magit-commit.el" (22221 60708 +;;;;;; 708000 0)) +;;; Generated autoloads from magit-commit.el + +(autoload 'magit-commit "magit-commit" "\ +Create a new commit on HEAD. +With a prefix argument amend to the commit at HEAD instead. + +\(git commit [--amend] ARGS) + +\(fn &optional ARGS)" t nil) + +(autoload 'magit-commit-amend "magit-commit" "\ +Amend the last commit. + +\(git commit --amend ARGS) + +\(fn &optional ARGS)" t nil) + +(autoload 'magit-commit-extend "magit-commit" "\ +Amend the last commit, without editing the message. + +With a prefix argument keep the committer date, otherwise change +it. The option `magit-commit-extend-override-date' can be used +to inverse the meaning of the prefix argument. +\(git commit +--amend --no-edit) + +\(fn &optional ARGS OVERRIDE-DATE)" t nil) + +(autoload 'magit-commit-reword "magit-commit" "\ +Reword the last commit, ignoring staged changes. + +With a prefix argument keep the committer date, otherwise change +it. The option `magit-commit-reword-override-date' can be used +to inverse the meaning of the prefix argument. + +Non-interactively respect the optional OVERRIDE-DATE argument +and ignore the option. + +\(git commit --amend --only) + +\(fn &optional ARGS OVERRIDE-DATE)" t nil) + +(autoload 'magit-commit-fixup "magit-commit" "\ +Create a fixup commit. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'. + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-squash "magit-commit" "\ +Create a squash commit, without editing the squash message. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'. + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-augment "magit-commit" "\ +Create a squash commit, editing the squash message. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'. + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-instant-fixup "magit-commit" "\ +Create a fixup commit targeting COMMIT and instantly rebase. + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-instant-squash "magit-commit" "\ +Create a squash commit targeting COMMIT and instantly rebase. + +\(fn &optional COMMIT ARGS)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-diff" "magit-diff.el" (22221 60708 779000 +;;;;;; 0)) +;;; Generated autoloads from magit-diff.el + +(autoload 'magit-diff-dwim "magit-diff" "\ +Show changes for the thing at point. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-diff "magit-diff" "\ +Show differences between two commits. + +REV-OR-RANGE should be a range or a single revision. If it is a +revision, then show changes in the working tree relative to that +revision. If it is a range, but one side is omitted, then show +changes relative to `HEAD'. + +If the region is active, use the revisions on the first and last +line of the region as the two sides of the range. With a prefix +argument, instead of diffing the revisions, choose a revision to +view changes along, starting at the common ancestor of both +revisions (i.e., use a \"...\" range). + +\(fn REV-OR-RANGE &optional ARGS FILES)" t nil) + +(autoload 'magit-diff-working-tree "magit-diff" "\ +Show changes between the current working tree and the `HEAD' commit. +With a prefix argument show changes between the working tree and +a commit read from the minibuffer. + +\(fn &optional REV ARGS FILES)" t nil) + +(autoload 'magit-diff-staged "magit-diff" "\ +Show changes between the index and the `HEAD' commit. +With a prefix argument show changes between the index and +a commit read from the minibuffer. + +\(fn &optional REV ARGS FILES)" t nil) + +(autoload 'magit-diff-unstaged "magit-diff" "\ +Show changes between the working tree and the index. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-diff-while-committing "magit-diff" "\ +While committing, show the changes that are about to be committed. +While amending, invoking the command again toggles between +showing just the new changes or all the changes that will +be committed. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-diff-paths "magit-diff" "\ +Show changes between any two files on disk. + +\(fn A B)" t nil) + +(autoload 'magit-show-commit "magit-diff" "\ +Show the revision at point. +If there is no revision at point or with a prefix argument prompt +for a revision. + +\(fn REV &optional ARGS FILES MODULE)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-ediff" "magit-ediff.el" (22221 60708 +;;;;;; 770000 0)) +;;; Generated autoloads from magit-ediff.el + (autoload 'magit-ediff-popup "magit-ediff" nil t) + +(autoload 'magit-ediff-resolve "magit-ediff" "\ +Resolve outstanding conflicts in FILE using Ediff. +FILE has to be relative to the top directory of the repository. + +In the rare event that you want to manually resolve all +conflicts, including those already resolved by Git, use +`ediff-merge-revisions-with-ancestor'. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-stage "magit-ediff" "\ +Stage and unstage changes to FILE using Ediff. +FILE has to be relative to the top directory of the repository. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-compare "magit-ediff" "\ +Compare REVA:FILEA with REVB:FILEB using Ediff. + +FILEA and FILEB have to be relative to the top directory of the +repository. If REVA or REVB is nil then this stands for the +working tree state. + +If the region is active, use the revisions on the first and last +line of the region. With a prefix argument, instead of diffing +the revisions, choose a revision to view changes along, starting +at the common ancestor of both revisions (i.e., use a \"...\" +range). + +\(fn REVA REVB FILEA FILEB)" t nil) + +(autoload 'magit-ediff-dwim "magit-ediff" "\ +Compare, stage, or resolve using Ediff. +This command tries to guess what file, and what commit or range +the user wants to compare, stage, or resolve using Ediff. It +might only be able to guess either the file, or range or commit, +in which case the user is asked about the other. It might not +always guess right, in which case the appropriate `magit-ediff-*' +command has to be used explicitly. If it cannot read the user's +mind at all, then it asks the user for a command to run. + +\(fn)" t nil) + +(autoload 'magit-ediff-show-staged "magit-ediff" "\ +Show staged changes using Ediff. + +This only allows looking at the changes; to stage, unstage, +and discard changes using Ediff, use `magit-ediff-stage'. + +FILE must be relative to the top directory of the repository. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-show-unstaged "magit-ediff" "\ +Show unstaged changes using Ediff. + +This only allows looking at the changes; to stage, unstage, +and discard changes using Ediff, use `magit-ediff-stage'. + +FILE must be relative to the top directory of the repository. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-show-working-tree "magit-ediff" "\ +Show changes between HEAD and working tree using Ediff. +FILE must be relative to the top directory of the repository. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-show-commit "magit-ediff" "\ +Show changes introduced by COMMIT using Ediff. + +\(fn COMMIT)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-extras" "magit-extras.el" (22221 60708 +;;;;;; 711000 0)) +;;; Generated autoloads from magit-extras.el + +(autoload 'magit-run-git-gui "magit-extras" "\ +Run `git gui' for the current git repository. + +\(fn)" t nil) + +(autoload 'magit-run-git-gui-blame "magit-extras" "\ +Run `git gui blame' on the given FILENAME and COMMIT. +Interactively run it for the current file and the HEAD, with a +prefix or when the current file cannot be determined let the user +choose. When the current buffer is visiting FILENAME instruct +blame to center around the line point is on. + +\(fn COMMIT FILENAME &optional LINENUM)" t nil) + +(autoload 'magit-run-gitk "magit-extras" "\ +Run `gitk' in the current repository. + +\(fn)" t nil) + +(autoload 'magit-run-gitk-branches "magit-extras" "\ +Run `gitk --branches' in the current repository. + +\(fn)" t nil) + +(autoload 'magit-run-gitk-all "magit-extras" "\ +Run `gitk --all' in the current repository. + +\(fn)" t nil) + +(autoload 'magit-clean "magit-extras" "\ +Remove untracked files from the working tree. +With a prefix argument also remove ignored files, +with two prefix arguments remove ignored files only. + +\(git clean -f -d [-x|-X]) + +\(fn &optional ARG)" t nil) + +(autoload 'magit-gitignore "magit-extras" "\ +Instruct Git to ignore FILE-OR-PATTERN. +With a prefix argument only ignore locally. + +\(fn FILE-OR-PATTERN &optional LOCAL)" t nil) + +(autoload 'magit-gitignore-locally "magit-extras" "\ +Instruct Git to locally ignore FILE-OR-PATTERN. + +\(fn FILE-OR-PATTERN)" t nil) + +(autoload 'magit-add-change-log-entry "magit-extras" "\ +Find change log file and add date entry and item for current change. +This differs from `add-change-log-entry' (which see) in that +it acts on the current hunk in a Magit buffer instead of on +a position in a file-visiting buffer. + +\(fn &optional WHOAMI FILE-NAME OTHER-WINDOW)" t nil) + +(autoload 'magit-add-change-log-entry-other-window "magit-extras" "\ +Find change log file in other window and add entry and item. +This differs from `add-change-log-entry-other-window' (which see) +in that it acts on the current hunk in a Magit buffer instead of +on a position in a file-visiting buffer. + +\(fn &optional WHOAMI FILE-NAME)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-log" "magit-log.el" (22221 60708 775000 +;;;;;; 0)) +;;; Generated autoloads from magit-log.el + +(autoload 'magit-log-current "magit-log" "\ +Show log for the current branch. +When `HEAD' is detached or with a prefix argument show log for +one or more revs read from the minibuffer. + +\(fn REVS &optional ARGS FILES)" t nil) + +(autoload 'magit-log "magit-log" "\ +Show log for one or more revs read from the minibuffer. +The user can input any revision or revisions separated by a +space, or even ranges, but only branches and tags, and a +representation of the commit at point, are available as +completion candidates. + +\(fn REVS &optional ARGS FILES)" t nil) + +(autoload 'magit-log-head "magit-log" "\ +Show log for `HEAD'. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-log-branches "magit-log" "\ +Show log for all local branches and `HEAD'. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-log-all-branches "magit-log" "\ +Show log for all local and remote branches and `HEAD'. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-log-all "magit-log" "\ +Show log for all references and `HEAD'. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-log-buffer-file "magit-log" "\ +Show log for the blob or file visited in the current buffer. +With a prefix argument or when `--follow' is part of +`magit-log-arguments', then follow renames. + +\(fn &optional FOLLOW BEG END)" t nil) + +(autoload 'magit-reflog-current "magit-log" "\ +Display the reflog of the current branch. + +\(fn)" t nil) + +(autoload 'magit-reflog "magit-log" "\ +Display the reflog of a branch. + +\(fn REF)" t nil) + +(autoload 'magit-reflog-head "magit-log" "\ +Display the `HEAD' reflog. + +\(fn)" t nil) + +(autoload 'magit-cherry "magit-log" "\ +Show commits in a branch that are not merged in the upstream branch. + +\(fn HEAD UPSTREAM)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-remote" "magit-remote.el" (22221 60708 +;;;;;; 805000 0)) +;;; Generated autoloads from magit-remote.el + +(autoload 'magit-clone "magit-remote" "\ +Clone the REPOSITORY to DIRECTORY. +Then show the status buffer for the new repository. + +\(fn REPOSITORY DIRECTORY)" t nil) + (autoload 'magit-remote-popup "magit-remote" nil t) + +(autoload 'magit-remote-add "magit-remote" "\ +Add a remote named REMOTE and fetch it. + +\(fn REMOTE URL)" t nil) + +(autoload 'magit-remote-rename "magit-remote" "\ +Rename the remote named OLD to NEW. + +\(fn OLD NEW)" t nil) + +(autoload 'magit-remote-set-url "magit-remote" "\ +Change the url of the remote named REMOTE to URL. + +\(fn REMOTE URL)" t nil) + +(autoload 'magit-remote-remove "magit-remote" "\ +Delete the remote named REMOTE. + +\(fn REMOTE)" t nil) + +(autoload 'magit-remote-set-head "magit-remote" "\ +Set the local representation of REMOTE's default branch. +Query REMOTE and set the symbolic-ref refs/remotes//HEAD +accordingly. With a prefix argument query for the branch to be +used, which allows you to select an incorrect value if you fancy +doing that. + +\(fn REMOTE &optional BRANCH)" t nil) + +(autoload 'magit-remote-unset-head "magit-remote" "\ +Unset the local representation of REMOTE's default branch. +Delete the symbolic-ref \"refs/remotes//HEAD\". + +\(fn REMOTE)" t nil) + (autoload 'magit-fetch-popup "magit-remote" nil t) + +(autoload 'magit-fetch-from-pushremote "magit-remote" "\ +Fetch from the push-remote of the current branch. + +\(fn ARGS)" t nil) + +(autoload 'magit-fetch-from-upstream "magit-remote" "\ +Fetch from the upstream repository of the current branch. + +\(fn ARGS)" t nil) + +(autoload 'magit-fetch "magit-remote" "\ +Fetch from another repository. + +\(fn REMOTE ARGS)" t nil) + +(autoload 'magit-fetch-all "magit-remote" "\ +Fetch from all remotes. + +\(fn ARGS)" t nil) + +(autoload 'magit-fetch-all-prune "magit-remote" "\ +Fetch from all remotes, and prune. +Prune remote tracking branches for branches that have been +removed on the respective remote. + +\(fn)" t nil) + +(autoload 'magit-fetch-all-no-prune "magit-remote" "\ +Fetch from all remotes. + +\(fn)" t nil) + (autoload 'magit-pull-popup "magit-remote" nil t) + (autoload 'magit-pull-and-fetch-popup "magit-remote" nil t) + +(autoload 'magit-pull-from-pushremote "magit-remote" "\ +Pull from the push-remote of the current branch. + +\(fn ARGS)" t nil) + +(autoload 'magit-pull-from-upstream "magit-remote" "\ +Pull from the upstream of the current branch. + +\(fn ARGS)" t nil) + +(autoload 'magit-pull "magit-remote" "\ +Pull from a branch read in the minibuffer. + +\(fn SOURCE ARGS)" t nil) + (autoload 'magit-push-popup "magit-remote" nil t) + +(autoload 'magit-push-current-to-pushremote "magit-remote" "\ +Push the current branch to `branch..pushRemote'. +If that variable is unset, then push to `remote.pushDefault'. + +When `magit-push-current-set-remote-if-missing' is non-nil and +the push-remote is not configured, then read the push-remote from +the user, set it, and then push to it. With a prefix argument +the push-remote can be changed before pushed to it. + +\(fn ARGS &optional PUSH-REMOTE)" t nil) + +(autoload 'magit-push-current-to-upstream "magit-remote" "\ +Push the current branch to its upstream branch. + +When `magit-push-current-set-remote-if-missing' is non-nil and +the upstream is not configured, then read the upstream from the +user, set it, and then push to it. With a prefix argument the +upstream can be changed before pushed to it. + +\(fn ARGS &optional UPSTREAM)" t nil) + +(autoload 'magit-push-current "magit-remote" "\ +Push the current branch to a branch read in the minibuffer. + +\(fn TARGET ARGS)" t nil) + +(autoload 'magit-push "magit-remote" "\ +Push an arbitrary branch or commit somewhere. +Both the source and the target are read in the minibuffer. + +\(fn SOURCE TARGET ARGS)" t nil) + +(autoload 'magit-push-refspecs "magit-remote" "\ +Push one or multiple REFSPECS to a REMOTE. +Both the REMOTE and the REFSPECS are read in the minibuffer. To +use multiple REFSPECS, separate them with commas. Completion is +only available for the part before the colon, or when no colon +is used. + +\(fn REMOTE REFSPECS ARGS)" t nil) + +(autoload 'magit-push-matching "magit-remote" "\ +Push all matching branches to another repository. +If multiple remotes exist, then read one from the user. +If just one exists, use that without requiring confirmation. + +\(fn REMOTE &optional ARGS)" t nil) + +(autoload 'magit-push-tags "magit-remote" "\ +Push all tags to another repository. +If only one remote exists, then push to that. Otherwise prompt +for a remote, offering the remote configured for the current +branch as default. + +\(fn REMOTE &optional ARGS)" t nil) + +(autoload 'magit-push-tag "magit-remote" "\ +Push a tag to another repository. + +\(fn TAG REMOTE &optional ARGS)" t nil) + +(autoload 'magit-push-implicitly "magit-remote" "\ +Push somewhere without using an explicit refspec. + +This command simply runs \"git push -v [ARGS]\". ARGS are the +arguments specified in the popup buffer. No explicit refspec +arguments are used. Instead the behavior depends on at least +these Git variables: `push.default', `remote.pushDefault', +`branch..pushRemote', `branch..remote', +`branch..merge', and `remote..push'. + +To add this command to the push popup add this to your init file: + + (with-eval-after-load \\='magit-remote + (magit-define-popup-action \\='magit-push-popup ?P + 'magit-push-implicitly--desc + 'magit-push-implicitly ?p t)) + +The function `magit-push-implicitly--desc' attempts to predict +what this command will do, the value it returns is displayed in +the popup buffer. + +\(fn ARGS)" t nil) + +(autoload 'magit-push-to-remote "magit-remote" "\ +Push to REMOTE without using an explicit refspec. +The REMOTE is read in the minibuffer. + +This command simply runs \"git push -v [ARGS] REMOTE\". ARGS +are the arguments specified in the popup buffer. No refspec +arguments are used. Instead the behavior depends on at least +these Git variables: `push.default', `remote.pushDefault', +`branch..pushRemote', `branch..remote', +`branch..merge', and `remote..push'. + +To add this command to the push popup add this to your init file: + + (with-eval-after-load \\='magit-remote + (magit-define-popup-action \\='magit-push-popup ?r + 'magit-push-to-remote--desc + 'magit-push-to-remote ?p t)) + +\(fn REMOTE ARGS)" t nil) + (autoload 'magit-patch-popup "magit-remote" nil t) + +(autoload 'magit-format-patch "magit-remote" "\ +Create patches for the commits in RANGE. +When a single commit is given for RANGE, create a patch for the +changes introduced by that commit (unlike 'git format-patch' +which creates patches for all commits that are reachable from +HEAD but not from the specified commit). + +\(fn RANGE ARGS)" t nil) + +(autoload 'magit-request-pull "magit-remote" "\ +Request upstream to pull from you public repository. + +URL is the url of your publically accessible repository. +START is a commit that already is in the upstream repository. +END is the last commit, usually a branch name, which upstream +is asked to pull. START has to be reachable from that commit. + +\(fn URL START END)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-sequence" "magit-sequence.el" (22221 +;;;;;; 60708 797000 0)) +;;; Generated autoloads from magit-sequence.el + +(autoload 'magit-sequencer-continue "magit-sequence" "\ +Resume the current cherry-pick or revert sequence. + +\(fn)" t nil) + +(autoload 'magit-sequencer-skip "magit-sequence" "\ +Skip the stopped at commit during a cherry-pick or revert sequence. + +\(fn)" t nil) + +(autoload 'magit-sequencer-abort "magit-sequence" "\ +Abort the current cherry-pick or revert sequence. +This discards all changes made since the sequence started. + +\(fn)" t nil) + (autoload 'magit-cherry-pick-popup "magit-sequence" nil t) + +(autoload 'magit-cherry-pick "magit-sequence" "\ +Cherry-pick COMMIT. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then pick all of them, +without prompting. + +\(fn COMMIT &optional ARGS)" t nil) + +(autoload 'magit-cherry-apply "magit-sequence" "\ +Apply the changes in COMMIT but do not commit them. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then apply all of them, +without prompting. + +\(fn COMMIT &optional ARGS)" t nil) + (autoload 'magit-revert-popup "magit-sequence" nil t) + +(autoload 'magit-revert "magit-sequence" "\ +Revert COMMIT by creating a new commit. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then revert all of them, +without prompting. + +\(fn COMMIT &optional ARGS)" t nil) + +(autoload 'magit-revert-no-commit "magit-sequence" "\ +Revert COMMIT by applying it in reverse to the worktree. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then revert all of them, +without prompting. + +\(fn COMMIT &optional ARGS)" t nil) + (autoload 'magit-am-popup "magit-sequence" nil t) + +(autoload 'magit-am-apply-patches "magit-sequence" "\ +Apply the patches FILES. + +\(fn &optional FILES ARGS)" t nil) + +(autoload 'magit-am-apply-maildir "magit-sequence" "\ +Apply the patches from MAILDIR. + +\(fn &optional MAILDIR ARGS)" t nil) + +(autoload 'magit-am-continue "magit-sequence" "\ +Resume the current patch applying sequence. + +\(fn)" t nil) + +(autoload 'magit-am-skip "magit-sequence" "\ +Skip the stopped at patch during a patch applying sequence. + +\(fn)" t nil) + +(autoload 'magit-am-abort "magit-sequence" "\ +Abort the current patch applying sequence. +This discards all changes made since the sequence started. + +\(fn)" t nil) + (autoload 'magit-rebase-popup "magit-sequence" nil t) + +(autoload 'magit-rebase-onto-pushremote "magit-sequence" "\ +Rebase the current branch onto `branch..pushRemote'. +If that variable is unset, then rebase onto `remote.pushDefault'. + +\(fn ARGS)" t nil) + +(autoload 'magit-rebase-onto-upstream "magit-sequence" "\ +Rebase the current branch onto its upstream branch. + +\(fn ARGS)" t nil) + +(autoload 'magit-rebase "magit-sequence" "\ +Rebase the current branch onto a branch read in the minibuffer. +All commits that are reachable from head but not from the +selected branch TARGET are being rebased. + +\(fn TARGET ARGS)" t nil) + +(autoload 'magit-rebase-subset "magit-sequence" "\ +Rebase a subset of the current branches history onto a new base. +Rebase commits from START to `HEAD' onto NEWBASE. +START has to be selected from a list of recent commits. + +\(fn NEWBASE START ARGS)" t nil) + +(autoload 'magit-rebase-interactive "magit-sequence" "\ +Start an interactive rebase sequence. + +\(fn COMMIT ARGS)" t nil) + +(autoload 'magit-rebase-autosquash "magit-sequence" "\ +Combine squash and fixup commits with their intended targets. + +\(fn ARGS)" t nil) + +(autoload 'magit-rebase-edit-commit "magit-sequence" "\ +Edit a single older commit using rebase. + +\(fn COMMIT ARGS)" t nil) + +(autoload 'magit-rebase-reword-commit "magit-sequence" "\ +Reword a single older commit using rebase. + +\(fn COMMIT ARGS)" t nil) + +(autoload 'magit-rebase-continue "magit-sequence" "\ +Restart the current rebasing operation. + +\(fn)" t nil) + +(autoload 'magit-rebase-skip "magit-sequence" "\ +Skip the current commit and restart the current rebase operation. + +\(fn)" t nil) + +(autoload 'magit-rebase-edit "magit-sequence" "\ +Edit the todo list of the current rebase operation. + +\(fn)" t nil) + +(autoload 'magit-rebase-abort "magit-sequence" "\ +Abort the current rebase operation, restoring the original branch. + +\(fn)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-stash" "magit-stash.el" (22221 60708 +;;;;;; 764000 0)) +;;; Generated autoloads from magit-stash.el + (autoload 'magit-stash-popup "magit-stash" nil t) + +(autoload 'magit-stash "magit-stash" "\ +Create a stash of the index and working tree. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn MESSAGE &optional INCLUDE-UNTRACKED)" t nil) + +(autoload 'magit-stash-index "magit-stash" "\ +Create a stash of the index only. +Unstaged and untracked changes are not stashed. The stashed +changes are applied in reverse to both the index and the +worktree. This command can fail when the worktree is not clean. +Applying the resulting stash has the inverse effect. + +\(fn MESSAGE)" t nil) + +(autoload 'magit-stash-worktree "magit-stash" "\ +Create a stash of the working tree only. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn MESSAGE &optional INCLUDE-UNTRACKED)" t nil) + +(autoload 'magit-stash-keep-index "magit-stash" "\ +Create a stash of the index and working tree, keeping index intact. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn MESSAGE &optional INCLUDE-UNTRACKED)" t nil) + +(autoload 'magit-snapshot "magit-stash" "\ +Create a snapshot of the index and working tree. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn &optional INCLUDE-UNTRACKED)" t nil) + +(autoload 'magit-snapshot-index "magit-stash" "\ +Create a snapshot of the index only. +Unstaged and untracked changes are not stashed. + +\(fn)" t nil) + +(autoload 'magit-snapshot-worktree "magit-stash" "\ +Create a snapshot of the working tree only. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn &optional INCLUDE-UNTRACKED)" t nil) + +(autoload 'magit-stash-apply "magit-stash" "\ +Apply a stash to the working tree. +Try to preserve the stash index. If that fails because there +are staged changes, apply without preserving the stash index. + +\(fn STASH)" t nil) + +(autoload 'magit-stash-drop "magit-stash" "\ +Remove a stash from the stash list. +When the region is active offer to drop all contained stashes. + +\(fn STASH)" t nil) + +(autoload 'magit-stash-clear "magit-stash" "\ +Remove all stashes saved in REF's reflog by deleting REF. + +\(fn REF)" t nil) + +(autoload 'magit-stash-branch "magit-stash" "\ +Create and checkout a new BRANCH from STASH. + +\(fn STASH BRANCH)" t nil) + +(autoload 'magit-stash-format-patch "magit-stash" "\ +Create a patch from STASH + +\(fn STASH)" t nil) + +(autoload 'magit-stash-list "magit-stash" "\ +List all stashes in a buffer. + +\(fn)" t nil) + +(autoload 'magit-stash-show "magit-stash" "\ +Show all diffs of a stash in a buffer. + +\(fn STASH &optional ARGS FILES)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-submodule" "magit-submodule.el" (22221 +;;;;;; 60708 818000 0)) +;;; Generated autoloads from magit-submodule.el + (autoload 'magit-submodule-popup "magit-submodule" nil t) + +(autoload 'magit-submodule-add "magit-submodule" "\ +Add the repository at URL as a submodule. +Optional PATH is the path to the submodule relative to the root +of the superproject. If it is nil then the path is determined +based on URL. + +\(fn URL &optional PATH)" t nil) + +(autoload 'magit-submodule-setup "magit-submodule" "\ +Clone and register missing submodules and checkout appropriate commits. + +\(fn)" t nil) + +(autoload 'magit-submodule-init "magit-submodule" "\ +Register submodules listed in \".gitmodules\" into \".git/config\". + +\(fn)" t nil) + +(autoload 'magit-submodule-update "magit-submodule" "\ +Clone missing submodules and checkout appropriate commits. +With a prefix argument also register submodules in \".git/config\". + +\(fn &optional INIT)" t nil) + +(autoload 'magit-submodule-sync "magit-submodule" "\ +Update each submodule's remote URL according to \".gitmodules\". + +\(fn)" t nil) + +(autoload 'magit-submodule-fetch "magit-submodule" "\ +Fetch all submodules. +With a prefix argument fetch all remotes. + +\(fn &optional ALL)" t nil) + +(autoload 'magit-submodule-deinit "magit-submodule" "\ +Unregister the submodule at PATH. + +\(fn PATH)" t nil) + +(autoload 'magit-insert-submodule-commits "magit-submodule" "\ +For internal use, don't add to a hook. + +\(fn SECTION RANGE)" nil nil) + +(autoload 'magit-insert-unpulled-module-commits "magit-submodule" "\ +Insert sections for all submodules with unpulled commits. +These sections can be expanded to show the respective commits. + +\(fn)" nil nil) + +(autoload 'magit-insert-unpushed-module-commits "magit-submodule" "\ +Insert sections for all submodules with unpushed commits. +These sections can be expanded to show the respective commits. + +\(fn)" nil nil) + +;;;*** + +;;;### (autoloads nil "magit-wip" "magit-wip.el" (22221 60708 721000 +;;;;;; 0)) +;;; Generated autoloads from magit-wip.el + +(defvar magit-wip-after-save-mode nil "\ +Non-nil if Magit-Wip-After-Save mode is enabled. +See the command `magit-wip-after-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 `magit-wip-after-save-mode'.") + +(custom-autoload 'magit-wip-after-save-mode "magit-wip" nil) + +(autoload 'magit-wip-after-save-mode "magit-wip" "\ +Toggle Magit-Wip-After-Save-Local mode in all buffers. +With prefix ARG, enable Magit-Wip-After-Save mode if ARG is positive; +otherwise, disable it. If called from Lisp, enable the mode if +ARG is omitted or nil. + +Magit-Wip-After-Save-Local mode is enabled in all buffers where +`magit-wip-after-save-local-mode-turn-on' would do it. +See `magit-wip-after-save-local-mode' for more information on Magit-Wip-After-Save-Local mode. + +\(fn &optional ARG)" t nil) + +(defvar magit-wip-after-apply-mode nil "\ +Non-nil if Magit-Wip-After-Apply mode is enabled. +See the command `magit-wip-after-apply-mode' for a description of this minor mode.") + +(custom-autoload 'magit-wip-after-apply-mode "magit-wip" nil) + +(autoload 'magit-wip-after-apply-mode "magit-wip" "\ +Commit to work-in-progress refs. + +After applying a change using any \"apply variant\" +command (apply, stage, unstage, discard, and reverse) commit the +affected files to the current wip refs. For each branch there +may be two wip refs; one contains snapshots of the files as found +in the worktree and the other contains snapshots of the entries +in the index. + +\(fn &optional ARG)" t nil) + +(defvar magit-wip-before-change-mode nil "\ +Non-nil if Magit-Wip-Before-Change mode is enabled. +See the command `magit-wip-before-change-mode' for a description of this minor mode.") + +(custom-autoload 'magit-wip-before-change-mode "magit-wip" nil) + +(autoload 'magit-wip-before-change-mode "magit-wip" "\ +Commit to work-in-progress refs before certain destructive changes. + +Before invoking a revert command or an \"apply variant\" +command (apply, stage, unstage, discard, and reverse) commit the +affected tracked files to the current wip refs. For each branch +there may be two wip refs; one contains snapshots of the files +as found in the worktree and the other contains snapshots of the +entries in the index. + +Only changes to files which could potentially be affected by the +command which is about to be called are committed. + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil nil ("magit-core.el" "magit-git.el" "magit-mode.el" +;;;;;; "magit-pkg.el" "magit-process.el" "magit-section.el" "magit-utils.el") +;;;;;; (22221 60708 822158 468000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; magit-autoloads.el ends here diff --git a/elpa/magit-20160223.828/magit-autorevert.el b/elpa/magit-20160223.828/magit-autorevert.el new file mode 100644 index 0000000..5c4b16f --- /dev/null +++ b/elpa/magit-20160223.828/magit-autorevert.el @@ -0,0 +1,260 @@ +;;; magit-autorevert.el --- revert buffers when files in repository change -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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. + +;;; Code: + +(require 'cl-lib) +(require 'dash) + +(require 'magit-git) + +(require 'autorevert) + +(defgroup magit-auto-revert nil + "Revert buffers when files in repository change." + :group 'auto-revert + :group 'magit-extensions) + +(defcustom auto-revert-buffer-list-filter nil + "Filter that determines which buffers `auto-revert-buffers' reverts. + +This option is provided by `magit', which also redefines +`auto-revert-buffers' to respect it. Magit users who do not turn +on the local mode `auto-revert-mode' themselves, are best served +by setting the value to `magit-auto-revert-repository-buffers-p'. + +However the default is nil, to not disturb users who do use the +local mode directly. If you experience delays when running Magit +commands, then you should consider using one of the predicates +provided by Magit - especially if you also use Tramp. + +Users who do turn on `auto-revert-mode' in buffers in which Magit +doesn't do that for them, should likely not use any filter. +Users who turn on `global-auto-revert-mode', do not have to worry +about this option, because it is disregarded if the global mode +is enabled." + :package-version '(magit . "2.4.2") + :group 'auto-revert + :group 'magit-auto-revert + :type '(radio (const :tag "no filter" nil) + (function-item magit-auto-revert-buffer-p) + (function-item magit-auto-revert-repository-buffer-p) + function)) + +(defcustom magit-auto-revert-tracked-only t + "Whether `magit-auto-revert-mode' only reverts tracked files." + :package-version '(magit . "2.4.0") + :group 'magit-auto-revert + :type 'boolean + :set (lambda (var val) + (set var val) + (when (and (bound-and-true-p magit-auto-revert-mode) + (featurep 'magit-autorevert)) + (magit-auto-revert-mode -1) + (magit-auto-revert-mode)))) + +(defcustom magit-auto-revert-immediately t + "Whether Magit reverts buffers immediately. + +If this is non-nil and either `global-auto-revert-mode' or +`magit-auto-revert-mode' is enabled, then Magit immediately +reverts buffers by explicitly calling `auto-revert-buffers' +after running git for side-effects. + +If `auto-revert-use-notify' is non-nil (and file notifications +are actually supported), then `magit-auto-revert-immediately' +does not have to be non-nil, because the reverts happen +immediately anyway. + +If `magit-auto-revert-immediately' and `auto-revert-use-notify' +are both nil, then reverts happen after `auto-revert-interval' +seconds of user inactivity. That is not desirable." + :package-version '(magit . "2.4.0") + :group 'magit-auto-revert + :type 'boolean) + +(defun magit-turn-on-auto-revert-mode-if-desired (&optional file) + (if file + (--when-let (find-buffer-visiting file) + (with-current-buffer it + (magit-turn-on-auto-revert-mode-if-desired))) + (when (and buffer-file-name + (file-readable-p buffer-file-name) + (magit-toplevel) + (or (not magit-auto-revert-tracked-only) + (magit-file-tracked-p buffer-file-name))) + (auto-revert-mode)))) + +;;;###autoload +(defvar magit-revert-buffers t) +(make-obsolete-variable 'magit-revert-buffers 'magit-auto-revert-mode + "Magit 2.4.0") + +;;;###autoload +(define-globalized-minor-mode magit-auto-revert-mode auto-revert-mode + magit-turn-on-auto-revert-mode-if-desired + :package-version '(magit . "2.4.0") + :group 'magit + :group 'magit-auto-revert + ;; When `global-auto-revert-mode' is enabled, then this mode is + ;; redundant. When `magit-revert-buffers' is nil, then the user has + ;; opted out of the automatic reverts while the old implementation + ;; was still in use. In all other cases enable the mode because if + ;; buffers are not automatically reverted that would make many very + ;; common tasks much more cumbersome. + :init-value (and magit-revert-buffers + (not global-auto-revert-mode) + (not noninteractive))) +;; - Unfortunately `:init-value t' only sets the value of the mode +;; variable but does not cause the mode function to be called. +;; - I don't think it works like this on purpose, but since one usually +;; should not enable global modes by default, it is understandable. +;; - If the user has set the variable `magit-auto-revert-mode' to nil +;; after loading magit (instead of doing so before loading magit or +;; by using the function), then we should still respect that setting. +;; - If the user has set the obsolete variable `magit-revert-buffers' +;; to nil before or after loading magit, then we should still respect +;; that setting. +;; - If the user sets one of these variables after loading magit and +;; after `after-init-hook' has run, then that won't have an effect +;; and there is nothing we can do about it. +(defun magit-auto-revert-mode--init-kludge () + "This is an internal kludge to be used on `after-init-hook'. +Do not use this function elsewhere, and don't remove it from +the `after-init-hook'. For more information see the comments +and code surrounding the definition of this function." + ;; `magit-revert-buffers' may have been set to nil before the alias + ;; had been established, so consult the value of both variables. + (if (and magit-auto-revert-mode magit-revert-buffers) + (let ((start (current-time))) + (message "Turning on magit-auto-revert-mode...") + (magit-auto-revert-mode 1) + (message + "Turning on magit-auto-revert-mode...done%s" + (let ((elapsed (float-time (time-subtract (current-time) start)))) + (if (> elapsed 0.2) + (format " (%.3fs, %s buffers checked)" elapsed + (length (buffer-list))) + "")))) + (magit-auto-revert-mode -1))) +(if after-init-time + ;; Since `after-init-hook' has already been + ;; run, turn the mode on or off right now. + (magit-auto-revert-mode--init-kludge) + ;; By the time the init file has been fully loaded the + ;; values of the relevant variables might have changed. + (add-hook 'after-init-hook #'magit-auto-revert-mode--init-kludge t)) + +(put 'magit-auto-revert-mode 'function-documentation + "Toggle Magit Auto Revert mode. +With a prefix argument ARG, enable Magit Auto Revert mode if ARG +is positive, and disable it otherwise. If called from Lisp, +enable the mode if ARG is omitted or nil. + +Magit Auto Revert mode is a global minor mode that reverts +buffers associated with a file that is located inside a Git +repository when the file changes on disk. Use `auto-revert-mode' +to revert a particular buffer. Or use `global-auto-revert-mode' +to revert all file-visiting buffers, not just those that visit +a file located inside a Git repository. + +This global mode works by turning on the buffer-local mode +`auto-revert-mode' at the time a buffer is first created. The +local mode is turned on if the visited file is being tracked in +a Git repository at the time when the buffer is created. + +If `magit-auto-revert-tracked-only' is non-nil (the default), +then only tracked files are reverted. But if you stage a +previously untracked file using `magit-stage', then this mode +notices that. + +Unlike `global-auto-revert-mode', this mode never reverts any +buffers that are not visiting files. + +The behavior of this mode can be customized using the options +in the `autorevert' and `magit-autorevert' groups. + +This function calls the hook `magit-auto-revert-mode-hook'.") + +(defun magit-auto-revert-buffers () + (when (and magit-auto-revert-immediately + (or global-auto-revert-mode + (and magit-auto-revert-mode auto-revert-buffer-list))) + (let ((auto-revert-buffer-list-filter + (or auto-revert-buffer-list-filter + 'magit-auto-revert-repository-buffer-p))) + (auto-revert-buffers)))) + +(defvar magit-auto-revert-toplevel nil) + +(when (< emacs-major-version 25) + (defvar auto-revert-buffers-counter 1 + "Incremented each time `auto-revert-buffers' is called")) + +(defun magit-auto-revert-buffer-p (buffer) + "Return t if BUFFER visits a file inside the current repository. +The current repository is the one in which `default-directory' is +located. If there is no current repository, then return t for +any BUFFER." + (magit-auto-revert-repository-buffer-p buffer t)) + +(defun magit-auto-revert-repository-buffer-p (buffer &optional fallback) + "Return t if BUFFER visits a file inside the current repository. +The current repository is the one in which `default-directory' is +located. If there is no current repository, then return FALLBACK +\(which defaults to nil) for any BUFFER." + ;; Call `magit-toplevel' just once per cycle. + (unless (and magit-auto-revert-toplevel + (= (cdr magit-auto-revert-toplevel) + auto-revert-buffers-counter)) + (setq magit-auto-revert-toplevel + (cons (or (magit-toplevel) 'no-repo) + auto-revert-buffers-counter))) + (let ((top (car magit-auto-revert-toplevel))) + (if (eq top 'no-repo) + fallback + (let ((dir (with-current-buffer buffer default-directory))) + (and (equal (file-remote-p dir) + (file-remote-p top)) + ;; ^ `tramp-handle-file-in-directory-p' lacks this optimization. + (file-in-directory-p dir top)))))) + +(defun auto-revert-buffers--buffer-list-filter () + (when (< emacs-major-version 25) + (cl-incf auto-revert-buffers-counter)) + (when auto-revert-buffer-list-filter + (setq auto-revert-buffer-list + (--filter auto-revert-buffer-list-filter + auto-revert-buffer-list)))) + +(advice-add 'auto-revert-buffers :before + 'auto-revert-buffers--buffer-list-filter) + +(custom-add-to-group 'magit 'auto-revert-check-vc-info 'custom-variable) + +;;; magit-autorevert.el ends soon +(provide 'magit-autorevert) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-autorevert.el ends here diff --git a/elpa/magit-20160223.828/magit-bisect.el b/elpa/magit-20160223.828/magit-bisect.el new file mode 100644 index 0000000..3956a97 --- /dev/null +++ b/elpa/magit-20160223.828/magit-bisect.el @@ -0,0 +1,200 @@ +;;; magit-bisect.el --- bisect support for Magit -*- lexical-binding: t -*- + +;; Copyright (C) 2011-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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: + +;; Use a binary search to find the commit that introduced a bug. + +;;; Code: + +(require 'magit) + +(defface magit-bisect-good + '((t :foreground "DarkOliveGreen")) + "Face for good bisect revisions." + :group 'magit-faces) + +(defface magit-bisect-skip + '((t :foreground "DarkGoldenrod")) + "Face for skipped bisect revisions." + :group 'magit-faces) + +(defface magit-bisect-bad + '((t :foreground "IndianRed4")) + "Face for bad bisect revisions." + :group 'magit-faces) + +;;;###autoload (autoload 'magit-bisect-popup "magit-bisect" nil t) +(magit-define-popup magit-bisect-popup + "Popup console for bisect commands." + 'magit-commands + :man-page "git-bisect" + :actions '((?B "Start" magit-bisect-start) + (?s "Start script" magit-bisect-run)) + :sequence-actions '((?b "Bad" magit-bisect-bad) + (?g "Good" magit-bisect-good) + (?k "Skip" magit-bisect-skip) + (?r "Reset" magit-bisect-reset) + (?s "Run script" magit-bisect-run)) + :sequence-predicate 'magit-bisect-in-progress-p) + +;;;###autoload +(defun magit-bisect-start (bad good) + "Start a bisect session. + +Bisecting a bug means to find the commit that introduced it. +This command starts such a bisect session by asking for a know +good and a bad commit. To move the session forward use the +other actions from the bisect popup (\ +\\\\[magit-bisect-popup])." + (interactive (if (magit-bisect-in-progress-p) + (user-error "Already bisecting") + (magit-bisect-start-read-args))) + (magit-git-bisect "start" (list bad good) t)) + +(defun magit-bisect-start-read-args () + (let ((b (magit-read-branch-or-commit "Start bisect with bad revision"))) + (list b (magit-read-other-branch-or-commit "Good revision" b)))) + +;;;###autoload +(defun magit-bisect-reset () + "After bisecting, cleanup bisection state and return to original `HEAD'." + (interactive) + (when (magit-confirm 'reset-bisect) + (magit-run-git "bisect" "reset") + (ignore-errors (delete-file (magit-git-dir "BISECT_CMD_OUTPUT"))))) + +;;;###autoload +(defun magit-bisect-good () + "While bisecting, mark the current commit as good. +Use this after you have asserted that the commit does not contain +the bug in question." + (interactive) + (magit-git-bisect "good")) + +;;;###autoload +(defun magit-bisect-bad () + "While bisecting, mark the current commit as bad. +Use this after you have asserted that the commit does contain the +bug in question." + (interactive) + (magit-git-bisect "bad")) + +;;;###autoload +(defun magit-bisect-skip () + "While bisecting, skip the current commit. +Use this if for some reason the current commit is not a good one +to test. This command lets Git choose a different one." + (interactive) + (magit-git-bisect "skip")) + +;;;###autoload +(defun magit-bisect-run (cmdline &optional bad good) + "Bisect automatically by running commands after each step. + +Unlike `git bisect run' this can be used before bisecting has +begun. In that case it behaves like `git bisect start; git +bisect run'." + (interactive (let ((args (and (not (magit-bisect-in-progress-p)) + (magit-bisect-start-read-args)))) + (cons (read-shell-command "Bisect shell command: ") args))) + (when (and bad good) + (magit-bisect-start bad good)) + (magit-git-bisect "run" (list cmdline))) + +(defun magit-git-bisect (subcommand &optional args no-assert) + (unless (or no-assert (magit-bisect-in-progress-p)) + (user-error "Not bisecting")) + (magit-with-toplevel + (magit-run-git-with-logfile + (magit-git-dir "BISECT_CMD_OUTPUT") "bisect" subcommand args))) + +(defun magit-bisect-in-progress-p () + (file-exists-p (magit-git-dir "BISECT_LOG"))) + +(defun magit-insert-bisect-output () + "While bisecting, insert section with output from `git bisect'." + (when (magit-bisect-in-progress-p) + (let ((lines + (or (magit-file-lines (magit-git-dir "BISECT_CMD_OUTPUT")) + (list "Bisecting: (no saved bisect output)" + "It appears you have invoked `git bisect' from a shell." + "There is nothing wrong with that, we just cannot display" + "anything useful here. Consult the shell output instead."))) + (done-re "^[a-z0-9]\\{40\\} is the first bad commit$")) + (magit-insert-section (bisect-output t) + (magit-insert-heading + (propertize (or (and (string-match done-re (car lines)) (pop lines)) + (--first (string-match done-re it) lines) + (pop lines)) + 'face 'magit-section-heading)) + (dolist (line lines) + (insert line "\n")))) + (insert "\n"))) + +(defun magit-insert-bisect-rest () + "While bisecting, insert section visualizing the bisect state." + (when (magit-bisect-in-progress-p) + (magit-insert-section (bisect-view) + (magit-insert-heading "Bisect Rest:") + (magit-git-wash (apply-partially 'magit-log-wash-log 'bisect-vis) + "bisect" "visualize" "git" "log" + "--format=%h%d %s" "--decorate=full")))) + +(defun magit-insert-bisect-log () + "While bisecting, insert section logging bisect progress." + (when (magit-bisect-in-progress-p) + (magit-insert-section (bisect-log) + (magit-insert-heading "Bisect Log:") + (magit-git-wash #'magit-wash-bisect-log "bisect" "log") + (insert ?\n)))) + +(defun magit-wash-bisect-log (_args) + (let (beg) + (while (progn (setq beg (point-marker)) + (re-search-forward "^\\(git bisect [^\n]+\n\\)" nil t)) + (magit-bind-match-strings (heading) nil + (magit-delete-match) + (save-restriction + (narrow-to-region beg (point)) + (goto-char (point-min)) + (magit-insert-section (bisect-log heading t) + (insert (propertize heading 'face 'magit-section-secondary-heading)) + (magit-insert-heading) + (magit-wash-sequence + (apply-partially 'magit-log-wash-rev 'bisect-log + (magit-abbrev-length))) + (insert ?\n))))) + (when (re-search-forward + "# first bad commit: \\[\\([a-z0-9]\\{40\\}\\)\\] [^\n]+\n" nil t) + (magit-bind-match-strings (hash) nil + (magit-delete-match) + (magit-insert-section (bisect-log) + (insert hash " is the first bad commit\n")))))) + +;;; magit-bisect.el ends soon +(provide 'magit-bisect) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-bisect.el ends here diff --git a/elpa/magit-20160223.828/magit-blame.el b/elpa/magit-20160223.828/magit-blame.el new file mode 100644 index 0000000..4af7bb7 --- /dev/null +++ b/elpa/magit-20160223.828/magit-blame.el @@ -0,0 +1,519 @@ +;;; magit-blame.el --- blame support for Magit -*- lexical-binding: t -*- + +;; Copyright (C) 2012-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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: + +;; Annotates each line in file-visiting buffer with information from +;; the revision which last modified the line. + +;;; Code: + +(require 'magit) + +;;; Options + +(defgroup magit-blame nil + "Blame support for Magit." + :group 'magit-extensions) + +(defcustom magit-blame-heading-format "%-20a %C %s" + "Format string used for blame headings. + +The following placeholders are recognized: + + %H hash + %s summary + %a author + %A author time + %c committer + %C committer time + +The author and committer time formats can be specified with +`magit-blame-time-format'." + :group 'magit-blame + :type 'string) + +(defcustom magit-blame-time-format "%F %H:%M" + "Format for time strings in blame headings." + :group 'magit-blame + :type 'string) + +(defcustom magit-blame-show-headings t + "Whether to initially show blame block headings. +The headings can also be toggled locally using command +`magit-blame-toggle-headings'." + :group 'magit-blame + :type 'boolean) + +(defcustom magit-blame-disable-modes '(fci-mode yascroll-bar-mode) + "List of modes not compatible with Magit-Blame mode. +This modes are turned off when Magit-Blame mode is turned on, +and then turned on again when turning off the latter." + :group 'magit-blame + :type '(repeat (symbol :tag "Mode"))) + +(make-variable-buffer-local 'magit-blame-disabled-modes) + +(defcustom magit-blame-mode-lighter " Blame" + "The mode-line lighter of the Magit-Blame mode." + :group 'magit-blame + :type '(choice (const :tag "No lighter" "") string)) + +(unless (find-lisp-object-file-name 'magit-blame-goto-chunk-hook 'defvar) + (add-hook 'magit-blame-goto-chunk-hook 'magit-blame-maybe-update-revision-buffer)) +(defcustom magit-blame-goto-chunk-hook '(magit-blame-maybe-update-revision-buffer) + "Hook run by `magit-blame-next-chunk' and `magit-blame-previous-chunk'." + :package-version '(magit . "2.1.0") + :group 'magit-blame + :type 'hook + :options '(magit-blame-maybe-update-revision-buffer)) + +(defface magit-blame-heading + '((((class color) (background light)) + :background "grey80" + :foreground "black") + (((class color) (background dark)) + :background "grey25" + :foreground "black")) + "Face for blame headings." + :group 'magit-faces) + +(defface magit-blame-summary + '((t :inherit magit-blame-heading)) + "Face used for commit summary in blame headings." + :group 'magit-faces) + +(defface magit-blame-hash + '((t :inherit magit-blame-heading)) + "Face used for commit hash in blame headings." + :group 'magit-faces) + +(defface magit-blame-name + '((t :inherit magit-blame-heading)) + "Face used for author and committer names in blame headings." + :group 'magit-faces) + +(defface magit-blame-date + '((t :inherit magit-blame-heading)) + "Face used for dates in blame headings." + :group 'magit-faces) + +;;; Code + +(defvar magit-blame-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "\r" 'magit-show-commit) + (define-key map "\s" 'magit-diff-show-or-scroll-up) + (define-key map "\d" 'magit-diff-show-or-scroll-down) + (define-key map "b" 'magit-blame-popup) + (define-key map "n" 'magit-blame-next-chunk) + (define-key map "N" 'magit-blame-next-chunk-same-commit) + (define-key map "p" 'magit-blame-previous-chunk) + (define-key map "P" 'magit-blame-previous-chunk-same-commit) + (define-key map "q" 'magit-blame-quit) + (define-key map "t" 'magit-blame-toggle-headings) + (define-key map "\M-w" 'magit-blame-copy-hash) + map) + "Keymap for `magit-blame-mode'.") + +(defun magit-blame-put-keymap-before-view-mode () + "Put `magit-blame-mode' ahead of `view-mode' in `minor-mode-map-alist'." + (--when-let (assq 'magit-blame-mode + (cl-member 'view-mode minor-mode-map-alist :key #'car)) + (setq minor-mode-map-alist + (cons it (delq it minor-mode-map-alist)))) + (remove-hook 'view-mode-hook #'magit-blame-put-keymap-before-view-mode)) + +(add-hook 'view-mode-hook #'magit-blame-put-keymap-before-view-mode) + +(defvar-local magit-blame-buffer-read-only nil) +(defvar-local magit-blame-cache nil) +(defvar-local magit-blame-process nil) +(defvar-local magit-blame-recursive-p nil) +(defvar-local magit-blame-separator nil) + +(define-minor-mode magit-blame-mode + "Display blame information inline. +\n\\{magit-blame-mode-map}" + :lighter magit-blame-mode-lighter + (cond (magit-blame-mode + (when (called-interactively-p 'any) + (setq magit-blame-mode nil) + (user-error + (concat "Don't call `magit-blame-mode' directly; " + "instead use `magit-blame' or `magit-blame-popup'"))) + (setq magit-blame-buffer-read-only buffer-read-only) + (read-only-mode 1) + (dolist (mode magit-blame-disable-modes) + (when (and (boundp mode) (symbol-value mode)) + (funcall mode -1) + (push mode magit-blame-disabled-modes))) + (setq magit-blame-separator (magit-blame-format-separator))) + (t + (unless magit-blame-buffer-read-only + (read-only-mode -1)) + (dolist (mode magit-blame-disabled-modes) + (funcall mode 1)) + (when (process-live-p magit-blame-process) + (kill-process magit-blame-process)) + (save-excursion + (save-restriction + (widen) + (dolist (ov (overlays-in (point-min) (point-max))) + (when (overlay-get ov 'magit-blame) + (delete-overlay ov)))))))) + +(defun auto-revert-handler--unless-magit-blame-mode () + "If Magit-Blame mode is on, then do nothing. See #1731." + magit-blame-mode) + +(advice-add 'auto-revert-handler :before-until + 'auto-revert-handler--unless-magit-blame-mode) + +;;;###autoload (autoload 'magit-blame-popup "magit-blame" nil t) +(magit-define-popup magit-blame-popup + "Popup console for blame commands." + 'magit-commands + :man-page "git-blame" + :switches '((?w "Ignore whitespace" "-w") + (?r "Do not treat root commits as boundaries" "--root")) + :options '((?C "Detect lines moved or copied within a file" "-C") + (?M "Detect lines moved or copied between files" "-M")) + :actions '((?b "Blame" magit-blame)) + :default-arguments '("-w") + :default-action 'magit-blame) + +;;;###autoload +(defun magit-blame (revision file &optional args line) + "Display edit history of FILE up to REVISION. + +Interactively blame the file being visited in the current buffer. +If the buffer visits a revision of that file, then blame up to +that revision, otherwise blame the file's full history, including +uncommitted changes. + +If Magit-Blame mode is already turned on then blame recursively, by +visiting REVISION:FILE (using `magit-find-file'), where revision +is the revision before the revision that added the lines at +point. + +ARGS is a list of additional arguments to pass to `git blame'; +only arguments available from `magit-blame-popup' should be used. +\n(fn REVISION FILE &optional ARGS)" ; LINE is for internal use + (interactive + (let ((args (magit-blame-arguments))) + (if magit-blame-mode + (--if-let (magit-blame-chunk-get :previous-hash) + (list it (magit-blame-chunk-get :previous-file) + args (magit-blame-chunk-get :previous-start)) + (user-error "Block has no further history")) + (--if-let (magit-file-relative-name nil 'tracked) + (list (or magit-buffer-refname magit-buffer-revision) it args) + (if buffer-file-name + (user-error "Buffer isn't visiting a tracked file") + (user-error "Buffer isn't visiting a file")))))) + (magit-with-toplevel + (if revision + (magit-find-file revision file) + (let ((default-directory default-directory)) + (--if-let (find-buffer-visiting file) + (progn (switch-to-buffer it) + (save-buffer)) + (find-file file)))) + ;; ^ Make sure this doesn't affect the value used below. b640c6f + (widen) + (when line + (setq magit-blame-recursive-p t) + (goto-char (point-min)) + (forward-line (1- line))) + (unless magit-blame-mode + (setq magit-blame-cache (make-hash-table :test 'equal)) + (let ((show-headings magit-blame-show-headings)) + (magit-blame-mode 1) + (setq-local magit-blame-show-headings show-headings)) + (message "Blaming...") + (let ((magit-process-popup-time -1) + (inhibit-magit-refresh t)) + (magit-run-git-async + "blame" "--incremental" args + "-L" (format "%s,%s" + (line-number-at-pos (window-start)) + (line-number-at-pos (1- (window-end nil t)))) + revision "--" file)) + (setq magit-blame-process magit-this-process) + (set-process-filter magit-this-process 'magit-blame-process-filter) + (set-process-sentinel + magit-this-process + `(lambda (process event) + (when (memq (process-status process) '(exit signal)) + (magit-process-sentinel process event) + (magit-blame-assert-buffer process) + (with-current-buffer (process-get process 'command-buf) + (when magit-blame-mode + (let ((magit-process-popup-time -1) + (inhibit-magit-refresh t) + (default-directory ,default-directory)) + (magit-run-git-async "blame" "--incremental" ,@args + ,revision "--" ,file)) + (setq magit-blame-process magit-this-process) + (set-process-filter + magit-this-process 'magit-blame-process-filter) + (set-process-sentinel + magit-this-process 'magit-blame-process-sentinel))))))))) + +(defun magit-blame-process-sentinel (process event) + (let ((status (process-status process))) + (when (memq status '(exit signal)) + (magit-process-sentinel process event) + (if (eq status 'exit) + (message "Blaming...done") + (magit-blame-assert-buffer process) + (with-current-buffer (process-get process 'command-buf) + (magit-blame-mode -1)) + (message "Blaming...failed"))))) + +(defvar magit-blame-log nil + "Whether to log blame output to the process buffer. +This is intended for debugging purposes.") + +(defun magit-blame-process-filter (process string) + (when magit-blame-log + (magit-process-filter process string)) + (--when-let (process-get process 'partial-line) + (setq string (concat it string)) + (setf (process-get process 'partial-line) nil)) + (magit-blame-assert-buffer process) + (with-current-buffer (process-get process 'command-buf) + (when magit-blame-mode + (let ((chunk (process-get process 'chunk)) + (lines (split-string string "\n" t))) + (unless (string-match-p "\n\\'" string) + (process-put process 'chunk chunk) + (process-put process 'partial-line (car (last lines))) + (setq lines (butlast lines))) + (dolist (line lines) + (cond + ((equal line "")) + ((not chunk) + (string-match + "^\\(.\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)" line) + (setq chunk + (list :hash (let ((hash (match-string 1 line))) + (unless (equal hash (make-string 40 ?0)) + hash)) + :previous-start (string-to-number (match-string 2 line)) + :start (string-to-number (match-string 3 line)) + :lines (string-to-number (match-string 4 line))))) + ((string-match "^filename \\(.+\\)" line) + (let* ((hash (plist-get chunk :hash)) + (file (match-string 1 line))) + (--if-let (gethash hash magit-blame-cache) + (setq chunk (nconc chunk it)) + (plist-put chunk :filename file) + (puthash hash chunk magit-blame-cache))) + (magit-blame-make-overlay chunk) + (setq chunk nil)) + ((string-match "^previous \\(.\\{40\\}\\) \\(.+\\)" line) + (plist-put chunk :previous-hash (match-string 1 line)) + (plist-put chunk :previous-file (match-string 2 line))) + ((string-match "^\\([^ ]+?-mail\\) <\\([^>]+\\)>" line) + (plist-put chunk (intern (concat ":" (match-string 1 line))) + (string-to-number (match-string 2 line)))) + ((string-match "^\\([^ ]+?-\\(?:time\\|tz\\)\\) \\(.+\\)" line) + (plist-put chunk (intern (concat ":" (match-string 1 line))) + (string-to-number (match-string 2 line)))) + ((string-match "^\\([^ ]+\\) \\(.+\\)" line) + (plist-put chunk (intern (concat ":" (match-string 1 line))) + (match-string 2 line)))) + (process-put process 'chunk chunk)))))) + +(defun magit-blame-assert-buffer (process) + (unless (buffer-live-p (process-get process 'command-buf)) + (kill-process process) + (user-error "Buffer being blamed has been killed"))) + +(defun magit-blame-make-overlay (chunk) + (let ((ov (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (forward-line (1- (plist-get chunk :start))) + (--when-let (--first (overlay-get it 'magit-blame) + (overlays-at (point))) + (delete-overlay it)) + (make-overlay (point) + (progn (forward-line + (plist-get chunk :lines)) + (point)))))) + (heading (magit-blame-format-heading chunk))) + (overlay-put ov 'magit-blame chunk) + (overlay-put ov 'magit-blame-heading heading) + (overlay-put ov 'before-string + (if magit-blame-show-headings + heading + magit-blame-separator)))) + +(defun magit-blame-format-separator () + (propertize + (concat (propertize " " 'display '(space :height (2))) + (propertize "\n" 'line-height t)) + 'face (list :background (face-attribute 'magit-blame-heading :background)))) + +(defun magit-blame-format-heading (chunk) + (with-temp-buffer + (insert (format-spec + (concat magit-blame-heading-format "\n") + `((?H . ,(propertize (or (plist-get chunk :hash) "") + 'face 'magit-blame-hash)) + (?s . ,(propertize (or (plist-get chunk :summary) "") + 'face 'magit-blame-summary)) + (?a . ,(propertize (or (plist-get chunk :author) "") + 'face 'magit-blame-name)) + (?A . ,(propertize (magit-blame-format-time-string + magit-blame-time-format + (plist-get chunk :author-time) + (plist-get chunk :author-tz)) + 'face 'magit-blame-date)) + (?c . ,(propertize (or (plist-get chunk :committer) "") + 'face 'magit-blame-name)) + (?C . ,(propertize (magit-blame-format-time-string + magit-blame-time-format + (plist-get chunk :committer-time) + (plist-get chunk :committer-tz)) + 'face 'magit-blame-date))))) + (goto-char (point-min)) + (while (not (eobp)) + (let ((face (get-text-property (point) 'face)) + (next (or (next-single-property-change (point) 'face) + (point-max)))) + (unless face + (put-text-property (point) next 'face 'magit-blame-heading)) + (goto-char next))) + (buffer-string))) + +(defun magit-blame-format-time-string (format time tz) + (format-time-string + format (seconds-to-time (+ time (* (/ tz 100) 60 60) (* (% tz 100) 60))))) + +(defun magit-blame-quit () + "Turn off Magit-Blame mode. +If the buffer was created during a recursive blame, +then also kill the buffer." + (interactive) + (if magit-blame-recursive-p + (kill-buffer) + (magit-blame-mode -1))) + +(defun magit-blame-next-chunk () + "Move to the next chunk." + (interactive) + (--if-let (next-single-char-property-change (point) 'magit-blame) + (progn (goto-char it) + (run-hooks 'magit-blame-goto-chunk-hook)) + (user-error "No more chunks"))) + +(defun magit-blame-previous-chunk () + "Move to the previous chunk." + (interactive) + (--if-let (previous-single-char-property-change (point) 'magit-blame) + (progn (goto-char it) + (run-hooks 'magit-blame-goto-chunk-hook)) + (user-error "No more chunks"))) + +(defun magit-blame-next-chunk-same-commit (&optional previous) + "Move to the next chunk from the same commit.\n\n(fn)" + (interactive) + (-if-let (hash (magit-blame-chunk-get :hash)) + (let ((pos (point)) ov) + (save-excursion + (while (and (not ov) + (not (= pos (if previous (point-min) (point-max)))) + (setq pos (funcall + (if previous + 'previous-single-char-property-change + 'next-single-char-property-change) + pos 'magit-blame))) + (--when-let (magit-blame-overlay-at pos) + (when (equal (magit-blame-chunk-get :hash pos) hash) + (setq ov it))))) + (if ov + (goto-char (overlay-start ov)) + (user-error "No more chunks from same commit"))) + (user-error "This chunk hasn't been blamed yet"))) + +(defun magit-blame-previous-chunk-same-commit () + "Move to the previous chunk from the same commit." + (interactive) + (magit-blame-next-chunk-same-commit 'previous-single-char-property-change)) + +(defun magit-blame-toggle-headings () + "Show or hide blame chunk headings." + (interactive) + (setq-local magit-blame-show-headings (not magit-blame-show-headings)) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (while (not (eobp)) + (let ((next (next-single-char-property-change (point) 'magit-blame))) + (--when-let (magit-blame-overlay-at (point)) + (overlay-put it 'before-string + (if magit-blame-show-headings + (overlay-get it 'magit-blame-heading) + magit-blame-separator))) + (goto-char (or next (point-max)))))))) + +(defun magit-blame-copy-hash () + "Save hash of the current chunk's commit to the kill ring." + (interactive) + (kill-new (message "%s" (magit-blame-chunk-get :hash)))) + +(defun magit-blame-chunk-get (key &optional pos) + (--when-let (magit-blame-overlay-at pos) + (plist-get (overlay-get it 'magit-blame) key))) + +(defun magit-blame-overlay-at (&optional pos) + (--first (overlay-get it 'magit-blame) + (overlays-at (or pos (point))))) + +(defun magit-blame-maybe-update-revision-buffer () + (unless magit--update-revision-buffer + (setq magit--update-revision-buffer nil) + (-when-let* ((commit (magit-blame-chunk-get :hash)) + (buffer (magit-mode-get-buffer 'magit-revision-mode nil t))) + (setq magit--update-revision-buffer (list commit buffer)) + (run-with-idle-timer + magit-update-other-window-delay nil + (lambda () + (-let [(rev buf) magit--update-revision-buffer] + (setq magit--update-revision-buffer nil) + (when (buffer-live-p buf) + (let ((magit-display-buffer-noselect t)) + (apply #'magit-show-commit rev (magit-diff-arguments)))))))))) + +;;; magit-blame.el ends soon +(provide 'magit-blame) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-blame.el ends here diff --git a/elpa/magit-20160223.828/magit-commit.el b/elpa/magit-20160223.828/magit-commit.el new file mode 100644 index 0000000..85b6288 --- /dev/null +++ b/elpa/magit-20160223.828/magit-commit.el @@ -0,0 +1,402 @@ +;;; magit-commit.el --- create Git commits -*- lexical-binding: t -*- + +;; Copyright (C) 2008-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library implements commands for creating Git commits. These +;; commands just initiate the commit, support for writing the commit +;; messages is implemented in `git-commit.el'. + +;;; Code: + +(require 'magit) +(require 'magit-sequence) + +(eval-when-compile (require 'epa)) ; for `epa-protocol' +(eval-when-compile (require 'epg)) +(declare-function epg-sub-key-id 'epg) +(declare-function epg-key-sub-key-list 'epg) +(declare-function epg-key-user-id-list 'epg) +(declare-function epg-user-id-string 'epg) +(declare-function epg-decode-dn 'epg) +(declare-function epg-list-keys 'epg) + +;;; Options + +(defcustom magit-commit-arguments nil + "The arguments used when committing." + :group 'magit-commands + :type '(repeat (string :tag "Argument"))) + +(defcustom magit-commit-ask-to-stage 'verbose + "Whether to ask to stage everything when committing and nothing is staged." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type '(choice (const :tag "Ask showing diff" verbose) + (const :tag "Ask" t) + (const :tag "Don't ask" nil))) + +(defcustom magit-commit-show-diff t + "Whether the relevant diff is automatically shown when committing." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-extend-override-date t + "Whether using `magit-commit-extend' changes the committer date." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-reword-override-date t + "Whether using `magit-commit-reword' changes the committer date." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-squash-confirm t + "Whether the commit targeted by squash and fixup has to be confirmed. +When non-nil then the commit at point (if any) is used as default +choice, otherwise it has to be confirmed. This option only +affects `magit-commit-squash' and `magit-commit-fixup'. The +\"instant\" variants always require confirmation because making +an error while using those is harder to recover from." + :package-version '(magit . "2.1.0") + :group 'magit-commands + :type 'boolean) + +;;; Code + +(defun magit-commit-popup (&optional arg) + "Popup console for commit commands." + (interactive "P") + (--if-let (magit-commit-message-buffer) + (switch-to-buffer it) + (magit-invoke-popup 'magit-commit-popup nil arg))) + +(defvar magit-commit-popup + '(:variable magit-commit-arguments + :man-page "git-commit" + :switches ((?a "Stage all modified and deleted files" "--all") + (?e "Allow empty commit" "--allow-empty") + (?v "Show diff of changes to be committed" "--verbose") + (?n "Bypass git hooks" "--no-verify") + (?s "Add Signed-off-by line" "--signoff") + (?R "Claim authorship and reset author date" "--reset-author")) + :options ((?A "Override the author" "--author=") + (?S "Sign using gpg" "--gpg-sign=" magit-read-gpg-secret-key) + (?C "Reuse commit message" "--reuse-message=")) + :actions ((?c "Commit" magit-commit) + (?e "Extend" magit-commit-extend) + (?f "Fixup" magit-commit-fixup) + (?F "Instant Fixup" magit-commit-instant-fixup) nil + (?w "Reword" magit-commit-reword) + (?s "Squash" magit-commit-squash) + (?S "Instant Squash" magit-commit-instant-squash) nil + (?a "Amend" magit-commit-amend) + (?A "Augment" magit-commit-augment)) + :max-action-columns 4 + :default-action magit-commit)) + +(magit-define-popup-keys-deferred 'magit-commit-popup) + +(defun magit-commit-arguments nil + (if (eq magit-current-popup 'magit-commit-popup) + magit-current-popup-args + magit-commit-arguments)) + +(defun magit-commit-message-buffer () + (let* ((find-file-visit-truename t) ; git uses truename of COMMIT_EDITMSG + (topdir (magit-toplevel))) + (--first (equal topdir (with-current-buffer it + (and git-commit-mode (magit-toplevel)))) + (append (buffer-list (selected-frame)) + (buffer-list))))) + +;;;###autoload +(defun magit-commit (&optional args) + "Create a new commit on HEAD. +With a prefix argument amend to the commit at HEAD instead. +\n(git commit [--amend] ARGS)" + (interactive (if current-prefix-arg + (list (cons "--amend" (magit-commit-arguments))) + (list (magit-commit-arguments)))) + (when (setq args (magit-commit-assert args)) + (magit-run-git-with-editor "commit" args))) + +;;;###autoload +(defun magit-commit-amend (&optional args) + "Amend the last commit. +\n(git commit --amend ARGS)" + (interactive (list (magit-commit-arguments))) + (magit-run-git-with-editor "commit" "--amend" args)) + +;;;###autoload +(defun magit-commit-extend (&optional args override-date) + "Amend the last commit, without editing the message. + +With a prefix argument keep the committer date, otherwise change +it. The option `magit-commit-extend-override-date' can be used +to inverse the meaning of the prefix argument. \n(git commit +--amend --no-edit)" + (interactive (list (magit-commit-arguments) + (if current-prefix-arg + (not magit-commit-extend-override-date) + magit-commit-extend-override-date))) + (when (setq args (magit-commit-assert args (not override-date))) + (let ((process-environment process-environment)) + (unless override-date + (push (magit-rev-format "GIT_COMMITTER_DATE=%cD") process-environment)) + (magit-run-git-with-editor "commit" "--amend" "--no-edit" args)))) + +;;;###autoload +(defun magit-commit-reword (&optional args override-date) + "Reword the last commit, ignoring staged changes. + +With a prefix argument keep the committer date, otherwise change +it. The option `magit-commit-reword-override-date' can be used +to inverse the meaning of the prefix argument. + +Non-interactively respect the optional OVERRIDE-DATE argument +and ignore the option. +\n(git commit --amend --only)" + (interactive (list (magit-commit-arguments) + (if current-prefix-arg + (not magit-commit-reword-override-date) + magit-commit-reword-override-date))) + (let ((process-environment process-environment)) + (unless override-date + (push (magit-rev-format "GIT_COMMITTER_DATE=%cD") process-environment)) + (magit-run-git-with-editor "commit" "--amend" "--only" args))) + +;;;###autoload +(defun magit-commit-fixup (&optional commit args) + "Create a fixup commit. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--fixup" commit args)) + +;;;###autoload +(defun magit-commit-squash (&optional commit args) + "Create a squash commit, without editing the squash message. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--squash" commit args)) + +;;;###autoload +(defun magit-commit-augment (&optional commit args) + "Create a squash commit, editing the squash message. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--squash" commit args nil t)) + +;;;###autoload +(defun magit-commit-instant-fixup (&optional commit args) + "Create a fixup commit targeting COMMIT and instantly rebase." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--fixup" commit args t)) + +;;;###autoload +(defun magit-commit-instant-squash (&optional commit args) + "Create a squash commit targeting COMMIT and instantly rebase." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--squash" commit args t)) + +(defun magit-commit-squash-internal + (option commit &optional args rebase edit confirmed) + (-when-let (args (magit-commit-assert args t)) + (if (and commit + (or confirmed + (not (or rebase + current-prefix-arg + magit-commit-squash-confirm)))) + (let ((magit-commit-show-diff nil)) + (magit-run-git-with-editor "commit" + (unless edit "--no-edit") + (concat option "=" commit) + args)) + (magit-log-select + `(lambda (commit) + (magit-commit-squash-internal ,option commit ',args ,rebase ,edit t) + ,@(when rebase + `((magit-rebase-interactive-1 commit + (list "--autosquash" "--autostash") + "" "true")))) + (format "Type %%p on a commit to %s into it," + (substring option 2))) + (when magit-commit-show-diff + (let ((magit-display-buffer-noselect t)) + (apply #'magit-diff-staged nil (magit-diff-arguments))))))) + +(defun magit-commit-assert (args &optional strict) + (cond + ((or (magit-anything-staged-p) + (and (magit-anything-unstaged-p) + ;; ^ Everything of nothing is still nothing. + (member "--all" args)) + (and (not strict) + ;; ^ For amend variants that don't make sense otherwise. + (or (member "--amend" args) + (member "--allow-empty" args)))) + (or args (list "--"))) + ((and (magit-rebase-in-progress-p) + (not (magit-anything-unstaged-p)) + (y-or-n-p "Nothing staged. Continue in-progress rebase? ")) + (magit-run-git-sequencer "rebase" "--continue") + nil) + ((and (file-exists-p (magit-git-dir "MERGE_MSG")) + (not (magit-anything-unstaged-p))) + (or args (list "--"))) + ((not (magit-anything-unstaged-p)) + (user-error "Nothing staged (or unstaged)")) + (magit-commit-ask-to-stage + (when (eq magit-commit-ask-to-stage 'verbose) + (magit-diff-unstaged)) + (prog1 (when (y-or-n-p "Nothing staged. Stage and commit everything? ") + (magit-run-git "add" "-u" ".") + (or args (list "--"))) + (when (and (eq magit-commit-ask-to-stage 'verbose) + (derived-mode-p 'magit-diff-mode)) + (magit-mode-bury-buffer)))) + (t + (user-error "Nothing staged")))) + +(defun magit-commit-diff () + (--when-let (and git-commit-mode + magit-commit-show-diff + (pcase last-command + (`magit-commit + (apply-partially 'magit-diff-staged nil)) + (`magit-commit-amend 'magit-diff-while-amending) + (`magit-commit-reword 'magit-diff-while-amending))) + (condition-case nil + (let ((magit-inhibit-save-previous-winconf 'unset) + (magit-display-buffer-noselect t) + (inhibit-quit nil)) + (message "Diffing changes to be committed (C-g to abort diffing)") + (funcall it (car (magit-diff-arguments)))) + (quit)))) + +;; Mention `magit-diff-while-committing' because that's +;; always what I search for when I try to find this line. +(add-hook 'server-switch-hook 'magit-commit-diff) + +(add-to-list 'with-editor-server-window-alist + (cons git-commit-filename-regexp 'switch-to-buffer)) + +(defvar magit-gpg-secret-key-hist nil) + +(defun magit-read-gpg-secret-key (prompt &optional _initial-input) + (require 'epa) + (let ((keys (--map (list (epg-sub-key-id (car (epg-key-sub-key-list it))) + (-when-let (id-obj (car (epg-key-user-id-list it))) + (let ((id-str (epg-user-id-string id-obj))) + (if (stringp id-str) + id-str + (epg-decode-dn id-obj))))) + (epg-list-keys (epg-make-context epa-protocol) nil t)))) + (magit-completing-read prompt keys nil nil nil 'magit-gpg-secret-key-hist + (car (or magit-gpg-secret-key-hist keys))))) + +(defvar magit-commit-add-log-insert-function 'magit-commit-add-log-insert + "Used by `magit-commit-add-log' to insert a single entry.") + +(defun magit-commit-add-log () + "Add a stub for the current change into the commit message buffer. +If no commit is in progress, then initiate it. Use the function +specified by variable `magit-commit-add-log-insert-function' to +actually insert the entry." + (interactive) + (let ((hunk (magit-section-when 'hunk it)) + (log (magit-commit-message-buffer)) buf pos) + (save-window-excursion + (call-interactively #'magit-diff-visit-file) + (setq buf (current-buffer) + pos (point))) + (unless log + (unless (magit-commit-assert nil) + (user-error "Abort")) + (magit-commit) + (while (not (setq log (magit-commit-message-buffer))) + (sit-for 0.01))) + (save-excursion + (with-current-buffer buf + (goto-char pos) + (funcall magit-commit-add-log-insert-function log + (magit-file-relative-name) + (and hunk (add-log-current-defun))))))) + +(defun magit-commit-add-log-insert (buffer file defun) + (with-current-buffer buffer + (undo-boundary) + (goto-char (point-max)) + (while (re-search-backward (concat "^" comment-start) nil t)) + (cond ((re-search-backward (format "* %s\\(?: (\\([^)]+\\))\\)?: " file) + nil t) + (when (equal (match-string 1) defun) + (setq defun nil)) + (re-search-forward ": ")) + (t + (when (re-search-backward "^[\\*(].+\n" nil t) + (goto-char (match-end 0))) + (while (re-search-forward "^[^\\*#\n].*\n" nil t)) + (if defun + (progn (insert (format "* %s (%s): \n" file defun)) + (setq defun nil)) + (insert (format "* %s: \n" file))) + (backward-char) + (unless (looking-at "\n[\n\\']") + (insert ?\n) + (backward-char)))) + (when defun + (forward-line) + (let ((limit (save-excursion + (and (re-search-forward "^\\*" nil t) + (point))))) + (unless (or (looking-back (format "(%s): " defun) + (line-beginning-position)) + (re-search-forward (format "^(%s): " defun) limit t)) + (while (re-search-forward "^[^\\*#\n].*\n" limit t)) + (insert (format "(%s): \n" defun)) + (backward-char)))))) + +;;; magit-commit.el ends soon +(provide 'magit-commit) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-commit.el ends here diff --git a/elpa/magit-20160223.828/magit-core.el b/elpa/magit-20160223.828/magit-core.el new file mode 100644 index 0000000..ea14dc1 --- /dev/null +++ b/elpa/magit-20160223.828/magit-core.el @@ -0,0 +1,77 @@ +;;; magit-core.el --- core functionality -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library requires several other libraries, so that yet other +;; libraries can just require this one, instead of having to require +;; all the other ones. In other words this separates the low-level +;; stuff from the rest. It also defines some Custom groups. + +;;; Code: + +(require 'magit-utils) +(require 'magit-section) +(require 'magit-git) +(require 'magit-mode) +(require 'magit-popup) +(require 'magit-process) +(require 'magit-autorevert) + +(defgroup magit nil + "Controlling Git from Emacs." + :group 'tools) + +(defgroup magit-commands nil + "Options controlling behavior of certain commands." + :group 'magit) + +(defgroup magit-modes nil + "Modes used or provided by Magit." + :group 'magit) + +(defgroup magit-extensions nil + "Extensions to Magit." + :group 'magit) + +(defgroup magit-faces nil + "Faces used by Magit." + :group 'magit + :group 'faces) + +(custom-add-to-group 'magit-modes 'magit-popup 'custom-group) +(custom-add-to-group 'magit-faces 'magit-popup-faces 'custom-group) +(custom-add-to-group 'magit-modes 'git-commit 'custom-group) +(custom-add-to-group 'magit-faces 'git-commit-faces 'custom-group) +(custom-add-to-group 'magit-modes 'git-rebase 'custom-group) +(custom-add-to-group 'magit-faces 'git-rebase-faces 'custom-group) +(custom-add-to-group 'magit-process 'with-editor 'custom-group) + +(custom-add-to-group 'magit 'vc-follow-symlinks 'custom-variable) + +;;; magit-core.el ends soon +(provide 'magit-core) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-core.el ends here diff --git a/elpa/magit-20160223.828/magit-diff.el b/elpa/magit-20160223.828/magit-diff.el new file mode 100644 index 0000000..1af889f --- /dev/null +++ b/elpa/magit-20160223.828/magit-diff.el @@ -0,0 +1,2069 @@ +;;; magit-diff.el --- inspect Git diffs -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library implements support for looking at Git diffs and +;; commits. + +;;; Code: + +(require 'git-commit) +(require 'magit-core) + +;; For `magit-diff-popup' +(declare-function magit-stash-show 'magit-stash) +;; For `magit-diff-visit-file' +(declare-function magit-dired-jump 'magit) +(declare-function magit-find-file-noselect 'magit) +(declare-function magit-status-internal 'magit) +;; For `magit-diff-wash-revision' +(declare-function magit-insert-tags-header 'magit) +;; For `magit-diff-while-committing' +(declare-function magit-commit-message-buffer 'magit) +;; For `magit-insert-revision-gravatar' +(defvar gravatar-size) +;; For `magit-show-commit' and `magit-diff-show-or-scroll' +(declare-function magit-blame-chunk-get 'magit-blame) +(declare-function magit-blame-mode 'magit-blame) +(defvar magit-blame-mode) +(defvar git-rebase-line) + +(require 'diff-mode) +(require 'smerge-mode) + +;;; Options +;;;; Diff Mode + +(defgroup magit-diff nil + "Inspect and manipulate Git diffs." + :group 'magit-modes) + +(custom-add-to-group 'magit-diff 'smerge-refine-ignore-whitespace + 'custom-variable) + +(defcustom magit-diff-mode-hook nil + "Hook run after entering Magit-Diff mode." + :group 'magit-diff + :type 'hook) + +(defcustom magit-diff-sections-hook + '(magit-insert-diff + magit-insert-xref-buttons) + "Hook run to insert sections into a `magit-diff-mode' buffer." + :package-version '(magit . "2.3.0") + :group 'magit-revision + :type 'hook) + +(defcustom magit-diff-expansion-threshold 1.0 + "After how many seconds not to expand anymore diffs. + +Except in status buffers, diffs are usually start out fully +expanded. Because that can take a long time, all diffs that +haven't been fontified during a refresh before the threshold +defined here are instead displayed with their bodies collapsed. + +Note that this can cause sections that were previously expanded +to be collapsed. So you should not pick a very low value here. + +The hook function `magit-diff-expansion-threshold' has to be a +member of `magit-section-set-visibility-hook' for this option +to have any effect" + :package-version '(magit . "2.1.0") + :group 'magit-diff + :type 'float) + +(defcustom magit-diff-highlight-hunk-body t + "Whether to highlight bodies of selected hunk sections. +This only has an effect if `magit-diff-highlight' is a +member of `magit-section-highlight-hook', which see." + :package-version '(magit . "2.1.0") + :group 'magit-diff + :type 'boolean) + +(defcustom magit-diff-show-lines-boundary t + "Whether to delimit hunk-internal region with thin lines. + +When a hunk-internal region (used to stage just the lines that +fall into the region instead of the complete hunk) only covers +context lines, then these lines are the only visual indicator +for the region. In character-only terminals it's not possible +to draw thin lines." + :package-version '(magit . "2.1.0") + :group 'magit-diff + :type 'boolean) + +(defcustom magit-diff-refine-hunk nil + "Whether to show word-granularity differences within diff hunks. + +nil never show fine differences. +t show fine differences for the current diff hunk only. +`all' show fine differences for all displayed diff hunks." + :group 'magit-diff + :safe (lambda (val) (memq val '(nil t all))) + :type '(choice (const :tag "Never" nil) + (const :tag "Current" t) + (const :tag "All" all))) + +(put 'magit-diff-refine-hunk 'permanent-local t) + +(defcustom magit-diff-paint-whitespace t + "Specify where to highlight whitespace errors. +See `magit-highlight-trailing-whitespace', +`magit-highlight-indentation'. The symbol t means in all diffs, +`status' means only in the status buffer, and nil means nowhere." + :group 'magit-diff + :safe (lambda (val) (memq val '(t nil status))) + :type '(choice (const :tag "Always" t) + (const :tag "Never" nil) + (const :tag "In status buffer" status))) + +(defcustom magit-diff-highlight-trailing t + "Whether to highlight whitespace at the end of a line in diffs. +Used only when `magit-diff-paint-whitespace' is non-nil." + :group 'magit-diff + :safe 'booleanp + :type 'boolean) + +(defcustom magit-diff-highlight-indentation nil + "Highlight the \"wrong\" indentation style. +Used only when `magit-diff-paint-whitespace' is non-nil. + +The value is a list of cons cells. The car is a regular +expression, and the cdr is the value that applies to repositories +whose directory matches the regular expression. If more than one +element matches, then the *last* element in the list applies. +The default value should therefore come first in the list. + +If the value is `tabs', highlight indentation with tabs. If the +value is an integer, highlight indentation with at least that +many spaces. Otherwise, highlight neither." + :group 'magit-diff + :type `(repeat (cons (string :tag "Directory regexp") + (choice (const :tag "Tabs" tabs) + (integer :tag "Spaces" :value ,tab-width) + (const :tag "Neither" nil))))) + +;;;; Revision Mode + +(defgroup magit-revision nil + "Inspect and manipulate Git commits." + :group 'magit-modes) + +(defcustom magit-revision-mode-hook nil + "Hook run after entering Magit-Revision mode." + :group 'magit-revision + :type 'hook) + +(defcustom magit-revision-sections-hook + '(magit-insert-revision-tag + magit-insert-revision-headers + magit-insert-revision-message + magit-insert-revision-notes + magit-insert-revision-diff + magit-insert-xref-buttons) + "Hook run to insert sections into a `magit-revision-mode' buffer." + :package-version '(magit . "2.3.0") + :group 'magit-revision + :type 'hook) + +(defcustom magit-revision-headers-format "\ +Author: %aN <%aE> +AuthorDate: %ad +Commit: %cN <%cE> +CommitDate: %cd +" + "Format string used to insert headers in revision buffers. + +All headers in revision buffers are inserted by the section +inserter `magit-insert-revision-headers'. Some of the headers +are created by calling `git show --format=FORMAT' where FORMAT +is the format specified here. Other headers are hard coded or +subject to option `magit-revision-insert-related-refs'." + :package-version '(magit . "2.3.0") + :group 'magit-revision + :type 'string) + +(defcustom magit-revision-insert-related-refs t + "Whether to show related refs in revision buffers." + :package-version '(magit . "2.1.0") + :group 'magit-revision + :type 'boolean) + +(defcustom magit-revision-show-gravatars nil + "Whether to show gravatar images in revision buffers. + +If non-nil, then the value has to be a cons-cell which specifies +where the gravatar images for the author and/or the committer are +inserted inside the text that was previously inserted according +to `magit-revision-header-format'. + +Both cells are regular expressions. The car specifies where to +insert the author gravatar image. The top halve of the image is +inserted right after the matched text, the bottom halve on the +next line at the same offset. The cdr specifies where to insert +the committer image, accordingly. Either the car or the cdr may +be nil." + :package-version '(magit . "2.3.0") + :group 'magit-revision + :type '(choice (const :tag "Don't show gravatars" nil) + (cons :tag "Show gravatars" + (regexp :tag "Author regexp" "^Author: ") + (regexp :tag "Committer regexp" "^Commit: ")))) + +(defcustom magit-revision-use-gravatar-kludge nil + "Whether to work around a bug which affects display of gravatars. + +Gravatar images are spliced into two halves which are then +displayed on separate lines. On OS X the splicing has a bug in +some Emacs builds, which causes the top and bottom halves to be +interchanged. Enabling this option works around this issue by +interchanging the halves once more, which cancels out the effect +of the bug. + +See https://github.com/magit/magit/issues/2265 +and https://debbugs.gnu.org/cgi/bugreport.cgi?bug=7847." + :package-version '(magit . "2.3.0") + :group 'magit-revision + :type 'boolean) + +;;; Faces + +(defface magit-diff-file-heading + '((t :weight bold)) + "Face for diff file headings." + :group 'magit-faces) + +(defface magit-diff-file-heading-highlight + '((t :inherit (magit-diff-file-heading magit-section-highlight))) + "Face for current diff file headings." + :group 'magit-faces) + +(defface magit-diff-file-heading-selection + '((((class color) (background light)) + :inherit magit-diff-file-heading-highlight + :foreground "salmon4") + (((class color) (background dark)) + :inherit magit-diff-file-heading-highlight + :foreground "LightSalmon3")) + "Face for selected diff file headings." + :group 'magit-faces) + +(defface magit-diff-hunk-heading + '((((class color) (background light)) + :background "grey80" + :foreground "grey30") + (((class color) (background dark)) + :background "grey25" + :foreground "grey70")) + "Face for diff hunk headings." + :group 'magit-faces) + +(defface magit-diff-hunk-heading-highlight + '((((class color) (background light)) + :background "grey75" + :foreground "grey30") + (((class color) (background dark)) + :background "grey35" + :foreground "grey70")) + "Face for current diff hunk headings." + :group 'magit-faces) + +(defface magit-diff-hunk-heading-selection + '((((class color) (background light)) + :inherit magit-diff-hunk-heading-highlight + :foreground "salmon4") + (((class color) (background dark)) + :inherit magit-diff-hunk-heading-highlight + :foreground "LightSalmon3")) + "Face for selected diff hunk headings." + :group 'magit-faces) + +(defface magit-diff-lines-heading + '((((class color) (background light)) + :inherit magit-diff-hunk-heading-highlight + :background "LightSalmon3") + (((class color) (background dark)) + :inherit magit-diff-hunk-heading-highlight + :foreground "grey80" + :background "salmon4")) + "Face for diff hunk heading when lines are marked." + :group 'magit-faces) + +(defface magit-diff-lines-boundary + '((t :inherit magit-diff-lines-heading)) + "Face for boundary of marked lines in diff hunk." + :group 'magit-faces) + +(defface magit-diff-conflict-heading + '((t :inherit magit-diff-hunk-heading)) + "Face for conflict markers." + :group 'magit-faces) + +(defface magit-diff-added + '((((class color) (background light)) + :background "#ddffdd" + :foreground "#22aa22") + (((class color) (background dark)) + :background "#335533" + :foreground "#ddffdd")) + "Face for lines in a diff that have been added." + :group 'magit-faces) + +(defface magit-diff-removed + '((((class color) (background light)) + :background "#ffdddd" + :foreground "#aa2222") + (((class color) (background dark)) + :background "#553333" + :foreground "#ffdddd")) + "Face for lines in a diff that have been removed." + :group 'magit-faces) + +(defface magit-diff-our + '((t :inherit magit-diff-removed)) + "Face for lines in a diff for our side in a conflict." + :group 'magit-faces) + +(defface magit-diff-base + '((((class color) (background light)) + :background "#ffffcc" + :foreground "#aaaa11") + (((class color) (background dark)) + :background "#555522" + :foreground "#ffffcc")) + "Face for lines in a diff for the base side in a conflict." + :group 'magit-faces) + +(defface magit-diff-their + '((t :inherit magit-diff-added)) + "Face for lines in a diff for their side in a conflict." + :group 'magit-faces) + +(defface magit-diff-context + '((((class color) (background light)) :foreground "grey50") + (((class color) (background dark)) :foreground "grey70")) + "Face for lines in a diff that are unchanged." + :group 'magit-faces) + +(defface magit-diff-added-highlight + '((((class color) (background light)) + :background "#cceecc" + :foreground "#22aa22") + (((class color) (background dark)) + :background "#336633" + :foreground "#cceecc")) + "Face for lines in a diff that have been added." + :group 'magit-faces) + +(defface magit-diff-removed-highlight + '((((class color) (background light)) + :background "#eecccc" + :foreground "#aa2222") + (((class color) (background dark)) + :background "#663333" + :foreground "#eecccc")) + "Face for lines in a diff that have been removed." + :group 'magit-faces) + +(defface magit-diff-our-highlight + '((t :inherit magit-diff-removed-highlight)) + "Face for lines in a diff for our side in a conflict." + :group 'magit-faces) + +(defface magit-diff-base-highlight + '((((class color) (background light)) + :background "#eeeebb" + :foreground "#aaaa11") + (((class color) (background dark)) + :background "#666622" + :foreground "#eeeebb")) + "Face for lines in a diff for the base side in a conflict." + :group 'magit-faces) + +(defface magit-diff-their-highlight + '((t :inherit magit-diff-added-highlight)) + "Face for lines in a diff for their side in a conflict." + :group 'magit-faces) + +(defface magit-diff-context-highlight + '((((class color) (background light)) + :background "grey95" + :foreground "grey50") + (((class color) (background dark)) + :background "grey20" + :foreground "grey70")) + "Face for lines in a diff that have been removed." + :group 'magit-faces) + +(defface magit-diff-whitespace-warning + '((t :inherit trailing-whitespace)) + "Face for highlighting whitespace errors added lines." + :group 'magit-faces) + +(defface magit-diffstat-added + '((((class color) (background light)) :foreground "#22aa22") + (((class color) (background dark)) :foreground "#448844")) + "Face for plus sign in diffstat." + :group 'magit-faces) + +(defface magit-diffstat-removed + '((((class color) (background light)) :foreground "#aa2222") + (((class color) (background dark)) :foreground "#aa4444")) + "Face for minus sign in diffstat." + :group 'magit-faces) + +;;; Commands + +(defconst magit-diff-popup-common + '(:variable magit-diff-arguments + :man-page "git-diff" + :options ((?f "Limit to files" "-- " magit-read-files) + (?u "Context lines" "-U") + (?m "Detect renames" "-M") + (?c "Detect copies" "-C") + (?a "Diff algorithm" "--diff-algorithm=" + magit-diff-select-algorithm)))) + +(defvar magit-diff-popup + `(,@magit-diff-popup-common + :switches ((?f "Show surrounding functions" "--function-context") + (?b "Ignore whitespace changes" "--ignore-space-change") + (?w "Ignore all whitespace" "--ignore-all-space") + (?x "Disallow external diff drivers" "--no-ext-diff") + (?s "Show stats" "--stat")) + :actions ((?d "Dwim" magit-diff-dwim) + (?u "Diff unstaged" magit-diff-unstaged) + (?c "Show commit" magit-show-commit) + (?r "Diff range" magit-diff) + (?s "Diff staged" magit-diff-staged) + (?t "Show stash" magit-stash-show) + (?p "Diff paths" magit-diff-paths) + (?w "Diff worktree" magit-diff-working-tree)) + :default-action magit-diff-dwim + :max-action-columns 3)) + +(defvar magit-diff-refresh-popup + `(,@magit-diff-popup-common + :switches ((?f "Show surrounding functions" "--function-context") + (?b "Ignore whitespace changes" "--ignore-space-change") + (?w "Ignore all whitespace" "--ignore-all-space") + (?x "Disallow external diff drivers" "--no-ext-diff")) + :actions ((?g "Refresh" magit-diff-refresh) + (?t "Toggle hunk refinement" magit-diff-toggle-refine-hunk) + (?s "Set defaults" magit-diff-set-default-arguments) nil + (?w "Save defaults" magit-diff-save-default-arguments)) + :max-action-columns 2)) + +(defvar magit-diff-mode-refresh-popup + `(,@magit-diff-popup-common + :switches ((?f "Show surrounding functions" "--function-context") + (?b "Ignore whitespace changes" "--ignore-space-change") + (?w "Ignore all whitespace" "--ignore-all-space") + (?x "Disallow external diff drivers" "--no-ext-diff") + (?s "Show stats" "--stat")) + :actions ((?g "Refresh" magit-diff-refresh) + (?t "Toggle hunk refinement" magit-diff-toggle-refine-hunk) + (?s "Set defaults" magit-diff-set-default-arguments) + (?r "Switch range type" magit-diff-switch-range-type) + (?w "Save defaults" magit-diff-save-default-arguments) + (?f "Flip revisions" magit-diff-flip-revs)) + :max-action-columns 2)) + +(defvar magit-revision-mode-refresh-popup + `(,@magit-diff-popup-common + :switches ((?f "Show surrounding functions" "--function-context") + (?b "Ignore whitespace changes" "--ignore-space-change") + (?w "Ignore all whitespace" "--ignore-all-space") + (?x "Disallow external diff drivers" "--no-ext-diff") + (?s "Show stats" "--stat")) + :actions ((?g "Refresh" magit-diff-refresh) + (?t "Toggle hunk refinement" magit-diff-toggle-refine-hunk) + (?s "Set defaults" magit-diff-set-default-arguments) nil + (?w "Save defaults" magit-diff-save-default-arguments)) + :max-action-columns 2)) + +(magit-define-popup-keys-deferred 'magit-diff-popup) +(magit-define-popup-keys-deferred 'magit-diff-refresh-popup) +(magit-define-popup-keys-deferred 'magit-diff-mode-refresh-popup) +(magit-define-popup-keys-deferred 'magit-revision-mode-refresh-popup) + +(defcustom magit-diff-arguments '("--stat" "--no-ext-diff") + "The diff arguments used in buffers whose mode derives from `magit-diff-mode'." + :group 'magit-diff + :group 'magit-commands + :type '(repeat (string :tag "Argument"))) + +(defcustom magit-diff-section-arguments '("--no-ext-diff") + "The diff arguments used in buffers that show other things besides diffs." + :group 'magit-diff + :group 'magit-status + :type '(repeat (string :tag "Argument"))) + +(defvar magit-diff-section-file-args nil) +(put 'magit-diff-section-file-args 'permanent-local t) +(put 'magit-diff-section-arguments 'permanent-local t) + +(defun magit-diff-arguments (&optional refresh) + (cond ((memq magit-current-popup '(magit-diff-popup magit-diff-refresh-popup)) + (magit-popup-export-file-args magit-current-popup-args)) + ((derived-mode-p 'magit-diff-mode) + (list (nth 2 magit-refresh-args) + (nth 3 magit-refresh-args))) + (refresh + (list magit-diff-section-arguments + magit-diff-section-file-args)) + (t + (-if-let (buffer (magit-mode-get-buffer 'magit-diff-mode)) + (with-current-buffer buffer + (list (nth 2 magit-refresh-args) + (nth 3 magit-refresh-args))) + (list (default-value 'magit-diff-arguments) nil))))) + +(defun magit-diff-popup (arg) + "Popup console for diff commands." + (interactive "P") + (let ((magit-diff-arguments + ;; We cannot possibly know what suffix command the user is + ;; about to invoke, so we also don't know from which buffer + ;; we should get the current values. However it is much + ;; more likely that we will end up updating the diff buffer, + ;; and we therefore use the value from that buffer. + (-if-let (buffer (magit-mode-get-buffer 'magit-diff-mode)) + (with-current-buffer buffer + (magit-popup-import-file-args (nth 2 magit-refresh-args) + (nth 3 magit-refresh-args))) + (default-value 'magit-diff-arguments)))) + (magit-invoke-popup 'magit-diff-popup nil arg))) + +(defun magit-diff-refresh-popup (arg) + "Popup console for changing diff arguments in the current buffer." + (interactive "P") + (let ((magit-diff-refresh-popup + (pcase major-mode + (`magit-revision-mode magit-revision-mode-refresh-popup) + (`magit-diff-mode magit-diff-mode-refresh-popup) + (_ magit-diff-refresh-popup))) + (magit-diff-arguments + (if (derived-mode-p 'magit-diff-mode) + (magit-popup-import-file-args (nth 2 magit-refresh-args) + (nth 3 magit-refresh-args)) + (magit-popup-import-file-args magit-diff-section-arguments + magit-diff-section-file-args)))) + (magit-invoke-popup 'magit-diff-refresh-popup nil arg))) + +(defun magit-diff-select-algorithm (&rest _ignore) + (magit-read-char-case nil t + (?d "[d]efault" "default") + (?m "[m]inimal" "minimal") + (?p "[p]atience" "patience") + (?h "[h]istogram" "histogram"))) + +;;;###autoload +(defun magit-diff-dwim (&optional args files) + "Show changes for the thing at point." + (interactive (magit-diff-arguments)) + (pcase (magit-diff--dwim) + (`unstaged (magit-diff-unstaged args files)) + (`staged (magit-diff-staged nil args files)) + (`(commit . ,value) + (magit-diff (format "%s^..%s" value value) args files)) + (`(stash . ,value) (magit-stash-show value args)) + ((and range (pred stringp)) + (magit-diff range args files)) + (_ + (call-interactively #'magit-diff)))) + +(defun magit-diff--dwim () + "Return information for performing DWIM diff. + +The information can be in three forms: +1. TYPE + A symbol describing a type of diff where no additional information + is needed to generate the diff. Currently, this includes `staged' + and `unstaged'. +2. (TYPE . VALUE) + Like #1 but the diff requires additional information, which is + given by VALUE. Currently, this includes `commit' and `stash', + where VALUE is the given commit or stash, respectively. +3. RANGE + A string indicating a diff range. + +If no DWIM context is found, nil is returned." + (cond + ((--when-let (magit-region-values 'commit 'branch) + (deactivate-mark) + (concat (car (last it)) ".." (car it)))) + (magit-buffer-refname + (cons 'commit magit-buffer-refname)) + ((derived-mode-p 'magit-revision-mode) + (cons 'commit (car magit-refresh-args))) + ((derived-mode-p 'magit-diff-mode) + (nth 0 magit-refresh-args)) + (t + (magit-section-case + ([* unstaged] 'unstaged) + ([* staged] 'staged) + (unpushed (magit-section-value it)) + (unpulled (magit-section-value it)) + (branch (let ((current (magit-get-current-branch)) + (atpoint (magit-section-value it))) + (if (equal atpoint current) + (--if-let (magit-get-upstream-branch) + (format "%s...%s" it current) + (if (magit-anything-modified-p) + current + (cons 'commit current))) + (format "%s..%s" atpoint current)))) + (commit (cons 'commit (magit-section-value it))) + (stash (cons 'stash (magit-section-value it))))))) + +(defun magit-diff-read-range-or-commit (prompt &optional secondary-default mbase) + "Read range or revision with special diff range treatment. +If MBASE is non-nil, prompt for which rev to place at the end of +a \"revA...revB\" range. Otherwise, always construct +\"revA..revB\" range." + (--if-let (magit-region-values 'commit 'branch) + (let ((revA (car (last it))) + (revB (car it))) + (deactivate-mark) + (if mbase + (let ((base (magit-git-string "merge-base" revA revB))) + (cond + ((string= (magit-rev-parse revA) base) + (format "%s..%s" revA revB)) + ((string= (magit-rev-parse revB) base) + (format "%s..%s" revB revA)) + (t + (let ((main (magit-completing-read "View changes along" + (list revA revB) + nil t nil nil revB))) + (format "%s...%s" + (if (string= main revB) revA revB) main))))) + (format "%s..%s" revA revB))) + (magit-read-range prompt + (or (pcase (magit-diff--dwim) + (`(commit . ,value) + (format "%s^..%s" value value)) + ((and range (pred stringp)) + range)) + secondary-default + (magit-get-current-branch))))) + +(defun magit-diff-setup (rev-or-range const args files) + (require 'magit) + (magit-mode-setup #'magit-diff-mode rev-or-range const args files)) + +;;;###autoload +(defun magit-diff (rev-or-range &optional args files) + "Show differences between two commits. + +REV-OR-RANGE should be a range or a single revision. If it is a +revision, then show changes in the working tree relative to that +revision. If it is a range, but one side is omitted, then show +changes relative to `HEAD'. + +If the region is active, use the revisions on the first and last +line of the region as the two sides of the range. With a prefix +argument, instead of diffing the revisions, choose a revision to +view changes along, starting at the common ancestor of both +revisions (i.e., use a \"...\" range)." + (interactive (cons (magit-diff-read-range-or-commit "Diff for range" + nil current-prefix-arg) + (magit-diff-arguments))) + (magit-diff-setup rev-or-range nil args files)) + +;;;###autoload +(defun magit-diff-working-tree (&optional rev args files) + "Show changes between the current working tree and the `HEAD' commit. +With a prefix argument show changes between the working tree and +a commit read from the minibuffer." + (interactive + (cons (and current-prefix-arg + (magit-read-branch-or-commit "Diff working tree and commit")) + (magit-diff-arguments))) + (magit-diff-setup (or rev "HEAD") nil args files)) + +;;;###autoload +(defun magit-diff-staged (&optional rev args files) + "Show changes between the index and the `HEAD' commit. +With a prefix argument show changes between the index and +a commit read from the minibuffer." + (interactive + (cons (and current-prefix-arg + (magit-read-branch-or-commit "Diff index and commit")) + (magit-diff-arguments))) + (magit-diff-setup rev (list "--cached") args files)) + +;;;###autoload +(defun magit-diff-unstaged (&optional args files) + "Show changes between the working tree and the index." + (interactive (magit-diff-arguments)) + (magit-diff-setup nil nil args files)) + +;;;###autoload +(defun magit-diff-while-committing (&optional args files) + "While committing, show the changes that are about to be committed. +While amending, invoking the command again toggles between +showing just the new changes or all the changes that will +be committed." + (interactive (magit-diff-arguments)) + (let ((toplevel (magit-toplevel)) + (diff-buf (magit-mode-get-buffer 'magit-diff-mode))) + (if (magit-commit-message-buffer) + (if (and (or ;; most likely an explicit amend + (not (magit-anything-staged-p)) + ;; explicitly toggled from within diff + (and (eq (current-buffer) diff-buf))) + (or (not diff-buf) + (with-current-buffer diff-buf + (or ;; default to include last commit + (not (equal (magit-toplevel) toplevel)) + ;; toggle to include last commit + (not (car magit-refresh-args)))))) + (magit-diff-while-amending args files) + (magit-diff-staged nil args files)) + (user-error "No commit in progress")))) + +(define-key git-commit-mode-map + (kbd "C-c C-d") 'magit-diff-while-committing) + +(defun magit-diff-while-amending (&optional args files) + (magit-diff-setup "HEAD^" (list "--cached") args files)) + +;;;###autoload +(defun magit-diff-paths (a b) + "Show changes between any two files on disk." + (interactive (list (read-file-name "First file: " nil nil t) + (read-file-name "Second file: " nil nil t))) + (magit-diff-setup nil (list "--no-index") + nil (list (expand-file-name a) + (expand-file-name b)))) + +;;;###autoload +(defun magit-show-commit (rev &optional args files module) + "Show the revision at point. +If there is no revision at point or with a prefix argument prompt +for a revision." + (interactive + (let* ((mcommit (magit-section-when module-commit)) + (atpoint (or (and (bound-and-true-p magit-blame-mode) + (magit-blame-chunk-get :hash)) + mcommit + (magit-branch-or-commit-at-point) + (magit-tag-at-point)))) + (nconc (cons (or (and (not current-prefix-arg) atpoint) + (magit-read-branch-or-commit "Show commit" atpoint)) + (magit-diff-arguments)) + (and mcommit (list (magit-section-parent-value + (magit-current-section))))))) + (require 'magit) + (magit-with-toplevel + (when module + (setq default-directory + (expand-file-name (file-name-as-directory module)))) + (unless (magit-rev-verify-commit rev) + (user-error "%s is not a commit" rev)) + (-when-let (buffer (magit-mode-get-buffer 'magit-revision-mode)) + (with-current-buffer buffer + (let ((prev (car magit-refresh-args))) + (unless (equal rev prev) + (dolist (child (cdr (magit-section-children magit-root-section))) + (when (eq (magit-section-type child) 'file) + (magit-section-cache-visibility child))))))) + (magit-mode-setup #'magit-revision-mode rev nil args files))) + +(defun magit-diff-refresh (args files) + "Set the local diff arguments for the current buffer." + (interactive (magit-diff-arguments t)) + (cond ((derived-mode-p 'magit-diff-mode) + (setcdr (cdr magit-refresh-args) (list args files))) + (t + (setq-local magit-diff-section-arguments args) + (setq-local magit-diff-section-file-args files))) + (magit-refresh)) + +(defun magit-diff-set-default-arguments (args files) + "Set the global diff arguments for the current buffer." + (interactive (magit-diff-arguments t)) + (cond ((derived-mode-p 'magit-diff-mode) + (customize-set-variable 'magit-diff-arguments args) + (setcdr (cdr magit-refresh-args) (list args files))) + (t + (customize-set-variable 'magit-diff-section-arguments args) + (kill-local-variable 'magit-diff-section-arguments) + (kill-local-variable 'magit-diff-section-file-args))) + (magit-refresh)) + +(defun magit-diff-save-default-arguments (args files) + "Set and save the global diff arguments for the current buffer." + (interactive (magit-diff-arguments t)) + (cond ((derived-mode-p 'magit-diff-mode) + (customize-save-variable 'magit-diff-arguments args) + (setcdr (cdr magit-refresh-args) (list args files))) + (t + (customize-save-variable 'magit-diff-section-arguments args) + (kill-local-variable 'magit-diff-section-arguments) + (kill-local-variable 'magit-diff-section-file-args))) + (magit-refresh)) + +(defun magit-diff-switch-range-type () + "Convert diff range type. +Change \"revA..revB\" to \"revB...revA\", or vice versa." + (interactive) + (let ((range (car magit-refresh-args))) + (if (and range + (derived-mode-p 'magit-diff-mode) + (string-match magit-range-re range)) + (progn + (setcar magit-refresh-args + (concat (match-string 1 range) + (if (string= (match-string 2 range) "..") + "..." + "..") + (match-string 3 range))) + (magit-refresh)) + (user-error "No range to change")))) + +(defun magit-diff-flip-revs () + "Swap revisions in diff range. +Change \"revA..revB\" to \"revB..revA\"." + (interactive) + (let ((range (car magit-refresh-args))) + (if (and range + (derived-mode-p 'magit-diff-mode) + (string-match magit-range-re range)) + (progn + (setcar magit-refresh-args + (concat (match-string 3 range) + (match-string 2 range) + (match-string 1 range))) + (magit-refresh)) + (user-error "No range to swap")))) + +(defun magit-diff-less-context (&optional count) + "Decrease the context for diff hunks by COUNT lines." + (interactive "p") + (magit-diff-set-context `(lambda (cur) (max 0 (- (or cur 0) ,count))))) + +(defun magit-diff-more-context (&optional count) + "Increase the context for diff hunks by COUNT lines." + (interactive "p") + (magit-diff-set-context `(lambda (cur) (+ (or cur 0) ,count)))) + +(defun magit-diff-default-context () + "Reset context for diff hunks to the default height." + (interactive) + (magit-diff-set-context #'ignore)) + +(defun magit-diff-set-context (fn) + (let* ((def (--if-let (magit-get "diff.context") (string-to-number it) 3)) + (val (car (magit-diff-arguments t))) + (arg (--first (string-match "^-U\\([0-9]+\\)?$" it) val)) + (num (--if-let (and arg (match-string 1 arg)) (string-to-number it) def)) + (val (delete arg val)) + (num (funcall fn num)) + (arg (and num (not (= num def)) (format "-U%i" num))) + (val (if arg (cons arg val) val))) + (if (derived-mode-p 'magit-diff-mode) + (setcar (cddr magit-refresh-args) val) + (setq magit-diff-section-arguments val))) + (magit-refresh)) + +(defun magit-diff-context-p () + (--if-let (--first (string-match "^-U\\([0-9]+\\)$" it) + (car (magit-diff-arguments t))) + (not (equal "-U0" it)) + t)) + +(defun magit-diff-toggle-refine-hunk (&optional style) + "Turn diff-hunk refining on or off. + +If hunk refining is currently on, then hunk refining is turned off. +If hunk refining is off, then hunk refining is turned on, in +`selected' mode (only the currently selected hunk is refined). + +With a prefix argument, the \"third choice\" is used instead: +If hunk refining is currently on, then refining is kept on, but +the refining mode (`selected' or `all') is switched. +If hunk refining is off, then hunk refining is turned on, in +`all' mode (all hunks refined). + +Customize variable `magit-diff-refine-hunk' to change the default mode." + (interactive "P") + (setq-local magit-diff-refine-hunk + (if style + (if (eq magit-diff-refine-hunk 'all) t 'all) + (not magit-diff-refine-hunk))) + (magit-diff-update-hunk-refinement)) + +(defun magit-diff-visit-file (file &optional other-window force-worktree) + "From a diff, visit the corresponding file at the appropriate position. + +When the file is already being displayed in another window of the +same frame, then just select that window and adjust point. With +a prefix argument also display in another window. + +If the diff shows changes in the worktree, the index, or `HEAD', +then visit the actual file. Otherwise when the diff is about +an older commit, then visit the respective blob using +`magit-find-file'. Also see `magit-diff-visit-file-worktree' +which, as the name suggests always visits the actual file." + (interactive (list (--if-let (magit-file-at-point) + (expand-file-name it) + (user-error "No file at point")) + current-prefix-arg)) + (if (file-accessible-directory-p file) + (magit-diff-visit-directory file other-window) + (let ((current (magit-current-section)) + (rev (cond (force-worktree nil) + ((derived-mode-p 'magit-revision-mode) + (car magit-refresh-args)) + ((derived-mode-p 'magit-diff-mode) + (--when-let (car magit-refresh-args) + (and (string-match "\\.\\.\\([^.].*\\)?[ \t]*\\'" it) + (match-string 1 it)))))) + (unmerged-p (magit-anything-unmerged-p file)) + hunk line col buffer) + (when (and rev (magit-rev-head-p rev)) + (setq rev nil)) + (setq hunk + (pcase (magit-diff-scope) + ((or `hunk `region) current) + ((or `file `files) (car (magit-section-children current))) + (`list (car (magit-section-children + (car (magit-section-children current))))))) + (when (and hunk + ;; Currently the `hunk' type is also abused for file + ;; mode changes. Luckily such sections have no value. + (magit-section-value hunk)) + (setq line (magit-diff-hunk-line hunk) + col (magit-diff-hunk-column hunk))) + (setq buffer (if rev + (magit-find-file-noselect rev file) + (or (get-file-buffer file) + (find-file-noselect file)))) + (magit-display-file-buffer buffer) + (with-current-buffer buffer + (when line + (goto-char (point-min)) + (forward-line (1- line)) + (when col + (move-to-column col))) + (when unmerged-p + (smerge-start-session)) + (run-hooks 'magit-diff-visit-file-hook))))) + +(defvar magit-display-file-buffer-function + 'magit-display-file-buffer-traditional + "The function used by `magit-diff-visit-file' to display blob buffers. + +Other commands such as `magit-find-file' do not use this +function. Instead they use high-level functions to select the +window to be used to display the buffer. This variable and the +related functions are an experimental feature and should be +treated as such.") + +(defun magit-display-file-buffer (buffer) + (funcall magit-display-file-buffer-function buffer)) + +(defun magit-display-file-buffer-traditional (buffer) + (if (or current-prefix-arg (get-buffer-window buffer)) + (pop-to-buffer buffer) + (switch-to-buffer buffer))) + +(defun magit-diff-visit-file-worktree (file &optional other-window) + "From a diff, visit the corresponding file at the appropriate position. + +When the file is already being displayed in another window of the +same frame, then just select that window and adjust point. With +a prefix argument also display in another window. + +The actual file in the worktree is visited. The positions in the +hunk headers get less useful the \"older\" the changes are, and +as a result, jumping to the appropriate position gets less +reliable. + +Also see `magit-diff-visit-file' which visits the respective +blob, unless the diff shows changes in the worktree, the index, +or `HEAD'." + (interactive (list (or (magit-file-at-point) + (user-error "No file at point")) + current-prefix-arg)) + (magit-diff-visit-file file other-window t)) + +(defun magit-diff-hunk-line (section) + (let* ((value (magit-section-value section)) + (prefix (- (length value) 2)) + (cpos (marker-position (magit-section-content section))) + (stop (line-number-at-pos)) + (cstart (save-excursion (goto-char cpos) (line-number-at-pos))) + (line (car (last value)))) + (string-match "^\\+\\([0-9]+\\)" line) + (setq line (string-to-number (match-string 1 line))) + (when (> cstart stop) + (save-excursion + (goto-char cpos) + (re-search-forward "^[-+]") + (setq stop (line-number-at-pos)))) + (save-excursion + (goto-char cpos) + (while (< (line-number-at-pos) stop) + (unless (string-match-p + "-" (buffer-substring (point) (+ (point) prefix))) + (cl-incf line)) + (forward-line))) + line)) + +(defun magit-diff-hunk-column (section) + (if (or (< (point) (magit-section-content section)) + (save-excursion (beginning-of-line) (looking-at-p "-"))) + 0 + (max 0 (- (+ (current-column) 2) + (length (magit-section-value section)))))) + +(defun magit-diff-visit-directory (directory &optional other-window) + (if (equal (magit-toplevel directory) + (magit-toplevel)) + (magit-dired-jump other-window) + (let ((display-buffer-overriding-action + (if other-window + '(nil (inhibit-same-window t)) + '(display-buffer-same-window)))) + (magit-status-internal directory)))) + +(defun magit-diff-show-or-scroll-up () + "Update the commit or diff buffer for the thing at point. + +Either show the commit or stash at point in the appropriate +buffer, or if that buffer is already being displayed in the +current frame and contains information about that commit or +stash, then instead scroll the buffer up. If there is no +commit or stash at point, then prompt for a commit." + (interactive) + (magit-diff-show-or-scroll 'scroll-up)) + +(defun magit-diff-show-or-scroll-down () + "Update the commit or diff buffer for the thing at point. + +Either show the commit or stash at point in the appropriate +buffer, or if that buffer is already being displayed in the +current frame and contains information about that commit or +stash, then instead scroll the buffer down. If there is no +commit or stash at point, then prompt for a commit." + (interactive) + (magit-diff-show-or-scroll 'scroll-down)) + +(defun magit-diff-show-or-scroll (fn) + (let (rev cmd buf win) + (cond + (magit-blame-mode + (setq rev (magit-blame-chunk-get :hash) + cmd 'magit-show-commit + buf (magit-mode-get-buffer 'magit-revision-mode))) + ((derived-mode-p 'git-rebase-mode) + (save-excursion + (goto-char (line-beginning-position)) + (--if-let (and (looking-at git-rebase-line) + (match-string 2)) + (setq rev it + cmd 'magit-show-commit + buf (magit-mode-get-buffer 'magit-revision-mode)) + (user-error "No commit on this line")))) + (t + (magit-section-case + ((commit branch) + (setq rev (magit-section-value it) + cmd 'magit-show-commit + buf (magit-mode-get-buffer 'magit-revision-mode))) + (stash + (setq rev (magit-section-value it) + cmd 'magit-stash-show + buf (magit-mode-get-buffer 'magit-diff-mode)))))) + (if rev + (if (and buf + (setq win (get-buffer-window buf)) + (with-current-buffer buf + (equal (if (eq cmd 'magit-stash-show) + (concat rev "^2^.." rev) + rev) + (car magit-refresh-args)))) + (with-selected-window win + (condition-case nil + (funcall fn) + (error + (goto-char (pcase fn + (`scroll-up (point-min)) + (`scroll-down (point-max))))))) + (let ((magit-display-buffer-noselect t)) + (if (eq cmd 'magit-show-commit) + (apply #'magit-show-commit rev (magit-diff-arguments)) + (funcall cmd rev)))) + (call-interactively #'magit-show-commit)))) + +;;; Diff Mode + +(defvar magit-diff-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map "\C-c\C-d" 'magit-diff-while-committing) + (define-key map "\C-c\C-b" 'magit-go-backward) + (define-key map "\C-c\C-f" 'magit-go-forward) + (define-key map "\s" 'scroll-up) + (define-key map "\d" 'scroll-down) + (define-key map "j" 'magit-jump-to-diffstat-or-diff) + map) + "Keymap for `magit-diff-mode'.") + +(define-derived-mode magit-diff-mode magit-mode "Magit Diff" + "Mode for looking at a Git diff. + +This mode is documented in info node `(magit)Diff buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-visit-thing] to visit the hunk or file at point. + +Staging and applying changes is documented in info node +`(magit)Staging and unstaging' and info node `(magit)Applying'. + +\\Type \ +\\[magit-apply] to apply the change at point, \ +\\[magit-stage] to stage, +\\[magit-unstage] to unstage, \ +\\[magit-discard] to discard, or \ +\\[magit-reverse] to reverse it. + +\\{magit-diff-mode-map}" + :group 'magit-diff + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-diff-refresh-buffer (rev-or-range const _args files) + "Refresh the current `magit-diff-mode' buffer. + +In such buffers the buffer-local value of `magit-refresh-args' +has the same form as the arguments of this function. The value +is set in `magit-mode-setup'." + (setq header-line-format + (propertize + (if (member "--no-index" const) + (apply #'format " Differences between %s and %s" files) + (concat (if rev-or-range + (if (string-match-p "\\.\\." rev-or-range) + (format " Changes in %s" rev-or-range) + (format " Changes from %s to working tree" rev-or-range)) + (if (member "--cached" const) + " Staged changes" + " Unstaged changes")) + (pcase (length files) + (0) + (1 (concat " in file " (car files))) + (_ (concat " in files " + (mapconcat #'identity files ", ")))))) + 'face 'magit-header-line)) + (magit-insert-section (diffbuf) + (run-hook-with-args 'magit-diff-sections-hook rev-or-range))) + +(defun magit-insert-diff (rev-or-range) + "Insert the diff into this `magit-diff-mode' buffer." + (magit-git-wash #'magit-diff-wash-diffs + "diff" rev-or-range "-p" "--no-prefix" + (and (member "--stat" (nth 2 magit-refresh-args)) "--numstat") + (nth 1 magit-refresh-args) + (nth 2 magit-refresh-args) "--" + (nth 3 magit-refresh-args))) + +(defvar magit-file-section-map + (let ((map (make-sparse-keymap))) + (define-key map [C-return] 'magit-diff-visit-file-worktree) + (define-key map "\C-j" 'magit-diff-visit-file-worktree) + (define-key map [remap magit-visit-thing] 'magit-diff-visit-file) + (define-key map [remap magit-delete-thing] 'magit-discard) + (define-key map [remap magit-revert-no-commit] 'magit-reverse) + (define-key map "a" 'magit-apply) + (define-key map "C" 'magit-commit-add-log) + (define-key map "K" 'magit-file-untrack) + (define-key map "R" 'magit-file-rename) + (define-key map "s" 'magit-stage) + (define-key map "u" 'magit-unstage) + map) + "Keymap for `file' sections.") + +(defvar magit-hunk-section-map + (let ((map (make-sparse-keymap))) + (define-key map [C-return] 'magit-diff-visit-file-worktree) + (define-key map "\C-j" 'magit-diff-visit-file-worktree) + (define-key map [remap magit-visit-thing] 'magit-diff-visit-file) + (define-key map [remap magit-delete-thing] 'magit-discard) + (define-key map [remap magit-revert-no-commit] 'magit-reverse) + (define-key map "a" 'magit-apply) + (define-key map "C" 'magit-commit-add-log) + (define-key map "s" 'magit-stage) + (define-key map "u" 'magit-unstage) + map) + "Keymap for `hunk' sections.") + +(defconst magit-diff-headline-re + (concat "^\\(@@@?\\|diff\\|Submodule\\|" + "\\* Unmerged path\\|merged\\|changed in both\\|" + "added in remote\\|removed in remote\\)")) + +(defconst magit-diff-statline-re + (concat "^ ?" + "\\(.*\\)" ; file + "\\( +| +\\)" ; separator + "\\([0-9]+\\|Bin\\(?: +[0-9]+ -> [0-9]+ bytes\\)?$\\) ?" + "\\(\\+*\\)" ; add + "\\(-*\\)$")) ; del + +(defconst magit-diff-submodule-re + (concat "^Submodule \\([^ ]+\\) \\(?:" + "\\([^ ]+ (new submodule)\\)\\|" + "\\([^ ]+ (submodule deleted)\\)\\|" + "\\(contains \\(?:modified\\|untracked\\) content\\)\\|" + "\\([^ :]+\\)\\( (rewind)\\)?:\\)$")) + +(defun magit-diff-wash-diffs (args &optional limit) + (when (member "--stat" args) + (magit-diff-wash-diffstat)) + (when (re-search-forward magit-diff-headline-re limit t) + (goto-char (line-beginning-position)) + (magit-wash-sequence (apply-partially 'magit-diff-wash-diff args)) + (insert ?\n))) + +(defun magit-jump-to-diffstat-or-diff () + "Jump to the diffstat or diff. +When point is on a file inside the diffstat section, then jump +to the respective diff section, otherwise jump to the diffstat +section or a child thereof." + (interactive) + (--if-let (magit-get-section + (append (magit-section-case + ([file diffstat] `((file . ,(magit-section-value it)))) + (file `((file . ,(magit-section-value it)) (diffstat))) + (t '((diffstat)))) + (magit-section-ident magit-root-section))) + (magit-section-goto it) + (user-error "No diffstat in this buffer"))) + +(defun magit-diff-wash-diffstat () + (let (heading (beg (point))) + (when (re-search-forward "^ ?\\([0-9]+ +files? change[^\n]*\n\\)" nil t) + (setq heading (match-string 1)) + (magit-delete-match) + (goto-char beg) + (magit-insert-section it (diffstat) + (insert (propertize heading 'face 'magit-diff-file-heading)) + (magit-insert-heading) + (let (files) + (while (looking-at "^[-0-9]+\t[-0-9]+\t\\(.+\\)$") + (push (magit-decode-git-path (match-string 1)) files) + (magit-delete-line)) + (setq files (nreverse files)) + (while (looking-at magit-diff-statline-re) + (magit-bind-match-strings (file sep cnt add del) nil + (magit-delete-line) + (when (string-match " +$" file) + (setq sep (concat (match-string 0 file) sep)) + (setq file (substring file 0 (match-beginning 0)))) + (let ((le (length file)) ld) + (setq file (magit-decode-git-path file)) + (setq ld (length file)) + (when (> le ld) + (setq sep (concat (make-string (- le ld) ?\s) sep)))) + (magit-insert-section (file (pop files)) + (insert (propertize file 'face 'magit-filename) sep cnt " ") + (when add + (insert (propertize add 'face 'magit-diffstat-added))) + (when del + (insert (propertize del 'face 'magit-diffstat-removed))) + (insert "\n"))))) + (if (looking-at "^$") (forward-line) (insert "\n")))))) + +(defun magit-diff-wash-diff (args) + (cond + ((looking-at magit-diff-submodule-re) + (magit-diff-wash-submodule)) + ((looking-at "^\\* Unmerged path \\(.*\\)") + (let ((file (magit-decode-git-path (match-string 1)))) + (magit-delete-line) + (unless (and (derived-mode-p 'magit-status-mode) + (not (member "--cached" args))) + (magit-insert-section (file file) + (insert (propertize + (format "unmerged %s%s" file + (pcase (cddr (car (magit-file-status file))) + (`(?D ?D) " (both deleted)") + (`(?D ?U) " (deleted by us)") + (`(?U ?D) " (deleted by them)") + (`(?A ?A) " (both added)") + (`(?A ?U) " (added by us)") + (`(?U ?A) " (added by them)") + (`(?U ?U) ""))) + 'face 'magit-diff-file-heading)) + (insert ?\n)))) + t) + ((looking-at (concat "^\\(merged\\|changed in both\\|" + "added in remote\\|removed in remote\\)")) + (let ((status (pcase (match-string 1) + ("merged" "merged") + ("changed in both" "conflict") + ("added in remote" "new file") + ("removed in remote" "deleted"))) + file orig base modes) + (magit-delete-line) + (while (looking-at + "^ \\([^ ]+\\) +[0-9]\\{6\\} \\([a-z0-9]\\{40\\}\\) \\(.+\\)$") + (magit-bind-match-strings (side _blob name) nil + (pcase side + ("result" (setq file name)) + ("our" (setq orig name)) + ("their" (setq file name)) + ("base" (setq base name)))) + (magit-delete-line)) + (when orig (setq orig (magit-decode-git-path orig))) + (when file (setq file (magit-decode-git-path file))) + (magit-diff-insert-file-section (or file base) orig status modes nil))) + ((looking-at + "^diff --\\(?:\\(git\\) \\(?:\\(.+?\\) \\2\\)?\\|\\(cc\\|combined\\) \\(.+\\)\\)") + (let ((status (cond ((equal (match-string 1) "git") "modified") + ((derived-mode-p 'magit-revision-mode) "resolved") + (t "unmerged"))) + (file (or (match-string 2) (match-string 4))) + (beg (point)) + orig header modes) + (save-excursion + (forward-line 1) + (setq header (buffer-substring + beg (if (re-search-forward magit-diff-headline-re nil t) + (match-beginning 0) + (point-max))))) + (magit-delete-line) + (while (not (or (eobp) (looking-at magit-diff-headline-re))) + (if (looking-at "^old mode \\([^\n]+\\)\nnew mode \\([^\n]+\\)\n") + (progn (setq modes (match-string 0)) + (magit-delete-match)) + (cond + ((looking-at "^--- \\([^/].*?\\)\t?$") ; i.e. not /dev/null + (setq orig (match-string 1))) + ((looking-at "^\\+\\+\\+ \\([^/].*?\\)\t?$") + (setq file (match-string 1))) + ((looking-at "^\\(copy\\|rename\\) from \\(.+\\)$") + (setq orig (match-string 2))) + ((looking-at "^\\(copy\\|rename\\) to \\(.+\\)$") + (setq file (match-string 2)) + (setq status (if (equal (match-string 1) "copy") "new file" "renamed"))) + ((looking-at "^\\(new file\\|deleted\\)") + (setq status (match-string 1)))) + (magit-delete-line))) + (when orig + (setq orig (magit-decode-git-path orig))) + (setq file (magit-decode-git-path file)) + ;; KLUDGE `git-log' ignores `--no-prefix' when `-L' is used. + (when (derived-mode-p 'magit-log-mode) + (setq file (substring file 2)) + (when orig + (setq orig (substring orig 2)))) + (magit-diff-insert-file-section file orig status modes header))))) + +(defun magit-diff-insert-file-section (file orig status modes header) + (magit-insert-section section + (file file (or (equal status "deleted") + (derived-mode-p 'magit-status-mode))) + (insert (propertize (format "%-10s %s\n" status + (if (or (not orig) (equal orig file)) + file + (format "%s -> %s" orig file))) + 'face 'magit-diff-file-heading)) + (magit-insert-heading) + (unless (equal orig file) + (setf (magit-section-source section) orig)) + (setf (magit-section-diff-header section) header) + (when modes + (magit-insert-section (hunk) + (insert modes))) + (magit-wash-sequence #'magit-diff-wash-hunk))) + +(defun magit-diff-wash-submodule () + (magit-bind-match-strings (module new deleted dirty range rewind) nil + (magit-delete-line) + (when (and dirty + (looking-at magit-diff-submodule-re) + (string= (match-string 1) module)) + (setq range (match-string 5)) + (magit-delete-line)) + (while (looking-at "^ \\([<>]\\) \\(.+\\)$") + (magit-delete-line)) + (if range + (let ((default-directory + (file-name-as-directory + (expand-file-name module (magit-toplevel))))) + (setf (magit-section-value + (magit-insert-section (file module t) + (magit-insert-heading + (concat (propertize (concat "modified " module) + 'face 'magit-diff-file-heading) + " (" + (if rewind "rewind" "new commits") + (and dirty ", modified content") + ")")) + (unless rewind + (magit-git-wash + (apply-partially 'magit-log-wash-log 'module) + "log" "--oneline" "--left-right" range) + (delete-char -1)))) + module)) + (magit-insert-section (file module) + (insert (propertize (if new + (concat "new module " module) + (concat "modified " module)) + 'face 'magit-diff-file-heading)) + (cond (dirty (insert " (modified content)")) + (deleted (insert " (deleted submodule)"))) + (insert ?\n))))) + +(defun magit-diff-wash-hunk () + (when (looking-at "^@\\{2,\\} \\(.+?\\) @\\{2,\\}\\(?: \\(.*\\)\\)?") + (let ((heading (match-string 0)) + (value (cons (match-string 2) (split-string (match-string 1))))) + (magit-delete-line) + (magit-insert-section it (hunk value) + (insert (propertize (concat heading "\n") 'face 'magit-diff-hunk-heading)) + (magit-insert-heading) + (while (not (or (eobp) (looking-at "^[^-+\s\\]"))) + (forward-line)) + (setf (magit-section-end it) (point)) + (setf (magit-section-washer it) #'magit-diff-paint-hunk))) + t)) + +(defun magit-diff-expansion-threshold (section) + "Keep new diff sections collapsed if washing takes too long." + (and (memq (magit-section-type section) '(file)) + (> (float-time (time-subtract (current-time) magit-refresh-start-time)) + magit-diff-expansion-threshold) + 'hide)) + +;;; Revision Mode + +(define-derived-mode magit-revision-mode magit-diff-mode "Magit Rev" + "Mode for looking at a Git commit. + +This mode is documented in info node `(magit)Revision buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-visit-thing] to visit the hunk or file at point. + +Staging and applying changes is documented in info node +`(magit)Staging and unstaging' and info node `(magit)Applying'. + +\\Type \ +\\[magit-apply] to apply the change at point, \ +\\[magit-stage] to stage, +\\[magit-unstage] to unstage, \ +\\[magit-discard] to discard, or \ +\\[magit-reverse] to reverse it. + +\\{magit-revision-mode-map}" + :group 'magit-revision + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-revision-refresh-buffer (rev __const _args _files) + (setq header-line-format + (propertize (format " %s %s" (capitalize (magit-object-type rev)) rev) + 'face 'magit-header-line)) + (magit-insert-section (commitbuf) + (run-hook-with-args 'magit-revision-sections-hook rev))) + +(defun magit-insert-revision-diff (rev) + "Insert the diff into this `magit-revision-mode' buffer." + ;; Before v2.2.0, "--format=" did not mean "no output". + ;; Instead the default format was used. So use "--format=%n" + ;; and then delete the empty lines. + (magit-git-wash (lambda (args) + (delete-region (point) (progn (forward-line 3) (point))) + (magit-diff-wash-diffs args)) + "show" "-p" "--cc" "--format=%n" "--no-prefix" + (and (member "--stat" (nth 2 magit-refresh-args)) "--numstat") + (nth 2 magit-refresh-args) (concat rev "^{commit}") "--" + (nth 3 magit-refresh-args))) + +(defun magit-insert-revision-tag (rev) + "Insert tag message and headers into a revision buffer. +This function only inserts anything when `magit-show-commit' is +called with a tag as argument, when that is called with a commit +or a ref which is not a branch, then it inserts nothing." + (when (equal (magit-object-type rev) "tag") + (magit-insert-section (taginfo) + (let ((beg (point))) + (magit-git-insert "cat-file" "tag" rev) + (goto-char beg) + (forward-line 3) + (delete-region beg (point))) + (looking-at "^tagger \\([^<]+\\) <\\([^>]+\\)") + (let ((heading (format "Tagger: %s <%s>" + (match-string 1) + (match-string 2)))) + (magit-delete-line) + (insert (propertize heading 'face 'magit-section-secondary-heading))) + (magit-insert-heading) + (goto-char (point-max)) + (insert ?\n)))) + +(defun magit-insert-revision-message (rev) + "Insert the commit message into a revision buffer." + (magit-insert-section (message) + (let ((beg (point))) + (magit-rev-insert-format "%B" rev) + (if (= (point) (+ beg 2)) + (progn (backward-delete-char 2) + (insert "(no message)\n")) + (goto-char beg) + (forward-line) + (put-text-property beg (point) 'face 'magit-section-secondary-heading) + (magit-insert-heading) + (goto-char (point-max)))))) + +(defun magit-insert-revision-notes (rev) + "Insert commit notes into a revision buffer." + (magit-insert-section (notes) + (let ((beg (point))) + (magit-git-insert "notes" "show" rev) + (if (= (point) beg) + (magit-cancel-section) + (goto-char beg) + (forward-line) + (put-text-property beg (point) 'face 'magit-section-secondary-heading) + (magit-insert-heading) + (goto-char (point-max)) + (insert ?\n))))) + +(defun magit-insert-revision-headers (rev) + "Insert headers about the commit into a revision buffer." + (magit-insert-section (headers) + ;; Before v2.2.0, "%D" was not supported. + (--when-let (magit-rev-format "%d" rev "--decorate=full") + (insert (magit-format-ref-labels (substring it 2 -1)) ?\s)) + (insert (propertize (magit-rev-parse (concat rev "^{commit}")) + 'face 'magit-hash)) + (magit-insert-heading) + (let ((beg (point))) + (magit-rev-insert-format magit-revision-headers-format rev) + (magit-insert-revision-gravatars rev beg)) + (when magit-revision-insert-related-refs + (dolist (parent (magit-commit-parents rev)) + (magit-insert-section (commit parent) + (let ((line (magit-rev-format "%h %s" parent))) + (string-match "^\\([^ ]+\\) \\(.*\\)" line) + (magit-bind-match-strings (hash msg) line + (insert "Parent: ") + (insert (propertize hash 'face 'magit-hash)) + (insert " " msg "\n"))))) + (-when-let (merged (magit-list-merged-branches rev)) + (insert "Merged: ") + (let (branch) + (while (and (< (+ (- (point) (line-beginning-position)) + (length (car merged)) 9) + (window-width)) + (setq branch (pop merged))) + (insert ?\s) + (magit-insert-section (branch branch) + (insert (propertize branch 'face 'magit-branch-local))))) + (when merged + (insert (format " (%s more)" (length merged)))) + (insert ?\n)) + (-when-let (containing (magit-list-containing-branches rev)) + (insert "Containing:") + (let (branch) + (while (and (< (+ (- (point) (line-beginning-position)) + (length (car containing)) 9) + (window-width)) + (setq branch (pop containing))) + (insert ?\s) + (magit-insert-section (branch branch) + (insert (propertize branch 'face 'magit-branch-local))))) + (when containing + (insert (format " (%s more)" (length containing)))) + (insert ?\n)) + (-when-let (follows (magit-get-current-tag rev t)) + (let ((tag (car follows)) + (cnt (cadr follows))) + (magit-insert-section (tag tag) + (insert (format "Follows: %s (%s)\n" + (propertize tag 'face 'magit-tag) + (propertize (number-to-string cnt) + 'face 'magit-branch-local)))))) + (-when-let (precedes (magit-get-next-tag rev t)) + (let ((tag (car precedes)) + (cnt (cadr precedes))) + (magit-insert-section (tag tag) + (insert (format "Precedes: %s (%s)\n" + (propertize tag 'face 'magit-tag) + (propertize (number-to-string cnt) + 'face 'magit-tag)))))) + (insert ?\n)))) + +(defun magit-insert-revision-gravatars (rev beg) + (when (and magit-revision-show-gravatars (window-system)) + (require 'gravatar) + (magit-insert-revision-gravatar beg (magit-rev-format "%aE" rev) + (car magit-revision-show-gravatars)) + (magit-insert-revision-gravatar beg (magit-rev-format "%cE" rev) + (cdr magit-revision-show-gravatars)) + (goto-char (point-max)))) + +(defun magit-insert-revision-gravatar (beg email regexp) + (when (and email (goto-char beg) (re-search-forward regexp nil t)) + (ignore-errors + (let* ((offset (length (match-string 0))) + (font-obj (query-font (font-at (point) (get-buffer-window)))) + (size (* 2 (+ (aref font-obj 4) (aref font-obj 5)))) + (align-to (+ offset (ceiling (/ size (aref font-obj 7) 1.0)))) + (gravatar-size (- size 2)) + (slice1 '(slice .0 .0 1.0 0.5)) + (slice2 '(slice .0 .5 1.0 1.0))) + (gravatar-retrieve + email + (lambda (image offset align-to slice1 slice2) + (unless (eq image 'error) + (insert (propertize " " 'display `((,@image :ascent center :relief 1) + ,slice1))) + (insert (propertize " " 'display `((space :align-to ,align-to)))) + (forward-line) + (forward-char offset) + (insert (propertize " " 'display `((,@image :ascent center :relief 1) + ,slice2))) + (insert (propertize " " 'display `((space :align-to ,align-to)))))) + (list offset align-to + (if magit-revision-use-gravatar-kludge slice2 slice1) + (if magit-revision-use-gravatar-kludge slice1 slice2))))))) + +;;; Diff Sections + +(defvar magit-unstaged-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-visit-thing] 'magit-diff-unstaged) + (define-key map [remap magit-delete-thing] 'magit-discard) + (define-key map "s" 'magit-stage) + (define-key map "u" 'magit-unstage) + map) + "Keymap for the `unstaged' section.") + +(magit-define-section-jumper magit-jump-to-unstaged "Unstaged changes" unstaged) + +(defun magit-insert-unstaged-changes () + "Insert section showing unstaged changes." + (magit-insert-section (unstaged) + (magit-insert-heading "Unstaged changes:") + (magit-git-wash #'magit-diff-wash-diffs + "diff" magit-diff-section-arguments "--no-prefix" + "--" magit-diff-section-file-args))) + +(defvar magit-staged-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-visit-thing] 'magit-diff-staged) + (define-key map [remap magit-delete-thing] 'magit-discard) + (define-key map [remap magit-revert-no-commit] 'magit-reverse) + (define-key map "s" 'magit-stage) + (define-key map "u" 'magit-unstage) + map) + "Keymap for the `staged' section.") + +(magit-define-section-jumper magit-jump-to-staged "Staged changes" staged) + +(defun magit-insert-staged-changes () + "Insert section showing staged changes." + (magit-insert-section (staged) + (magit-insert-heading "Staged changes:") + (magit-git-wash #'magit-diff-wash-diffs + "diff" "--cached" magit-diff-section-arguments "--no-prefix" + "--" magit-diff-section-file-args))) + +;;; Diff Type + +(defun magit-diff-type (&optional section) + "Return the diff type of SECTION. + +The returned type is one of the symbols `staged', `unstaged', +`committed', or `undefined'. This type serves a similar purpose +as the general type common to all sections (which is stored in +the `type' slot of the corresponding `magit-section' struct) but +takes additional information into account. When the SECTION +isn't related to diffs and the buffer containing it also isn't +a diff-only buffer, then return nil. + +Currently the type can also be one of `tracked' and `untracked' +but these values are not handled explicitly everywhere they +should be and a possible fix could be to just return nil here. + +The section has to be a `diff' or `hunk' section, or a section +whose children are of type `diff'. If optional SECTION is nil, +return the diff type for the current section. In buffers whose +major mode is `magit-diff-mode' SECTION is ignored and the type +is determined using other means. In `magit-revision-mode' +buffers the type is always `committed'. + +Do not confuse this with `magit-diff-scope' (which see)." + (--when-let (or section (magit-current-section)) + (cond ((derived-mode-p 'magit-revision-mode 'magit-stash-mode) 'committed) + ((derived-mode-p 'magit-diff-mode) + (let ((range (nth 0 magit-refresh-args)) + (const (nth 1 magit-refresh-args))) + (cond ((member "--no-index" const) 'undefined) + ((not range) + (if (member "--cached" const) + 'staged + 'unstaged)) + ((member "--cached" const) + (if (magit-rev-head-p range) + 'staged + 'undefined)) ; i.e. committed and staged + (t 'committed)))) + ((derived-mode-p 'magit-status-mode) + (let ((stype (magit-section-type it))) + (if (memq stype '(staged unstaged tracked untracked)) + stype + (pcase stype + (`file (let* ((parent (magit-section-parent it)) + (type (magit-section-type parent))) + (if (eq type 'file) + (magit-diff-type parent) + type))) + (`hunk (-> it magit-section-parent magit-section-parent + magit-section-type)))))) + ((derived-mode-p 'magit-log-mode) + (if (or (and (magit-section-match 'commit section) + (magit-section-children section)) + (magit-section-match [* file commit] section)) + 'committed + 'undefined)) + (t 'undefined)))) + +(cl-defun magit-diff-scope (&optional (section nil ssection) strict) + "Return the diff scope of SECTION or the selected section(s). + +A diff's \"scope\" describes what part of a diff is selected, it is +a symbol, one of `region', `hunk', `hunks', `file', `files', or +`list'. Do not confuse this with the diff \"type\", as returned by +`magit-diff-type'. + +If optional SECTION is non-nil, then return the scope of that, +ignoring the sections selected by the region. Otherwise return +the scope of the current section, or if the region is active and +selects a valid group of diff related sections, the type of these +sections, i.e. `hunks' or `files'. If SECTION, or if that is nil +the current section, is a `hunk' section; and the region region +starts and ends inside the body of a that section, then the type +is `region'. + +If optional STRICT is non-nil then return nil if the diff type of +the section at point is `untracked' or the section at point is not +actually a `diff' but a `diffstat' section." + (let ((siblings (and (not ssection) (magit-region-sections)))) + (setq section (or section (car siblings) (magit-current-section))) + (when (and section + (or (not strict) + (and (not (eq (magit-diff-type section) 'untracked)) + (not (eq (--when-let (magit-section-parent section) + (magit-section-type it)) + 'diffstat))))) + (pcase (list (magit-section-type section) + (and siblings t) + (and (region-active-p) t) + ssection) + (`(hunk nil t ,_) + (if (magit-section-internal-region-p section) 'region 'hunk)) + (`(hunk t t nil) 'hunks) + (`(hunk ,_ ,_ ,_) 'hunk) + (`(file t t nil) 'files) + (`(file ,_ ,_ ,_) 'file) + (`(,(or `staged `unstaged `untracked) + nil ,_ ,_) 'list))))) + +;;; Diff Highlight + +(defun magit-diff-unhighlight (section selection) + "Remove the highlighting of the diff-related SECTION." + (when (eq (magit-section-type section) 'hunk) + (magit-diff-paint-hunk section selection nil) + t)) + +(defun magit-diff-highlight (section selection) + "Highlight the diff-related SECTION and return t. +If SECTION is not a diff-related section, then do nothing and +return nil. If SELECTION is non-nil then it is a list of sections +selected by the region, including SECTION. All of these sections +are highlighted." + (if (and (magit-section-match 'commit section) + (magit-section-children section)) + (progn (if selection + (dolist (section selection) + (magit-diff-highlight-list section selection)) + (magit-diff-highlight-list section)) + t) + (-when-let (scope (magit-diff-scope section t)) + (cond ((eq scope 'region) + (magit-diff-paint-hunk section selection t)) + (selection + (dolist (section selection) + (magit-diff-highlight-recursive section selection))) + (t + (magit-diff-highlight-recursive section))) + t))) + +(defun magit-diff-highlight-recursive (section &optional selection) + (pcase (magit-diff-scope section) + (`list (magit-diff-highlight-list section selection)) + (`file (magit-diff-highlight-file section selection)) + (`hunk (magit-diff-highlight-heading section selection) + (magit-diff-paint-hunk section selection t)) + (_ (magit-section-highlight section nil)))) + +(defun magit-diff-highlight-list (section &optional selection) + (let ((beg (magit-section-start section)) + (cnt (magit-section-content section)) + (end (magit-section-end section))) + (unless (and (region-active-p) + (= end (1+ (region-end)))) + (magit-section-make-overlay beg cnt 'magit-section-highlight) + (unless (magit-section-hidden section) + (dolist (child (magit-section-children section)) + (magit-diff-highlight-recursive child selection)))) + (when magit-diff-highlight-hunk-body + (magit-section-make-overlay (1- end) end 'magit-section-highlight)))) + +(defun magit-diff-highlight-file (section &optional selection) + (magit-diff-highlight-heading section selection) + (unless (magit-section-hidden section) + (dolist (child (magit-section-children section)) + (magit-diff-highlight-recursive child selection)))) + +(defun magit-diff-highlight-heading (section &optional selection) + (magit-section-make-overlay + (magit-section-start section) + (or (magit-section-content section) + (magit-section-end section)) + (pcase (list (magit-section-type section) + (and (member section selection) t)) + (`(file t) 'magit-diff-file-heading-selection) + (`(file nil) 'magit-diff-file-heading-highlight) + (`(hunk t) 'magit-diff-hunk-heading-selection) + (`(hunk nil) 'magit-diff-hunk-heading-highlight)))) + +;;; Hunk Paint + +(cl-defun magit-diff-paint-hunk + (section &optional selection + (highlight (magit-section-selected-p section selection))) + (let (paint) + (unless magit-diff-highlight-hunk-body + (setq highlight nil)) + (cond (highlight + (unless (magit-section-hidden section) + (add-to-list 'magit-section-highlighted-sections section) + (cond ((memq section magit-section-unhighlight-sections) + (setq magit-section-unhighlight-sections + (delq section magit-section-unhighlight-sections))) + (magit-diff-highlight-hunk-body + (setq paint t))))) + (t + (cond ((and (magit-section-hidden section) + (memq section magit-section-unhighlight-sections)) + (add-to-list 'magit-section-highlighted-sections section) + (setq magit-section-unhighlight-sections + (delq section magit-section-unhighlight-sections))) + (t + (setq paint t))))) + (when paint + (save-excursion + (goto-char (magit-section-start section)) + (let ((end (magit-section-end section)) + (merging (looking-at "@@@")) + (stage nil)) + (forward-line) + (while (< (point) end) + (put-text-property + (point) (1+ (line-end-position)) 'face + (cond + ((looking-at "^\\+\\+?\\([<=|>]\\)\\{7\\}") + (setq stage (pcase (list (match-string 1) highlight) + (`("<" nil) 'magit-diff-our) + (`("<" t) 'magit-diff-our-highlight) + (`("|" nil) 'magit-diff-base) + (`("|" t) 'magit-diff-base-highlight) + (`("=" nil) 'magit-diff-their) + (`("=" t) 'magit-diff-their-highlight) + (`(">" nil) nil))) + 'magit-diff-conflict-heading) + ((looking-at (if merging "^\\(\\+\\| \\+\\)" "^\\+")) + (magit-diff-paint-whitespace merging) + (or stage + (if highlight 'magit-diff-added-highlight 'magit-diff-added))) + ((looking-at (if merging "^\\(-\\| -\\)" "^-")) + (if highlight 'magit-diff-removed-highlight 'magit-diff-removed)) + (t + (if highlight 'magit-diff-context-highlight 'magit-diff-context)))) + (forward-line)))))) + (magit-diff-update-hunk-refinement section)) + +(defun magit-diff-paint-whitespace (merging) + (when (and magit-diff-paint-whitespace + (or (derived-mode-p 'magit-status-mode) + (not (eq magit-diff-paint-whitespace 'status)))) + (let ((prefix (if merging "^[-\\+\s]\\{2\\}" "^[-\\+]")) + (indent + (if (local-variable-p 'magit-diff-highlight-indentation) + magit-diff-highlight-indentation + (setq-local + magit-diff-highlight-indentation + (cdr (--first (string-match-p (car it) default-directory) + (nreverse + (default-value + 'magit-diff-highlight-indentation)))))))) + (when (and magit-diff-highlight-trailing + (looking-at (concat prefix ".*?\\([ \t]+\\)$"))) + (let ((ov (make-overlay (match-beginning 1) (match-end 1) nil t))) + (overlay-put ov 'face 'magit-diff-whitespace-warning) + (overlay-put ov 'evaporate t))) + (when (or (and (eq indent 'tabs) + (looking-at (concat prefix "\\( *\t[ \t]*\\)"))) + (and (integerp indent) + (looking-at (format "%s\\([ \t]* \\{%s,\\}[ \t]*\\)" + prefix indent)))) + (let ((ov (make-overlay (match-beginning 1) (match-end 1) nil t))) + (overlay-put ov 'face 'magit-diff-whitespace-warning) + (overlay-put ov 'evaporate t)))))) + +(defun magit-diff-update-hunk-refinement (&optional section) + (if section + (unless (magit-section-hidden section) + (pcase (list magit-diff-refine-hunk + (magit-section-refined section) + (eq section (magit-current-section))) + ((or `(all nil ,_) `(t nil t)) + (setf (magit-section-refined section) t) + (save-excursion + (goto-char (magit-section-start section)) + ;; `diff-refine-hunk' does not handle combined diffs. + (unless (looking-at "@@@") + (diff-refine-hunk)))) + ((or `(nil t ,_) `(t t nil)) + (setf (magit-section-refined section) nil) + (remove-overlays (magit-section-start section) + (magit-section-end section) + 'diff-mode 'fine)))) + (cl-labels ((recurse (section) + (if (magit-section-match 'hunk section) + (magit-diff-update-hunk-refinement section) + (--each (magit-section-children section) + (recurse it))))) + (recurse magit-root-section)))) + + +;;; Highlight Region + +(defvar magit-diff-unmarked-lines-keep-foreground t) + +(defun magit-diff-update-hunk-region (section) + (when (and (eq (magit-diff-scope section t) 'region) + (not (and (eq this-command 'mouse-drag-region) + (eq (mark) (point))))) + (let ((sbeg (magit-section-start section)) + (cbeg (magit-section-content section)) + (rbeg (save-excursion (goto-char (region-beginning)) + (line-beginning-position))) + (rend (save-excursion (goto-char (region-end)) + (line-end-position))) + (send (magit-section-end section)) + (face (if magit-diff-highlight-hunk-body + 'magit-diff-context-highlight + 'magit-diff-context))) + (when magit-diff-unmarked-lines-keep-foreground + (setq face (list :background (face-attribute face :background)))) + (cl-flet ((ov (start end &rest args) + (let ((ov (make-overlay start end nil t))) + (overlay-put ov 'evaporate t) + (while args (overlay-put ov (pop args) (pop args))) + (push ov magit-region-overlays) + ov))) + (ov sbeg cbeg 'face 'magit-diff-lines-heading + 'display (concat (magit-diff-hunk-region-header section) "\n")) + (ov cbeg rbeg 'face face 'priority 2) + (when (and (window-system) magit-diff-show-lines-boundary) + (ov rbeg (1+ rbeg) 'before-string + (propertize (concat (propertize "\s" 'display '(space :height (1))) + (propertize "\n" 'line-height t)) + 'face 'magit-diff-lines-boundary)) + (ov rend (1+ rend) 'after-string + (propertize (concat (propertize "\s" 'display '(space :height (1))) + (propertize "\n" 'line-height t)) + 'face 'magit-diff-lines-boundary))) + (ov (1+ rend) send 'face face 'priority 2))))) + +;;; Diff Extract + +(defun magit-diff-file-header (section) + (when (eq (magit-section-type section) 'hunk) + (setq section (magit-section-parent section))) + (when (eq (magit-section-type section) 'file) + (magit-section-diff-header section))) + +(defun magit-diff-hunk-region-header (section) + (let ((patch (magit-diff-hunk-region-patch section))) + (string-match "\n" patch) + (substring patch 0 (1- (match-end 0))))) + +(defun magit-diff-hunk-region-patch (section &optional args) + (let ((op (if (member "--reverse" args) "+" "-")) + (sbeg (magit-section-start section)) + (rbeg (save-excursion + (goto-char (region-beginning)) + (line-beginning-position))) + (rend (region-end)) + (send (magit-section-end section)) + (patch nil)) + (save-excursion + (goto-char sbeg) + (while (< (point) send) + (looking-at "\\(.\\)\\([^\n]*\n\\)") + (cond ((or (string-match-p "[@ ]" (match-string-no-properties 1)) + (and (>= (point) rbeg) + (<= (point) rend))) + (push (match-string-no-properties 0) patch)) + ((equal op (match-string-no-properties 1)) + (push (concat " " (match-string-no-properties 2)) patch))) + (forward-line))) + (with-temp-buffer + (insert (mapconcat 'identity (reverse patch) "")) + (diff-fixup-modifs (point-min) (point-max)) + (setq patch (buffer-string))) + patch)) + +;;; magit-diff.el ends soon +(provide 'magit-diff) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-diff.el ends here diff --git a/elpa/magit-20160223.828/magit-ediff.el b/elpa/magit-20160223.828/magit-ediff.el new file mode 100644 index 0000000..4e06ccf --- /dev/null +++ b/elpa/magit-20160223.828/magit-ediff.el @@ -0,0 +1,431 @@ +;;; magit-ediff.el --- Ediff extension for Magit -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library provides basic support for Ediff. + +;;; Code: + +(require 'magit) + +(require 'ediff) +(require 'smerge-mode) + +(defvar smerge-ediff-buf) +(defvar smerge-ediff-windows) + +(defgroup magit-ediff nil + "Ediff support for Magit." + :group 'magit-extensions) + +(unless (find-lisp-object-file-name 'magit-ediff-quit-hook 'defvar) + (add-hook 'magit-ediff-quit-hook 'magit-ediff-restore-previous-winconf) + (add-hook 'magit-ediff-quit-hook 'magit-ediff-cleanup-auxiliary-buffers)) +(defcustom magit-ediff-quit-hook + '(magit-ediff-cleanup-auxiliary-buffers + magit-ediff-restore-previous-winconf) + "Hooks to run after finishing Ediff, when that was invoked using Magit. +The hooks are run in the Ediff control buffer. This is similar +to `ediff-quit-hook' but takes the needs of Magit into account. +The `ediff-quit-hook' is ignored by Ediff sessions which were +invoked using Magit." + :package-version '(magit . "2.2.0") + :group 'magit-ediff + :type 'hook + :options '(magit-ediff-cleanup-auxiliary-buffers + magit-ediff-restore-previous-winconf)) + +(defcustom magit-ediff-dwim-show-on-hunks nil + "Whether `magit-ediff-dwim' runs show variants on hunks. +If non-nil, `magit-ediff-show-staged' or +`magit-ediff-show-unstaged' are called based on what section the +hunk is in. Otherwise, `magit-ediff-dwim' runs +`magit-ediff-stage' when point is on an uncommitted hunk." + :package-version '(magit . "2.2.0") + :group 'magit-ediff + :type 'boolean) + +(defvar magit-ediff-previous-winconf nil) + +;;;###autoload (autoload 'magit-ediff-popup "magit-ediff" nil t) +(magit-define-popup magit-ediff-popup + "Popup console for ediff commands." + 'magit-diff nil nil + :actions '((?E "Dwim" magit-ediff-dwim) + (?u "Show unstaged" magit-ediff-show-unstaged) + (?s "Stage" magit-ediff-stage) + (?i "Show staged" magit-ediff-show-staged) + (?m "Resolve" magit-ediff-resolve) + (?w "Show worktree" magit-ediff-show-working-tree) + (?r "Diff range" magit-ediff-compare) + (?c "Show commit" magit-ediff-show-commit)) + :max-action-columns 2) + +;;;###autoload +(defun magit-ediff-resolve (file) + "Resolve outstanding conflicts in FILE using Ediff. +FILE has to be relative to the top directory of the repository. + +In the rare event that you want to manually resolve all +conflicts, including those already resolved by Git, use +`ediff-merge-revisions-with-ancestor'." + (interactive + (let ((current (magit-current-file)) + (unmerged (magit-unmerged-files))) + (unless unmerged + (user-error "There are no unresolved conflicts")) + (list (magit-completing-read "Resolve file" unmerged nil t nil nil + (car (member current unmerged)))))) + (magit-with-toplevel + (with-current-buffer (find-file-noselect file) + (smerge-ediff) + (setq-local + ediff-quit-hook + (lambda () + (let ((bufC ediff-buffer-C) + (bufS smerge-ediff-buf)) + (with-current-buffer bufS + (when (yes-or-no-p (format "Conflict resolution finished; save %s?" + buffer-file-name)) + (erase-buffer) + (insert-buffer-substring bufC) + (save-buffer)))) + (when (buffer-live-p ediff-buffer-A) (kill-buffer ediff-buffer-A)) + (when (buffer-live-p ediff-buffer-B) (kill-buffer ediff-buffer-B)) + (when (buffer-live-p ediff-buffer-C) (kill-buffer ediff-buffer-C)) + (when (buffer-live-p ediff-ancestor-buffer) + (kill-buffer ediff-ancestor-buffer)) + (let ((magit-ediff-previous-winconf smerge-ediff-windows)) + (run-hooks 'magit-ediff-quit-hook))))))) + +;;;###autoload +(defun magit-ediff-stage (file) + "Stage and unstage changes to FILE using Ediff. +FILE has to be relative to the top directory of the repository." + (interactive + (list (magit-completing-read "Selectively stage file" nil + (magit-tracked-files) nil nil nil + (magit-current-file)))) + (magit-with-toplevel + (let* ((conf (current-window-configuration)) + (bufA (magit-get-revision-buffer "HEAD" file)) + (bufB (get-buffer (concat file ".~{index}~"))) + (bufBrw (and bufB (with-current-buffer bufB (not buffer-read-only)))) + (bufC (get-file-buffer file))) + (ediff-buffers3 + (or bufA (magit-find-file-noselect "HEAD" file)) + (with-current-buffer (magit-find-file-index-noselect file t) + (setq buffer-read-only nil) + (current-buffer)) + (or bufC (find-file-noselect file)) + `((lambda () + (setq-local + ediff-quit-hook + (lambda () + (and (buffer-live-p ediff-buffer-B) + (buffer-modified-p ediff-buffer-B) + (with-current-buffer ediff-buffer-B + (magit-update-index))) + (and (buffer-live-p ediff-buffer-C) + (buffer-modified-p ediff-buffer-C) + (with-current-buffer ediff-buffer-C + (when (y-or-n-p + (format "Save file %s? " buffer-file-name)) + (save-buffer)))) + ,@(unless bufA '((ediff-kill-buffer-carefully ediff-buffer-A))) + ,@(if bufB + (unless bufBrw '((with-current-buffer ediff-buffer-B + (setq buffer-read-only t)))) + '((ediff-kill-buffer-carefully ediff-buffer-B))) + ,@(unless bufC '((ediff-kill-buffer-carefully ediff-buffer-C))) + (let ((magit-ediff-previous-winconf ,conf)) + (run-hooks 'magit-ediff-quit-hook)))))) + 'ediff-buffers3)))) + +;;;###autoload +(defun magit-ediff-compare (revA revB fileA fileB) + "Compare REVA:FILEA with REVB:FILEB using Ediff. + +FILEA and FILEB have to be relative to the top directory of the +repository. If REVA or REVB is nil then this stands for the +working tree state. + +If the region is active, use the revisions on the first and last +line of the region. With a prefix argument, instead of diffing +the revisions, choose a revision to view changes along, starting +at the common ancestor of both revisions (i.e., use a \"...\" +range)." + (interactive (-let [(revA revB) (magit-ediff-compare--read-revisions + nil current-prefix-arg)] + (nconc (list revA revB) + (magit-ediff-compare--read-files revA revB)))) + (magit-with-toplevel + (let ((conf (current-window-configuration)) + (bufA (if revA + (magit-get-revision-buffer revA fileA) + (get-file-buffer fileA))) + (bufB (if revB + (magit-get-revision-buffer revB fileB) + (get-file-buffer fileB)))) + (ediff-buffers + (or bufA (if revA + (magit-find-file-noselect revA fileA) + (find-file-noselect fileA))) + (or bufB (if revB + (magit-find-file-noselect revB fileB) + (find-file-noselect fileB))) + `((lambda () + (setq-local + ediff-quit-hook + (lambda () + ,@(unless bufA '((ediff-kill-buffer-carefully ediff-buffer-A))) + ,@(unless bufB '((ediff-kill-buffer-carefully ediff-buffer-B))) + (let ((magit-ediff-previous-winconf ,conf)) + (run-hooks 'magit-ediff-quit-hook)))))) + 'ediff-revision)))) + +(defun magit-ediff-compare--read-revisions (&optional arg mbase) + (let ((input (or arg (magit-diff-read-range-or-commit "Compare range or commit" + nil mbase))) + revA revB) + (if (string-match magit-range-re input) + (progn (setq revA (or (match-string 1 input) "HEAD") + revB (or (match-string 3 input) "HEAD")) + (when (string= (match-string 2 input) "...") + (setq revA (magit-git-string "merge-base" revA revB)))) + (setq revA input)) + (list revA revB))) + +(defun magit-ediff-compare--read-files (revA revB &optional fileB) + (unless fileB + (setq fileB (magit-read-file-choice + (format "File to compare between %s and %s" + revA (or revB "the working tree")) + (magit-changed-files revA revB) + (format "No changed files between %s and %s" + revA (or revB "the working tree"))))) + (list (or (car (member fileB (magit-revision-files revA))) + (cdr (assoc fileB (magit-renamed-files revB revA))) + (magit-read-file-choice + (format "File in %s to compare with %s in %s" + revA fileB (or revB "the working tree")) + (magit-changed-files revB revA) + (format "File in %s to compare with %s in %s" + revA fileB (or revB "the working tree")))) + fileB)) + +;;;###autoload +(defun magit-ediff-dwim () + "Compare, stage, or resolve using Ediff. +This command tries to guess what file, and what commit or range +the user wants to compare, stage, or resolve using Ediff. It +might only be able to guess either the file, or range or commit, +in which case the user is asked about the other. It might not +always guess right, in which case the appropriate `magit-ediff-*' +command has to be used explicitly. If it cannot read the user's +mind at all, then it asks the user for a command to run." + (interactive) + (magit-section-case + (hunk (save-excursion + (goto-char (magit-section-start (magit-section-parent it))) + (magit-ediff-dwim))) + (t + (let ((range (magit-diff--dwim)) + (file (magit-current-file)) + command revA revB) + (pcase range + ((and (guard (not magit-ediff-dwim-show-on-hunks)) + (or `unstaged `staged)) + (setq command (if (magit-anything-unmerged-p) + #'magit-ediff-resolve + #'magit-ediff-stage))) + (`unstaged (setq command #'magit-ediff-show-unstaged)) + (`staged (setq command #'magit-ediff-show-staged)) + (`(commit . ,value) + (setq command #'magit-ediff-show-commit + revB value)) + ((pred stringp) + (-let [(a b) (magit-ediff-compare--read-revisions range)] + (setq command #'magit-ediff-compare + revA a + revB b))) + (_ + (when (derived-mode-p 'magit-diff-mode) + (pcase (magit-diff-type) + (`committed (-let [(a b) (magit-ediff-compare--read-revisions + (car magit-refresh-args))] + (setq revA a revB b))) + ((guard (not magit-ediff-dwim-show-on-hunks)) + (setq command #'magit-ediff-stage)) + (`unstaged (setq command #'magit-ediff-show-unstaged)) + (`staged (setq command #'magit-ediff-show-staged)) + (`undefined (setq command nil)) + (_ (setq command nil)))))) + (cond ((not command) + (call-interactively + (magit-read-char-case + "Failed to read your mind; do you want to " t + (?c "[c]ommit" 'magit-ediff-show-commit) + (?r "[r]ange" 'magit-ediff-compare) + (?s "[s]tage" 'magit-ediff-stage) + (?v "resol[v]e" 'magit-ediff-resolve)))) + ((eq command 'magit-ediff-compare) + (apply 'magit-ediff-compare revA revB + (magit-ediff-compare--read-files revA revB file))) + ((eq command 'magit-ediff-show-commit) + (magit-ediff-show-commit revB)) + (file + (funcall command file)) + (t + (call-interactively command))))))) + +;;;###autoload +(defun magit-ediff-show-staged (file) + "Show staged changes using Ediff. + +This only allows looking at the changes; to stage, unstage, +and discard changes using Ediff, use `magit-ediff-stage'. + +FILE must be relative to the top directory of the repository." + (interactive + (list (magit-read-file-choice "Show staged changes for file" + (magit-staged-files) + "No staged files"))) + (let ((conf (current-window-configuration)) + (bufA (magit-get-revision-buffer "HEAD" file)) + (bufB (get-buffer (concat file ".~{index}~")))) + (ediff-buffers + (or bufA (magit-find-file-noselect "HEAD" file)) + (or bufB (magit-find-file-index-noselect file t)) + `((lambda () + (setq-local + ediff-quit-hook + (lambda () + ,@(unless bufA '((ediff-kill-buffer-carefully ediff-buffer-A))) + ,@(unless bufB '((ediff-kill-buffer-carefully ediff-buffer-B))) + (let ((magit-ediff-previous-winconf ,conf)) + (run-hooks 'magit-ediff-quit-hook)))))) + 'ediff-buffers))) + +;;;###autoload +(defun magit-ediff-show-unstaged (file) + "Show unstaged changes using Ediff. + +This only allows looking at the changes; to stage, unstage, +and discard changes using Ediff, use `magit-ediff-stage'. + +FILE must be relative to the top directory of the repository." + (interactive + (list (magit-read-file-choice "Show unstaged changes for file" + (magit-modified-files) + "No unstaged files"))) + (magit-with-toplevel + (let ((conf (current-window-configuration)) + (bufA (get-buffer (concat file ".~{index}~"))) + (bufB (get-file-buffer file))) + (ediff-buffers + (or bufA (magit-find-file-index-noselect file t)) + (or bufB (find-file-noselect file)) + `((lambda () + (setq-local + ediff-quit-hook + (lambda () + ,@(unless bufA '((ediff-kill-buffer-carefully ediff-buffer-A))) + ,@(unless bufB '((ediff-kill-buffer-carefully ediff-buffer-B))) + (let ((magit-ediff-previous-winconf ,conf)) + (run-hooks 'magit-ediff-quit-hook)))))) + 'ediff-buffers)))) + +;;;###autoload +(defun magit-ediff-show-working-tree (file) + "Show changes between HEAD and working tree using Ediff. +FILE must be relative to the top directory of the repository." + (interactive + (list (magit-read-file-choice "Show changes in file" + (magit-changed-files "HEAD") + "No changed files"))) + (magit-with-toplevel + (let ((conf (current-window-configuration)) + (bufA (magit-get-revision-buffer "HEAD" file)) + (bufB (get-file-buffer file))) + (ediff-buffers + (or bufA (magit-find-file-noselect "HEAD" file)) + (or bufB (find-file-noselect file)) + `((lambda () + (setq-local + ediff-quit-hook + (lambda () + ,@(unless bufA '((ediff-kill-buffer-carefully ediff-buffer-A))) + ,@(unless bufB '((ediff-kill-buffer-carefully ediff-buffer-B))) + (let ((magit-ediff-previous-winconf ,conf)) + (run-hooks 'magit-ediff-quit-hook)))))) + 'ediff-buffers)))) + +;;;###autoload +(defun magit-ediff-show-commit (commit) + "Show changes introduced by COMMIT using Ediff." + (interactive (list (magit-read-branch-or-commit "Revision"))) + (let ((revA (concat commit "^")) + (revB commit)) + (apply #'magit-ediff-compare + revA revB + (magit-ediff-compare--read-files revA revB (magit-current-file))))) + +(defun magit-ediff-cleanup-auxiliary-buffers () + (let* ((ctl-buf ediff-control-buffer) + (ctl-win (ediff-get-visible-buffer-window ctl-buf)) + (ctl-frm ediff-control-frame) + (main-frame (cond ((window-live-p ediff-window-A) + (window-frame ediff-window-A)) + ((window-live-p ediff-window-B) + (window-frame ediff-window-B))))) + (ediff-kill-buffer-carefully ediff-diff-buffer) + (ediff-kill-buffer-carefully ediff-custom-diff-buffer) + (ediff-kill-buffer-carefully ediff-fine-diff-buffer) + (ediff-kill-buffer-carefully ediff-tmp-buffer) + (ediff-kill-buffer-carefully ediff-error-buffer) + (ediff-kill-buffer-carefully ediff-msg-buffer) + (ediff-kill-buffer-carefully ediff-debug-buffer) + (when (boundp 'ediff-patch-diagnostics) + (ediff-kill-buffer-carefully ediff-patch-diagnostics)) + (cond ((and (ediff-window-display-p) + (frame-live-p ctl-frm)) + (delete-frame ctl-frm)) + ((window-live-p ctl-win) + (delete-window ctl-win))) + (unless (ediff-multiframe-setup-p) + (ediff-kill-bottom-toolbar)) + (ediff-kill-buffer-carefully ctl-buf) + (when (frame-live-p main-frame) + (select-frame main-frame)))) + +(defun magit-ediff-restore-previous-winconf () + (set-window-configuration magit-ediff-previous-winconf)) + +;;; magit-ediff.el ends soon +(provide 'magit-ediff) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-ediff.el ends here diff --git a/elpa/magit-20160223.828/magit-extras.el b/elpa/magit-20160223.828/magit-extras.el new file mode 100644 index 0000000..e66611d --- /dev/null +++ b/elpa/magit-20160223.828/magit-extras.el @@ -0,0 +1,203 @@ +;;; magit-extras.el --- additional functionality for Magit -*- lexical-binding: t -*- + +;; Copyright (C) 2008-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; 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: + +;; Additional functionality for Magit. + +;;; Code: + +(require 'magit) + +(defgroup magit-extras nil + "Additional functionality for Magit." + :group 'magit-extensions) + +;;; External Tools + +(defcustom magit-gitk-executable + (or (and (eq system-type 'windows-nt) + (let ((exe (expand-file-name + "gitk" (file-name-nondirectory magit-git-executable)))) + (and (file-executable-p exe) exe))) + (executable-find "gitk") "gitk") + "The Gitk executable." + :group 'magit-extras + :set-after '(magit-git-executable) + :type 'string) + +;;;###autoload +(defun magit-run-git-gui () + "Run `git gui' for the current git repository." + (interactive) + (magit-with-toplevel + (call-process magit-git-executable nil 0 nil "gui"))) + +;;;###autoload +(defun magit-run-git-gui-blame (commit filename &optional linenum) + "Run `git gui blame' on the given FILENAME and COMMIT. +Interactively run it for the current file and the HEAD, with a +prefix or when the current file cannot be determined let the user +choose. When the current buffer is visiting FILENAME instruct +blame to center around the line point is on." + (interactive + (let (revision filename) + (when (or current-prefix-arg + (not (setq revision "HEAD" + filename (magit-file-relative-name nil 'tracked)))) + (setq revision (magit-read-branch-or-commit "Blame from revision") + filename (magit-read-file-from-rev revision "Blame file"))) + (list revision filename + (and (equal filename + (ignore-errors + (magit-file-relative-name buffer-file-name))) + (line-number-at-pos))))) + (magit-with-toplevel + (apply #'call-process magit-git-executable nil 0 nil "gui" "blame" + `(,@(and linenum (list (format "--line=%d" linenum))) + ,commit + ,filename)))) + +;;;###autoload +(defun magit-run-gitk () + "Run `gitk' in the current repository." + (interactive) + (call-process magit-gitk-executable nil 0)) + +;;;###autoload +(defun magit-run-gitk-branches () + "Run `gitk --branches' in the current repository." + (interactive) + (call-process magit-gitk-executable nil 0 nil "--branches")) + +;;;###autoload +(defun magit-run-gitk-all () + "Run `gitk --all' in the current repository." + (interactive) + (call-process magit-gitk-executable nil 0 nil "--all")) + +;;; Clean + +;;;###autoload +(defun magit-clean (&optional arg) + "Remove untracked files from the working tree. +With a prefix argument also remove ignored files, +with two prefix arguments remove ignored files only. +\n(git clean -f -d [-x|-X])" + (interactive "p") + (when (yes-or-no-p (format "Remove %s files? " + (pcase arg + (1 "untracked") + (4 "untracked and ignored") + (_ "ignored")))) + (magit-wip-commit-before-change) + (magit-run-git "clean" "-f" "-d" (pcase arg (4 "-x") (16 "-X"))))) + +(put 'magit-clean 'disabled t) + +;;; Gitignore + +;;;###autoload +(defun magit-gitignore (file-or-pattern &optional local) + "Instruct Git to ignore FILE-OR-PATTERN. +With a prefix argument only ignore locally." + (interactive (list (magit-gitignore-read-pattern current-prefix-arg) + current-prefix-arg)) + (let ((gitignore + (if local + (magit-git-dir (convert-standard-filename "info/exclude")) + (expand-file-name ".gitignore" (magit-toplevel))))) + (make-directory (file-name-directory gitignore) t) + (with-temp-buffer + (when (file-exists-p gitignore) + (insert-file-contents gitignore)) + (goto-char (point-max)) + (unless (bolp) + (insert "\n")) + (insert (replace-regexp-in-string "\\(\\\\*\\)" "\\1\\1" file-or-pattern)) + (insert "\n") + (write-region nil nil gitignore)) + (if local + (magit-refresh) + (magit-run-git "add" ".gitignore")))) + +;;;###autoload +(defun magit-gitignore-locally (file-or-pattern) + "Instruct Git to locally ignore FILE-OR-PATTERN." + (interactive (list (magit-gitignore-read-pattern t))) + (magit-gitignore file-or-pattern t)) + +(defun magit-gitignore-read-pattern (local) + (let* ((default (magit-current-file)) + (choices + (delete-dups + (--mapcat + (cons (concat "/" it) + (-when-let (ext (file-name-extension it)) + (list (concat "/" (file-name-directory "foo") "*." ext) + (concat "*." ext)))) + (magit-untracked-files))))) + (when default + (setq default (concat "/" default)) + (unless (member default choices) + (setq default (concat "*." (file-name-extension default))) + (unless (member default choices) + (setq default nil)))) + (magit-completing-read (concat "File or pattern to ignore" + (and local " locally")) + choices nil nil nil nil default))) + +;;; ChangeLog + +;;;###autoload +(defun magit-add-change-log-entry (&optional whoami file-name other-window) + "Find change log file and add date entry and item for current change. +This differs from `add-change-log-entry' (which see) in that +it acts on the current hunk in a Magit buffer instead of on +a position in a file-visiting buffer." + (interactive (list current-prefix-arg + (prompt-for-change-log-name))) + (let (buf pos) + (save-window-excursion + (call-interactively #'magit-diff-visit-file) + (setq buf (current-buffer) + pos (point))) + (save-excursion + (with-current-buffer buf + (goto-char pos) + (add-change-log-entry whoami file-name other-window))))) + +;;;###autoload +(defun magit-add-change-log-entry-other-window (&optional whoami file-name) + "Find change log file in other window and add entry and item. +This differs from `add-change-log-entry-other-window' (which see) +in that it acts on the current hunk in a Magit buffer instead of +on a position in a file-visiting buffer." + (interactive (and current-prefix-arg + (list current-prefix-arg + (prompt-for-change-log-name)))) + (magit-add-change-log-entry whoami file-name t)) + +;;; magit-extras.el ends soon +(provide 'magit-extras) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-extras.el ends here diff --git a/elpa/magit-20160223.828/magit-git.el b/elpa/magit-20160223.828/magit-git.el new file mode 100644 index 0000000..006268e --- /dev/null +++ b/elpa/magit-20160223.828/magit-git.el @@ -0,0 +1,1389 @@ +;;; magit-git.el --- Git functionality -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library implements wrappers for various Git plumbing commands. + +;;; Code: + +(require 'cl-lib) +(require 'dash) + +(require 'magit-utils) +(require 'magit-section) + +(declare-function magit-process-buffer 'magit-process) +(declare-function magit-process-file 'magit-process) +(declare-function magit-process-insert-section 'magit-process) +(defvar magit-process-error-message-re) +(defvar magit-refresh-args) ; from `magit-mode' for `magit-current-file' +(defvar magit-branch-prefer-remote-upstream) + +(defvar magit-tramp-process-environment nil) + +(require 'crm) + +;;; Options + +;; For now this is shared between `magit-process' and `magit-git'. +(defgroup magit-process nil + "Git and other external processes used by Magit." + :group 'magit) + +(defvar magit-git-environment nil + "Prepended to `process-environment' while running git.") + +(defcustom magit-git-executable + ;; Git might be installed in a different location on a remote, so + ;; it is better not to use the full path to the executable, except + ;; on Window were we would otherwise end up using one one of the + ;; wrappers "cmd/git.exe" or "cmd/git.cmd", which are much slower + ;; than using "bin/git.exe" directly. + (or (and (eq system-type 'windows-nt) + (--when-let (executable-find "git.exe") + (or (ignore-errors + ;; Git for Windows 2.x provides cygpath so we can + ;; ask it for native paths. Using an upper case + ;; alias makes this fail on 1.x (which is good, + ;; because we would not want to end up using some + ;; other cygpath). + (prog1 (car + (process-lines + it "-c" + "alias.X=!x() { which \"$1\" | cygpath -mf -; }; x" + "X" "git")) + (setq magit-git-environment + (list (concat "PATH=" + (car (process-lines + it "-c" + "alias.P=!cygpath -wp \"$PATH\"" + "P"))))))) + ;; For 1.x, we search for bin/ next to cmd/. + (let ((alt (directory-file-name (file-name-directory it)))) + (if (and (equal (file-name-nondirectory alt) "cmd") + (setq alt (expand-file-name + (convert-standard-filename "bin/git.exe") + (file-name-directory alt))) + (file-executable-p alt)) + alt + it))))) + "git") + "The Git executable used by Magit." + :group 'magit-process + :type 'string) + +(defcustom magit-git-global-arguments + '("--no-pager" "--literal-pathspecs" "-c" "core.preloadindex=true") + "Global git arguments. + +The arguments set here are used every time the git executable is +run as a subprocess. They are placed right after the executable +itself and before the git command - as in `git HERE... COMMAND +REST'. See the manpage `git(1)' for valid arguments. + +Be careful what you add here, especially if you are using Tramp +to connect to servers with ancient Git versions. Never remove +anything that is part of the default value, unless you really +know what you are doing. And think very hard before adding +something; it will be used every time Magit runs Git for any +purpose." + :package-version '(magit . "2.1.0") + :group 'magit + :type '(repeat string)) + +(define-obsolete-variable-alias 'magit-git-standard-options + 'magit-git-global-arguments "2.1.0") + +(defcustom magit-git-debug nil + "Whether to enable additional reporting of git errors. + +Magit basically calls git for one of these two reasons: for +side-effects or to do something with its standard output. + +When git is run for side-effects then its output, including error +messages, go into the process buffer which is shown when using \ +\\\\[magit-process]. + +When git's output is consumed in some way, then it would be too +expensive to also insert it into this buffer, but when this +option is non-nil and git returns with a non-zero exit status, +then at least its standard error is inserted into this buffer. + +This is only intended for debugging purposes. Do not enable this +permanently, that would negatively affect performance" + :group 'magit + :group 'magit-process + :type 'boolean) + +(defcustom magit-ref-namespaces + '(("^@$" magit-head nil) + ("^refs/tags/\\(.+\\)" magit-tag nil) + ("^refs/heads/\\(.+\\)" magit-branch-local nil) + ("^refs/remotes/\\(.+\\)" magit-branch-remote nil) + ("^refs/bisect/\\(bad\\)" magit-bisect-bad nil) + ("^refs/bisect/\\(skip.*\\)" magit-bisect-skip nil) + ("^refs/bisect/\\(good.*\\)" magit-bisect-good nil) + ("^refs/stash$" magit-refname-stash nil) + ("^refs/wip/\\(.+\\)" magit-refname-wip nil) + ("^\\(bad\\):" magit-bisect-bad nil) + ("^\\(skip\\):" magit-bisect-skip nil) + ("^\\(good\\):" magit-bisect-good nil) + ("\\(.+\\)" magit-refname nil)) + "How refs are formatted for display. + +Each entry controls how a certain type of ref is displayed, and +has the form (REGEXP FACE FORMATTER). REGEXP is a regular +expression used to match full refs. The first entry whose REGEXP +matches the reference is used. The first regexp submatch becomes +the \"label\" that represents the ref and is propertized with +font FONT. If FORMATTER is non-nil it should be a function that +takes two arguments, the full ref and the face. It is supposed +to return a propertized label that represents the ref." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type '(repeat + (list regexp + face + (choice (const :tag "first submatch is label" nil) + (function :tag "format using function"))))) + +(defcustom magit-prefer-remote-upstream nil + "Whether to favor remote branches when reading the upstream branch. + +This controls whether commands that read a branch from the user +and then set it as the upstream branch, offer a local or a remote +branch as default completion candidate, when they have the choice. + +This affects all commands that use `magit-read-upstream-branch' +or `magit-read-starting-point', which includes all commands that +change the upstream and many which create new branches." + :package-version '(magit . "2.4.2") + :group 'magit-commands + :type 'boolean) + +;;; Git + +(defun magit-process-git-arguments (args) + "Prepare ARGS for a function that invokes Git. + +Magit has many specialized functions for running Git; they all +pass arguments through this function before handing them to Git, +to do the following. + +* Flatten ARGS, removing nil arguments. +* Prepend `magit-git-global-arguments' to ARGS." + (append magit-git-global-arguments (-flatten args))) + +(defun magit-git-exit-code (&rest args) + "Execute Git with ARGS, returning its exit code." + (apply #'magit-process-file magit-git-executable nil nil nil + (magit-process-git-arguments args))) + +(defun magit-git-success (&rest args) + "Execute Git with ARGS, returning t if its exit code is 0." + (= (magit-git-exit-code args) 0)) + +(defun magit-git-failure (&rest args) + "Execute Git with ARGS, returning t if its exit code is 1." + (= (magit-git-exit-code args) 1)) + +(defun magit-git-str (&rest args) + "Execute Git with ARGS, returning the first line of its output. +If there is no output return nil. If the output begins with a +newline return an empty string. Like `magit-git-string' but +ignore `magit-git-debug'." + (with-temp-buffer + (apply #'magit-process-file magit-git-executable nil (list t nil) nil + (magit-process-git-arguments args)) + (unless (bobp) + (goto-char (point-min)) + (buffer-substring-no-properties (point) (line-end-position))))) + +(defun magit-git-true (&rest args) + "Execute Git with ARGS, returning t if it prints \"true\". +Return t if the first (and usually only) output line is the +string \"true\", otherwise return nil." + (equal (magit-git-str args) "true")) + +(defun magit-git-false (&rest args) + "Execute Git with ARGS, returning t if it prints \"false\". +Return t if the first (and usually only) output line is the +string \"false\", otherwise return nil." + (equal (magit-git-str args) "false")) + +(defun magit-git-insert (&rest args) + "Execute Git with ARGS, inserting its output at point. +If Git exits with a non-zero exit status, then show a message and +add a section in the respective process buffer." + (setq args (magit-process-git-arguments args)) + (if magit-git-debug + (let (log) + (unwind-protect + (progn + (setq log (make-temp-file "magit-stderr")) + (delete-file log) + (let ((exit (apply #'magit-process-file magit-git-executable + nil (list t log) nil args))) + (when (> exit 0) + (let ((msg "Git failed")) + (when (file-exists-p log) + (setq msg (with-temp-buffer + (insert-file-contents log) + (goto-char (point-max)) + (and (re-search-backward + magit-process-error-message-re nil t) + (match-string 1)))) + (let ((magit-git-debug nil)) + (with-current-buffer (magit-process-buffer t) + (magit-process-insert-section default-directory + magit-git-executable + args exit log)))) + (message "%s" msg))) + exit)) + (ignore-errors (delete-file log)))) + (apply #'magit-process-file magit-git-executable + nil (list t nil) nil args))) + +(defun magit-git-string (&rest args) + "Execute Git with ARGS, returning the first line of its output. +If there is no output return nil. If the output begins with a +newline return an empty string." + (with-temp-buffer + (apply #'magit-git-insert args) + (unless (bobp) + (goto-char (point-min)) + (buffer-substring-no-properties (point) (line-end-position))))) + +(defun magit-git-lines (&rest args) + "Execute Git with ARGS, returning its output as a list of lines. +Empty lines anywhere in the output are omitted. + +If Git exits with a non-zero exit status, then report show a +message and add a section in the respective process buffer." + (with-temp-buffer + (apply #'magit-git-insert args) + (split-string (buffer-string) "\n" t))) + +(defun magit-git-items (&rest args) + "Execute Git with ARGS, returning its null-separated output as a list. +Empty items anywhere in the output are omitted. + +If Git exits with a non-zero exit status, then report show a +message and add a section in the respective process buffer." + (with-temp-buffer + (apply #'magit-git-insert args) + (split-string (buffer-string) "\0" t))) + +(defun magit-git-wash (washer &rest args) + "Execute Git with ARGS, inserting washed output at point. +Actually first insert the raw output at point. If there is no +output call `magit-cancel-section'. Otherwise temporarily narrow +the buffer to the inserted text, move to its beginning, and then +call function WASHER with no argument." + (declare (indent 1)) + (let ((beg (point))) + (setq args (-flatten args)) + (magit-git-insert args) + (if (= (point) beg) + (magit-cancel-section) + (unless (bolp) + (insert "\n")) + (save-restriction + (narrow-to-region beg (point)) + (goto-char beg) + (funcall washer args)) + (when (or (= (point) beg) + (= (point) (1+ beg))) + (magit-cancel-section))))) + +(defun magit-git-version (&optional raw) + (--when-let (let (magit-git-global-arguments) + (ignore-errors (substring (magit-git-string "version") 12))) + (if raw it (and (string-match "^\\([0-9]+\\.[0-9]+\\.[0-9]+\\)" it) + (match-string 1 it))))) + +;;; Files + +(defun magit--safe-default-directory (&optional file) + (catch 'unsafe-default-dir + (let ((dir (file-name-as-directory + (expand-file-name (or file default-directory)))) + (previous nil)) + (while (not (file-accessible-directory-p dir)) + (setq dir (file-name-directory (directory-file-name dir))) + (when (equal dir previous) + (throw 'unsafe-default-dir nil)) + (setq previous dir)) + dir))) + +(defmacro magit--with-safe-default-directory (file &rest body) + (declare (indent 1) (debug (form body))) + `(-when-let (default-directory (magit--safe-default-directory ,file)) + ,@body)) + +(defun magit-git-dir (&optional path) + "Return absolute path to the control directory of the current repository. + +All symlinks are followed. If optional PATH is non-nil, then +it has to be a path relative to the control directory and its +absolute path is returned." + (magit--with-safe-default-directory nil + (--when-let (magit-rev-parse-safe "--git-dir") + (setq it (file-name-as-directory (magit-expand-git-file-name it))) + (if path (expand-file-name (convert-standard-filename path) it) it)))) + +(defun magit-toplevel (&optional directory) + "Return the absolute path to the toplevel of the current repository. + +From within the working tree or control directory of a repository +return the absolute path to the toplevel directory of the working +tree. As a special case, from within a bare repository return +the control directory instead. When called outside a repository +then return nil. + +When optional DIRECTORY is non-nil then return the toplevel for +that directory instead of the one for `default-directory'. + +Try to respect the option `find-file-visit-truename', i.e. when +the value of that option is nil, then avoid needlessly returning +the truename. When a symlink to a sub-directory of the working +tree is involved, or when called from within a sub-directory of +the gitdir or from the toplevel of a gitdir, which itself is not +located within the working tree, then it is not possible to avoid +returning the truename." + (magit--with-safe-default-directory directory + (-if-let (topdir (magit-rev-parse-safe "--show-toplevel")) + (let (updir) + (setq topdir (magit-expand-git-file-name topdir)) + (if (and + ;; Always honor these settings. + (not find-file-visit-truename) + (not (getenv "GIT_WORK_TREE")) + ;; `--show-cdup' is the relative path to the toplevel + ;; from `(file-truename default-directory)'. Here we + ;; pretend it is relative to `default-directory', and + ;; go to that directory. Then we check whether + ;; `--show-toplevel' still returns the same value and + ;; whether `--show-cdup' now is the empty string. If + ;; both is the case, then we are at the toplevel of + ;; the same working tree, but also avoided needlessly + ;; following any symlinks. + (progn + (setq updir (file-name-as-directory + (magit-rev-parse-safe "--show-cdup"))) + (setq updir (if (file-name-absolute-p updir) + (concat (file-remote-p default-directory) updir) + (expand-file-name updir))) + (let ((default-directory updir)) + (and (string-equal (magit-rev-parse-safe "--show-cdup") "") + (--when-let (magit-rev-parse-safe "--show-toplevel") + (string-equal (magit-expand-git-file-name it) + topdir)))))) + updir + (concat (file-remote-p default-directory) + (file-name-as-directory topdir)))) + (-when-let (gitdir (magit-rev-parse-safe "--git-dir")) + (setq gitdir (file-name-as-directory + (if (file-name-absolute-p gitdir) + ;; We might have followed a symlink. + (concat (file-remote-p default-directory) + (magit-expand-git-file-name gitdir)) + (expand-file-name gitdir)))) + (if (magit-bare-repo-p) + gitdir + (let* ((link (expand-file-name "gitdir" gitdir)) + (wtree (and (file-exists-p link) + (magit-file-line link)))) + (if (and wtree + ;; Ignore .git/gitdir files that result from a + ;; Git bug. See #2364. + (not (equal wtree ".git"))) + ;; Return the linked working tree. + (file-name-directory wtree) + ;; Step outside the control directory to enter the working tree. + (file-name-directory (directory-file-name gitdir))))))))) + +(defmacro magit-with-toplevel (&rest body) + (declare (indent defun) (debug (body))) + (let ((toplevel (cl-gensym "toplevel"))) + `(let ((,toplevel (magit-toplevel))) + (if ,toplevel + (let ((default-directory ,toplevel)) + ,@body) + (error "Not inside a Git repository: %s" default-directory))))) + +(defun magit-inside-gitdir-p () + "Return t if `default-directory' is below a repository directory." + (magit-rev-parse-p "--is-inside-git-dir")) + +(defun magit-inside-worktree-p () + "Return t if `default-directory' is below the work tree of a repository." + (magit-rev-parse-p "--is-inside-work-tree")) + +(defun magit-bare-repo-p () + "Return t if the current repository is bare." + (magit-rev-parse-p "--is-bare-repository")) + +(defun magit-git-repo-p (directory &optional non-bare) + "Return t if DIRECTORY is a Git repository. +When optional NON-BARE is non-nil also return nil if DIRECTORY is +a bare repositories." + (or (file-regular-p (expand-file-name ".git" directory)) + (file-directory-p (expand-file-name ".git" directory)) + (and (not non-bare) + (file-regular-p (expand-file-name "HEAD" directory)) + (file-directory-p (expand-file-name "refs" directory)) + (file-directory-p (expand-file-name "objects" directory))))) + +(defvar-local magit-buffer-revision nil) +(defvar-local magit-buffer-refname nil) +(defvar-local magit-buffer-file-name nil) +(put 'magit-buffer-revision 'permanent-local t) +(put 'magit-buffer-refname 'permanent-local t) +(put 'magit-buffer-file-name 'permanent-local t) + +(defun magit-file-relative-name (&optional file tracked) + "Return the path of FILE relative to the repository root. + +If optional FILE is nil or omitted return the relative path of +the file being visited in the current buffer, if any, else nil. +If the file is not inside a Git repository then return nil. + +If TRACKED is non-nil, return the path only if it matches a +tracked file." + (unless file + (with-current-buffer (or (buffer-base-buffer) + (current-buffer)) + (setq file (or magit-buffer-file-name buffer-file-name)))) + (when (and file (or (not tracked) + (magit-file-tracked-p (file-relative-name file)))) + (--when-let (magit-toplevel + (magit--safe-default-directory + (directory-file-name (file-name-directory file)))) + (file-relative-name file it)))) + +(defun magit-file-tracked-p (file) + (magit-git-success "ls-files" "--error-unmatch" file)) + +(defun magit-list-files (&rest args) + (apply #'magit-git-items "ls-files" "-z" "--full-name" args)) + +(defun magit-tracked-files () + (magit-list-files "--cached")) + +(defun magit-untracked-files (&optional all files) + (magit-list-files "--other" (unless all "--exclude-standard") "--" files)) + +(defun magit-modified-files (&optional nomodules) + (magit-git-items "diff-files" "-z" "--name-only" + (and nomodules "--ignore-submodules"))) + +(defun magit-staged-files (&optional nomodules files) + (magit-git-items "diff-index" "-z" "--name-only" "--cached" + (and nomodules "--ignore-submodules") + (magit-headish) "--" files)) + +(defun magit-unstaged-files (&optional nomodules files) + (magit-git-items "diff-index" "-z" "--name-only" + (and nomodules "--ignore-submodules") + (magit-headish) "--" files)) + +(defun magit-staged-binary-files () + (--mapcat (and (string-match "^-\t-\t\\(.+\\)" it) + (list (match-string 1 it))) + (magit-git-items "diff" "-z" "--cached" + "--numstat" "--ignore-submodules"))) + +(defun magit-unmerged-files () + (magit-git-items "diff-files" "-z" "--name-only" "--diff-filter=U")) + +(defun magit-revision-files (rev) + (magit-with-toplevel + (magit-git-items "ls-tree" "-z" "-r" "--name-only" rev))) + +(defun magit-changed-files (rev-or-range &optional other-rev) + "Return list of files the have changed between two revisions. +If OTHER-REV is non-nil, REV-OR-RANGE should be a revision, not a +range. Otherwise, it can be any revision or range accepted by +\"git diff\" (i.e., , .., or ...)." + (magit-with-toplevel + (magit-git-items "diff" "-z" "--name-only" rev-or-range other-rev))) + +(defun magit-renamed-files (revA revB) + (--map (cons (nth 1 it) (nth 2 it)) + (-partition 3 (magit-git-items + "diff-tree" "-r" "--diff-filter=R" "-z" "-M" + revA revB)))) + +(defun magit-file-status (&rest args) + (with-temp-buffer + (save-excursion (magit-git-insert "status" "-z" args)) + (let ((pos (point)) status) + (while (> (skip-chars-forward "[:print:]") 0) + (let ((x (char-after pos)) + (y (char-after (1+ pos))) + (file (buffer-substring (+ pos 3) (point)))) + (forward-char) + (if (memq x '(?R ?C)) + (progn + (setq pos (point)) + (skip-chars-forward "[:print:]") + (push (list file (buffer-substring pos (point)) x y) status) + (forward-char)) + (push (list file nil x y) status))) + (setq pos (point))) + status))) + +(defcustom magit-cygwin-mount-points + (when (eq system-type 'windows-nt) + (cl-sort (--map (if (string-match "^\\(.*\\) on \\(.*\\) type" it) + (cons (file-name-as-directory (match-string 2 it)) + (file-name-as-directory (match-string 1 it))) + (lwarn '(magit) :error + "Failed to parse Cygwin mount: %S" it)) + ;; If --exec-path is not a native Windows path, + ;; then we probably have a cygwin git. + (and (not (string-match-p + "\\`[a-zA-Z]:" + (car (process-lines "git" "--exec-path")))) + (ignore-errors (process-lines "mount")))) + #'> :key (-lambda ((cyg . _win)) (length cyg)))) + "Alist of (CYGWIN . WIN32) directory names. +Sorted from longest to shortest CYGWIN name." + :package-version '(magit . "2.3.0") + :group 'magit-process + :type '(alist :key-type string :value-type directory)) + +(defun magit-expand-git-file-name (filename) + (unless (file-name-absolute-p filename) + (setq filename (expand-file-name filename))) + (-if-let ((cyg . win) + (cl-assoc filename magit-cygwin-mount-points + :test (lambda (f cyg) (string-prefix-p cyg f)))) + (concat win (substring filename (length cyg))) + filename)) + +(defun magit-convert-git-filename (filename) + (-if-let ((cyg . win) + (cl-rassoc filename magit-cygwin-mount-points + :test (lambda (f win) (string-prefix-p win f)))) + (concat cyg (substring filename (length win))) + filename)) + +(defun magit-decode-git-path (path) + (if (eq (aref path 0) ?\") + (string-as-multibyte (read path)) + path)) + +(defun magit-file-at-point () + (magit-section-case + (file (magit-section-value it)) + (hunk (magit-section-parent-value it)))) + +(defun magit-current-file () + (or (magit-file-relative-name) + (magit-file-at-point) + (and (derived-mode-p 'magit-log-mode) + (nth 3 magit-refresh-args)))) + +;;; Predicates + +(defun magit-no-commit-p () + "Return t if there is no commit in the current Git repository." + (not (magit-rev-verify "HEAD"))) + +(defun magit-anything-staged-p (&optional ignore-submodules &rest files) + "Return t if there are any staged changes. +If optional FILES is non-nil, then only changes to those files +are considered." + (magit-git-failure "diff" "--quiet" "--cached" + (and ignore-submodules "--ignore-submodules") + "--" files)) + +(defun magit-anything-unstaged-p (&optional ignore-submodules &rest files) + "Return t if there are any unstaged changes. +If optional FILES is non-nil, then only changes to those files +are considered." + (magit-git-failure "diff" "--quiet" + (and ignore-submodules "--ignore-submodules") + "--" files)) + +(defun magit-anything-modified-p (&optional ignore-submodules &rest files) + "Return t if there are any staged or unstaged changes. +If optional FILES is non-nil, then only changes to those files +are considered." + (or (apply 'magit-anything-staged-p ignore-submodules files) + (apply 'magit-anything-unstaged-p ignore-submodules files))) + +(defun magit-anything-unmerged-p (&rest files) + "Return t if there are any merge conflicts. +If optional FILES is non-nil, then only conflicts in those files +are considered." + (and (magit-git-string "ls-files" "--unmerged" files) t)) + +;;; Revisions and References + +(defun magit-rev-parse (&rest args) + "Execute `git rev-parse ARGS', returning first line of output. +If there is no output return nil." + (apply #'magit-git-string "rev-parse" args)) + +(defun magit-rev-parse-safe (&rest args) + "Execute `git rev-parse ARGS', returning first line of output. +If there is no output return nil. Like `magit-rev-parse' but +ignore `magit-git-debug'." + (apply #'magit-git-str "rev-parse" args)) + +(defun magit-rev-parse-p (&rest args) + "Execute `git rev-parse ARGS', returning t if it prints \"true\". +Return t if the first (and usually only) output line is the +string \"true\", otherwise return nil." + (magit-git-true "rev-parse" args)) + +(defun magit-rev-verify (rev) + (magit-rev-parse-safe "--verify" rev)) + +(defun magit-rev-verify-commit (rev) + "Return full hash for REV if it names an existing commit." + (magit-rev-verify (concat rev "^{commit}"))) + +(defun magit-rev-equal (a b) + (magit-git-success "diff" "--quiet" a b)) + +(defun magit-rev-eq (a b) + (equal (magit-rev-verify a) + (magit-rev-verify b))) + +(defun magit-rev-ancestor-p (a b) + "Return non-nil if commit A is an ancestor of commit B." + (magit-git-success "merge-base" "--is-ancestor" a b)) + +(defun magit-rev-head-p (rev) + (or (equal rev "HEAD") + (and rev + (not (string-match-p "\\.\\." rev)) + (equal (magit-rev-parse rev) + (magit-rev-parse "HEAD"))))) + +(defun magit-rev-name (rev &optional pattern) + (magit-git-string "name-rev" "--name-only" "--no-undefined" + (and pattern (concat "--refs=" pattern)) + rev)) + +(defun magit-rev-branch (rev) + (--when-let (magit-rev-name rev "refs/heads/*") + (unless (string-match-p "~" it) it))) + +(defun magit-get-shortname (rev) + (let ((fn (apply-partially 'magit-git-string "name-rev" + "--name-only" "--no-undefined" rev))) + (setq rev (or (funcall fn "--refs=refs/tags/*") + (funcall fn "--refs=refs/heads/*") + (funcall fn "--refs=refs/remotes/*" "--always"))) + (if (and (string-match "^\\(?:tags\\|remotes\\)/\\(.+\\)" rev) + (magit-ref-fullname (match-string 1 rev))) + (match-string 1 rev) + rev))) + +(defun magit-name-branch (rev &optional lax) + (or (magit-name-local-branch rev) + (magit-name-remote-branch rev) + (and lax (or (magit-name-local-branch rev t) + (magit-name-remote-branch rev t))))) + +(defun magit-name-local-branch (rev &optional lax) + (--when-let (magit-git-string "name-rev" "--name-only" "--no-undefined" + "--refs=refs/heads/*" rev) + (and (or lax (not (string-match-p "~" it))) it))) + +(defun magit-name-remote-branch (rev &optional lax) + (--when-let (magit-git-string "name-rev" "--name-only" "--no-undefined" + "--refs=refs/remotes/*" rev) + (and (or lax (not (string-match-p "~" it))) + (substring it 8)))) + +(defun magit-name-tag (rev &optional lax) + (--when-let (magit-git-string "name-rev" "--name-only" "--no-undefined" + "--refs=refs/tags/*" rev) + (and (or lax (not (string-match-p "~" it))) + (substring it 5)))) + +(defun magit-ref-fullname (name) + (magit-rev-parse "--symbolic-full-name" name)) + +(defun magit-ref-exists-p (ref) + (magit-git-success "show-ref" "--verify" ref)) + +(defun magit-headish () + "Return \"HEAD\" or if that doesn't exist the hash of the empty tree." + (if (magit-no-commit-p) + (magit-git-string "mktree") + "HEAD")) + +(defun magit-branch-at-point () + (magit-section-case + (branch (magit-section-value it)) + (commit (magit-name-branch (magit-section-value it))))) + +(defun magit-local-branch-at-point () + (magit-section-case + (branch (let ((branch (magit-section-value it))) + (when (member branch (magit-list-local-branch-names)) + branch))) + (commit (magit-name-local-branch (magit-section-value it))))) + +(defun magit-remote-branch-at-point () + (magit-section-case + (branch (let ((branch (magit-section-value it))) + (when (member branch (magit-list-remote-branch-names)) + branch))) + (commit (magit-name-remote-branch (magit-section-value it))))) + +(defun magit-commit-at-point () + (or (magit-section-when commit) + (and (derived-mode-p 'magit-revision-mode) + (car magit-refresh-args)))) + +(defun magit-branch-or-commit-at-point () + (or (magit-section-case + (branch (magit-section-value it)) + (commit (let ((rev (magit-section-value it))) + (or (magit-get-shortname rev) rev)))) + (and (derived-mode-p 'magit-revision-mode) + (car magit-refresh-args)))) + +(defun magit-tag-at-point () + (magit-section-case + (tag (magit-section-value it)) + (commit (magit-name-tag (magit-section-value it))))) + +(defun magit-stash-at-point () + (magit-section-when stash)) + +(defun magit-remote-at-point () + (magit-section-case + (remote (magit-section-value it)) + (branch (magit-section-parent-value it)))) + +(defun magit-get-current-branch () + "Return the refname of the currently checked out branch. +Return nil if no branch is currently checked out." + (magit-git-string "symbolic-ref" "--short" "HEAD")) + +(defun magit-get-previous-branch () + "Return the refname of the previously checked out branch. +Return nil if no branch can be found in the `HEAD' reflog +which is different from the current branch and still exists." + (let ((current (magit-get-current-branch)) + (i 1) prev) + (while (and (setq prev (magit-rev-verify (format "@{-%i}" i))) + (or (not (setq prev (magit-rev-branch prev))) + (equal prev current))) + (cl-incf i)) + prev)) + +(cl-defun magit-get-upstream-ref + (&optional (branch (magit-get-current-branch))) + (when branch + (let ((remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge"))) + (when (and remote merge) + (cond ((string-equal remote ".") merge) + ((string-prefix-p "refs/heads/" merge) + (concat "refs/remotes/" remote "/" (substring merge 11)))))))) + +(cl-defun magit-get-upstream-branch + (&optional (branch (magit-get-current-branch))) + (when branch + (let ((remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge"))) + (when (and remote merge (string-prefix-p "refs/heads/" merge)) + (setq merge (substring merge 11)) + (if (string-equal remote ".") + (propertize merge 'face 'magit-branch-local) + (propertize (concat remote "/" merge) 'face 'magit-branch-remote)))))) + +(defun magit-get-indirect-upstream-branch (branch &optional force) + (let ((remote (magit-get "branch" branch "remote"))) + (and remote (not (equal remote ".")) + ;; The user has opted in... + (or force (member branch magit-branch-prefer-remote-upstream)) + ;; and local BRANCH tracks a remote branch... + (let ((upstream (magit-get-upstream-branch branch))) + ;; whose upstream... + (and upstream + ;; has the same name as BRANCH and... + (equal (substring upstream (1+ (length remote))) branch) + ;; and can be fast-forwarded to BRANCH. + (magit-rev-ancestor-p upstream branch) + upstream))))) + +(cl-defun magit-get-upstream-remote + (&optional (branch (magit-get-current-branch))) + (when branch + (magit-get "branch" branch "remote"))) + +(cl-defun magit-get-push-remote + (&optional (branch (magit-get-current-branch))) + (or (and branch (magit-get "branch" branch "pushRemote")) + (magit-get "remote.pushDefault"))) + +(cl-defun magit-get-push-branch + (&optional (branch (magit-get-current-branch))) + (-when-let (remote (and branch (magit-get-push-remote branch))) + (concat remote "/" branch))) + +(defun magit-get-@{push}-branch (&optional branch) + (let ((ref (magit-rev-parse "--symbolic-full-name" + (concat branch "@{push}")))) + (when (and ref (string-prefix-p "refs/remotes/" ref)) + (substring ref 13)))) + +(defun magit-get-remote (&optional branch) + (when (or branch (setq branch (magit-get-current-branch))) + (let ((remote (magit-get "branch" branch "remote"))) + (unless (equal remote ".") + remote)))) + +(defun magit-split-branch-name (branch) + (cond ((member branch (magit-list-local-branch-names)) + (cons "." branch)) + ((string-match "/" branch) + (let ((remote (substring branch 0 (match-beginning 0)))) + (if (save-match-data (member remote (magit-list-remotes))) + (cons remote (substring branch (match-end 0))) + (error "Invalid branch name %s" branch)))))) + +(defun magit-get-current-tag (&optional rev with-distance) + "Return the closest tag reachable from REV. + +If optional REV is nil then default to \"HEAD\". +If optional WITH-DISTANCE is non-nil then return (TAG COMMITS), +if it is `dirty' return (TAG COMMIT DIRTY). COMMITS is the number +of commits in \"HEAD\" but not in TAG and DIRTY is t if there are +uncommitted changes, nil otherwise." + (--when-let (magit-git-str "describe" "--long" "--tags" + (and (eq with-distance 'dirty) "--dirty") rev) + (save-match-data + (string-match + "\\(.+\\)-\\(?:0[0-9]*\\|\\([0-9]+\\)\\)-g[0-9a-z]+\\(-dirty\\)?$" it) + (if with-distance + `(,(match-string 1 it) + ,(string-to-number (or (match-string 2 it) "0")) + ,@(and (match-string 3 it) (list t))) + (match-string 1 it))))) + +(defun magit-get-next-tag (&optional rev with-distance) + "Return the closest tag from which REV is reachable. + +If optional REV is nil then default to \"HEAD\". +If no such tag can be found or if the distance is 0 (in which +case it is the current tag, not the next) return nil instead. +If optional WITH-DISTANCE is non-nil then return (TAG COMMITS) +where COMMITS is the number of commits in TAG but not in REV." + (--when-let (magit-git-str "describe" "--contains" (or rev "HEAD")) + (save-match-data + (when (string-match "^[^^~]+" it) + (setq it (match-string 0 it)) + (unless (equal it (magit-get-current-tag rev)) + (if with-distance + (list it (car (magit-rev-diff-count it rev))) + it)))))) + +(defvar magit-list-refs-namespaces + '("refs/heads" "refs/remotes" "refs/tags" "refs/pull")) + +(defun magit-list-refs (&rest args) + (magit-git-lines "for-each-ref" "--format=%(refname)" + (or args magit-list-refs-namespaces))) + +(defun magit-list-branches () + (magit-list-refs "refs/heads" "refs/remotes")) + +(defun magit-list-local-branches () + (magit-list-refs "refs/heads")) + +(defun magit-list-remote-branches (&optional remote) + (magit-list-refs (concat "refs/remotes/" remote))) + +(defun magit-list-containing-branches (&optional commit) + (--filter (not (string-match-p "\\`(HEAD" it)) + (--map (substring it 2) + (magit-git-lines "branch" "--contains" commit)))) + +(defun magit-list-merged-branches (&optional commit) + (--filter (not (string-match-p "\\`(HEAD" it)) + (--map (substring it 2) + (magit-git-lines "branch" "--merged" commit)))) + +(defun magit-list-unmerged-branches (&optional commit) + (--filter (not (string-match-p "\\`(HEAD" it)) + (--map (substring it 2) + (magit-git-lines "branch" "--no-merged" commit)))) + +(defun magit-list-unmerged-to-upstream-branches () + (--filter (-when-let (upstream (magit-get-upstream-branch it)) + (member it (magit-list-unmerged-branches upstream))) + (magit-list-local-branch-names))) + +(defun magit-list-refnames (&rest args) + (magit-git-lines "for-each-ref" "--format=%(refname:short)" + (or args magit-list-refs-namespaces))) + +(defun magit-list-branch-names () + (magit-list-refnames "refs/heads" "refs/remotes")) + +(defun magit-list-local-branch-names () + (magit-list-refnames "refs/heads")) + +(defun magit-list-remote-branch-names (&optional remote relative) + (if (and remote relative) + (let ((regexp (format "^refs/remotes/%s/\\(.+\\)" remote))) + (--mapcat (when (string-match regexp it) + (list (match-string 1 it))) + (magit-list-remote-branches remote))) + (magit-list-refnames (concat "refs/remotes/" remote)))) + +(defun magit-format-refs (format &rest args) + (let ((lines (magit-git-lines + "for-each-ref" (concat "--format=" format) + (or args (list "refs/heads" "refs/remotes" "refs/tags"))))) + (if (string-match-p "\f" format) + (--map (split-string it "\f") lines) + lines))) + +(defun magit-list-remotes () + (magit-git-lines "remote")) + +(defun magit-list-tags () + (magit-git-lines "tag")) + +(defun magit-list-notes-refnames () + (--map (substring it 6) (magit-list-refnames "refs/notes"))) + +(defun magit-remote-list-tags (remote) + (--map (substring it 51) + (--filter (not (string-match-p "\\^{}$" it)) + (magit-git-lines "ls-remote" "--tags" remote)))) + +(defun magit-remote-list-branches (remote) + (--map (substring it 52) + (--filter (not (string-match-p "\\^{}$" it)) + (magit-git-lines "ls-remote" "--heads" remote)))) + +(defun magit-get-submodules () + (--mapcat (and (string-match "^160000 [0-9a-z]\\{40\\} 0\t\\(.+\\)$" it) + (list (match-string 1 it))) + (magit-git-items "ls-files" "-z" "--stage"))) + +(defun magit-ref-p (rev) + (or (car (member rev (magit-list-refs))) + (car (member rev (magit-list-refnames))))) + +(defun magit-branch-p (rev) + (or (car (member rev (magit-list-branches))) + (car (member rev (magit-list-branch-names))))) + +(defun magit-local-branch-p (rev) + (or (car (member rev (magit-list-local-branches))) + (car (member rev (magit-list-local-branch-names))))) + +(defun magit-branch-set-face (branch) + (propertize branch 'face (if (magit-local-branch-p branch) + 'magit-branch-local + 'magit-branch-remote))) + +(defun magit-tag-p (rev) + (car (member rev (magit-list-tags)))) + +(defun magit-remote-p (string) + (car (member string (magit-list-remotes)))) + +(defun magit-rev-diff-count (a b) + "Return the commits in A but not B and vice versa. +Return a list of two integers: (A>B B>A)." + (mapcar 'string-to-number + (split-string (magit-git-string "rev-list" + "--count" "--left-right" + (concat a "..." b)) + "\t"))) + +(defun magit-abbrev-length () + (string-to-number (or (magit-get "core.abbrev") "7"))) + +(defun magit-abbrev-arg (&optional arg) + (format "--%s=%d" (or arg "abbrev") (magit-abbrev-length))) + +(defun magit-rev-abbrev (rev) + (magit-rev-parse (magit-abbrev-arg "short") rev)) + +(defun magit-commit-children (commit &optional args) + (-map #'car + (--filter (member commit (cdr it)) + (--map (split-string it " ") + (magit-git-lines + "log" "--format=%H %P" + (or args (list "--branches" "--tags" "--remotes")) + "--not" commit))))) + +(defun magit-commit-parents (commit) + (--when-let (magit-git-string "rev-list" "-1" "--parents" commit) + (cdr (split-string it)))) + +(defun magit-assert-one-parent (commit command) + (when (> (length (magit-commit-parents commit)) 1) + (user-error "Cannot %s a merge commit" command))) + +(defun magit-patch-id (rev) + (with-temp-buffer + (magit-process-file + shell-file-name nil '(t nil) nil shell-command-switch + (let ((exec (shell-quote-argument magit-git-executable))) + (format "%s diff-tree -u %s | %s patch-id" exec rev exec))) + (car (split-string (buffer-string))))) + +(defun magit-rev-format (format &optional rev args) + (let ((str (magit-git-string "show" "--no-patch" + (concat "--format=" format) args + (if rev (concat rev "^{commit}") "HEAD") "--"))) + (unless (string-equal str "") + str))) + +(defun magit-rev-insert-format (format &optional rev args) + (magit-git-insert "show" "--no-patch" + (concat "--format=" format) args + (if rev (concat rev "^{commit}") "HEAD") "--")) + +(defun magit-format-rev-summary (rev) + (--when-let (magit-rev-format "%h %s" rev) + (string-match " " it) + (put-text-property 0 (match-beginning 0) 'face 'magit-hash it) + it)) + +(defun magit-format-ref-label (ref &optional head) + (-let [(_re face fn) + (--first (string-match (car it) ref) magit-ref-namespaces)] + (if fn + (funcall fn ref face) + (propertize (or (match-string 1 ref) ref) + 'face (if (equal ref head) 'magit-branch-current face))))) + +(defun magit-format-ref-labels (string) + (save-match-data + (let ((regexp "\\(, \\|tag: \\| -> \\|[()]\\)") head names) + (if (and (derived-mode-p 'magit-log-mode) + (member "--simplify-by-decoration" (cadr magit-refresh-args))) + (let ((branches (magit-list-local-branch-names)) + (re (format "^%s/.+" (regexp-opt (magit-list-remotes))))) + (setq names + (--map (cond ((string-equal it "HEAD") it) + ((string-prefix-p "refs/" it) it) + ((member it branches) (concat "refs/heads/" it)) + ((string-match re it) (concat "refs/remotes/" it)) + (t (concat "refs/" it))) + (split-string + (replace-regexp-in-string "tag: " "refs/tags/" string) + regexp t)))) + (setq names (split-string string regexp t))) + (when (member "HEAD" names) + (setq head (magit-git-string "symbolic-ref" "HEAD") + names (cons (or head "@") (delete head (delete "HEAD" names))))) + (mapconcat (lambda (it) (magit-format-ref-label it head)) names " ")))) + +(defun magit-object-type (object) + (magit-git-string "cat-file" "-t" object)) + +(defmacro magit-with-blob (commit file &rest body) + (declare (indent 2) + (debug (form form body))) + `(with-temp-buffer + (let ((buffer-file-name ,file)) + (save-excursion + (magit-git-insert "cat-file" "-p" + (concat ,commit ":" buffer-file-name))) + (decode-coding-inserted-region + (point-min) (point-max) buffer-file-name t nil nil t) + ,@body))) + +(defmacro magit-with-temp-index (tree arg &rest body) + (declare (indent 2) (debug (form form body))) + (let ((file (cl-gensym "file"))) + `(let ((,file (magit-convert-git-filename + (magit-git-dir (make-temp-name "index.magit."))))) + (setq ,file (or (file-remote-p ,file 'localname) ,file)) + (unwind-protect + (progn (--when-let ,tree + (or (magit-git-success "read-tree" ,arg it + (concat "--index-output=" ,file)) + (error "Cannot read tree %s" it))) + (if (file-remote-p default-directory) + (let ((magit-tramp-process-environment + (cons (concat "GIT_INDEX_FILE=" ,file) + magit-tramp-process-environment))) + ,@body) + (let ((process-environment + (cons (concat "GIT_INDEX_FILE=" ,file) + process-environment))) + ,@body))) + (ignore-errors + (delete-file (concat (file-remote-p default-directory) ,file))))))) + +(defun magit-commit-tree (message &optional tree &rest parents) + (magit-git-string "commit-tree" "-m" message + (--mapcat (list "-p" it) (delq nil parents)) + (or tree (magit-git-string "write-tree")))) + +(defun magit-commit-worktree (message &optional arg &rest other-parents) + (magit-with-temp-index "HEAD" arg + (and (magit-update-files (magit-modified-files)) + (apply #'magit-commit-tree message nil "HEAD" other-parents)))) + +(defun magit-update-files (files) + (magit-git-success "update-index" "--add" "--remove" "--" files)) + +(defun magit-update-ref (ref message rev &optional stashish) + (or (if (not (version< (magit-git-version) "2.6.0")) + (magit-git-success "update-ref" "--create-reflog" + "-m" message ref rev + (or (magit-rev-verify ref) "")) + ;; `--create-reflog' didn't exist before v2.6.0 + (let ((oldrev (magit-rev-verify ref)) + (logfile (magit-git-dir (concat "logs/" ref)))) + (unless (file-exists-p logfile) + (when oldrev + (magit-git-success "update-ref" "-d" ref oldrev)) + (make-directory (file-name-directory logfile) t) + (with-temp-file logfile) + (when (and oldrev (not stashish)) + (magit-git-success "update-ref" "-m" "enable reflog" + ref oldrev "")))) + (magit-git-success "update-ref" "-m" message ref rev + (or (magit-rev-verify ref) ""))) + (error "Cannot update %s with %s" ref rev))) + +(defconst magit-range-re + (concat "\\`\\([^ \t]*[^.]\\)?" ; revA + "\\(\\.\\.\\.?\\)" ; range marker + "\\([^.][^ \t]*\\)?\\'")) ; revB + +;;; Completion + +(defvar magit-revision-history nil) + +(defun magit-read-branch (prompt &optional default) + (magit-completing-read prompt (magit-list-branch-names) + nil t nil 'magit-revision-history + (or (magit-branch-at-point) + default (magit-get-current-branch)))) + +(defun magit-read-branch-or-commit (prompt &optional secondary-default) + (or (magit-completing-read prompt (cons "HEAD" (magit-list-refnames)) + nil nil nil 'magit-revision-history + (or (magit-branch-or-commit-at-point) + secondary-default + (magit-get-current-branch))) + (user-error "Nothing selected"))) + +(defun magit-read-range-or-commit (prompt &optional secondary-default) + (magit-read-range + prompt + (or (--when-let (magit-region-values 'commit 'branch) + (deactivate-mark) + (concat (car (last it)) ".." (car it))) + (magit-branch-or-commit-at-point) + secondary-default + (magit-get-current-branch)))) + +(defun magit-read-range (prompt &optional default) + (let* ((choose-completion-string-functions + '(crm--choose-completion-string)) + (minibuffer-completion-table #'crm--collection-fn) + (minibuffer-completion-confirm t) + (crm-completion-table (magit-list-refnames)) + (crm-separator "\\.\\.\\.?") + (input (read-from-minibuffer + (concat prompt (and default (format " (%s)" default)) ": ") + nil crm-local-completion-map + nil 'magit-revision-history + default))) + (when (string-equal input "") + (or (setq input default) + (user-error "Nothing selected"))) + input)) + +(defun magit-read-remote-branch + (prompt &optional remote default local-branch require-match) + (let ((choice (magit-completing-read + prompt + (nconc (and local-branch + (if remote + (concat remote "/" local-branch) + (--map (concat it "/" local-branch) + (magit-list-remotes)))) + (magit-list-remote-branch-names remote t)) + nil require-match nil 'magit-revision-history default))) + (if (or remote (string-match "\\`\\([^/]+\\)/\\(.+\\)" choice)) + choice + (user-error "`%s' doesn't have the form REMOTE/BRANCH" choice)))) + +(defun magit-read-local-branch (prompt &optional secondary-default) + (magit-completing-read prompt (magit-list-local-branch-names) + nil t nil 'magit-revision-history + (or (magit-local-branch-at-point) + secondary-default + (magit-get-current-branch)))) + +(defun magit-read-local-branch-or-commit (prompt) + (let ((branches (magit-list-local-branch-names)) + (commit (magit-commit-at-point))) + (or (magit-completing-read prompt + (if commit (cons commit branches) branches) + nil nil nil 'magit-revision-history + (or (magit-local-branch-at-point) commit)) + (user-error "Nothing selected")))) + +(defun magit-read-local-branch-or-ref (prompt &optional secondary-default) + (magit-completing-read prompt (nconc (magit-list-local-branch-names) + (magit-list-refs "refs/")) + nil t nil 'magit-revision-history + (or (magit-local-branch-at-point) + secondary-default + (magit-get-current-branch)))) + +(defun magit-read-other-branch + (prompt &optional exclude secondary-default no-require-match) + (let* ((current (magit-get-current-branch)) + (atpoint (magit-branch-at-point)) + (exclude (or exclude current)) + (default (or (and (not (equal atpoint exclude)) atpoint) + (and (not (equal current exclude)) current) + secondary-default + (magit-get-previous-branch)))) + (magit-completing-read prompt (delete exclude (magit-list-branch-names)) + nil (not no-require-match) + nil 'magit-revision-history default))) + +(defun magit-read-other-branch-or-commit + (prompt &optional exclude secondary-default) + (let* ((current (magit-get-current-branch)) + (atpoint (magit-branch-or-commit-at-point)) + (exclude (or exclude current)) + (default (or (and (not (equal atpoint exclude)) atpoint) + (and (not (equal current exclude)) current) + secondary-default + (magit-get-previous-branch)))) + (or (magit-completing-read prompt (delete exclude (magit-list-refnames)) + nil nil nil 'magit-revision-history default) + (user-error "Nothing selected")))) + +(cl-defun magit-read-upstream-branch + (&optional (branch (magit-get-current-branch)) prompt) + (magit-completing-read + (or prompt (format "Change upstream of %s to" branch)) + (nconc (--map (concat it "/" branch) + (magit-list-remotes)) + (delete branch (magit-list-branch-names))) + nil nil nil 'magit-revision-history + (or (let ((r (magit-remote-branch-at-point)) + (l (magit-branch-at-point))) + (when (and l (equal l branch)) + (setq l nil)) + (if magit-prefer-remote-upstream (or r l) (or l r))) + (let ((r (magit-branch-p "origin/master")) + (l (and (not (equal branch "master")) + (magit-branch-p "master")))) + (if magit-prefer-remote-upstream (or r l) (or l r))) + (let ((previous (magit-get-previous-branch))) + (and (not (equal previous branch)) previous))))) + +(defun magit-read-starting-point (prompt) + (or (magit-completing-read + (concat prompt " starting at") + (cons "HEAD" (magit-list-refnames)) + nil nil nil 'magit-revision-history + (or (let ((r (magit-remote-branch-at-point)) + (l (magit-local-branch-at-point))) + (if magit-prefer-remote-upstream (or r l) (or l r))) + (magit-commit-at-point) + (magit-stash-at-point) + (magit-get-current-branch))) + (user-error "Nothing selected"))) + +(defun magit-read-tag (prompt &optional require-match) + (magit-completing-read prompt (magit-list-tags) nil + require-match nil 'magit-revision-history + (magit-tag-at-point))) + +(defun magit-read-stash (prompt &optional use-at-point) + (let ((atpoint (magit-stash-at-point))) + (or (and use-at-point atpoint) + (let ((stashes (magit-git-lines "stash" "list" "--format=%gd"))) + (magit-completing-read prompt stashes nil t nil nil + (or atpoint (car stashes))))))) + +(defun magit-read-remote (prompt &optional default use-only) + (let ((remotes (magit-list-remotes))) + (if (and use-only (= (length remotes) 1)) + (car remotes) + (magit-completing-read prompt remotes + nil t nil nil + (or default + (magit-remote-at-point) + (magit-get-remote)))))) + +;;; Variables + +(defun magit-get (&rest keys) + "Return the value of Git config entry specified by KEYS." + (magit-git-str "config" (mapconcat 'identity keys "."))) + +(defun magit-get-all (&rest keys) + "Return all values of the Git config entry specified by KEYS." + (let ((magit-git-debug nil)) + (magit-git-lines "config" "--get-all" (mapconcat 'identity keys ".")))) + +(defun magit-get-boolean (&rest keys) + "Return the boolean value of Git config entry specified by KEYS." + (magit-git-true "config" "--bool" (mapconcat 'identity keys "."))) + +(defun magit-set (val &rest keys) + "Set Git config settings specified by KEYS to VAL." + (if val + (magit-git-string "config" (mapconcat 'identity keys ".") val) + (magit-git-string "config" "--unset" (mapconcat 'identity keys ".")))) + +;;; magit-git.el ends soon + +(define-obsolete-function-alias 'magit-get-tracked-ref + 'magit-get-upstream-ref "Magit 2.4.0") +(define-obsolete-function-alias 'magit-get-tracked-branch + 'magit-get-upstream-branch "Magit 2.4.0") +(define-obsolete-function-alias 'magit-get-tracked-remote + 'magit-get-upstream-remote "Magit 2.4.0") + +(provide 'magit-git) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-git.el ends here diff --git a/elpa/magit-20160223.828/magit-log.el b/elpa/magit-20160223.828/magit-log.el new file mode 100644 index 0000000..aed85bd --- /dev/null +++ b/elpa/magit-20160223.828/magit-log.el @@ -0,0 +1,1467 @@ +;;; magit-log.el --- inspect Git history -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library implements support for looking at Git logs, including +;; special logs like reflogs and cherry-logs, as well as for selecting +;; a commit from a log. + +;;; Code: + +(require 'magit-core) +(require 'magit-diff) + +(declare-function magit-blame-chunk-get 'magit-blame) +(declare-function magit-blob-visit 'magit) +(declare-function magit-find-file-noselect 'magit) +(declare-function magit-insert-head-branch-header 'magit) +(declare-function magit-insert-upstream-branch-header 'magit) +(declare-function magit-read-file-from-rev 'magit) +(declare-function magit-show-commit 'magit) +(defvar magit-refs-indent-cherry-lines) +(defvar magit-refs-show-commit-count) +(defvar magit-status-sections-hook) + +(require 'ansi-color) +(require 'crm) + +;;; Options +;;;; Log Mode + +(defgroup magit-log nil + "Inspect and manipulate Git history." + :group 'magit-modes) + +(defcustom magit-log-mode-hook nil + "Hook run after entering Magit-Log mode." + :group 'magit-log + :type 'hook) + +(defcustom magit-log-arguments '("-n256" "--graph" "--decorate") + "The log arguments used in `magit-log-mode' buffers." + :package-version '(magit . "2.3.0") + :group 'magit-log + :group 'magit-commands + :type '(repeat (string :tag "Argument"))) + +(defcustom magit-log-remove-graph-args '("--follow" "--grep" "-G" "-S" "-L") + "The log arguments that cause the `--graph' argument to be dropped." + :package-version '(magit . "2.3.0") + :group 'magit-log + :type '(repeat (string :tag "Argument")) + :options '("--follow" "--grep" "-G" "-S" "-L")) + +(defcustom magit-log-revision-headers-format "\ +%+b +Author: %aN <%aE> +Committer: %cN <%cE>" + "Additional format string used with the `++header' argument." + :package-version '(magit . "2.3.0") + :group 'magit-log + :type 'string) + +(defcustom magit-log-auto-more nil + "Insert more log entries automatically when moving past the last entry. +Only considered when moving past the last entry with +`magit-goto-*-section' commands." + :group 'magit-log + :type 'boolean) + +(defcustom magit-log-show-margin t + "Whether to initially show the margin in log buffers. + +When non-nil the author name and date are initially displayed in +the margin of log buffers. The margin can be shown or hidden in +the current buffer using the command `magit-toggle-margin'. In +status buffers this option is ignored but it is possible to show +the margin using the mentioned command." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type 'boolean) + +(defcustom magit-duration-spec + `((?Y "year" "years" ,(round (* 60 60 24 365.2425))) + (?M "month" "months" ,(round (* 60 60 24 30.436875))) + (?w "week" "weeks" ,(* 60 60 24 7)) + (?d "day" "days" ,(* 60 60 24)) + (?h "hour" "hours" ,(* 60 60)) + (?m "minute" "minutes" 60) + (?s "second" "seconds" 1)) + "Units used to display durations in a human format. +The value is a list of time units, beginning with the longest. +Each element has the form (CHAR UNIT UNITS SECONDS). UNIT is the +time unit, UNITS is the plural of that unit. CHAR is a character +abbreviation. And SECONDS is the number of seconds in one UNIT. +Also see option `magit-log-margin-spec'." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type '(repeat (list (character :tag "Unit character") + (string :tag "Unit singular string") + (string :tag "Unit plural string") + (integer :tag "Seconds in unit")))) + +(defcustom magit-log-margin-spec '(28 7 magit-duration-spec) + "How to format the log margin. + +The log margin is used to display each commit's author followed +by the commit's age. This option controls the total width of the +margin and how time units are formatted, the value has the form: + + (WIDTH UNIT-WIDTH DURATION-SPEC) + +WIDTH specifies the total width of the log margin. UNIT-WIDTH is +either the integer 1, in which case time units are displayed as a +single characters, leaving more room for author names; or it has +to be the width of the longest time unit string in DURATION-SPEC. +DURATION-SPEC has to be a variable, its value controls which time +units, in what language, are being used." + :package-version '(magit . "2.1.0") + :group 'magit-log + :set-after '(magit-duration-spec) + :type '(list (integer :tag "Margin width") + (choice :tag "Time unit style" + (const :format "%t\n" + :tag "abbreviate to single character" 1) + (integer :format "%t\n" + :tag "show full name" 7)) + (variable :tag "Duration spec variable"))) + +(defcustom magit-log-show-refname-after-summary nil + "Whether to show refnames after commit summaries. +This is useful if you use really long branch names." + :package-version '(magit . "2.2.0") + :group 'magit-log + :type 'boolean) + +(defface magit-log-graph + '((((class color) (background light)) :foreground "grey30") + (((class color) (background dark)) :foreground "grey80")) + "Face for the graph part of the log output." + :group 'magit-faces) + +(defface magit-log-author + '((((class color) (background light)) :foreground "firebrick") + (((class color) (background dark)) :foreground "tomato")) + "Face for the author part of the log output." + :group 'magit-faces) + +(defface magit-log-date + '((((class color) (background light)) :foreground "grey30") + (((class color) (background dark)) :foreground "grey80")) + "Face for the date part of the log output." + :group 'magit-faces) + +;;;; Select Mode + +(defcustom magit-log-select-arguments '("-n256" "--decorate") + "The log arguments used in `magit-log-select-mode' buffers." + :package-version '(magit . "2.3.0") + :group 'magit-log + :type '(repeat (string :tag "Argument"))) + +(defcustom magit-log-select-show-usage 'both + "Whether to show usage information when selecting a commit from a log. +The message can be shown in the `echo-area' or the `header-line', or in +`both' places. If the value isn't one of these symbols, then it should +be nil, in which case no usage information is shown." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type '(choice (const :tag "in echo-area" echo-area) + (const :tag "in header-line" header-line) + (const :tag "in both places" both) + (const :tag "nowhere"))) + +;;;; Cherry Mode + +(defcustom magit-cherry-sections-hook + '(magit-insert-cherry-headers + magit-insert-cherry-commits) + "Hook run to insert sections into the cherry buffer." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type 'hook) + +;;;; Reflog Mode + +(defcustom magit-reflog-arguments '("-n256") + "The log arguments used in `magit-reflog-mode' buffers." + :package-version '(magit . "2.3.0") + :group 'magit-log + :group 'magit-commands + :type '(repeat (string :tag "Argument"))) + +(defcustom magit-reflog-show-margin t + "Whether to initially show the margin in reflog buffers. + +When non-nil the author name and date are initially displayed in +the margin of reflog buffers. The margin can be shown or hidden +in the current buffer using the command `magit-toggle-margin'." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type 'boolean) + +(defface magit-reflog-commit '((t :foreground "green")) + "Face for commit commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-amend '((t :foreground "magenta")) + "Face for amend commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-merge '((t :foreground "green")) + "Face for merge, checkout and branch commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-checkout '((t :foreground "blue")) + "Face for checkout commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-reset '((t :foreground "red")) + "Face for reset commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-rebase '((t :foreground "magenta")) + "Face for rebase commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-cherry-pick '((t :foreground "green")) + "Face for cherry-pick commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-remote '((t :foreground "cyan")) + "Face for pull and clone commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-other '((t :foreground "cyan")) + "Face for other commands in reflogs." + :group 'magit-faces) + +;;;; Log Sections + +(defcustom magit-log-section-commit-count 10 + "How many recent commits to show in certain log sections. +How many recent commits `magit-insert-recent-commits' and +`magit-insert-unpulled-from-upstream-or-recent' (provided +the upstream isn't ahead of the current branch) show." + :package-version '(magit . "2.1.0") + :group 'magit-status + :type 'number) + +(defcustom magit-log-section-arguments '("-n256" "--decorate") + "The log arguments used in buffers that show other things besides logs." + :package-version '(magit . "2.4.0") + :group 'magit-log + :group 'magit-status + :type '(repeat (string :tag "Argument"))) + +(define-obsolete-variable-alias 'magit-log-section-args + 'magit-log-section-arguments "2.2.0") + +;;; Commands + +(defvar magit-log-popup + '(:variable magit-log-arguments + :man-page "git-log" + :switches ((?g "Show graph" "--graph") + (?c "Show graph in color" "--color") + (?d "Show refnames" "--decorate") + (?S "Show signatures" "--show-signature") + (?u "Show diffs" "--patch") + (?s "Show diffstats" "--stat") + (?h "Show header" "++header") + (?D "Simplify by decoration" "--simplify-by-decoration") + (?f "Follow renames when showing single-file log" "--follow")) + :options ((?n "Limit number of commits" "-n") + (?f "Limit to files" "-- " magit-read-files) + (?a "Limit to author" "--author=") + (?o "Order commits by" "++order=" magit-log-select-order) + (?g "Search messages" "--grep=") + (?G "Search changes" "-G") + (?S "Search occurences" "-S") + (?L "Trace line evolution" "-L" magit-read-file-trace)) + :actions ((?l "Log current" magit-log-current) + (?L "Log local branches" magit-log-branches) + (?r "Reflog current" magit-reflog-current) + (?o "Log other" magit-log) + (?b "Log all branches" magit-log-all-branches) + (?O "Reflog other" magit-reflog) + (?h "Log HEAD" magit-log-head) + (?a "Log all references" magit-log-all) + (?H "Reflog HEAD" magit-reflog-head)) + :default-action magit-log-current + :max-action-columns 3)) + +(defvar magit-log-mode-refresh-popup + '(:variable magit-log-arguments + :man-page "git-log" + :switches ((?g "Show graph" "--graph") + (?c "Show graph in color" "--color") + (?d "Show refnames" "--decorate") + (?S "Show signatures" "--show-signature") + (?u "Show diffs" "--patch") + (?s "Show diffstats" "--stat") + (?D "Simplify by decoration" "--simplify-by-decoration") + (?f "Follow renames when showing single-file log" "--follow")) + :options ((?n "Limit number of commits" "-n") + (?f "Limit to files" "-- " magit-read-files) + (?a "Limit to author" "--author=") + (?o "Order commits by" "++order=" magit-log-select-order) + (?g "Search messages" "--grep=") + (?G "Search changes" "-G") + (?S "Search occurences" "-S") + (?L "Trace line evolution" "-L" magit-read-file-trace)) + :actions ((?g "Refresh" magit-log-refresh) + (?t "Toggle margin" magit-toggle-margin) + (?s "Set defaults" magit-log-set-default-arguments) nil + (?w "Save defaults" magit-log-save-default-arguments)) + :max-action-columns 2)) + +(defvar magit-reflog-mode-refresh-popup + '(:variable magit-reflog-arguments + :man-page "git-reflog" + :options ((?n "Limit number of commits" "-n")))) + +(defvar magit-log-refresh-popup + '(:variable magit-log-arguments + :man-page "git-log" + :switches ((?g "Show graph" "--graph") + (?c "Show graph in color" "--color") + (?d "Show refnames" "--decorate")) + :options ((?n "Limit number of commits" "-n") + (?o "Order commits by" "++order=" magit-log-select-order)) + :actions ((?g "Refresh" magit-log-refresh) + (?t "Toggle margin" magit-toggle-margin) + (?s "Set defaults" magit-log-set-default-arguments) nil + (?w "Save defaults" magit-log-save-default-arguments)) + :max-action-columns 2)) + +(magit-define-popup-keys-deferred 'magit-log-popup) +(magit-define-popup-keys-deferred 'magit-log-mode-refresh-popup) +(magit-define-popup-keys-deferred 'magit-log-refresh-popup) + +(defun magit-read-file-trace (&rest _ignored) + (let ((file (magit-read-file-from-rev "HEAD" "File")) + (trace (magit-read-string "Trace"))) + (if (string-match + "^\\(/.+/\\|:[^:]+\\|[0-9]+,[-+]?[0-9]+\\)\\(:\\)?$" trace) + (concat trace (or (match-string 2 trace) ":") file) + (user-error "Trace is invalid, see man git-log")))) + +(defun magit-log-select-order (&rest _ignored) + (magit-read-char-case "Order commits by " t + (?t "[t]opography" "topo") + (?a "[a]uthor date" "author-date") + (?c "[c]ommitter date" "date"))) + +(defun magit-log-arguments (&optional refresh) + (cond ((memq magit-current-popup + '(magit-log-popup magit-log-refresh-popup)) + (magit-popup-export-file-args magit-current-popup-args)) + ((derived-mode-p 'magit-log-mode) + (list (nth 1 magit-refresh-args) + (nth 2 magit-refresh-args))) + (refresh + (list magit-log-section-arguments nil)) + (t + (-if-let (buffer (magit-mode-get-buffer 'magit-log-mode)) + (with-current-buffer buffer + (list (nth 1 magit-refresh-args) + (nth 2 magit-refresh-args))) + (list (default-value 'magit-log-arguments) nil))))) + +(defun magit-log-popup (arg) + "Popup console for log commands." + (interactive "P") + (let ((magit-log-refresh-popup + (pcase major-mode + (`magit-log-mode magit-log-mode-refresh-popup) + (_ magit-log-refresh-popup))) + (magit-log-arguments + (-if-let (buffer (magit-mode-get-buffer 'magit-log-mode)) + (with-current-buffer buffer + (magit-popup-import-file-args (nth 1 magit-refresh-args) + (nth 2 magit-refresh-args))) + (default-value 'magit-log-arguments)))) + (magit-invoke-popup 'magit-log-popup nil arg))) + +(defun magit-log-refresh-popup (arg) + "Popup console for changing log arguments in the current buffer." + (interactive "P") + (magit-log-refresh-assert) + (let ((magit-log-refresh-popup + (cond ((derived-mode-p 'magit-log-select-mode) + magit-log-refresh-popup) + ((derived-mode-p 'magit-log-mode) + (let ((def (copy-sequence magit-log-refresh-popup))) + (plist-put def :switches (plist-get magit-log-popup :switches)) + (plist-put def :options (plist-get magit-log-popup :options)) + def)) + (t + magit-log-refresh-popup))) + (magit-log-arguments + (cond ((derived-mode-p 'magit-log-select-mode) + (cadr magit-refresh-args)) + ((derived-mode-p 'magit-log-mode) + (magit-popup-import-file-args (nth 1 magit-refresh-args) + (nth 2 magit-refresh-args))) + (t + magit-log-section-arguments)))) + (magit-invoke-popup 'magit-log-refresh-popup nil arg))) + +(defun magit-log-refresh (args files) + "Set the local log arguments for the current buffer." + (interactive (magit-log-arguments t)) + (magit-log-refresh-assert) + (cond ((derived-mode-p 'magit-log-select-mode) + (setcar (cdr magit-refresh-args) args)) + ((derived-mode-p 'magit-log-mode) + (setcdr magit-refresh-args (list args files))) + (t + (setq-local magit-log-section-arguments args))) + (magit-refresh)) + +(defun magit-log-set-default-arguments (args files) + "Set the global log arguments for the current buffer." + (interactive (magit-log-arguments t)) + (magit-log-refresh-assert) + (cond ((derived-mode-p 'magit-log-select-mode) + (customize-set-variable 'magit-log-select-arguments args) + (setcar (cdr magit-refresh-args) args)) + ((derived-mode-p 'magit-log-mode) + (customize-set-variable 'magit-log-arguments args) + (setcdr magit-refresh-args (list args files))) + (t + (customize-set-variable 'magit-log-section-arguments args) + (kill-local-variable 'magit-log-section-arguments))) + (magit-refresh)) + +(defun magit-log-save-default-arguments (args files) + "Set and save the global log arguments for the current buffer." + (interactive (magit-log-arguments t)) + (magit-log-refresh-assert) + (cond ((derived-mode-p 'magit-log-select-mode) + (customize-save-variable 'magit-log-select-arguments args) + (setcar (cdr magit-refresh-args) args)) + ((derived-mode-p 'magit-log-mode) + (customize-save-variable 'magit-log-arguments args) + (setcdr magit-refresh-args (list args files))) + (t + (customize-save-variable 'magit-log-section-arguments args) + (kill-local-variable 'magit-log-section-arguments))) + (magit-refresh)) + +(defun magit-log-refresh-assert () + (cond ((derived-mode-p 'magit-reflog-mode) + (user-error "Cannot change log arguments in reflog buffers")) + ((derived-mode-p 'magit-cherry-mode) + (user-error "Cannot change log arguments in cherry buffers")))) + +(defvar magit-log-read-revs-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map crm-local-completion-map) + (define-key map "\s" 'self-insert-command) + map)) + +(defun magit-log-read-revs (&optional use-current) + (or (and use-current (--when-let (magit-get-current-branch) (list it))) + (let* ((choose-completion-string-functions + '(crm--choose-completion-string)) + (minibuffer-completion-table #'crm--collection-fn) + (minibuffer-completion-confirm t) + (crm-completion-table + `(,@(and (file-exists-p (magit-git-dir "FETCH_HEAD")) + (list "FETCH_HEAD")) + ,@(magit-list-branch-names))) + (crm-separator "\\(\\.\\.\\.?\\|[, ]\\)") + (default (or (magit-branch-or-commit-at-point) + (unless use-current + (magit-get-previous-branch)))) + (input (read-from-minibuffer + (format "Log rev,s%s: " + (if default (format " (%s)" default) "")) + nil magit-log-read-revs-map + nil 'magit-revision-history default))) + (when (string-equal input "") + (or (setq input default) + (user-error "Nothing selected"))) + (split-string input "[, ]" t)))) + +;;;###autoload +(defun magit-log-current (revs &optional args files) + "Show log for the current branch. +When `HEAD' is detached or with a prefix argument show log for +one or more revs read from the minibuffer." + (interactive (cons (magit-log-read-revs t) + (magit-log-arguments))) + (magit-log revs args files)) + +;;;###autoload +(defun magit-log (revs &optional args files) + "Show log for one or more revs read from the minibuffer. +The user can input any revision or revisions separated by a +space, or even ranges, but only branches and tags, and a +representation of the commit at point, are available as +completion candidates." + (interactive (cons (magit-log-read-revs) + (magit-log-arguments))) + (require 'magit) + (magit-mode-setup #'magit-log-mode revs args files) + (magit-log-goto-same-commit)) + +;;;###autoload +(defun magit-log-head (&optional args files) + "Show log for `HEAD'." + (interactive (magit-log-arguments)) + (magit-log (list "HEAD") args files)) + +;;;###autoload +(defun magit-log-branches (&optional args files) + "Show log for all local branches and `HEAD'." + (interactive (magit-log-arguments)) + (magit-log (if (magit-get-current-branch) + (list "--branches") + (list "HEAD" "--branches")) + args files)) + +;;;###autoload +(defun magit-log-all-branches (&optional args files) + "Show log for all local and remote branches and `HEAD'." + (interactive (magit-log-arguments)) + (magit-log (if (magit-get-current-branch) + (list "--branches" "--remotes") + (list "HEAD" "--branches" "--remotes")) + args files)) + +;;;###autoload +(defun magit-log-all (&optional args files) + "Show log for all references and `HEAD'." + (interactive (magit-log-arguments)) + (magit-log (if (magit-get-current-branch) + (list "--all") + (list "HEAD" "--all")) + args files)) + +;;;###autoload +(defun magit-log-buffer-file (&optional follow beg end) + "Show log for the blob or file visited in the current buffer. +With a prefix argument or when `--follow' is part of +`magit-log-arguments', then follow renames." + (interactive (if (region-active-p) + (list current-prefix-arg + (1- (line-number-at-pos (region-beginning))) + (1- (line-number-at-pos (region-end)))) + (list current-prefix-arg))) + (-if-let (file (magit-file-relative-name)) + (magit-mode-setup #'magit-log-mode + (list (or magit-buffer-refname + (magit-get-current-branch) "HEAD")) + (let ((args (car (magit-log-arguments)))) + (when (and follow (not (member "--follow" args))) + (push "--follow" args)) + (when (and beg end) + (setq args (cons (format "-L%s,%s:%s" beg end file) + (cl-delete "-L" args :test + 'string-prefix-p))) + (setq file nil)) + args) + (and file (list file))) + (user-error "Buffer isn't visiting a file")) + (magit-log-goto-same-commit)) + +;;;###autoload +(defun magit-reflog-current () + "Display the reflog of the current branch." + (interactive) + (magit-reflog (magit-get-current-branch))) + +;;;###autoload +(defun magit-reflog (ref) + "Display the reflog of a branch." + (interactive (list (magit-read-local-branch-or-ref "Show reflog for"))) + (magit-mode-setup #'magit-reflog-mode ref magit-reflog-arguments)) + +;;;###autoload +(defun magit-reflog-head () + "Display the `HEAD' reflog." + (interactive) + (magit-reflog "HEAD")) + +(defun magit-log-toggle-commit-limit () + "Toggle the number of commits the current log buffer is limited to. +If the number of commits is currently limited, then remove that +limit. Otherwise set it to 256." + (interactive) + (magit-log-set-commit-limit (lambda (&rest _) nil))) + +(defun magit-log-double-commit-limit () + "Double the number of commits the current log buffer is limited to." + (interactive) + (magit-log-set-commit-limit '*)) + +(defun magit-log-half-commit-limit () + "Half the number of commits the current log buffer is limited to." + (interactive) + (magit-log-set-commit-limit '/)) + +(defun magit-log-set-commit-limit (fn) + (let* ((val (car (magit-log-arguments t))) + (arg (--first (string-match "^-n\\([0-9]+\\)?$" it) val)) + (num (and arg (string-to-number (match-string 1 arg)))) + (num (if num (funcall fn num 2) 256))) + (setq val (delete arg val)) + (setcar (cdr magit-refresh-args) + (if (and num (> num 0)) + (cons (format "-n%i" num) val) + val))) + (magit-refresh)) + +(defun magit-log-get-commit-limit () + (--when-let (--first (string-match "^-n\\([0-9]+\\)?$" it) + (car (magit-log-arguments t))) + (string-to-number (match-string 1 it)))) + +(defun magit-log-bury-buffer (&optional arg) + "Bury the current buffer or the revision buffer in the same frame. +Like `magit-mode-bury-buffer' (which see) but with a negative +prefix argument instead bury the revision buffer, provided it +is displayed in the current frame." + (interactive "p") + (if (< arg 0) + (let* ((buf (magit-mode-get-buffer 'magit-revision-mode)) + (win (and buf (get-buffer-window buf (selected-frame))))) + (if win + (with-selected-window win + (with-current-buffer buf + (magit-mode-bury-buffer (> (abs arg) 1)))) + (user-error "No revision buffer in this frame"))) + (magit-mode-bury-buffer (> arg 1)))) + +;;; Log Mode + +(defvar magit-log-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map "\C-c\C-b" 'magit-go-backward) + (define-key map "\C-c\C-f" 'magit-go-forward) + (define-key map "=" 'magit-log-toggle-commit-limit) + (define-key map "+" 'magit-log-double-commit-limit) + (define-key map "-" 'magit-log-half-commit-limit) + (define-key map "q" 'magit-log-bury-buffer) + map) + "Keymap for `magit-log-mode'.") + +(define-derived-mode magit-log-mode magit-mode "Magit Log" + "Mode for looking at Git log. + +This mode is documented in info node `(magit)Log Buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit at point. + +Type \\[magit-branch-popup] to see available branch commands. +Type \\[magit-merge-popup] to merge the branch or commit at point. +Type \\[magit-cherry-pick-popup] to apply the commit at point. +Type \\[magit-reset] to reset HEAD to the commit at point. + +\\{magit-log-mode-map}" + :group 'magit-log + (hack-dir-local-variables-non-file-buffer)) + +(defvar magit-log-disable-graph-hack-args + '("-G" "--grep" "--author") + "Arguments which disable the graph speedup hack.") + +(defun magit-log-refresh-buffer (revs args files) + (setq header-line-format + (propertize + (concat " Commits in " (mapconcat 'identity revs " ") + (and files (concat " touching " + (mapconcat 'identity files " ")))) + 'face 'magit-header-line)) + (unless (= (length files) 1) + (setq args (remove "--follow" args))) + (when (--any-p (string-match-p + (concat "^" (regexp-opt magit-log-remove-graph-args)) it) + args) + (setq args (remove "--graph" args))) + (unless (member "--graph" args) + (setq args (remove "--color" args))) + (-when-let* ((limit (magit-log-get-commit-limit)) + (limit (* 2 limit)) ; increase odds for complete graph + (count (and (= (length revs) 1) + (> limit 1024) ; otherwise it's fast enough + (setq revs (car revs)) + (not (string-match-p "\\.\\." revs)) + (not (member revs '("--all" "--branches"))) + (-none-p (lambda (arg) + (--any-p (string-prefix-p it arg) + magit-log-disable-graph-hack-args)) + args) + (magit-git-string "rev-list" "--count" + "--first-parent" args revs)))) + (setq revs (if (< (string-to-number count) limit) + revs + (format "%s~%s..%s" revs limit revs)))) + (magit-insert-section (logbuf) + (magit-insert-log revs args files))) + +(defun magit-insert-log (revs &optional args files) + "Insert a log section. +Do not add this to a hook variable." + (magit-git-wash (apply-partially #'magit-log-wash-log 'log) + "log" + (format "--format=%%h%s %s[%%aN][%%at]%%s%s" + (if (member "--decorate" args) "%d" "") + (if (member "--show-signature" args) + (progn (setq args (remove "--show-signature" args)) "%G?") + "") + (if (member "++header" args) + (if (member "--graph" (setq args (remove "++header" args))) + (concat "\n" magit-log-revision-headers-format "\n") + (concat "\n" magit-log-revision-headers-format "\n")) + "")) + (progn + (--when-let (--first (string-match "^\\+\\+order=\\(.+\\)$" it) args) + (setq args (cons (format "--%s-order" (match-string 1 it)) + (remove it args)))) + (if (member "--decorate" args) + (cons "--decorate=full" (remove "--decorate" args)) + args)) + "--use-mailmap" "--no-prefix" revs "--" files)) + +(defvar magit-commit-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-visit-thing] 'magit-show-commit) + (define-key map "a" 'magit-cherry-apply) + map) + "Keymap for `commit' sections.") + +(defvar magit-module-commit-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-visit-thing] 'magit-show-commit) + map) + "Keymap for `module-commit' sections.") + +(defconst magit-log-heading-re + (concat "^" + "\\(?4:[-_/|\\*o. ]*\\)" ; graph + "\\(?1:[0-9a-fA-F]+\\) " ; sha1 + "\\(?:\\(?3:([^()]+)\\) \\)?" ; refs + "\\(?7:[BGUN]\\)?" ; gpg + "\\[\\(?5:[^]]*\\)\\]" ; author + "\\[\\(?6:[^]]*\\)\\]" ; date + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-cherry-re + (concat "^" + "\\(?8:[-+]\\) " ; cherry + "\\(?1:[0-9a-fA-F]+\\) " ; sha1 + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-module-re + (concat "^" + "\\(?:\\(?11:[<>]\\) \\)?" ; side + "\\(?1:[0-9a-fA-F]+\\) " ; sha1 + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-bisect-vis-re + (concat "^" + "\\(?1:[0-9a-fA-F]+\\) " ; sha1 + "\\(?:\\(?3:([^()]+)\\) \\)?" ; refs + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-bisect-log-re + (concat "^# " + "\\(?3:bad:\\|skip:\\|good:\\) " ; "refs" + "\\[\\(?1:[^]]+\\)\\] " ; sha1 + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-reflog-re + (concat "^" + "\\(?1:[^ ]+\\) " ; sha1 + "\\(?:\\(?:[^@]+@{\\(?6:[^}]+\\)} " ; date + "\\(?10:merge \\|autosave \\|restart \\|[^:]+: \\)?" ; refsub + "\\(?2:.*\\)?\\)\\| \\)$")) ; msg + +(defconst magit-reflog-subject-re + (concat "\\(?1:[^ ]+\\) ?" ; command + "\\(?2:\\(?: ?-[^ ]+\\)+\\)?" ; option + "\\(?: ?(\\(?3:[^)]+\\))\\)?")) ; type + +(defconst magit-log-stash-re + (concat "^" + "\\(?1:[^ ]+\\)" ; "sha1" + "\\(?5: \\)" ; "author" + "\\(?6:[^ ]+\\) " ; date + "\\(?2:.*\\)$")) ; msg + +(defvar magit-log-count nil) + +(defun magit-log-wash-log (style args) + (setq args (-flatten args)) + (when (and (member "--graph" args) + (member "--color" args)) + (let ((ansi-color-apply-face-function + (lambda (beg end face) + (put-text-property beg end 'font-lock-face + (or face 'magit-log-graph))))) + (ansi-color-apply-on-region (point-min) (point-max)))) + (when (eq style 'cherry) + (reverse-region (point-min) (point-max))) + (let ((magit-log-count 0) + (abbrev (magit-abbrev-length))) + (magit-wash-sequence (apply-partially 'magit-log-wash-rev style abbrev)) + (if (derived-mode-p 'magit-log-mode) + (when (eq magit-log-count (magit-log-get-commit-limit)) + (magit-insert-section (longer) + (insert-text-button + (substitute-command-keys + (format "Type \\<%s>\\[%s] to show more history" + 'magit-log-mode-map + 'magit-log-double-commit-limit)) + 'action (lambda (_button) + (magit-log-double-commit-limit)) + 'follow-link t + 'mouse-face 'magit-section-highlight))) + (unless (equal (car args) "cherry") + (insert ?\n))))) + +(defun magit-log-wash-rev (style abbrev) + (when (derived-mode-p 'magit-log-mode) + (cl-incf magit-log-count)) + (looking-at (pcase style + (`log magit-log-heading-re) + (`cherry magit-log-cherry-re) + (`module magit-log-module-re) + (`reflog magit-log-reflog-re) + (`stash magit-log-stash-re) + (`bisect-vis magit-log-bisect-vis-re) + (`bisect-log magit-log-bisect-log-re))) + (magit-bind-match-strings + (hash msg refs graph author date gpg cherry _ refsub side) nil + (let ((align (not (member "--stat" (cadr magit-refresh-args))))) + (magit-delete-line) + (magit-insert-section section (commit hash) + (pcase style + (`stash (setf (magit-section-type section) 'stash)) + (`module (setf (magit-section-type section) 'module-commit)) + (`bisect-log (setq hash (magit-rev-parse "--short" hash)))) + (when cherry + (when (and (derived-mode-p 'magit-refs-mode) + magit-refs-show-commit-count) + (insert (make-string magit-refs-indent-cherry-lines ?\s))) + (insert (propertize cherry 'face (if (string= cherry "-") + 'magit-cherry-equivalent + 'magit-cherry-unmatched))) + (insert ?\s)) + (when side + (insert (propertize side 'face (if (string= side "<") + 'magit-diff-removed + 'magit-diff-added))) + (insert ?\s)) + (when align + (insert (propertize hash 'face 'magit-hash) ?\s)) + (when graph + (insert graph)) + (unless align + (insert (propertize hash 'face 'magit-hash) ?\s)) + (when (and refs (not magit-log-show-refname-after-summary)) + (insert (magit-format-ref-labels refs) ?\s)) + (when refsub + (insert (format "%-2s " (1- magit-log-count))) + (insert (magit-reflog-format-subject + (substring refsub 0 (if (string-match-p ":" refsub) -2 -1))))) + (when msg + (insert (propertize msg 'face + (pcase (and gpg (aref gpg 0)) + (?G 'magit-signature-good) + (?B 'magit-signature-bad) + (?U 'magit-signature-untrusted))))) + (when (and refs magit-log-show-refname-after-summary) + (insert ?\s) + (insert (magit-format-ref-labels refs))) + (insert ?\n) + (when (memq style '(log reflog stash)) + (goto-char (line-beginning-position)) + (when (and refsub + (string-match "\\`\\([^ ]\\) \\+\\(..\\)\\(..\\)" date)) + (setq date (+ (string-to-number (match-string 1 date)) + (* (string-to-number (match-string 2 date)) 60 60) + (* (string-to-number (match-string 3 date)) 60)))) + (save-excursion + (backward-char) + (magit-format-log-margin author date))) + (when (and (eq style 'log) + (not (or (eobp) (looking-at magit-log-heading-re)))) + (when (looking-at "") + (magit-insert-heading) + (delete-char 1) + (magit-insert-section (commit-header) + (forward-line) + (magit-insert-heading) + (re-search-forward "") + (backward-delete-char 1) + (forward-char) + (insert ?\n)) + (delete-char 1)) + (if (looking-at "^\\(---\\|\n\s\\|\ndiff\\)") + (let ((limit (save-excursion + (and (re-search-forward magit-log-heading-re nil t) + (match-beginning 0))))) + (unless (magit-section-content magit-insert-section--current) + (magit-insert-heading)) + (delete-char (if (looking-at "\n") 1 4)) + (magit-diff-wash-diffs (list "--stat") limit)) + (when align + (setq align (make-string (1+ abbrev) ? ))) + (when (and (not (eobp)) (not (looking-at magit-log-heading-re))) + (when align + (setq align (make-string (1+ abbrev) ? ))) + (while (and (not (eobp)) (not (looking-at magit-log-heading-re))) + (when align + (save-excursion (insert align))) + (magit-format-log-margin) + (forward-line)) + ;; When `--format' is used and its value isn't one of the + ;; predefined formats, then `git-log' does not insert a + ;; separator line. + (save-excursion + (forward-line -1) + (looking-at "[-_/|\\*o. ]*")) + (setq graph (match-string 0)) + (unless (string-match-p "[/\\]" graph) + (insert graph ?\n)))))))) + t) + +(defun magit-format-log-margin (&optional author date) + (-let [(width unit-width duration-spec) magit-log-margin-spec] + (when (and date (not author)) + (setq width (+ (if (= unit-width 1) 1 (1+ unit-width)) + (if (derived-mode-p 'magit-log-mode) 1 0)))) + (if date + (magit-make-margin-overlay + (and author + (concat (propertize (truncate-string-to-width + (or author "") + (- width 1 3 ; gap, digits + (if (= unit-width 1) 1 (1+ unit-width)) + (if (derived-mode-p 'magit-log-mode) 1 0)) + nil ?\s (make-string 1 magit-ellipsis)) + 'face 'magit-log-author) + " ")) + (propertize (magit-format-duration + (abs (truncate (- (float-time) + (string-to-number date)))) + (symbol-value duration-spec) + unit-width) + 'face 'magit-log-date) + (and (derived-mode-p 'magit-log-mode) + (propertize " " 'face 'fringe))) + (magit-make-margin-overlay + (propertize (make-string (1- width) ?\s) 'face 'default) + (propertize " " 'face 'fringe))))) + +(defun magit-format-duration (duration spec &optional width) + (-let [(char unit units weight) (car spec)] + (let ((cnt (round (/ duration weight 1.0)))) + (if (or (not (cdr spec)) + (>= (/ duration weight) 1)) + (if (eq width 1) + (format "%3i%c" cnt char) + (format (if width (format "%%3i %%-%is" width) "%i %s") + cnt (if (= cnt 1) unit units))) + (magit-format-duration duration (cdr spec) width))))) + +(defun magit-log-maybe-show-more-commits (section) + "Automatically insert more commit sections in a log. +Only do so if `point' is on the \"show more\" section, +and `magit-log-auto-more' is non-nil." + (when (and (eq (magit-section-type section) 'longer) + magit-log-auto-more) + (magit-log-double-commit-limit) + (forward-line -1) + (magit-section-forward))) + +(defvar magit--update-revision-buffer nil) + +(defun magit-log-maybe-update-revision-buffer (&optional _) + "When moving in the log buffer, update the revision buffer. +If there is no revision buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-log-mode) + (magit-log-maybe-update-revision-buffer-1))) + +(defun magit-log-maybe-update-revision-buffer-1 () + (unless magit--update-revision-buffer + (-when-let* ((commit (magit-section-when 'commit)) + (buffer (magit-mode-get-buffer 'magit-revision-mode nil t))) + (setq magit--update-revision-buffer (list commit buffer)) + (run-with-idle-timer + magit-update-other-window-delay nil + (lambda () + (-let [(rev buf) magit--update-revision-buffer] + (setq magit--update-revision-buffer nil) + (when (buffer-live-p buf) + (let ((magit-display-buffer-noselect t)) + (apply #'magit-show-commit rev (magit-diff-arguments))))) + (setq magit--update-revision-buffer nil)))))) + +(defvar magit--update-blob-buffer nil) + +(defun magit-log-maybe-update-blob-buffer (&optional _) + "When moving in the log buffer, update the blob buffer. +If there is no blob buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-log-mode) + (magit-log-maybe-update-blob-buffer-1))) + +(defun magit-log-maybe-update-blob-buffer-1 () + (unless magit--update-revision-buffer + (-when-let* ((commit (magit-section-when 'commit)) + (buffer (--first (with-current-buffer it magit-buffer-revision) + (-map #'window-buffer (window-list))))) + (setq magit--update-blob-buffer (list commit buffer)) + (run-with-idle-timer + magit-update-other-window-delay nil + (lambda () + (-let [(rev buf) magit--update-blob-buffer] + (setq magit--update-blob-buffer nil) + (when (buffer-live-p buf) + (save-excursion + (with-selected-window (get-buffer-window buf) + (with-current-buffer buf + (magit-blob-visit (list (magit-rev-parse rev) + (magit-file-relative-name + magit-buffer-file-name)) + (line-number-at-pos)))))))))))) + +(defun magit-log-goto-same-commit () + (-when-let* ((prev magit-previous-section) + (rev (cond ((magit-section-match 'commit prev) + (magit-section-value prev)) + ((magit-section-match 'branch prev) + (magit-rev-format + "%h" (magit-section-value prev))))) + (same (--first (equal (magit-section-value it) rev) + (magit-section-children magit-root-section)))) + (goto-char (magit-section-start same)))) + +;;; Select Mode + +(defvar magit-log-select-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-log-mode-map) + (define-key map "\C-c\C-b" 'undefined) + (define-key map "\C-c\C-f" 'undefined) + (define-key map "." 'magit-log-select-pick) + (define-key map "e" 'magit-log-select-pick) + (define-key map "\C-c\C-c" 'magit-log-select-pick) + (define-key map "q" 'magit-log-select-quit) + (define-key map "\C-c\C-k" 'magit-log-select-quit) + map) + "Keymap for `magit-log-select-mode'.") + +(put 'magit-log-select-pick :advertised-binding [?\C-c ?\C-c]) +(put 'magit-log-select-quit :advertised-binding [?\C-c ?\C-k]) + +(define-derived-mode magit-log-select-mode magit-log-mode "Magit Select" + "Mode for selecting a commit from history. + +This mode is documented in info node `(magit)Select from log'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit at point. + +\\\ +Type \\[magit-log-select-pick] to select the commit at point. +Type \\[magit-log-select-quit] to abort without selecting a commit." + :group 'magit-log + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-log-select-refresh-buffer (rev args) + (magit-insert-section (logbuf) + (magit-insert-log rev args))) + +(defvar-local magit-log-select-pick-function nil) +(defvar-local magit-log-select-quit-function nil) + +(defun magit-log-select (pick &optional msg quit branch) + (declare (indent defun)) + (magit-mode-setup #'magit-log-select-mode + (or branch (magit-get-current-branch) "HEAD") + magit-log-select-arguments) + (magit-log-goto-same-commit) + (setq magit-log-select-pick-function pick) + (setq magit-log-select-quit-function quit) + (when magit-log-select-show-usage + (setq msg (substitute-command-keys + (format-spec + (if msg + (if (string-suffix-p "," msg) + (concat msg " or %q to abort") + msg) + "Type %p to select commit at point, or %q to abort") + '((?p . "\\[magit-log-select-pick]") + (?q . "\\[magit-log-select-quit]"))))) + (when (memq magit-log-select-show-usage '(both header-line)) + (setq header-line-format (propertize (concat " " msg) 'face 'bold))) + (when (memq magit-log-select-show-usage '(both echo-area)) + (message "%s" msg)))) + +(defun magit-log-select-pick () + "Select the commit at point and act on it. +Call `magit-log-select-pick-function' with the selected +commit as argument." + (interactive) + (let ((fun magit-log-select-pick-function) + (rev (magit-commit-at-point))) + (kill-buffer (current-buffer)) + (funcall fun rev))) + +(defun magit-log-select-quit () + "Abort selecting a commit, don't act on any commit." + (interactive) + (kill-buffer (current-buffer)) + (when magit-log-select-quit-function + (funcall magit-log-select-quit-function))) + +;;; Cherry Mode + +(defvar magit-cherry-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map "q" 'magit-log-bury-buffer) + (define-key map "L" 'magit-toggle-margin) + map) + "Keymap for `magit-cherry-mode'.") + +(define-derived-mode magit-cherry-mode magit-mode "Magit Cherry" + "Mode for looking at commits not merged upstream. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit at point. + +Type \\[magit-cherry-pick-popup] to apply the commit at point. + +\\{magit-cherry-mode-map}" + :group 'magit-log + (hack-dir-local-variables-non-file-buffer)) + +;;;###autoload +(defun magit-cherry (head upstream) + "Show commits in a branch that are not merged in the upstream branch." + (interactive + (let ((head (magit-read-branch "Cherry head"))) + (list head (magit-read-other-branch "Cherry upstream" head + (magit-get-upstream-branch head))))) + (magit-mode-setup #'magit-cherry-mode upstream head)) + +(defun magit-cherry-refresh-buffer (_upstream _head) + (magit-insert-section (cherry) + (run-hooks 'magit-cherry-sections-hook))) + +(defun magit-insert-cherry-headers () + "Insert headers appropriate for `magit-cherry-mode' buffers." + (magit-insert-head-branch-header (nth 1 magit-refresh-args)) + (magit-insert-upstream-branch-header (nth 1 magit-refresh-args) + (nth 0 magit-refresh-args) + "Upstream: ") + (insert ?\n)) + +(defun magit-insert-cherry-commits () + "Insert commit sections into a `magit-cherry-mode' buffer." + (magit-insert-section (cherries) + (magit-insert-heading "Cherry commits:") + (magit-git-wash (apply-partially 'magit-log-wash-log 'cherry) + "cherry" "-v" "--abbrev" magit-refresh-args))) + +;;; Reflog Mode + +(defvar magit-reflog-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-log-mode-map) + (define-key map "L" 'magit-toggle-margin) + map) + "Keymap for `magit-reflog-mode'.") + +(define-derived-mode magit-reflog-mode magit-log-mode "Magit Reflog" + "Mode for looking at Git reflog. + +This mode is documented in info node `(magit)Reflog'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit at point. + +Type \\[magit-cherry-pick-popup] to apply the commit at point. +Type \\[magit-reset] to reset HEAD to the commit at point. + +\\{magit-reflog-mode-map}" + :group 'magit-log + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-reflog-refresh-buffer (ref args) + (setq header-line-format + (propertize (concat " Reflog for " ref) 'face 'magit-header-line)) + (magit-insert-section (reflogbuf) + (magit-git-wash (apply-partially 'magit-log-wash-log 'reflog) + "reflog" "show" "--format=%h %gd %gs" "--date=raw" args ref))) + +(defvar magit-reflog-labels + '(("commit" . magit-reflog-commit) + ("amend" . magit-reflog-amend) + ("merge" . magit-reflog-merge) + ("checkout" . magit-reflog-checkout) + ("branch" . magit-reflog-checkout) + ("reset" . magit-reflog-reset) + ("rebase" . magit-reflog-rebase) + ("cherry-pick" . magit-reflog-cherry-pick) + ("initial" . magit-reflog-commit) + ("pull" . magit-reflog-remote) + ("clone" . magit-reflog-remote) + ("autosave" . magit-reflog-commit) + ("restart" . magit-reflog-reset))) + +(defun magit-reflog-format-subject (subject) + (let* ((match (string-match magit-reflog-subject-re subject)) + (command (and match (match-string 1 subject))) + (option (and match (match-string 2 subject))) + (type (and match (match-string 3 subject))) + (label (if (string= command "commit") + (or type command) + command)) + (text (if (string= command "commit") + label + (mapconcat #'identity + (delq nil (list command option type)) + " ")))) + (format "%-16s " + (propertize text 'face + (or (cdr (assoc label magit-reflog-labels)) + 'magit-reflog-other))))) + +;;; Log Sections +;;;; Standard Log Sections + +(defvar magit-unpulled-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-visit-thing] 'magit-diff-dwim) + map) + "Keymap for `unpulled' sections.") + +(magit-define-section-jumper magit-jump-to-unpulled-from-upstream + "Unpulled from @{upstream}" unpulled "..@{upstream}") + +(defun magit-insert-unpulled-from-upstream () + "Insert commits that haven't been pulled from the upstream yet." + (when (magit-git-success "rev-parse" "@{upstream}") + (magit-insert-section (unpulled "..@{upstream}") + (magit-insert-heading + (format (propertize "Unpulled from %s:" 'face 'magit-section-heading) + (magit-get-upstream-branch))) + (magit-insert-log "..@{upstream}" magit-log-section-arguments) + (magit-section-cache-visibility)))) + +(magit-define-section-jumper magit-jump-to-unpulled-from-pushremote + "Unpulled from " unpulled + (concat ".." (magit-get-push-branch))) + +(defun magit-insert-unpulled-from-pushremote () + "Insert commits that haven't been pulled from the push-remote yet." + (--when-let (magit-get-push-branch) + (unless (and (equal (magit-rev-name it) + (magit-rev-name "@{upstream}")) + (or (memq 'magit-insert-unpulled-from-upstream + magit-status-sections-hook) + (memq 'magit-insert-unpulled-from-upstream-or-recent + magit-status-sections-hook))) + (magit-insert-section (unpulled (concat ".." it)) + (magit-insert-heading + (format (propertize "Unpulled from %s:" 'face 'magit-section-heading) + (propertize it 'face 'magit-branch-remote))) + (magit-insert-log (concat ".." it) magit-log-section-arguments) + (magit-section-cache-visibility))))) + +(defvar magit-unpushed-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-visit-thing] 'magit-diff-dwim) + map) + "Keymap for `unpushed' sections.") + +(magit-define-section-jumper magit-jump-to-unpushed-to-upstream + "Unpushed to @{upstream}" unpushed "@{upstream}..") + +(defun magit-insert-unpushed-to-upstream () + "Insert commits that haven't been pushed to the upstream yet." + (when (magit-git-success "rev-parse" "@{upstream}") + (magit-insert-section (unpushed "@{upstream}..") + (magit-insert-heading + (format (propertize "Unmerged into %s:" 'face 'magit-section-heading) + (magit-get-upstream-branch))) + (magit-insert-log "@{upstream}.." magit-log-section-arguments) + (magit-section-cache-visibility)))) + +(magit-define-section-jumper magit-jump-to-unpushed-to-pushremote + "Unpushed to " unpushed + (concat (magit-get-push-branch) "..")) + +(defun magit-insert-unpushed-to-pushremote () + "Insert commits that haven't been pushed to the push-remote yet." + (--when-let (magit-get-push-branch) + (unless (and (equal (magit-rev-name it) + (magit-rev-name "@{upstream}")) + (memq 'magit-insert-unpushed-to-upstream + magit-status-sections-hook)) + (magit-insert-section (unpushed (concat it "..")) + (magit-insert-heading + (format (propertize "Unpushed to %s:" 'face 'magit-section-heading) + (propertize it 'face 'magit-branch-remote))) + (magit-insert-log (concat it "..") magit-log-section-arguments) + (magit-section-cache-visibility))))) + +(defun magit-insert-recent-commits (&optional collapse) + "Insert section showing recent commits. +Show the last `magit-log-section-commit-count' commits." + (let* ((start (format "HEAD~%s" magit-log-section-commit-count)) + (range (and (magit-rev-verify start) + (concat start "..HEAD")))) + (magit-insert-section (recent range collapse) + (magit-insert-heading "Recent commits:") + (magit-insert-log range + (cons (format "-%d" magit-log-section-commit-count) + magit-log-section-arguments))))) + +(defun magit-insert-unpulled-from-upstream-or-recent () + "Insert section showing unpulled or recent commits. +If an upstream is configured for the current branch and it is +ahead of the current branch, then show the commits that have +not yet been pulled into the current branch. If no upstream is +configured or if the upstream is not ahead of the current branch, +then show the last `magit-log-section-commit-count' commits." + (if (equal (magit-rev-parse "HEAD") + (magit-rev-parse "@{upstream}")) + (magit-insert-recent-commits t) + (magit-insert-unpulled-from-upstream))) + +;;;; Auxiliary Log Sections + +(defun magit-insert-unpulled-cherries () + "Insert section showing unpulled commits. +Like `magit-insert-unpulled-to-upstream' but prefix each commit +which has not been applied yet (i.e. a commit with a patch-id +not shared with any local commit) with \"+\", and all others with +\"-\"." + (when (magit-git-success "rev-parse" "@{upstream}") + (magit-insert-section (unpulled "..@{upstream}") + (magit-insert-heading "Unpulled commits:") + (magit-git-wash (apply-partially 'magit-log-wash-log 'cherry) + "cherry" "-v" (magit-abbrev-arg) + (magit-get-current-branch) "@{upstream}")))) + +(defun magit-insert-unpushed-cherries () + "Insert section showing unpushed commits. +Like `magit-insert-unpushed-to-upstream' but prefix each commit +which has not been applied to upstream yet (i.e. a commit with +a patch-id not shared with any upstream commit) with \"+\", and +all others with \"-\"." + (when (magit-git-success "rev-parse" "@{upstream}") + (magit-insert-section (unpushed "@{upstream}..") + (magit-insert-heading "Unpushed commits:") + (magit-git-wash (apply-partially 'magit-log-wash-log 'cherry) + "cherry" "-v" (magit-abbrev-arg) "@{upstream}")))) + +;;; Buffer Margins + +(defvar-local magit-set-buffer-margin-refresh nil) + +(defvar-local magit-show-margin nil) +(put 'magit-show-margin 'permanent-local t) + +(defun magit-toggle-margin () + "Show or hide the Magit margin." + (interactive) + (unless (derived-mode-p 'magit-log-mode 'magit-status-mode 'magit-refs-mode) + (user-error "Buffer doesn't contain any logs")) + (magit-set-buffer-margin (not (cdr (window-margins))))) + +(defun magit-maybe-show-margin () + "Maybe show the margin, depending on the major-mode and an option. +Supported modes are `magit-log-mode' and `magit-reflog-mode', +and the respective options are `magit-log-show-margin' and +`magit-reflog-show-margin'." + (if (local-variable-p 'magit-show-margin) + (magit-set-buffer-margin magit-show-margin) + (pcase major-mode + (`magit-log-mode (magit-set-buffer-margin magit-log-show-margin)) + (`magit-reflog-mode (magit-set-buffer-margin magit-reflog-show-margin))))) + +(defun magit-set-buffer-margin (enable) + (let ((width (cond ((not enable) nil) + ((derived-mode-p 'magit-reflog-mode) + (+ (cadr magit-log-margin-spec) 5)) + (t (car magit-log-margin-spec))))) + (setq magit-show-margin width) + (when (and enable magit-set-buffer-margin-refresh) + (magit-refresh)) + (-when-let (window (get-buffer-window)) + (with-selected-window window + (set-window-margins nil (car (window-margins)) width) + (if enable + (add-hook 'window-configuration-change-hook + 'magit-set-buffer-margin-1 nil t) + (remove-hook 'window-configuration-change-hook + 'magit-set-buffer-margin-1 t)))))) + +(defun magit-set-buffer-margin-1 () + (-when-let (window (get-buffer-window)) + (with-selected-window window + (set-window-margins nil (car (window-margins)) magit-show-margin)))) + +(defun magit-make-margin-overlay (&rest strings) + ;; Don't put the overlay on the complete line to work around #1880. + (let ((o (make-overlay (1+ (line-beginning-position)) + (line-end-position) + nil t))) + (overlay-put o 'evaporate t) + (overlay-put o 'before-string + (propertize "o" 'display + (list '(margin right-margin) + (apply #'concat strings)))))) + +;;; magit-log.el ends soon + +(define-obsolete-function-alias 'magit-insert-unpulled-or-recent-commits + 'magit-insert-unpulled-from-upstream-or-recent "Magit 2.4.0") + +(provide 'magit-log) +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: +;;; magit-log.el ends here diff --git a/elpa/magit-20160223.828/magit-mode.el b/elpa/magit-20160223.828/magit-mode.el new file mode 100644 index 0000000..fbae834 --- /dev/null +++ b/elpa/magit-20160223.828/magit-mode.el @@ -0,0 +1,962 @@ +;;; magit-mode.el --- create and refresh Magit buffers -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library implements the abstract major-mode `magit-mode' from +;; which almost all other Magit major-modes derive. The code in here +;; is mostly concerned with creating and refreshing Magit buffers. + +;;; Code: + +(require 'cl-lib) +(require 'dash) + +(require 'magit-section) +(require 'magit-git) + +;; For `magit-xref-insert-buttons' from `magit' +(defvar magit-diff-show-xref-buttons) +(defvar magit-revision-show-xref-buttons) +;; For `magit-refresh' and `magit-refresh-all' +(declare-function magit-auto-revert-buffers 'magit-autorevert) + +(require 'format-spec) +(require 'help-mode) + +;;; Options + +(defcustom magit-mode-hook + '(magit-load-config-extensions + magit-xref-setup) + "Hook run when entering a mode derived from Magit mode." + :group 'magit-modes + :type 'hook + :options '(magit-load-config-extensions + magit-xref-setup + bug-reference-mode)) + +(defcustom magit-mode-setup-hook + '(magit-maybe-save-repository-buffers + magit-maybe-show-margin) + "Hook run by `magit-mode-setup'." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type 'hook + :options '(magit-maybe-save-repository-buffers + magit-maybe-show-margin)) + +(defcustom magit-pre-refresh-hook '(magit-maybe-save-repository-buffers) + "Hook run before refreshing in `magit-refresh'. + +This hook, or `magit-post-refresh-hook', should be used +for functions that are not tied to a particular buffer. + +To run a function with a particular buffer current, use +`magit-refresh-buffer-hook' and use `derived-mode-p' +inside your function." + :package-version '(magit . "2.4.0") + :group 'magit-modes + :type 'hook + :options '(magit-maybe-save-repository-buffers)) + +(defcustom magit-post-refresh-hook nil + "Hook run after refreshing in `magit-refresh'. + +This hook, or `magit-pre-refresh-hook', should be used +for functions that are not tied to a particular buffer. + +To run a function with a particular buffer current, use +`magit-refresh-buffer-hook' and use `derived-mode-p' +inside your function." + :package-version '(magit . "2.4.0") + :group 'magit-modes + :type 'hook) + +(defcustom magit-display-buffer-function 'magit-display-buffer-traditional + "The function used display a Magit buffer. + +All Magit buffers (buffers whose major-modes derive from +`magit-mode') are displayed using `magit-display-buffer', +which in turn uses the function specified here." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type '(radio (function-item magit-display-buffer-traditional) + (function-item display-buffer) + (function :tag "Function"))) + +(unless (find-lisp-object-file-name 'magit-pre-display-buffer-hook 'defvar) + (add-hook 'magit-pre-display-buffer-hook 'magit-save-window-configuration)) +(defcustom magit-pre-display-buffer-hook '(magit-save-window-configuration) + "Hook run by `magit-display-buffer' before displaying the buffer." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type 'hook + :options '(magit-save-window-configuration)) + +(unless (find-lisp-object-file-name 'magit-post-display-buffer-hook 'defvar) + (add-hook 'magit-post-display-buffer-hook 'magit-maybe-set-dedicated)) +(defcustom magit-post-display-buffer-hook '(magit-maybe-set-dedicated) + "Hook run by `magit-display-buffer' after displaying the buffer." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type 'hook + :options '(magit-maybe-set-dedicated)) + +(defcustom magit-generate-buffer-name-function + 'magit-generate-buffer-name-default-function + "The function used to generate the name for a Magit buffer." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type '(radio (function-item magit-generate-buffer-name-default-function) + (function :tag "Function"))) + +(defcustom magit-buffer-name-format "*%M%v: %t" + "The format string used to name Magit buffers. + +The following %-sequences are supported: + +`%m' The name of the major-mode, but with the `-mode' suffix + removed. + +`%M' Like \"%m\" but abbreviate `magit-status-mode' as `magit'. + +`%v' The value the buffer is locked to, in parentheses, or an empty + string if the buffer is not locked to a value. + +`%V' Like \"%v\", but the string is prefixed with a space, unless it + is an empty string. + +`%t' The top-level directory of the working tree of the + repository, or if `magit-uniquify-buffer-names' is non-nil + an abbreviation of that. + +The value should always contain either \"%m\" or \"%M\" as well as +\"%t\". If `magit-uniquify-buffer-names' is non-nil, then the +value must end with \"%t\". + +This is used by `magit-generate-buffer-name-default-function'. +If another `magit-generate-buffer-name-function' is used, then +it may not respect this option, or on the contrary it may +support additional %-sequences." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type 'string) + +(defcustom magit-uniquify-buffer-names t + "Whether to uniquify the names of Magit buffers." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type 'boolean) + +(defcustom magit-bury-buffer-function 'magit-restore-window-configuration + "The function used to bury or kill the current Magit buffer." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type '(radio (function-item quit-window) + (function-item magit-mode-quit-window) + (function-item magit-restore-window-configuration) + (function :tag "Function"))) + +(defcustom magit-region-highlight-hook + '(magit-section-update-region magit-diff-update-hunk-region) + "Functions used to highlight the region. +Each function is run with the current section as only argument +until one of them returns non-nil. When multiple sections are +selected, then this hook does not run and the region is not +displayed. Otherwise fall back to regular region highlighting." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'hook + :options '(magit-section-update-region magit-diff-update-hunk-region)) + +(defcustom magit-refresh-verbose nil + "Whether to revert Magit buffers verbosely." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'boolean) + +(defcustom magit-refresh-buffer-hook nil + "Normal hook for `magit-refresh-buffer' to run after refreshing." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'hook) + +(defcustom magit-refresh-status-buffer t + "Whether the status buffer is refreshed after running git. + +When this is non-nil, then the status buffer is automatically +refreshed after running git for side-effects, in addition to the +current Magit buffer, which is always refreshed automatically. + +Only set this to nil after exhausting all other options to +improve performance." + :package-version '(magit . "2.4.0") + :group 'magit-status + :type 'boolean) + +(defcustom magit-save-repository-buffers t + "Whether to save file-visiting buffers when appropriate. + +If this is non-nil then all modified file-visiting buffers +belonging to the current repository may be saved before running +commands, before creating new Magit buffers, and before +explicitly refreshing such buffers. If this is `dontask' then +this is done without user intervention, if it is t then the user +has to confirm each save. `dontask' is the recommended setting." + :group 'magit + :type '(choice (const :tag "Never" nil) + (const :tag "Ask" t) + (const :tag "Save without asking" dontask))) + +(defcustom magit-keep-region-overlay nil + "Whether to keep the region overlay when there is a valid selection. + +By default Magit removes the regular region overlay if, and only +if, that region constitutes a valid selection as understood by +Magit commands. Otherwise it does not remove that overlay, and +the region looks like it would in other buffers. + +There are two types of such valid selections: hunk-internal +regions and regions that select two or more sibling sections. +In such cases Magit removes the region overlay and instead +highlights a slightly larger range. All text (for hunk-internal +regions) or the headings of all sections (for sibling selections) +that are inside that range (not just inside the region) are acted +on by commands such as the staging command. This buffer range +begins at the beginning of the line on which the region begins +and ends at the end of the line on which the region ends. + +Because Magit acts on this larger range and not the region, it is +actually quite important to visualize that larger range. If we +don't do that, then one might think that these commands act on +the region instead. If you want to *also* visualize the region, +then set this option to t. But please note that when the region +does *not* constitute a valid selection, then the region is +*always* visualized as usual, and that it is usually under such +circumstances that you want to use a non-magit command to act on +the region. + +Besides keeping the region overlay, setting this option to t also +causes all face properties, except for `:foreground', to be +ignored for the faces used to highlight headings of selected +sections. This avoids the worst conflicts that result from +displaying the region and the selection overlays at the same +time. We are not interested in dealing with other conflicts. +In fact we *already* provide a way to avoid all of these +conflicts: *not* changing the value of this option. + +It should be clear by now that we consider it a mistake to set +this to display the region when the Magit selection is also +visualized, but since it has been requested a few times and +because it doesn't cost much to offer this option we do so. +However that might change. If the existence of this option +starts complicating other things, then it will be removed." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type 'boolean) + +;;; Magit Mode + +(defvar magit-mode-map + (let ((map (make-keymap))) + (suppress-keymap map t) + (define-key map "\t" 'magit-section-toggle) + (define-key map [C-tab] 'magit-section-cycle) + (define-key map [M-tab] 'magit-section-cycle-diffs) + (define-key map [s-tab] 'magit-section-cycle-global) + (define-key map [backtab] 'magit-section-cycle-global) + (define-key map "^" 'magit-section-up) + (define-key map "n" 'magit-section-forward) + (define-key map "p" 'magit-section-backward) + (define-key map "\M-n" 'magit-section-forward-sibling) + (define-key map "\M-p" 'magit-section-backward-sibling) + (define-key map "+" 'magit-diff-more-context) + (define-key map "-" 'magit-diff-less-context) + (define-key map "0" 'magit-diff-default-context) + (define-key map "1" 'magit-section-show-level-1) + (define-key map "2" 'magit-section-show-level-2) + (define-key map "3" 'magit-section-show-level-3) + (define-key map "4" 'magit-section-show-level-4) + (define-key map "\M-1" 'magit-section-show-level-1-all) + (define-key map "\M-2" 'magit-section-show-level-2-all) + (define-key map "\M-3" 'magit-section-show-level-3-all) + (define-key map "\M-4" 'magit-section-show-level-4-all) + (define-key map "g" 'magit-refresh) + (define-key map "G" 'magit-refresh-all) + (define-key map "q" 'magit-mode-bury-buffer) + (define-key map "$" 'magit-process-buffer) + (define-key map "a" 'magit-cherry-apply) + (define-key map "A" 'magit-cherry-pick-popup) + (define-key map "b" 'magit-branch-popup) + (define-key map "B" 'magit-bisect-popup) + (define-key map "c" 'magit-commit-popup) + (define-key map "d" 'magit-diff-popup) + (define-key map "D" 'magit-diff-refresh-popup) + (define-key map "h" 'magit-dispatch-popup) + (define-key map "?" 'magit-dispatch-popup) + (define-key map "\C-c\C-c" 'magit-dispatch-popup) + (define-key map "\C-c\C-e" 'magit-dispatch-popup) + (define-key map "e" 'magit-ediff-dwim) + (define-key map "E" 'magit-ediff-popup) + (define-key map "f" 'magit-fetch-popup) + (define-key map "F" 'magit-pull-popup) + (define-key map "i" 'magit-gitignore) + (define-key map "I" 'magit-gitignore-locally) + (define-key map "k" 'magit-delete-thing) + (define-key map "K" 'magit-file-untrack) + (define-key map "l" 'magit-log-popup) + (define-key map "L" 'magit-log-refresh-popup) + (define-key map "m" 'magit-merge-popup) + (define-key map "M" 'magit-remote-popup) + (define-key map "o" 'magit-submodule-popup) + (define-key map "P" 'magit-push-popup) + (define-key map "r" 'magit-rebase-popup) + (define-key map "R" 'magit-file-rename) + (define-key map "t" 'magit-tag-popup) + (define-key map "T" 'magit-notes-popup) + (define-key map "\r" 'magit-visit-thing) + (define-key map [C-return] 'magit-visit-thing) + (define-key map [M-return] 'magit-dired-jump) + (define-key map "\s" 'magit-diff-show-or-scroll-up) + (define-key map "\d" 'magit-diff-show-or-scroll-down) + (define-key map "s" 'magit-stage-file) + (define-key map "S" 'magit-stage-modified) + (define-key map "u" 'magit-unstage-file) + (define-key map "U" 'magit-unstage-all) + (define-key map "v" 'magit-revert-no-commit) + (define-key map "V" 'magit-revert-popup) + (define-key map "w" 'magit-am-popup) + (define-key map "W" 'magit-patch-popup) + (define-key map "x" 'magit-reset) + (define-key map "y" 'magit-show-refs-popup) + (define-key map "Y" 'magit-cherry) + (define-key map "z" 'magit-stash-popup) + (define-key map "Z" 'magit-stash-popup) + (define-key map ":" 'magit-git-command) + (define-key map "!" 'magit-run-popup) + (define-key map "\C-xa" 'magit-add-change-log-entry) + (define-key map "\C-x4a" 'magit-add-change-log-entry-other-window) + (define-key map "\C-w" 'magit-copy-section-value) + (define-key map "\M-w" 'magit-copy-buffer-revision) + (define-key map [remap evil-previous-line] 'evil-previous-visual-line) + (define-key map [remap evil-next-line] 'evil-next-visual-line) + map) + "Parent keymap for all keymaps of modes derived from `magit-mode'.") + +(defun magit-delete-thing () + "This is a placeholder command. +Where applicable, section-specific keymaps bind another command +which deletes the thing at point." + (interactive) + (user-error "There is no thing at point that could be deleted")) + +(defun magit-visit-thing () + "This is a placeholder command. +Where applicable, section-specific keymaps bind another command +which visits the thing at point." + (interactive) + (user-error "There is no thing at point that could be visited")) + +(easy-menu-define magit-mode-menu magit-mode-map + "Magit menu" + '("Magit" + ["Refresh" magit-refresh t] + ["Refresh all" magit-refresh-all t] + "---" + ["Stage" magit-stage t] + ["Stage modified" magit-stage-modified t] + ["Unstage" magit-unstage t] + ["Reset index" magit-reset-index t] + ["Commit" magit-commit-popup t] + ["Add log entry" magit-commit-add-log t] + ["Tag" magit-tag t] + "---" + ["Diff working tree" magit-diff-working-tree t] + ["Diff" magit-diff t] + ("Log" + ["Log" magit-log t] + ["Reflog" magit-reflog t] + ["Extended..." magit-log-popup t]) + "---" + ["Cherry pick" magit-cherry-pick t] + ["Revert commit" magit-revert-popup t] + "---" + ["Ignore" magit-gitignore t] + ["Ignore locally" magit-gitignore-locally t] + ["Discard" magit-discard t] + ["Reset head" magit-reset-head t] + ["Stash" magit-stash t] + ["Snapshot" magit-snapshot t] + "---" + ["Branch..." magit-checkout t] + ["Merge" magit-merge t] + ["Ediff resolve" magit-ediff-resolve t] + ["Rebase..." magit-rebase-popup t] + "---" + ["Push" magit-push t] + ["Pull" magit-pull t] + ["Remote update" magit-fetch-all t] + ("Submodule" + ["Submodule update" magit-submodule-update t] + ["Submodule update and init" magit-submodule-setup t] + ["Submodule init" magit-submodule-init t] + ["Submodule sync" magit-submodule-sync t]) + "---" + ("Extensions") + "---" + ["Display Git output" magit-process-buffer t] + ["Quit Magit" magit-mode-bury-buffer t])) + +(defun magit-load-config-extensions () + "Load Magit extensions that are defined at the Git config layer." + (dolist (ext (magit-get-all "magit.extension")) + (let ((sym (intern (format "magit-%s-mode" ext)))) + (when (fboundp sym) + (funcall sym 1))))) + +(define-derived-mode magit-mode special-mode "Magit" + "Parent major mode from which Magit major modes inherit. + +Magit is documented in info node `(magit)'." + :group 'magit-modes + (buffer-disable-undo) + (setq truncate-lines t) + (setq buffer-read-only t) + (setq-local line-move-visual t) ; see #1771 + (setq show-trailing-whitespace nil) + (setq list-buffers-directory default-directory) + (hack-dir-local-variables-non-file-buffer) + (make-local-variable 'text-property-default-nonsticky) + (push (cons 'keymap t) text-property-default-nonsticky) + (add-hook 'post-command-hook #'magit-section-update-highlight t t) + (setq-local redisplay-highlight-region-function 'magit-highlight-region) + (setq-local redisplay-unhighlight-region-function 'magit-unhighlight-region) + (when (fboundp 'linum-mode) + (linum-mode -1))) + +(defvar-local magit-region-overlays nil) + +(defun magit-highlight-region (start end window rol) + (mapc #'delete-overlay magit-region-overlays) + (if (and (run-hook-with-args-until-success 'magit-region-highlight-hook + (magit-current-section)) + (not magit-keep-region-overlay)) + (funcall (default-value 'redisplay-unhighlight-region-function) rol) + (funcall (default-value 'redisplay-highlight-region-function) + start end window rol))) + +(defun magit-unhighlight-region (rol) + (setq magit-section-highlighted-section nil) + (mapc #'delete-overlay magit-region-overlays) + (funcall (default-value 'redisplay-unhighlight-region-function) rol)) + +(defvar-local magit-refresh-args nil + "The arguments used to refresh the current buffer.") +(put 'magit-refresh-args 'permanent-local t) + +(defvar-local magit-previous-section nil) +(put 'magit-previous-section 'permanent-local t) + +(defun magit-mode-setup (mode &rest args) + "Setup up a MODE buffer using ARGS to generate its content." + (let ((buffer (magit-mode-get-buffer mode t)) + (section (magit-current-section))) + (with-current-buffer buffer + (setq magit-previous-section section) + (setq magit-refresh-args args) + (funcall mode)) + (magit-display-buffer buffer) + (with-current-buffer buffer + (run-hooks 'magit-mode-setup-hook) + (magit-refresh-buffer)))) + +(defvar magit-display-buffer-noselect nil + "If non-nil, then `magit-display-buffer' doesn't call `select-window'.") + +(defun magit-display-buffer (buffer) + "Display BUFFER in some window and maybe select it. + +Display the buffer using `magit-display-buffer-function' and +then, unless `magit-display-buffer-noselect' is non-nil, select +the window which was used to display the buffer. + +Also run the hooks `magit-pre-display-buffer-hook' +and `magit-post-display-buffer-hook'." + (with-current-buffer buffer + (run-hooks 'magit-pre-display-buffer-hook)) + (let ((window (funcall magit-display-buffer-function buffer))) + (unless magit-display-buffer-noselect + (select-window window))) + (with-current-buffer buffer + (run-hooks 'magit-post-display-buffer-hook))) + +(defun magit-display-buffer-traditional (buffer) + "Display BUFFER the way this has traditionally been done." + (display-buffer + buffer (if (and (derived-mode-p 'magit-mode) + (not (memq (with-current-buffer buffer major-mode) + '(magit-process-mode + magit-revision-mode + magit-diff-mode + magit-stash-mode + magit-status-mode)))) + '(display-buffer-same-window) + nil))) ; display in another window + +(defun magit-maybe-set-dedicated () + "Mark the selected window as dedicated if appropriate. + +If a new window was created to display the buffer, then remember +that fact. That information is used by `magit-mode-quit-window', +to determine whether the window should be deleted when its last +Magit buffer is buried." + (let ((window (get-buffer-window (current-buffer)))) + (when (and (window-live-p window) + (not (window-prev-buffers window))) + (set-window-parameter window 'magit-dedicated t)))) + +(defvar-local magit--default-directory nil + "Value of `default-directory' when buffer is generated. +This exists to prevent a let-bound `default-directory' from +tricking `magit-mode-get-buffer' or `magit-mode-get-buffers' into +thinking a buffer belongs to a repo that it doesn't.") +(put 'magit--default-directory 'permanent-local t) + +(defun magit-mode-get-buffers () + (let ((topdir (magit-toplevel))) + (--filter (with-current-buffer it + (and (derived-mode-p 'magit-mode) + (equal magit--default-directory topdir))) + (buffer-list)))) + +(defvar-local magit-buffer-locked-p nil) +(put 'magit-buffer-locked-p 'permanent-local t) + +(defun magit-mode-get-buffer (mode &optional create frame) + (-if-let (topdir (magit-toplevel)) + (or (--first (with-current-buffer it + (and (eq major-mode mode) + (equal magit--default-directory topdir) + (not magit-buffer-locked-p))) + (if frame + (-map #'window-buffer + (window-list (unless (eq frame t) frame))) + (buffer-list))) + (and create + (let ((default-directory topdir)) + (magit-generate-new-buffer mode)))) + (user-error "Not inside a Git repository"))) + +(defun magit-generate-new-buffer (mode) + (let* ((name (funcall magit-generate-buffer-name-function mode)) + (buffer (generate-new-buffer name))) + (with-current-buffer buffer + (setq magit--default-directory default-directory)) + (when magit-uniquify-buffer-names + (add-to-list 'uniquify-list-buffers-directory-modes mode) + (with-current-buffer buffer + (setq list-buffers-directory default-directory)) + (let ((uniquify-buffer-name-style + (if (memq uniquify-buffer-name-style '(nil forward)) + 'post-forward-angle-brackets + uniquify-buffer-name-style))) + (uniquify-rationalize-file-buffer-names + name (file-name-directory (directory-file-name default-directory)) + buffer))) + buffer)) + +(defun magit-generate-buffer-name-default-function (mode &optional value) + (let ((m (substring (symbol-name mode) 0 -5)) + (v (and value (format "%s" (if (listp value) value (list value)))))) + (format-spec + magit-buffer-name-format + `((?m . ,m) + (?M . ,(if (eq mode 'magit-status-mode) "magit" m)) + (?v . ,(or v "")) + (?V . ,(if v (concat " " v) "")) + (?t . ,(if magit-uniquify-buffer-names + (file-name-nondirectory + (directory-file-name default-directory)) + default-directory)))))) + +(defun magit-toggle-buffer-lock () + "Lock the current buffer to its value or unlock it. + +Locking a buffer to its value, prevents it from being reused to +display another value. The name of a locked buffer contains its +value, which allows telling it apart from other locked buffers +and the unlocked buffer. + +Not all Magit buffers can be locked to their values, for example +it wouldn't make sense to lock a status buffer. + +There can only be a single unlocked buffer using a certain +major-mode per repository. So when a buffer is being unlocked +and another unlocked buffer already exists for that mode and +repository, then the former buffer is instead deleted and the +latter is displayed in its place." + (interactive) + (if magit-buffer-locked-p + (-if-let (unlocked (magit-mode-get-buffer major-mode)) + (let ((locked (current-buffer))) + (set-buffer unlocked) + (kill-buffer locked)) + (setq magit-buffer-locked-p nil) + (rename-buffer (funcall magit-generate-buffer-name-function + major-mode))) + (setq magit-buffer-locked-p + (cond ((memq major-mode '(magit-cherry-mode + magit-log-mode + magit-reflog-mode + magit-refs-mode + magit-revision-mode + magit-stash-mode + magit-stashes-mode)) + (car magit-refresh-args)) + ((eq major-mode 'magit-diff-mode) + (let ((rev (nth 0 magit-refresh-args)) + (args (nth 1 magit-refresh-args))) + (cond + ((member "--no-index" args) + (nth 3 magit-refresh-args)) + (rev (if args (cons rev args) rev)) + (t (if (member "--cached" args) "staged" "unstaged"))))))) + (if magit-buffer-locked-p + (rename-buffer (funcall magit-generate-buffer-name-function + major-mode magit-buffer-locked-p)) + (user-error "Buffer has no value it could be locked to")))) + +(defun magit-mode-bury-buffer (&optional kill-buffer) + "Bury the current buffer. +With a prefix argument, kill the buffer instead. +This is done using `magit-bury-buffer-function'." + (interactive "P") + (funcall magit-bury-buffer-function kill-buffer)) + +(defun magit-mode-quit-window (kill-buffer) + "Quit the selected window and bury its buffer. + +This behaves similar to `quit-window', but when the window +was originally created to display a Magit buffer and the +current buffer is the last remaining Magit buffer that was +ever displayed in the selected window, then delete that +window." + (if (or (one-window-p) + (--first (let ((buffer (car it))) + (and (not (eq buffer (current-buffer))) + (buffer-live-p buffer) + (or (not (window-parameter nil 'magit-dedicated)) + (with-current-buffer buffer + (derived-mode-p 'magit-mode + 'magit-process-mode))))) + (window-prev-buffers))) + (quit-window kill-buffer) + (let ((window (selected-window))) + (quit-window kill-buffer) + (when (window-live-p window) + (delete-window window))))) + +;;; Refresh Magit Buffers + +(defvar inhibit-magit-refresh nil) + +(defun magit-refresh () + "Refresh some buffers belonging to the current repository. + +Refresh the current buffer if its major mode derives from +`magit-mode', and refresh the corresponding status buffer. + +Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'." + (interactive) + (unless inhibit-magit-refresh + (magit-run-hook-with-benchmark 'magit-pre-refresh-hook) + (when (derived-mode-p 'magit-mode) + (magit-refresh-buffer)) + (--when-let (and magit-refresh-status-buffer + (not (derived-mode-p 'magit-status-mode)) + (magit-mode-get-buffer 'magit-status-mode)) + (with-current-buffer it + (magit-refresh-buffer))) + (magit-auto-revert-buffers) + (magit-run-hook-with-benchmark 'magit-post-refresh-hook))) + +(defun magit-refresh-all () + "Refresh all buffers belonging to the current repository. + +Refresh all Magit buffers belonging to the current repository, +and revert buffers that visit files located inside the current +repository. + +Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'." + (interactive) + (magit-run-hook-with-benchmark 'magit-pre-refresh-hook) + (dolist (buffer (magit-mode-get-buffers)) + (with-current-buffer buffer (magit-refresh-buffer))) + (magit-auto-revert-buffers) + (magit-run-hook-with-benchmark 'magit-post-refresh-hook)) + +(defvar-local magit-refresh-start-time nil) + +(defun magit-refresh-buffer () + "Refresh the current Magit buffer." + (setq magit-refresh-start-time (current-time)) + (let ((refresh (intern (format "%s-refresh-buffer" + (substring (symbol-name major-mode) 0 -5))))) + (when (functionp refresh) + (when magit-refresh-verbose + (message "Refreshing buffer `%s'..." (buffer-name))) + (let* ((buffer (current-buffer)) + (windows + (--mapcat (with-selected-window it + (with-current-buffer buffer + (-when-let (section (magit-current-section)) + (list + (nconc (list it section) + (magit-refresh-get-relative-position)))))) + (or (get-buffer-window-list buffer nil t) + (list (selected-window)))))) + (deactivate-mark) + (setq magit-section-highlight-overlays nil + magit-section-highlighted-section nil + magit-section-highlighted-sections nil + magit-section-unhighlight-sections nil) + (let ((inhibit-read-only t)) + (erase-buffer) + (save-excursion + (apply refresh magit-refresh-args))) + (dolist (window windows) + (with-selected-window (car window) + (with-current-buffer buffer + (apply #'magit-section-goto-successor (cdr window))))) + (run-hooks 'magit-refresh-buffer-hook) + (magit-section-update-highlight) + (set-buffer-modified-p nil)) + (when magit-refresh-verbose + (message "Refreshing buffer `%s'...done (%.3fs)" (buffer-name) + (float-time (time-subtract (current-time) + magit-refresh-start-time))))))) + +(defun magit-refresh-get-relative-position () + (-when-let (section (magit-current-section)) + (let ((start (magit-section-start section))) + (list (count-lines start (point)) + (- (point) (line-beginning-position)) + (and (eq (magit-section-type section) 'hunk) + (region-active-p) + (progn (goto-char (line-beginning-position)) + (when (looking-at "^[-+]") (forward-line)) + (while (looking-at "^[ @]") (forward-line)) + (let ((beg (point))) + (cond ((looking-at "^[-+]") + (forward-line) + (while (looking-at "^[-+]") (forward-line)) + (while (looking-at "^ ") (forward-line)) + (forward-line -1) + (regexp-quote (buffer-substring-no-properties + beg (line-end-position)))) + (t t))))))))) + +;;; Save File-Visiting Buffers + +(defvar disable-magit-save-buffers nil) + +(defun magit-pre-command-hook () + (setq disable-magit-save-buffers nil)) +(add-hook 'pre-command-hook #'magit-pre-command-hook) + +(defvar magit-after-save-refresh-buffers nil) + +(defun magit-after-save-refresh-buffers () + (dolist (buffer magit-after-save-refresh-buffers) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (magit-refresh-buffer)))) + (setq magit-after-save-refresh-buffers nil) + (remove-hook 'post-command-hook 'magit-after-save-refresh-buffers)) + +(defun magit-after-save-refresh-status () + "Refresh the status buffer of the current repository. + +This function is intended to be added to `after-save-hook'. + +If the status buffer does not exist or the file being visited in +the current buffer isn't inside a repository, then do nothing. + +Note that refreshing a Magit buffer is done by re-creating its +contents from scratch, which can be slow in large repositories. +If you are not satisfied with Magit's performance, then you +should obviously not add this function to that hook." + (unless disable-magit-save-buffers + (--when-let (ignore-errors (magit-mode-get-buffer 'magit-status-mode)) + (add-to-list 'magit-after-save-refresh-buffers it) + (add-hook 'post-command-hook 'magit-after-save-refresh-buffers)))) + +(defun magit-maybe-save-repository-buffers () + "Maybe save file-visiting buffers belonging to the current repository. +Do so if `magit-save-repository-buffers' is non-nil. You should +not remove this from any hooks, instead set that variable to nil +if you so desire." + (when (and magit-save-repository-buffers + (not disable-magit-save-buffers)) + (setq disable-magit-save-buffers t) + (let ((msg (current-message))) + (magit-save-repository-buffers + (eq magit-save-repository-buffers 'dontask)) + (when (and msg (not (equal msg (current-message)))) + (message "%s" msg))))) + +(add-hook 'magit-pre-refresh-hook #'magit-maybe-save-repository-buffers) +(add-hook 'magit-pre-call-git-hook #'magit-maybe-save-repository-buffers) +(add-hook 'magit-pre-start-git-hook #'magit-maybe-save-repository-buffers) + +(defun magit-save-repository-buffers (&optional arg) + "Save file-visiting buffers belonging to the current repository. +After any buffer where `buffer-save-without-query' is non-nil +is saved without asking, the user is asked about each modified +buffer which visits a file in the current repository. Optional +argument (the prefix) non-nil means save all with no questions." + (interactive "P") + (-when-let (topdir (magit-rev-parse-safe "--show-toplevel")) + (save-some-buffers + arg (-partial (lambda (topdir) + (and buffer-file-name + ;; Avoid needlessly connecting to unrelated remotes. + (string-prefix-p topdir buffer-file-name) + (equal (magit-rev-parse-safe "--show-toplevel") + topdir))) + topdir)))) + +;;; Restore Window Configuration + +(defvar magit-inhibit-save-previous-winconf nil) + +(defvar-local magit-previous-window-configuration nil) +(put 'magit-previous-window-configuration 'permanent-local t) + +(defun magit-save-window-configuration () + "Save the current window configuration. + +Later, when the buffer is buried, it may be restored by +`magit-restore-window-configuration'." + (if magit-inhibit-save-previous-winconf + (when (eq magit-inhibit-save-previous-winconf 'unset) + (setq magit-previous-window-configuration nil)) + (unless (get-buffer-window (current-buffer) (selected-frame)) + (setq magit-previous-window-configuration + (current-window-configuration))))) + +(defun magit-restore-window-configuration (&optional kill-buffer) + "Bury or kill the current buffer and restore previous window configuration." + (let ((winconf magit-previous-window-configuration) + (buffer (current-buffer)) + (frame (selected-frame))) + (quit-window kill-buffer (selected-window)) + (when (and winconf (equal frame (window-configuration-frame winconf))) + (set-window-configuration winconf) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (setq magit-previous-window-configuration nil)))))) + +;;; Buffer History + +(defun magit-go-backward () + "Move backward in current buffer's history." + (interactive) + (if help-xref-stack + (help-xref-go-back (current-buffer)) + (user-error "No previous entry in buffer's history"))) + +(defun magit-go-forward () + "Move forward in current buffer's history." + (interactive) + (if help-xref-forward-stack + (help-xref-go-forward (current-buffer)) + (user-error "No next entry in buffer's history"))) + +(defun magit-insert-xref-buttons (&optional _) + "Insert xref buttons." + (when (or help-xref-stack help-xref-forward-stack) + (when help-xref-stack + (magit-xref-insert-button help-back-label 'magit-xref-backward)) + (when help-xref-forward-stack + (when help-xref-stack + (insert " ")) + (magit-xref-insert-button help-forward-label 'magit-xref-forward)))) + +(defun magit-xref-insert-button (label type) + (magit-insert-section (button label) + (insert-text-button label 'type type + 'help-args (list (current-buffer))))) + +(define-button-type 'magit-xref-backward + :supertype 'help-back + 'mouse-face 'magit-section-highlight + 'help-echo (purecopy "mouse-2, RET: go back to previous history entry")) + +(define-button-type 'magit-xref-forward + :supertype 'help-forward + 'mouse-face 'magit-section-highlight + 'help-echo (purecopy "mouse-2, RET: go back to next history entry")) + +(defun magit-xref-setup () + "Insert backward/forward buttons if the major-mode supports it. +Currently `magit-log-mode', `magit-reflog-mode', +`magit-diff-mode', and `magit-revision-mode' support it" + (when (memq major-mode '(magit-log-mode + magit-reflog-mode + magit-diff-mode + magit-revision-mode)) + (when help-xref-stack-item + (push (cons (point) help-xref-stack-item) help-xref-stack) + (setq help-xref-forward-stack nil)) + (when (called-interactively-p 'interactive) + (--when-let (nthcdr 10 help-xref-stack) + (setcdr it nil))) + (setq help-xref-stack-item + `(magit-xref-restore ,default-directory ,@magit-refresh-args)))) + +(defun magit-xref-restore (&rest args) + (magit-xref-setup) + (setq default-directory (car args)) + (setq magit-refresh-args (cdr args)) + (magit-refresh-buffer)) + +;;; Utilities + +(defun magit-run-hook-with-benchmark (hook) + (when hook + (if magit-refresh-verbose + (let ((start (current-time))) + (message "Running %s..." hook) + (run-hooks hook) + (message "Running %s...done (%.3fs)" hook + (float-time (time-subtract (current-time) start)))) + (run-hooks hook)))) + +;;; magit-mode.el ends soon +(provide 'magit-mode) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-mode.el ends here diff --git a/elpa/magit-20160223.828/magit-pkg.el b/elpa/magit-20160223.828/magit-pkg.el new file mode 100644 index 0000000..0b98849 --- /dev/null +++ b/elpa/magit-20160223.828/magit-pkg.el @@ -0,0 +1,12 @@ +(define-package "magit" "20160223.828" "A Git porcelain inside Emacs" + '((emacs "24.4") + (async "20150909.2257") + (dash "20151021.113") + (with-editor "20160128.1201") + (git-commit "20160119.1409") + (magit-popup "20160119.1409")) + :url "https://github.com/magit/magit" :keywords + '("git" "tools" "vc")) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/magit-20160223.828/magit-process.el b/elpa/magit-20160223.828/magit-process.el new file mode 100644 index 0000000..918c94c --- /dev/null +++ b/elpa/magit-20160223.828/magit-process.el @@ -0,0 +1,836 @@ +;;; magit-process.el --- process functionality -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library implements the tools used to run Git for side-effects. + +;; Note that the functions used to run Git and then consume its +;; output, are defined in `magit-git.el'. There's a bit of overlap +;; though. + +;;; Code: + +(require 'cl-lib) +(require 'dash) + +(require 'with-editor) +(require 'magit-utils) +(require 'magit-section) +(require 'magit-git) +(require 'magit-mode) + +(eval-when-compile (require 'dired)) +(declare-function dired-uncache 'dired) + +;;; Options + +(defcustom magit-process-connection-type (not (eq system-type 'cygwin)) + "Connection type used for the Git process. + +If nil, use pipes: this is usually more efficient, and works on Cygwin. +If t, use ptys: this enables Magit to prompt for passphrases when needed." + :group 'magit-process + :type '(choice (const :tag "pipe" nil) + (const :tag "pty" t))) + +(defcustom magit-need-cygwin-noglob + (equal "x0\n" (with-temp-buffer + (let ((process-environment + (append magit-git-environment process-environment))) + (process-file magit-git-executable + nil (current-buffer) nil + "-c" "alias.echo=!echo" "echo" "x{0}")) + (buffer-string))) + "Whether to use a workaround for Cygwin's globbing behavior. + +If non-nil, add environment variables to `process-environment' to +prevent the git.exe distributed by Cygwin and MSYS2 from +attempting to perform glob expansion when called from a native +Windows build of Emacs. See #2246." + :package-version '(magit . "2.3.0") + :group 'magit-process + :type '(choice (const :tag "Yes" t) + (const :tag "No" nil))) + +(defcustom magit-process-popup-time -1 + "Popup the process buffer if a command takes longer than this many seconds." + :group 'magit-process + :type '(choice (const :tag "Never" -1) + (const :tag "Immediately" 0) + (integer :tag "After this many seconds"))) + +(defcustom magit-process-log-max 32 + "Maximum number of sections to keep in a process log buffer. +When adding a new section would go beyond the limit set here, +then the older half of the sections are remove. Sections that +belong to processes that are still running are never removed. +When this is nil, no sections are ever removed." + :package-version '(magit . "2.1.0") + :group 'magit-process + :type '(choice (const :tag "Never remove old sections" nil) integer)) + +(defcustom magit-credential-cache-daemon-socket + (--some (-let [(prog . args) (split-string it)] + (if (string-match-p + "\\`\\(?:\\(?:/.*/\\)?git-credential-\\)?cache\\'" prog) + (or (cl-loop for (opt val) on args + if (string= opt "--socket") + return val) + (expand-file-name "~/.git-credential-cache/socket")))) + ;; Note: `magit-process-file' is not yet defined when + ;; evaluating this form, so we use `process-lines'. + (ignore-errors + (let ((process-environment + (append magit-git-environment process-environment))) + (process-lines magit-git-executable + "config" "--get-all" "credential.helper")))) + "If non-nil, start a credential cache daemon using this socket. + +When using Git's cache credential helper in the normal way, Emacs +sends a SIGHUP to the credential daemon after the git subprocess +has exited, causing the daemon to also quit. This can be avoided +by starting the `git-credential-cache--daemon' process directly +from Emacs. + +The function `magit-maybe-start-credential-cache-daemon' takes +care of starting the daemon if necessary, using the value of this +option as the socket. If this option is nil, then it does not +start any daemon. Likewise if another daemon is already running, +then it starts no new daemon. This function has to be a member +of the hook variable `magit-credential-hook' for this to work. +If an error occurs while starting the daemon, most likely because +the necessary executable is missing, then the function removes +itself from the hook, to avoid further futile attempts." + :package-version '(magit . "2.3.0") + :group 'magit-process + :type '(choice (file :tag "Socket") + (const :tag "Don't start a cache daemon" nil))) + +(defcustom magit-process-yes-or-no-prompt-regexp + " [\[(]\\([Yy]\\(?:es\\)?\\)[/|]\\([Nn]o?\\)[\])] ?[?:] ?$" + "Regexp matching Yes-or-No prompts of Git and its subprocesses." + :package-version '(magit . "2.1.0") + :group 'magit-process + :type 'regexp) + +(defcustom magit-process-password-prompt-regexps + '("^\\(Enter \\)?[Pp]assphrase\\( for \\(RSA \\)?key '.*'\\)?: ?$" + ;; match-group 99 is used to identify a host + "^\\(Enter \\)?[Pp]assword\\( for '\\(?99:.*\\)'\\)?: ?$" + "^.*'s password: ?$" + "^Yubikey for .*: ?$") + "List of regexps matching password prompts of Git and its subprocesses. +Also see `magit-process-find-password-functions'." + :package-version '(magit . "2.1.0") + :group 'magit-process + :type '(repeat (regexp))) + +(defcustom magit-process-find-password-functions nil + "List of functions to try in sequence to get a password. + +These functions may be called when git asks for a password, which +is detected using `magit-process-password-prompt-regexps'. They +are called if and only if matching the prompt resulted in the +value of the 99th submatch to be non-nil. Therefore users can +control for which prompts these functions should be called by +putting the host name in the 99th submatch, or not. + +If the functions are called, then they are called in the order +given, with the host name as only argument, until one of them +returns non-nil. If they are not called or none of them returns +non-nil, then the password is read from the user instead." + :package-version '(magit . "2.3.0") + :group 'magit-process + :type 'hook + :options '(magit-process-password-auth-source)) + +(defcustom magit-process-username-prompt-regexps + '("^Username for '.*': ?$") + "List of regexps matching username prompts of Git and its subprocesses." + :package-version '(magit . "2.1.0") + :group 'magit-process + :type '(repeat (regexp))) + +(defface magit-process-ok + '((t :inherit magit-section-heading :foreground "green")) + "Face for zero exit-status." + :group 'magit-faces) + +(defface magit-process-ng + '((t :inherit magit-section-heading :foreground "red")) + "Face for non-zero exit-status." + :group 'magit-faces) + +;;; Process Mode + +(defvar magit-process-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + map) + "Keymap for `magit-process-mode'.") + +(define-derived-mode magit-process-mode magit-mode "Magit Process" + "Mode for looking at Git process output." + :group 'magit-process + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-process-buffer (&optional nodisplay) + "Display the current repository's process buffer. + +If that buffer doesn't exist yet, then create it. +Non-interactively return the buffer and unless +optional NODISPLAY is non-nil also display it." + (interactive) + (let ((topdir (magit-toplevel))) + (unless topdir + (magit--with-safe-default-directory nil + (setq topdir default-directory) + (let (prev) + (while (not (equal topdir prev)) + (setq prev topdir) + (setq topdir (file-name-directory (directory-file-name topdir))))))) + (let ((buffer (or (--first (with-current-buffer it + (and (eq major-mode 'magit-process-mode) + (equal default-directory topdir))) + (buffer-list)) + (let ((default-directory topdir)) + (magit-generate-new-buffer 'magit-process-mode))))) + (with-current-buffer buffer + (if magit-root-section + (when magit-process-log-max + (magit-process-truncate-log)) + (magit-process-mode) + (let ((inhibit-read-only t)) + (make-local-variable 'text-property-default-nonsticky) + (magit-insert-section (processbuf) + (insert "\n"))))) + (unless nodisplay + (magit-display-buffer buffer)) + buffer))) + +(defun magit-process-kill () + "Kill the process at point." + (interactive) + (magit-section-when process + (let ((process (magit-section-value it))) + (if (eq (process-status process) 'run) + (when (magit-confirm 'kill-process) + (kill-process process)) + (user-error "Process isn't running"))))) + +;;; Synchronous Processes + +(defvar magit-process-raise-error nil) + +(defun magit-git (&rest args) + "Call Git synchronously in a separate process, for side-effects. + +Option `magit-git-executable' specifies the Git executable. +The arguments ARGS specify arguments to Git, they are flattened +before use. + +Process output goes into a new section in the buffer returned by +`magit-process-buffer'. If Git exits with a non-zero status, +then raise an error." + (let ((magit-process-raise-error t)) + (magit-call-git args))) + +(defun magit-run-git (&rest args) + "Call Git synchronously in a separate process, and refresh. + +Option `magit-git-executable' specifies the Git executable and +option `magit-git-global-arguments' specifies constant arguments. +The arguments ARGS specify arguments to Git, they are flattened +before use. + +After Git returns, the current buffer (if it is a Magit buffer) +as well as the current repository's status buffer are refreshed. + +Process output goes into a new section in the buffer returned by +`magit-process-buffer'." + (magit-call-git args) + (magit-refresh)) + +(defvar magit-pre-call-git-hook nil) + +(defun magit-call-git (&rest args) + "Call Git synchronously in a separate process. + +Option `magit-git-executable' specifies the Git executable and +option `magit-git-global-arguments' specifies constant arguments. +The arguments ARGS specify arguments to Git, they are flattened +before use. + +Process output goes into a new section in the buffer returned by +`magit-process-buffer'." + (run-hooks 'magit-pre-call-git-hook) + (apply #'magit-call-process magit-git-executable + (magit-process-git-arguments args))) + +(defun magit-call-process (program &rest args) + "Call PROGRAM synchronously in a separate process. +Process output goes into a new section in the buffer returned by +`magit-process-buffer'." + (-let [(process-buf . section) (magit-process-setup program args)] + (magit-process-finish + (let ((inhibit-read-only t)) + (apply #'magit-process-file program nil process-buf nil args)) + process-buf (current-buffer) default-directory section))) + +(defun magit-process-file (&rest args) + "Process files synchronously in a separate process. +Identical to `process-file' but temporarily enable Cygwin's +\"noglob\" option during the call." + (let ((process-environment (append (magit-cygwin-env-vars) + process-environment))) + (apply #'process-file args))) + +(defun magit-cygwin-env-vars () + (append magit-git-environment + (when magit-need-cygwin-noglob + (mapcar (lambda (var) + (concat var "=" (--if-let (getenv var) + (concat it " noglob") + "noglob"))) + '("CYGWIN" "MSYS"))))) + +(defvar magit-this-process nil) + +(defun magit-run-git-with-input (&rest args) + "Call Git in a separate process. +ARGS is flattened and then used as arguments to Git. + +The current buffer's content is used as the process' standard +input. + +Option `magit-git-executable' specifies the Git executable and +option `magit-git-global-arguments' specifies constant arguments. +The remaining arguments ARGS specify arguments to Git, they are +flattened before use." + (declare (indent 1)) + (if (file-remote-p default-directory) + ;; We lack `process-file-region', so fall back to asynch + + ;; waiting in remote case. + (progn + (magit-start-git (current-buffer) args) + (while (and magit-this-process + (eq (process-status magit-this-process) 'run)) + (sleep-for 0.005))) + (run-hooks 'magit-pre-call-git-hook) + (-let* ((process-environment (append (magit-cygwin-env-vars) + process-environment)) + (flat-args (magit-process-git-arguments args)) + ((process-buf . section) + (magit-process-setup magit-git-executable flat-args)) + (inhibit-read-only t)) + (magit-process-finish + (apply #'call-process-region (point-min) (point-max) + magit-git-executable nil process-buf nil flat-args) + process-buf nil default-directory section)))) + +(defun magit-run-git-with-logfile (file &rest args) + "Call Git in a separate process and log its output to FILE. +This function might have a short halflive." + (apply #'magit-process-file magit-git-executable nil `(:file ,file) nil + (magit-process-git-arguments args)) + (magit-refresh)) + +;;; Asynchronous Processes + +(defun magit-run-git-async (&rest args) + "Start Git, prepare for refresh, and return the process object. +ARGS is flattened and then used as arguments to Git. + +Display the command line arguments in the echo area. + +After Git returns some buffers are refreshed: the buffer that was +current when this function was called (if it is a Magit buffer +and still alive), as well as the respective Magit status buffer. + +See `magit-start-process' for more information." + (message "Running %s %s" magit-git-executable + (let ((m (mapconcat #'identity (-flatten args) " "))) + (remove-list-of-text-properties 0 (length m) '(face) m) + m)) + (magit-start-git nil args)) + +(defun magit-run-git-with-editor (&rest args) + "Export GIT_EDITOR and start Git. +Also prepare for refresh and return the process object. +ARGS is flattened and then used as arguments to Git. + +Display the command line arguments in the echo area. + +After Git returns some buffers are refreshed: the buffer that was +current when this function was called (if it is a Magit buffer +and still alive), as well as the respective Magit status buffer. + +See `magit-start-process' and `with-editor' for more information." + (with-editor "GIT_EDITOR" + (let ((magit-process-popup-time -1)) + (magit-run-git-async args)))) + +(defun magit-run-git-sequencer (&rest args) + "Export GIT_EDITOR and start Git. +Also prepare for refresh and return the process object. +ARGS is flattened and then used as arguments to Git. + +Display the command line arguments in the echo area. + +After Git returns some buffers are refreshed: the buffer that was +current when this function was called (if it is a Magit buffer +and still alive), as well as the respective Magit status buffer. +If the sequence stops at a commit, make the section representing +that commit the current section by moving `point' there. + +See `magit-start-process' and `with-editor' for more information." + (with-editor "GIT_EDITOR" + (let ((magit-process-popup-time -1)) + (magit-run-git-async args))) + (set-process-sentinel magit-this-process #'magit-sequencer-process-sentinel) + magit-this-process) + +(defvar magit-pre-start-git-hook nil) + +(defun magit-start-git (input &rest args) + "Start Git, prepare for refresh, and return the process object. + +If INPUT is non-nil, it has to be a buffer or the name of an +existing buffer. The buffer content becomes the processes +standard input. + +Option `magit-git-executable' specifies the Git executable and +option `magit-git-global-arguments' specifies constant arguments. +The remaining arguments ARGS specify arguments to Git, they are +flattened before use. + +After Git returns some buffers are refreshed: the buffer that was +current when this function was called (if it is a Magit buffer +and still alive), as well as the respective Magit status buffer. + +See `magit-start-process' for more information." + (run-hooks 'magit-pre-start-git-hook) + (apply #'magit-start-process magit-git-executable input + (magit-process-git-arguments args))) + +(defun magit-start-process (program &optional input &rest args) + "Start PROGRAM, prepare for refresh, and return the process object. + +If optional argument INPUT is non-nil, it has to be a buffer or +the name of an existing buffer. The buffer content becomes the +processes standard input. + +The process is started using `start-file-process' and then setup +to use the sentinel `magit-process-sentinel' and the filter +`magit-process-filter'. Information required by these functions +is stored in the process object. When this function returns the +process has not started to run yet so it is possible to override +the sentinel and filter. + +After the process returns, `magit-process-sentinel' refreshes the +buffer that was current when `magit-start-process' was called (if +it is a Magit buffer and still alive), as well as the respective +Magit status buffer." + (-let* (((process-buf . section) + (magit-process-setup program args)) + (process + (let ((process-connection-type + ;; Don't use a pty, because it would set icrnl + ;; which would modify the input (issue #20). + (and (not input) magit-process-connection-type)) + (process-environment (append (magit-cygwin-env-vars) + process-environment))) + (apply #'start-file-process + (file-name-nondirectory program) + process-buf program args)))) + (with-editor-set-process-filter process #'magit-process-filter) + (set-process-sentinel process #'magit-process-sentinel) + (set-process-buffer process process-buf) + (process-put process 'section section) + (process-put process 'command-buf (current-buffer)) + (process-put process 'default-dir default-directory) + (when inhibit-magit-refresh + (process-put process 'inhibit-refresh t)) + (setf (magit-section-process section) process) + (with-current-buffer process-buf + (set-marker (process-mark process) (point))) + (when input + (with-current-buffer input + (process-send-region process (point-min) (point-max)) + (process-send-eof process))) + (setq magit-this-process process) + (setf (magit-section-value section) process) + (magit-process-display-buffer process) + process)) + +;;; Process Internals + +(defun magit-process-setup (program args) + (magit-process-set-mode-line program args) + (let ((pwd default-directory) + (buf (magit-process-buffer t))) + (cons buf (with-current-buffer buf + (prog1 (magit-process-insert-section pwd program args nil nil) + (backward-char 1)))))) + +(defun magit-process-insert-section (pwd program args &optional errcode errlog) + (let ((inhibit-read-only t) + (magit-insert-section--parent magit-root-section)) + (goto-char (1- (point-max))) + (magit-insert-section (process) + (insert (if errcode + (format "%3s " (propertize (number-to-string errcode) + 'face 'magit-process-ng)) + "run ")) + (unless (equal (expand-file-name pwd) + (expand-file-name default-directory)) + (insert (file-relative-name pwd default-directory) ?\s)) + (insert (propertize program 'face 'magit-section-heading)) + (insert " ") + (when (and args (equal program magit-git-executable)) + (setq args (-split-at (length magit-git-global-arguments) args)) + (insert (propertize (char-to-string magit-ellipsis) + 'face 'magit-section-heading + 'help-echo (mapconcat #'identity (car args) " "))) + (insert " ") + (setq args (cadr args))) + (insert (propertize (mapconcat #'identity args " ") + 'face 'magit-section-heading)) + (magit-insert-heading) + (when errlog + (insert-file-contents errlog) + (goto-char (1- (point-max)))) + (insert "\n")))) + +(defun magit-process-truncate-log () + (let* ((head nil) + (tail (magit-section-children magit-root-section)) + (count (length tail))) + (when (> (1+ count) magit-process-log-max) + (while (and (cdr tail) + (> count (/ magit-process-log-max 2))) + (let* ((inhibit-read-only t) + (section (car tail)) + (process (magit-section-process section))) + (cond ((not process)) + ((memq (process-status process) '(exit signal)) + (delete-region (magit-section-start section) + (1+ (magit-section-end section))) + (cl-decf count)) + (t + (push section head)))) + (pop tail)) + (setf (magit-section-children magit-root-section) + (nconc (reverse head) tail))))) + +(defun magit-process-sentinel (process event) + "Default sentinel used by `magit-start-process'." + (when (memq (process-status process) '(exit signal)) + (setq event (substring event 0 -1)) + (when (string-match "^finished" event) + (message (concat (capitalize (process-name process)) " finished"))) + (magit-process-finish process) + (when (eq process magit-this-process) + (setq magit-this-process nil)) + (unless (process-get process 'inhibit-refresh) + (let ((command-buf (process-get process 'command-buf))) + (if (buffer-live-p command-buf) + (with-current-buffer command-buf + (magit-refresh)) + (with-temp-buffer + (setq default-directory (process-get process 'default-dir)) + (magit-refresh))))))) + +(defun magit-sequencer-process-sentinel (process event) + "Special sentinel used by `magit-run-git-sequencer'." + (when (memq (process-status process) '(exit signal)) + (magit-process-sentinel process event) + (--when-let (magit-mode-get-buffer 'magit-status-mode) + (with-current-buffer it + (--when-let + (magit-get-section + `((commit . ,(magit-rev-parse "HEAD")) + (,(pcase (car (cadr (-split-at + (1+ (length magit-git-global-arguments)) + (process-command process)))) + ((or "rebase" "am") 'rebase-sequence) + ((or "cherry-pick" "revert") 'sequence))) + (status))) + (goto-char (magit-section-start it)) + (magit-section-update-highlight)))))) + +(defun magit-process-filter (proc string) + "Default filter used by `magit-start-process'." + (with-current-buffer (process-buffer proc) + (let ((inhibit-read-only t)) + (magit-process-yes-or-no-prompt proc string) + (magit-process-username-prompt proc string) + (magit-process-password-prompt proc string) + (goto-char (process-mark proc)) + (setq string (propertize string 'magit-section + (process-get proc 'section))) + ;; Find last ^M in string. If one was found, ignore + ;; everything before it and delete the current line. + (let ((ret-pos (length string))) + (while (and (>= (cl-decf ret-pos) 0) + (/= ?\r (aref string ret-pos)))) + (if (< ret-pos 0) + (insert string) + (delete-region (line-beginning-position) (point)) + (insert (substring string (1+ ret-pos))))) + (set-marker (process-mark proc) (point))))) + +(defmacro magit-process-kill-on-abort (proc &rest body) + (declare (indent 1) (debug (form body))) + (let ((map (cl-gensym))) + `(let ((,map (make-sparse-keymap))) + (set-keymap-parent ,map minibuffer-local-map) + (define-key ,map "\C-g" + (lambda () + (interactive) + (ignore-errors (kill-process ,proc)) + (abort-recursive-edit))) + (let ((minibuffer-local-map ,map)) + ,@body)))) + +(defun magit-process-yes-or-no-prompt (process string) + "Forward Yes-or-No prompts to the user." + (-when-let (beg (string-match magit-process-yes-or-no-prompt-regexp string)) + (let ((max-mini-window-height 30)) + (process-send-string + process + (downcase + (concat + (match-string + (if (save-match-data + (magit-process-kill-on-abort process + (yes-or-no-p (substring string 0 beg)))) 1 2) + string) + "\n")))))) + +(defun magit-process-password-auth-source (key) + "Use `auth-source-search' to get a password. +If found, return the password. Otherwise, return nil." + (require 'auth-source) + (let ((secret (plist-get (car (auth-source-search :max 1 :host key + :require '(:host))) + :secret))) + (if (functionp secret) + (funcall secret) + secret))) + +(defun magit-process-password-prompt (process string) + "Find a password based on prompt STRING and send it to git. +First try the functions in `magit-process-find-password-functions'. +If none of them returns a password, then read it from the user +instead." + (--when-let (magit-process-match-prompt + magit-process-password-prompt-regexps string) + (process-send-string + process (magit-process-kill-on-abort process + (concat (or (--when-let (match-string 99 string) + (run-hook-with-args-until-success + 'magit-process-find-password-functions it)) + (read-passwd it)) + "\n"))))) + +(defun magit-process-username-prompt (process string) + "Forward username prompts to the user." + (--when-let (magit-process-match-prompt + magit-process-username-prompt-regexps string) + (process-send-string + process (magit-process-kill-on-abort process + (concat (read-string it nil nil (user-login-name)) "\n"))))) + +(defun magit-process-match-prompt (prompts string) + "Match STRING against PROMPTS and set match data. +Return the matched string suffixed with \": \", if needed." + (when (--any? (string-match it string) prompts) + (let ((prompt (match-string 0 string))) + (cond ((string-suffix-p ": " prompt) prompt) + ((string-suffix-p ":" prompt) (concat prompt " ")) + (t (concat prompt ": ")))))) + +(defvar magit-credential-hook nil + "Hook run before Git needs credentials.") + +(defvar magit-credential-cache-daemon-process nil) + +(defun magit-maybe-start-credential-cache-daemon () + "Maybe start a `git-credential-cache--daemon' process. + +If such a process is already running or if the value of option +`magit-credential-cache-daemon-socket' is nil, then do nothing. +Otherwise start the process passing the value of that options +as argument." + (unless (or (not magit-credential-cache-daemon-socket) + (process-live-p magit-credential-cache-daemon-process) + (memq magit-credential-cache-daemon-process + (list-system-processes))) + (setq magit-credential-cache-daemon-process + (or (--first (-let (((&alist 'comm comm 'user user) + (process-attributes it))) + (and (string= comm "git-credential-cache--daemon") + (string= user user-login-name))) + (list-system-processes)) + (condition-case nil + (start-process "git-credential-cache--daemon" + " *git-credential-cache--daemon*" + magit-git-executable + "credential-cache--daemon" + magit-credential-cache-daemon-socket) + ;; Some Git implementations (e.g. Windows) won't have + ;; this program; if we fail the first time, stop trying. + ((debug error) + (remove-hook 'magit-credential-hook + #'magit-maybe-start-credential-cache-daemon))))))) + +(add-hook 'magit-credential-hook #'magit-maybe-start-credential-cache-daemon) + +(defun tramp-sh-handle-start-file-process--magit-tramp-process-environment + (fn name buffer program &rest args) + (if magit-tramp-process-environment + (apply fn name buffer + (car magit-tramp-process-environment) + (append (cdr magit-tramp-process-environment) + (cons program args))) + (apply fn name buffer program args))) + +(advice-add 'tramp-sh-handle-start-file-process :around + 'tramp-sh-handle-start-file-process--magit-tramp-process-environment) + +(defun tramp-sh-handle-process-file--magit-tramp-process-environment + (fn program &optional infile destination display &rest args) + (if magit-tramp-process-environment + (apply fn "env" infile destination display + (append magit-tramp-process-environment + (cons program args))) + (apply fn program infile destination display args))) + +(advice-add 'tramp-sh-handle-process-file :around + 'tramp-sh-handle-process-file--magit-tramp-process-environment) + +(defun magit-process-set-mode-line (program args) + (when (equal program magit-git-executable) + (setq args (nthcdr (length magit-git-global-arguments) args))) + (let ((str (concat " " program (and args (concat " " (car args)))))) + (dolist (buf (magit-mode-get-buffers)) + (with-current-buffer buf (setq mode-line-process str))))) + +(defun magit-process-unset-mode-line () + (dolist (buf (magit-mode-get-buffers)) + (with-current-buffer buf (setq mode-line-process nil)))) + +(defvar magit-process-error-message-re + (concat "^\\(?:error\\|fatal\\|git\\): \\(.*\\)" paragraph-separate)) + +(define-error 'magit-git-error "Git error") + +(defvar-local magit-this-error nil) + +(defun magit-process-finish (arg &optional process-buf command-buf + default-dir section) + (unless (integerp arg) + (setq process-buf (process-buffer arg) + command-buf (process-get arg 'command-buf) + default-dir (process-get arg 'default-dir) + section (process-get arg 'section) + arg (process-exit-status arg))) + (magit-process-unset-mode-line) + (when (featurep 'dired) + (dired-uncache default-dir)) + (when (buffer-live-p process-buf) + (with-current-buffer process-buf + (let ((inhibit-read-only t) + (marker (magit-section-start section))) + (goto-char marker) + (save-excursion + (delete-char 3) + (set-marker-insertion-type marker nil) + (insert (propertize (format "%3s" arg) + 'magit-section section + 'face (if (= arg 0) + 'magit-process-ok + 'magit-process-ng))) + (set-marker-insertion-type marker t)) + (if (= (magit-section-end section) + (+ (line-end-position) 2)) + (save-excursion + (goto-char (1+ (line-end-position))) + (delete-char -1) + (setf (magit-section-content section) nil)) + (let ((buf (magit-process-buffer t))) + (when (and (= arg 0) + (not (--any-p (eq (window-buffer it) buf) + (window-list)))) + (magit-section-hide section))))))) + (unless (= arg 0) + (let ((msg (or (and (buffer-live-p process-buf) + (with-current-buffer process-buf + (save-excursion + (goto-char (magit-section-end section)) + (--when-let (magit-section-content section) + (when (re-search-backward + magit-process-error-message-re it t) + (match-string 1)))))) + "Git failed"))) + (if magit-process-raise-error + (signal 'magit-git-error (format "%s (in %s)" msg default-dir)) + (--when-let (magit-mode-get-buffer 'magit-status-mode) + (setq magit-this-error msg)) + (message "%s ... [%s buffer %s for details]" msg + (-if-let (key (and (buffer-live-p command-buf) + (with-current-buffer command-buf + (car (where-is-internal + 'magit-process-buffer))))) + (format "Hit %s to see" (key-description key)) + "See") + (buffer-name process-buf))))) + arg) + +(defun magit-process-display-buffer (process) + (when (process-live-p process) + (let ((buf (process-buffer process))) + (cond ((not (buffer-live-p buf))) + ((= magit-process-popup-time 0) + (if (minibufferp) + (switch-to-buffer-other-window buf) + (pop-to-buffer buf))) + ((> magit-process-popup-time 0) + (run-with-timer magit-process-popup-time nil + (lambda (p) + (when (eq (process-status p) 'run) + (let ((buf (process-buffer p))) + (when (buffer-live-p buf) + (if (minibufferp) + (switch-to-buffer-other-window buf) + (pop-to-buffer buf)))))) + process)))))) + +;;; magit-process.el ends soon +(provide 'magit-process) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-process.el ends here diff --git a/elpa/magit-20160223.828/magit-remote.el b/elpa/magit-20160223.828/magit-remote.el new file mode 100644 index 0000000..7a29ac4 --- /dev/null +++ b/elpa/magit-20160223.828/magit-remote.el @@ -0,0 +1,719 @@ +;;; magit-remote.el --- transfer Git commits -*- lexical-binding: t -*- + +;; Copyright (C) 2008-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library implements support for interacting with remote +;; repositories. Commands for cloning, fetching, pulling, and +;; pushing are defined here. + +;;; Code: + +(require 'magit) + +;;; Clone + +(defcustom magit-clone-set-remote-head nil + "Whether cloning creates the symbolic-ref `/HEAD'." + :package-version '(magit . "2.4.2") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-clone-set-remote.pushDefault 'ask + "Whether to set the value of `remote.pushDefault' after cloning. + +If t, then set without asking. If nil, then don't set. If +`ask', then ask." + :package-version '(magit . "2.4.0") + :group 'magit-commands + :type '(choice (const :tag "set" t) + (const :tag "ask" ask) + (const :tag "don't set" nil))) + +;;;###autoload +(defun magit-clone (repository directory) + "Clone the REPOSITORY to DIRECTORY. +Then show the status buffer for the new repository." + (interactive + (let ((url (magit-read-string-ns "Clone repository"))) + (list url (read-directory-name + "Clone to: " nil nil nil + (and (string-match "\\([^./]+\\)\\(\\.git\\)?$" url) + (match-string 1 url)))))) + (setq directory (file-name-as-directory (expand-file-name directory))) + (message "Cloning %s..." repository) + (when (= (magit-call-git "clone" repository + ;; Stop cygwin git making a "c:" directory. + (magit-convert-git-filename directory)) + 0) + (let ((default-directory directory)) + (when (or (eq magit-clone-set-remote.pushDefault t) + (and magit-clone-set-remote.pushDefault + (y-or-n-p "Set `remote.pushDefault' to \"origin\"? "))) + (magit-call-git "config" "remote.pushDefault" "origin")) + (unless magit-clone-set-remote-head + (magit-remote-unset-head "origin"))) + (message "Cloning %s...done" repository) + (magit-status-internal directory))) + +;;; Setup + +(defcustom magit-remote-add-set-remote.pushDefault 'ask-if-unset + "Whether to set the value of `remote.pushDefault' after adding a remote. + +If `ask', then always ask. If `ask-if-unset', then ask, but only +if the variable isn't set already. If nil, then don't ever set. +If the value is a string, then set without asking, provided the +name of the name of the added remote is equal to that string and +the variable isn't already set." + :package-version '(magit . "2.4.0") + :group 'magit-commands + :type '(choice (const :tag "ask if unset" ask-if-unset) + (const :tag "always ask" ask) + (string :tag "set if named") + (const :tag "don't set"))) + +;;;###autoload (autoload 'magit-remote-popup "magit-remote" nil t) +(magit-define-popup magit-remote-popup + "Popup console for remote commands." + 'magit-commands nil nil + :man-page "git-remote" + :actions '((?a "Add" magit-remote-add) + (?r "Rename" magit-remote-rename) + (?k "Remove" magit-remote-remove) + (?u "Set url" magit-remote-set-url))) + +(defun magit-read-url (prompt &optional initial-input) + (let ((url (magit-read-string-ns prompt initial-input))) + (if (string-prefix-p "~" url) + (expand-file-name url) + url))) + +;;;###autoload +(defun magit-remote-add (remote url) + "Add a remote named REMOTE and fetch it." + (interactive (list (magit-read-string-ns "Remote name") + (magit-read-url "Remote url"))) + (if (pcase (list magit-remote-add-set-remote.pushDefault + (magit-get "remote.defaultPush")) + (`(,(pred stringp) ,_) t) + ((or `(ask ,_) `(ask-if-unset nil)) + (y-or-n-p (format "Set `remote.pushDefault' to \"%s\"? " remote)))) + (progn (magit-call-git "remote" "add" "-f" remote url) + (magit-call-git "config" "remote.pushDefault" remote) + (magit-refresh)) + (magit-run-git-async "remote" "add" "-f" remote url))) + +;;;###autoload +(defun magit-remote-rename (old new) + "Rename the remote named OLD to NEW." + (interactive + (let ((remote (magit-read-remote "Rename remote"))) + (list remote (magit-read-string-ns (format "Rename %s to" remote))))) + (unless (string= old new) + (magit-run-git "remote" "rename" old new))) + +;;;###autoload +(defun magit-remote-set-url (remote url) + "Change the url of the remote named REMOTE to URL." + (interactive + (let ((remote (magit-read-remote "Set url of remote"))) + (list remote (magit-read-url + "Url" (magit-get "remote" remote "url"))))) + (magit-run-git "remote" "set-url" remote url)) + +;;;###autoload +(defun magit-remote-remove (remote) + "Delete the remote named REMOTE." + (interactive (list (magit-read-remote "Delete remote"))) + (magit-run-git "remote" "rm" remote)) + +;;;###autoload +(defun magit-remote-set-head (remote &optional branch) + "Set the local representation of REMOTE's default branch. +Query REMOTE and set the symbolic-ref refs/remotes//HEAD +accordingly. With a prefix argument query for the branch to be +used, which allows you to select an incorrect value if you fancy +doing that." + (interactive + (let ((remote (magit-read-remote "Set HEAD for remote"))) + (list remote + (and current-prefix-arg + (magit-read-remote-branch (format "Set %s/HEAD to" remote) + remote nil nil t))))) + (magit-run-git "remote" "set-head" remote (or branch "--auto"))) + +;;;###autoload +(defun magit-remote-unset-head (remote) + "Unset the local representation of REMOTE's default branch. +Delete the symbolic-ref \"refs/remotes//HEAD\"." + (interactive (list (magit-read-remote "Unset HEAD for remote"))) + (magit-run-git "remote" "set-head" remote "--delete")) + +;;; Fetch + +;;;###autoload (autoload 'magit-fetch-popup "magit-remote" nil t) +(magit-define-popup magit-fetch-popup + "Popup console for fetch commands." + 'magit-commands + :man-page "git-fetch" + :switches '((?p "Prune deleted branches" "--prune")) + :actions '("Fetch from" + (?p magit-get-push-remote magit-fetch-from-pushremote) + (?u magit-get-remote magit-fetch-from-upstream) + (?e "elsewhere" magit-fetch) + (?a "all remotes" magit-fetch-all) + "Fetch" + (?m "submodules" magit-submodule-fetch)) + :default-action 'magit-fetch + :max-action-columns 1) + +(defun magit-git-fetch (remote args) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "fetch" remote args)) + +;;;###autoload +(defun magit-fetch-from-pushremote (args) + "Fetch from the push-remote of the current branch." + (interactive (list (magit-fetch-arguments))) + (--if-let (magit-get-push-remote) + (magit-git-fetch it args) + (--if-let (magit-get-current-branch) + (user-error "No push-remote is configured for %s" it) + (user-error "No branch is checked out")))) + +;;;###autoload +(defun magit-fetch-from-upstream (args) + "Fetch from the upstream repository of the current branch." + (interactive (list (magit-fetch-arguments))) + (--if-let (magit-get-remote) + (magit-git-fetch it args) + (--if-let (magit-get-current-branch) + (user-error "No upstream is configured for %s" it) + (user-error "No branch is checked out")))) + +;;;###autoload +(defun magit-fetch (remote args) + "Fetch from another repository." + (interactive (list (magit-read-remote "Fetch remote") + (magit-fetch-arguments))) + (magit-git-fetch remote args)) + +;;;###autoload +(defun magit-fetch-all (args) + "Fetch from all remotes." + (interactive (list (magit-fetch-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "remote" "update" args)) + +;;;###autoload +(defun magit-fetch-all-prune () + "Fetch from all remotes, and prune. +Prune remote tracking branches for branches that have been +removed on the respective remote." + (interactive) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "remote" "update" "--prune")) + +;;;###autoload +(defun magit-fetch-all-no-prune () + "Fetch from all remotes." + (interactive) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "remote" "update")) + +;;; Pull + +;;;###autoload (autoload 'magit-pull-popup "magit-remote" nil t) +(magit-define-popup magit-pull-popup + "Popup console for pull commands." + 'magit-commands + :man-page "git-pull" + :variables '("Variables" + (?r "branch.%s.rebase" + magit-cycle-branch*rebase + magit-pull-format-branch*rebase)) + :actions '((lambda () + (--if-let (magit-get-current-branch) + (concat + (propertize "Pull into " 'face 'magit-popup-heading) + (propertize it 'face 'magit-branch-local) + (propertize " from" 'face 'magit-popup-heading)) + (propertize "Pull from" 'face 'magit-popup-heading))) + (?p magit-get-push-branch magit-pull-from-pushremote) + (?u magit-get-upstream-branch magit-pull-from-upstream) + (?e "elsewhere" magit-pull)) + :default-action 'magit-pull + :max-action-columns 1) + +;;;###autoload (autoload 'magit-pull-and-fetch-popup "magit-remote" nil t) +(magit-define-popup magit-pull-and-fetch-popup + "Popup console for pull and fetch commands. + +This popup is intended as a replacement for the separate popups +`magit-pull-popup' and `magit-fetch-popup'. To use it, add this +to your init file: + + (with-eval-after-load \\='magit-remote + (define-key magit-mode-map \"f\" \\='magit-pull-and-fetch-popup) + (define-key magit-mode-map \"F\" nil)) + +The combined popup does not offer all commands and arguments +available from the individual popups. Instead of the argument +`--prune' and the command `magit-fetch-all' it uses two commands +`magit-fetch-prune' and `magit-fetch-no-prune'. And the commands +`magit-fetch-from-pushremote' and `magit-fetch-from-upstream' are +missing. To add them use something like: + + (with-eval-after-load \\='magit-remote + (magit-define-popup-action \\='magit-pull-and-fetch-popup ?U + \\='magit-get-upstream-branch + \\='magit-fetch-from-upstream-remote ?F) + (magit-define-popup-action \\='magit-pull-and-fetch-popup ?P + \\='magit-get-push-branch + \\='magit-fetch-from-push-remote ?F))" + 'magit-commands + :man-page "git-pull" + :variables '("Pull variables" + (?r "branch.%s.rebase" + magit-cycle-branch*rebase + magit-pull-format-branch*rebase)) + :actions '((lambda () + (--if-let (magit-get-current-branch) + (concat + (propertize "Pull into " 'face 'magit-popup-heading) + (propertize it 'face 'magit-branch-local) + (propertize " from" 'face 'magit-popup-heading)) + (propertize "Pull from" 'face 'magit-popup-heading))) + (?p magit-get-push-branch magit-pull-from-pushremote) + (?u magit-get-upstream-branch magit-pull-from-upstream) + (?e "elsewhere" magit-pull) + "Fetch from" + (?f "remotes" magit-fetch-all-no-prune) + (?F "remotes and prune" magit-fetch-all-prune) + "Fetch" + (?m "submodules" magit-submodule-fetch)) + :default-action 'magit-fetch + :max-action-columns 1) + +(defun magit-pull-format-branch*rebase () + (magit-popup-format-variable (format "branch.%s.rebase" + (or (magit-get-current-branch) "")) + '("true" "false") + "false" "pull.rebase")) + +(defun magit-git-pull (source args) + (run-hooks 'magit-credential-hook) + (-let [(remote . branch) + (magit-split-branch-name source)] + (magit-run-git-with-editor "pull" args remote branch))) + +;;;###autoload +(defun magit-pull-from-pushremote (args) + "Pull from the push-remote of the current branch." + (interactive (list (magit-pull-arguments))) + (--if-let (magit-get-push-branch) + (magit-git-pull it args) + (--if-let (magit-get-current-branch) + (user-error "No push-remote is configured for %s" it) + (user-error "No branch is checked out")))) + +;;;###autoload +(defun magit-pull-from-upstream (args) + "Pull from the upstream of the current branch." + (interactive (list (magit-pull-arguments))) + (--if-let (magit-get-upstream-branch) + (magit-git-pull it args) + (--if-let (magit-get-current-branch) + (user-error "No upstream is configured for %s" it) + (user-error "No branch is checked out")))) + +;;;###autoload +(defun magit-pull (source args) + "Pull from a branch read in the minibuffer." + (interactive (list (magit-read-remote-branch "Pull" nil nil nil t) + (magit-pull-arguments))) + (magit-git-pull source args)) + +;;; Push + +(defcustom magit-push-current-set-remote-if-missing t + "Whether to configure missing remotes before pushing. + +When nil, then the command `magit-push-current-to-pushremote' and +`magit-push-current-to-upstream' do not appear in the push popup +if the push-remote resp. upstream is not configured. If the user +invokes one of these commands anyway, then it raises an error. + +When non-nil, then these commands always appear in the push +popup. But if the required configuration is missing, then they +do appear in a way that indicates that this is the case. If the +user invokes one of them, then it asks for the necessary +configuration, stores the configuration, and then uses it to push +a first time. + +This option also affects whether the argument `--set-upstream' is +available in the popup. If the value is t, then that argument is +redundant. But note that changing the value of this option does +not take affect immediately, the argument will only be added or +removed after restarting Emacs." + :package-version '(magit . "2.4.0") + :group 'magit-commands + :type 'boolean) + +;;;###autoload (autoload 'magit-push-popup "magit-remote" nil t) +(magit-define-popup magit-push-popup + "Popup console for push commands." + 'magit-commands + :man-page "git-push" + :switches `((?f "Force" "--force-with-lease") + (?h "Disable hooks" "--no-verify") + (?d "Dry run" "--dry-run") + ,@(and (not magit-push-current-set-remote-if-missing) + '((?u "Set upstream" "--set-upstream")))) + :actions '((lambda () + (--when-let (magit-get-current-branch) + (concat (propertize "Push " 'face 'magit-popup-heading) + (propertize it 'face 'magit-branch-local) + (propertize " to" 'face 'magit-popup-heading)))) + (?p magit--push-current-to-pushremote-desc + magit-push-current-to-pushremote) + (?u magit--push-current-to-upstream-desc + magit-push-current-to-upstream) + (?e "elsewhere\n" magit-push-current) + "Push" + (?o "another branch" magit-push) + (?T "a tag" magit-push-tag) + (?r "explicit refspecs" magit-push-refspecs) + (?t "all tags" magit-push-tags) + (?m "matching branches" magit-push-matching)) + :max-action-columns 2) + +(defun magit-git-push (branch target args) + (run-hooks 'magit-credential-hook) + (-let [(remote . target) + (magit-split-branch-name target)] + (magit-run-git-async "push" "-v" args remote + (format "%s:refs/heads/%s" branch target)))) + +;;;###autoload +(defun magit-push-current-to-pushremote (args &optional push-remote) + "Push the current branch to `branch..pushRemote'. +If that variable is unset, then push to `remote.pushDefault'. + +When `magit-push-current-set-remote-if-missing' is non-nil and +the push-remote is not configured, then read the push-remote from +the user, set it, and then push to it. With a prefix argument +the push-remote can be changed before pushed to it." + (interactive + (list (magit-push-arguments) + (and (magit--push-current-set-pushremote-p current-prefix-arg) + (magit-read-remote (format "Set push-remote of %s and push there" + (magit-get-current-branch)))))) + (--if-let (magit-get-current-branch) + (progn (when push-remote + (magit-call-git "config" + (format "branch.%s.pushRemote" + (magit-get-current-branch)) + push-remote)) + (-if-let (remote (magit-get-push-remote it)) + (if (member remote (magit-list-remotes)) + (magit-git-push it (concat remote "/" it) args) + (user-error "Remote `%s' doesn't exist" remote)) + (user-error "No push-remote is configured for %s" it))) + (user-error "No branch is checked out"))) + +(defun magit--push-current-set-pushremote-p (&optional change) + (and (or change + (and magit-push-current-set-remote-if-missing + (not (magit-get-push-remote)))) + (magit-get-current-branch))) + +(defun magit--push-current-to-pushremote-desc () + (--if-let (magit-get-push-branch) + (concat (magit-branch-set-face it) "\n") + (and (magit--push-current-set-pushremote-p) + (concat (propertize "pushRemote" 'face 'bold) + ", after setting that\n")))) + +;;;###autoload +(defun magit-push-current-to-upstream (args &optional upstream) + "Push the current branch to its upstream branch. + +When `magit-push-current-set-remote-if-missing' is non-nil and +the upstream is not configured, then read the upstream from the +user, set it, and then push to it. With a prefix argument the +upstream can be changed before pushed to it." + (interactive + (list (magit-push-arguments) + (and (magit--push-current-set-upstream-p current-prefix-arg) + (magit-read-upstream-branch)))) + (--if-let (magit-get-current-branch) + (progn + (when upstream + (magit-set-branch*merge/remote it upstream)) + (-if-let (target (magit-get-upstream-branch it)) + (magit-git-push it target args) + (user-error "No upstream is configured for %s" it))) + (user-error "No branch is checked out"))) + +(defun magit--push-current-set-upstream-p (&optional change) + (and (or change + (and magit-push-current-set-remote-if-missing + (not (magit-get-upstream-branch)))) + (magit-get-current-branch))) + +(defun magit--push-current-to-upstream-desc () + (--if-let (magit-get-upstream-branch) + (concat (magit-branch-set-face it) "\n") + (and (magit--push-current-set-upstream-p) + (concat (propertize "@{upstream}" 'face 'bold) + ", after setting that\n")))) + +;;;###autoload +(defun magit-push-current (target args) + "Push the current branch to a branch read in the minibuffer." + (interactive + (--if-let (magit-get-current-branch) + (list (magit-read-remote-branch (format "Push %s to" it) + nil nil it 'confirm) + (magit-push-arguments)) + (user-error "No branch is checked out"))) + (magit-git-push (magit-get-current-branch) target args)) + +;;;###autoload +(defun magit-push (source target args) + "Push an arbitrary branch or commit somewhere. +Both the source and the target are read in the minibuffer." + (interactive + (let ((source (magit-read-local-branch-or-commit "Push"))) + (list source + (magit-read-remote-branch (format "Push %s to" source) nil + (magit-get-upstream-branch source) + source 'confirm) + (magit-push-arguments)))) + (magit-git-push source target args)) + +;;;###autoload +(defun magit-push-refspecs (remote refspecs args) + "Push one or multiple REFSPECS to a REMOTE. +Both the REMOTE and the REFSPECS are read in the minibuffer. To +use multiple REFSPECS, separate them with commas. Completion is +only available for the part before the colon, or when no colon +is used." + (interactive + (list (magit-read-remote "Push to remote") + (completing-read-multiple + "Push refspec,s: " + (cons "HEAD" (magit-list-local-branch-names))) + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote refspecs)) + +;;;###autoload +(defun magit-push-matching (remote &optional args) + "Push all matching branches to another repository. +If multiple remotes exist, then read one from the user. +If just one exists, use that without requiring confirmation." + (interactive (list (magit-read-remote "Push matching branches to" nil t) + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote ":")) + +;;;###autoload +(defun magit-push-tags (remote &optional args) + "Push all tags to another repository. +If only one remote exists, then push to that. Otherwise prompt +for a remote, offering the remote configured for the current +branch as default." + (interactive (list (magit-read-remote "Push tags to remote" nil t) + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" remote "--tags" args)) + +;;;###autoload +(defun magit-push-tag (tag remote &optional args) + "Push a tag to another repository." + (interactive + (let ((tag (magit-read-tag "Push tag"))) + (list tag (magit-read-remote (format "Push %s to remote" tag) nil t) + (magit-push-arguments)))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" remote tag args)) + +;;;###autoload +(defun magit-push-implicitly (args) + "Push somewhere without using an explicit refspec. + +This command simply runs \"git push -v [ARGS]\". ARGS are the +arguments specified in the popup buffer. No explicit refspec +arguments are used. Instead the behavior depends on at least +these Git variables: `push.default', `remote.pushDefault', +`branch..pushRemote', `branch..remote', +`branch..merge', and `remote..push'. + +To add this command to the push popup add this to your init file: + + (with-eval-after-load \\='magit-remote + (magit-define-popup-action \\='magit-push-popup ?P + 'magit-push-implicitly--desc + 'magit-push-implicitly ?p t)) + +The function `magit-push-implicitly--desc' attempts to predict +what this command will do, the value it returns is displayed in +the popup buffer." + (interactive (list (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args)) + +(defun magit-push-implicitly--desc () + (let ((default (magit-get "push.default"))) + (unless (equal default "nothing") + (or (-when-let* ((remote (or (magit-get-remote) + (magit-remote-p "origin"))) + (refspec (magit-get "remote" remote "push"))) + (format "%s using %s" + (propertize remote 'face 'magit-branch-remote) + (propertize refspec 'face 'bold))) + (--when-let (and (not (magit-get-push-branch)) + (magit-get-upstream-branch)) + (format "%s aka %s\n" + (magit-branch-set-face it) + (propertize "@{upstream}" 'face 'bold))) + (--when-let (magit-get-push-branch) + (format "%s aka %s\n" + (magit-branch-set-face it) + (propertize "pushRemote" 'face 'bold))) + (--when-let (magit-get-@{push}-branch) + (format "%s aka %s\n" + (magit-branch-set-face it) + (propertize "@{push}" 'face 'bold))) + (format "using %s (%s is %s)\n" + (propertize "git push" 'face 'bold) + (propertize "push.default" 'face 'bold) + (propertize default 'face 'bold)))))) + +;;;###autoload +(defun magit-push-to-remote (remote args) + "Push to REMOTE without using an explicit refspec. +The REMOTE is read in the minibuffer. + +This command simply runs \"git push -v [ARGS] REMOTE\". ARGS +are the arguments specified in the popup buffer. No refspec +arguments are used. Instead the behavior depends on at least +these Git variables: `push.default', `remote.pushDefault', +`branch..pushRemote', `branch..remote', +`branch..merge', and `remote..push'. + +To add this command to the push popup add this to your init file: + + (with-eval-after-load \\='magit-remote + (magit-define-popup-action \\='magit-push-popup ?r + 'magit-push-to-remote--desc + 'magit-push-to-remote ?p t))" + (interactive (list (magit-read-remote "Push to remote") + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote)) + +(defun magit-push-to-remote--desc () + (format "using %s\n" (propertize "git push " 'face 'bold))) + +;;; Email + +;;;###autoload (autoload 'magit-patch-popup "magit-remote" nil t) +(magit-define-popup magit-patch-popup + "Popup console for patch commands." + 'magit-commands + :man-page "git-format-patch" + :switches '("Switches for formatting patches" + (?l "Add cover letter" "--cover-letter")) + :options '("Options for formatting patches" + (?f "From" "--from=") + (?t "To" "--to=") + (?c "CC" "--cc=") + (?r "In reply to" "--in-reply-to=") + (?v "Reroll count" "--reroll-count=") + (?s "Thread style" "--thread=") + (?U "Context lines" "-U") + (?M "Detect renames" "-M") + (?C "Detect copies" "-C") + (?A "Diff algorithm" "--diff-algorithm=" + magit-diff-select-algorithm) + (?o "Output directory" "--output-directory=")) + :actions '((?p "Format patches" magit-format-patch) + (?r "Request pull" magit-request-pull)) + :default-action 'magit-format-patch) + +;;;###autoload +(defun magit-format-patch (range args) + "Create patches for the commits in RANGE. +When a single commit is given for RANGE, create a patch for the +changes introduced by that commit (unlike 'git format-patch' +which creates patches for all commits that are reachable from +HEAD but not from the specified commit)." + (interactive + (list (-if-let (revs (magit-region-values 'commit)) + (concat (car (last revs)) "^.." (car revs)) + (let ((range (magit-read-range-or-commit "Format range or commit"))) + (if (string-match-p "\\.\\." range) + range + (format "%s~..%s" range range)))) + (magit-patch-arguments))) + (magit-call-git "format-patch" range args) + (when (member "--cover-letter" args) + (find-file + (expand-file-name + "0000-cover-letter.patch" + (let ((topdir (magit-toplevel))) + (or (--some (and (string-match "--output-directory=\\(.+\\)" it) + (expand-file-name (match-string 1 it) topdir)) + args) + topdir)))))) + +;;;###autoload +(defun magit-request-pull (url start end) + "Request upstream to pull from you public repository. + +URL is the url of your publically accessible repository. +START is a commit that already is in the upstream repository. +END is the last commit, usually a branch name, which upstream +is asked to pull. START has to be reachable from that commit." + (interactive + (list (magit-get "remote" (magit-read-remote "Remote") "url") + (magit-read-branch-or-commit "Start" (magit-get-upstream-branch)) + (magit-read-branch-or-commit "End"))) + (let ((dir default-directory)) + ;; mu4e changes default-directory + (compose-mail) + (setq default-directory dir)) + (message-goto-body) + (magit-git-insert "request-pull" start url end) + (set-buffer-modified-p nil)) + +;;; magit-remote.el ends soon +(provide 'magit-remote) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-remote.el ends here diff --git a/elpa/magit-20160223.828/magit-section.el b/elpa/magit-20160223.828/magit-section.el new file mode 100644 index 0000000..596a6fc --- /dev/null +++ b/elpa/magit-20160223.828/magit-section.el @@ -0,0 +1,1146 @@ +;;; magit-section.el --- section functionality -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library implements "sections" as used in all Magit buffers. +;; If you have used Magit before then you probably know what that +;; means, otherwise think "read-only Org-Mode for Git", kinda. + +;;; Code: + +(require 'cl-lib) +(require 'dash) + +(require 'magit-utils) + +(defvar magit-keep-region-overlay) + +;;; Options + +(defgroup magit-section nil + "Expandable sections." + :group 'magit) + +(defcustom magit-section-show-child-count t + "Whether to append the number of children to section headings." + :package-version '(magit . "2.1.0") + :group 'magit-section + :type 'boolean) + +(defcustom magit-section-movement-hook + '(magit-hunk-set-window-start + magit-log-maybe-update-revision-buffer + magit-log-maybe-show-more-commits) + "Hook run by `magit-section-goto'. +That function in turn is used by all section movement commands." + :package-version '(magit . "2.3.0") + :group 'magit-section + :type 'hook + :options '(magit-hunk-set-window-start + magit-status-maybe-update-revision-buffer + magit-status-maybe-update-blob-buffer + magit-log-maybe-update-revision-buffer + magit-log-maybe-update-blob-buffer + magit-log-maybe-show-more-commits)) + +(defcustom magit-section-highlight-hook + '(magit-diff-highlight + magit-section-highlight + magit-section-highlight-selection) + "Functions used to highlight the current section. +Each function is run with the current section as only argument +until one of them returns non-nil." + :package-version '(magit . "2.1.0") + :group 'magit-section + :type 'hook + :options '(magit-diff-highlight + magit-section-highlight + magit-section-highlight-selection)) + +(defcustom magit-section-unhighlight-hook + '(magit-diff-unhighlight) + "Functions used to unhighlight the previously current section. +Each function is run with the current section as only argument +until one of them returns non-nil. Most sections are properly +unhighlighted without requiring a specialized unhighlighter, +diff-related sections being the only exception." + :package-version '(magit . "2.1.0") + :group 'magit-section + :type 'hook + :options '(magit-diff-unhighlight)) + +(defcustom magit-section-set-visibility-hook + '(magit-diff-expansion-threshold + magit-section-set-visibility-from-cache) + "Hook used to set the initial visibility of a section. +Stop at the first function that returns non-nil. The value +should be `show' or `hide'. If no function returns non-nil +determine the visibility as usual, i.e. use the hardcoded +section specific default (see `magit-insert-section')." + :package-version '(magit . "2.4.0") + :group 'magit-section + :type 'hook + :options '(magit-diff-expansion-threshold + magit-section-set-visibility-from-cache)) + +(defface magit-section-highlight + '((((class color) (background light)) :background "grey95") + (((class color) (background dark)) :background "grey20")) + "Face for highlighting the current section." + :group 'magit-faces) + +(defface magit-section-heading + '((((class color) (background light)) :foreground "DarkGoldenrod4" :weight bold) + (((class color) (background dark)) :foreground "LightGoldenrod2" :weight bold)) + "Face for section headings." + :group 'magit-faces) + +(defface magit-section-secondary-heading '((t :weight bold)) + "Face for section headings of some secondary headings." + :group 'magit-faces) + +(defface magit-section-heading-selection + '((((class color) (background light)) :foreground "salmon4") + (((class color) (background dark)) :foreground "LightSalmon3")) + "Face for selected section headings." + :group 'magit-faces) + +;;; Core + +(cl-defstruct magit-section + type value start content end hidden washer refined + source diff-header process parent children) + +(defvar-local magit-root-section nil + "The root section in the current buffer. +All other sections are descendants of this section. The value +of this variable is set by `magit-insert-section' and you should +never modify it.") +(put 'magit-root-section 'permanent-local t) + +(defun magit-current-section () + "Return the section at point." + (or (get-text-property (point) 'magit-section) magit-root-section)) + +(defun magit-section-ident (section) + "Return an unique identifier for SECTION. +The return value has the form ((TYPE . VALUE)...)." + (cons (cons (magit-section-type section) + (magit-section-value section)) + (--when-let (magit-section-parent section) + (magit-section-ident it)))) + +(defun magit-get-section (ident &optional root) + "Return the section identified by IDENT. +IDENT has to be a list as returned by `magit-section-ident'." + (setq ident (reverse ident)) + (let ((section (or root magit-root-section))) + (when (eq (car (pop ident)) (magit-section-type section)) + (while (and ident + (setq section + (--first + (and (eq (caar ident) (magit-section-type it)) + (equal (cdar ident) (magit-section-value it))) + (magit-section-children section)))) + (pop ident)) + section))) + +(defvar magit-insert-section--current nil "For internal use only.") +(defvar magit-insert-section--parent nil "For internal use only.") +(defvar magit-insert-section--oldroot nil "For internal use only.") + +;;; Commands +;;;; Movement + +(defun magit-section-forward () + "Move to the beginning of the next visible section." + (interactive) + (if (eobp) + (user-error "No next section") + (let ((section (magit-current-section))) + (if (magit-section-parent section) + (let ((next (and (not (magit-section-hidden section)) + (not (= (magit-section-end section) (1+ (point)))) + (car (magit-section-children section))))) + (while (and section (not next)) + (unless (setq next (car (magit-section-siblings section 'next))) + (setq section (magit-section-parent section)))) + (if next + (magit-section-goto next) + (user-error "No next section"))) + (magit-section-goto 1))))) + +(defun magit-section-backward () + "Move to the beginning of the current or the previous visible section. +When point is at the beginning of a section then move to the +beginning of the previous visible section. Otherwise move to +the beginning of the current section." + (interactive) + (if (bobp) + (user-error "No previous section") + (let ((section (magit-current-section)) children) + (cond + ((and (= (point) (1- (magit-section-end section))) + (setq children (magit-section-children section))) + (magit-section-goto (car (last children)))) + ((and (magit-section-parent section) + (not (= (point) (magit-section-start section)))) + (magit-section-goto section)) + (t + (let ((prev (car (magit-section-siblings section 'prev)))) + (if prev + (while (and (not (magit-section-hidden prev)) + (setq children (magit-section-children prev))) + (setq prev (car (last children)))) + (setq prev (magit-section-parent section))) + (cond (prev + (magit-section-goto prev)) + ((magit-section-parent section) + (user-error "No previous section")) + ;; Eob special cases. + ((not (get-text-property (1- (point)) 'invisible)) + (magit-section-goto -1)) + (t + (goto-char (previous-single-property-change + (1- (point)) 'invisible)) + (forward-line -1) + (magit-section-goto (magit-current-section)))))))))) + +(defun magit-section-up () + "Move to the beginning of the parent section." + (interactive) + (--if-let (magit-section-parent (magit-current-section)) + (magit-section-goto it) + (user-error "No parent section"))) + +(defun magit-section-forward-sibling () + "Move to the beginning of the next sibling section. +If there is no next sibling section, then move to the parent." + (interactive) + (let ((current (magit-current-section))) + (if (magit-section-parent current) + (--if-let (car (magit-section-siblings current 'next)) + (magit-section-goto it) + (magit-section-forward)) + (magit-section-goto 1)))) + +(defun magit-section-backward-sibling () + "Move to the beginning of the previous sibling section. +If there is no previous sibling section, then move to the parent." + (interactive) + (let ((current (magit-current-section))) + (if (magit-section-parent current) + (--if-let (car (magit-section-siblings current 'prev)) + (magit-section-goto it) + (magit-section-backward)) + (magit-section-goto -1)))) + +(defun magit-section-goto (arg) + (if (integerp arg) + (progn (forward-line arg) + (setq arg (magit-current-section))) + (goto-char (magit-section-start arg))) + (run-hook-with-args 'magit-section-movement-hook arg)) + +(defun magit-section-set-window-start (section) + "Ensure the beginning of SECTION is visible." + (unless (pos-visible-in-window-p (magit-section-end section)) + (set-window-start (selected-window) (magit-section-start section)))) + +(defun magit-hunk-set-window-start (section) + "Ensure the beginning of the `hunk' SECTION is visible. +It the SECTION has a different type, then do nothing." + (when (eq (magit-section-type section) 'hunk) + (magit-section-set-window-start section))) + +(defmacro magit-define-section-jumper (name heading type &optional value) + "Define an interactive function to go some section. +Together TYPE and VALUE identify the section. +HEADING is the displayed heading of the section." + (declare (indent defun)) + `(defun ,name (&optional expand) ,(format "\ +Jump to the section \"%s\". +With a prefix argument also expand it." heading) + (interactive "P") + (--if-let (magit-get-section + (cons (cons ',type ,value) + (magit-section-ident magit-root-section))) + (progn (goto-char (magit-section-start it)) + (when expand + (with-local-quit (magit-section-show it)) + (recenter 0))) + (message ,(format "Section \"%s\" wasn't found" heading))))) + +;;;; Visibility + +(defun magit-section-show (section) + "Show the body of the current section." + (interactive (list (magit-current-section))) + (setf (magit-section-hidden section) nil) + (-when-let (washer (magit-section-washer section)) + (setf (magit-section-washer section) nil) + (let ((inhibit-read-only t) + (magit-insert-section--parent section) + (content (magit-section-content section))) + (save-excursion + (if (and content (< content (magit-section-end section))) + (funcall washer section) ; already partially washed (hunk) + (goto-char (magit-section-end section)) + (setf (magit-section-content section) (point-marker)) + (funcall washer) + (setf (magit-section-end section) (point-marker))))) + (magit-section-update-highlight)) + (-when-let (beg (magit-section-content section)) + (remove-overlays beg (magit-section-end section) 'invisible t)) + (magit-section-update-visibility-cache section) + (dolist (child (magit-section-children section)) + (if (magit-section-hidden child) + (magit-section-hide child) + (magit-section-show child)))) + +(defun magit-section-hide (section) + "Hide the body of the current section." + (interactive (list (magit-current-section))) + (if (eq section magit-root-section) + (user-error "Cannot hide root section") + (setf (magit-section-hidden section) t) + (-when-let (beg (magit-section-content section)) + (let ((end (magit-section-end section))) + (remove-overlays beg end 'invisible t) + (let ((o (make-overlay beg end))) + (overlay-put o 'evaporate t) + (overlay-put o 'invisible t)))))) + +(defun magit-section-toggle (section) + "Toggle visibility of the body of the current section." + (interactive (list (magit-current-section))) + (if (eq section magit-root-section) + (user-error "Cannot hide root section") + (goto-char (magit-section-start section)) + (if (magit-section-hidden section) + (magit-section-show section) + (magit-section-hide section)))) + +(defun magit-section-toggle-children (section) + "Toggle visibility of bodies of children of the current section." + (interactive (list (magit-current-section))) + (goto-char (magit-section-start section)) + (let* ((children (magit-section-children section)) + (show (-any? 'magit-section-hidden children))) + (dolist (c children) + (setf (magit-section-hidden c) show))) + (magit-section-show section)) + +(defun magit-section-show-children (section &optional depth) + "Recursively show the bodies of children of the current section. +With a prefix argument show children that deep and hide deeper +children." + (interactive (list (magit-current-section))) + (magit-section-show-children-1 section depth) + (magit-section-show section)) + +(defun magit-section-show-children-1 (section &optional depth) + (dolist (s (magit-section-children section)) + (setf (magit-section-hidden s) nil) + (if depth + (if (> depth 0) + (magit-section-show-children-1 s (1- depth)) + (magit-section-hide s)) + (magit-section-show-children-1 s)))) + +(defun magit-section-hide-children (section) + "Recursively hide the bodies of children of the current section." + (interactive (list (magit-current-section))) + (mapc 'magit-section-hide (magit-section-children section))) + +(defun magit-section-show-headings (section) + "Recursively show headings of children of the current section. +Only show the headings, previously shown text-only bodies are +hidden." + (interactive (list (magit-current-section))) + (magit-section-show-headings-1 section) + (magit-section-show section)) + +(defun magit-section-show-headings-1 (section) + (dolist (s (magit-section-children section)) + (setf (magit-section-hidden s) nil) + (when (or (magit-section-children s) + (not (magit-section-content s))) + (magit-section-show-headings-1 s)))) + +(defun magit-section-cycle (section) + "Cycle visibility of current section and its children." + (interactive (list (magit-current-section))) + (goto-char (magit-section-start section)) + (if (magit-section-hidden section) + (progn (magit-section-show section) + (magit-section-hide-children section)) + (let ((children (magit-section-children section))) + (cond ((and (-any? 'magit-section-hidden children) + (-any? 'magit-section-children children)) + (magit-section-show-headings section)) + ((-any? 'magit-section-hidden-body children) + (magit-section-show-children section)) + (t + (magit-section-hide section)))))) + +(defun magit-section-cycle-global () + "Cycle visibility of all sections in the current buffer." + (interactive) + (let ((children (magit-section-children magit-root-section))) + (cond ((and (-any? 'magit-section-hidden children) + (-any? 'magit-section-children children)) + (magit-section-show-headings magit-root-section)) + ((-any? 'magit-section-hidden-body children) + (magit-section-show-children magit-root-section)) + (t + (mapc 'magit-section-hide children))))) + +(defun magit-section-cycle-diffs () + "Cycle visibility of diff-related sections in the current buffer." + (interactive) + (-when-let (sections + (cond ((derived-mode-p 'magit-status-mode) + (--mapcat + (when it + (when (magit-section-hidden it) + (magit-section-show it)) + (magit-section-children it)) + (list (magit-get-section '((staged) (status))) + (magit-get-section '((unstaged) (status)))))) + ((derived-mode-p 'magit-diff-mode) + (--filter (eq (magit-section-type it) 'file) + (magit-section-children magit-root-section))))) + (if (-any? 'magit-section-hidden sections) + (dolist (s sections) + (magit-section-show s) + (magit-section-hide-children s)) + (let ((children (cl-mapcan 'magit-section-children sections))) + (cond ((and (-any? 'magit-section-hidden children) + (-any? 'magit-section-children children)) + (mapc 'magit-section-show-headings sections)) + ((-any? 'magit-section-hidden-body children) + (mapc 'magit-section-show-children sections)) + (t + (mapc 'magit-section-hide sections))))))) + +(defun magit-section-hidden-body (section &optional pred) + (--if-let (magit-section-children section) + (funcall (or pred '-any?) 'magit-section-hidden-body it) + (and (magit-section-content section) + (magit-section-hidden section)))) + +(defun magit-section-invisible-p (section) + "Return t if the SECTION's body is invisible. +When the body of an ancestor of SECTION is collapsed then +SECTION's body (and heading) obviously cannot be visible." + (or (magit-section-hidden section) + (--when-let (magit-section-parent section) + (magit-section-invisible-p it)))) + +(defun magit-section-show-level (level) + "Show surrounding sections up to LEVEL. +If LEVEL is negative show up to the absolute value. +Sections at higher levels are hidden." + (if (< level 0) + (let ((s (magit-current-section))) + (setq level (- level)) + (while (> (1- (length (magit-section-ident s))) level) + (setq s (magit-section-parent s)) + (goto-char (magit-section-start s))) + (magit-section-show-children magit-root-section (1- level))) + (cl-do* ((s (magit-current-section) (magit-section-parent s)) + (i (1- (length (magit-section-ident s))) (cl-decf i))) + ((cond ((< i level) (magit-section-show-children s (- level i 1)) t) + ((= i level) (magit-section-hide s) t)) + (magit-section-goto s))))) + +(defun magit-section-show-level-1 () + "Show surrounding sections on first level." + (interactive) + (magit-section-show-level 1)) + +(defun magit-section-show-level-1-all () + "Show all sections on first level." + (interactive) + (magit-section-show-level -1)) + +(defun magit-section-show-level-2 () + "Show surrounding sections up to second level." + (interactive) + (magit-section-show-level 2)) + +(defun magit-section-show-level-2-all () + "Show all sections up to second level." + (interactive) + (magit-section-show-level -2)) + +(defun magit-section-show-level-3 () + "Show surrounding sections up to third level." + (interactive) + (magit-section-show-level 3)) + +(defun magit-section-show-level-3-all () + "Show all sections up to third level." + (interactive) + (magit-section-show-level -3)) + +(defun magit-section-show-level-4 () + "Show surrounding sections up to fourth level." + (interactive) + (magit-section-show-level 4)) + +(defun magit-section-show-level-4-all () + "Show all sections up to fourth level." + (interactive) + (magit-section-show-level -4)) + +;;;; Auxiliary + +(defun magit-describe-section () + "Show information about the section at point. +This command is intended for debugging purposes." + (interactive) + (let ((section (magit-current-section))) + (message "%S %S %s-%s" + (magit-section-value section) + (apply 'vector (mapcar 'car (magit-section-ident section))) + (marker-position (magit-section-start section)) + (marker-position (magit-section-end section))))) + +;;; Match + +(defun magit-section-match (condition &optional section) + "Return t if SECTION matches CONDITION. +SECTION defaults to the section at point. + +Conditions can take the following forms: + (CONDITION...) matches if any of the CONDITIONs matches. + [TYPE...] matches if the first TYPE matches the type + of the section at point, the second matches + that of its parent, and so on. + [* TYPE...] matches sections that match [TYPE...] and + also recursively all their child sections. + TYPE matches TYPE regardless of its parents. + +Each TYPE is a symbol. Note that is not necessary to specify all +TYPEs up to the root section as printed by `magit-describe-type', +unless of course your want to be that precise." + ;; When recursing SECTION actually is a type list. Matching + ;; macros also pass such a list instead of a section struct. + (let ((types (if (magit-section-p section) + (mapcar 'car (magit-section-ident section)) + section))) + (when (or types section (magit-current-section)) + (if (listp condition) + (--first (magit-section-match it types) condition) + (magit-section-match-1 (if (symbolp condition) + (list condition) + (append condition nil)) + types))))) + +(defun magit-section-match-1 (l1 l2) + (or (null l1) + (if (eq (car l1) '*) + (or (magit-section-match-1 (cdr l1) l2) + (and l2 + (magit-section-match-1 l1 (cdr l2)))) + (and l2 + (equal (car l1) (car l2)) + (magit-section-match-1 (cdr l1) (cdr l2)))))) + +(defmacro magit-section-when (condition &rest body) + "If the section at point matches CONDITION evaluate BODY. + +If the section matches evaluate BODY forms sequentially and +return the value of the last one, or if there are no BODY forms +return the value of the section. If the section does not match +return nil. + +See `magit-section-match' for the forms CONDITION can take." + (declare (indent 1) + (debug (sexp body))) + `(--when-let (magit-current-section) + (when (magit-section-match ',condition + (mapcar 'car (magit-section-ident it))) + ,@(or body '((magit-section-value it)))))) + +(defmacro magit-section-case (&rest clauses) + "Choose among clauses on the type of the section at point. + +Each clause looks like (CONDITION BODY...). The type of the +section is compared against each CONDITION; the BODY forms of the +first match are evaluated sequentially and the value of the last +form is returned. Inside BODY the symbol `it' is bound to the +section at point. If no clause succeeds or if there is no +section at point return nil. + +See `magit-section-match' for the forms CONDITION can take. +Additionally a CONDITION of t is allowed in the final clause, and +matches if no other CONDITION match, even if there is no section +at point." + (declare (indent 0) + (debug (&rest (sexp body)))) + (let ((ident (cl-gensym "id"))) + `(let* ((it (magit-current-section)) + (,ident (and it (mapcar 'car (magit-section-ident it))))) + (cond ,@(mapcar (lambda (clause) + `(,(or (eq (car clause) t) + `(and it (magit-section-match + ',(car clause) ,ident))) + ,@(cdr clause))) + clauses))))) +;;; Create + +(defvar magit-insert-section-hook nil + "Hook run after `magit-insert-section's BODY. +Avoid using this hook and only ever do so if you know +what you are doing and are sure there is no other way.") + +(defmacro magit-insert-section (&rest args) + "Insert a section at point. + +TYPE is the section type, a symbol. Many commands that act on +the current section behave differently depending on that type. +Also if a variable `magit-TYPE-section-map' exists, then use +that as the text-property `keymap' of all text belonging to the +section (but this may be overwritten in subsections). + +Optional VALUE is the value of the section, usually a string +that is required when acting on the section. + +When optional HIDE is non-nil collapse the section body by +default, i.e. when first creating the section, but not when +refreshing the buffer. Else expand it by default. This can be +overwritten using `magit-section-set-visibility-hook'. When a +section is recreated during a refresh, then the visibility of +predecessor is inherited and HIDE is ignored (but the hook is +still honored). + +BODY is any number of forms that actually insert the section's +heading and body. Optional NAME, if specified, has to be a +symbol, which is then bound to the struct of the section being +inserted. + +Before BODY is evaluated the `start' of the section object is set +to the value of `point' and after BODY was evaluated its `end' is +set to the new value of `point'; BODY is responsible for moving +`point' forward. + +If it turns out inside BODY that the section is empty, then +`magit-cancel-section' can be used to abort and remove all traces +of the partially inserted section. This can happen when creating +a section by washing Git's output and Git didn't actually output +anything this time around. + +\(fn [NAME] (TYPE &optional VALUE HIDE) &rest BODY)" + (declare (indent defun) + (debug ([&optional symbolp] (symbolp &optional form form) body))) + (let ((s (if (symbolp (car args)) + (pop args) + (cl-gensym "section")))) + `(let* ((,s (make-magit-section + :type ',(nth 0 (car args)) + :value ,(nth 1 (car args)) + :start (point-marker) + :parent magit-insert-section--parent))) + (setf (magit-section-hidden ,s) + (-if-let (value (run-hook-with-args-until-success + 'magit-section-set-visibility-hook ,s)) + (eq value 'hide) + (--if-let (and magit-insert-section--oldroot + (magit-get-section + (magit-section-ident ,s) + magit-insert-section--oldroot)) + (magit-section-hidden it) + ,(nth 2 (car args))))) + (let ((magit-insert-section--current ,s) + (magit-insert-section--parent ,s) + (magit-insert-section--oldroot + (or magit-insert-section--oldroot + (unless magit-insert-section--parent + (prog1 magit-root-section + (setq magit-root-section ,s)))))) + (catch 'cancel-section + ,@(cdr args) + (run-hooks 'magit-insert-section-hook) + (magit-insert-child-count ,s) + (set-marker-insertion-type (magit-section-start ,s) t) + (let* ((end (setf (magit-section-end ,s) (point-marker))) + (map (intern (format "magit-%s-section-map" + (magit-section-type ,s)))) + (map (and (boundp map) (symbol-value map)))) + (save-excursion + (goto-char (magit-section-start ,s)) + (while (< (point) end) + (let ((next (or (next-single-property-change + (point) 'magit-section) + end))) + (unless (get-text-property (point) 'magit-section) + (put-text-property (point) next 'magit-section ,s) + (when map + (put-text-property (point) next 'keymap map))) + (goto-char next))))) + (if (eq ,s magit-root-section) + (magit-section-show ,s) + (setf (magit-section-children (magit-section-parent ,s)) + (nconc (magit-section-children (magit-section-parent ,s)) + (list ,s))))) + ,s)))) + +(defun magit-cancel-section () + (when magit-insert-section--current + (if (not (magit-section-parent magit-insert-section--current)) + (insert "(empty)\n") + (delete-region (magit-section-start magit-insert-section--current) + (point)) + (setq magit-insert-section--current nil) + (throw 'cancel-section nil)))) + +(defun magit-insert-heading (&rest args) + "Insert the heading for the section currently being inserted. + +This function should only be used inside `magit-insert-section'. + +When called without any arguments, then just set the `content' +slot of the object representing the section being inserted to +a marker at `point'. The section should only contain a single +line when this function is used like this. + +When called with arguments ARGS, which have to be strings, then +insert those strings at point. The section should not contain +any text before this happens and afterwards it should again only +contain a single line. If the `face' property is set anywhere +inside any of these strings, then insert all of them unchanged. +Otherwise use the `magit-section-heading' face for all inserted +text. + +The `content' property of the section struct is the end of the +heading (which lasts from `start' to `content') and the beginning +of the the body (which lasts from `content' to `end'). If the +value of `content' is nil, then the section has no heading and +its body cannot be collapsed. If a section does have a heading +then its height must be exactly one line, including a trailing +newline character. This isn't enforced, you are responsible for +getting it right. The only exception is that this function does +insert a newline character if necessary." + (declare (indent defun)) + (when args + (let ((heading (apply #'concat args))) + (insert (if (next-single-property-change 0 'face (concat "0" heading)) + heading + (propertize heading 'face 'magit-section-heading))))) + (unless (bolp) + (insert ?\n)) + (setf (magit-section-content magit-insert-section--current) (point-marker))) + +(defvar magit-insert-headers-hook nil "For internal use only.") + +(defun magit-insert-headers (hooks) + (let ((magit-insert-section-hook + (cons 'magit-insert-remaining-headers + (if (listp magit-insert-section-hook) + magit-insert-section-hook + (list magit-insert-section-hook)))) + (magit-insert-headers-hook hooks) + wrapper) + (while (and (setq wrapper (pop magit-insert-headers-hook)) + (= (point) (point-min))) + (funcall wrapper)))) + +(defun magit-insert-remaining-headers () + (if (= (point) (point-min)) + (magit-cancel-section) + (magit-insert-heading) + (remove-hook 'magit-insert-section-hook 'magit-insert-remaining-headers) + (mapc #'funcall magit-insert-headers-hook) + (insert "\n"))) + +(defun magit-insert-child-count (section) + "Modify SECTION's heading to contain number of child sections. + +If `magit-section-show-child-count' is non-nil and the SECTION +has children and its heading ends with \":\", then replace that +with \" (N)\", where N is the number of child sections. + +This function is called by `magit-insert-section' after that has +evaluated its BODY. Admittedly that's a bit of a hack." + ;; This has to be fast, not pretty! + (let (content count) + (when (and magit-section-show-child-count + (setq count (length (magit-section-children section))) + (> count 0) + (setq content (magit-section-content section)) + (eq (char-before (1- content)) ?:)) + (save-excursion + (goto-char (- content 2)) + (insert (format " (%s)" count)) + (delete-char 1))))) + +;;; Update + +(defvar-local magit-section-highlight-overlays nil) +(defvar-local magit-section-highlighted-section nil) +(defvar-local magit-section-highlighted-sections nil) +(defvar-local magit-section-unhighlight-sections nil) + +(defun magit-section-update-region (_) + ;; Don't show complete region. Highlighting emphasizes headings. + (magit-region-sections)) + +(defun magit-section-update-highlight () + (let ((section (magit-current-section))) + (unless (eq section magit-section-highlighted-section) + (let ((inhibit-read-only t) + (deactivate-mark nil) + (selection (magit-region-sections))) + (mapc #'delete-overlay magit-section-highlight-overlays) + (setq magit-section-unhighlight-sections + magit-section-highlighted-sections + magit-section-highlighted-sections nil) + (unless (eq section magit-root-section) + (run-hook-with-args-until-success + 'magit-section-highlight-hook section selection)) + (--each magit-section-unhighlight-sections + (run-hook-with-args-until-success + 'magit-section-unhighlight-hook it selection)) + (restore-buffer-modified-p nil) + (unless (eq magit-section-highlighted-section section) + (setq magit-section-highlighted-section + (unless (magit-section-hidden section) section)))) + (setq deactivate-mark nil)))) + +(defun magit-section-highlight (section selection) + "Highlight SECTION and if non-nil all SELECTION. +This function works for any section but produces undesirable +effects for diff related sections, which by default are +highlighted using `magit-diff-highlight'. Return t." + (cond (selection + (magit-section-make-overlay (magit-section-start (car selection)) + (magit-section-end (car (last selection))) + 'magit-section-highlight) + (magit-section-highlight-selection nil selection)) + (t + (magit-section-make-overlay (magit-section-start section) + (magit-section-end section) + 'magit-section-highlight))) + t) + +(defun magit-section-highlight-selection (_ selection) + "Highlight the section selection region. +If SELECTION is non-nil then it is a list of sections selected by +the region. The headings of these sections are then highlighted. + +This is a fallback for people who don't want to highlight the +current section and therefore removed `magit-section-highlight' +from `magit-section-highlight-hook'. + +This function is necessary to ensure that a representation of +such a region is visible. If neither of these functions were +part of the hook variable, then such a region would be +invisible." + (when selection + (--each selection + (magit-section-make-overlay (magit-section-start it) + (or (magit-section-content it) + (magit-section-end it)) + 'magit-section-heading-selection)) + t)) + +(defun magit-section-make-overlay (start end face) + ;; Yes, this doesn't belong here. But the alternative of + ;; spreading this hack across the code base is even worse. + (when (and magit-keep-region-overlay + (memq face '(magit-section-heading-selection + magit-diff-file-heading-selection + magit-diff-hunk-heading-selection))) + (setq face (list :foreground (face-foreground face)))) + (let ((ov (make-overlay start end nil t))) + (overlay-put ov 'face face) + (overlay-put ov 'evaporate t) + (push ov magit-section-highlight-overlays) + ov)) + +(defun magit-section-goto-successor (section line char arg) + (let ((ident (magit-section-ident section))) + (--if-let (magit-get-section ident) + (let ((start (magit-section-start it))) + (goto-char start) + (unless (eq it magit-root-section) + (ignore-errors + (forward-line line) + (forward-char char)) + (unless (eq (magit-current-section) it) + (goto-char start)))) + (or (and (eq (magit-section-type section) 'hunk) + (-when-let (parent (magit-get-section + (magit-section-ident + (magit-section-parent section)))) + (let* ((children (magit-section-children parent)) + (siblings (magit-section-siblings section 'prev)) + (previous (nth (length siblings) children))) + (if (not arg) + (--when-let (or previous (car (last children))) + (goto-char (magit-section-start it))) + (when previous + (goto-char (magit-section-start previous))) + (if (and (stringp arg) + (re-search-forward + arg (magit-section-end parent) t)) + (goto-char (match-beginning 0)) + (goto-char (magit-section-end (car (last children)))) + (forward-line -1) + (while (looking-at "^ ") (forward-line -1)) + (while (looking-at "^[-+]") (forward-line -1)) + (forward-line)))))) + (goto-char (--if-let (magit-section-goto-successor-1 section) + (if (eq (magit-section-type it) 'button) + (point-min) + (magit-section-start it)) + (point-min))))))) + +(defun magit-section-goto-successor-1 (section) + (or (--when-let (pcase (magit-section-type section) + (`staged 'unstaged) + (`unstaged 'staged) + (`unpushed 'unpulled) + (`unpulled 'unpushed)) + (magit-get-section `((,it) (status)))) + (--when-let (car (magit-section-siblings section 'next)) + (magit-get-section (magit-section-ident it))) + (--when-let (car (magit-section-siblings section 'prev)) + (magit-get-section (magit-section-ident it))) + (--when-let (magit-section-parent section) + (or (magit-get-section (magit-section-ident it)) + (magit-section-goto-successor-1 it))))) + +;;; Visibility + +(defvar-local magit-section-visibility-cache nil) +(put 'magit-section-visibility-cache 'permanent-local t) + +(defun magit-section-set-visibility-from-cache (section) + "Set SECTION's visibility to the cached value. +Currently the cache can only be used to remember that a section's +body should be collapsed, not that it should be expanded. Return +either `hide' or nil." + (and (member (magit-section-visibility-ident section) + magit-section-visibility-cache) + 'hide)) + +(cl-defun magit-section-cache-visibility + (&optional (section magit-insert-section--current)) + (let ((ident (magit-section-visibility-ident section))) + (if (magit-section-hidden section) + (cl-pushnew ident magit-section-visibility-cache :test #'equal) + (setq magit-section-visibility-cache + (delete ident magit-section-visibility-cache))))) + +(defun magit-section-update-visibility-cache (section) + (setq magit-section-visibility-cache + (delete (magit-section-visibility-ident section) + magit-section-visibility-cache))) + +(defun magit-section-visibility-ident (section) + (let ((type (magit-section-type section)) + (value (magit-section-value section))) + (cons type + (cond ((not (memq type '(unpulled unpushed))) value) + ((string-match-p "@{upstream}" value) value) + ;; Unfortunately Git chokes on "@{push}" when the + ;; value of `push.default' does not allow a 1:1 + ;; mapping. But collapsed logs of unpushed and + ;; unpulled commits in the status buffer should + ;; remain invisible after changing branches. + ;; So we have to pretend the value is constant. + ((string-match-p "\\`\\.\\." value) "..@{push}") + (t "@{push}.."))))) + +;;; Utilities + +(cl-defun magit-section-selected-p (section &optional (selection nil sselection)) + (and (not (eq section magit-root-section)) + (or (eq section (magit-current-section)) + (memq section (if sselection + selection + (setq selection (magit-region-sections)))) + (--when-let (magit-section-parent section) + (magit-section-selected-p it selection))))) + +(defun magit-section-parent-value (section) + (setq section (magit-section-parent section)) + (when section (magit-section-value section))) + +(defun magit-section-siblings (section &optional direction) + "Return a list of the sibling sections of SECTION. + +If optional DIRECTION is `prev' then return siblings that come +before SECTION, if it is `next' then return siblings that come +after SECTION. For all other values return all siblings +excluding SECTION itself." + (-when-let (parent (magit-section-parent section)) + (let ((siblings (magit-section-children parent))) + (pcase direction + (`prev (cdr (member section (reverse siblings)))) + (`next (cdr (member section siblings))) + (_ (remq section siblings)))))) + +(defun magit-region-values (&rest types) + "Return a list of the values of the selected sections. + +Also see `magit-region-sections' whose doc-string explains when a +region is a valid section selection. If the region is not active +or is not a valid section selection, then return nil. If optional +TYPES is non-nil then the selection not only has to be valid; the +types of all selected sections additionally have to match one of +TYPES, or nil is returned." + (mapcar 'magit-section-value (apply 'magit-region-sections types))) + +(defun magit-region-sections (&rest types) + "Return a list of the selected sections. + +When the region is active and constitutes a valid section +selection, then return a list of all selected sections. This is +the case when the region begins in the heading of a section and +ends in the heading of a sibling of that first section. When +the selection is not valid then return nil. Most commands that +can act on the selected sections, then instead just act on the +current section, the one point is in. + +When the region looks like it would in any other buffer then +the selection is invalid. When the selection is valid then the +region uses the `magit-section-highlight'. This does not apply +to diffs were things get a bit more complicated, but even here +if the region looks like it usually does, then that's not a +valid selection as far as this function is concerned. + +If optional TYPES is non-nil then the selection not only has to +be valid; the types of all selected sections additionally have to +match one of TYPES, or nil is returned." + (when (use-region-p) + (let* ((rbeg (region-beginning)) + (rend (region-end)) + (sbeg (get-text-property rbeg 'magit-section)) + (send (get-text-property rend 'magit-section))) + (unless (memq send (list sbeg magit-root-section nil)) + (let ((siblings (magit-section-siblings sbeg 'next)) sections) + (when (and (memq send siblings) + (magit-section-position-in-heading-p sbeg rbeg) + (magit-section-position-in-heading-p send rend)) + (while siblings + (push (car siblings) sections) + (when (eq (pop siblings) send) + (setq siblings nil))) + (setq sections (cons sbeg (nreverse sections))) + (when (or (not types) + (--all-p (memq (magit-section-type it) types) sections)) + sections))))))) + +(defun magit-section-position-in-heading-p (section pos) + "Return t if POSITION is inside the heading of SECTION." + (and (>= pos (magit-section-start section)) + (< pos (or (magit-section-content section) + (magit-section-end section))) + t)) + +(defun magit-section-internal-region-p (&optional section) + "Return t if the region is active and inside SECTION's body. +If optional SECTION is nil, use the current section." + (and (region-active-p) + (or section (setq section (magit-current-section))) + (let ((beg (get-text-property (region-beginning) 'magit-section))) + (and (eq beg (get-text-property (region-end) 'magit-section)) + (eq beg section))) + (not (or (magit-section-position-in-heading-p section (region-beginning)) + (magit-section-position-in-heading-p section (region-end)))) + t)) + +(defun magit-wash-sequence (function) + "Repeatedly call FUNCTION until it returns nil or eob is reached. +FUNCTION has to move point forward or return nil." + (while (and (not (eobp)) (funcall function)))) + +(defun magit-add-section-hook (hook function &optional at append local) + "Add to the value of section hook HOOK the function FUNCTION. + +Add FUNCTION at the beginning of the hook list unless optional +APPEND is non-nil, in which case FUNCTION is added at the end. +If FUNCTION already is a member then move it to the new location. + +If optional AT is non-nil and a member of the hook list, then +add FUNCTION next to that instead. Add before or after AT, or +replace AT with FUNCTION depending on APPEND. If APPEND is the +symbol `replace', then replace AT with FUNCTION. For any other +non-nil value place FUNCTION right after AT. If nil, then place +FUNCTION right before AT. If FUNCTION already is a member of the +list but AT is not, then leave FUNCTION where ever it already is. + +If optional LOCAL is non-nil, then modify the hook's buffer-local +value rather than its global value. This makes the hook local by +copying the default value. That copy is then modified. + +HOOK should be a symbol. If HOOK is void, it is first set to nil. +HOOK's value must not be a single hook function. FUNCTION should +be a function that takes no arguments and inserts one or multiple +sections at point, moving point forward. FUNCTION may choose not +to insert its section(s), when doing so would not make sense. It +should not be abused for other side-effects. To remove FUNCTION +again use `remove-hook'." + (unless (boundp hook) + (error "Cannot add function to undefined hook variable %s" hook)) + (or (default-boundp hook) (set-default hook nil)) + (let ((value (if local + (if (local-variable-p hook) + (symbol-value hook) + (unless (local-variable-if-set-p hook) + (make-local-variable hook)) + (copy-sequence (default-value hook))) + (default-value hook)))) + (if at + (when (setq at (member at value)) + (setq value (delq function value)) + (cond ((eq append 'replace) + (setcar at function)) + (append + (push function (cdr at))) + (t + (push (car at) (cdr at)) + (setcar at function)))) + (setq value (delq function value))) + (unless (member function value) + (setq value (if append + (append value (list function)) + (cons function value)))) + (when (eq append 'replace) + (setq value (delq at value))) + (if local + (set hook value) + (set-default hook value)))) + +;;; magit-section.el ends soon +(provide 'magit-section) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-section.el ends here diff --git a/elpa/magit-20160223.828/magit-sequence.el b/elpa/magit-20160223.828/magit-sequence.el new file mode 100644 index 0000000..a75e715 --- /dev/null +++ b/elpa/magit-20160223.828/magit-sequence.el @@ -0,0 +1,651 @@ +;;; magit-sequence.el --- history manipulation in Magit -*- lexical-binding: t -*- + +;; Copyright (C) 2011-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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: + +;; Support for Git commands that replay commits and help the user make +;; changes along the way. Supports `cherry-pick', `revert', `rebase', +;; `rebase--interactive' and `am'. + +;;; Code: + +(require 'magit) + +;;; Options +;;;; Faces + +(defface magit-sequence-pick + '((t :inherit default)) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-stop + '((((class color) (background light)) :foreground "DarkOliveGreen4") + (((class color) (background dark)) :foreground "DarkSeaGreen2")) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-part + '((((class color) (background light)) :foreground "Goldenrod4") + (((class color) (background dark)) :foreground "LightGoldenrod2")) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-head + '((((class color) (background light)) :foreground "SkyBlue4") + (((class color) (background dark)) :foreground "LightSkyBlue1")) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-drop + '((((class color) (background light)) :foreground "IndianRed") + (((class color) (background dark)) :foreground "IndianRed")) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-done + '((t :inherit magit-hash)) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-onto + '((t :inherit magit-sequence-done)) + "Face used in sequence sections." + :group 'magit-faces) + +;;; Common + +;;;###autoload +(defun magit-sequencer-continue () + "Resume the current cherry-pick or revert sequence." + (interactive) + (if (magit-sequencer-in-progress-p) + (if (magit-anything-unstaged-p t) + (user-error "Cannot continue due to unstaged changes") + (magit-run-git-sequencer + (if (magit-revert-in-progress-p) "revert" "cherry-pick") "--continue")) + (user-error "No cherry-pick or revert in progress"))) + +;;;###autoload +(defun magit-sequencer-skip () + "Skip the stopped at commit during a cherry-pick or revert sequence." + (interactive) + (if (magit-sequencer-in-progress-p) + (progn (magit-call-git "reset" "--hard") + (magit-sequencer-continue)) + (user-error "No cherry-pick or revert in progress"))) + +;;;###autoload +(defun magit-sequencer-abort () + "Abort the current cherry-pick or revert sequence. +This discards all changes made since the sequence started." + (interactive) + (if (magit-sequencer-in-progress-p) + (magit-run-git-sequencer + (if (magit-revert-in-progress-p) "revert" "cherry-pick") "--abort") + (user-error "No cherry-pick or revert in progress"))) + +(defun magit-sequencer-in-progress-p () + (or (magit-cherry-pick-in-progress-p) + (magit-revert-in-progress-p))) + +;;; Cherry-Pick + +;;;###autoload (autoload 'magit-cherry-pick-popup "magit-sequence" nil t) +(magit-define-popup magit-cherry-pick-popup + "Popup console for cherry-pick commands." + 'magit-commands + :man-page "git-cherry-pick" + :switches '((?s "Add Signed-off-by lines" "--signoff") + (?e "Edit commit messages" "--edit") + (?x "Reference cherry in commit message" "-x") + (?F "Attempt fast-forward" "--ff") + (?m "Reply merge relative to parent" "--mainline=")) + :options '((?s "Strategy" "--strategy=")) + :actions '((?A "Cherry Pick" magit-cherry-pick) + (?a "Cherry Apply" magit-cherry-apply)) + :sequence-actions '((?A "Continue" magit-sequencer-continue) + (?s "Skip" magit-sequencer-skip) + (?a "Abort" magit-sequencer-abort)) + :sequence-predicate 'magit-sequencer-in-progress-p + :default-arguments '("--ff")) + +(defun magit-cherry-pick-read-args (prompt) + (list (or (nreverse (magit-region-values 'commit)) + (magit-read-other-branch-or-commit prompt)) + (magit-cherry-pick-arguments))) + +;;;###autoload +(defun magit-cherry-pick (commit &optional args) + "Cherry-pick COMMIT. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then pick all of them, +without prompting." + (interactive (magit-cherry-pick-read-args "Cherry-pick")) + (magit-assert-one-parent (car (if (listp commit) + commit + (split-string commit "\\.\\."))) + "cherry-pick") + (magit-run-git-sequencer "cherry-pick" args commit)) + +;;;###autoload +(defun magit-cherry-apply (commit &optional args) + "Apply the changes in COMMIT but do not commit them. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then apply all of them, +without prompting." + (interactive (magit-cherry-pick-read-args "Apply changes from commit")) + (magit-assert-one-parent commit "cherry-pick") + (magit-run-git-sequencer "cherry-pick" "--no-commit" + (remove "--ff" args) commit)) + +(defun magit-cherry-pick-in-progress-p () + ;; .git/sequencer/todo does not exist when there is only one commit left. + (file-exists-p (magit-git-dir "CHERRY_PICK_HEAD"))) + +;;; Revert + +;;;###autoload (autoload 'magit-revert-popup "magit-sequence" nil t) +(magit-define-popup magit-revert-popup + "Popup console for revert commands." + 'magit-commands + :man-page "git-revert" + :switches '((?s "Add Signed-off-by lines" "--signoff")) + :options '((?s "Strategy" "--strategy=")) + :actions '((?V "Revert commit(s)" magit-revert) + (?v "Revert changes" magit-revert-no-commit)) + :sequence-actions '((?V "Continue" magit-sequencer-continue) + (?s "Skip" magit-sequencer-skip) + (?a "Abort" magit-sequencer-abort)) + :sequence-predicate 'magit-sequencer-in-progress-p) + +(defun magit-revert-read-args (prompt) + (list (or (magit-region-values 'commit) + (magit-read-branch-or-commit prompt)) + (magit-revert-arguments))) + +;;;###autoload +(defun magit-revert (commit &optional args) + "Revert COMMIT by creating a new commit. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then revert all of them, +without prompting." + (interactive (magit-revert-read-args "Revert commit")) + (magit-assert-one-parent commit "revert") + (magit-run-git-sequencer "revert" args commit)) + +;;;###autoload +(defun magit-revert-no-commit (commit &optional args) + "Revert COMMIT by applying it in reverse to the worktree. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then revert all of them, +without prompting." + (interactive (magit-revert-read-args "Revert changes")) + (magit-assert-one-parent commit "revert") + (magit-run-git-sequencer "revert" "--no-commit" args commit)) + +(defun magit-revert-in-progress-p () + ;; .git/sequencer/todo does not exist when there is only one commit left. + (file-exists-p (magit-git-dir "REVERT_HEAD"))) + +;;; Patch + +;;;###autoload (autoload 'magit-am-popup "magit-sequence" nil t) +(magit-define-popup magit-am-popup + "Popup console for mailbox commands." + 'magit-commands + :man-page "git-am" + :switches '((?3 "Fall back on 3way merge" "--3way") + (?s "Add Signed-off-by lines" "--signoff") + (?c "Remove text before scissors line" "--scissors") + (?k "Inhibit removal of email cruft" "--keep") + (?b "Limit removal of email cruft" "--keep-non-patch") + (?d "Use author date as committer date" + "--committer-date-is-author-date") + (?D "Use committer date as author date" "--ignore-date")) + :options '((?p "Remove leading slashes from paths" "-p" + magit-popup-read-number)) + :actions '((?w "Apply patches" magit-am-apply-patches) + (?m "Apply maildir" magit-am-apply-maildir)) + :default-arguments '("--3way") + :default-actions 'magit-am-apply-patches + :sequence-actions '((?w "Continue" magit-am-continue) + (?s "Skip" magit-am-skip) + (?a "Abort" magit-am-abort)) + :sequence-predicate 'magit-am-in-progress-p) + +;;;###autoload +(defun magit-am-apply-patches (&optional files args) + "Apply the patches FILES." + (interactive (list (or (magit-region-values 'file) + (list (let ((default (magit-file-at-point))) + (read-file-name + (if default + (format "Apply patch (%s): " default) + "Apply patch: ") + nil default)))) + (magit-am-arguments))) + (magit-run-git-sequencer "am" args "--" (mapcar 'expand-file-name files))) + +;;;###autoload +(defun magit-am-apply-maildir (&optional maildir args) + "Apply the patches from MAILDIR." + (interactive (list (read-file-name "Apply mbox or Maildir: ") + (magit-am-arguments))) + (magit-run-git-sequencer "am" args (expand-file-name maildir))) + +;;;###autoload +(defun magit-am-continue () + "Resume the current patch applying sequence." + (interactive) + (if (magit-am-in-progress-p) + (if (magit-anything-unstaged-p t) + (error "Cannot continue due to unstaged changes") + (magit-run-git-sequencer "am" "--continue")) + (user-error "Not applying any patches"))) + +;;;###autoload +(defun magit-am-skip () + "Skip the stopped at patch during a patch applying sequence." + (interactive) + (if (magit-am-in-progress-p) + (magit-run-git-sequencer "am" "--skip") + (user-error "Not applying any patches"))) + +;;;###autoload +(defun magit-am-abort () + "Abort the current patch applying sequence. +This discards all changes made since the sequence started." + (interactive) + (if (magit-am-in-progress-p) + (magit-run-git "am" "--abort") + (user-error "Not applying any patches"))) + +(defun magit-am-in-progress-p () + (file-exists-p (magit-git-dir "rebase-apply/applying"))) + +;;; Rebase + +;;;###autoload (autoload 'magit-rebase-popup "magit-sequence" nil t) +(magit-define-popup magit-rebase-popup + "Key menu for rebasing." + 'magit-commands + :man-page "git-rebase" + :switches '((?k "Keep empty commits" "--keep-empty") + (?p "Preserve merges" "--preserve-merges") + (?c "Lie about author date" "--committer-date-is-author-date") + (?a "Autosquash" "--autosquash") + (?A "Autostash" "--autostash") + (?i "Interactive" "--interactive")) + :actions '((lambda () + (concat (propertize "Rebase " 'face 'magit-popup-heading) + (propertize (or (magit-get-current-branch) "HEAD") + 'face 'magit-branch-local) + (propertize " onto" 'face 'magit-popup-heading))) + (?p (lambda () + (--when-let (magit-get-push-branch) (concat it "\n"))) + magit-rebase-onto-pushremote) + (?u (lambda () + (--when-let (magit-get-upstream-branch) (concat it "\n"))) + magit-rebase-onto-upstream) + (?e "elsewhere" magit-rebase) + "Rebase" + (?i "interactively" magit-rebase-interactive) + (?m "to edit a commit" magit-rebase-edit-commit) + (?s "subset" magit-rebase-subset) + (?w "to reword a commit" magit-rebase-reword-commit) nil + (?f "to autosquash" magit-rebase-autosquash)) + :sequence-actions '((?r "Continue" magit-rebase-continue) + (?s "Skip" magit-rebase-skip) + (?e "Edit" magit-rebase-edit) + (?a "Abort" magit-rebase-abort)) + :sequence-predicate 'magit-rebase-in-progress-p + :max-action-columns 2) + +(defun magit-git-rebase (target args) + (magit-run-git-sequencer "rebase" target args)) + +;;;###autoload +(defun magit-rebase-onto-pushremote (args) + "Rebase the current branch onto `branch..pushRemote'. +If that variable is unset, then rebase onto `remote.pushDefault'." + (interactive (list (magit-rebase-arguments))) + (--if-let (magit-get-current-branch) + (-if-let (remote (magit-get-push-remote it)) + (if (member remote (magit-list-remotes)) + (magit-git-rebase (concat remote "/" it) args) + (user-error "Remote `%s' doesn't exist" remote)) + (user-error "No push-remote is configured for %s" it)) + (user-error "No branch is checked out"))) + +;;;###autoload +(defun magit-rebase-onto-upstream (args) + "Rebase the current branch onto its upstream branch." + (interactive (list (magit-rebase-arguments))) + (--if-let (magit-get-current-branch) + (-if-let (target (magit-get-upstream-branch it)) + (magit-git-rebase target args) + (user-error "No upstream is configured for %s" it)) + (user-error "No branch is checked out"))) + +;;;###autoload +(defun magit-rebase (target args) + "Rebase the current branch onto a branch read in the minibuffer. +All commits that are reachable from head but not from the +selected branch TARGET are being rebased." + (interactive (list (magit-read-other-branch-or-commit "Rebase onto") + (magit-rebase-arguments))) + (message "Rebasing...") + (magit-git-rebase target args) + (message "Rebasing...done")) + +;;;###autoload +(defun magit-rebase-subset (newbase start args) + "Rebase a subset of the current branches history onto a new base. +Rebase commits from START to `HEAD' onto NEWBASE. +START has to be selected from a list of recent commits." + (interactive (list (magit-read-other-branch-or-commit + "Rebase subset onto" nil + (magit-get-upstream-branch)) + nil + (magit-rebase-arguments))) + (if start + (progn (message "Rebasing...") + (magit-run-git-sequencer "rebase" "--onto" newbase start args) + (message "Rebasing...done")) + (magit-log-select + `(lambda (commit) + (magit-rebase-subset ,newbase (concat commit "^") (list ,@args))) + (concat "Type %p on a commit to rebase it " + "and commits above it onto " newbase ",")))) + +(defun magit-rebase-interactive-1 (commit args message &optional editor) + (declare (indent 2)) + (when commit + (if (eq commit :merge-base) + (setq commit (--if-let (magit-get-upstream-branch) + (magit-git-string "merge-base" it "HEAD") + nil)) + (when (magit-git-failure "merge-base" "--is-ancestor" commit "HEAD") + (user-error "%s isn't an ancestor of HEAD" commit)) + (if (magit-commit-parents commit) + (setq commit (concat commit "^")) + (setq args (cons "--root" args))))) + (when (and commit + (magit-git-lines "rev-list" "--merges" (concat commit "..HEAD"))) + (magit-read-char-case "Proceed despite merge in rebase range? " nil + (?c "[c]ontinue") + (?s "[s]elect other" (setq commit nil)) + (?a "[a]bort" (user-error "Quit")))) + (if commit + (let ((process-environment process-environment)) + (when editor + (push (concat "GIT_SEQUENCE_EDITOR=" editor) process-environment)) + (magit-run-git-sequencer "rebase" "-i" args + (unless (member "--root" args) commit))) + (magit-log-select + `(lambda (commit) + (magit-rebase-interactive-1 commit (list ,@args) ,message ,editor)) + message))) + +;;;###autoload +(defun magit-rebase-interactive (commit args) + "Start an interactive rebase sequence." + (interactive (list (magit-commit-at-point) + (magit-rebase-arguments))) + (magit-rebase-interactive-1 commit args + "Type %p on a commit to rebase it and all commits above it,")) + +;;;###autoload +(defun magit-rebase-autosquash (args) + "Combine squash and fixup commits with their intended targets." + (interactive (list (magit-rebase-arguments))) + (magit-rebase-interactive-1 :merge-base (cons "--autosquash" args) + "Type %p on a commit to squash into it and then rebase as necessary," + "true")) + +;;;###autoload +(defun magit-rebase-edit-commit (commit args) + "Edit a single older commit using rebase." + (interactive (list (magit-commit-at-point) + (magit-rebase-arguments))) + (magit-rebase-interactive-1 commit args + "Type %p on a commit to edit it," + "perl -i -p -e '++$x if not $x and s/^pick/edit/'")) + +;;;###autoload +(defun magit-rebase-reword-commit (commit args) + "Reword a single older commit using rebase." + (interactive (list (magit-commit-at-point) + (magit-rebase-arguments))) + (magit-rebase-interactive-1 commit args + "Type %p on a commit to reword its message," + "perl -i -p -e '++$x if not $x and s/^pick/reword/'")) + +;;;###autoload +(defun magit-rebase-continue () + "Restart the current rebasing operation." + (interactive) + (if (magit-rebase-in-progress-p) + (if (magit-anything-unstaged-p t) + (user-error "Cannot continue rebase with unstaged changes") + (magit-run-git-sequencer "rebase" "--continue")) + (user-error "No rebase in progress"))) + +;;;###autoload +(defun magit-rebase-skip () + "Skip the current commit and restart the current rebase operation." + (interactive) + (if (magit-rebase-in-progress-p) + (magit-run-git-sequencer "rebase" "--skip") + (user-error "No rebase in progress"))) + +;;;###autoload +(defun magit-rebase-edit () + "Edit the todo list of the current rebase operation." + (interactive) + (if (magit-rebase-in-progress-p) + (magit-run-git-sequencer "rebase" "--edit-todo") + (user-error "No rebase in progress"))) + +;;;###autoload +(defun magit-rebase-abort () + "Abort the current rebase operation, restoring the original branch." + (interactive) + (if (magit-rebase-in-progress-p) + (magit-run-git "rebase" "--abort") + (user-error "No rebase in progress"))) + +(defun magit-rebase-in-progress-p () + "Return t if a rebase is in progress." + (or (file-exists-p (magit-git-dir "rebase-merge")) + (file-exists-p (magit-git-dir "rebase-apply/onto")))) + +;;; Sections + +(defun magit-insert-sequencer-sequence () + "Insert section for the on-going cherry-pick or revert sequence. +If no such sequence is in progress, do nothing." + (let ((picking (magit-cherry-pick-in-progress-p))) + (when (or picking (magit-revert-in-progress-p)) + (magit-insert-section (sequence) + (magit-insert-heading (if picking "Cherry Picking" "Reverting")) + (-when-let (lines (cdr (magit-file-lines (magit-git-dir "sequencer/todo")))) + (dolist (line (nreverse lines)) + (when (string-match "^\\(pick\\|revert\\) \\([^ ]+\\) \\(.*\\)$" line) + (magit-bind-match-strings (cmd hash msg) line + (magit-insert-section (commit hash) + (insert (propertize cmd 'face 'magit-sequence-pick) + " " (propertize hash 'face 'magit-hash) + " " msg "\n")))))) + (magit-sequence-insert-sequence + (magit-file-line (magit-git-dir (if picking + "CHERRY_PICK_HEAD" + "REVERT_HEAD"))) + (magit-file-line (magit-git-dir "sequencer/head"))) + (insert "\n"))))) + +(defun magit-insert-am-sequence () + "Insert section for the on-going patch applying sequence. +If no such sequence is in progress, do nothing." + (when (magit-am-in-progress-p) + (magit-insert-section (rebase-sequence) + (magit-insert-heading "Applying patches") + (let ((patches (nreverse (magit-rebase-patches))) + patch commit) + (while patches + (setq patch (pop patches) + commit (magit-rev-verify-commit + (cadr (split-string (magit-file-line patch))))) + (cond ((and commit patches) + (magit-sequence-insert-commit + "pick" commit 'magit-sequence-pick)) + (patches + (magit-sequence-insert-am-patch + "pick" patch 'magit-sequence-pick)) + (commit + (magit-sequence-insert-sequence commit "ORIG_HEAD")) + (t + (magit-sequence-insert-am-patch + "stop" patch 'magit-sequence-stop) + (magit-sequence-insert-sequence nil "ORIG_HEAD"))))) + (insert ?\n)))) + +(defun magit-sequence-insert-am-patch (type patch face) + (magit-insert-section (file patch) + (insert (propertize type 'face face) + ?\s (propertize (file-name-nondirectory patch) 'face 'magit-hash) + ?\n))) + +(defun magit-insert-rebase-sequence () + "Insert section for the on-going rebase sequence. +If no such sequence is in progress, do nothing." + (when (magit-rebase-in-progress-p) + (let* ((interactive (file-directory-p (magit-git-dir "rebase-merge"))) + (dir (if interactive "rebase-merge/" "rebase-apply/")) + (name (-> (concat dir "head-name") magit-git-dir magit-file-line)) + (onto (-> (concat dir "onto") magit-git-dir magit-file-line)) + (onto (or (magit-rev-name onto name) + (magit-rev-name onto "refs/heads/*") onto)) + (name (or (magit-rev-name name "refs/heads/*") name))) + (magit-insert-section (rebase-sequence) + (magit-insert-heading (format "Rebasing %s onto %s" name onto)) + (if interactive + (magit-rebase-insert-merge-sequence) + (magit-rebase-insert-apply-sequence)) + (magit-sequence-insert-sequence + (magit-file-line + (magit-git-dir + (concat dir (if interactive "stopped-sha" "original-commit")))) + onto (--map (cadr (split-string it)) + (magit-file-lines (magit-git-dir "rebase-merge/done")))) + (insert ?\n))))) + +(defun magit-rebase-insert-merge-sequence () + (dolist (line (nreverse + (magit-file-lines + (magit-git-dir "rebase-merge/git-rebase-todo")))) + (when (string-match "^\\([^# ]+\\) \\([^ ]+\\) .*$" line) + (magit-bind-match-strings (action hash) line + (magit-sequence-insert-commit action hash 'magit-sequence-pick))))) + +(defun magit-rebase-insert-apply-sequence () + (dolist (patch (nreverse (cdr (magit-rebase-patches)))) + (magit-sequence-insert-commit + "pick" (cadr (split-string (magit-file-line patch))) 'magit-sequence-pick))) + +(defun magit-rebase-patches () + (directory-files (magit-git-dir "rebase-apply") t "^[0-9]\\{4\\}$")) + +(defun magit-sequence-insert-sequence (stop onto &optional orig) + (let ((head (magit-rev-parse "HEAD")) done) + (setq onto (if onto (magit-rev-parse onto) head)) + (setq done (magit-git-lines "log" "--format=%H" (concat onto "..HEAD"))) + (when (and stop (not (member stop done))) + (let ((id (magit-patch-id stop))) + (--if-let (--first (equal (magit-patch-id it) id) done) + (setq stop it) + (cond + ((--first (magit-rev-equal it stop) done) + ;; The commit's testament has been executed. + (magit-sequence-insert-commit "void" stop 'magit-sequence-drop)) + ;; The faith of the commit is still undecided... + ((magit-anything-unmerged-p) + ;; ...and time travel isn't for the faint of heart. + (magit-sequence-insert-commit "join" stop 'magit-sequence-part)) + ((magit-anything-modified-p t) + ;; ...and the dust hasn't settled yet... + (magit-sequence-insert-commit + (let ((staged (magit-commit-tree "oO" nil "HEAD")) + (unstaged (magit-commit-worktree "oO" "--reset"))) + (cond + ;; ...but we could end up at the same tree just by committing. + ((or (magit-rev-equal staged stop) + (magit-rev-equal unstaged stop)) "goal") + ;; ...but the changes are still there, untainted. + ((or (equal (magit-patch-id staged) id) + (equal (magit-patch-id unstaged) id)) "same") + ;; ...and some changes are gone and/or others were added. + (t "work"))) + stop 'magit-sequence-part)) + ;; The commit is definitely gone... + ((--first (magit-rev-equal it stop) done) + ;; ...but all of its changes are still in effect. + (magit-sequence-insert-commit "poof" stop 'magit-sequence-drop)) + (t + ;; ...and some changes are gone and/or other changes were added. + (magit-sequence-insert-commit "gone" stop 'magit-sequence-drop))) + (setq stop nil)))) + (dolist (rev done) + (apply 'magit-sequence-insert-commit + (cond ((equal rev stop) + ;; ...but its reincarnation lives on. + ;; Or it didn't die in the first place. + (list (if (and (equal rev head) + (equal (magit-patch-id (concat stop "^")) + (magit-patch-id (car (last orig 2))))) + "stop" ; We haven't done anything yet. + "same") ; There are new commits. + rev (if (equal rev head) + 'magit-sequence-head + 'magit-sequence-stop))) + ((equal rev head) + (list "done" rev 'magit-sequence-head)) + (t + (list "done" rev 'magit-sequence-done))))) + (magit-sequence-insert-commit "onto" onto + (if (equal onto head) + 'magit-sequence-head + 'magit-sequence-onto)))) + +(defun magit-sequence-insert-commit (type hash face) + (magit-insert-section (commit hash) + (insert (propertize type 'face face) ?\s + (magit-format-rev-summary hash) ?\n))) + +;;; magit-sequence.el ends soon +(provide 'magit-sequence) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-sequence.el ends here diff --git a/elpa/magit-20160223.828/magit-stash.el b/elpa/magit-20160223.828/magit-stash.el new file mode 100644 index 0000000..bc435e8 --- /dev/null +++ b/elpa/magit-20160223.828/magit-stash.el @@ -0,0 +1,400 @@ +;;; magit-stash.el --- stash support for Magit -*- lexical-binding: t -*- + +;; Copyright (C) 2008-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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: + +;; Support for Git stashes. + +;;; Code: + +(require 'magit) + +;;; Commands + +;;;###autoload (autoload 'magit-stash-popup "magit-stash" nil t) +(magit-define-popup magit-stash-popup + "Popup console for stash commands." + 'magit-commands + :man-page "git-stash" + :switches '((?u "Also save untracked files" "--include-untracked") + (?a "Also save untracked and ignored files" "--all")) + :actions '((?z "Save" magit-stash) + (?Z "Snapshot" magit-snapshot) + (?p "Pop" magit-stash-pop) + (?i "Save index" magit-stash-index) + (?I "Snapshot index" magit-snapshot-index) + (?a "Apply" magit-stash-apply) + (?w "Save worktree" magit-stash-worktree) + (?W "Snapshot worktree" magit-snapshot-worktree) + (?l "List" magit-stash-list) + (?x "Save keeping index" magit-stash-keep-index) + (?r "Snapshot to wipref" magit-wip-commit) + (?v "Show" magit-stash-show) + (?b "Branch" magit-stash-branch) + (?k "Drop" magit-stash-drop) nil + (?f "Format patch" magit-stash-format-patch)) + :default-action 'magit-stash + :max-action-columns 3) + +;;;###autoload +(defun magit-stash (message &optional include-untracked) + "Create a stash of the index and working tree. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive (magit-stash-read-args)) + (magit-stash-save message t t include-untracked t)) + +;;;###autoload +(defun magit-stash-index (message) + "Create a stash of the index only. +Unstaged and untracked changes are not stashed. The stashed +changes are applied in reverse to both the index and the +worktree. This command can fail when the worktree is not clean. +Applying the resulting stash has the inverse effect." + (interactive (list (magit-stash-read-message))) + (magit-stash-save message t nil nil t 'worktree)) + +;;;###autoload +(defun magit-stash-worktree (message &optional include-untracked) + "Create a stash of the working tree only. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive (magit-stash-read-args)) + (magit-stash-save message nil t include-untracked t 'index)) + +;;;###autoload +(defun magit-stash-keep-index (message &optional include-untracked) + "Create a stash of the index and working tree, keeping index intact. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive (magit-stash-read-args)) + (magit-stash-save message t t include-untracked t 'index)) + +(defun magit-stash-read-args () + (list (magit-stash-read-message) + (magit-stash-read-untracked))) + +(defun magit-stash-read-untracked () + (let ((prefix (prefix-numeric-value current-prefix-arg)) + (args (magit-stash-arguments))) + (cond ((or (= prefix 16) (member "--all" args)) 'all) + ((or (= prefix 4) (member "--include-untracked" args)) t)))) + +(defun magit-stash-read-message () + (let* ((default (format "On %s: " + (or (magit-get-current-branch) "(no branch)"))) + (input (magit-read-string "Stash message" default))) + (if (equal input default) + (concat default (magit-rev-format "%h %s")) + input))) + +;;;###autoload +(defun magit-snapshot (&optional include-untracked) + "Create a snapshot of the index and working tree. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive (magit-snapshot-read-args)) + (magit-snapshot-save t t include-untracked t)) + +;;;###autoload +(defun magit-snapshot-index () + "Create a snapshot of the index only. +Unstaged and untracked changes are not stashed." + (interactive) + (magit-snapshot-save t nil nil t)) + +;;;###autoload +(defun magit-snapshot-worktree (&optional include-untracked) + "Create a snapshot of the working tree only. +Untracked files are included according to popup arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive (magit-snapshot-read-args)) + (magit-snapshot-save nil t include-untracked t)) + +(defun magit-snapshot-read-args () + (list (magit-stash-read-untracked))) + +(defun magit-snapshot-save (index worktree untracked &optional refresh) + (magit-stash-save (concat "WIP on " (magit-stash-summary)) + index worktree untracked refresh t)) + +;;;###autoload +(defun magit-stash-apply (stash) + "Apply a stash to the working tree. +Try to preserve the stash index. If that fails because there +are staged changes, apply without preserving the stash index." + (interactive (list (magit-read-stash "Apply stash" t))) + (if (= (magit-call-git "stash" "apply" "--index" stash) 0) + (magit-refresh) + (magit-run-git "stash" "apply" stash))) + +(defun magit-stash-pop (stash) + "Apply a stash to the working tree and remove it from stash list. +Try to preserve the stash index. If that fails because there +are staged changes, apply without preserving the stash index +and forgo removing the stash." + (interactive (list (magit-read-stash "Apply pop" t))) + (if (= (magit-call-git "stash" "apply" "--index" stash) 0) + (magit-stash-drop stash) + (magit-run-git "stash" "apply" stash))) + +;;;###autoload +(defun magit-stash-drop (stash) + "Remove a stash from the stash list. +When the region is active offer to drop all contained stashes." + (interactive (list (--if-let (magit-region-values 'stash) + (magit-confirm t nil "Drop %i stashes" it) + (magit-read-stash "Drop stash")))) + (dolist (stash (if (listp stash) + (nreverse (prog1 stash (setq stash (car stash)))) + (list stash))) + (message "Deleted refs/%s (was %s)" stash + (magit-rev-parse "--short" stash)) + (magit-call-git "reflog" "delete" "--updateref" "--rewrite" stash)) + (-when-let (ref (and (string-match "\\(.+\\)@{[0-9]+}$" stash) + (match-string 1 stash))) + (unless (string-match "^refs/" ref) + (setq ref (concat "refs/" ref))) + (unless (magit-rev-verify (concat ref "@{0}")) + (magit-run-git "update-ref" "-d" ref))) + (magit-refresh)) + +;;;###autoload +(defun magit-stash-clear (ref) + "Remove all stashes saved in REF's reflog by deleting REF." + (interactive + (let ((ref (or (magit-section-when 'stashes) "refs/stash"))) + (if (magit-confirm t (format "Drop all stashes in %s" ref)) + (list ref) + (user-error "Abort")))) + (magit-run-git "update-ref" "-d" ref)) + +;;;###autoload +(defun magit-stash-branch (stash branch) + "Create and checkout a new BRANCH from STASH." + (interactive (list (magit-read-stash "Branch stash" t) + (magit-read-string-ns "Branch name"))) + (magit-run-git "stash" "branch" branch stash)) + +;;;###autoload +(defun magit-stash-format-patch (stash) + "Create a patch from STASH" + (interactive (list (magit-read-stash "Create patch from stash" t))) + (with-temp-file (magit-rev-format "0001-%f.patch" stash) + (magit-git-insert "stash" "show" "-p" stash)) + (magit-refresh)) + +;;; Plumbing + +(defun magit-stash-save (message index worktree untracked + &optional refresh keep noerror ref) + (if (or (and index (magit-staged-files t)) + (and worktree (magit-modified-files t)) + (and untracked (magit-untracked-files (eq untracked 'all)))) + (magit-with-toplevel + (magit-stash-store message (or ref "refs/stash") + (magit-stash-create message index worktree untracked)) + (if (eq keep 'worktree) + (with-temp-buffer + (magit-git-insert "diff" "--cached") + (magit-run-git-with-input + "apply" "--reverse" "--cached" "--ignore-space-change" "-") + (magit-run-git-with-input + "apply" "--reverse" "--ignore-space-change" "-")) + (unless (eq keep t) + (if (eq keep 'index) + (magit-call-git "checkout" "--" ".") + (magit-call-git "reset" "--hard" "HEAD")) + (when untracked + (magit-call-git "clean" "-f" (and (eq untracked 'all) "-x"))))) + (when refresh + (magit-refresh))) + (unless noerror + (user-error "No %s changes to save" (cond ((not index) "unstaged") + ((not worktree) "staged") + (t "local")))))) + +(defun magit-stash-store (message ref commit) + (magit-update-ref ref message commit t)) + +(defun magit-stash-create (message index worktree untracked) + (unless (magit-rev-parse "--verify" "HEAD") + (error "You do not have the initial commit yet")) + (let ((magit-git-global-arguments (nconc (list "-c" "commit.gpgsign=false") + magit-git-global-arguments)) + (default-directory (magit-toplevel)) + (summary (magit-stash-summary)) + (head "HEAD")) + (when (and worktree (not index)) + (setq head (magit-commit-tree "pre-stash index" nil "HEAD"))) + (or (setq index (magit-commit-tree (concat "index on " summary) nil head)) + (error "Cannot save the current index state")) + (and untracked + (setq untracked (magit-untracked-files (eq untracked 'all))) + (setq untracked (magit-with-temp-index nil nil + (or (and (magit-update-files untracked) + (magit-commit-tree + (concat "untracked files on " summary))) + (error "Cannot save the untracked files"))))) + (magit-with-temp-index index "-m" + (when worktree + (or (magit-update-files (magit-git-items "diff" "-z" "--name-only" head)) + (error "Cannot save the current worktree state"))) + (or (magit-commit-tree message nil head index untracked) + (error "Cannot save the current worktree state"))))) + +(defun magit-stash-summary () + (concat (or (magit-get-current-branch) "(no branch)") + ": " (magit-rev-format "%h %s"))) + +;;; Sections + +(defvar magit-stashes-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-delete-thing] 'magit-stash-clear) + map) + "Keymap for `stashes' section.") + +(defvar magit-stash-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-visit-thing] 'magit-stash-show) + (define-key map [remap magit-delete-thing] 'magit-stash-drop) + (define-key map "a" 'magit-stash-apply) + (define-key map "A" 'magit-stash-pop) + map) + "Keymap for `stash' sections.") + +(magit-define-section-jumper magit-jump-to-stashes + "Stashes" stashes "refs/stash") + +(cl-defun magit-insert-stashes (&optional (ref "refs/stash") + (heading "Stashes:")) + "Insert `stashes' section showing reflog for \"refs/stash\". +If optional REF is non-nil show reflog for that instead. +If optional HEADING is non-nil use that as section heading +instead of \"Stashes:\"." + (when (magit-rev-verify ref) + (magit-insert-section (stashes ref (not magit-status-expand-stashes)) + (magit-insert-heading heading) + (magit-git-wash (apply-partially 'magit-log-wash-log 'stash) + "reflog" "--format=%gd %at %gs" ref)))) + +;;; List Stashes + +;;;###autoload +(defun magit-stash-list () + "List all stashes in a buffer." + (interactive) + (magit-mode-setup #'magit-stashes-mode "refs/stash")) + +(define-derived-mode magit-stashes-mode magit-reflog-mode "Magit Stashes" + "Mode for looking at lists of stashes." + :group 'magit-log + (hack-dir-local-variables-non-file-buffer)) + +(cl-defun magit-stashes-refresh-buffer (ref) + (magit-insert-section (stashesbuf) + (magit-insert-heading (if (equal ref "refs/stash") + "Stashes:" + (format "Stashes [%s]:" ref))) + (magit-git-wash (apply-partially 'magit-log-wash-log 'stash) + "reflog" "--format=%gd %at %gs" ref))) + +;;; Show Stash + +(defcustom magit-stash-sections-hook + '(magit-insert-stash-worktree + magit-insert-stash-index + magit-insert-stash-untracked) + "Hook run to insert sections into stash buffers." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type 'hook) + +;;;###autoload +(defun magit-stash-show (stash &optional args files) + "Show all diffs of a stash in a buffer." + (interactive (cons (or (and (not current-prefix-arg) + (magit-stash-at-point)) + (magit-read-stash "Show stash")) + (-let [(args files) (magit-diff-arguments)] + (list (delete "--stat" args) files)))) + (magit-mode-setup #'magit-stash-mode stash nil args files)) + +(define-derived-mode magit-stash-mode magit-diff-mode "Magit Stash" + "Mode for looking at individual stashes." + :group 'magit-diff + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-stash-refresh-buffer (stash _const _args _files) + (setq header-line-format + (concat + "\s" (propertize (capitalize stash) 'face 'magit-section-heading) + "\s" (magit-rev-format "%s" stash))) + (magit-insert-section (stash) + (run-hooks 'magit-stash-sections-hook))) + +(defun magit-stash-insert-section (commit range message &optional files) + (magit-insert-section (commit commit) + (magit-insert-heading message) + (magit-git-wash #'magit-diff-wash-diffs + "diff" range "-p" "--no-prefix" + (nth 2 magit-refresh-args) + "--" (or files (nth 3 magit-refresh-args))))) + +(defun magit-insert-stash-index () + "Insert section showing the index commit of the stash." + (let ((stash (car magit-refresh-args))) + (magit-stash-insert-section (format "%s^2" stash) + (format "%s^..%s^2" stash stash) + "Index"))) + +(defun magit-insert-stash-worktree () + "Insert section showing the worktree commit of the stash." + (let ((stash (car magit-refresh-args))) + (magit-stash-insert-section stash + (format "%s^2..%s" stash stash) + "Working tree"))) + +(defun magit-insert-stash-untracked () + "Insert section showing the untracked files commit of the stash." + (let ((stash (car magit-refresh-args)) + (rev (concat (car magit-refresh-args) "^3"))) + (when (magit-rev-verify rev) + (magit-stash-insert-section (format "%s^3" stash) + (format "%s^..%s^3" stash stash) + "Untracked files" + (magit-git-items "ls-tree" "-z" "--name-only" + "--full-tree" rev))))) + +;;; magit-stash.el ends soon +(provide 'magit-stash) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-stash.el ends here diff --git a/elpa/magit-20160223.828/magit-submodule.el b/elpa/magit-20160223.828/magit-submodule.el new file mode 100644 index 0000000..e4c59b3 --- /dev/null +++ b/elpa/magit-20160223.828/magit-submodule.el @@ -0,0 +1,174 @@ +;;; magit-submodule.el --- submodule support for Magit -*- lexical-binding: t -*- + +;; Copyright (C) 2011-2015 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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. + +;;; Code: + +(require 'magit) + +;;; Commands + +;;;###autoload (autoload 'magit-submodule-popup "magit-submodule" nil t) +(magit-define-popup magit-submodule-popup + "Popup console for submodule commands." + 'magit-commands nil nil + :man-page "git-submodule" + :actions '((?a "Add" magit-submodule-add) + (?b "Setup" magit-submodule-setup) + (?i "Init" magit-submodule-init) + (?u "Update" magit-submodule-update) + (?s "Sync" magit-submodule-sync) + (?f "Fetch" magit-submodule-fetch) + (?d "Deinit" magit-submodule-deinit))) + +;;;###autoload +(defun magit-submodule-add (url &optional path) + "Add the repository at URL as a submodule. +Optional PATH is the path to the submodule relative to the root +of the superproject. If it is nil then the path is determined +based on URL." + (interactive + (magit-with-toplevel + (let ((path (read-file-name + "Add submodule: " nil nil nil + (magit-section-when [file untracked] + (directory-file-name (magit-section-value it)))))) + (when path + (setq path (file-name-as-directory (expand-file-name path))) + (when (member path (list "" default-directory)) + (setq path nil))) + (list (magit-read-string-ns + "Remote url" + (and path (magit-git-repo-p path t) + (let ((default-directory path)) + (magit-get "remote" (or (magit-get-remote) "origin") + "url")))) + (and path (directory-file-name (file-relative-name path))))))) + (magit-run-git "submodule" "add" url path)) + +;;;###autoload +(defun magit-submodule-setup () + "Clone and register missing submodules and checkout appropriate commits." + (interactive) + (magit-submodule-update t)) + +;;;###autoload +(defun magit-submodule-init () + "Register submodules listed in \".gitmodules\" into \".git/config\"." + (interactive) + (magit-with-toplevel + (magit-run-git-async "submodule" "init"))) + +;;;###autoload +(defun magit-submodule-update (&optional init) + "Clone missing submodules and checkout appropriate commits. +With a prefix argument also register submodules in \".git/config\"." + (interactive "P") + (magit-with-toplevel + (magit-run-git-async "submodule" "update" (and init "--init")))) + +;;;###autoload +(defun magit-submodule-sync () + "Update each submodule's remote URL according to \".gitmodules\"." + (interactive) + (magit-with-toplevel + (magit-run-git-async "submodule" "sync"))) + +;;;###autoload +(defun magit-submodule-fetch (&optional all) + "Fetch all submodules. +With a prefix argument fetch all remotes." + (interactive "P") + (magit-with-toplevel + (magit-run-git-async "submodule" "foreach" + (format "git fetch %s || true" (if all "--all" ""))))) + +;;;###autoload +(defun magit-submodule-deinit (path) + "Unregister the submodule at PATH." + (interactive + (list (magit-completing-read "Deinit module" (magit-get-submodules) + nil t nil nil (magit-section-when module)))) + (magit-with-toplevel + (magit-run-git-async "submodule" "deinit" path))) + +;;; Sections + +;;;###autoload +(defun magit-insert-submodule-commits (section range) + "For internal use, don't add to a hook." + (if (magit-section-hidden section) + (setf (magit-section-washer section) + (apply-partially #'magit-insert-submodule-commits section range)) + (magit-git-wash (apply-partially 'magit-log-wash-log 'module) + "log" "--oneline" range) + (when (> (point) (magit-section-content section)) + (delete-char -1)))) + +;;;###autoload +(defun magit-insert-unpulled-module-commits () + "Insert sections for all submodules with unpulled commits. +These sections can be expanded to show the respective commits." + (-when-let (modules (magit-get-submodules)) + (magit-insert-section section (unpulled-modules) + (magit-insert-heading "Unpulled modules:") + (magit-with-toplevel + (dolist (module modules) + (let ((default-directory + (expand-file-name (file-name-as-directory module)))) + (-when-let (tracked (magit-get-upstream-ref)) + (magit-insert-section sec (file module t) + (magit-insert-heading + (concat (propertize module 'face 'magit-diff-file-heading) ":")) + (magit-insert-submodule-commits + section (concat "HEAD.." tracked))))))) + (if (> (point) (magit-section-content section)) + (insert ?\n) + (magit-cancel-section))))) + +;;;###autoload +(defun magit-insert-unpushed-module-commits () + "Insert sections for all submodules with unpushed commits. +These sections can be expanded to show the respective commits." + (-when-let (modules (magit-get-submodules)) + (magit-insert-section section (unpushed-modules) + (magit-insert-heading "Unpushed modules:") + (magit-with-toplevel + (dolist (module modules) + (let ((default-directory + (expand-file-name (file-name-as-directory module)))) + (-when-let (tracked (magit-get-upstream-ref)) + (magit-insert-section sec (file module t) + (magit-insert-heading + (concat (propertize module 'face 'magit-diff-file-heading) ":")) + (magit-insert-submodule-commits + section (concat tracked "..HEAD"))))))) + (if (> (point) (magit-section-content section)) + (insert ?\n) + (magit-cancel-section))))) + +;;; magit-submodule.el ends soon +(provide 'magit-submodule) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-submodule.el ends here diff --git a/elpa/magit-20160223.828/magit-utils.el b/elpa/magit-20160223.828/magit-utils.el new file mode 100644 index 0000000..ca65830 --- /dev/null +++ b/elpa/magit-20160223.828/magit-utils.el @@ -0,0 +1,414 @@ +;;; magit-utils.el --- various utilities -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; Contains code from GNU Emacs https://www.gnu.org/software/emacs, +;; released under the GNU General Public License version 3 or later. + +;; 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 library defines several utility functions used by several +;; other libraries which cannot depend on one another (because +;; circular dependencies are not good). Luckily most (all) of these +;; functions have very little (nothing) to do with Git, so we not only +;; have to do this, it even makes sense. + +;; Unfortunately there are also some options which are used by several +;; libraries which cannot depend on one another, they are defined here +;; too. + +;;; Code: + +(require 'cl-lib) +(require 'dash) + +(eval-when-compile (require 'ido)) +(declare-function ido-completing-read+ 'ido-completing-read+) + +(defvar magit-wip-before-change-mode) + +;;; Options + +(defcustom magit-completing-read-function 'magit-builtin-completing-read + "Function to be called when requesting input from the user. + +For Helm users, the simplest way to get Helm completion is to +turn on `helm-mode' and leave this option set to the default +value. However, if you prefer to not use `helm-mode' but still +want Magit to use Helm for completion, you can set this option to +`helm--completing-read-default'." + :group 'magit + :type '(radio (function-item magit-builtin-completing-read) + (function-item magit-ido-completing-read) + (function-item helm--completing-read-default) + (function :tag "Other"))) + +(defcustom magit-no-confirm nil + "A list of symbols for actions Magit should not confirm, or t. + +Many potentially dangerous commands by default ask the user for +confirmation. Each of the below symbols stands for an action +which, when invoked unintentionally or without being fully aware +of the consequences, could lead to tears. In many cases there +are several commands that perform variations of a certain action, +so we don't use the command names but more generic symbols. + +Applying changes: + + `discard' Discarding one or more changes (i.e. hunks or the + complete diff for a file) loses that change, obviously. + + `reverse' Reverting one or more changes can usually be undone + by reverting the reversion. + + `stage-all-changes', `unstage-all-changes' When there are both + staged and unstaged changes, then un-/staging everything would + destroy that distinction. Of course that also applies when + un-/staging a single change, but then less is lost and one does + that so often that having to confirm every time would be + unacceptable. + +Files: + + `delete' When a file that isn't yet tracked by Git is deleted + then it is completely lost, not just the last changes. Very + dangerous. + + `trash' Instead of deleting a file it can also be move to the + system trash. Obviously much less dangerous than deleting it. + + Also see option `magit-delete-by-moving-to-trash'. + + `resurrect' A deleted file can easily be resurrected by + \"deleting\" the deletion, which is done using the same command + that was used to delete the same file in the first place. + + `rename' Renaming a file can easily be undone. + +Sequences: + + `reset-bisect' Aborting (known to Git as \"resetting\") a + bisect operation loses all information collected so far. + + `abort-merge' Aborting a merge throws away all conflict + resolutions which has already been carried out by the user. + + `merge-dirty' Merging with a dirty worktree can make it hard to + go back to the state before the merge was initiated. + +References: + + `delete-unmerged-branch' Once a branch has been deleted it can + only be restored using low-level recovery tools provided by + Git. And even then the reflog is gone. The user always has + to confirm the deletion of a branch by accepting the default + choice (or selecting another branch), but when a branch has + not been merged yet, also make sure the user is aware of that. + + `drop-stashes' Dropping a stash is dangerous because Git stores + stashes in the reflog. Once a stash is removed, there is no + going back without using low-level recovery tools provided by + Git. When a single stash is dropped, then the user always has + to confirm by accepting the default (or selecting another). + This action only concerns the deletion of multiple stashes at + once. + +Various: + + `kill-process' There seldom is a reason to kill a process. + +Global settings: + + Instead of adding all of the above symbols to the value of this + option you can also set it to the atom `t', which has the same + effect as adding all of the above symbols. Doing that most + certainly is a bad idea, especially because other symbols might + be added in the future. So even if you don't want to be asked + for confirmation for any of these actions, you are still better + of adding all of the respective symbols individually. + + When `magit-wip-before-change-mode' is enabled then these actions + can fairly easily be undone: `discard', `reverse', + `stage-all-changes', and `unstage-all-changes'. If and only if + this mode is enabled then `safe-with-wip' has the same effect + as adding all of these symbols individually." + :package-version '(magit . "2.1.0") + :group 'magit + :type '(choice (const :tag "No confirmation needed" t) + (set (const reverse) (const discard) + (const rename) (const resurrect) + (const trash) (const delete) + (const abort-merge) (const merge-dirty) + (const drop-stashes) (const resect-bisect) + (const kill-process) (const delete-unmerged-branch) + (const stage-all-changes) (const unstage-all-changes) + (const safe-with-wip)))) + +(defcustom magit-ellipsis ?… + "Character used to abbreviate text." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'character) + +(defcustom magit-update-other-window-delay 0.2 + "Delay before automatically updating the other window. + +When moving around in certain buffers certain other buffers, +which are being displayed in another window, may optionally be +updated to display information about the section at point. + +When holding down a key to move by more than just one section, +then that would update that buffer for each section on the way. +To prevent that, updating the revision buffer is delayed, and +this option controls for how long. For optimal experience you +might have to adjust this delay and/or the keyboard repeat rate +and delay of your graphical environment or operating system." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type 'number) + +;;; User Input + +(defun magit-completing-read + (prompt collection &optional predicate require-match initial-input hist def) + "Magit wrapper around `completing-read' or an alternative function. + +Option `magit-completing-read-function' can be used to wrap +around another `completing-read'-like function. Unless it +doesn't have the exact same signature, an additional wrapper is +required. Even if it has the same signature it might be a good +idea to wrap it, so that `magit-prompt-with-default' can be used. + +See `completing-read' for the meanings of the arguments, but note +that this wrapper makes the following changes: + +- If REQUIRE-MATCH is nil and the user exits without a choice, + then return nil instead of an empty string. + +- If REQUIRE-MATCH is non-nil and the users exits without a + choice, then raise an user-error. + +- \": \" is appended to PROMPT. + +- If a `magit-completing-read-function' is used which in turn + uses `magit-prompt-with-completion' and DEF is non-nil, then + PROMPT is modified to end with \" (default DEF): \". + +The use of another completing function and/or wrapper obviously +results in additional differences." + (let ((reply (funcall magit-completing-read-function + (concat prompt ": ") collection predicate + require-match initial-input hist def))) + (if (string= reply "") + (if require-match + (user-error "Nothing selected") + nil) + reply))) + +(defun magit-builtin-completing-read + (prompt choices &optional predicate require-match initial-input hist def) + "Magit wrapper for standard `completing-read' function." + (completing-read (magit-prompt-with-default prompt def) + choices predicate require-match + initial-input hist def)) + +(defun magit-ido-completing-read + (prompt choices &optional predicate require-match initial-input hist def) + "Ido-based `completing-read' almost-replacement. + +Unfortunately `ido-completing-read' is not suitable as a +drop-in replacement for `completing-read', instead we use +`ido-completing-read+' from the third-party package by the +same name." + (if (require 'ido-completing-read+ nil t) + (ido-completing-read+ prompt choices predicate require-match + initial-input hist def) + (display-warning 'magit "ido-completing-read+ is not installed + +To use Ido completion with Magit you need to install the +third-party `ido-completing-read+' packages. Falling +back to built-in `completing-read' for now." :error) + (magit-builtin-completing-read prompt choices predicate require-match + initial-input hist def))) + +(defun magit-prompt-with-default (prompt def) + (if (and def (> (length prompt) 2) + (string-equal ": " (substring prompt -2))) + (format "%s (default %s): " (substring prompt 0 -2) def) + prompt)) + +(defvar magit-minibuffer-local-ns-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map minibuffer-local-map) + (define-key map "\s" 'magit-whitespace-disallowed) + (define-key map "\t" 'magit-whitespace-disallowed) + map)) + +(defun magit-whitespace-disallowed () + "Beep to tell the user that whitespace is not allowed." + (interactive) + (ding) + (message "Whitespace isn't allowed here") + (setq defining-kbd-macro nil) + (force-mode-line-update)) + +(defun magit-read-string (prompt &optional initial-input history default-value + inherit-input-method no-whitespace) + "Read a string from the minibuffer, prompting with string PROMPT. + +This is similar to `read-string', but +* empty input is only allowed if DEFAULT-VALUE is non-nil in + which case that is returned, +* whitespace is not allowed if NO-WHITESPACE is non-nil, +* \": \" is appended to PROMPT, and +* an invalid DEFAULT-VALUE is silently ignored." + (when default-value + (when (consp default-value) + (setq default-value (car default-value))) + (unless (stringp default-value) + (setq default-value nil))) + (let* ((minibuffer-completion-table nil) + (val (read-from-minibuffer + (magit-prompt-with-default (concat prompt ": ") default-value) + initial-input (and no-whitespace magit-minibuffer-local-ns-map) + nil history default-value inherit-input-method))) + (when (and (string= val "") default-value) + (setq val default-value)) + (cond ((string= val "") + (user-error "Need non-empty input")) + ((and no-whitespace (string-match-p "[\s\t\n]" val)) + (user-error "Input contains whitespace")) + (t val)))) + +(defun magit-read-string-ns (prompt &optional initial-input history + default-value inherit-input-method) + "Call `magit-read-string' with non-nil NO-WHITESPACE." + (magit-read-string prompt initial-input history default-value + inherit-input-method t)) + +(defmacro magit-read-char-case (prompt verbose &rest clauses) + (declare (indent 2) + (debug (form form &rest (characterp form body)))) + `(pcase (read-char-choice + (concat ,prompt + ,(concat (mapconcat 'cadr clauses ", ") + (and verbose ", or [C-g] to abort") " ")) + ',(mapcar 'car clauses)) + ,@(--map `(,(car it) ,@(cddr it)) clauses))) + +(cl-defun magit-confirm (action &optional prompt prompt-n (items nil sitems)) + (declare (indent defun)) + (setq prompt-n (format (concat (or prompt-n prompt) "? ") (length items)) + prompt (format (concat (or prompt (magit-confirm-make-prompt action)) + "? ") + (car items))) + (cond ((and (not (eq action t)) + (or (eq magit-no-confirm t) + (memq action + `(,@magit-no-confirm + ,@(and magit-wip-before-change-mode + (memq 'safe-with-wip magit-no-confirm) + `(discard reverse + stage-all-changes + unstage-all-changes)))))) + (or (not sitems) items)) + ((not sitems) + (y-or-n-p prompt)) + ((= (length items) 1) + (and (y-or-n-p prompt) items)) + ((> (length items) 1) + (let ((buffer (get-buffer-create " *Magit Confirm*"))) + (with-current-buffer buffer + (with-current-buffer-window + buffer (cons 'display-buffer-below-selected + '((window-height . fit-window-to-buffer))) + (lambda (window _value) + (with-selected-window window + (unwind-protect (and (y-or-n-p prompt-n) items) + (when (window-live-p window) + (quit-restore-window window 'kill))))) + (dolist (item items) + (insert item "\n")))))))) + +(defun magit-confirm-files (action files &optional prompt) + (when files + (unless prompt + (setq prompt (magit-confirm-make-prompt action))) + (magit-confirm action + (concat prompt " %s") + (concat prompt " %i files") + files))) + +(defun magit-confirm-make-prompt (action) + (let ((prompt (symbol-name action))) + (replace-regexp-in-string + "-" " " (concat (upcase (substring prompt 0 1)) (substring prompt 1))))) + +;;; Text Utilities + +(defmacro magit-bind-match-strings (varlist string &rest body) + "Bind variables to submatches according to VARLIST then evaluate BODY. +Bind the symbols in VARLIST to submatches of the current match +data, starting with 1 and incrementing by 1 for each symbol. If +the last match was against a string then that has to be provided +as STRING." + (declare (indent 2) (debug (listp form body))) + (let ((s (cl-gensym "string")) + (i 0)) + `(let ((,s ,string)) + (let ,(save-match-data + (--map (list it (list 'match-string (cl-incf i) s)) varlist)) + ,@body)))) + +(defun magit-delete-line () + "Delete the rest of the current line." + (delete-region (point) (1+ (line-end-position)))) + +(defun magit-delete-match (&optional num) + "Delete text matched by last search. +If optional NUM is specified only delete that subexpression." + (delete-region (match-beginning (or num 0)) + (match-end (or num 0)))) + +(defun magit-file-line (file) + "Return the first line of FILE as a string." + (when (file-regular-p file) + (with-temp-buffer + (insert-file-contents file) + (buffer-substring-no-properties (point-min) + (line-end-position))))) + +(defun magit-file-lines (file &optional keep-empty-lines) + "Return a list of strings containing one element per line in FILE. +Unless optional argument KEEP-EMPTY-LINES is t, trim all empty lines." + (when (file-regular-p file) + (with-temp-buffer + (insert-file-contents file) + (split-string (buffer-string) "\n" (not keep-empty-lines))))) + +;;; magit-utils.el ends soon +(provide 'magit-utils) +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: +;;; magit-utils.el ends here diff --git a/elpa/magit-20160223.828/magit-wip.el b/elpa/magit-20160223.828/magit-wip.el new file mode 100644 index 0000000..6e34997 --- /dev/null +++ b/elpa/magit-20160223.828/magit-wip.el @@ -0,0 +1,288 @@ +;;; magit-wip.el --- commit snapshots to work-in-progress refs -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; 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 library defines tree global modes which automatically commit +;; snapshots to branch specific work-in-progress refs before and after +;; making changes, and two commands which can be used to do so on +;; demand. + +;;; Code: + +(require 'magit-core) +(require 'magit-log) + +;;; Options + +(defgroup magit-wip nil + "Automatically commit to work-in-progress refs." + :group 'magit-extensions) + +(defcustom magit-wip-after-save-local-mode-lighter " sWip" + "Lighter for Magit-Wip-After-Save-Local mode." + :package-version '(magit . "2.1.0") + :group 'magit-wip + :type 'string) + +(defcustom magit-wip-after-apply-mode-lighter " aWip" + "Lighter for Magit-Wip-After-Apply mode." + :package-version '(magit . "2.1.0") + :group 'magit-wip + :type 'string) + +(defcustom magit-wip-before-change-mode-lighter " cWip" + "Lighter for Magit-Wip-Before-Change mode." + :package-version '(magit . "2.1.0") + :group 'magit-wip + :type 'string) + +(defcustom magit-wip-namespace "refs/wip/" + "Namespace used for work-in-progress refs. +The wip refs are named \"index/\" +and \"wtree/\". When snapshots +are created while the `HEAD' is detached then \"HEAD\" +is used as `branch-ref'." + :package-version '(magit . "2.1.0") + :group 'magit-wip + :type 'string) + +;;; Modes + +(define-minor-mode magit-wip-after-save-local-mode + "After saving, also commit to a worktree work-in-progress ref. + +After saving the current file-visiting buffer this mode also +commits the changes to the worktree work-in-progress ref for +the current branch. + +This mode should be enabled globally by turning on the globalized +variant `magit-wip-after-save-mode'." + :package-version '(magit . "2.1.0") + :lighter magit-wip-after-save-local-mode-lighter + (if magit-wip-after-save-local-mode + (if (and buffer-file-name (magit-inside-worktree-p)) + (add-hook 'after-save-hook 'magit-wip-commit-buffer-file t t) + (setq magit-wip-after-save-local-mode nil) + (user-error "Need a worktree and a file")) + (remove-hook 'after-save-hook 'magit-wip-commit-buffer-file t))) + +(defun magit-wip-after-save-local-mode-turn-on () + (and buffer-file-name + (ignore-errors (magit-inside-worktree-p)) + (magit-file-tracked-p buffer-file-name) + (magit-wip-after-save-local-mode))) + +;;;###autoload +(define-globalized-minor-mode magit-wip-after-save-mode + magit-wip-after-save-local-mode magit-wip-after-save-local-mode-turn-on + :package-version '(magit . "2.1.0") + :group 'magit-wip) + +(defun magit-wip-commit-buffer-file () + "Commit visited file to a worktree work-in-progress ref. + +Also see `magit-wip-after-save-mode' which calls this function +automatically whenever a buffer visiting a tracked file is saved." + (interactive) + (--when-let (magit-wip-get-ref) + (magit-with-toplevel + (let ((file (file-relative-name buffer-file-name))) + (magit-wip-commit-worktree + it (list file) (if (called-interactively-p 'any) + (format "wip-save %s after save" file) + (format "autosave %s after save" file))))))) + +;;;###autoload +(define-minor-mode magit-wip-after-apply-mode + "Commit to work-in-progress refs. + +After applying a change using any \"apply variant\" +command (apply, stage, unstage, discard, and reverse) commit the +affected files to the current wip refs. For each branch there +may be two wip refs; one contains snapshots of the files as found +in the worktree and the other contains snapshots of the entries +in the index." + :package-version '(magit . "2.1.0") + :group 'magit-wip + :lighter magit-wip-after-change-mode-lighter + :global t) + +(defun magit-wip-commit-after-apply (&optional files msg) + (when magit-wip-after-apply-mode + (magit-wip-commit files msg))) + +;;;###autoload +(define-minor-mode magit-wip-before-change-mode + "Commit to work-in-progress refs before certain destructive changes. + +Before invoking a revert command or an \"apply variant\" +command (apply, stage, unstage, discard, and reverse) commit the +affected tracked files to the current wip refs. For each branch +there may be two wip refs; one contains snapshots of the files +as found in the worktree and the other contains snapshots of the +entries in the index. + +Only changes to files which could potentially be affected by the +command which is about to be called are committed." + :package-version '(magit . "2.1.0") + :group 'magit-wip + :lighter magit-wip-before-change-mode-lighter + :global t) + +(defun magit-wip-commit-before-change (&optional files msg) + (when magit-wip-before-change-mode + (magit-with-toplevel + (magit-wip-commit files msg)))) + +;;; Core + +(defun magit-wip-commit (&optional files msg) + "Commit all tracked files to the work-in-progress refs. + +Interactively, commit all changes to all tracked files using +a generic commit message. With a prefix-argument the commit +message is read in the minibuffer. + +Non-interactively, only commit changes to FILES using MSG as +commit message." + (interactive (list nil (if current-prefix-arg + (magit-read-string "Wip commit message") + "wip-save tracked files"))) + (--when-let (magit-wip-get-ref) + (magit-wip-commit-index it files msg) + (magit-wip-commit-worktree it files msg))) + +(defun magit-wip-commit-index (ref files msg &optional cached-only) + (let* ((wipref (concat magit-wip-namespace "index/" ref)) + (parent (magit-wip-get-parent ref wipref))) + (when (magit-git-failure "diff-index" "--quiet" + (and cached-only "--cached") + parent "--" files) + (magit-wip-update-wipref wipref (magit-git-string "write-tree") + parent files msg "index")))) + +(defun magit-wip-commit-worktree (ref files msg) + (let* ((wipref (concat magit-wip-namespace "wtree/" ref)) + (parent (magit-wip-get-parent ref wipref)) + (tree (magit-with-temp-index parent "--reset" + (if files + (magit-call-git "add" "--" files) + (magit-with-toplevel + (magit-call-git "add" "-u" "."))) + (magit-git-string "write-tree")))) + (when (magit-git-failure "diff-tree" "--quiet" parent tree "--" files) + (magit-wip-update-wipref wipref tree parent files msg "worktree")))) + +(defun magit-wip-update-wipref (wipref tree parent files msg start-msg) + (let ((len (length files))) + (unless (and msg (not (= (aref msg 0) ?\s))) + (setq msg (concat + (cond ((= len 0) "autosave tracked files") + ((> len 1) (format "autosave %s files" len)) + (t (concat "autosave " + (file-relative-name (car files) + (magit-toplevel))))) + msg))) + (unless (equal parent wipref) + (setq start-msg (concat "restart autosaving " start-msg)) + (magit-update-ref wipref start-msg + (magit-git-string "commit-tree" "-p" parent + "-m" start-msg + (concat parent "^{tree}"))) + (setq parent wipref)) + (magit-update-ref wipref msg + (magit-git-string "commit-tree" tree + "-p" parent "-m" msg)))) + +(defun magit-wip-get-ref () + (let ((ref (or (magit-git-string "symbolic-ref" "HEAD") "HEAD"))) + (when (magit-rev-verify ref) + ref))) + +(defun magit-wip-get-parent (ref wipref) + (if (and (magit-rev-verify wipref) + (equal (magit-git-string "merge-base" wipref ref) + (magit-rev-verify ref))) + wipref + ref)) + +;;; Log + +(defun magit-wip-log-current (branch args files count) + "Show log for the current branch and its wip refs. +With a negative prefix argument only show the worktree wip ref. +The absolute numeric value of the prefix argument controls how +many \"branches\" of each wip ref are shown." + (interactive + (nconc (list (or (magit-get-current-branch) "HEAD")) + (magit-log-arguments) + (list (prefix-numeric-value current-prefix-arg)))) + (magit-wip-log branch args files count)) + +(defun magit-wip-log (branch args files count) + "Show log for a branch and its wip refs. +With a negative prefix argument only show the worktree wip ref. +The absolute numeric value of the prefix argument controls how +many \"branches\" of each wip ref are shown." + (interactive + (nconc (list (magit-completing-read + "Log branch and its wip refs" + (-snoc (magit-list-local-branch-names) "HEAD") + nil t nil 'magit-revision-history + (or (magit-branch-at-point) + (magit-get-current-branch) + "HEAD"))) + (magit-log-arguments) + (list (prefix-numeric-value current-prefix-arg)))) + (unless (equal branch "HEAD") + (setq branch (concat "refs/heads/" branch))) + (magit-log (nconc (list branch) + (magit-wip-log-get-tips + (concat magit-wip-namespace "wtree/" branch) + (abs count)) + (and (>= count 0) + (magit-wip-log-get-tips + (concat magit-wip-namespace "index/" branch) + (abs count)))) + args files)) + +(defun magit-wip-log-get-tips (wipref count) + (-when-let (reflog (magit-git-lines "reflog" wipref)) + (let (tips) + (while (and reflog (> count 1)) + (setq reflog (cl-member "^[^ ]+ [^:]+: restart autosaving" + reflog :test #'string-match-p)) + (when (and (cadr reflog) + (string-match "^[^ ]+ \\([^:]+\\)" (cadr reflog))) + (push (match-string 1 (cadr reflog)) tips)) + (setq reflog (cddr reflog)) + (cl-decf count)) + (cons wipref (nreverse tips))))) + +;;; magit-wip.el ends soon +(provide 'magit-wip) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-wip.el ends here diff --git a/elpa/magit-20160223.828/magit.el b/elpa/magit-20160223.828/magit.el new file mode 100644 index 0000000..1117f79 --- /dev/null +++ b/elpa/magit-20160223.828/magit.el @@ -0,0 +1,2970 @@ +;;; magit.el --- A Git porcelain inside Emacs -*- lexical-binding: t -*- + +;; Copyright (C) 2008-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; Author: Marius Vollmer +;; Maintainer: Jonas Bernoulli +;; Kyle Meyer +;; Noam Postavsky +;; Former-Maintainers: +;; Nicolas Dudebout +;; Peter J. Weisberg +;; Phil Jackson +;; Rémi Vanicat +;; Yann Hodique + +;; Package-Requires: ((emacs "24.4") (async "20150909.2257") (dash "20151021.113") (with-editor "20160128.1201") (git-commit "20160119.1409") (magit-popup "20160119.1409")) +;; Keywords: git tools vc +;; Homepage: https://github.com/magit/magit + +;; Magit requires at least GNU Emacs 24.4 and Git 1.9.4. + +;; 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: + +;; Magit is an interface to the version control system Git, +;; implemented as an Emacs package. Magit aspires to be a complete +;; Git porcelain. While we cannot (yet) claim, that Magit wraps and +;; improves upon each and every Git command, it is complete enough to +;; allow even experienced Git users to perform almost all of their +;; daily version control tasks directly from within Emacs. While many +;; fine Git clients exist, only Magit and Git itself deserve to be +;; called porcelains. + +;;; Code: + +(require 'cl-lib) +(require 'dash) + +(require 'with-editor) +(require 'git-commit) +(require 'magit-core) +(require 'magit-diff) +(require 'magit-apply) +(require 'magit-log) + +(require 'format-spec) +(require 'package nil t) ; used in `magit-version' + +(eval-when-compile (require 'dired-x)) +(declare-function dired-jump 'dired-x) +(eval-when-compile (require 'eshell)) +(declare-function eshell-parse-arguments 'eshell) +(eval-when-compile (require 'message)) +(declare-function message-goto-body 'message) + +(defconst magit--minimal-git "1.9.4") +(defconst magit--minimal-emacs "24.4") + +;;; Options +;;;; Status Mode + +(defgroup magit-status nil + "Inspect and manipulate Git repositories." + :group 'magit-modes) + +(defcustom magit-status-mode-hook nil + "Hook run after entering Magit-Status mode." + :group 'magit-status + :type 'hook) + +(defcustom magit-status-headers-hook + '(magit-insert-error-header + magit-insert-diff-filter-header + magit-insert-head-branch-header + magit-insert-upstream-branch-header + magit-insert-push-branch-header + magit-insert-tags-header) + "Hook run to insert headers into the status buffer. + +This hook is run by `magit-insert-status-headers', which in turn +has to be a member of `magit-insert-status-sections' to be used +at all." + :package-version '(magit . "2.1.0") + :group 'magit-status + :type 'hook + :options '(magit-insert-error-header + magit-insert-diff-filter-header + magit-insert-repo-header + magit-insert-remote-header + magit-insert-head-branch-header + magit-insert-upstream-branch-header + magit-insert-push-branch-header + magit-insert-tags-header)) + +(defcustom magit-status-sections-hook + '(magit-insert-status-headers + magit-insert-merge-log + magit-insert-rebase-sequence + magit-insert-am-sequence + magit-insert-sequencer-sequence + magit-insert-bisect-output + magit-insert-bisect-rest + magit-insert-bisect-log + magit-insert-untracked-files + magit-insert-unstaged-changes + magit-insert-staged-changes + magit-insert-stashes + magit-insert-unpulled-from-upstream + magit-insert-unpulled-from-pushremote + magit-insert-unpushed-to-upstream + magit-insert-unpushed-to-pushremote) + "Hook run to insert sections into a status buffer." + :package-version '(magit . "2.4.0") + :group 'magit-status + :type 'hook) + +(defvar magit-status-refresh-hook nil + "Hook run after a status buffer has been refreshed.") + +(make-obsolete-variable 'magit-status-refresh-hook "\ +use `magit-pre-refresh-hook', `magit-post-refresh-hook', + `magit-refresh-buffer-hook', or `magit-status-mode-hook' instead. + + If you want to run a function every time the status buffer is + refreshed, in order to do something with that buffer, then use: + + (add-hook 'magit-refresh-buffer-hook + (lambda () + (when (derived-mode-p 'magit-status-mode) + ...))) + + If your hook function should run regardless of whether the + status buffer exists or not, then use `magit-pre-refresh-hook' + or `magit-post-refresh-hook'. + + If your hook function only has to be run once, when the buffer + is first created, then `magit-status-mode-hook' instead. +" "Magit 2.4.0") + +(defcustom magit-status-expand-stashes t + "Whether the list of stashes is expanded initially." + :package-version '(magit . "2.3.0") + :group 'magit-status + :type 'boolean) + +(defcustom magit-status-show-hashes-in-headers nil + "Whether headers in the status buffer show hashes. +The functions which respect this option are +`magit-insert-head-branch-header', +`magit-insert-upstream-branch-header', and +`magit-insert-push-branch-header'." + :package-version '(magit . "2.4.0") + :group 'magit-status + :type 'boolean) + +;;;; Refs Mode + +(defgroup magit-refs nil + "Inspect and manipulate Git branches and tags." + :group 'magit-modes) + +(defcustom magit-refs-mode-hook nil + "Hook run after entering Magit-Refs mode." + :package-version '(magit . "2.1.0") + :group 'magit-refs + :type 'hook) + +(defcustom magit-refs-sections-hook + '(magit-insert-error-header + magit-insert-branch-description + magit-insert-local-branches + magit-insert-remote-branches + magit-insert-tags) + "Hook run to insert sections into a references buffer." + :package-version '(magit . "2.1.0") + :group 'magit-refs + :type 'hook) + +(defcustom magit-refs-show-commit-count nil + "Whether to show commit counts in Magit-Refs mode buffers. + +all Show counts for branches and tags. +branch Show counts for branches only. +nil Never show counts. + +To change the value in an existing buffer use the command +`magit-refs-show-commit-count'" + :package-version '(magit . "2.1.0") + :group 'magit-refs + :safe (lambda (val) (memq val '(all branch nil))) + :type '(choice (const all :tag "For branches and tags") + (const branch :tag "For branches only") + (const nil :tag "Never"))) +(put 'magit-refs-show-commit-count 'safe-local-variable 'symbolp) +(put 'magit-refs-show-commit-count 'permanent-local t) + +(defcustom magit-refs-show-margin 'branch + "Whether to initially show the margin in refs buffers. + +When non-nil the committer name and date are initially displayed +in the margin of refs buffers. The margin can be shown or hidden +in the current buffer using the command `magit-toggle-margin'." + :package-version '(magit . "2.1.0") + :group 'magit-refs + :safe (lambda (val) (memq val '(all branch nil))) + :type '(choice (const all :tag "For branches and tags") + (const branch :tag "For branches only") + (const nil :tag "Never"))) + +(defcustom magit-visit-ref-create nil + "Whether `magit-visit-ref' may create new branches. + +When this is non-nil, then \"visiting\" a remote branch in a +refs buffer works by creating a new local branch which tracks +the remote branch and then checking out the new local branch." + :package-version '(magit . "2.1.0") + :group 'magit-refs + :group 'magit-commands + :type 'boolean) + +;;;; Miscellaneous + +(defcustom magit-branch-read-upstream-first t + "When creating a branch, read upstream before name of new branch." + :package-version '(magit . "2.2.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-branch-prefer-remote-upstream nil + "Whether to favor remote upstreams when creating new branches. + +When a new branch is created, Magit offers the branch, commit, or +stash as the default starting point of the new branch. If there +is no such thing at point, then it falls back to offer the +current branch as starting-point. The user may then accept that +default or pick something else. + +If the chosen starting-point is a branch, then it may also be set +as the upstream of the new branch, depending on the value of the +Git variable `branch.autoSetupMerge'. By default this is done +for remote branches, but not for local branches. + +You might prefer to always use some remote branch as upstream. +If the chosen starting-point is (1) a local branch, (2) whose +name is a member of the value of this option, (3) the upstream of +that local branch is a remote branch with the same name, and (4) +that remote branch can be fast-forwarded to the local branch, +then the chosen branch is used as starting-point, but its own +upstream is used as the upstream of the new branch. + +Assuming the chosen branch matches these conditions you would end +up with with e.g.: + + feature --upstream--> origin/master + +instead of + + feature --upstream--> master --upstream--> origin/master + +Which you prefer is a matter of personal preference. If you do +prefer the former, then you should add branches such as \"master\", +\"next\", and \"maint\" to the value of this options." + :package-version '(magit . "2.4.0") + :group 'magit-commands + :type '(repeat string)) + +(defcustom magit-repository-directories nil + "Directories containing Git repositories. +Magit checks these directories for Git repositories and offers +them as choices when `magit-status' is used with a prefix +argument." + :group 'magit + :type '(repeat string)) + +(defcustom magit-repository-directories-depth 3 + "The maximum depth to look for Git repositories. +When looking for a Git repository below the directories in +`magit-repository-directories', only descend this many levels +deep." + :group 'magit + :type 'integer) + +;;;; Faces + +(defface magit-header-line + '((t :inherit magit-section-heading)) + "Face for the `header-line'." + :group 'magit-faces) + +(defface magit-dimmed + '((((class color) (background light)) :foreground "grey50") + (((class color) (background dark)) :foreground "grey50")) + "Face for text that shouldn't stand out." + :group 'magit-faces) + +(defface magit-hash + '((((class color) (background light)) :foreground "grey60") + (((class color) (background dark)) :foreground "grey40")) + "Face for the sha1 part of the log output." + :group 'magit-faces) + +(defface magit-tag + '((((class color) (background light)) :foreground "Goldenrod4") + (((class color) (background dark)) :foreground "LightGoldenrod2")) + "Face for tag labels shown in log buffer." + :group 'magit-faces) + +(defface magit-branch-remote + '((((class color) (background light)) :foreground "DarkOliveGreen4") + (((class color) (background dark)) :foreground "DarkSeaGreen2")) + "Face for remote branch head labels shown in log buffer." + :group 'magit-faces) + +(defface magit-branch-local + '((((class color) (background light)) :foreground "SkyBlue4") + (((class color) (background dark)) :foreground "LightSkyBlue1")) + "Face for local branches." + :group 'magit-faces) + +(defface magit-branch-current + '((((class color) (background light)) :inherit magit-branch-local :box t) + (((class color) (background dark)) :inherit magit-branch-local :box t)) + "Face for current branch." + :group 'magit-faces) + +(defface magit-head + '((((class color) (background light)) :inherit magit-branch-local) + (((class color) (background dark)) :inherit magit-branch-local)) + "Face for the symbolic ref \"HEAD\"." + :group 'magit-faces) + +(defface magit-refname + '((((class color) (background light)) :foreground "grey30") + (((class color) (background dark)) :foreground "grey80")) + "Face for refnames without a dedicated face." + :group 'magit-faces) + +(defface magit-refname-stash + '((t :inherit magit-refname)) + "Face for wip refnames." + :group 'magit-faces) + +(defface magit-refname-wip + '((t :inherit magit-refname)) + "Face for wip refnames." + :group 'magit-faces) + +(defface magit-signature-good + '((t :foreground "green")) + "Face for good signatures." + :group 'magit-faces) + +(defface magit-signature-bad + '((t :foreground "red")) + "Face for bad signatures." + :group 'magit-faces) + +(defface magit-signature-untrusted + '((t :foreground "cyan")) + "Face for good untrusted signatures." + :group 'magit-faces) + +(defface magit-cherry-unmatched + '((t :foreground "cyan")) + "Face for unmatched cherry commits." + :group 'magit-faces) + +(defface magit-cherry-equivalent + '((t :foreground "magenta")) + "Face for equivalent cherry commits." + :group 'magit-faces) + +(defface magit-filename + '((t :weight normal)) + "Face for filenames." + :group 'magit-faces) + +;;; Inspect +;;;; Status Mode +;;;;; Status Core + +(defvar magit-status-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map "jz" 'magit-jump-to-stashes) + (define-key map "jt" 'magit-jump-to-tracked) + (define-key map "jn" 'magit-jump-to-untracked) + (define-key map "ju" 'magit-jump-to-unstaged) + (define-key map "js" 'magit-jump-to-staged) + (define-key map "jfu" 'magit-jump-to-unpulled-from-upstream) + (define-key map "jfp" 'magit-jump-to-unpulled-from-pushremote) + (define-key map "jpu" 'magit-jump-to-unpushed-to-upstream) + (define-key map "jpp" 'magit-jump-to-unpushed-to-pushremote) + map) + "Keymap for `magit-status-mode'.") + +(eval-after-load 'dired-x + '(define-key magit-status-mode-map [remap dired-jump] 'magit-dired-jump)) + +(define-derived-mode magit-status-mode magit-mode "Magit" + "Mode for looking at Git status. + +This mode is documented in info node `(magit)Status buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-visit-thing] to visit the change or commit at point. + +Type \\[magit-dispatch-popup] to see available prefix popups. + +Staging and applying changes is documented in info node +`(magit)Staging and unstaging' and info node `(magit)Applying'. + +\\Type \ +\\[magit-apply] to apply the change at point, \ +\\[magit-stage] to stage, +\\[magit-unstage] to unstage, \ +\\[magit-discard] to discard, or \ +\\[magit-reverse] to reverse it. + +\\\ +Type \\[magit-commit-popup] to create a commit. + +\\{magit-status-mode-map}" + :group 'magit-status + (hack-dir-local-variables-non-file-buffer)) + +;;;###autoload +(defun magit-status (&optional directory) + "Show the status of the current Git repository in a buffer. +With a prefix argument prompt for a repository to be shown. +With two prefix arguments prompt for an arbitrary directory. +If that directory isn't the root of an existing repository +then offer to initialize it as a new repository." + (interactive + (list (and (or current-prefix-arg (not (magit-toplevel))) + (magit-read-repository + (>= (prefix-numeric-value current-prefix-arg) 16))))) + (if directory + (let ((toplevel (magit-toplevel directory))) + (setq directory (file-name-as-directory (expand-file-name directory))) + (if (and toplevel (string-equal directory toplevel)) + (magit-status-internal directory) + (when (y-or-n-p + (if toplevel + (format "%s is a repository. Create another in %s? " + toplevel directory) + (format "Create repository in %s? " directory))) + (magit-init directory)))) + (magit-status-internal default-directory))) + +(put 'magit-status 'interactive-only 'magit-status-internal) + +;;;###autoload +(defun magit-status-internal (directory) + (magit-tramp-asserts directory) + (let ((default-directory directory)) + (magit-mode-setup #'magit-status-mode))) + +;;;;; Standard Status Sections + +(defvar magit-status-sections-hook-1 nil) + +(defun magit-status-refresh-buffer () + (magit-git-exit-code "update-index" "--refresh") + (magit-insert-section (status) + (if (-all-p #'functionp magit-status-sections-hook) + (run-hooks 'magit-status-sections-hook) + (message "`magit-status-sections-hook' contains entries that are \ +no longer valid.\nUsing standard value instead. Please re-configure") + (sit-for 5) + (let ((magit-status-sections-hook-1 + (eval (car (get 'magit-status-sections-hook 'standard-value))))) + (run-hooks 'magit-status-sections-hook-1)))) + (run-hooks 'magit-status-refresh-hook)) + +(defun magit-insert-status-headers () + "Insert header sections appropriate for `magit-status-mode' buffers. +The sections are inserted by running the functions on the hook +`magit-status-headers-hook'." + (if (magit-rev-verify "HEAD") + (magit-insert-headers magit-status-headers-hook) + (insert "In the beginning there was darkness\n\n"))) + +(defun magit-insert-error-header () + "Insert the message about the Git error that just occured. + +This function is only aware of the last error that occur when Git +was run for side-effects. If, for example, an error occurs while +generating a diff, then that error won't be inserted. Refreshing +the status buffer causes this section to disappear again." + (when magit-this-error + (magit-insert-section (error 'git) + (insert (propertize (format "%-10s" "GitError! ") + 'face 'magit-section-heading)) + (insert (propertize magit-this-error 'face 'font-lock-warning-face)) + (-when-let (key (car (where-is-internal 'magit-process-buffer))) + (insert (format " [Type `%s' for details]" (key-description key)))) + (insert ?\n)) + (setq magit-this-error nil))) + +(cl-defun magit-insert-head-branch-header + (&optional (branch (magit-get-current-branch))) + "Insert a header line about BRANCH. +When BRANCH is nil, use the current branch or, if none, the +detached `HEAD'." + (let ((output (magit-rev-format "%h %s" (or branch "HEAD")))) + (string-match "^\\([^ ]+\\) \\(.*\\)" output) + (magit-bind-match-strings (commit summary) output + (if branch + (magit-insert-section (branch branch) + (insert (format "%-10s" "Head: ")) + (when magit-status-show-hashes-in-headers + (insert (propertize commit 'face 'magit-hash) ?\s)) + (insert (propertize branch 'face 'magit-branch-local)) + (insert ?\s summary ?\n)) + (magit-insert-section (commit commit) + (insert (format "%-10s" "Head: ")) + (insert (propertize commit 'face 'magit-hash)) + (insert ?\s summary ?\n)))))) + +(cl-defun magit-insert-upstream-branch-header + (&optional (branch (magit-get-current-branch)) + (pull (magit-get-upstream-branch branch)) + keyword) + "Insert a header line about branch usually pulled into current branch." + (when pull + (magit-insert-section (branch pull) + (insert (format "%-10s" + (or keyword + (if (magit-get-boolean "branch" branch "rebase") + "Rebase: " + "Merge: ")))) + (--when-let (and magit-status-show-hashes-in-headers + (magit-rev-format "%h" pull)) + (insert (propertize it 'face 'magit-hash) ?\s)) + (insert (propertize pull 'face + (if (string= (magit-get "branch" branch "remote") ".") + 'magit-branch-local + 'magit-branch-remote))) + (insert ?\s) + (if (magit-rev-verify pull) + (insert (or (magit-rev-format "%s" pull) "")) + (insert (propertize "is missing" 'face 'font-lock-warning-face))) + (insert ?\n)))) + +(cl-defun magit-insert-push-branch-header + (&optional (branch (magit-get-current-branch)) + (push (magit-get-push-branch branch))) + "Insert a header line about the branch the current branch is pushed to." + (when push + (magit-insert-section (branch push) + (insert (format "%-10s" "Push: ")) + (--when-let (and magit-status-show-hashes-in-headers + (magit-rev-format "%h" push)) + (insert (propertize it 'face 'magit-hash) ?\s)) + (insert (propertize push 'face 'magit-branch-remote) ?\s) + (if (magit-rev-verify push) + (insert (or (magit-rev-format "%s" push) "")) + (insert (propertize "is missing" 'face 'font-lock-warning-face))) + (insert ?\n)))) + +(defun magit-insert-tags-header () + "Insert a header line about the current and/or next tag." + (let* ((this-tag (magit-get-current-tag nil t)) + (next-tag (magit-get-next-tag nil t)) + (this-cnt (cadr this-tag)) + (next-cnt (cadr next-tag)) + (this-tag (car this-tag)) + (next-tag (car next-tag)) + (both-tags (and this-tag next-tag t))) + (when (or this-tag next-tag) + (magit-insert-section (tag (or this-tag next-tag)) + (insert (format "%-10s" (if both-tags "Tags: " "Tag: "))) + (when this-tag + (insert (magit-format-status-tag-sentence this-tag this-cnt nil))) + (when both-tags + (insert ", ")) + (when next-tag + (insert (magit-format-status-tag-sentence next-tag next-cnt t))) + (insert ?\n))))) + +(defun magit-format-status-tag-sentence (tag count next) + (concat (propertize tag 'face 'magit-tag) + (and (> count 0) + (format " (%s)" + (propertize (format "%s" count) 'face + (if next 'magit-tag 'magit-branch-local)))))) + +(defun magit-insert-diff-filter-header () + "Insert a header line showing the effective diff filters." + (when magit-diff-section-file-args + (magit-insert-section (filter 'diff) + (insert (propertize (format "%-10s" "Filter! ") + 'face 'magit-section-heading)) + (insert (mapconcat #'identity magit-diff-section-file-args " ")) + (insert ?\n)))) + +(magit-define-section-jumper magit-jump-to-untracked "Untracked files" untracked) + +(defvar magit-untracked-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-delete-thing] 'magit-discard) + (define-key map "s" 'magit-stage) + map) + "Keymap for the `untracked' section.") + +(defun magit-insert-untracked-files () + "Maybe insert a list or tree of untracked files. +Do so depending on the value of `status.showUntrackedFiles'." + (let ((show (or (magit-get "status.showUntrackedFiles") "normal"))) + (unless (equal show "no") + (if (equal show "all") + (-when-let (files (magit-untracked-files)) + (magit-insert-section (untracked) + (magit-insert-heading "Untracked files:") + (magit-insert-un/tracked-files-1 files nil) + (insert ?\n))) + (-when-let + (files (--mapcat (and (eq (aref it 0) ??) + (list (substring it 3))) + (magit-git-items "status" "-z" "--porcelain"))) + (magit-insert-section (untracked) + (magit-insert-heading "Untracked files:") + (dolist (file files) + (magit-insert-section (file file) + (insert (propertize file 'face 'magit-filename) ?\n)))) + (insert ?\n)))))) + +(defun magit-insert-un/tracked-files-1 (files directory) + (while (and files (string-prefix-p (or directory "") (car files))) + (let ((dir (file-name-directory (car files)))) + (if (equal dir directory) + (let ((file (pop files))) + (magit-insert-section (file file) + (insert (propertize file 'face 'magit-filename) ?\n))) + (magit-insert-section (file dir t) + (insert (propertize dir 'file 'magit-filename) ?\n) + (magit-insert-heading) + (setq files (magit-insert-un/tracked-files-1 files dir)))))) + files) + +;;;;; Auxiliary Status Sections + +(magit-define-section-jumper magit-jump-to-tracked "Tracked files" tracked) + +(defun magit-insert-tracked-files () + "Insert a tree of tracked files." + (-when-let (files (magit-list-files)) + (magit-insert-section (tracked nil t) + (magit-insert-heading "Tracked files:") + (magit-insert-un/tracked-files-1 files nil) + (insert ?\n)))) + +(defun magit-insert-user-header () + "Insert a header line about the current user." + (let ((name (magit-get "user.name")) + (email (magit-get "user.email"))) + (when (and name email) + (magit-insert-section (user name) + (insert (format "%-10s" "User: ")) + (insert (propertize name 'face 'magit-log-author)) + (insert " <" email ">\n"))))) + +(defun magit-insert-repo-header () + "Insert a header line showing the path to the repository top-level." + (let ((topdir (magit-toplevel))) + (magit-insert-section (repo topdir) + (insert (format "%-10s%s\n" "Repo: " (abbreviate-file-name topdir)))))) + +(defun magit-insert-remote-header () + "Insert a header line about the remote of the current branch. + +If no remote is configured for the current branch, then fall back +showing the \"origin\" remote, or if that does not exist the first +remote in alphabetic order." + (--when-let (or (magit-get-remote) + (let ((remotes (magit-list-remotes))) + (or (car (member "origin" remotes)) + (car remotes)))) + (magit-insert-section (remote it) + (insert (format "%-10s" "Remote: ")) + (insert (propertize it 'face 'magit-branch-remote) ?\s) + (insert (magit-get "remote" it "url") ?\n)))) + +;;;;; Status Miscellaneous + +(defun ido-enter-magit-status () + "Drop into `magit-status' from file switching. + +To make this command available use something like: + + (add-hook \\='ido-setup-hook + (lambda () + (define-key ido-completion-map + (kbd \"C-x g\") \\='ido-enter-magit-status))) + +Starting with Emacs 25.1 the Ido keymaps are defined just once +instead of every time Ido is invoked, so now you can modify it +like pretty much every other keymap: + + (define-key ido-common-completion-map + (kbd \"C-x g\") 'ido-enter-magit-status)" + (interactive) + (with-no-warnings ; FIXME these are internal variables + (setq ido-exit 'fallback fallback 'magit-status)) + (exit-minibuffer)) + +(defun magit-status-maybe-update-revision-buffer (&optional _) + "When moving in the status buffer, update the revision buffer. +If there is no revision buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-status-mode) + (magit-log-maybe-update-revision-buffer-1))) + +(defun magit-status-maybe-update-blob-buffer (&optional _) + "When moving in the status buffer, update the blob buffer. +If there is no blob buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-status-mode) + (magit-log-maybe-update-blob-buffer-1))) + +;;;; Refs Mode + +(defvar magit-refs-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map "\C-y" 'magit-refs-set-show-commit-count) + map) + "Keymap for `magit-refs-mode'.") + +(define-derived-mode magit-refs-mode magit-mode "Magit Refs" + "Mode which lists and compares references. + +This mode is documented in info node `(magit)References buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit or branch at point. + +Type \\[magit-branch-popup] to see available branch commands. +Type \\[magit-merge-popup] to merge the branch or commit at point. +Type \\[magit-cherry-pick-popup] to apply the commit at point. +Type \\[magit-reset] to reset HEAD to the commit at point. + +\\{magit-refs-mode-map}" + :group 'magit-refs + (hack-dir-local-variables-non-file-buffer)) + +;;;###autoload (autoload 'magit-show-refs-popup "magit" nil t) +(magit-define-popup magit-show-refs-popup + "Popup console for `magit-show-refs'." + 'magit-refs + :man-page "git-branch" + :switches '((?m "Merged to HEAD" "--merged") + (?M "Merged to master" "--merged=master") + (?n "Not merged to HEAD" "--no-merged") + (?N "Not merged to master" "--no-merged=master")) + :options '((?c "Contains" "--contains=" magit-read-branch-or-commit) + (?m "Merged" "--merged=" magit-read-branch-or-commit) + (?n "Not merged" "--no-merged=" magit-read-branch-or-commit)) + :actions '((?y "Show refs, comparing them with HEAD" + magit-show-refs-head) + (?c "Show refs, comparing them with current branch" + magit-show-refs-current) + (?o "Show refs, comparing them with other branch" + magit-show-refs)) + :default-action 'magit-show-refs-head + :use-prefix 'popup) + +;;;###autoload +(defun magit-show-refs-head (&optional args) + "List and compare references in a dedicated buffer. +Refs are compared with `HEAD'." + (interactive (list (magit-show-refs-arguments))) + (magit-show-refs nil args)) + +;;;###autoload +(defun magit-show-refs-current (&optional args) + "List and compare references in a dedicated buffer. +Refs are compared with the current branch or `HEAD' if +it is detached." + (interactive (list (magit-show-refs-arguments))) + (magit-show-refs (magit-get-current-branch) args)) + +;;;###autoload +(defun magit-show-refs (&optional ref args) + "List and compare references in a dedicated buffer. +Refs are compared with a branch read form the user." + (interactive (list (magit-read-other-branch "Compare with") + (magit-show-refs-arguments))) + (magit-mode-setup #'magit-refs-mode ref args)) + +(defun magit-refs-refresh-buffer (&rest _ignore) + (setq magit-set-buffer-margin-refresh (not magit-show-margin)) + (unless (magit-rev-verify (or (car magit-refresh-args) "HEAD")) + (setq magit-refs-show-commit-count nil)) + (magit-insert-section (branchbuf) + (run-hooks 'magit-refs-sections-hook))) + +(defun magit-insert-branch-description () + "Insert header containing the description of the current branch. +Insert a header line with the name and description of the +current branch. The description is taken from the Git variable +`branch..description'; if that is undefined then no header +line is inserted at all." + (let ((branch (magit-get-current-branch))) + (--when-let (magit-git-lines + "config" (format "branch.%s.description" branch)) + (magit-insert-section (branchdesc branch t) + (magit-insert-heading branch ": " (car it)) + (insert (mapconcat 'identity (cdr it) "\n")) + (insert "\n\n"))))) + +(defconst magit-refs-branch-line-re + (concat "^" + "\\(?:[ \\*]\\) " + "\\(?1:([^)]+)\\|[^ ]+?\\)" ; branch + "\\(?: +\\)" + "\\(?2:[0-9a-fA-F]+\\) " ; sha1 + "\\(?:\\[" + "\\(?4:[^:]+\\)" ; upstream + "\\(?:: \\(?:" + "\\(?7:gone\\)\\|" ; gone + "\\(?:ahead \\(?5:[0-9]+\\)\\)?" ; ahead + "\\(?:, \\)?" + "\\(?:behind \\(?6:[0-9]+\\)\\)?" ; behind + "\\)\\)?" + "\\] \\)?" + "\\(?3:.*\\)")) ; message + +(defvar magit-refs-local-branch-format "%4c %-25n %U%m\n" + "Format used for local branches in refs buffers.") +(defvar magit-refs-remote-branch-format "%4c %-25n %m\n" + "Format used for remote branches in refs buffers.") +(defvar magit-refs-tags-format "%4c %-25n %m\n" + "Format used for tags in refs buffers.") +(defvar magit-refs-indent-cherry-lines 3 + "Indentation of cherries in refs buffers.") + +(defvar magit-branch-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-visit-thing] 'magit-visit-ref) + (define-key map [remap magit-delete-thing] 'magit-branch-delete) + (define-key map "R" 'magit-branch-rename) + map) + "Keymap for `branch' sections.") + +(defvar magit-remote-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-delete-thing] 'magit-remote-remove) + (define-key map "R" 'magit-remote-rename) + map) + "Keymap for `remote' sections.") + +(defun magit-refs-set-show-commit-count () + "Change for which refs the commit count is shown." + (interactive) + (setq-local magit-refs-show-commit-count + (magit-read-char-case "Show commit counts for " nil + (?a "[a]ll refs" 'all) + (?b "[b]ranches only" t) + (?n "[n]othing" nil))) + (magit-refresh)) + +(defun magit-visit-ref () + "Visit the reference or revision at point. + +In most places use `magit-show-commit' to visit the reference or +revision at point. + +In `magit-refs-mode', when there is a reference at point, instead +checkout that reference. When option `magit-visit-ref-create' is +non-nil and point is on remote branch, then create a local branch +with the same name and check it out. + +With a prefix argument only focus on the reference at point, i.e. +the commit counts and cherries are updated to be relative to that +reference, but it is not checked out." + (interactive) + (if (derived-mode-p 'magit-refs-mode) + (magit-section-case + (([branch * branchbuf] + [tag * branchbuf]) + (let ((ref (magit-section-value (magit-current-section)))) + (if current-prefix-arg + (magit-show-refs ref) + (if (magit-section-when [branch remote]) + (let ((start ref) + (arg "-b")) + (string-match "^[^/]+/\\(.+\\)" ref) + (setq ref (match-string 1 ref)) + (when (magit-branch-p ref) + (if (yes-or-no-p + (format "Branch %s already exists. Recreate it?" ref)) + (setq arg "-B") + (user-error "Abort"))) + (magit-run-git "checkout" arg ref start)) + (magit-run-git "checkout" ref)) + (setcar magit-refresh-args ref) + (magit-refresh)))) + ([commit * branchbuf] + (call-interactively #'magit-show-commit))) + (call-interactively #'magit-show-commit))) + +(defun magit-insert-local-branches () + "Insert sections showing all local branches." + (magit-insert-section (local nil) + (magit-insert-heading "Branches:") + (let ((current (magit-get-current-branch)) + (branches (magit-list-local-branch-names))) + (dolist (line (magit-git-lines "branch" "-vv" + (cadr magit-refresh-args))) + (string-match magit-refs-branch-line-re line) + (magit-bind-match-strings + (branch hash message upstream ahead behind gone) line + (when (string-match-p "(HEAD detached" branch) + (setq branch nil)) + (magit-insert-branch + branch magit-refs-local-branch-format current branches + 'magit-branch-local hash message upstream ahead behind gone)))) + (insert ?\n))) + +(defun magit-insert-remote-branches () + "Insert sections showing all remote-tracking branches." + (dolist (remote (magit-list-remotes)) + (magit-insert-section (remote remote) + (magit-insert-heading + (let ((pull (magit-get "remote" remote "url")) + (push (magit-get "remote" remote "pushurl"))) + (format "%s (%s):" (capitalize remote) + (concat pull (and pull push ", ") push)))) + (let ((current (magit-get-current-branch)) + (branches (magit-list-local-branch-names))) + (dolist (line (magit-git-lines "branch" "-vvr" + (cadr magit-refresh-args))) + (when (string-match magit-refs-branch-line-re line) + (magit-bind-match-strings (branch hash message) line + (when (string-match-p (format "^%s/" remote) branch) + (magit-insert-branch + branch magit-refs-remote-branch-format current branches + 'magit-branch-remote hash message)))))) + (insert ?\n)))) + +(defun magit-insert-branch (branch format &rest args) + "For internal use, don't add to a hook." + (unless magit-refs-show-commit-count + (setq format (replace-regexp-in-string "%[0-9]\\([cC]\\)" "%1\\1" format t))) + (if (equal branch "HEAD") + (magit-insert-section it (commit (magit-rev-parse "HEAD") t) + (apply #'magit-insert-branch-1 it nil format args)) + (magit-insert-section it (branch branch t) + (apply #'magit-insert-branch-1 it branch format args)))) + +(defun magit-insert-branch-1 + (section branch format current branches face + &optional hash message upstream ahead behind gone) + "For internal use, don't add to a hook." + (let* ((head (or (car magit-refresh-args) current "HEAD")) + (count (and branch + (magit-refs-format-commit-count branch head format))) + (mark (cond ((or (equal branch head) + (and (not branch) (equal head "HEAD"))) + (if (equal branch current) + (propertize "@" 'face 'magit-head) + (propertize "#" 'face 'magit-tag))) + ((equal branch current) + (propertize "." 'face 'magit-head))))) + (when upstream + (setq upstream (propertize upstream 'face + (if (member upstream branches) + 'magit-branch-local + 'magit-branch-remote)))) + (magit-insert-heading + (format-spec + format + `((?a . ,(or ahead "")) + (?b . ,(or behind "")) + (?c . ,(or mark count "")) + (?C . ,(or mark " ")) + (?h . ,(or (propertize hash 'face 'magit-hash) "")) + (?m . ,(or message "")) + (?n . ,(propertize (or branch "(detached)") 'face face)) + (?u . ,(or upstream "")) + (?U . ,(if upstream + (format (propertize "[%s%s] " 'face 'magit-dimmed) + upstream + (cond + (gone + (concat ": " (propertize gone 'face 'error))) + ((or ahead behind) + (concat ": " + (and ahead (format "ahead %s" ahead)) + (and ahead behind ", ") + (and behind (format "behind %s" behind)))) + (t ""))) + ""))))) + (when magit-show-margin + (magit-refs-format-margin branch)) + (magit-refs-insert-cherry-commits head branch section))) + +(defvar magit-tag-section-map + (let ((map (make-sparse-keymap))) + (define-key map [remap magit-visit-thing] 'magit-visit-ref) + (define-key map [remap magit-delete-thing] 'magit-tag-delete) + map) + "Keymap for `tag' sections.") + +(defun magit-insert-tags () + "Insert sections showing all tags." + (-when-let (tags (magit-git-lines "tag" "-l" "-n")) + (magit-insert-section (tags) + (magit-insert-heading "Tags:") + (let ((head (or (car magit-refresh-args) + (magit-get-current-branch) + "HEAD")) + (format (if magit-refs-show-commit-count + magit-refs-tags-format + (replace-regexp-in-string + "%[0-9]\\([cC]\\)" "%1\\1" magit-refs-tags-format t)))) + (dolist (tag (nreverse tags)) + (string-match "^\\([^ \t]+\\)[ \t]+\\([^ \t\n].*\\)?" tag) + (let* ((message (match-string 2 tag)) + (tag (match-string 1 tag)) + (count (magit-refs-format-commit-count tag head format t)) + (mark (and (equal tag head) + (propertize "#" 'face 'magit-tag)))) + (magit-insert-section section (tag tag t) + (magit-insert-heading + (format-spec format + `((?n . ,(propertize tag 'face 'magit-tag)) + (?c . ,(or mark count "")) + (?m . ,(or message ""))))) + (when (and magit-show-margin + (eq magit-refs-show-margin 'all)) + (magit-refs-format-margin (concat tag "^{commit}"))) + (magit-refs-insert-cherry-commits head tag section))))) + (insert ?\n)))) + +(defun magit-refs-insert-cherry-commits (head ref section) + (if (magit-section-hidden section) + (setf (magit-section-washer section) + (apply-partially #'magit-refs-insert-cherry-commits-1 + head ref section)) + (magit-refs-insert-cherry-commits-1 head ref section))) + +(defun magit-refs-insert-cherry-commits-1 (head ref section) + (let ((start (point))) + (magit-git-wash (apply-partially 'magit-log-wash-log 'cherry) + "cherry" "-v" "--abbrev" head ref magit-refresh-args) + (unless (= (point) start) + (insert (propertize "\n" 'magit-section section))))) + +(defun magit-refs-format-commit-count (ref head format &optional tag-p) + (and (string-match-p "%-?[0-9]+c" format) + (if tag-p + (eq magit-refs-show-commit-count 'all) + magit-refs-show-commit-count) + (let ((count (cadr (magit-rev-diff-count head ref)))) + (and (> count 0) + (propertize (number-to-string count) 'face 'magit-dimmed))))) + +(defun magit-refs-format-margin (commit) + (save-excursion + (goto-char (line-beginning-position 0)) + (let ((line (magit-rev-format "%ct%cN" commit))) + (magit-format-log-margin (substring line 10) + (substring line 0 10))))) + +;;;; Files + +;;;###autoload +(defun magit-find-file (rev file) + "View FILE from REV. +Switch to a buffer visiting blob REV:FILE, +creating one if none already exists." + (interactive (magit-find-file-read-args "Find file")) + (switch-to-buffer (magit-find-file-noselect rev file))) + +;;;###autoload +(defun magit-find-file-other-window (rev file) + "View FILE from REV, in another window. +Like `magit-find-file', but create a new window or reuse an +existing one." + (interactive (magit-find-file-read-args "Find file in other window")) + (switch-to-buffer-other-window (magit-find-file-noselect rev file))) + +(defun magit-find-file-read-args (prompt) + (let ((rev (magit-read-branch-or-commit "Find file from revision"))) + (list rev (magit-read-file-from-rev rev prompt)))) + +(defvar magit-read-file-hist nil) + +(defun magit-read-file-from-rev (rev prompt &optional default) + (let ((files (magit-revision-files rev))) + (magit-completing-read + prompt files nil t nil 'magit-read-file-hist + (car (member (or default (magit-current-file)) files))))) + +(defun magit-read-changed-file (rev-or-range prompt &optional default) + (magit-read-file-choice + prompt + (magit-changed-files rev-or-range) + default + (concat "No file changed in " rev-or-range))) + +(defun magit-get-revision-buffer (rev file &optional create) + (funcall (if create 'get-buffer-create 'get-buffer) + (format "%s.~%s~" file (subst-char-in-string ?/ ?_ rev)))) + +(defun magit-get-revision-buffer-create (rev file) + (magit-get-revision-buffer rev file t)) + +(defvar magit-find-file-hook nil) + +(defun magit-find-file-noselect (rev file) + "Read FILE from REV into a buffer and return the buffer. +FILE must be relative to the top directory of the repository." + (let ((topdir (magit-toplevel))) + (when (file-name-absolute-p file) + (setq file (file-relative-name file topdir))) + (or (magit-get-revision-buffer rev file) + (with-current-buffer (magit-get-revision-buffer-create rev file) + (let ((inhibit-read-only t)) + (erase-buffer) + (magit-git-insert "cat-file" "-p" (concat rev ":" file))) + (setq magit-buffer-revision (magit-rev-format "%H" rev) + magit-buffer-refname rev + magit-buffer-file-name (expand-file-name file topdir)) + (let ((buffer-file-name magit-buffer-file-name)) + (normal-mode t)) + (setq buffer-read-only t) + (set-buffer-modified-p nil) + (goto-char (point-min)) + (magit-blob-mode 1) + (run-hooks 'magit-find-file-hook) + (current-buffer))))) + +(defvar magit-find-index-hook nil) + +(defun magit-find-file-index-noselect (file &optional revert) + "Read FILE from the index into a buffer and return the buffer. +FILE must to be relative to the top directory of the repository." + (let* ((bufname (concat file ".~{index}~")) + (origbuf (get-buffer bufname)) + (default-directory (magit-toplevel))) + (with-current-buffer (get-buffer-create bufname) + (when (or (not origbuf) revert + (y-or-n-p (format "%s already exists; revert it? " bufname))) + (let ((inhibit-read-only t) + (temp (car (split-string + (or (magit-git-string "checkout-index" "--temp" file) + (error "Error making temp file")) + "\t")))) + (erase-buffer) + (insert-file-contents temp nil nil nil t) + (delete-file temp))) + (setq magit-buffer-revision "{index}" + magit-buffer-refname "{index}" + magit-buffer-file-name (expand-file-name file)) + (let ((buffer-file-name magit-buffer-file-name)) + (normal-mode t)) + (setq buffer-read-only t) + (set-buffer-modified-p nil) + (goto-char (point-min)) + (run-hooks 'magit-find-index-hook) + (current-buffer)))) + +(defun magit-update-index () + "Update the index with the contents of the current buffer. +The current buffer has to be visiting a file in the index, which +is done using `magit-find-index-noselect'." + (interactive) + (let ((file (magit-file-relative-name))) + (unless (equal magit-buffer-refname "{index}") + (user-error "%s isn't visiting the index" file)) + (if (y-or-n-p (format "Update index with contents of %s" (buffer-name))) + (let ((index (make-temp-file "index")) + (buffer (current-buffer))) + (when magit-wip-before-change-mode + (magit-wip-commit-before-change (list file) " before un-/stage")) + (with-temp-file index + (insert-buffer-substring buffer)) + (magit-call-git "update-index" "--cacheinfo" + (substring (magit-git-string "ls-files" "-s" file) 0 6) + (magit-git-string "hash-object" "-t" "blob" "-w" + (concat "--path=" file) + "--" index) + file) + (set-buffer-modified-p nil) + (when magit-wip-after-apply-mode + (magit-wip-commit-after-apply (list file) " after un-/stage"))) + (message "Abort"))) + (--when-let (magit-mode-get-buffer 'magit-status-mode) + (with-current-buffer it (magit-refresh))) + t) + +;;;###autoload +(defun magit-dired-jump (&optional other-window) + "Visit file at point using Dired. +With a prefix argument, visit in other window. If there +is no file at point then instead visit `default-directory'." + (interactive "P") + (dired-jump other-window (--if-let (magit-file-at-point) + (expand-file-name it) + default-directory))) + +;;;###autoload +(defun magit-checkout-file (rev file) + "Checkout FILE from REV." + (interactive + (let ((rev (magit-read-branch-or-commit + "Checkout from revision" magit-buffer-revision))) + (list rev (magit-read-file-from-rev rev "Checkout file")))) + (magit-with-toplevel + (magit-run-git "checkout" rev "--" file))) + +;;; Manipulate +;;;; Init + +;;;###autoload +(defun magit-init (directory) + "Initialize a Git repository, then show its status. + +If the directory is below an existing repository, then the user +has to confirm that a new one should be created inside. If the +directory is the root of the existing repository, then the user +has to confirm that it should be reinitialized. + +Non-interactively DIRECTORY is (re-)initialized unconditionally." + (interactive + (let ((directory (file-name-as-directory + (expand-file-name + (read-directory-name "Create repository in: "))))) + (-when-let (toplevel (magit-toplevel directory)) + (setq toplevel (expand-file-name toplevel)) + (unless (y-or-n-p (if (string-equal toplevel directory) + (format "Reinitialize existing repository %s? " + directory) + (format "%s is a repository. Create another in %s? " + toplevel directory))) + (user-error "Abort"))) + (list directory))) + ;; `git init' does not understand the meaning of "~"! + (magit-call-git "init" (magit-convert-git-filename + (expand-file-name directory))) + (magit-status-internal directory)) + +;;;; Branch +;;;;; Branch Popup + +;;;###autoload (autoload 'magit-branch-popup "magit" nil t) +(magit-define-popup magit-branch-popup + "Popup console for branch commands." + 'magit-commands + :man-page "git-branch" + :variables '("Configure existing branches" + (?d "branch.%s.description" + magit-edit-branch*description + magit-format-branch*description) + (?u "branch.%s.merge" + magit-set-branch*merge/remote + magit-format-branch*merge/remote) + (?r "branch.%s.rebase" + magit-cycle-branch*rebase + magit-format-branch*rebase) + (?p "branch.%s.pushRemote" + magit-cycle-branch*pushRemote + magit-format-branch*pushRemote) + "Configure repository defaults" + (?\M-r "pull.rebase" + magit-cycle-pull.rebase + magit-format-pull.rebase) + (?\M-p "remote.pushDefault" + magit-cycle-remote.pushDefault + magit-format-remote.pushDefault) + "Configure branch creation" + (?U "branch.autoSetupMerge" + magit-cycle-branch*autoSetupMerge + magit-format-branch*autoSetupMerge) + (?R "branch.autoSetupRebase" + magit-cycle-branch*autoSetupRebase + magit-format-branch*autoSetupRebase)) + :actions '((?c "Create and checkout" magit-branch-and-checkout) + (?b "Checkout" magit-checkout) + (?n "Create" magit-branch) + (?m "Rename" magit-branch-rename) + (?s "Create spin-off" magit-branch-spinoff) + (?x "Reset" magit-branch-reset) nil + (?k "Delete" magit-branch-delete)) + :default-action 'magit-checkout + :max-action-columns 2 + :setup-function 'magit-branch-popup-setup) + +(defun magit-branch-popup-setup (val def) + (magit-popup-default-setup val def) + (use-local-map (copy-keymap magit-popup-mode-map)) + (dolist (ev (-filter #'magit-popup-event-p (magit-popup-get :variables))) + (local-set-key (vector (magit-popup-event-key ev)) + 'magit-invoke-popup-action))) + +;;;;; Branch Actions + +;;;###autoload +(defun magit-checkout (revision) + "Checkout REVISION, updating the index and the working tree. +If REVISION is a local branch then that becomes the current +branch. If it is something else then `HEAD' becomes detached. +Checkout fails if the working tree or the staging area contain +changes. +\n(git checkout REVISION)." + (interactive (list (magit-read-other-branch-or-commit "Checkout"))) + (magit-run-git "checkout" revision)) + +;;;###autoload +(defun magit-branch (branch start-point &optional args) + "Create BRANCH at branch or revision START-POINT. +\n(git branch [ARGS] BRANCH START-POINT)." + (interactive (magit-branch-read-args "Create branch")) + (magit-call-git "branch" args branch start-point) + (--when-let (and (magit-get-upstream-branch branch) + (magit-get-indirect-upstream-branch start-point)) + (magit-call-git "branch" (concat "--set-upstream-to=" it) branch)) + (magit-refresh)) + +;;;###autoload +(defun magit-branch-and-checkout (branch start-point &optional args) + "Create and checkout BRANCH at branch or revision START-POINT. +\n(git checkout [ARGS] -b BRANCH START-POINT)." + (interactive (magit-branch-read-args "Create and checkout branch")) + (if (string-match-p "^stash@{[0-9]+}$" start-point) + (magit-run-git "stash" "branch" branch start-point) + (magit-call-git "checkout" args "-b" branch start-point) + (--when-let (and (magit-get-upstream-branch branch) + (magit-get-indirect-upstream-branch start-point)) + (magit-call-git "branch" (concat "--set-upstream-to=" it) branch)) + (magit-refresh))) + +(defun magit-branch-read-args (prompt) + (let ((args (magit-branch-arguments)) start branch) + (cond (magit-branch-read-upstream-first + (setq start (magit-read-starting-point prompt)) + (setq branch (magit-read-string-ns + "Branch name" + (and (member start (magit-list-remote-branch-names)) + (mapconcat #'identity + (cdr (split-string start "/")) + "/"))))) + (t + (setq branch (magit-read-string-ns "Branch name")) + (setq start (magit-read-starting-point prompt)))) + (list branch start args))) + +;;;###autoload +(defun magit-branch-spinoff (branch &rest args) + "Create new branch from the unpushed commits. + +Create and checkout a new branch starting at and tracking the +current branch. That branch in turn is reset to the last commit +it shares with its upstream. If the current branch has no +upstream or no unpushed commits, then the new branch is created +anyway and the previously current branch is not touched. + +This is useful to create a feature branch after work has already +began on the old branch (likely but not necessarily \"master\"). + +If the current branch is a member of the value of option +`magit-branch-prefer-remote-upstream' (which see), then the +current branch will be used as the starting point as usual, but +the upstream of the starting-point may be used as the upstream +of the new branch, instead of the starting-point itself." + (interactive (list (magit-read-string "Spin off branch") + (magit-branch-arguments))) + (when (magit-branch-p branch) + (user-error "Branch %s already exists" branch)) + (-if-let (current (magit-get-current-branch)) + (let (tracked base) + (magit-call-git "checkout" args "-b" branch current) + (--when-let (magit-get-indirect-upstream-branch current) + (magit-call-git "branch" "--set-upstream-to" it branch)) + (when (and (setq tracked (magit-get-upstream-branch current)) + (setq base (magit-git-string "merge-base" current tracked)) + (not (magit-rev-eq base current))) + (magit-call-git "update-ref" "-m" + (format "reset: moving to %s" base) + (concat "refs/heads/" current) base)) + (magit-refresh)) + (magit-run-git "checkout" "-b" branch))) + +;;;###autoload +(defun magit-branch-reset (branch to &optional args set-upstream) + "Reset a branch to the tip of another branch or any other commit. + +When the branch being reset is the current branch, then do a +hard reset. If there are any uncommitted changes, then the user +has to confirming the reset because those changes would be lost. + +This is useful when you have started work on a feature branch but +realize it's all crap and want to start over. + +When resetting to another branch and a prefix argument is used, +then also set the target branch as the upstream of the branch +that is being reset." + (interactive + (let* ((atpoint (magit-branch-at-point)) + (branch (magit-read-local-branch "Reset branch" atpoint))) + (list branch + (magit-completing-read (format "Reset %s to" branch) + (delete branch (magit-list-branch-names)) + nil nil nil 'magit-revision-history + (or (and (not (equal branch atpoint)) atpoint) + (magit-get-upstream-branch branch))) + (magit-branch-arguments) + current-prefix-arg))) + (unless (member "--force" args) + (setq args (cons "--force" args))) + (if (equal branch (magit-get-current-branch)) + (if (and (magit-anything-modified-p) + (not (yes-or-no-p "Uncommitted changes will be lost. Proceed?"))) + (user-error "Abort") + (magit-reset-hard to) + (when (and set-upstream (magit-branch-p to)) + (magit-set-branch*merge/remote branch to))) + (magit-branch branch to args))) + +;;;###autoload +(defun magit-branch-delete (branches &optional force) + "Delete one or multiple branches. +If the region marks multiple branches, then offer to delete +those, otherwise prompt for a single branch to be deleted, +defaulting to the branch at point." + (interactive + (let ((branches (magit-region-values 'branch)) + (force current-prefix-arg)) + (if (if (> (length branches) 1) + (magit-confirm t nil "Delete %i branches" branches) + (setq branches + (list (magit-read-branch (if current-prefix-arg + "Force delete branch" + "Delete branch") + (magit-get-previous-branch))))) + (unless force + (--when-let (-intersection + (-union (magit-list-unmerged-branches) + (magit-list-unmerged-to-upstream-branches)) + branches) + (if (magit-confirm 'delete-unmerged-branch + "Delete unmerged branch %s" + "Delete %i unmerged branches" it) + (setq force t) + (or (setq branches (-difference branches it)) + (user-error "Abort"))))) + (user-error "Abort")) + (list branches force))) + (let* ((refs (-map #'magit-ref-fullname branches)) + (ambiguous (--filter (not it) refs))) + (when ambiguous + (user-error + "%s ambiguous. Please cleanup using git directly." + (let ((len (length ambiguous))) + (cond + ((= len 1) + (format "%s is" (--first (not (magit-ref-fullname it)) branches))) + ((= len (length refs)) + (format "These %s names are" len)) + (t + (format "%s of these names are" len)))))) + (cond + ((string-match "^refs/remotes/\\([^/]+\\)" (car refs)) + (let* ((remote (match-string 1 (car refs))) + (offset (1+ (length remote)))) + (magit-run-git-async + "push" remote (--map (concat ":" (substring it offset)) branches)))) + ((> (length branches) 1) + (magit-run-git "branch" (if force "-D" "-d") + (delete (magit-get-current-branch) branches))) + (t ; And now for something completely different. + (let* ((branch (car branches)) + (prompt (format "Branch %s is checked out. " branch))) + (when (equal branch (magit-get-current-branch)) + (pcase (if (or (equal branch "master") + (not (magit-rev-verify "master"))) + (magit-read-char-case prompt nil + (?d "[d]etach HEAD & delete" 'detach) + (?a "[a]bort" 'abort)) + (magit-read-char-case prompt nil + (?d "[d]etach HEAD & delete" 'detach) + (?c "[c]heckout master & delete" 'master) + (?a "[a]bort" 'abort))) + (`detach (magit-call-git "checkout" "--detach")) + (`master (magit-call-git "checkout" "master")) + (`abort (user-error "Abort"))) + (setq force t)) + (magit-run-git "branch" (if force "-D" "-d") branch)))))) + +(put 'magit-branch-delete 'interactive-only t) + +;;;###autoload +(defun magit-branch-rename (old new &optional force) + "Rename branch OLD to NEW. +With prefix, forces the rename even if NEW already exists. +\n(git branch -m|-M OLD NEW)." + (interactive + (let ((branch (magit-read-local-branch "Rename branch"))) + (list branch + (magit-read-string-ns (format "Rename branch '%s' to" branch)) + current-prefix-arg))) + (unless (string= old new) + (magit-run-git "branch" (if force "-M" "-m") old new))) + +;;;;; Branch Variables + +;;;###autoload +(defun magit-edit-branch*description (branch) + "Edit the description of the current branch. +With a prefix argument edit the description of another branch. + +The description for the branch named NAME is stored in the Git +variable `branch..description'." + (interactive + (list (or (and (not current-prefix-arg) + (magit-get-current-branch)) + (magit-read-local-branch "Edit branch description")))) + (magit-run-git-with-editor "branch" "--edit-description" branch)) + +(defun magit-edit-branch*description-check-buffers () + (and buffer-file-name + (string-match-p "/BRANCH_DESCRIPTION\\'" buffer-file-name) + (add-hook 'with-editor-post-finish-hook + (lambda () + (when (derived-mode-p 'magit-popup-mode) + (magit-refresh-popup-buffer))) + nil t))) + +(add-hook 'find-file-hook 'magit-edit-branch*description-check-buffers) + +(defun magit-format-branch*description () + (let* ((branch (or (magit-get-current-branch) "")) + (width (+ (length branch) 19)) + (var (format "branch.%s.description" branch))) + (concat var " " (make-string (- width (length var)) ?\s) + (-if-let (value (magit-get var)) + (propertize value 'face 'magit-popup-option-value) + (propertize "unset" 'face 'magit-popup-disabled-argument))))) + +;;;###autoload +(defun magit-set-branch*merge/remote (branch upstream) + "Set or unset the upstream of the current branch. +With a prefix argument do so for another branch. + +When the branch in question already has an upstream then simply +unsets it. Invoke this command again to set another upstream. + +Together the Git variables `branch..remote' and +`branch..merge' define the upstream branch of the local +branch named NAME. The value of `branch..remote' is the +name of the upstream remote. The value of `branch..merge' +is the full reference of the upstream branch, on the remote. + +Non-interactively, when UPSTREAM is non-nil, then always set it +as the new upstream, regardless of whether another upstream was +already set. When nil, then always unset." + (interactive + (let ((branch (or (and (not current-prefix-arg) + (magit-get-current-branch)) + (magit-read-local-branch "Change upstream of branch")))) + (list branch (and (not (magit-get-upstream-branch branch)) + (magit-read-upstream-branch))))) + (if upstream + (-let (((remote . merge) (magit-split-branch-name upstream)) + (branch (magit-get-current-branch))) + (magit-call-git "config" (format "branch.%s.remote" branch) remote) + (magit-call-git "config" (format "branch.%s.merge" branch) + (concat "refs/heads/" merge))) + (magit-call-git "branch" "--unset-upstream" branch)) + (when (called-interactively-p 'any) + (magit-refresh))) + +(defun magit-format-branch*merge/remote () + (let* ((branch (or (magit-get-current-branch) "")) + (width (+ (length branch) 20)) + (varM (format "branch.%s.merge" branch)) + (varR (format "branch.%s.remote" branch)) + (face (if (equal (magit-get varR) ".") + 'magit-branch-local + 'magit-branch-remote))) + (concat varM (make-string (- width (length varM)) ?\s) + (-if-let (value (magit-get varM)) + (propertize value 'face face) + (propertize "unset" 'face 'magit-popup-disabled-argument)) + "\n " varR (make-string (- width (length varR)) ?\s) + (-if-let (value (magit-get varR)) + (propertize value 'face face) + (propertize "unset" 'face 'magit-popup-disabled-argument))))) + +;;;###autoload +(defun magit-cycle-branch*rebase (branch) + "Cycle the value of `branch..rebase' for the current branch. +With a prefix argument cycle the value for another branch. + +The Git variables `branch..rebase' controls whether pulling +into the branch named NAME is done by rebasing that branch onto +the fetched branch or by merging that branch. + +When `true' then pulling is done by rebasing. +When `false' then pulling is done by merging. + +When that variable is undefined then the value of `pull.rebase' +is used instead. It defaults to `false'." + (interactive + (list (or (and (not current-prefix-arg) + (magit-get-current-branch)) + (magit-read-local-branch "Cycle branch..rebase for")))) + (magit-popup-set-variable (format "branch.%s.rebase" branch) + '("true" "false") + "false" "pull.rebase")) + +(defun magit-format-branch*rebase () + (let ((branch (or (magit-get-current-branch) ""))) + (magit-popup-format-variable (format "branch.%s.rebase" branch) + '("true" "false") + "false" "pull.rebase" + (+ (length branch) 20)))) + +;;;###autoload +(defun magit-cycle-branch*pushRemote (branch) + "Cycle the value of `branch..pushRemote' for the current branch. +With a prefix argument cycle the value for another branch. + +The Git variable `branch..pushRemote' specifies the remote +that the branch named NAME is usually pushed to. The value has +to be the name of an existing remote. + +If that variable is undefined, then the value of the Git variable +`remote.pushDefault' is used instead, provided that it is defined, +which by default it is not." + (interactive + (list (or (and (not current-prefix-arg) + (magit-get-current-branch)) + (magit-read-local-branch "Cycle branch..pushRemote for")))) + (magit-popup-set-variable (format "branch.%s.pushRemote" branch) + (magit-list-remotes) + "remote.pushDefault")) + +(defun magit-format-branch*pushRemote () + (let ((branch (or (magit-get-current-branch) ""))) + (magit-popup-format-variable (format "branch.%s.pushRemote" branch) + (magit-list-remotes) + nil "remote.pushDefault" + (+ (length branch) 20)))) + +;;;###autoload +(defun magit-cycle-pull.rebase () + "Cycle the repository-local value of `pull.rebase'. + +The Git variable `pull.rebase' specifies whether pulling is done +by rebasing or by merging. It can be overwritten using the Git +variable `branch..rebase'. + +When `true' then pulling is done by rebasing. +When `false' (the default) then pulling is done by merging." + (interactive) + (magit-popup-set-variable "pull.rebase" '("true" "false") "false")) + +(defun magit-format-pull.rebase () + (magit-popup-format-variable "pull.rebase" '("true" "false") "false" nil 19)) + +;;;###autoload +(defun magit-cycle-remote.pushDefault () + "Cycle the repository-local value of `remote.pushDefault'. + +The Git variable `remote.pushDefault' specifies the remote that +local branches are usually pushed to. It can be overwritten +using the Git variable `branch..pushRemote'." + (interactive) + (magit-popup-set-variable "remote.pushDefault" (magit-list-remotes))) + +(defun magit-format-remote.pushDefault () + (magit-popup-format-variable "remote.pushDefault" + (magit-list-remotes) nil nil 19)) + +;;;###autoload +(defun magit-cycle-branch*autoSetupMerge () + "Cycle the repository-local value of `branch.autoSetupMerge'. + +The Git variable `branch.autoSetupMerge' under what circumstances +creating a branch (named NAME) should result in the variables +`branch..merge' and `branch..remote' being set +according to the starting point used to create the branch. If +the starting point isn't a branch, then these variables are never +set. + +When `always' then the variables are set regardless of whether +the starting point is a local or a remote branch. + +When `true' (the default) then the variable are set when the +starting point is a remote branch, but not when it is a local +branch. + +When `false' then the variables are never set." + (interactive) + (magit-popup-set-variable "branch.autoSetupMerge" + '("always" "true" "false") "true")) + +(defun magit-format-branch*autoSetupMerge () + (magit-popup-format-variable "branch.autoSetupMerge" + '("always" "true" "false") "true" nil 23)) + +;;;###autoload +(defun magit-cycle-branch*autoSetupRebase () + "Cycle the repository-local value of `branch.autoSetupRebase'. + +The Git variable `branch.autoSetupRebase' specifies whether +creating a branch (named NAME) should result in the variable +`branch..rebase' being set to `true'. + +When `always' then the variable is set regardless of whether the +starting point is a local or a remote branch. + +When `local' then the variable are set when the starting point +is a local branch, but not when it is a remote branch. + +When `remote' then the variable are set when the starting point +is a remote branch, but not when it is a local branch. + +When `never' (the default) then the variable is never set." + (interactive) + (magit-popup-set-variable "branch.autoSetupRebase" + '("always" "local" "remote" "never") "never")) + +(defun magit-format-branch*autoSetupRebase () + (magit-popup-format-variable "branch.autoSetupRebase" + '("always" "local" "remote" "never") + "never" nil 23)) + +;;;; Merge + +;;;###autoload (autoload 'magit-merge-popup "magit" nil t) +(magit-define-popup magit-merge-popup + "Popup console for merge commands." + 'magit-commands + :man-page "git-merge" + :switches '((?f "Fast-forward only" "--ff-only") + (?n "No fast-forward" "--no-ff") + (?s "Squash" "--squash")) + :options '((?s "Strategy" "--strategy=")) + :actions '((?m "Merge" magit-merge) + (?e "Merge and edit message" magit-merge-editmsg) + (?p "Preview merge" magit-merge-preview) + (?n "Merge but don't commit" magit-merge-nocommit)) + :sequence-actions '((?m "Commit merge" magit-commit) + (?a "Abort merge" magit-merge-abort)) + :sequence-predicate 'magit-merge-state + :default-action 'magit-merge + :max-action-columns 2) + +;;;###autoload +(defun magit-merge (rev &optional args nocommit) + "Merge commit REV into the current branch; using default message. + +Unless there are conflicts or a prefix argument is used create a +merge commit using a generic commit message and without letting +the user inspect the result. With a prefix argument pretend the +merge failed to give the user the opportunity to inspect the +merge. + +\(git merge --no-edit|--no-commit [ARGS] REV)" + (interactive (list (magit-read-other-branch-or-commit "Merge") + (magit-merge-arguments) + current-prefix-arg)) + (magit-merge-assert) + (magit-run-git "merge" (if nocommit "--no-commit" "--no-edit") args rev)) + +;;;###autoload +(defun magit-merge-editmsg (rev &optional args) + "Merge commit REV into the current branch; and edit message. +Perform the merge and prepare a commit message but let the user +edit it. +\n(git merge --edit [ARGS] rev)" + (interactive (list (magit-read-other-branch-or-commit "Merge") + (magit-merge-arguments))) + (magit-merge-assert) + (with-editor "GIT_EDITOR" + (let ((magit-process-popup-time -1)) + (magit-run-git-async "merge" "--edit" args rev)))) + +;;;###autoload +(defun magit-merge-nocommit (rev &optional args) + "Merge commit REV into the current branch; pretending it failed. +Pretend the merge failed to give the user the opportunity to +inspect the merge and change the commit message. +\n(git merge --no-commit [ARGS] rev)" + (interactive (list (magit-read-other-branch-or-commit "Merge") + (magit-merge-arguments))) + (magit-merge-assert) + (magit-run-git "merge" "--no-commit" args rev)) + +;;;###autoload +(defun magit-merge-preview (rev) + "Preview result of merging REV into the current branch." + (interactive (list (magit-read-other-branch-or-commit "Preview merge"))) + (magit-mode-setup #'magit-merge-preview-mode rev)) + +(define-derived-mode magit-merge-preview-mode magit-diff-mode "Magit Merge" + "Mode for previewing a merge." + :group 'magit-diff + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-merge-preview-refresh-buffer (rev) + (let* ((branch (magit-get-current-branch)) + (head (or branch (magit-rev-verify "HEAD")))) + (setq header-line-format + (propertize (format "Preview merge of %s into %s" + rev (or branch "HEAD")) + 'face 'magit-header-line)) + (magit-insert-section (diffbuf) + (magit-git-wash #'magit-diff-wash-diffs + "merge-tree" (magit-git-string "merge-base" head rev) head rev)))) + +;;;###autoload +(defun magit-merge-abort () + "Abort the current merge operation. +\n(git merge --abort)" + (interactive) + (if (file-exists-p (magit-git-dir "MERGE_HEAD")) + (when (magit-confirm 'abort-merge) + (magit-run-git-async "merge" "--abort")) + (user-error "No merge in progress"))) + +(defun magit-checkout-stage (file arg) + "During a conflict checkout and stage side, or restore conflict." + (interactive + (let ((file (magit-completing-read "Checkout file" + (magit-tracked-files) nil nil nil + 'magit-read-file-hist + (magit-current-file)))) + (cond ((member file (magit-unmerged-files)) + (list file (magit-checkout-read-stage file))) + ((yes-or-no-p (format "Restore conflicts in %s? " file)) + (list file "--merge")) + (t + (user-error "Quit"))))) + (pcase (cons arg (cddr (car (magit-file-status file)))) + ((or `("--ours" ?D ,_) + `("--theirs" ,_ ?D)) + (magit-run-git "rm" "--" file)) + (_ (if (equal arg "--merge") + ;; This fails if the file was deleted on one + ;; side. And we cannot do anything about it. + (magit-run-git "checkout" "--merge" "--" file) + (magit-call-git "checkout" arg "--" file) + (magit-run-git "add" "-u" "--" file))))) + +(defun magit-merge-state () + (file-exists-p (magit-git-dir "MERGE_HEAD"))) + +(defun magit-merge-assert () + (or (not (magit-anything-modified-p)) + (magit-confirm 'merge-dirty + "Merging with dirty worktree is risky. Continue") + (user-error "Abort"))) + +(defun magit-checkout-read-stage (file) + (magit-read-char-case (format "For %s checkout: " file) t + (?o "[o]ur stage" "--ours") + (?t "[t]heir stage" "--theirs") + (?c "[c]onflict" "--merge"))) + +(defun magit-insert-merge-log () + "Insert section for the on-going merge. +Display the heads that are being merged. +If no merge is in progress, do nothing." + (-when-let (heads (mapcar 'magit-get-shortname + (magit-file-lines (magit-git-dir "MERGE_HEAD")))) + (magit-insert-section (commit (car heads)) + (magit-insert-heading + (format "Merging %s:" (mapconcat 'identity heads ", "))) + (magit-insert-log + (concat (magit-git-string "merge-base" "--octopus" "HEAD" (car heads)) + ".." (car heads)) + (let ((args magit-log-section-arguments)) + (unless (member "--decorate=full" magit-log-section-arguments) + (push "--decorate=full" args)) + args))))) + +;;;; Reset + +;;;###autoload +(defun magit-reset-index (commit) + "Reset the index to COMMIT. +Keep the head and working tree as-is, so if COMMIT refers to the +head this effectively unstages all changes. +\n(git reset COMMIT)" + (interactive (list (magit-read-branch-or-commit "Reset index to"))) + (magit-reset-internal nil commit ".")) + +;;;###autoload +(defun magit-reset (commit &optional hard) + "Reset the head and index to COMMIT, but not the working tree. +With a prefix argument also reset the working tree. +\n(git reset --mixed|--hard COMMIT)" + (interactive (list (magit-read-branch-or-commit + (if current-prefix-arg + "Hard reset to" + "Reset head to")) + current-prefix-arg)) + (magit-reset-internal (if hard "--hard" "--mixed") commit)) + +;;;###autoload +(defun magit-reset-head (commit) + "Reset the head and index to COMMIT, but not the working tree. +\n(git reset --mixed COMMIT)" + (interactive (list (magit-read-branch-or-commit "Reset head to"))) + (magit-reset-internal "--mixed" commit)) + +;;;###autoload +(defun magit-reset-soft (commit) + "Reset the head to COMMIT, but not the index and working tree. +\n(git reset --soft REVISION)" + (interactive (list (magit-read-branch-or-commit "Soft reset to"))) + (magit-reset-internal "--soft" commit)) + +;;;###autoload +(defun magit-reset-hard (commit) + "Reset the head, index, and working tree to COMMIT. +\n(git reset --hard REVISION)" + (interactive (list (magit-read-branch-or-commit "Hard reset to"))) + (magit-reset-internal "--hard" commit)) + +(defun magit-reset-internal (arg commit &optional path) + (when (and (not (member arg '("--hard" nil))) + (equal (magit-rev-parse commit) + (magit-rev-parse "HEAD~"))) + (with-temp-buffer + (magit-git-insert "show" "-s" "--format=%B" "HEAD") + (when git-commit-major-mode + (funcall git-commit-major-mode)) + (git-commit-setup-font-lock) + (git-commit-save-message))) + (let ((cmd (if (and (equal commit "HEAD") (not arg)) "unstage" "reset"))) + (magit-wip-commit-before-change nil (concat " before " cmd)) + (magit-run-git "reset" arg commit "--" path) + (when (equal cmd "unstage") + (magit-wip-commit-after-apply nil " after unstage")))) + +;;;; Files + +(defun magit-file-rename (file newname) + "Rename the FILE to NEWNAME. +If FILE isn't tracked in Git fallback to using `rename-file'." + (interactive + (let* ((file (magit-read-file "Rename file")) + (newname (read-file-name (format "Rename %s to file: " file)))) + (list (expand-file-name file (magit-toplevel)) + (expand-file-name newname)))) + (if (magit-file-tracked-p file) + (let ((oldbuf (get-file-buffer file))) + (when (and oldbuf (buffer-modified-p oldbuf)) + (user-error "Save %s before moving it" file)) + (when (file-exists-p newname) + (user-error "%s already exists" newname)) + (magit-run-git "mv" file newname) + (when oldbuf + (with-current-buffer oldbuf + (let ((buffer-read-only buffer-read-only)) + (set-visited-file-name newname)) + (if (fboundp 'vc-refresh-state) + (vc-refresh-state) + (with-no-warnings + (vc-find-file-hook)))))) + (rename-file file newname current-prefix-arg) + (magit-refresh))) + +(defun magit-file-untrack (file) + "Untrack FILE. +Stop tracking FILE in Git but do not remove it from the working +tree." + (interactive (list (magit-read-tracked-file "Untrack file"))) + (magit-run-git "rm" "--cached" "--" file)) + +(defun magit-file-delete (file &optional force) + "Delete FILE. +With a prefix argument FORCE do so even when FILE has uncommitted +changes. + +If FILE isn't tracked in Git fallback to using `delete-file'." + (interactive (list (magit-read-file "Delete file"))) + (if (magit-file-tracked-p file) + (magit-run-git "rm" (and force "--force") "--" file) + (delete-file (expand-file-name file (magit-toplevel)) t) + (magit-refresh))) + +(defun magit-read-tracked-file (prompt) + (magit-read-file prompt t)) + +(defun magit-read-file (prompt &optional tracked-only) + (let ((choices (nconc (magit-list-files) + (unless tracked-only (magit-untracked-files))))) + (magit-completing-read prompt choices nil t nil nil + (car (member (or (magit-section-when (file)) + (magit-file-relative-name + nil tracked-only)) + choices))))) + +(defun magit-read-files (prompt initial-contents) + (mapconcat 'identity + (completing-read-multiple (or prompt "File,s: ") + (magit-list-files) + nil nil initial-contents) ",")) + +(defun magit-read-file-choice (prompt files &optional error default) + "Read file from FILES. + +If FILES has only one member, return that instead of prompting. +If FILES has no members, give a user error. ERROR can be given +to provide a more informative error. + +If DEFAULT is non-nil, use this as the default value instead of +`magit-current-file'." + (pcase (length files) + (0 (user-error (or error "No file choices"))) + (1 (car files)) + (_ (magit-completing-read + prompt files nil t nil 'magit-read-file-hist + (car (member (or default (magit-current-file)) files)))))) + +;;; Miscellaneous +;;;; Tag + +;;;###autoload (autoload 'magit-tag-popup "magit" nil t) +(magit-define-popup magit-tag-popup + "Popup console for tag commands." + 'magit-commands + :man-page "git-tag" + :switches '((?a "Annotate" "--annotate") + (?s "Sign" "--sign") + (?f "Force" "--force")) + :actions '((?t "Create" magit-tag) + (?k "Delete" magit-tag-delete) + (?p "Prune" magit-tag-prune)) + :default-action 'magit-tag) + +;;;###autoload +(defun magit-tag (name rev &optional args) + "Create a new tag with the given NAME at REV. +With a prefix argument annotate the tag. +\n(git tag [--annotate] NAME REV)" + (interactive (list (magit-read-tag "Tag name") + (magit-read-branch-or-commit "Place tag on") + (let ((args (magit-tag-arguments))) + (when current-prefix-arg + (cl-pushnew "--annotate" args)) + args))) + (magit-run-git-with-editor "tag" args name rev)) + +;;;###autoload +(defun magit-tag-delete (tags) + "Delete one or more tags. +If the region marks multiple tags (and nothing else), then offer +to delete those, otherwise prompt for a single tag to be deleted, +defaulting to the tag at point. +\n(git tag -d TAGS)" + (interactive (list (--if-let (magit-region-values 'tag) + (magit-confirm t nil "Delete %i tags" it) + (magit-read-tag "Delete tag" t)))) + (magit-run-git "tag" "-d" tags)) + +(defun magit-tag-prune (tags remote-tags remote) + "Offer to delete tags missing locally from REMOTE, and vice versa." + (interactive + (let* ((remote (magit-read-remote "Prune tags using remote")) + (tags (magit-list-tags)) + (rtags (prog2 (message "Determining remote tags...") + (magit-remote-list-tags remote) + (message "Determining remote tags...done"))) + (ltags (-difference tags rtags)) + (rtags (-difference rtags tags))) + (unless (or ltags rtags) + (message "Same tags exist locally and remotely")) + (unless (magit-confirm t "Delete %s locally" + "Delete %i tags locally" ltags) + (setq ltags nil)) + (unless (magit-confirm t "Delete %s from remote" + "Delete %i tags from remote" rtags) + (setq rtags nil)) + (list ltags rtags remote))) + (when tags + (magit-call-git "tag" "-d" tags)) + (when remote-tags + (magit-run-git-async "push" remote (--map (concat ":" it) remote-tags)))) + +;;;; Notes + +;;;###autoload (autoload 'magit-notes-popup "magit" nil t) +(magit-define-popup magit-notes-popup + "Popup console for notes commands." + 'magit-commands + :man-page "git-tag" + :switches '("Switch for prune" + (?n "Dry run" "--dry-run")) + :options '("Option for edit and remove" + (?r "Manipulate ref" "--ref=" magit-notes-popup-read-ref) + "Option for merge" + (?s "Merge strategy" "--strategy=")) + :actions '((?T "Edit" magit-notes-edit) + (?r "Remove" magit-notes-remove) + (?m "Merge" magit-notes-merge) + (?p "Prune" magit-notes-prune) + (?s "Set ref" magit-notes-set-ref) + (?S "Set display refs" magit-notes-set-display-refs)) + :sequence-actions '((?c "Commit merge" magit-notes-merge-commit) + (?a "Abort merge" magit-notes-merge-abort)) + :sequence-predicate 'magit-notes-merging-p + :default-action 'magit-notes-edit) + +(defun magit-notes-edit (commit &optional ref) + "Edit the note attached to COMMIT. +REF is the notes ref used to store the notes. + +Interactively or when optional REF is nil use the value of Git +variable `core.notesRef' or \"refs/notes/commits\" if that is +undefined." + (interactive (magit-notes-read-args "Edit notes")) + (magit-run-git-with-editor "notes" (and ref (concat "--ref=" ref)) + "edit" commit)) + +(defun magit-notes-remove (commit &optional ref) + "Remove the note attached to COMMIT. +REF is the notes ref from which the note is removed. + +Interactively or when optional REF is nil use the value of Git +variable `core.notesRef' or \"refs/notes/commits\" if that is +undefined." + (interactive (magit-notes-read-args "Remove notes")) + (magit-run-git-with-editor "notes" (and ref (concat "--ref=" ref)) + "remove" commit)) + +(defun magit-notes-merge (ref) + "Merge the notes ref REF into the current notes ref. + +The current notes ref is the value of Git variable +`core.notesRef' or \"refs/notes/commits\" if that is undefined. + +When there are conflict, then they have to resolved in the +temporary worktree \".git/NOTES_MERGE_WORKTREE\". When +done use `magit-notes-merge-commit' to finish. To abort +use `magit-notes-merge-abort'." + (interactive (list (magit-read-string-ns "Merge reference"))) + (magit-run-git-with-editor "notes" "merge" ref)) + +(defun magit-notes-merge-commit () + "Commit the current notes ref merge. +Also see `magit-notes-merge'." + (interactive) + (magit-run-git-with-editor "notes" "merge" "--commit")) + +(defun magit-notes-merge-abort () + "Abort the current notes ref merge. +Also see `magit-notes-merge'." + (interactive) + (magit-run-git-with-editor "notes" "merge" "--abort")) + +(defun magit-notes-prune (&optional dry-run) + "Remove notes about unreachable commits." + (interactive (list (and (member "--dry-run" (magit-notes-arguments)) t))) + (when dry-run + (magit-process-buffer)) + (magit-run-git-with-editor "notes" "prune" (and dry-run "--dry-run"))) + +(defun magit-notes-set-ref (ref &optional global) + "Set the current notes ref to REF. +The ref is made current by setting the value of the Git variable +`core.notesRef'. With a prefix argument GLOBAL change the global +value, else the value in the current repository. When this is +undefined, then \"refs/notes/commit\" is used. + +Other `magit-notes-*' commands, as well as the sub-commands +of Git's `note' command, default to operate on that ref." + (interactive + (list (magit-completing-read "Set notes ref" + (nconc (list "refs/" "refs/notes/") + (magit-list-notes-refnames)) + nil nil + (--when-let (magit-get "core.notesRef") + (if (string-match "^refs/notes/\\(.+\\)" it) + (match-string 1 it) + it))) + current-prefix-arg)) + (if ref + (magit-run-git "config" (and global "--global") "core.notesRef" + (if (string-prefix-p "refs/" ref) + ref + (concat "refs/notes/" ref))) + (magit-run-git "config" (and global "--global") + "--unset" "core.notesRef"))) + +(defun magit-notes-set-display-refs (refs &optional global) + "Set notes refs to be display in addition to \"core.notesRef\". +REFS is a colon separated list of notes refs. The values are +stored in the Git variable `notes.displayRef'. With a prefix +argument GLOBAL change the global values, else the values in +the current repository." + (interactive + (list (magit-completing-read "Set additional notes ref(s)" + (nconc (list "refs/" "refs/notes/") + (magit-list-notes-refnames)) + nil nil + (mapconcat #'identity + (magit-get-all "notes.displayRef") + ":")) + current-prefix-arg)) + (when (and refs (atom refs)) + (setq refs (split-string refs ":"))) + (when global + (setq global "--global")) + (magit-git-success "config" "--unset-all" global "notes.displayRef") + (dolist (ref refs) + (magit-call-git "config" "--add" global "notes.displayRef" ref)) + (magit-refresh)) + +(defun magit-notes-read-args (prompt) + (list (magit-read-branch-or-commit prompt) + (--when-let (--first (string-match "^--ref=\\(.+\\)" it) + (magit-notes-arguments)) + (match-string 1 it)))) + +(defun magit-notes-popup-read-ref (prompt &optional initial-input) + (magit-completing-read prompt (nconc (list "refs/" "refs/notes/") + (magit-list-notes-refnames)) + nil nil initial-input)) + +(defun magit-notes-merging-p () + (let ((dir (magit-git-dir "NOTES_MERGE_WORKTREE"))) + (and (file-directory-p dir) + (directory-files dir nil "^[^.]")))) + +;;;; File Mode + +(defvar magit-file-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "\C-xg" 'magit-status) + (define-key map "\C-x\M-g" 'magit-dispatch-popup) + (define-key map "\C-c\M-g" 'magit-file-popup) + map) + "Keymap for `magit-file-mode'.") + +(magit-define-popup magit-file-popup + "Popup console for Magit commands in file-visiting buffers." + :actions '((?s "Stage" magit-stage-file) + (?l "Log" magit-log-buffer-file) + (?c "Commit" magit-commit-popup) + (?u "Unstage" magit-unstage-file) + (?b "Blame" magit-blame-popup) nil nil + (?p "Find blob" magit-blob-previous)) + :max-action-columns 3) + +(defvar magit-file-mode-lighter "") + +(define-minor-mode magit-file-mode + "Enable some Magit features in file-visiting buffers. + +Currently this only adds the following key bindings. +\n\\{magit-file-mode-map}" + :package-version '(magit . "2.2.0") + :lighter magit-file-mode-lighter + :keymap magit-file-mode-map) + +(defun magit-file-mode-turn-on () + (and buffer-file-name + (ignore-errors (magit-inside-worktree-p)) + (magit-file-mode))) + +;;;###autoload +(define-globalized-minor-mode global-magit-file-mode + magit-file-mode magit-file-mode-turn-on + :package-version '(magit . "2.2.0") + :group 'magit-modes) + +;;;; Blob Mode + +(defvar magit-blob-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "n" 'magit-blob-next) + (define-key map "p" 'magit-blob-previous) + (define-key map "q" 'magit-kill-this-buffer) + map) + "Keymap for `magit-blob-mode'.") + +(define-minor-mode magit-blob-mode + "Enable some Magit features in blob-visiting buffers. + +Currently this only adds the following key bindings. +\n\\{magit-blob-mode-map}" + :package-version '(magit . "2.3.0")) + +(defun magit-blob-next () + "Visit the next blob which modified the current file." + (interactive) + (if magit-buffer-file-name + (magit-blob-visit (or (magit-blob-successor magit-buffer-revision + magit-buffer-file-name) + magit-buffer-file-name) + (line-number-at-pos)) + (if (buffer-file-name (buffer-base-buffer)) + (user-error "You have reached the end of time") + (user-error "Buffer isn't visiting a file or blob")))) + +(defun magit-blob-previous () + "Visit the previous blob which modified the current file." + (interactive) + (-if-let (file (or magit-buffer-file-name + (buffer-file-name (buffer-base-buffer)))) + (--if-let (magit-blob-ancestor magit-buffer-revision file) + (magit-blob-visit it (line-number-at-pos)) + (user-error "You have reached the beginning of time")) + (user-error "Buffer isn't visiting a file or blob"))) + +(defun magit-blob-visit (blob-or-file line) + (if (stringp blob-or-file) + (find-file blob-or-file) + (-let [(rev file) blob-or-file] + (magit-find-file rev file) + (let ((str (magit-rev-format "%ct%s" rev))) + (message "%s (%s ago)" (substring str 10) + (magit-format-duration + (abs (truncate (- (float-time) + (string-to-number + (substring str 0 10))))) + magit-duration-spec))))) + (goto-char (point-min)) + (forward-line (1- line))) + +(defun magit-blob-ancestor (rev file) + (let ((lines (magit-with-toplevel + (magit-git-lines "log" "-2" "--format=%H" "--name-only" + "--follow" (or rev "HEAD") "--" file)))) + (if rev (cddr lines) (butlast lines 2)))) + +(defun magit-blob-successor (rev file) + (let ((lines (magit-with-toplevel + (magit-git-lines "log" "--format=%H" "--name-only" "--follow" + "HEAD" "--" file)))) + (catch 'found + (while lines + (if (equal (nth 2 lines) rev) + (throw 'found (list (nth 0 lines) (nth 1 lines))) + (setq lines (nthcdr 2 lines))))))) + +(defun magit-kill-this-buffer () + "Kill the current buffer." + (interactive) + (kill-buffer (current-buffer))) + +;;;; Dispatch Popup + +;;;###autoload (autoload 'magit-dispatch-popup "magit" nil t) +(magit-define-popup magit-dispatch-popup + "Popup console for dispatching other popups." + 'magit-commands nil nil + :actions '("Popup and dwim commands" + (?A "Cherry-picking" magit-cherry-pick-popup) + (?b "Branching" magit-branch-popup) + (?B "Bisecting" magit-bisect-popup) + (?c "Committing" magit-commit-popup) + (?d "Diffing" magit-diff-popup) + (?D "Change diffs" magit-diff-refresh-popup) + (?e "Ediff dwimming" magit-ediff-dwim) + (?E "Ediffing" magit-ediff-popup) + (?f "Fetching" magit-fetch-popup) + (?F "Pulling" magit-pull-popup) + (?l "Logging" magit-log-popup) + (?m "Merging" magit-merge-popup) + (?M "Remoting" magit-remote-popup) + (?o "Submodules" magit-submodule-popup) + (?P "Pushing" magit-push-popup) + (?r "Rebasing" magit-rebase-popup) + (?t "Tagging" magit-tag-popup) + (?T "Notes" magit-notes-popup) + (?V "Reverting" magit-revert-popup) + (?w "Apply patches" magit-am-popup) + (?W "Format patches" magit-patch-popup) + (?y "Show Refs" magit-show-refs-popup) + (?z "Stashing" magit-stash-popup) + (?! "Running" magit-run-popup) + "Applying changes" + (?a "Apply" magit-apply) + (?s "Stage" magit-stage) + (?u "Unstage" magit-unstage) + nil + (?v "Reverse" magit-reverse) + (?S "Stage all" magit-stage-modified) + (?U "Unstage all" magit-unstage-all) + nil + (?k "Discard" magit-discard) + "\ + g refresh current buffer + TAB toggle section at point + RET visit thing at point + + C-h m show all key bindings" nil) + :max-action-columns 4) + +;;;; Git Popup + +(defvar magit-git-command-history nil) + +;;;###autoload (autoload 'magit-run-popup "magit" nil t) +(magit-define-popup magit-run-popup + "Popup console for running raw Git commands." + 'magit-commands nil nil + :actions '((?! "Git Subcommand (in topdir)" magit-git-command-topdir) + (?k "Gitk" magit-run-gitk) + (?p "Git Subcommand (in pwd)" magit-git-command) + (?a "Gitk --all" magit-run-gitk-all) + (?s "Shell command (in topdir)" magit-shell-command-topdir) + (?b "Gitk --branches" magit-run-gitk-branches) + (?S "Shell command (in pwd)" magit-shell-command) + (?g "Git Gui" magit-run-git-gui)) + :default-action 'magit-git-command + :max-action-columns 2) + +;;;###autoload +(defun magit-git-command (args directory) + "Execute a Git subcommand asynchronously, displaying the output. +With a prefix argument run Git in the root of the current +repository, otherwise in `default-directory'." + (interactive (magit-read-shell-command "Git subcommand (pwd: %s)")) + (require 'eshell) + (with-temp-buffer + (insert args) + (setq args (mapcar 'eval (eshell-parse-arguments (point-min) + (point-max)))) + (setq default-directory directory) + (let ((magit-git-global-arguments + ;; A human will want globbing by default. + (remove "--literal-pathspecs" + magit-git-global-arguments))) + (magit-run-git-async args))) + (magit-process-buffer)) + +;;;###autoload +(defun magit-git-command-topdir (args directory) + "Execute a Git subcommand asynchronously, displaying the output. +Run Git in the top-level directory of the current repository. +\n(fn)" ; arguments are for internal use + (interactive (magit-read-shell-command "Git subcommand (pwd: %s)" t)) + (magit-git-command args directory)) + +;;;###autoload +(defun magit-shell-command (args directory) + "Execute a shell command asynchronously, displaying the output. +With a prefix argument run the command in the root of the current +repository, otherwise in `default-directory'." + (interactive (magit-read-shell-command "Shell command (pwd: %s)")) + (require 'eshell) + (with-temp-buffer + (insert args) + (setq args (mapcar 'eval (eshell-parse-arguments (point-min) + (point-max)))) + (setq default-directory directory) + (apply #'magit-start-process (car args) nil (cdr args))) + (magit-process-buffer)) + +;;;###autoload +(defun magit-shell-command-topdir (args directory) + "Execute a shell command asynchronously, displaying the output. +Run the command in the top-level directory of the current repository. +\n(fn)" ; arguments are for internal use + (interactive (magit-read-shell-command "Shell command (pwd: %s)" t)) + (magit-shell-command args directory)) + +(defun magit-read-shell-command (prompt &optional root) + (let ((dir (if (or root current-prefix-arg) + (or (magit-toplevel) + (user-error "Not inside a Git repository")) + default-directory))) + (list (magit-read-string (format prompt (abbreviate-file-name dir)) + nil 'magit-git-command-history) + dir))) + +;;;; Read Repository + +(defun magit-read-repository (&optional read-directory-name) + "Read a Git repository in the minibuffer, with completion. + +The completion choices are the basenames of top-levels of +repositories found in the directories specified by option +`magit-repository-directories'. In case of name conflicts +the basenames are prefixed with the name of the respective +parent directories. The returned value is the actual path +to the selected repository. + +With prefix argument simply read a directory name using +`read-directory-name'." + (if (and (not read-directory-name) magit-repository-directories) + (let* ((repos (magit-list-repos-uniquify + (--map (cons (file-name-nondirectory it) it) + (magit-list-repos)))) + (reply (magit-completing-read "Git repository" repos))) + (file-name-as-directory + (or (cdr (assoc reply repos)) + (if (file-directory-p reply) + (expand-file-name reply) + (user-error "Not a repository or a directory: %s" reply))))) + (file-name-as-directory + (read-directory-name "Git repository: " + (or (magit-toplevel) default-directory))))) + +(defun magit-list-repos () + (--mapcat (magit-list-repos-1 it magit-repository-directories-depth) + magit-repository-directories)) + +(defun magit-list-repos-1 (directory depth) + (cond ((file-readable-p (expand-file-name ".git" directory)) + (list directory)) + ((and (> depth 0) (file-accessible-directory-p directory)) + (--mapcat (when (file-directory-p it) + (magit-list-repos-1 it (1- depth))) + (directory-files directory t "^[^.]" t))))) + +(defun magit-list-repos-uniquify (alist) + (let (result (dict (make-hash-table :test 'equal))) + (dolist (a (delete-dups alist)) + (puthash (car a) (cons (cdr a) (gethash (car a) dict)) dict)) + (maphash + (lambda (key value) + (if (= (length value) 1) + (push (cons key (car value)) result) + (setq result + (append result + (magit-list-repos-uniquify + (--map (cons (concat + key "\\" + (file-name-nondirectory + (directory-file-name + (substring it 0 (- (length key)))))) + it) + value)))))) + dict) + result)) + +;;;; Revision Stack + +(defvar magit-revision-stack nil) + +(defcustom magit-pop-revision-stack-format + '("[%N: %h] " "%N: %H\n %s\n" "\\[\\([0-9]+\\)[]:]") + "Control how `magit-pop-revision-stack' inserts a revision. + +The command `magit-pop-revision-stack' inserts a representation +of the revision last pushed to the `magit-revision-stack' into +the current buffer. It inserts text at point and/or near the end +of the buffer, and removes the consumed revision from the stack. + +The entries on the stack have the format (HASH TOPLEVEL) and this +option has the format (POINT-FORMAT EOB-FORMAT INDEX-REGEXP), all +of which may be nil or a string (though either one of EOB-FORMAT +or POINT-FORMAT should be a string, and if INDEX-REGEXP is +non-nil, then the two formats should be too). + +First INDEX-REGEXP is used to find the previously inserted entry, +by searching backward from point. The first submatch must match +the index number. That number is incremented by one, and becomes +the index number of the entry to be inserted. If you don't want +to number the inserted revisions, then use nil for INDEX-REGEXP. + +If INDEX-REGEXP is non-nil then both POINT-FORMAT and EOB-FORMAT +should contain \"%N\", which is replaced with the number that was +determined in the previous step. + +Both formats, if non-nil and after removing %N, are then expanded +using `git show --format=FORMAT ...' inside TOPLEVEL. + +The expansion of POINT-FORMAT is inserted at point, and the +expansion of EOB-FORMAT is inserted at the end of the buffer (if +the buffer ends with a comment, then it is inserted right before +that)." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type '(list (choice (string :tag "Insert at point format") + (cons (string :tag "Insert at point format") + (repeat (string :tag "Argument to git show"))) + (const :tag "Don't insert at point" nil)) + (choice (string :tag "Insert at eob format") + (cons (string :tag "Insert at eob format") + (repeat (string :tag "Argument to git show"))) + (const :tag "Don't insert at eob" nil)) + (choice (regexp :tag "Find index regexp") + (const :tag "Don't number entries" nil)))) + +(defun magit-pop-revision-stack (rev toplevel) + "Insert a representation of a revision into the current buffer. + +Pop a revision from the `magit-revision-stack' and insert it into +the current buffer according to `magit-pop-revision-stack-format'. +Revisions can be put on the stack using `magit-copy-section-value' +and `magit-copy-buffer-revision'. + +If the stack is empty or with a prefix argument instead read a +revision in the minibuffer. By using the minibuffer history this +allows selecting an item which was popped earlier or to insert an +arbitrary reference or revision without first pushing it onto the +stack. + +When reading the revision from the minibuffer, then it might not +be possible to guess the correct repository. When this command +is called inside a repository (e.g. while composing a commit +message), then that repository is used. Otherwise (e.g. while +composing an email) then the repository recorded for the top +element of the stack is used (even though we insert another +revision). If not called inside a repository and with an empty +stack, or with two prefix arguments, then read the repository in +the minibuffer too." + (interactive + (if (or current-prefix-arg (not magit-revision-stack)) + (let ((default-directory + (or (and (not (= (prefix-numeric-value current-prefix-arg) 16)) + (or (magit-toplevel) + (cadr (car magit-revision-stack)))) + (magit-read-repository)))) + (list (magit-read-branch-or-commit "Insert revision") + default-directory)) + (push (caar magit-revision-stack) magit-revision-history) + (pop magit-revision-stack))) + (if rev + (-let [(pnt-format eob-format idx-format) magit-pop-revision-stack-format] + (let ((default-directory toplevel) + (idx (and idx-format + (save-excursion + (if (re-search-backward idx-format nil t) + (number-to-string + (1+ (string-to-number (match-string 1)))) + "1")))) + pnt-args eob-args) + (when (listp pnt-format) + (setq pnt-args (cdr pnt-format) + pnt-format (car pnt-format))) + (when (listp eob-format) + (setq eob-args (cdr eob-format) + eob-format (car eob-format))) + (when pnt-format + (when idx-format + (setq pnt-format + (replace-regexp-in-string "%N" idx pnt-format t t))) + (magit-rev-insert-format pnt-format rev pnt-args) + (backward-delete-char 1)) + (when eob-format + (when idx-format + (setq eob-format + (replace-regexp-in-string "%N" idx eob-format t t))) + (save-excursion + (goto-char (point-max)) + (skip-syntax-backward ">s-") + (beginning-of-line) + (if (and comment-start (looking-at comment-start)) + (while (looking-at comment-start) + (forward-line -1)) + (forward-line) + (unless (= (current-column) 0) + (insert ?\n))) + (insert ?\n) + (magit-rev-insert-format eob-format rev eob-args) + (backward-delete-char 1))))) + (user-error "Revision stack is empty"))) + +(define-key git-commit-mode-map + (kbd "C-c C-w") 'magit-pop-revision-stack) + +(defun magit-copy-section-value () + "Save the value of the current section for later use. + +Save the section value to the `kill-ring', and, provided that +the current section is a commit, branch, or tag section, push +the (referenced) revision to the `magit-revision-stack' for use +with `magit-pop-revision-stack'. + +When the current section is a branch or a tag, and a prefix +argument is used, then save the revision at its tip to the +`kill-ring' instead of the reference name. + +When the region is active, then save that to the `kill-ring', +like `kill-ring-save' would, instead of behaving as described +above." + (interactive) + (if (region-active-p) + (copy-region-as-kill (mark) (point) 'region) + (-when-let* ((section (magit-current-section)) + (value (magit-section-value section))) + (magit-section-case + ((branch commit module-commit tag) + (let ((default-directory default-directory) ref) + (magit-section-case + ((branch tag) + (setq ref value)) + (module-commit + (setq default-directory + (file-name-as-directory + (expand-file-name (magit-section-parent-value section) + (magit-toplevel)))))) + (setq value (magit-rev-parse value)) + (push (list value default-directory) magit-revision-stack) + (kill-new (message "%s" (or (and current-prefix-arg ref) + value))))) + (t (kill-new (message "%s" value))))))) + +(defun magit-copy-buffer-revision () + "Save the revision of the current buffer for later use. + +Save the revision shown in the current buffer to the `kill-ring' +and push it to the `magit-revision-stack'. + +This command is mainly intended for use in `magit-revision-mode' +buffers, the only buffers where it is always unambiguous exactly +which revision should be saved. + +Most other Magit buffers usually show more than one revision, in +some way or another, so this command has to select one of them, +and that choice might not always be the one you think would have +been the best pick. + +In such buffers it is often more useful to save the value of +the current section instead, using `magit-copy-section-value'. + +When the region is active, then save that to the `kill-ring', +like `kill-ring-save' would, instead of behaving as described +above." + (interactive) + (if (region-active-p) + (copy-region-as-kill (mark) (point) 'region) + (-when-let (rev (cond ((memq major-mode '(magit-cherry-mode + magit-log-select-mode + magit-reflog-mode + magit-refs-mode + magit-revision-mode + magit-stash-mode + magit-stashes-mode)) + (car magit-refresh-args)) + ((memq major-mode '(magit-diff-mode + magit-log-mode)) + (let ((r (caar magit-refresh-args))) + (if (string-match "\\.\\.\\.?\\(.+\\)" r) + (match-string 1 r) + r))) + ((eq major-mode 'magit-status-mode) "HEAD"))) + (when (magit-rev-verify-commit rev) + (setq rev (magit-rev-parse rev)) + (push (list rev default-directory) magit-revision-stack) + (kill-new (message "%s" rev)))))) + +;;; magit.el ends soon + +(defconst magit-font-lock-keywords + (eval-when-compile + `((,(concat "(\\(magit-define-section-jumper\\)\\_>" + "[ \t'\(]*" + "\\(\\(?:\\sw\\|\\s_\\)+\\)?") + (1 'font-lock-keyword-face) + (2 'font-lock-function-name-face nil t)) + (,(concat "(" (regexp-opt '("magit-insert-section" + "magit-section-case" + "magit-section-when" + "magit-bind-match-strings" + "magit-with-temp-index" + "magit-with-blob" + "magit-with-toplevel") t) + "\\_>") + . 1)))) + +(font-lock-add-keywords 'emacs-lisp-mode magit-font-lock-keywords) + +(defvar magit-version 'undefined + "The version of Magit that you're using. +Use the function by the same name instead of this variable.") + +;;;###autoload +(defun magit-version () + "Return the version of Magit currently in use. +When called interactive also show the used versions of Magit, +Git, and Emacs in the echo area." + (interactive) + (let ((magit-git-global-arguments nil) + (toplib (or load-file-name buffer-file-name)) + debug) + (unless (and toplib + (equal (file-name-nondirectory toplib) "magit.el")) + (setq toplib (locate-library "magit.el"))) + (push toplib debug) + (when toplib + (let* ((topdir (file-name-directory toplib)) + (gitdir (expand-file-name + ".git" (file-name-directory + (directory-file-name topdir)))) + (static (expand-file-name "magit-version.el" topdir))) + (or (progn + (push 'repo debug) + (when (and (file-exists-p gitdir) + ;; It is a repo, but is it the Magit repo? + (file-exists-p + (expand-file-name "../lisp/magit.el" gitdir))) + (push t debug) + ;; Inside the repo the version file should only exist + ;; while running make. + (unless noninteractive + (ignore-errors (delete-file static))) + (setq magit-version + (let ((default-directory topdir)) + (magit-git-string "describe" "--tags" "--dirty"))))) + (progn + (push 'static debug) + (when (file-exists-p static) + (push t debug) + (load-file static) + magit-version)) + (when (featurep 'package) + (push 'elpa debug) + (ignore-errors + (--when-let (assq 'magit package-alist) + (push t debug) + (setq magit-version + (and (fboundp 'package-desc-version) + (package-version-join + (package-desc-version (cadr it))))))))))) + (if (stringp magit-version) + (when (called-interactively-p 'any) + (message "Magit %s, Git %s, Emacs %s" + (or magit-version "(unknown)") + (or (magit-git-version t) "(unknown)") + emacs-version)) + (setq debug (reverse debug)) + (setq magit-version 'error) + (when magit-version + (push magit-version debug)) + (message "Cannot determine Magit's version %S" debug)) + magit-version)) + +(defun magit-startup-asserts () + (let ((version (magit-git-version))) + (when (and version + (version< version magit--minimal-git) + (not (equal (getenv "TRAVIS") "true"))) + (display-warning 'magit (format "\ +Magit requires Git >= %s, you are using %s. + +If this comes as a surprise to you, because you do actually have +a newer version installed, then that probably means that the +older version happens to appear earlier on the `$PATH'. If you +always start Emacs from a shell, then that can be fixed in the +shell's init file. If you start Emacs by clicking on an icon, +or using some sort of application launcher, then you probably +have to adjust the environment as seen by graphical interface. +For X11 something like ~/.xinitrc should work. + +If you use Tramp to work inside remote Git repositories, then you +have to make sure a suitable Git is used on the remote machines +too.\n" magit--minimal-git version) :error))) + (when (version< emacs-version magit--minimal-emacs) + (display-warning 'magit (format "\ +Magit requires Emacs >= %s, you are using %s. + +If this comes as a surprise to you, because you do actually have +a newer version installed, then that probably means that the +older version happens to appear earlier on the `$PATH'. If you +always start Emacs from a shell, then that can be fixed in the +shell's init file. If you start Emacs by clicking on an icon, +or using some sort of application launcher, then you probably +have to adjust the environment as seen by graphical interface. +For X11 something like ~/.xinitrc should work.\n" + magit--minimal-emacs emacs-version) + :error)) + (--each '((magit-log-edit . git-commit) + (git-commit-mode . git-commit) + (git-rebase-mode . git-rebase)) + (when (or (featurep (car it)) (locate-library (symbol-name (car it)))) + (display-warning 'magit (format "%s has to be removed + +Magit is no longer compatible with the library `%s', +which was used in earlier releases. Please remove it, so that +Magit can use the successor `%s' without the obsolete +library getting in the way. Then restart Emacs.\n" + (car it) (car it) (cdr it)) :error)))) + +(defvar magit--remotes-using-recent-git nil) + +(defun magit-tramp-asserts (directory) + (-when-let (remote (file-remote-p directory)) + (unless (member remote magit--remotes-using-recent-git) + (-if-let (version (let ((default-directory directory)) + (magit-git-version))) + (if (version<= magit--minimal-git version) + (push version magit--remotes-using-recent-git) + (display-warning 'magit (format "\ +Magit requires Git >= %s, but on %s the version is %s. + +If multiple Git versions are installed on the host then the +problem might be that TRAMP uses the wrong executable. + +First check the value of `magit-git-executable'. Its value is +used when running git locally as well as when running it on a +remote host. The default value is \"git\", except on Windows +where an absolute path is used for performance reasons. + +If the value already is just \"git\" but TRAMP never-the-less +doesn't use the correct executable, then consult the info node +`(tramp)Remote programs'.\n" magit--minimal-git remote version) :error)) + (display-warning 'magit (format "\ +Magit cannot find Git on %s. + +First check the value of `magit-git-executable'. Its value is +used when running git locally as well as when running it on a +remote host. The default value is \"git\", except on Windows +where an absolute path is used for performance reasons. + +If the value already is just \"git\" but TRAMP never-the-less +doesn't find the executable, then consult the info node +`(tramp)Remote programs'.\n" remote) :error))))) + +(define-obsolete-function-alias 'global-magit-file-buffer-mode + 'global-magit-file-mode "Magit 2.3.0") + +(define-obsolete-function-alias 'magit-insert-head-header + 'magit-insert-head-branch-header "Magit 2.4.0") + +(define-obsolete-function-alias 'magit-insert-upstream-header + 'magit-insert-upstream-branch-header "Magit 2.4.0") + +(define-obsolete-function-alias 'magit-insert-pull-branch-header + 'magit-insert-upstream-branch-header "Magit 2.4.0") + +(provide 'magit) + +(cl-eval-when (load eval) + (require 'magit-sequence) + (require 'magit-commit) + (require 'magit-remote) + (require 'magit-bisect) + (require 'magit-stash) + (require 'magit-blame) + (unless (load "magit-autoloads" t t) + (require 'magit-submodule) + (require 'magit-ediff) + (require 'magit-extras) + (require 'git-rebase))) + +(if after-init-time + (progn (magit-startup-asserts) + (magit-version)) + (add-hook 'after-init-hook #'magit-startup-asserts t) + (add-hook 'after-init-hook #'magit-version t)) + +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: +;;; magit.el ends here diff --git a/elpa/magit-20160223.828/magit.info b/elpa/magit-20160223.828/magit.info new file mode 100644 index 0000000..38c95e2 --- /dev/null +++ b/elpa/magit-20160223.828/magit.info @@ -0,0 +1,164 @@ +This is magit.info, produced by makeinfo version 5.2 from magit.texi. + +Magit is an interface to the version control system Git, implemented as +an Emacs package. Magit aspires to be a complete Git porcelain. While +we cannot (yet) claim that Magit wraps and improves upon each and every +Git command, it is complete enough to allow even experienced Git users +to perform almost all of their daily version control tasks directly from +within Emacs. While many fine Git clients exist, only Magit and Git +itself deserve to be called porcelains. + + Copyright (C) 2015-2016 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document 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. +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Magit: (magit). Using Git from Emacs with Magit. +END-INFO-DIR-ENTRY + + +Indirect: +magit.info-1: 1222 +magit.info-2: 316256 + +Tag Table: +(Indirect) +Node: Top1222 +Node: Introduction5928 +Node: Installation10621 +Node: Updating from an older release10996 +Node: Installing from an Elpa archive12591 +Node: Installing from the Git repository13930 +Node: Post-installation tasks16726 +Node: Getting started18115 +Node: Interface concepts23850 +Node: Modes and Buffers24124 +Node: Switching Buffers25870 +Node: Naming Buffers28934 +Node: Quitting Windows31769 +Node: Automatic Refreshing of Magit Buffers33401 +Node: Automatic Saving of File-Visiting Buffers36169 +Node: Automatic Reverting of File-Visiting Buffers37354 +Node: Risk of Reverting Automatically42350 +Node: Sections44733 +Node: Section movement45674 +Node: Section visibility49601 +Node: Section hooks53192 +Node: Section types and values55473 +Node: Section options56743 +Node: Popup buffers and prefix commands57215 +Node: Completion and confirmation58529 +Node: Running Git61435 +Node: Viewing Git output61671 +Node: Running Git manually62671 +Node: Git executable64797 +Node: Global Git arguments66804 +Node: Inspecting67611 +Node: Status buffer68738 +Node: Status sections71261 +Node: Status header sections76008 +Node: Status options78567 +Node: Logging79291 +Node: Refreshing logs81820 +Node: Log Buffer83205 +Node: Select from log86294 +Node: Reflog87234 +Node: Diffing87712 +Node: Refreshing diffs90524 +Node: Diff buffer93505 +Node: Diff options95407 +Node: Revision buffer97039 +Node: Ediffing97994 +Node: References buffer101043 +Node: References sections105753 +Node: Bisecting106628 +Node: Visiting blobs108124 +Node: Blaming108633 +Node: Manipulating111953 +Node: Repository setup112245 +Node: Staging and unstaging113285 +Node: Staging from file-visiting buffers117180 +Node: Applying118348 +Node: Committing119991 +Node: Initiating a commit120574 +Node: Editing commit messages123886 +Node: Branching134282 +Node: Merging147096 +Node: Rebasing149180 +Node: Editing rebase sequences152128 +Node: Rebase sequence log155162 +Node: Cherry picking161906 +Node: Reverting163512 +Node: Resetting164875 +Node: Stashing166385 +Node: Transferring169530 +Node: Remotes169768 +Node: Fetching171054 +Node: Pulling172148 +Node: Pushing172994 +Node: Creating and sending patches177443 +Node: Applying patches178138 +Node: Miscellaneous179136 +Node: Tagging179427 +Node: Notes180212 +Node: Submodules182737 +Node: Common commands184057 +Node: Wip modes185805 +Node: Minor mode for buffers visiting files192541 +Node: Minor mode for buffers visiting blobs194684 +Node: Customizing195489 +Node: Per-repository configuration197161 +Node: Essential settings198795 +Node: Safety199119 +Node: Performance200952 +Node: Committing Performance207625 +Node: Plumbing208606 +Node: Calling Git209234 +Node: Getting a value from Git210757 +Node: Calling Git for effect213861 +Node: Section plumbing220365 +Node: Creating sections220593 +Node: Section selection224396 +Node: Matching sections226076 +Node: Refreshing buffers231278 +Node: Conventions234413 +Node: Confirmation and completion234590 +Node: Theming Faces235488 +Node: FAQ243639 +Node: Magit is slow244972 +Node: I changed several thousand files at once and now Magit is unusable245173 +Node: I am having problems committing245889 +Node: I don't understand how branching and pushing work246347 +Node: I don't like the key binding in v24246716 +Node: I cannot install the pre-requisites for Magit v2247055 +Node: I am using an Emacs release older than v244247520 +Node: I am using a Git release older than v194249133 +Node: I am using MS Windows and cannot push with Magit250120 +Node: How to install the gitman info manual?250699 +Node: How can I show Git's output?253227 +Node: Expanding a file to show the diff causes it to disappear254039 +Node: Point is wrong in the COMMIT_EDITMSG buffer254565 +Node: Can Magit be used as ediff-version-control-package?255583 +Node: How to show diffs for gpg-encrypted files?257607 +Node: Emacs 245 hangs when loading Magit258198 +Node: Symbol's value as function is void --some258767 +Node: Where is the branch manager259087 +Node: Keystroke Index259372 +Node: Command Index286718 +Node: Function Index316256 +Node: Variable Index328039 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/magit-20160223.828/magit.info-1 b/elpa/magit-20160223.828/magit.info-1 new file mode 100644 index 0000000..fe00cdd --- /dev/null +++ b/elpa/magit-20160223.828/magit.info-1 @@ -0,0 +1,7500 @@ +This is magit.info, produced by makeinfo version 5.2 from magit.texi. + +Magit is an interface to the version control system Git, implemented as +an Emacs package. Magit aspires to be a complete Git porcelain. While +we cannot (yet) claim that Magit wraps and improves upon each and every +Git command, it is complete enough to allow even experienced Git users +to perform almost all of their daily version control tasks directly from +within Emacs. While many fine Git clients exist, only Magit and Git +itself deserve to be called porcelains. + + Copyright (C) 2015-2016 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document 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. +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Magit: (magit). Using Git from Emacs with Magit. +END-INFO-DIR-ENTRY + + +File: magit.info, Node: Top, Next: Introduction, Up: (dir) + +Magit User Manual +***************** + +Magit is an interface to the version control system Git, implemented as +an Emacs package. Magit aspires to be a complete Git porcelain. While +we cannot (yet) claim that Magit wraps and improves upon each and every +Git command, it is complete enough to allow even experienced Git users +to perform almost all of their daily version control tasks directly from +within Emacs. While many fine Git clients exist, only Magit and Git +itself deserve to be called porcelains. + + Copyright (C) 2015-2016 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document 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. + +* Menu: + +* Introduction:: +* Installation:: +* Getting started:: +* Interface concepts:: +* Inspecting:: +* Manipulating:: +* Transferring:: +* Miscellaneous:: +* Customizing:: +* Plumbing:: +* FAQ:: +* Keystroke Index:: +* Command Index:: +* Function Index:: +* Variable Index:: + +— The Detailed Node Listing — + +Installation + +* Updating from an older release:: +* Installing from an Elpa archive:: +* Installing from the Git repository:: +* Post-installation tasks:: + +Interface concepts + +* Modes and Buffers:: +* Sections:: +* Popup buffers and prefix commands:: +* Completion and confirmation:: +* Running Git:: + +Modes and Buffers + +* Switching Buffers:: +* Naming Buffers:: +* Quitting Windows:: +* Automatic Refreshing of Magit Buffers:: +* Automatic Saving of File-Visiting Buffers:: +* Automatic Reverting of File-Visiting Buffers:: + +Sections + +* Section movement:: +* Section visibility:: +* Section hooks:: +* Section types and values:: +* Section options:: + +Running Git + +* Viewing Git output:: +* Running Git manually:: +* Git executable:: +* Global Git arguments:: + +Inspecting + +* Status buffer:: +* Logging:: +* Diffing:: +* Ediffing:: +* References buffer:: +* Bisecting:: +* Visiting blobs:: +* Blaming:: + +Status buffer + +* Status sections:: +* Status header sections:: +* Status options:: + +Logging + +* Refreshing logs:: +* Log Buffer:: +* Select from log:: +* Reflog:: + +Diffing + +* Refreshing diffs:: +* Diff buffer:: +* Diff options:: +* Revision buffer:: + +References buffer + +* References sections:: + +Manipulating + +* Repository setup:: +* Staging and unstaging:: +* Applying:: +* Committing:: +* Branching:: +* Merging:: +* Rebasing:: +* Cherry picking:: +* Resetting:: +* Stashing:: + +Staging and unstaging + +* Staging from file-visiting buffers:: + +Committing + +* Initiating a commit:: +* Editing commit messages:: + +Rebasing + +* Editing rebase sequences:: +* Rebase sequence log:: + +Cherry picking + +* Reverting:: + +Transferring + +* Remotes:: +* Fetching:: +* Pulling:: +* Pushing:: +* Creating and sending patches:: +* Applying patches:: + +Miscellaneous + +* Tagging:: +* Notes:: +* Submodules:: +* Common commands:: +* Wip modes:: +* Minor mode for buffers visiting files:: +* Minor mode for buffers visiting blobs:: + +Customizing + +* Per-repository configuration:: +* Essential settings:: + +Essential settings + +* Safety:: +* Performance:: + +Plumbing + +* Calling Git:: +* Section plumbing:: +* Refreshing buffers:: +* Conventions:: + +Calling Git + +* Getting a value from Git:: +* Calling Git for effect:: + +Section plumbing + +* Creating sections:: +* Section selection:: +* Matching sections:: + +Conventions + +* Confirmation and completion:: +* Theming Faces:: + +FAQ + +* Magit is slow:: +* I changed several thousand files at once and now Magit is unusable:: +* I am having problems committing:: +* I don't understand how branching and pushing work:: +* I don’t like the key binding in v2.4: I don't like the key binding in v24. +* I cannot install the pre-requisites for Magit v2:: +* I am using an Emacs release older than v24.4: I am using an Emacs release older than v244. +* I am using a Git release older than v1.9.4: I am using a Git release older than v194. +* I am using MS Windows and cannot push with Magit:: +* How to install the gitman info manual?:: +* How can I show Git's output?:: +* Expanding a file to show the diff causes it to disappear:: +* Point is wrong in the COMMIT_EDITMSG buffer:: +* Can Magit be used as ediff-version-control-package?:: +* How to show diffs for gpg-encrypted files?:: +* Emacs 24.5 hangs when loading Magit: Emacs 245 hangs when loading Magit. +* Symbol's value as function is void --some:: +* Where is the branch manager:: + + + +File: magit.info, Node: Introduction, Next: Installation, Prev: Top, Up: Top + +1 Introduction +************** + +Magit is an interface to the version control system Git, implemented as +an Emacs package. Magit aspires to be a complete Git porcelain. While +we cannot (yet) claim that Magit wraps and improves upon each and every +Git command, it is complete enough to allow even experienced Git users +to perform almost all of their daily version control tasks directly from +within Emacs. While many fine Git clients exist, only Magit and Git +itself deserve to be called porcelains. + + Staging and otherwise applying changes is one of the most important +features in a Git porcelain and here Magit outshines anything else, +including Git itself. Git’s own staging interface (‘git add --patch’) +is so cumbersome that many users only use it in exceptional cases. In +Magit staging a hunk or even just part of a hunk is as trivial as +staging all changes made to a file. + + The most visible part of Magit’s interface is the status buffer, +which displays information about the current repository. Its content is +created by running several Git commands and making their output +actionable. Among other things, it displays information about the +current branch, lists unpulled and unpushed changes and contains +sections displaying the staged and unstaged changes. That might sound +noisy, but, since sections are collapsible, it’s not. + + To stage or unstage a change one places the cursor on the change and +then types ‘s’ or ‘u’. The change can be a file or a hunk, or when the +region is active (i.e. when there is a selection) several files or +hunks, or even just part of a hunk. The change or changes that these +commands - and many others - would act on are highlighted. + + Magit also implements several other "apply variants" in addition to +staging and unstaging. One can discard or reverse a change, or apply it +to the working tree. Git’s own porcelain only supports this for staging +and unstaging and you would have to do something like ‘git diff ... | +??? | git apply ...’ to discard, revert, or apply a single hunk on the +command line. In fact that’s exactly what Magit does internally (which +is what lead to the term "apply variants"). + + Magit isn’t just for Git experts, but it does assume some prior +experience with Git as well as Emacs. That being said, many users have +reported that using Magit was what finally taught them what Git is +capable of and how to use it to its fullest. Other users wished they +had switched to Emacs sooner so that they would have gotten their hands +on Magit earlier. + + While one has to know the basic features of Emacs to be able to make +full use of Magit, acquiring just enough Emacs skills doesn’t take long +and is worth it, even for users who prefer other editors. Vim users are +advised to give Evil (https://bitbucket.org/lyro/evil/wiki/Home), the +"Extensible VI Layer for Emacs", and Spacemacs +(https://github.com/syl20bnr/spacemacs), an "Emacs starter-kit focused +on Evil" a try. + + Magit provides a consistent and efficient Git porcelain. After a +short learning period, you will be able to perform most of your daily +version control tasks faster than you would on the command line. You +will likely also start using features that seemed too daunting in the +past. + + Magit fully embraces Git. It exposes many advanced features using a +simple but flexible interface instead of only wrapping the trivial ones +like many GUI clients do. Of course Magit supports logging, cloning, +pushing, and other commands that usually don’t fail in spectacular ways; +but it also supports tasks that often cannot be completed in a single +step. Magit fully supports tasks such as merging, rebasing, +cherry-picking, reverting, and blaming by not only providing a command +to initiate these tasks but also by displaying context sensitive +information along the way and providing commands that are useful for +resolving conflicts and resuming the sequence after doing so. + + Magit wraps and in many cases improves upon at least the following +Git porcelain commands: ‘add’, ‘am’, ‘bisect’, ‘blame’, ‘branch’, +‘checkout’, ‘cherry’, ‘cherry-pick’, ‘clean’, ‘clone’, ‘commit’, +‘config’, ‘describe’, ‘diff’, ‘fetch’, ‘format-patch’, ‘init’, ‘log’, +‘merge’, ‘merge-tree’, ‘mv’, ‘notes’, ‘pull’, ‘rebase’, ‘reflog’, +‘remote’, ‘request-pull’, ‘reset’, ‘revert’, ‘rm’, ‘show’, ‘stash’, +‘submodule’, and ‘tag’. Many more Magit porcelain commands are +implemented on top of Git plumbing commands. + + +File: magit.info, Node: Installation, Next: Getting started, Prev: Introduction, Up: Top + +2 Installation +************** + +Magit can be installed using Emacs’ package manager or manually from its +development repository. + +* Menu: + +* Updating from an older release:: +* Installing from an Elpa archive:: +* Installing from the Git repository:: +* Post-installation tasks:: + + +File: magit.info, Node: Updating from an older release, Next: Installing from an Elpa archive, Up: Installation + +2.1 Updating from an older release +================================== + +When updating from ‘1.2.*’ or ‘1.4.*’, you should first uninstall Magit +and some of its dependencies and restart Emacs before installing the +latest release. + + • The old Magit installation has to be removed because some macros + have changed and using the old definitions when building the new + release would lead to very strange results, including compile + errors. This is due to a limitation in Emacs’ package manager or + rather Emacs itself: it’s not possible to reliably unload a feature + or even all features belonging to a package. + + • Furthermore the old dependencies ‘git-commit-mode’ and + ‘git-rebase-mode’ have to be removed because they are no longer + used by the ‘2.1.0’ release and later, and get in the way of their + successors ‘git-commit’ and ‘git-rebase’. + + So please uninstall the packages ‘magit’, ‘git-commit-mode’, and +‘git-rebase-mode’. Then quit Emacs and start a new instance. Only then +follow the instructions in either one of the next two sections. + + Also note that starting with the ‘2.1.0’ release, Magit requires at +least Emacs ‘24.4’ and Git ‘1.9.4’. You should make sure you have at +least these releases installed before updating Magit. And if you +connect to remote hosts using Tramp, then you should also make sure to +install a recent enough Git version on these hosts. + + +File: magit.info, Node: Installing from an Elpa archive, Next: Installing from the Git repository, Prev: Updating from an older release, Up: Installation + +2.2 Installing from an Elpa archive +=================================== + +If you are updating from a release older than ‘2.1.0’, then you have to +first uninstall the old version. See *note Updating from an older +release: Updating from an older release. + + Magit is available from Melpa and Melpa-Stable. If you haven’t used +Emacs’ package manager before, then it is high time you familiarize +yourself with it by reading the documentation in the Emacs manual, see +*note (emacs)Packages::. Then add one of the archives to +‘package-archives’: + + • To use Melpa: + + (require 'package) + (add-to-list 'package-archives + '("melpa" . "http://melpa.org/packages/") t) + + • To use Melpa-Stable: + + (require 'package) + (add-to-list 'package-archives + '("melpa-stable" . "http://stable.melpa.org/packages/") t) + + Once you have added your preferred archive, you need to update the +local package list using: + + M-x package-refresh-contents RET + + Once you have done that, you can install Magit and its dependencies +using: + + M-x package-install RET magit RET + + Now see *note Post-installation tasks: Post-installation tasks. + + +File: magit.info, Node: Installing from the Git repository, Next: Post-installation tasks, Prev: Installing from an Elpa archive, Up: Installation + +2.3 Installing from the Git repository +====================================== + +If you are updating from a release older than ‘2.1.0’, then you have to +first uninstall the old version. See *note Updating from an older +release: Updating from an older release. + + Magit depends on the ‘dash’ and ‘with-editor’ library which are +available from Melpa and Melpa-Stable. Install them using ‘M-x +package-install RET RET’. Of course you may also install them +manually from their development repository, but I won’t cover that here. + + (An older release of Magit is also available from Marmalade, but no +new versions will be uploaded in the future. Marmalade’s maintainer has +stopped responding to requests from package maintainers who are having +difficulties or require him to create an account so that they can upload +their packages in the first place.) + + Then clone the Magit repository: + + $ git clone https://github.com/magit/magit.git ~/.emacs.d/site-lisp/magit + $ cd ~/.emacs.d/site-lisp/magit + + Then compile the libraries and generate the info manuals: + + $ make + + If you haven’t installed ‘dash’ and ‘with-editor’ using Elpa or at +‘/path/to/magit/../’, then you have to tell ‘make’ where to +find them. To do so create ‘/path/to/magit/config.mk’ with the +following content before running ‘make’: + + LOAD_PATH = -L /path/to/magit/lisp + LOAD_PATH += -L /path/to/dash + LOAD_PATH += -L /path/to/with-editor + + Finally add this to your init file: + + (add-to-list 'load-path "~/.emacs.d/site-lisp/magit/lisp") + (require 'magit) + + (with-eval-after-load 'info + (info-initialize) + (add-to-list 'Info-directory-list + "~/.emacs.d/site-lisp/magit/Documentation/")) + + Note that you have to add the ‘lisp’ subdirectory to the ‘load-path’, +not the top-level of the repository, and that elements of ‘load-path’ +should not end with a slash, while those of ‘Info-directory-list’ +should. + + Instead of requiring the feature ‘magit’, you could load just the +autoload definitions, by loading the file ‘magit-autoloads.el’. + + Instead of running Magit directly from the repository by adding that +to the ‘load-path’, you might want to instead install it in some other +directory using ‘sudo make install’ and setting ‘load-path’ accordingly. + + To update Magit use: + + $ git pull + $ make + + At times it might be necessary to run ‘make clean all’ instead. + + To view all available targets use ‘make help’. + + Now see *note Post-installation tasks: Post-installation tasks. + + +File: magit.info, Node: Post-installation tasks, Prev: Installing from the Git repository, Up: Installation + +2.4 Post-installation tasks +=========================== + +After installing Magit you should verify that you are indeed using the +Magit, Git, and Emacs releases you think you are using. It’s best to +restart Emacs before doing so, to make sure you are not using an +outdated value for ‘load-path’. + + M-x magit-version RET + + should display something like + + Magit 2.4.0, Git 2.7.0, Emacs 24.5.1 + + Then you might also want to read about options that many users likely +want to customize. See *note Essential settings: Essential settings. + + To be able to follow cross references to Git manpages found in this +manual, you might also have to manually install the ‘gitman’ info +manual, or advice ‘Info-follow-nearest-node’ to instead open the actual +manpage. See *note How to install the gitman info manual?: How to +install the gitman info manual?. + + If you are completely new to Magit then see *note Getting started: +Getting started. + + If you have used an older Magit release before, then you should have +a look at the release notes . + + And last but not least please consider making a donation, to ensure +that I can keep working on Magit. See +for various donation options. + + +File: magit.info, Node: Getting started, Next: Interface concepts, Prev: Installation, Up: Top + +3 Getting started +***************** + +This section describes the most essential features that many Magitians +use on a daily basis. It only scratches the surface but should be +enough to get you started. + + (You might want to create a repository just for this walk-through, +e.g. by cloning an existing repository. If you don’t use a separate +repository then make sure you create a snapshot as described below). + + To display information about the current Git repository, type ‘M-x +magit-status’. You will be doing that so often that it is best to bind +this command globally: + + (global-set-key (kbd "C-x g") 'magit-status) + + Most Magit commands are commonly invoked from this buffer. It should +be considered the primary interface to interact with Git using Magit. +There are many other Magit buffers, but they are usually created from +this buffer. + + Depending on what state your repository is in, this buffer will +contain sections titled "Staged changes", "Unstaged changes", "Unpulled +commits", "Unpushed commits", and/or some others. + + If some staged and/or unstaged changes exist, you should back them up +now. Type ‘z’ to show the stashing popup buffer featuring various stash +variants and arguments that can be passed to these commands. Do not +worry about those for now, just type ‘Z’ (uppercase) to create a stash +while also keeping the index and work tree intact. The status buffer +should now also contain a section titled "Stashes". + + Otherwise, if there are no uncommitted changes, you should create +some now by editing and saving some of the tracked files. Then go back +to the status buffer, while at the same time refreshing it, by typing +‘C-x g’. (When the status buffer, or any Magit buffer for that matter, +is the current buffer, then you can also use just ‘g’ to refresh it). + + Move between sections using ‘p’ and ‘n’. Note that the bodies of +some sections are hidden. Type ‘TAB’ to expand or collapse the section +at point. You can also use ‘C-tab’ to cycle the visibility of the +current section and its children. Move to a file section inside the +section named "Unstaged changes" and type ‘s’ to stage the changes you +have made to that file. That file now appears under "Staged changes". + + Magit can stage and unstage individual hunks, not just complete +files. Move to the file you have just staged, expand it using ‘TAB’, +move to one of the hunks using ‘n’, and unstage just that by typing ‘u’. +Note how the staging (‘s’) and unstaging (‘u’) commands operate on the +change at point. Many other commands behave the same way. + + You can also un-/stage just part of a hunk. Inside the body of a +hunk section (move there using ‘C-n’), set the mark using ‘C-SPC’ and +move down until some added and removed lines fall inside the region but +not all of them. Again type ‘s’ to stage. + + It’s also possible to un-/stage multiple files at once. Move to a +file section, type ‘C-SPC’, move to the next file using ‘n’, and then +‘s’ to stage both files. Note that both the mark and point have to be +on the headings of sibling sections for this to work. If the region +looks like it does in other buffers, then it doesn’t select Magit +sections that can be acted on as a unit. + + And then of course you want to commit your changes. Type ‘c’. This +shows the committing popup buffer featuring various commit variants and +arguments that can be passed to ‘git commit’. Do not worry about those +for now. We want to create a "normal" commit, which is done by typing +‘c’ again. + + Now two new buffers appear. One is for writing the commit message, +the other shows a diff with the changes that are about to committed. +Write a message and then type ‘C-c C-c’ to actually create the commit. + + You probably don’t want to push the commit you just created because +you just committed some random changes, but if that is not the case you +could push it by typing ‘P’ to bring up the push popup and then ‘P’ +again to push to the configured upstream. (If the upstream is not +configured, then you would be prompted for the push target instead.) + + Instead we are going to undo the changes made so far. Bring up the +log for the current branch by typing ‘l l’, move to the last commit +created before starting with this walk through using ‘n’, and do a hard +reset using ‘C-u x’. *WARNING*: this discards all uncommitted changes. +If you did not follow the advice about using a separate repository for +these experiments and did not create a snapshot of uncommitted changes +before starting to try out Magit, then don’t do this. + + So far we have mentioned the commit, push, and log popups. These are +probably among the popups you will be using the most, but many others +exist. To show a popup with all other popups (as well as the various +apply commands), type ‘h’. Try a few. + + The key bindings in that popup correspond to the bindings in Magit +buffers, including but not limited to the status buffer. So you could +type ‘h d’ to bring up the diff popup, but once you remember that "d" +stands for "diff", you would usually do so by just typing ‘d’. But the +"popup of popups" is useful even once you have memorized all the +bindings, as it can provide easy access to Magit commands from non-Magit +buffers. So you should bind this globally too: + + (global-set-key (kbd "C-x M-g") 'magit-dispatch-popup) + + You might also want to enable ‘global-magit-file-mode’ (see *note +Minor mode for buffers visiting files: Minor mode for buffers visiting +files.). + + +File: magit.info, Node: Interface concepts, Next: Inspecting, Prev: Getting started, Up: Top + +4 Interface concepts +******************** + +* Menu: + +* Modes and Buffers:: +* Sections:: +* Popup buffers and prefix commands:: +* Completion and confirmation:: +* Running Git:: + + +File: magit.info, Node: Modes and Buffers, Next: Sections, Up: Interface concepts + +4.1 Modes and Buffers +===================== + +Magit provides several major-modes. For each of these modes there +usually exists only one buffer per repository. Separate modes and thus +buffers exist for commits, diffs, logs, and some other things. + + Besides these special purpose buffers, there also exists an overview +buffer, called the *status buffer*. Its usually from this buffer that +the user invokes Git commands, or creates or visits other buffers. + + In this manual we often speak about "Magit buffers". By that we mean +buffers whose major-modes derive from ‘magit-mode’. + +‘M-x magit-toggle-buffer-lock’ (‘magit-toggle-buffer-lock’) + + This command locks the current buffer to its value or if the buffer + is already locked, then it unlocks it. + + Locking a buffer to its value, prevents it from being reused to + display another value. The name of a locked buffer contains its + value, which allows telling it apart from other locked buffers and + the unlocked buffer. + + Not all Magit buffers can be locked to their values, for example it + wouldn’t make sense to lock a status buffer. + + There can only be a single unlocked buffer using a certain + major-mode per repository. So when a buffer is being unlocked and + another unlocked buffer already exists for that mode and + repository, then the former buffer is instead deleted and the + latter is displayed in its place. + +* Menu: + +* Switching Buffers:: +* Naming Buffers:: +* Quitting Windows:: +* Automatic Refreshing of Magit Buffers:: +* Automatic Saving of File-Visiting Buffers:: +* Automatic Reverting of File-Visiting Buffers:: + + +File: magit.info, Node: Switching Buffers, Next: Naming Buffers, Up: Modes and Buffers + +4.1.1 Switching Buffers +----------------------- + + -- Function: magit-display-buffer buffer + + This function is a wrapper around ‘display-buffer’ and is used to + display any Magit buffer. It displays BUFFER in some window and, + unlike ‘display-buffer’, also selects that window, provided + ‘magit-display-buffer-noselect’ is ‘nil’. It also runs the hooks + mentioned below. + + -- Variable: magit-display-buffer-noselect + + When this is non-nil, then ‘magit-display-buffer’ only displays the + buffer but forgoes also selecting the window. This variable should + not be set globally, it is only intended to be let-bound, by code + that automatically updates "the other window". This is used for + example when the revision buffer is updated when you move inside + the log buffer. + + -- User Option: magit-display-buffer-function + + The function specified here is called by ‘magit-display-buffer’ + with one argument, a buffer, to actually display that buffer. This + function should call ‘display-buffer’ with that buffer as first and + a list of display actions as second argument. + + Instead of using a wrapper around ‘display-buffer’, that function + itself can be used here, in which case the display actions have to + be specified by adding them to ‘display-buffer-alist’ instead. + + To learn about display actions, see *note (elisp)Choosing a Window + for Display::. + + -- Function: magit-display-buffer-traditional buffer + + This function is the current default value of the option + ‘magit-display-buffer-function’. Before that option and this + function were added, the behavior was hard-coded in many places all + over the code base but now all the rules are contained in this one + function (except for the "noselect" special case mentioned above). + + If you want to use different rules, then a good way of doing that + is to start with a copy of this function and then adjust it to your + needs. More functions to choose from will be added in the future, + and eventually the default will change. + + -- User Option: magit-pre-display-buffer-hook + + This hook is run by ‘magit-display-buffer’ before displaying the + buffer. + + -- Function: magit-save-window-configuration + + This function saves the current window configuration. Later when + the buffer is buried, it may be restored by + ‘magit-restore-window-configuration’. + + -- User Option: magit-post-display-buffer-hook + + This hook is run by ‘magit-display-buffer’ after displaying the + buffer. + + -- Function: magit-maybe-set-dedicated + + This function remembers if a new window had to be created to + display the buffer, or whether an existing window was reused. This + information is later used by ‘magit-mode-quit-window’, to determine + whether the window should be deleted when its last Magit buffer is + buried. + + +File: magit.info, Node: Naming Buffers, Next: Quitting Windows, Prev: Switching Buffers, Up: Modes and Buffers + +4.1.2 Naming Buffers +-------------------- + + -- User Option: magit-generate-buffer-name-function + + The function used to generate the names of Magit buffers. + + Such a function should take the options + ‘magit-uniquify-buffer-names’ as well as ‘magit-buffer-name-format’ + into account. If it doesn’t, then should be clearly stated in the + doc-string. And if it supports %-sequences beyond those mentioned + in the doc-string of the option ‘magit-buffer-name-format’, then + its own doc-string should describe the additions. + + -- Function: magit-generate-buffer-name-default-function mode + + This function returns a buffer name suitable for a buffer whose + major-mode is MODE and which shows information about the repository + in which ‘default-directory’ is located. + + This function uses ‘magit-buffer-name-format’ and supporting all of + the %-sequences mentioned the documentation of that option. It + also respects the option ‘magit-uniquify-buffer-names’. + + -- User Option: magit-buffer-name-format + + The format string used to name Magit buffers. + + At least the following %-sequences are supported: + + • ‘%m’ + + The name of the major-mode, but with the ‘-mode’ suffix + removed. + + • ‘%M’ + + Like ‘%m’ but abbreviate ‘magit-status-mode’ as ‘magit’. + + • ‘%v’ + + The value the buffer is locked to, in parentheses, or an empty + string if the buffer is not locked to a value. + + • ‘%V’ + + Like ‘%v’, but the string is prefixed with a space, unless it + is an empty string. + + • ‘%t’ + + The top-level directory of the working tree of the repository, + or if ‘magit-uniquify-buffer-names’ is non-nil an abbreviation + of that. + The value should always contain either ‘%m’ or ‘%M’, ‘%v’ or ‘%V’, + and ‘%t’. If ‘magit-uniquify-buffer-names’ is non-nil, then the + value must end with ‘%t’. + + -- User Option: magit-uniquify-buffer-names + + This option controls whether the names of Magit buffers are + uniquified. If the names are not being uniquified, then they + contain the full path of the top-level of the working tree of the + corresponding repository. If they are being uniquified, then they + end with the basename of the top-level, or if that would conflict + with the name used for other buffers, then the names of all these + buffers are adjusted until they no longer conflict. + + This is done using the ‘uniquify’ package; customize its options to + control how buffer names are uniquified. + + +File: magit.info, Node: Quitting Windows, Next: Automatic Refreshing of Magit Buffers, Prev: Naming Buffers, Up: Modes and Buffers + +4.1.3 Quitting Windows +---------------------- + +‘q’ (‘magit-mode-bury-buffer’) + + This command buries the current Magit buffer. With a prefix + argument, it instead kills the buffer. + + -- User Option: magit-bury-buffer-function + + The function used to actually bury or kill the current buffer. + + ‘magit-mode-bury-buffer’ calls this function with one argument. If + the argument is non-nil, then the function has to kill the current + buffer. Otherwise it has to bury it alive. The default value + currently is ‘magit-restore-window-configuration’. + + -- Function: magit-restore-window-configuration kill-buffer + + Bury or kill the current buffer using ‘quit-window’, which is + called with KILL-BUFFER as first and the selected window as second + argument. + + Then restore the window configuration that existed right before the + current buffer was displayed in the selected frame. Unfortunately + that also means that point gets adjusted in all the buffers, which + are being displayed in the selected frame. + + -- Function: magit-mode-quit-window kill-buffer + + Bury or kill the current buffer using ‘quit-window’, which is + called with KILL-BUFFER as first and the selected window as second + argument. + + Then, if the window was originally created to display a Magit + buffer and the buried buffer was the last remaining Magit buffer + that was ever displayed in the window, then that is deleted. + + +File: magit.info, Node: Automatic Refreshing of Magit Buffers, Next: Automatic Saving of File-Visiting Buffers, Prev: Quitting Windows, Up: Modes and Buffers + +4.1.4 Automatic Refreshing of Magit Buffers +------------------------------------------- + +After running a command which may change the state of the current +repository, the current Magit buffer and the corresponding status buffer +are refreshed. The status buffer may optionally be automatically +refreshed whenever a buffer is saved to a file inside the respective +repository. + + Automatically refreshing Magit buffers ensures that the displayed +information is up-to-date most of the time but can lead to a noticeable +delay in big repositories. Other Magit buffers are not refreshed to +keep the delay to a minimum and also because doing so can sometimes be +undesirable. + + Buffers can also be refreshed explicitly, which is useful in buffers +that weren’t current during the last refresh and after changes were made +to the repository outside of Magit. + +‘g’ (‘magit-refresh’) + + This command refreshes the current buffer if its major mode derives + from ‘magit-mode’ as well as the corresponding status buffer. + + If the option ‘magit-revert-buffers’ calls for it, then it also + reverts all unmodified buffers that visit files being tracked in + the current repository. + +‘G’ (‘magit-refresh-all’) + + This command refreshes all Magit buffers belonging to the current + repository and also reverts all unmodified buffers that visit files + being tracked in the current repository. + + The file-visiting buffers are always reverted, even if + ‘magit-revert-buffers’ is nil. + + -- User Option: magit-refresh-buffer-hook + + This hook is run in each Magit buffer that was refreshed during the + current refresh - normally the current buffer and the status + buffer. + + -- User Option: magit-refresh-status-buffer + + When this option is non-nil, then the status buffer is + automatically refreshed after running git for side-effects, in + addition to the current Magit buffer, which is always refreshed + automatically. + + Only set this to nil after exhausting all other options to improve + performance. + + -- Function: magit-after-save-refresh-status + + This function is intended to be added to ‘after-save-hook’. After + doing that the corresponding status buffer is refreshed whenever a + buffer is saved to a file inside a repository. + + Note that refreshing a Magit buffer is done by re-creating its + contents from scratch, which can be slow in large repositories. If + you are not satisfied with Magit’s performance, then you should + obviously not add this function to that hook. + + +File: magit.info, Node: Automatic Saving of File-Visiting Buffers, Next: Automatic Reverting of File-Visiting Buffers, Prev: Automatic Refreshing of Magit Buffers, Up: Modes and Buffers + +4.1.5 Automatic Saving of File-Visiting Buffers +----------------------------------------------- + +File-visiting buffers are by default saved at certain points in time. +This doesn’t guarantee that Magit buffers are always up-to-date, but, +provided one only edits files by editing them in Emacs and uses only +Magit to interact with Git, one can be fairly confident. When in doubt +or after outside changes, type ‘g’ (‘magit-refresh’) to save and refresh +explicitly. + + -- User Option: magit-save-repository-buffers + + This option controls whether file-visiting buffers are saved before + certain events. + + If this is non-nil then all modified file-visiting buffers + belonging to the current repository may be saved before running + commands, before creating new Magit buffers, and before explicitly + refreshing such buffers. If this is ‘dontask’ then this is done + without user intervention. If it is ‘t’ then the user has to + confirm each save. + + +File: magit.info, Node: Automatic Reverting of File-Visiting Buffers, Prev: Automatic Saving of File-Visiting Buffers, Up: Modes and Buffers + +4.1.6 Automatic Reverting of File-Visiting Buffers +-------------------------------------------------- + +By default Magit automatically reverts buffers that are visiting files +that are being tracked in a Git repository, after they have changed on +disk. When using Magit one often changes files on disk by running git, +i.e. "outside Emacs", making this a rather important feature. + + For example, if you discard a change in the status buffer, then that +is done by running ‘git apply --reverse ...’, and Emacs considers the +file to have "changed on disk". If Magit did not automatically revert +the buffer, then you would have to type ‘M-x revert-buffer RET RET’ in +the visiting buffer before you could continue making changes. + + -- User Option: magit-auto-revert-mode + + When this mode is enabled, then buffers that visit tracked files, + are automatically reverted after the visited files changed on disk. + + -- User Option: global-auto-revert-mode + + When this mode is enabled, then any file-visiting buffer is + automatically reverted after the visited file changed on disk. + + If you like buffers that visit tracked files to be automatically + reverted, then you might also like any buffer to be reverted, not + just those visiting tracked files. If that is the case, then + enable this mode _instead of_ ‘magit-auto-revert-mode’. + + -- User Option: magit-auto-revert-immediately + + This option controls whether Magit reverts buffers immediately. + + If this is non-nil and either ‘global-auto-revert-mode’ or + ‘magit-auto-revert-mode’ is enabled, then Magit immediately reverts + buffers by explicitly calling ‘auto-revert-buffers’ after running + git for side-effects. + + If ‘auto-revert-use-notify’ is non-nil (and file notifications are + actually supported), then ‘magit-auto-revert-immediately’ does not + have to be non-nil, because the reverts happen immediately anyway. + + If ‘magit-auto-revert-immediately’ and ‘auto-revert-use-notify’ are + both ‘nil’, then reverts happen after ‘auto-revert-interval’ + seconds of user inactivity. That is not desirable. + + -- User Option: auto-revert-use-notify + + This option controls whether file notification functions should be + used. Note that this variable unfortunately defaults to ‘t’ even + on systems on which file notifications cannot be used. + + -- User Option: magit-auto-revert-tracked-only + + This option controls whether ‘magit-auto-revert-mode’ only reverts + tracked files or all files that are located inside Git + repositories, including untracked files and files located inside + Git’s control directory. + + -- Command: auto-revert-mode + + The global mode ‘magit-auto-revert-mode’ works by turning on this + local mode in the appropriate buffers (but + ‘global-auto-revert-mode’ is implemented differently). You can + also turn it on or off manually, which might be necessary if Magit + does not notice that a previously untracked file now is being + tracked or vice-versa. + + -- User Option: auto-revert-stop-on-user-input + + This option controls whether the arrival of user input suspends the + automatic reverts for ‘auto-revert-interval’ seconds. + + -- User Option: auto-revert-interval + + This option controls for how many seconds Emacs waits before + resuming suspended reverts. + + -- User Option: auto-revert-buffer-list-filter + + This option specifies an additional filter used by + ‘auto-revert-buffers’ to determine whether a buffer should be + reverted or not. + + This option is provided by ‘magit’, which also redefines + ‘auto-revert-buffers’ to respect it. Magit users who do not turn + on the local mode ‘auto-revert-mode’ themselves, are best served by + setting the value to ‘magit-auto-revert-repository-buffers-p’. + + However the default is nil, to not disturb users who do use the + local mode directly. If you experience delays when running Magit + commands, then you should consider using one of the predicates + provided by Magit - especially if you also use Tramp. + + Users who do turn on ‘auto-revert-mode’ in buffers in which Magit + doesn’t do that for them, should likely not use any filter. Users + who turn on ‘global-auto-revert-mode’, do not have to worry about + this option, because it is disregarded if the global mode is + enabled. + + -- User Option: auto-revert-verbose + + This option controls whether Emacs reports when a buffer has been + reverted. + + The options with the ‘auto-revert-’ prefix are located in the Custom +group named ‘auto-revert’. The other, magit-specific, options are +located in the ‘magit’ group. + +* Menu: + +* Risk of Reverting Automatically:: + + +File: magit.info, Node: Risk of Reverting Automatically, Up: Automatic Reverting of File-Visiting Buffers + +Risk of Reverting Automatically +............................... + +For the vast majority users automatically reverting file-visiting +buffers after they have changed on disk is harmless. + + If a buffer is modified (i.e. it contains changes that haven’t been +saved yet), then Emacs would refuse to automatically revert it. If you +save a previously modified buffer, then that results in what is seen by +Git as an uncommitted change. Git would then refuse to carry out any +commands that would cause these changes to be lost. In other words, if +there is anything that could be lost, then either Git or Emacs would +refuse to discard the changes. + + However if you do use file-visiting buffers as a sort of ad hoc +"staging area", then the automatic reverts could potentially cause data +loss. So far I have only heard from one user who uses such a workflow. + + An example: You visit some file in a buffer, edit it, and save the +changes. Then, outside of Emacs (or at least not using Magit or by +saving the buffer) you change the file on disk again. At this point the +buffer is the only place where the intermediate version still exists. +You have saved the changes to disk, but that has since been overwritten. +Meanwhile Emacs considers the buffer to be unmodified (because you have +not made any changes to it since you last saved it to the visited file) +and therefore would not object to it being automatically reverted. At +this point an Auto-Revert mode would kick in. It would check whether +the buffer is modified and since that is not the case it would revert +it. The intermediate version would be lost. (Actually you could still +get it back using the ‘undo’ command.) + + If your workflow depends on Emacs preserving the intermediate version +in the buffer, then you have to disable all Auto-Revert modes. But +please consider that such a workflow would be dangerous even without +using an Auto-Revert mode, and should therefore be avoided. If Emacs +crashed or if you quit Emacs by mistake, then you would also lose the +buffer content. There would be no autosave file still containing the +intermediate version (because that was deleted when you saved the +buffer) and you would not be asked whether you want to safe the buffer +(because it isn’t modified). + + +File: magit.info, Node: Sections, Next: Popup buffers and prefix commands, Prev: Modes and Buffers, Up: Interface concepts + +4.2 Sections +============ + +Magit buffers are organized into nested sections, which can be collapsed +and expanded, similar to how sections are handled in Org mode. Each +section also has a type, and some sections also have a value. For each +section type there can also be a local keymap, shared by all sections of +that type. + + Taking advantage of the section value and type, many commands operate +on the current section, or when the region is active and selects +sections of the same type, all of the selected sections. Commands that +only make sense for a particular section type (as opposed to just +behaving differently depending on the type) are usually bound in section +type keymaps. + +* Menu: + +* Section movement:: +* Section visibility:: +* Section hooks:: +* Section types and values:: +* Section options:: + + +File: magit.info, Node: Section movement, Next: Section visibility, Up: Sections + +4.2.1 Section movement +---------------------- + +To move within a section use the usual keys (‘C-p’, ‘C-n’, ‘C-b’, ‘C-f’ +etc), whose global bindings are not shadowed. To move to another +section use the following commands. + +‘p’ (‘magit-section-backward’) + + When not at the beginning of a section, then move to the beginning + of the current section. At the beginning of a section, instead + move to the beginning of the previous visible section. + +‘n’ (‘magit-section-forward’) + + Move to the beginning of the next visible section. + +‘M-p’ (‘magit-section-backward-siblings’) + + Move to the beginning of the previous sibling section. If there is + no previous sibling section, then move to the parent section + instead. + +‘M-n’ (‘magit-section-forward-siblings’) + + Move to the beginning of the next sibling section. If there is no + next sibling section, then move to the parent section instead. + +‘^’ (‘magit-section-up’) + + Move to the beginning of the parent of the current section. + + The above commands all call the hook ‘magit-section-movement-hook’. +And, except for the second, the below functions are all members of that +hook’s default value. + + -- Variable: magit-section-movement-hook + + This hook is run by all of the above movement commands, after + arriving at the destination. + + -- Function: magit-hunk-set-window-start + + This hook function ensures that the beginning of the current + section is visible, provided it is a ‘hunk’ section. Otherwise, it + does nothing. + + -- Function: magit-section-set-window-start + + This hook function ensures that the beginning of the current + section is visible, regardless of the section’s type. If you add + this to ‘magit-section-movement-hook’, then you must remove the + hunk-only variant in turn. + + -- Function: magit-log-maybe-show-more-commits + + This hook function only has an effect in log buffers, and ‘point’ + is on the "show more" section. If that is the case, then it + doubles the number of commits that are being shown. + + -- Function: magit-log-maybe-update-revision-buffer + + When moving inside a log buffer, then this function updates the + revision buffer, provided it is already being displayed in another + window of the same frame. + + -- Function: magit-log-maybe-update-blob-buffer + + When moving inside a log buffer and another window of the same + frame displays a blob buffer, then this function instead displays + the blob buffer for the commit at point in that window. + + -- Function: magit-status-maybe-update-revision-buffer + + When moving inside a status buffer, then this function updates the + revision buffer, provided it is already being displayed in another + window of the same frame. + + -- Function: magit-status-maybe-update-blob-buffer + + When moving inside a status buffer and another window of the same + frame displays a blob buffer, then this function instead displays + the blob buffer for the commit at point in that window. + + -- User Option: magit-update-other-window-delay + + Delay before automatically updating the other window. + + When moving around in certain buffers certain other buffers, which + are being displayed in another window, may optionally be updated to + display information about the section at point. + + When holding down a key to move by more than just one section, then + that would update that buffer for each section on the way. To + prevent that, updating the revision buffer is delayed, and this + option controls for how long. For optimal experience you might + have to adjust this delay and/or the keyboard repeat rate and delay + of your graphical environment or operating system. + + +File: magit.info, Node: Section visibility, Next: Section hooks, Prev: Section movement, Up: Sections + +4.2.2 Section visibility +------------------------ + +Magit provides many commands for changing the visibility of sections, +but all you need to get started are the next two. + +‘TAB’ (‘magit-section-toggle’) + + Toggle the visibility of the body of the current section. + +‘C-’ (‘magit-section-cycle’) + + Cycle the visibility of current section and its children. + +‘M-’ (‘magit-section-cycle-diffs’) + + Cycle the visibility of diff-related sections in the current + buffer. + +‘s-’ (‘magit-section-cycle-global’) + + Cycle the visibility of all sections in the current buffer. + + -- Command: magit-section-show-level-1 + -- Command: magit-section-show-level-2 + -- Command: magit-section-show-level-3 + -- Command: magit-section-show-level-4 + + To show sections surrounding the current section, up to level N, + press the respective number key (‘1’, ‘2’, ‘3’, or ‘4’). + + -- Command: magit-section-show-level-1-all + -- Command: magit-section-show-level-2-all + -- Command: magit-section-show-level-3-all + -- Command: magit-section-show-level-4-all + + To show all sections up to level N, press the respective number key + and meta (‘M-1’, ‘M-2’, ‘M-3’, or ‘M-4’). + + Some functions, which are used to implement the above commands, are +also exposed as commands themselves. By default no keys are bound to +these commands, as they are generally perceived to be much less useful. +But your mileage may vary. + + -- Command: magit-section-show + + Show the body of the current section. + + -- Command: magit-section-hide + + Hide the body of the current section. + + -- Command: magit-section-show-headings + + Recursively show headings of children of the current section. Only + show the headings. Previously shown text-only bodies are hidden. + + -- Command: magit-section-show-children + + Recursively show the bodies of children of the current section. + With a prefix argument show children down to the level of the + current section, and hide deeper children. + + -- Command: magit-section-hide-children + + Recursively hide the bodies of children of the current section. + + -- Command: magit-section-toggle-children + + Toggle visibility of bodies of children of the current section. + + When a buffer is first created then some sections are shown expanded +while others are not. This is hard coded. When a buffer is refreshed +then the previous visibility is preserved. The initial visibility of +certain sections can also be overwritten using the hook +‘magit-section-set-visibility-hook’. + + -- Variable: magit-section-set-visibility-hook + + This hook is run when first creating a buffer and also when + refreshing an existing buffer, and is used to determine the + visibility of the section currently being inserted. + + Each function is called with one argument, the section being + inserted. It should return ‘hide’ or ‘show’, or to leave the + visibility undefined ‘nil’. If no function decides on the + visibility and the buffer is being refreshed, then the visibility + is preserved; or if the buffer is being created, then the hard + coded default is used. + + Usually this should only be used to set the initial visibility but + not during refreshes. If ‘magit-insert-section--oldroot’ is + non-nil, then the buffer is being refreshed and these functions + should immediately return ‘nil’. + + +File: magit.info, Node: Section hooks, Next: Section types and values, Prev: Section visibility, Up: Sections + +4.2.3 Section hooks +------------------- + +Which sections are inserted into certain buffers is controlled with +hooks. This includes the status and the refs buffers. For other +buffers, e.g. log, diff, and revision buffers, this is not possible. + + For buffers whose sections can be customized by the user, a hook +variable called ‘magit-TYPE-sections-hook’ exists. This hook should be +changed using ‘magit-add-section-hook’. Avoid using ‘add-hooks’ or the +Custom interface. + + The various available section hook variables are described later in +this manual along with the appropriate "section inserter functions". + + -- Function: magit-add-section-hook hook function &optional at append + local + + Add the function FUNCTION to the value of section hook HOOK. + + Add FUNCTION at the beginning of the hook list unless optional + APPEND is non-nil, in which case FUNCTION is added at the end. If + FUNCTION already is a member then move it to the new location. + + If optional AT is non-nil and a member of the hook list, then add + FUNCTION next to that instead. Add before or after AT, or replace + AT with FUNCTION depending on APPEND. If APPEND is the symbol + ‘replace’, then replace AT with FUNCTION. For any other non-nil + value place FUNCTION right after AT. If nil, then place FUNCTION + right before AT. If FUNCTION already is a member of the list but AT + is not, then leave FUNCTION where ever it already is. + + If optional LOCAL is non-nil, then modify the hook’s buffer-local + value rather than its global value. This makes the hook local by + copying the default value. That copy is then modified. + + HOOK should be a symbol. If HOOK is void, it is first set to nil. + HOOK’s value must not be a single hook function. FUNCTION should + be a function that takes no arguments and inserts one or multiple + sections at point, moving point forward. FUNCTION may choose not + to insert its section(s), when doing so would not make sense. It + should not be abused for other side-effects. + + To remove a function from a section hook, use ‘remove-hook’. + + +File: magit.info, Node: Section types and values, Next: Section options, Prev: Section hooks, Up: Sections + +4.2.4 Section types and values +------------------------------ + +Each section has a type, for example ‘hunk’, ‘file’, and ‘commit’. +Instances of certain section types also have a value. The value of a +section of type ‘file’, for example, is a file name. + + Users usually do not have to worry about a section’s type and value, +but knowing them can be handy at times. + +‘M-x magit-describe-section’ (‘magit-describe-section’) + + Show information about the section at point in the echo area, as + "VALUE [TYPE PARENT-TYPE…] BEGINNING-END". + + Many commands behave differently depending on the type of the section +at point and/or somehow consume the value of that section. But that is +only one of the reasons why the same key may do something different, +depending on what section is current. + + Additionally for each section type a keymap *might* be defined, named +‘magit-TYPE-section-map’. That keymap is used as text property keymap +of all text belonging to any section of the respective type. If such a +map does not exist for a certain type, then you can define it yourself, +and it will automatically be used. + + +File: magit.info, Node: Section options, Prev: Section types and values, Up: Sections + +4.2.5 Section options +--------------------- + +This section describes options that have an effect on more than just a +certain type of sections. As you can see there are not many of those. + + -- User Option: magit-section-show-child-count + + Whether to append the number of children to section headings. This + only affects sections that could benefit from this information. + + +File: magit.info, Node: Popup buffers and prefix commands, Next: Completion and confirmation, Prev: Sections, Up: Interface concepts + +4.3 Popup buffers and prefix commands +===================================== + +Many Magit commands are implemented using *popup buffers*. First the +user invokes a *popup* or *prefix* command, which causes a popup buffer +with the available *infix* arguments and *suffix* commands to be +displayed. The user then optionally toggles/sets some arguments and +finally invokes one of the suffix commands. + + This is implemented in the library ‘magit-popup’. Earlier releases +used the library ‘magit-key-mode’. A future release will switch to a +yet-to-be-written successor, which will likely be named ‘transient’. + + Because ‘magit-popup’ can also be used by other packages without +having to depend on all of Magit, it is documented in its own manual. +See *note (magit-popup)Top::. + +‘C-c C-c’ (‘magit-dispatch-popup’) + + This popup command shows a buffer featuring all other Magit popup + commands as well as some other commands that are not popup commands + themselves. + + This command is also, or especially, useful outside Magit buffers, so +you should setup a global binding: + + (global-set-key (kbd "C-x M-g") 'magit-dispatch-popup) + + +File: magit.info, Node: Completion and confirmation, Next: Running Git, Prev: Popup buffers and prefix commands, Up: Interface concepts + +4.4 Completion and confirmation +=============================== + +Many commands read a value from the user. By default this is done using +the built-in function ‘completing-read’, but Magit can instead use +another completion framework. + + -- User Option: magit-completing-read-function + + The value of this variable is the function used to perform + completion. Because functions _intended_ to replace + ‘completing-read’ often are not fully compatible drop-in + replacements, and also because Magit expects them to add the + default choice to the prompt themselves, such functions should not + be used directly. Instead a wrapper function has to be used. + + Currently only the real ‘completing-read’ and Ido +(http://www.emacswiki.org/emacs/InteractivelyDoThings) are fully +supported. More frameworks will be supported in the future. + + -- Function: magit-builtin-completing-read prompt choices &optional + predicate require-match initial-input hist def + + Perform completion using ‘completion-read’. + + -- Function: magit-ido-completing-read prompt choices &optional + predicate require-match initial-input hist def + + Perform completion using ‘ido-completing-read+’ from the package by + the same name (which you have to explicitly install). Ido itself + comes with a supposed drop-in replacement ‘ido-completing-read’, + but that has too many deficits to serve our needs. + + By default many commands that could potentially lead to data loss +have to be confirmed. This includes many very common commands, so this +can become annoying quickly. Many of these actions can be undone, +provided ‘magit-wip-before-change-mode’ is turned on (which it is not by +default, due to performance concerns). + + -- User Option: magit-no-confirm + + The value of this option is a list of symbols, representing + commands which do not have to be confirmed by the user before being + carried out. + + When the global mode ‘magit-wip-before-change-mode’ is enabled then + many commands can be undone. If that mode is enabled then adding + ‘safe-with-wip’ to this list has the same effect as adding + ‘discard’, ‘reverse’, ‘stage-all-changes’, and + ‘unstage-all-changes’. + + (add-to-list 'magit-no-confirm 'safe-with-wip) + + For a list of all symbols that can be added to the value of this + variable, see the doc-string. + + Note that there are commands that ignore this option and always +require confirmation, or which can be told not to do so using another +dedicated option. Also most commands, when acting on multiple sections +at once always, require confirmation, even when they do respect this +option when acting on a single section. + + +File: magit.info, Node: Running Git, Prev: Completion and confirmation, Up: Interface concepts + +4.5 Running Git +=============== + +* Menu: + +* Viewing Git output:: +* Running Git manually:: +* Git executable:: +* Global Git arguments:: + + +File: magit.info, Node: Viewing Git output, Next: Running Git manually, Up: Running Git + +4.5.1 Viewing Git output +------------------------ + +Magit runs Git either for side-effects (e.g. when pushing) or to get +some value (e.g. the name of the current branch). When Git is run for +side-effects then the output goes into a per-repository log buffer, +which can be consulted when things don’t go as expected. + +‘$’ (‘magit-process’) + + This commands displays the process buffer for the current + repository. + + Inside that buffer, the usual key bindings for navigating and showing +sections are available. There is one additional command. + +‘k’ (‘magit-process-kill’) + + This command kills the process represented by the section at point. + + -- User Option: magit-git-debug + + When this is non-nil then the output of all calls to git are logged + in the process buffer. This is useful when debugging, otherwise it + just negatively affects performance. + + +File: magit.info, Node: Running Git manually, Next: Git executable, Prev: Viewing Git output, Up: Running Git + +4.5.2 Running Git manually +-------------------------- + +While Magit provides many Emacs commands to interact with Git, it does +not cover everything. In those cases your existing Git knowledge will +come in handy. Magit provides some commands for running arbitrary Git +commands by typing them into the minibuffer, instead of having to switch +to a shell. + +‘!’ (‘magit-run-popup’) + + Shows the popup buffer featuring the below suffix commands. + + These suffix commands run a Git subcommand. The user input has to +begin with the subcommand, "git" is assumed. + +‘! !’ (‘magit-git-command-topdir’) + + This command reads a Git subcommand from the user and executes it + in the top-level directory of the current repository. + +‘! p’ (‘magit-git-command’) + + This command reads a Git subcommand from the user and executes it + in ‘default-directory’. With a prefix argument the command is + executed in the top-level directory of the current repository + instead. + + These suffix commands run arbitrary shell commands. + +‘! s’ (‘magit-shell-command-topdir’) + + This command reads a shell command from the user and executes it in + the top-level directory of the current repository. + +‘! S’ (‘magit-shell-command’) + + This command reads a shell command from the user and executes it in + ‘default-directory’. With a prefix argument the command is + executed in the top-level directory of the current repository + instead. + + These suffix commands start external gui tools. + +‘! k’ (‘magit-run-gitk’) + + This command runs ‘gitk’ in the current repository. + +‘! a’ (‘magit-run-gitk-all’) + + This command runs ‘gitk --all’ in the current repository. + +‘! b’ (‘magit-run-gitk-branches’) + + This command runs ‘gitk --branches’ in the current repository. + +‘! g’ (‘magit-run-git-gui’) + + This command runs ‘git gui’ in the current repository. + + +File: magit.info, Node: Git executable, Next: Global Git arguments, Prev: Running Git manually, Up: Running Git + +4.5.3 Git executable +-------------------- + +Except on MS Windows, Magit defaults to running Git without specifying +the path to the git executable. Instead the first executable found by +Emacs on ‘exec-path’ is used (whose value in turn is set based on the +value of the environment variable ‘$PATH’ when Emacs was started). + + This has the advantage that it continues to work even when using +Tramp to connect to a remote machine on which the executable is found in +a different place. The downside is that if you have multiple versions +of Git installed, then you might end up using another version than the +one you think you are using. + +‘M-x magit-version’ (‘magit-version’) + + Shows the currently used versions of Magit, Git, and Emacs in the + echo area. Non-interactively this just returns the Magit version. + + When the ‘system-type’ is ‘windows-nt’, then ‘magit-git-executable’ +is set to an absolute path when Magit is first loaded. This is +necessary because Git on that platform comes with several wrapper +scripts for the actual git binary, which are also placed on ‘$PATH’, and +using one of these wrappers instead of the binary would degrade +performance horribly. + + If Magit doesn’t find the correct executable then you *can* work +around that by setting ‘magit-git-executable’ to an absolute path. But +note that doing so is a kludge. It is better to make sure the order in +the environment variable ‘$PATH’ is correct, and that Emacs is started +with that environment in effect. If you have to connect from Windows to +a non-Windows machine, then you must change the value to "git". + + -- User Option: magit-git-executable + + The git executable used by Magit, either the full path to the + executable or the string "git" to let Emacs find the executable + itself, using the standard mechanism for doing such things. + + +File: magit.info, Node: Global Git arguments, Prev: Git executable, Up: Running Git + +4.5.4 Global Git arguments +-------------------------- + + -- User Option: magit-git-global-arguments + + The arguments set here are used every time the git executable is + run as a subprocess. They are placed right after the executable + itself and before the git command - as in ‘git HERE... COMMAND + REST’. For valid arguments see *note (gitman)git:: . + + Be careful what you add here, especially if you are using Tramp to + connect to servers with ancient Git versions. Never remove + anything that is part of the default value, unless you really know + what you are doing. And think very hard before adding something; + it will be used every time Magit runs Git for any purpose. + + +File: magit.info, Node: Inspecting, Next: Manipulating, Prev: Interface concepts, Up: Top + +5 Inspecting +************ + +The functionality provided by Magit can be roughly divided into three +groups: inspecting existing data, manipulating existing data or adding +new data, and transferring data. Of course that is a rather crude +distinction that often falls short, but it’s more useful than no +distinction at all. This section is concerned with inspecting data, the +next two with manipulating and transferring it. Then follows a section +about miscellaneous functionality, which cannot easily be fit into this +distinction. + + Of course other distinctions make sense too, e.g. Git’s distinction +between porcelain and plumbing commands, which for the most part is +equivalent to Emacs’ distinction between interactive commands and +non-interactive functions. All of the sections mentioned before are +mainly concerned with the porcelain – Magit’s plumbing layer is +described later. + +* Menu: + +* Status buffer:: +* Logging:: +* Diffing:: +* Ediffing:: +* References buffer:: +* Bisecting:: +* Visiting blobs:: +* Blaming:: + + +File: magit.info, Node: Status buffer, Next: Logging, Up: Inspecting + +5.1 Status buffer +================= + +While other Magit buffers contain e.g. one particular diff or one +particular log, the status buffer contains the diffs for staged and +unstaged changes, logs for unpushed and unpulled commits, lists of +stashes and untracked files, and information related to the current +branch. + + During certain incomplete operations – for example when a merge +resulted in a conflict – additional information is displayed that helps +proceeding with or aborting the operation. + + The command ‘magit-status’ displays the status buffer belonging to +the current repository in another window. This command is used so often +that it should be bound globally. We recommend using ‘C-x g’: + + (global-set-key (kbd "C-x g") 'magit-status) + +‘C-x g’ (‘magit-status’) + + Show the status of the current Git repository in a buffer. With a + prefix argument prompt for a repository to be shown. With two + prefix arguments prompt for an arbitrary directory. If that + directory isn’t the root of an existing repository, then offer to + initialize it as a new repository. + + -- User Option: magit-repository-directories + + Directories containing Git repositories. Magit checks these + directories for Git repositories and offers them as choices when + ‘magit-status’ is used with a prefix argument. + + -- User Option: magit-repository-directories-depth + + The maximum depth to look for Git repositories. When looking for a + Git repository below the directories in + ‘magit-repository-directories’, only descend this many levels deep. + + -- Command: ido-enter-magit-status + + From an Ido prompt used to open a file, instead drop into + ‘magit-status’. This is similar to ‘ido-magic-delete-char’, which, + despite its name, usually causes a Dired buffer to be created. + + To make this command available, use something like: + + (add-hook 'ido-setup-hook + (lambda () + (define-key ido-completion-map + (kbd \"C-x g\") 'ido-enter-magit-status))) + + Starting with Emacs 25.1 the Ido keymaps are defined just once + instead of every time Ido is invoked, so now you can modify it like + pretty much every other keymap: + + (define-key ido-common-completion-map + (kbd \"C-x g\") 'ido-enter-magit-status) + +* Menu: + +* Status sections:: +* Status header sections:: +* Status options:: + + +File: magit.info, Node: Status sections, Next: Status header sections, Up: Status buffer + +5.1.1 Status sections +--------------------- + +The contents of status buffers is controlled using the hook +‘magit-status-sections-hook’. See *note Section hooks: Section hooks. +to learn about such hooks and how to customize them. + + -- User Option: magit-status-sections-hook + + Hook run to insert sections into a status buffer. + + The first function on that hook by default is +‘magit-insert-status-headers’; it is described in the next section. By +default the following functions are also members of that hook: + + -- Function: magit-insert-merge-log + + Insert section for the on-going merge. Display the heads that are + being merged. If no merge is in progress, do nothing. + + -- Function: magit-insert-rebase-sequence + + Insert section for the on-going rebase sequence. If no such + sequence is in progress, do nothing. + + -- Function: magit-insert-am-sequence + + Insert section for the on-going patch applying sequence. If no + such sequence is in progress, do nothing. + + -- Function: magit-insert-sequencer-sequence + + Insert section for the on-going cherry-pick or revert sequence. If + no such sequence is in progress, do nothing. + + -- Function: magit-insert-bisect-output + + While bisecting, insert section with output from ‘git bisect’. + + -- Function: magit-insert-bisect-rest + + While bisecting, insert section visualizing the bisect state. + + -- Function: magit-insert-bisect-log + + While bisecting, insert section logging bisect progress. + + -- Function: magit-insert-untracked-files + + Maybe insert a list or tree of untracked files. Do so depending on + the value of ‘status.showUntrackedFiles’. + + -- Function: magit-insert-unstaged-changes + + Insert section showing unstaged changes. + + -- Function: magit-insert-staged-changes + + Insert section showing staged changes. + + -- Function: magit-insert-stashes &optional ref heading + + Insert the ‘stashes’ section showing reflog for "refs/stash". If + optional REF is non-nil show reflog for that instead. If optional + HEADING is non-nil use that as section heading instead of + "Stashes:". + + -- Function: magit-insert-unpulled-from-upstream + + Insert section showing commits that haven’t been pulled from the + upstream branch yet. + + -- Function: magit-insert-unpulled-from-pushremote + + Insert section showing commits that haven’t been pulled from the + push-remote branch yet. + + -- Function: magit-insert-unpushed-to-upstream + + Insert section showing commits that haven’t been pushed to the + upstream yet. + + -- Function: magit-insert-unpushed-to-pushremote + + Insert section showing commits that haven’t been pushed to the + push-remote yet. + + The following functions can also be added to the above hook: + + -- Function: magit-insert-tracked-files + + Insert a tree of tracked files. + + -- Function: magit-insert-unpulled-or-recent-commits + + Insert section showing unpulled or recent commits. If an upstream + is configured for the current branch and it is ahead of the current + branch, then show the missing commits. Otherwise, show the last + ‘magit-log-section-commit-count’ commits. + + -- Function: magit-insert-recent-commits + + Insert section showing the last ‘magit-log-section-commit-count’ + commits. + + -- User Option: magit-log-section-commit-count + + How many recent commits ‘magit-insert-recent-commits’ and + ‘magit-insert-unpulled-or-recent-commits’ (provided there are no + unpulled commits) show. + + -- Function: magit-insert-unpulled-cherries + + Insert section showing unpulled commits. Like + ‘magit-insert-unpulled-commits’ but prefix each commit that has not + been applied yet (i.e. a commit with a patch-id not shared with + any local commit) with "+", and all others with "-". + + -- Function: magit-insert-unpulled-module-commits + + Insert sections for all submodules with unpulled commits. These + sections can be expanded to show the respective commits. + + -- Function: magit-insert-unpushed-cherries + + Insert section showing unpushed commits. Like + ‘magit-insert-unpushed-commits’ but prefix each commit which has + not been applied to upstream yet (i.e. a commit with a patch-id + not shared with any upstream commit) with "+" and all others with + "-". + + -- Function: magit-insert-unpushed-module-commits + + Insert sections for all submodules with unpushed commits. These + sections can be expanded to show the respective commits. + + See *note References buffer: References buffer. for some more section +inserters, which could be used here. + + +File: magit.info, Node: Status header sections, Next: Status options, Prev: Status sections, Up: Status buffer + +5.1.2 Status header sections +---------------------------- + +The contents of status buffers is controlled using the hook +‘magit-status-sections-hook’, as described in the previous section. By +default ‘magit-insert-status-headers’ is the first member of that hook +variable. + + -- Function: magit-insert-status-headers + + Insert headers sections appropriate for ‘magit-status-mode’ + buffers. The sections are inserted by running the functions on the + hook ‘magit-status-headers-hook’. + + -- User Option: magit-status-headers-hook + + Hook run to insert headers sections into the status buffer. + + This hook is run by ‘magit-insert-status-headers’, which in turn + has to be a member of ‘magit-insert-status-sections’ to be used at + all. + + By default the following functions are members of the above hook: + + -- Function: magit-insert-error-header + + Insert a header line showing the message about the Git error that + just occurred. + + This function is only aware of the last error that occur when Git + was run for side-effects. If, for example, an error occurs while + generating a diff, then that error won’t be inserted. Refreshing + the status buffer causes this section to disappear again. + + -- Function: magit-insert-diff-filter-header + + Insert a header line showing the effective diff filters. + + -- Function: magit-insert-head-branch-header + + Insert a header line about the current branch or detached ‘HEAD’. + + -- Function: magit-insert-upstream-branch-header + + Insert a header line about the branch that is usually pulled into + the current branch. + + -- Function: magit-insert-push-branch-header + + Insert a header line about the branch that the current branch is + usually pushed to. + + -- Function: magit-insert-tags-header + + Insert a header line about the current and/or next tag. + + The following functions can also be added to the above hook: + + -- Function: magit-insert-repo-header + + Insert a header line showing the path to the repository top-level. + + -- Function: magit-insert-remote-header + + Insert a header line about the remote of the current branch. + + If no remote is configured for the current branch, then fall back + showing the "origin" remote, or if that does not exist the first + remote in alphabetic order. + + -- Function: magit-insert-user-header + + Insert a header line about the current user. + + +File: magit.info, Node: Status options, Prev: Status header sections, Up: Status buffer + +5.1.3 Status options +-------------------- + + -- User Option: magit-status-refresh-hook + + Hook run after a status buffer has been refreshed. + + -- User Option: magit-log-section-args + + Additional Git arguments used when creating log sections. Only + ‘--graph’, ‘--decorate’, and ‘--show-signature’ are supported. + This option is only a temporary kludge and will be removed. + + Note that due to an issue in Git the use of ‘--graph’ is very slow + with long histories, so you probably don’t want to add this here. + + Also see the proceeding section for more options concerning status +buffers. + + +File: magit.info, Node: Logging, Next: Diffing, Prev: Status buffer, Up: Inspecting + +5.2 Logging +=========== + +The status buffer contains logs for the unpushed and unpulled commits, +but that obviously isn’t enough. The prefix command ‘magit-log-popup’, +on ‘l’, features several suffix commands, which show a specific log in a +separate log buffer. + + Like other popups, the log popup also features several arguments that +can be changed before invoking one of the suffix commands. However in +case of the log popup these arguments correspond to those currently in +use in the current repository’s log buffer. When the log popup is +invoked while no log buffer exists for the current repository yet, then +the default value of ‘magit-log-arguments’ is used instead. + + For information about the various arguments, see *note +(gitman)git-log:: . The switch ‘++order=VALUE’ is converted to one of +‘--author-date-order’, ‘--date-order’, or ‘--topo-order’ before being +passed to ‘git log’. + + The log popup also features several reflog commands. See *note +Reflog: Reflog. + +‘l’ (‘magit-log-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘l l’ (‘magit-log-current’) + + Show log for the current branch. When ‘HEAD’ is detached or with a + prefix argument, show log for one or more revs read from the + minibuffer. + +‘l o’ (‘magit-log’) + + Show log for one or more revs read from the minibuffer. The user + can input any revision or revisions separated by a space, or even + ranges, but only branches, tags, and a representation of the commit + at point are available as completion candidates. + +‘l h’ (‘magit-log-head’) + + Show log for ‘HEAD’. + +‘l L’ (‘magit-log-branches’) + + Show log for all local branches and ‘HEAD’. + +‘l b’ (‘magit-log-all-branches’) + + Show log for all local and remote branches and ‘HEAD’. + +‘l a’ (‘magit-log-all’) + + Show log for all references and ‘HEAD’. + + The following related commands are not available from the popup. + +‘Y’ (‘magit-cherry’) + + Show commits in a branch that are not merged in the upstream + branch. + +‘M-x magit-log-buffer-file’ (‘magit-log-buffer-file’) + + Show log for the file visited in the current buffer. + +* Menu: + +* Refreshing logs:: +* Log Buffer:: +* Select from log:: +* Reflog:: + + +File: magit.info, Node: Refreshing logs, Next: Log Buffer, Up: Logging + +5.2.1 Refreshing logs +--------------------- + +The prefix command ‘magit-log-refresh-popup’, on ‘L’, can be used to +change the log arguments used in the current buffer, without changing +which log is shown. This works in dedicated log buffers, but also in +the status buffer. + +‘L’ (‘magit-log-refresh-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘L g’ (‘magit-log-refresh’) + + This suffix command sets the local log arguments for the current + buffer. + +‘L s’ (‘magit-log-set-default-arguments’) + + This suffix command sets the default log arguments for buffers of + the same type as that of the current buffer. Other existing + buffers of the same type are not affected because their local + values have already been initialized. + +‘L w’ (‘magit-log-save-default-arguments’) + + This suffix command sets the default log arguments for buffers of + the same type as that of the current buffer, and saves the value + for future sessions. Other existing buffers of the same type are + not affected because their local values have already been + initialized. + +‘L t’ (‘magit-toggle-margin’) + + Show or hide the margin. + + +File: magit.info, Node: Log Buffer, Next: Select from log, Prev: Refreshing logs, Up: Logging + +5.2.2 Log Buffer +---------------- + +‘L’ (‘magit-log-refresh-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. See *note + Refreshing logs: Refreshing logs. + +‘q’ (‘magit-log-bury-buffer’) + + Bury the current buffer or the revision buffer in the same frame. + Like ‘magit-mode-bury-buffer’ (which see) but with a negative + prefix argument instead bury the revision buffer, provided it is + displayed in the current frame. + +‘C-c C-b’ (‘magit-go-backward’) + + Move backward in current buffer’s history. + +‘C-c C-f’ (‘magit-go-forward’) + + Move forward in current buffer’s history. + +‘SPC’ (‘magit-diff-show-or-scroll-up’) + + Update the commit or diff buffer for the thing at point. + + Either show the commit or stash at point in the appropriate buffer, + or if that buffer is already being displayed in the current frame + and contains information about that commit or stash, then instead + scroll the buffer up. If there is no commit or stash at point, + then prompt for a commit. + +‘DEL’ (‘magit-diff-show-or-scroll-down’) + + Update the commit or diff buffer for the thing at point. + + Either show the commit or stash at point in the appropriate buffer, + or if that buffer is already being displayed in the current frame + and contains information about that commit or stash, then instead + scroll the buffer down. If there is no commit or stash at point, + then prompt for a commit. + +‘=’ (‘magit-log-toggle-commit-limit’) + + Toggle the number of commits the current log buffer is limited to. + If the number of commits is currently limited, then remove that + limit. Otherwise set it to 256. + +‘+’ (‘magit-log-double-commit-limit’) + + Double the number of commits the current log buffer is limited to. + +‘=’ (‘magit-log-half-commit-limit’) + + Half the number of commits the current log buffer is limited to. + + -- User Option: magit-log-auto-more + + Insert more log entries automatically when moving past the last + entry. Only considered when moving past the last entry with + ‘magit-goto-*-section’ commands. + + -- User Option: magit-log-show-margin + + Whether to initially show the margin in log buffers. + + When non-nil the author name and date are initially displayed in + the margin of log buffers. The margin can be shown or hidden in + the current buffer using the command ‘magit-toggle-margin’. + + When a log buffer contains a verbose log, then the margin is never + displayed. In status buffers this option is ignored, but it is + possible to show the margin using the mentioned command. + + -- User Option: magit-log-show-refname-after-summary + + Whether to show the refnames after the commit summaries. This is + useful if you use really long branch names. + + +File: magit.info, Node: Select from log, Next: Reflog, Prev: Log Buffer, Up: Logging + +5.2.3 Select from log +--------------------- + +When the user has to select a recent commit that is reachable from +‘HEAD’, using regular completion would be inconvenient (because most +humans cannot remember hashes or "HEAD~5", at least not without double +checking). Instead a log buffer is used to select the commit, which has +the advantage that commits are presented in order and with the commit +message. The following additional key bindings are available when a log +is used for selection: + +‘C-c C-c’ (‘magit-log-select-pick’) + + Select the commit at point and act on it. Call + ‘magit-log-select-pick-function’ with the selected commit as + argument. + +‘C-c C-k’ (‘magit-log-select-quit’) + + Abort selecting a commit, don’t act on any commit. + + This feature is used by rebase and squash commands. + + +File: magit.info, Node: Reflog, Prev: Select from log, Up: Logging + +5.2.4 Reflog +------------ + +Also see *note (gitman)git-reflog:: . + + These reflog commands are available from the log popup. See *note +Logging: Logging. + +‘l r’ (‘magit-reflog-current’) + + Display the reflog of the current branch. + +‘l O’ (‘magit-reflog-other’) + + Display the reflog of a branch. + +‘l H’ (‘magit-reflog-head’) + + Display the ‘HEAD’ reflog. + + +File: magit.info, Node: Diffing, Next: Ediffing, Prev: Logging, Up: Inspecting + +5.3 Diffing +=========== + +The status buffer contains diffs for the staged and unstaged commits, +but that obviously isn’t enough. The prefix command ‘magit-diff-popup’, +on ‘d’, features several suffix commands, which show a specific diff in +a separate diff buffer. + + Like other popups, the diff popup also features several arguments +that can be changed before invoking one of the suffix commands. However +in case of the diff popup these arguments correspond to those currently +in use in the current repository’s diff buffer. When the diff popup is +invoked while no diff buffer exists for the current repository yet, then +the default value of ‘magit-diff-arguments’ is used instead. + + Also see *note (gitman)git-diff:: . + +‘d’ (‘magit-diff-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘d d’ (‘magit-diff-dwim’) + + Show changes for the thing at point. + +‘d r’ (‘magit-diff’) + + Show differences between two commits. + + RANGE should be a range (A..B or A…B) but can also be a single + commit. If one side of the range is omitted, then it defaults to + HEAD. If just a commit is given, then changes in the working tree + relative to that commit are shown. + + If the region is active, use the revisions on the first and last + line of the region. With a prefix argument, instead of diffing the + revisions, choose a revision to view changes along, starting at the + common ancestor of both revisions (i.e., use a "…" range). + +‘d w’ (‘magit-diff-worktree’) + + Show changes between the current working tree and the ‘HEAD’ + commit. With a prefix argument show changes between the working + tree and a commit read from the minibuffer. + +‘d s’ (‘magit-diff-staged’) + + Show changes between the index and the ‘HEAD’ commit. With a + prefix argument show changes between the index and a commit read + from the minibuffer. + +‘d u’ (‘magit-diff-unstaged’) + + Show changes between the working tree and the index. + +‘d p’ (‘magit-diff-paths’) + + Show changes between any two files on disk. + + All of the above suffix commands update the repository’s diff buffer. +The diff popup also features two commands which show differences in +another buffer: + +‘d c’ (‘magit-show-commit’) + + Show the commit at point. If there is no commit at point or with a + prefix argument, prompt for a commit. + +‘d t’ (‘magit-stash-show’) + + Show all diffs of a stash in a buffer. + +* Menu: + +* Refreshing diffs:: +* Diff buffer:: +* Diff options:: +* Revision buffer:: + + +File: magit.info, Node: Refreshing diffs, Next: Diff buffer, Up: Diffing + +5.3.1 Refreshing diffs +---------------------- + +The prefix command ‘magit-diff-refresh-popup’, on ‘D’, can be used to +change the diff arguments used in the current buffer, without changing +which diff is shown. This works in dedicated diff buffers, but also in +the status buffer. + +‘D’ (‘magit-diff-refresh-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘D g’ (‘magit-diff-refresh’) + + This suffix command sets the local diff arguments for the current + buffer. + +‘D s’ (‘magit-diff-set-default-arguments’) + + This suffix command sets the default diff arguments for buffers of + the same type as that of the current buffer. Other existing + buffers of the same type are not affected because their local + values have already been initialized. + +‘D w’ (‘magit-diff-save-default-arguments’) + + This suffix command sets the default diff arguments for buffers of + the same type as that of the current buffer, and saves the value + for future sessions. Other existing buffers of the same type are + not affected because their local values have already been + initialized. + +‘D t’ (‘magit-diff-toggle-refine-hunk’) + + This command toggles hunk refinement on or off. + +‘D r’ (‘magit-diff-switch-range-type’) + + This command converts the diff range type from "revA..revB" to + "revB…revA", or vice versa. + +‘D f’ (‘magit-diff-flip-revs’) + + This command swaps revisions in the diff range from "revA..revB" to + "revB..revA", or vice versa. + + In addition to the above popup, which allows changing any of the +supported arguments, there also exist some commands which change a +particular argument. + +‘-’ (‘magit-diff-less-context’) + + This command decreases the context for diff hunks by COUNT lines. + +‘+’ (‘magit-diff-more-context’) + + This command increases the context for diff hunks by COUNT lines. + +‘0’ (‘magit-diff-default-context’) + + This command resets the context for diff hunks to the default + height. + + The following commands quickly change what diff is being displayed +without having to using one of the diff popups. + +‘C-c C-d’ (‘magit-diff-while-committing’) + + While committing, this command shows the changes that are about to + be committed. While amending, invoking the command again toggles + between showing just the new changes or all the changes that will + be committed. + + This binding is available in the diff buffer as well as the commit + message buffer. + +‘C-c C-b’ (‘magit-go-backward’) + + This command moves backward in current buffer’s history. + +‘C-c C-f’ (‘magit-go-forward’) + + This command moves forward in current buffer’s history. + + +File: magit.info, Node: Diff buffer, Next: Diff options, Prev: Refreshing diffs, Up: Diffing + +5.3.2 Diff buffer +----------------- + +‘RET’ (‘magit-diff-visit-file’) + + From a diff, visit the corresponding file at the appropriate + position. + + When the file is already being displayed in another window of the + same frame, then just select that window and adjust point. With a + prefix argument also display in another window. + + If the diff shows changes in the worktree, the index, or ‘HEAD’, + then visit the actual file. Otherwise when the diff is about an + older commit, then visit the respective blob using + ‘magit-find-file’. Also see ‘magit-diff-visit-file-worktree’, + which, as the name suggests, always visits the actual file. + +‘C-’ (‘magit-diff-visit-file-worktree’) + + From a diff, visit the corresponding file at the appropriate + position. + + When the file is already being displayed in another window of the + same frame, then just select that window and adjust point. With a + prefix argument also display in another window. + + The actual file in the worktree is visited. The positions in the + hunk headers get less useful the "older" the changes are, and as a + result, jumping to the appropriate position gets less reliable. + + Also see ‘magit-diff-visit-file-worktree’, which visits the + respective blob, unless the diff shows changes in the worktree, the + index, or ‘HEAD’. + +‘j’ (‘magit-jump-to-diffstat-or-diff’) + + Jump to the diffstat or diff. When point is on a file inside the + diffstat section, then jump to the respective diff section. + Otherwise, jump to the diffstat section or a child thereof. + +‘SPC’ (‘scroll-up’) + + Scroll text upward. + +‘DEL’ (‘scroll-down’) + + Scroll text downward. + + +File: magit.info, Node: Diff options, Next: Revision buffer, Prev: Diff buffer, Up: Diffing + +5.3.3 Diff options +------------------ + + -- User Option: magit-diff-refine-hunk + + Whether to show word-granularity differences within diff hunks. + + • ‘nil’ never show fine differences. + + • ‘t’ show fine differences for the current diff hunk only. + + • ‘all’ show fine differences for all displayed diff hunks. + + -- User Option: magit-diff-paint-whitespace + + Specify where to highlight whitespace errors. + + See ‘magit-highlight-trailing-whitespace’, + ‘magit-highlight-indentation’. The symbol ‘t’ means in all diffs, + ‘status’ means only in the status buffer, and nil means nowhere. + + -- User Option: magit-diff-highlight-trailing + + Whether to highlight whitespace at the end of a line in diffs. + Used only when ‘magit-diff-paint-whitespace’ is non-nil. + + -- User Option: magit-diff-highlight-indentation + + Highlight the "wrong" indentation style. Used only when + ‘magit-diff-paint-whitespace’ is non-nil. + + The value is a list of cons cells. The car is a regular + expression, and the cdr is the value that applies to repositories + whose directory matches the regular expression. If more than one + element matches, then the *last* element in the list applies. The + default value should therefore come first in the list. + + If the value is ‘tabs’, highlight indentation with tabs. If the + value is an integer, highlight indentation with at least that many + spaces. Otherwise, highlight neither. + + +File: magit.info, Node: Revision buffer, Prev: Diff options, Up: Diffing + +5.3.4 Revision buffer +--------------------- + + -- User Option: magit-revision-insert-related-refs + + Whether to show related refs in revision buffers. + + -- User Option: magit-revision-show-gravatar + + Whether to show gravatar images in revision buffers. + + If non-nil, then the value has to be a cons-cell which specifies + where the gravatar images for the author and/or the committer are + inserted inside the text that was previously inserted according to + ‘magit-revision-header-format’. + + Both cells are regular expressions. The car specifies where to + insert the author gravatar image. The top halve of the image is + inserted right after the matched text, the bottom halve on the next + line at the same offset. The cdr specifies where to insert the + committer image, accordingly. Either the car or the cdr may be + nil. + + +File: magit.info, Node: Ediffing, Next: References buffer, Prev: Diffing, Up: Inspecting + +5.4 Ediffing +============ + +‘e’ (‘magit-ediff-dwim’) + + Compare, stage, or resolve using Ediff. + + This command tries to guess what file, and what commit or range the + user wants to compare, stage, or resolve using Ediff. It might + only be able to guess either the file, or range/commit, in which + case the user is asked about the other. It might not always guess + right, in which case the appropriate ‘magit-ediff-*’ command has to + be used explicitly. If it cannot read the user’s mind at all, then + it asks the user for a command to run. + +‘E’ (‘magit-ediff-popup’) + + This prefix command shows the following suffix commands in a popup + buffer. + +‘E r’ (‘magit-ediff-compare’) + + Compare two revisions of a file using Ediff. + + If the region is active, use the revisions on the first and last + line of the region. With a prefix argument, instead of diffing the + revisions, choose a revision to view changes along, starting at the + common ancestor of both revisions (i.e., use a "…" range). + +‘E m’ (‘magit-ediff-resolve’) + + Resolve outstanding conflicts in a file using Ediff, defaulting to + the file at point. + + Provided that the value of ‘merge.conflictstyle’ is ‘diff3’, you + can view the file’s merge-base revision using ‘/’ in the Ediff + control buffer. + + In the rare event that you want to manually resolve all conflicts, + including those already resolved by Git, use + ‘ediff-merge-revisions-with-ancestor’. + +‘E s’ (‘magit-ediff-stage’) + + Stage and unstage changes to a file using Ediff, defaulting to the + file at point. + +‘E u’ (‘magit-ediff-show-unstaged’) + + Show unstaged changes to a file using Ediff. + +‘E i’ (‘magit-ediff-show-staged’) + + Show staged changes to a file using Ediff. + +‘E w’ (‘magit-ediff-show-working-tree’) + + Show changes in a file between HEAD and working tree using Ediff. + +‘E c’ (‘magit-ediff-show-commit’) + + Show changes to a file introduced by a commit using Ediff. + + -- User Option: magit-ediff-dwim-show-on-hunks + + This option controls what command ‘magit-ediff-dwim’ calls when + point is on uncommitted hunks. When nil, always run + ‘magit-ediff-stage’. Otherwise, use ‘magit-ediff-show-staged’ and + ‘magit-ediff-show-unstaged’ to show staged and unstaged changes, + respectively. + + -- User Option: magit-ediff-quit-hook + + This hook is run after quitting an Ediff session that was created + using a Magit command. The hook functions are run inside the Ediff + control buffer, and should not change the current buffer. + + This is similar to ‘ediff-quit-hook’ but takes the needs of Magit + into account. The regular ‘ediff-quit-hook’ is ignored by Ediff + sessions that were created using a Magit command. + + +File: magit.info, Node: References buffer, Next: Bisecting, Prev: Ediffing, Up: Inspecting + +5.5 References buffer +===================== + +‘y’ (‘magit-show-refs-popup’) + + List and compare references in a dedicated buffer. By default all + refs are compared with ‘HEAD’, but with a prefix argument this + command instead acts as a prefix command and shows the following + suffix commands along with the appropriate infix arguments in a + popup buffer. + +‘y y’ (‘magit-show-refs-head’) + + List and compare references in a dedicated buffer. Refs are + compared with ‘HEAD’. + +‘y c’ (‘magit-show-refs-current’) + + List and compare references in a dedicated buffer. Refs are + compared with the current branch or ‘HEAD’ if it is detached. + +‘y o’ (‘magit-show-refs’) + + List and compare references in a dedicated buffer. Refs are + compared with a branch read from the user. + + -- User Option: magit-refs-show-commit-count + + Whether to show commit counts in Magit-Refs mode buffers. + + • ‘all’ Show counts for branches and tags. + + • ‘branch’ Show counts for branches only. + + • ‘nil’ Never show counts. + The default is ‘nil’ because anything else can be very expensive. + + -- User Option: magit-refs-show-margin + + Whether to initially show the margin in refs buffers. + + When non-nil the committer name and date are initially displayed in + the margin of refs buffers. The margin can be shown or hidden in + the current buffer using the command ‘magit-toggle-margin’. + + The following variables control how individual refs are displayed. +If you change one of these variables (especially the "%c" part), then +you should also change the others to keep things aligned. The following +%-sequences are supported: + + • ‘%a’ Number of commits this ref has over the one we compare to. + + • ‘%b’ Number of commits the ref we compare to has over this one. + + • ‘%c’ Number of commits this ref has over the one we compare to. + For the ref which all other refs are compared this is instead "@", + if it is the current branch, or "#" otherwise. + + • ‘%C’ For the ref which all other refs are compared this is "@", if + it is the current branch, or "#" otherwise. For all other refs " + ". + + • ‘%h’ Hash of this ref’s tip. + + • ‘%m’ Commit summary of the tip of this ref. + + • ‘%n’ Name of this ref. + + • ‘%u’ Upstream of this local branch and additional local vs. + upstream information. + + • ‘%U’ Upstream of this local branch. + + -- Variable: magit-refs-local-branch-format + + Format used for local branches in refs buffers. + + -- Variable: magit-refs-remote-branch-format + + Format used for remote branches in refs buffers. + + -- Variable: magit-refs-tags-format + + Format used for tags in refs buffers. + + -- Variable: magit-refs-indent-cherry-lines + + Indentation of cherries in refs buffers. This should be N-1 where + N is taken from "%Nc" in the above format strings. + + Everywhere in Magit ‘RET’ visits the thing represented by the section +at point. In almost all cases visiting is done by showing some +information in another buffer and *not* doing anything else. In refs +buffers ‘RET’ behaves differently, and because many users have grown +accustomed to that inconsistency we are keeping it that way. + +‘RET’ (‘magit-visit-ref’) + + Everywhere except in refs buffers this command behaves exactly like + ‘magit-show-commit’; it shows the commit at point in another + buffer. + + In refs buffers, when there is a local branch at point, then this + command instead checks out that branch. When there is a remote + branch or a tag at point then the respective commit is checked out + causing ‘HEAD’ to be detached. + + When a prefix argument it used, then this command only *focuses* on + the reference at point, i.e. the commit counts and cherries are + updated to be relative to that reference, but nothing is checked + out. + + -- User Option: magit-visit-ref-create + + When this is non-nil and ‘magit-visit-ref’ is called inside a refs + buffer, then it "visits" the remote branch at point by creating a + new local branch which tracks that remote branch and then checking + out the newly created branch. + + This is not enabled by default because one has to use an extremely + loose definition of the verb "to visit" to be able to argue that + creating and then checking out a new local branch is a form of + visiting a remote branch. + +* Menu: + +* References sections:: + + +File: magit.info, Node: References sections, Up: References buffer + +5.5.1 References sections +------------------------- + +The contents of references buffers is controlled using the hook +‘magit-refs-sections-hook’. See *note Section hooks: Section hooks. to +learn about such hooks and how to customize them. All of the below +functions are members of the default value. Note that it makes much +less sense to customize this hook than it does for the respective hook +used for the status buffer. + + -- User Option: magit-refs-sections-hook + + Hook run to insert sections into a references buffer. + + -- Function: magit-insert-local-branches + + Insert sections showing all local branches. + + -- Function: magit-insert-remote-branches + + Insert sections showing all remote-tracking branches. + + -- Function: magit-insert-tags + + Insert sections showing all tags. + + +File: magit.info, Node: Bisecting, Next: Visiting blobs, Prev: References buffer, Up: Inspecting + +5.6 Bisecting +============= + +Also see *note (gitman)git-bisect:: . + +‘B’ (‘magit-bisect-popup’) + + This prefix command shows the following suffix commands in a popup + buffer. + + When bisecting is not in progress, then the popup buffer features the +following commands. + +‘B s’ (‘magit-bisect-start’) + + Start a bisect session. + + Bisecting a bug means to find the commit that introduced it. This + command starts such a bisect session by asking for a known good and + a bad commit. + +‘B u’ (‘magit-bisect-run’) + + Bisect automatically by running commands after each step. + + When bisecting is in progress, then the popup buffer features these +commands instead. + +‘B b’ (‘magit-bisect-bad’) + + Mark the current commit as bad. Use this after you have asserted + that the commit does contain the bug in question. + +‘B g’ (‘magit-bisect-good’) + + Mark the current commit as good. Use this after you have asserted + that the commit does not contain the bug in question. + +‘B k’ (‘magit-bisect-skip’) + + Skip the current commit. Use this if for some reason the current + commit is not a good one to test. This command lets Git choose a + different one. + +‘B r’ (‘magit-bisect-reset’) + + After bisecting, cleanup bisection state and return to original + ‘HEAD’. + + +File: magit.info, Node: Visiting blobs, Next: Blaming, Prev: Bisecting, Up: Inspecting + +5.7 Visiting blobs +================== + +‘M-x magit-find-file’ (‘magit-find-file’) + + View FILE from REV. Switch to a buffer visiting blob REV:FILE, + creating one if none already exists. + +‘M-x magit-find-file-other-window’ (‘magit-find-file-other-window’) + + View FILE from REV, in another window. Like ‘magit-find-file’, but + create a new window or reuse an existing one. + + +File: magit.info, Node: Blaming, Prev: Visiting blobs, Up: Inspecting + +5.8 Blaming +=========== + +Also see *note (gitman)git-blame:: . + +‘M-x magit-blame’ (‘magit-blame’) + + Display edit history of FILE up to REVISION. + + Interactively blame the file being visited in the current buffer. + If the buffer visits a revision of that file, then blame up to that + revision. Otherwise, blame the file’s full history, including + uncommitted changes. + + If Magit-Blame mode is already turned on then blame recursively, by + visiting REVISION:FILE (using ‘magit-find-file’), where revision is + the revision before the revision that added the lines at point. + + ARGS is a list of additional arguments to pass to ‘git blame’; only + arguments available from ‘magit-blame-popup’ should be used. + +‘M-x magit-blame-popup’ (‘magit-blame-popup’) + + This prefix command shows the above suffix command along with the + appropriate infix arguments in a popup buffer. + +‘RET’ (‘magit-show-commit’) + + Show the commit at point. If there is no commit at point or with a + prefix argument, prompt for a commit. + +‘SPC’ (‘magit-diff-show-or-scroll-up’) + + Update the commit or diff buffer for the thing at point. + + Either show the commit or stash at point in the appropriate buffer, + or if that buffer is already being displayed in the current frame + and contains information about that commit or stash, then instead + scroll the buffer up. If there is no commit or stash at point, + then prompt for a commit. + +‘DEL’ (‘magit-diff-show-or-scroll-down’) + + Update the commit or diff buffer for the thing at point. + + Either show the commit or stash at point in the appropriate buffer, + or if that buffer is already being displayed in the current frame + and contains information about that commit or stash, then instead + scroll the buffer down. If there is no commit or stash at point, + then prompt for a commit. + +‘n’ (‘magit-blame-next-chunk’) + + Move to the next chunk. + +‘N’ (‘magit-blame-next-chunk-same-commit’) + + Move to the next chunk from the same commit. + +‘p’ (‘magit-blame-previous-chunk’) + + Move to the previous chunk. + +‘P’ (‘magit-blame-previous-chunk-same-commit’) + + Move to the previous chunk from the same commit. + +‘q’ (‘magit-blame-quit’) + + Turn off Magit-Blame mode. If the buffer was created during a + recursive blame, then also kill the buffer. + +‘M-w’ (‘magit-blame-copy-hash’) + + Save the hash of the current chunk’s commit to the kill ring. + +‘t’ (‘magit-blame-toggle-headings’) + + Show or hide blame chunk headings. + + -- User Option: magit-blame-heading-format + + Format string used for blame headings. + + -- User Option: magit-blame-time-format + + Format string used for time strings in blame headings. + + -- User Option: magit-blame-show-headings + + Whether to initially show blame block headings. The headings can + also be toggled locally using command + ‘magit-blame-toggle-headings’. + + -- User Option: magit-blame-goto-chunk-hook + + Hook run by ‘magit-blame-next-chunk’ and + ‘magit-blame-previous-chunk’. + + +File: magit.info, Node: Manipulating, Next: Transferring, Prev: Inspecting, Up: Top + +6 Manipulating +************** + +* Menu: + +* Repository setup:: +* Staging and unstaging:: +* Applying:: +* Committing:: +* Branching:: +* Merging:: +* Rebasing:: +* Cherry picking:: +* Resetting:: +* Stashing:: + + +File: magit.info, Node: Repository setup, Next: Staging and unstaging, Up: Manipulating + +6.1 Repository setup +==================== + +‘M-x magit-init’ (‘magit-init’) + + This command initializes a repository and then shows the status + buffer for the new repository. + + If the directory is below an existing repository, then the user has + to confirm that a new one should be created inside. If the + directory is the root of the existing repository, then the user has + to confirm that it should be reinitialized. + +‘M-x magit-clone’ (‘magit-clone’) + + This command clones a repository and then shows the status buffer + for the new repository. + + The user is queried for a remote url and a local directory. + + -- User Option: magit-clone-set-remote.pushDefault + + Whether to set the value of ‘remote.pushDefault’ after cloning. + + If ‘t’, then set without asking. If ‘nil’, then don’t set. If + ‘ask’, then ask the user every time she clones a repository. + + +File: magit.info, Node: Staging and unstaging, Next: Applying, Prev: Repository setup, Up: Manipulating + +6.2 Staging and unstaging +========================= + +Like Git, Magit can of course stage and unstage complete files. Unlike +Git, it also allows users to gracefully un-/stage individual hunks and +even just part of a hunk. To stage individual hunks and parts of hunks +using Git directly, one has to use the very modal and rather clumsy +interface of a ‘git add --interactive’ session. + + With Magit, on the other hand, one can un-/stage individual hunks by +just moving point into the respective section inside a diff displayed in +the status buffer or a separate diff buffer and typing ‘s’ or ‘u’. To +operate on just parts of a hunk, mark the changes that should be +un-/staged using the region and then press the same key that would be +used to un-/stage. To stage multiple files or hunks at once use a +region that starts inside the heading of such a section and ends inside +the heading of a sibling section of the same type. + + Besides staging and unstaging, Magit also provides several other +"apply variants" that can also operate on a file, multiple files at +once, a hunk, multiple hunks at once, and on parts of a hunk. These +apply variants are described in the next section. + + You can also use Ediff to stage and unstage. See *note Ediffing: +Ediffing. + +‘s’ (‘magit-stage’) + + Add the change at point to the staging area. + +‘S’ (‘magit-stage-modified’) + + Stage all changes to files modified in the worktree. Stage all new + content of tracked files and remove tracked files that no longer + exist in the working tree from the index also. With a prefix + argument also stage previously untracked (but not ignored) files. + +‘u’ (‘magit-unstage’) + + Remove the change at point from the staging area. + + Only staged changes can be unstaged. But by default this command + performs an action that is somewhat similar to unstaging, when it + is called on a committed change: it reverses the change in the + index but not in the working tree. + +‘U’ (‘magit-unstage-all’) + + Remove all changes from the staging area. + + -- User Option: magit-unstage-committed + + This option controls whether ‘magit-unstage’ "unstages" committed + changes by reversing them in the index but not the working tree. + The alternative is to raise an error. + +‘M-x magit-reverse-in-index’ (‘magit-reverse-in-index’) + + This command reverses the committed change at point in the index + but not the working tree. By default no key is bound directly to + this command, but it is indirectly called when ‘u’ + (‘magit-unstage’) is pressed on a committed change. + + This allows extracting a change from ‘HEAD’, while leaving it in + the working tree, so that it can later be committed using a + separate commit. A typical workflow would be: + + • Optionally make sure that there are no uncommitted changes. + + • Visit the ‘HEAD’ commit and navigate to the change that should + not have been included in that commit. + + • Type ‘u’ (‘magit-unstage’) to reverse it in the index. This + assumes that ‘magit-unstage-committed-changes’ is non-nil. + + • Type ‘c e’ to extend ‘HEAD’ with the staged changes, including + those that were already staged before. + + • Optionally stage the remaining changes using ‘s’ or ‘S’ and + then type ‘c c’ to create a new commit. + +‘M-x magit-reset-index’ (‘magit-reset-index’) + + Reset the index to some commit. The commit is read from the user + and defaults to the commit at point. If there is no commit at + point, then it defaults to ‘HEAD’. + +* Menu: + +* Staging from file-visiting buffers:: + + +File: magit.info, Node: Staging from file-visiting buffers, Up: Staging and unstaging + +6.2.1 Staging from file-visiting buffers +---------------------------------------- + +Fine-grained un-/staging has to be done from the status or a diff +buffer, but it’s also possible to un-/stage all changes made to the file +visited in the current buffer right from inside that buffer. + +‘M-x magit-stage-file’ (‘magit-stage-file’) + + When invoked inside a file-visiting buffer, then stage all changes + to that file. In a Magit buffer, stage the file at point if any. + Otherwise prompt for a file to be staged. With a prefix argument + always prompt the user for a file, even in a file-visiting buffer + or when there is a file section at point. + +‘M-x magit-unstage-file’ (‘magit-unstage-file’) + + When invoked inside a file-visiting buffer, then unstage all + changes to that file. In a Magit buffer, unstage the file at point + if any. Otherwise prompt for a file to be unstaged. With a prefix + argument always prompt the user for a file, even in a file-visiting + buffer or when there is a file section at point. + + +File: magit.info, Node: Applying, Next: Committing, Prev: Staging and unstaging, Up: Manipulating + +6.3 Applying +============ + +Magit provides several "apply variants": stage, unstage, discard, +reverse, and "regular apply". At least when operating on a hunk they +are all implemented using ‘git apply’, which is why they are called +"apply variants". + + • Stage. Apply a change from the working tree to the index. The + change also remains in the working tree. + + • Unstage. Remove a change from the index. The change remains in + the working tree. + + • Discard. On a staged change, remove it from the working tree and + the index. On an unstaged change, remove it from the working tree + only. + + • Reverse. Reverse a change in the working tree. Both committed and + staged changes can be reversed. Unstaged changes cannot be + reversed. Discard them instead. + + • Apply. Apply a change to the working tree. Both committed and + staged changes can be applied. Unstaged changes cannot be applied + - as they already have been applied. + + The previous section described the staging and unstaging commands. +What follows are the commands which implement the remaining apply +variants. + +‘a’ (‘magit-apply’) + + Apply the change at point to the working tree. + +‘k’ (‘magit-discard’) + + Remove the change at point from the working tree. + +‘v’ (‘magit-reverse’) + + Reverse the change at point in the working tree. + + With a prefix argument all apply variants attempt a 3-way merge when +appropriate (i.e. when ‘git apply’ is used internally). + + +File: magit.info, Node: Committing, Next: Branching, Prev: Applying, Up: Manipulating + +6.4 Committing +============== + +When the user initiates a commit, Magit calls ‘git commit’ without any +arguments, so Git has to get it from the user. It creates the file +‘.git/COMMIT_EDITMSG’ and then opens that file in an editor. Magit +arranges for that editor to be the Emacsclient. Once the user finishes +the editing session, the Emacsclient exits and Git creates the commit +using the file’s content as message. + +* Menu: + +* Initiating a commit:: +* Editing commit messages:: + + +File: magit.info, Node: Initiating a commit, Next: Editing commit messages, Up: Committing + +6.4.1 Initiating a commit +------------------------- + +Also see *note (gitman)git-commit:: . + +‘c’ (‘magit-commit-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘c c’ (‘magit-commit’) + + Create a new commit on ‘HEAD’. With a prefix argument amend to the + commit at ‘HEAD’ instead. + +‘c a’ (‘magit-commit-amend’) + + Amend the last commit. + +‘c e’ (‘magit-commit-extend’) + + Amend the last commit, without editing the message. With a prefix + argument keep the committer date, otherwise change it. The option + ‘magit-commit-extend-override-date’ can be used to inverse the + meaning of the prefix argument. + + Non-interactively respect the optional OVERRIDE-DATE argument and + ignore the option. + +‘c w’ (‘magit-commit-reword’) + + Reword the last commit, ignoring staged changes. With a prefix + argument keep the committer date, otherwise change it. The option + ‘magit-commit-reword-override-date’ can be used to inverse the + meaning of the prefix argument. + + Non-interactively respect the optional OVERRIDE-DATE argument and + ignore the option. + +‘c f’ (‘magit-commit-fixup’) + + Create a fixup commit. + + With a prefix argument the target commit has to be confirmed. + Otherwise the commit at point may be used without confirmation + depending on the value of option ‘magit-commit-squash-confirm’. + +‘c F’ (‘magit-commit-instant-fixup’) + + Create a fixup commit and instantly rebase. + +‘c s’ (‘magit-commit-squash’) + + Create a squash commit, without editing the squash message. + + With a prefix argument the target commit has to be confirmed. + Otherwise the commit at point may be used without confirmation + depending on the value of option ‘magit-commit-squash-confirm’. + +‘c S’ (‘magit-commit-instant-squash’) + + Create a squash commit and instantly rebase. + +‘c A’ (‘magit-commit-augment’) + + Create a squash commit, editing the squash message. + + With a prefix argument the target commit has to be confirmed. + Otherwise the commit at point may be used without confirmation + depending on the value of option ‘magit-commit-squash-confirm’. + + -- User Option: magit-commit-ask-to-stage + + Whether to ask to stage everything when committing and nothing is + staged. + + -- User Option: magit-commit-extend-override-date + + Whether using ‘magit-commit-extend’ changes the committer date. + + -- User Option: magit-commit-reword-override-date + + Whether using ‘magit-commit-reword’ changes the committer date. + + -- User Option: magit-commit-squash-confirm + + Whether the commit targeted by squash and fixup has to be + confirmed. When non-nil then the commit at point (if any) is used + as default choice. Otherwise it has to be confirmed. This option + only affects ‘magit-commit-squash’ and ‘magit-commit-fixup’. The + "instant" variants always require confirmation because making an + error while using those is harder to recover from. + + +File: magit.info, Node: Editing commit messages, Prev: Initiating a commit, Up: Committing + +6.4.2 Editing commit messages +----------------------------- + +After initiating a commit as described in the previous section, two new +buffers appear. One shows the changes that are about to committed, +while the other is used to write the message. All regular editing +commands are available in the commit message buffer. This section only +describes the additional commands. + + Commit messages are edited in an edit session - in the background Git +is waiting for the editor, in our case the Emacsclient, to save the +commit message in a file (in most cases ‘.git/COMMIT_EDITMSG’) and then +return. If the Emacsclient returns with a non-zero exit status then Git +does not create the commit. So the most important commands are those +for finishing and aborting the commit. + +‘C-c C-c’ (‘with-editor-finish’) + + Finish the current editing session by returning with exit code 0. + Git then creates the commit using the message it finds in the file. + +‘C-c C-k’ (‘with-editor-cancel’) + + Cancel the current editing session by returning with exit code 1. + Git then cancels the commit, but leaves the file untouched. + + In addition to being used by Git, these messages may also be stored +in a ring that persists until Emacs is closed. By default the message +is stored at the beginning and the end of an edit session (regardless of +whether the session is finished successfully or was canceled). It is +sometimes useful to bring back messages from that ring. + +‘C-s M-s’ (‘git-commit-save-message’) + + Save the current buffer content to the commit message ring. + +‘M-p’ (‘git-commit-prev-message’) + + Cycle backward through the commit message ring, after saving the + current message to the ring. With a numeric prefix ARG, go back + ARG comments. + +‘M-n’ (‘git-commit-next-message’) + + Cycle forward through the commit message ring, after saving the + current message to the ring. With a numeric prefix ARG, go back + ARG comments. + + By default the diff for the changes that are about to be committed +are automatically shown when invoking the commit. When amending to an +existing commit it may be useful to show either the changes that are +about to be added to that commit or to show those changes together with +those that are already committed. + +‘C-c C-d’ (‘magit-diff-while-committing’) + + While committing, show the changes that are about to be committed. + While amending, invoking the command again toggles between showing + just the new changes or all the changes that will be committed. + +‘C-c C-w’ (‘magit-pop-revision-stack’) + + This command inserts a representation of a revision into the + current buffer. It can be used inside buffers used to write commit + messages but also in other buffers such as buffers used to edit + emails or ChangeLog files. + + By default this command pops the revision which was last added to + the ‘magit-revision-stack’ and inserts it into the current buffer + according to ‘magit-pop-revision-stack-format’. Revisions can be + put on the stack using ‘magit-copy-section-value’ and + ‘magit-copy-buffer-revision’. + + If the stack is empty or with a prefix argument it instead reads a + revision in the minibuffer. By using the minibuffer history this + allows selecting an item which was popped earlier or to insert an + arbitrary reference or revision without first pushing it onto the + stack. + + When reading the revision from the minibuffer, then it might not be + possible to guess the correct repository. When this command is + called inside a repository (e.g. while composing a commit + message), then that repository is used. Otherwise (e.g. while + composing an email) then the repository recorded for the top + element of the stack is used (even though we insert another + revision). If not called inside a repository and with an empty + stack, or with two prefix arguments, then read the repository in + the minibuffer too. + + -- User Option: magit-pop-revision-stack-format + + This option controls how the command ‘magit-pop-revision-stack’ + inserts a revision into the current buffer. + + The entries on the stack have the format ‘(HASH TOPLEVEL)’ and this + option has the format ‘(POINT-FORMAT EOB-FORMAT INDEX-REGEXP)’, all + of which may be nil or a string (though either one of EOB-FORMAT or + POINT-FORMAT should be a string, and if INDEX-REGEXP is non-nil, + then the two formats should be too). + + First INDEX-REGEXP is used to find the previously inserted entry, + by searching backward from point. The first submatch must match + the index number. That number is incremented by one, and becomes + the index number of the entry to be inserted. If you don’t want to + number the inserted revisions, then use nil for INDEX-REGEXP. + + If INDEX-REGEXP is non-nil then both POINT-FORMAT and EOB-FORMAT + should contain \"%N\", which is replaced with the number that was + determined in the previous step. + + Both formats, if non-nil and after removing %N, are then expanded + using ‘git show –format=FORMAT …’ inside TOPLEVEL. + + The expansion of POINT-FORMAT is inserted at point, and the + expansion of EOB-FORMAT is inserted at the end of the buffer (if + the buffer ends with a comment, then it is inserted right before + that). + + Some projects use pseudo headers in commit messages. Magit colorizes +such headers and provides some commands to insert such headers. + + -- User Option: git-commit-known-pseudo-headers + + A list of Git pseudo headers to be highlighted. + +‘C-c C-a’ (‘git-commit-ack’) + + Insert a header acknowledging that you have looked at the commit. + +‘C-c C-r’ (‘git-commit-review’) + + Insert a header acknowledging that you have reviewed the commit. + +‘C-c C-s’ (‘git-commit-signoff’) + + Insert a header to sign off the commit. + +‘C-c C-t’ (‘git-commit-test’) + + Insert a header acknowledging that you have tested the commit. + +‘C-c C-o’ (‘git-commit-cc’) + + Insert a header mentioning someone who might be interested. + +‘C-c C-p’ (‘git-commit-reported’) + + Insert a header mentioning the person who reported the issue being + fixed by the commit. + +‘C-c C-i’ (‘git-commit-suggested’) + + Insert a header mentioning the person who suggested the change. + + ‘git-commit-mode’ is a minor mode that is only used to establish the +above key bindings. This allows using an arbitrary major mode when +editing the commit message. It’s even possible to use a different major +mode in different repositories, which is useful when different projects +impose different commit message conventions. + + -- User Option: git-commit-major-mode + + The value of this option is the major mode used to edit Git commit + messages. + + Because ‘git-commit-mode’ is a minor mode, we don’t use its mode hook +to setup the buffer, except for the key bindings. All other setup +happens in the function ‘git-commit-setup’, which among other things +runs the hook ‘git-commit-setup-hook’. The following functions are +suitable for that hook. + + -- User Option: git-commit-setup-hook + + Hook run at the end of ‘git-commit-setup’. + + -- Function: magit-revert-buffers &optional force + + Revert unmodified file-visiting buffers of the current repository. + + If either ‘magit-revert-buffers’ is non-nil and + ‘inhibit-magit-revert’ is nil, or if optional FORCE is non-nil, + then revert all unmodified buffers that visit files being tracked + in the current repository. + + -- Function: git-commit-save-message + + Save the current buffer content to the commit message ring. + + -- Function: git-commit-setup-changelog-support + + After this function is called, ChangeLog entries are treated as + paragraphs. + + -- Function: git-commit-turn-on-auto-fill + + Turn on ‘auto-fill-mode’ and set ‘fill-column’ to the value of + ‘git-commit-fill-column’. + + -- Function: git-commit-turn-on-flyspell + + Turn on Flyspell mode. Also prevent comments from being checked + and finally check current non-comment text. + + -- Function: git-commit-propertize-diff + + Propertize the diff shown inside the commit message buffer. Git + inserts such diffs into the commit message template when the + ‘--verbose’ argument is used. Magit’s commit popup by default does + not offer that argument because the diff that is shown in a + separate buffer is more useful. But some users disagree, which is + why this function exists. + + -- Function: with-editor-usage-message + + Show usage information in the echo area. + + Magit also helps with writing *good* commit messages by complaining +when certain rules are violated. + + -- User Option: git-commit-summary-max-length + + The intended maximal length of the summary line of commit messages. + Characters beyond this column are colorized to indicate that this + preference has been violated. + + -- User Option: git-commit-fill-column + + Column beyond which automatic line-wrapping should happen in commit + message buffers. + + -- User Option: git-commit-finish-query-functions + + List of functions called to query before performing commit. + + The commit message buffer is current while the functions are + called. If any of them returns nil, then the commit is not + performed and the buffer is not killed. The user should then fix + the issue and try again. + + The functions are called with one argument. If it is non-nil then + that indicates that the user used a prefix argument to force + finishing the session despite issues. Functions should usually + honor this wish and return non-nil. + + -- Function: git-commit-check-style-conventions + + Check for violations of certain basic style conventions. For each + violation ask the user if she wants to proceed anyway. This makes + sure the summary line isn’t too long and that the second line is + empty. + + To show no diff while committing remove ‘magit-commit-diff’ from +‘server-switch-hook’. + + +File: magit.info, Node: Branching, Next: Merging, Prev: Committing, Up: Manipulating + +6.5 Branching +============= + +The upstream branch of some local branch is the branch into which the +commits on that local branch should eventually be merged, usually +something like ‘origin/master’. For the ‘master’ branch itself the +upstream branch and the branch it is being pushed to, are usually the +same remote branch. But for a feature branch the upstream branch and +the branch it is being pushed to should differ. + + Feature branches too should _eventually_ end up in a remote branch +such as ‘origin/master’ or ‘origin/maint’. Such a branch should +therefore be used as the upstream. But feature branches shouldn’t be +pushed directly to such branches. Instead a feature branch ‘my-feature’ +is usually pushed to ‘my-fork/my-feature’ or ‘origin/my-feature’. After +the new feature has been reviewed, the maintainer merges the feature +into ‘master’. And finally ‘master’ (not ‘my-feature’ itself) is pushed +to ‘origin/master’. + + But new features seldom are perfect on the first try, and so feature +branches usually have to be improved and re-pushed many times. Pushing +should therefore be easy to do, and for that reason some users have +concluded that the remote branch to which a feature branch is being +pushed should be set as the upstream. Luckily Git has long ago gained +support for a push-remote which can be configured separately from the +upstream branch, using the variables ‘branch..pushRemote’ and +‘remote.pushDefault’, so we no longer have to choose which of the two +remotes should be used as "the remote". + + Each of the fetching, pulling, and pushing popups features three +commands which act on the current branch and some other branch. Of +these, ‘p’ is bound to a command which acts on the push-remote, ‘u’ is +bound to a command which acts on the upstream, and ‘e’ is bound to a +command which acts on any other branch. The status buffer shows +unpushed and unpulled for both the push-remote and the upstream. + + It’s fairly simple to configure these two remotes. The values of all +the variables that are related to fetching, pulling, and pushing (as +well as some other branch-related variables) can be inspected and +changed in the branching popup. It is also possible to set the +push-remote and/or upstream while pushing (see *note Pushing: Pushing.). + +‘b’ (‘magit-branch-popup’) + + This prefix command shows the following suffix commands in a popup + buffer. It also displays the values of the following variables and + allows changing their values. + + The following variables are used to configure a specific branch. The +values are being displayed for the current branch (if any). To change +the value for another branch use a prefix argument. + + -- Variable: branch.NAME.merge + + Together with ‘branch.NAME.remote’ this variable defines the + upstream branch of the local branch named NAME. The value of this + variable is the full reference of the upstream _branch_. + + -- Variable: branch.NAME.remote + + Together with ‘branch.NAME.merge’ this variable defines the + upstream branch of the local branch named NAME. The value of this + variable is the name of the upstream _remote_. + + -- Variable: branch.NAME.rebase + + This variable controls whether pulling into the branch named NAME + is done by rebasing or by merging the fetched branch. + + • When ‘true’ then pulling is done by rebasing. + + • When ‘false’ then pulling is done by merging. + + • When undefined then the value of ‘pull.rebase’ is used. The + default of that variable is ‘false’. + + -- Variable: branch.NAME.pushRemote + + This variable specifies the remote that the branch named NAME is + usually pushed to. The value has to be the name of an existing + remote. + + It is not possible to specify the name of _branch_ to push the + local branch to. The name of the remote branch is always the same + as the name of the local branch. + + If this variable is undefined but ‘remote.pushDefault’ is defined, + then the value of the latter is used. By default + ‘remote.pushDefault’ is undefined. + + -- Variable: branch.NAME.description + + This variable can be used to describe the branch named NAME. That + description is used e.g. when turning the branch into a series of + patches. + + The following variables specify defaults which are used if the above +branch-specific variables are not set. + + -- Variable: pull.rebase + + This variable specifies whether pulling is done by rebasing or by + merging. It can be overwritten using ‘branch.NAME.rebase’. + + • When ‘true’ then pulling is done by rebasing. + + • When ‘false’ (the default) then pulling is done by merging. + Since it is never a good idea to merge the upstream branch into a + feature or hotfix branch and most branches are such branches, you + should consider setting this to ‘true’, and ‘branch.master.rebase’ + to ‘false’. + + -- Variable: remote.pushDefault + + This variable specifies what remote the local branches are usually + pushed to. This can be overwritten per branch using + ‘branch.NAME.pushRemote’. + + The following variables are used during the creation of a branch and +control whether the various branch-specific variables are automatically +set at this time. + + -- Variable: branch.autoSetupMerge + + This variable specifies under what circumstances creating a branch + NAME should result in the variables ‘branch.NAME.merge’ and + ‘branch.NAME.remote’ being set according to the starting point used + to create the branch. If the starting point isn’t a branch, then + these variables are never set. + + • When ‘always’ then the variables are set regardless of whether + the starting point is a local or a remote branch. + + • When ‘true’ (the default) then the variables are set when the + starting point is a remote branch, but not when it is a local + branch. + + • When ‘false’ then the variables are never set. + + -- Variable: branch.autoSetupRebase + + This variable specifies whether creating a branch NAME should + result in the variable ‘branch.NAME.rebase’ being set to ‘true’. + + • When ‘always’ then the variable is set regardless of whether + the starting point is a local or a remote branch. + + • When ‘local’ then the variable are set when the starting point + is a local branch, but not when it is a remote branch. + + • When ‘remote’ then the variable are set when the starting + point is a remote branch, but not when it is a local branch. + + • When ‘never’ (the default) then the variable is never set. + + Note that the respective commands always change the repository-local +values. If you want to change the global value, which is used when the +local value is undefined, then you have to do so on the command line, +e.g.: + + git config --global remote.autoSetupMerge always + + For more information about these variables you should also see + + *note (gitman)git-config:: . Also see *note (gitman)git-branch:: , + + *note (gitman)git-checkout:: , and *note Pushing: Pushing. + + -- User Option: magit-prefer-remote-upstream + + This option controls whether commands that read a branch from the + user and then set it as the upstream branch, offer a local or a + remote branch as default completion candidate, when they have the + choice. + + This affects all commands that use ‘magit-read-upstream-branch’ or + ‘magit-read-starting-point’, which includes all commands that + change the upstream and many which create new branches. + +‘b b’ (‘magit-checkout’) + + Checkout a revision read in the minibuffer and defaulting to the + branch or arbitrary revision at point. If the revision is a local + branch then that becomes the current branch. If it is something + else then ‘HEAD’ becomes detached. Checkout fails if the working + tree or the staging area contain changes. + +‘b n’ (‘magit-branch’) + + Create a new branch. The user is asked for a branch or arbitrary + revision to use as the starting point of the new branch. When a + branch name is provided, then that becomes the upstream branch of + the new branch. The name of the new branch is also read in the + minibuffer. + + Also see option ‘magit-branch-prefer-remote-upstream’. + +‘b c’ (‘magit-branch-and-checkout’) + + This command creates a new branch like ‘magit-branch’, but then + also checks it out. + + Also see option ‘magit-branch-prefer-remote-upstream’. + +‘b s’ (‘magit-branch-spinoff’) + + This command creates and checks out a new branch starting at and + tracking the current branch. That branch in turn is reset to the + last commit it shares with its upstream. If the current branch has + no upstream or no unpushed commits, then the new branch is created + anyway and the previously current branch is not touched. + + This is useful to create a feature branch after work has already + began on the old branch (likely but not necessarily "master"). + + If the current branch is a member of the value of option + ‘magit-branch-prefer-remote-upstream’ (which see), then the current + branch will be used as the starting point as usual, but the + upstream of the starting-point may be used as the upstream of the + new branch, instead of the starting-point itself. + +‘b x’ (‘magit-branch-reset’) + + This command resets a branch, defaulting to the branch at point, to + the tip of another branch or any other commit. + + When the branch being reset is the current branch, then a hard + reset is performed. If there are any uncommitted changes, then the + user has to confirming the reset because those changes would be + lost. + + This is useful when you have started work on a feature branch but + realize it’s all crap and want to start over. + + When resetting to another branch and a prefix argument is used, + then the target branch is set as the upstream of the branch that is + being reset. + +‘b d’ (‘magit-branch-delete’) + + Delete one or multiple branches. If the region marks multiple + branches, then offer to delete those. Otherwise, prompt for a + single branch to be deleted, defaulting to the branch at point. + +‘b r’ (‘magit-branch-rename’) + + Rename a branch. The branch and the new name are read in the + minibuffer. With prefix argument the branch is renamed even if + that name conflicts with an existing branch. + + -- User Option: magit-branch-read-upstream-first + + When creating a branch, whether to read the upstream branch before + the name of the branch that is to be created. The default is + ‘nil’, and I recommend you leave it at that. + + -- User Option: magit-branch-prefer-remote-upstream + + This option specifies whether remote upstreams are favored over + local upstreams when creating new branches. + + When a new branch is created, Magit offers the branch, commit, or + stash as the default starting point of the new branch. If there is + no such thing at point, then it falls back to offer the current + branch as starting-point. The user may then accept that default or + pick something else. + + If the chosen starting-point is a branch, then it may also be set + as the upstream of the new branch, depending on the value of the + Git variable ‘branch.autoSetupMerge’. By default this is done for + remote branches, but not for local branches. + + You might prefer to always use some remote branch as upstream. If + the chosen starting-point is (1) a local branch, (2) whose name is + a member of the value of this option, (3) the upstream of that + local branch is a remote branch with the same name, and (4) that + remote branch can be fast-forwarded to the local branch, then the + chosen branch is used as starting-point, but its own upstream is + used as the upstream of the new branch. + + Assuming the chosen branch matches these conditions you would end + up with with e.g.: + + feature --upstream--> origin/master + + instead of + + feature --upstream--> master --upstream--> origin/master + + Which you prefer is a matter of personal preference. If you do + prefer the former, then you should add branches such as ‘master’, + ‘next’, and ‘maint’ to the value of this options. + + +File: magit.info, Node: Merging, Next: Rebasing, Prev: Branching, Up: Manipulating + +6.6 Merging +=========== + +Also see *note (gitman)git-merge:: . + +‘m’ (‘magit-merge-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + + When no merge is in progress, then the popup buffer features the +following commands. + +‘m m’ (‘magit-merge’) + + Merge another branch or an arbitrary revision into the current + branch. The branch or revision to be merged is read in the + minibuffer and defaults to the one at point. + + Unless there are conflicts or a prefix argument is used, the + resulting merge commit uses a generic commit message, and the user + does not get a chance to inspect or change it before the commit is + created. With a prefix argument this does not actually create the + merge commit, which makes it possible to inspect how conflicts were + resolved and to adjust the commit message. + +‘m e’ (‘magit-merge-editmsg’) + + Merge another branch or an arbitrary revision into the current + branch and open a commit message buffer, so that the user can make + adjustments. The commit is not actually created until the user + finishes with ‘C-c C-c’. + +‘m n’ (‘magit-merge-nocommit’) + + Merge another branch or an arbitrary revision into the current + branch, but do not actually create the commit. The user can then + further adjust the merge, even when automatic conflict resolution + succeeded and/or adjust the commit message. + +‘m p’ (‘magit-merge-preview’) + + Preview result of merging another branch or an arbitrary revision + into the current branch. + + When a merge is in progress, then the popup buffer features these +commands instead. + +‘m m’ (‘magit-merge’) + + After resolving conflicts, proceed with the merge. If there are + still conflicts, then this fails. + +‘m a’ (‘magit-merge-abort’) + + Abort the current merge operation. + + +File: magit.info, Node: Rebasing, Next: Cherry picking, Prev: Merging, Up: Manipulating + +6.7 Rebasing +============ + +Also see *note (gitman)git-rebase:: . + +‘r’ (‘magit-rebase-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + + When no rebase is in progress, then the popup buffer features the +following commands. + +‘r p’ (‘magit-rebase-onto-pushremote’) + + Rebase the current branch onto ‘branch..pushRemote’. If that + variable is unset, then rebase onto ‘remote.pushDefault’. + +‘r u’ (‘magit-rebase-onto-upstream’) + + Rebase the current branch onto its upstream branch. + +‘r e’ (‘magit-rebase’) + + Rebase the current branch onto a branch read in the minibuffer. + All commits that are reachable from head but not from the selected + branch TARGET are being rebased." + +‘r o’ (‘magit-rebase-subset’) + + Start a non-interactive rebase sequence with commits from START to + ‘HEAD’ onto NEWBASE. START has to be selected from a list of recent + commits. + + Note that the popup also features the infix argument ‘--interactive’. +This can be used to turn one of the above non-interactive rebase +variants into an interactive rebase. + + For example if you want to clean up a feature branch and at the same +time rebase it onto ‘master’, then you could use ‘r-iu’. But we +recommend that you instead do that in two steps. First use ‘ri’ to +cleanup the feature branch, and then in a second step ‘ru’ to rebase it +onto ‘master’. That way if things turn out to be more complicated than +you thought and/or you make a mistake and have to start over, then you +only have to redo half the work. + + Explicitly enabling ‘--interactive’ won’t have an effect on the +following commands as they always use that argument anyway, even if it +is not enabled in the popup. + +‘r i’ (‘magit-rebase-interactive’) + + Start an interactive rebase sequence. + +‘r f’ (‘magit-rebase-autosquash’) + + Combine squash and fixup commits with their intended targets. + +‘r m’ (‘magit-rebase-edit-commit’) + + Edit a single older commit using rebase. + +‘r w’ (‘magit-rebase-reword-commit’) + + Reword a single older commit using rebase. + + When a rebase is in progress, then the popup buffer features these +commands instead. + +‘r r’ (‘magit-rebase-continue’) + + Restart the current rebasing operation. + +‘r s’ (‘magit-rebase-skip’) + + Skip the current commit and restart the current rebase operation. + +‘r e’ (‘magit-rebase-edit’) + + Edit the todo list of the current rebase operation. + +‘r a’ (‘magit-rebase-abort’) + + Abort the current rebase operation, restoring the original branch. + +* Menu: + +* Editing rebase sequences:: +* Rebase sequence log:: + + +File: magit.info, Node: Editing rebase sequences, Next: Rebase sequence log, Up: Rebasing + +6.7.1 Editing rebase sequences +------------------------------ + +‘C-c C-c’ (‘with-editor-finish’) + + Finish the current editing session by returning with exit code 0. + Git then creates the commit using the message it finds in the file. + +‘C-c C-k’ (‘with-editor-cancel’) + + Cancel the current editing session by returning with exit code 1. + Git then cancels the commit, but leaves the file untouched. + +‘RET’ (‘git-rebase-show-commit’) + + Show the commit on the current line in another buffer and select + that buffer. + +‘SPC’ (‘magit-diff-show-or-scroll-up’) + + Show the commit on the current line in another buffer without + selecting that buffer. If the revision buffer is already visible + in another window of the current frame, then instead scroll that + window up. + +‘DEL’ (‘magit-diff-show-or-scroll-down’) + + Show the commit on the current line in another buffer without + selecting that buffer. If the revision buffer is already visible + in another window of the current frame, then instead scroll that + window down. + +‘p’ (‘git-rebase-backward-line’) + + Move to previous line. + +‘n’ (‘forward-line’) + + Move to next line. + +‘M-p’ (‘git-rebase-move-line-up’) + + Move the current commit (or command) up. + +‘M-n’ (‘git-rebase-move-line-down’) + + Move the current commit (or command) down. + +‘r’ (‘git-rebase-reword’) + + Edit message of commit on current line. + +‘e’ (‘git-rebase-edit’) + + Stop at the commit on the current line. + +‘s’ (‘git-rebase-squash’) + + Meld commit on current line into previous commit, and edit message. + +‘f’ (‘git-rebase-fixup’) + + Meld commit on current line into previous commit, discarding the + current commit’s message. + +‘k’ (‘git-rebase-kill-line’) + + Kill the current action line. + +‘c’ (‘git-rebase-pick’) + + Use commit on current line. + +‘x’ (‘git-rebase-exec’) + + Insert a shell command to be run after the proceeding commit. + + If there already is such a command on the current line, then edit + that instead. With a prefix argument insert a new command even + when there already is one on the current line. With empty input + remove the command on the current line, if any. + +‘y’ (‘git-rebase-insert’) + + Read an arbitrary commit and insert it below current line. + +‘C-x u’ (‘git-rebase-undo’) + + Undo some previous changes. Like ‘undo’ but works in read-only + buffers. + + -- User Option: git-rebase-auto-advance + + Whether to move to next line after changing a line. + + -- User Option: git-rebase-show-instructions + + Whether to show usage instructions inside the rebase buffer. + + -- User Option: git-rebase-confirm-cancel + + Whether confirmation is required to cancel. + + +File: magit.info, Node: Rebase sequence log, Prev: Editing rebase sequences, Up: Rebasing + +6.7.2 Rebase sequence log +------------------------- + +While a rebase sequence is in progress, the status buffer features a +section which lists the commits that have already been applied as well +as the commits that still have to be applied. + + The commits are split in two halves. When rebase stops at a commit, +either because the user has to deal with a conflict or explicitly +requested that rebase stops at that commit, then point is placed on the +commit that separates the two groups, i.e. on ‘HEAD’. The commits +above it have not been applied yet, while it and the commits below it +have already been applied. In between these two groups of applied and +yet-to-be applied commits, there sometimes is a commit which has been +dropped. + + Each commit is prefixed with a word and these words are additionally +shown in different colors to indicate the status of the commits. + + The following colors are used: + + • Yellow commits have not been applied yet. + + • Gray commits have already been applied. + + • The blue commit is the ‘HEAD’ commit. + + • The green commit is the commit the rebase sequence stopped at. If + this is the same commit as ‘HEAD’ (e.g. because you haven’t done + anything yet after rebase stopped at the commit, then this commit + is shown in blue, not green. There can only be a green and a blue + commit at the same time, if you create one or more new commits + after rebase stops at a commit. + + • Red commits have been dropped. They are shown for reference only, + e.g. to make it easier to diff. + + Of course these colors are subject to the color-theme in use. + + The following words are used: + + • Commits prefixed with ‘pick’, ‘reword’, ‘edit’, ‘squash’, and + ‘fixup’ have not been applied yet. These words have the same + meaning here as they do in the buffer used to edit the rebase + sequence. See *note Editing rebase sequences: Editing rebase + sequences. + + • The commit prefixed with ‘onto’ is the commit on top of which all + the other commits are being re-applied. Like the commits that have + already been re-applied, it is reachable from ‘HEAD’, but unlike + those it has not actually been re-applied during the current + session - it wasn’t touched at all. + + • Commits prefixed with ‘done’ have already been re-applied. Not all + commits that have already been applied are prefixed with this word, + though. + + • When a commit is prefixed with ‘void’, then that indicates that + Magit knows for sure that all the changes in that commit have been + applied using several new commits. This commit is no longer + reachable from ‘HEAD’, and it also isn’t one of the commits that + will be applied when resuming the session. + + • When a commit is prefixed with ‘join’, then that indicates that the + rebase sequence stopped at that commit due to a conflict - you now + have to join (merge) the changes with what has already been + applied. In a sense this is the commit rebase stopped at, but + while its effect is already in the index and in the worktree (with + conflict markers), the commit itself has not actually been applied + yet (it isn’t the ‘HEAD’). So it is shown in yellow, like the + other commits that still have to be applied. + + • When a commit is prefixed with ‘goal’, ‘same’, or ‘work’, then that + indicates that you reset to an earlier commit (and that this commit + therefore is no longer reachable from ‘HEAD’), but that it might + still be possible to create a new commit with the exact same tree + or at least the same patch-id, without manually editing any file. + Or at the very least that there are some uncommitted remaining, + which may or may not originate from that commit. + + • When a commit is prefixed with ‘goal’, then that indicates + that it is still possible to create a commit with the exact + same tree (the "goal") without manually editing a file, by + simply committing the index (or, provided nothing is already + staged, by staging all unstaged changes and then committing + that). This is the case when the original tree exists in the + index or worktree in untainted form. + + • When a commit is prefixed with ‘same’, then that indicates + that it is no longer possible to create a commit with the + exact same tree, but that it is still possible to create a + commit with the same patch-id. This would be the case if you + created a new commit with other changes, but the changes from + the original commit still exist in the index and/or working + tree in untainted form. + + • When a commit is prefixed with ‘work’, then that indicates + that you are working with the changes from that commit after + resetting to an earlier commit. There are changes in the + index and/or working tree and some of them likely originate + from that commit. + + • When a commit is prefixed with ‘poof’ or ‘gone’, then that + indicates that you reset to an earlier commit (and that this commit + therefore is no longer reachable from ‘HEAD’), and that there are + no uncommitted changes remaining which might allow you to create a + new commit with the same tree or at least the same patch-id. + + • When a commit is prefixed with ‘poof’, then that indicates + that it is no longer reachable from ‘HEAD’, but that it has + been replaced with one or more commits, which together have + the exact same effect. + + • When a commit is prefixed with ‘gone’, then that indicates + that it is no longer reachable from ‘HEAD’ and that we also + cannot determine whether its changes are still in effect in + one or more new commits. They might be, but if so, then there + must also be other changes which makes it impossible to know + for sure. + + Do not worry if you do not fully understand the above. That’s okay, +you will acquire a good enough understanding through practice. + + For other sequence operations such as cherry-picking, a similar +section is displayed, but they lack some of the features described +above, due to limitations in the git commands used to implement them. +Most importantly these sequences only support "picking" a commit but not +other actions such as "rewording", and they do not keep track of the +commits which have already been applied. + + +File: magit.info, Node: Cherry picking, Next: Resetting, Prev: Rebasing, Up: Manipulating + +6.8 Cherry picking +================== + +Also see *note (gitman)git-cherry-pick:: . + +‘A’ (‘magit-cherry-pick-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + + When no cherry-pick or revert is in progress, then the popup buffer +features the following commands. + +‘A A’ (‘magit-cherry-pick’) + + Cherry-pick a commit. Prompt for a commit, defaulting to the + commit at point. If the region selects multiple commits, then pick + all of them, without prompting. + +‘A a’ (‘magit-cherry-apply’) + + Apply the changes in a commit to the working tree, but do not + commit them. Prompt for a commit, defaulting to the commit at + point. If the region selects multiple commits, then apply all of + them, without prompting. + + This command also has a top-level binding, which can be invoked + without using the popup by typing ‘a’ at the top-level. + + When a cherry-pick or revert is in progress, then the popup buffer +features these commands instead. + +‘A A’ (‘magit-sequence-continue’) + + Resume the current cherry-pick or revert sequence. + +‘A s’ (‘magit-sequence-skip’) + + Skip the stopped at commit during a cherry-pick or revert sequence. + +‘A a’ (‘magit-sequence-abort’) + + Abort the current cherry-pick or revert sequence. This discards + all changes made since the sequence started. + +* Menu: + +* Reverting:: + + +File: magit.info, Node: Reverting, Up: Cherry picking + +6.8.1 Reverting +--------------- + +‘V’ (‘magit-revert-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + + When no cherry-pick or revert is in progress, then the popup buffer +features the following commands. + +‘V V’ (‘magit-revert’) + + Revert a commit by creating a new commit. Prompt for a commit, + defaulting to the commit at point. If the region selects multiple + commits, then revert all of them, without prompting. + +‘V v’ (‘magit-revert-no-commit’) + + Revert a commit by applying it in reverse to the working tree. + Prompt for a commit, defaulting to the commit at point. If the + region selects multiple commits, then revert all of them, without + prompting. + + When a cherry-pick or revert is in progress, then the popup buffer +features these commands instead. + +‘V A’ (‘magit-sequence-continue’) + + Resume the current cherry-pick or revert sequence. + +‘V s’ (‘magit-sequence-skip’) + + Skip the stopped at commit during a cherry-pick or revert sequence. + +‘V a’ (‘magit-sequence-abort’) + + Abort the current cherry-pick or revert sequence. This discards + all changes made since the sequence started. + + +File: magit.info, Node: Resetting, Next: Stashing, Prev: Cherry picking, Up: Manipulating + +6.9 Resetting +============= + +Also see *note (gitman)git-reset:: . + +‘x’ (‘magit-reset’) + + Reset the head and index to some commit read from the user and + defaulting to the commit at point. The working tree is kept as-is. + With a prefix argument also reset the working tree. + +‘M-x magit-reset-index’ (‘magit-reset-index’) + + Reset the index to some commit read from the user and defaulting to + the commit at point. Keep the ‘HEAD’ and working tree as-is, so if + the commit refers to the ‘HEAD’, then this effectively unstages all + changes. + +‘M-x magit-reset-head’ (‘magit-reset-head’) + + Reset the ‘HEAD’ and index to some commit read from the user and + defaulting to the commit at point. The working tree is kept as-is. + +‘M-x magit-reset-soft’ (‘magit-reset-soft’) + + Reset the ‘HEAD’ to some commit read from the user and defaulting + to the commit at point. The index and the working tree are kept + as-is. + +‘M-x magit-reset-hard’ (‘magit-reset-hard’) + + Reset the ‘HEAD’, index, and working tree to some commit read from + the user and defaulting to the commit at point. + +‘M-x magit-checkout-file’ (‘magit-checkout-file’) + + Update file in the working tree and index to the contents from a + revision. + + Both the revision and file are read from the user. + + +File: magit.info, Node: Stashing, Prev: Resetting, Up: Manipulating + +6.10 Stashing +============= + +Also see *note (gitman)git-stash:: . + +‘z’ (‘magit-stash-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘z z’ (‘magit-stash’) + + Create a stash of the index and working tree. Untracked files are + included according to popup arguments. One prefix argument is + equivalent to ‘--include-untracked’ while two prefix arguments are + equivalent to ‘--all’. + +‘z i’ (‘magit-stash-index’) + + Create a stash of the index only. Unstaged and untracked changes + are not stashed. + +‘z w’ (‘magit-stash-worktree’) + + Create a stash of the working tree only. Untracked files are + included according to popup arguments. One prefix argument is + equivalent to ‘--include-untracked’ while two prefix arguments are + equivalent to ‘--all’. + +‘z x’ (‘magit-stash-keep-index’) + + Create a stash of the index and working tree, keeping index intact. + Untracked files are included according to popup arguments. One + prefix argument is equivalent to ‘--include-untracked’ while two + prefix arguments are equivalent to ‘--all’. + +‘z Z’ (‘magit-snapshot’) + + Create a snapshot of the index and working tree. Untracked files + are included according to popup arguments. One prefix argument is + equivalent to ‘--include-untracked’ while two prefix arguments are + equivalent to ‘--all’. + +‘z I’ (‘magit-snapshot-index’) + + Create a snapshot of the index only. Unstaged and untracked + changes are not stashed. + +‘z W’ (‘magit-snapshot-worktree’) + + Create a snapshot of the working tree only. Untracked files are + included according to popup arguments. One prefix argument is + equivalent to ‘--include-untracked’ while two prefix arguments are + equivalent to ‘--all’-. + +‘z a’ (‘magit-stash-apply’) + + Apply a stash to the working tree. Try to preserve the stash + index. If that fails because there are staged changes, apply + without preserving the stash index. + +‘z p’ (‘magit-stash-pop’) + + Apply a stash to the working tree and remove it from stash list. + Try to preserve the stash index. If that fails because there are + staged changes, apply without preserving the stash index and forgo + removing the stash. + +‘z d’ (‘magit-stash-drop’) + + Remove a stash from the stash list. When the region is active, + offer to drop all contained stashes. + +‘z l’ (‘magit-stash-list’) + + List all stashes in a buffer. + +‘z v’ (‘magit-stash-show’) + + Show all diffs of a stash in a buffer. + +‘z b’ (‘magit-stash-branch’) + + Create and checkout a new BRANCH from STASH. + +‘z f’ (‘magit-stash-format-patch’) + + Create a patch from STASH. + +‘k’ (‘magit-stash-clear’) + + Remove all stashes saved in REF’s reflog by deleting REF. + + +File: magit.info, Node: Transferring, Next: Miscellaneous, Prev: Manipulating, Up: Top + +7 Transferring +************** + +* Menu: + +* Remotes:: +* Fetching:: +* Pulling:: +* Pushing:: +* Creating and sending patches:: +* Applying patches:: + + +File: magit.info, Node: Remotes, Next: Fetching, Up: Transferring + +7.1 Remotes +=========== + +Also see *note (gitman)git-remote:: . + +‘M’ (‘magit-remote-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘M a’ (‘magit-remote-add’) + + Add a remote and fetch it. The remote name and url are read in the + minibuffer. + +‘M r’ (‘magit-remote-rename’) + + Rename a remote. Both the old and the new names are read in the + minibuffer. + +‘M u’ (‘magit-remote-set-url’) + + Change the url of a remote. Both the remote and the new url are + read in the minibuffer. + +‘M k’ (‘magit-remote-remove’) + + Delete a remote, read from the minibuffer. + + -- User Option: magit-remote-add-set-remote.pushDefault + + Whether to set the value of ‘remote.pushDefault’ after adding a + remote. + + If ‘ask’, then always ask. If ‘ask-if-unset’, then ask, but only + if the variable isn’t set already. If ‘nil’, then don’t ever set. + If the value is a string, then set without asking, provided the + name of the name of the added remote is equal to that string and + the variable isn’t already set. + + +File: magit.info, Node: Fetching, Next: Pulling, Prev: Remotes, Up: Transferring + +7.2 Fetching +============ + +For information about the differences between the _upstream_ and the +_push-remote_, see *note Branching: Branching. + + Also see *note (gitman)git-fetch:: . + +‘f’ (‘magit-fetch-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘f p’ (‘magit-fetch-from-pushremote’) + + Fetch from the push-remote of the current branch. + +‘f u’ (‘magit-fetch-from-upstream’) + + Fetch from the upstream of the current branch. + +‘f e’ (‘magit-fetch’) + + Fetch from another repository. + +‘f a’ (‘magit-fetch-all’) + + Fetch from all remotes. + +‘f m’ (‘magit-submodule-fetch’) + + Fetch all submodules. With a prefix argument fetch all remotes of + all submodules. + + Instead of using one popup for fetching and another for pulling, you +could also use ‘magit-pull-and-fetch-popup’. See its doc-string for +more information. + + +File: magit.info, Node: Pulling, Next: Pushing, Prev: Fetching, Up: Transferring + +7.3 Pulling +=========== + +For information about the differences between the _upstream_ and the +_push-remote_, see *note Branching: Branching. + + Also see *note (gitman)git-pull:: . + +‘F’ (‘magit-pull-popup’) + + This prefix command shows the following suffix commands in a popup + buffer. + +‘F p’ (‘magit-pull-from-pushremote’) + + Pull from the push-remote of the current branch. + +‘F u’ (‘magit-pull-from-upstream’) + + Pull from the upstream of the current branch. + +‘F e’ (‘magit-pull’) + + Pull from a branch read in the minibuffer. + + Instead of using one popup for fetching and another for pulling, you +could also use ‘magit-pull-and-fetch-popup’. See its doc-string for +more information. + + +File: magit.info, Node: Pushing, Next: Creating and sending patches, Prev: Pulling, Up: Transferring + +7.4 Pushing +=========== + +For information about the differences between the _upstream_ and the +_push-remote_, see *note Branching: Branching. + + Also see *note (gitman)git-push:: . + +‘P’ (‘magit-push-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘P p’ (‘magit-push-current-to-pushremote’) + + Push the current branch to ‘branch..pushRemote’ or if that is + unset to ‘remote.pushDefault’. + + When ‘magit-push-current-set-remote-if-missing’ is non-nil and the + push-remote is not configured, then read the push-remote from the + user, set it, and then push to it. With a prefix argument the + push-remote can be changed before pushed to it. + +‘P u’ (‘magit-push-current-to-upstream’) + + Push the current branch to its upstream branch. + + When ‘magit-push-current-set-remote-if-missing’ is non-nil and the + push-remote is not configured, then read the upstram from the user, + set it, and then push to it. With a prefix argument the + push-remote can be changed before pushed to it. + +‘P e’ (‘magit-push-current’) + + Push the current branch to a branch read in the minibuffer. + +‘P o’ (‘magit-push’) + + Push an arbitrary branch or commit somewhere. Both the source and + the target are read in the minibuffer. + +‘P m’ (‘magit-push-matching’) + + Push all matching branches to another repository. If multiple + remotes exit, then read one from the user. If just one exists, use + that without requiring confirmation. + +‘P t’ (‘magit-push-tags’) + + Push all tags to another repository. If only one remote exists, + then push to that. Otherwise prompt for a remote, offering the + remote configured for the current branch as default. + +‘P T’ (‘magit-push-tag’) + + Push a tag to another repository. + + Two more push commands exist, which by default are not available from +the push popup. See their doc-strings for instructions on how to add +them to the popup. + + -- Command: magit-push-implicitly args + + Push somewhere without using an explicit refspec. + + This command simply runs ‘git push -v [ARGS]’. ARGS are the + arguments specified in the popup buffer. No explicit refspec + arguments are used. Instead the behavior depends on at least these + Git variables: ‘push.default’, ‘remote.pushDefault’, + ‘branch..pushRemote’, ‘branch..remote’, + ‘branch..merge’, and ‘remote..push’. + + -- Command: magit-push-to-remote remote args + + Push to the remote REMOTE without using an explicit refspec. The + remote is read in the minibuffer. + + This command simply runs ‘git push -v [ARGS] REMOTE’. ARGS are the + arguments specified in the popup buffer. No refspec arguments are + used. Instead the behavior depends on at least these Git + variables: ‘push.default’, ‘remote.pushDefault’, + ‘branch..pushRemote’, ‘branch..remote’, + ‘branch..merge’, and ‘remote..push’. + + -- User Option: magit-push-current-set-remote-if-missing + + This option controls whether missing remotes are configured before + pushing. + + When ‘nil’, then the command ‘magit-push-current-to-pushremote’ and + ‘magit-push-current-to-upstream’ do not appear in the push popup if + the push-remote resp. upstream is not configured. If the user + invokes one of these commands anyway, then it raises an error. + + When ‘non-nil’, then these commands always appear in the push + popup. But if the required configuration is missing, then they do + appear in a way that indicates that this is the case. If the user + invokes one of them, then it asks for the necessary configuration, + stores the configuration, and then uses it to push a first time. + + This option also affects whether the argument ‘--set-upstream’ is + available in the popup. If the value is ‘non-nil’, then that + argument is redundant. But note that changing the value of this + option does not take affect immediately, the argument will only be + added or removed after restarting Emacs. + + +File: magit.info, Node: Creating and sending patches, Next: Applying patches, Prev: Pushing, Up: Transferring + +7.5 Creating and sending patches +================================ + +‘W’ (‘magit-patch-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘W p’ (‘magit-format-patch’) + + Create patches for a set commits. If the region marks commits, + then create patches for those. Otherwise prompt for a range or a + single commit, defaulting to the commit at point. + +‘W r’ (‘magit-request-pull’) + + Request that upstream pulls from your public repository. + + +File: magit.info, Node: Applying patches, Prev: Creating and sending patches, Up: Transferring + +7.6 Applying patches +==================== + +Also see *note (gitman)git-am:: . + +‘w’ (‘magit-am-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘w w’ (‘magit-am-apply-patches’) + + Apply one or more patches. If the region marks files, then apply + those patches. Otherwise read a file name in the minibuffer + defaulting to the file at point. + +‘w m’ (‘magit-am-apply-maildir’) + + Apply the patches from a maildir. + +‘w w’ (‘magit-am-continue’) + + Resume the current patch applying sequence. + +‘w s’ (‘magit-am-skip’) + + Skip the stopped at patch during a patch applying sequence. + +‘w a’ (‘magit-am-abort’) + + Abort the current patch applying sequence. This discards all + changes made since the sequence started. + + +File: magit.info, Node: Miscellaneous, Next: Customizing, Prev: Transferring, Up: Top + +8 Miscellaneous +*************** + +* Menu: + +* Tagging:: +* Notes:: +* Submodules:: +* Common commands:: +* Wip modes:: +* Minor mode for buffers visiting files:: +* Minor mode for buffers visiting blobs:: + + +File: magit.info, Node: Tagging, Next: Notes, Up: Miscellaneous + +8.1 Tagging +=========== + +Also see *note (gitman)git-tag:: . + +‘t’ (‘magit-tag-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘t t’ (‘magit-tag’) + + Create a new tag with the given NAME at REV. With a prefix argument + annotate the tag. + +‘t k’ (‘magit-tag-delete’) + + Delete one or more tags. If the region marks multiple tags (and + nothing else), then offer to delete those. Otherwise, prompt for a + single tag to be deleted, defaulting to the tag at point. + +‘t p’ (‘magit-tag-prune’) + + Offer to delete tags missing locally from REMOTE, and vice versa. + + +File: magit.info, Node: Notes, Next: Submodules, Prev: Tagging, Up: Miscellaneous + +8.2 Notes +========= + +Also see *note (gitman)git-notes:: . + +‘T’ (‘magit-notes-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘T T’ (‘magit-notes-edit’) + + Edit the note attached to a commit, defaulting to the commit at + point. + + By default use the value of Git variable ‘core.notesRef’ or + "refs/notes/commits" if that is undefined. + +‘T r’ (‘magit-notes-remove’) + + Remove the note attached to a commit, defaulting to the commit at + point. + + By default use the value of Git variable ‘core.notesRef’ or + "refs/notes/commits" if that is undefined. + +‘T p’ (‘magit-notes-prune’) + + Remove notes about unreachable commits. + +‘T s’ (‘magit-notes-set-ref’) + + Set the current notes ref to a the value read from the user. The + ref is made current by setting the value of the Git variable + ‘core.notesRef’. With a prefix argument change the global value + instead of the value in the current repository. When this is + undefined, then "refs/notes/commit" is used. + + Other ‘magit-notes-*’ commands, as well as the sub-commands of + Git’s ‘note’ command, default to operate on that ref. + +‘T S’ (‘magit-notes-set-display-refs’) + + Set notes refs to be display in addition to "core.notesRef". This + reads a colon separated list of notes refs from the user. The + values are stored in the Git variable ‘notes.displayRef’. With a + prefix argument GLOBAL change the global values instead of the + values in the current repository. + + It is possible to merge one note ref into another. That may result +in conflicts which have to resolved in the temporary worktree +".git/NOTES_MERGE_WORKTREE". + +‘T m’ (‘magit-notes-merge’) + + Merge the notes of a ref read from the user into the current notes + ref. The current notes ref is the value of Git variable + ‘core.notesRef’ or "refs/notes/commits" if that is undefined. + + When a notes merge is in progress then the popup features the +following suffix commands, instead of those listed above. + +‘T c’ (‘magit-notes-merge-commit’) + + Commit the current notes ref merge, after manually resolving + conflicts. + +‘T a’ (‘magit-notes-merge-abort’) + + Abort the current notes ref merge. + + +File: magit.info, Node: Submodules, Next: Common commands, Prev: Notes, Up: Miscellaneous + +8.3 Submodules +============== + +Also see *note (gitman)git-submodule:: . + +‘o’ (‘magit-submodule-popup’) + + This prefix command shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘o a’ (‘magit-submodule-add’) + + Add the repository at URL as a submodule. Optional PATH is the + path to the submodule relative to the root of the super-project. + If it is nil then the path is determined based on URL. + +‘o b’ (‘magit-submodule-setup’) + + Clone and register missing submodules and checkout appropriate + commits. + +‘o i’ (‘magit-submodule-init’) + + Register submodules listed in ".gitmodules" into ".git/config". + +‘o u’ (‘magit-submodule-update’) + + Clone missing submodules and checkout appropriate commits. With a + prefix argument also register submodules in ".git/config". + +‘o s’ (‘magit-submodule-sync’) + + Update each submodule’s remote URL according to ".gitmodules". + +‘o f’ (‘magit-submodule-fetch’) + + Fetch submodule. With a prefix argument fetch all remotes. + +‘o i’ (‘magit-submodule-init’) + + Unregister the submodule at PATH. + + +File: magit.info, Node: Common commands, Next: Wip modes, Prev: Submodules, Up: Miscellaneous + +8.4 Common commands +=================== + +These are some of the commands that can be used in all buffers whose +major-modes derive from ‘magit-mode’. There are other common commands +beside the ones below, but these didn’t fit well anywhere else. + +‘M-w’ (‘magit-copy-section-value’) + + This command saves the value of the current section to the + ‘kill-ring’, and, provided that the current section is a commit, + branch, or tag section, it also pushes the (referenced) revision to + the ‘magit-revision-stack’. + + When the current section is a branch or a tag, and a prefix + argument is used, then it saves the revision at its tip to the + ‘kill-ring’ instead of the reference name. + +‘C-w’ (‘magit-copy-buffer-revision’) + + This command save the revision being displayed in the current + buffer to the ‘kill-ring’ and also pushes it to the + ‘magit-revision-stack’. It is mainly intended for use in + ‘magit-revision-mode’ buffers, the only buffers where it is always + unambiguous exactly which revision should be saved. + + Most other Magit buffers usually show more than one revision, in + some way or another, so this command has to select one of them, and + that choice might not always be the one you think would have been + the best pick. + + Outside of Magit ‘M-w’ and ‘C-w’ are usually bound to +‘kill-ring-save’ and ‘kill-region’, and these commands would also be +useful in Magit buffers. Therefore when the region is active, then both +of these commands behave like ‘kill-ring-save’ instead of as described +above. + + +File: magit.info, Node: Wip modes, Next: Minor mode for buffers visiting files, Prev: Common commands, Up: Miscellaneous + +8.5 Wip modes +============= + +Git keeps *committed* changes around long enough for users to recover +changes they have accidentally deleted. It does so by not garbage +collecting any committed but no longer referenced objects for a certain +period of time, by default 30 days. + + But Git does *not* keep track of *uncommitted* changes in the working +tree and not even the index (the staging area). Because Magit makes it +so convenient to modify uncommitted changes, it also makes it easy to +shoot yourself in the foot in the process. + + For that reason Magit provides three global modes that save *tracked* +files to work-in-progress references after or before certain actions. +(Untracked files are never saved and these modes also only work after +the first commit has been created). + + Two separate work-in-progress references are used to track the state +of the index and of the working tree: "refs/wip/index/" and +"refs/wip/wtree/", where ‘’ is the full ref of the +current branch, e.g. "refs/heads/master". When the ‘HEAD’ is detached +then "HEAD" is in place of ‘’. + + Checking out another branch (or detaching ‘HEAD’) causes the use of +different wip refs for subsequent changes, but the old refs are not +deleted. + + Creating a commit and then making a change causes the wip refs to be +recreated to fork from the new commit. But the old commits on the wip +refs are not lost. They are still available from the reflog. To make +it easier to see when the fork point of a wip ref was changed, an +additional commit with the message "restart autosaving" is created on it +(‘xxO’ commits below are such boundary commits). + + Starting with + + BI0---BI1 refs/wip/index/refs/heads/master + / +A---B refs/heads/master + \ + BW0---BW1 refs/wip/wtree/refs/heads/master + + and committing the staged changes and editing and saving a file would +result in + + BI0---BI1 refs/wip/index/refs/heads/master + / +A---B---C refs/heads/master + \ \ + \ CW0---CW1 refs/wip/wtree/refs/heads/master + \ + BW0---BW1 refs/wip/wtree/refs/heads/master@{2} + + The fork-point of the index wip ref is not changed until some change +is being staged. Likewise just checking out a branch or creating a +commit does not change the fork-point of the working tree wip ref. The +fork-points are not adjusted until there actually is a change that +should be committed to the respective wip ref. + + To view the log for the a branch and its wip refs use the commands +‘magit-wip-log’ and ‘magit-wip-log-current’. You should use ‘--graph’ +when using these commands. Alternatively you can use the reflog to show +all commits that ever existed on a wip ref. You can then recover lost +changes from the commits shown in the log or reflog. + + -- Command: magit-wip-log + + This command shows the log for a branch and its wip refs. + + With a negative prefix argument only the worktree wip ref is shown. + The absolute numeric value of the prefix argument controls how many + "branches" of each wip ref are shown. + + -- Command: magit-wip-log-current + + This command shows the log for the current branch and its wip refs. + + With a negative prefix argument only the worktree wip ref is shown. + The absolute numeric value of the prefix argument controls how many + "branches" of each wip ref are shown. + + There exists a total of three global modes that save to the wip refs, +which might seem excessive, but allows fine tuning of when exactly +changes are being committed to the wip refs. Enabling all modes makes +it less likely that a change slips through the cracks. + + Setting the below variable directly does not take effect; either +customize them or call the respective mode function. + + -- User Option: magit-wip-after-save-mode + + When this mode is enabled, then saving a buffer that visits a file + tracked in a Git repository causes its current state to be + committed to the working tree wip ref for the current branch. + + -- User Option: magit-wip-after-apply-mode + + When this mode is enabled, then applying (i.e. staging, unstaging, + discarding, reversing, and regularly applying) a change to a file + tracked in a Git repository causes its current state to be + committed to the index and/or working tree wip refs for the current + branch. + + If you only ever edit files using Emacs and only ever interact with +Git using Magit, then the above two modes should be enough to protect +each and every change from accidental loss. In practice nobody does +that. So an additional mode exists that does commit to the wip refs +before making changes that could cause the loss of earlier changes. + + -- User Option: magit-wip-before-change-mode + + When this mode is enabled, then certain commands commit the + existing changes to the files they are about to make changes to. + + Note that even if you enable all three modes this won’t give you +perfect protection. The most likely scenario for losing changes despite +the use of these modes is making a change outside Emacs and then +destroying it also outside Emacs. In such a scenario, Magit, being an +Emacs package, didn’t get the opportunity to keep you from shooting +yourself in the foot. + + When you are unsure whether Magit did commit a change to the wip +refs, then you can explicitly request that all changes to all tracked +files are being committed. + +‘M-x magit-wip-commit’ (‘magit-wip-commit’) + + This command commits all changes to all tracked files to the index + and working tree work-in-progress refs. Like the modes described + above, it does not commit untracked files, but it does check all + tracked files for changes. Use this command when you suspect that + the modes might have overlooked a change made outside Emacs/Magit. + + -- User Option: magit-wip-after-save-local-mode-lighter + + Mode-line lighter for ‘magit-wip-after-save-local-mode’. + + -- User Option: magit-wip-after-apply-mode-lighter + + Mode-line lighter for ‘magit-wip-after-apply-mode’. + + -- User Option: magit-wip-before-change-mode-lighter + + Mode-line lighter for ‘magit-wip-before-change-mode’. + + -- User Option: magit-wip-namespace + + The namespace used for work-in-progress refs. It has to end with a + slash. The wip refs are named "index/" and + "wtree/". When snapshots are created while + the ‘HEAD’ is detached then "HEAD" is used in place of + ‘’. + + +File: magit.info, Node: Minor mode for buffers visiting files, Next: Minor mode for buffers visiting blobs, Prev: Wip modes, Up: Miscellaneous + +8.6 Minor mode for buffers visiting files +========================================= + +The ‘magit-file-mode’ enables certain Magit features in file-visiting +buffers belonging to a Git repository. It should be enabled globally +using ‘global-magit-file-mode’. Currently this mode only establishes a +few key bindings, but this might be extended in the future. + + -- User Option: global-magit-file-mode + + Whether to establish certain Magit key bindings in all + file-visiting buffers belonging to a Git repository. This + establishes the bindings suggested in *note Getting started: + Getting started. (but only for file-visiting buffers), and + additionally binds ‘C-c M-g’ to ‘magit-file-popup’. + +‘C-c M-g’ (‘magit-file-popup’) + + This prefix command shows a popup buffer featuring suffix commands + that operate on the file being visited in the current buffer. + +‘C-c M-g s’ (‘magit-stage-file’) + + Stage all changes to the file being visited in the current buffer. + +‘C-c M-g u’ (‘magit-unstage-file’) + + Unstage all changes to the file being visited in the current + buffer. + +‘C-c M-g l’ (‘magit-log-buffer-file’) + + This command shows the log for the file of blob that the current + buffer visits. Renames are followed when a prefix argument is used + or when ‘--follow’ is part of ‘magit-log-arguments’. + +‘C-c M-g b’ (‘magit-blame-popup’) + + This prefix command shows the ‘magit-blame’ suffix command along + with the appropriate infix arguments in a popup buffer. See *note + Initiating a commit: Initiating a commit. + +‘C-c M-g p’ (‘magit-blob-previous’) + + Visit the previous blob which modified the current file. + +‘C-c M-g c’ (‘magit-commit-popup’) + + This prefix command shows suffix commands along with the + appropriate infix arguments in a popup buffer. See *note + Initiating a commit: Initiating a commit. + + +File: magit.info, Node: Minor mode for buffers visiting blobs, Prev: Minor mode for buffers visiting files, Up: Miscellaneous + +8.7 Minor mode for buffers visiting blobs +========================================= + +The ‘magit-blob-mode’ enables certain Magit features in blob-visiting +buffers. Such buffers can be created using ‘magit-find-file’ and some +of the commands mentioned below, which also take care of turning on this +minor mode. Currently this mode only establishes a few key bindings, +but this might be e + +‘p’ (‘magit-blob-previous’) + + Visit the previous blob which modified the current file. + +‘n’ (‘magit-blob-next’) + + Visit the next blob which modified the current file. + +‘q’ (‘magit-kill-this-buffer’) + + Kill the current buffer. + + +File: magit.info, Node: Customizing, Next: Plumbing, Prev: Miscellaneous, Up: Top + +9 Customizing +************* + +Both Git and Emacs are highly customizable. Magit is both a Git +porcelain as well as an Emacs package, so it makes sense to customize it +using both Git variables as well as Emacs options. However this +flexibility doesn’t come without problems, including but not limited to +the following. + + • Some Git variables automatically have an effect in Magit without + requiring any explicit support. Sometimes that is desirable - in + other cases, it breaks Magit. + + When a certain Git setting breaks Magit but you want to keep using + that setting on the command line, then that can be accomplished by + overriding the value for Magit only by appending something like + ‘("-c" "some.variable=compatible-value")’ to + ‘magit-git-global-arguments’. + + • Certain settings like ‘fetch.prune=true’ are respected by Magit + commands (because they simply call the respective Git command) but + their value is not reflected in the respective popup buffers. In + this case the ‘--prune’ argument in ‘magit-fetch-popup’ might be + active or inactive depending on the value of + ‘magit-fetch-arguments’ only, but that doesn’t keep the Git + variable from being honored by the suffix commands anyway. So + pruning might happen despite the the ‘--prune’ arguments being + displayed in a way that seems to indicate that no pruning will + happen. + + I intend to address these and similar issues in a future release. + +* Menu: + +* Per-repository configuration:: +* Essential settings:: + + +File: magit.info, Node: Per-repository configuration, Next: Essential settings, Up: Customizing + +9.1 Per-repository configuration +================================ + +Magit can be configured on a per-repository level using both Git +variables as well as Emacs options. + + To set a Git variable for one repository only, simply set it in +‘/path/to/repo/.git/config’ instead of ‘$HOME/.gitconfig’ or +‘/etc/gitconfig’. See *note (gitman)git-config:: . + + Similarly, Emacs options can be set for one repository only by +editing ‘/path/to/repo/.dir-locals.el’. See *note (emacs)Directory +Variables::. For example to disable automatic refreshes of +file-visiting buffers in just one huge repository use this: + + • ‘/path/to/huge/repo/.dir-locals.el’ + + ((nil . ((magit-refresh-buffers . nil)) + + If you want to apply the same settings to several, but not all, +repositories then keeping the repository-local config files in sync +would quickly become annoying. To avoid that you can create config +files for certain classes of repositories (e.g. "huge repositories") +and then include those files in the per-repository config files. For +example: + + • ‘/path/to/huge/repo/.git/config’ + + [include] + path = /path/to/huge-gitconfig + + • ‘/path/to/huge-gitconfig’ + + [status] + showUntrackedFiles = no + + • ‘$HOME/.emacs.d/init.el’ + + (dir-locals-set-class-variables 'huge-git-repository + '((nil . ((magit-refresh-buffers . nil))))) + + (dir-locals-set-directory-class + "/path/to/huge/repo/" 'huge-git-repository) + + +File: magit.info, Node: Essential settings, Prev: Per-repository configuration, Up: Customizing + +9.2 Essential settings +====================== + +The next two sections list and discuss several variables that many users +might want to customize, for safety and/or performance reasons. + +* Menu: + +* Safety:: +* Performance:: + + +File: magit.info, Node: Safety, Next: Performance, Up: Essential settings + +9.2.1 Safety +------------ + +This section discusses various variables that you might want to change +(or *not* change) for safety reasons. + + Git keeps *committed* changes around long enough for users to recover +changes they have accidentally been deleted. It does not do the same +for *uncommitted* changes in the working tree and not even the index +(the staging area). Because Magit makes it so easy to modify +uncommitted changes, it also makes it easy to shoot yourself in the foot +in the process. For that reason Magit provides three global modes that +save *tracked* files to work-in-progress references after or before +certain actions. See *note Wip modes: Wip modes. + + These modes are not enabled by default because of performance +concerns. Instead a lot of potentially destructive commands require +confirmation every time they are used. In many cases this can be +disabled by adding a symbol to ‘magit-no-confirm’ (see *note Completion +and confirmation: Completion and confirmation.). If you enable the +various wip modes then you should add ‘safe-with-wip’ to this list. + + Similarly it isn’t necessary to require confirmation before moving a +file to the system trash - if you trashed a file by mistake then you can +recover it from the there. Option ‘magit-delete-by-moving-to-trash’ +controls whether the system trash is used, which is the case by default. +Nevertheless, ‘trash’ isn’t a member of ‘magit-no-confirm’ - you might +want to change that. + + By default buffers visiting files are automatically reverted when the +visited file changes on disk. This isn’t as risky as it might seem, but +to make an informed decision you should see *note Risk of Reverting +Automatically: Risk of Reverting Automatically. + + +File: magit.info, Node: Performance, Prev: Safety, Up: Essential settings + +9.2.2 Performance +----------------- + +After Magit has run ‘git’ for side-effects, it also refreshes the +current Magit buffer and the respective status buffer. This is +necessary because otherwise outdated information might be displayed +without the user noticing. Magit buffers are updated by recreating +their content from scratch, which makes updating simpler and less +error-prone, but also more costly. Keeping it simple and just +re-creating everything from scratch is an old design decision and +departing from that will require major refactoring. + + I plan to do that in time for the next major release. I also intend +to create logs and diffs asynchronously, which should also help a lot +but also requires major refactoring. + + Meanwhile you can tell Magit to only automatically refresh the +current Magit buffer, but not the status buffer. If you do that, then +the status buffer is only refreshed automatically if it itself is the +current buffer. + + (setq magit-refresh-status-buffer nil) + + You should also check whether any third-party packages have added +anything to ‘magit-refresh-buffer-hook’, ‘magit-status-refresh-hook’, +‘magit-pre-refresh-hook’, and ‘magit-post-refresh-hook’. If so, then +check whether those additions impacts performance significantly. +Setting ‘magit-refresh-verbose’ and then inspecting the output in the +‘*Messages*’ buffer, should help doing so. + + Magit also reverts buffers which visit files located inside the +current repository, when the visited file changes on disk. That is +implemented on top of ‘auto-revert-mode’ from the built-in library +‘autorevert’. To figure out whether that impacts performance, check +whether performance is significantly worse, when many buffers exist +and/or when some buffers visit files using Tramp. If so, then this +should help. + + (setq auto-revert-buffer-list-filter + 'magit-auto-revert-repository-buffers-p) + + For alternative approaches see *note Automatic Reverting of +File-Visiting Buffers: Automatic Reverting of File-Visiting Buffers. + + If you have enabled any features that are disabled by default, then +you should check whether they impact performance significantly. It’s +likely that they were not enabled by default because it is known that +they reduce performance at least in large repositories. + + If performance is only slow inside certain unusually large +repositories, then you might want to disable certain features on a +per-repository or per-repository-class basis only. See *note +Per-repository configuration: Per-repository configuration. + +* Menu: + +* Committing Performance:: + +Microsoft Windows Performance +............................. + +In order to update the status buffer, ‘git’ has to be run a few dozen +times. That is only problematic on Microsoft Windows, because that +operating system is exceptionally slow at starting processes. Sadly +this is an issue that can only be fixed by Microsoft itself, and they +don’t appear to particularly interested in doing so. + + Beside the subprocess issue, there also exist other Window-specific +performance issues, some of which can be worked around. The maintainers +of "Git for Windows" try to reduce their effect, and in order to benefit +from the latest performance tweaks, should always use the latest +release. Magit too tries to work around some Windows-specific issues. + + But all these efforts might not be enough, forcing users to make some +changes themselves. For example, according to +, setting the following Git +variables might also help: + + git config --global core.preloadindex true + git config --global core.fscache true + git config --global gc.auto 256 + + You should also check whether an anti-virus program is slowing things +down. + +Log Performance +............... + +When showing logs, Magit limits the number of commits initially shown in +the hope that this avoids unnecessary work. When using ‘--graph’ is +used, then this unfortunately does not have the desired effect for large +histories. Junio, Git’s maintainer, said on the git mailing list +(): "‘--graph’ wants to +compute the whole history and the max-count only affects the output +phase after ‘--graph’ does its computation". + + In other words, it’s not that Git is slow at outputting the +differences, or that Magit is slow at parsing the output - the problem +is that Git first goes outside and has a smoke. + + We actually work around this issue by limiting the number of commits +not only by using ‘-’ but by also using a range. But unfortunately +that’s not always possible. + + In repositories with more than a few thousand commits ‘--graph’ +should never be a member of ‘magit-log-section-arguments’. That +variable is used in the status buffer which is refreshed every time you +run any Magit command. + + Using ‘--color --graph’ is even slower. Magit uses code that is part +of Emacs to turn control characters into faces. That code is pretty +slow and this is quite noticeable when showing a log with many branches +and merges. For that reason ‘--color’ is not enabled by default +anymore. Consider leaving it at that. + +Diff Performance +................ + +If diffs are slow, then consider turning off some optional diff features +by setting all or some of the following variables to ‘nil’: +‘magit-diff-highlight-indentation’, ‘magit-diff-highlight-trailing’, +‘magit-diff-paint-whitespace’, ‘magit-diff-highlight-hunk-body’, and +‘magit-diff-refine-hunk’. + + When showing a commit instead of some arbitrary diff, then some +additional information is displayed. Calculating this information can +be quite expensive given certain circumstances. If looking at a commit +using ‘magit-revision-mode’ takes considerably more time than looking at +the same commit in ‘magit-diff-mode’, then consider setting +‘magit-revision-insert-related-refs’ to ‘nil’. + +Refs Buffer Performance +....................... + +When refreshing the "references buffer" is slow, then that’s usually +because several hundred refs are being displayed. The best way to +address that is to display fewer refs, obviously. + + If you are not, or only mildly, interested in seeing the list of +tags, then start by not displaying them: + + (remove-hook 'magit-refs-sections-hook 'magit-insert-tags) + + Then you should also make sure that the listed remote branches +actually all exist. You can do so by pruning branches which no longer +exist using ‘f-pa’. + + +File: magit.info, Node: Committing Performance, Up: Performance + +Committing Performance +...................... + +When you initiate a commit, then Magit by default automatically shows a +diff of the changes you are about to commit. For large commits this can +take a long time, which is especially distracting when you are +committing large amounts of generated data which you don’t actually +intend to inspect before committing. This behavior can be turned off +using: + + (remove-hook 'server-switch-hook 'magit-commit-diff) + + Then you can type ‘C-c C-d’ to show the diff when you actually want +to see it, but only then. Alternatively you can leave the hook alone +and just type ‘C-g’ in those cases when it takes to long to generate the +diff. If you do that, then you will end up with a broken diff buffer, +but doing it this way has the advantage that you usually get to see the +diff, which is useful because it increases the odds that you spot +potential issues. + + +File: magit.info, Node: Plumbing, Next: FAQ, Prev: Customizing, Up: Top + +10 Plumbing +*********** + +The following sections describe how to use several of Magit’s core +abstractions to extend Magit itself or implement a separate extension. + + A few of the low-level features used by Magit have been factored out +into separate libraries/packages, so that they can be used by other +packages, without having to depend on Magit. These libraries are +described in separate manuals, see *note (with-editor)Top:: and *note +(magit-popup)Top::. + +* Menu: + +* Calling Git:: +* Section plumbing:: +* Refreshing buffers:: +* Conventions:: + + +File: magit.info, Node: Calling Git, Next: Section plumbing, Up: Plumbing + +10.1 Calling Git +================ + +Magit provides many specialized functions for calling Git. All of these +functions are defined in either ‘magit-git.el’ or ‘magit-process.el’ and +have one of the prefixes ‘magit-run-’, ‘magit-call-’, ‘magit-start-’, or +‘magit-git-’ (which is also used for other things). + + All of these functions accept an indefinite number of arguments, +which are strings that specify command line arguments for git (or in +some cases an arbitrary executable). These arguments are flattened +before being passed on to the executable; so instead of strings they can +also be lists of strings and arguments that are ‘nil’ are silently +dropped. Some of these functions also require a single mandatory +argument before these command line arguments. + + Roughly speaking these functions run Git either to get some value or +for side-effect. The functions that return a value are useful to +collect the information necessary to populate a Magit buffer, while the +others are used to implement Magit commands. + + The functions in the value-only group always run synchronously, and +they never trigger a refresh. The function in the side-effect group can +be further divided into subgroups depending on whether they run Git +synchronously or asynchronously, and depending on whether they trigger a +refresh when the executable has finished. + +* Menu: + +* Getting a value from Git:: +* Calling Git for effect:: + + +File: magit.info, Node: Getting a value from Git, Next: Calling Git for effect, Up: Calling Git + +10.1.1 Getting a value from Git +------------------------------- + +These functions run Git in order to get a value, either its exit status +or its output. Of course you could also use them to run Git commands +that have side-effects, but that should be avoided. + + -- Function: magit-git-exit-code &rest args + + Executes git with ARGS and returns its exit code. + + -- Function: magit-git-success &rest args + + Executes git with ARGS and returns ‘t’ if the exit code is ‘0’, + ‘nil’ otherwise. + + -- Function: magit-git-failure &rest args + + Executes git with ARGS and returns ‘t’ if the exit code is ‘1’, + ‘nil’ otherwise. + + -- Function: magit-git-true &rest args + + Executes git with ARGS and returns ‘t’ if the first line printed by + git is the string "true", ‘nil’ otherwise. + + -- Function: magit-git-false &rest args + + Executes git with ARGS and returns ‘t’ if the first line printed by + git is the string "false", ‘nil’ otherwise. + + -- Function: magit-git-insert &rest args + + Executes git with ARGS and inserts its output at point. + + -- Function: magit-git-string &rest args + + Executes git with ARGS and returns the first line of its output. + If there is no output or if it begins with a newline character, + then this returns ‘nil’. + + -- Function: magit-git-lines &rest args + + Executes git with ARGS and returns its output as a list of lines. + Empty lines anywhere in the output are omitted. + + -- Function: magit-git-items &rest args + + Executes git with ARGS and returns its null-separated output as a + list. Empty items anywhere in the output are omitted. + + If the value of option ‘magit-git-debug’ is non-nil and git exits + with a non-zero exit status, then warn about that in the echo area + and add a section containing git’s standard error in the current + repository’s process buffer. + + When an error occurs when using one of the above functions, then that +is usually due to a bug, i.e. the use of an argument which is not +actually supported. Such errors are usually not reported, but when they +occur we need to be able to debug them. + + -- User Option: magit-git-debug + + Whether to report errors that occur when using ‘magit-git-insert’, + ‘magit-git-string’, ‘magit-git-lines’, or ‘magit-git-items’. This + does not actually raise an error. Instead a message is shown in + the echo area, and git’s standard error is insert into a new + section in the current repository’s process buffer. + + -- Function: magit-git-str &rest args + + This is a variant of ‘magit-git-string’ that ignores the option + ‘magit-git-debug’. It is mainly intended to be used while handling + errors in functions that do respect that option. Using such a + function while handing an error could cause yet another error and + therefore lead to an infinite recursion. You probably won’t ever + need to use this function. + + +File: magit.info, Node: Calling Git for effect, Prev: Getting a value from Git, Up: Calling Git + +10.1.2 Calling Git for effect +----------------------------- + +These functions are used to run git to produce some effect. Most Magit +commands that actually run git do so by using such a function. + + Because we do not need to consume git’s output when using these +functions, their output is instead logged into a per-repository buffer, +which can be shown using ‘$’ from a Magit buffer or ‘M-x magit-process’ +elsewhere. + + These functions can have an effect in two distinct ways. Firstly, +running git may change something, i.e. create or push a new commit. +Secondly, that change may require that Magit buffers are refreshed to +reflect the changed state of the repository. But refreshing isn’t +always desirable, so only some of these functions do perform such a +refresh after git has returned. + + Sometimes it is useful to run git asynchronously. For example, when +the user has just initiated a push, then there is no reason to make her +wait until that has completed. In other cases it makes sense to wait +for git to complete before letting the user do something else. For +example after staging a change it is useful to wait until after the +refresh because that also automatically moves to the next change. + + -- Function: magit-call-git &rest args + + Calls git synchronously with ARGS. + + -- Function: magit-call-process program &rest args + + Calls PROGRAM synchronously with ARGS. + + -- Function: magit-run-git &rest args + + Calls git synchronously with ARGS and then refreshes. + + -- Function: magit-run-git-with-input input &rest args + + Calls git synchronously with ARGS and sends it INPUT on standard + input. + + INPUT should be a buffer or the name of an existing buffer. The + content of that buffer is used as the process’ standard input. + After the process returns a refresh is performed. + + As a special case, INPUT may also be nil. In that case the content + of the current buffer is used as standard input and *no* refresh is + performed. + + This function actually runs git asynchronously. But then it waits + for the process to return, so the function itself is synchronous. + + -- Function: magit-run-git-with-logfile file &rest args + + Calls git synchronously with ARGS. The process’ output is saved in + FILE. This is rarely useful and so this function might be removed + in the future. + + This function actually runs git asynchronously. But then it waits + for the process to return, so the function itself is synchronous. + + -- Function: magit-git &rest args + + Calls git synchronously with ARGS for side-effects only. This + function does not refresh the buffer. + + -- Function: magit-git-wash washer &rest args + + Execute Git with ARGS, inserting washed output at point. Actually + first insert the raw output at point. If there is no output call + ‘magit-cancel-section’. Otherwise temporarily narrow the buffer to + the inserted text, move to its beginning, and then call function + WASHER with no argument. + + And now for the asynchronous variants. + + -- Function: magit-run-git-async &rest args + + Start Git, prepare for refresh, and return the process object. + ARGS is flattened and then used as arguments to Git. + + Display the command line arguments in the echo area. + + After Git returns some buffers are refreshed: the buffer that was + current when this function was called (if it is a Magit buffer and + still alive), as well as the respective Magit status buffer. + Unmodified buffers visiting files that are tracked in the current + repository are reverted if ‘magit-revert-buffers’ is non-nil. + + -- Function: magit-run-git-with-editor &rest args + + Export GIT_EDITOR and start Git. Also prepare for refresh and + return the process object. ARGS is flattened and then used as + arguments to Git. + + Display the command line arguments in the echo area. + + After Git returns some buffers are refreshed: the buffer that was + current when this function was called (if it is a Magit buffer and + still alive), as well as the respective Magit status buffer. + + -- Function: magit-start-git &rest args + + Start Git, prepare for refresh, and return the process object. + + If INPUT is non-nil, it has to be a buffer or the name of an + existing buffer. The buffer content becomes the processes standard + input. + + Option ‘magit-git-executable’ specifies the Git executable and + option ‘magit-git-global-arguments’ specifies constant arguments. + The remaining arguments ARGS specify arguments to Git. They are + flattened before use. + + After Git returns, some buffers are refreshed: the buffer that was + current when this function was called (if it is a Magit buffer and + still alive), as well as the respective Magit status buffer. + Unmodified buffers visiting files that are tracked in the current + repository are reverted if ‘magit-revert-buffers’ is non-nil. + + -- Function: magit-start-process &rest args + + Start PROGRAM, prepare for refresh, and return the process object. + + If optional argument INPUT is non-nil, it has to be a buffer or the + name of an existing buffer. The buffer content becomes the + processes standard input. + + The process is started using ‘start-file-process’ and then setup to + use the sentinel ‘magit-process-sentinel’ and the filter + ‘magit-process-filter’. Information required by these functions is + stored in the process object. When this function returns the + process has not started to run yet so it is possible to override + the sentinel and filter. + + After the process returns, ‘magit-process-sentinel’ refreshes the + buffer that was current when ‘magit-start-process’ was called (if + it is a Magit buffer and still alive), as well as the respective + Magit status buffer. Unmodified buffers visiting files that are + tracked in the current repository are reverted if + ‘magit-revert-buffers’ is non-nil. + + -- Variable: magit-this-process + + The child process which is about to start. This can be used to + change the filter and sentinel. + + -- Variable: magit-process-raise-error + + When this is non-nil, then ‘magit-process-sentinel’ raises an error + if git exits with a non-zero exit status. For debugging purposes. + + +File: magit.info, Node: Section plumbing, Next: Refreshing buffers, Prev: Calling Git, Up: Plumbing + +10.2 Section plumbing +===================== + +* Menu: + +* Creating sections:: +* Section selection:: +* Matching sections:: + + +File: magit.info, Node: Creating sections, Next: Section selection, Up: Section plumbing + +10.2.1 Creating sections +------------------------ + + -- Macro: magit-insert-section &rest args + + Insert a section at point. + + TYPE is the section type, a symbol. Many commands that act on the + current section behave differently depending on that type. Also if + a variable ‘magit-TYPE-section-map’ exists, then use that as the + text-property ‘keymap’ of all text belonging to the section (but + this may be overwritten in subsections). + + Optional VALUE is the value of the section, usually a string that + is required when acting on the section. + + When optional HIDE is non-nil collapse the section body by default, + i.e. when first creating the section, but not when refreshing the + buffer. Otherwise, expand it by default. This can be overwritten + using ‘magit-section-set-visibility-hook’. When a section is + recreated during a refresh, then the visibility of predecessor is + inherited and HIDE is ignored (but the hook is still honored). + + BODY is any number of forms that actually insert the section’s + heading and body. Optional NAME, if specified, has to be a symbol, + which is then bound to the struct of the section being inserted. + + Before BODY is evaluated the ‘start’ of the section object is set + to the value of ‘point’ and after BODY was evaluated its ‘end’ is + set to the new value of ‘point’; BODY is responsible for moving + ‘point’ forward. + + If it turns out inside BODY that the section is empty, then + ‘magit-cancel-section’ can be used to abort and remove all traces + of the partially inserted section. This can happen when creating a + section by washing Git’s output and Git didn’t actually output + anything this time around. + + -- Function: magit-insert-heading &rest args + + Insert the heading for the section currently being inserted. + + This function should only be used inside ‘magit-insert-section’. + + When called without any arguments, then just set the ‘content’ slot + of the object representing the section being inserted to a marker + at ‘point’. The section should only contain a single line when + this function is used like this. + + When called with arguments ARGS, which have to be strings, then + insert those strings at point. The section should not contain any + text before this happens and afterwards it should again only + contain a single line. If the ‘face’ property is set anywhere + inside any of these strings, then insert all of them unchanged. + Otherwise use the ‘magit-section-heading’ face for all inserted + text. + + The ‘content’ property of the section struct is the end of the + heading (which lasts from ‘start’ to ‘content’) and the beginning + of the body (which lasts from ‘content’ to ‘end’). If the value of + ‘content’ is nil, then the section has no heading and its body + cannot be collapsed. If a section does have a heading then its + height must be exactly one line, including a trailing newline + character. This isn’t enforced; you are responsible for getting it + right. The only exception is that this function does insert a + newline character if necessary. + + -- Function: magit-cancel-section + + Cancel the section currently being inserted. This exits the + innermost call to ‘magit-insert-section’ and removes all traces of + what has already happened inside that call. + + -- Function: magit-define-section-jumper sym title &optional value + + Define an interactive function to go to section SYM. TITLE is the + displayed title of the section. + + +File: magit.info, Node: Section selection, Next: Matching sections, Prev: Creating sections, Up: Section plumbing + +10.2.2 Section selection +------------------------ + + -- Function: magit-current-section + + Return the section at point. + + -- Function: magit-region-sections + + Return a list of the selected sections. + + When the region is active and constitutes a valid section + selection, then return a list of all selected sections. This is + the case when the region begins in the heading of a section and + ends in the heading of a sibling of that first section. When the + selection is not valid then return nil. Most commands that can act + on the selected sections, then instead just act on the current + section, the one point is in. + + When the region looks like it would in any other buffer then the + selection is invalid. When the selection is valid then the region + uses the ‘magit-section-highlight’. This does not apply to diffs + where things get a bit more complicated, but even here if the + region looks like it usually does, then that’s not a valid + selection as far as this function is concerned. + + -- Function: magit-region-values &rest types + + Return a list of the values of the selected sections. + + Also see ‘magit-region-sections’ whose doc-string explains when a + region is a valid section selection. If the region is not active + or is not a valid section selection, then return nil. If optional + TYPES is non-nil then the selection not only has to be valid; the + types of all selected sections additionally have to match one of + TYPES, or nil is returned. + + +File: magit.info, Node: Matching sections, Prev: Section selection, Up: Section plumbing + +10.2.3 Matching sections +------------------------ + +‘M-x magit-describe-section’ (‘magit-describe-section’) + + Show information about the section at point. This command is + intended for debugging purposes. + + -- Function: magit-section-ident + + Return an unique identifier for SECTION. The return value has the + form ‘((TYPE . VALUE)...)’. + + -- Function: magit-get-section + + Return the section identified by IDENT. IDENT has to be a list as + returned by ‘magit-section-ident’. + + -- Function: magit-section-match condition &optional section + + Return ‘t’ if SECTION matches CONDITION. SECTION defaults to the + section at point. + + Conditions can take the following forms: + • ‘(CONDITION...)’ + + matches if any of the CONDITIONs matches. + + • ‘[TYPE...]’ + + matches if the first TYPE matches the type of the section at + point, the second matches that of its parent, and so on. + + • ‘[* TYPE...]’ + + matches sections that match [TYPE…] and also recursively all + their child sections. + + • ‘TYPE’ + + matches TYPE regardless of its parents. + Each TYPE is a symbol. Note that is not necessary to specify all + TYPEs up to the root section as printed by ‘magit-describe-type’, + unless of course your want to be that precise. + + -- Function: magit-section-when condition &rest body + + If the section at point matches CONDITION evaluate BODY. + + If the section matches evaluate BODY forms sequentially and return + the value of the last one, or if there are no BODY forms return the + value of the section. If the section does not match return nil. + + See ‘magit-section-match’ for the forms CONDITION can take. + + -- Function: magit-section-case &rest clauses + + Choose among clauses on the type of the section at point. + + Each clause looks like (CONDITION BODY…). The type of the section + is compared against each CONDITION; the BODY forms of the first + match are evaluated sequentially and the value of the last form is + returned. Inside BODY the symbol ‘it’ is bound to the section at + point. If no clause succeeds or if there is no section at point + return nil. + + See ‘magit-section-match’ for the forms CONDITION can take. + Additionally a CONDITION of t is allowed in the final clause and + matches if no other CONDITION match, even if there is no section at + point. + + -- Variable: magit-root-section + + The root section in the current buffer. All other sections are + descendants of this section. The value of this variable is set by + ‘magit-insert-section’ and you should never modify it. + + For diff related sections a few additional tools exist. + + -- Function: magit-diff-type &optional section + + Return the diff type of SECTION. + + The returned type is one of the symbols ‘staged’, ‘unstaged’, + ‘committed’, or ‘undefined’. This type serves a similar purpose as + the general type common to all sections (which is stored in the + ‘type’ slot of the corresponding ‘magit-section’ struct) but takes + additional information into account. When the SECTION isn’t + related to diffs and the buffer containing it also isn’t a + diff-only buffer, then return nil. + + Currently the type can also be one of ‘tracked’ and ‘untracked’, + but these values are not handled explicitly in every place they + should be. A possible fix could be to just return nil here. + + The section has to be a ‘diff’ or ‘hunk’ section, or a section + whose children are of type ‘diff’. If optional SECTION is nil, + return the diff type for the current section. In buffers whose + major mode is ‘magit-diff-mode’ SECTION is ignored and the type is + determined using other means. In ‘magit-revision-mode’ buffers the + type is always ‘committed’. + + -- Function: magit-diff-scope &optional section strict + + Return the diff scope of SECTION or the selected section(s). + + A diff’s "scope" describes what part of a diff is selected, it is a + symbol, one of ‘region’, ‘hunk’, ‘hunks’, ‘file’, ‘files’, or + ‘list’. Do not confuse this with the diff "type", as returned by + ‘magit-diff-type’. + + If optional SECTION is non-nil, then return the scope of that, + ignoring the sections selected by the region. Otherwise return the + scope of the current section, or if the region is active and + selects a valid group of diff related sections, the type of these + sections, i.e. ‘hunks’ or ‘files’. If SECTION (or if the current + section that is nil) is a ‘hunk’ section and the region starts and + ends inside the body of a that section, then the type is ‘region’. + + If optional STRICT is non-nil then return nil if the diff type of + the section at point is ‘untracked’ or the section at point is not + actually a ‘diff’ but a ‘diffstat’ section. + + +File: magit.info, Node: Refreshing buffers, Next: Conventions, Prev: Section plumbing, Up: Plumbing + +10.3 Refreshing buffers +======================= + +All commands that create a new Magit buffer or change what is being +displayed in an existing buffer do so by calling ‘magit-mode-setup’. +Among other things, that function sets the buffer local values of +‘default-directory’ (to the top-level of the repository), +‘magit-refresh-function’, and ‘magit-refresh-args’. + + Buffers are refreshed by calling the function that is the local value +of ‘magit-refresh-function’ (a function named ‘magit-*-refresh-buffer’, +where ‘*’ may be something like ‘diff’) with the value of +‘magit-refresh-args’ as arguments. + + -- Macro: magit-mode-setup buffer switch-func mode refresh-func + &optional refresh-args + + This function displays and selects BUFFER, turns on MODE, and + refreshes a first time. + + This function displays and optionally selects BUFFER by calling + ‘magit-mode-display-buffer’ with BUFFER, MODE and SWITCH-FUNC as + arguments. Then it sets the local value of + ‘magit-refresh-function’ to REFRESH-FUNC and that of + ‘magit-refresh-args’ to REFRESH-ARGS. Finally it creates the buffer + content by calling REFRESH-FUNC with REFRESH-ARGS as arguments. + + All arguments are evaluated before switching to BUFFER. + + -- Function: magit-mode-display-buffer buffer mode &optional + switch-function + + This function display BUFFER in some window and select it. BUFFER + may be a buffer or a string, the name of a buffer. The buffer is + returned. + + Unless BUFFER is already displayed in the selected frame, store the + previous window configuration as a buffer local value, so that it + can later be restored by ‘magit-mode-bury-buffer’. + + The buffer is displayed and selected using SWITCH-FUNCTION. If that + is ‘nil’ then ‘pop-to-buffer’ is used if the current buffer’s major + mode derives from ‘magit-mode’. Otherwise ‘switch-to-buffer’ is + used. + + -- Variable: magit-refresh-function + + The value of this buffer-local variable is the function used to + refresh the current buffer. It is called with ‘magit-refresh-args’ + as arguments. + + -- Variable: magit-refresh-args + + The list of arguments used by ‘magit-refresh-function’ to refresh + the current buffer. ‘magit-refresh-function’ is called with these + arguments. + + The value is usually set using ‘magit-mode-setup’, but in some + cases it’s also useful to provide commands which can change the + value. For example, the ‘magit-diff-refresh-popup’ can be used to + change any of the arguments used to display the diff, without + having to specify again which differences should be shown. + ‘magit-diff-more-context’, ‘magit-diff-less-context’, and + ‘magit-diff-default-context’ change just the ‘-U’ argument. In + both case this is done by changing the value of this variable and + then calling this ‘magit-refresh-function’. + + +File: magit.info, Node: Conventions, Prev: Refreshing buffers, Up: Plumbing + +10.4 Conventions +================ + +* Menu: + +* Confirmation and completion:: +* Theming Faces:: + + +File: magit.info, Node: Confirmation and completion, Next: Theming Faces, Up: Conventions + +10.4.1 Confirmation and completion +---------------------------------- + +Dangerous operations that may lead to data loss have to be confirmed by +default. With a multi-section selection, this is done using questions +that can be answered with "yes" or "no". When the region isn’t active, +or if it doesn’t constitute a valid section selection, then such +commands instead read a single item in the minibuffer. When the value +of the current section is among the possible choices, then that is +presented as default choice. To confirm the action on a single item, +the user has to answer ‘RET’ (instead of "yes"), and to abort, ‘C-g’ +(instead of "no"). But alternatively the user may also select another +item, just like if the command had been invoked with no suitable section +at point at all. + + +File: magit.info, Node: Theming Faces, Prev: Confirmation and completion, Up: Conventions + +10.4.2 Theming Faces +-------------------- + +The default theme uses blue for local branches, green for remote +branches, and goldenrod (brownish yellow) for tags. When creating a new +theme, you should probably follow that example. If your theme already +uses other colors, then stick to that. + + In older releases these reference faces used to have a background +color and a box around them. The basic default faces no longer do so, +to make Magit buffers much less noisy, and you should follow that +example at least with regards to boxes. (Boxes were used in the past to +work around a conflict between the highlighting overlay and text +property backgrounds. That’s no longer necessary because highlighting +no longer causes other background colors to disappear.) Alternatively +you can keep the background color and/or box, but then have to take +special care to adjust ‘magit-branch-current’ accordingly. By default +it looks mostly like ‘magit-branch-local’, but with a box (by default +the former is the only face that uses a box, exactly so that it sticks +out). If the former also uses a box, then you have to make sure that it +differs in some other way from the latter. + + The most difficult faces to theme are those related to diffs, +headings, highlighting, and the region. There are faces that fall into +all four groups - expect to spend some time getting this right. + + The ‘region’ face in the default theme, in both the light and dark +variants, as well as in many other themes, distributed with Emacs or by +third-parties, is very ugly. It is common to use a background color +that really sticks out, which is ugly but if that were the only problem +then it would be acceptable. Unfortunately many themes also set the +foreground color, which ensures that all text within the region is +readable. Without doing that there might be cases where some foreground +color is too close to the region background color to still be readable. +But it also means that text within the region loses all syntax +highlighting. + + I consider the work that went into getting the ‘region’ face right to +be a good indicator for the general quality of a theme. My +recommendation for the ‘region’ face is this: use a background color +slightly different from the background color of the ‘default’ face, and +do not set the foreground color at all. So for a light theme you might +use a light (possibly tinted) gray as the background color of ‘default’ +and a somewhat darker gray for the background of ‘region’. That should +usually be enough to not collide with the foreground color of any other +face. But if some other faces also set a light gray as background +color, then you should also make sure it doesn’t collide with those (in +some cases it might be acceptable though). + + Magit only uses the ‘region’ face when the region is "invalid" by its +own definition. In a Magit buffer the region is used to either select +multiple sibling sections, so that commands which support it act on all +of these sections instead of just the current section, or to select +lines within a single hunk section. In all other cases, the section is +considered invalid and Magit won’t act on it. But such invalid sections +happen, either because the user has not moved point enough yet to make +it valid or because she wants to use a non-magit command to act on the +region, e.g. ‘kill-region’. + + So using the regular ‘region’ face for invalid sections is a feature. +It tells the user that Magit won’t be able to act on it. It’s +acceptable if that face looks a bit odd and even (but less so) if it +collides with the background colors of section headings and other things +that have a background color. + + Magit highlights the current section. If a section has subsections, +then all of them are highlighted. This is done using faces that have +"highlight" in their names. For most sections, +‘magit-section-highlight’ is used for both the body and the heading. +Like the ‘region’ face, it should only set the background color to +something similar to that of ‘default’. The highlight background color +must be different from both the ‘region’ background color and the +‘default’ background color. + + For diff related sections Magit uses various faces to highlight +different parts of the selected section(s). Note that hunk headings, +unlike all other section headings, by default have a background color, +because it is useful to have very visible separators between hunks. +That face ‘magit-diff-hunk-heading’, should be different from both +‘magit-diff-hunk-heading-highlight’ and ‘magit-section-highlight’, as +well as from ‘magit-diff-context’ and ‘magit-diff-context-highlight’. +By default we do that by changing the foreground color. Changing the +background color would lead to complications, and there are already +enough we cannot get around. (Also note that it is generally a good +idea for section headings to always be bold, but only for sections that +have subsections). + + When there is a valid region selecting diff-related sibling sections, +i.e. multiple files or hunks, then the bodies of all these sections use +the respective highlight faces, but additionally the headings instead +use one of the faces ‘magit-diff-file-heading-selection’ or +‘magit-diff-hunk-heading-selection’. These faces have to be different +from the regular highlight variants to provide explicit visual +indication that the region is active. + + When theming diff related faces, start by setting the option +‘magit-diff-refine-hunk’ to ‘all’. You might personally prefer to only +refine the current hunk or not use hunk refinement at all, but some of +the users of your theme want all hunks to be refined, so you have to +cater to that. + + (Also turn on ‘magit-diff-highlight-indentation’, +‘magit-diff-highlight-trailing’, and ‘magit-diff-paint-whitespace’; and +insert some whitespace errors into the code you use for testing.) + + For e.g. "added lines" you have to adjust three faces: +‘magit-diff-added’, ‘magit-diff-added-highlight’, and +‘smerge-refined-added’. Make sure that the latter works well with both +of the former, as well as ‘smerge-other’ and ‘diff-added’. Then do the +same for the removed lines, context lines, lines added by us, and lines +added by them. Also make sure the respective added, removed, and +context faces use approximately the same saturation for both the +highlighted and unhighlighted variants. Also make sure the file and +diff headings work nicely with context lines (e.g. make them look +different). Line faces should set both the foreground and the +background color. For example, for added lines use two different +greens. + + It’s best if the foreground color of both the highlighted and the +unhighlighted variants are the same, so you will need to have to find a +color that works well on the highlight and unhighlighted background, the +refine background, and the highlight context background. When there is +an hunk internal region, then the added- and removed-lines background +color is used only within that region. Outside the region the +highlighted context background color is used. This makes it easier to +see what is being staged. With an hunk internal region the hunk heading +is shown using ‘magit-diff-hunk-heading-selection’, and so are the thin +lines that are added around the lines that fall within the region. The +background color of that has to be distinct enough from the various +other involved background colors. + + Nobody said this would be easy. If your theme restricts itself to a +certain set of colors, then you should make an exception here. +Otherwise it would be impossible to make the diffs look good in each and +every variation. Actually you might want to just stick to the default +definitions for these faces. You have been warned. Also please note +that if you do not get this right, this will in some cases look to users +like bugs in Magit - so please do it right or not at all. + + +File: magit.info, Node: FAQ, Next: Keystroke Index, Prev: Plumbing, Up: Top + +Appendix A FAQ +************** + +Below you find a list of frequently asked questions. For a list of +frequently *and recently* asked questions, i.e. questions that haven’t +made it into the manual yet, see +. + +* Menu: + +* Magit is slow:: +* I changed several thousand files at once and now Magit is unusable:: +* I am having problems committing:: +* I don't understand how branching and pushing work:: +* I don’t like the key binding in v2.4: I don't like the key binding in v24. +* I cannot install the pre-requisites for Magit v2:: +* I am using an Emacs release older than v24.4: I am using an Emacs release older than v244. +* I am using a Git release older than v1.9.4: I am using a Git release older than v194. +* I am using MS Windows and cannot push with Magit:: +* How to install the gitman info manual?:: +* How can I show Git's output?:: +* Expanding a file to show the diff causes it to disappear:: +* Point is wrong in the COMMIT_EDITMSG buffer:: +* Can Magit be used as ediff-version-control-package?:: +* How to show diffs for gpg-encrypted files?:: +* Emacs 24.5 hangs when loading Magit: Emacs 245 hangs when loading Magit. +* Symbol's value as function is void --some:: +* Where is the branch manager:: + + +File: magit.info, Node: Magit is slow, Next: I changed several thousand files at once and now Magit is unusable, Up: FAQ + +A.1 Magit is slow +================= + +See *note Performance: Performance. + + +File: magit.info, Node: I changed several thousand files at once and now Magit is unusable, Next: I am having problems committing, Prev: Magit is slow, Up: FAQ + +A.2 I changed several thousand files at once and now Magit is unusable +====================================================================== + +Magit is *currently* not expected to work under such conditions. It +sure would be nice if it did, and v2.5 will hopefully be a big step into +that direction. But it might take until v3.1 to accomplish fully +satisfactory performance, because that requires some heavy refactoring. + + But for now we recommend you use the command line to complete this +one commit. Also see *note Performance: Performance. + + +File: magit.info, Node: I am having problems committing, Next: I don't understand how branching and pushing work, Prev: I changed several thousand files at once and now Magit is unusable, Up: FAQ + +A.3 I am having problems committing +=================================== + +That likely means that Magit is having problems finding an appropriate +emacsclient executable. See *note (with-editor)Configuring +With-Editor:: and *note (with-editor)Debugging::. + + +File: magit.info, Node: I don't understand how branching and pushing work, Next: I don't like the key binding in v24, Prev: I am having problems committing, Up: FAQ + +A.4 I don’t understand how branching and pushing work +===================================================== + +Please see *note Branching: Branching. and + + + +File: magit.info, Node: I don't like the key binding in v24, Next: I cannot install the pre-requisites for Magit v2, Prev: I don't understand how branching and pushing work, Up: FAQ + +A.5 I don’t like the key binding in v2.4 +======================================== + +Please see . + + +File: magit.info, Node: I cannot install the pre-requisites for Magit v2, Next: I am using an Emacs release older than v244, Prev: I don't like the key binding in v24, Up: FAQ + +A.6 I cannot install the pre-requisites for Magit v2 +==================================================== + +An Elpa archive featuring obsolete Magit v1.4.2 and its dependencies is +available from . But note that v1.4.2 is +obsolete and no longer maintained. + + +File: magit.info, Node: I am using an Emacs release older than v244, Next: I am using a Git release older than v194, Prev: I cannot install the pre-requisites for Magit v2, Up: FAQ + +A.7 I am using an Emacs release older than v24.4 +================================================ + +At least Emacs v24.4 is required. There is no way around it, if you +want to use Magit v2. + + If you own the machine you work on, then consider updating to the +latest release provided by your distribution. If it doesn’t feature a +recent enough release, then you will have to use a backport package or +build Emacs from source. + + Installing Emacs from source is quite simple. See the instructions +at and + to get an +idea of that this involves. But when you perform the installation then +use the instructions for the release you are actually installing. + + Unfortunately these instructions do not cover the hardest part (which +is the hardest part exactly because it is not covered there): installing +the build time dependencies. + + For that you’ll need to perform a web search and find an appropriate +tutorial for your distribution. If you think you should not have had to +do that yourself, then consider informing me about the resources that +helped you figure what to do for your specific setup, so that I can post +a link here. That way those coming after you have it easier. + + An Elpa archive featuring obsolete Magit v1.4.2 and its dependencies +is available from . + + +File: magit.info, Node: I am using a Git release older than v194, Next: I am using MS Windows and cannot push with Magit, Prev: I am using an Emacs release older than v244, Up: FAQ + +A.8 I am using a Git release older than v1.9.4 +============================================== + +At least Git v1.9.4 is required. There is no way around it, if you want +to use Magit v2. + + If you own the machine, then consider updating to the latest release +provided by your distribution. If it doesn’t feature a recent enough +release, then you will have to use a backport package or build Git from +source. + + Installing Git from source is quite simple. See the instructions at + to get an idea of that +this involves. But when you perform the installation then use the +instructions for the release you are actually installing. + + An Elpa archive featuring obsolete Magit v1.4.2 and its dependencies +is available from . + + +File: magit.info, Node: I am using MS Windows and cannot push with Magit, Next: How to install the gitman info manual?, Prev: I am using a Git release older than v194, Up: FAQ + +A.9 I am using MS Windows and cannot push with Magit +==================================================== + +It’s almost certain that Magit is only incidental to this issue. It is +much more likely that this is a configuration issue, even if you can +push on the command line. + + Detailed setup instructions can be found at +. + + +File: magit.info, Node: How to install the gitman info manual?, Next: How can I show Git's output?, Prev: I am using MS Windows and cannot push with Magit, Up: FAQ + +A.10 How to install the gitman info manual? +=========================================== + +Git’s manpages can be exported as an info manual called ‘gitman’. +Magit’s own info manual links to nodes in that manual instead of the +actual manpages because texinfo sadly doesn’t support linking to +manpages. + + Unfortunately many distributions do not install the ‘gitman’ manual +by default. Some distributions may provide a separate package +containing the info manual. Please let me know the name of that package +for your distribution, so that I can mention here. + + If the distribution you are using does not offer a package that +contains the ‘gitman’ manual, then you have to install it manually. +Clone Git’s own Git repository, checkout the tag corresponding to the +Git release you have installed, and follow the instructions in +‘INSTALL’. The relevant make targets are ‘info’ and ‘install-info’. + + Alternatively you may add this advice to your ‘init.el’ file. + + (defadvice Info-follow-nearest-node (around gitman activate) + "When encountering a cross reference to the `gitman' info + manual, then instead of following that cross reference show + the actual manpage using the function `man'." + (let ((node (Info-get-token + (point) "\\*note[ \n\t]+" + "\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))) + (if (and node (string-match "^(gitman)\\(.+\\)" node)) + (progn (require 'man) + (man (match-string 1 node))) + ad-do-it))) + + Or if you are using MS Windows and ‘man’ is not available, use this +variation with used the Emacs Lisp implementation provided by the +‘woman’ package. + + (defadvice Info-follow-nearest-node (around gitman activate) + "When encountering a cross reference to the `gitman' info + manual, then instead of following that cross reference show + the actual manpage using the function `woman'." + (let ((node (Info-get-token + (point) "\\*note[ \n\t]+" + "\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))) + (if (and node (string-match "^(gitman)\\(.+\\)" node)) + (progn (require 'woman) + (woman (match-string 1 node))) + ad-do-it))) + + Did I mention that texinfo cross reference are just awful? (This is +just one of many issues.) + + +File: magit.info, Node: How can I show Git's output?, Next: Expanding a file to show the diff causes it to disappear, Prev: How to install the gitman info manual?, Up: FAQ + +A.11 How can I show Git’s output? +================================= + +To show the output of recently run git commands, press ‘$’ (or, if that +isn’t available, ‘M-x magit-process-buffer’). This will show a buffer +containing a section per git invocation; as always press ‘TAB’ to expand +or collapse them. + + By default git’s output is only inserted into the process buffer if +it is run for side-effects. When the output is consumed in some way +then also inserting it into the process buffer would be to expensive. +For debugging purposes it’s possible to do so anyway by setting +‘magit-git-debug’ to ‘t’. + + +File: magit.info, Node: Expanding a file to show the diff causes it to disappear, Next: Point is wrong in the COMMIT_EDITMSG buffer, Prev: How can I show Git's output?, Up: FAQ + +A.12 Expanding a file to show the diff causes it to disappear +============================================================= + +This is probably caused by a change of a ‘diff.*’ Git variable. You +probably set that variable for a reason, and should therefore only undo +that setting in Magit by customizing ‘magit-git-global-arguments’. + + +File: magit.info, Node: Point is wrong in the COMMIT_EDITMSG buffer, Next: Can Magit be used as ediff-version-control-package?, Prev: Expanding a file to show the diff causes it to disappear, Up: FAQ + +A.13 Point is wrong in the COMMIT_EDITMSG buffer +================================================ + +Neither Magit nor ‘git-commit‘ fiddle with point in the buffer used to +write commit messages, so something else must be doing it. + + You have probably globally enabled a mode which does restore point in +file-visiting buffers. It might be a bit surprising, but when you write +a commit message, then you are actually editing a file. + + So you have to figure out which package is doing. ‘saveplace’, +‘pointback’, and ‘session’ are likely candidates. These snippets might +help: + + (setq session-name-disable-regexp "\\(?:\\`'\\.git/[A-Z_]+\\'\\)") + + (with-eval-after-load 'pointback + (lambda () + (when (or git-commit-mode git-rebase-mode) + (pointback-mode -1)))) + + +File: magit.info, Node: Can Magit be used as ediff-version-control-package?, Next: How to show diffs for gpg-encrypted files?, Prev: Point is wrong in the COMMIT_EDITMSG buffer, Up: FAQ + +A.14 Can Magit be used as ediff-version-control-package? +======================================================== + +No, it cannot. For that to work the functions ‘ediff-magit-internal’ +and ‘ediff-magit-merge-internal’ would have to be implemented, and they +are not. These two functions are only used by the three commands +‘ediff-revision’, ‘ediff-merge-revisions-with-ancestor’, and +‘ediff-merge-revisions’. + + These commands only delegate the task of populating buffers with +certain revisions to the "internal" functions. The equally important +task of determining which revisions are to be compared/merged is not +delegated. Instead this is done without any support whatsoever, from +the version control package/system - meaning that the user has to enter +the revisions explicitly. Instead of implementing +‘ediff-magit-internal’ we provide ‘magit-ediff-compare’, which handles +both tasks like it is 2005. + + The other commands ‘ediff-merge-revisions’ and +‘ediff-merge-revisions-with-ancestor’ are normally not what you want +when using a modern version control system like Git. Instead of letting +the user resolve only those conflicts which Git could not resolve on its +own, they throw away all work done by Git and then expect the user to +manually merge all conflicts, including those that had already been +resolved. That made sense back in the days when version control systems +couldn’t merge (or so I have been told), but not anymore. Once in a +blue moon you might actually want to see all conflicts, in which case +you *can* use these commands, which then use ‘ediff-vc-merge-internal’. +So we don’t actually have to implement ‘ediff-magit-merge-internal’. +Instead we provide the more useful command ‘magit-ediff-resolve’ which +only shows yet-to-be resolved conflicts. + + +File: magit.info, Node: How to show diffs for gpg-encrypted files?, Next: Emacs 245 hangs when loading Magit, Prev: Can Magit be used as ediff-version-control-package?, Up: FAQ + +A.15 How to show diffs for gpg-encrypted files? +=============================================== + +Git supports showing diffs for encrypted files, but has to be told to do +so. Since Magit just uses Git to get the diffs, configuring Git also +affects the diffs displayed inside Magit. + + git config --global diff.gpg.textconv "gpg --no-tty --decrypt" + echo "*.gpg filter=gpg diff=gpg" > .gitattributes + + +File: magit.info, Node: Emacs 245 hangs when loading Magit, Next: Symbol's value as function is void --some, Prev: How to show diffs for gpg-encrypted files?, Up: FAQ + +A.16 Emacs 24.5 hangs when loading Magit +======================================== + +This is actually triggered by loading Tramp. See + for details. You +can work around the problem by setting +‘tramp-ssh-controlmaster-options’. Changing your DNS server (e.g. to +Google’s ‘8.8.8.8’) may also be sufficient to work around the issue. + + +File: magit.info, Node: Symbol's value as function is void --some, Next: Where is the branch manager, Prev: Emacs 245 hangs when loading Magit, Up: FAQ + +A.17 Symbol’s value as function is void –some +============================================= + +Update ‘dash’, restart Emacs, and then it will be defined. + + +File: magit.info, Node: Where is the branch manager, Prev: Symbol's value as function is void --some, Up: FAQ + +A.18 Where is the branch manager +================================ + +‘y’ is bound to the command that shows the "refs buffer", the successor +of the "branch manager". + + +File: magit.info, Node: Keystroke Index, Next: Command Index, Prev: FAQ, Up: Top + +Appendix B Keystroke Index +************************** + +[index] +* Menu: + +* !: Running Git manually. + (line 12) +* ! !: Running Git manually. + (line 19) +* ! a: Running Git manually. + (line 51) +* ! b: Running Git manually. + (line 55) +* ! g: Running Git manually. + (line 59) +* ! k: Running Git manually. + (line 47) +* ! p: Running Git manually. + (line 24) +* ! s: Running Git manually. + (line 33) +* ! S: Running Git manually. + (line 38) +* $: Viewing Git output. (line 11) +* +: Log Buffer. (line 53) +* + <1>: Refreshing diffs. (line 58) +* -: Refreshing diffs. (line 54) +* 0: Refreshing diffs. (line 62) +* 1: Section visibility. (line 26) +* 2: Section visibility. (line 26) +* 3: Section visibility. (line 26) +* 4: Section visibility. (line 26) +* =: Log Buffer. (line 47) +* = <1>: Log Buffer. (line 57) +* ^: Section movement. (line 31) +* a: Applying. (line 33) +* A: Cherry picking. (line 8) +* A A: Cherry picking. (line 16) +* A a: Cherry picking. (line 22) +* A A <1>: Cherry picking. (line 35) +* A a <1>: Cherry picking. (line 43) +* A s: Cherry picking. (line 39) +* B: Bisecting. (line 8) +* b: Branching. (line 45) +* B b: Bisecting. (line 31) +* b b: Branching. (line 182) +* b c: Branching. (line 200) +* b d: Branching. (line 241) +* B g: Bisecting. (line 36) +* B k: Bisecting. (line 41) +* b n: Branching. (line 190) +* B r: Bisecting. (line 47) +* b r: Branching. (line 247) +* B s: Bisecting. (line 16) +* b s: Branching. (line 207) +* B u: Bisecting. (line 24) +* b x: Branching. (line 224) +* c: Initiating a commit. (line 8) +* c <1>: Editing rebase sequences. + (line 72) +* c a: Initiating a commit. (line 18) +* c A: Initiating a commit. (line 66) +* c c: Initiating a commit. (line 13) +* c e: Initiating a commit. (line 22) +* c f: Initiating a commit. (line 42) +* c F: Initiating a commit. (line 50) +* c s: Initiating a commit. (line 54) +* c S: Initiating a commit. (line 62) +* c w: Initiating a commit. (line 32) +* C-: Diff buffer. (line 21) +* C-: Section visibility. (line 13) +* C-c C-a: Editing commit messages. + (line 128) +* C-c C-b: Log Buffer. (line 19) +* C-c C-b <1>: Refreshing diffs. (line 80) +* C-c C-c: Popup buffers and prefix commands. + (line 20) +* C-c C-c <1>: Select from log. (line 14) +* C-c C-c <2>: Editing commit messages. + (line 19) +* C-c C-c <3>: Editing rebase sequences. + (line 6) +* C-c C-d: Refreshing diffs. (line 70) +* C-c C-d <1>: Editing commit messages. + (line 57) +* C-c C-f: Log Buffer. (line 23) +* C-c C-f <1>: Refreshing diffs. (line 84) +* C-c C-i: Editing commit messages. + (line 153) +* C-c C-k: Select from log. (line 20) +* C-c C-k <1>: Editing commit messages. + (line 24) +* C-c C-k <2>: Editing rebase sequences. + (line 11) +* C-c C-o: Editing commit messages. + (line 144) +* C-c C-p: Editing commit messages. + (line 148) +* C-c C-r: Editing commit messages. + (line 132) +* C-c C-s: Editing commit messages. + (line 136) +* C-c C-t: Editing commit messages. + (line 140) +* C-c C-w: Editing commit messages. + (line 63) +* C-c M-g: Minor mode for buffers visiting files. + (line 19) +* C-c M-g b: Minor mode for buffers visiting files. + (line 39) +* C-c M-g c: Minor mode for buffers visiting files. + (line 49) +* C-c M-g l: Minor mode for buffers visiting files. + (line 33) +* C-c M-g p: Minor mode for buffers visiting files. + (line 45) +* C-c M-g s: Minor mode for buffers visiting files. + (line 24) +* C-c M-g u: Minor mode for buffers visiting files. + (line 28) +* C-s M-s: Editing commit messages. + (line 35) +* C-w: Common commands. (line 21) +* C-x g: Status buffer. (line 22) +* C-x u: Editing rebase sequences. + (line 89) +* d: Diffing. (line 20) +* D: Refreshing diffs. (line 11) +* d c: Diffing. (line 67) +* d d: Diffing. (line 25) +* D f: Refreshing diffs. (line 45) +* D g: Refreshing diffs. (line 16) +* d p: Diffing. (line 59) +* d r: Diffing. (line 29) +* D r: Refreshing diffs. (line 40) +* d s: Diffing. (line 49) +* D s: Refreshing diffs. (line 21) +* d t: Diffing. (line 72) +* D t: Refreshing diffs. (line 36) +* d u: Diffing. (line 55) +* d w: Diffing. (line 43) +* D w: Refreshing diffs. (line 28) +* DEL: Log Buffer. (line 37) +* DEL <1>: Diff buffer. (line 48) +* DEL <2>: Blaming. (line 44) +* DEL <3>: Editing rebase sequences. + (line 28) +* e: Ediffing. (line 6) +* E: Ediffing. (line 18) +* e <1>: Editing rebase sequences. + (line 55) +* E c: Ediffing. (line 62) +* E i: Ediffing. (line 54) +* E m: Ediffing. (line 32) +* E r: Ediffing. (line 23) +* E s: Ediffing. (line 45) +* E u: Ediffing. (line 50) +* E w: Ediffing. (line 58) +* f: Editing rebase sequences. + (line 63) +* f <1>: Fetching. (line 11) +* F: Pulling. (line 11) +* f a: Fetching. (line 28) +* f e: Fetching. (line 24) +* F e: Pulling. (line 24) +* f m: Fetching. (line 32) +* f p: Fetching. (line 16) +* F p: Pulling. (line 16) +* f u: Fetching. (line 20) +* F u: Pulling. (line 20) +* g: Automatic Refreshing of Magit Buffers. + (line 22) +* G: Automatic Refreshing of Magit Buffers. + (line 31) +* j: Diff buffer. (line 38) +* k: Viewing Git output. (line 19) +* k <1>: Applying. (line 37) +* k <2>: Editing rebase sequences. + (line 68) +* k <3>: Stashing. (line 92) +* l: Logging. (line 26) +* L: Refreshing logs. (line 11) +* L <1>: Log Buffer. (line 6) +* l a: Logging. (line 56) +* l b: Logging. (line 52) +* L g: Refreshing logs. (line 16) +* l h: Logging. (line 44) +* l H: Reflog. (line 19) +* l l: Logging. (line 31) +* l L: Logging. (line 48) +* l o: Logging. (line 37) +* l O: Reflog. (line 15) +* l r: Reflog. (line 11) +* L s: Refreshing logs. (line 21) +* L t: Refreshing logs. (line 36) +* L w: Refreshing logs. (line 28) +* m: Merging. (line 8) +* M: Remotes. (line 8) +* m a: Merging. (line 56) +* M a: Remotes. (line 13) +* m e: Merging. (line 29) +* M k: Remotes. (line 28) +* m m: Merging. (line 16) +* m m <1>: Merging. (line 51) +* m n: Merging. (line 36) +* m p: Merging. (line 43) +* M r: Remotes. (line 18) +* M u: Remotes. (line 23) +* M-1: Section visibility. (line 34) +* M-2: Section visibility. (line 34) +* M-3: Section visibility. (line 34) +* M-4: Section visibility. (line 34) +* M-: Section visibility. (line 17) +* M-n: Section movement. (line 26) +* M-n <1>: Editing commit messages. + (line 45) +* M-n <2>: Editing rebase sequences. + (line 47) +* M-p: Section movement. (line 20) +* M-p <1>: Editing commit messages. + (line 39) +* M-p <2>: Editing rebase sequences. + (line 43) +* M-w: Blaming. (line 75) +* M-w <1>: Common commands. (line 10) +* M-x magit-blame: Blaming. (line 8) +* M-x magit-blame-popup: Blaming. (line 24) +* M-x magit-checkout-file: Resetting. (line 37) +* M-x magit-clone: Repository setup. (line 16) +* M-x magit-describe-section: Section types and values. + (line 13) +* M-x magit-describe-section <1>: Matching sections. (line 6) +* M-x magit-find-file: Visiting blobs. (line 6) +* M-x magit-find-file-other-window: Visiting blobs. (line 11) +* M-x magit-init: Repository setup. (line 6) +* M-x magit-log-buffer-file: Logging. (line 67) +* M-x magit-reset-hard: Resetting. (line 32) +* M-x magit-reset-head: Resetting. (line 21) +* M-x magit-reset-index: Staging and unstaging. + (line 84) +* M-x magit-reset-index <1>: Resetting. (line 14) +* M-x magit-reset-soft: Resetting. (line 26) +* M-x magit-reverse-in-index: Staging and unstaging. + (line 59) +* M-x magit-stage-file: Staging from file-visiting buffers. + (line 10) +* M-x magit-toggle-buffer-lock: Modes and Buffers. (line 17) +* M-x magit-unstage-file: Staging from file-visiting buffers. + (line 18) +* M-x magit-version: Git executable. (line 17) +* M-x magit-wip-commit: Wip modes. (line 129) +* n: Section movement. (line 16) +* n <1>: Blaming. (line 54) +* N: Blaming. (line 58) +* n <2>: Editing rebase sequences. + (line 39) +* n <3>: Minor mode for buffers visiting blobs. + (line 16) +* o: Submodules. (line 8) +* o a: Submodules. (line 13) +* o b: Submodules. (line 19) +* o f: Submodules. (line 37) +* o i: Submodules. (line 24) +* o i <1>: Submodules. (line 41) +* o s: Submodules. (line 33) +* o u: Submodules. (line 28) +* p: Section movement. (line 10) +* p <1>: Blaming. (line 62) +* P: Blaming. (line 66) +* p <2>: Editing rebase sequences. + (line 35) +* P <1>: Pushing. (line 11) +* p <3>: Minor mode for buffers visiting blobs. + (line 12) +* P e: Pushing. (line 35) +* P m: Pushing. (line 44) +* P o: Pushing. (line 39) +* P p: Pushing. (line 16) +* P t: Pushing. (line 50) +* P T: Pushing. (line 56) +* P u: Pushing. (line 26) +* q: Quitting Windows. (line 6) +* q <1>: Log Buffer. (line 12) +* q <2>: Blaming. (line 70) +* q <3>: Minor mode for buffers visiting blobs. + (line 20) +* r: Rebasing. (line 8) +* r <1>: Editing rebase sequences. + (line 51) +* r a: Rebasing. (line 84) +* r e: Rebasing. (line 25) +* r e <1>: Rebasing. (line 80) +* r f: Rebasing. (line 57) +* r i: Rebasing. (line 53) +* r m: Rebasing. (line 61) +* r o: Rebasing. (line 31) +* r p: Rebasing. (line 16) +* r r: Rebasing. (line 72) +* r s: Rebasing. (line 76) +* r u: Rebasing. (line 21) +* r w: Rebasing. (line 65) +* RET: Diff buffer. (line 6) +* RET <1>: References buffer. (line 99) +* RET <2>: Blaming. (line 29) +* RET <3>: Editing rebase sequences. + (line 16) +* s: Staging and unstaging. + (line 29) +* S: Staging and unstaging. + (line 33) +* s <1>: Editing rebase sequences. + (line 59) +* s-: Section visibility. (line 22) +* SPC: Log Buffer. (line 27) +* SPC <1>: Diff buffer. (line 44) +* SPC <2>: Blaming. (line 34) +* SPC <3>: Editing rebase sequences. + (line 21) +* t: Blaming. (line 79) +* t <1>: Tagging. (line 8) +* T: Notes. (line 8) +* T a: Notes. (line 70) +* T c: Notes. (line 65) +* t k: Tagging. (line 18) +* T m: Notes. (line 56) +* t p: Tagging. (line 24) +* T p: Notes. (line 29) +* T r: Notes. (line 21) +* T s: Notes. (line 33) +* T S: Notes. (line 44) +* t t: Tagging. (line 13) +* T T: Notes. (line 13) +* TAB: Section visibility. (line 9) +* u: Staging and unstaging. + (line 40) +* U: Staging and unstaging. + (line 49) +* v: Applying. (line 41) +* V: Reverting. (line 6) +* V A: Reverting. (line 30) +* V a: Reverting. (line 38) +* V s: Reverting. (line 34) +* V V: Reverting. (line 14) +* V v: Reverting. (line 20) +* W: Creating and sending patches. + (line 6) +* w: Applying patches. (line 8) +* w a: Applying patches. (line 31) +* w m: Applying patches. (line 19) +* W p: Creating and sending patches. + (line 11) +* W r: Creating and sending patches. + (line 17) +* w s: Applying patches. (line 27) +* w w: Applying patches. (line 13) +* w w <1>: Applying patches. (line 23) +* x: Editing rebase sequences. + (line 76) +* x <1>: Resetting. (line 8) +* Y: Logging. (line 62) +* y: References buffer. (line 6) +* y <1>: Editing rebase sequences. + (line 85) +* y c: References buffer. (line 19) +* y o: References buffer. (line 24) +* y y: References buffer. (line 14) +* z: Stashing. (line 8) +* z a: Stashing. (line 58) +* z b: Stashing. (line 84) +* z d: Stashing. (line 71) +* z f: Stashing. (line 88) +* z i: Stashing. (line 20) +* z I: Stashing. (line 46) +* z l: Stashing. (line 76) +* z p: Stashing. (line 64) +* z v: Stashing. (line 80) +* z w: Stashing. (line 25) +* z W: Stashing. (line 51) +* z x: Stashing. (line 32) +* z z: Stashing. (line 13) +* z Z: Stashing. (line 39) + + +File: magit.info, Node: Command Index, Next: Function Index, Prev: Keystroke Index, Up: Top + +Appendix C Command Index +************************ + +[index] +* Menu: + +* auto-revert-mode: Automatic Reverting of File-Visiting Buffers. + (line 62) +* forward-line: Editing rebase sequences. + (line 39) +* git-commit-ack: Editing commit messages. + (line 128) +* git-commit-cc: Editing commit messages. + (line 144) +* git-commit-next-message: Editing commit messages. + (line 45) +* git-commit-prev-message: Editing commit messages. + (line 39) +* git-commit-reported: Editing commit messages. + (line 148) +* git-commit-review: Editing commit messages. + (line 132) +* git-commit-save-message: Editing commit messages. + (line 35) +* git-commit-signoff: Editing commit messages. + (line 136) +* git-commit-suggested: Editing commit messages. + (line 153) +* git-commit-test: Editing commit messages. + (line 140) +* git-rebase-backward-line: Editing rebase sequences. + (line 35) +* git-rebase-edit: Editing rebase sequences. + (line 55) +* git-rebase-exec: Editing rebase sequences. + (line 76) +* git-rebase-fixup: Editing rebase sequences. + (line 63) +* git-rebase-insert: Editing rebase sequences. + (line 85) +* git-rebase-kill-line: Editing rebase sequences. + (line 68) +* git-rebase-move-line-down: Editing rebase sequences. + (line 47) +* git-rebase-move-line-up: Editing rebase sequences. + (line 43) +* git-rebase-pick: Editing rebase sequences. + (line 72) +* git-rebase-reword: Editing rebase sequences. + (line 51) +* git-rebase-show-commit: Editing rebase sequences. + (line 16) +* git-rebase-squash: Editing rebase sequences. + (line 59) +* git-rebase-undo: Editing rebase sequences. + (line 89) +* ido-enter-magit-status: Status buffer. (line 42) +* magit-am-abort: Applying patches. (line 31) +* magit-am-apply-maildir: Applying patches. (line 19) +* magit-am-apply-patches: Applying patches. (line 13) +* magit-am-continue: Applying patches. (line 23) +* magit-am-popup: Applying patches. (line 8) +* magit-am-skip: Applying patches. (line 27) +* magit-apply: Applying. (line 33) +* magit-bisect-bad: Bisecting. (line 31) +* magit-bisect-good: Bisecting. (line 36) +* magit-bisect-popup: Bisecting. (line 8) +* magit-bisect-reset: Bisecting. (line 47) +* magit-bisect-run: Bisecting. (line 24) +* magit-bisect-skip: Bisecting. (line 41) +* magit-bisect-start: Bisecting. (line 16) +* magit-blame: Blaming. (line 8) +* magit-blame-copy-hash: Blaming. (line 75) +* magit-blame-next-chunk: Blaming. (line 54) +* magit-blame-next-chunk-same-commit: Blaming. (line 58) +* magit-blame-popup: Blaming. (line 24) +* magit-blame-popup <1>: Minor mode for buffers visiting files. + (line 39) +* magit-blame-previous-chunk: Blaming. (line 62) +* magit-blame-previous-chunk-same-commit: Blaming. (line 66) +* magit-blame-quit: Blaming. (line 70) +* magit-blame-toggle-headings: Blaming. (line 79) +* magit-blob-next: Minor mode for buffers visiting blobs. + (line 16) +* magit-blob-previous: Minor mode for buffers visiting files. + (line 45) +* magit-blob-previous <1>: Minor mode for buffers visiting blobs. + (line 12) +* magit-branch: Branching. (line 190) +* magit-branch-and-checkout: Branching. (line 200) +* magit-branch-delete: Branching. (line 241) +* magit-branch-popup: Branching. (line 45) +* magit-branch-rename: Branching. (line 247) +* magit-branch-reset: Branching. (line 224) +* magit-branch-spinoff: Branching. (line 207) +* magit-checkout: Branching. (line 182) +* magit-checkout-file: Resetting. (line 37) +* magit-cherry: Logging. (line 62) +* magit-cherry-apply: Cherry picking. (line 22) +* magit-cherry-pick: Cherry picking. (line 16) +* magit-cherry-pick-popup: Cherry picking. (line 8) +* magit-clone: Repository setup. (line 16) +* magit-commit: Initiating a commit. (line 13) +* magit-commit-amend: Initiating a commit. (line 18) +* magit-commit-augment: Initiating a commit. (line 66) +* magit-commit-extend: Initiating a commit. (line 22) +* magit-commit-fixup: Initiating a commit. (line 42) +* magit-commit-instant-fixup: Initiating a commit. (line 50) +* magit-commit-instant-squash: Initiating a commit. (line 62) +* magit-commit-popup: Initiating a commit. (line 8) +* magit-commit-popup <1>: Minor mode for buffers visiting files. + (line 49) +* magit-commit-reword: Initiating a commit. (line 32) +* magit-commit-squash: Initiating a commit. (line 54) +* magit-copy-buffer-revision: Common commands. (line 21) +* magit-copy-section-value: Common commands. (line 10) +* magit-describe-section: Section types and values. + (line 13) +* magit-describe-section <1>: Matching sections. (line 6) +* magit-diff: Diffing. (line 29) +* magit-diff-default-context: Refreshing diffs. (line 62) +* magit-diff-dwim: Diffing. (line 25) +* magit-diff-flip-revs: Refreshing diffs. (line 45) +* magit-diff-less-context: Refreshing diffs. (line 54) +* magit-diff-more-context: Refreshing diffs. (line 58) +* magit-diff-paths: Diffing. (line 59) +* magit-diff-popup: Diffing. (line 20) +* magit-diff-refresh: Refreshing diffs. (line 16) +* magit-diff-refresh-popup: Refreshing diffs. (line 11) +* magit-diff-save-default-arguments: Refreshing diffs. (line 28) +* magit-diff-set-default-arguments: Refreshing diffs. (line 21) +* magit-diff-show-or-scroll-down: Log Buffer. (line 37) +* magit-diff-show-or-scroll-down <1>: Blaming. (line 44) +* magit-diff-show-or-scroll-down <2>: Editing rebase sequences. + (line 28) +* magit-diff-show-or-scroll-up: Log Buffer. (line 27) +* magit-diff-show-or-scroll-up <1>: Blaming. (line 34) +* magit-diff-show-or-scroll-up <2>: Editing rebase sequences. + (line 21) +* magit-diff-staged: Diffing. (line 49) +* magit-diff-switch-range-type: Refreshing diffs. (line 40) +* magit-diff-toggle-refine-hunk: Refreshing diffs. (line 36) +* magit-diff-unstaged: Diffing. (line 55) +* magit-diff-visit-file: Diff buffer. (line 6) +* magit-diff-visit-file-worktree: Diff buffer. (line 21) +* magit-diff-while-committing: Refreshing diffs. (line 70) +* magit-diff-while-committing <1>: Editing commit messages. + (line 57) +* magit-diff-worktree: Diffing. (line 43) +* magit-discard: Applying. (line 37) +* magit-dispatch-popup: Popup buffers and prefix commands. + (line 20) +* magit-ediff-compare: Ediffing. (line 23) +* magit-ediff-dwim: Ediffing. (line 6) +* magit-ediff-popup: Ediffing. (line 18) +* magit-ediff-resolve: Ediffing. (line 32) +* magit-ediff-show-commit: Ediffing. (line 62) +* magit-ediff-show-staged: Ediffing. (line 54) +* magit-ediff-show-unstaged: Ediffing. (line 50) +* magit-ediff-show-working-tree: Ediffing. (line 58) +* magit-ediff-stage: Ediffing. (line 45) +* magit-fetch: Fetching. (line 24) +* magit-fetch-all: Fetching. (line 28) +* magit-fetch-from-pushremote: Fetching. (line 16) +* magit-fetch-from-upstream: Fetching. (line 20) +* magit-fetch-popup: Fetching. (line 11) +* magit-file-popup: Minor mode for buffers visiting files. + (line 19) +* magit-find-file: Visiting blobs. (line 6) +* magit-find-file-other-window: Visiting blobs. (line 11) +* magit-format-patch: Creating and sending patches. + (line 11) +* magit-git-command: Running Git manually. + (line 24) +* magit-git-command-topdir: Running Git manually. + (line 19) +* magit-go-backward: Log Buffer. (line 19) +* magit-go-backward <1>: Refreshing diffs. (line 80) +* magit-go-forward: Log Buffer. (line 23) +* magit-go-forward <1>: Refreshing diffs. (line 84) +* magit-init: Repository setup. (line 6) +* magit-jump-to-diffstat-or-diff: Diff buffer. (line 38) +* magit-kill-this-buffer: Minor mode for buffers visiting blobs. + (line 20) +* magit-log: Logging. (line 37) +* magit-log-all: Logging. (line 56) +* magit-log-all-branches: Logging. (line 52) +* magit-log-branches: Logging. (line 48) +* magit-log-buffer-file: Logging. (line 67) +* magit-log-buffer-file <1>: Minor mode for buffers visiting files. + (line 33) +* magit-log-bury-buffer: Log Buffer. (line 12) +* magit-log-current: Logging. (line 31) +* magit-log-double-commit-limit: Log Buffer. (line 53) +* magit-log-half-commit-limit: Log Buffer. (line 57) +* magit-log-head: Logging. (line 44) +* magit-log-popup: Logging. (line 26) +* magit-log-refresh: Refreshing logs. (line 16) +* magit-log-refresh-popup: Refreshing logs. (line 11) +* magit-log-refresh-popup <1>: Log Buffer. (line 6) +* magit-log-save-default-arguments: Refreshing logs. (line 28) +* magit-log-select-pick: Select from log. (line 14) +* magit-log-select-quit: Select from log. (line 20) +* magit-log-set-default-arguments: Refreshing logs. (line 21) +* magit-log-toggle-commit-limit: Log Buffer. (line 47) +* magit-merge: Merging. (line 16) +* magit-merge <1>: Merging. (line 51) +* magit-merge-abort: Merging. (line 56) +* magit-merge-editmsg: Merging. (line 29) +* magit-merge-nocommit: Merging. (line 36) +* magit-merge-popup: Merging. (line 8) +* magit-merge-preview: Merging. (line 43) +* magit-mode-bury-buffer: Quitting Windows. (line 6) +* magit-notes-edit: Notes. (line 13) +* magit-notes-merge: Notes. (line 56) +* magit-notes-merge-abort: Notes. (line 70) +* magit-notes-merge-commit: Notes. (line 65) +* magit-notes-popup: Notes. (line 8) +* magit-notes-prune: Notes. (line 29) +* magit-notes-remove: Notes. (line 21) +* magit-notes-set-display-refs: Notes. (line 44) +* magit-notes-set-ref: Notes. (line 33) +* magit-patch-popup: Creating and sending patches. + (line 6) +* magit-pop-revision-stack: Editing commit messages. + (line 63) +* magit-process: Viewing Git output. (line 11) +* magit-process-kill: Viewing Git output. (line 19) +* magit-pull: Pulling. (line 24) +* magit-pull-from-pushremote: Pulling. (line 16) +* magit-pull-from-upstream: Pulling. (line 20) +* magit-pull-popup: Pulling. (line 11) +* magit-push: Pushing. (line 39) +* magit-push-current: Pushing. (line 35) +* magit-push-current-to-pushremote: Pushing. (line 16) +* magit-push-current-to-upstream: Pushing. (line 26) +* magit-push-implicitly args: Pushing. (line 64) +* magit-push-matching: Pushing. (line 44) +* magit-push-popup: Pushing. (line 11) +* magit-push-tag: Pushing. (line 56) +* magit-push-tags: Pushing. (line 50) +* magit-push-to-remote remote args: Pushing. (line 75) +* magit-rebase: Rebasing. (line 25) +* magit-rebase-abort: Rebasing. (line 84) +* magit-rebase-autosquash: Rebasing. (line 57) +* magit-rebase-continue: Rebasing. (line 72) +* magit-rebase-edit: Rebasing. (line 80) +* magit-rebase-edit-commit: Rebasing. (line 61) +* magit-rebase-interactive: Rebasing. (line 53) +* magit-rebase-onto-pushremote: Rebasing. (line 16) +* magit-rebase-onto-upstream: Rebasing. (line 21) +* magit-rebase-popup: Rebasing. (line 8) +* magit-rebase-reword-commit: Rebasing. (line 65) +* magit-rebase-skip: Rebasing. (line 76) +* magit-rebase-subset: Rebasing. (line 31) +* magit-reflog-current: Reflog. (line 11) +* magit-reflog-head: Reflog. (line 19) +* magit-reflog-other: Reflog. (line 15) +* magit-refresh: Automatic Refreshing of Magit Buffers. + (line 22) +* magit-refresh-all: Automatic Refreshing of Magit Buffers. + (line 31) +* magit-remote-add: Remotes. (line 13) +* magit-remote-popup: Remotes. (line 8) +* magit-remote-remove: Remotes. (line 28) +* magit-remote-rename: Remotes. (line 18) +* magit-remote-set-url: Remotes. (line 23) +* magit-request-pull: Creating and sending patches. + (line 17) +* magit-reset: Resetting. (line 8) +* magit-reset-hard: Resetting. (line 32) +* magit-reset-head: Resetting. (line 21) +* magit-reset-index: Staging and unstaging. + (line 84) +* magit-reset-index <1>: Resetting. (line 14) +* magit-reset-soft: Resetting. (line 26) +* magit-reverse: Applying. (line 41) +* magit-reverse-in-index: Staging and unstaging. + (line 59) +* magit-revert: Reverting. (line 14) +* magit-revert-no-commit: Reverting. (line 20) +* magit-revert-popup: Reverting. (line 6) +* magit-run-git-gui: Running Git manually. + (line 59) +* magit-run-gitk: Running Git manually. + (line 47) +* magit-run-gitk-all: Running Git manually. + (line 51) +* magit-run-gitk-branches: Running Git manually. + (line 55) +* magit-run-popup: Running Git manually. + (line 12) +* magit-section-backward: Section movement. (line 10) +* magit-section-backward-siblings: Section movement. (line 20) +* magit-section-cycle: Section visibility. (line 13) +* magit-section-cycle-diffs: Section visibility. (line 17) +* magit-section-cycle-global: Section visibility. (line 22) +* magit-section-forward: Section movement. (line 16) +* magit-section-forward-siblings: Section movement. (line 26) +* magit-section-hide: Section visibility. (line 51) +* magit-section-hide-children: Section visibility. (line 66) +* magit-section-show: Section visibility. (line 47) +* magit-section-show-children: Section visibility. (line 60) +* magit-section-show-headings: Section visibility. (line 55) +* magit-section-show-level-1: Section visibility. (line 26) +* magit-section-show-level-1-all: Section visibility. (line 34) +* magit-section-show-level-2: Section visibility. (line 27) +* magit-section-show-level-2-all: Section visibility. (line 35) +* magit-section-show-level-3: Section visibility. (line 28) +* magit-section-show-level-3-all: Section visibility. (line 36) +* magit-section-show-level-4: Section visibility. (line 29) +* magit-section-show-level-4-all: Section visibility. (line 37) +* magit-section-toggle: Section visibility. (line 9) +* magit-section-toggle-children: Section visibility. (line 70) +* magit-section-up: Section movement. (line 31) +* magit-sequence-abort: Cherry picking. (line 43) +* magit-sequence-abort <1>: Reverting. (line 38) +* magit-sequence-continue: Cherry picking. (line 35) +* magit-sequence-continue <1>: Reverting. (line 30) +* magit-sequence-skip: Cherry picking. (line 39) +* magit-sequence-skip <1>: Reverting. (line 34) +* magit-shell-command: Running Git manually. + (line 38) +* magit-shell-command-topdir: Running Git manually. + (line 33) +* magit-show-commit: Diffing. (line 67) +* magit-show-commit <1>: Blaming. (line 29) +* magit-show-refs: References buffer. (line 24) +* magit-show-refs-current: References buffer. (line 19) +* magit-show-refs-head: References buffer. (line 14) +* magit-show-refs-popup: References buffer. (line 6) +* magit-snapshot: Stashing. (line 39) +* magit-snapshot-index: Stashing. (line 46) +* magit-snapshot-worktree: Stashing. (line 51) +* magit-stage: Staging and unstaging. + (line 29) +* magit-stage-file: Staging from file-visiting buffers. + (line 10) +* magit-stage-file <1>: Minor mode for buffers visiting files. + (line 24) +* magit-stage-modified: Staging and unstaging. + (line 33) +* magit-stash: Stashing. (line 13) +* magit-stash-apply: Stashing. (line 58) +* magit-stash-branch: Stashing. (line 84) +* magit-stash-clear: Stashing. (line 92) +* magit-stash-drop: Stashing. (line 71) +* magit-stash-format-patch: Stashing. (line 88) +* magit-stash-index: Stashing. (line 20) +* magit-stash-keep-index: Stashing. (line 32) +* magit-stash-list: Stashing. (line 76) +* magit-stash-pop: Stashing. (line 64) +* magit-stash-popup: Stashing. (line 8) +* magit-stash-show: Diffing. (line 72) +* magit-stash-show <1>: Stashing. (line 80) +* magit-stash-worktree: Stashing. (line 25) +* magit-status: Status buffer. (line 22) +* magit-submodule-add: Submodules. (line 13) +* magit-submodule-fetch: Fetching. (line 32) +* magit-submodule-fetch <1>: Submodules. (line 37) +* magit-submodule-init: Submodules. (line 24) +* magit-submodule-init <1>: Submodules. (line 41) +* magit-submodule-popup: Submodules. (line 8) +* magit-submodule-setup: Submodules. (line 19) +* magit-submodule-sync: Submodules. (line 33) +* magit-submodule-update: Submodules. (line 28) +* magit-tag: Tagging. (line 13) +* magit-tag-delete: Tagging. (line 18) +* magit-tag-popup: Tagging. (line 8) +* magit-tag-prune: Tagging. (line 24) +* magit-toggle-buffer-lock: Modes and Buffers. (line 17) +* magit-toggle-margin: Refreshing logs. (line 36) +* magit-unstage: Staging and unstaging. + (line 40) +* magit-unstage-all: Staging and unstaging. + (line 49) +* magit-unstage-file: Staging from file-visiting buffers. + (line 18) +* magit-unstage-file <1>: Minor mode for buffers visiting files. + (line 28) +* magit-version: Git executable. (line 17) +* magit-visit-ref: References buffer. (line 99) +* magit-wip-commit: Wip modes. (line 129) +* magit-wip-log: Wip modes. (line 69) +* magit-wip-log-current: Wip modes. (line 77) +* scroll-down: Diff buffer. (line 48) +* scroll-up: Diff buffer. (line 44) +* with-editor-cancel: Editing commit messages. + (line 24) +* with-editor-cancel <1>: Editing rebase sequences. + (line 11) +* with-editor-finish: Editing commit messages. + (line 19) +* with-editor-finish <1>: Editing rebase sequences. + (line 6) + diff --git a/elpa/magit-20160223.828/magit.info-2 b/elpa/magit-20160223.828/magit.info-2 new file mode 100644 index 0000000..bbd3ef1 Binary files /dev/null and b/elpa/magit-20160223.828/magit.info-2 differ diff --git a/elpa/magit-gerrit-20160128.1926/magit-gerrit-autoloads.el b/elpa/magit-gerrit-20160128.1926/magit-gerrit-autoloads.el new file mode 100644 index 0000000..833574f --- /dev/null +++ b/elpa/magit-gerrit-20160128.1926/magit-gerrit-autoloads.el @@ -0,0 +1,16 @@ +;;; magit-gerrit-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil nil ("magit-gerrit.el") (22221 60707 386755 +;;;;;; 352000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; magit-gerrit-autoloads.el ends here diff --git a/elpa/magit-gerrit-20160128.1926/magit-gerrit-pkg.el b/elpa/magit-gerrit-20160128.1926/magit-gerrit-pkg.el new file mode 100644 index 0000000..ae82368 --- /dev/null +++ b/elpa/magit-gerrit-20160128.1926/magit-gerrit-pkg.el @@ -0,0 +1 @@ +(define-package "magit-gerrit" "20160128.1926" "Magit plugin for Gerrit Code Review" '((magit "2.3.1")) :url "https://github.com/terranpro/magit-gerrit") diff --git a/elpa/magit-gerrit-20160128.1926/magit-gerrit.el b/elpa/magit-gerrit-20160128.1926/magit-gerrit.el new file mode 100644 index 0000000..2ec3ab1 --- /dev/null +++ b/elpa/magit-gerrit-20160128.1926/magit-gerrit.el @@ -0,0 +1,590 @@ +;;; magit-gerrit.el --- Magit plugin for Gerrit Code Review +;; +;; Copyright (C) 2013 Brian Fransioli +;; +;; Author: Brian Fransioli +;; URL: https://github.com/terranpro/magit-gerrit +;; Package-Version: 20160128.1926 +;; Package-Requires: ((magit "2.3.1")) +;; +;; This program 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 of the +;; License, or (at your option) any later version. + +;; This program 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 this program. If not, see http://www.gnu.org/licenses/. + +;;; Commentary: +;; +;; Magit plugin to make Gerrit code review easy-to-use from emacs and +;; without the need for a browser! +;; +;; Currently uses the [deprecated] gerrit ssh interface, which has +;; meant that obtaining the list of reviewers is not possible, only +;; the list of approvals (those who have already verified and/or code +;; reviewed). +;; +;;; To Use: +;; +;; (require 'magit-gerrit) +;; (setq-default magit-gerrit-ssh-creds "myid@gerrithost.org") +;; +;; +;; M-x `magit-status' +;; h R <= magit-gerrit uses the R prefix, see help +;; +;;; Workflow: +;; +;; 1) *check out branch => changes => (ma)git commit* +;; 2) R P <= [ger*R*it *P*ush for review] +;; 3) R A <= [ger*R*it *A*dd reviewer] (by email address) +;; 4) *wait for verification/code reviews* [approvals shown in status] +;; 5) R S <= [ger*R*it *S*ubmit review] +;; +;;; Other Comments: +;; `magit-gerrit-ssh-creds' is buffer local, so if you work with +;; multiple Gerrit's, you can make this a file or directory local +;; variable for one particular project. +;; +;; If your git remote for gerrit is not the default "origin", then +;; `magit-gerrit-remote' should be adjusted accordingly (e.g. "gerrit") +;; +;; Recommended to auto add reviewers via git hooks (precommit), rather +;; than manually performing 'R A' for every review. +;; +;; `magit-gerrit' will be enabled automatically on `magit-status' if +;; the git remote repo uses the same creds found in +;; `magit-gerrit-ssh-creds'. +;; +;; Ex: magit-gerrit-ssh-creds == br.fransioli@gerrit.org +;; $ cd ~/elisp; git remote -v => https://github.com/terranpro/magit-gerrit.git +;; ^~~ `magit-gerrit-mode' would *NOT* be enabled here +;; +;; $ cd ~/gerrit/prja; git remote -v => ssh://br.fransioli@gerrit.org/.../prja +;; ^~~ `magit-gerrit-mode' *WOULD* be enabled here +;; +;;; Code: + +(require 'magit) +(if (locate-library "magit-popup") + (require 'magit-popup)) +(require 'json) + +(eval-when-compile + (require 'cl-lib)) + +;; Define a defvar-local macro for Emacs < 24.3 +(unless (fboundp 'defvar-local) + (defmacro defvar-local (var val &optional docstring) + `(progn + (defvar ,var ,val ,docstring) + (make-variable-buffer-local ',var)))) + +(defvar-local magit-gerrit-ssh-creds nil + "Credentials used to execute gerrit commands via ssh of the form ID@Server") + +(defvar-local magit-gerrit-remote "origin" + "Default remote name to use for gerrit (e.g. \"origin\", \"gerrit\")") + +(defcustom magit-gerrit-popup-prefix (kbd "R") + "Key code to open magit-gerrit popup" + :group 'magit-gerrit + :type 'key-sequence) + +(defun gerrit-command (cmd &rest args) + (let ((gcmd (concat + "-x -p 29418 " + (or magit-gerrit-ssh-creds + (error "`magit-gerrit-ssh-creds' must be set!")) + " " + "gerrit " + cmd + " " + (mapconcat 'identity args " ")))) + ;; (message (format "Using cmd: %s" gcmd)) + gcmd)) + +(defun gerrit-query (prj &optional status) + (gerrit-command "query" + "--format=JSON" + "--all-approvals" + "--comments" + "--current-patch-set" + (concat "project:" prj) + (concat "status:" (or status "open")))) + +(defun gerrit-review ()) + +(defun gerrit-ssh-cmd (cmd &rest args) + (apply #'call-process + "ssh" nil nil nil + (split-string (apply #'gerrit-command cmd args)))) + +(defun gerrit-review-abandon (prj rev) + (gerrit-ssh-cmd "review" "--project" prj "--abandon" rev)) + +(defun gerrit-review-submit (prj rev &optional msg) + (gerrit-ssh-cmd "review" "--project" prj "--submit" + (if msg msg "") rev)) + +(defun gerrit-code-review (prj rev score &optional msg) + (gerrit-ssh-cmd "review" "--project" prj "--code-review" score + (if msg msg "") rev)) + +(defun gerrit-review-verify (prj rev score &optional msg) + (gerrit-ssh-cmd "review" "--project" prj "--verified" score + (if msg msg "") rev)) + +(defun magit-gerrit-get-remote-url () + (magit-git-string "ls-remote" "--get-url" magit-gerrit-remote)) + +(defun magit-gerrit-get-project () + (let* ((regx (rx (zero-or-one ?:) (zero-or-more (any digit)) ?/ + (group (not (any "/"))) + (group (one-or-more (not (any ".")))))) + (str (or (magit-gerrit-get-remote-url) "")) + (sstr (car (last (split-string str "//"))))) + (when (string-match regx sstr) + (concat (match-string 1 sstr) + (match-string 2 sstr))))) + +(defun magit-gerrit-string-trunc (str maxlen) + (if (> (length str) maxlen) + (concat (substring str 0 maxlen) + "...") + str)) + +(defun magit-gerrit-create-branch-force (branch parent) + "Switch 'HEAD' to new BRANCH at revision PARENT and update working tree. +Fails if working tree or staging area contain uncommitted changes. +Succeed even if branch already exist +\('git checkout -B BRANCH REVISION')." + (cond ((run-hook-with-args-until-success + 'magit-create-branch-hook branch parent)) + ((and branch (not (string= branch ""))) + (magit-save-repository-buffers) + (magit-run-git "checkout" "-B" branch parent)))) + + +(defun magit-gerrit-pretty-print-reviewer (name email crdone vrdone) + (let* ((wid (1- (window-width))) + (crstr (propertize (if crdone (format "%+2d" (string-to-number crdone)) " ") + 'face '(magit-diff-lines-heading + bold))) + (vrstr (propertize (if vrdone (format "%+2d" (string-to-number vrdone)) " ") + 'face '(magit-diff-added-highlight + bold))) + (namestr (propertize (or name "") 'face 'magit-refname)) + (emailstr (propertize (if email (concat "(" email ")") "") + 'face 'change-log-name))) + (format "%-12s%s %s" (concat crstr " " vrstr) namestr emailstr))) + +(defun magit-gerrit-pretty-print-review (num subj owner-name &optional draft) + ;; window-width - two prevents long line arrow from being shown + (let* ((wid (- (window-width) 2)) + (numstr (propertize (format "%-10s" num) 'face 'magit-hash)) + (nlen (length numstr)) + (authmaxlen (/ wid 4)) + + (author (propertize (magit-gerrit-string-trunc owner-name authmaxlen) + 'face 'magit-log-author)) + + (subjmaxlen (- wid (length author) nlen 6)) + + (subjstr (propertize (magit-gerrit-string-trunc subj subjmaxlen) + 'face + (if draft + 'magit-signature-bad + 'magit-signature-good))) + (authsubjpadding (make-string + (max 0 (- wid (+ nlen 1 (length author) (length subjstr)))) + ? ))) + (format "%s%s%s%s\n" + numstr subjstr authsubjpadding author))) + +(defun magit-gerrit-wash-approval (approval) + (let* ((approver (cdr-safe (assoc 'by approval))) + (approvname (cdr-safe (assoc 'name approver))) + (approvemail (cdr-safe (assoc 'email approver))) + (type (cdr-safe (assoc 'type approval))) + (verified (string= type "Verified")) + (codereview (string= type "Code-Review")) + (score (cdr-safe (assoc 'value approval)))) + + (magit-insert-section (section approval) + (insert (magit-gerrit-pretty-print-reviewer approvname approvemail + (and codereview score) + (and verified score)) + "\n")))) + +(defun magit-gerrit-wash-approvals (approvals) + (mapc #'magit-gerrit-wash-approval approvals)) + +(defun magit-gerrit-wash-review () + (let* ((beg (point)) + (jobj (json-read)) + (end (point)) + (num (cdr-safe (assoc 'number jobj))) + (subj (cdr-safe (assoc 'subject jobj))) + (owner (cdr-safe (assoc 'owner jobj))) + (owner-name (cdr-safe (assoc 'name owner))) + (owner-email (cdr-safe (assoc 'email owner))) + (patchsets (cdr-safe (assoc 'currentPatchSet jobj))) + ;; compare w/t since when false the value is => :json-false + (isdraft (eq (cdr-safe (assoc 'isDraft patchsets)) t)) + (approvs (cdr-safe (if (listp patchsets) + (assoc 'approvals patchsets) + (assoc 'approvals (aref patchsets 0)))))) + (if (and beg end) + (delete-region beg end)) + (when (and num subj owner-name) + (magit-insert-section (section subj) + (insert (propertize + (magit-gerrit-pretty-print-review num subj owner-name isdraft) + 'magit-gerrit-jobj + jobj)) + (unless (magit-section-hidden (magit-current-section)) + (magit-gerrit-wash-approvals approvs)) + (add-text-properties beg (point) (list 'magit-gerrit-jobj jobj))) + t))) + +(defun magit-gerrit-wash-reviews (&rest args) + (magit-wash-sequence #'magit-gerrit-wash-review)) + +(defun magit-gerrit-section (section title washer &rest args) + (let ((magit-git-executable "ssh") + (magit-git-global-arguments nil)) + (magit-insert-section (section title) + (magit-insert-heading title) + (magit-git-wash washer (split-string (car args))) + (insert "\n")))) + +(defun magit-gerrit-remote-update (&optional remote) + nil) + +(defun magit-gerrit-review-at-point () + (get-text-property (point) 'magit-gerrit-jobj)) + +(defun magit-gerrit-view-patchset-diff () + "View the Diff for a Patchset" + (interactive) + (let ((jobj (magit-gerrit-review-at-point))) + (when jobj + (let ((ref (cdr (assoc 'ref (assoc 'currentPatchSet jobj)))) + (dir default-directory)) + (let* ((magit-proc (magit-fetch magit-gerrit-remote ref))) + (message (format "Waiting a git fetch from %s to complete..." + magit-gerrit-remote)) + (magit-process-wait)) + (message (format "Generating Gerrit Patchset for refs %s dir %s" ref dir)) + (magit-diff "FETCH_HEAD~1..FETCH_HEAD"))))) + +(defun magit-gerrit-download-patchset () + "Download a Gerrit Review Patchset" + (interactive) + (let ((jobj (magit-gerrit-review-at-point))) + (when jobj + (let ((ref (cdr (assoc 'ref (assoc 'currentPatchSet jobj)))) + (dir default-directory) + (branch (format "review/%s/%s" + (cdr (assoc 'username (assoc 'owner jobj))) + (cdr (or (assoc 'topic jobj) (assoc 'number jobj)))))) + (let* ((magit-proc (magit-fetch magit-gerrit-remote ref))) + (message (format "Waiting a git fetch from %s to complete..." + magit-gerrit-remote)) + (magit-process-wait)) + (message (format "Checking out refs %s to %s in %s" ref branch dir)) + (magit-gerrit-create-branch-force branch "FETCH_HEAD"))))) + +(defun magit-gerrit-browse-review () + "Browse the Gerrit Review with a browser." + (interactive) + (let ((jobj (magit-gerrit-review-at-point))) + (if jobj + (browse-url (cdr (assoc 'url jobj)))))) + +(defun magit-gerrit-copy-review (with-commit-message) + "Copy review url and commit message." + (let ((jobj (magit-gerrit-review-at-point))) + (if jobj + (with-temp-buffer + (insert + (concat (cdr (assoc 'url jobj)) + (if with-commit-message + (concat " " (car (split-string (cdr (assoc 'commitMessage jobj)) "\n" t)))))) + (clipboard-kill-region (point-min) (point-max)))))) + +(defun magit-gerrit-copy-review-url () + "Copy review url only" + (interactive) + (magit-gerrit-copy-review nil)) + +(defun magit-gerrit-copy-review-url-commit-message () + "Copy review url with commit message" + (interactive) + (magit-gerrit-copy-review t)) + +(defun magit-insert-gerrit-reviews () + (magit-gerrit-section 'gerrit-reviews + "Reviews:" 'magit-gerrit-wash-reviews + (gerrit-query (magit-gerrit-get-project)))) + +(defun magit-gerrit-add-reviewer () + (interactive) + "ssh -x -p 29418 user@gerrit gerrit set-reviewers --project toplvlroot/prjname --add email@addr" + + (gerrit-ssh-cmd "set-reviewers" + "--project" (magit-gerrit-get-project) + "--add" (read-string "Reviewer Name/Email: ") + (cdr-safe (assoc 'id (magit-gerrit-review-at-point))))) + +(defun magit-gerrit-popup-args (&optional something) + (or (magit-gerrit-arguments) (list ""))) + +(defun magit-gerrit-verify-review (args) + "Verify a Gerrit Review" + (interactive (magit-gerrit-popup-args)) + + (let ((score (completing-read "Score: " + '("-2" "-1" "0" "+1" "+2") + nil t + "+1")) + (rev (cdr-safe (assoc + 'revision + (cdr-safe (assoc 'currentPatchSet + (magit-gerrit-review-at-point)))))) + (prj (magit-gerrit-get-project))) + (gerrit-review-verify prj rev score args) + (magit-refresh))) + +(defun magit-gerrit-code-review (args) + "Perform a Gerrit Code Review" + (interactive (magit-gerrit-popup-args)) + (let ((score (completing-read "Score: " + '("-2" "-1" "0" "+1" "+2") + nil t + "+1")) + (rev (cdr-safe (assoc + 'revision + (cdr-safe (assoc 'currentPatchSet + (magit-gerrit-review-at-point)))))) + (prj (magit-gerrit-get-project))) + (gerrit-code-review prj rev score args) + (magit-refresh))) + +(defun magit-gerrit-submit-review (args) + "Submit a Gerrit Code Review" + ;; "ssh -x -p 29418 user@gerrit gerrit review REVISION -- --project PRJ --submit " + (interactive (magit-gerrit-popup-args)) + (gerrit-ssh-cmd "review" + (cdr-safe (assoc + 'revision + (cdr-safe (assoc 'currentPatchSet + (magit-gerrit-review-at-point))))) + "--project" + (magit-gerrit-get-project) + "--submit" + args) + (magit-fetch-from-upstream "")) + +(defun magit-gerrit-push-review (status) + (let* ((branch (or (magit-get-current-branch) + (error "Don't push a detached head. That's gross"))) + (commitid (or (when (eq (magit-section-type (magit-current-section)) + 'commit) + (magit-section-value (magit-current-section))) + (error "Couldn't find a commit at point"))) + (rev (magit-rev-parse (or commitid + (error "Select a commit for review")))) + + (branch-remote (and branch (magit-get "branch" branch "remote")))) + + ;; (message "Args: %s " + ;; (concat rev ":" branch-pub)) + + (let* ((branch-merge (if (or (null branch-remote) + (string= branch-remote ".")) + (completing-read + "Remote Branch: " + (let ((rbs (magit-list-remote-branch-names))) + (mapcar + #'(lambda (rb) + (and (string-match (rx bos + (one-or-more (not (any "/"))) + "/" + (group (one-or-more any)) + eos) + rb) + (concat "refs/heads/" (match-string 1 rb)))) + rbs))) + (and branch (magit-get "branch" branch "merge")))) + (branch-pub (progn + (string-match (rx "refs/heads" (group (one-or-more any))) + branch-merge) + (format "refs/%s%s/%s" status (match-string 1 branch-merge) branch)))) + + + (when (or (null branch-remote) + (string= branch-remote ".")) + (setq branch-remote magit-gerrit-remote)) + + (magit-run-git-async "push" "-v" branch-remote + (concat rev ":" branch-pub))))) + +(defun magit-gerrit-create-review () + (interactive) + (magit-gerrit-push-review 'publish)) + +(defun magit-gerrit-create-draft () + (interactive) + (magit-gerrit-push-review 'drafts)) + +(defun magit-gerrit-publish-draft () + (interactive) + (let ((prj (magit-gerrit-get-project)) + (id (cdr-safe (assoc 'id + (magit-gerrit-review-at-point)))) + (rev (cdr-safe (assoc + 'revision + (cdr-safe (assoc 'currentPatchSet + (magit-gerrit-review-at-point))))))) + (gerrit-ssh-cmd "review" "--project" prj "--publish" rev)) + (magit-refresh)) + +(defun magit-gerrit-delete-draft () + (interactive) + (let ((prj (magit-gerrit-get-project)) + (id (cdr-safe (assoc 'id + (magit-gerrit-review-at-point)))) + (rev (cdr-safe (assoc + 'revision + (cdr-safe (assoc 'currentPatchSet + (magit-gerrit-review-at-point))))))) + (gerrit-ssh-cmd "review" "--project" prj "--delete" rev)) + (magit-refresh)) + +(defun magit-gerrit-abandon-review () + (interactive) + (let ((prj (magit-gerrit-get-project)) + (id (cdr-safe (assoc 'id + (magit-gerrit-review-at-point)))) + (rev (cdr-safe (assoc + 'revision + (cdr-safe (assoc 'currentPatchSet + (magit-gerrit-review-at-point))))))) + ;; (message "Prj: %s Rev: %s Id: %s" prj rev id) + (gerrit-review-abandon prj rev) + (magit-refresh))) + +(defun magit-gerrit-read-comment (&rest args) + (format "\'\"%s\"\'" + (read-from-minibuffer "Message: "))) + +(defun magit-gerrit-create-branch (branch parent)) + +(magit-define-popup magit-gerrit-popup + "Popup console for magit gerrit commands." + 'magit-gerrit + :actions '((?P "Push Commit For Review" magit-gerrit-create-review) + (?W "Push Commit For Draft Review" magit-gerrit-create-draft) + (?p "Publish Draft Patchset" magit-gerrit-publish-draft) + (?k "Delete Draft" magit-gerrit-delete-draft) + (?A "Add Reviewer" magit-gerrit-add-reviewer) + (?V "Verify" magit-gerrit-verify-review) + (?C "Code Review" magit-gerrit-code-review) + (?d "View Patchset Diff" magit-gerrit-view-patchset-diff) + (?D "Download Patchset" magit-gerrit-download-patchset) + (?S "Submit Review" magit-gerrit-submit-review) + (?B "Abandon Review" magit-gerrit-abandon-review) + (?b "Browse Review" magit-gerrit-browse-review)) + :options '((?m "Comment" "--message " magit-gerrit-read-comment))) + +;; Attach Magit Gerrit to Magit's default help popup +(magit-define-popup-action 'magit-dispatch-popup ?R "Gerrit" + 'magit-gerrit-popup) + +(magit-define-popup magit-gerrit-copy-review-popup + "Popup console for copy review to clipboard." + 'magit-gerrit + :actions '((?C "url and commit message" magit-gerrit-copy-review-url-commit-message) + (?c "url only" magit-gerrit-copy-review-url))) + +(magit-define-popup-action 'magit-gerrit-popup ?c "Copy Review" + 'magit-gerrit-copy-review-popup) + +(defvar magit-gerrit-mode-map + (let ((map (make-sparse-keymap))) + (define-key map magit-gerrit-popup-prefix 'magit-gerrit-popup) + map)) + +(define-minor-mode magit-gerrit-mode "Gerrit support for Magit" + :lighter " Gerrit" :require 'magit-topgit :keymap 'magit-gerrit-mode-map + (or (derived-mode-p 'magit-mode) + (error "This mode only makes sense with magit")) + (or magit-gerrit-ssh-creds + (error "You *must* set `magit-gerrit-ssh-creds' to enable magit-gerrit-mode")) + (or (magit-gerrit-get-remote-url) + (error "You *must* set `magit-gerrit-remote' to a valid Gerrit remote")) + (cond + (magit-gerrit-mode + (magit-add-section-hook 'magit-status-sections-hook + 'magit-insert-gerrit-reviews + 'magit-insert-stashes t t) + (add-hook 'magit-create-branch-command-hook + 'magit-gerrit-create-branch nil t) + ;(add-hook 'magit-pull-command-hook 'magit-gerrit-pull nil t) + (add-hook 'magit-remote-update-command-hook + 'magit-gerrit-remote-update nil t) + (add-hook 'magit-push-command-hook + 'magit-gerrit-push nil t)) + + (t + (remove-hook 'magit-after-insert-stashes-hook + 'magit-insert-gerrit-reviews t) + (remove-hook 'magit-create-branch-command-hook + 'magit-gerrit-create-branch t) + ;(remove-hook 'magit-pull-command-hook 'magit-gerrit-pull t) + (remove-hook 'magit-remote-update-command-hook + 'magit-gerrit-remote-update t) + (remove-hook 'magit-push-command-hook + 'magit-gerrit-push t))) + (when (called-interactively-p 'any) + (magit-refresh))) + +(defun magit-gerrit-detect-ssh-creds (remote-url) + "Derive magit-gerrit-ssh-creds from remote-url. +Assumes remote-url is a gerrit repo if scheme is ssh +and port is the default gerrit ssh port." + (let ((url (url-generic-parse-url remote-url))) + (when (and (string= "ssh" (url-type url)) + (eq 29418 (url-port url))) + (set (make-local-variable 'magit-gerrit-ssh-creds) + (format "%s@%s" (url-user url) (url-host url))) + (message "Detected magit-gerrit-ssh-creds=%s" magit-gerrit-ssh-creds)))) + +(defun magit-gerrit-check-enable () + (let ((remote-url (magit-gerrit-get-remote-url))) + (when (and remote-url + (or magit-gerrit-ssh-creds + (magit-gerrit-detect-ssh-creds remote-url)) + (string-match magit-gerrit-ssh-creds remote-url)) + ;; update keymap with prefix incase it has changed + (define-key magit-gerrit-mode-map magit-gerrit-popup-prefix 'magit-gerrit-popup) + (magit-gerrit-mode t)))) + +;; Hack in dir-local variables that might be set for magit gerrit +(add-hook 'magit-status-mode-hook #'hack-dir-local-variables-non-file-buffer t) + +;; Try to auto enable magit-gerrit in the magit-status buffer +(add-hook 'magit-status-mode-hook #'magit-gerrit-check-enable t) +(add-hook 'magit-log-mode-hook #'magit-gerrit-check-enable t) + +(provide 'magit-gerrit) + +;;; magit-gerrit.el ends here diff --git a/elpa/magit-gh-pulls-20160222.1802/magit-gh-pulls-autoloads.el b/elpa/magit-gh-pulls-20160222.1802/magit-gh-pulls-autoloads.el new file mode 100644 index 0000000..1f9a731 --- /dev/null +++ b/elpa/magit-gh-pulls-20160222.1802/magit-gh-pulls-autoloads.el @@ -0,0 +1,27 @@ +;;; magit-gh-pulls-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil "magit-gh-pulls" "magit-gh-pulls.el" (22221 +;;;;;; 60704 390000 0)) +;;; Generated autoloads from magit-gh-pulls.el + +(autoload 'magit-gh-pulls-mode "magit-gh-pulls" "\ +Pull requests support for Magit + +\(fn &optional ARG)" t nil) + +(autoload 'turn-on-magit-gh-pulls "magit-gh-pulls" "\ +Unconditionally turn on `magit-pulls-mode'. + +\(fn)" nil nil) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; magit-gh-pulls-autoloads.el ends here diff --git a/elpa/magit-gh-pulls-20160222.1802/magit-gh-pulls-pkg.el b/elpa/magit-gh-pulls-20160222.1802/magit-gh-pulls-pkg.el new file mode 100644 index 0000000..561b26b --- /dev/null +++ b/elpa/magit-gh-pulls-20160222.1802/magit-gh-pulls-pkg.el @@ -0,0 +1 @@ +(define-package "magit-gh-pulls" "20160222.1802" "GitHub pull requests extension for Magit" '((emacs "24") (gh "0.9.1") (magit "2.1.0") (pcache "0.2.3") (s "1.6.1")) :url "https://github.com/sigma/magit-gh-pulls" :keywords '("git" "tools")) diff --git a/elpa/magit-gh-pulls-20160222.1802/magit-gh-pulls.el b/elpa/magit-gh-pulls-20160222.1802/magit-gh-pulls.el new file mode 100644 index 0000000..94e91ef --- /dev/null +++ b/elpa/magit-gh-pulls-20160222.1802/magit-gh-pulls.el @@ -0,0 +1,596 @@ +;;; magit-gh-pulls.el --- GitHub pull requests extension for Magit + +;; Copyright (C) 2011-2015 Yann Hodique, Alexander Yakushev + +;; Author: Yann Hodique +;; Keywords: git tools +;; Package-Version: 20160222.1802 +;; Version: 0.5.2 +;; URL: https://github.com/sigma/magit-gh-pulls +;; Package-Requires: ((emacs "24") (gh "0.9.1") (magit "2.1.0") (pcache "0.2.3") (s "1.6.1")) + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; This is a Magit extension for manipulating GitHub pull requests + +;; No configuration is needed in the repository if any of your remotes contain a +;; URL to Github's remote repository. If for some reason you don't have any +;; Github remotes in your config, you can specify username and repository +;; explicitly: + +;; $ git config magit.gh-pulls-repo / # your github repository + +;; Add these lines to your init.el: + +;; (require 'magit-gh-pulls) +;; (add-hook 'magit-mode-hook 'turn-on-magit-gh-pulls) + +;; These are the bindings for pull requests, defined in magit-gh-pulls-mode-map: +;; # g --- refreshes the list of pull requests +;; # f --- fetches the commits associated with the pull request at point +;; # b --- helps you creating a topic branch from a review request +;; # m --- merges the PR on top of the current branch +;; # c --- creates a PR from the current branch +;; # o --- opens a pull request on GitHub in your default browser + +;; Then, you can do whatever you want with the commit objects associated with +;; the pull request (merge, cherry-pick, diff, ...) + +;; When you create a new pull request, you can enable -w option to automatically +;; open it on GitHub in your default browser. + +;;; Code: + +(require 'eieio) + +(require 'magit) +(require 'git-commit) +(require 'gh) +(require 'gh-pulls) +(require 'pcache) +(require 's) + +(defgroup magit-gh-pulls nil + "Github.com pull-requests for Magit." + :group 'magit-extensions) + +(defcustom magit-gh-pulls-open-new-pr-in-browser nil + "DEPRECATED: use magit switch instead." + :group 'magit-gh-pulls + :type 'boolean) + +(defvar magit-gh-pulls-maybe-filter-pulls 'identity + "Filter function which should validate pulls you want to be + viewed in magit. It receives a list of pull requests and should + return a list of pull requests.") + +(defvar magit-gh-pulls-collapse-commits t + "Collapse commits in pull requests listing.") + +(defvar magit-gh-pulls-pull-detail-limit 10 + "Pull in additional information for each pull request in the + status buffer only if the total number of open PRs is <= + this number. Additional information includes individual + commits in each PR and highlighting based on the merge + status of the PR. Increasing this number may adversely + affect performance on repos with many PRs.") + +(defvar-local magit-gh-pulls-previous-winconf nil) + +(defvar magit-gh-pulls-editor-mode-map + (let ((map (make-keymap))) + (define-key map (kbd "C-c C-c") 'magit-gh-pulls-pull-editor-finish) + (define-key map (kbd "C-c C-k") 'magit-gh-pulls-pull-editor-quit) + map)) + +(define-derived-mode magit-gh-pulls-editor-mode text-mode "Magit GitHub Pulls Editor" + (font-lock-add-keywords nil (git-commit-mode-font-lock-keywords) t)) + +(easy-menu-define magit-gh-pulls-editor-mode-menu magit-gh-pulls-editor-mode-map + "Magit GitHub Pulls Editor Menu" + '("Magit GitHub Pulls" + ["Submit Pull Request" magit-gh-pulls-pull-editor-finish t] + ["Cancel" magit-gh-pulls-pull-editor-quit t])) + +(defun magit-gh-pulls-get-api () + (gh-pulls-api "api" :sync t :num-retries 1 :cache (gh-cache "cache"))) + +(defun magit-gh-pulls-get-repo-from-config () + "Return (user . project) pair read from magit.gh-pulls-repo +config option." + (let* ((cfg (magit-get "magit" "gh-pulls-repo"))) + (when cfg + (let* ((split (split-string cfg "/"))) + (cons (car split) (cadr split)))))) + + +;;Find all the Hostname Lines until we hit the end of config-lines or the +;;next Host line. Return '(remaining-config-lines list-of-hostnames) +(defun magit-gh-pulls-collect-hostnames (config-lines) + (let ((cur-line (car config-lines)) + (rest config-lines) + (result '())) + (while (and cur-line (not (string= (cadr cur-line) "Host"))) + (setq result (cons (cadr (cdr cur-line)) result)) + (setq rest (cdr rest)) + (setq cur-line (car rest))) + (list rest result))) + + +(defun magit-gh-pulls-get-host-hostnames (config-lines) + (let (result-alist + (curline (car config-lines)) + (rest-lines (cdr config-lines))) + (while rest-lines + (if (string= (cadr curline) "Host") + (let ((hosts (s-split "\\s*" (cadr (cdr curline)))) ;;List of the host aliases + (rest-result (magit-gh-pulls-collect-hostnames rest-lines))) + (dolist (host hosts) + ;;Host must be lowercase because the url parser lowercases the string + (setq result-alist (cons (cons (downcase host) (cadr rest-result)) result-alist))) + (setq curline (caar rest-result)) + (setq rest-lines (cdar rest-result))) + (progn + (setq curline (car rest-lines)) + (setq rest-lines (cdr rest-lines))))) + result-alist)) + +(defun -magit-gh-pulls-filter-and-split-host-lines (lines) + (delq nil + (mapcar (lambda (line) + (s-match "^[ \t]*\\(Host\\|HostName\\|Hostname\\)[ \t]+\\(.+\\)$" line)) + lines))) + + +;; Port of github/hub's SSHConfig +(defun magit-gh-pulls-get-ssh-config-hosts () + (let* ((file-lines (mapcar (lambda (path) + (if (file-exists-p path) + (with-temp-buffer + (insert-file-contents path) + (split-string (buffer-string) "\n" t)) + '())) + (list + (concat (file-name-as-directory (getenv "HOME")) ".ssh/config") + "/etc/ssh_config" + "/etc/ssh/ssh_config"))) + (all-lines (apply #'append file-lines)) + (matched-lines (-magit-gh-pulls-filter-and-split-host-lines all-lines))) + (magit-gh-pulls-get-host-hostnames matched-lines))) + + +;; Port of github/hub's ParseURL, with modifications to align with existing parse-url +(defun magit-gh-pulls-parse-url (url ssh-config-hosts) + (let* ((fixed-url (if (and (not (s-matches? "^[a-zA-Z_-]+://" url)) + (s-matches? ":" url) + (not (s-matches? "\\\\\\\\" url))) ;;Two literal backlashes + (concat "ssh://" (s-replace ":" "/" url)) + url)) + (parsed-url (url-generic-parse-url fixed-url)) + (ssh-host (when (string= (url-type parsed-url) "ssh") + (assoc (url-host parsed-url) ssh-config-hosts)))) + (when (and ssh-host (cadr ssh-host)) + (setf (url-host parsed-url) (cadr ssh-host))) + (when (and + (string= (url-host parsed-url) "github.com") + (s-matches? "\\(git\\|ssh\\|https?\\)" (url-type parsed-url))) + (let ((creds (s-match "/\\(.+\\)/\\([^/]+\\)/?$" (url-filename parsed-url)))) + (when creds + (cons (cadr creds) (s-chop-suffix ".git" (cadr (cdr creds))))))))) + + +(defun magit-gh-pulls-guess-repo-from-origin () + "Return (user . project) pair inferred from remotes in +.git/config." + (let ((creds nil) + (ssh-config-hosts (magit-gh-pulls-get-ssh-config-hosts))) + (dolist (remote (magit-git-lines "remote") creds) + (let ((parsed (magit-gh-pulls-parse-url + (magit-get "remote" remote "url") + ssh-config-hosts))) + (when parsed + (setq creds parsed)))))) + +(defun magit-gh-pulls-guess-repo () + "Return (user . project) pair obtained either from explicit +option, or inferred from remotes." + (or (magit-gh-pulls-get-repo-from-config) + (magit-gh-pulls-guess-repo-from-origin))) + +(defun magit-gh-pulls-requests-cached-p (api user proj) + "Returns T if the API request to the given USER and PROJ is cached." + (let ((cache-repo (format "/repos/%s/%s/pulls" user proj)) + (cached? nil)) + (pcache-map (oref api :cache) + (lambda (key _) (when (equal (car key) cache-repo) + (setq cached? t)))) + cached?)) + +(defun magit-gh-pulls-insert-gh-pulls () + (condition-case-unless-debug print-section + (progn + (let* ((repo (magit-gh-pulls-guess-repo))) + (when repo + (let* ((api (magit-gh-pulls-get-api)) + (user (car repo)) + (proj (cdr repo)) + (cached? (magit-gh-pulls-requests-cached-p api user proj)) + (stubs (when cached? + (funcall magit-gh-pulls-maybe-filter-pulls + (oref (gh-pulls-list api user proj) :data)))) + (num-total-stubs (length stubs)) + (branch (magit-get-current-branch))) + (when (or (> (length stubs) 0) (not cached?)) + (magit-insert-section (pulls) + (magit-insert-heading "Pull Requests:") + (dolist (stub stubs) + (let* ((id (oref stub :number)) + (req (oref (gh-pulls-get api user proj id) :data)) + (base-sha (oref (oref req :base) :sha)) + (base-ref (oref (oref req :base) :ref)) + (head-sha (oref (oref req :head) :sha)) + ;; branch has been deleted in the meantime... + (invalid (equal (oref (oref req :head) :ref) head-sha)) + (have-commits + (and (>= magit-gh-pulls-pull-detail-limit num-total-stubs) + (eql 0 (magit-git-exit-code "cat-file" "-e" base-sha)) + (eql 0 (magit-git-exit-code "cat-file" "-e" head-sha)))) + (applied (and have-commits + (magit-git-string "branch" branch + (format "--contains=%s" head-sha)))) + (heading + (format "[%s@%s] %s\n" + (propertize (number-to-string id) + 'face 'magit-tag) + (if (string= base-ref branch) + (propertize base-ref + 'face 'magit-branch-local) + base-ref) + (propertize + (oref req :title) 'face + (cond (applied 'magit-cherry-equivalent) + (have-commits nil) + (invalid 'error) + (t 'italic))))) + (info (list user proj id))) + (cond + (have-commits + (magit-insert-section + (pull info magit-gh-pulls-collapse-commits) + (insert heading) + (magit-insert-heading) + (when (and have-commits (not applied)) + (magit-git-wash + (apply-partially 'magit-log-wash-log 'cherry) + "cherry" "-v" (magit-abbrev-arg) + base-sha head-sha)))) + (invalid + (magit-insert-section (invalid-pull info) + (insert heading))) + (t + (magit-insert-section (unfetched-pull info) + (insert heading)))))) + (when (not cached?) + (insert "Press `# g` to update the pull request list.\n\n")) + (when (> (length stubs) 0) + (insert "\n")))))))) + (error nil))) + +(defun magit-gh-pulls-guess-topic-name (req) + (let ((user (oref (oref req :user) :login)) + (topic (oref (oref req :head) :ref))) + (format "%s/%s" user topic))) + +(defun magit-gh-section-req-data (&optional section) + (oref (apply #'gh-pulls-get + (magit-gh-pulls-get-api) + (magit-section-value (or section (magit-current-section)))) + :data)) + +(defun magit-gh-pulls-diff-pull-request () + (interactive) + (magit-section-case + (pull + (let* ((req (magit-gh-section-req-data)) + (inhibit-magit-refresh t)) + (magit-diff (concat (oref (oref req :base) :sha) ".." + (oref (oref req :head) :sha)))) + (magit-refresh)) + (unfetched-pull + (error "Please fetch pull request commits first")) + (invalid-pull + (error "This pull request refers to invalid reference")))) + + +(defun magit-gh-pulls-create-branch () + (interactive) + (magit-section-case + (pull + (let* ((req (magit-gh-section-req-data)) + (branch (read-from-minibuffer + "Branch name: " (magit-gh-pulls-guess-topic-name req))) + (base (magit-read-branch-or-commit + "Branch base: " + (oref (oref req :base) :ref))) + (inhibit-magit-refresh t)) + (magit-branch-and-checkout branch base) + (magit-merge (oref (oref req :head) :sha))) + (magit-refresh)) + (unfetched-pull + (error "Please fetch pull request commits first")) + (invalid-pull + (error "This pull request refers to invalid reference")))) + +(defun magit-gh-pulls-merge-pull-request () + (interactive) + (magit-section-case + (pull + (let* ((req (magit-gh-section-req-data)) + (branch (magit-gh-pulls-guess-topic-name req)) + (base (oref (oref req :base) :ref)) + (inhibit-magit-refresh t)) + (magit-branch-and-checkout branch base) + (magit-merge (oref (oref req :head) :sha)) + (magit-checkout base) + (magit-merge branch (when (member "--no-ff" (magit-gh-pulls-arguments)) + '("--no-ff"))) + (magit-call-git "branch" "-D" branch)) + (magit-refresh)) + (unfetched-pull + (error "Please fetch pull request commits first")) + (invalid-pull + (error "This pull request refers to invalid reference")))) + +(defun magit-gh-pulls-fetch-commits () + (interactive) + (magit-section-case + (unfetched-pull + (let* ((req (magit-gh-section-req-data)) + (head (oref req :head))) + (magit-run-git "fetch" (oref (oref head :repo) :git-url) + (oref head :ref)))) + (pull nil) + (invalid-pull + (error "This pull request refers to invalid reference")))) + +(defun magit-gh-pulls-url-for-pull (info) + "Return github url for a pull request using INFO." + (let ((url "https://github.com/%s/%s/pull/%s")) + (apply 'format url info))) + +(defun magit-gh-pulls-open-in-browser () + (interactive) + (let ((info (magit-section-value (magit-current-section)))) + (magit-section-case + (pull (browse-url (magit-gh-pulls-url-for-pull info))) + (unfetched-pull (browse-url (magit-gh-pulls-url-for-pull info)))))) + +(defun magit-gh-pulls-purge-cache () + (let* ((api (magit-gh-pulls-get-api)) + (cache (oref api :cache)) + (repo (magit-gh-pulls-guess-repo))) + (pcache-map cache (lambda (k v) + (when (string-match + (format "/repos/%s/%s/" (car repo) (cdr repo)) + (car k)) + (pcache-invalidate cache k)))))) + +(defun magit-gh-pulls-get-remote-default (&optional remote-name-override) + (let ((remote-name (or remote-name-override "origin")) + (remote-branches (magit-git-lines "branch" "-r")) + remote-head) + (while (and remote-branches (not remote-head)) + (let ((m (s-match (format "^\\s-*%s/HEAD -> %s/\\(\\w*\\)" remote-name remote-name) (car remote-branches)))) + (if m + (setq remote-head (cadr m)) + (setq remote-branches (cdr remote-branches))))) + remote-head)) + +(defun magit-gh-pulls-build-req (api user proj callback) + "Builds a request entity for a new pull request. Under + synchronous flow (editor disabled), fires CALLBACK with + API, USER, PROJ and the new REQUEST as args. Under + asynchronous flow, passes all ARGS through to the PR + editor which is responsible for continuing the flow." + (let* ((current (magit-get-current-branch)) + (current-default (magit-gh-pulls-get-remote-default)) + (base-branch (magit-read-other-branch-or-commit "Base" nil current-default)) + (head-branch (magit-read-other-branch-or-commit "Head" nil current))) + (let* ((head-remote (concat (magit-get-remote base-branch) "/" head-branch)) + (pushed-p (and (magit-branch-p head-remote) + (null (magit-git-lines "diff" (concat head-remote ".." head-branch)))))) + (when (and (not pushed-p) + (yes-or-no-p "PR branch doesn't appear to be pushed. Push it?")) + (magit-push current (magit-get-remote base-branch)))) + (let* ((base + (make-instance 'gh-repos-ref :user (make-instance 'gh-users-user :name user) + :repo (make-instance 'gh-repos-repo :name proj) + :ref base-branch)) + (head + (make-instance 'gh-repos-ref :user (make-instance 'gh-users-user :name user) + :repo (make-instance 'gh-repos-repo :name proj) + :ref head-branch)) + (default-title (magit-git-string "log" + (format "%s..%s" base-branch head-branch) + "--format=%s" "--reverse")) + (default-body (mapconcat 'identity (magit-git-lines "log" + (format "%s..%s" base-branch head-branch) + "-1" "--format=%b") " "))) + (if (member "--use-pr-editor" (magit-gh-pulls-arguments)) + (magit-gh-pulls-init-pull-editor api user proj default-title default-body base head callback) + (let* ((title (read-string "Title: " default-title)) + (body (read-string "Description: " default-body)) + (req (make-instance 'gh-pulls-request :head head :base base :body body :title title))) + (funcall callback api user proj req)))))) + +(defun magit-gh-pulls-init-pull-editor (api user proj default-title default-body base head callback) + "Create a new buffer for editing this pull request and + switch to it. The context needed to finalize the + pull request is stored in a buffer-local var in the + newly created buffer." + (let ((winconf (current-window-configuration)) + (buffer (get-buffer-create (format "*magit-gh-pulls: %s*" proj))) + (context (make-hash-table :test 'equal))) + (dolist (var '(api user proj base head callback)) + (puthash (symbol-name var) (eval var) context)) + (split-window-vertically) + (other-window 1) + (switch-to-buffer buffer) + (funcall 'magit-gh-pulls-editor-mode) + (insert (or default-title "") "\n\n" default-body) + (goto-char (point-min)) + (message "Opening pull request editor. C-c C-c to finish, C-c C-k to quit.") + (setq-local magit-gh-pulls-editor-context context) + (setq magit-gh-pulls-previous-winconf winconf))) + +(defun magit-gh-pulls-pull-editor-finish () + "Finish editing the current pull request and continue + to submit it. This should be called interactively + from within a pull request editor buffer." + (interactive) + (if (eq nil magit-gh-pulls-editor-context) + (message "This function can only be run in a pull editor buffer.") + (let* ((context magit-gh-pulls-editor-context) + (end-of-first-line (save-excursion + (beginning-of-buffer) + (line-end-position))) + (title (s-trim (buffer-substring-no-properties 1 end-of-first-line))) + (body (s-trim (buffer-substring-no-properties end-of-first-line (point-max)))) + (req (make-instance 'gh-pulls-request + :head (gethash "head" context) + :base (gethash "base" context) + :body body :title title))) + (funcall (gethash "callback" context) + (gethash "api" context) + (gethash "user" context) + (gethash "proj" context) + req) + (magit-gh-pulls-pull-editor-quit)))) + +(defun magit-gh-pulls-pull-editor-quit () + "Cleanup the current pull request editor and restore + the previous window config." + (interactive) + (if (eq nil magit-gh-pulls-editor-context) + (message "This function can only be run in a pull editor buffer.") + (let ((winconf magit-gh-pulls-previous-winconf)) + (kill-buffer) + (kill-local-variable 'magit-gh-pulls-previous-winconf) + (when winconf + (set-window-configuration winconf))))) + +(defun magit-gh-pulls-create-pull-request () + "Entrypoint for creating a new pull request." + (interactive) + (let ((repo (magit-gh-pulls-guess-repo))) + (when repo + (let* ((current-branch (magit-get-current-branch)) + (api (magit-gh-pulls-get-api)) + (user (car repo)) + (proj (cdr repo))) + (magit-gh-pulls-build-req api user proj 'magit-gh-pulls-submit-pull-request))))) + +(defun magit-gh-pulls-submit-pull-request (api user proj req) + "Endpoint for creating a new pull request. Sync and async + flows should both call this function to finish creating + a new pull request." + (interactive) + (let* ((a (gh-pulls-new api user proj req))) + (if (not (= (oref a :http-status) 201)) + (message "Error creating pull-request: %s. Have you pushed the branch to github?" (cdr (assoc "Status" (oref a :headers)))) + (let ((url (oref (oref a :data) :html-url))) + (message (concat "Created pull-request and copied URL to kill ring: " url)) + (when (member "--open-new-in-browser" (magit-gh-pulls-arguments)) + (browse-url url)) + (kill-new url))))) + +(defun magit-gh-pulls-reload () + (interactive) + (let ((creds (magit-gh-pulls-guess-repo))) + (if (not (and creds (car creds) (cdr creds))) + (message "Remote repository is not configured or incorrect.") + (magit-gh-pulls-purge-cache) + (gh-pulls-list (magit-gh-pulls-get-api) (car creds) (cdr creds)) + (magit-refresh)))) + +(easy-menu-define magit-gh-pulls-extension-menu + nil + "GitHub Pull Requests extension menu" + '("GitHub Pull Requests" + :visible magit-gh-pulls-mode + ["Reload pull request" magit-gh-pulls-reload] + ["Create pull request branch" magit-gh-pulls-create-branch] + ["Fetch pull request commits" magit-gh-pulls-fetch-commits] + ["Open pull request in browser" magit-gh-pulls-open-in-browser] + )) + +(easy-menu-add-item 'magit-mode-menu + '("Extensions") + magit-gh-pulls-extension-menu) + +(magit-define-section-jumper magit-jump-to-pulls "Pull Requests" pulls) +(define-key magit-status-mode-map (kbd "jq") 'magit-jump-to-pulls) + +(defvar magit-gh-pulls-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "#") 'magit-gh-pulls-popup) + map)) + +(defvar magit-gh-pulls-mode-lighter " Pulls") + +;;;###autoload +(define-minor-mode magit-gh-pulls-mode "Pull requests support for Magit" + :lighter magit-gh-pulls-mode-lighter + :require 'magit-gh-pulls + :keymap 'magit-gh-pulls-mode-map + (or (derived-mode-p 'magit-mode) + (error "This mode only makes sense with magit")) + (if magit-gh-pulls-mode + (magit-add-section-hook + 'magit-status-sections-hook + 'magit-gh-pulls-insert-gh-pulls + 'magit-insert-stashes) + (remove-hook 'magit-status-sections-hook 'magit-gh-pulls-insert-gh-pulls)) + (when (called-interactively-p 'any) + (magit-refresh))) + +;;;###autoload +(defun turn-on-magit-gh-pulls () + "Unconditionally turn on `magit-pulls-mode'." + (magit-gh-pulls-mode 1)) + +(magit-define-popup magit-gh-pulls-popup + "Show popup buffer featuring Github Pull Requests commands." + 'magit-commands + :switches '((?c "Produce merge commit" "--no-ff") + (?w "Open new PR in browser" "--open-new-in-browser") + (?e "Edit PR in full buffer" "--use-pr-editor")) + :actions '((?g "Reload" magit-gh-pulls-reload) + (?f "Fetch" magit-gh-pulls-fetch-commits) + (?d "Diff" magit-gh-pulls-diff-pull-request) + (?b "Make branch" magit-gh-pulls-create-branch) + (?m "Merge" magit-gh-pulls-merge-pull-request) + (?c "Create new PR" magit-gh-pulls-create-pull-request) + (?o "Open in browser" magit-gh-pulls-open-in-browser)) + :default-action 'magit-gh-pulls-reload) + +(provide 'magit-gh-pulls) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-gh-pulls.el ends here diff --git a/elpa/magit-popup-20160130.649/dir b/elpa/magit-popup-20160130.649/dir new file mode 100644 index 0000000..c276daf --- /dev/null +++ b/elpa/magit-popup-20160130.649/dir @@ -0,0 +1,18 @@ +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" 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-Popup: (magit-popup). Infix arguments with feedback. diff --git a/elpa/magit-popup-20160130.649/magit-popup-autoloads.el b/elpa/magit-popup-20160130.649/magit-popup-autoloads.el new file mode 100644 index 0000000..74c8caf --- /dev/null +++ b/elpa/magit-popup-20160130.649/magit-popup-autoloads.el @@ -0,0 +1,16 @@ +;;; magit-popup-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil nil ("magit-popup-pkg.el" "magit-popup.el") +;;;;;; (22221 60697 911041 79000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; magit-popup-autoloads.el ends here diff --git a/elpa/magit-popup-20160130.649/magit-popup-pkg.el b/elpa/magit-popup-20160130.649/magit-popup-pkg.el new file mode 100644 index 0000000..a62d46b --- /dev/null +++ b/elpa/magit-popup-20160130.649/magit-popup-pkg.el @@ -0,0 +1,9 @@ +(define-package "magit-popup" "20160130.649" "Define prefix-infix-suffix command combos" + '((emacs "24.4") + (async "20150909.2257") + (dash "20151021.113")) + :url "https://github.com/magit/magit" :keywords + '("bindings")) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/magit-popup-20160130.649/magit-popup.el b/elpa/magit-popup-20160130.649/magit-popup.el new file mode 100644 index 0000000..6119f7c --- /dev/null +++ b/elpa/magit-popup-20160130.649/magit-popup.el @@ -0,0 +1,1264 @@ +;;; magit-popup.el --- Define prefix-infix-suffix command combos -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file which +;; lists all contributors. If not, see http://magit.vc/authors. + +;; This library was inspired by and replaces library `magit-key-mode', +;; which was written by Phil Jackson and is +;; distributed under the GNU General Public License version 3 or later. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; Package-Requires: ((emacs "24.4") (async "20150909.2257") (dash "20151021.113")) +;; Keywords: bindings +;; Homepage: https://github.com/magit/magit + +;; 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 library implements a generic interface for toggling switches +;; and setting options and then invoking an Emacs command which does +;; something with these arguments. The prototypical use is for the +;; command to call an external process, passing on the arguments as +;; command line arguments. But this is only one of many possible +;; uses (though the one this library is optimized for). + +;; With the Emacs concept of "prefix arguments" in mind this could be +;; described as "infix arguments with feedback in a buffer". + +;; Commands that set the prefix argument for the subsequent command do +;; not limit what that next command could be. But entering a command +;; console popup does limit the selection to the commands defined for +;; that popup, and so we use the term "infix" instead of "prefix". + +;;; Code: + +(require 'button) +(require 'cl-lib) +(require 'dash) +(require 'format-spec) + +(and (require 'async-bytecomp nil t) + (memq 'magit (bound-and-true-p async-bytecomp-allowed-packages)) + (fboundp 'async-bytecomp-package-mode) + (async-bytecomp-package-mode 1)) + +(declare-function info 'info) +(declare-function Man-find-section 'man) +(declare-function Man-next-section 'man) + +;; For the `:variable' event type. +(declare-function magit-call-git 'magit-process) +(declare-function magit-git-string 'magit-git) +(declare-function magit-refresh 'magit-mode) +(declare-function magit-set 'magit-git) + +;; For branch actions. +(declare-function magit-branch-set-face 'magit-git) +(declare-function magit-local-branch-p 'magit-git) + +;;; Settings +;;;; Custom Groups + +(defgroup magit-popup nil + "Infix arguments with a popup as feedback." + :group 'bindings) + +(defgroup magit-popup-faces nil + "Faces used by Magit-Popup." + :group 'magit-popup) + +;;;; Custom Options + +(defcustom magit-popup-display-buffer-action '((display-buffer-below-selected)) + "The action used to display a popup buffer. + +Popup buffers are displayed using `display-buffer' with the value +of this option as ACTION argument. You can also set this to nil +and instead add an entry to `display-buffer-alist'." + :package-version '(magit-popup . "2.4.0") + :group 'magit-popup + :type 'sexp) + +(defcustom magit-popup-manpage-package + (if (memq system-type '(windows-nt ms-dos)) 'woman 'man) + "The package used to display manpages. +One of `man' or `woman'." + :group 'magit-popup + :type '(choice (const man) (const woman))) + +(defcustom magit-popup-show-help-echo t + "Show usage information in the echo area." + :group 'magit-popup + :type 'boolean) + +(defcustom magit-popup-show-common-commands t + "Initially show section with commands common to all popups. +This section can also be toggled temporarily using \ +\\\\[magit-popup-toggle-show-common-commands]." + :group 'magit-popup + :type 'boolean) + +(defcustom magit-popup-use-prefix-argument 'disabled + "Control how prefix arguments affect infix argument popups. + +This option controls the effect that the use of a prefix argument +before entering a popup has. The *intended* default is `default', +but the *actual* default is `disabled'. This is necessary because +the old popup implementation did simply forward such a pre-popup +prefix argument to the action invoked from the popup, and changing +that without users being aware of it could lead to tears. + +`disabled' Bring up a Custom option buffer so that the user reads + this and then makes an informed choice. + +`default' With a prefix argument directly invoke the popup's + default action (an Emacs command), instead of bringing + up the popup. + +`popup' With a prefix argument bring up the popup, otherwise + directly invoke the popup's default action. + +`nil' Ignore prefix arguments." + :group 'magit-popup + :type '(choice + (const :tag "Call default action instead of showing popup" default) + (const :tag "Show popup instead of calling default action" popup) + (const :tag "Ignore prefix argument" nil) + (const :tag "Abort and show usage information" disabled))) + +;;;; Custom Faces + +(defface magit-popup-heading + '((t :inherit font-lock-keyword-face)) + "Face for key mode header lines." + :group 'magit-popup-faces) + +(defface magit-popup-key + '((t :inherit font-lock-builtin-face)) + "Face for key mode buttons." + :group 'magit-popup-faces) + +(defface magit-popup-argument + '((t :inherit font-lock-warning-face)) + "Face used to display enabled arguments in popups." + :group 'magit-popup-faces) + +(defface magit-popup-disabled-argument + '((t :inherit shadow)) + "Face used to display disabled arguments in popups." + :group 'magit-popup-faces) + +(defface magit-popup-option-value + '((t :inherit font-lock-string-face)) + "Face used to display option values in popups." + :group 'magit-popup-faces) + +;;;; Keymap + +(defvar magit-popup-mode-map + (let ((map (make-sparse-keymap))) + (define-key map [remap self-insert-command] 'magit-invoke-popup-action) + (define-key map [?- t] 'magit-invoke-popup-switch) + (define-key map [?= t] 'magit-invoke-popup-option) + (define-key map [?\C-c ?\C-c] 'magit-popup-set-default-arguments) + (define-key map [?\C-x ?\C-s] 'magit-popup-save-default-arguments) + (define-key map [?\C-g] 'magit-popup-quit) + (define-key map [??] 'magit-popup-help) + (define-key map [?\C-h ?i] 'magit-popup-info) + (define-key map [?\C-t] 'magit-popup-toggle-show-common-commands) + (define-key map [?\d] 'backward-button) + (define-key map [?\C-p] 'backward-button) + (define-key map [?\t] 'forward-button) + (define-key map [?\C-n] 'forward-button) + (define-key map [?\r] 'push-button) + map) + "Keymap for `magit-popup-mode'. + +\\\ +This keymap contains bindings common to all popups. A section +listing these commands can be shown or hidden using \ +\\[magit-popup-toggle-show-common-commands]. + +The prefix used to toggle any switch can be changed by binding +another key to `magit-invoke-popup-switch'. Likewise binding +another key to `magit-invoke-popup-option' changes the prefixed +used to set any option. The two prefixes have to be different. +If you change these bindings you should also change the `prefix' +property of the button types `magit-popup-switch-button' and +`magit-popup-option-button'. + +If you change any other binding, then you might have to also edit +`magit-popup-common-commands' for things to align correctly in +the section listing these commands. + +Never bind an alphabetic character in this keymap or you might +make it impossible to invoke certain actions.") + +(defvar magit-popup-common-commands + '(("Set defaults" magit-popup-set-default-arguments) + ("View popup manual" magit-popup-info) + ("Toggle this section" magit-popup-toggle-show-common-commands) + ("Save defaults" magit-popup-save-default-arguments) + (" Popup help prefix" magit-popup-help) + ("Abort" magit-popup-quit))) + +;;;; Buttons + +(define-button-type 'magit-popup-button + 'face nil + 'action (lambda (button) + (funcall (button-get button 'function) + (button-get button 'event)))) + +(define-button-type 'magit-popup-switch-button + 'supertype 'magit-popup-button + 'function 'magit-invoke-popup-switch + 'property :switches + 'heading "Switches\n" + 'formatter 'magit-popup-format-argument-button + 'format " %k %d (%a)" + 'prefix ?- + 'maxcols 1) + +(define-button-type 'magit-popup-option-button + 'supertype 'magit-popup-button + 'function 'magit-invoke-popup-option + 'property :options + 'heading "Options\n" + 'formatter 'magit-popup-format-argument-button + 'format " %k %d (%a%v)" + 'prefix ?= + 'maxcols 1) + +(define-button-type 'magit-popup-variable-button + 'supertype 'magit-popup-button + 'function 'magit-invoke-popup-action + 'property :variables + 'heading "Variables\n" + 'formatter 'magit-popup-format-variable-button + 'format " %k %d" + 'prefix nil + 'maxcols 1) + +(define-button-type 'magit-popup-action-button + 'supertype 'magit-popup-button + 'function 'magit-invoke-popup-action + 'property :actions + 'heading "Actions\n" + 'formatter 'magit-popup-format-action-button + 'format " %k %d" + 'prefix nil + 'maxcols :max-action-columns) + +(define-button-type 'magit-popup-command-button + 'supertype 'magit-popup-action-button + 'formatter 'magit-popup-format-command-button + 'action (lambda (button) + (let ((command (button-get button 'function))) + (unless (eq command 'push-button) + (call-interactively command))))) + +(define-button-type 'magit-popup-internal-command-button + 'supertype 'magit-popup-command-button + 'heading "Common Commands\n" + 'maxcols 3) + +;;; Events + +(defvar-local magit-this-popup nil + "The popup which is currently active. +This is intended for internal use only. +Don't confuse this with `magit-current-popup'.") + +(defvar-local magit-this-popup-events nil + "The events known to the active popup. +This is intended for internal use only. +Don't confuse this with `magit-current-popup-args'.") + +(defun magit-popup-get (prop) + "While a popup is active, get the value of PROP." + (if (memq prop '(:switches :options :variables :actions)) + (plist-get magit-this-popup-events prop) + (plist-get (symbol-value magit-this-popup) prop))) + +(defun magit-popup-put (prop val) + "While a popup is active, set the value of PROP to VAL." + (if (memq prop '(:switches :options :variables :actions)) + (setq magit-this-popup-events + (plist-put magit-this-popup-events prop val)) + (error "Property %s isn't supported" prop))) + +(defvar magit-current-popup nil + "The popup from which this editing command was invoked. + +Use this inside the `interactive' form of a popup aware command +to determine whether it was invoked from a popup and if so from +which popup. If the current command was invoked without the use +of a popup then this is nil.") + +(defvar magit-current-popup-args nil + "The value of the popup arguments for this editing command. + +If the current command was invoked from a popup, then this is +a list of strings of all the set switches and options. This +includes arguments which are set by default not only those +explicitly set during this invocation. + +When the value is nil, then that can be because no argument is +set, or because the current command wasn't invoked from a popup; +consult `magit-current-popup' to tell the difference. + +Generally it is better to use `NAME-arguments', which is created +by `magit-define-popup', instead of this variable or the function +by the same name, because `NAME-argument' uses the default value +for the arguments when the editing command is invoked directly +instead of from a popup. When the command is bound in several +popups that might not be feasible though.") + +(defun magit-current-popup-args (&rest filter) + "Return the value of the popup arguments for this editing command. + +The value is the same as that of the variable by the same name +\(which see), except that FILTER is applied. FILTER is a list +of regexps; only arguments that match one of them are returned. +The first element of FILTER may also be `:not' in which case +only arguments that don't match any of the regexps are returned, +or `:only' which doesn't change the behaviour." + (let ((-compare-fn (lambda (a b) (magit-popup-arg-match b a)))) + (-filter (if (eq (car filter) :not) + (lambda (arg) (not (-contains? (cdr filter) arg))) + (when (eq (car filter) :only) + (pop filter)) + (lambda (arg) (-contains? filter arg))) + magit-current-popup-args))) + +(defun magit-popup-arg-match (pattern string) + (if (or (string-match-p "=$" pattern) + (string-match-p "^-[A-Z]$" pattern)) + (string-match (format "^%s\\(.*\\)$" pattern) string) + (string-equal string pattern))) + +(cl-defstruct magit-popup-event key dsc arg fun use val) + +(defun magit-popup-event-keydsc (ev) + (let ((key (magit-popup-event-key ev))) + (key-description (if (vectorp key) key (vector key))))) + +(defun magit-popup-lookup (event type) + (--first (equal (magit-popup-event-key it) event) + (-filter 'magit-popup-event-p (magit-popup-get type)))) + +(defun magit-popup-get-args () + (--mapcat (when (and (magit-popup-event-p it) + (magit-popup-event-use it)) + (list (format "%s%s" + (magit-popup-event-arg it) + (or (magit-popup-event-val it) "")))) + (append (magit-popup-get :switches) + (magit-popup-get :options)))) + +(defmacro magit-popup-convert-events (def form) + (declare (indent 1) (debug (form form))) + `(--map (if (or (null it) (stringp it) (functionp it)) it ,form) ,def)) + +(defun magit-popup-convert-switches (val def) + (magit-popup-convert-events def + (let ((a (nth 2 it))) + (make-magit-popup-event + :key (car it) :dsc (cadr it) :arg a + :use (and (member a val) t))))) + +(defun magit-popup-convert-options (val def) + (magit-popup-convert-events def + (let* ((a (nth 2 it)) + (r (format "^%s\\(.*\\)" a)) + (v (--first (string-match r it) val))) + (make-magit-popup-event + :key (car it) :dsc (cadr it) :arg a + :use (and v t) :val (and v (match-string 1 v)) + :fun (or (nth 3 it) 'read-from-minibuffer))))) + +(defun magit-popup-convert-variables (_val def) + (magit-popup-convert-events def + (make-magit-popup-event + :key (car it) :dsc (cadr it) :fun (nth 2 it) :arg (nth 3 it)))) + +(defun magit-popup-convert-actions (_val def) + (magit-popup-convert-events def + (make-magit-popup-event + :key (car it) :dsc (cadr it) :fun (nth 2 it)))) + +;;; Define + +(defmacro magit-define-popup (name doc &rest args) + "Define a popup command named NAME. + +NAME should begin with the package prefix and by convention end +with `-popup'. That name is used for the actual command as well +as for a variable used internally. DOC is used as the doc-string +of that command. + +Also define an option and a function named `SHORTNAME-arguments', +where SHORTNAME is NAME with the trailing `-popup' removed. The +name of this option and this function can be overwritten using +the optional argument OPTION, but that is rarely advisable. As a +special case if OPTION is specified but nil, do not define this +option and this function at all. + +The option `SHORTNAME-arguments' holds the default value for the +popup arguments. It can be customized from within the popup or +using the Custom interface. + +The function `SHORTNAME-arguments' is a wrapper around the +variable `magit-current-popup-args', both of which are intended +to be used inside the `interactive' form of commands commonly +invoked from the popup `NAME'. When such a command is invoked +from that popup, then the function `SHORTNAME-arguments' returns +the value of the variable `magit-current-popup-args'; however +when the command is invoked directly, then it returns the default +value of the variable `SHORTNAME-arguments'. + +Optional argument GROUP specifies the Custom group in which the +option is placed. If omitted then the option is placed in some +group the same way it is done when directly using `defcustom'. + +Optional argument MODE is deprecated, instead use the keyword +arguments `:setup-function' and/or `:refresh-function'. If MODE +is non-nil, then it specifies the mode used by the popup buffer, +instead of the default, which is `magit-popup-mode'. + +The remaining arguments should have the form + + [KEYWORD VALUE]... + +The following keywords are meaningful (and by convention are +usually specified in that order): + +`:actions' + The actions which can be invoked from the popup. VALUE is a + list whose members have the form (KEY DESC COMMAND), see + `magit-define-popup-action' for details. + + Actions are regular Emacs commands, which usually have an + `interactive' form setup to consume the values of the popup + `:switches' and `:options' when invoked from the corresponding + popup, else when invoked as the default action or directly + without using the popup, the default value of the variable + `SHORTNAME-arguments'. This is usually done by calling the + function `SHORTNAME-arguments'. + + Members of VALUE may also be strings, assuming the first member + is also a string. Instead of just one action section with the + heading \"Actions\", multiple sections are then inserted into + the popup buffer, using these strings as headings. + + Members of VALUE may also be nil. This should only be used + together with `:max-action-columns' and allows having gaps in + the action grit, which can help arranging actions sensibly. + +`:default-action' + The default action of the popup which is used directly instead + of displaying the popup buffer, when the popup is invoked with + a prefix argument. Also see `magit-popup-use-prefix-argument' + and `:use-prefix', which can be used to inverse the meaning of + the prefix argument. + +`:use-prefix' + Controls when to display the popup buffer and when to invoke + the default action (if any) directly. This overrides the + global default set using `magit-popup-use-prefix-argument'. + The value, if specified, should be one of `default' or `popup'. + +`:max-action-columns' + The maximum number of actions to display on a single line. + This helps arranging actions more sensibly. If there is not + enough room to display that many actions on one line, then + this is ignored. + +`:switches' + The popup arguments which can be toggled on and off. VALUE + is a list whose members have the form (KEY DESC SWITCH), see + `magit-define-popup-switch' for details. + +`:options' + The popup arguments which take a value, as in \"--opt=OPTVAL\". + VALUE is a list whose members have the form (KEY DESC OPTION + READER), see `magit-define-popup-option' for details. + +`:default-arguments' + The default arguments, a list of switches (which are then + enabled by default) and options with there default values, as + in \"--OPT=OPTVAL\". + +`:sequence-predicate' + When this function returns non-nil, then the popup uses + `:sequence-actions' instead of `:actions', and does not show + the `:switches' and `:options'. + +`:sequence-actions' + The actions which can be invoked from the popup, when + `:sequence-predicate' returns non-nil. + +`:setup-function' + When this function is specified, then it is used instead of + `magit-popup-default-setup'. + +`:refresh-function' + When this function is specified, then it is used instead of + calling `magit-popup-insert-section' three times with symbols + `magit-popup-switch-button', `magit-popup-option-button', and + finally `magit-popup-action-button' as argument. + +`:man-page' + The name of the manpage to be displayed when the user requests + help for a switch or argument. + +\(fn NAME DOC [GROUP [MODE [OPTION]]] :KEYWORD VALUE...)" + (declare (indent defun) (doc-string 2)) + (let* ((grp (unless (keywordp (car args)) (pop args))) + (mode (unless (keywordp (car args)) (pop args))) + (opt (symbol-name name)) + (opt (if (keywordp (car args)) + (intern (concat (if (string-suffix-p "-popup" opt) + (substring opt 0 -6) + opt) + "-arguments")) + (eval (pop args))))) + `(progn + (defun ,name (&optional arg) ,doc + (interactive "P") + (magit-invoke-popup ',name ,mode arg)) + (defvar ,name + (list :variable ',opt ,@args)) + (magit-define-popup-keys-deferred ',name) + ,@(when opt + `((defcustom ,opt (plist-get ,name :default-arguments) + "" + ,@(and grp (list :group grp)) + :type '(repeat (string :tag "Argument"))) + (defun ,opt () + (if (eq magit-current-popup ',name) + magit-current-popup-args + ,opt)) + (put ',opt 'definition-name ',name)))))) + +(defun magit-define-popup-switch (popup key desc switch + &optional enable at prepend) + "In POPUP, define KEY as SWITCH. + +POPUP is a popup command defined using `magit-define-popup'. +SWITCH is a string representing an argument that takes no value. +KEY is a character representing the second event in the sequence +of keystrokes used to toggle the argument. (The first event, the +prefix, is shared among all switches, defaults to -, and can be +changed in `magit-popup-mode-keymap'). + +DESC is a string describing the purpose of the argument, it is +displayed in the popup. + +If optional ENABLE is non-nil then the switch is on by default. + +SWITCH is inserted after all other switches already defined for +POPUP, unless optional PREPEND is non-nil, in which case it is +placed first. If optional AT is non-nil then it should be the +KEY of another switch already defined for POPUP, the argument +is then placed before or after AT, depending on PREPEND." + (declare (indent defun)) + (magit-define-popup-key popup :switches key + (list desc switch enable) at prepend)) + +(defun magit-define-popup-option (popup key desc option + &optional reader value at prepend) + "In POPUP, define KEY as OPTION. + +POPUP is a popup command defined using `magit-define-popup'. +OPTION is a string representing an argument that takes a value. +KEY is a character representing the second event in the sequence +of keystrokes used to set the argument's value. (The first +event, the prefix, is shared among all options, defaults to =, +and can be changed in `magit-popup-mode-keymap'). + +DESC is a string describing the purpose of the argument, it is +displayed in the popup. + +If optional VALUE is non-nil then the option is on by default, +and VALUE is its default value. + +OPTION is inserted after all other options already defined for +POPUP, unless optional PREPEND is non-nil, in which case it is +placed first. If optional AT is non-nil then it should be the +KEY of another option already defined for POPUP, the argument +is then placed before or after AT, depending on PREPEND." + (declare (indent defun)) + (magit-define-popup-key popup :options key + (list desc option reader value) at prepend)) + +(defun magit-define-popup-variable (popup key desc command formatter + &optional at prepend) + "In POPUP, define KEY as COMMAND. + +POPUP is a popup command defined using `magit-define-popup'. +COMMAND is a command which calls `magit-popup-set-variable'. +FORMATTER is a function which calls `magit-popup-format-variable'. +These two functions have to be called with the same arguments. + +KEY is a character representing the event used interactively call +the COMMAND. + +DESC is the variable or a representation thereof. It's not +actually used for anything. + +COMMAND is inserted after all other commands already defined for +POPUP, unless optional PREPEND is non-nil, in which case it is +placed first. If optional AT is non-nil then it should be the +KEY of another command already defined for POPUP, the command +is then placed before or after AT, depending on PREPEND." + (declare (indent defun)) + (magit-define-popup-key popup :variables key + (list desc command formatter) at prepend)) + +(defun magit-define-popup-action (popup key desc command + &optional at prepend) + "In POPUP, define KEY as COMMAND. + +POPUP is a popup command defined using `magit-define-popup'. +COMMAND can be any command but should usually consume the popup +arguments in its `interactive' form. +KEY is a character representing the event used invoke the action, +i.e. to interactively call the COMMAND. + +DESC is a string describing the purpose of the action, it is +displayed in the popup. + +COMMAND is inserted after all other commands already defined for +POPUP, unless optional PREPEND is non-nil, in which case it is +placed first. If optional AT is non-nil then it should be the +KEY of another command already defined for POPUP, the command +is then placed before or after AT, depending on PREPEND." + (declare (indent defun)) + (magit-define-popup-key popup :actions key + (list desc command) at prepend)) + +(defun magit-define-popup-sequence-action + (popup key desc command &optional at prepend) + "Like `magit-define-popup-action' but for `:sequence-action'." + (declare (indent defun)) + (magit-define-popup-key popup :sequence-actions key + (list desc command) at prepend)) + +(defconst magit-popup-type-plural-alist + '((:switch . :switches) + (:option . :options) + (:variable . :variables) + (:action . :actions) + (:sequence-action . :sequence-actions))) + +(defun magit-popup-pluralize-type (type) + (or (cdr (assq type magit-popup-type-plural-alist)) + type)) + +(defun magit-define-popup-key + (popup type key def &optional at prepend) + "In POPUP, define KEY as an action, switch, or option. +It's better to use one of the specialized functions + `magit-define-popup-action', + `magit-define-popup-sequence-action', + `magit-define-popup-switch', + `magit-define-popup-option', or + `magit-define-popup-variable'." + (declare (indent defun)) + (setq type (magit-popup-pluralize-type type)) + (if (memq type '(:switches :options :variables :actions :sequence-actions)) + (if (boundp popup) + (let* ((plist (symbol-value popup)) + (value (plist-get plist type)) + (elt (assoc key value))) + (if elt + (setcdr elt def) + (setq elt (cons key def))) + (if at + (when (setq at (cl-member at value :key 'car-safe :test 'equal)) + (setq value (cl-delete key value :key 'car-safe :test 'equal)) + (if prepend + (progn (push (car at) (cdr at)) + (setcar at elt)) + (push elt (cdr at)))) + (setq value (cl-delete key value :key 'car-safe :test 'equal))) + (unless (assoc key value) + (setq value (if prepend + (cons elt value) + (append value (list elt))))) + (set popup (plist-put plist type value))) + (push (list type key def at prepend) + (get popup 'magit-popup-deferred))) + (error "Unknown popup event type: %s" type))) + +(defun magit-define-popup-keys-deferred (popup) + (dolist (args (get popup 'magit-popup-deferred)) + (condition-case err + (apply #'magit-define-popup-key popup args) + ((debug error) + (display-warning 'magit (error-message-string err) :error)))) + (put popup 'magit-popup-deferred nil)) + +(defun magit-change-popup-key (popup type from to) + "In POPUP, bind TO to what FROM was bound to. +TYPE is one of `:action', `:sequence-action', `:switch', or +`:option'. Bind TO and unbind FROM, both are characters." + (--if-let (assoc from (plist-get (symbol-value popup) + (magit-popup-pluralize-type type))) + (setcar it to) + (message "magit-change-popup-key: FROM key %c is unbound" from))) + +(defun magit-remove-popup-key (popup type key) + "In POPUP, remove KEY's binding of TYPE. +POPUP is a popup command defined using `magit-define-popup'. +TYPE is one of `:action', `:sequence-action', `:switch', or +`:option'. KEY is the character which is to be unbound." + (setq type (magit-popup-pluralize-type type)) + (let* ((plist (symbol-value popup)) + (alist (plist-get plist type)) + (value (assoc key alist))) + (set popup (plist-put plist type (delete value alist))))) + +;;; Invoke + +(defvar-local magit-popup-previous-winconf nil) + +(defun magit-invoke-popup (popup mode arg) + (let* ((def (symbol-value popup)) + (val (symbol-value (plist-get def :variable))) + (default (plist-get def :default-action)) + (local (plist-get def :use-prefix)) + (use-prefix (or local magit-popup-use-prefix-argument))) + (cond + ((and arg (eq magit-popup-use-prefix-argument 'disabled)) + (customize-option-other-window 'magit-popup-use-prefix-argument) + (error (concat "The meaning of prefix arguments has changed. " + "Please explicitly enable their use again."))) + ((or (and (eq use-prefix 'default) arg) + (and (eq use-prefix 'popup) (not arg))) + (if default + (let ((magit-current-popup (list popup 'default)) + (magit-current-popup-args + (let ((magit-this-popup popup) + (magit-this-popup-events nil)) + (magit-popup-default-setup val def) + (magit-popup-get-args)))) + (when (and arg (listp arg)) + (setq current-prefix-arg (and (not (= (car arg) 4)) + (list (/ (car arg) 4))))) + (call-interactively default)) + (message "%s has no default action; showing popup instead." popup) + (magit-popup-mode-setup popup mode))) + ((memq use-prefix '(disabled default popup nil)) + (magit-popup-mode-setup popup mode) + (when magit-popup-show-help-echo + (message (concat "Type C-h i to view popup manual, " + "? to describe an argument or action.")))) + (local + (error "Invalid :use-prefix popup property value: %s" use-prefix)) + (t + (error "Invalid magit-popup-use-prefix-argument value: %s" use-prefix))))) + +(defun magit-invoke-popup-switch (event) + (interactive (list last-command-event)) + (--if-let (magit-popup-lookup event :switches) + (progn + (setf (magit-popup-event-use it) + (not (magit-popup-event-use it))) + (magit-refresh-popup-buffer)) + (user-error "%c isn't bound to any switch" event))) + +(defun magit-invoke-popup-option (event) + (interactive (list last-command-event)) + (--if-let (magit-popup-lookup event :options) + (progn + (if (magit-popup-event-use it) + (setf (magit-popup-event-use it) nil) + (let* ((arg (magit-popup-event-arg it)) + (val (funcall + (magit-popup-event-fun it) + (concat arg (unless (string-match-p "=$" arg) ": ")) + (magit-popup-event-val it)))) + (setf (magit-popup-event-use it) t) + (setf (magit-popup-event-val it) val))) + (magit-refresh-popup-buffer)) + (user-error "%c isn't bound to any option" event))) + +(defun magit-invoke-popup-action (event) + (interactive (list last-command-event)) + (let ((action (magit-popup-lookup event :actions)) + (variable (magit-popup-lookup event :variables))) + (if (or action variable) + (let ((magit-current-popup magit-this-popup) + (magit-current-popup-args (magit-popup-get-args)) + (command (magit-popup-event-fun (or action variable)))) + (when action + (magit-popup-quit)) + (call-interactively command) + (setq this-command command) + (unless action + (magit-refresh-popup-buffer))) + (if (eq event ?q) + (magit-popup-quit) + (user-error "%c isn't bound to any action" event))))) + +(defun magit-popup-set-variable + (variable choices &optional default other) + (--if-let (--if-let (magit-git-string "config" "--local" variable) + (cadr (member it choices)) + (car choices)) + (magit-set it variable) + (magit-call-git "config" "--unset" variable)) + (magit-refresh) + (message "%s %s" variable + (magit-popup-format-variable-1 variable choices default other))) + +(defun magit-popup-quit () + "Quit the current popup command without invoking an action." + (interactive) + (let ((winconf magit-popup-previous-winconf)) + (if (derived-mode-p 'magit-popup-mode) + (kill-buffer) + (magit-popup-help-mode -1) + (kill-local-variable 'magit-popup-previous-winconf)) + (when winconf + (set-window-configuration winconf)))) + +(defun magit-popup-read-number (prompt &optional default) + "Like `read-number' but DEFAULT may be a numeric string." + (read-number prompt (if (stringp default) + (string-to-number default) + default))) + +;;; Save + +(defun magit-popup-set-default-arguments (arg) + "Set default value for the arguments for the current popup. +Then close the popup without invoking an action; unless a prefix +argument is used in which case the popup remains open. + +For a popup named `NAME-popup' that usually means setting the +value of the custom option `NAME-arguments'." + (interactive "P") + (customize-set-variable (magit-popup-get :variable) + (magit-popup-get-args)) + (unless arg (magit-popup-quit))) + +(defun magit-popup-save-default-arguments (arg) + "Save default value for the arguments for the current popup. +Then close the popup without invoking an action; unless a prefix +argument is used in which case the popup remains open. + +For a popup named `NAME-popup' that usually means saving the +value of the custom option `NAME-arguments'." + (interactive "P") + (customize-save-variable (magit-popup-get :variable) + (magit-popup-get-args)) + (unless arg (magit-popup-quit))) + +;;; Help + +(defun magit-popup-toggle-show-common-commands () + "Show or hide an additional section with common commands. +The commands listed in this section are common to all popups +and are defined in `magit-popup-mode-map' (which see)." + (interactive) + (setq magit-popup-show-common-commands + (not magit-popup-show-common-commands)) + (magit-refresh-popup-buffer) + (fit-window-to-buffer)) + +(defun magit-popup-help () + "Show help for the argument or action at point." + (interactive) + (let* ((man (magit-popup-get :man-page)) + (key (read-key-sequence + (concat "Describe key" (and man " (? for manpage)") ": "))) + (int (aref key (1- (length key)))) + (def (or (lookup-key (current-local-map) key t) + (lookup-key (current-global-map) key)))) + (pcase def + (`magit-invoke-popup-switch + (magit-popup-manpage man (magit-popup-lookup int :switches))) + (`magit-invoke-popup-option + (magit-popup-manpage man (magit-popup-lookup int :options))) + (`magit-popup-help + (magit-popup-manpage man nil)) + ((or `self-insert-command + `magit-invoke-popup-action) + (setq def (or (magit-popup-lookup int :actions) + (magit-popup-lookup int :variables))) + (if def + (magit-popup-describe-function (magit-popup-event-fun def)) + (ding) + (message nil))) + (`nil (ding) + (message nil)) + (_ (magit-popup-describe-function def))))) + +(defun magit-popup-manpage (topic arg) + (unless topic + (user-error "No man page associated with %s" + (magit-popup-get :man-page))) + (when arg + (setq arg (magit-popup-event-arg arg))) + (let ((winconf (current-window-configuration)) buffer) + (pcase magit-popup-manpage-package + (`woman (delete-other-windows) + (split-window-below) + (with-no-warnings ; display-buffer-function is obsolete + (let ((display-buffer-alist nil) + (display-buffer-function nil)) + (woman topic))) + (setq buffer (current-buffer))) + (`man (cl-letf (((symbol-function #'fboundp) (lambda (_) nil))) + (setq buffer (man topic))) + (delete-other-windows) + (split-window-below) + (set-window-buffer (selected-window) buffer))) + (with-current-buffer buffer + (setq magit-popup-previous-winconf winconf) + (magit-popup-help-mode) + (fit-window-to-buffer (next-window)) + (if (and arg + (Man-find-section "OPTIONS") + (re-search-forward (format "^[\t\s]+\\(-., \\)*?%s[=\n]" arg) + (save-excursion + (Man-next-section 1) + (point)) + t)) + (goto-char (1+ (match-beginning 0))) + (goto-char (point-min)))))) + +(defun magit-popup-describe-function (function) + (let ((winconf (current-window-configuration))) + (delete-other-windows) + (split-window-below) + (other-window 1) + (with-no-warnings ; display-buffer-function is obsolete + (let ((display-buffer-alist nil) + (display-buffer-function nil)) + (describe-function function))) + (fit-window-to-buffer) + (other-window 1) + (setq magit-popup-previous-winconf winconf) + (magit-popup-help-mode))) + +(defun magit-popup-info () + "Show the popup manual." + (interactive) + (let ((winconf (current-window-configuration))) + (delete-other-windows) + (split-window-below) + (info "(magit-popup.info)Usage") + (magit-popup-help-mode) + (setq magit-popup-previous-winconf winconf)) + (magit-popup-help-mode) + (fit-window-to-buffer (next-window))) + +(define-minor-mode magit-popup-help-mode + "Auxiliary minor mode used to restore previous window configuration. +When some sort of help buffer is created from within a popup, +then this minor mode is turned on in that buffer, so that when +the user quits it, the previous window configuration is also +restored." + :keymap '(([remap Man-quit] . magit-popup-quit) + ([remap Info-exit] . magit-popup-quit) + ([remap quit-window] . magit-popup-quit))) + +;;; Modes + +(define-derived-mode magit-popup-mode fundamental-mode "MagitPopup" + "Major mode for infix argument popups." + :mode 'magit-popup + (setq truncate-lines t) + (setq buffer-read-only t) + (setq-local scroll-margin 0) + (setq-local magit-popup-show-common-commands magit-popup-show-common-commands) + (hack-dir-local-variables-non-file-buffer)) + +(put 'magit-popup-mode 'mode-class 'special) + +(defun magit-popup-default-setup (val def) + (if (--when-let (magit-popup-get :sequence-predicate) + (funcall it)) + (magit-popup-put :actions (magit-popup-convert-actions + val (magit-popup-get :sequence-actions))) + (magit-popup-put :variables (magit-popup-convert-variables + val (plist-get def :variables))) + (magit-popup-put :switches (magit-popup-convert-switches + val (plist-get def :switches))) + (magit-popup-put :options (magit-popup-convert-options + val (plist-get def :options))) + (magit-popup-put :actions (magit-popup-convert-actions + val (plist-get def :actions))))) + +(defun magit-popup-mode-setup (popup mode) + (let ((val (symbol-value (plist-get (symbol-value popup) :variable))) + (def (symbol-value popup))) + (magit-popup-mode-display-buffer (get-buffer-create + (format "*%s*" popup)) + (or mode 'magit-popup-mode)) + (setq magit-this-popup popup) + (if (bound-and-true-p magit-popup-setup-hook) ; obsolete + (run-hook-with-args 'magit-popup-setup-hook val def) + (funcall (or (magit-popup-get :setup-function) + 'magit-popup-default-setup) + val def))) + (magit-refresh-popup-buffer) + (fit-window-to-buffer nil nil (line-number-at-pos (point-max)))) + +(defun magit-popup-mode-display-buffer (buffer mode) + (let ((winconf (current-window-configuration))) + (select-window (display-buffer buffer magit-popup-display-buffer-action)) + (funcall mode) + (setq magit-popup-previous-winconf winconf))) + +(defvar magit-refresh-popup-buffer-hook nil + "Hook run by `magit-refresh-popup-buffer'. + +The hook is run right after inserting the representation of the +popup events but before optionally inserting the representation +of events shared by all popups and before point is adjusted.") + +(defun magit-refresh-popup-buffer () + (let* ((inhibit-read-only t) + (button (button-at (point))) + (prefix (and button (button-get button 'prefix))) + (event (and button (button-get button 'event)))) + (erase-buffer) + (save-excursion + (--if-let (magit-popup-get :refresh-function) + (funcall it) + (magit-popup-insert-section 'magit-popup-switch-button) + (magit-popup-insert-section 'magit-popup-option-button) + (magit-popup-insert-section 'magit-popup-variable-button) + (magit-popup-insert-section 'magit-popup-action-button)) + (run-hooks 'magit-refresh-popup-buffer-hook) + (when magit-popup-show-common-commands + (magit-popup-insert-command-section + 'magit-popup-internal-command-button + magit-popup-common-commands))) + (set-buffer-modified-p nil) + (when event + (while (and (ignore-errors (forward-button 1)) + (let ((b (button-at (point)))) + (or (not (equal (button-get b 'prefix) prefix)) + (not (equal (button-get b 'event) event))))))))) + +;;; Draw + +(defvar magit-popup-min-padding 3 + "Minimal amount of whitespace between columns in popup buffers.") + +(defun magit-popup-insert-section (type &optional spec heading) + (if (not spec) + (progn (setq spec (magit-popup-get (button-type-get type 'property))) + (when spec + (if (or (stringp (car spec)) + (functionp (car spec))) + (--each (--partition-by-header + (or (stringp it) (functionp it)) + spec) + (magit-popup-insert-section type (cdr it) (car it))) + (magit-popup-insert-section type spec)))) + (let* ((formatter (button-type-get type 'formatter)) + (items (mapcar (lambda (ev) + (and ev (or (funcall formatter type ev) '("")))) + (or spec (magit-popup-get + (button-type-get type 'property))))) + (maxcols (button-type-get type 'maxcols)) + (pred (magit-popup-get :sequence-predicate))) + (if (and pred (funcall pred)) + (setq maxcols nil) + (cl-typecase maxcols + (keyword (setq maxcols (magit-popup-get maxcols))) + (symbol (setq maxcols (symbol-value maxcols))))) + (when items + (if (functionp heading) + (when (setq heading (funcall heading)) + (insert heading ?\n)) + (unless heading + (setq heading (button-type-get type 'heading))) + (insert (propertize heading 'face 'magit-popup-heading)) + (unless (string-match "\n$" heading) + (insert "\n"))) + (when heading + (let ((colwidth + (+ (apply 'max (mapcar (lambda (e) (length (car e))) items)) + magit-popup-min-padding))) + (dolist (item items) + (unless (bolp) + (let ((padding (- colwidth (% (current-column) colwidth)))) + (if (and (< (+ (current-column) padding colwidth) + (window-width)) + (< (ceiling (/ (current-column) (* colwidth 1.0))) + (or maxcols 1000))) + (insert (make-string padding ?\s)) + (insert "\n")))) + (unless (equal item '("")) + (if item + (apply 'insert-button item) + (insert ?\s))))) + (insert (if (= (char-before) ?\n) "\n" "\n\n"))))))) + +(defun magit-popup-format-argument-button (type ev) + (list (format-spec + (button-type-get type 'format) + `((?k . ,(propertize (concat + (--when-let (button-type-get type 'prefix) + (char-to-string it)) + (magit-popup-event-keydsc ev)) + 'face 'magit-popup-key)) + (?d . ,(magit-popup-event-dsc ev)) + (?a . ,(propertize (magit-popup-event-arg ev) + 'face (if (magit-popup-event-use ev) + 'magit-popup-argument + 'magit-popup-disabled-argument))) + (?v . ,(let ((val (magit-popup-event-val ev))) + (if (and (magit-popup-event-use ev) + (not (equal val ""))) + (propertize (format "\"%s\"" val) + 'face 'magit-popup-option-value) + ""))))) + 'type type 'event (magit-popup-event-key ev))) + +(defun magit-popup-format-variable-button (type ev) + (list (format-spec + (button-type-get type 'format) + `((?k . ,(propertize (magit-popup-event-keydsc ev) + 'face 'magit-popup-key)) + (?d . ,(funcall (magit-popup-event-arg ev))))) + 'type type 'event (magit-popup-event-key ev))) + +(defun magit-popup-format-variable + (variable choices &optional default other width) + (concat variable + (if width (make-string (- width (length variable)) ?\s) " ") + (magit-popup-format-variable-1 variable choices default other))) + +(defun magit-popup-format-variable-1 + (variable choices &optional default other) + (let ((local (magit-git-string "config" "--local" variable)) + (global (magit-git-string "config" "--global" variable))) + (when other + (--if-let (magit-git-string "config" other) + (setq other (concat other ":" it)) + (setq other nil))) + (concat + (propertize "[" 'face 'magit-popup-disabled-argument) + (mapconcat + (lambda (choice) + (propertize choice 'face (if (equal choice local) + 'magit-popup-option-value + 'magit-popup-disabled-argument))) + choices + (propertize "|" 'face 'magit-popup-disabled-argument)) + (when (or global other default) + (concat + (propertize "|" 'face 'magit-popup-disabled-argument) + (cond (global + (propertize (concat "global:" global) + 'face (cond (local + 'magit-popup-disabled-argument) + ((member global choices) + 'magit-popup-option-value) + (t + 'font-lock-warning-face)))) + (other + (propertize other + 'face (if local + 'magit-popup-disabled-argument + 'magit-popup-option-value))) + (default + (propertize (concat "default:" default) + 'face (if local + 'magit-popup-disabled-argument + 'magit-popup-option-value)))))) + (propertize "]" 'face 'magit-popup-disabled-argument)))) + +(defun magit-popup-format-action-button (type ev) + (let* ((dsc (magit-popup-event-dsc ev)) + (fun (and (functionp dsc) dsc))) + (when fun + (setq dsc + (-when-let (branch (funcall fun)) + (if (next-single-property-change 0 'face (concat "0" branch)) + branch + (magit-branch-set-face branch))))) + (when dsc + (list (format-spec + (button-type-get type 'format) + `((?k . ,(propertize (magit-popup-event-keydsc ev) + 'face 'magit-popup-key)) + (?d . ,dsc) + (?D . ,(if (and (not fun) + (eq (magit-popup-event-fun ev) + (magit-popup-get :default-action))) + (propertize dsc 'face 'bold) + dsc)))) + 'type type 'event (magit-popup-event-key ev))))) + +(defun magit-popup-insert-command-section (type spec) + (magit-popup-insert-section + type (mapcar (lambda (elt) + (list (car (where-is-internal (cadr elt) + (current-local-map))) + (car elt))) + spec))) + +(defun magit-popup-format-command-button (type elt) + (nconc (magit-popup-format-action-button + type (make-magit-popup-event :key (car elt) + :dsc (cadr elt))) + (list 'function (lookup-key (current-local-map) (car elt))))) + +;;; Utilities + +(defun magit-popup-import-file-args (args files) + (if files + (cons (concat "-- " (mapconcat #'identity files ",")) args) + args)) + +(defun magit-popup-export-file-args (args) + (let ((files (--first (string-prefix-p "-- " it) args))) + (when files + (setq args (remove files args) + files (split-string (substring files 3) ","))) + (list args files))) + +;;; magit-popup.el ends soon + +(defconst magit-popup-font-lock-keywords + (eval-when-compile + `((,(concat "(\\(magit-define-popup\\)\\_>" + "[ \t'\(]*" + "\\(\\(?:\\sw\\|\\s_\\)+\\)?") + (1 'font-lock-keyword-face) + (2 'font-lock-function-name-face nil t))))) + +(font-lock-add-keywords 'emacs-lisp-mode magit-popup-font-lock-keywords) + +(provide 'magit-popup) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-popup.el ends here diff --git a/elpa/magit-popup-20160130.649/magit-popup.info b/elpa/magit-popup-20160130.649/magit-popup.info new file mode 100644 index 0000000..a4b6822 --- /dev/null +++ b/elpa/magit-popup-20160130.649/magit-popup.info @@ -0,0 +1,710 @@ +This is magit-popup.info, produced by makeinfo version 5.2 from +magit-popup.texi. + +Taking inspiration from regular prefix commands and prefix arguments, +this library implements a similar abstraction; a new kind of prefix +command that is associated with a specific set of infix arguments and +suffix commands. + + Copyright (C) 2015-2016 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document 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. +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Magit-Popup: (magit-popup). Infix arguments with feedback. +END-INFO-DIR-ENTRY + + +File: magit-popup.info, Node: Top, Next: Introduction, Up: (dir) + +Magit-Popup User Manual +*********************** + +Taking inspiration from regular prefix commands and prefix arguments, +this library implements a similar abstraction; a new kind of prefix +command that is associated with a specific set of infix arguments and +suffix commands. + + Copyright (C) 2015-2016 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document 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. + +* Menu: + +* Introduction:: +* Usage:: +* Defining prefix and suffix commands:: + +— The Detailed Node Listing — + +Usage + +* Customizing existing popups:: +* Other options:: + +Defining prefix and suffix commands + +* Defining prefix commands:: +* Defining suffix commands:: + + + +File: magit-popup.info, Node: Introduction, Next: Usage, Prev: Top, Up: Top + +1 Introduction +************** + +Taking inspiration from regular prefix commands and prefix arguments, +this library implements a similar abstraction; a new kind of prefix +command that is associated with a specific set of infix arguments and +suffix commands. + + Invoking such a prefix command displays a popup buffer which lists +the associated infix arguments and suffix commands. In that buffer each +argument is prefixes with the key sequence that can be used to toggle it +or change its value. Likewise each suffix command is prefixed with the +key used to invoke it. Such a popup buffer might look like this: + +,----------------------------------------- +|Switches +| -l Show graph (--graph) +| -d Show refnames (--decorate) +| +|Options +| =m Search messages (--grep="popup") +| =p Search patches (-G) +| +|Action +| l Show log for current branch +| o Show log for another branch +'----------------------------------------- + + The user could then for example type ‘-l’ to toggle the ‘--graph’ +*switch* (when it is on then it is shown in green, otherwise in gray), +or ‘=m’ to change the value of the *option* ‘--grep’. + + Once all arguments are as desired one invokes a suffix command, which +causes the popup buffer to disappears. The suffix command should then +retrieve the infix arguments in its ‘interactive’ form like this is done +for prefix arguments. + + While such "prefix-infix-suffix" combos were inspired by regular +prefix commands and prefix arguments, they are also quite different. +This should illustrate the most basic differences: + + • A regular prefix commands + + /- command1 + prefix --- command2 + \- command3 + + • Prefix arguments + + /- command1 + C-u ... --- command2 + \- well *any* command + + • A Prefix-Infix-Suffix combo + + /- argument1 -\ /- suffix1 + prefix----- argument2 --+-- suffix2 + ^ \- argument3 -/ + | | + '--------' + (refresh buffer) + + This library was written as a replacement for ‘magit-key-mode’ which +was used in Magit releases before 2.1.0. It is used to implement all +"popups" in the current Magit release but a future release will switch +to yet another implementation. + + This library does not depend on any other Magit libraries and it is +distributed as a separate package, which makes it possible to use it in +packages that are not related to Magit. But keep in mind that it will +be deprecated eventually. + + +File: magit-popup.info, Node: Usage, Next: Defining prefix and suffix commands, Prev: Introduction, Up: Top + +2 Usage +******* + +Every popup buffers created with a prefix command contains a section +named "Actions" listing the available suffix commands. Most buffers +also contain a "Switches" and/or an "Options" section which list the two +types of infix arguments separately. + + Switches are arguments that can be toggled on or off. When a switch +is active then it is shown in color, when it is off then it is shown in +gray (of course the details depend on the color theme in use). + + Options are arguments that have a value. When an option has a value +then that is shown after the option itself. Because for some options +the empty string is a valid value, options are additionally colorized +like switches to indicate whether they are active or not. + + The events bound to suffix commands are always single alphabetic +characters. The bindings for arguments are always two events long. For +switches the first key is always ‘-’, for options it is always ‘=’. The +second key is always an alphabetic character. + + By default popup buffers also feature a section listing commands +common to all popups. To avoid conflicts with suffix commands, the +bindings of these common commands are not alphabetic characters. This +section is shown by default so that documentation-resistant users get a +change to notice them. + + -- User Option: magit-popup-show-common-commands + + This option controls whether the section which lists the commands + that are common to all popups is initially show. We recommend you + set this to ‘nil’ - after you have memorized that it can be shown + on demand using ‘C-t’. + +‘C-t’ (‘magit-popup-toggle-show-common-commands’) + + Show or hide the section listing the commands shared by all popups. + +‘C-g’ (‘magit-popup-quit’) + + Quit popup buffer without invoking a suffix command. + + Without further action, setting arguments only affects the next +suffix command. Invoking the same prefix command again resets the +arguments to their default value, but the defaults can be changed +directly from the popup buffer itself. For a prefix command named +‘NAME-popup’ the default values are stored as the value of the custom +option named ‘NAME-arguments’. While this option can be customized +using the Custom interface, it is better to do so directly from the +popup buffer. + +‘C-c C-c’ (‘magit-popup-set-default-arguments’) + + This sets the default value for the arguments for the current + popup. + + Then the popup buffer is closed without invoking a suffix command; + unless a prefix argument is used in which case the popup remains + open. + +‘C-x C-s’ (‘magit-popup-save-default-arguments’) + + This sets the default value for the arguments for the current popup + and saves it for future Emacs sessions. + + Then the popup buffer is closed without invoking an action; unless + a prefix argument is used in which case the popup remains open. + + It is also possible to add additional arguments and commands to an +existing popup, but that cannot be done directly from the popup (or the +Custom interface). See *note Customizing existing popups: Customizing +existing popups. + + Documentation about a popup’s arguments and commands can be shown +directly from the popup. + +‘C-h i’ (‘magit-popup-info’) + + Show this manual. + +‘?’ (‘magit-popup-help’) + + This command reads a key sequence and then shows the documentation + of the argument or command that sequence is bound to. In other + words type the same keys that you would use to invoke the argument + or command, but prefix the sequence with ‘?’. + + For suffix commands this shows the doc-string. For arguments this + command can only show something for popups that have an associated + man-page. If the man-page is set, then this command displays it in + a separate buffer and puts point on the entry about the argument in + question. + + The buffer which is used to display the documentation is selected. + Simply press ‘q’ to leave that buffer and restore the old window + configuration. + + While it isn’t very useful, it is possible to move around in a popup +buffer using ‘C-p’ and ‘C-n’, and to invoke the argument or command at +point using ‘RET’. But it is much more efficient to use the dedicated +key bindings instead, so these commands are not listed in popup buffers +along with the other common commands. + +* Menu: + +* Customizing existing popups:: +* Other options:: + + +File: magit-popup.info, Node: Customizing existing popups, Next: Other options, Up: Usage + +2.1 Customizing existing popups +=============================== + +It is possible to define additional infix arguments and suffix commands +to an existing popup using the following functions. + + You can find some examples which use the below commands at +. + + -- Function: magit-define-popup-switch popup key desc switch &optional + enable at prepend + + In POPUP, define KEY as SWITCH. + + POPUP is a popup command defined using ‘magit-define-popup’. + SWITCH is a string representing an argument that takes no value. + KEY is a character representing the second event in the sequence of + keystrokes used to toggle the argument. (The first event, the + prefix, is shared among all switches, defaults to ‘-’, and can be + changed in ‘magit-popup-mode-keymap’). + + DESC is a string describing the purpose of the argument, it is + displayed in the popup. + + If optional ENABLE is non-nil then the switch is on by default. + + SWITCH is inserted after all other switches already defined for + POPUP, unless optional PREPEND is non-nil, in which case it is + placed first. If optional AT is non-nil then it should be the KEY + of another switch already defined for POPUP, the argument is then + placed before or after AT, depending on PREPEND. + + -- Function: magit-define-popup-option popup key desc option &optional + reader value at prepend + + In POPUP, define KEY as OPTION. + + POPUP is a popup command defined using ‘magit-define-popup’. + OPTION is a string representing an argument that takes a value. + KEY is a character representing the second event in the sequence of + keystrokes used to set the argument’s value. (The first event, the + prefix, is shared among all options, defaults to ‘=’, and can be + changed in ‘magit-popup-mode-keymap’). + + DESC is a string describing the purpose of the argument, it is + displayed in the popup. + + If optional VALUE is non-nil then the option is on by default, and + VALUE is its default value. + + OPTION is inserted after all other options already defined for + POPUP, unless optional PREPEND is non-nil, in which case it is + placed first. If optional AT is non-nil then it should be the KEY + of another option already defined for POPUP, the argument is then + placed before or after AT, depending on PREPEND. + + -- Function: magit-define-popup-action popup key desc command &optional + at prepend + + In POPUP, define KEY as COMMAND. + + POPUP is a popup command defined using ‘magit-define-popup’. + COMMAND can be any command but should usually consume the popup + arguments in its ‘interactive’ form. KEY is a character + representing the event used invoke the action, i.e. to + interactively call the COMMAND. + + DESC is a string describing the purpose of the action, it is + displayed in the popup. + + COMMAND is inserted after all other commands already defined for + POPUP, unless optional PREPEND is non-nil, in which case it is + placed first. If optional AT is non-nil then it should be the KEY + of another command already defined for POPUP, the command is then + placed before or after AT, depending on PREPEND. + + -- Function: magit-define-popup-sequence-action popup key desc command + &optional at prepend + + Like ‘magit-define-popup-action’, but modifies the value of the + ‘:sequence-actions’ property instead of ‘:actions’. + + -- Function: magit-define-popup-variable popup key desc command + formatter &optional at prepend + + In POPUP, define KEY as COMMAND. + + POPUP is a popup command defined using ‘magit-define-popup’. + COMMAND is a command which calls ‘magit-popup-set-variable’. + FORMATTER is a function which calls ‘magit-popup-format-variable’. + These two functions have to be called with the same arguments. + + KEY is a character representing the event used interactively call + the COMMAND. + + DESC is the variable or a representation thereof. It’s not + actually used for anything. + + COMMAND is inserted after all other commands already defined for + POPUP, unless optional PREPEND is non-nil, in which case it is + placed first. If optional AT is non-nil then it should be the KEY + of another command already defined for POPUP, the command is then + placed before or after AT, depending on PREPEND." + + -- Function: magit-change-popup-key popup type from to + + In POPUP, bind TO to what FROM was bound to. TYPE is one of + ‘:action’, ‘:sequence-action’, ‘:switch’, or ‘:option’. Bind TO + and unbind FROM, both are characters. + + -- Function: magit-remove-popup-key popup type key + + In POPUP, remove KEY’s binding of TYPE. POPUP is a popup command + defined using ‘magit-define-popup’. TYPE is one of ‘:action’, + ‘:sequence-action’, ‘:switch’, or ‘:option’. KEY is the character + which is to be unbound. + + It is also possible to change other aspects of a popup by setting a +property using ‘plist-put’. See *note Defining prefix commands: +Defining prefix commands. for valid properties. The most likely change +Magit users might want to make is: + + (plist-put magit-show-refs-popup :use-prefix nil) + + +File: magit-popup.info, Node: Other options, Prev: Customizing existing popups, Up: Usage + +2.2 Other options +================= + + -- User Option: magit-popup-use-prefix-argument + + This option controls the effect that the use of a prefix argument + before entering a popup has. The *intended* default is ‘default’, + but the *actual* default is ‘disabled’. This is necessary because + the old popup implementation did simply forward such a pre-popup + prefix argument to the suffix command invoked from the popup, and + changing that without users being aware of it could lead to tears. + + • ‘disabled’ + + Bring up a Custom option buffer so that the user reads this + and then makes an informed choice. + + • ‘default’ + + With a prefix argument directly invoke the popup’s default + action (an Emacs command), instead of bringing up the popup. + + • ‘popup’ + + With a prefix argument bring up the popup, otherwise directly + invoke the popup’s default action. + + • ‘nil’ + + Ignore prefix arguments. + This option can be overridden for individual popups. + ‘magit-show-refs-popup’ for example defaults to invoking the + default action directly. It only shows the popup buffer when a + prefix argument is used. See *note Customizing existing popups: + Customizing existing popups. + + -- User Option: magit-popup-manpage-package + + The Emacs package used to display man-pages, one of ‘man’ or + ‘woman’. + + -- User Option: magit-popup-display-buffer-action + + The option controls how the window used to display a popup buffer + is created. Popup buffers are displayed using ‘display-buffer’ + with the value of this option as ACTION argument. You can also set + this to nil and instead add an entry to ‘display-buffer-alist’. + + To emphasize the default action by making it bold use this: + + (button-type-put 'magit-popup-action-button 'format " %k %D") + + +File: magit-popup.info, Node: Defining prefix and suffix commands, Prev: Usage, Up: Top + +3 Defining prefix and suffix commands +************************************* + +If you write an extension for Magit then you should use this library now +and later when ‘transient’ is released port to that. + + If you are considering using this library to define popups for +packages not related to Magit, then keep in mind that it will be +superseded eventually. Once ‘transient’ has been released I will only +fix bugs in ‘magit-popup’ but not implement any new features. + + Also consider using ‘hydra’ instead. To some extend ‘magit-popup’ +and ‘hydra’ are similar but have a different focus. The main purpose of +‘magit-popup’ is to pass infix arguments to suffix commands. If all you +need is a command dispatcher then you are better of using ‘hydra’. Of +course ‘hydra’ may also be a better fit not only because of the features +it lacks, but also because of the features it provides, which are in +turn missing from ‘magit-popup’. + + Here is an example of how one defines a prefix command along with its +infix arguments, and then also one of its suffix commands. + + ;;;###autoload (autoload 'magit-tag-popup "magit" nil t) + (magit-define-popup magit-tag-popup + "Show popup buffer featuring tagging commands." + 'magit-commands + :man-page "git-tag" + :switches '((?a "Annotate" "--annotate") + (?s "Sign" "--sign") + (?f "Force" "--force")) + :actions '((?t "Create" magit-tag) + (?k "Delete" magit-tag-delete) + (?p "Prune" magit-tag-prune)) + :default-action 'magit-tag) + + ;;;###autoload + (defun magit-tag (name rev &optional args) + "Create a new tag with the given NAME at REV." + (interactive (list (magit-read-tag "Tag name") + (magit-read-branch-or-commit "Place tag on") + (magit-tag-arguments))) + (magit-run-git-with-editor "tag" args name rev)) + +* Menu: + +* Defining prefix commands:: +* Defining suffix commands:: + + +File: magit-popup.info, Node: Defining prefix commands, Next: Defining suffix commands, Up: Defining prefix and suffix commands + +3.1 Defining prefix commands +============================ + +Prefix commands and their infix arguments are defined using the macro +‘magit-define-popup’. The key bindings and descriptions of suffix +commands are also defined using that macro, but the actual interactive +commands have to be defined separately using plain ‘defun’. + + -- Macro: magit-define-popup name doc [group [mode [option]]] :keyword + value… + + This macro defines a popup named NAME. The NAME should begin with + the package prefix and by convention end with ‘-popup’, it is used + as the name of the command which shows the popup and for an + internal variable (whose value is used to store information about + the popup and should not be accessed directly). DOC is the + doc-string of the popup command. + + This macro also defines an option and a function both named + ‘SHORTNAME-arguments’, where SHORTNAME is NAME with the trailing + ‘-popup’ removed. The name of this option and this function can be + overwritten using the optional argument OPTION, but that is rarely + advisable. As a special case if OPTION is specified but ‘nil’, + then this option and this function are not defined at all, which is + useful for popups that are used as simple dispatchers that offer no + arguments. + + The option ‘SHORTNAME-arguments’ holds the value for the popup + arguments. It can be customized from within the popup or using the + Custom interface. It can also have a buffer local value in any + non-popup buffer. The local value for the buffer from which the + popup command was invoked, can be set from within the popup buffer. + + The function ‘SHORTNAME-arguments’ returns the currently effective + value of the variable by the same name. See below for more + information. + + The optional argument GROUP specifies the Custom group in which the + option is placed. If omitted then the option is placed in some + group the same way it is done when directly using ‘defcustom’ and + omitting the group. + + The optional argument MODE specifies the mode used by the popup + buffer. If it is omitted or ‘nil’ then ‘magit-popup-mode’ is used. + + The remaining arguments should have the form ‘[KEYWORD VALUE]...’. + + The following keywords are meaningful (and by convention are + usually specified in that order): + + • ‘:actions’ + + The actions which can be invoked from the popup. VALUE is a + list whose members have the form (KEY DESC COMMAND), see + ‘magit-define-popup-action’ for details. + + How the actions are split into rows and columns currently + depends on the available space and ‘:max-action-columns’. + + WARNING: This will likely be change to use a more explicit + format (((KEY DESC COMMAND)…)…) before the release. + + Actions are regular Emacs commands, which usually have an + ‘interactive’ form setup to consume the values of the popup + ‘:switches’ and ‘:options’ when invoked from the corresponding + popup, else when invoked as the default action or directly + without using the popup, the default value of the variable + ‘SHORTNAME-arguments’. This is usually done by calling the + function ‘SHORTNAME-arguments’. + + Members of VALUE may also be strings, assuming the first + member is also a string. Instead of just one action section + with the heading \"Actions\", multiple sections are then + inserted into the popup buffer, using these strings as + headings. + + Members of VALUE may also be nil. This should only be used + together with ‘:max-action-columns’ and allows having gaps in + the action grit, which can help arranging actions sensibly. + + • ‘:default-action’ + + The default action of the popup which is used directly instead + of displaying the popup buffer, when the popup is invoked with + a prefix argument. Also see ‘magit-popup-use-prefix-argument’ + and ‘:use-prefix’, which can be used to inverse the meaning of + the prefix argument. + + • ‘:use-prefix’ + + Controls when to display the popup buffer and when to invoke + the default action (if any) directly. This overrides the + global default set using ‘magit-popup-use-prefix-argument’. + The value, if specified, should be one of ‘default’ or + ‘prefix’. + + • ‘:switches’ + + The popup arguments which can be toggled on and off. VALUE is + a list whose members have the form ‘(KEY DESC SWITCH)’, see + ‘magit-define-popup-switch’ for details. + + • ‘:options’ + + The popup arguments which take a value, as in "–opt~OPTVAL". + VALUE is a list whose members have the form (KEY DESC OPTION + READER), see ‘magit-define-popup-option’ for details. + + • ‘:variables’ + + Git variables which can be set from the popup. VALUE is a + list whose members have the form (KEY DESC COMMAND FORMATTER), + see ‘magit-define-popup-variable’ for details. + + • ‘:default-arguments’ + + The default arguments, a list of switches (which are then + enabled by default) and options with there default values, as + in "–OPT~OPTVAL\". + + • ‘:sequence-predicate’ + + When this function returns non-nil, then the popup uses + ‘:sequence-actions’ instead of ‘:actions’, and does not show + the ‘:switches’ and ‘:options’. + + • ‘:sequence-actions’ + + The actions which can be invoked from the popup, when + ‘:sequence-predicate’ returns non-nil. + + • ‘:setup-function’ + + When this function is specified, then it is used instead of + ‘magit-popup-default-setup’. + + • ‘:refresh-function’ + + When this function is specified, then it is used instead of + calling ‘magit-popup-insert-section’ three times with symbols + ‘magit-popup-switch-button’, ‘magit-popup-option-button’, and + finally ‘magit-popup-action-button’ as argument. + + • ‘:man-page’ + + The name of the manpage to be displayed when the user requests + help for an argument. + + +File: magit-popup.info, Node: Defining suffix commands, Prev: Defining prefix commands, Up: Defining prefix and suffix commands + +3.2 Defining suffix commands +============================ + +Commands intended to be invoked from a particular popup should determine +the currently effective arguments by calling the function +‘SHORTNAME-arguments’ inside their ‘interactive’ form. This function is +created by the ‘magit-define-popup’ macro. For a popup named +‘prefix-foo-popup’ the name of this function is ‘prefix-foo-arguments’. + + When the command was invoked as an action in the respective popup, +then this function returns the arguments that were set in the popup. +Otherwise when the command was invoked as the default of the popup (by +calling the popup command with a prefix argument), or without using the +popup command at all, then this function returns the buffer-local or +global value of the variable ‘SHORTNAME-arguments’. + + Internally arguments are handled as a list of strings. This might +not be appropriate for the intended use inside commands, or it might be +necessary to manipulate that list somehow, i.e. to split "–ARG=VAL" +into "–ARG""VAL". This should be done by advising or redefining the +function ‘SHORTNAME-arguments’. + + Internally ‘SHORNAME-arguments’ used following variables and +function. Except when redefining the former, you should not use these +directly. + + -- Variable: magit-current-popup + + The popup from which this editing command was invoked. + + -- Variable: magit-current-popup-args + + The value of the popup arguments for this editing command. + + If the current command was invoked from a popup, then this is a + list of strings of all the set switches and options. This includes + arguments which are set by default not only those explicitly set + during this invocation. + + When the value is nil, then that can be because no argument is set, + or because the current command wasn’t invoked from a popup at all. + + -- Function: magit-current-popup-args &rest args + + This function returns the value of the popup arguments for this + editing command. The value is the same as that of the variable by + the same name, except that FILTER is applied. FILTER is a list of + regexps; only arguments that match one of them are returned. The + first element of FILTER may also be ‘:not’ in which case only + arguments that don’t match any of the regexps are returned, or + ‘:only’ which doesn’t change the behavior. + + + +Tag Table: +Node: Top994 +Node: Introduction2168 +Node: Usage4745 +Node: Customizing existing popups9402 +Node: Other options14930 +Node: Defining prefix and suffix commands16979 +Node: Defining prefix commands19075 +Node: Defining suffix commands25755 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/magit-rockstar-readme.txt b/elpa/magit-rockstar-readme.txt new file mode 100644 index 0000000..c23787f --- /dev/null +++ b/elpa/magit-rockstar-readme.txt @@ -0,0 +1,34 @@ +This package provides two commands which manipulate author and +committer dates. You could use it to make yourself look like +a rockstar programmer who hammers out commits at one commit per +minute. But the real purpose is to recover from heavy +re-arrangements of commits, that have causes the existing author +and committer dates to become meaningless. + +I add these commands to the appropriate popups like this: + + (magit-define-popup-action 'magit-rebase-popup + ?R "Rockstar" 'magit-rockstar) + + (magit-define-popup-action 'magit-commit-popup + ?n "Reshelve" 'magit-reshelve) + +Also included are tools that are either only useful for people +working on Magit itself and/or that aren't ready to be added to +Magit yet. These tools might change at any time, without prior +notice or way to appeal. This is a staging ground. It's okay +if things ain't perfect, or if they only do what *I currently* +need but not what you (or I) think they should (eventually) be +doing instead. + +Currently my init file also contains this: + + (magit-define-popup-action 'magit-fetch-popup + ?P "Pull request" 'magit-branch-pull-request) + +To use the "anti-stage" feature add this: + + (setq magit-unstage-use-anti-stage t) + + (magit-define-popup-action 'magit-revert-popup + ?e "Revert & edit HEAD" 'magit-uncommit-extend) diff --git a/elpa/magit-topgit-readme.txt b/elpa/magit-topgit-readme.txt new file mode 100644 index 0000000..d7bed03 --- /dev/null +++ b/elpa/magit-topgit-readme.txt @@ -0,0 +1,24 @@ +This package provides very basic support for TopGit. + + TopGit is a patch queue manager that aims to make handling + of large amounts of interdependent topic branches easier. + +For information about TopGit see https://github.com/greenrd/topgit. + +When `magit-topgit-mode' is turned on then the list of TopGit +topics is displayed in the status buffer. While point is on such +a topic it can checked out using `RET' and discarded using `k'. +Other TopGit commands are available from the TopGit popup on `T'. + +To enable the mode in a particular repository use: + + cd /path/to/repository + git config --add magit.extension topgit + +To enable the mode for all repositories use: + + git config --global --add magit.extension topgit + +To enable the mode globally without dropping to a shell: + + (add-hook 'magit-mode-hook 'magit-topgit-mode) diff --git a/elpa/pcache-20151109.639/pcache-autoloads.el b/elpa/pcache-20151109.639/pcache-autoloads.el new file mode 100644 index 0000000..5adeb47 --- /dev/null +++ b/elpa/pcache-20151109.639/pcache-autoloads.el @@ -0,0 +1,15 @@ +;;; pcache-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil nil ("pcache.el") (22221 60696 480763 960000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; pcache-autoloads.el ends here diff --git a/elpa/pcache-20151109.639/pcache-pkg.el b/elpa/pcache-20151109.639/pcache-pkg.el new file mode 100644 index 0000000..edc6507 --- /dev/null +++ b/elpa/pcache-20151109.639/pcache-pkg.el @@ -0,0 +1 @@ +(define-package "pcache" "20151109.639" "persistent caching for Emacs." '((eieio "1.3"))) diff --git a/elpa/pcache-20151109.639/pcache.el b/elpa/pcache-20151109.639/pcache.el new file mode 100644 index 0000000..e853502 --- /dev/null +++ b/elpa/pcache-20151109.639/pcache.el @@ -0,0 +1,226 @@ +;;; pcache.el --- persistent caching for Emacs. + +;; Copyright (C) 2011 Yann Hodique + +;; Author: Yann Hodique +;; Keywords: +;; Package-Version: 20151109.639 +;; Version: 0.3.2 +;; Package-Requires: ((eieio "1.3")) + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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 GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; pcache provides a persistent way of caching data, in a hashtable-like +;; structure. It relies on `eieio-persistent' in the backend, so that any +;; object that can be serialized by EIEIO can be stored with pcache. + +;; pcache handles objects called "repositories" (`pcache-repository') and +;; "entries" (`pcache-entry'). Each repository is identified by a unique name, +;; that defines an entry in `pcache-directory'. Subdirectories are allowed, by +;; the use of a directory separator in the repository name. + +;; Example: +;; (let ((repo (pcache-repository "plop"))) +;; (pcache-put repo 'foo 42) ; store value 42 with key 'foo +;; (pcache-get repo 'foo) ; => 42 +;; ) + +;; Keys can be pretty much any Lisp object, and are compared for equality using +;; `eql' + +;; Optionally, cache entries can expire: +;; (let ((repo (pcache-repository "plop"))) +;; (pcache-put repo 'foo 42 1) ; store value 42 with key 'foo for 1 second +;; (sleep-for 1) +;; (pcache-get repo 'foo) ; => nil +;; ) + +;;; Code: + +(eval-when-compile + (require 'cl)) + +(require 'eieio) +(require 'eieio-base) + +(defvar pcache-directory + (let ((dir (concat user-emacs-directory "var/pcache/"))) + (make-directory dir t) + dir)) + +(defvar *pcache-repositories* (make-hash-table :test 'equal)) + +(defconst pcache-default-save-delay 300) + +(defconst pcache-version-constant "0.3") + +(defclass pcache-repository (eieio-persistent eieio-named) + ((version :initarg :version :initform nil) + (version-constant :allocation :class) + (entries :initarg :entries :initform (make-hash-table)) + (entry-cls :initarg :entry-cls :initform pcache-entry) + (timestamp :initarg :timestamp :initform (float-time (current-time))) + (save-delay :initarg :save-delay))) + +(oset-default 'pcache-repository :save-delay pcache-default-save-delay) +(oset-default 'pcache-repository version-constant pcache-version-constant) + +(defvar *pcache-repository-name* nil) + +(defmethod constructor :static ((cache pcache-repository) &rest args) + (let* ((newname (or (and (stringp (car args)) (car args)) + (plist-get args :object-name) + *pcache-repository-name* + (symbol-name cache))) + (e (gethash newname *pcache-repositories*)) + (path (concat pcache-directory newname))) + (setq args (append args (list :object-name newname))) + (or e + (and (not (boundp 'pcache-avoid-recursion)) + (file-exists-p path) + (condition-case nil + (let* ((pcache-avoid-recursion t) + (*pcache-repository-name* newname) + (obj (eieio-persistent-read path 'pcache-repository t))) + (and (or (equal (oref obj :version) + (oref-default (object-class obj) version-constant)) + (error "wrong version")) + (puthash newname obj *pcache-repositories*) + obj)) + (error nil))) + (let ((obj (call-next-method)) + (dir (file-name-directory path))) + (unless (file-exists-p dir) + (make-directory dir t)) + (oset obj :file path) + (puthash newname obj *pcache-repositories*) + obj)))) + +(defclass pcache-entry () + ((timestamp :initarg :timestamp + :initform (float-time (current-time))) + (ttl :initarg :ttl :initform nil) + (value :initarg :value :initform nil))) + +(defmethod pcache-entry-valid-p ((entry pcache-entry)) + (let ((ttl (oref entry :ttl))) + (or (null ttl) + (let ((time (float-time (current-time)))) + (< time (+ ttl (oref entry :timestamp))))))) + +(defmethod pcache-get ((cache pcache-repository) key &optional default) + (let* ((table (oref cache :entries)) + (entry (gethash key table))) + (if entry + (if (pcache-entry-valid-p entry) + (oref entry :value) + (remhash key table) + default) + default))) + +(defmethod pcache-has ((cache pcache-repository) key) + (let* ((default (make-symbol ":nil")) + (table (oref cache :entries)) + (entry (gethash key table default))) + (if (eq entry default) nil + (if (pcache-entry-valid-p entry) + t nil)))) + +(defmethod pcache-put ((cache pcache-repository) key value &optional ttl) + (let ((table (oref cache :entries)) + (entry (or (and (eieio-object-p value) + (object-of-class-p value 'pcache-entry) + value) + (make-instance (oref cache :entry-cls) :value value)))) + (when ttl + (oset entry :ttl ttl)) + (prog1 + (puthash key entry table) + (pcache-save cache)))) + +(defmethod pcache-invalidate ((cache pcache-repository) key) + (let ((table (oref cache :entries))) + (remhash key table) + (pcache-save cache))) + +(defmethod pcache-clear ((cache pcache-repository)) + (let* ((entries (oref cache :entries)) + (test (hash-table-test entries)) + (resize (hash-table-rehash-size entries)) + (threshold (hash-table-rehash-threshold entries)) + (weakness (hash-table-weakness entries))) + (oset cache :entries (make-hash-table :test test :rehash-size resize + :rehash-threshold threshold + :weakness weakness))) + (pcache-save cache)) + +(defmethod pcache-purge-invalid ((cache pcache-repository)) + (let ((table (oref cache :entries))) + (maphash #'(lambda (k e) + (unless (pcache-entry-valid-p e) + (remhash k table))) + table) + (pcache-save cache))) + +(defmethod pcache-save ((cache pcache-repository) &optional force) + (let ((timestamp (oref cache :timestamp)) + (delay (oref cache :save-delay)) + (time (float-time (current-time)))) + (when (or force (> time (+ timestamp delay))) + (oset cache :timestamp time) + ;; make sure version is saved to file + (oset cache :version (oref-default (object-class cache) version-constant)) + (eieio-persistent-save cache)))) + +(defmethod pcache-map ((cache pcache-repository) func) + (let ((table (oref cache :entries))) + (maphash func table))) + +(defun pcache-kill-emacs-hook () + (maphash #'(lambda (k v) + (condition-case nil + (pcache-purge-invalid v) + (error nil)) + (condition-case nil + (pcache-save v t) + (error nil))) + *pcache-repositories*)) + +(defun pcache-destroy-repository (name) + (remhash name *pcache-repositories*) + (let ((fname (concat pcache-directory name))) + (when (file-exists-p fname) + (delete-file fname)))) + +(add-hook 'kill-emacs-hook 'pcache-kill-emacs-hook) + +;; in case we reload in place, clean all repositories with invalid version +(let (to-clean) + (maphash #'(lambda (k v) + (condition-case nil + (unless (eql (oref v :version) + pcache-version-constant) + (signal 'error nil)) + (error + (setq to-clean (cons k to-clean))))) + *pcache-repositories*) + (dolist (k to-clean) + (remhash k *pcache-repositories*))) + +(provide 'pcache) +;;; pcache.el ends here diff --git a/elpa/s-20160115.58/s-autoloads.el b/elpa/s-20160115.58/s-autoloads.el new file mode 100644 index 0000000..e4422b6 --- /dev/null +++ b/elpa/s-20160115.58/s-autoloads.el @@ -0,0 +1,15 @@ +;;; s-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil nil ("s.el") (22221 60695 988837 699000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; s-autoloads.el ends here diff --git a/elpa/s-20160115.58/s-pkg.el b/elpa/s-20160115.58/s-pkg.el new file mode 100644 index 0000000..f8c3991 --- /dev/null +++ b/elpa/s-20160115.58/s-pkg.el @@ -0,0 +1 @@ +(define-package "s" "20160115.58" "The long lost Emacs string manipulation library." 'nil :keywords '("strings")) diff --git a/elpa/s-20160115.58/s.el b/elpa/s-20160115.58/s.el new file mode 100644 index 0000000..8652ad1 --- /dev/null +++ b/elpa/s-20160115.58/s.el @@ -0,0 +1,618 @@ +;;; s.el --- The long lost Emacs string manipulation library. + +;; Copyright (C) 2012-2015 Magnar Sveen + +;; Author: Magnar Sveen +;; Version: 1.10.0 +;; Package-Version: 20160115.58 +;; Keywords: strings + +;; This program 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 of the License, or +;; (at your option) any later version. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; The long lost Emacs string manipulation library. +;; +;; See documentation on https://github.com/magnars/s.el#functions + +;;; Code: + +(require 'ucs-normalize) + +(defun s-trim-left (s) + "Remove whitespace at the beginning of S." + (if (string-match "\\`[ \t\n\r]+" s) + (replace-match "" t t s) + s)) + +(defun s-trim-right (s) + "Remove whitespace at the end of S." + (if (string-match "[ \t\n\r]+\\'" s) + (replace-match "" t t s) + s)) + +(defun s-trim (s) + "Remove whitespace at the beginning and end of S." + (s-trim-left (s-trim-right s))) + +(defun s-collapse-whitespace (s) + "Convert all adjacent whitespace characters to a single space." + (replace-regexp-in-string "[ \t\n\r]+" " " s)) + +(defun s-split (separator s &optional omit-nulls) + "Split S into substrings bounded by matches for regexp SEPARATOR. +If OMIT-NULLS is non-nil, zero-length substrings are omitted. + +This is a simple wrapper around the built-in `split-string'." + (split-string s separator omit-nulls)) + +(defun s-split-up-to (separator s n &optional omit-nulls) + "Split S up to N times into substrings bounded by matches for regexp SEPARATOR. + +If OMIT-NULLS is non-nil, zero-length substrings are omitted. + +See also `s-split'." + (save-match-data + (let ((op 0) + (r nil)) + (with-temp-buffer + (insert s) + (setq op (goto-char (point-min))) + (while (and (re-search-forward separator nil t) + (< 0 n)) + (let ((sub (buffer-substring-no-properties op (match-beginning 0)))) + (unless (and omit-nulls + (equal sub "")) + (push sub r))) + (setq op (goto-char (match-end 0))) + (setq n (1- n))) + (let ((sub (buffer-substring-no-properties op (point-max)))) + (unless (and omit-nulls + (equal sub "")) + (push sub r)))) + (nreverse r)))) + +(defun s-lines (s) + "Splits S into a list of strings on newline characters." + (s-split "\\(\r\n\\|[\n\r]\\)" s)) + +(defun s-join (separator strings) + "Join all the strings in STRINGS with SEPARATOR in between." + (mapconcat 'identity strings separator)) + +(defun s-concat (&rest strings) + "Join all the string arguments into one string." + (apply 'concat strings)) + +(defun s-prepend (prefix s) + "Concatenate PREFIX and S." + (concat prefix s)) + +(defun s-append (suffix s) + "Concatenate S and SUFFIX." + (concat s suffix)) + +(defun s-repeat (num s) + "Make a string of S repeated NUM times." + (let (ss) + (while (> num 0) + (setq ss (cons s ss)) + (setq num (1- num))) + (apply 'concat ss))) + +(defun s-chop-suffix (suffix s) + "Remove SUFFIX if it is at end of S." + (let ((pos (- (length suffix)))) + (if (and (>= (length s) (length suffix)) + (string= suffix (substring s pos))) + (substring s 0 pos) + s))) + +(defun s-chop-suffixes (suffixes s) + "Remove SUFFIXES one by one in order, if they are at the end of S." + (while suffixes + (setq s (s-chop-suffix (car suffixes) s)) + (setq suffixes (cdr suffixes))) + s) + +(defun s-chop-prefix (prefix s) + "Remove PREFIX if it is at the start of S." + (let ((pos (length prefix))) + (if (and (>= (length s) (length prefix)) + (string= prefix (substring s 0 pos))) + (substring s pos) + s))) + +(defun s-chop-prefixes (prefixes s) + "Remove PREFIXES one by one in order, if they are at the start of S." + (while prefixes + (setq s (s-chop-prefix (car prefixes) s)) + (setq prefixes (cdr prefixes))) + s) + +(defun s-shared-start (s1 s2) + "Returns the longest prefix S1 and S2 have in common." + (let ((search-length (min (length s1) (length s2))) + (i 0)) + (while (and (< i search-length) + (= (aref s1 i) (aref s2 i))) + (setq i (1+ i))) + (substring s1 0 i))) + +(defun s-shared-end (s1 s2) + "Returns the longest suffix S1 and S2 have in common." + (let* ((l1 (length s1)) + (l2 (length s2)) + (search-length (min l1 l2)) + (i 0)) + (while (and (< i search-length) + (= (aref s1 (- l1 i 1)) (aref s2 (- l2 i 1)))) + (setq i (1+ i))) + ;; If I is 0, then it means that there's no common suffix between + ;; S1 and S2. + ;; + ;; However, since (substring s (- 0)) will return the whole + ;; string, `s-shared-end' should simply return the empty string + ;; when I is 0. + (if (zerop i) + "" + (substring s1 (- i))))) + +(defun s-chomp (s) + "Remove one trailing `\\n`, `\\r` or `\\r\\n` from S." + (s-chop-suffixes '("\n" "\r") s)) + +(defun s-truncate (len s) + "If S is longer than LEN, cut it down to LEN - 3 and add ... at the end." + (if (> (length s) len) + (format "%s..." (substring s 0 (- len 3))) + s)) + +(defun s-word-wrap (len s) + "If S is longer than LEN, wrap the words with newlines." + (with-temp-buffer + (insert s) + (let ((fill-column len)) + (fill-region (point-min) (point-max))) + (buffer-substring-no-properties (point-min) (point-max)))) + +(defun s-center (len s) + "If S is shorter than LEN, pad it with spaces so it is centered." + (let ((extra (max 0 (- len (length s))))) + (concat + (make-string (ceiling extra 2) ? ) + s + (make-string (floor extra 2) ? )))) + +(defun s-pad-left (len padding s) + "If S is shorter than LEN, pad it with PADDING on the left." + (let ((extra (max 0 (- len (length s))))) + (concat (make-string extra (string-to-char padding)) + s))) + +(defun s-pad-right (len padding s) + "If S is shorter than LEN, pad it with PADDING on the right." + (let ((extra (max 0 (- len (length s))))) + (concat s + (make-string extra (string-to-char padding))))) + +(defun s-left (len s) + "Returns up to the LEN first chars of S." + (if (> (length s) len) + (substring s 0 len) + s)) + +(defun s-right (len s) + "Returns up to the LEN last chars of S." + (let ((l (length s))) + (if (> l len) + (substring s (- l len) l) + s))) + +(defun s-ends-with? (suffix s &optional ignore-case) + "Does S end with SUFFIX? + +If IGNORE-CASE is non-nil, the comparison is done without paying +attention to case differences. + +Alias: `s-suffix?'" + (let ((start-pos (- (length s) (length suffix)))) + (and (>= start-pos 0) + (eq t (compare-strings suffix nil nil + s start-pos nil ignore-case))))) + +(defalias 's-ends-with-p 's-ends-with?) + +(defun s-starts-with? (prefix s &optional ignore-case) + "Does S start with PREFIX? + +If IGNORE-CASE is non-nil, the comparison is done without paying +attention to case differences. + +Alias: `s-prefix?'. This is a simple wrapper around the built-in +`string-prefix-p'." + (string-prefix-p prefix s ignore-case)) + +(defalias 's-starts-with-p 's-starts-with?) + +(defalias 's-suffix? 's-ends-with?) +(defalias 's-prefix? 's-starts-with?) +(defalias 's-suffix-p 's-ends-with?) +(defalias 's-prefix-p 's-starts-with?) + +(defun s--truthy? (val) + (not (null val))) + +(defun s-contains? (needle s &optional ignore-case) + "Does S contain NEEDLE? + +If IGNORE-CASE is non-nil, the comparison is done without paying +attention to case differences." + (let ((case-fold-search ignore-case)) + (s--truthy? (string-match-p (regexp-quote needle) s)))) + +(defalias 's-contains-p 's-contains?) + +(defun s-equals? (s1 s2) + "Is S1 equal to S2? + +This is a simple wrapper around the built-in `string-equal'." + (string-equal s1 s2)) + +(defalias 's-equals-p 's-equals?) + +(defun s-less? (s1 s2) + "Is S1 less than S2? + +This is a simple wrapper around the built-in `string-lessp'." + (string-lessp s1 s2)) + +(defalias 's-less-p 's-less?) + +(defun s-matches? (regexp s &optional start) + "Does REGEXP match S? +If START is non-nil the search starts at that index. + +This is a simple wrapper around the built-in `string-match-p'." + (s--truthy? (string-match-p regexp s start))) + +(defalias 's-matches-p 's-matches?) + +(defun s-blank? (s) + "Is S nil or the empty string?" + (or (null s) (string= "" s))) + +(defun s-present? (s) + "Is S anything but nil or the empty string?" + (not (s-blank? s))) + +(defun s-presence (s) + "Return S if it's `s-present?', otherwise return nil." + (and (s-present? s) s)) + +(defun s-lowercase? (s) + "Are all the letters in S in lower case?" + (let ((case-fold-search nil)) + (not (string-match-p "[[:upper:]]" s)))) + +(defun s-uppercase? (s) + "Are all the letters in S in upper case?" + (let ((case-fold-search nil)) + (not (string-match-p "[[:lower:]]" s)))) + +(defun s-mixedcase? (s) + "Are there both lower case and upper case letters in S?" + (let ((case-fold-search nil)) + (s--truthy? + (and (string-match-p "[[:lower:]]" s) + (string-match-p "[[:upper:]]" s))))) + +(defun s-capitalized? (s) + "In S, is the first letter upper case, and all other letters lower case?" + (let ((case-fold-search nil)) + (s--truthy? + (string-match-p "^[[:upper:]][^[:upper:]]*$" s)))) + +(defun s-numeric? (s) + "Is S a number?" + (s--truthy? + (string-match-p "^[0-9]+$" s))) + +(defun s-replace (old new s) + "Replaces OLD with NEW in S." + (replace-regexp-in-string (regexp-quote old) new s t t)) + +(defun s--aget (alist key) + (cdr (assoc key alist))) + +(defun s-replace-all (replacements s) + "REPLACEMENTS is a list of cons-cells. Each `car` is replaced with `cdr` in S." + (replace-regexp-in-string (regexp-opt (mapcar 'car replacements)) + (lambda (it) (s--aget replacements it)) + s)) + +(defun s-downcase (s) + "Convert S to lower case. + +This is a simple wrapper around the built-in `downcase'." + (downcase s)) + +(defun s-upcase (s) + "Convert S to upper case. + +This is a simple wrapper around the built-in `upcase'." + (upcase s)) + +(defun s-capitalize (s) + "Convert the first word's first character to upper case and the rest to lower case in S." + (concat (upcase (substring s 0 1)) (downcase (substring s 1)))) + +(defun s-titleize (s) + "Convert each word's first character to upper case and the rest to lower case in S. + +This is a simple wrapper around the built-in `capitalize'." + (capitalize s)) + +(defmacro s-with (s form &rest more) + "Threads S through the forms. Inserts S as the last item +in the first form, making a list of it if it is not a list +already. If there are more forms, inserts the first form as the +last item in second form, etc." + (declare (debug (form &rest [&or (function &rest form) fboundp]))) + (if (null more) + (if (listp form) + `(,(car form) ,@(cdr form) ,s) + (list form s)) + `(s-with (s-with ,s ,form) ,@more))) + +(put 's-with 'lisp-indent-function 1) + +(defun s-index-of (needle s &optional ignore-case) + "Returns first index of NEEDLE in S, or nil. + +If IGNORE-CASE is non-nil, the comparison is done without paying +attention to case differences." + (let ((case-fold-search ignore-case)) + (string-match-p (regexp-quote needle) s))) + +(defun s-reverse (s) + "Return the reverse of S." + (if (multibyte-string-p s) + (let ((input (string-to-list s)) + (output ())) + (while input + ;; Handle entire grapheme cluster as a single unit + (let ((grapheme (list (pop input)))) + (while (memql (car input) ucs-normalize-combining-chars) + (push (pop input) grapheme)) + (setq output (nconc (nreverse grapheme) output)))) + (concat output)) + (concat (nreverse (string-to-list s))))) + +(defun s-match-strings-all (regex string) + "Return a list of matches for REGEX in STRING. + +Each element itself is a list of matches, as per +`match-string'. Multiple matches at the same position will be +ignored after the first." + (let ((all-strings ()) + (i 0)) + (while (and (< i (length string)) + (string-match regex string i)) + (setq i (1+ (match-beginning 0))) + (let (strings + (num-matches (/ (length (match-data)) 2)) + (match 0)) + (while (/= match num-matches) + (push (match-string match string) strings) + (setq match (1+ match))) + (push (nreverse strings) all-strings))) + (nreverse all-strings))) + +(defun s-matched-positions-all (regexp string &optional subexp-depth) + "Return a list of matched positions for REGEXP in STRING. +SUBEXP-DEPTH is 0 by default." + (if (null subexp-depth) + (setq subexp-depth 0)) + (let ((pos 0) result) + (while (and (string-match regexp string pos) + (< pos (length string))) + (let ((m (match-end subexp-depth))) + (push (cons (match-beginning subexp-depth) (match-end subexp-depth)) result) + (setq pos m))) + (nreverse result))) + +(defun s-match (regexp s &optional start) + "When the given expression matches the string, this function returns a list +of the whole matching string and a string for each matched subexpressions. +If it did not match the returned value is an empty list (nil). + +When START is non-nil the search will start at that index." + (save-match-data + (if (string-match regexp s start) + (let ((match-data-list (match-data)) + result) + (while match-data-list + (let* ((beg (car match-data-list)) + (end (cadr match-data-list)) + (subs (if (and beg end) (substring s beg end) nil))) + (setq result (cons subs result)) + (setq match-data-list + (cddr match-data-list)))) + (nreverse result))))) + +(defun s-slice-at (regexp s) + "Slices S up at every index matching REGEXP." + (save-match-data + (let (i) + (setq i (string-match regexp s 1)) + (if i + (cons (substring s 0 i) + (s-slice-at regexp (substring s i))) + (list s))))) + +(defun s-split-words (s) + "Split S into list of words." + (s-split + "[^[:word:]0-9]+" + (let ((case-fold-search nil)) + (replace-regexp-in-string + "\\([[:lower:]]\\)\\([[:upper:]]\\)" "\\1 \\2" + (replace-regexp-in-string "\\([[:upper:]]\\)\\([[:upper:]][0-9[:lower:]]\\)" "\\1 \\2" s))) + t)) + +(defun s--mapcar-head (fn-head fn-rest list) + "Like MAPCAR, but applies a different function to the first element." + (if list + (cons (funcall fn-head (car list)) (mapcar fn-rest (cdr list))))) + +(defun s-lower-camel-case (s) + "Convert S to lowerCamelCase." + (s-join "" (s--mapcar-head 'downcase 'capitalize (s-split-words s)))) + +(defun s-upper-camel-case (s) + "Convert S to UpperCamelCase." + (s-join "" (mapcar 'capitalize (s-split-words s)))) + +(defun s-snake-case (s) + "Convert S to snake_case." + (s-join "_" (mapcar 'downcase (s-split-words s)))) + +(defun s-dashed-words (s) + "Convert S to dashed-words." + (s-join "-" (mapcar 'downcase (s-split-words s)))) + +(defun s-capitalized-words (s) + "Convert S to Capitalized words." + (let ((words (s-split-words s))) + (s-join " " (cons (capitalize (car words)) (mapcar 'downcase (cdr words)))))) + +(defun s-titleized-words (s) + "Convert S to Titleized Words." + (s-join " " (mapcar 's-titleize (s-split-words s)))) + +(defun s-word-initials (s) + "Convert S to its initials." + (s-join "" (mapcar (lambda (ss) (substring ss 0 1)) + (s-split-words s)))) + +;; Errors for s-format +(progn + (put 's-format-resolve + 'error-conditions + '(error s-format s-format-resolve)) + (put 's-format-resolve + 'error-message + "Cannot resolve a template to values")) + +(defun s-format (template replacer &optional extra) + "Format TEMPLATE with the function REPLACER. + +REPLACER takes an argument of the format variable and optionally +an extra argument which is the EXTRA value from the call to +`s-format'. + +Several standard `s-format' helper functions are recognized and +adapted for this: + + (s-format \"${name}\" 'gethash hash-table) + (s-format \"${name}\" 'aget alist) + (s-format \"$0\" 'elt sequence) + +The REPLACER function may be used to do any other kind of +transformation." + (let ((saved-match-data (match-data))) + (unwind-protect + (replace-regexp-in-string + "\\$\\({\\([^}]+\\)}\\|[0-9]+\\)" + (lambda (md) + (let ((var + (let ((m (match-string 2 md))) + (if m m + (string-to-number (match-string 1 md))))) + (replacer-match-data (match-data))) + (unwind-protect + (let ((v + (cond + ((eq replacer 'gethash) + (funcall replacer var extra)) + ((eq replacer 'aget) + (funcall 's--aget extra var)) + ((eq replacer 'elt) + (funcall replacer extra var)) + (t + (set-match-data saved-match-data) + (if extra + (funcall replacer var extra) + (funcall replacer var)))))) + (if v v (signal 's-format-resolve md))) + (set-match-data replacer-match-data)))) template + ;; Need literal to make sure it works + t t) + (set-match-data saved-match-data)))) + +(defvar s-lex-value-as-lisp nil + "If `t' interpolate lisp values as lisp. + +`s-lex-format' inserts values with (format \"%S\").") + +(defun s-lex-fmt|expand (fmt) + "Expand FMT into lisp." + (list 's-format fmt (quote 'aget) + (append '(list) + (mapcar + (lambda (matches) + (list + 'cons + (cadr matches) + `(format + (if s-lex-value-as-lisp "%S" "%s") + ,(intern (cadr matches))))) + (s-match-strings-all "${\\([^}]+\\)}" fmt))))) + +(defmacro s-lex-format (format-str) + "`s-format` with the current environment. + +FORMAT-STR may use the `s-format' variable reference to refer to +any variable: + + (let ((x 1)) + (s-lex-format \"x is: ${x}\")) + +The values of the variables are interpolated with \"%s\" unless +the variable `s-lex-value-as-lisp' is `t' and then they are +interpolated with \"%S\"." + (declare (debug (form))) + (s-lex-fmt|expand format-str)) + +(defun s-count-matches (regexp s &optional start end) + "Count occurrences of `regexp' in `s'. + +`start', inclusive, and `end', exclusive, delimit the part of `s' +to match. " + (with-temp-buffer + (insert s) + (goto-char (point-min)) + (count-matches regexp (or start 1) (or end (point-max))))) + +(defun s-wrap (s prefix &optional suffix) + "Wrap string S with PREFIX and optionally SUFFIX. + +Return string S with PREFIX prepended. If SUFFIX is present, it +is appended, otherwise PREFIX is used as both prefix and +suffix." + (concat prefix s (or suffix prefix))) + +(provide 's) +;;; s.el ends here diff --git a/elpa/with-editor-20160223.1155/dir b/elpa/with-editor-20160223.1155/dir new file mode 100644 index 0000000..93a7ef7 --- /dev/null +++ b/elpa/with-editor-20160223.1155/dir @@ -0,0 +1,18 @@ +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" 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 +* With-Editor: (with-editor). Using the Emacsclient as $EDITOR. diff --git a/elpa/with-editor-20160223.1155/with-editor-autoloads.el b/elpa/with-editor-20160223.1155/with-editor-autoloads.el new file mode 100644 index 0000000..19b1f33 --- /dev/null +++ b/elpa/with-editor-20160223.1155/with-editor-autoloads.el @@ -0,0 +1,16 @@ +;;; with-editor-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil nil ("with-editor-pkg.el" "with-editor.el") +;;;;;; (22221 60698 212139 491000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; with-editor-autoloads.el ends here diff --git a/elpa/with-editor-20160223.1155/with-editor-pkg.el b/elpa/with-editor-20160223.1155/with-editor-pkg.el new file mode 100644 index 0000000..98a355f --- /dev/null +++ b/elpa/with-editor-20160223.1155/with-editor-pkg.el @@ -0,0 +1,9 @@ +(define-package "with-editor" "20160223.1155" "Use the Emacsclient as $EDITOR" + '((emacs "24.4") + (async "1.5") + (dash "2.12.1")) + :url "https://github.com/magit/with-editor" :keywords + '("tools")) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/with-editor-20160223.1155/with-editor.el b/elpa/with-editor-20160223.1155/with-editor.el new file mode 100644 index 0000000..459b1c8 --- /dev/null +++ b/elpa/with-editor-20160223.1155/with-editor.el @@ -0,0 +1,732 @@ +;;; with-editor.el --- Use the Emacsclient as $EDITOR -*- lexical-binding: t -*- + +;; Copyright (C) 2014-2016 The Magit Project Contributors +;; +;; You should have received a copy of the AUTHORS.md file. If not, +;; see https://github.com/magit/with-editor/blob/master/AUTHORS.md. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; Package-Requires: ((emacs "24.4") (async "1.5") (dash "2.12.1")) +;; Keywords: tools +;; Homepage: https://github.com/magit/with-editor + +;; This file is not part of GNU Emacs. + +;; This file 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. + +;; This file 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 library makes it possible to reliably use the Emacsclient as +;; the `$EDITOR' of child processes. It makes sure that they know how +;; to call home. For remote processes a substitute is provided, which +;; communicates with Emacs on standard output/input instead of using a +;; socket as the Emacsclient does. + +;; It provides the commands `with-editor-async-shell-command' and +;; `with-editor-shell-command', which are intended as replacements +;; for `async-shell-command' and `shell-command'. They automatically +;; export `$EDITOR' making sure the executed command uses the current +;; Emacs instance as "the editor". With a prefix argument these +;; commands prompt for an alternative environment variable such as +;; `$GIT_EDITOR'. To always use these variants add this to your init +;; file: +;; +;; (define-key (current-global-map) +;; [remap async-shell-command] 'with-editor-async-shell-command) +;; (define-key (current-global-map) +;; [remap shell-command] 'with-editor-shell-command) + +;; Alternatively use the global `shell-command-with-editor-mode', +;; which always sets `$EDITOR' for all Emacs commands which ultimately +;; use `shell-command' to asynchronously run some shell command. + +;; The command `with-editor-export-editor' exports `$EDITOR' or +;; another such environment variable in `shell-mode', `term-mode' and +;; `eshell-mode' buffers. Use this Emacs command before executing a +;; shell command which needs the editor set, or always arrange for the +;; current Emacs instance to be used as editor by adding it to the +;; appropriate mode hooks: +;; +;; (add-hook 'shell-mode-hook 'with-editor-export-editor) +;; (add-hook 'term-mode-hook 'with-editor-export-editor) +;; (add-hook 'eshell-mode-hook 'with-editor-export-editor) + +;; Some variants of this function exist, these two forms are +;; equivalent: +;; +;; (add-hook 'shell-mode-hook +;; (apply-partially 'with-editor-export-editor "GIT_EDITOR")) +;; (add-hook 'shell-mode-hook 'with-editor-export-git-editor) + +;; This library can also be used by other packages which need to use +;; the current Emacs instance as editor. In fact this library was +;; written for Magit and its `git-commit-mode' and `git-rebase-mode'. +;; Consult `git-rebase.el' and the related code in `magit-sequence.el' +;; for a simple example. + +;;; Code: + +(require 'cl-lib) +(require 'dash) +(require 'server) +(require 'tramp) +(require 'tramp-sh nil t) + +(and (require 'async-bytecomp nil t) + (memq 'magit (bound-and-true-p async-bytecomp-allowed-packages)) + (fboundp 'async-bytecomp-package-mode) + (async-bytecomp-package-mode 1)) + +(eval-when-compile + (progn (require 'dired nil t) + (require 'eshell nil t) + (require 'term nil t) + (require 'warnings nil t))) +(declare-function dired-get-filename 'dired) +(declare-function term-emulate-terminal 'term) +(defvar eshell-preoutput-filter-functions) + +;;; Options + +(defgroup with-editor nil + "Use the Emacsclient as $EDITOR." + :group 'external + :group 'server) + +(defun with-editor-locate-emacsclient () + "Search for a suitable Emacsclient executable." + (--if-let (with-editor-locate-emacsclient-1 (with-editor-emacsclient-path) 3) + it + (display-warning 'with-editor (format "\ +Cannot determine a suitable Emacsclient + +Determining an Emacsclient executable suitable for the +current Emacs instance failed. For more information +please see https://github.com/magit/magit/wiki/Emacsclient.")) + nil)) + +(defun with-editor-locate-emacsclient-1 (path depth) + (let* ((version-lst (-take depth (split-string emacs-version "\\."))) + (version-reg (concat "^" (mapconcat #'identity version-lst "\\.")))) + (or (locate-file-internal + "emacsclient" path + (cl-mapcan + (lambda (v) (cl-mapcar (lambda (e) (concat v e)) exec-suffixes)) + (nconc (cl-mapcon (lambda (v) + (setq v (mapconcat #'identity (reverse v) ".")) + (list v (concat "-" v) (concat ".emacs" v))) + (reverse version-lst)) + (list "" "-snapshot"))) + (lambda (exec) + (ignore-errors + (string-match-p version-reg + (with-editor-emacsclient-version exec))))) + (and (> depth 1) + (with-editor-locate-emacsclient-1 path (1- depth)))))) + +(defun with-editor-emacsclient-version (exec) + (-when-let (1st-line (car (process-lines exec "--version"))) + (cadr (split-string 1st-line)))) + +(defun with-editor-emacsclient-path () + (let ((path exec-path)) + (when invocation-directory + (push (directory-file-name invocation-directory) path) + (let* ((linkname (expand-file-name invocation-name invocation-directory)) + (truename (file-chase-links linkname))) + (unless (equal truename linkname) + (push (directory-file-name (file-name-directory truename)) path))) + (when (eq system-type 'darwin) + (let ((dir (expand-file-name "bin" invocation-directory))) + (when (file-directory-p dir) + (push dir path))) + (when (string-match-p "Cellar" invocation-directory) + (let ((dir (expand-file-name "../../../bin" invocation-directory))) + (when (file-directory-p dir) + (push dir path)))))) + (cl-remove-duplicates path :test 'equal))) + +(defcustom with-editor-emacsclient-executable (with-editor-locate-emacsclient) + "The Emacsclient executable used by the `with-editor' macro." + :group 'with-editor + :type '(choice (string :tag "Executable") + (const :tag "Don't use Emacsclient" nil))) + +(defcustom with-editor-sleeping-editor "\ +sh -c '\ +echo \"WITH-EDITOR: $$ OPEN $0\"; \ +sleep 604800 & sleep=$!; \ +trap \"kill $sleep; exit 0\" USR1; \ +trap \"kill $sleep; exit 1\" USR2; \ +wait $sleep'" + "The sleeping editor, used when the Emacsclient cannot be used. + +This fallback is used for asynchronous process started inside the +macro `with-editor', when the process runs on a remote machine or +for local processes when `with-editor-emacsclient-executable' is +nil (i.e. when no suitable Emacsclient was found, or the user +decided not to use it). + +Where the latter uses a socket to communicate with Emacs' server, +this substitute prints edit requests to its standard output on +which a process filter listens for such requests. As such it is +not a complete substitute for a proper Emacsclient, it can only +be used as $EDITOR of child process of the current Emacs instance." + :group 'with-editor + :type 'string) + +(defcustom with-editor-finish-query-functions nil + "List of functions called to query before finishing session. + +The buffer in question is current while the functions are called. +If any of them returns nil, then the session is not finished and +the buffer is not killed. The user should then fix the issue and +try again. The functions are called with one argument. If it is +non-nil then that indicates that the user used a prefix argument +to force finishing the session despite issues. Functions should +usually honor that and return non-nil." + :group 'with-editor + :type 'hook) +(put 'with-editor-finish-query-functions 'permanent-local t) + +(defcustom with-editor-cancel-query-functions nil + "List of functions called to query before canceling session. + +The buffer in question is current while the functions are called. +If any of them returns nil, then the session is not canceled and +the buffer is not killed. The user should then fix the issue and +try again. The functions are called with one argument. If it is +non-nil then that indicates that the user used a prefix argument +to force canceling the session despite issues. Functions should +usually honor that and return non-nil." + :group 'with-editor + :type 'hook) +(put 'with-editor-cancel-query-functions 'permanent-local t) + +(defcustom with-editor-mode-lighter " WE" + "The mode-line lighter of the With-Editor mode." + :group 'with-editor + :type '(choice (const :tag "No lighter" "") string)) + +(defvar with-editor-server-window-alist nil + "Alist of filename patterns vs corresponding `server-window'. + +Each element looks like (REGEXP . FUNCTION). Files matching +REGEXP are selected using FUNCTION instead of the default in +`server-window'. + +Note that when a package adds an entry here then it probably +has a reason to disrespect `server-window' and it likely is +not a good idea to change such entries.") + +;;; Mode Commands + +(defvar with-editor-pre-finish-hook nil) +(defvar with-editor-pre-cancel-hook nil) +(defvar with-editor-post-finish-hook nil) +(defvar with-editor-post-finish-hook-1 nil) +(defvar with-editor-post-cancel-hook nil) +(defvar with-editor-post-cancel-hook-1 nil) +(defvar with-editor-cancel-alist nil) +(put 'with-editor-pre-finish-hook 'permanent-local t) +(put 'with-editor-pre-cancel-hook 'permanent-local t) +(put 'with-editor-post-finish-hook 'permanent-local t) +(put 'with-editor-post-cancel-hook 'permanent-local t) + +(defvar with-editor-show-usage t) +(defvar with-editor-cancel-message nil) +(defvar with-editor-previous-winconf nil) +(make-variable-buffer-local 'with-editor-show-usage) +(make-variable-buffer-local 'with-editor-cancel-message) +(make-variable-buffer-local 'with-editor-previous-winconf) +(put 'with-editor-cancel-message 'permanent-local t) +(put 'with-editor-previous-winconf 'permanent-local t) + +(defvar-local with-editor--pid nil "For internal use.") +(put 'with-editor--pid 'permanent-local t) + +(defun with-editor-finish (force) + "Finish the current edit session." + (interactive "P") + (when (run-hook-with-args-until-failure + 'with-editor-finish-query-functions force) + (let ((with-editor-post-finish-hook-1 + (ignore-errors (delq t with-editor-post-finish-hook)))) + (run-hooks 'with-editor-pre-finish-hook) + (with-editor-return nil) + (accept-process-output nil 0.1) + (run-hooks 'with-editor-post-finish-hook-1)))) + +(defun with-editor-cancel (force) + "Cancel the current edit session." + (interactive "P") + (when (run-hook-with-args-until-failure + 'with-editor-cancel-query-functions force) + (let ((message with-editor-cancel-message)) + (when (functionp message) + (setq message (funcall message))) + (let ((with-editor-post-cancel-hook-1 + (ignore-errors (delq t with-editor-post-cancel-hook))) + (with-editor-cancel-alist nil)) + (run-hooks 'with-editor-pre-cancel-hook) + (with-editor-return t) + (accept-process-output nil 0.1) + (run-hooks 'with-editor-post-cancel-hook-1)) + (message (or message "Canceled by user"))))) + +(defun with-editor-return (cancel) + (let ((winconf with-editor-previous-winconf) + (clients server-buffer-clients) + (dir default-directory) + (pid with-editor--pid)) + (remove-hook 'kill-buffer-query-functions + 'with-editor-kill-buffer-noop t) + (cond (cancel + (save-buffer) + (if clients + (dolist (client clients) + (ignore-errors + (server-send-string client "-error Canceled by user")) + (delete-process client)) + ;; Fallback for when emacs was used as $EDITOR instead + ;; of emacsclient or the sleeping editor. See #2258. + (ignore-errors (delete-file buffer-file-name)) + (kill-buffer))) + (t + (save-buffer) + (if clients + ;; Don't use `server-edit' because we do not want to show + ;; another buffer belonging to another client. See #2197. + (server-done) + (kill-buffer)))) + (when pid + (let ((default-directory dir)) + (process-file "kill" nil nil nil + "-s" (if cancel "USR2" "USR1") pid))) + (when (and winconf (eq (window-configuration-frame winconf) + (selected-frame))) + (set-window-configuration winconf)))) + +;;; Mode + +(defvar with-editor-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "\C-c\C-c" 'with-editor-finish) + (define-key map [remap server-edit] 'with-editor-finish) + (define-key map [remap evil-save-and-close] 'with-editor-finish) + (define-key map [remap evil-save-modified-and-close] 'with-editor-finish) + (define-key map "\C-c\C-k" 'with-editor-cancel) + (define-key map [remap kill-buffer] 'with-editor-cancel) + (define-key map [remap ido-kill-buffer] 'with-editor-cancel) + (define-key map [remap iswitchb-kill-buffer] 'with-editor-cancel) + (define-key map [remap evil-quit] 'with-editor-cancel) + map)) + +(define-minor-mode with-editor-mode + "Edit a file as the $EDITOR of an external process." + :lighter with-editor-mode-lighter + ;; Protect the user from killing the buffer without using + ;; either `with-editor-finish' or `with-editor-cancel', + ;; and from removing the key bindings for these commands. + (unless with-editor-mode + (error "With-Editor mode cannot be turned off")) + (add-hook 'kill-buffer-query-functions + 'with-editor-kill-buffer-noop nil t) + ;; `server-execute' displays a message which is not + ;; correct when using this mode. + (when with-editor-show-usage + (with-editor-usage-message))) + +(put 'with-editor-mode 'permanent-local t) + +(defun with-editor-kill-buffer-noop () + (message (substitute-command-keys "\ +Don't kill this buffer. Instead cancel using \\[with-editor-cancel]"))) + +(defun with-editor-usage-message () + ;; Run after `server-execute', which is run using + ;; a timer which starts immediately. + (run-with-timer + 0.01 nil `(lambda () + (with-current-buffer ,(current-buffer) + (message (substitute-command-keys "\ +Type \\[with-editor-finish] to finish, \ +or \\[with-editor-cancel] to cancel")))))) + +;;; Wrappers + +(defvar with-editor--envvar nil "For internal use.") + +(defmacro with-editor (&rest body) + "Use the Emacsclient as $EDITOR while evaluating BODY. +Modify the `process-environment' for processes started in BODY, +instructing them to use the Emacsclient as $EDITOR. If optional +ENVVAR is provided then bind that environment variable instead. +\n(fn [ENVVAR] BODY...)" + (declare (indent defun) (debug (body))) + `(let ((with-editor--envvar ,(if (stringp (car body)) + (pop body) + '(or with-editor--envvar "EDITOR"))) + (process-environment process-environment)) + (if (or (not with-editor-emacsclient-executable) + (file-remote-p default-directory)) + (push (concat with-editor--envvar "=" with-editor-sleeping-editor) + process-environment) + ;; Make sure server-use-tcp's value is valid. + (unless (featurep 'make-network-process '(:family local)) + (setq server-use-tcp t)) + ;; Make sure the server is running. + (unless server-process + (when (server-running-p server-name) + (setq server-name (format "server%s" (emacs-pid))) + (when (server-running-p server-name) + (server-force-delete server-name))) + (server-start)) + ;; Tell $EDITOR to use the Emacsclient. + (push (concat with-editor--envvar "=" + (shell-quote-argument with-editor-emacsclient-executable) + ;; Tell the process where the server file is. + (and (not server-use-tcp) + (concat " --socket-name=" + (shell-quote-argument + (expand-file-name server-name + server-socket-dir))))) + process-environment) + (when server-use-tcp + (push (concat "EMACS_SERVER_FILE=" + (expand-file-name server-name server-auth-dir)) + process-environment)) + ;; As last resort fallback to the sleeping editor. + (push (concat "ALTERNATE_EDITOR=" with-editor-sleeping-editor) + process-environment)) + ,@body)) + +(defun with-editor-server-window () + (or (and buffer-file-name + (cdr (--first (string-match-p (car it) buffer-file-name) + with-editor-server-window-alist))) + server-window)) + +(defun server-switch-buffer--with-editor-server-window-alist + (fn &optional next-buffer killed-one filepos) + "Honor `with-editor-server-window-alist' (which see)." + (let ((server-window (with-current-buffer + (or next-buffer (current-buffer)) + (when with-editor-mode + (setq with-editor-previous-winconf + (current-window-configuration))) + (with-editor-server-window)))) + (funcall fn next-buffer killed-one filepos))) + +(advice-add 'server-switch-buffer :around + 'server-switch-buffer--with-editor-server-window-alist) + +(defun start-file-process--with-editor-process-filter + (fn name buffer program &rest program-args) + "When called inside a `with-editor' form and the Emacsclient +cannot be used, then give the process the filter function +`with-editor-process-filter'. To avoid overriding the filter +being added here you should use `with-editor-set-process-filter' +instead of `set-process-filter' inside `with-editor' forms. + +When the `default-directory' is located on a remote machine, +then also manipulate PROGRAM and PROGRAM-ARGS in order to set +the appropriate editor environment variable." + (if (not with-editor--envvar) + (apply fn name buffer program program-args) + (when (file-remote-p default-directory) + (unless (equal program "env") + (push program program-args) + (setq program "env")) + (push (concat with-editor--envvar "=" with-editor-sleeping-editor) + program-args)) + (let ((process (apply fn name buffer program program-args))) + (set-process-filter process 'with-editor-process-filter) + (process-put process 'default-dir default-directory) + process))) + +(advice-add 'start-file-process :around + 'start-file-process--with-editor-process-filter) + +(defun with-editor-set-process-filter (process filter) + "Like `set-process-filter' but keep `with-editor-process-filter'. +Give PROCESS the new FILTER but keep `with-editor-process-filter' +if that was added earlier by the adviced `start-file-process'. + +Do so by wrapping the two filter functions using a lambda, which +becomes the actual filter. It calls `with-editor-process-filter' +first, passing t as NO-STANDARD-FILTER. Then it calls FILTER, +which may or may not insert the text into the PROCESS' buffer." + (set-process-filter + process + (if (eq (process-filter process) 'with-editor-process-filter) + `(lambda (proc str) + (,filter proc str) + (with-editor-process-filter proc str t)) + filter))) + +(defvar with-editor-filter-visit-hook nil) + +(defun with-editor-output-filter (string) + (save-match-data + (if (string-match "^WITH-EDITOR: \\([0-9]+\\) OPEN \\(.+?\\)\r?$" string) + (let ((pid (match-string 1 string)) + (file (match-string 2 string))) + (with-current-buffer + (find-file-noselect + (if (file-name-absolute-p file) + (if (tramp-tramp-file-p default-directory) + (with-parsed-tramp-file-name default-directory nil + (tramp-make-tramp-file-name method user host file hop)) + file) + (expand-file-name file))) + (with-editor-mode 1) + (setq with-editor--pid pid) + (run-hooks 'with-editor-filter-visit-hook) + (funcall (or (with-editor-server-window) 'switch-to-buffer) + (current-buffer)) + (kill-local-variable 'server-window)) + nil) + string))) + +(defun with-editor-process-filter + (process string &optional no-default-filter) + "Listen for edit requests by child processes." + (let ((default-directory (process-get process 'default-dir))) + (with-editor-output-filter string)) + (unless no-default-filter + (internal-default-process-filter process string))) + +;;; Augmentations + +(cl-defun with-editor-export-editor (&optional (envvar "EDITOR")) + "Teach subsequent commands to use current Emacs instance as editor. + +Set and export the environment variable ENVVAR, by default +\"EDITOR\". The value is automatically generated to teach +commands use the current Emacs instance as \"the editor\". + +This works in `shell-mode', `term-mode' and `eshell-mode'." + (interactive (list (with-editor-read-envvar))) + (cond + ((derived-mode-p 'comint-mode 'term-mode) + (let* ((process (get-buffer-process (current-buffer))) + (filter (process-filter process))) + (set-process-filter process 'ignore) + (goto-char (process-mark process)) + (process-send-string + process (format "export %s=%s\n" envvar + (shell-quote-argument with-editor-sleeping-editor))) + (while (accept-process-output process 0.1)) + (set-process-filter process filter) + (if (derived-mode-p 'term-mode) + (with-editor-set-process-filter process 'with-editor-emulate-terminal) + (add-hook 'comint-output-filter-functions 'with-editor-output-filter + nil t)))) + ((derived-mode-p 'eshell-mode) + (add-to-list 'eshell-preoutput-filter-functions + 'with-editor-output-filter) + (setenv envvar with-editor-sleeping-editor)) + (t + (error "Cannot export environment variables in this buffer"))) + (message "Successfully exported %s" envvar)) + +(defun with-editor-export-git-editor () + "Like `with-editor-export-editor' but always set `$GIT_EDITOR'." + (interactive) + (with-editor-export-editor "GIT_EDITOR")) + +(defun with-editor-export-hg-editor () + "Like `with-editor-export-editor' but always set `$HG_EDITOR'." + (interactive) + (with-editor-export-editor "HG_EDITOR")) + +(defun with-editor-emulate-terminal (process string) + "Like `term-emulate-terminal' but also handle edit requests." + (when (with-editor-output-filter string) + (term-emulate-terminal process string))) + +(defvar with-editor-envvars '("EDITOR" "GIT_EDITOR" "HG_EDITOR")) + +(cl-defun with-editor-read-envvar + (&optional (prompt "Set environment variable") + (default "EDITOR")) + (let ((reply (completing-read (if default + (format "%s (%s): " prompt default) + (concat prompt ": ")) + with-editor-envvars nil nil nil nil default))) + (if (string= reply "") (user-error "Nothing selected") reply))) + +(define-minor-mode shell-command-with-editor-mode + "Teach `shell-command' to use current Emacs instance as editor. + +Teach `shell-command', and all commands that ultimately call that +command, to use the current Emacs instance as editor by executing +\"EDITOR=CLIENT COMMAND&\" instead of just \"COMMAND&\". + +CLIENT is automatically generated; EDITOR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming no other variable overrides the effect of \"$EDITOR\". +CLIENT may be the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Alternatively you can use the `with-editor-async-shell-command', +which also allows the use of another variable instead of +\"EDITOR\"." + :global t) + +(defun with-editor-async-shell-command + (command &optional output-buffer error-buffer envvar) + "Like `async-shell-command' but with `$EDITOR' set. + +Execute string \"ENVVAR=CLIENT COMMAND\" in an inferior shell; +display output, if any. With a prefix argument prompt for an +environment variable, otherwise the default \"EDITOR\" variable +is used. With a negative prefix argument additionally insert +the COMMAND's output at point. + +CLIENT is automatically generated; ENVVAR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming it respects ENVVAR as an \"EDITOR\"-like variable. +CLIENT maybe the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Also see `async-shell-command' and `shell-command'." + (interactive (with-editor-shell-command-read-args "Async shell command: " t)) + (let ((with-editor--envvar envvar)) + (with-editor + (async-shell-command command output-buffer error-buffer)))) + +(defun with-editor-shell-command + (command &optional output-buffer error-buffer envvar) + "Like `shell-command' or `with-editor-async-shell-command'. +If COMMAND ends with \"&\" behave like the latter, +else like the former." + (interactive (with-editor-shell-command-read-args "Shell command: ")) + (if (string-match "&[ \t]*\\'" command) + (with-editor-async-shell-command + command output-buffer error-buffer envvar) + (shell-command command output-buffer error-buffer))) + +(defun with-editor-shell-command-read-args (prompt &optional async) + (let ((command (read-shell-command + prompt nil nil + (--when-let (or buffer-file-name + (and (eq major-mode 'dired-mode) + (dired-get-filename nil t))) + (file-relative-name it))))) + (list command + (if (or async (setq async (string-match-p "&[ \t]*\\'" command))) + (< (prefix-numeric-value current-prefix-arg) 0) + current-prefix-arg) + shell-command-default-error-buffer + (and async current-prefix-arg (with-editor-read-envvar))))) + +(defun shell-command--shell-command-with-editor-mode + (fn command &optional output-buffer error-buffer) + (cond ((or (not (or with-editor--envvar shell-command-with-editor-mode)) + (not (string-match-p "&\\'" command))) + (funcall fn command output-buffer error-buffer)) + ((and with-editor-emacsclient-executable + (not (file-remote-p default-directory))) + (with-editor (funcall fn command output-buffer error-buffer))) + (t + (apply fn (format "%s=%s %s" + (or with-editor--envvar "EDITOR") + (shell-quote-argument with-editor-sleeping-editor) + command) + output-buffer error-buffer) + (ignore-errors + (let ((process (get-buffer-process + (or output-buffer + (get-buffer "*Async Shell Command*"))))) + (set-process-filter + process (lambda (proc str) + (comint-output-filter proc str) + (with-editor-process-filter proc str t))) + process))))) + +(advice-add 'shell-command :around + 'shell-command--shell-command-with-editor-mode) + +;;; with-editor.el ends soon + +(defun with-editor-debug () + "Debug configuration issues. +See `with-editor.info' for instructions." + (interactive) + (with-current-buffer (get-buffer-create "*with-editor-debug*") + (pop-to-buffer (current-buffer)) + (erase-buffer) + (ignore-errors (with-editor)) + (insert + (format "with-editor: %s\n" (locate-library "with-editor.el")) + (format "emacs: %s (%s)\n" + (expand-file-name invocation-name invocation-directory) + emacs-version) + "system:\n" + (format " system-type: %s\n" system-type) + (format " system-configuration: %s\n" system-configuration) + (format " system-configuration-options: %s\n" system-configuration-options) + "server:\n" + (format " server-running-p: %s\n" (server-running-p)) + (format " server-process: %S\n" server-process) + (format " server-use-tcp: %s\n" server-use-tcp) + (format " server-name: %s\n" server-name) + (format " server-socket-dir: %s\n" server-socket-dir)) + (if (and server-socket-dir (file-accessible-directory-p server-socket-dir)) + (--each (directory-files server-socket-dir nil "^[^.]") + (insert (format " %s\n" it))) + (insert (format " %s: not an accessible directory\n" + (if server-use-tcp "WARNING" "ERROR")))) + (insert (format " server-auth-dir: %s\n" server-auth-dir)) + (if (file-accessible-directory-p server-auth-dir) + (--each (directory-files server-auth-dir nil "^[^.]") + (insert (format " %s\n" it))) + (insert (format " %s: not an accessible directory\n" + (if server-use-tcp "ERROR" "WARNING")))) + (let ((val with-editor-emacsclient-executable) + (def (default-value 'with-editor-emacsclient-executable)) + (fun (let ((warning-minimum-level :error) + (warning-minimum-log-level :error)) + (with-editor-locate-emacsclient)))) + (insert "magit-emacsclient-executable:\n" + (format " value: %s (%s)\n" val + (and val (with-editor-emacsclient-version val))) + (format " default: %s (%s)\n" def + (and def (with-editor-emacsclient-version def))) + (format " funcall: %s (%s)\n" fun + (and fun (with-editor-emacsclient-version fun))))) + (insert "path:\n" + (format " $PATH: %S\n" (getenv "PATH")) + (format " exec-path: %s\n" exec-path)) + (insert (format " with-editor-emacsclient-path:\n")) + (--each (with-editor-emacsclient-path) + (insert (format " %s (%s)\n" it (car (file-attributes it)))) + (when (file-directory-p it) + (dolist (exec (directory-files it t "emacsclient")) + (insert (format " %s (%s)\n" exec + (with-editor-emacsclient-version exec)))))))) + +(defconst with-editor-font-lock-keywords + '(("(\\(with-\\(?:git-\\)?editor\\)\\_>" (1 'font-lock-keyword-face)))) +(font-lock-add-keywords 'emacs-lisp-mode with-editor-font-lock-keywords) + +(provide 'with-editor) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; with-editor.el ends here diff --git a/elpa/with-editor-20160223.1155/with-editor.info b/elpa/with-editor-20160223.1155/with-editor.info new file mode 100644 index 0000000..41419b9 --- /dev/null +++ b/elpa/with-editor-20160223.1155/with-editor.info @@ -0,0 +1,323 @@ +This is with-editor.info, produced by makeinfo version 5.2 from +with-editor.texi. + +The library ‘with-editor’ makes it easy to use the Emacsclient as the +‘$EDITOR’ of child processes, making sure they know how to call home. +For remote processes a substitute is provided, which communicates with +Emacs on standard output instead of using a socket as the Emacsclient +does. + + This library was written because Magit has to be able to do the above +to allow the user to edit commit messages gracefully and to edit rebase +sequences, which wouldn’t be possible at all otherwise. + + Because other packages can benefit from such functionality, this +library is made available as a separate package. It also defines some +additional functionality which makes it useful even for end-users, who +don’t use Magit or another package which uses it internally. + + Copyright (C) 2015-2016 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document 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. +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* With-Editor: (with-editor). Using the Emacsclient as $EDITOR. +END-INFO-DIR-ENTRY + + +File: with-editor.info, Node: Top, Next: Using the With-Editor package, Up: (dir) + +With-Editor User Manual +*********************** + +The library ‘with-editor’ makes it easy to use the Emacsclient as the +‘$EDITOR’ of child processes, making sure they know how to call home. +For remote processes a substitute is provided, which communicates with +Emacs on standard output instead of using a socket as the Emacsclient +does. + + This library was written because Magit has to be able to do the above +to allow the user to edit commit messages gracefully and to edit rebase +sequences, which wouldn’t be possible at all otherwise. + + Because other packages can benefit from such functionality, this +library is made available as a separate package. It also defines some +additional functionality which makes it useful even for end-users, who +don’t use Magit or another package which uses it internally. + + Copyright (C) 2015-2016 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document 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. + +* Menu: + +* Using the With-Editor package:: +* Using With-Editor as a library:: +* Debugging:: + +— The Detailed Node Listing — + +Using the With-Editor package + +* Configuring With-Editor:: +* Using With-Editor commands:: + + + +File: with-editor.info, Node: Using the With-Editor package, Next: Using With-Editor as a library, Prev: Top, Up: Top + +1 Using the With-Editor package +******************************* + +The ‘With-Editor’ package is used internally by Magit when editing +commit messages and rebase sequences. It also provides some commands +and features which are useful by themselves, even if you don’t use +Magit. + + For information about using this library in you own package, see +*note Using With-Editor as a library: Using With-Editor as a library. + +* Menu: + +* Configuring With-Editor:: +* Using With-Editor commands:: + + +File: with-editor.info, Node: Configuring With-Editor, Next: Using With-Editor commands, Up: Using the With-Editor package + +1.1 Configuring With-Editor +=========================== + +With-Editor tries very hard to locate a suitable emacsclient executable, +so ideally you should never have to customize the option +‘with-editor-emacsclient-executable’. When it fails to do so, then the +most likely reason is that someone found yet another way to package +Emacs (most likely on OS X) without putting the executable on ‘$PATH’, +and we have to add another kludge to find it anyway. + + -- User Option: with-editor-emacsclient-executable + + The emacsclient executable used as the editor by child process of + this Emacs instance. By using this executable, child processes can + call home to their parent process. + + This option is automatically set at startup by looking in + ‘exec-path’, and other places where the executable could be + installed, to find the emacsclient executable most suitable for the + current emacs instance. + + You should *not* customize this option permanently. If you have to + do it, then you should consider that a temporary kludge and inform + the Magit maintainer as described in *note Debugging: Debugging. + + If With-Editor fails to find a suitable emacsclient on you system, + then this should be fixed for all users at once, by teaching + ‘with-editor-locate-emacsclient’ how to so on your system and + system like yours. Doing it this way has the advantage, that you + won’t have do it again every time you update Emacs, and that other + users who have installed Emacs the same way as you have, won’t have + to go through the same trouble. + + Note that there also is a nuclear option; setting this variable to + ‘nil’ causes the "sleeping editor" described below to be used even + for local child processes. Obviously we don’t recommend that you + use this except in "emergencies", i.e. before we had a change to + add a kludge appropriate for you setup. + + -- Function: with-editor-locate-emacsclient + + The function used to set the initial value of the option + ‘with-editor-emacsclient-executable’. There’s a lot of voodoo + here. + + The emacsclient cannot be used when using Tramp to run a process on a +remote machine. (Theoretically it could, but that would be hard to +setup, very fragile, and rather insecure). + + With-Editor provides an alternative "editor" which can be used by +remote processes in much the same way as local processes use an +emacsclient executable. This alternative is known as the "sleeping +editor" because it is implemented as a shell script which sleeps until +it receives a signal. + + -- User Option: with-editor-sleeping-editor + + The sleeping editor is a shell script used as the editor of child + processes when the emacsclient executable cannot be used. + + This fallback is used for asynchronous process started inside the + macro ‘with-editor’, when the process runs on a remote machine or + for local processes when ‘with-editor-emacsclient-executable’ is + ‘nil’. + + Where the latter uses a socket to communicate with Emacs’ server, + this substitute prints edit requests to its standard output on + which a process filter listens for such requests. As such it is + not a complete substitute for a proper Emacsclient, it can only be + used as ‘$EDITOR’ of child process of the current Emacs instance. + + It is unlikely that you should ever have to customize this option. + + +File: with-editor.info, Node: Using With-Editor commands, Prev: Configuring With-Editor, Up: Using the With-Editor package + +1.2 Using With-Editor commands +============================== + +This section describes how to use the ‘with-editor’ library _outside_ of +Magit. You don’t need to know any of this just to create commits using +Magit. + + The commands ‘with-editor-async-shell-command’ and +‘with-editor-shell-command’ are intended as drop in replacements for +‘async-shell-command’ and ‘shell-command’. They automatically export +‘$EDITOR’ making sure the executed command uses the current Emacs +instance as "the editor". With a prefix argument these commands prompt +for an alternative environment variable such as ‘$GIT_EDITOR’. + + -- Command: with-editor-async-shell-command + + Like ‘async-shell-command’, but the command is run with the current + Emacs instance exported as ‘$EDITOR’. + + -- Command: with-editor-shell-command + + Like ‘async-shell-command’, but the command is run with the current + Emacs instance exported as ‘$EDITOR’. This only has an effect if + the command is run asynchronously, i.e. when the command ends with + ‘&’. + + To always use these variants add this to you init file: + + (define-key (current-global-map) + [remap async-shell-command] 'with-editor-async-shell-command) + (define-key (current-global-map) + [remap shell-command] 'with-editor-shell-command) + + Alternatively use the global ‘shell-command-with-editor-mode’. + + -- Variable: shell-command-with-editor-mode + + When this mode is active, then ‘$EDITOR’ is exported whenever + ultimately ‘shell-command’ is called to asynchronously run some + shell command. This affects most variants of that command, whether + they are defined in Emacs or in some third-party package. + + The command ‘with-editor-export-editor’ exports ‘$EDITOR’ or another +such environment variable in ‘shell-mode’, ‘term-mode’ and ‘eshell-mode’ +buffers. Use this Emacs command before executing a shell command which +needs the editor set, or always arrange for the current Emacs instance +to be used as editor by adding it to the appropriate mode hooks: + + (add-hook 'shell-mode-hook 'with-editor-export-editor) + (add-hook 'term-mode-hook 'with-editor-export-editor) + (add-hook 'eshell-mode-hook 'with-editor-export-editor) + + Some variants of this function exist; these two forms are equivalent: + + (add-hook 'shell-mode-hook + (apply-partially 'with-editor-export-editor "GIT_EDITOR")) + (add-hook 'shell-mode-hook 'with-editor-export-git-editor) + + -- Command: with-editor-export-editor + + When invoked in a ‘shell-mode’, ‘term-mode’, or ‘eshell-mode’ + buffer, this command teaches shell commands to use the current + Emacs instance as the editor, by exporting ‘$EDITOR’. + + -- Command: with-editor-export-git-editor + + Like ‘with-editor-export-editor’ but exports ‘$GIT_EDITOR’. + + -- Command: with-editor-export-hg-editor + + Like ‘with-editor-export-editor’ but exports ‘$HG_EDITOR’. + + +File: with-editor.info, Node: Using With-Editor as a library, Next: Debugging, Prev: Using the With-Editor package, Up: Top + +2 Using With-Editor as a library +******************************** + +This section describes how to use the with-editor library _outside_ of +Magit to teach another package how to have its child processes call +home, just like Magit does. You don’t need to know any of this just to +create commits using Magit. You can also ignore this if you use +‘with-editor’ outside of Magit, but only as an end-user. + + For information about interactive use and options which affect both +interactive and non-interactive use, see *note Using the With-Editor +package: Using the With-Editor package. + + -- Macro: with-editor &rest body + + This macro arranges for the emacsclient or the sleeping editor to + be used as the editor of child processes, effectively teaching them + to call home to the current emacs instance when they require that + the user edits a file. + + This is essentially done by establishing a local binding for + ‘process-environment’ and changing the value of the ‘$EDITOR’ + environment variable. This affects all processes started by forms + inside BODY. + + -- Function: with-editor-set-process-filter process filter + + This function is like ‘set-process-filter’ but ensures that adding + the new FILTER does not remove the ‘with-editor-process-filter’. + This is done by wrapping the two filter functions using a lambda, + which becomes the actual filter. It calls + ‘with-editor-process-filter’ first, passing ‘t’ as + NO-STANDARD-FILTER. Then it calls FILTER. + + +File: with-editor.info, Node: Debugging, Prev: Using With-Editor as a library, Up: Top + +3 Debugging +*********** + +With-Editor tries very hard to locate a suitable emacsclient executable, +and then sets option ‘with-editor-emacsclient-executable’ accordingly. +In very rare cases this fails. When it does fail, then the most likely +reason is that someone found yet another way to package Emacs (most +likely on OS X) without putting the executable on ‘$PATH’, and we have +to add another kludge to find it anyway. + + If you are having problems using ‘with-editor’, e.g. you cannot +commit in Magit, then please open a new issue at + and provide information about +your Emacs installation. Most importantly how did you install Emacs and +what is the output of ‘M-x with-editor-debug’? + + + +Tag Table: +Node: Top1545 +Node: Using the With-Editor package3237 +Node: Configuring With-Editor3853 +Node: Using With-Editor commands7460 +Node: Using With-Editor as a library10623 +Node: Debugging12295 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: