mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -619,4 +619,8 @@ public class GitUtil {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean repoContainsRemoteBranch(@NotNull GitRepository repository, @NotNull GitBranch dest) {
|
||||
return repository.getBranches().getRemoteBranches().contains(dest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,8 @@ public class DetailsCache {
|
||||
myBranches.clear();
|
||||
myStash.clear();
|
||||
// will be cleared by itself; commits are not changed while they have same hash
|
||||
//myCache.clear();
|
||||
// uncommented because of reference caching
|
||||
myCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.push;
|
||||
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.GridBag;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import git4idea.repo.GitRemote;
|
||||
import git4idea.repo.GitRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
class GitManualPushToBranch extends JPanel {
|
||||
|
||||
private final Collection<GitRepository> myRepositories;
|
||||
|
||||
private final JCheckBox myManualPush;
|
||||
private final JTextField myDestBranchTextField;
|
||||
private final JBLabel myComment;
|
||||
private final JButton myRefreshButton;
|
||||
private final RemoteSelector myRemoteSelector;
|
||||
private final JComponent myRemoteSelectorComponent;
|
||||
|
||||
GitManualPushToBranch(@NotNull Collection<GitRepository> repositories,
|
||||
@NotNull final Runnable performOnRefresh) {
|
||||
super();
|
||||
myRepositories = repositories;
|
||||
|
||||
myManualPush = new JCheckBox("Push current branch to: ", false);
|
||||
myManualPush.setMnemonic('b');
|
||||
|
||||
myDestBranchTextField = new JTextField(15);
|
||||
|
||||
myComment = new JBLabel("This will apply to all selected repositories", UIUtil.ComponentStyle.SMALL);
|
||||
|
||||
myRefreshButton = new JButton(IconLoader.getIcon("/actions/sync.png"));
|
||||
myRefreshButton.setToolTipText("Refresh commit list");
|
||||
|
||||
myRefreshButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
performOnRefresh.run();
|
||||
}
|
||||
});
|
||||
|
||||
myRemoteSelector = new RemoteSelector(getRemotesWithCommonNames(repositories));
|
||||
myRemoteSelectorComponent = myRemoteSelector.createComponent();
|
||||
|
||||
setDefaultComponentsEnabledState(myManualPush.isSelected());
|
||||
myManualPush.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
boolean isManualPushSelected = myManualPush.isSelected();
|
||||
setDefaultComponentsEnabledState(isManualPushSelected);
|
||||
}
|
||||
});
|
||||
|
||||
layoutComponents();
|
||||
}
|
||||
|
||||
private void setDefaultComponentsEnabledState(boolean selected) {
|
||||
setComponentsEnabledState(selected, myRemoteSelectorComponent, myDestBranchTextField, myRefreshButton, myComment);
|
||||
}
|
||||
|
||||
private void layoutComponents() {
|
||||
JPanel panel = new JPanel();
|
||||
GridBagLayout layout = new GridBagLayout();
|
||||
panel.setLayout(layout);
|
||||
GridBag g = new GridBag()
|
||||
.setDefaultFill(GridBagConstraints.NONE)
|
||||
.setDefaultAnchor(GridBagConstraints.LINE_START)
|
||||
.setDefaultWeightX(1, 1);
|
||||
|
||||
panel.add(myManualPush, g.nextLine().next());
|
||||
panel.add(myRemoteSelectorComponent, g.next());
|
||||
panel.add(myDestBranchTextField, g.next());
|
||||
panel.add(myRefreshButton, g.next());
|
||||
g.nextLine();
|
||||
if (myRepositories.size() > 1) {
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
constraints.gridwidth = GridBagConstraints.REMAINDER;
|
||||
constraints.anchor = GridBagConstraints.LINE_START;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = 1;
|
||||
constraints.insets = new Insets(0, 28, 0, 0);
|
||||
//panel.add(myComment, g.insets(0, 20, 0, 0).coverLine().next());
|
||||
panel.add(myComment, constraints);
|
||||
}
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
add(panel, BorderLayout.WEST);
|
||||
}
|
||||
|
||||
boolean canBeUsed() {
|
||||
return myManualPush.isSelected() && !myDestBranchTextField.getText().isEmpty();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
String getBranchToPush() {
|
||||
return myDestBranchTextField.getText();
|
||||
}
|
||||
|
||||
void setBranchToPushIfNotSet(@NotNull String text) {
|
||||
if (myDestBranchTextField.getText().isEmpty()) {
|
||||
myDestBranchTextField.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
GitRemote getSelectedRemote() {
|
||||
return myRemoteSelector.getSelectedValue();
|
||||
}
|
||||
|
||||
private static void setComponentsEnabledState(boolean enabled, JComponent... components) {
|
||||
for (JComponent component : components) {
|
||||
component.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<GitRemote> getRemotesWithCommonNames(@NotNull Collection<GitRepository> repositories) {
|
||||
if (repositories.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Iterator<GitRepository> iterator = repositories.iterator();
|
||||
List<GitRemote> commonRemotes = new ArrayList<GitRemote>(iterator.next().getRemotes());
|
||||
while (iterator.hasNext()) {
|
||||
GitRepository repository = iterator.next();
|
||||
Collection<String> remoteNames = getRemoteNames(repository);
|
||||
for (Iterator<GitRemote> commonIter = commonRemotes.iterator(); commonIter.hasNext(); ) {
|
||||
GitRemote remote = commonIter.next();
|
||||
if (!remoteNames.contains(remote.getName())) {
|
||||
commonIter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return commonRemotes;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<String> getRemoteNames(@NotNull GitRepository repository) {
|
||||
Collection<String> names = new ArrayList<String>(repository.getRemotes().size());
|
||||
for (GitRemote remote : repository.getRemotes()) {
|
||||
names.add(remote.getName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component to select remotes.
|
||||
* If there is only one remote, JLabel is used instead of JCombobox.
|
||||
*/
|
||||
private static class RemoteSelector {
|
||||
|
||||
private final Collection<GitRemote> myRemotes;
|
||||
private JComboBox myRemoteCombobox;
|
||||
|
||||
private RemoteSelector(@NotNull Collection<GitRemote> remotes) {
|
||||
myRemotes = remotes;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
JComponent createComponent() {
|
||||
//if (myRemotes.size() == 1) {
|
||||
// JBLabel label = new JBLabel(myRemotes.iterator().next().getName());
|
||||
// label.setToolTipText("Remote");
|
||||
// return label;
|
||||
//} else {
|
||||
myRemoteCombobox = new JComboBox();
|
||||
myRemoteCombobox.setRenderer(new RemoteCellRenderer());
|
||||
for (GitRemote remote : myRemotes) {
|
||||
myRemoteCombobox.addItem(remote);
|
||||
}
|
||||
myRemoteCombobox.setToolTipText("Select remote");
|
||||
return myRemoteCombobox;
|
||||
//}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
GitRemote getSelectedValue() {
|
||||
//if (myRemotes.size() == 1) {
|
||||
// return myRemotes.iterator().next();
|
||||
//}
|
||||
return (GitRemote)myRemoteCombobox.getSelectedItem();
|
||||
}
|
||||
|
||||
private static class RemoteCellRenderer implements ListCellRenderer {
|
||||
|
||||
public static final DefaultListCellRenderer DEFAULT_RENDERER = new DefaultListCellRenderer();
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
Component renderer = DEFAULT_RENDERER.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
if (value instanceof GitRemote) {
|
||||
((JLabel) renderer).setText(((GitRemote)value).getName());
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,16 +29,24 @@ import java.util.List;
|
||||
*/
|
||||
final class GitPushBranchInfo {
|
||||
|
||||
private final GitBranch mySourceBranch;
|
||||
private final GitBranch myDestBranch;
|
||||
private final boolean myNewBranch;
|
||||
private final List<GitCommit> myCommits;
|
||||
|
||||
GitPushBranchInfo(@NotNull GitBranch destBranch, @NotNull List<GitCommit> commits) {
|
||||
GitPushBranchInfo(@NotNull GitBranch sourceBranch, @NotNull GitBranch destBranch, @NotNull List<GitCommit> commits, boolean newBranch) {
|
||||
mySourceBranch = sourceBranch;
|
||||
myCommits = commits;
|
||||
myDestBranch = destBranch;
|
||||
myNewBranch = newBranch;
|
||||
}
|
||||
|
||||
GitPushBranchInfo(@NotNull GitPushBranchInfo pushBranchInfo) {
|
||||
this(pushBranchInfo.getDestBranch(), pushBranchInfo.getCommits());
|
||||
this(pushBranchInfo.getSourceBranch(), pushBranchInfo.getDestBranch(), pushBranchInfo.getCommits(), pushBranchInfo.isNewBranchCreated());
|
||||
}
|
||||
|
||||
boolean isNewBranchCreated() {
|
||||
return myNewBranch;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -50,4 +58,9 @@ final class GitPushBranchInfo {
|
||||
List<GitCommit> getCommits() {
|
||||
return new ArrayList<GitCommit>(myCommits);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GitBranch getSourceBranch() {
|
||||
return mySourceBranch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package git4idea.push;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Result of pushing a single branch.
|
||||
*
|
||||
@@ -24,28 +27,35 @@ final class GitPushBranchResult {
|
||||
|
||||
private final Type myType;
|
||||
private final int myNumberOfPushedCommits;
|
||||
private final String myTargetBranchName;
|
||||
|
||||
enum Type {
|
||||
SUCCESS,
|
||||
NEW_BRANCH,
|
||||
REJECTED,
|
||||
ERROR
|
||||
}
|
||||
|
||||
private GitPushBranchResult(Type type, int numberOfPushedCommits) {
|
||||
private GitPushBranchResult(Type type, int numberOfPushedCommits, @Nullable String targetBranchName) {
|
||||
myType = type;
|
||||
myNumberOfPushedCommits = numberOfPushedCommits;
|
||||
myTargetBranchName = targetBranchName;
|
||||
}
|
||||
|
||||
static GitPushBranchResult success(int numberOfPushedCommits) {
|
||||
return new GitPushBranchResult(Type.SUCCESS, numberOfPushedCommits);
|
||||
return new GitPushBranchResult(Type.SUCCESS, numberOfPushedCommits, null);
|
||||
}
|
||||
|
||||
static GitPushBranchResult newBranch(String targetBranchName) {
|
||||
return new GitPushBranchResult(Type.NEW_BRANCH, 0, targetBranchName);
|
||||
}
|
||||
|
||||
static GitPushBranchResult rejected() {
|
||||
return new GitPushBranchResult(Type.REJECTED, 0);
|
||||
return new GitPushBranchResult(Type.REJECTED, 0, null);
|
||||
}
|
||||
|
||||
static GitPushBranchResult error() {
|
||||
return new GitPushBranchResult(Type.ERROR, 0);
|
||||
return new GitPushBranchResult(Type.ERROR, 0, null);
|
||||
}
|
||||
|
||||
int getNumberOfPushedCommits() {
|
||||
@@ -64,4 +74,13 @@ final class GitPushBranchResult {
|
||||
return myType == Type.ERROR;
|
||||
}
|
||||
|
||||
boolean isNewBranch() {
|
||||
return myType == Type.NEW_BRANCH;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
String getTargetBranchName() {
|
||||
return myTargetBranchName != null ? myTargetBranchName : "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public class GitPushDialog extends DialogWrapper {
|
||||
private final JBLoadingPanel myLoadingPanel;
|
||||
private final JCheckBox myPushAllCheckbox;
|
||||
private final Object COMMITS_LOADING_LOCK = new Object();
|
||||
private final GitManualPushToBranch myRefspecPanel;
|
||||
|
||||
public GitPushDialog(@NotNull Project project) {
|
||||
super(project);
|
||||
@@ -73,28 +74,16 @@ public class GitPushDialog extends DialogWrapper {
|
||||
myPushAllCheckbox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
loadCommitsInBackground(myPushAllCheckbox.isSelected());
|
||||
loadCommitsInBackground();
|
||||
}
|
||||
});
|
||||
/* hidden: it may confuse users, the target is not clear, hidden until really needed,
|
||||
not removed completely because it is default behavior for 'git push' in command line. */
|
||||
myPushAllCheckbox.setVisible(false);
|
||||
|
||||
myListPanel = new GitPushLog(myProject, myRepositories, new Consumer<Boolean>() {
|
||||
@Override public void consume(Boolean checked) {
|
||||
if (checked) {
|
||||
setOKActionEnabled(true);
|
||||
} else {
|
||||
Collection<GitRepository> repositories = myListPanel.getSelectedRepositories();
|
||||
if (repositories.isEmpty()) {
|
||||
setOKActionEnabled(false);
|
||||
} else {
|
||||
setOKActionEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
myListPanel = new GitPushLog(myProject, myRepositories, new RepositoryCheckboxListener());
|
||||
myRefspecPanel = new GitManualPushToBranch(myRepositories, new RefreshButtonListener());
|
||||
|
||||
init();
|
||||
setOKButtonText("Push");
|
||||
setTitle("Git Push");
|
||||
@@ -103,31 +92,33 @@ public class GitPushDialog extends DialogWrapper {
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
JPanel optionsPanel = new JPanel(new BorderLayout());
|
||||
optionsPanel.add(myPushAllCheckbox);
|
||||
optionsPanel.add(myPushAllCheckbox, BorderLayout.NORTH);
|
||||
optionsPanel.add(myRefspecPanel);
|
||||
|
||||
myRootPanel = new JPanel(new BorderLayout());
|
||||
myRootPanel = new JPanel(new BorderLayout(0, 15));
|
||||
myRootPanel.add(createCommitListPanel(), BorderLayout.CENTER);
|
||||
myRootPanel.add(optionsPanel, BorderLayout.SOUTH);
|
||||
return myRootPanel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private JComponent createCommitListPanel() {
|
||||
myLoadingPanel.add(myListPanel, BorderLayout.CENTER);
|
||||
loadCommitsInBackground(false);
|
||||
loadCommitsInBackground();
|
||||
|
||||
JPanel commitListPanel = new JPanel(new BorderLayout());
|
||||
commitListPanel.add(myLoadingPanel, BorderLayout.CENTER);
|
||||
return commitListPanel;
|
||||
}
|
||||
|
||||
private void loadCommitsInBackground(final boolean pushAll) {
|
||||
private void loadCommitsInBackground() {
|
||||
myLoadingPanel.startLoading();
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
|
||||
public void run() {
|
||||
final AtomicReference<String> error = new AtomicReference<String>();
|
||||
synchronized (COMMITS_LOADING_LOCK) {
|
||||
error.set(collectInfoToPush(pushAll));
|
||||
error.set(collectInfoToPush());
|
||||
}
|
||||
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@@ -138,17 +129,50 @@ public class GitPushDialog extends DialogWrapper {
|
||||
} else {
|
||||
myListPanel.setCommits(myGitCommitsToPush);
|
||||
}
|
||||
myRefspecPanel.setBranchToPushIfNotSet(getTrackedCurrentBranchName());
|
||||
myLoadingPanel.stopLoading();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private String getTrackedCurrentBranchName() {
|
||||
if (myGitCommitsToPush != null) {
|
||||
Collection<GitRepository> repositories = myGitCommitsToPush.getRepositories();
|
||||
if (!repositories.isEmpty()) {
|
||||
GitRepository repository = repositories.iterator().next();
|
||||
GitBranch currentBranch = repository.getCurrentBranch();
|
||||
assert currentBranch != null;
|
||||
return getNameWithoutRemote(myGitCommitsToPush.get(repository).get(currentBranch).getDestBranch());
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getNameWithoutRemote(@NotNull GitBranch remoteBranch) {
|
||||
String remoteName = myRefspecPanel.getSelectedRemote().getName() + "/";
|
||||
String branchName = remoteBranch.getName();
|
||||
if (branchName.startsWith(remoteName)) {
|
||||
return branchName.substring(remoteName.length());
|
||||
}
|
||||
else {
|
||||
// we are taking the current branch of the first repository
|
||||
// it is possible (though unlikely), that this branch has other remote than the common remote selected in the refspec panel
|
||||
// then we return the full branch name.
|
||||
// the push won't work absolutely correct, if the remote doesn't have this branch, but it is not our problem in the case of
|
||||
// several repositories with different remotes sets and different branches.
|
||||
return remoteBranch.getFullName();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String collectInfoToPush(boolean pushAll) {
|
||||
private String collectInfoToPush() {
|
||||
try {
|
||||
myPushSpecs = pushAll ? pushSpecsForPushAll() : pushSpecsForCurrentBranches();
|
||||
boolean pushAll = myPushAllCheckbox.isSelected();
|
||||
myPushSpecs = pushAll ? pushSpecsForPushAll() : pushSpecsForCurrentOrEnteredBranches();
|
||||
myGitCommitsToPush = myPusher.collectCommitsToPush(myPushSpecs);
|
||||
return null;
|
||||
}
|
||||
@@ -159,7 +183,7 @@ public class GitPushDialog extends DialogWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<GitRepository, GitPushSpec> pushSpecsForCurrentBranches() throws VcsException {
|
||||
private Map<GitRepository, GitPushSpec> pushSpecsForCurrentOrEnteredBranches() throws VcsException {
|
||||
Map<GitRepository, GitPushSpec> defaultSpecs = new HashMap<GitRepository, GitPushSpec>();
|
||||
for (GitRepository repository : myRepositories) {
|
||||
GitBranch currentBranch = repository.getCurrentBranch();
|
||||
@@ -178,6 +202,19 @@ public class GitPushDialog extends DialogWrapper {
|
||||
remote = remoteAndBranch.getFirst();
|
||||
tracked = remoteAndBranch.getSecond();
|
||||
}
|
||||
|
||||
if (myRefspecPanel.canBeUsed()) {
|
||||
String manualBranchName = myRefspecPanel.getBranchToPush();
|
||||
GitBranch manualBranch = findRemoteBranchByName(repository, remote, manualBranchName);
|
||||
if (manualBranch == null) {
|
||||
if (!manualBranchName.startsWith("refs/remotes/")) {
|
||||
manualBranchName = myRefspecPanel.getSelectedRemote().getName() + "/" + manualBranchName;
|
||||
}
|
||||
manualBranch = new GitBranch(manualBranchName, false, true);
|
||||
}
|
||||
tracked = manualBranch;
|
||||
}
|
||||
|
||||
GitPushSpec pushSpec = new GitPushSpec(remote, currentBranch, tracked);
|
||||
defaultSpecs.put(repository, pushSpec);
|
||||
}
|
||||
@@ -227,7 +264,7 @@ public class GitPushDialog extends DialogWrapper {
|
||||
synchronized (COMMITS_LOADING_LOCK) {
|
||||
GitCommitsByRepoAndBranch selectedCommits;
|
||||
if (myGitCommitsToPush == null) {
|
||||
collectInfoToPush(myPushAllCheckbox.isSelected());
|
||||
collectInfoToPush();
|
||||
selectedCommits = myGitCommitsToPush;
|
||||
} else {
|
||||
Collection<GitRepository> selectedRepositories = myListPanel.getSelectedRepositories();
|
||||
@@ -236,4 +273,27 @@ public class GitPushDialog extends DialogWrapper {
|
||||
return new GitPushInfo(selectedCommits, myPushSpecs);
|
||||
}
|
||||
}
|
||||
|
||||
private class RepositoryCheckboxListener implements Consumer<Boolean> {
|
||||
@Override public void consume(Boolean checked) {
|
||||
if (checked) {
|
||||
setOKActionEnabled(true);
|
||||
} else {
|
||||
Collection<GitRepository> repositories = myListPanel.getSelectedRepositories();
|
||||
if (repositories.isEmpty()) {
|
||||
setOKActionEnabled(false);
|
||||
} else {
|
||||
setOKActionEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RefreshButtonListener implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
loadCommitsInBackground();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import git4idea.GitBranch;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.branch.GitBranchPair;
|
||||
import git4idea.history.browser.GitCommit;
|
||||
import git4idea.repo.GitRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -208,7 +207,7 @@ class GitPushLog extends JPanel implements TypeSafeDataProvider {
|
||||
}
|
||||
|
||||
private static DefaultMutableTreeNode createBranchNode(@NotNull GitBranch branch, @NotNull GitPushBranchInfo branchInfo) {
|
||||
DefaultMutableTreeNode branchNode = new DefaultMutableTreeNode(new GitBranchPair(branch, branchInfo.getDestBranch()));
|
||||
DefaultMutableTreeNode branchNode = new DefaultMutableTreeNode(branchInfo);
|
||||
for (GitCommit commit : branchInfo.getCommits()) {
|
||||
branchNode.add(new DefaultMutableTreeNode(commit));
|
||||
}
|
||||
@@ -290,10 +289,10 @@ class GitPushLog extends JPanel implements TypeSafeDataProvider {
|
||||
Font font = EditorColorsManager.getInstance().getGlobalScheme().getFont(EditorFontType.PLAIN); // using probable monospace font to emulate table
|
||||
renderer.setFont(font);
|
||||
|
||||
SimpleTextAttributes smallGrey = new SimpleTextAttributes(SimpleTextAttributes.STYLE_SMALLER, UIUtil.getInactiveTextColor());
|
||||
if (userObject instanceof GitCommit) {
|
||||
GitCommit commit = (GitCommit)userObject;
|
||||
SimpleTextAttributes small = new SimpleTextAttributes(SimpleTextAttributes.STYLE_SMALLER, renderer.getForeground());
|
||||
SimpleTextAttributes smallGrey = new SimpleTextAttributes(SimpleTextAttributes.STYLE_SMALLER, UIUtil.getInactiveTextColor());
|
||||
renderer.append(commit.getShortHash().toString(), smallGrey);
|
||||
renderer.append(String.format(" %" + myDateMaxWidth + "s ", getDateString(commit)), smallGrey);
|
||||
renderer.append(commit.getSubject(), small);
|
||||
@@ -302,13 +301,20 @@ class GitPushLog extends JPanel implements TypeSafeDataProvider {
|
||||
String repositoryPath = calcRootPath((GitRepository)userObject);
|
||||
renderer.append(repositoryPath, SimpleTextAttributes.GRAY_ATTRIBUTES);
|
||||
}
|
||||
else if (userObject instanceof GitBranchPair) {
|
||||
GitBranchPair branchPair = (GitBranchPair) userObject;
|
||||
GitBranch fromBranch = branchPair.getBranch();
|
||||
GitBranch dest = branchPair.getDest();
|
||||
assert dest != null : "Destination branch can't be null for branch " + fromBranch;
|
||||
else if (userObject instanceof GitPushBranchInfo) {
|
||||
GitPushBranchInfo branchInfo = (GitPushBranchInfo) userObject;
|
||||
GitBranch fromBranch = branchInfo.getSourceBranch();
|
||||
GitBranch dest = branchInfo.getDestBranch();
|
||||
|
||||
renderer.append(fromBranch.getName() + " -> " + dest.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
|
||||
String text = fromBranch.getName() + " -> ";
|
||||
if (branchInfo.isNewBranchCreated()) {
|
||||
text += "+";
|
||||
}
|
||||
text += dest.getName();
|
||||
renderer.append(text, branchInfo.isNewBranchCreated() ? SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES);
|
||||
if (branchInfo.isNewBranchCreated()) {
|
||||
renderer.append(" new branch will be created, showing last 10 commits on the current branch", smallGrey);
|
||||
}
|
||||
}
|
||||
else if (userObject instanceof FakeCommit) {
|
||||
int spaces = 6 + 15 + 3 + 30;
|
||||
|
||||
@@ -132,7 +132,7 @@ final class GitPushRepoResult {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
String getPerBranchesReport() {
|
||||
String getPerBranchesNonErrorReport() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int i = 0;
|
||||
for (Map.Entry<GitBranch, GitPushBranchResult> entry : myBranchResults.entrySet()) {
|
||||
@@ -141,7 +141,11 @@ final class GitPushRepoResult {
|
||||
|
||||
if (branchResult.isSuccess()) {
|
||||
sb.append(bold(branch.getName()) + ": pushed " + commits(branchResult.getNumberOfPushedCommits()));
|
||||
} else {
|
||||
}
|
||||
else if (branchResult.isNewBranch()) {
|
||||
sb.append(bold(branch.getName()) + " pushed to new branch " + bold(branchResult.getTargetBranchName()));
|
||||
}
|
||||
else {
|
||||
sb.append(code(branch.getName())).append(": rejected");
|
||||
}
|
||||
|
||||
|
||||
@@ -225,8 +225,12 @@ class GitPushResult {
|
||||
}
|
||||
notificationType = NotificationType.WARNING;
|
||||
} else {
|
||||
title = "Pushed " + pushedCommitsNumber + " " + StringUtil.pluralize("commit", pushedCommitsNumber);
|
||||
notificationType = NotificationType.INFORMATION;
|
||||
if (pushedCommitsNumber == 0) { // happens on new branch creation
|
||||
title = "Pushed successfully";
|
||||
} else {
|
||||
title = "Pushed " + pushedCommitsNumber + " " + StringUtil.pluralize("commit", pushedCommitsNumber);
|
||||
}
|
||||
}
|
||||
|
||||
String errorReport = reportForGroup(groupedResult.myErrorResults, GroupedResult.Type.ERROR);
|
||||
@@ -291,7 +295,7 @@ class GitPushResult {
|
||||
sb.append("<code>" + repository.getPresentableUrl() + "</code>:<br/>");
|
||||
}
|
||||
if (resultType == GroupedResult.Type.SUCCESS || resultType == GroupedResult.Type.REJECT) {
|
||||
sb.append(result.getPerBranchesReport());
|
||||
sb.append(result.getPerBranchesNonErrorReport());
|
||||
} else {
|
||||
sb.append(result.getOutput());
|
||||
}
|
||||
|
||||
@@ -168,16 +168,28 @@ public final class GitPusher {
|
||||
GitBranch dest = sourceDest.getDest();
|
||||
assert dest != null : "Destination branch can't be null here for branch " + source;
|
||||
|
||||
List<GitCommit> commits = collectCommitsToPush(repository, source.getName(), dest.getName());
|
||||
List<GitCommit> commits;
|
||||
boolean newBranch;
|
||||
if (GitUtil.repoContainsRemoteBranch(repository, dest)) {
|
||||
commits = collectCommitsToPush(repository, source.getName(), dest.getName());
|
||||
newBranch = false;
|
||||
}
|
||||
else {
|
||||
commits = collectRecentCommitsOnBranch(repository, source);
|
||||
newBranch = true;
|
||||
}
|
||||
if (!commits.isEmpty()) {
|
||||
commitsByBranch.put(source, new GitPushBranchInfo(dest, commits));
|
||||
commitsByBranch.put(source, new GitPushBranchInfo(source, dest, commits, newBranch));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new GitCommitsByBranch(commitsByBranch);
|
||||
}
|
||||
|
||||
private List<GitCommit> collectRecentCommitsOnBranch(GitRepository repository, GitBranch source) throws VcsException {
|
||||
return GitHistoryUtils.history(myProject, repository.getRoot(), "--max-count=10", source.getName());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<GitCommit> collectCommitsToPush(@NotNull GitRepository repository, @NotNull String source, @NotNull String destination)
|
||||
throws VcsException {
|
||||
@@ -350,7 +362,11 @@ public final class GitPusher {
|
||||
|
||||
@NotNull
|
||||
private static GitPushBranchResult successfulResultForBranch(@NotNull GitCommitsByBranch commitsByBranch, @NotNull GitBranch branch) {
|
||||
return GitPushBranchResult.success(commitsByBranch.get(branch).getCommits().size());
|
||||
GitPushBranchInfo branchInfo = commitsByBranch.get(branch);
|
||||
if (branchInfo.isNewBranchCreated()) {
|
||||
return GitPushBranchResult.newBranch(branchInfo.getDestBranch().getName());
|
||||
}
|
||||
return GitPushBranchResult.success(branchInfo.getCommits().size());
|
||||
}
|
||||
|
||||
private static boolean branchInRejected(@NotNull GitBranch branch, @NotNull Collection<String> rejectedBranches) {
|
||||
|
||||
+31
-2
@@ -17,29 +17,36 @@ package com.intellij.codeInsight.editorActions;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.psi.xml.XmlTokenType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EnterBetweenXmlTagsHandler extends EnterHandlerDelegateAdapter {
|
||||
public Result preprocessEnter(@NotNull final PsiFile file, @NotNull final Editor editor, @NotNull final Ref<Integer> caretOffset, @NotNull final Ref<Integer> caretAdvance,
|
||||
@NotNull final DataContext dataContext, final EditorActionHandler originalHandler) {
|
||||
if (file instanceof XmlFile && isBetweenXmlTags(editor, caretOffset.get().intValue())) {
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(dataContext);
|
||||
|
||||
if (file instanceof XmlFile && isBetweenXmlTags(project, editor, file, caretOffset.get().intValue())) {
|
||||
originalHandler.execute(editor, dataContext);
|
||||
return Result.DefaultForceIndent;
|
||||
}
|
||||
return Result.Continue;
|
||||
}
|
||||
|
||||
private static boolean isBetweenXmlTags(Editor editor, int offset) {
|
||||
private static boolean isBetweenXmlTags(Project project, Editor editor, PsiFile file, int offset) {
|
||||
if (offset == 0) return false;
|
||||
CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
if (chars.charAt(offset - 1) != '>') return false;
|
||||
@@ -47,6 +54,11 @@ public class EnterBetweenXmlTagsHandler extends EnterHandlerDelegateAdapter {
|
||||
EditorHighlighter highlighter = ((EditorEx)editor).getHighlighter();
|
||||
HighlighterIterator iterator = highlighter.createIterator(offset - 1);
|
||||
if (iterator.getTokenType() != XmlTokenType.XML_TAG_END) return false;
|
||||
|
||||
if (isAtTheEndOfEmptyTag(project, editor, file, iterator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
iterator.retreat();
|
||||
|
||||
int retrieveCount = 1;
|
||||
@@ -62,4 +74,21 @@ public class EnterBetweenXmlTagsHandler extends EnterHandlerDelegateAdapter {
|
||||
iterator.advance();
|
||||
return !iterator.atEnd() && iterator.getTokenType() == XmlTokenType.XML_END_TAG_START;
|
||||
}
|
||||
|
||||
private static boolean isAtTheEndOfEmptyTag(Project project, Editor editor, PsiFile file, HighlighterIterator iterator) {
|
||||
if (iterator.getTokenType() != XmlTokenType.XML_TAG_END) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
|
||||
final PsiElement element = file.findElementAt(iterator.getStart());
|
||||
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PsiElement parent = element.getParent();
|
||||
return parent instanceof XmlTag &&
|
||||
parent.getTextRange().getEndOffset() == iterator.getEnd();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user