1 /*******************************************************************************
2 * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
3 * Copyright (C) 2011, Mathias Kinzler <mathias.kinzler@sap.com>
4 * Copyright (C) 2012, Robin Stocker <robin@nibor.org>
5 * Copyright (C) 2015, Stephan Hackstedt <stephan.hackstedt@googlemail.com>
6 * Copyright (C) 2016, Thomas Wolf <thomas.wolf@paranor.ch>
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License 2.0
10 * which accompanies this distribution, and is available at
11 * https://www.eclipse.org/legal/epl-2.0/
13 * SPDX-License-Identifier: EPL-2.0
14 *******************************************************************************/
15 package org
.eclipse
.egit
.core
.op
;
17 import java
.io
.OutputStream
;
18 import java
.lang
.reflect
.InvocationTargetException
;
19 import java
.net
.URISyntaxException
;
20 import java
.util
.Collection
;
22 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
23 import org
.eclipse
.core
.runtime
.SubMonitor
;
24 import org
.eclipse
.egit
.core
.Activator
;
25 import org
.eclipse
.egit
.core
.EclipseGitProgressTransformer
;
26 import org
.eclipse
.egit
.core
.internal
.CoreText
;
27 import org
.eclipse
.jgit
.api
.Git
;
28 import org
.eclipse
.jgit
.api
.errors
.JGitInternalException
;
29 import org
.eclipse
.jgit
.lib
.Repository
;
30 import org
.eclipse
.jgit
.transport
.CredentialsProvider
;
31 import org
.eclipse
.jgit
.transport
.PushResult
;
32 import org
.eclipse
.jgit
.transport
.RemoteConfig
;
33 import org
.eclipse
.jgit
.transport
.RemoteRefUpdate
;
34 import org
.eclipse
.jgit
.transport
.RemoteRefUpdate
.Status
;
35 import org
.eclipse
.jgit
.transport
.Transport
;
36 import org
.eclipse
.jgit
.transport
.URIish
;
37 import org
.eclipse
.osgi
.util
.NLS
;
40 * Push operation: pushing from local repository to one or many remote ones.
42 public class PushOperation
{
44 private final Repository localDb
;
46 private final PushOperationSpecification specification
;
48 private final boolean dryRun
;
50 private final String remoteName
;
52 private final int timeout
;
54 private OutputStream out
;
56 private PushOperationResult operationResult
;
58 private CredentialsProvider credentialsProvider
;
61 * Create push operation for provided specification.
65 * @param specification
66 * specification of ref updates for remote repositories.
68 * true if push operation should just check for possible result
69 * and not really update remote refs, false otherwise - when push
70 * should act normally.
72 * the timeout in seconds (0 for no timeout)
74 public PushOperation(final Repository localDb
,
75 final PushOperationSpecification specification
,
76 final boolean dryRun
, int timeout
) {
77 this(localDb
, null, specification
, dryRun
, timeout
);
81 * Creates a push operation for a remote configuration.
88 public PushOperation(final Repository localDb
, final String remoteName
,
89 final boolean dryRun
, int timeout
) {
90 this(localDb
, remoteName
, null, dryRun
, timeout
);
93 private PushOperation(final Repository localDb
, final String remoteName
,
94 PushOperationSpecification specification
, final boolean dryRun
,
96 this.localDb
= localDb
;
97 this.specification
= specification
;
99 this.remoteName
= remoteName
;
100 this.timeout
= timeout
;
104 * @param credentialsProvider
106 public void setCredentialsProvider(CredentialsProvider credentialsProvider
) {
107 this.credentialsProvider
= credentialsProvider
;
111 * @return the operation's credentials provider
113 public CredentialsProvider
getCredentialsProvider() {
114 return credentialsProvider
;
118 * @return push operation result
120 public PushOperationResult
getOperationResult() {
121 if (operationResult
== null)
122 throw new IllegalStateException(CoreText
.OperationNotYetExecuted
);
123 return operationResult
;
127 * @return operation specification, as provided in constructor (may be
130 public PushOperationSpecification
getSpecification() {
131 return specification
;
136 * may be <code>null</code> if progress monitoring is not desired
137 * @throws InvocationTargetException
138 * not really used: failure is communicated via the result (see
139 * {@link #getOperationResult()})
141 public void run(IProgressMonitor actMonitor
)
142 throws InvocationTargetException
{
144 if (operationResult
!= null)
145 throw new IllegalStateException(CoreText
.OperationAlreadyExecuted
);
147 if (this.specification
!= null)
148 for (URIish uri
: this.specification
.getURIs()) {
149 for (RemoteRefUpdate update
: this.specification
151 if (update
.getStatus() != Status
.NOT_ATTEMPTED
)
152 throw new IllegalStateException(
153 CoreText
.RemoteRefUpdateCantBeReused
);
157 if (specification
!= null) {
158 totalWork
= specification
.getURIsNumber();
163 String taskName
= dryRun ? CoreText
.PushOperation_taskNameDryRun
164 : CoreText
.PushOperation_taskNameNormalRun
;
165 SubMonitor progress
= SubMonitor
.convert(actMonitor
, totalWork
);
166 progress
.setTaskName(taskName
);
168 operationResult
= new PushOperationResult();
169 try (Git git
= new Git(localDb
)) {
170 if (specification
!= null)
171 for (final URIish uri
: specification
.getURIs()) {
172 if (progress
.isCanceled()) {
173 operationResult
.addOperationResult(uri
,
174 CoreText
.PushOperation_resultCancelled
);
179 Collection
<RemoteRefUpdate
> refUpdates
= specification
181 final EclipseGitProgressTransformer gitSubMonitor
= new EclipseGitProgressTransformer(
182 progress
.newChild(1));
184 try (Transport transport
= Transport
.open(localDb
, uri
)) {
185 transport
.setDryRun(dryRun
);
186 transport
.setTimeout(timeout
);
187 if (credentialsProvider
!= null) {
188 transport
.setCredentialsProvider(
189 credentialsProvider
);
191 PushResult result
= transport
.push(gitSubMonitor
,
194 operationResult
.addOperationResult(result
.getURI(),
196 specification
.addURIRefUpdates(result
.getURI(),
197 result
.getRemoteUpdates());
198 } catch (JGitInternalException e
) {
199 String errorMessage
= e
.getCause() != null
200 ? e
.getCause().getMessage() : e
.getMessage();
201 String userMessage
= NLS
.bind(
202 CoreText
.PushOperation_InternalExceptionOccurredMessage
,
204 handleException(uri
, e
, userMessage
);
205 } catch (Exception e
) {
206 handleException(uri
, e
, e
.getMessage());
210 final EclipseGitProgressTransformer gitMonitor
= new EclipseGitProgressTransformer(
211 progress
.newChild(totalWork
));
213 Iterable
<PushResult
> results
= git
.push()
214 .setRemote(remoteName
).setDryRun(dryRun
)
215 .setTimeout(timeout
).setProgressMonitor(gitMonitor
)
216 .setCredentialsProvider(credentialsProvider
)
217 .setOutputStream(out
).call();
218 for (PushResult result
: results
) {
219 operationResult
.addOperationResult(result
.getURI(),
222 } catch (JGitInternalException e
) {
223 String errorMessage
= e
.getCause() != null
224 ? e
.getCause().getMessage() : e
.getMessage();
225 String userMessage
= NLS
.bind(
226 CoreText
.PushOperation_InternalExceptionOccurredMessage
,
228 URIish uri
= getPushURIForErrorHandling();
229 handleException(uri
, e
, userMessage
);
230 } catch (Exception e
) {
231 URIish uri
= getPushURIForErrorHandling();
232 handleException(uri
, e
, e
.getMessage());
238 private void handleException(final URIish uri
, Exception e
,
239 String userMessage
) {
242 operationResult
.addOperationResult(uri
, userMessage
);
243 uriString
= uri
.toString();
245 uriString
= "retrieving URI failed"; //$NON-NLS-1$
247 String userMessageForUri
= NLS
.bind(
248 CoreText
.PushOperation_ExceptionOccurredDuringPushOnUriMessage
,
249 uriString
, userMessage
);
250 Activator
.logError(userMessageForUri
, e
);
253 private URIish
getPushURIForErrorHandling() {
254 RemoteConfig rc
= null;
256 rc
= new RemoteConfig(localDb
.getConfig(), remoteName
);
257 return rc
.getPushURIs().isEmpty() ? rc
.getURIs().get(0) : rc
258 .getPushURIs().get(0);
259 } catch (URISyntaxException e
) {
261 Activator
.logError("Reading RemoteConfig failed", e
); //$NON-NLS-1$
267 * Sets the output stream this operation will write sideband messages to.
270 * the outputstream to write to
273 public void setOutputStream(OutputStream out
) {