From a05543443a49850e17071f65cd1e18a85ea0d819 Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 19 Nov 2025 22:16:10 +0300 Subject: [PATCH 1/2] Localizing and refactoring (part 2). --- plugin/src/main/java/git4idea/GitBranch.java | 2 +- plugin/src/main/java/git4idea/GitUtil.java | 9 +- plugin/src/main/java/git4idea/GitVcs.java | 2 +- .../git4idea/branch/GitBranchOperation.java | 6 +- .../git4idea/branch/GitBranchUiHandler.java | 109 +- .../branch/GitBranchUiHandlerImpl.java | 35 +- .../branch/GitCheckoutNewBranchOperation.java | 12 +- .../git4idea/branch/GitCheckoutOperation.java | 16 +- .../branch/GitDeleteBranchOperation.java | 12 +- .../GitDeleteRemoteBranchOperation.java | 4 +- .../git4idea/branch/GitMergeOperation.java | 20 +- .../branch/GitRenameBranchOperation.java | 12 +- .../git4idea/cherrypick/GitCherryPicker.java | 13 +- .../git4idea/commands/GitSSHGUIHandler.java | 2 +- .../commands/GitStandardProgressAnalyzer.java | 2 +- .../GitTaskResultNotificationHandler.java | 19 +- .../crlf/GitCrlfProblemsDetector.java | 2 +- .../git4idea/merge/GitConflictResolver.java | 50 +- .../push/GitPushResultNotification.java | 4 +- .../rebase/GitAbortRebaseProcess.java | 5 +- .../git4idea/rebase/GitRebaseProcess.java | 70 +- .../java/git4idea/rebase/GitRebaseUtils.java | 5 +- .../main/java/git4idea/rebase/GitRebaser.java | 33 +- .../main/java/git4idea/repo/GitConfig.java | 4 +- .../java/git4idea/stash/GitChangesSaver.java | 280 ++--- .../git4idea/stash/GitStashChangesSaver.java | 30 +- .../main/java/git4idea/ui/GitTagDialog.form | 143 --- .../main/java/git4idea/ui/GitTagDialog.java | 371 +++++- .../java/git4idea/ui/GitUnstashDialog.form | 165 --- .../java/git4idea/ui/GitUnstashDialog.java | 513 ++++++++- .../java/git4idea/update/GitMergeUpdater.java | 23 +- .../git4idea/update/GitUpdateProcess.java | 34 +- .../git4idea/util/GitPreservingProcess.java | 307 +++-- .../util/GitUntrackedFilesHelper.java | 55 +- .../LocalChangesWouldBeOverwrittenHelper.java | 19 +- .../branch/GitBranchWorkerTest.groovy | 27 +- .../jetbrains/git4idea/rt/ssh/SSHConfig.java | 1023 +++++++++-------- 37 files changed, 1978 insertions(+), 1460 deletions(-) delete mode 100644 plugin/src/main/java/git4idea/ui/GitTagDialog.form delete mode 100644 plugin/src/main/java/git4idea/ui/GitUnstashDialog.form diff --git a/plugin/src/main/java/git4idea/GitBranch.java b/plugin/src/main/java/git4idea/GitBranch.java index d084510..30580f2 100644 --- a/plugin/src/main/java/git4idea/GitBranch.java +++ b/plugin/src/main/java/git4idea/GitBranch.java @@ -37,7 +37,7 @@ *

*

*

