Conflict resolution with Emacs Ediff: How can I take the changes of both versions
Asked Answered
C

4

41

(Question adapted from How do I combine the two variants of a conflict in emacs' emerge?)

I have a file with merge conflict markers. It looks similar to this:

<<<<<<< HEAD
            522ADC9C14B2FD9D00F56BAD /* close_test_button.png in Resources */,
            522ADC9D14B2FD9D00F56BAD /* [email protected] in Resources */,
            522ADCA014B2FDB100F56BAD /* test_failed.png in Resources */,
            522ADCA114B2FDB100F56BAD /* [email protected] in Resources */,
=======
            EC1633C014B2F3E3004B52E7 /* arrow.png in Resources */,
            EC1633C114B2F3E3004B52E7 /* [email protected] in Resources */,
            EC1633C214B2F3E3004B52E7 /* groups.png in Resources */,
            EC1633C314B2F3E3004B52E7 /* [email protected] in Resources */,
>>>>>>> beta_2.8

I use M-x vc-resolve-conflicts to start Ediff. I can select variant A or B by hitting a or b on my keyboard, but how do I combine both variants, one after the other?

Conchoid answered 11/3, 2012 at 15:36 Comment(1)
I've so wanted this for a long time, thanks for the impetus.Holtz
Z
36

This does the same thing as Trey Jackson's helpful answer, and it's much simpler. Pressing d will copy both A and B to buffer C.

(defun ediff-copy-both-to-C ()
  (interactive)
  (ediff-copy-diff ediff-current-difference nil 'C nil
                   (concat
                    (ediff-get-region-contents ediff-current-difference 'A ediff-control-buffer)
                    (ediff-get-region-contents ediff-current-difference 'B ediff-control-buffer))))
(defun add-d-to-ediff-mode-map () (define-key ediff-mode-map "d" 'ediff-copy-both-to-C))
(add-hook 'ediff-keymap-setup-hook 'add-d-to-ediff-mode-map)
Zizith answered 20/4, 2015 at 20:27 Comment(7)
This is great. For completeness would it be possible to add a line about it to the quick help menu as well?Storer
This is brilliant. Combined with ~ to swap the order of the buffers you can get A then B or B then A, which also sometimes comes in handy.Woodworth
The keybinding d conflicted with jump to diff. I bound it to B (uppercase) instead.Danella
Fantastic, it works very well. As @chengiz suggested, if you want to add a line in the help message to remember it, look at the variable ediff-long-help-message-merge in ediff-help.el. It defines the whole table. You can copy it in your init file and add a new line with this command in it.Jalbert
What is ~C~ in this case? Do I have to create it myself?Pristine
@Pristine C is the "resolved" version of the file. A and B are the two conflicting file versions.Zizith
Should be part of ediffKithara
B
9

You can switch to buffer "C" and edit it. Press + If you've already chose A or B to restore the diff.

If what you're after is to press a key to automatically remove the diff markers, I can only say that seems like a terrible idea.

Manual merges should be left up to the user. To remove clues as to where each diff region came from doesn't feel right to me.

You can customize the markers to be blank lines with this:

M-:

(setq ediff-combination-pattern '("" A "" B "" Ancestor))
Bender answered 11/3, 2012 at 16:16 Comment(2)
The point isn't to just remove the diff markers, it's to request that both diffs be included in the final result - and forcing people to manually do this is a PITA. Often the diff is just showing where two people have added things to a list. Obviously the user can select this and break their code, but they can do that by pressing either A or B too...Holtz
Agree with Trey. A shortkey is pushed becaues a user wants to do something.. By your rationale they ediff should remove short cuts keys for xa and xb too... most every other decent tool has this capability of taking A then B or B then A.... its the only reason i ever leave edfiff.. it seems to suck for merging because of this short coming.Prevision
H
5

Yes, I totally want to do this! With the following chunk of code you can get both by typing d - which will get you the code from both buffer A and buffer B (in that order) in the merge buffer.

The only real difficulty in this code is the fact that ediff uses a macro in one place, and the compiled version of the function needs to be re-evaluated with the new macro. Anyway, try the code out. Tested with Emacs 23.2.

(require 'ediff-init)           ;ensure the macro is defined, so we can override it

(defmacro ediff-char-to-buftype (arg)
  `(cond ((memq ,arg '(?a ?A)) 'A)
     ((memq ,arg '(?b ?B)) 'B)
     ((memq ,arg '(?c ?C)) 'C)
     ((memq ,arg '(?d ?D)) 'D)
     ))

(require 'ediff)

;; Literally copied from ediff-util
;; need to re-evaluate because it uses the macro defined above
;; and the compiled version needs to be re-compiled with the new definition
;; why a macro????
(defun ediff-diff-to-diff (arg &optional keys)
  "Copy buffer-X'th difference region to buffer Y \(X,Y are A, B, or C\).
If numerical prefix argument, copy the difference specified in the arg.
Otherwise, copy the difference given by `ediff-current-difference'.
This command assumes it is bound to a 2-character key sequence, `ab', `ba',
`ac', etc., which is used to determine the types of buffers to be used for
copying difference regions.  The first character in the sequence specifies
the source buffer and the second specifies the target.

If the second optional argument, a 2-character string, is given, use it to
determine the source and the target buffers instead of the command keys."
  (interactive "P")
  (ediff-barf-if-not-control-buffer)
  (or keys (setq keys (this-command-keys)))
  (if (eq arg '-) (setq arg -1)) ; translate neg arg to -1
  (if (numberp arg) (ediff-jump-to-difference arg))

  (let* ((key1 (aref keys 0))
     (key2 (aref keys 1))
     (char1 (ediff-event-key key1))
     (char2 (ediff-event-key key2))
     ediff-verbose-p)
(ediff-copy-diff ediff-current-difference
         (ediff-char-to-buftype char1)
         (ediff-char-to-buftype char2))
;; recenter with rehighlighting, but no messages
(ediff-recenter)))

(defun ediff-copy-D-to-C (arg)
  "Copy ARGth difference region from both buffers A and B to C.
ARG is a prefix argument.  If nil, copy the current difference region."
  (interactive "P")
  (ediff-diff-to-diff arg "dc"))

(defun ediff-copy-diff (n from-buf-type to-buf-type
              &optional batch-invocation reg-to-copy)
  (let* ((to-buf (ediff-get-buffer to-buf-type))
     ;;(from-buf (if (not reg-to-copy) (ediff-get-buffer from-buf-type)))
     (ctrl-buf ediff-control-buffer)
     (saved-p t)
     (three-way ediff-3way-job)
     messg
     ediff-verbose-p
     reg-to-delete reg-to-delete-beg reg-to-delete-end)

(setq reg-to-delete-beg
      (ediff-get-diff-posn to-buf-type 'beg n ctrl-buf))
(setq reg-to-delete-end
      (ediff-get-diff-posn to-buf-type 'end n ctrl-buf))

(if (eq from-buf-type 'D)
    ;; want to copy *both* A and B
    (if reg-to-copy
    (setq from-buf-type nil)
      (setq reg-to-copy (concat (ediff-get-region-contents n 'A ctrl-buf)
                (ediff-get-region-contents n 'B ctrl-buf))))
  ;; regular code
  (if reg-to-copy
      (setq from-buf-type nil)
    (setq reg-to-copy (ediff-get-region-contents n from-buf-type ctrl-buf))))

(setq reg-to-delete (ediff-get-region-contents
             n to-buf-type ctrl-buf
             reg-to-delete-beg reg-to-delete-end))

(if (string= reg-to-delete reg-to-copy)
    (setq saved-p nil) ; don't copy identical buffers
  ;; seems ok to copy
  (if (or batch-invocation (ediff-test-save-region n to-buf-type))
      (condition-case conds
      (progn
        (ediff-with-current-buffer to-buf
          ;; to prevent flags from interfering if buffer is writable
          (let ((inhibit-read-only (null buffer-read-only)))

        (goto-char reg-to-delete-end)
        (insert reg-to-copy)

        (if (> reg-to-delete-end reg-to-delete-beg)
            (kill-region reg-to-delete-beg reg-to-delete-end))
        ))
        (or batch-invocation
        (setq
         messg
         (ediff-save-diff-region n to-buf-type reg-to-delete))))
    (error (message "ediff-copy-diff: %s %s"
            (car conds)
            (mapconcat 'prin1-to-string (cdr conds) " "))
           (beep 1)
           (sit-for 2) ; let the user see the error msg
           (setq saved-p nil)
           )))
  )

;; adjust state of difference in case 3-way and diff was copied ok
(if (and saved-p three-way)
    (ediff-set-state-of-diff-in-all-buffers n ctrl-buf))

(if batch-invocation
    (ediff-clear-fine-differences n)
  ;; If diff3 job, we should recompute fine diffs so we clear them
  ;; before reinserting flags (and thus before ediff-recenter).
  (if (and saved-p three-way)
      (ediff-clear-fine-differences n))

  (ediff-refresh-mode-lines)

  ;; For diff2 jobs, don't recompute fine diffs, since we know there
  ;; aren't any.  So we clear diffs after ediff-recenter.
  (if (and saved-p (not three-way))
      (ediff-clear-fine-differences n))
  ;; Make sure that the message about saving and how to restore is seen
  ;; by the user
  (message "%s" messg))
))

;; add keybinding in a hook b/c the keymap isn't defined until the hook is run
(add-hook 'ediff-keymap-setup-hook 'add-d-to-ediff-mode-map)

(defun add-d-to-ediff-mode-map ()
  (define-key ediff-mode-map "d" 'ediff-copy-D-to-C))
Holtz answered 23/3, 2012 at 21:51 Comment(0)
R
4

The doc string for vc-resolve-conflicts says that it is an alias for smerge-ediff.

If that is working as I expect, then your buffer should be in smerge minor-mode, and there should be a menu for smerge. That menu contains everything you need.

Radmen answered 12/3, 2012 at 13:15 Comment(1)
Not actually relevant to the ediff functionality (which doesn't use smerge-mode -- or at least not these days, if it ever did), but certainly a useful answer if you are using smerge-mode.Tequila

© 2022 - 2024 — McMap. All rights reserved.