magit.el (30422B)
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: 20241208.1345 21 ;; Package-Revision: c50f784855f0 22 ;; Package-Requires: ( 23 ;; (emacs "26.1") 24 ;; (compat "30.0.0.0") 25 ;; (dash "2.19.1") 26 ;; (magit-section "4.1.3") 27 ;; (seq "2.24") 28 ;; (transient "0.8.0") 29 ;; (with-editor "3.4.3")) 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 (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 nowarn) 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--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--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,%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 (let ((lib (locate-library "forge.el" t))) 684 (or (and lib 685 (format 686 " Forge %s," 687 (or (ignore-errors 688 (require 'lisp-mnt) 689 (with-temp-buffer 690 (insert-file-contents lib) 691 (and (fboundp 'lm-header) 692 (lm-header "Package-Version")))) 693 "(unknown)"))) 694 "")) 695 (magit--safe-git-version) 696 emacs-version 697 system-type))) 698 (when interactive 699 (kill-new str)) 700 (princ str print-dest))) 701 (setq debug (reverse debug)) 702 (setq magit-version 'error) 703 (when magit-version 704 (push magit-version debug)) 705 (unless (or nowarn (equal (getenv "CI") "true")) 706 (message "Cannot determine Magit's version %S" debug))) 707 magit-version)) 708 709 ;;; Startup Asserts 710 711 (defun magit-startup-asserts () 712 (when-let ((val (getenv "GIT_DIR"))) 713 (setenv "GIT_DIR") 714 (message 715 "Magit unset $GIT_DIR (was %S). See %s" val 716 ;; Note: Pass URL as argument rather than embedding in the format 717 ;; string to prevent the single quote from being rendered 718 ;; according to `text-quoting-style'. 719 "https://github.com/magit/magit/wiki/Don't-set-$GIT_DIR-and-alike")) 720 (when-let ((val (getenv "GIT_WORK_TREE"))) 721 (setenv "GIT_WORK_TREE") 722 (message 723 "Magit unset $GIT_WORK_TREE (was %S). See %s" val 724 ;; See comment above. 725 "https://github.com/magit/magit/wiki/Don't-set-$GIT_DIR-and-alike")) 726 ;; Git isn't required while building Magit. 727 (unless (bound-and-true-p byte-compile-current-file) 728 (magit-git-version-assert)) 729 (when (version< emacs-version magit--minimal-emacs) 730 (display-warning 'magit (format "\ 731 Magit requires Emacs >= %s, you are using %s. 732 733 If this comes as a surprise to you, because you do actually have 734 a newer version installed, then that probably means that the 735 older version happens to appear earlier on the `$PATH'. If you 736 always start Emacs from a shell, then that can be fixed in the 737 shell's init file. If you start Emacs by clicking on an icon, 738 or using some sort of application launcher, then you probably 739 have to adjust the environment as seen by graphical interface. 740 For X11 something like ~/.xinitrc should work.\n" 741 magit--minimal-emacs emacs-version) 742 :error))) 743 744 ;;; Loading Libraries 745 746 (provide 'magit) 747 748 (cl-eval-when (load eval) 749 (require 'magit-status) 750 (require 'magit-refs) 751 (require 'magit-files) 752 (require 'magit-reset) 753 (require 'magit-branch) 754 (require 'magit-merge) 755 (require 'magit-tag) 756 (require 'magit-worktree) 757 (require 'magit-notes) 758 (require 'magit-sequence) 759 (require 'magit-commit) 760 (require 'magit-remote) 761 (require 'magit-clone) 762 (require 'magit-fetch) 763 (require 'magit-pull) 764 (require 'magit-push) 765 (require 'magit-bisect) 766 (require 'magit-stash) 767 (require 'magit-blame) 768 (require 'magit-submodule) 769 (unless (load "magit-autoloads" t t) 770 (require 'magit-patch) 771 (require 'magit-subtree) 772 (require 'magit-ediff) 773 (require 'magit-gitignore) 774 (require 'magit-sparse-checkout) 775 (require 'magit-extras) 776 (require 'git-rebase) 777 (require 'magit-bookmark))) 778 779 (with-eval-after-load 'bookmark 780 (require 'magit-bookmark)) 781 782 (unless (bound-and-true-p byte-compile-current-file) 783 (if after-init-time 784 (progn (magit-startup-asserts) 785 (magit-version nil nil t)) 786 (add-hook 'after-init-hook #'magit-startup-asserts t) 787 (add-hook 'after-init-hook #'magit-version t))) 788 789 ;;; magit.el ends here