From 2d994d83afa0d1ee74981201cb91d3e0fab61375 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 26 Jul 2012 15:31:36 +0400 Subject: [PATCH] [git] Fix "Show Diff" from file history for directories. IDEA-63725, IDEA-53116 Show the ChangesBrowser window with differences in the folder between revisions. VcsHistoryUtil: extract revision sorting to a separate method to reuse it. --- .../openapi/vcs/history/VcsHistoryUtil.java | 23 +++- .../history/GitDiffFromHistoryHandler.java | 123 +++++++++++++++--- 2 files changed, 125 insertions(+), 21 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/VcsHistoryUtil.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/VcsHistoryUtil.java index 0cf2c513dd97..3fba542ae1e6 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/VcsHistoryUtil.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/VcsHistoryUtil.java @@ -27,6 +27,7 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.FilePath; @@ -213,9 +214,10 @@ public class VcsHistoryUtil { public void run(@NotNull ProgressIndicator indicator) { VcsFileRevision left = revision1; VcsFileRevision right = revision2; - if (findOlderNewer && compare(revision1, revision2) > 0) { - left = revision2; - right = revision1; + if (findOlderNewer) { + Pair pair = sortRevisions(revision1, revision2); + left = pair.first; + right = pair.second; } try { @@ -244,4 +246,19 @@ public class VcsHistoryUtil { }.queue(); } + /** + * Compares the given revisions and returns a pair of them, where the first one is older, and second is newer. + */ + @NotNull + public static Pair sortRevisions(@NotNull VcsFileRevision revision1, + @NotNull VcsFileRevision revision2) { + VcsFileRevision left = revision1; + VcsFileRevision right = revision2; + if (compare(revision1, revision2) > 0) { + left = revision2; + right = revision1; + } + return Pair.create(left, right); + } + } diff --git a/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java b/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java index 410131c760d8..8f2f0d26661d 100644 --- a/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java +++ b/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java @@ -22,12 +22,16 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ui.ChangesBrowser; import com.intellij.openapi.vcs.history.DiffFromHistoryHandler; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsHistoryUtil; @@ -35,9 +39,11 @@ import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.ArrayUtil; +import com.intellij.util.Consumer; import git4idea.GitFileRevision; import git4idea.GitRevisionNumber; import git4idea.GitUtil; +import git4idea.changes.GitChangeUtils; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; import git4idea.repo.GitRepository; @@ -92,34 +98,122 @@ class GitDiffFromHistoryHandler implements DiffFromHistoryHandler { private void doShowDiff(@NotNull FilePath filePath, @NotNull VcsFileRevision revision1, @NotNull VcsFileRevision revision2, boolean autoSort) { - VcsHistoryUtil.showDifferencesInBackground(myProject, filePath, revision1, revision2, autoSort); + if (!filePath.isDirectory()) { + VcsHistoryUtil.showDifferencesInBackground(myProject, filePath, revision1, revision2, autoSort); + } + else { + GitFileRevision left = (GitFileRevision)revision1; + GitFileRevision right = (GitFileRevision)revision2; + if (autoSort) { + Pair pair = VcsHistoryUtil.sortRevisions(revision1, revision2); + left = (GitFileRevision)pair.first; + right = (GitFileRevision)pair.second; + } + showDiffForDirectory(filePath, left, right); + } } - private void showDiffForMergeCommit(@NotNull final AnActionEvent event, @NotNull final FilePath filePath, @NotNull final GitFileRevision rev, - @NotNull final Collection parents) { - final AtomicBoolean fileTouched = new AtomicBoolean(); - new Task.Backgroundable(myProject, "Retrieving revision changes", false) { - @Override public void run(@NotNull ProgressIndicator indicator) { + private void showDiffForDirectory(@NotNull final FilePath path, @NotNull final GitFileRevision revision1, @NotNull final GitFileRevision revision2) { + GitRepository repository = getRepository(path); + calculateDiffInBackground(repository, revision1.getHash(), revision2.getHash(), new Consumer>() { + @Override + public void consume(List changes) { + showDirDiffDialog(path, revision1, revision2, changes); + } + }); + } + + @NotNull + private GitRepository getRepository(@NotNull FilePath path) { + VirtualFile file = path.getVirtualFile(); + LOG.assertTrue(file != null, "VirtualFile can't be null for " + path); // we clicked on a file and asked its history => VF must exist. + GitRepository repository = myRepositoryManager.getRepositoryForFile(file); + LOG.assertTrue(repository != null, "Repository is null for " + file); + return repository; + } + + private void calculateDiffInBackground(@NotNull final GitRepository repository, final String hash1, final String hash2, + final Consumer> successHandler) { + new Task.Backgroundable(myProject, "Comparing revisions...") { + private List myChanges; + @Override + public void run(@NotNull ProgressIndicator indicator) { try { - fileTouched.set(wasFileTouched(rev, filePath)); + myChanges = new ArrayList(GitChangeUtils.getDiff(repository.getProject(), repository.getRoot(), hash1, hash2, null)); } catch (VcsException e) { - LOG.info("Error happened while executing git show " + rev + ":" + filePath, e); - VcsBalloonProblemNotifier.showOverVersionControlView(GitDiffFromHistoryHandler.this.myProject, e.getMessage(), MessageType.ERROR); + showError(e, "Error during requesting diff for directory"); } } @Override public void onSuccess() { - if (fileTouched.get()) { + successHandler.consume(myChanges); + } + }.queue(); + } + + private void showDirDiffDialog(@NotNull FilePath path, GitFileRevision revision1, GitFileRevision revision2, @NotNull List diff) { + DialogBuilder dialogBuilder = new DialogBuilder(myProject); + dialogBuilder.setTitle(String.format("%s diff in %s..%s", path.getName(), GitUtil.getShortHash(revision1.getHash()), + GitUtil.getShortHash(revision2.getHash()))); + dialogBuilder.setActionDescriptors(new DialogBuilder.ActionDescriptor[] { new DialogBuilder.CloseDialogAction()}); + final ChangesBrowser changesBrowser = new ChangesBrowser(myProject, null, diff, null, false, true, + null, ChangesBrowser.MyUseCase.COMMITTED_CHANGES, null); + changesBrowser.setChangesToDisplay(diff); + dialogBuilder.setCenterPanel(changesBrowser); + dialogBuilder.show(); + } + + private void showDiffForMergeCommit(@NotNull final AnActionEvent event, @NotNull final FilePath filePath, + @NotNull final GitFileRevision rev, @NotNull final Collection parents) { + + final Consumer afterTouchCheck = new Consumer() { + @Override + public void consume(Boolean wasTouched) { + if (wasTouched) { String message = filePath.getName() + " did not change in this merge commit"; VcsBalloonProblemNotifier.showOverVersionControlView(GitDiffFromHistoryHandler.this.myProject, message, MessageType.INFO); } showPopup(event, rev, filePath, parents); } + }; + + if (filePath.isDirectory()) { // for directories don't check if the file was modified in the merge commit + afterTouchCheck.consume(false); + } + else { + checkIfFileWasTouchedInBackground(filePath, rev, afterTouchCheck); + } + } + + private void checkIfFileWasTouchedInBackground(@NotNull final FilePath filePath, @NotNull final GitFileRevision rev, + @NotNull final Consumer afterTouchCheck) { + new Task.Backgroundable(myProject, "Loading changes...", false) { + private final AtomicBoolean fileTouched = new AtomicBoolean(); + + @Override public void run(@NotNull ProgressIndicator indicator) { + try { + fileTouched.set(wasFileTouched(rev, filePath)); + } + catch (VcsException e) { + String logMessage = "Error happened while executing git show " + rev + ":" + filePath; + showError(e, logMessage); + } + } + + @Override + public void onSuccess() { + afterTouchCheck.consume(fileTouched.get()); + } }.queue(); } + private void showError(VcsException e, String logMessage) { + LOG.info(logMessage, e); + VcsBalloonProblemNotifier.showOverVersionControlView(this.myProject, e.getMessage(), MessageType.ERROR); + } + private void showPopup(@NotNull AnActionEvent event, @NotNull GitFileRevision rev, @NotNull FilePath filePath, @NotNull Collection parents) { ActionGroup parentActions = createActionGroup(rev, filePath, parents); @@ -175,10 +269,7 @@ class GitDiffFromHistoryHandler implements DiffFromHistoryHandler { } private boolean wasFileTouched(@NotNull GitFileRevision rev, @NotNull FilePath path) throws VcsException { - VirtualFile file = path.getVirtualFile(); - LOG.assertTrue(file != null, "VirtualFile can't be null for " + path); // we clicked on a file and asked its history => VF must exist. - GitRepository repository = myRepositoryManager.getRepositoryForFile(file); - LOG.assertTrue(repository != null, "Repository is null for " + file); + GitRepository repository = getRepository(path); GitCommandResult result = myGit.show(repository, rev + ":" + path); if (result.success()) { return isFilePresentInOutput(repository, path, result.getOutput()); @@ -216,9 +307,5 @@ class GitDiffFromHistoryHandler implements DiffFromHistoryHandler { doShowDiff(myFilePath, makeRevisionFromHash(myFilePath, myParentRevision), myRevision, false); } - @Override - public void update(AnActionEvent e) { - - } } }