magit-ediff.el (25995B)
1 ;;; magit-ediff.el --- Ediff extension for Magit -*- lexical-binding:t -*- 2 3 ;; Copyright (C) 2008-2024 The Magit Project Contributors 4 5 ;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> 6 ;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> 7 8 ;; SPDX-License-Identifier: GPL-3.0-or-later 9 10 ;; Magit is free software: you can redistribute it and/or modify it 11 ;; under the terms of the GNU General Public License as published by 12 ;; the Free Software Foundation, either version 3 of the License, or 13 ;; (at your option) any later version. 14 ;; 15 ;; Magit is distributed in the hope that it will be useful, but WITHOUT 16 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 17 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 18 ;; License for more details. 19 ;; 20 ;; You should have received a copy of the GNU General Public License 21 ;; along with Magit. If not, see <https://www.gnu.org/licenses/>. 22 23 ;;; Commentary: 24 25 ;; This library provides basic support for Ediff. 26 27 ;;; Code: 28 29 (require 'magit) 30 31 (require 'ediff) 32 (require 'smerge-mode) 33 34 (defvar smerge-ediff-buf) 35 (defvar smerge-ediff-windows) 36 37 ;;; Options 38 39 (defgroup magit-ediff nil 40 "Ediff support for Magit." 41 :link '(info-link "(magit)Ediffing") 42 :group 'magit-extensions) 43 44 (defcustom magit-ediff-quit-hook 45 '(magit-ediff-cleanup-auxiliary-buffers 46 magit-ediff-restore-previous-winconf) 47 "Hooks to run after finishing Ediff, when that was invoked using Magit. 48 The hooks are run in the Ediff control buffer. This is similar 49 to `ediff-quit-hook' but takes the needs of Magit into account. 50 The `ediff-quit-hook' is ignored by Ediff sessions which were 51 invoked using Magit." 52 :package-version '(magit . "2.2.0") 53 :group 'magit-ediff 54 :type 'hook 55 :get #'magit-hook-custom-get 56 :options '(magit-ediff-cleanup-auxiliary-buffers 57 magit-ediff-restore-previous-winconf)) 58 59 (defcustom magit-ediff-dwim-resolve-function #'magit-ediff-resolve-rest 60 "The function `magit-ediff-dwim' uses to resolve conflicts." 61 :package-version '(magit . "4.0.0") 62 :group 'magit-ediff 63 :type '(choice (const magit-ediff-resolve-rest) 64 (const magit-ediff-resolve-all) 65 (const magit-git-mergetool))) 66 67 (defcustom magit-ediff-dwim-show-on-hunks nil 68 "Whether `magit-ediff-dwim' runs show variants on hunks. 69 If non-nil, `magit-ediff-show-staged' or 70 `magit-ediff-show-unstaged' are called based on what section the 71 hunk is in. Otherwise, `magit-ediff-dwim' runs 72 `magit-ediff-stage' when point is on an uncommitted hunk." 73 :package-version '(magit . "2.2.0") 74 :group 'magit-ediff 75 :type 'boolean) 76 77 (defcustom magit-ediff-show-stash-with-index t 78 "Whether `magit-ediff-show-stash' shows the state of the index. 79 80 If non-nil, use a third Ediff buffer to distinguish which changes 81 in the stash were staged. In cases where the stash contains no 82 staged changes, fall back to a two-buffer Ediff. 83 84 More specifically, a stash is a merge commit, stash@{N}, with 85 potentially three parents. 86 87 * stash@{N}^1 represents the `HEAD' commit at the time the stash 88 was created. 89 90 * stash@{N}^2 records any changes that were staged when the stash 91 was made. 92 93 * stash@{N}^3, if it exists, contains files that were untracked 94 when stashing. 95 96 If this option is non-nil, `magit-ediff-show-stash' will run 97 Ediff on a file using three buffers: one for stash@{N}, another 98 for stash@{N}^1, and a third for stash@{N}^2. 99 100 Otherwise, Ediff uses two buffers, comparing 101 stash@{N}^1..stash@{N}. Along with any unstaged changes, changes 102 in the index commit, stash@{N}^2, will be shown in this 103 comparison unless they conflicted with changes in the working 104 tree at the time of stashing." 105 :package-version '(magit . "2.6.0") 106 :group 'magit-ediff 107 :type 'boolean) 108 109 (defvar magit-ediff-use-indirect-buffers nil 110 "Whether to use indirect buffers. 111 Ediff already does a lot of buffer and file shuffling and I 112 recommend you do not further complicate that by enabling this.") 113 114 ;;; Commands 115 116 (defvar magit-ediff-previous-winconf nil) 117 118 ;;;###autoload (autoload 'magit-ediff "magit-ediff" nil) 119 (transient-define-prefix magit-ediff () 120 "Show differences using the Ediff package." 121 :info-manual "(ediff)" 122 ["Ediff" 123 [("E" "Dwim" magit-ediff-dwim) 124 ("s" "Stage" magit-ediff-stage)] 125 [("m" "Resolve rest" magit-ediff-resolve-rest) 126 ("M" "Resolve all conflicts" magit-ediff-resolve-all) 127 ("t" "Resolve using mergetool" magit-git-mergetool)] 128 [("u" "Show unstaged" magit-ediff-show-unstaged) 129 ("i" "Show staged" magit-ediff-show-staged) 130 ("w" "Show worktree" magit-ediff-show-working-tree)] 131 [("c" "Show commit" magit-ediff-show-commit) 132 ("r" "Show range" magit-ediff-compare) 133 ("z" "Show stash" magit-ediff-show-stash)]]) 134 135 (defmacro magit-ediff-buffers (a b &optional c setup quit file) 136 "Run Ediff on two or three buffers. 137 This is a wrapper around `ediff-buffers-internal'. 138 139 A, B and C have the form (GET-BUFFER CREATE-BUFFER). If 140 GET-BUFFER returns a non-nil value, then that buffer is used and 141 it is not killed when exiting Ediff. Otherwise CREATE-BUFFER 142 must return a buffer and that is killed when exiting Ediff. 143 144 If non-nil, SETUP must be a function. It is called without 145 arguments after Ediff is done setting up buffers. 146 147 If non-nil, QUIT must be a function. It is added to 148 `ediff-quit-hook' and is called without arguments. 149 150 If FILE is non-nil, then perform a merge. The merge result 151 is put in FILE." 152 (let (get make kill (char ?A)) 153 (dolist (spec (list a b c)) 154 (if (not spec) 155 (push nil make) 156 (pcase-let ((`(,g ,m) spec)) 157 (let ((b (intern (format "buf%c" char)))) 158 (push `(,b ,g) get) 159 ;; This is an unfortunate complication that I have added for 160 ;; the benefit of one user. Pretend we used this instead: 161 ;; (push `(or ,b ,m) make) 162 (push `(if ,b 163 (if magit-ediff-use-indirect-buffers 164 (prog1 (make-indirect-buffer 165 ,b 166 (generate-new-buffer-name (buffer-name ,b)) 167 t) 168 (setq ,b nil)) 169 ,b) 170 ,m) 171 make) 172 (push `(unless ,b 173 ;; For merge jobs Ediff switches buffer names around. 174 ;; See (if ediff-merge-job ...) in `ediff-setup'. 175 (let ((var ,(if (and file (= char ?C)) 176 'ediff-ancestor-buffer 177 (intern (format "ediff-buffer-%c" char))))) 178 (ediff-kill-buffer-carefully var))) 179 kill)) 180 (cl-incf char)))) 181 (setq get (nreverse get)) 182 (setq make (nreverse make)) 183 (setq kill (nreverse kill)) 184 (let ((mconf (cl-gensym "conf")) 185 (mfile (cl-gensym "file"))) 186 `(magit-with-toplevel 187 (let ((,mconf (current-window-configuration)) 188 (,mfile ,file) 189 ,@get) 190 (ediff-buffers-internal 191 ,@make 192 (list ,@(and setup (list setup)) 193 (lambda () 194 ;; We do not want to kill buffers that existed before 195 ;; Ediff was invoked, so we cannot use Ediff's default 196 ;; quit functions. Ediff splits quitting across two 197 ;; hooks for merge jobs but we only ever use one. 198 (setq-local ediff-quit-merge-hook nil) 199 (setq-local ediff-quit-hook 200 (list 201 ,@(and quit (list quit)) 202 (lambda () 203 ,@kill 204 (let ((magit-ediff-previous-winconf ,mconf)) 205 (run-hooks 'magit-ediff-quit-hook))))))) 206 (pcase (list ,(and c t) (and ,mfile t)) 207 ('(nil nil) 'ediff-buffers) 208 ('(nil t) 'ediff-merge-buffers) 209 ('(t nil) 'ediff-buffers3) 210 ('(t t) 'ediff-merge-buffers-with-ancestor)) 211 ,mfile)))))) 212 213 ;;;###autoload 214 (defun magit-ediff-resolve-all (file) 215 "Resolve all conflicts in the FILE at point using Ediff. 216 217 If there is no file at point or if it doesn't have any unmerged 218 changes, then prompt for a file. 219 220 See info node `(magit) Ediffing' for more information about this 221 and alternative commands." 222 (interactive (list (magit-read-unmerged-file))) 223 (magit-with-toplevel 224 (let* ((dir (magit-gitdir)) 225 (revA (or (magit-name-branch "HEAD") 226 (magit-commit-p "HEAD"))) 227 (revB (cl-find-if (lambda (head) 228 (file-exists-p (expand-file-name head dir))) 229 '("MERGE_HEAD" "CHERRY_PICK_HEAD" "REVERT_HEAD"))) 230 (revB (or (magit-name-branch revB) 231 (magit-commit-p revB))) 232 (revC (magit-commit-p (magit-git-string "merge-base" revA revB))) 233 (fileA (magit--rev-file-name file revA revB)) 234 (fileB (magit--rev-file-name file revB revA)) 235 (fileC (or (magit--rev-file-name file revC revA) 236 (magit--rev-file-name file revC revB)))) 237 ;; Ediff assumes that the FILE where it is going to store the merge 238 ;; result does not exist yet, so move the existing file out of the 239 ;; way. If a buffer visits FILE, then we have to kill that upfront. 240 (when-let ((buffer (find-buffer-visiting file))) 241 (when (and (buffer-modified-p buffer) 242 (not (y-or-n-p (format "Save buffer %s %s? " 243 (buffer-name buffer) 244 "(cannot continue otherwise)")))) 245 (user-error "Abort")) 246 (kill-buffer buffer)) 247 (let ((orig (concat file ".ORIG"))) 248 (when (file-exists-p orig) 249 (rename-file orig (make-temp-name (concat orig "_")))) 250 (rename-file file orig)) 251 (let ((setup (lambda () 252 ;; Use the same conflict marker style as Git uses. 253 (setq-local ediff-combination-pattern 254 '("<<<<<<< HEAD" A 255 ,(format "||||||| %s" revC) Ancestor 256 "=======" B 257 ,(format ">>>>>>> %s" revB))))) 258 (quit (lambda () 259 ;; For merge jobs Ediff switches buffer names around. 260 ;; At this point `ediff-buffer-C' no longer refer to 261 ;; the ancestor buffer but to the merge result buffer. 262 ;; See (if ediff-merge-job ...) in `ediff-setup'. 263 (when (buffer-live-p ediff-buffer-C) 264 (with-current-buffer ediff-buffer-C 265 (save-buffer) 266 (save-excursion 267 (goto-char (point-min)) 268 (unless (re-search-forward "^<<<<<<< " nil t) 269 (magit-stage-file file)))))))) 270 (if fileC 271 (magit-ediff-buffers 272 ((magit-get-revision-buffer revA fileA) 273 (magit-find-file-noselect revA fileA)) 274 ((magit-get-revision-buffer revB fileB) 275 (magit-find-file-noselect revB fileB)) 276 ((magit-get-revision-buffer revC fileC) 277 (magit-find-file-noselect revC fileC)) 278 setup quit file) 279 (magit-ediff-buffers 280 ((magit-get-revision-buffer revA fileA) 281 (magit-find-file-noselect revA fileA)) 282 ((magit-get-revision-buffer revB fileB) 283 (magit-find-file-noselect revB fileB)) 284 nil setup quit file)))))) 285 286 ;;;###autoload 287 (defun magit-ediff-resolve-rest (file) 288 "Resolve outstanding conflicts in the FILE at point using Ediff. 289 290 If there is no file at point or if it doesn't have any unmerged 291 changes, then prompt for a file. 292 293 See info node `(magit) Ediffing' for more information about this 294 and alternative commands." 295 (interactive (list (magit-read-unmerged-file))) 296 (magit-with-toplevel 297 (with-current-buffer (find-file-noselect file) 298 (smerge-ediff) 299 (setq-local 300 ediff-quit-hook 301 (lambda () 302 (let ((bufC ediff-buffer-C) 303 (bufS smerge-ediff-buf)) 304 (with-current-buffer bufS 305 (when (yes-or-no-p (format "Conflict resolution finished; save %s? " 306 buffer-file-name)) 307 (erase-buffer) 308 (insert-buffer-substring bufC) 309 (save-buffer)))) 310 (when (buffer-live-p ediff-buffer-A) (kill-buffer ediff-buffer-A)) 311 (when (buffer-live-p ediff-buffer-B) (kill-buffer ediff-buffer-B)) 312 (when (buffer-live-p ediff-buffer-C) (kill-buffer ediff-buffer-C)) 313 (when (buffer-live-p ediff-ancestor-buffer) 314 (kill-buffer ediff-ancestor-buffer)) 315 (let ((magit-ediff-previous-winconf smerge-ediff-windows)) 316 (run-hooks 'magit-ediff-quit-hook))))))) 317 318 ;;;###autoload 319 (defun magit-ediff-stage (file) 320 "Stage and unstage changes to FILE using Ediff. 321 FILE has to be relative to the top directory of the repository." 322 (interactive 323 (let ((files (magit-tracked-files))) 324 (list (magit-completing-read "Selectively stage file" files nil t nil nil 325 (car (member (magit-current-file) files)))))) 326 (magit-with-toplevel 327 (let* ((bufA (magit-get-revision-buffer "HEAD" file)) 328 (bufB (magit-get-revision-buffer "{index}" file)) 329 (lockB (and bufB (buffer-local-value 'buffer-read-only bufB))) 330 (bufC (get-file-buffer file)) 331 ;; Use the same encoding for all three buffers or we 332 ;; may end up changing the file in an unintended way. 333 (bufC* (or bufC (find-file-noselect file))) 334 (coding-system-for-read 335 (buffer-local-value 'buffer-file-coding-system bufC*)) 336 (bufA* (magit-find-file-noselect-1 "HEAD" file t)) 337 (bufB* (magit-find-file-index-noselect file t))) 338 (with-current-buffer bufB* (setq buffer-read-only nil)) 339 (magit-ediff-buffers 340 (bufA bufA*) 341 (bufB bufB*) 342 (bufC bufC*) 343 nil 344 (lambda () 345 (when (buffer-live-p ediff-buffer-B) 346 (when lockB 347 (with-current-buffer bufB (setq buffer-read-only t))) 348 (when (buffer-modified-p ediff-buffer-B) 349 (with-current-buffer ediff-buffer-B 350 (magit-update-index)))) 351 (when (and (buffer-live-p ediff-buffer-C) 352 (buffer-modified-p ediff-buffer-C)) 353 (with-current-buffer ediff-buffer-C 354 (when (y-or-n-p (format "Save file %s? " buffer-file-name)) 355 (save-buffer))))))))) 356 357 ;;;###autoload 358 (defun magit-ediff-compare (revA revB fileA fileB) 359 "Compare REVA:FILEA with REVB:FILEB using Ediff. 360 361 FILEA and FILEB have to be relative to the top directory of the 362 repository. If REVA or REVB is nil, then this stands for the 363 working tree state. 364 365 If the region is active, use the revisions on the first and last 366 line of the region. With a prefix argument, instead of diffing 367 the revisions, choose a revision to view changes along, starting 368 at the common ancestor of both revisions (i.e., use a \"...\" 369 range)." 370 (interactive 371 (pcase-let ((`(,revA ,revB) (magit-ediff-compare--read-revisions 372 nil current-prefix-arg))) 373 (nconc (list revA revB) 374 (magit-ediff-read-files revA revB)))) 375 (magit-ediff-buffers 376 ((if revA (magit-get-revision-buffer revA fileA) (get-file-buffer fileA)) 377 (if revA (magit-find-file-noselect revA fileA) (find-file-noselect fileA))) 378 ((if revB (magit-get-revision-buffer revB fileB) (get-file-buffer fileB)) 379 (if revB (magit-find-file-noselect revB fileB) (find-file-noselect fileB))))) 380 381 (defun magit-ediff-compare--read-revisions (&optional arg mbase) 382 (let ((input (or arg (magit-diff-read-range-or-commit 383 "Compare range or commit" 384 nil mbase)))) 385 (if-let ((range (magit-split-range input))) 386 (list (car range) (cdr range)) 387 (list input nil)))) 388 389 (defun magit-ediff-read-files (revA revB &optional fileB) 390 "Read file in REVB, return it and the corresponding file in REVA. 391 When FILEB is non-nil, use this as REVB's file instead of 392 prompting for it." 393 (unless (and fileB (member fileB (magit-revision-files revB))) 394 (setq fileB 395 (or (and fileB 396 magit-buffer-log-files 397 (derived-mode-p 'magit-log-mode) 398 (member "--follow" magit-buffer-log-args) 399 (cdr (assoc fileB 400 (magit-renamed-files 401 revB 402 (oref (car (oref magit-root-section children)) 403 value))))) 404 (magit-read-file-choice 405 (format "File to compare between %s and %s" 406 revA (or revB "the working tree")) 407 (magit-changed-files revA revB) 408 (format "No changed files between %s and %s" 409 revA (or revB "the working tree")))))) 410 (list (or (car (member fileB (magit-revision-files revA))) 411 (cdr (assoc fileB (magit-renamed-files revB revA))) 412 (magit-read-file-choice 413 (format "File in %s to compare with %s in %s" 414 revA fileB (or revB "the working tree")) 415 (magit-changed-files revB revA) 416 (format "No files have changed between %s and %s" 417 revA revB))) 418 fileB)) 419 420 ;;;###autoload 421 (defun magit-ediff-dwim () 422 "Compare, stage, or resolve using Ediff. 423 This command tries to guess what file, and what commit or range 424 the user wants to compare, stage, or resolve using Ediff. It 425 might only be able to guess either the file, or range or commit, 426 in which case the user is asked about the other. It might not 427 always guess right, in which case the appropriate `magit-ediff-*' 428 command has to be used explicitly. If it cannot read the user's 429 mind at all, then it asks the user for a command to run." 430 (interactive) 431 (magit-section-case 432 (hunk (save-excursion 433 (goto-char (oref (oref it parent) start)) 434 (magit-ediff-dwim))) 435 (t 436 (let ((range (magit-diff--dwim)) 437 (file (magit-current-file)) 438 command revA revB) 439 (pcase range 440 ((and (guard (not magit-ediff-dwim-show-on-hunks)) 441 (or 'unstaged 'staged)) 442 (setq command (if (magit-anything-unmerged-p) 443 magit-ediff-dwim-resolve-function 444 #'magit-ediff-stage))) 445 ('unstaged (setq command #'magit-ediff-show-unstaged)) 446 ('staged (setq command #'magit-ediff-show-staged)) 447 (`(commit . ,value) 448 (setq command #'magit-ediff-show-commit) 449 (setq revB value)) 450 (`(stash . ,value) 451 (setq command #'magit-ediff-show-stash) 452 (setq revB value)) 453 ((pred stringp) 454 (pcase-let ((`(,a ,b) (magit-ediff-compare--read-revisions range))) 455 (setq command #'magit-ediff-compare) 456 (setq revA a) 457 (setq revB b))) 458 (_ 459 (when (derived-mode-p 'magit-diff-mode) 460 (pcase (magit-diff-type) 461 ('committed (pcase-let ((`(,a ,b) 462 (magit-ediff-compare--read-revisions 463 magit-buffer-range))) 464 (setq revA a) 465 (setq revB b))) 466 ((guard (not magit-ediff-dwim-show-on-hunks)) 467 (setq command #'magit-ediff-stage)) 468 ('unstaged (setq command #'magit-ediff-show-unstaged)) 469 ('staged (setq command #'magit-ediff-show-staged)) 470 ('undefined (setq command nil)) 471 (_ (setq command nil)))))) 472 (cond ((not command) 473 (call-interactively 474 (magit-read-char-case 475 "Failed to read your mind; do you want to " t 476 (?c "[c]ommit" #'magit-ediff-show-commit) 477 (?r "[r]ange" #'magit-ediff-compare) 478 (?s "[s]tage" #'magit-ediff-stage) 479 (?m "[m] resolve remaining conflicts" 480 #'magit-ediff-resolve-rest) 481 (?M "[M] resolve all conflicts" 482 #'magit-ediff-resolve-all)))) 483 ((eq command #'magit-ediff-compare) 484 (apply #'magit-ediff-compare revA revB 485 (magit-ediff-read-files revA revB file))) 486 ((eq command #'magit-ediff-show-commit) 487 (magit-ediff-show-commit revB)) 488 ((eq command #'magit-ediff-show-stash) 489 (magit-ediff-show-stash revB)) 490 (file 491 (funcall command file)) 492 (t 493 (call-interactively command))))))) 494 495 ;;;###autoload 496 (defun magit-ediff-show-staged (file) 497 "Show staged changes using Ediff. 498 499 This only allows looking at the changes; to stage, unstage, 500 and discard changes using Ediff, use `magit-ediff-stage'. 501 502 FILE must be relative to the top directory of the repository." 503 (interactive 504 (list (magit-read-file-choice "Show staged changes for file" 505 (magit-staged-files) 506 "No staged files"))) 507 (magit-ediff-buffers ((magit-get-revision-buffer "HEAD" file) 508 (magit-find-file-noselect "HEAD" file)) 509 ((get-buffer (concat file ".~{index}~")) 510 (magit-find-file-index-noselect file t)))) 511 512 ;;;###autoload 513 (defun magit-ediff-show-unstaged (file) 514 "Show unstaged changes using Ediff. 515 516 This only allows looking at the changes; to stage, unstage, 517 and discard changes using Ediff, use `magit-ediff-stage'. 518 519 FILE must be relative to the top directory of the repository." 520 (interactive 521 (list (magit-read-file-choice "Show unstaged changes for file" 522 (magit-unstaged-files) 523 "No unstaged files"))) 524 (magit-ediff-buffers ((get-buffer (concat file ".~{index}~")) 525 (magit-find-file-index-noselect file t)) 526 ((get-file-buffer file) 527 (find-file-noselect file)))) 528 529 ;;;###autoload 530 (defun magit-ediff-show-working-tree (file) 531 "Show changes between `HEAD' and working tree using Ediff. 532 FILE must be relative to the top directory of the repository." 533 (interactive 534 (list (magit-read-file-choice "Show changes in file" 535 (magit-changed-files "HEAD") 536 "No changed files"))) 537 (magit-ediff-buffers ((magit-get-revision-buffer "HEAD" file) 538 (magit-find-file-noselect "HEAD" file)) 539 ((get-file-buffer file) 540 (find-file-noselect file)))) 541 542 ;;;###autoload 543 (defun magit-ediff-show-commit (commit) 544 "Show changes introduced by COMMIT using Ediff." 545 (interactive (list (magit-read-branch-or-commit "Revision"))) 546 (let ((revA (concat commit "^")) 547 (revB commit)) 548 (apply #'magit-ediff-compare 549 revA revB 550 (magit-ediff-read-files revA revB (magit-current-file))))) 551 552 ;;;###autoload 553 (defun magit-ediff-show-stash (stash) 554 "Show changes introduced by STASH using Ediff. 555 `magit-ediff-show-stash-with-index' controls whether a 556 three-buffer Ediff is used in order to distinguish changes in the 557 stash that were staged." 558 (interactive (list (magit-read-stash "Stash"))) 559 (pcase-let* ((revA (concat stash "^1")) 560 (revB (concat stash "^2")) 561 (revC stash) 562 (`(,fileA ,fileC) (magit-ediff-read-files revA revC)) 563 (fileB fileC)) 564 (if (and magit-ediff-show-stash-with-index 565 (member fileA (magit-changed-files revB revA))) 566 (magit-ediff-buffers 567 ((magit-get-revision-buffer revA fileA) 568 (magit-find-file-noselect revA fileA)) 569 ((magit-get-revision-buffer revB fileB) 570 (magit-find-file-noselect revB fileB)) 571 ((magit-get-revision-buffer revC fileC) 572 (magit-find-file-noselect revC fileC))) 573 (magit-ediff-compare revA revC fileA fileC)))) 574 575 (defun magit-ediff-cleanup-auxiliary-buffers () 576 (let* ((ctl-buf ediff-control-buffer) 577 (ctl-win (ediff-get-visible-buffer-window ctl-buf)) 578 (ctl-frm ediff-control-frame) 579 (main-frame (cond ((window-live-p ediff-window-A) 580 (window-frame ediff-window-A)) 581 ((window-live-p ediff-window-B) 582 (window-frame ediff-window-B))))) 583 (ediff-kill-buffer-carefully ediff-diff-buffer) 584 (ediff-kill-buffer-carefully ediff-custom-diff-buffer) 585 (ediff-kill-buffer-carefully ediff-fine-diff-buffer) 586 (ediff-kill-buffer-carefully ediff-tmp-buffer) 587 (ediff-kill-buffer-carefully ediff-error-buffer) 588 (ediff-kill-buffer-carefully ediff-msg-buffer) 589 (ediff-kill-buffer-carefully ediff-debug-buffer) 590 (when (boundp 'ediff-patch-diagnostics) 591 (ediff-kill-buffer-carefully ediff-patch-diagnostics)) 592 (cond ((and (display-graphic-p) 593 (frame-live-p ctl-frm)) 594 (delete-frame ctl-frm)) 595 ((window-live-p ctl-win) 596 (delete-window ctl-win))) 597 (ediff-kill-buffer-carefully ctl-buf) 598 (when (frame-live-p main-frame) 599 (select-frame main-frame)))) 600 601 (defun magit-ediff-restore-previous-winconf () 602 (set-window-configuration magit-ediff-previous-winconf)) 603 604 ;;; _ 605 (provide 'magit-ediff) 606 ;;; magit-ediff.el ends here