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.

RebaseCommandTest.java 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. /*
  2. * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
  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.api;
  44. import static org.junit.Assert.assertEquals;
  45. import static org.junit.Assert.assertFalse;
  46. import static org.junit.Assert.assertNotNull;
  47. import static org.junit.Assert.assertTrue;
  48. import static org.junit.Assert.fail;
  49. import java.io.BufferedReader;
  50. import java.io.File;
  51. import java.io.FileInputStream;
  52. import java.io.IOException;
  53. import java.io.InputStreamReader;
  54. import org.eclipse.jgit.api.RebaseCommand.Action;
  55. import org.eclipse.jgit.api.RebaseCommand.Operation;
  56. import org.eclipse.jgit.api.RebaseResult.Status;
  57. import org.eclipse.jgit.api.errors.RefNotFoundException;
  58. import org.eclipse.jgit.api.errors.UnmergedPathsException;
  59. import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
  60. import org.eclipse.jgit.dircache.DirCacheCheckout;
  61. import org.eclipse.jgit.lib.Constants;
  62. import org.eclipse.jgit.lib.ObjectId;
  63. import org.eclipse.jgit.lib.PersonIdent;
  64. import org.eclipse.jgit.lib.RefUpdate;
  65. import org.eclipse.jgit.lib.RepositoryState;
  66. import org.eclipse.jgit.lib.RepositoryTestCase;
  67. import org.eclipse.jgit.revwalk.RevCommit;
  68. import org.eclipse.jgit.revwalk.RevWalk;
  69. import org.junit.Before;
  70. import org.junit.Test;
  71. public class RebaseCommandTest extends RepositoryTestCase {
  72. private static final String FILE1 = "file1";
  73. protected Git git;
  74. @Override
  75. @Before
  76. public void setUp() throws Exception {
  77. super.setUp();
  78. this.git = new Git(db);
  79. }
  80. private void createBranch(ObjectId objectId, String branchName)
  81. throws IOException {
  82. RefUpdate updateRef = db.updateRef(branchName);
  83. updateRef.setNewObjectId(objectId);
  84. updateRef.update();
  85. }
  86. private void checkoutBranch(String branchName)
  87. throws IllegalStateException, IOException {
  88. RevWalk walk = new RevWalk(db);
  89. RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD));
  90. RevCommit branch = walk.parseCommit(db.resolve(branchName));
  91. DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree().getId(),
  92. db.lockDirCache(), branch.getTree().getId());
  93. dco.setFailOnConflict(true);
  94. dco.checkout();
  95. walk.release();
  96. // update the HEAD
  97. RefUpdate refUpdate = db.updateRef(Constants.HEAD);
  98. refUpdate.link(branchName);
  99. }
  100. private void checkoutCommit(RevCommit commit) throws IllegalStateException,
  101. IOException {
  102. RevWalk walk = new RevWalk(db);
  103. RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD));
  104. DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree(), db
  105. .lockDirCache(), commit.getTree());
  106. dco.setFailOnConflict(true);
  107. dco.checkout();
  108. walk.release();
  109. // update the HEAD
  110. RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
  111. refUpdate.setNewObjectId(commit);
  112. refUpdate.forceUpdate();
  113. }
  114. @Test
  115. public void testFastForwardWithNewFile() throws Exception {
  116. // create file1 on master
  117. writeTrashFile(FILE1, FILE1);
  118. git.add().addFilepattern(FILE1).call();
  119. RevCommit first = git.commit().setMessage("Add file1").call();
  120. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  121. // create a topic branch
  122. createBranch(first, "refs/heads/topic");
  123. // create file2 on master
  124. writeTrashFile("file2", "file2");
  125. git.add().addFilepattern("file2").call();
  126. git.commit().setMessage("Add file2").call();
  127. assertTrue(new File(db.getWorkTree(), "file2").exists());
  128. checkoutBranch("refs/heads/topic");
  129. assertFalse(new File(db.getWorkTree(), "file2").exists());
  130. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  131. assertEquals(Status.UP_TO_DATE, res.getStatus());
  132. }
  133. @Test
  134. public void testUpToDate() throws Exception {
  135. // create file1 on master
  136. writeTrashFile(FILE1, FILE1);
  137. git.add().addFilepattern(FILE1).call();
  138. RevCommit first = git.commit().setMessage("Add file1").call();
  139. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  140. RebaseResult res = git.rebase().setUpstream(first).call();
  141. assertEquals(Status.UP_TO_DATE, res.getStatus());
  142. }
  143. @Test
  144. public void testUnknownUpstream() throws Exception {
  145. // create file1 on master
  146. writeTrashFile(FILE1, FILE1);
  147. git.add().addFilepattern(FILE1).call();
  148. git.commit().setMessage("Add file1").call();
  149. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  150. try {
  151. git.rebase().setUpstream("refs/heads/xyz").call();
  152. fail("expected exception was not thrown");
  153. } catch (RefNotFoundException e) {
  154. // expected exception
  155. }
  156. }
  157. @Test
  158. public void testConflictFreeWithSingleFile() throws Exception {
  159. // create file1 on master
  160. File theFile = writeTrashFile(FILE1, "1\n2\n3\n");
  161. git.add().addFilepattern(FILE1).call();
  162. RevCommit second = git.commit().setMessage("Add file1").call();
  163. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  164. // change first line in master and commit
  165. writeTrashFile(FILE1, "1master\n2\n3\n");
  166. checkFile(theFile, "1master\n2\n3\n");
  167. git.add().addFilepattern(FILE1).call();
  168. RevCommit lastMasterChange = git.commit().setMessage(
  169. "change file1 in master").call();
  170. // create a topic branch based on second commit
  171. createBranch(second, "refs/heads/topic");
  172. checkoutBranch("refs/heads/topic");
  173. // we have the old content again
  174. checkFile(theFile, "1\n2\n3\n");
  175. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  176. // change third line in topic branch
  177. writeTrashFile(FILE1, "1\n2\n3\ntopic\n");
  178. git.add().addFilepattern(FILE1).call();
  179. git.commit().setMessage("change file1 in topic").call();
  180. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  181. assertEquals(Status.OK, res.getStatus());
  182. checkFile(theFile, "1master\n2\n3\ntopic\n");
  183. // our old branch should be checked out again
  184. assertEquals("refs/heads/topic", db.getFullBranch());
  185. assertEquals(lastMasterChange, new RevWalk(db).parseCommit(
  186. db.resolve(Constants.HEAD)).getParent(0));
  187. }
  188. @Test
  189. public void testDetachedHead() throws Exception {
  190. // create file1 on master
  191. File theFile = writeTrashFile(FILE1, "1\n2\n3\n");
  192. git.add().addFilepattern(FILE1).call();
  193. RevCommit second = git.commit().setMessage("Add file1").call();
  194. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  195. // change first line in master and commit
  196. writeTrashFile(FILE1, "1master\n2\n3\n");
  197. checkFile(theFile, "1master\n2\n3\n");
  198. git.add().addFilepattern(FILE1).call();
  199. RevCommit lastMasterChange = git.commit().setMessage(
  200. "change file1 in master").call();
  201. // create a topic branch based on second commit
  202. createBranch(second, "refs/heads/topic");
  203. checkoutBranch("refs/heads/topic");
  204. // we have the old content again
  205. checkFile(theFile, "1\n2\n3\n");
  206. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  207. // change third line in topic branch
  208. writeTrashFile(FILE1, "1\n2\n3\ntopic\n");
  209. git.add().addFilepattern(FILE1).call();
  210. RevCommit topicCommit = git.commit()
  211. .setMessage("change file1 in topic").call();
  212. checkoutBranch("refs/heads/master");
  213. checkoutCommit(topicCommit);
  214. assertEquals(topicCommit.getId().getName(), db.getFullBranch());
  215. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  216. assertEquals(Status.OK, res.getStatus());
  217. checkFile(theFile, "1master\n2\n3\ntopic\n");
  218. assertEquals(lastMasterChange, new RevWalk(db).parseCommit(
  219. db.resolve(Constants.HEAD)).getParent(0));
  220. }
  221. @Test
  222. public void testFilesAddedFromTwoBranches() throws Exception {
  223. // create file1 on master
  224. writeTrashFile(FILE1, FILE1);
  225. git.add().addFilepattern(FILE1).call();
  226. RevCommit masterCommit = git.commit().setMessage("Add file1 to master")
  227. .call();
  228. // create a branch named file2 and add file2
  229. createBranch(masterCommit, "refs/heads/file2");
  230. checkoutBranch("refs/heads/file2");
  231. writeTrashFile("file2", "file2");
  232. git.add().addFilepattern("file2").call();
  233. RevCommit addFile2 = git.commit().setMessage(
  234. "Add file2 to branch file2").call();
  235. // create a branch named file3 and add file3
  236. createBranch(masterCommit, "refs/heads/file3");
  237. checkoutBranch("refs/heads/file3");
  238. writeTrashFile("file3", "file3");
  239. git.add().addFilepattern("file3").call();
  240. git.commit().setMessage("Add file3 to branch file3").call();
  241. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  242. assertFalse(new File(db.getWorkTree(), "file2").exists());
  243. assertTrue(new File(db.getWorkTree(), "file3").exists());
  244. RebaseResult res = git.rebase().setUpstream("refs/heads/file2").call();
  245. assertEquals(Status.OK, res.getStatus());
  246. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  247. assertTrue(new File(db.getWorkTree(), "file2").exists());
  248. assertTrue(new File(db.getWorkTree(), "file3").exists());
  249. // our old branch should be checked out again
  250. assertEquals("refs/heads/file3", db.getFullBranch());
  251. assertEquals(addFile2, new RevWalk(db).parseCommit(
  252. db.resolve(Constants.HEAD)).getParent(0));
  253. checkoutBranch("refs/heads/file2");
  254. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  255. assertTrue(new File(db.getWorkTree(), "file2").exists());
  256. assertFalse(new File(db.getWorkTree(), "file3").exists());
  257. }
  258. @Test
  259. public void testStopOnConflict() throws Exception {
  260. // create file1 on master
  261. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  262. "2", "3");
  263. // change first line in master
  264. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  265. checkFile(FILE1, "1master", "2", "3");
  266. // create a topic branch based on second commit
  267. createBranch(firstInMaster, "refs/heads/topic");
  268. checkoutBranch("refs/heads/topic");
  269. // we have the old content again
  270. checkFile(FILE1, "1", "2", "3");
  271. // add a line (non-conflicting)
  272. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  273. "3", "topic4");
  274. // change first line (conflicting)
  275. RevCommit conflicting = writeFileAndCommit(FILE1,
  276. "change file1 in topic", "1topic", "2", "3", "topic4");
  277. RevCommit lastTopicCommit = writeFileAndCommit(FILE1,
  278. "change file1 in topic again", "1topic", "2", "3", "topic4");
  279. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  280. assertEquals(Status.STOPPED, res.getStatus());
  281. assertEquals(conflicting, res.getCurrentCommit());
  282. checkFile(FILE1,
  283. "<<<<<<< OURS\n1master\n=======\n1topic\n>>>>>>> THEIRS\n2\n3\ntopic4");
  284. assertEquals(RepositoryState.REBASING_INTERACTIVE, db
  285. .getRepositoryState());
  286. assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
  287. // the first one should be included, so we should have left two picks in
  288. // the file
  289. assertEquals(1, countPicks());
  290. // rebase should not succeed in this state
  291. try {
  292. git.rebase().setUpstream("refs/heads/master").call();
  293. fail("Expected exception was not thrown");
  294. } catch (WrongRepositoryStateException e) {
  295. // expected
  296. }
  297. // abort should reset to topic branch
  298. res = git.rebase().setOperation(Operation.ABORT).call();
  299. assertEquals(res.getStatus(), Status.ABORTED);
  300. assertEquals("refs/heads/topic", db.getFullBranch());
  301. checkFile(FILE1, "1topic", "2", "3", "topic4");
  302. RevWalk rw = new RevWalk(db);
  303. assertEquals(lastTopicCommit, rw
  304. .parseCommit(db.resolve(Constants.HEAD)));
  305. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  306. // rebase- dir in .git must be deleted
  307. assertFalse(new File(db.getDirectory(), "rebase-merge").exists());
  308. }
  309. @Test
  310. public void testStopOnConflictAndContinue() throws Exception {
  311. // create file1 on master
  312. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  313. "2", "3");
  314. // change in master
  315. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  316. checkFile(FILE1, "1master", "2", "3");
  317. // create a topic branch based on the first commit
  318. createBranch(firstInMaster, "refs/heads/topic");
  319. checkoutBranch("refs/heads/topic");
  320. // we have the old content again
  321. checkFile(FILE1, "1", "2", "3");
  322. // add a line (non-conflicting)
  323. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  324. "3", "4topic");
  325. // change first line (conflicting)
  326. writeFileAndCommit(FILE1,
  327. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  328. "3", "4topic");
  329. // change second line (not conflicting)
  330. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic",
  331. "2topic", "3", "4topic");
  332. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  333. assertEquals(Status.STOPPED, res.getStatus());
  334. // continue should throw a meaningful exception
  335. try {
  336. res = git.rebase().setOperation(Operation.CONTINUE).call();
  337. fail("Expected Exception not thrown");
  338. } catch (UnmergedPathsException e) {
  339. // expected
  340. }
  341. // merge the file; the second topic commit should go through
  342. writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");
  343. res = git.rebase().setOperation(Operation.CONTINUE).call();
  344. assertNotNull(res);
  345. assertEquals(Status.OK, res.getStatus());
  346. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  347. ObjectId headId = db.resolve(Constants.HEAD);
  348. RevWalk rw = new RevWalk(db);
  349. RevCommit rc = rw.parseCommit(headId);
  350. RevCommit parent = rw.parseCommit(rc.getParent(0));
  351. assertEquals("change file1 in topic\n\nThis is conflicting", parent
  352. .getFullMessage());
  353. }
  354. @Test
  355. public void testStopOnConflictAndFailContinueIfFileIsDirty()
  356. throws Exception {
  357. // create file1 on master
  358. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  359. "2", "3");
  360. // change in master
  361. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  362. checkFile(FILE1, "1master", "2", "3");
  363. // create a topic branch based on the first commit
  364. createBranch(firstInMaster, "refs/heads/topic");
  365. checkoutBranch("refs/heads/topic");
  366. // we have the old content again
  367. checkFile(FILE1, "1", "2", "3");
  368. // add a line (non-conflicting)
  369. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  370. "3", "4topic");
  371. // change first line (conflicting)
  372. writeFileAndCommit(FILE1,
  373. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  374. "3", "4topic");
  375. // change second line (not conflicting)
  376. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic",
  377. "2topic", "3", "4topic");
  378. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  379. assertEquals(Status.STOPPED, res.getStatus());
  380. git.add().addFilepattern(FILE1).call();
  381. File trashFile = writeTrashFile(FILE1, "Some local change");
  382. res = git.rebase().setOperation(Operation.CONTINUE).call();
  383. assertNotNull(res);
  384. assertEquals(Status.STOPPED, res.getStatus());
  385. checkFile(trashFile, "Some local change");
  386. }
  387. @Test
  388. public void testStopOnLastConflictAndContinue() throws Exception {
  389. // create file1 on master
  390. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  391. "2", "3");
  392. // change in master
  393. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  394. checkFile(FILE1, "1master", "2", "3");
  395. // create a topic branch based on the first commit
  396. createBranch(firstInMaster, "refs/heads/topic");
  397. checkoutBranch("refs/heads/topic");
  398. // we have the old content again
  399. checkFile(FILE1, "1", "2", "3");
  400. // add a line (non-conflicting)
  401. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  402. "3", "4topic");
  403. // change first line (conflicting)
  404. writeFileAndCommit(FILE1,
  405. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  406. "3", "4topic");
  407. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  408. assertEquals(Status.STOPPED, res.getStatus());
  409. // merge the file; the second topic commit should go through
  410. writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");
  411. res = git.rebase().setOperation(Operation.CONTINUE).call();
  412. assertNotNull(res);
  413. assertEquals(Status.OK, res.getStatus());
  414. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  415. }
  416. @Test
  417. public void testStopOnLastConflictAndSkip() throws Exception {
  418. // create file1 on master
  419. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  420. "2", "3");
  421. // change in master
  422. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  423. checkFile(FILE1, "1master", "2", "3");
  424. // create a topic branch based on the first commit
  425. createBranch(firstInMaster, "refs/heads/topic");
  426. checkoutBranch("refs/heads/topic");
  427. // we have the old content again
  428. checkFile(FILE1, "1", "2", "3");
  429. // add a line (non-conflicting)
  430. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  431. "3", "4topic");
  432. // change first line (conflicting)
  433. writeFileAndCommit(FILE1,
  434. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  435. "3", "4topic");
  436. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  437. assertEquals(Status.STOPPED, res.getStatus());
  438. // merge the file; the second topic commit should go through
  439. writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");
  440. res = git.rebase().setOperation(Operation.SKIP).call();
  441. assertNotNull(res);
  442. assertEquals(Status.OK, res.getStatus());
  443. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  444. }
  445. @Test
  446. public void testMergeFirstStopOnLastConflictAndSkip() throws Exception {
  447. // create file1 on master
  448. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  449. "2", "3");
  450. // change in master
  451. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  452. checkFile(FILE1, "1master", "2", "3");
  453. // create a topic branch based on the first commit
  454. createBranch(firstInMaster, "refs/heads/topic");
  455. checkoutBranch("refs/heads/topic");
  456. // we have the old content again
  457. checkFile(FILE1, "1", "2", "3");
  458. // add a line (conflicting)
  459. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1topic",
  460. "2", "3", "4topic");
  461. // change first line (conflicting again)
  462. writeFileAndCommit(FILE1,
  463. "change file1 in topic\n\nThis is conflicting", "1topicagain",
  464. "2", "3", "4topic");
  465. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  466. assertEquals(Status.STOPPED, res.getStatus());
  467. writeFileAndAdd(FILE1, "merged");
  468. res = git.rebase().setOperation(Operation.CONTINUE).call();
  469. assertEquals(Status.STOPPED, res.getStatus());
  470. res = git.rebase().setOperation(Operation.SKIP).call();
  471. assertNotNull(res);
  472. assertEquals(Status.OK, res.getStatus());
  473. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  474. checkFile(FILE1, "merged");
  475. }
  476. @Test
  477. public void testStopOnConflictAndSkipNoConflict() throws Exception {
  478. // create file1 on master
  479. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  480. "2", "3");
  481. // change in master
  482. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  483. checkFile(FILE1, "1master", "2", "3");
  484. // create a topic branch based on the first commit
  485. createBranch(firstInMaster, "refs/heads/topic");
  486. checkoutBranch("refs/heads/topic");
  487. // we have the old content again
  488. checkFile(FILE1, "1", "2", "3");
  489. // add a line (non-conflicting)
  490. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  491. "3", "4topic");
  492. // change first line (conflicting)
  493. writeFileAndCommit(FILE1,
  494. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  495. "3", "4topic");
  496. // change third line (not conflicting)
  497. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
  498. "3topic", "4topic");
  499. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  500. assertEquals(Status.STOPPED, res.getStatus());
  501. res = git.rebase().setOperation(Operation.SKIP).call();
  502. checkFile(FILE1, "1master", "2", "3topic", "4topic");
  503. assertEquals(Status.OK, res.getStatus());
  504. }
  505. @Test
  506. public void testStopOnConflictAndSkipWithConflict() throws Exception {
  507. // create file1 on master
  508. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  509. "2", "3", "4");
  510. // change in master
  511. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2",
  512. "3master", "4");
  513. checkFile(FILE1, "1master", "2", "3master", "4");
  514. // create a topic branch based on the first commit
  515. createBranch(firstInMaster, "refs/heads/topic");
  516. checkoutBranch("refs/heads/topic");
  517. // we have the old content again
  518. checkFile(FILE1, "1", "2", "3", "4");
  519. // add a line (non-conflicting)
  520. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  521. "3", "4", "5topic");
  522. // change first line (conflicting)
  523. writeFileAndCommit(FILE1,
  524. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  525. "3", "4", "5topic");
  526. // change third line (conflicting)
  527. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
  528. "3topic", "4", "5topic");
  529. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  530. assertEquals(Status.STOPPED, res.getStatus());
  531. res = git.rebase().setOperation(Operation.SKIP).call();
  532. // TODO is this correct? It is what the command line returns
  533. checkFile(FILE1,
  534. "1master\n2\n<<<<<<< OURS\n3master\n=======\n3topic\n>>>>>>> THEIRS\n4\n5topic");
  535. assertEquals(Status.STOPPED, res.getStatus());
  536. }
  537. @Test
  538. public void testStopOnConflictCommitAndContinue() throws Exception {
  539. // create file1 on master
  540. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  541. "2", "3");
  542. // change in master
  543. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  544. checkFile(FILE1, "1master", "2", "3");
  545. // create a topic branch based on the first commit
  546. createBranch(firstInMaster, "refs/heads/topic");
  547. checkoutBranch("refs/heads/topic");
  548. // we have the old content again
  549. checkFile(FILE1, "1", "2", "3");
  550. // add a line (non-conflicting)
  551. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  552. "3", "4topic");
  553. // change first line (conflicting)
  554. writeFileAndCommit(FILE1,
  555. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  556. "3", "4topic");
  557. // change second line (not conflicting)
  558. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
  559. "3topic", "4topic");
  560. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  561. assertEquals(Status.STOPPED, res.getStatus());
  562. // continue should throw a meaningful exception
  563. try {
  564. res = git.rebase().setOperation(Operation.CONTINUE).call();
  565. fail("Expected Exception not thrown");
  566. } catch (UnmergedPathsException e) {
  567. // expected
  568. }
  569. // merge the file; the second topic commit should go through
  570. writeFileAndCommit(FILE1, "A different commit message", "1topic", "2",
  571. "3", "4topic");
  572. res = git.rebase().setOperation(Operation.CONTINUE).call();
  573. assertNotNull(res);
  574. assertEquals(Status.OK, res.getStatus());
  575. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  576. ObjectId headId = db.resolve(Constants.HEAD);
  577. RevWalk rw = new RevWalk(db);
  578. RevCommit rc = rw.parseCommit(headId);
  579. RevCommit parent = rw.parseCommit(rc.getParent(0));
  580. assertEquals("A different commit message", parent.getFullMessage());
  581. }
  582. private RevCommit writeFileAndCommit(String fileName, String commitMessage,
  583. String... lines) throws Exception {
  584. StringBuilder sb = new StringBuilder();
  585. for (String line : lines) {
  586. sb.append(line);
  587. sb.append('\n');
  588. }
  589. writeTrashFile(fileName, sb.toString());
  590. git.add().addFilepattern(fileName).call();
  591. return git.commit().setMessage(commitMessage).call();
  592. }
  593. private void writeFileAndAdd(String fileName, String... lines)
  594. throws Exception {
  595. StringBuilder sb = new StringBuilder();
  596. for (String line : lines) {
  597. sb.append(line);
  598. sb.append('\n');
  599. }
  600. writeTrashFile(fileName, sb.toString());
  601. git.add().addFilepattern(fileName).call();
  602. }
  603. private void checkFile(String fileName, String... lines) throws Exception {
  604. File file = new File(db.getWorkTree(), fileName);
  605. StringBuilder sb = new StringBuilder();
  606. for (String line : lines) {
  607. sb.append(line);
  608. sb.append('\n');
  609. }
  610. checkFile(file, sb.toString());
  611. }
  612. @Test
  613. public void testStopOnConflictFileCreationAndDeletion() throws Exception {
  614. // create file1 on master
  615. writeTrashFile(FILE1, "Hello World");
  616. git.add().addFilepattern(FILE1).call();
  617. // create file2 on master
  618. File file2 = writeTrashFile("file2", "Hello World 2");
  619. git.add().addFilepattern("file2").call();
  620. // create file3 on master
  621. File file3 = writeTrashFile("file3", "Hello World 3");
  622. git.add().addFilepattern("file3").call();
  623. RevCommit firstInMaster = git.commit()
  624. .setMessage("Add file 1, 2 and 3").call();
  625. // create file4 on master
  626. File file4 = writeTrashFile("file4", "Hello World 4");
  627. git.add().addFilepattern("file4").call();
  628. deleteTrashFile("file2");
  629. git.add().setUpdate(true).addFilepattern("file2").call();
  630. // create folder folder6 on topic (conflicts with file folder6 on topic
  631. // later on)
  632. writeTrashFile("folder6/file1", "Hello World folder6");
  633. git.add().addFilepattern("folder6/file1").call();
  634. git.commit().setMessage(
  635. "Add file 4 and folder folder6, delete file2 on master").call();
  636. // create a topic branch based on second commit
  637. createBranch(firstInMaster, "refs/heads/topic");
  638. checkoutBranch("refs/heads/topic");
  639. deleteTrashFile("file3");
  640. git.add().setUpdate(true).addFilepattern("file3").call();
  641. // create file5 on topic
  642. File file5 = writeTrashFile("file5", "Hello World 5");
  643. git.add().addFilepattern("file5").call();
  644. git.commit().setMessage("Delete file3 and add file5 in topic").call();
  645. // create file folder6 on topic (conflicts with folder6 on master)
  646. writeTrashFile("folder6", "Hello World 6");
  647. git.add().addFilepattern("folder6").call();
  648. // create file7 on topic
  649. File file7 = writeTrashFile("file7", "Hello World 7");
  650. git.add().addFilepattern("file7").call();
  651. deleteTrashFile("file5");
  652. git.add().setUpdate(true).addFilepattern("file5").call();
  653. RevCommit conflicting = git.commit().setMessage(
  654. "Delete file5, add file folder6 and file7 in topic").call();
  655. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  656. assertEquals(Status.STOPPED, res.getStatus());
  657. assertEquals(conflicting, res.getCurrentCommit());
  658. assertEquals(RepositoryState.REBASING_INTERACTIVE, db
  659. .getRepositoryState());
  660. assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
  661. // the first one should be included, so we should have left two picks in
  662. // the file
  663. assertEquals(0, countPicks());
  664. assertFalse(file2.exists());
  665. assertFalse(file3.exists());
  666. assertTrue(file4.exists());
  667. assertFalse(file5.exists());
  668. assertTrue(file7.exists());
  669. // abort should reset to topic branch
  670. res = git.rebase().setOperation(Operation.ABORT).call();
  671. assertEquals(res.getStatus(), Status.ABORTED);
  672. assertEquals("refs/heads/topic", db.getFullBranch());
  673. RevWalk rw = new RevWalk(db);
  674. assertEquals(conflicting, rw.parseCommit(db.resolve(Constants.HEAD)));
  675. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  676. // rebase- dir in .git must be deleted
  677. assertFalse(new File(db.getDirectory(), "rebase-merge").exists());
  678. assertTrue(file2.exists());
  679. assertFalse(file3.exists());
  680. assertFalse(file4.exists());
  681. assertFalse(file5.exists());
  682. assertTrue(file7.exists());
  683. }
  684. @Test
  685. public void testAuthorScriptConverter() throws Exception {
  686. // -1 h timezone offset
  687. PersonIdent ident = new PersonIdent("Author name", "a.mail@some.com",
  688. 123456789123L, -60);
  689. String convertedAuthor = git.rebase().toAuthorScript(ident);
  690. String[] lines = convertedAuthor.split("\n");
  691. assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
  692. assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]);
  693. assertEquals("GIT_AUTHOR_DATE='123456789 -0100'", lines[2]);
  694. PersonIdent parsedIdent = git.rebase().parseAuthor(
  695. convertedAuthor.getBytes("UTF-8"));
  696. assertEquals(ident.getName(), parsedIdent.getName());
  697. assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
  698. // this is rounded to the last second
  699. assertEquals(123456789000L, parsedIdent.getWhen().getTime());
  700. assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
  701. // + 9.5h timezone offset
  702. ident = new PersonIdent("Author name", "a.mail@some.com",
  703. 123456789123L, +570);
  704. convertedAuthor = git.rebase().toAuthorScript(ident);
  705. lines = convertedAuthor.split("\n");
  706. assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
  707. assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]);
  708. assertEquals("GIT_AUTHOR_DATE='123456789 +0930'", lines[2]);
  709. parsedIdent = git.rebase().parseAuthor(
  710. convertedAuthor.getBytes("UTF-8"));
  711. assertEquals(ident.getName(), parsedIdent.getName());
  712. assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
  713. assertEquals(123456789000L, parsedIdent.getWhen().getTime());
  714. assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
  715. }
  716. @Test
  717. public void testRepositoryStateChecks() throws Exception {
  718. try {
  719. git.rebase().setOperation(Operation.ABORT).call();
  720. fail("Expected Exception not thrown");
  721. } catch (WrongRepositoryStateException e) {
  722. // expected
  723. }
  724. try {
  725. git.rebase().setOperation(Operation.SKIP).call();
  726. fail("Expected Exception not thrown");
  727. } catch (WrongRepositoryStateException e) {
  728. // expected
  729. }
  730. try {
  731. git.rebase().setOperation(Operation.CONTINUE).call();
  732. fail("Expected Exception not thrown");
  733. } catch (WrongRepositoryStateException e) {
  734. // expected
  735. }
  736. }
  737. private int countPicks() throws IOException {
  738. int count = 0;
  739. File todoFile = new File(db.getDirectory(),
  740. "rebase-merge/git-rebase-todo");
  741. BufferedReader br = new BufferedReader(new InputStreamReader(
  742. new FileInputStream(todoFile), "UTF-8"));
  743. try {
  744. String line = br.readLine();
  745. while (line != null) {
  746. String actionToken = line.substring(0, line.indexOf(' '));
  747. Action action = null;
  748. try {
  749. action = Action.parse(actionToken);
  750. } catch (Exception e) {
  751. // ignore
  752. }
  753. if (action != null)
  754. count++;
  755. line = br.readLine();
  756. }
  757. return count;
  758. } finally {
  759. br.close();
  760. }
  761. }
  762. }