config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

magit-stash.el (26484B)


      1 ;;; magit-stash.el --- Stash support for Magit  -*- lexical-binding:t -*-
      2 
      3 ;; Copyright (C) 2008-2024 The Magit Project Contributors
      4 
      5 ;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
      6 ;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
      7 
      8 ;; SPDX-License-Identifier: GPL-3.0-or-later
      9 
     10 ;; Magit is free software: you can redistribute it and/or modify it
     11 ;; under the terms of the GNU General Public License as published by
     12 ;; the Free Software Foundation, either version 3 of the License, or
     13 ;; (at your option) any later version.
     14 ;;
     15 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
     16 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     17 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     18 ;; License for more details.
     19 ;;
     20 ;; You should have received a copy of the GNU General Public License
     21 ;; along with Magit.  If not, see <https://www.gnu.org/licenses/>.
     22 
     23 ;;; Commentary:
     24 
     25 ;; Support for Git stashes.
     26 
     27 ;;; Code:
     28 
     29 (require 'magit)
     30 (require 'magit-reflog)
     31 (require 'magit-sequence)
     32 
     33 ;;; Options
     34 
     35 (defgroup magit-stash nil
     36   "List stashes and show stash diffs."
     37   :group 'magit-modes)
     38 
     39 ;;;; Diff options
     40 
     41 (defcustom magit-stash-sections-hook
     42   '(magit-insert-stash-notes
     43     magit-insert-stash-worktree
     44     magit-insert-stash-index
     45     magit-insert-stash-untracked)
     46   "Hook run to insert sections into stash diff buffers."
     47   :package-version '(magit . "2.1.0")
     48   :group 'magit-stash
     49   :type 'hook)
     50 
     51 ;;;; Log options
     52 
     53 (defcustom magit-stashes-margin
     54   (list (nth 0 magit-log-margin)
     55         (nth 1 magit-log-margin)
     56         'magit-log-margin-width nil
     57         (nth 4 magit-log-margin))
     58   "Format of the margin in `magit-stashes-mode' buffers.
     59 
     60 The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH).
     61 
     62 If INIT is non-nil, then the margin is shown initially.
     63 STYLE controls how to format the author or committer date.
     64   It can be one of `age' (to show the age of the commit),
     65   `age-abbreviated' (to abbreviate the time unit to a character),
     66   or a string (suitable for `format-time-string') to show the
     67   actual date.  Option `magit-log-margin-show-committer-date'
     68   controls which date is being displayed.
     69 WIDTH controls the width of the margin.  This exists for forward
     70   compatibility and currently the value should not be changed.
     71 AUTHOR controls whether the name of the author is also shown by
     72   default.
     73 AUTHOR-WIDTH has to be an integer.  When the name of the author
     74   is shown, then this specifies how much space is used to do so."
     75   :package-version '(magit . "2.9.0")
     76   :group 'magit-stash
     77   :group 'magit-margin
     78   :type magit-log-margin--custom-type
     79   :initialize #'magit-custom-initialize-reset
     80   :set-after '(magit-log-margin)
     81   :set (apply-partially #'magit-margin-set-variable 'magit-stashes-mode))
     82 
     83 ;;;; Variables
     84 
     85 (defvar magit-stash-read-message-function #'magit-stash-read-message
     86   "Function used to read the message when creating a stash.")
     87 
     88 ;;; Commands
     89 
     90 ;;;###autoload (autoload 'magit-stash "magit-stash" nil t)
     91 (transient-define-prefix magit-stash ()
     92   "Stash uncommitted changes."
     93   :man-page "git-stash"
     94   ["Arguments"
     95    ("-u" "Also save untracked files" ("-u" "--include-untracked"))
     96    ("-a" "Also save untracked and ignored files" ("-a" "--all"))]
     97   [["Stash"
     98     ("z" "both"          magit-stash-both)
     99     ("i" "index"         magit-stash-index)
    100     ("w" "worktree"      magit-stash-worktree)
    101     ("x" "keeping index" magit-stash-keep-index)
    102     ("P" "push"          magit-stash-push :level 5)]
    103    ["Snapshot"
    104     ("Z" "both"          magit-snapshot-both)
    105     ("I" "index"         magit-snapshot-index)
    106     ("W" "worktree"      magit-snapshot-worktree)
    107     ("r" "to wip ref"    magit-wip-commit)]
    108    ["Use"
    109     ("a" "Apply"         magit-stash-apply)
    110     ("p" "Pop"           magit-stash-pop)
    111     ("k" "Drop"          magit-stash-drop)]
    112    ["Inspect"
    113     ("l" "List"          magit-stash-list)
    114     ("v" "Show"          magit-stash-show)]
    115    ["Transform"
    116     ("b" "Branch"        magit-stash-branch)
    117     ("B" "Branch here"   magit-stash-branch-here)
    118     ("f" "Format patch"  magit-stash-format-patch)]])
    119 
    120 (defun magit-stash-arguments ()
    121   (transient-args 'magit-stash))
    122 
    123 ;;;###autoload
    124 (defun magit-stash-both (message &optional include-untracked)
    125   "Create a stash of the index and working tree.
    126 Untracked files are included according to infix arguments.
    127 One prefix argument is equivalent to `--include-untracked'
    128 while two prefix arguments are equivalent to `--all'."
    129   (interactive
    130    (progn (when (and (magit-merge-in-progress-p)
    131                      (not (magit-y-or-n-p "\
    132 Stashing and resetting during a merge conflict. \
    133 Applying the resulting stash won't restore the merge state. \
    134 Proceed anyway? ")))
    135             (user-error "Abort"))
    136           (magit-stash-read-args)))
    137   (magit-stash-save message t t include-untracked t))
    138 
    139 ;;;###autoload
    140 (defun magit-stash-index (message)
    141   "Create a stash of the index only.
    142 Unstaged and untracked changes are not stashed.  The stashed
    143 changes are applied in reverse to both the index and the
    144 worktree.  This command can fail when the worktree is not clean.
    145 Applying the resulting stash has the inverse effect."
    146   (interactive (list (magit-stash-read-message)))
    147   (magit-stash-save message t nil nil t 'worktree))
    148 
    149 ;;;###autoload
    150 (defun magit-stash-worktree (message &optional include-untracked)
    151   "Create a stash of unstaged changes in the working tree.
    152 Untracked files are included according to infix arguments.
    153 One prefix argument is equivalent to `--include-untracked'
    154 while two prefix arguments are equivalent to `--all'."
    155   (interactive (magit-stash-read-args))
    156   (magit-stash-save message nil t include-untracked t 'index))
    157 
    158 ;;;###autoload
    159 (defun magit-stash-keep-index (message &optional include-untracked)
    160   "Create a stash of the index and working tree, keeping index intact.
    161 Untracked files are included according to infix arguments.
    162 One prefix argument is equivalent to `--include-untracked'
    163 while two prefix arguments are equivalent to `--all'."
    164   (interactive (magit-stash-read-args))
    165   (magit-stash-save message t t include-untracked t 'index))
    166 
    167 (defun magit-stash-read-args ()
    168   (list (funcall magit-stash-read-message-function)
    169         (magit-stash-read-untracked)))
    170 
    171 (defun magit-stash-read-message ()
    172   "Read a message from the minibuffer, to be used for a stash.
    173 
    174 The message that Git would have picked, is available as the
    175 default (used when the user enters the empty string) and as
    176 the next history element (which can be accessed with \
    177 \\<minibuffer-local-map>\\[next-history-element])."
    178   (read-string (format "Stash message (default: On%s:%s): "
    179                        (magit--ellipsis) (magit--ellipsis))
    180                nil nil
    181                (format "On %s: %s"
    182                        (or (magit-get-current-branch) "(no branch)")
    183                        (magit-rev-format "%h %s"))))
    184 
    185 (defun magit-stash-read-message-traditional ()
    186   "Read a message from the minibuffer, to be used for a stash.
    187 
    188 If the user confirms the initial-input unmodified, then the
    189 abbreviated commit hash and commit summary are appended.
    190 The resulting message is what Git would have used."
    191   (let* ((default (format "On %s: "
    192                           (or (magit-get-current-branch) "(no branch)")))
    193          (input (magit-read-string "Stash message" default)))
    194     (if (equal input default)
    195         (concat default (magit-rev-format "%h %s"))
    196       input)))
    197 
    198 (defun magit-stash-read-untracked ()
    199   (let ((prefix (prefix-numeric-value current-prefix-arg))
    200         (args   (magit-stash-arguments)))
    201     (cond ((or (= prefix 16) (member "--all" args)) 'all)
    202           ((or (= prefix  4) (member "--include-untracked" args)) t))))
    203 
    204 ;;;###autoload
    205 (defun magit-snapshot-both (&optional include-untracked)
    206   "Create a snapshot of the index and working tree.
    207 Untracked files are included according to infix arguments.
    208 One prefix argument is equivalent to `--include-untracked'
    209 while two prefix arguments are equivalent to `--all'."
    210   (interactive (magit-snapshot-read-args))
    211   (magit-snapshot-save t t include-untracked t))
    212 
    213 ;;;###autoload
    214 (defun magit-snapshot-index ()
    215   "Create a snapshot of the index only.
    216 Unstaged and untracked changes are not stashed."
    217   (interactive)
    218   (magit-snapshot-save t nil nil t))
    219 
    220 ;;;###autoload
    221 (defun magit-snapshot-worktree (&optional include-untracked)
    222   "Create a snapshot of unstaged changes in the working tree.
    223 Untracked files are included according to infix arguments.
    224 One prefix argument is equivalent to `--include-untracked'
    225 while two prefix arguments are equivalent to `--all'."
    226   (interactive (magit-snapshot-read-args))
    227   (magit-snapshot-save nil t include-untracked t))
    228 
    229 (defun magit-snapshot-read-args ()
    230   (list (magit-stash-read-untracked)))
    231 
    232 (defun magit-snapshot-save (index worktree untracked &optional refresh)
    233   (magit-stash-save (concat "WIP on " (magit-stash-summary))
    234                     index worktree untracked refresh t))
    235 
    236 ;;;###autoload (autoload 'magit-stash-push "magit-stash" nil t)
    237 (transient-define-prefix magit-stash-push (&optional transient args)
    238   "Create stash using \"git stash push\".
    239 
    240 This differs from Magit's other stashing commands, which don't
    241 use \"git stash\" and are generally more flexible but don't allow
    242 specifying a list of files to be stashed."
    243   :man-page "git-stash"
    244   ["Arguments"
    245    (magit:-- :reader (lambda (prompt initial-input history)
    246                        (magit-read-files prompt initial-input history
    247                                          #'magit-modified-files)))
    248    ("-u" "Also save untracked files" ("-u" "--include-untracked"))
    249    ("-a" "Also save untracked and ignored files" ("-a" "--all"))
    250    ("-k" "Keep index" ("-k" "--keep-index"))
    251    ("-K" "Don't keep index" "--no-keep-index")]
    252   ["Actions"
    253    ("P" "push" magit-stash-push)]
    254   (interactive (if (eq transient-current-command 'magit-stash-push)
    255                    (list nil (transient-args 'magit-stash-push))
    256                  (list t)))
    257   (if transient
    258       (transient-setup 'magit-stash-push)
    259     (magit-run-git "stash" "push" args)))
    260 
    261 ;;;###autoload
    262 (defun magit-stash-apply (stash)
    263   "Apply a stash to the working tree.
    264 
    265 First try \"git stash apply --index\", which tries to preserve
    266 the index stored in the stash, if any.  This may fail because
    267 applying the stash could result in conflicts and those have to
    268 be stored in the index, making it impossible to also store the
    269 stash's index there as well.
    270 
    271 If the above failed, then try \"git stash apply\".  This fails
    272 \(with or without \"--index\") if there are any uncommitted
    273 changes to files that are also modified in the stash.
    274 
    275 If both of the above failed, then apply using \"git apply\".
    276 If there are no conflicting files, use \"--3way\".  If there are
    277 conflicting files, then using \"--3way\" requires that those
    278 files are staged first, which may be undesirable, so prompt
    279 the user whether to use \"--3way\" or \"--reject\"."
    280   (interactive (list (magit-read-stash "Apply stash")))
    281   (magit-stash--apply "apply" stash))
    282 
    283 ;;;###autoload
    284 (defun magit-stash-pop (stash)
    285   "Apply a stash to the working tree, on success remove it from stash list.
    286 
    287 First try \"git stash pop --index\", which tries to preserve
    288 the index stored in the stash, if any.  This may fail because
    289 applying the stash could result in conflicts and those have to
    290 be stored in the index, making it impossible to also store the
    291 stash's index there as well.
    292 
    293 If the above failed, then try \"git stash apply\".  This fails
    294 \(with or without \"--index\") if there are any uncommitted
    295 changes to files that are also modified in the stash.
    296 
    297 If both of the above failed, then apply using \"git apply\".
    298 If there are no conflicting files, use \"--3way\".  If there are
    299 conflicting files, then using \"--3way\" requires that those
    300 files are staged first, which may be undesirable, so prompt
    301 the user whether to use \"--3way\" or \"--reject\"."
    302   (interactive (list (magit-read-stash "Pop stash")))
    303   (magit-stash--apply "pop" stash))
    304 
    305 (defun magit-stash--apply (action stash)
    306   (or (= (magit-call-git "stash" action "--index" stash) 0)
    307       ;; The stash's index could not be applied, so always keep the stash.
    308       (= (magit-call-git "stash" "apply" stash) 0)
    309       (let* ((range (format "%s^..%s" stash stash))
    310              (stashed (magit-git-items "diff" "-z" "--name-only" range "--"))
    311              (conflicts (cl-sort (cl-union (magit-unstaged-files t stashed)
    312                                            (magit-untracked-files t stashed)
    313                                            :test #'equal)
    314                                  #'string<))
    315              (arg (cond
    316                    ((not conflicts) "--3way")
    317                    ((magit-confirm-files
    318                      'stash-apply-3way conflicts
    319                      "Apply stash using `--3way', which requires first staging"
    320                      "(else use `--reject')"
    321                      t)
    322                     (magit-stage-1 nil conflicts)
    323                     "--3way")
    324                    ("--reject"))))
    325         (with-temp-buffer
    326           (magit-git-insert "diff" range)
    327           (magit-run-git-with-input "apply" arg "-"))))
    328   (magit-refresh))
    329 
    330 ;;;###autoload
    331 (defun magit-stash-drop (stash)
    332   "Remove a stash from the stash list.
    333 When the region is active offer to drop all contained stashes."
    334   (interactive
    335    (list (if-let ((values (magit-region-values 'stash)))
    336              (magit-confirm 'drop-stashes nil "Drop %d stashes" nil values)
    337            (magit-read-stash "Drop stash"))))
    338   (dolist (stash (if (listp stash)
    339                      (nreverse (prog1 stash (setq stash (car stash))))
    340                    (list stash)))
    341     (message "Deleted refs/%s (was %s)" stash
    342              (magit-rev-parse "--short" stash))
    343     (magit-call-git "rev-parse" stash)
    344     (magit-call-git "stash" "drop" stash))
    345   (magit-refresh))
    346 
    347 ;;;###autoload
    348 (defun magit-stash-clear (ref)
    349   "Remove all stashes saved in REF's reflog by deleting REF."
    350   (interactive (let ((ref (or (magit-section-value-if 'stashes) "refs/stash")))
    351                  (magit-confirm t (format "Drop all stashes in %s" ref))
    352                  (list ref)))
    353   (magit-run-git "update-ref" "-d" ref))
    354 
    355 ;;;###autoload
    356 (defun magit-stash-branch (stash branch)
    357   "Create and checkout a new BRANCH from an existing STASH.
    358 The new branch starts at the commit that was current when the
    359 stash was created.  If the stash applies cleanly, then drop it."
    360   (interactive (list (magit-read-stash "Branch stash")
    361                      (magit-read-string-ns "Branch name")))
    362   (magit-run-git "stash" "branch" branch stash))
    363 
    364 ;;;###autoload
    365 (defun magit-stash-branch-here (stash branch)
    366   "Create and checkout a new BRANCH from an existing STASH.
    367 Use the current branch or `HEAD' as the starting-point of BRANCH.
    368 Then apply STASH, dropping it if it applies cleanly."
    369   (interactive (list (magit-read-stash "Branch stash")
    370                      (magit-read-string-ns "Branch name")))
    371   (let ((start-point (or (magit-get-current-branch) "HEAD")))
    372     (magit-call-git "checkout" "-b" branch start-point)
    373     (magit-branch-maybe-adjust-upstream branch start-point))
    374   (magit-stash-apply stash))
    375 
    376 ;;;###autoload
    377 (defun magit-stash-format-patch (stash)
    378   "Create a patch from STASH"
    379   (interactive (list (magit-read-stash "Create patch from stash")))
    380   (with-temp-file (magit-rev-format "0001-%f.patch" stash)
    381     (magit-git-insert "stash" "show" "-p" stash))
    382   (magit-refresh))
    383 
    384 ;;; Plumbing
    385 
    386 (defun magit-stash-save (message index worktree untracked
    387                                  &optional refresh keep noerror ref)
    388   (if (or (and index     (magit-staged-files t))
    389           (and worktree  (magit-unstaged-files t))
    390           (and untracked (magit-untracked-files (eq untracked 'all))))
    391       (magit-with-toplevel
    392         (magit-stash-store message (or ref "refs/stash")
    393                            (magit-stash-create message index worktree untracked))
    394         (if (eq keep 'worktree)
    395             (with-temp-buffer
    396               (magit-git-insert "diff" "--cached" "--no-ext-diff")
    397               (magit-run-git-with-input
    398                "apply" "--reverse" "--cached" "--ignore-space-change" "-")
    399               (magit-run-git-with-input
    400                "apply" "--reverse" "--ignore-space-change" "-"))
    401           (unless (eq keep t)
    402             (if (eq keep 'index)
    403                 (magit-call-git "checkout" "--" ".")
    404               (magit-call-git "reset" "--hard" "HEAD" "--"))
    405             (when untracked
    406               (magit-call-git "clean" "--force" "-d"
    407                               (and (eq untracked 'all) "-x")))))
    408         (when refresh
    409           (magit-refresh)))
    410     (unless noerror
    411       (user-error "No %s changes to save" (cond ((not index)  "unstaged")
    412                                                 ((not worktree) "staged")
    413                                                 (t "local"))))))
    414 
    415 (defun magit-stash-store (message ref commit)
    416   (magit-update-ref ref message commit t))
    417 
    418 (defun magit-stash-create (message index worktree untracked)
    419   (unless (magit-rev-parse "--verify" "HEAD")
    420     (error "You do not have the initial commit yet"))
    421   (let ((magit-git-global-arguments (nconc (list "-c" "commit.gpgsign=false")
    422                                            magit-git-global-arguments))
    423         (default-directory (magit-toplevel))
    424         (summary (magit-stash-summary))
    425         (head "HEAD"))
    426     (when (and worktree (not index))
    427       (setq head (or (magit-commit-tree "pre-stash index" nil "HEAD")
    428                      (error "Cannot save the current index state"))))
    429     (or (setq index (magit-commit-tree (concat "index on " summary) nil head))
    430         (error "Cannot save the current index state"))
    431     (and untracked
    432          (setq untracked (magit-untracked-files (eq untracked 'all)))
    433          (setq untracked (magit-with-temp-index nil nil
    434                            (or (and (magit-update-files untracked)
    435                                     (magit-commit-tree
    436                                      (concat "untracked files on " summary)))
    437                                (error "Cannot save the untracked files")))))
    438     (magit-with-temp-index index "-m"
    439       (when worktree
    440         (or (magit-update-files (magit-git-items "diff" "-z" "--name-only" head))
    441             (error "Cannot save the current worktree state")))
    442       (or (magit-commit-tree message nil head index untracked)
    443           (error "Cannot save the current worktree state")))))
    444 
    445 (defun magit-stash-summary ()
    446   (concat (or (magit-get-current-branch) "(no branch)")
    447           ": " (magit-rev-format "%h %s")))
    448 
    449 ;;; Sections
    450 
    451 (defvar-keymap magit-stashes-section-map
    452   :doc "Keymap for `stashes' section."
    453   "<remap> <magit-delete-thing>" #'magit-stash-clear
    454   "<remap> <magit-visit-thing>"  #'magit-stash-list
    455   "<2>" (magit-menu-item "Clear %t" #'magit-stash-clear)
    456   "<1>" (magit-menu-item "List %t"  #'magit-stash-list))
    457 
    458 (defvar-keymap magit-stash-section-map
    459   :doc "Keymap for `stash' sections."
    460   "<remap> <magit-cherry-pick>"  #'magit-stash-pop
    461   "<remap> <magit-cherry-apply>" #'magit-stash-apply
    462   "<remap> <magit-delete-thing>" #'magit-stash-drop
    463   "<remap> <magit-visit-thing>"  #'magit-stash-show
    464   "<4>" (magit-menu-item "Pop %M"    #'magit-stash-pop)
    465   "<3>" (magit-menu-item "Apply %M"  #'magit-stash-apply)
    466   "<2>" (magit-menu-item "Delete %M" #'magit-stash-drop)
    467   "<1>" (magit-menu-item "Visit %v"  #'magit-stash-show))
    468 
    469 (magit-define-section-jumper magit-jump-to-stashes
    470   "Stashes" stashes "refs/stash")
    471 
    472 (cl-defun magit-insert-stashes (&optional (ref   "refs/stash")
    473                                           (heading "Stashes:"))
    474   "Insert `stashes' section showing reflog for \"refs/stash\".
    475 If optional REF is non-nil, show reflog for that instead.
    476 If optional HEADING is non-nil, use that as section heading
    477 instead of \"Stashes:\"."
    478   (let ((verified (magit-rev-verify ref))
    479         (autostash (magit-rebase--get-state-lines "autostash")))
    480     (when (or autostash verified)
    481       (magit-insert-section (stashes ref)
    482         (magit-insert-heading heading)
    483         (when autostash
    484           (pcase-let ((`(,author ,date ,msg)
    485                        (split-string
    486                         (car (magit-git-lines
    487                               "show" "-q" "--format=%aN%x00%at%x00%s"
    488                               autostash))
    489                         "\0")))
    490             (magit-insert-section (stash autostash)
    491               (insert (propertize "AUTOSTASH" 'font-lock-face 'magit-hash))
    492               (insert " " msg "\n")
    493               (save-excursion
    494                 (backward-char)
    495                 (magit-log-format-margin autostash author date)))))
    496         (if verified
    497             (magit-git-wash (apply-partially #'magit-log-wash-log 'stash)
    498               "reflog" "--format=%gd%x00%aN%x00%at%x00%gs" ref)
    499           (insert ?\n)
    500           (save-excursion
    501             (backward-char)
    502             (magit-make-margin-overlay)))))))
    503 
    504 ;;; List Stashes
    505 
    506 ;;;###autoload
    507 (defun magit-stash-list ()
    508   "List all stashes in a buffer."
    509   (interactive)
    510   (magit-stashes-setup-buffer))
    511 
    512 (define-derived-mode magit-stashes-mode magit-reflog-mode "Magit Stashes"
    513   "Mode for looking at lists of stashes."
    514   :group 'magit-log
    515   (hack-dir-local-variables-non-file-buffer))
    516 
    517 (defun magit-stashes-setup-buffer ()
    518   (magit-setup-buffer #'magit-stashes-mode nil
    519     (magit-buffer-refname "refs/stash")))
    520 
    521 (defun magit-stashes-refresh-buffer ()
    522   (magit-insert-section (stashesbuf)
    523     (magit-insert-heading (if (equal magit-buffer-refname "refs/stash")
    524                               "Stashes:"
    525                             (format "Stashes [%s]:" magit-buffer-refname)))
    526     (magit-git-wash (apply-partially #'magit-log-wash-log 'stash)
    527       "reflog" "--format=%gd%x00%aN%x00%at%x00%gs" magit-buffer-refname)))
    528 
    529 (cl-defmethod magit-buffer-value (&context (major-mode magit-stashes-mode))
    530   magit-buffer-refname)
    531 
    532 (defvar magit--update-stash-buffer nil)
    533 
    534 (defun magit-stashes-maybe-update-stash-buffer (&optional _)
    535   "When moving in the stashes buffer, update the stash buffer.
    536 If there is no stash buffer in the same frame, then do nothing."
    537   (when (derived-mode-p 'magit-stashes-mode)
    538     (magit--maybe-update-stash-buffer)))
    539 
    540 (defun magit--maybe-update-stash-buffer ()
    541   (when-let* ((stash  (magit-section-value-if 'stash))
    542               (buffer (magit-get-mode-buffer 'magit-stash-mode nil t)))
    543     (if magit--update-stash-buffer
    544         (setq magit--update-stash-buffer (list stash buffer))
    545       (setq magit--update-stash-buffer (list stash buffer))
    546       (run-with-idle-timer
    547        magit-update-other-window-delay nil
    548        (let ((args (with-current-buffer buffer
    549                      (let ((magit-direct-use-buffer-arguments 'selected))
    550                        (magit-show-commit--arguments)))))
    551          (lambda ()
    552            (pcase-let ((`(,stash ,buf) magit--update-stash-buffer))
    553              (setq magit--update-stash-buffer nil)
    554              (when (buffer-live-p buf)
    555                (let ((magit-display-buffer-noselect t))
    556                  (apply #'magit-stash-show stash args))))
    557            (setq magit--update-stash-buffer nil)))))))
    558 
    559 ;;; Show Stash
    560 
    561 ;;;###autoload
    562 (defun magit-stash-show (stash &optional args files)
    563   "Show all diffs of a stash in a buffer."
    564   (interactive (cons (or (and (not current-prefix-arg)
    565                               (magit-stash-at-point))
    566                          (magit-read-stash "Show stash"))
    567                      (pcase-let ((`(,args ,files)
    568                                   (magit-diff-arguments 'magit-stash-mode)))
    569                        (list (delete "--stat" args) files))))
    570   (magit-stash-setup-buffer stash args files))
    571 
    572 (define-derived-mode magit-stash-mode magit-diff-mode "Magit Stash"
    573   "Mode for looking at individual stashes."
    574   :group 'magit-diff
    575   (hack-dir-local-variables-non-file-buffer)
    576   (setq magit--imenu-group-types '(commit)))
    577 
    578 (defun magit-stash-setup-buffer (stash args files)
    579   (magit-setup-buffer #'magit-stash-mode nil
    580     (magit-buffer-revision stash)
    581     (magit-buffer-range (format "%s^..%s" stash stash))
    582     (magit-buffer-diff-args args)
    583     (magit-buffer-diff-files files)))
    584 
    585 (defun magit-stash-refresh-buffer ()
    586   (magit-set-header-line-format
    587    (concat (capitalize magit-buffer-revision) " "
    588            (propertize (magit-rev-format "%s" magit-buffer-revision)
    589                        'font-lock-face
    590                        (list :weight 'normal :foreground
    591                              (face-attribute 'default :foreground)))))
    592   (setq magit-buffer-revision-hash (magit-rev-parse magit-buffer-revision))
    593   (magit-insert-section (stash)
    594     (magit-run-section-hook 'magit-stash-sections-hook)))
    595 
    596 (cl-defmethod magit-buffer-value (&context (major-mode magit-stash-mode))
    597   magit-buffer-revision)
    598 
    599 (defun magit-stash-insert-section (commit range message &optional files)
    600   (magit-insert-section (commit commit)
    601     (magit-insert-heading message)
    602     (magit--insert-diff nil
    603       "diff" range "-p" "--no-prefix" magit-buffer-diff-args
    604       "--" (or files magit-buffer-diff-files))))
    605 
    606 (defun magit-insert-stash-notes ()
    607   "Insert section showing notes for a stash.
    608 This shows the notes for stash@{N} but not for the other commits
    609 that make up the stash."
    610   (magit-insert-section (note)
    611     (magit-insert-heading "Notes")
    612     (magit-git-insert "notes" "show" magit-buffer-revision)
    613     (magit-cancel-section 'if-empty)
    614     (insert "\n")))
    615 
    616 (defun magit-insert-stash-index ()
    617   "Insert section showing staged changes of the stash."
    618   (magit-stash-insert-section
    619    (format "%s^2" magit-buffer-revision)
    620    (format "%s^..%s^2" magit-buffer-revision magit-buffer-revision)
    621    "Staged"))
    622 
    623 (defun magit-insert-stash-worktree ()
    624   "Insert section showing unstaged changes of the stash."
    625   (magit-stash-insert-section
    626    magit-buffer-revision
    627    (format "%s^2..%s" magit-buffer-revision magit-buffer-revision)
    628    "Unstaged"))
    629 
    630 (defun magit-insert-stash-untracked ()
    631   "Insert section showing the untracked files commit of the stash."
    632   (let ((stash magit-buffer-revision)
    633         (rev (concat magit-buffer-revision "^3")))
    634     (when (magit-rev-verify rev)
    635       (magit-stash-insert-section (format "%s^3" stash)
    636                                   (format "%s^..%s^3" stash stash)
    637                                   "Untracked files"
    638                                   (magit-git-items "ls-tree" "-z" "--name-only"
    639                                                    "-r" "--full-tree" rev)))))
    640 
    641 ;;; _
    642 (provide 'magit-stash)
    643 ;;; magit-stash.el ends here