From: Andreas Gerler Date: Tue, 14 Jul 2015 20:24:08 +0000 (+0200) Subject: new version of magit. X-Git-Url: http://git.bundesbrandschatzamt.de/?a=commitdiff_plain;h=a16a3c138c65f692741bcc211efb28c56a70ab14;p=emacsconfig.git new version of magit. --- diff --git a/bba.org b/bba.org index 129c8c2d..b43550e7 100644 --- a/bba.org +++ b/bba.org @@ -116,7 +116,7 @@ Keep track of your important stuff! (package-initialize) ) (progn - (add-to-list 'load-path "~/.emacs.d/elpa/magit-1.2.0") + (add-to-list 'load-path "~/.emacs.d/elpa/magit-20150704.855") (add-to-list 'load-path "~/.emacs.d/elpa/yaml-mode-0.0.9") (require 'yaml-mode) ) @@ -130,6 +130,7 @@ For annotated tags prepare message with commit messages since last tag. #+BEGIN_SRC emacs-lisp (require 'magit) + (setq magit-last-seen-setup-instructions "1.4.0") (add-hook 'magit-log-edit-mode-hook (lambda () (when (magit-log-edit-get-field 'tag-name) diff --git a/elpa/dash-20140811.523/dash-autoloads.el b/elpa/dash-20140811.523/dash-autoloads.el deleted file mode 100644 index a2508e24..00000000 --- a/elpa/dash-20140811.523/dash-autoloads.el +++ /dev/null @@ -1,18 +0,0 @@ -;;; dash-autoloads.el --- automatically extracted autoloads -;; -;;; Code: - - -;;;### (autoloads nil nil ("dash-pkg.el" "dash.el") (21490 25513 -;;;;;; 236456 0)) - -;;;*** - -(provide 'dash-autoloads) -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; coding: utf-8 -;; End: -;;; dash-autoloads.el ends here diff --git a/elpa/dash-20140811.523/dash-pkg.el b/elpa/dash-20140811.523/dash-pkg.el deleted file mode 100644 index 1dab17d0..00000000 --- a/elpa/dash-20140811.523/dash-pkg.el +++ /dev/null @@ -1 +0,0 @@ -(define-package "dash" "20140811.523" "A modern list library for Emacs" (quote nil)) diff --git a/elpa/dash-20140811.523/dash.el b/elpa/dash-20140811.523/dash.el deleted file mode 100644 index a7078b97..00000000 --- a/elpa/dash-20140811.523/dash.el +++ /dev/null @@ -1,1805 +0,0 @@ -;;; dash.el --- A modern list library for Emacs - -;; Copyright (C) 2012-2014 Magnar Sveen - -;; Author: Magnar Sveen -;; Version: 20140811.523 -;; X-Original-Version: 2.8.0 -;; 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 - -;;; 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`." - (--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`." - (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." - (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." - (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'" - (--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) - -(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." - (--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 -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 -flatten (l) - "Take a nested list L and return its contents as a single, flat list. - -See also: `-flatten-n'" - (if (and (listp l) (listp (cdr l))) - (-mapcat '-flatten l) - (list l))) - -(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)) - -(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)) - -(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)) - -(defun --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 --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) - (len 0)) - (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 (form form))) - (let ((l (make-symbol "list")) - (v (make-symbol "value")) - (k (make-symbol "key")) - (r (make-symbol "result"))) - `(let ((,l ,list) - ,r) - ;; Convert `list' to an alist and store it in `r'. - (while ,l - (let* ((,v (car ,l)) - (it ,v) - (,k ,form) - (kv (assoc ,k ,r))) - (if kv - (setcdr kv (cons ,v (cdr kv))) - (push (list ,k ,v) ,r)) - (setq ,l (cdr ,l)))) - ;; Reverse lists in each group. - (let ((rest ,r)) - (while rest - (let ((kv (car rest))) - (setcdr kv (nreverse (cdr kv)))) - (setq rest (cdr rest)))) - ;; Reverse order of keys. - (nreverse ,r)))) - -(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. " - (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))) - -(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. - -Return how many lists were re-seted." - (while (and (not (car lists)) - (not (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 (--map nil (number-sequence 1 (length lists))))) - (while (car last-list) - (let ((item (apply fn (-map 'car lists)))) - (push item (car re)) - (pop (car lists)) - (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'" - (let ((restore-lists (copy-sequence lists)) - (last-list (last lists)) - re) - (while (car last-list) - (push (apply fn (-map 'car lists)) 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." - (let ((i 0)) - (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." - (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." - (-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 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." - (if (null more) - (if (listp form) - `(,(car form) ,@(cdr form) ,x) - (list form x)) - `(->> (->> ,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))) - -(put '-> 'lisp-indent-function 1) -(put '->> 'lisp-indent-function 1) -(put '--> 'lisp-indent-function 1) - -(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)))) - -(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." - (declare (debug ((symbolp form) body)) - (indent 1)) - (let ((var (car var-val)) - (val (cadr var-val))) - `(let ((,var ,val)) - (when ,var - ,@body)))) - -(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 (corresponding to bindings of `let*')." - (declare (debug ((&rest (symbolp form)) body)) - (indent 1)) - (if (= (length vars-vals) 1) - `(-when-let ,(car vars-vals) - ,@body) - `(-when-let ,(car vars-vals) - (-when-let* ,(cdr vars-vals) - ,@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)) - `(let ((it ,val)) - (when it - ,@body))) - -(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." - (declare (debug ((symbolp form) form body)) - (indent 2)) - (let ((var (car var-val)) - (val (cadr var-val))) - `(let ((,var ,val)) - (if ,var ,then ,@else)))) - -(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 (corresponding to the bindings of `let*')." - (declare (debug ((&rest (symbolp form)) form body)) - (indent 2)) - (let ((first-pair (car vars-vals)) - (rest (cdr vars-vals))) - (if (= (length vars-vals) 1) - `(-if-let ,first-pair ,then ,@else) - `(-if-let ,first-pair - (-if-let* ,rest ,then ,@else) - ,@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)) - `(let ((it ,val)) - (if it ,then ,@else))) - -(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." - (let (result) - (--each list (!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)) - -(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 -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? (nreverse suffix) (nreverse 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)))) - -(defmacro --iterate (form init n) - "Anaphoric version of `-iterate'." - (declare (debug (form form form))) - `(-iterate (lambda (it) ,form) ,init ,n)) - -(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 (listp 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" - "-non-nil" - "-keep" - "--keep" - "-map-indexed" - "--map-indexed" - "-splice" - "--splice" - "-splice-list" - "--splice-list" - "-map-when" - "--map-when" - "-replace-where" - "--replace-where" - "-replace" - "-flatten" - "-flatten-n" - "-concat" - "-mapcat" - "--mapcat" - "-copy" - "-cons*" - "-snoc" - "-first" - "--first" - "-find" - "--find" - "-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" - "-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/dash-20150704.253/dash-autoloads.el b/elpa/dash-20150704.253/dash-autoloads.el new file mode 100644 index 00000000..aef3961e --- /dev/null +++ b/elpa/dash-20150704.253/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") (21913 4133 260662 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; dash-autoloads.el ends here diff --git a/elpa/dash-20150704.253/dash-pkg.el b/elpa/dash-20150704.253/dash-pkg.el new file mode 100644 index 00000000..e039f088 --- /dev/null +++ b/elpa/dash-20150704.253/dash-pkg.el @@ -0,0 +1 @@ +(define-package "dash" "20150704.253" "A modern list library for Emacs" 'nil :keywords '("lists")) diff --git a/elpa/dash-20150704.253/dash.el b/elpa/dash-20150704.253/dash.el new file mode 100644 index 00000000..6e19e7e4 --- /dev/null +++ b/elpa/dash-20150704.253/dash.el @@ -0,0 +1,2381 @@ +;;; dash.el --- A modern list library for Emacs + +;; Copyright (C) 2012-2014 Magnar Sveen + +;; Author: Magnar Sveen +;; Version: 2.11.0 +;; Package-Version: 20150704.253 +;; 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 + +;;; 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'" + (--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) ,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) ,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. + +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) + (len 0)) + (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. " + (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))) + +(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. + +Return how many lists were re-seted." + (while (and (not (car lists)) + (not (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 (--map nil (number-sequence 1 (length lists))))) + (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'" + (let ((restore-lists (copy-sequence lists)) + (last-list (last lists)) + re) + (while (car last-list) + (push (apply fn (-map 'car lists)) 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." + (let ((i 0)) + (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." + (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." + (-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))) + +(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)) + (eq (car match-form) '&keys)) + (dash--match-kv (cons '&plist (cdr 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 + ((eq type '&plist) + `(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) '(&plist &alist &hash)) + (dash--match-kv match-form source)) + ((eq (car match-form) '&keys) + (dash--match-kv (cons '&plist (cdr 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))) + +(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." + (let (result) + (--each list (!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)) + +(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 -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? (nreverse suffix) (nreverse 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/magit-1.2.0/dir b/elpa/magit-1.2.0/dir deleted file mode 100644 index 2e646dbd..00000000 --- a/elpa/magit-1.2.0/dir +++ /dev/null @@ -1,18 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "?" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs -* Magit: (magit). Using Git from Emacs with Magit. diff --git a/elpa/magit-1.2.0/magit-autoloads.el b/elpa/magit-1.2.0/magit-autoloads.el deleted file mode 100644 index 98afb1c2..00000000 --- a/elpa/magit-1.2.0/magit-autoloads.el +++ /dev/null @@ -1,158 +0,0 @@ -;;; magit-autoloads.el --- automatically extracted autoloads -;; -;;; Code: - - -;;;### (autoloads (magit-status) "magit" "magit.el" (21205 9255)) -;;; Generated autoloads from magit.el - -(autoload 'magit-status "magit" "\ -Open a Magit status buffer for the Git repository containing -DIR. If DIR is not within a Git repository, offer to create a -Git repository in DIR. - -Interactively, a prefix argument means to ask the user which Git -repository to use even if `default-directory' is under Git control. -Two prefix arguments means to ignore `magit-repo-dirs' when asking for -user input. - -\(fn DIR)" t nil) - -;;;*** - -;;;### (autoloads (magit-blame-mode) "magit-blame" "magit-blame.el" -;;;;;; (21205 9255)) -;;; Generated autoloads from magit-blame.el - -(autoload 'magit-blame-mode "magit-blame" "\ -Display blame information inline. - -\(fn &optional ARG)" t nil) - -;;;*** - -;;;### (autoloads (turn-on-magit-stgit magit-stgit-mode) "magit-stgit" -;;;;;; "magit-stgit.el" (21205 9255)) -;;; Generated autoloads from magit-stgit.el - -(autoload 'magit-stgit-mode "magit-stgit" "\ -StGit support for Magit - -\(fn &optional ARG)" t nil) - -(autoload 'turn-on-magit-stgit "magit-stgit" "\ -Unconditionally turn on `magit-stgit-mode'. - -\(fn)" nil nil) - -;;;*** - -;;;### (autoloads (turn-on-magit-svn magit-svn-mode) "magit-svn" -;;;;;; "magit-svn.el" (21205 9255)) -;;; Generated autoloads from magit-svn.el - -(autoload 'magit-svn-mode "magit-svn" "\ -SVN support for Magit - -\(fn &optional ARG)" t nil) - -(autoload 'turn-on-magit-svn "magit-svn" "\ -Unconditionally turn on `magit-svn-mode'. - -\(fn)" nil nil) - -;;;*** - -;;;### (autoloads (turn-on-magit-topgit magit-topgit-mode) "magit-topgit" -;;;;;; "magit-topgit.el" (21205 9255)) -;;; Generated autoloads from magit-topgit.el - -(autoload 'magit-topgit-mode "magit-topgit" "\ -Topgit support for Magit - -\(fn &optional ARG)" t nil) - -(autoload 'turn-on-magit-topgit "magit-topgit" "\ -Unconditionally turn on `magit-topgit-mode'. - -\(fn)" nil nil) - -;;;*** - -;;;### (autoloads (global-magit-wip-save-mode magit-wip-save-mode -;;;;;; magit-wip-mode) "magit-wip" "magit-wip.el" (21205 9255)) -;;; Generated autoloads from magit-wip.el - -(defvar magit-wip-mode nil "\ -Non-nil if Magit-Wip mode is enabled. -See the command `magit-wip-mode' for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `magit-wip-mode'.") - -(custom-autoload 'magit-wip-mode "magit-wip" nil) - -(autoload 'magit-wip-mode "magit-wip" "\ -In Magit log buffers; give wip refs a special appearance. - -\(fn &optional ARG)" t nil) - -(autoload 'magit-wip-save-mode "magit-wip" "\ -Magit support for committing to a work-in-progress ref. - -When this minor mode is turned on and a file is saved inside a writable -git repository then it is also committed to a special work-in-progress -ref. - -\(fn &optional ARG)" t nil) - -(defvar global-magit-wip-save-mode nil "\ -Non-nil if Global-Magit-Wip-Save mode is enabled. -See the command `global-magit-wip-save-mode' for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `global-magit-wip-save-mode'.") - -(custom-autoload 'global-magit-wip-save-mode "magit-wip" nil) - -(autoload 'global-magit-wip-save-mode "magit-wip" "\ -Toggle Magit-Wip-Save mode in every possible buffer. -With prefix ARG, turn Global-Magit-Wip-Save mode on if and only if ARG is positive. -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 (rebase-mode) "rebase-mode" "rebase-mode.el" (21205 -;;;;;; 9256)) -;;; Generated autoloads from rebase-mode.el - -(autoload 'rebase-mode "rebase-mode" "\ -Major mode for editing of a Git rebase file. - -Rebase files are generated when you run 'git rebase -i' or run -`magit-interactive-rebase'. They describe how Git should perform -the rebase. See the documentation for git-rebase (e.g., by -running 'man git-rebase' at the command line) for details. - -\(fn)" t nil) - -(add-to-list 'auto-mode-alist '("git-rebase-todo" . rebase-mode)) - -;;;*** - -;;;### (autoloads nil nil ("magit-bisect.el" "magit-key-mode.el" -;;;;;; "magit-pkg.el") (21205 9256 127962)) - -;;;*** - -(provide 'magit-autoloads) -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; coding: utf-8 -;; End: -;;; magit-autoloads.el ends here diff --git a/elpa/magit-1.2.0/magit-bisect.el b/elpa/magit-1.2.0/magit-bisect.el deleted file mode 100644 index cdb6455e..00000000 --- a/elpa/magit-1.2.0/magit-bisect.el +++ /dev/null @@ -1,195 +0,0 @@ -(require 'magit) - -(defvar magit--bisect-last-pos) -(defvar magit--bisect-tmp-file) -(defvar magit--bisect-info nil) -(make-variable-buffer-local 'magit--bisect-info) -(put 'magit--bisect-info 'permanent-local t) - -(defun magit--bisecting-p (&optional required-status) - "Return t if a bisect session is running. -If REQUIRED-STATUS is not nil then the current status must also -match REQUIRED-STATUS." - (and (file-exists-p (concat (magit-git-dir) "BISECT_LOG")) - (or (not required-status) - (eq (plist-get (magit--bisect-info) :status) - required-status)))) - -(defun magit--bisect-info () - (with-current-buffer (magit-find-status-buffer) - (or (if (local-variable-p 'magit--bisect-info) magit--bisect-info) - (list :status (if (magit--bisecting-p) 'running 'not-running))))) - -(defun magit--bisect-cmd (&rest args) - "Run `git bisect ...' and update the status buffer" - (with-current-buffer (magit-find-status-buffer) - (let* ((output (apply 'magit-git-lines (append '("bisect") args))) - (cmd (car args)) - (first-line (car output))) - (save-match-data - (setq magit--bisect-info - (cond ((string= cmd "reset") - (list :status 'not-running)) - ;; Bisecting: 78 revisions left to test after this (roughly 6 steps) - ((string-match "^Bisecting:\\s-+\\([0-9]+\\).+roughly\\s-+\\([0-9]+\\)" first-line) - (list :status 'running - :revs (match-string 1 first-line) - :steps (match-string 2 first-line))) - ;; e2596955d9253a80aec9071c18079705597fa102 is the first bad commit - ((string-match "^\\([a-f0-9]+\\)\\s-.*first bad commit" first-line) - (list :status 'finished - :bad (match-string 1 first-line))) - (t - (list :status 'error))))))) - (magit-refresh)) - -(defun magit--bisect-info-for-status (branch) - "Return bisect info suitable for display in the status buffer" - (let* ((info (magit--bisect-info)) - (status (plist-get info :status))) - (cond ((eq status 'not-running) - (or branch "(detached)")) - ((eq status 'running) - (format "(bisecting; %s revisions & %s steps left)" - (or (plist-get info :revs) "unknown number of") - (or (plist-get info :steps) "unknown number of"))) - ((eq status 'finished) - (format "(bisected: first bad revision is %s)" (plist-get info :bad))) - (t - "(bisecting; unknown error occured)")))) - -(defun magit-bisect-start () - "Start a bisect session" - (interactive) - (if (magit--bisecting-p) - (error "Already bisecting")) - (let ((bad (magit-read-rev "Start bisect with known bad revision" "HEAD")) - (good (magit-read-rev "Good revision" (magit-default-rev)))) - (magit--bisect-cmd "start" bad good))) - -(defun magit-bisect-reset () - "Quit a bisect session" - (interactive) - (unless (magit--bisecting-p) - (error "Not bisecting")) - (magit--bisect-cmd "reset")) - -(defun magit-bisect-good () - "Tell git that the current revision is good during a bisect session" - (interactive) - (unless (magit--bisecting-p 'running) - (error "Not bisecting")) - (magit--bisect-cmd "good")) - -(defun magit-bisect-bad () - "Tell git that the current revision is bad during a bisect session" - (interactive) - (unless (magit--bisecting-p 'running) - (error "Not bisecting")) - (magit--bisect-cmd "bad")) - -(defun magit-bisect-skip () - "Tell git to skip the current revision during a bisect session." - (interactive) - (unless (magit--bisecting-p 'running) - (error "Not bisecting")) - (magit--bisect-cmd "skip")) - -(defun magit-bisect-log () - "Show the bisect log" - (interactive) - (unless (magit--bisecting-p) - (error "Not bisecting")) - (magit-run-git "bisect" "log") - (magit-display-process)) - -(defun magit-bisect-visualize () - "Show the remaining suspects with gitk" - (interactive) - (unless (magit--bisecting-p) - (error "Not bisecting")) - (magit-run-git "bisect" "visualize") - (unless (getenv "DISPLAY") - (magit-display-process))) - -(easy-mmode-defmap magit-bisect-minibuffer-local-map - '(("\C-i" . comint-dynamic-complete-filename)) - "Keymap for minibuffer prompting of rebase command." - :inherit minibuffer-local-map) - -(defvar magit-bisect-mode-history nil - "Previously run bisect commands.") - -(defun magit-bisect-run (command) - "Bisect automatically by running commands after each step" - (interactive - (list - (read-from-minibuffer "Run command (like this): " - "" - magit-bisect-minibuffer-local-map - nil - 'magit-bisect-mode-history))) - (unless (magit--bisecting-p) - (error "Not bisecting")) - (let ((file (make-temp-file "magit-bisect-run")) - buffer) - (with-temp-buffer - (insert "#!/bin/sh\n" command "\n") - (write-region (point-min) (point-max) file)) - (set-file-modes file #o755) - (magit-run-git-async "bisect" "run" file) - (magit-display-process) - (setq buffer (get-buffer magit-process-buffer-name)) - (with-current-buffer buffer - (set (make-local-variable 'magit--bisect-last-pos) 0) - (set (make-local-variable 'magit--bisect-tmp-file) file)) - (set-process-filter (get-buffer-process buffer) 'magit--bisect-run-filter) - (set-process-sentinel (get-buffer-process buffer) 'magit--bisect-run-sentinel))) - -(defun magit--bisect-run-filter (process output) - (with-current-buffer (process-buffer process) - (save-match-data - (let ((inhibit-read-only t) - line new-info) - (insert output) - (goto-char magit--bisect-last-pos) - (beginning-of-line) - (while (< (point) (point-max)) - (cond ( ;; Bisecting: 78 revisions left to test after this (roughly 6 steps) - (looking-at "^Bisecting:\\s-+\\([0-9]+\\).+roughly\\s-+\\([0-9]+\\)") - (setq new-info (list :status 'running - :revs (match-string 1) - :steps (match-string 2)))) - ( ;; e2596955d9253a80aec9071c18079705597fa102 is the first bad commit - (looking-at "^\\([a-f0-9]+\\)\\s-.*first bad commit") - (setq new-info (list :status 'finished - :bad (match-string 1))))) - (forward-line 1)) - (goto-char (point-max)) - (setq magit--bisect-last-pos (point)) - (if new-info - (with-current-buffer (magit-find-status-buffer) - (setq magit--bisect-info new-info) - (magit--bisect-update-status-buffer))))))) - -(defun magit--bisect-run-sentinel (process event) - (if (string-match-p "^finish" event) - (with-current-buffer (process-buffer process) - (delete-file magit--bisect-tmp-file))) - (magit-process-sentinel process event)) - -(defun magit--bisect-update-status-buffer () - (with-current-buffer (magit-find-status-buffer) - (save-excursion - (save-match-data - (let ((inhibit-read-only t)) - (goto-char (point-min)) - (when (search-forward-regexp "Local:" nil t) - (beginning-of-line) - (kill-line) - (insert (format "Local: %s %s" - (propertize (magit--bisect-info-for-status (magit-get-current-branch)) - 'face 'magit-branch) - (abbreviate-file-name default-directory))))))))) - -(provide 'magit-bisect) diff --git a/elpa/magit-1.2.0/magit-blame.el b/elpa/magit-1.2.0/magit-blame.el deleted file mode 100644 index 8518dcb4..00000000 --- a/elpa/magit-1.2.0/magit-blame.el +++ /dev/null @@ -1,303 +0,0 @@ -;;; magit-blame.el --- blame support for magit - -;; Copyright (C) 2012 Rüdiger Sonderfeld -;; Copyright (C) 2012 Yann Hodique -;; Copyright (C) 2011 byplayer -;; Copyright (C) 2010 Alexander Prusov -;; Copyright (C) 2009 Tim Moore -;; Copyright (C) 2008 Linh Dang -;; Copyright (C) 2008 Marius Vollmer - -;; Author: Yann Hodique -;; Keywords: - -;; Magit is free software; you can redistribute it and/or modify it -;; under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. -;; -;; Magit is distributed in the hope that it will be useful, but WITHOUT -;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public -;; License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with Magit. If not, see . - -;;; Commentary: - -;; This code has been backported from Egg (Magit fork) to Magit - -;;; Code: - -(eval-when-compile (require 'cl)) -(require 'magit) - -(defface magit-blame-header - '((t :inherit magit-header)) - "Face for blame header." - :group 'magit-faces) - -(defface magit-blame-sha1 - '((t :inherit (magit-log-sha1 - magit-blame-header))) - "Face for blame sha1." - :group 'magit-faces) - -(defface magit-blame-culprit - '((t :inherit magit-blame-header)) - "Face for blame culprit." - :group 'magit-faces) - -(defface magit-blame-time - '((t :inherit magit-blame-header)) - "Face for blame time." - :group 'magit-faces) - -(defface magit-blame-subject - '((t :inherit (magit-log-message magit-blame-header))) - "Face for blame tag line." - :group 'magit-faces) - -(defconst magit-blame-map - (let ((map (make-sparse-keymap "Magit:Blame"))) - (define-key map (kbd "l") 'magit-blame-locate-commit) - (define-key map (kbd "RET") 'magit-blame-locate-commit) - (define-key map (kbd "q") 'magit-blame-mode) - (define-key map (kbd "n") 'magit-blame-next-chunk) - (define-key map (kbd "p") 'magit-blame-previous-chunk) - map) - "Keymap for an annotated section.\\{magit-blame-map}") - -(defvar magit-blame-buffer-read-only) -(make-variable-buffer-local 'magit-blame-buffer-read-only) - -;;;###autoload -(define-minor-mode magit-blame-mode - "Display blame information inline." - :keymap magit-blame-map - :lighter " blame" - (unless (buffer-file-name) - (error "Current buffer has no associated file!")) - (when (and (buffer-modified-p) - (y-or-n-p (format "save %s first? " (buffer-file-name)))) - (save-buffer)) - - (if magit-blame-mode - (progn - (setq magit-blame-buffer-read-only buffer-read-only) - (magit-blame-file-on (current-buffer)) - (set-buffer-modified-p nil) - (setq buffer-read-only t)) - (magit-blame-file-off (current-buffer)) - (set-buffer-modified-p nil) - (setq buffer-read-only magit-blame-buffer-read-only))) - -(defun magit-blame-file-off (buffer) - (save-excursion - (save-restriction - (with-current-buffer buffer - (widen) - (mapc (lambda (ov) - (if (overlay-get ov :blame) - (delete-overlay ov))) - (overlays-in (point-min) (point-max))))))) - -(defun magit-blame-file-on (buffer) - (magit-blame-file-off buffer) - (save-excursion - (with-current-buffer buffer - (save-restriction - (with-temp-buffer - (magit-git-insert (list "blame" "--porcelain" "--" - (file-name-nondirectory - (buffer-file-name buffer)))) - (magit-blame-parse buffer (current-buffer))))))) - -(defun magit-blame-locate-commit (pos) - "Jump to a commit in the branch history from an annotated blame section." - (interactive "d") - (let ((overlays (overlays-at pos)) - sha1) - (dolist (ov overlays) - (if (overlay-get ov :blame) - (setq sha1 (plist-get (nth 3 (overlay-get ov :blame)) :sha1)))) - (if sha1 - (magit-show-commit sha1)))) - -(defun magit-find-next-overlay-change (BEG END PROP) - "Return the next position after BEG where an overlay matching a -property PROP starts or ends. If there are no matching overlay -boundaries from BEG to END, the return value is nil." - (save-excursion - (goto-char BEG) - (catch 'found - (flet ((overlay-change (pos) - (if (< BEG END) (next-overlay-change pos) - (previous-overlay-change pos))) - (within-bounds-p (pos) - (if (< BEG END) (< pos END) - (> pos END)))) - (let ((ov-pos BEG)) - ;; iterate through overlay changes from BEG to END - (while (within-bounds-p ov-pos) - (let* ((next-ov-pos (overlay-change ov-pos)) - ;; search for an overlay with a PROP property - (next-ov - (let ((overlays (overlays-at next-ov-pos))) - (while (and overlays - (not (overlay-get (car overlays) PROP))) - (setq overlays (cdr overlays))) - (car overlays)))) - (if next-ov - ;; found the next overlay with prop PROP at next-ov-pos - (throw 'found next-ov-pos) - ;; no matching overlay found, keep looking - (setq ov-pos next-ov-pos))))))))) - -(defun magit-blame-next-chunk (pos) - "Go to the next blame chunk." - (interactive "d") - (let ((next-chunk-pos (magit-find-next-overlay-change pos (point-max) :blame))) - (when next-chunk-pos - (goto-char next-chunk-pos)))) - -(defun magit-blame-previous-chunk (pos) - "Go to the previous blame chunk." - (interactive "d") - (let ((prev-chunk-pos (magit-find-next-overlay-change pos (point-min) :blame))) - (when prev-chunk-pos - (goto-char prev-chunk-pos)))) - -(defcustom magit-time-format-string "%Y-%m-%dT%T%z" - "How to format time in magit-blame header." - :group 'magit - :type 'string) - -(defun magit-blame-decode-time (unixtime &optional tz) - "Decode UNIXTIME into (HIGH LOW) format. - -The second argument TZ can be used to add the timezone in (-)HHMM -format to UNIXTIME. UNIXTIME should be either a number -containing seconds since epoch or Emacs's (HIGH LOW -. IGNORED) format." - (when (numberp tz) - (unless (numberp unixtime) - (setq unixtime (float-time unixtime))) - (let* ((ptz (abs tz)) - (min (+ (* (/ ptz 100) 60) - (mod ptz 100)))) - (setq unixtime (+ (* (if (< tz 0) (- min) min) 60) unixtime)))) - - (when (numberp unixtime) - (setq unixtime (seconds-to-time unixtime))) - unixtime) - -(defun magit-blame-format-time-string (format &optional unixtime tz) - "Use FORMAT to format the time UNIXTIME, or now if omitted. - -UNIXTIME is specified as a number containing seconds since epoch -or Emacs's (HIGH LOW . IGNORED) format. The optional argument TZ -can be used to set the time zone. If TZ is a number it is -treated as a (-)HHMM offset to Universal Time. If TZ is not -a number and non-nil the time is printed in UTC. If TZ is nil -the local zime zone is used. The format of the function is -similar to `format-time-string' except for %Z which is not -officially supported at the moment." - (unless unixtime - (setq unixtime (current-time))) - (when (numberp tz) ;; TODO add support for %Z - (setq format (replace-regexp-in-string "%z" (format "%+05d" tz) format))) - (format-time-string format (magit-blame-decode-time unixtime tz) tz)) - -(defun magit-blame-parse (target-buf blame-buf) - "Parse blame-info in buffer BLAME-BUF and decorate TARGET-BUF buffer." - (save-match-data - (let ((blank (propertize " " 'face 'magit-blame-header)) - (nl (propertize "\n" 'face 'magit-blame-header)) - (commit-hash (make-hash-table :test 'equal :size 577)) - commit commit-info old-line new-line num old-file subject author - author-time author-timezone info ov beg end blame) - (with-current-buffer blame-buf - (goto-char (point-min)) - ;; search for a ful commit info - (while (re-search-forward "^\\([0-9a-f]\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)$" nil t) - (setq commit (match-string-no-properties 1) - old-line (string-to-number - (match-string-no-properties 2)) - new-line (string-to-number - (match-string-no-properties 3)) - num (string-to-number - (match-string-no-properties 4))) - ;; was this commit already seen (and stored in the hash)? - (setq commit-info (gethash commit commit-hash)) - ;; Nope, this is the 1st time, the full commit-info follow. - (unless commit-info - (re-search-forward "^author \\(.+\\)$") - (setq author (match-string-no-properties 1)) - (re-search-forward "^author-time \\(.+\\)$") - (setq author-time (string-to-number - (match-string-no-properties 1))) - (re-search-forward "^author-tz \\(.+\\)$") - (setq author-timezone (string-to-number - (match-string-no-properties 1))) - (re-search-forward "^summary \\(.+\\)$") - (setq subject (match-string-no-properties 1)) - (re-search-forward "^filename \\(.+\\)$") - (setq old-file (match-string-no-properties 1)) - (setq commit-info (list :sha1 commit :author author - :author-time author-time - :author-timezone author-timezone - :subject subject :file old-file)) - ;; save it in the hash - (puthash commit commit-info commit-hash)) - ;; add the current blame-block into the list INFO. - (setq info (cons (list old-line new-line num commit-info) - info)))) - ;; now do from beginning - (setq info (nreverse info)) - (with-current-buffer target-buf - ;; for every blame chunk - (dolist (chunk info) - (setq commit-info (nth 3 chunk) - old-line (nth 0 chunk) - new-line (nth 1 chunk) - num (nth 2 chunk) - commit (plist-get commit-info :sha1) - author (plist-get commit-info :author) - author-time (plist-get commit-info :author-time) - author-timezone (plist-get commit-info :author-timezone) - subject (plist-get commit-info :subject)) - - (goto-char (point-min)) - (forward-line (1- new-line)) - - (setq beg (line-beginning-position) - end (save-excursion - (forward-line num) - (line-beginning-position))) - ;; mark the blame chunk - (put-text-property beg end :blame chunk) - - ;; make an overlay with blame info as 'before-string - ;; on the current chunk. - (setq ov (make-overlay beg end)) - (overlay-put ov :blame chunk) - (setq blame (concat - (propertize (substring-no-properties commit 0 8) - 'face 'magit-blame-sha1) - blank - (propertize (format "%-20s" author) - 'face 'magit-blame-culprit) - blank - (propertize (magit-blame-format-time-string - magit-time-format-string - author-time author-timezone) - 'face 'magit-blame-time) - blank - (propertize subject 'face 'magit-blame-subject) - blank nl)) - (overlay-put ov 'before-string blame)))))) - -(provide 'magit-blame) -;;; magit-blame.el ends here diff --git a/elpa/magit-1.2.0/magit-key-mode.el b/elpa/magit-1.2.0/magit-key-mode.el deleted file mode 100644 index 2fcf2964..00000000 --- a/elpa/magit-1.2.0/magit-key-mode.el +++ /dev/null @@ -1,521 +0,0 @@ -(require 'magit) - -(eval-when-compile (require 'cl)) - -(defvar magit-key-mode-key-maps '() - "This will be filled lazily with proper `define-key' built - keymaps as they're requested.") - -(defvar magit-key-mode-buf-name "*magit-key*" - "Name of the buffer.") - -(defvar magit-key-mode-current-args '() - "Will contain the arguments to be passed to git.") - -(defvar magit-key-mode-current-options '() - "Will contain the arguments to be passed to git.") - -(defvar magit-log-mode-window-conf nil - "Will hold the pre-menu configuration of magit.") - -(defvar magit-key-mode-groups - '((logging - (man-page "git-log") - (actions - ("l" "Short" magit-log) - ("L" "Long" magit-log-long) - ("h" "Reflog" magit-reflog) - ("rl" "Ranged short" magit-log-ranged) - ("rL" "Ranged long" magit-log-long-ranged) - ("rh" "Ranged reflog" magit-reflog-ranged)) - (switches - ("-m" "Only merge commits" "--merges") - ("-f" "First parent" "--first-parent") - ("-i" "Case insensitive patterns" "-i") - ("-pr" "Pickaxe regex" "--pickaxe-regex") - ("-n" "Name only" "--name-only") - ("-am" "All match" "--all-match") - ("-al" "All" "--all")) - (arguments - ("=r" "Relative" "--relative=" read-directory-name) - ("=c" "Committer" "--committer=" read-from-minibuffer) - ("=>" "Since" "--since=" read-from-minibuffer) - ("=<" "Before" "--before=" read-from-minibuffer) - ("=s" "Pickaxe search" "-S" read-from-minibuffer) - ("=a" "Author" "--author=" read-from-minibuffer) - ("=g" "Grep" "--grep=" read-from-minibuffer))) - - (running - (actions - ("!" "Command from root" magit-shell-command) - (":" "Git command" magit-git-command) - ("g" "git gui" magit-run-git-gui) - ("k" "gitk" magit-run-gitk))) - - (fetching - (man-page "git-fetch") - (actions - ("f" "Current" magit-fetch-current) - ("a" "All" magit-remote-update) - ("o" "Other" magit-fetch)) - (switches - ("-p" "Prune" "--prune"))) - - (pushing - (man-page "git-push") - (actions - ("P" "Push" magit-push) - ("t" "Push tags" magit-push-tags)) - (switches - ("-f" "Force" "--force") - ("-d" "Dry run" "-n") - ("-u" "Set upstream" "-u"))) - - (pulling - (man-page "git-pull") - (actions - ("F" "Pull" magit-pull)) - (switches - ("-r" "Rebase" "--rebase"))) - - (branching - (man-page "git-branch") - (actions - ("v" "Branch manager" magit-branch-manager) - ("c" "Create" magit-create-branch) - ("r" "Rename" magit-move-branch) - ("k" "Delete" magit-delete-branch) - ("b" "Checkout" magit-checkout))) - - (remoting - (man-page "git-remote") - (actions - ("v" "Branch manager" magit-branch-manager) - ("a" "Add" magit-add-remote) - ("r" "Rename" magit-rename-remote) - ("k" "Remove" magit-remove-remote))) - - (tagging - (man-page "git-tag") - (actions - ("t" "Lightweight" magit-tag) - ("a" "Annotated" magit-annotated-tag)) - (switches - ("-f" "Force" "-f"))) - - (stashing - (man-page "git-stash") - (actions - ("z" "Save" magit-stash) - ("s" "Snapshot" magit-stash-snapshot)) - (switches - ("-k" "Keep index" "--keep-index") - ("-u" "Include untracked files" "--include-untracked") - ("-a" "Include all files" "--all"))) - - (merging - (man-page "git-merge") - (actions - ("m" "Merge" magit-manual-merge)) - (switches - ("-ff" "Fast-forward only" "--ff-only") - ("-nf" "No fast-forward" "--no-ff") - ("-sq" "Squash" "--squash")) - (arguments - ("-st" "Strategy" "--strategy=" read-from-minibuffer))) - - (rewriting - (actions - ("b" "Begin" magit-rewrite-start) - ("s" "Stop" magit-rewrite-stop) - ("a" "Abort" magit-rewrite-abort) - ("f" "Finish" magit-rewrite-finish) - ("*" "Set unused" magit-rewrite-set-unused) - ("." "Set used" magit-rewrite-set-used))) - - (submodule - (man-page "git-submodule") - (actions - ("u" "Update" magit-submodule-update) - ("b" "Both update and init" magit-submodule-update-init) - ("i" "Init" magit-submodule-init) - ("s" "Sync" magit-submodule-sync))) - - (bisecting - (man-page "git-bisect") - (actions - ("b" "Bad" magit-bisect-bad) - ("g" "Good" magit-bisect-good) - ("k" "Skip" magit-bisect-skip) - ("l" "Log" magit-bisect-log) - ("r" "Reset" magit-bisect-reset) - ("s" "Start" magit-bisect-start) - ("u" "Run" magit-bisect-run) - ("v" "Visualize" magit-bisect-visualize)))) - "Holds the key, help, function mapping for the log-mode. If you - modify this make sure you reset `magit-key-mode-key-maps' to - nil.") - -(defun magit-key-mode-delete-group (group) - "Delete a group from `magit-key-mode-key-maps'." - (let ((items (assoc group magit-key-mode-groups))) - (when items - ;; reset the cache - (setq magit-key-mode-key-maps nil) - ;; delete the whole group - (setq magit-key-mode-groups - (delq items magit-key-mode-groups)) - ;; unbind the defun - (magit-key-mode-de-generate group)) - magit-key-mode-groups)) - -(defun magit-key-mode-add-group (group) - "Add a new group to `magit-key-mode-key-maps'. If there's -already a group of that name then this will completely remove it -and put in its place an empty one of the same name." - (when (assoc group magit-key-mode-groups) - (magit-key-mode-delete-group group)) - (setq magit-key-mode-groups - (cons (list group (list 'actions)) magit-key-mode-groups))) - -(defun magit-key-mode-key-defined-p (for-group key) - "If KEY is defined as any of switch, argument or action within -FOR-GROUP then return t" - (catch 'result - (let ((options (magit-key-mode-options-for-group for-group))) - (dolist (type '(actions switches arguments)) - (when (assoc key (assoc type options)) - (throw 'result t)))))) - -(defun magit-key-mode-update-group (for-group thing &rest args) - "Abstraction for setting values in `magit-key-mode-key-maps'." - (let* ((options (magit-key-mode-options-for-group for-group)) - (things (assoc thing options)) - (key (car args))) - (if (cdr things) - (if (magit-key-mode-key-defined-p for-group key) - (error "%s is already defined in the %s group." key for-group) - (setcdr (cdr things) (cons args (cddr things)))) - (setcdr things (list args))) - (setq magit-key-mode-key-maps nil) - things)) - -(defun magit-key-mode-insert-argument (for-group key desc arg read-func) - "Add a new binding (KEY) in FOR-GROUP which will use READ-FUNC -to receive input to apply to argument ARG git is run. DESC should -be a brief description of the binding." - (magit-key-mode-update-group for-group 'arguments key desc arg read-func)) - -(defun magit-key-mode-insert-switch (for-group key desc switch) - "Add a new binding (KEY) in FOR-GROUP which will add SWITCH to git's -command line when it runs. DESC should be a brief description of -the binding." - (magit-key-mode-update-group for-group 'switches key desc switch)) - -(defun magit-key-mode-insert-action (for-group key desc func) - "Add a new binding (KEY) in FOR-GROUP which will run command -FUNC. DESC should be a brief description of the binding." - (magit-key-mode-update-group for-group 'actions key desc func)) - -(defun magit-key-mode-options-for-group (for-group) - "Retrieve the options (switches, commands and arguments) for -the group FOR-GROUP." - (or (cdr (assoc for-group magit-key-mode-groups)) - (error "Unknown group '%s'" for-group))) - -(defun magit-key-mode-help (for-group) - "Provide help for a key (which the user is prompted for) within -FOR-GROUP." - (let* ((opts (magit-key-mode-options-for-group for-group)) - (man-page (cadr (assoc 'man-page opts))) - (seq (read-key-sequence - (format "Enter command prefix%s: " - (if man-page - (format ", `?' for man `%s'" man-page) - "")))) - (actions (cdr (assoc 'actions opts)))) - (cond - ;; if it is an action popup the help for the to-be-run function - ((assoc seq actions) (describe-function (nth 2 (assoc seq actions)))) - ;; if there is "?" show a man page if there is one - ((equal seq "?") - (if man-page - (man man-page) - (error "No man page associated with `%s'" for-group))) - (t (error "No help associated with `%s'" seq))))) - -(defun magit-key-mode-exec-at-point () - "Run action/args/option at point." - (interactive) - (let* ((key (or (get-text-property (point) 'key-group-executor) - (error "Nothing at point to do."))) - (def (lookup-key (current-local-map) key))) - (call-interactively def))) -(defun magit-key-mode-jump-to-next-exec () - "Jump to the next action/args/option point." - (interactive) - (let* ((oldp (point)) - (old (get-text-property oldp 'key-group-executor)) - (p (if (= oldp (point-max)) (point-min) (1+ oldp)))) - (while (let ((new (get-text-property p 'key-group-executor))) - (and (not (= p oldp)) (or (not new) (eq new old)))) - (setq p (if (= p (point-max)) (point-min) (1+ p)))) - (goto-char p) - (skip-chars-forward " "))) - -(defun magit-key-mode-build-keymap (for-group) - "Construct a normal looking keymap for the key mode to use and -put it in magit-key-mode-key-maps for fast lookup." - (let* ((options (magit-key-mode-options-for-group for-group)) - (actions (cdr (assoc 'actions options))) - (switches (cdr (assoc 'switches options))) - (arguments (cdr (assoc 'arguments options))) - (map (make-sparse-keymap))) - (suppress-keymap map 'nodigits) - ;; ret dwim - (define-key map (kbd "RET") 'magit-key-mode-exec-at-point) - ;; tab jumps to the next "button" - (define-key map (kbd "TAB") 'magit-key-mode-jump-to-next-exec) - - ;; all maps should `quit' with `C-g' or `q' - (define-key map (kbd "C-g") `(lambda () - (interactive) - (magit-key-mode-command nil))) - (define-key map (kbd "q") `(lambda () - (interactive) - (magit-key-mode-command nil))) - ;; run help - (define-key map (kbd "?") `(lambda () - (interactive) - (magit-key-mode-help ',for-group))) - - (flet ((defkey (k action) - (when (and (lookup-key map (car k)) - (not (numberp (lookup-key map (car k))))) - (message "Warning: overriding binding for `%s' in %S" - (car k) for-group) - (ding) - (sit-for 2)) - (define-key map (car k) - `(lambda () (interactive) ,action)))) - (when actions - (dolist (k actions) - (defkey k `(magit-key-mode-command ',(nth 2 k))))) - (when switches - (dolist (k switches) - (defkey k `(magit-key-mode-add-option ',for-group ,(nth 2 k))))) - (when arguments - (dolist (k arguments) - (defkey k `(magit-key-mode-add-argument - ',for-group ,(nth 2 k) ',(nth 3 k)))))) - - (push (cons for-group map) magit-key-mode-key-maps) - map)) - -(defvar magit-key-mode-prefix nil - "For internal use. Holds the prefix argument to the command -that brought up the key-mode window, so it can be used by the -command that's eventually invoked.") - -(defun magit-key-mode-command (func) - (let ((args '())) - ;; why can't maphash return a list?! - (maphash (lambda (k v) - (push (concat k (shell-quote-argument v)) args)) - magit-key-mode-current-args) - (let ((magit-custom-options (append args magit-key-mode-current-options)) - (current-prefix-arg (or current-prefix-arg magit-key-mode-prefix))) - (set-window-configuration magit-log-mode-window-conf) - (when func - (call-interactively func)) - (magit-key-mode-kill-buffer)))) - -(defvar magit-key-mode-current-args nil - "A hash-table of current argument set (which will eventually - make it to the git command-line).") - -(defun magit-key-mode-add-argument (for-group arg-name input-func) - (let ((input (funcall input-func (concat arg-name ": ")))) - (puthash arg-name input magit-key-mode-current-args) - (magit-key-mode-redraw for-group))) - -(defvar magit-key-mode-current-options '() - "Current option set (which will eventually make it to the git - command-line).") - -(defun magit-key-mode-add-option (for-group option-name) - "Toggles the appearance of OPTION-NAME in -`magit-key-mode-current-options'." - (if (not (member option-name magit-key-mode-current-options)) - (add-to-list 'magit-key-mode-current-options option-name) - (setq magit-key-mode-current-options - (delete option-name magit-key-mode-current-options))) - (magit-key-mode-redraw for-group)) - -(defun magit-key-mode-kill-buffer () - (interactive) - (kill-buffer magit-key-mode-buf-name)) - -(defvar magit-log-mode-window-conf nil - "Pre-popup window configuration.") - -(defun magit-key-mode (for-group &optional original-opts) - "Mode for magit key selection. All commands, switches and -options can be toggled/actioned with the key combination -highlighted before the description." - (interactive) - ;; save the window config to restore it as was (no need to make this - ;; buffer local) - (setq magit-log-mode-window-conf - (current-window-configuration)) - ;; setup the mode, draw the buffer - (let ((buf (get-buffer-create magit-key-mode-buf-name))) - (delete-other-windows) - (split-window-vertically) - (other-window 1) - (switch-to-buffer buf) - (kill-all-local-variables) - (set (make-local-variable - 'magit-key-mode-current-options) - original-opts) - (set (make-local-variable - 'magit-key-mode-current-args) - (make-hash-table)) - (set (make-local-variable 'magit-key-mode-prefix) current-prefix-arg) - (magit-key-mode-redraw for-group)) - (message - (concat - "Type a prefix key to toggle it. Run 'actions' with their prefixes. " - "'?' for more help."))) - -(defun magit-key-mode-get-key-map (for-group) - "Get or build the keymap for FOR-GROUP." - (or (cdr (assoc for-group magit-key-mode-key-maps)) - (magit-key-mode-build-keymap for-group))) - -(defun magit-key-mode-redraw (for-group) - "(re)draw the magit key buffer." - (let ((buffer-read-only nil) - (old-point (point)) - (is-first (zerop (buffer-size))) - (actions-p nil)) - (erase-buffer) - (make-local-variable 'font-lock-defaults) - (use-local-map (magit-key-mode-get-key-map for-group)) - (setq actions-p (magit-key-mode-draw for-group)) - (delete-trailing-whitespace) - (setq mode-name "magit-key-mode" major-mode 'magit-key-mode) - (if (and is-first actions-p) - (progn (goto-char actions-p) - (magit-key-mode-jump-to-next-exec)) - (goto-char old-point))) - (setq buffer-read-only t) - (fit-window-to-buffer)) - -(defun magit-key-mode-draw-header (header) - "Draw a header with the correct face." - (insert (propertize header 'face 'font-lock-keyword-face) "\n")) - -(defvar magit-key-mode-args-in-cols nil - "When true, draw arguments in columns as with switches and - options.") - -(defun magit-key-mode-draw-args (args) - "Draw the args part of the menu." - (magit-key-mode-draw-buttons - "Args" - args - (lambda (x) - (format "(%s) %s" - (nth 2 x) - (propertize (gethash (nth 2 x) magit-key-mode-current-args "") - 'face 'widget-field))) - (not magit-key-mode-args-in-cols))) - -(defun magit-key-mode-draw-switches (switches) - "Draw the switches part of the menu." - (magit-key-mode-draw-buttons - "Switches" - switches - (lambda (x) - (format "(%s)" (let ((s (nth 2 x))) - (if (member s magit-key-mode-current-options) - (propertize s 'face 'font-lock-warning-face) - s)))))) - -(defun magit-key-mode-draw-actions (actions) - "Draw the actions part of the menu." - (magit-key-mode-draw-buttons "Actions" actions nil)) - -(defun magit-key-mode-draw-buttons (section xs maker - &optional one-col-each) - (when xs - (magit-key-mode-draw-header section) - (magit-key-mode-draw-in-cols - (mapcar (lambda (x) - (let* ((head (propertize (car x) 'face 'font-lock-builtin-face)) - (desc (nth 1 x)) - (more (and maker (funcall maker x))) - (text (format " %s: %s%s%s" - head desc (if more " " "") (or more "")))) - (propertize text 'key-group-executor (car x)))) - xs) - one-col-each))) - -(defun magit-key-mode-draw-in-cols (strings one-col-each) - "Given a list of strings, print in columns (using `insert'). If -ONE-COL-EACH is true then don't columify, but rather, draw each -item on one line." - (let ((longest-act (apply 'max (mapcar 'length strings)))) - (while strings - (let ((str (car strings))) - (let ((padding (make-string (- (+ longest-act 3) (length str)) ? ))) - (insert str) - (if (or one-col-each - (and (> (+ (length padding) ; - (current-column) - longest-act) - (window-width)) - (cdr strings))) - (insert "\n") - (insert padding)))) - (setq strings (cdr strings)))) - (insert "\n")) - -(defun magit-key-mode-draw (for-group) - "Function used to draw actions, switches and parameters. - -Returns the point before the actions part, if any." - (let* ((options (magit-key-mode-options-for-group for-group)) - (switches (cdr (assoc 'switches options))) - (arguments (cdr (assoc 'arguments options))) - (actions (cdr (assoc 'actions options))) - (p nil)) - (magit-key-mode-draw-switches switches) - (magit-key-mode-draw-args arguments) - (when actions (setq p (point-marker))) - (magit-key-mode-draw-actions actions) - (insert "\n") - p)) - -(defun magit-key-mode-de-generate (group) - "Unbind the function for GROUP." - (fmakunbound - (intern (concat "magit-key-mode-popup-" (symbol-name group))))) - -(defun magit-key-mode-generate (group) - "Generate the key-group menu for GROUP" - (let ((opts (magit-key-mode-options-for-group group))) - (eval - `(defun ,(intern (concat "magit-key-mode-popup-" (symbol-name group))) nil - ,(concat "Key menu for " (symbol-name group)) - (interactive) - (magit-key-mode (quote ,group)))))) - -;; create the interactive functions for the key mode popups (which are -;; applied in the top-level key maps) -(mapc (lambda (g) - (magit-key-mode-generate (car g))) - magit-key-mode-groups) - -(provide 'magit-key-mode) diff --git a/elpa/magit-1.2.0/magit-pkg.el b/elpa/magit-1.2.0/magit-pkg.el deleted file mode 100644 index 11ad3243..00000000 --- a/elpa/magit-1.2.0/magit-pkg.el +++ /dev/null @@ -1 +0,0 @@ -(define-package "magit" "1.2.0" "Control Git from Emacs.") diff --git a/elpa/magit-1.2.0/magit-stgit.el b/elpa/magit-1.2.0/magit-stgit.el deleted file mode 100644 index b105b71f..00000000 --- a/elpa/magit-1.2.0/magit-stgit.el +++ /dev/null @@ -1,288 +0,0 @@ -;;; magit-stgit.el --- StGit plug-in for Magit - -;; Copyright (C) 2011 Lluis Vilanova -;; -;; Magit is free software; you can redistribute it and/or modify it -;; under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. -;; -;; Magit is distributed in the hope that it will be useful, but WITHOUT -;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public -;; License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with Magit. If not, see . - -;;; Commentary: - -;; This plug-in provides StGit functionality as a separate component of Magit. - -;; Available actions: -;; - visit: Shows the patch at point in the series (stg show) -;; - apply: Goes to the patch at point in the series (stg goto) -;; - discard: Deletes the marked/at point patch in the series (stg delete) - -;; Available commands: -;; - `magit-stgit-refresh': Refresh the marked/at point patch in the series -;; (stg refresh) -;; - `magit-stgit-repair': Repair the StGit metadata (stg repair) -;; - `magit-stgit-rebase': Rebase the whole series (stg rebase) - -;; TODO: -;; - Let the user select which files must be included in a refresh. -;; - Missing actions for `magit-show-item-or-scroll-up' and -;; `magit-show-item-or-scroll-down'. -;; - Marking a patch is slow and refreshes all buffers, which resets their -;; position (i.e., the buffer is shown from its first line). - -;;; Code: - -(require 'magit) -(eval-when-compile - (require 'cl)) - -;;; Customizables: - -(defcustom magit-stgit-executable "stg" - "The name of the StGit executable." - :group 'magit - :type 'string) - -(defface magit-stgit-applied - '((t :inherit magit-diff-add)) - "Face for an applied stgit patch." - :group 'magit-faces) - -(defface magit-stgit-current - '((t :inherit magit-item-highlight)) - "Face for the current stgit patch." - :group 'magit-faces) - -(defface magit-stgit-other - '((t :inherit magit-diff-del)) - "Face for a non-applied stgit patch." - :group 'magit-faces) - -(defface magit-stgit-marked - '((t :inherit magit-item-mark)) - "Face for a marked stgit patch." - :group 'magit-faces) - -(defface magit-stgit-empty - '((t :inherit magit-item-mark)) - "Face for an empty stgit patch." - :group 'magit-faces) - -;;; Common code: - -(defvar magit-stgit--enabled nil - "Whether this buffer has StGit support.") -(make-variable-buffer-local 'magit-stgit--enabled) - -(defvar magit-stgit-mode) - -(defun magit-stgit--enabled () - "Whether this buffer has StGit support enabled." - (if (assoc 'magit-stgit--enabled (buffer-local-variables)) - magit-stgit--enabled - (setq magit-stgit--enabled - (and magit-stgit-mode - (not (null - (member (concat (magit-get-current-branch) ".stgit") - (mapcar #'(lambda (line) - (string-match "^\\*?\s*\\([^\s]*\\)" - line) - (match-string 1 line)) - (magit-git-lines "branch"))))))))) - -(defun magit-stgit--enabled-reset () - "Reset the StGit enabled state." - (kill-local-variable 'magit-stgit--enabled)) - -(defvar magit-stgit--marked-patch nil - "The (per-buffer) currently marked patch in an StGit series.") -(make-variable-buffer-local 'magit-stgit--marked-patch) - -;;; Menu: - -(easy-menu-define magit-stgit-extension-menu - nil - "StGit extension menu" - '("StGit" - :active (magit-stgit--enabled) - - ["Refresh patch" magit-stgit-refresh - :help "Refresh the contents of a patch in an StGit series"] - ["Repair" magit-stgit-repair - :help "Repair StGit metadata if branch was modified with git commands"] - ["Rebase series" magit-stgit-rebase - :help "Rebase an StGit patch series"] - )) - -(easy-menu-add-item 'magit-mode-menu - '("Extensions") - magit-stgit-extension-menu) - -;;; Series section: - -(defun magit-stgit--wash-patch () - (if (search-forward-regexp "^\\(.\\)\\(.\\) \\([^\s]*\\)\\(\s*# ?\\)\\(.*\\)" - (line-end-position) t) - (let* ((empty-str "[empty] ") - (indent-str (make-string (string-bytes empty-str) ?\ )) - (empty (match-string 1)) - (state (match-string 2)) - (patch (match-string 3)) - (descr (match-string 5))) - (delete-region (line-beginning-position) (line-end-position)) - (insert - (cond ((string= empty "0") - (propertize (concat empty-str " " state " " descr) 'face 'magit-stgit-empty)) - ((string= magit-stgit--marked-patch patch) - (propertize (concat indent-str " " state " " descr) 'face 'magit-stgit-marked)) - ((string= state "+") - (concat indent-str " " (propertize state 'face 'magit-stgit-applied) " " descr)) - ((string= state ">") - (propertize (concat indent-str " " state " " descr) 'face 'magit-stgit-current)) - ((string= state "-") - (concat indent-str " " (propertize state 'face 'magit-stgit-other) " " descr)))) - (goto-char (line-beginning-position)) - (magit-with-section patch 'series - (magit-set-section-info patch) - (goto-char (line-end-position))) - (forward-line)) - (delete-region (line-beginning-position) (1+ (line-end-position)))) - t) - -(defun magit-stgit--wash-series () - (let ((magit-old-top-section nil)) - (magit-wash-sequence #'magit-stgit--wash-patch))) - -(magit-define-inserter series () - (when (executable-find magit-stgit-executable) - (magit-insert-section 'series - "Series:" 'magit-stgit--wash-series - magit-stgit-executable "series" "-a" "-d" "-e"))) - -;;; Actions: - -;; Copy of `magit-refresh-commit-buffer' (version 1.0.0) -(defun magit-stgit--refresh-patch-buffer (patch) - (magit-create-buffer-sections - (magit-insert-section nil nil - 'magit-wash-commit - magit-stgit-executable - "show" - patch))) - -;; Copy of `magit-show-commit' (version 1.0.0) -(defun magit-stgit--show-patch (patch &optional scroll) - (when (magit-section-p patch) - (setq patch (magit-section-info patch))) - (let ((dir default-directory) - (buf (get-buffer-create magit-commit-buffer-name))) - (cond ((and (equal magit-currently-shown-commit patch) - ;; if it's empty then the buffer was killed - (with-current-buffer buf - (> (length (buffer-string)) 1))) - (let ((win (get-buffer-window buf))) - (cond ((not win) - (display-buffer buf)) - (scroll - (with-selected-window win - (funcall scroll)))))) - (t - (setq magit-currently-shown-commit patch) - (display-buffer buf) - (with-current-buffer buf - (set-buffer buf) - (goto-char (point-min)) - (magit-mode-init dir 'magit-commit-mode - #'magit-stgit--refresh-patch-buffer patch)))))) - -(magit-add-action (item info "visit") - ((series) - (magit-stgit--show-patch info) - (pop-to-buffer magit-commit-buffer-name))) - -(magit-add-action (item info "apply") - ((series) - (magit-run magit-stgit-executable "goto" info))) - -(magit-add-action (item info "discard") - ((series) - (let ((patch (or magit-stgit--marked-patch info))) - (if (yes-or-no-p (format "Delete patch '%s' in series? " patch)) - (progn - (if (string= magit-stgit--marked-patch patch) - (setq magit-stgit--marked-patch nil)) - (magit-run magit-stgit-executable "delete" patch)))))) - -(defun magit-stgit--set-marked-patch (patch) - (setq magit-stgit--marked-patch - (if (string= magit-stgit--marked-patch patch) - nil - patch))) - -(magit-add-action (item info "mark") - ((series) - (magit-stgit--set-marked-patch info) - (magit-refresh-all))) - -;;; Commands: - -(defun magit-stgit-refresh () - "Refresh the contents of a patch in an StGit series. -If there is no marked patch in the series, refreshes the current -patch. -Otherwise, refreshes the marked patch." - (interactive) - (if magit-stgit--marked-patch - (magit-run magit-stgit-executable "refresh" "-p" magit-stgit--marked-patch) - (magit-run magit-stgit-executable "refresh"))) - -(defun magit-stgit-repair () - "Repair StGit metadata if branch was modified with git commands. -In the case of Git commits these will be imported as new patches -into the series." - (interactive) - (message "Repairing series...") - (magit-run magit-stgit-executable "repair") - (message "")) - -(defun magit-stgit-rebase () - "Rebase an StGit patch series." - (interactive) - (if (magit-get-current-remote) - (progn - (if (yes-or-no-p "Update remotes? ") - (progn - (message "Updating remotes...") - (magit-run-git-async "remote" "update"))) - (magit-run magit-stgit-executable "rebase" - (format "remotes/%s/%s" - (magit-get-current-remote) - (magit-get-current-branch)))))) - -;;;###autoload -(define-minor-mode magit-stgit-mode "StGit support for Magit" - :lighter " Stg" :require 'magit-stgit - (or (derived-mode-p 'magit-mode) - (error "This mode only makes sense with magit")) - (if magit-stgit-mode - (progn - (add-hook 'magit-after-insert-stashes-hook 'magit-insert-series nil t)) - (progn - (remove-hook 'magit-after-insert-stashes-hook 'magit-insert-series t))) - (when (called-interactively-p 'any) - (magit-refresh))) - -;;;###autoload -(defun turn-on-magit-stgit () - "Unconditionally turn on `magit-stgit-mode'." - (magit-stgit-mode 1)) - -(provide 'magit-stgit) -;;; magit-stgit.el ends here diff --git a/elpa/magit-1.2.0/magit-svn.el b/elpa/magit-1.2.0/magit-svn.el deleted file mode 100644 index d08c048d..00000000 --- a/elpa/magit-1.2.0/magit-svn.el +++ /dev/null @@ -1,240 +0,0 @@ -;;; magit-svn.el --- git-svn plug-in for Magit - -;; Copyright (C) 2008 Alex Ott -;; Copyright (C) 2009 Alexey Voinov -;; Copyright (C) 2009 John Wiegley -;; Copyright (C) 2008 Linh Dang -;; Copyright (C) 2008 Marcin Bachry -;; Copyright (C) 2008, 2009 Marius Vollmer -;; Copyright (C) 2010 Yann Hodique -;; -;; Magit is free software; you can redistribute it and/or modify it -;; under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. -;; -;; Magit is distributed in the hope that it will be useful, but WITHOUT -;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public -;; License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with Magit. If not, see . - -;;; Commentary: - -;; This plug-in provides git-svn functionality as a separate component of Magit - -;;; Code: - -(require 'magit) -(eval-when-compile - (require 'cl)) - -;; git svn commands - -(defun magit-svn-find-rev (rev &optional branch) - (interactive - (list (read-string "SVN revision: ") - (if current-prefix-arg - (read-string "In branch: ")))) - (let* ((sha (apply 'magit-git-string - `("svn" - "find-rev" - ,(concat "r" rev) - ,@(when branch (list branch)))))) - (if sha - (magit-show-commit - (magit-with-section sha 'commit - (magit-set-section-info sha) - sha)) - (error "Revision %s could not be mapped to a commit" rev)))) - -(defun magit-svn-create-branch (name) - (interactive "sBranch name: ") - (magit-run-git "svn" "branch" name)) - -(defun magit-svn-rebase () - (interactive) - (magit-run-git-async "svn" "rebase")) - -(defun magit-svn-dcommit () - (interactive) - (magit-run-git-async "svn" "dcommit")) - -(defun magit-svn-enabled () - (not (null (magit-svn-get-ref-info t)))) - -(defun magit-svn-expand-braces-in-branches (branch) - (if (not (string-match "\\(.+\\){\\(.+,.+\\)}\\(.*\\):\\(.*\\)\\\*" branch)) - (list branch) - (let ((prefix (match-string 1 branch)) - (suffix (match-string 3 branch)) - (rhs (match-string 4 branch)) - (pieces (split-string (match-string 2 branch) ","))) - (mapcar (lambda (p) (concat prefix p suffix ":" rhs p)) pieces)))) - -(defun magit-svn-get-local-ref (url) - (let* ((branches (cons (magit-get "svn-remote" "svn" "fetch") - (magit-get-all "svn-remote" "svn" "branches"))) - (branches (apply 'nconc - (mapcar 'magit-svn-expand-braces-in-branches - branches))) - (base-url (magit-get "svn-remote" "svn" "url")) - (result nil)) - (while branches - (let* ((pats (split-string (pop branches) ":")) - (src (replace-regexp-in-string "\\*" "\\\\(.*\\\\)" (car pats))) - (dst (replace-regexp-in-string "\\*" "\\\\1" (cadr pats))) - (base-url (replace-regexp-in-string "\\+" "\\\\+" base-url)) - (base-url (replace-regexp-in-string "//.+@" "//" base-url)) - (pat1 (concat "^" src "$")) - (pat2 (cond ((equal src "") (concat "^" base-url "$")) - (t (concat "^" base-url "/" src "$"))))) - (cond ((string-match pat1 url) - (setq result (replace-match dst nil nil url)) - (setq branches nil)) - ((string-match pat2 url) - (setq result (replace-match dst nil nil url)) - (setq branches nil))))) - result)) - -(defvar magit-svn-get-ref-info-cache nil - "A cache for svn-ref-info. -As `magit-get-svn-ref-info' might be considered a quite -expensive operation a cache is taken so that `magit-status' -doesn't repeatedly call it.") - -(defun magit-svn-get-ref-info (&optional use-cache) - "Gather details about the current git-svn repository. -Return nil if there isn't one. Keys of the alist are ref-path, -trunk-ref-name and local-ref-name. -If USE-CACHE is non-nil then return the value of `magit-get-svn-ref-info-cache'." - (if (and use-cache magit-svn-get-ref-info-cache) - magit-svn-get-ref-info-cache - (let* ((fetch (magit-get "svn-remote" "svn" "fetch")) - (url) - (revision)) - (when fetch - (let* ((ref (cadr (split-string fetch ":"))) - (ref-path (file-name-directory ref)) - (trunk-ref-name (file-name-nondirectory ref))) - (set (make-local-variable - 'magit-svn-get-ref-info-cache) - (list - (cons 'ref-path ref-path) - (cons 'trunk-ref-name trunk-ref-name) - ;; get the local ref from the log. This is actually - ;; the way that git-svn does it. - (cons 'local-ref - (with-temp-buffer - (insert (or (magit-git-string "log" "--first-parent" - "--grep" "git-svn" "-1") - "")) - (goto-char (point-min)) - (cond ((re-search-forward "git-svn-id: \\(.+/.+?\\)@\\([0-9]+\\)" nil t) - (setq url (match-string 1) - revision (match-string 2)) - (magit-svn-get-local-ref url)) - (t - (setq url (magit-get "svn-remote" "svn" "url")) - nil)))) - (cons 'revision revision) - (cons 'url url)))))))) - -(defun magit-svn-get-ref (&optional use-cache) - "Get the best guess remote ref for the current git-svn based branch. -If USE-CACHE is non nil, use the cached information." - (let ((info (magit-svn-get-ref-info use-cache))) - (cdr (assoc 'local-ref info)))) - -(magit-define-inserter svn-unpulled (&optional use-cache) - (when (magit-svn-enabled) - (apply #'magit-git-section - 'svn-unpulled "Unpulled commits (SVN):" 'magit-wash-log "log" - (append magit-git-log-options - (list - (format "HEAD..%s" (magit-svn-get-ref use-cache))))))) - -(magit-define-inserter svn-unpushed (&optional use-cache) - (when (magit-svn-enabled) - (apply #'magit-git-section - 'svn-unpushed "Unpushed commits (SVN):" 'magit-wash-log "log" - (append magit-git-log-options - (list - (format "%s..HEAD" (magit-svn-get-ref use-cache))))))) - -(magit-define-section-jumper svn-unpushed "Unpushed commits (SVN)") - -(defun magit-svn-remote-string () - (let ((svn-info (magit-svn-get-ref-info))) - (when svn-info - (concat (cdr (assoc 'url svn-info)) - " @ " - (cdr (assoc 'revision svn-info)))))) - -(defun magit-svn-remote-update () - (interactive) - (when (magit-svn-enabled) - (magit-run-git-async "svn" "fetch"))) - -(easy-menu-define magit-svn-extension-menu - nil - "Git SVN extension menu" - '("Git SVN" - :visible magit-svn-mode - ["Create branch" magit-svn-create-branch (magit-svn-enabled)] - ["Rebase" magit-svn-rebase (magit-svn-enabled)] - ["Fetch" magit-svn-remote-update (magit-svn-enabled)] - ["Commit" magit-svn-dcommit (magit-svn-enabled)])) - -(easy-menu-add-item 'magit-mode-menu - '("Extensions") - magit-svn-extension-menu) - -;; add the group and its keys -(progn - ;; (re-)create the group - (magit-key-mode-add-group 'svn) - - (magit-key-mode-insert-action 'svn "r" "Rebase" 'magit-svn-rebase) - (magit-key-mode-insert-action 'svn "c" "DCommit" 'magit-svn-dcommit) - (magit-key-mode-insert-action 'svn "f" "Fetch" 'magit-svn-remote-update) - (magit-key-mode-insert-action 'svn "s" "Find rev" 'magit-svn-find-rev) - (magit-key-mode-insert-action 'svn "B" "Create branch" 'magit-svn-create-branch) - - ;; generate and bind the menu popup function - (magit-key-mode-generate 'svn)) - -(defvar magit-svn-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "N") 'magit-key-mode-popup-svn) - map)) - -;;;###autoload -(define-minor-mode magit-svn-mode "SVN support for Magit" - :lighter " SVN" :require 'magit-svn :keymap 'magit-svn-mode-map - (or (derived-mode-p 'magit-mode) - (error "This mode only makes sense with magit")) - (let ((unpulled-hook (lambda () (magit-insert-svn-unpulled t))) - (unpushed-hook (lambda () (magit-insert-svn-unpushed t))) - (remote-hook 'magit-svn-remote-string)) - (if magit-svn-mode - (progn - (add-hook 'magit-after-insert-unpulled-commits-hook unpulled-hook nil t) - (add-hook 'magit-after-insert-unpushed-commits-hook unpushed-hook nil t) - (add-hook 'magit-remote-string-hook remote-hook nil t)) - (progn - (remove-hook 'magit-after-insert-unpulled-commits-hook unpulled-hook t) - (remove-hook 'magit-after-insert-unpushed-commits-hook unpushed-hook t) - (remove-hook 'magit-remote-string-hook remote-hook t))) - (when (called-interactively-p 'any) - (magit-refresh)))) - -;;;###autoload -(defun turn-on-magit-svn () - "Unconditionally turn on `magit-svn-mode'." - (magit-svn-mode 1)) - -(provide 'magit-svn) -;;; magit-svn.el ends here diff --git a/elpa/magit-1.2.0/magit-topgit.el b/elpa/magit-1.2.0/magit-topgit.el deleted file mode 100644 index 6f0080cc..00000000 --- a/elpa/magit-1.2.0/magit-topgit.el +++ /dev/null @@ -1,191 +0,0 @@ -;;; magit-topgit.el --- topgit plug-in for Magit - -;; Copyright (C) 2010 Nathan Weizenbaum -;; Copyright (C) 2010 Yann Hodique -;; -;; Magit is free software; you can redistribute it and/or modify it -;; under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. -;; -;; Magit is distributed in the hope that it will be useful, but WITHOUT -;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public -;; License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with Magit. If not, see . - -;;; Commentary: - -;; This plug-in provides topgit functionality as a separate component of Magit - -;;; Code: - -(require 'magit) -(eval-when-compile - (require 'cl)) - -(defcustom magit-topgit-executable "tg" - "The name of the TopGit executable." - :group 'magit - :type 'string) - -(defcustom magit-topgit-branch-prefix "t/" - "Convention prefix for topic branch creation." - :group 'magit - :type 'string) - -(defface magit-topgit-current - '((t :weight bold :inherit magit-branch)) - "Face for section titles." - :group 'magit-faces) - -;;; Topic branches (using topgit) - -(defun magit-topgit-in-topic-p () - (and (file-exists-p ".topdeps") - (executable-find magit-topgit-executable))) - -(defun magit-topgit-create-branch (branch parent) - (when (zerop (or (string-match magit-topgit-branch-prefix branch) -1)) - (magit-run* (list magit-topgit-executable "create" - branch (magit-rev-to-git parent)) - nil nil nil t) - t)) - -(defun magit-topgit-pull () - (when (magit-topgit-in-topic-p) - (magit-run* (list magit-topgit-executable "update") - nil nil nil t) - t)) - -(defun magit-topgit-push () - (when (magit-topgit-in-topic-p) - (let* ((branch (or (magit-get-current-branch) - (error "Don't push a detached head. That's gross"))) - (remote (magit-get "topgit" "remote")) - (push-remote (if (or current-prefix-arg (not remote)) - (magit-read-remote (format "Push %s to" branch)) - remote))) - (when (and (not remote) - (not current-prefix-arg)) - (magit-set push-remote "topgit" "remote")) - (magit-run magit-topgit-executable "push" "-r" push-remote)) - t)) - -(defun magit-topgit-remote-update (&optional remote) - (when (magit-topgit-in-topic-p) - (let* ((remote (magit-get "topgit" "remote")) - (remote-update (if (or current-prefix-arg (not remote)) - (magit-read-remote) - remote))) - (if (and (not remote) - (not current-prefix-arg)) - (progn - (magit-set remote-update "topgit" "remote") - (magit-run magit-topgit-executable "remote" - "--populate" remote-update))) - (magit-run magit-topgit-executable "remote" remote-update)) - ;; We return nil anyway, as we also want regular "git remote update" to - ;; happen - nil)) - -(defun magit-topgit-parse-flags (flags-string) - (let ((flags (string-to-list flags-string)) - (void-flag ?\ )) - (list :current (not (eq (nth 0 flags) void-flag)) - :empty (not (eq (nth 1 flags) void-flag))))) - -(defun magit-topgit-wash-topic () - (let ((fmt "^\\(.\\{7\\}\\)\\s-\\(\\S-+\\)\\s-+\\(.*\\)")) - (if (search-forward-regexp fmt (line-end-position) t) - (let ((flags (magit-topgit-parse-flags (match-string 1))) - (topic (match-string 2))) - (goto-char (line-beginning-position)) - (delete-char 8) - (insert "\t") - (goto-char (line-beginning-position)) - (magit-with-section topic 'topic - (magit-set-section-info topic) - (let ((beg (1+ (line-beginning-position))) - (end (line-end-position))) - (when (plist-get flags :current) - (put-text-property beg end 'face 'magit-topgit-current)) - (when (plist-get flags :empty) - (put-text-property beg end 'face `(:strike-through t :inherit ,(get-text-property beg 'face))))) - (forward-line))) - (delete-region (line-beginning-position) (1+ (line-end-position)))) - t)) - -(defun magit-topgit-wash-topics () - (let ((magit-old-top-section nil)) - (magit-wash-sequence #'magit-topgit-wash-topic))) - -(defun magit-topgit-section (section title washer &rest args) - (when (executable-find magit-topgit-executable) - (let ((magit-git-executable magit-topgit-executable) - (magit-git-standard-options nil)) - (apply 'magit-git-section section title washer args)))) - -(magit-define-inserter topics () - (magit-topgit-section 'topics - "Topics:" 'magit-topgit-wash-topics - "summary")) - -(magit-add-action (item info "discard") - ((topic) - (when (yes-or-no-p "Discard topic? ") - (magit-run* (list magit-topgit-executable "delete" "-f" info) - nil nil nil t)))) - -(magit-add-action (item info "visit") - ((topic) - (magit-checkout info))) - -(defun magit-topgit-get-top-bases-color (suffix) - (list nil nil)) - -(defun magit-topgit-get-remote-top-bases-color (suffix) - (when (string-match "^\\(?:[^/]+\\)/top-bases" suffix) - (list nil nil))) - -(defconst magit-topgit-ignored-namespace - '("top-bases" magit-topgit-get-top-bases-color)) - -;;;###autoload -(define-minor-mode magit-topgit-mode "Topgit support for Magit" - :lighter " Topgit" :require 'magit-topgit - (or (derived-mode-p 'magit-mode) - (error "This mode only makes sense with magit")) - (if magit-topgit-mode - (progn - (add-hook 'magit-after-insert-stashes-hook 'magit-insert-topics nil t) - (add-hook 'magit-create-branch-command-hook 'magit-topgit-create-branch nil t) - (add-hook 'magit-pull-command-hook 'magit-topgit-pull nil t) - (add-hook 'magit-remote-update-command-hook 'magit-topgit-remote-update nil t) - (add-hook 'magit-push-command-hook 'magit-topgit-push nil t) - ;; hide refs for top-bases namespace in any remote - (add-hook 'magit-log-remotes-color-hook - 'magit-topgit-get-remote-top-bases-color) - ;; hide refs in the top-bases namespace, as they're not meant for the user - (add-to-list 'magit-refs-namespaces magit-topgit-ignored-namespace)) - (progn - (remove-hook 'magit-after-insert-stashes-hook 'magit-insert-topics t) - (remove-hook 'magit-create-branch-command-hook 'magit-topgit-create-branch t) - (remove-hook 'magit-pull-command-hook 'magit-topgit-pull t) - (remove-hook 'magit-remote-update-command-hook 'magit-topgit-remote-update t) - (remove-hook 'magit-push-command-hook 'magit-topgit-push t) - (remove-hook 'magit-log-remotes-color-hook - 'magit-topgit-get-remote-top-bases-color) - (delete magit-topgit-ignored-namespace magit-refs-namespaces))) - (when (called-interactively-p 'any) - (magit-refresh))) - -;;;###autoload -(defun turn-on-magit-topgit () - "Unconditionally turn on `magit-topgit-mode'." - (magit-topgit-mode 1)) - -(provide 'magit-topgit) -;;; magit-topgit.el ends here diff --git a/elpa/magit-1.2.0/magit-wip.el b/elpa/magit-1.2.0/magit-wip.el deleted file mode 100644 index 5c198404..00000000 --- a/elpa/magit-1.2.0/magit-wip.el +++ /dev/null @@ -1,153 +0,0 @@ -;;; magit-wip.el --- git-wip plug-in for Magit - -;; Copyright (C) 2012 Jonas Bernoulli -;; Copyright (C) 2012 Ryan C. Thompson - -;; Maintainer: Jonas Bernoulli - -;; 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. - -;; The global mode `magit-wip-mode' provides highlighting of wip refs in -;; Magit buffers while the local mode `magit-wip-save-mode' commits to -;; such a ref when saving a file-visiting buffer. - -;; To enable `magit-wip-save-mode' enable `global-magit-wip-save-mode' -;; and use the Magit extension mechanism to select the repositories in -;; which you want to use a work-in-progress ref. Usually you also want -;; to enable `magit-wip-mode'. -;; -;; (magit-wip-mode 1) -;; (global-magit-wip-save-mode 1) -;; -;; $ git config --add magit.extension wip-save # or -;; $ git config --global --add magit.extension wip-save - -;; Note that `global-magit-wip-save-mode' is the only mode that uses the -;; extension mechanism for file-visiting buffers all other global modes -;; making use of it to turn on local modes in Magit buffers. - -;;; Code: - -(require 'magit) -(require 'format-spec) - -;;; Magit Wip Mode. - -(defface magit-log-head-label-wip - '((((class color) (background light)) - :box t - :background "Grey95" - :foreground "LightSkyBlue3") - (((class color) (background dark)) - :box t - :background "Grey07" - :foreground "LightSkyBlue4")) - "Face for git-wip labels shown in log buffer." - :group 'magit-faces) - -(defun magit-log-get-wip-color (suffix) - (list (concat "(WIP) " suffix) - 'magit-log-head-label-wip)) - -(defconst magit-wip-refs-namespace - '("wip" magit-log-get-wip-color)) - -;;;###autoload -(define-minor-mode magit-wip-mode - "In Magit log buffers; give wip refs a special appearance." - :group 'magit - :global t - (if magit-wip-mode - (add-to-list 'magit-refs-namespaces magit-wip-refs-namespace 'append) - (setq magit-refs-namespaces - (delete magit-wip-refs-namespace magit-refs-namespaces)))) - -;;; Magit Wip Save Mode. - -(defcustom magit-wip-commit-message "WIP %r" - "Commit message for git-wip commits. - -The following `format'-like specs are supported: -%f the full name of the file being saved, and -%r the name of the file being saved, relative to the repository root -%g the root of the git repository." - :group 'magit - :type 'string) - -(defcustom magit-wip-echo-area-message "Wrote %f (wip)" - "Message shown in the echo area after creating a git-wip commit. - -The following `format'-like specs are supported: -%f the full name of the file being saved, and -%r the name of the file being saved, relative to the repository root. -%g the root of the git repository." - :group 'magit - :type '(choice (const :tag "No message" nil) string)) - -(defvar magit-wip-save-mode-lighter " Wip") - -;;;###autoload -(define-minor-mode magit-wip-save-mode - "Magit support for committing to a work-in-progress ref. - -When this minor mode is turned on and a file is saved inside a writable -git repository then it is also committed to a special work-in-progress -ref." - :lighter magit-wip-save-mode-lighter - (if magit-wip-save-mode - (add-hook 'after-save-hook 'magit-wip-save-safe t t) - (remove-hook 'after-save-hook 'magit-wip-save-safe t))) - -;;;###autoload -(define-globalized-minor-mode global-magit-wip-save-mode - magit-wip-save-mode turn-on-magit-wip-save - :group 'magit) - -(defun turn-on-magit-wip-save () - (when (and (buffer-file-name) - (magit-get-top-dir default-directory) - (member "wip-save" (magit-get-all "magit.extension"))) - (if (= (magit-git-exit-code "wip" "-h") 0) - (magit-wip-save-mode 1) - (message "Git command 'git wip' cannot be found")))) - -(defun magit-wip-save-safe () - (condition-case err - (magit-wip-save) - (error - (message "Magit WIP got an error: %S" err)))) - -(defun magit-wip-save () - (let* ((top-dir (magit-get-top-dir default-directory)) - (name (file-truename (buffer-file-name))) - (spec `((?r . ,(file-relative-name name top-dir)) - (?f . ,(buffer-file-name)) - (?g . ,top-dir)))) - (when (and top-dir (file-writable-p top-dir)) - (save-excursion ; kludge see https://github.com/magit/magit/issues/441 - (magit-run-git "wip" "save" - (format-spec magit-wip-commit-message spec) - "--editor" "--" name)) - (when magit-wip-echo-area-message - (message (format-spec magit-wip-echo-area-message spec)))))) - -(provide 'magit-wip) -;;; magit-wip.el ends here diff --git a/elpa/magit-1.2.0/magit.el b/elpa/magit-1.2.0/magit.el deleted file mode 100644 index 121ec9c7..00000000 --- a/elpa/magit-1.2.0/magit.el +++ /dev/null @@ -1,6001 +0,0 @@ -;;; magit.el --- control Git from Emacs - -;; Copyright (C) 2010 Aaron Culich. -;; Copyright (C) 2010 Alan Falloon. -;; Copyright (C) 2008, 2010 Alex Ott. -;; Copyright (C) 2008, 2009, 2010 Alexey Voinov. -;; Copyright (C) 2010 Ben Walton. -;; Copyright (C) 2010 Chris Bernard. -;; Copyright (C) 2010 Christian Kluge. -;; Copyright (C) 2008 Daniel Farina. -;; Copyright (C) 2010 David Abrahams. -;; Copyright (C) 2009 David Wallin. -;; Copyright (C) 2009, 2010 Hannu Koivisto. -;; Copyright (C) 2009 Ian Eure. -;; Copyright (C) 2009 Jesse Alama. -;; Copyright (C) 2009 John Wiegley. -;; Copyright (C) 2010 Leo. -;; Copyright (C) 2008, 2009 Marcin Bachry. -;; Copyright (C) 2008, 2009 Marius Vollmer. -;; Copyright (C) 2010 Mark Hepburn. -;; Copyright (C) 2010 Moritz Bunkus. -;; Copyright (C) 2010 Nathan Weizenbaum. -;; Copyright (C) 2010 Oscar Fuentes. -;; Copyright (C) 2009 Pavel Holejsovsky. -;; Copyright (C) 2011-2012 Peter J Weisberg -;; Copyright (C) 2009, 2010 Phil Jackson. -;; Copyright (C) 2010 Philip Weaver. -;; Copyright (C) 2010 Ramkumar Ramachandra. -;; Copyright (C) 2010 Remco van 't Veer. -;; Copyright (C) 2009 René Stadler. -;; Copyright (C) 2010 Robin Green. -;; Copyright (C) 2010 Roger Crew. -;; Copyright (C) 2009, 2010, 2011 Rémi Vanicat. -;; Copyright (C) 2010 Sean Bryant. -;; Copyright (C) 2009, 2011 Steve Purcell. -;; Copyright (C) 2010 Timo Juhani Lindfors. -;; Copyright (C) 2010, 2011 Yann Hodique. -;; Copyright (C) 2010 Ævar Arnfjörð Bjarmason. -;; Copyright (C) 2010 Óscar Fuentes. - -;; Original Author: Marius Vollmer -;; Former Maintainer: Phil Jackson -;; Maintenance Group: https://github.com/organizations/magit/teams/53130 -;; Currently composed of: -;; - Phil Jackson -;; - Peter J Weisberg -;; - Yann Hodique -;; - Rémi Vanicat -;; Version: 1.2.0 -;; Keywords: tools - -;; -;; 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: - -(eval-when-compile (require 'cl)) -(require 'log-edit) -(require 'easymenu) -(require 'diff-mode) - -;; Silences byte-compiler warnings -(eval-and-compile - (unless (fboundp 'declare-function) (defmacro declare-function (&rest args)))) - -(eval-when-compile (require 'view)) -(declare-function view-mode 'view) -(eval-when-compile (require 'iswitchb)) -(eval-when-compile (require 'ido)) -(eval-when-compile (require 'ediff)) - -;; Dummy to be used by the defcustoms when first loading the file. -(eval-when (load eval) - (defalias 'magit-set-variable-and-refresh 'set-default)) - -;;; Code: -(defgroup magit nil - "Controlling Git from Emacs." - :prefix "magit-" - :group 'tools) - -(defcustom magit-git-executable "git" - "The name of the Git executable." - :group 'magit - :type 'string) - -(defcustom magit-gitk-executable (concat (file-name-directory magit-git-executable) - "gitk") - "The name of the Gitk executable." - :group 'magit - :type 'string) - -(defcustom magit-git-standard-options '("--no-pager") - "Standard options when running Git." - :group 'magit - :type '(repeat string)) - -(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 string)) - -(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) - -(defcustom magit-set-upstream-on-push nil - "Non-nil means that `magit-push' may use --set-upstream when pushing a branch. -This only applies if the branch does not have an upstream set yet. -Setting this to t will ask if --set-upstream should be used. -Setting it to 'dontask will always use --set-upstream. -Setting it to 'refuse will refuse to push unless a remote branch has already been set. - ---set-upstream is supported with git > 1.7.0" - :group 'magit - :type '(choice (const :tag "Never" nil) - (const :tag "Ask" t) - (const :tag "Refuse" refuse) - (const :tag "Always" dontask))) - -(defcustom magit-save-some-buffers t - "Non-nil means that \\[magit-status] will save modified buffers before running. -Setting this to t will ask which buffers to save, setting it to 'dontask will -save all modified 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-default-tracking-name-function - 'magit-default-tracking-name-remote-plus-branch - "Specifies the function to use to generate default tracking branch names -when doing a \\[magit-checkout]. - -The default is magit-default-tracking-name-remote-plus-branch, -which generates a tracking name of the form 'REMOTE-BRANCHNAME'." - :group 'magit - :type '(radio (function-item magit-default-tracking-name-remote-plus-branch) - (function-item magit-default-tracking-name-branch-only) - (function :tag "Other"))) - -(defcustom magit-commit-all-when-nothing-staged 'ask - "Determines what \\[magit-log-edit] does when nothing is staged. -Setting this to nil will make it do nothing, setting it to t will -arrange things so that the actual commit command will use the \"--all\" option, -setting it to 'ask will first ask for confirmation whether to do this, -and setting it to 'ask-stage will cause all changes to be staged, -after a confirmation." - :group 'magit - :type '(choice (const :tag "No" nil) - (const :tag "Always" t) - (const :tag "Ask" ask) - (const :tag "Ask to stage everything" ask-stage))) - -(defcustom magit-commit-signoff nil - "Add the \"Signed-off-by:\" line when committing." - :group 'magit - :type 'boolean) - -(defcustom magit-sha1-abbrev-length 7 - "The number of digits to show when a sha1 is displayed in abbreviated form." - :group 'magit - :type 'integer) - -(defcustom magit-log-cutoff-length 100 - "The maximum number of commits to show in the log and whazzup buffers." - :group 'magit - :type 'integer) - -(defcustom magit-log-infinite-length 99999 - "Number of log used to show as maximum for `magit-log-cutoff-length'." - :group 'magit - :type 'integer) - -(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 - :type 'boolean) - -(defcustom magit-process-popup-time -1 - "Popup the process buffer if a command takes longer than this many seconds." - :group 'magit - :type '(choice (const :tag "Never" -1) - (const :tag "Immediately" 0) - (integer :tag "After this many seconds"))) - -(defcustom magit-revert-item-confirm t - "Require acknowledgment before reverting an item." - :group 'magit - :type 'boolean) - -(defcustom magit-log-edit-confirm-cancellation nil - "Require acknowledgment before canceling the log edit buffer." - :group 'magit - :type 'boolean) - -(defcustom magit-remote-ref-format 'branch-then-remote - "What format to use for autocompleting refs, in pariticular for remotes. - -Autocompletion is used by functions like `magit-checkout', -`magit-interactive-rebase' and others which offer branch name -completion. - -The value 'name-then-remote means remotes will be of the -form \"name (remote)\", while the value 'remote-slash-name -means that they'll be of the form \"remote/name\". I.e. something that's -listed as \"remotes/upstream/next\" by \"git branch -l -a\" -will be \"upstream/next\"." - :group 'magit - :type '(choice (const :tag "name (remote)" branch-then-remote) - (const :tag "remote/name" remote-slash-branch))) - -(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 - :type '(choice (const :tag "pipe" nil) - (const :tag "pty" t))) - -(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-iswitchb-completing-read) - (function-item magit-ido-completing-read) - (function-item magit-builtin-completing-read) - (function :tag "Other"))) - -(defcustom magit-create-branch-behaviour 'at-head - "Where magit will create a new branch if not supplied a branchname or ref. - -The value 'at-head means a new branch will be created at the tip -of your current branch, while the value 'at-point means magit -will try to find a valid reference at point..." - :group 'magit - :type '(choice (const :tag "at HEAD" at-head) - (const :tag "at point" at-point))) - -(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 - :type '(radio (function-item switch-to-buffer) - (function-item pop-to-buffer) - (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))) - -(defcustom magit-highlight-whitespace t - "Specifies 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 - :type '(choice (const :tag "Always" t) - (const :tag "Never" nil) - (const :tag "In status buffer" status)) - :set 'magit-set-variable-and-refresh) - -(defcustom magit-highlight-trailing-whitespace t - "Highlight whitespace at the end of a line in diffs. -Used only when `magit-highlight-whitespace' is non-nil." - :group 'magit - :type 'boolean - :set 'magit-set-variable-and-refresh) - -(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 - :type `(repeat (cons (string :tag "Directory regexp") - (choice (const :tag "Tabs" tabs) - (integer :tag "Spaces" :value ,tab-width) - (const :tag "Neither" nil)))) - :set 'magit-set-variable-and-refresh) - -(defcustom magit-diff-refine-hunk nil - "Show fine (word-granularity) differences within diff hunks. - -There are three possible settings: - - nil means to never show fine differences - - t means to only show fine differences for the currently - selected diff hunk - - `all' means to always show fine differences for all displayed diff hunks" - :group 'magit - :type '(choice (const :tag "Never" nil) - (const :tag "Selected only" t) - (const :tag "All" all)) - :set 'magit-set-variable-and-refresh) - -(defvar magit-current-indentation nil - "Indentation highlight used in the current buffer. -This is calculated from `magit-highlight-indentation'.") -(make-variable-buffer-local 'magit-current-indentation) - -(defgroup magit-faces nil - "Customize the appearance of Magit." - :prefix "magit-" - :group 'faces - :group 'magit) - -(defface magit-header - '((t :inherit header-line)) - "Face for generic header lines. - -Many Magit faces inherit from this one by default." - :group 'magit-faces) - -(defface magit-section-title - '((t :inherit magit-header)) - "Face for section titles." - :group 'magit-faces) - -(defface magit-branch - '((t :inherit magit-header)) - "Face for the current branch." - :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-none - '((t :inherit diff-context)) - "Face for lines in a diff that are unchanged." - :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-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-message - '((t)) - "Face for the message element of the log output." - :group 'magit-faces) - -(defface magit-item-highlight - '((t :inherit highlight)) - "Face for highlighting the current item." - :group 'magit-faces) - -(defface magit-item-mark - '((t :inherit secondary-selection)) - "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-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) - -(defvar magit-custom-options '() - "List of custom options to pass to Git. -Do not customize this (used in the `magit-key-mode' implementation).") - -(defvar magit-read-rev-history nil - "The history of inputs to `magit-read-rev'.") - -(defvar magit-buffer-internal nil - "Track associated *magit* buffers. -Do not customize this (used in the `magit-log-edit-mode' implementation -to switch back to the *magit* buffer associated with a given commit -operation after commit).") - -(defvar magit-back-navigation-history nil - "History items that will be visited by successively going \"back\".") -(make-variable-buffer-local 'magit-back-navigation-history) -(put 'magit-back-navigation-history 'permanent-local t) - -(defvar magit-forward-navigation-history nil - "History items that will be visited by successively going \"forward\".") -(make-variable-buffer-local 'magit-forward-navigation-history) -(put 'magit-forward-navigation-history 'permanent-local t) - -(defvar magit-omit-untracked-dir-contents nil - "When non-nil magit will only list an untracked directory, not its contents.") - -(defvar magit-tmp-buffer-name " *magit-tmp*") - -(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-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) - -(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-describe-item) - (define-key map (kbd "!") 'magit-key-mode-popup-running) - (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-no-option) - (define-key map (kbd "RET") 'magit-visit-item) - (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) - (define-key map (kbd "R") 'magit-rebase-step) - (define-key map (kbd "t") 'magit-key-mode-popup-tagging) - (define-key map (kbd "r") 'magit-key-mode-popup-rewriting) - (define-key map (kbd "P") 'magit-key-mode-popup-pushing) - (define-key map (kbd "f") 'magit-key-mode-popup-fetching) - (define-key map (kbd "b") 'magit-key-mode-popup-branching) - (define-key map (kbd "M") 'magit-key-mode-popup-remoting) - (define-key map (kbd "B") 'magit-key-mode-popup-bisecting) - (define-key map (kbd "F") 'magit-key-mode-popup-pulling) - (define-key map (kbd "l") 'magit-key-mode-popup-logging) - (define-key map (kbd "$") 'magit-display-process) - (define-key map (kbd "c") 'magit-log-edit) - (define-key map (kbd "E") 'magit-interactive-rebase) - (define-key map (kbd "e") 'magit-ediff) - (define-key map (kbd "w") 'magit-wazzup) - (define-key map (kbd "q") 'magit-quit-window) - (define-key map (kbd "m") 'magit-key-mode-popup-merging) - (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-toggle-diff-refine-hunk) - map)) - -(defvar magit-commit-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-b") 'magit-show-commit-backward) - (define-key map (kbd "C-c C-f") 'magit-show-commit-forward) - map)) - -(defvar magit-status-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "s") 'magit-stage-item) - (define-key map (kbd "S") 'magit-stage-all) - (define-key map (kbd "u") 'magit-unstage-item) - (define-key map (kbd "U") 'magit-unstage-all) - (define-key map (kbd "i") 'magit-ignore-item) - (define-key map (kbd "I") 'magit-ignore-item-locally) - (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-add-log) - (define-key map (kbd "X") 'magit-reset-working-tree) - (define-key map (kbd "z") 'magit-key-mode-popup-stashing) - map)) - -(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))) - (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) - map)) - -(defvar magit-wazzup-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd ".") 'magit-mark-item) - (define-key map (kbd "=") 'magit-diff-with-mark) - (define-key map (kbd "i") 'magit-ignore-item) - map)) - -(defvar magit-branch-manager-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "c") 'magit-create-branch) - (define-key map (kbd "a") 'magit-add-remote) - (define-key map (kbd "r") 'magit-move-item) - (define-key map (kbd "k") 'magit-discard-item) - (define-key map (kbd "T") 'magit-change-what-branch-tracks) - map)) - -(defvar magit-bug-report-url - "http://github.com/magit/magit/issues") - -(defconst magit-version "1.2.0" - "The version of Magit that you're using.") - -(defun magit-bug-report (str) - "Asks the user to submit a bug report about the error described in STR." -;; XXX - should propose more information to be included. - (message (concat - "Unknown error: %s\n" - "Please, with as much information as possible, file a bug at\n" - "%s\n" - "You are using Magit version %s.") - str magit-bug-report-url magit-version)) - -(defun magit-buffer-switch (buf) - (if (string-match "magit" (buffer-name)) - (switch-to-buffer buf) - (pop-to-buffer buf))) - -;;; Macros - -(defmacro magit-with-refresh (&rest body) - (declare (indent 0)) - `(magit-refresh-wrapper (lambda () ,@body))) - -;;; Git features - -(defvar magit-have-graph 'unset) -(defvar magit-have-decorate 'unset) -(defvar magit-have-abbrev 'unset) -(make-variable-buffer-local 'magit-have-graph) -(put 'magit-have-graph 'permanent-local t) -(make-variable-buffer-local 'magit-have-decorate) -(put 'magit-have-decorate 'permanent-local t) -(make-variable-buffer-local 'magit-have-abbrev) -(put 'magit-have-abbrev 'permanent-local t) - -(defun magit-configure-have-graph () - (if (eq magit-have-graph 'unset) - (let ((res (magit-git-exit-code "log" "--graph" "--max-count=0"))) - (setq magit-have-graph (eq res 0))))) - -(defun magit-configure-have-decorate () - (if (eq magit-have-decorate 'unset) - (let ((res (magit-git-exit-code "log" "--decorate=full" "--max-count=0"))) - (setq magit-have-decorate (eq res 0))))) - -(defun magit-configure-have-abbrev () - (if (eq magit-have-abbrev 'unset) - (let ((res (magit-git-exit-code "log" "--no-abbrev-commit" "--max-count=0"))) - (setq magit-have-abbrev (eq res 0))))) - -;;; Compatibilities - -(eval-and-compile - (defun magit-max-args-internal (function) - "Returns the maximum number of arguments accepted by FUNCTION." - (if (symbolp function) - (setq function (symbol-function function))) - (if (subrp function) - (let ((max (cdr (subr-arity function)))) - (if (eq 'many max) - most-positive-fixnum - max)) - (if (eq 'macro (car-safe function)) - (setq function (cdr function))) - (let ((arglist (if (byte-code-function-p function) - (aref function 0) - (second function)))) - (if (memq '&rest arglist) - most-positive-fixnum - (length (remq '&optional arglist)))))) - - (if (functionp 'start-file-process) - (defalias 'magit-start-process 'start-file-process) - (defalias 'magit-start-process 'start-process)) - - (unless (fboundp 'string-match-p) - (defun string-match-p (regexp string &optional start) - "Same as `string-match' except this function does not -change the match data." - (let ((inhibit-changing-match-data t)) - (string-match regexp string start)))) - - (if (fboundp 'with-silent-modifications) - (defalias 'magit-with-silent-modifications 'with-silent-modifications) - (defmacro magit-with-silent-modifications (&rest body) - "Execute body without changing `buffer-modified-p'. Also, do not -record undo information." - `(set-buffer-modified-p - (prog1 (buffer-modified-p) - (let ((buffer-undo-list t) - before-change-functions - after-change-functions) - ,@body))))) - - (if (>= (magit-max-args-internal 'delete-directory) 2) - (defalias 'magit-delete-directory 'delete-directory) - (defun magit-delete-directory (directory &optional recursive) - "Deletes a directory named DIRECTORY. If RECURSIVE is non-nil, -recursively delete all of DIRECTORY's contents as well. - -Does not follow symlinks." - (if (or (file-symlink-p directory) - (not (file-directory-p directory))) - (delete-file directory) - (if recursive - ;; `directory-files-no-dot-files-regex' borrowed from Emacs 23 - (dolist (file (directory-files directory 'full "\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")) - (magit-delete-directory file recursive))) - (delete-directory directory))))) - -;;; Utilities - -(defun magit-set-variable-and-refresh (symbol value) - "Set SYMBOL to VALUE and call `magit-refresh-all'" - (set-default symbol value) - (magit-refresh-all)) - -(defun magit-iswitchb-completing-read (prompt choices &optional predicate require-match - initial-input hist def) - "iswitchb-based completing-read almost-replacement." - (require 'iswitchb) - (let ((iswitchb-make-buflist-hook - (lambda () - (setq iswitchb-temp-buflist (if (consp (first choices)) - (mapcar #'car choices) - choices))))) - (iswitchb-read-buffer prompt (or initial-input def) require-match))) - -(defun magit-ido-completing-read (prompt choices &optional predicate require-match initial-input hist def) - "ido-based completing-read almost-replacement." - (require 'ido) - (let ((selected (ido-completing-read prompt (if (consp (first choices)) - (mapcar #'car choices) - choices) - predicate require-match initial-input hist def))) - (if (consp (first choices)) - (or (cdr (assoc selected choices)) - selected) - selected))) - -(defun magit-builtin-completing-read (prompt choices &optional predicate require-match - initial-input hist def) - "Magit wrapper for standard `completing-read' function." - (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)) - -(defun magit-completing-read (prompt choices &optional predicate require-match - initial-input hist def) - (funcall magit-completing-read-function prompt choices predicate require-match - initial-input hist def)) - -(defun magit-use-region-p () - (if (fboundp 'use-region-p) - (use-region-p) - (and transient-mark-mode mark-active))) - -(defun magit-goto-line (line) - "Like `goto-line' but doesn't set the mark." - (save-restriction - (widen) - (goto-char 1) - (forward-line (1- line)))) - -(defun magit-trim-line (str) - (if (string= str "") - nil - (if (equal (elt str (- (length str) 1)) ?\n) - (substring str 0 (- (length str) 1)) - str))) - -(defun magit-split-lines (str) - (if (string= str "") - nil - (let ((lines (nreverse (split-string str "\n")))) - (if (string= (car lines) "") - (setq lines (cdr lines))) - (nreverse lines)))) - -(defun magit-git-insert (args) - (insert (magit-git-output args))) - -(defun magit-git-output (args) - (magit-cmd-output magit-git-executable (append magit-git-standard-options args))) - -(defun magit-cmd-insert (cmd args) - (insert (magit-cmd-output cmd args))) - -(defun magit-cmd-output (cmd args) - (let ((cmd-output (with-output-to-string - (with-current-buffer standard-output - (apply #'process-file - cmd - nil (list t nil) nil - args))))) - (replace-regexp-in-string "\e\\[.*?m" "" cmd-output))) - -(defun magit-git-string (&rest args) - (magit-trim-line (magit-git-output args))) - -(defun magit-git-lines (&rest args) - (magit-split-lines (magit-git-output args))) - -(defun magit-git-exit-code (&rest args) - (apply #'process-file magit-git-executable nil nil nil - (append magit-git-standard-options args))) - -(defun magit-file-lines (file) - (when (file-exists-p file) - (with-temp-buffer - (insert-file-contents file) - (let ((rev (nreverse (split-string (buffer-string) "\n")))) - (nreverse (if (equal (car rev) "") - (cdr rev) - rev)))))) - -(defun magit-write-file-lines (file lines) - (with-temp-buffer - (dolist (l lines) - (insert l "\n")) - (write-file file))) - -(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." - (equal (magit-git-string "config" "--bool" (mapconcat 'identity keys ".")) - "true")) - -(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 ".")))) - -(defun magit-remove-conflicts (alist) - (let ((dict (make-hash-table :test 'equal)) - (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-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)) - -(defun magit-git-repo-p (dir) - (file-exists-p (expand-file-name ".git" dir))) - -(defun magit-git-dir () - "Returns the .git directory for the current repository." - (concat (expand-file-name (magit-git-string "rev-parse" "--git-dir")) "/")) - -(defun magit-no-commit-p () - "Return non-nil if there is no commit in the current git repository." - (not (magit-git-string - "rev-list" "HEAD" "--max-count=1"))) - -(defun magit-list-repos* (dir level) - (if (magit-git-repo-p dir) - (list dir) - (apply #'append - (mapcar (lambda (entry) - (unless (or (string= (substring entry -3) "/..") - (string= (substring entry -2) "/.")) - (magit-list-repos* entry (+ level 1)))) - (and (file-directory-p dir) - (< level magit-repo-dirs-depth) - (directory-files dir t nil t)))))) - -(defun magit-list-repos (dirs) - (magit-remove-conflicts - (apply #'append - (mapcar (lambda (dir) - (mapcar #'(lambda (repo) - (cons (file-name-nondirectory repo) - repo)) - (magit-list-repos* dir 0))) - dirs)))) - -(defun magit-get-top-dir (cwd) - (let ((cwd (expand-file-name (file-truename cwd)))) - (when (file-directory-p cwd) - (let* ((default-directory (file-name-as-directory cwd)) - (cdup (magit-git-string "rev-parse" "--show-cdup"))) - (when cdup - (file-name-as-directory (expand-file-name cdup cwd))))))) - -(defun magit-get-ref (ref) - (magit-git-string "symbolic-ref" "-q" ref)) - -(defun magit-get-current-branch () - (let* ((head (magit-get-ref "HEAD")) - (pos (and head (string-match "^refs/heads/" head)))) - (if pos - (substring head 11) - nil))) - -(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")))) - (if (string= remote "") nil 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-exit-code "show-ref" "--verify" ref) 0)) - -(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-nill, 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) - (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) - default-directory))))) - -(defun magit-rev-parse (ref) - "Return the SHA hash for REF." - (magit-git-string "rev-parse" ref)) - -(defun magit-ref-ambiguous-p (ref) - "Return whether or not REF is ambiguous." - ;; If REF is ambiguous, rev-parse just prints errors, - ;; so magit-git-string returns nil. - (not (magit-git-string "rev-parse" "--abbrev-ref" ref))) - -(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-highlight-line-whitespace () - (when (and magit-highlight-whitespace - (or (derived-mode-p 'magit-status-mode) - (not (eq magit-highlight-whitespace 'status)))) - (if (and magit-highlight-trailing-whitespace - (looking-at "^[-+].*?\\([ \t]+\\)$")) - (overlay-put (make-overlay (match-beginning 1) (match-end 1)) - 'face 'magit-whitespace-warning-face)) - (if (or (and (eq magit-current-indentation 'tabs) - (looking-at "^[-+]\\( *\t[ \t]*\\)")) - (and (integerp magit-current-indentation) - (looking-at (format "^[-+]\\([ \t]* \\{%s,\\}[ \t]*\\)" - magit-current-indentation)))) - (overlay-put (make-overlay (match-beginning 1) (match-end 1)) - 'face 'magit-whitespace-warning-face)))) - -(defun magit-put-line-property (prop val) - (put-text-property (line-beginning-position) (line-beginning-position 2) - prop val)) - -(defun magit-format-commit (commit format) - (magit-git-string "log" "--max-count=1" - (concat "--pretty=format:" format) - commit)) - -(defun magit-current-line () - (buffer-substring-no-properties (line-beginning-position) - (line-end-position))) - -(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-file-uptodate-p (file) - (eq (magit-git-exit-code "diff" "--quiet" "--" file) 0)) - -(defun magit-anything-staged-p () - (not (eq (magit-git-exit-code "diff" "--quiet" "--cached") 0))) - -(defun magit-everything-clean-p () - (and (not (magit-anything-staged-p)) - (eq (magit-git-exit-code "diff" "--quiet") 0))) - -(defun magit-commit-parents (commit) - (cdr (split-string (magit-git-string "rev-list" "-1" "--parents" commit)))) - -;; XXX - let the user choose the parent - -(defun magit-choose-parent-id (commit op) - (let* ((parents (magit-commit-parents commit))) - (if (> (length parents) 1) - (error "Can't %s merge commits" op) - nil))) - -;;; Revisions and ranges - -(defvar magit-current-range nil - "The range described by the current buffer. -This is only non-nil in diff and log buffers. - -This has three possible (non-nil) forms. If it's a string REF or -a singleton list (REF), then the range is from REF to the current -working directory state (or HEAD in a log buffer). If it's a -pair (START . END), then the range is START..END.") -(make-variable-buffer-local 'magit-current-range) - -(defun magit-list-interesting-refs (&optional uninteresting) - "Return interesting references as given by `git show-ref'. -Removes references matching UNINTERESTING from the -results. UNINTERESTING can be either a function taking a single -argument or a list of strings used as regexps." - (let ((refs ())) - (dolist (line (magit-git-lines "show-ref")) - (if (string-match "[^ ]+ +\\(.*\\)" line) - (let ((ref (match-string 1 line))) - (cond ((and (functionp uninteresting) - (funcall uninteresting ref))) - ((and (not (functionp uninteresting)) - (loop for i in uninteresting thereis (string-match i ref)))) - (t - (let ((fmt-ref (magit-format-ref ref))) - (when fmt-ref - (push (cons fmt-ref - (replace-regexp-in-string "^refs/heads/" - "" ref)) - refs)))))))) - (nreverse refs))) - -(defun magit-format-ref (ref) - "Convert fully-specified ref REF into its displayable form -according to `magit-remote-ref-format'" - (cond - ((null ref) - nil) - ((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)) - (format "%s/%s" - (match-string 1 ref) - (match-string 2 ref)))))) - -(defun magit-tree-contents (treeish) - "Returns a list of all files under TREEISH. TREEISH can be a tree, -a commit, or any reference to one of those." - (let ((return-value nil)) - (with-temp-buffer - (magit-git-insert (list "ls-tree" "-r" treeish)) - (if (eql 0 (buffer-size)) - (error "%s is not a commit or tree." treeish)) - (goto-char (point-min)) - (while (search-forward-regexp "\t\\(.*\\)" nil 'noerror) - (push (match-string 1) return-value))) - return-value)) - -(defvar magit-uninteresting-refs '("refs/remotes/\\([^/]+\\)/HEAD$" "refs/stash")) - -(defun magit-read-file-from-rev (revision) - (magit-completing-read (format "Retrieve file from %s: " revision) - (magit-tree-contents revision) - nil - 'require-match - nil - 'magit-read-file-hist - (if buffer-file-name - (let ((topdir-length (length (magit-get-top-dir default-directory)))) - (substring (buffer-file-name) topdir-length))))) - -(defun magit-read-rev (prompt &optional default uninteresting) - (let* ((interesting-refs (magit-list-interesting-refs - (or uninteresting magit-uninteresting-refs))) - (reply (magit-completing-read (concat prompt ": ") interesting-refs - nil nil nil 'magit-read-rev-history default)) - (rev (or (cdr (assoc reply interesting-refs)) reply))) - (if (string= rev "") - nil - rev))) - -(defun magit-read-rev-range (op &optional def-beg def-end) - (let ((beg (magit-read-rev (format "%s start" op) - def-beg))) - (if (not beg) - nil - (save-match-data - (if (string-match "^\\(.+\\)\\.\\.\\(.+\\)$" beg) - (cons (match-string 1 beg) (match-string 2 beg)) - (let ((end (magit-read-rev (format "%s end" op) def-end))) - (cons beg end))))))) - -(defun magit-rev-to-git (rev) - (or rev - (error "No revision specified")) - (if (string= rev ".") - (magit-marked-commit) - rev)) - -(defun magit-rev-range-to-git (range) - (or range - (error "No revision range specified")) - (if (stringp range) - range - (if (cdr range) - (format "%s..%s" - (magit-rev-to-git (car range)) - (magit-rev-to-git (cdr range))) - (format "%s" (magit-rev-to-git (car range)))))) - -(defun magit-rev-describe (rev) - (or rev - (error "No revision specified")) - (if (string= rev ".") - "mark" - (magit-name-rev rev))) - -(defun magit-rev-range-describe (range things) - (or range - (error "No revision range specified")) - (if (stringp range) - (format "%s in %s" things range) - (if (cdr range) - (format "%s from %s to %s" things - (magit-rev-describe (car range)) - (magit-rev-describe (cdr range))) - (format "%s at %s" things (magit-rev-describe (car range)))))) - -(defun magit-default-rev (&optional no-trim) - (or (magit-name-rev (magit-commit-at-point t) no-trim) - (let ((branch (magit-guess-branch))) - (if branch - (if (string-match "^refs/\\(.*\\)" branch) - (match-string 1 branch) - branch))))) - -(defun magit-read-remote (&optional prompt def) - "Read the name of a remote. -PROMPT is used as the prompt, and defaults to \"Remote\". -DEF is the default value." - (let* ((prompt (or prompt "Remote")) - (def (or def (magit-guess-remote))) - (remotes (magit-git-lines "remote")) - - (reply (magit-completing-read (concat prompt ": ") remotes - nil nil nil nil def))) - (if (string= reply "") nil reply))) - -(defun magit-read-remote-branch (remote &optional prompt default) - (let* ((prompt (or prompt (format "Remote branch (in %s)" remote))) - (branches (delete nil - (mapcar - (lambda (b) - (and (not (string-match " -> " b)) - (string-match (format "^ *%s/\\(.*\\)$" - (regexp-quote remote)) b) - (match-string 1 b))) - (magit-git-lines "branch" "-r")))) - (reply (magit-completing-read (concat prompt ": ") branches - nil nil nil nil default))) - (if (string= reply "") nil reply))) - -;;; Sections - -;; A buffer in magit-mode is organized into hierarchical sections. -;; These sections are used for navigation and for hiding parts of the -;; buffer. -;; -;; Most sections also represent the objects that Magit works with, -;; such as files, diffs, hunks, commits, etc. The 'type' of a section -;; identifies what kind of object it represents (if any), and the -;; parent and grand-parent, etc provide the context. - -(defstruct magit-section - parent title beginning end children hidden type info - needs-refresh-on-show) - -(defvar magit-top-section nil - "The top section of the current buffer.") -(make-variable-buffer-local 'magit-top-section) -(put 'magit-top-section 'permanent-local t) - -(defvar magit-old-top-section nil) - -(defvar magit-section-hidden-default nil) - -(defun magit-new-section (title type) - "Create a new section with title TITLE and type TYPE in current buffer. - -If not `magit-top-section' exist, the new section will be the new top-section -otherwise, the new-section will be a child of the current top-section. - -If TYPE is nil, the section won't be highlighted." - (let* ((s (make-magit-section :parent magit-top-section - :title title - :type type - :hidden magit-section-hidden-default)) - (old (and magit-old-top-section - (magit-find-section (magit-section-path s) - magit-old-top-section)))) - (if magit-top-section - (push s (magit-section-children magit-top-section)) - (setq magit-top-section s)) - (if old - (setf (magit-section-hidden s) (magit-section-hidden old))) - s)) - -(defun magit-cancel-section (section) - "Delete the section SECTION." - (delete-region (magit-section-beginning section) - (magit-section-end section)) - (let ((parent (magit-section-parent section))) - (if parent - (setf (magit-section-children parent) - (delq section (magit-section-children parent))) - (setq magit-top-section nil)))) - -(defmacro magit-with-section (title type &rest body) - "Create a new section of title TITLE and type TYPE and evaluate BODY there. - -Sections created inside BODY will become children of the new -section. BODY must leave point at the end of the created section. - -If TYPE is nil, the section won't be highlighted." - (declare (indent 2)) - (let ((s (make-symbol "*section*"))) - `(let* ((,s (magit-new-section ,title ,type)) - (magit-top-section ,s)) - (setf (magit-section-beginning ,s) (point)) - ,@body - (setf (magit-section-end ,s) (point)) - (setf (magit-section-children ,s) - (nreverse (magit-section-children ,s))) - ,s))) - -(defun magit-set-section (title type start end) - "Create a new section of title TITLE and type TYPE with specified start and -end positions." - (let ((section (magit-new-section title type))) - (setf (magit-section-beginning section) start) - (setf (magit-section-end section) end) - section)) - -(defun magit-set-section-info (info &optional section) - (setf (magit-section-info (or section magit-top-section)) info)) - -(defun magit-set-section-needs-refresh-on-show (flag &optional section) - (setf (magit-section-needs-refresh-on-show - (or section magit-top-section)) - flag)) - -(defmacro magit-create-buffer-sections (&rest body) - "Empty current buffer of text and Magit's sections, and then evaluate BODY." - (declare (indent 0)) - `(let ((inhibit-read-only t)) - (erase-buffer) - (let ((magit-old-top-section magit-top-section)) - (setq magit-top-section nil) - ,@body - (when (null magit-top-section) - (magit-with-section 'top nil - (insert "(empty)\n"))) - (magit-propertize-section magit-top-section) - (magit-section-set-hidden magit-top-section - (magit-section-hidden magit-top-section))))) - -(defun magit-propertize-section (section) - "Add text-property needed for SECTION." - (put-text-property (magit-section-beginning section) - (magit-section-end section) - 'magit-section section) - (dolist (s (magit-section-children section)) - (magit-propertize-section s))) - -(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-title (car secs))))) - (setq secs (cdr secs))) - (and (car secs) - (magit-find-section (cdr path) (car secs)))))) - -(defun magit-section-path (section) - "Return the path of SECTION." - (if (not (magit-section-parent section)) - '() - (append (magit-section-path (magit-section-parent section)) - (list (magit-section-title section))))) - -(defun magit-find-section-after (pos) - "Find the first section that begins after POS." - (magit-find-section-after* pos (list magit-top-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))) - (do* ((current (or (magit-section-parent section) - section) - next) - (next (if (not (magit-section-hidden current)) - (magit-find-section-before* pos (magit-section-children current))) - (if (not (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-top-section)) - -(defun magit-insert-section (section-title-and-type - buffer-title washer cmd &rest args) - "Run CMD and put its result in a new section. - -SECTION-TITLE-AND-TYPE is either a string that is the title of the section -or (TITLE . TYPE) where TITLE is the title of the section and TYPE is its type. - -If there is no type, or if type is nil, the section won't be highlighted. - -BUFFER-TITLE is the inserted title of the section - -WASHER is a function that will be run after CMD. -The buffer will be narrowed to the inserted text. -It should add sectioning as needed for Magit interaction. - -CMD is an external command that will be run with ARGS as arguments." - (let* ((body-beg nil) - (section-title (if (consp section-title-and-type) - (car section-title-and-type) - section-title-and-type)) - (section-type (if (consp section-title-and-type) - (cdr section-title-and-type) - nil)) - (section - (magit-with-section section-title section-type - (if buffer-title - (insert (propertize buffer-title 'face 'magit-section-title) - "\n")) - (setq body-beg (point)) - (magit-cmd-insert cmd args) - (if (not (eq (char-before) ?\n)) - (insert "\n")) - (if washer - (save-restriction - (narrow-to-region body-beg (point)) - (goto-char (point-min)) - (funcall washer) - (goto-char (point-max))))))) - (if (= body-beg (point)) - (magit-cancel-section section) - (insert "\n")) - section)) - -(defun magit-git-section (section-title-and-type - buffer-title washer &rest args) - "Run git and put its result in a new section. - -see `magit-insert-section' for meaning of the arguments" - (apply #'magit-insert-section - section-title-and-type - buffer-title - washer - magit-git-executable - (append magit-git-standard-options args))) - -(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* ((initial (point)) - (section (magit-current-section)) - (end (- (magit-section-end section) 1)) - (parent (magit-section-parent section)) - (siblings (magit-section-children parent)) - (next-sibling (magit-find-section-after* end siblings))) - (if next-sibling - (magit-goto-section next-sibling) - (magit-goto-next-section)))) - -(defun magit-goto-previous-sibling-section () - "Go to the previous sibling section." - (interactive) - (let* ((section (magit-current-section)) - (beginning (magit-section-beginning section)) - (parent (magit-section-parent section)) - (siblings (magit-section-children parent)) - (previous-sibling (magit-find-section-before* beginning siblings))) - (if previous-sibling - (magit-goto-section previous-sibling) - (magit-goto-parent-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)) - (magit-show-commit section)))) - -(defun magit-goto-section-at-path (path) - "Go to the section described by PATH." - (let ((sec (magit-find-section path magit-top-section))) - (if sec - (goto-char (magit-section-beginning sec)) - (message "No such section")))) - -(defun magit-for-all-sections (func &optional top) - "Run FUNC on TOP and recursively on all its children. - -Default value for TOP is `magit-top-section'" - (let ((section (or top magit-top-section))) - (when section - (funcall func section) - (dolist (c (magit-section-children section)) - (magit-for-all-sections func c))))) - -(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))) - (if (< beg end) - (put-text-property beg end 'invisible hidden))) - (if (not 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 value, the section will be hidden if its 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 'hunk (first (magit-section-context-type (magit-current-section)))) - (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)) - ((notany #'magit-section-hidden (magit-section-children s)) - (magit-section-set-hidden s t)) - (t - (magit-section-expand s)))))) - -(defun magit-section-lineage (s) - "Return list of parent, grand-parents... for section S." - (when s - (cons s (magit-section-lineage (magit-section-parent s))))) - -(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." - (magit-with-refresh - (if all - (magit-section-show-level magit-top-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 there 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 there 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))) - -(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))) - (doc (format "Jump to section `%s'." title))) - `(progn - (defun ,fun () - ,doc - (interactive) - (magit-goto-section-at-path '(,sym))) - (put ',fun 'definition-name ',sym)))) - -(defmacro magit-define-inserter (sym arglist &rest body) - (declare (indent defun)) - (let ((fun (intern (format "magit-insert-%s" sym))) - (before (intern (format "magit-before-insert-%s-hook" sym))) - (after (intern (format "magit-after-insert-%s-hook" sym))) - (doc (format "Insert items for `%s'." sym))) - `(progn - (defvar ,before nil) - (defvar ,after nil) - (defun ,fun ,arglist - ,doc - (run-hooks ',before) - ,@body - (run-hooks ',after)) - (put ',before 'definition-name ',sym) - (put ',after 'definition-name ',sym) - (put ',fun 'definition-name ',sym)))) - -(defvar magit-highlighted-section nil) - -(defun magit-refine-section (section) - "Apply temporary refinements to the display of SECTION. -Refinements can be undone with `magit-unrefine-section'." - (let ((type (and section (magit-section-type section)))) - (cond ((and (eq type 'hunk) - magit-diff-refine-hunk - (not (eq magit-diff-refine-hunk 'all))) - ;; Refine the current hunk to show fine details, using - ;; diff-mode machinery. - (save-excursion - (goto-char (magit-section-beginning magit-highlighted-section)) - (diff-refine-hunk)))))) - -(defun magit-unrefine-section (section) - "Remove refinements to the display of SECTION done by `magit-refine-section'." - (let ((type (and section (magit-section-type section)))) - (cond ((and (eq type 'hunk) - magit-diff-refine-hunk - (not (eq magit-diff-refine-hunk 'all))) - ;; XXX this should be in some diff-mode function, like - ;; `diff-unrefine-hunk' - (remove-overlays (magit-section-beginning section) - (magit-section-end section) - 'diff-mode 'fine))))) - -(defvar magit-highlight-overlay nil) - -(defun magit-highlight-section () - "Highlight current section if it has a type." - (let ((section (magit-current-section))) - (when (not (eq section magit-highlighted-section)) - (when magit-highlighted-section - ;; remove any refinement from previous hunk - (magit-unrefine-section magit-highlighted-section)) - (setq magit-highlighted-section section) - (if (not magit-highlight-overlay) - (let ((ov (make-overlay 1 1))) - (overlay-put ov 'face 'magit-item-highlight) - (setq magit-highlight-overlay ov))) - (if (and section (magit-section-type section)) - (progn - (magit-refine-section section) - (move-overlay magit-highlight-overlay - (magit-section-beginning section) - (magit-section-end section) - (current-buffer))) - (delete-overlay magit-highlight-overlay))))) - -(defun magit-section-context-type (section) - (if (null section) - '() - (let ((c (or (magit-section-type section) - (if (symbolp (magit-section-title section)) - (magit-section-title section))))) - (if c - (cons c (magit-section-context-type - (magit-section-parent section))) - '())))) - -(defun magit-prefix-p (prefix list) - "Returns non-nil if PREFIX is a prefix of LIST. PREFIX and LIST should both be -lists. - -If the car of PREFIX is the symbol '*, then return non-nil if the cdr of PREFIX -is a sublist of LIST (as if '* matched zero or more arbitrary elements of LIST)" - ;;; Very schemish... - (or (null prefix) - (if (eq (car prefix) '*) - (or (magit-prefix-p (cdr prefix) list) - (and (not (null list)) - (magit-prefix-p prefix (cdr list)))) - (and (not (null list)) - (equal (car prefix) (car list)) - (magit-prefix-p (cdr prefix) (cdr list)))))) - -(defmacro magit-section-case (head &rest clauses) - "Make different action depending of current section. - -HEAD is (SECTION INFO &optional OPNAME), - SECTION will be bind to the current section, - INFO will be bind to the info's of the current section, - OPNAME is a string that will be used to describe current action, - -CLAUSES is a list of CLAUSE, each clause is (SECTION-TYPE &BODY) -where SECTION-TYPE describe section where BODY will be run. - -This returns non-nil if some section matches. If the -corresponding body return a non-nil value, it is returned, -otherwise it returns t. - -If no section matches, this returns nil if no OPNAME was given -and throws an error otherwise." - (declare (indent 1)) - (let ((section (car head)) - (info (cadr head)) - (type (make-symbol "*type*")) - (context (make-symbol "*context*")) - (opname (caddr head))) - `(let* ((,section (magit-current-section)) - (,info (and ,section (magit-section-info ,section))) - (,type (and ,section (magit-section-type ,section))) - (,context (magit-section-context-type ,section))) - (cond ,@(mapcar (lambda (clause) - (if (eq (car clause) t) - `(t (or (progn ,@(cdr clause)) - t)) - (let ((prefix (reverse (car clause))) - (body (cdr clause))) - `((magit-prefix-p ',prefix ,context) - (or (progn ,@body) - t))))) - clauses) - ,@(when opname - `(((run-hook-with-args-until-success - ',(intern (format "magit-%s-action-hook" opname)))) - ((not ,type) - (error "Nothing to %s here" ,opname)) - (t - (error "Can't %s a %s" - ,opname - (or (get ,type 'magit-description) - ,type))))))))) - -(defmacro magit-section-action (head &rest clauses) - (declare (indent 1)) - `(magit-with-refresh - (magit-section-case ,head ,@clauses))) - -(defmacro magit-add-action (head &rest clauses) - "Add additional actions to a pre-existing operator. -The syntax is identical to `magit-section-case', except that -OPNAME is mandatory and specifies the operation to which to add -the actions." - (declare (indent 1)) - (let ((section (car head)) - (info (cadr head)) - (type (caddr head))) - `(add-hook ',(intern (format "magit-%s-action-hook" type)) - (lambda () - ,(macroexpand - ;; Don't pass in the opname so we don't recursively - ;; run the hook again, and so we don't throw an - ;; error if no action matches. - `(magit-section-case (,section ,info) - ,@clauses)))))) - -(defun magit-wash-sequence (func) - "Run FUNC until end of buffer is reached. - -FUNC should leave point at the end of the modified region" - (while (and (not (eobp)) - (funcall func)))) - -(defmacro magit-define-command (sym arglist &rest body) - "Macro to define a magit command. -It will define the magit-SYM function having ARGLIST as argument. -It will also define the magit-SYM-command-hook variable. - -The defined function will call the function in the hook in -order until one return non nil. If they all return nil then body will be called. - -It is used to define hookable magit command: command defined by this -function can be enriched by magit extension like magit-topgit and magit-svn" - (declare (indent defun) - (debug (&define name lambda-list - [&optional stringp] ; Match the doc string, if present. - [&optional ("interactive" interactive)] - def-body))) - (let ((fun (intern (format "magit-%s" sym))) - (hook (intern (format "magit-%s-command-hook" sym))) - (doc (format "Command for `%s'." sym)) - (inter nil) - (instr body)) - (when (stringp (car body)) - (setq doc (car body) - instr (cdr body))) - (let ((form (car instr))) - (when (eq (car form) 'interactive) - (setq inter form - instr (cdr instr)))) - `(progn - (defvar ,hook nil) - (defun ,fun ,arglist - ,doc - ,inter - (or (run-hook-with-args-until-success - ',hook ,@(remq '&optional (remq '&rest arglist))) - ,@instr)) - (put ',fun 'definition-name ',sym) - (put ',hook 'definition-name ',sym)))) - -;;; Running commands - -(defun magit-set-mode-line-process (str) - (let ((pr (if str (concat " " str) ""))) - (save-excursion - (magit-for-all-buffers (lambda () - (setq mode-line-process pr)))))) - -(defun magit-process-indicator-from-command (comps) - (if (magit-prefix-p (cons magit-git-executable magit-git-standard-options) - comps) - (setq comps (nthcdr (+ (length magit-git-standard-options) 1) comps))) - (cond ((or (null (cdr comps)) - (not (member (car comps) '("remote")))) - (car comps)) - (t - (concat (car comps) " " (cadr comps))))) - -(defvar magit-process nil) -(defvar magit-process-client-buffer nil) -(defvar magit-process-buffer-name "*magit-process*" - "Buffer name for running git commands.") - -(defun magit-run* (cmd-and-args - &optional logline noerase noerror nowait input) - (if (and magit-process - (get-buffer magit-process-buffer-name)) - (error "Git is already running")) - (let ((cmd (car cmd-and-args)) - (args (cdr cmd-and-args)) - (dir default-directory) - (buf (get-buffer-create magit-process-buffer-name)) - (successp nil)) - (magit-set-mode-line-process - (magit-process-indicator-from-command cmd-and-args)) - (setq magit-process-client-buffer (current-buffer)) - (with-current-buffer buf - (view-mode 1) - (set (make-local-variable 'view-no-disable-on-exit) t) - (setq view-exit-action - (lambda (buffer) - (with-current-buffer buffer - (bury-buffer)))) - (setq buffer-read-only t) - (let ((inhibit-read-only t)) - (setq default-directory dir) - (if noerase - (goto-char (point-max)) - (erase-buffer)) - (insert "$ " (or logline - (mapconcat 'identity cmd-and-args " ")) - "\n") - (cond (nowait - (setq magit-process - (let ((process-connection-type magit-process-connection-type)) - (apply 'magit-start-process cmd buf cmd args))) - (set-process-sentinel magit-process 'magit-process-sentinel) - (set-process-filter magit-process 'magit-process-filter) - (when input - (with-current-buffer input - (process-send-region magit-process - (point-min) (point-max))) - (process-send-eof magit-process) - (sit-for 0.1 t)) - (cond ((= magit-process-popup-time 0) - (pop-to-buffer (process-buffer magit-process))) - ((> magit-process-popup-time 0) - (run-with-timer - magit-process-popup-time nil - (function - (lambda (buf) - (with-current-buffer buf - (when magit-process - (display-buffer (process-buffer magit-process)) - (goto-char (point-max)))))) - (current-buffer)))) - (setq successp t)) - (input - (with-current-buffer input - (setq default-directory dir) - (setq magit-process - ;; Don't use a pty, because it would set icrnl - ;; which would modify the input (issue #20). - (let ((process-connection-type nil)) - (apply 'magit-start-process cmd buf cmd args))) - (set-process-filter magit-process 'magit-process-filter) - (process-send-region magit-process - (point-min) (point-max)) - (process-send-eof magit-process) - (while (equal (process-status magit-process) 'run) - (sit-for 0.1 t)) - (setq successp - (equal (process-exit-status magit-process) 0)) - (setq magit-process nil)) - (magit-set-mode-line-process nil) - (magit-need-refresh magit-process-client-buffer)) - (t - (setq successp - (equal (apply 'process-file cmd nil buf nil args) 0)) - (magit-set-mode-line-process nil) - (magit-need-refresh magit-process-client-buffer)))) - (or successp - noerror - (error - "%s ... [Hit %s or see buffer %s for details]" - (or (with-current-buffer (get-buffer magit-process-buffer-name) - (when (re-search-backward - (concat "^error: \\(.*\\)" paragraph-separate) nil t) - (match-string 1))) - "Git failed") - (with-current-buffer magit-process-client-buffer - (key-description (car (where-is-internal - 'magit-display-process)))) - magit-process-buffer-name)) - successp))) - -(autoload 'dired-uncache "dired") -(defun magit-process-sentinel (process event) - (let ((msg (format "%s %s." (process-name process) (substring event 0 -1))) - (successp (string-match "^finished" event)) - (key (with-current-buffer magit-process-client-buffer - (key-description (car (where-is-internal - 'magit-display-process)))))) - (with-current-buffer (process-buffer process) - (let ((inhibit-read-only t)) - (goto-char (point-max)) - (insert msg "\n") - (message (if successp msg - (format "%s Hit %s or see buffer %s for details." - msg key (current-buffer))))) - (unless (memq (process-status process) '(run open)) - (dired-uncache default-directory))) - (setq magit-process nil) - (magit-set-mode-line-process nil) - (magit-refresh-buffer magit-process-client-buffer))) - -(defun magit-password (proc string) - "Checks if git/ssh asks for a password and ask the user for it." - (let (ask) - (cond ((or (string-match "^Enter passphrase for key '\\\(.*\\\)': $" string) - (string-match "^\\\(.*\\\)'s password:" string) - (string-match "^Password for '\\\(.*\\\)':" string)) - (setq ask (format "Password for '%s': " (match-string 1 string)))) - ((string-match "^[pP]assword:" string) - (setq ask "Password:"))) - (when ask - (process-send-string proc (concat (read-passwd ask nil) "\n"))))) - -(defun magit-username (proc string) - "Checks if git asks for a username and ask the user for it." - (when (string-match "^Username for '\\\(.*\\\)':" string) - (process-send-string proc - (concat - (read-string (format "Username for '%s': " - (match-string 1 string)) - nil nil (user-login-name)) - "\n")))) - -(defun magit-process-filter (proc string) - (save-current-buffer - (set-buffer (process-buffer proc)) - (let ((inhibit-read-only t)) - (magit-username proc string) - (magit-password proc string) - (goto-char (process-mark proc)) - ;; 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 (substring string (+ ret-pos 1)))) - (t - (insert string)))) - (set-marker (process-mark proc) (point))))) - -(defun magit-run (cmd &rest args) - (magit-with-refresh - (magit-run* (cons cmd args)))) - -(defun magit-run-git (&rest args) - (magit-with-refresh - (magit-run* (append (cons magit-git-executable - magit-git-standard-options) - args)))) - -(defun magit-run-git-with-input (input &rest args) - (magit-with-refresh - (magit-run* (append (cons magit-git-executable - magit-git-standard-options) - args) - nil nil nil nil input))) - -(defun magit-run-git-async (&rest args) - (message "Running %s %s" magit-git-executable (mapconcat 'identity args " ")) - (magit-run* (append (cons magit-git-executable - magit-git-standard-options) - args) - nil nil nil t)) - -(defun magit-run-async-with-input (input cmd &rest args) - (magit-run* (cons cmd args) nil nil nil t input)) - -(defun magit-display-process () - "Display output from most recent git command." - (interactive) - (unless (get-buffer magit-process-buffer-name) - (error "No Git commands have run")) - (display-buffer magit-process-buffer-name)) - -;;; Mode - -;; We define individual functions (instead of using lambda etc) so -;; that the online help can show something meaningful. - -(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 unpushed "Unpushed commits") - -(magit-define-level-shower 1) -(magit-define-level-shower 2) -(magit-define-level-shower 3) -(magit-define-level-shower 4) - -(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-log-edit t] - ["Add log entry" magit-add-log t] - ["Tag" magit-tag t] - ["Annotated tag" magit-annotated-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-manual-merge t] - ["Interactive resolve" magit-interactive-resolve-item 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-display-process t] - ["Quit Magit" magit-quit-window t])) - -(defvar magit-mode-hook nil "Hook run by `magit-mode'.") - -(put 'magit-mode 'mode-class 'special) - -(defvar magit-refresh-function nil) -(make-variable-buffer-local 'magit-refresh-function) -(put 'magit-refresh-function 'permanent-local t) - -(defvar magit-refresh-args nil) -(make-variable-buffer-local 'magit-refresh-args) -(put 'magit-refresh-args 'permanent-local t) - -(defvar last-point) - -(defun magit-remember-point () - (setq 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." - (if (invisible-p (point)) - (let ((end (magit-invisible-region-end (point)))) - (goto-char (if (= end last-point) - (magit-invisible-region-start (point)) - end)))) - (setq disable-point-adjustment t)) - -(defun magit-post-command-hook () - (magit-correct-point-after-command) - (magit-highlight-section)) - -(defun magit-mode () - "Review the status of a git repository and act on it. - -Please see the manual for a complete description of Magit. - -\\{magit-mode-map}" - (kill-all-local-variables) - (buffer-disable-undo) - (setq buffer-read-only t - truncate-lines t - major-mode 'magit-mode - mode-name "Magit" - mode-line-process "") - (add-hook 'pre-command-hook #'magit-remember-point nil t) - (add-hook 'post-command-hook #'magit-post-command-hook t t) - (use-local-map magit-mode-map) - (setq magit-current-indentation (magit-indentation-for default-directory)) - ;; Emacs' normal method of showing trailing whitespace gives weird - ;; results when `magit-whitespace-warning-face' is different from - ;; `trailing-whitespace'. - (if (and magit-highlight-whitespace magit-highlight-trailing-whitespace) - (setq show-trailing-whitespace nil)) - (run-mode-hooks 'magit-mode-hook)) - -(defun magit-mode-init (dir submode refresh-func &rest refresh-args) - (setq default-directory dir - magit-refresh-function refresh-func - magit-refresh-args refresh-args) - (funcall submode) - (magit-refresh-buffer)) - -(defun magit-indentation-for (dir) - (let (result) - (dolist (pair magit-highlight-indentation) - (if (string-match-p (car pair) dir) - (setq result (cdr pair)))) - result)) - -(defun magit-find-buffer (submode &optional dir) - (let ((topdir (magit-get-top-dir (or dir default-directory)))) - (dolist (buf (buffer-list)) - (if (with-current-buffer buf - (and (eq major-mode submode) - default-directory - (equal (expand-file-name default-directory) topdir))) - (return buf))))) - -(defun magit-find-status-buffer (&optional dir) - (magit-find-buffer 'magit-status-mode dir)) - -(defun magit-for-all-buffers (func &optional dir) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (if (and (derived-mode-p 'magit-mode) - (or (null dir) - (equal default-directory dir))) - (funcall func))))) - -(defun magit-refresh-buffer (&optional buffer) - (with-current-buffer (or buffer (current-buffer)) - (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 ((section-line (and old-section - (count-lines - (magit-section-beginning old-section) - (point)))) - (line-char (- old-point (point)))) - (if magit-refresh-function - (apply magit-refresh-function - magit-refresh-args)) - (magit-refresh-marked-commits-in-buffer) - (let ((s (and old-path (magit-find-section old-path magit-top-section)))) - (cond (s - (goto-char (magit-section-beginning s)) - (forward-line section-line) - (forward-char line-char)) - (t - (magit-goto-line old-line))) - (dolist (w (get-buffer-window-list (current-buffer))) - (set-window-point w (point))) - (magit-highlight-section)))))) - -(defun magit-string-has-prefix-p (string prefix) - (eq (compare-strings string nil (length prefix) prefix nil nil) t)) - -(defun magit-revert-buffers (dir &optional ignore-modtime) - (dolist (buffer (buffer-list)) - (when (and buffer - (buffer-file-name buffer) - ;; don't revert indirect buffers, as the parent will be reverted - (not (buffer-base-buffer buffer)) - (magit-string-has-prefix-p (buffer-file-name buffer) dir) - (file-readable-p (buffer-file-name buffer)) - (or ignore-modtime (not (verify-visited-file-modtime buffer))) - (not (buffer-modified-p buffer))) - (with-current-buffer buffer - (condition-case var - (revert-buffer t t nil) - (error (let ((signal-data (cadr var))) - (cond (t (magit-bug-report signal-data)))))))))) - -(defun magit-update-vc-modeline (dir) - "Update the modeline for buffers representable by magit." - (dolist (buffer (buffer-list)) - (when (and buffer - (buffer-file-name buffer) - (magit-string-has-prefix-p (buffer-file-name buffer) dir)) - (with-current-buffer buffer - (condition-case var - (vc-find-file-hook) - (error (let ((signal-data (cadr var))) - (cond (t (magit-bug-report signal-data)))))))))) - -(defvar magit-refresh-needing-buffers nil) -(defvar magit-refresh-pending nil) - -(defun magit-refresh-wrapper (func) - (if magit-refresh-pending - (funcall func) - (let* ((dir default-directory) - (status-buffer (magit-find-status-buffer dir)) - (magit-refresh-needing-buffers nil) - (magit-refresh-pending t)) - (unwind-protect - (funcall func) - (when magit-refresh-needing-buffers - (magit-revert-buffers dir) - (dolist (b (adjoin status-buffer - magit-refresh-needing-buffers)) - (magit-refresh-buffer b))))))) - -(defun magit-need-refresh (&optional buffer) - "Mark BUFFER as needing to be refreshed. If BUFFER is nil, use the -current buffer." - (pushnew (or buffer (current-buffer)) magit-refresh-needing-buffers :test 'eq)) - -(defun magit-refresh () - "Refresh current buffer to match repository state. -Also revert every unmodified buffer visiting files -in the corresponding directory." - (interactive) - (magit-with-refresh - (magit-need-refresh))) - -(defun magit-refresh-all () - "Refresh all magit buffers to match respective repository states. -Also revert every unmodified buffer visiting files -in the corresponding directories." - (interactive) - (magit-for-all-buffers #'magit-refresh-buffer default-directory)) - -;;; Untracked files - -(defun magit-wash-untracked-file () - (if (looking-at "^? \\(.*\\)$") - (let ((file (match-string-no-properties 1))) - (delete-region (point) (+ (line-end-position) 1)) - (magit-with-section file 'file - (magit-set-section-info file) - (insert "\t" file "\n")) - t) - nil)) - -(defun magit-wash-untracked-files () - ;; Setting magit-old-top-section to nil speeds up washing: no time - ;; is wasted looking up the old visibility, which doesn't matter for - ;; untracked files. - ;; - ;; XXX - speed this up in a more general way. - ;; - (let ((magit-old-top-section nil)) - (magit-wash-sequence #'magit-wash-untracked-file))) - -(defun magit-insert-untracked-files () - (unless (string= (magit-get "status" "showUntrackedFiles") "no") - (apply 'magit-git-section - `(untracked - "Untracked files:" - magit-wash-untracked-files - "ls-files" "--others" "-t" "--exclude-standard" - ,@(when magit-omit-untracked-dir-contents - '("--directory")))))) - -;;; Diffs and Hunks - -(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-toggle-diff-refine-hunk (&optional other) - (interactive "P") - "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 `magit-diff-refine-hunk' to change the default mode." - (let* ((old magit-diff-refine-hunk) - (new - (if other - (if (eq old 'all) t 'all) - (not old)))) - - ;; remove any old refining in currently highlighted section - (when (and magit-highlighted-section old (not (eq old 'all))) - (magit-unrefine-section magit-highlighted-section)) - - ;; set variable to new value locally - (set (make-local-variable 'magit-diff-refine-hunk) new) - - ;; if now highlighting in "selected only" mode, turn refining back - ;; on in the current section - (when (and magit-highlighted-section new (not (eq new 'all))) - (magit-refine-section magit-highlighted-section)) - - ;; `all' mode being turned on or off needs a complete refresh - (when (or (eq old 'all) (eq new 'all)) - (magit-refresh)))) - -(defun magit-diff-line-file () - (cond ((looking-at "^diff --git ./\\(.*\\) ./\\(.*\\)$") - (match-string-no-properties 2)) - ((looking-at "^diff --cc +\\(.*\\)$") - (match-string-no-properties 1)) - (t - nil))) - -(defun magit-wash-diffs () - (magit-wash-sequence #'magit-wash-diff-or-other-file)) - -(defun magit-wash-diff-or-other-file () - (or (magit-wash-diff) - (magit-wash-other-file))) - -(defun magit-wash-other-file () - (if (looking-at "^? \\(.*\\)$") - (let ((file (match-string-no-properties 1))) - (delete-region (point) (+ (line-end-position) 1)) - (magit-with-section file 'file - (magit-set-section-info file) - (insert "\tNew " file "\n")) - t) - nil)) - -(defvar magit-hide-diffs nil) - -(defvar magit-indentation-level 1) - -(defun magit-insert-diff-title (status file file2) - (let ((status-text (case status - ((unmerged) - (format "Unmerged %s" file)) - ((new) - (format "New %s" file)) - ((deleted) - (format "Deleted %s" file)) - ((renamed) - (format "Renamed %s (from %s)" - file file2)) - ((modified) - (format "Modified %s" file)) - ((typechange) - (format "Typechange %s" file)) - (t - (format "? %s" file))))) - (insert (make-string magit-indentation-level ?\t) status-text "\n"))) - -(defvar magit-current-diff-range nil - "Used internally when setting up magit diff sections.") - -(defun magit-wash-typechange-section (file) - (magit-set-section-info (list 'typechange file)) - (let ((first-start (point-marker)) - (second-start (progn (forward-line 1) - (search-forward-regexp "^diff") - (beginning-of-line) - (point-marker)))) - (let ((magit-indentation-level (+ magit-indentation-level 1))) - (save-restriction - (narrow-to-region first-start second-start) - (goto-char (point-min)) - (magit-with-section file 'diff - (magit-wash-diff-section))) - (save-restriction - (narrow-to-region second-start (point-max)) - (goto-char (point-min)) - (magit-with-section file 'diff - (magit-wash-diff-section)))))) - -(defun magit-wash-diff-section () - (cond ((looking-at "^\\* Unmerged path \\(.*\\)") - (let ((file (match-string-no-properties 1))) - (delete-region (point) (line-end-position)) - (insert "\tUnmerged " file "\n") - (magit-set-section-info (list 'unmerged file nil)) - t)) - ((looking-at "^diff") - (let ((file (magit-diff-line-file)) - (end (save-excursion - (forward-line) ;; skip over "diff" line - (if (search-forward-regexp "^diff\\|^@@" nil t) - (goto-char (match-beginning 0)) - (goto-char (point-max))) - (point-marker)))) - (let* ((status (cond - ((looking-at "^diff --cc") - 'unmerged) - ((save-excursion - (search-forward-regexp "^new file" end t)) - 'new) - ((save-excursion - (search-forward-regexp "^deleted" end t)) - 'deleted) - ((save-excursion - (search-forward-regexp "^rename" end t)) - 'renamed) - (t - 'modified))) - (file2 (cond - ((save-excursion - (search-forward-regexp "^rename from \\(.*\\)" - end t)) - (match-string-no-properties 1))))) - (magit-set-section-info (list status - file - (or file2 file) - magit-current-diff-range)) - (magit-insert-diff-title status file file2) - (when (search-forward-regexp "\\(--- \\(.*\\)\n\\+\\+\\+ \\(.*\\)\n\\)" () t) - (when (match-string 1) - (add-text-properties (match-beginning 1) (match-end 1) - '(face magit-diff-hunk-header)) - (add-text-properties (match-beginning 2) (match-end 2) - '(face magit-diff-file-header)) - (add-text-properties (match-beginning 3) (match-end 3) - '(face magit-diff-file-header)))) - (goto-char end) - (let ((magit-section-hidden-default nil)) - (magit-wash-sequence #'magit-wash-hunk)))) - t) - (t - nil))) - -(defun magit-wash-diff () - (let ((magit-section-hidden-default magit-hide-diffs)) - (magit-with-section (magit-current-line) 'diff - (magit-wash-diff-section)))) - -(defun magit-diff-item-kind (diff) - (car (magit-section-info diff))) - -(defun magit-diff-item-file (diff) - (cadr (magit-section-info diff))) - -(defun magit-diff-item-file2 (diff) - (caddr (magit-section-info diff))) - -(defun magit-diff-item-range (diff) - (nth 3 (magit-section-info diff))) - -(defun magit-wash-hunk () - (cond ((looking-at "\\(^@+\\)[^@]*@+.*") - (let ((n-columns (1- (length (match-string 1)))) - (head (match-string 0)) - (hunk-start-pos (point))) - (magit-with-section head 'hunk - (add-text-properties (match-beginning 0) (match-end 0) - '(face magit-diff-hunk-header)) - (forward-line) - (while (not (or (eobp) - (looking-at "^diff\\|^@@"))) - (magit-highlight-line-whitespace) - (let ((prefix (buffer-substring-no-properties - (point) (min (+ (point) n-columns) (point-max))))) - (cond ((string-match "\\+" prefix) - (magit-put-line-property 'face 'magit-diff-add)) - ((string-match "-" prefix) - (magit-put-line-property 'face 'magit-diff-del)) - (t - (magit-put-line-property 'face 'magit-diff-none)))) - (forward-line))) - - (when (eq magit-diff-refine-hunk 'all) - (save-excursion - (goto-char hunk-start-pos) - (diff-refine-hunk)))) - t) - (t - nil))) - -(defvar magit-diff-options nil) - -(defun magit-insert-diff (file status) - (let ((cmd magit-git-executable) - (args (append (list "diff") - (list (magit-diff-U-arg)) - magit-diff-options - (list "--" file)))) - (let ((p (point))) - (magit-git-insert args) - (if (not (eq (char-before) ?\n)) - (insert "\n")) - (save-restriction - (narrow-to-region p (point)) - (goto-char p) - (cond - ((eq status 'typechange) - (magit-insert-diff-title status file file) - (magit-wash-typechange-section file)) - (t - (magit-wash-diff-section))) - (goto-char (point-max)))))) - -(defvar magit-last-raw-diff nil) -(defvar magit-ignore-unmerged-raw-diffs nil) - -(defun magit-wash-raw-diffs () - (let ((magit-last-raw-diff nil)) - (magit-wash-sequence #'magit-wash-raw-diff))) - -(defun magit-wash-raw-diff () - (if (looking-at - ":\\([0-7]+\\) \\([0-7]+\\) [0-9a-f]+ [0-9a-f]+ \\(.\\)[0-9]*\t\\([^\t\n]+\\)$") - (let ((old-perm (match-string-no-properties 1)) - (new-perm (match-string-no-properties 2)) - (status (case (string-to-char (match-string-no-properties 3)) - (?A 'new) - (?D 'deleted) - (?M 'modified) - (?U 'unmerged) - (?T 'typechange) - (t nil))) - (file (match-string-no-properties 4))) - ;; If this is for the same file as the last diff, ignore it. - ;; Unmerged files seem to get two entries. - ;; We also ignore unmerged files when told so. - (if (or (equal file magit-last-raw-diff) - (and magit-ignore-unmerged-raw-diffs (eq status 'unmerged))) - (delete-region (point) (+ (line-end-position) 1)) - (setq magit-last-raw-diff file) - ;; The 'diff' section that is created here will not work with - ;; magit-insert-diff-item-patch etc when we leave it empty. - ;; Luckily, raw diffs are only produced for staged and - ;; unstaged changes, and we never call - ;; magit-insert-diff-item-patch on them. This is a bit - ;; brittle, of course. - (let ((magit-section-hidden-default magit-hide-diffs)) - (magit-with-section file 'diff - (delete-region (point) (+ (line-end-position) 1)) - (if (not (magit-section-hidden magit-top-section)) - (magit-insert-diff file status) - (magit-set-section-info (list status file nil)) - (magit-set-section-needs-refresh-on-show t) - (magit-insert-diff-title status file nil))))) - t) - nil)) - -(defun magit-hunk-item-diff (hunk) - (let ((diff (magit-section-parent hunk))) - (or (eq (magit-section-type diff) 'diff) - (error "Huh? Parent of hunk not a diff")) - diff)) - -(defun magit-diff-item-insert-header (diff buf) - (let ((beg (save-excursion - (goto-char (magit-section-beginning diff)) - (forward-line) - (point))) - (end (if (magit-section-children diff) - (magit-section-beginning (car (magit-section-children diff))) - (magit-section-end diff)))) - (magit-insert-region beg end buf))) - -(defun magit-insert-diff-item-patch (diff buf) - (let ((beg (save-excursion - (goto-char (magit-section-beginning diff)) - (forward-line) - (point))) - (end (magit-section-end diff))) - (magit-insert-region beg end buf))) - -(defun magit-insert-hunk-item-patch (hunk buf) - (magit-diff-item-insert-header (magit-hunk-item-diff 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-hunk-item-diff 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)) - (if (and (<= beg (point)) (< (point) end)) - (magit-insert-current-line buf) - (cond ((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-hunk-item-is-conflict-p (hunk) - ;;; XXX - Using the title is a bit too clever... - (string-match "^diff --cc" - (magit-section-title (magit-hunk-item-diff hunk)))) - -(defun magit-hunk-item-target-line (hunk) - (save-excursion - (beginning-of-line) - (let ((line (line-number-at-pos))) - (goto-char (magit-section-beginning hunk)) - (if (not (looking-at "@@+ .* \\+\\([0-9]+\\)\\(,[0-9]+\\)? @@+")) - (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 - (if (not (looking-at "-")) - (setq target (+ target 1))) - (forward-line)) - target)))) - -(defun magit-show (commit filename &optional select prefix) - "Returns a buffer containing the contents of the file FILENAME, as stored in -COMMIT. COMMIT may be one of the following: - -- A string with the name of a commit, such as \"head\" or \"dae86e\". See 'git - help revisions' for syntax. -- The symbol 'index, indicating that you want the version in Git's index or - staging area. -- The symbol 'working, indicating that you want the version in the working - directory. In this case you'll get a buffer visiting the file. If there's - already a buffer visiting that file, you'll get that one. - -When called interactively or when SELECT is non-nil, make the buffer active, -either in another window or (with a prefix argument) in the current window." - (interactive (let* ((revision (magit-read-rev "Retrieve file from revision")) - (filename (magit-read-file-from-rev revision))) - (list revision filename t current-prefix-arg))) - (if (eq commit 'working) - (find-file-noselect filename) - (let ((buffer (create-file-buffer (format "%s.%s" filename (replace-regexp-in-string ".*/" "" (prin1-to-string commit t)))))) - (cond - ((eq commit 'index) - (let ((checkout-string (magit-git-string "checkout-index" - "--temp" - filename))) - (string-match "^\\(.*\\)\t" checkout-string) - (with-current-buffer buffer - (let ((tmpname (match-string 1 checkout-string))) - (magit-with-silent-modifications - (insert-file-contents tmpname nil nil nil t)) - (delete-file tmpname))))) - (t - (with-current-buffer buffer - (magit-with-silent-modifications - (magit-git-insert (list "cat-file" "-p" - (concat commit ":" filename))))))) - (with-current-buffer buffer - (let ((buffer-file-name filename)) - (normal-mode)) - (goto-char (point-min))) - (if select - (if prefix - (switch-to-buffer buffer) - (switch-to-buffer-other-window buffer)) - buffer)))) - -(defmacro with-magit-tmp-buffer (var &rest body) - (declare (indent 1) - (debug (symbolp &rest form))) - `(let ((,var (generate-new-buffer magit-tmp-buffer-name))) - (unwind-protect - (progn ,@body) - (kill-buffer ,var)))) - -(defun magit-apply-diff-item (diff &rest args) - (when (zerop magit-diff-context-lines) - (setq args (cons "--unidiff-zero" args))) - (with-magit-tmp-buffer tmp - (magit-insert-diff-item-patch diff tmp) - (apply #'magit-run-git-with-input tmp - "apply" (append args (list "-"))))) - -(defun magit-apply-hunk-item* (hunk reverse &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." - (let ((zero-context (zerop magit-diff-context-lines)) - (use-region (magit-use-region-p))) - (when zero-context - (setq args (cons "--unidiff-zero" args))) - (when reverse - (setq args (cons "--reverse" args))) - (when (and use-region zero-context) - (error (concat "Not enough context to partially apply hunk. " - "Use `+' to increase context."))) - (with-magit-tmp-buffer tmp - (if use-region - (magit-insert-hunk-item-region-patch - hunk reverse (region-beginning) (region-end) tmp) - (magit-insert-hunk-item-patch hunk tmp)) - (apply #'magit-run-git-with-input tmp - "apply" (append args (list "-")))))) - -(defun magit-apply-hunk-item (hunk &rest args) - (apply #'magit-apply-hunk-item* hunk nil args)) - -(defun magit-apply-hunk-item-reverse (hunk &rest args) - (apply #'magit-apply-hunk-item* hunk t args)) - -(magit-define-inserter unstaged-changes (title) - (let ((magit-hide-diffs t) - (magit-current-diff-range (cons 'index 'working))) - (let ((magit-diff-options (append '() magit-diff-options))) - (magit-git-section 'unstaged title 'magit-wash-raw-diffs - "diff-files")))) - -(magit-define-inserter staged-changes (staged no-commit) - (let ((magit-current-diff-range (cons "HEAD" 'index))) - (when staged - (let ((magit-hide-diffs t) - (base (if no-commit - (magit-git-string "mktree") - "HEAD"))) - (let ((magit-diff-options (append '("--cached") magit-diff-options)) - (magit-ignore-unmerged-raw-diffs t)) - (magit-git-section 'staged "Staged changes:" 'magit-wash-raw-diffs - "diff-index" "--cached" - base)))))) - -;;; Logs and Commits - - ; Note: making this a plain defcustom would probably let users break - ; the parser too easily -(defvar magit-git-log-options - (list - "--pretty=format:* %h %s" - (format "--abbrev=%s" magit-sha1-abbrev-length))) - ; --decorate=full otherwise some ref prefixes are stripped - ; '("--pretty=format:* %H%d %s" "--decorate=full")) - -;; -;; Regexps for parsing ref names -;; -;; see the `git-check-ref-format' manpage for details - -(defconst magit-ref-nonchars "\000-\037\177 ~^:?*[\\" - "Characters specifically disallowed from appearing in Git symbolic refs. - -Evaluate (man \"git-check-ref-format\") for details") - -(defconst magit-ref-nonslash-re - (concat "\\(?:" - ;; "no slash-separated component can begin with a dot ." (rule 1) - "[^" magit-ref-nonchars "./]" - ;; "cannot have two consecutive dots .. anywhere." (rule 3) - "\\.?" - "\\)*") - "Regexp that matches the non-slash parts of a ref name. - -Evaluate (man \"git-check-ref-format\") for details") - -(defconst magit-refname-re - (concat - "\\(?:HEAD\\|" - - "\\(?:tag: \\)?" - - ;; optional non-slash sequence at the beginning - magit-ref-nonslash-re - - ;; any number of slash-prefixed sequences - "\\(?:" - "/" - magit-ref-nonslash-re - "\\)*" - - "/" ;; "must contain at least one /." (rule 2) - magit-ref-nonslash-re - - ;; "cannot end with a slash / nor a dot .." (rule 5) - "[^" magit-ref-nonchars "./]" - - "\\)" - ) - "Regexp that matches a git symbolic reference name. - -Evaluate (man \"git-check-ref-format\") for details") - -(defconst magit-log-oneline-re - (concat - "^\\([_\\*|/ -.]+\\)?" ; graph (1) - "\\(?:" - "\\([0-9a-fA-F]+\\)" ; sha1 (2) - "\\(?:" ; refs (3) - " " - "\\(" - "(" - magit-refname-re "\\(?:, " magit-refname-re "\\)*" - ")" - "\\)" - "\\)?" - "\\)?" - " ?\\(.*\\)$" ; msg (4) - )) - -(defconst magit-log-longline-re - (concat - ;; use \0 delimiter (from -z option) to identify commits. this prevents - ;; commit messages containing lines like "commit 00000" from polluting the - ;; display - "\\(?:\\(?:\\`\\|\0\\)" - "\\([_\\*|/ -.]+\\)?" ; graph (1) - "commit " - "\\([0-9a-fA-F]+\\)" ; sha1 (2) - "\\(?:" ; refs (3) - " " - "\\(" - "(" - magit-refname-re "\\(?:, " magit-refname-re "\\)*" - ")" - "\\)" - "\\)?" - "$\\)?" - " ?\\(.*\\)$" ; msg (4) - )) - -(defvar magit-present-log-line-function 'magit-present-log-line - "The function to use when generating a log line. -It takes four args: CHART, SHA1, REFS and MESSAGE. The function -must return a string which will represent the log line.") - -(defun magit-log-get-bisect-state-color (suffix) - (if (string= suffix "bad") - (list suffix 'magit-log-head-label-bisect-bad) - (list suffix 'magit-log-head-label-bisect-good))) - -(defun magit-log-get-patches-color (suffix) - (list (and (string-match ".+/\\(.+\\)" suffix) - (match-string 1 suffix)) - 'magit-log-head-label-patches)) - -(defvar magit-log-remotes-color-hook nil) - -(defun magit-log-get-remotes-color (suffix) - (or - (run-hook-with-args-until-success - 'magit-log-remotes-color-hook suffix) - (list suffix 'magit-log-head-label-remote))) - -(defvar magit-refs-namespaces - '(("tags" . magit-log-head-label-tags) - ("remotes" magit-log-get-remotes-color) - ("heads" . magit-log-head-label-local) - ("patches" magit-log-get-patches-color) - ("bisect" magit-log-get-bisect-state-color))) - -(defun magit-ref-get-label-color (r) - (let ((uninteresting (loop for re in magit-uninteresting-refs - thereis (string-match re r)))) - (if uninteresting (list nil nil) - (let* ((ref-re "\\(?:tag: \\)?refs/\\(?:\\([^/]+\\)/\\)?\\(.+\\)") - (label (and (string-match ref-re r) - (match-string 2 r))) - (res (let ((colorizer - (cdr (assoc (match-string 1 r) - magit-refs-namespaces)))) - (cond ((null colorizer) - (list r 'magit-log-head-label-default)) - ((symbolp colorizer) - (list label colorizer)) - ((listp colorizer) - (funcall (car colorizer) - (match-string 2 r))) - (t - (list r 'magit-log-head-label-default)))))) - res)))) - -(defun magit-present-log-line (graph sha1 refs message) - "The default log line generator." - (let ((string-refs - (when refs - (let ((colored-labels - (delete nil - (mapcar (lambda (r) - (destructuring-bind (label face) - (magit-ref-get-label-color r) - (and label - (propertize label 'face face)))) - refs)))) - (concat - (mapconcat 'identity colored-labels " ") - " "))))) - - (concat - (if sha1 - (propertize sha1 'face 'magit-log-sha1) - (insert-char ? magit-sha1-abbrev-length)) - " " - (when graph - (propertize graph 'face 'magit-log-graph)) - string-refs - (when message - (propertize message 'face 'magit-log-message))))) - -(defvar magit-log-count () - "Internal var used to count the number of logs actually added in a buffer.") - -(defmacro magit-create-log-buffer-sections (&rest body) - "Empty current buffer of text and magit's section, and then evaluate BODY. - -if the number of logs inserted in the buffer is `magit-log-cutoff-length' -insert a line to tell how to insert more of them" - (declare (indent 0)) - `(let ((magit-log-count 0) (inhibit-read-only t)) - (magit-create-buffer-sections - (magit-with-section 'log nil - ,@body - (if (= magit-log-count magit-log-cutoff-length) - (magit-with-section "longer" 'longer - (insert "type \"e\" to show more logs\n"))))))) - -(defun magit-wash-log-line (style) - (beginning-of-line) - (let ((line-re (cond ((eq style 'long) magit-log-longline-re) - (t magit-log-oneline-re)))) - (cond - ((looking-at line-re) - (let ((chart (match-string 1)) - (sha1 (match-string 2)) - (msg (match-string 4)) - (refs (when (match-string 3) - (delq nil - (mapcar - (lambda (s) - (and (not - (or (string= s "tag:") - (string= s "HEAD"))) ; as of 1.6.6 - s)) - (split-string (match-string 3) "[(), ]" t)))))) - (delete-region (point-at-bol) (point-at-eol)) - (insert (funcall magit-present-log-line-function chart sha1 refs msg)) - (goto-char (point-at-bol)) - (if sha1 - (magit-with-section sha1 'commit - (when magit-log-count (setq magit-log-count (1+ magit-log-count))) - (magit-set-section-info sha1) - (forward-line)) - (forward-line)))) - (t - (forward-line))) - t)) - -(defun magit-wash-log (&optional style) - (let ((magit-old-top-section nil)) - (magit-wash-sequence (apply-partially 'magit-wash-log-line style)))) - -(defvar magit-currently-shown-commit nil) - -(defun magit-wash-commit () - (let ((magit-current-diff-range)) - (when (looking-at "^commit \\([0-9a-fA-F]\\{40\\}\\)") - (setq magit-current-diff-range (match-string 1)) - (add-text-properties (match-beginning 1) (match-end 1) - '(face magit-log-sha1))) - (cond - ((search-forward-regexp "^Merge: \\([0-9a-fA-F]+\\) \\([0-9a-fA-F]+\\)$" nil t) - (setq magit-current-diff-range (cons (cons (match-string 1) - (match-string 2)) - magit-current-diff-range)) - (let ((first (magit-set-section nil 'commit (match-beginning 1) (match-end 1))) - (second (magit-set-section nil 'commit (match-beginning 2) (match-end 2)))) - (magit-set-section-info (match-string 1) first) - (magit-set-section-info (match-string 2) second)) - (make-commit-button (match-beginning 1) (match-end 1)) - (make-commit-button (match-beginning 2) (match-end 2))) - (t - (setq magit-current-diff-range (cons (concat magit-current-diff-range "^") - magit-current-diff-range)))) - (search-forward-regexp "^$") - (while (and - (search-forward-regexp "\\(\\b[0-9a-fA-F]\\{4,40\\}\\b\\)\\|\\(^diff\\)" nil 'noerror) - (not (match-string 2))) - (let ((sha1 (match-string 1)) - (start (match-beginning 1)) - (end (match-end 1))) - (when (string-equal "commit" (magit-git-string "cat-file" "-t" sha1)) - (make-commit-button start end) - (let ((section (magit-set-section sha1 'commit start end))) - (magit-set-section-info sha1 section))))) - (beginning-of-line) - (when (looking-at "^diff") - (magit-wash-diffs)) - (goto-char (point-max)) - (insert "\n") - (if magit-back-navigation-history - (magit-with-section "[back]" 'button - (insert-text-button "[back]" - 'help-echo "Previous commit" - 'action 'magit-show-commit-backward - 'follow-link t - 'mouse-face 'magit-item-highlight))) - (insert " ") - (if magit-forward-navigation-history - (magit-with-section "[forward]" 'button - (insert-text-button "[forward]" - 'help-echo "Next commit" - 'action 'magit-show-commit-forward - 'follow-link t - 'mouse-face 'magit-item-highlight))))) - -(defun make-commit-button (start end) - (make-text-button start end - 'help-echo "Visit commit" - 'action (lambda (button) - (save-excursion - (goto-char button) - (magit-visit-item))) - 'follow-link t - 'mouse-face 'magit-item-highlight - 'face 'magit-log-sha1)) - -(defun magit-refresh-commit-buffer (commit) - (magit-configure-have-abbrev) - (magit-configure-have-decorate) - (magit-create-buffer-sections - (apply #'magit-git-section nil nil - 'magit-wash-commit - "log" - "--max-count=1" - "--pretty=medium" - `(,@(if magit-have-abbrev (list "--no-abbrev-commit")) - ,@(if magit-have-decorate (list "--decorate=full")) - "--cc" - "-p" ,commit)))) - -(define-derived-mode magit-commit-mode magit-mode "Magit" - "Mode to view a git commit. - -\\{magit-commit-mode-map}" - :group 'magit) - -(defvar magit-commit-buffer-name "*magit-commit*" - "Buffer name for displaying commit log messages.") - -(defun magit-show-commit (commit &optional scroll inhibit-history select) - "Show information about a commit in the buffer named by -`magit-commit-buffer-name'. COMMIT can be any valid name for a commit -in the current Git repository. - -When called interactively or when SELECT is non-nil, switch to -the commit buffer using `pop-to-buffer'. - -Unless INHIBIT-HISTORY is non-nil, the commit currently shown -will be pushed onto `magit-back-navigation-history' and -`magit-forward-navigation-history' will be cleared. - -Noninteractively, if the commit is already displayed and SCROLL -is provided, call SCROLL's function definition in the commit -window. (`scroll-up' and `scroll-down' are typically passed in -for this argument.)" - (interactive (list (magit-read-rev "Show commit (hash or ref)") - nil nil t)) - (when (magit-section-p commit) - (setq commit (magit-section-info commit))) - (unless (eql 0 (magit-git-exit-code "cat-file" "commit" commit)) - (error "%s is not a commit" commit)) - (let ((dir default-directory) - (buf (get-buffer-create magit-commit-buffer-name))) - (cond - ((and (equal magit-currently-shown-commit commit) - ;; if it's empty then the buffer was killed - (with-current-buffer buf - (> (length (buffer-string)) 1))) - (let ((win (get-buffer-window buf))) - (cond ((not win) - (display-buffer buf)) - (scroll - (with-selected-window win - (funcall scroll)))))) - (commit - (display-buffer buf) - (with-current-buffer buf - (unless inhibit-history - (push (cons default-directory magit-currently-shown-commit) - magit-back-navigation-history) - (setq magit-forward-navigation-history nil)) - (setq magit-currently-shown-commit commit) - (goto-char (point-min)) - (magit-mode-init dir 'magit-commit-mode - #'magit-refresh-commit-buffer commit)))) - (if select - (pop-to-buffer buf)))) - -(defun magit-show-commit-backward (&optional ignored) - ;; Ignore argument passed by push-button - "Show the commit at the head of `magit-back-navigation-history in -`magit-commit-buffer-name`." - (interactive) - (with-current-buffer magit-commit-buffer-name - (unless magit-back-navigation-history - (error "No previous commit.")) - (let ((histitem (pop magit-back-navigation-history))) - (push (cons default-directory magit-currently-shown-commit) - magit-forward-navigation-history) - (setq default-directory (car histitem)) - (magit-show-commit (cdr histitem) nil 'inhibit-history)))) - -(defun magit-show-commit-forward (&optional ignored) - ;; Ignore argument passed by push-button - "Show the commit at the head of `magit-forward-navigation-history in -`magit-commit-buffer-name`." - (interactive) - (with-current-buffer magit-commit-buffer-name - (unless magit-forward-navigation-history - (error "No next commit.")) - (let ((histitem (pop magit-forward-navigation-history))) - (push (cons default-directory magit-currently-shown-commit) - magit-back-navigation-history) - (setq default-directory (car histitem)) - (magit-show-commit (cdr histitem) nil 'inhibit-history)))) - -(defvar magit-marked-commit nil) - -(defvar magit-mark-overlay nil) -(make-variable-buffer-local 'magit-mark-overlay) -(put 'magit-mark-overlay 'permanent-local t) - -(defun magit-refresh-marked-commits () - (magit-for-all-buffers #'magit-refresh-marked-commits-in-buffer)) - -(defun magit-refresh-marked-commits-in-buffer () - (if (not magit-mark-overlay) - (let ((ov (make-overlay 1 1))) - (overlay-put ov 'face 'magit-item-mark) - (setq magit-mark-overlay ov))) - (delete-overlay magit-mark-overlay) - (magit-for-all-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)))))) - -(defun magit-set-marked-commit (commit) - (setq magit-marked-commit commit) - (magit-refresh-marked-commits)) - -(defun magit-marked-commit () - (or magit-marked-commit - (error "No commit marked"))) - -(defun magit-remote-branch-name (remote branch) - "Get the name of the branch BRANCH on remote REMOTE" - (if (string= remote ".") - branch - (concat remote "/" branch))) - -(magit-define-inserter unpulled-commits (remote branch) - (when remote - (apply #'magit-git-section - 'unpulled "Unpulled commits:" 'magit-wash-log "log" - (append magit-git-log-options - (list - (format "HEAD..%s" (magit-remote-branch-name remote branch))))))) - -(magit-define-inserter unpushed-commits (remote branch) - (when remote - (apply #'magit-git-section - 'unpushed "Unpushed commits:" 'magit-wash-log "log" - (append magit-git-log-options - (list - (format "%s..HEAD" (magit-remote-branch-name remote branch))))))) - -(defun magit-remote-branch-for (local-branch &optional fully-qualified-name) - "Guess the remote branch name that LOCAL-BRANCH is tracking. -Gives a fully qualified name (e.g., refs/remotes/origin/master) if -FULLY-QUALIFIED-NAME is non-nil." - (let ((merge (magit-get "branch" local-branch "merge"))) - (save-match-data - (if (and merge (string-match "^refs/heads/\\(.+\\)" merge)) - (concat (if fully-qualified-name - (let ((remote-name (magit-get "branch" local-branch "remote"))) - (if (string= "." remote-name) - "refs/heads/" - (concat "refs/remotes/" remote-name "/")))) - (match-string 1 merge)))))) - -;;; Status - -(defvar magit-remote-string-hook nil) - -(defun magit-remote-string (remote remote-branch remote-rebase) - (cond - ((string= "." remote) - (concat - (when remote-rebase "onto ") - "branch " - (propertize remote-branch 'face 'magit-branch))) - (remote - (concat - (when remote-rebase "onto ") - (propertize remote-branch 'face 'magit-branch) - " @ " - remote - " (" - (magit-get "remote" remote "url") - ")")) - (t - (run-hook-with-args-until-success 'magit-remote-string-hook)))) - -(declare-function magit--bisect-info-for-status "magit-bisect" (branch)) - -(defun magit-refresh-status () - (magit-create-buffer-sections - (magit-with-section 'status nil - (let* ((branch (magit-get-current-branch)) - (remote (and branch (magit-get "branch" branch "remote"))) - (remote-rebase (and branch (magit-get-boolean "branch" branch "rebase"))) - (remote-branch (or (and branch (magit-remote-branch-for branch)) branch)) - (remote-string (magit-remote-string remote remote-branch remote-rebase)) - (head (magit-git-string - "log" - "--max-count=1" - "--abbrev-commit" - (format "--abbrev=%s" magit-sha1-abbrev-length) - "--pretty=oneline")) - (no-commit (not head))) - (when remote-string - (insert "Remote: " remote-string "\n")) - (insert (format "Local: %s %s\n" - (propertize (magit--bisect-info-for-status branch) - 'face 'magit-branch) - (abbreviate-file-name default-directory))) - (insert (format "Head: %s\n" - (if no-commit "nothing commited (yet)" head))) - (let ((merge-heads (magit-file-lines (concat (magit-git-dir) - "MERGE_HEAD")))) - (if merge-heads - (insert (format "Merging: %s\n" - (mapconcat 'identity - (mapcar 'magit-name-rev merge-heads) - ", "))))) - (let ((rebase (magit-rebase-info))) - (if rebase - (insert (apply 'format "Rebasing: onto %s (%s of %s); Press \"R\" to Abort, Skip, or Continue\n" rebase)))) - (insert "\n") - (magit-git-exit-code "update-index" "--refresh") - (magit-insert-stashes) - (magit-insert-untracked-files) - (magit-insert-pending-changes) - (magit-insert-pending-commits) - (magit-insert-unpulled-commits remote remote-branch) - (let ((staged (or no-commit (magit-anything-staged-p)))) - (magit-insert-unstaged-changes - (if staged "Unstaged changes:" "Changes:")) - (magit-insert-staged-changes staged no-commit)) - (magit-insert-unpushed-commits remote remote-branch) - (run-hooks 'magit-refresh-status-hook))))) - -(defun magit-init (dir) - "Initialize git repository in the DIR directory." - (interactive (list (read-directory-name "Directory for Git repository: "))) - (let* ((dir (file-name-as-directory (expand-file-name dir))) - (topdir (magit-get-top-dir dir))) - (when (or (not topdir) - (yes-or-no-p - (format - (if (string-equal topdir dir) - "There is already a Git repository in %s. Reinitialize? " - "There is a Git repository in %s. Create another in %s? ") - topdir dir))) - (unless (file-directory-p dir) - (and (y-or-n-p (format "Directory %s does not exists. Create it? " dir)) - (make-directory dir))) - (let ((default-directory dir)) - (magit-run* (list magit-git-executable "init")))))) - -(define-derived-mode magit-status-mode magit-mode "Magit" - "Mode for looking at git status. - -\\{magit-status-mode-map}" - :group 'magit) - -(defvar magit-default-directory nil) - -(defun magit-save-some-buffers (&optional msg pred) - "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 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 dir passed to `magit-status'." - (and buffer-file-name - (string= (magit-get-top-dir magit-default-directory) - (magit-get-top-dir (file-name-directory buffer-file-name))))) - -;;;###autoload -(defun magit-status (dir) - "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." - (interactive (list (if current-prefix-arg - (magit-read-top-dir - (> (prefix-numeric-value current-prefix-arg) - 4)) - (or (magit-get-top-dir default-directory) - (magit-read-top-dir nil))))) - (magit-save-some-buffers) - (let ((topdir (magit-get-top-dir dir))) - (unless topdir - (when (y-or-n-p (format "There is no Git repository in %S. Create one? " - dir)) - (magit-init dir) - (setq topdir (magit-get-top-dir dir)))) - (when topdir - (let ((buf (or (magit-find-status-buffer topdir) - (generate-new-buffer - (concat "*magit: " - (file-name-nondirectory - (directory-file-name topdir)) "*"))))) - (funcall magit-status-buffer-switch-function buf) - (magit-mode-init topdir 'magit-status-mode #'magit-refresh-status))))) - -(magit-define-command automatic-merge (revision) - "Merge REVISION into the current 'HEAD'; commit unless merge fails. -\('git merge REVISION')." - (interactive (list (magit-read-rev "Merge" (magit-guess-branch)))) - (if revision - (magit-run-git "merge" (magit-rev-to-git revision)))) - -(magit-define-command manual-merge (revision) - "Merge REVISION into the current 'HEAD'; commit unless merge fails. -\('git merge REVISION')." - (interactive (list (magit-read-rev "Merge" (magit-guess-branch)))) - (when revision - (apply 'magit-run-git - "merge" "--no-commit" - (magit-rev-to-git revision) - magit-custom-options) - (when (file-exists-p ".git/MERGE_MSG") - (magit-log-edit)))) - -;;; Staging and Unstaging - -(defun magit-stage-item (&optional ask) - "Add the item at point to the staging area. -If ASK is set, ask for the file name rather than picking the one -at point." - (interactive "P") - (if ask - (magit-run-git "add" (read-file-name "File to stage: ")) - (magit-section-action (item info "stage") - ((untracked file) - (magit-run-git "add" info)) - ((untracked) - (apply #'magit-run-git "add" "--" - (magit-git-lines "ls-files" "--other" "--exclude-standard"))) - ((unstaged diff hunk) - (if (magit-hunk-item-is-conflict-p item) - (error (concat "Can't stage individual resolution hunks. " - "Please stage the whole file."))) - (magit-apply-hunk-item item "--cached")) - ((unstaged diff) - (magit-run-git "add" "-u" (magit-diff-item-file item))) - ((staged *) - (error "Already staged")) - ((diff diff) - (save-excursion - (magit-goto-parent-section) - (magit-stage-item))) - ((diff diff hunk) - (save-excursion - (magit-goto-parent-section) - (magit-goto-parent-section) - (magit-stage-item))) - ((hunk) - (error "Can't stage this hunk")) - ((diff) - (error "Can't stage this diff"))))) - -(defun magit-unstage-item () - "Remove the item at point from the staging area." - (interactive) - (magit-section-action (item info "unstage") - ((staged diff hunk) - (magit-apply-hunk-item-reverse item "--cached")) - ((staged diff) - (if (eq (car info) 'unmerged) - (error "Can't unstage an unmerged file. Resolve it first")) - (if (magit-no-commit-p) - (magit-run-git "rm" "--cached" "--" (magit-diff-item-file item)) - (magit-run-git "reset" "-q" "HEAD" "--" (magit-diff-item-file item)))) - ((unstaged *) - (error "Already unstaged")) - ((diff diff) - (save-excursion - (magit-goto-parent-section) - (magit-unstage-item))) - ((diff diff hunk) - (save-excursion - (magit-goto-parent-section) - (magit-goto-parent-section) - (magit-unstage-item))) - ((hunk) - (error "Can't unstage this hunk")) - ((diff) - (error "Can't unstage this diff")))) - -(defun magit-stage-all (&optional also-untracked-p) - "Add all remaining changes in tracked files to staging area. -With prefix argument, add remaining untracked files as well. -\('git add -u .' or 'git add .', respectively)." - (interactive "P") - (if also-untracked-p - (magit-run-git "add" ".") - (magit-run-git "add" "-u" "."))) - -(defun magit-unstage-all () - "Remove all changes from staging area. -\('git reset --mixed HEAD')." - (interactive) - (magit-run-git "reset" "HEAD")) - -;;; Branches - -(defun escape-branch-name (branch) - "Escapes branch names to remove problematic characters." - (replace-regexp-in-string "[/]" "-" branch)) - -(defun magit-default-tracking-name-remote-plus-branch - (remote branch) - "Use the remote name plus a hyphen plus the escaped branch name for tracking branches." - (concat remote "-" (escape-branch-name branch))) - -(defun magit-default-tracking-name-branch-only - (remote branch) - "Use just the escaped branch name for tracking branches." - (escape-branch-name branch)) - -(defun magit-get-tracking-name (remote branch) - "Given a REMOTE and a BRANCH name, ask the user for a local -tracking brach name suggesting a sensible default." - (when (yes-or-no-p - (format "Create local tracking branch for %s? " branch)) - (let* ((default-name - (funcall magit-default-tracking-name-function remote branch)) - (chosen-name (read-string (format "Call local branch (%s): " default-name) - nil - nil - default-name))) - (when (magit-ref-exists-p (concat "refs/heads/" chosen-name)) - (error "'%s' already exists." chosen-name)) - chosen-name))) - -(defun magit-maybe-create-local-tracking-branch (rev) - "Depending on the users wishes, create a tracking branch for -rev... maybe." - (if (string-match "^\\(?:refs/\\)?remotes/\\([^/]+\\)/\\(.+\\)" rev) - (let* ((remote (match-string 1 rev)) - (branch (match-string 2 rev)) - (tracker-name (magit-get-tracking-name remote branch))) - (when tracker-name - (magit-run-git "checkout" "-b" tracker-name rev) - t)) - nil)) - -(magit-define-command 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 tracking branch. -\('git checkout [-b] REVISION')." - (interactive - (list (let ((current-branch (magit-get-current-branch)) - (default (magit-default-rev))) - (magit-read-rev "Switch to" - (unless (string= current-branch default) - default) - (if current-branch - (cons (concat "refs/heads/" current-branch "$") - magit-uninteresting-refs) - magit-uninteresting-refs))))) - (if revision - (when (not (magit-maybe-create-local-tracking-branch revision)) - (magit-save-some-buffers) - (magit-run-git "checkout" (magit-rev-to-git revision)) - (magit-update-vc-modeline default-directory)))) - -(defun magit-read-create-branch-args () - (let* ((cur-branch (magit-get-current-branch)) - (cur-point (magit-default-rev)) - (branch (read-string "Create branch: ")) - (parent (magit-read-rev "Parent" - (cond - ((eq magit-create-branch-behaviour 'at-point) cur-point) - ((eq magit-create-branch-behaviour 'at-head) cur-branch) - (t cur-branch))))) - (list branch parent))) - -(magit-define-command 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 (magit-read-create-branch-args)) - (when (and branch (not (string= branch "")) - parent) - (magit-save-some-buffers) - (magit-run-git "checkout" "-b" - branch - (append - magit-custom-options - (magit-rev-to-git parent))) - (magit-update-vc-modeline default-directory))) - -(defun magit-delete-branch (branch &optional force) - "Deletes a 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" (magit-default-rev 'notrim)) - current-prefix-arg)) - (let* ((remote (magit-remote-part-of-branch branch)) - (is-current (string= branch (magit-get-current-branch))) - (args (list "branch" - (if force "-D" "-d") - branch))) - (cond - (remote - (magit-run-git "push" remote (concat ":refs/heads/" (magit-branch-no-remote branch)))) - (is-current - (when (y-or-n-p "Cannot delete current branch. Switch to master first? ") - (progn - (magit-checkout "master") - (apply 'magit-run-git args)) - (message "The current branch was not deleted."))) - (t - (apply 'magit-run-git args))))) - -(defun magit-move-branch (old new &optional force) - "Renames or moves a branch. -With prefix, forces the move even if NEW already exists. -\('git branch [-m|-M] OLD NEW')." - (interactive (list (magit-read-rev "Old name" (magit-default-rev)) - (read-string "New name: ") - current-prefix-arg)) - (magit-run-git "branch" (if force - "-M" - "-m") - (magit-rev-to-git old) new)) - -(defun magit-guess-branch () - (magit-section-case (item info) - ((branch) - (magit-section-info (magit-current-section))) - ((wazzup commit) - (magit-section-info (magit-section-parent item))) - ((commit) (magit-name-rev (substring info 0 magit-sha1-abbrev-length))) - ((wazzup) info))) - -;;; Remotes - -(defun magit-add-remote (remote url) - "Adds a remote and fetches it. -\('git remote add REMOTE URL')." - (interactive (list (read-string "Add remote: ") - (read-string "URL: "))) - (magit-run-git "remote" "add" "-f" remote url)) - -(defun magit-remove-remote (remote) - "Deletes a remote. -\('git remote rm REMOTE')." - (interactive (list (magit-read-remote "Remote to delete"))) - (magit-run-git "remote" "rm" remote)) - -(defun magit-rename-remote (old new) - "Renames a remote. -\('git remote rename OLD NEW')." - (interactive (list (magit-read-remote "Old name") - (read-string "New name: "))) - (magit-run-git "remote" "rename" old new)) - -(defun magit-guess-remote () - (magit-section-case (item info) - ((branch) - (magit-section-info (magit-section-parent item))) - ((remote) - info) - (t - (if (string= info ".") - info - (magit-get-current-remote))))) - -;;; Merging - -(defun magit-merge (revision) - "Merge REVISION into the current 'HEAD'; leave changes uncommitted. -With a prefix-arg, the merge will be squashed. -\('git merge --no-commit [--squash|--no-ff] REVISION')." - (interactive - (list (magit-read-rev "Merge" (magit-default-rev)))) - (if revision - (apply 'magit-run-git - "merge" - (magit-rev-to-git revision) - magit-custom-options))) - -;;; Rebasing - -(defun magit-rebase-info () - "Returns a list indicating the state of an in-progress rebase, -if any." - (let ((git-dir (magit-git-dir))) - (cond ((file-exists-p (concat git-dir "rebase-merge")) - (list - ;; The commit we're rebasing onto, i.e. git rebase -i - (magit-name-rev (car (magit-file-lines (concat git-dir "rebase-merge/onto")))) - - ;; How many commits we've gone through - (length (magit-file-lines (concat git-dir "rebase-merge/done"))) - - ;; How many commits we have in total, without the comments - ;; at the end of git-rebase-todo.backup - (let ((todo-lines-with-comments (magit-file-lines (concat git-dir "rebase-merge/git-rebase-todo.backup")))) - (loop for i in todo-lines-with-comments - until (string= "" i) - count i)))) - ((and (file-exists-p (concat git-dir "rebase-apply")) - (file-exists-p (concat git-dir "rebase-apply/onto"))) - ;; we might be here because a non-interactive rebase failed: the - ;; patches didn't apply cleanly - (list - ;; The commit we're rebasing onto, i.e. git rebase -i - (magit-name-rev (car (magit-file-lines (concat git-dir "rebase-apply/onto")))) - - ;; How many commits we've gone through - (- (string-to-number (car (magit-file-lines (concat git-dir "rebase-apply/next")))) 1) - - ;; How many commits we have in total - (string-to-number (car (magit-file-lines (concat git-dir "rebase-apply/last")))) - )) - (t nil)))) - -(defun magit-rebase-step () - (interactive) - (let ((info (magit-rebase-info))) - (if (not info) - (let* ((current-branch (magit-get-current-branch)) - (rev (magit-read-rev "Rebase to" - (magit-format-ref (magit-remote-branch-for current-branch t)) - (if current-branch - (cons (concat "refs/heads/" current-branch) - magit-uninteresting-refs) - magit-uninteresting-refs)))) - (if rev - (magit-run-git "rebase" (magit-rev-to-git rev)))) - (let ((cursor-in-echo-area t) - (message-log-max nil)) - (message "Rebase in progress. [A]bort, [S]kip, or [C]ontinue? ") - (let ((reply (read-event))) - (case reply - ((?A ?a) - (magit-run-git-async "rebase" "--abort")) - ((?S ?s) - (magit-run-git-async "rebase" "--skip")) - ((?C ?c) - (magit-run-git-async "rebase" "--continue")))))))) - -;;; Resetting - -(magit-define-command 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-default-rev) - "HEAD^")) - current-prefix-arg)) - (when revision - (magit-run-git "reset" (if hard "--hard" "--soft") - (magit-rev-to-git revision)) - (magit-update-vc-modeline default-directory))) - -(magit-define-command 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-default-rev) - "HEAD")))) - (magit-reset-head revision t)) - -(magit-define-command 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") - (if include-untracked - (magit-run-git "clean" "-fd" (if include-ignored - "-x" - "")))))) - -;;; Rewriting - -(defun magit-read-rewrite-info () - (when (file-exists-p (concat (magit-git-dir) "magit-rewrite-info")) - (with-temp-buffer - (insert-file-contents (concat (magit-git-dir) "magit-rewrite-info")) - (goto-char (point-min)) - (read (current-buffer))))) - -(defun magit-write-rewrite-info (info) - (with-temp-file (concat (magit-git-dir) "magit-rewrite-info") - (prin1 info (current-buffer)) - (princ "\n" (current-buffer)))) - -(magit-define-inserter pending-commits () - (let* ((info (magit-read-rewrite-info)) - (pending (cdr (assq 'pending info)))) - (when pending - (magit-with-section 'pending nil - (insert (propertize "Pending commits:\n" - 'face 'magit-section-title)) - (dolist (p pending) - (let* ((commit (car p)) - (properties (cdr p)) - (used (plist-get properties 'used))) - (magit-with-section commit 'commit - (magit-set-section-info commit) - (insert (magit-git-string - "log" "--max-count=1" - (if used - "--pretty=format:. %s" - "--pretty=format:* %s") - commit "--") - "\n"))))) - (insert "\n")))) - -(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-need-refresh)))) - -(defun magit-rewrite-set-used () - (interactive) - (magit-section-action (item info) - ((pending commit) - (magit-rewrite-set-commit-property info 'used t)))) - -(defun magit-rewrite-set-unused () - (interactive) - (magit-section-action (item info) - ((pending commit) - (magit-rewrite-set-commit-property info 'used nil)))) - -(magit-define-inserter pending-changes () - (let* ((info (magit-read-rewrite-info)) - (orig (cadr (assq 'orig info)))) - (when orig - (let ((magit-hide-diffs t)) - (magit-git-section 'pending-changes - "Pending changes" - 'magit-wash-diffs - "diff" (magit-diff-U-arg) "-R" orig))))) - -(defun magit-rewrite-start (from &optional onto) - (interactive (list (magit-read-rev "Rewrite from" (magit-default-rev)))) - (or (magit-everything-clean-p) - (error "You have uncommitted changes")) - (or (not (magit-read-rewrite-info)) - (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)) - (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 - (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 - (error "No rewrite in progress")) - (or (magit-everything-clean-p) - (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-with-refresh - (magit-rewrite-finish-step t))) - -(defun magit-rewrite-finish-step (first-p) - (let ((info (magit-read-rewrite-info))) - (or info - (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-apply-commit commit t (not first-p)) - (magit-rewrite-set-commit-property commit 'used t) - (magit-rewrite-finish-step nil)))))) - -;;; Updating, pull, and push - -(magit-define-command fetch (remote) - "Fetch from REMOTE." - (interactive (list (magit-read-remote))) - (apply 'magit-run-git-async "fetch" remote magit-custom-options)) - -(magit-define-command 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)))) - -(magit-define-command remote-update () - "Update all remotes." - (interactive) - (apply 'magit-run-git-async "remote" "update" magit-custom-options)) - -(magit-define-command pull () - "Run git pull against the current remote." - (interactive) - (let* ((branch (magit-get-current-branch)) - (branch-remote (magit-get-remote branch)) - (config-branch (and branch (magit-get "branch" branch "merge"))) - (merge-branch (or (and config-branch (not current-prefix-arg)) - (magit-read-remote-branch - branch-remote (format "Pull from: "))))) - (when (and branch (not config-branch)) - (magit-set branch-remote "branch" branch "remote") - (magit-set (format "refs/heads/%s" merge-branch) - "branch" branch "merge")) - (apply 'magit-run-git-async "pull" "-v" magit-custom-options))) - -(eval-when-compile (require 'eshell)) - -(defun magit-parse-arguments (command) - (require 'eshell) - (with-temp-buffer - (insert command) - (mapcar 'eval (eshell-parse-arguments (point-min) (point-max))))) - -(defun magit-shell-command (command) - "Perform arbitrary shell COMMAND." - (interactive "sCommand: ") - (let ((args (magit-parse-arguments command)) - (magit-process-popup-time 0)) - (magit-run* args nil nil nil t))) - -(defvar magit-git-command-history nil) - -(defun magit-git-command (command) - "Perform arbitrary Git COMMAND. - -Similar to `magit-shell-command', but involves slightly less -typing and automatically refreshes the status buffer." - (interactive - (list (read-string "Run git like this: " nil 'magit-git-command-history))) - (require 'pcomplete) - (let ((args (magit-parse-arguments command)) - (magit-process-popup-time 0)) - (magit-with-refresh - (magit-run* (append (cons magit-git-executable - magit-git-standard-options) - args) - nil nil nil t)))) - -(magit-define-command push-tags () - "Push tags." - (interactive) - (magit-run-git-async "push" "--tags")) - -(magit-define-command push () - "Push the current branch to a remote repository. - -With no prefix argument, ask `magit-get-remote' what remote to -use for this branch. - -With a prefix arg \(e.g., \\[universal-argument] \\[magit-push]), \ -ask user instead. - -With \\[universal-argument] \\[universal-argument] as prefix, \ -also prompt user for the remote branch; -otherwise, try to use the branch..merge git-config(1) -option, falling back to something hairy if that is unset." - (interactive) - (let* ((branch (or (magit-get-current-branch) - (error "Don't push a detached head. That's gross"))) - (branch-remote (magit-get-remote branch)) - (push-remote (if (or current-prefix-arg - (not branch-remote)) - (magit-read-remote (format "Push %s to remote" - branch) - branch-remote) - branch-remote)) - (ref-branch (or (and (>= (prefix-numeric-value current-prefix-arg) 16) - (magit-read-remote-branch - push-remote (format "Push %s as branch" branch))) - (magit-get "branch" branch "merge")))) - (if (and (not ref-branch) - (eq magit-set-upstream-on-push 'refuse)) - (error "Not pushing since no upstream has been set.") - (let ((set-upstream-on-push (and (not ref-branch) - (or (eq magit-set-upstream-on-push 'dontask) - (and (eq magit-set-upstream-on-push t) - (yes-or-no-p "Set upstream while pushing? ")))))) - (if (and (not branch-remote) - (not current-prefix-arg)) - (magit-set push-remote "branch" branch "remote")) - (apply 'magit-run-git-async "push" "-v" push-remote - (if ref-branch - (format "%s:%s" branch ref-branch) - branch) - (if set-upstream-on-push - (cons "--set-upstream" magit-custom-options) - magit-custom-options)) - ;; Although git will automatically set up the remote, - ;; it doesn't set up the branch to merge (at least as of Git 1.6.6.1), - ;; so we have to do that manually. - (unless ref-branch - (magit-set (concat "refs/heads/" branch) "branch" branch "merge")))))) - -;;; Log edit mode - -(defvar magit-log-edit-buffer-name "*magit-edit-log*" - "Buffer name for composing commit messages.") - -(defvar magit-log-edit-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-c") 'magit-log-edit-commit) - (define-key map (kbd "C-x #") 'magit-log-edit-commit) - (define-key map (kbd "C-c C-a") 'magit-log-edit-toggle-amending) - (define-key map (kbd "C-c C-s") 'magit-log-edit-toggle-signoff) - (define-key map (kbd "C-c C-t") 'magit-log-edit-toggle-author) - (define-key map (kbd "C-c C-e") 'magit-log-edit-toggle-allow-empty) - (define-key map (kbd "M-p") 'log-edit-previous-comment) - (define-key map (kbd "M-n") 'log-edit-next-comment) - (define-key map (kbd "C-c C-k") 'magit-log-edit-cancel-log-message) - (define-key map (kbd "C-c C-]") 'magit-log-edit-cancel-log-message) - (define-key map (kbd "C-x C-s") (lambda () - (interactive) - (message "Not saved. Use C-c C-c to finalize this commit message."))) - map)) - -(defvar magit-pre-log-edit-window-configuration nil) - -(define-derived-mode magit-log-edit-mode text-mode "Magit Log Edit" - ;; Recognize changelog-style paragraphs - (set (make-local-variable 'paragraph-start) - (concat paragraph-start "\\|*\\|("))) - -(defun magit-log-edit-cleanup () - (save-excursion - (goto-char (point-min)) - (goto-char (point-min)) - (if (re-search-forward "[ \t\n]*\\'" nil t) - (replace-match "\n" nil nil)))) - -(defun magit-log-edit-append (str) - (with-current-buffer (get-buffer-create magit-log-edit-buffer-name) - (goto-char (point-max)) - (insert str "\n"))) - -(defconst magit-log-header-end "-- End of Magit header --\n") - -(defun magit-log-edit-get-fields () - (let ((buf (get-buffer magit-log-edit-buffer-name)) - (result nil)) - (if buf - (with-current-buffer buf - (goto-char (point-min)) - (while (looking-at "^\\([A-Za-z0-9-_]+\\): *\\(.+\\)?$") - (setq result (acons (intern (downcase (match-string 1))) - (read (or (match-string 2) "nil")) - result)) - (forward-line)) - (if (not (looking-at (regexp-quote magit-log-header-end))) - (setq result nil)))) - (nreverse result))) - -(defun magit-log-edit-set-fields (fields) - (let ((buf (get-buffer-create magit-log-edit-buffer-name))) - (with-current-buffer buf - (goto-char (point-min)) - (if (search-forward-regexp (format "^\\([A-Za-z0-9-_]+:.*\n\\)*%s" - (regexp-quote magit-log-header-end)) - nil t) - (delete-region (match-beginning 0) (match-end 0))) - (goto-char (point-min)) - (when fields - (while fields - (insert (capitalize (symbol-name (caar fields))) ": " - (prin1-to-string (cdar fields)) "\n") - (setq fields (cdr fields))) - (insert magit-log-header-end))))) - -(defun magit-log-edit-set-field (name value) - (let* ((fields (magit-log-edit-get-fields)) - (cell (assq name fields))) - (cond (cell - (if value - (rplacd cell value) - (setq fields (delq cell fields)))) - (t - (if value - (setq fields (append fields (list (cons name value))))))) - (magit-log-edit-set-fields fields))) - -(defun magit-log-edit-get-field (name) - (cdr (assq name (magit-log-edit-get-fields)))) - -(defun magit-log-edit-toggle-field (name default) - "Toggle the log-edit field named NAME. -If it's currently unset, set it to DEFAULT (t or nil). - -Return nil if the field is toggled off, and non-nil if it's -toggled on. When it's toggled on for the first time, return -'first." - (let* ((fields (magit-log-edit-get-fields)) - (cell (assq name fields)) yesp) - (if cell - (progn - (setq yesp (equal (cdr cell) "yes")) - (rplacd cell (if yesp "no" "yes"))) - (setq fields (acons name (if default "yes" "no") fields)) - (setq yesp (if default 'first))) - (magit-log-edit-set-fields fields) - yesp)) - -(defun magit-log-edit-toggle-input (name default) - "Toggle the log-edit input named NAME. -If it's currently unset, set it to DEFAULT (a string). If it is -set remove it. - -Return nil if the input is toggled off, and its valud if it's -toggled on." - (let* ((fields (magit-log-edit-get-fields)) - (cell (assq name fields)) - result) - (if cell - (progn - (setq fields (assq-delete-all name fields) - result (cdr cell))) - (setq fields (acons name default fields))) - (magit-log-edit-set-fields fields) - result)) - -(defun magit-log-edit-setup-author-env (author) - "Set GIT_AUTHOR_* variables from AUTHOR spec. -If AUTHOR is nil, honor default values from -environment (potentially empty)." - (when author - ;; XXX - this is a bit strict, probably. - (or (string-match "\\(.*\\) <\\(.*\\)>\\(?:,\\s-*\\(.+\\)\\)?" author) - (error "Can't parse author string")) - ;; Shucks, setenv destroys the match data. - (let ((name (match-string 1 author)) - (email (match-string 2 author)) - (date (match-string 3 author))) - (make-local-variable 'process-environment) - (setenv "GIT_AUTHOR_NAME" name) - (setenv "GIT_AUTHOR_EMAIL" email) - (if date - (setenv "GIT_AUTHOR_DATE" date))))) - -(defun magit-log-edit-push-to-comment-ring (comment) - (when (or (ring-empty-p log-edit-comment-ring) - (not (equal comment (ring-ref log-edit-comment-ring 0)))) - (ring-insert log-edit-comment-ring comment))) - -(defun magit-log-edit-commit () - "Finish edits and create new commit object. -\('git commit ...')" - (interactive) - (let* ((fields (magit-log-edit-get-fields)) - (amend (equal (cdr (assq 'amend fields)) "yes")) - (allow-empty (equal (cdr (assq 'allow-empty fields)) "yes")) - (commit-all (equal (cdr (assq 'commit-all fields)) "yes")) - (sign-off-field (assq 'sign-off fields)) - (sign-off (if sign-off-field - (equal (cdr sign-off-field) "yes") - magit-commit-signoff)) - (tag-rev (cdr (assq 'tag-rev fields))) - (tag-name (cdr (assq 'tag-name fields))) - (author (cdr (assq 'author fields))) - (tag-options (cdr (assq 'tag-options fields)))) - - (unless (or (magit-anything-staged-p) - allow-empty - amend - tag-name - (file-exists-p (concat (magit-git-dir) "MERGE_HEAD")) - (and commit-all - (not (magit-everything-clean-p)))) - (error "Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?" - (key-description (car (where-is-internal - 'magit-log-edit-toggle-amending))) - (key-description (car (where-is-internal - 'magit-log-edit-toggle-allow-empty))))) - - (magit-log-edit-push-to-comment-ring (buffer-string)) - (magit-log-edit-setup-author-env author) - (magit-log-edit-set-fields nil) - (magit-log-edit-cleanup) - (if (= (buffer-size) 0) - (insert "(Empty description)\n")) - (let ((env process-environment) - (commit-buf (current-buffer))) - (with-current-buffer (magit-find-status-buffer default-directory) - (let ((process-environment env)) - (cond (tag-name - (apply #'magit-run-git-with-input commit-buf - "tag" (append tag-options (list tag-name "-a" "-F" "-" tag-rev)))) - (t - (apply #'magit-run-async-with-input commit-buf - magit-git-executable - (append magit-git-standard-options - '("commit") - magit-custom-options - '("-F" "-") - (if (and commit-all (not allow-empty)) '("--all") '()) - (if amend '("--amend") '()) - (if allow-empty '("--allow-empty")) - (if sign-off '("--signoff") '())))))))) - ;; shouldn't we kill that buffer altogether? - (erase-buffer) - (let ((magit-buf magit-buffer-internal)) - (bury-buffer) - (set-buffer magit-buf)) - (when (file-exists-p (concat (magit-git-dir) "MERGE_MSG")) - (delete-file (concat (magit-git-dir) "MERGE_MSG"))) - ;; potentially the local environment has been altered with settings that - ;; were specific to this commit. Let's revert it - (kill-local-variable 'process-environment) - (magit-update-vc-modeline default-directory) - (when magit-pre-log-edit-window-configuration - (set-window-configuration magit-pre-log-edit-window-configuration) - (setq magit-pre-log-edit-window-configuration nil)))) - -(defun magit-log-edit-cancel-log-message () - "Abort edits and erase commit message being composed." - (interactive) - (when (or (not magit-log-edit-confirm-cancellation) - (yes-or-no-p - "Really cancel editing the log (any changes will be lost)?")) - (erase-buffer) - (bury-buffer) - (when magit-pre-log-edit-window-configuration - (set-window-configuration magit-pre-log-edit-window-configuration) - (setq magit-pre-log-edit-window-configuration nil)))) - -(defun magit-log-edit-toggle-amending () - "Toggle whether this will be an amendment to the previous commit. -\(i.e., whether eventual commit does 'git commit --amend')" - (interactive) - (when (eq (magit-log-edit-toggle-field 'amend t) 'first) - (magit-log-edit-append - (magit-trim-line (magit-format-commit "HEAD" "%s%n%n%b"))))) - -(defun magit-log-edit-toggle-signoff () - "Toggle whether this commit will include a signoff. -\(i.e., whether eventual commit does 'git commit --signoff')" - (interactive) - (magit-log-edit-toggle-field 'sign-off (not magit-commit-signoff))) - -(defun magit-log-edit-toggle-author () - "Toggle whether this commit will include an author. -\(i.e., whether eventual commit is run with GIT_AUTHOR_NAME and -GIT_AUTHOR_EMAIL set)" - (interactive) - (magit-log-edit-toggle-input 'author (format "%s <%s>" - (or (magit-get "user" "name") "Author Name") - (or (magit-get "user" "email") "author@email")))) - -(defun magit-log-edit-toggle-allow-empty () - "Toggle whether this commit is allowed to be empty. -This means that the eventual commit does 'git commit --allow-empty'." - (interactive) - (magit-log-edit-toggle-field 'allow-empty t)) - -(defun magit-pop-to-log-edit (operation) - (let ((dir default-directory) - (magit-buf (current-buffer)) - (buf (get-buffer-create magit-log-edit-buffer-name))) - (setq magit-pre-log-edit-window-configuration - (current-window-configuration)) - (pop-to-buffer buf) - (setq default-directory dir) - (when (file-exists-p (concat (magit-git-dir) "MERGE_MSG")) - (insert-file-contents (concat (magit-git-dir) "MERGE_MSG"))) - (magit-log-edit-mode) - (make-local-variable 'magit-buffer-internal) - (setq magit-buffer-internal magit-buf) - (message "Type C-c C-c to %s (C-c C-k to cancel)." operation))) - -(defun magit-log-edit (&optional arg) - "Brings up a buffer to allow editing of commit messages. - -Giving a simple prefix arg will amend a previous commit, while -a double prefix arg will allow creating an empty one. - -If there is a rebase in progress, offer the user the option to -continue it. - -\\{magit-log-edit-mode-map}" - (interactive "P") - ;; If repository is dirty there is no point in trying to - ;; suggest to continue the rebase. Git will rebuke you and exit with - ;; error code, so suggest it only if theres absolutely nothing else - ;; to do and rebase is ongoing. - (if (and (magit-everything-clean-p) - (magit-rebase-info) - (y-or-n-p "Rebase in progress. Continue it? ")) - (magit-run-git-async "rebase" "--continue") - - ;; If there's nothing staged, set commit flag to `nil', thus - ;; avoiding unnescessary popping up of the log edit buffer in case - ;; when user chose to forgo commiting all unstaged changes - (let ((amend-p (= (prefix-numeric-value arg) 4)) - (empty-p (= (prefix-numeric-value arg) 16))) - (when (and magit-commit-all-when-nothing-staged - (not (magit-everything-clean-p)) - (not (magit-anything-staged-p))) - (cond ((eq magit-commit-all-when-nothing-staged 'ask-stage) - (when (y-or-n-p "Nothing staged. Stage everything now? ") - (magit-stage-all))) - ((not (magit-log-edit-get-field 'commit-all)) - (when (or (eq magit-commit-all-when-nothing-staged t) - (y-or-n-p - "Nothing staged. Commit all unstaged changes? ")) - (magit-log-edit-set-field 'commit-all "yes"))))) - (when amend-p - (magit-log-edit-toggle-amending)) - (when empty-p - (magit-log-edit-toggle-allow-empty)) - (let ((author-email (or (getenv "GIT_AUTHOR_EMAIL") "")) - (author-name (or (getenv "GIT_AUTHOR_NAME") "")) - (author-date (or (getenv "GIT_AUTHOR_DATE") ""))) - (if (not (string= author-email "")) - (magit-log-edit-set-field 'author (format "%s <%s>%s" - (if (string= "" author-name) author-email author-name) - author-email - (if (string= "" author-date) "" (format ", %s" author-date)))))) - (magit-pop-to-log-edit "commit")))) - -(defun magit-add-log () - (interactive) - (cond ((magit-rebase-info) - (if (y-or-n-p "Rebase in progress. Continue it? ") - (magit-run-git-async "rebase" "--continue"))) - (t - (let ((section (magit-current-section))) - (let ((fun (if (eq (magit-section-type section) 'hunk) - (save-window-excursion - (save-excursion - (magit-visit-item) - (add-log-current-defun))) - nil)) - (file (magit-diff-item-file - (cond ((eq (magit-section-type section) 'hunk) - (magit-hunk-item-diff section)) - ((eq (magit-section-type section) 'diff) - section) - (t - (error "No change at point")))))) - (magit-log-edit nil) - (goto-char (point-min)) - (cond ((not (search-forward-regexp - (format "^\\* %s" (regexp-quote file)) nil t)) - ;; No entry for file, create it. - (goto-char (point-max)) - (insert (format "\n* %s" file)) - (if fun - (insert (format " (%s)" fun))) - (insert ": ")) - (fun - ;; found entry for file, look for fun - (let ((limit (or (save-excursion - (and (search-forward-regexp "^\\* " - nil t) - (match-beginning 0))) - (point-max)))) - (cond ((search-forward-regexp (format "(.*\\<%s\\>.*):" - (regexp-quote fun)) - limit t) - ;; found it, goto end of current entry - (if (search-forward-regexp "^(" 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))))))))) - -;;; Tags - -(magit-define-command tag (name rev) - "Create a new lightweight tag with the given NAME at REV. -\('git tag NAME')." - (interactive - (list - (read-string "Tag name: ") - (magit-read-rev "Place tag on: " (or (magit-default-rev) "HEAD")))) - (apply #'magit-run-git "tag" (append magit-custom-options (list name rev)))) - -(magit-define-command annotated-tag (name rev) - "Start composing an annotated tag with the given NAME. -Tag will point to the current 'HEAD'." - (interactive - (list - (read-string "Tag name: ") - (magit-read-rev "Place tag on: " (or (magit-default-rev) "HEAD")))) - (magit-log-edit-set-field 'tag-name name) - (magit-log-edit-set-field 'tag-rev rev) - (magit-log-edit-set-field 'tag-options magit-custom-options) - (magit-pop-to-log-edit "tag")) - -;;; Stashing - -(defun magit-wash-stash () - (if (search-forward-regexp "stash@{\\(.*?\\)}" (line-end-position) t) - (let ((stash (match-string-no-properties 0)) - (name (match-string-no-properties 1))) - (delete-region (match-beginning 0) (match-end 0)) - (goto-char (match-beginning 0)) - (fixup-whitespace) - (goto-char (line-beginning-position)) - (insert name) - (goto-char (line-beginning-position)) - (magit-with-section stash 'stash - (magit-set-section-info stash) - (forward-line))) - (forward-line)) - t) - -(defun magit-wash-stashes () - (let ((magit-old-top-section nil)) - (magit-wash-sequence #'magit-wash-stash))) - -(magit-define-inserter stashes () - (magit-git-section 'stashes - "Stashes:" 'magit-wash-stashes - "stash" "list")) - -(magit-define-command 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 "sStash description: ") - (apply 'magit-run-git `("stash" "save" ,@magit-custom-options "--" ,description))) - -(magit-define-command 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-with-refresh - (apply 'magit-run-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}"))) - -(defvar magit-currently-shown-stash nil) - -(define-derived-mode magit-stash-mode magit-mode "Magit Stash" - "Mode for looking at a git stash. - -\\{magit-stash-mode-map}" - :group 'magit) - -(defvar magit-stash-buffer-name "*magit-stash*" - "Buffer name for displaying a stash.") - -(defun magit-show-stash (stash &optional scroll) - (when (magit-section-p stash) - (setq stash (magit-section-info stash))) - (let ((dir default-directory) - (buf (get-buffer-create magit-stash-buffer-name)) - (stash-id (magit-git-string "rev-list" "-1" stash))) - (cond ((and (equal magit-currently-shown-stash stash-id) - (with-current-buffer buf - (> (length (buffer-string)) 1))) - (let ((win (get-buffer-window buf))) - (cond ((not win) - (display-buffer buf)) - (scroll - (with-selected-window win - (funcall scroll)))))) - (t - (setq magit-currently-shown-stash stash-id) - (display-buffer buf) - (with-current-buffer buf - (set-buffer buf) - (goto-char (point-min)) - (let* ((range (cons (concat stash "^2^") stash)) - (magit-current-diff-range range) - (args (magit-rev-range-to-git range))) - (magit-mode-init dir 'magit-diff-mode #'magit-refresh-diff-buffer - range args))))))) -;;; Commits - -(defun magit-commit-at-point (&optional nil-ok-p) - (let* ((section (magit-current-section)) - (commit (if (and section - (eq (magit-section-type section) 'commit)) - (magit-section-info section) - (get-text-property (point) 'revision)))) - (if nil-ok-p - commit - (or commit - (error "No commit at point"))))) - -(defun magit-apply-commit (commit &optional docommit noerase revert) - (let* ((parent-id (magit-choose-parent-id commit "cherry-pick")) - (success (magit-run* `(,magit-git-executable - ,@magit-git-standard-options - ,(if revert "revert" "cherry-pick") - ,@(if parent-id - (list "-m" (number-to-string parent-id))) - ,@(if (not docommit) (list "--no-commit")) - ,commit) - nil noerase))) - (when (and (not docommit) success) - (cond (revert - (magit-log-edit-append - (magit-format-commit commit "Reverting \"%s\""))) - (t - (magit-log-edit-append - (magit-format-commit commit "%s%n%n%b")) - (magit-log-edit-set-field - 'author - (magit-format-commit commit "%an <%ae>, %ai"))))) - success)) - -(defun magit-apply-item () - (interactive) - (magit-section-action (item info "apply") - ((pending commit) - (magit-apply-commit info) - (magit-rewrite-set-commit-property info 'used t)) - ((commit) - (magit-apply-commit info)) - ((unstaged *) - (error "Change is already in your working tree")) - ((staged *) - (error "Change is already in your working tree")) - ((hunk) - (magit-apply-hunk-item item)) - ((diff) - (magit-apply-diff-item item)) - ((stash) - (magit-run-git "stash" "apply" info)))) - -(defun magit-cherry-pick-item () - (interactive) - (magit-section-action (item info "cherry-pick") - ((pending commit) - (magit-apply-commit info t) - (magit-rewrite-set-commit-property info 'used t)) - ((commit) - (magit-apply-commit info t)) - ((stash) - (magit-run-git "stash" "pop" info)))) - -(defmacro magit-with-revert-confirmation (&rest body) - `(when (or (not magit-revert-item-confirm) - (yes-or-no-p "Really revert this item? ")) - ,@body)) - -(defun magit-revert-item () - (interactive) - (magit-section-action (item info "revert") - ((pending commit) - (magit-with-revert-confirmation - (magit-apply-commit info nil nil t) - (magit-rewrite-set-commit-property info 'used nil))) - ((commit) - (magit-with-revert-confirmation - (magit-apply-commit info nil nil t))) - ;; Reverting unstaged changes cannot be undone - ((unstaged *) - (magit-discard-item)) - ((hunk) - (magit-with-revert-confirmation - (magit-apply-hunk-item-reverse item))) - ((diff) - (magit-with-revert-confirmation - (magit-apply-diff-item item "--reverse"))))) - -(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") - (make-local-variable 'magit-log-cutoff-length) - (cond - ((numberp arg) - (setq magit-log-cutoff-length (+ magit-log-cutoff-length arg))) - (arg - (setq magit-log-cutoff-length magit-log-infinite-length)) - (t (setq magit-log-cutoff-length (* magit-log-cutoff-length 2)))) - (let ((old-point (point))) - (magit-refresh) - (goto-char old-point))) - -(defun magit-refresh-log-buffer (range style args) - (magit-configure-have-graph) - (magit-configure-have-decorate) - (magit-configure-have-abbrev) - (setq magit-current-range range) - (magit-create-log-buffer-sections - (apply #'magit-git-section nil - (magit-rev-range-describe range "Commits") - (apply-partially 'magit-wash-log style) - `("log" - ,(format "--max-count=%s" magit-log-cutoff-length) - ,"--abbrev-commit" - ,(format "--abbrev=%s" magit-sha1-abbrev-length) - ,@(cond ((eq style 'long) (list "--stat" "-z")) - ((eq style 'oneline) (list "--pretty=oneline")) - (t nil)) - ,@(if magit-have-decorate (list "--decorate=full")) - ,@(if magit-have-graph (list "--graph")) - ,@args - "--")))) - -(define-derived-mode magit-log-mode magit-mode "Magit Log" - "Mode for looking at git log. - -\\{magit-log-mode-map}" - :group 'magit) - -(defvar magit-log-buffer-name "*magit-log*" - "Buffer name for display of log entries.") - -(magit-define-command log-ranged () - (interactive) - (magit-log t)) -(define-obsolete-function-alias 'magit-display-log-ranged 'magit-log-ranged) - -(magit-define-command log (&optional ask-for-range &rest extra-args) - (interactive) - (let* ((log-range (if ask-for-range - (magit-read-rev-range "Log" "HEAD") - "HEAD")) - (topdir (magit-get-top-dir default-directory)) - (args (nconc (list (magit-rev-range-to-git log-range)) - magit-custom-options - extra-args))) - (magit-buffer-switch magit-log-buffer-name) - (magit-mode-init topdir 'magit-log-mode #'magit-refresh-log-buffer log-range - 'oneline args))) - -(define-obsolete-function-alias 'magit-display-log 'magit-log) - -(magit-define-command log-long-ranged () - (interactive) - (magit-log-long t)) - -(magit-define-command log-long (&optional ranged) - (interactive) - (let* ((range (if ranged - (magit-read-rev-range "Long log" "HEAD") - "HEAD")) - (topdir (magit-get-top-dir default-directory)) - (args (append (list (magit-rev-range-to-git range)) - magit-custom-options))) - (magit-buffer-switch magit-log-buffer-name) - (magit-mode-init topdir 'magit-log-mode #'magit-refresh-log-buffer range - 'long args))) - -;;; Reflog - -(defvar magit-reflog-head nil - "The HEAD of the reflog in the current buffer. -This is only non-nil in reflog buffers.") -(make-variable-buffer-local 'magit-reflog-head) - -(defun magit-refresh-reflog-buffer (head args) - (setq magit-reflog-head head) - (magit-create-log-buffer-sections - (apply #'magit-git-section - 'reflog (format "Local history of head %s" head) 'magit-wash-log "log" - (append magit-git-log-options - (list - "--walk-reflogs" - (format "--max-count=%s" magit-log-cutoff-length) - args))))) - -(define-derived-mode magit-reflog-mode magit-log-mode "Magit Reflog" - "Mode for looking at git reflog. - -\\{magit-reflog-mode-map}" - :group 'magit) - -(magit-define-command reflog (&optional ask-for-range) - (interactive) - (let ((at (or (if ask-for-range - (magit-read-rev "Reflog of" (or (magit-guess-branch) "HEAD"))) - "HEAD"))) - (let* ((topdir (magit-get-top-dir default-directory)) - (args (magit-rev-to-git at))) - (magit-buffer-switch "*magit-reflog*") - (magit-mode-init topdir 'magit-reflog-mode - #'magit-refresh-reflog-buffer at args)))) - -(magit-define-command reflog-ranged () - (interactive) - (magit-reflog t)) - -;;; Diffing - -(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 (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))) - (if (eq 'hunk (magit-section-type diff)) - (setq diff (magit-section-parent diff))) - (unless (eq 'diff (magit-section-type diff)) - (error "No diff at this location")) - (let* ((type (magit-diff-item-kind diff)) - (file1 (magit-diff-item-file diff)) - (file2 (magit-diff-item-file2 diff)) - (range (magit-diff-item-range diff))) - (cond - ((memq type '(new deleted typechange)) - (message "Why ediff a %s file?" type)) - ((and (eq type 'unmerged) - (eq (cdr range) 'working)) - (magit-interactive-resolve file1)) - ((consp (car range)) - (magit-ediff* (magit-show (caar range) file2) - (magit-show (cdar range) file2) - (magit-show (cdr range) file1))) - (t - (magit-ediff* (magit-show (car range) file2) - (magit-show (cdr range) file1))))))) - -(defun magit-ediff* (a b &optional c) - (setq magit-ediff-buffers (list a b c)) - (setq magit-ediff-windows (current-window-configuration)) - (add-hook 'ediff-quit-hook 'magit-ediff-restore 'append) - (if c - (ediff-buffers3 a b c) - (ediff-buffers a b))) - -(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) - (if (and (null (buffer-file-name buffer)) - (buffer-live-p buffer)) - (kill-buffer buffer))) - (setq magit-ediff-buffers nil) - (set-window-configuration magit-ediff-windows) - (remove-hook 'ediff-quit-hook 'magit-ediff-restore)) - -(defun magit-refresh-diff-buffer (range args) - (let ((magit-current-diff-range (cond - ((stringp range) - (cons range 'working)) - ((null (cdr range)) - (cons (car range) 'working)) - (t - range)))) - (setq magit-current-range range) - (magit-create-buffer-sections - (magit-git-section 'diffbuf - (magit-rev-range-describe range "Changes") - 'magit-wash-diffs - "diff" (magit-diff-U-arg) args "--")))) - -(define-derived-mode magit-diff-mode magit-mode "Magit Diff" - "Mode for looking at a git diff. - -\\{magit-diff-mode-map}" - :group 'magit) - -(magit-define-command diff (range) - (interactive (list (magit-read-rev-range "Diff"))) - (if range - (let* ((dir default-directory) - (args (magit-rev-range-to-git range)) - (buf (get-buffer-create "*magit-diff*"))) - (display-buffer buf) - (with-current-buffer buf - (magit-mode-init dir 'magit-diff-mode #'magit-refresh-diff-buffer range args))))) - -(magit-define-command diff-working-tree (rev) - (interactive (list (magit-read-rev "Diff with" (magit-default-rev)))) - (magit-diff (or rev "HEAD"))) - -(defun magit-diff-with-mark () - (interactive) - (magit-diff (cons (magit-marked-commit) - (magit-commit-at-point)))) - -;;; Wazzup - -(defvar magit-wazzup-head nil - "The integration head for the current wazzup buffer. -This is only non-nil in wazzup buffers.") -(make-variable-buffer-local 'magit-wazzup-head) - -(defvar magit-wazzup-all-p nil - "Non-nil if the current wazzup buffer displays excluded branches. -This is only meaningful in wazzup buffers.") -(make-variable-buffer-local 'magit-wazzup-all-p) - -(defun magit-wazzup-toggle-ignore (branch edit) - (let ((ignore-file (concat (magit-git-dir) "info/wazzup-exclude"))) - (if edit - (setq branch (read-string "Branch to ignore for wazzup: " branch))) - (let ((ignored (magit-file-lines ignore-file))) - (cond ((member branch ignored) - (when (or (not edit) - (y-or-n-p "Branch %s is already ignored. Unignore? ")) - (setq ignored (delete branch ignored)))) - (t - (setq ignored (append ignored (list branch))))) - (magit-write-file-lines ignore-file ignored) - (magit-need-refresh)))) - -(defun magit-refresh-wazzup-buffer (head all) - (setq magit-wazzup-head head) - (setq magit-wazzup-all-p all) - (let ((branch-desc (or head "(detached) HEAD"))) - (unless head (setq head "HEAD")) - (magit-create-buffer-sections - (magit-with-section 'wazzupbuf nil - (insert (format "Wazzup, %s\n\n" branch-desc)) - (let* ((excluded (magit-file-lines (concat (magit-git-dir) "info/wazzup-exclude"))) - (all-branches (magit-list-interesting-refs)) - (branches (if all all-branches - (delq nil (mapcar - (lambda (b) - (and (not - (member (cdr b) excluded)) - b)) - all-branches)))) - (reported (make-hash-table :test #'equal))) - (dolist (branch branches) - (let* ((name (car branch)) - (ref (cdr branch)) - (hash (magit-rev-parse ref)) - (reported-branch (gethash hash reported))) - (unless (or (and reported-branch - (string= (file-name-nondirectory ref) - reported-branch)) - (not (magit-git-string "merge-base" head ref))) - (puthash hash (file-name-nondirectory ref) reported) - (let* ((n (length (magit-git-lines "log" "--pretty=oneline" - (concat head ".." ref)))) - (section - (let ((magit-section-hidden-default t)) - (magit-git-section - (cons ref 'wazzup) - (format "%s unmerged commits in %s%s" - n name - (if (member ref excluded) - " (normally ignored)" - "")) - 'magit-wash-log - "log" - (format "--max-count=%s" magit-log-cutoff-length) - "--abbrev-commit" - (format "--abbrev=%s" magit-sha1-abbrev-length) - "--graph" - "--pretty=oneline" - (format "%s..%s" head ref) - "--")))) - (magit-set-section-info ref section)))))))))) - -(define-derived-mode magit-wazzup-mode magit-mode "Magit Wazzup" - "Mode for looking at commits that could be merged from other branches. - -\\{magit-wazzup-mode-map}" - :group 'magit) - -(defun magit-wazzup (&optional all) - (interactive "P") - (let ((topdir (magit-get-top-dir default-directory)) - (current-branch (magit-get-current-branch))) - (magit-buffer-switch "*magit-wazzup*") - (magit-mode-init topdir 'magit-wazzup-mode - #'magit-refresh-wazzup-buffer - current-branch all))) - -(defun magit-filename (filename) - "Return the path of FILENAME relative to its git repository. - -If FILENAME is absolute, return a path relative to the git -repository containing it. Otherwise, return a path relative to -the current git repository." - (let ((topdir (expand-file-name - (magit-get-top-dir (or (file-name-directory filename) - default-directory)))) - (file (file-truename filename))) - (when (and (not (string= topdir "")) - ;; FILE must start with the git repository path - (zerop (string-match-p (concat "\\`" topdir) file))) - (substring file (length topdir))))) - -;; This variable is used to keep track of the current file in the -;; *magit-log* buffer when this one is dedicated to showing the log of -;; just 1 file. -(defvar magit-file-log-file nil) -(make-variable-buffer-local 'magit-file-log-file) - -(defun magit-refresh-file-log-buffer (file range style) - "Refresh the current file-log buffer by calling git. - -FILE is the path of the file whose log must be displayed. - -`magit-current-range' will be set to the value of RANGE. - -STYLE controls the display. It is either `'long', `'oneline', or something else. - " - (magit-configure-have-graph) - (magit-configure-have-decorate) - (magit-configure-have-abbrev) - (setq magit-current-range range) - (setq magit-file-log-file file) - (magit-create-log-buffer-sections - (apply #'magit-git-section nil - (magit-rev-range-describe range (format "Commits for file %s" file)) - (apply-partially 'magit-wash-log style) - `("log" - ,(format "--max-count=%s" magit-log-cutoff-length) - ,"--abbrev-commit" - ,(format "--abbrev=%s" magit-sha1-abbrev-length) - ,@(cond ((eq style 'long) (list "--stat" "-z")) - ((eq style 'oneline) (list "--pretty=oneline")) - (t nil)) - ,@(if magit-have-decorate (list "--decorate=full")) - ,@(if magit-have-graph (list "--graph")) - "--" - ,file)))) - -(defun magit-file-log (&optional all) - "Display the log for the currently visited file or another one. - -With a prefix argument or if no file is currently visited, ask -for the file whose log must be displayed." - (interactive "P") - (let ((topdir (magit-get-top-dir default-directory)) - (current-file (magit-filename - (if (or current-prefix-arg (not buffer-file-name)) - (magit-read-file-from-rev (magit-get-current-branch)) - buffer-file-name))) - (range "HEAD")) - (magit-buffer-switch "*magit-log*") - (magit-mode-init topdir 'magit-log-mode - #'magit-refresh-file-log-buffer - current-file range 'oneline))) - -(defun magit-show-file-revision () - "Open a new buffer showing the current file in the revision at point." - (interactive) - (flet ((magit-show-file-from-diff (item) - (switch-to-buffer-other-window - (magit-show (cdr (magit-diff-item-range item)) - (magit-diff-item-file item))))) - (magit-section-action (item info "show") - ((commit) - (let ((current-file (or magit-file-log-file - (magit-read-file-from-rev info)))) - (switch-to-buffer-other-window - (magit-show info current-file)))) - ((hunk) (magit-show-file-from-diff (magit-hunk-item-diff item))) - ((diff) (magit-show-file-from-diff item))))) - -;;; Miscellaneous - -(defun magit-ignore-modifiable-file (file edit) - "Prompt the user for the filename to be added to git ignore. -\\ -The minibuffer's future history (accessible with \\[next-history-element]) -contains predefined values (such as wildcards) that might -be of interest. -The history and default value are derived from the filename FILE. -If EDIT argument is negative, the prompt proposes wildcard by default. -" - (let* ((just-extension (concat "*." (file-name-extension file))) - (full-extension (concat (file-name-directory file) just-extension)) - (just-file (file-name-nondirectory file)) - ;; change the order in history depending on the negativity of - ;; EDIT. - (history (if (< (prefix-numeric-value edit) 0) - (list full-extension just-extension file just-file) - (list file full-extension just-extension just-file)))) - (read-string - (format "File to ignore [%s]: " (car history)) - nil nil history))) - -(defun magit-ignore-file (file edit local) - "Add FILE to the list of files to ignore. -\\ -If EDIT is non-`nil', prompt the user for the filename to -be added to git ignore. In this case, the minibuffer's -future history (accessible with \\[next-history-element]) contains predefined -values (such as wildcards) that might be of interest. - -If LOCAL is nil, the `.gitignore' file is updated. -Otherwise, it is `.git/info/exclude'." - (let* ((local-ignore-dir (concat (magit-git-dir) "info/")) - (ignore-file (if local - (concat local-ignore-dir "exclude") - ".gitignore"))) - (if edit - (setq file (magit-ignore-modifiable-file file edit))) - (if (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)) - (magit-need-refresh))) - -(defun magit-ignore-item () - "Add FILE to the `.gitignore' list of files to ignore. -\\ -With a prefix argument, prompt the user for the filename to -be added. In this case, the minibuffer's future history -\(accessible with \\[next-history-element]) contains predefined values (such as -wildcards) that might be of interest. If prefix argument is -negative, the prompt proposes wildcard by default." - (interactive) - (magit-section-action (item info "ignore") - ((untracked file) - (magit-ignore-file (concat "/" info) current-prefix-arg nil)) - ((wazzup) - (magit-wazzup-toggle-ignore info current-prefix-arg)))) - -(defun magit-ignore-item-locally () - "Add FILE to the `.git/info/exclude' list of files to ignore. -\\ -With a prefix argument, prompt the user for the filename to -be added. In this case, the minibuffer's future history -(accessible with \\[next-history-element]) contains predefined values (such as -wildcards) that might be of interest. If prefix argument is -negative, the prompt proposes wildcard by default." - (interactive) - (magit-section-action (item info "ignore") - ((untracked file) - (magit-ignore-file (concat "/" info) current-prefix-arg t)))) - -(defun magit-discard-diff (diff stagedp) - (let ((kind (magit-diff-item-kind diff)) - (file (magit-diff-item-file diff))) - (cond ((eq kind 'deleted) - (when (yes-or-no-p (format "Resurrect %s? " file)) - (magit-run-git "reset" "-q" "--" file) - (magit-run-git "checkout" "--" file))) - ((eq kind 'new) - (if (yes-or-no-p (format "Delete %s? " file)) - (magit-run-git "rm" "-f" "--" file))) - (t - (if (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-discard-item () - (interactive) - (magit-section-action (item info "discard") - ((untracked file) - (when (yes-or-no-p (format "Delete %s? " info)) - (if (and (file-directory-p info) - (not (file-symlink-p info))) - (magit-delete-directory info 'recursive) - (delete-file info)) - (magit-refresh-buffer))) - ((untracked) - (if (yes-or-no-p "Delete all untracked files and directories? ") - (magit-run-git "clean" "-df"))) - ((unstaged diff hunk) - (when (yes-or-no-p (if (magit-use-region-p) - "Discard changes in region? " - "Discard hunk? ")) - (magit-apply-hunk-item-reverse item))) - ((staged diff hunk) - (if (magit-file-uptodate-p (magit-diff-item-file - (magit-hunk-item-diff item))) - (when (yes-or-no-p (if (magit-use-region-p) - "Discard changes in region? " - "Discard hunk? ")) - (magit-apply-hunk-item-reverse item "--index")) - (error "Can't discard this hunk. Please unstage it first"))) - ((unstaged diff) - (magit-discard-diff item nil)) - ((staged diff) - (if (magit-file-uptodate-p (magit-diff-item-file item)) - (magit-discard-diff item t) - (error "Can't discard staged changes to this file. Please unstage it first"))) - ((diff diff) - (save-excursion - (magit-goto-parent-section) - (magit-discard-item))) - ((diff diff hunk) - (save-excursion - (magit-goto-parent-section) - (magit-goto-parent-section) - (magit-discard-item))) - ((hunk) - (error "Can't discard this hunk")) - ((diff) - (error "Can't discard this diff")) - ((stash) - (when (yes-or-no-p "Discard stash? ") - (magit-run-git "stash" "drop" info))) - ((branch) - (when (yes-or-no-p (if current-prefix-arg - "Force delete branch?" - "Delete branch? ")) - (magit-delete-branch info current-prefix-arg))) - ((remote) - (when (yes-or-no-p "Remove remote? ") - (magit-remove-remote info))))) - -(defun magit-move-item () - (interactive) - (magit-section-action (item info "move") - ((branch) - (call-interactively 'magit-move-branch)) - ((remote) - (call-interactively 'magit-rename-remote)))) - -(defmacro magit-visiting-file-item (&rest body) - `(let ((marker (save-window-excursion - (magit-visit-file-item) - (set-marker (make-marker) (point))))) - (save-excursion - (with-current-buffer (marker-buffer marker) - (goto-char marker) - ,@body)))) - -(defun magit-add-change-log-entry-no-option (&optional other-window) - "Add a change log entry for current change. -With a prefix argument, edit in other window. -The name of the change log file is set by variable change-log-default-name." - (interactive "P") - (if other-window - (magit-visiting-file-item (add-change-log-entry-other-window)) - (magit-visiting-file-item (add-change-log-entry)))) - -(defun magit-add-change-log-entry-other-window () - (interactive) - (magit-visiting-file-item (call-interactively 'add-change-log-entry-other-window))) - -(eval-after-load 'dired-x - '(defun magit-dired-jump (&optional other-window) - "Visit current item. -With a prefix argument, visit in other window." - (interactive "P") - (require 'dired-x) - (magit-section-action (item info "dired-jump") - ((untracked file) - (dired-jump other-window (file-truename info))) - ((diff) - (dired-jump other-window (file-truename (magit-diff-item-file item)))) - ((hunk) - (dired-jump other-window - (file-truename (magit-diff-item-file - (magit-hunk-item-diff item)))))))) - -(defun magit-visit-file-item (&optional other-window) - "Visit current file associated with item. -With a prefix argument, visit in other window." - (interactive "P") - (magit-section-action (item info "visit-file") - ((untracked file) - (funcall - (if other-window 'find-file-other-window 'find-file) - info)) - ((diff) - (let ((file (magit-diff-item-file item))) - (cond ((not (file-exists-p file)) - (error "Can't visit deleted file: %s" file)) - ((file-directory-p file) - (magit-status file)) - (t - (funcall - (if other-window 'find-file-other-window 'find-file) - file))))) - ((hunk) - (let ((file (magit-diff-item-file (magit-hunk-item-diff item))) - (line (magit-hunk-item-target-line item))) - (if (not (file-exists-p file)) - (error "Can't visit deleted file: %s" file)) - (funcall - (if other-window 'find-file-other-window 'find-file) - file) - (goto-char (point-min)) - (forward-line (1- line)))))) - -(defun magit-visit-item (&optional other-window) - "Visit current item. -With a prefix argument, visit in other window." - (interactive "P") - (magit-section-action (item info "visit") - ((untracked file) - (call-interactively 'magit-visit-file-item)) - ((diff) - (call-interactively 'magit-visit-file-item)) - ((hunk) - (call-interactively 'magit-visit-file-item)) - ((commit) - (magit-show-commit info nil nil 'select)) - ((stash) - (magit-show-stash info) - (pop-to-buffer magit-stash-buffer-name)) - ((branch) - (magit-checkout info)) - ((longer) - (magit-log-show-more-entries ())))) - -(defun magit-show-item-or-scroll-up () - (interactive) - (magit-section-action (item info) - ((commit) - (magit-show-commit info #'scroll-up)) - ((stash) - (magit-show-stash info #'scroll-up)) - (t - (scroll-up)))) - -(defun magit-show-item-or-scroll-down () - (interactive) - (magit-section-action (item info) - ((commit) - (magit-show-commit info #'scroll-down)) - ((stash) - (magit-show-stash info #'scroll-down)) - (t - (scroll-down)))) - -(defun magit-mark-item (&optional unmark) - (interactive "P") - (if unmark - (magit-set-marked-commit nil) - (magit-section-action (item info "mark") - ((commit) - (magit-set-marked-commit (if (eq magit-marked-commit info) - nil - info)))))) - -(defun magit-describe-item () - (interactive) - (let ((section (magit-current-section))) - (message "Section: %s %s-%s %S %S %S" - (magit-section-type section) - (magit-section-beginning section) - (magit-section-end section) - (magit-section-title section) - (magit-section-info section) - (magit-section-context-type section)))) - -(defun magit-copy-item-as-kill () - "Copy sha1 of commit at point into kill ring." - (interactive) - (magit-section-action (item info "copy") - ((commit) - (kill-new info) - (message "%s" info)))) - -(eval-when-compile (require 'server)) - -(defun magit-server-running-p () - "Test whether server is running (works with < 23 as well). - -Return values: - nil the server is definitely not running. - t the server seems to be running. - something else we cannot determine whether it's running without using - commands which may have to wait for a long time." - (require 'server) - (if (functionp 'server-running-p) - (server-running-p) - (condition-case nil - (if server-use-tcp - (with-temp-buffer - (insert-file-contents-literally (expand-file-name server-name server-auth-dir)) - (or (and (looking-at "127\\.0\\.0\\.1:[0-9]+ \\([0-9]+\\)") - (assq 'comm - (process-attributes - (string-to-number (match-string 1)))) - t) - :other)) - (delete-process - (make-network-process - :name "server-client-test" :family 'local :server nil :noquery t - :service (expand-file-name server-name server-socket-dir))) - t) - (file-error nil)))) - -(defun magit-interactive-rebase () - "Start a git rebase -i session, old school-style." - (interactive) - (unless (magit-server-running-p) - (server-start)) - (let* ((section (get-text-property (point) 'magit-section)) - (commit (and (member 'commit (magit-section-context-type section)) - (magit-section-info section))) - (old-editor (getenv "GIT_EDITOR"))) - (setenv "GIT_EDITOR" (concat (locate-file "emacsclient" exec-path) - " -s " server-name)) - (unwind-protect - (magit-run-git-async "rebase" "-i" - (or (and commit (concat commit "^")) - (magit-read-rev "Interactively rebase to" (magit-guess-branch)))) - (if old-editor - (setenv "GIT_EDITOR" old-editor))))) - -(define-derived-mode magit-branch-manager-mode magit-mode "Magit Branch" - "Magit Branches") - -(defun magit-quit-window (&optional kill-buffer) - "Bury the buffer and delete its window. With a prefix argument, kill the -buffer instead." - (interactive "P") - (quit-window kill-buffer (selected-window))) - -(defun magit--branch-name-at-point () - "Get the branch name in the line at point." - (let ((branch (magit-section-info (magit-current-section)))) - (or branch (error "No branch at point")))) - -(defun magit--branches-for-remote-repo (remote) - "Return a list of remote branch names for REMOTE. -These are the branch names with the remote name stripped." - (remq nil - (mapcar (lambda (line) - (save-match-data - (if (and (not (string-match-p " -> " line)) - (string-match (concat "^ +" remote "/\\([^ $]+\\)") - line)) - (match-string 1 line)))) - (magit-git-lines "branch" "-r")))) - -(defvar magit-branches-buffer-name "*magit-branches*") - -(defun magit--is-branch-at-point-remote () - "Return non-nil if the branch at point is a remote tracking branch" - (magit-remote-part-of-branch (magit--branch-name-at-point))) - -(defun magit-remote-part-of-branch (branch) - (when (string-match-p "^\\(?:refs/\\)?remotes\\/" branch) - (loop for remote in (magit-git-lines "remote") - when (string-match-p (format "^\\(?:refs/\\)?remotes\\/%s\\/" (regexp-quote remote)) branch) return remote))) - -(defun magit-branch-no-remote (branch) - (let ((remote (magit-remote-part-of-branch branch))) - (if remote - (progn - ;; This has to match if remote is non-nil - (assert (string-match (format "^\\(?:refs/\\)?remotes\\/%s\\/\\(.*\\)" (regexp-quote remote)) branch) - 'show-args "Unexpected string-match failure: %s %s") - (match-string 1 branch)) - branch))) - -(defun magit-wash-branch-line (&optional remote-name) - (looking-at (concat - "^\\([ *] \\)" ; 1: current branch marker - "\\(.+?\\) +" ; 2: branch name - - "\\(?:" - - "\\([0-9a-fA-F]+\\)" ; 3: sha1 - " " - "\\(?:\\[" - "\\([^:\n]+?\\)" ; 4: tracking (non-greedy + to avoid matching \n) - "\\(?:: \\)?" - "\\(?:ahead \\([0-9]+\\)\\)?" ; 5: ahead - "\\(?:, \\)?" - "\\(?:behind \\([0-9]+\\)\\)?" ; 6: behind - "\\] \\)?" - "\\(?:.*\\)" ; message - - "\\|" ; or - - "-> " ; the pointer to - "\\(.+\\)" ; 7: a ref - - "\\)\n")) - - (let* ((current-string (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)) - (current (string-match-p "^\\*" current-string))) - - ; the current line is deleted before being reconstructed - (delete-region (point) - (line-beginning-position 2)) - - (magit-with-section branch 'branch - (magit-set-section-info branch) - (insert-before-markers - ; sha1 - (propertize (or sha1 - (make-string magit-sha1-abbrev-length ? )) - 'face 'magit-log-sha1) - " " - ; current marker - (if current - "# " - " ") - ; branch name - (apply 'propertize (magit-branch-no-remote branch) - (if current - '(face magit-branch))) - ; other ref that this branch is pointing to - (if other-ref - (concat " -> " (substring other-ref (+ 1 (length remote-name)))) - "") - ; tracking information - (if (and tracking - (equal (magit-remote-branch-for branch t) - (concat "refs/remotes/" tracking))) - (concat " [" - ; getting rid of the tracking branch name if it is the same as the branch name - (let* ((tracking-remote (magit-get "branch" branch "remote")) - (tracking-branch (substring tracking (+ 1 (length tracking-remote))))) - (propertize (if (string= branch tracking-branch) - (concat "@ " tracking-remote) - (concat tracking-branch " @ " tracking-remote)) - 'face 'magit-log-head-label-remote)) - ; ahead/behind information - (if (or ahead - behind) - ": " - "") - (if ahead - (concat "ahead " - (propertize ahead - 'face (if current - 'magit-branch)) - (if behind - ", " - "")) - "") - (if behind - (concat "behind " - (propertize behind - 'face 'magit-log-head-label-remote)) - "") - "]") - "") - "\n")))) - -(defun magit-wash-remote-branches-group (group) - (let* ((remote-name (first group)) - (url (magit-get "remote" remote-name "url")) - (push-url (magit-get "remote" remote-name "pushurl")) - (urls (concat url (if push-url - (concat ", "push-url) - ""))) - (marker (second group))) - - (magit-with-section (concat "remote:" remote-name) 'remote - (magit-set-section-info remote-name) - (insert-before-markers (propertize (format "%s (%s):" remote-name urls) 'face 'magit-section-title) "\n") - (magit-wash-branches-between-point-and-marker marker remote-name)) - (insert-before-markers "\n"))) - -(defun magit-wash-branches-between-point-and-marker (marker &optional remote-name) - (save-restriction - (narrow-to-region (point) marker) - (magit-wash-sequence - (if remote-name - (apply-partially 'magit-wash-branch-line remote-name) - #'magit-wash-branch-line)))) - -(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 (search-forward-regexp - (concat "^ remotes\\/" 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 (loop for remote in remotes - for end-markers on (cdr markers) - for marker = (loop for x in end-markers thereis x) - collect (list remote marker)))) - - ; actual displaying of information - (magit-with-section "local" nil - (insert-before-markers (propertize "Local:" 'face 'magit-section-title) "\n") - (magit-set-section-info ".") - (magit-wash-branches-between-point-and-marker - (loop for x in markers thereis x))) - - (insert-before-markers "\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))) - -(defun magit-refresh-branch-manager () - (magit-create-buffer-sections - (magit-git-section "branches" nil 'magit-wash-branches - "branch" - "-vva" - (format "--abbrev=%s" magit-sha1-abbrev-length)))) - -(magit-define-command branch-manager () - (interactive) - (let ((topdir (magit-get-top-dir default-directory))) - (magit-buffer-switch magit-branches-buffer-name) - (magit-mode-init topdir 'magit-branch-manager-mode #'magit-refresh-branch-manager))) - -(defun magit-change-what-branch-tracks () - "Change which remote branch the current branch tracks." - (interactive) - (if (magit--is-branch-at-point-remote) - (error "Cannot modify a remote branch")) - (let* ((local-branch (magit--branch-name-at-point)) - (new-tracked (magit-read-rev "Change tracked branch to" - nil - (lambda (ref) - (not (string-match-p "refs/remotes/" - ref))))) - new-remote new-branch) - (unless (string= (or new-tracked "") "") - (cond (;; Match refs that are unknown in the local repository if - ;; `magit-remote-ref-format' is set to - ;; `name-then-remote'. Can be useful if you want to - ;; create a new branch in a remote repository. - (string-match "^\\([^ ]+\\) +(\\(.+\\))$" ; 1: branch name; 2: remote name - new-tracked) - (setq new-remote (match-string 2 new-tracked) - new-branch (concat "refs/heads/" (match-string 1 new-tracked)))) - ((string-match "^\\(?:refs/remotes/\\)?\\([^/]+\\)/\\(.+\\)" ; 1: remote name; 2: branch name - new-tracked) - (setq new-remote (match-string 1 new-tracked) - new-branch (concat "refs/heads/" (match-string 2 new-tracked)))) - (t (error "Cannot parse the remote and branch name")))) - (magit-set new-remote "branch" local-branch "remote") - (magit-set new-branch "branch" local-branch "merge") - (magit-branch-manager) - (if (string= (magit-get-current-branch) local-branch) - (magit-refresh-buffer (magit-find-status-buffer default-directory))))) - -(defvar magit-ediff-file) - -(defun magit-interactive-resolve (file) - (require 'ediff) - (let ((merge-status (magit-git-string "ls-files" "-u" "--" file)) - (base-buffer (generate-new-buffer (concat file ".base"))) - (our-buffer (generate-new-buffer (concat file ".current"))) - (their-buffer (generate-new-buffer (concat file ".merged"))) - (windows (current-window-configuration))) - (if (null merge-status) - (error "Cannot resolve %s" file)) - (with-current-buffer base-buffer - (if (string-match "^[0-9]+ [0-9a-f]+ 1" merge-status) - (insert (magit-git-output `("cat-file" "blob" ,(concat ":1:" file)))))) - (with-current-buffer our-buffer - (if (string-match "^[0-9]+ [0-9a-f]+ 2" merge-status) - (insert (magit-git-output `("cat-file" "blob" ,(concat ":2:" file))))) - (let ((buffer-file-name file)) - (normal-mode))) - (with-current-buffer their-buffer - (if (string-match "^[0-9]+ [0-9a-f]+ 3" merge-status) - (insert (magit-git-output `("cat-file" "blob" ,(concat ":3:" file))))) - (let ((buffer-file-name file)) - (normal-mode))) - ;; We have now created the 3 buffer with ours, theirs and the ancestor files - (with-current-buffer (ediff-merge-buffers-with-ancestor our-buffer their-buffer base-buffer) - (make-local-variable 'magit-ediff-file) - (setq magit-ediff-file file) - (make-local-variable 'magit-ediff-windows) - (setq 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) - (file magit-ediff-file) - (file-buffer) - (windows magit-ediff-windows)) - (ediff-cleanup-mess) - (find-file file) - (setq file-buffer (current-buffer)) - (erase-buffer) - (insert-buffer-substring buffer-C) - (kill-buffer buffer-A) - (kill-buffer buffer-B) - (kill-buffer buffer-C) - (when (bufferp buffer-Ancestor) (kill-buffer buffer-Ancestor)) - (set-window-configuration windows) - (magit-save-some-buffers - "Conflict resolution finished; you may save the buffer" - (lambda () (eq (current-buffer) file-buffer))))))))) - -(defun magit-interactive-resolve-item () - (interactive) - (magit-section-action (item info "resolv") - ((diff) - (magit-interactive-resolve (cadr info))))) - -(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 default-directory))) - (apply #'magit-run-git-async "submodule" "update" (if init '("--init") ())))) - -(defun magit-submodule-update-init () - "Update and init the submodule of the current git repository." - (interactive) - (magit-submodule-update t)) - -(defun magit-submodule-init () - "Initialize the submodules" - (interactive) - (let ((default-directory (magit-get-top-dir default-directory))) - (magit-run-git-async "submodule" "init"))) - -(defun magit-submodule-sync () - "Synchronizes submodules' remote URL configuration" - (interactive) - (let ((default-directory (magit-get-top-dir default-directory))) - (magit-run-git-async "submodule" "sync"))) - -(defun magit-run-git-gui () - "Run `git gui' for the current git repository" - (interactive) - (let* ((default-directory (magit-get-top-dir default-directory))) - (magit-start-process "Git Gui" nil magit-git-executable "gui"))) - -(defun magit-run-gitk () - "Run `gitk --all' for the current git repository" - (interactive) - (let ((default-directory (magit-get-top-dir default-directory))) - (cond - ((eq system-type 'windows-nt) - ;; Gitk is a shell script, and Windows doesn't know how to - ;; "execute" it. The Windows version of Git comes with an - ;; implementation of "sh" and everything else it needs, but - ;; Windows users might not have added the directory where it's - ;; installed to their path - (let ((git-bin-dir (file-name-directory magit-gitk-executable)) - (exec-path exec-path) - (process-environment process-environment)) - (when git-bin-dir - ;; Adding it onto the end so that anything the user - ;; specified will get tried first. Emacs looks in - ;; exec-path; PATH is the environment variable inherited by - ;; the process. I need to change both. - (setq exec-path (append exec-path (list git-bin-dir))) - (push (format "PATH=%s;%s" - (getenv "PATH") - (replace-regexp-in-string "/" "\\\\" git-bin-dir)) - process-environment)) - (magit-start-process "Gitk" nil "sh" magit-gitk-executable "--all"))) - (t - (magit-start-process "Gitk" nil magit-gitk-executable "--all"))))) - -(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))))) - -(provide 'magit) - -;; rest of magit core -(require 'magit-key-mode) -(require 'magit-bisect) - -;;; magit.el ends here diff --git a/elpa/magit-1.2.0/magit.el-e b/elpa/magit-1.2.0/magit.el-e deleted file mode 100644 index 5923afc1..00000000 --- a/elpa/magit-1.2.0/magit.el-e +++ /dev/null @@ -1,6001 +0,0 @@ -;;; magit.el --- control Git from Emacs - -;; Copyright (C) 2010 Aaron Culich. -;; Copyright (C) 2010 Alan Falloon. -;; Copyright (C) 2008, 2010 Alex Ott. -;; Copyright (C) 2008, 2009, 2010 Alexey Voinov. -;; Copyright (C) 2010 Ben Walton. -;; Copyright (C) 2010 Chris Bernard. -;; Copyright (C) 2010 Christian Kluge. -;; Copyright (C) 2008 Daniel Farina. -;; Copyright (C) 2010 David Abrahams. -;; Copyright (C) 2009 David Wallin. -;; Copyright (C) 2009, 2010 Hannu Koivisto. -;; Copyright (C) 2009 Ian Eure. -;; Copyright (C) 2009 Jesse Alama. -;; Copyright (C) 2009 John Wiegley. -;; Copyright (C) 2010 Leo. -;; Copyright (C) 2008, 2009 Marcin Bachry. -;; Copyright (C) 2008, 2009 Marius Vollmer. -;; Copyright (C) 2010 Mark Hepburn. -;; Copyright (C) 2010 Moritz Bunkus. -;; Copyright (C) 2010 Nathan Weizenbaum. -;; Copyright (C) 2010 Oscar Fuentes. -;; Copyright (C) 2009 Pavel Holejsovsky. -;; Copyright (C) 2011-2012 Peter J Weisberg -;; Copyright (C) 2009, 2010 Phil Jackson. -;; Copyright (C) 2010 Philip Weaver. -;; Copyright (C) 2010 Ramkumar Ramachandra. -;; Copyright (C) 2010 Remco van 't Veer. -;; Copyright (C) 2009 René Stadler. -;; Copyright (C) 2010 Robin Green. -;; Copyright (C) 2010 Roger Crew. -;; Copyright (C) 2009, 2010, 2011 Rémi Vanicat. -;; Copyright (C) 2010 Sean Bryant. -;; Copyright (C) 2009, 2011 Steve Purcell. -;; Copyright (C) 2010 Timo Juhani Lindfors. -;; Copyright (C) 2010, 2011 Yann Hodique. -;; Copyright (C) 2010 Ævar Arnfjörð Bjarmason. -;; Copyright (C) 2010 Óscar Fuentes. - -;; Original Author: Marius Vollmer -;; Former Maintainer: Phil Jackson -;; Maintenance Group: https://github.com/organizations/magit/teams/53130 -;; Currently composed of: -;; - Phil Jackson -;; - Peter J Weisberg -;; - Yann Hodique -;; - Rémi Vanicat -;; Version: @GIT_DEV_VERSION@ -;; Keywords: tools - -;; -;; 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: - -(eval-when-compile (require 'cl)) -(require 'log-edit) -(require 'easymenu) -(require 'diff-mode) - -;; Silences byte-compiler warnings -(eval-and-compile - (unless (fboundp 'declare-function) (defmacro declare-function (&rest args)))) - -(eval-when-compile (require 'view)) -(declare-function view-mode 'view) -(eval-when-compile (require 'iswitchb)) -(eval-when-compile (require 'ido)) -(eval-when-compile (require 'ediff)) - -;; Dummy to be used by the defcustoms when first loading the file. -(eval-when (load eval) - (defalias 'magit-set-variable-and-refresh 'set-default)) - -;;; Code: -(defgroup magit nil - "Controlling Git from Emacs." - :prefix "magit-" - :group 'tools) - -(defcustom magit-git-executable "git" - "The name of the Git executable." - :group 'magit - :type 'string) - -(defcustom magit-gitk-executable (concat (file-name-directory magit-git-executable) - "gitk") - "The name of the Gitk executable." - :group 'magit - :type 'string) - -(defcustom magit-git-standard-options '("--no-pager") - "Standard options when running Git." - :group 'magit - :type '(repeat string)) - -(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 string)) - -(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) - -(defcustom magit-set-upstream-on-push nil - "Non-nil means that `magit-push' may use --set-upstream when pushing a branch. -This only applies if the branch does not have an upstream set yet. -Setting this to t will ask if --set-upstream should be used. -Setting it to 'dontask will always use --set-upstream. -Setting it to 'refuse will refuse to push unless a remote branch has already been set. - ---set-upstream is supported with git > 1.7.0" - :group 'magit - :type '(choice (const :tag "Never" nil) - (const :tag "Ask" t) - (const :tag "Refuse" refuse) - (const :tag "Always" dontask))) - -(defcustom magit-save-some-buffers t - "Non-nil means that \\[magit-status] will save modified buffers before running. -Setting this to t will ask which buffers to save, setting it to 'dontask will -save all modified 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-default-tracking-name-function - 'magit-default-tracking-name-remote-plus-branch - "Specifies the function to use to generate default tracking branch names -when doing a \\[magit-checkout]. - -The default is magit-default-tracking-name-remote-plus-branch, -which generates a tracking name of the form 'REMOTE-BRANCHNAME'." - :group 'magit - :type '(radio (function-item magit-default-tracking-name-remote-plus-branch) - (function-item magit-default-tracking-name-branch-only) - (function :tag "Other"))) - -(defcustom magit-commit-all-when-nothing-staged 'ask - "Determines what \\[magit-log-edit] does when nothing is staged. -Setting this to nil will make it do nothing, setting it to t will -arrange things so that the actual commit command will use the \"--all\" option, -setting it to 'ask will first ask for confirmation whether to do this, -and setting it to 'ask-stage will cause all changes to be staged, -after a confirmation." - :group 'magit - :type '(choice (const :tag "No" nil) - (const :tag "Always" t) - (const :tag "Ask" ask) - (const :tag "Ask to stage everything" ask-stage))) - -(defcustom magit-commit-signoff nil - "Add the \"Signed-off-by:\" line when committing." - :group 'magit - :type 'boolean) - -(defcustom magit-sha1-abbrev-length 7 - "The number of digits to show when a sha1 is displayed in abbreviated form." - :group 'magit - :type 'integer) - -(defcustom magit-log-cutoff-length 100 - "The maximum number of commits to show in the log and whazzup buffers." - :group 'magit - :type 'integer) - -(defcustom magit-log-infinite-length 99999 - "Number of log used to show as maximum for `magit-log-cutoff-length'." - :group 'magit - :type 'integer) - -(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 - :type 'boolean) - -(defcustom magit-process-popup-time -1 - "Popup the process buffer if a command takes longer than this many seconds." - :group 'magit - :type '(choice (const :tag "Never" -1) - (const :tag "Immediately" 0) - (integer :tag "After this many seconds"))) - -(defcustom magit-revert-item-confirm t - "Require acknowledgment before reverting an item." - :group 'magit - :type 'boolean) - -(defcustom magit-log-edit-confirm-cancellation nil - "Require acknowledgment before canceling the log edit buffer." - :group 'magit - :type 'boolean) - -(defcustom magit-remote-ref-format 'branch-then-remote - "What format to use for autocompleting refs, in pariticular for remotes. - -Autocompletion is used by functions like `magit-checkout', -`magit-interactive-rebase' and others which offer branch name -completion. - -The value 'name-then-remote means remotes will be of the -form \"name (remote)\", while the value 'remote-slash-name -means that they'll be of the form \"remote/name\". I.e. something that's -listed as \"remotes/upstream/next\" by \"git branch -l -a\" -will be \"upstream/next\"." - :group 'magit - :type '(choice (const :tag "name (remote)" branch-then-remote) - (const :tag "remote/name" remote-slash-branch))) - -(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 - :type '(choice (const :tag "pipe" nil) - (const :tag "pty" t))) - -(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-iswitchb-completing-read) - (function-item magit-ido-completing-read) - (function-item magit-builtin-completing-read) - (function :tag "Other"))) - -(defcustom magit-create-branch-behaviour 'at-head - "Where magit will create a new branch if not supplied a branchname or ref. - -The value 'at-head means a new branch will be created at the tip -of your current branch, while the value 'at-point means magit -will try to find a valid reference at point..." - :group 'magit - :type '(choice (const :tag "at HEAD" at-head) - (const :tag "at point" at-point))) - -(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 - :type '(radio (function-item switch-to-buffer) - (function-item pop-to-buffer) - (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))) - -(defcustom magit-highlight-whitespace t - "Specifies 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 - :type '(choice (const :tag "Always" t) - (const :tag "Never" nil) - (const :tag "In status buffer" status)) - :set 'magit-set-variable-and-refresh) - -(defcustom magit-highlight-trailing-whitespace t - "Highlight whitespace at the end of a line in diffs. -Used only when `magit-highlight-whitespace' is non-nil." - :group 'magit - :type 'boolean - :set 'magit-set-variable-and-refresh) - -(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 - :type `(repeat (cons (string :tag "Directory regexp") - (choice (const :tag "Tabs" tabs) - (integer :tag "Spaces" :value ,tab-width) - (const :tag "Neither" nil)))) - :set 'magit-set-variable-and-refresh) - -(defcustom magit-diff-refine-hunk nil - "Show fine (word-granularity) differences within diff hunks. - -There are three possible settings: - - nil means to never show fine differences - - t means to only show fine differences for the currently - selected diff hunk - - `all' means to always show fine differences for all displayed diff hunks" - :group 'magit - :type '(choice (const :tag "Never" nil) - (const :tag "Selected only" t) - (const :tag "All" all)) - :set 'magit-set-variable-and-refresh) - -(defvar magit-current-indentation nil - "Indentation highlight used in the current buffer. -This is calculated from `magit-highlight-indentation'.") -(make-variable-buffer-local 'magit-current-indentation) - -(defgroup magit-faces nil - "Customize the appearance of Magit." - :prefix "magit-" - :group 'faces - :group 'magit) - -(defface magit-header - '((t :inherit header-line)) - "Face for generic header lines. - -Many Magit faces inherit from this one by default." - :group 'magit-faces) - -(defface magit-section-title - '((t :inherit magit-header)) - "Face for section titles." - :group 'magit-faces) - -(defface magit-branch - '((t :inherit magit-header)) - "Face for the current branch." - :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-none - '((t :inherit diff-context)) - "Face for lines in a diff that are unchanged." - :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-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-message - '((t)) - "Face for the message element of the log output." - :group 'magit-faces) - -(defface magit-item-highlight - '((t :inherit highlight)) - "Face for highlighting the current item." - :group 'magit-faces) - -(defface magit-item-mark - '((t :inherit secondary-selection)) - "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-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) - -(defvar magit-custom-options '() - "List of custom options to pass to Git. -Do not customize this (used in the `magit-key-mode' implementation).") - -(defvar magit-read-rev-history nil - "The history of inputs to `magit-read-rev'.") - -(defvar magit-buffer-internal nil - "Track associated *magit* buffers. -Do not customize this (used in the `magit-log-edit-mode' implementation -to switch back to the *magit* buffer associated with a given commit -operation after commit).") - -(defvar magit-back-navigation-history nil - "History items that will be visited by successively going \"back\".") -(make-variable-buffer-local 'magit-back-navigation-history) -(put 'magit-back-navigation-history 'permanent-local t) - -(defvar magit-forward-navigation-history nil - "History items that will be visited by successively going \"forward\".") -(make-variable-buffer-local 'magit-forward-navigation-history) -(put 'magit-forward-navigation-history 'permanent-local t) - -(defvar magit-omit-untracked-dir-contents nil - "When non-nil magit will only list an untracked directory, not its contents.") - -(defvar magit-tmp-buffer-name " *magit-tmp*") - -(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-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) - -(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-describe-item) - (define-key map (kbd "!") 'magit-key-mode-popup-running) - (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-no-option) - (define-key map (kbd "RET") 'magit-visit-item) - (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) - (define-key map (kbd "R") 'magit-rebase-step) - (define-key map (kbd "t") 'magit-key-mode-popup-tagging) - (define-key map (kbd "r") 'magit-key-mode-popup-rewriting) - (define-key map (kbd "P") 'magit-key-mode-popup-pushing) - (define-key map (kbd "f") 'magit-key-mode-popup-fetching) - (define-key map (kbd "b") 'magit-key-mode-popup-branching) - (define-key map (kbd "M") 'magit-key-mode-popup-remoting) - (define-key map (kbd "B") 'magit-key-mode-popup-bisecting) - (define-key map (kbd "F") 'magit-key-mode-popup-pulling) - (define-key map (kbd "l") 'magit-key-mode-popup-logging) - (define-key map (kbd "$") 'magit-display-process) - (define-key map (kbd "c") 'magit-log-edit) - (define-key map (kbd "E") 'magit-interactive-rebase) - (define-key map (kbd "e") 'magit-ediff) - (define-key map (kbd "w") 'magit-wazzup) - (define-key map (kbd "q") 'magit-quit-window) - (define-key map (kbd "m") 'magit-key-mode-popup-merging) - (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-toggle-diff-refine-hunk) - map)) - -(defvar magit-commit-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-b") 'magit-show-commit-backward) - (define-key map (kbd "C-c C-f") 'magit-show-commit-forward) - map)) - -(defvar magit-status-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "s") 'magit-stage-item) - (define-key map (kbd "S") 'magit-stage-all) - (define-key map (kbd "u") 'magit-unstage-item) - (define-key map (kbd "U") 'magit-unstage-all) - (define-key map (kbd "i") 'magit-ignore-item) - (define-key map (kbd "I") 'magit-ignore-item-locally) - (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-add-log) - (define-key map (kbd "X") 'magit-reset-working-tree) - (define-key map (kbd "z") 'magit-key-mode-popup-stashing) - map)) - -(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))) - (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) - map)) - -(defvar magit-wazzup-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd ".") 'magit-mark-item) - (define-key map (kbd "=") 'magit-diff-with-mark) - (define-key map (kbd "i") 'magit-ignore-item) - map)) - -(defvar magit-branch-manager-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "c") 'magit-create-branch) - (define-key map (kbd "a") 'magit-add-remote) - (define-key map (kbd "r") 'magit-move-item) - (define-key map (kbd "k") 'magit-discard-item) - (define-key map (kbd "T") 'magit-change-what-branch-tracks) - map)) - -(defvar magit-bug-report-url - "http://github.com/magit/magit/issues") - -(defconst magit-version "@GIT_DEV_VERSION@" - "The version of Magit that you're using.") - -(defun magit-bug-report (str) - "Asks the user to submit a bug report about the error described in STR." -;; XXX - should propose more information to be included. - (message (concat - "Unknown error: %s\n" - "Please, with as much information as possible, file a bug at\n" - "%s\n" - "You are using Magit version %s.") - str magit-bug-report-url magit-version)) - -(defun magit-buffer-switch (buf) - (if (string-match "magit" (buffer-name)) - (switch-to-buffer buf) - (pop-to-buffer buf))) - -;;; Macros - -(defmacro magit-with-refresh (&rest body) - (declare (indent 0)) - `(magit-refresh-wrapper (lambda () ,@body))) - -;;; Git features - -(defvar magit-have-graph 'unset) -(defvar magit-have-decorate 'unset) -(defvar magit-have-abbrev 'unset) -(make-variable-buffer-local 'magit-have-graph) -(put 'magit-have-graph 'permanent-local t) -(make-variable-buffer-local 'magit-have-decorate) -(put 'magit-have-decorate 'permanent-local t) -(make-variable-buffer-local 'magit-have-abbrev) -(put 'magit-have-abbrev 'permanent-local t) - -(defun magit-configure-have-graph () - (if (eq magit-have-graph 'unset) - (let ((res (magit-git-exit-code "log" "--graph" "--max-count=0"))) - (setq magit-have-graph (eq res 0))))) - -(defun magit-configure-have-decorate () - (if (eq magit-have-decorate 'unset) - (let ((res (magit-git-exit-code "log" "--decorate=full" "--max-count=0"))) - (setq magit-have-decorate (eq res 0))))) - -(defun magit-configure-have-abbrev () - (if (eq magit-have-abbrev 'unset) - (let ((res (magit-git-exit-code "log" "--no-abbrev-commit" "--max-count=0"))) - (setq magit-have-abbrev (eq res 0))))) - -;;; Compatibilities - -(eval-and-compile - (defun magit-max-args-internal (function) - "Returns the maximum number of arguments accepted by FUNCTION." - (if (symbolp function) - (setq function (symbol-function function))) - (if (subrp function) - (let ((max (cdr (subr-arity function)))) - (if (eq 'many max) - most-positive-fixnum - max)) - (if (eq 'macro (car-safe function)) - (setq function (cdr function))) - (let ((arglist (if (byte-code-function-p function) - (aref function 0) - (second function)))) - (if (memq '&rest arglist) - most-positive-fixnum - (length (remq '&optional arglist)))))) - - (if (functionp 'start-file-process) - (defalias 'magit-start-process 'start-file-process) - (defalias 'magit-start-process 'start-process)) - - (unless (fboundp 'string-match-p) - (defun string-match-p (regexp string &optional start) - "Same as `string-match' except this function does not -change the match data." - (let ((inhibit-changing-match-data t)) - (string-match regexp string start)))) - - (if (fboundp 'with-silent-modifications) - (defalias 'magit-with-silent-modifications 'with-silent-modifications) - (defmacro magit-with-silent-modifications (&rest body) - "Execute body without changing `buffer-modified-p'. Also, do not -record undo information." - `(set-buffer-modified-p - (prog1 (buffer-modified-p) - (let ((buffer-undo-list t) - before-change-functions - after-change-functions) - ,@body))))) - - (if (>= (magit-max-args-internal 'delete-directory) 2) - (defalias 'magit-delete-directory 'delete-directory) - (defun magit-delete-directory (directory &optional recursive) - "Deletes a directory named DIRECTORY. If RECURSIVE is non-nil, -recursively delete all of DIRECTORY's contents as well. - -Does not follow symlinks." - (if (or (file-symlink-p directory) - (not (file-directory-p directory))) - (delete-file directory) - (if recursive - ;; `directory-files-no-dot-files-regex' borrowed from Emacs 23 - (dolist (file (directory-files directory 'full "\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")) - (magit-delete-directory file recursive))) - (delete-directory directory))))) - -;;; Utilities - -(defun magit-set-variable-and-refresh (symbol value) - "Set SYMBOL to VALUE and call `magit-refresh-all'" - (set-default symbol value) - (magit-refresh-all)) - -(defun magit-iswitchb-completing-read (prompt choices &optional predicate require-match - initial-input hist def) - "iswitchb-based completing-read almost-replacement." - (require 'iswitchb) - (let ((iswitchb-make-buflist-hook - (lambda () - (setq iswitchb-temp-buflist (if (consp (first choices)) - (mapcar #'car choices) - choices))))) - (iswitchb-read-buffer prompt (or initial-input def) require-match))) - -(defun magit-ido-completing-read (prompt choices &optional predicate require-match initial-input hist def) - "ido-based completing-read almost-replacement." - (require 'ido) - (let ((selected (ido-completing-read prompt (if (consp (first choices)) - (mapcar #'car choices) - choices) - predicate require-match initial-input hist def))) - (if (consp (first choices)) - (or (cdr (assoc selected choices)) - selected) - selected))) - -(defun magit-builtin-completing-read (prompt choices &optional predicate require-match - initial-input hist def) - "Magit wrapper for standard `completing-read' function." - (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)) - -(defun magit-completing-read (prompt choices &optional predicate require-match - initial-input hist def) - (funcall magit-completing-read-function prompt choices predicate require-match - initial-input hist def)) - -(defun magit-use-region-p () - (if (fboundp 'use-region-p) - (use-region-p) - (and transient-mark-mode mark-active))) - -(defun magit-goto-line (line) - "Like `goto-line' but doesn't set the mark." - (save-restriction - (widen) - (goto-char 1) - (forward-line (1- line)))) - -(defun magit-trim-line (str) - (if (string= str "") - nil - (if (equal (elt str (- (length str) 1)) ?\n) - (substring str 0 (- (length str) 1)) - str))) - -(defun magit-split-lines (str) - (if (string= str "") - nil - (let ((lines (nreverse (split-string str "\n")))) - (if (string= (car lines) "") - (setq lines (cdr lines))) - (nreverse lines)))) - -(defun magit-git-insert (args) - (insert (magit-git-output args))) - -(defun magit-git-output (args) - (magit-cmd-output magit-git-executable (append magit-git-standard-options args))) - -(defun magit-cmd-insert (cmd args) - (insert (magit-cmd-output cmd args))) - -(defun magit-cmd-output (cmd args) - (let ((cmd-output (with-output-to-string - (with-current-buffer standard-output - (apply #'process-file - cmd - nil (list t nil) nil - args))))) - (replace-regexp-in-string "\e\\[.*?m" "" cmd-output))) - -(defun magit-git-string (&rest args) - (magit-trim-line (magit-git-output args))) - -(defun magit-git-lines (&rest args) - (magit-split-lines (magit-git-output args))) - -(defun magit-git-exit-code (&rest args) - (apply #'process-file magit-git-executable nil nil nil - (append magit-git-standard-options args))) - -(defun magit-file-lines (file) - (when (file-exists-p file) - (with-temp-buffer - (insert-file-contents file) - (let ((rev (nreverse (split-string (buffer-string) "\n")))) - (nreverse (if (equal (car rev) "") - (cdr rev) - rev)))))) - -(defun magit-write-file-lines (file lines) - (with-temp-buffer - (dolist (l lines) - (insert l "\n")) - (write-file file))) - -(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." - (equal (magit-git-string "config" "--bool" (mapconcat 'identity keys ".")) - "true")) - -(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 ".")))) - -(defun magit-remove-conflicts (alist) - (let ((dict (make-hash-table :test 'equal)) - (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-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)) - -(defun magit-git-repo-p (dir) - (file-exists-p (expand-file-name ".git" dir))) - -(defun magit-git-dir () - "Returns the .git directory for the current repository." - (concat (expand-file-name (magit-git-string "rev-parse" "--git-dir")) "/")) - -(defun magit-no-commit-p () - "Return non-nil if there is no commit in the current git repository." - (not (magit-git-string - "rev-list" "HEAD" "--max-count=1"))) - -(defun magit-list-repos* (dir level) - (if (magit-git-repo-p dir) - (list dir) - (apply #'append - (mapcar (lambda (entry) - (unless (or (string= (substring entry -3) "/..") - (string= (substring entry -2) "/.")) - (magit-list-repos* entry (+ level 1)))) - (and (file-directory-p dir) - (< level magit-repo-dirs-depth) - (directory-files dir t nil t)))))) - -(defun magit-list-repos (dirs) - (magit-remove-conflicts - (apply #'append - (mapcar (lambda (dir) - (mapcar #'(lambda (repo) - (cons (file-name-nondirectory repo) - repo)) - (magit-list-repos* dir 0))) - dirs)))) - -(defun magit-get-top-dir (cwd) - (let ((cwd (expand-file-name (file-truename cwd)))) - (when (file-directory-p cwd) - (let* ((default-directory (file-name-as-directory cwd)) - (cdup (magit-git-string "rev-parse" "--show-cdup"))) - (when cdup - (file-name-as-directory (expand-file-name cdup cwd))))))) - -(defun magit-get-ref (ref) - (magit-git-string "symbolic-ref" "-q" ref)) - -(defun magit-get-current-branch () - (let* ((head (magit-get-ref "HEAD")) - (pos (and head (string-match "^refs/heads/" head)))) - (if pos - (substring head 11) - nil))) - -(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")))) - (if (string= remote "") nil 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-exit-code "show-ref" "--verify" ref) 0)) - -(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-nill, 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) - (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) - default-directory))))) - -(defun magit-rev-parse (ref) - "Return the SHA hash for REF." - (magit-git-string "rev-parse" ref)) - -(defun magit-ref-ambiguous-p (ref) - "Return whether or not REF is ambiguous." - ;; If REF is ambiguous, rev-parse just prints errors, - ;; so magit-git-string returns nil. - (not (magit-git-string "rev-parse" "--abbrev-ref" ref))) - -(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-highlight-line-whitespace () - (when (and magit-highlight-whitespace - (or (derived-mode-p 'magit-status-mode) - (not (eq magit-highlight-whitespace 'status)))) - (if (and magit-highlight-trailing-whitespace - (looking-at "^[-+].*?\\([ \t]+\\)$")) - (overlay-put (make-overlay (match-beginning 1) (match-end 1)) - 'face 'magit-whitespace-warning-face)) - (if (or (and (eq magit-current-indentation 'tabs) - (looking-at "^[-+]\\( *\t[ \t]*\\)")) - (and (integerp magit-current-indentation) - (looking-at (format "^[-+]\\([ \t]* \\{%s,\\}[ \t]*\\)" - magit-current-indentation)))) - (overlay-put (make-overlay (match-beginning 1) (match-end 1)) - 'face 'magit-whitespace-warning-face)))) - -(defun magit-put-line-property (prop val) - (put-text-property (line-beginning-position) (line-beginning-position 2) - prop val)) - -(defun magit-format-commit (commit format) - (magit-git-string "log" "--max-count=1" - (concat "--pretty=format:" format) - commit)) - -(defun magit-current-line () - (buffer-substring-no-properties (line-beginning-position) - (line-end-position))) - -(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-file-uptodate-p (file) - (eq (magit-git-exit-code "diff" "--quiet" "--" file) 0)) - -(defun magit-anything-staged-p () - (not (eq (magit-git-exit-code "diff" "--quiet" "--cached") 0))) - -(defun magit-everything-clean-p () - (and (not (magit-anything-staged-p)) - (eq (magit-git-exit-code "diff" "--quiet") 0))) - -(defun magit-commit-parents (commit) - (cdr (split-string (magit-git-string "rev-list" "-1" "--parents" commit)))) - -;; XXX - let the user choose the parent - -(defun magit-choose-parent-id (commit op) - (let* ((parents (magit-commit-parents commit))) - (if (> (length parents) 1) - (error "Can't %s merge commits" op) - nil))) - -;;; Revisions and ranges - -(defvar magit-current-range nil - "The range described by the current buffer. -This is only non-nil in diff and log buffers. - -This has three possible (non-nil) forms. If it's a string REF or -a singleton list (REF), then the range is from REF to the current -working directory state (or HEAD in a log buffer). If it's a -pair (START . END), then the range is START..END.") -(make-variable-buffer-local 'magit-current-range) - -(defun magit-list-interesting-refs (&optional uninteresting) - "Return interesting references as given by `git show-ref'. -Removes references matching UNINTERESTING from the -results. UNINTERESTING can be either a function taking a single -argument or a list of strings used as regexps." - (let ((refs ())) - (dolist (line (magit-git-lines "show-ref")) - (if (string-match "[^ ]+ +\\(.*\\)" line) - (let ((ref (match-string 1 line))) - (cond ((and (functionp uninteresting) - (funcall uninteresting ref))) - ((and (not (functionp uninteresting)) - (loop for i in uninteresting thereis (string-match i ref)))) - (t - (let ((fmt-ref (magit-format-ref ref))) - (when fmt-ref - (push (cons fmt-ref - (replace-regexp-in-string "^refs/heads/" - "" ref)) - refs)))))))) - (nreverse refs))) - -(defun magit-format-ref (ref) - "Convert fully-specified ref REF into its displayable form -according to `magit-remote-ref-format'" - (cond - ((null ref) - nil) - ((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)) - (format "%s/%s" - (match-string 1 ref) - (match-string 2 ref)))))) - -(defun magit-tree-contents (treeish) - "Returns a list of all files under TREEISH. TREEISH can be a tree, -a commit, or any reference to one of those." - (let ((return-value nil)) - (with-temp-buffer - (magit-git-insert (list "ls-tree" "-r" treeish)) - (if (eql 0 (buffer-size)) - (error "%s is not a commit or tree." treeish)) - (goto-char (point-min)) - (while (search-forward-regexp "\t\\(.*\\)" nil 'noerror) - (push (match-string 1) return-value))) - return-value)) - -(defvar magit-uninteresting-refs '("refs/remotes/\\([^/]+\\)/HEAD$" "refs/stash")) - -(defun magit-read-file-from-rev (revision) - (magit-completing-read (format "Retrieve file from %s: " revision) - (magit-tree-contents revision) - nil - 'require-match - nil - 'magit-read-file-hist - (if buffer-file-name - (let ((topdir-length (length (magit-get-top-dir default-directory)))) - (substring (buffer-file-name) topdir-length))))) - -(defun magit-read-rev (prompt &optional default uninteresting) - (let* ((interesting-refs (magit-list-interesting-refs - (or uninteresting magit-uninteresting-refs))) - (reply (magit-completing-read (concat prompt ": ") interesting-refs - nil nil nil 'magit-read-rev-history default)) - (rev (or (cdr (assoc reply interesting-refs)) reply))) - (if (string= rev "") - nil - rev))) - -(defun magit-read-rev-range (op &optional def-beg def-end) - (let ((beg (magit-read-rev (format "%s start" op) - def-beg))) - (if (not beg) - nil - (save-match-data - (if (string-match "^\\(.+\\)\\.\\.\\(.+\\)$" beg) - (cons (match-string 1 beg) (match-string 2 beg)) - (let ((end (magit-read-rev (format "%s end" op) def-end))) - (cons beg end))))))) - -(defun magit-rev-to-git (rev) - (or rev - (error "No revision specified")) - (if (string= rev ".") - (magit-marked-commit) - rev)) - -(defun magit-rev-range-to-git (range) - (or range - (error "No revision range specified")) - (if (stringp range) - range - (if (cdr range) - (format "%s..%s" - (magit-rev-to-git (car range)) - (magit-rev-to-git (cdr range))) - (format "%s" (magit-rev-to-git (car range)))))) - -(defun magit-rev-describe (rev) - (or rev - (error "No revision specified")) - (if (string= rev ".") - "mark" - (magit-name-rev rev))) - -(defun magit-rev-range-describe (range things) - (or range - (error "No revision range specified")) - (if (stringp range) - (format "%s in %s" things range) - (if (cdr range) - (format "%s from %s to %s" things - (magit-rev-describe (car range)) - (magit-rev-describe (cdr range))) - (format "%s at %s" things (magit-rev-describe (car range)))))) - -(defun magit-default-rev (&optional no-trim) - (or (magit-name-rev (magit-commit-at-point t) no-trim) - (let ((branch (magit-guess-branch))) - (if branch - (if (string-match "^refs/\\(.*\\)" branch) - (match-string 1 branch) - branch))))) - -(defun magit-read-remote (&optional prompt def) - "Read the name of a remote. -PROMPT is used as the prompt, and defaults to \"Remote\". -DEF is the default value." - (let* ((prompt (or prompt "Remote")) - (def (or def (magit-guess-remote))) - (remotes (magit-git-lines "remote")) - - (reply (magit-completing-read (concat prompt ": ") remotes - nil nil nil nil def))) - (if (string= reply "") nil reply))) - -(defun magit-read-remote-branch (remote &optional prompt default) - (let* ((prompt (or prompt (format "Remote branch (in %s)" remote))) - (branches (delete nil - (mapcar - (lambda (b) - (and (not (string-match " -> " b)) - (string-match (format "^ *%s/\\(.*\\)$" - (regexp-quote remote)) b) - (match-string 1 b))) - (magit-git-lines "branch" "-r")))) - (reply (magit-completing-read (concat prompt ": ") branches - nil nil nil nil default))) - (if (string= reply "") nil reply))) - -;;; Sections - -;; A buffer in magit-mode is organized into hierarchical sections. -;; These sections are used for navigation and for hiding parts of the -;; buffer. -;; -;; Most sections also represent the objects that Magit works with, -;; such as files, diffs, hunks, commits, etc. The 'type' of a section -;; identifies what kind of object it represents (if any), and the -;; parent and grand-parent, etc provide the context. - -(defstruct magit-section - parent title beginning end children hidden type info - needs-refresh-on-show) - -(defvar magit-top-section nil - "The top section of the current buffer.") -(make-variable-buffer-local 'magit-top-section) -(put 'magit-top-section 'permanent-local t) - -(defvar magit-old-top-section nil) - -(defvar magit-section-hidden-default nil) - -(defun magit-new-section (title type) - "Create a new section with title TITLE and type TYPE in current buffer. - -If not `magit-top-section' exist, the new section will be the new top-section -otherwise, the new-section will be a child of the current top-section. - -If TYPE is nil, the section won't be highlighted." - (let* ((s (make-magit-section :parent magit-top-section - :title title - :type type - :hidden magit-section-hidden-default)) - (old (and magit-old-top-section - (magit-find-section (magit-section-path s) - magit-old-top-section)))) - (if magit-top-section - (push s (magit-section-children magit-top-section)) - (setq magit-top-section s)) - (if old - (setf (magit-section-hidden s) (magit-section-hidden old))) - s)) - -(defun magit-cancel-section (section) - "Delete the section SECTION." - (delete-region (magit-section-beginning section) - (magit-section-end section)) - (let ((parent (magit-section-parent section))) - (if parent - (setf (magit-section-children parent) - (delq section (magit-section-children parent))) - (setq magit-top-section nil)))) - -(defmacro magit-with-section (title type &rest body) - "Create a new section of title TITLE and type TYPE and evaluate BODY there. - -Sections created inside BODY will become children of the new -section. BODY must leave point at the end of the created section. - -If TYPE is nil, the section won't be highlighted." - (declare (indent 2)) - (let ((s (make-symbol "*section*"))) - `(let* ((,s (magit-new-section ,title ,type)) - (magit-top-section ,s)) - (setf (magit-section-beginning ,s) (point)) - ,@body - (setf (magit-section-end ,s) (point)) - (setf (magit-section-children ,s) - (nreverse (magit-section-children ,s))) - ,s))) - -(defun magit-set-section (title type start end) - "Create a new section of title TITLE and type TYPE with specified start and -end positions." - (let ((section (magit-new-section title type))) - (setf (magit-section-beginning section) start) - (setf (magit-section-end section) end) - section)) - -(defun magit-set-section-info (info &optional section) - (setf (magit-section-info (or section magit-top-section)) info)) - -(defun magit-set-section-needs-refresh-on-show (flag &optional section) - (setf (magit-section-needs-refresh-on-show - (or section magit-top-section)) - flag)) - -(defmacro magit-create-buffer-sections (&rest body) - "Empty current buffer of text and Magit's sections, and then evaluate BODY." - (declare (indent 0)) - `(let ((inhibit-read-only t)) - (erase-buffer) - (let ((magit-old-top-section magit-top-section)) - (setq magit-top-section nil) - ,@body - (when (null magit-top-section) - (magit-with-section 'top nil - (insert "(empty)\n"))) - (magit-propertize-section magit-top-section) - (magit-section-set-hidden magit-top-section - (magit-section-hidden magit-top-section))))) - -(defun magit-propertize-section (section) - "Add text-property needed for SECTION." - (put-text-property (magit-section-beginning section) - (magit-section-end section) - 'magit-section section) - (dolist (s (magit-section-children section)) - (magit-propertize-section s))) - -(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-title (car secs))))) - (setq secs (cdr secs))) - (and (car secs) - (magit-find-section (cdr path) (car secs)))))) - -(defun magit-section-path (section) - "Return the path of SECTION." - (if (not (magit-section-parent section)) - '() - (append (magit-section-path (magit-section-parent section)) - (list (magit-section-title section))))) - -(defun magit-find-section-after (pos) - "Find the first section that begins after POS." - (magit-find-section-after* pos (list magit-top-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))) - (do* ((current (or (magit-section-parent section) - section) - next) - (next (if (not (magit-section-hidden current)) - (magit-find-section-before* pos (magit-section-children current))) - (if (not (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-top-section)) - -(defun magit-insert-section (section-title-and-type - buffer-title washer cmd &rest args) - "Run CMD and put its result in a new section. - -SECTION-TITLE-AND-TYPE is either a string that is the title of the section -or (TITLE . TYPE) where TITLE is the title of the section and TYPE is its type. - -If there is no type, or if type is nil, the section won't be highlighted. - -BUFFER-TITLE is the inserted title of the section - -WASHER is a function that will be run after CMD. -The buffer will be narrowed to the inserted text. -It should add sectioning as needed for Magit interaction. - -CMD is an external command that will be run with ARGS as arguments." - (let* ((body-beg nil) - (section-title (if (consp section-title-and-type) - (car section-title-and-type) - section-title-and-type)) - (section-type (if (consp section-title-and-type) - (cdr section-title-and-type) - nil)) - (section - (magit-with-section section-title section-type - (if buffer-title - (insert (propertize buffer-title 'face 'magit-section-title) - "\n")) - (setq body-beg (point)) - (magit-cmd-insert cmd args) - (if (not (eq (char-before) ?\n)) - (insert "\n")) - (if washer - (save-restriction - (narrow-to-region body-beg (point)) - (goto-char (point-min)) - (funcall washer) - (goto-char (point-max))))))) - (if (= body-beg (point)) - (magit-cancel-section section) - (insert "\n")) - section)) - -(defun magit-git-section (section-title-and-type - buffer-title washer &rest args) - "Run git and put its result in a new section. - -see `magit-insert-section' for meaning of the arguments" - (apply #'magit-insert-section - section-title-and-type - buffer-title - washer - magit-git-executable - (append magit-git-standard-options args))) - -(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* ((initial (point)) - (section (magit-current-section)) - (end (- (magit-section-end section) 1)) - (parent (magit-section-parent section)) - (siblings (magit-section-children parent)) - (next-sibling (magit-find-section-after* end siblings))) - (if next-sibling - (magit-goto-section next-sibling) - (magit-goto-next-section)))) - -(defun magit-goto-previous-sibling-section () - "Go to the previous sibling section." - (interactive) - (let* ((section (magit-current-section)) - (beginning (magit-section-beginning section)) - (parent (magit-section-parent section)) - (siblings (magit-section-children parent)) - (previous-sibling (magit-find-section-before* beginning siblings))) - (if previous-sibling - (magit-goto-section previous-sibling) - (magit-goto-parent-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)) - (magit-show-commit section)))) - -(defun magit-goto-section-at-path (path) - "Go to the section described by PATH." - (let ((sec (magit-find-section path magit-top-section))) - (if sec - (goto-char (magit-section-beginning sec)) - (message "No such section")))) - -(defun magit-for-all-sections (func &optional top) - "Run FUNC on TOP and recursively on all its children. - -Default value for TOP is `magit-top-section'" - (let ((section (or top magit-top-section))) - (when section - (funcall func section) - (dolist (c (magit-section-children section)) - (magit-for-all-sections func c))))) - -(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))) - (if (< beg end) - (put-text-property beg end 'invisible hidden))) - (if (not 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 value, the section will be hidden if its 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 'hunk (first (magit-section-context-type (magit-current-section)))) - (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)) - ((notany #'magit-section-hidden (magit-section-children s)) - (magit-section-set-hidden s t)) - (t - (magit-section-expand s)))))) - -(defun magit-section-lineage (s) - "Return list of parent, grand-parents... for section S." - (when s - (cons s (magit-section-lineage (magit-section-parent s))))) - -(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." - (magit-with-refresh - (if all - (magit-section-show-level magit-top-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 there 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 there 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))) - -(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))) - (doc (format "Jump to section `%s'." title))) - `(progn - (defun ,fun () - ,doc - (interactive) - (magit-goto-section-at-path '(,sym))) - (put ',fun 'definition-name ',sym)))) - -(defmacro magit-define-inserter (sym arglist &rest body) - (declare (indent defun)) - (let ((fun (intern (format "magit-insert-%s" sym))) - (before (intern (format "magit-before-insert-%s-hook" sym))) - (after (intern (format "magit-after-insert-%s-hook" sym))) - (doc (format "Insert items for `%s'." sym))) - `(progn - (defvar ,before nil) - (defvar ,after nil) - (defun ,fun ,arglist - ,doc - (run-hooks ',before) - ,@body - (run-hooks ',after)) - (put ',before 'definition-name ',sym) - (put ',after 'definition-name ',sym) - (put ',fun 'definition-name ',sym)))) - -(defvar magit-highlighted-section nil) - -(defun magit-refine-section (section) - "Apply temporary refinements to the display of SECTION. -Refinements can be undone with `magit-unrefine-section'." - (let ((type (and section (magit-section-type section)))) - (cond ((and (eq type 'hunk) - magit-diff-refine-hunk - (not (eq magit-diff-refine-hunk 'all))) - ;; Refine the current hunk to show fine details, using - ;; diff-mode machinery. - (save-excursion - (goto-char (magit-section-beginning magit-highlighted-section)) - (diff-refine-hunk)))))) - -(defun magit-unrefine-section (section) - "Remove refinements to the display of SECTION done by `magit-refine-section'." - (let ((type (and section (magit-section-type section)))) - (cond ((and (eq type 'hunk) - magit-diff-refine-hunk - (not (eq magit-diff-refine-hunk 'all))) - ;; XXX this should be in some diff-mode function, like - ;; `diff-unrefine-hunk' - (remove-overlays (magit-section-beginning section) - (magit-section-end section) - 'diff-mode 'fine))))) - -(defvar magit-highlight-overlay nil) - -(defun magit-highlight-section () - "Highlight current section if it has a type." - (let ((section (magit-current-section))) - (when (not (eq section magit-highlighted-section)) - (when magit-highlighted-section - ;; remove any refinement from previous hunk - (magit-unrefine-section magit-highlighted-section)) - (setq magit-highlighted-section section) - (if (not magit-highlight-overlay) - (let ((ov (make-overlay 1 1))) - (overlay-put ov 'face 'magit-item-highlight) - (setq magit-highlight-overlay ov))) - (if (and section (magit-section-type section)) - (progn - (magit-refine-section section) - (move-overlay magit-highlight-overlay - (magit-section-beginning section) - (magit-section-end section) - (current-buffer))) - (delete-overlay magit-highlight-overlay))))) - -(defun magit-section-context-type (section) - (if (null section) - '() - (let ((c (or (magit-section-type section) - (if (symbolp (magit-section-title section)) - (magit-section-title section))))) - (if c - (cons c (magit-section-context-type - (magit-section-parent section))) - '())))) - -(defun magit-prefix-p (prefix list) - "Returns non-nil if PREFIX is a prefix of LIST. PREFIX and LIST should both be -lists. - -If the car of PREFIX is the symbol '*, then return non-nil if the cdr of PREFIX -is a sublist of LIST (as if '* matched zero or more arbitrary elements of LIST)" - ;;; Very schemish... - (or (null prefix) - (if (eq (car prefix) '*) - (or (magit-prefix-p (cdr prefix) list) - (and (not (null list)) - (magit-prefix-p prefix (cdr list)))) - (and (not (null list)) - (equal (car prefix) (car list)) - (magit-prefix-p (cdr prefix) (cdr list)))))) - -(defmacro magit-section-case (head &rest clauses) - "Make different action depending of current section. - -HEAD is (SECTION INFO &optional OPNAME), - SECTION will be bind to the current section, - INFO will be bind to the info's of the current section, - OPNAME is a string that will be used to describe current action, - -CLAUSES is a list of CLAUSE, each clause is (SECTION-TYPE &BODY) -where SECTION-TYPE describe section where BODY will be run. - -This returns non-nil if some section matches. If the -corresponding body return a non-nil value, it is returned, -otherwise it returns t. - -If no section matches, this returns nil if no OPNAME was given -and throws an error otherwise." - (declare (indent 1)) - (let ((section (car head)) - (info (cadr head)) - (type (make-symbol "*type*")) - (context (make-symbol "*context*")) - (opname (caddr head))) - `(let* ((,section (magit-current-section)) - (,info (and ,section (magit-section-info ,section))) - (,type (and ,section (magit-section-type ,section))) - (,context (magit-section-context-type ,section))) - (cond ,@(mapcar (lambda (clause) - (if (eq (car clause) t) - `(t (or (progn ,@(cdr clause)) - t)) - (let ((prefix (reverse (car clause))) - (body (cdr clause))) - `((magit-prefix-p ',prefix ,context) - (or (progn ,@body) - t))))) - clauses) - ,@(when opname - `(((run-hook-with-args-until-success - ',(intern (format "magit-%s-action-hook" opname)))) - ((not ,type) - (error "Nothing to %s here" ,opname)) - (t - (error "Can't %s a %s" - ,opname - (or (get ,type 'magit-description) - ,type))))))))) - -(defmacro magit-section-action (head &rest clauses) - (declare (indent 1)) - `(magit-with-refresh - (magit-section-case ,head ,@clauses))) - -(defmacro magit-add-action (head &rest clauses) - "Add additional actions to a pre-existing operator. -The syntax is identical to `magit-section-case', except that -OPNAME is mandatory and specifies the operation to which to add -the actions." - (declare (indent 1)) - (let ((section (car head)) - (info (cadr head)) - (type (caddr head))) - `(add-hook ',(intern (format "magit-%s-action-hook" type)) - (lambda () - ,(macroexpand - ;; Don't pass in the opname so we don't recursively - ;; run the hook again, and so we don't throw an - ;; error if no action matches. - `(magit-section-case (,section ,info) - ,@clauses)))))) - -(defun magit-wash-sequence (func) - "Run FUNC until end of buffer is reached. - -FUNC should leave point at the end of the modified region" - (while (and (not (eobp)) - (funcall func)))) - -(defmacro magit-define-command (sym arglist &rest body) - "Macro to define a magit command. -It will define the magit-SYM function having ARGLIST as argument. -It will also define the magit-SYM-command-hook variable. - -The defined function will call the function in the hook in -order until one return non nil. If they all return nil then body will be called. - -It is used to define hookable magit command: command defined by this -function can be enriched by magit extension like magit-topgit and magit-svn" - (declare (indent defun) - (debug (&define name lambda-list - [&optional stringp] ; Match the doc string, if present. - [&optional ("interactive" interactive)] - def-body))) - (let ((fun (intern (format "magit-%s" sym))) - (hook (intern (format "magit-%s-command-hook" sym))) - (doc (format "Command for `%s'." sym)) - (inter nil) - (instr body)) - (when (stringp (car body)) - (setq doc (car body) - instr (cdr body))) - (let ((form (car instr))) - (when (eq (car form) 'interactive) - (setq inter form - instr (cdr instr)))) - `(progn - (defvar ,hook nil) - (defun ,fun ,arglist - ,doc - ,inter - (or (run-hook-with-args-until-success - ',hook ,@(remq '&optional (remq '&rest arglist))) - ,@instr)) - (put ',fun 'definition-name ',sym) - (put ',hook 'definition-name ',sym)))) - -;;; Running commands - -(defun magit-set-mode-line-process (str) - (let ((pr (if str (concat " " str) ""))) - (save-excursion - (magit-for-all-buffers (lambda () - (setq mode-line-process pr)))))) - -(defun magit-process-indicator-from-command (comps) - (if (magit-prefix-p (cons magit-git-executable magit-git-standard-options) - comps) - (setq comps (nthcdr (+ (length magit-git-standard-options) 1) comps))) - (cond ((or (null (cdr comps)) - (not (member (car comps) '("remote")))) - (car comps)) - (t - (concat (car comps) " " (cadr comps))))) - -(defvar magit-process nil) -(defvar magit-process-client-buffer nil) -(defvar magit-process-buffer-name "*magit-process*" - "Buffer name for running git commands.") - -(defun magit-run* (cmd-and-args - &optional logline noerase noerror nowait input) - (if (and magit-process - (get-buffer magit-process-buffer-name)) - (error "Git is already running")) - (let ((cmd (car cmd-and-args)) - (args (cdr cmd-and-args)) - (dir default-directory) - (buf (get-buffer-create magit-process-buffer-name)) - (successp nil)) - (magit-set-mode-line-process - (magit-process-indicator-from-command cmd-and-args)) - (setq magit-process-client-buffer (current-buffer)) - (with-current-buffer buf - (view-mode 1) - (set (make-local-variable 'view-no-disable-on-exit) t) - (setq view-exit-action - (lambda (buffer) - (with-current-buffer buffer - (bury-buffer)))) - (setq buffer-read-only t) - (let ((inhibit-read-only t)) - (setq default-directory dir) - (if noerase - (goto-char (point-max)) - (erase-buffer)) - (insert "$ " (or logline - (mapconcat 'identity cmd-and-args " ")) - "\n") - (cond (nowait - (setq magit-process - (let ((process-connection-type magit-process-connection-type)) - (apply 'magit-start-process cmd buf cmd args))) - (set-process-sentinel magit-process 'magit-process-sentinel) - (set-process-filter magit-process 'magit-process-filter) - (when input - (with-current-buffer input - (process-send-region magit-process - (point-min) (point-max))) - (process-send-eof magit-process) - (sit-for 0.1 t)) - (cond ((= magit-process-popup-time 0) - (pop-to-buffer (process-buffer magit-process))) - ((> magit-process-popup-time 0) - (run-with-timer - magit-process-popup-time nil - (function - (lambda (buf) - (with-current-buffer buf - (when magit-process - (display-buffer (process-buffer magit-process)) - (goto-char (point-max)))))) - (current-buffer)))) - (setq successp t)) - (input - (with-current-buffer input - (setq default-directory dir) - (setq magit-process - ;; Don't use a pty, because it would set icrnl - ;; which would modify the input (issue #20). - (let ((process-connection-type nil)) - (apply 'magit-start-process cmd buf cmd args))) - (set-process-filter magit-process 'magit-process-filter) - (process-send-region magit-process - (point-min) (point-max)) - (process-send-eof magit-process) - (while (equal (process-status magit-process) 'run) - (sit-for 0.1 t)) - (setq successp - (equal (process-exit-status magit-process) 0)) - (setq magit-process nil)) - (magit-set-mode-line-process nil) - (magit-need-refresh magit-process-client-buffer)) - (t - (setq successp - (equal (apply 'process-file cmd nil buf nil args) 0)) - (magit-set-mode-line-process nil) - (magit-need-refresh magit-process-client-buffer)))) - (or successp - noerror - (error - "%s ... [Hit %s or see buffer %s for details]" - (or (with-current-buffer (get-buffer magit-process-buffer-name) - (when (re-search-backward - (concat "^error: \\(.*\\)" paragraph-separate) nil t) - (match-string 1))) - "Git failed") - (with-current-buffer magit-process-client-buffer - (key-description (car (where-is-internal - 'magit-display-process)))) - magit-process-buffer-name)) - successp))) - -(autoload 'dired-uncache "dired") -(defun magit-process-sentinel (process event) - (let ((msg (format "%s %s." (process-name process) (substring event 0 -1))) - (successp (string-match "^finished" event)) - (key (with-current-buffer magit-process-client-buffer - (key-description (car (where-is-internal - 'magit-display-process)))))) - (with-current-buffer (process-buffer process) - (let ((inhibit-read-only t)) - (goto-char (point-max)) - (insert msg "\n") - (message (if successp msg - (format "%s Hit %s or see buffer %s for details." - msg key (current-buffer))))) - (unless (memq (process-status process) '(run open)) - (dired-uncache default-directory))) - (setq magit-process nil) - (magit-set-mode-line-process nil) - (magit-refresh-buffer magit-process-client-buffer))) - -(defun magit-password (proc string) - "Checks if git/ssh asks for a password and ask the user for it." - (let (ask) - (cond ((or (string-match "^Enter passphrase for key '\\\(.*\\\)': $" string) - (string-match "^\\\(.*\\\)'s password:" string) - (string-match "^Password for '\\\(.*\\\)':" string)) - (setq ask (format "Password for '%s': " (match-string 1 string)))) - ((string-match "^[pP]assword:" string) - (setq ask "Password:"))) - (when ask - (process-send-string proc (concat (read-passwd ask nil) "\n"))))) - -(defun magit-username (proc string) - "Checks if git asks for a username and ask the user for it." - (when (string-match "^Username for '\\\(.*\\\)':" string) - (process-send-string proc - (concat - (read-string (format "Username for '%s': " - (match-string 1 string)) - nil nil (user-login-name)) - "\n")))) - -(defun magit-process-filter (proc string) - (save-current-buffer - (set-buffer (process-buffer proc)) - (let ((inhibit-read-only t)) - (magit-username proc string) - (magit-password proc string) - (goto-char (process-mark proc)) - ;; 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 (substring string (+ ret-pos 1)))) - (t - (insert string)))) - (set-marker (process-mark proc) (point))))) - -(defun magit-run (cmd &rest args) - (magit-with-refresh - (magit-run* (cons cmd args)))) - -(defun magit-run-git (&rest args) - (magit-with-refresh - (magit-run* (append (cons magit-git-executable - magit-git-standard-options) - args)))) - -(defun magit-run-git-with-input (input &rest args) - (magit-with-refresh - (magit-run* (append (cons magit-git-executable - magit-git-standard-options) - args) - nil nil nil nil input))) - -(defun magit-run-git-async (&rest args) - (message "Running %s %s" magit-git-executable (mapconcat 'identity args " ")) - (magit-run* (append (cons magit-git-executable - magit-git-standard-options) - args) - nil nil nil t)) - -(defun magit-run-async-with-input (input cmd &rest args) - (magit-run* (cons cmd args) nil nil nil t input)) - -(defun magit-display-process () - "Display output from most recent git command." - (interactive) - (unless (get-buffer magit-process-buffer-name) - (error "No Git commands have run")) - (display-buffer magit-process-buffer-name)) - -;;; Mode - -;; We define individual functions (instead of using lambda etc) so -;; that the online help can show something meaningful. - -(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 unpushed "Unpushed commits") - -(magit-define-level-shower 1) -(magit-define-level-shower 2) -(magit-define-level-shower 3) -(magit-define-level-shower 4) - -(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-log-edit t] - ["Add log entry" magit-add-log t] - ["Tag" magit-tag t] - ["Annotated tag" magit-annotated-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-manual-merge t] - ["Interactive resolve" magit-interactive-resolve-item 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-display-process t] - ["Quit Magit" magit-quit-window t])) - -(defvar magit-mode-hook nil "Hook run by `magit-mode'.") - -(put 'magit-mode 'mode-class 'special) - -(defvar magit-refresh-function nil) -(make-variable-buffer-local 'magit-refresh-function) -(put 'magit-refresh-function 'permanent-local t) - -(defvar magit-refresh-args nil) -(make-variable-buffer-local 'magit-refresh-args) -(put 'magit-refresh-args 'permanent-local t) - -(defvar last-point) - -(defun magit-remember-point () - (setq 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." - (if (invisible-p (point)) - (let ((end (magit-invisible-region-end (point)))) - (goto-char (if (= end last-point) - (magit-invisible-region-start (point)) - end)))) - (setq disable-point-adjustment t)) - -(defun magit-post-command-hook () - (magit-correct-point-after-command) - (magit-highlight-section)) - -(defun magit-mode () - "Review the status of a git repository and act on it. - -Please see the manual for a complete description of Magit. - -\\{magit-mode-map}" - (kill-all-local-variables) - (buffer-disable-undo) - (setq buffer-read-only t - truncate-lines t - major-mode 'magit-mode - mode-name "Magit" - mode-line-process "") - (add-hook 'pre-command-hook #'magit-remember-point nil t) - (add-hook 'post-command-hook #'magit-post-command-hook t t) - (use-local-map magit-mode-map) - (setq magit-current-indentation (magit-indentation-for default-directory)) - ;; Emacs' normal method of showing trailing whitespace gives weird - ;; results when `magit-whitespace-warning-face' is different from - ;; `trailing-whitespace'. - (if (and magit-highlight-whitespace magit-highlight-trailing-whitespace) - (setq show-trailing-whitespace nil)) - (run-mode-hooks 'magit-mode-hook)) - -(defun magit-mode-init (dir submode refresh-func &rest refresh-args) - (setq default-directory dir - magit-refresh-function refresh-func - magit-refresh-args refresh-args) - (funcall submode) - (magit-refresh-buffer)) - -(defun magit-indentation-for (dir) - (let (result) - (dolist (pair magit-highlight-indentation) - (if (string-match-p (car pair) dir) - (setq result (cdr pair)))) - result)) - -(defun magit-find-buffer (submode &optional dir) - (let ((topdir (magit-get-top-dir (or dir default-directory)))) - (dolist (buf (buffer-list)) - (if (with-current-buffer buf - (and (eq major-mode submode) - default-directory - (equal (expand-file-name default-directory) topdir))) - (return buf))))) - -(defun magit-find-status-buffer (&optional dir) - (magit-find-buffer 'magit-status-mode dir)) - -(defun magit-for-all-buffers (func &optional dir) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (if (and (derived-mode-p 'magit-mode) - (or (null dir) - (equal default-directory dir))) - (funcall func))))) - -(defun magit-refresh-buffer (&optional buffer) - (with-current-buffer (or buffer (current-buffer)) - (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 ((section-line (and old-section - (count-lines - (magit-section-beginning old-section) - (point)))) - (line-char (- old-point (point)))) - (if magit-refresh-function - (apply magit-refresh-function - magit-refresh-args)) - (magit-refresh-marked-commits-in-buffer) - (let ((s (and old-path (magit-find-section old-path magit-top-section)))) - (cond (s - (goto-char (magit-section-beginning s)) - (forward-line section-line) - (forward-char line-char)) - (t - (magit-goto-line old-line))) - (dolist (w (get-buffer-window-list (current-buffer))) - (set-window-point w (point))) - (magit-highlight-section)))))) - -(defun magit-string-has-prefix-p (string prefix) - (eq (compare-strings string nil (length prefix) prefix nil nil) t)) - -(defun magit-revert-buffers (dir &optional ignore-modtime) - (dolist (buffer (buffer-list)) - (when (and buffer - (buffer-file-name buffer) - ;; don't revert indirect buffers, as the parent will be reverted - (not (buffer-base-buffer buffer)) - (magit-string-has-prefix-p (buffer-file-name buffer) dir) - (file-readable-p (buffer-file-name buffer)) - (or ignore-modtime (not (verify-visited-file-modtime buffer))) - (not (buffer-modified-p buffer))) - (with-current-buffer buffer - (condition-case var - (revert-buffer t t nil) - (error (let ((signal-data (cadr var))) - (cond (t (magit-bug-report signal-data)))))))))) - -(defun magit-update-vc-modeline (dir) - "Update the modeline for buffers representable by magit." - (dolist (buffer (buffer-list)) - (when (and buffer - (buffer-file-name buffer) - (magit-string-has-prefix-p (buffer-file-name buffer) dir)) - (with-current-buffer buffer - (condition-case var - (vc-find-file-hook) - (error (let ((signal-data (cadr var))) - (cond (t (magit-bug-report signal-data)))))))))) - -(defvar magit-refresh-needing-buffers nil) -(defvar magit-refresh-pending nil) - -(defun magit-refresh-wrapper (func) - (if magit-refresh-pending - (funcall func) - (let* ((dir default-directory) - (status-buffer (magit-find-status-buffer dir)) - (magit-refresh-needing-buffers nil) - (magit-refresh-pending t)) - (unwind-protect - (funcall func) - (when magit-refresh-needing-buffers - (magit-revert-buffers dir) - (dolist (b (adjoin status-buffer - magit-refresh-needing-buffers)) - (magit-refresh-buffer b))))))) - -(defun magit-need-refresh (&optional buffer) - "Mark BUFFER as needing to be refreshed. If BUFFER is nil, use the -current buffer." - (pushnew (or buffer (current-buffer)) magit-refresh-needing-buffers :test 'eq)) - -(defun magit-refresh () - "Refresh current buffer to match repository state. -Also revert every unmodified buffer visiting files -in the corresponding directory." - (interactive) - (magit-with-refresh - (magit-need-refresh))) - -(defun magit-refresh-all () - "Refresh all magit buffers to match respective repository states. -Also revert every unmodified buffer visiting files -in the corresponding directories." - (interactive) - (magit-for-all-buffers #'magit-refresh-buffer default-directory)) - -;;; Untracked files - -(defun magit-wash-untracked-file () - (if (looking-at "^? \\(.*\\)$") - (let ((file (match-string-no-properties 1))) - (delete-region (point) (+ (line-end-position) 1)) - (magit-with-section file 'file - (magit-set-section-info file) - (insert "\t" file "\n")) - t) - nil)) - -(defun magit-wash-untracked-files () - ;; Setting magit-old-top-section to nil speeds up washing: no time - ;; is wasted looking up the old visibility, which doesn't matter for - ;; untracked files. - ;; - ;; XXX - speed this up in a more general way. - ;; - (let ((magit-old-top-section nil)) - (magit-wash-sequence #'magit-wash-untracked-file))) - -(defun magit-insert-untracked-files () - (unless (string= (magit-get "status" "showUntrackedFiles") "no") - (apply 'magit-git-section - `(untracked - "Untracked files:" - magit-wash-untracked-files - "ls-files" "--others" "-t" "--exclude-standard" - ,@(when magit-omit-untracked-dir-contents - '("--directory")))))) - -;;; Diffs and Hunks - -(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-toggle-diff-refine-hunk (&optional other) - (interactive "P") - "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 `magit-diff-refine-hunk' to change the default mode." - (let* ((old magit-diff-refine-hunk) - (new - (if other - (if (eq old 'all) t 'all) - (not old)))) - - ;; remove any old refining in currently highlighted section - (when (and magit-highlighted-section old (not (eq old 'all))) - (magit-unrefine-section magit-highlighted-section)) - - ;; set variable to new value locally - (set (make-local-variable 'magit-diff-refine-hunk) new) - - ;; if now highlighting in "selected only" mode, turn refining back - ;; on in the current section - (when (and magit-highlighted-section new (not (eq new 'all))) - (magit-refine-section magit-highlighted-section)) - - ;; `all' mode being turned on or off needs a complete refresh - (when (or (eq old 'all) (eq new 'all)) - (magit-refresh)))) - -(defun magit-diff-line-file () - (cond ((looking-at "^diff --git ./\\(.*\\) ./\\(.*\\)$") - (match-string-no-properties 2)) - ((looking-at "^diff --cc +\\(.*\\)$") - (match-string-no-properties 1)) - (t - nil))) - -(defun magit-wash-diffs () - (magit-wash-sequence #'magit-wash-diff-or-other-file)) - -(defun magit-wash-diff-or-other-file () - (or (magit-wash-diff) - (magit-wash-other-file))) - -(defun magit-wash-other-file () - (if (looking-at "^? \\(.*\\)$") - (let ((file (match-string-no-properties 1))) - (delete-region (point) (+ (line-end-position) 1)) - (magit-with-section file 'file - (magit-set-section-info file) - (insert "\tNew " file "\n")) - t) - nil)) - -(defvar magit-hide-diffs nil) - -(defvar magit-indentation-level 1) - -(defun magit-insert-diff-title (status file file2) - (let ((status-text (case status - ((unmerged) - (format "Unmerged %s" file)) - ((new) - (format "New %s" file)) - ((deleted) - (format "Deleted %s" file)) - ((renamed) - (format "Renamed %s (from %s)" - file file2)) - ((modified) - (format "Modified %s" file)) - ((typechange) - (format "Typechange %s" file)) - (t - (format "? %s" file))))) - (insert (make-string magit-indentation-level ?\t) status-text "\n"))) - -(defvar magit-current-diff-range nil - "Used internally when setting up magit diff sections.") - -(defun magit-wash-typechange-section (file) - (magit-set-section-info (list 'typechange file)) - (let ((first-start (point-marker)) - (second-start (progn (forward-line 1) - (search-forward-regexp "^diff") - (beginning-of-line) - (point-marker)))) - (let ((magit-indentation-level (+ magit-indentation-level 1))) - (save-restriction - (narrow-to-region first-start second-start) - (goto-char (point-min)) - (magit-with-section file 'diff - (magit-wash-diff-section))) - (save-restriction - (narrow-to-region second-start (point-max)) - (goto-char (point-min)) - (magit-with-section file 'diff - (magit-wash-diff-section)))))) - -(defun magit-wash-diff-section () - (cond ((looking-at "^\\* Unmerged path \\(.*\\)") - (let ((file (match-string-no-properties 1))) - (delete-region (point) (line-end-position)) - (insert "\tUnmerged " file "\n") - (magit-set-section-info (list 'unmerged file nil)) - t)) - ((looking-at "^diff") - (let ((file (magit-diff-line-file)) - (end (save-excursion - (forward-line) ;; skip over "diff" line - (if (search-forward-regexp "^diff\\|^@@" nil t) - (goto-char (match-beginning 0)) - (goto-char (point-max))) - (point-marker)))) - (let* ((status (cond - ((looking-at "^diff --cc") - 'unmerged) - ((save-excursion - (search-forward-regexp "^new file" end t)) - 'new) - ((save-excursion - (search-forward-regexp "^deleted" end t)) - 'deleted) - ((save-excursion - (search-forward-regexp "^rename" end t)) - 'renamed) - (t - 'modified))) - (file2 (cond - ((save-excursion - (search-forward-regexp "^rename from \\(.*\\)" - end t)) - (match-string-no-properties 1))))) - (magit-set-section-info (list status - file - (or file2 file) - magit-current-diff-range)) - (magit-insert-diff-title status file file2) - (when (search-forward-regexp "\\(--- \\(.*\\)\n\\+\\+\\+ \\(.*\\)\n\\)" () t) - (when (match-string 1) - (add-text-properties (match-beginning 1) (match-end 1) - '(face magit-diff-hunk-header)) - (add-text-properties (match-beginning 2) (match-end 2) - '(face magit-diff-file-header)) - (add-text-properties (match-beginning 3) (match-end 3) - '(face magit-diff-file-header)))) - (goto-char end) - (let ((magit-section-hidden-default nil)) - (magit-wash-sequence #'magit-wash-hunk)))) - t) - (t - nil))) - -(defun magit-wash-diff () - (let ((magit-section-hidden-default magit-hide-diffs)) - (magit-with-section (magit-current-line) 'diff - (magit-wash-diff-section)))) - -(defun magit-diff-item-kind (diff) - (car (magit-section-info diff))) - -(defun magit-diff-item-file (diff) - (cadr (magit-section-info diff))) - -(defun magit-diff-item-file2 (diff) - (caddr (magit-section-info diff))) - -(defun magit-diff-item-range (diff) - (nth 3 (magit-section-info diff))) - -(defun magit-wash-hunk () - (cond ((looking-at "\\(^@+\\)[^@]*@+.*") - (let ((n-columns (1- (length (match-string 1)))) - (head (match-string 0)) - (hunk-start-pos (point))) - (magit-with-section head 'hunk - (add-text-properties (match-beginning 0) (match-end 0) - '(face magit-diff-hunk-header)) - (forward-line) - (while (not (or (eobp) - (looking-at "^diff\\|^@@"))) - (magit-highlight-line-whitespace) - (let ((prefix (buffer-substring-no-properties - (point) (min (+ (point) n-columns) (point-max))))) - (cond ((string-match "\\+" prefix) - (magit-put-line-property 'face 'magit-diff-add)) - ((string-match "-" prefix) - (magit-put-line-property 'face 'magit-diff-del)) - (t - (magit-put-line-property 'face 'magit-diff-none)))) - (forward-line))) - - (when (eq magit-diff-refine-hunk 'all) - (save-excursion - (goto-char hunk-start-pos) - (diff-refine-hunk)))) - t) - (t - nil))) - -(defvar magit-diff-options nil) - -(defun magit-insert-diff (file status) - (let ((cmd magit-git-executable) - (args (append (list "diff") - (list (magit-diff-U-arg)) - magit-diff-options - (list "--" file)))) - (let ((p (point))) - (magit-git-insert args) - (if (not (eq (char-before) ?\n)) - (insert "\n")) - (save-restriction - (narrow-to-region p (point)) - (goto-char p) - (cond - ((eq status 'typechange) - (magit-insert-diff-title status file file) - (magit-wash-typechange-section file)) - (t - (magit-wash-diff-section))) - (goto-char (point-max)))))) - -(defvar magit-last-raw-diff nil) -(defvar magit-ignore-unmerged-raw-diffs nil) - -(defun magit-wash-raw-diffs () - (let ((magit-last-raw-diff nil)) - (magit-wash-sequence #'magit-wash-raw-diff))) - -(defun magit-wash-raw-diff () - (if (looking-at - ":\\([0-7]+\\) \\([0-7]+\\) [0-9a-f]+ [0-9a-f]+ \\(.\\)[0-9]*\t\\([^\t\n]+\\)$") - (let ((old-perm (match-string-no-properties 1)) - (new-perm (match-string-no-properties 2)) - (status (case (string-to-char (match-string-no-properties 3)) - (?A 'new) - (?D 'deleted) - (?M 'modified) - (?U 'unmerged) - (?T 'typechange) - (t nil))) - (file (match-string-no-properties 4))) - ;; If this is for the same file as the last diff, ignore it. - ;; Unmerged files seem to get two entries. - ;; We also ignore unmerged files when told so. - (if (or (equal file magit-last-raw-diff) - (and magit-ignore-unmerged-raw-diffs (eq status 'unmerged))) - (delete-region (point) (+ (line-end-position) 1)) - (setq magit-last-raw-diff file) - ;; The 'diff' section that is created here will not work with - ;; magit-insert-diff-item-patch etc when we leave it empty. - ;; Luckily, raw diffs are only produced for staged and - ;; unstaged changes, and we never call - ;; magit-insert-diff-item-patch on them. This is a bit - ;; brittle, of course. - (let ((magit-section-hidden-default magit-hide-diffs)) - (magit-with-section file 'diff - (delete-region (point) (+ (line-end-position) 1)) - (if (not (magit-section-hidden magit-top-section)) - (magit-insert-diff file status) - (magit-set-section-info (list status file nil)) - (magit-set-section-needs-refresh-on-show t) - (magit-insert-diff-title status file nil))))) - t) - nil)) - -(defun magit-hunk-item-diff (hunk) - (let ((diff (magit-section-parent hunk))) - (or (eq (magit-section-type diff) 'diff) - (error "Huh? Parent of hunk not a diff")) - diff)) - -(defun magit-diff-item-insert-header (diff buf) - (let ((beg (save-excursion - (goto-char (magit-section-beginning diff)) - (forward-line) - (point))) - (end (if (magit-section-children diff) - (magit-section-beginning (car (magit-section-children diff))) - (magit-section-end diff)))) - (magit-insert-region beg end buf))) - -(defun magit-insert-diff-item-patch (diff buf) - (let ((beg (save-excursion - (goto-char (magit-section-beginning diff)) - (forward-line) - (point))) - (end (magit-section-end diff))) - (magit-insert-region beg end buf))) - -(defun magit-insert-hunk-item-patch (hunk buf) - (magit-diff-item-insert-header (magit-hunk-item-diff 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-hunk-item-diff 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)) - (if (and (<= beg (point)) (< (point) end)) - (magit-insert-current-line buf) - (cond ((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-hunk-item-is-conflict-p (hunk) - ;;; XXX - Using the title is a bit too clever... - (string-match "^diff --cc" - (magit-section-title (magit-hunk-item-diff hunk)))) - -(defun magit-hunk-item-target-line (hunk) - (save-excursion - (beginning-of-line) - (let ((line (line-number-at-pos))) - (goto-char (magit-section-beginning hunk)) - (if (not (looking-at "@@+ .* \\+\\([0-9]+\\)\\(,[0-9]+\\)? @@+")) - (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 - (if (not (looking-at "-")) - (setq target (+ target 1))) - (forward-line)) - target)))) - -(defun magit-show (commit filename &optional select prefix) - "Returns a buffer containing the contents of the file FILENAME, as stored in -COMMIT. COMMIT may be one of the following: - -- A string with the name of a commit, such as \"head\" or \"dae86e\". See 'git - help revisions' for syntax. -- The symbol 'index, indicating that you want the version in Git's index or - staging area. -- The symbol 'working, indicating that you want the version in the working - directory. In this case you'll get a buffer visiting the file. If there's - already a buffer visiting that file, you'll get that one. - -When called interactively or when SELECT is non-nil, make the buffer active, -either in another window or (with a prefix argument) in the current window." - (interactive (let* ((revision (magit-read-rev "Retrieve file from revision")) - (filename (magit-read-file-from-rev revision))) - (list revision filename t current-prefix-arg))) - (if (eq commit 'working) - (find-file-noselect filename) - (let ((buffer (create-file-buffer (format "%s.%s" filename (replace-regexp-in-string ".*/" "" (prin1-to-string commit t)))))) - (cond - ((eq commit 'index) - (let ((checkout-string (magit-git-string "checkout-index" - "--temp" - filename))) - (string-match "^\\(.*\\)\t" checkout-string) - (with-current-buffer buffer - (let ((tmpname (match-string 1 checkout-string))) - (magit-with-silent-modifications - (insert-file-contents tmpname nil nil nil t)) - (delete-file tmpname))))) - (t - (with-current-buffer buffer - (magit-with-silent-modifications - (magit-git-insert (list "cat-file" "-p" - (concat commit ":" filename))))))) - (with-current-buffer buffer - (let ((buffer-file-name filename)) - (normal-mode)) - (goto-char (point-min))) - (if select - (if prefix - (switch-to-buffer buffer) - (switch-to-buffer-other-window buffer)) - buffer)))) - -(defmacro with-magit-tmp-buffer (var &rest body) - (declare (indent 1) - (debug (symbolp &rest form))) - `(let ((,var (generate-new-buffer magit-tmp-buffer-name))) - (unwind-protect - (progn ,@body) - (kill-buffer ,var)))) - -(defun magit-apply-diff-item (diff &rest args) - (when (zerop magit-diff-context-lines) - (setq args (cons "--unidiff-zero" args))) - (with-magit-tmp-buffer tmp - (magit-insert-diff-item-patch diff tmp) - (apply #'magit-run-git-with-input tmp - "apply" (append args (list "-"))))) - -(defun magit-apply-hunk-item* (hunk reverse &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." - (let ((zero-context (zerop magit-diff-context-lines)) - (use-region (magit-use-region-p))) - (when zero-context - (setq args (cons "--unidiff-zero" args))) - (when reverse - (setq args (cons "--reverse" args))) - (when (and use-region zero-context) - (error (concat "Not enough context to partially apply hunk. " - "Use `+' to increase context."))) - (with-magit-tmp-buffer tmp - (if use-region - (magit-insert-hunk-item-region-patch - hunk reverse (region-beginning) (region-end) tmp) - (magit-insert-hunk-item-patch hunk tmp)) - (apply #'magit-run-git-with-input tmp - "apply" (append args (list "-")))))) - -(defun magit-apply-hunk-item (hunk &rest args) - (apply #'magit-apply-hunk-item* hunk nil args)) - -(defun magit-apply-hunk-item-reverse (hunk &rest args) - (apply #'magit-apply-hunk-item* hunk t args)) - -(magit-define-inserter unstaged-changes (title) - (let ((magit-hide-diffs t) - (magit-current-diff-range (cons 'index 'working))) - (let ((magit-diff-options (append '() magit-diff-options))) - (magit-git-section 'unstaged title 'magit-wash-raw-diffs - "diff-files")))) - -(magit-define-inserter staged-changes (staged no-commit) - (let ((magit-current-diff-range (cons "HEAD" 'index))) - (when staged - (let ((magit-hide-diffs t) - (base (if no-commit - (magit-git-string "mktree") - "HEAD"))) - (let ((magit-diff-options (append '("--cached") magit-diff-options)) - (magit-ignore-unmerged-raw-diffs t)) - (magit-git-section 'staged "Staged changes:" 'magit-wash-raw-diffs - "diff-index" "--cached" - base)))))) - -;;; Logs and Commits - - ; Note: making this a plain defcustom would probably let users break - ; the parser too easily -(defvar magit-git-log-options - (list - "--pretty=format:* %h %s" - (format "--abbrev=%s" magit-sha1-abbrev-length))) - ; --decorate=full otherwise some ref prefixes are stripped - ; '("--pretty=format:* %H%d %s" "--decorate=full")) - -;; -;; Regexps for parsing ref names -;; -;; see the `git-check-ref-format' manpage for details - -(defconst magit-ref-nonchars "\000-\037\177 ~^:?*[\\" - "Characters specifically disallowed from appearing in Git symbolic refs. - -Evaluate (man \"git-check-ref-format\") for details") - -(defconst magit-ref-nonslash-re - (concat "\\(?:" - ;; "no slash-separated component can begin with a dot ." (rule 1) - "[^" magit-ref-nonchars "./]" - ;; "cannot have two consecutive dots .. anywhere." (rule 3) - "\\.?" - "\\)*") - "Regexp that matches the non-slash parts of a ref name. - -Evaluate (man \"git-check-ref-format\") for details") - -(defconst magit-refname-re - (concat - "\\(?:HEAD\\|" - - "\\(?:tag: \\)?" - - ;; optional non-slash sequence at the beginning - magit-ref-nonslash-re - - ;; any number of slash-prefixed sequences - "\\(?:" - "/" - magit-ref-nonslash-re - "\\)*" - - "/" ;; "must contain at least one /." (rule 2) - magit-ref-nonslash-re - - ;; "cannot end with a slash / nor a dot .." (rule 5) - "[^" magit-ref-nonchars "./]" - - "\\)" - ) - "Regexp that matches a git symbolic reference name. - -Evaluate (man \"git-check-ref-format\") for details") - -(defconst magit-log-oneline-re - (concat - "^\\([_\\*|/ -.]+\\)?" ; graph (1) - "\\(?:" - "\\([0-9a-fA-F]+\\)" ; sha1 (2) - "\\(?:" ; refs (3) - " " - "\\(" - "(" - magit-refname-re "\\(?:, " magit-refname-re "\\)*" - ")" - "\\)" - "\\)?" - "\\)?" - " ?\\(.*\\)$" ; msg (4) - )) - -(defconst magit-log-longline-re - (concat - ;; use \0 delimiter (from -z option) to identify commits. this prevents - ;; commit messages containing lines like "commit 00000" from polluting the - ;; display - "\\(?:\\(?:\\`\\|\0\\)" - "\\([_\\*|/ -.]+\\)?" ; graph (1) - "commit " - "\\([0-9a-fA-F]+\\)" ; sha1 (2) - "\\(?:" ; refs (3) - " " - "\\(" - "(" - magit-refname-re "\\(?:, " magit-refname-re "\\)*" - ")" - "\\)" - "\\)?" - "$\\)?" - " ?\\(.*\\)$" ; msg (4) - )) - -(defvar magit-present-log-line-function 'magit-present-log-line - "The function to use when generating a log line. -It takes four args: CHART, SHA1, REFS and MESSAGE. The function -must return a string which will represent the log line.") - -(defun magit-log-get-bisect-state-color (suffix) - (if (string= suffix "bad") - (list suffix 'magit-log-head-label-bisect-bad) - (list suffix 'magit-log-head-label-bisect-good))) - -(defun magit-log-get-patches-color (suffix) - (list (and (string-match ".+/\\(.+\\)" suffix) - (match-string 1 suffix)) - 'magit-log-head-label-patches)) - -(defvar magit-log-remotes-color-hook nil) - -(defun magit-log-get-remotes-color (suffix) - (or - (run-hook-with-args-until-success - 'magit-log-remotes-color-hook suffix) - (list suffix 'magit-log-head-label-remote))) - -(defvar magit-refs-namespaces - '(("tags" . magit-log-head-label-tags) - ("remotes" magit-log-get-remotes-color) - ("heads" . magit-log-head-label-local) - ("patches" magit-log-get-patches-color) - ("bisect" magit-log-get-bisect-state-color))) - -(defun magit-ref-get-label-color (r) - (let ((uninteresting (loop for re in magit-uninteresting-refs - thereis (string-match re r)))) - (if uninteresting (list nil nil) - (let* ((ref-re "\\(?:tag: \\)?refs/\\(?:\\([^/]+\\)/\\)?\\(.+\\)") - (label (and (string-match ref-re r) - (match-string 2 r))) - (res (let ((colorizer - (cdr (assoc (match-string 1 r) - magit-refs-namespaces)))) - (cond ((null colorizer) - (list r 'magit-log-head-label-default)) - ((symbolp colorizer) - (list label colorizer)) - ((listp colorizer) - (funcall (car colorizer) - (match-string 2 r))) - (t - (list r 'magit-log-head-label-default)))))) - res)))) - -(defun magit-present-log-line (graph sha1 refs message) - "The default log line generator." - (let ((string-refs - (when refs - (let ((colored-labels - (delete nil - (mapcar (lambda (r) - (destructuring-bind (label face) - (magit-ref-get-label-color r) - (and label - (propertize label 'face face)))) - refs)))) - (concat - (mapconcat 'identity colored-labels " ") - " "))))) - - (concat - (if sha1 - (propertize sha1 'face 'magit-log-sha1) - (insert-char ? magit-sha1-abbrev-length)) - " " - (when graph - (propertize graph 'face 'magit-log-graph)) - string-refs - (when message - (propertize message 'face 'magit-log-message))))) - -(defvar magit-log-count () - "Internal var used to count the number of logs actually added in a buffer.") - -(defmacro magit-create-log-buffer-sections (&rest body) - "Empty current buffer of text and magit's section, and then evaluate BODY. - -if the number of logs inserted in the buffer is `magit-log-cutoff-length' -insert a line to tell how to insert more of them" - (declare (indent 0)) - `(let ((magit-log-count 0) (inhibit-read-only t)) - (magit-create-buffer-sections - (magit-with-section 'log nil - ,@body - (if (= magit-log-count magit-log-cutoff-length) - (magit-with-section "longer" 'longer - (insert "type \"e\" to show more logs\n"))))))) - -(defun magit-wash-log-line (style) - (beginning-of-line) - (let ((line-re (cond ((eq style 'long) magit-log-longline-re) - (t magit-log-oneline-re)))) - (cond - ((looking-at line-re) - (let ((chart (match-string 1)) - (sha1 (match-string 2)) - (msg (match-string 4)) - (refs (when (match-string 3) - (delq nil - (mapcar - (lambda (s) - (and (not - (or (string= s "tag:") - (string= s "HEAD"))) ; as of 1.6.6 - s)) - (split-string (match-string 3) "[(), ]" t)))))) - (delete-region (point-at-bol) (point-at-eol)) - (insert (funcall magit-present-log-line-function chart sha1 refs msg)) - (goto-char (point-at-bol)) - (if sha1 - (magit-with-section sha1 'commit - (when magit-log-count (setq magit-log-count (1+ magit-log-count))) - (magit-set-section-info sha1) - (forward-line)) - (forward-line)))) - (t - (forward-line))) - t)) - -(defun magit-wash-log (&optional style) - (let ((magit-old-top-section nil)) - (magit-wash-sequence (apply-partially 'magit-wash-log-line style)))) - -(defvar magit-currently-shown-commit nil) - -(defun magit-wash-commit () - (let ((magit-current-diff-range)) - (when (looking-at "^commit \\([0-9a-fA-F]\\{40\\}\\)") - (setq magit-current-diff-range (match-string 1)) - (add-text-properties (match-beginning 1) (match-end 1) - '(face magit-log-sha1))) - (cond - ((search-forward-regexp "^Merge: \\([0-9a-fA-F]+\\) \\([0-9a-fA-F]+\\)$" nil t) - (setq magit-current-diff-range (cons (cons (match-string 1) - (match-string 2)) - magit-current-diff-range)) - (let ((first (magit-set-section nil 'commit (match-beginning 1) (match-end 1))) - (second (magit-set-section nil 'commit (match-beginning 2) (match-end 2)))) - (magit-set-section-info (match-string 1) first) - (magit-set-section-info (match-string 2) second)) - (make-commit-button (match-beginning 1) (match-end 1)) - (make-commit-button (match-beginning 2) (match-end 2))) - (t - (setq magit-current-diff-range (cons (concat magit-current-diff-range "^") - magit-current-diff-range)))) - (search-forward-regexp "^$") - (while (and - (search-forward-regexp "\\(\\b[0-9a-fA-F]\\{4,40\\}\\b\\)\\|\\(^diff\\)" nil 'noerror) - (not (match-string 2))) - (let ((sha1 (match-string 1)) - (start (match-beginning 1)) - (end (match-end 1))) - (when (string-equal "commit" (magit-git-string "cat-file" "-t" sha1)) - (make-commit-button start end) - (let ((section (magit-set-section sha1 'commit start end))) - (magit-set-section-info sha1 section))))) - (beginning-of-line) - (when (looking-at "^diff") - (magit-wash-diffs)) - (goto-char (point-max)) - (insert "\n") - (if magit-back-navigation-history - (magit-with-section "[back]" 'button - (insert-text-button "[back]" - 'help-echo "Previous commit" - 'action 'magit-show-commit-backward - 'follow-link t - 'mouse-face 'magit-item-highlight))) - (insert " ") - (if magit-forward-navigation-history - (magit-with-section "[forward]" 'button - (insert-text-button "[forward]" - 'help-echo "Next commit" - 'action 'magit-show-commit-forward - 'follow-link t - 'mouse-face 'magit-item-highlight))))) - -(defun make-commit-button (start end) - (make-text-button start end - 'help-echo "Visit commit" - 'action (lambda (button) - (save-excursion - (goto-char button) - (magit-visit-item))) - 'follow-link t - 'mouse-face 'magit-item-highlight - 'face 'magit-log-sha1)) - -(defun magit-refresh-commit-buffer (commit) - (magit-configure-have-abbrev) - (magit-configure-have-decorate) - (magit-create-buffer-sections - (apply #'magit-git-section nil nil - 'magit-wash-commit - "log" - "--max-count=1" - "--pretty=medium" - `(,@(if magit-have-abbrev (list "--no-abbrev-commit")) - ,@(if magit-have-decorate (list "--decorate=full")) - "--cc" - "-p" ,commit)))) - -(define-derived-mode magit-commit-mode magit-mode "Magit" - "Mode to view a git commit. - -\\{magit-commit-mode-map}" - :group 'magit) - -(defvar magit-commit-buffer-name "*magit-commit*" - "Buffer name for displaying commit log messages.") - -(defun magit-show-commit (commit &optional scroll inhibit-history select) - "Show information about a commit in the buffer named by -`magit-commit-buffer-name'. COMMIT can be any valid name for a commit -in the current Git repository. - -When called interactively or when SELECT is non-nil, switch to -the commit buffer using `pop-to-buffer'. - -Unless INHIBIT-HISTORY is non-nil, the commit currently shown -will be pushed onto `magit-back-navigation-history' and -`magit-forward-navigation-history' will be cleared. - -Noninteractively, if the commit is already displayed and SCROLL -is provided, call SCROLL's function definition in the commit -window. (`scroll-up' and `scroll-down' are typically passed in -for this argument.)" - (interactive (list (magit-read-rev "Show commit (hash or ref)") - nil nil t)) - (when (magit-section-p commit) - (setq commit (magit-section-info commit))) - (unless (eql 0 (magit-git-exit-code "cat-file" "commit" commit)) - (error "%s is not a commit" commit)) - (let ((dir default-directory) - (buf (get-buffer-create magit-commit-buffer-name))) - (cond - ((and (equal magit-currently-shown-commit commit) - ;; if it's empty then the buffer was killed - (with-current-buffer buf - (> (length (buffer-string)) 1))) - (let ((win (get-buffer-window buf))) - (cond ((not win) - (display-buffer buf)) - (scroll - (with-selected-window win - (funcall scroll)))))) - (commit - (display-buffer buf) - (with-current-buffer buf - (unless inhibit-history - (push (cons default-directory magit-currently-shown-commit) - magit-back-navigation-history) - (setq magit-forward-navigation-history nil)) - (setq magit-currently-shown-commit commit) - (goto-char (point-min)) - (magit-mode-init dir 'magit-commit-mode - #'magit-refresh-commit-buffer commit)))) - (if select - (pop-to-buffer buf)))) - -(defun magit-show-commit-backward (&optional ignored) - ;; Ignore argument passed by push-button - "Show the commit at the head of `magit-back-navigation-history in -`magit-commit-buffer-name`." - (interactive) - (with-current-buffer magit-commit-buffer-name - (unless magit-back-navigation-history - (error "No previous commit.")) - (let ((histitem (pop magit-back-navigation-history))) - (push (cons default-directory magit-currently-shown-commit) - magit-forward-navigation-history) - (setq default-directory (car histitem)) - (magit-show-commit (cdr histitem) nil 'inhibit-history)))) - -(defun magit-show-commit-forward (&optional ignored) - ;; Ignore argument passed by push-button - "Show the commit at the head of `magit-forward-navigation-history in -`magit-commit-buffer-name`." - (interactive) - (with-current-buffer magit-commit-buffer-name - (unless magit-forward-navigation-history - (error "No next commit.")) - (let ((histitem (pop magit-forward-navigation-history))) - (push (cons default-directory magit-currently-shown-commit) - magit-back-navigation-history) - (setq default-directory (car histitem)) - (magit-show-commit (cdr histitem) nil 'inhibit-history)))) - -(defvar magit-marked-commit nil) - -(defvar magit-mark-overlay nil) -(make-variable-buffer-local 'magit-mark-overlay) -(put 'magit-mark-overlay 'permanent-local t) - -(defun magit-refresh-marked-commits () - (magit-for-all-buffers #'magit-refresh-marked-commits-in-buffer)) - -(defun magit-refresh-marked-commits-in-buffer () - (if (not magit-mark-overlay) - (let ((ov (make-overlay 1 1))) - (overlay-put ov 'face 'magit-item-mark) - (setq magit-mark-overlay ov))) - (delete-overlay magit-mark-overlay) - (magit-for-all-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)))))) - -(defun magit-set-marked-commit (commit) - (setq magit-marked-commit commit) - (magit-refresh-marked-commits)) - -(defun magit-marked-commit () - (or magit-marked-commit - (error "No commit marked"))) - -(defun magit-remote-branch-name (remote branch) - "Get the name of the branch BRANCH on remote REMOTE" - (if (string= remote ".") - branch - (concat remote "/" branch))) - -(magit-define-inserter unpulled-commits (remote branch) - (when remote - (apply #'magit-git-section - 'unpulled "Unpulled commits:" 'magit-wash-log "log" - (append magit-git-log-options - (list - (format "HEAD..%s" (magit-remote-branch-name remote branch))))))) - -(magit-define-inserter unpushed-commits (remote branch) - (when remote - (apply #'magit-git-section - 'unpushed "Unpushed commits:" 'magit-wash-log "log" - (append magit-git-log-options - (list - (format "%s..HEAD" (magit-remote-branch-name remote branch))))))) - -(defun magit-remote-branch-for (local-branch &optional fully-qualified-name) - "Guess the remote branch name that LOCAL-BRANCH is tracking. -Gives a fully qualified name (e.g., refs/remotes/origin/master) if -FULLY-QUALIFIED-NAME is non-nil." - (let ((merge (magit-get "branch" local-branch "merge"))) - (save-match-data - (if (and merge (string-match "^refs/heads/\\(.+\\)" merge)) - (concat (if fully-qualified-name - (let ((remote-name (magit-get "branch" local-branch "remote"))) - (if (string= "." remote-name) - "refs/heads/" - (concat "refs/remotes/" remote-name "/")))) - (match-string 1 merge)))))) - -;;; Status - -(defvar magit-remote-string-hook nil) - -(defun magit-remote-string (remote remote-branch remote-rebase) - (cond - ((string= "." remote) - (concat - (when remote-rebase "onto ") - "branch " - (propertize remote-branch 'face 'magit-branch))) - (remote - (concat - (when remote-rebase "onto ") - (propertize remote-branch 'face 'magit-branch) - " @ " - remote - " (" - (magit-get "remote" remote "url") - ")")) - (t - (run-hook-with-args-until-success 'magit-remote-string-hook)))) - -(declare-function magit--bisect-info-for-status "magit-bisect" (branch)) - -(defun magit-refresh-status () - (magit-create-buffer-sections - (magit-with-section 'status nil - (let* ((branch (magit-get-current-branch)) - (remote (and branch (magit-get "branch" branch "remote"))) - (remote-rebase (and branch (magit-get-boolean "branch" branch "rebase"))) - (remote-branch (or (and branch (magit-remote-branch-for branch)) branch)) - (remote-string (magit-remote-string remote remote-branch remote-rebase)) - (head (magit-git-string - "log" - "--max-count=1" - "--abbrev-commit" - (format "--abbrev=%s" magit-sha1-abbrev-length) - "--pretty=oneline")) - (no-commit (not head))) - (when remote-string - (insert "Remote: " remote-string "\n")) - (insert (format "Local: %s %s\n" - (propertize (magit--bisect-info-for-status branch) - 'face 'magit-branch) - (abbreviate-file-name default-directory))) - (insert (format "Head: %s\n" - (if no-commit "nothing commited (yet)" head))) - (let ((merge-heads (magit-file-lines (concat (magit-git-dir) - "MERGE_HEAD")))) - (if merge-heads - (insert (format "Merging: %s\n" - (mapconcat 'identity - (mapcar 'magit-name-rev merge-heads) - ", "))))) - (let ((rebase (magit-rebase-info))) - (if rebase - (insert (apply 'format "Rebasing: onto %s (%s of %s); Press \"R\" to Abort, Skip, or Continue\n" rebase)))) - (insert "\n") - (magit-git-exit-code "update-index" "--refresh") - (magit-insert-stashes) - (magit-insert-untracked-files) - (magit-insert-pending-changes) - (magit-insert-pending-commits) - (magit-insert-unpulled-commits remote remote-branch) - (let ((staged (or no-commit (magit-anything-staged-p)))) - (magit-insert-unstaged-changes - (if staged "Unstaged changes:" "Changes:")) - (magit-insert-staged-changes staged no-commit)) - (magit-insert-unpushed-commits remote remote-branch) - (run-hooks 'magit-refresh-status-hook))))) - -(defun magit-init (dir) - "Initialize git repository in the DIR directory." - (interactive (list (read-directory-name "Directory for Git repository: "))) - (let* ((dir (file-name-as-directory (expand-file-name dir))) - (topdir (magit-get-top-dir dir))) - (when (or (not topdir) - (yes-or-no-p - (format - (if (string-equal topdir dir) - "There is already a Git repository in %s. Reinitialize? " - "There is a Git repository in %s. Create another in %s? ") - topdir dir))) - (unless (file-directory-p dir) - (and (y-or-n-p (format "Directory %s does not exists. Create it? " dir)) - (make-directory dir))) - (let ((default-directory dir)) - (magit-run* (list magit-git-executable "init")))))) - -(define-derived-mode magit-status-mode magit-mode "Magit" - "Mode for looking at git status. - -\\{magit-status-mode-map}" - :group 'magit) - -(defvar magit-default-directory nil) - -(defun magit-save-some-buffers (&optional msg pred) - "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 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 dir passed to `magit-status'." - (and buffer-file-name - (string= (magit-get-top-dir magit-default-directory) - (magit-get-top-dir (file-name-directory buffer-file-name))))) - -;;;###autoload -(defun magit-status (dir) - "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." - (interactive (list (if current-prefix-arg - (magit-read-top-dir - (> (prefix-numeric-value current-prefix-arg) - 4)) - (or (magit-get-top-dir default-directory) - (magit-read-top-dir nil))))) - (magit-save-some-buffers) - (let ((topdir (magit-get-top-dir dir))) - (unless topdir - (when (y-or-n-p (format "There is no Git repository in %S. Create one? " - dir)) - (magit-init dir) - (setq topdir (magit-get-top-dir dir)))) - (when topdir - (let ((buf (or (magit-find-status-buffer topdir) - (generate-new-buffer - (concat "*magit: " - (file-name-nondirectory - (directory-file-name topdir)) "*"))))) - (funcall magit-status-buffer-switch-function buf) - (magit-mode-init topdir 'magit-status-mode #'magit-refresh-status))))) - -(magit-define-command automatic-merge (revision) - "Merge REVISION into the current 'HEAD'; commit unless merge fails. -\('git merge REVISION')." - (interactive (list (magit-read-rev "Merge" (magit-guess-branch)))) - (if revision - (magit-run-git "merge" (magit-rev-to-git revision)))) - -(magit-define-command manual-merge (revision) - "Merge REVISION into the current 'HEAD'; commit unless merge fails. -\('git merge REVISION')." - (interactive (list (magit-read-rev "Merge" (magit-guess-branch)))) - (when revision - (apply 'magit-run-git - "merge" "--no-commit" - (magit-rev-to-git revision) - magit-custom-options) - (when (file-exists-p ".git/MERGE_MSG") - (magit-log-edit)))) - -;;; Staging and Unstaging - -(defun magit-stage-item (&optional ask) - "Add the item at point to the staging area. -If ASK is set, ask for the file name rather than picking the one -at point." - (interactive "P") - (if ask - (magit-run-git "add" (read-file-name "File to stage: ")) - (magit-section-action (item info "stage") - ((untracked file) - (magit-run-git "add" info)) - ((untracked) - (apply #'magit-run-git "add" "--" - (magit-git-lines "ls-files" "--other" "--exclude-standard"))) - ((unstaged diff hunk) - (if (magit-hunk-item-is-conflict-p item) - (error (concat "Can't stage individual resolution hunks. " - "Please stage the whole file."))) - (magit-apply-hunk-item item "--cached")) - ((unstaged diff) - (magit-run-git "add" "-u" (magit-diff-item-file item))) - ((staged *) - (error "Already staged")) - ((diff diff) - (save-excursion - (magit-goto-parent-section) - (magit-stage-item))) - ((diff diff hunk) - (save-excursion - (magit-goto-parent-section) - (magit-goto-parent-section) - (magit-stage-item))) - ((hunk) - (error "Can't stage this hunk")) - ((diff) - (error "Can't stage this diff"))))) - -(defun magit-unstage-item () - "Remove the item at point from the staging area." - (interactive) - (magit-section-action (item info "unstage") - ((staged diff hunk) - (magit-apply-hunk-item-reverse item "--cached")) - ((staged diff) - (if (eq (car info) 'unmerged) - (error "Can't unstage an unmerged file. Resolve it first")) - (if (magit-no-commit-p) - (magit-run-git "rm" "--cached" "--" (magit-diff-item-file item)) - (magit-run-git "reset" "-q" "HEAD" "--" (magit-diff-item-file item)))) - ((unstaged *) - (error "Already unstaged")) - ((diff diff) - (save-excursion - (magit-goto-parent-section) - (magit-unstage-item))) - ((diff diff hunk) - (save-excursion - (magit-goto-parent-section) - (magit-goto-parent-section) - (magit-unstage-item))) - ((hunk) - (error "Can't unstage this hunk")) - ((diff) - (error "Can't unstage this diff")))) - -(defun magit-stage-all (&optional also-untracked-p) - "Add all remaining changes in tracked files to staging area. -With prefix argument, add remaining untracked files as well. -\('git add -u .' or 'git add .', respectively)." - (interactive "P") - (if also-untracked-p - (magit-run-git "add" ".") - (magit-run-git "add" "-u" "."))) - -(defun magit-unstage-all () - "Remove all changes from staging area. -\('git reset --mixed HEAD')." - (interactive) - (magit-run-git "reset" "HEAD")) - -;;; Branches - -(defun escape-branch-name (branch) - "Escapes branch names to remove problematic characters." - (replace-regexp-in-string "[/]" "-" branch)) - -(defun magit-default-tracking-name-remote-plus-branch - (remote branch) - "Use the remote name plus a hyphen plus the escaped branch name for tracking branches." - (concat remote "-" (escape-branch-name branch))) - -(defun magit-default-tracking-name-branch-only - (remote branch) - "Use just the escaped branch name for tracking branches." - (escape-branch-name branch)) - -(defun magit-get-tracking-name (remote branch) - "Given a REMOTE and a BRANCH name, ask the user for a local -tracking brach name suggesting a sensible default." - (when (yes-or-no-p - (format "Create local tracking branch for %s? " branch)) - (let* ((default-name - (funcall magit-default-tracking-name-function remote branch)) - (chosen-name (read-string (format "Call local branch (%s): " default-name) - nil - nil - default-name))) - (when (magit-ref-exists-p (concat "refs/heads/" chosen-name)) - (error "'%s' already exists." chosen-name)) - chosen-name))) - -(defun magit-maybe-create-local-tracking-branch (rev) - "Depending on the users wishes, create a tracking branch for -rev... maybe." - (if (string-match "^\\(?:refs/\\)?remotes/\\([^/]+\\)/\\(.+\\)" rev) - (let* ((remote (match-string 1 rev)) - (branch (match-string 2 rev)) - (tracker-name (magit-get-tracking-name remote branch))) - (when tracker-name - (magit-run-git "checkout" "-b" tracker-name rev) - t)) - nil)) - -(magit-define-command 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 tracking branch. -\('git checkout [-b] REVISION')." - (interactive - (list (let ((current-branch (magit-get-current-branch)) - (default (magit-default-rev))) - (magit-read-rev "Switch to" - (unless (string= current-branch default) - default) - (if current-branch - (cons (concat "refs/heads/" current-branch "$") - magit-uninteresting-refs) - magit-uninteresting-refs))))) - (if revision - (when (not (magit-maybe-create-local-tracking-branch revision)) - (magit-save-some-buffers) - (magit-run-git "checkout" (magit-rev-to-git revision)) - (magit-update-vc-modeline default-directory)))) - -(defun magit-read-create-branch-args () - (let* ((cur-branch (magit-get-current-branch)) - (cur-point (magit-default-rev)) - (branch (read-string "Create branch: ")) - (parent (magit-read-rev "Parent" - (cond - ((eq magit-create-branch-behaviour 'at-point) cur-point) - ((eq magit-create-branch-behaviour 'at-head) cur-branch) - (t cur-branch))))) - (list branch parent))) - -(magit-define-command 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 (magit-read-create-branch-args)) - (when (and branch (not (string= branch "")) - parent) - (magit-save-some-buffers) - (magit-run-git "checkout" "-b" - branch - (append - magit-custom-options - (magit-rev-to-git parent))) - (magit-update-vc-modeline default-directory))) - -(defun magit-delete-branch (branch &optional force) - "Deletes a 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" (magit-default-rev 'notrim)) - current-prefix-arg)) - (let* ((remote (magit-remote-part-of-branch branch)) - (is-current (string= branch (magit-get-current-branch))) - (args (list "branch" - (if force "-D" "-d") - branch))) - (cond - (remote - (magit-run-git "push" remote (concat ":refs/heads/" (magit-branch-no-remote branch)))) - (is-current - (when (y-or-n-p "Cannot delete current branch. Switch to master first? ") - (progn - (magit-checkout "master") - (apply 'magit-run-git args)) - (message "The current branch was not deleted."))) - (t - (apply 'magit-run-git args))))) - -(defun magit-move-branch (old new &optional force) - "Renames or moves a branch. -With prefix, forces the move even if NEW already exists. -\('git branch [-m|-M] OLD NEW')." - (interactive (list (magit-read-rev "Old name" (magit-default-rev)) - (read-string "New name: ") - current-prefix-arg)) - (magit-run-git "branch" (if force - "-M" - "-m") - (magit-rev-to-git old) new)) - -(defun magit-guess-branch () - (magit-section-case (item info) - ((branch) - (magit-section-info (magit-current-section))) - ((wazzup commit) - (magit-section-info (magit-section-parent item))) - ((commit) (magit-name-rev (substring info 0 magit-sha1-abbrev-length))) - ((wazzup) info))) - -;;; Remotes - -(defun magit-add-remote (remote url) - "Adds a remote and fetches it. -\('git remote add REMOTE URL')." - (interactive (list (read-string "Add remote: ") - (read-string "URL: "))) - (magit-run-git "remote" "add" "-f" remote url)) - -(defun magit-remove-remote (remote) - "Deletes a remote. -\('git remote rm REMOTE')." - (interactive (list (magit-read-remote "Remote to delete"))) - (magit-run-git "remote" "rm" remote)) - -(defun magit-rename-remote (old new) - "Renames a remote. -\('git remote rename OLD NEW')." - (interactive (list (magit-read-remote "Old name") - (read-string "New name: "))) - (magit-run-git "remote" "rename" old new)) - -(defun magit-guess-remote () - (magit-section-case (item info) - ((branch) - (magit-section-info (magit-section-parent item))) - ((remote) - info) - (t - (if (string= info ".") - info - (magit-get-current-remote))))) - -;;; Merging - -(defun magit-merge (revision) - "Merge REVISION into the current 'HEAD'; leave changes uncommitted. -With a prefix-arg, the merge will be squashed. -\('git merge --no-commit [--squash|--no-ff] REVISION')." - (interactive - (list (magit-read-rev "Merge" (magit-default-rev)))) - (if revision - (apply 'magit-run-git - "merge" - (magit-rev-to-git revision) - magit-custom-options))) - -;;; Rebasing - -(defun magit-rebase-info () - "Returns a list indicating the state of an in-progress rebase, -if any." - (let ((git-dir (magit-git-dir))) - (cond ((file-exists-p (concat git-dir "rebase-merge")) - (list - ;; The commit we're rebasing onto, i.e. git rebase -i - (magit-name-rev (car (magit-file-lines (concat git-dir "rebase-merge/onto")))) - - ;; How many commits we've gone through - (length (magit-file-lines (concat git-dir "rebase-merge/done"))) - - ;; How many commits we have in total, without the comments - ;; at the end of git-rebase-todo.backup - (let ((todo-lines-with-comments (magit-file-lines (concat git-dir "rebase-merge/git-rebase-todo.backup")))) - (loop for i in todo-lines-with-comments - until (string= "" i) - count i)))) - ((and (file-exists-p (concat git-dir "rebase-apply")) - (file-exists-p (concat git-dir "rebase-apply/onto"))) - ;; we might be here because a non-interactive rebase failed: the - ;; patches didn't apply cleanly - (list - ;; The commit we're rebasing onto, i.e. git rebase -i - (magit-name-rev (car (magit-file-lines (concat git-dir "rebase-apply/onto")))) - - ;; How many commits we've gone through - (- (string-to-number (car (magit-file-lines (concat git-dir "rebase-apply/next")))) 1) - - ;; How many commits we have in total - (string-to-number (car (magit-file-lines (concat git-dir "rebase-apply/last")))) - )) - (t nil)))) - -(defun magit-rebase-step () - (interactive) - (let ((info (magit-rebase-info))) - (if (not info) - (let* ((current-branch (magit-get-current-branch)) - (rev (magit-read-rev "Rebase to" - (magit-format-ref (magit-remote-branch-for current-branch t)) - (if current-branch - (cons (concat "refs/heads/" current-branch) - magit-uninteresting-refs) - magit-uninteresting-refs)))) - (if rev - (magit-run-git "rebase" (magit-rev-to-git rev)))) - (let ((cursor-in-echo-area t) - (message-log-max nil)) - (message "Rebase in progress. [A]bort, [S]kip, or [C]ontinue? ") - (let ((reply (read-event))) - (case reply - ((?A ?a) - (magit-run-git-async "rebase" "--abort")) - ((?S ?s) - (magit-run-git-async "rebase" "--skip")) - ((?C ?c) - (magit-run-git-async "rebase" "--continue")))))))) - -;;; Resetting - -(magit-define-command 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-default-rev) - "HEAD^")) - current-prefix-arg)) - (when revision - (magit-run-git "reset" (if hard "--hard" "--soft") - (magit-rev-to-git revision)) - (magit-update-vc-modeline default-directory))) - -(magit-define-command 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-default-rev) - "HEAD")))) - (magit-reset-head revision t)) - -(magit-define-command 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") - (if include-untracked - (magit-run-git "clean" "-fd" (if include-ignored - "-x" - "")))))) - -;;; Rewriting - -(defun magit-read-rewrite-info () - (when (file-exists-p (concat (magit-git-dir) "magit-rewrite-info")) - (with-temp-buffer - (insert-file-contents (concat (magit-git-dir) "magit-rewrite-info")) - (goto-char (point-min)) - (read (current-buffer))))) - -(defun magit-write-rewrite-info (info) - (with-temp-file (concat (magit-git-dir) "magit-rewrite-info") - (prin1 info (current-buffer)) - (princ "\n" (current-buffer)))) - -(magit-define-inserter pending-commits () - (let* ((info (magit-read-rewrite-info)) - (pending (cdr (assq 'pending info)))) - (when pending - (magit-with-section 'pending nil - (insert (propertize "Pending commits:\n" - 'face 'magit-section-title)) - (dolist (p pending) - (let* ((commit (car p)) - (properties (cdr p)) - (used (plist-get properties 'used))) - (magit-with-section commit 'commit - (magit-set-section-info commit) - (insert (magit-git-string - "log" "--max-count=1" - (if used - "--pretty=format:. %s" - "--pretty=format:* %s") - commit "--") - "\n"))))) - (insert "\n")))) - -(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-need-refresh)))) - -(defun magit-rewrite-set-used () - (interactive) - (magit-section-action (item info) - ((pending commit) - (magit-rewrite-set-commit-property info 'used t)))) - -(defun magit-rewrite-set-unused () - (interactive) - (magit-section-action (item info) - ((pending commit) - (magit-rewrite-set-commit-property info 'used nil)))) - -(magit-define-inserter pending-changes () - (let* ((info (magit-read-rewrite-info)) - (orig (cadr (assq 'orig info)))) - (when orig - (let ((magit-hide-diffs t)) - (magit-git-section 'pending-changes - "Pending changes" - 'magit-wash-diffs - "diff" (magit-diff-U-arg) "-R" orig))))) - -(defun magit-rewrite-start (from &optional onto) - (interactive (list (magit-read-rev "Rewrite from" (magit-default-rev)))) - (or (magit-everything-clean-p) - (error "You have uncommitted changes")) - (or (not (magit-read-rewrite-info)) - (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)) - (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 - (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 - (error "No rewrite in progress")) - (or (magit-everything-clean-p) - (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-with-refresh - (magit-rewrite-finish-step t))) - -(defun magit-rewrite-finish-step (first-p) - (let ((info (magit-read-rewrite-info))) - (or info - (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-apply-commit commit t (not first-p)) - (magit-rewrite-set-commit-property commit 'used t) - (magit-rewrite-finish-step nil)))))) - -;;; Updating, pull, and push - -(magit-define-command fetch (remote) - "Fetch from REMOTE." - (interactive (list (magit-read-remote))) - (apply 'magit-run-git-async "fetch" remote magit-custom-options)) - -(magit-define-command 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)))) - -(magit-define-command remote-update () - "Update all remotes." - (interactive) - (apply 'magit-run-git-async "remote" "update" magit-custom-options)) - -(magit-define-command pull () - "Run git pull against the current remote." - (interactive) - (let* ((branch (magit-get-current-branch)) - (branch-remote (magit-get-remote branch)) - (config-branch (and branch (magit-get "branch" branch "merge"))) - (merge-branch (or (and config-branch (not current-prefix-arg)) - (magit-read-remote-branch - branch-remote (format "Pull from: "))))) - (when (and branch (not config-branch)) - (magit-set branch-remote "branch" branch "remote") - (magit-set (format "refs/heads/%s" merge-branch) - "branch" branch "merge")) - (apply 'magit-run-git-async "pull" "-v" magit-custom-options))) - -(eval-when-compile (require 'eshell)) - -(defun magit-parse-arguments (command) - (require 'eshell) - (with-temp-buffer - (insert command) - (mapcar 'eval (eshell-parse-arguments (point-min) (point-max))))) - -(defun magit-shell-command (command) - "Perform arbitrary shell COMMAND." - (interactive "sCommand: ") - (let ((args (magit-parse-arguments command)) - (magit-process-popup-time 0)) - (magit-run* args nil nil nil t))) - -(defvar magit-git-command-history nil) - -(defun magit-git-command (command) - "Perform arbitrary Git COMMAND. - -Similar to `magit-shell-command', but involves slightly less -typing and automatically refreshes the status buffer." - (interactive - (list (read-string "Run git like this: " nil 'magit-git-command-history))) - (require 'pcomplete) - (let ((args (magit-parse-arguments command)) - (magit-process-popup-time 0)) - (magit-with-refresh - (magit-run* (append (cons magit-git-executable - magit-git-standard-options) - args) - nil nil nil t)))) - -(magit-define-command push-tags () - "Push tags." - (interactive) - (magit-run-git-async "push" "--tags")) - -(magit-define-command push () - "Push the current branch to a remote repository. - -With no prefix argument, ask `magit-get-remote' what remote to -use for this branch. - -With a prefix arg \(e.g., \\[universal-argument] \\[magit-push]), \ -ask user instead. - -With \\[universal-argument] \\[universal-argument] as prefix, \ -also prompt user for the remote branch; -otherwise, try to use the branch..merge git-config(1) -option, falling back to something hairy if that is unset." - (interactive) - (let* ((branch (or (magit-get-current-branch) - (error "Don't push a detached head. That's gross"))) - (branch-remote (magit-get-remote branch)) - (push-remote (if (or current-prefix-arg - (not branch-remote)) - (magit-read-remote (format "Push %s to remote" - branch) - branch-remote) - branch-remote)) - (ref-branch (or (and (>= (prefix-numeric-value current-prefix-arg) 16) - (magit-read-remote-branch - push-remote (format "Push %s as branch" branch))) - (magit-get "branch" branch "merge")))) - (if (and (not ref-branch) - (eq magit-set-upstream-on-push 'refuse)) - (error "Not pushing since no upstream has been set.") - (let ((set-upstream-on-push (and (not ref-branch) - (or (eq magit-set-upstream-on-push 'dontask) - (and (eq magit-set-upstream-on-push t) - (yes-or-no-p "Set upstream while pushing? ")))))) - (if (and (not branch-remote) - (not current-prefix-arg)) - (magit-set push-remote "branch" branch "remote")) - (apply 'magit-run-git-async "push" "-v" push-remote - (if ref-branch - (format "%s:%s" branch ref-branch) - branch) - (if set-upstream-on-push - (cons "--set-upstream" magit-custom-options) - magit-custom-options)) - ;; Although git will automatically set up the remote, - ;; it doesn't set up the branch to merge (at least as of Git 1.6.6.1), - ;; so we have to do that manually. - (unless ref-branch - (magit-set (concat "refs/heads/" branch) "branch" branch "merge")))))) - -;;; Log edit mode - -(defvar magit-log-edit-buffer-name "*magit-edit-log*" - "Buffer name for composing commit messages.") - -(defvar magit-log-edit-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-c") 'magit-log-edit-commit) - (define-key map (kbd "C-x #") 'magit-log-edit-commit) - (define-key map (kbd "C-c C-a") 'magit-log-edit-toggle-amending) - (define-key map (kbd "C-c C-s") 'magit-log-edit-toggle-signoff) - (define-key map (kbd "C-c C-t") 'magit-log-edit-toggle-author) - (define-key map (kbd "C-c C-e") 'magit-log-edit-toggle-allow-empty) - (define-key map (kbd "M-p") 'log-edit-previous-comment) - (define-key map (kbd "M-n") 'log-edit-next-comment) - (define-key map (kbd "C-c C-k") 'magit-log-edit-cancel-log-message) - (define-key map (kbd "C-c C-]") 'magit-log-edit-cancel-log-message) - (define-key map (kbd "C-x C-s") (lambda () - (interactive) - (message "Not saved. Use C-c C-c to finalize this commit message."))) - map)) - -(defvar magit-pre-log-edit-window-configuration nil) - -(define-derived-mode magit-log-edit-mode text-mode "Magit Log Edit" - ;; Recognize changelog-style paragraphs - (set (make-local-variable 'paragraph-start) - (concat paragraph-start "\\|*\\|("))) - -(defun magit-log-edit-cleanup () - (save-excursion - (goto-char (point-min)) - (goto-char (point-min)) - (if (re-search-forward "[ \t\n]*\\'" nil t) - (replace-match "\n" nil nil)))) - -(defun magit-log-edit-append (str) - (with-current-buffer (get-buffer-create magit-log-edit-buffer-name) - (goto-char (point-max)) - (insert str "\n"))) - -(defconst magit-log-header-end "-- End of Magit header --\n") - -(defun magit-log-edit-get-fields () - (let ((buf (get-buffer magit-log-edit-buffer-name)) - (result nil)) - (if buf - (with-current-buffer buf - (goto-char (point-min)) - (while (looking-at "^\\([A-Za-z0-9-_]+\\): *\\(.+\\)?$") - (setq result (acons (intern (downcase (match-string 1))) - (read (or (match-string 2) "nil")) - result)) - (forward-line)) - (if (not (looking-at (regexp-quote magit-log-header-end))) - (setq result nil)))) - (nreverse result))) - -(defun magit-log-edit-set-fields (fields) - (let ((buf (get-buffer-create magit-log-edit-buffer-name))) - (with-current-buffer buf - (goto-char (point-min)) - (if (search-forward-regexp (format "^\\([A-Za-z0-9-_]+:.*\n\\)*%s" - (regexp-quote magit-log-header-end)) - nil t) - (delete-region (match-beginning 0) (match-end 0))) - (goto-char (point-min)) - (when fields - (while fields - (insert (capitalize (symbol-name (caar fields))) ": " - (prin1-to-string (cdar fields)) "\n") - (setq fields (cdr fields))) - (insert magit-log-header-end))))) - -(defun magit-log-edit-set-field (name value) - (let* ((fields (magit-log-edit-get-fields)) - (cell (assq name fields))) - (cond (cell - (if value - (rplacd cell value) - (setq fields (delq cell fields)))) - (t - (if value - (setq fields (append fields (list (cons name value))))))) - (magit-log-edit-set-fields fields))) - -(defun magit-log-edit-get-field (name) - (cdr (assq name (magit-log-edit-get-fields)))) - -(defun magit-log-edit-toggle-field (name default) - "Toggle the log-edit field named NAME. -If it's currently unset, set it to DEFAULT (t or nil). - -Return nil if the field is toggled off, and non-nil if it's -toggled on. When it's toggled on for the first time, return -'first." - (let* ((fields (magit-log-edit-get-fields)) - (cell (assq name fields)) yesp) - (if cell - (progn - (setq yesp (equal (cdr cell) "yes")) - (rplacd cell (if yesp "no" "yes"))) - (setq fields (acons name (if default "yes" "no") fields)) - (setq yesp (if default 'first))) - (magit-log-edit-set-fields fields) - yesp)) - -(defun magit-log-edit-toggle-input (name default) - "Toggle the log-edit input named NAME. -If it's currently unset, set it to DEFAULT (a string). If it is -set remove it. - -Return nil if the input is toggled off, and its valud if it's -toggled on." - (let* ((fields (magit-log-edit-get-fields)) - (cell (assq name fields)) - result) - (if cell - (progn - (setq fields (assq-delete-all name fields) - result (cdr cell))) - (setq fields (acons name default fields))) - (magit-log-edit-set-fields fields) - result)) - -(defun magit-log-edit-setup-author-env (author) - "Set GIT_AUTHOR_* variables from AUTHOR spec. -If AUTHOR is nil, honor default values from -environment (potentially empty)." - (when author - ;; XXX - this is a bit strict, probably. - (or (string-match "\\(.*\\) <\\(.*\\)>\\(?:,\\s-*\\(.+\\)\\)?" author) - (error "Can't parse author string")) - ;; Shucks, setenv destroys the match data. - (let ((name (match-string 1 author)) - (email (match-string 2 author)) - (date (match-string 3 author))) - (make-local-variable 'process-environment) - (setenv "GIT_AUTHOR_NAME" name) - (setenv "GIT_AUTHOR_EMAIL" email) - (if date - (setenv "GIT_AUTHOR_DATE" date))))) - -(defun magit-log-edit-push-to-comment-ring (comment) - (when (or (ring-empty-p log-edit-comment-ring) - (not (equal comment (ring-ref log-edit-comment-ring 0)))) - (ring-insert log-edit-comment-ring comment))) - -(defun magit-log-edit-commit () - "Finish edits and create new commit object. -\('git commit ...')" - (interactive) - (let* ((fields (magit-log-edit-get-fields)) - (amend (equal (cdr (assq 'amend fields)) "yes")) - (allow-empty (equal (cdr (assq 'allow-empty fields)) "yes")) - (commit-all (equal (cdr (assq 'commit-all fields)) "yes")) - (sign-off-field (assq 'sign-off fields)) - (sign-off (if sign-off-field - (equal (cdr sign-off-field) "yes") - magit-commit-signoff)) - (tag-rev (cdr (assq 'tag-rev fields))) - (tag-name (cdr (assq 'tag-name fields))) - (author (cdr (assq 'author fields))) - (tag-options (cdr (assq 'tag-options fields)))) - - (unless (or (magit-anything-staged-p) - allow-empty - amend - tag-name - (file-exists-p (concat (magit-git-dir) "MERGE_HEAD")) - (and commit-all - (not (magit-everything-clean-p)))) - (error "Refusing to create empty commit. Maybe you want to amend (%s) or allow-empty (%s)?" - (key-description (car (where-is-internal - 'magit-log-edit-toggle-amending))) - (key-description (car (where-is-internal - 'magit-log-edit-toggle-allow-empty))))) - - (magit-log-edit-push-to-comment-ring (buffer-string)) - (magit-log-edit-setup-author-env author) - (magit-log-edit-set-fields nil) - (magit-log-edit-cleanup) - (if (= (buffer-size) 0) - (insert "(Empty description)\n")) - (let ((env process-environment) - (commit-buf (current-buffer))) - (with-current-buffer (magit-find-status-buffer default-directory) - (let ((process-environment env)) - (cond (tag-name - (apply #'magit-run-git-with-input commit-buf - "tag" (append tag-options (list tag-name "-a" "-F" "-" tag-rev)))) - (t - (apply #'magit-run-async-with-input commit-buf - magit-git-executable - (append magit-git-standard-options - '("commit") - magit-custom-options - '("-F" "-") - (if (and commit-all (not allow-empty)) '("--all") '()) - (if amend '("--amend") '()) - (if allow-empty '("--allow-empty")) - (if sign-off '("--signoff") '())))))))) - ;; shouldn't we kill that buffer altogether? - (erase-buffer) - (let ((magit-buf magit-buffer-internal)) - (bury-buffer) - (set-buffer magit-buf)) - (when (file-exists-p (concat (magit-git-dir) "MERGE_MSG")) - (delete-file (concat (magit-git-dir) "MERGE_MSG"))) - ;; potentially the local environment has been altered with settings that - ;; were specific to this commit. Let's revert it - (kill-local-variable 'process-environment) - (magit-update-vc-modeline default-directory) - (when magit-pre-log-edit-window-configuration - (set-window-configuration magit-pre-log-edit-window-configuration) - (setq magit-pre-log-edit-window-configuration nil)))) - -(defun magit-log-edit-cancel-log-message () - "Abort edits and erase commit message being composed." - (interactive) - (when (or (not magit-log-edit-confirm-cancellation) - (yes-or-no-p - "Really cancel editing the log (any changes will be lost)?")) - (erase-buffer) - (bury-buffer) - (when magit-pre-log-edit-window-configuration - (set-window-configuration magit-pre-log-edit-window-configuration) - (setq magit-pre-log-edit-window-configuration nil)))) - -(defun magit-log-edit-toggle-amending () - "Toggle whether this will be an amendment to the previous commit. -\(i.e., whether eventual commit does 'git commit --amend')" - (interactive) - (when (eq (magit-log-edit-toggle-field 'amend t) 'first) - (magit-log-edit-append - (magit-trim-line (magit-format-commit "HEAD" "%s%n%n%b"))))) - -(defun magit-log-edit-toggle-signoff () - "Toggle whether this commit will include a signoff. -\(i.e., whether eventual commit does 'git commit --signoff')" - (interactive) - (magit-log-edit-toggle-field 'sign-off (not magit-commit-signoff))) - -(defun magit-log-edit-toggle-author () - "Toggle whether this commit will include an author. -\(i.e., whether eventual commit is run with GIT_AUTHOR_NAME and -GIT_AUTHOR_EMAIL set)" - (interactive) - (magit-log-edit-toggle-input 'author (format "%s <%s>" - (or (magit-get "user" "name") "Author Name") - (or (magit-get "user" "email") "author@email")))) - -(defun magit-log-edit-toggle-allow-empty () - "Toggle whether this commit is allowed to be empty. -This means that the eventual commit does 'git commit --allow-empty'." - (interactive) - (magit-log-edit-toggle-field 'allow-empty t)) - -(defun magit-pop-to-log-edit (operation) - (let ((dir default-directory) - (magit-buf (current-buffer)) - (buf (get-buffer-create magit-log-edit-buffer-name))) - (setq magit-pre-log-edit-window-configuration - (current-window-configuration)) - (pop-to-buffer buf) - (setq default-directory dir) - (when (file-exists-p (concat (magit-git-dir) "MERGE_MSG")) - (insert-file-contents (concat (magit-git-dir) "MERGE_MSG"))) - (magit-log-edit-mode) - (make-local-variable 'magit-buffer-internal) - (setq magit-buffer-internal magit-buf) - (message "Type C-c C-c to %s (C-c C-k to cancel)." operation))) - -(defun magit-log-edit (&optional arg) - "Brings up a buffer to allow editing of commit messages. - -Giving a simple prefix arg will amend a previous commit, while -a double prefix arg will allow creating an empty one. - -If there is a rebase in progress, offer the user the option to -continue it. - -\\{magit-log-edit-mode-map}" - (interactive "P") - ;; If repository is dirty there is no point in trying to - ;; suggest to continue the rebase. Git will rebuke you and exit with - ;; error code, so suggest it only if theres absolutely nothing else - ;; to do and rebase is ongoing. - (if (and (magit-everything-clean-p) - (magit-rebase-info) - (y-or-n-p "Rebase in progress. Continue it? ")) - (magit-run-git-async "rebase" "--continue") - - ;; If there's nothing staged, set commit flag to `nil', thus - ;; avoiding unnescessary popping up of the log edit buffer in case - ;; when user chose to forgo commiting all unstaged changes - (let ((amend-p (= (prefix-numeric-value arg) 4)) - (empty-p (= (prefix-numeric-value arg) 16))) - (when (and magit-commit-all-when-nothing-staged - (not (magit-everything-clean-p)) - (not (magit-anything-staged-p))) - (cond ((eq magit-commit-all-when-nothing-staged 'ask-stage) - (when (y-or-n-p "Nothing staged. Stage everything now? ") - (magit-stage-all))) - ((not (magit-log-edit-get-field 'commit-all)) - (when (or (eq magit-commit-all-when-nothing-staged t) - (y-or-n-p - "Nothing staged. Commit all unstaged changes? ")) - (magit-log-edit-set-field 'commit-all "yes"))))) - (when amend-p - (magit-log-edit-toggle-amending)) - (when empty-p - (magit-log-edit-toggle-allow-empty)) - (let ((author-email (or (getenv "GIT_AUTHOR_EMAIL") "")) - (author-name (or (getenv "GIT_AUTHOR_NAME") "")) - (author-date (or (getenv "GIT_AUTHOR_DATE") ""))) - (if (not (string= author-email "")) - (magit-log-edit-set-field 'author (format "%s <%s>%s" - (if (string= "" author-name) author-email author-name) - author-email - (if (string= "" author-date) "" (format ", %s" author-date)))))) - (magit-pop-to-log-edit "commit")))) - -(defun magit-add-log () - (interactive) - (cond ((magit-rebase-info) - (if (y-or-n-p "Rebase in progress. Continue it? ") - (magit-run-git-async "rebase" "--continue"))) - (t - (let ((section (magit-current-section))) - (let ((fun (if (eq (magit-section-type section) 'hunk) - (save-window-excursion - (save-excursion - (magit-visit-item) - (add-log-current-defun))) - nil)) - (file (magit-diff-item-file - (cond ((eq (magit-section-type section) 'hunk) - (magit-hunk-item-diff section)) - ((eq (magit-section-type section) 'diff) - section) - (t - (error "No change at point")))))) - (magit-log-edit nil) - (goto-char (point-min)) - (cond ((not (search-forward-regexp - (format "^\\* %s" (regexp-quote file)) nil t)) - ;; No entry for file, create it. - (goto-char (point-max)) - (insert (format "\n* %s" file)) - (if fun - (insert (format " (%s)" fun))) - (insert ": ")) - (fun - ;; found entry for file, look for fun - (let ((limit (or (save-excursion - (and (search-forward-regexp "^\\* " - nil t) - (match-beginning 0))) - (point-max)))) - (cond ((search-forward-regexp (format "(.*\\<%s\\>.*):" - (regexp-quote fun)) - limit t) - ;; found it, goto end of current entry - (if (search-forward-regexp "^(" 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))))))))) - -;;; Tags - -(magit-define-command tag (name rev) - "Create a new lightweight tag with the given NAME at REV. -\('git tag NAME')." - (interactive - (list - (read-string "Tag name: ") - (magit-read-rev "Place tag on: " (or (magit-default-rev) "HEAD")))) - (apply #'magit-run-git "tag" (append magit-custom-options (list name rev)))) - -(magit-define-command annotated-tag (name rev) - "Start composing an annotated tag with the given NAME. -Tag will point to the current 'HEAD'." - (interactive - (list - (read-string "Tag name: ") - (magit-read-rev "Place tag on: " (or (magit-default-rev) "HEAD")))) - (magit-log-edit-set-field 'tag-name name) - (magit-log-edit-set-field 'tag-rev rev) - (magit-log-edit-set-field 'tag-options magit-custom-options) - (magit-pop-to-log-edit "tag")) - -;;; Stashing - -(defun magit-wash-stash () - (if (search-forward-regexp "stash@{\\(.*?\\)}" (line-end-position) t) - (let ((stash (match-string-no-properties 0)) - (name (match-string-no-properties 1))) - (delete-region (match-beginning 0) (match-end 0)) - (goto-char (match-beginning 0)) - (fixup-whitespace) - (goto-char (line-beginning-position)) - (insert name) - (goto-char (line-beginning-position)) - (magit-with-section stash 'stash - (magit-set-section-info stash) - (forward-line))) - (forward-line)) - t) - -(defun magit-wash-stashes () - (let ((magit-old-top-section nil)) - (magit-wash-sequence #'magit-wash-stash))) - -(magit-define-inserter stashes () - (magit-git-section 'stashes - "Stashes:" 'magit-wash-stashes - "stash" "list")) - -(magit-define-command 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 "sStash description: ") - (apply 'magit-run-git `("stash" "save" ,@magit-custom-options "--" ,description))) - -(magit-define-command 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-with-refresh - (apply 'magit-run-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}"))) - -(defvar magit-currently-shown-stash nil) - -(define-derived-mode magit-stash-mode magit-mode "Magit Stash" - "Mode for looking at a git stash. - -\\{magit-stash-mode-map}" - :group 'magit) - -(defvar magit-stash-buffer-name "*magit-stash*" - "Buffer name for displaying a stash.") - -(defun magit-show-stash (stash &optional scroll) - (when (magit-section-p stash) - (setq stash (magit-section-info stash))) - (let ((dir default-directory) - (buf (get-buffer-create magit-stash-buffer-name)) - (stash-id (magit-git-string "rev-list" "-1" stash))) - (cond ((and (equal magit-currently-shown-stash stash-id) - (with-current-buffer buf - (> (length (buffer-string)) 1))) - (let ((win (get-buffer-window buf))) - (cond ((not win) - (display-buffer buf)) - (scroll - (with-selected-window win - (funcall scroll)))))) - (t - (setq magit-currently-shown-stash stash-id) - (display-buffer buf) - (with-current-buffer buf - (set-buffer buf) - (goto-char (point-min)) - (let* ((range (cons (concat stash "^2^") stash)) - (magit-current-diff-range range) - (args (magit-rev-range-to-git range))) - (magit-mode-init dir 'magit-diff-mode #'magit-refresh-diff-buffer - range args))))))) -;;; Commits - -(defun magit-commit-at-point (&optional nil-ok-p) - (let* ((section (magit-current-section)) - (commit (if (and section - (eq (magit-section-type section) 'commit)) - (magit-section-info section) - (get-text-property (point) 'revision)))) - (if nil-ok-p - commit - (or commit - (error "No commit at point"))))) - -(defun magit-apply-commit (commit &optional docommit noerase revert) - (let* ((parent-id (magit-choose-parent-id commit "cherry-pick")) - (success (magit-run* `(,magit-git-executable - ,@magit-git-standard-options - ,(if revert "revert" "cherry-pick") - ,@(if parent-id - (list "-m" (number-to-string parent-id))) - ,@(if (not docommit) (list "--no-commit")) - ,commit) - nil noerase))) - (when (and (not docommit) success) - (cond (revert - (magit-log-edit-append - (magit-format-commit commit "Reverting \"%s\""))) - (t - (magit-log-edit-append - (magit-format-commit commit "%s%n%n%b")) - (magit-log-edit-set-field - 'author - (magit-format-commit commit "%an <%ae>, %ai"))))) - success)) - -(defun magit-apply-item () - (interactive) - (magit-section-action (item info "apply") - ((pending commit) - (magit-apply-commit info) - (magit-rewrite-set-commit-property info 'used t)) - ((commit) - (magit-apply-commit info)) - ((unstaged *) - (error "Change is already in your working tree")) - ((staged *) - (error "Change is already in your working tree")) - ((hunk) - (magit-apply-hunk-item item)) - ((diff) - (magit-apply-diff-item item)) - ((stash) - (magit-run-git "stash" "apply" info)))) - -(defun magit-cherry-pick-item () - (interactive) - (magit-section-action (item info "cherry-pick") - ((pending commit) - (magit-apply-commit info t) - (magit-rewrite-set-commit-property info 'used t)) - ((commit) - (magit-apply-commit info t)) - ((stash) - (magit-run-git "stash" "pop" info)))) - -(defmacro magit-with-revert-confirmation (&rest body) - `(when (or (not magit-revert-item-confirm) - (yes-or-no-p "Really revert this item? ")) - ,@body)) - -(defun magit-revert-item () - (interactive) - (magit-section-action (item info "revert") - ((pending commit) - (magit-with-revert-confirmation - (magit-apply-commit info nil nil t) - (magit-rewrite-set-commit-property info 'used nil))) - ((commit) - (magit-with-revert-confirmation - (magit-apply-commit info nil nil t))) - ;; Reverting unstaged changes cannot be undone - ((unstaged *) - (magit-discard-item)) - ((hunk) - (magit-with-revert-confirmation - (magit-apply-hunk-item-reverse item))) - ((diff) - (magit-with-revert-confirmation - (magit-apply-diff-item item "--reverse"))))) - -(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") - (make-local-variable 'magit-log-cutoff-length) - (cond - ((numberp arg) - (setq magit-log-cutoff-length (+ magit-log-cutoff-length arg))) - (arg - (setq magit-log-cutoff-length magit-log-infinite-length)) - (t (setq magit-log-cutoff-length (* magit-log-cutoff-length 2)))) - (let ((old-point (point))) - (magit-refresh) - (goto-char old-point))) - -(defun magit-refresh-log-buffer (range style args) - (magit-configure-have-graph) - (magit-configure-have-decorate) - (magit-configure-have-abbrev) - (setq magit-current-range range) - (magit-create-log-buffer-sections - (apply #'magit-git-section nil - (magit-rev-range-describe range "Commits") - (apply-partially 'magit-wash-log style) - `("log" - ,(format "--max-count=%s" magit-log-cutoff-length) - ,"--abbrev-commit" - ,(format "--abbrev=%s" magit-sha1-abbrev-length) - ,@(cond ((eq style 'long) (list "--stat" "-z")) - ((eq style 'oneline) (list "--pretty=oneline")) - (t nil)) - ,@(if magit-have-decorate (list "--decorate=full")) - ,@(if magit-have-graph (list "--graph")) - ,@args - "--")))) - -(define-derived-mode magit-log-mode magit-mode "Magit Log" - "Mode for looking at git log. - -\\{magit-log-mode-map}" - :group 'magit) - -(defvar magit-log-buffer-name "*magit-log*" - "Buffer name for display of log entries.") - -(magit-define-command log-ranged () - (interactive) - (magit-log t)) -(define-obsolete-function-alias 'magit-display-log-ranged 'magit-log-ranged) - -(magit-define-command log (&optional ask-for-range &rest extra-args) - (interactive) - (let* ((log-range (if ask-for-range - (magit-read-rev-range "Log" "HEAD") - "HEAD")) - (topdir (magit-get-top-dir default-directory)) - (args (nconc (list (magit-rev-range-to-git log-range)) - magit-custom-options - extra-args))) - (magit-buffer-switch magit-log-buffer-name) - (magit-mode-init topdir 'magit-log-mode #'magit-refresh-log-buffer log-range - 'oneline args))) - -(define-obsolete-function-alias 'magit-display-log 'magit-log) - -(magit-define-command log-long-ranged () - (interactive) - (magit-log-long t)) - -(magit-define-command log-long (&optional ranged) - (interactive) - (let* ((range (if ranged - (magit-read-rev-range "Long log" "HEAD") - "HEAD")) - (topdir (magit-get-top-dir default-directory)) - (args (append (list (magit-rev-range-to-git range)) - magit-custom-options))) - (magit-buffer-switch magit-log-buffer-name) - (magit-mode-init topdir 'magit-log-mode #'magit-refresh-log-buffer range - 'long args))) - -;;; Reflog - -(defvar magit-reflog-head nil - "The HEAD of the reflog in the current buffer. -This is only non-nil in reflog buffers.") -(make-variable-buffer-local 'magit-reflog-head) - -(defun magit-refresh-reflog-buffer (head args) - (setq magit-reflog-head head) - (magit-create-log-buffer-sections - (apply #'magit-git-section - 'reflog (format "Local history of head %s" head) 'magit-wash-log "log" - (append magit-git-log-options - (list - "--walk-reflogs" - (format "--max-count=%s" magit-log-cutoff-length) - args))))) - -(define-derived-mode magit-reflog-mode magit-log-mode "Magit Reflog" - "Mode for looking at git reflog. - -\\{magit-reflog-mode-map}" - :group 'magit) - -(magit-define-command reflog (&optional ask-for-range) - (interactive) - (let ((at (or (if ask-for-range - (magit-read-rev "Reflog of" (or (magit-guess-branch) "HEAD"))) - "HEAD"))) - (let* ((topdir (magit-get-top-dir default-directory)) - (args (magit-rev-to-git at))) - (magit-buffer-switch "*magit-reflog*") - (magit-mode-init topdir 'magit-reflog-mode - #'magit-refresh-reflog-buffer at args)))) - -(magit-define-command reflog-ranged () - (interactive) - (magit-reflog t)) - -;;; Diffing - -(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 (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))) - (if (eq 'hunk (magit-section-type diff)) - (setq diff (magit-section-parent diff))) - (unless (eq 'diff (magit-section-type diff)) - (error "No diff at this location")) - (let* ((type (magit-diff-item-kind diff)) - (file1 (magit-diff-item-file diff)) - (file2 (magit-diff-item-file2 diff)) - (range (magit-diff-item-range diff))) - (cond - ((memq type '(new deleted typechange)) - (message "Why ediff a %s file?" type)) - ((and (eq type 'unmerged) - (eq (cdr range) 'working)) - (magit-interactive-resolve file1)) - ((consp (car range)) - (magit-ediff* (magit-show (caar range) file2) - (magit-show (cdar range) file2) - (magit-show (cdr range) file1))) - (t - (magit-ediff* (magit-show (car range) file2) - (magit-show (cdr range) file1))))))) - -(defun magit-ediff* (a b &optional c) - (setq magit-ediff-buffers (list a b c)) - (setq magit-ediff-windows (current-window-configuration)) - (add-hook 'ediff-quit-hook 'magit-ediff-restore 'append) - (if c - (ediff-buffers3 a b c) - (ediff-buffers a b))) - -(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) - (if (and (null (buffer-file-name buffer)) - (buffer-live-p buffer)) - (kill-buffer buffer))) - (setq magit-ediff-buffers nil) - (set-window-configuration magit-ediff-windows) - (remove-hook 'ediff-quit-hook 'magit-ediff-restore)) - -(defun magit-refresh-diff-buffer (range args) - (let ((magit-current-diff-range (cond - ((stringp range) - (cons range 'working)) - ((null (cdr range)) - (cons (car range) 'working)) - (t - range)))) - (setq magit-current-range range) - (magit-create-buffer-sections - (magit-git-section 'diffbuf - (magit-rev-range-describe range "Changes") - 'magit-wash-diffs - "diff" (magit-diff-U-arg) args "--")))) - -(define-derived-mode magit-diff-mode magit-mode "Magit Diff" - "Mode for looking at a git diff. - -\\{magit-diff-mode-map}" - :group 'magit) - -(magit-define-command diff (range) - (interactive (list (magit-read-rev-range "Diff"))) - (if range - (let* ((dir default-directory) - (args (magit-rev-range-to-git range)) - (buf (get-buffer-create "*magit-diff*"))) - (display-buffer buf) - (with-current-buffer buf - (magit-mode-init dir 'magit-diff-mode #'magit-refresh-diff-buffer range args))))) - -(magit-define-command diff-working-tree (rev) - (interactive (list (magit-read-rev "Diff with" (magit-default-rev)))) - (magit-diff (or rev "HEAD"))) - -(defun magit-diff-with-mark () - (interactive) - (magit-diff (cons (magit-marked-commit) - (magit-commit-at-point)))) - -;;; Wazzup - -(defvar magit-wazzup-head nil - "The integration head for the current wazzup buffer. -This is only non-nil in wazzup buffers.") -(make-variable-buffer-local 'magit-wazzup-head) - -(defvar magit-wazzup-all-p nil - "Non-nil if the current wazzup buffer displays excluded branches. -This is only meaningful in wazzup buffers.") -(make-variable-buffer-local 'magit-wazzup-all-p) - -(defun magit-wazzup-toggle-ignore (branch edit) - (let ((ignore-file (concat (magit-git-dir) "info/wazzup-exclude"))) - (if edit - (setq branch (read-string "Branch to ignore for wazzup: " branch))) - (let ((ignored (magit-file-lines ignore-file))) - (cond ((member branch ignored) - (when (or (not edit) - (y-or-n-p "Branch %s is already ignored. Unignore? ")) - (setq ignored (delete branch ignored)))) - (t - (setq ignored (append ignored (list branch))))) - (magit-write-file-lines ignore-file ignored) - (magit-need-refresh)))) - -(defun magit-refresh-wazzup-buffer (head all) - (setq magit-wazzup-head head) - (setq magit-wazzup-all-p all) - (let ((branch-desc (or head "(detached) HEAD"))) - (unless head (setq head "HEAD")) - (magit-create-buffer-sections - (magit-with-section 'wazzupbuf nil - (insert (format "Wazzup, %s\n\n" branch-desc)) - (let* ((excluded (magit-file-lines (concat (magit-git-dir) "info/wazzup-exclude"))) - (all-branches (magit-list-interesting-refs)) - (branches (if all all-branches - (delq nil (mapcar - (lambda (b) - (and (not - (member (cdr b) excluded)) - b)) - all-branches)))) - (reported (make-hash-table :test #'equal))) - (dolist (branch branches) - (let* ((name (car branch)) - (ref (cdr branch)) - (hash (magit-rev-parse ref)) - (reported-branch (gethash hash reported))) - (unless (or (and reported-branch - (string= (file-name-nondirectory ref) - reported-branch)) - (not (magit-git-string "merge-base" head ref))) - (puthash hash (file-name-nondirectory ref) reported) - (let* ((n (length (magit-git-lines "log" "--pretty=oneline" - (concat head ".." ref)))) - (section - (let ((magit-section-hidden-default t)) - (magit-git-section - (cons ref 'wazzup) - (format "%s unmerged commits in %s%s" - n name - (if (member ref excluded) - " (normally ignored)" - "")) - 'magit-wash-log - "log" - (format "--max-count=%s" magit-log-cutoff-length) - "--abbrev-commit" - (format "--abbrev=%s" magit-sha1-abbrev-length) - "--graph" - "--pretty=oneline" - (format "%s..%s" head ref) - "--")))) - (magit-set-section-info ref section)))))))))) - -(define-derived-mode magit-wazzup-mode magit-mode "Magit Wazzup" - "Mode for looking at commits that could be merged from other branches. - -\\{magit-wazzup-mode-map}" - :group 'magit) - -(defun magit-wazzup (&optional all) - (interactive "P") - (let ((topdir (magit-get-top-dir default-directory)) - (current-branch (magit-get-current-branch))) - (magit-buffer-switch "*magit-wazzup*") - (magit-mode-init topdir 'magit-wazzup-mode - #'magit-refresh-wazzup-buffer - current-branch all))) - -(defun magit-filename (filename) - "Return the path of FILENAME relative to its git repository. - -If FILENAME is absolute, return a path relative to the git -repository containing it. Otherwise, return a path relative to -the current git repository." - (let ((topdir (expand-file-name - (magit-get-top-dir (or (file-name-directory filename) - default-directory)))) - (file (file-truename filename))) - (when (and (not (string= topdir "")) - ;; FILE must start with the git repository path - (zerop (string-match-p (concat "\\`" topdir) file))) - (substring file (length topdir))))) - -;; This variable is used to keep track of the current file in the -;; *magit-log* buffer when this one is dedicated to showing the log of -;; just 1 file. -(defvar magit-file-log-file nil) -(make-variable-buffer-local 'magit-file-log-file) - -(defun magit-refresh-file-log-buffer (file range style) - "Refresh the current file-log buffer by calling git. - -FILE is the path of the file whose log must be displayed. - -`magit-current-range' will be set to the value of RANGE. - -STYLE controls the display. It is either `'long', `'oneline', or something else. - " - (magit-configure-have-graph) - (magit-configure-have-decorate) - (magit-configure-have-abbrev) - (setq magit-current-range range) - (setq magit-file-log-file file) - (magit-create-log-buffer-sections - (apply #'magit-git-section nil - (magit-rev-range-describe range (format "Commits for file %s" file)) - (apply-partially 'magit-wash-log style) - `("log" - ,(format "--max-count=%s" magit-log-cutoff-length) - ,"--abbrev-commit" - ,(format "--abbrev=%s" magit-sha1-abbrev-length) - ,@(cond ((eq style 'long) (list "--stat" "-z")) - ((eq style 'oneline) (list "--pretty=oneline")) - (t nil)) - ,@(if magit-have-decorate (list "--decorate=full")) - ,@(if magit-have-graph (list "--graph")) - "--" - ,file)))) - -(defun magit-file-log (&optional all) - "Display the log for the currently visited file or another one. - -With a prefix argument or if no file is currently visited, ask -for the file whose log must be displayed." - (interactive "P") - (let ((topdir (magit-get-top-dir default-directory)) - (current-file (magit-filename - (if (or current-prefix-arg (not buffer-file-name)) - (magit-read-file-from-rev (magit-get-current-branch)) - buffer-file-name))) - (range "HEAD")) - (magit-buffer-switch "*magit-log*") - (magit-mode-init topdir 'magit-log-mode - #'magit-refresh-file-log-buffer - current-file range 'oneline))) - -(defun magit-show-file-revision () - "Open a new buffer showing the current file in the revision at point." - (interactive) - (flet ((magit-show-file-from-diff (item) - (switch-to-buffer-other-window - (magit-show (cdr (magit-diff-item-range item)) - (magit-diff-item-file item))))) - (magit-section-action (item info "show") - ((commit) - (let ((current-file (or magit-file-log-file - (magit-read-file-from-rev info)))) - (switch-to-buffer-other-window - (magit-show info current-file)))) - ((hunk) (magit-show-file-from-diff (magit-hunk-item-diff item))) - ((diff) (magit-show-file-from-diff item))))) - -;;; Miscellaneous - -(defun magit-ignore-modifiable-file (file edit) - "Prompt the user for the filename to be added to git ignore. -\\ -The minibuffer's future history (accessible with \\[next-history-element]) -contains predefined values (such as wildcards) that might -be of interest. -The history and default value are derived from the filename FILE. -If EDIT argument is negative, the prompt proposes wildcard by default. -" - (let* ((just-extension (concat "*." (file-name-extension file))) - (full-extension (concat (file-name-directory file) just-extension)) - (just-file (file-name-nondirectory file)) - ;; change the order in history depending on the negativity of - ;; EDIT. - (history (if (< (prefix-numeric-value edit) 0) - (list full-extension just-extension file just-file) - (list file full-extension just-extension just-file)))) - (read-string - (format "File to ignore [%s]: " (car history)) - nil nil history))) - -(defun magit-ignore-file (file edit local) - "Add FILE to the list of files to ignore. -\\ -If EDIT is non-`nil', prompt the user for the filename to -be added to git ignore. In this case, the minibuffer's -future history (accessible with \\[next-history-element]) contains predefined -values (such as wildcards) that might be of interest. - -If LOCAL is nil, the `.gitignore' file is updated. -Otherwise, it is `.git/info/exclude'." - (let* ((local-ignore-dir (concat (magit-git-dir) "info/")) - (ignore-file (if local - (concat local-ignore-dir "exclude") - ".gitignore"))) - (if edit - (setq file (magit-ignore-modifiable-file file edit))) - (if (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)) - (magit-need-refresh))) - -(defun magit-ignore-item () - "Add FILE to the `.gitignore' list of files to ignore. -\\ -With a prefix argument, prompt the user for the filename to -be added. In this case, the minibuffer's future history -\(accessible with \\[next-history-element]) contains predefined values (such as -wildcards) that might be of interest. If prefix argument is -negative, the prompt proposes wildcard by default." - (interactive) - (magit-section-action (item info "ignore") - ((untracked file) - (magit-ignore-file (concat "/" info) current-prefix-arg nil)) - ((wazzup) - (magit-wazzup-toggle-ignore info current-prefix-arg)))) - -(defun magit-ignore-item-locally () - "Add FILE to the `.git/info/exclude' list of files to ignore. -\\ -With a prefix argument, prompt the user for the filename to -be added. In this case, the minibuffer's future history -(accessible with \\[next-history-element]) contains predefined values (such as -wildcards) that might be of interest. If prefix argument is -negative, the prompt proposes wildcard by default." - (interactive) - (magit-section-action (item info "ignore") - ((untracked file) - (magit-ignore-file (concat "/" info) current-prefix-arg t)))) - -(defun magit-discard-diff (diff stagedp) - (let ((kind (magit-diff-item-kind diff)) - (file (magit-diff-item-file diff))) - (cond ((eq kind 'deleted) - (when (yes-or-no-p (format "Resurrect %s? " file)) - (magit-run-git "reset" "-q" "--" file) - (magit-run-git "checkout" "--" file))) - ((eq kind 'new) - (if (yes-or-no-p (format "Delete %s? " file)) - (magit-run-git "rm" "-f" "--" file))) - (t - (if (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-discard-item () - (interactive) - (magit-section-action (item info "discard") - ((untracked file) - (when (yes-or-no-p (format "Delete %s? " info)) - (if (and (file-directory-p info) - (not (file-symlink-p info))) - (magit-delete-directory info 'recursive) - (delete-file info)) - (magit-refresh-buffer))) - ((untracked) - (if (yes-or-no-p "Delete all untracked files and directories? ") - (magit-run-git "clean" "-df"))) - ((unstaged diff hunk) - (when (yes-or-no-p (if (magit-use-region-p) - "Discard changes in region? " - "Discard hunk? ")) - (magit-apply-hunk-item-reverse item))) - ((staged diff hunk) - (if (magit-file-uptodate-p (magit-diff-item-file - (magit-hunk-item-diff item))) - (when (yes-or-no-p (if (magit-use-region-p) - "Discard changes in region? " - "Discard hunk? ")) - (magit-apply-hunk-item-reverse item "--index")) - (error "Can't discard this hunk. Please unstage it first"))) - ((unstaged diff) - (magit-discard-diff item nil)) - ((staged diff) - (if (magit-file-uptodate-p (magit-diff-item-file item)) - (magit-discard-diff item t) - (error "Can't discard staged changes to this file. Please unstage it first"))) - ((diff diff) - (save-excursion - (magit-goto-parent-section) - (magit-discard-item))) - ((diff diff hunk) - (save-excursion - (magit-goto-parent-section) - (magit-goto-parent-section) - (magit-discard-item))) - ((hunk) - (error "Can't discard this hunk")) - ((diff) - (error "Can't discard this diff")) - ((stash) - (when (yes-or-no-p "Discard stash? ") - (magit-run-git "stash" "drop" info))) - ((branch) - (when (yes-or-no-p (if current-prefix-arg - "Force delete branch?" - "Delete branch? ")) - (magit-delete-branch info current-prefix-arg))) - ((remote) - (when (yes-or-no-p "Remove remote? ") - (magit-remove-remote info))))) - -(defun magit-move-item () - (interactive) - (magit-section-action (item info "move") - ((branch) - (call-interactively 'magit-move-branch)) - ((remote) - (call-interactively 'magit-rename-remote)))) - -(defmacro magit-visiting-file-item (&rest body) - `(let ((marker (save-window-excursion - (magit-visit-file-item) - (set-marker (make-marker) (point))))) - (save-excursion - (with-current-buffer (marker-buffer marker) - (goto-char marker) - ,@body)))) - -(defun magit-add-change-log-entry-no-option (&optional other-window) - "Add a change log entry for current change. -With a prefix argument, edit in other window. -The name of the change log file is set by variable change-log-default-name." - (interactive "P") - (if other-window - (magit-visiting-file-item (add-change-log-entry-other-window)) - (magit-visiting-file-item (add-change-log-entry)))) - -(defun magit-add-change-log-entry-other-window () - (interactive) - (magit-visiting-file-item (call-interactively 'add-change-log-entry-other-window))) - -(eval-after-load 'dired-x - '(defun magit-dired-jump (&optional other-window) - "Visit current item. -With a prefix argument, visit in other window." - (interactive "P") - (require 'dired-x) - (magit-section-action (item info "dired-jump") - ((untracked file) - (dired-jump other-window (file-truename info))) - ((diff) - (dired-jump other-window (file-truename (magit-diff-item-file item)))) - ((hunk) - (dired-jump other-window - (file-truename (magit-diff-item-file - (magit-hunk-item-diff item)))))))) - -(defun magit-visit-file-item (&optional other-window) - "Visit current file associated with item. -With a prefix argument, visit in other window." - (interactive "P") - (magit-section-action (item info "visit-file") - ((untracked file) - (funcall - (if other-window 'find-file-other-window 'find-file) - info)) - ((diff) - (let ((file (magit-diff-item-file item))) - (cond ((not (file-exists-p file)) - (error "Can't visit deleted file: %s" file)) - ((file-directory-p file) - (magit-status file)) - (t - (funcall - (if other-window 'find-file-other-window 'find-file) - file))))) - ((hunk) - (let ((file (magit-diff-item-file (magit-hunk-item-diff item))) - (line (magit-hunk-item-target-line item))) - (if (not (file-exists-p file)) - (error "Can't visit deleted file: %s" file)) - (funcall - (if other-window 'find-file-other-window 'find-file) - file) - (goto-char (point-min)) - (forward-line (1- line)))))) - -(defun magit-visit-item (&optional other-window) - "Visit current item. -With a prefix argument, visit in other window." - (interactive "P") - (magit-section-action (item info "visit") - ((untracked file) - (call-interactively 'magit-visit-file-item)) - ((diff) - (call-interactively 'magit-visit-file-item)) - ((hunk) - (call-interactively 'magit-visit-file-item)) - ((commit) - (magit-show-commit info nil nil 'select)) - ((stash) - (magit-show-stash info) - (pop-to-buffer magit-stash-buffer-name)) - ((branch) - (magit-checkout info)) - ((longer) - (magit-log-show-more-entries ())))) - -(defun magit-show-item-or-scroll-up () - (interactive) - (magit-section-action (item info) - ((commit) - (magit-show-commit info #'scroll-up)) - ((stash) - (magit-show-stash info #'scroll-up)) - (t - (scroll-up)))) - -(defun magit-show-item-or-scroll-down () - (interactive) - (magit-section-action (item info) - ((commit) - (magit-show-commit info #'scroll-down)) - ((stash) - (magit-show-stash info #'scroll-down)) - (t - (scroll-down)))) - -(defun magit-mark-item (&optional unmark) - (interactive "P") - (if unmark - (magit-set-marked-commit nil) - (magit-section-action (item info "mark") - ((commit) - (magit-set-marked-commit (if (eq magit-marked-commit info) - nil - info)))))) - -(defun magit-describe-item () - (interactive) - (let ((section (magit-current-section))) - (message "Section: %s %s-%s %S %S %S" - (magit-section-type section) - (magit-section-beginning section) - (magit-section-end section) - (magit-section-title section) - (magit-section-info section) - (magit-section-context-type section)))) - -(defun magit-copy-item-as-kill () - "Copy sha1 of commit at point into kill ring." - (interactive) - (magit-section-action (item info "copy") - ((commit) - (kill-new info) - (message "%s" info)))) - -(eval-when-compile (require 'server)) - -(defun magit-server-running-p () - "Test whether server is running (works with < 23 as well). - -Return values: - nil the server is definitely not running. - t the server seems to be running. - something else we cannot determine whether it's running without using - commands which may have to wait for a long time." - (require 'server) - (if (functionp 'server-running-p) - (server-running-p) - (condition-case nil - (if server-use-tcp - (with-temp-buffer - (insert-file-contents-literally (expand-file-name server-name server-auth-dir)) - (or (and (looking-at "127\\.0\\.0\\.1:[0-9]+ \\([0-9]+\\)") - (assq 'comm - (process-attributes - (string-to-number (match-string 1)))) - t) - :other)) - (delete-process - (make-network-process - :name "server-client-test" :family 'local :server nil :noquery t - :service (expand-file-name server-name server-socket-dir))) - t) - (file-error nil)))) - -(defun magit-interactive-rebase () - "Start a git rebase -i session, old school-style." - (interactive) - (unless (magit-server-running-p) - (server-start)) - (let* ((section (get-text-property (point) 'magit-section)) - (commit (and (member 'commit (magit-section-context-type section)) - (magit-section-info section))) - (old-editor (getenv "GIT_EDITOR"))) - (setenv "GIT_EDITOR" (concat (locate-file "emacsclient" exec-path) - " -s " server-name)) - (unwind-protect - (magit-run-git-async "rebase" "-i" - (or (and commit (concat commit "^")) - (magit-read-rev "Interactively rebase to" (magit-guess-branch)))) - (if old-editor - (setenv "GIT_EDITOR" old-editor))))) - -(define-derived-mode magit-branch-manager-mode magit-mode "Magit Branch" - "Magit Branches") - -(defun magit-quit-window (&optional kill-buffer) - "Bury the buffer and delete its window. With a prefix argument, kill the -buffer instead." - (interactive "P") - (quit-window kill-buffer (selected-window))) - -(defun magit--branch-name-at-point () - "Get the branch name in the line at point." - (let ((branch (magit-section-info (magit-current-section)))) - (or branch (error "No branch at point")))) - -(defun magit--branches-for-remote-repo (remote) - "Return a list of remote branch names for REMOTE. -These are the branch names with the remote name stripped." - (remq nil - (mapcar (lambda (line) - (save-match-data - (if (and (not (string-match-p " -> " line)) - (string-match (concat "^ +" remote "/\\([^ $]+\\)") - line)) - (match-string 1 line)))) - (magit-git-lines "branch" "-r")))) - -(defvar magit-branches-buffer-name "*magit-branches*") - -(defun magit--is-branch-at-point-remote () - "Return non-nil if the branch at point is a remote tracking branch" - (magit-remote-part-of-branch (magit--branch-name-at-point))) - -(defun magit-remote-part-of-branch (branch) - (when (string-match-p "^\\(?:refs/\\)?remotes\\/" branch) - (loop for remote in (magit-git-lines "remote") - when (string-match-p (format "^\\(?:refs/\\)?remotes\\/%s\\/" (regexp-quote remote)) branch) return remote))) - -(defun magit-branch-no-remote (branch) - (let ((remote (magit-remote-part-of-branch branch))) - (if remote - (progn - ;; This has to match if remote is non-nil - (assert (string-match (format "^\\(?:refs/\\)?remotes\\/%s\\/\\(.*\\)" (regexp-quote remote)) branch) - 'show-args "Unexpected string-match failure: %s %s") - (match-string 1 branch)) - branch))) - -(defun magit-wash-branch-line (&optional remote-name) - (looking-at (concat - "^\\([ *] \\)" ; 1: current branch marker - "\\(.+?\\) +" ; 2: branch name - - "\\(?:" - - "\\([0-9a-fA-F]+\\)" ; 3: sha1 - " " - "\\(?:\\[" - "\\([^:\n]+?\\)" ; 4: tracking (non-greedy + to avoid matching \n) - "\\(?:: \\)?" - "\\(?:ahead \\([0-9]+\\)\\)?" ; 5: ahead - "\\(?:, \\)?" - "\\(?:behind \\([0-9]+\\)\\)?" ; 6: behind - "\\] \\)?" - "\\(?:.*\\)" ; message - - "\\|" ; or - - "-> " ; the pointer to - "\\(.+\\)" ; 7: a ref - - "\\)\n")) - - (let* ((current-string (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)) - (current (string-match-p "^\\*" current-string))) - - ; the current line is deleted before being reconstructed - (delete-region (point) - (line-beginning-position 2)) - - (magit-with-section branch 'branch - (magit-set-section-info branch) - (insert-before-markers - ; sha1 - (propertize (or sha1 - (make-string magit-sha1-abbrev-length ? )) - 'face 'magit-log-sha1) - " " - ; current marker - (if current - "# " - " ") - ; branch name - (apply 'propertize (magit-branch-no-remote branch) - (if current - '(face magit-branch))) - ; other ref that this branch is pointing to - (if other-ref - (concat " -> " (substring other-ref (+ 1 (length remote-name)))) - "") - ; tracking information - (if (and tracking - (equal (magit-remote-branch-for branch t) - (concat "refs/remotes/" tracking))) - (concat " [" - ; getting rid of the tracking branch name if it is the same as the branch name - (let* ((tracking-remote (magit-get "branch" branch "remote")) - (tracking-branch (substring tracking (+ 1 (length tracking-remote))))) - (propertize (if (string= branch tracking-branch) - (concat "@ " tracking-remote) - (concat tracking-branch " @ " tracking-remote)) - 'face 'magit-log-head-label-remote)) - ; ahead/behind information - (if (or ahead - behind) - ": " - "") - (if ahead - (concat "ahead " - (propertize ahead - 'face (if current - 'magit-branch)) - (if behind - ", " - "")) - "") - (if behind - (concat "behind " - (propertize behind - 'face 'magit-log-head-label-remote)) - "") - "]") - "") - "\n")))) - -(defun magit-wash-remote-branches-group (group) - (let* ((remote-name (first group)) - (url (magit-get "remote" remote-name "url")) - (push-url (magit-get "remote" remote-name "pushurl")) - (urls (concat url (if push-url - (concat ", "push-url) - ""))) - (marker (second group))) - - (magit-with-section (concat "remote:" remote-name) 'remote - (magit-set-section-info remote-name) - (insert-before-markers (propertize (format "%s (%s):" remote-name urls) 'face 'magit-section-title) "\n") - (magit-wash-branches-between-point-and-marker marker remote-name)) - (insert-before-markers "\n"))) - -(defun magit-wash-branches-between-point-and-marker (marker &optional remote-name) - (save-restriction - (narrow-to-region (point) marker) - (magit-wash-sequence - (if remote-name - (apply-partially 'magit-wash-branch-line remote-name) - #'magit-wash-branch-line)))) - -(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 (search-forward-regexp - (concat "^ remotes\\/" 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 (loop for remote in remotes - for end-markers on (cdr markers) - for marker = (loop for x in end-markers thereis x) - collect (list remote marker)))) - - ; actual displaying of information - (magit-with-section "local" nil - (insert-before-markers (propertize "Local:" 'face 'magit-section-title) "\n") - (magit-set-section-info ".") - (magit-wash-branches-between-point-and-marker - (loop for x in markers thereis x))) - - (insert-before-markers "\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))) - -(defun magit-refresh-branch-manager () - (magit-create-buffer-sections - (magit-git-section "branches" nil 'magit-wash-branches - "branch" - "-vva" - (format "--abbrev=%s" magit-sha1-abbrev-length)))) - -(magit-define-command branch-manager () - (interactive) - (let ((topdir (magit-get-top-dir default-directory))) - (magit-buffer-switch magit-branches-buffer-name) - (magit-mode-init topdir 'magit-branch-manager-mode #'magit-refresh-branch-manager))) - -(defun magit-change-what-branch-tracks () - "Change which remote branch the current branch tracks." - (interactive) - (if (magit--is-branch-at-point-remote) - (error "Cannot modify a remote branch")) - (let* ((local-branch (magit--branch-name-at-point)) - (new-tracked (magit-read-rev "Change tracked branch to" - nil - (lambda (ref) - (not (string-match-p "refs/remotes/" - ref))))) - new-remote new-branch) - (unless (string= (or new-tracked "") "") - (cond (;; Match refs that are unknown in the local repository if - ;; `magit-remote-ref-format' is set to - ;; `name-then-remote'. Can be useful if you want to - ;; create a new branch in a remote repository. - (string-match "^\\([^ ]+\\) +(\\(.+\\))$" ; 1: branch name; 2: remote name - new-tracked) - (setq new-remote (match-string 2 new-tracked) - new-branch (concat "refs/heads/" (match-string 1 new-tracked)))) - ((string-match "^\\(?:refs/remotes/\\)?\\([^/]+\\)/\\(.+\\)" ; 1: remote name; 2: branch name - new-tracked) - (setq new-remote (match-string 1 new-tracked) - new-branch (concat "refs/heads/" (match-string 2 new-tracked)))) - (t (error "Cannot parse the remote and branch name")))) - (magit-set new-remote "branch" local-branch "remote") - (magit-set new-branch "branch" local-branch "merge") - (magit-branch-manager) - (if (string= (magit-get-current-branch) local-branch) - (magit-refresh-buffer (magit-find-status-buffer default-directory))))) - -(defvar magit-ediff-file) - -(defun magit-interactive-resolve (file) - (require 'ediff) - (let ((merge-status (magit-git-string "ls-files" "-u" "--" file)) - (base-buffer (generate-new-buffer (concat file ".base"))) - (our-buffer (generate-new-buffer (concat file ".current"))) - (their-buffer (generate-new-buffer (concat file ".merged"))) - (windows (current-window-configuration))) - (if (null merge-status) - (error "Cannot resolve %s" file)) - (with-current-buffer base-buffer - (if (string-match "^[0-9]+ [0-9a-f]+ 1" merge-status) - (insert (magit-git-output `("cat-file" "blob" ,(concat ":1:" file)))))) - (with-current-buffer our-buffer - (if (string-match "^[0-9]+ [0-9a-f]+ 2" merge-status) - (insert (magit-git-output `("cat-file" "blob" ,(concat ":2:" file))))) - (let ((buffer-file-name file)) - (normal-mode))) - (with-current-buffer their-buffer - (if (string-match "^[0-9]+ [0-9a-f]+ 3" merge-status) - (insert (magit-git-output `("cat-file" "blob" ,(concat ":3:" file))))) - (let ((buffer-file-name file)) - (normal-mode))) - ;; We have now created the 3 buffer with ours, theirs and the ancestor files - (with-current-buffer (ediff-merge-buffers-with-ancestor our-buffer their-buffer base-buffer) - (make-local-variable 'magit-ediff-file) - (setq magit-ediff-file file) - (make-local-variable 'magit-ediff-windows) - (setq 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) - (file magit-ediff-file) - (file-buffer) - (windows magit-ediff-windows)) - (ediff-cleanup-mess) - (find-file file) - (setq file-buffer (current-buffer)) - (erase-buffer) - (insert-buffer-substring buffer-C) - (kill-buffer buffer-A) - (kill-buffer buffer-B) - (kill-buffer buffer-C) - (when (bufferp buffer-Ancestor) (kill-buffer buffer-Ancestor)) - (set-window-configuration windows) - (magit-save-some-buffers - "Conflict resolution finished; you may save the buffer" - (lambda () (eq (current-buffer) file-buffer))))))))) - -(defun magit-interactive-resolve-item () - (interactive) - (magit-section-action (item info "resolv") - ((diff) - (magit-interactive-resolve (cadr info))))) - -(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 default-directory))) - (apply #'magit-run-git-async "submodule" "update" (if init '("--init") ())))) - -(defun magit-submodule-update-init () - "Update and init the submodule of the current git repository." - (interactive) - (magit-submodule-update t)) - -(defun magit-submodule-init () - "Initialize the submodules" - (interactive) - (let ((default-directory (magit-get-top-dir default-directory))) - (magit-run-git-async "submodule" "init"))) - -(defun magit-submodule-sync () - "Synchronizes submodules' remote URL configuration" - (interactive) - (let ((default-directory (magit-get-top-dir default-directory))) - (magit-run-git-async "submodule" "sync"))) - -(defun magit-run-git-gui () - "Run `git gui' for the current git repository" - (interactive) - (let* ((default-directory (magit-get-top-dir default-directory))) - (magit-start-process "Git Gui" nil magit-git-executable "gui"))) - -(defun magit-run-gitk () - "Run `gitk --all' for the current git repository" - (interactive) - (let ((default-directory (magit-get-top-dir default-directory))) - (cond - ((eq system-type 'windows-nt) - ;; Gitk is a shell script, and Windows doesn't know how to - ;; "execute" it. The Windows version of Git comes with an - ;; implementation of "sh" and everything else it needs, but - ;; Windows users might not have added the directory where it's - ;; installed to their path - (let ((git-bin-dir (file-name-directory magit-gitk-executable)) - (exec-path exec-path) - (process-environment process-environment)) - (when git-bin-dir - ;; Adding it onto the end so that anything the user - ;; specified will get tried first. Emacs looks in - ;; exec-path; PATH is the environment variable inherited by - ;; the process. I need to change both. - (setq exec-path (append exec-path (list git-bin-dir))) - (push (format "PATH=%s;%s" - (getenv "PATH") - (replace-regexp-in-string "/" "\\\\" git-bin-dir)) - process-environment)) - (magit-start-process "Gitk" nil "sh" magit-gitk-executable "--all"))) - (t - (magit-start-process "Gitk" nil magit-gitk-executable "--all"))))) - -(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))))) - -(provide 'magit) - -;; rest of magit core -(require 'magit-key-mode) -(require 'magit-bisect) - -;;; magit.el ends here diff --git a/elpa/magit-1.2.0/magit.info b/elpa/magit-1.2.0/magit.info deleted file mode 100644 index deda92f0..00000000 --- a/elpa/magit-1.2.0/magit.info +++ /dev/null @@ -1,1359 +0,0 @@ -This is magit.info, produced by makeinfo version 4.8 from magit.texi. - -INFO-DIR-SECTION Emacs -START-INFO-DIR-ENTRY -* Magit: (magit). Using Git from Emacs with Magit. -END-INFO-DIR-ENTRY - - Copyright (C) 2008, 2009, 2010, 2011 Magit contributors. (See the -header of magit.el for the lengthy list of Magit contributors.) - - 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. - - -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 extension to Emacs. Magit supports GNU Emacs version 22 or later. -It may work with other emacsen, but Magit developers do not intend to -investigate and fix bugs that only appear in unsupported versions. -Patches to fix bugs in other emacsen or volunteers to maintain -compatibility are still welcome. - -* Menu: - -* Introduction:: -* Acknowledgments:: -* Sections:: -* Status:: -* Untracked files:: -* Staging and Committing:: -* History:: -* Reflogs:: -* Commit Buffer:: -* Diffing:: -* Tagging:: -* Resetting:: -* Stashing:: -* Branching:: -* The Branch Manager:: -* Wazzup:: -* Merging:: -* Rebasing:: -* Interactive Rebasing:: -* Rewriting:: -* Pushing and Pulling:: -* Bisecting:: -* Submodules:: -* Using Magit Extensions:: -* Using Git Directly:: -* Customization:: -* Frequently Asked Questions:: - - -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 all Magit features. It does not give -an introduction to version control in general, or to Git in particular. - - The main entry point to Magit is `M-x magit-status', which will put -you in Magit's status buffer. You will be using it frequently, so it -is probably a good idea to 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 `magit-mode' and have the same key bindings. -Not all commands make sense in all contexts, but a given key will -always do the same thing in all 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 -***************** - -Marius Vollmer started the whole project. Thanks ! - - From day one of the first Magit announcement, John Wiegley has -contributed numerous fixes, UI improvements, and new features. Thanks! - - Linh Dang and Christian Neukirchen also contributed from day one. -Thanks! - - Phil Hagelberg joined a few days later. Thanks! - - Alex Ott contributed support for git svn. Thanks! - - Marcin Bachry contributed bug fixes and support for decorated logs. -Thanks! - - Alexey Voinov fixed bugs. Thanks! - - Rémi Vanicat helped with Tramp support. Thanks! - - -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 -************************ - -Comitting with Git is a two step process: first you add the changes you -want to commit to a 'staging area', and then you commit them to the -repository. This allows you to only commit a subset of your local -changes. - - Magit allows you to ignore the staging area if you wish. As long as -your staging area is unused, Magit will show your uncomitted changes in -a section named _Changes_. - - When the staging area is in use, Magit uses two sections: _Unstaged -changes_ and _Staged changes_. 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. - - 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' 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' 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'. - - You can type `C-c C-a' in the buffer with the change description to -toggle a flag that determines whether the next commit will _amend_ the -current commit in HEAD. - - Typing `C-c C-s' will toggle the `--signoff' option. The default is -determined by the `magit-commit-signoff' customization variable. - - Typing `C-c C-e' will toggle the `--allow-empty' option. This -allows you to make commits that serve as notes, without including any -changes. - - Typing `C-c C-t' will toggle the option to specify the name and -email address for the commit's author. The default is determined by -the `user.name' and `user.email' git configuration settings. - - If you change your mind and don't want to go ahead with your commit -while you are in the `*magit-log-edit*' buffer, you can just switch to -another buffer, continue editing there, staging and unstaging things -until you are happy, and then return to the `*magit-log-edit*' buffer, -maybe via `C-x b', or by hitting `c' again in a Magit buffer. - - If you want to erase the `*magit-log-edit*' buffer and bury it, you -can hit `C-c C-k' in it. - - 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 lighweight 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: Branching, 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: Branching, Next: The Branch Manager, Prev: Stashing, Up: Top - -14 Branching -************ - -The current branch is indicated in the header of the status buffer. -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. - - Typing `b m' will let you rename a branch. Unless a branch with the -same name already exists, obviously... - - To create a new branch and switch to it immediately, type `b n'. - - To delete a branch, type `b d'. If you're currently on that branch, -Magit will offer to switch to the 'master' branch. - - Deleting a branch is only possible if it's already fully merged into -HEAD or its upstream branch. Unless you type `b D', that is. Here be -dragons... - - Typing `b v' will list the local and remote branches in a new buffer -called `*magit-branches*' from which you can work with them. See *Note -The Branch Manager:: for more details. - - -File: magit.info, Node: The Branch Manager, Next: Wazzup, Prev: Branching, Up: Top - -15 The Branch Manager -********************* - -The Branch Manager is a separate buffer called `*magit-branches*' with -its own local key map. The buffer contains both local and remote -branches. The current local branch is marked by a "*" in front of the -name. - - To check out a branch, move your cursor to the desired branch and -press `RET'. - - 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. - - By typing `T' on a local branch, you can change which remote branch -it's set to track. - - -File: magit.info, Node: Wazzup, Next: Merging, Prev: The Branch Manager, Up: Top - -16 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 - -17 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 - -18 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 - -19 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 - -20 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. - - 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 us `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: Bisecting, Prev: Rewriting, Up: Top - -21 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: Using Magit Extensions, Prev: Bisecting, Up: Top - -22 Submodules -************* - -`M u' - Update the submodules, with a prefix argument it will initializing. - -`M i' - Initialize the submodules. - -`M b' - Update and initialize the submodules in one go. - -`M s' - Synchronizes submodules' remote URL configuration setting to the - value specified in .gitmodules. - - -File: magit.info, Node: Bisecting, Next: Submodules, Prev: Pushing and Pulling, Up: Top - -23 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: Using Magit Extensions, Next: Using Git Directly, Prev: Submodules, Up: Top - -24 Magit Extensions -******************* - -* Menu: - -* Activating extensions:: -* Interfacing with Subversion:: -* Interfacing with Topgit:: -* Interfacing with StGit:: -* Developing Extensions:: - - -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",… 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, Next: Developing Extensions, 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: Developing Extensions, Prev: Interfacing with StGit, Up: Using Magit Extensions - -24.5 Developing Extensions -========================== - -Magit provides a generic mechanism to allow cooperation with Git-related -systems, such as foreign VCS, patch systems,… - - In particular it allows to: - - * Define sections to display specific informations about the current - state of the repository, and place them relatively to existing - sections. - - `magit-define-inserter' automagically defines two hooks called - `magit-before-insert-SECTION-hook' and - `magit-after-insert-SECTION-hook' that allow to generate and place - more sections. - - In the following example, we use the builtin "stashes" section to - place our own "foo" one. - - (magit-define-inserter foo () - (magit-git-section 'foo - "Foo:" 'foo-wash-function - "foo" "arg1" "arg2")) - (add-hook 'magit-after-insert-stashes-hook 'magit-insert-foo) - - * Define new types of objects in those sections. - - The function `foo-wash-function' defined above post-processes each - line of the output of the "git foo arg1 arg2" command, and is able - to associate a type to certain lines. - - A simple implementation could be: - - (defun foo-wash-function () - (let ((foo (buffer-substring (line-beginning-position) (line-end-position)))) - (goto-char (line-beginning-position)) - (magit-with-section foo 'foo - (magit-set-section-info foo) - (forward-line)))) - - In this case, every line of the command output is transformed into - an object of type `'foo'. - - * Alter behavior of generic commands to dispatch them correctly to - the relevant system, optionally making use of the newly defined - types. - - (magit-add-action (item info "discard") - ((foo) - (do-something-meaningful-for-discarding-a-foo))) - - This will alter the behavior of `k', when applied to those objects. - - * Plug a different logic into basic commands, to reflect the - presence of the extension. - - `magit-define-command' automagically defines a - `magit-CMD-command-hook' that can contain a list of functions to - call before the actual core code. Execution stops after the first - hook that returns a non-nil value. This leaves room for extension - logic. - - (add-hook 'magit-create-branch-command-hook 'foo-create-branch) - - The function `foo-create-branch' will be called each time an - attempt is made to create a branch, and can, for example, react to - a certain name convention. - - * Define new commands and associated menu. - - This part is not really specific to extensions, except that menus - take place in the "Extensions" submenu. - - - It is suggested that Magit extensions authors stick to the -convention of making extensions minor modes. This has many advantages, -including the fact that users are able to toggle extensions, and that -it's easy to configure a specific set of extensions for a given -repository. - - Shipped extensions can serve as an example of how to develop new -extensions. - - Basically a `foo' extension should provide a `magit-foo-mode' minor -mode, as well as a `turn-on-magit-foo' function. The main task of the -minor mode is to register/unregister the various hooks that the -extension requires. The registered actions on the other hand can be -left alone and activated globally, since they can be run only on -displayed items, which won't happen when the minor mode is off. - - Don't forget to call `magit-refresh' when the minor mode is toggled -interactively, so that the relevant sections can be shown or hidden. - - -File: magit.info, Node: Using Git Directly, Next: Customization, 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: Customization, Next: Frequently Asked Questions, Prev: Using Git Directly, Up: Top - -26 Customization -**************** - -The following variables can be used to adapt Magit to your workflow: - -`magit-git-executable' - The name of the Git executable. - -`magit-git-standard-options' - Standard options when running Git. - -`magit-repo-dirs' - Directories containing Git repositories. - - Magit will look into these directories for Git repositories and - offer them as choices for `magit-status'. - -`magit-repo-dirs-depth' - 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. - -`magit-save-some-buffers' - Non-nil means that `magit-status' will save modified buffers - before running. Setting this to `t' will ask which buffers to - save, setting it to `'dontask' will save all modified buffers - without asking. - -`magit-save-some-buffers-predicate' - Specifies a predicate function on `magit-save-some-buffers' to - determine which unsaved buffers should be prompted for saving. - -`magit-commit-all-when-nothing-staged' - Determines what `magit-log-edit' does when nothing is staged. - Setting this to `nil' will make it do nothing, setting it to `t' - will arrange things so that the actual commit command will use the - `--all' option, setting it to `'ask' will first ask for - confirmation whether to do this, and setting it to `'ask-stage' - will cause all changes to be staged, after a confirmation. - -`magit-commit-signoff' - When performing `git commit' adds `--signoff'. - -`magit-log-cutoff-length' - The maximum number of commits to show in the `log' and `whazzup' - buffers. - -`magit-log-infinite-length' - Number of log used to show as maximum for - `magit-log-cutoff-length'. - -`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-next-section'. - -`magit-process-popup-time' - Popup the process buffer if a command takes longer than this many - seconds. - -`magit-revert-item-confirm' - Require acknowledgment before reverting an item. - -`magit-log-edit-confirm-cancellation' - Require acknowledgment before canceling the log edit buffer. - -`magit-remote-ref-format' - What format to use for autocompleting refs, in pariticular for - remotes. - - Autocompletion is used by functions like `magit-checkout', - `magit-interactive-rebase' and others which offer branch name - completion. - - The value `'name-then-remote' means remotes will be of the form - `name (remote)', while the value `'remote-slash-name' means that - they'll be of the form `remote/name'. I.e. something that's listed - as `remotes/upstream/next' by `git branch -l -a' will be - `upstream/next'. - -`magit-process-connection-type' - Connection type used for the git process. - - `nil' mean pipe, it is usually faster and more efficient, and work - on cygwin. `t' mean pty, it enable magit to prompt for passphrase - when needed. - -`magit-completing-read-function' - Function to be called when requesting input from the user. - -`magit-create-branch-behaviour' - Where magit will create a new branch if not supplied a branchname - or ref. - - The value `'at-head' means a new branch will be created at the tip - of your current branch, while the value `'at-point' means magit - will try to find a valid reference at point... - -`magit-status-buffer-switch-function' - Function for `magit-status' to use for switching to the status - buffer. - - The function is given one argument, the status buffer. - -`magit-rewrite-inclusive' - 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' - ^ - -`magit-topgit-executable' - The name of the TopGit executable. - -`magit-topgit-branch-prefix' - Convention prefix for topic branch creation. - - - -File: magit.info, Node: Frequently Asked Questions, Prev: Customization, Up: Top - -27 Frequently Asked Questions -***************************** - -* Menu: - -* FAQ - Changes:: -* FAQ 1 - Troubleshooting:: -* FAQ 2 - Display issues:: - - -File: magit.info, Node: FAQ - Changes, Next: FAQ 1 - Troubleshooting, Up: Frequently Asked Questions - -27.1 Changes -============ - - * v1.1: Changed the way extensions work. Previously, they were - enabled unconditionally once the library was loaded. Now they are - minor modes that need to be activated explicitly (potentially on a - per-repository basis). See *Note Activating extensions::. - - - -File: magit.info, Node: FAQ 1 - Troubleshooting, Next: FAQ 2 - Display issues, Prev: FAQ - Changes, Up: Frequently Asked Questions - -27.2 Troubleshooting -==================== - -* Menu: - -* FAQ 1-1:: How do I get raw error messages from git? - - -File: magit.info, Node: FAQ 1-1, Up: FAQ 1 - Troubleshooting - -27.2.1 Question 1.1 -------------------- - -How do I get raw error messages from git? - -Answer -...... - -If a command goes wrong, you can hit `$' to access the git process -buffer. There, the entire trace for the latest operation is available. - - -File: magit.info, Node: FAQ 2 - Display issues, Prev: FAQ 1 - Troubleshooting, Up: Frequently Asked Questions - -27.3 Display issues -=================== - -* Menu: - -* FAQ 2-1:: How do I fix international characters display? - - -File: magit.info, Node: FAQ 2-1, Up: FAQ 2 - Display issues - -27.3.1 Question 2.1 -------------------- - -How do I fix international characters display? - -Answer -...... - -Please make sure your Magit buffer uses a compatible coding system. In -the particular case of file names, git itself quotes them by default. -You can disable this with one of the following approaches: - - $ git config core.quotepath false - - or - - (setq magit-git-standard-options (append magit-git-standard-options - '("-c" "core.quotepath=false"))) - - The latter might not work in old versions of git. - - - -Tag Table: -Node: Top639 -Node: Introduction1592 -Node: Acknowledgments3064 -Node: Sections3744 -Node: Status6294 -Node: Untracked files9033 -Node: Staging and Committing10237 -Node: History14135 -Node: Reflogs17315 -Node: Commit Buffer17719 -Node: Diffing19045 -Node: Tagging20032 -Node: Resetting20481 -Node: Stashing22005 -Node: Branching23063 -Node: The Branch Manager24441 -Node: Wazzup25157 -Node: Merging25948 -Node: Rebasing26998 -Node: Interactive Rebasing27824 -Node: Rewriting29116 -Node: Pushing and Pulling32141 -Node: Submodules34068 -Node: Bisecting34488 -Node: Using Magit Extensions35703 -Node: Activating extensions36000 -Node: Interfacing with Subversion37384 -Node: Interfacing with Topgit37982 -Node: Interfacing with StGit39010 -Node: Developing Extensions40008 -Node: Using Git Directly43786 -Node: Customization44157 -Node: Frequently Asked Questions48703 -Node: FAQ - Changes48934 -Node: FAQ 1 - Troubleshooting49342 -Node: FAQ 1-149590 -Node: FAQ 2 - Display issues49894 -Node: FAQ 2-150123 - -End Tag Table diff --git a/elpa/magit-1.2.0/rebase-mode.el b/elpa/magit-1.2.0/rebase-mode.el deleted file mode 100644 index 1ccfb540..00000000 --- a/elpa/magit-1.2.0/rebase-mode.el +++ /dev/null @@ -1,322 +0,0 @@ -;;; rebase-mode -- edit git rebase files. - -;; Copyright (C) 2010 Phil Jackson -;; Copyright (C) 2011 Peter J Weisberg -;; -;; Magit is free software; you can redistribute it and/or modify it -;; under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. -;; -;; Magit is distributed in the hope that it will be useful, but WITHOUT -;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public -;; License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with Magit. If not, see . - -;;; Commentary: - -;; Allows the editing of a git rebase file (which you might get when -;; using 'git rebase -i' or hitting 'E' in Magit). Assumes editing is -;; happening in a server. - -;;; Code: - -(require 'server) - -(defgroup rebase-mode nil - "Customize Rebase Mode" - :group 'faces) - -(defface rebase-mode-killed-action-face - '((((class color)) - :inherit font-lock-comment-face - :strike-through t)) - "Action lines in the rebase TODO list that have been commented out." - :group 'rebase-mode) - -(defface rebase-mode-description-face - '((t :inherit font-lock-comment-face)) - "Face for one-line commit descriptions" - :group 'rebase-mode) - -(defconst rebase-mode-action-line-re - (rx - line-start - (? "#") - (group - (| - (any "presf") - "pick" - "reword" - "edit" - "squash" - "fixup")) - (char space) - (group - (** 4 40 hex-digit)) ;sha1 - (char space) - (group - (* not-newline))) - "Regexp that matches an action line in a rebase buffer.") - -(defconst rebase-mode-exec-line-re - (rx - line-start - (? "#") - (group - (| "x" - "exec")) - (char space) - (group - (* not-newline))) - "Regexp that matches an exec line in a rebase buffer.") - -(defconst rebase-mode-dead-line-re - (rx-to-string `(and line-start - (char ?#) - (or (regexp ,(substring rebase-mode-action-line-re 1)) - (regexp ,(substring rebase-mode-exec-line-re 1)))) t) - "Regexp that matches a commented-out exec or action line in a rebase buffer.") - -(defvar rebase-mode-font-lock-keywords - (list - (list rebase-mode-action-line-re - '(1 font-lock-keyword-face) - '(2 font-lock-builtin-face) - '(3 'rebase-mode-description-face)) - (list rebase-mode-exec-line-re - '(1 font-lock-keyword-face)) - (list (rx line-start (char "#") (* not-newline)) 0 font-lock-comment-face) - (list rebase-mode-dead-line-re 0 ''rebase-mode-killed-action-face t)) - "Font lock keywords for `rebase-mode'.") - -(defvar key-to-action-map - '(("c" . "pick") - ("r" . "reword") - ("e" . "edit") - ("s" . "squash") - ("f" . "fixup")) - "Mapping from key to action.") - -(defvar rebase-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "q") 'server-edit) - (define-key map (kbd "C-c C-c") 'server-edit) - - (define-key map (kbd "a") 'rebase-mode-abort) - (define-key map (kbd "C-c C-k") 'rebase-mode-abort) - - (define-key map (kbd "M-p") 'rebase-mode-move-line-up) - (define-key map (kbd "M-n") 'rebase-mode-move-line-down) - (define-key map (kbd "k") 'rebase-mode-kill-line) - (define-key map (kbd "x") 'rebase-mode-exec) - - (define-key map (kbd "n") 'forward-line) - (define-key map (kbd "p") '(lambda(n) - (interactive "p") - (forward-line (* n -1)))) - (define-key map [remap undo] 'rebase-mode-undo) - map) - "Keymap for rebase-mode. Note this will be added to by the -top-level code which defines the edit functions.") - -(require 'easymenu) -(easy-menu-define rebase-mode-menu rebase-mode-map - "Rebase-mode menu" - '("Rebase" - ["Pick" rebase-mode-pick t] - ["Reword" rebase-mode-reword t] - ["Edit" rebase-mode-edit t] - ["Squash" rebase-mode-squash t] - ["Fixup" rebase-mode-fixup t] - ["Kill" rebase-mode-kill-line t] - ["Move Down" rebase-mode-move-line-down t] - ["Move Up" rebase-mode-move-line-up t] - ["Execute" rebase-mode-exec t] - "---" - ["Abort" rebase-mode-abort t] - ["Done" server-edit t])) - -;; create the functions which edit the action lines themselves (based -;; on `key-to-action-map' above) -(mapc (lambda (key-action) - (let ((fun-name (intern (concat "rebase-mode-" (cdr key-action))))) - ;; define the function - (eval `(defun ,fun-name () - (interactive) - (rebase-mode-edit-line ,(cdr key-action)))) - - ;; bind the function in `rebase-mode-map' - (define-key rebase-mode-map (car key-action) fun-name))) - key-to-action-map) - -(defun rebase-mode-edit-line (change-to) - "Change the keyword at the start of the current action line to -that of CHANGE-TO." - (when (rebase-mode-looking-at-action) - (let ((buffer-read-only nil) - (start (point))) - (goto-char (point-at-bol)) - (delete-region (point) (progn (forward-word 1) (point))) - (insert change-to) - (goto-char start)))) - -(defun rebase-mode-looking-at-action () - "Return non-nil if looking at an action line." - (save-excursion - (goto-char (point-at-bol)) - (looking-at rebase-mode-action-line-re))) - -(defun rebase-mode-looking-at-action-or-exec () - "Return non-nil if looking at an action line or exec line." - (save-excursion - (goto-char (point-at-bol)) - (or (looking-at rebase-mode-action-line-re) - (looking-at rebase-mode-exec-line-re)))) - -(defun rebase-mode-looking-at-exec () - "Return non-nil if cursor is on an exec line." - (string-match rebase-mode-exec-line-re (thing-at-point 'line))) - -(defun rebase-mode-looking-at-killed-exec () - "Return non-nil if looking at an exec line that has been commented out" - (let ((line (thing-at-point 'line))) - (and (eq (aref line 0) ?#) - (string-match rebase-mode-exec-line-re line)))) - -(defun rebase-mode-move-line-up () - "Move the current action line up." - (interactive) - (when (rebase-mode-looking-at-action-or-exec) - (let ((buffer-read-only nil) - (col (current-column))) - (transpose-lines 1) - (forward-line -2) - (move-to-column col)))) - -(defun rebase-mode-move-line-down () - "Assuming the next line is also an action line, move the current line down." - (interactive) - ;; if we're on an action and the next line is also an action - (when (and (rebase-mode-looking-at-action-or-exec) - (save-excursion - (forward-line) - (rebase-mode-looking-at-action-or-exec))) - (let ((buffer-read-only nil) - (col (current-column))) - (forward-line 1) - (transpose-lines 1) - (forward-line -1) - (move-to-column col)))) - -(defun rebase-mode-abort () - "Abort this rebase (by emptying the buffer, saving and closing -server connection)." - (interactive) - (when (or (not (buffer-modified-p)) - (y-or-n-p "Abort this rebase? ")) - (let ((buffer-read-only nil)) - (delete-region (point-min) (point-max)) - (save-buffer) - (server-edit)))) - -(defun rebase-mode-kill-line () - "Kill the current action line." - (interactive) - (when (and (not (eq (char-after (point-at-bol)) ?#)) - (rebase-mode-looking-at-action-or-exec)) - (beginning-of-line) - (let ((buffer-read-only nil)) - (insert "#")) - (forward-line))) - -(defun rebase-mode-exec (edit) - "Prompt the user for a shell command to be executed, and add it to -the todo list. - -If the cursor is on a commented-out exec line, uncomment the -current line instead of prompting. - -When the prefix argument EDIT is non-nil and the cursor is on an -exec line, edit that line instead of inserting a new one. If the -exec line was commented out, also uncomment it." - (interactive "P") - (cond - ((and edit (rebase-mode-looking-at-exec)) - (let ((new-line (rebase-mode-read-exec-line - (match-string-no-properties 2 (thing-at-point 'line)))) - (inhibit-read-only t)) - (delete-region (point-at-bol) (point-at-eol)) - (if (not (equal "" new-line)) - (insert "exec " new-line) - (delete-char -1) - (forward-line)) - (move-beginning-of-line nil))) - ((rebase-mode-looking-at-killed-exec) - (save-excursion - (beginning-of-line) - (let ((buffer-read-only nil)) - (delete-char 1)))) - (t - (let ((inhibit-read-only t) - (line (rebase-mode-read-exec-line))) - (unless (equal "" line) - (move-end-of-line nil) - (newline) - (insert (concat "exec " line)))) - (move-beginning-of-line nil)))) - -(defun rebase-mode-read-exec-line (&optional initial-line) - (read-shell-command "Execute: " initial-line)) - -(defun rebase-mode-undo (&optional arg) - "A thin wrapper around `undo', which allows undoing in -read-only buffers." - (interactive "P") - (let ((inhibit-read-only t)) - (undo arg))) - -;;;###autoload -(define-derived-mode rebase-mode special-mode "Rebase" - "Major mode for editing of a Git rebase file. - -Rebase files are generated when you run 'git rebase -i' or run -`magit-interactive-rebase'. They describe how Git should perform -the rebase. See the documentation for git-rebase (e.g., by -running 'man git-rebase' at the command line) for details." - (setq font-lock-defaults '(rebase-mode-font-lock-keywords t t))) - -(defun rebase-mode-show-keybindings () - "Modify the \"Commands:\" section of the comment Git generates -at the bottom of the file so that in place of the one-letter -abbreviation for the command, it shows the command's keybinding. -By default, this is the same except for the \"pick\" command." - (save-excursion - (goto-char (point-min)) - (while (search-forward-regexp "^# \\(.\\), \\([[:alpha:]]+\\) = " nil t) - (let ((start (match-beginning 1)) - (end (match-end 1)) - (command (intern (concat "rebase-mode-" (match-string 2))))) - (when (fboundp command) - (let ((overlay (make-overlay start end))) - (overlay-put overlay - 'display - (key-description (where-is-internal command nil t))))))))) - -(add-hook 'rebase-mode-hook 'rebase-mode-show-keybindings t) - -(defun rebase-mode-disable-before-save-hook () - (set (make-local-variable 'before-save-hook) nil)) - -(add-hook 'rebase-mode-hook 'rebase-mode-disable-before-save-hook) - -;;;###autoload -(add-to-list 'auto-mode-alist - '("git-rebase-todo" . rebase-mode)) - -(provide 'rebase-mode) - -;;; rebase-mode.el ends here diff --git a/elpa/magit-20150704.855/AUTHORS.md b/elpa/magit-20150704.855/AUTHORS.md new file mode 100644 index 00000000..22949885 --- /dev/null +++ b/elpa/magit-20150704.855/AUTHORS.md @@ -0,0 +1,213 @@ +Authors +======= + +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 +------ + +- Marius Vollmer + +Maintainer +---------- + +- Jonas Bernoulli + +Retired Maintainers +------------------- + +- Nicolas Dudebout +- Peter J. Weisberg +- Phil Jackson +- Rémi Vanicat +- Yann Hodique + +Contributors +------------ + +- 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 +- Andreas Rottmann +- Andrei Chițu +- Andrew Kirkpatrick +- Andrew Schwartzmeyer +- Andrey Smirnov +- Andy Sawyer +- Bastian Beischer +- Ben Walton +- Bradley Wright +- Brandon W Maister +- Brian Warner +- Bryan Shell +- Chris Bernard +- Chris Done +- Chris Moore +- Chris Ring +- Christian Dietrich +- Christian Kluge +- Christopher Monsanto +- Cornelius Mika +- Craig Andera +- Dale Hagglund +- Damien Cassou +- Daniel Brockman +- Daniel Farina +- Daniel Hackney +- Dan LaManna +- David Abrahams +- David Hull +- David Wallin +- Dennis Paskorz +- Divye Kapoor +- Dominique Quatravaux +- Eli Barzilay +- Eric Davis +- Eric Schulte +- Evgkeni Sampelnikof +- Felix Geller +- Feng Li +- Florian Ragwitz +- Geoff Shannon +- George Kadianakis +- Graham Clark +- Greg A. Woods +- Greg Sexton +- Guillaume Martres +- Hannu Koivisto +- Hans-Peter Deifel +- Ian Eure +- Ingo Lohmar +- Jan Tatarik +- Jasper St. Pierre +- Jeff Bellegarde +- Jeff Dairiki +- Jesse Alama +- John Mastro +- John Wiegley +- Jonas Bernoulli +- Jonathan Roes +- Jordan Greenberg +- Julien Danjou +- Justin Caratzas +- Kan-Ru Chen +- Kimberly Wolk +- Kyle Meyer +- Laurent Laffont +- Leandro Facchinetti +- Lele Gaifax +- Leo Liu +- Leonardo Etcheverry +- Lingchao Xin +- Lluís Vilanova +- Loic Dachary +- Luís Borges de Oliveira +- Luke Amdor +- Manuel Vázquez Acosta +- Marcel Wolf +- Marc Herbert +- Marcin Bachry +- Marco Craveiro +- Marian Schubert +- Marius Vollmer +- Mark Hepburn +- Mark Oteiza +- Matus Goljer +- Michael Fogleman +- Michael Griffiths +- Michal Sojka +- Miles Bader +- Mitchel Humpherys +- Moritz Bunkus +- Natalie Weizenbaum +- Nguyễn Tuấn Anh +- Nic Ferier +- Nick Alcock +- Nick Alexander +- Nick Dimiduk +- Nicolas Dudebout +- Nicolas Richard +- Noam Postavsky +- Ole Arndt +- Oleh Krehel +- Óscar Fuentes +- Paul Stadig +- Pavel Holejsovsky +- Pekka Pessi +- Peter Eisentraut +- Peter J. Weisberg +- Peter Vasil +- Philippe Vaucher +- Philipp Haselwarter +- Philip Weaver +- Phil Jackson +- Pieter Praet +- Prathamesh Sonpatki +- rabio +- Rafael Laboissiere +- Raimon Grau +- Ramkumar Ramachandra +- Remco van 't Veer +- Rémi Vanicat +- René Stadler +- Robert Boone +- Robin Green +- Roger Crew +- Romain Francoise +- Ron Parker +- Roy Crihfield +- Rüdiger Sonderfeld +- Ryan C. Thompson +- Samuel Bronson +- Sanjoy Das +- Sean Bryant +- Sebastian Wiesner +- Sébastien Gross +- Seong-Kook Shin +- Sergey Pashinin +- Sergey Vinokurov +- Servilio Afre Puentes +- Štěpán Němec +- Steven Chow +- Steven E. Harris +- Steven Thomas +- Steve Purcell +- Suhail Shergill +- 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 +- Wei Huang +- Wilfred Hughes +- Win Treese +- Xavier Noria +- Yann Hodique +- York Zhao +- Yuichi Higashi diff --git a/elpa/magit-20150704.855/COPYING b/elpa/magit-20150704.855/COPYING new file mode 100644 index 00000000..44325404 --- /dev/null +++ b/elpa/magit-20150704.855/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-20150704.855/dir b/elpa/magit-20150704.855/dir new file mode 100644 index 00000000..5fec5439 --- /dev/null +++ b/elpa/magit-20150704.855/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: (magit). Using Git from Emacs with Magit. diff --git a/elpa/magit-20150704.855/git-rebase.el b/elpa/magit-20150704.855/git-rebase.el new file mode 100644 index 00000000..de0a06da --- /dev/null +++ b/elpa/magit-20150704.855/git-rebase.el @@ -0,0 +1,392 @@ +;;; git-rebase.el --- Edit Git rebase files + +;; Copyright (C) 2010-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: 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 "e 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 [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) + (define-key map (kbd "C-x C-t") 'git-rebase-move-line-up) + 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] + ["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])) + +;;; 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-move-line-up () + "Move the current commit (or command) up." + (interactive) + (goto-char (line-beginning-position)) + (if (bobp) + (ding) + (when (looking-at git-rebase-line) + (let ((inhibit-read-only t)) + (transpose-lines 1)) + (forward-line -2)))) + +(defun git-rebase-move-line-down () + "Move the current commit (or command) down." + (interactive) + (goto-char (line-beginning-position)) + (when (and (looking-at git-rebase-line) + (save-excursion + (forward-line) + (looking-at git-rebase-line))) + (forward-line) + (let ((inhibit-read-only t)) + (transpose-lines 1)) + (forward-line -1))) + +(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)) + (magit-show-commit it) + (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." + (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)) + (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)) + (insert "# C-c C-c tell Git to make it happen\n") + (insert "# C-c C-k tell Git that you changed your mind, i.e. abort\n") + (insert "# p move point to previous line\n") + (insert "# n move point to next line\n") + (insert "# M-p move the commit at point up\n") + (insert "# M-n move the commit at point down\n") + (insert "# RET show the commit at point in another buffer\n") + (insert "# C-/ undo last change\n") + (insert "# k drop the commit at point\n") + (while (re-search-forward + "^#\\( ?\\)\\([^,]\\)\\(,\\) \\([^ ]+\\) = " nil t) + (replace-match " " t t nil 1) + (replace-match " " t t nil 3) + (let* ((cmd (intern (concat "git-rebase-" (match-string 4)))) + (key (where-is-internal cmd nil t))) + (when (and (fboundp cmd) key) ; see #1875 + (replace-match (key-description key) 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-20150704.855/magit-apply.el b/elpa/magit-20150704.855/magit-apply.el new file mode 100644 index 00000000..d8992ab2 --- /dev/null +++ b/elpa/magit-20150704.855/magit-apply.el @@ -0,0 +1,425 @@ +;;; magit-apply.el --- apply Git diffs + +;; Copyright (C) 2010-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. + +;;; 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-am-popup 'magit-sequence) +;; For `magit-discard-files' +(declare-function magit-checkout-stage 'magit) +(declare-function magit-checkout-read-stage 'magit) + +(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) + +;;; 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-current-section) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(,(or `unstaged `staged) ,_) + (user-error "Change is already in the working tree")) + (`(untracked file) (magit-am-popup)) + (`(,_ region) (magit-apply-region it args)) + (`(,_ hunk) (magit-apply-hunk it args)) + (`(,_ file) (magit-apply-diff it args))))) + +(defun magit-apply-diff (section &rest args) + (magit-apply-patch section args + (concat (magit-diff-file-header section) + (buffer-substring (magit-section-content section) + (magit-section-end section))))) + +(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 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) + (magit-apply-patch section args (magit-diff-hunk-region-patch section args))) + +(defvar magit-apply-inhibit-wip nil) + +(defun magit-apply-patch (section args patch) + (let* ((file (if (eq (magit-section-type section) 'file) + (magit-section-value section) + (magit-section-parent-value section))) + (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 magit-apply-inhibit-wip)) + (magit-wip-commit-before-change (list file) (concat " before " command))) + (with-temp-buffer + (insert patch) + (magit-run-git-with-input nil + "apply" args + (unless (magit-diff-context-p) "--unidiff-zero") + "--ignore-space-change" "-")) + (when (and magit-wip-after-apply-mode (not magit-apply-inhibit-wip)) + (magit-wip-commit-after-apply (list file) (concat " after " command))) + (magit-refresh))) + +;;;; Stage + +(defun magit-stage () + "Add the change at point to the staging area." + (interactive) + (--when-let (magit-current-section) + (let ((inhibit-magit-revert t)) + (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 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)))) + (let ((default-directory (magit-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))) + (let ((default-directory (magit-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-no-revert "add" arg (if files (cons "--" files) "."))) + +(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-no-revert "add" "--" plain)) + (dolist (repo repos) + (let ((inhibit-magit-revert t)) + (save-excursion + (goto-char (magit-section-start + (magit-get-section + `((file . ,repo) (untracked) (status))))) + (call-interactively 'magit-submodule-add)))))) + +;;;; Unstage + +(defun magit-unstage () + "Remove the change at point from the staging area." + (interactive) + (--when-let (magit-current-section) + (let ((inhibit-magit-revert t)) + (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 file) (magit-unstage-1 (list (magit-section-value it)))) + (`(staged files) (magit-unstage-1 (magit-region-values))) + (`(staged list) (magit-unstage-all)) + (`(committed ,_) (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)))) + (let ((default-directory (magit-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))) + +;;;###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" "--"))) + +;;;; Discard + +(defun magit-discard () + "Remove the change at point." + (interactive) + (--when-let (magit-current-section) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(committed ,_) (user-error "Cannot discard committed changes")) + (`(undefined ,_) (user-error "Cannot discard this change")) + (`(,_ list) (magit-discard-files (magit-section-children it))) + (`(,_ files) (magit-discard-files (magit-region-sections))) + (`(,_ file) (magit-discard-files (list it))) + (_ (magit-discard-apply it))))) + +(defun magit-discard-apply (section) + (let* ((type (magit-diff-type section)) + (scope (magit-diff-scope section)) + (fn (pcase scope + (`region 'magit-apply-region) + (`hunk 'magit-apply-hunk) + (`file 'magit-apply-diff)))) + (when (or (eq scope 'file) + (magit-confirm 'discard (format "Discard %s" scope))) + (if (eq type 'unstaged) + (funcall fn section "--reverse") + (if (magit-anything-unstaged-p + nil (if (eq scope 'file) + (magit-section-value section) + (magit-section-parent-value section))) + (progn + (let ((inhibit-magit-refresh t)) + (funcall fn section "--reverse" "--cached") + (funcall fn section "--reverse")) + (magit-refresh)) + (funcall fn section "--reverse" "--index")))))) + +(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) (push file 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)) + (with-no-warnings ; #1933 + (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 ((magit-apply-inhibit-wip t)) + (-if-let (binaries (magit-staged-binary-files)) + (let ((text (--filter (not (member (magit-section-value it) binaries)) + sections))) + (cl-destructuring-bind (unsafe safe) + (let ((modified (magit-modified-files t))) + (--separate (member it modified) binaries)) + (mapc #'magit-discard-apply text) + (when safe + (magit-call-git "reset" "--" safe)) + (user-error + (concat "Cannot discard staged changes to binary files, " + "which also have unstaged changes. Unstage instead.")))) + (mapc #'magit-discard-apply sections))))))) + +;;;; Reverse + +(defun magit-reverse () + "Reverse the change at point in the working tree." + (interactive) + (--when-let (magit-current-section) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(untracked ,_) (user-error "Cannot reverse untracked changes")) + (`(unstaged ,_) (user-error "Cannot reverse unstaged changes")) + (`(,_ list) (magit-reverse-files (magit-section-children it))) + (`(,_ files) (magit-reverse-files (magit-region-sections))) + (`(,_ file) (magit-reverse-files (list it))) + (_ (magit-reverse-apply it))))) + +(defun magit-reverse-apply (section) + (let ((scope (magit-diff-scope section))) + (when (or (eq scope 'file) + (magit-confirm 'reverse (format "Reverse %s" scope))) + (funcall (pcase scope + (`region 'magit-apply-region) + (`hunk 'magit-apply-hunk) + (`file 'magit-apply-diff)) + section "--reverse")))) + +(defun magit-reverse-files (sections) + (cl-destructuring-bind (binaries sections) + (let ((binaries (magit-staged-binary-files))) + (--separate (member (magit-section-value it) binaries) sections)) + (let ((files (mapcar #'magit-section-value sections))) + (when (magit-confirm-files 'reverse files) + (magit-wip-commit-before-change files " before reverse") + (let ((magit-apply-inhibit-wip t)) + (mapc #'magit-reverse-apply sections)) + (magit-wip-commit-after-apply files " after reverse"))) + (when binaries + (user-error "Cannot reverse binary files")))) + +;;; 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-20150704.855/magit-autoloads.el b/elpa/magit-20150704.855/magit-autoloads.el new file mode 100644 index 00000000..b7929814 --- /dev/null +++ b/elpa/magit-20150704.855/magit-autoloads.el @@ -0,0 +1,1163 @@ +;;; 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" (21913 4083 0 +;;;;;; 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" (21913 4083 0 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-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 non 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-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-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-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-set-upstream "magit" "\ +Change the UPSTREAM branch of BRANCH. + +\(fn BRANCH UPSTREAM)" t nil) + +(autoload 'magit-branch-unset-upstream "magit" "\ +Unset the upstream branch of BRANCH. + +\(fn BRANCH)" 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-branch-edit-description "magit" "\ +Edit the description of BRANCH. + +\(fn BRANCH)" 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 effectivley 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) + (autoload 'magit-submodule-popup "magit" nil t) + +(autoload 'magit-submodule-add "magit" "\ +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" "\ +Clone and register missing submodules and checkout appropriate commits. + +\(fn)" t nil) + +(autoload 'magit-submodule-init "magit" "\ +Register submodules listed in \".gitmodules\" into \".git/config\". + +\(fn)" t nil) + +(autoload 'magit-submodule-update "magit" "\ +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" "\ +Update each submodule's remote URL according to \".gitmodules\". + +\(fn)" t nil) + +(autoload 'magit-submodule-fetch "magit" "\ +Fetch all submodules. +With a prefix argument fetch all remotes. + +\(fn &optional ALL)" 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'. + +Non-interactively run Git in DIRECTORY with ARGS. + +\(fn ARGS DIRECTORY)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-apply" "magit-apply.el" (21913 4083 +;;;;;; 0 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-bisect" "magit-bisect.el" (21913 4083 +;;;;;; 0 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. + +\(fn CMDLINE)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-blame" "magit-blame.el" (21913 4083 +;;;;;; 0 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" (21913 4082 +;;;;;; 0 0)) +;;; Generated autoloads from magit-commit.el + (autoload 'magit-commit-popup "magit-commit" nil t) + +(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 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 --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 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 --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'. + +\(git commit --no-edit --fixup=COMMIT [ARGS]) + +\(fn &optional COMMIT ARGS CONFIRM)" t nil) + +(autoload 'magit-commit-squash "magit-commit" "\ +Create a squash 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'. + +\(git commit --no-edit --squash=COMMIT [ARGS]) + +\(fn &optional COMMIT ARGS CONFIRM)" t nil) + +(autoload 'magit-commit-instant-fixup "magit-commit" "\ +Create a fixup commit and instantly rebase. + +\(git commit --no-edit --fixup=COMMIT ARGS; + git rebase -i COMMIT^ --autosquash --autostash) + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-instant-squash "magit-commit" "\ +Create a squash commit and instantly rebase. + +\(git commit --no-edit --squash=COMMIT ARGS; + git rebase -i COMMIT^ --autosquash --autostash) + +\(fn &optional COMMIT ARGS)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-diff" "magit-diff.el" (21913 4083 0 +;;;;;; 0)) +;;; Generated autoloads from magit-diff.el + (autoload 'magit-diff-popup "magit-diff" nil t) + +(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. +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 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-unpushed "magit-diff" "\ +Show unpushed changes. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-diff-unpulled "magit-diff" "\ +Show unpulled changes. + +\(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 commited. + +\(fn &optional ARGS)" 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 commit at point. +If there is no commit at point or with a prefix argument prompt +for a commit. + +\(fn COMMIT &optional NOSELECT MODULE ARGS)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-ediff" "magit-ediff.el" (21913 4083 +;;;;;; 0 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. + +\(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 users +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'. + +\(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'. + +\(fn FILE)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-extras" "magit-extras.el" (21913 4082 +;;;;;; 0 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 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) + +(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" (21913 4083 0 0)) +;;; Generated autoloads from magit-log.el + (autoload 'magit-log-popup "magit-log" nil t) + +(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 file visited in the current buffer. + +\(fn)" 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" (21913 4083 +;;;;;; 0 0)) +;;; Generated autoloads from magit-remote.el + +(autoload 'magit-clone "magit-remote" "\ +Clone the REPOSITORY to DIRECTORY. + +\(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-fetch-popup "magit-remote" nil t) + +(autoload 'magit-fetch-current "magit-remote" "\ +Fetch from the upstream repository of the current branch. +If `HEAD' is detached or if the upstream is not configured, +then read the remote. + +\(fn REMOTE &optional ARGS)" t nil) + +(autoload 'magit-fetch "magit-remote" "\ +Fetch from another repository. + +\(fn REMOTE &optional ARGS)" t nil) + +(autoload 'magit-fetch-all "magit-remote" "\ +Fetch from all configured remotes. + +\(fn &optional ARGS)" t nil) + (autoload 'magit-pull-popup "magit-remote" nil t) + +(autoload 'magit-pull-current "magit-remote" "\ +Fetch and merge into current branch. + +\(fn REMOTE BRANCH &optional ARGS)" t nil) + +(autoload 'magit-pull "magit-remote" "\ +Fetch from another repository and merge a fetched branch. + +\(fn REMOTE BRANCH &optional ARGS)" t nil) + (autoload 'magit-push-popup "magit-remote" nil t) + +(autoload 'magit-push-current "magit-remote" "\ +Push the current branch to its upstream branch. +If the upstream isn't set, then read the remote branch. + +\(fn BRANCH REMOTE &optional REMOTE-BRANCH ARGS)" t nil) + +(autoload 'magit-push "magit-remote" "\ +Push a branch to its upstream branch. +If the upstream isn't set, then read the remote branch. + +\(fn BRANCH REMOTE &optional REMOTE-BRANCH ARGS)" t nil) + +(autoload 'magit-push-elsewhere "magit-remote" "\ +Push a branch or commit to some remote branch. +Read the local and remote branch. + +\(fn BRANCH REMOTE REMOTE-BRANCH &optional ARGS)" t nil) + +(autoload 'magit-push-matching "magit-remote" "\ +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. + +\(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-patch-popup "magit-remote" nil t) + +(autoload 'magit-format-patch "magit-remote" "\ +Create patches for the commits in RANGE. + +\(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" (21913 +;;;;;; 4083 0 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 "magit-sequence" "\ +Start a non-interactive rebase sequence. +All commits not in UPSTREAM are rebased. + +\(git rebase UPSTREAM[^] [ARGS]) + +\(fn UPSTREAM &optional ARGS)" t nil) + +(autoload 'magit-rebase-from "magit-sequence" "\ +Start a non-interactive rebase sequence. +Commits from START to `HEAD' onto NEWBASE. START has to be +selected from a list of recent commits. + +\(git rebase --onto NEWBASE START[^] [ARGS]) + +\(fn NEWBASE START &optional ARGS)" t nil) + +(autoload 'magit-rebase-interactive "magit-sequence" "\ +Start an interactive rebase sequence. + +\(git rebase -i COMMIT[^] [ARGS]) + +\(fn COMMIT &optional ARGS)" t nil) + +(autoload 'magit-rebase-autosquash "magit-sequence" "\ +Combine squash and fixup commits with their intended targets. + +\(git rebase -i COMMIT[^] --autosquash [ARGS]) + +\(fn COMMIT &optional ARGS)" t nil) + +(autoload 'magit-rebase-edit-commit "magit-sequence" "\ +Edit a single older commit using rebase. + +\(fn COMMIT &optional ARGS)" t nil) + +(autoload 'magit-rebase-reword-commit "magit-sequence" "\ +Reword a single older commit using rebase. + +\(fn COMMIT &optional 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" (21913 4083 +;;;;;; 0 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. + +\(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-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 NOSELECT ARGS)" t nil) + +;;;*** + +;;;### (autoloads nil "magit-wip" "magit-wip.el" (21913 4083 0 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 + +\(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") +;;;;;; (21913 4083 685633 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; magit-autoloads.el ends here diff --git a/elpa/magit-20150704.855/magit-bisect.el b/elpa/magit-20150704.855/magit-bisect.el new file mode 100644 index 00000000..28a66271 --- /dev/null +++ b/elpa/magit-20150704.855/magit-bisect.el @@ -0,0 +1,193 @@ +;;; magit-bisect.el --- bisect support for Magit + +;; 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. + +;;; 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 'magit-popup-sequence-mode + :man-page "git-bisect" + :actions '((?B "Start" magit-bisect-start) + (?a "Run" magit-bisect-run)) + :sequence-actions '((?r "Reset" magit-bisect-reset) + (?b "Bad" magit-bisect-bad) + (?g "Good" magit-bisect-good) + (?k "Skip" magit-bisect-skip)) + :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") + (let ((b (magit-read-branch-or-commit "Start bisect with bad revision"))) + (list b (magit-read-other-branch-or-commit "Good revision" b))))) + (magit-bisect-async "start" (list bad good) t)) + +;;;###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-bisect-async "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-bisect-async "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-bisect-async "skip")) + +;;;###autoload +(defun magit-bisect-run (cmdline) + "Bisect automatically by running commands after each step." + (interactive (list (read-shell-command "Bisect shell command: "))) + (magit-bisect-async "run" (list cmdline))) + +(defun magit-bisect-async (subcommand &optional args no-assert) + (unless (or no-assert (magit-bisect-in-progress-p)) + (user-error "Not bisecting")) + (magit-with-toplevel + (let ((file (magit-git-dir "BISECT_CMD_OUTPUT"))) + (ignore-errors (delete-file file)) + (magit-run-git-with-logfile file "bisect" subcommand args)) + (magit-process-wait) + (magit-refresh))) + +(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) + (magit-insert (propertize heading 'face + 'magit-section-secondary-heading)) + (magit-insert-heading) + (magit-wash-sequence + (apply-partially 'magit-log-wash-line '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) + (magit-insert (concat 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-20150704.855/magit-blame.el b/elpa/magit-20150704.855/magit-blame.el new file mode 100644 index 00000000..94b88ad7 --- /dev/null +++ b/elpa/magit-20150704.855/magit-blame.el @@ -0,0 +1,470 @@ +;;; magit-blame.el --- blame support for Magit + +;; Copyright (C) 2012-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. + +;;; 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 used for blame headings." + :group 'magit-blame + :type 'regexp) + +(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 on the latter." + :group 'magit-blame + :type '(repeat function)) + +(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)) + +(defcustom magit-blame-goto-chunk-hook '(magit-log-maybe-show-commit) + "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-log-maybe-show-commit)) + +(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) + 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-disabled-modes 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." + :lighter magit-blame-mode-lighter + (cond (magit-blame-mode + (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)) + (unless (eq this-command 'magit-blame) + (unless (magit-file-relative-name) + (user-error "Current buffer has no associated file")) + (let ((magit-blame-mode nil)) + (call-interactively 'magit-blame)))) + (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)))))))) + +(defadvice auto-revert-handler (around magit-blame activate) + "If Magit-Blame mode is on, then do nothing. +See #1731." + (unless magit-blame-mode + ad-do-it)) + +;;;###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" + magit-popup-read-number) + (?M "Detect lines moved or copied between files" "-M" + magit-popup-read-number)) + :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) + (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) + (find-file (expand-file-name file))) + (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)) + (setq this-command 'magit-blame) + (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)))) + 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) + (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") + (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)) + (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-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 (plist-get chunk :summary) + 'face 'magit-blame-summary)) + (?a . ,(propertize (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 (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-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))))) + +;;; 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-20150704.855/magit-commit.el b/elpa/magit-20150704.855/magit-commit.el new file mode 100644 index 00000000..6630c410 --- /dev/null +++ b/elpa/magit-20150704.855/magit-commit.el @@ -0,0 +1,372 @@ +;;; magit-commit.el --- create Git commits + +;; Copyright (C) 2008-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. + +;;; 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-ask-to-stage t + "Whether to ask to stage everything when committing and nothing is staged." + :package-version '(magit . "2.1.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-extend-override-date nil + "Whether using `magit-commit-extend' changes the committer date." + :package-version '(magit . "2.1.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-reword-override-date nil + "Whether using `magit-commit-reword' changes the committer date." + :package-version '(magit . "2.1.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 + +;;;###autoload (autoload 'magit-commit-popup "magit-commit" nil t) +(with-no-warnings ; quiet byte-compiler +(magit-define-popup magit-commit-popup + "Popup console for commit commands." + 'magit-commands + :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=" read-from-minibuffer) + (?S "Sign using gpg" "--gpg-sign=" magit-read-gpg-secret-key) + (?C "Reuse commit message" "--reuse-message=" read-from-minibuffer)) + :actions '((?c "Commit" magit-commit) + (?e "Extend" magit-commit-extend) + (?f "Fixup" magit-commit-fixup) + (?F "Instant Fixup" magit-commit-instant-fixup) + (?a "Amend" magit-commit-amend) + (?w "Reword" magit-commit-reword) + (?s "Squash" magit-commit-squash) + (?S "Instant Squash" magit-commit-instant-squash)) + :max-action-columns 4 + :default-action 'magit-commit)) + +(defun magit-commit-message-buffer () + (let ((topdir (magit-toplevel))) + (--first (equal topdir (with-current-buffer it + (and git-commit-mode (magit-toplevel)))) + (append (buffer-list (selected-frame)) + (buffer-list))))) + +(defadvice magit-commit-popup (around pop-to-ongoing activate) + (--if-let (magit-commit-message-buffer) (switch-to-buffer it) ad-do-it)) + +;;;###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 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. +\n(git commit --amend --no-edit)" + (interactive (list (magit-commit-arguments) + (if current-prefix-arg + (not magit-commit-reword-override-date) + magit-commit-reword-override-date))) + (when (setq args (magit-commit-assert args (not override-date))) + (let ((process-environment process-environment)) + (unless override-date + (setenv "GIT_COMMITTER_DATE" (magit-rev-format "%cd"))) + (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 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. +\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 + (setenv "GIT_COMMITTER_DATE" (magit-rev-format "%cd"))) + (magit-run-git-with-editor "commit" "--amend" "--only" args))) + +;;;###autoload +(defun magit-commit-fixup (&optional commit args confirm) + "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'. +\n(git commit --no-edit --fixup=COMMIT [ARGS])" + (interactive (magit-commit-squash-read-args)) + (magit-commit-squash-internal 'magit-commit-fixup "--fixup" + commit args confirm)) + +;;;###autoload +(defun magit-commit-squash (&optional commit args confirm) + "Create a squash 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'. +\n(git commit --no-edit --squash=COMMIT [ARGS])" + (interactive (magit-commit-squash-read-args)) + (magit-commit-squash-internal 'magit-commit-squash "--squash" + commit args confirm)) + +;;;###autoload +(defun magit-commit-instant-fixup (&optional commit args) + "Create a fixup commit and instantly rebase. +\n(git commit --no-edit --fixup=COMMIT ARGS; + git rebase -i COMMIT^ --autosquash --autostash)" + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal + (lambda (c a) + (when (setq c (magit-commit-fixup c a)) + (magit-rebase-autosquash (concat c "^") (list "--autostash")))) + "--fixup" commit args t)) + +;;;###autoload +(defun magit-commit-instant-squash (&optional commit args) + "Create a squash commit and instantly rebase. +\n(git commit --no-edit --squash=COMMIT ARGS; + git rebase -i COMMIT^ --autosquash --autostash)" + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal + (lambda (c a) + (when (setq c (magit-commit-squash c a)) + (magit-rebase-autosquash (concat c "^") (list "--autostash")))) + "--squash" commit args t)) + +(defun magit-commit-squash-read-args () + (list (magit-commit-at-point) + (magit-commit-arguments) + (or current-prefix-arg magit-commit-squash-confirm))) + +(defun magit-commit-squash-internal (fn option commit args confirm) + (when (setq args (magit-commit-assert args t)) + (if (and commit (not confirm)) + (let ((magit-diff-auto-show nil)) + (magit-run-git-with-editor "commit" "--no-edit" + (concat option "=" commit) args) + commit) + (magit-log-select + `(lambda (commit) (,fn commit (list ,@args))) + "Type %p on the commit to squash/fixup into it,") + (when (magit-diff-auto-show-p 'log-select) + (let ((magit-diff-switch-buffer-function 'display-buffer)) + (magit-diff-staged)))))) + +(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 (magit-diff-auto-show-p 'stage-all) + (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 (magit-diff-auto-show-p 'stage-all) + (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-diff-auto-show-p 'commit) + (pcase last-command + (`magit-commit 'magit-diff-staged) + (`magit-commit-amend 'magit-diff-while-amending) + (`magit-commit-reword 'magit-diff-while-amending))) + (setq with-editor-previous-winconf (current-window-configuration)) + (let ((magit-inhibit-save-previous-winconf 'unset) + (magit-diff-switch-buffer-function 'display-buffer)) + (funcall it)))) + +(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 hunk 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 ((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 + (file-relative-name buffer-file-name (magit-toplevel)) + (add-log-current-defun)))))) + +(defun magit-commit-add-log-insert (buffer file defun) + (with-current-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 defun + (insert (format " (%s)" defun))) + (insert ": ")) + (defun + ;; found entry for file, look for defun + (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 defun)) + 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): " defun)))))) + (t + ;; found entry for file, look for its beginning + (when (looking-at ":") + (forward-char 2)))))) + +;;; 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-20150704.855/magit-core.el b/elpa/magit-20150704.855/magit-core.el new file mode 100644 index 00000000..844671b5 --- /dev/null +++ b/elpa/magit-20150704.855/magit-core.el @@ -0,0 +1,76 @@ +;;; magit-core.el --- core functionality + +;; Copyright (C) 2010-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. + +;;; 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) + +(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-20150704.855/magit-diff.el b/elpa/magit-20150704.855/magit-diff.el new file mode 100644 index 00000000..f6d149bd --- /dev/null +++ b/elpa/magit-20150704.855/magit-diff.el @@ -0,0 +1,1779 @@ +;;; magit-diff.el --- inspect Git diffs + +;; Copyright (C) 2010-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. + +;;; 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-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) + +(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-buffer-name-format "*magit-diff: %a*" + "Name format for buffers used to display a diff. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :package-version '(magit . "2.1.0") + :group 'magit-diff + :type 'string) + +(defcustom magit-diff-switch-buffer-function 'pop-to-buffer + "Function used to display and possibly select a diff buffer. + +By default `pop-to-buffer' is used to display the diff buffer in +another window. If the value is nil, then that function is also +used, except when the current buffer is another Magit buffer. +Then the window is reused; the diff buffer replaces the buffer +which was previously shown. Another function can be used, but +that's not recommended, e.g. `switch-to-buffer' likely is not +what you want. + +Note that the value of this variable is ignored when the diff +buffer is automatically shown along side a buffer used to write +a commit message." + :package-version '(magit . "2.1.0") + :group 'magit-diff + :type '(choice (function-item pop-to-buffer) + (function nil) + (const :tag "Context sensitive (nil)" nil))) + +(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 treshold +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-treshold' 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-show-diffstat t + "Whether to show diffstat in diff buffers." + :package-version '(magit . "2.1.0") + :group 'magit-diff + :type 'boolean) + +(defcustom magit-diff-show-xref-buttons t + "Whether to show buffer history buttons in diff buffers." + :package-version '(magit . "2.1.0") + :group 'magit-diff + :type 'boolean) + +(defcustom magit-diff-auto-show + '(commit stage-all log-oneline log-select blame-follow) + "Whether to automatically show relevant diff or commit. + +When this option is non-nil certain operations cause the relevant +changes to be displayed automatically. + +`commit' +`stage-all' +`log-oneline' +`log-follow' +`log-select' +`blame-follow' + +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 . "2.1.0") + :group 'magit-diff + :type 'sexp) + +(defun magit-diff-auto-show-p (op) + (if (eq (car magit-diff-auto-show) 'not) + (not (memq op (cdr magit-diff-auto-show))) + (memq op magit-diff-auto-show))) + +(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-buffer-name-format "*magit-rev: %a*" + "Name format for buffers used to display a commit. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :package-version '(magit . "2.1.0") + :group 'magit-revision + :type 'string) + +(defcustom magit-revision-show-diffstat t + "Whether to show diffstat in revision buffers." + :package-version '(magit . "2.1.0") + :group 'magit-revision + :type 'boolean) + +(defcustom magit-revision-show-notes t + "Whether to show notes in revision buffers." + :package-version '(magit . "2.1.0") + :group 'magit-revision + :safe 'booleanp + :type 'boolean) + +(defcustom magit-revision-show-xref-buttons t + "Whether to show buffer history buttons in revision buffers." + :package-version '(magit . "2.1.0") + :group 'magit-revision + :type 'boolean) + +(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) + +;;; 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 "#aaccaa")) + "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 "#ccaaaa")) + "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 "#ccccbb")) + "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 "#bbddbb")) + "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 "#ddbbbb")) + "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 "#ddddaa")) + "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 "grey85" + :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 + +;;;###autoload (autoload 'magit-diff-popup "magit-diff" nil t) +(magit-define-popup magit-diff-popup + "Popup console for diff commands." + 'magit-diff + :man-page "git-diff" + :switches '((?f "Show surrounding functions" "--function-context") + (?b "Ignore whitespace changes" "--ignore-space-change") + (?w "Ignore all whitespace" "--ignore-all-space")) + :options '((?f "Limit to files" "-- " magit-read-files) + (?u "Context lines" "-U" read-from-minibuffer) + (?m "Detect renames" "-M" read-from-minibuffer) + (?c "Detect copies" "-C" read-from-minibuffer) + (?a "Diff algorithm" "--diff-algorithm=" + magit-diff-select-algorithm)) + :actions '((?d "Dwim" magit-diff-dwim) + (?u "Diff unstaged" magit-diff-unstaged) + (?c "Show commit" magit-show-commit) + (?r "Diff commits" 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) + +(defun magit-diff-read-args (&optional no-files) + (let* ((args (magit-diff-arguments)) + (files (--first (string-prefix-p "-- " it) args))) + (when files + (setq args (delete files args) + files (split-string (substring files 3) ","))) + (if no-files + (list args) + (list args files)))) + +(with-no-warnings + (magit-define-popup magit-diff-refresh-popup + "Popup console for changing diff arguments in the current buffer." + 'magit-diff nil 'magit-diff-section-arguments + :man-page "git-diff" + :switches '((?f "Show surrounding functions" "--function-context") + (?b "Ignore whitespace changes" "--ignore-space-change") + (?w "Ignore all whitespace" "--ignore-all-space")) + :options '((?u "Context lines" "-U" read-from-minibuffer) + (?m "Detect renames" "-M" read-from-minibuffer) + (?c "Detect copies" "-C" read-from-minibuffer) + (?a "Diff algorithm" "--diff-algorithm=" + magit-diff-select-algorithm)) + :actions '((?g "Refresh" magit-diff-refresh) + (?s "Set defaults" magit-diff-set-default-arguments) + (?w "Save defaults" magit-diff-save-default-arguments) + (?t "Toggle hunk refinement" magit-diff-toggle-refine-hunk)))) + +(defadvice magit-diff-refresh-popup (around get-current-arguments activate) + (if (derived-mode-p 'magit-diff-mode) + (let ((magit-diff-section-arguments (cadr magit-refresh-args))) + ad-do-it) + ad-do-it)) + +(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-read-args)) + (--if-let (magit-region-values 'commit 'branch) + (progn (deactivate-mark) + (magit-diff (concat (car (last it)) ".." (car it)))) + (--when-let (magit-current-section) + (let ((value (magit-section-value it))) + (magit-section-case + ([* unstaged] (magit-diff-unstaged args)) + ([* staged] (magit-diff-staged nil args)) + (unpushed (magit-diff-unpushed args)) + (unpulled (magit-diff-unpulled args)) + (branch (-if-let (tracked (magit-get-tracked-ref value)) + (magit-diff (format "%s...%s" tracked value) args) + (call-interactively 'magit-diff))) + (commit (magit-show-commit value nil nil args)) + (stash (magit-stash-show value nil args)) + (t (call-interactively 'magit-diff))))))) + +;;;###autoload +(defun magit-diff (range &optional args files) + "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 (cons (magit-read-range-or-commit "Diff for range") + (magit-diff-read-args))) + (magit-mode-setup magit-diff-buffer-name-format + magit-diff-switch-buffer-function + #'magit-diff-mode + #'magit-diff-refresh-buffer range 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-read-args))) + (magit-diff (or rev "HEAD") 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-read-args))) + (magit-diff rev (cons "--cached" args))) + +;;;###autoload +(defun magit-diff-unstaged (&optional args files) + "Show changes between the working tree and the index." + (interactive (magit-diff-read-args)) + (magit-diff nil args files)) + +;;;###autoload +(defun magit-diff-unpushed (&optional args files) + "Show unpushed changes." + (interactive (magit-diff-read-args)) + (-if-let (tracked (magit-get-tracked-ref)) + (magit-diff (concat tracked "...") args files) + (error "Detached HEAD or upstream unset"))) + +;;;###autoload +(defun magit-diff-unpulled (&optional args files) + "Show unpulled changes." + (interactive (magit-diff-read-args)) + (-if-let (tracked (magit-get-tracked-ref)) + (magit-diff (concat "..." tracked) args files) + (error "Detached HEAD or upstream unset"))) + +;;;###autoload +(defun magit-diff-while-committing (&optional args) + "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 commited." + (interactive (magit-diff-read-args t)) + (let* ((toplevel (magit-toplevel)) + (diff-buf (magit-mode-get-buffer magit-diff-buffer-name-format + 'magit-diff-mode toplevel))) + (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) + (magit-diff-staged nil args)) + (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) + (magit-diff "HEAD^" (cons "--cached" args))) + +;;;###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 nil + (list "--no-index") + (list (expand-file-name a) + (expand-file-name b)))) + +(defvar-local magit-diff-hidden-files nil) +(put 'magit-diff-hidden-files 'permanent-local t) + +;;;###autoload +(defun magit-show-commit (commit &optional noselect module args) + "Show the commit at point. +If there is no commit at point or with a prefix argument prompt +for a commit." + (interactive + (let* ((mcommit (magit-section-when module-commit)) + (atpoint (or (and magit-blame-mode (magit-blame-chunk-get :hash)) + mcommit + (magit-branch-or-commit-at-point) + (magit-tag-at-point)))) + (nconc (list (or (and (not current-prefix-arg) atpoint) + (magit-read-branch-or-commit "Show commit" atpoint)) + nil (and mcommit (magit-section-parent-value + (magit-current-section)))) + (magit-diff-read-args t)))) + (let ((default-directory (if module + (file-name-as-directory + (expand-file-name module (magit-toplevel))) + default-directory))) + (unless (magit-rev-verify commit) + (user-error "%s is not a commit" commit)) + (-when-let (buffer (magit-mode-get-buffer + magit-revision-buffer-name-format + 'magit-revision-mode)) + (with-current-buffer buffer + (let ((prev (car magit-refresh-args))) + (unless (equal commit prev) + (dolist (child (cdr (magit-section-children magit-root-section))) + (when (eq (magit-section-type child) 'file) + (let ((file (magit-section-value child))) + (if (magit-section-hidden child) + (add-to-list 'magit-diff-hidden-files file) + (setq magit-diff-hidden-files + (delete file magit-diff-hidden-files)))))))))) + (magit-mode-setup magit-revision-buffer-name-format + (if noselect 'display-buffer 'pop-to-buffer) + #'magit-revision-mode + #'magit-revision-refresh-buffer + commit args))) + +(defun magit-diff-refresh-arguments () + (cond ((memq magit-current-popup '(magit-diff-popup magit-diff-refresh-popup)) + magit-current-popup-args) + ((derived-mode-p 'magit-diff-mode) + (--filter (not (member it '("--cached" "--no-index" "--"))) + (cadr magit-refresh-args))) + (t + magit-diff-section-arguments))) + +(defun magit-diff-refresh (args) + "Set the local diff arguments for the current buffer." + (interactive (list (magit-diff-refresh-arguments))) + (cond ((derived-mode-p 'magit-diff-mode) + (setq magit-refresh-args (list (car magit-refresh-args) args))) + (t + (setq magit-diff-section-arguments args))) + (magit-refresh)) + +(defun magit-diff-set-default-arguments (args) + "Set the global diff arguments for the current buffer." + (interactive (list (magit-diff-refresh-arguments))) + (cond ((derived-mode-p 'magit-diff-mode) + (customize-set-variable 'magit-diff-arguments args) + (setq magit-refresh-args (list (car magit-refresh-args) args))) + (t + (customize-set-variable 'magit-diff-section-arguments args) + (kill-local-variable 'magit-diff-section-arguments))) + (magit-refresh)) + +(defun magit-diff-save-default-arguments (args) + "Set and save the global diff arguments for the current buffer." + (interactive (list (magit-diff-refresh-arguments))) + (cond ((derived-mode-p 'magit-diff-mode) + (customize-save-variable 'magit-diff-arguments args) + (setq magit-refresh-args (list (car magit-refresh-args) args))) + (t + (customize-save-variable 'magit-diff-section-arguments args) + (kill-local-variable 'magit-diff-section-arguments))) + (magit-refresh)) + +(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 (magit-diff-refresh-arguments)) + (arg (--first (string-match "^-U\\([0-9]+\\)?$" it) val)) + (num (--if-let (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) + (setq magit-refresh-args (list (car 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) + (magit-diff-arguments)) + (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 (or (magit-file-at-point) + (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 diff hunk line col buffer) + (when (and (derived-mode-p 'magit-revision-mode) + (not force-worktree)) + (setq rev (car (last magit-refresh-args 2))) + (when (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 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)))) + (if (or other-window (get-buffer-window buffer)) + (pop-to-buffer buffer) + (switch-to-buffer buffer)) + (when line + (goto-char (point-min)) + (forward-line (1- line)) + (when col + (move-to-column col))))) + (when (magit-anything-unmerged-p file) + (smerge-start-session))) + +(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-worktree' 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)) + (stop (line-number-at-pos)) + (line (car (last value)))) + (string-match "^\\+\\([0-9]+\\)" line) + (setq line (string-to-number (match-string 1 line))) + (save-excursion + (goto-char (magit-section-content section)) + (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 (looking-at "-") + 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) + (magit-status-internal directory (if other-window + 'pop-to-buffer + 'switch-to-buffer)))) + +(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) + (if magit-blame-mode + (setq rev (magit-blame-chunk-get :hash) + cmd 'magit-show-commit + buf (magit-mode-get-buffer + magit-revision-buffer-name-format 'magit-revision-mode)) + (magit-section-case + (commit (setq rev (magit-section-value it) + cmd 'magit-show-commit + buf (magit-mode-get-buffer + magit-revision-buffer-name-format 'magit-revision-mode))) + (stash (setq rev (magit-section-value it) + cmd 'magit-stash-show + buf (magit-mode-get-buffer + magit-diff-buffer-name-format '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 err + (funcall fn) + (error + (goto-char (pcase fn + (`scroll-up (point-min)) + (`scroll-down (point-max))))))) + (funcall cmd rev t)) + (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)Diffing'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-diff-visit-file] to visit the file at point. +Type \\[magit-apply] to apply the change at point to the worktree. +Type \\[magit-reverse] to reverse the change at point in the worktree. +\n\\{magit-diff-mode-map}" + :group 'magit-diff + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-diff-refresh-buffer (range &optional args files) + (setq header-line-format + (propertize + (if (member "--no-index" args) + (apply #'format " Differences between %s and %s" files) + (concat (if range + (if (string-match-p "\\.\\." range) + (format " Changes in %s" range) + (format " Changes from %s to working tree" range)) + (if (member "--cached" args) + " 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) + (magit-git-wash #'magit-diff-wash-diffs + "diff" range "-p" (and magit-diff-show-diffstat "--stat") + "--no-prefix" args "--" files))) + +(defvar magit-file-section-map + (let ((map (make-sparse-keymap))) + (define-key map [C-return] 'magit-diff-visit-file-worktree) + (define-key map "\r" 'magit-diff-visit-file) + (define-key map "a" 'magit-apply) + (define-key map "k" 'magit-discard) + (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) + (define-key map "v" 'magit-reverse) + 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 "\r" 'magit-diff-visit-file) + (define-key map "a" 'magit-apply) + (define-key map "C" 'magit-commit-add-log) + (define-key map "k" 'magit-discard) + (define-key map "s" 'magit-stage) + (define-key map "u" 'magit-unstage) + (define-key map "v" 'magit-reverse) + map) + "Keymap for `hunk' sections.") + +(defconst magit-diff-headline-re + (concat "^\\(@@@?\\|diff\\|Submodule\\|" + "\\* Unmerged path\\|merged\\|changed in both\\)")) + +(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 diffstats) + (unless diffstats + (setq diffstats (magit-diff-wash-diffstat))) + (when (re-search-forward magit-diff-headline-re nil t) + (goto-char (line-beginning-position)) + (magit-wash-sequence + (lambda () + (magit-diff-wash-diff args (pop diffstats)))) + (insert ?\n)) + (goto-char (point-max)) + (magit-xref-insert-buttons)) + +(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) + ,@(and (derived-mode-p 'magit-revision-mode) + '((headers))))) + (t (if (derived-mode-p 'magit-revision-mode) + '((diffstat) (headers)) + '((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 children (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 heading) + (magit-insert-heading) + (magit-wash-sequence + (lambda () + (when (looking-at magit-diff-statline-re) + (magit-bind-match-strings (file sep cnt add del) nil + (magit-delete-line) + (magit-insert-section (file file) + (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")))))) + (setq children (magit-section-children it)))) + children)) + +(defun magit-diff-wash-diff (args diffstat) + (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) + (magit-insert (propertize (format "unmerged %s" file) + 'face 'magit-diff-file-heading) nil ?\n)))) + t) + ((looking-at "^\\(merged\\|changed in both\\)") + (let ((status (if (equal (match-string 1) "merged") 'merged 'conflict)) + file orig modes blobs blobA blobB) + (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 blobB blob)) + ("our" (setq orig name blobA blob)) + ("their" (setq file name blobB blob)))) + (magit-delete-line)) + (setq orig (magit-decode-git-path orig)) + (setq file (magit-decode-git-path file)) + (magit-diff-insert-file-section file orig status modes + (concat blobA ".." blobB)))) + ((looking-at "^diff --\\(git\\|cc\\|combined\\) \\(?:\\(.+?\\) \\2\\)?") + (let ((status (cond ((equal (match-string 1) "git") "modified") + ((derived-mode-p 'magit-revision-mode) "resolved") + (t "unmerged"))) + (orig (match-string 2)) + (file (match-string 2)) + modes blobs) + (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))) + ((looking-at "^index \\([^\s\n]+\\)") + (setq blobs (match-string 1)))) + (magit-delete-line))) + (setq orig (magit-decode-git-path orig)) + (setq file (magit-decode-git-path file)) + (when diffstat + (setf (magit-section-value diffstat) file)) + (magit-diff-insert-file-section file orig status modes blobs))))) + +(defun magit-diff-insert-file-section (file orig status modes blobs) + (magit-insert-section section + (file file (or (equal status "deleted") + (derived-mode-p 'magit-status-mode))) + (insert (propertize (format "%-10s %s\n" status + (if (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-blobs section) blobs) + (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) + (magit-insert + (concat (propertize (if new + (concat "new module " module) + (concat "modified " module)) + 'face 'magit-diff-file-heading) + (cond (dirty " (modified content)") + (deleted " (deleted submodule)"))) + nil ?\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 magit-diff-headline-re))) + (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 to 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" + "Mode for looking at a Git commit. +This mode is documented in info node `(magit)Commit Buffer'. + +\\\ +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-diff-visit-file] to visit the hunk or file at point. +Type \\[magit-apply] to apply the change at point to the worktree. +Type \\[magit-reverse] to reverse the change at point in the worktree. +\n\\{magit-revision-mode-map}" + :group 'magit-revision + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-revision-refresh-buffer (commit args) + (magit-insert-section (commitbuf) + (magit-git-wash #'magit-diff-wash-revision + "show" "-p" "--cc" "--decorate=full" "--format=fuller" "--no-prefix" + (and magit-revision-show-diffstat "--stat") + (and magit-revision-show-notes "--notes") + args commit "--"))) + +(defun magit-diff-wash-revision (args) + (magit-diff-wash-tag) + (let (children) + (looking-at "^commit \\([a-z0-9]+\\)\\(?: \\(.+\\)\\)?$") + (magit-bind-match-strings (rev refs) nil + (magit-delete-line) + (setq header-line-format + (propertize (concat " Commit " rev) 'face 'magit-header-line)) + (magit-insert-section (headers) + (magit-insert-heading (char-to-string magit-ellipsis)) + (when refs + (magit-insert (format "References: %s\n" + (magit-format-ref-labels refs)))) + (while (looking-at "^\\([a-z]+\\):") + (when (string-equal (match-string 1) "Merge") + (magit-delete-line)) + (forward-line 1)) + (re-search-forward "^\\(\\(---\\)\\| .\\)") + (goto-char (line-beginning-position)) + (if (match-string 2) + (progn (magit-delete-match) + (insert ?\n) + (magit-insert-section (message) + (insert " (no message)\n"))) + (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)))) + (magit-delete-line) + (magit-insert-section (message) + (insert summary ?\n) + (magit-insert-heading) + (cond ((re-search-forward "^---" bound t) + (magit-delete-match)) + ((re-search-forward "^.[^ ]" bound t) + (goto-char (1- (match-beginning 0)))))))) + (forward-line) + (when magit-revision-insert-related-refs + (magit-revision-insert-related-refs rev)) + (setq children (magit-diff-wash-diffstat)) + (forward-line))) + (magit-diff-wash-diffs args children))) + +(defun magit-diff-wash-tag () + (when (looking-at "^tag \\(.+\\)$") + (let ((tag (match-string 1))) + (magit-delete-line) + (magit-insert-section (tag tag) + (magit-insert-heading + (propertize "Tag " 'face 'magit-section-heading) + (propertize tag 'face 'magit-tag)) + (while (looking-at "^\\([a-z]+\\):") + (forward-line)) + (forward-line) + (magit-insert-section (message) + (forward-line) + (magit-insert-heading) + (while (not (looking-at "\ncommit [a-z0-9]\\{40\\}")) + (forward-line))) + (forward-line))))) + +(defun magit-revision-insert-related-refs (rev) + (let ((parents (magit-commit-parents rev)) + (merged (magit-list-merged-branches rev)) + (containing (magit-list-containing-branches rev)) + (follows (magit-get-current-tag rev t)) + (precedes (magit-get-next-tag rev t)) + branch) + (magit-insert-section (related-refs) + (magit-insert + (let ((p (length parents)) (m (length merged)) (s (length containing))) + (format "%s parent commit%s, %s merged branch%s, %s containing branch%s\n" + p (if (= p 1) "" "s") + m (if (= m 1) "" "es") + s (if (= s 1) "" "es")))) + (magit-insert-heading) + (dolist (commit parents) + (magit-insert-section (commit commit) + (let ((line (magit-rev-format "%h %s" commit))) + (string-match "^\\([^ ]+\\) \\(.*\\)" line) + (magit-bind-match-strings (commit msg) line + (insert " Parent | ") + (insert (propertize commit 'face 'magit-hash)) + (insert " " msg "\n"))))) + (when merged + (insert " Merged |") + (while (and (< (+ (- (point) (line-beginning-position)) + (length (car merged)) 9) + (window-width)) + (setq branch (pop merged))) + (insert ?\s) + (magit-insert-section (branch branch) + (magit-insert branch 'magit-branch-local))) + (when merged + (insert (format " (%s more)" (length merged)))) + (insert ?\n)) + (when containing + (insert " Containing |") + (while (and (< (+ (- (point) (line-beginning-position)) + (length (car containing)) 9) + (window-width)) + (setq branch (pop containing))) + (insert ?\s) + (magit-insert-section (branch branch) + (magit-insert branch 'magit-branch-local))) + (when containing + (insert (format " (%s more)" (length containing)))) + (insert ?\n)) + (when follows + (let ((tag (car follows)) + (cnt (cadr follows))) + (magit-insert-section (tag tag) + (magit-insert (format " Follows | %s (%s)\n" + (propertize tag 'face 'magit-tag) + (propertize (number-to-string cnt) + 'face 'magit-branch-local)))))) + (when precedes + (let ((tag (car precedes)) + (cnt (cadr precedes))) + (magit-insert-section (tag tag) + (magit-insert (format " Precedes | %s (%s)\n" + (propertize tag 'face 'magit-tag) + (propertize (number-to-string cnt) + 'face 'magit-tag)))))) + (insert ?\n)))) + +(defun magit-revision-set-visibility (section) + "Preserve section visibility when displaying another commit in." + (and (derived-mode-p 'magit-revision-mode) + (eq (magit-section-type section) 'file) + (member (magit-section-value section) magit-diff-hidden-files) + 'hide)) + +;;; Diff Sections + +(defvar magit-unstaged-section-map + (let ((map (make-sparse-keymap))) + (define-key map "\r" 'magit-diff-unstaged) + (define-key map "k" 'magit-discard) + (define-key map "s" 'magit-stage) + (define-key map "u" 'magit-unstage) + map) + "Keymap for the `unstaged' section.") + +(magit-define-section-jumper unstaged "Unstaged changes") + +(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"))) + +(defvar magit-staged-section-map + (let ((map (make-sparse-keymap))) + (define-key map "\r" 'magit-diff-staged) + (define-key map "k" 'magit-discard) + (define-key map "s" 'magit-stage) + (define-key map "u" 'magit-unstage) + (define-key map "v" 'magit-reverse) + map) + "Keymap for the `staged' section.") + +(magit-define-section-jumper staged "Staged changes") + +(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"))) + +;;; 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-diff-mode) + (if (car magit-refresh-args) + 'undefined + (if (member "--cached" (cadr magit-refresh-args)) + 'staged + 'unstaged))) + ((derived-mode-p 'magit-revision-mode 'magit-stash-mode) '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)))))) + (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 + `stashed-index `stashed-worktree `stashed-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." + (-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) + (if (magit-section-match 'module-commit section) + (magit-section-highlight section nil) + (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))))) + +(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]+\\)$"))) + (magit-put-face-property (match-beginning 1) (match-end 1) + 'magit-diff-whitespace-warning)) + (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-diff-whitespace-warning))))) + +(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) + (let* ((file (magit-section-value section)) + (orig (or (magit-section-source section) file))) + (format "diff --git a/%s b/%s\nindex %s\n--- a/%s\n+++ b/%s\n" + orig file (magit-section-blobs section) orig file)))) + +(defun magit-diff-hunk-region-header (section) + (nth 4 (split-string (magit-diff-hunk-region-patch section) "\n"))) + +(defun magit-diff-hunk-region-patch (section &optional 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")) + (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 (list (magit-diff-file-header section)))) + (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-20150704.855/magit-ediff.el b/elpa/magit-20150704.855/magit-ediff.el new file mode 100644 index 00000000..29c3568c --- /dev/null +++ b/elpa/magit-20150704.855/magit-ediff.el @@ -0,0 +1,330 @@ +;;; magit-ediff.el --- Ediff extension for Magit + +;; Copyright (C) 2010-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. + +;;; Commentary: + +;; This library provides basic support for Ediff. + +;;; Code: + +(require 'magit) + +(require 'ediff) +(require 'smerge-mode) + +;;;###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) + (?d "Compare" magit-ediff-compare) + (?m "Resolve" magit-ediff-resolve) + (?s "Stage" magit-ediff-stage))) + +;;;###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)))))) + (with-current-buffer (find-file-noselect file) + (smerge-ediff))) + +;;;###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)))) + (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))) + (magit-ediff-cleanup-auxiliary-buffers) + (set-window-configuration ,conf))))) + '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." + (interactive (cl-destructuring-bind (range revA revB) + (magit-ediff-compare--read-revisions) + (nconc (list revA revB) + (magit-ediff-compare--read-files range revA revB)))) + (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))) + (magit-ediff-cleanup-auxiliary-buffers) + (set-window-configuration ,conf))))) + 'ediff-revision))) + +(defun magit-ediff-compare--read-revisions (&optional arg) + (let ((input (or arg (magit-read-range-or-commit "Compare range or commit"))) + range revA revB) + (if (string-match + "^\\([^.]+\\)?\\(?:\\.\\.\\(\\.\\)?\\([^.]+\\)?\\)" input) + (progn (setq revA (or (match-string 1 input) "HEAD") + revB (or (match-string 3 input) "HEAD") + range (match-string 0 input)) + (when (match-string 2 input) + (setq revA (magit-git-string "merge-base" revA revB)))) + (setq revA (concat input "^") + revB input)) + (list range revA revB))) + +(defun magit-ediff-compare--read-files (range revA revB &optional fileB) + (unless fileB + (setq fileB (magit-read-file-from-rev + revB (if range + (format "In range %s compare file" range) + (format "Show changes in %s to file" revB))))) + (list (or (car (member fileB (magit-revision-files revA))) + (car (rassoc fileB + (cl-mapcan + (lambda (elt) + (setq elt (split-string (substring elt 97))) + (when (nth 2 elt) + (list (cons (nth 1 elt) (nth 2 elt))))) + (magit-git-items + "diff-tree" "-z" "-M" "HEAD^" "HEAD")))) + (magit-read-file-from-rev + revA (format "Compare %s:%s with file in %s" revB fileB revA))) + 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 users +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))) + (commit (call-interactively 'magit-ediff-compare)) + (t + (let ((command 'magit-ediff-compare) + (file (magit-current-file)) + revA revB range) + (cond (magit-buffer-refname + (setq revB magit-buffer-refname + revA (concat revB "^"))) + ((derived-mode-p 'magit-revision-mode) + (setq revB (car magit-refresh-args) + revA (concat revB "^"))) + ((derived-mode-p 'magit-diff-mode) + (pcase (magit-diff-type) + (`committed (cl-destructuring-bind (r a b) + (magit-ediff-compare--read-revisions + (car magit-refresh-args)) + (setq revA a revB b range r))) + (`undefined (setq command nil)) + (_ (setq command 'magit-ediff-stage)))) + (t + (magit-section-case + (commit (setq revA (concat (magit-section-value it) "^") + revB (magit-section-value it))) + (branch (let ((current (magit-get-current-branch)) + (atpoint (magit-section-value it))) + (setq revA current + revB (if (eq atpoint current) + (magit-get-tracked-branch) + atpoint) + range (concat revA "..." revB)))) + (unpushed (setq revA (magit-get-tracked-branch) + revB (magit-get-current-branch) + range (concat revA ".." revB))) + (unpulled (setq revA (magit-get-current-branch) + revB (magit-get-tracked-branch) + range (concat revA ".." revB))) + ((staged unstaged) + (setq command (if (magit-anything-unmerged-p) + 'magit-ediff-resolve + 'magit-ediff-stage))) + (t + (setq command (cond ((not file) nil) + ((magit-anything-unmerged-p file) + 'magit-ediff-resolve) + (t 'magit-ediff-stage))))))) + (cond ((not command) + (call-interactively + (magit-read-char-case + "Failed to read your mind; do you want to " t + (?c "[c]ompare" 'magit-ediff-compare) + (?r "[r]esolve" 'magit-ediff-resolve) + (?s "[s]tage" 'magit-ediff-stage)))) + ((eq command 'magit-ediff-compare) + (apply 'magit-ediff-compare revA revB + (magit-ediff-compare--read-files range revA revB file))) + (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'." + (interactive + (list (magit-completing-read "Show staged changes for file" nil + (magit-tracked-files) nil nil nil + (magit-current-file)))) + (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))) + (magit-ediff-cleanup-auxiliary-buffers) + (set-window-configuration ,conf))))) + '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'." + (interactive + (list (magit-completing-read "Show unstaged changes for file" nil + (magit-tracked-files) nil nil nil + (magit-current-file)))) + (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))) + (magit-ediff-cleanup-auxiliary-buffers) + (set-window-configuration ,conf))))) + 'ediff-buffers))) + +(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)))) + +;;; 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-20150704.855/magit-extras.el b/elpa/magit-20150704.855/magit-extras.el new file mode 100644 index 00000000..65655834 --- /dev/null +++ b/elpa/magit-20150704.855/magit-extras.el @@ -0,0 +1,195 @@ +;;; magit-extras.el --- additional functionality for Magit + +;; Copyright (C) 2008-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. + +;; 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)))) + (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))))) + (let ((default-directory (magit-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 (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")))) + +;;; 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 (magit-gitignore-read-args 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 &optional local) + "Instruct Git to locally ignore FILE-OR-PATTERN. +\n(fn FILE-OR-PATTERN)" + (interactive (magit-gitignore-read-args t)) + (magit-gitignore file-or-pattern t)) + +(defun magit-gitignore-read-args (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)))) + (list (magit-completing-read + (concat "File or pattern to ignore" (and local " locally")) + choices nil nil nil nil default) + local))) + +;;; 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-20150704.855/magit-git.el b/elpa/magit-20150704.855/magit-git.el new file mode 100644 index 00000000..4a690afb --- /dev/null +++ b/elpa/magit-20150704.855/magit-git.el @@ -0,0 +1,1020 @@ +;;; magit-git.el --- Git functionality + +;; Copyright (C) 2010-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. + +;;; 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-insert-section 'magit-process) +(defvar magit-process-error-message-re) +(defvar magit-refresh-args) ; from `magit-mode' for `magit-current-file' + +(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) + +(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") + (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 to +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 standart error is inserted into this buffer." + :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 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." + :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"))))) + +;;; 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. +* Quote arguments as required when using Powershell together + with Cygwin Git. See #816." + (setq args (-flatten args)) + (when (and (eq system-type 'windows-nt) + (let ((case-fold-search t)) + (string-match-p "cygwin" magit-git-executable))) + (setq args (--map (replace-regexp-in-string + "{\\([0-9]+\\)}" "\\\\{\\1\\\\}" it) + args))) + (append magit-git-global-arguments args)) + +(defun magit-git-exit-code (&rest args) + "Execute Git with ARGS, returning its exit code." + (apply #'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 #'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 report 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 #'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 nil t) + (magit-process-insert-section magit-git-executable + args exit log)))) + (message "%s" msg))) + exit)) + (ignore-errors (delete-file log)))) + (apply #'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))) + (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))))) + +;;; Files + +(defmacro magit--with-safe-default-directory (file &rest body) + (declare (indent 1)) + `(catch 'unsafe-default-dir + (let ((default-directory + (let ((file ,file)) + (file-name-as-directory (if file + (expand-file-name file) + default-directory))))) + (while (not (file-accessible-directory-p default-directory)) + (when (string-equal default-directory "/") + (throw 'unsafe-default-dir nil)) + (setq default-directory + (file-name-directory + (directory-file-name default-directory)))) + ,@body))) + +(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." + (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 file strict) + (magit--with-safe-default-directory file + (-if-let (cdup (magit-rev-parse-safe "--show-cdup")) + (magit-expand-git-file-name + (file-name-as-directory (expand-file-name cdup))) + (unless strict + (-when-let (gitdir (magit-git-dir)) + (if (magit-bare-repo-p) + gitdir + (file-name-directory (directory-file-name gitdir)))))))) + +(defmacro magit-with-toplevel (&rest body) + (declare (indent defun)) + (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) + "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." + (unless file + (with-current-buffer (or (buffer-base-buffer) + (current-buffer)) + (setq file (or magit-buffer-file-name buffer-file-name)))) + (when file + (--when-let (magit-toplevel 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) + (magit-list-files "--other" (unless all "--exclude-standard"))) + +(defun magit-modified-files (&optional nomodules) + (magit-git-items "diff-files" "-z" "--name-only" + (and nomodules "--ignore-submodules"))) + +(defun magit-staged-files (&optional nomodules) + (magit-git-items "diff-index" "-z" "--name-only" "--cached" + (and nomodules "--ignore-submodules") + (magit-headish))) + +(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) + (let ((default-directory (magit-toplevel))) + (magit-git-items "ls-tree" "-z" "-r" "--name-only" rev))) + +(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))) + +(defun magit-expand-git-file-name (filename) + (unless (file-name-absolute-p filename) + (setq filename (expand-file-name filename))) + (if (and (eq system-type 'windows-nt) ; together with cygwin git, see #1318 + (string-match "^/\\(cygdrive/\\)?\\([a-z]\\)/\\(.*\\)" filename)) + (concat (match-string 2 filename) ":/" + (match-string 3 filename)) + 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-equal (a b) + (magit-git-success "diff" "--quiet" a b)) + +(defun magit-rev-head-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-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-when branch)) + +(defun magit-local-branch-at-point () + (magit-section-when branch + (let ((branch (magit-section-value it))) + (when (member branch (magit-list-local-branch-names)) + branch)))) + +(defun magit-commit-at-point () + (or (magit-section-when commit) + (and (derived-mode-p 'magit-revision-mode) + (car (last magit-refresh-args 2))))) + +(defun magit-branch-or-commit-at-point () + (or (magit-section-case + (branch (magit-section-value it)) + (commit (magit-get-shortname (magit-section-value it)))) + (and (derived-mode-p 'magit-revision-mode) + (car (last magit-refresh-args 2))))) + + +(defun magit-tag-at-point () + (magit-section-when tag)) + +(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-tracked-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-tracked-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 ".") + merge + (concat remote "/" merge)))))) + +(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-get-remote-branch (&optional branch) + (-when-let (local (or branch (magit-get-current-branch))) + (let ((remote (magit-get-remote local)) + (branch (magit-get "branch" local "merge"))) + (when (and remote branch (string-match "^refs/heads/\\(.+\\)" branch)) + (cons remote (match-string 1 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-string "describe" "--contains" rev) + (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) + (--map (substring it 2) (magit-git-lines "branch" "--contains" commit))) + +(defun magit-list-merged-branches (&optional commit) + (--map (substring it 2) (magit-git-lines "branch" "--merged" commit))) + +(defun magit-list-unmerged-branches (&optional commit) + (--map (substring it 2) (magit-git-lines "branch" "--no-merged" commit))) + +(defun magit-list-unmerged-to-upstream-branches () + (--filter (-when-let (upstream (magit-get-tracked-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-list-remote-tags (remote) + (--map (substring it 51) + (--filter (not (string-match-p "\\^{}$" it)) + (magit-git-lines "ls-remote" "--tags" 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-branch-p (string) + (and (or (member string (magit-list-branches)) + (member string (magit-list-branch-names))) t)) + +(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 () + (format "--abbrev=%d" (magit-abbrev-length))) + +(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 + (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-reflog-enable (ref &optional stashish) + (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 ""))))) + +(defun magit-rev-format (format &optional rev) + "Return output of `git show -s --format=FORMAT [REV]'." + (magit-git-string "show" "-s" (concat "--format=" format) rev)) + +(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) + (cl-destructuring-bind (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" (nth 2 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 " ")))) + +(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 &rest body) + (declare (indent 1) (debug (form body))) + (let ((file (cl-gensym "file"))) + `(let ((,file (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" ,it (concat "--index-output=" ,file)) + (error "Cannot read tree %s" ,it)))) + (if (file-remote-p default-directory) + (let ((magit-tramp-process-environment + (setenv-internal magit-tramp-process-environment + "GIT_INDEX_FILE" ,file t))) + ,@body) + (let ((process-environment process-environment)) + (setenv "GIT_INDEX_FILE" ,file) + ,@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 &rest other-parents) + (magit-with-temp-index "HEAD" + (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)) + +;;; 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) + (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 "\\.\\.\\.?") + (default (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))) + (input (read-from-minibuffer + (format "%s (%s): " prompt 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) + (when (consp default) + (setq default (concat (car default) "/" (cdr default)))) + (let ((branch (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))) + (string-match "^\\([^/]+\\)/\\(.+\\)" branch) + (cons (match-string 1 branch) + (match-string 2 branch)))) + +(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-branch-at-point) + secondary-default + (magit-get-current-branch)))) + +(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-branch-at-point) + secondary-default + (magit-get-current-branch)))) + +(defun magit-read-other-branch (prompt &optional exclude secondary-default) + (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 t 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")))) + +(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) + (magit-completing-read prompt + (--map (car (split-string it ":")) + (magit-git-lines "stash" "list")) + nil t nil nil atpoint)))) + +(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 (magit-list-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 +(provide 'magit-git) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; magit-git.el ends here diff --git a/elpa/magit-20150704.855/magit-log.el b/elpa/magit-20150704.855/magit-log.el new file mode 100644 index 00000000..569321d8 --- /dev/null +++ b/elpa/magit-20150704.855/magit-log.el @@ -0,0 +1,1209 @@ +;;; magit-log.el --- inspect Git history + +;; Copyright (C) 2010-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. + +;;; 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-insert-head-header 'magit) +(declare-function magit-insert-upstream-header 'magit) +(declare-function magit-read-file-from-rev 'magit) +(declare-function magit-show-commit 'magit) +(defvar magit-blame-mode) +(defvar magit-refs-indent-cherry-lines) +(defvar magit-refs-show-commit-count) + +(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-buffer-name-format "*magit-log: %a*" + "Name format for buffers used to display log entries. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :package-version '(magit . "2.1.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-cutoff-length 256 + "The maximum number of commits to show in log and reflog buffers." + :group 'magit-log + :type '(choice (const :tag "no limit") integer)) +(make-variable-buffer-local 'magit-log-cutoff-length) + +(defcustom magit-log-format-graph-function 'identity + "Function used to format graphs in log buffers. +The function is called with one argument, the graph of a single +line as a propertized string. It has to return the formatted +string. Use `identity' to forgo changing the graph." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type '(choice (function-item identity) + (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-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'. + +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." + :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.1.1") + :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-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-buffer-name-format "*magit-cherry: %a*" + "Name format for buffers used to display commits not merged upstream. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :group 'magit-log + :type 'string) + +(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-buffer-name-format "*magit-reflog: %a*" + "Name format for buffers used to display reflog entries. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type 'string) + +(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-or-recent-commits' (provided there +are no unpulled commits) show." + :package-version '(magit . "2.1.0") + :group 'magit-status + :type 'number) + +(defcustom magit-log-section-args nil + "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 again. Note that due to an issue in Git the +use of `--graph' is very slow with long histories. See +http://www.mail-archive.com/git@vger.kernel.org/msg51337.html" + :package-version '(magit . "2.1.0") + :group 'magit-status + :type '(repeat (choice (const "--graph") + (const "--decorate") + (const "--show-signature")))) + +;;; Commands + +;;;###autoload (autoload 'magit-log-popup "magit-log" nil t) +(magit-define-popup magit-log-popup + "Popup console for log commands." + 'magit-log + :man-page "git-log" + :switches '((?g "Show graph" "--graph") + (?d "Show refnames" "--decorate") + (?S "Show signatures" "--show-signature") + (?u "Show diffs" "--patch") + (?s "Show diffstats" "--stat") + (?D "Simplify by decoration" "--simplify-by-decoration")) + :options '((?f "Limit to files" "-- " magit-read-files) + (?a "Limit to author" "--author=" read-from-minibuffer) + (?m "Search messages" "--grep=" read-from-minibuffer) + (?p "Search patches" "-G" read-from-minibuffer)) + :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-arguments '("--graph" "--decorate") + :default-action 'magit-log-current + :max-action-columns 3) + +(defvar magit-log-use-verbose-re + (concat "^" (regexp-opt '("--patch" "--stat"))) + "Regexp matching arguments which trigger the use of verbose log.") + +(defvar magit-log-remove-graph-re + (concat "^" (regexp-opt '("-G" "--grep"))) + "Regexp matching arguments which are not compatible with `--graph'.") + +(defun magit-log-read-args (&optional use-current norev) + (let* ((args (magit-log-arguments)) + (files (--first (string-match-p "^-- " it) args))) + (when files + (setq args (remove files args) + files (split-string (substring files 3) ","))) + `(,@(unless norev (list (magit-log-read-revs use-current))) + ,args ,files))) + +(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 (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 (magit-log-read-args t)) + (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 (magit-log-read-args nil)) + (magit-mode-setup magit-log-buffer-name-format nil + #'magit-log-mode + #'magit-log-refresh-buffer + (if (--any? (string-match-p magit-log-use-verbose-re it) + args) + 'verbose + 'oneline) + revs + (if (--any? (string-match-p magit-log-remove-graph-re it) + args) + (delete "--graph" args) + args) + files) + (magit-log-goto-same-commit)) + +;;;###autoload +(defun magit-log-head (&optional args files) + "Show log for `HEAD'." + (interactive (magit-log-read-args nil t)) + (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-read-args nil t)) + (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-read-args nil t)) + (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-read-args nil t)) + (magit-log (if (magit-get-current-branch) + (list "--all") + (list "HEAD" "--all")) + args files)) + +;;;###autoload +(defun magit-log-buffer-file () + "Show log for the file visited in the current buffer." + (interactive) + (-if-let (file (or buffer-file-name magit-buffer-file-name)) + (magit-mode-setup magit-log-buffer-name-format nil + #'magit-log-mode + #'magit-log-refresh-buffer + 'oneline + (list (or magit-buffer-refname + (magit-get-current-branch) "HEAD")) + (magit-log-arguments) + (list (file-relative-name file (magit-toplevel)))) + (user-error "Buffer does not visit 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-buffer-name-format nil + #'magit-reflog-mode + #'magit-reflog-refresh-buffer ref)) + +;;;###autoload +(defun magit-reflog-head () + "Display the `HEAD' reflog." + (interactive) + (magit-reflog "HEAD")) + +(defun magit-log-show-more-commits (&optional arg) + "Increase the number of commits shown in current log. + +With no prefix argument, show twice as many commits as before. +With a numerical prefix argument, show this many additional +commits. With a non-numeric prefix argument, show all commits. + +When no limit was previously imposed in the current buffer, set +the local limit to the default limit instead (or if that is nil +then 100), regardless of the prefix argument. + +By default `magit-log-cutoff-length' commits are shown." + (interactive "P") + (setq magit-log-cutoff-length + (if magit-log-cutoff-length + (if arg + (and (numberp arg) (+ magit-log-cutoff-length arg)) + (* magit-log-cutoff-length 2)) + (or (default-value 'magit-log-cutoff-length) 100))) + (let ((old-point (point))) + (magit-refresh) + (goto-char old-point))) + +(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-buffer-name-format + '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-show-more-commits) + (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)History'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-show-commit] or \\[magit-diff-show-or-scroll-up]\ + to visit the commit at point. +Type \\[magit-merge-popup] to merge the commit at point. +Type \\[magit-cherry-pick] to cherry-pick the commit at point. +Type \\[magit-reset-head] to reset HEAD to the commit at point. +\n\\{magit-log-mode-map}" + :group 'magit-log + (magit-set-buffer-margin magit-log-show-margin) + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-log-refresh-buffer (style revs args &optional files) + (setq header-line-format + (propertize + (concat " Commits in " (mapconcat 'identity revs " ") + (and files (concat " touching " + (mapconcat 'identity files " ")))) + 'face 'magit-header-line)) + (magit-insert-section (logbuf) + (if (eq style 'oneline) + (magit-insert-log revs args files) + (magit-insert-log-verbose revs args files)))) + +(defun magit-insert-log (revs &optional args files) + "Insert a oneline log section. +For internal use; don't add to a hook." + (magit-git-wash (apply-partially 'magit-log-wash-log 'oneline) + "log" (magit-log-format-max-count) "--color" + (format "--format=%%h%s %s[%%an][%%at]%%s" + (if (member "--decorate" args) "%d" "") + (if (member "--show-signature" args) + (progn (setq args (remove "--show-signature" args)) "%G?") + "")) + (if (member "--decorate" args) + (cons "--decorate=full" (remove "--decorate" args)) + args) + revs "--" files)) + +(defun magit-insert-log-verbose (revs &optional args files) + "Insert a multiline log section. +For internal use; don't add to a hook." + (magit-git-wash (apply-partially 'magit-log-wash-log 'verbose) + "log" (magit-log-format-max-count) "--color" + (if (member "--decorate" args) + (cons "--decorate=full" (remove "--decorate" args)) + args) + revs "--" files)) + +(defvar magit-commit-section-map + (let ((map (make-sparse-keymap))) + (define-key map "\r" 'magit-show-commit) + (define-key map "a" 'magit-cherry-apply) + (define-key map "v" 'magit-revert-no-commit) + map) + "Keymap for `commit' sections.") + +(defvar magit-module-commit-section-map + (let ((map (make-sparse-keymap))) + (define-key map "\r" 'magit-show-commit) + map) + "Keymap for `module-commit' sections.") + +(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-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 + "\\[\\(?5:[^]]*\\)\\] " ; author + "\\(?6:[^ ]*\\) " ; date + "\\(?:\\(?:[^@]+@{\\(?9:[^}]+\\)} " ; refsel + "\\(?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 + +(defconst magit-log-verbose-re + (concat "^" + "\\(\\(?:[-_/|\\*o.] *\\)+ *\\)?" + "commit \\([0-9a-fA-F]+\\)" + "\\(?: \\(([^()]+)\\)\\)?")) + +(defvar magit-log-count nil) + +(defun magit-log-format-max-count () + (and magit-log-cutoff-length + (format "-%d" magit-log-cutoff-length))) + +(defun magit-log-wash-log (style args) + (when (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 + (if (eq style 'verbose) + (apply-partially 'magit-log-wash-verbose abbrev) + (apply-partially 'magit-log-wash-line style abbrev))) + (if (derived-mode-p 'magit-log-mode) + (when (= magit-log-count magit-log-cutoff-length) + (magit-insert-section (longer) + (insert-text-button + (substitute-command-keys + (format "Type \\<%s>\\[%s] to show more history" + 'magit-log-mode-map + 'magit-log-show-more-commits)) + 'action (lambda (button) + (magit-log-show-more-commits)) + 'follow-link t + 'mouse-face 'magit-section-highlight))) + (unless (equal (car args) "cherry") + (insert ?\n))))) + +(defun magit-log-wash-line (style abbrev) + (when (derived-mode-p 'magit-log-mode) + (cl-incf magit-log-count)) + (looking-at (pcase style + (`oneline magit-log-oneline-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 refsel refsub side) nil + (magit-delete-match) + (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))) + (magit-insert cherry (if (string= cherry "-") + 'magit-cherry-equivalent + 'magit-cherry-unmatched) ?\s)) + (when side + (magit-insert side (if (string= side "<") + 'magit-diff-removed + 'magit-diff-added) ?\s)) + (insert (propertize hash 'face 'magit-hash) ?\s) + (when graph + (insert (funcall magit-log-format-graph-function graph))) + (when (and refs (not magit-log-show-refname-after-summary)) + (magit-insert (magit-format-ref-labels refs) nil ?\s)) + (when refsub + (insert (format "%-2s " refsel)) + (magit-insert + (magit-reflog-format-subject + (substring refsub 0 (if (string-match-p ":" refsub) -2 -1))))) + (when msg + (magit-insert msg (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) + (magit-insert (magit-format-ref-labels refs))) + (when (memq style '(oneline reflog stash)) + (goto-char (line-beginning-position)) + (magit-format-log-margin author date)) + (forward-line))) + (when (eq style 'oneline) + (let ((align (make-string (1+ abbrev) ? ))) + (while (and (not (eobp)) (not (looking-at magit-log-oneline-re))) + (insert align) + (goto-char (line-beginning-position)) + (magit-format-log-margin) + (forward-line)))) + t) + +(defun magit-log-wash-verbose (abbrev) + (cl-incf magit-log-count) + (looking-at magit-log-verbose-re) + (magit-bind-match-strings (graph hash refs) nil + (magit-delete-match) + (magit-insert-section (commit hash) + (when graph (insert graph)) + (when refs + (magit-insert (magit-format-ref-labels refs) nil ?\s)) + (magit-insert hash 'magit-hash ?\s) + (forward-line) + (while (and (not (eobp)) (not (looking-at magit-log-verbose-re))) + (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) + (cl-destructuring-bind (width unit-width duration-spec) + magit-log-margin-spec + (if author + (magit-make-margin-overlay + (propertize (truncate-string-to-width + 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 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-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-show-more-commits) + (forward-line -1) + (magit-section-forward))) + +(defun magit-log-maybe-show-commit (&optional section) + "Automatically show commit at point in another window. +If the section at point is a `commit' section and the value of +`magit-diff-auto-show-p' calls for it, then show that commit in +another window, using `magit-show-commit'." + (--when-let + (or (and section + (eq (magit-section-type section) 'commit) + (or (and (magit-diff-auto-show-p 'log-follow) + (get-buffer-window magit-revision-buffer-name-format)) + (and (magit-diff-auto-show-p 'log-oneline) + (derived-mode-p 'magit-log-mode))) + (magit-section-value section)) + (and magit-blame-mode + (magit-diff-auto-show-p 'blame-follow) + (get-buffer-window magit-revision-buffer-name-format) + (magit-blame-chunk-get :hash))) + (magit-show-commit it t))) + +(defun magit-log-goto-same-commit () + (--when-let + (and magit-previous-section + (derived-mode-p 'magit-log-mode) + (-when-let (value (magit-section-value magit-previous-section)) + (--first (equal (magit-section-value it) value) + (magit-section-children magit-root-section)))) + (goto-char (magit-section-start it)))) + +;;; 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." + :group 'magit-log + (hack-dir-local-variables-non-file-buffer)) + +(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 args) + (declare (indent defun)) + (magit-mode-setup magit-log-buffer-name-format nil + #'magit-log-select-mode + #'magit-log-refresh-buffer 'oneline + (list (or branch (magit-get-current-branch) "HEAD")) + args) + (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 (concat " " msg))) + (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) + 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-show-commit] or \\[magit-diff-show-or-scroll-up]\ + to visit the commit at point. +Type \\[magit-cherry-pick] to cherry-pick the commit at point. +\n\\{magit-cherry-mode-map}" + :group 'magit-modes + (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-tracked-branch head))))) + (magit-mode-setup magit-cherry-buffer-name-format nil + #'magit-cherry-mode + #'magit-cherry-refresh-buffer 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-header (nth 1 magit-refresh-args)) + (magit-insert-upstream-header (nth 1 magit-refresh-args) + (nth 0 magit-refresh-args)) + (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) + 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)Reflogs'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-show-commit] or \\[magit-diff-show-or-scroll-up]\ + to visit the commit at point. +Type \\[magit-cherry-pick] to cherry-pick the commit at point. +Type \\[magit-reset-head] to reset HEAD to the commit at point. +\n\\{magit-reflog-mode-map}" + :group 'magit-log + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-reflog-refresh-buffer (ref) + (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 [%an] %ct %gd %gs" + (magit-log-format-max-count) 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 + +(defvar magit-unpulled-section-map + (let ((map (make-sparse-keymap))) + (define-key map "\r" 'magit-diff-unpulled) + map) + "Keymap for the `unpulled' section.") + +(magit-define-section-jumper unpulled "Unpulled commits") + +(defun magit-insert-unpulled-commits () + "Insert section showing unpulled commits." + (-when-let (tracked (magit-get-tracked-ref)) + (magit-insert-section (unpulled) + (magit-insert-heading "Unpulled commits:") + (magit-insert-log (concat "HEAD.." tracked) magit-log-section-args)))) + +(defun 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." + (let ((tracked (magit-get-tracked-ref))) + (if (and tracked (not (equal (magit-rev-parse "HEAD") + (magit-rev-parse tracked)))) + (magit-insert-unpulled-commits) + (magit-insert-recent-commits t)))) + +(defun magit-insert-recent-commits (&optional collapse) + "Insert section showing recent commits. +Show the last `magit-log-section-commit-count' commits." + (magit-insert-section (recent nil collapse) + (magit-insert-heading "Recent commits:") + (magit-insert-log nil (cons (format "-%d" magit-log-section-commit-count) + magit-log-section-args)))) + +(defun magit-insert-unpulled-cherries () + "Insert section showing unpulled commits. +Like `magit-insert-unpulled-commits' 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-let (tracked (magit-get-tracked-ref)) + (magit-insert-section (unpulled) + (magit-insert-heading "Unpulled commits:") + (magit-git-wash (apply-partially 'magit-log-wash-log 'cherry) + "cherry" "-v" (magit-abbrev-arg) (magit-get-current-branch) tracked)))) + +(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:") + (let ((topdir (magit-toplevel))) + (dolist (module modules) + (let ((default-directory + (expand-file-name (file-name-as-directory module) topdir))) + (-when-let (tracked (magit-get-tracked-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))))) + +(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)))) + +(defvar magit-unpushed-section-map + (let ((map (make-sparse-keymap))) + (define-key map "\r" 'magit-diff-unpushed) + map) + "Keymap for the `unpushed' section.") + +(magit-define-section-jumper unpushed "Unpushed commits") + +(defun magit-insert-unpushed-commits () + "Insert section showing unpushed commits." + (-when-let (tracked (magit-get-tracked-ref)) + (magit-insert-section (unpushed) + (magit-insert-heading "Unpushed commits:") + (magit-insert-log (concat tracked "..HEAD") magit-log-section-args)))) + +(defun 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 \"-\"." + (-when-let (tracked (magit-get-tracked-ref)) + (magit-insert-section (unpushed) + (magit-insert-heading "Unpushed commits:") + (magit-git-wash (apply-partially 'magit-log-wash-log 'cherry) + "cherry" "-v" (magit-abbrev-arg) tracked)))) + +(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:") + (let ((topdir (magit-toplevel))) + (dolist (module modules) + (let ((default-directory + (expand-file-name (file-name-as-directory module) topdir))) + (-when-let (tracked (magit-get-tracked-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))))) + +;;; 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")) + (when (and (derived-mode-p 'magit-log-mode) + (eq (car magit-refresh-args) 'verbose)) + (user-error "Log margin is redundant when showing verbose logs")) + (magit-set-buffer-margin (not (cdr (window-margins))))) + +(defun magit-set-buffer-margin (enable) + (let ((width (and enable + (if (and (derived-mode-p 'magit-log-mode) + (eq (car magit-refresh-args) 'verbose)) + 0 ; temporarily hide redundant margin + (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+ (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)))))) + +;;; magit-log.el ends soon +(provide 'magit-log) +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: +;;; magit-log.el ends here diff --git a/elpa/magit-20150704.855/magit-mode.el b/elpa/magit-20150704.855/magit-mode.el new file mode 100644 index 00000000..b45523b5 --- /dev/null +++ b/elpa/magit-20150704.855/magit-mode.el @@ -0,0 +1,816 @@ +;;; magit-mode.el --- create and refresh Magit buffers + +;; Copyright (C) 2010-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. + +;;; 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' from `magit' +(defvar magit-status-buffer-name-format) +;; For `magit-revert-buffers' +(declare-function magit-blame-mode 'magit-blame) +(defvar magit-blame-mode) + +(require 'format-spec) +(require 'help-mode) + +;;; Options + +(defcustom magit-mode-hook '(magit-load-config-extensions) + "Hook run when entering a mode derived from Magit mode." + :group 'magit-modes + :type 'hook + :options '(magit-load-config-extensions bug-reference-mode)) + +(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-restore-window-configuration t + "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-bury-buffer', 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 then the default value +might 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 . "2.1.0") + :group 'magit + :type 'boolean) + +(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-revert-buffer' to run after refreshing." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'hook) + +(defvar magit-revert-buffers-timer nil) + +(defun magit-revert-buffers-set-timer () + (when (timerp magit-revert-buffers-timer) + (cancel-timer magit-revert-buffers-timer)) + (setq magit-revert-buffers-timer + (and (boundp 'magit-revert-buffers) + (numberp magit-revert-buffers) + (run-with-timer 0 magit-revert-buffers + 'magit-revert-buffers-async)))) + +(defcustom magit-revert-buffers 'usage + "How file-visiting buffers in the current repository are reverted. + +After running certain commands, after refreshing the current +Magit buffer, unmodified buffers visiting files belonging to +the current repository may optionally be reverted. + +`nil' Don't revert any buffers. + +`ask' List the buffers which might potentially have to be + reverted and ask the user whether she wants to revert + them. If so, then do it synchronously. + +`t' Revert the buffers synchronously, mentioning each one + as it is being reverted and then also show a summary. + +`usage' Like `t' but include usage information in the summary. + This is the default so that users come here and pick + what is right for them. + +`silent' Revert the buffers synchronously and be quiet about it. + +NUMBER An integer or float. Revert the buffers asynchronously, + mentioning each one as it is being reverted. If user + input arrives, then stop reverting. After NUMBER + seconds resume reverting." + :package-version '(magit . "2.1.0") + :group 'magit + :type '(choice + (const :tag "Don't revert" nil) + (const :tag "Ask whether to revert" ask) + (const :tag "Revert synchronously" t) + (const :tag "Revert synchronously but in silence" silent) + (const :tag "Revert synchronously with usage information" usage) + (integer :tag "Revert asynchronously (interval in seconds)")) + :set (lambda (var val) + (set-default var val) + (magit-revert-buffers-set-timer))) + +(defcustom magit-after-revert-hook '(magit-refresh-vc-mode-line) + "Normal hook for `magit-revert-buffer' to run after reverting." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'hook + :options '(magit-refresh-vc-mode-line)) + +(defcustom magit-not-reverted-hook '(magit-refresh-vc-mode-line) + "Normal hook for `magit-revert-buffer' to run instead of reverting. +Run if the visited file has not changed on disk and the buffer +therefore does not have to be reverted." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'hook + :options '(magit-refresh-vc-mode-line)) + +(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. It this is `dontask' then +this is done without user intervention, if it is t then the user +has to confirm each save." + :group 'magit + :type '(choice (const :tag "Never" nil) + (const :tag "Ask" t) + (const :tag "Save without asking" dontask))) + +;;; 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 "^" '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) + (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 "l" 'magit-log-popup) + (define-key map "L" 'magit-toggle-margin) + (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 "t" 'magit-tag-popup) + (define-key map "T" 'magit-notes-popup) + (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-reset-index) + (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-as-kill) + (define-key map "\M-w" 'magit-copy-buffer-thing-as-kill) + (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'.") + +(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" + ["Oneline Log" magit-log t] + ["Verbose Log" magit-log-verbose 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-stash-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-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-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) + (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 (run-hook-with-args-until-success 'magit-region-highlight-hook + (magit-current-section)) + (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-function nil + "The function used to refresh the current buffer. +This is called with `magit-refresh-args' as arguments. +The value is usually set using `magit-mode-setup'.") +(put 'magit-refresh-function 'permanent-local t) + +(defvar-local magit-refresh-args nil + "The arguments used to refresh the current buffer. +`magit-refresh-function' is called with these arguments. +The value is usually set using `magit-mode-setup'.") +(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." + (declare (debug (form form form form &rest form))) + (let ((smode (cl-gensym "mode")) + (sroot (cl-gensym "root")) + (sfunc (cl-gensym "func")) + (sargs (cl-gensym "args")) + (sbuf (cl-gensym "buffer"))) + `(let* ((,smode ,mode) + (,sroot (magit-toplevel)) + (,sfunc ,refresh-func) + (,sargs (list ,@refresh-args)) + (,sbuf (magit-mode-display-buffer ,buffer ,smode ,switch-func))) + (when find-file-visit-truename + (setq ,sroot (file-truename ,sroot))) + (if ,sroot + (with-current-buffer ,sbuf + (setq default-directory ,sroot + magit-refresh-function ,sfunc + magit-refresh-args ,sargs) + (run-hooks 'magit-mode-setup-hook) + (pcase ,smode + ((or `magit-log-mode `magit-reflog-mode) + (magit-xref-setup ,sargs)) + ((or `magit-diff-mode `magit-revision-mode) + (magit-xref-setup ,sargs) + (goto-char (point-min)))) + (funcall ,smode) + (magit-refresh-buffer)) + (user-error "Not inside a Git repository"))))) + +(defvar magit-inhibit-save-previous-winconf nil) + +(defvar-local magit-previous-window-configuration nil) +(put 'magit-previous-window-configuration 'permanent-local t) + +(defvar-local magit-previous-section nil) +(put 'magit-previous-section '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-bury-buffer'. + +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'." + (cond ((stringp buffer) + (setq buffer (magit-mode-get-buffer-create buffer mode))) + ((not (bufferp buffer)) + (signal 'wrong-type-argument (list 'bufferp nil)))) + (let ((section (magit-current-section))) + (with-current-buffer (get-buffer-create buffer) + (setq magit-previous-section section) + (if magit-inhibit-save-previous-winconf + (when (eq magit-inhibit-save-previous-winconf 'unset) + (setq magit-previous-window-configuration nil)) + (unless (get-buffer-window buffer (selected-frame)) + (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-buffers (&optional topdir) + (unless topdir + (setq topdir (magit-toplevel))) + (--filter (with-current-buffer it + (and (derived-mode-p 'magit-mode) + (equal default-directory topdir))) + (buffer-list))) + +(defun magit-mode-get-buffer (format mode &optional topdir create) + (if (not (string-match-p "%[ab]" format)) + (funcall (if create #'get-buffer-create #'get-buffer) format) + (unless topdir + (setq topdir (magit-toplevel))) + (let ((name (format-spec format + `((?a . ,(abbreviate-file-name (or topdir "-"))) + (?b . ,(if topdir + (file-name-nondirectory + (directory-file-name topdir)) + "-")))))) + (or (--first (with-current-buffer it + (and (or (not topdir) + (equal (expand-file-name default-directory) + topdir)) + (string-match-p (format "^%s$" (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)) + +(defun magit-mode-bury-buffer (&optional kill-buffer) + "Bury the current buffer. +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 burying/killing +the buffer." + (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-bury-buffer-hook buffer))) + +(defun magit-rename-buffer (&optional newname) + "Rename the current buffer, so that Magit won't reuse it. + +By default there is only one buffer with a certain Magit mode +per repository. Displaying e.g. some diff will reuse the buffer +previously used to display another diff. If you want to have +two buffers displaying different diffs belonging to the same +repository, then you have to create a buffer whose name differs +from the default name. + +The easiest way to do that is to use this command. It appends +\"\" to the name of the current buffer, where N is the lowest +available number, starting with 2, which is still available. + +With a prefix argument, the user can pick an arbitrary name." + (interactive + (list (and current-prefix-arg + (read-buffer "Rename buffer to: " (current-buffer))))) + (unless newname + (setq newname (buffer-name))) + (when (string-match "<[0-9]+>\\'" newname) + (setq newname (substring newname 0 (match-beginning 0)))) + (rename-buffer (generate-new-buffer-name newname))) + +;;; Refresh Machinery + +(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. + +If option `magit-revert-buffers' call for it, then also revert +all unmodified buffers that visit files being tracked in the +current repository." + (interactive) + (unless inhibit-magit-refresh + (when (derived-mode-p 'magit-mode) + (run-hooks 'magit-pre-refresh-hook) + (magit-refresh-buffer) + (unless (derived-mode-p 'magit-status-mode) + (--when-let (magit-mode-get-buffer + magit-status-buffer-name-format + 'magit-status-mode) + (with-current-buffer it + (magit-refresh-buffer))))) + (magit-revert-buffers))) + +(defun magit-refresh-all () + "Refresh all buffers belonging to the current repository. + +Refresh all Magit buffers belonging to the current repository. + +Also always revert all unmodified buffers that visit files being +tracked in the current repository." + (interactive) + (dolist (buffer (magit-mode-get-buffers)) + (with-current-buffer buffer (magit-refresh-buffer))) + (magit-revert-buffers t)) + +(defvar-local magit-refresh-start-time nil) + +(defun magit-refresh-buffer () + "Refresh the current Magit buffer. +Uses the buffer-local `magit-refresh-function'." + (setq magit-refresh-start-time (current-time)) + (when magit-refresh-function + (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 + (list it section + (count-lines (magit-section-start section) + (point)) + (- (point) (line-beginning-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 magit-refresh-function + 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)))))) + +(defvar inhibit-magit-revert nil) +(defvar magit-revert-buffers-backlog nil) + +(defun 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. + +When called interactively then the revert is forced." + (interactive (list t)) + (when (or force (and magit-revert-buffers (not inhibit-magit-revert))) + (-when-let (topdir (magit-toplevel)) + (let* ((tracked (magit-revision-files "HEAD")) + (buffers + (if (> (length tracked) + (length (buffer-list))) + (--filter + (let ((file (buffer-file-name it))) + (and file + (file-in-directory-p file topdir) + (member (file-relative-name file topdir) tracked))) + (buffer-list)) + (--mapcat + (--when-let (find-buffer-visiting (expand-file-name it topdir)) + (list it)) + tracked)))) + (when (and buffers + (or force + (not (eq magit-revert-buffers 'ask)) + (magit-confirm 'revert-buffer + "Revert %s from visited file" + "Revert %i buffers from visited files" + (mapcar #'buffer-name buffers)))) + (cond + ((numberp magit-revert-buffers) + (magit-revert-buffers-async buffers)) + ((eq magit-revert-buffers 'silent) + (mapc #'magit-revert-buffer buffers)) + (t + (let ((cnt (length buffers))) + (when (> cnt 0) + (message "Reverting (up to) %s file-visiting buffer(s)..." cnt) + (setq cnt (length (-non-nil (mapcar #'magit-revert-buffer + buffers)))) + (if (> cnt 0) + (pcase magit-revert-buffers + (`t + (message "Reverting %s file-visiting buffer(s)...done" cnt)) + (`usage + (message + "Reverting %s file-visiting buffer(s)...done%s%s%s" cnt + (substitute-command-keys + "\n This can be undone using `\\[undo]' in the ") + "affected buffers\n Customize behavior using `M-x " + "customize-option RET magit-revert-buffers RET'")) + ((or `nil `ask) + (message "Reverting %s file-visiting buffer(s)...done%s" + cnt (if force " (forced)" "")))) + (message "(No buffers need to be reverted)"))))))))))) + +(defun magit-revert-buffers-async (&optional buffers) + (setq buffers (nconc buffers (--filter (not (memq it buffers)) + magit-revert-buffers-backlog))) + (while (and buffers (not (input-pending-p))) + (let ((buf (pop buffers))) + (when (buffer-live-p buf) + (with-current-buffer buf + (magit-revert-buffer buf))))) + (setq magit-revert-buffers-backlog buffers)) + +(defun magit-revert-buffer (&optional buffer) + "Revert the current file-visiting buffer." + (let (ret) + (with-current-buffer (or buffer (current-buffer)) + (if (and (file-readable-p buffer-file-name) + (not (verify-visited-file-modtime (current-buffer)))) + (if magit-blame-mode + (progn (message "Reverting %s inhibited due to magit-blame-mode" + buffer-file-name) + (run-hooks 'magit-not-reverted-hook)) + (if (eq magit-revert-buffers 'silent) + (revert-buffer 'ignore-auto t t) + (message "Reverting buffer `%s'..." (buffer-name)) + (revert-buffer 'ignore-auto t t) + (message "Reverting buffer `%s'...done" (buffer-name))) + (run-hooks 'magit-after-revert-hook) + (setq ret t)) + (run-hooks 'magit-not-reverted-hook))) + ret)) + +(add-hook 'git-commit-setup-hook #'magit-revert-buffers) + +(defun magit-refresh-vc-mode-line () + "Update `vc-mode' which is displayed in the mode-line. +Like `vc-mode-line' but simpler, more efficient, and less buggy." + (setq vc-mode + (if vc-display-status + (let* ((rev (or (magit-get-current-branch) + (magit-rev-parse "--short" "HEAD"))) + (msg (cl-letf (((symbol-function #'vc-working-revision) + (lambda (&rest _) rev))) + (vc-default-mode-line-string 'Git buffer-file-name)))) + (propertize + (concat " " msg) + 'mouse-face 'mode-line-highlight + 'help-echo (concat (get-text-property 0 'help-echo msg) + "\nCurrent revision: " rev + "\nmouse-1: Version Control menu") + 'local-map vc-mode-line-map)) + " Git")) + (force-mode-line-update)) + +(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) + +(defun magit-maybe-save-repository-buffers () + (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 msg (message "%s" msg))))) + +(add-hook 'magit-mode-setup-hook #'magit-maybe-save-repository-buffers) +(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-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 (ignore-errors (magit-toplevel nil t)) topdir))) + topdir)))) + +;;; 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 (and magit-revision-show-xref-buttons + (derived-mode-p 'magit-revision-mode)) + (and magit-diff-show-xref-buttons + (derived-mode-p 'magit-diff-mode))) + (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-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 (refresh-args) + (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 ,@refresh-args))) + +(defun magit-xref-restore (&rest args) + (magit-xref-setup magit-refresh-args) + (setq default-directory (car args)) + (setq magit-refresh-args (cdr args)) + (magit-refresh-buffer)) + +;;; 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-20150704.855/magit-pkg.el b/elpa/magit-20150704.855/magit-pkg.el new file mode 100644 index 00000000..14614c8c --- /dev/null +++ b/elpa/magit-20150704.855/magit-pkg.el @@ -0,0 +1,11 @@ +(define-package "magit" "20150704.855" "A Git porcelain inside Emacs" + '((emacs "24.4") + (dash "2.10.0") + (with-editor "2.1.0") + (git-commit "2.1.0") + (magit-popup "2.1.0")) + :url "https://github.com/magit/magit" :keywords + '("git" "tools" "vc")) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/magit-20150704.855/magit-process.el b/elpa/magit-20150704.855/magit-process.el new file mode 100644 index 00000000..a1196c2b --- /dev/null +++ b/elpa/magit-20150704.855/magit-process.el @@ -0,0 +1,724 @@ +;;; magit-process.el --- process functionality + +;; Copyright (C) 2010-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. + +;;; 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) + + +(defvar magit-status-buffer-name-format) + +(eval-when-compile (require 'dired)) +(declare-function dired-uncache 'dired) + +;;; Options + +(defcustom magit-process-buffer-name-format "*magit-process: %a*" + "Name format for buffers where output of processes is put. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :package-version '(magit . "2.1.0") + :group 'magit-process + :type 'string) + +(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. +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-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 '.*'\\)?: ?$" + "^\\(Enter \\)?[Pp]assword\\( for '.*'\\)?: ?$" + "^.*'s password: ?$" + "^Yubikey for .*: ?$") + "List of regexps matching password prompts of Git and its subprocesses." + :package-version '(magit . "2.1.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 . "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 topdir create) + (or (magit-mode-get-buffer magit-process-buffer-name-format + 'magit-process-mode topdir) + (and create + (with-current-buffer (magit-mode-get-buffer-create + magit-process-buffer-name-format + 'magit-process-mode topdir) + (magit-process-mode) + (let ((inhibit-read-only t)) + (make-local-variable 'text-property-default-nonsticky) + (magit-insert-section (processbuf) + (insert "\n"))) + (current-buffer))))) + +(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-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 a buffer specified by +variable `magit-process-buffer-name-format'. 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. +Unmodified buffers visiting files that are tracked in the current +repository are reverted if `magit-revert-buffers' is non-nil. + +Process output goes into a new section in a buffer specified by +variable `magit-process-buffer-name-format'." + (magit-call-git args) + (magit-refresh)) + +(defun magit-run-git-no-revert (&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 a buffer specified by +variable `magit-process-buffer-name-format'." + (let ((inhibit-magit-revert t)) + (magit-run-git args))) + +(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 a buffer specified by +variable `magit-process-buffer-name-format'." + (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 a buffer specified by +variable `magit-process-buffer-name-format'." + (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. +ARGS is flattened and then used as arguments to Git. + +The first argument, INPUT, should be a buffer or the name of +an existing buffer. The content of that buffer is used as the +process' standard input. It may also be nil in which case the +current buffer is used. + +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, 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-revert-buffers' is non-nil. +When INPUT is nil then do not refresh any buffers. + +This function actually starts a asynchronous process, but it then +waits for that process to return." + (declare (indent 1)) + (magit-start-git (or input (current-buffer)) args) + (magit-process-wait) + (when input (magit-refresh))) + +(defvar magit-this-process nil) + +(defun magit-run-git-with-logfile (file &rest args) + "Call Git in a separate process and log its output to FILE. +See `magit-run-git' for more information. +This function might have a short halflive." + (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 + +(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. +Unmodified buffers visiting files that are tracked in the current +repository are reverted if `magit-revert-buffers' is non-nil. + +See `magit-start-process' for more information." + (message "Running %s %s" magit-git-executable + (mapconcat 'identity (-flatten args) " ")) + (magit-start-git nil args)) + +(defun magit-run-git-async-no-revert (&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." + (let ((inhibit-magit-revert t)) + (magit-run-git-async 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) + (inhibit-magit-revert nil)) + (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. + +Unmodified buffers visiting files that are tracked in the current +repository are reverted if `magit-revert-buffers' is non-nil. + +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) + +(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. +Unmodified buffers visiting files that are tracked in the current +repository are reverted if `magit-revert-buffers' is non-nil. + +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. Unmodified buffers visiting files that are +tracked in the current repository are reverted if +`magit-revert-buffers' is non-nil." + (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))) + (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)) + (when inhibit-magit-revert + (process-put process 'inhibit-revert 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 ((buf (magit-process-buffer))) + (if buf + (magit-process-truncate-log buf) + (setq buf (magit-process-buffer nil t))) + (cons buf (with-current-buffer buf + (prog1 (magit-process-insert-section program args) + (backward-char 1)))))) + +(defun magit-process-insert-section (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 ")) + (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 (buffer) + (when magit-process-log-max + (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-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'." + (let ((debug-on-error t)) ; temporary + (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)) + (--when-let (and (not (process-get process 'inhibit-refresh)) + (process-get process 'command-buf)) + (when (buffer-live-p it) + (with-current-buffer it + (let ((inhibit-magit-revert (process-get process 'inhibit-revert))) + (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-buffer-name-format + '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))))) + +(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)))) + +(defmacro magit-process-kill-on-abort (proc &rest body) + (declare (indent 1)) + (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-prompt (process string) + "Forward password prompts to the user." + (--when-let (magit-process-match-prompt + magit-process-password-prompt-regexps string) + (process-send-string + process (magit-process-kill-on-abort process + (concat (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) + (when (--any? (string-match it 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))) + +(defadvice tramp-sh-handle-start-file-process + (before magit-tramp-process-environment activate) + (when magit-tramp-process-environment + (ad-set-args 3 (append (cdr magit-tramp-process-environment) + (cons (ad-get-arg 2) + (ad-get-args 3)))) + (ad-set-arg 2 (car magit-tramp-process-environment)))) + +(defadvice tramp-sh-handle-process-file + (before magit-tramp-process-environment activate) + (when magit-tramp-process-environment + (ad-set-args 4 (append magit-tramp-process-environment + (cons (ad-get-arg 0) + (ad-get-args 4)))) + (ad-set-arg 0 "env"))) + +(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)) + +(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)) + (set-marker-insertion-type marker t) + (magit-put-face-property (- (point) 3) (point) + (if (= arg 0) + 'magit-process-ok + 'magit-process-ng))) + (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)) + (when (= arg 0) + (magit-section-hide section)))))) + (unless (= arg 0) + (funcall + (if magit-process-raise-error #'error #'message) + "%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-let (magit-section-content section) + (when (re-search-backward + magit-process-error-message-re it t) + (match-string 1)))))) + "Git failed") + (-if-let (key (and (buffer-live-p command-buf) + (with-current-buffer command-buf + (car (where-is-internal + 'magit-process-display-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-20150704.855/magit-remote.el b/elpa/magit-20150704.855/magit-remote.el new file mode 100644 index 00000000..32c2e854 --- /dev/null +++ b/elpa/magit-20150704.855/magit-remote.el @@ -0,0 +1,309 @@ +;;; magit-remote.el --- transfer Git commits + +;; Copyright (C) 2008-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. + +;;; Commentary: + +;; This library implements support for interacting with remote +;; repositories. Commands for cloning, fetching, pulling, and +;; pushing are defined here. + +;;; Code: + +(require 'magit) + +;;; Clone + +;;;###autoload +(defun magit-clone (repository directory) + "Clone the REPOSITORY to DIRECTORY." + (interactive + (let ((url (magit-read-string "Clone repository"))) + (list url (read-directory-name + "Clone to: " nil nil nil + (and (string-match "\\([^./]+\\)\\(\\.git\\)?$" url) + (match-string 1 url)))))) + (make-directory directory t) + (magit-run-git "clone" repository directory)) + +;;; Setup + +;;;###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))) + +;;;###autoload +(defun magit-remote-add (remote url) + "Add a remote named REMOTE and fetch it." + (interactive (list (magit-read-string "Remote name") + (magit-read-string "Remote url"))) + (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 (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 "Rename remote"))) + (list remote (magit-read-string "Url" (magit-get "remote" remote "url"))))) + (magit-set url "remote" 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)) + +;;; 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" "--prune")) + :actions '((?f "Current" magit-fetch-current) + (?o "Other" magit-fetch) + (?a "All" magit-fetch-all) + (?m "Submodules" magit-submodule-fetch)) + :default-action 'magit-fetch-current) + +;;;###autoload +(defun magit-fetch-current (remote &optional args) + "Fetch from the upstream repository of the current branch. +If `HEAD' is detached or if the upstream is not configured, +then read the remote." + (interactive (list (or (magit-get-remote) + (magit-read-remote "Fetch remote")) + (magit-fetch-arguments))) + (magit-run-git-async-no-revert "fetch" remote args)) + +;;;###autoload +(defun magit-fetch (remote &optional args) + "Fetch from another repository." + (interactive (list (magit-read-remote "Fetch remote") + (magit-fetch-arguments))) + (magit-run-git-async-no-revert "fetch" remote args)) + +;;;###autoload +(defun magit-fetch-all (&optional args) + "Fetch from all configured remotes." + (interactive (list (magit-fetch-arguments))) + (magit-run-git-async-no-revert "remote" "update" args)) + +;;; 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" + :switches '((?r "Rebase" "--rebase")) + :actions '((?F "Current" magit-pull-current) + (?o "Other" magit-pull)) + :default-action 'magit-pull-current) + +;;;###autoload +(defun magit-pull-current (remote branch &optional args) + "Fetch and merge into current branch." + (interactive (magit-pull-read-args t)) + (magit-run-git-async "pull" args remote branch)) + +;;;###autoload +(defun magit-pull (remote branch &optional args) + "Fetch from another repository and merge a fetched branch." + (interactive (magit-pull-read-args)) + (magit-run-git-async "pull" args remote branch)) + +(defun magit-pull-read-args (&optional use-upstream) + (let ((remote (magit-get-remote-branch))) + (unless (and use-upstream remote) + (setq remote (magit-read-remote-branch "Pull" nil remote nil t))) + (list (car remote) (cdr remote) (magit-pull-arguments)))) + +;;; Push + +;;;###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") + (?u "Set upstream" "--set-upstream")) + :actions '((?P "Current" magit-push-current) + (?e "Elsewhere" magit-push-elsewhere) + (?t "Tags" magit-push-tags) + (?o "Other" magit-push) + (?m "Matching" magit-push-matching) + (?T "Tag" magit-push-tag)) + :default-action 'magit-push-current + :max-action-columns 3) + +;;;###autoload +(defun magit-push-current (branch remote &optional remote-branch args) + "Push the current branch to its upstream branch. +If the upstream isn't set, then read the remote branch." + (interactive (magit-push-read-args t t)) + (magit-push branch remote remote-branch args)) + +;;;###autoload +(defun magit-push (branch remote &optional remote-branch args) + "Push a branch to its upstream branch. +If the upstream isn't set, then read the remote branch." + (interactive (magit-push-read-args t)) + (magit-run-git-async-no-revert + "push" "-v" args remote + (if remote-branch + (format "%s:refs/heads/%s" branch remote-branch) + branch))) + +;;;###autoload +(defun magit-push-elsewhere (branch remote remote-branch &optional args) + "Push a branch or commit to some remote branch. +Read the local and remote branch." + (interactive (magit-push-read-args nil nil t)) + (magit-push branch remote remote-branch args)) + +(defun magit-push-read-args (&optional use-upstream use-current default-current) + (let* ((current (magit-get-current-branch)) + (local (or (and use-current current) + (magit-completing-read + "Push" (--if-let (magit-commit-at-point) + (cons it (magit-list-local-branch-names)) + (magit-list-local-branch-names)) + nil nil nil 'magit-revision-history + (or (and default-current current) + (magit-local-branch-at-point))) + (user-error "Nothing selected"))) + (remote (and (magit-branch-p local) + (magit-get-remote-branch local)))) + (unless (and use-upstream remote) + (setq remote (magit-read-remote-branch (format "Push %s to" local) + nil remote local 'confirm))) + (list local (car remote) (cdr remote) (magit-push-arguments)))) + +;;;###autoload +(defun magit-push-matching (remote &optional args) + "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." + (interactive (list (magit-read-remote "Push matching branches to" nil t))) + (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))) + (magit-run-git-async-no-revert "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-run-git-async-no-revert "push" remote tag)) + + +;;; 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" + :options '((?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." + (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-run-git-no-revert "format-patch" range args)) + +;;;###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-tracked-branch)) + (magit-read-branch-or-commit "End"))) + (let ((dir default-directory)) + ;; mu4e changes default-directory + (compose-mail) + (setq default-directory dir)) + (message-goto-body) + (let ((inhibit-magit-revert t)) + (magit-git-insert "request-pull" start url)) + (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-20150704.855/magit-section.el b/elpa/magit-20150704.855/magit-section.el new file mode 100644 index 00000000..68ed80df --- /dev/null +++ b/elpa/magit-20150704.855/magit-section.el @@ -0,0 +1,1102 @@ +;;; magit-section.el --- section functionality + +;; Copyright (C) 2010-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. + +;;; 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) + +;;; Options + +(defgroup magit-section nil + "Expandable sections." + :group 'magit) + +(defcustom magit-section-show-child-count t + "Whether to append the number of childen to section headings." + :package-version '(magit . "2.1.0") + :group 'magit-section + :type 'boolean) + +(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 unhighligher, +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-revision-set-visibility) + "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.1.0") + :group 'magit-section + :type 'hook + :options '(magit-diff-expansion-threshold magit-revision-set-visibility)) + +(defface magit-section-highlight + '((((class color) (background light)) :background "grey85") + (((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 blobs 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))) + +;;; 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)) + +(defvar magit-section-movement-hook + '(magit-hunk-set-window-start + magit-log-maybe-show-commit + magit-log-maybe-show-more-commits) + "Hook run by `magit-section-goto'. +That function in turn is used by all section movement commands.") + +(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 (sym title &optional value) + "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-let (magit-get-section + (cons '(,sym . ,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" title)))) + (put ',fun 'definition-name ',sym)))) + +;;;; 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)) + (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. +Additionall 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--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.") + +(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 secton 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))) + (magit-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))))) + +(defun magit-insert (string &optional face &rest args) + "Insert the strings STRING and ARGS at point. + +First insert STRING, possibly doing some crazy things as +described below; then insert ARGS as is, in a totally sane +fashion. + +This function owes its existence to the fact that Emacs does +not implement negative overlay priorities, and that some time +in the past it was decided that this is not acceptable and that +such negative prioritize have to be faked. I wish we had shown +some restrain, but here we are. At least this madness is now +contained in this function and `magit-put-face-property'. + +Insert STRING and then create an overlay on the inserted text, +which sets the `face' property. If optional FACE is non-nil, +then use that face. Otherwise use the first `face' property +found in STRING." + (if face + (let ((start (point))) + (insert string) + (magit-put-face-property start (point) face)) + (let ((buf (current-buffer)) + (offset (1- (point)))) + (with-temp-buffer + (save-excursion (insert string)) + (while (not (eobp)) + (let* ((beg (point)) + (end (or (next-single-property-change beg 'face) + (point-max))) + (face (get-text-property beg 'face)) + (text (buffer-substring-no-properties beg end))) + (with-current-buffer buf + (insert text) + (when face + (magit-put-face-property (+ beg offset) + (+ end offset) face))) + (goto-char end)))))) + (apply #'insert args)) + +(defun magit-put-face-property (start end face) + (let ((ov (make-overlay start end nil t))) + (overlay-put ov 'face face) + (overlay-put ov 'evaporate t) + ov)) + +;;; 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) + (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) + (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)))) + (goto-char (--if-let (magit-section-goto-successor-1 section) + (magit-section-start it) + (point-min)))))) + +(defun magit-section-goto-successor-1 (section) + (or (--when-let (and (eq (magit-section-type section) 'hunk) + (magit-get-section + (magit-section-ident + (magit-section-parent section)))) + (let ((children (magit-section-children it))) + (or (nth (length (magit-section-siblings section 'prev)) children) + (car (last children))))) + (--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))))) + +;;; 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 +including 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." + (when (use-region-p) + (let ((sections (magit-region-sections))) + (when (or (not types) + (--all-p (memq (magit-section-type it) types) sections)) + (mapcar 'magit-section-value sections))))) + +(defun 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 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." + (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))) + (cons sbeg (nreverse 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 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)))) + +;;; 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-20150704.855/magit-sequence.el b/elpa/magit-20150704.855/magit-sequence.el new file mode 100644 index 00000000..09c398ab --- /dev/null +++ b/elpa/magit-20150704.855/magit-sequence.el @@ -0,0 +1,624 @@ +;;; magit-sequence.el --- history manipulation in Magit + +;; 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. + +;;; 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) + (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 'magit-popup-sequence-mode + :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=" read-from-minibuffer)) + :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 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 'magit-popup-sequence-mode + :man-page "git-revert" + :switches '((?s "Add Signed-off-by lines" "--signoff")) + :options '((?s "Strategy" "--strategy=" read-from-minibuffer)) + :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 'magit-popup-sequence-mode + :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 (read-file-name "Apply patch: " nil + (magit-file-at-point)))) + (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) + (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 'magit-popup-sequence-mode + :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")) + :actions '((?r "Rebase" magit-rebase) + (?f "Autosquash" magit-rebase-autosquash) + (?o "Rebase subset" magit-rebase-from) + nil + (?e "Rebase interactive" magit-rebase-interactive) + (?s "Edit commit" magit-rebase-edit-commit) + (?l "Rebase unpushed" magit-rebase-unpushed) + (?w "Reword commit" magit-rebase-reword-commit)) + :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) + +;;;###autoload +(defun magit-rebase (upstream &optional args) + "Start a non-interactive rebase sequence. +All commits not in UPSTREAM are rebased. +\n(git rebase UPSTREAM[^] [ARGS])" + (interactive (list (magit-read-other-branch-or-commit + "Rebase onto" + (magit-get-current-branch) + (magit-get-tracked-branch)) + (magit-rebase-arguments))) + (message "Rebasing...") + (magit-run-git-sequencer "rebase" upstream args) + (message "Rebasing...done")) + +;;;###autoload +(defun magit-rebase-from (newbase start &optional args) + "Start a non-interactive rebase sequence. +Commits from START to `HEAD' onto NEWBASE. START has to be +selected from a list of recent commits. +\n(git rebase --onto NEWBASE START[^] [ARGS])" + (interactive (list (magit-read-other-branch-or-commit + "Rebase subset onto" + (magit-get-current-branch) + (magit-get-tracked-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-from ,newbase (concat commit "^") (list ,@args))) + (concat "Type %p on a commit to rebase it " + "and commits above it onto " newbase ",")))) + +;;;###autoload +(defun magit-rebase-interactive (commit &optional args) + "Start an interactive rebase sequence. +\n(git rebase -i COMMIT[^] [ARGS])" + (interactive (let ((commit (magit-commit-at-point))) + (list (and commit (concat commit "^")) + (magit-rebase-arguments)))) + (if (setq commit (magit-rebase-interactive-assert commit)) + (magit-run-git-sequencer "rebase" "-i" commit args) + (magit-log-select + `(lambda (commit) + (magit-rebase-interactive (concat commit "^") (list ,@args))) + "Type %p on a commit to interactively rebase it and all commits above it,"))) + +(defun magit-rebase-unpushed (commit &optional args) + "Start an interactive rebase sequence of all unpushed commits. +\n(git rebase -i UPSTREAM [ARGS])" + (interactive (list (magit-get-tracked-branch) + (magit-rebase-arguments))) + (if (setq commit (magit-rebase-interactive-assert commit)) + (magit-run-git-sequencer "rebase" "-i" commit args) + (magit-log-select + `(lambda (commit) + (magit-rebase-interactive (concat commit "^") (list ,@args))) + "Type %p on a commit to interactively rebase it and all commits above it,"))) + +;;;###autoload +(defun magit-rebase-autosquash (commit &optional args) + "Combine squash and fixup commits with their intended targets. +\n(git rebase -i COMMIT[^] --autosquash [ARGS])" + (interactive (list (magit-get-tracked-branch) (magit-rebase-arguments))) + (if (setq commit (magit-rebase-interactive-assert commit)) + (let ((process-environment process-environment)) + (setenv "GIT_SEQUENCE_EDITOR" "true") + (magit-run-git-sequencer "rebase" "-i" commit "--autosquash" args)) + (magit-log-select + `(lambda (commit) + (magit-rebase-autosquash (concat commit "^") (list ,@args))) + "Type %p on a commit to squash into it and commits above it,"))) + +;;;###autoload +(defun magit-rebase-edit-commit (commit &optional args) + "Edit a single older commit using rebase." + (interactive (list (magit-commit-at-point) + (magit-rebase-arguments))) + (if (setq commit (magit-rebase-interactive-assert commit)) + (let ((process-environment process-environment)) + (setenv "GIT_SEQUENCE_EDITOR" + "perl -i -p -e '++$x if not $x and s/^pick/edit/'") + (magit-run-git-sequencer "rebase" "-i" (concat commit "^") args)) + (magit-log-select + `(lambda (commit) + (magit-rebase-edit-commit commit (list ,@args))) + "Type %p on a commit to edit it,"))) + +;;;###autoload +(defun magit-rebase-reword-commit (commit &optional args) + "Reword a single older commit using rebase." + (interactive (list (magit-commit-at-point) + (magit-rebase-arguments))) + (if (setq commit (magit-rebase-interactive-assert commit)) + (let ((process-environment process-environment)) + (setenv "GIT_SEQUENCE_EDITOR" + "perl -i -p -e '++$x if not $x and s/^pick/reword/'") + (magit-run-git-sequencer "rebase" "-i" (concat commit "^") args)) + (magit-log-select + `(lambda (commit) + (magit-rebase-reword-commit commit (list ,@args))) + "Type %p on a commit to reword its message,"))) + +(defun magit-rebase-interactive-assert (commit) + (when commit + (if (magit-git-lines "rev-list" "--merges" (concat commit "..HEAD")) + (magit-read-char-case "Proceed despite merge in rebase range? " nil + (?c "[c]ontinue" commit) + (?s "[s]elect other" nil) + (?a "[a]bort" (user-error "Quit"))) + commit))) + +;;;###autoload +(defun magit-rebase-continue () + "Restart the current rebasing operation." + (interactive) + (if (magit-rebase-in-progress-p) + (if (magit-anything-unstaged-p) + (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 (magit-rebase-patches)) + (stop (magit-file-line (car patches)))) + (if (string-match "^From \\([^ ]\\{40\\}\\)" stop) + (progn (setq stop (match-string 1 stop)) + (magit-rebase-insert-apply-sequence) + (magit-sequence-insert-sequence stop "ORIG_HEAD")) + (setq patches (nreverse patches)) + (while patches + (let ((patch (pop patches))) + (magit-insert-section (file patch) + (insert (if patches + (propertize "pick" 'face 'magit-sequence-pick) + (propertize "stop" 'face 'magit-sequence-stop)) + ?\s (propertize (file-name-nondirectory patch) + 'face 'magit-hash) + ?\n)))) + (magit-sequence-insert-sequence nil "ORIG_HEAD"))) + (insert ?\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) + (setq onto (magit-rev-parse onto)) + (let ((head (magit-rev-parse "HEAD")) + (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"))) + (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-20150704.855/magit-stash.el b/elpa/magit-20150704.855/magit-stash.el new file mode 100644 index 00000000..9c85810a --- /dev/null +++ b/elpa/magit-20150704.855/magit-stash.el @@ -0,0 +1,412 @@ +;;; magit-stash.el --- stash support for Magit + +;; Copyright (C) 2008-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. + +;;; 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-save) + (?v "Show" magit-stash-show) + (?b "Branch" magit-stash-branch) + (?k "Drop" magit-stash-drop)) + :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." + (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")))) + (if (listp stash) + (mapc 'magit-stash-drop (nreverse 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 "Branch name"))) + (magit-run-git "stash" "branch" branch stash)) + +;;; 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 nil + "apply" "--reverse" "--cached" "--ignore-space-change" "-") + (magit-run-git-with-input nil + "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-reflog-enable ref t) + (unless (magit-git-success "update-ref" "-m" message ref commit + (or (magit-rev-verify ref) "")) + (error "Cannot update %s with %s" ref commit))) + +(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)) + (conflicts (magit-anything-unmerged-p)) + (summary (magit-stash-summary)) + (head "HEAD")) + (when (and worktree (not index) (not conflicts)) + (setq head (magit-commit-tree "pre-stash index" nil "HEAD"))) + (or (setq index (if conflicts + (magit-commit-tree (concat "index on " summary) + "HEAD^{tree}" "HEAD") + (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 + (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 + (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 "k" 'magit-stash-clear) + map) + "Keymap for `stashes' section.") + +(defvar magit-stash-section-map + (let ((map (make-sparse-keymap))) + (define-key map "\r" 'magit-stash-show) + (define-key map "a" 'magit-stash-apply) + (define-key map "A" 'magit-stash-pop) + (define-key map "k" 'magit-stash-drop) + map) + "Keymap for `stash' sections.") + +(magit-define-section-jumper 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) + (magit-insert-heading heading) + (magit-git-wash (apply-partially 'magit-log-wash-log 'stash) + "reflog" "--format=%gd %at %gs" ref)))) + +;;; List Stashes + +(defcustom magit-stashes-buffer-name-format "*magit-stashes: %a*" + "Name format for buffers used to list stashes. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'string) + +;;;###autoload +(defun magit-stash-list () + "List all stashes in a buffer." + (interactive) + (magit-mode-setup magit-stashes-buffer-name-format nil + #'magit-stashes-mode + #'magit-stashes-refresh-buffer)) + +(define-derived-mode magit-stashes-mode magit-reflog-mode "Magit Stashes" + "Mode for looking at lists of stashes." + :group 'magit + (hack-dir-local-variables-non-file-buffer)) + +(cl-defun magit-stashes-refresh-buffer (&optional (ref "refs/stash") + (heading "Stashes:")) + (magit-insert-section (stashesbuf) + (magit-insert-heading heading) + (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-message + magit-insert-stash-index + magit-insert-stash-worktree + magit-insert-stash-untracked) + "Hook run to insert sections into stash buffers." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'hook) + +(defcustom magit-stash-buffer-name-format "*magit-stash: %a*" + "Name format for buffers used to show stash diffs. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :package-version '(magit . "2.1.0") + :group 'magit-modes + :type 'string) + +;;;###autoload +(defun magit-stash-show (stash &optional noselect args) + "Show all diffs of a stash in a buffer." + (interactive (nconc (list (or (and (not current-prefix-arg) + (magit-stash-at-point)) + (magit-read-stash "Show stash")) + nil) + (magit-diff-read-args t))) + (magit-mode-setup magit-stash-buffer-name-format + (if noselect 'display-buffer 'pop-to-buffer) + #'magit-stash-mode + #'magit-stash-refresh-buffer stash args)) + +(define-derived-mode magit-stash-mode magit-diff-mode "Magit Stash" + "Mode for looking at individual stashes." + :group 'magit + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-stash-refresh-buffer (stash args) + (magit-insert-section (stash) + (run-hooks 'magit-stash-sections-hook))) + +(defun magit-insert-stash-message () + "Insert section showing the message of the stash." + (let ((stash (car magit-refresh-args))) + (magit-insert-section (stash-message) + (magit-insert + (concat (propertize (capitalize stash) 'face 'magit-section-heading) "\s" + (magit-rev-format "%s" stash) "\n"))))) + +(defmacro magit-stash-insert-section (subtype format &optional files) + `(let ((stash (car magit-refresh-args))) + (magit-insert-section (,(intern (format "stashed-%s" subtype))) + (magit-insert-heading (format "%s %s:" (capitalize stash) ',subtype)) + (magit-git-wash #'magit-diff-wash-diffs + "diff" (cdr magit-refresh-args) "--no-prefix" + (format ,format stash stash) "--" ,files)))) + +(defun magit-insert-stash-index () + "Insert section showing the index commit of the stash." + (magit-stash-insert-section index "%s^..%s^2")) + +(defun magit-insert-stash-worktree () + "Insert section showing the worktree commit of the stash." + (magit-stash-insert-section worktree "%s^2..%s")) + +(defun magit-insert-stash-untracked () + "Insert section showing the untracked files commit of the stash." + (let ((rev (concat (car magit-refresh-args) "^3"))) + (when (magit-rev-verify rev) + (magit-stash-insert-section + untracked "%s^..%s^3" + (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-20150704.855/magit-utils.el b/elpa/magit-20150704.855/magit-utils.el new file mode 100644 index 00000000..4a7c71c0 --- /dev/null +++ b/elpa/magit-20150704.855/magit-utils.el @@ -0,0 +1,352 @@ +;;; magit-utils.el --- various utilities + +;; Copyright (C) 2010-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 + +;; 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." + :group 'magit + :type '(radio (function-item magit-builtin-completing-read) + (function-item magit-ido-completing-read) + (function-item helm-completing-read-with-cands-in-buffer) + (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 + them in the reflog, once it 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 stages 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 abreviate text." + :package-version '(magit . "2.1.0") + :group 'magit + :type 'character) + +;;; 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)) + +(defun magit-read-string (prompt &optional initial-input history default-value) + "Like `read-string' but require non-empty input. +Empty input is only allowed if DEFAULT-VALUE is non-nil in +which case that is returned. Also append \": \" to PROMPT." + (let ((reply (read-string (magit-prompt-with-default + (concat prompt ": ") default-value) + initial-input history default-value))) + (if (string= reply "") + (user-error "Need non-empty input") + reply))) + +(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 varibles to submatches accoring 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-20150704.855/magit-wip.el b/elpa/magit-20150704.855/magit-wip.el new file mode 100644 index 00000000..37c359f1 --- /dev/null +++ b/elpa/magit-20150704.855/magit-wip.el @@ -0,0 +1,221 @@ +;;; magit-wip.el --- commit snapshots to work-in-progress refs + +;; Copyright (C) 2010-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. + +;;; 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 'format-spec) + +;;; 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) + (let* ((default-directory (magit-toplevel)) + (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" + :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-wip-commit files msg))) + +;;; Core + +(defun magit-wip-commit (&optional files msg) + "Commit all tracked files to the work-in-progress refs. + +Non-interactivly, on behalf of `magit-wip-before-change-hook', +only commit changes to FILES using MSG as commit message." + (interactive (list nil "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)))) + +(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 + (if files + (magit-call-git "add" "--" files) + (let ((default-directory (magit-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)))) + +(defun magit-wip-update-wipref (wipref tree parent files 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))) + (magit-reflog-enable wipref) + (unless (equal parent wipref) + (magit-call-git "update-ref" wipref "-m" "restart autosaving" + (magit-git-string "commit-tree" "-p" parent + "-m" "restart autosaving" + (concat parent "^{tree}"))) + (setq parent wipref)) + (magit-call-git "update-ref" wipref "-m" 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)) + +;;; 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-20150704.855/magit.el b/elpa/magit-20150704.855/magit.el new file mode 100644 index 00000000..f31cff5e --- /dev/null +++ b/elpa/magit-20150704.855/magit.el @@ -0,0 +1,2059 @@ +;;; magit.el --- A Git porcelain inside Emacs + +;; Copyright (C) 2008-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: Marius Vollmer +;; Maintainer: Jonas Bernoulli +;; Former-Maintainers: +;; Nicolas Dudebout +;; Peter J. Weisberg +;; Phil Jackson +;; Rémi Vanicat +;; Yann Hodique + +;; Package-Requires: ((emacs "24.4") (dash "2.10.0") (with-editor "2.1.0") (git-commit "2.1.0") (magit-popup "2.1.0")) +;; 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) + +;;; 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-head-header + magit-insert-upstream-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-repo-header + magit-insert-remote-header + magit-insert-head-header + magit-insert-upstream-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-commits + magit-insert-unpushed-commits) + "Hook run to insert sections into a status buffer." + :package-version '(magit . "2.1.0") + :group 'magit-status + :type 'hook) + +(defcustom magit-status-refresh-hook nil + "Hook run after a status buffer has been refreshed." + :package-version '(magit . "2.1.0") + :group 'magit-status + :type 'hook) + +(defcustom magit-status-buffer-switch-function 'pop-to-buffer + "Function used by `magit-status' to switch to a 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-buffer-name-format "*magit: %a*" + "Name format for buffers used to display a repository's status. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :package-version '(magit . "2.1.0") + :group 'magit-status + :type 'string) + +;;;; 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-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-buffer-name-format "*magit-refs: %a*" + "Name format for buffers used to display and manage refs. + +The following `format'-like specs are supported: +%a the absolute filename of the repository toplevel. +%b the basename of the repository toplevel." + :package-version '(magit . "2.1.0") + :group 'magit-refs + :type 'string) + +(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." + :package-version '(magit . "2.1.0") + :group 'magit + :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"))) + +;;;; Miscellaneous + +(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 local branches." + :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 + +(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 "jf" 'magit-jump-to-unpulled) + (define-key map "jp" 'magit-jump-to-unpushed) + 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'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-dispatch-popup] to see available action popups. +Type \\[magit-section-toggle] to expand or hide the section at point. +\\\ +Type \\[magit-diff-visit-file] to visit the change at point. +Type \\[magit-stage] to stage the change at point; \\[magit-unstage] to unstage. +\\\ +Type \\[magit-commit-popup] to create a commit. +\n\\{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) + +(defun magit-status-internal (directory &optional switch-function) + (let ((default-directory (file-name-as-directory + (expand-file-name directory)))) + (magit-mode-setup magit-status-buffer-name-format + (or switch-function + magit-status-buffer-switch-function) + #'magit-status-mode + #'magit-status-refresh-buffer))) + +(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)))" + (interactive) + (with-no-warnings ; FIXME these are internal variables + (setq ido-exit 'fallback fallback 'magit-status)) + (exit-minibuffer)) + +(defun magit-status-refresh-buffer () + (magit-git-exit-code "update-index" "--refresh") + (magit-insert-section (status) + (run-hooks 'magit-status-sections-hook)) + (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-repo-header () + "Insert a header line showing the path to the repository top-level." + (let ((topdir (magit-toplevel))) + (magit-insert-section (repo topdir) + (magit-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." + (-when-let (remote (magit-get-remote)) + (magit-insert-section (remote remote) + (magit-insert + (concat (format "%-10s" "Remote: ") + (propertize remote 'face 'magit-branch-remote) " " + (magit-get "remote" remote "url") "\n"))))) + +(cl-defun magit-insert-head-header + (&optional (branch (magit-get-current-branch))) + "Insert a header line about the `HEAD' commit." + (let ((output (magit-rev-format "%h %s" "HEAD"))) + (string-match "^\\([^ ]+\\) \\(.*\\)" output) + (magit-bind-match-strings (hash msg) output + (magit-insert-section it (branch (or branch hash)) + (unless branch + (setf (magit-section-type it) 'commit)) + (magit-insert + (concat + (format "%-10s" "Head: ") + (propertize hash 'face 'magit-hash) " " + (and branch + (concat (propertize branch 'face 'magit-branch-local) " ")) + msg "\n")))))) + +(cl-defun magit-insert-upstream-header + (&optional (branch (magit-get-current-branch)) + (upstream (magit-get-tracked-branch branch))) + "Insert a header line about the upstream branch and its tip." + (-when-let (string (and upstream (magit-rev-format "%h %s" upstream))) + (string-match "^\\([^ ]+\\) \\(.*\\)" string) + (magit-bind-match-strings (hash msg) string + (magit-insert-section (branch upstream) + (magit-insert + (concat + (format "%-10s" "Upstream: ") + (if hash (propertize hash 'face 'magit-hash) "missing") " " + (and (magit-get-boolean "branch" branch "rebase") "onto ") + (propertize upstream 'face + (if (string= (magit-get "branch" branch "remote") ".") + 'magit-branch-local + 'magit-branch-remote)) + " " msg "\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)) + (magit-insert + (concat + (format "%-10s" (if both-tags "Tags: " "Tag: ")) + (and this-tag (magit-format-status-tag-sentence this-tag this-cnt nil)) + (and both-tags ", ") + (and next-tag (magit-format-status-tag-sentence next-tag next-cnt t)) + "\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-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) + (magit-insert + (concat (format "%-10s" "User: ") + (propertize name 'face 'magit-log-author) + " <" email ">" "\n")))))) + +(magit-define-section-jumper tracked "Tracked files") + +(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)))) + +(magit-define-section-jumper untracked "Untracked files") + +(defvar magit-untracked-section-map + (let ((map (make-sparse-keymap))) + (define-key map "k" '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) + +;;;; 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)Branches and Remotes'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-branch-popup] to see available branch commands. +Type \\[magit-show-commit] or \\[magit-diff-show-or-scroll-up]\ + to visit the commit at point. +Type \\[magit-merge-popup] to merge the branch or commit at point. +Type \\[magit-cherry-pick] to cherry-pick the commit at point. +Type \\[magit-reset-head] to reset HEAD to the commit at point. +\n\\{magit-refs-mode-map}" + :group 'magit-modes + (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-buffer-name-format nil + #'magit-refs-mode + #'magit-refs-refresh-buffer 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))) + +(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 "\r" 'magit-show-commit) + (define-key map "k" '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 "k" '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-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 "(" 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 (and (or (equal branch head) + (and (not branch) (equal head "HEAD"))) + (if (equal branch current) + (propertize "@" 'face 'magit-head) + (propertize "#" 'face 'magit-tag))))) + (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 "\r" 'magit-show-commit) + (define-key map "k" '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))) + (magit-insert-section section (tag tag t) + (magit-insert-heading + (format-spec format + `((?n . ,(propertize tag 'face 'magit-tag)) + (?c . ,(or 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 non 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-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)) + +(defun magit-find-file-noselect (rev file) + "Read FILE from REV into a buffer and return the buffer." + (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 (magit-toplevel))) + (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-file-hook) + (current-buffer)))) + +(defun magit-find-file-index-noselect (file &optional revert) + "Read FILE from the index into a buffer and return the buffer." + (let* ((bufname (concat file ".~{index}~")) + (origbuf (get-buffer bufname))) + (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 + (magit-git-string "checkout-index" "--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 (magit-toplevel))) + (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))) + (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)) + (message "Abort"))) + (--when-let (magit-mode-get-buffer + magit-status-buffer-name-format '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 (or (magit-file-at-point) default-directory))) + +;;; 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))) + (make-directory directory t) + ;; `git init' does not understand the meaning of "~"! + (magit-call-git "init" (expand-file-name directory)) + (magit-status-internal directory)) + +;;;; Branch + +;;;###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" + :switches '((?t "Set upstream configuration" "--track")) + :actions '((?b "Checkout" magit-checkout) + (?u "Set upstream" magit-branch-set-upstream) + (?k "Delete" magit-branch-delete) + (?c "Create" magit-branch) + (?U "Unset upstream" magit-branch-unset-upstream) + (?r "Rename" magit-branch-rename) + (?B "Create & Checkout" magit-branch-and-checkout) + (?e "Set description" magit-branch-edit-description)) + :default-arguments '("--track") + :default-action 'magit-checkout + :max-action-columns 3) + +;;;###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)) + +(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-run-git-no-revert "branch" args branch start-point)) + +;;;###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" + (magit-stash-at-point))) + (if (string-match-p "^stash@{[0-9]+}$" start-point) + (magit-run-git "stash" "branch" branch start-point) + (magit-run-git "checkout" args "-b" branch start-point))) + +(defun magit-branch-read-args (prompt &optional secondary-default) + (let* ((args (magit-branch-arguments)) + (start (magit-read-branch-or-commit (concat prompt " starting at") + secondary-default)) + (branch + (magit-read-string + "Branch name" + (and (member start (magit-list-remote-branch-names)) + (mapconcat #'identity (cdr (split-string start "/")) "/"))))) + (when (and (member "--track" args) + (not (magit-branch-p start))) + (setq args (delete "--track" args))) + (list branch start 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-set-upstream (branch upstream) + "Change the UPSTREAM branch of BRANCH." + (interactive + (let ((branch (magit-read-local-branch "Change upstream of branch"))) + (list branch (magit-completing-read + "Change upstream to branch" + (delete branch (magit-list-branch-names)) + nil nil nil 'magit-revision-history)))) + (magit-run-git-no-revert "branch" (concat "--set-upstream-to=" upstream) + branch)) + +;;;###autoload +(defun magit-branch-unset-upstream (branch) + "Unset the upstream branch of BRANCH." + (interactive (list (magit-read-local-branch "Unset upstream of branch"))) + (magit-run-git-no-revert "branch" "--unset-upstream" branch)) + +;;;###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 (format "Rename branch '%s' to" branch)) + current-prefix-arg))) + (unless (string= old new) + (magit-run-git-no-revert "branch" (if force "-M" "-m") old new))) + +;;;###autoload +(defun magit-branch-edit-description (branch) + "Edit the description of BRANCH." + (interactive (list (magit-read-local-branch "Edit branch description"))) + (magit-run-git-with-editor "branch" "--edit-description")) + +(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"))))) + +;;;; Merge + +;;;###autoload (autoload 'magit-merge-popup "magit" nil t) +(magit-define-popup magit-merge-popup + "Popup console for merge commands." + 'magit-commands 'magit-popup-sequence-mode + :man-page "git-merge" + :switches '((?f "Fast-forward only" "--ff-only") + (?n "No fast-forward" "--no-ff") + (?s "Squash" "--squash")) + :options '((?s "Strategy" "--strategy=" read-from-minibuffer)) + :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-diff-buffer-name-format + magit-diff-switch-buffer-function + #'magit-diff-mode + #'magit-merge-refresh-preview-buffer rev)) + +(defun magit-merge-refresh-preview-buffer (rev) + (magit-insert-section (diffbuf) + (let* ((branch (magit-get-current-branch)) + (head (or branch (magit-rev-verify "HEAD")))) + (magit-insert-heading (format "Preview merge of %s into %s" + rev (or branch "HEAD"))) + (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 &optional restore-conflict) + "During a conflict checkout and stage side, or restore conflict." + (interactive + (let ((default-directory (magit-toplevel)) + (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)) + (t + (user-error "Quit"))))) + (if restore-conflict + (progn + (with-temp-buffer + (insert "0 0000000000000000000000000000000000000000\t" file "\n") + (--> (magit-git-string "ls-tree" (magit-git-string + "merge-base" "MERGE_HEAD" "HEAD") + file) + (replace-regexp-in-string "\t" " 1\t" it) + (insert it "\n")) + (--> (magit-git-string "ls-tree" "HEAD" file) + (replace-regexp-in-string "\t" " 2\t" it) + (insert it "\n")) + (--> (magit-git-string "ls-tree" "MERGE_HEAD" file) + (replace-regexp-in-string "\t" " 3\t" it) + (insert it "\n")) + (magit-run-git-with-input nil "checkout" arg file)) + (magit-refresh)) + (magit-call-git "checkout" arg file) + (if (string= arg "--merge") + (magit-refresh) + (magit-run-git "add" 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-args)) + (unless (member "--decorate=full" magit-log-section-args) + (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 effectivley 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) + (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))) + (magit-wip-commit-before-change nil " before reset") + (magit-run-git "reset" arg commit "--")) + +;;;; 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)) + (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)) + choices))))) + +(defun magit-read-files (prompt initial-contents) + (mapconcat 'identity + (completing-read-multiple (or prompt "File,s: ") + (magit-list-files) + nil nil initial-contents) ",")) + +;;; 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 + (add-to-list 'args "--annotate")) + 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-no-revert "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-list-remote-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-no-revert + "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 'magit-popup-sequence-mode + :man-page "git-tag" + :switches '((?n "Dry run" "--dry-run")) + :options '((?r "Manipulate ref" "--ref=" magit-notes-popup-read-ref) + (?s "Merge strategy" "--strategy=" read-from-minibuffer)) + :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" "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 "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)) + (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-no-revert "config" (and global "--global") "core.notesRef" + (if (string-prefix-p "refs/" ref) + ref + (concat "refs/notes/" ref))) + (magit-run-git-no-revert "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)) + (let ((inhibit-magit-revert t)) + (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 "^[^.]")))) + +;;;; Submodules + +;;;###autoload (autoload 'magit-submodule-popup "magit" 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))) + +;;;###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 + (let* ((default-directory (magit-toplevel)) + (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 + "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" ""))))) + +;;;; 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 '((?b "Branching" magit-branch-popup) + (?B "Bisecting" magit-bisect-popup) + (?c "Committing" magit-commit-popup) + (?d "Diffing" magit-diff-popup) + (?f "Fetching" magit-fetch-popup) + (?F "Pulling" magit-pull-popup) + (?g "Refresh Buffers" magit-refresh-all) + (?l "Logging" magit-log-popup) + (?m "Merging" magit-merge-popup) + (?M "Remoting" magit-remote-popup) + (?P "Pushing" magit-push-popup) + (?o "Submoduling" magit-submodule-popup) + (?r "Rebasing" magit-rebase-popup) + (?w "Apply patches" magit-am-popup) + (?W "Format patches" magit-patch-popup) + (?s "Show Status" magit-status) + (?S "Stage all" magit-stage-modified) + (?t "Tagging" magit-tag-popup) + (?U "Reset Index" magit-reset-index) + (?v "Show Commit" magit-show-commit) + (?V "Show File" magit-find-file) + (?y "Show Refs" magit-show-refs-popup) + (?Y "Cherry" magit-cherry) + (?z "Stashing" magit-stash-popup) + (?! "Running" magit-run-popup) + (?$ "Show Process" magit-process))) + +;;;; 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 (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)) + :default-action 'magit-git-command) + +;;;###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'. + +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 top-level directory 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-toplevel) + (user-error "Not inside a Git repository")) + default-directory))) + (list (magit-read-string (format "Git subcommand (in %s)" + (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)) + +;;;; Various + +(defun magit-copy-as-kill () + "Save the value of the current section to the kill ring. +For commits save the full hash. For branches do so only when +a prefix argument is used, otherwise save the branch name." + (interactive) + (-when-let (section (magit-current-section)) + (let ((value (magit-section-value section))) + (magit-section-case + (branch (when current-prefix-arg + (setq value (magit-rev-parse value)))) + (commit (setq value (magit-rev-parse value))) + (module-commit (let ((default-directory + (file-name-as-directory + (expand-file-name + (magit-section-parent-value section) + (magit-toplevel))))) + (setq value (magit-rev-parse value)))) + (t value)) + (kill-new (message "%s" value))))) + +(defun magit-copy-buffer-thing-as-kill () + "Save the thing displayed in the current buffer to the kill ring." + (interactive) + (--when-let (cond ((derived-mode-p 'magit-diff-mode + 'magit-cherry-mode + 'magit-reflog-mode + 'magit-refs-mode + 'magit-revision-mode + 'magit-stash-mode) + (car magit-refresh-args)) + ((derived-mode-p 'magit-log-mode) + (if magit-log-select-pick-function + (car magit-refresh-args) + (cadr magit-refresh-args))) + ((derived-mode-p 'magit-status-mode) + (or (magit-get-current-branch) "HEAD")) + ((derived-mode-p 'magit-stashes-mode) + "refs/stash") + (t nil)) + (kill-new (message "%s" it)))) + +;;; 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.") + +(defun magit-version (&optional noerror) + "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.\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" (file-name-directory (directory-file-name dir))))) + (cond ((file-exists-p gitdir) + (setq magit-version + (let ((default-directory dir)) + (magit-git-string "describe" "--tags" "--dirty"))) + (unless noninteractive + (ignore-errors (delete-file static)))) + ((file-exists-p static) + (load-file static)) + ((featurep 'package) + (--when-let (assq 'magit package-alist) + (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" + magit-version + (substring (magit-git-string "version") 12) + emacs-version)) + (if noerror + (progn (setq magit-version 'error) + (message "Cannot determine Magit's version")) + (error "Cannot determine Magit's version"))) + magit-version)) + +(defun magit-startup-asserts () + (let ((version (substring (magit-git-string "version") 12))) + (when version + (when (string-match "^\\([0-9]+\\.[0-9]+\\.[0-9]+\\)" version) + (setq version (match-string 1 version))) + (when (version< version "1.9.4") + (display-warning 'magit (format "\ +Magit requires Git >= 1.9.4, 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" version) :error)))) + (when (version< emacs-version "24.4") + (display-warning 'magit (format "\ +Magit requires Emacs >= 24.4, 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" 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)))) + +(provide 'magit) + +(cl-eval-when (load eval) + (magit-version t) + (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-ediff) + (require 'magit-extras) + (require 'git-rebase))) + +(if after-init-time + (magit-startup-asserts) + (add-hook 'after-init-hook #'magit-startup-asserts)) + +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: +;;; magit.el ends here diff --git a/elpa/magit-20150704.855/magit.info b/elpa/magit-20150704.855/magit.info new file mode 100644 index 00000000..0163775c --- /dev/null +++ b/elpa/magit-20150704.855/magit.info @@ -0,0 +1,6269 @@ +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 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 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:: +* Automatic save:: +* Automatic refresh and revert:: +* Sections:: +* Popup buffers and prefix commands:: +* Completion and confirmation:: +* Running Git:: + +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 + +* 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:: + +Cherry picking + +* Reverting:: + +Transferring + +* Remotes:: +* Fetching:: +* Pulling:: +* Pushing:: +* Creating and sending patches:: +* Applying patches:: + +Miscellaneous + +* Tagging:: +* Notes:: +* Submodules:: +* Wip Modes:: + +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 am having problems committing:: +* I am using an Emacs release older than 24.4: I am using an Emacs release older than 244. +* I am using a Git release older than 1.9.4: I am using a Git release older than 194. +* 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:: +* Magit claims repository accessed using Tramp does not exist:: +* 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. + + + +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 it is +capable off 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 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 all three of the popular unofficial Elpa +archives: Melpa, Melpa-Stable, and Marmalade. 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) + + • To use Marmalade: + + (require 'package) + (add-to-list 'package-archives + '("marmalade" . "http://marmalade-repo.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’ library, available from all three of the +popular third-party Elpa archives. Install it using ‘M-x +install-package RET dash RET’. Of course you may also install it +manually from its development repository, but I won’t cover that here. + + Then clone the Magit repository: + + $ git clone git://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’ using Elpa or at +‘/path/to/magit/../dash’, then you have to tell ‘make’ where to find it. +To do so create ‘/path/to/magit/config.mk’ with the following content +before running ‘make’: + + LOAD_PATH = -L /path/to/magit/lisp -L /path/to/dash + + 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. + + Instead of requiring the feature ‘magit’, you could only load the +autoloads, 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.1.0, Git 2.4.2, 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 (and some other commands +which are not popups), type ‘h’. Try a few. + + In most cases 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) + + +File: magit.info, Node: Interface concepts, Next: Inspecting, Prev: Getting started, Up: Top + +4 Interface concepts +******************** + +* Menu: + +* Modes and Buffers:: +* Automatic save:: +* Automatic refresh and revert:: +* Sections:: +* Popup buffers and prefix commands:: +* Completion and confirmation:: +* Running Git:: + + +File: magit.info, Node: Modes and Buffers, Next: Automatic save, 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. + + In this manual we often speak about "Magit buffers". By that we mean +buffers whose major-modes derive from ‘magit-mode’. + + 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. + +‘q’ (‘magit-mode-bury-buffer’) + + This command buries the current Magit buffer. With a prefix + argument, it instead kills the buffer. + + If ‘magit-restore-window-configuration’ is non-nil and the last + configuration stored by ‘magit-mode-display-buffer’ originates from + the selected frame (which usually is the case), then that is + restored after burying or killing the buffer. + + -- User Option: magit-restore-window-configuration + + This option controls whether quitting a Magit buffer restores the + previous window configuration. + + While it often is enough to have one buffer of a certain Magit mode +per repository, this is not always the case. You might, for example, +want to view the diffs for two commits at the same time. To do so, +first rename the existing revision buffer. Then show another commit. +Usually that would reuse the existing buffer, but because its name no +longer matches the default name, a new buffer is created instead. The +new buffer uses the default name, and therefore will be reused to show +other commits as usual. + +‘M-x magit-rename-buffer’ (‘magit-rename-buffer’) + + This command changes the name of the current Magit buffer by + appending ‘’ and thereby keeping Magit from re-using it to + display other information of the same kind. + + With a prefix argument, the user can pick an arbitrary name. + + +File: magit.info, Node: Automatic save, Next: Automatic refresh and revert, Prev: Modes and Buffers, Up: Interface concepts + +4.2 Automatic save +================== + +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 refresh and revert, Next: Sections, Prev: Automatic save, Up: Interface concepts + +4.3 Automatic refresh and revert +================================ + +After running a commands which may change the state of the current +repository, the current Magit buffer and the corresponding status buffer +are refreshed. This ensures that the displayed information is +up-to-date 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. + + -- User Option: magit-revert-buffers + + This option controls if and how file-visiting buffers in the + current repository are reverted. + + Unmodified buffers visiting files belonging to the current + repository may be reverted after refreshing the current Magit + buffer and after running certain other commands. + + • ‘nil’ + + Don’t revert any buffers. + + • ‘ask’ + + List the buffers which might potentially have to be reverted + and ask the user whether she wants to revert them. If so, + then do it synchronously. + + • ‘t’ + + Revert the buffers synchronously, mentioning each one as it is + being reverted and then also show a summary in the echo area. + + • ‘usage’ + + Like ‘t’ but include usage information in the summary. This + is the default so that users come here and pick what is right + for them. + + • ‘silent’ + + Revert the buffers synchronously and be quiet about it. + + • NUMBER + + An integer or float. Revert the buffers asynchronously, + mentioning each one as it is being reverted. If user input + arrives, then stop reverting. After NUMBER seconds resume + reverting. + + 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-after-revert-hook + + This hook is run in each file-visiting buffer belonging to the + current repository that was actually reverted during a refresh. + + Note that adding something here is very expensive. If you + experience performance issues, you might want to check this hook, + as well as ‘magit-not-reverted-hook’ and, if possible, remove some + of the functions added by third-party packages. + + -- User Option: magit-not-reverted-hook + + This hook is run in each file-visiting buffer belonging to the + current repository that was reverted during a refresh. The file + was not reverted because it did not change, and so Magit does not + have to do anything. This hook is intended for third-party + extensions that need to run some functions even on such files. + + +File: magit.info, Node: Sections, Next: Popup buffers and prefix commands, Prev: Automatic refresh and revert, Up: Interface concepts + +4.4 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 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.4.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. + +‘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. + +‘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-commit + + This hook function shows the commit at point in another window. If + the section at point is a ‘commit’ section and the value of + ‘magit-diff-auto-show-p’ calls for it, then the commit is shown in + another window, using ‘magit-show-commit’. + + -- 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. + + +File: magit.info, Node: Section visibility, Next: Section hooks, Prev: Section movement, Up: Sections + +4.4.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 + currect 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.4.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 depending on + APPEND. If only FUNCTION is a member of the list, then leave it + wherever 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.4.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.4.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.5 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.6 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.7 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.7.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.7.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’) + + Show the popup buffer featuring the below suffix commands. + +‘! !’ (‘magit-git-command-topdir’) + + Execute a Git subcommand asynchronously, displaying the output. + + With a prefix argument run Git in the root of the current + repository. + +‘! :’ (‘magit-git-command’) + + Execute a Git subcommand asynchronously, displaying the output. + Run Git in the top-level directory of the current repository. + + This popup also features some commands that start external gui tools. + +‘! g’ (‘magit-run-git-gui’) + + Run ‘git gui’ for the current git repository. + +‘! k’ (‘magit-run-gitk’) + + Run Gitk for the current git repository. + + Run ‘gitk --all’. With a prefix argument run gitk without any + arguments. + + +File: magit.info, Node: Git executable, Next: Global Git arguments, Prev: Running Git manually, Up: Running Git + +4.7.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.7.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 that keymap can also be customized in a + sane fashion: + + (define-key ido-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-commits + + Insert section showing unpulled commits. + + -- Function: magit-insert-unpushed-commits + + Insert section showing unpushed commits. + + 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-head-header + + Insert a header line about the ‘HEAD’ commit. + + -- Function: magit-insert-upstream-header + + Insert a header line about the upstream branch and its tip. + + -- 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. + + +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-status-buffer-switch-function + + Function used by ‘magit-status’ to switch to a status buffer. The + function is given one argument, the status buffer. + + -- User Option: magit-status-buffer-name-format + + Name format for buffers used to display a repository’s status. + + The following ‘format’-like specs are supported: ‘%a’ the absolute + filename of the repository top-level. ‘%b’ the basename of the + repository top-level. + + -- 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 +=========== + +Also see *note (gitman)git-log:: . + +‘l’ (‘magit-log-popup’) + + This prefix commands 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: + +* Log Buffer:: +* Select from log:: +* Reflog:: + + +File: magit.info, Node: Log Buffer, Next: Select from log, Up: Logging + +5.2.1 Log Buffer +---------------- + +‘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. + +‘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. + + -- 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. + +‘+’ (‘magit-log-show-more-commits’) + + Increase the number of commits shown in current log. + + With no prefix argument, show twice as many commits as before. + With a numerical prefix argument, show this many additional + commits. With a non-numeric prefix argument, show all commits. + + When no limit was previously imposed in the current buffer, set the + local limit to the default limit instead (or if that is nil then + 100), regardless of the prefix argument. + + By default ‘magit-log-cutoff-length’ commits are shown. + + -- 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. + +‘L’ (‘magit-toggle-margin’) + + Show or hide the Magit margin. + + -- User Option: magit-log-cutoff-length + + The maximum number of commits to show in log and reflog buffers. + + +File: magit.info, Node: Select from log, Next: Reflog, Prev: Log Buffer, Up: Logging + +5.2.2 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.3 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 +=========== + +Also see *note (gitman)git-diff:: . + +‘d’ (‘magit-diff-popup’) + + This prefix commands 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. + +‘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. + +‘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. + +‘M-x magit-diff-unpushed’ (‘magit-diff-unpushed’) + + Show unpushed changes. + +‘M-x magit-diff-unpulled’ (‘magit-diff-unpulled’) + + Show unpulled changes. + +* 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 ‘magit-diff-popup’ described in the previous section is used to +display a log in the current repositories diff buffer. The following +popup is used to change the arguments used to generate the diff or diffs +in the current buffer, the status buffer or the diff buffer. + + In the diff buffer you can of course just use ‘magit-diff-popup’, but +‘magit-diff-refresh-popup’ is more convenient because you don’t have to +again specify which differences to show. In the status buffer this +popup is the only way to change certain arguments. + +‘D’ (‘magit-diff-refresh-popup’) + + This prefix commands shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘D g’ (‘magit-diff-refresh’) + + Set the local diff arguments for the current buffer. + +‘D s’ (‘magit-diff-set-default-arguments’) + + Set the global diff arguments for the current buffer. + +‘D w’ (‘magit-diff-save-default-arguments’) + + Set and save the global diff arguments for the current buffer. + +‘D t’ (‘magit-diff-toggle-refine-hunk’) + + Toggle hunk refinement on or off. + + In addition to the above popup, which allow changing any of the +supported arguments, there also exist some commands which change a +particular argument. + +‘+’ (‘magit-diff-less-context’) + + Decrease the context for diff hunks by COUNT lines. + +‘-’ (‘magit-diff-more-context’) + + Increase the context for diff hunks by COUNT lines. + +‘0’ (‘magit-diff-default-context’) + + Reset context for diff hunks to the default height. + + While all of the above commands change *how* some change is being +displayed without changing *what* change is being displayed, the +following commands do the opposite: they change what is being displayed +but not how. + +‘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. + + This binding is available in the diff buffer as well as the commit + message buffer. + +‘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. + + +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-show-diffstat + + Whether to show diffstat in diff buffers. + + -- User Option: magit-diff-show-xref-buttons + + Whether to show buffer history buttons in diff buffers. + + -- User Option: magit-diff-auto-show + + Whether to automatically show the relevant diff or commit. + + When this option is non-nil certain operations cause the relevant + changes to be displayed automatically. + + • ‘commit’ + + • ‘stage-all’ + + • ‘log-oneline’ + + • ‘log-follow’ + + • ‘log-select’ + + • ‘blame-follow’ + In the event that expanding very large patches takes a long time, + ‘C-g’ 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. + + -- 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-show-diffstat + + Whether to show diffstat in revision buffers. + + -- User Option: magit-revision-show-notes + + Whether to show notes in revision buffers. + + -- User Option: magit-revision-show-xref-buttons + + Whether to show buffer history buttons in revision buffers. + + -- User Option: magit-revision-insert-related-refs + + Whether to show related refs in revision buffers. + + +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 commands shows the following suffix commands in a popup + buffer. + +‘E d’ (‘magit-ediff-compare’) + + Compare two revisions of a file using Ediff, defaulting to the file + at point. + +‘E m’ (‘magit-ediff-resolve’) + + Resolve outstanding conflicts in a file using Ediff, defaulting to + the file at point. + + 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. + + +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. + +* 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 commands 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’) + + By default this behaves just like ‘magit-blame’. With a prefix + argument instead show a popup buffer featuring a few infix + arguments and a single suffix command (‘magit-blame’). + +‘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. + +‘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’) + + 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. + +‘M-x magit-clone’ (‘magit-clone’) + + Clone a repository. The user is queried for a remote url and a + local directory. + + +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~/~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. + +‘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. + +‘U’ (‘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’. + + So ‘U RET’ with no commit at point does the inverse of ‘S’ (or + actually ‘S yes RET’), i.e. "unstage all staged changes". If you + would rather use a command which always does just that, then rebind + ‘U’ to ‘magit-unstage-all’. + +‘M-x magit-unstage-all’ (‘magit-unstage-all’) + + Remove all changes from the staging area. + +* 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. With a prefix + argument and if necessary, attempt a 3-way merge. + +‘k’ (‘magit-discard’) + + Remove the change at point from the working tree. + +‘v’ (‘magit-reverse’) + + Reverse the change at point in the working tree. + + +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 commands 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 change the committer date. The option + ‘magit-commit-extend-override-date’ can be used to inverse the + meaning of the prefix argument. + +‘c r’ (‘magit-commit-reword’) + + Reword the last commit, ignoring staged changes. + + With a prefix argument change the committer date. 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. + +‘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. 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. + + -- 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 additon 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. + + 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 +============= + +Also see *note (gitman)git-branch:: and *note (gitman)git-checkout:: . + +‘b’ (‘magit-branch-popup’) + + This prefix commands shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘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 c’ (‘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. + +‘b B’ (‘magit-branch-and-checkout’) + + This command creates a new branch like ‘magit-branch’, but then + also checks it out. + +‘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 u’ (‘magit-branch-set-upstream’) + + Change the upstream branch of a branch. Both branches are read in + the minibuffer, while providing reasonable defaults. + +‘b U’ (‘magit-branch-unset-upstream’) + + Unset the upstream branch of a branch read in the minibuffer and + defaulting to the branch at point or the current branch. + +‘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. + +‘b e’ (‘magit-branch-edit-description’) + + Edit the description of a branch. The branch is read in the + minibuffer defaulting to the branch at point or the current branch. + The description is edited in a regular buffer similar to how commit + messages are edited. + + +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 commands 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 commands 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 r’ (‘magit-rebase’) + + Start a non-interactive rebase sequence. All commits not in + UPSTREAM are rebased. + +‘r o’ (‘magit-rebase-from’) + + 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. + +‘r e’ (‘magit-rebase-interactive’) + + Start an interactive rebase sequence. + +‘r f’ (‘magit-rebase-autosquash’) + + Combine squash and fixup commits with their intended targets. + +‘r s’ (‘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:: + + +File: magit.info, Node: Editing rebase sequences, 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 if any. + +‘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: 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 commands 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 commands 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. + +‘U’ (‘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. + + +File: magit.info, Node: Stashing, Prev: Resetting, Up: Manipulating + +6.10 Stashing +============= + +Also see *note (gitman)git-stash:: . + +‘z’ (‘magit-stash-popup’) + + This prefix commands 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. + +‘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 commands 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. + + +File: magit.info, Node: Fetching, Next: Pulling, Prev: Remotes, Up: Transferring + +7.2 Fetching +============ + +Also see *note (gitman)git-fetch:: . + +‘f’ (‘magit-fetch-popup’) + + This prefix commands shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘f f’ (‘magit-fetch-current’) + + Fetch from the upstream repository of the current branch. If + ‘HEAD’ is detached or if the upstream is not configured, then read + the remote. + +‘f o’ (‘magit-fetch’) + + Fetch from another repository. + +‘f a’ (‘magit-fetch-all’) + + Fetch from all configured remotes. + +‘f m’ (‘magit-submodule-fetch’) + + Fetch all submodules. With a prefix argument fetch all remotes or + all submodules. + + +File: magit.info, Node: Pulling, Next: Pushing, Prev: Fetching, Up: Transferring + +7.3 Pulling +=========== + +Also see *note (gitman)git-pull:: . + +‘F’ (‘magit-pull-popup’) + + This prefix commands shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘F F’ (‘magit-pull-current’) + + Fetch and merge into current branch. + +‘F o’ (‘magit-pull’) + + Fetch from another repository and merge a fetched branch. + + +File: magit.info, Node: Pushing, Next: Creating and sending patches, Prev: Pulling, Up: Transferring + +7.4 Pushing +=========== + +Also see *note (gitman)git-push:: . + +‘P’ (‘magit-push-popup’) + + This prefix commands shows the following suffix commands along with + the appropriate infix arguments in a popup buffer. + +‘P P’ (‘magit-push-current’) + + Push the current branch to its upstream branch. If the upstream + isn’t set, then read the remote branch. + +‘P o’ (‘magit-push’) + + Push a branch to its upstream branch. If the upstream isn’t set, + then read the remote branch. + +‘P e’ (‘magit-push-elsewhere’) + + Push a branch or commit to some remote branch. Read the local and + remote branch. + +‘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. + + +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 commands 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 commands 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:: +* Wip Modes:: + + +File: magit.info, Node: Tagging, Next: Notes, Up: Miscellaneous + +8.1 Tagging +=========== + +Also see *note (gitman)git-tag:: . + +‘t’ (‘magit-tag-popup’) + + This prefix commands 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 commands 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: Wip Modes, Prev: Notes, Up: Miscellaneous + +8.3 Submodules +============== + +Also see *note (gitman)git-submodule:: . + +‘o’ (‘magit-submodule-popup’) + + This prefix commands 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. + + +File: magit.info, Node: Wip Modes, Prev: Submodules, Up: Miscellaneous + +8.4 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 recover a lost change from a wip ref, use the reflog. To show the +reflog, use e.g. ‘lO refs/wip/index/refs/heads/master RET’ and then +move around until you find the commit which has the lost change. You +might then be able to simply apply it using ‘a’ (‘magit-apply’). + + 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. + + -- 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: 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 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. + + Buffers visiting files tracked in the current repository are being +refreshed before certain actions. See *note Automatic refresh and +revert: Automatic refresh and revert. This isn’t as risky as it might +seem. If a buffer is modified (i.e. it contains changes that haven’t +been saved yet), then Emacs/Magit would refuse to revert it. If the +buffer has been saved resulting in what is seen by Git as an uncommitted +change, then Git in turn would refuse to carry out the action that would +cause these changes to be lost. Since Git doesn’t do anything, the file +doesn’t change on disk, and Emacs/Magit has nothing to revert. + + However if you do modify some files, visit the respective files in +Emacs, somehow discard the changes (not using Magit and probably even +outside Emacs), and then expect the respective file-visiting buffers to +retain the uncommitted changes, then the automatic reverting would +actually be harmful. In other words if you use file-visiting buffers as +a sort of "staging area", then you should set ‘magit-revert-buffers’ to +‘nil’. + + So far I have only heard from one user who uses such a workflow. But +because there might be some other users doing such things, and I don’t +want to be responsible for data loss, these reverts by default happen +quite verbosely, allowing these few users to undo the reverts using the +‘undo’ command and then disabling the automatic reverts for the future. +Most users should however keep automatic reverts turned on and instead +configure it to be less verbose by setting ‘magit-revert-buffers’ to ‘t’ +or even ‘silent’. + + +File: magit.info, Node: Performance, Prev: Safety, Up: Essential settings + +9.2.2 Performance +----------------- + +Magit is slower than raw Git because it does more. For example ‘git +commit’ creates a commit and that’s it. ‘magit-commit’ also updates the +current Magit buffer to make sure you are not looking at outdated +information without noticing it. To refresh the status buffer, Magit +has to run Git a dozen times or more, making it slower than ‘git +status’, but also much more informative. Magit also optionally reverts +file-visiting buffers, creates backups, runs hooks where third-party +extensions can do their slow thing, etc. + + I do care about performance and try to optimize for it as much as +possible, but many features simply come with an inherent performance +penalty. When a feature is just too slow given certain usage and +repository characteristics, then it often can be disabled, globally or +on a per-repository basis. + + But first a short list of performance issues that cannot easily be +worked around: + + • Creating a new process on MS Windows is much slower than on + POSIX-compatible systems. This is a problem because Magit creates + a lot of child processes to retrieve information from Git. In the + short run, only switching to a POSIX system can fix this. Of + course this could also be fixed in Windows itself, but I am just + one unpaid person while Microsoft is a billion dollar company, so I + will leave it to them to fix this grave flaw in their OS. + + • When showing logs, Magit limits the number of commits initially + shown in the hope that this avoids unnecessary work. When using + ‘--graph’ this unfortunately does not have the desired effect for + large histories. Junio 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. This has + to be fixed in Git but so far nobody volunteered to do it. Maybe + you could do that? + + • Whenever "something changes", Magit "refreshes" the status buffer + and the current Magit buffer by recreating them from scratch. This + is an old design decision that we couldn’t depart from easily. And + it has its benefits too - most importantly it’s much simpler and + less error prone to do it this way than to only refreshing "what + actually has changed" (that would basically be a huge collection of + special cases). So for now at least, we don’t avoid recreating the + buffer content and instead focus on making doing so faster. + + Now for the things that you can do to improve performance: + + Most optional features which can have a negative effect on +performance are disabled by default. So start by checking the options +you have customized. Even the potentially slow features are expected to +only lead to barely noticeable delays, but your mileage may vary. Also +note that it is now possible to set options on a per-repository or +per-repository-class basis. See *note Per-repository configuration: +Per-repository configuration. + + You should check the values of at least the following variables: + + • ‘magit-after-revert-hook’ + + • ‘magit-diff-auto-show’ + + • ‘magit-diff-highlight-hunk-body’ + + • ‘magit-diff-highlight-indentation’ + + • ‘magit-diff-highlight-whitespace’ + + • ‘magit-diff-paint-whitespace’ + + • ‘magit-diff-refine-hunk’ + + • ‘magit-not-reverted-hook’ + + • ‘magit-refresh-buffer-hook’ + + • ‘magit-status-refresh-hook’ + + • ‘magit-wip-after-apply-mode’ + + • ‘magit-wip-after-save-mode’ + + • ‘magit-wip-before-change-mode’ + + Also note that everything involving "cherry commits" is slow. + + If nothing helps, then feel free to open a new issue. Please provide +benchmarks. + + +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 am having problems committing:: +* I am using an Emacs release older than 24.4: I am using an Emacs release older than 244. +* I am using a Git release older than 1.9.4: I am using a Git release older than 194. +* 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:: +* Magit claims repository accessed using Tramp does not exist:: +* 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. + + +File: magit.info, Node: Magit is slow, Next: I am having problems committing, Up: FAQ + +A.1 Magit is slow +================= + +See *note Performance: Performance. + + +File: magit.info, Node: I am having problems committing, Next: I am using an Emacs release older than 244, Prev: Magit is slow, Up: FAQ + +A.2 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 am using an Emacs release older than 244, Next: I am using a Git release older than 194, Prev: I am having problems committing, Up: FAQ + +A.3 I am using an Emacs release older than 24.4 +=============================================== + +At least version 24.4 is required. There is no way around it. + + 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. + + +File: magit.info, Node: I am using a Git release older than 194, Next: I am using MS Windows and cannot push with Magit, Prev: I am using an Emacs release older than 244, Up: FAQ + +A.4 I am using a Git release older than 1.9.4 +============================================= + +At least version 1.9.4 is required. There is no way around it. + + 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. + + +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 194, Up: FAQ + +A.5 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.6 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.7 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: Magit claims repository accessed using Tramp does not exist, Prev: How can I show Git's output?, Up: FAQ + +A.8 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: Magit claims repository accessed using Tramp does not exist, 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.9 Magit claims repository accessed using Tramp does not exist +=============================================================== + +"There is no Git repository in /ssh:host:/path/to/repository". + + Magit requires at least version ‘1.9.4’. When connecting to a remote +machine using Tramp it requires that same version on that remote. + + There are others reasons why Magit/Git might think that there is no +repository where there actually is one, but when this is being reported +then it’s usually the above. + + +File: magit.info, Node: Can Magit be used as ediff-version-control-package?, Next: How to show diffs for gpg-encrypted files?, Prev: Magit claims repository accessed using Tramp does not exist, Up: FAQ + +A.10 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.11 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, Prev: How to show diffs for gpg-encrypted files?, Up: FAQ + +A.12 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: Keystroke Index, Next: Command Index, Prev: FAQ, Up: Top + +Appendix B Keystroke Index +************************** + +[index] +* Menu: + +* !: Running Git manually. + (line 12) +* ! !: Running Git manually. + (line 16) +* ! :: Running Git manually. + (line 23) +* ! g: Running Git manually. + (line 30) +* ! k: Running Git manually. + (line 34) +* $: Viewing Git output. (line 11) +* +: Log Buffer. (line 47) +* + <1>: Refreshing diffs. (line 41) +* -: Refreshing diffs. (line 45) +* 0: Refreshing diffs. (line 49) +* 1: Section visibility. (line 26) +* 2: Section visibility. (line 26) +* 3: Section visibility. (line 26) +* 4: Section visibility. (line 26) +* ^: 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 8) +* B b: Bisecting. (line 31) +* b b: Branching. (line 13) +* b B: Branching. (line 29) +* b c: Branching. (line 21) +* b d: Branching. (line 34) +* b e: Branching. (line 56) +* B g: Bisecting. (line 36) +* B k: Bisecting. (line 41) +* B r: Bisecting. (line 47) +* b r: Branching. (line 50) +* B s: Bisecting. (line 16) +* B u: Bisecting. (line 24) +* b u: Branching. (line 40) +* b U: Branching. (line 45) +* c: Initiating a commit. (line 8) +* c <1>: Editing rebase sequences. + (line 57) +* c a: Initiating a commit. (line 18) +* c c: Initiating a commit. (line 13) +* c e: Initiating a commit. (line 22) +* c f: Initiating a commit. (line 40) +* c F: Initiating a commit. (line 47) +* c r: Initiating a commit. (line 29) +* c s: Initiating a commit. (line 51) +* c S: Initiating a commit. (line 58) +* C-: Diff buffer. (line 21) +* C-: Section visibility. (line 13) +* C-c C-a: Editing commit messages. + (line 70) +* C-c C-b: Log Buffer. (line 6) +* C-c C-b <1>: Refreshing diffs. (line 67) +* 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 58) +* C-c C-d <1>: Editing commit messages. + (line 57) +* C-c C-f: Log Buffer. (line 10) +* C-c C-f <1>: Refreshing diffs. (line 71) +* C-c C-i: Editing commit messages. + (line 95) +* 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 86) +* C-c C-p: Editing commit messages. + (line 90) +* C-c C-r: Editing commit messages. + (line 74) +* C-c C-s: Editing commit messages. + (line 78) +* C-c C-t: Editing commit messages. + (line 82) +* C-s M-s: Editing commit messages. + (line 35) +* C-x g: Status buffer. (line 22) +* C-x u: Editing rebase sequences. + (line 74) +* d: Diffing. (line 8) +* D: Refreshing diffs. (line 16) +* d c: Diffing. (line 46) +* d d: Diffing. (line 13) +* D g: Refreshing diffs. (line 21) +* d p: Diffing. (line 42) +* d r: Diffing. (line 17) +* d s: Diffing. (line 32) +* D s: Refreshing diffs. (line 25) +* d t: Diffing. (line 51) +* D t: Refreshing diffs. (line 33) +* d u: Diffing. (line 38) +* d w: Diffing. (line 26) +* D w: Refreshing diffs. (line 29) +* DEL: Log Buffer. (line 24) +* DEL <1>: Diff buffer. (line 48) +* DEL <2>: Blaming. (line 45) +* e: Ediffing. (line 6) +* E: Ediffing. (line 18) +* e <1>: Editing rebase sequences. + (line 40) +* E d: Ediffing. (line 23) +* E m: Ediffing. (line 28) +* E s: Ediffing. (line 37) +* f: Editing rebase sequences. + (line 48) +* f <1>: Fetching. (line 8) +* F: Pulling. (line 8) +* f a: Fetching. (line 23) +* f f: Fetching. (line 13) +* F F: Pulling. (line 13) +* f m: Fetching. (line 27) +* f o: Fetching. (line 19) +* F o: Pulling. (line 17) +* g: Automatic refresh and revert. + (line 58) +* G: Automatic refresh and revert. + (line 67) +* j: Diff buffer. (line 38) +* k: Viewing Git output. (line 19) +* k <1>: Applying. (line 38) +* k <2>: Editing rebase sequences. + (line 53) +* k <3>: Stashing. (line 88) +* l: Logging. (line 8) +* L: Log Buffer. (line 73) +* l a: Logging. (line 38) +* l b: Logging. (line 34) +* l h: Logging. (line 26) +* l H: Reflog. (line 19) +* l l: Logging. (line 13) +* l L: Logging. (line 30) +* l o: Logging. (line 19) +* l O: Reflog. (line 15) +* l r: Reflog. (line 11) +* 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: Editing commit messages. + (line 45) +* M-n <1>: Editing rebase sequences. + (line 32) +* M-p: Editing commit messages. + (line 39) +* M-p <1>: Editing rebase sequences. + (line 28) +* M-x magit-blame: Blaming. (line 8) +* M-x magit-blame-popup: Blaming. (line 24) +* M-x magit-clone: Repository setup. (line 15) +* M-x magit-describe-section: Section types and values. + (line 13) +* M-x magit-describe-section <1>: Matching sections. (line 6) +* M-x magit-diff-unpulled: Diffing. (line 59) +* M-x magit-diff-unpushed: Diffing. (line 55) +* 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 49) +* M-x magit-rename-buffer: Modes and Buffers. (line 41) +* M-x magit-reset-hard: Resetting. (line 32) +* M-x magit-reset-head: Resetting. (line 21) +* M-x magit-reset-soft: Resetting. (line 26) +* M-x magit-stage-file: Staging from file-visiting buffers. + (line 10) +* M-x magit-unstage-all: Staging and unstaging. + (line 52) +* 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 109) +* n: Section movement. (line 16) +* N: Section movement. (line 26) +* n <1>: Blaming. (line 55) +* N <1>: Blaming. (line 59) +* n <2>: Editing rebase sequences. + (line 24) +* 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 s: Submodules. (line 33) +* o u: Submodules. (line 28) +* p: Section movement. (line 10) +* P: Section movement. (line 20) +* p <1>: Blaming. (line 63) +* P <1>: Blaming. (line 67) +* p <2>: Editing rebase sequences. + (line 20) +* P <2>: Pushing. (line 8) +* P e: Pushing. (line 23) +* P m: Pushing. (line 28) +* P o: Pushing. (line 18) +* P P: Pushing. (line 13) +* P t: Pushing. (line 34) +* P T: Pushing. (line 40) +* q: Modes and Buffers. (line 17) +* q <1>: Log Buffer. (line 34) +* q <2>: Blaming. (line 71) +* r: Rebasing. (line 8) +* r <1>: Editing rebase sequences. + (line 36) +* r a: Rebasing. (line 58) +* r e: Rebasing. (line 27) +* r e <1>: Rebasing. (line 54) +* r f: Rebasing. (line 31) +* r o: Rebasing. (line 21) +* r r: Rebasing. (line 16) +* r r <1>: Rebasing. (line 46) +* r s: Rebasing. (line 35) +* r s <1>: Rebasing. (line 50) +* r w: Rebasing. (line 39) +* RET: Diff buffer. (line 6) +* RET <1>: Blaming. (line 30) +* RET <2>: Editing rebase sequences. + (line 16) +* s: Staging and unstaging. + (line 26) +* S: Staging and unstaging. + (line 30) +* s <1>: Editing rebase sequences. + (line 44) +* s-: Section visibility. (line 22) +* SPC: Log Buffer. (line 14) +* SPC <1>: Diff buffer. (line 44) +* SPC <2>: Blaming. (line 35) +* t: Blaming. (line 76) +* 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 37) +* U: Staging and unstaging. + (line 41) +* U <1>: Resetting. (line 14) +* v: Applying. (line 42) +* 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 61) +* x <1>: Resetting. (line 8) +* Y: Logging. (line 44) +* y: References buffer. (line 6) +* y <1>: Editing rebase sequences. + (line 70) +* 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 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: + +* forward-line: Editing rebase sequences. + (line 24) +* git-commit-ack: Editing commit messages. + (line 70) +* git-commit-cc: Editing commit messages. + (line 86) +* 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 90) +* git-commit-review: Editing commit messages. + (line 74) +* git-commit-save-message: Editing commit messages. + (line 35) +* git-commit-signoff: Editing commit messages. + (line 78) +* git-commit-suggested: Editing commit messages. + (line 95) +* git-commit-test: Editing commit messages. + (line 82) +* git-rebase-backward-line: Editing rebase sequences. + (line 20) +* git-rebase-edit: Editing rebase sequences. + (line 40) +* git-rebase-exec: Editing rebase sequences. + (line 61) +* git-rebase-fixup: Editing rebase sequences. + (line 48) +* git-rebase-insert: Editing rebase sequences. + (line 70) +* git-rebase-kill-line: Editing rebase sequences. + (line 53) +* git-rebase-move-line-down: Editing rebase sequences. + (line 32) +* git-rebase-move-line-up: Editing rebase sequences. + (line 28) +* git-rebase-pick: Editing rebase sequences. + (line 57) +* git-rebase-reword: Editing rebase sequences. + (line 36) +* git-rebase-show-commit: Editing rebase sequences. + (line 16) +* git-rebase-squash: Editing rebase sequences. + (line 44) +* git-rebase-undo: Editing rebase sequences. + (line 74) +* 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-next-chunk: Blaming. (line 55) +* magit-blame-next-chunk-same-commit: Blaming. (line 59) +* magit-blame-popup: Blaming. (line 24) +* magit-blame-previous-chunk: Blaming. (line 63) +* magit-blame-previous-chunk-same-commit: Blaming. (line 67) +* magit-blame-quit: Blaming. (line 71) +* magit-blame-toggle-headings: Blaming. (line 76) +* magit-branch: Branching. (line 21) +* magit-branch-and-checkout: Branching. (line 29) +* magit-branch-delete: Branching. (line 34) +* magit-branch-edit-description: Branching. (line 56) +* magit-branch-popup: Branching. (line 8) +* magit-branch-rename: Branching. (line 50) +* magit-branch-set-upstream: Branching. (line 40) +* magit-branch-unset-upstream: Branching. (line 45) +* magit-checkout: Branching. (line 13) +* magit-cherry: Logging. (line 44) +* 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 15) +* magit-commit: Initiating a commit. (line 13) +* magit-commit-amend: Initiating a commit. (line 18) +* magit-commit-extend: Initiating a commit. (line 22) +* magit-commit-fixup: Initiating a commit. (line 40) +* magit-commit-instant-fixup: Initiating a commit. (line 47) +* magit-commit-instant-squash: Initiating a commit. (line 58) +* magit-commit-popup: Initiating a commit. (line 8) +* magit-commit-reword: Initiating a commit. (line 29) +* magit-commit-squash: Initiating a commit. (line 51) +* magit-describe-section: Section types and values. + (line 13) +* magit-describe-section <1>: Matching sections. (line 6) +* magit-diff: Diffing. (line 17) +* magit-diff-default-context: Refreshing diffs. (line 49) +* magit-diff-dwim: Diffing. (line 13) +* magit-diff-less-context: Refreshing diffs. (line 41) +* magit-diff-more-context: Refreshing diffs. (line 45) +* magit-diff-paths: Diffing. (line 42) +* magit-diff-popup: Diffing. (line 8) +* magit-diff-refresh: Refreshing diffs. (line 21) +* magit-diff-refresh-popup: Refreshing diffs. (line 16) +* magit-diff-save-default-arguments: Refreshing diffs. (line 29) +* magit-diff-set-default-arguments: Refreshing diffs. (line 25) +* magit-diff-show-or-scroll-down: Log Buffer. (line 24) +* magit-diff-show-or-scroll-down <1>: Blaming. (line 45) +* magit-diff-show-or-scroll-up: Log Buffer. (line 14) +* magit-diff-show-or-scroll-up <1>: Blaming. (line 35) +* magit-diff-staged: Diffing. (line 32) +* magit-diff-toggle-refine-hunk: Refreshing diffs. (line 33) +* magit-diff-unpulled: Diffing. (line 59) +* magit-diff-unpushed: Diffing. (line 55) +* magit-diff-unstaged: Diffing. (line 38) +* magit-diff-visit-file: Diff buffer. (line 6) +* magit-diff-visit-file-worktree: Diff buffer. (line 21) +* magit-diff-while-committing: Refreshing diffs. (line 58) +* magit-diff-while-committing <1>: Editing commit messages. + (line 57) +* magit-diff-worktree: Diffing. (line 26) +* magit-discard: Applying. (line 38) +* 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 28) +* magit-ediff-stage: Ediffing. (line 37) +* magit-fetch: Fetching. (line 19) +* magit-fetch-all: Fetching. (line 23) +* magit-fetch-current: Fetching. (line 13) +* magit-fetch-popup: Fetching. (line 8) +* 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 23) +* magit-git-command-topdir: Running Git manually. + (line 16) +* magit-go-backward: Log Buffer. (line 6) +* magit-go-backward <1>: Refreshing diffs. (line 67) +* magit-go-forward: Log Buffer. (line 10) +* magit-go-forward <1>: Refreshing diffs. (line 71) +* magit-init: Repository setup. (line 6) +* magit-jump-to-diffstat-or-diff: Diff buffer. (line 38) +* magit-log: Logging. (line 19) +* magit-log-all: Logging. (line 38) +* magit-log-all-branches: Logging. (line 34) +* magit-log-branches: Logging. (line 30) +* magit-log-buffer-file: Logging. (line 49) +* magit-log-bury-buffer: Log Buffer. (line 34) +* magit-log-current: Logging. (line 13) +* magit-log-head: Logging. (line 26) +* magit-log-popup: Logging. (line 8) +* magit-log-select-pick: Select from log. (line 14) +* magit-log-select-quit: Select from log. (line 20) +* magit-log-show-more-commits: 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: Modes and Buffers. (line 17) +* 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-process: Viewing Git output. (line 11) +* magit-process-kill: Viewing Git output. (line 19) +* magit-pull: Pulling. (line 17) +* magit-pull-current: Pulling. (line 13) +* magit-pull-popup: Pulling. (line 8) +* magit-push: Pushing. (line 18) +* magit-push-current: Pushing. (line 13) +* magit-push-elsewhere: Pushing. (line 23) +* magit-push-matching: Pushing. (line 28) +* magit-push-popup: Pushing. (line 8) +* magit-push-tag: Pushing. (line 40) +* magit-push-tags: Pushing. (line 34) +* magit-rebase: Rebasing. (line 16) +* magit-rebase-abort: Rebasing. (line 58) +* magit-rebase-autosquash: Rebasing. (line 31) +* magit-rebase-continue: Rebasing. (line 46) +* magit-rebase-edit: Rebasing. (line 54) +* magit-rebase-edit-commit: Rebasing. (line 35) +* magit-rebase-from: Rebasing. (line 21) +* magit-rebase-interactive: Rebasing. (line 27) +* magit-rebase-popup: Rebasing. (line 8) +* magit-rebase-reword-commit: Rebasing. (line 39) +* magit-rebase-skip: Rebasing. (line 50) +* magit-reflog-current: Reflog. (line 11) +* magit-reflog-head: Reflog. (line 19) +* magit-reflog-other: Reflog. (line 15) +* magit-refresh: Automatic refresh and revert. + (line 58) +* magit-refresh-all: Automatic refresh and revert. + (line 67) +* 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-rename-buffer: Modes and Buffers. (line 41) +* 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 41) +* magit-reset-index <1>: Resetting. (line 14) +* magit-reset-soft: Resetting. (line 26) +* magit-reverse: Applying. (line 42) +* 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 30) +* magit-run-gitk: Running Git manually. + (line 34) +* 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-show-commit: Diffing. (line 46) +* magit-show-commit <1>: Blaming. (line 30) +* 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 26) +* magit-stage-file: Staging from file-visiting buffers. + (line 10) +* magit-stage-modified: Staging and unstaging. + (line 30) +* magit-stash: Stashing. (line 13) +* magit-stash-apply: Stashing. (line 58) +* magit-stash-branch: Stashing. (line 84) +* magit-stash-clear: Stashing. (line 88) +* magit-stash-drop: Stashing. (line 71) +* 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 51) +* 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 27) +* magit-submodule-fetch <1>: Submodules. (line 37) +* magit-submodule-init: Submodules. (line 24) +* 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-margin: Log Buffer. (line 73) +* magit-unstage: Staging and unstaging. + (line 37) +* magit-unstage-all: Staging and unstaging. + (line 52) +* magit-unstage-file: Staging from file-visiting buffers. + (line 18) +* magit-version: Git executable. (line 17) +* magit-wip-commit: Wip Modes. (line 109) +* 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) + + +File: magit.info, Node: Function Index, Next: Variable Index, Prev: Command Index, Up: Top + +Appendix D Function Index +************************* + +[index] +* Menu: + +* git-commit-check-style-conventions: Editing commit messages. + (line 189) +* git-commit-propertize-diff: Editing commit messages. + (line 148) +* git-commit-save-message: Editing commit messages. + (line 129) +* git-commit-setup-changelog-support: Editing commit messages. + (line 133) +* git-commit-turn-on-auto-fill: Editing commit messages. + (line 138) +* git-commit-turn-on-flyspell: Editing commit messages. + (line 143) +* ido-enter-magit-status: Status buffer. (line 42) +* magit-add-section-hook: Section hooks. (line 18) +* magit-builtin-completing-read: Completion and confirmation. + (line 23) +* magit-call-git: Calling Git for effect. + (line 28) +* magit-call-process: Calling Git for effect. + (line 32) +* magit-cancel-section: Creating sections. (line 70) +* magit-current-section: Section selection. (line 6) +* magit-define-section-jumper: Creating sections. (line 76) +* magit-diff-scope: Matching sections. (line 105) +* magit-diff-type: Matching sections. (line 82) +* magit-get-section: Matching sections. (line 16) +* magit-git: Calling Git for effect. + (line 65) +* magit-git-exit-code: Getting a value from Git. + (line 10) +* magit-git-failure: Getting a value from Git. + (line 19) +* magit-git-false: Getting a value from Git. + (line 29) +* magit-git-insert: Getting a value from Git. + (line 34) +* magit-git-items: Getting a value from Git. + (line 49) +* magit-git-lines: Getting a value from Git. + (line 44) +* magit-git-str: Getting a value from Git. + (line 72) +* magit-git-string: Getting a value from Git. + (line 38) +* magit-git-success: Getting a value from Git. + (line 14) +* magit-git-true: Getting a value from Git. + (line 24) +* magit-git-wash: Calling Git for effect. + (line 70) +* magit-hunk-set-window-start: Section movement. (line 44) +* magit-ido-completing-read: Completion and confirmation. + (line 28) +* magit-insert-am-sequence: Status sections. (line 28) +* magit-insert-bisect-log: Status sections. (line 46) +* magit-insert-bisect-output: Status sections. (line 38) +* magit-insert-bisect-rest: Status sections. (line 42) +* magit-insert-head-header: Status header sections. + (line 27) +* magit-insert-heading: Creating sections. (line 41) +* magit-insert-local-branches: References sections. (line 17) +* magit-insert-merge-log: Status sections. (line 18) +* magit-insert-rebase-sequence: Status sections. (line 23) +* magit-insert-recent-commits: Status sections. (line 91) +* magit-insert-remote-branches: References sections. (line 21) +* magit-insert-remote-header: Status header sections. + (line 45) +* magit-insert-repo-header: Status header sections. + (line 41) +* magit-insert-section: Creating sections. (line 6) +* magit-insert-sequencer-sequence: Status sections. (line 33) +* magit-insert-staged-changes: Status sections. (line 59) +* magit-insert-stashes: Status sections. (line 63) +* magit-insert-status-headers: Status header sections. + (line 11) +* magit-insert-tags: References sections. (line 25) +* magit-insert-tags-header: Status header sections. + (line 35) +* magit-insert-tracked-files: Status sections. (line 80) +* magit-insert-unpulled-cherries: Status sections. (line 102) +* magit-insert-unpulled-commits: Status sections. (line 70) +* magit-insert-unpulled-module-commits: Status sections. (line 109) +* magit-insert-unpulled-or-recent-commits: Status sections. (line 84) +* magit-insert-unpushed-cherries: Status sections. (line 114) +* magit-insert-unpushed-commits: Status sections. (line 74) +* magit-insert-unpushed-module-commits: Status sections. (line 122) +* magit-insert-unstaged-changes: Status sections. (line 55) +* magit-insert-untracked-files: Status sections. (line 50) +* magit-insert-upstream-header: Status header sections. + (line 31) +* magit-log-maybe-show-commit: Section movement. (line 57) +* magit-log-maybe-show-more-commits: Section movement. (line 64) +* magit-mode-display-buffer: Refreshing buffers. (line 32) +* magit-mode-setup: Refreshing buffers. (line 17) +* magit-region-sections: Section selection. (line 10) +* magit-region-values: Section selection. (line 29) +* magit-revert-buffers: Editing commit messages. + (line 120) +* magit-run-git: Calling Git for effect. + (line 36) +* magit-run-git-async: Calling Git for effect. + (line 80) +* magit-run-git-with-editor: Calling Git for effect. + (line 93) +* magit-run-git-with-input: Calling Git for effect. + (line 40) +* magit-run-git-with-logfile: Calling Git for effect. + (line 56) +* magit-section-case: Matching sections. (line 58) +* magit-section-hide: Section visibility. (line 51) +* magit-section-hide-children: Section visibility. (line 66) +* magit-section-ident: Matching sections. (line 11) +* magit-section-match: Matching sections. (line 21) +* magit-section-set-window-start: Section movement. (line 50) +* 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-children: Section visibility. (line 70) +* magit-section-when: Matching sections. (line 48) +* magit-start-git: Calling Git for effect. + (line 105) +* magit-start-process: Calling Git for effect. + (line 124) +* with-editor-usage-message: Editing commit messages. + (line 157) + + +File: magit.info, Node: Variable Index, Prev: Function Index, Up: Top + +Appendix E Variable Index +************************* + +[index] +* Menu: + +* git-commit-fill-column: Editing commit messages. + (line 170) +* git-commit-finish-query-functions: Editing commit messages. + (line 175) +* git-commit-known-pseudo-headers: Editing commit messages. + (line 66) +* git-commit-major-mode: Editing commit messages. + (line 105) +* git-commit-setup-hook: Editing commit messages. + (line 116) +* git-commit-summary-max-length: Editing commit messages. + (line 164) +* git-rebase-auto-advance: Editing rebase sequences. + (line 79) +* git-rebase-confirm-cancel: Editing rebase sequences. + (line 87) +* git-rebase-show-instructions: Editing rebase sequences. + (line 83) +* magit-after-revert-hook: Automatic refresh and revert. + (line 82) +* magit-blame-goto-chunk-hook: Blaming. (line 93) +* magit-blame-heading-format: Blaming. (line 80) +* magit-blame-show-headings: Blaming. (line 88) +* magit-blame-time-format: Blaming. (line 84) +* magit-commit-ask-to-stage: Initiating a commit. (line 62) +* magit-commit-extend-override-date: Initiating a commit. (line 67) +* magit-commit-reword-override-date: Initiating a commit. (line 71) +* magit-commit-squash-confirm: Initiating a commit. (line 75) +* magit-completing-read-function: Completion and confirmation. + (line 10) +* magit-diff-auto-show: Diff options. (line 14) +* magit-diff-highlight-indentation: Diff options. (line 60) +* magit-diff-highlight-trailing: Diff options. (line 55) +* magit-diff-paint-whitespace: Diff options. (line 47) +* magit-diff-refine-hunk: Diff options. (line 37) +* magit-diff-show-diffstat: Diff options. (line 6) +* magit-diff-show-xref-buttons: Diff options. (line 10) +* magit-git-debug: Viewing Git output. (line 23) +* magit-git-debug <1>: Getting a value from Git. + (line 64) +* magit-git-executable: Git executable. (line 36) +* magit-git-global-arguments: Global Git arguments. + (line 6) +* magit-log-auto-more: Log Buffer. (line 41) +* magit-log-cutoff-length: Log Buffer. (line 77) +* magit-log-section-args: Status options. (line 23) +* magit-log-section-commit-count: Status sections. (line 96) +* magit-log-show-margin: Log Buffer. (line 61) +* magit-no-confirm: Completion and confirmation. + (line 42) +* magit-not-reverted-hook: Automatic refresh and revert. + (line 92) +* magit-process-raise-error: Calling Git for effect. + (line 151) +* magit-refresh-args: Refreshing buffers. (line 54) +* magit-refresh-buffer-hook: Automatic refresh and revert. + (line 76) +* magit-refresh-function: Refreshing buffers. (line 48) +* magit-refs-indent-cherry-lines: References buffer. (line 88) +* magit-refs-local-branch-format: References buffer. (line 76) +* magit-refs-remote-branch-format: References buffer. (line 80) +* magit-refs-sections-hook: References sections. (line 13) +* magit-refs-show-commit-count: References buffer. (line 29) +* magit-refs-show-margin: References buffer. (line 40) +* magit-refs-tags-format: References buffer. (line 84) +* magit-repository-directories: Status buffer. (line 30) +* magit-repository-directories-depth: Status buffer. (line 36) +* magit-restore-window-configuration: Modes and Buffers. (line 27) +* magit-revert-buffers: Automatic refresh and revert. + (line 13) +* magit-revision-insert-related-refs: Revision buffer. (line 18) +* magit-revision-show-diffstat: Revision buffer. (line 6) +* magit-revision-show-notes: Revision buffer. (line 10) +* magit-revision-show-xref-buttons: Revision buffer. (line 14) +* magit-root-section: Matching sections. (line 74) +* magit-save-repository-buffers: Automatic save. (line 13) +* magit-section-movement-hook: Section movement. (line 39) +* magit-section-set-visibility-hook: Section visibility. (line 80) +* magit-section-show-child-count: Section options. (line 9) +* magit-status-buffer-name-format: Status options. (line 15) +* magit-status-buffer-switch-function: Status options. (line 10) +* magit-status-headers-hook: Status header sections. + (line 17) +* magit-status-refresh-hook: Status options. (line 6) +* magit-status-sections-hook: Status sections. (line 10) +* magit-this-process: Calling Git for effect. + (line 146) +* magit-wip-after-apply-mode: Wip Modes. (line 79) +* magit-wip-after-apply-mode-lighter: Wip Modes. (line 121) +* magit-wip-after-save-local-mode-lighter: Wip Modes. (line 117) +* magit-wip-after-save-mode: Wip Modes. (line 73) +* magit-wip-before-change-mode: Wip Modes. (line 93) +* magit-wip-before-change-mode-lighter: Wip Modes. (line 125) +* magit-wip-namespace: Wip Modes. (line 129) + + + +Tag Table: +Node: Top1217 +Node: Introduction5279 +Node: Installation9976 +Node: Updating from an older release10351 +Node: Installing from an Elpa archive11935 +Node: Installing from the Git repository13514 +Node: Post-installation tasks15758 +Node: Getting started17193 +Node: Interface concepts22795 +Node: Modes and Buffers23121 +Node: Automatic save25221 +Node: Automatic refresh and revert26286 +Node: Sections30059 +Node: Section movement31008 +Node: Section visibility33559 +Node: Section hooks37150 +Node: Section types and values39188 +Node: Section options40458 +Node: Popup buffers and prefix commands40930 +Node: Completion and confirmation42244 +Node: Running Git45150 +Node: Viewing Git output45386 +Node: Running Git manually46386 +Node: Git executable47681 +Node: Global Git arguments49688 +Node: Inspecting50495 +Node: Status buffer51622 +Node: Status sections54073 +Node: Status header sections58416 +Node: Status options59980 +Node: Logging61193 +Node: Log Buffer62745 +Node: Select from log65693 +Node: Reflog66633 +Node: Diffing67111 +Node: Refreshing diffs68985 +Node: Diff buffer71516 +Node: Diff options73418 +Node: Revision buffer75904 +Node: Ediffing76446 +Node: References buffer77837 +Node: References sections80984 +Node: Bisecting81859 +Node: Visiting blobs83356 +Node: Blaming83865 +Node: Manipulating87137 +Node: Repository setup87429 +Node: Staging and unstaging88068 +Node: Staging from file-visiting buffers90512 +Node: Applying91680 +Node: Committing93259 +Node: Initiating a commit93842 +Node: Editing commit messages96677 +Node: Branching104184 +Node: Merging106538 +Node: Rebasing108623 +Node: Editing rebase sequences110326 +Node: Cherry picking112735 +Node: Reverting114342 +Node: Resetting115706 +Node: Stashing116990 +Node: Transferring120055 +Node: Remotes120293 +Node: Fetching121097 +Node: Pulling121914 +Node: Pushing122415 +Node: Creating and sending patches123734 +Node: Applying patches124430 +Node: Miscellaneous125429 +Node: Tagging125616 +Node: Notes126402 +Node: Submodules128928 +Node: Wip Modes130159 +Node: Customizing136079 +Node: Per-repository configuration137751 +Node: Essential settings139399 +Node: Safety139723 +Node: Performance142904 +Node: Plumbing147059 +Node: Calling Git147687 +Node: Getting a value from Git149210 +Node: Calling Git for effect152314 +Node: Section plumbing158818 +Node: Creating sections159046 +Node: Section selection162849 +Node: Matching sections164529 +Node: Refreshing buffers169731 +Node: Conventions172866 +Node: Confirmation and completion173043 +Node: Theming Faces173941 +Node: FAQ182092 +Node: Magit is slow183101 +Node: I am having problems committing183267 +Node: I am using an Emacs release older than 244183665 +Node: I am using a Git release older than 194185111 +Node: I am using MS Windows and cannot push with Magit185950 +Node: How to install the gitman info manual?186528 +Node: How can I show Git's output?189110 +Node: Expanding a file to show the diff causes it to disappear189920 +Node: Magit claims repository accessed using Tramp does not exist190460 +Node: Can Magit be used as ediff-version-control-package?191195 +Node: How to show diffs for gpg-encrypted files?193235 +Node: Emacs 245 hangs when loading Magit193826 +Node: Keystroke Index194345 +Node: Command Index217364 +Node: Function Index240901 +Node: Variable Index250525 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/magit-annex-readme.txt b/elpa/magit-annex-readme.txt new file mode 100644 index 00000000..7b0dcb16 --- /dev/null +++ b/elpa/magit-annex-readme.txt @@ -0,0 +1,34 @@ +magit-annex adds a few git annex operations to the magit interface. +Most features are present under the annex popup menu, which is bound +to "@". This key was chosen as a leading key mostly to be consistent +with John Wiegley's git-annex.el [1], but also because there aren't +too many single letters available in the magit keymap. + +Adding files: + @a Add a file in the status buffer to annex. + Behaves similarly to staging a file with 's'. + @A Add all untracked files and unstaged changes to annex. + Behaves similarly to staging all files with 'S'. + +Managing file content: + @fu Unlock a file. + @fl Lock a file. + @fg Get a file. + @fd Drop a file. + @fc Copy a file. + @fm Move a file. + + @eg Get all files. + @ed Drop all files. + @ec Copy all files. + @em Move all files. + +Updating git annex: + @m Run `git annex merge'. + @Pg Push git annex branch. + @Pb Push current and git annex branch. + @y Run `git annex sync'. + +For working with git annex in dired, see git-annex.el [1]. + +[1] https://github.com/jwiegley/git-annex-el diff --git a/elpa/magit-find-file-readme.txt b/elpa/magit-find-file-readme.txt new file mode 100644 index 00000000..f939f4c4 --- /dev/null +++ b/elpa/magit-find-file-readme.txt @@ -0,0 +1,16 @@ +This is a Git-powered replacement for something like TextMate's +Cmd-t. + +Uses a configurable completing-read to open any file in the +Git repository of the current buffer. + +Usage: + + (require 'magit-find-file) ;; if not using the ELPA package + (global-set-key (kbd "C-c p") 'magit-find-file-completing-read) + +Customize `magit-completing-read-function' to change the completing +read engine used (so it should behave like Magit does for you). + +Customize `magit-find-file-ignore-extensions' to exclude certain +files from completion. By default all files can be selected. diff --git a/elpa/magit-gerrit-readme.txt b/elpa/magit-gerrit-readme.txt new file mode 100644 index 00000000..ff98501b --- /dev/null +++ b/elpa/magit-gerrit-readme.txt @@ -0,0 +1,7 @@ +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). diff --git a/elpa/magit-popup-20150701.1246/dir b/elpa/magit-popup-20150701.1246/dir new file mode 100644 index 00000000..c276dafb --- /dev/null +++ b/elpa/magit-popup-20150701.1246/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-20150701.1246/magit-popup-autoloads.el b/elpa/magit-popup-20150701.1246/magit-popup-autoloads.el new file mode 100644 index 00000000..439beb25 --- /dev/null +++ b/elpa/magit-popup-20150701.1246/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") +;;;;;; (21910 48885 479571 0)) + +;;;*** + +;; 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-20150701.1246/magit-popup-pkg.el b/elpa/magit-popup-20150701.1246/magit-popup-pkg.el new file mode 100644 index 00000000..7c2af7ff --- /dev/null +++ b/elpa/magit-popup-20150701.1246/magit-popup-pkg.el @@ -0,0 +1,8 @@ +(define-package "magit-popup" "20150701.1246" "Define prefix-infix-suffix command combos" + '((emacs "24.4") + (dash "2.10.0")) + :url "https://github.com/magit/magit" :keywords + '("bindings")) +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/magit-popup-20150701.1246/magit-popup.el b/elpa/magit-popup-20150701.1246/magit-popup.el new file mode 100644 index 00000000..7b523ed1 --- /dev/null +++ b/elpa/magit-popup-20150701.1246/magit-popup.el @@ -0,0 +1,1055 @@ +;;; magit-popup.el --- Define prefix-infix-suffix command combos + +;; Copyright (C) 2010-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. + +;; 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") (dash "2.10.0")) +;; 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) + +(declare-function info 'info) +(declare-function Man-find-section 'man) +(declare-function Man-next-section 'man) + +;;; 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-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 "Use default action, else show popup" default) + (const :tag "Show popup, else use 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-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 :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 :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 (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)) + `(--map (if (or (null it) (stringp 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-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 ommitted then the option is placed in some +group the same way it is done when directly using `defcustom'. + +Optional argument MODE specifies the mode used by the popup +buffer. If it is ommitted 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. + + 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 may, 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'. + +`: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 defails. + +`: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\". + +`:man-page' + The name of the manpage to be displayed when the user requests + help for a switch or argument. + +When MODE is `magit-popup-sequence-mode', then the following +keywords are also meaningful: + +`:sequence-predicate' + When this function returns non-nil, then the popup uses + `:sequence-actions' instead of `:actions'. + +`:sequence-actions' + The actions which can be invoked from the popup, when + `:sequence-predicate' returns non-nil. + +\(fn NAME DOC [GROUP [MODE [OPTION]]] :KEYWORD VALUE...)" + ;; TODO document keywords + (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)) + ,@(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-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-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-switch', or + `magit-define-popup-option'." + (declare (indent defun)) + (if (memq type '(:switches :options :actions)) + (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 :test 'equal)) + (setq value (cl-delete key value :key 'car :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 :test 'equal))) + (unless (assoc key value) + (setq value (if prepend + (cons elt value) + (append value (list elt))))) + (set popup (plist-put plist type value))) + (error "Unknown popup event type: %s" type))) + +(defun magit-change-popup-key (popup type from to) + "In POPUP, bind TO to what FROM was bound to. +TYPE is one of `:action', `:switch', or `:option'. +Bind TO and unbind FROM, both are characters." + (--if-let (assoc from (plist-get (symbol-value popup) 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', `:switch', or `:option'. +KEY is the character which is to be unbound." + (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)) + (--if-let (magit-popup-lookup event :actions) + (let ((magit-current-popup magit-this-popup) + (magit-current-popup-args (magit-popup-get-args)) + (command (magit-popup-event-fun it))) + (magit-popup-quit) + (call-interactively command) + (setq this-command command)) + (if (eq event ?q) + (magit-popup-quit) + (user-error "%c isn't bound to any action" event)))) + +(defun magit-popup-quit () + "Quit the current popup command without invoking an action." + (interactive) + (let ((buf (current-buffer)) + (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)) + (`self-insert-command + (setq def (magit-popup-lookup int :actions)) + (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." + (setq buffer-read-only t) + (setq-local scroll-margin 0) + (setq-local magit-popup-show-common-commands magit-popup-show-common-commands) + (add-hook 'magit-popup-setup-hook 'magit-popup-default-setup nil t) + (hack-dir-local-variables-non-file-buffer)) + +(put 'magit-popup-mode 'mode-class 'special) + +(defvar magit-popup-setup-hook nil "For internal use.") + +(defun magit-popup-default-setup (val def) + (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)))) + +(define-derived-mode magit-popup-sequence-mode magit-popup-mode "MagitPopup" + "Major mode for infix argument popups, which are affected by state. +Used for popups that display different actions depending on some +external state. Within Magit this is used for sequence commands +such as rebase. The function `:sequence-predicate', which takes +no arguments, is used to determine whether to use the actions +defined with regular `:actions' or those in `:sequence-actions'. +When a sequence is in progress the arguments are not available +in the popup." + (remove-hook 'magit-popup-setup-hook 'magit-popup-default-setup t) + (add-hook 'magit-popup-setup-hook + (lambda (val def) + (if (funcall (magit-popup-get :sequence-predicate)) + (magit-popup-put + :actions (magit-popup-convert-actions + val (magit-popup-get :sequence-actions))) + (magit-popup-default-setup val def))) + t t) + (hack-dir-local-variables-non-file-buffer)) + +(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) + (run-hook-with-args 'magit-popup-setup-hook val def)) + (magit-refresh-popup-buffer) + (fit-window-to-buffer)) + +(defun magit-popup-mode-display-buffer (buffer mode) + (let ((winconf (current-window-configuration))) + (split-window-vertically) + (other-window 1) + (switch-to-buffer buffer) + (funcall mode) + (setq magit-popup-previous-winconf winconf))) + +(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 + (magit-popup-insert-section 'magit-popup-switch-button) + (magit-popup-insert-section 'magit-popup-option-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) + (if event + (while (and (forward-button 1) + (let ((b (button-at (point)))) + (or (not (equal (button-get b 'prefix) prefix)) + (not (equal (button-get b 'event) event)))))) + (re-search-forward "^Actions" nil t) + (forward-button 1)))) + +;;; 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 (stringp (car spec)) + (--each (-partition-by-header 'stringp spec) + (magit-popup-insert-section type (cdr it) (car it))) + (magit-popup-insert-section type spec)))) + (unless heading + (setq heading (button-type-get type 'heading))) + (let* ((formatter (button-type-get type 'formatter)) + (items (mapcar (lambda (ev) + (and ev (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 + (insert (propertize heading 'face 'magit-popup-heading)) + (unless (string-match "\n$" heading) + (insert "\n")) + (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")))) + (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-action-button (type ev) + (list (format-spec + (button-type-get type 'format) + `((?k . ,(propertize (magit-popup-event-keydsc ev) + 'face 'magit-popup-key)) + (?d . ,(magit-popup-event-dsc ev)))) + '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))))) + +;;; 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-20150701.1246/magit-popup.info b/elpa/magit-popup-20150701.1246/magit-popup.info new file mode 100644 index 00000000..0256d377 --- /dev/null +++ b/elpa/magit-popup-20150701.1246/magit-popup.info @@ -0,0 +1,639 @@ +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 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 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. + + -- 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-change-popup-key popup type from to + + In POPUP, bind TO to what FROM was bound to. TYPE is one of + ‘: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’, + ‘:switch’, or ‘:option’. KEY is the character which is to be + unbound. + + +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. + + -- User Option: magit-popup-manpage-package + + The Emacs package used to display man-pages, one of ‘man’ or + ‘woman’. + + +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. + + • ‘: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\". + + • ‘:man-page’ + + The name of the manpage to be displayed when the user requests + help for an argument. + When MODE is ‘magit-popup-sequence-mode’, then the following + keywords are also meaningful: + + • ‘:sequence-predicate’ + + When this function returns non-nil, then the popup uses + ‘:sequence-actions’ instead of ‘:actions’. + + • ‘:sequence-actions’ + + The actions which can be invoked from the popup, when + ‘:sequence-predicate’ returns non-nil. + + +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: Top989 +Node: Introduction2158 +Node: Usage4735 +Node: Customizing existing popups9392 +Node: Other options13208 +Node: Defining prefix and suffix commands14481 +Node: Defining prefix commands16633 +Node: Defining suffix commands22640 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/magit-push-remote-readme.txt b/elpa/magit-push-remote-readme.txt new file mode 100644 index 00000000..cf05c33e --- /dev/null +++ b/elpa/magit-push-remote-readme.txt @@ -0,0 +1,56 @@ +This plug-in provides support for an additional default remote +which when pushing is used instead of the "merge" default specified +by the git-config(1) option `branch..remote'. + +Together `branch..remote' and `branch..merge' set the +default used by git-pull(1) and git-push(1). Like their git +counterparts `magit-push' and `magit-pull' use these options. So +does `magit-status' which displays commits not pushed to or not +pulled from the default remote/branch. + +This works nicely if commits most often flow like this: + + +------------+ +------------+ + | remote | -- pull -> | local | + | repo | <- push -- | repo | + +------------+ +------------+ + +But it is inconventient if commits most often flow through your +local repository like this: + + +------------+ +------------+ +------------+ + | upstream | -- pull -> | local | | your | + | repo | | repo | -- push -> | public | + | | +------------+ | repo | + | | <- merge pull reguest -------------- | | + +------------+ +------------+ + +This package modifies Magit to automatically detect whether the +latter workflow is used; and if so provide additional information +related to that "personal" or "push" remote and push to it by +default. + +When `magit-push-remote-mode' is turned on and the repository has a +push-remote `magit-push' and `magit-push-tags' now by default push +to the push-remote, and `magit-status' shows information related to +both the push and pull (Git's default) remote. + +This is done by REDEFINING `magit-push-tags' and adding specialized +functions to the hooks `magit-status-sections-hook' and +`magit-push-hook'. + +To enable this turn on the global `magit-push-remote-mode' and +select the push-remote either per repository or globally using the +git variable `magit.pushremote'. + + (magit-push-remote-mode 1) + + git config --global magit.pushremote # or + git config magit.pushremote + +Git already provides a variable for the same purpose, which is +also supported, but note that setting that also Git, but at least +currently does not affect regular Magit push commands + + git config --global remote.pushdefault # or + git config remote.pushdefault diff --git a/elpa/magit-readme.txt b/elpa/magit-readme.txt new file mode 100644 index 00000000..52bcc521 --- /dev/null +++ b/elpa/magit-readme.txt @@ -0,0 +1,8 @@ +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.