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