config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

magit-status.el (33063B)


      1 ;;; magit-status.el --- The grand overview  -*- lexical-binding:t -*-
      2 
      3 ;; Copyright (C) 2008-2024 The Magit Project Contributors
      4 
      5 ;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
      6 ;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
      7 
      8 ;; SPDX-License-Identifier: GPL-3.0-or-later
      9 
     10 ;; Magit is free software: you can redistribute it and/or modify it
     11 ;; under the terms of the GNU General Public License as published by
     12 ;; the Free Software Foundation, either version 3 of the License, or
     13 ;; (at your option) any later version.
     14 ;;
     15 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
     16 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     17 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     18 ;; License for more details.
     19 ;;
     20 ;; You should have received a copy of the GNU General Public License
     21 ;; along with Magit.  If not, see <https://www.gnu.org/licenses/>.
     22 
     23 ;;; Commentary:
     24 
     25 ;; This library implements the status buffer.
     26 
     27 ;;; Code:
     28 
     29 (require 'magit)
     30 
     31 ;;; Options
     32 
     33 (defgroup magit-status nil
     34   "Inspect and manipulate Git repositories."
     35   :link '(info-link "(magit)Status Buffer")
     36   :group 'magit-modes)
     37 
     38 (defcustom magit-status-mode-hook nil
     39   "Hook run after entering Magit-Status mode."
     40   :group 'magit-status
     41   :type 'hook)
     42 
     43 (defcustom magit-status-headers-hook
     44   '(magit-insert-error-header
     45     magit-insert-diff-filter-header
     46     magit-insert-head-branch-header
     47     magit-insert-upstream-branch-header
     48     magit-insert-push-branch-header
     49     magit-insert-tags-header)
     50   "Hook run to insert headers into the status buffer.
     51 
     52 This hook is run by `magit-insert-status-headers', which in turn
     53 has to be a member of `magit-status-sections-hook' to be used at
     54 all."
     55   :package-version '(magit . "2.1.0")
     56   :group 'magit-status
     57   :type 'hook
     58   :options '(magit-insert-error-header
     59              magit-insert-diff-filter-header
     60              magit-insert-repo-header
     61              magit-insert-remote-header
     62              magit-insert-head-branch-header
     63              magit-insert-upstream-branch-header
     64              magit-insert-push-branch-header
     65              magit-insert-tags-header))
     66 
     67 (defcustom magit-status-sections-hook
     68   '(magit-insert-status-headers
     69     magit-insert-merge-log
     70     magit-insert-rebase-sequence
     71     magit-insert-am-sequence
     72     magit-insert-sequencer-sequence
     73     magit-insert-bisect-output
     74     magit-insert-bisect-rest
     75     magit-insert-bisect-log
     76     magit-insert-untracked-files
     77     magit-insert-unstaged-changes
     78     magit-insert-staged-changes
     79     magit-insert-stashes
     80     magit-insert-unpushed-to-pushremote
     81     magit-insert-unpushed-to-upstream-or-recent
     82     magit-insert-unpulled-from-pushremote
     83     magit-insert-unpulled-from-upstream)
     84   "Hook run to insert sections into a status buffer."
     85   :package-version '(magit . "2.12.0")
     86   :group 'magit-status
     87   :type 'hook)
     88 
     89 (defcustom magit-status-initial-section '(1)
     90   "The section point is placed on when a status buffer is created.
     91 
     92 When such a buffer is merely being refreshed or being shown again
     93 after it was merely buried, then this option has no effect.
     94 
     95 If this is nil, then point remains on the very first section as
     96 usual.  Otherwise it has to be a list of integers and section
     97 identity lists.  The members of that list are tried in order
     98 until a matching section is found.
     99 
    100 An integer means to jump to the nth section, 1 for example
    101 jumps over the headings.  To get a section's \"identity list\"
    102 use \\[universal-argument] \\[magit-describe-section-briefly].
    103 
    104 If, for example, you want to jump to the commits that haven't
    105 been pulled from the upstream, or else the second section, then
    106 use: (((unpulled . \"..@{upstream}\") (status)) 1).
    107 
    108 See option `magit-section-initial-visibility-alist' for how to
    109 control the initial visibility of the jumped to section."
    110   :package-version '(magit . "2.90.0")
    111   :group 'magit-status
    112   :type '(choice (const :tag "as usual" nil)
    113                  (repeat (choice (number :tag "nth top-level section")
    114                                  (sexp   :tag "section identity")))))
    115 
    116 (defcustom magit-status-goto-file-position nil
    117   "Whether to go to position corresponding to file position.
    118 
    119 If this is non-nil and the current buffer is visiting a file,
    120 then `magit-status' tries to go to the position in the status
    121 buffer that corresponds to the position in the file-visiting
    122 buffer.  This jumps into either the diff of unstaged changes
    123 or the diff of staged changes.
    124 
    125 If the previously current buffer does not visit a file, or if
    126 the file has neither unstaged nor staged changes then this has
    127 no effect.
    128 
    129 The command `magit-status-here' tries to go to that position,
    130 regardless of the value of this option."
    131   :package-version '(magit . "3.0.0")
    132   :group 'magit-status
    133   :type 'boolean)
    134 
    135 (defcustom magit-status-show-hashes-in-headers nil
    136   "Whether headers in the status buffer show hashes.
    137 The functions which respect this option are
    138 `magit-insert-head-branch-header',
    139 `magit-insert-upstream-branch-header', and
    140 `magit-insert-push-branch-header'."
    141   :package-version '(magit . "2.4.0")
    142   :group 'magit-status
    143   :type 'boolean)
    144 
    145 (defcustom magit-status-margin
    146   (list nil
    147         (nth 1 magit-log-margin)
    148         'magit-log-margin-width nil
    149         (nth 4 magit-log-margin))
    150   "Format of the margin in `magit-status-mode' buffers.
    151 
    152 The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH).
    153 
    154 If INIT is non-nil, then the margin is shown initially.
    155 STYLE controls how to format the author or committer date.
    156   It can be one of `age' (to show the age of the commit),
    157   `age-abbreviated' (to abbreviate the time unit to a character),
    158   or a string (suitable for `format-time-string') to show the
    159   actual date.  Option `magit-log-margin-show-committer-date'
    160   controls which date is being displayed.
    161 WIDTH controls the width of the margin.  This exists for forward
    162   compatibility and currently the value should not be changed.
    163 AUTHOR controls whether the name of the author is also shown by
    164   default.
    165 AUTHOR-WIDTH has to be an integer.  When the name of the author
    166   is shown, then this specifies how much space is used to do so."
    167   :package-version '(magit . "2.9.0")
    168   :group 'magit-status
    169   :group 'magit-margin
    170   :type magit-log-margin--custom-type
    171   :initialize #'magit-custom-initialize-reset
    172   :set-after '(magit-log-margin)
    173   :set (apply-partially #'magit-margin-set-variable 'magit-status-mode))
    174 
    175 (defcustom magit-status-use-buffer-arguments 'selected
    176   "Whether `magit-status' reuses arguments when the buffer already exists.
    177 
    178 This option has no effect when merely refreshing the status
    179 buffer using `magit-refresh'.
    180 
    181 Valid values are:
    182 
    183 `always': Always use the set of arguments that is currently
    184   active in the status buffer, provided that buffer exists
    185   of course.
    186 `selected': Use the set of arguments from the status
    187   buffer, but only if it is displayed in a window of the
    188   current frame.  This is the default.
    189 `current': Use the set of arguments from the status buffer,
    190   but only if it is the current buffer.
    191 `never': Never use the set of arguments from the status
    192   buffer."
    193   :package-version '(magit . "3.0.0")
    194   :group 'magit-buffers
    195   :group 'magit-commands
    196   :type '(choice
    197           (const :tag "always use args from buffer" always)
    198           (const :tag "use args from buffer if displayed in frame" selected)
    199           (const :tag "use args from buffer if it is current" current)
    200           (const :tag "never use args from buffer" never)))
    201 
    202 ;;; Commands
    203 
    204 ;;;###autoload
    205 (defun magit-init (directory)
    206   "Initialize a Git repository, then show its status.
    207 
    208 If the directory is below an existing repository, then the user
    209 has to confirm that a new one should be created inside.  If the
    210 directory is the root of the existing repository, then the user
    211 has to confirm that it should be reinitialized.
    212 
    213 Non-interactively DIRECTORY is (re-)initialized unconditionally."
    214   (interactive
    215    (let ((directory (file-name-as-directory
    216                      (expand-file-name
    217                       (read-directory-name "Create repository in: ")))))
    218      (when-let ((toplevel (magit-toplevel directory)))
    219        (setq toplevel (expand-file-name toplevel))
    220        (unless (y-or-n-p (if (file-equal-p toplevel directory)
    221                              (format "Reinitialize existing repository %s? "
    222                                      directory)
    223                            (format "%s is a repository.  Create another in %s? "
    224                                    toplevel directory)))
    225          (user-error "Abort")))
    226      (list directory)))
    227   ;; `git init' does not understand the meaning of "~"!
    228   (magit-call-git "init" (magit-convert-filename-for-git
    229                           (expand-file-name directory)))
    230   (magit-status-setup-buffer directory))
    231 
    232 ;;;###autoload
    233 (defun magit-status (&optional directory cache)
    234   "Show the status of the current Git repository in a buffer.
    235 
    236 If the current directory isn't located within a Git repository,
    237 then prompt for an existing repository or an arbitrary directory,
    238 depending on option `magit-repository-directories', and show the
    239 status of the selected repository instead.
    240 
    241 * If that option specifies any existing repositories, then offer
    242   those for completion and show the status buffer for the
    243   selected one.
    244 
    245 * Otherwise read an arbitrary directory using regular file-name
    246   completion.  If the selected directory is the top-level of an
    247   existing working tree, then show the status buffer for that.
    248 
    249 * Otherwise offer to initialize the selected directory as a new
    250   repository.  After creating the repository show its status
    251   buffer.
    252 
    253 These fallback behaviors can also be forced using one or more
    254 prefix arguments:
    255 
    256 * With two prefix arguments (or more precisely a numeric prefix
    257   value of 16 or greater) read an arbitrary directory and act on
    258   it as described above.  The same could be accomplished using
    259   the command `magit-init'.
    260 
    261 * With a single prefix argument read an existing repository, or
    262   if none can be found based on `magit-repository-directories',
    263   then fall back to the same behavior as with two prefix
    264   arguments."
    265   (interactive
    266    (let ((magit--refresh-cache (list (cons 0 0))))
    267      (list (and (or current-prefix-arg (not (magit-toplevel)))
    268                 (progn (magit--assert-usable-git)
    269                        (magit-read-repository
    270                         (>= (prefix-numeric-value current-prefix-arg) 16))))
    271            magit--refresh-cache)))
    272   (let ((magit--refresh-cache (or cache (list (cons 0 0)))))
    273     (if directory
    274         (let ((toplevel (magit-toplevel directory)))
    275           (setq directory (file-name-as-directory
    276                            (expand-file-name directory)))
    277           (if (and toplevel (file-equal-p directory toplevel))
    278               (magit-status-setup-buffer directory)
    279             (when (y-or-n-p
    280                    (if toplevel
    281                        (format "%s is a repository.  Create another in %s? "
    282                                toplevel directory)
    283                      (format "Create repository in %s? " directory)))
    284               ;; Creating a new repository invalidates cached values.
    285               (setq magit--refresh-cache nil)
    286               (magit-init directory))))
    287       (magit-status-setup-buffer default-directory))))
    288 
    289 (put 'magit-status 'interactive-only 'magit-status-setup-buffer)
    290 
    291 ;;;###autoload
    292 (defalias 'magit #'magit-status
    293   "Begin using Magit.
    294 
    295 This alias for `magit-status' exists for better discoverability.
    296 
    297 Instead of invoking this alias for `magit-status' using
    298 \"M-x magit RET\", you should bind a key to `magit-status'
    299 and read the info node `(magit)Getting Started', which
    300 also contains other useful hints.")
    301 
    302 ;;;###autoload
    303 (defun magit-status-here ()
    304   "Like `magit-status' but with non-nil `magit-status-goto-file-position'."
    305   (interactive)
    306   (let ((magit-status-goto-file-position t))
    307     (call-interactively #'magit-status)))
    308 
    309 (put 'magit-status-here 'interactive-only 'magit-status-setup-buffer)
    310 
    311 ;;;###autoload
    312 (defun magit-status-quick ()
    313   "Show the status of the current Git repository, maybe without refreshing.
    314 
    315 If the status buffer of the current Git repository exists but
    316 isn't being displayed in the selected frame, then display it
    317 without refreshing it.
    318 
    319 If the status buffer is being displayed in the selected frame,
    320 then also refresh it.
    321 
    322 Prefix arguments have the same meaning as for `magit-status',
    323 and additionally cause the buffer to be refresh.
    324 
    325 To use this function instead of `magit-status', add this to your
    326 init file: (global-set-key (kbd \"C-x g\") \\='magit-status-quick)."
    327   (interactive)
    328   (if-let ((buffer
    329             (and (not current-prefix-arg)
    330                  (not (magit-get-mode-buffer 'magit-status-mode nil 'selected))
    331                  (magit-get-mode-buffer 'magit-status-mode))))
    332       (magit-display-buffer buffer)
    333     (call-interactively #'magit-status)))
    334 
    335 ;;; Mode
    336 
    337 (defvar-keymap magit-status-mode-map
    338   :doc "Keymap for `magit-status-mode'."
    339   :parent magit-mode-map
    340   "j" #'magit-status-jump
    341   "<remap> <dired-jump>" #'magit-dired-jump)
    342 
    343 (transient-define-prefix magit-status-jump ()
    344   "In a Magit-Status buffer, jump to a section."
    345   [["Jump to"
    346     ("z " magit-jump-to-stashes)
    347     ("t " magit-jump-to-tracked)
    348     ("n " magit-jump-to-untracked)
    349     ("i " magit-jump-to-ignored)
    350     ("u " magit-jump-to-unstaged)
    351     ("s " magit-jump-to-staged)]
    352    [""
    353     ("fu" magit-jump-to-unpulled-from-upstream)
    354     ("fp" magit-jump-to-unpulled-from-pushremote)
    355     ("pu" magit-jump-to-unpushed-to-upstream)
    356     ("pp" magit-jump-to-unpushed-to-pushremote)
    357     ("a " magit-jump-to-assume-unchanged)
    358     ("w " magit-jump-to-skip-worktree)]
    359    ["Jump using"
    360     ("j"  "Imenu" imenu)]])
    361 
    362 (define-derived-mode magit-status-mode magit-mode "Magit"
    363   "Mode for looking at Git status.
    364 
    365 This mode is documented in info node `(magit)Status Buffer'.
    366 
    367 \\<magit-mode-map>\
    368 Type \\[magit-refresh] to refresh the current buffer.
    369 Type \\[magit-section-toggle] to expand or hide the section at point.
    370 Type \\[magit-visit-thing] to visit the change or commit at point.
    371 
    372 Type \\[magit-dispatch] to invoke major commands.
    373 
    374 Staging and applying changes is documented in info node
    375 `(magit)Staging and Unstaging' and info node `(magit)Applying'.
    376 
    377 \\<magit-hunk-section-map>Type \
    378 \\[magit-apply] to apply the change at point, \
    379 \\[magit-stage] to stage,
    380 \\[magit-unstage] to unstage, \
    381 \\[magit-discard] to discard, or \
    382 \\[magit-reverse] to reverse it.
    383 
    384 \\<magit-status-mode-map>\
    385 Type \\[magit-commit] to create a commit.
    386 
    387 \\{magit-status-mode-map}"
    388   :interactive nil
    389   :group 'magit-status
    390   (magit-hack-dir-local-variables)
    391   (when magit-status-initial-section
    392     (add-hook 'magit-post-create-buffer-hook
    393               #'magit-status-goto-initial-section nil t))
    394   (setq magit--imenu-group-types '(not branch commit)))
    395 
    396 (put 'magit-status-mode 'magit-diff-default-arguments
    397      '("--no-ext-diff"))
    398 (put 'magit-status-mode 'magit-log-default-arguments
    399      '("-n256" "--decorate"))
    400 
    401 ;;;###autoload
    402 (defun magit-status-setup-buffer (&optional directory)
    403   (unless directory
    404     (setq directory default-directory))
    405   (when (file-remote-p directory)
    406     (magit-git-version-assert))
    407   (let* ((default-directory directory)
    408          (d (magit-diff--get-value 'magit-status-mode
    409                                    magit-status-use-buffer-arguments))
    410          (l (magit-log--get-value 'magit-status-mode
    411                                   magit-status-use-buffer-arguments))
    412          (file (and magit-status-goto-file-position
    413                     (magit-file-relative-name)))
    414          (line (and file (save-restriction (widen) (line-number-at-pos))))
    415          (col  (and file (save-restriction (widen) (current-column))))
    416          (buf  (magit-setup-buffer #'magit-status-mode nil
    417                  (magit-buffer-diff-args  (nth 0 d))
    418                  (magit-buffer-diff-files (nth 1 d))
    419                  (magit-buffer-log-args   (nth 0 l))
    420                  (magit-buffer-log-files  (nth 1 l)))))
    421     (when file
    422       (with-current-buffer buf
    423         (let ((staged (magit-get-section '((staged) (status)))))
    424           (if (and staged
    425                    (cadr (magit-diff--locate-hunk file line staged)))
    426               (magit-diff--goto-position file line col staged)
    427             (let ((unstaged (magit-get-section '((unstaged) (status)))))
    428               (unless (and unstaged
    429                            (magit-diff--goto-position file line col unstaged))
    430                 (when staged
    431                   (magit-diff--goto-position file line col staged))))))))
    432     buf))
    433 
    434 (defun magit-status-refresh-buffer ()
    435   (magit-git-exit-code "update-index" "--refresh")
    436   (magit-insert-section (status)
    437     (magit-run-section-hook 'magit-status-sections-hook)))
    438 
    439 (defun magit-status-goto-initial-section ()
    440   "Jump to the section specified by `magit-status-initial-section'."
    441   (when-let ((section
    442               (--some (if (integerp it)
    443                           (nth (1- it)
    444                                (magit-section-siblings (magit-current-section)
    445                                                        'next))
    446                         (magit-get-section it))
    447                       magit-status-initial-section)))
    448     (goto-char (oref section start))
    449     (when-let ((vis (cdr (assq 'magit-status-initial-section
    450                                magit-section-initial-visibility-alist))))
    451       (if (eq vis 'hide)
    452           (magit-section-hide section)
    453         (magit-section-show section)))))
    454 
    455 (defun magit-status-maybe-update-revision-buffer (&optional _)
    456   "When moving in the status buffer, update the revision buffer.
    457 If there is no revision buffer in the same frame, then do nothing."
    458   (when (derived-mode-p 'magit-status-mode)
    459     (magit--maybe-update-revision-buffer)))
    460 
    461 (defun magit-status-maybe-update-stash-buffer (&optional _)
    462   "When moving in the status buffer, update the stash buffer.
    463 If there is no stash buffer in the same frame, then do nothing."
    464   (when (derived-mode-p 'magit-status-mode)
    465     (magit--maybe-update-stash-buffer)))
    466 
    467 (defun magit-status-maybe-update-blob-buffer (&optional _)
    468   "When moving in the status buffer, update the blob buffer.
    469 If there is no blob buffer in the same frame, then do nothing."
    470   (when (derived-mode-p 'magit-status-mode)
    471     (magit--maybe-update-blob-buffer)))
    472 
    473 ;;; Sections
    474 ;;;; Special Headers
    475 
    476 (defun magit-insert-status-headers ()
    477   "Insert header sections appropriate for `magit-status-mode' buffers.
    478 The sections are inserted by running the functions on the hook
    479 `magit-status-headers-hook'."
    480   (if (magit-rev-verify "HEAD")
    481       (magit-insert-headers 'magit-status-headers-hook)
    482     (insert "In the beginning there was darkness\n\n")))
    483 
    484 (defvar-keymap magit-error-section-map
    485   :doc "Keymap for `error' sections."
    486   "<remap> <magit-visit-thing>" #'magit-process-buffer
    487   "<1>" (magit-menu-item "Visit process output" #'magit-process-buffer))
    488 
    489 (defun magit-insert-error-header ()
    490   "Insert the message about the Git error that just occurred.
    491 
    492 This function is only aware of the last error that occur when Git
    493 was run for side-effects.  If, for example, an error occurs while
    494 generating a diff, then that error won't be inserted.  Refreshing
    495 the status buffer causes this section to disappear again."
    496   (when magit-this-error
    497     (magit-insert-section (error 'git)
    498       (insert (propertize (format "%-10s" "GitError! ")
    499                           'font-lock-face 'magit-section-heading))
    500       (insert (propertize magit-this-error 'font-lock-face 'error))
    501       (when-let ((key (car (where-is-internal 'magit-process-buffer))))
    502         (insert (format "  [Type `%s' for details]" (key-description key))))
    503       (insert ?\n))
    504     (setq magit-this-error nil)))
    505 
    506 (defun magit-insert-diff-filter-header ()
    507   "Insert a header line showing the effective diff filters."
    508   (let ((ignore-modules (magit-ignore-submodules-p)))
    509     (when (or ignore-modules
    510               magit-buffer-diff-files)
    511       (insert (propertize (format "%-10s" "Filter! ")
    512                           'font-lock-face 'magit-section-heading))
    513       (when ignore-modules
    514         (insert ignore-modules)
    515         (when magit-buffer-diff-files
    516           (insert " -- ")))
    517       (when magit-buffer-diff-files
    518         (insert (string-join magit-buffer-diff-files " ")))
    519       (insert ?\n))))
    520 
    521 ;;;; Reference Headers
    522 
    523 (defun magit-insert-head-branch-header (&optional branch)
    524   "Insert a header line about the current branch.
    525 If `HEAD' is detached, then insert information about that commit
    526 instead.  The optional BRANCH argument is for internal use only."
    527   (let ((branch (or branch (magit-get-current-branch)))
    528         (output (magit-rev-format "%h %s" (or branch "HEAD"))))
    529     (string-match "^\\([^ ]+\\) \\(.*\\)" output)
    530     (magit-bind-match-strings (commit summary) output
    531       (when (equal summary "")
    532         (setq summary "(no commit message)"))
    533       (if branch
    534           (magit-insert-section (branch branch)
    535             (insert (format "%-10s" "Head: "))
    536             (when magit-status-show-hashes-in-headers
    537               (insert (propertize commit 'font-lock-face 'magit-hash) ?\s))
    538             (insert (propertize branch 'font-lock-face 'magit-branch-local))
    539             (insert ?\s)
    540             (insert (funcall magit-log-format-message-function branch summary))
    541             (insert ?\n))
    542         (magit-insert-section (commit commit)
    543           (insert (format "%-10s" "Head: "))
    544           (insert (propertize commit 'font-lock-face 'magit-hash))
    545           (insert ?\s)
    546           (insert (funcall magit-log-format-message-function nil summary))
    547           (insert ?\n))))))
    548 
    549 (defun magit-insert-upstream-branch-header (&optional branch upstream keyword)
    550   "Insert a header line about the upstream of the current branch.
    551 If no branch is checked out, then insert nothing.  The optional
    552 arguments are for internal use only."
    553   (when-let ((branch (or branch (magit-get-current-branch))))
    554     (let ((remote (magit-get "branch" branch "remote"))
    555           (merge  (magit-get "branch" branch "merge"))
    556           (rebase (magit-get "branch" branch "rebase")))
    557       (when (or remote merge)
    558         (unless upstream
    559           (setq upstream (magit-get-upstream-branch branch)))
    560         (magit-insert-section (branch upstream)
    561           (pcase rebase
    562             ("true")
    563             ("false" (setq rebase nil))
    564             (_       (setq rebase (magit-get-boolean "pull.rebase"))))
    565           (insert (format "%-10s" (or keyword (if rebase "Rebase: " "Merge: "))))
    566           (insert
    567            (if upstream
    568                (concat (and magit-status-show-hashes-in-headers
    569                             (concat (propertize (magit-rev-format "%h" upstream)
    570                                                 'font-lock-face 'magit-hash)
    571                                     " "))
    572                        upstream " "
    573                        (funcall magit-log-format-message-function upstream
    574                                 (funcall magit-log-format-message-function nil
    575                                          (or (magit-rev-format "%s" upstream)
    576                                              "(no commit message)"))))
    577              (cond
    578               ((magit--unnamed-upstream-p remote merge)
    579                (concat (propertize merge  'font-lock-face 'magit-branch-remote)
    580                        " from "
    581                        (propertize remote 'font-lock-face 'bold)))
    582               ((magit--valid-upstream-p remote merge)
    583                (if (equal remote ".")
    584                    (concat
    585                     (propertize merge 'font-lock-face 'magit-branch-local) " "
    586                     (propertize "does not exist"
    587                                 'font-lock-face 'magit-branch-warning))
    588                  (format
    589                   "%s %s %s"
    590                   (propertize merge 'font-lock-face 'magit-branch-remote)
    591                   (propertize "does not exist on"
    592                               'font-lock-face 'magit-branch-warning)
    593                   (propertize remote 'font-lock-face 'magit-branch-remote))))
    594               (t
    595                (propertize "invalid upstream configuration"
    596                            'font-lock-face 'magit-branch-warning)))))
    597           (insert ?\n))))))
    598 
    599 (defun magit-insert-push-branch-header ()
    600   "Insert a header line about the branch the current branch is pushed to."
    601   (when-let* ((branch (magit-get-current-branch))
    602               (target (magit-get-push-branch branch)))
    603     (magit-insert-section (branch target)
    604       (insert (format "%-10s" "Push: "))
    605       (insert
    606        (if (magit-rev-verify target)
    607            (concat (and magit-status-show-hashes-in-headers
    608                         (concat (propertize (magit-rev-format "%h" target)
    609                                             'font-lock-face 'magit-hash)
    610                                 " "))
    611                    target " "
    612                    (funcall magit-log-format-message-function target
    613                             (funcall magit-log-format-message-function nil
    614                                      (or (magit-rev-format "%s" target)
    615                                          "(no commit message)"))))
    616          (let ((remote (magit-get-push-remote branch)))
    617            (if (magit-remote-p remote)
    618                (concat target " "
    619                        (propertize "does not exist"
    620                                    'font-lock-face 'magit-branch-warning))
    621              (concat remote " "
    622                      (propertize "remote does not exist"
    623                                  'font-lock-face 'magit-branch-warning))))))
    624       (insert ?\n))))
    625 
    626 (defun magit-insert-tags-header ()
    627   "Insert a header line about the current and/or next tag."
    628   (let* ((this-tag (magit-get-current-tag nil t))
    629          (next-tag (magit-get-next-tag nil t))
    630          (this-cnt (cadr this-tag))
    631          (next-cnt (cadr next-tag))
    632          (this-tag (car this-tag))
    633          (next-tag (car next-tag))
    634          (both-tags (and this-tag next-tag t)))
    635     (when (or this-tag next-tag)
    636       (magit-insert-section (tag (or this-tag next-tag))
    637         (insert (format "%-10s" (if both-tags "Tags: " "Tag: ")))
    638         (cl-flet ((insert-count (tag count face)
    639                     (insert (concat (propertize tag 'font-lock-face 'magit-tag)
    640                                     (and (> count 0)
    641                                          (format " (%s)"
    642                                                  (propertize
    643                                                   (format "%s" count)
    644                                                   'font-lock-face face)))))))
    645           (when this-tag  (insert-count this-tag this-cnt 'magit-branch-local))
    646           (when both-tags (insert ", "))
    647           (when next-tag  (insert-count next-tag next-cnt 'magit-tag)))
    648         (insert ?\n)))))
    649 
    650 ;;;; Auxiliary Headers
    651 
    652 (defun magit-insert-user-header ()
    653   "Insert a header line about the current user."
    654   (let ((name  (magit-get "user.name"))
    655         (email (magit-get "user.email")))
    656     (when (and name email)
    657       (magit-insert-section (user name)
    658         (insert (format "%-10s" "User: "))
    659         (insert (propertize name 'font-lock-face 'magit-log-author))
    660         (insert " <" email ">\n")))))
    661 
    662 (defun magit-insert-repo-header ()
    663   "Insert a header line showing the path to the repository top-level."
    664   (let ((topdir (magit-toplevel)))
    665     (magit-insert-section (repo topdir)
    666       (insert (format "%-10s%s\n" "Repo: " (abbreviate-file-name topdir))))))
    667 
    668 (defun magit-insert-remote-header ()
    669   "Insert a header line about the remote of the current branch.
    670 
    671 If no remote is configured for the current branch, then fall back
    672 showing the \"origin\" remote, or if that does not exist the first
    673 remote in alphabetic order."
    674   (when-let* ((name (magit-get-some-remote))
    675               ;; Under certain configurations it's possible for
    676               ;; url to be nil, when name is not, see #2858.
    677               (url (magit-get "remote" name "url")))
    678     (magit-insert-section (remote name)
    679       (insert (format "%-10s" "Remote: "))
    680       (insert (propertize name 'font-lock-face 'magit-branch-remote) ?\s)
    681       (insert url ?\n))))
    682 
    683 ;;;; File Sections
    684 
    685 (defvar-keymap magit-untracked-section-map
    686   :doc "Keymap for the `untracked' section."
    687   "<remap> <magit-delete-thing>" #'magit-discard
    688   "<remap> <magit-stage-file>"   #'magit-stage
    689   "<2>" (magit-menu-item "Discard files" #'magit-discard)
    690   "<1>" (magit-menu-item "Stage files"   #'magit-stage))
    691 
    692 (magit-define-section-jumper magit-jump-to-untracked
    693   "Untracked files" untracked nil magit-insert-untracked-files)
    694 
    695 (magit-define-section-jumper magit-jump-to-tracked
    696   "Tracked files" tracked nil magit-insert-tracked-files)
    697 
    698 (magit-define-section-jumper magit-jump-to-ignored
    699   "Ignored files" ignored nil magit-insert-ignored-files)
    700 
    701 (magit-define-section-jumper magit-jump-to-skip-worktree
    702   "Skip-worktree files" skip-worktree nil magit-insert-skip-worktree-files)
    703 
    704 (magit-define-section-jumper magit-jump-to-assume-unchanged
    705   "Assume-unchanged files" assume-unchanged nil
    706   magit-insert-assume-unchanged-files)
    707 
    708 (defun magit-insert-untracked-files ()
    709   "Maybe insert a list or tree of untracked files.
    710 
    711 Do so depending on the value of `status.showUntrackedFiles'.  Note
    712 that even if the value is `all', Magit still initially only shows
    713 directories.  But the directory sections can then be expanded using
    714 \"TAB\".
    715 
    716 If the first element of `magit-buffer-diff-files' is a directory, then
    717 limit the list to files below that.  The value of that variable can be
    718 set using \"D -- DIRECTORY RET g\"."
    719   (let ((show (or (magit-get "status.showUntrackedFiles") "normal")))
    720     (unless (equal show "no")
    721       (let* ((all (equal show "all"))
    722              (base (car magit-buffer-diff-files))
    723              (base (and base (file-directory-p base) base)))
    724         (magit-insert-files 'untracked
    725                             (lambda () (magit-untracked-files nil base (not all)))
    726                             (not all))))))
    727 
    728 (defun magit-insert-tracked-files ()
    729   "Insert a tree of tracked files.
    730 
    731 If the first element of `magit-buffer-diff-files' is a
    732 directory, then limit the list to files below that.  The value
    733 value of that variable can be set using \"D -- DIRECTORY RET g\"."
    734   (magit-insert-files 'tracked #'magit-list-files))
    735 
    736 (defun magit-insert-ignored-files ()
    737   "Insert a tree of ignored files.
    738 
    739 If the first element of `magit-buffer-diff-files' is a
    740 directory, then limit the list to files below that.  The value
    741 of that variable can be set using \"D -- DIRECTORY RET g\"."
    742   (magit-insert-files 'ignored #'magit-ignored-files))
    743 
    744 (defun magit-insert-skip-worktree-files ()
    745   "Insert a tree of skip-worktree files.
    746 
    747 If the first element of `magit-buffer-diff-files' is a
    748 directory, then limit the list to files below that.  The value
    749 of that variable can be set using \"D -- DIRECTORY RET g\"."
    750   (magit-insert-files 'skip-worktree #'magit-skip-worktree-files))
    751 
    752 (defun magit-insert-assume-unchanged-files ()
    753   "Insert a tree of files that are assumed to be unchanged.
    754 
    755 If the first element of `magit-buffer-diff-files' is a
    756 directory, then limit the list to files below that.  The value
    757 of that variable can be set using \"D -- DIRECTORY RET g\"."
    758   (magit-insert-files 'assume-unchanged #'magit-assume-unchanged-files))
    759 
    760 (defvar magit-file-section-indent nil
    761   "Indentation used for simple `file' sections.
    762 If non-nil, this must be a string or character, and is used as
    763 indentation of `file' sections inserted by `magit-insert-*-files'.
    764 `file' sections that are part of diffs are not affected.  This is mainly
    765 intended as a workaround for https://github.com/magit/magit/issues/5176,
    766 in which case ?\N{ZERO WIDTH SPACE} is a good value.")
    767 
    768 (defun magit-insert-files (type fn &optional nogroup)
    769   (when-let ((files (funcall fn)))
    770     (let* ((base (car magit-buffer-diff-files))
    771            (base (and base (file-directory-p base) base))
    772            (title (symbol-name type)))
    773       (magit-insert-section ((eval type) nil t)
    774         (magit-insert-heading (length files)
    775           (format "%c%s files"
    776                   (capitalize (aref title 0))
    777                   (substring title 1)))
    778         (magit-insert-files-1 files base nogroup)
    779         (insert ?\n)))))
    780 
    781 (defun magit-insert-files-1 (files directory &optional nogroup)
    782   (while (and files (or nogroup
    783                         (not directory)
    784                         (string-prefix-p directory (car files))))
    785     (let ((dir (file-name-directory (car files))))
    786       (if (or nogroup (equal dir directory))
    787           (let ((file (pop files)))
    788             (magit-insert-section (file file)
    789               (when magit-file-section-indent
    790                 (insert magit-file-section-indent))
    791               (insert (propertize file 'font-lock-face 'magit-filename) ?\n)))
    792         (magit-insert-section (file dir t)
    793           (when magit-file-section-indent
    794             (insert magit-file-section-indent))
    795           (insert (propertize dir 'file 'magit-filename) ?\n)
    796           (magit-insert-heading)
    797           (setq files (magit-insert-files-1 files dir))))))
    798   files)
    799 
    800 ;;; _
    801 (provide 'magit-status)
    802 ;;; magit-status.el ends here