magit-stash.el (27962B)
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" 260 (seq-filter #'atom args) 261 (assoc "--" args)))) 262 263 ;;;###autoload 264 (defun magit-stash-apply (stash) 265 "Apply a stash to the working tree. 266 267 When using a Git release before v2.38.0, simply run \"git stash 268 apply\" or with a prefix argument \"git stash apply --index\". 269 270 When using Git v2.38.0 or later, behave more intelligently: 271 272 First try \"git stash apply --index\", which tries to preserve 273 the index stored in the stash, if any. This may fail because 274 applying the stash could result in conflicts and those have to 275 be stored in the index, making it impossible to also store the 276 stash's index there. 277 278 If the above failed, then try \"git stash apply\". This fails 279 \(with or without \"--index\") if there are any uncommitted 280 changes to files that are also modified in the stash. 281 282 If both of the above failed, then apply using \"git apply\". 283 If there are no conflicting files, use \"--3way\". If there are 284 conflicting files, then using \"--3way\" requires that those 285 files are staged first, which may be undesirable, so prompt 286 the user whether to use \"--3way\" or \"--reject\"." 287 (interactive (list (magit-read-stash "Apply stash"))) 288 (magit-stash--apply "apply" stash)) 289 290 ;;;###autoload 291 (defun magit-stash-pop (stash) 292 "Apply a stash to the working tree, on success remove it from stash list. 293 294 When using a Git release before v2.38.0, simply run \"git stash 295 pop\" or with a prefix argument \"git stash pop --index\". 296 297 When using Git v2.38.0 or later, behave more intelligently: 298 299 First try \"git stash pop --index\", which tries to preserve 300 the index stored in the stash, if any. This may fail because 301 applying the stash could result in conflicts and those have to 302 be stored in the index, making it impossible to also store the 303 stash's index there. 304 305 If the above failed, then try \"git stash apply\". This fails 306 \(with or without \"--index\") if there are any uncommitted 307 changes to files that are also modified in the stash. 308 309 If both of the above failed, then apply using \"git apply\". 310 If there are no conflicting files, use \"--3way\". If there are 311 conflicting files, then using \"--3way\" requires that those 312 files are staged first, which may be undesirable, so prompt 313 the user whether to use \"--3way\" or \"--reject\"." 314 (interactive (list (magit-read-stash "Pop stash"))) 315 (magit-stash--apply "pop" stash)) 316 317 (defun magit-stash--apply (action stash) 318 (if (magit-git-version< "2.38.0") 319 (magit-run-git "stash" action stash (and current-prefix-arg "--index")) 320 (or (magit--run-git-stash action "--index" stash) 321 ;; The stash's index could not be applied, so always keep the stash. 322 (magit--run-git-stash "apply" stash) 323 (let* ((range (format "%s^..%s" stash stash)) 324 (stashed (magit-git-items "diff" "-z" "--name-only" range "--")) 325 (conflicts (cl-sort (cl-union (magit-unstaged-files t stashed) 326 (magit-untracked-files t stashed) 327 :test #'equal) 328 #'string<)) 329 (arg (cond 330 ((not conflicts) "--3way") 331 ((magit-confirm-files 332 'stash-apply-3way conflicts 333 "Apply stash using `--3way', which requires first staging" 334 "(else use `--reject')" 335 t) 336 (magit-stage-1 nil conflicts) 337 "--3way") 338 ("--reject")))) 339 (with-temp-buffer 340 (magit-git-insert "diff" range) 341 (magit-run-git-with-input "apply" arg "-")))) 342 (magit-refresh))) 343 344 (defun magit--run-git-stash (&rest args) 345 (magit--with-temp-process-buffer 346 (let ((exit (save-excursion 347 (with-environment-variables (("LC_ALL" "en_US.utf8")) 348 (magit-process-git t "stash" args)))) 349 (buffer (current-buffer)) 350 (failed (looking-at "\\`error: \ 351 Your local changes to the following files would be overwritten by merge"))) 352 (with-current-buffer (magit-process-buffer t) 353 (magit-process-finish-section 354 (magit-process-insert-section default-directory magit-git-executable 355 (magit-process-git-arguments args) 356 exit buffer) 357 exit)) 358 (pcase (list exit failed) 359 (`(0 ,_) t) ; no conflict 360 (`(1 nil) t) ; successfully installed conflict 361 (_ nil))))) ; could not install conflict, or genuine error 362 363 ;;;###autoload 364 (defun magit-stash-drop (stash) 365 "Remove a stash from the stash list. 366 When the region is active offer to drop all contained stashes." 367 (interactive 368 (list (if-let ((values (magit-region-values 'stash))) 369 (magit-confirm 'drop-stashes nil "Drop %d stashes" nil values) 370 (magit-read-stash "Drop stash")))) 371 (dolist (stash (if (listp stash) 372 (nreverse (prog1 stash (setq stash (car stash)))) 373 (list stash))) 374 (message "Deleted refs/%s (was %s)" stash 375 (magit-rev-parse "--short" stash)) 376 (magit-call-git "rev-parse" stash) 377 (magit-call-git "stash" "drop" stash)) 378 (magit-refresh)) 379 380 ;;;###autoload 381 (defun magit-stash-clear (ref) 382 "Remove all stashes saved in REF's reflog by deleting REF." 383 (interactive (let ((ref (or (magit-section-value-if 'stashes) "refs/stash"))) 384 (magit-confirm t (list "Drop all stashes in %s" ref)) 385 (list ref))) 386 (magit-run-git "update-ref" "-d" ref)) 387 388 ;;;###autoload 389 (defun magit-stash-branch (stash branch) 390 "Create and checkout a new BRANCH from an existing STASH. 391 The new branch starts at the commit that was current when the 392 stash was created. If the stash applies cleanly, then drop it." 393 (interactive (list (magit-read-stash "Branch stash") 394 (magit-read-string-ns "Branch name"))) 395 (magit-run-git "stash" "branch" branch stash)) 396 397 ;;;###autoload 398 (defun magit-stash-branch-here (stash branch) 399 "Create and checkout a new BRANCH from an existing STASH. 400 Use the current branch or `HEAD' as the starting-point of BRANCH. 401 Then apply STASH, dropping it if it applies cleanly." 402 (interactive (list (magit-read-stash "Branch stash") 403 (magit-read-string-ns "Branch name"))) 404 (let ((start-point (or (magit-get-current-branch) "HEAD"))) 405 (magit-call-git "checkout" "-b" branch start-point) 406 (magit-branch-maybe-adjust-upstream branch start-point)) 407 (magit-stash-apply stash)) 408 409 ;;;###autoload 410 (defun magit-stash-format-patch (stash) 411 "Create a patch from STASH" 412 (interactive (list (magit-read-stash "Create patch from stash"))) 413 (with-temp-file (magit-rev-format "0001-%f.patch" stash) 414 (magit-git-insert "stash" "show" "-p" stash)) 415 (magit-refresh)) 416 417 ;;; Plumbing 418 419 (defun magit-stash-save (message index worktree untracked 420 &optional refresh keep noerror ref) 421 (if (or (and index (magit-staged-files t)) 422 (and worktree (magit-unstaged-files t)) 423 (and untracked (magit-untracked-files (eq untracked 'all)))) 424 (magit-with-toplevel 425 (magit-stash-store message (or ref "refs/stash") 426 (magit-stash-create message index worktree untracked)) 427 (if (eq keep 'worktree) 428 (with-temp-buffer 429 (magit-git-insert "diff" "--cached" "--no-ext-diff") 430 (magit-run-git-with-input 431 "apply" "--reverse" "--cached" "--ignore-space-change" "-") 432 (magit-run-git-with-input 433 "apply" "--reverse" "--ignore-space-change" "-")) 434 (unless (eq keep t) 435 (if (eq keep 'index) 436 (magit-call-git "checkout" "--" ".") 437 (magit-call-git "reset" "--hard" "HEAD" "--")) 438 (when untracked 439 (magit-call-git "clean" "--force" "-d" 440 (and (eq untracked 'all) "-x"))))) 441 (when refresh 442 (magit-refresh))) 443 (unless noerror 444 (user-error "No %s changes to save" (cond ((not index) "unstaged") 445 ((not worktree) "staged") 446 (t "local")))))) 447 448 (defun magit-stash-store (message ref commit) 449 (magit-update-ref ref message commit t)) 450 451 (defun magit-stash-create (message index worktree untracked) 452 (unless (magit-rev-parse "--verify" "HEAD") 453 (error "You do not have the initial commit yet")) 454 (let ((magit-git-global-arguments (nconc (list "-c" "commit.gpgsign=false") 455 magit-git-global-arguments)) 456 (default-directory (magit-toplevel)) 457 (summary (magit-stash-summary)) 458 (head "HEAD")) 459 (when (and worktree (not index)) 460 (setq head (or (magit-commit-tree "pre-stash index" nil "HEAD") 461 (error "Cannot save the current index state")))) 462 (or (setq index (magit-commit-tree (concat "index on " summary) nil head)) 463 (error "Cannot save the current index state")) 464 (and untracked 465 (setq untracked (magit-untracked-files (eq untracked 'all))) 466 (setq untracked (magit-with-temp-index nil nil 467 (or (and (magit-update-files untracked) 468 (magit-commit-tree 469 (concat "untracked files on " summary))) 470 (error "Cannot save the untracked files"))))) 471 (magit-with-temp-index index "-m" 472 (when worktree 473 (or (magit-update-files (magit-git-items "diff" "-z" "--name-only" head)) 474 (error "Cannot save the current worktree state"))) 475 (or (magit-commit-tree message nil head index untracked) 476 (error "Cannot save the current worktree state"))))) 477 478 (defun magit-stash-summary () 479 (concat (or (magit-get-current-branch) "(no branch)") 480 ": " (magit-rev-format "%h %s"))) 481 482 ;;; Sections 483 484 (defvar-keymap magit-stashes-section-map 485 :doc "Keymap for `stashes' section." 486 "<remap> <magit-delete-thing>" #'magit-stash-clear 487 "<remap> <magit-visit-thing>" #'magit-stash-list 488 "<2>" (magit-menu-item "Clear %t" #'magit-stash-clear) 489 "<1>" (magit-menu-item "List %t" #'magit-stash-list)) 490 491 (defvar-keymap magit-stash-section-map 492 :doc "Keymap for `stash' sections." 493 "<remap> <magit-cherry-pick>" #'magit-stash-pop 494 "<remap> <magit-cherry-apply>" #'magit-stash-apply 495 "<remap> <magit-delete-thing>" #'magit-stash-drop 496 "<remap> <magit-visit-thing>" #'magit-stash-show 497 "<4>" (magit-menu-item "Pop %M" #'magit-stash-pop) 498 "<3>" (magit-menu-item "Apply %M" #'magit-stash-apply) 499 "<2>" (magit-menu-item "Delete %M" #'magit-stash-drop) 500 "<1>" (magit-menu-item "Visit %v" #'magit-stash-show)) 501 502 (magit-define-section-jumper magit-jump-to-stashes 503 "Stashes" stashes "refs/stash" magit-insert-stashes) 504 505 (cl-defun magit-insert-stashes (&optional (ref "refs/stash") 506 (heading "Stashes:")) 507 "Insert `stashes' section showing reflog for \"refs/stash\". 508 If optional REF is non-nil, show reflog for that instead. 509 If optional HEADING is non-nil, use that as section heading 510 instead of \"Stashes:\"." 511 (let ((verified (magit-rev-verify ref)) 512 (autostash (magit-rebase--get-state-lines "autostash"))) 513 (when (or autostash verified) 514 (magit-insert-section (stashes ref) 515 (magit-insert-heading heading) 516 (when autostash 517 (pcase-let ((`(,author ,date ,msg) 518 (split-string 519 (car (magit-git-lines 520 "show" "-q" "--format=%aN%x00%at%x00%s" 521 autostash)) 522 "\0"))) 523 (magit-insert-section (stash autostash) 524 (insert (propertize "AUTOSTASH" 'font-lock-face 'magit-hash)) 525 (insert " " msg "\n") 526 (save-excursion 527 (backward-char) 528 (magit-log-format-margin autostash author date))))) 529 (if verified 530 (magit-git-wash (apply-partially #'magit-log-wash-log 'stash) 531 "reflog" "--format=%gd%x00%aN%x00%at%x00%gs" ref) 532 (insert ?\n) 533 (save-excursion 534 (backward-char) 535 (magit-make-margin-overlay))))))) 536 537 ;;; List Stashes 538 539 ;;;###autoload 540 (defun magit-stash-list () 541 "List all stashes in a buffer." 542 (interactive) 543 (magit-stashes-setup-buffer)) 544 545 (define-derived-mode magit-stashes-mode magit-reflog-mode "Magit Stashes" 546 "Mode for looking at lists of stashes." 547 :interactive nil 548 :group 'magit-log 549 (magit-hack-dir-local-variables)) 550 551 (defun magit-stashes-setup-buffer () 552 (magit-setup-buffer #'magit-stashes-mode nil 553 (magit-buffer-refname "refs/stash"))) 554 555 (defun magit-stashes-refresh-buffer () 556 (magit-insert-section (stashesbuf) 557 (magit-insert-heading t 558 (if (equal magit-buffer-refname "refs/stash") 559 "Stashes" 560 (format "Stashes [%s]" magit-buffer-refname))) 561 (magit-git-wash (apply-partially #'magit-log-wash-log 'stash) 562 "reflog" "--format=%gd%x00%aN%x00%at%x00%gs" magit-buffer-refname))) 563 564 (cl-defmethod magit-buffer-value (&context (major-mode magit-stashes-mode)) 565 magit-buffer-refname) 566 567 (defvar magit--update-stash-buffer nil) 568 569 (defun magit-stashes-maybe-update-stash-buffer (&optional _) 570 "When moving in the stashes buffer, update the stash buffer. 571 If there is no stash buffer in the same frame, then do nothing." 572 (when (derived-mode-p 'magit-stashes-mode) 573 (magit--maybe-update-stash-buffer))) 574 575 (defun magit--maybe-update-stash-buffer () 576 (when-let* ((stash (magit-section-value-if 'stash)) 577 (buffer (magit-get-mode-buffer 'magit-stash-mode nil t))) 578 (if magit--update-stash-buffer 579 (setq magit--update-stash-buffer (list stash buffer)) 580 (setq magit--update-stash-buffer (list stash buffer)) 581 (run-with-idle-timer 582 magit-update-other-window-delay nil 583 (let ((args (with-current-buffer buffer 584 (let ((magit-direct-use-buffer-arguments 'selected)) 585 (magit-show-commit--arguments))))) 586 (lambda () 587 (pcase-let ((`(,stash ,buf) magit--update-stash-buffer)) 588 (setq magit--update-stash-buffer nil) 589 (when (buffer-live-p buf) 590 (let ((magit-display-buffer-noselect t)) 591 (apply #'magit-stash-show stash args)))) 592 (setq magit--update-stash-buffer nil))))))) 593 594 ;;; Show Stash 595 596 ;;;###autoload 597 (defun magit-stash-show (stash &optional args files) 598 "Show all diffs of a stash in a buffer." 599 (interactive (cons (or (and (not current-prefix-arg) 600 (magit-stash-at-point)) 601 (magit-read-stash "Show stash")) 602 (pcase-let ((`(,args ,files) 603 (magit-diff-arguments 'magit-stash-mode))) 604 (list (delete "--stat" args) files)))) 605 (magit-stash-setup-buffer stash args files)) 606 607 (define-derived-mode magit-stash-mode magit-diff-mode "Magit Stash" 608 "Mode for looking at individual stashes." 609 :interactive nil 610 :group 'magit-diff 611 (magit-hack-dir-local-variables) 612 (setq magit--imenu-group-types '(commit))) 613 614 (defun magit-stash-setup-buffer (stash args files) 615 (magit-setup-buffer #'magit-stash-mode nil 616 (magit-buffer-revision stash) 617 (magit-buffer-range (format "%s^..%s" stash stash)) 618 (magit-buffer-diff-args args) 619 (magit-buffer-diff-files files))) 620 621 (defun magit-stash-refresh-buffer () 622 (magit-set-header-line-format 623 (concat (capitalize magit-buffer-revision) " " 624 (propertize (magit-rev-format "%s" magit-buffer-revision) 625 'font-lock-face 626 (list :weight 'normal :foreground 627 (face-attribute 'default :foreground))))) 628 (setq magit-buffer-revision-hash (magit-rev-parse magit-buffer-revision)) 629 (magit-insert-section (stash) 630 (magit-run-section-hook 'magit-stash-sections-hook))) 631 632 (cl-defmethod magit-buffer-value (&context (major-mode magit-stash-mode)) 633 magit-buffer-revision) 634 635 (defun magit-stash-insert-section (commit range message &optional files) 636 (magit-insert-section (commit commit) 637 (magit-insert-heading message) 638 (magit--insert-diff nil 639 "diff" range "-p" "--no-prefix" magit-buffer-diff-args 640 "--" (or files magit-buffer-diff-files)))) 641 642 (defun magit-insert-stash-notes () 643 "Insert section showing notes for a stash. 644 This shows the notes for stash@{N} but not for the other commits 645 that make up the stash." 646 (magit-insert-section (note) 647 (magit-insert-heading t "Notes") 648 (magit-git-insert "notes" "show" magit-buffer-revision) 649 (magit-cancel-section 'if-empty) 650 (insert "\n"))) 651 652 (defun magit-insert-stash-index () 653 "Insert section showing staged changes of the stash." 654 (magit-stash-insert-section 655 (format "%s^2" magit-buffer-revision) 656 (format "%s^..%s^2" magit-buffer-revision magit-buffer-revision) 657 "Staged")) 658 659 (defun magit-insert-stash-worktree () 660 "Insert section showing unstaged changes of the stash." 661 (magit-stash-insert-section 662 magit-buffer-revision 663 (format "%s^2..%s" magit-buffer-revision magit-buffer-revision) 664 "Unstaged")) 665 666 (defun magit-insert-stash-untracked () 667 "Insert section showing the untracked files commit of the stash." 668 (let ((stash magit-buffer-revision) 669 (rev (concat magit-buffer-revision "^3"))) 670 (when (magit-rev-verify rev) 671 (magit-stash-insert-section (format "%s^3" stash) 672 (format "%s^..%s^3" stash stash) 673 "Untracked files" 674 (magit-git-items "ls-tree" "-z" "--name-only" 675 "-r" "--full-tree" rev))))) 676 677 ;;; _ 678 (provide 'magit-stash) 679 ;;; magit-stash.el ends here