Klimi's new dotfiles with stow.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

300 行
11 KiB

  1. ;;; magit-merge.el --- merge 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 merge commands.
  22. ;;; Code:
  23. (eval-when-compile
  24. (require 'subr-x))
  25. (require 'magit)
  26. (declare-function magit-git-push "magit-push" (branch target args))
  27. ;;; Commands
  28. ;;;###autoload (autoload 'magit-merge "magit" nil t)
  29. (define-transient-command magit-merge ()
  30. "Merge branches."
  31. :man-page "git-merge"
  32. :incompatible '(("--ff-only" "--no-ff"))
  33. ["Arguments"
  34. :if-not magit-merge-in-progress-p
  35. ("-f" "Fast-forward only" "--ff-only")
  36. ("-n" "No fast-forward" "--no-ff")
  37. (magit-merge:--strategy)
  38. (5 magit:--gpg-sign)]
  39. ["Actions"
  40. :if-not magit-merge-in-progress-p
  41. [("m" "Merge" magit-merge-plain)
  42. ("e" "Merge and edit message" magit-merge-editmsg)
  43. ("n" "Merge but don't commit" magit-merge-nocommit)
  44. ("a" "Absorb" magit-merge-absorb)]
  45. [("p" "Preview merge" magit-merge-preview)
  46. ""
  47. ("s" "Squash merge" magit-merge-squash)
  48. ("i" "Merge into" magit-merge-into)]]
  49. ["Actions"
  50. :if magit-merge-in-progress-p
  51. ("m" "Commit merge" magit-commit-create)
  52. ("a" "Abort merge" magit-merge-abort)])
  53. (defun magit-merge-arguments ()
  54. (transient-args 'magit-merge))
  55. (define-infix-argument magit-merge:--strategy ()
  56. :description "Strategy"
  57. :class 'transient-option
  58. ;; key for merge: "-s"
  59. ;; key for cherry-pick and revert: "=s"
  60. ;; shortarg for merge: "-s"
  61. ;; shortarg for cherry-pick and revert: none
  62. :key "-s"
  63. :argument "--strategy="
  64. :choices '("resolve" "recursive" "octopus" "ours" "subtree"))
  65. ;;;###autoload
  66. (defun magit-merge-plain (rev &optional args nocommit)
  67. "Merge commit REV into the current branch; using default message.
  68. Unless there are conflicts or a prefix argument is used create a
  69. merge commit using a generic commit message and without letting
  70. the user inspect the result. With a prefix argument pretend the
  71. merge failed to give the user the opportunity to inspect the
  72. merge.
  73. \(git merge --no-edit|--no-commit [ARGS] REV)"
  74. (interactive (list (magit-read-other-branch-or-commit "Merge")
  75. (magit-merge-arguments)
  76. current-prefix-arg))
  77. (magit-merge-assert)
  78. (magit-run-git-async "merge" (if nocommit "--no-commit" "--no-edit") args rev))
  79. ;;;###autoload
  80. (defun magit-merge-editmsg (rev &optional args)
  81. "Merge commit REV into the current branch; and edit message.
  82. Perform the merge and prepare a commit message but let the user
  83. edit it.
  84. \n(git merge --edit --no-ff [ARGS] REV)"
  85. (interactive (list (magit-read-other-branch-or-commit "Merge")
  86. (magit-merge-arguments)))
  87. (magit-merge-assert)
  88. (cl-pushnew "--no-ff" args :test #'equal)
  89. (apply #'magit-run-git-with-editor "merge" "--edit"
  90. (append (delete "--ff-only" args)
  91. (list rev))))
  92. ;;;###autoload
  93. (defun magit-merge-nocommit (rev &optional args)
  94. "Merge commit REV into the current branch; pretending it failed.
  95. Pretend the merge failed to give the user the opportunity to
  96. inspect the merge and change the commit message.
  97. \n(git merge --no-commit --no-ff [ARGS] REV)"
  98. (interactive (list (magit-read-other-branch-or-commit "Merge")
  99. (magit-merge-arguments)))
  100. (magit-merge-assert)
  101. (cl-pushnew "--no-ff" args :test #'equal)
  102. (magit-run-git-async "merge" "--no-commit" args rev))
  103. ;;;###autoload
  104. (defun magit-merge-into (branch &optional args)
  105. "Merge the current branch into BRANCH and remove the former.
  106. Before merging, force push the source branch to its push-remote,
  107. provided the respective remote branch already exists, ensuring
  108. that the respective pull-request (if any) won't get stuck on some
  109. obsolete version of the commits that are being merged. Finally
  110. if `forge-branch-pullreq' was used to create the merged branch,
  111. branch, then also remove the respective remote branch."
  112. (interactive
  113. (list (magit-read-other-local-branch
  114. (format "Merge `%s' into" (magit-get-current-branch))
  115. nil
  116. (when-let ((upstream (magit-get-upstream-branch))
  117. (upstream (cdr (magit-split-branch-name upstream))))
  118. (and (magit-branch-p upstream) upstream)))
  119. (magit-merge-arguments)))
  120. (let ((current (magit-get-current-branch)))
  121. (when (zerop (magit-call-git "checkout" branch))
  122. (magit--merge-absorb current args))))
  123. ;;;###autoload
  124. (defun magit-merge-absorb (branch &optional args)
  125. "Merge BRANCH into the current branch and remove the former.
  126. Before merging, force push the source branch to its push-remote,
  127. provided the respective remote branch already exists, ensuring
  128. that the respective pull-request (if any) won't get stuck on some
  129. obsolete version of the commits that are being merged. Finally
  130. if `forge-branch-pullreq' was used to create the merged branch,
  131. then also remove the respective remote branch."
  132. (interactive (list (magit-read-other-local-branch "Absorb branch")
  133. (magit-merge-arguments)))
  134. (magit--merge-absorb branch args))
  135. (defun magit--merge-absorb (branch args)
  136. (when (equal branch "master")
  137. (unless (yes-or-no-p
  138. "Do you really want to merge `master' into another branch? ")
  139. (user-error "Abort")))
  140. (if-let ((target (magit-get-push-branch branch t)))
  141. (progn
  142. (magit-git-push branch target (list "--force-with-lease"))
  143. (set-process-sentinel
  144. magit-this-process
  145. (lambda (process event)
  146. (when (memq (process-status process) '(exit signal))
  147. (if (not (zerop (process-exit-status process)))
  148. (magit-process-sentinel process event)
  149. (process-put process 'inhibit-refresh t)
  150. (magit-process-sentinel process event)
  151. (magit--merge-absorb-1 branch args))))))
  152. (magit--merge-absorb-1 branch args)))
  153. (defun magit--merge-absorb-1 (branch args)
  154. (if-let ((pr (magit-get "branch" branch "pullRequest")))
  155. (magit-run-git-async
  156. "merge" args "-m"
  157. (format "Merge branch '%s'%s [%s]"
  158. branch
  159. (let ((current (magit-get-current-branch)))
  160. (if (equal current "master") "" (format " into %s" current)))
  161. pr)
  162. branch)
  163. (magit-run-git-async "merge" args "--no-edit" branch))
  164. (set-process-sentinel
  165. magit-this-process
  166. (lambda (process event)
  167. (when (memq (process-status process) '(exit signal))
  168. (if (> (process-exit-status process) 0)
  169. (magit-process-sentinel process event)
  170. (process-put process 'inhibit-refresh t)
  171. (magit-process-sentinel process event)
  172. (magit-branch-maybe-delete-pr-remote branch)
  173. (magit-branch-unset-pushRemote branch)
  174. (magit-run-git "branch" "-D" branch))))))
  175. ;;;###autoload
  176. (defun magit-merge-squash (rev)
  177. "Squash commit REV into the current branch; don't create a commit.
  178. \n(git merge --squash REV)"
  179. (interactive (list (magit-read-other-branch-or-commit "Squash")))
  180. (magit-merge-assert)
  181. (magit-run-git-async "merge" "--squash" rev))
  182. ;;;###autoload
  183. (defun magit-merge-preview (rev)
  184. "Preview result of merging REV into the current branch."
  185. (interactive (list (magit-read-other-branch-or-commit "Preview merge")))
  186. (magit-merge-preview-setup-buffer rev))
  187. ;;;###autoload
  188. (defun magit-merge-abort ()
  189. "Abort the current merge operation.
  190. \n(git merge --abort)"
  191. (interactive)
  192. (unless (file-exists-p (magit-git-dir "MERGE_HEAD"))
  193. (user-error "No merge in progress"))
  194. (magit-confirm 'abort-merge)
  195. (magit-run-git-async "merge" "--abort"))
  196. (defun magit-checkout-stage (file arg)
  197. "During a conflict checkout and stage side, or restore conflict."
  198. (interactive
  199. (let ((file (magit-completing-read "Checkout file"
  200. (magit-tracked-files) nil nil nil
  201. 'magit-read-file-hist
  202. (magit-current-file))))
  203. (cond ((member file (magit-unmerged-files))
  204. (list file (magit-checkout-read-stage file)))
  205. ((yes-or-no-p (format "Restore conflicts in %s? " file))
  206. (list file "--merge"))
  207. (t
  208. (user-error "Quit")))))
  209. (pcase (cons arg (cddr (car (magit-file-status file))))
  210. ((or `("--ours" ?D ,_)
  211. `("--theirs" ,_ ?D))
  212. (magit-run-git "rm" "--" file))
  213. (_ (if (equal arg "--merge")
  214. ;; This fails if the file was deleted on one
  215. ;; side. And we cannot do anything about it.
  216. (magit-run-git "checkout" "--merge" "--" file)
  217. (magit-call-git "checkout" arg "--" file)
  218. (magit-run-git "add" "-u" "--" file)))))
  219. ;;; Utilities
  220. (defun magit-merge-in-progress-p ()
  221. (file-exists-p (magit-git-dir "MERGE_HEAD")))
  222. (defun magit--merge-range (&optional head)
  223. (unless head
  224. (setq head (magit-get-shortname
  225. (car (magit-file-lines (magit-git-dir "MERGE_HEAD"))))))
  226. (and head
  227. (concat (magit-git-string "merge-base" "--octopus" "HEAD" head)
  228. ".." head)))
  229. (defun magit-merge-assert ()
  230. (or (not (magit-anything-modified-p t))
  231. (magit-confirm 'merge-dirty
  232. "Merging with dirty worktree is risky. Continue")))
  233. (defun magit-checkout-read-stage (file)
  234. (magit-read-char-case (format "For %s checkout: " file) t
  235. (?o "[o]ur stage" "--ours")
  236. (?t "[t]heir stage" "--theirs")
  237. (?c "[c]onflict" "--merge")))
  238. ;;; Sections
  239. (defvar magit-unmerged-section-map
  240. (let ((map (make-sparse-keymap)))
  241. (define-key map [remap magit-visit-thing] 'magit-diff-dwim)
  242. map)
  243. "Keymap for `unmerged' sections.")
  244. (defun magit-insert-merge-log ()
  245. "Insert section for the on-going merge.
  246. Display the heads that are being merged.
  247. If no merge is in progress, do nothing."
  248. (when (magit-merge-in-progress-p)
  249. (let* ((heads (mapcar #'magit-get-shortname
  250. (magit-file-lines (magit-git-dir "MERGE_HEAD"))))
  251. (range (magit--merge-range (car heads))))
  252. (magit-insert-section (unmerged range)
  253. (magit-insert-heading
  254. (format "Merging %s:" (mapconcat #'identity heads ", ")))
  255. (magit-insert-log
  256. range
  257. (let ((args magit-buffer-log-args))
  258. (unless (member "--decorate=full" magit-buffer-log-args)
  259. (push "--decorate=full" args))
  260. args))))))
  261. ;;; _
  262. (provide 'magit-merge)
  263. ;;; magit-merge.el ends here