magit-git.el (123507B)
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 "Git executable cannot be found (see https://magit.vc/goto/e6a78ed2)") 941 942 (defun magit--assert-usable-git () 943 (if (not (compat-call executable-find (magit-git-executable) t)) 944 (signal 'magit-git-executable-not-found (magit-git-executable)) 945 (let ((magit-git-debug 946 (lambda (err) 947 (signal 'magit-corrupt-git-config 948 (format "%s: %s" default-directory err))))) 949 ;; This should always succeed unless there's a corrupt config 950 ;; (or at least a similarly severe failing state). Note that 951 ;; git-config's --default is avoided because it's not available 952 ;; until Git 2.18. 953 (magit-git-string "config" "--get-color" "" "reset")) 954 nil)) 955 956 (defun magit--not-inside-repository-error () 957 (magit--assert-usable-git) 958 (signal 'magit-outside-git-repo default-directory)) 959 960 (defun magit-inside-gitdir-p (&optional noerror) 961 "Return t if `default-directory' is below the repository directory. 962 If it is below the working directory, then return nil. 963 If it isn't below either, then signal an error unless NOERROR 964 is non-nil, in which case return nil." 965 (and (magit--assert-default-directory noerror) 966 ;; Below a repository directory that is not located below the 967 ;; working directory "git rev-parse --is-inside-git-dir" prints 968 ;; "false", which is wrong. 969 (let ((gitdir (magit-gitdir))) 970 (cond (gitdir (file-in-directory-p default-directory gitdir)) 971 (noerror nil) 972 ((signal 'magit-outside-git-repo default-directory)))))) 973 974 (defun magit-inside-worktree-p (&optional noerror) 975 "Return t if `default-directory' is below the working directory. 976 If it is below the repository directory, then return nil. 977 If it isn't below either, then signal an error unless NOERROR 978 is non-nil, in which case return nil." 979 (and (magit--assert-default-directory noerror) 980 (condition-case nil 981 (magit-rev-parse-true "--is-inside-work-tree") 982 (magit-invalid-git-boolean 983 (and (not noerror) 984 (signal 'magit-outside-git-repo default-directory)))))) 985 986 (cl-defgeneric magit-bare-repo-p (&optional noerror) 987 "Return t if the current repository is bare. 988 If it is non-bare, then return nil. If `default-directory' 989 isn't below a Git repository, then signal an error unless 990 NOERROR is non-nil, in which case return nil." 991 (and (magit--assert-default-directory noerror) 992 (condition-case nil 993 (magit-rev-parse-true "--is-bare-repository") 994 (magit-invalid-git-boolean 995 (and (not noerror) 996 (signal 'magit-outside-git-repo default-directory)))))) 997 998 (defun magit--assert-default-directory (&optional noerror) 999 (or (file-directory-p default-directory) 1000 (and (not noerror) 1001 (let ((exists (file-exists-p default-directory))) 1002 (signal (if exists 'file-error 'file-missing) 1003 (list "Running git in directory" 1004 (if exists 1005 "Not a directory" 1006 "No such file or directory") 1007 default-directory)))))) 1008 1009 (defun magit-git-repo-p (directory &optional non-bare) 1010 "Return t if DIRECTORY is a Git repository. 1011 When optional NON-BARE is non-nil also return nil if DIRECTORY is 1012 a bare repository." 1013 (and (file-directory-p directory) ; Avoid archives, see #3397. 1014 (or (file-regular-p (expand-file-name ".git" directory)) 1015 (file-directory-p (expand-file-name ".git" directory)) 1016 (and (not non-bare) 1017 (file-regular-p (expand-file-name "HEAD" directory)) 1018 (file-directory-p (expand-file-name "refs" directory)) 1019 (file-directory-p (expand-file-name "objects" directory)))))) 1020 1021 (defun magit-file-relative-name (&optional file tracked) 1022 "Return the path of FILE relative to the repository root. 1023 1024 If optional FILE is nil or omitted, return the relative path of 1025 the file being visited in the current buffer, if any, else nil. 1026 If the file is not inside a Git repository, then return nil. 1027 1028 If TRACKED is non-nil, return the path only if it matches a 1029 tracked file." 1030 (unless file 1031 (with-current-buffer (or (buffer-base-buffer) 1032 (current-buffer)) 1033 (setq file (or magit-buffer-file-name buffer-file-name 1034 (and (derived-mode-p 'dired-mode) default-directory))))) 1035 (when (and file (or (not tracked) 1036 (magit-file-tracked-p (file-relative-name file)))) 1037 (and-let* ((dir (magit-toplevel 1038 (magit--safe-default-directory 1039 (directory-file-name (file-name-directory file)))))) 1040 (file-relative-name file dir)))) 1041 1042 (defun magit-file-ignored-p (file) 1043 (magit-git-string-p "ls-files" "--others" "--ignored" "--exclude-standard" 1044 "--" (magit-convert-filename-for-git file))) 1045 1046 (defun magit-file-tracked-p (file) 1047 (magit-git-success "ls-files" "--error-unmatch" 1048 "--" (magit-convert-filename-for-git file))) 1049 1050 (defun magit-list-files (&rest args) 1051 (apply #'magit-git-items "ls-files" "-z" "--full-name" args)) 1052 1053 (defun magit-tracked-files () 1054 (magit-list-files "--cached")) 1055 1056 (defun magit-untracked-files (&optional all files compact) 1057 (if compact 1058 (--mapcat (and (eq (aref it 0) ??) 1059 (list (substring it 3))) 1060 (magit-git-items "status" "-z" "--porcelain" 1061 (magit-ignore-submodules-p t) 1062 "--" files)) 1063 (magit-list-files "--other" 1064 (and (not all) "--exclude-standard") 1065 "--" files))) 1066 1067 (defun magit-modified-files (&optional nomodules files) 1068 (magit-git-items "diff-index" "-z" "--name-only" 1069 (and nomodules "--ignore-submodules") 1070 (magit-headish) "--" files)) 1071 1072 (defun magit-unstaged-files (&optional nomodules files) 1073 (magit-git-items "diff-files" "-z" "--name-only" "--diff-filter=u" 1074 (and nomodules "--ignore-submodules") 1075 "--" files)) 1076 1077 (defun magit-staged-files (&optional nomodules files) 1078 (magit-git-items "diff-index" "-z" "--name-only" "--cached" 1079 (and nomodules "--ignore-submodules") 1080 (magit-headish) "--" files)) 1081 1082 (defun magit-binary-files (&rest args) 1083 (--mapcat (and (string-match "^-\t-\t\\(.+\\)" it) 1084 (list (match-string 1 it))) 1085 (apply #'magit-git-items 1086 "diff" "-z" "--numstat" "--ignore-submodules" 1087 args))) 1088 1089 (defun magit-unmerged-files () 1090 (magit-git-items "diff-files" "-z" "--name-only" "--diff-filter=U")) 1091 1092 (defun magit-ignored-files () 1093 (magit-git-items "ls-files" "-z" "--others" "--ignored" 1094 "--exclude-standard" "--directory")) 1095 1096 (defun magit-stashed-files (stash) 1097 (magit-git-items "stash" "show" "-z" "--name-only" stash)) 1098 1099 (defun magit-skip-worktree-files () 1100 (--keep (and (= (aref it 0) ?S) 1101 (substring it 2)) 1102 (magit-list-files "-t"))) 1103 1104 (defun magit-assume-unchanged-files () 1105 (--keep (and (memq (aref it 0) '(?h ?s ?m ?r ?c ?k)) 1106 (substring it 2)) 1107 (magit-list-files "-v"))) 1108 1109 (defun magit-revision-files (rev) 1110 (magit-with-toplevel 1111 (magit-git-items "ls-tree" "-z" "-r" "--name-only" rev))) 1112 1113 (defun magit-revision-directories (rev) 1114 "List directories that contain a tracked file in revision REV." 1115 (magit-with-toplevel 1116 (mapcar #'file-name-as-directory 1117 (magit-git-items "ls-tree" "-z" "-r" "-d" "--name-only" rev)))) 1118 1119 (defun magit-changed-files (rev-or-range &optional other-rev) 1120 "Return list of files the have changed between two revisions. 1121 If OTHER-REV is non-nil, REV-OR-RANGE should be a revision, not a 1122 range. Otherwise, it can be any revision or range accepted by 1123 \"git diff\" (i.e., <rev>, <revA>..<revB>, or <revA>...<revB>)." 1124 (magit-with-toplevel 1125 (magit-git-items "diff" "-z" "--name-only" rev-or-range other-rev))) 1126 1127 (defun magit-renamed-files (revA revB) 1128 (mapcar (pcase-lambda (`(,_status ,fileA ,fileB)) 1129 (cons fileA fileB)) 1130 (seq-partition (magit-git-items "diff" "-z" "--name-status" 1131 "--find-renames" 1132 "--diff-filter=R" revA revB) 1133 3))) 1134 1135 (defun magit--rev-file-name (file rev other-rev) 1136 "For FILE, potentially renamed between REV and OTHER-REV, return name in REV. 1137 Return nil, if FILE appears neither in REV nor OTHER-REV, 1138 or if no rename is detected." 1139 (or (car (member file (magit-revision-files rev))) 1140 (and-let* ((renamed (magit-renamed-files rev other-rev))) 1141 (car (rassoc file renamed))))) 1142 1143 (defun magit-file-status (&rest args) 1144 (magit--with-temp-process-buffer 1145 (save-excursion (magit-git-insert "status" "-z" args)) 1146 (let ((pos (point)) status) 1147 (while (> (skip-chars-forward "[:print:]") 0) 1148 (let ((x (char-after pos)) 1149 (y (char-after (1+ pos))) 1150 (file (buffer-substring (+ pos 3) (point)))) 1151 (forward-char) 1152 (if (memq x '(?R ?C)) 1153 (progn 1154 (setq pos (point)) 1155 (skip-chars-forward "[:print:]") 1156 (push (list file (buffer-substring pos (point)) x y) status) 1157 (forward-char)) 1158 (push (list file nil x y) status))) 1159 (setq pos (point))) 1160 status))) 1161 1162 (defcustom magit-cygwin-mount-points 1163 (and (eq system-type 'windows-nt) 1164 (cl-sort (--map (if (string-match "^\\(.*\\) on \\(.*\\) type" it) 1165 (cons (file-name-as-directory (match-string 2 it)) 1166 (file-name-as-directory (match-string 1 it))) 1167 (lwarn '(magit) :error 1168 "Failed to parse Cygwin mount: %S" it)) 1169 ;; If --exec-path is not a native Windows path, 1170 ;; then we probably have a cygwin git. 1171 (let ((process-environment 1172 (append magit-git-environment 1173 process-environment))) 1174 (and (not (string-match-p 1175 "\\`[a-zA-Z]:" 1176 (car (process-lines 1177 magit-git-executable "--exec-path")))) 1178 (ignore-errors (process-lines "mount"))))) 1179 #'> :key (pcase-lambda (`(,cyg . ,_win)) (length cyg)))) 1180 "Alist of (CYGWIN . WIN32) directory names. 1181 Sorted from longest to shortest CYGWIN name." 1182 :package-version '(magit . "2.3.0") 1183 :group 'magit-process 1184 :type '(alist :key-type string :value-type directory)) 1185 1186 (defun magit-expand-git-file-name (filename) 1187 (unless (file-name-absolute-p filename) 1188 (setq filename (expand-file-name filename))) 1189 (if-let ((cyg:win (and (not (file-remote-p default-directory)) ; see #4976 1190 (cl-assoc filename magit-cygwin-mount-points 1191 :test (lambda (f cyg) (string-prefix-p cyg f)))))) 1192 (concat (cdr cyg:win) 1193 (substring filename (length (car cyg:win)))) 1194 filename)) 1195 1196 (defun magit-convert-filename-for-git (filename) 1197 "Convert FILENAME so that it can be passed to git. 1198 1. If it is a absolute filename, then pass it through 1199 `expand-file-name' to replace things such as \"~/\" that 1200 Git does not understand. 1201 2. If it is a remote filename, then remove the remote part. 1202 3. Deal with an `windows-nt' Emacs vs. Cygwin Git incompatibility." 1203 (if (file-name-absolute-p filename) 1204 (if-let ((cyg:win (cl-rassoc filename magit-cygwin-mount-points 1205 :test (lambda (f win) (string-prefix-p win f))))) 1206 (concat (car cyg:win) 1207 (substring filename (length (cdr cyg:win)))) 1208 (let ((expanded (expand-file-name filename))) 1209 (or (file-remote-p expanded 'localname) 1210 expanded))) 1211 filename)) 1212 1213 (defun magit-decode-git-path (path) 1214 (if (eq (aref path 0) ?\") 1215 (decode-coding-string (read path) 1216 (or magit-git-output-coding-system 1217 (car default-process-coding-system)) 1218 t) 1219 path)) 1220 1221 (defun magit-file-at-point (&optional expand assert) 1222 (if-let ((file (magit-section-case 1223 (file (oref it value)) 1224 (hunk (magit-section-parent-value it))))) 1225 (if expand 1226 (expand-file-name file (magit-toplevel)) 1227 file) 1228 (when assert 1229 (user-error "No file at point")))) 1230 1231 (defun magit-current-file () 1232 (or (magit-file-relative-name) 1233 (magit-file-at-point) 1234 (and (derived-mode-p 'magit-log-mode) 1235 (car magit-buffer-log-files)))) 1236 1237 ;;; Predicates 1238 1239 (defun magit-no-commit-p () 1240 "Return t if there is no commit in the current Git repository." 1241 (not (magit-rev-verify "HEAD"))) 1242 1243 (defun magit-merge-commit-p (commit) 1244 "Return t if COMMIT is a merge commit." 1245 (length> (magit-commit-parents commit) 1)) 1246 1247 (defun magit-anything-staged-p (&optional ignore-submodules &rest files) 1248 "Return t if there are any staged changes. 1249 If optional FILES is non-nil, then only changes to those files 1250 are considered." 1251 (magit-git-failure "diff" "--quiet" "--cached" 1252 (and ignore-submodules "--ignore-submodules") 1253 "--" files)) 1254 1255 (defun magit-anything-unstaged-p (&optional ignore-submodules &rest files) 1256 "Return t if there are any unstaged changes. 1257 If optional FILES is non-nil, then only changes to those files 1258 are considered." 1259 (magit-git-failure "diff" "--quiet" 1260 (and ignore-submodules "--ignore-submodules") 1261 "--" files)) 1262 1263 (defun magit-anything-modified-p (&optional ignore-submodules &rest files) 1264 "Return t if there are any staged or unstaged changes. 1265 If optional FILES is non-nil, then only changes to those files 1266 are considered." 1267 (or (apply #'magit-anything-staged-p ignore-submodules files) 1268 (apply #'magit-anything-unstaged-p ignore-submodules files))) 1269 1270 (defun magit-anything-unmerged-p (&rest files) 1271 "Return t if there are any merge conflicts. 1272 If optional FILES is non-nil, then only conflicts in those files 1273 are considered." 1274 (and (magit-git-string "ls-files" "--unmerged" files) t)) 1275 1276 (defun magit-module-worktree-p (module) 1277 (magit-with-toplevel 1278 (file-exists-p (expand-file-name ".git" module)))) 1279 1280 (defun magit-module-no-worktree-p (module) 1281 (not (magit-module-worktree-p module))) 1282 1283 (defun magit-ignore-submodules-p (&optional return-argument) 1284 (or (cl-find-if (lambda (arg) 1285 (string-prefix-p "--ignore-submodules" arg)) 1286 magit-buffer-diff-args) 1287 (and-let* ((value (magit-get "diff.ignoreSubmodules"))) 1288 (if return-argument 1289 (concat "--ignore-submodules=" value) 1290 (concat "diff.ignoreSubmodules=" value))))) 1291 1292 ;;; Revisions and References 1293 1294 (defun magit-rev-parse (&rest args) 1295 "Execute `git rev-parse ARGS', returning first line of output. 1296 If there is no output, return nil." 1297 (apply #'magit-git-string "rev-parse" args)) 1298 1299 (defun magit-rev-parse-safe (&rest args) 1300 "Execute `git rev-parse ARGS', returning first line of output. 1301 If there is no output, return nil. Like `magit-rev-parse' but 1302 ignore `magit-git-debug'." 1303 (apply #'magit-git-str "rev-parse" args)) 1304 1305 (defun magit-rev-parse-true (&rest args) 1306 "Execute `git rev-parse ARGS', returning t if it prints \"true\". 1307 If it prints \"false\", then return nil. For any other output 1308 signal an error." 1309 (magit-git-true "rev-parse" args)) 1310 1311 (defun magit-rev-parse-false (&rest args) 1312 "Execute `git rev-parse ARGS', returning t if it prints \"false\". 1313 If it prints \"true\", then return nil. For any other output 1314 signal an error." 1315 (magit-git-false "rev-parse" args)) 1316 1317 (defun magit-rev-parse-p (&rest args) 1318 "Execute `git rev-parse ARGS', returning t if it prints \"true\". 1319 Return t if the first (and usually only) output line is the 1320 string \"true\", otherwise return nil." 1321 (equal (magit-git-str "rev-parse" args) "true")) 1322 1323 (defun magit-rev-verify (rev) 1324 (magit-git-string-p "rev-parse" "--verify" rev)) 1325 1326 (defun magit-commit-p (rev) 1327 "Return full hash for REV if it names an existing commit." 1328 (magit-rev-verify (magit--rev-dereference rev))) 1329 1330 (defalias 'magit-rev-verify-commit #'magit-commit-p) 1331 1332 (defalias 'magit-rev-hash #'magit-commit-p) 1333 1334 (defun magit--rev-dereference (rev) 1335 "Return a rev that forces Git to interpret REV as a commit. 1336 If REV is nil or has the form \":/TEXT\", return REV itself." 1337 (cond ((not rev) nil) 1338 ((string-match-p "^:/" rev) rev) 1339 ((concat rev "^{commit}")))) 1340 1341 (defun magit-rev-equal (a b) 1342 "Return t if there are no differences between the commits A and B." 1343 (magit-git-success "diff" "--quiet" a b)) 1344 1345 (defun magit-rev-eq (a b) 1346 "Return t if A and B refer to the same commit." 1347 (let ((a (magit-commit-p a)) 1348 (b (magit-commit-p b))) 1349 (and a b (equal a b)))) 1350 1351 (defun magit-rev-ancestor-p (a b) 1352 "Return non-nil if commit A is an ancestor of commit B." 1353 (magit-git-success "merge-base" "--is-ancestor" a b)) 1354 1355 (defun magit-rev-head-p (rev) 1356 (or (equal rev "HEAD") 1357 (and rev 1358 (not (string-search ".." rev)) 1359 (equal (magit-rev-parse rev) 1360 (magit-rev-parse "HEAD"))))) 1361 1362 (defun magit-rev-author-p (rev) 1363 "Return t if the user is the author of REV. 1364 More precisely return t if `user.name' is equal to the author 1365 name of REV and/or `user.email' is equal to the author email 1366 of REV." 1367 (or (equal (magit-get "user.name") (magit-rev-format "%an" rev)) 1368 (equal (magit-get "user.email") (magit-rev-format "%ae" rev)))) 1369 1370 (defun magit-rev-name (rev &optional pattern not-anchored) 1371 "Return a symbolic name for REV using `git-name-rev'. 1372 1373 PATTERN can be used to limit the result to a matching ref. 1374 Unless NOT-ANCHORED is non-nil, the beginning of the ref must 1375 match PATTERN. 1376 1377 An anchored lookup is done using the arguments 1378 \"--exclude=*/<PATTERN> --exclude=*/HEAD\" in addition to 1379 \"--refs=<PATTERN>\", provided at least version v2.13 of Git is 1380 used. Older versions did not support the \"--exclude\" argument. 1381 When \"--exclude\" cannot be used and `git-name-rev' returns a 1382 ref that should have been excluded, then that is discarded and 1383 this function returns nil instead. This is unfortunate because 1384 there might be other refs that do match. To fix that, update 1385 Git." 1386 (if (magit-git-version< "2.13") 1387 (and-let* 1388 ((ref (magit-git-string "name-rev" "--name-only" "--no-undefined" 1389 (and pattern (concat "--refs=" pattern)) 1390 rev))) 1391 (if (and pattern 1392 (string-match-p "\\`refs/[^/]+/\\*\\'" pattern)) 1393 (let ((namespace (substring pattern 0 -1))) 1394 (and (not (or (string-suffix-p "HEAD" ref) 1395 (and (string-match-p namespace ref) 1396 (not (magit-rev-verify 1397 (concat namespace ref)))))) 1398 ref)) 1399 ref)) 1400 (magit-git-string "name-rev" "--name-only" "--no-undefined" 1401 (and pattern (concat "--refs=" pattern)) 1402 (and pattern 1403 (not not-anchored) 1404 (list "--exclude=*/HEAD" 1405 (concat "--exclude=*/" pattern))) 1406 rev))) 1407 1408 (defun magit-rev-branch (rev) 1409 (and-let* ((name (magit-rev-name rev "refs/heads/*"))) 1410 (and (not (string-match-p "[~^]" name)) name))) 1411 1412 (defun magit-rev-fixup-target (rev) 1413 (let ((msg (magit-rev-format "%s" rev))) 1414 (save-match-data 1415 (and (string-match "\\`\\(fixup\\|squash\\)! \\(.+\\)" msg) 1416 (magit-rev-format 1417 "%h" (format "%s^{/^%s}" rev 1418 (magit--ext-regexp-quote (match-string 2 msg)))))))) 1419 1420 (defun magit-get-shortname (rev) 1421 (let* ((fn (apply-partially #'magit-rev-name rev)) 1422 (name (or (funcall fn "refs/tags/*") 1423 (funcall fn "refs/heads/*") 1424 (funcall fn "refs/remotes/*")))) 1425 (cond ((not name) 1426 (magit-rev-parse "--short" rev)) 1427 ((string-match "^\\(?:tags\\|remotes\\)/\\(.+\\)" name) 1428 (if (magit-ref-ambiguous-p (match-string 1 name)) 1429 name 1430 (match-string 1 name))) 1431 ((magit-ref-maybe-qualify name))))) 1432 1433 (defun magit-name-branch (rev &optional lax) 1434 (or (magit-name-local-branch rev) 1435 (magit-name-remote-branch rev) 1436 (and lax (or (magit-name-local-branch rev t) 1437 (magit-name-remote-branch rev t))))) 1438 1439 (defun magit-name-local-branch (rev &optional lax) 1440 (and-let* ((name (magit-rev-name rev "refs/heads/*"))) 1441 (and (or lax (not (string-match-p "[~^]" name))) name))) 1442 1443 (defun magit-name-remote-branch (rev &optional lax) 1444 (and-let* ((name (magit-rev-name rev "refs/remotes/*"))) 1445 (and (or lax (not (string-match-p "[~^]" name))) 1446 (substring name 8)))) 1447 1448 (defun magit-name-tag (rev &optional lax) 1449 (and-let* ((name (magit-rev-name rev "refs/tags/*"))) 1450 ;; The progn is necessary to work around debbugs#31840. This, and all 1451 ;; the other instances, can be removed once we require at least Emacs 27. 1452 (progn 1453 (when (string-suffix-p "^0" name) 1454 (setq name (substring name 0 -2))) 1455 (and (or lax (not (string-match-p "[~^]" name))) 1456 (substring name 5))))) 1457 1458 (defun magit-ref-abbrev (refname) 1459 "Return an unambiguous abbreviation of REFNAME." 1460 (magit-rev-parse "--verify" "--abbrev-ref" refname)) 1461 1462 (defun magit-ref-fullname (refname) 1463 "Return fully qualified refname for REFNAME. 1464 If REFNAME is ambiguous, return nil." 1465 (magit-rev-parse "--verify" "--symbolic-full-name" refname)) 1466 1467 (defun magit-ref-ambiguous-p (refname) 1468 (save-match-data 1469 (if (string-match "\\`\\([^^~]+\\)\\(.*\\)" refname) 1470 (not (magit-ref-fullname (match-string 1 refname))) 1471 (error "%S has an unrecognized format" refname)))) 1472 1473 (defun magit-ref-maybe-qualify (refname &optional prefix) 1474 "If REFNAME is ambiguous, try to disambiguate it by prepend PREFIX to it. 1475 Return an unambiguous refname, either REFNAME or that prefixed 1476 with PREFIX, nil otherwise. If REFNAME has an offset suffix 1477 such as \"~1\", then that is preserved. If optional PREFIX is 1478 nil, then use \"heads/\". " 1479 (if (magit-ref-ambiguous-p refname) 1480 (let ((refname (concat (or prefix "heads/") refname))) 1481 (and (not (magit-ref-ambiguous-p refname)) refname)) 1482 refname)) 1483 1484 (defun magit-ref-exists-p (ref) 1485 (magit-git-success "show-ref" "--verify" ref)) 1486 1487 (defun magit-ref-equal (a b) 1488 "Return t if the refnames A and B are `equal'. 1489 A symbolic-ref pointing to some ref, is `equal' to that ref, 1490 as are two symbolic-refs pointing to the same ref. Refnames 1491 may be abbreviated." 1492 (let ((a (magit-ref-fullname a)) 1493 (b (magit-ref-fullname b))) 1494 (and a b (equal a b)))) 1495 1496 (defun magit-ref-eq (a b) 1497 "Return t if the refnames A and B are `eq'. 1498 A symbolic-ref is `eq' to itself, but not to the ref it points 1499 to, or to some other symbolic-ref that points to the same ref." 1500 (let ((symbolic-a (magit-symbolic-ref-p a)) 1501 (symbolic-b (magit-symbolic-ref-p b))) 1502 (or (and symbolic-a 1503 symbolic-b 1504 (equal a b)) 1505 (and (not symbolic-a) 1506 (not symbolic-b) 1507 (magit-ref-equal a b))))) 1508 1509 (defun magit-headish () 1510 "Return the `HEAD' or if that doesn't exist the hash of the empty tree." 1511 (if (magit-no-commit-p) 1512 (magit-git-string "mktree") 1513 "HEAD")) 1514 1515 (defun magit-branch-at-point () 1516 (magit-section-case 1517 (branch (oref it value)) 1518 (commit (or (magit--painted-branch-at-point) 1519 (magit-name-branch (oref it value)))))) 1520 1521 (defun magit--painted-branch-at-point (&optional type) 1522 (or (and (not (eq type 'remote)) 1523 (memq (get-text-property (magit-point) 'font-lock-face) 1524 (list 'magit-branch-local 1525 'magit-branch-current)) 1526 (and-let* ((branch (magit-thing-at-point 'git-revision t))) 1527 (cdr (magit-split-branch-name branch)))) 1528 (and (not (eq type 'local)) 1529 (memq (get-text-property (magit-point) 'font-lock-face) 1530 (list 'magit-branch-remote 1531 'magit-branch-remote-head)) 1532 (thing-at-point 'git-revision t)))) 1533 1534 (defun magit-local-branch-at-point () 1535 (magit-section-case 1536 (branch (let ((branch (magit-ref-maybe-qualify (oref it value)))) 1537 (when (member branch (magit-list-local-branch-names)) 1538 branch))) 1539 (commit (or (magit--painted-branch-at-point 'local) 1540 (magit-name-local-branch (oref it value)))))) 1541 1542 (defun magit-remote-branch-at-point () 1543 (magit-section-case 1544 (branch (let ((branch (oref it value))) 1545 (when (member branch (magit-list-remote-branch-names)) 1546 branch))) 1547 (commit (or (magit--painted-branch-at-point 'remote) 1548 (magit-name-remote-branch (oref it value)))))) 1549 1550 (defun magit-commit-at-point () 1551 (or (magit-section-value-if 'commit) 1552 (magit-thing-at-point 'git-revision t) 1553 (and-let* ((chunk (and (bound-and-true-p magit-blame-mode) 1554 (fboundp 'magit-current-blame-chunk) 1555 (magit-current-blame-chunk)))) 1556 (oref chunk orig-rev)) 1557 (and (derived-mode-p 'magit-stash-mode 1558 'magit-merge-preview-mode 1559 'magit-revision-mode) 1560 magit-buffer-revision))) 1561 1562 (defun magit-branch-or-commit-at-point () 1563 (or (magit-section-case 1564 (branch (magit-ref-maybe-qualify (oref it value))) 1565 (commit (or (magit--painted-branch-at-point) 1566 (let ((rev (oref it value))) 1567 (or (magit-name-branch rev) rev)))) 1568 (tag (magit-ref-maybe-qualify (oref it value) "tags/")) 1569 (pullreq (or (and (fboundp 'forge--pullreq-branch) 1570 (magit-branch-p 1571 (forge--pullreq-branch (oref it value)))) 1572 (magit-ref-p (format "refs/pullreqs/%s" 1573 (oref (oref it value) number))))) 1574 ((unpulled unpushed) 1575 (magit-ref-abbrev 1576 (replace-regexp-in-string "\\.\\.\\.?" "" (oref it value))))) 1577 (magit-thing-at-point 'git-revision t) 1578 (and-let* ((chunk (and (bound-and-true-p magit-blame-mode) 1579 (fboundp 'magit-current-blame-chunk) 1580 (magit-current-blame-chunk)))) 1581 (oref chunk orig-rev)) 1582 (and magit-buffer-file-name 1583 magit-buffer-refname) 1584 (and (derived-mode-p 'magit-stash-mode 1585 'magit-merge-preview-mode 1586 'magit-revision-mode) 1587 magit-buffer-revision))) 1588 1589 (defun magit-tag-at-point () 1590 (magit-section-case 1591 (tag (oref it value)) 1592 (commit (magit-name-tag (oref it value))))) 1593 1594 (defun magit-stash-at-point () 1595 (magit-section-value-if 'stash)) 1596 1597 (defun magit-remote-at-point () 1598 (magit-section-case 1599 (remote (oref it value)) 1600 ([branch remote] (magit-section-parent-value it)))) 1601 1602 (defun magit-module-at-point (&optional predicate) 1603 (when (magit-section-match 'module) 1604 (let ((module (oref (magit-current-section) value))) 1605 (and (or (not predicate) 1606 (funcall predicate module)) 1607 module)))) 1608 1609 (defun magit-get-current-branch () 1610 "Return the refname of the currently checked out branch. 1611 Return nil if no branch is currently checked out." 1612 (magit-git-string "symbolic-ref" "--short" "HEAD")) 1613 1614 (defvar magit-get-previous-branch-timeout 0.5 1615 "Maximum time to spend in `magit-get-previous-branch'. 1616 Given as a number of seconds.") 1617 1618 (defun magit-get-previous-branch () 1619 "Return the refname of the previously checked out branch. 1620 Return nil if no branch can be found in the `HEAD' reflog 1621 which is different from the current branch and still exists. 1622 The amount of time spent searching is limited by 1623 `magit-get-previous-branch-timeout'." 1624 (let ((t0 (float-time)) 1625 (current (magit-get-current-branch)) 1626 (i 1) prev) 1627 (while (if (> (- (float-time) t0) magit-get-previous-branch-timeout) 1628 (setq prev nil) ;; Timed out. 1629 (and (setq prev (magit-rev-verify (format "@{-%d}" i))) 1630 (or (not (setq prev (magit-rev-branch prev))) 1631 (equal prev current)))) 1632 (cl-incf i)) 1633 prev)) 1634 1635 (defun magit--set-default-branch (newname oldname) 1636 (let ((remote (or (magit-primary-remote) 1637 (user-error "Cannot determine primary remote"))) 1638 (branches (mapcar (lambda (line) (split-string line "\t")) 1639 (magit-git-lines 1640 "for-each-ref" "refs/heads" 1641 "--format=%(refname:short)\t%(upstream:short)")))) 1642 (when-let ((old (assoc oldname branches))) 1643 (unless (assoc newname branches) 1644 (magit-call-git "branch" "-m" oldname newname) 1645 (setcar old newname))) 1646 (let ((new (if (magit-branch-p newname) 1647 newname 1648 (concat remote "/" newname)))) 1649 (pcase-dolist (`(,branch ,upstream) branches) 1650 (cond 1651 ((equal upstream oldname) 1652 (magit-set-upstream-branch branch new)) 1653 ((equal upstream (concat remote "/" oldname)) 1654 (magit-set-upstream-branch branch (concat remote "/" newname)))))))) 1655 1656 (defun magit--get-default-branch (&optional update) 1657 (let ((remote (magit-primary-remote))) 1658 (when update 1659 (if (not remote) 1660 (user-error "Cannot determine primary remote") 1661 (message "Determining default branch...") 1662 (magit-git "fetch" "--prune") 1663 (magit-git "remote" "set-head" "--auto" remote) 1664 (message "Determining default branch...done"))) 1665 (let ((branch (magit-git-string "symbolic-ref" "--short" 1666 (format "refs/remotes/%s/HEAD" remote)))) 1667 (when (and update (not branch)) 1668 (error "Cannot determine new default branch")) 1669 (list remote (and branch (cdr (magit-split-branch-name branch))))))) 1670 1671 (defun magit-set-upstream-branch (branch upstream) 1672 "Set UPSTREAM as the upstream of BRANCH. 1673 If UPSTREAM is nil, then unset BRANCH's upstream. 1674 Otherwise UPSTREAM has to be an existing branch." 1675 (if upstream 1676 (magit-call-git "branch" "--set-upstream-to" upstream branch) 1677 (magit-call-git "branch" "--unset-upstream" branch))) 1678 1679 (defun magit-get-upstream-ref (&optional branch) 1680 "Return the upstream branch of BRANCH as a fully qualified ref. 1681 It BRANCH is nil, then return the upstream of the current branch, 1682 if any, nil otherwise. If the upstream is not configured, the 1683 configured remote is an url, or the named branch does not exist, 1684 then return nil. I.e., return an existing local or 1685 remote-tracking branch ref." 1686 (and-let* ((branch (or branch (magit-get-current-branch)))) 1687 (magit-ref-fullname (concat branch "@{upstream}")))) 1688 1689 (defun magit-get-upstream-branch (&optional branch) 1690 "Return the name of the upstream branch of BRANCH. 1691 It BRANCH is nil, then return the upstream of the current branch 1692 if any, nil otherwise. If the upstream is not configured, the 1693 configured remote is an url, or the named branch does not exist, 1694 then return nil. I.e., return the name of an existing local or 1695 remote-tracking branch. The returned string is colorized 1696 according to the branch type." 1697 (magit--with-refresh-cache 1698 (list default-directory 'magit-get-upstream-branch branch) 1699 (and-let* ((branch (or branch (magit-get-current-branch))) 1700 (upstream (magit-ref-abbrev (concat branch "@{upstream}")))) 1701 (magit--propertize-face 1702 upstream (if (equal (magit-get "branch" branch "remote") ".") 1703 'magit-branch-local 1704 'magit-branch-remote))))) 1705 1706 (defun magit-get-indirect-upstream-branch (branch &optional force) 1707 (let ((remote (magit-get "branch" branch "remote"))) 1708 (and remote (not (equal remote ".")) 1709 ;; The user has opted in... 1710 (or force 1711 (--some (if (magit-git-success "check-ref-format" "--branch" it) 1712 (equal it branch) 1713 (string-match-p it branch)) 1714 magit-branch-prefer-remote-upstream)) 1715 ;; and local BRANCH tracks a remote branch... 1716 (let ((upstream (magit-get-upstream-branch branch))) 1717 ;; whose upstream... 1718 (and upstream 1719 ;; has the same name as BRANCH... 1720 (equal (substring upstream (1+ (length remote))) branch) 1721 ;; and can be fast-forwarded to BRANCH. 1722 (magit-rev-ancestor-p upstream branch) 1723 upstream))))) 1724 1725 (defun magit-get-upstream-remote (&optional branch allow-unnamed) 1726 (and-let* ((branch (or branch (magit-get-current-branch))) 1727 (remote (magit-get "branch" branch "remote"))) 1728 (and (not (equal remote ".")) 1729 (cond ((member remote (magit-list-remotes)) 1730 (magit--propertize-face remote 'magit-branch-remote)) 1731 ((and allow-unnamed 1732 (string-match-p "\\(\\`.\\{0,2\\}/\\|[:@]\\)" remote)) 1733 (magit--propertize-face remote 'bold)))))) 1734 1735 (defun magit-get-unnamed-upstream (&optional branch) 1736 (and-let* ((branch (or branch (magit-get-current-branch))) 1737 (remote (magit-get "branch" branch "remote")) 1738 (merge (magit-get "branch" branch "merge"))) 1739 (and (magit--unnamed-upstream-p remote merge) 1740 (list (magit--propertize-face remote 'bold) 1741 (magit--propertize-face merge 'magit-branch-remote))))) 1742 1743 (defun magit--unnamed-upstream-p (remote merge) 1744 (and remote (string-match-p "\\(\\`\\.\\{0,2\\}/\\|[:@]\\)" remote) 1745 merge (string-prefix-p "refs/" merge))) 1746 1747 (defun magit--valid-upstream-p (remote merge) 1748 (and (or (equal remote ".") 1749 (member remote (magit-list-remotes))) 1750 (string-prefix-p "refs/" merge))) 1751 1752 (defun magit-get-current-remote (&optional allow-unnamed) 1753 (or (magit-get-upstream-remote nil allow-unnamed) 1754 (and-let* ((remotes (magit-list-remotes)) 1755 (remote (if (length= remotes 1) 1756 (car remotes) 1757 (magit-primary-remote)))) 1758 (magit--propertize-face remote 'magit-branch-remote)))) 1759 1760 (defun magit-get-push-remote (&optional branch) 1761 (and-let* ((remote 1762 (or (and (or branch (setq branch (magit-get-current-branch))) 1763 (magit-get "branch" branch "pushRemote")) 1764 (magit-get "remote.pushDefault")))) 1765 (magit--propertize-face remote 'magit-branch-remote))) 1766 1767 (defun magit-get-push-branch (&optional branch verify) 1768 (magit--with-refresh-cache 1769 (list default-directory 'magit-get-push-branch branch verify) 1770 (and-let* ((branch (or branch (setq branch (magit-get-current-branch)))) 1771 (remote (magit-get-push-remote branch)) 1772 (target (concat remote "/" branch))) 1773 (and (or (not verify) 1774 (magit-rev-verify target)) 1775 (magit--propertize-face target 'magit-branch-remote))))) 1776 1777 (defun magit-get-@{push}-branch (&optional branch) 1778 (let ((ref (magit-rev-parse "--symbolic-full-name" 1779 (concat branch "@{push}")))) 1780 (and ref 1781 (string-prefix-p "refs/remotes/" ref) 1782 (substring ref 13)))) 1783 1784 (defun magit-get-remote (&optional branch) 1785 (and (or branch (setq branch (magit-get-current-branch))) 1786 (let ((remote (magit-get "branch" branch "remote"))) 1787 (and (not (equal remote ".")) 1788 remote)))) 1789 1790 (defun magit-get-some-remote (&optional branch) 1791 (or (magit-get-remote branch) 1792 (and-let* ((main (magit-main-branch))) 1793 (magit-get-remote main)) 1794 (magit-primary-remote) 1795 (car (magit-list-remotes)))) 1796 1797 (defvar magit-primary-remote-names 1798 '("upstream" "origin")) 1799 1800 (defun magit-primary-remote () 1801 "Return the primary remote. 1802 1803 The primary remote is the remote that tracks the repository that 1804 other repositories are forked from. It often is called \"origin\" 1805 but because many people name their own fork \"origin\", using that 1806 term would be ambiguous. Likewise we avoid the term \"upstream\" 1807 because a branch's @{upstream} branch may be a local branch or a 1808 branch from a remote other than the primary remote. 1809 1810 If a remote exists whose name matches `magit.primaryRemote', then 1811 that is considered the primary remote. If no remote by that name 1812 exists, then remotes in `magit-primary-remote-names' are tried in 1813 order and the first remote from that list that actually exists in 1814 the current repository is considered its primary remote." 1815 (let ((remotes (magit-list-remotes))) 1816 (seq-find (lambda (name) 1817 (member name remotes)) 1818 (delete-dups 1819 (delq nil 1820 (cons (magit-get "magit.primaryRemote") 1821 magit-primary-remote-names)))))) 1822 1823 (defun magit-branch-merged-p (branch &optional target) 1824 "Return non-nil if BRANCH is merged into its upstream and TARGET. 1825 1826 TARGET defaults to the current branch. If `HEAD' is detached and 1827 TARGET is nil, then always return nil. As a special case, if 1828 TARGET is t, then return non-nil if BRANCH is merged into any one 1829 of the other local branches. 1830 1831 If, and only if, BRANCH has an upstream, then only return non-nil 1832 if BRANCH is merged into both TARGET (as described above) as well 1833 as into its upstream." 1834 (and (if-let ((upstream (and (magit-branch-p branch) 1835 (magit-get-upstream-branch branch)))) 1836 (magit-rev-ancestor-p branch upstream) 1837 t) 1838 (if (eq target t) 1839 (delete (magit-name-local-branch branch) 1840 (magit-list-containing-branches branch)) 1841 (and-let* ((target (or target (magit-get-current-branch)))) 1842 (magit-rev-ancestor-p branch target))))) 1843 1844 (defun magit-get-tracked (refname) 1845 "Return the remote branch tracked by the remote-tracking branch REFNAME. 1846 The returned value has the form (REMOTE . REF), where REMOTE is 1847 the name of a remote and REF is the ref local to the remote." 1848 (and-let* ((ref (magit-ref-fullname refname))) 1849 (save-match-data 1850 (seq-some (lambda (line) 1851 (and (string-match "\ 1852 \\`remote\\.\\([^.]+\\)\\.fetch=\\+?\\([^:]+\\):\\(.+\\)" line) 1853 (let ((rmt (match-string 1 line)) 1854 (src (match-string 2 line)) 1855 (dst (match-string 3 line))) 1856 (and (string-match (format "\\`%s\\'" 1857 (string-replace 1858 "*" "\\(.+\\)" dst)) 1859 ref) 1860 (cons rmt (string-replace 1861 "*" (match-string 1 ref) src)))))) 1862 (magit-git-lines "config" "--local" "--list"))))) 1863 1864 (defun magit-split-branch-name (branch) 1865 (cond ((member branch (magit-list-local-branch-names)) 1866 (cons "." branch)) 1867 ((string-match "/" branch) 1868 (or (seq-some (lambda (remote) 1869 (and (string-match 1870 (format "\\`\\(%s\\)/\\(.+\\)\\'" remote) 1871 branch) 1872 (cons (match-string 1 branch) 1873 (match-string 2 branch)))) 1874 (magit-list-remotes)) 1875 (error "Invalid branch name %s" branch))))) 1876 1877 (defun magit-get-current-tag (&optional rev with-distance) 1878 "Return the closest tag reachable from REV. 1879 1880 If optional REV is nil, then default to `HEAD'. 1881 If optional WITH-DISTANCE is non-nil then return (TAG COMMITS), 1882 if it is `dirty' return (TAG COMMIT DIRTY). COMMITS is the number 1883 of commits in `HEAD' but not in TAG and DIRTY is t if there are 1884 uncommitted changes, nil otherwise." 1885 (and-let* ((str (magit-git-str "describe" "--long" "--tags" 1886 (and (eq with-distance 'dirty) "--dirty") 1887 rev))) 1888 (save-match-data 1889 (string-match 1890 "\\(.+\\)-\\(?:0[0-9]*\\|\\([0-9]+\\)\\)-g[0-9a-z]+\\(-dirty\\)?$" str) 1891 (if with-distance 1892 `(,(match-string 1 str) 1893 ,(string-to-number (or (match-string 2 str) "0")) 1894 ,@(and (match-string 3 str) (list t))) 1895 (match-string 1 str))))) 1896 1897 (defun magit-get-next-tag (&optional rev with-distance) 1898 "Return the closest tag from which REV is reachable. 1899 1900 If optional REV is nil, then default to `HEAD'. 1901 If no such tag can be found or if the distance is 0 (in which 1902 case it is the current tag, not the next), return nil instead. 1903 If optional WITH-DISTANCE is non-nil, then return (TAG COMMITS) 1904 where COMMITS is the number of commits in TAG but not in REV." 1905 (and-let* ((str (magit-git-str "describe" "--contains" (or rev "HEAD")))) 1906 (save-match-data 1907 (when (string-match "^[^^~]+" str) 1908 (setq str (match-string 0 str)) 1909 (unless (equal str (magit-get-current-tag rev)) 1910 (if with-distance 1911 (list str (car (magit-rev-diff-count str rev))) 1912 str)))))) 1913 1914 (defun magit-list-refs (&optional namespaces format sortby) 1915 "Return list of references, excluding symbolic references. 1916 1917 When NAMESPACES is non-nil, list refs from these namespaces 1918 rather than those from `magit-list-refs-namespaces'. 1919 1920 FORMAT is passed to the `--format' flag of `git for-each-ref' 1921 and defaults to \"%(refname)\". 1922 1923 SORTBY is a key or list of keys to pass to the `--sort' flag of 1924 `git for-each-ref'. When nil, use `magit-list-refs-sortby'" 1925 (unless format 1926 (setq format "%(refname)")) 1927 (seq-keep (lambda (line) 1928 (pcase-let* ((`(,symrefp ,value) 1929 (split-string line "")) 1930 (symrefp (not (equal symrefp "")))) 1931 (and (not symrefp) value))) 1932 (magit-git-lines "for-each-ref" 1933 (concat "--format=%(symref)" format) 1934 (--map (concat "--sort=" it) 1935 (pcase (or sortby magit-list-refs-sortby) 1936 ((and val (pred stringp)) (list val)) 1937 ((and val (pred listp)) val))) 1938 (or namespaces magit-list-refs-namespaces)))) 1939 1940 (defun magit-list-branches () 1941 (magit-list-refs (list "refs/heads" "refs/remotes"))) 1942 1943 (defun magit-list-local-branches () 1944 (magit-list-refs "refs/heads")) 1945 1946 (defun magit-list-remote-branches (&optional remote) 1947 (magit-list-refs (concat "refs/remotes/" remote))) 1948 1949 (defun magit-list-related-branches (relation &optional commit &rest args) 1950 (--remove (string-match-p "\\(\\`(HEAD\\|HEAD -> \\)" it) 1951 (--map (substring it 2) 1952 (magit-git-lines "branch" args relation commit)))) 1953 1954 (defun magit-list-containing-branches (&optional commit &rest args) 1955 (magit-list-related-branches "--contains" commit args)) 1956 1957 (defun magit-list-publishing-branches (&optional commit) 1958 (--filter (magit-rev-ancestor-p (or commit "HEAD") it) 1959 magit-published-branches)) 1960 1961 (defun magit-list-merged-branches (&optional commit &rest args) 1962 (magit-list-related-branches "--merged" commit args)) 1963 1964 (defun magit-list-unmerged-branches (&optional commit &rest args) 1965 (magit-list-related-branches "--no-merged" commit args)) 1966 1967 (defun magit-list-unmerged-to-upstream-branches () 1968 (--filter (and-let* ((upstream (magit-get-upstream-branch it))) 1969 (member it (magit-list-unmerged-branches upstream))) 1970 (magit-list-local-branch-names))) 1971 1972 (defun magit-list-branches-pointing-at (commit) 1973 (let ((re (format "\\`%s refs/\\(heads\\|remotes\\)/\\(.*\\)\\'" 1974 (magit-rev-verify commit)))) 1975 (--keep (and (string-match re it) 1976 (let ((name (match-string 2 it))) 1977 (and (not (string-suffix-p "HEAD" name)) 1978 name))) 1979 (magit-git-lines "show-ref")))) 1980 1981 (defun magit-list-refnames (&optional namespaces include-special) 1982 (nconc (magit-list-refs namespaces "%(refname:short)") 1983 (and include-special 1984 (magit-list-special-refnames)))) 1985 1986 (defvar magit-special-refnames 1987 '("HEAD" "ORIG_HEAD" "FETCH_HEAD" "MERGE_HEAD" "CHERRY_PICK_HEAD")) 1988 1989 (defun magit-list-special-refnames () 1990 (let ((gitdir (magit-gitdir))) 1991 (cl-mapcan (lambda (name) 1992 (and (file-exists-p (expand-file-name name gitdir)) 1993 (list name))) 1994 magit-special-refnames))) 1995 1996 (defun magit-list-branch-names () 1997 (magit-list-refnames (list "refs/heads" "refs/remotes"))) 1998 1999 (defun magit-list-local-branch-names () 2000 (magit-list-refnames "refs/heads")) 2001 2002 (defun magit-list-remote-branch-names (&optional remote relative) 2003 (if (and remote relative) 2004 (let ((regexp (format "^refs/remotes/%s/\\(.+\\)" remote))) 2005 (--mapcat (when (string-match regexp it) 2006 (list (match-string 1 it))) 2007 (magit-list-remote-branches remote))) 2008 (magit-list-refnames (concat "refs/remotes/" remote)))) 2009 2010 (defun magit-format-refs (format &rest args) 2011 (let ((lines (magit-git-lines 2012 "for-each-ref" (concat "--format=" format) 2013 (or args (list "refs/heads" "refs/remotes" "refs/tags"))))) 2014 (if (string-search "\f" format) 2015 (--map (split-string it "\f") lines) 2016 lines))) 2017 2018 (defun magit-list-remotes () 2019 (magit-git-lines "remote")) 2020 2021 (defun magit-list-tags () 2022 (magit-git-lines "tag")) 2023 2024 (defun magit-list-stashes (&optional format) 2025 (magit-git-lines "stash" "list" (concat "--format=" (or format "%gd")))) 2026 2027 (defun magit-list-active-notes-refs () 2028 "Return notes refs according to `core.notesRef' and `notes.displayRef'." 2029 (magit-git-lines "for-each-ref" "--format=%(refname)" 2030 (or (magit-get "core.notesRef") "refs/notes/commits") 2031 (magit-get-all "notes.displayRef"))) 2032 2033 (defun magit-list-notes-refnames () 2034 (--map (substring it 6) (magit-list-refnames "refs/notes"))) 2035 2036 (defun magit-remote-list-tags (remote) 2037 (--keep (and (not (string-suffix-p "^{}" it)) 2038 (substring it 51)) 2039 (magit-git-lines "ls-remote" "--tags" remote))) 2040 2041 (defun magit-remote-list-branches (remote) 2042 (--keep (and (not (string-suffix-p "^{}" it)) 2043 (substring it 52)) 2044 (magit-git-lines "ls-remote" "--heads" remote))) 2045 2046 (defun magit-remote-list-refs (remote) 2047 (--keep (and (not (string-suffix-p "^{}" it)) 2048 (substring it 41)) 2049 (magit-git-lines "ls-remote" remote))) 2050 2051 (defun magit-remote-head (remote) 2052 (and-let* ((line (cl-find-if 2053 (lambda (line) 2054 (string-match 2055 "\\`ref: refs/heads/\\([^\s\t]+\\)[\s\t]HEAD\\'" line)) 2056 (magit-git-lines "ls-remote" "--symref" remote "HEAD")))) 2057 (match-string 1 line))) 2058 2059 (defun magit-list-modified-modules () 2060 (--keep (and (string-match "\\`\\+\\([^ ]+\\) \\(.+\\) (.+)\\'" it) 2061 (match-string 2 it)) 2062 (magit-git-lines "submodule" "status"))) 2063 2064 (defun magit-list-module-paths () 2065 (magit-with-toplevel 2066 (--mapcat (and (string-match "^160000 [0-9a-z]\\{40,\\} 0\t\\(.+\\)$" it) 2067 (list (match-string 1 it))) 2068 (magit-git-items "ls-files" "-z" "--stage")))) 2069 2070 (defun magit-list-module-names () 2071 (mapcar #'magit-get-submodule-name (magit-list-module-paths))) 2072 2073 (defun magit-get-submodule-name (path) 2074 "Return the name of the submodule at PATH. 2075 PATH has to be relative to the super-repository." 2076 (if (magit-git-version>= "2.38.0") 2077 ;; "git submodule--helper name" was removed, 2078 ;; but might still come back in another form. 2079 (substring 2080 (car (split-string 2081 (car (or (magit-git-items 2082 "config" "-z" 2083 "-f" (expand-file-name ".gitmodules" (magit-toplevel)) 2084 "--get-regexp" "^submodule\\..*\\.path$" 2085 (concat "^" (regexp-quote (directory-file-name path)) "$")) 2086 (error "No such submodule `%s'" path))) 2087 "\n")) 2088 10 -5) 2089 (magit-git-string "submodule--helper" "name" path))) 2090 2091 (defun magit-list-worktrees () 2092 "Return list of the worktrees of this repository. 2093 2094 The returned list has the form (PATH COMMIT BRANCH BARE DETACHED 2095 LOCKED PRUNABLE). The last four elements are booleans, with the 2096 exception of LOCKED and PRUNABLE, which may also be strings. 2097 See git-worktree(1) manpage for the meaning of the various parts. 2098 2099 This function corrects a situation where \"git worktree list\" 2100 would claim a worktree is bare, even though the working tree is 2101 specified using `core.worktree'." 2102 (let ((remote (file-remote-p default-directory)) 2103 worktrees worktree) 2104 (dolist (line (let ((magit-git-global-arguments 2105 ;; KLUDGE At least in Git v2.8.3 this argument 2106 ;; would trigger a segfault. 2107 (remove "--no-pager" magit-git-global-arguments))) 2108 (if (magit-git-version>= "2.36") 2109 (magit-git-items "worktree" "list" "--porcelain" "-z") 2110 (magit-git-lines "worktree" "list" "--porcelain")))) 2111 (cond ((string-prefix-p "worktree" line) 2112 (let ((path (substring line 9))) 2113 (when remote 2114 (setq path (concat remote path))) 2115 ;; If the git directory is separate from the main 2116 ;; worktree, then "git worktree" returns the git 2117 ;; directory instead of the worktree, which isn't 2118 ;; what it is supposed to do and not what we want. 2119 ;; However, if the worktree has been removed, then 2120 ;; we want to return it anyway; instead of nil. 2121 (setq path (or (magit-toplevel path) path)) 2122 (setq worktree (list path nil nil nil nil nil nil)) 2123 (push worktree worktrees))) 2124 ((string-prefix-p "HEAD" line) 2125 (setf (nth 1 worktree) (substring line 5))) 2126 ((string-prefix-p "branch" line) 2127 (setf (nth 2 worktree) (substring line 18))) 2128 ((string-equal line "bare") 2129 (let* ((default-directory (car worktree)) 2130 (wt (and (not (magit-get-boolean "core.bare")) 2131 (magit-get "core.worktree")))) 2132 (if (and wt (file-exists-p (expand-file-name wt))) 2133 (progn (setf (nth 0 worktree) (expand-file-name wt)) 2134 (setf (nth 2 worktree) (magit-rev-parse "HEAD")) 2135 (setf (nth 3 worktree) (magit-get-current-branch))) 2136 (setf (nth 3 worktree) t)))) 2137 ((string-equal line "detached") 2138 (setf (nth 4 worktree) t)) 2139 ((string-prefix-p line "locked") 2140 (setf (nth 5 worktree) 2141 (if (> (length line) 6) (substring line 7) t))) 2142 ((string-prefix-p line "prunable") 2143 (setf (nth 6 worktree) 2144 (if (> (length line) 8) (substring line 9) t))))) 2145 (nreverse worktrees))) 2146 2147 (defun magit-symbolic-ref-p (name) 2148 (magit-git-success "symbolic-ref" "--quiet" name)) 2149 2150 (defun magit-ref-p (rev) 2151 (or (car (member rev (magit-list-refs "refs/"))) 2152 (car (member rev (magit-list-refnames "refs/"))))) 2153 2154 (defun magit-branch-p (rev) 2155 (or (car (member rev (magit-list-branches))) 2156 (car (member rev (magit-list-branch-names))))) 2157 2158 (defun magit-local-branch-p (rev) 2159 (or (car (member rev (magit-list-local-branches))) 2160 (car (member rev (magit-list-local-branch-names))))) 2161 2162 (defun magit-remote-branch-p (rev) 2163 (or (car (member rev (magit-list-remote-branches))) 2164 (car (member rev (magit-list-remote-branch-names))))) 2165 2166 (defun magit-branch-set-face (branch) 2167 (magit--propertize-face branch (if (magit-local-branch-p branch) 2168 'magit-branch-local 2169 'magit-branch-remote))) 2170 2171 (defun magit-tag-p (rev) 2172 (car (member rev (magit-list-tags)))) 2173 2174 (defun magit-remote-p (string) 2175 (car (member string (magit-list-remotes)))) 2176 2177 (defvar magit-main-branch-names 2178 '("main" "master" "trunk" "development") 2179 "Branch names reserved for use by the primary branch. 2180 Use function `magit-main-branch' to get the name actually used in 2181 the current repository.") 2182 2183 (defvar magit-long-lived-branches 2184 (append magit-main-branch-names (list "maint" "next")) 2185 "Branch names reserved for use by long lived branches.") 2186 2187 (defun magit-main-branch () 2188 "Return the main branch. 2189 2190 If a branch exists whose name matches `init.defaultBranch', then 2191 that is considered the main branch. If no branch by that name 2192 exists, then the branch names in `magit-main-branch-names' are 2193 tried in order. The first branch from that list that actually 2194 exists in the current repository is considered its main branch." 2195 (let ((branches (magit-list-local-branch-names))) 2196 (seq-find (lambda (name) 2197 (member name branches)) 2198 (delete-dups 2199 (delq nil 2200 (cons (magit-get "init.defaultBranch") 2201 magit-main-branch-names)))))) 2202 2203 (defun magit-rev-diff-count (a b &optional first-parent) 2204 "Return the commits in A but not B and vice versa. 2205 Return a list of two integers: (A>B B>A). 2206 2207 If `first-parent' is set, traverse only first parents." 2208 (mapcar #'string-to-number 2209 (split-string (magit-git-string "rev-list" 2210 "--count" "--left-right" 2211 (and first-parent "--first-parent") 2212 (concat a "..." b)) 2213 "\t"))) 2214 2215 (defun magit-abbrev-length () 2216 (let ((abbrev (magit-get "core.abbrev"))) 2217 (if (and abbrev (not (equal abbrev "auto"))) 2218 (string-to-number abbrev) 2219 ;; Guess the length git will be using based on an example 2220 ;; abbreviation. Actually HEAD's abbreviation might be an 2221 ;; outlier, so use the shorter of the abbreviations for two 2222 ;; commits. See #3034. 2223 (if-let ((head (magit-rev-parse "--short" "HEAD")) 2224 (head-len (length head))) 2225 (min head-len 2226 (if-let ((rev (magit-rev-parse "--short" "HEAD~"))) 2227 (length rev) 2228 head-len)) 2229 ;; We're on an unborn branch, but perhaps the repository has 2230 ;; other commits. See #4123. 2231 (if-let ((commits (magit-git-lines "rev-list" "-n2" "--all" 2232 "--abbrev-commit"))) 2233 (apply #'min (mapcar #'length commits)) 2234 ;; A commit does not exist. Fall back to the default of 7. 2235 7))))) 2236 2237 (defun magit-abbrev-arg (&optional arg) 2238 (format "--%s=%d" (or arg "abbrev") (magit-abbrev-length))) 2239 2240 (defun magit-rev-abbrev (rev) 2241 (magit-rev-parse (magit-abbrev-arg "short") rev)) 2242 2243 (defun magit-commit-children (commit &optional args) 2244 (mapcar #'car 2245 (--filter (member commit (cdr it)) 2246 (--map (split-string it " ") 2247 (magit-git-lines 2248 "log" "--format=%H %P" 2249 (or args (list "--branches" "--tags" "--remotes")) 2250 "--not" commit))))) 2251 2252 (defun magit-commit-parents (commit) 2253 (and-let* ((str (magit-git-string "rev-list" "-1" "--parents" commit))) 2254 (cdr (split-string str)))) 2255 2256 (defun magit-patch-id (rev) 2257 (magit--with-connection-local-variables 2258 (magit--with-temp-process-buffer 2259 (magit-process-file 2260 shell-file-name nil '(t nil) nil shell-command-switch 2261 (let ((exec (shell-quote-argument (magit-git-executable)))) 2262 (format "%s diff-tree -u %s | %s patch-id" exec rev exec))) 2263 (car (split-string (buffer-string)))))) 2264 2265 (defun magit-rev-format (format &optional rev args) 2266 ;; Prefer `git log --no-walk' to `git show --no-patch' because it 2267 ;; performs better in some scenarios. 2268 (let ((str (magit-git-string "log" "--no-walk" 2269 (concat "--format=" format) args 2270 (if rev (magit--rev-dereference rev) "HEAD") 2271 "--"))) 2272 (and (not (string-equal str "")) 2273 str))) 2274 2275 (defun magit-rev-insert-format (format &optional rev args) 2276 ;; Prefer `git log --no-walk' to `git show --no-patch' because it 2277 ;; performs better in some scenarios. 2278 (magit-git-insert "log" "--no-walk" 2279 (concat "--format=" format) args 2280 (if rev (magit--rev-dereference rev) "HEAD") 2281 "--")) 2282 2283 (defun magit-format-rev-summary (rev) 2284 (and-let* ((str (magit-rev-format "%h %s" rev))) 2285 (progn 2286 (magit--put-face 0 (string-match " " str) 'magit-hash str) 2287 str))) 2288 2289 (defvar magit-ref-namespaces 2290 '(("\\`HEAD\\'" . magit-head) 2291 ("\\`refs/tags/\\(.+\\)" . magit-tag) 2292 ("\\`refs/heads/\\(.+\\)" . magit-branch-local) 2293 ("\\`refs/remotes/\\(.+\\)" . magit-branch-remote) 2294 ("\\`refs/bisect/\\(bad\\)" . magit-bisect-bad) 2295 ("\\`refs/bisect/\\(skip.*\\)" . magit-bisect-skip) 2296 ("\\`refs/bisect/\\(good.*\\)" . magit-bisect-good) 2297 ("\\`refs/stash$" . magit-refname-stash) 2298 ("\\`refs/wip/\\(.+\\)" . magit-refname-wip) 2299 ("\\`refs/pullreqs/\\(.+\\)" . magit-refname-pullreq) 2300 ("\\`\\(bad\\):" . magit-bisect-bad) 2301 ("\\`\\(skip\\):" . magit-bisect-skip) 2302 ("\\`\\(good\\):" . magit-bisect-good) 2303 ("\\`\\(.+\\)" . magit-refname)) 2304 "How refs are formatted for display. 2305 2306 Each entry controls how a certain type of ref is displayed, and 2307 has the form (REGEXP . FACE). REGEXP is a regular expression 2308 used to match full refs. The first entry whose REGEXP matches 2309 the reference is used. 2310 2311 In log and revision buffers the first regexp submatch becomes the 2312 \"label\" that represents the ref and is propertized with FONT. 2313 In refs buffers the displayed text is controlled by other means 2314 and this option only controls what face is used.") 2315 2316 (defun magit-format-ref-labels (string) 2317 (save-match-data 2318 (let ((regexp "\\(, \\|tag: \\|HEAD -> \\)") 2319 names) 2320 (if (and (derived-mode-p 'magit-log-mode) 2321 (member "--simplify-by-decoration" magit-buffer-log-args)) 2322 (let ((branches (magit-list-local-branch-names)) 2323 (re (format "^%s/.+" (regexp-opt (magit-list-remotes))))) 2324 (setq names 2325 (--map (cond ((string-equal it "HEAD") it) 2326 ((string-prefix-p "refs/" it) it) 2327 ((member it branches) (concat "refs/heads/" it)) 2328 ((string-match re it) (concat "refs/remotes/" it)) 2329 (t (concat "refs/" it))) 2330 (split-string 2331 (string-replace "tag: " "refs/tags/" string) 2332 regexp t)))) 2333 (setq names (split-string string regexp t))) 2334 (let (state head upstream tags branches remotes other combined) 2335 (dolist (ref names) 2336 (let* ((face (cdr (--first (string-match (car it) ref) 2337 magit-ref-namespaces))) 2338 (name (magit--propertize-face 2339 (or (match-string 1 ref) ref) face))) 2340 (cl-case face 2341 ((magit-bisect-bad magit-bisect-skip magit-bisect-good) 2342 (setq state name)) 2343 (magit-head 2344 (setq head (magit--propertize-face "@" 'magit-head))) 2345 (magit-tag (push name tags)) 2346 (magit-branch-local (push name branches)) 2347 (magit-branch-remote (push name remotes)) 2348 (t (push name other))))) 2349 (setq remotes 2350 (seq-keep 2351 (lambda (name) 2352 (if (string-match "\\`\\([^/]*\\)/\\(.*\\)\\'" name) 2353 (let ((r (match-string 1 name)) 2354 (b (match-string 2 name))) 2355 (and (not (equal b "HEAD")) 2356 (if (equal (concat "refs/remotes/" name) 2357 (magit-git-string 2358 "symbolic-ref" 2359 (format "refs/remotes/%s/HEAD" r))) 2360 (magit--propertize-face 2361 name 'magit-branch-remote-head) 2362 name))) 2363 name)) 2364 remotes)) 2365 (let* ((current (magit-get-current-branch)) 2366 (target (magit-get-upstream-branch current))) 2367 (dolist (name branches) 2368 (let ((push (car (member (magit-get-push-branch name) remotes)))) 2369 (when push 2370 (setq remotes (delete push remotes)) 2371 (string-match "^[^/]*/" push) 2372 (setq push (substring push 0 (match-end 0)))) 2373 (cond 2374 ((equal name current) 2375 (setq head 2376 (concat push 2377 (magit--propertize-face 2378 name 'magit-branch-current)))) 2379 ((equal name target) 2380 (setq upstream 2381 (concat push 2382 (magit--propertize-face 2383 name '(magit-branch-upstream 2384 magit-branch-local))))) 2385 (t 2386 (push (concat push name) combined))))) 2387 (when (and target (not upstream)) 2388 (if (member target remotes) 2389 (progn 2390 (magit--add-face-text-property 2391 0 (length target) 'magit-branch-upstream nil target) 2392 (setq upstream target) 2393 (setq remotes (delete target remotes))) 2394 (when-let ((target (car (member target combined)))) 2395 (magit--add-face-text-property 2396 0 (length target) 'magit-branch-upstream nil target) 2397 (setq upstream target) 2398 (setq combined (delete target combined)))))) 2399 (string-join (flatten-tree `(,state 2400 ,head 2401 ,upstream 2402 ,@(nreverse tags) 2403 ,@(nreverse combined) 2404 ,@(nreverse remotes) 2405 ,@other)) 2406 " "))))) 2407 2408 (defun magit-object-type (object) 2409 (magit-git-string "cat-file" "-t" object)) 2410 2411 (defmacro magit-with-blob (commit file &rest body) 2412 (declare (indent 2) 2413 (debug (form form body))) 2414 `(magit--with-temp-process-buffer 2415 (let ((buffer-file-name ,file)) 2416 (save-excursion 2417 (magit-git-insert "cat-file" "-p" 2418 (concat ,commit ":" buffer-file-name))) 2419 (decode-coding-inserted-region 2420 (point-min) (point-max) buffer-file-name t nil nil t) 2421 ,@body))) 2422 2423 (defvar magit-tramp-process-environment nil) 2424 2425 (defmacro magit-with-temp-index (tree arg &rest body) 2426 (declare (indent 2) (debug (form form body))) 2427 (let ((file (cl-gensym "file"))) 2428 `(let ((magit--refresh-cache nil) 2429 (,file (magit-convert-filename-for-git 2430 (make-temp-name 2431 (expand-file-name "index.magit." (magit-gitdir)))))) 2432 (unwind-protect 2433 (magit-with-toplevel 2434 (when-let ((tree ,tree)) 2435 (unless (magit-git-success "read-tree" ,arg tree 2436 (concat "--index-output=" ,file)) 2437 (error "Cannot read tree %s" tree))) 2438 (if (file-remote-p default-directory) 2439 (let ((magit-tramp-process-environment 2440 (cons (concat "GIT_INDEX_FILE=" ,file) 2441 magit-tramp-process-environment))) 2442 ,@body) 2443 (with-environment-variables (("GIT_INDEX_FILE" ,file)) 2444 ,@body))) 2445 (ignore-errors 2446 (delete-file (concat (file-remote-p default-directory) ,file))))))) 2447 2448 (defun magit-commit-tree (message &optional tree &rest parents) 2449 (magit-git-string "commit-tree" "--no-gpg-sign" "-m" message 2450 (--mapcat (list "-p" it) (delq nil parents)) 2451 (or tree 2452 (magit-git-string "write-tree") 2453 (error "Cannot write tree")))) 2454 2455 (defun magit-commit-worktree (message &optional arg &rest other-parents) 2456 (magit-with-temp-index "HEAD" arg 2457 (and (magit-update-files (magit-unstaged-files)) 2458 (apply #'magit-commit-tree message nil "HEAD" other-parents)))) 2459 2460 (defun magit-update-files (files) 2461 (magit-git-success "update-index" "--add" "--remove" "--" files)) 2462 2463 (defun magit-update-ref (ref message rev &optional stashish) 2464 (let ((magit--refresh-cache nil)) 2465 (or (if (magit-git-version>= "2.6.0") 2466 (zerop (magit-call-git "update-ref" "--create-reflog" 2467 "-m" message ref rev 2468 (or (magit-rev-verify ref) ""))) 2469 ;; `--create-reflog' didn't exist before v2.6.0 2470 (let ((oldrev (magit-rev-verify ref)) 2471 (logfile (expand-file-name (concat "logs/" ref) 2472 (magit-gitdir)))) 2473 (unless (file-exists-p logfile) 2474 (when oldrev 2475 (magit-git-success "update-ref" "-d" ref oldrev)) 2476 (make-directory (file-name-directory logfile) t) 2477 (with-temp-file logfile) 2478 (when (and oldrev (not stashish)) 2479 (magit-git-success "update-ref" "-m" "enable reflog" 2480 ref oldrev "")))) 2481 (magit-git-success "update-ref" "-m" message ref rev 2482 (or (magit-rev-verify ref) ""))) 2483 (error "Cannot update %s with %s" ref rev)))) 2484 2485 (defconst magit-range-re 2486 (concat "\\`\\([^ \t]*[^.]\\)?" ; revA 2487 "\\(\\.\\.\\.?\\)" ; range marker 2488 "\\([^.][^ \t]*\\)?\\'")) ; revB 2489 2490 (defun magit-split-range (range) 2491 (pcase-let ((`(,beg ,end ,sep) (magit--split-range-raw range))) 2492 (and sep 2493 (let ((beg (or beg "HEAD")) 2494 (end (or end "HEAD"))) 2495 (if (string-equal (match-string 2 range) "...") 2496 (and-let* ((base (magit-git-string "merge-base" beg end))) 2497 (cons base end)) 2498 (cons beg end)))))) 2499 2500 (defun magit--split-range-raw (range) 2501 (and (string-match magit-range-re range) 2502 (let ((beg (match-string 1 range)) 2503 (end (match-string 3 range))) 2504 (and (or beg end) 2505 (list beg end (match-string 2 range)))))) 2506 2507 (defun magit-hash-range (range) 2508 (if (string-match magit-range-re range) 2509 (let ((beg (match-string 1 range)) 2510 (end (match-string 3 range))) 2511 (and (or beg end) 2512 (let ((beg-hash (and beg (magit-rev-hash (match-string 1 range)))) 2513 (end-hash (and end (magit-rev-hash (match-string 3 range))))) 2514 (and (or (not beg) beg-hash) 2515 (or (not end) end-hash) 2516 (concat beg-hash (match-string 2 range) end-hash))))) 2517 (magit-rev-hash range))) 2518 2519 (defvar magit-revision-faces 2520 '(magit-hash 2521 magit-tag 2522 magit-branch-remote 2523 magit-branch-remote-head 2524 magit-branch-local 2525 magit-branch-current 2526 magit-branch-upstream 2527 magit-branch-warning 2528 magit-head 2529 magit-refname 2530 magit-refname-stash 2531 magit-refname-wip 2532 magit-refname-pullreq)) 2533 2534 (put 'git-revision 'thing-at-point #'magit-thingatpt--git-revision) 2535 (defun magit-thingatpt--git-revision (&optional disallow) 2536 ;; Support hashes and references. 2537 (and-let* ((bounds 2538 (let ((c (concat "\s\n\t~^:?*[\\" disallow))) 2539 (cl-letf 2540 (((get 'git-revision 'beginning-op) 2541 (lambda () 2542 (if (re-search-backward (format "[%s]" c) nil t) 2543 (forward-char) 2544 (goto-char (point-min))))) 2545 ((get 'git-revision 'end-op) 2546 (lambda () 2547 (re-search-forward (format "\\=[^%s]*" c) nil t)))) 2548 (bounds-of-thing-at-point 'git-revision)))) 2549 (string (buffer-substring-no-properties (car bounds) (cdr bounds))) 2550 ;; References are allowed to contain most parentheses and 2551 ;; most punctuation, but if those characters appear at the 2552 ;; edges of a possible reference in arbitrary text, then 2553 ;; they are much more likely to be intended as just that: 2554 ;; punctuation and delimiters. 2555 (string (thread-first string 2556 (string-trim-left "[(</]") 2557 (string-trim-right "[])>/.,;!]")))) 2558 (let (disallow) 2559 (when (or (string-match-p "\\.\\." string) 2560 (string-match-p "/\\." string)) 2561 (setq disallow (concat disallow "."))) 2562 (when (string-match-p "@{" string) 2563 (setq disallow (concat disallow "@{"))) 2564 (if disallow 2565 ;; These additional restrictions overcompensate, 2566 ;; but that only matters in rare cases. 2567 (magit-thingatpt--git-revision disallow) 2568 (and (not (equal string "@")) 2569 (or (and (>= (length string) 7) 2570 (string-match-p "[a-z]" string) 2571 (magit-commit-p string)) 2572 (and (magit-ref-p string) 2573 (let ((face (get-text-property (point) 'face))) 2574 (or (not face) 2575 (member face magit-revision-faces))))) 2576 string))))) 2577 2578 (put 'git-revision-range 'thing-at-point #'magit-thingatpt--git-revision-range) 2579 (defun magit-thingatpt--git-revision-range () 2580 ;; Support hashes but no references. 2581 (and-let* ((bounds 2582 (cl-letf (((get 'git-revision 'beginning-op) 2583 (lambda () 2584 (if (re-search-backward "[^a-z0-9.]" nil t) 2585 (forward-char) 2586 (goto-char (point-min))))) 2587 ((get 'git-revision 'end-op) 2588 (lambda () 2589 (and (re-search-forward "[^a-z0-9.]" nil t) 2590 (backward-char))))) 2591 (bounds-of-thing-at-point 'git-revision))) 2592 (range (buffer-substring-no-properties (car bounds) (cdr bounds)))) 2593 ;; Validate but return as-is. 2594 (and (magit-hash-range range) range))) 2595 2596 ;;; Completion 2597 2598 (defvar magit-revision-history nil) 2599 2600 (defun magit--minibuf-default-add-commit () 2601 (let ((fn minibuffer-default-add-function)) 2602 (lambda () 2603 (if-let ((commit (with-selected-window (minibuffer-selected-window) 2604 (or (magit-thing-at-point 'git-revision-range t) 2605 (magit-commit-at-point))))) 2606 (let ((rest (cons commit (delete commit (funcall fn)))) 2607 (def minibuffer-default)) 2608 (if (listp def) 2609 (append def rest) 2610 (cons def (delete def rest)))) 2611 (funcall fn))))) 2612 2613 (defun magit-read-branch (prompt &optional secondary-default) 2614 (magit-completing-read prompt (magit-list-branch-names) 2615 nil t nil 'magit-revision-history 2616 (or (magit-branch-at-point) 2617 secondary-default 2618 (magit-get-current-branch)))) 2619 2620 (defun magit-read-branch-or-commit (prompt &optional secondary-default) 2621 (let ((minibuffer-default-add-function (magit--minibuf-default-add-commit))) 2622 (or (magit-completing-read prompt (magit-list-refnames nil t) 2623 nil nil nil 'magit-revision-history 2624 (or (magit-branch-or-commit-at-point) 2625 secondary-default 2626 (magit-get-current-branch))) 2627 (user-error "Nothing selected")))) 2628 2629 (defun magit-read-range-or-commit (prompt &optional secondary-default) 2630 (magit-read-range 2631 prompt 2632 (or (and-let* ((revs (magit-region-values '(commit branch) t))) 2633 (progn 2634 (deactivate-mark) 2635 (concat (car (last revs)) ".." (car revs)))) 2636 (magit-branch-or-commit-at-point) 2637 secondary-default 2638 (magit-get-current-branch)))) 2639 2640 (defun magit-read-range (prompt &optional default) 2641 (let ((minibuffer-default-add-function (magit--minibuf-default-add-commit)) 2642 (crm-separator "\\.\\.\\.?")) 2643 (magit-completing-read-multiple 2644 (concat prompt ": ") 2645 (magit-list-refnames) 2646 nil nil nil 'magit-revision-history default nil t))) 2647 2648 (defun magit-read-remote-branch 2649 (prompt &optional remote default local-branch require-match) 2650 (let ((choice (magit-completing-read 2651 prompt 2652 (cl-union (and local-branch 2653 (if remote 2654 (concat remote "/" local-branch) 2655 (--map (concat it "/" local-branch) 2656 (magit-list-remotes)))) 2657 (magit-list-remote-branch-names remote t) 2658 :test #'equal) 2659 nil require-match nil 'magit-revision-history default))) 2660 (if (or remote (string-match "\\`\\([^/]+\\)/\\(.+\\)" choice)) 2661 choice 2662 (user-error "`%s' doesn't have the form REMOTE/BRANCH" choice)))) 2663 2664 (defun magit-read-refspec (prompt remote) 2665 (magit-completing-read prompt 2666 (prog2 (message "Determining available refs...") 2667 (magit-remote-list-refs remote) 2668 (message "Determining available refs...done")))) 2669 2670 (defun magit-read-local-branch (prompt &optional secondary-default) 2671 (magit-completing-read prompt (magit-list-local-branch-names) 2672 nil t nil 'magit-revision-history 2673 (or (magit-local-branch-at-point) 2674 secondary-default 2675 (magit-get-current-branch)))) 2676 2677 (defun magit-read-local-branch-or-commit (prompt) 2678 (let ((minibuffer-default-add-function (magit--minibuf-default-add-commit)) 2679 (choices (nconc (magit-list-local-branch-names) 2680 (magit-list-special-refnames))) 2681 (commit (magit-commit-at-point))) 2682 (when commit 2683 (push commit choices)) 2684 (or (magit-completing-read prompt choices 2685 nil nil nil 'magit-revision-history 2686 (or (magit-local-branch-at-point) commit)) 2687 (user-error "Nothing selected")))) 2688 2689 (defun magit-read-local-branch-or-ref (prompt &optional secondary-default) 2690 (magit-completing-read prompt (nconc (magit-list-local-branch-names) 2691 (magit-list-refs "refs/")) 2692 nil t nil 'magit-revision-history 2693 (or (magit-local-branch-at-point) 2694 secondary-default 2695 (magit-get-current-branch)))) 2696 2697 (defun magit-read-other-branch 2698 (prompt &optional exclude secondary-default no-require-match) 2699 (let* ((current (magit-get-current-branch)) 2700 (atpoint (magit-branch-at-point)) 2701 (exclude (or exclude current)) 2702 (default (or (and (not (equal atpoint exclude)) atpoint) 2703 (and (not (equal current exclude)) current) 2704 secondary-default 2705 (magit-get-previous-branch)))) 2706 (magit-completing-read prompt (delete exclude (magit-list-branch-names)) 2707 nil (not no-require-match) 2708 nil 'magit-revision-history default))) 2709 2710 (defun magit-read-other-branch-or-commit 2711 (prompt &optional exclude secondary-default) 2712 (let* ((minibuffer-default-add-function (magit--minibuf-default-add-commit)) 2713 (current (magit-get-current-branch)) 2714 (atpoint (magit-branch-or-commit-at-point)) 2715 (exclude (or exclude current)) 2716 (default (or (and (not (equal atpoint exclude)) 2717 (not (and (not current) 2718 (magit-rev-equal atpoint "HEAD"))) 2719 atpoint) 2720 (and (not (equal current exclude)) current) 2721 secondary-default 2722 (magit-get-previous-branch)))) 2723 (or (magit-completing-read prompt (delete exclude (magit-list-refnames)) 2724 nil nil nil 'magit-revision-history default) 2725 (user-error "Nothing selected")))) 2726 2727 (defun magit-read-other-local-branch 2728 (prompt &optional exclude secondary-default no-require-match) 2729 (let* ((current (magit-get-current-branch)) 2730 (atpoint (magit-local-branch-at-point)) 2731 (exclude (or exclude current)) 2732 (default (or (and (not (equal atpoint exclude)) atpoint) 2733 (and (not (equal current exclude)) current) 2734 secondary-default 2735 (magit-get-previous-branch)))) 2736 (magit-completing-read prompt 2737 (delete exclude (magit-list-local-branch-names)) 2738 nil (not no-require-match) 2739 nil 'magit-revision-history default))) 2740 2741 (defun magit-read-branch-prefer-other (prompt) 2742 (let* ((current (magit-get-current-branch)) 2743 (commit (magit-commit-at-point)) 2744 (atrev (and commit (magit-list-branches-pointing-at commit))) 2745 (atpoint (magit--painted-branch-at-point))) 2746 (magit-completing-read prompt (magit-list-branch-names) 2747 nil t nil 'magit-revision-history 2748 (or (magit-section-value-if 'branch) 2749 atpoint 2750 (and (not (cdr atrev)) (car atrev)) 2751 (--first (not (equal it current)) atrev) 2752 (magit-get-previous-branch) 2753 (car atrev))))) 2754 2755 (defun magit-read-upstream-branch (&optional branch prompt) 2756 "Read the upstream for BRANCH using PROMPT. 2757 If optional BRANCH is nil, then read the upstream for the 2758 current branch, or raise an error if no branch is checked 2759 out. Only existing branches can be selected." 2760 (unless branch 2761 (setq branch (or (magit-get-current-branch) 2762 (error "Need a branch to set its upstream")))) 2763 (let ((branches (delete branch (magit-list-branch-names)))) 2764 (magit-completing-read 2765 (or prompt (format "Change upstream of %s to" branch)) 2766 branches nil t nil 'magit-revision-history 2767 (or (let ((r (car (member (magit-remote-branch-at-point) branches))) 2768 (l (car (member (magit-local-branch-at-point) branches)))) 2769 (if magit-prefer-remote-upstream (or r l) (or l r))) 2770 (and-let* ((main (magit-main-branch))) 2771 (let ((r (car (member (concat "origin/" main) branches))) 2772 (l (car (member main branches)))) 2773 (if magit-prefer-remote-upstream (or r l) (or l r)))) 2774 (car (member (magit-get-previous-branch) branches)))))) 2775 2776 (defun magit-read-starting-point (prompt &optional branch default) 2777 (or (magit-completing-read 2778 (concat prompt 2779 (and branch 2780 (if (bound-and-true-p ivy-mode) 2781 ;; Ivy-mode strips faces from prompt. 2782 (format " `%s'" branch) 2783 (concat " " (magit--propertize-face 2784 branch 'magit-branch-local)))) 2785 " starting at") 2786 (nconc (list "HEAD") 2787 (magit-list-refnames) 2788 (directory-files (magit-gitdir) nil "_HEAD\\'")) 2789 nil nil nil 'magit-revision-history 2790 (or default (magit--default-starting-point))) 2791 (user-error "Nothing selected"))) 2792 2793 (defun magit--default-starting-point () 2794 (or (let ((r (magit-remote-branch-at-point)) 2795 (l (magit-local-branch-at-point))) 2796 (if magit-prefer-remote-upstream (or r l) (or l r))) 2797 (magit-commit-at-point) 2798 (magit-stash-at-point) 2799 (magit-get-current-branch))) 2800 2801 (defun magit-read-tag (prompt &optional require-match) 2802 (magit-completing-read prompt (magit-list-tags) nil 2803 require-match nil 'magit-revision-history 2804 (magit-tag-at-point))) 2805 2806 (defun magit-read-stash (prompt) 2807 (let* ((atpoint (magit-stash-at-point)) 2808 (default (and atpoint 2809 (concat atpoint (magit-rev-format " %s" atpoint)))) 2810 (choices (mapcar (lambda (c) 2811 (pcase-let ((`(,rev ,msg) (split-string c "\0"))) 2812 (concat (propertize rev 'face 'magit-hash) 2813 " " msg))) 2814 (magit-list-stashes "%gd%x00%s"))) 2815 (choice (magit-completing-read prompt choices 2816 nil t nil nil 2817 default 2818 (car choices)))) 2819 (and choice 2820 (string-match "^\\([^ ]+\\) \\(.+\\)" choice) 2821 (substring-no-properties (match-string 1 choice))))) 2822 2823 (defun magit-read-remote (prompt &optional default use-only) 2824 (let ((remotes (magit-list-remotes))) 2825 (if (and use-only (length= remotes 1)) 2826 (car remotes) 2827 (magit-completing-read prompt remotes 2828 nil t nil nil 2829 (or default 2830 (magit-remote-at-point) 2831 (magit-get-remote)))))) 2832 2833 (defun magit-read-remote-or-url (prompt &optional default) 2834 (magit-completing-read prompt 2835 (nconc (magit-list-remotes) 2836 (list "https://" "git://" "git@")) 2837 nil nil nil nil 2838 (or default 2839 (magit-remote-at-point) 2840 (magit-get-remote)))) 2841 2842 (defun magit-read-module-path (prompt &optional predicate) 2843 (magit-completing-read prompt (magit-list-module-paths) 2844 predicate t nil nil 2845 (magit-module-at-point predicate))) 2846 2847 (defun magit-module-confirm (verb &optional predicate) 2848 ;; Some predicates use the inefficient `magit-toplevel' 2849 ;; and some repositories have thousands of submodules. 2850 (let ((magit--refresh-cache (list (cons 0 0))) 2851 (modules nil)) 2852 (if current-prefix-arg 2853 (progn 2854 (setq modules (magit-list-module-paths)) 2855 (when predicate 2856 (setq modules (seq-filter predicate modules))) 2857 (unless modules 2858 (if predicate 2859 (user-error "No modules satisfying %s available" predicate) 2860 (user-error "No modules available")))) 2861 (setq modules (magit-region-values 'module)) 2862 (when modules 2863 (when predicate 2864 (setq modules (seq-filter predicate modules))) 2865 (unless modules 2866 (user-error "No modules satisfying %s selected" predicate)))) 2867 (if (or (length> modules 1) current-prefix-arg) 2868 (magit-confirm t nil (format "%s %%d modules" verb) nil modules) 2869 (list (magit-read-module-path (format "%s module" verb) predicate))))) 2870 2871 ;;; _ 2872 (provide 'magit-git) 2873 ;;; magit-git.el ends here