magit-process.el 46.3 KB
Newer Older
1
;;; magit-process.el --- process functionality  -*- lexical-binding: t -*-
2

Jonas Bernoulli's avatar
Jonas Bernoulli committed
3
;; Copyright (C) 2010-2018  The Magit Project Contributors
4
;;
5 6
;; You should have received a copy of the AUTHORS.md file which
;; lists all contributors.  If not, see http://magit.vc/authors.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

;; Author: Jonas Bernoulli <jonas@bernoul.li>
;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>

;; Magit is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; Magit is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
;; License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with Magit.  If not, see http://www.gnu.org/licenses.

24 25 26 27 28 29 30 31
;;; Commentary:

;; This library implements the tools used to run Git for side-effects.

;; Note that the functions used to run Git and then consume its
;; output, are defined in `magit-git.el'.  There's a bit of overlap
;; though.

32 33
;;; Code:

34
(require 'ansi-color)
35 36 37
(require 'cl-lib)
(require 'dash)

38
(require 'with-editor)
39 40 41 42 43
(require 'magit-utils)
(require 'magit-section)
(require 'magit-git)
(require 'magit-mode)

44 45
(declare-function auth-source-search "auth-source"
                  (&rest spec &key max require create delete &allow-other-keys))
Jonas Bernoulli's avatar
Jonas Bernoulli committed
46

47 48 49 50 51 52 53 54 55 56 57
;;; Options

(defcustom magit-process-connection-type (not (eq system-type 'cygwin))
  "Connection type used for the Git process.

If nil, use pipes: this is usually more efficient, and works on Cygwin.
If t, use ptys: this enables Magit to prompt for passphrases when needed."
  :group 'magit-process
  :type '(choice (const :tag "pipe" nil)
                 (const :tag "pty" t)))

58
(defcustom magit-need-cygwin-noglob
59 60 61 62 63 64 65 66 67 68 69 70
  (and (eq system-type 'windows-nt)
       (with-temp-buffer
         (let ((process-environment
                (append magit-git-environment process-environment)))
           (condition-case e
               (process-file magit-git-executable
                             nil (current-buffer) nil
                             "-c" "alias.echo=!echo" "echo" "x{0}")
             (file-error
              (lwarn 'magit-process :warning
                     "Could not run Git: %S" e))))
         (equal "x0\n" (buffer-string))))
71 72 73 74 75 76 77 78 79 80 81
  "Whether to use a workaround for Cygwin's globbing behavior.

If non-nil, add environment variables to `process-environment' to
prevent the git.exe distributed by Cygwin and MSYS2 from
attempting to perform glob expansion when called from a native
Windows build of Emacs.  See #2246."
  :package-version '(magit . "2.3.0")
  :group 'magit-process
  :type '(choice (const :tag "Yes" t)
                 (const :tag "No" nil)))

82 83 84 85 86 87 88 89 90 91 92
(defcustom magit-process-popup-time -1
  "Popup the process buffer if a command takes longer than this many seconds."
  :group 'magit-process
  :type '(choice (const :tag "Never" -1)
                 (const :tag "Immediately" 0)
                 (integer :tag "After this many seconds")))

(defcustom magit-process-log-max 32
  "Maximum number of sections to keep in a process log buffer.
When adding a new section would go beyond the limit set here,
then the older half of the sections are remove.  Sections that
93 94
belong to processes that are still running are never removed.
When this is nil, no sections are ever removed."
95 96
  :package-version '(magit . "2.1.0")
  :group 'magit-process
97
  :type '(choice (const :tag "Never remove old sections" nil) integer))
98

99 100 101 102 103 104 105 106 107 108 109 110
(defcustom magit-process-error-tooltip-max-lines 20
  "The number of lines for `magit-process-error-lines' to return.

These are displayed in a tooltip for `mode-line-process' errors.

If `magit-process-error-tooltip-max-lines' is nil, the tooltip
displays the text of `magit-process-error-summary' instead."
  :package-version '(magit . "2.12.0")
  :group 'magit-process
  :type '(choice (const :tag "Use summary line" nil)
                 integer))

111
(defcustom magit-credential-cache-daemon-socket
112
  (--some (pcase-let ((`(,prog . ,args) (split-string it)))
113 114 115
            (if (and prog
                     (string-match-p
                      "\\`\\(?:\\(?:/.*/\\)?git-credential-\\)?cache\\'" prog))
116 117 118 119 120 121 122
                (or (cl-loop for (opt val) on args
                             if (string= opt "--socket")
                             return val)
                    (expand-file-name "~/.git-credential-cache/socket"))))
          ;; Note: `magit-process-file' is not yet defined when
          ;; evaluating this form, so we use `process-lines'.
          (ignore-errors
123 124 125 126
            (let ((process-environment
                   (append magit-git-environment process-environment)))
              (process-lines magit-git-executable
                             "config" "--get-all" "credential.helper"))))
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
  "If non-nil, start a credential cache daemon using this socket.

