mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
git4idea: Added possiblitity to selectively push commits in push active branches dialog
This commit is contained in:
@@ -15,11 +15,9 @@
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="c6c81" class="javax.swing.JTree" binding="myCommitTree">
|
||||
<component id="56124" class="com.intellij.ui.CheckboxTree" binding="myCommitTree" custom-create="true">
|
||||
<constraints/>
|
||||
<properties>
|
||||
<rootVisible value="false"/>
|
||||
</properties>
|
||||
<properties/>
|
||||
</component>
|
||||
</children>
|
||||
</scrollpane>
|
||||
@@ -32,15 +30,58 @@
|
||||
<text resource-bundle="git4idea/i18n/GitBundle" key="push.active.commits"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="3f20a" class="javax.swing.JButton" binding="myViewButton" default-binding="true">
|
||||
<grid id="a16e5" layout-manager="GridLayoutManager" row-count="5" 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="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<enabled value="false"/>
|
||||
<text resource-bundle="git4idea/i18n/GitBundle" key="push.active.view"/>
|
||||
</properties>
|
||||
</component>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="3f20a" class="javax.swing.JButton" binding="myViewButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<enabled value="false"/>
|
||||
<text resource-bundle="git4idea/i18n/GitBundle" key="push.active.view"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="2dbc5" class="javax.swing.JButton" binding="myFetchButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="2" column="0" 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 resource-bundle="git4idea/i18n/GitBundle" key="push.active.fetch"/>
|
||||
<toolTipText resource-bundle="git4idea/i18n/GitBundle" key="push.active.fetch.tooltip"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="5c023" class="javax.swing.JButton" binding="myRebaseButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="3" column="0" 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 resource-bundle="git4idea/i18n/GitBundle" key="push.active.rebase"/>
|
||||
<toolTipText resource-bundle="git4idea/i18n/GitBundle" key="push.active.rebase.tooltip"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="8ca38" class="javax.swing.JCheckBox" binding="myAutoStashCheckBox" default-binding="true">
|
||||
<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"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<selected value="true"/>
|
||||
<text resource-bundle="git4idea/i18n/GitBundle" key="push.active.autostash"/>
|
||||
<toolTipText resource-bundle="git4idea/i18n/GitBundle" key="push.active.autostash.tooltip"/>
|
||||
</properties>
|
||||
</component>
|
||||
<vspacer id="90dc0">
|
||||
<constraints>
|
||||
<grid row="1" 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>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
|
||||
@@ -21,26 +21,28 @@ import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.CheckboxTree;
|
||||
import com.intellij.ui.CheckedTreeNode;
|
||||
import com.intellij.ui.ColoredTreeCellRenderer;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import git4idea.GitBranch;
|
||||
import git4idea.GitRevisionNumber;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.actions.GitShowAllSubmittedFilesAction;
|
||||
import git4idea.commands.*;
|
||||
import git4idea.i18n.GitBundle;
|
||||
import git4idea.ui.GitUIUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The dialog that allows pushing active branches.
|
||||
@@ -59,37 +61,47 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
*/
|
||||
private JPanel myPanel;
|
||||
/**
|
||||
* The commit tree control
|
||||
* Fetch changes from remote repository
|
||||
*/
|
||||
private JTree myCommitTree;
|
||||
private JButton myFetchButton;
|
||||
/**
|
||||
* The root information structure
|
||||
* Rebase commits to new roots
|
||||
*/
|
||||
private List<Root> myRoots;
|
||||
private JButton myRebaseButton;
|
||||
/**
|
||||
* If selected, the changes are auto-stashed before rebase
|
||||
*/
|
||||
private JCheckBox myAutoStashCheckBox;
|
||||
/**
|
||||
* The commit tree (sorted by vcs roots)
|
||||
*/
|
||||
private CheckboxTree myCommitTree;
|
||||
/**
|
||||
* The root node
|
||||
*/
|
||||
private CheckedTreeNode myTreeRoot;
|
||||
/**
|
||||
* The context project
|
||||
*/
|
||||
private Project myProject;
|
||||
/**
|
||||
* The vcs roots for the project
|
||||
*/
|
||||
private List<VirtualFile> myVcsRoots;
|
||||
|
||||
/**
|
||||
* The constructor
|
||||
*
|
||||
* @param project the project
|
||||
* @param roots the loaded roots
|
||||
* @param project the project
|
||||
* @param vcsRoots the vcs roots
|
||||
* @param roots the loaded information about roots
|
||||
*/
|
||||
private GitPushActiveBranchesDialog(final Project project, List<Root> roots) {
|
||||
private GitPushActiveBranchesDialog(final Project project, List<VirtualFile> vcsRoots, List<Root> roots) {
|
||||
super(project, true);
|
||||
myRoots = roots;
|
||||
myCommitTree.setModel(new DefaultTreeModel(createTree()));
|
||||
myProject = project;
|
||||
myVcsRoots = vcsRoots;
|
||||
updateTree(roots, null);
|
||||
TreeUtil.expandAll(myCommitTree);
|
||||
for (Root r : roots) {
|
||||
if (r.branch == null) {
|
||||
setErrorText(GitBundle.getString("push.active.error.no.branch"));
|
||||
setOKActionEnabled(false);
|
||||
break;
|
||||
}
|
||||
if (r.remoteCommits != 0 && r.commits.size() != 0) {
|
||||
setErrorText(GitBundle.getString("push.active.error.behind"));
|
||||
setOKActionEnabled(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
myCommitTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
|
||||
public void valueChanged(TreeSelectionEvent e) {
|
||||
TreePath path = myCommitTree.getSelectionModel().getSelectionPath();
|
||||
@@ -115,29 +127,267 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
GitShowAllSubmittedFilesAction.showSubmittedFiles(project, c.revision.asString(), c.root.root);
|
||||
}
|
||||
});
|
||||
myFetchButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
doFetch();
|
||||
}
|
||||
});
|
||||
myRebaseButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
doRebase();
|
||||
}
|
||||
});
|
||||
setTitle(GitBundle.getString("push.active.title"));
|
||||
setOKButtonText(GitBundle.getString("push.active.button"));
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the created tree
|
||||
* Perform fetch operation
|
||||
*/
|
||||
private TreeNode createTree() {
|
||||
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("ROOT", true);
|
||||
for (Root r : myRoots) {
|
||||
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(r, true);
|
||||
private void doFetch() {
|
||||
Map<VirtualFile, Set<String>> unchecked = new HashMap<VirtualFile, Set<String>>();
|
||||
for (int i = 0; i < myTreeRoot.getChildCount(); i++) {
|
||||
Set<String> uncheckedCommits = new HashSet<String>();
|
||||
CheckedTreeNode node = (CheckedTreeNode)myTreeRoot.getChildAt(i);
|
||||
Root r = (Root)node.getUserObject();
|
||||
for (int j = 0; j < node.getChildCount(); j++) {
|
||||
if (node.getChildAt(j) instanceof CheckedTreeNode) {
|
||||
CheckedTreeNode commitNode = (CheckedTreeNode)node.getChildAt(j);
|
||||
if (!commitNode.isChecked()) {
|
||||
uncheckedCommits.add(((Commit)commitNode.getUserObject()).commitId());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!uncheckedCommits.isEmpty()) {
|
||||
unchecked.put(r.root, uncheckedCommits);
|
||||
}
|
||||
}
|
||||
refreshTree(true, unchecked);
|
||||
}
|
||||
|
||||
/**
|
||||
* The rebase operation is needed if the current branch is behind remote branch or if some commit is not selected.
|
||||
*
|
||||
* @return true if rebase is needed for at least one vcs root
|
||||
*/
|
||||
private boolean isRebaseNeeded() {
|
||||
for (int i = 0; i < myTreeRoot.getChildCount(); i++) {
|
||||
CheckedTreeNode node = (CheckedTreeNode)myTreeRoot.getChildAt(i);
|
||||
Root r = (Root)node.getUserObject();
|
||||
if (r.commits.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (r.remoteCommits > 0) {
|
||||
return true;
|
||||
}
|
||||
boolean seenCheckedNode = false;
|
||||
for (int j = 0; j < node.getChildCount(); j++) {
|
||||
if (node.getChildAt(j) instanceof CheckedTreeNode) {
|
||||
CheckedTreeNode commitNode = (CheckedTreeNode)node.getChildAt(j);
|
||||
if (commitNode.isChecked()) {
|
||||
seenCheckedNode = true;
|
||||
}
|
||||
else {
|
||||
if (seenCheckedNode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preform rebase operation
|
||||
*/
|
||||
private void doRebase() {
|
||||
final Set<VirtualFile> roots = new HashSet<VirtualFile>();
|
||||
final Set<VirtualFile> rootsWithMerges = new HashSet<VirtualFile>();
|
||||
final Map<VirtualFile, List<String>> reorderedCommits = new HashMap<VirtualFile, List<String>>();
|
||||
final Map<VirtualFile, Set<String>> uncheckedCommits = new HashMap<VirtualFile, Set<String>>();
|
||||
for (int i = 0; i < myTreeRoot.getChildCount(); i++) {
|
||||
CheckedTreeNode node = (CheckedTreeNode)myTreeRoot.getChildAt(i);
|
||||
Root r = (Root)node.getUserObject();
|
||||
Set<String> unchecked = new HashSet<String>();
|
||||
uncheckedCommits.put(r.root, unchecked);
|
||||
if (r.commits.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
boolean seenCheckedNode = false;
|
||||
boolean reorderNeeded = false;
|
||||
boolean seenMerges = false;
|
||||
for (int j = 0; j < node.getChildCount(); j++) {
|
||||
if (node.getChildAt(j) instanceof CheckedTreeNode) {
|
||||
CheckedTreeNode commitNode = (CheckedTreeNode)node.getChildAt(j);
|
||||
Commit commit = (Commit)commitNode.getUserObject();
|
||||
seenMerges |= commit.isMerge;
|
||||
if (commitNode.isChecked()) {
|
||||
seenCheckedNode = true;
|
||||
}
|
||||
else {
|
||||
unchecked.add(commit.commitId());
|
||||
if (seenCheckedNode) {
|
||||
reorderNeeded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (seenMerges) {
|
||||
rootsWithMerges.add(r.root);
|
||||
}
|
||||
if (r.remoteCommits > 0 && seenCheckedNode || reorderNeeded) {
|
||||
roots.add(r.root);
|
||||
}
|
||||
if (reorderNeeded) {
|
||||
List<String> reordered = new ArrayList<String>();
|
||||
for (int j = 0; j < node.getChildCount(); j++) {
|
||||
if (node.getChildAt(j) instanceof CheckedTreeNode) {
|
||||
CheckedTreeNode commitNode = (CheckedTreeNode)node.getChildAt(j);
|
||||
if (!commitNode.isChecked()) {
|
||||
Commit commit = (Commit)commitNode.getUserObject();
|
||||
reordered.add(commit.revision.asString());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < node.getChildCount(); j++) {
|
||||
if (node.getChildAt(j) instanceof CheckedTreeNode) {
|
||||
CheckedTreeNode commitNode = (CheckedTreeNode)node.getChildAt(j);
|
||||
if (commitNode.isChecked()) {
|
||||
Commit commit = (Commit)commitNode.getUserObject();
|
||||
reordered.add(commit.revision.asString());
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.reverse(reordered);
|
||||
reorderedCommits.put(r.root, reordered);
|
||||
}
|
||||
}
|
||||
final List<VcsException> exceptions = new ArrayList<VcsException>();
|
||||
final boolean autoStash = myAutoStashCheckBox.isSelected();
|
||||
final ProgressManager progressManager = ProgressManager.getInstance();
|
||||
final GitVcs vcs = GitVcs.getInstance(myProject);
|
||||
progressManager.runProcessWithProgressSynchronously(new Runnable() {
|
||||
public void run() {
|
||||
GitPushRebaseProcess process = new GitPushRebaseProcess(vcs, myProject, exceptions, autoStash, reorderedCommits, rootsWithMerges);
|
||||
process.doUpdate(progressManager.getProgressIndicator(), roots);
|
||||
}
|
||||
}, GitBundle.getString("push.active.rebasing"), false, myProject);
|
||||
refreshTree(false, uncheckedCommits);
|
||||
if (!exceptions.isEmpty()) {
|
||||
GitUIUtil.showOperationErrors(myProject, exceptions, "git rebase");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh tree
|
||||
*
|
||||
* @param fetchData if true, the current state is fetched from remote
|
||||
* @param unchecked the map from vcs root to commit identifiers that should be unchecked
|
||||
*/
|
||||
private void refreshTree(final boolean fetchData, Map<VirtualFile, Set<String>> unchecked) {
|
||||
ArrayList<VcsException> exceptions = new ArrayList<VcsException>();
|
||||
List<Root> roots = loadRoots(myProject, myVcsRoots, exceptions, fetchData);
|
||||
if (!exceptions.isEmpty()) {
|
||||
//noinspection ThrowableResultOfMethodCallIgnored
|
||||
GitUIUtil.showOperationErrors(myProject, exceptions, "Refreshing root information");
|
||||
return;
|
||||
}
|
||||
updateTree(roots, unchecked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tree according to the list of loaded roots
|
||||
*
|
||||
* @param roots the list of roots to add to the tree
|
||||
* @param uncheckedCommits the map from vcs root to commit identifiers that should be uncheckedCommits
|
||||
*/
|
||||
private void updateTree(List<Root> roots, Map<VirtualFile, Set<String>> uncheckedCommits) {
|
||||
myTreeRoot.removeAllChildren();
|
||||
for (Root r : roots) {
|
||||
CheckedTreeNode rootNode = new CheckedTreeNode(r);
|
||||
Status status = new Status();
|
||||
status.root = r;
|
||||
rootNode.add(new DefaultMutableTreeNode(status, false));
|
||||
Set<String> unchecked =
|
||||
uncheckedCommits != null && uncheckedCommits.containsKey(r.root) ? uncheckedCommits.get(r.root) : Collections.<String>emptySet();
|
||||
for (Commit c : r.commits) {
|
||||
rootNode.add(new DefaultMutableTreeNode(c, false));
|
||||
CheckedTreeNode child = new CheckedTreeNode(c);
|
||||
rootNode.add(child);
|
||||
child.setChecked(r.remote != null && !unchecked.contains(c.commitId()));
|
||||
}
|
||||
treeRoot.add(rootNode);
|
||||
myTreeRoot.add(rootNode);
|
||||
}
|
||||
return treeRoot;
|
||||
((DefaultTreeModel)myCommitTree.getModel()).reload(myTreeRoot);
|
||||
TreeUtil.expandAll(myCommitTree);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update buttons on the form
|
||||
*/
|
||||
private void updateButtons() {
|
||||
String error = null;
|
||||
boolean wasCheckedNode = false;
|
||||
boolean reorderMerges = false;
|
||||
for (int i = 0; i < myTreeRoot.getChildCount(); i++) {
|
||||
CheckedTreeNode node = (CheckedTreeNode)myTreeRoot.getChildAt(i);
|
||||
boolean seenCheckedNode = false;
|
||||
boolean reorderNeeded = false;
|
||||
boolean seenMerges = false;
|
||||
boolean seenUnchecked = false;
|
||||
for (int j = 0; j < node.getChildCount(); j++) {
|
||||
if (node.getChildAt(j) instanceof CheckedTreeNode) {
|
||||
CheckedTreeNode commitNode = (CheckedTreeNode)node.getChildAt(j);
|
||||
Commit commit = (Commit)commitNode.getUserObject();
|
||||
seenMerges |= commit.isMerge;
|
||||
if (commitNode.isChecked()) {
|
||||
seenCheckedNode = true;
|
||||
}
|
||||
else {
|
||||
seenUnchecked = true;
|
||||
if (seenCheckedNode) {
|
||||
reorderNeeded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!seenCheckedNode) {
|
||||
continue;
|
||||
}
|
||||
Root r = (Root)node.getUserObject();
|
||||
if( seenMerges && seenUnchecked) {
|
||||
error = GitBundle.getString("push.active.error.merges.unchecked");
|
||||
}
|
||||
if (seenMerges && reorderNeeded) {
|
||||
reorderMerges = true;
|
||||
error = GitBundle.getString("push.active.error.reorder.merges");
|
||||
}
|
||||
if (reorderNeeded) {
|
||||
if (error == null) {
|
||||
error = GitBundle.getString("push.active.error.reorder.needed");
|
||||
}
|
||||
}
|
||||
if (r.branch == null) {
|
||||
if (error == null) {
|
||||
error = GitBundle.getString("push.active.error.no.branch");
|
||||
}
|
||||
break;
|
||||
}
|
||||
wasCheckedNode |= r.remoteBranch != null;
|
||||
if (r.remoteCommits != 0 && r.commits.size() != 0) {
|
||||
if (error == null) {
|
||||
error = GitBundle.getString("push.active.error.behind");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
boolean rebaseNeeded = isRebaseNeeded();
|
||||
setOKActionEnabled(wasCheckedNode && error == null && !rebaseNeeded);
|
||||
setErrorText(error);
|
||||
myRebaseButton.setEnabled(rebaseNeeded && !reorderMerges);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@@ -158,11 +408,16 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
/**
|
||||
* Load VCS roots
|
||||
*
|
||||
* @param project the project
|
||||
* @param roots the VCS root list
|
||||
* @param project the project
|
||||
* @param roots the VCS root list
|
||||
* @param exceptions the list of of exceptions to use
|
||||
* @param fetchData if true, the data for remote is fetched.
|
||||
* @return the loaded information about vcs roots
|
||||
*/
|
||||
static List<Root> loadRoots(final Project project, final List<VirtualFile> roots, final Collection<VcsException> exceptions) {
|
||||
static List<Root> loadRoots(final Project project,
|
||||
final List<VirtualFile> roots,
|
||||
final Collection<VcsException> exceptions,
|
||||
final boolean fetchData) {
|
||||
final ProgressManager manager = ProgressManager.getInstance();
|
||||
final ArrayList<Root> rc = new ArrayList<Root>();
|
||||
manager.runProcessWithProgressSynchronously(new Runnable() {
|
||||
@@ -178,7 +433,7 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
r.remote = b.getTrackedRemoteName(project, root);
|
||||
r.remoteBranch = b.getTrackedBranchName(project, root);
|
||||
if (r.remote != null) {
|
||||
if(!r.remote.equals(".")) {
|
||||
if (fetchData && !r.remote.equals(".")) {
|
||||
GitLineHandler fetch = new GitLineHandler(project, root, GitHandler.FETCH);
|
||||
fetch.addParameters(r.remote, "-v");
|
||||
Collection<VcsException> exs = GitHandlerUtil.doSynchronouslyWithExceptions(fetch);
|
||||
@@ -197,7 +452,7 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
}
|
||||
}
|
||||
GitSimpleHandler toPush = new GitSimpleHandler(project, root, GitHandler.LOG);
|
||||
toPush.addParameters("--pretty=format:%H%x20%ct%x20%s", tracked.getFullName() + ".." + r.branch);
|
||||
toPush.addParameters("--pretty=format:%H%x20%ct%x20%at%x20%s%n%P", tracked.getFullName() + ".." + r.branch);
|
||||
toPush.setNoSSH(true);
|
||||
toPush.setStdoutSuppressed(true);
|
||||
StringScanner sp = new StringScanner(toPush.run());
|
||||
@@ -211,7 +466,9 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
String hash = sp.spaceToken();
|
||||
String time = sp.spaceToken();
|
||||
c.revision = new GitRevisionNumber(hash, new Date(Long.parseLong(time) * 1000L));
|
||||
c.authorTime = sp.spaceToken();
|
||||
c.message = sp.line();
|
||||
c.isMerge = sp.line().indexOf(' ') != -1;
|
||||
r.commits.add(c);
|
||||
}
|
||||
}
|
||||
@@ -234,35 +491,110 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
* @param exceptions the collected exceptions
|
||||
*/
|
||||
public static void showDialog(final Project project, List<VirtualFile> vcsRoots, final Collection<VcsException> exceptions) {
|
||||
final List<Root> roots = loadRoots(project, vcsRoots, exceptions);
|
||||
final List<Root> roots = loadRoots(project, vcsRoots, exceptions, true);
|
||||
if (!exceptions.isEmpty()) {
|
||||
Messages
|
||||
.showErrorDialog(project, GitBundle.getString("push.active.fetch.failed"), GitBundle.getString("push.active.fetch.failed.title"));
|
||||
return;
|
||||
}
|
||||
GitPushActiveBranchesDialog d = new GitPushActiveBranchesDialog(project, roots);
|
||||
GitPushActiveBranchesDialog d = new GitPushActiveBranchesDialog(project, vcsRoots, roots);
|
||||
d.show();
|
||||
if (d.isOK()) {
|
||||
final ArrayList<Root> rootsToPush = new ArrayList<Root>();
|
||||
for (int i = 0; i < d.myTreeRoot.getChildCount(); i++) {
|
||||
CheckedTreeNode node = (CheckedTreeNode)d.myTreeRoot.getChildAt(i);
|
||||
Root r = (Root)node.getUserObject();
|
||||
if (r.remote == null || r.commits.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
boolean topCommit = true;
|
||||
for (int j = 0; j < node.getChildCount(); j++) {
|
||||
if (node.getChildAt(j) instanceof CheckedTreeNode) {
|
||||
CheckedTreeNode commitNode = (CheckedTreeNode)node.getChildAt(j);
|
||||
if (commitNode.isChecked()) {
|
||||
Commit commit = (Commit)commitNode.getUserObject();
|
||||
if (!topCommit) {
|
||||
r.commitToPush = commit.revision.asString();
|
||||
}
|
||||
rootsToPush.add(r);
|
||||
break;
|
||||
}
|
||||
topCommit = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
final ProgressManager manager = ProgressManager.getInstance();
|
||||
manager.runProcessWithProgressSynchronously(new Runnable() {
|
||||
public void run() {
|
||||
for (Root r : roots) {
|
||||
if (r.remote != null && r.commits.size() != 0) {
|
||||
GitLineHandler h = new GitLineHandler(project, r.root, GitHandler.PUSH);
|
||||
h.addParameters("-v", r.remote, r.branch+":"+r.remoteBranch);
|
||||
GitHandlerUtil.doSynchronouslyWithExceptions(h);
|
||||
}
|
||||
for (Root r : rootsToPush) {
|
||||
GitLineHandler h = new GitLineHandler(project, r.root, GitHandler.PUSH);
|
||||
String src = r.commitToPush != null ? r.commitToPush : r.branch;
|
||||
h.addParameters("-v", r.remote, src + ":" + r.remoteBranch);
|
||||
GitHandlerUtil.doSynchronouslyWithExceptions(h);
|
||||
}
|
||||
}
|
||||
}, GitBundle.getString("push.active.pushing"), false, project);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create UI components for the dialog
|
||||
*/
|
||||
private void createUIComponents() {
|
||||
myTreeRoot = new CheckedTreeNode("ROOT");
|
||||
myCommitTree = new CheckboxTree(new CheckboxTree.CheckboxTreeCellRenderer() {
|
||||
@Override
|
||||
public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||
ColoredTreeCellRenderer r = getTextRenderer();
|
||||
if (!(value instanceof DefaultMutableTreeNode)) {
|
||||
// unknown node type
|
||||
renderUnknown(r, value);
|
||||
return;
|
||||
}
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
|
||||
if (!(node.getUserObject() instanceof Node)) {
|
||||
// unknown node type
|
||||
renderUnknown(r, node.getUserObject());
|
||||
return;
|
||||
}
|
||||
((Node)node.getUserObject()).render(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render unknown node
|
||||
*
|
||||
* @param r a renderer to use
|
||||
* @param value the unknown value
|
||||
*/
|
||||
private void renderUnknown(ColoredTreeCellRenderer r, Object value) {
|
||||
r.append("UNSUPPORTED NODE TYPE: " + (value == null ? "null" : value.getClass().getName()), SimpleTextAttributes.ERROR_ATTRIBUTES);
|
||||
}
|
||||
}, myTreeRoot) {
|
||||
@Override
|
||||
protected void onNodeStateChanged(CheckedTreeNode node) {
|
||||
updateButtons();
|
||||
super.onNodeStateChanged(node);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The base class for nodes in the tree
|
||||
*/
|
||||
static abstract class Node {
|
||||
/**
|
||||
* Render the node text
|
||||
*
|
||||
* @param renderer the renderer to use
|
||||
*/
|
||||
protected abstract void render(ColoredTreeCellRenderer renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* The commit descriptor
|
||||
*/
|
||||
static class Status {
|
||||
static class Status extends Node {
|
||||
/**
|
||||
* The root
|
||||
*/
|
||||
@@ -272,30 +604,34 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
protected void render(ColoredTreeCellRenderer renderer) {
|
||||
renderer.append(GitBundle.getString("push.active.status.status"));
|
||||
if (root.branch == null) {
|
||||
return GitBundle.message("push.active.status.no.branch");
|
||||
renderer.append(GitBundle.message("push.active.status.no.branch"), SimpleTextAttributes.ERROR_ATTRIBUTES);
|
||||
}
|
||||
if (root.remote == null) {
|
||||
return GitBundle.message("push.active.status.no.tracked");
|
||||
else if (root.remote == null) {
|
||||
renderer.append(GitBundle.message("push.active.status.no.tracked"), SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
|
||||
}
|
||||
if (root.remoteCommits != 0 && root.commits.size() == 0) {
|
||||
return GitBundle.message("push.active.status.no.commits.behind", root.remoteCommits);
|
||||
else if (root.remoteCommits != 0 && root.commits.size() == 0) {
|
||||
renderer.append(GitBundle.message("push.active.status.no.commits.behind", root.remoteCommits),
|
||||
SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
|
||||
}
|
||||
if (root.commits.size() == 0) {
|
||||
return GitBundle.message("push.active.status.no.commits");
|
||||
else if (root.commits.size() == 0) {
|
||||
renderer.append(GitBundle.message("push.active.status.no.commits"), SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
|
||||
}
|
||||
if (root.remoteCommits != 0) {
|
||||
return GitBundle.message("push.active.status.behind", root.remoteCommits);
|
||||
else if (root.remoteCommits != 0) {
|
||||
renderer.append(GitBundle.message("push.active.status.behind", root.remoteCommits), SimpleTextAttributes.ERROR_ATTRIBUTES);
|
||||
}
|
||||
else {
|
||||
renderer.append(GitBundle.message("push.active.status.push", root.commits.size()));
|
||||
}
|
||||
return GitBundle.message("push.active.status.push", root.commits.size());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The commit descriptor
|
||||
*/
|
||||
static class Commit {
|
||||
static class Commit extends Node {
|
||||
/**
|
||||
* The root
|
||||
*/
|
||||
@@ -308,20 +644,40 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
* The message
|
||||
*/
|
||||
String message;
|
||||
/**
|
||||
* The author time
|
||||
*/
|
||||
String authorTime;
|
||||
/**
|
||||
* If true, the commit is a merge
|
||||
*/
|
||||
boolean isMerge;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return GitBundle.message("push.active.commit.node", revision.asString().substring(0, HASH_PREFIX_SIZE), message);
|
||||
protected void render(ColoredTreeCellRenderer renderer) {
|
||||
renderer.append(revision.asString().substring(0, HASH_PREFIX_SIZE), SimpleTextAttributes.GRAYED_ATTRIBUTES);
|
||||
renderer.append(": ");
|
||||
renderer.append(message);
|
||||
if (isMerge) {
|
||||
renderer.append(GitBundle.getString("push.active.commit.node.merge"), SimpleTextAttributes.GRAYED_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the identifier that is supposed to be stable with respect to rebase
|
||||
*/
|
||||
String commitId() {
|
||||
return authorTime + ":" + message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The root node
|
||||
*/
|
||||
static class Root {
|
||||
static class Root extends Node {
|
||||
/**
|
||||
* if true, the update is required
|
||||
*/
|
||||
@@ -342,6 +698,10 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
* the remote branch name
|
||||
*/
|
||||
String remoteBranch;
|
||||
/**
|
||||
* The commit that will be actually pushed
|
||||
*/
|
||||
String commitToPush;
|
||||
/**
|
||||
* the commit
|
||||
*/
|
||||
@@ -351,20 +711,29 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (branch == null) {
|
||||
return GitBundle.message("push.active.root.node.no.branch", root.getPresentableUrl());
|
||||
protected void render(ColoredTreeCellRenderer renderer) {
|
||||
SimpleTextAttributes rootAttributes;
|
||||
SimpleTextAttributes branchAttributes;
|
||||
if (remote != null && commits.size() != 0 && remoteCommits != 0 || branch == null) {
|
||||
rootAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES.derive(SimpleTextAttributes.STYLE_BOLD, null, null, null);
|
||||
branchAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
|
||||
}
|
||||
if (remote == null) {
|
||||
return GitBundle.message("push.active.root.node.no.tracked", root.getPresentableUrl(), branch);
|
||||
else if (remote == null || commits.size() == 0) {
|
||||
rootAttributes = SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES;
|
||||
branchAttributes = SimpleTextAttributes.GRAYED_ATTRIBUTES;
|
||||
}
|
||||
if (commits.size() == 0) {
|
||||
return GitBundle.message("push.active.root.node.no.commits", root.getPresentableUrl(), branch, remote, remoteBranch);
|
||||
else {
|
||||
branchAttributes = SimpleTextAttributes.REGULAR_ATTRIBUTES;
|
||||
rootAttributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES;
|
||||
}
|
||||
if (remoteCommits != 0) {
|
||||
return GitBundle.message("push.active.root.node.behind", root.getPresentableUrl(), branch, remote, remoteBranch);
|
||||
renderer.append(root.getPresentableUrl(), rootAttributes);
|
||||
if (branch != null) {
|
||||
renderer.append(" [" + branch, branchAttributes);
|
||||
if (remote != null) {
|
||||
renderer.append(" -> " + remote + "#" + remoteBranch, branchAttributes);
|
||||
}
|
||||
renderer.append("]", branchAttributes);
|
||||
}
|
||||
return GitBundle.message("push.active.root.node.push", root.getPresentableUrl(), branch, remote, remoteBranch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitBranch;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.commands.GitHandler;
|
||||
import git4idea.commands.GitLineHandler;
|
||||
import git4idea.commands.StringScanner;
|
||||
import git4idea.rebase.GitInteractiveRebaseEditorHandler;
|
||||
import git4idea.rebase.GitRebaseEditorService;
|
||||
import git4idea.update.GitBaseRebaseProcess;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.DateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This is subclass of {@link git4idea.update.GitBaseRebaseProcess} that implement rebase operation for {@link GitPushActiveBranchesDialog}.
|
||||
* This operation reorders commits if needed.
|
||||
*/
|
||||
public class GitPushRebaseProcess extends GitBaseRebaseProcess {
|
||||
/**
|
||||
* The logger
|
||||
*/
|
||||
private static final Logger LOG = Logger.getInstance(GitPushRebaseProcess.class.getName());
|
||||
/**
|
||||
* If true, auto-stash is required before running rebase
|
||||
*/
|
||||
private final boolean myAutoStash;
|
||||
/**
|
||||
* The map from vcs root to list of the commit identifier for reordered commits, if vcs root is not provided, the reordering is not needed.
|
||||
*/
|
||||
private final Map<VirtualFile, List<String>> myReorderedCommits;
|
||||
/**
|
||||
* A set of roots that have non-pushed merges
|
||||
*/
|
||||
private Set<VirtualFile> myRootsWithMerges;
|
||||
/**
|
||||
* The registration number for the rebase editor
|
||||
*/
|
||||
private Integer myRebaseEditorNo;
|
||||
/**
|
||||
* The rebase editor service
|
||||
*/
|
||||
private final GitRebaseEditorService myRebaseEditorService;
|
||||
|
||||
/**
|
||||
* The constructor
|
||||
*
|
||||
* @param vcs the vcs instance
|
||||
* @param project the project instance
|
||||
* @param exceptions the list of exceptions for the process
|
||||
* @param autoStash if true, the auto-stash is required
|
||||
* @param rootsWithMerges a set of roots with merges
|
||||
*/
|
||||
public GitPushRebaseProcess(final GitVcs vcs,
|
||||
final Project project,
|
||||
List<VcsException> exceptions,
|
||||
boolean autoStash,
|
||||
Map<VirtualFile, List<String>> reorderedCommits,
|
||||
Set<VirtualFile> rootsWithMerges) {
|
||||
super(vcs, project, exceptions);
|
||||
myAutoStash = autoStash;
|
||||
myReorderedCommits = reorderedCommits;
|
||||
myRootsWithMerges = rootsWithMerges;
|
||||
myRebaseEditorService = GitRebaseEditorService.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected GitLineHandler makeStartHandler(VirtualFile root) throws VcsException {
|
||||
List<String> commits = myReorderedCommits.get(root);
|
||||
boolean hasMerges = myRootsWithMerges.contains(root);
|
||||
GitLineHandler h = new GitLineHandler(myProject, root, GitHandler.REBASE);
|
||||
if (commits != null || hasMerges) {
|
||||
h.addParameters("-i");
|
||||
PushRebaseEditor pushRebaseEditor = new PushRebaseEditor(root, commits, hasMerges);
|
||||
myRebaseEditorNo = pushRebaseEditor.getHandlerNo();
|
||||
myRebaseEditorService.configureHandler(h, myRebaseEditorNo);
|
||||
if (hasMerges) {
|
||||
h.addParameters("-p");
|
||||
}
|
||||
}
|
||||
h.addParameters("-m", "-v");
|
||||
GitBranch currentBranch = GitBranch.current(myProject, root);
|
||||
assert currentBranch != null;
|
||||
GitBranch trackedBranch = currentBranch.tracked(myProject, root);
|
||||
assert trackedBranch != null;
|
||||
h.addParameters(trackedBranch.getFullName());
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void cleanupHandler(VirtualFile root, GitLineHandler h) {
|
||||
if (myRebaseEditorNo != null) {
|
||||
myRebaseEditorService.unregisterHandler(myRebaseEditorNo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void configureRebaseEditor(VirtualFile root, GitLineHandler h) {
|
||||
GitInteractiveRebaseEditorHandler editorHandler = new GitInteractiveRebaseEditorHandler(myRebaseEditorService, myProject, root);
|
||||
editorHandler.setRebaseEditorShown();
|
||||
myRebaseEditorNo = editorHandler.getHandlerNo();
|
||||
myRebaseEditorService.configureHandler(h, myRebaseEditorNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected String makeStashMessage() {
|
||||
return "Uncommitted changes before rebase operation in push dialog at " +
|
||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.US).format(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean isAutoStash() {
|
||||
return myAutoStash;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rebase editor that just overrides the list of commits
|
||||
*/
|
||||
class PushRebaseEditor extends GitInteractiveRebaseEditorHandler {
|
||||
/**
|
||||
* The reordered commits
|
||||
*/
|
||||
private List<String> myCommits;
|
||||
/**
|
||||
* The true means that the root has merges
|
||||
*/
|
||||
private boolean myHasMerges;
|
||||
|
||||
/**
|
||||
* The constructor from fields that is expected to be
|
||||
* accessed only from {@link git4idea.rebase.GitRebaseEditorService}.
|
||||
*
|
||||
* @param root the git repository root
|
||||
* @param commits the reordered commits
|
||||
* @param hasMerges if true, the vcs root has merges
|
||||
*/
|
||||
public PushRebaseEditor(final VirtualFile root, List<String> commits, boolean hasMerges) {
|
||||
super(myRebaseEditorService, myProject, root);
|
||||
myCommits = commits;
|
||||
myHasMerges = hasMerges;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int editCommits(String path) {
|
||||
if (!myRebaseEditorShown) {
|
||||
myRebaseEditorShown = true;
|
||||
if (myHasMerges) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
TreeMap<String, String> pickLines = new TreeMap<String, String>();
|
||||
StringScanner s = new StringScanner(new String(FileUtil.loadFileText(new File(path), GitUtil.UTF8_ENCODING)));
|
||||
while (s.hasMoreData()) {
|
||||
if (!s.tryConsume("pick ")) {
|
||||
s.line();
|
||||
continue;
|
||||
}
|
||||
String commit = s.spaceToken();
|
||||
pickLines.put(commit, "pick " + commit + " " + s.line());
|
||||
}
|
||||
PrintWriter w = new PrintWriter(new OutputStreamWriter(new FileOutputStream(path), GitUtil.UTF8_ENCODING));
|
||||
try {
|
||||
for (String commit : myCommits) {
|
||||
String key = pickLines.headMap(commit + "\u0000").lastKey();
|
||||
if (key == null || !commit.startsWith(key)) {
|
||||
continue; // commit from merged branch
|
||||
}
|
||||
w.print(pickLines.get(key) + "\n");
|
||||
}
|
||||
}
|
||||
finally {
|
||||
w.close();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.error("Editor failed: ", ex);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return super.editCommits(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,6 +94,8 @@ diff.find.error=Finding revision for diff: {0}
|
||||
error.dialog.title=Error
|
||||
error.list.title={0} Error:
|
||||
error.occurred.during=Error occurred during ''{0}''
|
||||
errors.message.item=<li>{0}</li>
|
||||
errors.message=<html>The git operation ended with multiple errors:<ul></ul></html>
|
||||
fetch.action.name=Fetch
|
||||
fetch.button=Fetch
|
||||
fetch.force.references.update.tooltip=Forces update of branch references for which update is not forced in refrence mapping.
|
||||
@@ -188,26 +190,32 @@ pull.url.title=Pull URL
|
||||
pulling.title=Pulling changes from {0}
|
||||
push.action.name=Push
|
||||
push.active.action.name=Push Active Branches
|
||||
push.active.autostash.tooltip=Auto-stash changes before rebase
|
||||
push.active.autostash=&Auto-stash
|
||||
push.active.button=Push
|
||||
push.active.commit.node=<html><span style="color: #7f7f7f">{0}</span>: {1}</html>
|
||||
push.active.commit.node.merge= (merge commit)
|
||||
push.active.commits=&Commits:
|
||||
push.active.error.behind=Some local branches are behind remote branches
|
||||
push.active.error.merges.unchecked=Roots with merge commits cannot be selectively pushed
|
||||
push.active.error.no.branch=Some roots are not on the branch
|
||||
push.active.error.reorder.merges=It is not possible to reorder commits if merges present
|
||||
push.active.error.reorder.needed=The nodes should be reordered using rebase operation.
|
||||
push.active.fetch.failed.title=Fetch Failed
|
||||
push.active.fetch.failed=The fetch operation failed for some branches
|
||||
push.active.fetch.tooltip=Fetch state of tracked branches from remote repository
|
||||
push.active.fetch=&Fetch
|
||||
push.active.fetching=Fetching changes for active branches
|
||||
push.active.pushing=Pushing branches...
|
||||
push.active.root.node.behind=<html><span style="color: red"><b>{0}</b>[{1} -> {2}#{3}]</span></html>
|
||||
push.active.root.node.no.branch=<html><span style="color: red"><b>{0}</b></span></html>
|
||||
push.active.root.node.no.commits=<html><span style="color: #7f7f7f"><b>{0}</b>[{1} -> {2}#{3}]</span></html>
|
||||
push.active.root.node.no.tracked=<html><span style="color: #7f7f7f"><b>{0}</b>[{1}]</span></html>
|
||||
push.active.root.node.push=<html><b>{0}</b>[{1} -> {2}#{3}]</html>
|
||||
push.active.status.behind=<html>Status: <span style="color: red">Unable to push. The current branch is behind tracked branch by {0} commit(s).</span></html>
|
||||
push.active.status.no.branch=<html>Status: <span style="color: red">The head is not on the branch.</span></html>
|
||||
push.active.status.no.commits.behind=<html>Status: <span style="color: #7f7f7f">Nothing to push. The current branch is behind tracked branch by {0} commit(s).</span></html>
|
||||
push.active.status.no.commits=<html>Status: <span style="color: #7f7f7f">Nothing to push.</span></html>
|
||||
push.active.status.no.tracked=<html>Status: <span style="color: #7f7f7f">No tracked branch is configured.</span></html>
|
||||
push.active.status.push=<html>Status: {0} commit(s) will be pushed.</html>
|
||||
push.active.rebase.tooltip=Rebase branches in order to make push possible (might reorder commits)
|
||||
push.active.rebase=&Rebase
|
||||
push.active.rebasing=Rebasing ...
|
||||
push.active.status.behind=Unable to push. The current branch is behind tracked branch by {0} commit(s).
|
||||
push.active.status.no.branch=The head is not on the branch.
|
||||
push.active.status.no.commits.behind=Nothing to push. The current branch is behind tracked branch by {0} commit(s).
|
||||
push.active.status.no.commits=Nothing to push.
|
||||
push.active.status.no.tracked=No tracked branch is configured.
|
||||
push.active.status.push={0} commit(s) will be pushed.
|
||||
push.active.status.status=Status:
|
||||
push.active.title=Push Active Branches
|
||||
push.active.view=&View
|
||||
push.branches.tooltip=Select branches to push
|
||||
|
||||
@@ -32,6 +32,7 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -182,6 +183,30 @@ public class GitUIUtil {
|
||||
showOperationError(project, operation, ex.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show errors associated with the specified operation
|
||||
*
|
||||
* @param project the project
|
||||
* @param exs the exceptions to show
|
||||
* @param operation the operation name
|
||||
*/
|
||||
public static void showOperationErrors(final Project project,
|
||||
final Collection<VcsException> exs,
|
||||
@NonNls @NotNull final String operation) {
|
||||
if (exs.size() == 1) {
|
||||
//noinspection ThrowableResultOfMethodCallIgnored
|
||||
showOperationError(project, operation, exs.iterator().next().getMessage());
|
||||
}
|
||||
else if (exs.size() > 1) {
|
||||
// TODO use dialog in order to show big messages
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (VcsException ex : exs) {
|
||||
b.append(GitBundle.message("errors.message.item", ex.getMessage()));
|
||||
}
|
||||
showOperationError(project, operation, GitBundle.message("errors.message", b.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error associated with the specified operation
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user