emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/git-commit 46685b1c76 7/9: magit-ediff-resolve-all: New co


From: ELPA Syncer
Subject: [nongnu] elpa/git-commit 46685b1c76 7/9: magit-ediff-resolve-all: New command
Date: Mon, 16 May 2022 16:58:17 -0400 (EDT)

branch: elpa/git-commit
commit 46685b1c76d06792594dcf91e9972369e61abf95
Author: Jonas Bernoulli <jonas@bernoul.li>
Commit: Jonas Bernoulli <jonas@bernoul.li>

    magit-ediff-resolve-all: New command
---
 docs/magit.org       | 74 ++++++++++++++++++++++-----------------------
 docs/magit.texi      | 78 ++++++++++++++++++++++++------------------------
 lisp/magit-ediff.el  | 84 +++++++++++++++++++++++++++++++++++++++++++++++-----
 lisp/magit-extras.el |  3 +-
 lisp/magit-git.el    |  8 +++++
 5 files changed, 161 insertions(+), 86 deletions(-)

diff --git a/docs/magit.org b/docs/magit.org
index 8f7b9bb44e..d649f7ffe8 100644
--- a/docs/magit.org
+++ b/docs/magit.org
@@ -3498,16 +3498,48 @@ information on how to use Ediff itself, see info:ediff.
 
 - Key: E m (magit-ediff-resolve-rest) ::
 
-  Resolve outstanding conflicts in a file using Ediff, defaulting to
-  the file at point.
+  This command allows you to resolve outstanding conflicts in a file
+  using Ediff, defaulting to the file at point.
 
   Provided that the value of ~merge.conflictstyle~ is ~diff3~, you can
   view the file's merge-base revision using ~/~ in the Ediff control
   buffer.
 
-  In the rare event that you want to manually resolve all conflicts,
-  including those already resolved by Git, use
-  ~ediff-merge-revisions-with-ancestor~.
+  The A, B and Ancestor buffers are constructed from the conflict
+  markers in the worktree file.  Because you and/or Git may have
+  already resolved some conflicts, that means that these buffers
+  to not contain the actual versions from the respective blobs.
+
+- Key: E m (magit-ediff-resolve-all) ::
+
+  This command allows you to resolve all conflicts in FILE using
+  Ediff, defaulting to the file at point.
+
+  Provided that the value of ~merge.conflictstyle~ is ~diff3~, you can
+  view the file's merge-base revision using ~/~ in the Ediff control
+  buffer.
+
+  First the file in the worktree is moved aside, appending the suffix
+  =.ORIG=, so that you could later go back to that version.  Then it is
+  reconstructed from the two sides of the conflict and the merge-base,
+  if available.
+
+  It would be nice if the worktree file were just used as-is, but
+  Ediff does not support that.  This means that all conflicts, that
+  Git has already resolved, are restored.  On the other hand Ediff
+  also tries to resolve conflicts, and in many cases Ediff and Git
+  should produce similar results.
+
+  However if you have already resolved some conflicts manually, then
+  those changes are discarded (though you can recover them from the
+  backup file).  In such cases ~magit-ediff-resolve-rest~ might be more
+  suitable.
+
+  The advantage that this command has over ~magit-ediff-resolve-rest~
+  is that the A, B and Ancestor buffers correspond to blobs from the
+  respective commits, allowing you to inspect a side in context and
+  to use Magit commands in these buffers to do so.  Blame and log
+  commands are particularly useful here.
 
 - Key: E t (magit-git-mergetool) ::
 
@@ -8807,38 +8839,6 @@ also affects the diffs displayed inside Magit.
 
 Please see [[*Branching]] and http://emacsair.me/2016/01/18/magit-2.4
 
