blob: 68b16164b335dd2cd2ddab0d41a7914699c99a6f [file] [log] [blame]
/*
* Copyright 2000-2013 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.merge;
import com.intellij.ide.util.ElementsChooser;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ListCellRendererWrapper;
import git4idea.GitBranch;
import git4idea.GitRemoteBranch;
import git4idea.GitUtil;
import git4idea.commands.GitCommand;
import git4idea.commands.GitLineHandler;
import git4idea.i18n.GitBundle;
import git4idea.repo.GitBranchTrackInfo;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import git4idea.util.GitUIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class GitPullDialog extends DialogWrapper {
private static final Logger LOG = Logger.getInstance(GitPullDialog.class);
private JPanel myPanel;
private JComboBox myGitRoot;
private JLabel myCurrentBranch;
private JComboBox myStrategy;
private JCheckBox myNoCommitCheckBox;
private JCheckBox mySquashCommitCheckBox;
private JCheckBox myNoFastForwardCheckBox;
private JCheckBox myAddLogInformationCheckBox;
private JComboBox myRemote;
private ElementsChooser<String> myBranchChooser;
private final Project myProject;
private final GitRepositoryManager myRepositoryManager;
public GitPullDialog(Project project, List<VirtualFile> roots, VirtualFile defaultRoot) {
super(project, true);
setTitle(GitBundle.getString("pull.title"));
myProject = project;
myRepositoryManager = GitUtil.getRepositoryManager(myProject);
GitUIUtil.setupRootChooser(myProject, roots, defaultRoot, myGitRoot, myCurrentBranch);
myGitRoot.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
updateRemotes();
}
});
setOKButtonText(GitBundle.getString("pull.button"));
updateRemotes();
updateBranches();
myRemote.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateBranches();
}
});
final ElementsChooser.ElementsMarkListener<String> listener = new ElementsChooser.ElementsMarkListener<String>() {
public void elementMarkChanged(final String element, final boolean isMarked) {
validateDialog();
}
};
myBranchChooser.addElementsMarkListener(listener);
listener.elementMarkChanged(null, true);
GitUIUtil.imply(mySquashCommitCheckBox, true, myNoCommitCheckBox, true);
GitUIUtil.imply(mySquashCommitCheckBox, true, myAddLogInformationCheckBox, false);
GitUIUtil.exclusive(mySquashCommitCheckBox, true, myNoFastForwardCheckBox, true);
GitMergeUtil.setupStrategies(myBranchChooser, myStrategy);
init();
}
private void validateDialog() {
String selectedRemote = getRemote();
if (StringUtil.isEmptyOrSpaces(selectedRemote)) {
setOKActionEnabled(false);
return;
}
setOKActionEnabled(myBranchChooser.getMarkedElements().size() != 0);
}
public GitLineHandler makeHandler(@NotNull String url) {
GitLineHandler h = new GitLineHandler(myProject, gitRoot(), GitCommand.PULL);
// ignore merge failure for the pull
h.ignoreErrorCode(1);
h.setUrl(url);
h.addProgressParameter();
h.addParameters("--no-stat");
if (myNoCommitCheckBox.isSelected()) {
h.addParameters("--no-commit");
}
else {
if (myAddLogInformationCheckBox.isSelected()) {
h.addParameters("--log");
}
}
if (mySquashCommitCheckBox.isSelected()) {
h.addParameters("--squash");
}
if (myNoFastForwardCheckBox.isSelected()) {
h.addParameters("--no-ff");
}
String strategy = (String)myStrategy.getSelectedItem();
if (!GitMergeUtil.DEFAULT_STRATEGY.equals(strategy)) {
h.addParameters("--strategy", strategy);
}
h.addParameters("-v");
h.addProgressParameter();
final List<String> markedBranches = myBranchChooser.getMarkedElements();
String remote = getRemote();
LOG.assertTrue(remote != null, "Selected remote can't be null here.");
// git pull origin master (remote branch name in the format local to that remote)
h.addParameters(remote);
for (String branch : markedBranches) {
h.addParameters(removeRemotePrefix(branch, remote));
}
return h;
}
@NotNull
private static String removeRemotePrefix(@NotNull String branch, @NotNull String remote) {
String prefix = remote + "/";
if (branch.startsWith(prefix)) {
return branch.substring(prefix.length());
}
LOG.error(String.format("Remote branch name seems to be invalid. Branch: %s, remote: %s", branch, remote));
return branch;
}
private void updateBranches() {
String selectedRemote = getRemote();
myBranchChooser.removeAllElements();
if (selectedRemote == null) {
return;
}
GitRepository repository = getRepository();
if (repository == null) {
return;
}
GitBranchTrackInfo trackInfo = GitUtil.getTrackInfoForCurrentBranch(repository);
String currentRemoteBranch = trackInfo == null ? null : trackInfo.getRemoteBranch().getNameForLocalOperations();
List<GitRemoteBranch> remoteBranches = new ArrayList<GitRemoteBranch>(repository.getBranches().getRemoteBranches());
Collections.sort(remoteBranches);
for (GitBranch remoteBranch : remoteBranches) {
if (belongsToRemote(remoteBranch, selectedRemote)) {
myBranchChooser.addElement(remoteBranch.getName(), remoteBranch.getName().equals(currentRemoteBranch));
}
}
validateDialog();
}
private static boolean belongsToRemote(@NotNull GitBranch branch, @NotNull String remote) {
return branch.getName().startsWith(remote + "/");
}
private void updateRemotes() {
GitRepository repository = getRepository();
if (repository == null) {
return;
}
GitRemote currentRemote = getCurrentOrDefaultRemote(repository);
myRemote.setRenderer(getGitRemoteListCellRenderer(currentRemote != null ? currentRemote.getName() : null));
myRemote.removeAllItems();
for (GitRemote remote : repository.getRemotes()) {
myRemote.addItem(remote);
}
myRemote.setSelectedItem(currentRemote);
}
/**
* If the current branch is a tracking branch, returns its remote.
* Otherwise tries to guess: if there is origin, returns origin, otherwise returns the first remote in the list.
*/
@Nullable
private static GitRemote getCurrentOrDefaultRemote(@NotNull GitRepository repository) {
Collection<GitRemote> remotes = repository.getRemotes();
if (remotes.isEmpty()) {
return null;
}
GitBranchTrackInfo trackInfo = GitUtil.getTrackInfoForCurrentBranch(repository);
if (trackInfo != null) {
return trackInfo.getRemote();
}
else {
GitRemote origin = getOriginRemote(remotes);
if (origin != null) {
return origin;
}
else {
return remotes.iterator().next();
}
}
}
@Nullable
private static GitRemote getOriginRemote(@NotNull Collection<GitRemote> remotes) {
for (GitRemote remote : remotes) {
if (remote.getName().equals(GitRemote.ORIGIN_NAME)) {
return remote;
}
}
return null;
}
@Nullable
private GitRepository getRepository() {
VirtualFile root = gitRoot();
GitRepository repository = myRepositoryManager.getRepositoryForRoot(root);
if (repository == null) {
LOG.error("Repository is null for " + root);
return null;
}
return repository;
}
/**
* Create list cell renderer for remotes. It shows both name and url and highlights the default
* remote for the branch with bold.
*
*
* @param defaultRemote a default remote
* @return a list cell renderer for virtual files (it renders presentable URL
*/
public ListCellRendererWrapper<GitRemote> getGitRemoteListCellRenderer(final String defaultRemote) {
return new ListCellRendererWrapper<GitRemote>() {
@Override
public void customize(final JList list, final GitRemote remote, final int index, final boolean selected, final boolean hasFocus) {
final String text;
if (remote == null) {
text = GitBundle.getString("util.remote.renderer.none");
}
else if (".".equals(remote.getName())) {
text = GitBundle.getString("util.remote.renderer.self");
}
else {
String key;
if (defaultRemote != null && defaultRemote.equals(remote.getName())) {
key = "util.remote.renderer.default";
}
else {
key = "util.remote.renderer.normal";
}
text = GitBundle.message(key, remote.getName(), remote.getFirstUrl());
}
setText(text);
}
};
}
public VirtualFile gitRoot() {
return (VirtualFile)myGitRoot.getSelectedItem();
}
private void createUIComponents() {
myBranchChooser = new ElementsChooser<String>(true);
}
protected JComponent createCenterPanel() {
return myPanel;
}
@Override
protected String getDimensionServiceKey() {
return getClass().getName();
}
@Override
protected String getHelpId() {
return "reference.VersionControl.Git.Pull";
}
@Nullable
public String getRemote() {
GitRemote remote = (GitRemote)myRemote.getSelectedItem();
return remote == null ? null : remote.getName();
}
@Override
public JComponent getPreferredFocusedComponent() {
return myBranchChooser.getComponent();
}
}