magit-git.el (123227B)
1 ;;; magit-git.el --- Git functionality -*- 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 wrappers for various Git plumbing commands. 26 27 ;;; Code: 28 29 (require 'magit-base) 30 31 (require 'format-spec) 32 33 ;; From `magit-branch'. 34 (defvar magit-branch-prefer-remote-upstream) 35 (defvar magit-published-branches) 36 37 ;; From `magit-margin'. 38 (declare-function magit-maybe-make-margin-overlay "magit-margin" ()) 39 40 ;; From `magit-mode'. 41 (declare-function magit-get-mode-buffer "magit-mode" 42 (mode &optional value frame)) 43 (declare-function magit-refresh "magit-mode" ()) 44 (defvar magit-buffer-diff-type) 45 (defvar magit-buffer-diff-args) 46 (defvar magit-buffer-file-name) 47 (defvar magit-buffer-log-args) 48 (defvar magit-buffer-log-files) 49 (defvar magit-buffer-refname) 50 (defvar magit-buffer-revision) 51 52 ;; From `magit-process'. 53 (declare-function magit-call-git "magit-process" (&rest args)) 54 (declare-function magit-git "magit-process" (&rest args)) 55 (declare-function magit-process-buffer "magit-process" (&optional nodisplay)) 56 (declare-function magit-process-file "magit-process" 57 (process &optional infile buffer display &rest args)) 58 (declare-function magit-process-git "magit-process" (destination &rest args)) 59 (declare-function magit-process-insert-section "magit-process" 60 (pwd program args &optional errcode errlog)) 61 (defvar magit-this-error) 62 (defvar magit-process-error-message-regexps) 63 64 (eval-when-compile 65 (cl-pushnew 'orig-rev eieio--known-slot-names) 66 (cl-pushnew 'number eieio--known-slot-names)) 67 68 ;;; Options 69 70 ;; For now this is shared between `magit-process' and `magit-git'. 71 (defgroup magit-process nil 72 "Git and other external processes used by Magit." 73 :group 'magit) 74 75 (defvar magit-git-environment 76 (list (format "INSIDE_EMACS=%s,magit" emacs-version)) 77 "Prepended to `process-environment' while running git.") 78 79 (defcustom magit-git-output-coding-system 80 (and (eq system-type 'windows-nt) 'utf-8) 81 "Coding system for receiving output from Git. 82 83 If non-nil, the Git config value `i18n.logOutputEncoding' should 84 be set via `magit-git-global-arguments' to value consistent with 85 this." 86 :package-version '(magit . "2.9.0") 87 :group 'magit-process 88 :type '(choice (coding-system :tag "Coding system to decode Git output") 89 (const :tag "Use system default" nil))) 90 91 (defvar magit-git-w32-path-hack nil 92 "Alist of (EXE . (PATHENTRY)). 93 This specifies what additional PATH setting needs to be added to 94 the environment in order to run the non-wrapper git executables 95 successfully.") 96 97 (defcustom magit-git-executable 98 (or (and (eq system-type 'windows-nt) 99 ;; Avoid the wrappers "cmd/git.exe" and "cmd/git.cmd", 100 ;; which are much slower than using "bin/git.exe" directly. 101 (and-let* ((exec (executable-find "git"))) 102 (ignore-errors 103 ;; Git for Windows 2.x provides cygpath so we can 104 ;; ask it for native paths. 105 (let* ((core-exe 106 (car 107 (process-lines 108 exec "-c" 109 "alias.X=!x() { which \"$1\" | cygpath -mf -; }; x" 110 "X" "git"))) 111 (hack-entry (assoc core-exe magit-git-w32-path-hack)) 112 ;; Running the libexec/git-core executable 113 ;; requires some extra PATH entries. 114 (path-hack 115 (list (concat "PATH=" 116 (car (process-lines 117 exec "-c" 118 "alias.P=!cygpath -wp \"$PATH\"" 119 "P")))))) 120 ;; The defcustom STANDARD expression can be 121 ;; evaluated many times, so make sure it is 122 ;; idempotent. 123 (if hack-entry 124 (setcdr hack-entry path-hack) 125 (push (cons core-exe path-hack) magit-git-w32-path-hack)) 126 core-exe)))) 127 (and (eq system-type 'darwin) 128 (executable-find "git")) 129 "git") 130 "The Git executable used by Magit on the local host. 131 On remote machines `magit-remote-git-executable' is used instead." 132 :package-version '(magit . "3.2.0") 133 :group 'magit-process 134 :type 'string) 135 136 (defcustom magit-remote-git-executable "git" 137 "The Git executable used by Magit on remote machines. 138 On the local host `magit-git-executable' is used instead. 139 Consider customizing `tramp-remote-path' instead of this 140 option." 141 :package-version '(magit . "3.2.0") 142 :group 'magit-process 143 :type 'string) 144 145 (defcustom magit-git-global-arguments 146 `("--no-pager" "--literal-pathspecs" 147 "-c" "core.preloadindex=true" 148 "-c" "log.showSignature=false" 149 "-c" "color.ui=false" 150 "-c" "color.diff=false" 151 ,@(and (eq system-type 'windows-nt) 152 (list "-c" "i18n.logOutputEncoding=UTF-8"))) 153 "Global Git arguments. 154 155 The arguments set here are used every time the git executable is 156 run as a subprocess. They are placed right after the executable 157 itself and before the git command - as in `git HERE... COMMAND 158 REST'. See the manpage `git(1)' for valid arguments. 159 160 Be careful what you add here, especially if you are using Tramp 161 to connect to servers with ancient Git versions. Never remove 162 anything that is part of the default value, unless you really 163 know what you are doing. And think very hard before adding 164 something; it will be used every time Magit runs Git for any 165 purpose." 166 :package-version '(magit . "2.9.0") 167 :group 'magit-commands 168 :group 'magit-process 169 :type '(repeat string)) 170 171 (defcustom magit-prefer-remote-upstream nil 172 "Whether to favor remote branches when reading the upstream branch. 173 174 This controls whether commands that read a branch from the user 175 and then set it as the upstream branch, offer a local or a remote 176 branch as default completion candidate, when they have the choice. 177 178 This affects all commands that use `magit-read-upstream-branch' 179 or `magit-read-starting-point', which includes most commands 180 that change the upstream and many that create new branches." 181 :package-version '(magit . "2.4.2") 182 :group 'magit-commands 183 :type 'boolean) 184 185 (defcustom magit-list-refs-namespaces 186 '("refs/heads" 187 "refs/remotes" 188 "refs/tags" 189 "refs/pullreqs") 190 "List of ref namespaces considered when reading a ref. 191 192 This controls the order of refs returned by `magit-list-refs', 193 which is called by functions like `magit-list-branch-names' to 194 generate the collection of refs." 195 :package-version '(magit . "3.1.0") 196 :group 'magit-commands 197 :type '(repeat string)) 198 199 (defcustom magit-list-refs-sortby nil 200 "How to sort the ref collection in the prompt. 201 202 This affects commands that read a ref. More specifically, it 203 controls the order of refs returned by `magit-list-refs', which 204 is called by functions like `magit-list-branch-names' to generate 205 the collection of refs. By default, refs are sorted according to 206 their full refname (i.e., \"refs/...\"). 207 208 Any value accepted by the `--sort' flag of \"git for-each-ref\" can 209 be used. For example, \"-creatordate\" places refs with more 210 recent committer or tagger dates earlier in the list. A list of 211 strings can also be given in order to pass multiple sort keys to 212 \"git for-each-ref\". 213 214 Note that, depending on the completion framework you use, this 215 may not be sufficient to change the order in which the refs are 216 displayed. It only controls the order of the collection passed 217 to `magit-completing-read' or, for commands that support reading 218 multiple strings, `read-from-minibuffer'. The completion 219 framework ultimately determines how the collection is displayed." 220 :package-version '(magit . "2.11.0") 221 :group 'magit-miscellaneous 222 :type '(choice string (repeat string))) 223 224 ;;; Git 225 226 (defvar magit-git-debug nil 227 "Whether to enable additional reporting of git errors. 228 229 Magit basically calls git for one of these two reasons: for 230 side-effects or to do something with its standard output. 231 232 When git is run for side-effects then its output, including error 233 messages, go into the process buffer which is shown when using \ 234 \\<magit-status-mode-map>\\[magit-process-buffer]. 235 236 When git's output is consumed in some way, then it would be too 237 expensive to also insert it into this buffer, but when this 238 option is non-nil and git returns with a non-zero exit status, 239 then at least its standard error is inserted into this buffer. 240 241 This is only intended for debugging purposes. Do not enable this 242 permanently, that would negatively affect performance. Also note 243 that just because git exits with a non-zero exit status and prints 244 an error message that usually doesn't mean that it is an error as 245 far as Magit is concerned, which is another reason we usually hide 246 these error messages. Whether some error message is relevant in 247 the context of some unexpected behavior has to be judged on a case 248 by case basis. 249 250 The command `magit-toggle-git-debug' changes the value of this 251 variable. 252 253 Also see `magit-process-extreme-logging'.") 254 255 (defun magit-toggle-git-debug () 256 "Toggle whether additional git errors are reported. 257 See info node `(magit)Debugging Tools' for more information." 258 (interactive) 259 (setq magit-git-debug (not magit-git-debug)) 260 (message "Additional reporting of Git errors %s" 261 (if magit-git-debug "enabled" "disabled"))) 262 263 (defvar magit--refresh-cache nil) 264 265 (defmacro magit--with-refresh-cache (key &rest body) 266 (declare (indent 1) (debug (form body))) 267 (let ((k (cl-gensym)) 268 (hit (cl-gensym))) 269 `(if magit--refresh-cache 270 (let ((,k ,key)) 271 (if-let ((,hit (assoc ,k (cdr magit--refresh-cache)))) 272 (progn (cl-incf (caar magit--refresh-cache)) 273 (cdr ,hit)) 274 (cl-incf (cdar magit--refresh-cache)) 275 (let ((value ,(macroexp-progn body))) 276 (push (cons ,k value) 277 (cdr magit--refresh-cache)) 278 value))) 279 ,@body))) 280 281 (defvar magit-with-editor-envvar "GIT_EDITOR" 282 "The environment variable exported by `magit-with-editor'. 283 Set this to \"GIT_SEQUENCE_EDITOR\" if you do not want to use 284 Emacs to edit commit messages but would like to do so to edit 285 rebase sequences.") 286 287 (defmacro magit-with-editor (&rest body) 288 "Like `with-editor*' but let-bind some more variables. 289 Also respect the value of `magit-with-editor-envvar'." 290 (declare (indent 0) (debug (body))) 291 `(let ((magit-process-popup-time -1) 292 ;; The user may have customized `shell-file-name' to 293 ;; something which results in `w32-shell-dos-semantics' nil 294 ;; (which changes the quoting style used by 295 ;; `shell-quote-argument'), but Git for Windows expects shell 296 ;; quoting in the dos style. 297 (shell-file-name (if (and (eq system-type 'windows-nt) 298 ;; If we have Cygwin mount points, 299 ;; the git flavor is cygwin, so dos 300 ;; shell quoting is probably wrong. 301 (not magit-cygwin-mount-points)) 302 "cmdproxy" 303 shell-file-name))) 304 (with-editor* magit-with-editor-envvar 305 ,@body))) 306 307 (defmacro magit--with-temp-process-buffer (&rest body) 308 "Like `with-temp-buffer', but always propagate `process-environment'. 309 When that var is buffer-local in the calling buffer, it is not 310 propagated by `with-temp-buffer', so we explicitly ensure that 311 happens, so that processes will be invoked consistently. BODY is 312 as for that macro." 313 (declare (indent 0) (debug (body))) 314 (let ((p (cl-gensym))) 315 `(let ((,p process-environment)) 316 (with-temp-buffer 317 (setq-local process-environment ,p) 318 ,@body)))) 319 320 (defsubst magit-git-executable () 321 "Return value of `magit-git-executable' or `magit-remote-git-executable'. 322 The variable is chosen depending on whether `default-directory' 323 is remote." 324 (if (file-remote-p default-directory) 325 magit-remote-git-executable 326 magit-git-executable)) 327 328 (defun magit-process-git-arguments (args) 329 "Prepare ARGS for a function that invokes Git. 330 331 Magit has many specialized functions for running Git; they all 332 pass arguments through this function before handing them to Git, 333 to do the following. 334 335 * Flatten ARGS, removing nil arguments. 336 * Prepend `magit-git-global-arguments' to ARGS. 337 * On w32 systems, encode to `w32-ansi-code-page'." 338 (setq args (append magit-git-global-arguments (flatten-tree args))) 339 (if (and (eq system-type 'windows-nt) (boundp 'w32-ansi-code-page)) 340 ;; On w32, the process arguments *must* be encoded in the 341 ;; current code-page (see #3250). 342 (mapcar (lambda (arg) 343 (encode-coding-string 344 arg (intern (format "cp%d" w32-ansi-code-page)))) 345 args) 346 args)) 347 348 (defun magit-git-exit-code (&rest args) 349 "Execute Git with ARGS, returning its exit code." 350 (magit-process-git nil args)) 351 352 (defun magit-git-success (&rest args) 353 "Execute Git with ARGS, returning t if its exit code is 0." 354 (= (magit-git-exit-code args) 0)) 355 356 (defun magit-git-failure (&rest args) 357 "Execute Git with ARGS, returning t if its exit code is 1." 358 (= (magit-git-exit-code args) 1)) 359 360 (defun magit-git-string-p (&rest args) 361 "Execute Git with ARGS, returning the first line of its output. 362 If the exit code isn't zero or if there is no output, then return 363 nil. Neither of these results is considered an error; if that is 364 what you want, then use `magit-git-string-ng' instead. 365 366 This is an experimental replacement for `magit-git-string', and 367 still subject to major changes." 368 (magit--with-refresh-cache (cons default-directory args) 369 (magit--with-temp-process-buffer 370 (and (zerop (magit-process-git t args)) 371 (not (bobp)) 372 (progn 373 (goto-char (point-min)) 374 (buffer-substring-no-properties (point) (line-end-position))))))) 375 376 (defun magit-git-string-ng (&rest args) 377 "Execute Git with ARGS, returning the first line of its output. 378 If the exit code isn't zero or if there is no output, then that 379 is considered an error, but instead of actually signaling an 380 error, return nil. Additionally the output is put in the process 381 buffer (creating it if necessary) and the error message is shown 382 in the status buffer (provided it exists). 383 384 This is an experimental replacement for `magit-git-string', and 385 still subject to major changes. Also see `magit-git-string-p'." 386 (magit--with-refresh-cache 387 (list default-directory 'magit-git-string-ng args) 388 (magit--with-temp-process-buffer 389 (let* ((args (magit-process-git-arguments args)) 390 (status (magit-process-git t args))) 391 (if (zerop status) 392 (and (not (bobp)) 393 (progn 394 (goto-char (point-min)) 395 (buffer-substring-no-properties 396 (point) (line-end-position)))) 397 (let ((buf (current-buffer))) 398 (with-current-buffer (magit-process-buffer t) 399 (magit-process-insert-section default-directory 400 magit-git-executable args 401 status buf))) 402 (when-let ((status-buf (magit-get-mode-buffer 'magit-status-mode))) 403 (let ((msg (magit--locate-error-message))) 404 (with-current-buffer status-buf 405 (setq magit-this-error msg)))) 406 nil))))) 407 408 (defun magit-git-str (&rest args) 409 "Execute Git with ARGS, returning the first line of its output. 410 If there is no output, return nil. If the output begins with a 411 newline, return an empty string. Like `magit-git-string' but 412 ignore `magit-git-debug'." 413 (setq args (flatten-tree args)) 414 (magit--with-refresh-cache (cons default-directory args) 415 (magit--with-temp-process-buffer 416 (magit-process-git (list t nil) args) 417 (unless (bobp) 418 (goto-char (point-min)) 419 (buffer-substring-no-properties (point) (line-end-position)))))) 420 421 (defun magit-git-output (&rest args) 422 "Execute Git with ARGS, returning its output." 423 (setq args (flatten-tree args)) 424 (magit--with-refresh-cache (cons default-directory args) 425 (magit--with-temp-process-buffer 426 (magit-process-git (list t nil) args) 427 (buffer-substring-no-properties (point-min) (point-max))))) 428 429 (define-error 'magit-invalid-git-boolean "Not a Git boolean") 430 431 (defun magit-git-true (&rest args) 432 "Execute Git with ARGS, returning t if it prints \"true\". 433 If it prints \"false\", then return nil. For any other output 434 signal `magit-invalid-git-boolean'." 435 (pcase (magit-git-output args) 436 ((or "true" "true\n") t) 437 ((or "false" "false\n") nil) 438 (output (signal 'magit-invalid-git-boolean (list output))))) 439 440 (defun magit-git-false (&rest args) 441 "Execute Git with ARGS, returning t if it prints \"false\". 442 If it prints \"true\", then return nil. For any other output 443 signal `magit-invalid-git-boolean'." 444 (pcase (magit-git-output args) 445 ((or "true" "true\n") nil) 446 ((or "false" "false\n") t) 447 (output (signal 'magit-invalid-git-boolean (list output))))) 448 449 (defun magit-git-config-p (variable &optional default) 450 "Return the boolean value of the Git variable VARIABLE. 451 VARIABLE has to be specified as a string. Return DEFAULT (which 452 defaults to nil) if VARIABLE is unset. If VARIABLE's value isn't 453 a boolean, then raise an error." 454 (let ((args (list "config" "--bool" "--default" (if default "true" "false") 455 variable))) 456 (magit--with-refresh-cache (cons default-directory args) 457 (magit--with-temp-process-buffer 458 (let ((status (magit-process-git t args)) 459 (output (buffer-substring (point-min) (1- (point-max))))) 460 (if (zerop status) 461 (equal output "true") 462 (signal 'magit-invalid-git-boolean (list output)))))))) 463 464 (defun magit-git-insert (&rest args) 465 "Execute Git with ARGS, inserting its output at point. 466 If Git exits with a non-zero exit status, then show a message and 467 add a section in the respective process buffer." 468 (apply #'magit--git-insert nil args)) 469 470 (defun magit--git-insert (return-error &rest args) 471 (setq args (magit-process-git-arguments args)) 472 (if (or return-error magit-git-debug) 473 (let (log) 474 (unwind-protect 475 (let (exit errmsg) 476 (setq log (make-temp-file "magit-stderr")) 477 (delete-file log) 478 (setq exit (magit-process-git (list t log) args)) 479 (when (> exit 0) 480 (when (file-exists-p log) 481 (with-temp-buffer 482 (insert-file-contents log) 483 (goto-char (point-max)) 484 (setq errmsg 485 (if (functionp magit-git-debug) 486 (funcall magit-git-debug (buffer-string)) 487 (magit--locate-error-message)))) 488 (unless return-error 489 (let ((magit-git-debug nil)) 490 (with-current-buffer (magit-process-buffer t) 491 (magit-process-insert-section default-directory 492 magit-git-executable 493 args exit log))))) 494 (unless return-error 495 (if errmsg 496 (message "%s" errmsg) 497 (message "Git returned with exit-code %s" exit)))) 498 (or errmsg exit)) 499 (ignore-errors (delete-file log)))) 500 (magit-process-git (list t nil) args))) 501 502 (defun magit--locate-error-message () 503 (goto-char (point-max)) 504 (and (run-hook-wrapped 'magit-process-error-message-regexps 505 (lambda (re) (re-search-backward re nil t))) 506 (match-string-no-properties 1))) 507 508 (defun magit-git-string (&rest args) 509 "Execute Git with ARGS, returning the first line of its output. 510 If there is no output, return nil. If the output begins with a 511 newline, return an empty string." 512 (setq args (flatten-tree args)) 513 (magit--with-refresh-cache (cons default-directory args) 514 (magit--with-temp-process-buffer 515 (apply #'magit-git-insert args) 516 (unless (bobp) 517 (goto-char (point-min)) 518 (buffer-substring-no-properties (point) (line-end-position)))))) 519 520 (defun magit-git-lines (&rest args) 521 "Execute Git with ARGS, returning its output as a list of lines. 522 Empty lines anywhere in the output are omitted. 523 524 If Git exits with a non-zero exit status, then report show a 525 message and add a section in the respective process buffer." 526 (magit--with-temp-process-buffer 527 (apply #'magit-git-insert args) 528 (split-string (buffer-string) "\n" t))) 529 530 (defun magit-git-items (&rest args) 531 "Execute Git with ARGS, returning its null-separated output as a list. 532 Empty items anywhere in the output are omitted. 533 534 If Git exits with a non-zero exit status, then report show a 535 message and add a section in the respective process buffer." 536 (magit--with-temp-process-buffer 537 (apply #'magit-git-insert args) 538 (split-string (buffer-string) "\0" t))) 539 540 (defvar magit--git-wash-keep-error nil) ; experimental 541 542 (defun magit-git-wash (washer &rest args) 543 "Execute Git with ARGS, inserting washed output at point. 544 Actually first insert the raw output at point. If there is no 545 output, call `magit-cancel-section'. Otherwise temporarily narrow 546 the buffer to the inserted text, move to its beginning, and then 547 call function WASHER with ARGS as its sole argument." 548 (declare (indent 1)) 549 (apply #'magit--git-wash washer magit--git-wash-keep-error args)) 550 551 (defun magit--git-wash (washer keep-error &rest args) 552 (declare (indent 2)) 553 (setq args (flatten-tree args)) 554 (let ((beg (point)) 555 (exit (magit--git-insert keep-error args))) 556 (when (stringp exit) 557 (goto-char beg) 558 (insert (propertize exit 'face 'error)) 559 (unless (bolp) 560 (insert "\n"))) 561 (if (= (point) beg) 562 (magit-cancel-section) 563 (unless (bolp) 564 (insert "\n")) 565 (when (or (equal exit 0) 566 (eq keep-error 'wash-anyway)) 567 (save-restriction 568 (narrow-to-region beg (point)) 569 (goto-char beg) 570 (funcall washer args)) 571 (when (or (= (point) beg) 572 (= (point) (1+ beg))) 573 (magit-cancel-section)) 574 (magit-maybe-make-margin-overlay))) 575 exit)) 576 577 (defun magit-git-executable-find (command) 578 "Search for COMMAND in Git's exec path, falling back to `exec-path'. 579 Like `executable-find', return the absolute file name of the 580 executable." 581 (or (locate-file command 582 (list (concat 583 (file-remote-p default-directory) 584 (or (magit-git-string "--exec-path") 585 (error "`git --exec-path' failed")))) 586 exec-suffixes 587 #'file-executable-p) 588 (compat-call executable-find command t))) 589 590 ;;; Git Version 591 592 (defconst magit--git-version-regexp 593 "\\`git version \\([0-9]+\\(\\.[0-9]+\\)\\{1,2\\}\\)") 594 595 (defvar magit--host-git-version-cache nil) 596 597 (defun magit-git-version>= (n) 598 "Return t if `magit-git-version's value is greater than or equal to N." 599 (magit--version>= (magit-git-version) n)) 600 601 (defun magit-git-version< (n) 602 "Return t if `magit-git-version's value is smaller than N." 603 (version< (magit-git-version) n)) 604 605 (defun magit-git-version () 606 "Return the Git version used for `default-directory'. 607 Raise an error if Git cannot be found, if it exits with a 608 non-zero status, or the output does not have the expected 609 format." 610 (magit--with-refresh-cache default-directory 611 (let ((host (file-remote-p default-directory))) 612 (or (cdr (assoc host magit--host-git-version-cache)) 613 (magit--with-temp-process-buffer 614 ;; Unset global arguments for ancient Git versions. 615 (let* ((magit-git-global-arguments nil) 616 (status (magit-process-git t "version")) 617 (output (buffer-string))) 618 (cond 619 ((not (zerop status)) 620 (display-warning 621 'magit 622 (format "%S\n\nRunning \"%s --version\" failed with output:\n\n%s" 623 (if host 624 (format "Magit cannot find Git on host %S.\n 625 Check the value of `magit-remote-git-executable' using 626 `magit-debug-git-executable' and consult the info node 627 `(tramp)Remote programs'." host) 628 "Magit cannot find Git.\n 629 Check the values of `magit-git-executable' and `exec-path' 630 using `magit-debug-git-executable'.") 631 (magit-git-executable) 632 output))) 633 ((save-match-data 634 (and (string-match magit--git-version-regexp output) 635 (let ((version (match-string 1 output))) 636 (push (cons host version) 637 magit--host-git-version-cache) 638 version)))) 639 ((error "Unexpected \"%s --version\" output: %S" 640 (magit-git-executable) 641 output))))))))) 642 643 (defun magit-git-version-assert (&optional minimal who) 644 "Assert that the used Git version is greater than or equal to MINIMAL. 645 If optional MINIMAL is nil, compare with `magit--minimal-git' 646 instead. Optional WHO if non-nil specifies what functionality 647 needs at least MINIMAL, otherwise it defaults to \"Magit\"." 648 (when (magit-git-version< (or minimal magit--minimal-git)) 649 (let* ((host (file-remote-p default-directory)) 650 (msg (format-spec 651 (cond (host "\ 652 %w requires Git %m or greater, but on %h the version is %v. 653 654 If multiple Git versions are installed on the host, then the 655 problem might be that TRAMP uses the wrong executable. 656 657 Check the value of `magit-remote-git-executable' and consult 658 the info node `(tramp)Remote programs'.\n") 659 (t "\ 660 %w requires Git %m or greater, but you are using %v. 661 662 If you have multiple Git versions installed, then check the 663 values of `magit-remote-git-executable' and `exec-path'.\n")) 664 `((?w . ,(or who "Magit")) 665 (?m . ,(or minimal magit--minimal-git)) 666 (?v . ,(magit-git-version)) 667 (?h . ,host))))) 668 (display-warning 'magit msg :error)))) 669 670 (defun magit--safe-git-version () 671 "Return the Git version used for `default-directory' or an error message." 672 (magit--with-temp-process-buffer 673 (let* ((magit-git-global-arguments nil) 674 (status (magit-process-git t "version")) 675 (output (buffer-string))) 676 (cond ((not (zerop status)) output) 677 ((save-match-data 678 (and (string-match magit--git-version-regexp output) 679 (match-string 1 output)))) 680 (t output))))) 681 682 (defun magit-debug-git-executable () 683 "Display a buffer with information about `magit-git-executable'. 684 Also include information about `magit-remote-git-executable'. 685 See info node `(magit)Debugging Tools' for more information." 686 (interactive) 687 (with-current-buffer (get-buffer-create "*magit-git-debug*") 688 (pop-to-buffer (current-buffer)) 689 (erase-buffer) 690 (insert (format "magit-remote-git-executable: %S\n" 691 magit-remote-git-executable)) 692 (insert (concat 693 (format "magit-git-executable: %S" magit-git-executable) 694 (and (not (file-name-absolute-p magit-git-executable)) 695 (format " [%S]" (executable-find magit-git-executable))) 696 (format " (%s)\n" (magit--safe-git-version)))) 697 (insert (format "exec-path: %S\n" exec-path)) 698 (when-let ((diff (cl-set-difference 699 (seq-filter #'file-exists-p (remq nil (parse-colon-path 700 (getenv "PATH")))) 701 (seq-filter #'file-exists-p (remq nil exec-path)) 702 :test #'file-equal-p))) 703 (insert (format " entries in PATH, but not in exec-path: %S\n" diff))) 704 (dolist (execdir exec-path) 705 (insert (format " %s (%s)\n" execdir (car (file-attributes execdir)))) 706 (when (file-directory-p execdir) 707 (dolist (exec (directory-files 708 execdir t (concat 709 "\\`git" (regexp-opt exec-suffixes) "\\'"))) 710 (insert (format " %s (%s)\n" exec 711 (magit--safe-git-version)))))))) 712 713 ;;; Variables 714 715 (defun magit-config-get-from-cached-list (key) 716 (gethash 717 ;; `git config --list' downcases first and last components of the key. 718 (let* ((key (replace-regexp-in-string "\\`[^.]+" #'downcase key t t)) 719 (key (replace-regexp-in-string "[^.]+\\'" #'downcase key t t))) 720 key) 721 (magit--with-refresh-cache (cons (magit-toplevel) 'config) 722 (let ((configs (make-hash-table :test #'equal))) 723 (dolist (conf (magit-git-items "config" "--list" "-z")) 724 (let* ((nl-pos (cl-position ?\n conf)) 725 (key (substring conf 0 nl-pos)) 726 (val (if nl-pos (substring conf (1+ nl-pos)) ""))) 727 (puthash key (nconc (gethash key configs) (list val)) configs))) 728 configs)))) 729 730 (defun magit-get (&rest keys) 731 "Return the value of the Git variable specified by KEYS." 732 (car (last (apply #'magit-get-all keys)))) 733 734 (defun magit-get-all (&rest keys) 735 "Return all values of the Git variable specified by KEYS." 736 (let ((magit-git-debug nil) 737 (arg (and (or (null (car keys)) 738 (string-prefix-p "--" (car keys))) 739 (pop keys))) 740 (key (string-join keys "."))) 741 (if (and magit--refresh-cache (not arg)) 742 (magit-config-get-from-cached-list key) 743 (magit-git-items "config" arg "-z" "--get-all" key)))) 744 745 (defun magit-get-boolean (&rest keys) 746 "Return the boolean value of the Git variable specified by KEYS. 747 Also see `magit-git-config-p'." 748 (let ((arg (and (or (null (car keys)) 749 (string-prefix-p "--" (car keys))) 750 (pop keys))) 751 (key (string-join keys "."))) 752 (equal (if magit--refresh-cache 753 (car (last (magit-config-get-from-cached-list key))) 754 (magit-git-str "config" arg "--bool" key)) 755 "true"))) 756 757 (defun magit-set (value &rest keys) 758 "Set the value of the Git variable specified by KEYS to VALUE." 759 (let ((arg (and (or (null (car keys)) 760 (string-prefix-p "--" (car keys))) 761 (pop keys))) 762 (key (string-join keys "."))) 763 (if value 764 (magit-git-success "config" arg key value) 765 (magit-git-success "config" arg "--unset" key)) 766 value)) 767 768 (gv-define-setter magit-get (val &rest keys) 769 `(magit-set ,val ,@keys)) 770 771 (defun magit-set-all (values &rest keys) 772 "Set all values of the Git variable specified by KEYS to VALUES." 773 (let ((arg (and (or (null (car keys)) 774 (string-prefix-p "--" (car keys))) 775 (pop keys))) 776 (var (string-join keys "."))) 777 (when (magit-get var) 778 (magit-call-git "config" arg "--unset-all" var)) 779 (dolist (v values) 780 (magit-call-git "config" arg "--add" var v)))) 781 782 ;;; Files 783 784 (defun magit--safe-default-directory (&optional file) 785 (catch 'unsafe-default-dir 786 (let ((dir (file-name-as-directory 787 (expand-file-name (or file default-directory)))) 788 (previous nil)) 789 (while (not (file-accessible-directory-p dir)) 790 (setq dir (file-name-directory (directory-file-name dir))) 791 (when (equal dir previous) 792 (throw 'unsafe-default-dir nil)) 793 (setq previous dir)) 794 dir))) 795 796 (defmacro magit--with-safe-default-directory (file &rest body) 797 (declare (indent 1) (debug (form body))) 798 `(when-let ((default-directory (magit--safe-default-directory ,file))) 799 ,@body)) 800 801 (defun magit-git-dir (&optional path) 802 "Like (expand-file-name PATH (magit-gitdir)) or just (magit-gitdir)." 803 (declare (obsolete 'magit-gitdir "Magit 4.0.0")) 804 (and-let* ((dir (magit-gitdir))) 805 (if path 806 (expand-file-name (convert-standard-filename path) dir) 807 dir))) 808 809 (defun magit-gitdir (&optional directory) 810 "Return the absolute and resolved path of the .git directory. 811 812 If the `GIT_DIR' environment variable is defined, return that. 813 Otherwise return the .git directory for DIRECTORY, or if that is 814 nil, then for `default-directory' instead. If the directory is 815 not located inside a Git repository, then return nil." 816 (let ((default-directory (or directory default-directory))) 817 (magit--with-refresh-cache (list default-directory 'magit-gitdir) 818 (magit--with-safe-default-directory nil 819 (and-let* 820 ((dir (magit-rev-parse-safe "--git-dir")) 821 (dir (file-name-as-directory (magit-expand-git-file-name dir)))) 822 (if (file-remote-p dir) 823 dir 824 (concat (file-remote-p default-directory) dir))))))) 825 826 (defvar magit--separated-gitdirs nil) 827 828 (defun magit--record-separated-gitdir () 829 (let ((topdir (magit-toplevel)) 830 (gitdir (magit-gitdir))) 831 ;; Kludge: git-annex converts submodule gitdirs to symlinks. See #3599. 832 (when (file-symlink-p (directory-file-name gitdir)) 833 (setq gitdir (file-truename gitdir))) 834 ;; We want to delete the entry for `topdir' here, rather than within 835 ;; (unless ...), in case a `--separate-git-dir' repository was switched to 836 ;; the standard structure (i.e., "topdir/.git/"). 837 (setq magit--separated-gitdirs (cl-delete topdir 838 magit--separated-gitdirs 839 :key #'car :test #'equal)) 840 (unless (equal (file-name-as-directory (expand-file-name ".git" topdir)) 841 gitdir) 842 (push (cons topdir gitdir) magit--separated-gitdirs)))) 843 844 (defun magit-toplevel (&optional directory) 845 "Return the absolute path to the toplevel of the current repository. 846 847 From within the working tree or control directory of a repository 848 return the absolute path to the toplevel directory of the working 849 tree. As a special case, from within a bare repository return 850 the control directory instead. When called outside a repository 851 then return nil. 852 853 When optional DIRECTORY is non-nil then return the toplevel for 854 that directory instead of the one for `default-directory'. 855 856 Try to respect the option `find-file-visit-truename', i.e., when 857 the value of that option is nil, then avoid needlessly returning 858 the truename. When a symlink to a sub-directory of the working 859 tree is involved, or when called from within a sub-directory of 860 the gitdir or from the toplevel of a gitdir, which itself is not 861 located within the working tree, then it is not possible to avoid 862 returning the truename." 863 (or 864 (magit--with-refresh-cache 865 (cons (or directory default-directory) 'magit-toplevel) 866 (magit--with-safe-default-directory directory 867 (if-let ((topdir (magit-rev-parse-safe "--show-toplevel"))) 868 (let (updir) 869 (setq topdir (magit-expand-git-file-name topdir)) 870 (cond 871 ((and 872 ;; Always honor these settings. 873 (not find-file-visit-truename) 874 (not (getenv "GIT_WORK_TREE")) 875 ;; `--show-cdup' is the relative path to the toplevel 876 ;; from `(file-truename default-directory)'. Here we 877 ;; pretend it is relative to `default-directory', and 878 ;; go to that directory. Then we check whether 879 ;; `--show-toplevel' still returns the same value and 880 ;; whether `--show-cdup' now is the empty string. If 881 ;; both is the case, then we are at the toplevel of 882 ;; the same working tree, but also avoided needlessly 883 ;; following any symlinks. 884 (progn 885 (setq updir (file-name-as-directory 886 (magit-rev-parse-safe "--show-cdup"))) 887 (setq updir (if (file-name-absolute-p updir) 888 (concat (file-remote-p default-directory) 889 updir) 890 (expand-file-name updir))) 891 (and-let* 892 ((default-directory updir) 893 (top (and (string-equal 894 (magit-rev-parse-safe "--show-cdup") "") 895 (magit-rev-parse-safe "--show-toplevel")))) 896 (string-equal (magit-expand-git-file-name top) topdir)))) 897 updir) 898 ((concat (file-remote-p default-directory) 899 (file-name-as-directory topdir))))) 900 (and-let* ((gitdir (magit-rev-parse-safe "--git-dir")) 901 (gitdir (file-name-as-directory 902 (if (file-name-absolute-p gitdir) 903 ;; We might have followed a symlink. 904 (concat (file-remote-p default-directory) 905 (magit-expand-git-file-name gitdir)) 906 (expand-file-name gitdir))))) 907 (if (magit-bare-repo-p) 908 gitdir 909 (let* ((link (expand-file-name "gitdir" gitdir)) 910 (wtree (and (file-exists-p link) 911 (magit-file-line link)))) 912 (cond 913 ((and wtree 914 ;; Ignore .git/gitdir files that result from a 915 ;; Git bug. See #2364. 916 (not (equal wtree ".git"))) 917 ;; Return the linked working tree. 918 (concat (file-remote-p default-directory) 919 (file-name-directory wtree))) 920 ;; The working directory may not be the parent 921 ;; directory of .git if it was set up with 922 ;; "git init --separate-git-dir". See #2955. 923 ((car (rassoc gitdir magit--separated-gitdirs))) 924 (;; Step outside the control directory to enter the 925 ;; working tree. 926 (file-name-directory (directory-file-name gitdir)))))))))))) 927 928 (defun magit--toplevel-safe () 929 (or (magit-toplevel) 930 (magit--not-inside-repository-error))) 931 932 (defmacro magit-with-toplevel (&rest body) 933 (declare (indent defun) (debug (body))) 934 `(let ((default-directory (magit--toplevel-safe))) 935 ,@body)) 936 937 (define-error 'magit-outside-git-repo "Not inside Git repository") 938 (define-error 'magit-corrupt-git-config "Corrupt Git configuration") 939 (define-error 'magit-git-executable-not-found 940 (concat "Git executable cannot be found " 941 "(see https://magit.vc/goto/e6a78ed2)")) 942 943 (defun magit--assert-usable-git () 944 (if (not (compat-call executable-find (magit-git-executable) t)) 945 (signal 'magit-git-executable-not-found (magit-git-executable)) 946 (let ((magit-git-debug 947 (lambda (err) 948 (signal 'magit-corrupt-git-config 949 (format "%s: %s" default-directory err))))) 950 ;; This should always succeed unless there's a corrupt config 951 ;; (or at least a similarly severe failing state). Note that 952 ;; git-config's --default is avoided because it's not available 953 ;; until Git 2.18. 954 (magit-git-string "config" "--get-color" "" "reset")) 955 nil)) 956 957 (defun magit--not-inside-repository-error () 958 (magit--assert-usable-git) 959 (signal 'magit-outside-git-repo default-directory)) 960 961 (defun magit-inside-gitdir-p (&optional noerror) 962 "Return t if `default-directory' is below the repository directory. 963 If it is below the working directory, then return nil. 964 If it isn't below either, then signal an error unless NOERROR 965 is non-nil, in which case return nil." 966 (and (magit--assert-default-directory noerror) 967 ;; Below a repository directory that is not located below the 968 ;; working directory "git rev-parse --is-inside-git-dir" prints 969 ;; "false", which is wrong. 970 (let ((gitdir (magit-gitdir))) 971 (cond (gitdir (file-in-directory-p default-directory gitdir)) 972 (noerror nil) 973 ((signal 'magit-outside-git-repo default-directory)))))) 974 975 (defun magit-inside-worktree-p (&optional noerror) 976 "Return t if `default-directory' is below the working directory. 977 If it is below the repository directory, then return nil. 978 If it isn't below either, then signal an error unless NOERROR 979 is non-nil, in which case return nil." 980 (and (magit--assert-default-directory noerror) 981 (condition-case nil 982 (magit-rev-parse-true "--is-inside-work-tree") 983 (magit-invalid-git-boolean 984 (and (not noerror) 985 (signal 'magit-outside-git-repo default-directory)))))) 986 987 (cl-defgeneric magit-bare-repo-p (&optional noerror) 988 "Return t if the current repository is bare. 989 If it is non-bare, then return nil. If `default-directory' 990 isn't below a Git repository, then signal an error unless 991 NOERROR is non-nil, in which case return nil." 992 (and (magit--assert-default-directory noerror) 993 (condition-case nil 994 (magit-rev-parse-true "--is-bare-repository") 995 (magit-invalid-git-boolean 996 (and (not noerror) 997 (signal 'magit-outside-git-repo default-directory)))))) 998 999 (defun magit--assert-default-directory (&optional noerror) 1000 (or (file-directory-p default-directory) 1001 (and (not noerror) 1002 (let ((exists (file-exists-p default-directory))) 1003 (signal (if exists 'file-error 'file-missing) 1004 (list "Running git in directory" 1005 (if exists 1006 "Not a directory" 1007 "No such file or directory") 1008 default-directory)))))) 1009 1010 (defun magit-git-repo-p (directory &optional non-bare) 1011 "Return t if DIRECTORY is a Git repository. 1012 When optional NON-BARE is non-nil also return nil if DIRECTORY is 1013 a bare repository." 1014 (and (file-directory-p directory) ; Avoid archives, see #3397. 1015 (or (file-regular-p (expand-file-name ".git" directory)) 1016 (file-directory-p (expand-file-name ".git" directory)) 1017 (and (not non-bare) 1018 (file-regular-p (expand-file-name "HEAD" directory)) 1019 (file-directory-p (expand-file-name "refs" directory)) 1020 (file-directory-p (expand-file-name "objects" directory)))))) 1021 1022 (defun magit-file-relative-name (&optional file tracked) 1023 "Return the path of FILE relative to the repository root. 1024 1025 If optional FILE is nil or omitted, return the relative path of 1026 the file being visited in the current buffer, if any, else nil. 1027 If the file is not inside a Git repository, then return nil. 1028 1029 If TRACKED is non-nil, return the path only if it matches a 1030 tracked file." 1031 (unless file 1032 (with-current-buffer (or (buffer-base-buffer) 1033 (current-buffer)) 1034 (setq file (or magit-buffer-file-name buffer-file-name 1035 (and (derived-mode-p 'dired-mode) default-directory))))) 1036 (when (and file (or (not tracked) 1037 (magit-file-tracked-p (file-relative-name file)))) 1038 (and-let* ((dir (magit-toplevel 1039 (magit--safe-default-directory 1040 (directory-file-name (file-name-directory file)))))) 1041 (file-relative-name file dir)))) 1042 1043 (defun magit-file-ignored-p (file) 1044 (magit-git-string-p "ls-files" "--others" "--ignored" "--exclude-standard" 1045 "--" (magit-convert-filename-for-git file))) 1046 1047 (defun magit-file-tracked-p (file) 1048 (magit-git-success "ls-files" "--error-unmatch" 1049 "--" (magit-convert-filename-for-git file))) 1050 1051 (defun magit-list-files (&rest args) 1052 (apply #'magit-git-items "ls-files" "-z" "--full-name" args)) 1053 1054 (defun magit-tracked-files () 1055 (magit-list-files "--cached")) 1056 1057 (defun magit-untracked-files (&optional all files compact) 1058 (if compact 1059 (--mapcat (and (eq (aref it 0) ??) 1060 (list (substring it 3))) 1061 (magit-git-items "status" "-z" "--porcelain" 1062 (magit-ignore-submodules-p t) 1063 "--" files)) 1064 (magit-list-files "--other" 1065 (and (not all) "--exclude-standard") 1066 "--" files))) 1067 1068 (defun magit-modified-files (&optional nomodules files) 1069 (magit-git-items "diff-index" "-z" "--name-only" 1070 (and nomodules "--ignore-submodules") 1071 (magit-headish) "--" files)) 1072 1073 (defun magit-unstaged-files (&optional nomodules files) 1074 (magit-git-items "diff-files" "-z" "--name-only" "--diff-filter=u" 1075 (and nomodules "--ignore-submodules") 1076 "--" files)) 1077 1078 (defun magit-staged-files (&optional nomodules files) 1079 (magit-git-items "diff-index" "-z" "--name-only" "--cached" 1080 (and nomodules "--ignore-submodules") 1081 (magit-headish) "--" files)) 1082 1083 (defun magit-binary-files (&rest args) 1084 (--mapcat (and (string-match "^-\t-\t\\(.+\\)" it) 1085 (list (match-string 1 it))) 1086 (apply #'magit-git-items 1087 "diff" "-z" "--numstat" "--ignore-submodules" 1088 args))) 1089 1090 (defun magit-unmerged-files () 1091 (magit-git-items "diff-files" "-z" "--name-only" "--diff-filter=U")) 1092 1093 (defun magit-ignored-files () 1094 (magit-git-items "ls-files" "-z" "--others" "--ignored" 1095 "--exclude-standard" "--directory")) 1096 1097 (defun magit-stashed-files (stash) 1098 (magit-git-items "stash" "show" "-z" "--name-only" stash)) 1099 1100 (defun magit-skip-worktree-files () 1101 (--keep (and (= (aref it 0) ?S) 1102 (substring it 2)) 1103 (magit-list-files "-t"))) 1104 1105 (defun magit-assume-unchanged-files () 1106 (--keep (and (memq (aref it 0) '(?h ?s ?m ?r ?c ?k)) 1107 (substring it 2)) 1108 (magit-list-files "-v"))) 1109 1110 (defun magit-revision-files (rev) 1111 (magit-with-toplevel 1112 (magit-git-items "ls-tree" "-z" "-r" "--name-only" rev))) 1113 1114 (defun magit-revision-directories (rev) 1115 "List directories that contain a tracked file in revision REV." 1116 (magit-with-toplevel 1117 (mapcar #'file-name-as-directory 1118 (magit-git-items "ls-tree" "-z" "-r" "-d" "--name-only" rev)))) 1119 1120 (defun magit-changed-files (rev-or-range &optional other-rev) 1121 "Return list of files the have changed between two revisions. 1122 If OTHER-REV is non-nil, REV-OR-RANGE should be a revision, not a 1123 range. Otherwise, it can be any revision or range accepted by 1124 \"git diff\" (i.e., <rev>, <revA>..<revB>, or <revA>...<revB>)." 1125 (magit-with-toplevel 1126 (magit-git-items "diff" "-z" "--name-only" rev-or-range other-rev))) 1127 1128 (defun magit-renamed-files (revA revB) 1129 (mapcar (pcase-lambda (`(,_status ,fileA ,fileB)) 1130 (cons fileA fileB)) 1131 (seq-partition (magit-git-items "diff" "-z" "--name-status" 1132 "--find-renames" 1133 "--diff-filter=R" revA revB) 1134 3))) 1135 1136 (defun magit--rev-file-name (file rev other-rev) 1137 "For FILE, potentially renamed between REV and OTHER-REV, return name in REV. 1138 Return nil, if FILE appears neither in REV nor OTHER-REV, 1139 or if no rename is detected." 1140 (or (car (member file (magit-revision-files rev))) 1141 (and-let* ((renamed (magit-renamed-files rev other-rev))) 1142 (car (rassoc file renamed))))) 1143 1144 (defun magit-file-status (&rest args) 1145 (magit--with-temp-process-buffer 1146 (save-excursion (magit-git-insert "status" "-z" args)) 1147 (let ((pos (point)) status) 1148 (while (> (skip-chars-forward "[:print:]") 0) 1149 (let ((x (char-after pos)) 1150 (y (char-after (1+ pos))) 1151 (file (buffer-substring (+ pos 3) (point)))) 1152 (forward-char) 1153 (if (memq x '(?R ?C)) 1154 (progn 1155 (setq pos (point)) 1156 (skip-chars-forward "[:print:]") 1157 (push (list file (buffer-substring pos (point)) x y) status) 1158 (forward-char)) 1159 (push (list file nil x y) status))) 1160 (setq pos (point))) 1161 status))) 1162 1163 (defcustom magit-cygwin-mount-points 1164 (and (eq system-type 'windows-nt) 1165 (cl-sort (--map (if (string-match "^\\(.*\\) on \\(.*\\) type" it) 1166 (cons (file-name-as-directory (match-string 2 it)) 1167 (file-name-as-directory (match-string 1 it))) 1168 (lwarn '(magit) :error 1169 "Failed to parse Cygwin mount: %S" it)) 1170 ;; If --exec-path is not a native Windows path, 1171 ;; then we probably have a cygwin git. 1172 (let ((process-environment 1173 (append magit-git-environment 1174 process-environment))) 1175 (and (not (string-match-p 1176 "\\`[a-zA-Z]:" 1177 (car (process-lines 1178 magit-git-executable "--exec-path")))) 1179 (ignore-errors (process-lines "mount"))))) 1180 #'> :key (pcase-lambda (`(,cyg . ,_win)) (length cyg)))) 1181 "Alist of (CYGWIN . WIN32) directory names. 1182 Sorted from longest to shortest CYGWIN name." 1183 :package-version '(magit . "2.3.0") 1184 :group 'magit-process 1185 :type '(alist :key-type string :value-type directory)) 1186 1187 (defun magit-expand-git-file-name (filename) 1188 (unless (file-name-absolute-p filename) 1189 (setq filename (expand-file-name filename))) 1190 (if-let ((cyg:win (and (not (file-remote-p default-directory)) ; see #4976 1191 (cl-assoc filename magit-cygwin-mount-points 1192 :test (lambda (f cyg) (string-prefix-p cyg f)))))) 1193 (concat (cdr cyg:win) 1194 (substring filename (length (car cyg:win)))) 1195 filename)) 1196 1197 (defun magit-convert-filename-for-git (filename) 1198 "Convert FILENAME so that it can be passed to git. 1199 1. If it is a absolute filename, then pass it through 1200 `expand-file-name' to replace things such as \"~/\" that 1201 Git does not understand. 1202 2. If it is a remote filename, then remove the remote part. 1203 3. Deal with an `windows-nt' Emacs vs. Cygwin Git incompatibility." 1204 (if (file-name-absolute-p filename) 1205 (if-let ((cyg:win (cl-rassoc filename magit-cygwin-mount-points 1206 :test (lambda (f win) (string-prefix-p win f))))) 1207 (concat (car cyg:win) 1208 (substring filename (length (cdr cyg:win)))) 1209 (let ((expanded (expand-file-name filename))) 1210 (or (file-remote-p expanded 'localname) 1211 expanded))) 1212 filename)) 1213 1214 (defun magit-decode-git-path (path) 1215 (if (eq (aref path 0) ?\") 1216 (decode-coding-string (read path) 1217 (or magit-git-output-coding-system 1218 (car default-process-coding-system)) 1219 t) 1220 path)) 1221 1222 (defun magit-file-at-point (&optional expand assert) 1223 (if-let ((file (magit-section-case 1224 (file (oref it value)) 1225 (hunk (magit-section-parent-value it))))) 1226 (if expand 1227 (expand-file-name file (magit-toplevel)) 1228 file) 1229 (when assert 1230 (user-error "No file at point")))) 1231 1232 (defun magit-current-file () 1233 (or (magit-file-relative-name) 1234 (magit-file-at-point) 1235 (and (derived-mode-p 'magit-log-mode) 1236 (car magit-buffer-log-files)))) 1237 1238 ;;; Predicates 1239 1240 (defun magit-no-commit-p () 1241 "Return t if there is no commit in the current Git repository." 1242 (not (magit-rev-verify "HEAD"))) 1243 1244 (defun magit-merge-commit-p (commit) 1245 "Return t if COMMIT is a merge commit." 1246 (length> (magit-commit-parents commit) 1)) 1247 1248 (defun magit-anything-staged-p (&optional ignore-submodules &rest files) 1249 "Return t if there are any staged changes. 1250 If optional FILES is non-nil, then only changes to those files 1251 are considered." 1252 (magit-git-failure "diff" "--quiet" "--cached" 1253 (and ignore-submodules "--ignore-submodules") 1254 "--" files)) 1255 1256 (defun magit-anything-unstaged-p (&optional ignore-submodules &rest files) 1257 "Return t if there are any unstaged changes. 1258 If optional FILES is non-nil, then only changes to those files 1259 are considered." 1260 (magit-git-failure "diff" "--quiet" 1261 (and ignore-submodules "--ignore-submodules") 1262 "--" files)) 1263 1264 (defun magit-anything-modified-p (&optional ignore-submodules &rest files) 1265 "Return t if there are any staged or unstaged changes. 1266 If optional FILES is non-nil, then only changes to those files 1267 are considered." 1268 (or (apply #'magit-anything-staged-p ignore-submodules files) 1269 (apply #'magit-anything-unstaged-p ignore-submodules files))) 1270 1271 (defun magit-anything-unmerged-p (&rest files) 1272 "Return t if there are any merge conflicts. 1273 If optional FILES is non-nil, then only conflicts in those files 1274 are considered." 1275 (and (magit-git-string "ls-files" "--unmerged" files) t)) 1276 1277 (defun magit-module-worktree-p (module) 1278 (magit-with-toplevel 1279 (file-exists-p (expand-file-name ".git" module)))) 1280 1281 (defun magit-module-no-worktree-p (module) 1282 (not (magit-module-worktree-p module))) 1283 1284 (defun magit-ignore-submodules-p (&optional return-argument) 1285 (or (cl-find-if (lambda (arg) 1286 (string-prefix-p "--ignore-submodules" arg)) 1287 magit-buffer-diff-args) 1288 (and-let* ((value (magit-get "diff.ignoreSubmodules"))) 1289 (if return-argument 1290 (concat "--ignore-submodules=" value) 1291 (concat "diff.ignoreSubmodules=" value))))) 1292 1293 ;;; Revisions and References 1294 1295 (defun magit-rev-parse (&rest args) 1296 "Execute `git rev-parse ARGS', returning first line of output. 1297 If there is no output, return nil." 1298 (apply #'magit-git-string "rev-parse" args)) 1299 1300 (defun magit-rev-parse-safe (&rest args) 1301 "Execute `git rev-parse ARGS', returning first line of output. 1302 If there is no output, return nil. Like `magit-rev-parse' but 1303 ignore `magit-git-debug'." 1304 (apply #'magit-git-str "rev-parse" args)) 1305 1306 (defun magit-rev-parse-true (&rest args) 1307 "Execute `git rev-parse ARGS', returning t if it prints \"true\". 1308 If it prints \"false\", then return nil. For any other output 1309 signal an error." 1310 (magit-git-true "rev-parse" args)) 1311 1312 (defun magit-rev-parse-false (&rest args) 1313 "Execute `git rev-parse ARGS', returning t if it prints \"false\". 1314 If it prints \"true\", then return nil. For any other output 1315 signal an error." 1316 (magit-git-false "rev-parse" args)) 1317 1318 (defun magit-rev-parse-p (&rest args) 1319 "Execute `git rev-parse ARGS', returning t if it prints \"true\". 1320 Return t if the first (and usually only) output line is the 1321 string \"true\", otherwise return nil." 1322 (equal (magit-git-str "rev-parse" args) "true")) 1323 1324 (defun magit-rev-verify (rev) 1325 (magit-git-string-p "rev-parse" "--verify" rev)) 1326 1327 (defun magit-commit-p (rev) 1328 "Return full hash for REV if it names an existing commit." 1329 (magit-rev-verify (magit--rev-dereference rev))) 1330 1331 (defalias 'magit-rev-verify-commit #'magit-commit-p) 1332 1333 (defalias 'magit-rev-hash #'magit-commit-p) 1334 1335 (defun magit--rev-dereference (rev) 1336 "Return a rev that forces Git to interpret REV as a commit. 1337 If REV is nil or has the form \":/TEXT\", return REV itself." 1338 (cond ((not rev) nil) 1339 ((string-match-p "^:/" rev) rev) 1340 ((concat rev "^{commit}")))) 1341 1342 (defun magit-rev-equal (a b) 1343 "Return t if there are no differences between the commits A and B." 1344 (magit-git-success "diff" "--quiet" a b)) 1345 1346 (defun magit-rev-eq (a b) 1347 "Return t if A and B refer to the same commit." 1348 (let ((a (magit-commit-p a)) 1349 (b (magit-commit-p b))) 1350 (and a b (equal a b)))) 1351 1352 (defun magit-rev-ancestor-p (a b) 1353 "Return non-nil if commit A is an ancestor of commit B." 1354 (magit-git-success "merge-base" "--is-ancestor" a b)) 1355 1356 (defun magit-rev-head-p (rev) 1357 (or (equal rev "HEAD") 1358 (and rev 1359 (not (string-search ".." rev)) 1360 (equal (magit-rev-parse rev) 1361 (magit-rev-parse "HEAD"))))) 1362 1363 (defun magit-rev-author-p (rev) 1364 "Return t if the user is the author of REV. 1365 More precisely return t if `user.name' is equal to the author 1366 name of REV and/or `user.email' is equal to the author email 1367 of REV." 1368 (or (equal (magit-get "user.name") (magit-rev-format "%an" rev)) 1369 (equal (magit-get "user.email") (magit-rev-format "%ae" rev)))) 1370 1371 (defun magit-rev-name (rev &optional pattern not-anchored) 1372 "Return a symbolic name for REV using `git-name-rev'. 1373 1374 PATTERN can be used to limit the result to a matching ref. 1375 Unless NOT-ANCHORED is non-nil, the beginning of the ref must 1376 match PATTERN. 1377 1378 An anchored lookup is done using the arguments 1379 \"--exclude=*/<PATTERN> --exclude=*/HEAD\" in addition to 1380 \"--refs=<PATTERN>\", provided at least version v2.13 of Git is 1381 used. Older versions did not support the \"--exclude\" argument. 1382 When \"--exclude\" cannot be used and `git-name-rev' returns a 1383 ref that should have been excluded, then that is discarded and 1384 this function returns nil instead. This is unfortunate because 1385 there might be other refs that do match. To fix that, update 1386 Git." 1387 (if (magit-git-version< "2.13") 1388 (and-let* 1389 ((ref (magit-git-string "name-rev" "--name-only" "--no-undefined" 1390 (and pattern (concat "--refs=" pattern)) 1391 rev))) 1392 (if (and pattern 1393 (string-match-p "\\`refs/[^/]+/\\*\\'" pattern)) 1394 (let ((namespace (substring pattern 0 -1))) 1395 (and (not (or (string-suffix-p "HEAD" ref) 1396 (and (string-match-p namespace ref) 1397 (not (magit-rev-verify 1398 (concat namespace ref)))))) 1399 ref)) 1400 ref)) 1401 (magit-git-string "name-rev" "--name-only" "--no-undefined" 1402 (and pattern (concat "--refs=" pattern)) 1403 (and pattern 1404 (not not-anchored) 1405 (list "--exclude=*/HEAD" 1406 (concat "--exclude=*/" pattern))) 1407 rev))) 1408 1409 (defun magit-rev-branch (rev) 1410 (and-let* ((name (magit-rev-name rev "refs/heads/*"))) 1411 (and (not (string-match-p "[~^]" name)) name))) 1412 1413 (defun magit-rev-fixup-target (rev) 1414 (let ((msg (magit-rev-format "%s" rev))) 1415 (save-match-data 1416 (and (string-match "\\`\\(fixup\\|squash\\)! \\(.+\\)" msg) 1417 (magit-rev-format 1418 "%h" (format "%s^{/^%s}" rev 1419 (magit--ext-regexp-quote (match-string 2 msg)))))))) 1420 1421 (defun magit-get-shortname (rev) 1422 (let* ((fn (apply-partially #'magit-rev-name rev)) 1423 (name (or (funcall fn "refs/tags/*") 1424 (funcall fn "refs/heads/*") 1425 (funcall fn "refs/remotes/*")))) 1426 (cond ((not name) 1427 (magit-rev-parse "--short" rev)) 1428 ((string-match "^\\(?:tags\\|remotes\\)/\\(.+\\)" name) 1429 (if (magit-ref-ambiguous-p (match-string 1 name)) 1430 name 1431 (match-string 1 name))) 1432 ((magit-ref-maybe-qualify name))))) 1433 1434 (defun magit-name-branch (rev &optional lax) 1435 (or (magit-name-local-branch rev) 1436 (magit-name-remote-branch rev) 1437 (and lax (or (magit-name-local-branch rev t) 1438 (magit-name-remote-branch rev t))))) 1439 1440 (defun magit-name-local-branch (rev &optional lax) 1441 (and-let* ((name (magit-rev-name rev "refs/heads/*"))) 1442 (and (or lax (not (string-match-p "[~^]" name))) name))) 1443 1444 (defun magit-name-remote-branch (rev &optional lax) 1445 (and-let* ((name (magit-rev-name rev "refs/remotes/*"))) 1446 (and (or lax (not (string-match-p "[~^]" name))) 1447 (substring name 8)))) 1448 1449 (defun magit-name-tag (rev &optional lax) 1450 (and-let* ((name (magit-rev-name rev "refs/tags/*"))) 1451 ;; The progn is necessary to work around debbugs#31840. This, and all 1452 ;; the other instances, can be removed once we require at least Emacs 27. 1453 (progn 1454 (when (string-suffix-p "^0" name) 1455 (setq name (substring name 0 -2))) 1456 (and (or lax (not (string-match-p "[~^]" name))) 1457 (substring name 5))))) 1458 1459 (defun magit-ref-abbrev (refname) 1460 "Return an unambiguous abbreviation of REFNAME." 1461 (magit-rev-parse "--verify" "--abbrev-ref" refname)) 1462 1463 (defun magit-ref-fullname (refname) 1464 "Return fully qualified refname for REFNAME. 1465 If REFNAME is ambiguous, return nil." 1466 (magit-rev-parse "--verify" "--symbolic-full-name" refname)) 1467 1468 (defun magit-ref-ambiguous-p (refname) 1469 (save-match-data 1470 (if (string-match "\\`\\([^^~]+\\)\\(.*\\)" refname) 1471 (not (magit-ref-fullname (match-string 1 refname))) 1472 (error "%S has an unrecognized format" refname)))) 1473 1474 (defun magit-ref-maybe-qualify (refname &optional prefix) 1475 "If REFNAME is ambiguous, try to disambiguate it by prepend PREFIX to it. 1476 Return an unambiguous refname, either REFNAME or that prefixed 1477 with PREFIX, nil otherwise. If REFNAME has an offset suffix 1478 such as \"~1\", then that is preserved. If optional PREFIX is 1479 nil, then use \"heads/\". " 1480 (if (magit-ref-ambiguous-p refname) 1481 (let ((refname (concat (or prefix "heads/") refname))) 1482 (and (not (magit-ref-ambiguous-p refname)) refname)) 1483 refname)) 1484 1485 (defun magit-ref-exists-p (ref) 1486 (magit-git-success "show-ref" "--verify" ref)) 1487 1488 (defun magit-ref-equal (a b) 1489 "Return t if the refnames A and B are `equal'. 1490 A symbolic-ref pointing to some ref, is `equal' to that ref, 1491 as are two symbolic-refs pointing to the same ref. Refnames 1492 may be abbreviated." 1493 (let ((a (magit-ref-fullname a)) 1494 (b (magit-ref-fullname b))) 1495 (and a b (equal a b)))) 1496 1497 (defun magit-ref-eq (a b) 1498 "Return t if the refnames A and B are `eq'. 1499 A symbolic-ref is `eq' to itself, but not to the ref it points 1500 to, or to some other symbolic-ref that points to the same ref." 1501 (let ((symbolic-a (magit-symbolic-ref-p a)) 1502 (symbolic-b (magit-symbolic-ref-p b))) 1503 (or (and symbolic-a 1504 symbolic-b 1505 (equal a b)) 1506 (and (not symbolic-a) 1507 (not symbolic-b) 1508 (magit-ref-equal a b))))) 1509 1510 (defun magit-headish () 1511 "Return the `HEAD' or if that doesn't exist the hash of the empty tree." 1512 (if (magit-no-commit-p) 1513 (magit-git-string "mktree") 1514 "HEAD")) 1515 1516 (defun magit-branch-at-point () 1517 (magit-section-case 1518 (branch (oref it value)) 1519 (commit (or (magit--painted-branch-at-point) 1520 (magit-name-branch (oref it value)))))) 1521 1522 (defun magit--painted-branch-at-point (&optional type) 1523 (or (and (not (eq type 'remote)) 1524 (memq (get-text-property (magit-point) 'font-lock-face) 1525 (list 'magit-branch-local 1526 'magit-branch-current)) 1527 (and-let* ((branch (magit-thing-at-point 'git-revision t))) 1528 (cdr (magit-split-branch-name branch)))) 1529 (and (not (eq type 'local)) 1530 (memq (get-text-property (magit-point) 'font-lock-face) 1531 (list 'magit-branch-remote 1532 'magit-branch-remote-head)) 1533 (thing-at-point 'git-revision t)))) 1534 1535 (defun magit-local-branch-at-point () 1536 (magit-section-case 1537 (branch (let ((branch (magit-ref-maybe-qualify (oref it value)))) 1538 (when (member branch (magit-list-local-branch-names)) 1539 branch))) 1540 (commit (or (magit--painted-branch-at-point 'local) 1541 (magit-name-local-branch (oref it value)))))) 1542 1543 (defun magit-remote-branch-at-point () 1544 (magit-section-case 1545 (branch (let ((branch (oref it value))) 1546 (when (member branch (magit-list-remote-branch-names)) 1547 branch))) 1548 (commit (or (magit--painted-branch-at-point 'remote) 1549 (magit-name-remote-branch (oref it value)))))) 1550 1551 (defun magit-commit-at-point () 1552 (or (magit-section-value-if 'commit) 1553 (magit-thing-at-point 'git-revision t) 1554 (and-let* ((chunk (and (bound-and-true-p magit-blame-mode) 1555 (fboundp 'magit-current-blame-chunk) 1556 (magit-current-blame-chunk)))) 1557 (oref chunk orig-rev)) 1558 (and (derived-mode-p 'magit-stash-mode 1559 'magit-merge-preview-mode 1560 'magit-revision-mode) 1561 magit-buffer-revision))) 1562 1563 (defun magit-branch-or-commit-at-point () 1564 (or (magit-section-case 1565 (branch (magit-ref-maybe-qualify (oref it value))) 1566 (commit (or (magit--painted-branch-at-point) 1567 (let ((rev (oref it value))) 1568 (or (magit-name-branch rev) rev)))) 1569 (tag (magit-ref-maybe-qualify (oref it value) "tags/")) 1570 (pullreq (or (and (fboundp 'forge--pullreq-branch) 1571 (magit-branch-p 1572 (forge--pullreq-branch (oref it value)))) 1573 (magit-ref-p (format "refs/pullreqs/%s" 1574 (oref (oref it value) number))))) 1575 ((unpulled unpushed) 1576 (magit-ref-abbrev 1577 (replace-regexp-in-string "\\.\\.\\.?" "" (oref it value))))) 1578 (magit-thing-at-point 'git-revision t) 1579 (and-let* ((chunk (and (bound-and-true-p magit-blame-mode) 1580 (fboundp 'magit-current-blame-chunk) 1581 (magit-current-blame-chunk)))) 1582 (oref chunk orig-rev)) 1583 (and magit-buffer-file-name 1584 magit-buffer-refname) 1585 (and (derived-mode-p 'magit-stash-mode 1586 'magit-merge-preview-mode 1587 'magit-revision-mode) 1588 magit-buffer-revision))) 1589 1590 (defun magit-tag-at-point () 1591 (magit-section-case 1592 (tag (oref it value)) 1593 (commit (magit-name-tag (oref it value))))) 1594 1595 (defun magit-stash-at-point () 1596 (magit-section-value-if 'stash)) 1597 1598 (defun magit-remote-at-point () 1599 (magit-section-case 1600 (remote (oref it value)) 1601 ([branch remote] (magit-section-parent-value it)))) 1602 1603 (defun magit-module-at-point (&optional predicate) 1604 (when (magit-section-match 'module) 1605 (let ((module (oref (magit-current-section) value))) 1606 (and (or (not predicate) 1607 (funcall predicate module)) 1608 module)))) 1609 1610 (defun magit-get-current-branch () 1611 "Return the refname of the currently checked out branch. 1612 Return nil if no branch is currently checked out." 1613 (magit-git-string "symbolic-ref" "--short" "HEAD")) 1614 1615 (defvar magit-get-previous-branch-timeout 0.5 1616 "Maximum time to spend in `magit-get-previous-branch'. 1617 Given as a number of seconds.") 1618 1619 (defun magit-get-previous-branch () 1620 "Return the refname of the previously checked out branch. 1621 Return nil if no branch can be found in the `HEAD' reflog 1622 which is different from the current branch and still exists. 1623 The amount of time spent searching is limited by 1624 `magit-get-previous-branch-timeout'." 1625 (let ((t0 (float-time)) 1626 (current (magit-get-current-branch)) 1627 (i 1) prev) 1628 (while (if (> (- (float-time) t0) magit-get-previous-branch-timeout) 1629 (setq prev nil) ;; Timed out. 1630 (and (setq prev (magit-rev-verify (format "@{-%d}" i))) 1631 (or (not (setq prev (magit-rev-branch prev))) 1632 (equal prev current)))) 1633 (cl-incf i)) 1634 prev)) 1635 1636 (defun magit--set-default-branch (newname oldname) 1637 (let ((remote (or (magit-primary-remote) 1638 (user-error "Cannot determine primary remote"))) 1639 (branches (mapcar (lambda (line) (split-string line "\t")) 1640 (magit-git-lines 1641 "for-each-ref" "refs/heads" 1642 "--format=%(refname:short)\t%(upstream:short)")))) 1643 (when-let ((old (assoc oldname branches))) 1644 (unless (assoc newname branches) 1645 (magit-call-git "branch" "-m" oldname newname) 1646 (setcar old newname))) 1647 (let ((new (if (magit-branch-p newname) 1648 newname 1649 (concat remote "/" newname)))) 1650 (pcase-dolist (`(,branch ,upstream) branches) 1651 (cond 1652 ((equal upstream oldname) 1653 (magit-set-upstream-branch branch new)) 1654 ((equal upstream (concat remote "/" oldname)) 1655 (magit-set-upstream-branch branch (concat remote "/" newname)))))))) 1656 1657 (defun magit--get-default-branch (&optional update) 1658 (let ((remote (magit-primary-remote))) 1659 (when update 1660 (if (not remote) 1661 (user-error "Cannot determine primary remote") 1662 (message "Determining default branch...") 1663 (magit-git "fetch" "--prune") 1664 (magit-git "remote" "set-head" "--auto" remote) 1665 (message "Determining default branch...done"))) 1666 (let ((branch (magit-git-string "symbolic-ref" "--short" 1667 (format "refs/remotes/%s/HEAD" remote)))) 1668 (when (and update (not branch)) 1669 (error "Cannot determine new default branch")) 1670 (list remote (and branch (cdr (magit-split-branch-name branch))))))) 1671 1672 (defun magit-set-upstream-branch (branch upstream) 1673 "Set UPSTREAM as the upstream of BRANCH. 1674 If UPSTREAM is nil, then unset BRANCH's upstream. 1675 Otherwise UPSTREAM has to be an existing branch." 1676 (if upstream 1677 (magit-call-git "branch" "--set-upstream-to" upstream branch) 1678 (magit-call-git "branch" "--unset-upstream" branch))) 1679 1680 (defun magit-get-upstream-ref (&optional branch) 1681 "Return the upstream branch of BRANCH as a fully qualified ref. 1682 It BRANCH is nil, then return the upstream of the current branch, 1683 if any, nil otherwise. If the upstream is not configured, the 1684 configured remote is an url, or the named branch does not exist, 1685 then return nil. I.e., return an existing local or 1686 remote-tracking branch ref." 1687 (and-let* ((branch (or branch (magit-get-current-branch)))) 1688 (magit-ref-fullname (concat branch "@{upstream}")))) 1689 1690 (defun magit-get-upstream-branch (&optional branch) 1691 "Return the name of the upstream branch of BRANCH. 1692 It BRANCH is nil, then return the upstream of the current branch 1693 if any, nil otherwise. If the upstream is not configured, the 1694 configured remote is an url, or the named branch does not exist, 1695 then return nil. I.e., return the name of an existing local or 1696 remote-tracking branch. The returned string is colorized 1697 according to the branch type." 1698 (magit--with-refresh-cache 1699 (list default-directory 'magit-get-upstream-branch branch) 1700 (and-let* ((branch (or branch (magit-get-current-branch))) 1701 (upstream (magit-ref-abbrev (concat branch "@{upstream}")))) 1702 (magit--propertize-face 1703 upstream (if (equal (magit-get "branch" branch "remote") ".") 1704 'magit-branch-local 1705 'magit-branch-remote))))) 1706 1707 (defun magit-get-indirect-upstream-branch (branch &optional force) 1708 (let ((remote (magit-get "branch" branch "remote"))) 1709 (and remote (not (equal remote ".")) 1710 ;; The user has opted in... 1711 (or force 1712 (--some (if (magit-git-success "check-ref-format" "--branch" it) 1713 (equal it branch) 1714 (string-match-p it branch)) 1715 magit-branch-prefer-remote-upstream)) 1716 ;; and local BRANCH tracks a remote branch... 1717 (let ((upstream (magit-get-upstream-branch branch))) 1718 ;; whose upstream... 1719 (and upstream 1720 ;; has the same name as BRANCH... 1721 (equal (substring upstream (1+ (length remote))) branch) 1722 ;; and can be fast-forwarded to BRANCH. 1723 (magit-rev-ancestor-p upstream branch) 1724 upstream))))) 1725 1726 (defun magit-get-upstream-remote (&optional branch allow-unnamed) 1727 (and-let* ((branch (or branch (magit-get-current-branch))) 1728 (remote (magit-get "branch" branch "remote"))) 1729 (and (not (equal remote ".")) 1730 (cond ((member remote (magit-list-remotes)) 1731 (magit--propertize-face remote 'magit-branch-remote)) 1732 ((and allow-unnamed 1733 (string-match-p "\\(\\`.\\{0,2\\}/\\|[:@]\\)" remote)) 1734 (magit--propertize-face remote 'bold)))))) 1735 1736 (defun magit-get-unnamed-upstream (&optional branch) 1737 (and-let* ((branch (or branch (magit-get-current-branch))) 1738 (remote (magit-get "branch" branch "remote")) 1739 (merge (magit-get "branch" branch "merge"))) 1740 (and (magit--unnamed-upstream-p remote merge) 1741 (list (magit--propertize-face remote 'bold) 1742 (magit--propertize-face merge 'magit-branch-remote))))) 1743 1744 (defun magit--unnamed-upstream-p (remote merge) 1745 (and remote (string-match-p "\\(\\`\\.\\{0,2\\}/\\|[:@]\\)" remote) 1746 merge (string-prefix-p "refs/" merge))) 1747 1748 (defun magit--valid-upstream-p (remote merge) 1749 (and (or (equal remote ".") 1750 (member remote (magit-list-remotes))) 1751 (string-prefix-p "refs/" merge))) 1752 1753 (defun magit-get-current-remote (&optional allow-unnamed) 1754 (or (magit-get-upstream-remote nil allow-unnamed) 1755 (and-let* ((remotes (magit-list-remotes)) 1756 (remote (if (length= remotes 1) 1757 (car remotes) 1758 (magit-primary-remote)))) 1759 (magit--propertize-face remote 'magit-branch-remote)))) 1760 1761 (defun magit-get-push-remote (&optional branch) 1762 (and-let* ((remote 1763 (or (and (or branch (setq branch (magit-get-current-branch))) 1764 (magit-get "branch" branch "pushRemote")) 1765 (magit-get "remote.pushDefault")))) 1766 (magit--propertize-face remote 'magit-branch-remote))) 1767 1768 (defun magit-get-push-branch (&optional branch verify) 1769 (magit--with-refresh-cache 1770 (list default-directory 'magit-get-push-branch branch verify) 1771 (and-let* ((branch (or branch (setq branch (magit-get-current-branch)))) 1772 (remote (magit-get-push-remote branch)) 1773 (target (concat remote "/" branch))) 1774 (and (or (not verify) 1775 (magit-rev-verify target)) 1776 (magit--propertize-face target 'magit-branch-remote))))) 1777 1778 (defun magit-get-@{push}-branch (&optional branch) 1779 (let ((ref (magit-rev-parse "--symbolic-full-name" 1780 (concat branch "@{push}")))) 1781 (and ref 1782 (string-prefix-p "refs/remotes/" ref) 1783 (substring ref 13)))) 1784 1785 (defun magit-get-remote (&optional branch) 1786 (and (or branch (setq branch (magit-get-current-branch))) 1787 (let ((remote (magit-get "branch" branch "remote"))) 1788 (and (not (equal remote ".")) 1789 remote)))) 1790 1791 (defun magit-get-some-remote (&optional branch) 1792 (or (magit-get-remote branch) 1793 (and-let* ((main (magit-main-branch))) 1794 (magit-get-remote main)) 1795 (magit-primary-remote) 1796 (car (magit-list-remotes)))) 1797 1798 (defvar magit-primary-remote-names 1799 '("upstream" "origin")) 1800 1801 (defun magit-primary-remote () 1802 "Return the primary remote. 1803 1804 The primary remote is the remote that tracks the repository that 1805 other repositories are forked from. It often is called \"origin\" 1806 but because many people name their own fork \"origin\", using that 1807 term would be ambiguous. Likewise we avoid the term \"upstream\" 1808 because a branch's @{upstream} branch may be a local branch or a 1809 branch from a remote other than the primary remote. 1810 1811 If a remote exists whose name matches `magit.primaryRemote', then 1812 that is considered the primary remote. If no remote by that name 1813 exists, then remotes in `magit-primary-remote-names' are tried in 1814 order and the first remote from that list that actually exists in 1815 the current repository is considered its primary remote." 1816 (let ((remotes (magit-list-remotes))) 1817 (seq-find (lambda (name) 1818 (member name remotes)) 1819 (delete-dups 1820 (delq nil 1821 (cons (magit-get "magit.primaryRemote") 1822 magit-primary-remote-names)))))) 1823 1824 (defun magit-branch-merged-p (branch &optional target) 1825 "Return non-nil if BRANCH is merged into its upstream and TARGET. 1826 1827 TARGET defaults to the current branch. If `HEAD' is detached and 1828 TARGET is nil, then always return nil. As a special case, if 1829 TARGET is t, then return non-nil if BRANCH is merged into any one 1830 of the other local branches. 1831 1832 If, and only if, BRANCH has an upstream, then only return non-nil 1833 if BRANCH is merged into both TARGET (as described above) as well 1834 as into its upstream." 1835 (and (if-let ((upstream (and (magit-branch-p branch) 1836 (magit-get-upstream-branch branch)))) 1837 (magit-rev-ancestor-p branch upstream) 1838 t) 1839 (if (eq target t) 1840 (delete (magit-name-local-branch branch) 1841 (magit-list-containing-branches branch)) 1842 (and-let* ((target (or target (magit-get-current-branch)))) 1843 (magit-rev-ancestor-p branch target))))) 1844 1845 (defun magit-get-tracked (refname) 1846 "Return the remote branch tracked by the remote-tracking branch REFNAME. 1847 The returned value has the form (REMOTE . REF), where REMOTE is 1848 the name of a remote and REF is the ref local to the remote." 1849 (and-let* ((ref (magit-ref-fullname refname))) 1850 (save-match-data 1851 (seq-some (lambda (line) 1852 (and (string-match "\ 1853 \\`remote\\.\\([^.]+\\)\\.fetch=\\+?\\([^:]+\\):\\(.+\\)" line) 1854 (let ((rmt (match-string 1 line)) 1855 (src (match-string 2 line)) 1856 (dst (match-string 3 line))) 1857 (and (string-match (format "\\`%s\\'" 1858 (string-replace 1859 "*" "\\(.+\\)" dst)) 1860 ref) 1861 (cons rmt (string-replace 1862 "*" (match-string 1 ref) src)))))) 1863 (magit-git-lines "config" "--local" "--list"))))) 1864 1865 (defun magit-split-branch-name (branch) 1866 (cond ((member branch (magit-list-local-branch-names)) 1867 (cons "." branch)) 1868 ((string-match "/" branch) 1869 (or (seq-some (lambda (remote) 1870 (and (string-match 1871 (format "\\`\\(%s\\)/\\(.+\\)\\'" remote) 1872 branch) 1873 (cons (match-string 1 branch) 1874 (match-string 2 branch)))) 1875 (magit-list-remotes)) 1876 (error "Invalid branch name %s" branch))))) 1877 1878 (defun magit-get-current-tag (&optional rev with-distance) 1879 "Return the closest tag reachable from REV. 1880 1881 If optional REV is nil, then default to `HEAD'. 1882 If optional WITH-DISTANCE is non-nil then return (TAG COMMITS), 1883 if it is `dirty' return (TAG COMMIT DIRTY). COMMITS is the number 1884 of commits in `HEAD' but not in TAG and DIRTY is t if there are 1885 uncommitted changes, nil otherwise." 1886 (and-let* ((str (magit-git-str "describe" "--long" "--tags" 1887 (and (eq with-distance 'dirty) "--dirty") 1888 rev))) 1889 (save-match-data 1890 (string-match 1891 "\\(.+\\)-\\(?:0[0-9]*\\|\\([0-9]+\\)\\)-g[0-9a-z]+\\(-dirty\\)?$" str) 1892 (if with-distance 1893 `(,(match-string 1 str) 1894 ,(string-to-number (or (match-string 2 str) "0")) 1895 ,@(and (match-string 3 str) (list t))) 1896 (match-string 1 str))))) 1897 1898 (defun magit-get-next-tag (&optional rev with-distance) 1899 "Return the closest tag from which REV is reachable. 1900 1901 If optional REV is nil, then default to `HEAD'. 1902 If no such tag can be found or if the distance is 0 (in which 1903 case it is the current tag, not the next), return nil instead. 1904 If optional WITH-DISTANCE is non-nil, then return (TAG COMMITS) 1905 where COMMITS is the number of commits in TAG but not in REV." 1906 (and-let* ((str (magit-git-str "describe" "--contains" (or rev "HEAD")))) 1907 (save-match-data 1908 (when (string-match "^[^^~]+" str) 1909 (setq str (match-string 0 str)) 1910 (unless (equal str (magit-get-current-tag rev)) 1911 (if with-distance 1912 (list str (car (magit-rev-diff-count str rev))) 1913 str)))))) 1914 1915 (defun magit-list-refs (&optional namespaces format sortby) 1916 "Return list of references, excluding symbolic references. 1917 1918 When NAMESPACES is non-nil, list refs from these namespaces 1919 rather than those from `magit-list-refs-namespaces'. 1920 1921 FORMAT is passed to the `--format' flag of `git for-each-ref' 1922 and defaults to \"%(refname)\". 1923 1924 SORTBY is a key or list of keys to pass to the `--sort' flag of 1925 `git for-each-ref'. When nil, use `magit-list-refs-sortby'" 1926 (unless format 1927 (setq format "%(refname)")) 1928 (seq-keep (lambda (line) 1929 (pcase-let* ((`(,symrefp ,value) 1930 (split-string line "")) 1931 (symrefp (not (equal symrefp "")))) 1932 (and (not symrefp) value))) 1933 (magit-git-lines "for-each-ref" 1934 (concat "--format=%(symref)" format) 1935 (--map (concat "--sort=" it) 1936 (pcase (or sortby magit-list-refs-sortby) 1937 ((and val (pred stringp)) (list val)) 1938 ((and val (pred listp)) val))) 1939 (or namespaces magit-list-refs-namespaces)))) 1940 1941 (defun magit-list-branches () 1942 (magit-list-refs (list "refs/heads" "refs/remotes"))) 1943 1944 (defun magit-list-local-branches () 1945 (magit-list-refs "refs/heads")) 1946 1947 (defun magit-list-remote-branches (&optional remote) 1948 (magit-list-refs (concat "refs/remotes/" remote))) 1949 1950 (defun magit-list-related-branches (relation &optional commit &rest args) 1951 (--remove (string-match-p "\\(\\`(HEAD\\|HEAD -> \\)" it) 1952 (--map (substring it 2) 1953 (magit-git-lines "branch" args relation commit)))) 1954 1955 (defun magit-list-containing-branches (&optional commit &rest args) 1956 (magit-list-related-branches "--contains" commit args)) 1957 1958 (defun magit-list-publishing-branches (&optional commit) 1959 (--filter (magit-rev-ancestor-p (or commit "HEAD") it) 1960 magit-published-branches)) 1961 1962 (defun magit-list-merged-branches (&optional commit &rest args) 1963 (magit-list-related-branches "--merged" commit args)) 1964 1965 (defun magit-list-unmerged-branches (&optional commit &rest args) 1966 (magit-list-related-branches "--no-merged" commit args)) 1967 1968 (defun magit-list-unmerged-to-upstream-branches () 1969 (--filter (and-let* ((upstream (magit-get-upstream-branch it))) 1970 (member it (magit-list-unmerged-branches upstream))) 1971 (magit-list-local-branch-names))) 1972 1973 (defun magit-list-branches-pointing-at (commit) 1974 (let ((re (format "\\`%s refs/\\(heads\\|remotes\\)/\\(.*\\)\\'" 1975 (magit-rev-verify commit)))) 1976 (--keep (and (string-match re it) 1977 (let ((name (match-string 2 it))) 1978 (and (not (string-suffix-p "HEAD" name)) 1979 name))) 1980 (magit-git-lines "show-ref")))) 1981 1982 (defun magit-list-refnames (&optional namespaces include-special) 1983 (nconc (magit-list-refs namespaces "%(refname:short)") 1984 (and include-special 1985 (magit-list-special-refnames)))) 1986 1987 (defvar magit-special-refnames 1988 '("HEAD" "ORIG_HEAD" "FETCH_HEAD" "MERGE_HEAD" "CHERRY_PICK_HEAD")) 1989 1990 (defun magit-list-special-refnames () 1991 (let ((gitdir (magit-gitdir))) 1992 (cl-mapcan (lambda (name) 1993 (and (file-exists-p (expand-file-name name gitdir)) 1994 (list name))) 1995 magit-special-refnames))) 1996 1997 (defun magit-list-branch-names () 1998 (magit-list-refnames (list "refs/heads" "refs/remotes"))) 1999 2000 (defun magit-list-local-branch-names () 2001 (magit-list-refnames "refs/heads")) 2002 2003 (defun magit-list-remote-branch-names (&optional remote relative) 2004 (if (and remote relative) 2005 (let ((regexp (format "^refs/remotes/%s/\\(.+\\)" remote))) 2006 (--mapcat (when (string-match regexp it) 2007 (list (match-string 1 it))) 2008 (magit-list-remote-branches remote))) 2009 (magit-list-refnames (concat "refs/remotes/" remote)))) 2010 2011 (defun magit-format-refs (format &rest args) 2012 (let ((lines (magit-git-lines 2013 "for-each-ref" (concat "--format=" format) 2014 (or args (list "refs/heads" "refs/remotes" "refs/tags"))))) 2015 (if (string-search "\f" format) 2016 (--map (split-string it "\f") lines) 2017 lines))) 2018 2019 (defun magit-list-remotes () 2020 (magit-git-lines "remote")) 2021 2022 (defun magit-list-tags () 2023 (magit-git-lines "tag")) 2024 2025 (defun magit-list-stashes (&optional format) 2026 (magit-git-lines "stash" "list" (concat "--format=" (or format "%gd")))) 2027 2028 (defun magit-list-active-notes-refs () 2029 "Return notes refs according to `core.notesRef' and `notes.displayRef'." 2030 (magit-git-lines "for-each-ref" "--format=%(refname)" 2031 (or (magit-get "core.notesRef") "refs/notes/commits") 2032 (magit-get-all "notes.displayRef"))) 2033 2034 (defun magit-list-notes-refnames () 2035 (--map (substring it 6) (magit-list-refnames "refs/notes"))) 2036 2037 (defun magit-remote-list-tags (remote) 2038 (--keep (and (not (string-suffix-p "^{}" it)) 2039 (substring it 51)) 2040 (magit-git-lines "ls-remote" "--tags" remote))) 2041 2042 (defun magit-remote-list-branches (remote) 2043 (--keep (and (not (string-suffix-p "^{}" it)) 2044 (substring it 52)) 2045 (magit-git-lines "ls-remote" "--heads" remote))) 2046 2047 (defun magit-remote-list-refs (remote) 2048 (--keep (and (not (string-suffix-p "^{}" it)) 2049 (substring it 41)) 2050 (magit-git-lines "ls-remote" remote))) 2051 2052 (defun magit-remote-head (remote) 2053 (and-let* ((line (cl-find-if 2054 (lambda (line) 2055 (string-match 2056 "\\`ref: refs/heads/\\([^\s\t]+\\)[\s\t]HEAD\\'" line)) 2057 (magit-git-lines "ls-remote" "--symref" remote "HEAD")))) 2058 (match-string 1 line))) 2059 2060 (defun magit-list-modified-modules () 2061 (--keep (and (string-match "\\`\\+\\([^ ]+\\) \\(.+\\) (.+)\\'" it) 2062 (match-string 2 it)) 2063 (magit-git-lines "submodule" "status"))) 2064 2065 (defun magit-list-module-paths () 2066 (magit-with-toplevel 2067 (--mapcat (and (string-match "^160000 [0-9a-z]\\{40,\\} 0\t\\(.+\\)$" it) 2068 (list (match-string 1 it))) 2069 (magit-git-items "ls-files" "-z" "--stage")))) 2070 2071 (defun magit-list-module-names () 2072 (mapcar #'magit-get-submodule-name (magit-list-module-paths))) 2073 2074 (defun magit-get-submodule-name (path) 2075 "Return the name of the submodule at PATH. 2076 PATH has to be relative to the super-repository." 2077 (if (magit-git-version>= "2.38.0") 2078 ;; "git submodule--helper name" was removed, 2079 ;; but might still come back in another form. 2080 (substring 2081 (car (split-string 2082 (car (or (magit-git-items 2083 "config" "-z" 2084 "-f" (expand-file-name ".gitmodules" (magit-toplevel)) 2085 "--get-regexp" "^submodule\\..*\\.path$" 2086 (concat "^" (regexp-quote (directory-file-name path)) "$")) 2087 (error "No such submodule `%s'" path))) 2088 "\n")) 2089 10 -5) 2090 (magit-git-string "submodule--helper" "name" path))) 2091 2092 (defun magit-list-worktrees () 2093 "Return list of the worktrees of this repository. 2094 2095 The returned list has the form (PATH COMMIT BRANCH BARE DETACHED 2096 LOCKED PRUNABLE). The last four elements are booleans, with the 2097 exception of LOCKED and PRUNABLE, which may also be strings. 2098 See git-worktree(1) manpage for the meaning of the various parts. 2099 2100 This function corrects a situation where \"git worktree list\" 2101 would claim a worktree is bare, even though the working tree is 2102 specified using `core.worktree'." 2103 (let ((remote (file-remote-p default-directory)) 2104 worktrees worktree) 2105 (dolist (line (let ((magit-git-global-arguments 2106 ;; KLUDGE At least in Git v2.8.3 this argument 2107 ;; would trigger a segfault. 2108 (remove "--no-pager" magit-git-global-arguments))) 2109 (if (magit-git-version>= "2.36") 2110 (magit-git-items "worktree" "list" "--porcelain" "-z") 2111 (magit-git-lines "worktree" "list" "--porcelain")))) 2112 (cond ((string-prefix-p "worktree" line) 2113 (let ((path (substring line 9))) 2114 (when remote 2115 (setq path (concat remote path))) 2116 ;; If the git directory is separate from the main 2117 ;; worktree, then "git worktree" returns the git 2118 ;; directory instead of the worktree, which isn't 2119 ;; what it is supposed to do and not what we want. 2120 ;; However, if the worktree has been removed, then 2121 ;; we want to return it anyway; instead of nil. 2122 (setq path (or (magit-toplevel path) path)) 2123 (setq worktree (list path nil nil nil nil nil nil)) 2124 (push worktree worktrees))) 2125 ((string-prefix-p "HEAD" line) 2126 (setf (nth 1 worktree) (substring line 5))) 2127 ((string-prefix-p "branch" line) 2128 (setf (nth 2 worktree) (substring line 18))) 2129 ((string-equal line "bare") 2130 (let* ((default-directory (car worktree)) 2131 (wt (and (not (magit-get-boolean "core.bare")) 2132 (magit-get "core.worktree")))) 2133 (if (and wt (file-exists-p (expand-file-name wt))) 2134 (progn (setf (nth 0 worktree) (expand-file-name wt)) 2135 (setf (nth 2 worktree) (magit-rev-parse "HEAD")) 2136 (setf (nth 3 worktree) (magit-get-current-branch))) 2137 (setf (nth 3 worktree) t)))) 2138 ((string-equal line "detached") 2139 (setf (nth 4 worktree) t)) 2140 ((string-prefix-p line "locked") 2141 (setf (nth 5 worktree) 2142 (if (> (length line) 6) (substring line 7) t))) 2143 ((string-prefix-p line "prunable") 2144 (setf (nth 6 worktree) 2145 (if (> (length line) 8) (substring line 9) t))))) 2146 (nreverse worktrees))) 2147 2148 (defun magit-symbolic-ref-p (name) 2149 (magit-git-success "symbolic-ref" "--quiet" name)) 2150 2151 (defun magit-ref-p (rev) 2152 (or (car (member rev (magit-list-refs "refs/"))) 2153 (car (member rev (magit-list-refnames "refs/"))))) 2154 2155 (defun magit-branch-p (rev) 2156 (or (car (member rev (magit-list-branches))) 2157 (car (member rev (magit-list-branch-names))))) 2158 2159 (defun magit-local-branch-p (rev) 2160 (or (car (member rev (magit-list-local-branches))) 2161 (car (member rev (magit-list-local-branch-names))))) 2162 2163 (defun magit-remote-branch-p (rev) 2164 (or (car (member rev (magit-list-remote-branches))) 2165 (car (member rev (magit-list-remote-branch-names))))) 2166 2167 (defun magit-branch-set-face (branch) 2168 (magit--propertize-face branch (if (magit-local-branch-p branch) 2169 'magit-branch-local 2170 'magit-branch-remote))) 2171 2172 (defun magit-tag-p (rev) 2173 (car (member rev (magit-list-tags)))) 2174 2175 (defun magit-remote-p (string) 2176 (car (member string (magit-list-remotes)))) 2177 2178 (defvar magit-main-branch-names 2179 '("main" "master" "trunk" "development") 2180 "Branch names reserved for use by the primary branch. 2181 Use function `magit-main-branch' to get the name actually used in 2182 the current repository.") 2183 2184 (defvar magit-long-lived-branches 2185 (append magit-main-branch-names (list "maint" "next")) 2186 "Branch names reserved for use by long lived branches.") 2187 2188 (defun magit-main-branch () 2189 "Return the main branch. 2190 2191 If a branch exists whose name matches `init.defaultBranch', then 2192 that is considered the main branch. If no branch by that name 2193 exists, then the branch names in `magit-main-branch-names' are 2194 tried in order. The first branch from that list that actually 2195 exists in the current repository is considered its main branch." 2196 (let ((branches (magit-list-local-branch-names))) 2197 (seq-find (lambda (name) 2198 (member name branches)) 2199 (delete-dups 2200 (delq nil 2201 (cons (magit-get "init.defaultBranch") 2202 magit-main-branch-names)))))) 2203 2204 (defun magit-rev-diff-count (a b &optional first-parent) 2205 "Return the commits in A but not B and vice versa. 2206 Return a list of two integers: (A>B B>A). 2207 2208 If `first-parent' is set, traverse only first parents." 2209 (mapcar #'string-to-number 2210 (split-string (magit-git-string "rev-list" 2211 "--count" "--left-right" 2212 (and first-parent "--first-parent") 2213 (concat a "..." b)) 2214 "\t"))) 2215 2216 (defun magit-abbrev-length () 2217 (let ((abbrev (magit-get "core.abbrev"))) 2218 (if (and abbrev (not (equal abbrev "auto"))) 2219 (string-to-number abbrev) 2220 ;; Guess the length git will be using based on an example 2221 ;; abbreviation. Actually HEAD's abbreviation might be an 2222 ;; outlier, so use the shorter of the abbreviations for two 2223 ;; commits. See #3034. 2224 (if-let ((head (magit-rev-parse "--short" "HEAD")) 2225 (head-len (length head))) 2226 (min head-len 2227 (if-let ((rev (magit-rev-parse "--short" "HEAD~"))) 2228 (length rev) 2229 head-len)) 2230 ;; We're on an unborn branch, but perhaps the repository has 2231 ;; other commits. See #4123. 2232 (if-let ((commits (magit-git-lines "rev-list" "-n2" "--all" 2233 "--abbrev-commit"))) 2234 (apply #'min (mapcar #'length commits)) 2235 ;; A commit does not exist. Fall back to the default of 7. 2236 7))))) 2237 2238 (defun magit-abbrev-arg (&optional arg) 2239 (format "--%s=%d" (or arg "abbrev") (magit-abbrev-length))) 2240 2241 (defun magit-rev-abbrev (rev) 2242 (magit-rev-parse (magit-abbrev-arg "short") rev)) 2243 2244 (defun magit-commit-children (commit &optional args) 2245 (mapcar #'car 2246 (--filter (member commit (cdr it)) 2247 (--map (split-string it " ") 2248 (magit-git-lines 2249 "log" "--format=%H %P" 2250 (or args (list "--branches" "--tags" "--remotes")) 2251 "--not" commit))))) 2252 2253 (defun magit-commit-parents (commit) 2254 (and-let* ((str (magit-git-string "rev-list" "-1" "--parents" commit))) 2255 (cdr (split-string str)))) 2256 2257 (defun magit-patch-id (rev) 2258 (magit--with-connection-local-variables 2259 (magit--with-temp-process-buffer 2260 (magit-process-file 2261 shell-file-name nil '(t nil) nil shell-command-switch 2262 (let ((exec (shell-quote-argument (magit-git-executable)))) 2263 (format "%s diff-tree -u %s | %s patch-id" exec rev exec))) 2264 (car (split-string (buffer-string)))))) 2265 2266 (defun magit-rev-format (format &optional rev args) 2267 ;; Prefer `git log --no-walk' to `git show --no-patch' because it 2268 ;; performs better in some scenarios. 2269 (let ((str (magit-git-string "log" "--no-walk" 2270 (concat "--format=" format) args 2271 (if rev (magit--rev-dereference rev) "HEAD") 2272 "--"))) 2273 (and (not (string-equal str "")) 2274 str))) 2275 2276 (defun magit-rev-insert-format (format &optional rev args) 2277 ;; Prefer `git log --no-walk' to `git show --no-patch' because it 2278 ;; performs better in some scenarios. 2279 (magit-git-insert "log" "--no-walk" 2280 (concat "--format=" format) args 2281 (if rev (magit--rev-dereference rev) "HEAD") 2282 "--")) 2283 2284 (defun magit-format-rev-summary (rev) 2285 (and-let* ((str (magit-rev-format "%h %s" rev))) 2286 (progn 2287 (magit--put-face 0 (string-match " " str) 'magit-hash str) 2288 str))) 2289 2290 (defvar magit-ref-namespaces 2291 '(("\\`HEAD\\'" . magit-head) 2292 ("\\`refs/tags/\\(.+\\)" . magit-tag) 2293 ("\\`refs/heads/\\(.+\\)" . magit-branch-local) 2294 ("\\`refs/remotes/\\(.+\\)" . magit-branch-remote) 2295 ("\\`refs/bisect/\\(bad\\)" . magit-bisect-bad) 2296 ("\\`refs/bisect/\\(skip.*\\)" . magit-bisect-skip) 2297 ("\\`refs/bisect/\\(good.*\\)" . magit-bisect-good) 2298 ("\\`refs/stash$" . magit-refname-stash) 2299 ("\\`refs/wip/\\(.+\\)" . magit-refname-wip) 2300 ("\\`refs/pullreqs/\\(.+\\)" . magit-refname-pullreq) 2301 ("\\`\\(bad\\):" . magit-bisect-bad) 2302 ("\\`\\(skip\\):" . magit-bisect-skip) 2303 ("\\`\\(good\\):" . magit-bisect-good) 2304 ("\\`\\(.+\\)" . magit-refname)) 2305 "How refs are formatted for display. 2306 2307 Each entry controls how a certain type of ref is displayed, and 2308 has the form (REGEXP . FACE). REGEXP is a regular expression 2309 used to match full refs. The first entry whose REGEXP matches 2310 the reference is used. 2311 2312 In log and revision buffers the first regexp submatch becomes the 2313 \"label\" that represents the ref and is propertized with FONT. 2314 In refs buffers the displayed text is controlled by other means 2315 and this option only controls what face is used.") 2316 2317 (defun magit-format-ref-labels (string) 2318 (save-match-data 2319 (let ((regexp "\\(, \\|tag: \\|HEAD -> \\)") 2320 names) 2321 (if (and (derived-mode-p 'magit-log-mode) 2322 (member "--simplify-by-decoration" magit-buffer-log-args)) 2323 (let ((branches (magit-list-local-branch-names)) 2324 (re (format "^%s/.+" (regexp-opt (magit-list-remotes))))) 2325 (setq names 2326 (--map (cond ((string-equal it "HEAD") it) 2327 ((string-prefix-p "refs/" it) it) 2328 ((member it branches) (concat "refs/heads/" it)) 2329 ((string-match re it) (concat "refs/remotes/" it)) 2330 (t (concat "refs/" it))) 2331 (split-string 2332 (string-replace "tag: " "refs/tags/" string) 2333 regexp t)))) 2334 (setq names (split-string string regexp t))) 2335 (let (state head upstream tags branches remotes other combined) 2336 (dolist (ref names) 2337 (let* ((face (cdr (--first (string-match (car it) ref) 2338 magit-ref-namespaces))) 2339 (name (magit--propertize-face 2340 (or (match-string 1 ref) ref) face))) 2341 (cl-case face 2342 ((magit-bisect-bad magit-bisect-skip magit-bisect-good) 2343 (setq state name)) 2344 (magit-head 2345 (setq head (magit--propertize-face "@" 'magit-head))) 2346 (magit-tag (push name tags)) 2347 (magit-branch-local (push name branches)) 2348 (magit-branch-remote (push name remotes)) 2349 (t (push name other))))) 2350 (setq remotes 2351 (seq-keep 2352 (lambda (name) 2353 (if (string-match "\\`\\([^/]*\\)/\\(.*\\)\\'" name) 2354 (let ((r (match-string 1 name)) 2355 (b (match-string 2 name))) 2356 (and (not (equal b "HEAD")) 2357 (if (equal (concat "refs/remotes/" name) 2358 (magit-git-string 2359 "symbolic-ref" 2360 (format "refs/remotes/%s/HEAD" r))) 2361 (magit--propertize-face 2362 name 'magit-branch-remote-head) 2363 name))) 2364 name)) 2365 remotes)) 2366 (let* ((current (magit-get-current-branch)) 2367 (target (magit-get-upstream-branch current))) 2368 (dolist (name branches) 2369 (let ((push (car (member (magit-get-push-branch name) remotes)))) 2370 (when push 2371 (setq remotes (delete push remotes)) 2372 (string-match "^[^/]*/" push) 2373 (setq push (substring push 0 (match-end 0)))) 2374 (cond 2375 ((equal name current) 2376 (setq head 2377 (concat push 2378 (magit--propertize-face 2379 name 'magit-branch-current)))) 2380 ((equal name target) 2381 (setq upstream 2382 (concat push 2383 (magit--propertize-face 2384 name '(magit-branch-upstream 2385 magit-branch-local))))) 2386 (t 2387 (push (concat push name) combined))))) 2388 (when (and target (not upstream)) 2389 (if (member target remotes) 2390 (progn 2391 (magit--add-face-text-property 2392 0 (length target) 'magit-branch-upstream nil target) 2393 (setq upstream target) 2394 (setq remotes (delete target remotes))) 2395 (when-let ((target (car (member target combined)))) 2396 (magit--add-face-text-property 2397 0 (length target) 'magit-branch-upstream nil target) 2398 (setq upstream target) 2399 (setq combined (delete target combined)))))) 2400 (string-join (flatten-tree `(,state 2401 ,head 2402 ,upstream 2403 ,@(nreverse tags) 2404 ,@(nreverse combined) 2405 ,@(nreverse remotes) 2406 ,@other)) 2407 " "))))) 2408 2409 (defun magit-object-type (object) 2410 (magit-git-string "cat-file" "-t" object)) 2411 2412 (defmacro magit-with-blob (commit file &rest body) 2413 (declare (indent 2) 2414 (debug (form form body))) 2415 `(magit--with-temp-process-buffer 2416 (let ((buffer-file-name ,file)) 2417 (save-excursion 2418 (magit-git-insert "cat-file" "-p" 2419 (concat ,commit ":" buffer-file-name))) 2420 (decode-coding-inserted-region 2421 (point-min) (point-max) buffer-file-name t nil nil t) 2422 ,@body))) 2423 2424 (defmacro magit-with-temp-index (tree arg &rest body) 2425 (declare (indent 2) (debug (form form body))) 2426 (let ((file (cl-gensym "file"))) 2427 `(let ((magit--refresh-cache nil) 2428 (,file (magit-convert-filename-for-git 2429 (make-temp-name 2430 (expand-file-name "index.magit." (magit-gitdir)))))) 2431 (unwind-protect 2432 (magit-with-toplevel 2433 (when-let ((tree ,tree)) 2434 (unless (magit-git-success "read-tree" ,arg tree 2435 (concat "--index-output=" ,file)) 2436 (error "Cannot read tree %s" tree))) 2437 (with-environment-variables (("GIT_INDEX_FILE" ,file)) 2438 ,@body)) 2439 (ignore-errors 2440 (delete-file (concat (file-remote-p default-directory) ,file))))))) 2441 2442 (defun magit-commit-tree (message &optional tree &rest parents) 2443 (magit-git-string "commit-tree" "--no-gpg-sign" "-m" message 2444 (--mapcat (list "-p" it) (delq nil parents)) 2445 (or tree 2446 (magit-git-string "write-tree") 2447 (error "Cannot write tree")))) 2448 2449 (defun magit-commit-worktree (message &optional arg &rest other-parents) 2450 (magit-with-temp-index "HEAD" arg 2451 (and (magit-update-files (magit-unstaged-files)) 2452 (apply #'magit-commit-tree message nil "HEAD" other-parents)))) 2453 2454 (defun magit-update-files (files) 2455 (magit-git-success "update-index" "--add" "--remove" "--" files)) 2456 2457 (defun magit-update-ref (ref message rev &optional stashish) 2458 (let ((magit--refresh-cache nil)) 2459 (or (if (magit-git-version>= "2.6.0") 2460 (zerop (magit-call-git "update-ref" "--create-reflog" 2461 "-m" message ref rev 2462 (or (magit-rev-verify ref) ""))) 2463 ;; `--create-reflog' didn't exist before v2.6.0 2464 (let ((oldrev (magit-rev-verify ref)) 2465 (logfile (expand-file-name (concat "logs/" ref) 2466 (magit-gitdir)))) 2467 (unless (file-exists-p logfile) 2468 (when oldrev 2469 (magit-git-success "update-ref" "-d" ref oldrev)) 2470 (make-directory (file-name-directory logfile) t) 2471 (with-temp-file logfile) 2472 (when (and oldrev (not stashish)) 2473 (magit-git-success "update-ref" "-m" "enable reflog" 2474 ref oldrev "")))) 2475 (magit-git-success "update-ref" "-m" message ref rev 2476 (or (magit-rev-verify ref) ""))) 2477 (error "Cannot update %s with %s" ref rev)))) 2478 2479 (defconst magit-range-re 2480 (concat "\\`\\([^ \t]*[^.]\\)?" ; revA 2481 "\\(\\.\\.\\.?\\)" ; range marker 2482 "\\([^.][^ \t]*\\)?\\'")) ; revB 2483 2484 (defun magit-split-range (range) 2485 (pcase-let ((`(,beg ,end ,sep) (magit--split-range-raw range))) 2486 (and sep 2487 (let ((beg (or beg "HEAD")) 2488 (end (or end "HEAD"))) 2489 (if (string-equal (match-string 2 range) "...") 2490 (and-let* ((base (magit-git-string "merge-base" beg end))) 2491 (cons base end)) 2492 (cons beg end)))))) 2493 2494 (defun magit--split-range-raw (range) 2495 (and (string-match magit-range-re range) 2496 (let ((beg (match-string 1 range)) 2497 (end (match-string 3 range))) 2498 (and (or beg end) 2499 (list beg end (match-string 2 range)))))) 2500 2501 (defun magit-hash-range (range) 2502 (if (string-match magit-range-re range) 2503 (let ((beg (match-string 1 range)) 2504 (end (match-string 3 range))) 2505 (and (or beg end) 2506 (let ((beg-hash (and beg (magit-rev-hash (match-string 1 range)))) 2507 (end-hash (and end (magit-rev-hash (match-string 3 range))))) 2508 (and (or (not beg) beg-hash) 2509 (or (not end) end-hash) 2510 (concat beg-hash (match-string 2 range) end-hash))))) 2511 (magit-rev-hash range))) 2512 2513 (defvar magit-revision-faces 2514 '(magit-hash 2515 magit-tag 2516 magit-branch-remote 2517 magit-branch-remote-head 2518 magit-branch-local 2519 magit-branch-current 2520 magit-branch-upstream 2521 magit-branch-warning 2522 magit-head 2523 magit-refname 2524 magit-refname-stash 2525 magit-refname-wip 2526 magit-refname-pullreq)) 2527 2528 (put 'git-revision 'thing-at-point #'magit-thingatpt--git-revision) 2529 (defun magit-thingatpt--git-revision (&optional disallow) 2530 ;; Support hashes and references. 2531 (and-let* ((bounds 2532 (let ((c (concat "\s\n\t~^:?*[\\" disallow))) 2533 (cl-letf 2534 (((get 'git-revision 'beginning-op) 2535 (lambda () 2536 (if (re-search-backward (format "[%s]" c) nil t) 2537 (forward-char) 2538 (goto-char (point-min))))) 2539 ((get 'git-revision 'end-op) 2540 (lambda () 2541 (re-search-forward (format "\\=[^%s]*" c) nil t)))) 2542 (bounds-of-thing-at-point 'git-revision)))) 2543 (string (buffer-substring-no-properties (car bounds) (cdr bounds))) 2544 ;; References are allowed to contain most parentheses and 2545 ;; most punctuation, but if those characters appear at the 2546 ;; edges of a possible reference in arbitrary text, then 2547 ;; they are much more likely to be intended as just that: 2548 ;; punctuation and delimiters. 2549 (string (thread-first string 2550 (string-trim-left "[(</]") 2551 (string-trim-right "[])>/.,;!]")))) 2552 (let (disallow) 2553 (when (or (string-match-p "\\.\\." string) 2554 (string-match-p "/\\." string)) 2555 (setq disallow (concat disallow "."))) 2556 (when (string-match-p "@{" string) 2557 (setq disallow (concat disallow "@{"))) 2558 (if disallow 2559 ;; These additional restrictions overcompensate, 2560 ;; but that only matters in rare cases. 2561 (magit-thingatpt--git-revision disallow) 2562 (and (not (equal string "@")) 2563 (or (and (>= (length string) 7) 2564 (string-match-p "[a-z]" string) 2565 (magit-commit-p string)) 2566 (and (magit-ref-p string) 2567 (let ((face (get-text-property (point) 'face))) 2568 (or (not face) 2569 (member face magit-revision-faces))))) 2570 string))))) 2571 2572 (put 'git-revision-range 'thing-at-point #'magit-thingatpt--git-revision-range) 2573 (defun magit-thingatpt--git-revision-range () 2574 ;; Support hashes but no references. 2575 (and-let* ((bounds 2576 (cl-letf (((get 'git-revision 'beginning-op) 2577 (lambda () 2578 (if (re-search-backward "[^a-z0-9.]" nil t) 2579 (forward-char) 2580 (goto-char (point-min))))) 2581 ((get 'git-revision 'end-op) 2582 (lambda () 2583 (and (re-search-forward "[^a-z0-9.]" nil t) 2584 (backward-char))))) 2585 (bounds-of-thing-at-point 'git-revision))) 2586 (range (buffer-substring-no-properties (car bounds) (cdr bounds)))) 2587 ;; Validate but return as-is. 2588 (and (magit-hash-range range) range))) 2589 2590 ;;; Completion 2591 2592 (defvar magit-revision-history nil) 2593 2594 (defun magit--minibuf-default-add-commit () 2595 (let ((fn minibuffer-default-add-function)) 2596 (lambda () 2597 (if-let ((commit (with-selected-window (minibuffer-selected-window) 2598 (or (magit-thing-at-point 'git-revision-range t) 2599 (magit-commit-at-point))))) 2600 (let ((rest (cons commit (delete commit (funcall fn)))) 2601 (def minibuffer-default)) 2602 (if (listp def) 2603 (append def rest) 2604 (cons def (delete def rest)))) 2605 (funcall fn))))) 2606 2607 (defun magit-read-branch (prompt &optional secondary-default) 2608 (magit-completing-read prompt (magit-list-branch-names) 2609 nil t nil 'magit-revision-history 2610 (or (magit-branch-at-point) 2611 secondary-default 2612 (magit-get-current-branch)))) 2613 2614 (defun magit-read-branch-or-commit (prompt &optional secondary-default) 2615 (let ((minibuffer-default-add-function (magit--minibuf-default-add-commit))) 2616 (or (magit-completing-read prompt (magit-list-refnames nil t) 2617 nil nil nil 'magit-revision-history 2618 (or (magit-branch-or-commit-at-point) 2619 secondary-default 2620 (magit-get-current-branch))) 2621 (user-error "Nothing selected")))) 2622 2623 (defun magit-read-range-or-commit (prompt &optional secondary-default) 2624 (magit-read-range 2625 prompt 2626 (or (and-let* ((revs (magit-region-values '(commit branch) t))) 2627 (progn 2628 (deactivate-mark) 2629 (concat (car (last revs)) ".." (car revs)))) 2630 (magit-branch-or-commit-at-point) 2631 secondary-default 2632 (magit-get-current-branch)))) 2633 2634 (defun magit-read-range (prompt &optional default) 2635 (let ((minibuffer-default-add-function (magit--minibuf-default-add-commit)) 2636 (crm-separator "\\.\\.\\.?")) 2637 (magit-completing-read-multiple 2638 (concat prompt ": ") 2639 (magit-list-refnames) 2640 nil nil nil 'magit-revision-history default nil t))) 2641 2642 (defun magit-read-remote-branch 2643 (prompt &optional remote default local-branch require-match) 2644 (let ((choice (magit-completing-read 2645 prompt 2646 (cl-union (and local-branch 2647 (if remote 2648 (list local-branch) 2649 (--map (concat it "/" local-branch) 2650 (magit-list-remotes)))) 2651 (magit-list-remote-branch-names remote t) 2652 :test #'equal) 2653 nil require-match nil 'magit-revision-history default))) 2654 (if (or remote (string-match "\\`\\([^/]+\\)/\\(.+\\)" choice)) 2655 choice 2656 (user-error "`%s' doesn't have the form REMOTE/BRANCH" choice)))) 2657 2658 (defun magit-read-refspec (prompt remote) 2659 (magit-completing-read prompt 2660 (prog2 (message "Determining available refs...") 2661 (magit-remote-list-refs remote) 2662 (message "Determining available refs...done")))) 2663 2664 (defun magit-read-local-branch (prompt &optional secondary-default) 2665 (magit-completing-read prompt (magit-list-local-branch-names) 2666 nil t nil 'magit-revision-history 2667 (or (magit-local-branch-at-point) 2668 secondary-default 2669 (magit-get-current-branch)))) 2670 2671 (defun magit-read-local-branch-or-commit (prompt) 2672 (let ((minibuffer-default-add-function (magit--minibuf-default-add-commit)) 2673 (choices (nconc (magit-list-local-branch-names) 2674 (magit-list-special-refnames))) 2675 (commit (magit-commit-at-point))) 2676 (when commit 2677 (push commit choices)) 2678 (or (magit-completing-read prompt choices 2679 nil nil nil 'magit-revision-history 2680 (or (magit-local-branch-at-point) commit)) 2681 (user-error "Nothing selected")))) 2682 2683 (defun magit-read-local-branch-or-ref (prompt &optional secondary-default) 2684 (magit-completing-read prompt (nconc (magit-list-local-branch-names) 2685 (magit-list-refs "refs/")) 2686 nil t nil 'magit-revision-history 2687 (or (magit-local-branch-at-point) 2688 secondary-default 2689 (magit-get-current-branch)))) 2690 2691 (defun magit-read-other-branch 2692 (prompt &optional exclude secondary-default no-require-match) 2693 (let* ((current (magit-get-current-branch)) 2694 (atpoint (magit-branch-at-point)) 2695 (exclude (or exclude current)) 2696 (default (or (and (not (equal atpoint exclude)) atpoint) 2697 (and (not (equal current exclude)) current) 2698 secondary-default 2699 (magit-get-previous-branch)))) 2700 (magit-completing-read prompt (delete exclude (magit-list-branch-names)) 2701 nil (not no-require-match) 2702 nil 'magit-revision-history default))) 2703 2704 (defun magit-read-other-branch-or-commit 2705 (prompt &optional exclude secondary-default) 2706 (let* ((minibuffer-default-add-function (magit--minibuf-default-add-commit)) 2707 (current (magit-get-current-branch)) 2708 (atpoint (magit-branch-or-commit-at-point)) 2709 (exclude (or exclude current)) 2710 (default (or (and (not (equal atpoint exclude)) 2711 (not (and (not current) 2712 (magit-rev-equal atpoint "HEAD"))) 2713 atpoint) 2714 (and (not (equal current exclude)) current) 2715 secondary-default 2716 (magit-get-previous-branch)))) 2717 (or (magit-completing-read prompt (delete exclude (magit-list-refnames)) 2718 nil nil nil 'magit-revision-history default) 2719 (user-error "Nothing selected")))) 2720 2721 (defun magit-read-other-local-branch 2722 (prompt &optional exclude secondary-default no-require-match) 2723 (let* ((current (magit-get-current-branch)) 2724 (atpoint (magit-local-branch-at-point)) 2725 (exclude (or exclude current)) 2726 (default (or (and (not (equal atpoint exclude)) atpoint) 2727 (and (not (equal current exclude)) current) 2728 secondary-default 2729 (magit-get-previous-branch)))) 2730 (magit-completing-read prompt 2731 (delete exclude (magit-list-local-branch-names)) 2732 nil (not no-require-match) 2733 nil 'magit-revision-history default))) 2734 2735 (defun magit-read-branch-prefer-other (prompt) 2736 (let* ((current (magit-get-current-branch)) 2737 (commit (magit-commit-at-point)) 2738 (atrev (and commit (magit-list-branches-pointing-at commit))) 2739 (atpoint (magit--painted-branch-at-point))) 2740 (magit-completing-read prompt (magit-list-branch-names) 2741 nil t nil 'magit-revision-history 2742 (or (magit-section-value-if 'branch) 2743 atpoint 2744 (and (not (cdr atrev)) (car atrev)) 2745 (--first (not (equal it current)) atrev) 2746 (magit-get-previous-branch) 2747 (car atrev))))) 2748 2749 (defun magit-read-upstream-branch (&optional branch prompt) 2750 "Read the upstream for BRANCH using PROMPT. 2751 If optional BRANCH is nil, then read the upstream for the 2752 current branch, or raise an error if no branch is checked 2753 out. Only existing branches can be selected." 2754 (unless branch 2755 (setq branch (or (magit-get-current-branch) 2756 (error "Need a branch to set its upstream")))) 2757 (let ((branches (delete branch (magit-list-branch-names)))) 2758 (magit-completing-read 2759 (or prompt (format "Change upstream of %s to" branch)) 2760 branches nil t nil 'magit-revision-history 2761 (or (let ((r (car (member (magit-remote-branch-at-point) branches))) 2762 (l (car (member (magit-local-branch-at-point) branches)))) 2763 (if magit-prefer-remote-upstream (or r l) (or l r))) 2764 (and-let* ((main (magit-main-branch))) 2765 (let ((r (car (member (concat "origin/" main) branches))) 2766 (l (car (member main branches)))) 2767 (if magit-prefer-remote-upstream (or r l) (or l r)))) 2768 (car (member (magit-get-previous-branch) branches)))))) 2769 2770 (defun magit-read-starting-point (prompt &optional branch default) 2771 (or (magit-completing-read 2772 (concat prompt 2773 (and branch 2774 (if (bound-and-true-p ivy-mode) 2775 ;; Ivy-mode strips faces from prompt. 2776 (format " `%s'" branch) 2777 (concat " " (magit--propertize-face 2778 branch 'magit-branch-local)))) 2779 " starting at") 2780 (nconc (list "HEAD") 2781 (magit-list-refnames) 2782 (directory-files (magit-gitdir) nil "_HEAD\\'")) 2783 nil nil nil 'magit-revision-history 2784 (or default (magit--default-starting-point))) 2785 (user-error "Nothing selected"))) 2786 2787 (defun magit--default-starting-point () 2788 (or (let ((r (magit-remote-branch-at-point)) 2789 (l (magit-local-branch-at-point))) 2790 (if magit-prefer-remote-upstream (or r l) (or l r))) 2791 (magit-commit-at-point) 2792 (magit-stash-at-point) 2793 (magit-get-current-branch))) 2794 2795 (defun magit-read-tag (prompt &optional require-match) 2796 (magit-completing-read prompt (magit-list-tags) nil 2797 require-match nil 'magit-revision-history 2798 (magit-tag-at-point))) 2799 2800 (defun magit-read-stash (prompt) 2801 (let* ((atpoint (magit-stash-at-point)) 2802 (default (and atpoint 2803 (concat atpoint (magit-rev-format " %s" atpoint)))) 2804 (choices (mapcar (lambda (c) 2805 (pcase-let ((`(,rev ,msg) (split-string c "\0"))) 2806 (concat (propertize rev 'face 'magit-hash) 2807 " " msg))) 2808 (magit-list-stashes "%gd%x00%s"))) 2809 (choice (magit-completing-read prompt choices 2810 nil t nil nil 2811 default 2812 (car choices)))) 2813 (and choice 2814 (string-match "^\\([^ ]+\\) \\(.+\\)" choice) 2815 (substring-no-properties (match-string 1 choice))))) 2816 2817 (defun magit-read-remote (prompt &optional default use-only) 2818 (let ((remotes (magit-list-remotes))) 2819 (if (and use-only (length= remotes 1)) 2820 (car remotes) 2821 (magit-completing-read prompt remotes 2822 nil t nil nil 2823 (or default 2824 (magit-remote-at-point) 2825 (magit-get-remote)))))) 2826 2827 (defun magit-read-remote-or-url (prompt &optional default) 2828 (magit-completing-read prompt 2829 (nconc (magit-list-remotes) 2830 (list "https://" "git://" "git@")) 2831 nil nil nil nil 2832 (or default 2833 (magit-remote-at-point) 2834 (magit-get-remote)))) 2835 2836 (defun magit-read-module-path (prompt &optional predicate) 2837 (magit-completing-read prompt (magit-list-module-paths) 2838 predicate t nil nil 2839 (magit-module-at-point predicate))) 2840 2841 (defun magit-module-confirm (verb &optional predicate) 2842 ;; Some predicates use the inefficient `magit-toplevel' 2843 ;; and some repositories have thousands of submodules. 2844 (let ((magit--refresh-cache (list (cons 0 0))) 2845 (modules nil)) 2846 (if current-prefix-arg 2847 (progn 2848 (setq modules (magit-list-module-paths)) 2849 (when predicate 2850 (setq modules (seq-filter predicate modules))) 2851 (unless modules 2852 (if predicate 2853 (user-error "No modules satisfying %s available" predicate) 2854 (user-error "No modules available")))) 2855 (setq modules (magit-region-values 'module)) 2856 (when modules 2857 (when predicate 2858 (setq modules (seq-filter predicate modules))) 2859 (unless modules 2860 (user-error "No modules satisfying %s selected" predicate)))) 2861 (if (or (length> modules 1) current-prefix-arg) 2862 (magit-confirm t nil (format "%s %%d modules" verb) nil modules) 2863 (list (magit-read-module-path (format "%s module" verb) predicate))))) 2864 2865 ;;; _ 2866 (provide 'magit-git) 2867 ;;; magit-git.el ends here