-*** Can Magit be used as ~ediff-version-control-package~?
-
-No, it cannot.  For that to work the functions ~ediff-magit-internal~
-and ~ediff-magit-merge-internal~ would have to be implemented, and they
-are not.  These two functions are only used by the three commands
-~ediff-revision~, ~ediff-merge-revisions-with-ancestor~, and
-~ediff-merge-revisions~.
-
-These commands only delegate the task of populating buffers with
-certain revisions to the "internal" functions.  The equally important
-task of determining which revisions are to be compared/merged is not
-delegated.  Instead this is done without any support whatsoever from
-the version control package/system - meaning that the user has to
-enter the revisions explicitly.  Instead of implementing
-~ediff-magit-internal~ we provide ~magit-ediff-compare~, which handles
-both tasks like it is 2005.
-
-The other commands ~ediff-merge-revisions~ and
-~ediff-merge-revisions-with-ancestor~ are normally not what you want
-when using a modern version control system like Git.  Instead of
-letting the user resolve only those conflicts which Git could not
-resolve on its own, they throw away all work done by Git and then
-expect the user to manually merge all conflicts, including those that
-had already been resolved.  That made sense back in the days when
-version control systems couldn't merge (or so I have been told), but
-not anymore.  Once in a blue moon you might actually want to see all
-conflicts, in which case you *can* use these commands, which then use
-~ediff-vc-merge-internal~.  So we don't actually have to implement
-~ediff-magit-merge-internal~.  Instead we provide the more useful
-command ~magit-ediff-resolve~ which only shows yet-to-be resolved
-conflicts.
-
 *** Should I disable VC?
 
 If you don't use VC (the built-in version control interface) then
diff --git a/docs/magit.texi b/docs/magit.texi
index d36993b70c..ea7d1873a2 100644
--- a/docs/magit.texi
+++ b/docs/magit.texi
@@ -329,7 +329,6 @@ FAQ - How to @dots{}?
 * How to install the gitman info manual?::
 * How to show diffs for gpg-encrypted files?::
 * How does branching and pushing work?::
-* Can Magit be used as @code{ediff-version-control-package}?::
 * Should I disable VC@?::
 
 
@@ -4369,16 +4368,49 @@ common ancestor of both revisions (i.e., use a 
"@dots{}"  range).
 @item @kbd{E m} (@code{magit-ediff-resolve-rest})
 @kindex E m
 @findex magit-ediff-resolve-rest
-Resolve outstanding conflicts in a file using Ediff, defaulting to
-the file at point.
+This command allows you to resolve outstanding conflicts in a file
+using Ediff, defaulting to the file at point.
 
 Provided that the value of @code{merge.conflictstyle} is @code{diff3}, you can
 view the file's merge-base revision using @code{/} in the Ediff control
 buffer.
 
-In the rare event that you want to manually resolve all conflicts,
-including those already resolved by Git, use
-@code{ediff-merge-revisions-with-ancestor}.
+The A, B and Ancestor buffers are constructed from the conflict
+markers in the worktree file.  Because you and/or Git may have
+already resolved some conflicts, that means that these buffers
+to not contain the actual versions from the respective blobs.
+
+@item @kbd{E m} (@code{magit-ediff-resolve-all})
+@kindex E m
+@findex magit-ediff-resolve-all
+This command allows you to resolve all conflicts in FILE using
+Ediff, defaulting to the file at point.
+
+Provided that the value of @code{merge.conflictstyle} is @code{diff3}, you can
+view the file's merge-base revision using @code{/} in the Ediff control
+buffer.
+
+First the file in the worktree is moved aside, appending the suffix
+@samp{.ORIG}, so that you could later go back to that version.  Then it is
+reconstructed from the two sides of the conflict and the merge-base,
+if available.
+
+It would be nice if the worktree file were just used as-is, but
+Ediff does not support that.  This means that all conflicts, that
+Git has already resolved, are restored.  On the other hand Ediff
+also tries to resolve conflicts, and in many cases Ediff and Git
+should produce similar results.
+
+However if you have already resolved some conflicts manually, then
+those changes are discarded (though you can recover them from the
+backup file).  In such cases @code{magit-ediff-resolve-rest} might be more
+suitable.
+
+The advantage that this command has over @code{magit-ediff-resolve-rest}
+is that the A, B and Ancestor buffers correspond to blobs from the
+respective commits, allowing you to inspect a side in context and
+to use Magit commands in these buffers to do so.  Blame and log
+commands are particularly useful here.
 
 @item @kbd{E t} (@code{magit-git-mergetool})
 @kindex E t
@@ -10695,7 +10727,6 @@ Please also see @ref{Debugging Tools}.
 * How to install the gitman info manual?::
 * How to show diffs for gpg-encrypted files?::
 * How does branching and pushing work?::
-* Can Magit be used as @code{ediff-version-control-package}?::
 * Should I disable VC@?::
 @end menu
 
