Klimi's new dotfiles with stow.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

1148 строки
48 KiB

5 лет назад
  1. ;;; magit-process.el --- process functionality -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2010-2019 The Magit Project Contributors
  3. ;;
  4. ;; You should have received a copy of the AUTHORS.md file which
  5. ;; lists all contributors. If not, see http://magit.vc/authors.
  6. ;; Author: Jonas Bernoulli <jonas@bernoul.li>
  7. ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
  8. ;; Magit is free software; you can redistribute it and/or modify it
  9. ;; under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation; either version 3, or (at your option)
  11. ;; any later version.
  12. ;;
  13. ;; Magit is distributed in the hope that it will be useful, but WITHOUT
  14. ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  15. ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  16. ;; License for more details.
  17. ;;
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with Magit. If not, see http://www.gnu.org/licenses.
  20. ;;; Commentary:
  21. ;; This library implements the tools used to run Git for side-effects.
  22. ;; Note that the functions used to run Git and then consume its
  23. ;; output, are defined in `magit-git.el'. There's a bit of overlap
  24. ;; though.
  25. ;;; Code:
  26. (require 'ansi-color)
  27. (require 'cl-lib)
  28. (require 'dash)
  29. (eval-when-compile
  30. (require 'subr-x))
  31. (require 'with-editor)
  32. (require 'magit-utils)
  33. (require 'magit-section)
  34. (require 'magit-git)
  35. (require 'magit-mode)
  36. (declare-function auth-source-search "auth-source"
  37. (&rest spec &key max require create delete &allow-other-keys))
  38. ;;; Options
  39. (defcustom magit-process-connection-type (not (eq system-type 'cygwin))
  40. "Connection type used for the Git process.
  41. If nil, use pipes: this is usually more efficient, and works on Cygwin.
  42. If t, use ptys: this enables Magit to prompt for passphrases when needed."
  43. :group 'magit-process
  44. :type '(choice (const :tag "pipe" nil)
  45. (const :tag "pty" t)))
  46. (defcustom magit-need-cygwin-noglob
  47. (and (eq system-type 'windows-nt)
  48. (with-temp-buffer
  49. (let ((process-environment
  50. (append magit-git-environment process-environment)))
  51. (condition-case e
  52. (process-file magit-git-executable
  53. nil (current-buffer) nil
  54. "-c" "alias.echo=!echo" "echo" "x{0}")
  55. (file-error
  56. (lwarn 'magit-process :warning
  57. "Could not run Git: %S" e))))
  58. (equal "x0\n" (buffer-string))))
  59. "Whether to use a workaround for Cygwin's globbing behavior.
  60. If non-nil, add environment variables to `process-environment' to
  61. prevent the git.exe distributed by Cygwin and MSYS2 from
  62. attempting to perform glob expansion when called from a native
  63. Windows build of Emacs. See #2246."
  64. :package-version '(magit . "2.3.0")
  65. :group 'magit-process
  66. :type '(choice (const :tag "Yes" t)
  67. (const :tag "No" nil)))
  68. (defcustom magit-process-popup-time -1
  69. "Popup the process buffer if a command takes longer than this many seconds."
  70. :group 'magit-process
  71. :type '(choice (const :tag "Never" -1)
  72. (const :tag "Immediately" 0)
  73. (integer :tag "After this many seconds")))
  74. (defcustom magit-process-log-max 32
  75. "Maximum number of sections to keep in a process log buffer.
  76. When adding a new section would go beyond the limit set here,
  77. then the older half of the sections are remove. Sections that
  78. belong to processes that are still running are never removed.
  79. When this is nil, no sections are ever removed."
  80. :package-version '(magit . "2.1.0")
  81. :group 'magit-process
  82. :type '(choice (const :tag "Never remove old sections" nil) integer))
  83. (defcustom magit-process-error-tooltip-max-lines 20
  84. "The number of lines for `magit-process-error-lines' to return.
  85. These are displayed in a tooltip for `mode-line-process' errors.
  86. If `magit-process-error-tooltip-max-lines' is nil, the tooltip
  87. displays the text of `magit-process-error-summary' instead."
  88. :package-version '(magit . "2.12.0")
  89. :group 'magit-process
  90. :type '(choice (const :tag "Use summary line" nil)
  91. integer))
  92. (defcustom magit-credential-cache-daemon-socket
  93. (--some (pcase-let ((`(,prog . ,args) (split-string it)))
  94. (if (and prog
  95. (string-match-p
  96. "\\`\\(?:\\(?:/.*/\\)?git-credential-\\)?cache\\'" prog))
  97. (or (cl-loop for (opt val) on args
  98. if (string= opt "--socket")
  99. return val)
  100. (expand-file-name "~/.git-credential-cache/socket"))))
  101. ;; Note: `magit-process-file' is not yet defined when
  102. ;; evaluating this form, so we use `process-lines'.
  103. (ignore-errors
  104. (let ((process-environment
  105. (append magit-git-environment process-environment)))
  106. (process-lines magit-git-executable
  107. "config" "--get-all" "credential.helper"))))
  108. "If non-nil, start a credential cache daemon using this socket.
  109. When using Git's cache credential helper in the normal way, Emacs
  110. sends a SIGHUP to the credential daemon after the git subprocess
  111. has exited, causing the daemon to also quit. This can be avoided
  112. by starting the `git-credential-cache--daemon' process directly
  113. from Emacs.
  114. The function `magit-maybe-start-credential-cache-daemon' takes
  115. care of starting the daemon if necessary, using the value of this
  116. option as the socket. If this option is nil, then it does not
  117. start any daemon. Likewise if another daemon is already running,
  118. then it starts no new daemon. This function has to be a member
  119. of the hook variable `magit-credential-hook' for this to work.
  120. If an error occurs while starting the daemon, most likely because
  121. the necessary executable is missing, then the function removes
  122. itself from the hook, to avoid further futile attempts."
  123. :package-version '(magit . "2.3.0")
  124. :group 'magit-process
  125. :type '(choice (file :tag "Socket")
  126. (const :tag "Don't start a cache daemon" nil)))
  127. (defcustom magit-process-yes-or-no-prompt-regexp
  128. " [\[(]\\([Yy]\\(?:es\\)?\\)[/|]\\([Nn]o?\\)[\])] ?[?:] ?$"
  129. "Regexp matching Yes-or-No prompts of Git and its subprocesses."
  130. :package-version '(magit . "2.1.0")
  131. :group 'magit-process
  132. :type 'regexp)
  133. (defcustom magit-process-password-prompt-regexps
  134. '("^\\(Enter \\)?[Pp]assphrase\\( for \\(RSA \\)?key '.*'\\)?: ?$"
  135. ;; Match-group 99 is used to identify the "user@host" part.
  136. "^\\(Enter \\)?[Pp]assword\\( for '\\(https?://\\)?\\(?99:.*\\)'\\)?: ?$"
  137. "^.*'s password: ?$"
  138. "^Yubikey for .*: ?$"
  139. "^Enter PIN for .*: ?$")
  140. "List of regexps matching password prompts of Git and its subprocesses.
  141. Also see `magit-process-find-password-functions'."
  142. :package-version '(magit . "2.8.0")
  143. :group 'magit-process
  144. :type '(repeat (regexp)))
  145. (defcustom magit-process-find-password-functions nil
  146. "List of functions to try in sequence to get a password.
  147. These functions may be called when git asks for a password, which
  148. is detected using `magit-process-password-prompt-regexps'. They
  149. are called if and only if matching the prompt resulted in the
  150. value of the 99th submatch to be non-nil. Therefore users can
  151. control for which prompts these functions should be called by
  152. putting the host name in the 99th submatch, or not.
  153. If the functions are called, then they are called in the order
  154. given, with the host name as only argument, until one of them
  155. returns non-nil. If they are not called or none of them returns
  156. non-nil, then the password is read from the user instead."
  157. :package-version '(magit . "2.3.0")
  158. :group 'magit-process
  159. :type 'hook
  160. :options '(magit-process-password-auth-source))
  161. (defcustom magit-process-username-prompt-regexps
  162. '("^Username for '.*': ?$")
  163. "List of regexps matching username prompts of Git and its subprocesses."
  164. :package-version '(magit . "2.1.0")
  165. :group 'magit-process
  166. :type '(repeat (regexp)))
  167. (defcustom magit-process-prompt-functions nil
  168. "List of functions used to forward arbitrary questions to the user.
  169. Magit has dedicated support for forwarding username and password
  170. prompts and Yes-or-No questions asked by Git and its subprocesses
  171. to the user. This can be customized using other options in the
  172. `magit-process' customization group.
  173. If you encounter a new question that isn't handled by default,
  174. then those options should be used instead of this hook.
  175. However subprocesses may also ask questions that differ too much
  176. from what the code related to the above options assume, and this
  177. hook allows users to deal with such questions explicitly.
  178. Each function is called with the process and the output string
  179. as arguments until one of the functions returns non-nil. The
  180. function is responsible for asking the user the appropriate
  181. question using e.g. `read-char-choice' and then forwarding the
  182. answer to the process using `process-send-string'.
  183. While functions such as `magit-process-yes-or-no-prompt' may not
  184. be sufficient to handle some prompt, it may still be of benefit
  185. to look at the implementations to gain some insights on how to
  186. implement such functions."
  187. :package-version '(magit . "2.91.0")
  188. :group 'magit-process
  189. :type 'hook)
  190. (defcustom magit-process-ensure-unix-line-ending t
  191. "Whether Magit should ensure a unix coding system when talking to Git."
  192. :package-version '(magit . "2.6.0")
  193. :group 'magit-process
  194. :type 'boolean)
  195. (defcustom magit-process-display-mode-line-error t
  196. "Whether Magit should retain and highlight process errors in the mode line."
  197. :package-version '(magit . "2.12.0")
  198. :group 'magit-process
  199. :type 'boolean)
  200. (defface magit-process-ok
  201. '((t :inherit magit-section-heading :foreground "green"))
  202. "Face for zero exit-status."
  203. :group 'magit-faces)
  204. (defface magit-process-ng
  205. '((t :inherit magit-section-heading :foreground "red"))
  206. "Face for non-zero exit-status."
  207. :group 'magit-faces)
  208. (defface magit-mode-line-process
  209. '((t :inherit mode-line-emphasis))
  210. "Face for `mode-line-process' status when Git is running for side-effects."
  211. :group 'magit-faces)
  212. (defface magit-mode-line-process-error
  213. '((t :inherit error))
  214. "Face for `mode-line-process' error status.
  215. Used when `magit-process-display-mode-line-error' is non-nil."
  216. :group 'magit-faces)
  217. ;;; Process Mode
  218. (defvar magit-process-mode-map
  219. (let ((map (make-sparse-keymap)))
  220. (set-keymap-parent map magit-mode-map)
  221. map)
  222. "Keymap for `magit-process-mode'.")
  223. (define-derived-mode magit-process-mode magit-mode "Magit Process"
  224. "Mode for looking at Git process output."
  225. :group 'magit-process
  226. (hack-dir-local-variables-non-file-buffer)
  227. (setq imenu-prev-index-position-function
  228. 'magit-imenu--process-prev-index-position-function)
  229. (setq imenu-extract-index-name-function
  230. 'magit-imenu--process-extract-index-name-function))
  231. (defun magit-process-buffer (&optional nodisplay)
  232. "Display the current repository's process buffer.
  233. If that buffer doesn't exist yet, then create it.
  234. Non-interactively return the buffer and unless
  235. optional NODISPLAY is non-nil also display it."
  236. (interactive)
  237. (let ((topdir (magit-toplevel)))
  238. (unless topdir
  239. (magit--with-safe-default-directory nil
  240. (setq topdir default-directory)
  241. (let (prev)
  242. (while (not (equal topdir prev))
  243. (setq prev topdir)
  244. (setq topdir (file-name-directory (directory-file-name topdir)))))))
  245. (let ((buffer (or (--first (with-current-buffer it
  246. (and (eq major-mode 'magit-process-mode)
  247. (equal default-directory topdir)))
  248. (buffer-list))
  249. (let ((default-directory topdir))
  250. (magit-generate-new-buffer 'magit-process-mode)))))
  251. (with-current-buffer buffer
  252. (if magit-root-section
  253. (when magit-process-log-max
  254. (magit-process-truncate-log))
  255. (magit-process-mode)
  256. (let ((inhibit-read-only t)
  257. (magit-insert-section--parent nil)
  258. (magit-insert-section--oldroot nil))
  259. (make-local-variable 'text-property-default-nonsticky)
  260. (magit-insert-section (processbuf)
  261. (insert "\n")))))
  262. (unless nodisplay
  263. (magit-display-buffer buffer))
  264. buffer)))
  265. (defun magit-process-kill ()
  266. "Kill the process at point."
  267. (interactive)
  268. (when-let ((process (magit-section-value-if 'process)))
  269. (unless (eq (process-status process) 'run)
  270. (user-error "Process isn't running"))
  271. (magit-confirm 'kill-process)
  272. (kill-process process)))
  273. ;;; Synchronous Processes
  274. (defvar magit-process-raise-error nil)
  275. (defun magit-git (&rest args)
  276. "Call Git synchronously in a separate process, for side-effects.
  277. Option `magit-git-executable' specifies the Git executable.
  278. The arguments ARGS specify arguments to Git, they are flattened
  279. before use.
  280. Process output goes into a new section in the buffer returned by
  281. `magit-process-buffer'. If Git exits with a non-zero status,
  282. then raise an error."
  283. (let ((magit-process-raise-error t))
  284. (magit-call-git args)))
  285. (defun magit-run-git (&rest args)
  286. "Call Git synchronously in a separate process, and refresh.
  287. Option `magit-git-executable' specifies the Git executable and
  288. option `magit-git-global-arguments' specifies constant arguments.
  289. The arguments ARGS specify arguments to Git, they are flattened
  290. before use.
  291. After Git returns, the current buffer (if it is a Magit buffer)
  292. as well as the current repository's status buffer are refreshed.
  293. Process output goes into a new section in the buffer returned by
  294. `magit-process-buffer'."
  295. (let ((magit--refresh-cache (list (cons 0 0))))
  296. (magit-call-git args)
  297. (when (member (car args) '("init" "clone"))
  298. ;; Creating a new repository invalidates the cache.
  299. (setq magit--refresh-cache nil))
  300. (magit-refresh)))
  301. (defvar magit-pre-call-git-hook nil)
  302. (defun magit-call-git (&rest args)
  303. "Call Git synchronously in a separate process.
  304. Option `magit-git-executable' specifies the Git executable and
  305. option `magit-git-global-arguments' specifies constant arguments.
  306. The arguments ARGS specify arguments to Git, they are flattened
  307. before use.
  308. Process output goes into a new section in the buffer returned by
  309. `magit-process-buffer'."
  310. (run-hooks 'magit-pre-call-git-hook)
  311. (let ((default-process-coding-system (magit--process-coding-system)))
  312. (apply #'magit-call-process magit-git-executable
  313. (magit-process-git-arguments args))))
  314. (defun magit-call-process (program &rest args)
  315. "Call PROGRAM synchronously in a separate process.
  316. Process output goes into a new section in the buffer returned by
  317. `magit-process-buffer'."
  318. (pcase-let ((`(,process-buf . ,section)
  319. (magit-process-setup program args)))
  320. (magit-process-finish
  321. (let ((inhibit-read-only t))
  322. (apply #'magit-process-file program nil process-buf nil args))
  323. process-buf (current-buffer) default-directory section)))
  324. (defun magit-process-file (process &optional infile buffer display &rest args)
  325. "Process files synchronously in a separate process.
  326. Identical to `process-file' but temporarily enable Cygwin's
  327. \"noglob\" option during the call and ensure unix eol
  328. conversion."
  329. (let ((process-environment (magit-process-environment))
  330. (default-process-coding-system (magit--process-coding-system)))
  331. (apply #'process-file process infile buffer display args)))
  332. (defun magit-process-environment ()
  333. ;; The various w32 hacks are only applicable when running on the
  334. ;; local machine. As of Emacs 25.1, a local binding of
  335. ;; process-environment different from the top-level value affects
  336. ;; the environment used in
  337. ;; tramp-sh-handle-{start-file-process,process-file}.
  338. (let ((local (not (file-remote-p default-directory))))
  339. (append magit-git-environment
  340. (and local
  341. (cdr (assoc magit-git-executable magit-git-w32-path-hack)))
  342. (and local magit-need-cygwin-noglob
  343. (mapcar (lambda (var)
  344. (concat var "=" (--if-let (getenv var)
  345. (concat it " noglob")
  346. "noglob")))
  347. '("CYGWIN" "MSYS")))
  348. process-environment)))
  349. (defvar magit-this-process nil)
  350. (defun magit-run-git-with-input (&rest args)
  351. "Call Git in a separate process.
  352. ARGS is flattened and then used as arguments to Git.
  353. The current buffer's content is used as the process' standard
  354. input.
  355. Option `magit-git-executable' specifies the Git executable and
  356. option `magit-git-global-arguments' specifies constant arguments.
  357. The remaining arguments ARGS specify arguments to Git, they are
  358. flattened before use."
  359. (when (eq system-type 'windows-nt)
  360. ;; On w32, git expects UTF-8 encoded input, ignore any user
  361. ;; configuration telling us otherwise (see #3250).
  362. (encode-coding-region (point-min) (point-max) 'utf-8-unix))
  363. (if (file-remote-p default-directory)
  364. ;; We lack `process-file-region', so fall back to asynch +
  365. ;; waiting in remote case.
  366. (progn
  367. (magit-start-git (current-buffer) args)
  368. (while (and magit-this-process
  369. (eq (process-status magit-this-process) 'run))
  370. (sleep-for 0.005)))
  371. (run-hooks 'magit-pre-call-git-hook)
  372. (pcase-let* ((process-environment (magit-process-environment))
  373. (default-process-coding-system (magit--process-coding-system))
  374. (flat-args (magit-process-git-arguments args))
  375. (`(,process-buf . ,section)
  376. (magit-process-setup magit-git-executable flat-args))
  377. (inhibit-read-only t))
  378. (magit-process-finish
  379. (apply #'call-process-region (point-min) (point-max)
  380. magit-git-executable nil process-buf nil flat-args)
  381. process-buf nil default-directory section))))
  382. ;;; Asynchronous Processes
  383. (defun magit-run-git-async (&rest args)
  384. "Start Git, prepare for refresh, and return the process object.
  385. ARGS is flattened and then used as arguments to Git.
  386. Display the command line arguments in the echo area.
  387. After Git returns some buffers are refreshed: the buffer that was
  388. current when this function was called (if it is a Magit buffer
  389. and still alive), as well as the respective Magit status buffer.
  390. See `magit-start-process' for more information."
  391. (message "Running %s %s" magit-git-executable
  392. (let ((m (mapconcat #'identity (-flatten args) " ")))
  393. (remove-list-of-text-properties 0 (length m) '(face) m)
  394. m))
  395. (magit-start-git nil args))
  396. (defun magit-run-git-with-editor (&rest args)
  397. "Export GIT_EDITOR and start Git.
  398. Also prepare for refresh and return the process object.
  399. ARGS is flattened and then used as arguments to Git.
  400. Display the command line arguments in the echo area.
  401. After Git returns some buffers are refreshed: the buffer that was
  402. current when this function was called (if it is a Magit buffer
  403. and still alive), as well as the respective Magit status buffer.
  404. See `magit-start-process' and `with-editor' for more information."
  405. (magit--record-separated-gitdir)
  406. (magit-with-editor (magit-run-git-async args)))
  407. (defun magit-run-git-sequencer (&rest args)
  408. "Export GIT_EDITOR and start Git.
  409. Also prepare for refresh and return the process object.
  410. ARGS is flattened and then used as arguments to Git.
  411. Display the command line arguments in the echo area.
  412. After Git returns some buffers are refreshed: the buffer that was
  413. current when this function was called (if it is a Magit buffer
  414. and still alive), as well as the respective Magit status buffer.
  415. If the sequence stops at a commit, make the section representing
  416. that commit the current section by moving `point' there.
  417. See `magit-start-process' and `with-editor' for more information."
  418. (apply #'magit-run-git-with-editor args)
  419. (set-process-sentinel magit-this-process #'magit-sequencer-process-sentinel)
  420. magit-this-process)
  421. (defvar magit-pre-start-git-hook nil)
  422. (defun magit-start-git (input &rest args)
  423. "Start Git, prepare for refresh, and return the process object.
  424. If INPUT is non-nil, it has to be a buffer or the name of an
  425. existing buffer. The buffer content becomes the processes
  426. standard input.
  427. Option `magit-git-executable' specifies the Git executable and
  428. option `magit-git-global-arguments' specifies constant arguments.
  429. The remaining arguments ARGS specify arguments to Git, they are
  430. flattened before use.
  431. After Git returns some buffers are refreshed: the buffer that was
  432. current when this function was called (if it is a Magit buffer
  433. and still alive), as well as the respective Magit status buffer.
  434. See `magit-start-process' for more information."
  435. (run-hooks 'magit-pre-start-git-hook)
  436. (let ((default-process-coding-system (magit--process-coding-system)))
  437. (apply #'magit-start-process magit-git-executable input
  438. (magit-process-git-arguments args))))
  439. (defun magit-start-process (program &optional input &rest args)
  440. "Start PROGRAM, prepare for refresh, and return the process object.
  441. If optional argument INPUT is non-nil, it has to be a buffer or
  442. the name of an existing buffer. The buffer content becomes the
  443. processes standard input.
  444. The process is started using `start-file-process' and then setup
  445. to use the sentinel `magit-process-sentinel' and the filter
  446. `magit-process-filter'. Information required by these functions
  447. is stored in the process object. When this function returns the
  448. process has not started to run yet so it is possible to override
  449. the sentinel and filter.
  450. After the process returns, `magit-process-sentinel' refreshes the
  451. buffer that was current when `magit-start-process' was called (if
  452. it is a Magit buffer and still alive), as well as the respective
  453. Magit status buffer."
  454. (pcase-let*
  455. ((`(,process-buf . ,section)
  456. (magit-process-setup program args))
  457. (process
  458. (let ((process-connection-type
  459. ;; Don't use a pty, because it would set icrnl
  460. ;; which would modify the input (issue #20).
  461. (and (not input) magit-process-connection-type))
  462. (process-environment (magit-process-environment))
  463. (default-process-coding-system (magit--process-coding-system)))
  464. (apply #'start-file-process
  465. (file-name-nondirectory program)
  466. process-buf program args))))
  467. (with-editor-set-process-filter process #'magit-process-filter)
  468. (set-process-sentinel process #'magit-process-sentinel)
  469. (set-process-buffer process process-buf)
  470. (when (eq system-type 'windows-nt)
  471. ;; On w32, git expects UTF-8 encoded input, ignore any user
  472. ;; configuration telling us otherwise.
  473. (set-process-coding-system process 'utf-8-unix))
  474. (process-put process 'section section)
  475. (process-put process 'command-buf (current-buffer))
  476. (process-put process 'default-dir default-directory)
  477. (when inhibit-magit-refresh
  478. (process-put process 'inhibit-refresh t))
  479. (oset section process process)
  480. (with-current-buffer process-buf
  481. (set-marker (process-mark process) (point)))
  482. (when input
  483. (with-current-buffer input
  484. (process-send-region process (point-min) (point-max))
  485. (process-send-eof process)))
  486. (setq magit-this-process process)
  487. (oset section value process)
  488. (magit-process-display-buffer process)
  489. process))
  490. (defun magit-parse-git-async (&rest args)
  491. (setq args (magit-process-git-arguments args))
  492. (let ((command-buf (current-buffer))
  493. (process-buf (generate-new-buffer " *temp*"))
  494. (toplevel (magit-toplevel)))
  495. (with-current-buffer process-buf
  496. (setq default-directory toplevel)
  497. (let ((process
  498. (let ((process-connection-type nil)
  499. (process-environment (magit-process-environment))
  500. (default-process-coding-system
  501. (magit--process-coding-system)))
  502. (apply #'start-file-process "git" process-buf
  503. magit-git-executable args))))
  504. (process-put process 'command-buf command-buf)
  505. (process-put process 'parsed (point))
  506. (setq magit-this-process process)
  507. process))))
  508. ;;; Process Internals
  509. (defun magit-process-setup (program args)
  510. (magit-process-set-mode-line program args)
  511. (let ((pwd default-directory)
  512. (buf (magit-process-buffer t)))
  513. (cons buf (with-current-buffer buf
  514. (prog1 (magit-process-insert-section pwd program args nil nil)
  515. (backward-char 1))))))
  516. (defun magit-process-insert-section (pwd program args &optional errcode errlog)
  517. (let ((inhibit-read-only t)
  518. (magit-insert-section--parent magit-root-section)
  519. (magit-insert-section--oldroot nil))
  520. (goto-char (1- (point-max)))
  521. (magit-insert-section (process)
  522. (insert (if errcode
  523. (format "%3s " (propertize (number-to-string errcode)
  524. 'font-lock-face 'magit-process-ng))
  525. "run "))
  526. (unless (equal (expand-file-name pwd)
  527. (expand-file-name default-directory))
  528. (insert (file-relative-name pwd default-directory) ?\s))
  529. (cond
  530. ((and args (equal program magit-git-executable))
  531. (setq args (-split-at (length magit-git-global-arguments) args))
  532. (insert (propertize (file-name-nondirectory program)
  533. 'font-lock-face 'magit-section-heading) " ")
  534. (insert (propertize (char-to-string magit-ellipsis)
  535. 'font-lock-face 'magit-section-heading
  536. 'help-echo (mapconcat #'identity (car args) " ")))
  537. (insert " ")
  538. (insert (propertize (mapconcat #'shell-quote-argument (cadr args) " ")
  539. 'font-lock-face 'magit-section-heading)))
  540. ((and args (equal program shell-file-name))
  541. (insert (propertize (cadr args)
  542. 'font-lock-face 'magit-section-heading)))
  543. (t
  544. (insert (propertize (file-name-nondirectory program)
  545. 'font-lock-face 'magit-section-heading) " ")
  546. (insert (propertize (mapconcat #'shell-quote-argument args " ")
  547. 'font-lock-face 'magit-section-heading))))
  548. (magit-insert-heading)
  549. (when errlog
  550. (if (bufferp errlog)
  551. (insert (with-current-buffer errlog
  552. (buffer-substring-no-properties (point-min) (point-max))))
  553. (insert-file-contents errlog)
  554. (goto-char (1- (point-max)))))
  555. (insert "\n"))))
  556. (defun magit-process-truncate-log ()
  557. (let* ((head nil)
  558. (tail (oref magit-root-section children))
  559. (count (length tail)))
  560. (when (> (1+ count) magit-process-log-max)
  561. (while (and (cdr tail)
  562. (> count (/ magit-process-log-max 2)))
  563. (let* ((inhibit-read-only t)
  564. (section (car tail))
  565. (process (oref section process)))
  566. (cond ((not process))
  567. ((memq (process-status process) '(exit signal))
  568. (delete-region (oref section start)
  569. (1+ (oref section end)))
  570. (cl-decf count))
  571. (t
  572. (push section head))))
  573. (pop tail))
  574. (oset magit-root-section children
  575. (nconc (reverse head) tail)))))
  576. (defun magit-process-sentinel (process event)
  577. "Default sentinel used by `magit-start-process'."
  578. (when (memq (process-status process) '(exit signal))
  579. (setq event (substring event 0 -1))
  580. (when (string-match "^finished" event)
  581. (message (concat (capitalize (process-name process)) " finished")))
  582. (magit-process-finish process)
  583. (when (eq process magit-this-process)
  584. (setq magit-this-process nil))
  585. (unless (process-get process 'inhibit-refresh)
  586. (let ((command-buf (process-get process 'command-buf)))
  587. (if (buffer-live-p command-buf)
  588. (with-current-buffer command-buf
  589. (magit-refresh))
  590. (with-temp-buffer
  591. (setq default-directory (process-get process 'default-dir))
  592. (magit-refresh)))))))
  593. (defun magit-sequencer-process-sentinel (process event)
  594. "Special sentinel used by `magit-run-git-sequencer'."
  595. (when (memq (process-status process) '(exit signal))
  596. (magit-process-sentinel process event)
  597. (when-let ((process-buf (process-buffer process)))
  598. (when (buffer-live-p process-buf)
  599. (when-let ((status-buf (with-current-buffer process-buf
  600. (magit-get-mode-buffer 'magit-status-mode))))
  601. (with-current-buffer status-buf
  602. (--when-let
  603. (magit-get-section
  604. `((commit . ,(magit-rev-parse "HEAD"))
  605. (,(pcase (car (cadr (-split-at
  606. (1+ (length magit-git-global-arguments))
  607. (process-command process))))
  608. ((or "rebase" "am") 'rebase-sequence)
  609. ((or "cherry-pick" "revert") 'sequence)))
  610. (status)))
  611. (goto-char (oref it start))
  612. (magit-section-update-highlight))))))))
  613. (defun magit-process-filter (proc string)
  614. "Default filter used by `magit-start-process'."
  615. (with-current-buffer (process-buffer proc)
  616. (let ((inhibit-read-only t))
  617. (goto-char (process-mark proc))
  618. ;; Find last ^M in string. If one was found, ignore
  619. ;; everything before it and delete the current line.
  620. (when-let ((ret-pos (cl-position ?\r string :from-end t)))
  621. (cl-callf substring string (1+ ret-pos))
  622. (delete-region (line-beginning-position) (point)))
  623. (insert (propertize string 'magit-section
  624. (process-get proc 'section)))
  625. (set-marker (process-mark proc) (point))
  626. ;; Make sure prompts are matched after removing ^M.
  627. (magit-process-yes-or-no-prompt proc string)
  628. (magit-process-username-prompt proc string)
  629. (magit-process-password-prompt proc string)
  630. (run-hook-with-args-until-success 'magit-process-prompt-functions
  631. proc string))))
  632. (defmacro magit-process-kill-on-abort (proc &rest body)
  633. (declare (indent 1) (debug (form body)))
  634. (let ((map (cl-gensym)))
  635. `(let ((,map (make-sparse-keymap)))
  636. (set-keymap-parent ,map minibuffer-local-map)
  637. (define-key ,map "\C-g"
  638. (lambda ()
  639. (interactive)
  640. (ignore-errors (kill-process ,proc))
  641. (abort-recursive-edit)))
  642. (let ((minibuffer-local-map ,map))
  643. ,@body))))
  644. (defun magit-process-yes-or-no-prompt (process string)
  645. "Forward Yes-or-No prompts to the user."
  646. (when-let ((beg (string-match magit-process-yes-or-no-prompt-regexp string)))
  647. (let ((max-mini-window-height 30))
  648. (process-send-string
  649. process
  650. (downcase
  651. (concat
  652. (match-string
  653. (if (save-match-data
  654. (magit-process-kill-on-abort process
  655. (yes-or-no-p (substring string 0 beg)))) 1 2)
  656. string)
  657. "\n"))))))
  658. (defun magit-process-password-auth-source (key)
  659. "Use `auth-source-search' to get a password.
  660. If found, return the password. Otherwise, return nil.
  661. To use this function add it to the appropriate hook
  662. (add-hook 'magit-process-find-password-functions
  663. 'magit-process-password-auth-source)
  664. KEY typically derives from a prompt such as:
  665. Password for 'https://tarsius@bitbucket.org'
  666. in which case it would be the string
  667. tarsius@bitbucket.org
  668. which matches the ~/.authinfo.gpg entry
  669. machine bitbucket.org login tarsius password 12345
  670. or iff that is undefined, for backward compatibility
  671. machine tarsius@bitbucket.org password 12345"
  672. (require 'auth-source)
  673. (and (string-match "\\`\\(.+\\)@\\([^@]+\\)\\'" key)
  674. (let* ((user (match-string 1 key))
  675. (host (match-string 2 key))
  676. (secret
  677. (plist-get
  678. (car (or (auth-source-search :max 1 :host host :user user)
  679. (auth-source-search :max 1 :host key)))
  680. :secret)))
  681. (if (functionp secret)
  682. (funcall secret)
  683. secret))))
  684. (defun magit-process-password-prompt (process string)
  685. "Find a password based on prompt STRING and send it to git.
  686. Use `magit-process-password-prompt-regexps' to find a known
  687. prompt. If and only if one is found, then call functions in
  688. `magit-process-find-password-functions' until one of them returns
  689. the password. If all function return nil, then read the password
  690. from the user."
  691. (when-let ((prompt (magit-process-match-prompt
  692. magit-process-password-prompt-regexps string)))
  693. (process-send-string
  694. process (magit-process-kill-on-abort process
  695. (concat (or (when-let ((key (match-string 99 string)))
  696. (run-hook-with-args-until-success
  697. 'magit-process-find-password-functions key))
  698. (read-passwd prompt))
  699. "\n")))))
  700. (defun magit-process-username-prompt (process string)
  701. "Forward username prompts to the user."
  702. (--when-let (magit-process-match-prompt
  703. magit-process-username-prompt-regexps string)
  704. (process-send-string
  705. process (magit-process-kill-on-abort process
  706. (concat (read-string it nil nil (user-login-name)) "\n")))))
  707. (defun magit-process-match-prompt (prompts string)
  708. "Match STRING against PROMPTS and set match data.
  709. Return the matched string suffixed with \": \", if needed."
  710. (when (--any-p (string-match it string) prompts)
  711. (let ((prompt (match-string 0 string)))
  712. (cond ((string-suffix-p ": " prompt) prompt)
  713. ((string-suffix-p ":" prompt) (concat prompt " "))
  714. (t (concat prompt ": "))))))
  715. (defun magit--process-coding-system ()
  716. (let ((fro (or magit-git-output-coding-system
  717. (car default-process-coding-system)))
  718. (to (cdr default-process-coding-system)))
  719. (if magit-process-ensure-unix-line-ending
  720. (cons (coding-system-change-eol-conversion fro 'unix)
  721. (coding-system-change-eol-conversion to 'unix))
  722. (cons fro to))))
  723. (defvar magit-credential-hook nil
  724. "Hook run before Git needs credentials.")
  725. (defvar magit-credential-cache-daemon-process nil)
  726. (defun magit-maybe-start-credential-cache-daemon ()
  727. "Maybe start a `git-credential-cache--daemon' process.
  728. If such a process is already running or if the value of option
  729. `magit-credential-cache-daemon-socket' is nil, then do nothing.
  730. Otherwise start the process passing the value of that options
  731. as argument."
  732. (unless (or (not magit-credential-cache-daemon-socket)
  733. (process-live-p magit-credential-cache-daemon-process)
  734. (memq magit-credential-cache-daemon-process
  735. (list-system-processes)))
  736. (setq magit-credential-cache-daemon-process
  737. (or (--first (let* ((attr (process-attributes it))
  738. (comm (cdr (assq 'comm attr)))
  739. (user (cdr (assq 'user attr))))
  740. (and (string= comm "git-credential-cache--daemon")
  741. (string= user user-login-name)))
  742. (list-system-processes))
  743. (condition-case nil
  744. (start-process "git-credential-cache--daemon"
  745. " *git-credential-cache--daemon*"
  746. magit-git-executable
  747. "credential-cache--daemon"
  748. magit-credential-cache-daemon-socket)
  749. ;; Some Git implementations (e.g. Windows) won't have
  750. ;; this program; if we fail the first time, stop trying.
  751. ((debug error)
  752. (remove-hook 'magit-credential-hook
  753. #'magit-maybe-start-credential-cache-daemon)))))))
  754. (add-hook 'magit-credential-hook #'magit-maybe-start-credential-cache-daemon)
  755. (defun tramp-sh-handle-start-file-process--magit-tramp-process-environment
  756. (fn name buffer program &rest args)
  757. (if magit-tramp-process-environment
  758. (apply fn name buffer
  759. (car magit-tramp-process-environment)
  760. (append (cdr magit-tramp-process-environment)
  761. (cons program args)))
  762. (apply fn name buffer program args)))
  763. (advice-add 'tramp-sh-handle-start-file-process :around
  764. 'tramp-sh-handle-start-file-process--magit-tramp-process-environment)
  765. (defun tramp-sh-handle-process-file--magit-tramp-process-environment
  766. (fn program &optional infile destination display &rest args)
  767. (if magit-tramp-process-environment
  768. (apply fn "env" infile destination display
  769. (append magit-tramp-process-environment
  770. (cons program args)))
  771. (apply fn program infile destination display args)))
  772. (advice-add 'tramp-sh-handle-process-file :around
  773. 'tramp-sh-handle-process-file--magit-tramp-process-environment)
  774. (defvar magit-mode-line-process-map
  775. (let ((map (make-sparse-keymap)))
  776. (define-key map (kbd "<mode-line> <mouse-1>")
  777. 'magit-process-buffer)
  778. map)
  779. "Keymap for `mode-line-process'.")
  780. (defun magit-process-set-mode-line (program args)
  781. "Display the git command (sans arguments) in the mode line."
  782. (when (equal program magit-git-executable)
  783. (setq args (nthcdr (length magit-git-global-arguments) args)))
  784. (let ((str (concat " " (propertize
  785. (concat (file-name-nondirectory program)
  786. (and args (concat " " (car args))))
  787. 'mouse-face 'highlight
  788. 'keymap magit-mode-line-process-map
  789. 'help-echo "mouse-1: Show process buffer"
  790. 'font-lock-face 'magit-mode-line-process))))
  791. (magit-repository-local-set 'mode-line-process str)
  792. (dolist (buf (magit-mode-get-buffers))
  793. (with-current-buffer buf
  794. (setq mode-line-process str)))
  795. (force-mode-line-update t)))
  796. (defun magit-process-set-mode-line-error-status (&optional error str)
  797. "Apply an error face to the string set by `magit-process-set-mode-line'.
  798. If ERROR is supplied, include it in the `mode-line-process' tooltip.
  799. If STR is supplied, it replaces the `mode-line-process' text."
  800. (setq str (or str (magit-repository-local-get 'mode-line-process)))
  801. (when str
  802. (setq error (format "%smouse-1: Show process buffer"
  803. (if (stringp error)
  804. (concat error "\n\n")
  805. "")))
  806. (setq str (concat " " (propertize
  807. (substring-no-properties str 1)
  808. 'mouse-face 'highlight
  809. 'keymap magit-mode-line-process-map
  810. 'help-echo error
  811. 'font-lock-face 'magit-mode-line-process-error)))
  812. (magit-repository-local-set 'mode-line-process str)
  813. (dolist (buf (magit-mode-get-buffers))
  814. (with-current-buffer buf
  815. (setq mode-line-process str)))
  816. (force-mode-line-update t)
  817. ;; We remove any error status from the mode line when a magit
  818. ;; buffer is refreshed (see `magit-refresh-buffer'), but we must
  819. ;; ensure that we ignore any refreshes during the remainder of the
  820. ;; current command -- otherwise a newly-set error status would be
  821. ;; removed before it was seen. We set a flag which prevents the
  822. ;; status from being removed prior to the next command, so that
  823. ;; the error status is guaranteed to remain visible until then.
  824. (let ((repokey (magit-repository-local-repository)))
  825. ;; The following closure captures the repokey value, and is
  826. ;; added to `pre-command-hook'.
  827. (cl-labels ((enable-magit-process-unset-mode-line
  828. () ;; Remove ourself from the hook variable, so
  829. ;; that we only run once.
  830. (remove-hook 'pre-command-hook
  831. #'enable-magit-process-unset-mode-line)
  832. ;; Clear the inhibit flag for the repository in
  833. ;; which we set it.
  834. (magit-repository-local-set
  835. 'inhibit-magit-process-unset-mode-line nil repokey)))
  836. ;; Set the inhibit flag until the next command is invoked.
  837. (magit-repository-local-set
  838. 'inhibit-magit-process-unset-mode-line t repokey)
  839. (add-hook 'pre-command-hook
  840. #'enable-magit-process-unset-mode-line)))))
  841. (defun magit-process-unset-mode-line-error-status ()
  842. "Remove any current error status from the mode line."
  843. (let ((status (or mode-line-process
  844. (magit-repository-local-get 'mode-line-process))))
  845. (when (and status
  846. (eq (get-text-property 1 'font-lock-face status)
  847. 'magit-mode-line-process-error))
  848. (magit-process-unset-mode-line))))
  849. (defun magit-process-unset-mode-line (&optional directory)
  850. "Remove the git command from the mode line."
  851. (let ((default-directory (or directory default-directory)))
  852. (unless (magit-repository-local-get 'inhibit-magit-process-unset-mode-line)
  853. (magit-repository-local-set 'mode-line-process nil)
  854. (dolist (buf (magit-mode-get-buffers))
  855. (with-current-buffer buf (setq mode-line-process nil)))
  856. (force-mode-line-update t))))
  857. (defvar magit-process-error-message-regexps
  858. (list "^\\*ERROR\\*: Canceled by user$"
  859. "^\\(?:error\\|fatal\\|git\\): \\(.*\\)$"
  860. "^\\(Cannot rebase:.*\\)$"))
  861. (define-error 'magit-git-error "Git error")
  862. (defun magit-process-error-summary (process-buf section)
  863. "A one-line error summary from the given SECTION."
  864. (or (and (buffer-live-p process-buf)
  865. (with-current-buffer process-buf
  866. (and (oref section content)
  867. (save-excursion
  868. (goto-char (oref section end))
  869. (run-hook-wrapped
  870. 'magit-process-error-message-regexps
  871. (lambda (re)
  872. (save-excursion
  873. (and (re-search-backward
  874. re (oref section start) t)
  875. (or (match-string-no-properties 1)
  876. (and (not magit-process-raise-error)
  877. 'suppressed))))))))))
  878. "Git failed"))
  879. (defun magit-process-error-tooltip (process-buf section)
  880. "Returns the text from SECTION of the PROCESS-BUF buffer.
  881. Limited by `magit-process-error-tooltip-max-lines'."
  882. (and (integerp magit-process-error-tooltip-max-lines)
  883. (> magit-process-error-tooltip-max-lines 0)
  884. (buffer-live-p process-buf)
  885. (with-current-buffer process-buf
  886. (save-excursion
  887. (goto-char (or (oref section content)
  888. (oref section start)))
  889. (buffer-substring-no-properties
  890. (point)
  891. (save-excursion
  892. (forward-line magit-process-error-tooltip-max-lines)
  893. (goto-char
  894. (if (> (point) (oref section end))
  895. (oref section end)
  896. (point)))
  897. ;; Remove any trailing whitespace.
  898. (when (re-search-backward "[^[:space:]\n]"
  899. (oref section start) t)
  900. (forward-char 1))
  901. (point)))))))
  902. (defvar-local magit-this-error nil)
  903. (defvar magit-process-finish-apply-ansi-colors nil)
  904. (defun magit-process-finish (arg &optional process-buf command-buf
  905. default-dir section)
  906. (unless (integerp arg)
  907. (setq process-buf (process-buffer arg))
  908. (setq command-buf (process-get arg 'command-buf))
  909. (setq default-dir (process-get arg 'default-dir))
  910. (setq section (process-get arg 'section))
  911. (setq arg (process-exit-status arg)))
  912. (when (fboundp 'dired-uncache)
  913. (dired-uncache default-dir))
  914. (when (buffer-live-p process-buf)
  915. (with-current-buffer process-buf
  916. (let ((inhibit-read-only t)
  917. (marker (oref section start)))
  918. (goto-char marker)
  919. (save-excursion
  920. (delete-char 3)
  921. (set-marker-insertion-type marker nil)
  922. (insert (propertize (format "%3s" arg)
  923. 'magit-section section
  924. 'font-lock-face (if (= arg 0)
  925. 'magit-process-ok
  926. 'magit-process-ng)))
  927. (set-marker-insertion-type marker t))
  928. (when magit-process-finish-apply-ansi-colors
  929. (ansi-color-apply-on-region (oref section content)
  930. (oref section end)))
  931. (if (= (oref section end)
  932. (+ (line-end-position) 2))
  933. (save-excursion
  934. (goto-char (1+ (line-end-position)))
  935. (delete-char -1)
  936. (oset section content nil))
  937. (let ((buf (magit-process-buffer t)))
  938. (when (and (= arg 0)
  939. (not (--any-p (eq (window-buffer it) buf)
  940. (window-list))))
  941. (magit-section-hide section)))))))
  942. (if (= arg 0)
  943. ;; Unset the `mode-line-process' value upon success.
  944. (magit-process-unset-mode-line default-dir)
  945. ;; Otherwise process the error.
  946. (let ((msg (magit-process-error-summary process-buf section)))
  947. ;; Change `mode-line-process' to an error face upon failure.
  948. (if magit-process-display-mode-line-error
  949. (magit-process-set-mode-line-error-status
  950. (or (magit-process-error-tooltip process-buf section)
  951. msg))
  952. (magit-process-unset-mode-line default-dir))
  953. ;; Either signal the error, or else display the error summary in
  954. ;; the status buffer and with a message in the echo area.
  955. (cond
  956. (magit-process-raise-error
  957. (signal 'magit-git-error (list (format "%s (in %s)" msg default-dir))))
  958. ((not (eq msg 'suppressed))
  959. (when (buffer-live-p process-buf)
  960. (with-current-buffer process-buf
  961. (when-let ((status-buf (magit-get-mode-buffer 'magit-status-mode)))
  962. (with-current-buffer status-buf
  963. (setq magit-this-error msg)))))
  964. (message "%s ... [%s buffer %s for details]" msg
  965. (if-let ((key (and (buffer-live-p command-buf)
  966. (with-current-buffer command-buf
  967. (car (where-is-internal
  968. 'magit-process-buffer))))))
  969. (format "Hit %s to see" (key-description key))
  970. "See")
  971. (buffer-name process-buf))))))
  972. arg)
  973. (defun magit-process-display-buffer (process)
  974. (when (process-live-p process)
  975. (let ((buf (process-buffer process)))
  976. (cond ((not (buffer-live-p buf)))
  977. ((= magit-process-popup-time 0)
  978. (if (minibufferp)
  979. (switch-to-buffer-other-window buf)
  980. (pop-to-buffer buf)))
  981. ((> magit-process-popup-time 0)
  982. (run-with-timer magit-process-popup-time nil
  983. (lambda (p)
  984. (when (eq (process-status p) 'run)
  985. (let ((buf (process-buffer p)))
  986. (when (buffer-live-p buf)
  987. (if (minibufferp)
  988. (switch-to-buffer-other-window buf)
  989. (pop-to-buffer buf))))))
  990. process))))))
  991. (defun magit--log-action (summary line list)
  992. (let (heading lines)
  993. (if (cdr list)
  994. (progn (setq heading (funcall summary list))
  995. (setq lines (mapcar line list)))
  996. (setq heading (funcall line (car list))))
  997. (with-current-buffer (magit-process-buffer t)
  998. (goto-char (1- (point-max)))
  999. (let ((inhibit-read-only t))
  1000. (magit-insert-section (message)
  1001. (magit-insert-heading (concat " * " heading))
  1002. (when lines
  1003. (dolist (line lines)
  1004. (insert line "\n"))
  1005. (insert "\n"))))
  1006. (let ((inhibit-message t))
  1007. (when heading
  1008. (setq lines (cons heading lines)))
  1009. (message (mapconcat #'identity lines "\n"))))))
  1010. ;;; _
  1011. (provide 'magit-process)
  1012. ;;; magit-process.el ends here