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