@@ -10769,39 +10800,6 @@ echo "*.gpg filter=gpg diff=gpg" > .gitattributes
 
 Please see @ref{Branching} and @uref{http://emacsair.me/2016/01/18/magit-2.4}
 
-@node Can Magit be used as @code{ediff-version-control-package}?
-@appendixsubsec Can Magit be used as @code{ediff-version-control-package}?
-
-No, it cannot.  For that to work the functions @code{ediff-magit-internal}
-and @code{ediff-magit-merge-internal} would have to be implemented, and they
-are not.  These two functions are only used by the three commands
-@code{ediff-revision}, @code{ediff-merge-revisions-with-ancestor}, and
-@code{ediff-merge-revisions}.
-
-These commands only delegate the task of populating buffers with
-certain revisions to the "internal" functions.  The equally important
-task of determining which revisions are to be compared/merged is not
-delegated.  Instead this is done without any support whatsoever from
-the version control package/system - meaning that the user has to
-enter the revisions explicitly.  Instead of implementing
-@code{ediff-magit-internal} we provide @code{magit-ediff-compare}, which 
handles
-both tasks like it is 2005.
-
-The other commands @code{ediff-merge-revisions} and
-@code{ediff-merge-revisions-with-ancestor} are normally not what you want
-when using a modern version control system like Git.  Instead of
-letting the user resolve only those conflicts which Git could not
-resolve on its own, they throw away all work done by Git and then
-expect the user to manually merge all conflicts, including those that
-had already been resolved.  That made sense back in the days when
-version control systems couldn't merge (or so I have been told), but
-not anymore.  Once in a blue moon you might actually want to see all
-conflicts, in which case you @strong{can} use these commands, which then use
-@code{ediff-vc-merge-internal}.  So we don't actually have to implement
-@code{ediff-magit-merge-internal}.  Instead we provide the more useful
-command @code{magit-ediff-resolve} which only shows yet-to-be resolved
-conflicts.
-
 @node Should I disable VC@?
 @appendixsubsec Should I disable VC@?
 
diff --git a/lisp/magit-ediff.el b/lisp/magit-ediff.el
index a3bf6c477b..54f6d2ffdf 100644
--- a/lisp/magit-ediff.el
+++ b/lisp/magit-ediff.el
@@ -113,8 +113,9 @@ recommend you do not further complicate that by enabling 
this.")
   :info-manual "(ediff)"
   ["Ediff"
    [("E" "Dwim"          magit-ediff-dwim)
-    ("s" "Stage"         magit-ediff-stage)
-    ("m" "Resolve"       magit-ediff-resolve-rest)
+    ("s" "Stage"         magit-ediff-stage)]
+   [("m" "Resolve rest"            magit-ediff-resolve-rest)
+    ("M" "Resolve all conflicts"   magit-ediff-resolve-all)
     ("t" "Resolve using mergetool" magit-git-mergetool)]
    [("u" "Show unstaged" magit-ediff-show-unstaged)
     ("i" "Show staged"   magit-ediff-show-staged)
@@ -198,14 +199,78 @@ is put in FILE."
             ('(t   t)   'ediff-merge-buffers-with-ancestor))
           file)))))
 
