diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java index 78892d4d9e25..9478572be570 100644 --- a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java +++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java @@ -28,13 +28,12 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.Gray; import com.intellij.ui.JBColor; import com.intellij.ui.RawCommandLineEditor; import com.intellij.ui.components.JBLabel; -import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtilRt; +import com.intellij.util.execution.ParametersListUtil; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NotNull; @@ -48,23 +47,6 @@ import static com.intellij.compiler.options.CompilerOptionsFilter.Setting; public class CompilerUIConfigurable implements SearchableConfigurable, Configurable.NoScroll { private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.options.CompilerUIConfigurable"); - public static final Function> LINE_PARSER = new Function>() { - @Override - public List fun(String text) { - final ArrayList result = ContainerUtilRt.newArrayList(); - final StringTokenizer tokenizer = new StringTokenizer(text, ";", false); - while (tokenizer.hasMoreTokens()) { - result.add(tokenizer.nextToken()); - } - return result; - } - }; - public static final Function, String> LINE_JOINER = new Function, String>() { - @Override - public String fun(List strings) { - return StringUtil.join(strings, ";"); - } - }; private static final Set EXTERNAL_BUILD_SETTINGS = EnumSet.of( Setting.EXTERNAL_BUILD, Setting.AUTO_MAKE, Setting.PARALLEL_COMPILATION, Setting.REBUILD_MODULE_ON_DEPENDENCY_CHANGE, Setting.HEAP_SIZE, Setting.COMPILER_VM_OPTIONS @@ -327,7 +309,7 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura } private void createUIComponents() { - myResourcePatternsField = new RawCommandLineEditor(LINE_PARSER, LINE_JOINER); + myResourcePatternsField = new RawCommandLineEditor(ParametersListUtil.COLON_LINE_PARSER, ParametersListUtil.COLON_LINE_JOINER); myResourcePatternsField.setDialogCaption("Resource patterns"); } } diff --git a/platform/dvcs-api/src/com/intellij/dvcs/push/PushSupport.java b/platform/dvcs-api/src/com/intellij/dvcs/push/PushSupport.java index 907bfeeba900..cbd406b0a037 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/push/PushSupport.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/push/PushSupport.java @@ -69,4 +69,20 @@ public abstract class PushSupport + * Force push may be completely disabled for the project which is checked by {@link #isForcePushEnabled()}, + * or it might depend e.g. on the branch user is pushing to. + */ + public abstract boolean isForcePushAllowed(@NotNull Repo repo, Target target); + + /** + * Checks if force push is allowed for this VCS at all. + *

+ * If it is not allowed for all PushSupports in the project, the "Force Push" button will be invisible. + */ + public abstract boolean isForcePushEnabled(); + } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java index 3412e77e2da5..f2f7b07044c0 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java @@ -79,6 +79,24 @@ public class PushController implements Disposable { selectFirstChecked(); } + public boolean isForcePushEnabled() { + return ContainerUtil.exists(myView2Model.values(), new Condition() { + @Override + public boolean value(MyRepoModel model) { + return model.getSupport().isForcePushEnabled(); + } + }); + } + + public boolean isForcePushAllowed() { + return ContainerUtil.exists(myView2Model.values(), new Condition() { + @Override + public boolean value(MyRepoModel model) { + return model.isSelected() && model.getSupport().isForcePushAllowed(model.getRepository(), model.getTarget()); + } + }); + } + private void selectFirstChecked() { Map.Entry selected = ContainerUtil.find(myView2Model.entrySet(), new Condition>() { diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsPushDialog.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsPushDialog.java index 22f1e74abbbd..a6e8e1ca41fc 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsPushDialog.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsPushDialog.java @@ -41,10 +41,10 @@ public class VcsPushDialog extends DialogWrapper { @NotNull private final Project myProject; private final PushLog myListPanel; private final PushController myController; - private final Action[] myExecutorActions = {new DvcsPushAction("&Force Push", true)}; @NotNull private final JPanel myAdditionalOptionsFromVcsPanel; - private DvcsPushAction myPushAction; + private Action myPushAction; + @Nullable private ForcePushAction myForcePushAction; public VcsPushDialog(@NotNull Project project, @NotNull List selectedRepositories) { super(project); @@ -60,7 +60,6 @@ public class VcsPushDialog extends DialogWrapper { @Override protected JComponent createCenterPanel() { - JComponent rootPanel = new JPanel(new BorderLayout(0, 15)); rootPanel.add(myListPanel, BorderLayout.CENTER); for (VcsPushOptionsPanel panel : myController.getAdditionalPanels()) { @@ -79,10 +78,16 @@ public class VcsPushDialog extends DialogWrapper { @NotNull protected Action[] createActions() { final List actions = new ArrayList(); - myPushAction = new DvcsPushAction("&Push", false); + if (myController.isForcePushEnabled()) { + myForcePushAction = new ForcePushAction(); + myForcePushAction.setEnabled(myController.isForcePushAllowed()); + myPushAction = new ComplexPushAction(myForcePushAction); + } + else { + myPushAction = new SimplePushAction(); + } myPushAction.putValue(DEFAULT_ACTION, Boolean.TRUE); actions.add(myPushAction); - myPushAction.setOptions(myExecutorActions); actions.add(getCancelAction()); actions.add(getHelpAction()); return actions.toArray(new Action[actions.size()]); @@ -112,6 +117,9 @@ public class VcsPushDialog extends DialogWrapper { } public void updateButtons() { + if (myForcePushAction != null) { + myForcePushAction.setEnabled(myController.isForcePushAllowed()); + } initValidation(); } @@ -120,13 +128,39 @@ public class VcsPushDialog extends DialogWrapper { return false; } - private class DvcsPushAction extends AbstractAction implements OptionAction { - private Action[] myOptions = new Action[0]; - private final boolean myForce; + private class SimplePushAction extends AbstractAction { + SimplePushAction() { + super("&Push"); + } + @Override + public void actionPerformed(ActionEvent e) { + myController.push(false); + close(OK_EXIT_CODE); + } + } - private DvcsPushAction(String title, boolean force) { - super(title); - myForce = force; + private class ForcePushAction extends AbstractAction { + ForcePushAction() { + super("&Force Push"); + } + + @Override + public void actionPerformed(ActionEvent e) { + int answer = Messages.showOkCancelDialog(myProject, + "You're going to force push. It will overwrite commits at the remote. Are you sure you want to proceed?", + "Force Push", "&Force Push", CommonBundle.getCancelButtonText(), Messages.getWarningIcon()); + if (answer == OK) { + myController.push(true); + close(OK_EXIT_CODE); + } + } + } + + private class ComplexPushAction extends SimplePushAction implements OptionAction { + private final Action[] myOptions; + + private ComplexPushAction(Action additionalAction) { + myOptions = new Action[] { additionalAction }; } @Override @@ -137,31 +171,11 @@ public class VcsPushDialog extends DialogWrapper { } } - @Override - public void actionPerformed(ActionEvent e) { - if (myForce) { - int answer = Messages.showOkCancelDialog(myProject, getConfirmationMessage(), - "Force Push", - "&Force Push", CommonBundle.getCancelButtonText(), Messages.getWarningIcon()); - if (answer != OK) return; - } - myController.push(myForce); - close(OK_EXIT_CODE); - } - @NotNull @Override public Action[] getOptions() { return myOptions; } - - public void setOptions(Action[] actions) { - myOptions = actions; - } } - @NotNull - private static String getConfirmationMessage() { - return "You're going to force push. It will overwrite commits at the remote. Are you sure you want to proceed?"; - } } diff --git a/platform/util/src/com/intellij/util/execution/ParametersListUtil.java b/platform/util/src/com/intellij/util/execution/ParametersListUtil.java index d84b86cbad8b..2aea48de96fa 100644 --- a/platform/util/src/com/intellij/util/execution/ParametersListUtil.java +++ b/platform/util/src/com/intellij/util/execution/ParametersListUtil.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.StringTokenizer; /** * @author nik @@ -41,6 +42,23 @@ public class ParametersListUtil { return StringUtil.join(strings, " "); } }; + public static final Function> COLON_LINE_PARSER = new Function>() { + @Override + public List fun(String text) { + final ArrayList result = ContainerUtilRt.newArrayList(); + final StringTokenizer tokenizer = new StringTokenizer(text, ";", false); + while (tokenizer.hasMoreTokens()) { + result.add(tokenizer.nextToken()); + } + return result; + } + }; + public static final Function, String> COLON_LINE_JOINER = new Function, String>() { + @Override + public String fun(List strings) { + return StringUtil.join(strings, ";"); + } + }; /** *

Joins list of parameters into single string, which may be then parsed back into list by {@link #parseToArray(String)}.

diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index f0d8fa5ff5e1..8fe4a1137262 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -167,6 +167,7 @@ + diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index 5b3aa4dc0f97..a47ec0f025e9 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -173,7 +173,8 @@ public class GitVcs extends AbstractVcs { @NotNull final GitHistoryProvider gitHistoryProvider, @NotNull final GitRollbackEnvironment gitRollbackEnvironment, @NotNull final GitVcsApplicationSettings gitSettings, - @NotNull final GitVcsSettings gitProjectSettings) { + @NotNull final GitVcsSettings gitProjectSettings, + @NotNull GitSharedSettings sharedSettings) { super(project, NAME); myGit = git; myVcsManager = gitVcsManager; @@ -185,7 +186,7 @@ public class GitVcs extends AbstractVcs { myHistoryProvider = gitHistoryProvider; myRollbackEnvironment = gitRollbackEnvironment; myRevSelector = new GitRevisionSelector(); - myConfigurable = new GitVcsConfigurable(gitProjectSettings, myProject); + myConfigurable = new GitVcsConfigurable(myProject, gitProjectSettings, sharedSettings); myUpdateEnvironment = new GitUpdateEnvironment(myProject, this, gitProjectSettings); myCommittedChangeListProvider = new GitCommittedChangeListProvider(myProject); myOutgoingChangesProvider = new GitOutgoingChangesProvider(myProject); diff --git a/plugins/git4idea/src/git4idea/config/GitSharedSettings.java b/plugins/git4idea/src/git4idea/config/GitSharedSettings.java new file mode 100644 index 000000000000..4363880dd931 --- /dev/null +++ b/plugins/git4idea/src/git4idea/config/GitSharedSettings.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package git4idea.config; + +import com.intellij.openapi.components.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@State( + name = "GitSharedSettings", + storages = { + @Storage(file = StoragePathMacros.PROJECT_FILE), + @Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/vcs.xml", scheme = StorageScheme.DIRECTORY_BASED) + }) +public class GitSharedSettings implements PersistentStateComponent { + + public static class State { + public List FORCE_PUSH_PROHIBITED_PATTERNS = Arrays.asList("master"); + } + + private State myState = new State(); + + @Nullable + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(State state) { + myState = state; + } + + @NotNull + public List getForcePushProhibitedPatterns() { + return Collections.unmodifiableList(myState.FORCE_PUSH_PROHIBITED_PATTERNS); + } + + public void setForcePushProhibitedPatters(@NotNull List patterns) { + myState.FORCE_PUSH_PROHIBITED_PATTERNS = new ArrayList(patterns); + } + +} diff --git a/plugins/git4idea/src/git4idea/config/GitVcsConfigurable.java b/plugins/git4idea/src/git4idea/config/GitVcsConfigurable.java index 12dd5fb1e5d7..a2cf35d75e21 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsConfigurable.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsConfigurable.java @@ -28,11 +28,13 @@ public class GitVcsConfigurable implements Configurable { private final Project myProject; private final GitVcsSettings mySettings; + @NotNull private final GitSharedSettings mySharedSettings; private GitVcsPanel panel; - public GitVcsConfigurable(@NotNull GitVcsSettings settings, @NotNull Project project) { + public GitVcsConfigurable(@NotNull Project project, @NotNull GitVcsSettings settings, @NotNull GitSharedSettings sharedSettings) { myProject = project; mySettings = settings; + mySharedSettings = sharedSettings; } @NotNull @@ -51,23 +53,23 @@ public class GitVcsConfigurable implements Configurable { @Override public JComponent createComponent() { panel = new GitVcsPanel(myProject); - panel.load(mySettings); + panel.load(mySettings, mySharedSettings); return panel.getPanel(); } @Override public boolean isModified() { - return panel.isModified(mySettings); + return panel.isModified(mySettings, mySharedSettings); } @Override public void apply() throws ConfigurationException { - panel.save(mySettings); + panel.save(mySettings, mySharedSettings); } @Override public void reset() { - panel.load(mySettings); + panel.load(mySettings, mySharedSettings); } @Override diff --git a/plugins/git4idea/src/git4idea/config/GitVcsPanel.form b/plugins/git4idea/src/git4idea/config/GitVcsPanel.form index 87d3578f7c0a..844edc4e34a3 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsPanel.form +++ b/plugins/git4idea/src/git4idea/config/GitVcsPanel.form @@ -91,7 +91,7 @@ - + @@ -101,7 +101,7 @@ - + @@ -110,7 +110,7 @@ - + @@ -119,7 +119,7 @@ - + @@ -128,7 +128,7 @@ - + @@ -136,13 +136,38 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/git4idea/src/git4idea/config/GitVcsPanel.java b/plugins/git4idea/src/git4idea/config/GitVcsPanel.java index c8c2ad46a274..c16417abf5d2 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsPanel.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsPanel.java @@ -16,6 +16,7 @@ package git4idea.config; import com.intellij.dvcs.branch.DvcsBranchSync; +import com.intellij.icons.AllIcons; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; @@ -23,6 +24,9 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.ui.components.JBCheckBox; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.execution.ParametersListUtil; import git4idea.GitVcs; import git4idea.i18n.GitBundle; import git4idea.repo.GitRepositoryManager; @@ -31,6 +35,7 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.List; /** * Git VCS configuration panel @@ -52,6 +57,9 @@ public class GitVcsPanel { private JCheckBox myAutoCommitOnCherryPick; private JBCheckBox myWarnAboutCrlf; private JCheckBox myWarnAboutDetachedHead; + private JCheckBox myEnableForcePush; + private TextFieldWithBrowseButton myProtectedBranchesButton; + private JBLabel myProtectedBranchesLabel; public GitVcsPanel(@NotNull Project project) { myVcs = GitVcs.getInstance(project); @@ -70,6 +78,7 @@ public class GitVcsPanel { FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()); final GitRepositoryManager repositoryManager = ServiceManager.getService(project, GitRepositoryManager.class); mySyncBranchControl.setVisible(repositoryManager != null && repositoryManager.moreThanOneRoot()); + myProtectedBranchesLabel.setLabelFor(myProtectedBranchesButton); } /** @@ -115,7 +124,7 @@ public class GitVcsPanel { * * @param settings the settings to load */ - public void load(@NotNull GitVcsSettings settings) { + public void load(@NotNull GitVcsSettings settings, @NotNull GitSharedSettings sharedSettings) { myGitField.setText(settings.getAppSettings().getPathToGit()); mySSHExecutableComboBox.setSelectedItem(settings.isIdeaSsh() ? IDEA_SSH : NATIVE_SSH); myAutoUpdateIfPushRejected.setSelected(settings.autoUpdateIfPushRejected()); @@ -123,6 +132,8 @@ public class GitVcsPanel { myAutoCommitOnCherryPick.setSelected(settings.isAutoCommitOnCherryPick()); myWarnAboutCrlf.setSelected(settings.warnAboutCrlf()); myWarnAboutDetachedHead.setSelected(settings.warnAboutDetachedHead()); + myEnableForcePush.setSelected(settings.isForcePushAllowed()); + myProtectedBranchesButton.setText(ParametersListUtil.COLON_LINE_JOINER.fun(sharedSettings.getForcePushProhibitedPatterns())); } /** @@ -130,14 +141,17 @@ public class GitVcsPanel { * * @param settings the settings to load */ - public boolean isModified(@NotNull GitVcsSettings settings) { + public boolean isModified(@NotNull GitVcsSettings settings, @NotNull GitSharedSettings sharedSettings) { return !settings.getAppSettings().getPathToGit().equals(getCurrentExecutablePath()) || (settings.isIdeaSsh() != IDEA_SSH.equals(mySSHExecutableComboBox.getSelectedItem())) || !settings.autoUpdateIfPushRejected() == myAutoUpdateIfPushRejected.isSelected() || ((settings.getSyncSetting() == DvcsBranchSync.SYNC) != mySyncBranchControl.isSelected() || settings.isAutoCommitOnCherryPick() != myAutoCommitOnCherryPick.isSelected() || settings.warnAboutCrlf() != myWarnAboutCrlf.isSelected() || - settings.warnAboutDetachedHead() != myWarnAboutDetachedHead.isSelected()); + settings.warnAboutDetachedHead() != myWarnAboutDetachedHead.isSelected() || + settings.isForcePushAllowed() != myEnableForcePush.isSelected() || + !ContainerUtil.sorted(sharedSettings.getForcePushProhibitedPatterns()).equals( + ContainerUtil.sorted(getProtectedBranchesPatterns()))); } /** @@ -145,7 +159,7 @@ public class GitVcsPanel { * * @param settings the settings object */ - public void save(@NotNull GitVcsSettings settings) { + public void save(@NotNull GitVcsSettings settings, GitSharedSettings sharedSettings) { settings.getAppSettings().setPathToGit(getCurrentExecutablePath()); myVcs.checkVersion(); settings.getAppSettings().setIdeaSsh(IDEA_SSH.equals(mySSHExecutableComboBox.getSelectedItem()) ? @@ -157,6 +171,23 @@ public class GitVcsPanel { settings.setAutoCommitOnCherryPick(myAutoCommitOnCherryPick.isSelected()); settings.setWarnAboutCrlf(myWarnAboutCrlf.isSelected()); settings.setWarnAboutDetachedHead(myWarnAboutDetachedHead.isSelected()); + settings.setForcePushAllowed(myEnableForcePush.isSelected()); + sharedSettings.setForcePushProhibitedPatters(getProtectedBranchesPatterns()); } + @NotNull + private List getProtectedBranchesPatterns() { + return ParametersListUtil.COLON_LINE_PARSER.fun(myProtectedBranchesButton.getText()); + } + + private void createUIComponents() { + myProtectedBranchesButton = new TextFieldWithBrowseButton.NoPathCompletion(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Messages.showTextAreaDialog(myProtectedBranchesButton.getTextField(), "Protected Branches", "Git.Force.Push.Protected.Branches", + ParametersListUtil.COLON_LINE_PARSER, ParametersListUtil.COLON_LINE_JOINER); + } + }); + myProtectedBranchesButton.setButtonIcon(AllIcons.Actions.ShowViewer); + } } diff --git a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java index 7dd7f9b1004b..f4094f81eaec 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java @@ -68,6 +68,7 @@ public class GitVcsSettings implements PersistentStateComponent myPusher; @NotNull private final OutgoingCommitsProvider myOutgoingCommitsProvider; + @NotNull private final GitVcsSettings mySettings; + private final GitSharedSettings mySharedSettings; // instantiated from plugin.xml @SuppressWarnings("UnusedDeclaration") @@ -51,6 +58,8 @@ public class GitPushSupport extends PushSupport() { + @Override + public boolean value(String pattern) { + return targetBranch.matches("^" + pattern + "$"); // let "master" match only "master" and not "any-master-here" by default + } + }); + } + + @Override + public boolean isForcePushEnabled() { + return mySettings.isForcePushAllowed(); + } + } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java index 1a3bb3db1147..66cda264c210 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java @@ -16,6 +16,7 @@ package org.zmlx.hg4idea.push; import com.intellij.dvcs.push.*; +import com.intellij.dvcs.repo.Repository; import com.intellij.dvcs.repo.RepositoryManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.AbstractVcs; @@ -85,6 +86,16 @@ public class HgPushSupport extends PushSupport