You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PushCommand.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /*
  2. * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.api;
  11. import java.io.IOException;
  12. import java.io.OutputStream;
  13. import java.net.URISyntaxException;
  14. import java.text.MessageFormat;
  15. import java.util.ArrayList;
  16. import java.util.Arrays;
  17. import java.util.Collection;
  18. import java.util.Collections;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. import org.eclipse.jgit.api.errors.GitAPIException;
  23. import org.eclipse.jgit.api.errors.InvalidRemoteException;
  24. import org.eclipse.jgit.api.errors.JGitInternalException;
  25. import org.eclipse.jgit.errors.NotSupportedException;
  26. import org.eclipse.jgit.errors.TooLargeObjectInPackException;
  27. import org.eclipse.jgit.errors.TooLargePackException;
  28. import org.eclipse.jgit.errors.TransportException;
  29. import org.eclipse.jgit.internal.JGitText;
  30. import org.eclipse.jgit.lib.Constants;
  31. import org.eclipse.jgit.lib.NullProgressMonitor;
  32. import org.eclipse.jgit.lib.ProgressMonitor;
  33. import org.eclipse.jgit.lib.Ref;
  34. import org.eclipse.jgit.lib.Repository;
  35. import org.eclipse.jgit.transport.PushResult;
  36. import org.eclipse.jgit.transport.RefLeaseSpec;
  37. import org.eclipse.jgit.transport.RefSpec;
  38. import org.eclipse.jgit.transport.RemoteConfig;
  39. import org.eclipse.jgit.transport.RemoteRefUpdate;
  40. import org.eclipse.jgit.transport.Transport;
  41. /**
  42. * A class used to execute a {@code Push} command. It has setters for all
  43. * supported options and arguments of this command and a {@link #call()} method
  44. * to finally execute the command.
  45. *
  46. * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-push.html"
  47. * >Git documentation about Push</a>
  48. */
  49. public class PushCommand extends
  50. TransportCommand<PushCommand, Iterable<PushResult>> {
  51. private String remote = Constants.DEFAULT_REMOTE_NAME;
  52. private final List<RefSpec> refSpecs;
  53. private final Map<String, RefLeaseSpec> refLeaseSpecs;
  54. private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
  55. private String receivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
  56. private boolean dryRun;
  57. private boolean atomic;
  58. private boolean force;
  59. private boolean thin = Transport.DEFAULT_PUSH_THIN;
  60. private OutputStream out;
  61. private List<String> pushOptions;
  62. /**
  63. * <p>
  64. * Constructor for PushCommand.
  65. * </p>
  66. *
  67. * @param repo
  68. * the {@link org.eclipse.jgit.lib.Repository}
  69. */
  70. protected PushCommand(Repository repo) {
  71. super(repo);
  72. refSpecs = new ArrayList<>(3);
  73. refLeaseSpecs = new HashMap<>();
  74. }
  75. /**
  76. * {@inheritDoc}
  77. * <p>
  78. * Execute the {@code push} command with all the options and parameters
  79. * collected by the setter methods of this class. Each instance of this
  80. * class should only be used for one invocation of the command (means: one
  81. * call to {@link #call()})
  82. */
  83. @Override
  84. public Iterable<PushResult> call() throws GitAPIException,
  85. InvalidRemoteException,
  86. org.eclipse.jgit.api.errors.TransportException {
  87. checkCallable();
  88. ArrayList<PushResult> pushResults = new ArrayList<>(3);
  89. try {
  90. if (refSpecs.isEmpty()) {
  91. RemoteConfig config = new RemoteConfig(repo.getConfig(),
  92. getRemote());
  93. refSpecs.addAll(config.getPushRefSpecs());
  94. }
  95. if (refSpecs.isEmpty()) {
  96. Ref head = repo.exactRef(Constants.HEAD);
  97. if (head != null && head.isSymbolic())
  98. refSpecs.add(new RefSpec(head.getLeaf().getName()));
  99. }
  100. if (force) {
  101. for (int i = 0; i < refSpecs.size(); i++)
  102. refSpecs.set(i, refSpecs.get(i).setForceUpdate(true));
  103. }
  104. final List<Transport> transports;
  105. transports = Transport.openAll(repo, remote, Transport.Operation.PUSH);
  106. for (@SuppressWarnings("resource") // Explicitly closed in finally
  107. final Transport transport : transports) {
  108. transport.setPushThin(thin);
  109. transport.setPushAtomic(atomic);
  110. if (receivePack != null)
  111. transport.setOptionReceivePack(receivePack);
  112. transport.setDryRun(dryRun);
  113. transport.setPushOptions(pushOptions);
  114. configure(transport);
  115. final Collection<RemoteRefUpdate> toPush = transport
  116. .findRemoteRefUpdatesFor(refSpecs, refLeaseSpecs);
  117. try {
  118. PushResult result = transport.push(monitor, toPush, out);
  119. pushResults.add(result);
  120. } catch (TooLargePackException e) {
  121. throw new org.eclipse.jgit.api.errors.TooLargePackException(
  122. e.getMessage(), e);
  123. } catch (TooLargeObjectInPackException e) {
  124. throw new org.eclipse.jgit.api.errors.TooLargeObjectInPackException(
  125. e.getMessage(), e);
  126. } catch (TransportException e) {
  127. throw new org.eclipse.jgit.api.errors.TransportException(
  128. e.getMessage(), e);
  129. } finally {
  130. transport.close();
  131. }
  132. }
  133. } catch (URISyntaxException e) {
  134. throw new InvalidRemoteException(
  135. MessageFormat.format(JGitText.get().invalidRemote, remote),
  136. e);
  137. } catch (TransportException e) {
  138. throw new org.eclipse.jgit.api.errors.TransportException(
  139. e.getMessage(), e);
  140. } catch (NotSupportedException e) {
  141. throw new JGitInternalException(
  142. JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
  143. e);
  144. } catch (IOException e) {
  145. throw new JGitInternalException(
  146. JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
  147. e);
  148. }
  149. return pushResults;
  150. }
  151. /**
  152. * The remote (uri or name) used for the push operation. If no remote is
  153. * set, the default value of <code>Constants.DEFAULT_REMOTE_NAME</code> will
  154. * be used.
  155. *
  156. * @see Constants#DEFAULT_REMOTE_NAME
  157. * @param remote
  158. * the remote name
  159. * @return {@code this}
  160. */
  161. public PushCommand setRemote(String remote) {
  162. checkCallable();
  163. this.remote = remote;
  164. return this;
  165. }
  166. /**
  167. * Get remote name
  168. *
  169. * @return the remote used for the remote operation
  170. */
  171. public String getRemote() {
  172. return remote;
  173. }
  174. /**
  175. * The remote executable providing receive-pack service for pack transports.
  176. * If no receive-pack is set, the default value of
  177. * <code>RemoteConfig.DEFAULT_RECEIVE_PACK</code> will be used.
  178. *
  179. * @see RemoteConfig#DEFAULT_RECEIVE_PACK
  180. * @param receivePack
  181. * name of the remote executable providing the receive-pack
  182. * service
  183. * @return {@code this}
  184. */
  185. public PushCommand setReceivePack(String receivePack) {
  186. checkCallable();
  187. this.receivePack = receivePack;
  188. return this;
  189. }
  190. /**
  191. * Get the name of the remote executable providing the receive-pack service
  192. *
  193. * @return the receive-pack used for the remote operation
  194. */
  195. public String getReceivePack() {
  196. return receivePack;
  197. }
  198. /**
  199. * Get timeout used for push operation
  200. *
  201. * @return the timeout used for the push operation
  202. */
  203. public int getTimeout() {
  204. return timeout;
  205. }
  206. /**
  207. * Get the progress monitor
  208. *
  209. * @return the progress monitor for the push operation
  210. */
  211. public ProgressMonitor getProgressMonitor() {
  212. return monitor;
  213. }
  214. /**
  215. * The progress monitor associated with the push operation. By default, this
  216. * is set to <code>NullProgressMonitor</code>
  217. *
  218. * @see NullProgressMonitor
  219. * @param monitor
  220. * a {@link org.eclipse.jgit.lib.ProgressMonitor}
  221. * @return {@code this}
  222. */
  223. public PushCommand setProgressMonitor(ProgressMonitor monitor) {
  224. checkCallable();
  225. if (monitor == null) {
  226. monitor = NullProgressMonitor.INSTANCE;
  227. }
  228. this.monitor = monitor;
  229. return this;
  230. }
  231. /**
  232. * Get the <code>RefLeaseSpec</code>s.
  233. *
  234. * @return the <code>RefLeaseSpec</code>s
  235. * @since 4.7
  236. */
  237. public List<RefLeaseSpec> getRefLeaseSpecs() {
  238. return new ArrayList<>(refLeaseSpecs.values());
  239. }
  240. /**
  241. * The ref lease specs to be used in the push operation, for a
  242. * force-with-lease push operation.
  243. *
  244. * @param specs
  245. * a {@link org.eclipse.jgit.transport.RefLeaseSpec} object.
  246. * @return {@code this}
  247. * @since 4.7
  248. */
  249. public PushCommand setRefLeaseSpecs(RefLeaseSpec... specs) {
  250. return setRefLeaseSpecs(Arrays.asList(specs));
  251. }
  252. /**
  253. * The ref lease specs to be used in the push operation, for a
  254. * force-with-lease push operation.
  255. *
  256. * @param specs
  257. * list of {@code RefLeaseSpec}s
  258. * @return {@code this}
  259. * @since 4.7
  260. */
  261. public PushCommand setRefLeaseSpecs(List<RefLeaseSpec> specs) {
  262. checkCallable();
  263. this.refLeaseSpecs.clear();
  264. for (RefLeaseSpec spec : specs) {
  265. refLeaseSpecs.put(spec.getRef(), spec);
  266. }
  267. return this;
  268. }
  269. /**
  270. * Get {@code RefSpec}s.
  271. *
  272. * @return the ref specs
  273. */
  274. public List<RefSpec> getRefSpecs() {
  275. return refSpecs;
  276. }
  277. /**
  278. * The ref specs to be used in the push operation
  279. *
  280. * @param specs a {@link org.eclipse.jgit.transport.RefSpec} object.
  281. * @return {@code this}
  282. */
  283. public PushCommand setRefSpecs(RefSpec... specs) {
  284. checkCallable();
  285. this.refSpecs.clear();
  286. Collections.addAll(refSpecs, specs);
  287. return this;
  288. }
  289. /**
  290. * The ref specs to be used in the push operation
  291. *
  292. * @param specs
  293. * list of {@link org.eclipse.jgit.transport.RefSpec}s
  294. * @return {@code this}
  295. */
  296. public PushCommand setRefSpecs(List<RefSpec> specs) {
  297. checkCallable();
  298. this.refSpecs.clear();
  299. this.refSpecs.addAll(specs);
  300. return this;
  301. }
  302. /**
  303. * Push all branches under refs/heads/*.
  304. *
  305. * @return {code this}
  306. */
  307. public PushCommand setPushAll() {
  308. refSpecs.add(Transport.REFSPEC_PUSH_ALL);
  309. return this;
  310. }
  311. /**
  312. * Push all tags under refs/tags/*.
  313. *
  314. * @return {code this}
  315. */
  316. public PushCommand setPushTags() {
  317. refSpecs.add(Transport.REFSPEC_TAGS);
  318. return this;
  319. }
  320. /**
  321. * Add a reference to push.
  322. *
  323. * @param ref
  324. * the source reference. The remote name will match.
  325. * @return {@code this}.
  326. */
  327. public PushCommand add(Ref ref) {
  328. refSpecs.add(new RefSpec(ref.getLeaf().getName()));
  329. return this;
  330. }
  331. /**
  332. * Add a reference to push.
  333. *
  334. * @param nameOrSpec
  335. * any reference name, or a reference specification.
  336. * @return {@code this}.
  337. * @throws JGitInternalException
  338. * the reference name cannot be resolved.
  339. */
  340. public PushCommand add(String nameOrSpec) {
  341. if (0 <= nameOrSpec.indexOf(':')) {
  342. refSpecs.add(new RefSpec(nameOrSpec));
  343. } else {
  344. Ref src;
  345. try {
  346. src = repo.findRef(nameOrSpec);
  347. } catch (IOException e) {
  348. throw new JGitInternalException(
  349. JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
  350. e);
  351. }
  352. if (src != null)
  353. add(src);
  354. }
  355. return this;
  356. }
  357. /**
  358. * Whether to run the push operation as a dry run
  359. *
  360. * @return the dry run preference for the push operation
  361. */
  362. public boolean isDryRun() {
  363. return dryRun;
  364. }
  365. /**
  366. * Sets whether the push operation should be a dry run
  367. *
  368. * @param dryRun a boolean.
  369. * @return {@code this}
  370. */
  371. public PushCommand setDryRun(boolean dryRun) {
  372. checkCallable();
  373. this.dryRun = dryRun;
  374. return this;
  375. }
  376. /**
  377. * Get the thin-pack preference
  378. *
  379. * @return the thin-pack preference for push operation
  380. */
  381. public boolean isThin() {
  382. return thin;
  383. }
  384. /**
  385. * Set the thin-pack preference for push operation.
  386. *
  387. * Default setting is Transport.DEFAULT_PUSH_THIN
  388. *
  389. * @param thin
  390. * the thin-pack preference value
  391. * @return {@code this}
  392. */
  393. public PushCommand setThin(boolean thin) {
  394. checkCallable();
  395. this.thin = thin;
  396. return this;
  397. }
  398. /**
  399. * Whether this push should be executed atomically (all references updated,
  400. * or none)
  401. *
  402. * @return true if all-or-nothing behavior is requested.
  403. * @since 4.2
  404. */
  405. public boolean isAtomic() {
  406. return atomic;
  407. }
  408. /**
  409. * Requests atomic push (all references updated, or no updates).
  410. *
  411. * Default setting is false.
  412. *
  413. * @param atomic
  414. * whether to run the push atomically
  415. * @return {@code this}
  416. * @since 4.2
  417. */
  418. public PushCommand setAtomic(boolean atomic) {
  419. checkCallable();
  420. this.atomic = atomic;
  421. return this;
  422. }
  423. /**
  424. * Whether to push forcefully
  425. *
  426. * @return the force preference for push operation
  427. */
  428. public boolean isForce() {
  429. return force;
  430. }
  431. /**
  432. * Sets the force preference for push operation.
  433. *
  434. * @param force
  435. * whether to push forcefully
  436. * @return {@code this}
  437. */
  438. public PushCommand setForce(boolean force) {
  439. checkCallable();
  440. this.force = force;
  441. return this;
  442. }
  443. /**
  444. * Sets the output stream to write sideband messages to
  445. *
  446. * @param out
  447. * an {@link java.io.OutputStream}
  448. * @return {@code this}
  449. * @since 3.0
  450. */
  451. public PushCommand setOutputStream(OutputStream out) {
  452. this.out = out;
  453. return this;
  454. }
  455. /**
  456. * Get push options
  457. *
  458. * @return the option strings associated with the push operation
  459. * @since 4.5
  460. */
  461. public List<String> getPushOptions() {
  462. return pushOptions;
  463. }
  464. /**
  465. * Set the option strings associated with the push operation.
  466. *
  467. * @param pushOptions
  468. * a {@link java.util.List} of push option strings
  469. * @return {@code this}
  470. * @since 4.5
  471. */
  472. public PushCommand setPushOptions(List<String> pushOptions) {
  473. this.pushOptions = pushOptions;
  474. return this;
  475. }
  476. }