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

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