+;;;###autoload
+(defun magit-ediff-resolve-all (file)
+  "Resolve all conflicts in FILE using Ediff.
+See info node `(magit) Ediffing' for more information about this
+and alternative commands."
+  (interactive (list (magit-read-unmerged-file)))
+  (magit-with-toplevel
+    (let* ((revA  (or (magit-name-branch "HEAD")
+                      (magit-commit-p "HEAD")))
+           (revB  (cl-find-if (lambda (head) (file-exists-p (magit-git-dir 
head)))
+                              '("MERGE_HEAD" "CHERRY_PICK_HEAD" 
"REVERT_HEAD")))
+           (revB  (or (magit-name-branch revB)
+                      (magit-commit-p revB)))
+           (revC  (magit-commit-p (magit-git-string "merge-base" revA revB)))
+           (fileA (magit--rev-file-name file revA revB))
+           (fileB (magit--rev-file-name file revB revA))
+           (fileC (or (magit--rev-file-name file revC revA)
+                      (magit--rev-file-name file revC revB))))
+      ;; Ediff assumes that the FILE where it is going to store the merge
+      ;; result does not exist yet, so move the existing file out of the
+      ;; way.  If a buffer visits FILE, then we have to kill that upfront.
+      (when-let ((buffer (find-buffer-visiting file)))
+        (when (and (buffer-modified-p buffer)
+                   (not (y-or-n-p (format "Save buffer %s %s? "
+                                          (buffer-name buffer)
+                                          "(cannot continue otherwise)"))))
+          (user-error "Abort"))
+        (kill-buffer buffer))
+      (let ((orig (concat file ".ORIG")))
+        (when (file-exists-p orig)
+          (rename-file orig (make-temp-name (concat orig "_"))))
+        (rename-file file orig))
+      (let ((setup (lambda ()
+                     ;; Use the same conflict marker style as Git uses.
+                     (setq-local ediff-combination-pattern
+                                 '("<<<<<<< HEAD" A
+                                   ,(format "||||||| %s" revC) Ancestor
+                                   "=======" B
+                                   ,(format ">>>>>>> %s" revB)))))
+            (quit  (lambda ()
+                     ;; For merge jobs Ediff switches buffer names around.
+                     ;; At this point `ediff-buffer-C' no longer refer to
+                     ;; the ancestor buffer but to the merge result buffer.
+                     ;; See (if ediff-merge-job ...) in `ediff-setup'.
+                     (when (buffer-live-p ediff-buffer-C)
+                       (with-current-buffer ediff-buffer-C
+                         (save-buffer)
+                         (save-excursion
+                           (goto-char (point-min))
+                           (unless (re-search-forward "^<<<<<<< " nil t)
+                             (magit-stage-file file))))))))
+        (if fileC
+            (magit-ediff-buffers
+             ((magit-get-revision-buffer revA fileA)
+              (magit-find-file-noselect  revA fileA))
+             ((magit-get-revision-buffer revB fileB)
+              (magit-find-file-noselect  revB fileB))
+             ((magit-get-revision-buffer revC fileC)
+              (magit-find-file-noselect  revC fileC))
+             setup quit file)
+          (magit-ediff-buffers
+           ((magit-get-revision-buffer revA fileA)
+            (magit-find-file-noselect  revA fileA))
+           ((magit-get-revision-buffer revB fileB)
+            (magit-find-file-noselect  revB fileB))
+           nil setup quit file))))))
+
 ;;;###autoload
 (defun magit-ediff-resolve-rest (file)
   "Resolve outstanding conflicts in FILE using Ediff.
-FILE has to be relative to the top directory of the repository.
-
-In the rare event that you want to manually resolve all
-conflicts, including those already resolved by Git, use
-`ediff-merge-revisions-with-ancestor'."
+See info node `(magit) Ediffing' for more information about this
+and alternative commands."
   (interactive (list (magit-read-unmerged-file)))
   (magit-with-toplevel
     (with-current-buffer (find-file-noselect file)
@@ -390,7 +455,10 @@ mind at all, then it asks the user for a command to run."
                  (?c "[c]ommit"  #'magit-ediff-show-commit)
                  (?r "[r]ange"   #'magit-ediff-compare)
                  (?s "[s]tage"   #'magit-ediff-stage)
-                 (?v "resol[v]e" #'magit-ediff-resolve-rest))))
+                 (?m "[m] resolve remaining conflicts"
+                     #'magit-ediff-resolve-rest)
+                 (?M "[M] resolve all conflicts"
+                     #'magit-ediff-resolve-all))))
              ((eq command #'magit-ediff-compare)
               (apply #'magit-ediff-compare revA revB
                      (magit-ediff-read-files revA revB file)))
diff --git a/lisp/magit-extras.el b/lisp/magit-extras.el
index c1a11d8b45..8308cd8404 100644
--- a/lisp/magit-extras.el
+++ b/lisp/magit-extras.el
@@ -50,7 +50,8 @@
 (transient-define-prefix magit-git-mergetool (file args &optional transient)
   "Resolve conflicts in FILE using \"git mergetool --gui\".
 With a prefix argument allow changing ARGS using a transient
-popup."
+popup.  See info node `(magit) Ediffing' for information about
+alternative commands."
   :man-page "git-mergetool"
   ["Settings"
    ("-t" magit-git-mergetool:--tool)
diff --git a/lisp/magit-git.el b/lisp/magit-git.el
index 5cb4b5e02c..d3b88d8461 100644
--- a/lisp/magit-git.el
+++ b/lisp/magit-git.el
@@ -1108,6 +1108,14 @@ range.  Otherwise, it can be any revision or range 
accepted by
                                           "--diff-filter=R" revA revB)
                          3)))
 
+(defun magit--rev-file-name (file rev other-rev)
+  "For FILE, potentially renamed between REV and OTHER-REV, return name in REV.
+Return nil, if FILE appears neither in REV nor OTHER-REV,
+or if no rename is detected."
+  (or (car (member file (magit-revision-files rev)))
+      (and-let* ((renamed (magit-renamed-files rev other-rev)))
+        (car (rassoc file renamed)))))
+
 (defun magit-file-status (&rest args)
   (magit--with-temp-process-buffer
     (save-excursion (magit-git-insert "status" "-z" args))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]