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 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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.eclipse.jgit.transport.resolver.ReceivePackFactory;
  69. import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
  70. import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
  71. import org.junit.After;
  72. import org.junit.Before;
  73. import org.junit.Test;
  74. public class PushOptionsTest extends RepositoryTestCase {
  75. private URIish uri;
  76. private TestProtocol<Object> testProtocol;
  77. private Object ctx = new Object();
  78. private InMemoryRepository server;
  79. private InMemoryRepository client;
  80. private ObjectId obj1;
  81. private ObjectId obj2;
  82. private ReceivePack receivePack;
  83. @Before
  84. public void setUp() throws Exception {
  85. super.setUp();
  86. server = newRepo("server");
  87. client = newRepo("client");
  88. testProtocol = new TestProtocol<>(null,
  89. new ReceivePackFactory<Object>() {
  90. @Override
  91. public ReceivePack create(Object req, Repository git)
  92. throws ServiceNotEnabledException,
  93. ServiceNotAuthorizedException {
  94. receivePack = new ReceivePack(git);
  95. receivePack.setAllowPushOptions(true);
  96. receivePack.setAtomic(true);
  97. return receivePack;
  98. }
  99. });
  100. uri = testProtocol.register(ctx, server);
  101. try (ObjectInserter ins = client.newObjectInserter()) {
  102. obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
  103. obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
  104. ins.flush();
  105. }
  106. }
  107. @After
  108. public void tearDown() {
  109. Transport.unregister(testProtocol);
  110. }
  111. private static InMemoryRepository newRepo(String name) {
  112. return new InMemoryRepository(new DfsRepositoryDescription(name));
  113. }
  114. private List<RemoteRefUpdate> commands(boolean atomicSafe)
  115. throws IOException {
  116. List<RemoteRefUpdate> cmds = new ArrayList<>();
  117. cmds.add(new RemoteRefUpdate(null, null, obj1, "refs/heads/one",
  118. true /* force update */, null /* no local tracking ref */,
  119. ObjectId.zeroId()));
  120. cmds.add(new RemoteRefUpdate(null, null, obj2, "refs/heads/two",
  121. true /* force update */, null /* no local tracking ref */,
  122. atomicSafe ? ObjectId.zeroId() : obj1));
  123. return cmds;
  124. }
  125. private void connectLocalToRemote(Git local, Git remote)
  126. throws URISyntaxException, IOException {
  127. StoredConfig config = local.getRepository().getConfig();
  128. RemoteConfig remoteConfig = new RemoteConfig(config, "test");
  129. remoteConfig.addURI(new URIish(
  130. remote.getRepository().getDirectory().toURI().toURL()));
  131. remoteConfig.addFetchRefSpec(
  132. new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
  133. remoteConfig.update(config);
  134. config.save();
  135. }
  136. private RevCommit addCommit(Git git)
  137. throws IOException, NoFilepatternException, GitAPIException {
  138. writeTrashFile("f", "content of f");
  139. git.add().addFilepattern("f").call();
  140. return git.commit().setMessage("adding f").call();
  141. }
  142. @Test
  143. public void testNonAtomicPushWithOptions() throws Exception {
  144. PushResult r;
  145. server.setPerformsAtomicTransactions(false);
  146. List<String> pushOptions = Arrays.asList("Hello", "World!");
  147. try (Transport tn = testProtocol.open(uri, client, "server")) {
  148. tn.setPushAtomic(false);
  149. tn.setPushOptions(pushOptions);
  150. r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
  151. }
  152. RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
  153. RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
  154. assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
  155. assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
  156. two.getStatus());
  157. assertEquals(pushOptions, receivePack.getPushOptions());
  158. }
  159. @Test
  160. public void testAtomicPushWithOptions() throws Exception {
  161. PushResult r;
  162. server.setPerformsAtomicTransactions(true);
  163. List<String> pushOptions = Arrays.asList("Hello", "World!");
  164. try (Transport tn = testProtocol.open(uri, client, "server")) {
  165. tn.setPushAtomic(true);
  166. tn.setPushOptions(pushOptions);
  167. r = tn.push(NullProgressMonitor.INSTANCE, commands(true));
  168. }
  169. RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
  170. RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
  171. assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
  172. assertSame(RemoteRefUpdate.Status.OK, two.getStatus());
  173. assertEquals(pushOptions, receivePack.getPushOptions());
  174. }
  175. @Test
  176. public void testFailedAtomicPushWithOptions() throws Exception {
  177. PushResult r;
  178. server.setPerformsAtomicTransactions(true);
  179. List<String> pushOptions = Arrays.asList("Hello", "World!");
  180. try (Transport tn = testProtocol.open(uri, client, "server")) {
  181. tn.setPushAtomic(true);
  182. tn.setPushOptions(pushOptions);
  183. r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
  184. }
  185. RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
  186. RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
  187. assertSame(RemoteRefUpdate.Status.REJECTED_OTHER_REASON,
  188. one.getStatus());
  189. assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
  190. two.getStatus());
  191. assertNull(receivePack.getPushOptions());
  192. }
  193. @Test
  194. public void testThinPushWithOptions() throws Exception {
  195. PushResult r;
  196. List<String> pushOptions = Arrays.asList("Hello", "World!");
  197. try (Transport tn = testProtocol.open(uri, client, "server")) {
  198. tn.setPushThin(true);
  199. tn.setPushOptions(pushOptions);
  200. r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
  201. }
  202. RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
  203. RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
  204. assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
  205. assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
  206. two.getStatus());
  207. assertEquals(pushOptions, receivePack.getPushOptions());
  208. }
  209. @Test
  210. public void testPushWithoutOptions() throws Exception {
  211. try (Git local = new Git(db);
  212. Git remote = new Git(createBareRepository())) {
  213. connectLocalToRemote(local, remote);
  214. final StoredConfig config2 = remote.getRepository().getConfig();
  215. config2.setBoolean("receive", null, "pushoptions", true);
  216. config2.save();
  217. RevCommit commit = addCommit(local);
  218. local.checkout().setName("not-pushed").setCreateBranch(true).call();
  219. local.checkout().setName("branchtopush").setCreateBranch(true).call();
  220. assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
  221. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  222. assertNull(remote.getRepository().resolve("refs/heads/master"));
  223. PushCommand pushCommand = local.push().setRemote("test");
  224. pushCommand.call();
  225. assertEquals(commit.getId(),
  226. remote.getRepository().resolve("refs/heads/branchtopush"));
  227. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  228. assertNull(remote.getRepository().resolve("refs/heads/master"));
  229. }
  230. }
  231. @Test
  232. public void testPushWithEmptyOptions() throws Exception {
  233. try (Git local = new Git(db);
  234. Git remote = new Git(createBareRepository())) {
  235. connectLocalToRemote(local, remote);
  236. final StoredConfig config2 = remote.getRepository().getConfig();
  237. config2.setBoolean("receive", null, "pushoptions", true);
  238. config2.save();
  239. RevCommit commit = addCommit(local);
  240. local.checkout().setName("not-pushed").setCreateBranch(true).call();
  241. local.checkout().setName("branchtopush").setCreateBranch(true).call();
  242. assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
  243. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  244. assertNull(remote.getRepository().resolve("refs/heads/master"));
  245. List<String> pushOptions = new ArrayList<>();
  246. PushCommand pushCommand = local.push().setRemote("test")
  247. .setPushOptions(pushOptions);
  248. pushCommand.call();
  249. assertEquals(commit.getId(),
  250. remote.getRepository().resolve("refs/heads/branchtopush"));
  251. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  252. assertNull(remote.getRepository().resolve("refs/heads/master"));
  253. }
  254. }
  255. @Test
  256. public void testAdvertisedButUnusedPushOptions() throws Exception {
  257. try (Git local = new Git(db);
  258. Git remote = new Git(createBareRepository())) {
  259. connectLocalToRemote(local, remote);
  260. final StoredConfig config2 = remote.getRepository().getConfig();
  261. config2.setBoolean("receive", null, "pushoptions", true);
  262. config2.save();
  263. RevCommit commit = addCommit(local);
  264. local.checkout().setName("not-pushed").setCreateBranch(true).call();
  265. local.checkout().setName("branchtopush").setCreateBranch(true).call();
  266. assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
  267. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  268. assertNull(remote.getRepository().resolve("refs/heads/master"));
  269. PushCommand pushCommand = local.push().setRemote("test")
  270. .setPushOptions(null);
  271. pushCommand.call();
  272. assertEquals(commit.getId(),
  273. remote.getRepository().resolve("refs/heads/branchtopush"));
  274. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  275. assertNull(remote.getRepository().resolve("refs/heads/master"));
  276. }
  277. }
  278. @Test(expected = TransportException.class)
  279. public void testPushOptionsNotSupported() throws Exception {
  280. try (Git local = new Git(db);
  281. Git remote = new Git(createBareRepository())) {
  282. connectLocalToRemote(local, remote);
  283. final StoredConfig config2 = remote.getRepository().getConfig();
  284. config2.setBoolean("receive", null, "pushoptions", false);
  285. config2.save();
  286. addCommit(local);
  287. local.checkout().setName("not-pushed").setCreateBranch(true).call();
  288. local.checkout().setName("branchtopush").setCreateBranch(true).call();
  289. assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
  290. assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
  291. assertNull(remote.getRepository().resolve("refs/heads/master"));
  292. List<String> pushOptions = new ArrayList<>();
  293. PushCommand pushCommand = local.push().setRemote("test")
  294. .setPushOptions(pushOptions);
  295. pushCommand.call();
  296. fail("should already have thrown TransportException");
  297. }
  298. }
  299. }