When using Git's cache credential helper in the normal way, Emacs
sends a SIGHUP to the credential daemon after the git subprocess
has exited, causing the daemon to also quit.  This can be avoided
by starting the `git-credential-cache--daemon' process directly
from Emacs.

The function `magit-maybe-start-credential-cache-daemon' takes
care of starting the daemon if necessary, using the value of this
option as the socket.  If this option is nil, then it does not
start any daemon.  Likewise if another daemon is already running,
then it starts no new daemon.  This function has to be a member
of the hook variable `magit-credential-hook' for this to work.
If an error occurs while starting the daemon, most likely because
the necessary executable is missing, then the function removes
itself from the hook, to avoid further futile attempts."
  :package-version '(magit . "2.3.0")
  :group 'magit-process
  :type '(choice (file  :tag "Socket")
                 (const :tag "Don't start a cache daemon" nil)))

149 150 151 152 153 154 155 156 157
(defcustom magit-process-yes-or-no-prompt-regexp
  " [\[(]\\([Yy]\\(?:es\\)?\\)[/|]\\([Nn]o?\\)[\])] ?[?:] ?$"
  "Regexp matching Yes-or-No prompts of Git and its subprocesses."
  :package-version '(magit . "2.1.0")
  :group 'magit-process
  :type 'regexp)

(defcustom magit-process-password-prompt-regexps
  '("^\\(Enter \\)?[Pp]assphrase\\( for \\(RSA \\)?key '.*'\\)?: ?$"
158 159
    ;; match-group 99 is used to identify a host
    "^\\(Enter \\)?[Pp]assword\\( for '\\(?99:.*\\)'\\)?: ?$"
160
    "^.*'s password: ?$"
161 162
    "^Yubikey for .*: ?$"
    "^Enter PIN for .*: ?$")
163 164
  "List of regexps matching password prompts of Git and its subprocesses.
Also see `magit-process-find-password-functions'."
165
  :package-version '(magit . "2.8.0")
