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

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