[git] Restrict the ability to force push

* Introduce a checkbox in the settings which hides the "Force push"
  button by default.
* Let define a list of patterns of branches which are "protected"
  from the force pushing: the force push button will be disabled if
  such branches are selected as push targets.

Relates to IDEA-85773.
This commit is contained in:
Kirill Likhodedov
2014-09-23 16:14:51 +04:00
parent 98d5287aad
commit 606c38c513
14 changed files with 282 additions and 68 deletions
@@ -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<String, List<String>> LINE_PARSER = new Function<String, List<String>>() {
@Override
public List<String> fun(String text) {
final ArrayList<String> result = ContainerUtilRt.newArrayList();
final StringTokenizer tokenizer = new StringTokenizer(text, ";", false);
while (tokenizer.hasMoreTokens()) {
result.add(tokenizer.nextToken());
}
return result;
}
};
public static final Function<List<String>, String> LINE_JOINER = new Function<List<String>, String>() {
@Override
public String fun(List<String> strings) {
return StringUtil.join(strings, ";");
}
};
private static final Set<Setting> 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");
}
}
@@ -69,4 +69,20 @@ public abstract class PushSupport<Repo extends Repository, Source extends PushSo
public boolean shouldRequestIncomingChangesForNotCheckedRepositories() {
return true;
}
/**
* Returns true if force push is allowed now in the selected repository.
* <p/>
* 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.
* <p/>
* If it is not allowed for all PushSupports in the project, the "Force Push" button will be invisible.
*/
public abstract boolean isForcePushEnabled();
}
@@ -79,6 +79,24 @@ public class PushController implements Disposable {
selectFirstChecked();
}
public boolean isForcePushEnabled() {
return ContainerUtil.exists(myView2Model.values(), new Condition<MyRepoModel>() {
@Override
public boolean value(MyRepoModel model) {
return model.getSupport().isForcePushEnabled();
}
});
}
public boolean isForcePushAllowed() {
return ContainerUtil.exists(myView2Model.values(), new Condition<MyRepoModel>() {
@Override
public boolean value(MyRepoModel model) {
return model.isSelected() && model.getSupport().isForcePushAllowed(model.getRepository(), model.getTarget());
}
});
}
private void selectFirstChecked() {
Map.Entry<RepositoryNode, MyRepoModel> selected =
ContainerUtil.find(myView2Model.entrySet(), new Condition<Map.Entry<RepositoryNode, MyRepoModel>>() {
@@ -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<? extends Repository> 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<Action> actions = new ArrayList<Action>();
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?";
}
}
@@ -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<String, List<String>> COLON_LINE_PARSER = new Function<String, List<String>>() {
@Override
public List<String> fun(String text) {
final ArrayList<String> result = ContainerUtilRt.newArrayList();
final StringTokenizer tokenizer = new StringTokenizer(text, ";", false);
while (tokenizer.hasMoreTokens()) {
result.add(tokenizer.nextToken());
}
return result;
}
};
public static final Function<List<String>, String> COLON_LINE_JOINER = new Function<List<String>, String>() {
@Override
public String fun(List<String> strings) {
return StringUtil.join(strings, ";");
}
};
/**
* <p>Joins list of parameters into single string, which may be then parsed back into list by {@link #parseToArray(String)}.</p>
+1
View File
@@ -167,6 +167,7 @@
<projectService
serviceInterface="git4idea.config.GitVcsSettings"
serviceImplementation="git4idea.config.GitVcsSettings"/>
<projectService serviceImplementation="git4idea.config.GitSharedSettings" />
<projectService
serviceInterface="git4idea.settings.GitPushSettings"
serviceImplementation="git4idea.settings.GitPushSettings"/>
+3 -2
View File
@@ -173,7 +173,8 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
@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<CommittedChangeList> {
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);
@@ -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<GitSharedSettings.State> {
public static class State {
public List<String> 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<String> getForcePushProhibitedPatterns() {
return Collections.unmodifiableList(myState.FORCE_PUSH_PROHIBITED_PATTERNS);
}
public void setForcePushProhibitedPatters(@NotNull List<String> patterns) {
myState.FORCE_PUSH_PROHIBITED_PATTERNS = new ArrayList<String>(patterns);
}
}
@@ -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
@@ -91,7 +91,7 @@
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="4d15a" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="4d15a" layout-manager="GridLayoutManager" row-count="6" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
@@ -101,7 +101,7 @@
<children>
<component id="9d661" class="javax.swing.JCheckBox" binding="myAutoUpdateIfPushRejected">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<iconTextGap value="4"/>
@@ -110,7 +110,7 @@
</component>
<component id="2e80d" class="com.intellij.ui.components.JBCheckBox" binding="mySyncBranchControl">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="2" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Control &amp;branches from different roots synchronously"/>
@@ -119,7 +119,7 @@
</component>
<component id="5a98" class="javax.swing.JCheckBox" binding="myAutoCommitOnCherryPick">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="0" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<iconTextGap value="4"/>
@@ -128,7 +128,7 @@
</component>
<component id="1ad55" class="com.intellij.ui.components.JBCheckBox" binding="myWarnAboutCrlf" default-binding="true">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="3" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Warn if &amp;CRLF line separators are about to be committed"/>
@@ -136,13 +136,38 @@
</component>
<component id="29d5c" class="javax.swing.JCheckBox" binding="myWarnAboutDetachedHead">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="4" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="true"/>
<text value="Warn when committing in detached HEAD or during rebase"/>
</properties>
</component>
<component id="885e3" class="javax.swing.JCheckBox" binding="myEnableForcePush">
<constraints>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="true"/>
<text value="Allow &amp;force push"/>
</properties>
</component>
<component id="b0317" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myProtectedBranchesButton" custom-create="true" default-binding="true">
<constraints>
<grid row="5" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="e7b92" class="com.intellij.ui.components.JBLabel" binding="myProtectedBranchesLabel">
<constraints>
<grid row="5" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Protected branches:"/>
</properties>
</component>
</children>
</grid>
</children>
@@ -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<String> 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);
}
}
@@ -68,6 +68,7 @@ public class GitVcsSettings implements PersistentStateComponent<GitVcsSettings.S
public boolean WARN_ABOUT_CRLF = true;
public boolean WARN_ABOUT_DETACHED_HEAD = true;
public GitResetMode RESET_MODE = null;
public boolean FORCE_PUSH_ALLOWED = false;
}
public GitVcsSettings(GitVcsApplicationSettings appSettings) {
@@ -201,6 +202,14 @@ public class GitVcsSettings implements PersistentStateComponent<GitVcsSettings.S
myState.RESET_MODE = mode;
}
public boolean isForcePushAllowed() {
return myState.FORCE_PUSH_ALLOWED;
}
public void setForcePushAllowed(boolean allowed) {
myState.FORCE_PUSH_ALLOWED = allowed;
}
/**
* Provides migration from project settings.
* This method is to be removed in IDEA 13: it should be moved to {@link GitVcsApplicationSettings}
@@ -20,12 +20,17 @@ import com.intellij.dvcs.push.PushSupport;
import com.intellij.dvcs.push.PushTargetPanel;
import com.intellij.dvcs.push.Pusher;
import com.intellij.dvcs.repo.RepositoryManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import git4idea.*;
import git4idea.branch.GitBranchUtil;
import git4idea.config.GitSharedSettings;
import git4idea.config.GitVcsSettings;
import git4idea.repo.GitBranchTrackInfo;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
@@ -43,6 +48,8 @@ public class GitPushSupport extends PushSupport<GitRepository, GitPushSource, Gi
@NotNull private final GitVcs myVcs;
@NotNull private final Pusher<GitRepository, GitPushSource, GitPushTarget> myPusher;
@NotNull private final OutgoingCommitsProvider<GitRepository, GitPushSource, GitPushTarget> 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<GitRepository, GitPushSource, Gi
myVcs = ObjectUtils.assertNotNull(GitVcs.getInstance(project));
myPusher = new GitPusher(project);
myOutgoingCommitsProvider = new GitOutgoingCommitsProvider(project);
mySettings = GitVcsSettings.getInstance(project);
mySharedSettings = ServiceManager.getService(project, GitSharedSettings.class);
}
@NotNull
@@ -131,4 +140,20 @@ public class GitPushSupport extends PushSupport<GitRepository, GitPushSource, Gi
return new GitPushTargetPanel(repository, defaultTarget);
}
@Override
public boolean isForcePushAllowed(@NotNull GitRepository repo, @NotNull GitPushTarget target) {
final String targetBranch = target.getBranch().getNameForRemoteOperations();
return !ContainerUtil.exists(mySharedSettings.getForcePushProhibitedPatterns(), new Condition<String>() {
@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();
}
}
@@ -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<HgRepository, HgPushSource, HgTar
return new HgPushTargetPanel(repository, defaultTarget);
}
@Override
public boolean isForcePushAllowed(@NotNull HgRepository repo, @NotNull HgTarget target) {
return true;
}
@Override
public boolean isForcePushEnabled() {
return true;
}
@Override
public boolean shouldRequestIncomingChangesForNotCheckedRepositories() {
return false;