comparison lisp/smerge-mode.el @ 93445:87b6e6f4a207

(smerge-apply-resolution-patch): New fun. (smerge-resolve): Add various resolution heuristics.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Mon, 31 Mar 2008 02:57:39 +0000
parents a6d8268a0da6
children 955774916817
comparison
equal deleted inserted replaced
93444:3a3281d7f1dd 93445:87b6e6f4a207
405 (let ((beg (re-search-backward smerge-end-re nil t)) 405 (let ((beg (re-search-backward smerge-end-re nil t))
406 (end (re-search-forward smerge-begin-re nil t))) 406 (end (re-search-forward smerge-begin-re nil t)))
407 (smerge-remove-props (or beg (point-min)) (or end (point-max))) 407 (smerge-remove-props (or beg (point-min)) (or end (point-max)))
408 (push event unread-command-events))))) 408 (push event unread-command-events)))))
409 409
410 (defun smerge-apply-resolution-patch (buf m0b m0e m3b m3e &optional m2b)
411 "Replace the conflict with a bunch of subconflicts.
412 BUF contains a plain diff between match-1 and match-3."
413 (let ((line 1)
414 (textbuf (current-buffer))
415 (name1 (progn (goto-char m0b)
416 (buffer-substring (+ (point) 8) (line-end-position))))
417 (name2 (when m2b (goto-char m2b) (forward-line -1)
418 (buffer-substring (+ (point) 8) (line-end-position))))
419 (name3 (progn (goto-char m0e) (forward-line -1)
420 (buffer-substring (+ (point) 8) (line-end-position)))))
421 (smerge-remove-props m0b m0e)
422 (delete-region m3e m0e)
423 (delete-region m0b m3b)
424 (setq m3b m0b)
425 (setq m3e (- m3e (- m3b m0b)))
426 (goto-char m3b)
427 (with-current-buffer buf
428 (goto-char (point-min))
429 (while (not (eobp))
430 (if (not (looking-at "\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?\\([acd]\\)\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?$"))
431 (error "Unexpected patch hunk header: %s"
432 (buffer-substring (point) (line-end-position)))
433 (let* ((op (char-after (match-beginning 3)))
434 (startline (+ (string-to-number (match-string 1))
435 ;; No clue why this is the way it is, but line
436 ;; numbers seem to be off-by-one for `a' ops.
437 (if (eq op ?a) 1 0)))
438 (endline (if (eq op ?a) startline
439 (1+ (if (match-end 2)
440 (string-to-number (match-string 2))
441 startline))))
442 (lines (- endline startline))
443 (otherlines (cond
444 ((eq op ?d) nil)
445 ((null (match-end 5)) 1)
446 (t (- (string-to-number (match-string 5))
447 (string-to-number (match-string 4)) -1))))
448 othertext)
449 (forward-line 1) ;Skip header.
450 (forward-line lines) ;Skip deleted text.
451 (if (eq op ?c) (forward-line 1)) ;Skip separator.
452 (setq othertext
453 (if (null otherlines) ""
454 (let ((pos (point)))
455 (dotimes (i otherlines) (delete-char 2) (forward-line 1))
456 (buffer-substring pos (point)))))
457 (with-current-buffer textbuf
458 (forward-line (- startline line))
459 (insert "<<<<<<< " name1 "\n" othertext
460 (if name2 (concat "||||||| " name2))
461 "=======\n")
462 (forward-line lines)
463 (insert ">>>>>>> " name3 "\n")
464 (setq line endline))))))))
465
410 (defun smerge-resolve (&optional safe) 466 (defun smerge-resolve (&optional safe)
411 "Resolve the conflict at point intelligently. 467 "Resolve the conflict at point intelligently.
412 This relies on mode-specific knowledge and thus only works in 468 This relies on mode-specific knowledge and thus only works in
413 some major modes. Uses `smerge-resolve-function' to do the actual work." 469 some major modes. Uses `smerge-resolve-function' to do the actual work."
414 (interactive) 470 (interactive)
415 (smerge-match-conflict) 471 (smerge-match-conflict)
416 (smerge-remove-props (match-beginning 0) (match-end 0)) 472 (smerge-remove-props (match-beginning 0) (match-end 0))
417 (cond 473 (let ((md (match-data))
418 ;; Trivial diff3 -A non-conflicts. 474 (m0b (match-beginning 0))
419 ((and (eq (match-end 1) (match-end 3)) 475 (m1b (match-beginning 1))
420 (eq (match-beginning 1) (match-beginning 3))) 476 (m2b (match-beginning 2))
421 (smerge-keep-n 3)) 477 (m3b (match-beginning 3))
422 ;; Mode-specific conflict resolution. 478 (m0e (match-end 0))
423 ((condition-case nil 479 (m1e (match-end 1))
424 (atomic-change-group 480 (m2e (match-end 2))
425 (if safe 481 (m3e (match-end 3))
426 (funcall smerge-resolve-function safe) 482 (buf (generate-new-buffer " *smerge*"))
427 (funcall smerge-resolve-function)) 483 m b o)
428 t) 484 (unwind-protect
429 (error nil)) 485 (progn
430 ;; Nothing to do: the resolution function has done it already. 486 (cond
431 nil) 487 ;; Trivial diff3 -A non-conflicts.
432 ;; FIXME: Add "if [ diff -b MINE OTHER ]; then select OTHER; fi" 488 ((and (eq (match-end 1) (match-end 3))
433 ((and (match-end 2) 489 (eq (match-beginning 1) (match-beginning 3)))
434 ;; FIXME: Add "diff -b BASE MINE | patch OTHER". 490 (smerge-keep-n 3))
435 ;; FIXME: Add "diff -b BASE OTHER | patch MINE". 491 ;; Mode-specific conflict resolution.
436 nil) 492 ((condition-case nil
437 ) 493 (atomic-change-group
438 ((and (not (match-end 2)) 494 (if safe
439 ;; FIXME: Add "diff -b"-based refinement. 495 (funcall smerge-resolve-function safe)
440 nil) 496 (funcall smerge-resolve-function))
441 ) 497 t)
442 (t 498 (error nil))
443 (error "Don't know how to resolve"))) 499 ;; Nothing to do: the resolution function has done it already.
500 nil)
501 ;; "Mere whitespace" conflicts.
502 ((or (and (eq m1e m3e) (eq m1b m3b)) ;Non-conflict.
503 (progn
504 (setq m (make-temp-file "smm"))
505 (write-region m1b m1e m nil 'silent)
506 (setq o (make-temp-file "smo"))
507 (write-region m3b m3e o nil 'silent)
508 ;; Same patch applied on both sides, with whitespace changes.
509 (zerop (call-process diff-command nil nil nil "-b" m o)))
510 (when m2e
511 (setq b (make-temp-file "smb"))
512 (write-region m2b m2e b nil 'silent)
513 ;; Only minor whitespace changes made locally.
514 (zerop (call-process diff-command nil buf nil "-bc" b m))))
515 (set-match-data md)
516 (smerge-keep-n 3))
517 ;; Refine a 2-way conflict using "diff -b".
518 ;; In case of a 3-way conflict with an empty base
519 ;; (i.e. 2 conflicting additions), we do the same, presuming
520 ;; that the 2 additions should be somehow merged rather
521 ;; than concatenated.
522 ((not (or (and m2b (not (eq m2b m2e)))
523 (eq m1b m1e) (eq m3b m3e)
524 (let ((lines (count-lines m3b m3e)))
525 (call-process diff-command nil buf nil "-b" o m)
526 (with-current-buffer buf
527 (goto-char (point-min))
528 ;; Make sure there's some refinement.
529 (looking-at
530 (concat "1," (number-to-string lines) "c"))))))
531 (smerge-apply-resolution-patch buf m0b m0e m3b m3e m2b))
532 ;; Try "diff -b BASE MINE | patch OTHER".
533 ((when (and (not safe) m2e b
534 ;; If the BASE is empty, this would just concatenate
535 ;; the two, which is rarely right.
536 (not (eq m2b m2e)))
537 (with-current-buffer buf
538 (zerop (call-process-region
539 (point-min) (point-max) "patch" t nil nil
540 "-r" "/dev/null" "--no-backup-if-mismatch"
541 "-fl" o))))
542 (save-restriction
543 (narrow-to-region m0b m0e)
544 (smerge-remove-props m0b m0e)
545 (insert-file-contents o nil nil nil t)))
546 ;; Try "diff -b BASE OTHER | patch MINE".
547 ((when (and (not safe) m2e b
548 ;; If the BASE is empty, this would just concatenate
549 ;; the two, which is rarely right.
550 (not (eq m2b m2e)))
551 (write-region m3b m3e o nil 'silent)
552 (call-process diff-command nil buf nil "-bc" b o)
553 (with-current-buffer buf
554 (zerop (call-process-region
555 (point-min) (point-max) "patch" t nil nil
556 "-r" "/dev/null" "--no-backup-if-mismatch"
557 "-fl" m))))
558 (save-restriction
559 (narrow-to-region m0b m0e)
560 (smerge-remove-props m0b m0e)
561 (insert-file-contents m nil nil nil t)))
562 (t
563 (error "Don't know how to resolve"))))
564 (if (buffer-name buf) (kill-buffer buf))
565 (if m (delete-file m))
566 (if b (delete-file b))
567 (if o (delete-file o))))
444 (smerge-auto-leave)) 568 (smerge-auto-leave))
445 569
446 (defun smerge-resolve-all () 570 (defun smerge-resolve-all ()
447 "Perform automatic resolution on all conflicts." 571 "Perform automatic resolution on all conflicts."
448 (interactive) 572 (interactive)