mirror of https://github.com/Chizi123/.emacs.d.git

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