[git] Reverting unfinished changes for push.

There are some problems there that can't be fixed soon, so we don't want this in release.

This commit reverts the range 50a427639^..d81998d --author=Likhodedov
This commit is contained in:
Kirill Likhodedov
2012-11-15 19:30:45 +01:00
parent 77d6b68a6b
commit 0eb0a81f9a
20 changed files with 304 additions and 295 deletions
@@ -251,12 +251,6 @@ public class QueueProcessor<T> {
}
}
public boolean hasPendingJobs() {
synchronized (myQueue) {
return !myQueue.isEmpty();
}
}
private static class MyOverrideEquals {
private final Object myDelegate;
@@ -22,7 +22,6 @@ import com.intellij.openapi.diagnostic.Logger;
*/
public class GitLogger {
public static final Logger CORE_LOG = Logger.getInstance("GitCore");
public static final Logger PUSH_LOG = Logger.getInstance("GitPush");
}
@@ -15,74 +15,33 @@
*/
package git4idea.branch;
import com.intellij.openapi.diagnostic.Logger;
import git4idea.GitLocalBranch;
import git4idea.GitLogger;
import git4idea.GitRemoteBranch;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Holder for a Git branch and the branch it is "connected" with.
* Usually it is the tracked branch, but may any other (for example, when pushing to an alternative branch).
* Holder for Git branch and the branch it is "connected" with. It is tracked branch or so called "matched" branch.
*
* @author Kirill Likhodedov
*/
public class GitBranchPair {
private @NotNull GitLocalBranch myBranch;
private @Nullable GitRemoteBranch myDestBranch;
private static final Logger LOG = GitLogger.CORE_LOG;
@NotNull private GitLocalBranch mySource;
@Nullable private GitRemoteBranch myDest;
public GitBranchPair(@NotNull GitLocalBranch source, @Nullable GitRemoteBranch destination) {
mySource = source;
myDest = destination;
public GitBranchPair(@NotNull GitLocalBranch branch, @Nullable GitRemoteBranch destination) {
myBranch = branch;
myDestBranch = destination;
}
@NotNull
public static GitBranchPair findCurrentAnTracked(@NotNull GitRepository repository) {
repository.update();
GitLocalBranch currentBranch = repository.getCurrentBranch();
LOG.assertTrue(currentBranch != null, "Push shouldn't be available in the detached HEAD state");
return new GitBranchPair(currentBranch, currentBranch.findTrackedBranch(repository));
}
@NotNull
public GitLocalBranch getSource() {
return mySource;
public GitLocalBranch getBranch() {
return myBranch;
}
@Nullable
public GitRemoteBranch getDest() {
return myDest;
}
@Override
public String toString() {
String dest = myDest == null ? "nowhere" : myDest.getFullName();
return mySource.getName() + "->" + dest;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GitBranchPair spec = (GitBranchPair)o;
if (myDest != null ? !myDest.equals(spec.myDest) : spec.myDest != null) return false;
if (!mySource.equals(spec.mySource)) return false;
return true;
}
@Override
public int hashCode() {
int result = mySource.hashCode();
result = 31 * result + (myDest != null ? myDest.hashCode() : 0);
return result;
return myDestBranch;
}
}
@@ -18,8 +18,8 @@ package git4idea.commands;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.branch.GitBranchPair;
import git4idea.history.browser.GitCommit;
import git4idea.push.GitPushSpec;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -95,7 +95,7 @@ public interface Git {
@NotNull GitLineHandlerListener... listeners);
@NotNull
GitCommandResult push(@NotNull GitRepository repository, @NotNull GitBranchPair pushSpec,
GitCommandResult push(@NotNull GitRepository repository, @NotNull GitPushSpec pushSpec,
@NotNull GitLineHandlerListener... listeners);
@NotNull
@@ -25,9 +25,9 @@ import com.intellij.util.ExceptionUtil;
import com.intellij.vcsUtil.VcsFileUtil;
import git4idea.GitBranch;
import git4idea.GitExecutionException;
import git4idea.branch.GitBranchPair;
import git4idea.history.GitHistoryUtils;
import git4idea.history.browser.GitCommit;
import git4idea.push.GitPushSpec;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
@@ -368,7 +368,7 @@ public class GitImpl implements Git {
@Override
@NotNull
public GitCommandResult push(@NotNull GitRepository repository, @NotNull GitBranchPair pushSpec,
public GitCommandResult push(@NotNull GitRepository repository, @NotNull GitPushSpec pushSpec,
@NotNull GitLineHandlerListener... listeners) {
GitRemote remote = pushSpec.getDest().getRemote();
GitBranch remoteBranch = pushSpec.getDest();
@@ -78,15 +78,12 @@ final class GitCommitsByBranch {
return new GitCommitsByBranch(res);
}
boolean hasAnythingToPush() {
Collection<GitCommit> allCommits = new ArrayList<GitCommit>();
for (GitPushBranchInfo info : myCommitsByBranch.values()) {
if (info.getType() == GitPushBranchInfo.Type.NEW_BRANCH || info.getType() == GitPushBranchInfo.Type.NO_TRACKED_OR_TARGET) {
return true;
}
allCommits.addAll(info.getCommits());
@NotNull
public Collection<GitCommit> getAllCommits() {
Collection<GitCommit> commits = new ArrayList<GitCommit>();
for (GitPushBranchInfo branchInfo : myCommitsByBranch.values()) {
commits.addAll(branchInfo.getCommits());
}
return !allCommits.isEmpty();
return commits;
}
}
@@ -15,17 +15,19 @@
*/
package git4idea.push;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.impl.ActionButton;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBTextField;
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 javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.util.Collection;
@@ -36,6 +38,8 @@ class GitManualPushToBranch extends JPanel {
private final JTextField myDestBranchTextField;
private final JBLabel myComment;
private final GitPushLogRefreshAction myRefreshAction;
private final JComponent myRefreshButton;
private final RemoteSelector myRemoteSelector;
private final JComponent myRemoteSelectorComponent;
private boolean myMultiRepositoryProject;
@@ -44,25 +48,20 @@ class GitManualPushToBranch extends JPanel {
super();
myMultiRepositoryProject = multiRepositoryProject;
myDestBranchTextField = new JBTextField(20);
myDestBranchTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
performOnRefresh.run();
}
@Override
public void removeUpdate(DocumentEvent e) {
performOnRefresh.run();
}
@Override
public void changedUpdate(DocumentEvent e) {
performOnRefresh.run();
}
});
myDestBranchTextField = new JTextField(20);
myComment = new JBLabel("This will apply to all selected repositories", UIUtil.ComponentStyle.SMALL);
myRefreshAction = new GitPushLogRefreshAction() {
@Override public void actionPerformed(AnActionEvent e) {
performOnRefresh.run();
}
};
myRefreshButton = new ActionButton(myRefreshAction, myRefreshAction.getTemplatePresentation(), myRefreshAction.getTemplatePresentation().getText(), ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE);
myRefreshButton.setFocusable(true);
final ShortcutSet shortcutSet = ActionManager.getInstance().getAction(IdeActions.ACTION_REFRESH).getShortcutSet();
myRefreshAction.registerCustomShortcutSet(shortcutSet, myRefreshButton);
myRemoteSelector = new RemoteSelector();
myRemoteSelectorComponent = myRemoteSelector.createComponent();
@@ -80,12 +79,11 @@ class GitManualPushToBranch extends JPanel {
.setDefaultInsets(new Insets(0, 0, UIUtil.DEFAULT_VGAP, 5))
;
JLabel targetBranchLabel = new JBLabel("Target branch: ");
targetBranchLabel.setDisplayedMnemonic('b');
targetBranchLabel.setLabelFor(myDestBranchTextField);
panel.add(targetBranchLabel, g.nextLine().next());
panel.add(myRemoteSelectorComponent, g.next());
panel.add(myDestBranchTextField, g.next());
panel.add(myRefreshButton, g.next());
if (myMultiRepositoryProject) {
panel.add(myComment, g.nextLine().insets(0, 0, 0, 0).coverLine());
}
@@ -160,4 +158,11 @@ class GitManualPushToBranch extends JPanel {
}
private abstract static class GitPushLogRefreshAction extends DumbAwareAction {
GitPushLogRefreshAction() {
super("Refresh commit list", "Refresh commit list", AllIcons.Actions.Refresh);
}
}
}
@@ -15,13 +15,12 @@
*/
package git4idea.push;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.Consumer;
import com.intellij.util.concurrency.QueueProcessor;
import git4idea.*;
import git4idea.branch.GitBranchPair;
import git4idea.history.GitHistoryUtils;
@@ -36,13 +35,16 @@ import java.util.concurrent.TimeUnit;
/**
* Collects outgoing commits (commits to be pushed) for all repositories and holds this information.
*
* <p>The contract of the current implementation (probably, some or the limitations will be removed in the future):
* <p>The contract of the current implementations (probably, some or the limitations will be removed in the future):
* <ul>
* <li>If you want to request outgoing commits (and force refresh, even if they were previously collected),
* call {@link #collect(GitPushSpecs, ResultHandler)}. {@link GitPushDialog} should do it to get the up-to-date list of commits to push.</li>
* <li>If you want just to get the list of outgoing commits, but not sure if the list is ready, call
* {@link #waitForCompletionAndGetCommits()} and {@link #getCommits() get commits}.
* It will collect commits if they were not collected yet.</li>
* call {@link #collect(ResultHandler)}. {@link GitPushDialog} should do it to get the up-to-date list of commits to push.</li>
* <li>If you want to get the list of outgoing commits, but not sure if the list is ready, call
* {@link #waitForCompletionAndGetCommits(boolean)} and {@link #getCommits() get commits}.
* It will collect commits if they were not collected yet. It won't refresh the list if
* commits were collected, unless you specify the {@code refresh} parameter to it. <br/><br/>
* The latter is needed, when pushing without opening a push dialog (Commit & Push and if this behavior is switched on in the
* settings): otherwise we will get the list of commits requested by previous push dialog, which might be very out-of-date.</li>
* </ul>
* </p>
*
@@ -52,68 +54,99 @@ class GitOutgoingCommitsCollector {
private static final Logger LOG = GitLogger.PUSH_LOG;
@NotNull private final QueueProcessor<GitPushSpecs> myProcessor = new QueueProcessor<GitPushSpecs>(new Collector());
@NotNull private final Project myProject;
@NotNull private final GitPlatformFacade myPlatformFacade;
@NotNull private State myState;
@NotNull private final Object STATE_LOCK = new Object();
@NotNull private final Queue<ResultHandler> myResultHandlers = new ArrayDeque<ResultHandler>();
@NotNull private final Queue<ResultHandler> completionHandlers = new ArrayDeque<ResultHandler>();
private int refreshWaiters;
@NotNull private GitCommitsByRepoAndBranch myCommits = GitCommitsByRepoAndBranch.empty();
@Nullable private String myError;
/**
* Individual locks for threads which request {@link #waitForCompletionAndGetCommits()}.
* Individual locks for threads which request {@link #waitForCompletionAndGetCommits(boolean)}.
*/
@NotNull private final ThreadLocal<Object> WAITER_LOCK = new ThreadLocal<Object>();
/**
* Pass an instance of this handler to {@link #collect(GitPushSpecs, ResultHandler)}
* to handle result when collecting of outgoing commits completes.
* Pass an instance of this handler to {@link #collect(ResultHandler)} to handle result when collecting of outgoing commits completes.
*/
interface ResultHandler {
void onSuccess(@NotNull GitCommitsByRepoAndBranch commits);
void onError(@NotNull String error);
void onSuccess(GitCommitsByRepoAndBranch commits);
void onError(String error);
}
private static final ResultHandler EMPTY_RESULT_HANDLER = new ResultHandler() {
@Override
public void onSuccess(@NotNull GitCommitsByRepoAndBranch commits) {
public void onSuccess(GitCommitsByRepoAndBranch commits) {
}
@Override
public void onError(@NotNull String error) {
public void onError(String error) {
}
};
/**
* Current state of the component.
*/
private enum State {
EMPTY, // nothing was collected, need to load.
BUSY, // currently loading, please wait.
READY // everything loaded; refresh may be requested via the collect() method
}
static GitOutgoingCommitsCollector getInstance(@NotNull Project project) {
return ServiceManager.getService(project, GitOutgoingCommitsCollector.class);
}
GitOutgoingCommitsCollector(@NotNull Project project, @NotNull GitPlatformFacade facade) {
myProject = project;
myPlatformFacade = facade;
myState = State.EMPTY;
}
/**
* Collect commits (if not yet collected) in background and invoke the given runnable after completion.
* @param onComplete Executed after completed (successful or failed) execution of the task.
* It is executed in the current thread, so if you need it on AWT, include "invokeLater" to the handler.
*/
void collect(@NotNull GitPushSpecs pushSpecs, @Nullable ResultHandler onComplete) {
void collect(@Nullable ResultHandler onComplete) {
synchronized (STATE_LOCK) {
// if several collect requests go one by one, it is enough to have only one update: it will receive the up-to-date information.
// so we are removing any other pending requests.
myProcessor.clear();
myProcessor.add(pushSpecs);
if (myState == State.READY || myState == State.EMPTY) { // start initial collecting or refresh already collected info
myState = State.BUSY;
ApplicationManager.getApplication().executeOnPooledThread(new Updater());
}
else if (myState == State.BUSY) { // somebody already started collection => we request more up-to-date info afterwards.
refreshWaiters++;
}
// register the action that will be run after collection completes.
// important to add at least a fake action not to break the order of [collection requested-execute post-action].
myResultHandlers.offer(onComplete != null ? onComplete : EMPTY_RESULT_HANDLER);
// important to add at least fake action not to break the order of [collection requested-execute post-action].
completionHandlers.offer(onComplete != null ? onComplete : EMPTY_RESULT_HANDLER);
}
}
/**
* <ul>
* <li>If collection has completed immediately returns.</li>
* <li>If collection is in progress, waits for the completion.</li>
* <li>If collection has completed, and no need to {@code refresh}, immediately returns.</li>
* <li>If collection is in progress, waits for the completion.
* But if {@code refresh} is needed, starts new completion to get up-to-date results.</li>
* <li>If collection hasn't been started yet, starts it and waits for the completion.</li>
* </ul>
* @param refresh If the list of commits need to be re-queries even if we already have a version of it.
*/
@NotNull
GitCommitsByRepoAndBranch waitForCompletionAndGetCommits() {
GitCommitsByRepoAndBranch waitForCompletionAndGetCommits(boolean refresh) {
// if nobody has initialized the collection yet, or we need up-to-date version suspecting that something might have changed,
// then initialize collection.
synchronized (STATE_LOCK) {
if (myState == State.EMPTY || refresh) {
collect(null); // makes State BUSY and starts collection
}
}
while (!isReady()) {
try {
synchronized (WAITER_LOCK) {
@@ -130,7 +163,7 @@ class GitOutgoingCommitsCollector {
private boolean isReady() {
synchronized (STATE_LOCK) {
return myProcessor.isEmpty();
return myState == State.READY;
}
}
@@ -148,7 +181,11 @@ class GitOutgoingCommitsCollector {
}
}
private void doCollect(@NotNull GitPushSpecs pushSpecs) {
private boolean errorHappened() {
return getError() != null;
}
private void doCollect() {
// currently we clear the collected information before each collect.
// TODO Later we will persist it (providing in the Outgoing view) and update on push and other operations
synchronized (STATE_LOCK) {
@@ -157,7 +194,7 @@ class GitOutgoingCommitsCollector {
}
try {
GitCommitsByRepoAndBranch commits = collectOutgoingCommits(pushSpecs);
GitCommitsByRepoAndBranch commits = collectOutgoingCommits(GitPushUtil.getSpecsToPushForAllRepositories(myPlatformFacade, myProject));
synchronized (STATE_LOCK) {
myCommits = commits;
myError = null;
@@ -175,9 +212,8 @@ class GitOutgoingCommitsCollector {
if (handler == null) {
return;
}
String error = getError();
if (error != null) {
handler.onError(error);
if (errorHappened()) {
handler.onError(getError());
}
else {
handler.onSuccess(getCommits());
@@ -185,25 +221,28 @@ class GitOutgoingCommitsCollector {
}
// executed on a pooled thread
private class Collector implements Consumer<GitPushSpecs> {
@Override
public void consume(@NotNull GitPushSpecs pushSpecs) {
doCollect(pushSpecs);
private class Updater implements Runnable {
public void run() {
doCollect();
synchronized (STATE_LOCK) {
if (!myProcessor.hasPendingJobs()) {
// when we are completely up-to-date, execute all remaining tasks
while (!myResultHandlers.isEmpty()) {
handleResult(myResultHandlers.poll());
}
if (refreshWaiters > 0) { // if collection was requested again, we need to refresh information
refreshWaiters = 0; // but only once: we will get the up-to-date information anyway.
// execute the correspondent handler
handleResult(completionHandlers.poll());
// queue the next update (in a separate thread to release the lock and return).
ApplicationManager.getApplication().executeOnPooledThread(new Updater());
}
else {
// execute only the correspondent handler
handleResult(myResultHandlers.poll());
myState = State.READY;
// when we are completely up-to-date execute all remaining tasks
while (!completionHandlers.isEmpty()) {
handleResult(completionHandlers.poll());
}
}
}
}
}
/******************************************* ACTUAL COMMITS COLLECTION IS BELOW ********************************************************/
@@ -220,7 +259,7 @@ class GitOutgoingCommitsCollector {
}
}
Map<GitRepository, List<GitBranchPair>> reposAndBranchesToPush = prepareReposAndBranchesToPush(pushSpecs.getAllSpecs());
Map<GitRepository, List<GitBranchPair>> reposAndBranchesToPush = prepareReposAndBranchesToPush(pushSpecs.getSpecs());
Set<GitRepository> repositories = reposAndBranchesToPush.keySet();
Map<GitRepository, GitCommitsByBranch> commitsByRepoAndBranch = new HashMap<GitRepository, GitCommitsByBranch>();
@@ -236,13 +275,13 @@ class GitOutgoingCommitsCollector {
}
@NotNull
private static Map<GitRepository, List<GitBranchPair>> prepareReposAndBranchesToPush(@NotNull Map<GitRepository, GitBranchPair> pushSpecs)
private static Map<GitRepository, List<GitBranchPair>> prepareReposAndBranchesToPush(@NotNull Map<GitRepository, GitPushSpec> pushSpecs)
throws VcsException
{
Set<GitRepository> repositories = pushSpecs.keySet();
Map<GitRepository, List<GitBranchPair>> res = new HashMap<GitRepository, List<GitBranchPair>>();
for (GitRepository repository : repositories) {
GitBranchPair pushSpec = pushSpecs.get(repository);
GitPushSpec pushSpec = pushSpecs.get(repository);
if (pushSpec == null) {
continue;
}
@@ -257,7 +296,7 @@ class GitOutgoingCommitsCollector {
Map<GitBranch, GitPushBranchInfo> commitsByBranch = new HashMap<GitBranch, GitPushBranchInfo>();
for (GitBranchPair sourceDest : sourcesDestinations) {
GitLocalBranch source = sourceDest.getSource();
GitLocalBranch source = sourceDest.getBranch();
GitRemoteBranch dest = sourceDest.getDest();
List<GitCommit> commits = Collections.emptyList();
@@ -23,11 +23,9 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.components.JBLoadingPanel;
import com.intellij.util.Consumer;
import git4idea.*;
import git4idea.branch.GitBranchPair;
import git4idea.branch.GitBranchUtil;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.settings.GitSyncRepoSetting;
import git4idea.repo.GitRepositoryManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -44,9 +42,6 @@ public class GitPushDialog extends DialogWrapper {
private static final Logger LOG = GitLogger.PUSH_LOG;
private static final String DEFAULT_REMOTE = "origin";
@NotNull private final Collection<GitRepository> myAllRepositories;
@NotNull private final Project myProject;
@NotNull private final GitPlatformFacade myFacade;
@NotNull private final GitPushSpecs myInitialPushSpecs;
@NotNull private final GitOutgoingCommitsCollector myOutgoingCommitsCollector;
@@ -54,32 +49,29 @@ public class GitPushDialog extends DialogWrapper {
@NotNull private final JBLoadingPanel myLoadingPanel;
@NotNull private final GitManualPushToBranch myRefspecPanel;
public GitPushDialog(@NotNull Project project, @NotNull GitPlatformFacade facade, @NotNull GitPushSpecs pushSpecs) {
public GitPushDialog(@NotNull Project project, @NotNull GitPushSpecs pushSpecs) {
super(project);
myProject = project;
myFacade = facade;
myInitialPushSpecs = pushSpecs;
myAllRepositories = GitUtil.getRepositoryManager(project).getRepositories();
GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
Collection<GitRepository> repositories = myInitialPushSpecs.getRepositories();
myOutgoingCommitsCollector = GitOutgoingCommitsCollector.getInstance(project);
myLoadingPanel = new JBLoadingPanel(new BorderLayout(), this.getDisposable());
myListPanel = new GitPushLog(project, myAllRepositories, new RepositoryCheckboxListener());
myRefspecPanel = new GitManualPushToBranch(myAllRepositories.size() > 1, new RefreshButtonListener());
myListPanel = new GitPushLog(project, manager.getRepositories(), new RepositoryCheckboxListener());
myRefspecPanel = new GitManualPushToBranch(repositories.size() > 1, new RefreshButtonListener());
init();
setOKButtonText("Push");
setOKButtonMnemonic('P');
setTitle("Git Push");
updateTargetBranchField();
update();
}
private void updateTargetBranchField() {
private void update() {
Collection<GitRepository> repositories = myListPanel.getSelectedRepositories();
if (repositories == null) {
repositories = myInitialPushSpecs.getSelectedRepositories();
}
Collection<GitRemote> commonRemotes = getRemotesWithCommonNames(repositories);
myRefspecPanel.setRemotes(commonRemotes, getDefaultRemote(repositories));
@@ -166,6 +158,7 @@ public class GitPushDialog extends DialogWrapper {
private JComponent createCommitListPanel() {
myLoadingPanel.add(myListPanel, BorderLayout.CENTER);
loadCommitsInBackground(myInitialPushSpecs);
JPanel commitListPanel = new JPanel(new BorderLayout());
commitListPanel.add(myLoadingPanel, BorderLayout.CENTER);
@@ -175,20 +168,20 @@ public class GitPushDialog extends DialogWrapper {
private void loadCommitsInBackground(final GitPushSpecs pushSpecs) {
final ModalityState modalityState = ModalityState.stateForComponent(getRootPane());
myLoadingPanel.startLoading();
myOutgoingCommitsCollector.collect(pushSpecs, new GitOutgoingCommitsCollector.ResultHandler() {
myOutgoingCommitsCollector.collect(new GitOutgoingCommitsCollector.ResultHandler() {
@Override
public void onSuccess(@NotNull final GitCommitsByRepoAndBranch commits) {
public void onSuccess(final GitCommitsByRepoAndBranch commits) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
myListPanel.setCommits(defaultRepository(), pushSpecs.getSelectedRepositories(), commits);
myListPanel.setCommits(pushSpecs.getRepositories(), commits);
myLoadingPanel.stopLoading();
}
}, modalityState);
}
@Override
public void onError(@NotNull final String error) {
public void onError(final String error) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
@@ -199,34 +192,15 @@ public class GitPushDialog extends DialogWrapper {
});
}
@Nullable
private GitRepository defaultRepository() {
GitSyncRepoSetting syncSetting = myFacade.getSettings(myProject).getSyncSetting();
if (syncSetting.equals(GitSyncRepoSetting.SYNC)) {
return null;
}
GitRepository currentRepository = GitBranchUtil.getCurrentRepository(myProject);
if (currentRepository == null) {
LOG.info("Couldn't identify current repository");
}
return currentRepository;
}
@NotNull
public GitPushSpecs getPushSpecs() {
GitPushSpecs specs = new GitPushSpecs();
for (GitRepository repository : myAllRepositories) {
GitBranchPair spec = new GitBranchPair(repository.getCurrentBranch(), getTargetBranch()); // TODO what to do with detached head
specs.put(repository, spec, isSelected(repository));
}
return specs;
}
private boolean isSelected(@NotNull GitRepository repository) {
Collection<GitRepository> selectedRepositories = myListPanel.getSelectedRepositories();
return selectedRepositories != null
? selectedRepositories.contains(repository)
: myInitialPushSpecs.getSelectedRepositories().contains(repository);
Map<GitRepository, GitPushSpec> specs = new HashMap<GitRepository, GitPushSpec>();
for (GitRepository repository : selectedRepositories) {
GitPushSpec spec = new GitPushSpec(repository.getCurrentBranch(), getTargetBranch()); // TODO what to do with detached head
specs.put(repository, spec);
}
return new GitPushSpecs(specs);
}
@NotNull
@@ -275,7 +249,7 @@ public class GitPushDialog extends DialogWrapper {
private class RepositoryCheckboxListener implements Consumer<Boolean> {
@Override public void consume(Boolean checked) {
updateTargetBranchField();
update();
}
}
@@ -36,7 +36,6 @@ import git4idea.history.browser.GitCommit;
import git4idea.repo.GitRepository;
import git4idea.util.GitUIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
@@ -159,12 +158,11 @@ class GitPushLog extends JPanel implements TypeSafeDataProvider {
return myTree;
}
void setCommits(@Nullable GitRepository defaultRepository, @NotNull Collection<GitRepository> selectedRepositories,
@NotNull GitCommitsByRepoAndBranch commits) {
void setCommits(Collection<GitRepository> selectedRepositories, @NotNull GitCommitsByRepoAndBranch commits) {
try {
TREE_CONSTRUCTION_LOCK.writeLock().lock();
myRootNode.removeAllChildren();
createNodes(defaultRepository, selectedRepositories, commits);
createNodes(selectedRepositories, commits);
myTreeModel.nodeStructureChanged(myRootNode);
myTree.setModel(myTreeModel); // TODO: why doesn't it repaint otherwise?
TreeUtil.expandAll(myTree);
@@ -217,28 +215,19 @@ class GitPushLog extends JPanel implements TypeSafeDataProvider {
}
}
private void createNodes(@Nullable GitRepository defaultRepository,
@NotNull Collection<GitRepository> selectedRepositories,
@NotNull GitCommitsByRepoAndBranch commits) {
for (GitRepository repository : sortRepositories(defaultRepository, selectedRepositories, commits)) {
private void createNodes(Collection<GitRepository> selectedRepositories, @NotNull GitCommitsByRepoAndBranch commits) {
for (GitRepository repository : sortRepositories(selectedRepositories, commits)) {
GitCommitsByBranch commitsByBranch = commits.get(repository);
createRepoNode(repository, selectedRepositories.contains(repository), commitsByBranch, myRootNode);
}
}
@NotNull
private static List<GitRepository> sortRepositories(@Nullable final GitRepository defaultRepository,
@NotNull final Collection<GitRepository> selectedRepositories,
private static List<GitRepository> sortRepositories(@NotNull final Collection<GitRepository> selectedRepositories,
@NotNull final GitCommitsByRepoAndBranch commits) {
List<GitRepository> repos = new ArrayList<GitRepository>(commits.getRepositories());
Collections.sort(repos, new Comparator<GitRepository>() {
@Override public int compare(GitRepository r1, GitRepository r2) {
if (r1.equals(defaultRepository)) {
return -1;
}
if (r2.equals(defaultRepository)) {
return 1;
}
// deselected repositories - to the end
if (selectedRepositories.contains(r1) && !selectedRepositories.contains(r2)) {
return -1;
@@ -313,10 +302,8 @@ class GitPushLog extends JPanel implements TypeSafeDataProvider {
}
/**
* @return repositories selected (via checkboxes) to be pushed,
* or null if the tree is not ready (therefore no repositories could be selected).
* @return repositories selected (via checkboxes) to be pushed.
*/
@Nullable
Collection<GitRepository> getSelectedRepositories() {
if (myAllRepositories.size() == 1) {
return myAllRepositories;
@@ -325,12 +312,12 @@ class GitPushLog extends JPanel implements TypeSafeDataProvider {
try {
TREE_CONSTRUCTION_LOCK.readLock().lock(); // wait for tree to be constructed
if (!myTreeWasConstructed) {
return null;
return myAllRepositories;
}
else {
Collection<GitRepository> selectedRepositories = new ArrayList<GitRepository>(myAllRepositories.size());
if (myRootNode.getChildCount() == 0) { // the method is requested before tree construction began
return null;
if (myRootNode.getChildCount() == 0) { // the method is requested before tree construction began => returning all repos.
return myAllRepositories;
}
for (int i = 0; i < myRootNode.getChildCount(); i++) {
@@ -0,0 +1,89 @@
/*
* 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.diagnostic.Logger;
import git4idea.GitLocalBranch;
import git4idea.GitLogger;
import git4idea.GitRemoteBranch;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Kirill Likhodedov
*/
public class GitPushSpec {
private static final Logger LOG = GitLogger.PUSH_LOG;
@NotNull private final GitLocalBranch mySource;
@Nullable private final GitRemoteBranch myDest;
@NotNull
public static GitPushSpec collect(GitRepository repository) {
repository.update();
GitLocalBranch currentBranch = repository.getCurrentBranch();
LOG.assertTrue(currentBranch != null, "Push shouldn't be available in the detached HEAD state");
return new GitPushSpec(currentBranch, currentBranch.findTrackedBranch(repository));
}
GitPushSpec(@NotNull GitLocalBranch source, @Nullable GitRemoteBranch dest) {
myDest = dest;
mySource = source;
}
@NotNull
public GitLocalBranch getSource() {
return mySource;
}
/**
* Returns the destination branch: branch on the remote which the source branch will be pushed to.
* @return destination branch or null if no destination branch has been defined (i. e. if there is no current branch, and no branch has
* been defined via the Push dialog).
*/
@Nullable
public GitRemoteBranch getDest() {
return myDest;
}
@Override
public String toString() {
return mySource + "->" + myDest;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GitPushSpec spec = (GitPushSpec)o;
if (myDest != null ? !myDest.equals(spec.myDest) : spec.myDest != null) return false;
if (!mySource.equals(spec.mySource)) return false;
return true;
}
@Override
public int hashCode() {
int result = mySource.hashCode();
result = 31 * result + (myDest != null ? myDest.hashCode() : 0);
return result;
}
}
@@ -15,71 +15,41 @@
*/
package git4idea.push;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import git4idea.branch.GitBranchPair;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Collections;
import java.util.Map;
/**
* <p>A container for push specs (source and target branches) per repository.</p>
*
* <p>We need GitPushSpecs for two operations:
* <ul>
* <li>to collect outgoing commits (in all repositories);</li>
* <li>to push (in selected repositories).</li>
* </ul>
*
* <p>That said, repositories that are not pushed, will have the parameter "selected" false.</p>
* Just a container for {@link GitPushSpec} per repository.
*
* @author Kirill Likhodedov
*/
class GitPushSpecs {
@NotNull private final Map<GitRepository, GitBranchPair> mySpecs;
@NotNull private final Map<GitRepository, Boolean> mySelectedRepositories;
@NotNull private final Map<GitRepository, GitPushSpec> mySpecs;
GitPushSpecs() {
mySpecs = new HashMap<GitRepository, GitBranchPair>();
mySelectedRepositories = new HashMap<GitRepository, Boolean>();
}
static GitPushSpecs empty() {
return new GitPushSpecs();
GitPushSpecs(@NotNull Map<GitRepository, GitPushSpec> specs) {
mySpecs = specs;
}
@NotNull
Map<GitRepository, GitBranchPair> getAllSpecs() {
Collection<GitRepository> getRepositories() {
return mySpecs.keySet();
}
@NotNull
Map<GitRepository, GitPushSpec> getSpecs() {
return mySpecs;
}
void put(@NotNull GitRepository repository, @NotNull GitBranchPair branchPair, boolean selected) {
mySpecs.put(repository, branchPair);
mySelectedRepositories.put(repository, selected);
}
GitBranchPair get(@NotNull GitRepository repository) {
GitPushSpec get(@NotNull GitRepository repository) {
return mySpecs.get(repository);
}
@NotNull
public Collection<GitRepository> getSelectedRepositories() {
return Collections2.filter(mySpecs.keySet(), new Predicate<GitRepository>() {
@Override
public boolean apply(@Nullable GitRepository input) {
assert input != null;
return mySelectedRepositories.get(input);
}
});
static GitPushSpecs empty() {
return new GitPushSpecs(Collections.<GitRepository, GitPushSpec>emptyMap());
}
public boolean isSelected(@NotNull GitRepository repository) {
return mySelectedRepositories.get(repository);
}
}
@@ -17,16 +17,18 @@ package git4idea.push;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.util.containers.HashMap;
import git4idea.GitLogger;
import git4idea.GitPlatformFacade;
import git4idea.branch.GitBranchPair;
import git4idea.branch.GitBranchUtil;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import git4idea.settings.GitSyncRepoSetting;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Utilities specific to push.
@@ -40,9 +42,8 @@ class GitPushUtil {
@NotNull
static GitPushSpecs getRepositoriesAndSpecsToPush(@NotNull GitPlatformFacade facade, @NotNull Project project) {
GitSyncRepoSetting syncSetting = facade.getSettings(project).getSyncSetting();
if (syncSetting.equals(GitSyncRepoSetting.SYNC)) {
return getSpecsForAllRepositories(facade, project, null);
return getSpecsToPushForAllRepositories(facade, project);
}
else {
if (facade.getRepositoryManager(project).moreThanOneRoot() && syncSetting.equals(GitSyncRepoSetting.NOT_DECIDED)) {
@@ -50,21 +51,22 @@ class GitPushUtil {
}
GitRepository repository = GitBranchUtil.getCurrentRepository(project);
if (repository == null) {
LOG.info("Couldn't identify current repository");
LOG.warn("Couldn't retrieve current repository");
return GitPushSpecs.empty();
}
return getSpecsForAllRepositories(facade, project, repository);
return new GitPushSpecs(Collections.singletonMap(repository, GitPushSpec.collect(repository)));
}
}
@NotNull
private static GitPushSpecs getSpecsForAllRepositories(@NotNull GitPlatformFacade facade, @NotNull Project project,
@Nullable GitRepository selectedRepository) {
List<GitRepository> repositories = facade.getRepositoryManager(project).getRepositories();
GitPushSpecs specs = new GitPushSpecs();
public static GitPushSpecs getSpecsToPushForAllRepositories(@NotNull GitPlatformFacade facade,
@NotNull Project project) {
GitRepositoryManager manager = facade.getRepositoryManager(project);
List<GitRepository> repositories = manager.getRepositories();
Map<GitRepository, GitPushSpec> map = new HashMap<GitRepository, GitPushSpec>();
for (GitRepository repository : repositories) {
boolean selected = selectedRepository == null || repository.equals(selectedRepository);
specs.put(repository, GitBranchPair.findCurrentAnTracked(repository), selected);
map.put(repository, GitPushSpec.collect(repository));
}
return specs;
return new GitPushSpecs(map);
}
}
@@ -28,7 +28,6 @@ import com.intellij.openapi.vcs.update.UpdatedFiles;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ui.UIUtil;
import git4idea.*;
import git4idea.branch.GitBranchPair;
import git4idea.commands.Git;
import git4idea.commands.GitCommandResult;
import git4idea.config.GitConfigUtil;
@@ -45,10 +44,7 @@ import git4idea.update.GitUpdateResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -82,7 +78,7 @@ public final class GitPusher {
public static void showPushDialogAndPerformPush(@NotNull Project project, @NotNull GitPlatformFacade facade) {
GitPushSpecs repositoriesToPush = GitPushUtil.getRepositoriesAndSpecsToPush(facade, project);
GitPushDialog dialog = new GitPushDialog(project, facade, repositoriesToPush);
GitPushDialog dialog = new GitPushDialog(project, repositoriesToPush);
dialog.show();
if (dialog.isOK()) {
runPushInBackground(project, facade, dialog.getPushSpecs());
@@ -164,9 +160,9 @@ public final class GitPusher {
private GitPushResult tryPushAndGetResult(@NotNull GitPushSpecs pushSpecs) {
GitPushResult pushResult = new GitPushResult(myProject);
GitCommitsByRepoAndBranch commits = myOutgoingCommitsCollector.waitForCompletionAndGetCommits();
for (GitRepository repository : pushSpecs.getSelectedRepositories()) {
if (!commits.get(repository).hasAnythingToPush()) {
GitCommitsByRepoAndBranch commits = myOutgoingCommitsCollector.waitForCompletionAndGetCommits(false);
for (GitRepository repository : pushSpecs.getRepositories()) {
if (commits.get(repository).getAllCommits().size() == 0) {
// don't push repositories when we know that there is nothing to push.
continue;
}
@@ -185,7 +181,7 @@ public final class GitPusher {
}
@NotNull
private GitPushRepoResult pushRepository(@NotNull GitBranchPair pushSpec,
private GitPushRepoResult pushRepository(@NotNull GitPushSpec pushSpec,
@NotNull GitRepository repository, @NotNull GitCommitsByRepoAndBranch commits) {
GitSimplePushResult simplePushResult = pushAndGetSimpleResult(repository, pushSpec);
String output = simplePushResult.getOutput();
@@ -238,7 +234,7 @@ public final class GitPusher {
}
@NotNull
private GitSimplePushResult pushAndGetSimpleResult(@NotNull GitRepository repository, @NotNull GitBranchPair pushSpec) {
private GitSimplePushResult pushAndGetSimpleResult(@NotNull GitRepository repository, @NotNull GitPushSpec pushSpec) {
if (pushSpec.getDest() == NO_TARGET_BRANCH) {
return GitSimplePushResult.notPushed();
}
@@ -304,7 +300,7 @@ public final class GitPusher {
}
@NotNull
private static String formPushSpec(@NotNull GitBranchPair spec, @NotNull GitRemote remote) {
private static String formPushSpec(@NotNull GitPushSpec spec, @NotNull GitRemote remote) {
String destWithRemote = spec.getDest().getName();
String prefix = remote.getName() + "/";
String destName;
@@ -319,7 +315,7 @@ public final class GitPusher {
}
@NotNull
private GitSimplePushResult pushNatively(GitRepository repository, GitBranchPair pushSpec) {
private GitSimplePushResult pushNatively(GitRepository repository, GitPushSpec pushSpec) {
GitPushRejectedDetector rejectedDetector = new GitPushRejectedDetector();
GitCommandResult res = myGit.push(repository, pushSpec, rejectedDetector);
if (rejectedDetector.rejected()) {
@@ -436,12 +432,11 @@ public final class GitPusher {
@NotNull
private static GitPushSpecs retain(@NotNull GitPushSpecs initialSpecs, @NotNull Map<GitRepository, GitBranch> branchesToContinue) {
GitPushSpecs specs = new GitPushSpecs();
for (Map.Entry<GitRepository, GitBranchPair> entry : initialSpecs.getAllSpecs().entrySet()) {
GitRepository repository = entry.getKey();
specs.put(repository, entry.getValue(), initialSpecs.isSelected(repository) && branchesToContinue.keySet().contains(repository));
Map<GitRepository, GitPushSpec> specs = new HashMap<GitRepository, GitPushSpec>();
for (Map.Entry<GitRepository, GitBranch> entry : branchesToContinue.entrySet()) {
specs.put(entry.getKey(), initialSpecs.get(entry.getKey()));
}
return specs;
return new GitPushSpecs(specs);
}
private void saveUpdateSettings(@NotNull UpdateSettings updateSettings) {
@@ -161,7 +161,7 @@ public class GitMergeUpdater extends GitUpdater {
// git log --name-status master..origin/master
GitBranchPair gitBranchPair = myTrackedBranches.get(myRoot);
String currentBranch = gitBranchPair.getSource().getName();
String currentBranch = gitBranchPair.getBranch().getName();
String remoteBranch = gitBranchPair.getDest().getName();
try {
GitRepository repository = GitUtil.getRepositoryManager(myProject).getRepositoryForRoot(myRoot);
@@ -107,7 +107,7 @@ public class GitRebaseUpdater extends GitUpdater {
GitBranchPair gitBranchPair = myTrackedBranches.get(myRoot);
GitBranch dest = gitBranchPair.getDest();
LOG.assertTrue(dest != null, String.format("Destination branch is null for source branch %s in %s",
gitBranchPair.getSource().getName(), myRoot));
gitBranchPair.getBranch().getName(), myRoot));
return dest.getName();
}
@@ -134,7 +134,7 @@ public abstract class GitUpdater {
*/
public boolean isUpdateNeeded() throws VcsException {
GitBranchPair gitBranchPair = myTrackedBranches.get(myRoot);
String currentBranch = gitBranchPair.getSource().getName();
String currentBranch = gitBranchPair.getBranch().getName();
GitBranch dest = gitBranchPair.getDest();
assert dest != null;
String remoteBranch = dest.getName();
@@ -21,12 +21,12 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vcs.VcsException
import com.intellij.openapi.vfs.VirtualFile
import git4idea.branch.GitBranchPair
import git4idea.commands.Git
import git4idea.commands.GitCommandResult
import git4idea.commands.GitImpl
import git4idea.commands.GitLineHandlerListener
import git4idea.history.browser.GitCommit
import git4idea.push.GitPushSpec
import git4idea.repo.GitRepository
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
@@ -180,7 +180,7 @@ public class GitTestImpl implements Git {
@NotNull
@Override
public GitCommandResult push(@NotNull GitRepository repository,
@NotNull GitBranchPair pushSpec,
@NotNull GitPushSpec pushSpec,
@NotNull GitLineHandlerListener... listeners) {
throw new UnsupportedOperationException();
}
@@ -19,11 +19,11 @@ import com.intellij.execution.process.ProcessOutputTypes
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.containers.hash.HashMap
import git4idea.branch.GitBranchPair
import git4idea.commands.Git
import git4idea.commands.GitCommandResult
import git4idea.commands.GitLineHandlerListener
import git4idea.history.browser.GitCommit
import git4idea.push.GitPushSpec
import git4idea.repo.GitRepository
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
@@ -169,7 +169,7 @@ class MockGit implements Git {
@NotNull
@Override
GitCommandResult push(@NotNull GitRepository repository, @NotNull GitBranchPair pushSpec, @NotNull GitLineHandlerListener... listeners) {
GitCommandResult push(@NotNull GitRepository repository, @NotNull GitPushSpec pushSpec, @NotNull GitLineHandlerListener... listeners) {
throw new UnsupportedOperationException()
}
@@ -15,7 +15,6 @@
*/
package git4idea.push
import git4idea.branch.GitBranchPair
import git4idea.repo.GitRepository
import git4idea.test.GitExecutor
import git4idea.test.GitLightTest
@@ -97,7 +96,7 @@ class GitPushSpecTest extends GitLightTest {
}
void assertSpec(String local, String remote) {
GitBranchPair spec = GitBranchPair.findCurrentAnTracked(myRepository)
GitPushSpec spec = GitPushSpec.collect(myRepository)
assertEquals("Incorrect local branch to push", local, spec.getSource().getName())
if (remote != null) {
assertEquals("Incorrect remote branch to push", remote, spec.getDest().getNameForLocalOperations())