mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-21 e75a20334813452c6912c090d70a0de2c805f94d
commit | author | age
5cb5f7 1 ;;; magit-merge.el --- merge functionality  -*- lexical-binding: t -*-
C 2
3 ;; Copyright (C) 2010-2018  The Magit Project Contributors
4 ;;
5 ;; You should have received a copy of the AUTHORS.md file which
6 ;; lists all contributors.  If not, see http://magit.vc/authors.
7
8 ;; Author: Jonas Bernoulli <jonas@bernoul.li>
9 ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
10
11 ;; Magit is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 3, or (at your option)
14 ;; any later version.
15 ;;
16 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
17 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
19 ;; License for more details.
20 ;;
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with Magit.  If not, see http://www.gnu.org/licenses.
23
24 ;;; Commentary:
25
26 ;; This library implements merge commands.
27
28 ;;; Code:
29
30 (eval-when-compile
31   (require 'subr-x))
32
33 (require 'magit)
34
35 (declare-function magit-git-push "magit-push" (branch target args))
36
37 ;;; Commands
38
39 ;;;###autoload (autoload 'magit-merge-popup "magit" nil t)
40 (magit-define-popup magit-merge-popup
41   "Popup console for merge commands."
42   :man-page "git-merge"
43   :switches '((?f "Fast-forward only" "--ff-only")
44               (?n "No fast-forward"   "--no-ff"))
45   :options  '((?s "Strategy" "--strategy="))
46   :actions  '((?m "Merge"                  magit-merge-plain)
47               (?p "Preview merge"          magit-merge-preview)
48               (?e "Merge and edit message" magit-merge-editmsg) nil
49               (?n "Merge but don't commit" magit-merge-nocommit)
50               (?s "Squash merge"           magit-merge-squash)
51               (?a "Absorb"                 magit-merge-absorb)
52               (?i "Merge into"             magit-merge-into))
53   :sequence-actions   '((?m "Commit merge" magit-commit-create)
54                         (?a "Abort merge"  magit-merge-abort))
55   :sequence-predicate 'magit-merge-in-progress-p
56   :default-action 'magit-merge
57   :max-action-columns 2)
58
59 ;;;###autoload
60 (defun magit-merge-plain (rev &optional args nocommit)
61   "Merge commit REV into the current branch; using default message.
62
63 Unless there are conflicts or a prefix argument is used create a
64 merge commit using a generic commit message and without letting
65 the user inspect the result.  With a prefix argument pretend the
66 merge failed to give the user the opportunity to inspect the
67 merge.
68
69 \(git merge --no-edit|--no-commit [ARGS] REV)"
70   (interactive (list (magit-read-other-branch-or-commit "Merge")
71                      (magit-merge-arguments)
72                      current-prefix-arg))
73   (magit-merge-assert)
74   (magit-run-git-async "merge" (if nocommit "--no-commit" "--no-edit") args rev))
75
76 ;;;###autoload
77 (defun magit-merge-editmsg (rev &optional args)
78   "Merge commit REV into the current branch; and edit message.
79 Perform the merge and prepare a commit message but let the user
80 edit it.
81 \n(git merge --edit --no-ff [ARGS] REV)"
82   (interactive (list (magit-read-other-branch-or-commit "Merge")
83                      (magit-merge-arguments)))
84   (magit-merge-assert)
85   (cl-pushnew "--no-ff" args :test #'equal)
86   (apply #'magit-run-git-with-editor "merge" "--edit"
87          (append args (list rev))))
88
89 ;;;###autoload
90 (defun magit-merge-nocommit (rev &optional args)
91   "Merge commit REV into the current branch; pretending it failed.
92 Pretend the merge failed to give the user the opportunity to
93 inspect the merge and change the commit message.
94 \n(git merge --no-commit --no-ff [ARGS] REV)"
95   (interactive (list (magit-read-other-branch-or-commit "Merge")
96                      (magit-merge-arguments)))
97   (magit-merge-assert)
98   (cl-pushnew "--no-ff" args :test #'equal)
99   (magit-run-git-async "merge" "--no-commit" args rev))
100
101 ;;;###autoload
102 (defun magit-merge-into (branch &optional args)
103   "Merge the current branch into BRANCH and remove the former.
104
105 Before merging, force push the source branch to its push-remote,
106 provided the respective remote branch already exists, ensuring
107 that the respective pull-request (if any) won't get stuck on some
108 obsolete version of the commits that are being merged.  Finally
109 if `magit-branch-pull-request' was used to create the merged
110 branch, then also remove the respective remote branch."
111   (interactive
112    (list (magit-read-other-local-branch
113           (format "Merge `%s' into" (magit-get-current-branch))
114           nil
115           (when-let ((upstream (magit-get-upstream-branch)))
116             (when-let ((upstream (cdr (magit-split-branch-name upstream))))
117               (and (magit-branch-p upstream) upstream))))
118          (magit-merge-arguments)))
119   (let ((current (magit-get-current-branch)))
120     (when (zerop (magit-call-git "checkout" branch))
121       (magit--merge-absort current args))))
122
123 ;;;###autoload
124 (defun magit-merge-absorb (branch &optional args)
125   "Merge BRANCH into the current branch and remove the former.
126
127 Before merging, force push the source branch to its push-remote,
128 provided the respective remote branch already exists, ensuring
129 that the respective pull-request (if any) won't get stuck on some
130 obsolete version of the commits that are being merged.  Finally
131 if `magit-branch-pull-request' was used to create the merged
132 branch, then also remove the respective remote branch."
133   (interactive (list (magit-read-other-local-branch "Absorb branch")
134                      (magit-merge-arguments)))
135   (magit--merge-absort branch args))
136
137 (defun magit--merge-absort (branch args)
138   (when (equal branch "master")
139     (unless (yes-or-no-p
140              "Do you really want to merge `master' into another branch? ")
141       (user-error "Abort")))
142   (if-let ((target (magit-get-push-branch branch t)))
143       (progn
144         (magit-git-push branch target (list "--force-with-lease"))
145         (set-process-sentinel
146          magit-this-process
147          (lambda (process event)
148            (when (memq (process-status process) '(exit signal))
149              (if (not (zerop (process-exit-status process)))
150                  (magit-process-sentinel process event)
151                (process-put process 'inhibit-refresh t)
152                (magit-process-sentinel process event)
153                (magit--merge-absort-1 branch args))))))
154     (magit--merge-absort-1 branch args)))
155
156 (defun magit--merge-absort-1 (branch args)
157   (magit-run-git-async "merge" args "--no-edit" branch)
158   (set-process-sentinel
159    magit-this-process
160    (lambda (process event)
161      (when (memq (process-status process) '(exit signal))
162        (if (> (process-exit-status process) 0)
163            (magit-process-sentinel process event)
164          (process-put process 'inhibit-refresh t)
165          (magit-process-sentinel process event)
166          (magit-branch-maybe-delete-pr-remote branch)
167          (magit-branch-unset-pushRemote branch)
168          (magit-run-git "branch" "-D" branch))))))
169
170 ;;;###autoload
171 (defun magit-merge-squash (rev)
172   "Squash commit REV into the current branch; don't create a commit.
173 \n(git merge --squash REV)"
174   (interactive (list (magit-read-other-branch-or-commit "Squash")))
175   (magit-merge-assert)
176   (magit-run-git-async "merge" "--squash" rev))
177
178 ;;;###autoload
179 (defun magit-merge-preview (rev)
180   "Preview result of merging REV into the current branch."
181   (interactive (list (magit-read-other-branch-or-commit "Preview merge")))
182   (magit-mode-setup #'magit-merge-preview-mode rev))
183
184 (define-derived-mode magit-merge-preview-mode magit-diff-mode "Magit Merge"
185   "Mode for previewing a merge."
186   :group 'magit-diff
187   (hack-dir-local-variables-non-file-buffer))
188
189 (defun magit-merge-preview-refresh-buffer (rev)
190   (let* ((branch (magit-get-current-branch))
191          (head (or branch (magit-rev-verify "HEAD"))))
192     (magit-set-header-line-format (format "Preview merge of %s into %s"
193                                           rev
194                                           (or branch "HEAD")))
195     (magit-insert-section (diffbuf)
196       (magit-git-wash #'magit-diff-wash-diffs
197         "merge-tree" (magit-git-string "merge-base" head rev) head rev))))
198
199 ;;;###autoload
200 (defun magit-merge-abort ()
201   "Abort the current merge operation.
202 \n(git merge --abort)"
203   (interactive)
204   (unless (file-exists-p (magit-git-dir "MERGE_HEAD"))
205     (user-error "No merge in progress"))
206   (magit-confirm 'abort-merge)
207   (magit-run-git-async "merge" "--abort"))
208
209 (defun magit-checkout-stage (file arg)
210   "During a conflict checkout and stage side, or restore conflict."
211   (interactive
212    (let ((file (magit-completing-read "Checkout file"
213                                       (magit-tracked-files) nil nil nil
214                                       'magit-read-file-hist
215                                       (magit-current-file))))
216      (cond ((member file (magit-unmerged-files))
217             (list file (magit-checkout-read-stage file)))
218            ((yes-or-no-p (format "Restore conflicts in %s? " file))
219             (list file "--merge"))
220            (t
221             (user-error "Quit")))))
222   (pcase (cons arg (cddr (car (magit-file-status file))))
223     ((or `("--ours"   ?D ,_)
224          `("--theirs" ,_ ?D))
225      (magit-run-git "rm" "--" file))
226     (_ (if (equal arg "--merge")
227            ;; This fails if the file was deleted on one
228            ;; side.  And we cannot do anything about it.
229            (magit-run-git "checkout" "--merge" "--" file)
230          (magit-call-git "checkout" arg "--" file)
231          (magit-run-git "add" "-u" "--" file)))))
232
233 ;;; Utilities
234
235 (defun magit-merge-in-progress-p ()
236   (file-exists-p (magit-git-dir "MERGE_HEAD")))
237
238 (defun magit--merge-range (&optional head)
239   (unless head
240     (setq head (magit-get-shortname
241                 (car (magit-file-lines (magit-git-dir "MERGE_HEAD"))))))
242   (and head
243        (concat (magit-git-string "merge-base" "--octopus" "HEAD" head)
244                ".." head)))
245
246 (defun magit-merge-assert ()
247   (or (not (magit-anything-modified-p t))
248       (magit-confirm 'merge-dirty
249         "Merging with dirty worktree is risky.  Continue")))
250
251 (defun magit-checkout-read-stage (file)
252   (magit-read-char-case (format "For %s checkout: " file) t
253     (?o "[o]ur stage"   "--ours")
254     (?t "[t]heir stage" "--theirs")
255     (?c "[c]onflict"    "--merge")))
256
257 ;;; Sections
258
259 (defvar magit-unmerged-section-map
260   (let ((map (make-sparse-keymap)))
261     (define-key map [remap magit-visit-thing] 'magit-diff-dwim)
262     map)
263   "Keymap for `unmerged' sections.")
264
265 (defun magit-insert-merge-log ()
266   "Insert section for the on-going merge.
267 Display the heads that are being merged.
268 If no merge is in progress, do nothing."
269   (when (magit-merge-in-progress-p)
270     (let* ((heads (mapcar #'magit-get-shortname
271                           (magit-file-lines (magit-git-dir "MERGE_HEAD"))))
272            (range (magit--merge-range (car heads))))
273       (magit-insert-section (unmerged range)
274         (magit-insert-heading
275           (format "Merging %s:" (mapconcat #'identity heads ", ")))
276         (magit-insert-log
277          range
278          (let ((args magit-log-section-arguments))
279            (unless (member "--decorate=full" magit-log-section-arguments)
280              (push "--decorate=full" args))
281            args))))))
282
283 ;;; _
284 (provide 'magit-merge)
285 ;;; magit-merge.el ends here