[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))
- [nongnu] elpa/git-commit updated (421105ef4f -> 5a7519fd3d), ELPA Syncer, 2022/05/16
- [nongnu] elpa/git-commit 1e2b473f20 3/9: magit-ediff-resolve: Rename to magit-ediff-resolve-rest, ELPA Syncer, 2022/05/16
- [nongnu] elpa/git-commit d035386e96 6/9: magit-ediff-buffers: Support merge jobs, ELPA Syncer, 2022/05/16
- [nongnu] elpa/git-commit 5a7519fd3d 9/9: magit-ediff-dwim-resolve-function: New option, ELPA Syncer, 2022/05/16
- [nongnu] elpa/git-commit ce45c43f9d 2/9: magit-ediff-resolve: Move definition, ELPA Syncer, 2022/05/16
- [nongnu] elpa/git-commit efb09e8d57 1/9: magit-renamed-files: Minor tweaks, ELPA Syncer, 2022/05/16
- [nongnu] elpa/git-commit db2e9546f8 5/9: magit-ediff-buffers: Change argument order, ELPA Syncer, 2022/05/16
- [nongnu] elpa/git-commit 46685b1c76 7/9: magit-ediff-resolve-all: New command,
ELPA Syncer <=
- [nongnu] elpa/git-commit 6fc4bacdee 8/9: magit-ediff-resolve-{all, rest}: Use file at point, ELPA Syncer, 2022/05/16
- [nongnu] elpa/git-commit e2e90f7b0c 4/9: magit-ediff-buffers: Cosmetics and new comments, ELPA Syncer, 2022/05/16