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.

PushOptionsTest.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /*
  2. * Copyright (C) 2016, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.transport;
  44. import static org.junit.Assert.assertEquals;
  45. import static org.junit.Assert.assertNull;
  46. import static org.junit.Assert.assertSame;
  47. import static org.junit.Assert.fail;
  48. import java.io.IOException;
  49. import java.net.URISyntaxException;
  50. import java.util.ArrayList;
  51. import java.util.Arrays;
  52. import java.util.List;
  53. import org.eclipse.jgit.api.Git;
  54. import org.eclipse.jgit.api.PushCommand;
  55. import org.eclipse.jgit.api.errors.GitAPIException;
  56. import org.eclipse.jgit.api.errors.NoFilepatternException;
  57. import org.eclipse.jgit.api.errors.TransportException;
  58. import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
  59. import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
  60. import org.eclipse.jgit.junit.RepositoryTestCase;
  61. import org.eclipse.jgit.lib.Constants;
  62. import org.eclipse.jgit.lib.NullProgressMonitor;
  63. import org.eclipse.jgit.lib.ObjectId;
  64. import org.eclipse.jgit.lib.ObjectInserter;
  65. import org.eclipse.jgit.lib.Repository;
  66. import org.eclipse.jgit.lib.StoredConfig;
  67. import org.eclipse.jgit.revwalk.RevCommit;
  68. import org.junit.After;
  69. import org.junit.Before;
  70. import org.junit.Test;
  71. public class PushOptionsTest extends RepositoryTestCase {
  72. private URIish uri;
  73. private TestProtocol<Object> testProtocol;
  74. private Object ctx = new Object();
  75. private InMemoryRepository server;
  76. private InMemoryRepository client;
  77. private ObjectId obj1;
  78. private ObjectId obj2;
  79. private ReceivePack receivePack;
  80. @Override
  81. @Before
  82. public void setUp() throws Exception {
  83. super.setUp();
  84. server = newRepo("server");
  85. client = newRepo("client");
  86. testProtocol = new TestProtocol<>(null,
  87. (Object req, Repository git) -> {
  88. receivePack = new ReceivePack(git);
  89. receivePack.setAllowPushOptions(true);
  90. receivePack.setAtomic(true);
  91. return receivePack;
  92. });
  93. uri = testProtocol.register(ctx, server);
  94. try (ObjectInserter ins = client.newObjectInserter()) {
  95. obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
  96. obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
  97. ins.flush();
  98. }
  99. }
  100. @Override
  101. @After
  102. public void tearDown() {
  103. Transport.unregister(testProtocol);
  104. }
  105. private static InMemoryRepository newRepo(String name) {
  106. return new InMemoryRepository(new DfsRepositoryDescription(name));
  107. }
  108. private List<RemoteRefUpdate> commands(boolean atomicSafe)
  109. throws IOException {
  110. List<RemoteRefUpdate> cmds = new ArrayList<>();
  111. cmds.add(new RemoteRefUpdate(null, null, obj1, "refs/heads/one",
  112. true /* force update */, null /* no local tracking ref */,
  113. ObjectId.zeroId()));
  114. cmds.add(new RemoteRefUpdate(null, null, obj2, "refs/heads/two",
  115. true /* force update */, null /* no local tracking ref */,
  116. atomicSafe ? ObjectId.zeroId() : obj1));
  117. return cmds;
  118. }
  119. private void connectLocalToRemote(Git local, Git remote)
  120. throws URISyntaxException, IOException {
  121. StoredConfig config = local.getRepository().getConfig();
  122. RemoteConfig remoteConfig = new RemoteConfig(config, "test");
  123. remoteConfig.addURI(new URIish(
  124. remote.getRepository().getDirectory().toURI().toURL()));
  125. remoteConfig.addFetchRefSpec(
  126. new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
  127. remoteConfig.update(config);
  128. config.save();
  129. }
  130. private RevCommit addCommit(Git git)
  131. throws IOException, NoFilepatternException, GitAPIException {
  132. writeTrashFile("f", "content of f");
  133. git.add().addFilepattern("f").call();
  134. return git.commit().setMessage("adding f").call();
  135. }
  136. @Test
  137. public void testNonAtomicPushWithOptions() throws Exception {
  138. PushResult r;
  139. server.setPerformsAtomicTransactions(false);
  140. List<String> pushOptions = Arrays.asList("Hello", "World!");
  141. try (Transport tn = testProtocol.open(uri, client, "server")) {
  142. tn.setPushAtomic(false);
  143. tn.setPushOptions(pushOptions);
  144. r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
  145. }
  146. RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
  147. RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
  148. assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
  149. assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
  150. two.getStatus());
  151. assertEquals(pushOptions, receivePack.getPushOptions());
  152. }
  153. @Test
  154. public void testAtomicPushWithOptions() throws Exception {
  155. PushResult r;
  156. server.setPerformsAtomicTransactions(true);
  157. List<String> pushOptions = Arrays.asList("Hello", "World!");
  158. try (Transport tn = testProtocol.open(uri, client, "server")) {
  159. tn.setPushAtomic(true);
  160. tn.setPushOptions(pushOptions);
  161. r = tn.push(NullProgressMonitor.INSTANCE, commands(true));
  162. }
  163. RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
  164. RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
  165. assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
  166. assertSame(RemoteRefUpdate.Status.OK, two.getStatus());
  167. assertEquals(pushOptions, receivePack.getPushOptions());
  168. }
  169. @Test
  170. public void testFailedAtomicPushWithOptions() throws Exception {
  171. PushResult r;
  172. server.setPerformsAtomicTransactions(true);
  173. List<String> pushOptions = Arrays.asList("Hello", "World!");
  174. try (Transport tn = testProtocol.open(uri, client, "server")) {
  175. tn.setPushAtomic(true);
  176. tn.setPushOptions(pushOptions);
  177. r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
  178. }
  179. RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
  180. RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
  181. assertSame(RemoteRefUpdate.Status.REJECTED_OTHER_REASON,
  182. one.getStatus());
  183. assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
  184. two.getStatus());
  185. assertNull(receivePack.getPushOptions());
  186. }
  187. @Test
  188. public void testThinPushWithOptions() throws Exception {
  189. PushResult r;
  190. List<String> pushOptions = Arrays.asList("Hello", "World!");
  191. try (Transport tn = testProtocol.open(uri, client, "server")) {
  192. tn.setPushThin(true);
  193. tn.setPushOptions(pushOptions);
  194. r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
  195. }
  196. RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
  197. RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
  198. assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
  199. assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
  200. two.getStatus());
  201. assertEquals(pushOptions, receivePack.getPushOptions());
  202. }
  203. @Test
  204. public void testPushWithoutOptions() throws Exception {
  205. try (Git local = new Git(db);
  206. Git remote = new Git(createBareRepository())) {
  207. connectLocalToRemote(local, remote);
  208. final StoredConfig config2 = remote.getRepository().getConfig();
  209. config2.setBoolean("receive", null, "pushoptions", true);
  210. config2.save();
  211. RevCommit commit = addCommit(local);
  212. local.checkout().setName("not-pushed").setCreateBranch(true).call();
  213. local.checkout().setName("branchtopush").setCreateBranch(true).call();
  214. assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
  215. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  216. assertNull(remote.getRepository().resolve("refs/heads/master"));
  217. PushCommand pushCommand = local.push().setRemote("test");
  218. pushCommand.call();
  219. assertEquals(commit.getId(),
  220. remote.getRepository().resolve("refs/heads/branchtopush"));
  221. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  222. assertNull(remote.getRepository().resolve("refs/heads/master"));
  223. }
  224. }
  225. @Test
  226. public void testPushWithEmptyOptions() throws Exception {
  227. try (Git local = new Git(db);
  228. Git remote = new Git(createBareRepository())) {
  229. connectLocalToRemote(local, remote);
  230. final StoredConfig config2 = remote.getRepository().getConfig();
  231. config2.setBoolean("receive", null, "pushoptions", true);
  232. config2.save();
  233. RevCommit commit = addCommit(local);
  234. local.checkout().setName("not-pushed").setCreateBranch(true).call();
  235. local.checkout().setName("branchtopush").setCreateBranch(true).call();
  236. assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
  237. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  238. assertNull(remote.getRepository().resolve("refs/heads/master"));
  239. List<String> pushOptions = new ArrayList<>();
  240. PushCommand pushCommand = local.push().setRemote("test")
  241. .setPushOptions(pushOptions);
  242. pushCommand.call();
  243. assertEquals(commit.getId(),
  244. remote.getRepository().resolve("refs/heads/branchtopush"));
  245. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  246. assertNull(remote.getRepository().resolve("refs/heads/master"));
  247. }
  248. }
  249. @Test
  250. public void testAdvertisedButUnusedPushOptions() throws Exception {
  251. try (Git local = new Git(db);
  252. Git remote = new Git(createBareRepository())) {
  253. connectLocalToRemote(local, remote);
  254. final StoredConfig config2 = remote.getRepository().getConfig();
  255. config2.setBoolean("receive", null, "pushoptions", true);
  256. config2.save();
  257. RevCommit commit = addCommit(local);
  258. local.checkout().setName("not-pushed").setCreateBranch(true).call();
  259. local.checkout().setName("branchtopush").setCreateBranch(true).call();
  260. assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
  261. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  262. assertNull(remote.getRepository().resolve("refs/heads/master"));
  263. PushCommand pushCommand = local.push().setRemote("test")
  264. .setPushOptions(null);
  265. pushCommand.call();
  266. assertEquals(commit.getId(),
  267. remote.getRepository().resolve("refs/heads/branchtopush"));
  268. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  269. assertNull(remote.getRepository().resolve("refs/heads/master"));
  270. }
  271. }
  272. @Test(expected = TransportException.class)
  273. public void testPushOptionsNotSupported() throws Exception {
  274. try (Git local = new Git(db);
  275. Git remote = new Git(createBareRepository())) {
  276. connectLocalToRemote(local, remote);
  277. final StoredConfig config2 = remote.getRepository().getConfig();
  278. config2.setBoolean("receive", null, "pushoptions", false);
  279. config2.save();
  280. addCommit(local);
  281. local.checkout().setName("not-pushed").setCreateBranch(true).call();
  282. local.checkout().setName("branchtopush").setCreateBranch(true).call();
  283. assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
  284. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  285. assertNull(remote.getRepository().resolve("refs/heads/master"));
  286. List<String> pushOptions = new ArrayList<>();
  287. PushCommand pushCommand = local.push().setRemote("test")
  288. .setPushOptions(pushOptions);
  289. pushCommand.call();
  290. fail("should already have thrown TransportException");
  291. }
  292. }
  293. }