mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-89754 Add a pre-commit check on core.autocrlf option and warn if CRLF is about to be committed to the repository. Step #1.
* Add another check to the GitCheckinHandler (windows only). * Add the option to disable the check to the settings (visible for windows only). * Add the dialog warning about CRLFs: GitCrlfDialog * Declare the actual GitCrlfProblemsDetector - just as a stub now.
This commit is contained in:
@@ -19,6 +19,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.CheckinProjectPanel;
|
||||
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
|
||||
@@ -28,11 +29,15 @@ import com.intellij.openapi.vcs.checkin.CheckinHandler;
|
||||
import com.intellij.openapi.vcs.checkin.VcsCheckinHandlerFactory;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.PairConsumer;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.config.GitConfigUtil;
|
||||
import git4idea.config.GitVcsSettings;
|
||||
import git4idea.config.GitVersion;
|
||||
import git4idea.config.GitVersionSpecialty;
|
||||
import git4idea.crlf.GitCrlfDialog;
|
||||
import git4idea.crlf.GitCrlfProblemsDetector;
|
||||
import git4idea.i18n.GitBundle;
|
||||
import git4idea.repo.GitRepository;
|
||||
import git4idea.repo.GitRepositoryManager;
|
||||
@@ -60,10 +65,13 @@ public class GitCheckinHandlerFactory extends VcsCheckinHandlerFactory {
|
||||
}
|
||||
|
||||
private class MyCheckinHandler extends CheckinHandler {
|
||||
private CheckinProjectPanel myPanel;
|
||||
@NotNull private final CheckinProjectPanel myPanel;
|
||||
@NotNull private final Project myProject;
|
||||
|
||||
public MyCheckinHandler(CheckinProjectPanel panel) {
|
||||
|
||||
public MyCheckinHandler(@NotNull CheckinProjectPanel panel) {
|
||||
myPanel = panel;
|
||||
myProject = myPanel.getProject();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,11 +85,61 @@ public class GitCheckinHandlerFactory extends VcsCheckinHandlerFactory {
|
||||
if (result != ReturnResult.COMMIT) {
|
||||
return result;
|
||||
}
|
||||
result = warnAboutCrlfIfNeeded();
|
||||
if (result != ReturnResult.COMMIT) {
|
||||
return result;
|
||||
}
|
||||
return warnAboutDetachedHeadIfNeeded();
|
||||
}
|
||||
return ReturnResult.COMMIT;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ReturnResult warnAboutCrlfIfNeeded() {
|
||||
GitVcsSettings settings = GitVcsSettings.getInstance(myProject);
|
||||
if (!SystemInfo.isWindows || !settings.warnAboutCrlf()) {
|
||||
return ReturnResult.COMMIT;
|
||||
}
|
||||
|
||||
GitCrlfProblemsDetector crlfHelper = GitCrlfProblemsDetector.detect(myPanel.getVirtualFiles());
|
||||
if (crlfHelper.shouldWarn()) {
|
||||
final GitCrlfDialog dialog = new GitCrlfDialog(myProject);
|
||||
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
int decision = dialog.getExitCode();
|
||||
if (decision == GitCrlfDialog.CANCEL) {
|
||||
return ReturnResult.CANCEL;
|
||||
}
|
||||
else {
|
||||
if (decision == GitCrlfDialog.SET) {
|
||||
VirtualFile anyRoot = myPanel.getRoots().iterator().next(); // config will be set globally => any root will do.
|
||||
setCoreAutoCrlfAttribute(anyRoot);
|
||||
}
|
||||
else {
|
||||
if (dialog.dontWarnAgain()) {
|
||||
settings.setWarnAboutCrlf(false);
|
||||
}
|
||||
}
|
||||
return ReturnResult.COMMIT;
|
||||
}
|
||||
}
|
||||
return ReturnResult.COMMIT;
|
||||
}
|
||||
|
||||
private void setCoreAutoCrlfAttribute(@NotNull VirtualFile aRoot) {
|
||||
try {
|
||||
GitConfigUtil.setValue(myProject, aRoot, GitConfigUtil.CORE_AUTOCRLF, "true", "--global");
|
||||
}
|
||||
catch (VcsException e) {
|
||||
// it is not critical: the user just will get the dialog again next time
|
||||
LOG.warn("Couldn't globally set core.autocrlf in " + aRoot, e);
|
||||
}
|
||||
}
|
||||
|
||||
private ReturnResult checkUserName() {
|
||||
Project project = myPanel.getProject();
|
||||
GitVcs vcs = GitVcs.getInstance(project);
|
||||
|
||||
@@ -34,13 +34,12 @@ import java.util.Map;
|
||||
* Git utilities for working with configuration
|
||||
*/
|
||||
public class GitConfigUtil {
|
||||
|
||||
public static final String USER_NAME = "user.name";
|
||||
public static final String USER_EMAIL = "user.email";
|
||||
public static final String BRANCH_AUTOSETUP_REBASE = "branch.autosetuprebase";
|
||||
public static final String CORE_AUTOCRLF = "core.autocrlf";
|
||||
|
||||
/**
|
||||
* A private constructor for utility class
|
||||
*/
|
||||
private GitConfigUtil() {
|
||||
}
|
||||
|
||||
|
||||
@@ -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="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="4d15a" layout-manager="GridLayoutManager" row-count="4" column-count="1" 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"/>
|
||||
@@ -126,6 +126,14 @@
|
||||
<text value="Commit automatically on cherry-pick"/>
|
||||
</properties>
|
||||
</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"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Warn if &CRLF line separators are about to be committed"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.i18n.GitBundle;
|
||||
@@ -39,11 +40,7 @@ public class GitVcsPanel {
|
||||
|
||||
private static final String IDEA_SSH = GitBundle.getString("git.vcs.config.ssh.mode.idea"); // IDEA ssh value
|
||||
private static final String NATIVE_SSH = GitBundle.getString("git.vcs.config.ssh.mode.native"); // Native SSH value
|
||||
private static final String CRLF_CONVERT_TO_PROJECT = GitBundle.getString("git.vcs.config.convert.project");
|
||||
private static final String CRLF_DO_NOT_CONVERT = GitBundle.getString("git.vcs.config.convert.do.not.convert");
|
||||
private static final String CRLF_ASK = GitBundle.getString("git.vcs.config.convert.ask");
|
||||
|
||||
private final Project myProject;
|
||||
private final GitVcsApplicationSettings myAppSettings;
|
||||
private final GitVcs myVcs;
|
||||
|
||||
@@ -54,11 +51,11 @@ public class GitVcsPanel {
|
||||
private JCheckBox myAutoUpdateIfPushRejected;
|
||||
private JBCheckBox mySyncBranchControl;
|
||||
private JCheckBox myAutoCommitOnCherryPick;
|
||||
private JBCheckBox myWarnAboutCrlf;
|
||||
|
||||
public GitVcsPanel(@NotNull Project project) {
|
||||
myVcs = GitVcs.getInstance(project);
|
||||
myAppSettings = GitVcsApplicationSettings.getInstance();
|
||||
myProject = project;
|
||||
mySSHExecutableComboBox.addItem(IDEA_SSH);
|
||||
mySSHExecutableComboBox.addItem(NATIVE_SSH);
|
||||
mySSHExecutableComboBox.setSelectedItem(IDEA_SSH);
|
||||
@@ -71,8 +68,9 @@ public class GitVcsPanel {
|
||||
});
|
||||
myGitField.addBrowseFolderListener(GitBundle.getString("find.git.title"), GitBundle.getString("find.git.description"), project,
|
||||
FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
|
||||
final GitRepositoryManager repositoryManager = ServiceManager.getService(myProject, GitRepositoryManager.class);
|
||||
final GitRepositoryManager repositoryManager = ServiceManager.getService(project, GitRepositoryManager.class);
|
||||
mySyncBranchControl.setVisible(repositoryManager != null && repositoryManager.moreThanOneRoot());
|
||||
myWarnAboutCrlf.setVisible(SystemInfo.isWindows);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +122,7 @@ public class GitVcsPanel {
|
||||
myAutoUpdateIfPushRejected.setSelected(settings.autoUpdateIfPushRejected());
|
||||
mySyncBranchControl.setSelected(settings.getSyncSetting() == GitBranchSyncSetting.SYNC);
|
||||
myAutoCommitOnCherryPick.setSelected(settings.isAutoCommitOnCherryPick());
|
||||
myWarnAboutCrlf.setSelected(settings.warnAboutCrlf());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +135,8 @@ public class GitVcsPanel {
|
||||
(settings.isIdeaSsh() != IDEA_SSH.equals(mySSHExecutableComboBox.getSelectedItem())) ||
|
||||
!settings.autoUpdateIfPushRejected() == myAutoUpdateIfPushRejected.isSelected() ||
|
||||
((settings.getSyncSetting() == GitBranchSyncSetting.SYNC) != mySyncBranchControl.isSelected() ||
|
||||
settings.isAutoCommitOnCherryPick() != myAutoCommitOnCherryPick.isSelected());
|
||||
settings.isAutoCommitOnCherryPick() != myAutoCommitOnCherryPick.isSelected() ||
|
||||
settings.warnAboutCrlf() != myWarnAboutCrlf.isSelected());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,5 +154,7 @@ public class GitVcsPanel {
|
||||
|
||||
settings.setSyncSetting(mySyncBranchControl.isSelected() ? GitBranchSyncSetting.SYNC : GitBranchSyncSetting.DONT);
|
||||
settings.setAutoCommitOnCherryPick(myAutoCommitOnCherryPick.isSelected());
|
||||
settings.setWarnAboutCrlf(myWarnAboutCrlf.isSelected());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class GitVcsSettings implements PersistentStateComponent<GitVcsSettings.S
|
||||
|
||||
private final GitVcsApplicationSettings myAppSettings;
|
||||
private State myState = new State();
|
||||
|
||||
|
||||
/**
|
||||
* The way the local changes are saved before update if user has selected auto-stash
|
||||
*/
|
||||
@@ -63,6 +63,7 @@ public class GitVcsSettings implements PersistentStateComponent<GitVcsSettings.S
|
||||
public Map<String, String> RECENT_BRANCH_BY_REPOSITORY = new HashMap<String, String>();
|
||||
public String RECENT_COMMON_BRANCH = null;
|
||||
public boolean AUTO_COMMIT_ON_CHERRY_PICK = false;
|
||||
public boolean WARN_ABOUT_CRLF = true;
|
||||
}
|
||||
|
||||
public GitVcsSettings(GitVcsApplicationSettings appSettings) {
|
||||
@@ -171,6 +172,14 @@ public class GitVcsSettings implements PersistentStateComponent<GitVcsSettings.S
|
||||
return myState.AUTO_COMMIT_ON_CHERRY_PICK;
|
||||
}
|
||||
|
||||
public boolean warnAboutCrlf() {
|
||||
return myState.WARN_ABOUT_CRLF;
|
||||
}
|
||||
|
||||
public void setWarnAboutCrlf(boolean warn) {
|
||||
myState.WARN_ABOUT_CRLF = warn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides migration from project settings.
|
||||
* This method is to be removed in IDEA 13: it should be moved to {@link GitVcsApplicationSettings}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.crlf;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.ui.components.labels.LinkLabel;
|
||||
import com.intellij.ui.components.labels.LinkListener;
|
||||
import com.intellij.util.ui.GridBag;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
import static com.intellij.util.ui.UIUtil.DEFAULT_HGAP;
|
||||
import static com.intellij.util.ui.UIUtil.DEFAULT_VGAP;
|
||||
|
||||
/**
|
||||
* Warns the user that CRLF line separators are about to be committed to the repository.
|
||||
* Provides some additional information and proposes to set {@code git config --global core.autocrlf true}.
|
||||
*
|
||||
* @author Kirill Likhodedov
|
||||
* @see GitCrlfProblemsDetector
|
||||
*/
|
||||
public class GitCrlfDialog extends DialogWrapper {
|
||||
|
||||
public static final int SET = DialogWrapper.OK_EXIT_CODE;
|
||||
public static final int DONT_SET = DialogWrapper.NEXT_USER_EXIT_CODE;
|
||||
public static final int CANCEL = DialogWrapper.CANCEL_EXIT_CODE;
|
||||
private JBCheckBox myDontWarn;
|
||||
|
||||
public GitCrlfDialog(@Nullable Project project) {
|
||||
super(project, false);
|
||||
|
||||
setOKButtonText("Fix and Commit");
|
||||
setCancelButtonText("Cancel");
|
||||
setTitle("Line separators warning");
|
||||
getCancelAction().putValue(DialogWrapper.FOCUSED_ACTION, true);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Action[] createActions() {
|
||||
return new Action[] { getOKAction(), getCancelAction(), new DialogWrapperExitAction("Commit as Is", DONT_SET) };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
JLabel description = new JBLabel(
|
||||
"<html>You are about to commit CRLF line separators to the Git repository.<br/>" +
|
||||
"It is recommended to set core.autocrlf Git attribute to <code>true</code> to avoid line separator issues.</html>");
|
||||
|
||||
JLabel additionalDescription = new JBLabel(
|
||||
"<html>Fix and Commit: <code>git config --global core.autocrlf true</code> will be called,<br/>" +
|
||||
"Commit as Is: the config value won't be set.</html>", UIUtil.ComponentStyle.SMALL);
|
||||
|
||||
JLabel readMore = new LinkLabel("Read more", null, new LinkListener() {
|
||||
@Override
|
||||
public void linkSelected(LinkLabel aSource, Object aLinkData) {
|
||||
BrowserUtil.launchBrowser("https://help.github.com/articles/dealing-with-line-endings");
|
||||
}
|
||||
});
|
||||
|
||||
JLabel icon = new JLabel(UIUtil.getWarningIcon(), SwingConstants.LEFT);
|
||||
myDontWarn = new JBCheckBox("Don't warn again");
|
||||
myDontWarn.setMnemonic('w');
|
||||
|
||||
JPanel rootPanel = new JPanel(new GridBagLayout());
|
||||
GridBag g = new GridBag()
|
||||
.setDefaultInsets(new Insets(0, 6, DEFAULT_VGAP, DEFAULT_HGAP))
|
||||
.setDefaultAnchor(GridBagConstraints.LINE_START)
|
||||
.setDefaultFill(GridBagConstraints.HORIZONTAL);
|
||||
|
||||
rootPanel.add(icon, g.nextLine().next().coverColumn(4));
|
||||
rootPanel.add(description, g.next());
|
||||
rootPanel.add(readMore, g.nextLine().next().next());
|
||||
rootPanel.add(additionalDescription, g.nextLine().next().next().pady(DEFAULT_HGAP));
|
||||
rootPanel.add(myDontWarn, g.nextLine().next().next().insets(0, 0, 0, 0));
|
||||
|
||||
return rootPanel;
|
||||
|
||||
}
|
||||
|
||||
public boolean dontWarnAgain() {
|
||||
return myDontWarn.isSelected();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.crlf;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Given a number of files, detects if CRLF line separators in them are about to be committed to Git. That is:
|
||||
* <ul>
|
||||
* <li>Checks if {@code core.autocrlf} is set to {@code true} or {@code input}.</li>
|
||||
* <li>If not, checks if files contain CRLFs.</li>
|
||||
* <li>
|
||||
* For files with CRLFs checks if there are gitattributes set on them, such that would either force CRLF conversion on checkin,
|
||||
* either indicate that these CRLFs are here intentionally.
|
||||
* </li>
|
||||
* </ul>
|
||||
* All checks are made only for Windows system.
|
||||
*
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GitCrlfProblemsDetector {
|
||||
|
||||
@NotNull
|
||||
public static GitCrlfProblemsDetector detect(Collection<VirtualFile> files) {
|
||||
return new GitCrlfProblemsDetector();
|
||||
}
|
||||
|
||||
public boolean shouldWarn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user