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.

PushCommandTest.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /*
  2. * Copyright (C) 2010, 2014 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 static org.junit.Assert.assertEquals;
  12. import static org.junit.Assert.assertNotNull;
  13. import static org.junit.Assert.assertTrue;
  14. import static org.junit.Assert.fail;
  15. import java.io.File;
  16. import java.io.IOException;
  17. import java.net.URISyntaxException;
  18. import java.util.Properties;
  19. import org.eclipse.jgit.api.errors.GitAPIException;
  20. import org.eclipse.jgit.api.errors.JGitInternalException;
  21. import org.eclipse.jgit.api.errors.TransportException;
  22. import org.eclipse.jgit.errors.MissingObjectException;
  23. import org.eclipse.jgit.hooks.PrePushHook;
  24. import org.eclipse.jgit.junit.JGitTestUtil;
  25. import org.eclipse.jgit.junit.RepositoryTestCase;
  26. import org.eclipse.jgit.lib.ObjectId;
  27. import org.eclipse.jgit.lib.Ref;
  28. import org.eclipse.jgit.lib.RefUpdate;
  29. import org.eclipse.jgit.lib.Repository;
  30. import org.eclipse.jgit.lib.StoredConfig;
  31. import org.eclipse.jgit.revwalk.RevCommit;
  32. import org.eclipse.jgit.transport.PushResult;
  33. import org.eclipse.jgit.transport.RefLeaseSpec;
  34. import org.eclipse.jgit.transport.RefSpec;
  35. import org.eclipse.jgit.transport.RemoteConfig;
  36. import org.eclipse.jgit.transport.RemoteRefUpdate;
  37. import org.eclipse.jgit.transport.TrackingRefUpdate;
  38. import org.eclipse.jgit.transport.URIish;
  39. import org.eclipse.jgit.util.FS;
  40. import org.junit.Test;
  41. public class PushCommandTest extends RepositoryTestCase {
  42. @Test
  43. public void testPush() throws JGitInternalException, IOException,
  44. GitAPIException, URISyntaxException {
  45. // create other repository
  46. Repository db2 = createWorkRepository();
  47. final StoredConfig config2 = db2.getConfig();
  48. // this tests that this config can be parsed properly
  49. config2.setString("fsck", "", "missingEmail", "ignore");
  50. config2.save();
  51. // setup the first repository
  52. final StoredConfig config = db.getConfig();
  53. RemoteConfig remoteConfig = new RemoteConfig(config, "test");
  54. URIish uri = new URIish(db2.getDirectory().toURI().toURL());
  55. remoteConfig.addURI(uri);
  56. remoteConfig.update(config);
  57. config.save();
  58. try (Git git1 = new Git(db)) {
  59. // create some refs via commits and tag
  60. RevCommit commit = git1.commit().setMessage("initial commit").call();
  61. Ref tagRef = git1.tag().setName("tag").call();
  62. try {
  63. db2.resolve(commit.getId().getName() + "^{commit}");
  64. fail("id shouldn't exist yet");
  65. } catch (MissingObjectException e) {
  66. // we should get here
  67. }
  68. RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
  69. git1.push().setRemote("test").setRefSpecs(spec)
  70. .call();
  71. assertEquals(commit.getId(),
  72. db2.resolve(commit.getId().getName() + "^{commit}"));
  73. assertEquals(tagRef.getObjectId(),
  74. db2.resolve(tagRef.getObjectId().getName()));
  75. }
  76. }
  77. @Test
  78. public void testPrePushHook() throws JGitInternalException, IOException,
  79. GitAPIException, URISyntaxException {
  80. // create other repository
  81. Repository db2 = createWorkRepository();
  82. // setup the first repository
  83. final StoredConfig config = db.getConfig();
  84. RemoteConfig remoteConfig = new RemoteConfig(config, "test");
  85. URIish uri = new URIish(db2.getDirectory().toURI().toURL());
  86. remoteConfig.addURI(uri);
  87. remoteConfig.update(config);
  88. config.save();
  89. File hookOutput = new File(getTemporaryDirectory(), "hookOutput");
  90. writeHookFile(PrePushHook.NAME, "#!/bin/sh\necho 1:$1, 2:$2, 3:$3 >\""
  91. + hookOutput.toPath() + "\"\ncat - >>\"" + hookOutput.toPath()
  92. + "\"\nexit 0");
  93. try (Git git1 = new Git(db)) {
  94. // create some refs via commits and tag
  95. RevCommit commit = git1.commit().setMessage("initial commit").call();
  96. RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
  97. git1.push().setRemote("test").setRefSpecs(spec).call();
  98. assertEquals("1:test, 2:" + uri + ", 3:\n" + "refs/heads/master "
  99. + commit.getName() + " refs/heads/x "
  100. + ObjectId.zeroId().name() + "\n", read(hookOutput));
  101. }
  102. }
  103. private File writeHookFile(String name, String data)
  104. throws IOException {
  105. File path = new File(db.getWorkTree() + "/.git/hooks/", name);
  106. JGitTestUtil.write(path, data);
  107. FS.DETECTED.setExecute(path, true);
  108. return path;
  109. }
  110. @Test
  111. public void testTrackingUpdate() throws Exception {
  112. Repository db2 = createBareRepository();
  113. String remote = "origin";
  114. String branch = "refs/heads/master";
  115. String trackingBranch = "refs/remotes/" + remote + "/master";
  116. try (Git git = new Git(db)) {
  117. RevCommit commit1 = git.commit().setMessage("Initial commit")
  118. .call();
  119. RefUpdate branchRefUpdate = db.updateRef(branch);
  120. branchRefUpdate.setNewObjectId(commit1.getId());
  121. branchRefUpdate.update();
  122. RefUpdate trackingBranchRefUpdate = db.updateRef(trackingBranch);
  123. trackingBranchRefUpdate.setNewObjectId(commit1.getId());
  124. trackingBranchRefUpdate.update();
  125. final StoredConfig config = db.getConfig();
  126. RemoteConfig remoteConfig = new RemoteConfig(config, remote);
  127. URIish uri = new URIish(db2.getDirectory().toURI().toURL());
  128. remoteConfig.addURI(uri);
  129. remoteConfig.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/"
  130. + remote + "/*"));
  131. remoteConfig.update(config);
  132. config.save();
  133. RevCommit commit2 = git.commit().setMessage("Commit to push").call();
  134. RefSpec spec = new RefSpec(branch + ":" + branch);
  135. Iterable<PushResult> resultIterable = git.push().setRemote(remote)
  136. .setRefSpecs(spec).call();
  137. PushResult result = resultIterable.iterator().next();
  138. TrackingRefUpdate trackingRefUpdate = result
  139. .getTrackingRefUpdate(trackingBranch);
  140. assertNotNull(trackingRefUpdate);
  141. assertEquals(trackingBranch, trackingRefUpdate.getLocalName());
  142. assertEquals(branch, trackingRefUpdate.getRemoteName());
  143. assertEquals(commit2.getId(), trackingRefUpdate.getNewObjectId());
  144. assertEquals(commit2.getId(), db.resolve(trackingBranch));
  145. assertEquals(commit2.getId(), db2.resolve(branch));
  146. }
  147. }
  148. /**
  149. * Check that pushes over file protocol lead to appropriate ref-updates.
  150. *
  151. * @throws Exception
  152. */
  153. @Test
  154. public void testPushRefUpdate() throws Exception {
  155. try (Git git = new Git(db);
  156. Git git2 = new Git(createBareRepository())) {
  157. final StoredConfig config = git.getRepository().getConfig();
  158. RemoteConfig remoteConfig = new RemoteConfig(config, "test");
  159. URIish uri = new URIish(git2.getRepository().getDirectory().toURI()
  160. .toURL());
  161. remoteConfig.addURI(uri);
  162. remoteConfig.addPushRefSpec(new RefSpec("+refs/heads/*:refs/heads/*"));
  163. remoteConfig.update(config);
  164. config.save();
  165. writeTrashFile("f", "content of f");
  166. git.add().addFilepattern("f").call();
  167. RevCommit commit = git.commit().setMessage("adding f").call();
  168. assertEquals(null, git2.getRepository().resolve("refs/heads/master"));
  169. git.push().setRemote("test").call();
  170. assertEquals(commit.getId(),
  171. git2.getRepository().resolve("refs/heads/master"));
  172. git.branchCreate().setName("refs/heads/test").call();
  173. git.checkout().setName("refs/heads/test").call();
  174. for (int i = 0; i < 6; i++) {
  175. writeTrashFile("f" + i, "content of f" + i);
  176. git.add().addFilepattern("f" + i).call();
  177. commit = git.commit().setMessage("adding f" + i).call();
  178. git.push().setRemote("test").call();
  179. git2.getRepository().getRefDatabase().getRefs();
  180. assertEquals("failed to update on attempt " + i, commit.getId(),
  181. git2.getRepository().resolve("refs/heads/test"));
  182. }
  183. }
  184. }
  185. /**
  186. * Check that the push refspec is read from config.
  187. *
  188. * @throws Exception
  189. */
  190. @Test
  191. public void testPushWithRefSpecFromConfig() throws Exception {
  192. try (Git git = new Git(db);
  193. Git git2 = new Git(createBareRepository())) {
  194. final StoredConfig config = git.getRepository().getConfig();
  195. RemoteConfig remoteConfig = new RemoteConfig(config, "test");
  196. URIish uri = new URIish(git2.getRepository().getDirectory().toURI()
  197. .toURL());
  198. remoteConfig.addURI(uri);
  199. remoteConfig.addPushRefSpec(new RefSpec("HEAD:refs/heads/newbranch"));
  200. remoteConfig.update(config);
  201. config.save();
  202. writeTrashFile("f", "content of f");
  203. git.add().addFilepattern("f").call();
  204. RevCommit commit = git.commit().setMessage("adding f").call();
  205. assertEquals(null, git2.getRepository().resolve("refs/heads/master"));
  206. git.push().setRemote("test").call();
  207. assertEquals(commit.getId(),
  208. git2.getRepository().resolve("refs/heads/newbranch"));
  209. }
  210. }
  211. /**
  212. * Check that only HEAD is pushed if no refspec is given.
  213. *
  214. * @throws Exception
  215. */
  216. @Test
  217. public void testPushWithoutPushRefSpec() throws Exception {
  218. try (Git git = new Git(db);
  219. Git git2 = new Git(createBareRepository())) {
  220. final StoredConfig config = git.getRepository().getConfig();
  221. RemoteConfig remoteConfig = new RemoteConfig(config, "test");
  222. URIish uri = new URIish(git2.getRepository().getDirectory().toURI()
  223. .toURL());
  224. remoteConfig.addURI(uri);
  225. remoteConfig.addFetchRefSpec(new RefSpec(
  226. "+refs/heads/*:refs/remotes/origin/*"));
  227. remoteConfig.update(config);
  228. config.save();
  229. writeTrashFile("f", "content of f");
  230. git.add().addFilepattern("f").call();
  231. RevCommit commit = git.commit().setMessage("adding f").call();
  232. git.checkout().setName("not-pushed").setCreateBranch(true).call();
  233. git.checkout().setName("branchtopush").setCreateBranch(true).call();
  234. assertEquals(null,
  235. git2.getRepository().resolve("refs/heads/branchtopush"));
  236. assertEquals(null, git2.getRepository()
  237. .resolve("refs/heads/not-pushed"));
  238. assertEquals(null, git2.getRepository().resolve("refs/heads/master"));
  239. git.push().setRemote("test").call();
  240. assertEquals(commit.getId(),
  241. git2.getRepository().resolve("refs/heads/branchtopush"));
  242. assertEquals(null, git2.getRepository()
  243. .resolve("refs/heads/not-pushed"));
  244. assertEquals(null, git2.getRepository().resolve("refs/heads/master"));
  245. }
  246. }
  247. /**
  248. * Check that missing refs don't cause errors during push
  249. *
  250. * @throws Exception
  251. */
  252. @Test
  253. public void testPushAfterGC() throws Exception {
  254. // create other repository
  255. Repository db2 = createWorkRepository();
  256. // setup the first repository
  257. final StoredConfig config = db.getConfig();
  258. RemoteConfig remoteConfig = new RemoteConfig(config, "test");
  259. URIish uri = new URIish(db2.getDirectory().toURI().toURL());
  260. remoteConfig.addURI(uri);
  261. remoteConfig.update(config);
  262. config.save();
  263. try (Git git1 = new Git(db);
  264. Git git2 = new Git(db2)) {
  265. // push master (with a new commit) to the remote
  266. git1.commit().setMessage("initial commit").call();
  267. RefSpec spec = new RefSpec("refs/heads/*:refs/heads/*");
  268. git1.push().setRemote("test").setRefSpecs(spec).call();
  269. // create an unrelated ref and a commit on our remote
  270. git2.branchCreate().setName("refs/heads/other").call();
  271. git2.checkout().setName("refs/heads/other").call();
  272. writeTrashFile("a", "content of a");
  273. git2.add().addFilepattern("a").call();
  274. RevCommit commit2 = git2.commit().setMessage("adding a").call();
  275. // run a gc to ensure we have a bitmap index
  276. Properties res = git1.gc().setExpire(null).call();
  277. assertEquals(7, res.size());
  278. // create another commit so we have something else to push
  279. writeTrashFile("b", "content of b");
  280. git1.add().addFilepattern("b").call();
  281. RevCommit commit3 = git1.commit().setMessage("adding b").call();
  282. try {
  283. // Re-run the push. Failure may happen here.
  284. git1.push().setRemote("test").setRefSpecs(spec).call();
  285. } catch (TransportException e) {
  286. assertTrue("should be caused by a MissingObjectException", e
  287. .getCause().getCause() instanceof MissingObjectException);
  288. fail("caught MissingObjectException for a change we don't have");
  289. }
  290. // Remote will have both a and b. Master will have only b
  291. try {
  292. db.resolve(commit2.getId().getName() + "^{commit}");
  293. fail("id shouldn't exist locally");
  294. } catch (MissingObjectException e) {
  295. // we should get here
  296. }
  297. assertEquals(commit2.getId(),
  298. db2.resolve(commit2.getId().getName() + "^{commit}"));
  299. assertEquals(commit3.getId(),
  300. db2.resolve(commit3.getId().getName() + "^{commit}"));
  301. }
  302. }
  303. @Test
  304. public void testPushWithLease() throws JGitInternalException, IOException,
  305. GitAPIException, URISyntaxException {
  306. // create other repository
  307. Repository db2 = createWorkRepository();
  308. // setup the first repository
  309. final StoredConfig config = db.getConfig();
  310. RemoteConfig remoteConfig = new RemoteConfig(config, "test");
  311. URIish uri = new URIish(db2.getDirectory().toURI().toURL());
  312. remoteConfig.addURI(uri);
  313. remoteConfig.update(config);
  314. config.save();
  315. try (Git git1 = new Git(db)) {
  316. // create one commit and push it
  317. RevCommit commit = git1.commit().setMessage("initial commit").call();
  318. git1.branchCreate().setName("initial").call();
  319. RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
  320. git1.push().setRemote("test").setRefSpecs(spec)
  321. .call();
  322. assertEquals(commit.getId(),
  323. db2.resolve(commit.getId().getName() + "^{commit}"));
  324. //now try to force-push a new commit, with a good lease
  325. git1.commit().setMessage("second commit").call();
  326. Iterable<PushResult> results =
  327. git1.push().setRemote("test").setRefSpecs(spec)
  328. .setRefLeaseSpecs(new RefLeaseSpec("refs/heads/x", "initial"))
  329. .call();
  330. for (PushResult result : results) {
  331. RemoteRefUpdate update = result.getRemoteUpdate("refs/heads/x");
  332. assertEquals(update.getStatus(), RemoteRefUpdate.Status.OK);
  333. }
  334. git1.commit().setMessage("third commit").call();
  335. //now try to force-push a new commit, with a bad lease
  336. results =
  337. git1.push().setRemote("test").setRefSpecs(spec)
  338. .setRefLeaseSpecs(new RefLeaseSpec("refs/heads/x", "initial"))
  339. .call();
  340. for (PushResult result : results) {
  341. RemoteRefUpdate update = result.getRemoteUpdate("refs/heads/x");
  342. assertEquals(update.getStatus(), RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED);
  343. }
  344. }
  345. }
  346. }