[git] IDEA-132736 Define remote right from the push dialog
[idea/community.git] / plugins / git4idea / src / git4idea / push / GitPushSupport.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package git4idea.push;
17
18 import com.intellij.dvcs.push.*;
19 import com.intellij.dvcs.repo.RepositoryManager;
20 import com.intellij.openapi.components.ServiceManager;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.util.Condition;
23 import com.intellij.openapi.vcs.AbstractVcs;
24 import com.intellij.util.ObjectUtils;
25 import com.intellij.util.containers.ContainerUtil;
26 import git4idea.*;
27 import git4idea.branch.GitBranchUtil;
28 import git4idea.config.GitSharedSettings;
29 import git4idea.config.GitVcsSettings;
30 import git4idea.config.GitVersionSpecialty;
31 import git4idea.repo.GitBranchTrackInfo;
32 import git4idea.repo.GitRemote;
33 import git4idea.repo.GitRepository;
34 import git4idea.repo.GitRepositoryManager;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37
38 import java.util.Collection;
39
40 public class GitPushSupport extends PushSupport<GitRepository, GitPushSource, GitPushTarget> {
41
42   @NotNull private final GitRepositoryManager myRepositoryManager;
43   @NotNull private final GitVcs myVcs;
44   @NotNull private final Pusher<GitRepository, GitPushSource, GitPushTarget> myPusher;
45   @NotNull private final OutgoingCommitsProvider<GitRepository, GitPushSource, GitPushTarget> myOutgoingCommitsProvider;
46   @NotNull private final GitVcsSettings mySettings;
47   private final GitSharedSettings mySharedSettings;
48   @NotNull private final PushSettings myCommonPushSettings;
49
50   // instantiated from plugin.xml
51   @SuppressWarnings("UnusedDeclaration")
52   private GitPushSupport(@NotNull Project project, @NotNull GitRepositoryManager repositoryManager) {
53     myRepositoryManager = repositoryManager;
54     myVcs = ObjectUtils.assertNotNull(GitVcs.getInstance(project));
55     mySettings = GitVcsSettings.getInstance(project);
56     myPusher = new GitPusher(project, mySettings, this);
57     myOutgoingCommitsProvider = new GitOutgoingCommitsProvider(project);
58     mySharedSettings = ServiceManager.getService(project, GitSharedSettings.class);
59     myCommonPushSettings = ServiceManager.getService(project, PushSettings.class);
60   }
61
62   @NotNull
63   @Override
64   public AbstractVcs getVcs() {
65     return myVcs;
66   }
67
68   @NotNull
69   @Override
70   public Pusher<GitRepository, GitPushSource, GitPushTarget> getPusher() {
71     return myPusher;
72   }
73
74   @NotNull
75   @Override
76   public OutgoingCommitsProvider<GitRepository, GitPushSource, GitPushTarget> getOutgoingCommitsProvider() {
77     return myOutgoingCommitsProvider;
78   }
79
80   @Nullable
81   @Override
82   public GitPushTarget getDefaultTarget(@NotNull GitRepository repository) {
83     if (repository.isFresh()) {
84       return null;
85     }
86     GitLocalBranch currentBranch = repository.getCurrentBranch();
87     if (currentBranch == null) {
88       return null;
89     }
90
91     GitPushTarget persistedTarget = getPersistedTarget(repository, currentBranch);
92     if (persistedTarget != null) {
93       return persistedTarget;
94     }
95
96     GitBranchTrackInfo trackInfo = GitBranchUtil.getTrackInfoForBranch(repository, currentBranch);
97     if (trackInfo != null) {
98       return new GitPushTarget(trackInfo.getRemoteBranch(), false);
99     }
100     return proposeTargetForNewBranch(repository, currentBranch);
101   }
102
103   @Nullable
104   private GitPushTarget getPersistedTarget(@NotNull GitRepository repository, @NotNull GitLocalBranch branch) {
105     GitRemoteBranch target = mySettings.getPushTarget(repository, branch.getName());
106     return target == null ? null : new GitPushTarget(target, !repository.getBranches().getRemoteBranches().contains(target));
107   }
108
109   private static GitPushTarget proposeTargetForNewBranch(GitRepository repository, GitLocalBranch currentBranch) {
110     Collection<GitRemote> remotes = repository.getRemotes();
111     if (remotes.isEmpty()) {
112       return null; // TODO need to propose to declare new remote
113     }
114     else if (remotes.size() == 1) {
115       return makeTargetForNewBranch(repository, remotes.iterator().next(), currentBranch);
116     }
117     else {
118       GitRemote remote = GitUtil.getDefaultRemote(remotes);
119       if (remote == null) {
120         remote = remotes.iterator().next();
121       }
122       return makeTargetForNewBranch(repository, remote, currentBranch);
123     }
124   }
125
126   @NotNull
127   private static GitPushTarget makeTargetForNewBranch(@NotNull GitRepository repository,
128                                                       @NotNull GitRemote remote,
129                                                       @NotNull GitLocalBranch currentBranch) {
130     GitRemoteBranch existingRemoteBranch = GitUtil.findRemoteBranch(repository, remote, currentBranch.getName());
131     if (existingRemoteBranch != null) {
132       return new GitPushTarget(existingRemoteBranch, false);
133     }
134     return new GitPushTarget(new GitStandardRemoteBranch(remote, currentBranch.getName(), GitBranch.DUMMY_HASH), true);
135   }
136
137   @NotNull
138   @Override
139   public GitPushSource getSource(@NotNull GitRepository repository) {
140     GitLocalBranch currentBranch = repository.getCurrentBranch();
141     return currentBranch != null
142            ? GitPushSource.create(currentBranch)
143            : GitPushSource.create(ObjectUtils.assertNotNull(repository.getCurrentRevision())); // fresh repository is on branch
144   }
145
146   @NotNull
147   @Override
148   public RepositoryManager<GitRepository> getRepositoryManager() {
149     return myRepositoryManager;
150   }
151
152   @NotNull
153   @Override
154   public PushTargetPanel<GitPushTarget> createTargetPanel(@NotNull GitRepository repository, @Nullable GitPushTarget defaultTarget) {
155     return new GitPushTargetPanel(this, repository, defaultTarget);
156   }
157
158   @Override
159   public boolean isForcePushAllowed(@NotNull GitRepository repo, @NotNull GitPushTarget target) {
160     final String targetBranch = target.getBranch().getNameForRemoteOperations();
161     return !ContainerUtil.exists(mySharedSettings.getForcePushProhibitedPatterns(), new Condition<String>() {
162       @Override
163       public boolean value(String pattern) {
164         return targetBranch.matches("^" + pattern + "$"); // let "master" match only "master" and not "any-master-here" by default
165       }
166     });
167   }
168
169   @Override
170   public boolean isForcePushEnabled() {
171     return mySettings.isForcePushAllowed();
172   }
173
174   @Nullable
175   @Override
176   public VcsPushOptionsPanel createOptionsPanel() {
177     return new GitPushTagPanel(mySettings.getPushTagMode(), GitVersionSpecialty.SUPPORTS_FOLLOW_TAGS.existsIn(myVcs.getVersion()));
178   }
179
180   @Override
181   public boolean isSilentForcePushAllowed(@NotNull GitPushTarget target) {
182     return myCommonPushSettings.containsForcePushTarget(target.getBranch().getRemote().getName(),
183                                                         target.getBranch().getNameForRemoteOperations());
184   }
185
186   @Override
187   public void saveSilentForcePushTarget(@NotNull GitPushTarget target) {
188     myCommonPushSettings.addForcePushTarget(target.getBranch().getRemote().getName(), target.getBranch().getNameForRemoteOperations());
189   }
190 }