166 167 168
  :group 'magit-process
  :type '(repeat (regexp)))

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
(defcustom magit-process-find-password-functions nil
  "List of functions to try in sequence to get a password.

These functions may be called when git asks for a password, which
is detected using `magit-process-password-prompt-regexps'.  They
are called if and only if matching the prompt resulted in the
value of the 99th submatch to be non-nil.  Therefore users can
control for which prompts these functions should be called by
putting the host name in the 99th submatch, or not.

If the functions are called, then they are called in the order
given, with the host name as only argument, until one of them
returns non-nil.  If they are not called or none of them returns
non-nil, then the password is read from the user instead."
  :package-version '(magit . "2.3.0")
  :group 'magit-process
  :type 'hook
  :options '(magit-process-password-auth-source))

188 189 190 191 192 193 194
(defcustom magit-process-username-prompt-regexps
  '("^Username for '.*': ?$")
  "List of regexps matching username prompts of Git and its subprocesses."
  :package-version '(magit . "2.1.0")
  :group 'magit-process
  :type '(repeat (regexp)))

195 196 197 198 199 200
(defcustom magit-process-ensure-unix-line-ending t
  "Whether Magit should ensure a unix coding system when talking to Git."
  :package-version '(magit . "2.6.0")
  :group 'magit-process
  :type 'boolean)

201 202 203 204 205 206
(defcustom magit-process-display-mode-line-error t
  "Whether Magit should retain and highlight process errors in the mode line."
  :package-version '(magit . "2.12.0")
  :group 'magit-process
  :type 'boolean)

207 208 209 210 211 212 213 214 215 216
(defface magit-process-ok
  '((t :inherit magit-section-heading :foreground "green"))
  "Face for zero exit-status."
  :group 'magit-faces)

(defface magit-process-ng
  '((t :inherit magit-section-heading :foreground "red"))
  "Face for non-zero exit-status."
  :group 'magit-faces)

217 218 219 220 221
(defface magit-mode-line-process
  '((t :inherit mode-line-emphasis))
  "Face for `mode-line-process' status when Git is running for side-effects."
  :group 'magit-faces)

222 223 224 225 226 227 228
(defface magit-mode-line-process-error
  '((t :inherit error))
  "Face for `mode-line-process' error status.

Used when `magit-process-display-mode-line-error' is non-nil."
  :group 'magit-faces)

229 230 231 232 233 234 235 236 237 238
;;; Process Mode

(defvar magit-process-mode-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map magit-mode-map)
    map)
  "Keymap for `magit-process-mode'.")

(define-derived-mode magit-process-mode magit-mode "Magit Process"
  "Mode for looking at Git process output."
239
  :group 'magit-process
240 241 242 243 244
  (hack-dir-local-variables-non-file-buffer)
  (setq imenu-prev-index-position-function
        'magit-imenu--process-prev-index-position-function)
  (setq imenu-extract-index-name-function
        'magit-imenu--process-extract-index-name-function))
245

246 247 248 249 250 251 252
(defun magit-process-buffer (&optional nodisplay)
  "Display the current repository's process buffer.

If that buffer doesn't exist yet, then create it.
Non-interactively return the buffer and unless
optional NODISPLAY is non-nil also display it."
  (interactive)
253 254
  (let ((topdir (magit-toplevel)))
    (unless topdir
255 256 257 258 259 260
      (magit--with-safe-default-directory nil
        (setq topdir default-directory)
        (let (prev)
          (while (not (equal topdir prev))
            (setq prev topdir)
            (setq topdir (file-name-directory (directory-file-name topdir)))))))
261 262 263 264 265
    (let ((buffer (or (--first (with-current-buffer it
                                 (and (eq major-mode 'magit-process-mode)
                                      (equal default-directory topdir)))
                               (buffer-list))
                      (let ((default-directory topdir))
266
                        (magit-generate-new-buffer 'magit-process-mode)))))
267 268 269 270 271
      (with-current-buffer buffer
        (if magit-root-section
            (when magit-process-log-max
              (magit-process-truncate-log))
          (magit-process-mode)
272 273 274
          (let ((inhibit-read-only t)
                (magit-insert-section--parent  nil)
                (magit-insert-section--oldroot nil))
275 276 277 278 279 280
            (make-local-variable 'text-property-default-nonsticky)
            (magit-insert-section (processbuf)
              (insert "\n")))))
      (unless nodisplay
        (magit-display-buffer buffer))
      buffer)))
281 282 283 284

(defun magit-process-kill ()
  "Kill the process at point."
  (interactive)
285 286 287 288 289
  (when-let ((process (magit-section-value-if 'process)))
    (unless (eq (process-status process) 'run)
      (user-error "Process isn't running"))
    (magit-confirm 'kill-process)
    (kill-process process)))
290 291 292

;;; Synchronous Processes

293 294 295 296 297 298 299 300 301
(defvar magit-process-raise-error nil)

(defun magit-git (&rest args)
  "Call Git synchronously in a separate process, for side-effects.

Option `magit-git-executable' specifies the Git executable.
The arguments ARGS specify arguments to Git, they are flattened
before use.

302 303 304
Process output goes into a new section in the buffer returned by
`magit-process-buffer'.  If Git exits with a non-zero status,
then raise an error."
305 306 307
  (let ((magit-process-raise-error t))
    (magit-call-git args)))

308 309 310 311
(defun magit-run-git (&rest args)
  "Call Git synchronously in a separate process, and refresh.

Option `magit-git-executable' specifies the Git executable and
312
option `magit-git-global-arguments' specifies constant arguments.
313 314
The arguments ARGS specify arguments to Git, they are flattened
before use.
315 316 317 318

After Git returns, the current buffer (if it is a Magit buffer)
as well as the current repository's status buffer are refreshed.

319 320
Process output goes into a new section in the buffer returned by
`magit-process-buffer'."
321 322
  (let ((magit--refresh-cache (list (cons 0 0))))
    (magit-call-git args)
323 324 325
    (when (member (car args) '("init" "clone"))
      ;; Creating a new repository invalidates the cache.
      (setq magit--refresh-cache nil))
326
    (magit-refresh)))
327

328 329
(defvar magit-pre-call-git-hook nil)

330 331 332 333
(defun magit-call-git (&rest args)
  "Call Git synchronously in a separate process.

Option `magit-git-executable' specifies the Git executable and
334
option `magit-git-global-arguments' specifies constant arguments.
335 336
The arguments ARGS specify arguments to Git, they are flattened
before use.
337

338 339
Process output goes into a new section in the buffer returned by
`magit-process-buffer'."
340
  (run-hooks 'magit-pre-call-git-hook)
341
  (let ((default-process-coding-system (magit--process-coding-system)))
342 343
    (apply #'magit-call-process magit-git-executable
           (magit-process-git-arguments args))))
344 345 346

(defun magit-call-process (program &rest args)
  "Call PROGRAM synchronously in a separate process.
347 348
Process output goes into a new section in the buffer returned by
`magit-process-buffer'."
349 350
  (pcase-let ((`(,process-buf . ,section)
               (magit-process-setup program args)))
351 352
    (magit-process-finish
     (let ((inhibit-read-only t))
353
       (apply #'magit-process-file program nil process-buf nil args))
354 355
     process-buf (current-buffer) default-directory section)))

356
(defun magit-process-file (process &optional infile buffer display &rest args)
357 358
  "Process files synchronously in a separate process.
Identical to `process-file' but temporarily enable Cygwin's
359 360
\"noglob\" option during the call and ensure unix eol
conversion."
361
  (let ((process-environment (magit-process-environment))
362
        (default-process-coding-system (magit--process-coding-system)))
363
    (apply #'process-file process infile buffer display args)))
364

365
(defun magit-process-environment ()
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
  ;; The various w32 hacks are only applicable when running on the
  ;; local machine.  As of Emacs 25.1, a local binding of
  ;; process-environment different from the top-level value affects
  ;; the environment used in
  ;; tramp-sh-handle-{start-file-process,process-file}.
  (let ((local (not (file-remote-p default-directory))))
    (append magit-git-environment
            (and local
                 (cdr (assoc magit-git-executable magit-git-w32-path-hack)))
            (and local magit-need-cygwin-noglob
                 (mapcar (lambda (var)
                           (concat var "=" (--if-let (getenv var)
                                               (concat it " noglob")
                                             "noglob")))
                         '("CYGWIN" "MSYS")))
            process-environment)))
382

383 384
(defvar magit-this-process nil)

385
(defun magit-run-git-with-input (&rest args)
386 387 388
  "Call Git in a separate process.
ARGS is flattened and then used as arguments to Git.

389 390
The current buffer's content is used as the process' standard
input.
391 392

Option `magit-git-executable' specifies the Git executable and
393
option `magit-git-global-arguments' specifies constant arguments.
394
The remaining arguments ARGS specify arguments to Git, they are
395
flattened before use."
396 397 398 399
  (when (eq system-type 'windows-nt)
    ;; On w32, git expects UTF-8 encoded input, ignore any user
    ;; configuration telling us otherwise (see #3250).
    (encode-coding-region (point-min) (point-max) 'utf-8-unix))
400 401 402 403
  (if (file-remote-p default-directory)
      ;; We lack `process-file-region', so fall back to asynch +
      ;; waiting in remote case.
      (progn
404
        (magit-start-git (current-buffer) args)
405 406 407
        (while (and magit-this-process
                    (eq (process-status magit-this-process) 'run))
          (sleep-for 0.005)))
408
    (run-hooks 'magit-pre-call-git-hook)
409 410 411 412 413 414
    (pcase-let* ((process-environment (magit-process-environment))
                 (default-process-coding-system (magit--process-coding-system))
                 (flat-args (magit-process-git-arguments args))
                 (`(,process-buf . ,section)
                  (magit-process-setup magit-git-executable flat-args))
                 (inhibit-read-only t))
415 416 417 418
      (magit-process-finish
       (apply #'call-process-region (point-min) (point-max)
              magit-git-executable nil process-buf nil flat-args)
       process-buf nil default-directory section))))
419 420 421 422

(defun magit-run-git-with-logfile (file &rest args)
  "Call Git in a separate process and log its output to FILE.
This function might have a short halflive."
423 424
  (apply #'magit-process-file magit-git-executable nil `(:file ,file) nil
         (magit-process-git-arguments args))
425 426 427 428 429 430 431 432 433 434 435
  (magit-refresh))

;;; Asynchronous Processes

(defun magit-run-git-async (&rest args)
  "Start Git, prepare for refresh, and return the process object.
ARGS is flattened and then used as arguments to Git.

Display the command line arguments in the echo area.

After Git returns some buffers are refreshed: the buffer that was
436 437
current when this function was called (if it is a Magit buffer
and still alive), as well as the respective Magit status buffer.
438 439 440

See `magit-start-process' for more information."
  (message "Running %s %s" magit-git-executable
441 442 443
           (let ((m (mapconcat #'identity (-flatten args) " ")))
             (remove-list-of-text-properties 0 (length m) '(face) m)
             m))
444 445
  (magit-start-git nil args))

446 447 448 449 450 451 452 453 454 455
(defun magit-run-git-with-editor (&rest args)
  "Export GIT_EDITOR and start Git.
Also prepare for refresh and return the process object.
ARGS is flattened and then used as arguments to Git.

Display the command line arguments in the echo area.

After Git returns some buffers are refreshed: the buffer that was
current when this function was called (if it is a Magit buffer
and still alive), as well as the respective Magit status buffer.
456 457

See `magit-start-process' and `with-editor' for more information."
458
  (magit--record-separated-gitdir)
459
  (magit-with-editor (magit-run-git-async args)))
460 461 462 463 464 465 466 467 468 469 470 471 472 473

(defun magit-run-git-sequencer (&rest args)
  "Export GIT_EDITOR and start Git.
Also prepare for refresh and return the process object.
ARGS is flattened and then used as arguments to Git.

Display the command line arguments in the echo area.

After Git returns some buffers are refreshed: the buffer that was
current when this function was called (if it is a Magit buffer
and still alive), as well as the respective Magit status buffer.
If the sequence stops at a commit, make the section representing
that commit the current section by moving `point' there.

474
See `magit-start-process' and `with-editor' for more information."
475
  (apply #'magit-run-git-with-editor args)
476 477
  (set-process-sentinel magit-this-process #'magit-sequencer-process-sentinel)
  magit-this-process)
478

479 480
(defvar magit-pre-start-git-hook nil)

481 482 483 484 485 486 487 488
(defun magit-start-git (input &rest args)
  "Start Git, prepare for refresh, and return the process object.

If INPUT is non-nil, it has to be a buffer or the name of an
existing buffer.  The buffer content becomes the processes
standard input.

Option `magit-git-executable' specifies the Git executable and
489
option `magit-git-global-arguments' specifies constant arguments.
490 491 492 493
The remaining arguments ARGS specify arguments to Git, they are
flattened before use.

After Git returns some buffers are refreshed: the buffer that was
494 495
current when this function was called (if it is a Magit buffer
and still alive), as well as the respective Magit status buffer.
496 497 498

See `magit-start-process' for more information."
  (run-hooks 'magit-pre-start-git-hook)
499
  (let ((default-process-coding-system (magit--process-coding-system)))
500 501
    (apply #'magit-start-process magit-git-executable input
           (magit-process-git-arguments args))))
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519

(defun magit-start-process (program &optional input &rest args)
  "Start PROGRAM, prepare for refresh, and return the process object.

If optional argument INPUT is non-nil, it has to be a buffer or
the name of an existing buffer.  The buffer content becomes the
processes standard input.

The process is started using `start-file-process' and then setup
to use the sentinel `magit-process-sentinel' and the filter
`magit-process-filter'.  Information required by these functions
is stored in the process object.  When this function returns the
process has not started to run yet so it is possible to override
the sentinel and filter.

After the process returns, `magit-process-sentinel' refreshes the
buffer that was current when `magit-start-process' was called (if
it is a Magit buffer and still alive), as well as the respective
520
Magit status buffer."
521 522 523 524 525 526 527 528 529 530 531 532 533
  (pcase-let*
      ((`(,process-buf . ,section)
        (magit-process-setup program args))
       (process
        (let ((process-connection-type
               ;; Don't use a pty, because it would set icrnl
               ;; which would modify the input (issue #20).
               (and (not input) magit-process-connection-type))
              (process-environment (magit-process-environment))
              (default-process-coding-system (magit--process-coding-system)))
          (apply #'start-file-process
                 (file-name-nondirectory program)
                 process-buf program args))))
534 535 536
    (with-editor-set-process-filter process #'magit-process-filter)
    (set-process-sentinel process #'magit-process-sentinel)
    (set-process-buffer   process process-buf)
537 538 539
    (when (eq system-type 'windows-nt)
      ;; On w32, git expects UTF-8 encoded input, ignore any user
      ;; configuration telling us otherwise.
540
      (set-process-coding-system process 'utf-8-unix))
541 542 543 544 545
    (process-put process 'section section)
    (process-put process 'command-buf (current-buffer))
    (process-put process 'default-dir default-directory)
    (when inhibit-magit-refresh
      (process-put process 'inhibit-refresh t))
546
    (oset section process process)
547 548 549 550 551 552 553
    (with-current-buffer process-buf
      (set-marker (process-mark process) (point)))
    (when input
      (with-current-buffer input
        (process-send-region process (point-min) (point-max))
        (process-send-eof    process)))
    (setq magit-this-process process)
554
    (oset section value process)
555 556
    (magit-process-display-buffer process)
    process))
557

558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
(defun magit-parse-git-async (&rest args)
  (setq args (magit-process-git-arguments args))
  (let ((command-buf (current-buffer))
        (process-buf (generate-new-buffer " *temp*"))
        (toplevel (magit-toplevel)))
    (with-current-buffer process-buf
      (setq default-directory toplevel)
      (let ((process
             (let ((process-connection-type nil)
                   (process-environment (magit-process-environment))
                   (default-process-coding-system
                     (magit--process-coding-system)))
               (apply #'start-file-process "git" process-buf
                      magit-git-executable args))))
        (process-put process 'command-buf command-buf)
        (process-put process 'parsed (point))
        (setq magit-this-process process)
        process))))

577 578 579 580
;;; Process Internals

(defun magit-process-setup (program args)
  (magit-process-set-mode-line program args)
581
  (let ((pwd default-directory)
582
        (buf (magit-process-buffer t)))
583
    (cons buf (with-current-buffer buf
584
                (prog1 (magit-process-insert-section pwd program args nil nil)
585 586
                  (backward-char 1))))))

587
(defun magit-process-insert-section (pwd program args &optional errcode errlog)
588
  (let ((inhibit-read-only t)
589 590
        (magit-insert-section--parent magit-root-section)
        (magit-insert-section--oldroot nil))
591 592
    (goto-char (1- (point-max)))
    (magit-insert-section (process)
593 594 595 596
      (insert (if errcode
                  (format "%3s " (propertize (number-to-string errcode)
                                             'face 'magit-process-ng))
                "run "))
597 598
      (unless (equal (expand-file-name pwd)
                     (expand-file-name default-directory))
599
        (insert (file-relative-name pwd default-directory) ?\s))
600 601
      (cond
       ((and args (equal program magit-git-executable))
602
        (setq args (-split-at (length magit-git-global-arguments) args))
603 604
        (insert (propertize (file-name-nondirectory program)
                            'face 'magit-section-heading) " ")
605 606 607 608
        (insert (propertize (char-to-string magit-ellipsis)
                            'face 'magit-section-heading
                            'help-echo (mapconcat #'identity (car args) " ")))
        (insert " ")
609 610 611 612 613
        (insert (propertize (mapconcat #'shell-quote-argument (cadr args) " ")
                            'face 'magit-section-heading)))
       ((and args (equal program shell-file-name))
        (insert (propertize (cadr args) 'face 'magit-section-heading)))
       (t
614 615
        (insert (propertize (file-name-nondirectory program)
                            'face 'magit-section-heading) " ")
616 617
        (insert (propertize (mapconcat #'shell-quote-argument args " ")
                            'face 'magit-section-heading))))
618 619 620 621
      (magit-insert-heading)
      (when errlog
        (insert-file-contents errlog)
        (goto-char (1- (point-max))))
622
      (insert "\n"))))
623

624 625
(defun magit-process-truncate-log ()
  (let* ((head nil)
626
         (tail (oref magit-root-section children))
627 628 629 630 631 632
         (count (length tail)))
    (when (> (1+ count) magit-process-log-max)
      (while (and (cdr tail)
                  (> count (/ magit-process-log-max 2)))
        (let* ((inhibit-read-only t)
               (section (car tail))
633
               (process (oref section process)))
634 635
          (cond ((not process))
                ((memq (process-status process) '(exit signal))
636 637
                 (delete-region (oref section start)
                                (1+ (oref section end)))
638 639 640 641
                 (cl-decf count))
                (t
                 (push section head))))
        (pop tail))
642
      (oset magit-root-section children
643
            (nconc (reverse head) tail)))))
644 645 646 647 648 649 650 651 652 653

(defun magit-process-sentinel (process event)
  "Default sentinel used by `magit-start-process'."
  (when (memq (process-status process) '(exit signal))
    (setq event (substring event 0 -1))
    (when (string-match "^finished" event)
      (message (concat (capitalize (process-name process)) " finished")))
    (magit-process-finish process)
    (when (eq process magit-this-process)
      (setq magit-this-process nil))
654
    (unless (process-get process 'inhibit-refresh)
655
      (let ((command-buf (process-get process 'command-buf)))
656 657 658 659 660
        (if (buffer-live-p command-buf)
            (with-current-buffer command-buf
              (magit-refresh))
          (with-temp-buffer
            (setq default-directory (process-get process 'default-dir))
661
            (magit-refresh)))))))
662 663 664 665 666

(defun magit-sequencer-process-sentinel (process event)
  "Special sentinel used by `magit-run-git-sequencer'."
  (when (memq (process-status process) '(exit signal))
    (magit-process-sentinel process event)
667
    (when-let ((process-buf (process-buffer process)))
668
      (when (buffer-live-p process-buf)
669 670
        (when-let ((status-buf (with-current-buffer process-buf
                                 (magit-mode-get-buffer 'magit-status-mode))))
671 672 673 674 675 676 677 678 679 680
          (with-current-buffer status-buf
            (--when-let
                (magit-get-section
                 `((commit . ,(magit-rev-parse "HEAD"))
                   (,(pcase (car (cadr (-split-at
                                        (1+ (length magit-git-global-arguments))
                                        (process-command process))))
                       ((or "rebase" "am")   'rebase-sequence)
                       ((or "cherry-pick" "revert") 'sequence)))
                   (status)))
681
              (goto-char (oref it start))
682
              (magit-section-update-highlight))))))))
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704

(defun magit-process-filter (proc string)
  "Default filter used by `magit-start-process'."
  (with-current-buffer (process-buffer proc)
    (let ((inhibit-read-only t))
      (magit-process-yes-or-no-prompt proc string)
      (magit-process-username-prompt  proc string)
      (magit-process-password-prompt  proc string)
      (goto-char (process-mark proc))
      (setq string (propertize string 'magit-section
                               (process-get proc 'section)))
      ;; Find last ^M in string.  If one was found, ignore
      ;; everything before it and delete the current line.
      (let ((ret-pos (length string)))
        (while (and (>= (cl-decf ret-pos) 0)
                    (/= ?\r (aref string ret-pos))))
        (if (< ret-pos 0)
            (insert string)
          (delete-region (line-beginning-position) (point))
          (insert (substring string (1+ ret-pos)))))
      (set-marker (process-mark proc) (point)))))

705
(defmacro magit-process-kill-on-abort (proc &rest body)
706
  (declare (indent 1) (debug (form body)))
707
  (let ((map (cl-gensym)))
708 709 710 711 712 713 714 715 716 717 718
    `(let ((,map (make-sparse-keymap)))
       (set-keymap-parent ,map minibuffer-local-map)
       (define-key ,map "\C-g"
         (lambda ()
           (interactive)
           (ignore-errors (kill-process ,proc))
           (abort-recursive-edit)))
       (let ((minibuffer-local-map ,map))
         ,@body))))

(defun magit-process-yes-or-no-prompt (process string)
719
  "Forward Yes-or-No prompts to the user."
720
  (when-let ((beg (string-match magit-process-yes-or-no-prompt-regexp string)))
721 722
    (let ((max-mini-window-height 30))
      (process-send-string
Syohei YOSHIDA's avatar
Syohei YOSHIDA committed
723
       process
724
       (downcase
725 726
        (concat
         (match-string
727 728 729
          (if (save-match-data
                (magit-process-kill-on-abort process
                  (yes-or-no-p (substring string 0 beg)))) 1 2)
730 731
          string)
         "\n"))))))
732

733 734
(defun magit-process-password-auth-source (key)
  "Use `auth-source-search' to get a password.
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
If found, return the password.  Otherwise, return nil.

To use this function add it to the appropriate hook
  (add-hook 'magit-process-find-password-functions
            'magit-process-password-auth-source)

KEY typically derives from a prompt such as:
  Password for 'https://tarsius@bitbucket.org'
in which case it would be the string
  tarsius@bitbucket.org
which matches the ~/.authinfo.gpg entry
  machine bitbucket.org login tarsius password 12345
or iff that is undefined, for backward compatibility
  machine tarsius@bitbucket.org password 12345"
  (message "key: %S" key)
750
  (require 'auth-source)
751 752 753 754 755 756 757 758
  (let ((secret
         (plist-get
          (car (or (and (string-match "\\([^@]+\\)@\\([^@]+\\)" key)
                        (auth-source-search :max 1
                                            :host (match-string 2 key)
                                            :login (match-string 1 key)))
                   (auth-source-search :max 1 :host key)))
          :secret)))
759 760 761 762
    (if (functionp secret)
        (funcall secret)
      secret)))

763
(defun magit-process-password-prompt (process string)
764
  "Find a password based on prompt STRING and send it to git.
765 766 767 768 769
Use `magit-process-password-prompt-regexps' to find a known
prompt.  If and only if one is found, then call functions in
`magit-process-find-password-functions' until one of them returns
the password.  If all function return nil, then read the password
from the user."
770 771
  (--when-let (magit-process-match-prompt
               magit-process-password-prompt-regexps string)
772 773
    (process-send-string
     process (magit-process-kill-on-abort process
774 775 776 777 778
               (concat (or (--when-let (match-string 99 string)
                             (run-hook-with-args-until-success
                              'magit-process-find-password-functions it))
                           (read-passwd it))
                       "\n")))))
779

780
(defun magit-process-username-prompt (process string)
781 782 783 784
  "Forward username prompts to the user."
  (--when-let (magit-process-match-prompt
               magit-process-username-prompt-regexps string)
    (process-send-string
785 786
     process (magit-process-kill-on-abort process
               (concat (read-string it nil nil (user-login-name)) "\n")))))
787 788

(defun magit-process-match-prompt (prompts string)
789 790
  "Match STRING against PROMPTS and set match data.
Return the matched string suffixed with \": \", if needed."
791
  (when (--any-p (string-match it string) prompts)
792
    (let ((prompt (match-string 0 string)))
793 794 795
      (cond ((string-suffix-p ": " prompt) prompt)
            ((string-suffix-p ":"  prompt) (concat prompt " "))
            (t                             (concat prompt ": "))))))
796

797
(defun magit--process-coding-system ()
798 799 800 801 802 803 804
  (let ((fro (or magit-git-output-coding-system
                 (car default-process-coding-system)))
        (to (cdr default-process-coding-system)))
    (if magit-process-ensure-unix-line-ending
        (cons (coding-system-change-eol-conversion fro 'unix)
              (coding-system-change-eol-conversion to 'unix))
      (cons fro to))))
805

806 807 808
(defvar magit-credential-hook nil
  "Hook run before Git needs credentials.")

809 810 811 812 813 814 815 816 817 818 819 820 821 822
(defvar magit-credential-cache-daemon-process nil)

(defun magit-maybe-start-credential-cache-daemon ()
  "Maybe start a `git-credential-cache--daemon' process.

If such a process is already running or if the value of option
`magit-credential-cache-daemon-socket' is nil, then do nothing.
Otherwise start the process passing the value of that options
as argument."
  (unless (or (not magit-credential-cache-daemon-socket)
              (process-live-p magit-credential-cache-daemon-process)
              (memq magit-credential-cache-daemon-process
                    (list-system-processes)))
    (setq magit-credential-cache-daemon-process
823 824 825
          (or (--first (let* ((attr (process-attributes it))
                              (comm (cdr (assq 'comm attr)))
                              (user (cdr (assq 'user attr))))
826 827 828
                         (and (string= comm "git-credential-cache--daemon")
                              (string= user user-login-name)))
                       (list-system-processes))
829
              (condition-case nil
830 831 832 833 834 835 836 837 838 839 840 841 842
                  (start-process "git-credential-cache--daemon"
                                 " *git-credential-cache--daemon*"
                                 magit-git-executable
                                 "credential-cache--daemon"
                                 magit-credential-cache-daemon-socket)
                ;; Some Git implementations (e.g. Windows) won't have
                ;; this program; if we fail the first time, stop trying.
                ((debug error)
                 (remove-hook 'magit-credential-hook
                              #'magit-maybe-start-credential-cache-daemon)))))))

(add-hook 'magit-credential-hook #'magit-maybe-start-credential-cache-daemon)

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
(defun tramp-sh-handle-start-file-process--magit-tramp-process-environment
    (fn name buffer program &rest args)
  (if magit-tramp-process-environment
      (apply fn name buffer
             (car magit-tramp-process-environment)
             (append (cdr magit-tramp-process-environment)
                     (cons program args)))
    (apply fn name buffer program args)))

(advice-add 'tramp-sh-handle-start-file-process :around
            'tramp-sh-handle-start-file-process--magit-tramp-process-environment)

(defun tramp-sh-handle-process-file--magit-tramp-process-environment
    (fn program &optional infile destination display &rest args)
  (if magit-tramp-process-environment
      (apply fn "env" infile destination display
             (append magit-tramp-process-environment
                     (cons program args)))
    (apply fn program infile destination display args)))

(advice-add 'tramp-sh-handle-process-file :around
            'tramp-sh-handle-process-file--magit-tramp-process-environment)
865

866 867 868 869 870 871 872
(defvar magit-mode-line-process-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "<mode-line> <mouse-1>")
      'magit-process-buffer)
    map)
  "Keymap for `mode-line-process'.")

873
(defun magit-process-set-mode-line (program args)
874
  "Display the git command (sans arguments) in the mode line."
875
  (when (equal program magit-git-executable)
876
    (setq args (nthcdr (length magit-git-global-arguments) args)))
877
  (let ((str (concat " " (propertize
878 879
                          (concat (file-name-nondirectory program)
                                  (and args (concat " " (car args))))
880 881 882
                          'mouse-face 'highlight
                          'keymap magit-mode-line-process-map
                          'help-echo "mouse-1: Show process buffer"
883
                          'face 'magit-mode-line-process))))
884
    (magit-repository-local-set 'mode-line-process str)
885
    (dolist (buf (magit-mode-get-buffers))
886
      (with-current-buffer buf
887 888
        (setq mode-line-process str)))
    (force-mode-line-update t)))
889

890
(defun magit-process-set-mode-line-error-status (&optional error str)
891 892
  "Apply an error face to the string set by `magit-process-set-mode-line'.

893 894
If ERROR is supplied, include it in the `mode-line-process' tooltip.

895 896 897
If STR is supplied, it replaces the `mode-line-process' text."
  (setq str (or str (magit-repository-local-get 'mode-line-process)))
  (when str
898 899 900 901
    (setq error (format "%smouse-1: Show process buffer"
                        (if (stringp error)
                            (concat error "\n\n")
                          "")))
902 903
    (setq str (concat " " (propertize
                           (substring-no-properties str 1)
904 905
                           'mouse-face 'highlight
                           'keymap magit-mode-line-process-map
906
                           'help-echo error
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
                           'face 'magit-mode-line-process-error)))
    (magit-repository-local-set 'mode-line-process str)
    (dolist (buf (magit-mode-get-buffers))
      (with-current-buffer buf
        (setq mode-line-process str)))
    (force-mode-line-update t)
    ;; We remove any error status from the mode line when a magit
    ;; buffer is refreshed (see `magit-refresh-buffer'), but we must
    ;; ensure that we ignore any refreshes during the remainder of the
    ;; current command -- otherwise a newly-set error status would be
    ;; removed before it was seen.  We set a flag which prevents the
    ;; status from being removed prior to the next command, so that
    ;; the error status is guaranteed to remain visible until then.
    (let ((repokey (magit-repository-local-repository)))
      ;; The following closure captures the repokey value, and is
      ;; added to `pre-command-hook'.
      (cl-labels ((enable-magit-process-unset-mode-line
                   () ;; Remove ourself from the hook variable, so
                      ;; that we only run once.
                   (remove-hook 'pre-command-hook
                                #'enable-magit-process-unset-mode-line)
                   ;; Clear the inhibit flag for the repository in
                   ;; which we set it.
                   (magit-repository-local-set
                    'inhibit-magit-process-unset-mode-line nil repokey)))
        ;; Set the inhibit flag until the next command is invoked.
        (magit-repository-local-set
         'inhibit-magit-process-unset-mode-line t repokey)
        (add-hook 'pre-command-hook
                  #'enable-magit-process-unset-mode-line)))))

(defun magit-process-unset-mode-line-error-status ()
  "Remove any current error status from the mode line."
  (let ((status (or mode-line-process
                    (magit-repository-local-get 'mode-line-process))))
    (when (and status
               (eq (get-text-property 1 'face status)
                   'magit-mode-line-process-error))
      (magit-process-unset-mode-line))))

947
(defun magit-process-unset-mode-line ()
948
  "Remove the git command from the mode line."
949 950 951 952 953
  (unless (magit-repository-local-get 'inhibit-magit-process-unset-mode-line)
    (magit-repository-local-set 'mode-line-process nil)
    (dolist (buf (magit-mode-get-buffers))
      (with-current-buffer buf (setq mode-line-process nil)))
    (force-mode-line-update t)))
954

955
(defvar magit-process-error-message-regexps
956
  (list "^\\*ERROR\\*: Canceled by user$"
957 958
        "^\\(?:error\\|fatal\\|git\\): \\(.*\\)$"
        "^\\(Cannot rebase:.*\\)$"))
959

960 961
(define-error 'magit-git-error "Git error")

962 963 964 965
(defun magit-process-error-summary (process-buf section)
  "A one-line error summary from the given SECTION."
  (or (and (buffer-live-p process-buf)
           (with-current-buffer process-buf
966
             (and (oref section content)
967
                  (save-excursion
968
                    (goto-char (oref section end))
969 970 971 972
                    (run-hook-wrapped
                     'magit-process-error-message-regexps
                     (lambda (re)
                       (save-excursion
973
                         (and (re-search-backward
974
                               re (oref section start) t)
975 976 977 978 979 980 981 982 983 984 985 986 987 988
                              (or (match-string-no-properties 1)
                                  (and (not magit-process-raise-error)
                                       'suppressed))))))))))
      "Git failed"))

(defun magit-process-error-tooltip (process-buf section)
  "Returns the text from SECTION of the PROCESS-BUF buffer.

Limited by `magit-process-error-tooltip-max-lines'."
  (and (integerp magit-process-error-tooltip-max-lines)
       (> magit-process-error-tooltip-max-lines 0)
       (buffer-live-p process-buf)
       (with-current-buffer process-buf
         (save-excursion
989 990
           (goto-char (or (oref section content)
                          (oref section start)))
991 992 993 994 995
           (buffer-substring-no-properties
            (point)
            (save-excursion
              (forward-line magit-process-error-tooltip-max-lines)
              (goto-char
996 997
               (if (> (point) (oref section end))
                   (oref section end)
998 999 1000
                 (point)))
              ;; Remove any trailing whitespace.
              (when (re-search-backward "[^[:space:]\n]"
1001
                                        (oref section start) t)
1002 1003 1004
                (forward-char 1))
              (point)))))))

1005 1006
(defvar-local magit-this-error nil)

1007 1008
(defvar magit-process-finish-apply-ansi-colors nil)

1009 1010 1011
(defun magit-process-finish (arg &optional process-buf command-buf
                                 default-dir section)
  (unless (integerp arg)
1012 1013 1014 1015 1016
    (setq process-buf (process-buffer arg))
    (setq command-buf (process-get arg 'command-buf))
    (setq default-dir (process-get arg 'default-dir))
    (setq section     (process-get arg 'section))
    (setq arg         (process-exit-status arg)))
1017
  (when (fboundp 'dired-uncache)
1018 1019 1020 1021
    (dired-uncache default-dir))
  (when (buffer-live-p process-buf)
    (with-current-buffer process-buf
      (let ((inhibit-read-only t)
1022
            (marker (oref section start)))
1023 1024 1025 1026
        (goto-char marker)
        (save-excursion
          (delete-char 3)
          (set-marker-insertion-type marker nil)
1027 1028 1029 1030 1031 1032
          (insert (propertize (format "%3s" arg)
                              'magit-section section
                              'face (if (= arg 0)
                                        'magit-process-ok
                                      'magit-process-ng)))
          (set-marker-insertion-type marker t))
1033
        (when magit-process-finish-apply-ansi-colors
1034 1035 1036
          (ansi-color-apply-on-region (oref section content)
                                      (oref section end)))
        (if (= (oref section end)
1037 1038 1039 1040
               (+ (line-end-position) 2))
            (save-excursion
              (goto-char (1+ (line-end-position)))
              (delete-char -1)
1041
              (oset section content nil))
1042
          (let ((buf (magit-process-buffer t)))
1043 1044 1045 1046
            (when (and (= arg 0)
                       (not (--any-p (eq (window-buffer it) buf)
                                     (window-list))))
              (magit-section-hide section)))))))
1047 1048 1049
  (if (= arg 0)
      ;; Unset the `mode-line-process' value upon success.
      (magit-process-unset-mode-line)
1050 1051
    ;; Otherwise process the error.
    (let ((msg (magit-process-error-summary process-buf section)))
1052 1053
      ;; Change `mode-line-process' to an error face upon failure.
      (if magit-process-display-mode-line-error
1054 1055 1056
          (magit-process-set-mode-line-error-status
           (or (magit-process-error-tooltip process-buf section)
               msg))
1057
        (magit-process-unset-mode-line))
1058 1059
      ;; Either signal the error, or else display the error summary in
      ;; the status buffer and with a message in the echo area.
1060 1061 1062 1063
      (cond
       (magit-process-raise-error
        (signal 'magit-git-error (list (format "%s (in %s)" msg default-dir))))
       ((not (eq msg 'suppressed))
1064
        (when (buffer-live-p process-buf)
Jonas Bernoulli's avatar
Jonas Bernoulli committed
1065
          (with-current-buffer process-buf
1066
            (when-let ((status-buf (magit-mode-get-buffer 'magit-status-mode)))
Jonas Bernoulli's avatar
Jonas Bernoulli committed
1067 1068 1069
              (with-current-buffer status-buf
                (setq magit-this-error msg)))))
        (message "%s ... [%s buffer %s for details]" msg
1070 1071 1072 1073
                 (if-let ((key (and (buffer-live-p command-buf)
                                    (with-current-buffer command-buf
                                      (car (where-is-internal
                                            'magit-process-buffer))))))
Jonas Bernoulli's avatar
Jonas Bernoulli committed
1074 1075
                     (format "Hit %s to see" (key-description key))
                   "See")
1076
                 (buffer-name process-buf))))))
1077 1078 1079 1080 1081 1082 1083
  arg)

(defun magit-process-display-buffer (process)
  (when (process-live-p process)
    (let ((buf (process-buffer process)))
      (cond ((not (buffer-live-p buf)))
            ((= magit-process-popup-time 0)
1084 1085 1086
             (if (minibufferp)
                 (switch-to-buffer-other-window buf)
               (pop-to-buffer buf)))
1087 1088 1089 1090 1091 1092
            ((> magit-process-popup-time 0)
             (run-with-timer magit-process-popup-time nil
                             (lambda (p)
                               (when (eq (process-status p) 'run)
                                 (let ((buf (process-buffer p)))
                                   (when (buffer-live-p buf)
1093 1094 1095
                                     (if (minibufferp)
                                         (switch-to-buffer-other-window buf)
                                       (pop-to-buffer buf))))))
1096 1097
                             process))))))

1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
(defun magit--log-action (summary line list)
  (let (heading lines)
    (if (cdr list)
        (progn (setq heading (funcall summary list))
               (setq lines (mapcar line list)))
      (setq heading (funcall line (car list))))
    (with-current-buffer (magit-process-buffer t)
      (goto-char (1- (point-max)))
      (let ((inhibit-read-only t))
        (magit-insert-section (message)
          (magit-insert-heading (concat "  * " heading))
          (when lines
            (dolist (line lines)
              (insert line "\n"))
            (insert "\n"))))
      (let ((inhibit-message t))
        (when heading
          (setq lines (cons heading lines)))
        (message (mapconcat #'identity lines "\n"))))))

1118
;;; _
1119 1120
(provide 'magit-process)
;;; magit-process.el ends here