config

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

magit.el (29786B)


      1 ;;; magit.el --- A Git porcelain inside Emacs  -*- lexical-binding:t; coding:utf-8 -*-
      2 
      3 ;; Copyright (C) 2008-2024 The Magit Project Contributors
      4 
      5 ;; Author: Marius Vollmer <marius.vollmer@gmail.com>
      6 ;;     Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
      7 ;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
      8 ;;     Kyle Meyer <kyle@kyleam.com>
      9 ;; Former-Maintainers:
     10 ;;     Nicolas Dudebout <nicolas.dudebout@gatech.edu>
     11 ;;     Noam Postavsky <npostavs@users.sourceforge.net>
     12 ;;     Peter J. Weisberg <pj@irregularexpressions.net>
     13 ;;     Phil Jackson <phil@shellarchive.co.uk>
     14 ;;     Rémi Vanicat <vanicat@debian.org>
     15 ;;     Yann Hodique <yann.hodique@gmail.com>
     16 
     17 ;; Homepage: https://github.com/magit/magit
     18 ;; Keywords: git tools vc
     19 
     20 ;; Package-Version: 3.3.0.50-git
     21 ;; Package-Requires: (
     22 ;;     (emacs "25.1")
     23 ;;     (compat "29.1.4.4")
     24 ;;     (dash "2.19.1")
     25 ;;     (git-commit "3.3.0")
     26 ;;     (magit-section "3.3.0")
     27 ;;     (seq "2.24")
     28 ;;     (transient "0.5.3")
     29 ;;     (with-editor "3.3.2"))
     30 
     31 ;; SPDX-License-Identifier: GPL-3.0-or-later
     32 
     33 ;; Magit is free software: you can redistribute it and/or modify
     34 ;; it under the terms of the GNU General Public License as published
     35 ;; by the Free Software Foundation, either version 3 of the License,
     36 ;; or (at your option) any later version.
     37 ;;
     38 ;; Magit is distributed in the hope that it will be useful,
     39 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     40 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     41 ;; GNU General Public License for more details.
     42 ;;
     43 ;; You should have received a copy of the GNU General Public License
     44 ;; along with Magit.  If not, see <https://www.gnu.org/licenses/>.
     45 
     46 ;; You should have received a copy of the AUTHORS.md file, which
     47 ;; lists all contributors.  If not, see https://magit.vc/authors.
     48 
     49 ;;; Commentary:
     50 
     51 ;; Magit is a text-based Git user interface that puts an unmatched focus
     52 ;; on streamlining workflows.  Commands are invoked using short mnemonic
     53 ;; key sequences that take the cursor’s position in the highly actionable
     54 ;; interface into account to provide context-sensitive behavior.
     55 
     56 ;; With Magit you can do nearly everything that you can do when using Git
     57 ;; on the command-line, but at greater speed and while taking advantage
     58 ;; of advanced features that previously seemed too daunting to use on a
     59 ;; daily basis.  Many users will find that by using Magit they can become
     60 ;; more effective Git user.
     61 
     62 ;;; Code:
     63 
     64 (require 'magit-core)
     65 (require 'magit-diff)
     66 (require 'magit-log)
     67 (require 'magit-wip)
     68 (require 'magit-apply)
     69 (require 'magit-repos)
     70 (require 'git-commit)
     71 
     72 (require 'format-spec)
     73 (require 'package nil t) ; used in `magit-version'
     74 (require 'with-editor)
     75 
     76 ;; For `magit:--gpg-sign'
     77 (declare-function epg-list-keys "epg" (context &optional name mode))
     78 (declare-function epg-decode-dn "epg" (alist))
     79 (defvar epa-protocol)
     80 
     81 ;;; Options
     82 
     83 (defcustom magit-openpgp-default-signing-key nil
     84   "Fingerprint of your default Openpgp key used for signing.
     85 If the specified primary key has signing capacity then it is used
     86 as the value of the `--gpg-sign' argument without prompting, even
     87 when other such keys exist.  To be able to select another key you
     88 must then use a prefix argument."
     89   :package-version '(magit . "4.0.0")
     90   :group 'magit-commands
     91   :type 'string)
     92 
     93 ;;; Faces
     94 
     95 (defface magit-header-line
     96   '((t :inherit magit-section-heading))
     97   "Face for the `header-line' in some Magit modes.
     98 Note that some modes, such as `magit-log-select-mode', have their
     99 own faces for the `header-line', or for parts of the
    100 `header-line'."
    101   :group 'magit-faces)
    102 
    103 (defface magit-header-line-key
    104   '((t :inherit font-lock-builtin-face))
    105   "Face for keys in the `header-line'."
    106   :group 'magit-faces)
    107 
    108 (defface magit-dimmed
    109   '((((class color) (background light)) :foreground "grey50")
    110     (((class color) (background  dark)) :foreground "grey50"))
    111   "Face for text that shouldn't stand out."
    112   :group 'magit-faces)
    113 
    114 (defface magit-hash
    115   '((((class color) (background light)) :foreground "grey60")
    116     (((class color) (background  dark)) :foreground "grey40"))
    117   "Face for the commit object name in the log output."
    118   :group 'magit-faces)
    119 
    120 (defface magit-tag
    121   '((((class color) (background light)) :foreground "Goldenrod4")
    122     (((class color) (background  dark)) :foreground "LightGoldenrod2"))
    123   "Face for tag labels shown in log buffer."
    124   :group 'magit-faces)
    125 
    126 (defface magit-branch-remote
    127   '((((class color) (background light)) :foreground "DarkOliveGreen4")
    128     (((class color) (background  dark)) :foreground "DarkSeaGreen2"))
    129   "Face for remote branch head labels shown in log buffer."
    130   :group 'magit-faces)
    131 
    132 (defface magit-branch-remote-head
    133   '((((supports (:box t))) :inherit magit-branch-remote :box t)
    134     (t                     :inherit magit-branch-remote :inverse-video t))
    135   "Face for current branch."
    136   :group 'magit-faces)
    137 
    138 (defface magit-branch-local
    139   '((((class color) (background light)) :foreground "SkyBlue4")
    140     (((class color) (background  dark)) :foreground "LightSkyBlue1"))
    141   "Face for local branches."
    142   :group 'magit-faces)
    143 
    144 (defface magit-branch-current
    145   '((((supports (:box t))) :inherit magit-branch-local :box t)
    146     (t                     :inherit magit-branch-local :inverse-video t))
    147   "Face for current branch."
    148   :group 'magit-faces)
    149 
    150 (defface magit-branch-upstream
    151   '((t :slant italic))
    152   "Face for upstream branch.
    153 This face is only used in logs and it gets combined
    154  with `magit-branch-local', `magit-branch-remote'
    155 and/or `magit-branch-remote-head'."
    156   :group 'magit-faces)
    157 
    158 (defface magit-branch-warning
    159   '((t :inherit warning))
    160   "Face for warning about (missing) branch."
    161   :group 'magit-faces)
    162 
    163 (defface magit-head
    164   '((((class color) (background light)) :inherit magit-branch-local)
    165     (((class color) (background  dark)) :inherit magit-branch-local))
    166   "Face for the symbolic ref `HEAD'."
    167   :group 'magit-faces)
    168 
    169 (defface magit-refname
    170   '((((class color) (background light)) :foreground "grey30")
    171     (((class color) (background  dark)) :foreground "grey80"))
    172   "Face for refnames without a dedicated face."
    173   :group 'magit-faces)
    174 
    175 (defface magit-refname-stash
    176   '((t :inherit magit-refname))
    177   "Face for stash refnames."
    178   :group 'magit-faces)
    179 
    180 (defface magit-refname-wip
    181   '((t :inherit magit-refname))
    182   "Face for wip refnames."
    183   :group 'magit-faces)
    184 
    185 (defface magit-refname-pullreq
    186   '((t :inherit magit-refname))
    187   "Face for pullreq refnames."
    188   :group 'magit-faces)
    189 
    190 (defface magit-keyword
    191   '((t :inherit font-lock-string-face))
    192   "Face for parts of commit messages inside brackets."
    193   :group 'magit-faces)
    194 
    195 (defface magit-keyword-squash
    196   '((t :inherit font-lock-warning-face))
    197   "Face for squash! and fixup! keywords in commit messages."
    198   :group 'magit-faces)
    199 
    200 (defface magit-signature-good
    201   '((t :foreground "green"))
    202   "Face for good signatures."
    203   :group 'magit-faces)
    204 
    205 (defface magit-signature-bad
    206   '((t :foreground "red" :weight bold))
    207   "Face for bad signatures."
    208   :group 'magit-faces)
    209 
    210 (defface magit-signature-untrusted
    211   '((t :foreground "medium aquamarine"))
    212   "Face for good untrusted signatures."
    213   :group 'magit-faces)
    214 
    215 (defface magit-signature-expired
    216   '((t :foreground "orange"))
    217   "Face for signatures that have expired."
    218   :group 'magit-faces)
    219 
    220 (defface magit-signature-expired-key
    221   '((t :inherit magit-signature-expired))
    222   "Face for signatures made by an expired key."
    223   :group 'magit-faces)
    224 
    225 (defface magit-signature-revoked
    226   '((t :foreground "violet red"))
    227   "Face for signatures made by a revoked key."
    228   :group 'magit-faces)
    229 
    230 (defface magit-signature-error
    231   '((t :foreground "light blue"))
    232   "Face for signatures that cannot be checked (e.g., missing key)."
    233   :group 'magit-faces)
    234 
    235 (defface magit-cherry-unmatched
    236   '((t :foreground "cyan"))
    237   "Face for unmatched cherry commits."
    238   :group 'magit-faces)
    239 
    240 (defface magit-cherry-equivalent
    241   '((t :foreground "magenta"))
    242   "Face for equivalent cherry commits."
    243   :group 'magit-faces)
    244 
    245 (defface magit-filename
    246   '((t :weight normal))
    247   "Face for filenames."
    248   :group 'magit-faces)
    249 
    250 ;;; Global Bindings
    251 
    252 ;;;###autoload
    253 (defcustom magit-define-global-key-bindings 'default
    254   "Which set of key bindings to add to the global keymap, if any.
    255 
    256 This option controls which set of Magit key bindings, if any, may
    257 be added to the global keymap, even before Magit is first used in
    258 the current Emacs session.
    259 
    260 If the value is nil, no bindings are added.
    261 
    262 If `default', maybe add:
    263 
    264     C-x g     `magit-status'
    265     C-x M-g   `magit-dispatch'
    266     C-c M-g   `magit-file-dispatch'
    267 
    268 If `recommended', maybe add:
    269 
    270     C-x g     `magit-status'
    271     C-c g     `magit-dispatch'
    272     C-c f     `magit-file-dispatch'
    273 
    274     These bindings are strongly recommended, but we cannot use
    275     them by default, because the \"C-c <LETTER>\" namespace is
    276     strictly reserved for bindings added by the user.
    277 
    278 The bindings in the chosen set may be added when
    279 `after-init-hook' is run.  Each binding is added if, and only
    280 if, at that time no other key is bound to the same command,
    281 and no other command is bound to the same key.  In other words
    282 we try to avoid adding bindings that are unnecessary, as well
    283 as bindings that conflict with other bindings.
    284 
    285 Adding these bindings is delayed until `after-init-hook' is
    286 run to allow users to set the variable anywhere in their init
    287 file (without having to make sure to do so before `magit' is
    288 loaded or autoloaded) and to increase the likelihood that all
    289 the potentially conflicting user bindings have already been
    290 added.
    291 
    292 To set this variable use either `setq' or the Custom interface.
    293 Do not use the function `customize-set-variable' because doing
    294 that would cause Magit to be loaded immediately, when that form
    295 is evaluated (this differs from `custom-set-variables', which
    296 doesn't load the libraries that define the customized variables).
    297 
    298 Setting this variable has no effect if `after-init-hook' has
    299 already been run."
    300   :package-version '(magit . "4.0.0")
    301   :group 'magit-essentials
    302   :type '(choice (const :tag "Add no binding" nil)
    303                  (const :tag "Use default bindings" default)
    304                  (const :tag "Use recommended bindings" recommended)))
    305 
    306 ;;;###autoload
    307 (progn
    308   (defun magit-maybe-define-global-key-bindings (&optional force)
    309     "See variable `magit-define-global-key-bindings'."
    310     (when magit-define-global-key-bindings
    311       (let ((map (current-global-map)))
    312         (pcase-dolist (`(,key . ,def)
    313                        (cond ((eq magit-define-global-key-bindings 'recommended)
    314                               '(("C-x g"   . magit-status)
    315                                 ("C-c g"   . magit-dispatch)
    316                                 ("C-c f"   . magit-file-dispatch)))
    317                              ('(("C-x g"   . magit-status)
    318                                 ("C-x M-g" . magit-dispatch)
    319                                 ("C-c M-g" . magit-file-dispatch)))))
    320           ;; This is autoloaded and thus is used before `compat' is
    321           ;; loaded, so we cannot use `keymap-lookup' and `keymap-set'.
    322           (when (or force
    323                     (not (or (lookup-key map (kbd key))
    324                              (where-is-internal def (make-sparse-keymap) t))))
    325             (define-key map (kbd key) def))))))
    326   (if after-init-time
    327       (magit-maybe-define-global-key-bindings)
    328     (add-hook 'after-init-hook #'magit-maybe-define-global-key-bindings t)))
    329 
    330 ;;; Dispatch Popup
    331 
    332 ;;;###autoload (autoload 'magit-dispatch "magit" nil t)
    333 (transient-define-prefix magit-dispatch ()
    334   "Invoke a Magit command from a list of available commands."
    335   :info-manual "(magit)Top"
    336   ["Transient and dwim commands"
    337    ;; → bound in magit-mode-map or magit-section-mode-map
    338    ;; ↓ bound below
    339    [("A" "Apply"          magit-cherry-pick)
    340     ;; a                  ↓
    341     ("b" "Branch"         magit-branch)
    342     ("B" "Bisect"         magit-bisect)
    343     ("c" "Commit"         magit-commit)
    344     ("C" "Clone"          magit-clone)
    345     ("d" "Diff"           magit-diff)
    346     ("D" "Diff (change)"  magit-diff-refresh)
    347     ("e" "Ediff (dwim)"   magit-ediff-dwim)
    348     ("E" "Ediff"          magit-ediff)
    349     ("f" "Fetch"          magit-fetch)
    350     ("F" "Pull"           magit-pull)
    351     ;; g                  ↓
    352     ;; G                → magit-refresh-all
    353     ("h" "Help"           magit-info)
    354     ("H" "Section info"   magit-describe-section :if-derived magit-mode)]
    355    [("i" "Ignore"         magit-gitignore)
    356     ("I" "Init"           magit-init)
    357     ("j" "Jump to section"magit-status-jump  :if-mode     magit-status-mode)
    358     ("j" "Display status" magit-status-quick :if-not-mode magit-status-mode)
    359     ("J" "Display buffer" magit-display-repository-buffer)
    360     ;; k                  ↓
    361     ;; K                → magit-file-untrack
    362     ("l" "Log"            magit-log)
    363     ("L" "Log (change)"   magit-log-refresh)
    364     ("m" "Merge"          magit-merge)
    365     ("M" "Remote"         magit-remote)
    366     ;; n                → magit-section-forward
    367     ;; N       reserved → forge-dispatch
    368     ("o" "Submodule"      magit-submodule)
    369     ("O" "Subtree"        magit-subtree)
    370     ;; p                → magit-section-backward
    371     ("P" "Push"           magit-push)
    372     ;; q                → magit-mode-bury-buffer
    373     ("Q" "Command"        magit-git-command)]
    374    [("r" "Rebase"         magit-rebase)
    375     ;; R                → magit-file-rename
    376     ;; s                  ↓
    377     ;; S                  ↓
    378     ("t" "Tag"            magit-tag)
    379     ("T" "Note"           magit-notes)
    380     ;; u                  ↓
    381     ;; U                  ↓
    382     ;; v                  ↓
    383     ("V" "Revert"         magit-revert)
    384     ("w" "Apply patches"  magit-am)
    385     ("W" "Format patches" magit-patch)
    386     ;; x                → magit-reset-quickly
    387     ("X" "Reset"          magit-reset)
    388     ("y" "Show Refs"      magit-show-refs)
    389     ("Y" "Cherries"       magit-cherry)
    390     ("z" "Stash"          magit-stash)
    391     ("Z" "Worktree"       magit-worktree)
    392     ("!" "Run"            magit-run)]]
    393   ["Applying changes"
    394    :if-derived magit-mode
    395    [("a" "Apply"          magit-apply)
    396     ("v" "Reverse"        magit-reverse)
    397     ("k" "Discard"        magit-discard)]
    398    [("s" "Stage"          magit-stage)
    399     ("u" "Unstage"        magit-unstage)]
    400    [("S" "Stage all"      magit-stage-modified)
    401     ("U" "Unstage all"    magit-unstage-all)]]
    402   ["Essential commands"
    403    :if-derived magit-mode
    404    [("g" "       Refresh current buffer"   magit-refresh)
    405     ("q" "       Bury current buffer"      magit-mode-bury-buffer)
    406     ("<tab>" "   Toggle section at point"  magit-section-toggle)
    407     ("<return>" "Visit thing at point"     magit-visit-thing)]
    408    [("C-x m"    "Show all key bindings"    describe-mode)
    409     ("C-x i"    "Show Info manual"         magit-info)]])
    410 
    411 ;;; Git Popup
    412 
    413 (defcustom magit-shell-command-verbose-prompt t
    414   "Whether to show the working directory when reading a command.
    415 This affects `magit-git-command', `magit-git-command-topdir',
    416 `magit-shell-command', and `magit-shell-command-topdir'."
    417   :package-version '(magit . "2.11.0")
    418   :group 'magit-commands
    419   :type 'boolean)
    420 
    421 (defvar magit-git-command-history nil)
    422 
    423 ;;;###autoload (autoload 'magit-run "magit" nil t)
    424 (transient-define-prefix magit-run ()
    425   "Run git or another command, or launch a graphical utility."
    426   [["Run git subcommand"
    427     ("!" "in repository root"   magit-git-command-topdir)
    428     ("p" "in working directory" magit-git-command)]
    429    ["Run shell command"
    430     ("s" "in repository root"   magit-shell-command-topdir)
    431     ("S" "in working directory" magit-shell-command)]
    432    ["Launch"
    433     ("k" "gitk"                 magit-run-gitk)
    434     ("a" "gitk --all"           magit-run-gitk-all)
    435     ("b" "gitk --branches"      magit-run-gitk-branches)
    436     ("g" "git gui"              magit-run-git-gui)
    437     ("m" "git mergetool --gui"  magit-git-mergetool)]])
    438 
    439 ;;;###autoload
    440 (defun magit-git-command (command)
    441   "Execute COMMAND asynchronously; display output.
    442 
    443 Interactively, prompt for COMMAND in the minibuffer. \"git \" is
    444 used as initial input, but can be deleted to run another command.
    445 
    446 With a prefix argument COMMAND is run in the top-level directory
    447 of the current working tree, otherwise in `default-directory'."
    448   (interactive (list (magit-read-shell-command nil "git ")))
    449   (magit--shell-command command))
    450 
    451 ;;;###autoload
    452 (defun magit-git-command-topdir (command)
    453   "Execute COMMAND asynchronously; display output.
    454 
    455 Interactively, prompt for COMMAND in the minibuffer. \"git \" is
    456 used as initial input, but can be deleted to run another command.
    457 
    458 COMMAND is run in the top-level directory of the current
    459 working tree."
    460   (interactive (list (magit-read-shell-command t "git ")))
    461   (magit--shell-command command (magit-toplevel)))
    462 
    463 ;;;###autoload
    464 (defun magit-shell-command (command)
    465   "Execute COMMAND asynchronously; display output.
    466 
    467 Interactively, prompt for COMMAND in the minibuffer.  With a
    468 prefix argument COMMAND is run in the top-level directory of
    469 the current working tree, otherwise in `default-directory'."
    470   (interactive (list (magit-read-shell-command)))
    471   (magit--shell-command command))
    472 
    473 ;;;###autoload
    474 (defun magit-shell-command-topdir (command)
    475   "Execute COMMAND asynchronously; display output.
    476 
    477 Interactively, prompt for COMMAND in the minibuffer.  COMMAND
    478 is run in the top-level directory of the current working tree."
    479   (interactive (list (magit-read-shell-command t)))
    480   (magit--shell-command command (magit-toplevel)))
    481 
    482 (defun magit--shell-command (command &optional directory)
    483   (let ((default-directory (or directory default-directory)))
    484     (with-environment-variables (("GIT_PAGER" "cat"))
    485       (magit--with-connection-local-variables
    486         (magit-with-editor
    487           (magit-start-process shell-file-name nil
    488                                shell-command-switch command)))))
    489   (magit-process-buffer))
    490 
    491 (defun magit-read-shell-command (&optional toplevel initial-input)
    492   (let ((default-directory
    493          (if (or toplevel current-prefix-arg)
    494              (or (magit-toplevel)
    495                  (magit--not-inside-repository-error))
    496            default-directory)))
    497     (read-shell-command (if magit-shell-command-verbose-prompt
    498                             (format "Async shell command in %s: "
    499                                     (abbreviate-file-name default-directory))
    500                           "Async shell command: ")
    501                         initial-input 'magit-git-command-history)))
    502 
    503 ;;; Shared Infix Arguments
    504 
    505 (transient-define-argument magit:--gpg-sign ()
    506   :description "Sign using gpg"
    507   :class 'transient-option
    508   :shortarg "-S"
    509   :argument "--gpg-sign="
    510   :allow-empty t
    511   :reader #'magit-read-gpg-signing-key)
    512 
    513 (defvar magit-gpg-secret-key-hist nil)
    514 
    515 (defun magit-read-gpg-secret-key
    516     (prompt &optional initial-input history predicate default)
    517   (require 'epa)
    518   (let* ((keys (cl-mapcan
    519                 (lambda (cert)
    520                   (and (or (not predicate)
    521                            (funcall predicate cert))
    522                        (let* ((key (car (epg-key-sub-key-list cert)))
    523                               (fpr (epg-sub-key-fingerprint key))
    524                               (id  (epg-sub-key-id key))
    525                               (author
    526                                (and-let* ((id-obj
    527                                            (car (epg-key-user-id-list cert))))
    528                                  (let ((id-str (epg-user-id-string id-obj)))
    529                                    (if (stringp id-str)
    530                                        id-str
    531                                      (epg-decode-dn id-obj))))))
    532                          (list
    533                           (propertize fpr 'display
    534                                       (concat (substring fpr 0 (- (length id)))
    535                                               (propertize id 'face 'highlight)
    536                                               " " author))))))
    537                 (epg-list-keys (epg-make-context epa-protocol) nil t)))
    538          (choice (or (and (not current-prefix-arg)
    539                           (or (and (length= keys 1) (car keys))
    540                               (and default (car (member default keys)))))
    541                      (completing-read prompt keys nil nil nil
    542                                       history nil initial-input))))
    543     (set-text-properties 0 (length choice) nil choice)
    544     choice))
    545 
    546 (defun magit-read-gpg-signing-key (prompt &optional initial-input history)
    547   (magit-read-gpg-secret-key
    548    prompt initial-input history
    549    (lambda (cert)
    550      (cl-some (lambda (key)
    551                 (memq 'sign (epg-sub-key-capability key)))
    552               (epg-key-sub-key-list cert)))
    553    magit-openpgp-default-signing-key))
    554 
    555 ;;; Font-Lock Keywords
    556 
    557 (defconst magit-font-lock-keywords
    558   (eval-when-compile
    559     `((,(concat "(\\(magit-define-section-jumper\\)\\_>"
    560                 "[ \t'(]*"
    561                 "\\(\\(?:\\sw\\|\\s_\\)+\\)?")
    562        (1 'font-lock-keyword-face)
    563        (2 'font-lock-function-name-face nil t))
    564       (,(concat "(" (regexp-opt '("magit-insert-section"
    565                                   "magit-section-case"
    566                                   "magit-bind-match-strings"
    567                                   "magit-with-temp-index"
    568                                   "magit-with-blob"
    569                                   "magit-with-toplevel")
    570                                 t)
    571                 "\\_>")
    572        . 1))))
    573 
    574 (font-lock-add-keywords 'emacs-lisp-mode magit-font-lock-keywords)
    575 
    576 ;;; Version
    577 
    578 (defvar magit-version #'undefined
    579   "The version of Magit that you're using.
    580 Use the function by the same name instead of this variable.")
    581 
    582 ;;;###autoload
    583 (defun magit-version (&optional print-dest interactive)
    584   "Return the version of Magit currently in use.
    585 
    586 If optional argument PRINT-DEST is non-nil, also print the used
    587 versions of Magit, Transient, Git and Emacs to the output stream
    588 selected by that argument.  Interactively use the echo area, or
    589 with a prefix argument use the current buffer.  Additionally put
    590 the output in the kill ring.
    591 \n(fn &optional PRINT-DEST)"
    592   (interactive (list (if current-prefix-arg (current-buffer) t) t))
    593   (let ((magit-git-global-arguments nil)
    594         (toplib (or load-file-name buffer-file-name))
    595         debug)
    596     (unless (and toplib
    597                  (member (file-name-nondirectory toplib)
    598                          '("magit.el" "magit.el.gz")))
    599       (let ((load-suffixes (reverse load-suffixes))) ; prefer .el than .elc
    600         (setq toplib (locate-library "magit"))))
    601     (setq toplib (and toplib (magit--straight-chase-links toplib)))
    602     (push toplib debug)
    603     (when toplib
    604       (let* ((topdir (file-name-directory toplib))
    605              (gitdir (expand-file-name
    606                       ".git" (file-name-directory
    607                               (directory-file-name topdir))))
    608              (static (locate-library "magit-version.el" nil (list topdir)))
    609              (static (and static (magit--straight-chase-links static))))
    610         (or (progn
    611               (push 'repo debug)
    612               (when (and (file-exists-p gitdir)
    613                          ;; It is a repo, but is it the Magit repo?
    614                          (file-exists-p
    615                           (expand-file-name "../lisp/magit.el" gitdir)))
    616                 (push t debug)
    617                 ;; Inside the repo the version file should only exist
    618                 ;; while running make.
    619                 (when (and static (not noninteractive))
    620                   (ignore-errors (delete-file static)))
    621                 (setq magit-version
    622                       (let ((default-directory topdir))
    623                         (magit-git-string "describe"
    624                                           "--tags" "--dirty" "--always")))))
    625             (progn
    626               (push 'static debug)
    627               (when (and static (file-exists-p static))
    628                 (push t debug)
    629                 (load-file static)
    630                 magit-version))
    631             (when (featurep 'package)
    632               (push 'elpa debug)
    633               (ignore-errors
    634                 (when-let ((version (cadr (assq 'magit package-alist))))
    635                   (push t debug)
    636                   (setq magit-version
    637                         (and (fboundp 'package-desc-version)
    638                              (package-version-join
    639                               (package-desc-version version)))))))
    640             (progn
    641               (push 'dirname debug)
    642               (let ((dirname (file-name-nondirectory
    643                               (directory-file-name topdir))))
    644                 (when (string-match "\\`magit-\\([0-9].*\\)" dirname)
    645                   (setq magit-version (match-string 1 dirname)))))
    646             ;; If all else fails, just report the commit hash. It's
    647             ;; better than nothing and we cannot do better in the case
    648             ;; of e.g., a shallow clone.
    649             (progn
    650               (push 'hash debug)
    651               ;; Same check as above to see if it's really the Magit repo.
    652               (when (and (file-exists-p gitdir)
    653                          (file-exists-p
    654                           (expand-file-name "../lisp/magit.el" gitdir)))
    655                 (setq magit-version
    656                       (let ((default-directory topdir))
    657                         (magit-git-string "rev-parse" "HEAD"))))))))
    658     (if (stringp magit-version)
    659         (when print-dest
    660           (let ((str (format
    661                       "Magit %s%s, Transient %s, Git %s, Emacs %s, %s"
    662                       (or magit-version "(unknown)")
    663                       (or (and (ignore-errors
    664                                  (magit--version>= magit-version "2008"))
    665                                (ignore-errors
    666                                  (require 'lisp-mnt)
    667                                  (and (fboundp 'lm-header)
    668                                       (format
    669                                        " [>= %s]"
    670                                        (with-temp-buffer
    671                                          (insert-file-contents
    672                                           (locate-library "magit.el" t))
    673                                          (lm-header "Package-Version"))))))
    674                           "")
    675                       (or (ignore-errors
    676                             (require 'lisp-mnt)
    677                             (and (fboundp 'lm-header)
    678                                  (with-temp-buffer
    679                                    (insert-file-contents
    680                                     (locate-library "transient.el" t))
    681                                    (lm-header "Package-Version"))))
    682                           "(unknown)")
    683                       (magit--safe-git-version)
    684                       emacs-version
    685                       system-type)))
    686             (when interactive
    687               (kill-new str))
    688             (princ str print-dest)))
    689       (setq debug (reverse debug))
    690       (setq magit-version 'error)
    691       (when magit-version
    692         (push magit-version debug))
    693       (unless (equal (getenv "CI") "true")
    694         ;; The repository is a sparse clone.
    695         (message "Cannot determine Magit's version %S" debug)))
    696     magit-version))
    697 
    698 ;;; Startup Asserts
    699 
    700 (defun magit-startup-asserts ()
    701   (when-let ((val (getenv "GIT_DIR")))
    702     (setenv "GIT_DIR")
    703     (message
    704      "Magit unset $GIT_DIR (was %S).  See %s" val
    705      ;; Note: Pass URL as argument rather than embedding in the format
    706      ;; string to prevent the single quote from being rendered
    707      ;; according to `text-quoting-style'.
    708      "https://github.com/magit/magit/wiki/Don't-set-$GIT_DIR-and-alike"))
    709   (when-let ((val (getenv "GIT_WORK_TREE")))
    710     (setenv "GIT_WORK_TREE")
    711     (message
    712      "Magit unset $GIT_WORK_TREE (was %S).  See %s" val
    713      ;; See comment above.
    714      "https://github.com/magit/magit/wiki/Don't-set-$GIT_DIR-and-alike"))
    715   ;; Git isn't required while building Magit.
    716   (unless (bound-and-true-p byte-compile-current-file)
    717     (magit-git-version-assert))
    718   (when (version< emacs-version magit--minimal-emacs)
    719     (display-warning 'magit (format "\
    720 Magit requires Emacs >= %s, you are using %s.
    721 
    722 If this comes as a surprise to you, because you do actually have
    723 a newer version installed, then that probably means that the
    724 older version happens to appear earlier on the `$PATH'.  If you
    725 always start Emacs from a shell, then that can be fixed in the
    726 shell's init file.  If you start Emacs by clicking on an icon,
    727 or using some sort of application launcher, then you probably
    728 have to adjust the environment as seen by graphical interface.
    729 For X11 something like ~/.xinitrc should work.\n"
    730                                     magit--minimal-emacs emacs-version)
    731                      :error)))
    732 
    733 ;;; Loading Libraries
    734 
    735 (provide 'magit)
    736 
    737 (cl-eval-when (load eval)
    738   (require 'magit-status)
    739   (require 'magit-refs)
    740   (require 'magit-files)
    741   (require 'magit-reset)
    742   (require 'magit-branch)
    743   (require 'magit-merge)
    744   (require 'magit-tag)
    745   (require 'magit-worktree)
    746   (require 'magit-notes)
    747   (require 'magit-sequence)
    748   (require 'magit-commit)
    749   (require 'magit-remote)
    750   (require 'magit-clone)
    751   (require 'magit-fetch)
    752   (require 'magit-pull)
    753   (require 'magit-push)
    754   (require 'magit-bisect)
    755   (require 'magit-stash)
    756   (require 'magit-blame)
    757   (require 'magit-submodule)
    758   (unless (load "magit-autoloads" t t)
    759     (require 'magit-patch)
    760     (require 'magit-subtree)
    761     (require 'magit-ediff)
    762     (require 'magit-gitignore)
    763     (require 'magit-sparse-checkout)
    764     (require 'magit-extras)
    765     (require 'git-rebase)
    766     (require 'magit-bookmark)))
    767 
    768 (with-eval-after-load 'bookmark
    769   (require 'magit-bookmark))
    770 
    771 (unless (bound-and-true-p byte-compile-current-file)
    772   (if after-init-time
    773       (progn (magit-startup-asserts)
    774              (magit-version))
    775     (add-hook 'after-init-hook #'magit-startup-asserts t)
    776     (add-hook 'after-init-hook #'magit-version t)))
    777 
    778 ;;; magit.el ends here