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.

BranchCommandTest.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /*
  2. * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.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.assertFalse;
  13. import static org.junit.Assert.assertNull;
  14. import static org.junit.Assert.fail;
  15. import java.util.List;
  16. import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
  17. import org.eclipse.jgit.api.ListBranchCommand.ListMode;
  18. import org.eclipse.jgit.api.errors.CannotDeleteCurrentBranchException;
  19. import org.eclipse.jgit.api.errors.DetachedHeadException;
  20. import org.eclipse.jgit.api.errors.GitAPIException;
  21. import org.eclipse.jgit.api.errors.InvalidRefNameException;
  22. import org.eclipse.jgit.api.errors.JGitInternalException;
  23. import org.eclipse.jgit.api.errors.NotMergedException;
  24. import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
  25. import org.eclipse.jgit.api.errors.RefNotFoundException;
  26. import org.eclipse.jgit.junit.RepositoryTestCase;
  27. import org.eclipse.jgit.lib.Constants;
  28. import org.eclipse.jgit.lib.ObjectId;
  29. import org.eclipse.jgit.lib.Ref;
  30. import org.eclipse.jgit.lib.RefUpdate;
  31. import org.eclipse.jgit.lib.Repository;
  32. import org.eclipse.jgit.lib.StoredConfig;
  33. import org.eclipse.jgit.revwalk.RevCommit;
  34. import org.eclipse.jgit.transport.FetchResult;
  35. import org.eclipse.jgit.transport.RefSpec;
  36. import org.eclipse.jgit.transport.RemoteConfig;
  37. import org.eclipse.jgit.transport.URIish;
  38. import org.junit.Before;
  39. import org.junit.Test;
  40. public class BranchCommandTest extends RepositoryTestCase {
  41. private Git git;
  42. RevCommit initialCommit;
  43. RevCommit secondCommit;
  44. @Override
  45. @Before
  46. public void setUp() throws Exception {
  47. super.setUp();
  48. git = new Git(db);
  49. // checkout master
  50. git.commit().setMessage("initial commit").call();
  51. // commit something
  52. writeTrashFile("Test.txt", "Hello world");
  53. git.add().addFilepattern("Test.txt").call();
  54. initialCommit = git.commit().setMessage("Initial commit").call();
  55. writeTrashFile("Test.txt", "Some change");
  56. git.add().addFilepattern("Test.txt").call();
  57. secondCommit = git.commit().setMessage("Second commit").call();
  58. // create a master branch
  59. RefUpdate rup = db.updateRef("refs/heads/master");
  60. rup.setNewObjectId(initialCommit.getId());
  61. rup.setForceUpdate(true);
  62. rup.update();
  63. }
  64. private Git setUpRepoWithRemote() throws Exception {
  65. Repository remoteRepository = createWorkRepository();
  66. try (Git remoteGit = new Git(remoteRepository)) {
  67. // commit something
  68. writeTrashFile("Test.txt", "Hello world");
  69. remoteGit.add().addFilepattern("Test.txt").call();
  70. initialCommit = remoteGit.commit().setMessage("Initial commit").call();
  71. writeTrashFile("Test.txt", "Some change");
  72. remoteGit.add().addFilepattern("Test.txt").call();
  73. secondCommit = remoteGit.commit().setMessage("Second commit").call();
  74. // create a master branch
  75. RefUpdate rup = remoteRepository.updateRef("refs/heads/master");
  76. rup.setNewObjectId(initialCommit.getId());
  77. rup.forceUpdate();
  78. Repository localRepository = createWorkRepository();
  79. Git localGit = new Git(localRepository);
  80. StoredConfig config = localRepository.getConfig();
  81. RemoteConfig rc = new RemoteConfig(config, "origin");
  82. rc.addURI(new URIish(remoteRepository.getDirectory().getAbsolutePath()));
  83. rc.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
  84. rc.update(config);
  85. config.save();
  86. FetchResult res = localGit.fetch().setRemote("origin").call();
  87. assertFalse(res.getTrackingRefUpdates().isEmpty());
  88. rup = localRepository.updateRef("refs/heads/master");
  89. rup.setNewObjectId(initialCommit.getId());
  90. rup.forceUpdate();
  91. rup = localRepository.updateRef(Constants.HEAD);
  92. rup.link("refs/heads/master");
  93. rup.setNewObjectId(initialCommit.getId());
  94. rup.update();
  95. return localGit;
  96. }
  97. }
  98. @Test
  99. public void testCreateAndList() throws Exception {
  100. int localBefore;
  101. int remoteBefore;
  102. int allBefore;
  103. // invalid name not allowed
  104. try {
  105. git.branchCreate().setName("In va lid").call();
  106. fail("Create branch with invalid ref name should fail");
  107. } catch (InvalidRefNameException e) {
  108. // expected
  109. }
  110. // existing name not allowed w/o force
  111. try {
  112. git.branchCreate().setName("master").call();
  113. fail("Create branch with existing ref name should fail");
  114. } catch (RefAlreadyExistsException e) {
  115. // expected
  116. }
  117. localBefore = git.branchList().call().size();
  118. remoteBefore = git.branchList().setListMode(ListMode.REMOTE).call()
  119. .size();
  120. allBefore = git.branchList().setListMode(ListMode.ALL).call().size();
  121. assertEquals(localBefore + remoteBefore, allBefore);
  122. Ref newBranch = createBranch(git, "NewForTestList", false, "master",
  123. null);
  124. assertEquals("refs/heads/NewForTestList", newBranch.getName());
  125. assertEquals(1, git.branchList().call().size() - localBefore);
  126. assertEquals(0, git.branchList().setListMode(ListMode.REMOTE).call()
  127. .size()
  128. - remoteBefore);
  129. assertEquals(1, git.branchList().setListMode(ListMode.ALL).call()
  130. .size()
  131. - allBefore);
  132. // we can only create local branches
  133. newBranch = createBranch(git,
  134. "refs/remotes/origin/NewRemoteForTestList", false, "master",
  135. null);
  136. assertEquals("refs/heads/refs/remotes/origin/NewRemoteForTestList",
  137. newBranch.getName());
  138. assertEquals(2, git.branchList().call().size() - localBefore);
  139. assertEquals(0, git.branchList().setListMode(ListMode.REMOTE).call()
  140. .size()
  141. - remoteBefore);
  142. assertEquals(2, git.branchList().setListMode(ListMode.ALL).call()
  143. .size()
  144. - allBefore);
  145. }
  146. @Test(expected = InvalidRefNameException.class)
  147. public void testInvalidBranchHEAD() throws Exception {
  148. git.branchCreate().setName("HEAD").call();
  149. fail("Create branch with invalid ref name should fail");
  150. }
  151. @Test(expected = InvalidRefNameException.class)
  152. public void testInvalidBranchDash() throws Exception {
  153. git.branchCreate().setName("-x").call();
  154. fail("Create branch with invalid ref name should fail");
  155. }
  156. @Test
  157. public void testListAllBranchesShouldNotDie() throws Exception {
  158. setUpRepoWithRemote().branchList().setListMode(ListMode.ALL).call();
  159. }
  160. @Test
  161. public void testListBranchesWithContains() throws Exception {
  162. git.branchCreate().setName("foo").setStartPoint(secondCommit).call();
  163. List<Ref> refs = git.branchList().call();
  164. assertEquals(2, refs.size());
  165. List<Ref> refsContainingSecond = git.branchList()
  166. .setContains(secondCommit.name()).call();
  167. assertEquals(1, refsContainingSecond.size());
  168. // master is on initial commit, so it should not be returned
  169. assertEquals("refs/heads/foo", refsContainingSecond.get(0).getName());
  170. }
  171. @Test
  172. public void testCreateFromCommit() throws Exception {
  173. Ref branch = git.branchCreate().setName("FromInitial").setStartPoint(
  174. initialCommit).call();
  175. assertEquals(initialCommit.getId(), branch.getObjectId());
  176. branch = git.branchCreate().setName("FromInitial2").setStartPoint(
  177. initialCommit.getId().name()).call();
  178. assertEquals(initialCommit.getId(), branch.getObjectId());
  179. try {
  180. git.branchCreate().setName("FromInitial").setStartPoint(
  181. secondCommit).call();
  182. } catch (RefAlreadyExistsException e) {
  183. // expected
  184. }
  185. branch = git.branchCreate().setName("FromInitial").setStartPoint(
  186. secondCommit).setForce(true).call();
  187. assertEquals(secondCommit.getId(), branch.getObjectId());
  188. }
  189. @Test
  190. public void testCreateForce() throws Exception {
  191. // using commits
  192. Ref newBranch = createBranch(git, "NewForce", false, secondCommit
  193. .getId().name(), null);
  194. assertEquals(newBranch.getTarget().getObjectId(), secondCommit.getId());
  195. try {
  196. newBranch = createBranch(git, "NewForce", false, initialCommit
  197. .getId().name(), null);
  198. fail("Should have failed");
  199. } catch (RefAlreadyExistsException e) {
  200. // expected
  201. }
  202. newBranch = createBranch(git, "NewForce", true, initialCommit.getId()
  203. .name(), null);
  204. assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
  205. git.branchDelete().setBranchNames("NewForce").call();
  206. // using names
  207. git.branchCreate().setName("NewForce").setStartPoint("master").call();
  208. assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
  209. try {
  210. git.branchCreate().setName("NewForce").setStartPoint("master")
  211. .call();
  212. fail("Should have failed");
  213. } catch (RefAlreadyExistsException e) {
  214. // expected
  215. }
  216. git.branchCreate().setName("NewForce").setStartPoint("master")
  217. .setForce(true).call();
  218. assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
  219. }
  220. @Test
  221. public void testCreateFromLightweightTag() throws Exception {
  222. RefUpdate rup = db.updateRef("refs/tags/V10");
  223. rup.setNewObjectId(initialCommit);
  224. rup.setExpectedOldObjectId(ObjectId.zeroId());
  225. rup.update();
  226. Ref branch = git.branchCreate().setName("FromLightweightTag")
  227. .setStartPoint("refs/tags/V10").call();
  228. assertEquals(initialCommit.getId(), branch.getObjectId());
  229. }
  230. @Test
  231. public void testCreateFromAnnotatetdTag() throws Exception {
  232. Ref tagRef = git.tag().setName("V10").setObjectId(secondCommit).call();
  233. Ref branch = git.branchCreate().setName("FromAnnotatedTag")
  234. .setStartPoint("refs/tags/V10").call();
  235. assertFalse(tagRef.getObjectId().equals(branch.getObjectId()));
  236. assertEquals(secondCommit.getId(), branch.getObjectId());
  237. }
  238. @Test
  239. public void testDelete() throws Exception {
  240. createBranch(git, "ForDelete", false, "master", null);
  241. git.branchDelete().setBranchNames("ForDelete").call();
  242. // now point the branch to a non-merged commit
  243. createBranch(git, "ForDelete", false, secondCommit.getId().name(), null);
  244. try {
  245. git.branchDelete().setBranchNames("ForDelete").call();
  246. fail("Deletion of a non-merged branch without force should have failed");
  247. } catch (NotMergedException e) {
  248. // expected
  249. }
  250. List<String> deleted = git.branchDelete().setBranchNames("ForDelete")
  251. .setForce(true).call();
  252. assertEquals(1, deleted.size());
  253. assertEquals(Constants.R_HEADS + "ForDelete", deleted.get(0));
  254. createBranch(git, "ForDelete", false, "master", null);
  255. try {
  256. createBranch(git, "ForDelete", false, "master", null);
  257. fail("Repeated creation of same branch without force should fail");
  258. } catch (RefAlreadyExistsException e) {
  259. // expected
  260. }
  261. // change starting point
  262. Ref newBranch = createBranch(git, "ForDelete", true, initialCommit
  263. .name(), null);
  264. assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
  265. newBranch = createBranch(git, "ForDelete", true, secondCommit.name(),
  266. null);
  267. assertEquals(newBranch.getTarget().getObjectId(), secondCommit.getId());
  268. git.branchDelete().setBranchNames("ForDelete").setForce(true);
  269. try {
  270. git.branchDelete().setBranchNames("master").call();
  271. fail("Deletion of checked out branch without force should have failed");
  272. } catch (CannotDeleteCurrentBranchException e) {
  273. // expected
  274. }
  275. try {
  276. git.branchDelete().setBranchNames("master").setForce(true).call();
  277. fail("Deletion of checked out branch with force should have failed");
  278. } catch (CannotDeleteCurrentBranchException e) {
  279. // expected
  280. }
  281. }
  282. @Test
  283. public void testPullConfigRemoteBranch() throws Exception {
  284. Git localGit = setUpRepoWithRemote();
  285. Ref remote = localGit.branchList().setListMode(ListMode.REMOTE).call()
  286. .get(0);
  287. assertEquals("refs/remotes/origin/master", remote.getName());
  288. // by default, we should create pull configuration
  289. createBranch(localGit, "newFromRemote", false, remote.getName(), null);
  290. assertEquals("origin", localGit.getRepository().getConfig().getString(
  291. "branch", "newFromRemote", "remote"));
  292. localGit.branchDelete().setBranchNames("newFromRemote").call();
  293. // the pull configuration should be gone after deletion
  294. assertNull(localGit.getRepository().getConfig().getString("branch",
  295. "newFromRemote", "remote"));
  296. createBranch(localGit, "newFromRemote", false, remote.getName(), null);
  297. assertEquals("origin", localGit.getRepository().getConfig().getString(
  298. "branch", "newFromRemote", "remote"));
  299. localGit.branchDelete().setBranchNames("refs/heads/newFromRemote")
  300. .call();
  301. // the pull configuration should be gone after deletion
  302. assertNull(localGit.getRepository().getConfig().getString("branch",
  303. "newFromRemote", "remote"));
  304. // use --no-track
  305. createBranch(localGit, "newFromRemote", false, remote.getName(),
  306. SetupUpstreamMode.NOTRACK);
  307. assertNull(localGit.getRepository().getConfig().getString("branch",
  308. "newFromRemote", "remote"));
  309. localGit.branchDelete().setBranchNames("newFromRemote").call();
  310. }
  311. @Test
  312. public void testPullConfigLocalBranch() throws Exception {
  313. Git localGit = setUpRepoWithRemote();
  314. // by default, we should not create pull configuration
  315. createBranch(localGit, "newFromMaster", false, "master", null);
  316. assertNull(localGit.getRepository().getConfig().getString("branch",
  317. "newFromMaster", "remote"));
  318. localGit.branchDelete().setBranchNames("newFromMaster").call();
  319. // use --track
  320. createBranch(localGit, "newFromMaster", false, "master",
  321. SetupUpstreamMode.TRACK);
  322. assertEquals(".", localGit.getRepository().getConfig().getString(
  323. "branch", "newFromMaster", "remote"));
  324. localGit.branchDelete().setBranchNames("refs/heads/newFromMaster")
  325. .call();
  326. // the pull configuration should be gone after deletion
  327. assertNull(localGit.getRepository().getConfig().getString("branch",
  328. "newFromRemote", "remote"));
  329. }
  330. @Test
  331. public void testPullConfigRenameLocalBranch() throws Exception {
  332. Git localGit = setUpRepoWithRemote();
  333. // by default, we should not create pull configuration
  334. createBranch(localGit, "newFromMaster", false, "master", null);
  335. assertNull(localGit.getRepository().getConfig().getString("branch",
  336. "newFromMaster", "remote"));
  337. localGit.branchDelete().setBranchNames("newFromMaster").call();
  338. // use --track
  339. createBranch(localGit, "newFromMaster", false, "master",
  340. SetupUpstreamMode.TRACK);
  341. assertEquals(".", localGit.getRepository().getConfig().getString(
  342. "branch", "newFromMaster", "remote"));
  343. localGit.branchRename().setOldName("newFromMaster").setNewName(
  344. "renamed").call();
  345. assertNull(".", localGit.getRepository().getConfig().getString(
  346. "branch", "newFromMaster", "remote"));
  347. assertEquals(".", localGit.getRepository().getConfig().getString(
  348. "branch", "renamed", "remote"));
  349. localGit.branchDelete().setBranchNames("renamed").call();
  350. // the pull configuration should be gone after deletion
  351. assertNull(localGit.getRepository().getConfig().getString("branch",
  352. "newFromRemote", "remote"));
  353. }
  354. @Test
  355. public void testRenameLocalBranch() throws Exception {
  356. // null newName not allowed
  357. try {
  358. git.branchRename().call();
  359. } catch (InvalidRefNameException e) {
  360. // expected
  361. }
  362. // invalid newName not allowed
  363. try {
  364. git.branchRename().setNewName("In va lid").call();
  365. } catch (InvalidRefNameException e) {
  366. // expected
  367. }
  368. // not existing name not allowed
  369. try {
  370. git.branchRename().setOldName("notexistingbranch").setNewName(
  371. "newname").call();
  372. } catch (RefNotFoundException e) {
  373. // expected
  374. }
  375. // create some branch
  376. createBranch(git, "existing", false, "master", null);
  377. // a local branch
  378. Ref branch = createBranch(git, "fromMasterForRename", false, "master",
  379. null);
  380. assertEquals(Constants.R_HEADS + "fromMasterForRename", branch
  381. .getName());
  382. Ref renamed = git.branchRename().setOldName("fromMasterForRename")
  383. .setNewName("newName").call();
  384. assertEquals(Constants.R_HEADS + "newName", renamed.getName());
  385. try {
  386. git.branchRename().setOldName(renamed.getName()).setNewName(
  387. "existing").call();
  388. fail("Should have failed");
  389. } catch (RefAlreadyExistsException e) {
  390. // expected
  391. }
  392. try {
  393. git.branchRename().setNewName("In va lid").call();
  394. fail("Rename with invalid ref name should fail");
  395. } catch (InvalidRefNameException e) {
  396. // expected
  397. }
  398. // rename without old name and detached head not allowed
  399. RefUpdate rup = git.getRepository().updateRef(Constants.HEAD, true);
  400. rup.setNewObjectId(initialCommit);
  401. rup.forceUpdate();
  402. try {
  403. git.branchRename().setNewName("detached").call();
  404. } catch (DetachedHeadException e) {
  405. // expected
  406. }
  407. }
  408. @Test
  409. public void testRenameRemoteTrackingBranch() throws Exception {
  410. Git localGit = setUpRepoWithRemote();
  411. Ref remoteBranch = localGit.branchList().setListMode(ListMode.REMOTE)
  412. .call().get(0);
  413. Ref renamed = localGit.branchRename()
  414. .setOldName(remoteBranch.getName()).setNewName("newRemote")
  415. .call();
  416. assertEquals(Constants.R_REMOTES + "newRemote", renamed.getName());
  417. }
  418. @Test
  419. public void testCreationImplicitStart() throws Exception {
  420. git.branchCreate().setName("topic").call();
  421. assertEquals(db.resolve("HEAD"), db.resolve("topic"));
  422. }
  423. @Test
  424. public void testCreationNullStartPoint() throws Exception {
  425. String startPoint = null;
  426. git.branchCreate().setName("topic").setStartPoint(startPoint).call();
  427. assertEquals(db.resolve("HEAD"), db.resolve("topic"));
  428. }
  429. public Ref createBranch(Git actGit, String name, boolean force,
  430. String startPoint, SetupUpstreamMode mode)
  431. throws JGitInternalException, GitAPIException {
  432. CreateBranchCommand cmd = actGit.branchCreate();
  433. cmd.setName(name);
  434. cmd.setForce(force);
  435. cmd.setStartPoint(startPoint);
  436. cmd.setUpstreamMode(mode);
  437. return cmd.call();
  438. }
  439. }