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