Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

649 lines
26 KiB

4 years ago
  1. ;;; magit-extras.el --- additional functionality 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. ;; Magit is free software; you can redistribute it and/or modify it
  7. ;; under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation; either version 3, or (at your option)
  9. ;; any later version.
  10. ;;
  11. ;; Magit is distributed in the hope that it will be useful, but WITHOUT
  12. ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  13. ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  14. ;; License for more details.
  15. ;;
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with Magit. If not, see http://www.gnu.org/licenses.
  18. ;;; Commentary:
  19. ;; Additional functionality for Magit.
  20. ;;; Code:
  21. (eval-when-compile
  22. (require 'subr-x))
  23. (require 'magit)
  24. (declare-function dired-read-shell-command "dired-aux" (prompt arg files))
  25. (defvar ido-exit)
  26. (defvar ido-fallback)
  27. (defgroup magit-extras nil
  28. "Additional functionality for Magit."
  29. :group 'magit-extensions)
  30. ;;; External Tools
  31. (defcustom magit-gitk-executable
  32. (or (and (eq system-type 'windows-nt)
  33. (let ((exe (magit-git-string
  34. "-c" "alias.X=!x() { which \"$1\" | cygpath -mf -; }; x"
  35. "X" "gitk.exe")))
  36. (and exe (file-executable-p exe) exe)))
  37. (executable-find "gitk") "gitk")
  38. "The Gitk executable."
  39. :group 'magit-extras
  40. :set-after '(magit-git-executable)
  41. :type 'string)
  42. ;;;###autoload
  43. (defun magit-run-git-gui ()
  44. "Run `git gui' for the current git repository."
  45. (interactive)
  46. (magit-with-toplevel
  47. (magit-process-file magit-git-executable nil 0 nil "gui")))
  48. ;;;###autoload
  49. (defun magit-run-git-gui-blame (commit filename &optional linenum)
  50. "Run `git gui blame' on the given FILENAME and COMMIT.
  51. Interactively run it for the current file and the `HEAD', with a
  52. prefix or when the current file cannot be determined let the user
  53. choose. When the current buffer is visiting FILENAME instruct
  54. blame to center around the line point is on."
  55. (interactive
  56. (let (revision filename)
  57. (when (or current-prefix-arg
  58. (not (setq revision "HEAD"
  59. filename (magit-file-relative-name nil 'tracked))))
  60. (setq revision (magit-read-branch-or-commit "Blame from revision"))
  61. (setq filename (magit-read-file-from-rev revision "Blame file")))
  62. (list revision filename
  63. (and (equal filename
  64. (ignore-errors
  65. (magit-file-relative-name buffer-file-name)))
  66. (line-number-at-pos)))))
  67. (magit-with-toplevel
  68. (apply #'magit-process-file magit-git-executable nil 0 nil "gui" "blame"
  69. `(,@(and linenum (list (format "--line=%d" linenum)))
  70. ,commit
  71. ,filename))))
  72. ;;;###autoload
  73. (defun magit-run-gitk ()
  74. "Run `gitk' in the current repository."
  75. (interactive)
  76. (magit-process-file magit-gitk-executable nil 0))
  77. ;;;###autoload
  78. (defun magit-run-gitk-branches ()
  79. "Run `gitk --branches' in the current repository."
  80. (interactive)
  81. (magit-process-file magit-gitk-executable nil 0 nil "--branches"))
  82. ;;;###autoload
  83. (defun magit-run-gitk-all ()
  84. "Run `gitk --all' in the current repository."
  85. (interactive)
  86. (magit-process-file magit-gitk-executable nil 0 nil "--all"))
  87. ;;; Emacs Tools
  88. ;;;###autoload
  89. (defun ido-enter-magit-status ()
  90. "Drop into `magit-status' from file switching.
  91. This command does not work in Emacs 26.1.
  92. See https://github.com/magit/magit/issues/3634
  93. and https://debbugs.gnu.org/cgi/bugreport.cgi?bug=31707.
  94. To make this command available use something like:
  95. (add-hook \\='ido-setup-hook
  96. (lambda ()
  97. (define-key ido-completion-map
  98. (kbd \"C-x g\") \\='ido-enter-magit-status)))
  99. Starting with Emacs 25.1 the Ido keymaps are defined just once
  100. instead of every time Ido is invoked, so now you can modify it
  101. like pretty much every other keymap:
  102. (define-key ido-common-completion-map
  103. (kbd \"C-x g\") \\='ido-enter-magit-status)"
  104. (interactive)
  105. (setq ido-exit 'fallback)
  106. (setq ido-fallback 'magit-status) ; for Emacs >= 26.2
  107. (with-no-warnings (setq fallback 'magit-status)) ; for Emacs 25
  108. (exit-minibuffer))
  109. ;;;###autoload
  110. (defun magit-dired-jump (&optional other-window)
  111. "Visit file at point using Dired.
  112. With a prefix argument, visit in another window. If there
  113. is no file at point, then instead visit `default-directory'."
  114. (interactive "P")
  115. (dired-jump other-window
  116. (when-let ((file (magit-file-at-point)))
  117. (expand-file-name (if (file-directory-p file)
  118. (file-name-as-directory file)
  119. file)))))
  120. ;;;###autoload
  121. (defun magit-dired-log (&optional follow)
  122. "Show log for all marked files, or the current file."
  123. (interactive "P")
  124. (if-let ((topdir (magit-toplevel default-directory)))
  125. (let ((args (car (magit-log-arguments)))
  126. (files (dired-get-marked-files nil nil #'magit-file-tracked-p)))
  127. (unless files
  128. (user-error "No marked file is being tracked by Git"))
  129. (when (and follow
  130. (not (member "--follow" args))
  131. (not (cdr files)))
  132. (push "--follow" args))
  133. (magit-log-setup-buffer
  134. (list (or (magit-get-current-branch) "HEAD"))
  135. args
  136. (let ((default-directory topdir))
  137. (mapcar #'file-relative-name files))
  138. magit-log-buffer-file-locked))
  139. (magit--not-inside-repository-error)))
  140. ;;;###autoload
  141. (defun magit-do-async-shell-command (file)
  142. "Open FILE with `dired-do-async-shell-command'.
  143. Interactively, open the file at point."
  144. (interactive (list (or (magit-file-at-point)
  145. (completing-read "Act on file: "
  146. (magit-list-files)))))
  147. (require 'dired-aux)
  148. (dired-do-async-shell-command
  149. (dired-read-shell-command "& on %s: " current-prefix-arg (list file))
  150. nil (list file)))
  151. ;;; Shift Selection
  152. (defun magit--turn-on-shift-select-mode-p ()
  153. (and shift-select-mode
  154. this-command-keys-shift-translated
  155. (not mark-active)
  156. (not (eq (car-safe transient-mark-mode) 'only))))
  157. ;;;###autoload
  158. (defun magit-previous-line (&optional arg try-vscroll)
  159. "Like `previous-line' but with Magit-specific shift-selection.
  160. Magit's selection mechanism is based on the region but selects an
  161. area that is larger than the region. This causes `previous-line'
  162. when invoked while holding the shift key to move up one line and
  163. thereby select two lines. When invoked inside a hunk body this
  164. command does not move point on the first invocation and thereby
  165. it only selects a single line. Which inconsistency you prefer
  166. is a matter of preference."
  167. (declare (interactive-only
  168. "use `forward-line' with negative argument instead."))
  169. (interactive "p\np")
  170. (unless arg (setq arg 1))
  171. (let ((stay (or (magit-diff-inside-hunk-body-p)
  172. (magit-section-position-in-heading-p))))
  173. (if (and stay (= arg 1) (magit--turn-on-shift-select-mode-p))
  174. (push-mark nil nil t)
  175. (with-no-warnings
  176. (handle-shift-selection)
  177. (previous-line (if stay (max (1- arg) 1) arg) try-vscroll)))))
  178. ;;;###autoload
  179. (defun magit-next-line (&optional arg try-vscroll)
  180. "Like `next-line' but with Magit-specific shift-selection.
  181. Magit's selection mechanism is based on the region but selects
  182. an area that is larger than the region. This causes `next-line'
  183. when invoked while holding the shift key to move down one line
  184. and thereby select two lines. When invoked inside a hunk body
  185. this command does not move point on the first invocation and
  186. thereby it only selects a single line. Which inconsistency you
  187. prefer is a matter of preference."
  188. (declare (interactive-only forward-line))
  189. (interactive "p\np")
  190. (unless arg (setq arg 1))
  191. (let ((stay (or (magit-diff-inside-hunk-body-p)
  192. (magit-section-position-in-heading-p))))
  193. (if (and stay (= arg 1) (magit--turn-on-shift-select-mode-p))
  194. (push-mark nil nil t)
  195. (with-no-warnings
  196. (handle-shift-selection)
  197. (next-line (if stay (max (1- arg) 1) arg) try-vscroll)))))
  198. ;;; Clean
  199. ;;;###autoload
  200. (defun magit-clean (&optional arg)
  201. "Remove untracked files from the working tree.
  202. With a prefix argument also remove ignored files,
  203. with two prefix arguments remove ignored files only.
  204. \n(git clean -f -d [-x|-X])"
  205. (interactive "p")
  206. (when (yes-or-no-p (format "Remove %s files? "
  207. (pcase arg
  208. (1 "untracked")
  209. (4 "untracked and ignored")
  210. (_ "ignored"))))
  211. (magit-wip-commit-before-change)
  212. (magit-run-git "clean" "-f" "-d" (pcase arg (4 "-x") (16 "-X")))))
  213. (put 'magit-clean 'disabled t)
  214. ;;; ChangeLog
  215. ;;;###autoload
  216. (defun magit-add-change-log-entry (&optional whoami file-name other-window)
  217. "Find change log file and add date entry and item for current change.
  218. This differs from `add-change-log-entry' (which see) in that
  219. it acts on the current hunk in a Magit buffer instead of on
  220. a position in a file-visiting buffer."
  221. (interactive (list current-prefix-arg
  222. (prompt-for-change-log-name)))
  223. (pcase-let ((`(,buf ,pos) (magit-diff-visit-file--noselect)))
  224. (magit--with-temp-position buf pos
  225. (add-change-log-entry whoami file-name other-window))))
  226. ;;;###autoload
  227. (defun magit-add-change-log-entry-other-window (&optional whoami file-name)
  228. "Find change log file in other window and add entry and item.
  229. This differs from `add-change-log-entry-other-window' (which see)
  230. in that it acts on the current hunk in a Magit buffer instead of
  231. on a position in a file-visiting buffer."
  232. (interactive (and current-prefix-arg
  233. (list current-prefix-arg
  234. (prompt-for-change-log-name))))
  235. (magit-add-change-log-entry whoami file-name t))
  236. ;;; Edit Line Commit
  237. ;;;###autoload
  238. (defun magit-edit-line-commit (&optional type)
  239. "Edit the commit that added the current line.
  240. With a prefix argument edit the commit that removes the line,
  241. if any. The commit is determined using `git blame' and made
  242. editable using `git rebase --interactive' if it is reachable
  243. from `HEAD', or by checking out the commit (or a branch that
  244. points at it) otherwise."
  245. (interactive (list (and current-prefix-arg 'removal)))
  246. (let* ((chunk (magit-current-blame-chunk (or type 'addition)))
  247. (rev (oref chunk orig-rev)))
  248. (if (equal rev "0000000000000000000000000000000000000000")
  249. (message "This line has not been committed yet")
  250. (let ((rebase (magit-rev-ancestor-p rev "HEAD"))
  251. (file (expand-file-name (oref chunk orig-file)
  252. (magit-toplevel))))
  253. (if rebase
  254. (let ((magit--rebase-published-symbol 'edit-published))
  255. (magit-rebase-edit-commit rev (magit-rebase-arguments)))
  256. (magit-checkout (or (magit-rev-branch rev) rev)))
  257. (unless (and buffer-file-name
  258. (file-equal-p file buffer-file-name))
  259. (let ((blame-type (and magit-blame-mode magit-blame-type)))
  260. (if rebase
  261. (set-process-sentinel
  262. magit-this-process
  263. (lambda (process event)
  264. (magit-sequencer-process-sentinel process event)
  265. (when (eq (process-status process) 'exit)
  266. (find-file file)
  267. (when blame-type
  268. (magit-blame--pre-blame-setup blame-type)
  269. (magit-blame--run)))))
  270. (find-file file)
  271. (when blame-type
  272. (magit-blame--pre-blame-setup blame-type)
  273. (magit-blame--run)))))))))
  274. (put 'magit-edit-line-commit 'disabled t)
  275. ;;;###autoload
  276. (defun magit-diff-edit-hunk-commit (file)
  277. "From a hunk, edit the respective commit and visit the file.
  278. First visit the file being modified by the hunk at the correct
  279. location using `magit-diff-visit-file'. This actually visits a
  280. blob. When point is on a diff header, not within an individual
  281. hunk, then this visits the blob the first hunk is about.
  282. Then invoke `magit-edit-line-commit', which uses an interactive
  283. rebase to make the commit editable, or if that is not possible
  284. because the commit is not reachable from `HEAD' by checking out
  285. that commit directly. This also causes the actual worktree file
  286. to be visited.
  287. Neither the blob nor the file buffer are killed when finishing
  288. the rebase. If that is undesirable, then it might be better to
  289. use `magit-rebase-edit-command' instead of this command."
  290. (interactive (list (magit-file-at-point t t)))
  291. (let ((magit-diff-visit-previous-blob nil))
  292. (with-current-buffer
  293. (magit-diff-visit-file--internal file nil #'pop-to-buffer-same-window)
  294. (magit-edit-line-commit))))
  295. (put 'magit-diff-edit-hunk-commit 'disabled t)
  296. ;;; Reshelve
  297. ;;;###autoload
  298. (defun magit-reshelve-since (rev)
  299. "Change the author and committer dates of the commits since REV.
  300. Ask the user for the first reachable commit whose dates should
  301. be changed. The read the new date for that commit. The initial
  302. minibuffer input and the previous history element offer good
  303. values. The next commit will be created one minute later and so
  304. on.
  305. This command is only intended for interactive use and should only
  306. be used on highly rearranged and unpublished history."
  307. (interactive (list nil))
  308. (cond
  309. ((not rev)
  310. (let ((backup (concat "refs/original/refs/heads/"
  311. (magit-get-current-branch))))
  312. (when (and (magit-ref-p backup)
  313. (not (magit-y-or-n-p
  314. "Backup ref %s already exists. Override? " backup)))
  315. (user-error "Abort")))
  316. (magit-log-select 'magit-reshelve-since
  317. "Type %p on a commit to reshelve it and the commits above it,"))
  318. (t
  319. (cl-flet ((adjust (time offset)
  320. (format-time-string
  321. "%F %T %z"
  322. (+ (floor time)
  323. (* offset 60)
  324. (- (car (decode-time time)))))))
  325. (let* ((start (concat rev "^"))
  326. (range (concat start ".." (magit-get-current-branch)))
  327. (time-rev (adjust (float-time (string-to-number
  328. (magit-rev-format "%at" start)))
  329. 1))
  330. (time-now (adjust (float-time)
  331. (- (string-to-number
  332. (magit-git-string "rev-list" "--count"
  333. range))))))
  334. (push time-rev magit--reshelve-history)
  335. (let ((date (floor
  336. (float-time
  337. (date-to-time
  338. (read-string "Date for first commit: "
  339. time-now 'magit--reshelve-history))))))
  340. (magit-with-toplevel
  341. (magit-run-git-async
  342. "filter-branch" "--force" "--env-filter"
  343. (format "case $GIT_COMMIT in %s\nesac"
  344. (mapconcat (lambda (rev)
  345. (prog1 (format "%s) \
  346. export GIT_AUTHOR_DATE=\"%s\"; \
  347. export GIT_COMMITTER_DATE=\"%s\";;" rev date date)
  348. (cl-incf date 60)))
  349. (magit-git-lines "rev-list" "--reverse"
  350. range)
  351. " "))
  352. range "--")
  353. (set-process-sentinel
  354. magit-this-process
  355. (lambda (process event)
  356. (when (memq (process-status process) '(exit signal))
  357. (if (> (process-exit-status process) 0)
  358. (magit-process-sentinel process event)
  359. (process-put process 'inhibit-refresh t)
  360. (magit-process-sentinel process event)
  361. (magit-run-git "update-ref" "-d"
  362. (concat "refs/original/refs/heads/"
  363. (magit-get-current-branch))))))))))))))
  364. ;;; Revision Stack
  365. (defvar magit-revision-stack nil)
  366. (defcustom magit-pop-revision-stack-format
  367. '("[%N: %h] " "%N: %H\n %s\n" "\\[\\([0-9]+\\)[]:]")
  368. "Control how `magit-pop-revision-stack' inserts a revision.
  369. The command `magit-pop-revision-stack' inserts a representation
  370. of the revision last pushed to the `magit-revision-stack' into
  371. the current buffer. It inserts text at point and/or near the end
  372. of the buffer, and removes the consumed revision from the stack.
  373. The entries on the stack have the format (HASH TOPLEVEL) and this
  374. option has the format (POINT-FORMAT EOB-FORMAT INDEX-REGEXP), all
  375. of which may be nil or a string (though either one of EOB-FORMAT
  376. or POINT-FORMAT should be a string, and if INDEX-REGEXP is
  377. non-nil, then the two formats should be too).
  378. First INDEX-REGEXP is used to find the previously inserted entry,
  379. by searching backward from point. The first submatch must match
  380. the index number. That number is incremented by one, and becomes
  381. the index number of the entry to be inserted. If you don't want
  382. to number the inserted revisions, then use nil for INDEX-REGEXP.
  383. If INDEX-REGEXP is non-nil, then both POINT-FORMAT and EOB-FORMAT
  384. should contain \"%N\", which is replaced with the number that was
  385. determined in the previous step.
  386. Both formats, if non-nil and after removing %N, are then expanded
  387. using `git show --format=FORMAT ...' inside TOPLEVEL.
  388. The expansion of POINT-FORMAT is inserted at point, and the
  389. expansion of EOB-FORMAT is inserted at the end of the buffer (if
  390. the buffer ends with a comment, then it is inserted right before
  391. that)."
  392. :package-version '(magit . "2.3.0")
  393. :group 'magit-commands
  394. :type '(list (choice (string :tag "Insert at point format")
  395. (cons (string :tag "Insert at point format")
  396. (repeat (string :tag "Argument to git show")))
  397. (const :tag "Don't insert at point" nil))
  398. (choice (string :tag "Insert at eob format")
  399. (cons (string :tag "Insert at eob format")
  400. (repeat (string :tag "Argument to git show")))
  401. (const :tag "Don't insert at eob" nil))
  402. (choice (regexp :tag "Find index regexp")
  403. (const :tag "Don't number entries" nil))))
  404. ;;;###autoload
  405. (defun magit-pop-revision-stack (rev toplevel)
  406. "Insert a representation of a revision into the current buffer.
  407. Pop a revision from the `magit-revision-stack' and insert it into
  408. the current buffer according to `magit-pop-revision-stack-format'.
  409. Revisions can be put on the stack using `magit-copy-section-value'
  410. and `magit-copy-buffer-revision'.
  411. If the stack is empty or with a prefix argument, instead read a
  412. revision in the minibuffer. By using the minibuffer history this
  413. allows selecting an item which was popped earlier or to insert an
  414. arbitrary reference or revision without first pushing it onto the
  415. stack.
  416. When reading the revision from the minibuffer, then it might not
  417. be possible to guess the correct repository. When this command
  418. is called inside a repository (e.g. while composing a commit
  419. message), then that repository is used. Otherwise (e.g. while
  420. composing an email) then the repository recorded for the top
  421. element of the stack is used (even though we insert another
  422. revision). If not called inside a repository and with an empty
  423. stack, or with two prefix arguments, then read the repository in
  424. the minibuffer too."
  425. (interactive
  426. (if (or current-prefix-arg (not magit-revision-stack))
  427. (let ((default-directory
  428. (or (and (not (= (prefix-numeric-value current-prefix-arg) 16))
  429. (or (magit-toplevel)
  430. (cadr (car magit-revision-stack))))
  431. (magit-read-repository))))
  432. (list (magit-read-branch-or-commit "Insert revision")
  433. default-directory))
  434. (push (caar magit-revision-stack) magit-revision-history)
  435. (pop magit-revision-stack)))
  436. (if rev
  437. (pcase-let ((`(,pnt-format ,eob-format ,idx-format)
  438. magit-pop-revision-stack-format))
  439. (let ((default-directory toplevel)
  440. (idx (and idx-format
  441. (save-excursion
  442. (if (re-search-backward idx-format nil t)
  443. (number-to-string
  444. (1+ (string-to-number (match-string 1))))
  445. "1"))))
  446. pnt-args eob-args)
  447. (when (listp pnt-format)
  448. (setq pnt-args (cdr pnt-format))
  449. (setq pnt-format (car pnt-format)))
  450. (when (listp eob-format)
  451. (setq eob-args (cdr eob-format))
  452. (setq eob-format (car eob-format)))
  453. (when pnt-format
  454. (when idx-format
  455. (setq pnt-format
  456. (replace-regexp-in-string "%N" idx pnt-format t t)))
  457. (magit-rev-insert-format pnt-format rev pnt-args)
  458. (backward-delete-char 1))
  459. (when eob-format
  460. (when idx-format
  461. (setq eob-format
  462. (replace-regexp-in-string "%N" idx eob-format t t)))
  463. (save-excursion
  464. (goto-char (point-max))
  465. (skip-syntax-backward ">s-")
  466. (beginning-of-line)
  467. (if (and comment-start (looking-at comment-start))
  468. (while (looking-at comment-start)
  469. (forward-line -1))
  470. (forward-line)
  471. (unless (= (current-column) 0)
  472. (insert ?\n)))
  473. (insert ?\n)
  474. (magit-rev-insert-format eob-format rev eob-args)
  475. (backward-delete-char 1)))))
  476. (user-error "Revision stack is empty")))
  477. (define-key git-commit-mode-map
  478. (kbd "C-c C-w") 'magit-pop-revision-stack)
  479. ;;;###autoload
  480. (defun magit-copy-section-value ()
  481. "Save the value of the current section for later use.
  482. Save the section value to the `kill-ring', and, provided that
  483. the current section is a commit, branch, or tag section, push
  484. the (referenced) revision to the `magit-revision-stack' for use
  485. with `magit-pop-revision-stack'.
  486. When the current section is a branch or a tag, and a prefix
  487. argument is used, then save the revision at its tip to the
  488. `kill-ring' instead of the reference name.
  489. When the region is active, then save that to the `kill-ring',
  490. like `kill-ring-save' would, instead of behaving as described
  491. above. If a prefix argument is used and the region is within a
  492. hunk, strip the outer diff marker column."
  493. (interactive)
  494. (cond
  495. ((and current-prefix-arg
  496. (magit-section-internal-region-p)
  497. (magit-section-match 'hunk))
  498. (deactivate-mark)
  499. (kill-new (replace-regexp-in-string
  500. "^[ \\+\\-]" ""
  501. (buffer-substring-no-properties
  502. (region-beginning) (region-end)))))
  503. ((use-region-p)
  504. (copy-region-as-kill nil nil 'region))
  505. (t
  506. (when-let ((section (magit-current-section))
  507. (value (oref section value)))
  508. (magit-section-case
  509. ((branch commit module-commit tag)
  510. (let ((default-directory default-directory) ref)
  511. (magit-section-case
  512. ((branch tag)
  513. (setq ref value))
  514. (module-commit
  515. (setq default-directory
  516. (file-name-as-directory
  517. (expand-file-name (magit-section-parent-value section)
  518. (magit-toplevel))))))
  519. (setq value (magit-rev-parse value))
  520. (push (list value default-directory) magit-revision-stack)
  521. (kill-new (message "%s" (or (and current-prefix-arg ref)
  522. value)))))
  523. (t (kill-new (message "%s" value))))))))
  524. ;;;###autoload
  525. (defun magit-copy-buffer-revision ()
  526. "Save the revision of the current buffer for later use.
  527. Save the revision shown in the current buffer to the `kill-ring'
  528. and push it to the `magit-revision-stack'.
  529. This command is mainly intended for use in `magit-revision-mode'
  530. buffers, the only buffers where it is always unambiguous exactly
  531. which revision should be saved.
  532. Most other Magit buffers usually show more than one revision, in
  533. some way or another, so this command has to select one of them,
  534. and that choice might not always be the one you think would have
  535. been the best pick.
  536. In such buffers it is often more useful to save the value of
  537. the current section instead, using `magit-copy-section-value'.
  538. When the region is active, then save that to the `kill-ring',
  539. like `kill-ring-save' would, instead of behaving as described
  540. above."
  541. (interactive)
  542. (if (use-region-p)
  543. (copy-region-as-kill nil nil 'region)
  544. (when-let ((rev (or magit-buffer-revision
  545. (cl-case major-mode
  546. (magit-diff-mode
  547. (if (string-match "\\.\\.\\.?\\(.+\\)"
  548. magit-buffer-range)
  549. (match-string 1 magit-buffer-range)
  550. magit-buffer-range))
  551. (magit-status-mode "HEAD")))))
  552. (when (magit-commit-p rev)
  553. (setq rev (magit-rev-parse rev))
  554. (push (list rev default-directory) magit-revision-stack)
  555. (kill-new (message "%s" rev))))))
  556. ;;; Miscellaneous
  557. ;;;###autoload
  558. (defun magit-abort-dwim ()
  559. "Abort current operation.
  560. Depending on the context, this will abort a merge, a rebase, a
  561. patch application, a cherry-pick, a revert, or a bisect."
  562. (interactive)
  563. (cond ((magit-merge-in-progress-p) (magit-merge-abort))
  564. ((magit-rebase-in-progress-p) (magit-rebase-abort))
  565. ((magit-am-in-progress-p) (magit-am-abort))
  566. ((magit-sequencer-in-progress-p) (magit-sequencer-abort))
  567. ((magit-bisect-in-progress-p) (magit-bisect-reset))))
  568. ;;; _
  569. (provide 'magit-extras)
  570. ;;; magit-extras.el ends here