[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.
This commit is contained in:
Kirill Likhodedov
2012-07-26 15:33:02 +04:00
parent c7fb458fc2
commit 2d994d83af
2 changed files with 125 additions and 21 deletions
@@ -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<VcsFileRevision, VcsFileRevision> 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<VcsFileRevision, VcsFileRevision> 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);
}
}
@@ -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<VcsFileRevision, VcsFileRevision> 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<String> 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<List<Change>>() {
@Override
public void consume(List<Change> 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<List<Change>> successHandler) {
new Task.Backgroundable(myProject, "Comparing revisions...") {
private List<Change> myChanges;
@Override
public void run(@NotNull ProgressIndicator indicator) {
try {
fileTouched.set(wasFileTouched(rev, filePath));
myChanges = new ArrayList<Change>(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<Change> 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<String> parents) {
final Consumer<Boolean> afterTouchCheck = new Consumer<Boolean>() {
@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<Boolean> 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<String> 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) {
}
}
}