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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473
  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.MergeResult.MergeStatus;
  55. import org.eclipse.jgit.api.RebaseCommand.Action;
  56. import org.eclipse.jgit.api.RebaseCommand.Operation;
  57. import org.eclipse.jgit.api.RebaseResult.Status;
  58. import org.eclipse.jgit.api.errors.JGitInternalException;
  59. import org.eclipse.jgit.api.errors.RefNotFoundException;
  60. import org.eclipse.jgit.api.errors.UnmergedPathsException;
  61. import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
  62. import org.eclipse.jgit.dircache.DirCacheCheckout;
  63. import org.eclipse.jgit.lib.Constants;
  64. import org.eclipse.jgit.lib.ObjectId;
  65. import org.eclipse.jgit.lib.PersonIdent;
  66. import org.eclipse.jgit.lib.RefUpdate;
  67. import org.eclipse.jgit.lib.RepositoryState;
  68. import org.eclipse.jgit.lib.RepositoryTestCase;
  69. import org.eclipse.jgit.merge.MergeStrategy;
  70. import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
  71. import org.eclipse.jgit.revwalk.RevCommit;
  72. import org.eclipse.jgit.revwalk.RevWalk;
  73. import org.junit.Before;
  74. import org.junit.Test;
  75. public class RebaseCommandTest extends RepositoryTestCase {
  76. private static final String FILE1 = "file1";
  77. protected Git git;
  78. @Override
  79. @Before
  80. public void setUp() throws Exception {
  81. super.setUp();
  82. this.git = new Git(db);
  83. }
  84. private void checkoutCommit(RevCommit commit) throws IllegalStateException,
  85. IOException {
  86. RevWalk walk = new RevWalk(db);
  87. RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD));
  88. DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree(), db
  89. .lockDirCache(), commit.getTree());
  90. dco.setFailOnConflict(true);
  91. dco.checkout();
  92. walk.release();
  93. // update the HEAD
  94. RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
  95. refUpdate.setNewObjectId(commit);
  96. refUpdate.forceUpdate();
  97. }
  98. @Test
  99. public void testFastForwardWithNewFile() throws Exception {
  100. // create file1 on master
  101. writeTrashFile(FILE1, FILE1);
  102. git.add().addFilepattern(FILE1).call();
  103. RevCommit first = git.commit().setMessage("Add file1").call();
  104. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  105. // create a topic branch
  106. createBranch(first, "refs/heads/topic");
  107. // create file2 on master
  108. File file2 = writeTrashFile("file2", "file2");
  109. git.add().addFilepattern("file2").call();
  110. git.commit().setMessage("Add file2").call();
  111. assertTrue(new File(db.getWorkTree(), "file2").exists());
  112. checkoutBranch("refs/heads/topic");
  113. assertFalse(new File(db.getWorkTree(), "file2").exists());
  114. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  115. assertTrue(new File(db.getWorkTree(), "file2").exists());
  116. checkFile(file2, "file2");
  117. assertEquals(Status.FAST_FORWARD, res.getStatus());
  118. }
  119. @Test
  120. public void testFastForwardWithMultipleCommits() throws Exception {
  121. // create file1 on master
  122. writeTrashFile(FILE1, FILE1);
  123. git.add().addFilepattern(FILE1).call();
  124. RevCommit first = git.commit().setMessage("Add file1").call();
  125. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  126. // create a topic branch
  127. createBranch(first, "refs/heads/topic");
  128. // create file2 on master
  129. File file2 = writeTrashFile("file2", "file2");
  130. git.add().addFilepattern("file2").call();
  131. git.commit().setMessage("Add file2").call();
  132. assertTrue(new File(db.getWorkTree(), "file2").exists());
  133. // write a second commit
  134. writeTrashFile("file2", "file2 new content");
  135. git.add().addFilepattern("file2").call();
  136. git.commit().setMessage("Change content of file2").call();
  137. checkoutBranch("refs/heads/topic");
  138. assertFalse(new File(db.getWorkTree(), "file2").exists());
  139. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  140. assertTrue(new File(db.getWorkTree(), "file2").exists());
  141. checkFile(file2, "file2 new content");
  142. assertEquals(Status.FAST_FORWARD, res.getStatus());
  143. }
  144. @Test
  145. public void testRebaseFailsCantCherryPickMergeCommits()
  146. throws Exception {
  147. /**
  148. * Create the following commits and then attempt to rebase topic onto
  149. * master. This will fail as the cherry-pick list C, D, E an F contains
  150. * a merge commit (F).
  151. *
  152. * <pre>
  153. * A - B (master)
  154. * \
  155. * C - D - F (topic)
  156. * \ /
  157. * E - (side)
  158. * </pre>
  159. */
  160. // create file1 on master
  161. writeTrashFile(FILE1, FILE1);
  162. git.add().addFilepattern(FILE1).call();
  163. RevCommit first = git.commit().setMessage("Add file1").call();
  164. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  165. // create a topic branch
  166. createBranch(first, "refs/heads/topic");
  167. // update FILE1 on master
  168. writeTrashFile(FILE1, "blah");
  169. git.add().addFilepattern(FILE1).call();
  170. git.commit().setMessage("updated file1 on master").call();
  171. checkoutBranch("refs/heads/topic");
  172. writeTrashFile("file3", "more changess");
  173. git.add().addFilepattern("file3").call();
  174. RevCommit topicCommit = git.commit()
  175. .setMessage("update file3 on topic").call();
  176. // create a branch from the topic commit
  177. createBranch(topicCommit, "refs/heads/side");
  178. // second commit on topic
  179. writeTrashFile("file2", "file2");
  180. git.add().addFilepattern("file2").call();
  181. git.commit().setMessage("Add file2").call();
  182. assertTrue(new File(db.getWorkTree(), "file2").exists());
  183. // switch to side branch and update file2
  184. checkoutBranch("refs/heads/side");
  185. writeTrashFile("file3", "more change");
  186. git.add().addFilepattern("file3").call();
  187. RevCommit sideCommit = git.commit().setMessage("update file2 on side")
  188. .call();
  189. // switch back to topic and merge in side
  190. checkoutBranch("refs/heads/topic");
  191. MergeResult result = git.merge().include(sideCommit.getId())
  192. .setStrategy(MergeStrategy.RESOLVE).call();
  193. assertEquals(MergeStatus.MERGED, result.getMergeStatus());
  194. try {
  195. RebaseResult rebase = git.rebase().setUpstream("refs/heads/master")
  196. .call();
  197. fail("MultipleParentsNotAllowedException expected: "
  198. + rebase.getStatus());
  199. } catch (JGitInternalException e) {
  200. // expected
  201. }
  202. }
  203. @Test
  204. public void testRebaseParentOntoHeadShouldBeUptoDate() throws Exception {
  205. writeTrashFile(FILE1, FILE1);
  206. git.add().addFilepattern(FILE1).call();
  207. RevCommit parent = git.commit().setMessage("parent comment").call();
  208. writeTrashFile(FILE1, "another change");
  209. git.add().addFilepattern(FILE1).call();
  210. git.commit().setMessage("head commit").call();
  211. RebaseResult result = git.rebase().setUpstream(parent).call();
  212. assertEquals(Status.UP_TO_DATE, result.getStatus());
  213. }
  214. @Test
  215. public void testUpToDate() throws Exception {
  216. // create file1 on master
  217. writeTrashFile(FILE1, FILE1);
  218. git.add().addFilepattern(FILE1).call();
  219. RevCommit first = git.commit().setMessage("Add file1").call();
  220. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  221. RebaseResult res = git.rebase().setUpstream(first).call();
  222. assertEquals(Status.UP_TO_DATE, res.getStatus());
  223. }
  224. @Test
  225. public void testUnknownUpstream() throws Exception {
  226. // create file1 on master
  227. writeTrashFile(FILE1, FILE1);
  228. git.add().addFilepattern(FILE1).call();
  229. git.commit().setMessage("Add file1").call();
  230. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  231. try {
  232. git.rebase().setUpstream("refs/heads/xyz").call();
  233. fail("expected exception was not thrown");
  234. } catch (RefNotFoundException e) {
  235. // expected exception
  236. }
  237. }
  238. @Test
  239. public void testConflictFreeWithSingleFile() throws Exception {
  240. // create file1 on master
  241. File theFile = writeTrashFile(FILE1, "1\n2\n3\n");
  242. git.add().addFilepattern(FILE1).call();
  243. RevCommit second = git.commit().setMessage("Add file1").call();
  244. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  245. // change first line in master and commit
  246. writeTrashFile(FILE1, "1master\n2\n3\n");
  247. checkFile(theFile, "1master\n2\n3\n");
  248. git.add().addFilepattern(FILE1).call();
  249. RevCommit lastMasterChange = git.commit().setMessage(
  250. "change file1 in master").call();
  251. // create a topic branch based on second commit
  252. createBranch(second, "refs/heads/topic");
  253. checkoutBranch("refs/heads/topic");
  254. // we have the old content again
  255. checkFile(theFile, "1\n2\n3\n");
  256. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  257. // change third line in topic branch
  258. writeTrashFile(FILE1, "1\n2\n3\ntopic\n");
  259. git.add().addFilepattern(FILE1).call();
  260. RevCommit origHead = git.commit().setMessage("change file1 in topic")
  261. .call();
  262. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  263. assertEquals(Status.OK, res.getStatus());
  264. checkFile(theFile, "1master\n2\n3\ntopic\n");
  265. // our old branch should be checked out again
  266. assertEquals("refs/heads/topic", db.getFullBranch());
  267. assertEquals(lastMasterChange, new RevWalk(db).parseCommit(
  268. db.resolve(Constants.HEAD)).getParent(0));
  269. assertEquals(origHead, db.readOrigHead());
  270. }
  271. @Test
  272. public void testDetachedHead() throws Exception {
  273. // create file1 on master
  274. File theFile = writeTrashFile(FILE1, "1\n2\n3\n");
  275. git.add().addFilepattern(FILE1).call();
  276. RevCommit second = git.commit().setMessage("Add file1").call();
  277. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  278. // change first line in master and commit
  279. writeTrashFile(FILE1, "1master\n2\n3\n");
  280. checkFile(theFile, "1master\n2\n3\n");
  281. git.add().addFilepattern(FILE1).call();
  282. RevCommit lastMasterChange = git.commit().setMessage(
  283. "change file1 in master").call();
  284. // create a topic branch based on second commit
  285. createBranch(second, "refs/heads/topic");
  286. checkoutBranch("refs/heads/topic");
  287. // we have the old content again
  288. checkFile(theFile, "1\n2\n3\n");
  289. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  290. // change third line in topic branch
  291. writeTrashFile(FILE1, "1\n2\n3\ntopic\n");
  292. git.add().addFilepattern(FILE1).call();
  293. RevCommit topicCommit = git.commit()
  294. .setMessage("change file1 in topic").call();
  295. checkoutBranch("refs/heads/master");
  296. checkoutCommit(topicCommit);
  297. assertEquals(topicCommit.getId().getName(), db.getFullBranch());
  298. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  299. assertEquals(Status.OK, res.getStatus());
  300. checkFile(theFile, "1master\n2\n3\ntopic\n");
  301. assertEquals(lastMasterChange, new RevWalk(db).parseCommit(
  302. db.resolve(Constants.HEAD)).getParent(0));
  303. }
  304. @Test
  305. public void testFilesAddedFromTwoBranches() throws Exception {
  306. // create file1 on master
  307. writeTrashFile(FILE1, FILE1);
  308. git.add().addFilepattern(FILE1).call();
  309. RevCommit masterCommit = git.commit().setMessage("Add file1 to master")
  310. .call();
  311. // create a branch named file2 and add file2
  312. createBranch(masterCommit, "refs/heads/file2");
  313. checkoutBranch("refs/heads/file2");
  314. writeTrashFile("file2", "file2");
  315. git.add().addFilepattern("file2").call();
  316. RevCommit addFile2 = git.commit().setMessage(
  317. "Add file2 to branch file2").call();
  318. // create a branch named file3 and add file3
  319. createBranch(masterCommit, "refs/heads/file3");
  320. checkoutBranch("refs/heads/file3");
  321. writeTrashFile("file3", "file3");
  322. git.add().addFilepattern("file3").call();
  323. git.commit().setMessage("Add file3 to branch file3").call();
  324. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  325. assertFalse(new File(db.getWorkTree(), "file2").exists());
  326. assertTrue(new File(db.getWorkTree(), "file3").exists());
  327. RebaseResult res = git.rebase().setUpstream("refs/heads/file2").call();
  328. assertEquals(Status.OK, res.getStatus());
  329. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  330. assertTrue(new File(db.getWorkTree(), "file2").exists());
  331. assertTrue(new File(db.getWorkTree(), "file3").exists());
  332. // our old branch should be checked out again
  333. assertEquals("refs/heads/file3", db.getFullBranch());
  334. assertEquals(addFile2, new RevWalk(db).parseCommit(
  335. db.resolve(Constants.HEAD)).getParent(0));
  336. checkoutBranch("refs/heads/file2");
  337. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  338. assertTrue(new File(db.getWorkTree(), "file2").exists());
  339. assertFalse(new File(db.getWorkTree(), "file3").exists());
  340. }
  341. @Test
  342. public void testStopOnConflict() throws Exception {
  343. // create file1 on master
  344. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  345. "2", "3");
  346. // change first line in master
  347. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  348. checkFile(FILE1, "1master", "2", "3");
  349. // create a topic branch based on second commit
  350. createBranch(firstInMaster, "refs/heads/topic");
  351. checkoutBranch("refs/heads/topic");
  352. // we have the old content again
  353. checkFile(FILE1, "1", "2", "3");
  354. // add a line (non-conflicting)
  355. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  356. "3", "topic4");
  357. // change first line (conflicting)
  358. RevCommit conflicting = writeFileAndCommit(FILE1,
  359. "change file1 in topic", "1topic", "2", "3", "topic4");
  360. RevCommit lastTopicCommit = writeFileAndCommit(FILE1,
  361. "change file1 in topic again", "1topic", "2", "3", "topic4");
  362. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  363. assertEquals(Status.STOPPED, res.getStatus());
  364. assertEquals(conflicting, res.getCurrentCommit());
  365. checkFile(FILE1,
  366. "<<<<<<< OURS\n1master\n=======\n1topic\n>>>>>>> THEIRS\n2\n3\ntopic4");
  367. assertEquals(RepositoryState.REBASING_INTERACTIVE, db
  368. .getRepositoryState());
  369. assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
  370. // the first one should be included, so we should have left two picks in
  371. // the file
  372. assertEquals(1, countPicks());
  373. // rebase should not succeed in this state
  374. try {
  375. git.rebase().setUpstream("refs/heads/master").call();
  376. fail("Expected exception was not thrown");
  377. } catch (WrongRepositoryStateException e) {
  378. // expected
  379. }
  380. // abort should reset to topic branch
  381. res = git.rebase().setOperation(Operation.ABORT).call();
  382. assertEquals(res.getStatus(), Status.ABORTED);
  383. assertEquals("refs/heads/topic", db.getFullBranch());
  384. checkFile(FILE1, "1topic", "2", "3", "topic4");
  385. RevWalk rw = new RevWalk(db);
  386. assertEquals(lastTopicCommit, rw
  387. .parseCommit(db.resolve(Constants.HEAD)));
  388. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  389. // rebase- dir in .git must be deleted
  390. assertFalse(new File(db.getDirectory(), "rebase-merge").exists());
  391. }
  392. @Test
  393. public void testStopOnConflictAndContinue() throws Exception {
  394. // create file1 on master
  395. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  396. "2", "3");
  397. // change in master
  398. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  399. checkFile(FILE1, "1master", "2", "3");
  400. // create a topic branch based on the first commit
  401. createBranch(firstInMaster, "refs/heads/topic");
  402. checkoutBranch("refs/heads/topic");
  403. // we have the old content again
  404. checkFile(FILE1, "1", "2", "3");
  405. // add a line (non-conflicting)
  406. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  407. "3", "4topic");
  408. // change first line (conflicting)
  409. writeFileAndCommit(FILE1,
  410. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  411. "3", "4topic");
  412. // change second line (not conflicting)
  413. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic",
  414. "2topic", "3", "4topic");
  415. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  416. assertEquals(Status.STOPPED, res.getStatus());
  417. // continue should throw a meaningful exception
  418. try {
  419. res = git.rebase().setOperation(Operation.CONTINUE).call();
  420. fail("Expected Exception not thrown");
  421. } catch (UnmergedPathsException e) {
  422. // expected
  423. }
  424. // merge the file; the second topic commit should go through
  425. writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");
  426. res = git.rebase().setOperation(Operation.CONTINUE).call();
  427. assertNotNull(res);
  428. assertEquals(Status.OK, res.getStatus());
  429. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  430. ObjectId headId = db.resolve(Constants.HEAD);
  431. RevWalk rw = new RevWalk(db);
  432. RevCommit rc = rw.parseCommit(headId);
  433. RevCommit parent = rw.parseCommit(rc.getParent(0));
  434. assertEquals("change file1 in topic\n\nThis is conflicting", parent
  435. .getFullMessage());
  436. }
  437. @Test
  438. public void testStopOnConflictAndContinueWithNoDeltaToMaster()
  439. throws Exception {
  440. // create file1 on master
  441. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  442. "2", "3");
  443. // change in master
  444. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  445. checkFile(FILE1, "1master", "2", "3");
  446. // create a topic branch based on the first commit
  447. createBranch(firstInMaster, "refs/heads/topic");
  448. checkoutBranch("refs/heads/topic");
  449. // we have the old content again
  450. checkFile(FILE1, "1", "2", "3");
  451. // change first line (conflicting)
  452. writeFileAndCommit(FILE1,
  453. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  454. "3", "4topic");
  455. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  456. assertEquals(Status.STOPPED, res.getStatus());
  457. // continue should throw a meaningful exception
  458. try {
  459. res = git.rebase().setOperation(Operation.CONTINUE).call();
  460. fail("Expected Exception not thrown");
  461. } catch (UnmergedPathsException e) {
  462. // expected
  463. }
  464. // merge the file; the second topic commit should go through
  465. writeFileAndAdd(FILE1, "1master", "2", "3");
  466. res = git.rebase().setOperation(Operation.CONTINUE).call();
  467. assertNotNull(res);
  468. assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus());
  469. assertEquals(RepositoryState.REBASING_INTERACTIVE,
  470. db.getRepositoryState());
  471. git.rebase().setOperation(Operation.SKIP).call();
  472. ObjectId headId = db.resolve(Constants.HEAD);
  473. RevWalk rw = new RevWalk(db);
  474. RevCommit rc = rw.parseCommit(headId);
  475. assertEquals("change file1 in master", rc.getFullMessage());
  476. }
  477. @Test
  478. public void testStopOnConflictAndFailContinueIfFileIsDirty()
  479. throws Exception {
  480. // create file1 on master
  481. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  482. "2", "3");
  483. // change in master
  484. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  485. checkFile(FILE1, "1master", "2", "3");
  486. // create a topic branch based on the first commit
  487. createBranch(firstInMaster, "refs/heads/topic");
  488. checkoutBranch("refs/heads/topic");
  489. // we have the old content again
  490. checkFile(FILE1, "1", "2", "3");
  491. // add a line (non-conflicting)
  492. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  493. "3", "4topic");
  494. // change first line (conflicting)
  495. writeFileAndCommit(FILE1,
  496. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  497. "3", "4topic");
  498. // change second line (not conflicting)
  499. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic",
  500. "2topic", "3", "4topic");
  501. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  502. assertEquals(Status.STOPPED, res.getStatus());
  503. git.add().addFilepattern(FILE1).call();
  504. File trashFile = writeTrashFile(FILE1, "Some local change");
  505. res = git.rebase().setOperation(Operation.CONTINUE).call();
  506. assertNotNull(res);
  507. assertEquals(Status.STOPPED, res.getStatus());
  508. checkFile(trashFile, "Some local change");
  509. }
  510. @Test
  511. public void testStopOnLastConflictAndContinue() throws Exception {
  512. // create file1 on master
  513. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  514. "2", "3");
  515. // change in master
  516. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  517. checkFile(FILE1, "1master", "2", "3");
  518. // create a topic branch based on the first commit
  519. createBranch(firstInMaster, "refs/heads/topic");
  520. checkoutBranch("refs/heads/topic");
  521. // we have the old content again
  522. checkFile(FILE1, "1", "2", "3");
  523. // add a line (non-conflicting)
  524. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  525. "3", "4topic");
  526. // change first line (conflicting)
  527. writeFileAndCommit(FILE1,
  528. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  529. "3", "4topic");
  530. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  531. assertEquals(Status.STOPPED, res.getStatus());
  532. // merge the file; the second topic commit should go through
  533. writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");
  534. res = git.rebase().setOperation(Operation.CONTINUE).call();
  535. assertNotNull(res);
  536. assertEquals(Status.OK, res.getStatus());
  537. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  538. }
  539. @Test
  540. public void testStopOnLastConflictAndSkip() throws Exception {
  541. // create file1 on master
  542. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  543. "2", "3");
  544. // change in master
  545. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  546. checkFile(FILE1, "1master", "2", "3");
  547. // create a topic branch based on the first commit
  548. createBranch(firstInMaster, "refs/heads/topic");
  549. checkoutBranch("refs/heads/topic");
  550. // we have the old content again
  551. checkFile(FILE1, "1", "2", "3");
  552. // add a line (non-conflicting)
  553. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  554. "3", "4topic");
  555. // change first line (conflicting)
  556. writeFileAndCommit(FILE1,
  557. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  558. "3", "4topic");
  559. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  560. assertEquals(Status.STOPPED, res.getStatus());
  561. // merge the file; the second topic commit should go through
  562. writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");
  563. res = git.rebase().setOperation(Operation.SKIP).call();
  564. assertNotNull(res);
  565. assertEquals(Status.OK, res.getStatus());
  566. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  567. }
  568. @Test
  569. public void testMergeFirstStopOnLastConflictAndSkip() throws Exception {
  570. // create file1 on master
  571. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  572. "2", "3");
  573. // change in master
  574. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  575. checkFile(FILE1, "1master", "2", "3");
  576. // create a topic branch based on the first commit
  577. createBranch(firstInMaster, "refs/heads/topic");
  578. checkoutBranch("refs/heads/topic");
  579. // we have the old content again
  580. checkFile(FILE1, "1", "2", "3");
  581. // add a line (conflicting)
  582. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1topic",
  583. "2", "3", "4topic");
  584. // change first line (conflicting again)
  585. writeFileAndCommit(FILE1,
  586. "change file1 in topic\n\nThis is conflicting", "1topicagain",
  587. "2", "3", "4topic");
  588. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  589. assertEquals(Status.STOPPED, res.getStatus());
  590. writeFileAndAdd(FILE1, "merged");
  591. res = git.rebase().setOperation(Operation.CONTINUE).call();
  592. assertEquals(Status.STOPPED, res.getStatus());
  593. res = git.rebase().setOperation(Operation.SKIP).call();
  594. assertNotNull(res);
  595. assertEquals(Status.OK, res.getStatus());
  596. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  597. checkFile(FILE1, "merged");
  598. }
  599. @Test
  600. public void testStopOnConflictAndSkipNoConflict() throws Exception {
  601. // create file1 on master
  602. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  603. "2", "3");
  604. // change in master
  605. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  606. checkFile(FILE1, "1master", "2", "3");
  607. // create a topic branch based on the first commit
  608. createBranch(firstInMaster, "refs/heads/topic");
  609. checkoutBranch("refs/heads/topic");
  610. // we have the old content again
  611. checkFile(FILE1, "1", "2", "3");
  612. // add a line (non-conflicting)
  613. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  614. "3", "4topic");
  615. // change first line (conflicting)
  616. writeFileAndCommit(FILE1,
  617. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  618. "3", "4topic");
  619. // change third line (not conflicting)
  620. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
  621. "3topic", "4topic");
  622. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  623. assertEquals(Status.STOPPED, res.getStatus());
  624. res = git.rebase().setOperation(Operation.SKIP).call();
  625. checkFile(FILE1, "1master", "2", "3topic", "4topic");
  626. assertEquals(Status.OK, res.getStatus());
  627. }
  628. @Test
  629. public void testStopOnConflictAndSkipWithConflict() throws Exception {
  630. // create file1 on master
  631. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  632. "2", "3", "4");
  633. // change in master
  634. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2",
  635. "3master", "4");
  636. checkFile(FILE1, "1master", "2", "3master", "4");
  637. // create a topic branch based on the first commit
  638. createBranch(firstInMaster, "refs/heads/topic");
  639. checkoutBranch("refs/heads/topic");
  640. // we have the old content again
  641. checkFile(FILE1, "1", "2", "3", "4");
  642. // add a line (non-conflicting)
  643. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  644. "3", "4", "5topic");
  645. // change first line (conflicting)
  646. writeFileAndCommit(FILE1,
  647. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  648. "3", "4", "5topic");
  649. // change third line (conflicting)
  650. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
  651. "3topic", "4", "5topic");
  652. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  653. assertEquals(Status.STOPPED, res.getStatus());
  654. res = git.rebase().setOperation(Operation.SKIP).call();
  655. // TODO is this correct? It is what the command line returns
  656. checkFile(FILE1,
  657. "1master\n2\n<<<<<<< OURS\n3master\n=======\n3topic\n>>>>>>> THEIRS\n4\n5topic");
  658. assertEquals(Status.STOPPED, res.getStatus());
  659. }
  660. @Test
  661. public void testStopOnConflictCommitAndContinue() throws Exception {
  662. // create file1 on master
  663. RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
  664. "2", "3");
  665. // change in master
  666. writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
  667. checkFile(FILE1, "1master", "2", "3");
  668. // create a topic branch based on the first commit
  669. createBranch(firstInMaster, "refs/heads/topic");
  670. checkoutBranch("refs/heads/topic");
  671. // we have the old content again
  672. checkFile(FILE1, "1", "2", "3");
  673. // add a line (non-conflicting)
  674. writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
  675. "3", "4topic");
  676. // change first line (conflicting)
  677. writeFileAndCommit(FILE1,
  678. "change file1 in topic\n\nThis is conflicting", "1topic", "2",
  679. "3", "4topic");
  680. // change second line (not conflicting)
  681. writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
  682. "3topic", "4topic");
  683. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  684. assertEquals(Status.STOPPED, res.getStatus());
  685. // continue should throw a meaningful exception
  686. try {
  687. res = git.rebase().setOperation(Operation.CONTINUE).call();
  688. fail("Expected Exception not thrown");
  689. } catch (UnmergedPathsException e) {
  690. // expected
  691. }
  692. // merge the file; the second topic commit should go through
  693. writeFileAndCommit(FILE1, "A different commit message", "1topic", "2",
  694. "3", "4topic");
  695. res = git.rebase().setOperation(Operation.CONTINUE).call();
  696. assertNotNull(res);
  697. // nothing to commit. this leaves the repo state in rebase, so that the
  698. // user can decide what to do. if he accidentally committed, reset soft,
  699. // and continue, if he really has nothing to commit, skip.
  700. assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus());
  701. assertEquals(RepositoryState.REBASING_INTERACTIVE,
  702. db.getRepositoryState());
  703. git.rebase().setOperation(Operation.SKIP).call();
  704. ObjectId headId = db.resolve(Constants.HEAD);
  705. RevWalk rw = new RevWalk(db);
  706. RevCommit rc = rw.parseCommit(headId);
  707. RevCommit parent = rw.parseCommit(rc.getParent(0));
  708. assertEquals("A different commit message", parent.getFullMessage());
  709. }
  710. private RevCommit writeFileAndCommit(String fileName, String commitMessage,
  711. String... lines) throws Exception {
  712. StringBuilder sb = new StringBuilder();
  713. for (String line : lines) {
  714. sb.append(line);
  715. sb.append('\n');
  716. }
  717. writeTrashFile(fileName, sb.toString());
  718. git.add().addFilepattern(fileName).call();
  719. return git.commit().setMessage(commitMessage).call();
  720. }
  721. private void writeFileAndAdd(String fileName, String... lines)
  722. throws Exception {
  723. StringBuilder sb = new StringBuilder();
  724. for (String line : lines) {
  725. sb.append(line);
  726. sb.append('\n');
  727. }
  728. writeTrashFile(fileName, sb.toString());
  729. git.add().addFilepattern(fileName).call();
  730. }
  731. private void checkFile(String fileName, String... lines) throws Exception {
  732. File file = new File(db.getWorkTree(), fileName);
  733. StringBuilder sb = new StringBuilder();
  734. for (String line : lines) {
  735. sb.append(line);
  736. sb.append('\n');
  737. }
  738. checkFile(file, sb.toString());
  739. }
  740. @Test
  741. public void testStopOnConflictFileCreationAndDeletion() throws Exception {
  742. // create file1 on master
  743. writeTrashFile(FILE1, "Hello World");
  744. git.add().addFilepattern(FILE1).call();
  745. // create file2 on master
  746. File file2 = writeTrashFile("file2", "Hello World 2");
  747. git.add().addFilepattern("file2").call();
  748. // create file3 on master
  749. File file3 = writeTrashFile("file3", "Hello World 3");
  750. git.add().addFilepattern("file3").call();
  751. RevCommit firstInMaster = git.commit()
  752. .setMessage("Add file 1, 2 and 3").call();
  753. // create file4 on master
  754. File file4 = writeTrashFile("file4", "Hello World 4");
  755. git.add().addFilepattern("file4").call();
  756. deleteTrashFile("file2");
  757. git.add().setUpdate(true).addFilepattern("file2").call();
  758. // create folder folder6 on topic (conflicts with file folder6 on topic
  759. // later on)
  760. writeTrashFile("folder6/file1", "Hello World folder6");
  761. git.add().addFilepattern("folder6/file1").call();
  762. git.commit().setMessage(
  763. "Add file 4 and folder folder6, delete file2 on master").call();
  764. // create a topic branch based on second commit
  765. createBranch(firstInMaster, "refs/heads/topic");
  766. checkoutBranch("refs/heads/topic");
  767. deleteTrashFile("file3");
  768. git.add().setUpdate(true).addFilepattern("file3").call();
  769. // create file5 on topic
  770. File file5 = writeTrashFile("file5", "Hello World 5");
  771. git.add().addFilepattern("file5").call();
  772. git.commit().setMessage("Delete file3 and add file5 in topic").call();
  773. // create file folder6 on topic (conflicts with folder6 on master)
  774. writeTrashFile("folder6", "Hello World 6");
  775. git.add().addFilepattern("folder6").call();
  776. // create file7 on topic
  777. File file7 = writeTrashFile("file7", "Hello World 7");
  778. git.add().addFilepattern("file7").call();
  779. deleteTrashFile("file5");
  780. git.add().setUpdate(true).addFilepattern("file5").call();
  781. RevCommit conflicting = git.commit().setMessage(
  782. "Delete file5, add file folder6 and file7 in topic").call();
  783. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  784. assertEquals(Status.STOPPED, res.getStatus());
  785. assertEquals(conflicting, res.getCurrentCommit());
  786. assertEquals(RepositoryState.REBASING_INTERACTIVE, db
  787. .getRepositoryState());
  788. assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
  789. // the first one should be included, so we should have left two picks in
  790. // the file
  791. assertEquals(0, countPicks());
  792. assertFalse(file2.exists());
  793. assertFalse(file3.exists());
  794. assertTrue(file4.exists());
  795. assertFalse(file5.exists());
  796. assertTrue(file7.exists());
  797. // abort should reset to topic branch
  798. res = git.rebase().setOperation(Operation.ABORT).call();
  799. assertEquals(res.getStatus(), Status.ABORTED);
  800. assertEquals("refs/heads/topic", db.getFullBranch());
  801. RevWalk rw = new RevWalk(db);
  802. assertEquals(conflicting, rw.parseCommit(db.resolve(Constants.HEAD)));
  803. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  804. // rebase- dir in .git must be deleted
  805. assertFalse(new File(db.getDirectory(), "rebase-merge").exists());
  806. assertTrue(file2.exists());
  807. assertFalse(file3.exists());
  808. assertFalse(file4.exists());
  809. assertFalse(file5.exists());
  810. assertTrue(file7.exists());
  811. }
  812. @Test
  813. public void testAuthorScriptConverter() throws Exception {
  814. // -1 h timezone offset
  815. PersonIdent ident = new PersonIdent("Author name", "a.mail@some.com",
  816. 123456789123L, -60);
  817. String convertedAuthor = git.rebase().toAuthorScript(ident);
  818. String[] lines = convertedAuthor.split("\n");
  819. assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
  820. assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]);
  821. assertEquals("GIT_AUTHOR_DATE='123456789 -0100'", lines[2]);
  822. PersonIdent parsedIdent = git.rebase().parseAuthor(
  823. convertedAuthor.getBytes("UTF-8"));
  824. assertEquals(ident.getName(), parsedIdent.getName());
  825. assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
  826. // this is rounded to the last second
  827. assertEquals(123456789000L, parsedIdent.getWhen().getTime());
  828. assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
  829. // + 9.5h timezone offset
  830. ident = new PersonIdent("Author name", "a.mail@some.com",
  831. 123456789123L, +570);
  832. convertedAuthor = git.rebase().toAuthorScript(ident);
  833. lines = convertedAuthor.split("\n");
  834. assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
  835. assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]);
  836. assertEquals("GIT_AUTHOR_DATE='123456789 +0930'", lines[2]);
  837. parsedIdent = git.rebase().parseAuthor(
  838. convertedAuthor.getBytes("UTF-8"));
  839. assertEquals(ident.getName(), parsedIdent.getName());
  840. assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
  841. assertEquals(123456789000L, parsedIdent.getWhen().getTime());
  842. assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
  843. }
  844. @Test
  845. public void testRepositoryStateChecks() throws Exception {
  846. try {
  847. git.rebase().setOperation(Operation.ABORT).call();
  848. fail("Expected Exception not thrown");
  849. } catch (WrongRepositoryStateException e) {
  850. // expected
  851. }
  852. try {
  853. git.rebase().setOperation(Operation.SKIP).call();
  854. fail("Expected Exception not thrown");
  855. } catch (WrongRepositoryStateException e) {
  856. // expected
  857. }
  858. try {
  859. git.rebase().setOperation(Operation.CONTINUE).call();
  860. fail("Expected Exception not thrown");
  861. } catch (WrongRepositoryStateException e) {
  862. // expected
  863. }
  864. }
  865. @Test
  866. public void testRebaseWithUntrackedFile() throws Exception {
  867. // create file1, add and commit
  868. writeTrashFile(FILE1, "file1");
  869. git.add().addFilepattern(FILE1).call();
  870. RevCommit commit = git.commit().setMessage("commit1").call();
  871. // create topic branch and checkout / create file2, add and commit
  872. createBranch(commit, "refs/heads/topic");
  873. checkoutBranch("refs/heads/topic");
  874. writeTrashFile("file2", "file2");
  875. git.add().addFilepattern("file2").call();
  876. git.commit().setMessage("commit2").call();
  877. // checkout master branch / modify file1, add and commit
  878. checkoutBranch("refs/heads/master");
  879. writeTrashFile(FILE1, "modified file1");
  880. git.add().addFilepattern(FILE1).call();
  881. git.commit().setMessage("commit3").call();
  882. // checkout topic branch / create untracked file3
  883. checkoutBranch("refs/heads/topic");
  884. writeTrashFile("file3", "untracked file3");
  885. // rebase
  886. assertEquals(Status.OK, git.rebase().setUpstream("refs/heads/master")
  887. .call().getStatus());
  888. }
  889. @Test
  890. @SuppressWarnings("null")
  891. public void testRebaseWithUnstagedTopicChange() throws Exception {
  892. // create file1, add and commit
  893. writeTrashFile(FILE1, "file1");
  894. git.add().addFilepattern(FILE1).call();
  895. RevCommit commit = git.commit().setMessage("commit1").call();
  896. // create topic branch and checkout / create file2, add and commit
  897. createBranch(commit, "refs/heads/topic");
  898. checkoutBranch("refs/heads/topic");
  899. writeTrashFile("file2", "file2");
  900. git.add().addFilepattern("file2").call();
  901. git.commit().setMessage("commit2").call();
  902. // checkout master branch / modify file1, add and commit
  903. checkoutBranch("refs/heads/master");
  904. writeTrashFile(FILE1, "modified file1");
  905. git.add().addFilepattern(FILE1).call();
  906. git.commit().setMessage("commit3").call();
  907. // checkout topic branch / modify file2
  908. checkoutBranch("refs/heads/topic");
  909. writeTrashFile("file2", "unstaged file2");
  910. // rebase
  911. JGitInternalException exception = null;
  912. try {
  913. git.rebase().setUpstream("refs/heads/master").call();
  914. } catch (JGitInternalException e) {
  915. exception = e;
  916. }
  917. assertNotNull(exception);
  918. assertEquals("Checkout conflict with files: \nfile2",
  919. exception.getMessage());
  920. }
  921. @Test
  922. @SuppressWarnings("null")
  923. public void testRebaseWithUncommittedTopicChange() throws Exception {
  924. // create file1, add and commit
  925. writeTrashFile(FILE1, "file1");
  926. git.add().addFilepattern(FILE1).call();
  927. RevCommit commit = git.commit().setMessage("commit1").call();
  928. // create topic branch and checkout / create file2, add and commit
  929. createBranch(commit, "refs/heads/topic");
  930. checkoutBranch("refs/heads/topic");
  931. writeTrashFile("file2", "file2");
  932. git.add().addFilepattern("file2").call();
  933. git.commit().setMessage("commit2").call();
  934. // checkout master branch / modify file1, add and commit
  935. checkoutBranch("refs/heads/master");
  936. writeTrashFile(FILE1, "modified file1");
  937. git.add().addFilepattern(FILE1).call();
  938. git.commit().setMessage("commit3").call();
  939. // checkout topic branch / modify file2 and add
  940. checkoutBranch("refs/heads/topic");
  941. File uncommittedFile = writeTrashFile("file2", "uncommitted file2");
  942. git.add().addFilepattern("file2").call();
  943. // do not commit
  944. // rebase
  945. JGitInternalException exception = null;
  946. try {
  947. git.rebase().setUpstream("refs/heads/master").call();
  948. } catch (JGitInternalException e) {
  949. exception = e;
  950. }
  951. assertNotNull(exception);
  952. assertEquals("Checkout conflict with files: \nfile2",
  953. exception.getMessage());
  954. checkFile(uncommittedFile, "uncommitted file2");
  955. assertEquals(RepositoryState.SAFE, git.getRepository().getRepositoryState());
  956. }
  957. @Test
  958. @SuppressWarnings("null")
  959. public void testRebaseWithUnstagedMasterChange() throws Exception {
  960. // create file1, add and commit
  961. writeTrashFile(FILE1, "file1");
  962. git.add().addFilepattern(FILE1).call();
  963. RevCommit commit = git.commit().setMessage("commit1").call();
  964. // create topic branch and checkout / create file2, add and commit
  965. createBranch(commit, "refs/heads/topic");
  966. checkoutBranch("refs/heads/topic");
  967. writeTrashFile("file2", "file2");
  968. git.add().addFilepattern("file2").call();
  969. git.commit().setMessage("commit2").call();
  970. // checkout master branch / modify file1, add and commit
  971. checkoutBranch("refs/heads/master");
  972. writeTrashFile(FILE1, "modified file1");
  973. git.add().addFilepattern(FILE1).call();
  974. git.commit().setMessage("commit3").call();
  975. // checkout topic branch / modify file1
  976. checkoutBranch("refs/heads/topic");
  977. writeTrashFile(FILE1, "unstaged modified file1");
  978. // rebase
  979. JGitInternalException exception = null;
  980. try {
  981. git.rebase().setUpstream("refs/heads/master").call();
  982. } catch (JGitInternalException e) {
  983. exception = e;
  984. }
  985. assertNotNull(exception);
  986. assertEquals("Checkout conflict with files: \nfile1",
  987. exception.getMessage());
  988. }
  989. @Test
  990. @SuppressWarnings("null")
  991. public void testRebaseWithUncommittedMasterChange() throws Exception {
  992. // create file1, add and commit
  993. writeTrashFile(FILE1, "file1");
  994. git.add().addFilepattern(FILE1).call();
  995. RevCommit commit = git.commit().setMessage("commit1").call();
  996. // create topic branch and checkout / create file2, add and commit
  997. createBranch(commit, "refs/heads/topic");
  998. checkoutBranch("refs/heads/topic");
  999. writeTrashFile("file2", "file2");
  1000. git.add().addFilepattern("file2").call();
  1001. git.commit().setMessage("commit2").call();
  1002. // checkout master branch / modify file1, add and commit
  1003. checkoutBranch("refs/heads/master");
  1004. writeTrashFile(FILE1, "modified file1");
  1005. git.add().addFilepattern(FILE1).call();
  1006. git.commit().setMessage("commit3").call();
  1007. // checkout topic branch / modify file1 and add
  1008. checkoutBranch("refs/heads/topic");
  1009. writeTrashFile(FILE1, "uncommitted modified file1");
  1010. git.add().addFilepattern(FILE1).call();
  1011. // do not commit
  1012. // rebase
  1013. JGitInternalException exception = null;
  1014. try {
  1015. git.rebase().setUpstream("refs/heads/master").call();
  1016. } catch (JGitInternalException e) {
  1017. exception = e;
  1018. }
  1019. assertNotNull(exception);
  1020. assertEquals("Checkout conflict with files: \nfile1",
  1021. exception.getMessage());
  1022. }
  1023. @Test
  1024. public void testRebaseWithUnstagedMasterChangeBaseCommit() throws Exception {
  1025. // create file0 + file1, add and commit
  1026. writeTrashFile("file0", "file0");
  1027. writeTrashFile(FILE1, "file1");
  1028. git.add().addFilepattern("file0").addFilepattern(FILE1).call();
  1029. RevCommit commit = git.commit().setMessage("commit1").call();
  1030. // create topic branch and checkout / create file2, add and commit
  1031. createBranch(commit, "refs/heads/topic");
  1032. checkoutBranch("refs/heads/topic");
  1033. writeTrashFile("file2", "file2");
  1034. git.add().addFilepattern("file2").call();
  1035. git.commit().setMessage("commit2").call();
  1036. // checkout master branch / modify file1, add and commit
  1037. checkoutBranch("refs/heads/master");
  1038. writeTrashFile(FILE1, "modified file1");
  1039. git.add().addFilepattern(FILE1).call();
  1040. git.commit().setMessage("commit3").call();
  1041. // checkout topic branch / modify file0
  1042. checkoutBranch("refs/heads/topic");
  1043. writeTrashFile("file0", "unstaged modified file0");
  1044. // rebase
  1045. assertEquals(Status.OK, git.rebase().setUpstream("refs/heads/master")
  1046. .call().getStatus());
  1047. }
  1048. @Test
  1049. public void testRebaseWithUncommittedMasterChangeBaseCommit()
  1050. throws Exception {
  1051. // create file0 + file1, add and commit
  1052. File file0 = writeTrashFile("file0", "file0");
  1053. writeTrashFile(FILE1, "file1");
  1054. git.add().addFilepattern("file0").addFilepattern(FILE1).call();
  1055. RevCommit commit = git.commit().setMessage("commit1").call();
  1056. // create topic branch and checkout / create file2, add and commit
  1057. createBranch(commit, "refs/heads/topic");
  1058. checkoutBranch("refs/heads/topic");
  1059. writeTrashFile("file2", "file2");
  1060. git.add().addFilepattern("file2").call();
  1061. git.commit().setMessage("commit2").call();
  1062. // checkout master branch / modify file1, add and commit
  1063. checkoutBranch("refs/heads/master");
  1064. writeTrashFile(FILE1, "modified file1");
  1065. git.add().addFilepattern(FILE1).call();
  1066. git.commit().setMessage("commit3").call();
  1067. // checkout topic branch / modify file0 and add
  1068. checkoutBranch("refs/heads/topic");
  1069. write(file0, "unstaged modified file0");
  1070. git.add().addFilepattern("file0").call();
  1071. // do not commit
  1072. // get current index state
  1073. String indexState = indexState(CONTENT);
  1074. // rebase
  1075. RebaseResult result = git.rebase().setUpstream("refs/heads/master")
  1076. .call();
  1077. assertEquals(Status.FAILED, result.getStatus());
  1078. // staged file0 causes DIRTY_INDEX
  1079. assertEquals(1, result.getFailingPaths().size());
  1080. assertEquals(MergeFailureReason.DIRTY_INDEX, result.getFailingPaths()
  1081. .get("file0"));
  1082. assertEquals("unstaged modified file0", read(file0));
  1083. // index shall be unchanged
  1084. assertEquals(indexState, indexState(CONTENT));
  1085. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  1086. }
  1087. @Test
  1088. public void testRebaseWithUnstagedMasterChangeOtherCommit()
  1089. throws Exception {
  1090. // create file0, add and commit
  1091. writeTrashFile("file0", "file0");
  1092. git.add().addFilepattern("file0").call();
  1093. git.commit().setMessage("commit0").call();
  1094. // create file1, add and commit
  1095. writeTrashFile(FILE1, "file1");
  1096. git.add().addFilepattern(FILE1).call();
  1097. RevCommit commit = git.commit().setMessage("commit1").call();
  1098. // create topic branch and checkout / create file2, add and commit
  1099. createBranch(commit, "refs/heads/topic");
  1100. checkoutBranch("refs/heads/topic");
  1101. writeTrashFile("file2", "file2");
  1102. git.add().addFilepattern("file2").call();
  1103. git.commit().setMessage("commit2").call();
  1104. // checkout master branch / modify file1, add and commit
  1105. checkoutBranch("refs/heads/master");
  1106. writeTrashFile(FILE1, "modified file1");
  1107. git.add().addFilepattern(FILE1).call();
  1108. git.commit().setMessage("commit3").call();
  1109. // checkout topic branch / modify file0
  1110. checkoutBranch("refs/heads/topic");
  1111. writeTrashFile("file0", "unstaged modified file0");
  1112. // rebase
  1113. assertEquals(Status.OK, git.rebase().setUpstream("refs/heads/master")
  1114. .call().getStatus());
  1115. }
  1116. @Test
  1117. public void testRebaseWithUncommittedMasterChangeOtherCommit()
  1118. throws Exception {
  1119. // create file0, add and commit
  1120. File file0 = writeTrashFile("file0", "file0");
  1121. git.add().addFilepattern("file0").call();
  1122. git.commit().setMessage("commit0").call();
  1123. // create file1, add and commit
  1124. writeTrashFile(FILE1, "file1");
  1125. git.add().addFilepattern(FILE1).call();
  1126. RevCommit commit = git.commit().setMessage("commit1").call();
  1127. // create topic branch and checkout / create file2, add and commit
  1128. createBranch(commit, "refs/heads/topic");
  1129. checkoutBranch("refs/heads/topic");
  1130. writeTrashFile("file2", "file2");
  1131. git.add().addFilepattern("file2").call();
  1132. git.commit().setMessage("commit2").call();
  1133. // checkout master branch / modify file1, add and commit
  1134. checkoutBranch("refs/heads/master");
  1135. writeTrashFile(FILE1, "modified file1");
  1136. git.add().addFilepattern(FILE1).call();
  1137. git.commit().setMessage("commit3").call();
  1138. // checkout topic branch / modify file0 and add
  1139. checkoutBranch("refs/heads/topic");
  1140. write(file0, "unstaged modified file0");
  1141. git.add().addFilepattern("file0").call();
  1142. // do not commit
  1143. // get current index state
  1144. String indexState = indexState(CONTENT);
  1145. // rebase
  1146. RebaseResult result = git.rebase().setUpstream("refs/heads/master")
  1147. .call();
  1148. assertEquals(Status.FAILED, result.getStatus());
  1149. // staged file0 causes DIRTY_INDEX
  1150. assertEquals(1, result.getFailingPaths().size());
  1151. assertEquals(MergeFailureReason.DIRTY_INDEX, result.getFailingPaths()
  1152. .get("file0"));
  1153. assertEquals("unstaged modified file0", read(file0));
  1154. // index shall be unchanged
  1155. assertEquals(indexState, indexState(CONTENT));
  1156. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  1157. }
  1158. private int countPicks() throws IOException {
  1159. int count = 0;
  1160. File todoFile = new File(db.getDirectory(),
  1161. "rebase-merge/git-rebase-todo");
  1162. BufferedReader br = new BufferedReader(new InputStreamReader(
  1163. new FileInputStream(todoFile), "UTF-8"));
  1164. try {
  1165. String line = br.readLine();
  1166. while (line != null) {
  1167. String actionToken = line.substring(0, line.indexOf(' '));
  1168. Action action = null;
  1169. try {
  1170. action = Action.parse(actionToken);
  1171. } catch (Exception e) {
  1172. // ignore
  1173. }
  1174. if (action != null)
  1175. count++;
  1176. line = br.readLine();
  1177. }
  1178. return count;
  1179. } finally {
  1180. br.close();
  1181. }
  1182. }
  1183. @Test
  1184. public void testFastForwardWithMultipleCommitsOnDifferentBranches()
  1185. throws Exception {
  1186. // create file1 on master
  1187. writeTrashFile(FILE1, FILE1);
  1188. git.add().addFilepattern(FILE1).call();
  1189. RevCommit first = git.commit().setMessage("Add file1").call();
  1190. assertTrue(new File(db.getWorkTree(), FILE1).exists());
  1191. // create a topic branch
  1192. createBranch(first, "refs/heads/topic");
  1193. // create file2 on master
  1194. writeTrashFile("file2", "file2");
  1195. git.add().addFilepattern("file2").call();
  1196. RevCommit second = git.commit().setMessage("Add file2").call();
  1197. assertTrue(new File(db.getWorkTree(), "file2").exists());
  1198. // create side branch
  1199. createBranch(second, "refs/heads/side");
  1200. // update FILE1 on master
  1201. writeTrashFile(FILE1, "blah");
  1202. git.add().addFilepattern(FILE1).call();
  1203. git.commit().setMessage("updated file1 on master")
  1204. .call();
  1205. // switch to side branch and update file2
  1206. checkoutBranch("refs/heads/side");
  1207. writeTrashFile("file2", "more change");
  1208. git.add().addFilepattern("file2").call();
  1209. RevCommit fourth = git.commit().setMessage("update file2 on side")
  1210. .call();
  1211. // switch back to master and merge in side
  1212. checkoutBranch("refs/heads/master");
  1213. MergeResult result = git.merge().include(fourth.getId())
  1214. .setStrategy(MergeStrategy.RESOLVE).call();
  1215. assertEquals(MergeStatus.MERGED, result.getMergeStatus());
  1216. // switch back to topic branch and rebase it onto master
  1217. checkoutBranch("refs/heads/topic");
  1218. RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
  1219. assertTrue(new File(db.getWorkTree(), "file2").exists());
  1220. checkFile(new File(db.getWorkTree(), "file2"), "more change");
  1221. assertEquals(Status.FAST_FORWARD, res.getStatus());
  1222. }
  1223. @Test
  1224. public void testRebaseShouldLeaveWorkspaceUntouchedWithUnstagedChangesConflict()
  1225. throws Exception {
  1226. writeTrashFile(FILE1, "initial file");
  1227. git.add().addFilepattern(FILE1).call();
  1228. RevCommit initial = git.commit().setMessage("initial commit").call();
  1229. createBranch(initial, "refs/heads/side");
  1230. writeTrashFile(FILE1, "updated file");
  1231. git.add().addFilepattern(FILE1).call();
  1232. git.commit().setMessage("updated FILE1 on master").call();
  1233. // switch to side, modify the file
  1234. checkoutBranch("refs/heads/side");
  1235. writeTrashFile(FILE1, "side update");
  1236. git.add().addFilepattern(FILE1).call();
  1237. git.commit().setMessage("updated FILE1 on side").call();
  1238. File theFile = writeTrashFile(FILE1, "dirty the file");
  1239. // and attempt to rebase
  1240. try {
  1241. RebaseResult rebaseResult = git.rebase()
  1242. .setUpstream("refs/heads/master").call();
  1243. fail("Checkout with conflict should have occured, not "
  1244. + rebaseResult.getStatus());
  1245. } catch (JGitInternalException e) {
  1246. checkFile(theFile, "dirty the file");
  1247. }
  1248. assertEquals(RepositoryState.SAFE, git.getRepository()
  1249. .getRepositoryState());
  1250. }
  1251. }