diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml
index fc95f022af66..df65fd215cf1 100644
--- a/plugins/git4idea/src/META-INF/plugin.xml
+++ b/plugins/git4idea/src/META-INF/plugin.xml
@@ -235,5 +235,7 @@
+
diff --git a/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java b/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java
index d2d1ab5d3729..6fb2c7df5b85 100644
--- a/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java
+++ b/plugins/git4idea/src/git4idea/branch/GitDeleteRemoteBranchOperation.java
@@ -24,6 +24,7 @@ import com.intellij.openapi.vcs.VcsNotifier;
import git4idea.commands.Git;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitCompoundResult;
+import git4idea.push.GitPushParamsImpl;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.ui.branch.GitMultiRootBranchConfig;
@@ -162,7 +163,7 @@ class GitDeleteRemoteBranchOperation extends GitBranchOperation {
@NotNull
private GitCommandResult pushDeletion(@NotNull GitRepository repository, @NotNull GitRemote remote, @NotNull String branchName) {
- return myGit.push(repository, remote, ":" + branchName, false, false, false, null);
+ return myGit.push(repository, new GitPushParamsImpl(remote, ":" + branchName, false, false, false, null));
}
@Nullable
diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java
index cdd988c3fe79..3fdf65c8fcfd 100644
--- a/plugins/git4idea/src/git4idea/commands/Git.java
+++ b/plugins/git4idea/src/git4idea/commands/Git.java
@@ -21,6 +21,7 @@ import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.branch.GitRebaseParams;
+import git4idea.push.GitPushParams;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.reset.GitResetMode;
@@ -144,12 +145,7 @@ public interface Git {
@NotNull
GitCommandResult push(@NotNull GitRepository repository,
- @NotNull GitRemote remote,
- @NotNull String spec,
- boolean force,
- boolean updateTracking,
- boolean skipHook,
- @Nullable String tagMode,
+ @NotNull GitPushParams pushParams,
GitLineHandlerListener... listeners);
@NotNull
diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java
index 7f106ffed30c..5a69903e3b89 100644
--- a/plugins/git4idea/src/git4idea/commands/GitImpl.java
+++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java
@@ -27,6 +27,7 @@ import com.intellij.vcsUtil.VcsFileUtil;
import git4idea.GitVcs;
import git4idea.branch.GitRebaseParams;
import git4idea.config.GitVersionSpecialty;
+import git4idea.push.GitPushParams;
import git4idea.rebase.GitInteractiveRebaseEditorHandler;
import git4idea.rebase.GitRebaseEditorHandler;
import git4idea.rebase.GitRebaseEditorService;
@@ -38,8 +39,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
-import java.io.OutputStreamWriter;
-import java.nio.file.Paths;
import java.util.*;
import static git4idea.GitUtil.COMMENT_CHAR;
@@ -399,14 +398,10 @@ public class GitImpl extends GitImplBase {
@Override
@NotNull
public GitCommandResult push(@NotNull GitRepository repository,
- @NotNull GitRemote remote,
- @NotNull String spec,
- boolean force,
- boolean updateTracking,
- boolean skipHook,
- @Nullable String tagMode,
+ @NotNull GitPushParams pushParams,
GitLineHandlerListener... listeners) {
- return doPush(repository, remote.getName(), remote.getPushUrls(), spec, force, updateTracking, skipHook, tagMode, listeners);
+ return doPush(repository, pushParams.getRemote().getName(), pushParams.getRemote().getPushUrls(), pushParams.getSpec(),
+ pushParams.isForce(), pushParams.shouldSetupTracking(), pushParams.shouldSkipHooks(), pushParams.getTagMode(), listeners);
}
@NotNull
diff --git a/plugins/git4idea/src/git4idea/push/GitPushOperation.java b/plugins/git4idea/src/git4idea/push/GitPushOperation.java
index 441429e3819b..9655119a16ae 100644
--- a/plugins/git4idea/src/git4idea/push/GitPushOperation.java
+++ b/plugins/git4idea/src/git4idea/push/GitPushOperation.java
@@ -65,6 +65,7 @@ import static com.intellij.util.containers.ContainerUtil.filter;
import static git4idea.commands.GitAuthenticationListener.GIT_AUTHENTICATION_SUCCESS;
import static git4idea.push.GitPushNativeResult.Type.FORCED_UPDATE;
import static git4idea.push.GitPushNativeResult.Type.NEW_REF;
+import static git4idea.push.GitPushProcessCustomizationFactory.GIT_PUSH_CUSTOMIZATION_FACTORY_EP;
import static git4idea.push.GitPushRepoResult.Type.NOT_PUSHED;
import static git4idea.push.GitPushRepoResult.Type.REJECTED_NO_FF;
@@ -92,6 +93,7 @@ public class GitPushOperation {
private final ProgressIndicator myProgressIndicator;
private final GitVcsSettings mySettings;
private final GitRepositoryManager myRepositoryManager;
+ @Nullable private final GitPushProcessCustomizationFactory.GitPushProcessCustomization myPushProcessCustomization;
public GitPushOperation(@NotNull Project project,
@NotNull GitPushSupport pushSupport,
@@ -121,6 +123,8 @@ public class GitPushOperation {
currentHeads.put(repository, new GitRevisionNumber(head));
}
}
+
+ myPushProcessCustomization = findPushCustomization();
}
@NotNull
@@ -188,6 +192,7 @@ public class GitPushOperation {
}
}
}
+ if (myPushProcessCustomization != null) myPushProcessCustomization.executeAfterPush(results);
}
finally {
if (beforePushLabel != null) {
@@ -200,6 +205,24 @@ public class GitPushOperation {
return prepareCombinedResult(results, updatedRoots, preUpdatePositions, beforePushLabel, afterPushLabel);
}
+ @Nullable
+ private GitPushProcessCustomizationFactory.GitPushProcessCustomization findPushCustomization() {
+ List customizations = StreamEx
+ .of(GIT_PUSH_CUSTOMIZATION_FACTORY_EP.getExtensions())
+ .map(factory -> factory.createCustomization(myProject, myPushSpecs, myForce)).toList();
+
+ if (customizations.isEmpty()) {
+ return null;
+ }
+ else if (customizations.size() > 1) {
+ LOG.error("Only one GitPushProcessCustomization is allowed, but more are installed: " + customizations);
+ return null;
+ }
+ else {
+ return customizations.get(0);
+ }
+ }
+
@NotNull
private Collection getRootsToUpdate(@NotNull PushUpdateSettings updateSettings,
@NotNull Set rejectedRepositories) {
@@ -303,6 +326,10 @@ public class GitPushOperation {
results.put(repository, repoResult);
}
+ if (myPushProcessCustomization != null) {
+ return myPushProcessCustomization.executeAfterPushIteration(results);
+ }
+
// fill other not-processed repositories as not-pushed
for (GitRepository repository : repositories) {
if (!results.containsKey(repository)) {
@@ -358,8 +385,17 @@ public class GitPushOperation {
String spec = sourceBranch.getFullName() + ":" + targetBranch.getNameForRemoteOperations();
GitRemote remote = targetBranch.getRemote();
- GitCommandResult res = myGit.push(repository, remote, spec, myForce, setUpstream, mySkipHook, tagMode, progressListener);
- if(res.success()){
+ GitPushParamsImpl params = new GitPushParamsImpl(remote, spec, myForce, setUpstream, mySkipHook, tagMode);
+
+ GitCommandResult res;
+ if (myPushProcessCustomization != null) {
+ res = myPushProcessCustomization.runPushCommand(repository, pushSpec, params, progressListener);
+ }
+ else {
+ res = myGit.push(repository, params, progressListener);
+ }
+
+ if (res.success()) {
BackgroundTaskUtil.syncPublisher(myProject, GIT_AUTHENTICATION_SUCCESS).authenticationSucceeded(repository, remote);
}
return new ResultWithOutput(res);
diff --git a/plugins/git4idea/src/git4idea/push/GitPushParams.java b/plugins/git4idea/src/git4idea/push/GitPushParams.java
new file mode 100644
index 000000000000..0b823c26d749
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/push/GitPushParams.java
@@ -0,0 +1,24 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package git4idea.push;
+
+import git4idea.repo.GitRemote;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public interface GitPushParams {
+ @NotNull
+ GitRemote getRemote();
+
+ @NotNull
+ String getSpec();
+
+ boolean isForce();
+
+ boolean shouldSetupTracking();
+
+ boolean shouldSkipHooks();
+
+ @Nullable
+ String getTagMode();
+}
+
diff --git a/plugins/git4idea/src/git4idea/push/GitPushParamsImpl.java b/plugins/git4idea/src/git4idea/push/GitPushParamsImpl.java
new file mode 100644
index 000000000000..5d297aab185b
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/push/GitPushParamsImpl.java
@@ -0,0 +1,63 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package git4idea.push;
+
+import git4idea.repo.GitRemote;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class GitPushParamsImpl implements GitPushParams {
+ @NotNull private final GitRemote myRemote;
+ @NotNull private final String mySpec;
+ private final boolean myForce;
+ private final boolean mySetupTracking;
+ private final boolean mySkipHooks;
+ @Nullable private final String myTagMode;
+
+ public GitPushParamsImpl(@NotNull GitRemote remote,
+ @NotNull String spec,
+ boolean force,
+ boolean setupTracking,
+ boolean skipHooks,
+ @Nullable String tagMode) {
+
+ myRemote = remote;
+ mySpec = spec;
+ myForce = force;
+ mySetupTracking = setupTracking;
+ mySkipHooks = skipHooks;
+ myTagMode = tagMode;
+ }
+
+ @NotNull
+ @Override
+ public GitRemote getRemote() {
+ return myRemote;
+ }
+
+ @NotNull
+ @Override
+ public String getSpec() {
+ return mySpec;
+ }
+
+ @Override
+ public boolean isForce() {
+ return myForce;
+ }
+
+ @Override
+ public boolean shouldSetupTracking() {
+ return mySetupTracking;
+ }
+
+ @Override
+ public boolean shouldSkipHooks() {
+ return mySkipHooks;
+ }
+
+ @Nullable
+ @Override
+ public String getTagMode() {
+ return myTagMode;
+ }
+}
diff --git a/plugins/git4idea/src/git4idea/push/GitPushProcessCustomizationFactory.java b/plugins/git4idea/src/git4idea/push/GitPushProcessCustomizationFactory.java
new file mode 100644
index 000000000000..1dad85a69142
--- /dev/null
+++ b/plugins/git4idea/src/git4idea/push/GitPushProcessCustomizationFactory.java
@@ -0,0 +1,60 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package git4idea.push;
+
+import com.intellij.dvcs.push.PushSpec;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import git4idea.commands.GitCommandResult;
+import git4idea.commands.GitLineHandlerListener;
+import git4idea.repo.GitRepository;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+
+/**
+ * Implement to customize some parts of the {@link GitPushOperation git push process}.
+ *
+ * Note that only one customization can be installed: if several ones are found among installed plugins, none is being executed.
+ */
+public interface GitPushProcessCustomizationFactory {
+
+ ExtensionPointName GIT_PUSH_CUSTOMIZATION_FACTORY_EP =
+ ExtensionPointName.create("com.intellij.vcs.git.pushCustomizationFactory");
+
+ /**
+ * Creates a push customization instance for a push session, if such customization is allowed in the given conditions.
+ */
+ @Nullable
+ GitPushProcessCustomization createCustomization(@NotNull Project project,
+ @NotNull Map> pushSpecs,
+ boolean forcePush);
+
+ interface GitPushProcessCustomization {
+
+ /**
+ * Will be executed after one push iteration,
+ * i.e. after all `git push` commands are called against all selected repositories,
+ * but before the rebase/merge is proposed or a notification is shown.
+ *
+ * @param results Results of `git push` commands.
+ * @return Adjusted results of the push process - these will be processed by the push process instead of the original ones.
+ */
+ @NotNull
+ Map executeAfterPushIteration(@NotNull Map results);
+
+ /**
+ * Overrides the call to `git push`.
+ */
+ @NotNull
+ GitCommandResult runPushCommand(@NotNull GitRepository repository,
+ @NotNull PushSpec pushSpec,
+ @NotNull GitPushParams pushParams,
+ @NotNull GitLineHandlerListener progressListener);
+
+ /**
+ * Will be executed after the whole push procedure is finished, successfully or not.
+ */
+ void executeAfterPush(@NotNull Map results);
+ }
+}
diff --git a/plugins/git4idea/tests/git4idea/test/TestGit.kt b/plugins/git4idea/tests/git4idea/test/TestGit.kt
index da1cf3d45190..e6f4d344ef48 100644
--- a/plugins/git4idea/tests/git4idea/test/TestGit.kt
+++ b/plugins/git4idea/tests/git4idea/test/TestGit.kt
@@ -24,9 +24,9 @@ import git4idea.commands.GitCommandResult
import git4idea.commands.GitImpl
import git4idea.commands.GitLineHandler
import git4idea.commands.GitLineHandlerListener
+import git4idea.push.GitPushParams
import git4idea.rebase.GitInteractiveRebaseEditorHandler
import git4idea.rebase.GitRebaseEditorService
-import git4idea.repo.GitRemote
import git4idea.repo.GitRepository
import java.io.File
@@ -51,16 +51,10 @@ class TestGitImpl : GitImpl() {
val plainTextEditor: ((String) -> String)?)
override fun push(repository: GitRepository,
- remote: GitRemote,
- spec: String,
- force: Boolean,
- updateTracking: Boolean,
- skipHook: Boolean,
- tagMode: String?,
+ pushParams: GitPushParams,
vararg listeners: GitLineHandlerListener): GitCommandResult {
pushListener?.invoke(repository)
- return myPushHandler(repository) ?:
- super.push(repository, remote, spec, force, updateTracking, skipHook, tagMode, *listeners)
+ return myPushHandler(repository) ?: super.push(repository, pushParams, *listeners)
}
override fun branchDelete(repository: GitRepository,