From f2a059c44cfb06beb91b42d5e72bda54bb4b421b Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 27 Jan 2012 17:21:22 +0400 Subject: [PATCH] IDEA-64049 Git: before commit check if user.name and user.email have been set, otherwise notify Check if user.name/user.email is defined for all roots affected by commit. If not, display a dialog where user can defined the properties right away (globally or just for these roots). --- .../checkin/GitCheckinHandlerFactory.java | 98 ++++++++++ .../checkin/GitUserNameNotDefinedDialog.java | 176 ++++++++++++++++++ .../src/git4idea/config/GitConfigUtil.java | 4 +- 3 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 plugins/git4idea/src/git4idea/checkin/GitUserNameNotDefinedDialog.java diff --git a/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java b/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java index 1465c23a982c..dcbe6948d172 100644 --- a/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java +++ b/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java @@ -15,26 +15,37 @@ */ package git4idea.checkin; +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.text.StringUtil; import com.intellij.openapi.vcs.CheckinProjectPanel; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.CommitExecutor; 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 git4idea.GitVcs; +import git4idea.config.GitConfigUtil; import git4idea.i18n.GitBundle; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.*; + /** * Prohibits commiting with an empty messages. * @author Kirill Likhodedov */ public class GitCheckinHandlerFactory extends VcsCheckinHandlerFactory { + + private static final Logger LOG = Logger.getInstance(GitCheckinHandlerFactory.class); + public GitCheckinHandlerFactory() { super(GitVcs.getKey()); } @@ -57,12 +68,99 @@ public class GitCheckinHandlerFactory extends VcsCheckinHandlerFactory { if (emptyCommitMessage()) { return ReturnResult.CANCEL; } + + ReturnResult result = checkUserName(); + if (result != ReturnResult.COMMIT) { + return result; + } if (commitOrCommitAndPush(executor)) { return warnAboutDetachedHeadIfNeeded(); } return ReturnResult.COMMIT; } + + private ReturnResult checkUserName() { + Project project = myPanel.getProject(); + Collection notDefined = new ArrayList(); + Map> defined = new HashMap>(); + Collection allRoots = new ArrayList(Arrays.asList( + ProjectLevelVcsManager.getInstance(project).getRootsUnderVcs(GitVcs.getInstance(project)))); + + Collection affectedRoots = myPanel.getRoots(); + for (VirtualFile root : affectedRoots) { + try { + Pair nameAndEmail = getUserNameAndEmailFromGitConfig(project, root); + String name = nameAndEmail.getFirst(); + String email = nameAndEmail.getSecond(); + if (name == null || email == null) { + notDefined.add(root); + } + else { + defined.put(root, nameAndEmail); + } + } + catch (VcsException e) { + LOG.error("Couldn't get user.name and user.email for root " + root, e); + // doing nothing - let commit with possibly empty user.name/email + } + } + + if (notDefined.isEmpty()) { + return ReturnResult.COMMIT; + } + + if (defined.isEmpty() && allRoots.size() > affectedRoots.size()) { + allRoots.removeAll(affectedRoots); + for (VirtualFile root : allRoots) { + try { + Pair nameAndEmail = getUserNameAndEmailFromGitConfig(project, root); + String name = nameAndEmail.getFirst(); + String email = nameAndEmail.getSecond(); + if (name != null && email != null) { + defined.put(root, nameAndEmail); + break; + } + } + catch (VcsException e) { + LOG.error("Couldn't get user.name and user.email for root " + root, e); + // doing nothing - not critical not to find the values for other roots not affected by commit + } + } + } + + GitUserNameNotDefinedDialog dialog = new GitUserNameNotDefinedDialog(project, notDefined, affectedRoots, defined); + dialog.show(); + if (dialog.isOK()) { + try { + if (dialog.isGlobal()) { + GitConfigUtil.setValue(project, notDefined.iterator().next(), GitConfigUtil.USER_NAME, dialog.getUserName(), "--global"); + GitConfigUtil.setValue(project, notDefined.iterator().next(), GitConfigUtil.USER_EMAIL, dialog.getUserEmail(), "--global"); + } + else { + for (VirtualFile root : notDefined) { + GitConfigUtil.setValue(project, root, GitConfigUtil.USER_NAME, dialog.getUserName()); + GitConfigUtil.setValue(project, root, GitConfigUtil.USER_EMAIL, dialog.getUserEmail()); + } + } + } + catch (VcsException e) { + String message = "Couldn't set user.name and user.email"; + LOG.error(message, e); + Messages.showErrorDialog(myPanel.getComponent(), message); + return ReturnResult.CANCEL; + } + return ReturnResult.COMMIT; + } + return ReturnResult.CLOSE_WINDOW; + } + + @NotNull + private Pair getUserNameAndEmailFromGitConfig(@NotNull Project project, @NotNull VirtualFile root) throws VcsException { + String name = GitConfigUtil.getValue(project, root, GitConfigUtil.USER_NAME); + String email = GitConfigUtil.getValue(project, root, GitConfigUtil.USER_EMAIL); + return Pair.create(name, email); + } private boolean emptyCommitMessage() { if (myPanel.getCommitMessage().trim().isEmpty()) { diff --git a/plugins/git4idea/src/git4idea/checkin/GitUserNameNotDefinedDialog.java b/plugins/git4idea/src/git4idea/checkin/GitUserNameNotDefinedDialog.java new file mode 100644 index 000000000000..05ed716e9663 --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkin/GitUserNameNotDefinedDialog.java @@ -0,0 +1,176 @@ +/* + * 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.checkin; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.SystemProperties; +import com.intellij.util.ui.GridBag; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import static com.intellij.openapi.util.text.StringUtil.isEmptyOrSpaces; +import static com.intellij.util.ui.UIUtil.DEFAULT_HGAP; +import static com.intellij.util.ui.UIUtil.DEFAULT_VGAP; + +/** + * @author Kirill Likhodedov + */ +class GitUserNameNotDefinedDialog extends DialogWrapper { + + @NotNull private final Collection myRootsWithUndefinedProps; + @NotNull private final Collection myAllRootsAffectedByCommit; + @Nullable private final Pair myProposedValues; + + private JTextField myNameTextField; + private JTextField myEmailTextField; + private JBCheckBox myGlobalCheckbox; + + GitUserNameNotDefinedDialog(@NotNull Project project, + @NotNull Collection rootsWithUndefinedProps, + @NotNull Collection allRootsAffectedByCommit, + @NotNull Map> rootsWithDefinedProps) { + super(project, false); + myRootsWithUndefinedProps = rootsWithUndefinedProps; + myAllRootsAffectedByCommit = allRootsAffectedByCommit; + + myProposedValues = calcProposedValues(rootsWithDefinedProps); + + setTitle("Git User Name Is Not Defined"); + setOKButtonText("Set and Commit"); + + init(); + } + + @Override + protected ValidationInfo doValidate() { + String message = "You have to specify user name and email for Git"; + if (isEmptyOrSpaces(getUserName())) { + return new ValidationInfo(message, myNameTextField); + } + if (isEmptyOrSpaces(getUserEmail())) { + return new ValidationInfo(message, myEmailTextField); + } + return null; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myNameTextField; + } + + @Nullable + private static Pair calcProposedValues(Map> rootsWithDefinedProps) { + if (rootsWithDefinedProps.isEmpty()) { + return null; + } + Iterator>> iterator = rootsWithDefinedProps.entrySet().iterator(); + Pair firstValue = iterator.next().getValue(); + while (iterator.hasNext()) { + // nothing to propose if there are different values set in different repositories + if (!firstValue.equals(iterator.next().getValue())) { + return null; + } + } + return firstValue; + } + + @Override + protected JComponent createCenterPanel() { + + JLabel icon = new JLabel(UIUtil.getWarningIcon(), SwingConstants.LEFT); + JLabel description = new JLabel(getMessageText()); + + myNameTextField = new JTextField(20); + JBLabel nameLabel = new JBLabel("Name: "); + nameLabel.setDisplayedMnemonic('n'); + nameLabel.setLabelFor(myNameTextField); + + myEmailTextField = new JTextField(20); + JBLabel emailLabel = new JBLabel("E-mail: "); + emailLabel.setDisplayedMnemonic('e'); + emailLabel.setLabelFor(myEmailTextField); + + if (myProposedValues != null) { + myNameTextField.setText(myProposedValues.getFirst()); + myEmailTextField.setText(myProposedValues.getSecond()); + } + else { + myNameTextField.setText(SystemProperties.getUserName()); + } + + myGlobalCheckbox = new JBCheckBox("Set properties globally", true); + myGlobalCheckbox.setMnemonic('g'); + + JPanel rootPanel = new JPanel(new GridBagLayout()); + GridBag g = new GridBag() + .setDefaultInsets(new Insets(0, 0, DEFAULT_VGAP, DEFAULT_HGAP)) + .setDefaultAnchor(GridBagConstraints.LINE_START) + .setDefaultFill(GridBagConstraints.HORIZONTAL); + + rootPanel.add(description, g.nextLine().next().coverLine(3).pady(DEFAULT_HGAP)); + rootPanel.add(icon, g.nextLine().next().coverColumn(3)); + rootPanel.add(nameLabel, g.next().fillCellNone().insets(new Insets(0, 6, DEFAULT_VGAP, DEFAULT_HGAP))); + rootPanel.add(myNameTextField, g.next()); + rootPanel.add(emailLabel, g.nextLine().next().next().fillCellNone().insets(new Insets(0, 6, DEFAULT_VGAP, DEFAULT_HGAP))); + rootPanel.add(myEmailTextField, g.next()); + rootPanel.add(myGlobalCheckbox, g.nextLine().next().next().coverLine(2)); + + return rootPanel; + } + + @Override + protected JComponent createNorthPanel() { + return null; + } + + @NotNull + private String getMessageText() { + if (myAllRootsAffectedByCommit.size() == myRootsWithUndefinedProps.size()) { + return ""; + } + String text = "Git user.name and user.email properties are not defined in " + StringUtil.pluralize("root", myRootsWithUndefinedProps.size()) + "
"; + for (VirtualFile root : myRootsWithUndefinedProps) { + text += root.getPresentableUrl() + "
"; + } + return "" + text + ""; + } + + public String getUserName() { + return myNameTextField.getText(); + } + + public String getUserEmail() { + return myEmailTextField.getText(); + } + + public boolean isGlobal() { + return myGlobalCheckbox.isSelected(); + } + +} diff --git a/plugins/git4idea/src/git4idea/config/GitConfigUtil.java b/plugins/git4idea/src/git4idea/config/GitConfigUtil.java index 0e42072e2505..465559ed3193 100644 --- a/plugins/git4idea/src/git4idea/config/GitConfigUtil.java +++ b/plugins/git4idea/src/git4idea/config/GitConfigUtil.java @@ -35,6 +35,7 @@ import java.util.Map; */ public class GitConfigUtil { public static final String USER_NAME = "user.name"; + public static final String USER_EMAIL = "user.email"; /** * A private constructor for utility class @@ -240,11 +241,12 @@ public class GitConfigUtil { * @param value the value to set * @throws VcsException if there is a problem with running git */ - public static void setValue(Project project, VirtualFile root, String key, String value) throws VcsException { + public static void setValue(Project project, VirtualFile root, String key, String value, String... additionalParameters) throws VcsException { GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.CONFIG); h.setNoSSH(true); h.setSilent(true); h.ignoreErrorCode(1); + h.addParameters(additionalParameters); h.addParameters(key, value); h.run(); }