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

1715 regels
68 KiB

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