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

Chizi123
2018-11-17 c4001ccd1864293b64aa37d83a9d9457eb875e70
commit | author | age
5cb5f7 1 ;;; magit-log.el --- inspect Git history  -*- lexical-binding: t -*-
C 2
3 ;; Copyright (C) 2010-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 ;; Author: Jonas Bernoulli <jonas@bernoul.li>
9 ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
10
11 ;; Magit is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 3, or (at your option)
14 ;; any later version.
15 ;;
16 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
17 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
19 ;; License for more details.
20 ;;
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with Magit.  If not, see http://www.gnu.org/licenses.
23
24 ;;; Commentary:
25
26 ;; This library implements support for looking at Git logs, including
27 ;; special logs like reflogs and cherry-logs, as well as for selecting
28 ;; a commit from a log.
29
30 ;;; Code:
31
32 (require 'magit-core)
33 (require 'magit-diff)
34
35 (declare-function magit-blob-visit "magit-files" (blob-or-file line))
36 (declare-function magit-insert-head-branch-header "magit-status"
37                   (&optional branch))
38 (declare-function magit-insert-upstream-branch-header "magit-status"
39                   (&optional branch pull keyword))
40 (declare-function magit-read-file-from-rev "magit-files"
41                   (rev prompt &optional default))
42 (declare-function magit-show-commit "magit-diff"
43                   (arg1 &optional arg2 arg3 arg4))
44 (defvar magit-refs-focus-column-width)
45 (defvar magit-refs-margin)
46 (defvar magit-refs-show-commit-count)
47 (defvar magit-buffer-margin)
48 (defvar magit-status-margin)
49 (defvar magit-status-sections-hook)
50
51 (require 'ansi-color)
52 (require 'crm)
53 (require 'which-func)
54
55 (eval-when-compile
56   (require 'subr-x))
57
58 (defvar bookmark-make-record-function)
59
60 ;;; Options
61 ;;;; Log Mode
62
63 (defgroup magit-log nil
64   "Inspect and manipulate Git history."
65   :link '(info-link "(magit)Logging")
66   :group 'magit-modes)
67
68 (defcustom magit-log-mode-hook nil
69   "Hook run after entering Magit-Log mode."
70   :group 'magit-log
71   :type 'hook)
72
73 (defcustom magit-log-arguments '("-n256" "--graph" "--decorate")
74   "The log arguments used in `magit-log-mode' buffers."
75   :package-version '(magit . "2.3.0")
76   :group 'magit-git-arguments
77   :group 'magit-log
78   :type '(repeat (string :tag "Argument")))
79
80 (defcustom magit-log-remove-graph-args '("--follow" "--grep" "-G" "-S" "-L")
81   "The log arguments that cause the `--graph' argument to be dropped."
82   :package-version '(magit . "2.3.0")
83   :group 'magit-log
84   :type '(repeat (string :tag "Argument"))
85   :options '("--follow" "--grep" "-G" "-S" "-L"))
86
87 (defcustom magit-log-revision-headers-format "\
88 %+b
89 Author:    %aN <%aE>
90 Committer: %cN <%cE>"
91   "Additional format string used with the `++header' argument."
92   :package-version '(magit . "2.3.0")
93   :group 'magit-log
94   :type 'string)
95
96 (defcustom magit-log-auto-more nil
97   "Insert more log entries automatically when moving past the last entry.
98 Only considered when moving past the last entry with
99 `magit-goto-*-section' commands."
100   :group 'magit-log
101   :type 'boolean)
102
103 (defcustom magit-log-margin '(t age magit-log-margin-width t 18)
104   "Format of the margin in `magit-log-mode' buffers.
105
106 The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH).
107
108 If INIT is non-nil, then the margin is shown initially.
109 STYLE controls how to format the committer date.  It can be one
110   of `age' (to show the age of the commit), `age-abbreviated' (to
111   abbreviate the time unit to a character), or a string (suitable
112   for `format-time-string') to show the actual date.
113 WIDTH controls the width of the margin.  This exists for forward
114   compatibility and currently the value should not be changed.
115 AUTHOR controls whether the name of the author is also shown by
116   default.
117 AUTHOR-WIDTH has to be an integer.  When the name of the author
118   is shown, then this specifies how much space is used to do so."
119   :package-version '(magit . "2.9.0")
120   :group 'magit-log
121   :group 'magit-margin
122   :type magit-log-margin--custom-type
123   :initialize 'magit-custom-initialize-reset
124   :set (apply-partially #'magit-margin-set-variable 'magit-log-mode))
125
126 (defcustom magit-log-show-refname-after-summary nil
127   "Whether to show refnames after commit summaries.
128 This is useful if you use really long branch names."
129   :package-version '(magit . "2.2.0")
130   :group 'magit-log
131   :type 'boolean)
132
133 (defcustom magit-log-highlight-keywords t
134   "Whether to highlight bracketed keywords in commit summaries."
135   :package-version '(magit . "2.12.0")
136   :group 'magit-log
137   :type 'boolean)
138
139 (defcustom magit-log-header-line-function 'magit-log-header-line-sentence
140   "Function used to generate text shown in header line of log buffers."
141   :package-version '(magit . "2.12.0")
142   :group 'magit-log
143   :type '(choice (function-item magit-log-header-line-arguments)
144                  (function-item magit-log-header-line-sentence)
145                  function))
146
147 (defcustom magit-log-trace-definition-function 'which-function
148   "Function used to determine the function at point.
149 This is used by the command `magit-log-trace-definition'."
150   :package-version '(magit . "2.90.0")
151   :group 'magit-log
152   :type '(choice (function-item which-function) function))
153
154 (defface magit-log-graph
155   '((((class color) (background light)) :foreground "grey30")
156     (((class color) (background  dark)) :foreground "grey80"))
157   "Face for the graph part of the log output."
158   :group 'magit-faces)
159
160 (defface magit-log-author
161   '((((class color) (background light)) :foreground "firebrick")
162     (((class color) (background  dark)) :foreground "tomato"))
163   "Face for the author part of the log output."
164   :group 'magit-faces)
165
166 (defface magit-log-date
167   '((((class color) (background light)) :foreground "grey30")
168     (((class color) (background  dark)) :foreground "grey80"))
169   "Face for the date part of the log output."
170   :group 'magit-faces)
171
172 (defface magit-header-line-log-select
173   '((t :inherit bold))
174   "Face for the `header-line' in `magit-log-select-mode'."
175   :group 'magit-faces)
176
177 ;;;; File Log
178
179 (defcustom magit-log-buffer-file-locked t
180   "Whether `magit-log-buffer-file' uses a dedicated buffer."
181   :package-version '(magit . "2.7.0")
182   :group 'magit-commands
183   :group 'magit-log
184   :type 'boolean)
185
186 ;;;; Select Mode
187
188 (defcustom magit-log-select-arguments '("-n256" "--graph" "--decorate")
189   "The log arguments used in `magit-log-select-mode' buffers."
190   :package-version '(magit . "2.3.0")
191   :group 'magit-log
192   :type '(repeat (string :tag "Argument")))
193
194 (defcustom magit-log-select-show-usage 'both
195   "Whether to show usage information when selecting a commit from a log.
196 The message can be shown in the `echo-area' or the `header-line', or in
197 `both' places.  If the value isn't one of these symbols, then it should
198 be nil, in which case no usage information is shown."
199   :package-version '(magit . "2.1.0")
200   :group 'magit-log
201   :type '(choice (const :tag "in echo-area" echo-area)
202                  (const :tag "in header-line" header-line)
203                  (const :tag "in both places" both)
204                  (const :tag "nowhere")))
205
206 (defcustom magit-log-select-margin
207   (list (nth 0 magit-log-margin)
208         (nth 1 magit-log-margin)
209         'magit-log-margin-width t
210         (nth 4 magit-log-margin))
211   "Format of the margin in `magit-log-select-mode' buffers.
212
213 The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH).
214
215 If INIT is non-nil, then the margin is shown initially.
216 STYLE controls how to format the committer date.  It can be one
217   of `age' (to show the age of the commit), `age-abbreviated' (to
218   abbreviate the time unit to a character), or a string (suitable
219   for `format-time-string') to show the actual date.
220 WIDTH controls the width of the margin.  This exists for forward
221   compatibility and currently the value should not be changed.
222 AUTHOR controls whether the name of the author is also shown by
223   default.
224 AUTHOR-WIDTH has to be an integer.  When the name of the author
225   is shown, then this specifies how much space is used to do so."
226   :package-version '(magit . "2.9.0")
227   :group 'magit-log
228   :group 'magit-margin
229   :type magit-log-margin--custom-type
230   :initialize 'magit-custom-initialize-reset
231   :set-after '(magit-log-margin)
232   :set (apply-partially #'magit-margin-set-variable 'magit-log-select-mode))
233
234 ;;;; Cherry Mode
235
236 (defcustom magit-cherry-sections-hook
237   '(magit-insert-cherry-headers
238     magit-insert-cherry-commits)
239   "Hook run to insert sections into the cherry buffer."
240   :package-version '(magit . "2.1.0")
241   :group 'magit-log
242   :type 'hook)
243
244 (defcustom magit-cherry-margin
245   (list (nth 0 magit-log-margin)
246         (nth 1 magit-log-margin)
247         'magit-log-margin-width t
248         (nth 4 magit-log-margin))
249   "Format of the margin in `magit-cherry-mode' buffers.
250
251 The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH).
252
253 If INIT is non-nil, then the margin is shown initially.
254 STYLE controls how to format the committer date.  It can be one
255   of `age' (to show the age of the commit), `age-abbreviated' (to
256   abbreviate the time unit to a character), or a string (suitable
257   for `format-time-string') to show the actual date.
258 WIDTH controls the width of the margin.  This exists for forward
259   compatibility and currently the value should not be changed.
260 AUTHOR controls whether the name of the author is also shown by
261   default.
262 AUTHOR-WIDTH has to be an integer.  When the name of the author
263   is shown, then this specifies how much space is used to do so."
264   :package-version '(magit . "2.9.0")
265   :group 'magit-log
266   :group 'magit-margin
267   :type magit-log-margin--custom-type
268   :initialize 'magit-custom-initialize-reset
269   :set-after '(magit-log-margin)
270   :set (apply-partially #'magit-margin-set-variable 'magit-cherry-mode))
271
272 ;;;; Reflog Mode
273
274 (defcustom magit-reflog-arguments '("-n256")
275   "The log arguments used in `magit-reflog-mode' buffers."
276   :package-version '(magit . "2.3.0")
277   :group 'magit-git-arguments
278   :type '(repeat (string :tag "Argument")))
279
280 (defcustom magit-reflog-margin
281   (list (nth 0 magit-log-margin)
282         (nth 1 magit-log-margin)
283         'magit-log-margin-width nil
284         (nth 4 magit-log-margin))
285   "Format of the margin in `magit-reflog-mode' buffers.
286
287 The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH).
288
289 If INIT is non-nil, then the margin is shown initially.
290 STYLE controls how to format the committer date.  It can be one
291   of `age' (to show the age of the commit), `age-abbreviated' (to
292   abbreviate the time unit to a character), or a string (suitable
293   for `format-time-string') to show the actual date.
294 WIDTH controls the width of the margin.  This exists for forward
295   compatibility and currently the value should not be changed.
296 AUTHOR controls whether the name of the author is also shown by
297   default.
298 AUTHOR-WIDTH has to be an integer.  When the name of the author
299   is shown, then this specifies how much space is used to do so."
300   :package-version '(magit . "2.9.0")
301   :group 'magit-log
302   :group 'magit-margin
303   :type magit-log-margin--custom-type
304   :initialize 'magit-custom-initialize-reset
305   :set-after '(magit-log-margin)
306   :set (apply-partially #'magit-margin-set-variable 'magit-reflog-mode))
307
308 (defface magit-reflog-commit '((t :foreground "green"))
309   "Face for commit commands in reflogs."
310   :group 'magit-faces)
311
312 (defface magit-reflog-amend '((t :foreground "magenta"))
313   "Face for amend commands in reflogs."
314   :group 'magit-faces)
315
316 (defface magit-reflog-merge '((t :foreground "green"))
317   "Face for merge, checkout and branch commands in reflogs."
318   :group 'magit-faces)
319
320 (defface magit-reflog-checkout '((t :foreground "blue"))
321   "Face for checkout commands in reflogs."
322   :group 'magit-faces)
323
324 (defface magit-reflog-reset '((t :foreground "red"))
325   "Face for reset commands in reflogs."
326   :group 'magit-faces)
327
328 (defface magit-reflog-rebase '((t :foreground "magenta"))
329   "Face for rebase commands in reflogs."
330   :group 'magit-faces)
331
332 (defface magit-reflog-cherry-pick '((t :foreground "green"))
333   "Face for cherry-pick commands in reflogs."
334   :group 'magit-faces)
335
336 (defface magit-reflog-remote '((t :foreground "cyan"))
337   "Face for pull and clone commands in reflogs."
338   :group 'magit-faces)
339
340 (defface magit-reflog-other '((t :foreground "cyan"))
341   "Face for other commands in reflogs."
342   :group 'magit-faces)
343
344 ;;;; Log Sections
345
346 (defcustom magit-log-section-commit-count 10
347   "How many recent commits to show in certain log sections.
348 How many recent commits `magit-insert-recent-commits' and
349 `magit-insert-unpulled-from-upstream-or-recent' (provided
350 the upstream isn't ahead of the current branch) show."
351   :package-version '(magit . "2.1.0")
352   :group 'magit-status
353   :type 'number)
354
355 (defcustom magit-log-section-arguments '("-n256" "--decorate")
356   "The log arguments used in buffers that show other things besides logs."
357   :package-version '(magit . "2.4.0")
358   :group 'magit-git-arguments
359   :group 'magit-log
360   :group 'magit-status
361   :type '(repeat (string :tag "Argument")))
362
363 ;;; Commands
364 ;;;; Popups
365
366 (defvar magit-log-popup
367   '(:variable magit-log-arguments
368     :man-page "git-log"
369     :switches ((?g "Show graph"              "--graph")
370                (?c "Show graph in color"     "--color")
371                (?d "Show refnames"           "--decorate")
372                (?S "Show signatures"         "--show-signature")
373                (?u "Show diffs"              "--patch")
374                (?s "Show diffstats"          "--stat")
375                (?h "Show header"             "++header" magit-log++header)
376                (?r "Show in reverse order"   "--reverse")
377                (?D "Simplify by decoration"  "--simplify-by-decoration")
378                (?f "Follow renames when showing single-file log" "--follow"))
379     :options  ((?n "Limit number of commits" "-n")
380                (?f "Limit to files"          "-- " magit-read-files)
381                (?a "Limit to author"         "--author=")
382                (?o "Order commits by"        "++order=" magit-log-select-order)
383                (?g "Search messages"         "--grep=")
384                (?G "Search changes"          "-G")
385                (?S "Search occurrences"      "-S")
386                (?L "Trace line evolution"    "-L" magit-read-file-trace))
387     :actions  ((?l "Log current"             magit-log-current)
388                (?L "Log local branches"      magit-log-branches)
389                (?r "Reflog current"          magit-reflog-current)
390                (?o "Log other"               magit-log-other)
391                (?b "Log all branches"        magit-log-all-branches)
392                (?O "Reflog other"            magit-reflog-other)
393                (?h "Log HEAD"                magit-log-head)
394                (?a "Log all references"      magit-log-all)
395                (?H "Reflog HEAD"             magit-reflog-head))
396     :default-action magit-log-current
397     :max-action-columns 3))
398
399 (defvar magit-log-mode-refresh-popup
400   '(:variable magit-log-arguments
401     :man-page "git-log"
402     :switches ((?g "Show graph"              "--graph")
403                (?c "Show graph in color"     "--color")
404                (?d "Show refnames"           "--decorate")
405                (?S "Show signatures"         "--show-signature")
406                (?u "Show diffs"              "--patch")
407                (?s "Show diffstats"          "--stat")
408                (?r "Show in reverse order"   "--reverse")
409                (?D "Simplify by decoration"  "--simplify-by-decoration")
410                (?f "Follow renames when showing single-file log" "--follow"))
411     :options  ((?n "Limit number of commits" "-n")
412                (?f "Limit to files"          "-- " magit-read-files)
413                (?a "Limit to author"         "--author=")
414                (?o "Order commits by"        "++order=" magit-log-select-order)
415                (?g "Search messages"         "--grep=")
416                (?G "Search changes"          "-G")
417                (?S "Search occurrences"      "-S")
418                (?L "Trace line evolution"    "-L" magit-read-file-trace))
419     :actions  ((?g "Refresh"       magit-log-refresh)
420                (?L "Toggle margin" magit-toggle-margin)
421                (?s "Set defaults"  magit-log-set-default-arguments) nil
422                (?w "Save defaults" magit-log-save-default-arguments))
423     :max-action-columns 2))
424
425 (defvar magit-reflog-mode-refresh-popup
426   '(:variable magit-reflog-arguments
427     :man-page "git-reflog"
428     :options  ((?n "Limit number of commits" "-n"))))
429
430 (defvar magit-log-refresh-popup
431   '(:variable magit-log-arguments
432     :man-page "git-log"
433     :switches ((?g "Show graph"          "--graph")
434                (?c "Show graph in color" "--color")
435                (?d "Show refnames"       "--decorate"))
436     :options  ((?n "Limit number of commits" "-n")
437                (?o "Order commits by"        "++order=" magit-log-select-order))
438     :actions  ("Refresh"
439                (?g "buffer"                   magit-log-refresh)
440                (?s "buffer and set defaults"  magit-log-set-default-arguments)
441                (?w "buffer and save defaults" magit-log-save-default-arguments)
442                "Margin"
443                (?L "toggle visibility" magit-toggle-margin)
444                (?l "cycle style"       magit-cycle-margin-style)
445                (?d "toggle details"    magit-toggle-margin-details))
446     :max-action-columns 1))
447
448 (magit-define-popup-keys-deferred 'magit-log-popup)
449 (magit-define-popup-keys-deferred 'magit-log-mode-refresh-popup)
450 (magit-define-popup-keys-deferred 'magit-log-refresh-popup)
451
452 (defun magit-log-select-order (&rest _ignored)
453   "Set one `--<value>-order' option in Git log.
454 This encompasses the options `--author-date-order',
455 `--date-order', and `--topo-order'."
456   (magit-read-char-case "Order commits by " t
457     (?t "[t]opography"     "topo")
458     (?a "[a]uthor date"    "author-date")
459     (?c "[c]ommitter date" "date")))
460
461 ;; This is a dummy procedure used to show help in `magit-log-popup'.
462 (defun magit-log++header ()
463   "Insert a header after each revision summary in Git log.
464 Customize `magit-log-revision-headers-format' to change this
465 header."
466   nil)
467
468 (defun magit-log-get-buffer-args ()
469   (cond ((and magit-use-sticky-arguments
470               (derived-mode-p 'magit-log-mode))
471          (list (nth 1 magit-refresh-args)
472                (nth 2 magit-refresh-args)))
473         ((and (eq magit-use-sticky-arguments t)
474               (--when-let (magit-mode-get-buffer 'magit-log-mode)
475                 (with-current-buffer it
476                   (list (nth 1 magit-refresh-args)
477                         (nth 2 magit-refresh-args))))))
478         (t
479          (list (default-value 'magit-log-arguments) nil))))
480
481 (defun magit-log-arguments (&optional refresh)
482   (cond ((memq magit-current-popup
483                '(magit-log-popup magit-log-refresh-popup))
484          (magit-popup-export-file-args magit-current-popup-args))
485         ((and refresh (not (derived-mode-p 'magit-log-mode)))
486          (list magit-log-section-arguments nil))
487         (t
488          (magit-log-get-buffer-args))))
489
490 (defun magit-log-popup (arg)
491   "Popup console for log commands."
492   (interactive "P")
493   (let ((magit-log-refresh-popup
494          (pcase major-mode
495            (`magit-log-mode magit-log-mode-refresh-popup)
496            (_               magit-log-refresh-popup)))
497         (magit-log-arguments
498          (apply #'magit-popup-import-file-args (magit-log-get-buffer-args))))
499     (magit-invoke-popup 'magit-log-popup nil arg)))
500
501 ;;;###autoload
502 (defun magit-log-buffer-file-popup ()
503   "Popup console for log commands.
504
505 This is a variant of `magit-log-popup' which shows the same popup
506 but which limits the log to the file being visited in the current
507 buffer."
508   (interactive)
509   (if-let ((file (magit-file-relative-name)))
510       (let ((magit-log-arguments
511              (magit-popup-import-file-args
512               (if-let ((buffer (magit-mode-get-buffer 'magit-log-mode)))
513                   (with-current-buffer buffer
514                     (nth 2 magit-refresh-args))
515                 (default-value 'magit-log-arguments))
516               (list file))))
517         (magit-invoke-popup 'magit-log-popup nil nil))
518     (user-error "Buffer isn't visiting a file")))
519
520 (defun magit-log-refresh-popup (arg)
521   "Popup console for changing log arguments in the current buffer."
522   (interactive "P")
523   (magit-log-refresh-assert)
524   (let ((magit-log-refresh-popup
525          (cond ((derived-mode-p 'magit-log-select-mode)
526                 magit-log-refresh-popup)
527                ((derived-mode-p 'magit-log-mode)
528                 (let ((def (copy-sequence magit-log-refresh-popup)))
529                   (plist-put def :switches (plist-get magit-log-popup :switches))
530                   (plist-put def :options  (plist-get magit-log-popup :options))
531                   def))
532                (t
533                 magit-log-refresh-popup)))
534         (magit-log-arguments
535          (cond ((derived-mode-p 'magit-log-select-mode)
536                 (cadr magit-refresh-args))
537                ((derived-mode-p 'magit-log-mode)
538                 (magit-popup-import-file-args (nth 1 magit-refresh-args)
539                                               (nth 2 magit-refresh-args)))
540                (t
541                 magit-log-section-arguments))))
542     (magit-invoke-popup 'magit-log-refresh-popup nil arg)))
543
544 (defun magit-read-file-trace (&rest _ignored)
545   (let ((file  (magit-read-file-from-rev "HEAD" "File"))
546         (trace (magit-read-string "Trace")))
547     (concat trace (or (match-string 2 trace) ":") file)))
548
549 ;;;; Refresh Commands
550
551 (defun magit-log-refresh (args files)
552   "Set the local log arguments for the current buffer."
553   (interactive (magit-log-arguments t))
554   (magit-log-refresh-assert)
555   (cond ((derived-mode-p 'magit-log-select-mode)
556          (setcar (cdr magit-refresh-args) args))
557         ((derived-mode-p 'magit-log-mode)
558          (setcdr magit-refresh-args (list args files)))
559         (t
560          (setq-local magit-log-section-arguments args)))
561   (magit-refresh))
562
563 (defun magit-log-set-default-arguments (args files)
564   "Set the global log arguments for the current buffer."
565   (interactive (magit-log-arguments t))
566   (magit-log-refresh-assert)
567   (cond ((derived-mode-p 'magit-log-select-mode)
568          (customize-set-variable 'magit-log-select-arguments args)
569          (setcar (cdr magit-refresh-args) args))
570         ((derived-mode-p 'magit-log-mode)
571          (customize-set-variable 'magit-log-arguments args)
572          (setcdr magit-refresh-args (list args files)))
573         (t
574          (customize-set-variable 'magit-log-section-arguments args)
575          (kill-local-variable    'magit-log-section-arguments)))
576   (magit-refresh))
577
578 (defun magit-log-save-default-arguments (args files)
579   "Set and save the global log arguments for the current buffer."
580   (interactive (magit-log-arguments t))
581   (magit-log-refresh-assert)
582   (cond ((derived-mode-p 'magit-log-select-mode)
583          (customize-save-variable 'magit-log-select-arguments args)
584          (setcar (cdr magit-refresh-args) args))
585         ((derived-mode-p 'magit-log-mode)
586          (customize-save-variable 'magit-log-arguments args)
587          (setcdr magit-refresh-args (list args files)))
588         (t
589          (customize-save-variable 'magit-log-section-arguments args)
590          (kill-local-variable     'magit-log-section-arguments)))
591   (magit-refresh))
592
593 (defun magit-log-refresh-assert ()
594   (cond ((derived-mode-p 'magit-reflog-mode)
595          (user-error "Cannot change log arguments in reflog buffers"))
596         ((derived-mode-p 'magit-cherry-mode)
597          (user-error "Cannot change log arguments in cherry buffers"))))
598
599 ;;;; Log Commands
600
601 (defvar magit-log-read-revs-map
602   (let ((map (make-sparse-keymap)))
603     (set-keymap-parent map crm-local-completion-map)
604     (define-key map "\s" 'self-insert-command)
605     map))
606
607 (defun magit-log-read-revs (&optional use-current)
608   (or (and use-current (--when-let (magit-get-current-branch) (list it)))
609       (let ((collection `(,@(and (file-exists-p (magit-git-dir "FETCH_HEAD"))
610                                  (list "FETCH_HEAD"))
611                           ,@(magit-list-refnames))))
612         (split-string
613          (magit-completing-read-multiple "Log rev,s" collection
614                                          "\\(\\.\\.\\.?\\|[, ]\\)"
615                                          (or (magit-branch-or-commit-at-point)
616                                              (unless use-current
617                                                (magit-get-previous-branch)))
618                                          'magit-revision-history
619                                          magit-log-read-revs-map)
620          "[, ]" t))))
621
622 (defun magit-git-log (revs args files)
623   (require 'magit)
624   (magit-mode-setup #'magit-log-mode revs args files)
625   (magit-log-goto-same-commit))
626
627 ;;;###autoload
628 (defun magit-log-current (revs &optional args files)
629   "Show log for the current branch.
630 When `HEAD' is detached or with a prefix argument show log for
631 one or more revs read from the minibuffer."
632   (interactive (cons (magit-log-read-revs t)
633                      (magit-log-arguments)))
634   (magit-git-log revs args files))
635
636 ;;;###autoload
637 (defun magit-log-other (revs &optional args files)
638   "Show log for one or more revs read from the minibuffer.
639 The user can input any revision or revisions separated by a
640 space, or even ranges, but only branches and tags, and a
641 representation of the commit at point, are available as
642 completion candidates."
643   (interactive (cons (magit-log-read-revs)
644                      (magit-log-arguments)))
645   (magit-git-log revs args files))
646
647 ;;;###autoload
648 (defun magit-log-head (&optional args files)
649   "Show log for `HEAD'."
650   (interactive (magit-log-arguments))
651   (magit-git-log (list "HEAD") args files))
652
653 ;;;###autoload
654 (defun magit-log-branches (&optional args files)
655   "Show log for all local branches and `HEAD'."
656   (interactive (magit-log-arguments))
657   (magit-git-log (if (magit-get-current-branch)
658                  (list "--branches")
659                (list "HEAD" "--branches"))
660              args files))
661
662 ;;;###autoload
663 (defun magit-log-all-branches (&optional args files)
664   "Show log for all local and remote branches and `HEAD'."
665   (interactive (magit-log-arguments))
666   (magit-git-log (if (magit-get-current-branch)
667                      (list "--branches" "--remotes")
668                    (list "HEAD" "--branches" "--remotes"))
669                  args files))
670
671 ;;;###autoload
672 (defun magit-log-all (&optional args files)
673   "Show log for all references and `HEAD'."
674   (interactive (magit-log-arguments))
675   (magit-git-log (if (magit-get-current-branch)
676                      (list "--all")
677                    (list "HEAD" "--all"))
678                  args files))
679
680 ;;;###autoload
681 (defun magit-log-buffer-file (&optional follow beg end)
682   "Show log for the blob or file visited in the current buffer.
683 With a prefix argument or when `--follow' is part of
684 `magit-log-arguments', then follow renames.  When the region is
685 active, restrict the log to the lines that the region touches."
686   (interactive
687    (cons current-prefix-arg
688          (and (region-active-p)
689               (magit-file-relative-name)
690               (save-restriction
691                 (widen)
692                 (list (line-number-at-pos (region-beginning))
693                       (line-number-at-pos
694                        (let ((end (region-end)))
695                          (if (char-after end)
696                              end
697                            ;; Ensure that we don't get the line number
698                            ;; of a trailing newline.
699                            (1- end)))))))))
700   (require 'magit)
701   (if-let ((file (magit-file-relative-name)))
702       (magit-mode-setup-internal
703        #'magit-log-mode
704        (list (list (or magit-buffer-refname
705                        (magit-get-current-branch)
706                        "HEAD"))
707              (let ((args (car (magit-log-arguments))))
708                (when (and follow (not (member "--follow" args)))
709                  (push "--follow" args))
710                (when (and (file-regular-p
711                            (expand-file-name file (magit-toplevel)))
712                           beg end)
713                  (setq args (cons (format "-L%s,%s:%s" beg end file)
714                                   (cl-delete "-L" args :test
715                                              'string-prefix-p)))
716                  (setq file nil))
717                args)
718              (and file (list file)))
719        magit-log-buffer-file-locked)
720     (user-error "Buffer isn't visiting a file"))
721   (magit-log-goto-same-commit))
722
723 ;;;###autoload
724 (defun magit-log-trace-definition (file fn rev)
725   "Show log for the definition at point."
726   (interactive (list (or (magit-file-relative-name)
727                          (user-error "Buffer isn't visiting a file"))
728                      (funcall magit-log-trace-definition-function)
729                      (or magit-buffer-refname
730                          (magit-get-current-branch)
731                          "HEAD")))
732   (require 'magit)
733   (magit-mode-setup-internal
734    #'magit-log-mode
735    (list (list rev)
736          (cons (format "-L:%s:%s" (regexp-quote fn) file)
737                (cl-delete "-L" (car (magit-log-arguments))
738                           :test 'string-prefix-p))
739          nil)
740    magit-log-buffer-file-locked)
741   (magit-log-goto-same-commit))
742
743 (defun magit-diff-trace-definition ()
744   "Show log for the definition at point in a diff."
745   (interactive)
746   (let (buf pos)
747     (save-window-excursion
748       (call-interactively #'magit-diff-visit-file)
749       (setq buf (current-buffer))
750       (setq pos (point)))
751     (save-excursion
752       (with-current-buffer buf
753         (goto-char pos)
754         (call-interactively #'magit-log-trace-definition)))))
755
756 ;;;###autoload
757 (defun magit-log-merged (commit branch &optional args files)
758   "Show log for the merge of COMMIT into BRANCH.
759 More precisely, find merge commit M that brought COMMIT into
760 BRANCH, and show the log of the range \"M^..M\".  This command
761 requires git-when-merged, which is available from
762 https://github.com/mhagger/git-when-merged."
763   (interactive
764    (append (let ((commit (magit-read-branch-or-commit "Commit")))
765              (list commit
766                    (magit-read-other-branch "Merged into" commit)))
767            (magit-log-arguments)))
768   (unless (executable-find "git-when-merged")
769     (user-error "This command requires git-when-merged (%s)"
770                 "https://github.com/mhagger/git-when-merged"))
771   (magit-git-log
772    (list (or (magit-git-string "when-merged" "--show-branch" commit branch)
773              (user-error "Could not find when %s was merged into %s"
774                          commit branch)))
775    args files))
776
777 (defun magit-git-reflog (ref args)
778   (require 'magit)
779   (magit-mode-setup #'magit-reflog-mode ref args))
780
781 ;;;###autoload
782 (defun magit-reflog-current (args)
783   "Display the reflog of the current branch."
784   (interactive (list magit-reflog-arguments))
785   (magit-git-reflog (magit-get-current-branch) args))
786
787 ;;;###autoload
788 (defun magit-reflog-other (ref args)
789   "Display the reflog of a branch or another ref."
790   (interactive (list (magit-read-local-branch-or-ref "Show reflog for")
791                      magit-reflog-arguments))
792   (magit-git-reflog ref args))
793
794 ;;;###autoload
795 (defun magit-reflog-head (args)
796   "Display the `HEAD' reflog."
797   (interactive (list magit-reflog-arguments))
798   (magit-git-reflog "HEAD" args))
799
800 ;;;; Limit Commands
801
802 (defun magit-log-toggle-commit-limit ()
803   "Toggle the number of commits the current log buffer is limited to.
804 If the number of commits is currently limited, then remove that
805 limit.  Otherwise set it to 256."
806   (interactive)
807   (magit-log-set-commit-limit (lambda (&rest _) nil)))
808
809 (defun magit-log-double-commit-limit ()
810   "Double the number of commits the current log buffer is limited to."
811   (interactive)
812   (magit-log-set-commit-limit '*))
813
814 (defun magit-log-half-commit-limit ()
815   "Half the number of commits the current log buffer is limited to."
816   (interactive)
817   (magit-log-set-commit-limit '/))
818
819 (defun magit-log-set-commit-limit (fn)
820   (let* ((val (car (magit-log-arguments t)))
821          (arg (--first (string-match "^-n\\([0-9]+\\)?$" it) val))
822          (num (and arg (string-to-number (match-string 1 arg))))
823          (num (if num (funcall fn num 2) 256)))
824     (setq val (delete arg val))
825     (setcar (cdr magit-refresh-args)
826             (if (and num (> num 0))
827                 (cons (format "-n%i" num) val)
828               val)))
829   (magit-refresh))
830
831 (defun magit-log-get-commit-limit ()
832   (--when-let (--first (string-match "^-n\\([0-9]+\\)?$" it)
833                        (car (magit-log-arguments t)))
834     (string-to-number (match-string 1 it))))
835
836 ;;;; Other Commands
837
838 (defun magit-log-bury-buffer (&optional arg)
839   "Bury the current buffer or the revision buffer in the same frame.
840 Like `magit-mode-bury-buffer' (which see) but with a negative
841 prefix argument instead bury the revision buffer, provided it
842 is displayed in the current frame."
843   (interactive "p")
844   (if (< arg 0)
845       (let* ((buf (magit-mode-get-buffer 'magit-revision-mode))
846              (win (and buf (get-buffer-window buf (selected-frame)))))
847         (if win
848             (with-selected-window win
849               (with-current-buffer buf
850                 (magit-mode-bury-buffer (> (abs arg) 1))))
851           (user-error "No revision buffer in this frame")))
852     (magit-mode-bury-buffer (> arg 1))))
853
854 ;;;###autoload
855 (defun magit-log-move-to-parent (&optional n)
856   "Move to the Nth parent of the current commit."
857   (interactive "p")
858   (when (derived-mode-p 'magit-log-mode)
859     (when (magit-section-match 'commit)
860       (let* ((section (magit-current-section))
861              (parent-rev (format "%s^%s" (oref section value) (or n 1))))
862         (if-let ((parent-hash (magit-rev-parse "--short" parent-rev)))
863             (if-let ((parent (--first (equal (oref section value)
864                                              parent-hash)
865                                       (magit-section-siblings section 'next))))
866                 (magit-section-goto parent)
867               (user-error
868                (substitute-command-keys
869                 (concat "Parent " parent-hash " not found.  Try typing "
870                         "\\[magit-log-double-commit-limit] first"))))
871           (user-error "Parent %s does not exist" parent-rev))))))
872
873 ;;; Log Mode
874
875 (defvar magit-log-mode-map
876   (let ((map (make-sparse-keymap)))
877     (set-keymap-parent map magit-mode-map)
878     (define-key map "\C-c\C-b" 'magit-go-backward)
879     (define-key map "\C-c\C-f" 'magit-go-forward)
880     (define-key map "\C-c\C-n" 'magit-log-move-to-parent)
881     (define-key map "=" 'magit-log-toggle-commit-limit)
882     (define-key map "+" 'magit-log-double-commit-limit)
883     (define-key map "-" 'magit-log-half-commit-limit)
884     (define-key map "q" 'magit-log-bury-buffer)
885     map)
886   "Keymap for `magit-log-mode'.")
887
888 (define-derived-mode magit-log-mode magit-mode "Magit Log"
889   "Mode for looking at Git log.
890
891 This mode is documented in info node `(magit)Log Buffer'.
892
893 \\<magit-mode-map>\
894 Type \\[magit-refresh] to refresh the current buffer.
895 Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \
896 to visit the commit at point.
897
898 Type \\[magit-branch-popup] to see available branch commands.
899 Type \\[magit-merge-popup] to merge the branch or commit at point.
900 Type \\[magit-cherry-pick-popup] to apply the commit at point.
901 Type \\[magit-reset] to reset `HEAD' to the commit at point.
902
903 \\{magit-log-mode-map}"
904   :group 'magit-log
905   (hack-dir-local-variables-non-file-buffer)
906   (setq imenu-prev-index-position-function
907         'magit-imenu--log-prev-index-position-function)
908   (setq imenu-extract-index-name-function
909         'magit-imenu--log-extract-index-name-function)
910   (setq-local bookmark-make-record-function
911               'magit-bookmark--log-make-record))
912
913 (defvar magit-log-disable-graph-hack-args
914   '("-G" "--grep" "--author")
915   "Arguments which disable the graph speedup hack.")
916
917 (defun magit-log-refresh-buffer (revs args files)
918   (magit-set-header-line-format
919    (funcall magit-log-header-line-function revs args files))
920   (if (= (length files) 1)
921       (unless (magit-file-tracked-p (car files))
922         (setq args (cons "--full-history" args)))
923     (setq args (remove "--follow" args)))
924   (when (--any-p (string-match-p
925                   (concat "^" (regexp-opt magit-log-remove-graph-args)) it)
926                  args)
927     (setq args (remove "--graph" args)))
928   (unless (member "--graph" args)
929     (setq args (remove "--color" args)))
930   (when-let ((limit (magit-log-get-commit-limit))
931              (limit (* 2 limit)) ; increase odds for complete graph
932              (count (and (= (length revs) 1)
933                          (> limit 1024) ; otherwise it's fast enough
934                          (setq revs (car revs))
935                          (not (string-match-p "\\.\\." revs))
936                          (not (member revs '("--all" "--branches")))
937                          (-none-p (lambda (arg)
938                                     (--any-p (string-prefix-p it arg)
939                                              magit-log-disable-graph-hack-args))
940                                   args)
941                          (magit-git-string "rev-list" "--count"
942                                            "--first-parent" args revs))))
943     (setq revs (if (< (string-to-number count) limit)
944                    revs
945                  (format "%s~%s..%s" revs limit revs))))
946   (magit-insert-section (logbuf)
947     (magit-insert-log revs args files)))
948
949 (defun magit-log-header-line-arguments (revs args files)
950   "Return string describing some of the used arguments."
951   (mapconcat (lambda (arg)
952                (if (string-match-p " " arg)
953                    (prin1 arg)
954                  arg))
955              `("git" "log" ,@args ,@revs "--" ,@files)
956              " "))
957
958 (defun magit-log-header-line-sentence (revs args files)
959   "Return string containing all arguments."
960   (concat "Commits in "
961           (mapconcat #'identity revs " ")
962           (and (member "--reverse" args)
963                " in reverse")
964           (and files (concat " touching "
965                              (mapconcat 'identity files " ")))
966           (--some (and (string-prefix-p "-L" it)
967                        (concat " " it))
968                   args)))
969
970 (defun magit-insert-log (revs &optional args files)
971   "Insert a log section.
972 Do not add this to a hook variable."
973   (let ((magit-git-global-arguments
974          (remove "--literal-pathspecs" magit-git-global-arguments)))
975     (magit-git-wash (apply-partially #'magit-log-wash-log 'log)
976       "log"
977       (format "--format=%s%%h%s%%x00%s%%x00%%aN%%x00%%at%%x00%%s%s"
978               (if (and (member "--left-right" args)
979                        (not (member "--graph" args)))
980                   "%m "
981                 "")
982               (if (member "--decorate" args) "%d" "")
983               (if (member "--show-signature" args)
984                   (progn (setq args (remove "--show-signature" args)) "%G?")
985                 "")
986               (if (member "++header" args)
987                   (if (member "--graph" (setq args (remove "++header" args)))
988                       (concat "\n" magit-log-revision-headers-format "\n")
989                     (concat "\n" magit-log-revision-headers-format "\n"))
990                 ""))
991       (progn
992         (--when-let (--first (string-match "^\\+\\+order=\\(.+\\)$" it) args)
993           (setq args (cons (format "--%s-order" (match-string 1 it))
994                            (remove it args))))
995         (when (member "--decorate" args)
996           (setq args (cons "--decorate=full" (remove "--decorate" args))))
997         (when (member "--reverse" args)
998           (setq args (remove "--graph" args)))
999         args)
1000       "--use-mailmap" "--no-prefix" revs "--" files)))
1001
1002 (defvar magit-commit-section-map
1003   (let ((map (make-sparse-keymap)))
1004     (define-key map [remap magit-visit-thing] 'magit-show-commit)
1005     (define-key map "a" 'magit-cherry-apply)
1006     map)
1007   "Keymap for `commit' sections.")
1008
1009 (defvar magit-module-commit-section-map
1010   (let ((map (make-sparse-keymap)))
1011     (define-key map [remap magit-visit-thing] 'magit-show-commit)
1012     map)
1013   "Keymap for `module-commit' sections.")
1014
1015 (defconst magit-log-heading-re
1016   (concat "^"
1017           "\\(?4:[-_/|\\*o<>. ]*\\)"               ; graph
1018           "\\(?1:[0-9a-fA-F]+\\)"                  ; sha1
1019           "\\(?3:[^\0\n]+)\\)?\0"                  ; refs
1020           "\\(?7:[BGUXYREN]\\)?\0"                 ; gpg
1021           "\\(?5:[^\0\n]*\\)\0"                    ; author
1022           ;; Note: Date is optional because, prior to Git v2.19.0,
1023           ;; `git rebase -i --root` corrupts the root's author date.
1024           "\\(?6:[^\0\n]*\\)\0"                    ; date
1025           "\\(?2:.*\\)$"))                         ; msg
1026
1027 (defconst magit-log-cherry-re
1028   (concat "^"
1029           "\\(?8:[-+]\\) "                         ; cherry
1030           "\\(?1:[0-9a-fA-F]+\\) "                 ; sha1
1031           "\\(?2:.*\\)$"))                         ; msg
1032
1033 (defconst magit-log-module-re
1034   (concat "^"
1035           "\\(?:\\(?11:[<>]\\) \\)?"               ; side
1036           "\\(?1:[0-9a-fA-F]+\\) "                 ; sha1
1037           "\\(?2:.*\\)$"))                         ; msg
1038
1039 (defconst magit-log-bisect-vis-re
1040   (concat "^"
1041           "\\(?4:[-_/|\\*o<>. ]*\\)"               ; graph
1042           "\\(?1:[0-9a-fA-F]+\\)"                  ; sha1
1043           "\\(?3:[^\0\n]+)\\)?\0"                  ; refs
1044           "\\(?2:.*\\)$"))                         ; msg
1045
1046 (defconst magit-log-bisect-log-re
1047   (concat "^# "
1048           "\\(?3:bad:\\|skip:\\|good:\\) "         ; "refs"
1049           "\\[\\(?1:[^]\n]+\\)\\] "                ; sha1
1050           "\\(?2:.*\\)$"))                         ; msg
1051
1052 (defconst magit-log-reflog-re
1053   (concat "^"
1054           "\\(?1:[^\0\n]+\\)\0"                    ; sha1
1055           "\\(?5:[^\0\n]*\\)\0"                    ; author
1056           "\\(?:\\(?:[^@\n]+@{\\(?6:[^}\n]+\\)}\0" ; date
1057           "\\(?10:merge \\|autosave \\|restart \\|[^:\n]+: \\)?" ; refsub
1058           "\\(?2:.*\\)?\\)\\|\0\\)$"))             ; msg
1059
1060 (defconst magit-reflog-subject-re
1061   (concat "\\(?1:[^ ]+\\) ?"                       ; command
1062           "\\(?2:\\(?: ?-[^ ]+\\)+\\)?"            ; option
1063           "\\(?: ?(\\(?3:[^)]+\\))\\)?"))          ; type
1064
1065 (defconst magit-log-stash-re
1066   (concat "^"
1067           "\\(?1:[^\0\n]+\\)\0"                    ; "sha1"
1068           "\\(?5:[^\0\n]*\\)\0"                    ; author
1069           "\\(?6:[^\0\n]+\\)\0"                    ; date
1070           "\\(?2:.*\\)$"))                         ; msg
1071
1072 (defvar magit-log-count nil)
1073
1074 (defvar magit-log-format-message-function 'magit-log-propertize-keywords)
1075
1076 (defun magit-log-wash-log (style args)
1077   (setq args (-flatten args))
1078   (when (and (member "--graph" args)
1079              (member "--color" args))
1080     (let ((ansi-color-apply-face-function
1081            (lambda (beg end face)
1082              (put-text-property beg end 'font-lock-face
1083                                 (or face 'magit-log-graph)))))
1084       (ansi-color-apply-on-region (point-min) (point-max))))
1085   (when (eq style 'cherry)
1086     (reverse-region (point-min) (point-max)))
1087   (let ((magit-log-count 0))
1088     (magit-wash-sequence (apply-partially 'magit-log-wash-rev style
1089                                           (magit-abbrev-length)))
1090     (if (derived-mode-p 'magit-log-mode)
1091         (when (eq magit-log-count (magit-log-get-commit-limit))
1092           (magit-insert-section (longer)
1093             (insert-text-button
1094              (substitute-command-keys
1095               (format "Type \\<%s>\\[%s] to show more history"
1096                       'magit-log-mode-map
1097                       'magit-log-double-commit-limit))
1098              'action (lambda (_button)
1099                        (magit-log-double-commit-limit))
1100              'follow-link t
1101              'mouse-face 'magit-section-highlight)))
1102       (insert ?\n))))
1103
1104 (cl-defun magit-log-wash-rev (style abbrev)
1105   (when (derived-mode-p 'magit-log-mode)
1106     (cl-incf magit-log-count))
1107   (looking-at (pcase style
1108                 (`log        magit-log-heading-re)
1109                 (`cherry     magit-log-cherry-re)
1110                 (`module     magit-log-module-re)
1111                 (`reflog     magit-log-reflog-re)
1112                 (`stash      magit-log-stash-re)
1113                 (`bisect-vis magit-log-bisect-vis-re)
1114                 (`bisect-log magit-log-bisect-log-re)))
1115   (magit-bind-match-strings
1116       (hash msg refs graph author date gpg cherry _ refsub side) nil
1117     (setq msg (substring-no-properties msg))
1118     (when refs
1119       (setq refs (substring-no-properties refs)))
1120     (let ((align (or (eq style 'cherry)
1121                      (not (member "--stat" (cadr magit-refresh-args)))))
1122           (non-graph-re (if (eq style 'bisect-vis)
1123                             magit-log-bisect-vis-re
1124                           magit-log-heading-re)))
1125       (magit-delete-line)
1126       ;; If the reflog entries have been pruned, the output of `git
1127       ;; reflog show' includes a partial line that refers to the hash
1128       ;; of the youngest expired reflog entry.
1129       (when (and (eq style 'reflog) (not date))
1130         (cl-return-from magit-log-wash-rev t))
1131       (magit-insert-section section (commit hash)
1132         (pcase style
1133           (`stash      (oset section type 'stash))
1134           (`module     (oset section type 'module-commit))
1135           (`bisect-log (setq hash (magit-rev-parse "--short" hash))))
1136         (when cherry
1137           (when (and (derived-mode-p 'magit-refs-mode)
1138                      magit-refs-show-commit-count)
1139             (insert (make-string (1- magit-refs-focus-column-width) ?\s)))
1140           (insert (propertize cherry 'face (if (string= cherry "-")
1141                                                'magit-cherry-equivalent
1142                                              'magit-cherry-unmatched)))
1143           (insert ?\s))
1144         (when side
1145           (insert (propertize side 'face (if (string= side "<")
1146                                              'magit-cherry-equivalent
1147                                            'magit-cherry-unmatched)))
1148           (insert ?\s))
1149         (when align
1150           (insert (propertize hash 'face 'magit-hash) ?\s))
1151         (when graph
1152           (insert graph))
1153         (unless align
1154           (insert (propertize hash 'face 'magit-hash) ?\s))
1155         (when (and refs (not magit-log-show-refname-after-summary))
1156           (insert (magit-format-ref-labels refs) ?\s))
1157         (when (eq style 'reflog)
1158           (insert (format "%-2s " (1- magit-log-count)))
1159           (when refsub
1160             (insert (magit-reflog-format-subject
1161                      (substring refsub 0 (if (string-match-p ":" refsub) -2 -1))))))
1162         (when msg
1163           (when gpg
1164             (setq msg (propertize msg 'face
1165                                   (pcase (aref gpg 0)
1166                                     (?G 'magit-signature-good)
1167                                     (?B 'magit-signature-bad)
1168                                     (?U 'magit-signature-untrusted)
1169                                     (?X 'magit-signature-expired)
1170                                     (?Y 'magit-signature-expired-key)
1171                                     (?R 'magit-signature-revoked)
1172                                     (?E 'magit-signature-error)))))
1173           (insert (funcall magit-log-format-message-function hash msg)))
1174         (when (and refs magit-log-show-refname-after-summary)
1175           (insert ?\s)
1176           (insert (magit-format-ref-labels refs)))
1177         (insert ?\n)
1178         (when (memq style '(log reflog stash))
1179           (goto-char (line-beginning-position))
1180           (when (and refsub
1181                      (string-match "\\`\\([^ ]\\) \\+\\(..\\)\\(..\\)" date))
1182             (setq date (+ (string-to-number (match-string 1 date))
1183                           (* (string-to-number (match-string 2 date)) 60 60)
1184                           (* (string-to-number (match-string 3 date)) 60))))
1185           (save-excursion
1186             (backward-char)
1187             (magit-log-format-margin hash author date)))
1188         (when (and (eq style 'cherry)
1189                    (magit-buffer-margin-p))
1190           (save-excursion
1191             (backward-char)
1192             (apply #'magit-log-format-margin hash
1193                    (split-string (magit-rev-format "%aN%x00%ct" hash) "\0"))))
1194         (when (and graph
1195                    (not (eobp))
1196                    (not (looking-at non-graph-re)))
1197           (when (looking-at "")
1198             (magit-insert-heading)
1199             (delete-char 1)
1200             (magit-insert-section (commit-header)
1201               (forward-line)
1202               (magit-insert-heading)
1203               (re-search-forward "")
1204               (backward-delete-char 1)
1205               (forward-char)
1206               (insert ?\n))
1207             (delete-char 1))
1208           (if (looking-at "^\\(---\\|\n\s\\|\ndiff\\)")
1209               (let ((limit (save-excursion
1210                              (and (re-search-forward non-graph-re nil t)
1211                                   (match-beginning 0)))))
1212                 (unless (oref magit-insert-section--current content)
1213                   (magit-insert-heading))
1214                 (delete-char (if (looking-at "\n") 1 4))
1215                 (magit-diff-wash-diffs (list "--stat") limit))
1216             (when align
1217               (setq align (make-string (1+ abbrev) ? )))
1218             (when (and (not (eobp)) (not (looking-at non-graph-re)))
1219               (when align
1220                 (setq align (make-string (1+ abbrev) ? )))
1221               (while (and (not (eobp)) (not (looking-at non-graph-re)))
1222                 (when align
1223                   (save-excursion (insert align)))
1224                 (magit-make-margin-overlay)
1225                 (forward-line))
1226               ;; When `--format' is used and its value isn't one of the
1227               ;; predefined formats, then `git-log' does not insert a
1228               ;; separator line.
1229               (save-excursion
1230                 (forward-line -1)
1231                 (looking-at "[-_/|\\*o<>. ]*"))
1232               (setq graph (match-string 0))
1233               (unless (string-match-p "[/\\]" graph)
1234                 (insert graph ?\n))))))))
1235   t)
1236
1237 (defun magit-log-propertize-keywords (_rev msg)
1238   (let ((start 0))
1239     (when (string-match "^\\(squash\\|fixup\\)! " msg start)
1240       (setq start (match-end 0))
1241       (put-text-property (match-beginning 0)
1242                          (match-end 0)
1243                          'face 'magit-keyword-squash msg))
1244     (while (string-match "\\[[^[]*\\]" msg start)
1245       (setq start (match-end 0))
1246       (when magit-log-highlight-keywords
1247         (put-text-property (match-beginning 0)
1248                            (match-end 0)
1249                            'face 'magit-keyword msg))))
1250   msg)
1251
1252 (defun magit-log-maybe-show-more-commits (section)
1253   "When point is at the end of a log buffer, insert more commits.
1254
1255 Log buffers end with a button \"Type + to show more history\".
1256 When the use of a section movement command puts point on that
1257 button, then automatically show more commits, without the user
1258 having to press \"+\".
1259
1260 This function is called by `magit-section-movement-hook' and
1261 exists mostly for backward compatibility reasons."
1262   (when (and (eq (oref section type) 'longer)
1263              magit-log-auto-more)
1264     (magit-log-double-commit-limit)
1265     (forward-line -1)
1266     (magit-section-forward)))
1267
1268 (defvar magit--update-revision-buffer nil)
1269
1270 (defun magit-log-maybe-update-revision-buffer (&optional _)
1271   "When moving in the log buffer, update the revision buffer.
1272 If there is no revision buffer in the same frame, then do nothing."
1273   (when (derived-mode-p 'magit-log-mode)
1274     (magit-log-maybe-update-revision-buffer-1)))
1275
1276 (defun magit-log-maybe-update-revision-buffer-1 ()
1277   (unless magit--update-revision-buffer
1278     (when-let ((commit (magit-section-value-if 'commit))
1279                (buffer (magit-mode-get-buffer 'magit-revision-mode nil t)))
1280       (setq magit--update-revision-buffer (list commit buffer))
1281       (run-with-idle-timer
1282        magit-update-other-window-delay nil
1283        (let ((args (magit-show-commit--arguments)))
1284          (lambda ()
1285            (pcase-let ((`(,rev ,buf) magit--update-revision-buffer))
1286              (setq magit--update-revision-buffer nil)
1287              (when (buffer-live-p buf)
1288                (let ((magit-display-buffer-noselect t))
1289                  (apply #'magit-show-commit rev args))))
1290            (setq magit--update-revision-buffer nil)))))))
1291
1292 (defvar magit--update-blob-buffer nil)
1293
1294 (defun magit-log-maybe-update-blob-buffer (&optional _)
1295   "When moving in the log buffer, update the blob buffer.
1296 If there is no blob buffer in the same frame, then do nothing."
1297   (when (derived-mode-p 'magit-log-mode)
1298     (magit-log-maybe-update-blob-buffer-1)))
1299
1300 (defun magit-log-maybe-update-blob-buffer-1 ()
1301   (unless magit--update-revision-buffer
1302     (when-let ((commit (magit-section-value-if 'commit))
1303                (buffer (--first (with-current-buffer it magit-buffer-revision)
1304                                 (mapcar #'window-buffer (window-list)))))
1305         (setq magit--update-blob-buffer (list commit buffer))
1306         (run-with-idle-timer
1307          magit-update-other-window-delay nil
1308          (lambda ()
1309            (pcase-let ((`(,rev ,buf) magit--update-blob-buffer))
1310              (setq magit--update-blob-buffer nil)
1311              (when (buffer-live-p buf)
1312                (save-excursion
1313                  (with-selected-window (get-buffer-window buf)
1314                    (with-current-buffer buf
1315                      (magit-blob-visit (list (magit-rev-parse rev)
1316                                              (magit-file-relative-name
1317                                               magit-buffer-file-name))
1318                                        (line-number-at-pos))))))))))))
1319
1320 (defun magit-log-goto-same-commit (&optional default)
1321   (let ((prev magit-previous-section))
1322     (when-let ((rev (cond ((and prev (magit-section-match 'commit prev))
1323                            (oref prev value))
1324                           ((and prev (magit-section-match 'branch prev))
1325                            (magit-rev-format "%h" (oref prev value)))
1326                           (default (magit-rev-format "%h" default))))
1327                (same (--first (equal (oref it value) rev)
1328                               (oref magit-root-section children))))
1329       (goto-char (oref same start)))))
1330
1331 ;;; Log Margin
1332
1333 (defvar-local magit-log-margin-show-shortstat nil)
1334
1335 (defun magit-toggle-log-margin-style ()
1336   "Toggle between the regular and the shortstat margin style.
1337 The shortstat style is experimental and rather slow."
1338   (interactive)
1339   (setq magit-log-margin-show-shortstat
1340         (not magit-log-margin-show-shortstat))
1341   (magit-set-buffer-margin nil t))
1342
1343 (defun magit-log-format-margin (rev author date)
1344   (when-let ((option (magit-margin-option)))
1345     (if magit-log-margin-show-shortstat
1346         (magit-log-format-shortstat-margin rev)
1347       (pcase-let ((`(,_ ,style ,width ,details ,details-width)
1348                    (or magit-buffer-margin
1349                        (symbol-value option))))
1350         (magit-make-margin-overlay
1351          (concat (and details
1352                       (concat (propertize (truncate-string-to-width
1353                                            (or author "")
1354                                            details-width
1355                                            nil ?\s (make-string 1 magit-ellipsis))
1356                                           'face 'magit-log-author)
1357                               " "))
1358                  (propertize
1359                   (if (stringp style)
1360                       (format-time-string
1361                        style
1362                        (seconds-to-time (string-to-number date)))
1363                     (pcase-let* ((abbr (eq style 'age-abbreviated))
1364                                  (`(,cnt ,unit) (magit--age date abbr)))
1365                       (format (format (if abbr "%%2i%%-%ic" "%%2i %%-%is")
1366                                       (- width (if details (1+ details-width) 0)))
1367                               cnt unit)))
1368                   'face 'magit-log-date)))))))
1369
1370 (defun magit-log-format-shortstat-margin (rev)
1371   (magit-make-margin-overlay
1372    (if-let ((line (and rev (magit-git-string
1373                             "show" "--format=" "--shortstat" rev))))
1374        (if (string-match "\
1375 \\([0-9]+\\) files? changed, \
1376 \\(?:\\([0-9]+\\) insertions?(\\+)\\)?\
1377 \\(?:\\(?:, \\)?\\([0-9]+\\) deletions?(-)\\)?\\'" line)
1378            (magit-bind-match-strings (files add del) line
1379              (format
1380               "%5s %5s%4s"
1381               (if add
1382                   (propertize (format "%s+" add) 'face 'magit-diffstat-added)
1383                 "")
1384               (if del
1385                   (propertize (format "%s-" del) 'face 'magit-diffstat-removed)
1386                 "")
1387               files))
1388          "")
1389      "")))
1390
1391 (defun magit-log-margin-width (style details details-width)
1392   (if magit-log-margin-show-shortstat
1393       16
1394     (+ (if details (1+ details-width) 0)
1395        (if (stringp style)
1396            (length (format-time-string style))
1397          (+ 2 ; two digits
1398             1 ; trailing space
1399             (if (eq style 'age-abbreviated)
1400                 1  ; single character
1401               (+ 1 ; gap after digits
1402                  (apply #'max (--map (max (length (nth 1 it))
1403                                           (length (nth 2 it)))
1404                                      magit--age-spec)))))))))
1405
1406 ;;; Select Mode
1407
1408 (defvar magit-log-select-mode-map
1409   (let ((map (make-sparse-keymap)))
1410     (set-keymap-parent map magit-log-mode-map)
1411     (define-key map "\C-c\C-b" 'undefined)
1412     (define-key map "\C-c\C-f" 'undefined)
1413     (define-key map "."        'magit-log-select-pick)
1414     (define-key map "e"        'magit-log-select-pick)
1415     (define-key map "\C-c\C-c" 'magit-log-select-pick)
1416     (define-key map "q"        'magit-log-select-quit)
1417     (define-key map "\C-c\C-k" 'magit-log-select-quit)
1418     map)
1419   "Keymap for `magit-log-select-mode'.")
1420
1421 (put 'magit-log-select-pick :advertised-binding [?\C-c ?\C-c])
1422 (put 'magit-log-select-quit :advertised-binding [?\C-c ?\C-k])
1423
1424 (define-derived-mode magit-log-select-mode magit-log-mode "Magit Select"
1425   "Mode for selecting a commit from history.
1426
1427 This mode is documented in info node `(magit)Select from Log'.
1428
1429 \\<magit-mode-map>\
1430 Type \\[magit-refresh] to refresh the current buffer.
1431 Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \
1432 to visit the commit at point.
1433
1434 \\<magit-log-select-mode-map>\
1435 Type \\[magit-log-select-pick] to select the commit at point.
1436 Type \\[magit-log-select-quit] to abort without selecting a commit."
1437   :group 'magit-log
1438   (hack-dir-local-variables-non-file-buffer))
1439
1440 (defun magit-log-select-refresh-buffer (rev args)
1441   (magit-insert-section (logbuf)
1442     (magit-insert-log rev args)))
1443
1444 (defvar-local magit-log-select-pick-function nil)
1445 (defvar-local magit-log-select-quit-function nil)
1446
1447 (defun magit-log-select (pick &optional msg quit branch args initial)
1448   (declare (indent defun))
1449   (magit-mode-setup #'magit-log-select-mode
1450                     (or branch (magit-get-current-branch) "HEAD")
1451                     (append args magit-log-select-arguments))
1452   (magit-log-goto-same-commit initial)
1453   (setq magit-log-select-pick-function pick)
1454   (setq magit-log-select-quit-function quit)
1455   (when magit-log-select-show-usage
1456     (let ((pick (propertize (substitute-command-keys
1457                              "\\[magit-log-select-pick]")
1458                             'face
1459                             'magit-header-line-key))
1460           (quit (propertize (substitute-command-keys
1461                              "\\[magit-log-select-quit]")
1462                             'face
1463                             'magit-header-line-key)))
1464       (setq msg (format-spec
1465                  (if msg
1466                      (if (string-suffix-p "," msg)
1467                          (concat msg " or %q to abort")
1468                        msg)
1469                    "Type %p to select commit at point, or %q to abort")
1470                  `((?p . ,pick)
1471                    (?q . ,quit)))))
1472     (add-face-text-property 0 (length msg) 'magit-header-line-log-select t msg)
1473     (when (memq magit-log-select-show-usage '(both header-line))
1474       (magit-set-header-line-format msg))
1475     (when (memq magit-log-select-show-usage '(both echo-area))
1476       (message "%s" (substring-no-properties msg)))))
1477
1478 (defun magit-log-select-pick ()
1479   "Select the commit at point and act on it.
1480 Call `magit-log-select-pick-function' with the selected
1481 commit as argument."
1482   (interactive)
1483   (let ((fun magit-log-select-pick-function)
1484         (rev (magit-commit-at-point)))
1485     (magit-mode-bury-buffer 'kill)
1486     (funcall fun rev)))
1487
1488 (defun magit-log-select-quit ()
1489   "Abort selecting a commit, don't act on any commit."
1490   (interactive)
1491   (magit-mode-bury-buffer 'kill)
1492   (when magit-log-select-quit-function
1493     (funcall magit-log-select-quit-function)))
1494
1495 ;;; Cherry Mode
1496
1497 (defvar magit-cherry-mode-map
1498   (let ((map (make-sparse-keymap)))
1499     (set-keymap-parent map magit-mode-map)
1500     (define-key map "q" 'magit-log-bury-buffer)
1501     (define-key map "L" 'magit-margin-popup)
1502     map)
1503   "Keymap for `magit-cherry-mode'.")
1504
1505 (define-derived-mode magit-cherry-mode magit-mode "Magit Cherry"
1506   "Mode for looking at commits not merged upstream.
1507
1508 \\<magit-mode-map>\
1509 Type \\[magit-refresh] to refresh the current buffer.
1510 Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \
1511 to visit the commit at point.
1512
1513 Type \\[magit-cherry-pick-popup] to apply the commit at point.
1514
1515 \\{magit-cherry-mode-map}"
1516   :group 'magit-log
1517   (hack-dir-local-variables-non-file-buffer)
1518   (setq imenu-create-index-function
1519         'magit-imenu--cherry-create-index-function)
1520   (setq-local bookmark-make-record-function
1521               'magit-bookmark--cherry-make-record))
1522
1523 ;;;###autoload
1524 (defun magit-cherry (head upstream)
1525   "Show commits in a branch that are not merged in the upstream branch."
1526   (interactive
1527    (let  ((head (magit-read-branch "Cherry head")))
1528      (list head (magit-read-other-branch "Cherry upstream" head
1529                                          (magit-get-upstream-branch head)))))
1530   (require 'magit)
1531   (magit-mode-setup #'magit-cherry-mode upstream head))
1532
1533 (defun magit-cherry-refresh-buffer (_upstream _head)
1534   (magit-insert-section (cherry)
1535     (magit-run-section-hook 'magit-cherry-sections-hook)))
1536
1537 (defun magit-insert-cherry-headers ()
1538   "Insert headers appropriate for `magit-cherry-mode' buffers."
1539   (magit-insert-head-branch-header (nth 1 magit-refresh-args))
1540   (magit-insert-upstream-branch-header (nth 1 magit-refresh-args)
1541                                        (nth 0 magit-refresh-args)
1542                                        "Upstream: ")
1543   (insert ?\n))
1544
1545 (defun magit-insert-cherry-commits ()
1546   "Insert commit sections into a `magit-cherry-mode' buffer."
1547   (magit-insert-section (cherries)
1548     (magit-insert-heading "Cherry commits:")
1549     (magit-git-wash (apply-partially 'magit-log-wash-log 'cherry)
1550       "cherry" "-v" "--abbrev" magit-refresh-args)))
1551
1552 ;;; Reflog Mode
1553
1554 (defvar magit-reflog-mode-map
1555   (let ((map (make-sparse-keymap)))
1556     (set-keymap-parent map magit-log-mode-map)
1557     (define-key map "L" 'magit-margin-popup)
1558     map)
1559   "Keymap for `magit-reflog-mode'.")
1560
1561 (define-derived-mode magit-reflog-mode magit-log-mode "Magit Reflog"
1562   "Mode for looking at Git reflog.
1563
1564 This mode is documented in info node `(magit)Reflog'.
1565
1566 \\<magit-mode-map>\
1567 Type \\[magit-refresh] to refresh the current buffer.
1568 Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \
1569 to visit the commit at point.
1570
1571 Type \\[magit-cherry-pick-popup] to apply the commit at point.
1572 Type \\[magit-reset] to reset `HEAD' to the commit at point.
1573
1574 \\{magit-reflog-mode-map}"
1575   :group 'magit-log
1576   (hack-dir-local-variables-non-file-buffer)
1577   (setq-local bookmark-make-record-function
1578               'magit-bookmark--reflog-make-record))
1579
1580 (defun magit-reflog-refresh-buffer (ref args)
1581   (magit-set-header-line-format (concat "Reflog for " ref))
1582   (magit-insert-section (reflogbuf)
1583     (magit-git-wash (apply-partially 'magit-log-wash-log 'reflog)
1584       "reflog" "show" "--format=%h%x00%aN%x00%gd%x00%gs" "--date=raw"
1585       args ref "--")))
1586
1587 (defvar magit-reflog-labels
1588   '(("commit"      . magit-reflog-commit)
1589     ("amend"       . magit-reflog-amend)
1590     ("merge"       . magit-reflog-merge)
1591     ("checkout"    . magit-reflog-checkout)
1592     ("branch"      . magit-reflog-checkout)
1593     ("reset"       . magit-reflog-reset)
1594     ("rebase"      . magit-reflog-rebase)
1595     ("cherry-pick" . magit-reflog-cherry-pick)
1596     ("initial"     . magit-reflog-commit)
1597     ("pull"        . magit-reflog-remote)
1598     ("clone"       . magit-reflog-remote)
1599     ("autosave"    . magit-reflog-commit)
1600     ("restart"     . magit-reflog-reset)))
1601
1602 (defun magit-reflog-format-subject (subject)
1603   (let* ((match (string-match magit-reflog-subject-re subject))
1604          (command (and match (match-string 1 subject)))
1605          (option  (and match (match-string 2 subject)))
1606          (type    (and match (match-string 3 subject)))
1607          (label (if (string= command "commit")
1608                     (or type command)
1609                   command))
1610          (text (if (string= command "commit")
1611                    label
1612                  (mapconcat #'identity
1613                             (delq nil (list command option type))
1614                             " "))))
1615     (format "%-16s "
1616             (propertize text 'face
1617                         (or (cdr (assoc label magit-reflog-labels))
1618                             'magit-reflog-other)))))
1619
1620 ;;; Log Sections
1621 ;;;; Standard Log Sections
1622
1623 (defvar magit-unpulled-section-map
1624   (let ((map (make-sparse-keymap)))
1625     (define-key map [remap magit-visit-thing] 'magit-diff-dwim)
1626     map)
1627   "Keymap for `unpulled' sections.")
1628
1629 (magit-define-section-jumper magit-jump-to-unpulled-from-upstream
1630   "Unpulled from @{upstream}" unpulled "..@{upstream}")
1631
1632 (defun magit-insert-unpulled-from-upstream ()
1633   "Insert commits that haven't been pulled from the upstream yet."
1634   (when (magit-git-success "rev-parse" "@{upstream}")
1635     (magit-insert-section (unpulled "..@{upstream}" t)
1636       (magit-insert-heading
1637         (format (propertize "Unpulled from %s:" 'face 'magit-section-heading)
1638                 (magit-get-upstream-branch)))
1639       (magit-insert-log "..@{upstream}" magit-log-section-arguments))))
1640
1641 (magit-define-section-jumper magit-jump-to-unpulled-from-pushremote
1642   "Unpulled from <push-remote>" unpulled
1643   (concat ".." (magit-get-push-branch)))
1644
1645 (defun magit-insert-unpulled-from-pushremote ()
1646   "Insert commits that haven't been pulled from the push-remote yet."
1647   (--when-let (magit-get-push-branch)
1648     (unless (and (equal (magit-rev-name it)
1649                         (magit-rev-name "@{upstream}"))
1650                  (or (memq 'magit-insert-unpulled-from-upstream
1651                            magit-status-sections-hook)
1652                      (memq 'magit-insert-unpulled-from-upstream-or-recent
1653                            magit-status-sections-hook)))
1654       (magit-insert-section (unpulled (concat ".." it) t)
1655         (magit-insert-heading
1656           (format (propertize "Unpulled from %s:" 'face 'magit-section-heading)
1657                   (propertize it 'face 'magit-branch-remote)))
1658         (magit-insert-log (concat ".." it) magit-log-section-arguments)))))
1659
1660 (defvar magit-unpushed-section-map
1661   (let ((map (make-sparse-keymap)))
1662     (define-key map [remap magit-visit-thing] 'magit-diff-dwim)
1663     map)
1664   "Keymap for `unpushed' sections.")
1665
1666 (magit-define-section-jumper magit-jump-to-unpushed-to-upstream
1667   "Unpushed to @{upstream}" unpushed "@{upstream}..")
1668
1669 (defun magit-insert-unpushed-to-upstream-or-recent ()
1670   "Insert section showing unpushed or other recent commits.
1671 If an upstream is configured for the current branch and it is
1672 behind of the current branch, then show the commits that have
1673 not yet been pushed into the upstream branch.  If no upstream is
1674 configured or if the upstream is not behind of the current branch,
1675 then show the last `magit-log-section-commit-count' commits."
1676   (let ((upstream (magit-rev-parse "@{upstream}")))
1677     (if (or (not upstream)
1678             (magit-rev-ancestor-p "HEAD" upstream))
1679         (magit-insert-recent-commits 'unpushed "@{upstream}..")
1680       (magit-insert-unpushed-to-upstream))))
1681
1682 (defun magit-insert-unpushed-to-upstream ()
1683   "Insert commits that haven't been pushed to the upstream yet."
1684   (when (magit-git-success "rev-parse" "@{upstream}")
1685     (magit-insert-section (unpushed "@{upstream}..")
1686       (magit-insert-heading
1687         (format (propertize "Unmerged into %s:" 'face 'magit-section-heading)
1688                 (magit-get-upstream-branch)))
1689       (magit-insert-log "@{upstream}.." magit-log-section-arguments))))
1690
1691 (defun magit-insert-recent-commits (&optional type value)
1692   "Insert section showing recent commits.
1693 Show the last `magit-log-section-commit-count' commits."
1694   (let* ((start (format "HEAD~%s" magit-log-section-commit-count))
1695          (range (and (magit-rev-verify start)
1696                      (concat start "..HEAD"))))
1697     (magit-insert-section ((eval (or type 'recent))
1698                            (or value range)
1699                            t)
1700       (magit-insert-heading "Recent commits")
1701       (magit-insert-log range
1702                         (cons (format "-n%d" magit-log-section-commit-count)
1703                               (--remove (string-prefix-p "-n" it)
1704                                         magit-log-section-arguments))))))
1705
1706 (magit-define-section-jumper magit-jump-to-unpushed-to-pushremote
1707   "Unpushed to <push-remote>" unpushed
1708   (concat (magit-get-push-branch) ".."))
1709
1710 (defun magit-insert-unpushed-to-pushremote ()
1711   "Insert commits that haven't been pushed to the push-remote yet."
1712   (--when-let (magit-get-push-branch)
1713     (unless (and (equal (magit-rev-name it)
1714                         (magit-rev-name "@{upstream}"))
1715                  (or (memq 'magit-insert-unpushed-to-upstream
1716                            magit-status-sections-hook)
1717                      (memq 'magit-insert-unpushed-to-upstream-or-recent
1718                            magit-status-sections-hook)))
1719       (magit-insert-section (unpushed (concat it "..") t)
1720         (magit-insert-heading
1721           (format (propertize "Unpushed to %s:" 'face 'magit-section-heading)
1722                   (propertize it 'face 'magit-branch-remote)))
1723         (magit-insert-log (concat it "..") magit-log-section-arguments)))))
1724
1725 ;;;; Auxiliary Log Sections
1726
1727 (defun magit-insert-unpulled-cherries ()
1728   "Insert section showing unpulled commits.
1729 Like `magit-insert-unpulled-from-upstream' but prefix each commit
1730 which has not been applied yet (i.e. a commit with a patch-id
1731 not shared with any local commit) with \"+\", and all others with
1732 \"-\"."
1733   (when (magit-git-success "rev-parse" "@{upstream}")
1734     (magit-insert-section (unpulled "..@{upstream}")
1735       (magit-insert-heading "Unpulled commits:")
1736       (magit-git-wash (apply-partially 'magit-log-wash-log 'cherry)
1737         "cherry" "-v" (magit-abbrev-arg)
1738         (magit-get-current-branch) "@{upstream}"))))
1739
1740 (defun magit-insert-unpushed-cherries ()
1741   "Insert section showing unpushed commits.
1742 Like `magit-insert-unpushed-to-upstream' but prefix each commit
1743 which has not been applied to upstream yet (i.e. a commit with
1744 a patch-id not shared with any upstream commit) with \"+\", and
1745 all others with \"-\"."
1746   (when (magit-git-success "rev-parse" "@{upstream}")
1747     (magit-insert-section (unpushed "@{upstream}..")
1748       (magit-insert-heading "Unpushed commits:")
1749       (magit-git-wash (apply-partially 'magit-log-wash-log 'cherry)
1750         "cherry" "-v" (magit-abbrev-arg) "@{upstream}"))))
1751
1752 ;;; _
1753 (provide 'magit-log)
1754 ;;; magit-log.el ends here