about summary refs log tree commit diff
path: root/configs/shared/emacs/.emacs.d/elpa/magit-20180913.1247/magit-diff.el
diff options
context:
space:
mode:
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/magit-20180913.1247/magit-diff.el')
-rw-r--r--configs/shared/emacs/.emacs.d/elpa/magit-20180913.1247/magit-diff.el2780
1 files changed, 2780 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/magit-20180913.1247/magit-diff.el b/configs/shared/emacs/.emacs.d/elpa/magit-20180913.1247/magit-diff.el
new file mode 100644
index 000000000000..426894bd59b1
--- /dev/null
+++ b/configs/shared/emacs/.emacs.d/elpa/magit-20180913.1247/magit-diff.el
@@ -0,0 +1,2780 @@
+;;; magit-diff.el --- inspect Git diffs  -*- lexical-binding: t -*-
+
+;; Copyright (C) 2010-2018  The Magit Project Contributors
+;;
+;; You should have received a copy of the AUTHORS.md file which
+;; lists all contributors.  If not, see http://magit.vc/authors.
+
+;; Author: Jonas Bernoulli <jonas@bernoul.li>
+;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
+
+;; Magit is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; Magit is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+;; License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with Magit.  If not, see http://www.gnu.org/licenses.
+
+;;; Commentary:
+
+;; This library implements support for looking at Git diffs and
+;; commits.
+
+;;; Code:
+
+(require 'git-commit)
+(require 'magit-core)
+
+;; For `magit-diff-popup'
+(declare-function magit-stash-show "magit-stash" (stash &optional args files))
+;; For `magit-diff-visit-file'
+(declare-function dired-jump "dired-x" (&optional other-window file-name))
+(declare-function magit-find-file-noselect "magit-files" (rev file))
+(declare-function magit-status-internal "magit-status" (directory))
+;; For `magit-diff-while-committing'
+(declare-function magit-commit-message-buffer "magit-commit" ())
+;; For `magit-insert-revision-gravatar'
+(defvar gravatar-size)
+;; For `magit-show-commit' and `magit-diff-show-or-scroll'
+(declare-function magit-current-blame-chunk "magit-blame" ())
+(eval-when-compile
+  (cl-pushnew 'orig-rev eieio--known-slot-names))
+(declare-function magit-blame-mode "magit-blame" (&optional arg))
+(defvar magit-blame-mode)
+(defvar git-rebase-line)
+;; For `magit-diff-unmerged'
+(declare-function magit-merge-in-progress-p "magit-merge" ())
+(declare-function magit--merge-range "magit-merge" (&optional head))
+
+(require 'diff-mode)
+(require 'smerge-mode)
+
+(defvar bookmark-make-record-function)
+
+;;; Options
+;;;; Diff Mode
+
+(defgroup magit-diff nil
+  "Inspect and manipulate Git diffs."
+  :link '(info-link "(magit)Diffing")
+  :group 'magit-modes)
+
+(defcustom magit-diff-mode-hook nil
+  "Hook run after entering Magit-Diff mode."
+  :group 'magit-diff
+  :type 'hook)
+
+(defcustom magit-diff-arguments '("--stat" "--no-ext-diff")
+  "The diff arguments used in buffers whose mode derives from `magit-diff-mode'."
+  :group 'magit-git-arguments
+  :group 'magit-diff
+  :type '(repeat (string :tag "Argument")))
+
+(defcustom magit-diff-sections-hook
+  '(magit-insert-diff
+    magit-insert-xref-buttons)
+  "Hook run to insert sections into a `magit-diff-mode' buffer."
+  :package-version '(magit . "2.3.0")
+  :group 'magit-diff
+  :type 'hook)
+
+(defcustom magit-diff-expansion-threshold 60
+  "After how many seconds not to expand anymore diffs.
+
+Except in status buffers, diffs are usually start out fully
+expanded.  Because that can take a long time, all diffs that
+haven't been fontified during a refresh before the threshold
+defined here are instead displayed with their bodies collapsed.
+
+Note that this can cause sections that were previously expanded
+to be collapsed.  So you should not pick a very low value here.
+
+The hook function `magit-diff-expansion-threshold' has to be a
+member of `magit-section-set-visibility-hook' for this option
+to have any effect."
+  :package-version '(magit . "2.9.0")
+  :group 'magit-diff
+  :type 'float)
+
+(defcustom magit-diff-highlight-hunk-body t
+  "Whether to highlight bodies of selected hunk sections.
+This only has an effect if `magit-diff-highlight' is a
+member of `magit-section-highlight-hook', which see."
+  :package-version '(magit . "2.1.0")
+  :group 'magit-diff
+  :type 'boolean)
+
+(defcustom magit-diff-highlight-hunk-region-functions
+  '(magit-diff-highlight-hunk-region-dim-outside
+    magit-diff-highlight-hunk-region-using-overlays)
+  "The functions used to highlight the hunk-internal region.
+
+`magit-diff-highlight-hunk-region-dim-outside' overlays the outside
+of the hunk internal selection with a face that causes the added and
+removed lines to have the same background color as context lines.
+This function should not be removed from the value of this option.
+
+`magit-diff-highlight-hunk-region-using-overlays' and
+`magit-diff-highlight-hunk-region-using-underline' emphasize the
+region by placing delimiting horizonal lines before and after it.
+The underline variant was implemented because Eli said that is
+how we should do it.  However the overlay variant actually works
+better.  Also see https://github.com/magit/magit/issues/2758.
+
+Instead of, or in addition to, using delimiting horizontal lines,
+to emphasize the boundaries, you may which to emphasize the text
+itself, using `magit-diff-highlight-hunk-region-using-face'.
+
+In terminal frames it's not possible to draw lines as the overlay
+and underline variants normally do, so there they fall back to
+calling the face function instead."
+  :package-version '(magit . "2.9.0")
+  :set-after '(magit-diff-show-lines-boundaries)
+  :group 'magit-diff
+  :type 'hook
+  :options '(magit-diff-highlight-hunk-region-dim-outside
+             magit-diff-highlight-hunk-region-using-underline
+             magit-diff-highlight-hunk-region-using-overlays
+             magit-diff-highlight-hunk-region-using-face))
+
+(defcustom magit-diff-unmarked-lines-keep-foreground t
+  "Whether `magit-diff-highlight-hunk-region-dim-outside' preserves foreground.
+When this is set to nil, then that function only adjusts the
+foreground color but added and removed lines outside the region
+keep their distinct foreground colors."
+  :package-version '(magit . "2.9.0")
+  :group 'magit-diff
+  :type 'boolean)
+
+(defcustom magit-diff-refine-hunk nil
+  "Whether to show word-granularity differences within diff hunks.
+
+nil    never show fine differences.
+t      show fine differences for the current diff hunk only.
+`all'  show fine differences for all displayed diff hunks."
+  :group 'magit-diff
+  :safe (lambda (val) (memq val '(nil t all)))
+  :type '(choice (const :tag "Never" nil)
+                 (const :tag "Current" t)
+                 (const :tag "All" all)))
+
+(put 'magit-diff-refine-hunk 'permanent-local t)
+
+(defcustom magit-diff-adjust-tab-width nil
+  "Whether to adjust the width of tabs in diffs.
+
+Determining the correct width can be expensive if it requires
+opening large and/or many files, so the widths are cached in
+the variable `magit-diff--tab-width-cache'.  Set that to nil
+to invalidate the cache.
+
+nil       Never ajust tab width.  Use `tab-width's value from
+          the Magit buffer itself instead.
+
+t         If the corresponding file-visiting buffer exits, then
+          use `tab-width's value from that buffer.  Doing this is
+          cheap, so this value is used even if a corresponding
+          cache entry exists.
+
+`always'  If there is no such buffer, then temporarily visit the
+          file to determine the value.
+
+NUMBER    Like `always', but don't visit files larger than NUMBER
+          bytes."
+  :package-version '(magit . "2.12.0")
+  :group 'magit-diff
+  :type '(choice (const :tag "Never" nil)
+                 (const :tag "If file-visiting buffer exists" t)
+                 (const :tag "... or file isn't larger than bytes" all)
+                 (const :tag "Always" always)))
+
+(defcustom magit-diff-paint-whitespace t
+  "Specify where to highlight whitespace errors.
+See `magit-diff-highlight-trailing',
+`magit-diff-highlight-indentation'.  The symbol t means in all
+diffs, `status' means only in the status buffer, and nil means
+nowhere."
+  :group 'magit-diff
+  :safe (lambda (val) (memq val '(t nil status)))
+  :type '(choice (const :tag "Always" t)
+                 (const :tag "Never" nil)
+                 (const :tag "In status buffer" status)))
+
+(defcustom magit-diff-highlight-trailing t
+  "Whether to highlight whitespace at the end of a line in diffs.
+Used only when `magit-diff-paint-whitespace' is non-nil."
+  :group 'magit-diff
+  :safe 'booleanp
+  :type 'boolean)
+
+(defcustom magit-diff-highlight-indentation nil
+  "Highlight the \"wrong\" indentation style.
+Used only when `magit-diff-paint-whitespace' is non-nil.
+
+The value is a list of cons cells.  The car is a regular
+expression, and the cdr is the value that applies to repositories
+whose directory matches the regular expression.  If more than one
+element matches, then the *last* element in the list applies.
+The default value should therefore come first in the list.
+
+If the value is `tabs', highlight indentation with tabs.  If the
+value is an integer, highlight indentation with at least that
+many spaces.  Otherwise, highlight neither."
+  :group 'magit-diff
+  :type `(repeat (cons (string :tag "Directory regexp")
+                       (choice (const :tag "Tabs" tabs)
+                               (integer :tag "Spaces" :value ,tab-width)
+                               (const :tag "Neither" nil)))))
+
+(defcustom magit-diff-hide-trailing-cr-characters
+  (and (memq system-type '(ms-dos windows-nt)) t)
+  "Whether to hide ^M characters at the end of a line in diffs."
+  :package-version '(magit . "2.6.0")
+  :group 'magit-diff
+  :type 'boolean)
+
+(defcustom magit-diff-visit-previous-blob t
+  "Whether `magit-diff-visit-file' may visit the previous blob.
+
+When this is t and point is on a removed line in a diff for a
+committed change, then `magit-diff-visit-file' visits the blob
+from the last revision which still had that line.
+
+Currently this is only supported for committed changes, for
+staged and unstaged changes `magit-diff-visit-file' always
+visits the file in the working tree."
+  :package-version '(magit . "2.9.0")
+  :group 'magit-diff
+  :type 'boolean)
+
+(defcustom magit-diff-highlight-keywords t
+  "Whether to highlight bracketed keywords in commit messages."
+  :package-version '(magit . "2.12.0")
+  :group 'magit-diff
+  :type 'boolean)
+
+;;;; File Diff
+
+(defcustom magit-diff-buffer-file-locked t
+  "Whether `magit-diff-buffer-file' uses a dedicated buffer."
+  :package-version '(magit . "2.7.0")
+  :group 'magit-commands
+  :group 'magit-diff
+  :type 'boolean)
+
+;;;; Revision Mode
+
+(defgroup magit-revision nil
+  "Inspect and manipulate Git commits."
+  :link '(info-link "(magit)Revision Buffer")
+  :group 'magit-modes)
+
+(defcustom magit-revision-mode-hook '(bug-reference-mode)
+  "Hook run after entering Magit-Revision mode."
+  :group 'magit-revision
+  :type 'hook
+  :options '(bug-reference-mode))
+
+(defcustom magit-revision-sections-hook
+  '(magit-insert-revision-tag
+    magit-insert-revision-headers
+    magit-insert-revision-message
+    magit-insert-revision-notes
+    magit-insert-revision-diff
+    magit-insert-xref-buttons)
+  "Hook run to insert sections into a `magit-revision-mode' buffer."
+  :package-version '(magit . "2.3.0")
+  :group 'magit-revision
+  :type 'hook)
+
+(defcustom magit-revision-headers-format "\
+Author:     %aN <%aE>
+AuthorDate: %ad
+Commit:     %cN <%cE>
+CommitDate: %cd
+"
+  "Format string used to insert headers in revision buffers.
+
+All headers in revision buffers are inserted by the section
+inserter `magit-insert-revision-headers'.  Some of the headers
+are created by calling `git show --format=FORMAT' where FORMAT
+is the format specified here.  Other headers are hard coded or
+subject to option `magit-revision-insert-related-refs'."
+  :package-version '(magit . "2.3.0")
+  :group 'magit-revision
+  :type 'string)
+
+(defcustom magit-revision-insert-related-refs t
+  "Whether to show related refs in revision buffers."
+  :package-version '(magit . "2.1.0")
+  :group 'magit-revision
+  :type 'boolean)
+
+(defcustom magit-revision-use-hash-sections 'quicker
+  "Whether to turn hashes inside the commit message into sections.
+
+If non-nil, then hashes inside the commit message are turned into
+`commit' sections.  There is a trade off to be made between
+performance and reliability:
+
+- `slow' calls git for every word to be absolutely sure.
+- `quick' skips words less than seven characters long.
+- `quicker' additionally skips words that don't contain a number.
+- `quickest' uses all words that are at least seven characters
+  long and which contain at least one number as well as at least
+  one letter.
+
+If nil, then no hashes are turned into sections, but you can
+still visit the commit at point using \"RET\"."
+  :package-version '(magit . "2.12.0")
+  :group 'magit-revision
+  :type '(choice (const :tag "Use sections, quickest" quickest)
+                 (const :tag "Use sections, quicker" quicker)
+                 (const :tag "Use sections, quick" quick)
+                 (const :tag "Use sections, slow" slow)
+                 (const :tag "Don't use sections" nil)))
+
+(defcustom magit-revision-show-gravatars nil
+  "Whether to show gravatar images in revision buffers.
+
+If non-nil, then the value has to be a cons-cell which specifies
+where the gravatar images for the author and/or the committer are
+inserted inside the text that was previously inserted according
+to `magit-revision-header-format'.
+
+Both cells are regular expressions.  The car specifies where to
+insert the author gravatar image.  The top half of the image is
+inserted right after the matched text, the bottom half on the
+next line at the same offset.  The cdr specifies where to insert
+the committer image, accordingly.  Either the car or the cdr may
+be nil."
+  :package-version '(magit . "2.3.0")
+  :group 'magit-revision
+  :type '(choice (const :tag "Don't show gravatars" nil)
+                 (cons  :tag "Show gravatars"
+                        (regexp :tag "Author regexp"    "^Author:     ")
+                        (regexp :tag "Committer regexp" "^Commit:     "))))
+
+(defcustom magit-revision-use-gravatar-kludge nil
+  "Whether to work around a bug which affects display of gravatars.
+
+Gravatar images are spliced into two halves which are then
+displayed on separate lines.  On OS X the splicing has a bug in
+some Emacs builds, which causes the top and bottom halves to be
+interchanged.  Enabling this option works around this issue by
+interchanging the halves once more, which cancels out the effect
+of the bug.
+
+See https://github.com/magit/magit/issues/2265
+and https://debbugs.gnu.org/cgi/bugreport.cgi?bug=7847.
+
+Starting with Emacs 26.1 this kludge should not be required for
+any build."
+  :package-version '(magit . "2.3.0")
+  :group 'magit-revision
+  :type 'boolean)
+
+;;;; Diff Sections
+
+(defcustom magit-diff-section-arguments '("--no-ext-diff")
+  "The diff arguments used in buffers that show other things besides diffs."
+  :group 'magit-git-arguments
+  :group 'magit-diff
+  :group 'magit-status
+  :type '(repeat (string :tag "Argument")))
+
+(put 'magit-diff-section-arguments 'permanent-local t)
+
+;;; Faces
+
+(defface magit-diff-file-heading
+  '((t :weight bold))
+  "Face for diff file headings."
+  :group 'magit-faces)
+
+(defface magit-diff-file-heading-highlight
+  '((t :inherit (magit-section-highlight)))
+  "Face for current diff file headings."
+  :group 'magit-faces)
+
+(defface magit-diff-file-heading-selection
+  '((((class color) (background light))
+     :inherit magit-diff-file-heading-highlight
+     :foreground "salmon4")
+    (((class color) (background dark))
+     :inherit magit-diff-file-heading-highlight
+     :foreground "LightSalmon3"))
+  "Face for selected diff file headings."
+  :group 'magit-faces)
+
+(defface magit-diff-hunk-heading
+  '((((class color) (background light))
+     :background "grey80"
+     :foreground "grey30")
+    (((class color) (background dark))
+     :background "grey25"
+     :foreground "grey70"))
+  "Face for diff hunk headings."
+  :group 'magit-faces)
+
+(defface magit-diff-hunk-heading-highlight
+  '((((class color) (background light))
+     :background "grey75"
+     :foreground "grey30")
+    (((class color) (background dark))
+     :background "grey35"
+     :foreground "grey70"))
+  "Face for current diff hunk headings."
+  :group 'magit-faces)
+
+(defface magit-diff-hunk-heading-selection
+  '((((class color) (background light))
+     :inherit magit-diff-hunk-heading-highlight
+     :foreground "salmon4")
+    (((class color) (background dark))
+     :inherit magit-diff-hunk-heading-highlight
+     :foreground "LightSalmon3"))
+  "Face for selected diff hunk headings."
+  :group 'magit-faces)
+
+(defface magit-diff-hunk-region
+  '((t :inherit bold))
+  "Face used by `magit-diff-highlight-hunk-region-using-face'.
+
+This face is overlayed over text that uses other hunk faces,
+and those normally set the foreground and background colors.
+The `:foreground' and especially the `:background' properties
+should be avoided here.  Setting the latter would cause the
+lose of information.  Good properties to set here are `:weight'
+and `:slant'."
+  :group 'magit-faces)
+
+(defface magit-diff-lines-heading
+  '((((class color) (background light))
+     :inherit magit-diff-hunk-heading-highlight
+     :background "LightSalmon3")
+    (((class color) (background dark))
+     :inherit magit-diff-hunk-heading-highlight
+     :foreground "grey80"
+     :background "salmon4"))
+  "Face for diff hunk heading when lines are marked."
+  :group 'magit-faces)
+
+(defface magit-diff-lines-boundary
+  '((t :inherit magit-diff-lines-heading))
+  "Face for boundary of marked lines in diff hunk."
+  :group 'magit-faces)
+
+(defface magit-diff-conflict-heading
+  '((t :inherit magit-diff-hunk-heading))
+  "Face for conflict markers."
+  :group 'magit-faces)
+
+(defface magit-diff-added
+  '((((class color) (background light))
+     :background "#ddffdd"
+     :foreground "#22aa22")
+    (((class color) (background dark))
+     :background "#335533"
+     :foreground "#ddffdd"))
+  "Face for lines in a diff that have been added."
+  :group 'magit-faces)
+
+(defface magit-diff-removed
+ '((((class color) (background light))
+    :background "#ffdddd"
+    :foreground "#aa2222")
+   (((class color) (background dark))
+    :background "#553333"
+    :foreground "#ffdddd"))
+  "Face for lines in a diff that have been removed."
+  :group 'magit-faces)
+
+(defface magit-diff-our
+  '((t :inherit magit-diff-removed))
+  "Face for lines in a diff for our side in a conflict."
+  :group 'magit-faces)
+
+(defface magit-diff-base
+  '((((class color) (background light))
+     :background "#ffffcc"
+     :foreground "#aaaa11")
+    (((class color) (background dark))
+     :background "#555522"
+     :foreground "#ffffcc"))
+  "Face for lines in a diff for the base side in a conflict."
+  :group 'magit-faces)
+
+(defface magit-diff-their
+  '((t :inherit magit-diff-added))
+  "Face for lines in a diff for their side in a conflict."
+  :group 'magit-faces)
+
+(defface magit-diff-context
+  '((((class color) (background light)) :foreground "grey50")
+    (((class color) (background  dark)) :foreground "grey70"))
+  "Face for lines in a diff that are unchanged."
+  :group 'magit-faces)
+
+(defface magit-diff-added-highlight
+  '((((class color) (background light))
+     :background "#cceecc"
+     :foreground "#22aa22")
+    (((class color) (background dark))
+     :background "#336633"
+     :foreground "#cceecc"))
+  "Face for lines in a diff that have been added."
+  :group 'magit-faces)
+
+(defface magit-diff-removed-highlight
+  '((((class color) (background light))
+     :background "#eecccc"
+     :foreground "#aa2222")
+    (((class color) (background dark))
+     :background "#663333"
+     :foreground "#eecccc"))
+  "Face for lines in a diff that have been removed."
+  :group 'magit-faces)
+
+(defface magit-diff-our-highlight
+  '((t :inherit magit-diff-removed-highlight))
+  "Face for lines in a diff for our side in a conflict."
+  :group 'magit-faces)
+
+(defface magit-diff-base-highlight
+  '((((class color) (background light))
+     :background "#eeeebb"
+     :foreground "#aaaa11")
+    (((class color) (background dark))
+     :background "#666622"
+     :foreground "#eeeebb"))
+  "Face for lines in a diff for the base side in a conflict."
+  :group 'magit-faces)
+
+(defface magit-diff-their-highlight
+  '((t :inherit magit-diff-added-highlight))
+  "Face for lines in a diff for their side in a conflict."
+  :group 'magit-faces)
+
+(defface magit-diff-context-highlight
+  '((((class color) (background light))
+     :background "grey95"
+     :foreground "grey50")
+    (((class color) (background dark))
+     :background "grey20"
+     :foreground "grey70"))
+  "Face for lines in a diff that have been removed."
+  :group 'magit-faces)
+
+(defface magit-diff-whitespace-warning
+  '((t :inherit trailing-whitespace))
+  "Face for highlighting whitespace errors added lines."
+  :group 'magit-faces)
+
+(defface magit-diffstat-added
+  '((((class color) (background light)) :foreground "#22aa22")
+    (((class color) (background  dark)) :foreground "#448844"))
+  "Face for plus sign in diffstat."
+  :group 'magit-faces)
+
+(defface magit-diffstat-removed
+  '((((class color) (background light)) :foreground "#aa2222")
+    (((class color) (background  dark)) :foreground "#aa4444"))
+  "Face for minus sign in diffstat."
+  :group 'magit-faces)
+
+;;; Commands
+;;;; Diff popups
+
+(defconst magit-diff-popup-common-keywords
+  '(:variable magit-diff-arguments
+              :man-page "git-diff"))
+
+(defconst magit-diff-popup-common-options
+  '((?f "Limit to files" "-- " magit-read-files)
+    (?u "Context lines"  "-U")
+    (?m "Detect renames" "-M")
+    (?c "Detect copies"  "-C")
+    (?a "Diff algorithm" "--diff-algorithm=" magit-diff-select-algorithm)
+    (?i "Ignore submodules" "--ignore-submodules="
+        magit-diff-select-ignore-submodules)))
+
+(defun magit-diff-select-ignore-submodules (&rest _ignored)
+  (magit-read-char-case "Ignore submodules " t
+    (?u "[u]ntracked" "untracked")
+    (?d "[d]irty"     "dirty")
+    (?a "[a]ll"       "all")))
+
+(defconst magit-diff-popup-common-switches
+  '((?f "Show surrounding functions"     "--function-context")
+    (?b "Ignore whitespace changes"      "--ignore-space-change")
+    (?w "Ignore all whitespace"          "--ignore-all-space")
+    (?x "Disallow external diff drivers" "--no-ext-diff")))
+
+(defvar magit-diff-popup
+  `(,@magit-diff-popup-common-keywords
+    :options  ,magit-diff-popup-common-options
+    :switches (,@magit-diff-popup-common-switches
+               (?s "Show stats" "--stat"))
+    :actions  ((?d "Dwim"          magit-diff-dwim)
+               (?u "Diff unstaged" magit-diff-unstaged)
+               (?c "Show commit"   magit-show-commit)
+               (?r "Diff range"    magit-diff)
+               (?s "Diff staged"   magit-diff-staged)
+               (?t "Show stash"    magit-stash-show)
+               (?p "Diff paths"    magit-diff-paths)
+               (?w "Diff worktree" magit-diff-working-tree))
+    :default-action magit-diff-dwim
+    :max-action-columns 3))
+
+(defvar magit-diff-refresh-popup
+  `(,@magit-diff-popup-common-keywords
+    :options  ,magit-diff-popup-common-options
+    :switches ,magit-diff-popup-common-switches
+    :actions  ((?g "Refresh"                magit-diff-refresh)
+               (?t "Toggle hunk refinement" magit-diff-toggle-refine-hunk)
+               (?s "Set defaults"           magit-diff-set-default-arguments)
+               (?F "Toggle file filter"     magit-diff-toggle-file-filter)
+               (?w "Save defaults"          magit-diff-save-default-arguments))
+    :max-action-columns 2))
+
+(defvar magit-diff-mode-refresh-popup
+  `(,@magit-diff-popup-common-keywords
+    :options  ,magit-diff-popup-common-options
+    :switches (,@magit-diff-popup-common-switches
+               (?s "Show stats" "--stat"))
+    :actions  ((?g "Refresh"                magit-diff-refresh)
+               (?t "Toggle hunk refinement" magit-diff-toggle-refine-hunk)
+               (?s "Set defaults"           magit-diff-set-default-arguments)
+               (?r "Switch range type"      magit-diff-switch-range-type)
+               (?w "Save defaults"          magit-diff-save-default-arguments)
+               (?f "Flip revisions"         magit-diff-flip-revs) nil
+               (?F "Toggle file filter"     magit-diff-toggle-file-filter))
+    :max-action-columns 2))
+
+(defvar magit-revision-mode-refresh-popup
+  `(,@magit-diff-popup-common-keywords
+    :switches ((?f "Show surrounding functions"     "--function-context")
+               (?b "Ignore whitespace changes"      "--ignore-space-change")
+               (?w "Ignore all whitespace"          "--ignore-all-space")
+               (?x "Disallow external diff drivers" "--no-ext-diff")
+               (?s "Show stats"                     "--stat"))
+    :actions  ((?g "Refresh"                magit-diff-refresh)
+               (?t "Toggle hunk refinement" magit-diff-toggle-refine-hunk)
+               (?s "Set defaults"           magit-diff-set-default-arguments)
+               (?F "Toggle file filter"     magit-diff-toggle-file-filter)
+               (?w "Save defaults"          magit-diff-save-default-arguments))
+    :max-action-columns 2))
+
+(magit-define-popup-keys-deferred 'magit-diff-popup)
+(magit-define-popup-keys-deferred 'magit-diff-refresh-popup)
+(magit-define-popup-keys-deferred 'magit-diff-mode-refresh-popup)
+(magit-define-popup-keys-deferred 'magit-revision-mode-refresh-popup)
+
+(defvar magit-diff-section-file-args nil)
+(put 'magit-diff-section-file-args 'permanent-local t)
+(put 'magit-diff-section-file-args 'safe-local-variable
+     (lambda (val)
+       (and (listp val)
+            (-all-p #'stringp val))))
+
+(defun magit-diff-get-buffer-args ()
+  (cond ((and magit-use-sticky-arguments
+              (derived-mode-p 'magit-diff-mode))
+         (list (nth 2 magit-refresh-args)
+               (nth 3 magit-refresh-args)))
+        ((and (eq magit-use-sticky-arguments t)
+              (--when-let (magit-mode-get-buffer 'magit-diff-mode)
+                (with-current-buffer it
+                  (list (nth 2 magit-refresh-args)
+                        (nth 3 magit-refresh-args))))))
+        (t
+         (list (default-value 'magit-diff-arguments) nil))))
+
+(defun magit-diff-arguments (&optional refresh)
+  (cond ((memq magit-current-popup '(magit-diff-popup magit-diff-refresh-popup))
+         (magit-popup-export-file-args magit-current-popup-args))
+        ((and refresh (not (derived-mode-p 'magit-diff-mode)))
+         (list magit-diff-section-arguments
+               magit-diff-section-file-args))
+        (t
+         (magit-diff-get-buffer-args))))
+
+;;;###autoload
+(defun magit-diff-popup (arg)
+  "Popup console for diff commands."
+  (interactive "P")
+  (let ((magit-diff-arguments
+         ;; We cannot possibly know what suffix command the user is
+         ;; about to invoke, so we also don't know from which buffer
+         ;; we should get the current values.  However it is much
+         ;; more likely that we will end up updating the diff buffer,
+         ;; and we therefore use the value from that buffer.
+         (apply #'magit-popup-import-file-args (magit-diff-get-buffer-args))))
+    (magit-invoke-popup 'magit-diff-popup nil arg)))
+
+;;;###autoload
+(defun magit-diff-buffer-file-popup ()
+  "Popup console for diff commands.
+
+This is a variant of `magit-diff-popup' which shows the same popup
+but which limits the diff to the file being visited in the current
+buffer."
+  (interactive)
+  (if-let ((file (magit-file-relative-name)))
+      (let ((magit-diff-arguments
+             (magit-popup-import-file-args
+              (if-let ((buffer (magit-mode-get-buffer 'magit-diff-mode)))
+                  (with-current-buffer buffer
+                    (nth 3 magit-refresh-args))
+                (default-value 'magit-diff-arguments))
+              (list file))))
+        (magit-invoke-popup 'magit-diff-popup nil nil))
+    (user-error "Buffer isn't visiting a file")))
+
+(defun magit-diff-refresh-popup (arg)
+  "Popup console for changing diff arguments in the current buffer."
+  (interactive "P")
+  (let ((magit-diff-refresh-popup
+         (pcase major-mode
+           (`magit-revision-mode magit-revision-mode-refresh-popup)
+           (`magit-diff-mode     magit-diff-mode-refresh-popup)
+           (_                    magit-diff-refresh-popup)))
+        (magit-diff-arguments
+         (if (derived-mode-p 'magit-diff-mode)
+             (magit-popup-import-file-args (nth 2 magit-refresh-args)
+                                           (nth 3 magit-refresh-args))
+           (magit-popup-import-file-args magit-diff-section-arguments
+                                         magit-diff-section-file-args))))
+    (magit-invoke-popup 'magit-diff-refresh-popup nil arg)))
+
+(defun magit-diff-select-algorithm (&rest _ignore)
+  (magit-read-char-case nil t
+    (?d "[d]efault"   "default")
+    (?m "[m]inimal"   "minimal")
+    (?p "[p]atience"  "patience")
+    (?h "[h]istogram" "histogram")))
+
+;;;; Diff commands
+
+;;;###autoload
+(defun magit-diff-dwim (&optional args files)
+  "Show changes for the thing at point."
+  (interactive (magit-diff-arguments))
+  (pcase (magit-diff--dwim)
+    (`unmerged (magit-diff-unmerged args files))
+    (`unstaged (magit-diff-unstaged args files))
+    (`staged
+     (let ((file (magit-file-at-point)))
+       (if (and file (equal (cddr (car (magit-file-status file))) '(?D ?U)))
+           ;; File was deleted by us and modified by them.  Show the latter.
+           (magit-diff-unmerged args (list file))
+         (magit-diff-staged nil args files))))
+    (`(commit . ,value)
+     (magit-diff (format "%s^..%s" value value) args files))
+    (`(stash  . ,value) (magit-stash-show value args))
+    ((and range (pred stringp))
+     (magit-diff range args files))
+    (_
+     (call-interactively #'magit-diff))))
+
+(defun magit-diff--dwim ()
+  "Return information for performing DWIM diff.
+
+The information can be in three forms:
+1. TYPE
+   A symbol describing a type of diff where no additional information
+   is needed to generate the diff.  Currently, this includes `staged',
+   `unstaged' and `unmerged'.
+2. (TYPE . VALUE)
+   Like #1 but the diff requires additional information, which is
+   given by VALUE.  Currently, this includes `commit' and `stash',
+   where VALUE is the given commit or stash, respectively.
+3. RANGE
+   A string indicating a diff range.
+
+If no DWIM context is found, nil is returned."
+  (cond
+   ((--when-let (magit-region-values '(commit branch) t)
+      (deactivate-mark)
+      (concat (car (last it)) ".." (car it))))
+   (magit-buffer-refname
+    (cons 'commit magit-buffer-refname))
+   ((derived-mode-p 'magit-stash-mode)
+    (cons 'commit
+          (magit-section-case
+            (commit (oref it value))
+            (file (-> it
+                      (oref parent)
+                      (oref value)))
+            (hunk (-> it
+                      (oref parent)
+                      (oref parent)
+                      (oref value))))))
+   ((derived-mode-p 'magit-revision-mode)
+    (cons 'commit (car magit-refresh-args)))
+   ((derived-mode-p 'magit-diff-mode)
+    (nth 0 magit-refresh-args))
+   (t
+    (magit-section-case
+      ([* unstaged] 'unstaged)
+      ([* staged] 'staged)
+      (unmerged 'unmerged)
+      (unpushed (oref it value))
+      (unpulled (oref it value))
+      (branch (let ((current (magit-get-current-branch))
+                    (atpoint (oref it value)))
+                (if (equal atpoint current)
+                    (--if-let (magit-get-upstream-branch)
+                        (format "%s...%s" it current)
+                      (if (magit-anything-modified-p)
+                          current
+                        (cons 'commit current)))
+                  (format "%s...%s"
+                          (or current "HEAD")
+                          atpoint))))
+      (commit (cons 'commit (oref it value)))
+      (stash (cons 'stash (oref it value)))))))
+
+(defun magit-diff-read-range-or-commit (prompt &optional secondary-default mbase)
+  "Read range or revision with special diff range treatment.
+If MBASE is non-nil, prompt for which rev to place at the end of
+a \"revA...revB\" range.  Otherwise, always construct
+\"revA..revB\" range."
+  (--if-let (magit-region-values '(commit branch) t)
+      (let ((revA (car (last it)))
+            (revB (car it)))
+        (deactivate-mark)
+        (if mbase
+            (let ((base (magit-git-string "merge-base" revA revB)))
+              (cond
+               ((string= (magit-rev-parse revA) base)
+                (format "%s..%s" revA revB))
+               ((string= (magit-rev-parse revB) base)
+                (format "%s..%s" revB revA))
+               (t
+                (let ((main (magit-completing-read "View changes along"
+                                                   (list revA revB)
+                                                   nil t nil nil revB)))
+                  (format "%s...%s"
+                          (if (string= main revB) revA revB) main)))))
+          (format "%s..%s" revA revB)))
+    (magit-read-range prompt
+                      (or (pcase (magit-diff--dwim)
+                            (`(commit . ,value)
+                             (format "%s^..%s" value value))
+                            ((and range (pred stringp))
+                             range))
+                          secondary-default
+                          (magit-get-current-branch)))))
+
+(defun magit-diff-setup (rev-or-range const args files)
+  (require 'magit)
+  (magit-mode-setup #'magit-diff-mode rev-or-range const args files))
+
+;;;###autoload
+(defun magit-diff (rev-or-range &optional args files)
+  "Show differences between two commits.
+
+REV-OR-RANGE should be a range or a single revision.  If it is a
+revision, then show changes in the working tree relative to that
+revision.  If it is a range, but one side is omitted, then show
+changes relative to `HEAD'.
+
+If the region is active, use the revisions on the first and last
+line of the region as the two sides of the range.  With a prefix
+argument, instead of diffing the revisions, choose a revision to
+view changes along, starting at the common ancestor of both
+revisions (i.e., use a \"...\" range)."
+  (interactive (cons (magit-diff-read-range-or-commit "Diff for range"
+                                                      nil current-prefix-arg)
+                     (magit-diff-arguments)))
+  (magit-diff-setup rev-or-range nil args files))
+
+;;;###autoload
+(defun magit-diff-working-tree (&optional rev args files)
+  "Show changes between the current working tree and the `HEAD' commit.
+With a prefix argument show changes between the working tree and
+a commit read from the minibuffer."
+  (interactive
+   (cons (and current-prefix-arg
+              (magit-read-branch-or-commit "Diff working tree and commit"))
+         (magit-diff-arguments)))
+  (magit-diff-setup (or rev "HEAD") nil args files))
+
+;;;###autoload
+(defun magit-diff-staged (&optional rev args files)
+  "Show changes between the index and the `HEAD' commit.
+With a prefix argument show changes between the index and
+a commit read from the minibuffer."
+  (interactive
+   (cons (and current-prefix-arg
+              (magit-read-branch-or-commit "Diff index and commit"))
+         (magit-diff-arguments)))
+  (magit-diff-setup rev (list "--cached") args files))
+
+;;;###autoload
+(defun magit-diff-unstaged (&optional args files)
+  "Show changes between the working tree and the index."
+  (interactive (magit-diff-arguments))
+  (magit-diff-setup nil nil args files))
+
+;;;###autoload
+(defun magit-diff-unmerged (&optional args files)
+  "Show changes that are being merged."
+  (interactive (magit-diff-arguments))
+  (unless (magit-merge-in-progress-p)
+    (user-error "No merge is in progress"))
+  (magit-diff-setup (magit--merge-range) nil args files))
+
+;;;###autoload
+(defun magit-diff-while-committing (&optional args)
+  "While committing, show the changes that are about to be committed.
+While amending, invoking the command again toggles between
+showing just the new changes or all the changes that will
+be committed."
+  (interactive (list (car (magit-diff-arguments))))
+  (unless (magit-commit-message-buffer)
+    (user-error "No commit in progress"))
+  (let ((magit-display-buffer-noselect t)
+        (diff-buf (magit-mode-get-buffer 'magit-diff-mode)))
+    (if (and diff-buf
+             (get-buffer-window diff-buf))
+        (with-current-buffer diff-buf
+          (pcase-let ((`(,rev ,arg . ,_) magit-refresh-args))
+            (cond ((and (equal rev "HEAD^")
+                        (equal arg '("--cached")))
+                   (magit-diff-staged nil args))
+                  ((and (equal rev nil)
+                        (equal arg '("--cached")))
+                   (magit-diff-while-amending args))
+                  ((magit-anything-staged-p)
+                   (magit-diff-staged nil args))
+                  (t
+                   (magit-diff-while-amending args)))))
+      (if (magit-anything-staged-p)
+          (magit-diff-staged nil args)
+        (magit-diff-while-amending args)))))
+
+(define-key git-commit-mode-map
+  (kbd "C-c C-d") 'magit-diff-while-committing)
+
+(defun magit-diff-while-amending (&optional args)
+  (magit-diff-setup "HEAD^" (list "--cached") args nil))
+
+;;;###autoload
+(defun magit-diff-buffer-file ()
+  "Show diff for the blob or file visited in the current buffer."
+  (interactive)
+  (require 'magit)
+  (if-let ((file (magit-file-relative-name)))
+      (magit-mode-setup-internal #'magit-diff-mode
+                                 (list (or magit-buffer-refname
+                                           (magit-get-current-branch)
+                                           "HEAD")
+                                       nil
+                                       (cadr (magit-diff-arguments))
+                                       (list file))
+                                 magit-diff-buffer-file-locked)
+    (user-error "Buffer isn't visiting a file")))
+
+;;;###autoload
+(defun magit-diff-paths (a b)
+  "Show changes between any two files on disk."
+  (interactive (list (read-file-name "First file: " nil nil t)
+                     (read-file-name "Second file: " nil nil t)))
+  (magit-diff-setup nil (list "--no-index")
+                    nil (list (magit-convert-filename-for-git
+                               (expand-file-name a))
+                              (magit-convert-filename-for-git
+                               (expand-file-name b)))))
+
+(defvar-local magit-buffer-revision-hash nil)
+
+(defun magit-show-commit--arguments ()
+  (pcase-let ((`(,args ,diff-files) (magit-diff-arguments)))
+    (list args (if (derived-mode-p 'magit-log-mode)
+                   (and (not (member "--follow" (nth 1 magit-refresh-args)))
+                        (nth 2 magit-refresh-args))
+                 diff-files))))
+
+;;;###autoload
+(defun magit-show-commit (rev &optional args files module)
+  "Visit the revision at point in another buffer.
+If there is no revision at point or with a prefix argument prompt
+for a revision."
+  (interactive
+   (pcase-let* ((mcommit (magit-section-when module-commit))
+                (atpoint (or (and (bound-and-true-p magit-blame-mode)
+                                  (oref (magit-current-blame-chunk) orig-rev))
+                             mcommit
+                             (magit-branch-or-commit-at-point)))
+                (`(,args ,files) (magit-show-commit--arguments)))
+     (list (or (and (not current-prefix-arg) atpoint)
+               (magit-read-branch-or-commit "Show commit" atpoint))
+           args
+           files
+           (and mcommit
+                (magit-section-parent-value (magit-current-section))))))
+  (require 'magit)
+  (magit-with-toplevel
+    (when module
+      (setq default-directory
+            (expand-file-name (file-name-as-directory module))))
+    (unless (magit-rev-verify-commit rev)
+      (user-error "%s is not a commit" rev))
+    (magit-mode-setup #'magit-revision-mode rev nil args files)))
+
+;;;; Setting commands
+
+(defun magit-diff-refresh (args files)
+  "Set the local diff arguments for the current buffer."
+  (interactive (magit-diff-arguments t))
+  (cond ((derived-mode-p 'magit-diff-mode)
+         (setcdr (cdr magit-refresh-args) (list args files)))
+        (t
+         (setq-local magit-diff-section-arguments args)
+         (setq-local magit-diff-section-file-args files)))
+  (magit-refresh))
+
+(defun magit-diff-set-default-arguments (args files)
+  "Set the global diff arguments for the current buffer."
+  (interactive (magit-diff-arguments t))
+  (cond ((derived-mode-p 'magit-diff-mode)
+         (customize-set-variable 'magit-diff-arguments args)
+         (setcdr (cdr magit-refresh-args) (list args files)))
+        (t
+         (customize-set-variable 'magit-diff-section-arguments args)
+         (kill-local-variable 'magit-diff-section-arguments)
+         (kill-local-variable 'magit-diff-section-file-args)))
+  (magit-refresh))
+
+(defun magit-diff-save-default-arguments (args files)
+  "Set and save the global diff arguments for the current buffer."
+  (interactive (magit-diff-arguments t))
+  (cond ((derived-mode-p 'magit-diff-mode)
+         (customize-save-variable 'magit-diff-arguments args)
+         (setcdr (cdr magit-refresh-args) (list args files)))
+        (t
+         (customize-save-variable 'magit-diff-section-arguments args)
+         (kill-local-variable 'magit-diff-section-arguments)
+         (kill-local-variable 'magit-diff-section-file-args)))
+  (magit-refresh))
+
+(defun magit-diff-switch-range-type ()
+  "Convert diff range type.
+Change \"revA..revB\" to \"revB...revA\", or vice versa."
+  (interactive)
+  (let ((range (car magit-refresh-args)))
+    (if (and range
+             (derived-mode-p 'magit-diff-mode)
+             (string-match magit-range-re range))
+        (progn
+          (setcar magit-refresh-args
+                  (concat (match-string 1 range)
+                          (if (string= (match-string 2 range) "..")
+                              "..."
+                            "..")
+                          (match-string 3 range)))
+          (magit-refresh))
+      (user-error "No range to change"))))
+
+(defun magit-diff-flip-revs ()
+  "Swap revisions in diff range.
+Change \"revA..revB\" to \"revB..revA\"."
+  (interactive)
+  (let ((range (car magit-refresh-args)))
+    (if (and range
+             (derived-mode-p 'magit-diff-mode)
+             (string-match magit-range-re range))
+        (progn
+          (setcar magit-refresh-args
+                  (concat (match-string 3 range)
+                          (match-string 2 range)
+                          (match-string 1 range)))
+          (magit-refresh))
+      (user-error "No range to swap"))))
+
+(defvar-local magit-diff--last-file-args nil)
+(defun magit-diff--toggle-file-args (files)
+  (cond (files
+         (setq magit-diff--last-file-args files)
+               nil)
+        (magit-diff--last-file-args)
+        (t
+         (user-error "No diff file filter to toggle"))))
+
+(defun magit-diff-toggle-file-filter ()
+  "Toggle the file restriction of the current buffer's diffs.
+If the current buffer's mode is derived from `magit-log-mode',
+toggle the file restriction in the repository's revision buffer
+instead."
+  (interactive)
+  (--if-let (and (derived-mode-p 'magit-log-mode)
+                 (magit-mode-get-buffer 'magit-revision-mode))
+      (with-current-buffer it
+        (setf (nth 3 magit-refresh-args)
+              (magit-diff--toggle-file-args (nth 3 magit-refresh-args)))
+        (magit-refresh))
+    (if (derived-mode-p 'magit-diff-mode)
+        (setf (nth 3 magit-refresh-args)
+              (magit-diff--toggle-file-args (nth 3 magit-refresh-args)))
+      (setq-local magit-diff-section-file-args
+                  (magit-diff--toggle-file-args magit-diff-section-file-args)))
+    (magit-refresh)))
+
+(defun magit-diff-less-context (&optional count)
+  "Decrease the context for diff hunks by COUNT lines."
+  (interactive "p")
+  (magit-diff-set-context `(lambda (cur) (max 0 (- (or cur 0) ,count)))))
+
+(defun magit-diff-more-context (&optional count)
+  "Increase the context for diff hunks by COUNT lines."
+  (interactive "p")
+  (magit-diff-set-context `(lambda (cur) (+ (or cur 0) ,count))))
+
+(defun magit-diff-default-context ()
+  "Reset context for diff hunks to the default height."
+  (interactive)
+  (magit-diff-set-context #'ignore))
+
+(defun magit-diff-set-context (fn)
+  (let* ((def (--if-let (magit-get "diff.context") (string-to-number it) 3))
+         (val (car (magit-diff-arguments t)))
+         (arg (--first (string-match "^-U\\([0-9]+\\)?$" it) val))
+         (num (--if-let (and arg (match-string 1 arg)) (string-to-number it) def))
+         (val (delete arg val))
+         (num (funcall fn num))
+         (arg (and num (not (= num def)) (format "-U%i" num)))
+         (val (if arg (cons arg val) val)))
+    (if (derived-mode-p 'magit-diff-mode)
+        (setcar (cddr magit-refresh-args) val)
+      (setq magit-diff-section-arguments val)))
+  (magit-refresh))
+
+(defun magit-diff-context-p ()
+  (--if-let (--first (string-match "^-U\\([0-9]+\\)$" it)
+                     (car (magit-diff-arguments t)))
+      (not (equal "-U0" it))
+    t))
+
+(defun magit-diff-toggle-refine-hunk (&optional style)
+  "Turn diff-hunk refining on or off.
+
+If hunk refining is currently on, then hunk refining is turned off.
+If hunk refining is off, then hunk refining is turned on, in
+`selected' mode (only the currently selected hunk is refined).
+
+With a prefix argument, the \"third choice\" is used instead:
+If hunk refining is currently on, then refining is kept on, but
+the refining mode (`selected' or `all') is switched.
+If hunk refining is off, then hunk refining is turned on, in
+`all' mode (all hunks refined).
+
+Customize variable `magit-diff-refine-hunk' to change the default mode."
+  (interactive "P")
+  (setq-local magit-diff-refine-hunk
+              (if style
+                  (if (eq magit-diff-refine-hunk 'all) t 'all)
+                (not magit-diff-refine-hunk)))
+  (magit-diff-update-hunk-refinement))
+
+;;;; Visit commands
+
+(defun magit-diff-visit-file
+    (file &optional other-window force-worktree display-fn)
+  "From a diff, visit the corresponding file at the appropriate position.
+
+If the diff shows changes in the worktree, the index, or `HEAD',
+then visit the actual file.  Otherwise, when the diff is about an
+older commit or a range, then visit the appropriate blob.
+
+If point is on a removed line, then visit the blob for the first
+parent of the commit which removed that line, i.e. the last
+commit where that line still existed.  Otherwise visit the blob
+for the commit whose changes are being shown.
+
+Interactively, when the file or blob to be displayed is already
+being displayed in another window of the same frame, then just
+select that window and adjust point.  Otherwise, or with a prefix
+argument, display the buffer in another window.  The meaning of
+the prefix argument can be inverted or further modified using the
+option `magit-display-file-buffer-function'.
+
+Non-interactively the optional OTHER-WINDOW argument is taken
+literally.  DISPLAY-FN can be used to specify the display
+function explicitly, in which case OTHER-WINDOW is ignored.
+
+The optional FORCE-WORKTREE means to force visiting the worktree
+version of the file.  To do this interactively use the command
+`magit-diff-visit-file-worktree' instead."
+  (interactive (list (--if-let (magit-file-at-point)
+                         (expand-file-name it)
+                       (user-error "No file at point"))
+                     current-prefix-arg))
+  (if (magit-file-accessible-directory-p file)
+      (magit-diff-visit-directory file other-window)
+    (let* ((hunk (magit-diff-visit--hunk))
+           (last (and magit-diff-visit-previous-blob
+                      (not force-worktree)
+                      (magit-section-match 'hunk)
+                      (save-excursion
+                        (goto-char (line-beginning-position))
+                        (looking-at "-"))))
+           (line (and hunk (magit-diff-hunk-line   hunk)))
+           (col  (and hunk (magit-diff-hunk-column hunk last)))
+           (rev  (if last
+                     (magit-diff-visit--range-beginning)
+                   (magit-diff-visit--range-end)))
+           (buf  (if (and (not force-worktree)
+                          (stringp rev))
+                     (magit-find-file-noselect rev file)
+                   (or (get-file-buffer file)
+                       (find-file-noselect file)))))
+      (cond ((called-interactively-p 'any)
+             (magit-display-file-buffer buf))
+            (display-fn
+             (funcall display-fn buf))
+            ((or other-window (get-buffer-window buf))
+             (switch-to-buffer-other-window buf))
+            (t
+             (pop-to-buffer buf)))
+      (with-selected-window
+          (or (get-buffer-window buf 'visible)
+              (error "File buffer is not visible"))
+        (when line
+          (setq line
+                (cond ((eq rev 'staged)
+                       (apply 'magit-diff-visit--offset file nil line))
+                      ((and force-worktree
+                            (stringp rev))
+                       (apply 'magit-diff-visit--offset file rev line))
+                      (t
+                       (apply '+ line))))
+          (let ((pos (save-restriction
+                       (widen)
+                       (goto-char (point-min))
+                       (forward-line (1- line))
+                       (move-to-column col)
+                       (point))))
+            (unless (<= (point-min) pos (point-max))
+              (widen)
+              (goto-char pos))))
+        (when (magit-anything-unmerged-p file)
+          (smerge-start-session))
+        (run-hooks 'magit-diff-visit-file-hook)))))
+
+(defun magit-diff-visit-file-other-window (file)
+  "From a diff, visit the corresponding file at the appropriate position.
+The file is shown in another window.
+
+If the diff shows changes in the worktree, the index, or `HEAD',
+then visit the actual file.  Otherwise, when the diff is about an
+older commit or a range, then visit the appropriate blob.
+
+If point is on a removed line, then visit the blob for the first
+parent of the commit which removed that line, i.e. the last
+commit where that line still existed.  Otherwise visit the blob
+for the commit whose changes are being shown."
+  (interactive (list (--if-let (magit-file-at-point)
+                         (expand-file-name it)
+                       (user-error "No file at point"))))
+  (magit-diff-visit-file file t))
+
+(defvar magit-display-file-buffer-function
+  'magit-display-file-buffer-traditional
+  "The function used by `magit-diff-visit-file' to display blob buffers.
+
+Other commands such as `magit-find-file' do not use this
+function.  Instead they use high-level functions to select the
+window to be used to display the buffer.  This variable and the
+related functions are an experimental feature and should be
+treated as such.")
+
+(defun magit-display-file-buffer (buffer)
+  (funcall magit-display-file-buffer-function buffer))
+
+(defun magit-display-file-buffer-traditional (buffer)
+  "Display BUFFER in the current window.
+With a prefix argument display it in another window.
+Option `magit-display-file-buffer-function' controls
+whether `magit-diff-visit-file' uses this function."
+  (if (or current-prefix-arg (get-buffer-window buffer))
+      (pop-to-buffer buffer)
+    (switch-to-buffer buffer)))
+
+(defun magit-display-file-buffer-other-window (buffer)
+  "Display BUFFER in another window.
+With a prefix argument display it in the current window.
+Option `magit-display-file-buffer-function' controls
+whether `magit-diff-visit-file' uses this function."
+  (if (or current-prefix-arg (get-buffer-window buffer))
+      (switch-to-buffer buffer)
+    (pop-to-buffer buffer)))
+
+(defun magit-diff-visit-file-worktree (file &optional other-window)
+  "From a diff, visit the corresponding file at the appropriate position.
+
+When the file is already being displayed in another window of the
+same frame, then just select that window and adjust point.  With
+a prefix argument also display in another window.
+
+The actual file in the worktree is visited. The positions in the
+hunk headers get less useful the \"older\" the changes are, and
+as a result, jumping to the appropriate position gets less
+reliable.
+
+Also see `magit-diff-visit-file' which visits the respective
+blob, unless the diff shows changes in the worktree, the index,
+or `HEAD'."
+  (interactive (list (or (magit-file-at-point)
+                         (user-error "No file at point"))
+                     current-prefix-arg))
+  (magit-diff-visit-file file other-window t))
+
+(defun magit-diff-visit--range-end ()
+  (let ((rev (magit-diff--dwim)))
+    (if (symbolp rev)
+        rev
+      (setq rev (if (consp rev)
+                    (cdr rev)
+                  (cdr (magit-split-range rev))))
+      (if (magit-rev-head-p rev)
+          'unstaged
+        rev))))
+
+(defun magit-diff-visit--range-beginning ()
+  (let ((rev (magit-diff--dwim)))
+    (cond ((consp rev)
+           (concat (cdr rev) "^"))
+          ((stringp rev)
+           (car (magit-split-range rev)))
+          (t
+           rev))))
+
+(defun magit-diff-visit--hunk ()
+  (when-let ((scope (magit-diff-scope)))
+    (let ((section (magit-current-section)))
+      (cl-case scope
+        ((file files)
+         (setq section (car (oref section children))))
+        (list
+         (setq section (car (oref section children)))
+         (when section
+           (setq section (car (oref section children))))))
+      (and
+       ;; Unmerged files appear in the list of staged changes
+       ;; but unlike in the list of unstaged changes no diffs
+       ;; are shown here.  In that case `section' is nil.
+       section
+       ;; Currently the `hunk' type is also abused for file
+       ;; mode changes, which we are not interested in here.
+       ;; Such sections have no value.
+       (oref section value)
+       section))))
+
+(defun magit-diff-visit--offset (file rev hunk-start line-offset)
+  (let ((offset 0))
+    (with-temp-buffer
+      (save-excursion
+        (magit-with-toplevel
+          (magit-git-insert "diff" rev "--" file)))
+      (catch 'found
+        (while (re-search-forward
+                "^@@ -\\([0-9]+\\),\\([0-9]+\\) \\+\\([0-9]+\\),\\([0-9]+\\) @@"
+                nil t)
+          (let* ((abeg (string-to-number (match-string 1)))
+                 (alen (string-to-number (match-string 2)))
+                 (bbeg (string-to-number (match-string 3)))
+                 (blen (string-to-number (match-string 4)))
+                 (aend (+ abeg alen))
+                 (bend (+ bbeg blen))
+                 (hend (+ hunk-start line-offset)))
+            (if (<= abeg hunk-start)
+                (if (or (>= aend hend)
+                        (>= bend hend))
+                    (let ((line 0))
+                      (while (<= line alen)
+                        (forward-line 1)
+                        (cl-incf line)
+                        (cond ((looking-at "^\\+") (cl-incf offset))
+                              ((looking-at "^-")   (cl-decf offset)))))
+                  (cl-incf offset (- blen alen)))
+              (throw 'found nil))))))
+    (+ hunk-start line-offset offset)))
+
+(defun magit-diff-hunk-line (section)
+  (let* ((value  (oref section value))
+         (prefix (- (length value) 2))
+         (cpos   (marker-position (oref section content)))
+         (stop   (line-number-at-pos))
+         (cstart (save-excursion (goto-char cpos)
+                                 (line-number-at-pos)))
+         (prior  (and (= (length value) 3)
+                      (save-excursion (goto-char (line-beginning-position))
+                                      (looking-at "-"))))
+         (offset 0)
+         (line   (if prior
+                     (cadr value)
+                   (car (last value)))))
+    (string-match (format "^%s\\([0-9]+\\)" (if prior "-" "\\+")) line)
+    (setq line (string-to-number (match-string 1 line)))
+    (when (> cstart stop)
+      (save-excursion
+        (goto-char cpos)
+        (re-search-forward "^[-+]")
+        (setq stop (line-number-at-pos))))
+    (save-excursion
+      (goto-char cpos)
+      (while (< (line-number-at-pos) stop)
+        (unless (string-match-p
+                 (if prior "\\+" "-")
+                 (buffer-substring (point) (+ (point) prefix)))
+          (cl-incf offset))
+        (forward-line)))
+    (list line offset)))
+
+(defun magit-diff-hunk-column (section visit-beginning)
+  (if (or (< (point)
+             (oref section content))
+          (and (not visit-beginning)
+               (save-excursion (beginning-of-line) (looking-at-p "-"))))
+      0
+    (max 0 (- (+ (current-column) 2)
+              (length (oref section value))))))
+
+(defun magit-diff-visit-directory (directory &optional other-window)
+  (if (equal (magit-toplevel directory)
+             (magit-toplevel))
+      (dired-jump other-window (concat directory "/."))
+    (let ((display-buffer-overriding-action
+           (if other-window
+               '(nil (inhibit-same-window t))
+             '(display-buffer-same-window))))
+      (magit-status-internal directory))))
+
+;;;; Scroll commands
+
+(defun magit-diff-show-or-scroll-up ()
+  "Update the commit or diff buffer for the thing at point.
+
+Either show the commit or stash at point in the appropriate
+buffer, or if that buffer is already being displayed in the
+current frame and contains information about that commit or
+stash, then instead scroll the buffer up.  If there is no
+commit or stash at point, then prompt for a commit."
+  (interactive)
+  (magit-diff-show-or-scroll 'scroll-up))
+
+(defun magit-diff-show-or-scroll-down ()
+  "Update the commit or diff buffer for the thing at point.
+
+Either show the commit or stash at point in the appropriate
+buffer, or if that buffer is already being displayed in the
+current frame and contains information about that commit or
+stash, then instead scroll the buffer down.  If there is no
+commit or stash at point, then prompt for a commit."
+  (interactive)
+  (magit-diff-show-or-scroll 'scroll-down))
+
+(defun magit-diff-show-or-scroll (fn)
+  (let (rev cmd buf win)
+    (cond
+     (magit-blame-mode
+      (setq rev (oref (magit-current-blame-chunk) orig-rev))
+      (setq cmd 'magit-show-commit)
+      (setq buf (magit-mode-get-buffer 'magit-revision-mode)))
+     ((derived-mode-p 'git-rebase-mode)
+      (save-excursion
+        (goto-char (line-beginning-position))
+        (--if-let (and git-rebase-line
+                       (looking-at git-rebase-line)
+                       (match-string 2))
+            (progn (setq rev it)
+                   (setq cmd 'magit-show-commit)
+                   (setq buf (magit-mode-get-buffer 'magit-revision-mode)))
+          (user-error "No commit on this line"))))
+     (t
+      (magit-section-case
+        (branch
+         (setq rev (magit-ref-maybe-qualify (oref it value)))
+         (setq cmd 'magit-show-commit)
+         (setq buf (magit-mode-get-buffer 'magit-revision-mode)))
+        (commit
+         (setq rev (oref it value))
+         (setq cmd 'magit-show-commit)
+         (setq buf (magit-mode-get-buffer 'magit-revision-mode)))
+        (stash
+         (setq rev (oref it value))
+         (setq cmd 'magit-stash-show)
+         (setq buf (magit-mode-get-buffer 'magit-stash-mode))))))
+    (if rev
+        (if (and buf
+                 (setq win (get-buffer-window buf))
+                 (with-current-buffer buf
+                   (and (equal rev (car magit-refresh-args))
+                        (equal (magit-rev-parse rev)
+                               magit-buffer-revision-hash))))
+            (with-selected-window win
+              (condition-case nil
+                  (funcall fn)
+                (error
+                 (goto-char (pcase fn
+                              (`scroll-up   (point-min))
+                              (`scroll-down (point-max)))))))
+          (let ((magit-display-buffer-noselect t))
+            (if (eq cmd 'magit-show-commit)
+                (apply #'magit-show-commit rev (magit-show-commit--arguments))
+              (funcall cmd rev))))
+      (call-interactively #'magit-show-commit))))
+
+;;; Diff Mode
+
+(defvar magit-diff-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map magit-mode-map)
+    (define-key map "\C-c\C-d" 'magit-diff-while-committing)
+    (define-key map "\C-c\C-b" 'magit-go-backward)
+    (define-key map "\C-c\C-f" 'magit-go-forward)
+    (define-key map "\s" 'scroll-up)
+    (define-key map "\d" 'scroll-down)
+    (define-key map "j" 'magit-jump-to-diffstat-or-diff)
+    (define-key map [remap write-file] 'magit-patch-save)
+    map)
+  "Keymap for `magit-diff-mode'.")
+
+(define-derived-mode magit-diff-mode magit-mode "Magit Diff"
+  "Mode for looking at a Git diff.
+
+This mode is documented in info node `(magit)Diff Buffer'.
+
+\\<magit-mode-map>\
+Type \\[magit-refresh] to refresh the current buffer.
+Type \\[magit-section-toggle] to expand or hide the section at point.
+Type \\[magit-visit-thing] to visit the hunk or file at point.
+
+Staging and applying changes is documented in info node
+`(magit)Staging and Unstaging' and info node `(magit)Applying'.
+
+\\<magit-hunk-section-map>Type \
+\\[magit-apply] to apply the change at point, \
+\\[magit-stage] to stage,
+\\[magit-unstage] to unstage, \
+\\[magit-discard] to discard, or \
+\\[magit-reverse] to reverse it.
+
+\\{magit-diff-mode-map}"
+  :group 'magit-diff
+  (hack-dir-local-variables-non-file-buffer)
+  (setq imenu-prev-index-position-function
+        'magit-imenu--diff-prev-index-position-function)
+  (setq imenu-extract-index-name-function
+        'magit-imenu--diff-extract-index-name-function)
+  (setq-local bookmark-make-record-function
+              'magit-bookmark--diff-make-record))
+
+(defun magit-diff-refresh-buffer (rev-or-range const _args files)
+  "Refresh the current `magit-diff-mode' buffer.
+
+In such buffers the buffer-local value of `magit-refresh-args'
+has the same form as the arguments of this function.  The value
+is set in `magit-mode-setup'."
+  (magit-set-header-line-format
+   (if (member "--no-index" const)
+       (apply #'format "Differences between %s and %s" files)
+     (concat (if rev-or-range
+                 (if (string-match-p "\\(\\.\\.\\|\\^-\\)"
+                                     rev-or-range)
+                     (format "Changes in %s" rev-or-range)
+                   (format "Changes from %s to working tree" rev-or-range))
+               (if (member "--cached" const)
+                   "Staged changes"
+                 "Unstaged changes"))
+             (pcase (length files)
+               (0)
+               (1 (concat " in file " (car files)))
+               (_ (concat " in files "
+                          (mapconcat #'identity files ", ")))))))
+  (magit-insert-section (diffbuf)
+    (magit-run-section-hook 'magit-diff-sections-hook rev-or-range)))
+
+(defun magit-insert-diff (rev-or-range)
+  "Insert the diff into this `magit-diff-mode' buffer."
+  (let ((magit-git-global-arguments
+         (remove "--literal-pathspecs" magit-git-global-arguments)))
+    (magit-git-wash #'magit-diff-wash-diffs
+      "diff" rev-or-range "-p" "--no-prefix"
+      (and (member "--stat" (nth 2 magit-refresh-args)) "--numstat")
+      (nth 1 magit-refresh-args)
+      (nth 2 magit-refresh-args) "--"
+      (nth 3 magit-refresh-args))))
+
+(defvar magit-file-section-map
+  (let ((map (make-sparse-keymap)))
+    (unless (featurep 'jkl)
+      (define-key map (kbd "C-j") 'magit-diff-visit-file-worktree))
+    (define-key map [C-return] 'magit-diff-visit-file-worktree)
+    (define-key map [remap magit-visit-thing]      'magit-diff-visit-file)
+    (define-key map [remap magit-delete-thing]     'magit-discard)
+    (define-key map [remap magit-revert-no-commit] 'magit-reverse)
+    (define-key map "a" 'magit-apply)
+    (define-key map "C" 'magit-commit-add-log)
+    (define-key map "s" 'magit-stage)
+    (define-key map "u" 'magit-unstage)
+    (define-key map "&" 'magit-do-async-shell-command)
+    (define-key map "\C-c\C-t" 'magit-diff-trace-definition)
+    (define-key map "\C-c\C-e" 'magit-diff-edit-hunk-commit)
+    map)
+  "Keymap for `file' sections.")
+
+(defvar magit-hunk-section-map
+  (let ((map (make-sparse-keymap)))
+    (unless (featurep 'jkl)
+      (define-key map (kbd "C-j") 'magit-diff-visit-file-worktree))
+    (define-key map [C-return] 'magit-diff-visit-file-worktree)
+    (define-key map [remap magit-visit-thing]      'magit-diff-visit-file)
+    (define-key map [remap magit-delete-thing]     'magit-discard)
+    (define-key map [remap magit-revert-no-commit] 'magit-reverse)
+    (define-key map "a" 'magit-apply)
+    (define-key map "C" 'magit-commit-add-log)
+    (define-key map "s" 'magit-stage)
+    (define-key map "u" 'magit-unstage)
+    (define-key map "&" 'magit-do-async-shell-command)
+    (define-key map "\C-c\C-t" 'magit-diff-trace-definition)
+    (define-key map "\C-c\C-e" 'magit-diff-edit-hunk-commit)
+    map)
+  "Keymap for `hunk' sections.")
+
+(defconst magit-diff-headline-re
+  (concat "^\\(@@@?\\|diff\\|Submodule\\|"
+          "\\* Unmerged path\\|merged\\|changed in both\\|"
+          "added in remote\\|removed in remote\\)"))
+
+(defconst magit-diff-statline-re
+  (concat "^ ?"
+          "\\(.*\\)"     ; file
+          "\\( +| +\\)"  ; separator
+          "\\([0-9]+\\|Bin\\(?: +[0-9]+ -> [0-9]+ bytes\\)?$\\) ?"
+          "\\(\\+*\\)"   ; add
+          "\\(-*\\)$"))  ; del
+
+(defun magit-diff-wash-diffs (args &optional limit)
+  (when (member "--stat" args)
+    (magit-diff-wash-diffstat))
+  (when (re-search-forward magit-diff-headline-re limit t)
+    (goto-char (line-beginning-position))
+    (magit-wash-sequence (apply-partially 'magit-diff-wash-diff args))
+    (insert ?\n)))
+
+(defun magit-jump-to-diffstat-or-diff ()
+  "Jump to the diffstat or diff.
+When point is on a file inside the diffstat section, then jump
+to the respective diff section, otherwise jump to the diffstat
+section or a child thereof."
+  (interactive)
+  (--if-let (magit-get-section
+             (append (magit-section-case
+                       ([file diffstat] `((file . ,(oref it value))))
+                       (file `((file . ,(oref it value)) (diffstat)))
+                       (t '((diffstat))))
+                     (magit-section-ident magit-root-section)))
+      (magit-section-goto it)
+    (user-error "No diffstat in this buffer")))
+
+(defun magit-diff-wash-diffstat ()
+  (let (heading (beg (point)))
+    (when (re-search-forward "^ ?\\([0-9]+ +files? change[^\n]*\n\\)" nil t)
+      (setq heading (match-string 1))
+      (magit-delete-match)
+      (goto-char beg)
+      (magit-insert-section (diffstat)
+        (insert (propertize heading 'face 'magit-diff-file-heading))
+        (magit-insert-heading)
+        (let (files)
+          (while (looking-at "^[-0-9]+\t[-0-9]+\t\\(.+\\)$")
+            (push (magit-decode-git-path
+                   (let ((f (match-string 1)))
+                     (if (string-match " => " f)
+                         (substring f (match-end 0))
+                       f)))
+                  files)
+            (magit-delete-line))
+          (setq files (nreverse files))
+          (while (looking-at magit-diff-statline-re)
+            (magit-bind-match-strings (file sep cnt add del) nil
+              (magit-delete-line)
+              (when (string-match " +$" file)
+                (setq sep (concat (match-string 0 file) sep))
+                (setq file (substring file 0 (match-beginning 0))))
+              (let ((le (length file)) ld)
+                (setq file (magit-decode-git-path file))
+                (setq ld (length file))
+                (when (> le ld)
+                  (setq sep (concat (make-string (- le ld) ?\s) sep))))
+              (magit-insert-section (file (pop files))
+                (insert (propertize file 'face 'magit-filename) sep cnt " ")
+                (when add
+                  (insert (propertize add 'face 'magit-diffstat-added)))
+                (when del
+                  (insert (propertize del 'face 'magit-diffstat-removed)))
+                (insert "\n")))))
+        (if (looking-at "^$") (forward-line) (insert "\n"))))))
+
+(defun magit-diff-wash-diff (args)
+  (cond
+   ((looking-at "^Submodule")
+    (magit-diff-wash-submodule))
+   ((looking-at "^\\* Unmerged path \\(.*\\)")
+    (let ((file (magit-decode-git-path (match-string 1))))
+      (magit-delete-line)
+      (unless (and (derived-mode-p 'magit-status-mode)
+                   (not (member "--cached" args)))
+        (magit-insert-section (file file)
+          (insert (propertize
+                   (format "unmerged   %s%s" file
+                           (pcase (cddr (car (magit-file-status file)))
+                             (`(?D ?D) " (both deleted)")
+                             (`(?D ?U) " (deleted by us)")
+                             (`(?U ?D) " (deleted by them)")
+                             (`(?A ?A) " (both added)")
+                             (`(?A ?U) " (added by us)")
+                             (`(?U ?A) " (added by them)")
+                             (`(?U ?U) "")))
+                   'face 'magit-diff-file-heading))
+          (insert ?\n))))
+    t)
+   ((looking-at (concat "^\\(merged\\|changed in both\\|"
+                        "added in remote\\|removed in remote\\)"))
+    (let ((status (pcase (match-string 1)
+                    ("merged" "merged")
+                    ("changed in both" "conflict")
+                    ("added in remote" "new file")
+                    ("removed in remote" "deleted")))
+          file orig base modes)
+      (magit-delete-line)
+      (while (looking-at
+              "^  \\([^ ]+\\) +[0-9]\\{6\\} \\([a-z0-9]\\{40\\}\\) \\(.+\\)$")
+        (magit-bind-match-strings (side _blob name) nil
+          (pcase side
+            ("result" (setq file name))
+            ("our"    (setq orig name))
+            ("their"  (setq file name))
+            ("base"   (setq base name))))
+        (magit-delete-line))
+      (when orig (setq orig (magit-decode-git-path orig)))
+      (when file (setq file (magit-decode-git-path file)))
+      (magit-diff-insert-file-section (or file base) orig status modes nil)))
+   ((looking-at
+     "^diff --\\(?:\\(git\\) \\(?:\\(.+?\\) \\2\\)?\\|\\(cc\\|combined\\) \\(.+\\)\\)")
+    (let ((status (cond ((equal (match-string 1) "git")        "modified")
+                        ((derived-mode-p 'magit-revision-mode) "resolved")
+                        (t                                     "unmerged")))
+          (file (or (match-string 2) (match-string 4)))
+          (beg (point))
+          orig header modes)
+      (save-excursion
+        (forward-line 1)
+        (setq header (buffer-substring
+                      beg (if (re-search-forward magit-diff-headline-re nil t)
+                              (match-beginning 0)
+                            (point-max)))))
+      (magit-delete-line)
+      (while (not (or (eobp) (looking-at magit-diff-headline-re)))
+        (if (looking-at "^old mode \\([^\n]+\\)\nnew mode \\([^\n]+\\)\n")
+            (progn (setq modes (match-string 0))
+                   (magit-delete-match))
+          (cond
+           ((looking-at "^--- \\([^/].*?\\)\t?$") ; i.e. not /dev/null
+            (setq orig (match-string 1)))
+           ((looking-at "^\\+\\+\\+ \\([^/].*?\\)\t?$")
+            (setq file (match-string 1)))
+           ((looking-at "^\\(copy\\|rename\\) from \\(.+\\)$")
+            (setq orig (match-string 2)))
+           ((looking-at "^\\(copy\\|rename\\) to \\(.+\\)$")
+            (setq file (match-string 2))
+            (setq status (if (equal (match-string 1) "copy") "new file" "renamed")))
+           ((looking-at "^\\(new file\\|deleted\\)")
+            (setq status (match-string 1))))
+          (magit-delete-line)))
+      (when orig
+        (setq orig (magit-decode-git-path orig)))
+      (setq file (magit-decode-git-path file))
+      ;; KLUDGE `git-log' ignores `--no-prefix' when `-L' is used.
+      (when (and (derived-mode-p 'magit-log-mode)
+                 (--first (string-match-p "\\`-L" it)
+                          (nth 1 magit-refresh-args)))
+        (setq file (substring file 2))
+        (when orig
+          (setq orig (substring orig 2))))
+      (magit-diff-insert-file-section file orig status modes header)))))
+
+(defun magit-diff-insert-file-section (file orig status modes header)
+  (magit-insert-section section
+    (file file (or (equal status "deleted")
+                   (derived-mode-p 'magit-status-mode)))
+    (insert (propertize (format "%-10s %s\n" status
+                                (if (or (not orig) (equal orig file))
+                                    file
+                                  (format "%s -> %s" orig file)))
+                        'face 'magit-diff-file-heading))
+    (magit-insert-heading)
+    (unless (equal orig file)
+      (oset section source orig))
+    (oset section header header)
+    (when modes
+      (magit-insert-section (hunk)
+        (insert modes)))
+    (magit-wash-sequence #'magit-diff-wash-hunk)))
+
+(defun magit-diff-wash-submodule ()
+  ;; See `show_submodule_summary' in submodule.c and "this" commit.
+  (when (looking-at "^Submodule \\([^ ]+\\)")
+    (let ((module (match-string 1))
+          untracked modified)
+      (when (looking-at "^Submodule [^ ]+ contains untracked content$")
+        (magit-delete-line)
+        (setq untracked t))
+      (when (looking-at "^Submodule [^ ]+ contains modified content$")
+        (magit-delete-line)
+        (setq modified t))
+      (cond
+       ((and (looking-at "^Submodule \\([^ ]+\\) \\([^ :]+\\)\\( (rewind)\\)?:$")
+             (equal (match-string 1) module))
+        (magit-bind-match-strings (_module range rewind) nil
+          (magit-delete-line)
+          (while (looking-at "^  \\([<>]\\) \\(.+\\)$")
+            (magit-delete-line))
+          (when rewind
+            (setq range (replace-regexp-in-string "[^.]\\(\\.\\.\\)[^.]"
+                                                  "..." range t t 1)))
+          (magit-insert-section (file module t)
+            (magit-insert-heading
+              (propertize (concat "modified   " module)
+                          'face 'magit-diff-file-heading)
+              " ("
+              (cond (rewind "rewind")
+                    ((string-match-p "\\.\\.\\." range) "non-ff")
+                    (t "new commits"))
+              (and (or modified untracked)
+                   (concat ", "
+                           (and modified "modified")
+                           (and modified untracked " and ")
+                           (and untracked "untracked")
+                           " content"))
+              ")")
+            (let ((default-directory
+                    (file-name-as-directory
+                     (expand-file-name module (magit-toplevel)))))
+              (magit-git-wash (apply-partially 'magit-log-wash-log 'module)
+                "log" "--oneline" "--left-right" range)
+              (delete-char -1)))))
+       ((and (looking-at "^Submodule \\([^ ]+\\) \\([^ ]+\\) (\\([^)]+\\))$")
+             (equal (match-string 1) module))
+        (magit-bind-match-strings (_module _range msg) nil
+          (magit-delete-line)
+          (magit-insert-section (file module)
+            (magit-insert-heading
+              (propertize (concat "submodule  " module)
+                          'face 'magit-diff-file-heading)
+              " (" msg ")"))))
+       (t
+        (magit-insert-section (file module)
+          (magit-insert-heading
+            (propertize (concat "modified   " module)
+                        'face 'magit-diff-file-heading)
+            " ("
+            (and modified "modified")
+            (and modified untracked " and ")
+            (and untracked "untracked")
+            " content)")))))))
+
+(defun magit-diff-wash-hunk ()
+  (when (looking-at "^@\\{2,\\} \\(.+?\\) @\\{2,\\}\\(?: \\(.*\\)\\)?")
+    (let ((heading (match-string 0))
+          (value (cons (match-string 2) (split-string (match-string 1)))))
+      (magit-delete-line)
+      (magit-insert-section it (hunk value)
+        (insert (propertize (concat heading "\n") 'face 'magit-diff-hunk-heading))
+        (magit-insert-heading)
+        (while (not (or (eobp) (looking-at "^[^-+\s\\]")))
+          (forward-line))
+        (oset it end (point))
+        (oset it washer 'magit-diff-paint-hunk)))
+    t))
+
+(defun magit-diff-expansion-threshold (section)
+  "Keep new diff sections collapsed if washing takes too long."
+  (and (magit-file-section-p section)
+       (> (float-time (time-subtract (current-time) magit-refresh-start-time))
+          magit-diff-expansion-threshold)
+       'hide))
+
+;;; Revision Mode
+
+(define-derived-mode magit-revision-mode magit-diff-mode "Magit Rev"
+  "Mode for looking at a Git commit.
+
+This mode is documented in info node `(magit)Revision Buffer'.
+
+\\<magit-mode-map>\
+Type \\[magit-refresh] to refresh the current buffer.
+Type \\[magit-section-toggle] to expand or hide the section at point.
+Type \\[magit-visit-thing] to visit the hunk or file at point.
+
+Staging and applying changes is documented in info node
+`(magit)Staging and Unstaging' and info node `(magit)Applying'.
+
+\\<magit-hunk-section-map>Type \
+\\[magit-apply] to apply the change at point, \
+\\[magit-stage] to stage,
+\\[magit-unstage] to unstage, \
+\\[magit-discard] to discard, or \
+\\[magit-reverse] to reverse it.
+
+\\{magit-revision-mode-map}"
+  :group 'magit-revision
+  (hack-dir-local-variables-non-file-buffer)
+  (setq-local bookmark-make-record-function
+              'magit-bookmark--revision-make-record))
+
+(defun magit-revision-refresh-buffer (rev __const _args files)
+  (magit-set-header-line-format
+   (concat (capitalize (magit-object-type rev))
+           " "
+           rev
+           (pcase (length files)
+             (0)
+             (1 (concat " limited to file " (car files)))
+             (_ (concat " limited to files "
+                        (mapconcat #'identity files ", "))))))
+  (setq magit-buffer-revision-hash (magit-rev-parse rev))
+  (magit-insert-section (commitbuf)
+    (magit-run-section-hook 'magit-revision-sections-hook rev)))
+
+(defun magit-insert-revision-diff (rev)
+  "Insert the diff into this `magit-revision-mode' buffer."
+  (let ((magit-git-global-arguments
+         (remove "--literal-pathspecs" magit-git-global-arguments)))
+    ;; Before v2.2.0, "--format=" did not mean "no output".
+    ;; Instead the default format was used.  So use "--format=%n"
+    ;; and then delete the empty lines.
+    (magit-git-wash (lambda (args)
+                      (delete-region (point) (progn (forward-line 3) (point)))
+                      (magit-diff-wash-diffs args))
+      "show" "-p" "--cc" "--format=%n" "--no-prefix"
+      (and (member "--stat" (nth 2 magit-refresh-args)) "--numstat")
+      (nth 2 magit-refresh-args) (concat rev "^{commit}") "--"
+      (nth 3 magit-refresh-args))))
+
+(defun magit-insert-revision-tag (rev)
+  "Insert tag message and headers into a revision buffer.
+This function only inserts anything when `magit-show-commit' is
+called with a tag as argument, when that is called with a commit
+or a ref which is not a branch, then it inserts nothing."
+  (when (equal (magit-object-type rev) "tag")
+    (magit-insert-section (taginfo)
+      (let ((beg (point)))
+        ;; "git verify-tag -v" would output what we need, but the gpg
+        ;; output is send to stderr and we have no control over the
+        ;; order in which stdout and stderr are inserted, which would
+        ;; make parsing hard.  We are forced to use "git cat-file tag"
+        ;; instead, which inserts the signature instead of verifying
+        ;; it.  We remove that later and then insert the verification
+        ;; output using "git verify-tag" (without the "-v").
+        (magit-git-insert "cat-file" "tag" rev)
+        (goto-char beg)
+        (forward-line 3)
+        (delete-region beg (point)))
+      (looking-at "^tagger \\([^<]+\\) <\\([^>]+\\)")
+      (let ((heading (format "Tagger: %s <%s>"
+                             (match-string 1)
+                             (match-string 2))))
+        (magit-delete-line)
+        (insert (propertize heading 'face 'magit-section-secondary-heading)))
+      (magit-insert-heading)
+      (if (re-search-forward "-----BEGIN PGP SIGNATURE-----" nil t)
+          (progn
+            (let ((beg (match-beginning 0)))
+              (re-search-forward "-----END PGP SIGNATURE-----")
+              (delete-region beg (point)))
+            (insert ?\n)
+            (process-file magit-git-executable nil t nil "verify-tag" rev))
+        (goto-char (point-max)))
+      (insert ?\n))))
+
+(defvar magit-commit-message-section-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [remap magit-visit-thing] 'magit-show-commit)
+    map)
+  "Keymap for `commit-message' sections.")
+
+(defun magit-insert-revision-message (rev)
+  "Insert the commit message into a revision buffer."
+  (magit-insert-section section (commit-message)
+    (oset section heading-highlight-face 'magit-diff-hunk-heading-highlight)
+    (let ((beg (point)))
+      (insert (save-excursion ; The risky var query can move point.
+                (with-temp-buffer
+                  (magit-rev-insert-format "%B" rev)
+                  (magit-revision--wash-message))))
+      (if (= (point) (+ beg 2))
+          (progn (backward-delete-char 2)
+                 (insert "(no message)\n"))
+        (goto-char beg)
+        (save-excursion
+          (while (search-forward "\r\n" nil t) ; Remove trailing CRs.
+            (delete-region (match-beginning 0) (1+ (match-beginning 0)))))
+        (when magit-revision-use-hash-sections
+          (save-excursion
+            (while (not (eobp))
+              (re-search-forward "\\_<" nil 'move)
+              (let ((beg (point)))
+                (re-search-forward "\\_>" nil t)
+                (when (> (point) beg)
+                  (let ((text (buffer-substring-no-properties beg (point))))
+                    (when (pcase magit-revision-use-hash-sections
+                            (`quickest ; false negatives and positives
+                             (and (>= (length text) 7)
+                                  (string-match-p "[0-9]" text)
+                                  (string-match-p "[a-z]" text)))
+                            (`quicker  ; false negatives (number-less hashes)
+                             (and (>= (length text) 7)
+                                  (string-match-p "[0-9]" text)
+                                  (magit-rev-verify-commit text)))
+                            (`quick    ; false negatives (short hashes)
+                             (and (>= (length text) 7)
+                                  (magit-rev-verify-commit text)))
+                            (`slow
+                             (magit-rev-verify-commit text)))
+                      (put-text-property beg (point) 'face 'magit-hash)
+                      (let ((end (point)))
+                        (goto-char beg)
+                        (magit-insert-section (commit text)
+                          (goto-char end))))))))))
+        (save-excursion
+          (forward-line)
+          (add-face-text-property beg (point) 'magit-diff-hunk-heading)
+          (magit-insert-heading))
+        (when magit-diff-highlight-keywords
+          (save-excursion
+            (while (re-search-forward "\\[[^[]*\\]" nil t)
+              (let ((beg (match-beginning 0))
+                    (end (match-end 0)))
+                (put-text-property
+                 beg end 'face
+                 (if-let ((face (get-text-property beg 'face)))
+                     (list face 'magit-keyword)
+                   'magit-keyword))))))
+        (goto-char (point-max))))))
+
+(defun magit-insert-revision-notes (rev)
+  "Insert commit notes into a revision buffer."
+  (let* ((var "core.notesRef")
+         (def (or (magit-get var) "refs/notes/commits")))
+    (dolist (ref (or (magit-list-active-notes-refs)))
+      (magit-insert-section section (notes ref (not (equal ref def)))
+        (oset section heading-highlight-face 'magit-diff-hunk-heading-highlight)
+        (let ((beg (point)))
+          (insert (with-temp-buffer
+                    (magit-git-insert "-c" (concat "core.notesRef=" ref)
+                                      "notes" "show" rev)
+                    (magit-revision--wash-message)))
+          (if (= (point) beg)
+              (magit-cancel-section)
+            (goto-char beg)
+            (end-of-line)
+            (insert (format " (%s)"
+                            (propertize (if (string-prefix-p "refs/notes/" ref)
+                                            (substring ref 11)
+                                          ref)
+                                        'face 'magit-refname)))
+            (forward-char)
+            (add-face-text-property beg (point) 'magit-diff-hunk-heading)
+            (magit-insert-heading)
+            (goto-char (point-max))
+            (insert ?\n)))))))
+
+(defun magit-revision--wash-message ()
+  (let ((major-mode 'git-commit-mode))
+    (hack-dir-local-variables)
+    (hack-local-variables-apply))
+  (unless (memq git-commit-major-mode '(nil text-mode))
+    (funcall git-commit-major-mode)
+    (font-lock-ensure))
+  (buffer-string))
+
+(defun magit-insert-revision-headers (rev)
+  "Insert headers about the commit into a revision buffer."
+  (magit-insert-section (headers)
+    ;; Before v2.2.0, "%D" was not supported.
+    (--when-let (magit-rev-format "%d" rev "--decorate=full")
+      (insert (magit-format-ref-labels it) ?\s))
+    (insert (propertize (magit-rev-parse (concat rev "^{commit}"))
+                        'face 'magit-hash))
+    (magit-insert-heading)
+    (let ((beg (point)))
+      (magit-rev-insert-format magit-revision-headers-format rev)
+      (magit-insert-revision-gravatars rev beg))
+    (when magit-revision-insert-related-refs
+      (dolist (parent (magit-commit-parents rev))
+        (magit-insert-section (commit parent)
+          (let ((line (magit-rev-format "%h %s" parent)))
+            (string-match "^\\([^ ]+\\) \\(.*\\)" line)
+            (magit-bind-match-strings (hash msg) line
+              (insert "Parent:     ")
+              (insert (propertize hash 'face 'magit-hash))
+              (insert " " msg "\n")))))
+      (when-let ((merged (magit-list-merged-branches rev)))
+        (insert "Merged:    ")
+        (let (branch)
+          (while (and (< (+ (- (point) (line-beginning-position))
+                            (length (car merged)) 9)
+                         (window-width))
+                      (setq branch (pop merged)))
+            (insert ?\s)
+            (magit-insert-section (branch branch)
+              (insert (propertize branch 'face 'magit-branch-local)))))
+        (when merged
+          (insert (format " (%s more)" (length merged))))
+        (insert ?\n))
+      (when-let ((containing (magit-list-containing-branches rev)))
+        (insert "Containing:")
+        (let (branch)
+          (while (and (< (+ (- (point) (line-beginning-position))
+                            (length (car containing)) 9)
+                         (window-width))
+                      (setq branch (pop containing)))
+            (insert ?\s)
+            (magit-insert-section (branch branch)
+              (insert (propertize branch 'face 'magit-branch-local)))))
+        (when containing
+          (insert (format " (%s more)" (length containing))))
+        (insert ?\n))
+      (when-let ((follows (magit-get-current-tag rev t)))
+        (let ((tag (car  follows))
+              (cnt (cadr follows)))
+          (magit-insert-section (tag tag)
+            (insert (format "Follows:    %s (%s)\n"
+                            (propertize tag 'face 'magit-tag)
+                            (propertize (number-to-string cnt)
+                                        'face 'magit-branch-local))))))
+      (when-let ((precedes (magit-get-next-tag rev t)))
+        (let ((tag (car  precedes))
+              (cnt (cadr precedes)))
+          (magit-insert-section (tag tag)
+            (insert (format "Precedes:   %s (%s)\n"
+                            (propertize tag 'face 'magit-tag)
+                            (propertize (number-to-string cnt)
+                                        'face 'magit-tag))))))
+      (insert ?\n))))
+
+(defun magit-insert-revision-gravatars (rev beg)
+  (when (and magit-revision-show-gravatars
+             (window-system))
+    (require 'gravatar)
+    (pcase-let ((`(,author . ,committer) magit-revision-show-gravatars))
+      (--when-let (magit-rev-format "%aE" rev)
+        (magit-insert-revision-gravatar beg rev it author))
+      (--when-let (magit-rev-format "%cE" rev)
+        (magit-insert-revision-gravatar beg rev it committer)))))
+
+(defun magit-insert-revision-gravatar (beg rev email regexp)
+  (save-excursion
+    (goto-char beg)
+    (when (re-search-forward regexp nil t)
+      (let* ((column   (length (match-string 0)))
+             (font-obj (query-font (font-at (point) (get-buffer-window))))
+             (size     (* 2 (+ (aref font-obj 4)
+                               (aref font-obj 5))))
+             (align-to (+ column
+                          (ceiling (/ size (aref font-obj 7) 1.0))
+                          1))
+             (gravatar-size (- size 2)))
+        (gravatar-retrieve email 'magit-insert-revision-gravatar-cb
+                           (list rev (point-marker) align-to column))))))
+
+(defun magit-insert-revision-gravatar-cb (image rev marker align-to column)
+  (unless (eq image 'error)
+    (when-let ((buffer (marker-buffer marker)))
+      (with-current-buffer buffer
+        (save-excursion
+          (goto-char marker)
+          ;; The buffer might display another revision by now or
+          ;; it might have been refreshed, in which case another
+          ;; process might already have inserted the image.
+          (when (and (equal rev (car magit-refresh-args))
+                     (not (eq (car-safe
+                               (car-safe
+                                (get-text-property (point) 'display)))
+                              'image)))
+            (let ((top `((,@image :ascent center :relief 1)
+                         (slice 0.0 0.0 1.0 0.5)))
+                  (bot `((,@image :ascent center :relief 1)
+                         (slice 0.0 0.5 1.0 1.0)))
+                  (align `((space :align-to ,align-to))))
+              (when magit-revision-use-gravatar-kludge
+                (cl-rotatef top bot))
+              (let ((inhibit-read-only t))
+                (insert (propertize " " 'display top))
+                (insert (propertize " " 'display align))
+                (forward-line)
+                (forward-char column)
+                (insert (propertize " " 'display bot))
+                (insert (propertize " " 'display align))))))))))
+
+;;; Diff Sections
+
+(defvar magit-unstaged-section-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [remap magit-visit-thing]  'magit-diff-unstaged)
+    (define-key map [remap magit-delete-thing] 'magit-discard)
+    (define-key map "s" 'magit-stage)
+    (define-key map "u" 'magit-unstage)
+    map)
+  "Keymap for the `unstaged' section.")
+
+(magit-define-section-jumper magit-jump-to-unstaged "Unstaged changes" unstaged)
+
+(defun magit-insert-unstaged-changes ()
+  "Insert section showing unstaged changes."
+  (magit-insert-section (unstaged)
+    (magit-insert-heading "Unstaged changes:")
+    (magit-git-wash #'magit-diff-wash-diffs
+      "diff" magit-diff-section-arguments "--no-prefix"
+      "--" magit-diff-section-file-args)))
+
+(defvar magit-staged-section-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [remap magit-visit-thing]      'magit-diff-staged)
+    (define-key map [remap magit-delete-thing]     'magit-discard)
+    (define-key map [remap magit-revert-no-commit] 'magit-reverse)
+    (define-key map "s" 'magit-stage)
+    (define-key map "u" 'magit-unstage)
+    map)
+  "Keymap for the `staged' section.")
+
+(magit-define-section-jumper magit-jump-to-staged "Staged changes" staged)
+
+(defun magit-insert-staged-changes ()
+  "Insert section showing staged changes."
+  ;; Avoid listing all files as deleted when visiting a bare repo.
+  (unless (magit-bare-repo-p)
+    (magit-insert-section (staged)
+      (magit-insert-heading "Staged changes:")
+      (magit-git-wash #'magit-diff-wash-diffs
+        "diff" "--cached" magit-diff-section-arguments "--no-prefix"
+        "--" magit-diff-section-file-args))))
+
+;;; Diff Type
+
+(defun magit-diff-type (&optional section)
+  "Return the diff type of SECTION.
+
+The returned type is one of the symbols `staged', `unstaged',
+`committed', or `undefined'.  This type serves a similar purpose
+as the general type common to all sections (which is stored in
+the `type' slot of the corresponding `magit-section' struct) but
+takes additional information into account.  When the SECTION
+isn't related to diffs and the buffer containing it also isn't
+a diff-only buffer, then return nil.
+
+Currently the type can also be one of `tracked' and `untracked'
+but these values are not handled explicitly everywhere they
+should be and a possible fix could be to just return nil here.
+
+The section has to be a `diff' or `hunk' section, or a section
+whose children are of type `diff'.  If optional SECTION is nil,
+return the diff type for the current section.  In buffers whose
+major mode is `magit-diff-mode' SECTION is ignored and the type
+is determined using other means.  In `magit-revision-mode'
+buffers the type is always `committed'.
+
+Do not confuse this with `magit-diff-scope' (which see)."
+  (--when-let (or section (magit-current-section))
+    (cond ((derived-mode-p 'magit-revision-mode 'magit-stash-mode) 'committed)
+          ((derived-mode-p 'magit-diff-mode)
+           (let ((range (nth 0 magit-refresh-args))
+                 (const (nth 1 magit-refresh-args)))
+             (cond ((member "--no-index" const) 'undefined)
+                   ((or (not range)
+                        (magit-rev-eq range "HEAD"))
+                    (if (member "--cached" const)
+                        'staged
+                      'unstaged))
+                   ((member "--cached" const)
+                    (if (magit-rev-head-p range)
+                        'staged
+                      'undefined)) ; i.e. committed and staged
+                   (t 'committed))))
+          ((derived-mode-p 'magit-status-mode)
+           (let ((stype (oref it type)))
+             (if (memq stype '(staged unstaged tracked untracked))
+                 stype
+               (pcase stype
+                 (`file (let* ((parent (oref it parent))
+                               (type   (oref parent type)))
+                          (if (eq type 'file)
+                              (magit-diff-type parent)
+                            type)))
+                 (`hunk (-> it
+                            (oref parent)
+                            (oref parent)
+                            (oref type)))))))
+          ((derived-mode-p 'magit-log-mode)
+           (if (or (and (magit-section-match 'commit section)
+                        (oref section children))
+                   (magit-section-match [* file commit] section))
+               'committed
+           'undefined))
+          (t 'undefined))))
+
+(cl-defun magit-diff-scope (&optional (section nil ssection) strict)
+  "Return the diff scope of SECTION or the selected section(s).
+
+A diff's \"scope\" describes what part of a diff is selected, it is
+a symbol, one of `region', `hunk', `hunks', `file', `files', or
+`list'.  Do not confuse this with the diff \"type\", as returned by
+`magit-diff-type'.
+
+If optional SECTION is non-nil, then return the scope of that,
+ignoring the sections selected by the region.  Otherwise return
+the scope of the current section, or if the region is active and
+selects a valid group of diff related sections, the type of these
+sections, i.e. `hunks' or `files'.  If SECTION, or if that is nil
+the current section, is a `hunk' section; and the region region
+starts and ends inside the body of a that section, then the type
+is `region'.  If the region is empty after a mouse click, then
+`hunk' is returned instead of `region'.
+
+If optional STRICT is non-nil, then return nil if the diff type of
+the section at point is `untracked' or the section at point is not
+actually a `diff' but a `diffstat' section."
+  (let ((siblings (and (not ssection) (magit-region-sections nil t))))
+    (setq section (or section (car siblings) (magit-current-section)))
+    (when (and section
+               (or (not strict)
+                   (and (not (eq (magit-diff-type section) 'untracked))
+                        (not (eq (--when-let (oref section parent)
+                                   (oref it type))
+                                 'diffstat)))))
+      (pcase (list (oref section type)
+                   (and siblings t)
+                   (magit-diff-use-hunk-region-p)
+                   ssection)
+        (`(hunk nil   t  ,_)
+         (if (magit-section-internal-region-p section) 'region 'hunk))
+        (`(hunk   t   t nil) 'hunks)
+        (`(hunk  ,_  ,_  ,_) 'hunk)
+        (`(file   t   t nil) 'files)
+        (`(file  ,_  ,_  ,_) 'file)
+        (`(,(or `staged `unstaged `untracked)
+           nil ,_ ,_) 'list)))))
+
+(defun magit-diff-use-hunk-region-p ()
+  (and (region-active-p)
+       (not (and (if (version< emacs-version "25.1")
+                     (eq this-command 'mouse-drag-region)
+                   ;; TODO implement this from first principals
+                   ;; currently it's trial-and-error
+                   (or (eq this-command 'mouse-drag-region)
+                       (eq last-command 'mouse-drag-region)
+                       ;; When another window was previously
+                       ;; selected then the last-command is
+                       ;; some byte-code function.
+                       (byte-code-function-p last-command)))
+                 (eq (region-end) (region-beginning))))))
+
+;;; Diff Highlight
+
+(defun magit-diff-unhighlight (section selection)
+  "Remove the highlighting of the diff-related SECTION."
+  (when (magit-hunk-section-p section)
+    (magit-diff-paint-hunk section selection nil)
+    t))
+
+(defun magit-diff-highlight (section selection)
+  "Highlight the diff-related SECTION.
+If SECTION is not a diff-related section, then do nothing and
+return nil.  If SELECTION is non-nil, then it is a list of sections
+selected by the region, including SECTION.  All of these sections
+are highlighted."
+  (if (and (magit-section-match 'commit section)
+           (oref section children))
+      (progn (if selection
+                 (dolist (section selection)
+                   (magit-diff-highlight-list section selection))
+               (magit-diff-highlight-list section))
+             t)
+    (when-let ((scope (magit-diff-scope section t)))
+      (cond ((eq scope 'region)
+             (magit-diff-paint-hunk section selection t))
+            (selection
+             (dolist (section selection)
+               (magit-diff-highlight-recursive section selection)))
+            (t
+             (magit-diff-highlight-recursive section)))
+      t)))
+
+(defun magit-diff-highlight-recursive (section &optional selection)
+  (pcase (magit-diff-scope section)
+    (`list (magit-diff-highlight-list section selection))
+    (`file (magit-diff-highlight-file section selection))
+    (`hunk (magit-diff-highlight-heading section selection)
+           (magit-diff-paint-hunk section selection t))
+    (_     (magit-section-highlight section nil))))
+
+(defun magit-diff-highlight-list (section &optional selection)
+  (let ((beg (oref section start))
+        (cnt (oref section content))
+        (end (oref section end)))
+    (when (or (eq this-command 'mouse-drag-region)
+              (not selection))
+      (unless (and (region-active-p)
+                   (<= (region-beginning) beg))
+        (magit-section-make-overlay beg cnt 'magit-section-highlight))
+      (unless (oref section hidden)
+        (dolist (child (oref section children))
+          (when (or (eq this-command 'mouse-drag-region)
+                    (not (and (region-active-p)
+                              (<= (region-beginning)
+                                  (oref child start)))))
+            (magit-diff-highlight-recursive child selection)))))
+    (when magit-diff-highlight-hunk-body
+      (magit-section-make-overlay (1- end) end 'magit-section-highlight))))
+
+(defun magit-diff-highlight-file (section &optional selection)
+  (magit-diff-highlight-heading section selection)
+  (unless (oref section hidden)
+    (dolist (child (oref section children))
+      (magit-diff-highlight-recursive child selection))))
+
+(defun magit-diff-highlight-heading (section &optional selection)
+  (magit-section-make-overlay
+   (oref section start)
+   (or (oref section content)
+       (oref section end))
+   (pcase (list (oref section type)
+                (and (member section selection)
+                     (not (eq this-command 'mouse-drag-region))))
+     (`(file   t) 'magit-diff-file-heading-selection)
+     (`(file nil) 'magit-diff-file-heading-highlight)
+     (`(hunk   t) 'magit-diff-hunk-heading-selection)
+     (`(hunk nil) 'magit-diff-hunk-heading-highlight))))
+
+;;; Hunk Paint
+
+(cl-defun magit-diff-paint-hunk
+    (section &optional selection
+             (highlight (magit-section-selected-p section selection)))
+  (let (paint)
+    (unless magit-diff-highlight-hunk-body
+      (setq highlight nil))
+    (cond (highlight
+           (unless (oref section hidden)
+             (add-to-list 'magit-section-highlighted-sections section)
+             (cond ((memq section magit-section-unhighlight-sections)
+                    (setq magit-section-unhighlight-sections
+                          (delq section magit-section-unhighlight-sections)))
+                   (magit-diff-highlight-hunk-body
+                    (setq paint t)))))
+          (t
+           (cond ((and (oref section hidden)
+                       (memq section magit-section-unhighlight-sections))
+                  (add-to-list 'magit-section-highlighted-sections section)
+                  (setq magit-section-unhighlight-sections
+                        (delq section magit-section-unhighlight-sections)))
+                 (t
+                  (setq paint t)))))
+    (when paint
+      (save-excursion
+        (goto-char (oref section start))
+        (let ((end (oref section end))
+              (merging (looking-at "@@@"))
+              (stage nil)
+              (tab-width (magit-diff-tab-width
+                          (magit-section-parent-value section))))
+          (forward-line)
+          (while (< (point) end)
+            (when (and magit-diff-hide-trailing-cr-characters
+                       (char-equal ?\r (char-before (line-end-position))))
+              (put-text-property (1- (line-end-position)) (line-end-position)
+                                 'invisible t))
+            (put-text-property
+             (point) (1+ (line-end-position)) 'face
+             (cond
+              ((looking-at "^\\+\\+?\\([<=|>]\\)\\{7\\}")
+               (setq stage (pcase (list (match-string 1) highlight)
+                             (`("<" nil) 'magit-diff-our)
+                             (`("<"   t) 'magit-diff-our-highlight)
+                             (`("|" nil) 'magit-diff-base)
+                             (`("|"   t) 'magit-diff-base-highlight)
+                             (`("=" nil) 'magit-diff-their)
+                             (`("="   t) 'magit-diff-their-highlight)
+                             (`(">" nil) nil)))
+               'magit-diff-conflict-heading)
+              ((looking-at (if merging "^\\(\\+\\| \\+\\)" "^\\+"))
+               (magit-diff-paint-tab merging tab-width)
+               (magit-diff-paint-whitespace merging)
+               (or stage
+                   (if highlight 'magit-diff-added-highlight 'magit-diff-added)))
+              ((looking-at (if merging "^\\(-\\| -\\)" "^-"))
+               (magit-diff-paint-tab merging tab-width)
+               (if highlight 'magit-diff-removed-highlight 'magit-diff-removed))
+              (t
+               (magit-diff-paint-tab merging tab-width)
+               (if highlight 'magit-diff-context-highlight 'magit-diff-context))))
+            (forward-line))))))
+  (magit-diff-update-hunk-refinement section))
+
+(defvar magit-diff--tab-width-cache nil)
+
+(defun magit-diff-tab-width (file)
+  (setq file (expand-file-name file))
+  (cl-flet ((cache (value)
+                   (let ((elt (assoc file magit-diff--tab-width-cache)))
+                     (if elt
+                         (setcdr elt value)
+                       (setq magit-diff--tab-width-cache
+                             (cons (cons file value)
+                                   magit-diff--tab-width-cache))))
+                   value))
+    (cond
+     ((not magit-diff-adjust-tab-width)
+      tab-width)
+     ((--when-let (find-buffer-visiting file)
+        (cache (buffer-local-value 'tab-width it))))
+     ((--when-let (assoc file magit-diff--tab-width-cache)
+        (or (cdr it)
+            tab-width)))
+     ((or (eq magit-diff-adjust-tab-width 'always)
+          (and (numberp magit-diff-adjust-tab-width)
+               (>= magit-diff-adjust-tab-width
+                   (nth 7 (file-attributes file)))))
+      (cache (buffer-local-value 'tab-width (find-file-noselect file))))
+     (t
+      (cache nil)
+      tab-width))))
+
+(defun magit-diff-paint-tab (merging width)
+  (save-excursion
+    (forward-char (if merging 2 1))
+    (while (= (char-after) ?\t)
+      (put-text-property (point) (1+ (point))
+                         'display (list (list 'space :width width)))
+      (forward-char))))
+
+(defun magit-diff-paint-whitespace (merging)
+  (when (and magit-diff-paint-whitespace
+             (or (derived-mode-p 'magit-status-mode)
+                 (not (eq magit-diff-paint-whitespace 'status))))
+    (let ((prefix (if merging "^[-\\+\s]\\{2\\}" "^[-\\+]"))
+          (indent
+           (if (local-variable-p 'magit-diff-highlight-indentation)
+               magit-diff-highlight-indentation
+             (setq-local
+              magit-diff-highlight-indentation
+              (cdr (--first (string-match-p (car it) default-directory)
+                            (nreverse
+                             (default-value
+                               'magit-diff-highlight-indentation))))))))
+      (when (and magit-diff-highlight-trailing
+                 (looking-at (concat prefix ".*?\\([ \t]+\\)$")))
+        (let ((ov (make-overlay (match-beginning 1) (match-end 1) nil t)))
+          (overlay-put ov 'face 'magit-diff-whitespace-warning)
+          (overlay-put ov 'evaporate t)))
+      (when (or (and (eq indent 'tabs)
+                     (looking-at (concat prefix "\\( *\t[ \t]*\\)")))
+                (and (integerp indent)
+                     (looking-at (format "%s\\([ \t]* \\{%s,\\}[ \t]*\\)"
+                                         prefix indent))))
+        (let ((ov (make-overlay (match-beginning 1) (match-end 1) nil t)))
+          (overlay-put ov 'face 'magit-diff-whitespace-warning)
+          (overlay-put ov 'evaporate t))))))
+
+(defun magit-diff-update-hunk-refinement (&optional section)
+  (if section
+      (unless (oref section hidden)
+        (pcase (list magit-diff-refine-hunk
+                     (oref section refined)
+                     (eq section (magit-current-section)))
+          ((or `(all nil ,_) `(t nil t))
+           (oset section refined t)
+           (save-excursion
+             (goto-char (oref section start))
+             ;; `diff-refine-hunk' does not handle combined diffs.
+             (unless (looking-at "@@@")
+               ;; Avoid fsyncing many small temp files
+               (let ((write-region-inhibit-fsync t))
+                 (diff-refine-hunk)))))
+          ((or `(nil t ,_) `(t t nil))
+           (oset section refined nil)
+           (remove-overlays (oref section start)
+                            (oref section end)
+                            'diff-mode 'fine))))
+    (cl-labels ((recurse (section)
+                         (if (magit-section-match 'hunk section)
+                             (magit-diff-update-hunk-refinement section)
+                           (dolist (child (oref section children))
+                             (recurse child)))))
+      (recurse magit-root-section))))
+
+
+;;; Hunk Region
+
+(defun magit-diff-hunk-region-beginning ()
+  (save-excursion (goto-char (region-beginning))
+                  (line-beginning-position)))
+
+(defun magit-diff-hunk-region-end ()
+  (save-excursion (goto-char (region-end))
+                  (line-end-position)))
+
+(defun magit-diff-update-hunk-region (section)
+  "Highlight the hunk-internal region if any."
+  (when (eq (magit-diff-scope section t) 'region)
+    (magit-diff--make-hunk-overlay
+     (oref section start)
+     (1- (oref section content))
+     'face 'magit-diff-lines-heading
+     'display (magit-diff-hunk-region-header section)
+     'after-string (magit-diff--hunk-after-string 'magit-diff-lines-heading))
+    (run-hook-with-args 'magit-diff-highlight-hunk-region-functions section)
+    t))
+
+(defun magit-diff-highlight-hunk-region-dim-outside (section)
+  "Dim the parts of the hunk that are outside the hunk-internal region.
+This is done by using the same foreground and background color
+for added and removed lines as for context lines."
+  (let ((face (if magit-diff-highlight-hunk-body
+                  'magit-diff-context-highlight
+                'magit-diff-context)))
+    (when magit-diff-unmarked-lines-keep-foreground
+      (setq face (list :background (face-attribute face :background))))
+    (magit-diff--make-hunk-overlay (oref section content)
+                                   (magit-diff-hunk-region-beginning)
+                                   'face face
+                                   'priority 2)
+    (magit-diff--make-hunk-overlay (1+ (magit-diff-hunk-region-end))
+                                   (oref section end)
+                                   'face face
+                                   'priority 2)))
+
+(defun magit-diff-highlight-hunk-region-using-face (_section)
+  "Highlight the hunk-internal region by making it bold.
+Or rather highlight using the face `magit-diff-hunk-region', though
+changing only the `:weight' and/or `:slant' is recommended for that
+face."
+  (magit-diff--make-hunk-overlay (magit-diff-hunk-region-beginning)
+                                 (1+ (magit-diff-hunk-region-end))
+                                 'face 'magit-diff-hunk-region))
+
+(defun magit-diff-highlight-hunk-region-using-overlays (section)
+  "Emphasize the hunk-internal region using delimiting horizontal lines.
+This is implemented as single-pixel newlines places inside overlays."
+  (if (window-system)
+      (let ((beg (magit-diff-hunk-region-beginning))
+            (end (magit-diff-hunk-region-end))
+            (str (propertize
+                  (concat (propertize "\s" 'display '(space :height (1)))
+                          (propertize "\n" 'line-height t))
+                  'face 'magit-diff-lines-boundary)))
+        (magit-diff--make-hunk-overlay beg (1+ beg) 'before-string str)
+        (magit-diff--make-hunk-overlay end (1+ end) 'after-string  str))
+    (magit-diff-highlight-hunk-region-using-face section)))
+
+(defun magit-diff-highlight-hunk-region-using-underline (section)
+  "Emphasize the hunk-internal region using delimiting horizontal lines.
+This is implemented by overlining and underlining the first and
+last (visual) lines of the region."
+  (if (window-system)
+      (let* ((beg (magit-diff-hunk-region-beginning))
+             (end (magit-diff-hunk-region-end))
+             (beg-eol (save-excursion (goto-char beg)
+                                      (end-of-visual-line)
+                                      (point)))
+             (end-bol (save-excursion (goto-char end)
+                                      (beginning-of-visual-line)
+                                      (point)))
+             (color (face-background 'magit-diff-lines-boundary nil t)))
+        (cl-flet ((ln (b e &rest face)
+                      (magit-diff--make-hunk-overlay
+                       b e 'face face 'after-string
+                       (magit-diff--hunk-after-string face))))
+          (if (= beg end-bol)
+              (ln beg beg-eol :overline color :underline color)
+            (ln beg beg-eol :overline color)
+            (ln end-bol end :underline color))))
+    (magit-diff-highlight-hunk-region-using-face section)))
+
+(defun magit-diff--make-hunk-overlay (start end &rest args)
+  (let ((ov (make-overlay start end nil t)))
+    (overlay-put ov 'evaporate t)
+    (while args (overlay-put ov (pop args) (pop args)))
+    (push ov magit-region-overlays)
+    ov))
+
+(defun magit-diff--hunk-after-string (face)
+  (propertize "\s"
+              'face face
+              'display (list 'space :align-to
+                             `(+ (0 . right)
+                                 ,(min (window-hscroll)
+                                       (- (line-end-position)
+                                          (line-beginning-position)))))
+              ;; This prevents the cursor from being rendered at the
+              ;; edge of the window.
+              'cursor t))
+
+;;; Hunk Utilities
+
+(defun magit-diff-inside-hunk-body-p ()
+  "Return non-nil if point is inside the body of a hunk."
+  (and (magit-section-match 'hunk)
+       (> (point)
+          (oref (magit-current-section) content))))
+
+;;; Diff Extract
+
+(defun magit-diff-file-header (section)
+  (when (magit-hunk-section-p section)
+    (setq section (oref section parent)))
+  (when (magit-file-section-p section)
+    (oref section header)))
+
+(defun magit-diff-hunk-region-header (section)
+  (let ((patch (magit-diff-hunk-region-patch section)))
+    (string-match "\n" patch)
+    (substring patch 0 (1- (match-end 0)))))
+
+(defun magit-diff-hunk-region-patch (section &optional args)
+  (let ((op (if (member "--reverse" args) "+" "-"))
+        (sbeg (oref section start))
+        (rbeg (magit-diff-hunk-region-beginning))
+        (rend (region-end))
+        (send (oref section end))
+        (patch nil))
+    (save-excursion
+      (goto-char sbeg)
+      (while (< (point) send)
+        (looking-at "\\(.\\)\\([^\n]*\n\\)")
+        (cond ((or (string-match-p "[@ ]" (match-string-no-properties 1))
+                   (and (>= (point) rbeg)
+                        (<= (point) rend)))
+               (push (match-string-no-properties 0) patch))
+              ((equal op (match-string-no-properties 1))
+               (push (concat " " (match-string-no-properties 2)) patch)))
+        (forward-line)))
+    (with-temp-buffer
+      (insert (mapconcat 'identity (reverse patch) ""))
+      (diff-fixup-modifs (point-min) (point-max))
+      (setq patch (buffer-string)))
+    patch))
+
+(provide 'magit-diff)
+;;; magit-diff.el ends here