Klimi's new dotfiles with stow.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

516 řádky
20 KiB

před 4 roky
  1. ;;; magit-stash.el --- stash support for Magit -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2008-2019 The Magit Project Contributors
  3. ;;
  4. ;; You should have received a copy of the AUTHORS.md file which
  5. ;; lists all contributors. If not, see http://magit.vc/authors.
  6. ;; Author: Jonas Bernoulli <jonas@bernoul.li>
  7. ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
  8. ;; Magit is free software; you can redistribute it and/or modify it
  9. ;; under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation; either version 3, or (at your option)
  11. ;; any later version.
  12. ;;
  13. ;; Magit is distributed in the hope that it will be useful, but WITHOUT
  14. ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  15. ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  16. ;; License for more details.
  17. ;;
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with Magit. If not, see http://www.gnu.org/licenses.
  20. ;;; Commentary:
  21. ;; Support for Git stashes.
  22. ;;; Code:
  23. (eval-when-compile
  24. (require 'subr-x))
  25. (require 'magit)
  26. (require 'magit-reflog)
  27. ;;; Options
  28. (defgroup magit-stash nil
  29. "List stashes and show stash diffs."
  30. :group 'magit-modes)
  31. ;;;; Diff options
  32. (defcustom magit-stash-sections-hook
  33. '(magit-insert-stash-notes
  34. magit-insert-stash-worktree
  35. magit-insert-stash-index
  36. magit-insert-stash-untracked)
  37. "Hook run to insert sections into stash diff buffers."
  38. :package-version '(magit . "2.1.0")
  39. :group 'magit-stash
  40. :type 'hook)
  41. ;;;; Log options
  42. (defcustom magit-stashes-margin
  43. (list (nth 0 magit-log-margin)
  44. (nth 1 magit-log-margin)
  45. 'magit-log-margin-width nil
  46. (nth 4 magit-log-margin))
  47. "Format of the margin in `magit-stashes-mode' buffers.
  48. The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH).
  49. If INIT is non-nil, then the margin is shown initially.
  50. STYLE controls how to format the committer date. It can be one
  51. of `age' (to show the age of the commit), `age-abbreviated' (to
  52. abbreviate the time unit to a character), or a string (suitable
  53. for `format-time-string') to show the actual date.
  54. WIDTH controls the width of the margin. This exists for forward
  55. compatibility and currently the value should not be changed.
  56. AUTHOR controls whether the name of the author is also shown by
  57. default.
  58. AUTHOR-WIDTH has to be an integer. When the name of the author
  59. is shown, then this specifies how much space is used to do so."
  60. :package-version '(magit . "2.9.0")
  61. :group 'magit-stash
  62. :group 'magit-margin
  63. :type magit-log-margin--custom-type
  64. :initialize 'magit-custom-initialize-reset
  65. :set-after '(magit-log-margin)
  66. :set (apply-partially #'magit-margin-set-variable 'magit-stashes-mode))
  67. ;;; Commands
  68. ;;;###autoload (autoload 'magit-stash "magit-stash" nil t)
  69. (define-transient-command magit-stash ()
  70. "Stash uncommitted changes."
  71. :man-page "git-stash"
  72. ["Arguments"
  73. ("-u" "Also save untracked files" ("-u" "--include-untracked"))
  74. ("-a" "Also save untracked and ignored files" ("-a" "--all"))]
  75. [["Stash"
  76. ("z" "both" magit-stash-both)
  77. ("i" "index" magit-stash-index)
  78. ("w" "worktree" magit-stash-worktree)
  79. ("x" "keeping index" magit-stash-keep-index)]
  80. ["Snapshot"
  81. ("Z" "both" magit-snapshot-both)
  82. ("I" "index" magit-snapshot-index)
  83. ("W" "worktree" magit-snapshot-worktree)
  84. ("r" "to wip ref" magit-wip-commit)]
  85. ["Use"
  86. ("a" "Apply" magit-stash-apply)
  87. ("p" "Pop" magit-stash-pop)
  88. ("k" "Drop" magit-stash-drop)]
  89. ["Inspect"
  90. ("l" "List" magit-stash-list)
  91. ("v" "Show" magit-stash-show)]
  92. ["Transform"
  93. ("b" "Branch" magit-stash-branch)
  94. ("B" "Branch here" magit-stash-branch-here)
  95. ("f" "Format patch" magit-stash-format-patch)]])
  96. (defun magit-stash-arguments ()
  97. (transient-args 'magit-stash))
  98. ;;;###autoload
  99. (defun magit-stash-both (message &optional include-untracked)
  100. "Create a stash of the index and working tree.
  101. Untracked files are included according to infix arguments.
  102. One prefix argument is equivalent to `--include-untracked'
  103. while two prefix arguments are equivalent to `--all'."
  104. (interactive (magit-stash-read-args))
  105. (magit-stash-save message t t include-untracked t))
  106. ;;;###autoload
  107. (defun magit-stash-index (message)
  108. "Create a stash of the index only.
  109. Unstaged and untracked changes are not stashed. The stashed
  110. changes are applied in reverse to both the index and the
  111. worktree. This command can fail when the worktree is not clean.
  112. Applying the resulting stash has the inverse effect."
  113. (interactive (list (magit-stash-read-message)))
  114. (magit-stash-save message t nil nil t 'worktree))
  115. ;;;###autoload
  116. (defun magit-stash-worktree (message &optional include-untracked)
  117. "Create a stash of unstaged changes in the working tree.
  118. Untracked files are included according to infix arguments.
  119. One prefix argument is equivalent to `--include-untracked'
  120. while two prefix arguments are equivalent to `--all'."
  121. (interactive (magit-stash-read-args))
  122. (magit-stash-save message nil t include-untracked t 'index))
  123. ;;;###autoload
  124. (defun magit-stash-keep-index (message &optional include-untracked)
  125. "Create a stash of the index and working tree, keeping index intact.
  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 (magit-stash-read-args))
  130. (magit-stash-save message t t include-untracked t 'index))
  131. (defun magit-stash-read-args ()
  132. (list (magit-stash-read-message)
  133. (magit-stash-read-untracked)))
  134. (defun magit-stash-read-untracked ()
  135. (let ((prefix (prefix-numeric-value current-prefix-arg))
  136. (args (magit-stash-arguments)))
  137. (cond ((or (= prefix 16) (member "--all" args)) 'all)
  138. ((or (= prefix 4) (member "--include-untracked" args)) t))))
  139. (defun magit-stash-read-message ()
  140. (let* ((default (format "On %s: "
  141. (or (magit-get-current-branch) "(no branch)")))
  142. (input (magit-read-string "Stash message" default)))
  143. (if (equal input default)
  144. (concat default (magit-rev-format "%h %s"))
  145. input)))
  146. ;;;###autoload
  147. (defun magit-snapshot-both (&optional include-untracked)
  148. "Create a snapshot of the index and working tree.
  149. Untracked files are included according to infix arguments.
  150. One prefix argument is equivalent to `--include-untracked'
  151. while two prefix arguments are equivalent to `--all'."
  152. (interactive (magit-snapshot-read-args))
  153. (magit-snapshot-save t t include-untracked t))
  154. ;;;###autoload
  155. (defun magit-snapshot-index ()
  156. "Create a snapshot of the index only.
  157. Unstaged and untracked changes are not stashed."
  158. (interactive)
  159. (magit-snapshot-save t nil nil t))
  160. ;;;###autoload
  161. (defun magit-snapshot-worktree (&optional include-untracked)
  162. "Create a snapshot of unstaged changes in the working tree.
  163. Untracked files are included according to infix arguments.
  164. One prefix argument is equivalent to `--include-untracked'
  165. while two prefix arguments are equivalent to `--all'."
  166. (interactive (magit-snapshot-read-args))
  167. (magit-snapshot-save nil t include-untracked t))
  168. (defun magit-snapshot-read-args ()
  169. (list (magit-stash-read-untracked)))
  170. (defun magit-snapshot-save (index worktree untracked &optional refresh)
  171. (magit-stash-save (concat "WIP on " (magit-stash-summary))
  172. index worktree untracked refresh t))
  173. ;;;###autoload
  174. (defun magit-stash-apply (stash)
  175. "Apply a stash to the working tree.
  176. Try to preserve the stash index. If that fails because there
  177. are staged changes, apply without preserving the stash index."
  178. (interactive (list (magit-read-stash "Apply stash")))
  179. (if (= (magit-call-git "stash" "apply" "--index" stash) 0)
  180. (magit-refresh)
  181. (magit-run-git "stash" "apply" stash)))
  182. (defun magit-stash-pop (stash)
  183. "Apply a stash to the working tree and remove it from stash list.
  184. Try to preserve the stash index. If that fails because there
  185. are staged changes, apply without preserving the stash index
  186. and forgo removing the stash."
  187. (interactive (list (magit-read-stash "Pop stash")))
  188. (if (= (magit-call-git "stash" "apply" "--index" stash) 0)
  189. (magit-stash-drop stash)
  190. (magit-run-git "stash" "apply" stash)))
  191. ;;;###autoload
  192. (defun magit-stash-drop (stash)
  193. "Remove a stash from the stash list.
  194. When the region is active offer to drop all contained stashes."
  195. (interactive (list (--if-let (magit-region-values 'stash)
  196. (magit-confirm t nil "Drop %i stashes" nil it)
  197. (magit-read-stash "Drop stash"))))
  198. (dolist (stash (if (listp stash)
  199. (nreverse (prog1 stash (setq stash (car stash))))
  200. (list stash)))
  201. (message "Deleted refs/%s (was %s)" stash
  202. (magit-rev-parse "--short" stash))
  203. (magit-call-git "rev-parse" stash)
  204. (magit-call-git "reflog" "delete" "--updateref" "--rewrite" stash))
  205. (when-let ((ref (and (string-match "\\(.+\\)@{[0-9]+}$" stash)
  206. (match-string 1 stash))))
  207. (unless (string-match "^refs/" ref)
  208. (setq ref (concat "refs/" ref)))
  209. (unless (magit-rev-verify (concat ref "@{0}"))
  210. (magit-run-git "update-ref" "-d" ref)))
  211. (magit-refresh))
  212. ;;;###autoload
  213. (defun magit-stash-clear (ref)
  214. "Remove all stashes saved in REF's reflog by deleting REF."
  215. (interactive (let ((ref (or (magit-section-value-if 'stashes) "refs/stash")))
  216. (magit-confirm t (format "Drop all stashes in %s" ref))
  217. (list ref)))
  218. (magit-run-git "update-ref" "-d" ref))
  219. ;;;###autoload
  220. (defun magit-stash-branch (stash branch)
  221. "Create and checkout a new BRANCH from STASH."
  222. (interactive (list (magit-read-stash "Branch stash")
  223. (magit-read-string-ns "Branch name")))
  224. (magit-run-git "stash" "branch" branch stash))
  225. ;;;###autoload
  226. (defun magit-stash-branch-here (stash branch)
  227. "Create and checkout a new BRANCH and apply STASH.
  228. The branch is created using `magit-branch', using the current
  229. branch or `HEAD' as the string-point."
  230. (interactive (list (magit-read-stash "Branch stash")
  231. (magit-read-string-ns "Branch name")))
  232. (let ((inhibit-magit-refresh t))
  233. (magit-branch-create branch (or (magit-get-current-branch) "HEAD")))
  234. (magit-stash-apply stash))
  235. ;;;###autoload
  236. (defun magit-stash-format-patch (stash)
  237. "Create a patch from STASH"
  238. (interactive (list (magit-read-stash "Create patch from stash")))
  239. (with-temp-file (magit-rev-format "0001-%f.patch" stash)
  240. (magit-git-insert "stash" "show" "-p" stash))
  241. (magit-refresh))
  242. ;;; Plumbing
  243. (defun magit-stash-save (message index worktree untracked
  244. &optional refresh keep noerror ref)
  245. (if (or (and index (magit-staged-files t))
  246. (and worktree (magit-unstaged-files t))
  247. (and untracked (magit-untracked-files (eq untracked 'all))))
  248. (magit-with-toplevel
  249. (magit-stash-store message (or ref "refs/stash")
  250. (magit-stash-create message index worktree untracked))
  251. (if (eq keep 'worktree)
  252. (with-temp-buffer
  253. (magit-git-insert "diff" "--cached")
  254. (magit-run-git-with-input
  255. "apply" "--reverse" "--cached" "--ignore-space-change" "-")
  256. (magit-run-git-with-input
  257. "apply" "--reverse" "--ignore-space-change" "-"))
  258. (unless (eq keep t)
  259. (if (eq keep 'index)
  260. (magit-call-git "checkout" "--" ".")
  261. (magit-call-git "reset" "--hard" "HEAD" "--"))
  262. (when untracked
  263. (magit-call-git "clean" "--force" "-d"
  264. (and (eq untracked 'all) "-x")))))
  265. (when refresh
  266. (magit-refresh)))
  267. (unless noerror
  268. (user-error "No %s changes to save" (cond ((not index) "unstaged")
  269. ((not worktree) "staged")
  270. (t "local"))))))
  271. (defun magit-stash-store (message ref commit)
  272. (magit-update-ref ref message commit t))
  273. (defun magit-stash-create (message index worktree untracked)
  274. (unless (magit-rev-parse "--verify" "HEAD")
  275. (error "You do not have the initial commit yet"))
  276. (let ((magit-git-global-arguments (nconc (list "-c" "commit.gpgsign=false")
  277. magit-git-global-arguments))
  278. (default-directory (magit-toplevel))
  279. (summary (magit-stash-summary))
  280. (head "HEAD"))
  281. (when (and worktree (not index))
  282. (setq head (or (magit-commit-tree "pre-stash index" nil "HEAD")
  283. (error "Cannot save the current index state"))))
  284. (or (setq index (magit-commit-tree (concat "index on " summary) nil head))
  285. (error "Cannot save the current index state"))
  286. (and untracked
  287. (setq untracked (magit-untracked-files (eq untracked 'all)))
  288. (setq untracked (magit-with-temp-index nil nil
  289. (or (and (magit-update-files untracked)
  290. (magit-commit-tree
  291. (concat "untracked files on " summary)))
  292. (error "Cannot save the untracked files")))))
  293. (magit-with-temp-index index "-m"
  294. (when worktree
  295. (or (magit-update-files (magit-git-items "diff" "-z" "--name-only" head))
  296. (error "Cannot save the current worktree state")))
  297. (or (magit-commit-tree message nil head index untracked)
  298. (error "Cannot save the current worktree state")))))
  299. (defun magit-stash-summary ()
  300. (concat (or (magit-get-current-branch) "(no branch)")
  301. ": " (magit-rev-format "%h %s")))
  302. ;;; Sections
  303. (defvar magit-stashes-section-map
  304. (let ((map (make-sparse-keymap)))
  305. (define-key map [remap magit-delete-thing] 'magit-stash-clear)
  306. map)
  307. "Keymap for `stashes' section.")
  308. (defvar magit-stash-section-map
  309. (let ((map (make-sparse-keymap)))
  310. (define-key map [remap magit-visit-thing] 'magit-stash-show)
  311. (define-key map [remap magit-delete-thing] 'magit-stash-drop)
  312. (define-key map "a" 'magit-stash-apply)
  313. (define-key map "A" 'magit-stash-pop)
  314. map)
  315. "Keymap for `stash' sections.")
  316. (magit-define-section-jumper magit-jump-to-stashes
  317. "Stashes" stashes "refs/stash")
  318. (cl-defun magit-insert-stashes (&optional (ref "refs/stash")
  319. (heading "Stashes:"))
  320. "Insert `stashes' section showing reflog for \"refs/stash\".
  321. If optional REF is non-nil, show reflog for that instead.
  322. If optional HEADING is non-nil, use that as section heading
  323. instead of \"Stashes:\"."
  324. (let ((verified (magit-rev-verify ref))
  325. (autostash
  326. (and (magit-rebase-in-progress-p)
  327. (magit-file-line
  328. (magit-git-dir
  329. (-> (if (file-directory-p (magit-git-dir "rebase-merge"))
  330. "rebase-merge/autostash"
  331. "rebase-apply/autostash")))))))
  332. (when (or autostash verified)
  333. (magit-insert-section (stashes ref)
  334. (magit-insert-heading heading)
  335. (when autostash
  336. (pcase-let ((`(,author ,date ,msg)
  337. (split-string
  338. (car (magit-git-lines
  339. "show" "-q" "--format=%aN%x00%at%x00%s"
  340. autostash))
  341. "\0")))
  342. (magit-insert-section (stash autostash)
  343. (insert (propertize "AUTOSTASH" 'font-lock-face 'magit-hash))
  344. (insert " " msg "\n")
  345. (save-excursion
  346. (backward-char)
  347. (magit-log-format-margin autostash author date)))))
  348. (if verified
  349. (magit-git-wash (apply-partially 'magit-log-wash-log 'stash)
  350. "reflog" "--format=%gd%x00%aN%x00%at%x00%gs" ref)
  351. (insert ?\n)
  352. (save-excursion
  353. (backward-char)
  354. (magit-make-margin-overlay)))))))
  355. ;;; List Stashes
  356. ;;;###autoload
  357. (defun magit-stash-list ()
  358. "List all stashes in a buffer."
  359. (interactive)
  360. (magit-stashes-setup-buffer))
  361. (define-derived-mode magit-stashes-mode magit-reflog-mode "Magit Stashes"
  362. "Mode for looking at lists of stashes."
  363. :group 'magit-log
  364. (hack-dir-local-variables-non-file-buffer))
  365. (defun magit-stashes-setup-buffer ()
  366. (magit-setup-buffer #'magit-stashes-mode nil
  367. (magit-buffer-refname "refs/stash")))
  368. (defun magit-stashes-refresh-buffer ()
  369. (magit-insert-section (stashesbuf)
  370. (magit-insert-heading (if (equal magit-buffer-refname "refs/stash")
  371. "Stashes:"
  372. (format "Stashes [%s]:" magit-buffer-refname)))
  373. (magit-git-wash (apply-partially 'magit-log-wash-log 'stash)
  374. "reflog" "--format=%gd%x00%aN%x00%at%x00%gs" magit-buffer-refname)))
  375. (cl-defmethod magit-buffer-value (&context (major-mode magit-stashes-mode))
  376. magit-buffer-refname)
  377. ;;; Show Stash
  378. ;;;###autoload
  379. (defun magit-stash-show (stash &optional args files)
  380. "Show all diffs of a stash in a buffer."
  381. (interactive (cons (or (and (not current-prefix-arg)
  382. (magit-stash-at-point))
  383. (magit-read-stash "Show stash"))
  384. (pcase-let ((`(,args ,files)
  385. (magit-diff-arguments 'magit-stash-mode)))
  386. (list (delete "--stat" args) files))))
  387. (magit-stash-setup-buffer stash args files))
  388. (define-derived-mode magit-stash-mode magit-diff-mode "Magit Stash"
  389. "Mode for looking at individual stashes."
  390. :group 'magit-diff
  391. (hack-dir-local-variables-non-file-buffer))
  392. (defun magit-stash-setup-buffer (stash args files)
  393. (magit-setup-buffer #'magit-stash-mode nil
  394. (magit-buffer-revision stash)
  395. (magit-buffer-range (format "%s^..%s" stash stash))
  396. (magit-buffer-diff-args args)
  397. (magit-buffer-diff-files files)))
  398. (defun magit-stash-refresh-buffer ()
  399. (magit-set-header-line-format
  400. (concat (capitalize magit-buffer-revision) " "
  401. (propertize (magit-rev-format "%s" magit-buffer-revision)
  402. 'font-lock-face
  403. (list :weight 'normal :foreground
  404. (face-attribute 'default :foreground)))))
  405. (setq magit-buffer-revision-hash (magit-rev-parse magit-buffer-revision))
  406. (magit-insert-section (stash)
  407. (magit-run-section-hook 'magit-stash-sections-hook)))
  408. (cl-defmethod magit-buffer-value (&context (major-mode magit-stash-mode))
  409. magit-buffer-revision)
  410. (defun magit-stash-insert-section (commit range message &optional files)
  411. (magit-insert-section (commit commit)
  412. (magit-insert-heading message)
  413. (magit--insert-diff "diff" range "-p" "--no-prefix" magit-buffer-diff-args
  414. "--" (or files magit-buffer-diff-files))))
  415. (defun magit-insert-stash-notes ()
  416. "Insert section showing notes for a stash.
  417. This shows the notes for stash@{N} but not for the other commits
  418. that make up the stash."
  419. (magit-insert-section section (note)
  420. (magit-insert-heading "Notes")
  421. (magit-git-insert "notes" "show" magit-buffer-revision)
  422. (if (= (point)
  423. (oref section content))
  424. (magit-cancel-section)
  425. (insert "\n"))))
  426. (defun magit-insert-stash-index ()
  427. "Insert section showing staged changes of the stash."
  428. (magit-stash-insert-section
  429. (format "%s^2" magit-buffer-revision)
  430. (format "%s^..%s^2" magit-buffer-revision magit-buffer-revision)
  431. "Staged"))
  432. (defun magit-insert-stash-worktree ()
  433. "Insert section showing unstaged changes of the stash."
  434. (magit-stash-insert-section
  435. magit-buffer-revision
  436. (format "%s^2..%s" magit-buffer-revision magit-buffer-revision)
  437. "Unstaged"))
  438. (defun magit-insert-stash-untracked ()
  439. "Insert section showing the untracked files commit of the stash."
  440. (let ((stash magit-buffer-revision)
  441. (rev (concat magit-buffer-revision "^3")))
  442. (when (magit-rev-verify rev)
  443. (magit-stash-insert-section (format "%s^3" stash)
  444. (format "%s^..%s^3" stash stash)
  445. "Untracked files"
  446. (magit-git-items "ls-tree" "-z" "--name-only"
  447. "-r" "--full-tree" rev)))))
  448. ;;; _
  449. (provide 'magit-stash)
  450. ;;; magit-stash.el ends here