GitBranches are equal, if their full names are equal. That means that if two GitBranch objects have different hashes, they - * are considered equal. But in this case an error if logged, becase it means that one of this GitBranch instances is out-of-date, and + * are considered equal. But in this case an error if logged, because it means that one of this GitBranch instances is out-of-date, and * it is required to use an {@link GitRepository#update(TrackedTopic...) updated} version.

*/ public abstract class GitBranch extends GitReference diff --git a/plugin/src/main/java/git4idea/GitUtil.java b/plugin/src/main/java/git4idea/GitUtil.java index 71f5a30..81855ee 100644 --- a/plugin/src/main/java/git4idea/GitUtil.java +++ b/plugin/src/main/java/git4idea/GitUtil.java @@ -18,6 +18,7 @@ import consulo.application.progress.ProgressIndicator; import consulo.application.progress.Task; import consulo.git.localize.GitLocalize; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.project.Project; import consulo.ui.annotation.RequiredUIAccess; @@ -1013,13 +1014,13 @@ public static List findLocalChangesForPaths( public static void showPathsInDialog( @Nonnull Project project, @Nonnull Collection absolutePaths, - @Nonnull String title, - @Nullable String description + @Nonnull LocalizeValue title, + @Nonnull LocalizeValue description ) { DialogBuilder builder = new DialogBuilder(project); builder.setCenterPanel(new GitSimplePathsBrowser(project, absolutePaths)); - if (description != null) { - builder.setNorthPanel(new MultiLineLabel(description)); + if (description != LocalizeValue.empty()) { + builder.setNorthPanel(new MultiLineLabel(description.get())); } builder.addOkAction(); builder.setTitle(title); diff --git a/plugin/src/main/java/git4idea/GitVcs.java b/plugin/src/main/java/git4idea/GitVcs.java index 62e3d13..bd67438 100644 --- a/plugin/src/main/java/git4idea/GitVcs.java +++ b/plugin/src/main/java/git4idea/GitVcs.java @@ -416,7 +416,7 @@ public void checkVersion() { myNotificationService.newError(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) .title(LocalizeValue.localizeTODO("Unsupported Git version")) .content(LocalizeValue.localizeTODO(message)) - .optionalHyperlinkListener(new NotificationListener.Adapter() { + .hyperlinkListener(new NotificationListener.Adapter() { @Override protected void hyperlinkActivated(@Nonnull Notification notification, @Nonnull HyperlinkEvent e) { if (SETTINGS_LINK.equals(e.getDescription())) { diff --git a/plugin/src/main/java/git4idea/branch/GitBranchOperation.java b/plugin/src/main/java/git4idea/branch/GitBranchOperation.java index c3e0bee..48fb630 100644 --- a/plugin/src/main/java/git4idea/branch/GitBranchOperation.java +++ b/plugin/src/main/java/git4idea/branch/GitBranchOperation.java @@ -107,7 +107,7 @@ protected GitBranchOperation( public abstract LocalizeValue getSuccessMessage(); @Nonnull - protected abstract String getRollbackProposal(); + protected abstract LocalizeValue getRollbackProposal(); /** * Returns a short downcased name of the operation. @@ -115,7 +115,7 @@ protected GitBranchOperation( * Some operations (like checkout new branch) can be not mentioned in these dialogs, so their operation names would be not used. */ @Nonnull - protected abstract String getOperationName(); + protected abstract LocalizeValue getOperationName(); /** * @return next repository that wasn't handled (e.g. checked out) yet. @@ -224,7 +224,7 @@ protected void fatalError(@Nonnull LocalizeValue title, @Nonnull LocalizeValue m } protected void showFatalErrorDialogWithRollback(@Nonnull LocalizeValue title, @Nonnull LocalizeValue message) { - boolean rollback = myUiHandler.notifyErrorWithRollbackProposal(title.get(), message.get(), getRollbackProposal()); + boolean rollback = myUiHandler.notifyErrorWithRollbackProposal(title, message, getRollbackProposal()); if (rollback) { rollback(); } diff --git a/plugin/src/main/java/git4idea/branch/GitBranchUiHandler.java b/plugin/src/main/java/git4idea/branch/GitBranchUiHandler.java index bc63132..27d0a7f 100644 --- a/plugin/src/main/java/git4idea/branch/GitBranchUiHandler.java +++ b/plugin/src/main/java/git4idea/branch/GitBranchUiHandler.java @@ -20,6 +20,7 @@ import java.util.Map; import consulo.application.progress.ProgressIndicator; +import consulo.localize.LocalizeValue; import org.intellij.lang.annotations.MagicConstant; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -35,59 +36,73 @@ * Some methods return the choice selected by user to the calling code, if it is needed.

*

The purpose of this class is to separate UI interaction from the main code, which would in particular simplify testing.

*/ -public interface GitBranchUiHandler -{ - - @Nonnull +public interface GitBranchUiHandler { + @Nonnull ProgressIndicator getProgressIndicator(); - boolean notifyErrorWithRollbackProposal(@Nonnull String title, @Nonnull String message, @Nonnull String rollbackProposal); - - /** - * Shows notification about unmerged files preventing checkout, merge, etc. - * - * @param operationName - * @param repositories - */ - void showUnmergedFilesNotification(@Nonnull String operationName, @Nonnull Collection repositories); + boolean notifyErrorWithRollbackProposal(@Nonnull LocalizeValue title, @Nonnull LocalizeValue message, @Nonnull LocalizeValue rollbackProposal); - /** - * Shows a modal notification about unmerged files preventing an operation, with "Rollback" button. - * Pressing "Rollback" would should the operation which has already successfully executed on other repositories. - * - * @param operationName - * @param rollbackProposal - * @return true if user has agreed to rollback, false if user denied the rollback proposal. - */ - boolean showUnmergedFilesMessageWithRollback(@Nonnull String operationName, @Nonnull String rollbackProposal); + /** + * Shows notification about unmerged files preventing checkout, merge, etc. + * + * @param operationName + * @param repositories + */ + void showUnmergedFilesNotification(@Nonnull LocalizeValue operationName, @Nonnull Collection repositories); - /** - * Show notification about "untracked files would be overwritten by merge/checkout". - */ - void showUntrackedFilesNotification(@Nonnull String operationName, @Nonnull VirtualFile root, @Nonnull Collection relativePaths); + /** + * Shows a modal notification about unmerged files preventing an operation, with "Rollback" button. + * Pressing "Rollback" would should the operation which has already successfully executed on other repositories. + * + * @param operationName + * @param rollbackProposal + * @return true if user has agreed to rollback, false if user denied the rollback proposal. + */ + boolean showUnmergedFilesMessageWithRollback(@Nonnull LocalizeValue operationName, @Nonnull LocalizeValue rollbackProposal); - boolean showUntrackedFilesDialogWithRollback(@Nonnull String operationName, @Nonnull String rollbackProposal, @Nonnull VirtualFile root, @Nonnull Collection relativePaths); + /** + * Show notification about "untracked files would be overwritten by merge/checkout". + */ + void showUntrackedFilesNotification( + @Nonnull LocalizeValue operationName, + @Nonnull VirtualFile root, + @Nonnull Collection relativePaths + ); - /** - * Shows the dialog proposing to execute the operation (checkout or merge) smartly, i.e. stash-execute-unstash. - * - * @param project - * @param changes local changes that would be overwritten by checkout or merge. - * @param paths paths reported by Git (in most cases this is covered by {@code changes}. - * @param operation operation name: checkout or merge - * @param forceButtonTitle if the operation can be executed force (force checkout is possible), - * specify the title of the force button; otherwise (force merge is not possible) pass null. - * @return the code of the decision. - */ - @MagicConstant(valuesFromClass = GitSmartOperationDialog.class) - int showSmartOperationDialog(@Nonnull Project project, @Nonnull List changes, @Nonnull Collection paths, @Nonnull String operation, @Nullable String forceButtonTitle); + boolean showUntrackedFilesDialogWithRollback( + @Nonnull LocalizeValue operationName, + @Nonnull LocalizeValue rollbackProposal, + @Nonnull VirtualFile root, + @Nonnull Collection relativePaths + ); - /** - * @return true if user decided to restore the branch. - */ - boolean showBranchIsNotFullyMergedDialog(@Nonnull Project project, - @Nonnull Map> history, - @Nonnull Map baseBranches, - @Nonnull String removedBranch); + /** + * Shows the dialog proposing to execute the operation (checkout or merge) smartly, i.e. stash-execute-unstash. + * + * @param project + * @param changes local changes that would be overwritten by checkout or merge. + * @param paths paths reported by Git (in most cases this is covered by {@code changes}. + * @param operation operation name: checkout or merge + * @param forceButtonTitle if the operation can be executed force (force checkout is possible), + * specify the title of the force button; otherwise (force merge is not possible) pass null. + * @return the code of the decision. + */ + @MagicConstant(valuesFromClass = GitSmartOperationDialog.class) + int showSmartOperationDialog( + @Nonnull Project project, + @Nonnull List changes, + @Nonnull Collection paths, + @Nonnull String operation, + @Nullable String forceButtonTitle + ); + /** + * @return true if user decided to restore the branch. + */ + boolean showBranchIsNotFullyMergedDialog( + @Nonnull Project project, + @Nonnull Map> history, + @Nonnull Map baseBranches, + @Nonnull String removedBranch + ); } diff --git a/plugin/src/main/java/git4idea/branch/GitBranchUiHandlerImpl.java b/plugin/src/main/java/git4idea/branch/GitBranchUiHandlerImpl.java index df4ba2d..5f3f5a1 100644 --- a/plugin/src/main/java/git4idea/branch/GitBranchUiHandlerImpl.java +++ b/plugin/src/main/java/git4idea/branch/GitBranchUiHandlerImpl.java @@ -23,7 +23,6 @@ import consulo.ui.annotation.RequiredUIAccess; import consulo.ui.ex.awt.Messages; import consulo.ui.ex.awt.UIUtil; -import consulo.util.lang.StringUtil; import consulo.util.lang.xml.XmlStringUtil; import consulo.versionControlSystem.VcsNotifier; import consulo.versionControlSystem.change.Change; @@ -66,21 +65,21 @@ public GitBranchUiHandlerImpl(@Nonnull Project project, @Nonnull Git git, @Nonnu @Override public boolean notifyErrorWithRollbackProposal( - @Nonnull String title, - @Nonnull String message, - @Nonnull String rollbackProposal + @Nonnull LocalizeValue title, + @Nonnull LocalizeValue message, + @Nonnull LocalizeValue rollbackProposal ) { AtomicBoolean ok = new AtomicBoolean(); UIUtil.invokeAndWaitIfNeeded((Runnable) () -> { StringBuilder description = new StringBuilder(); - if (!StringUtil.isEmptyOrSpaces(message)) { + if (message != LocalizeValue.empty()) { description.append(message).append("
"); } description.append(rollbackProposal); ok.set(Messages.YES == DialogManager.showOkCancelDialog( myProject, XmlStringUtil.wrapInHtml(description), - title, + title.get(), "Rollback", "Don't rollback", UIUtil.getErrorIcon() @@ -90,18 +89,18 @@ public boolean notifyErrorWithRollbackProposal( } @Override - public void showUnmergedFilesNotification(@Nonnull String operationName, @Nonnull Collection repositories) { + public void showUnmergedFilesNotification(@Nonnull LocalizeValue operationName, @Nonnull Collection repositories) { myNotificationService.newError(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) .title(unmergedFilesErrorTitle(operationName)) .content(unmergedFilesErrorNotificationDescription(operationName)) - .optionalHyperlinkListener((notification, event) -> { + .hyperlinkListener((notification, event) -> { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED && event.getDescription().equals("resolve")) { GitConflictResolver.Params params = new GitConflictResolver.Params() - .setMergeDescription(String.format( + .setMergeDescription(LocalizeValue.localizeTODO(String.format( "The following files have unresolved conflicts. You need to resolve them before %s.", operationName - )) - .setErrorNotificationTitle("Unresolved files remain."); + ))) + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Unresolved files remain.")); new GitConflictResolver(myProject, myGit, GitUtil.getRootsFromRepositories(repositories), params).merge(); } }) @@ -109,7 +108,7 @@ public void showUnmergedFilesNotification(@Nonnull String operationName, @Nonnul } @Override - public boolean showUnmergedFilesMessageWithRollback(@Nonnull String operationName, @Nonnull String rollbackProposal) { + public boolean showUnmergedFilesMessageWithRollback(@Nonnull LocalizeValue operationName, @Nonnull LocalizeValue rollbackProposal) { AtomicBoolean ok = new AtomicBoolean(); UIUtil.invokeAndWaitIfNeeded((Runnable) () -> { String description = String.format( @@ -133,18 +132,18 @@ public boolean showUnmergedFilesMessageWithRollback(@Nonnull String operationNam @Override public void showUntrackedFilesNotification( - @Nonnull String operationName, + @Nonnull LocalizeValue operationName, @Nonnull VirtualFile root, @Nonnull Collection relativePaths ) { - GitUntrackedFilesHelper.notifyUntrackedFilesOverwrittenBy(myProject, root, relativePaths, operationName, null); + GitUntrackedFilesHelper.notifyUntrackedFilesOverwrittenBy(myProject, root, relativePaths, operationName, LocalizeValue.empty()); } @Override @RequiredUIAccess public boolean showUntrackedFilesDialogWithRollback( - @Nonnull String operationName, - @Nonnull String rollbackProposal, + @Nonnull LocalizeValue operationName, + @Nonnull LocalizeValue rollbackProposal, @Nonnull VirtualFile root, @Nonnull Collection relativePaths ) { @@ -205,12 +204,12 @@ public boolean showBranchIsNotFullyMergedDialog( } @Nonnull - private static LocalizeValue unmergedFilesErrorTitle(@Nonnull String operationName) { + private static LocalizeValue unmergedFilesErrorTitle(@Nonnull LocalizeValue operationName) { return LocalizeValue.localizeTODO("Can't " + operationName + " because of unmerged files"); } @Nonnull - private static LocalizeValue unmergedFilesErrorNotificationDescription(String operationName) { + private static LocalizeValue unmergedFilesErrorNotificationDescription(@Nonnull LocalizeValue operationName) { return LocalizeValue.localizeTODO( "You have to resolve all merge conflicts before " + operationName + ".
" + "After resolving conflicts you also probably would want to commit your files to the current branch." diff --git a/plugin/src/main/java/git4idea/branch/GitCheckoutNewBranchOperation.java b/plugin/src/main/java/git4idea/branch/GitCheckoutNewBranchOperation.java index d642bd3..2bde14c 100644 --- a/plugin/src/main/java/git4idea/branch/GitCheckoutNewBranchOperation.java +++ b/plugin/src/main/java/git4idea/branch/GitCheckoutNewBranchOperation.java @@ -100,16 +100,18 @@ public LocalizeValue getSuccessMessage() { @Nonnull @Override - protected String getRollbackProposal() { - return "However checkout has succeeded for the following " + repositories() + ":
" + + protected LocalizeValue getRollbackProposal() { + return LocalizeValue.localizeTODO( + "However checkout has succeeded for the following " + repositories() + ":
" + successfulRepositoriesJoined() + - "
You may rollback (checkout previous branch back, and delete " + myNewBranchName + ") not to let branches diverge."; + "
You may rollback (checkout previous branch back, and delete " + myNewBranchName + ") not to let branches diverge." + ); } @Nonnull @Override - protected String getOperationName() { - return "checkout"; + protected LocalizeValue getOperationName() { + return LocalizeValue.localizeTODO("checkout"); } @Override diff --git a/plugin/src/main/java/git4idea/branch/GitCheckoutOperation.java b/plugin/src/main/java/git4idea/branch/GitCheckoutOperation.java index 78465e4..80e0901 100644 --- a/plugin/src/main/java/git4idea/branch/GitCheckoutOperation.java +++ b/plugin/src/main/java/git4idea/branch/GitCheckoutOperation.java @@ -86,7 +86,7 @@ class GitCheckoutOperation extends GitBranchOperation { protected void execute() { saveAllDocuments(); boolean fatalErrorHappened = false; - try (AccessToken token = DvcsUtil.workingTreeChangeStarted(myProject, getOperationName())) { + try (AccessToken token = DvcsUtil.workingTreeChangeStarted(myProject, getOperationName().get())) { while (hasMoreRepositories() && !fatalErrorHappened) { GitRepository repository = next(); @@ -150,7 +150,7 @@ else if (!myRefShouldBeValid && unknownPathspec.hasHappened()) { myNotificationService.newInfo(VcsNotifier.NOTIFICATION_GROUP_ID) .content(LocalizeValue.localizeTODO(mentionSuccess + mentionSkipped + "
Rollback")) - .optionalHyperlinkListener(new RollbackOperationNotificationListener()) + .hyperlinkListener(new RollbackOperationNotificationListener()) .notify(myProject); updateRecentBranch(); } @@ -211,15 +211,17 @@ else if (smartCheckoutDecision == GitSmartOperationDialog.FORCE_EXIT_CODE) { @Nonnull @Override - protected String getRollbackProposal() { - return "However checkout has succeeded for the following " + repositories() + ":
" + - successfulRepositoriesJoined() + "
" + ROLLBACK_PROPOSAL_FORMAT; + protected LocalizeValue getRollbackProposal() { + return LocalizeValue.localizeTODO( + "However checkout has succeeded for the following " + repositories() + ":
" + + successfulRepositoriesJoined() + "
" + ROLLBACK_PROPOSAL_FORMAT + ); } @Nonnull @Override - protected String getOperationName() { - return "checkout"; + protected LocalizeValue getOperationName() { + return LocalizeValue.localizeTODO("checkout"); } @Override diff --git a/plugin/src/main/java/git4idea/branch/GitDeleteBranchOperation.java b/plugin/src/main/java/git4idea/branch/GitDeleteBranchOperation.java index 6d0e6ee..d603ec9 100644 --- a/plugin/src/main/java/git4idea/branch/GitDeleteBranchOperation.java +++ b/plugin/src/main/java/git4idea/branch/GitDeleteBranchOperation.java @@ -254,16 +254,18 @@ public LocalizeValue getSuccessMessage() { @Nonnull @Override - protected String getRollbackProposal() { - return "However branch deletion has succeeded for the following " + repositories() + ":
" + + protected LocalizeValue getRollbackProposal() { + return LocalizeValue.localizeTODO( + "However branch deletion has succeeded for the following " + repositories() + ":
" + successfulRepositoriesJoined() + - "
You may rollback (recreate " + myBranchName + " in these roots) not to let branches diverge."; + "
You may rollback (recreate " + myBranchName + " in these roots) not to let branches diverge." + ); } @Nonnull @Override - protected String getOperationName() { - return "branch deletion"; + protected LocalizeValue getOperationName() { + return LocalizeValue.localizeTODO("branch deletion"); } @Nonnull diff --git a/plugin/src/main/java/git4idea/branch/GitDeleteRemoteBranchOperation.java b/plugin/src/main/java/git4idea/branch/GitDeleteRemoteBranchOperation.java index a474366..404698c 100644 --- a/plugin/src/main/java/git4idea/branch/GitDeleteRemoteBranchOperation.java +++ b/plugin/src/main/java/git4idea/branch/GitDeleteRemoteBranchOperation.java @@ -108,13 +108,13 @@ public LocalizeValue getSuccessMessage() { @Nonnull @Override - protected String getRollbackProposal() { + protected LocalizeValue getRollbackProposal() { throw new UnsupportedOperationException(); } @Nonnull @Override - protected String getOperationName() { + protected LocalizeValue getOperationName() { throw new UnsupportedOperationException(); } diff --git a/plugin/src/main/java/git4idea/branch/GitMergeOperation.java b/plugin/src/main/java/git4idea/branch/GitMergeOperation.java index e13ddaa..a130c25 100644 --- a/plugin/src/main/java/git4idea/branch/GitMergeOperation.java +++ b/plugin/src/main/java/git4idea/branch/GitMergeOperation.java @@ -83,7 +83,7 @@ protected void execute() { saveAllDocuments(); boolean fatalErrorHappened = false; int alreadyUpToDateRepositories = 0; - try (AccessToken token = DvcsUtil.workingTreeChangeStarted(myProject, getOperationName())) { + try (AccessToken token = DvcsUtil.workingTreeChangeStarted(myProject, getOperationName().get())) { while (hasMoreRepositories() && !fatalErrorHappened) { GitRepository repository = next(); LOG.info("next repository: " + repository); @@ -185,7 +185,7 @@ protected void notifySuccess(@Nonnull LocalizeValue message) { case PROPOSE: myNotificationService.newInfo(VcsNotifier.NOTIFICATION_GROUP_ID) .content(LocalizeValue.localizeTODO(message + "
Delete " + myBranchToMerge + "")) - .optionalHyperlinkListener(new DeleteMergedLocalBranchNotificationListener()) + .hyperlinkListener(new DeleteMergedLocalBranchNotificationListener()) .notify(myProject); break; case NOTHING: @@ -194,6 +194,7 @@ protected void notifySuccess(@Nonnull LocalizeValue message) { } } + @RequiredUIAccess private boolean resolveConflicts() { return myConflictedRepositories.isEmpty() || new MyMergeConflictResolver().merge(); } @@ -371,16 +372,17 @@ public LocalizeValue getSuccessMessage() { @Nonnull @Override - protected String getRollbackProposal() { - return "However merge has succeeded for the following " + repositories() + ":
" + - successfulRepositoriesJoined() + - "
" + ROLLBACK_PROPOSAL; + protected LocalizeValue getRollbackProposal() { + return LocalizeValue.localizeTODO( + "However merge has succeeded for the following " + repositories() + ":
" + + successfulRepositoriesJoined() + "
" + ROLLBACK_PROPOSAL + ); } @Nonnull @Override - protected String getOperationName() { - return "merge"; + protected LocalizeValue getOperationName() { + return LocalizeValue.localizeTODO("merge"); } private void refresh(GitRepository... repositories) { @@ -408,7 +410,7 @@ protected void notifyUnresolvedRemain() { myNotificationService.newWarn(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) .title(LocalizeValue.localizeTODO("Merged branch " + myBranchToMerge + " with conflicts")) .content(LocalizeValue.localizeTODO("Unresolved conflicts remain in the project. Resolve now.")) - .optionalHyperlinkListener(getResolveLinkListener()) + .hyperlinkListener(getResolveLinkListener()) .notify(myProject); } } diff --git a/plugin/src/main/java/git4idea/branch/GitRenameBranchOperation.java b/plugin/src/main/java/git4idea/branch/GitRenameBranchOperation.java index 115dfbe..6a5c0a2 100644 --- a/plugin/src/main/java/git4idea/branch/GitRenameBranchOperation.java +++ b/plugin/src/main/java/git4idea/branch/GitRenameBranchOperation.java @@ -101,16 +101,18 @@ public LocalizeValue getSuccessMessage() { @Nonnull @Override - protected String getRollbackProposal() { - return "However rename has succeeded for the following " + repositories() + ":
" + + protected LocalizeValue getRollbackProposal() { + return LocalizeValue.localizeTODO( + "However rename has succeeded for the following " + repositories() + ":
" + successfulRepositoriesJoined() + - "
You may rollback (rename branch back to " + myCurrentName + ") not to let branches diverge."; + "
You may rollback (rename branch back to " + myCurrentName + ") not to let branches diverge." + ); } @Nonnull @Override - protected String getOperationName() { - return "rename"; + protected LocalizeValue getOperationName() { + return LocalizeValue.localizeTODO("rename"); } private static void refresh(@Nonnull GitRepository repository) { diff --git a/plugin/src/main/java/git4idea/cherrypick/GitCherryPicker.java b/plugin/src/main/java/git4idea/cherrypick/GitCherryPicker.java index b45bd45..ef84dcf 100644 --- a/plugin/src/main/java/git4idea/cherrypick/GitCherryPicker.java +++ b/plugin/src/main/java/git4idea/cherrypick/GitCherryPicker.java @@ -214,8 +214,8 @@ else if (untrackedFilesDetector.wasMessageDetected()) { myProject, repository.getRoot(), untrackedFilesDetector.getRelativeFilePaths(), - "cherry-pick", - description + LocalizeValue.localizeTODO("cherry-pick"), + LocalizeValue.localizeTODO(description) ); return false; } @@ -289,7 +289,7 @@ private void notifyConflictWarning( myNotificationService.newWarn(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) .title(LocalizeValue.localizeTODO("Cherry-picked with conflicts")) .content(LocalizeValue.localizeTODO(description)) - .optionalHyperlinkListener(resolveLinkListener) + .hyperlinkListener(resolveLinkListener) .notify(myProject); } @@ -678,10 +678,9 @@ public CherryPickConflictResolver( } private static Params makeParams(String commitHash, String commitAuthor, String commitMessage) { - Params params = new Params(); - params.setErrorNotificationTitle("Cherry-picked with conflicts"); - params.setMergeDialogCustomizer(new CherryPickMergeDialogCustomizer(commitHash, commitAuthor, commitMessage)); - return params; + return new Params() + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Cherry-picked with conflicts")) + .setMergeDialogCustomizer(new CherryPickMergeDialogCustomizer(commitHash, commitAuthor, commitMessage)); } @Override diff --git a/plugin/src/main/java/git4idea/commands/GitSSHGUIHandler.java b/plugin/src/main/java/git4idea/commands/GitSSHGUIHandler.java index f82e9de..fcbb532 100644 --- a/plugin/src/main/java/git4idea/commands/GitSSHGUIHandler.java +++ b/plugin/src/main/java/git4idea/commands/GitSSHGUIHandler.java @@ -83,7 +83,7 @@ public String askPassphrase(final String username, final String keyPath, boolean * * @param resetPassword true, if last entered password was incorrect * @param lastError the last error - * @return the error to show on the password dialo or null + * @return the error to show on the password dialog or null */ @Nullable private String processLastError(boolean resetPassword, final String lastError) { diff --git a/plugin/src/main/java/git4idea/commands/GitStandardProgressAnalyzer.java b/plugin/src/main/java/git4idea/commands/GitStandardProgressAnalyzer.java index e00cd04..ba6580f 100644 --- a/plugin/src/main/java/git4idea/commands/GitStandardProgressAnalyzer.java +++ b/plugin/src/main/java/git4idea/commands/GitStandardProgressAnalyzer.java @@ -31,7 +31,7 @@ public class GitStandardProgressAnalyzer implements GitProgressAnalyzer { // progress of each operation is stored here. this is an overhead since operations go one by one, - // but it looks simpler than storing current operation, checking that ther was no skipped, etc. + // but it looks simpler than storing current operation, checking that there was no skipped, etc. private Map myOperationsProgress = new HashMap<>(); public static GitLineHandlerListener createListener(final ProgressIndicator indicator) { diff --git a/plugin/src/main/java/git4idea/commands/GitTaskResultNotificationHandler.java b/plugin/src/main/java/git4idea/commands/GitTaskResultNotificationHandler.java index fc4fcb8..0e275e9 100644 --- a/plugin/src/main/java/git4idea/commands/GitTaskResultNotificationHandler.java +++ b/plugin/src/main/java/git4idea/commands/GitTaskResultNotificationHandler.java @@ -17,14 +17,20 @@ import consulo.localize.LocalizeValue; import consulo.project.Project; +import consulo.project.ui.notification.NotificationService; import consulo.versionControlSystem.VcsNotifier; import jakarta.annotation.Nonnull; public class GitTaskResultNotificationHandler extends GitTaskResultHandlerAdapter { @Nonnull private final Project myProject; + @Nonnull + private final NotificationService myNotificationService; + @Nonnull private final LocalizeValue mySuccessMessage; + @Nonnull private final LocalizeValue myCancelMessage; + @Nonnull private final LocalizeValue myErrorMessage; public GitTaskResultNotificationHandler( @@ -34,6 +40,7 @@ public GitTaskResultNotificationHandler( @Nonnull LocalizeValue errorMessage ) { myProject = project; + myNotificationService = NotificationService.getInstance(); mySuccessMessage = successMessage; myCancelMessage = cancelMessage; myErrorMessage = errorMessage; @@ -41,16 +48,22 @@ public GitTaskResultNotificationHandler( @Override protected void onSuccess() { - VcsNotifier.getInstance(myProject).notifySuccess(mySuccessMessage.get()); + myNotificationService.newInfo(VcsNotifier.NOTIFICATION_GROUP_ID) + .content(mySuccessMessage) + .notify(myProject); } @Override protected void onCancel() { - VcsNotifier.getInstance(myProject).notifySuccess(myCancelMessage.get()); + myNotificationService.newInfo(VcsNotifier.NOTIFICATION_GROUP_ID) + .content(myCancelMessage) + .notify(myProject); } @Override protected void onFailure() { - VcsNotifier.getInstance(myProject).notifyError("", myErrorMessage.get()); + myNotificationService.newError(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) + .content(myErrorMessage) + .notify(myProject); } } diff --git a/plugin/src/main/java/git4idea/crlf/GitCrlfProblemsDetector.java b/plugin/src/main/java/git4idea/crlf/GitCrlfProblemsDetector.java index 667ed63..c2362cf 100644 --- a/plugin/src/main/java/git4idea/crlf/GitCrlfProblemsDetector.java +++ b/plugin/src/main/java/git4idea/crlf/GitCrlfProblemsDetector.java @@ -38,7 +38,7 @@ *
  • Checks if {@code core.autocrlf} is set to {@code true} or {@code input}.
  • *
  • If not, checks if files contain CRLFs.
  • *
  • - * For files with CRLFs checks if there are gitattributes set on them, such that would either force CRLF conversion on checkin, + * For files with CRLFs checks if there are git attributes set on them, such that would either force CRLF conversion on checkin, * either indicate that these CRLFs are here intentionally. *
  • * diff --git a/plugin/src/main/java/git4idea/merge/GitConflictResolver.java b/plugin/src/main/java/git4idea/merge/GitConflictResolver.java index bb874a6..f9aac68 100644 --- a/plugin/src/main/java/git4idea/merge/GitConflictResolver.java +++ b/plugin/src/main/java/git4idea/merge/GitConflictResolver.java @@ -77,13 +77,16 @@ public class GitConflictResolver { */ public static class Params { private boolean reverse; - private String myErrorNotificationTitle = ""; - private String myErrorNotificationAdditionalDescription = ""; - private String myMergeDescription = ""; + @Nonnull + private LocalizeValue myErrorNotificationTitle = LocalizeValue.empty(); + @Nonnull + private LocalizeValue myErrorNotificationAdditionalDescription = LocalizeValue.empty(); + @Nonnull + private LocalizeValue myMergeDescription = LocalizeValue.empty(); private MergeDialogCustomizer myMergeDialogCustomizer = new MergeDialogCustomizer() { @Override public String getMultipleFileMergeDescription(@Nonnull Collection files) { - return myMergeDescription; + return myMergeDescription.get(); } }; @@ -95,17 +98,17 @@ public Params setReverse(boolean reverseMerge) { return this; } - public Params setErrorNotificationTitle(String errorNotificationTitle) { + public Params setErrorNotificationTitle(@Nonnull LocalizeValue errorNotificationTitle) { myErrorNotificationTitle = errorNotificationTitle; return this; } - public Params setErrorNotificationAdditionalDescription(String errorNotificationAdditionalDescription) { + public Params setErrorNotificationAdditionalDescription(@Nonnull LocalizeValue errorNotificationAdditionalDescription) { myErrorNotificationAdditionalDescription = errorNotificationAdditionalDescription; return this; } - public Params setMergeDescription(String mergeDescription) { + public Params setMergeDescription(@Nonnull LocalizeValue mergeDescription) { myMergeDescription = mergeDescription; return this; } @@ -183,10 +186,13 @@ public final boolean mergeNoProceed() { * Shows notification that not all conflicts were resolved. */ protected void notifyUnresolvedRemain() { - notifyWarning( - myParams.myErrorNotificationTitle, - "You have to resolve all conflicts first." + myParams.myErrorNotificationAdditionalDescription - ); + myNotificationService.newWarn(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) + .title(myParams.myErrorNotificationTitle) + .content(LocalizeValue.localizeTODO( + "You have to resolve all conflicts first." + myParams.myErrorNotificationAdditionalDescription + )) + .hyperlinkListener(new ResolveNotificationListener()) + .notify(myProject); } /** @@ -194,17 +200,13 @@ protected void notifyUnresolvedRemain() { * notification. */ private void notifyUnresolvedRemainAfterNotification() { - notifyWarning( - "Not all conflicts resolved", - "You should resolve all conflicts before update.
    " + myParams.myErrorNotificationAdditionalDescription - ); - } - - private void notifyWarning(String title, String content) { myNotificationService.newWarn(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) - .title(LocalizeValue.localizeTODO(title)) - .content(LocalizeValue.localizeTODO(content)) - .optionalHyperlinkListener(new ResolveNotificationListener()) + .title(LocalizeValue.localizeTODO("Not all conflicts resolved")) + .content(LocalizeValue.localizeTODO( + "You should resolve all conflicts before update.
    " + + myParams.myErrorNotificationAdditionalDescription + )) + .hyperlinkListener(new ResolveNotificationListener()) .notify(myProject); } @@ -241,7 +243,6 @@ private boolean merge(boolean mergeDialogInvokedFromNotification) { } } return false; - } @RequiredUIAccess @@ -259,12 +260,12 @@ private void showMergeDialog(@Nonnull Collection initiallyUnmergedF private void notifyException(VcsException e) { LOG.info("mergeFiles ", e); myNotificationService.newError(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) - .title(LocalizeValue.localizeTODO(myParams.myErrorNotificationTitle)) + .title(myParams.myErrorNotificationTitle) .content(LocalizeValue.localizeTODO( "Couldn't check the working tree for unmerged files because of an error." + myParams.myErrorNotificationAdditionalDescription + "
    " + e.getLocalizedMessage() )) - .optionalHyperlinkListener(new ResolveNotificationListener()) + .hyperlinkListener(new ResolveNotificationListener()) .notify(myProject); } @@ -343,5 +344,4 @@ private List unmergedFiles(VirtualFile root) throws VcsException { return sortVirtualFilesByPresentation(findVirtualFilesWithRefresh(files)); } } - } diff --git a/plugin/src/main/java/git4idea/push/GitPushResultNotification.java b/plugin/src/main/java/git4idea/push/GitPushResultNotification.java index 335d1c4..747c300 100644 --- a/plugin/src/main/java/git4idea/push/GitPushResultNotification.java +++ b/plugin/src/main/java/git4idea/push/GitPushResultNotification.java @@ -15,7 +15,6 @@ */ package git4idea.push; -import consulo.application.Application; import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.project.Project; @@ -34,7 +33,6 @@ import git4idea.branch.GitBranchUtil; import git4idea.repo.GitRepository; import git4idea.update.GitUpdateResult; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -102,7 +100,7 @@ else if (!grouped.rejected.isEmpty() || !grouped.customRejected.isEmpty()) { UpdatedFiles updatedFiles = pushResult.getUpdatedFiles(); if (!updatedFiles.isEmpty()) { - Application.get().invokeLater(() -> { + project.getApplication().invokeLater(() -> { UpdateInfoTree tree = ProjectLevelVcsManager.getInstance(project).showUpdateProjectInfo( updatedFiles, "Update", diff --git a/plugin/src/main/java/git4idea/rebase/GitAbortRebaseProcess.java b/plugin/src/main/java/git4idea/rebase/GitAbortRebaseProcess.java index cfe5dd2..8879755 100644 --- a/plugin/src/main/java/git4idea/rebase/GitAbortRebaseProcess.java +++ b/plugin/src/main/java/git4idea/rebase/GitAbortRebaseProcess.java @@ -176,8 +176,9 @@ private void doAbort(boolean rollback) { if (!result.success()) { myNotificationService.newError(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) .title(LocalizeValue.localizeTODO("Rebase Abort Failed")) - .content(LocalizeValue.localizeTODO( - result.getErrorOutputAsHtmlValue() + mentionLocalChangesRemainingInStash(mySaver) + .content(LocalizeValue.join( + result.getErrorOutputAsHtmlValue(), + mentionLocalChangesRemainingInStash(mySaver) )) .notify(myProject); return; diff --git a/plugin/src/main/java/git4idea/rebase/GitRebaseProcess.java b/plugin/src/main/java/git4idea/rebase/GitRebaseProcess.java index e585850..635ccd3 100644 --- a/plugin/src/main/java/git4idea/rebase/GitRebaseProcess.java +++ b/plugin/src/main/java/git4idea/rebase/GitRebaseProcess.java @@ -386,7 +386,7 @@ private void notifySuccess( myNotificationService.newInfo(VcsNotifier.STANDARD_NOTIFICATION) .title(LocalizeValue.localizeTODO("Rebase Successful")) .content(LocalizeValue.localizeTODO(message)) - .optionalHyperlinkListener(new NotificationListener.Adapter() { + .hyperlinkListener(new NotificationListener.Adapter() { @Override protected void hyperlinkActivated(@Nonnull Notification notification, @Nonnull HyperlinkEvent e) { handlePossibleCommitLinks(e.getDescription(), skippedCommits); @@ -411,12 +411,14 @@ private void notifyNotAllConflictsResolved( ) { myNotificationService.newWarn(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) .title(LocalizeValue.localizeTODO("Rebase Suspended")) - .content(LocalizeValue.localizeTODO( - "You have to resolve the conflicts and continue rebase.
    " + - "If you want to start from the beginning, you can abort rebase." + - GitRebaseUtils.mentionLocalChangesRemainingInStash(mySaver) + .content(LocalizeValue.join( + LocalizeValue.localizeTODO( + "You have to resolve the conflicts and continue rebase.
    " + + "If you want to start from the beginning, you can abort rebase." + ), + GitRebaseUtils.mentionLocalChangesRemainingInStash(mySaver) )) - .optionalHyperlinkListener(new RebaseNotificationListener(conflictingRepository, skippedCommits)) + .hyperlinkListener(new RebaseNotificationListener(conflictingRepository, skippedCommits)) .notify(myProject); } @@ -439,7 +441,7 @@ private void showStoppedForEditingMessage(@Nonnull GitRepository repository) { myNotificationService.newInfo(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) .title(LocalizeValue.localizeTODO("Rebase Stopped for Editing")) .content(LocalizeValue.localizeTODO("Once you are satisfied with your changes you may continue")) - .optionalHyperlinkListener(new RebaseNotificationListener( + .hyperlinkListener(new RebaseNotificationListener( repository, MultiMap.empty() )) @@ -456,13 +458,13 @@ private void showFatalError( String repo = myRepositoryManager.moreThanOneRoot() ? getShortRepositoryName(currentRepository) + ": " : ""; myNotificationService.newError(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) .title(LocalizeValue.localizeTODO(myRebaseSpec.getOngoingRebase() == null ? "Rebase Failed" : "Continue Rebase Failed")) - .content(LocalizeValue.localizeTODO( - repo + error + "
    " + - mentionRetryAndAbort(somethingWasRebased, successful) + - mentionSkippedCommits(skippedCommits) + - GitRebaseUtils.mentionLocalChangesRemainingInStash(mySaver) + .content(LocalizeValue.join( + LocalizeValue.localizeTODO(repo + error + "
    "), + mentionRetryAndAbort(somethingWasRebased, successful), + mentionSkippedCommits(skippedCommits), + GitRebaseUtils.mentionLocalChangesRemainingInStash(mySaver) )) - .optionalHyperlinkListener(new RebaseNotificationListener(currentRepository, skippedCommits)) + .hyperlinkListener(new RebaseNotificationListener(currentRepository, skippedCommits)) .notify(myProject); } @@ -473,28 +475,32 @@ private void showUntrackedFilesError( @Nonnull Collection successful, MultiMap skippedCommits ) { - String message = GitUntrackedFilesHelper.createUntrackedFilesOverwrittenDescription("rebase", true) + - mentionRetryAndAbort(somethingWasRebased, successful) + - mentionSkippedCommits(skippedCommits) + - GitRebaseUtils.mentionLocalChangesRemainingInStash(mySaver); + LocalizeValue message = LocalizeValue.join( + GitUntrackedFilesHelper.createUntrackedFilesOverwrittenDescription(LocalizeValue.localizeTODO("rebase"), true), + mentionRetryAndAbort(somethingWasRebased, successful), + mentionSkippedCommits(skippedCommits), + GitRebaseUtils.mentionLocalChangesRemainingInStash(mySaver) + ); GitUntrackedFilesHelper.notifyUntrackedFilesOverwrittenBy( myProject, currentRepository.getRoot(), untrackedPaths, - "rebase", + LocalizeValue.localizeTODO("rebase"), message ); } @Nonnull - private static String mentionRetryAndAbort(boolean somethingWasRebased, @Nonnull Collection successful) { - return somethingWasRebased || !successful.isEmpty() ? "You can retry or abort rebase." : "Retry."; + private static LocalizeValue mentionRetryAndAbort(boolean somethingWasRebased, @Nonnull Collection successful) { + return somethingWasRebased || !successful.isEmpty() + ? LocalizeValue.localizeTODO("You can retry or abort rebase.") + : LocalizeValue.localizeTODO("Retry."); } @Nonnull - private static String mentionSkippedCommits(@Nonnull MultiMap skippedCommits) { + private static LocalizeValue mentionSkippedCommits(@Nonnull MultiMap skippedCommits) { if (skippedCommits.isEmpty()) { - return ""; + return LocalizeValue.empty(); } String message = "
    "; if (skippedCommits.values().size() == 1) { @@ -503,14 +509,18 @@ private static String mentionSkippedCommits(@Nonnull MultiMap - { - String commitMessage = StringUtil.shortenPathWithEllipsis(commitInfo.subject, 72, true); - String hash = commitInfo.revision.asString(); - String shortHash = DvcsUtil.getShortHash(commitInfo.revision.asString()); - return String.format("%s %s", hash, shortHash, commitMessage); - }, "
    "); - return message; + message += StringUtil.join( + skippedCommits.values(), + commitInfo -> + { + String commitMessage = StringUtil.shortenPathWithEllipsis(commitInfo.subject, 72, true); + String hash = commitInfo.revision.asString(); + String shortHash = DvcsUtil.getShortHash(commitInfo.revision.asString()); + return String.format("%s %s", hash, shortHash, commitMessage); + }, + "
    " + ); + return LocalizeValue.localizeTODO(message); } @Nonnull diff --git a/plugin/src/main/java/git4idea/rebase/GitRebaseUtils.java b/plugin/src/main/java/git4idea/rebase/GitRebaseUtils.java index 9d26f09..4c99efa 100644 --- a/plugin/src/main/java/git4idea/rebase/GitRebaseUtils.java +++ b/plugin/src/main/java/git4idea/rebase/GitRebaseUtils.java @@ -300,9 +300,10 @@ public static CommitInfo getCurrentRebaseCommit(@Nonnull Project project, @Nonnu } @Nonnull - static String mentionLocalChangesRemainingInStash(@Nullable GitChangesSaver saver) { + static LocalizeValue mentionLocalChangesRemainingInStash(@Nullable GitChangesSaver saver) { return saver != null && saver.wereChangesSaved() - ? "
    Note that some local changes were " + toPast(saver.getOperationName()) + " before rebase." : ""; + ? LocalizeValue.localizeTODO("
    Note that some local changes were " + toPast(saver.getOperationName()) + " before rebase.") + : LocalizeValue.empty(); } @Nonnull diff --git a/plugin/src/main/java/git4idea/rebase/GitRebaser.java b/plugin/src/main/java/git4idea/rebase/GitRebaser.java index 1992459..5b1e58f 100644 --- a/plugin/src/main/java/git4idea/rebase/GitRebaser.java +++ b/plugin/src/main/java/git4idea/rebase/GitRebaser.java @@ -343,14 +343,14 @@ private void stageEverything(@Nonnull VirtualFile root) throws VcsException { } private static GitConflictResolver.Params makeParamsForRebaseConflict() { - return new GitConflictResolver.Params(). - setReverse(true). - setErrorNotificationTitle("Can't continue rebase"). - setMergeDescription("Merge conflicts detected. Resolve them before continuing rebase."). - setErrorNotificationAdditionalDescription( + return new GitConflictResolver.Params() + .setReverse(true) + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Can't continue rebase")) + .setMergeDescription(LocalizeValue.localizeTODO("Merge conflicts detected. Resolve them before continuing rebase.")) + .setErrorNotificationAdditionalDescription(LocalizeValue.localizeTODO( "Then you may continue rebase.
    " + "You also may abort rebase to restore the original branch and stop rebasing." - ); + )); } public static class TrivialEditor extends GitInteractiveRebaseEditorHandler { @@ -388,8 +388,8 @@ else if (untrackedWouldBeOverwrittenDetector.wasMessageDetected()) { myProject, root, untrackedWouldBeOverwrittenDetector.getRelativeFilePaths(), - "rebase", - null + LocalizeValue.localizeTODO("rebase"), + LocalizeValue.empty() ); return GitUpdateResult.ERROR; } @@ -426,15 +426,14 @@ public ConflictResolver(@Nonnull Project project, @Nonnull Git git, @Nonnull Vir } private static Params makeParams() { - Params params = new Params(); - params.setReverse(true); - params.setMergeDescription("Merge conflicts detected. Resolve them before continuing rebase."); - params.setErrorNotificationTitle("Can't continue rebase"); - params.setErrorNotificationAdditionalDescription( - "Then you may continue rebase.
    " + - "You also may abort rebase to restore the original branch and stop rebasing." - ); - return params; + return new Params() + .setReverse(true) + .setMergeDescription(LocalizeValue.localizeTODO("Merge conflicts detected. Resolve them before continuing rebase.")) + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Can't continue rebase")) + .setErrorNotificationAdditionalDescription(LocalizeValue.localizeTODO( + "Then you may continue rebase.
    " + + "You also may abort rebase to restore the original branch and stop rebasing." + )); } @Override diff --git a/plugin/src/main/java/git4idea/repo/GitConfig.java b/plugin/src/main/java/git4idea/repo/GitConfig.java index 5549c16..f617099 100644 --- a/plugin/src/main/java/git4idea/repo/GitConfig.java +++ b/plugin/src/main/java/git4idea/repo/GitConfig.java @@ -408,7 +408,7 @@ private Collection getPushUrls() { @Nonnull private Collection getPuttyKeys() { - return nonNullCollection(myRemoteBean.getPuttykeyfile()); + return nonNullCollection(myRemoteBean.getPuttyKeyFile()); } @Nonnull @@ -438,7 +438,7 @@ private interface RemoteBean { String[] getPushUrl(); @Nullable - String[] getPuttykeyfile(); + String[] getPuttyKeyFile(); } private static class Url { diff --git a/plugin/src/main/java/git4idea/stash/GitChangesSaver.java b/plugin/src/main/java/git4idea/stash/GitChangesSaver.java index 1ae2fe0..d599207 100644 --- a/plugin/src/main/java/git4idea/stash/GitChangesSaver.java +++ b/plugin/src/main/java/git4idea/stash/GitChangesSaver.java @@ -16,9 +16,11 @@ package git4idea.stash; import consulo.application.progress.ProgressIndicator; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.project.Project; import consulo.project.ui.notification.Notification; +import consulo.project.ui.notification.NotificationService; import consulo.project.ui.notification.event.NotificationListener; import consulo.versionControlSystem.VcsException; import consulo.versionControlSystem.VcsNotifier; @@ -40,145 +42,143 @@ * * @author Kirill Likhodedov */ -public abstract class GitChangesSaver -{ - - private static final Logger LOG = Logger.getInstance(GitChangesSaver.class); - - @Nonnull - protected final Project myProject; - @Nonnull - protected final ChangeListManager myChangeManager; - @Nonnull - protected final Git myGit; - @Nonnull - protected final ProgressIndicator myProgressIndicator; - @Nonnull - protected final String myStashMessage; - - protected GitConflictResolver.Params myParams; - - /** - * Returns an instance of the proper GitChangesSaver depending on the given save changes policy. - * - * @return {@link GitStashChangesSaver} or {@link GitShelveChangesSaver}. - */ - @Nonnull - public static GitChangesSaver getSaver(@Nonnull Project project, - @Nonnull Git git, - @Nonnull ProgressIndicator progressIndicator, - @Nonnull String stashMessage, - @Nonnull GitVcsSettings.UpdateChangesPolicy saveMethod) - { - if(saveMethod == GitVcsSettings.UpdateChangesPolicy.SHELVE) - { - return new GitShelveChangesSaver(project, git, progressIndicator, stashMessage); - } - return new GitStashChangesSaver(project, git, progressIndicator, stashMessage); - } - - protected GitChangesSaver(@Nonnull Project project, @Nonnull Git git, @Nonnull ProgressIndicator indicator, @Nonnull String stashMessage) - { - myProject = project; - myGit = git; - myProgressIndicator = indicator; - myStashMessage = stashMessage; - myChangeManager = ChangeListManager.getInstance(project); - } - - /** - * Saves local changes in stash or in shelf. - * - * @param rootsToSave Save changes only from these roots. - */ - public void saveLocalChanges(@Nullable Collection rootsToSave) throws VcsException - { - if(rootsToSave == null || rootsToSave.isEmpty()) - { - return; - } - save(rootsToSave); - } - - public void notifyLocalChangesAreNotRestored() - { - if(wereChangesSaved()) - { - LOG.info("Update is incomplete, changes are not restored"); - VcsNotifier.getInstance(myProject).notifyImportantWarning("Local changes were not restored", "Before update your uncommitted changes were saved to " + - getSaverName() + - ".
    " + - "Update is not complete, you have unresolved merges in your working tree
    " + - "Resolve conflicts, complete update and restore changes manually.", new ShowSavedChangesNotificationListener()); - } - } - - public void setConflictResolverParams(GitConflictResolver.Params params) - { - myParams = params; - } - - /** - * Saves local changes - specific for chosen save strategy. - * - * @param rootsToSave local changes should be saved on these roots. - */ - protected abstract void save(Collection rootsToSave) throws VcsException; - - /** - * Loads the changes - specific for chosen save strategy. - */ - public abstract void load(); - - /** - * @return true if there were local changes to save. - */ - public abstract boolean wereChangesSaved(); - - /** - * @return name of the save capability provider - stash or shelf. - */ - public abstract String getSaverName(); - - /** - * @return the name of the saving operation: stash or shelve. - */ - @Nonnull - public abstract String getOperationName(); - - /** - * Show the saved local changes in the proper viewer. - */ - public abstract void showSavedChanges(); - - /** - * The right panel title of the merge conflict dialog: changes that came from update. - */ - @Nonnull - protected static String getConflictRightPanelTitle() - { - return "Changes from remote"; - } - - /** - * The left panel title of the merge conflict dialog: changes that were preserved in this saver during update. - */ - @Nonnull - protected static String getConflictLeftPanelTitle() - { - return "Your uncommitted changes"; - } - - protected class ShowSavedChangesNotificationListener implements NotificationListener - { - @Override - public void hyperlinkUpdate(@Nonnull Notification notification, @Nonnull HyperlinkEvent event) - { - if(event.getEventType() == HyperlinkEvent.EventType.ACTIVATED && event.getDescription().equals("saver")) - { - showSavedChanges(); - } - } - } +public abstract class GitChangesSaver { + private static final Logger LOG = Logger.getInstance(GitChangesSaver.class); + + @Nonnull + protected final Project myProject; + @Nonnull + protected final ChangeListManager myChangeManager; + @Nonnull + protected final Git myGit; + @Nonnull + protected final ProgressIndicator myProgressIndicator; + @Nonnull + protected final String myStashMessage; + + protected GitConflictResolver.Params myParams; + + /** + * Returns an instance of the proper GitChangesSaver depending on the given save changes policy. + * + * @return {@link GitStashChangesSaver} or {@link GitShelveChangesSaver}. + */ + @Nonnull + public static GitChangesSaver getSaver( + @Nonnull Project project, + @Nonnull Git git, + @Nonnull ProgressIndicator progressIndicator, + @Nonnull String stashMessage, + @Nonnull GitVcsSettings.UpdateChangesPolicy saveMethod + ) { + if (saveMethod == GitVcsSettings.UpdateChangesPolicy.SHELVE) { + return new GitShelveChangesSaver(project, git, progressIndicator, stashMessage); + } + return new GitStashChangesSaver(project, git, progressIndicator, stashMessage); + } + + protected GitChangesSaver( + @Nonnull Project project, + @Nonnull Git git, + @Nonnull ProgressIndicator indicator, + @Nonnull String stashMessage + ) { + myProject = project; + myGit = git; + myProgressIndicator = indicator; + myStashMessage = stashMessage; + myChangeManager = ChangeListManager.getInstance(project); + } + + /** + * Saves local changes in stash or in shelf. + * + * @param rootsToSave Save changes only from these roots. + */ + public void saveLocalChanges(@Nullable Collection rootsToSave) throws VcsException { + if (rootsToSave == null || rootsToSave.isEmpty()) { + return; + } + save(rootsToSave); + } + + public void notifyLocalChangesAreNotRestored() { + if (wereChangesSaved()) { + LOG.info("Update is incomplete, changes are not restored"); + NotificationService.getInstance().newWarn(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) + .title(LocalizeValue.localizeTODO("Local changes were not restored")) + .content(LocalizeValue.localizeTODO( + "Before update your uncommitted changes were saved to " + + getSaverName() + + ".
    " + + "Update is not complete, you have unresolved merges in your working tree
    " + + "Resolve conflicts, complete update and restore changes manually." + )) + .hyperlinkListener(new ShowSavedChangesNotificationListener()) + .notify(myProject); + } + } + + public void setConflictResolverParams(GitConflictResolver.Params params) { + myParams = params; + } + + /** + * Saves local changes - specific for chosen save strategy. + * + * @param rootsToSave local changes should be saved on these roots. + */ + protected abstract void save(Collection rootsToSave) throws VcsException; + + /** + * Loads the changes - specific for chosen save strategy. + */ + public abstract void load(); + + /** + * @return true if there were local changes to save. + */ + public abstract boolean wereChangesSaved(); + + /** + * @return name of the save capability provider - stash or shelf. + */ + public abstract String getSaverName(); + + /** + * @return the name of the saving operation: stash or shelve. + */ + @Nonnull + public abstract String getOperationName(); + + /** + * Show the saved local changes in the proper viewer. + */ + public abstract void showSavedChanges(); + + /** + * The right panel title of the merge conflict dialog: changes that came from update. + */ + @Nonnull + protected static String getConflictRightPanelTitle() { + return "Changes from remote"; + } + + /** + * The left panel title of the merge conflict dialog: changes that were preserved in this saver during update. + */ + @Nonnull + protected static String getConflictLeftPanelTitle() { + return "Your uncommitted changes"; + } + + protected class ShowSavedChangesNotificationListener implements NotificationListener { + @Override + public void hyperlinkUpdate(@Nonnull Notification notification, @Nonnull HyperlinkEvent event) { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED && event.getDescription().equals("saver")) { + showSavedChanges(); + } + } + } } diff --git a/plugin/src/main/java/git4idea/stash/GitStashChangesSaver.java b/plugin/src/main/java/git4idea/stash/GitStashChangesSaver.java index 556bead..9ee514c 100644 --- a/plugin/src/main/java/git4idea/stash/GitStashChangesSaver.java +++ b/plugin/src/main/java/git4idea/stash/GitStashChangesSaver.java @@ -19,6 +19,7 @@ import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.project.Project; +import consulo.project.ui.notification.NotificationService; import consulo.util.lang.StringUtil; import consulo.versionControlSystem.VcsException; import consulo.versionControlSystem.VcsNotifier; @@ -173,7 +174,6 @@ public String toString() { } private static class UnstashConflictResolver extends GitConflictResolver { - private final Set myStashedRoots; public UnstashConflictResolver( @@ -190,22 +190,22 @@ private static Params makeParamsOrUse(@Nullable Params givenParams) { if (givenParams != null) { return givenParams; } - Params params = new Params(); - params.setErrorNotificationTitle("Local changes were not restored"); - params.setMergeDialogCustomizer(new UnstashMergeDialogCustomizer()); - params.setReverse(true); - return params; + return new Params() + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Local changes were not restored")) + .setMergeDialogCustomizer(new UnstashMergeDialogCustomizer()) + .setReverse(true); } - @Override protected void notifyUnresolvedRemain() { - VcsNotifier.getInstance(myProject).notifyImportantWarning( - "Local changes were restored with conflicts", - "Your uncommitted changes were saved to stash.
    " + - "Unstash is not complete, you have unresolved merges in your working tree
    " + - "Resolve conflicts and drop the stash.", - (notification, event) -> { + NotificationService.getInstance().newWarn(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) + .title(LocalizeValue.localizeTODO("Local changes were restored with conflicts")) + .content(LocalizeValue.localizeTODO( + "Your uncommitted changes were saved to stash.
    " + + "Unstash is not complete, you have unresolved merges in your working tree
    " + + "Resolve conflicts and drop the stash." + )) + .hyperlinkListener((notification, event) -> { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { if (event.getDescription().equals("saver")) { // we don't use #showSavedChanges to specify unmerged root first @@ -219,8 +219,8 @@ else if (event.getDescription().equals("resolve")) { mergeNoProceed(); } } - } - ); + }) + .notify(myProject); } } diff --git a/plugin/src/main/java/git4idea/ui/GitTagDialog.form b/plugin/src/main/java/git4idea/ui/GitTagDialog.form deleted file mode 100644 index 72caaf9..0000000 --- a/plugin/src/main/java/git4idea/ui/GitTagDialog.form +++ /dev/null @@ -1,143 +0,0 @@ - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    diff --git a/plugin/src/main/java/git4idea/ui/GitTagDialog.java b/plugin/src/main/java/git4idea/ui/GitTagDialog.java index 28e7346..8c00b76 100644 --- a/plugin/src/main/java/git4idea/ui/GitTagDialog.java +++ b/plugin/src/main/java/git4idea/ui/GitTagDialog.java @@ -15,11 +15,16 @@ */ package git4idea.ui; +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; import consulo.git.localize.GitLocalize; import consulo.localize.LocalizeValue; import consulo.project.Project; +import consulo.project.ui.notification.NotificationService; import consulo.ui.annotation.RequiredUIAccess; import consulo.ui.ex.awt.DialogWrapper; +import consulo.ui.ex.awt.JBScrollPane; +import consulo.ui.ex.awt.JBUI; import consulo.ui.ex.awt.Messages; import consulo.ui.ex.awt.event.DocumentAdapter; import consulo.util.io.FileUtil; @@ -36,6 +41,7 @@ import javax.swing.*; import javax.swing.event.DocumentEvent; +import java.awt.*; import java.io.*; import java.util.HashSet; import java.util.List; @@ -122,7 +128,7 @@ public GitTagDialog(Project project, List roots, VirtualFile defaul fetchTags(); myTagNameTextField.getDocument().addDocumentListener(new DocumentAdapter() { @Override - protected void textChanged(final DocumentEvent e) { + protected void textChanged(DocumentEvent e) { validateFields(); } }); @@ -149,10 +155,10 @@ public JComponent getPreferredFocusedComponent() { * @param exceptions the list where exceptions are collected */ @RequiredUIAccess - public void runAction(final List exceptions) { - final String message = myMessageTextArea.getText(); - final boolean hasMessage = message.trim().length() != 0; - final File messageFile; + public void runAction(List exceptions) { + String message = myMessageTextArea.getText(); + boolean hasMessage = message.trim().length() != 0; + File messageFile; if (hasMessage) { try { messageFile = FileUtil.createTempFile(MESSAGE_FILE_PREFIX, MESSAGE_FILE_SUFFIX); @@ -191,10 +197,10 @@ public void runAction(final List exceptions) { } try { GitHandlerUtil.doSynchronously(h, GitLocalize.taggingTitle(), LocalizeValue.ofNullable(h.printableCommandLine())); - VcsNotifier.getInstance(myProject).notifySuccess( - myTagNameTextField.getText(), - "Created tag " + myTagNameTextField.getText() + " successfully." - ); + NotificationService.getInstance().newInfo(VcsNotifier.NOTIFICATION_GROUP_ID) + .title(LocalizeValue.localizeTODO(myTagNameTextField.getText())) + .content(LocalizeValue.localizeTODO("Created tag " + myTagNameTextField.getText() + " successfully.")) + .notify(myProject); } finally { exceptions.addAll(h.errors()); @@ -264,7 +270,7 @@ private void fetchTags() { * @return the current git root */ private VirtualFile getGitRoot() { - return (VirtualFile)myGitRootComboBox.getSelectedItem(); + return (VirtualFile) myGitRootComboBox.getSelectedItem(); } /** @@ -290,4 +296,349 @@ protected String getDimensionServiceKey() { protected String getHelpId() { return "reference.VersionControl.Git.TagFiles"; } + + { +// GUI initializer generated by Consulo GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * Method generated by Consulo GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + */ + private void $$$setupUI$$$() { + myPanel = new JPanel(); + myPanel.setLayout(new GridLayoutManager(6, 2, JBUI.emptyInsets(), -1, -1)); + JLabel label1 = new JLabel(); + this.$$$loadLabelText$$$(label1, GitLocalize.commonGitRoot().get()); + myPanel.add( + label1, + new GridConstraints( + 0, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + myGitRootComboBox = new JComboBox(); + myGitRootComboBox.setToolTipText(GitLocalize.commonGitRootTooltip().get()); + myPanel.add( + myGitRootComboBox, + new GridConstraints( + 0, + 1, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + JLabel label2 = new JLabel(); + this.$$$loadLabelText$$$(label2, GitLocalize.commonCurrentBranch().get()); + myPanel.add( + label2, + new GridConstraints( + 1, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + myCurrentBranch = new JLabel(); + myCurrentBranch.setText(""); + myCurrentBranch.setToolTipText(GitLocalize.commonCurrentBranchTooltip().get()); + myPanel.add( + myCurrentBranch, + new GridConstraints( + 1, + 1, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_WANT_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + JLabel label3 = new JLabel(); + this.$$$loadLabelText$$$(label3, GitLocalize.tagNameLabel().get()); + myPanel.add( + label3, + new GridConstraints( + 2, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + myTagNameTextField = new JTextField(); + myTagNameTextField.setToolTipText(GitLocalize.tagNameTooltip().get()); + myPanel.add( + myTagNameTextField, + new GridConstraints( + 2, + 1, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_WANT_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + new Dimension(150, -1), + null, + 0, + false + ) + ); + JLabel label4 = new JLabel(); + this.$$$loadLabelText$$$(label4, GitLocalize.tagMessageLabel().get()); + label4.setVerticalAlignment(0); + myPanel.add( + label4, + new GridConstraints( + 5, + 0, + 1, + 1, + GridConstraints.ANCHOR_NORTHWEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + JBScrollPane jBScrollPane1 = new JBScrollPane(); + myPanel.add( + jBScrollPane1, + new GridConstraints( + 5, + 1, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_BOTH, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, + null, + null, + null, + 0, + false + ) + ); + myMessageTextArea = new JTextArea(); + myMessageTextArea.setRows(4); + myMessageTextArea.setToolTipText(GitLocalize.tagMessageTooltip().get()); + jBScrollPane1.setViewportView(myMessageTextArea); + JLabel label5 = new JLabel(); + this.$$$loadLabelText$$$(label5, GitLocalize.tagCommitLabel().get()); + myPanel.add( + label5, + new GridConstraints( + 4, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + myForceCheckBox = new JCheckBox(); + myForceCheckBox.setEnabled(false); + this.$$$loadButtonText$$$(myForceCheckBox, GitLocalize.tagForce().get()); + myForceCheckBox.setToolTipText(GitLocalize.tagForceTooltip().get()); + myPanel.add( + myForceCheckBox, + new GridConstraints( + 3, + 1, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + JPanel panel1 = new JPanel(); + panel1.setLayout(new GridLayoutManager(1, 2, JBUI.emptyInsets(), -1, -1)); + myPanel.add( + panel1, + new GridConstraints( + 4, + 1, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_BOTH, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + null, + null, + null, + 0, + false + ) + ); + myCommitTextField = new JTextField(); + myCommitTextField.setToolTipText(GitLocalize.tagCommitTooltip().get()); + panel1.add( + myCommitTextField, + new GridConstraints( + 0, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_WANT_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + new Dimension(150, -1), + null, + 0, + false + ) + ); + myValidateButton = new JButton(); + this.$$$loadButtonText$$$(myValidateButton, GitLocalize.tagValidate().get()); + myValidateButton.setToolTipText(GitLocalize.tagValidateTooltip().get()); + panel1.add( + myValidateButton, + new GridConstraints( + 0, + 1, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + label1.setLabelFor(myGitRootComboBox); + label3.setLabelFor(myTagNameTextField); + label4.setLabelFor(myMessageTextArea); + label5.setLabelFor(myCommitTextField); + } + + private void $$$loadLabelText$$$(JLabel component, String text) { + StringBuilder result = new StringBuilder(); + boolean haveMnemonic = false; + char mnemonic = '\0'; + int mnemonicIndex = -1; + for (int i = 0; i < text.length(); i++) { + if (text.charAt(i) == '&') { + i++; + if (i == text.length()) { + break; + } + if (!haveMnemonic && text.charAt(i) != '&') { + haveMnemonic = true; + mnemonic = text.charAt(i); + mnemonicIndex = result.length(); + } + } + result.append(text.charAt(i)); + } + component.setText(result.toString()); + if (haveMnemonic) { + component.setDisplayedMnemonic(mnemonic); + component.setDisplayedMnemonicIndex(mnemonicIndex); + } + } + + private void $$$loadButtonText$$$(AbstractButton component, String text) { + StringBuilder result = new StringBuilder(); + boolean haveMnemonic = false; + char mnemonic = '\0'; + int mnemonicIndex = -1; + for (int i = 0; i < text.length(); i++) { + if (text.charAt(i) == '&') { + i++; + if (i == text.length()) { + break; + } + if (!haveMnemonic && text.charAt(i) != '&') { + haveMnemonic = true; + mnemonic = text.charAt(i); + mnemonicIndex = result.length(); + } + } + result.append(text.charAt(i)); + } + component.setText(result.toString()); + if (haveMnemonic) { + component.setMnemonic(mnemonic); + component.setDisplayedMnemonicIndex(mnemonicIndex); + } + } + + public JComponent $$$getRootComponent$$$() { + return myPanel; + } } diff --git a/plugin/src/main/java/git4idea/ui/GitUnstashDialog.form b/plugin/src/main/java/git4idea/ui/GitUnstashDialog.form deleted file mode 100644 index 8781ef4..0000000 --- a/plugin/src/main/java/git4idea/ui/GitUnstashDialog.form +++ /dev/null @@ -1,165 +0,0 @@ - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    diff --git a/plugin/src/main/java/git4idea/ui/GitUnstashDialog.java b/plugin/src/main/java/git4idea/ui/GitUnstashDialog.java index 8a5c9db..25b616d 100644 --- a/plugin/src/main/java/git4idea/ui/GitUnstashDialog.java +++ b/plugin/src/main/java/git4idea/ui/GitUnstashDialog.java @@ -15,7 +15,9 @@ */ package git4idea.ui; -import consulo.application.Application; +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; +import com.intellij.uiDesigner.core.Spacer; import consulo.application.progress.ProgressIndicator; import consulo.application.progress.ProgressManager; import consulo.application.progress.Task; @@ -26,11 +28,10 @@ import consulo.platform.base.localize.CommonLocalize; import consulo.process.cmd.GeneralCommandLine; import consulo.project.Project; +import consulo.project.ui.notification.NotificationService; import consulo.ui.ModalityState; import consulo.ui.annotation.RequiredUIAccess; -import consulo.ui.ex.awt.DialogWrapper; -import consulo.ui.ex.awt.Messages; -import consulo.ui.ex.awt.UIUtil; +import consulo.ui.ex.awt.*; import consulo.ui.ex.awt.event.DocumentAdapter; import consulo.util.dataholder.Key; import consulo.versionControlSystem.VcsException; @@ -55,6 +56,7 @@ import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.HyperlinkEvent; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; @@ -112,9 +114,7 @@ public class GitUnstashDialog extends DialogWrapper { */ private final HashSet myBranches = new HashSet<>(); - /** - * The project - */ + @Nonnull private final Project myProject; private GitVcs myVcs; private static final Logger LOG = Logger.getInstance(GitUnstashDialog.class); @@ -126,7 +126,7 @@ public class GitUnstashDialog extends DialogWrapper { * @param roots the list of the roots * @param defaultRoot the default root to select */ - public GitUnstashDialog(final Project project, final List roots, final VirtualFile defaultRoot) { + public GitUnstashDialog(@Nonnull Project project, List roots, VirtualFile defaultRoot) { super(project, true); setModal(false); myProject = project; @@ -144,7 +144,7 @@ public GitUnstashDialog(final Project project, final List roots, fi myStashList.addListSelectionListener(e -> updateDialogState()); myBranchTextField.getDocument().addDocumentListener(new DocumentAdapter() { @Override - protected void textChanged(final DocumentEvent e) { + protected void textChanged(DocumentEvent e) { updateDialogState(); } }); @@ -165,7 +165,7 @@ protected void textChanged(final DocumentEvent e) { }); myDropButton.addActionListener(new ActionListener() { @Override - public void actionPerformed(final ActionEvent e) { + public void actionPerformed(ActionEvent e) { final StashInfo stash = getSelectedStash(); if (Messages.YES == Messages.showYesNoDialog( GitUnstashDialog.this.getContentPane(), @@ -173,23 +173,24 @@ public void actionPerformed(final ActionEvent e) { GitLocalize.gitUnstashDropConfirmationTitle(stash.getStash()).get(), UIUtil.getQuestionIcon() )) { - final ModalityState current = Application.get().getCurrentModalityState(); - ProgressManager.getInstance().run(new Task.Modal(myProject, "Removing stash " + stash.getStash(), false) { - @Override - public void run(@Nonnull ProgressIndicator indicator) { - final GitSimpleHandler h = dropHandler(stash.getStash()); - try { - h.run(); - h.unsilence(); + final ModalityState current = myProject.getApplication().getCurrentModalityState(); + ProgressManager.getInstance() + .run(new Task.Modal(myProject, LocalizeValue.localizeTODO("Removing stash " + stash.getStash()), false) { + @Override + public void run(@Nonnull ProgressIndicator indicator) { + GitSimpleHandler h = dropHandler(stash.getStash()); + try { + h.run(); + h.unsilence(); + } + catch (VcsException ex) { + project.getApplication().invokeLater( + () -> GitUIUtil.showOperationError((Project) myProject, ex, h.printableCommandLine()), + current + ); + } } - catch (final VcsException ex) { - project.getApplication().invokeLater( - () -> GitUIUtil.showOperationError((Project)myProject, ex, h.printableCommandLine()), - current - ); - } - } - }); + }); refreshStashList(); updateDialogState(); } @@ -203,7 +204,7 @@ private GitSimpleHandler dropHandler(String stash) { } }); myViewButton.addActionListener(e -> { - final VirtualFile root = getGitRoot(); + VirtualFile root = getGitRoot(); String resolvedStash; String selectedStash = getSelectedStash().getStash(); try { @@ -212,7 +213,7 @@ private GitSimpleHandler dropHandler(String stash) { h.addParameters("--timestamp", "--max-count=1"); addStashParameter(h, selectedStash); h.endOptions(); - final String output = h.run(); + String output = h.run(); resolvedStash = GitRevisionNumber.parseRevlistOutputAsRevisionNumber(h, output).asString(); } catch (VcsException ex) { @@ -294,7 +295,7 @@ private void updateDialogState() { * Refresh stash list */ private void refreshStashList() { - final DefaultListModel listModel = (DefaultListModel)myStashList.getModel(); + DefaultListModel listModel = (DefaultListModel) myStashList.getModel(); listModel.clear(); VirtualFile root = getGitRoot(); GitStashUtils.loadStashStack(myProject, root, listModel::addElement); @@ -313,7 +314,7 @@ private void refreshStashList() { * @return the selected git root */ private VirtualFile getGitRoot() { - return (VirtualFile)myGitRootComboBox.getSelectedItem(); + return (VirtualFile) myGitRootComboBox.getSelectedItem(); } /** @@ -341,7 +342,7 @@ private GitLineHandler handler() { * @throws NullPointerException if no stash is selected */ private StashInfo getSelectedStash() { - return (StashInfo)myStashList.getSelectedValue(); + return (StashInfo) myStashList.getSelectedValue(); } /** @@ -409,6 +410,428 @@ public static void showUnstashDialog(Project project, List gitRoots // d is not modal=> everything else in doOKAction. } + { +// GUI initializer generated by Consulo GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * Method generated by Consulo GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + */ + private void $$$setupUI$$$() { + myPanel = new JPanel(); + myPanel.setLayout(new GridLayoutManager(5, 3, JBUI.emptyInsets(), -1, -1)); + JLabel label1 = new JLabel(); + this.$$$loadLabelText$$$(label1, GitLocalize.commonGitRoot().get()); + myPanel.add( + label1, + new GridConstraints( + 0, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + myGitRootComboBox = new JComboBox(); + myGitRootComboBox.setToolTipText(GitLocalize.commonGitRootTooltip().get()); + myPanel.add( + myGitRootComboBox, + new GridConstraints( + 0, + 1, + 1, + 2, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + JLabel label2 = new JLabel(); + this.$$$loadLabelText$$$(label2, GitLocalize.commonCurrentBranch().get()); + myPanel.add( + label2, + new GridConstraints( + 1, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + myCurrentBranch = new JLabel(); + myCurrentBranch.setText(" "); + myCurrentBranch.setToolTipText(GitLocalize.commonCurrentBranchTooltip().get()); + myPanel.add( + myCurrentBranch, + new GridConstraints( + 1, + 1, + 1, + 2, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + JLabel label3 = new JLabel(); + this.$$$loadLabelText$$$(label3, GitLocalize.unstashStashes().get()); + myPanel.add( + label3, + new GridConstraints( + 2, + 0, + 1, + 1, + GridConstraints.ANCHOR_NORTHWEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + JPanel panel1 = new JPanel(); + panel1.setLayout(new GridLayoutManager(4, 1, JBUI.emptyInsets(), -1, -1)); + myPanel.add( + panel1, + new GridConstraints( + 2, + 2, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_BOTH, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + null, + null, + null, + 0, + false + ) + ); + myViewButton = new JButton(); + this.$$$loadButtonText$$$(myViewButton, GitLocalize.unstashView().get()); + myViewButton.setToolTipText(GitLocalize.unstashViewTooltip().get()); + panel1.add( + myViewButton, + new GridConstraints( + 0, + 0, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + Spacer spacer1 = new Spacer(); + panel1.add( + spacer1, + new GridConstraints( + 3, + 0, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_VERTICAL, + 1, + GridConstraints.SIZEPOLICY_WANT_GROW, + null, + null, + null, + 0, + false + ) + ); + myDropButton = new JButton(); + this.$$$loadButtonText$$$(myDropButton, GitLocalize.unstashDrop().get()); + myDropButton.setToolTipText(GitLocalize.unstashDropTooltip().get()); + panel1.add( + myDropButton, + new GridConstraints( + 1, + 0, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + myClearButton = new JButton(); + this.$$$loadButtonText$$$(myClearButton, GitLocalize.unstashClear().get()); + myClearButton.setToolTipText(GitLocalize.unstashClearTooltip().get()); + panel1.add( + myClearButton, + new GridConstraints( + 2, + 0, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + JLabel label4 = new JLabel(); + this.$$$loadLabelText$$$(label4, GitLocalize.unstashBranchLabel().get()); + myPanel.add( + label4, + new GridConstraints( + 4, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + myBranchTextField = new JTextField(); + myBranchTextField.setToolTipText(GitLocalize.unstashBranchTooltip().get()); + myPanel.add( + myBranchTextField, + new GridConstraints( + 4, + 1, + 1, + 2, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_WANT_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + new Dimension(150, -1), + null, + 0, + false + ) + ); + JBScrollPane jBScrollPane1 = new JBScrollPane(); + myPanel.add( + jBScrollPane1, + new GridConstraints( + 2, + 1, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_BOTH, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, + null, + null, + null, + 0, + false + ) + ); + myStashList = new JBList(); + myStashList.setSelectionMode(0); + jBScrollPane1.setViewportView(myStashList); + JPanel panel2 = new JPanel(); + panel2.setLayout(new GridLayoutManager(1, 3, JBUI.emptyInsets(), -1, -1)); + myPanel.add( + panel2, + new GridConstraints( + 3, + 1, + 1, + 2, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_BOTH, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + null, + null, + null, + 0, + false + ) + ); + myPopStashCheckBox = new JCheckBox(); + this.$$$loadButtonText$$$(myPopStashCheckBox, GitLocalize.unstashPopStash().get()); + myPopStashCheckBox.setToolTipText(GitLocalize.unstashPopStashTooltip().get()); + panel2.add( + myPopStashCheckBox, + new GridConstraints( + 0, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + myReinstateIndexCheckBox = new JCheckBox(); + this.$$$loadButtonText$$$(myReinstateIndexCheckBox, GitLocalize.unstashReinstateIndex().get()); + myReinstateIndexCheckBox.setToolTipText(GitLocalize.unstashReinstateIndexTooltip().get()); + panel2.add( + myReinstateIndexCheckBox, + new GridConstraints( + 0, + 1, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false + ) + ); + Spacer spacer2 = new Spacer(); + panel2.add( + spacer2, + new GridConstraints( + 0, + 2, + 1, + 1, + GridConstraints.ANCHOR_CENTER, + GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_WANT_GROW, + 1, + null, + null, + null, + 0, + false + ) + ); + label1.setLabelFor(myGitRootComboBox); + label3.setLabelFor(jBScrollPane1); + label4.setLabelFor(myBranchTextField); + } + + private void $$$loadLabelText$$$(JLabel component, String text) { + StringBuilder result = new StringBuilder(); + boolean haveMnemonic = false; + char mnemonic = '\0'; + int mnemonicIndex = -1; + for (int i = 0; i < text.length(); i++) { + if (text.charAt(i) == '&') { + i++; + if (i == text.length()) { + break; + } + if (!haveMnemonic && text.charAt(i) != '&') { + haveMnemonic = true; + mnemonic = text.charAt(i); + mnemonicIndex = result.length(); + } + } + result.append(text.charAt(i)); + } + component.setText(result.toString()); + if (haveMnemonic) { + component.setDisplayedMnemonic(mnemonic); + component.setDisplayedMnemonicIndex(mnemonicIndex); + } + } + + private void $$$loadButtonText$$$(AbstractButton component, String text) { + StringBuilder result = new StringBuilder(); + boolean haveMnemonic = false; + char mnemonic = '\0'; + int mnemonicIndex = -1; + for (int i = 0; i < text.length(); i++) { + if (text.charAt(i) == '&') { + i++; + if (i == text.length()) { + break; + } + if (!haveMnemonic && text.charAt(i) != '&') { + haveMnemonic = true; + mnemonic = text.charAt(i); + mnemonicIndex = result.length(); + } + } + result.append(text.charAt(i)); + } + component.setText(result.toString()); + if (haveMnemonic) { + component.setMnemonic(mnemonic); + component.setDisplayedMnemonicIndex(mnemonicIndex); + } + } + + public JComponent $$$getRootComponent$$$() { + return myPanel; + } + private static class UnstashConflictResolver extends GitConflictResolver { private final VirtualFile myRoot; private final StashInfo myStashInfo; @@ -420,27 +843,25 @@ public UnstashConflictResolver(Project project, VirtualFile root, StashInfo stas } private static Params makeParams(StashInfo stashInfo) { - Params params = new Params(); - params.setErrorNotificationTitle("Unstashed with conflicts"); - params.setMergeDialogCustomizer(new UnstashMergeDialogCustomizer(stashInfo)); - return params; + return new Params() + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Unstashed with conflicts")) + .setMergeDialogCustomizer(new UnstashMergeDialogCustomizer(stashInfo)); } @Override protected void notifyUnresolvedRemain() { - VcsNotifier.getInstance(myProject) - .notifyImportantWarning( - "Conflicts were not resolved during unstash", + NotificationService.getInstance().newWarn(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) + .title(LocalizeValue.localizeTODO("Conflicts were not resolved during unstash")) + .content(LocalizeValue.localizeTODO( "Unstash is not complete, you have unresolved merges in your working tree
    " + - "Resolve conflicts.", - (notification, event) -> { - if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - if (event.getDescription().equals("resolve")) { - new UnstashConflictResolver(myProject, myRoot, myStashInfo).mergeNoProceed(); - } - } + "Resolve conflicts." + )) + .hyperlinkListener((notification, event) -> { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED && "resolve".equals(event.getDescription())) { + new UnstashConflictResolver(myProject, myRoot, myStashInfo).mergeNoProceed(); } - ); + }) + .notify(myProject); } } diff --git a/plugin/src/main/java/git4idea/update/GitMergeUpdater.java b/plugin/src/main/java/git4idea/update/GitMergeUpdater.java index 687fbb8..65c20bc 100644 --- a/plugin/src/main/java/git4idea/update/GitMergeUpdater.java +++ b/plugin/src/main/java/git4idea/update/GitMergeUpdater.java @@ -86,8 +86,8 @@ protected GitUpdateResult doUpdate() { GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector = new GitUntrackedFilesOverwrittenByOperationDetector(myRoot); - String originalText = myProgressIndicator.getText(); - myProgressIndicator.setText("Merging" + GitUtil.mention(myRepository) + "..."); + LocalizeValue originalText = myProgressIndicator.getTextValue(); + myProgressIndicator.setTextValue(LocalizeValue.localizeTODO("Merging" + GitUtil.mention(myRepository) + "...")); try { GitCommandResult result = myGit.merge( myRepository, @@ -97,7 +97,7 @@ protected GitUpdateResult doUpdate() { untrackedFilesDetector, GitStandardProgressAnalyzer.createListener(myProgressIndicator) ); - myProgressIndicator.setText(originalText); + myProgressIndicator.setTextValue(originalText); return result.success() ? GitUpdateResult.SUCCESS : handleMergeFailure(mergeLineListener, untrackedFilesDetector, merger, result.getErrorOutputAsJoinedValue()); @@ -145,8 +145,8 @@ else if (untrackedFilesWouldBeOverwrittenByMergeDetector.wasMessageDetected()) { myProject, myRoot, untrackedFilesWouldBeOverwrittenByMergeDetector.getRelativeFilePaths(), - "merge", - null + LocalizeValue.localizeTODO("merge"), + LocalizeValue.empty() ); return GitUpdateResult.ERROR; } @@ -178,10 +178,10 @@ public boolean isSaveNeeded() { LOG.error("Repository is null for root " + myRoot); return true; // fail safe } - final Collection remotelyChanged = + Collection remotelyChanged = GitUtil.getPathsDiffBetweenRefs(Git.getInstance(), repository, currentBranch, remoteBranch); - final List locallyChanged = myChangeListManager.getAffectedPaths(); - for (final File localPath : locallyChanged) { + List locallyChanged = myChangeListManager.getAffectedPaths(); + for (File localPath : locallyChanged) { if (ContainerUtil.exists( remotelyChanged, remotelyChangedPath -> FileUtil.pathsEqual(localPath.getPath(), remotelyChangedPath) @@ -303,10 +303,9 @@ public MyConflictResolver(Project project, @Nonnull Git git, GitMerger merger, V } private static Params makeParams() { - Params params = new Params(); - params.setErrorNotificationTitle("Can't complete update"); - params.setMergeDescription("Merge conflicts detected. Resolve them before continuing update."); - return params; + return new Params() + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Can't complete update")) + .setMergeDescription(LocalizeValue.localizeTODO("Merge conflicts detected. Resolve them before continuing update.")); } @Override diff --git a/plugin/src/main/java/git4idea/update/GitUpdateProcess.java b/plugin/src/main/java/git4idea/update/GitUpdateProcess.java index e1eddc0..cdade7e 100644 --- a/plugin/src/main/java/git4idea/update/GitUpdateProcess.java +++ b/plugin/src/main/java/git4idea/update/GitUpdateProcess.java @@ -123,8 +123,8 @@ public GitUpdateProcess( @RequiredUIAccess public GitUpdateResult update(UpdateMethod updateMethod) { LOG.info("update started|" + updateMethod); - String oldText = myProgressIndicator.getText(); - myProgressIndicator.setText("Updating..."); + LocalizeValue oldText = myProgressIndicator.getTextValue(); + myProgressIndicator.setTextValue(LocalizeValue.localizeTODO("Updating...")); for (GitRepository repository : myRepositories) { repository.update(); @@ -146,7 +146,7 @@ public GitUpdateResult update(UpdateMethod updateMethod) { try (AccessToken ignored = DvcsUtil.workingTreeChangeStarted(myProject, "VCS Update")) { result = updateImpl(updateMethod); } - myProgressIndicator.setText(oldText); + myProgressIndicator.setTextValue(oldText); return result; } @@ -407,6 +407,7 @@ private static String recommendSetupTrackingCommand(@Nonnull GitRepository repos * * @return true if merge is in progress, which means that update can't continue. */ + @RequiredUIAccess private boolean isMergeInProgress() { LOG.info("isMergeInProgress: checking if there is an unfinished merge process..."); Collection mergingRoots = myMerger.getMergingRoots(); @@ -414,9 +415,9 @@ private boolean isMergeInProgress() { return false; } LOG.info("isMergeInProgress: roots with unfinished merge: " + mergingRoots); - GitConflictResolver.Params params = new GitConflictResolver.Params(); - params.setErrorNotificationTitle("Can't update"); - params.setMergeDescription("You have unfinished merge. These conflicts must be resolved before update."); + GitConflictResolver.Params params = new GitConflictResolver.Params() + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Can't update")) + .setMergeDescription(LocalizeValue.localizeTODO("You have unfinished merge. These conflicts must be resolved before update.")); return !new GitMergeCommittingConflictResolver(myProject, myGit, myMerger, mergingRoots, params, false).merge(); } @@ -428,9 +429,9 @@ private boolean isMergeInProgress() { @RequiredUIAccess private boolean areUnmergedFiles() { LOG.info("areUnmergedFiles: checking if there are unmerged files..."); - GitConflictResolver.Params params = new GitConflictResolver.Params(); - params.setErrorNotificationTitle("Update was not started"); - params.setMergeDescription("Unmerged files detected. These conflicts must be resolved before update."); + GitConflictResolver.Params params = new GitConflictResolver.Params() + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Update was not started")) + .setMergeDescription(LocalizeValue.localizeTODO("Unmerged files detected. These conflicts must be resolved before update.")); return !new GitMergeCommittingConflictResolver( myProject, myGit, @@ -456,12 +457,15 @@ private boolean checkRebaseInProgress() { } LOG.info("checkRebaseInProgress: roots with unfinished rebase: " + rebasingRoots); - GitConflictResolver.Params params = new GitConflictResolver.Params(); - params.setErrorNotificationTitle("Can't update"); - params.setMergeDescription("You have unfinished rebase process. These conflicts must be resolved before update."); - params.setErrorNotificationAdditionalDescription( - "Then you may continue rebase.
    You also may abort rebase to restore the original branch and stop rebasing." - ); + GitConflictResolver.Params params = new GitConflictResolver.Params() + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Can't update")) + .setMergeDescription( + LocalizeValue.localizeTODO("You have unfinished rebase process. These conflicts must be resolved before update.") + ) + .setErrorNotificationAdditionalDescription(LocalizeValue.localizeTODO( + "Then you may continue rebase.
    " + + "You also may abort rebase to restore the original branch and stop rebasing." + )); params.setReverse(true); return !new GitConflictResolver(myProject, myGit, rebasingRoots, params) { @Override diff --git a/plugin/src/main/java/git4idea/util/GitPreservingProcess.java b/plugin/src/main/java/git4idea/util/GitPreservingProcess.java index b19b979..83a261e 100644 --- a/plugin/src/main/java/git4idea/util/GitPreservingProcess.java +++ b/plugin/src/main/java/git4idea/util/GitPreservingProcess.java @@ -17,9 +17,10 @@ import consulo.application.progress.ProgressIndicator; import consulo.application.util.DateFormatUtil; -import consulo.application.util.function.Computable; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.project.Project; +import consulo.project.ui.notification.NotificationService; import consulo.util.lang.Clock; import consulo.util.lang.StringUtil; import consulo.versionControlSystem.VcsException; @@ -37,165 +38,157 @@ import java.util.Collection; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; /** * Executes a Git operation on a number of repositories surrounding it by stash-unstash procedure. * I.e. stashes changes, executes the operation and then unstashes it. */ -public class GitPreservingProcess -{ - private static final Logger LOG = Logger.getInstance(GitPreservingProcess.class); - - @Nonnull - private final Project myProject; - @Nonnull - private final Git myGit; - @Nonnull - private final Collection myRootsToSave; - @Nonnull - private final String myOperationTitle; - @Nonnull - private final String myDestinationName; - @Nonnull - private final ProgressIndicator myProgressIndicator; - @Nonnull - private final Runnable myOperation; - @Nonnull - private final String myStashMessage; - @Nonnull - private final GitChangesSaver mySaver; - - @Nonnull - private final AtomicBoolean myLoaded = new AtomicBoolean(); - - public GitPreservingProcess(@Nonnull Project project, - @Nonnull Git git, - @Nonnull Collection rootsToSave, - @Nonnull String operationTitle, - @Nonnull String destinationName, - @Nonnull GitVcsSettings.UpdateChangesPolicy saveMethod, - @Nonnull ProgressIndicator indicator, - @Nonnull Runnable operation) - { - myProject = project; - myGit = git; - myRootsToSave = rootsToSave; - myOperationTitle = operationTitle; - myDestinationName = destinationName; - myProgressIndicator = indicator; - myOperation = operation; - myStashMessage = String.format("Uncommitted changes before %s at %s", StringUtil.capitalize(myOperationTitle), DateFormatUtil.formatDateTime(Clock.getTime())); - mySaver = configureSaver(saveMethod); - } - - public void execute() - { - execute(null); - } - - public void execute(@Nullable final Computable autoLoadDecision) - { - Runnable operation = new Runnable() - { - @Override - public void run() - { - LOG.debug("starting"); - boolean savedSuccessfully = save(); - LOG.debug("save result: " + savedSuccessfully); - if(savedSuccessfully) - { - try - { - LOG.debug("running operation"); - myOperation.run(); - LOG.debug("operation completed."); - } - finally - { - if(autoLoadDecision == null || autoLoadDecision.compute()) - { - LOG.debug("loading"); - load(); - } - else - { - mySaver.notifyLocalChangesAreNotRestored(); - } - } - } - LOG.debug("finished."); - } - }; - - new GitFreezingProcess(myProject, myOperationTitle, operation).execute(); - } - - /** - * Configures the saver: i.e. notifications and texts for the GitConflictResolver used inside. - */ - @Nonnull - private GitChangesSaver configureSaver(@Nonnull GitVcsSettings.UpdateChangesPolicy saveMethod) - { - GitChangesSaver saver = GitChangesSaver.getSaver(myProject, myGit, myProgressIndicator, myStashMessage, saveMethod); - MergeDialogCustomizer mergeDialogCustomizer = new MergeDialogCustomizer() - { - @Override - public String getMultipleFileMergeDescription(@Nonnull Collection files) - { - return String.format("Uncommitted changes that were saved before %s have conflicts with files from %s", myOperationTitle, myDestinationName); - } - - @Override - public String getLeftPanelTitle(@Nonnull VirtualFile file) - { - return "Uncommitted changes from stash"; - } - - @Override - public String getRightPanelTitle(@Nonnull VirtualFile file, VcsRevisionNumber revisionNumber) - { - return String.format("Changes from %s", myDestinationName); - } - }; - - GitConflictResolver.Params params = new GitConflictResolver.Params(). - setReverse(true). - setMergeDialogCustomizer(mergeDialogCustomizer). - setErrorNotificationTitle("Local changes were not restored"); - - saver.setConflictResolverParams(params); - return saver; - } - - /** - * Saves local changes. In case of error shows a notification and returns false. - */ - private boolean save() - { - try - { - mySaver.saveLocalChanges(myRootsToSave); - return true; - } - catch(VcsException e) - { - LOG.info("Couldn't save local changes", e); - VcsNotifier.getInstance(myProject).notifyError("Couldn't save uncommitted changes.", String.format("Tried to save uncommitted changes in stash before %s, " + - "but failed with an error.
    %s", myOperationTitle, StringUtil.join(e.getMessages(), ""))); - return false; - } - } - - public void load() - { - if(myLoaded.compareAndSet(false, true)) - { - mySaver.load(); - } - else - { - LOG.warn("The changes were already loaded", new Throwable()); - } - } +public class GitPreservingProcess { + private static final Logger LOG = Logger.getInstance(GitPreservingProcess.class); + + @Nonnull + private final Project myProject; + @Nonnull + private final Git myGit; + @Nonnull + private final Collection myRootsToSave; + @Nonnull + private final String myOperationTitle; + @Nonnull + private final String myDestinationName; + @Nonnull + private final ProgressIndicator myProgressIndicator; + @Nonnull + private final Runnable myOperation; + @Nonnull + private final String myStashMessage; + @Nonnull + private final GitChangesSaver mySaver; + + @Nonnull + private final AtomicBoolean myLoaded = new AtomicBoolean(); + + public GitPreservingProcess( + @Nonnull Project project, + @Nonnull Git git, + @Nonnull Collection rootsToSave, + @Nonnull String operationTitle, + @Nonnull String destinationName, + @Nonnull GitVcsSettings.UpdateChangesPolicy saveMethod, + @Nonnull ProgressIndicator indicator, + @Nonnull Runnable operation + ) { + myProject = project; + myGit = git; + myRootsToSave = rootsToSave; + myOperationTitle = operationTitle; + myDestinationName = destinationName; + myProgressIndicator = indicator; + myOperation = operation; + myStashMessage = String.format( + "Uncommitted changes before %s at %s", + StringUtil.capitalize(myOperationTitle), + DateFormatUtil.formatDateTime(Clock.getTime()) + ); + mySaver = configureSaver(saveMethod); + } + + public void execute() { + execute(null); + } + + public void execute(@Nullable Supplier autoLoadDecision) { + Runnable operation = () -> { + LOG.debug("starting"); + boolean savedSuccessfully = save(); + LOG.debug("save result: " + savedSuccessfully); + if (savedSuccessfully) { + try { + LOG.debug("running operation"); + myOperation.run(); + LOG.debug("operation completed."); + } + finally { + if (autoLoadDecision == null || autoLoadDecision.get()) { + LOG.debug("loading"); + load(); + } + else { + mySaver.notifyLocalChangesAreNotRestored(); + } + } + } + LOG.debug("finished."); + }; + + new GitFreezingProcess(myProject, myOperationTitle, operation).execute(); + } + + /** + * Configures the saver: i.e. notifications and texts for the GitConflictResolver used inside. + */ + @Nonnull + private GitChangesSaver configureSaver(@Nonnull GitVcsSettings.UpdateChangesPolicy saveMethod) { + GitChangesSaver saver = GitChangesSaver.getSaver(myProject, myGit, myProgressIndicator, myStashMessage, saveMethod); + MergeDialogCustomizer mergeDialogCustomizer = new MergeDialogCustomizer() { + @Override + public String getMultipleFileMergeDescription(@Nonnull Collection files) { + return String.format( + "Uncommitted changes that were saved before %s have conflicts with files from %s", + myOperationTitle, + myDestinationName + ); + } + + @Override + public String getLeftPanelTitle(@Nonnull VirtualFile file) { + return "Uncommitted changes from stash"; + } + + @Override + public String getRightPanelTitle(@Nonnull VirtualFile file, VcsRevisionNumber revisionNumber) { + return String.format("Changes from %s", myDestinationName); + } + }; + + GitConflictResolver.Params params = new GitConflictResolver.Params() + .setReverse(true) + .setMergeDialogCustomizer(mergeDialogCustomizer) + .setErrorNotificationTitle(LocalizeValue.localizeTODO("Local changes were not restored")); + + saver.setConflictResolverParams(params); + return saver; + } + + /** + * Saves local changes. In case of error shows a notification and returns false. + */ + private boolean save() { + try { + mySaver.saveLocalChanges(myRootsToSave); + return true; + } + catch (VcsException e) { + LOG.info("Couldn't save local changes", e); + NotificationService.getInstance().newError(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) + .title(LocalizeValue.localizeTODO("Couldn't save uncommitted changes.")) + .content(LocalizeValue.localizeTODO(String.format( + "Tried to save uncommitted changes in stash before %s, but failed with an error.
    %s", + myOperationTitle, + StringUtil.join(e.getMessages(), "") + ))) + .notify(myProject); + return false; + } + } + + public void load() { + if (myLoaded.compareAndSet(false, true)) { + mySaver.load(); + } + else { + LOG.warn("The changes were already loaded", new Throwable()); + } + } } diff --git a/plugin/src/main/java/git4idea/util/GitUntrackedFilesHelper.java b/plugin/src/main/java/git4idea/util/GitUntrackedFilesHelper.java index de0b11c..090adc8 100644 --- a/plugin/src/main/java/git4idea/util/GitUntrackedFilesHelper.java +++ b/plugin/src/main/java/git4idea/util/GitUntrackedFilesHelper.java @@ -62,21 +62,23 @@ public static void notifyUntrackedFilesOverwrittenBy( @Nonnull Project project, @Nonnull VirtualFile root, @Nonnull Collection relativePaths, - @Nonnull String operation, - @Nullable String description + @Nonnull LocalizeValue operation, + @Nonnull LocalizeValue description ) { Collection absolutePaths = GitUtil.toAbsolute(root, relativePaths); List untrackedFiles = ContainerUtil.mapNotNull(absolutePaths, GitUtil::findRefreshFileOrLog); NotificationService.getInstance().newError(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) - .title(LocalizeValue.localizeTODO(StringUtil.capitalize(operation) + " failed")) - .content(LocalizeValue.localizeTODO( - description == null ? createUntrackedFilesOverwrittenDescription(operation, true) : description - )) - .optionalHyperlinkListener((notification, event) -> { + .title(LocalizeValue.localizeTODO(operation.capitalize() + " failed")) + .content( + description == LocalizeValue.empty() + ? createUntrackedFilesOverwrittenDescription(operation, true) + : description + ) + .hyperlinkListener((notification, event) -> { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - String dialogDesc = createUntrackedFilesOverwrittenDescription(operation, false); - String title = "Untracked Files Preventing " + StringUtil.capitalize(operation); + LocalizeValue dialogDesc = createUntrackedFilesOverwrittenDescription(operation, false); + LocalizeValue title = LocalizeValue.localizeTODO("Untracked Files Preventing " + operation.capitalize()); if (untrackedFiles.isEmpty()) { GitUtil.showPathsInDialog(project, absolutePaths, title, dialogDesc); } @@ -86,14 +88,14 @@ public static void notifyUntrackedFilesOverwrittenBy( LegacyDialog legacyDialog = componentFactory.createSelectFilesDialogOnlyOk( project, new ArrayList<>(untrackedFiles), - StringUtil.stripHtml(dialogDesc, true), + dialogDesc.map((localizeManager, string) -> StringUtil.stripHtml(string, true)).get(), null, false, false, true ); - legacyDialog.setTitle(LocalizeValue.localizeTODO(title)); + legacyDialog.setTitle(title); legacyDialog.show(); } } @@ -102,7 +104,7 @@ public static void notifyUntrackedFilesOverwrittenBy( } @Nonnull - public static String createUntrackedFilesOverwrittenDescription(@Nonnull String operation, boolean addLinkToViewFiles) { + public static LocalizeValue createUntrackedFilesOverwrittenDescription(@Nonnull LocalizeValue operation, boolean addLinkToViewFiles) { String description1 = " untracked working tree files would be overwritten by " + operation + "."; String description2 = "Please move or remove them before you can " + operation + "."; String notificationDesc; @@ -112,7 +114,7 @@ public static String createUntrackedFilesOverwrittenDescription(@Nonnull String else { notificationDesc = "These" + description1 + "
    " + description2; } - return notificationDesc; + return LocalizeValue.localizeTODO(notificationDesc); } /** @@ -128,8 +130,8 @@ public static String createUntrackedFilesOverwrittenDescription(@Nonnull String @RequiredUIAccess public static boolean showUntrackedFilesDialogWithRollback( @Nonnull Project project, - @Nonnull String operationName, - @Nonnull String rollbackProposal, + @Nonnull LocalizeValue operationName, + @Nonnull LocalizeValue rollbackProposal, @Nonnull VirtualFile root, @Nonnull Collection relativePaths ) { @@ -151,8 +153,9 @@ public static boolean showUntrackedFilesDialogWithRollback( componentFactory.createVirtualFileList(project, untrackedFiles, false, false).getComponent() ); } - String title = "Could not " + StringUtil.capitalize(operationName); - String description = StringUtil.stripHtml(createUntrackedFilesOverwrittenDescription(operationName, false), true); + LocalizeValue title = LocalizeValue.localizeTODO("Could not " + operationName.capitalize()); + LocalizeValue description = createUntrackedFilesOverwrittenDescription(operationName, false) + .map((localizeManager, string) -> StringUtil.stripHtml(string, true)); DialogWrapper dialog = new UntrackedFilesRollBackDialog(project, filesBrowser, description, rollbackProposal); dialog.setTitle(title); DialogManager.show(dialog); @@ -167,22 +170,22 @@ private static class UntrackedFilesRollBackDialog extends DialogWrapper { @Nonnull private final JComponent myFilesBrowser; @Nonnull - private final String myPrompt; + private final LocalizeValue myPrompt; @Nonnull - private final String myRollbackProposal; + private final LocalizeValue myRollbackProposal; public UntrackedFilesRollBackDialog( @Nonnull Project project, @Nonnull JComponent filesBrowser, - @Nonnull String prompt, - @Nonnull String rollbackProposal + @Nonnull LocalizeValue prompt, + @Nonnull LocalizeValue rollbackProposal ) { super(project); myFilesBrowser = filesBrowser; myPrompt = prompt; myRollbackProposal = rollbackProposal; - setOKButtonText("Rollback"); - setCancelButtonText("Don't rollback"); + setOKButtonText(LocalizeValue.localizeTODO("Rollback")); + setCancelButtonText(LocalizeValue.localizeTODO("Don't rollback")); init(); } @@ -191,7 +194,9 @@ public UntrackedFilesRollBackDialog( protected JComponent createSouthPanel() { JComponent buttons = super.createSouthPanel(); JPanel panel = new JPanel(new VerticalFlowLayout()); - panel.add(new JBLabel(XmlStringUtil.wrapInHtml(myRollbackProposal))); + panel.add( + new JBLabel(myRollbackProposal.map((localizeManager, string) -> XmlStringUtil.wrapInHtml(string)).get()) + ); if (buttons != null) { panel.add(buttons); } @@ -207,7 +212,7 @@ protected JComponent createCenterPanel() { @Nullable @Override protected JComponent createNorthPanel() { - JLabel label = new JLabel(myPrompt); + JLabel label = new JLabel(myPrompt.get()); label.setUI(new MultiLineLabelUI()); label.setBorder(new EmptyBorder(5, 1, 5, 1)); return label; diff --git a/plugin/src/main/java/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java b/plugin/src/main/java/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java index b293610..2aff421 100644 --- a/plugin/src/main/java/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java +++ b/plugin/src/main/java/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java @@ -38,23 +38,23 @@ public class LocalChangesWouldBeOverwrittenHelper { @Nonnull private static LocalizeValue getErrorNotificationDescription() { - return LocalizeValue.localizeTODO(getErrorDescription(true)); + return getErrorDescription(true); } @Nonnull - private static String getErrorDialogDescription() { + private static LocalizeValue getErrorDialogDescription() { return getErrorDescription(false); } @Nonnull - private static String getErrorDescription(boolean forNotification) { + private static LocalizeValue getErrorDescription(boolean forNotification) { String line1 = "Your local changes would be overwritten by merge."; String line2 = "Commit, stash or revert them to proceed."; if (forNotification) { - return line1 + "
    " + line2 + " View them"; + return LocalizeValue.localizeTODO(line1 + "
    " + line2 + " View them"); } else { - return line1 + "\n" + line2; + return LocalizeValue.localizeTODO(line1 + "\n" + line2); } } @@ -69,7 +69,7 @@ public static void showErrorNotification( NotificationService.getInstance().newError(VcsNotifier.IMPORTANT_ERROR_NOTIFICATION) .title(LocalizeValue.localizeTODO("Git " + StringUtil.capitalize(operationName) + " Failed")) .content(getErrorNotificationDescription()) - .optionalHyperlinkListener(new NotificationListener.Adapter() { + .hyperlinkListener(new NotificationListener.Adapter() { @Override @RequiredUIAccess protected void hyperlinkActivated(@Nonnull Notification notification, @Nonnull HyperlinkEvent e) { @@ -98,16 +98,15 @@ private static void showErrorDialog( @Nonnull List changes, @Nonnull Collection absolutePaths ) { - String title = "Local Changes Prevent from " + StringUtil.capitalize(operationName); - String description = getErrorDialogDescription(); + LocalizeValue title = LocalizeValue.localizeTODO("Local Changes Prevent from " + StringUtil.capitalize(operationName)); + LocalizeValue description = getErrorDialogDescription(); if (changes.isEmpty()) { GitUtil.showPathsInDialog(project, absolutePaths, title, description); } else { DialogBuilder builder = new DialogBuilder(project); - builder.setNorthPanel(new MultiLineLabel(description)); + builder.setNorthPanel(new MultiLineLabel(description.get())); ChangesBrowserFactory browserFactory = project.getApplication().getInstance(ChangesBrowserFactory.class); - builder.setCenterPanel(browserFactory.createChangeBrowserWithRollback(project, changes).getComponent()); builder.addOkAction(); builder.setTitle(title); diff --git a/plugin/src/test1/java/git4idea/branch/GitBranchWorkerTest.groovy b/plugin/src/test1/java/git4idea/branch/GitBranchWorkerTest.groovy index 19e8e7c..0bfd99b 100644 --- a/plugin/src/test1/java/git4idea/branch/GitBranchWorkerTest.groovy +++ b/plugin/src/test1/java/git4idea/branch/GitBranchWorkerTest.groovy @@ -23,6 +23,7 @@ import com.intellij.openapi.ui.DialogWrapper import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vcs.FilePathImpl +import consulo.localize.LocalizeValue import consulo.versionControlSystem.change.Change import com.intellij.openapi.vcs.changes.CurrentContentRevision import com.intellij.openapi.vfs.VirtualFile @@ -34,7 +35,6 @@ import git4idea.config.GitVersionSpecialty import git4idea.history.browser.GitCommit import git4idea.repo.GitRepository import git4idea.test.GitLightTest -import org.jetbrains.annotations.NotNull import org.junit.After import org.junit.Before import org.junit.Test @@ -770,66 +770,65 @@ class GitBranchWorkerTest extends GitLightTest { class AgreeToSmartOperationTestUiHandler implements GitBranchUiHandler { String mySuccessMessage - @NotNull + @Nonnull @Override ProgressIndicator getProgressIndicator() { new ProgressIndicatorBase() } @Override - int showSmartOperationDialog(@NotNull Project project, @NotNull List changes, @NotNull String operation, boolean force) { + int showSmartOperationDialog(@Nonnull Project project, @Nonnull List changes, @Nonnull String operation, boolean force) { GitSmartOperationDialog.SMART_EXIT_CODE } @Override - boolean showBranchIsNotFullyMergedDialog(@NotNull Project project, @NotNull Map> history, @NotNull String unmergedBranch, @NotNull List mergedToBranches, @NotNull String baseBranch) { + boolean showBranchIsNotFullyMergedDialog(@Nonnull Project project, @Nonnull Map> history, @Nonnull String unmergedBranch, @Nonnull List mergedToBranches, @Nonnull String baseBranch) { throw new UnsupportedOperationException() } @Override - void notifySuccess(@NotNull String message) { + void notifySuccess(@Nonnull String message) { mySuccessMessage = message } @Override - void notifySuccess(@NotNull String title, @NotNull String message, NotificationListener listener) { + void notifySuccess(@Nonnull String title, @Nonnull String message, NotificationListener listener) { mySuccessMessage = message } @Override - void notifyError(@NotNull String title, @NotNull String message) { + void notifyError(@Nonnull String title, @Nonnull String message) { throw new UnsupportedOperationException() } @Override - boolean notifyErrorWithRollbackProposal(@NotNull String title, @NotNull String message, @NotNull String rollbackProposal) { + boolean notifyErrorWithRollbackProposal(@Nonnull LocalizeValue title, @Nonnull LocalizeValue message, @Nonnull LocalizeValue rollbackProposal) { throw new UnsupportedOperationException() } @Override - void showUnmergedFilesNotification(@NotNull String operationName, @NotNull Collection repositories) { + void showUnmergedFilesNotification(@Nonnull LocalizeValue operationName, @Nonnull Collection repositories) { throw new UnsupportedOperationException() } @Override - boolean showUnmergedFilesMessageWithRollback(@NotNull String operationName, @NotNull String rollbackProposal) { + boolean showUnmergedFilesMessageWithRollback(@Nonnull LocalizeValue operationName, @Nonnull LocalizeValue rollbackProposal) { throw new UnsupportedOperationException() } @Override - void showUntrackedFilesNotification(@NotNull String operationName, @NotNull Collection untrackedFiles) { + void showUntrackedFilesNotification(@Nonnull String operationName, @Nonnull Collection untrackedFiles) { throw new UnsupportedOperationException() } @Override - boolean showUntrackedFilesDialogWithRollback(@NotNull String operationName, @NotNull String rollbackProposal, @NotNull Collection untrackedFiles) { + boolean showUntrackedFilesDialogWithRollback(@Nonnull String operationName, @Nonnull String rollbackProposal, @Nonnull Collection untrackedFiles) { throw new UnsupportedOperationException() } @Override - void notifySuccess(@NotNull String title, @NotNull String message) { + void notifySuccess(@Nonnull String title, @Nonnull String message) { mySuccessMessage = message } } - } diff --git a/rt/src/main/java/org/jetbrains/git4idea/rt/ssh/SSHConfig.java b/rt/src/main/java/org/jetbrains/git4idea/rt/ssh/SSHConfig.java index 6f80104..6feb2b3 100644 --- a/rt/src/main/java/org/jetbrains/git4idea/rt/ssh/SSHConfig.java +++ b/rt/src/main/java/org/jetbrains/git4idea/rt/ssh/SSHConfig.java @@ -17,6 +17,7 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.io.*; import java.net.InetAddress; import java.net.UnknownHostException; @@ -27,563 +28,571 @@ * This class allows accessing information in OpenSSH configuration file. */ public class SSHConfig { - /** - * Entry list for config - */ - final List myEntries = new ArrayList(); - /** - * User home directory - */ - public final static String USER_HOME; - - static { - String e = System.getenv("HOME"); - USER_HOME = e != null ? e : System.getProperty("user.home"); - } - - /** - * Allowed authentication methods - */ - private final static HashSet ALLOWED_METHODS = new HashSet(); - - static { - ALLOWED_METHODS.add(SSHMain.PUBLIC_KEY_METHOD); - ALLOWED_METHODS.add(SSHMain.KEYBOARD_INTERACTIVE_METHOD); - ALLOWED_METHODS.add(SSHMain.PASSWORD_METHOD); - } - - /** - * Look the host up in the host database - * - * @param user a user name - * @param host a host name - * @param port a port - * @return a create host entry - */ - @Nonnull - public Host lookup(@Nullable String user, @Nonnull String host, @Nullable Integer port) { - final Host rc = new Host(); - entriesLoop: - for (HostEntry e : myEntries) { - for (Pattern p : e.myNegative) { - if (p.matcher(host).matches()) { - continue entriesLoop; - } - } - if (e.myExactPositive.contains(host)) { - Host.merge(e.myHost, rc, rc); - continue; - } - for (Pattern p : e.myPositive) { - if (p.matcher(host).matches()) { - Host.merge(rc, e.myHost, rc); - } - } - } - // force user specified user and host names - if (user != null) { - rc.myUser = user; - } - if (port != null) { - rc.myPort = port; - } - if (rc.myHostName == null) { - rc.myHostName = host; - } - rc.setDefaults(); - return rc; - } - - - /** - * @return a SSH user configuration file - * @throws IOException if config could not be loaded - */ - @SuppressWarnings({"HardCodedStringLiteral"}) - @Nonnull - public static SSHConfig load() throws IOException { - SSHConfig rc = new SSHConfig(); - File configFile = new File(USER_HOME + File.separatorChar + ".ssh", "config"); - if (!configFile.exists()) { - // no config file = empty config file - return rc; - } - BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(configFile), "ISO-8859-1")); - try { - Host host = null; - String line; - while ((line = in.readLine()) != null) { - line = line.trim(); - if (line.length() == 0 || line.startsWith("#")) { - continue; - } - final String[] parts = line.split("[ \t]*[= \t]", 2); - final String keyword = parts[0]; - final String argument = unquoteIfNeeded(parts[1]); - if ("Host".equalsIgnoreCase(keyword)) { - HostEntry entry = new HostEntry(argument); - rc.myEntries.add(entry); - host = entry.myHost; - continue; - } - if (host == null) { - HostEntry entry = new HostEntry("*"); - rc.myEntries.add(entry); - host = entry.myHost; - } - if ("BatchMode".equalsIgnoreCase(keyword)) { - host.myBatchMode = parseBoolean(argument); - } - else if ("HostKeyAlgorithms".equalsIgnoreCase(keyword)) { - host.myHostKeyAlgorithms = Collections.unmodifiableList(parseList(argument)); - } - else if ("HostKeyAlias".equalsIgnoreCase(keyword)) { - host.myHostKeyAlias = argument; - } - else if ("HostName".equalsIgnoreCase(keyword)) { - host.myHostName = argument; - } - else if ("IdentityFile".equalsIgnoreCase(keyword)) { - host.myIdentityFile = argument; - } - else if ("NumberOfPasswordPrompts".equalsIgnoreCase(keyword)) { - host.myNumberOfPasswordPrompts = parseInt(argument); - } - else if ("PasswordAuthentication".equalsIgnoreCase(keyword)) { - host.myPasswordAuthentication = parseBoolean(argument); - } - else if ("Port".equalsIgnoreCase(keyword)) { - host.myPort = parseInt(argument); - } - else if ("PreferredAuthentications".equalsIgnoreCase(keyword)) { - final List list = parseList(argument); - list.retainAll(ALLOWED_METHODS); - if (!list.isEmpty()) { - host.myPreferredMethods = Collections.unmodifiableList(list); - } - } - else if ("PubkeyAuthentication".equalsIgnoreCase(keyword)) { - host.myPubkeyAuthentication = parseBoolean(argument); - } - else if ("User".equalsIgnoreCase(keyword)) { - host.myUser = argument; - } - } - } - finally { - in.close(); - } - return rc; - } - - /** - * Parse integer and return null if integer is incorrect - * - * @param value an argument to parse - * @return {@link Integer} or null. - */ - private static Integer parseInt(final String value) { - try { - return Integer.parseInt(value); - } - catch (NumberFormatException e) { - return null; - } - } - - /** - * Parse file name handling %d %u %l %h %r options - * - * @param host the host entry - * @param value a file name to parse - * @return actual file name - */ - private static File parseFileName(Host host, final String value) { - try { - StringBuilder rc = new StringBuilder(); - for (int i = 0; i < value.length(); i++) { - char ch = value.charAt(i); - if (i == 0 && ch == '~') { - rc.append(USER_HOME); - continue; - } - if (ch == '%' && i + 1 < value.length()) { - //noinspection AssignmentToForLoopParameter - i++; - switch (value.charAt(i)) { - case '%': - rc.append('%'); - break; - case 'd': - rc.append(USER_HOME); - break; - case 'h': - rc.append(host.getHostName()); - break; - case 'l': - rc.append(InetAddress.getLocalHost().getHostName()); - break; - case 'r': - rc.append(host.getUser()); - break; - default: - rc.append('%'); - rc.append(ch); - break; - } - } - rc.append(ch); - } - return new File(rc.toString()); - } - catch (UnknownHostException e) { - return null; - } - } - - /** - * Parse boolean value - * - * @param value an value to parse - * @return a boolean value - */ - private static Boolean parseBoolean(final String value) { - //noinspection HardCodedStringLiteral - return "yes".equals(value); - } - - private static List parseList(final String arg) { - List values = new ArrayList(); - for (String a : arg.split("[ \t,]+")) { - if (a.length() == 0) { - continue; - } - values.add(a); - } - return values; - } - - /** - * Unquote string if needed - * - * @param part a string to unquote - * @return unquoted value - */ - private static String unquoteIfNeeded(String part) { - if (part.length() > 1 && part.charAt(0) == '"' && part.charAt(part.length() - 1) == '"') { - part = part.substring(1, part.length() - 1); - } - return part.trim(); - } - - - /** - * Host entry in the file - */ - private static class HostEntry { - /** - * Negative patterns - */ - private final List myNegative = new ArrayList(); /** - * Positive patterns + * Entry list for config */ - private final List myPositive = new ArrayList(); + final List myEntries = new ArrayList<>(); /** - * Exact positive patterns that match host exactly rather than by mask + * User home directory */ - private final List myExactPositive = new ArrayList(); + public final static String USER_HOME; + + static { + String e = System.getenv("HOME"); + USER_HOME = e != null ? e : System.getProperty("user.home"); + } + /** - * The host entry + * Allowed authentication methods */ - private final Host myHost = new Host(); + private final static Set ALLOWED_METHODS = new HashSet<>(); + + static { + ALLOWED_METHODS.add(SSHMain.PUBLIC_KEY_METHOD); + ALLOWED_METHODS.add(SSHMain.KEYBOARD_INTERACTIVE_METHOD); + ALLOWED_METHODS.add(SSHMain.PASSWORD_METHOD); + } /** - * Create host entry from patterns + * Look the host up in the host database * - * @param patterns a patterns to match + * @param user a user name + * @param host a host name + * @param port a port + * @return a create host entry */ - public HostEntry(final String patterns) { - for (String pattern : patterns.split("[\t ,]+")) { - if (pattern.length() == 0) { - continue; + @Nonnull + public Host lookup(@Nullable String user, @Nonnull String host, @Nullable Integer port) { + Host rc = new Host(); + entriesLoop: + for (HostEntry e : myEntries) { + for (Pattern p : e.myNegative) { + if (p.matcher(host).matches()) { + continue entriesLoop; + } + } + if (e.myExactPositive.contains(host)) { + Host.merge(e.myHost, rc, rc); + continue; + } + for (Pattern p : e.myPositive) { + if (p.matcher(host).matches()) { + Host.merge(rc, e.myHost, rc); + } + } } - if (pattern.startsWith("!")) { - myNegative.add(compilePattern(pattern.substring(1).trim())); + // force user specified user and host names + if (user != null) { + rc.myUser = user; } - else if (pattern.indexOf('?') == 0 && pattern.indexOf('*') == 0) { - myExactPositive.add(pattern); + if (port != null) { + rc.myPort = port; } - else { - myPositive.add(compilePattern(pattern)); + if (rc.myHostName == null) { + rc.myHostName = host; } - } + rc.setDefaults(); + return rc; } /** - * Convert host pattern from glob format to regexp. Note that the function assumes - * a valid host pattern, so characters that might not happen in the host name but - * must be escaped in regular expressions are not escaped. - * - * @param s a pattern to convert - * @return the resulting pattern + * @return a SSH user configuration file + * @throws IOException if config could not be loaded */ - private static Pattern compilePattern(final String s) { - StringBuilder rc = new StringBuilder(); - for (int i = 0; i < s.length(); i++) { - char ch = s.charAt(i); - switch (ch) { - case '?': - rc.append('.'); - break; - case '*': - rc.append(".*"); - break; - case '.': - // for non-host strings more characters - rc.append('\\'); - default: - rc.append(ch); + @Nonnull + @SuppressWarnings({"HardCodedStringLiteral"}) + public static SSHConfig load() throws IOException { + SSHConfig rc = new SSHConfig(); + File configFile = new File(USER_HOME + File.separatorChar + ".ssh", "config"); + if (!configFile.exists()) { + // no config file = empty config file + return rc; + } + BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(configFile), "ISO-8859-1")); + try { + Host host = null; + String line; + while ((line = in.readLine()) != null) { + line = line.trim(); + if (line.length() == 0 || line.startsWith("#")) { + continue; + } + String[] parts = line.split("[ \t]*[= \t]", 2); + String keyword = parts[0]; + String argument = unquoteIfNeeded(parts[1]); + if ("Host".equalsIgnoreCase(keyword)) { + HostEntry entry = new HostEntry(argument); + rc.myEntries.add(entry); + host = entry.myHost; + continue; + } + if (host == null) { + HostEntry entry = new HostEntry("*"); + rc.myEntries.add(entry); + host = entry.myHost; + } + if ("BatchMode".equalsIgnoreCase(keyword)) { + host.myBatchMode = parseBoolean(argument); + } + else if ("HostKeyAlgorithms".equalsIgnoreCase(keyword)) { + host.myHostKeyAlgorithms = Collections.unmodifiableList(parseList(argument)); + } + else if ("HostKeyAlias".equalsIgnoreCase(keyword)) { + host.myHostKeyAlias = argument; + } + else if ("HostName".equalsIgnoreCase(keyword)) { + host.myHostName = argument; + } + else if ("IdentityFile".equalsIgnoreCase(keyword)) { + host.myIdentityFile = argument; + } + else if ("NumberOfPasswordPrompts".equalsIgnoreCase(keyword)) { + host.myNumberOfPasswordPrompts = parseInt(argument); + } + else if ("PasswordAuthentication".equalsIgnoreCase(keyword)) { + host.myPasswordAuthentication = parseBoolean(argument); + } + else if ("Port".equalsIgnoreCase(keyword)) { + host.myPort = parseInt(argument); + } + else if ("PreferredAuthentications".equalsIgnoreCase(keyword)) { + List list = parseList(argument); + list.retainAll(ALLOWED_METHODS); + if (!list.isEmpty()) { + host.myPreferredMethods = Collections.unmodifiableList(list); + } + } + else if ("PubkeyAuthentication".equalsIgnoreCase(keyword)) { + host.myPubkeyAuthentication = parseBoolean(argument); + } + else if ("User".equalsIgnoreCase(keyword)) { + host.myUser = argument; + } + } + } + finally { + in.close(); } - } - return Pattern.compile(rc.toString()); + return rc; } - } - /** - * Host information - */ - public static class Host { /** - * User name - */ - @Nullable private String myUser; - /** - * The name of the host - */ - @Nullable private String myHostName; - /** - * The port number - */ - @Nullable private Integer myPort; - /** - * Identity file - */ - @Nullable - private String myIdentityFile; - /** - * Preferred authentication methods - */ - @Nullable private List myPreferredMethods; - /** - * Preferred authentication methods - */ - @Nullable - private List myHostKeyAlgorithms; - /** - * Batch mode parameter - */ - @Nullable - private Boolean myBatchMode; - /** - * Alias for host key - */ - @Nullable - private String myHostKeyAlias; - /** - * Number of password prompts - */ - @Nullable - private Integer myNumberOfPasswordPrompts; - /** - * If true password authentication is enabled - */ - @Nullable - private Boolean myPasswordAuthentication; - /** - * If true, public key authentication is allowed - */ - @Nullable - private Boolean myPubkeyAuthentication; - - /** - * @return remote user name + * Parse integer and return null if integer is incorrect + * + * @param value an argument to parse + * @return {@link Integer} or null. */ - @Nonnull - public String getUser() { - return notNull(myUser); + private static Integer parseInt(String value) { + try { + return Integer.parseInt(value); + } + catch (NumberFormatException e) { + return null; + } } /** - * @return real host name - */ - @Nonnull - public String getHostName() { - return notNull(myHostName); + * Parse file name handling %d %u %l %h %r options + * + * @param host the host entry + * @param value a file name to parse + * @return actual file name + */ + private static File parseFileName(Host host, String value) { + try { + StringBuilder rc = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char ch = value.charAt(i); + if (i == 0 && ch == '~') { + rc.append(USER_HOME); + continue; + } + if (ch == '%' && i + 1 < value.length()) { + //noinspection AssignmentToForLoopParameter + i++; + switch (value.charAt(i)) { + case '%': + rc.append('%'); + break; + case 'd': + rc.append(USER_HOME); + break; + case 'h': + rc.append(host.getHostName()); + break; + case 'l': + rc.append(InetAddress.getLocalHost().getHostName()); + break; + case 'r': + rc.append(host.getUser()); + break; + default: + rc.append('%'); + rc.append(ch); + break; + } + } + rc.append(ch); + } + return new File(rc.toString()); + } + catch (UnknownHostException e) { + return null; + } } /** - * @return port number + * Parse boolean value + * + * @param value an value to parse + * @return a boolean value */ - @SuppressWarnings({"NullableProblems"}) - public int getPort() { - return notNull(myPort).intValue(); + private static Boolean parseBoolean(String value) { + //noinspection HardCodedStringLiteral + return "yes".equals(value); } - /** - * @return identity file or null if the default for algorithm should be used - */ - @Nullable - public File getIdentityFile() { - if (myIdentityFile == null) { - return null; - } - return parseFileName(this, myIdentityFile); + private static List parseList(String arg) { + List values = new ArrayList<>(); + for (String a : arg.split("[ \t,]+")) { + if (a.length() == 0) { + continue; + } + values.add(a); + } + return values; } /** - * @return preferred authentication methods + * Unquote string if needed + * + * @param part a string to unquote + * @return unquoted value */ - @Nonnull - public List getPreferredMethods() { - return notNull(myPreferredMethods); + private static String unquoteIfNeeded(String part) { + if (part.length() > 1 && part.charAt(0) == '"' && part.charAt(part.length() - 1) == '"') { + part = part.substring(1, part.length() - 1); + } + return part.trim(); } - /** - * @return true if the host should be use in the batch mode - */ - @SuppressWarnings({"NullableProblems"}) - public boolean isBatchMode() { - return notNull(myBatchMode).booleanValue(); - } /** - * @return algorithms that should be used for the host - */ - @Nonnull - public List getHostKeyAlgorithms() { - return notNull(myHostKeyAlgorithms); - } + * Host entry in the file + */ + private static class HostEntry { + /** + * Negative patterns + */ + private final List myNegative = new ArrayList<>(); + /** + * Positive patterns + */ + private final List myPositive = new ArrayList<>(); + /** + * Exact positive patterns that match host exactly rather than by mask + */ + private final List myExactPositive = new ArrayList<>(); + /** + * The host entry + */ + private final Host myHost = new Host(); + + /** + * Create host entry from patterns + * + * @param patterns a patterns to match + */ + public HostEntry(String patterns) { + for (String pattern : patterns.split("[\t ,]+")) { + if (pattern.length() == 0) { + continue; + } + if (pattern.startsWith("!")) { + myNegative.add(compilePattern(pattern.substring(1).trim())); + } + else if (pattern.indexOf('?') == 0 && pattern.indexOf('*') == 0) { + myExactPositive.add(pattern); + } + else { + myPositive.add(compilePattern(pattern)); + } + } + } - /** - * @return the number of the password prompts - */ - @SuppressWarnings({"NullableProblems"}) - public int getNumberOfPasswordPrompts() { - return notNull(myNumberOfPasswordPrompts).intValue(); + /** + * Convert host pattern from glob format to regexp. Note that the function assumes + * a valid host pattern, so characters that might not happen in the host name but + * must be escaped in regular expressions are not escaped. + * + * @param s a pattern to convert + * @return the resulting pattern + */ + private static Pattern compilePattern(String s) { + StringBuilder rc = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + switch (ch) { + case '?': + rc.append('.'); + break; + case '*': + rc.append(".*"); + break; + case '.': + // for non-host strings more characters + rc.append('\\'); + default: + rc.append(ch); + } + } + return Pattern.compile(rc.toString()); + } } /** - * @return alias for host to be used in known hosts file - */ - @Nonnull - public String getHostKeyAlias() { - return notNull(myHostKeyAlias); - } + * Host information + */ + public static class Host { + /** + * User name + */ + @Nullable + private String myUser; + /** + * The name of the host + */ + @Nullable + private String myHostName; + /** + * The port number + */ + @Nullable + private Integer myPort; + /** + * Identity file + */ + @Nullable + private String myIdentityFile; + /** + * Preferred authentication methods + */ + @Nullable + private List myPreferredMethods; + /** + * Preferred authentication methods + */ + @Nullable + private List myHostKeyAlgorithms; + /** + * Batch mode parameter + */ + @Nullable + private Boolean myBatchMode; + /** + * Alias for host key + */ + @Nullable + private String myHostKeyAlias; + /** + * Number of password prompts + */ + @Nullable + private Integer myNumberOfPasswordPrompts; + /** + * If true password authentication is enabled + */ + @Nullable + private Boolean myPasswordAuthentication; + /** + * If true, public key authentication is allowed + */ + @Nullable + private Boolean myPubkeyAuthentication; + + /** + * @return remote user name + */ + @Nonnull + public String getUser() { + return notNull(myUser); + } - /** - * @return true if password authentication is supported for the host - */ - public boolean supportsPasswordAuthentication() { - return notNull(myPasswordAuthentication).booleanValue(); - } + /** + * @return real host name + */ + @Nonnull + public String getHostName() { + return notNull(myHostName); + } - /** - * @return true if public key authentication is supported for the host - */ - public boolean supportsPubkeyAuthentication() { - return notNull(myPubkeyAuthentication).booleanValue(); - } + /** + * @return port number + */ + @SuppressWarnings({"NullableProblems"}) + public int getPort() { + return notNull(myPort); + } - /** - * Set defaults for unspecified fields - */ - @SuppressWarnings({"HardCodedStringLiteral"}) - private void setDefaults() { - if (myUser == null) { - myUser = System.getProperty("user.name"); - } - if (myPort == null) { - myPort = 22; - } - if (myPreferredMethods == null) { - myPreferredMethods = Collections - .unmodifiableList(Arrays.asList(SSHMain.PUBLIC_KEY_METHOD, SSHMain.KEYBOARD_INTERACTIVE_METHOD, SSHMain.PASSWORD_METHOD)); - } - if (myBatchMode == null) { - myBatchMode = Boolean.FALSE; - } - if (myHostKeyAlgorithms == null) { - myHostKeyAlgorithms = Collections.unmodifiableList(Arrays.asList(SSHMain.SSH_RSA_ALGORITHM, SSHMain.SSH_DSS_ALGORITHM)); - } - if (myNumberOfPasswordPrompts == null) { - myNumberOfPasswordPrompts = 3; - } - if (myHostKeyAlias == null) { - myHostKeyAlias = myHostName; - } - if (myPasswordAuthentication == null) { - myPasswordAuthentication = Boolean.TRUE; - } - if (myPubkeyAuthentication == null) { - myPubkeyAuthentication = Boolean.TRUE; - } - } + /** + * @return identity file or null if the default for algorithm should be used + */ + @Nullable + public File getIdentityFile() { + if (myIdentityFile == null) { + return null; + } + return parseFileName(this, myIdentityFile); + } - /** - * Ensure that the value is not null - * - * @param value the value to check - * @param the parameter type - * @return the provided argument if not null - * @throws IllegalStateException if the value is null - */ - @Nonnull - private static T notNull(@Nullable T value) { - if (value == null) { - throw new IllegalStateException("The value must not be null"); - } - return value; - } + /** + * @return preferred authentication methods + */ + @Nonnull + public List getPreferredMethods() { + return notNull(myPreferredMethods); + } - /** - * Merge information from two host entries - * - * @param first this entry is the preferred - * @param second this entry provides information only if {@code first) did not have one - * @param result this entry is updated as result of merge (usually {@code first) or {@code second)) - */ - private static void merge(Host first, Host second, Host result) { - result.myUser = mergeValue(first.myUser, second.myUser); - result.myHostName = mergeValue(first.myHostName, second.myHostName); - result.myPort = mergeValue(first.myPort, second.myPort); - result.myIdentityFile = mergeValue(first.myIdentityFile, second.myIdentityFile); - result.myPreferredMethods = mergeValue(first.myPreferredMethods, second.myPreferredMethods); - result.myHostKeyAlgorithms = mergeValue(first.myHostKeyAlgorithms, second.myHostKeyAlgorithms); - result.myBatchMode = mergeValue(first.myBatchMode, second.myBatchMode); - result.myHostKeyAlias = mergeValue(first.myHostKeyAlias, second.myHostKeyAlias); - result.myNumberOfPasswordPrompts = mergeValue(first.myNumberOfPasswordPrompts, second.myNumberOfPasswordPrompts); - result.myPasswordAuthentication = mergeValue(first.myPasswordAuthentication, second.myPasswordAuthentication); - result.myPubkeyAuthentication = mergeValue(first.myPubkeyAuthentication, second.myPubkeyAuthentication); - } + /** + * @return true if the host should be use in the batch mode + */ + @SuppressWarnings({"NullableProblems"}) + public boolean isBatchMode() { + return notNull(myBatchMode); + } - /** - * Merge single value - * - * @param first the first value - * @param second the second value - * @param a value type - * @return a result of merge - */ - private static T mergeValue(T first, T second) { - return first == null ? second : first; - } + /** + * @return algorithms that should be used for the host + */ + @Nonnull + public List getHostKeyAlgorithms() { + return notNull(myHostKeyAlgorithms); + } - @Override - public String toString() { - return String.format("Host{myUser='%s', myHostName='%s', myPort=%d, myIdentityFile='%s'}", - myUser, myHostName, myPort, myIdentityFile); - } + /** + * @return the number of the password prompts + */ + @SuppressWarnings({"NullableProblems"}) + public int getNumberOfPasswordPrompts() { + return notNull(myNumberOfPasswordPrompts); + } + + /** + * @return alias for host to be used in known hosts file + */ + @Nonnull + public String getHostKeyAlias() { + return notNull(myHostKeyAlias); + } + + /** + * @return true if password authentication is supported for the host + */ + public boolean supportsPasswordAuthentication() { + return notNull(myPasswordAuthentication); + } + + /** + * @return true if public key authentication is supported for the host + */ + public boolean supportsPubkeyAuthentication() { + return notNull(myPubkeyAuthentication); + } + + /** + * Set defaults for unspecified fields + */ + @SuppressWarnings({"HardCodedStringLiteral"}) + private void setDefaults() { + if (myUser == null) { + myUser = System.getProperty("user.name"); + } + if (myPort == null) { + myPort = 22; + } + if (myPreferredMethods == null) { + myPreferredMethods = Collections + .unmodifiableList(Arrays.asList( + SSHMain.PUBLIC_KEY_METHOD, + SSHMain.KEYBOARD_INTERACTIVE_METHOD, + SSHMain.PASSWORD_METHOD + )); + } + if (myBatchMode == null) { + myBatchMode = Boolean.FALSE; + } + if (myHostKeyAlgorithms == null) { + myHostKeyAlgorithms = Collections.unmodifiableList(Arrays.asList(SSHMain.SSH_RSA_ALGORITHM, SSHMain.SSH_DSS_ALGORITHM)); + } + if (myNumberOfPasswordPrompts == null) { + myNumberOfPasswordPrompts = 3; + } + if (myHostKeyAlias == null) { + myHostKeyAlias = myHostName; + } + if (myPasswordAuthentication == null) { + myPasswordAuthentication = Boolean.TRUE; + } + if (myPubkeyAuthentication == null) { + myPubkeyAuthentication = Boolean.TRUE; + } + } - } + /** + * Ensure that the value is not null + * + * @param value the value to check + * @param the parameter type + * @return the provided argument if not null + * @throws IllegalStateException if the value is null + */ + @Nonnull + private static T notNull(@Nullable T value) { + if (value == null) { + throw new IllegalStateException("The value must not be null"); + } + return value; + } + + /** + * Merge information from two host entries + * + * @param first this entry is the preferred + * @param second this entry provides information only if {@code first) did not have one + * @param result this entry is updated as result of merge (usually {@code first) or {@code second)) + */ + private static void merge(Host first, Host second, Host result) { + result.myUser = mergeValue(first.myUser, second.myUser); + result.myHostName = mergeValue(first.myHostName, second.myHostName); + result.myPort = mergeValue(first.myPort, second.myPort); + result.myIdentityFile = mergeValue(first.myIdentityFile, second.myIdentityFile); + result.myPreferredMethods = mergeValue(first.myPreferredMethods, second.myPreferredMethods); + result.myHostKeyAlgorithms = mergeValue(first.myHostKeyAlgorithms, second.myHostKeyAlgorithms); + result.myBatchMode = mergeValue(first.myBatchMode, second.myBatchMode); + result.myHostKeyAlias = mergeValue(first.myHostKeyAlias, second.myHostKeyAlias); + result.myNumberOfPasswordPrompts = mergeValue(first.myNumberOfPasswordPrompts, second.myNumberOfPasswordPrompts); + result.myPasswordAuthentication = mergeValue(first.myPasswordAuthentication, second.myPasswordAuthentication); + result.myPubkeyAuthentication = mergeValue(first.myPubkeyAuthentication, second.myPubkeyAuthentication); + } + + /** + * Merge single value + * + * @param first the first value + * @param second the second value + * @param a value type + * @return a result of merge + */ + private static T mergeValue(T first, T second) { + return first == null ? second : first; + } + + @Override + public String toString() { + return String.format("Host{myUser='%s', myHostName='%s', myPort=%d, myIdentityFile='%s'}", + myUser, myHostName, myPort, myIdentityFile + ); + } + + } } From 98e3c1cd68fc2ed820c92e3013b548d7737b877e Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 19 Nov 2025 22:46:24 +0300 Subject: [PATCH 2/2] Localizing GitChangesSaver. --- .../java/git4idea/stash/GitChangesSaver.java | 10 +- .../git4idea/stash/GitShelveChangesSaver.java | 10 +- .../java/git4idea/stash/GitShelveUtils.java | 200 +++++++++--------- .../git4idea/stash/GitStashChangesSaver.java | 14 +- 4 files changed, 123 insertions(+), 111 deletions(-) diff --git a/plugin/src/main/java/git4idea/stash/GitChangesSaver.java b/plugin/src/main/java/git4idea/stash/GitChangesSaver.java index d599207..97969af 100644 --- a/plugin/src/main/java/git4idea/stash/GitChangesSaver.java +++ b/plugin/src/main/java/git4idea/stash/GitChangesSaver.java @@ -22,6 +22,7 @@ import consulo.project.ui.notification.Notification; import consulo.project.ui.notification.NotificationService; import consulo.project.ui.notification.event.NotificationListener; +import consulo.ui.annotation.RequiredUIAccess; import consulo.versionControlSystem.VcsException; import consulo.versionControlSystem.VcsNotifier; import consulo.versionControlSystem.change.ChangeListManager; @@ -160,20 +161,21 @@ public void setConflictResolverParams(GitConflictResolver.Params params) { * The right panel title of the merge conflict dialog: changes that came from update. */ @Nonnull - protected static String getConflictRightPanelTitle() { - return "Changes from remote"; + protected static LocalizeValue getConflictRightPanelTitle() { + return LocalizeValue.localizeTODO("Changes from remote"); } /** * The left panel title of the merge conflict dialog: changes that were preserved in this saver during update. */ @Nonnull - protected static String getConflictLeftPanelTitle() { - return "Your uncommitted changes"; + protected static LocalizeValue getConflictLeftPanelTitle() { + return LocalizeValue.localizeTODO("Your uncommitted changes"); } protected class ShowSavedChangesNotificationListener implements NotificationListener { @Override + @RequiredUIAccess public void hyperlinkUpdate(@Nonnull Notification notification, @Nonnull HyperlinkEvent event) { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED && event.getDescription().equals("saver")) { showSavedChanges(); diff --git a/plugin/src/main/java/git4idea/stash/GitShelveChangesSaver.java b/plugin/src/main/java/git4idea/stash/GitShelveChangesSaver.java index 0fe07d4..b806db1 100644 --- a/plugin/src/main/java/git4idea/stash/GitShelveChangesSaver.java +++ b/plugin/src/main/java/git4idea/stash/GitShelveChangesSaver.java @@ -20,6 +20,7 @@ import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.project.Project; +import consulo.ui.annotation.RequiredUIAccess; import consulo.versionControlSystem.ProjectLevelVcsManager; import consulo.versionControlSystem.VcsException; import consulo.versionControlSystem.base.LocalChangesUnderRoots; @@ -53,7 +54,7 @@ public GitShelveChangesSaver(@Nonnull Project project, @Nonnull Git git, @Nonnul @Override protected void save(@Nonnull Collection rootsToSave) throws VcsException { LOG.info("save " + rootsToSave); - final Map>> lists = + Map>> lists = new LocalChangesUnderRoots(myChangeManager, myVcsManager).getChangesByLists(rootsToSave); LocalizeValue oldProgressTitle = myProgressIndicator.getTextValue(); @@ -62,13 +63,13 @@ protected void save(@Nonnull Collection rootsToSave) throws VcsExce myShelvedLists = new HashMap<>(); for (Map.Entry>> entry : lists.entrySet()) { - final Map> map = entry.getValue(); - final Set changes = new HashSet<>(); + Map> map = entry.getValue(); + Set changes = new HashSet<>(); for (Collection changeCollection : map.values()) { changes.addAll(changeCollection); } if (!changes.isEmpty()) { - final ShelvedChangeList list = GitShelveUtils.shelveChanges( + ShelvedChangeList list = GitShelveUtils.shelveChanges( myProject, myShelveManager, changes, @@ -94,6 +95,7 @@ protected void save(@Nonnull Collection rootsToSave) throws VcsExce } @Override + @RequiredUIAccess public void load() { if (myShelvedLists != null) { LOG.info("load "); diff --git a/plugin/src/main/java/git4idea/stash/GitShelveUtils.java b/plugin/src/main/java/git4idea/stash/GitShelveUtils.java index 92842d8..4de46ed 100644 --- a/plugin/src/main/java/git4idea/stash/GitShelveUtils.java +++ b/plugin/src/main/java/git4idea/stash/GitShelveUtils.java @@ -16,9 +16,9 @@ package git4idea.stash; import consulo.application.Application; -import consulo.application.ApplicationManager; import consulo.document.DocumentReference; import consulo.document.DocumentReferenceManager; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.project.Project; import consulo.ui.annotation.RequiredUIAccess; @@ -41,111 +41,115 @@ import java.util.List; public class GitShelveUtils { - private static final Logger LOG = Logger.getInstance(GitShelveUtils.class); + private static final Logger LOG = Logger.getInstance(GitShelveUtils.class); - public static void doSystemUnshelve(final Project project, - final ShelvedChangeList shelvedChangeList, - final ShelveChangesManager shelveManager, - @Nullable final String leftConflictTitle, - @Nullable final String rightConflictTitle) { - VirtualFile baseDir = project.getBaseDir(); - assert baseDir != null; - final String projectPath = baseDir.getPath() + "/"; + @RequiredUIAccess + public static void doSystemUnshelve( + @Nonnull Project project, + ShelvedChangeList shelvedChangeList, + ShelveChangesManager shelveManager, + @Nonnull LocalizeValue leftConflictTitle, + @Nonnull LocalizeValue rightConflictTitle + ) { + VirtualFile baseDir = project.getBaseDir(); + assert baseDir != null; + String projectPath = baseDir.getPath() + "/"; - LOG.info("refreshing files "); - // The changes are temporary copied to the first local change list, the next operation will restore them back - // Refresh files that might be affected by unshelve - refreshFilesBeforeUnshelve(project, shelvedChangeList, projectPath); + LOG.info("refreshing files "); + // The changes are temporary copied to the first local change list, the next operation will restore them back + // Refresh files that might be affected by unshelve + refreshFilesBeforeUnshelve(project, shelvedChangeList, projectPath); - LOG.info("Unshelving shelvedChangeList: " + shelvedChangeList); - final List changes = shelvedChangeList.getChanges(project); - // we pass null as target change list for Patch Applier to do NOTHING with change lists - shelveManager.unshelveChangeList(shelvedChangeList, - changes, - shelvedChangeList.getBinaryFiles(), - null, - false, - true, - true, - leftConflictTitle, - rightConflictTitle); - ApplicationManager.getApplication().invokeAndWait(new Runnable() { - @Override - public void run() { - markUnshelvedFilesNonUndoable(project, changes); - } - }, Application.get().getDefaultModalityState()); - } + LOG.info("Unshelving shelvedChangeList: " + shelvedChangeList); + List changes = shelvedChangeList.getChanges(project); + // we pass null as target change list for Patch Applier to do NOTHING with change lists + shelveManager.unshelveChangeList( + shelvedChangeList, + changes, + shelvedChangeList.getBinaryFiles(), + null, + false, + true, + true, + leftConflictTitle.get(), + rightConflictTitle.get() + ); + Application application = project.getApplication(); + application.invokeAndWait(() -> markUnshelvedFilesNonUndoable(project, changes), application.getDefaultModalityState()); + } - @RequiredUIAccess - private static void markUnshelvedFilesNonUndoable(@Nonnull final Project project, @Nonnull List changes) { - final UndoManager undoManager = ProjectUndoManager.getInstance(project); - if (undoManager != null && !changes.isEmpty()) { - ContainerUtil.process(changes, change -> { - final VirtualFile vfUnderProject = VirtualFileUtil.findFileByIoFile(new File(project.getBasePath(), change.getAfterPath()), false); - if (vfUnderProject != null) { - final DocumentReference documentReference = DocumentReferenceManager.getInstance().create(vfUnderProject); - undoManager.nonundoableActionPerformed(documentReference, false); - undoManager.invalidateActionsFor(documentReference); + @RequiredUIAccess + private static void markUnshelvedFilesNonUndoable(@Nonnull Project project, @Nonnull List changes) { + UndoManager undoManager = ProjectUndoManager.getInstance(project); + if (undoManager != null && !changes.isEmpty()) { + ContainerUtil.process(changes, change -> { + VirtualFile vfUnderProject = + VirtualFileUtil.findFileByIoFile(new File(project.getBasePath(), change.getAfterPath()), false); + if (vfUnderProject != null) { + DocumentReference documentReference = DocumentReferenceManager.getInstance().create(vfUnderProject); + undoManager.nonundoableActionPerformed(documentReference, false); + undoManager.invalidateActionsFor(documentReference); + } + return true; + }); } - return true; - }); } - } - private static void refreshFilesBeforeUnshelve(final Project project, ShelvedChangeList shelvedChangeList, String projectPath) { - HashSet filesToRefresh = new HashSet<>(); - for (ShelvedChange c : shelvedChangeList.getChanges(project)) { - if (c.getBeforePath() != null) { - filesToRefresh.add(new File(projectPath + c.getBeforePath())); - } - if (c.getAfterPath() != null) { - filesToRefresh.add(new File(projectPath + c.getAfterPath())); - } - } - for (ShelvedBinaryFile f : shelvedChangeList.getBinaryFiles()) { - if (f.getBeforePath() != null) { - filesToRefresh.add(new File(projectPath + f.getBeforePath())); - } - if (f.getAfterPath() != null) { - filesToRefresh.add(new File(projectPath + f.getAfterPath())); - } + private static void refreshFilesBeforeUnshelve(Project project, ShelvedChangeList shelvedChangeList, String projectPath) { + HashSet filesToRefresh = new HashSet<>(); + for (ShelvedChange c : shelvedChangeList.getChanges(project)) { + if (c.getBeforePath() != null) { + filesToRefresh.add(new File(projectPath + c.getBeforePath())); + } + if (c.getAfterPath() != null) { + filesToRefresh.add(new File(projectPath + c.getAfterPath())); + } + } + for (ShelvedBinaryFile f : shelvedChangeList.getBinaryFiles()) { + if (f.getBeforePath() != null) { + filesToRefresh.add(new File(projectPath + f.getBeforePath())); + } + if (f.getAfterPath() != null) { + filesToRefresh.add(new File(projectPath + f.getAfterPath())); + } + } + LocalFileSystem.getInstance().refreshIoFiles(filesToRefresh); } - LocalFileSystem.getInstance().refreshIoFiles(filesToRefresh); - } - /** - * Shelve changes - * - * @param project the context project - * @param shelveManager the shelve manager - * @param changes the changes to process - * @param description the description of for the shelve - * @param exceptions the generated exceptions - * @param rollback - * @return created shelved change list or null in case failure - */ - @Nullable - public static ShelvedChangeList shelveChanges(final Project project, - final ShelveChangesManager shelveManager, - Collection changes, - final String description, - final List exceptions, - boolean rollback, - boolean markToBeDeleted) { - try { - ShelvedChangeList shelve = shelveManager.shelveChanges(changes, description, rollback, markToBeDeleted); - project.getMessageBus().syncPublisher(ShelveChangesListener.class).changeChanged(shelveManager); - return shelve; - } - catch (IOException e) { - //noinspection ThrowableInstanceNeverThrown - exceptions.add(new VcsException("Shelving changes failed: " + description, e)); - return null; - } - catch (VcsException e) { - exceptions.add(e); - return null; + /** + * Shelve changes + * + * @param project the context project + * @param shelveManager the shelve manager + * @param changes the changes to process + * @param description the description of for the shelve + * @param exceptions the generated exceptions + * @param rollback + * @return created shelved change list or null in case failure + */ + @Nullable + public static ShelvedChangeList shelveChanges( + @Nonnull Project project, + ShelveChangesManager shelveManager, + Collection changes, + String description, + List exceptions, + boolean rollback, + boolean markToBeDeleted + ) { + try { + ShelvedChangeList shelve = shelveManager.shelveChanges(changes, description, rollback, markToBeDeleted); + project.getMessageBus().syncPublisher(ShelveChangesListener.class).changeChanged(shelveManager); + return shelve; + } + catch (IOException e) { + //noinspection ThrowableInstanceNeverThrown + exceptions.add(new VcsException("Shelving changes failed: " + description, e)); + return null; + } + catch (VcsException e) { + exceptions.add(e); + return null; + } } - } } diff --git a/plugin/src/main/java/git4idea/stash/GitStashChangesSaver.java b/plugin/src/main/java/git4idea/stash/GitStashChangesSaver.java index 9ee514c..ae5d71a 100644 --- a/plugin/src/main/java/git4idea/stash/GitStashChangesSaver.java +++ b/plugin/src/main/java/git4idea/stash/GitStashChangesSaver.java @@ -20,6 +20,7 @@ import consulo.logging.Logger; import consulo.project.Project; import consulo.project.ui.notification.NotificationService; +import consulo.ui.annotation.RequiredUIAccess; import consulo.util.lang.StringUtil; import consulo.versionControlSystem.VcsException; import consulo.versionControlSystem.VcsNotifier; @@ -47,7 +48,6 @@ import java.util.Set; public class GitStashChangesSaver extends GitChangesSaver { - private static final Logger LOG = Logger.getInstance(GitStashChangesSaver.class); private static final String NO_LOCAL_CHANGES_TO_SAVE = "No local changes to save"; @@ -73,7 +73,7 @@ protected void save(@Nonnull Collection rootsToSave) throws VcsExce for (VirtualFile root : rootsToSave) { String message = GitHandlerUtil.formatOperationName("Stashing changes from", root); LOG.info(message); - String oldProgressTitle = myProgressIndicator.getText(); + LocalizeValue oldProgressTitle = myProgressIndicator.getTextValue(); myProgressIndicator.setText(message); GitRepository repository = myRepositoryManager.getRepositoryForRoot(root); if (repository == null) { @@ -94,7 +94,7 @@ protected void save(@Nonnull Collection rootsToSave) throws VcsExce } } } - myProgressIndicator.setText(oldProgressTitle); + myProgressIndicator.setTextValue(oldProgressTitle); } } @@ -104,6 +104,7 @@ private static boolean somethingWasStashed(@Nonnull GitCommandResult result) { } @Override + @RequiredUIAccess public void load() { for (VirtualFile root : myStashedRoots) { loadRoot(root); @@ -130,6 +131,7 @@ public String getOperationName() { } @Override + @RequiredUIAccess public void showSavedChanges() { GitUnstashDialog.showUnstashDialog(myProject, new ArrayList<>(myStashedRoots), myStashedRoots.iterator().next()); } @@ -230,14 +232,16 @@ public String getMultipleFileMergeDescription(@Nonnull Collection f return "Uncommitted changes that were stashed before update have conflicts with updated files."; } + @Nonnull @Override public String getLeftPanelTitle(@Nonnull VirtualFile file) { - return getConflictLeftPanelTitle(); + return getConflictLeftPanelTitle().get(); } + @Nonnull @Override public String getRightPanelTitle(@Nonnull VirtualFile file, VcsRevisionNumber revisionNumber) { - return getConflictRightPanelTitle(); + return getConflictRightPanelTitle().get(); } } }