config

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

magit-stash.el (26473B)


      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" magit-insert-stashes)
    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 t
    524       (if (equal magit-buffer-refname "refs/stash")
    525           "Stashes"
    526         (format "Stashes [%s]" magit-buffer-refname)))
    527     (magit-git-wash (apply-partially #'magit-log-wash-log 'stash)
    528       "reflog" "--format=%gd%x00%aN%x00%at%x00%gs" magit-buffer-refname)))
    529 
    530 (cl-defmethod magit-buffer-value (&context (major-mode magit-stashes-mode))
    531   magit-buffer-refname)
    532 
    533 (defvar magit--update-stash-buffer nil)
    534 
    535 (defun magit-stashes-maybe-update-stash-buffer (&optional _)
    536   "When moving in the stashes buffer, update the stash buffer.
    537 If there is no stash buffer in the same frame, then do nothing."
    538   (when (derived-mode-p 'magit-stashes-mode)
    539     (magit--maybe-update-stash-buffer)))
    540 
    541 (defun magit--maybe-update-stash-buffer ()
    542   (when-let* ((stash  (magit-section-value-if 'stash))
    543               (buffer (magit-get-mode-buffer 'magit-stash-mode nil t)))
    544     (if magit--update-stash-buffer
    545         (setq magit--update-stash-buffer (list stash buffer))
    546       (setq magit--update-stash-buffer (list stash buffer))
    547       (run-with-idle-timer
    548        magit-update-other-window-delay nil
    549        (let ((args (with-current-buffer buffer
    550                      (let ((magit-direct-use-buffer-arguments 'selected))
    551                        (magit-show-commit--arguments)))))
    552          (lambda ()
    553            (pcase-let ((`(,stash ,buf) magit--update-stash-buffer))
    554              (setq magit--update-stash-buffer nil)
    555              (when (buffer-live-p buf)
    556                (let ((magit-display-buffer-noselect t))
    557                  (apply #'magit-stash-show stash args))))
    558            (setq magit--update-stash-buffer nil)))))))
    559 
    560 ;;; Show Stash
    561 
    562 ;;;###autoload
    563 (defun magit-stash-show (stash &optional args files)
    564   "Show all diffs of a stash in a buffer."
    565   (interactive (cons (or (and (not current-prefix-arg)
    566                               (magit-stash-at-point))
    567                          (magit-read-stash "Show stash"))
    568                      (pcase-let ((`(,args ,files)
    569                                   (magit-diff-arguments 'magit-stash-mode)))
    570                        (list (delete "--stat" args) files))))
    571   (magit-stash-setup-buffer stash args files))
    572 
    573 (define-derived-mode magit-stash-mode magit-diff-mode "Magit Stash"
    574   "Mode for looking at individual stashes."
    575   :group 'magit-diff
    576   (hack-dir-local-variables-non-file-buffer)
    577   (setq magit--imenu-group-types '(commit)))
    578 
    579 (defun magit-stash-setup-buffer (stash args files)
    580   (magit-setup-buffer #'magit-stash-mode nil
    581     (magit-buffer-revision stash)
    582     (magit-buffer-range (format "%s^..%s" stash stash))
    583     (magit-buffer-diff-args args)
    584     (magit-buffer-diff-files files)))
    585 
    586 (defun magit-stash-refresh-buffer ()
    587   (magit-set-header-line-format
    588    (concat (capitalize magit-buffer-revision) " "
    589            (propertize (magit-rev-format "%s" magit-buffer-revision)
    590                        'font-lock-face
    591                        (list :weight 'normal :foreground
    592                              (face-attribute 'default :foreground)))))
    593   (setq magit-buffer-revision-hash (magit-rev-parse magit-buffer-revision))
    594   (magit-insert-section (stash)
    595     (magit-run-section-hook 'magit-stash-sections-hook)))
    596 
    597 (cl-defmethod magit-buffer-value (&context (major-mode magit-stash-mode))
    598   magit-buffer-revision)
    599 
    600 (defun magit-stash-insert-section (commit range message &optional files)
    601   (magit-insert-section (commit commit)
    602     (magit-insert-heading message)
    603     (magit--insert-diff nil
    604       "diff" range "-p" "--no-prefix" magit-buffer-diff-args
    605       "--" (or files magit-buffer-diff-files))))
    606 
    607 (defun magit-insert-stash-notes ()
    608   "Insert section showing notes for a stash.
    609 This shows the notes for stash@{N} but not for the other commits
    610 that make up the stash."
    611   (magit-insert-section (note)
    612     (magit-insert-heading t "Notes")
    613     (magit-git-insert "notes" "show" magit-buffer-revision)
    614     (magit-cancel-section 'if-empty)
    615     (insert "\n")))
    616 
    617 (defun magit-insert-stash-index ()
    618   "Insert section showing staged changes of the stash."
    619   (magit-stash-insert-section
    620    (format "%s^2" magit-buffer-revision)
    621    (format "%s^..%s^2" magit-buffer-revision magit-buffer-revision)
    622    "Staged"))
    623 
    624 (defun magit-insert-stash-worktree ()
    625   "Insert section showing unstaged changes of the stash."
    626   (magit-stash-insert-section
    627    magit-buffer-revision
    628    (format "%s^2..%s" magit-buffer-revision magit-buffer-revision)
    629    "Unstaged"))
    630 
    631 (defun magit-insert-stash-untracked ()
    632   "Insert section showing the untracked files commit of the stash."
    633   (let ((stash magit-buffer-revision)
    634         (rev (concat magit-buffer-revision "^3")))
    635     (when (magit-rev-verify rev)
    636       (magit-stash-insert-section (format "%s^3" stash)
    637                                   (format "%s^..%s^3" stash stash)
    638                                   "Untracked files"
    639                                   (magit-git-items "ls-tree" "-z" "--name-only"
    640                                                    "-r" "--full-tree" rev)))))
    641 
    642 ;;; _
    643 (provide 'magit-stash)
    644 ;;; magit-stash.el ends here