Merge changes from emacs-23 branch
[bpt/emacs.git] / lisp / vc / ediff-merg.el
... / ...
CommitLineData
1;;; ediff-merg.el --- merging utilities
2
3;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4;; 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
5
6;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
7;; Package: ediff
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24;;; Commentary:
25
26;;; Code:
27
28
29;; compiler pacifier
30(defvar ediff-window-A)
31(defvar ediff-window-B)
32(defvar ediff-window-C)
33(defvar ediff-merge-window-share)
34(defvar ediff-window-config-saved)
35
36(eval-when-compile
37 (require 'ediff-util))
38;; end pacifier
39
40(require 'ediff-init)
41
42(defcustom ediff-quit-merge-hook 'ediff-maybe-save-and-delete-merge
43 "Hooks to run before quitting a merge job.
44The most common use is to save and delete the merge buffer."
45 :type 'hook
46 :group 'ediff-merge)
47
48
49(defcustom ediff-default-variant 'combined
50 "The variant to be used as a default for buffer C in merging.
51Valid values are the symbols `default-A', `default-B', and `combined'."
52 :type '(radio (const default-A) (const default-B) (const combined))
53 :group 'ediff-merge)
54
55(defcustom ediff-combination-pattern
56 '("<<<<<<< variant A" A ">>>>>>> variant B" B "####### Ancestor" Ancestor "======= end")
57 "Pattern to be used for combining difference regions in buffers A and B.
58The value must be a list of the form
59\(STRING1 bufspec1 STRING2 bufspec2 STRING3 bufspec3 STRING4)
60where bufspec is the symbol A, B, or Ancestor. For instance, if the value is
61'(STRING1 A STRING2 Ancestor STRING3 B STRING4) then the
62combined text will look like this:
63
64STRING1
65diff region from variant A
66STRING2
67diff region from the ancestor
68STRING3
69diff region from variant B
70STRING4
71"
72 :type '(choice (list string symbol string symbol string)
73 (list string symbol string symbol string symbol string))
74 :group 'ediff-merge)
75
76(defcustom ediff-show-clashes-only nil
77 "If t, show only those diff regions where both buffers disagree with the ancestor.
78This means that regions that have status prefer-A or prefer-B will be
79skipped over. A value of nil means show all regions."
80 :type 'boolean
81 :group 'ediff-merge
82 )
83(make-variable-buffer-local 'ediff-show-clashes-only)
84
85(defcustom ediff-skip-merge-regions-that-differ-from-default nil
86 "If t, show only the regions that have not been changed by the user.
87A region is considered to have been changed if it is different from the current
88default (`default-A', `default-B', `combined') and it hasn't been marked as
89`prefer-A' or `prefer-B'.
90A region is considered to have been changed also when it is marked as
91as `prefer-A', but is different from the corresponding difference region in
92Buffer A or if it is marked as `prefer-B' and is different from the region in
93Buffer B."
94 :type 'boolean
95 :group 'ediff-merge
96 )
97(make-variable-buffer-local 'ediff-skip-merge-regions-that-differ-from-default)
98
99;; check if there is no clash between the ancestor and one of the variants.
100;; if it is not a merge job then return true
101(defun ediff-merge-region-is-non-clash (n)
102 (if (ediff-merge-job)
103 (string-match "prefer" (or (ediff-get-state-of-merge n) ""))
104 t))
105
106;; If ediff-show-clashes-only, check if there is no clash between the ancestor
107;; and one of the variants.
108(defun ediff-merge-region-is-non-clash-to-skip (n)
109 (and (ediff-merge-job)
110 ediff-show-clashes-only
111 (ediff-merge-region-is-non-clash n)))
112
113;; If ediff-skip-changed-regions, check if the merge region differs from
114;; the current default. If a region is different from the default, it means
115;; that the user has made determination as to how to merge for this particular
116;; region.
117(defun ediff-skip-merge-region-if-changed-from-default-p (n)
118 (and (ediff-merge-job)
119 ediff-skip-merge-regions-that-differ-from-default
120 (ediff-merge-changed-from-default-p n 'prefers-too)))
121
122
123(defun ediff-get-combined-region (n)
124 (let ((pattern-list ediff-combination-pattern)
125 (combo-region "")
126 (err-msg
127 "ediff-combination-pattern: Invalid format. Please consult the documentation")
128 region-delim region-spec)
129
130 (if (< (length pattern-list) 5)
131 (error err-msg))
132
133 (while (> (length pattern-list) 2)
134 (setq region-delim (nth 0 pattern-list)
135 region-spec (nth 1 pattern-list))
136 (or (and (stringp region-delim) (memq region-spec '(A B Ancestor)))
137 (error err-msg))
138
139 (condition-case nil
140 (setq combo-region
141 (concat combo-region
142 region-delim "\n"
143 (ediff-get-region-contents
144 n region-spec ediff-control-buffer)))
145 (error ""))
146 (setq pattern-list (cdr (cdr pattern-list)))
147 )
148
149 (setq region-delim (nth 0 pattern-list))
150 (or (stringp region-delim)
151 (error err-msg))
152 (setq combo-region (concat combo-region region-delim "\n"))
153 ))
154
155;;(defsubst ediff-make-combined-diff (regA regB)
156;; (concat (nth 0 ediff-combination-pattern) "\n"
157;; regA
158;; (nth 1 ediff-combination-pattern) "\n"
159;; regB
160;; (nth 2 ediff-combination-pattern) "\n"))
161
162(defsubst ediff-set-state-of-all-diffs-in-all-buffers (ctl-buf)
163 (let ((n 0))
164 (while (< n ediff-number-of-differences)
165 (ediff-set-state-of-diff-in-all-buffers n ctl-buf)
166 (setq n (1+ n)))))
167
168(defun ediff-set-state-of-diff-in-all-buffers (n ctl-buf)
169 (let ((regA (ediff-get-region-contents n 'A ctl-buf))
170 (regB (ediff-get-region-contents n 'B ctl-buf))
171 (regC (ediff-get-region-contents n 'C ctl-buf)))
172 (cond ((and (string= regA regB) (string= regA regC))
173 (ediff-set-state-of-diff n 'A "=diff(B)")
174 (ediff-set-state-of-diff n 'B "=diff(C)")
175 (ediff-set-state-of-diff n 'C "=diff(A)"))
176 ((string= regA regB)
177 (ediff-set-state-of-diff n 'A "=diff(B)")
178 (ediff-set-state-of-diff n 'B "=diff(A)")
179 (ediff-set-state-of-diff n 'C nil))
180 ((string= regA regC)
181 (ediff-set-state-of-diff n 'A "=diff(C)")
182 (ediff-set-state-of-diff n 'C "=diff(A)")
183 (ediff-set-state-of-diff n 'B nil))
184 ((string= regB regC)
185 (ediff-set-state-of-diff n 'C "=diff(B)")
186 (ediff-set-state-of-diff n 'B "=diff(C)")
187 (ediff-set-state-of-diff n 'A nil))
188 ((string= regC (ediff-get-combined-region n))
189 (ediff-set-state-of-diff n 'A nil)
190 (ediff-set-state-of-diff n 'B nil)
191 (ediff-set-state-of-diff n 'C "=diff(A+B)"))
192 (t (ediff-set-state-of-diff n 'A nil)
193 (ediff-set-state-of-diff n 'B nil)
194 (ediff-set-state-of-diff n 'C nil)))
195 ))
196
197(defun ediff-set-merge-mode ()
198 (normal-mode t)
199 (remove-hook 'local-write-file-hooks 'ediff-set-merge-mode))
200
201
202;; Go over all diffs starting with DIFF-NUM and copy regions into buffer C
203;; according to the state of the difference.
204;; Since ediff-copy-diff refuses to copy identical diff regions, there is
205;; no need to optimize ediff-do-merge any further.
206;;
207;; If re-merging, change state of merge in all diffs starting with
208;; DIFF-NUM, except those where the state is prefer-* or where it is
209;; `default-*' or `combined' but the buf C region appears to be modified
210;; since last set by default.
211(defun ediff-do-merge (diff-num &optional remerging)
212 (if (< diff-num 0) (setq diff-num 0))
213 (let ((n diff-num)
214 ;;(default-state-of-merge (format "%S" ediff-default-variant))
215 do-not-copy state-of-merge)
216 (while (< n ediff-number-of-differences)
217 (setq do-not-copy nil) ; reset after each cycle
218 (if (= (mod n 10) 0)
219 (message "%s buffers A & B into C ... region %d of %d"
220 (if remerging "Re-merging" "Merging")
221 n
222 ediff-number-of-differences))
223
224 (setq state-of-merge (ediff-get-state-of-merge n))
225
226 (if remerging
227 ;;(let ((reg-A (ediff-get-region-contents n 'A ediff-control-buffer))
228 ;; (reg-B (ediff-get-region-contents n 'B ediff-control-buffer))
229 ;; (reg-C (ediff-get-region-contents n 'C ediff-control-buffer)))
230 (progn
231
232 ;; if region was edited since it was first set by default
233 (if (or (ediff-merge-changed-from-default-p n)
234 ;; was preferred
235 (string-match "prefer" state-of-merge))
236 ;; then ignore
237 (setq do-not-copy t))
238
239 ;; change state of merge for this diff, if necessary
240 (if (and (string-match "\\(default\\|combined\\)" state-of-merge)
241 (not do-not-copy))
242 (ediff-set-state-of-merge
243 n (format "%S" ediff-default-variant)))
244 ))
245
246 ;; state-of-merge may have changed via ediff-set-state-of-merge, so
247 ;; check it once again
248 (setq state-of-merge (ediff-get-state-of-merge n))
249
250 (or do-not-copy
251 (if (string= state-of-merge "combined")
252 ;; use n+1 because ediff-combine-diffs works via user numbering
253 ;; of diffs, which is 1+ to what ediff uses internally
254 (ediff-combine-diffs (1+ n) 'batch)
255 (ediff-copy-diff
256 n (if (string-match "-A" state-of-merge) 'A 'B) 'C 'batch)))
257 (setq n (1+ n)))
258 (message "Merging buffers A & B into C ... Done")
259 ))
260
261
262(defun ediff-re-merge ()
263 "Remerge unmodified diff regions using a new default. Start with the current region."
264 (interactive)
265 (let* ((default-variant-alist
266 (list '("default-A") '("default-B") '("combined")))
267 (actual-alist
268 (delete (list (symbol-name ediff-default-variant))
269 default-variant-alist)))
270 (setq ediff-default-variant
271 (intern
272 (completing-read
273 (format "Current merge default is `%S'. New default: "
274 ediff-default-variant)
275 actual-alist nil 'must-match)))
276 (ediff-do-merge ediff-current-difference 'remerge)
277 (ediff-recenter)
278 ))
279
280(defun ediff-shrink-window-C (arg)
281 "Shrink window C to just one line.
282With a prefix argument, returns window C to its normal size.
283Used only for merging jobs."
284 (interactive "P")
285 (if (not ediff-merge-job)
286 (error "ediff-shrink-window-C can be used only for merging jobs"))
287 (cond ((eq arg '-) (setq arg -1))
288 ((not (numberp arg)) (setq arg nil)))
289 (cond ((null arg)
290 (let ((ediff-merge-window-share
291 (if (< (window-height ediff-window-C) 3)
292 ediff-merge-window-share 0)))
293 (setq ediff-window-config-saved "") ; force redisplay
294 (ediff-recenter 'no-rehighlight)))
295 ((and (< arg 0) (> (window-height ediff-window-C) 2))
296 (setq ediff-merge-window-share (* ediff-merge-window-share 0.9))
297 (setq ediff-window-config-saved "") ; force redisplay
298 (ediff-recenter 'no-rehighlight))
299 ((and (> arg 0) (> (window-height ediff-window-A) 2))
300 (setq ediff-merge-window-share (* ediff-merge-window-share 1.1))
301 (setq ediff-window-config-saved "") ; force redisplay
302 (ediff-recenter 'no-rehighlight))))
303
304
305;; N here is the user's region number. It is 1+ what Ediff uses internally.
306(defun ediff-combine-diffs (n &optional batch-invocation)
307 "Combine Nth diff regions of buffers A and B and place the combination in C.
308N is a prefix argument. If nil, combine the current difference regions.
309Combining is done according to the specifications in variable
310`ediff-combination-pattern'."
311 (interactive "P")
312 (setq n (if (numberp n) (1- n) ediff-current-difference))
313
314 (let (reg-combined)
315 ;;(setq regA (ediff-get-region-contents n 'A ediff-control-buffer)
316 ;; regB (ediff-get-region-contents n 'B ediff-control-buffer))
317 ;;(setq reg-combined (ediff-make-combined-diff regA regB))
318 (setq reg-combined (ediff-get-combined-region n))
319
320 (ediff-copy-diff n nil 'C batch-invocation reg-combined))
321 (or batch-invocation (ediff-jump-to-difference (1+ n))))
322
323
324;; Checks if the region in buff C looks like a combination of the regions
325;; in buffers A and B. Return a list (reg-a-beg reg-a-end reg-b-beg reg-b-end)
326;; These refer to where the delimiters for region A, B, Ancestor start and end
327;; in buffer C
328(defun ediff-looks-like-combined-merge (region-num)
329 (if ediff-merge-job
330 (let ((combined (string-match (regexp-quote "(A+B)")
331 (or (ediff-get-state-of-diff region-num 'C)
332 "")))
333 (mrgreg-beg (ediff-get-diff-posn 'C 'beg region-num))
334 (mrgreg-end (ediff-get-diff-posn 'C 'end region-num))
335 (pattern-list ediff-combination-pattern)
336 delim reg-beg reg-end delim-regs-list)
337
338 (if combined
339 (ediff-with-current-buffer ediff-buffer-C
340 (while pattern-list
341 (goto-char mrgreg-beg)
342 (setq delim (nth 0 pattern-list))
343 (search-forward delim mrgreg-end 'noerror)
344 (setq reg-beg (match-beginning 0))
345 (setq reg-end (match-end 0))
346 (if (and reg-beg reg-end)
347 (setq delim-regs-list
348 ;; in reverse
349 (cons reg-end (cons reg-beg delim-regs-list))))
350 (if (> (length pattern-list) 1)
351 (setq pattern-list (cdr (cdr pattern-list)))
352 (setq pattern-list nil))
353 )))
354
355 (reverse delim-regs-list)
356 )))
357
358(defvar state-of-merge) ; dynamic var
359
360;; Check if the non-preferred merge has been modified since originally set.
361;; This affects only the regions that are marked as default-A/B or combined.
362;; If PREFERS-TOO is non-nil, then look at the regions marked as prefers-A/B as
363;; well.
364(defun ediff-merge-changed-from-default-p (diff-num &optional prefers-too)
365 (let ((reg-A (ediff-get-region-contents diff-num 'A ediff-control-buffer))
366 (reg-B (ediff-get-region-contents diff-num 'B ediff-control-buffer))
367 (reg-C (ediff-get-region-contents diff-num 'C ediff-control-buffer)))
368
369 (setq state-of-merge (ediff-get-state-of-merge diff-num))
370
371 ;; if region was edited since it was first set by default
372 (or (and (string= state-of-merge "default-A")
373 (not (string= reg-A reg-C)))
374 (and (string= state-of-merge "default-B")
375 (not (string= reg-B reg-C)))
376 (and (string= state-of-merge "combined")
377 ;;(not (string= (ediff-make-combined-diff reg-A reg-B) reg-C)))
378 (not (string= (ediff-get-combined-region diff-num) reg-C)))
379 (and prefers-too
380 (string= state-of-merge "prefer-A")
381 (not (string= reg-A reg-C)))
382 (and prefers-too
383 (string= state-of-merge "prefer-B")
384 (not (string= reg-B reg-C)))
385 )))
386
387
388(provide 'ediff-merg)
389
390
391;; Local Variables:
392;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
393;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1)
394;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body))
395;; End:
396
397;; arch-tag: 9b798cf9-02ba-487f-a62e-b63aa823dbfb
398;;; ediff-merg.el ends here