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.

CommitCommandTest.java 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. /*
  2. * Copyright (C) 2011-2012, GitHub Inc. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.api;
  11. import static org.junit.Assert.assertEquals;
  12. import static org.junit.Assert.assertNotEquals;
  13. import static org.junit.Assert.assertNotNull;
  14. import static org.junit.Assert.assertNull;
  15. import static org.junit.Assert.assertSame;
  16. import static org.junit.Assert.assertTrue;
  17. import static org.junit.Assert.fail;
  18. import static org.junit.Assume.assumeTrue;
  19. import java.io.File;
  20. import java.util.Date;
  21. import java.util.List;
  22. import java.util.TimeZone;
  23. import java.util.concurrent.atomic.AtomicInteger;
  24. import org.eclipse.jgit.api.CherryPickResult.CherryPickStatus;
  25. import org.eclipse.jgit.api.errors.CanceledException;
  26. import org.eclipse.jgit.api.errors.EmptyCommitException;
  27. import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
  28. import org.eclipse.jgit.diff.DiffEntry;
  29. import org.eclipse.jgit.dircache.DirCache;
  30. import org.eclipse.jgit.dircache.DirCacheBuilder;
  31. import org.eclipse.jgit.dircache.DirCacheEntry;
  32. import org.eclipse.jgit.junit.RepositoryTestCase;
  33. import org.eclipse.jgit.junit.time.TimeUtil;
  34. import org.eclipse.jgit.lib.CommitBuilder;
  35. import org.eclipse.jgit.lib.ConfigConstants;
  36. import org.eclipse.jgit.lib.Constants;
  37. import org.eclipse.jgit.lib.FileMode;
  38. import org.eclipse.jgit.lib.GpgSigner;
  39. import org.eclipse.jgit.lib.ObjectId;
  40. import org.eclipse.jgit.lib.PersonIdent;
  41. import org.eclipse.jgit.lib.RefUpdate;
  42. import org.eclipse.jgit.lib.RefUpdate.Result;
  43. import org.eclipse.jgit.lib.ReflogEntry;
  44. import org.eclipse.jgit.lib.Repository;
  45. import org.eclipse.jgit.lib.StoredConfig;
  46. import org.eclipse.jgit.revwalk.RevCommit;
  47. import org.eclipse.jgit.storage.file.FileBasedConfig;
  48. import org.eclipse.jgit.submodule.SubmoduleWalk;
  49. import org.eclipse.jgit.transport.CredentialsProvider;
  50. import org.eclipse.jgit.treewalk.TreeWalk;
  51. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  52. import org.eclipse.jgit.util.FS;
  53. import org.junit.Ignore;
  54. import org.junit.Test;
  55. /**
  56. * Unit tests of {@link CommitCommand}.
  57. */
  58. public class CommitCommandTest extends RepositoryTestCase {
  59. @Test
  60. public void testExecutableRetention() throws Exception {
  61. StoredConfig config = db.getConfig();
  62. config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  63. ConfigConstants.CONFIG_KEY_FILEMODE, true);
  64. config.save();
  65. FS executableFs = new FS() {
  66. @Override
  67. public boolean supportsExecute() {
  68. return true;
  69. }
  70. @Override
  71. public boolean setExecute(File f, boolean canExec) {
  72. return true;
  73. }
  74. @Override
  75. public ProcessBuilder runInShell(String cmd, String[] args) {
  76. return null;
  77. }
  78. @Override
  79. public boolean retryFailedLockFileCommit() {
  80. return false;
  81. }
  82. @Override
  83. public FS newInstance() {
  84. return this;
  85. }
  86. @Override
  87. protected File discoverGitExe() {
  88. return null;
  89. }
  90. @Override
  91. public boolean canExecute(File f) {
  92. return true;
  93. }
  94. @Override
  95. public boolean isCaseSensitive() {
  96. return true;
  97. }
  98. };
  99. Git git = Git.open(db.getDirectory(), executableFs);
  100. String path = "a.txt";
  101. writeTrashFile(path, "content");
  102. git.add().addFilepattern(path).call();
  103. RevCommit commit1 = git.commit().setMessage("commit").call();
  104. try (TreeWalk walk = TreeWalk.forPath(db, path, commit1.getTree())) {
  105. assertNotNull(walk);
  106. assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
  107. }
  108. FS nonExecutableFs = new FS() {
  109. @Override
  110. public boolean supportsExecute() {
  111. return false;
  112. }
  113. @Override
  114. public boolean setExecute(File f, boolean canExec) {
  115. return false;
  116. }
  117. @Override
  118. public ProcessBuilder runInShell(String cmd, String[] args) {
  119. return null;
  120. }
  121. @Override
  122. public boolean retryFailedLockFileCommit() {
  123. return false;
  124. }
  125. @Override
  126. public FS newInstance() {
  127. return this;
  128. }
  129. @Override
  130. protected File discoverGitExe() {
  131. return null;
  132. }
  133. @Override
  134. public boolean canExecute(File f) {
  135. return false;
  136. }
  137. @Override
  138. public boolean isCaseSensitive() {
  139. return true;
  140. }
  141. };
  142. config = db.getConfig();
  143. config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  144. ConfigConstants.CONFIG_KEY_FILEMODE, false);
  145. config.save();
  146. Git git2 = Git.open(db.getDirectory(), nonExecutableFs);
  147. writeTrashFile(path, "content2");
  148. RevCommit commit2 = git2.commit().setOnly(path).setMessage("commit2")
  149. .call();
  150. try (TreeWalk walk = TreeWalk.forPath(db, path, commit2.getTree())) {
  151. assertNotNull(walk);
  152. assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
  153. }
  154. }
  155. @Test
  156. public void commitNewSubmodule() throws Exception {
  157. try (Git git = new Git(db)) {
  158. writeTrashFile("file.txt", "content");
  159. git.add().addFilepattern("file.txt").call();
  160. RevCommit commit = git.commit().setMessage("create file").call();
  161. SubmoduleAddCommand command = new SubmoduleAddCommand(db);
  162. String path = "sub";
  163. command.setPath(path);
  164. String uri = db.getDirectory().toURI().toString();
  165. command.setURI(uri);
  166. Repository repo = command.call();
  167. assertNotNull(repo);
  168. addRepoToClose(repo);
  169. try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
  170. assertTrue(generator.next());
  171. assertEquals(path, generator.getPath());
  172. assertEquals(commit, generator.getObjectId());
  173. assertEquals(uri, generator.getModulesUrl());
  174. assertEquals(path, generator.getModulesPath());
  175. assertEquals(uri, generator.getConfigUrl());
  176. try (Repository subModRepo = generator.getRepository()) {
  177. assertNotNull(subModRepo);
  178. }
  179. }
  180. assertEquals(commit, repo.resolve(Constants.HEAD));
  181. RevCommit submoduleCommit = git.commit().setMessage("submodule add")
  182. .setOnly(path).call();
  183. assertNotNull(submoduleCommit);
  184. try (TreeWalk walk = new TreeWalk(db)) {
  185. walk.addTree(commit.getTree());
  186. walk.addTree(submoduleCommit.getTree());
  187. walk.setFilter(TreeFilter.ANY_DIFF);
  188. List<DiffEntry> diffs = DiffEntry.scan(walk);
  189. assertEquals(1, diffs.size());
  190. DiffEntry subDiff = diffs.get(0);
  191. assertEquals(FileMode.MISSING, subDiff.getOldMode());
  192. assertEquals(FileMode.GITLINK, subDiff.getNewMode());
  193. assertEquals(ObjectId.zeroId(), subDiff.getOldId().toObjectId());
  194. assertEquals(commit, subDiff.getNewId().toObjectId());
  195. assertEquals(path, subDiff.getNewPath());
  196. }
  197. }
  198. }
  199. @Test
  200. public void commitSubmoduleUpdate() throws Exception {
  201. try (Git git = new Git(db)) {
  202. writeTrashFile("file.txt", "content");
  203. git.add().addFilepattern("file.txt").call();
  204. RevCommit commit = git.commit().setMessage("create file").call();
  205. writeTrashFile("file.txt", "content2");
  206. git.add().addFilepattern("file.txt").call();
  207. RevCommit commit2 = git.commit().setMessage("edit file").call();
  208. SubmoduleAddCommand command = new SubmoduleAddCommand(db);
  209. String path = "sub";
  210. command.setPath(path);
  211. String uri = db.getDirectory().toURI().toString();
  212. command.setURI(uri);
  213. Repository repo = command.call();
  214. assertNotNull(repo);
  215. addRepoToClose(repo);
  216. try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
  217. assertTrue(generator.next());
  218. assertEquals(path, generator.getPath());
  219. assertEquals(commit2, generator.getObjectId());
  220. assertEquals(uri, generator.getModulesUrl());
  221. assertEquals(path, generator.getModulesPath());
  222. assertEquals(uri, generator.getConfigUrl());
  223. try (Repository subModRepo = generator.getRepository()) {
  224. assertNotNull(subModRepo);
  225. }
  226. }
  227. assertEquals(commit2, repo.resolve(Constants.HEAD));
  228. RevCommit submoduleAddCommit = git.commit().setMessage("submodule add")
  229. .setOnly(path).call();
  230. assertNotNull(submoduleAddCommit);
  231. RefUpdate update = repo.updateRef(Constants.HEAD);
  232. update.setNewObjectId(commit);
  233. assertEquals(Result.FORCED, update.forceUpdate());
  234. RevCommit submoduleEditCommit = git.commit()
  235. .setMessage("submodule add").setOnly(path).call();
  236. assertNotNull(submoduleEditCommit);
  237. try (TreeWalk walk = new TreeWalk(db)) {
  238. walk.addTree(submoduleAddCommit.getTree());
  239. walk.addTree(submoduleEditCommit.getTree());
  240. walk.setFilter(TreeFilter.ANY_DIFF);
  241. List<DiffEntry> diffs = DiffEntry.scan(walk);
  242. assertEquals(1, diffs.size());
  243. DiffEntry subDiff = diffs.get(0);
  244. assertEquals(FileMode.GITLINK, subDiff.getOldMode());
  245. assertEquals(FileMode.GITLINK, subDiff.getNewMode());
  246. assertEquals(commit2, subDiff.getOldId().toObjectId());
  247. assertEquals(commit, subDiff.getNewId().toObjectId());
  248. assertEquals(path, subDiff.getNewPath());
  249. assertEquals(path, subDiff.getOldPath());
  250. }
  251. }
  252. }
  253. @Ignore("very flaky when run with Hudson")
  254. @Test
  255. public void commitUpdatesSmudgedEntries() throws Exception {
  256. try (Git git = new Git(db)) {
  257. File file1 = writeTrashFile("file1.txt", "content1");
  258. TimeUtil.setLastModifiedWithOffset(file1.toPath(), -5000L);
  259. File file2 = writeTrashFile("file2.txt", "content2");
  260. TimeUtil.setLastModifiedWithOffset(file2.toPath(), -5000L);
  261. File file3 = writeTrashFile("file3.txt", "content3");
  262. TimeUtil.setLastModifiedWithOffset(file3.toPath(), -5000L);
  263. assertNotNull(git.add().addFilepattern("file1.txt")
  264. .addFilepattern("file2.txt").addFilepattern("file3.txt").call());
  265. RevCommit commit = git.commit().setMessage("add files").call();
  266. assertNotNull(commit);
  267. DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
  268. int file1Size = cache.getEntry("file1.txt").getLength();
  269. int file2Size = cache.getEntry("file2.txt").getLength();
  270. int file3Size = cache.getEntry("file3.txt").getLength();
  271. ObjectId file2Id = cache.getEntry("file2.txt").getObjectId();
  272. ObjectId file3Id = cache.getEntry("file3.txt").getObjectId();
  273. assertTrue(file1Size > 0);
  274. assertTrue(file2Size > 0);
  275. assertTrue(file3Size > 0);
  276. // Smudge entries
  277. cache = DirCache.lock(db.getIndexFile(), db.getFS());
  278. cache.getEntry("file1.txt").setLength(0);
  279. cache.getEntry("file2.txt").setLength(0);
  280. cache.getEntry("file3.txt").setLength(0);
  281. cache.write();
  282. assertTrue(cache.commit());
  283. // Verify entries smudged
  284. cache = DirCache.read(db.getIndexFile(), db.getFS());
  285. assertEquals(0, cache.getEntry("file1.txt").getLength());
  286. assertEquals(0, cache.getEntry("file2.txt").getLength());
  287. assertEquals(0, cache.getEntry("file3.txt").getLength());
  288. TimeUtil.setLastModifiedWithOffset(db.getIndexFile().toPath(),
  289. -5000L);
  290. write(file1, "content4");
  291. TimeUtil.setLastModifiedWithOffset(file1.toPath(), 2500L);
  292. assertNotNull(git.commit().setMessage("edit file").setOnly("file1.txt")
  293. .call());
  294. cache = db.readDirCache();
  295. assertEquals(file1Size, cache.getEntry("file1.txt").getLength());
  296. assertEquals(file2Size, cache.getEntry("file2.txt").getLength());
  297. assertEquals(file3Size, cache.getEntry("file3.txt").getLength());
  298. assertEquals(file2Id, cache.getEntry("file2.txt").getObjectId());
  299. assertEquals(file3Id, cache.getEntry("file3.txt").getObjectId());
  300. }
  301. }
  302. @Ignore("very flaky when run with Hudson")
  303. @Test
  304. public void commitIgnoresSmudgedEntryWithDifferentId() throws Exception {
  305. try (Git git = new Git(db)) {
  306. File file1 = writeTrashFile("file1.txt", "content1");
  307. TimeUtil.setLastModifiedWithOffset(file1.toPath(), -5000L);
  308. File file2 = writeTrashFile("file2.txt", "content2");
  309. TimeUtil.setLastModifiedWithOffset(file2.toPath(), -5000L);
  310. assertNotNull(git.add().addFilepattern("file1.txt")
  311. .addFilepattern("file2.txt").call());
  312. RevCommit commit = git.commit().setMessage("add files").call();
  313. assertNotNull(commit);
  314. DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
  315. int file1Size = cache.getEntry("file1.txt").getLength();
  316. int file2Size = cache.getEntry("file2.txt").getLength();
  317. assertTrue(file1Size > 0);
  318. assertTrue(file2Size > 0);
  319. writeTrashFile("file2.txt", "content3");
  320. assertNotNull(git.add().addFilepattern("file2.txt").call());
  321. writeTrashFile("file2.txt", "content4");
  322. // Smudge entries
  323. cache = DirCache.lock(db.getIndexFile(), db.getFS());
  324. cache.getEntry("file1.txt").setLength(0);
  325. cache.getEntry("file2.txt").setLength(0);
  326. cache.write();
  327. assertTrue(cache.commit());
  328. // Verify entries smudged
  329. cache = db.readDirCache();
  330. assertEquals(0, cache.getEntry("file1.txt").getLength());
  331. assertEquals(0, cache.getEntry("file2.txt").getLength());
  332. TimeUtil.setLastModifiedWithOffset(db.getIndexFile().toPath(),
  333. -5000L);
  334. write(file1, "content5");
  335. TimeUtil.setLastModifiedWithOffset(file1.toPath(), 1000L);
  336. assertNotNull(git.commit().setMessage("edit file").setOnly("file1.txt")
  337. .call());
  338. cache = db.readDirCache();
  339. assertEquals(file1Size, cache.getEntry("file1.txt").getLength());
  340. assertEquals(0, cache.getEntry("file2.txt").getLength());
  341. }
  342. }
  343. @Test
  344. public void commitAfterSquashMerge() throws Exception {
  345. try (Git git = new Git(db)) {
  346. writeTrashFile("file1", "file1");
  347. git.add().addFilepattern("file1").call();
  348. RevCommit first = git.commit().setMessage("initial commit").call();
  349. assertTrue(new File(db.getWorkTree(), "file1").exists());
  350. createBranch(first, "refs/heads/branch1");
  351. checkoutBranch("refs/heads/branch1");
  352. writeTrashFile("file2", "file2");
  353. git.add().addFilepattern("file2").call();
  354. git.commit().setMessage("second commit").call();
  355. assertTrue(new File(db.getWorkTree(), "file2").exists());
  356. checkoutBranch("refs/heads/master");
  357. MergeResult result = git.merge()
  358. .include(db.exactRef("refs/heads/branch1"))
  359. .setSquash(true)
  360. .call();
  361. assertTrue(new File(db.getWorkTree(), "file1").exists());
  362. assertTrue(new File(db.getWorkTree(), "file2").exists());
  363. assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
  364. result.getMergeStatus());
  365. // comment not set, should be inferred from SQUASH_MSG
  366. RevCommit squashedCommit = git.commit().call();
  367. assertEquals(1, squashedCommit.getParentCount());
  368. assertNull(db.readSquashCommitMsg());
  369. assertEquals("commit: Squashed commit of the following:", db
  370. .getReflogReader(Constants.HEAD).getLastEntry().getComment());
  371. assertEquals("commit: Squashed commit of the following:", db
  372. .getReflogReader(db.getBranch()).getLastEntry().getComment());
  373. }
  374. }
  375. @Test
  376. public void testReflogs() throws Exception {
  377. try (Git git = new Git(db)) {
  378. writeTrashFile("f", "1");
  379. git.add().addFilepattern("f").call();
  380. git.commit().setMessage("c1").call();
  381. writeTrashFile("f", "2");
  382. git.commit().setMessage("c2").setAll(true).setReflogComment(null)
  383. .call();
  384. writeTrashFile("f", "3");
  385. git.commit().setMessage("c3").setAll(true)
  386. .setReflogComment("testRl").call();
  387. db.getReflogReader(Constants.HEAD).getReverseEntries();
  388. assertEquals("testRl;commit (initial): c1;", reflogComments(
  389. db.getReflogReader(Constants.HEAD).getReverseEntries()));
  390. assertEquals("testRl;commit (initial): c1;", reflogComments(
  391. db.getReflogReader(db.getBranch()).getReverseEntries()));
  392. }
  393. }
  394. private static String reflogComments(List<ReflogEntry> entries) {
  395. StringBuilder b = new StringBuilder();
  396. for (ReflogEntry e : entries) {
  397. b.append(e.getComment()).append(";");
  398. }
  399. return b.toString();
  400. }
  401. @Test(expected = WrongRepositoryStateException.class)
  402. public void commitAmendOnInitialShouldFail() throws Exception {
  403. try (Git git = new Git(db)) {
  404. git.commit().setAmend(true).setMessage("initial commit").call();
  405. }
  406. }
  407. @Test
  408. public void commitAmendWithoutAuthorShouldSetOriginalAuthorAndAuthorTime()
  409. throws Exception {
  410. try (Git git = new Git(db)) {
  411. writeTrashFile("file1", "file1");
  412. git.add().addFilepattern("file1").call();
  413. final String authorName = "First Author";
  414. final String authorEmail = "author@example.org";
  415. final Date authorDate = new Date(1349621117000L);
  416. PersonIdent firstAuthor = new PersonIdent(authorName, authorEmail,
  417. authorDate, TimeZone.getTimeZone("UTC"));
  418. git.commit().setMessage("initial commit").setAuthor(firstAuthor).call();
  419. RevCommit amended = git.commit().setAmend(true)
  420. .setMessage("amend commit").call();
  421. PersonIdent amendedAuthor = amended.getAuthorIdent();
  422. assertEquals(authorName, amendedAuthor.getName());
  423. assertEquals(authorEmail, amendedAuthor.getEmailAddress());
  424. assertEquals(authorDate.getTime(), amendedAuthor.getWhen().getTime());
  425. }
  426. }
  427. @Test
  428. public void commitAmendWithAuthorShouldUseIt() throws Exception {
  429. try (Git git = new Git(db)) {
  430. writeTrashFile("file1", "file1");
  431. git.add().addFilepattern("file1").call();
  432. git.commit().setMessage("initial commit").call();
  433. RevCommit amended = git.commit().setAmend(true)
  434. .setAuthor("New Author", "newauthor@example.org")
  435. .setMessage("amend commit").call();
  436. PersonIdent amendedAuthor = amended.getAuthorIdent();
  437. assertEquals("New Author", amendedAuthor.getName());
  438. assertEquals("newauthor@example.org", amendedAuthor.getEmailAddress());
  439. }
  440. }
  441. @Test
  442. public void commitEmptyCommits() throws Exception {
  443. try (Git git = new Git(db)) {
  444. writeTrashFile("file1", "file1");
  445. git.add().addFilepattern("file1").call();
  446. RevCommit initial = git.commit().setMessage("initial commit")
  447. .call();
  448. RevCommit emptyFollowUp = git.commit()
  449. .setAuthor("New Author", "newauthor@example.org")
  450. .setMessage("no change").call();
  451. assertNotEquals(initial.getId(), emptyFollowUp.getId());
  452. assertEquals(initial.getTree().getId(),
  453. emptyFollowUp.getTree().getId());
  454. try {
  455. git.commit().setAuthor("New Author", "newauthor@example.org")
  456. .setMessage("again no change").setAllowEmpty(false)
  457. .call();
  458. fail("Didn't get the expected EmptyCommitException");
  459. } catch (EmptyCommitException e) {
  460. // expect this exception
  461. }
  462. // Allow empty commits also when setOnly was set
  463. git.commit().setAuthor("New Author", "newauthor@example.org")
  464. .setMessage("again no change").setOnly("file1")
  465. .setAllowEmpty(true).call();
  466. }
  467. }
  468. @Test
  469. public void commitOnlyShouldCommitUnmergedPathAndNotAffectOthers()
  470. throws Exception {
  471. DirCache index = db.lockDirCache();
  472. DirCacheBuilder builder = index.builder();
  473. addUnmergedEntry("unmerged1", builder);
  474. addUnmergedEntry("unmerged2", builder);
  475. DirCacheEntry other = new DirCacheEntry("other");
  476. other.setFileMode(FileMode.REGULAR_FILE);
  477. builder.add(other);
  478. builder.commit();
  479. writeTrashFile("unmerged1", "unmerged1 data");
  480. writeTrashFile("unmerged2", "unmerged2 data");
  481. writeTrashFile("other", "other data");
  482. assertEquals("[other, mode:100644]"
  483. + "[unmerged1, mode:100644, stage:1]"
  484. + "[unmerged1, mode:100644, stage:2]"
  485. + "[unmerged1, mode:100644, stage:3]"
  486. + "[unmerged2, mode:100644, stage:1]"
  487. + "[unmerged2, mode:100644, stage:2]"
  488. + "[unmerged2, mode:100644, stage:3]",
  489. indexState(0));
  490. try (Git git = new Git(db)) {
  491. RevCommit commit = git.commit().setOnly("unmerged1")
  492. .setMessage("Only one file").call();
  493. assertEquals("[other, mode:100644]" + "[unmerged1, mode:100644]"
  494. + "[unmerged2, mode:100644, stage:1]"
  495. + "[unmerged2, mode:100644, stage:2]"
  496. + "[unmerged2, mode:100644, stage:3]",
  497. indexState(0));
  498. try (TreeWalk walk = TreeWalk.forPath(db, "unmerged1", commit.getTree())) {
  499. assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
  500. }
  501. }
  502. }
  503. @Test
  504. public void commitOnlyShouldHandleIgnored() throws Exception {
  505. try (Git git = new Git(db)) {
  506. writeTrashFile("subdir/foo", "Hello World");
  507. writeTrashFile("subdir/bar", "Hello World");
  508. writeTrashFile(".gitignore", "bar");
  509. git.add().addFilepattern("subdir").call();
  510. git.commit().setOnly("subdir").setMessage("first commit").call();
  511. assertEquals("[subdir/foo, mode:100644, content:Hello World]",
  512. indexState(CONTENT));
  513. }
  514. }
  515. private void nonNormalizedIndexTest(boolean executable) throws Exception {
  516. String mode = executable ? "100755" : "100644";
  517. try (Git git = new Git(db)) {
  518. // Commit a file with CR/LF into the index
  519. FileBasedConfig config = db.getConfig();
  520. config.setString("core", null, "autocrlf", "false");
  521. config.save();
  522. File testFile = writeTrashFile("file.txt", "line 1\r\nline 2\r\n");
  523. if (executable) {
  524. FS.DETECTED.setExecute(testFile, true);
  525. }
  526. git.add().addFilepattern("file.txt").call();
  527. git.commit().setMessage("Initial").call();
  528. assertEquals(
  529. "[file.txt, mode:" + mode
  530. + ", content:line 1\r\nline 2\r\n]",
  531. indexState(CONTENT));
  532. config.setString("core", null, "autocrlf", "true");
  533. config.save();
  534. writeTrashFile("file.txt", "line 1\r\nline 1.5\r\nline 2\r\n");
  535. testFile = writeTrashFile("file2.txt", "new\r\nfile\r\n");
  536. if (executable) {
  537. FS.DETECTED.setExecute(testFile, true);
  538. }
  539. git.add().addFilepattern("file.txt").addFilepattern("file2.txt")
  540. .call();
  541. git.commit().setMessage("Second").call();
  542. assertEquals(
  543. "[file.txt, mode:" + mode
  544. + ", content:line 1\r\nline 1.5\r\nline 2\r\n]"
  545. + "[file2.txt, mode:" + mode
  546. + ", content:new\nfile\n]",
  547. indexState(CONTENT));
  548. writeTrashFile("file2.txt", "new\r\nfile\r\ncontent\r\n");
  549. git.add().addFilepattern("file2.txt").call();
  550. git.commit().setMessage("Third").call();
  551. assertEquals(
  552. "[file.txt, mode:" + mode
  553. + ", content:line 1\r\nline 1.5\r\nline 2\r\n]"
  554. + "[file2.txt, mode:" + mode
  555. + ", content:new\nfile\ncontent\n]",
  556. indexState(CONTENT));
  557. }
  558. }
  559. @Test
  560. public void commitWithAutoCrlfAndNonNormalizedIndex() throws Exception {
  561. nonNormalizedIndexTest(false);
  562. }
  563. @Test
  564. public void commitExecutableWithAutoCrlfAndNonNormalizedIndex()
  565. throws Exception {
  566. assumeTrue(FS.DETECTED.supportsExecute());
  567. nonNormalizedIndexTest(true);
  568. }
  569. @Test
  570. public void testDeletionConflictWithAutoCrlf() throws Exception {
  571. try (Git git = new Git(db)) {
  572. // Commit a file with CR/LF into the index
  573. FileBasedConfig config = db.getConfig();
  574. config.setString("core", null, "autocrlf", "false");
  575. config.save();
  576. File file = writeTrashFile("file.txt", "foo\r\n");
  577. git.add().addFilepattern("file.txt").call();
  578. git.commit().setMessage("Initial").call();
  579. // Switch to side branch
  580. git.checkout().setCreateBranch(true).setName("side").call();
  581. assertTrue(file.delete());
  582. git.rm().addFilepattern("file.txt").call();
  583. git.commit().setMessage("Side").call();
  584. // Switch on autocrlf=true
  585. config.setString("core", null, "autocrlf", "true");
  586. config.save();
  587. // Switch back to master and commit a conflict
  588. git.checkout().setName("master").call();
  589. writeTrashFile("file.txt", "foob\r\n");
  590. git.add().addFilepattern("file.txt").call();
  591. assertEquals("[file.txt, mode:100644, content:foob\r\n]",
  592. indexState(CONTENT));
  593. writeTrashFile("g", "file2.txt", "anything");
  594. git.add().addFilepattern("g/file2.txt");
  595. RevCommit master = git.commit().setMessage("Second").call();
  596. // Switch to side branch again so that the deletion is "ours"
  597. git.checkout().setName("side").call();
  598. // Cherry pick master: produces a delete-modify conflict.
  599. CherryPickResult pick = git.cherryPick().include(master).call();
  600. assertEquals("Expected a cherry-pick conflict",
  601. CherryPickStatus.CONFLICTING, pick.getStatus());
  602. // XXX: g/file2.txt should actually be staged already, but isn't.
  603. git.add().addFilepattern("g/file2.txt").call();
  604. // Resolve the conflict by taking the master version
  605. writeTrashFile("file.txt", "foob\r\n");
  606. git.add().addFilepattern("file.txt").call();
  607. git.commit().setMessage("Cherry").call();
  608. // We expect this to be committed with a single LF since there is no
  609. // "ours" stage.
  610. assertEquals(
  611. "[file.txt, mode:100644, content:foob\n]"
  612. + "[g/file2.txt, mode:100644, content:anything]",
  613. indexState(CONTENT));
  614. }
  615. }
  616. private void testConflictWithAutoCrlf(String baseLf, String lf)
  617. throws Exception {
  618. try (Git git = new Git(db)) {
  619. // Commit a file with CR/LF into the index
  620. FileBasedConfig config = db.getConfig();
  621. config.setString("core", null, "autocrlf", "false");
  622. config.save();
  623. writeTrashFile("file.txt", "foo" + baseLf);
  624. git.add().addFilepattern("file.txt").call();
  625. git.commit().setMessage("Initial").call();
  626. // Switch to side branch
  627. git.checkout().setCreateBranch(true).setName("side").call();
  628. writeTrashFile("file.txt", "bar\r\n");
  629. git.add().addFilepattern("file.txt").call();
  630. RevCommit side = git.commit().setMessage("Side").call();
  631. // Switch back to master and commit a conflict with the given lf
  632. git.checkout().setName("master");
  633. writeTrashFile("file.txt", "foob" + lf);
  634. git.add().addFilepattern("file.txt").call();
  635. git.commit().setMessage("Second").call();
  636. // Switch on autocrlf=true
  637. config.setString("core", null, "autocrlf", "true");
  638. config.save();
  639. // Cherry pick side: conflict. Resolve with CR-LF and commit.
  640. CherryPickResult pick = git.cherryPick().include(side).call();
  641. assertEquals("Expected a cherry-pick conflict",
  642. CherryPickStatus.CONFLICTING, pick.getStatus());
  643. writeTrashFile("file.txt", "foobar\r\n");
  644. git.add().addFilepattern("file.txt").call();
  645. git.commit().setMessage("Second").call();
  646. assertEquals("[file.txt, mode:100644, content:foobar" + lf + "]",
  647. indexState(CONTENT));
  648. }
  649. }
  650. @Test
  651. public void commitConflictWithAutoCrlfBaseCrLfOursLf() throws Exception {
  652. testConflictWithAutoCrlf("\r\n", "\n");
  653. }
  654. @Test
  655. public void commitConflictWithAutoCrlfBaseLfOursLf() throws Exception {
  656. testConflictWithAutoCrlf("\n", "\n");
  657. }
  658. @Test
  659. public void commitConflictWithAutoCrlfBasCrLfOursCrLf() throws Exception {
  660. testConflictWithAutoCrlf("\r\n", "\r\n");
  661. }
  662. @Test
  663. public void commitConflictWithAutoCrlfBaseLfOursCrLf() throws Exception {
  664. testConflictWithAutoCrlf("\n", "\r\n");
  665. }
  666. private static void addUnmergedEntry(String file, DirCacheBuilder builder) {
  667. DirCacheEntry stage1 = new DirCacheEntry(file, DirCacheEntry.STAGE_1);
  668. DirCacheEntry stage2 = new DirCacheEntry(file, DirCacheEntry.STAGE_2);
  669. DirCacheEntry stage3 = new DirCacheEntry(file, DirCacheEntry.STAGE_3);
  670. stage1.setFileMode(FileMode.REGULAR_FILE);
  671. stage2.setFileMode(FileMode.REGULAR_FILE);
  672. stage3.setFileMode(FileMode.REGULAR_FILE);
  673. builder.add(stage1);
  674. builder.add(stage2);
  675. builder.add(stage3);
  676. }
  677. @Test
  678. public void callSignerWithProperSigningKey() throws Exception {
  679. try (Git git = new Git(db)) {
  680. writeTrashFile("file1", "file1");
  681. git.add().addFilepattern("file1").call();
  682. String[] signingKey = new String[1];
  683. PersonIdent[] signingCommitters = new PersonIdent[1];
  684. AtomicInteger callCount = new AtomicInteger();
  685. GpgSigner.setDefault(new GpgSigner() {
  686. @Override
  687. public void sign(CommitBuilder commit, String gpgSigningKey,
  688. PersonIdent signingCommitter, CredentialsProvider credentialsProvider) {
  689. signingKey[0] = gpgSigningKey;
  690. signingCommitters[0] = signingCommitter;
  691. callCount.incrementAndGet();
  692. }
  693. @Override
  694. public boolean canLocateSigningKey(String gpgSigningKey,
  695. PersonIdent signingCommitter,
  696. CredentialsProvider credentialsProvider)
  697. throws CanceledException {
  698. return false;
  699. }
  700. });
  701. // first call should use config, which is expected to be null at
  702. // this time
  703. git.commit().setCommitter(committer).setSign(Boolean.TRUE)
  704. .setMessage("initial commit")
  705. .call();
  706. assertNull(signingKey[0]);
  707. assertEquals(1, callCount.get());
  708. assertSame(committer, signingCommitters[0]);
  709. writeTrashFile("file2", "file2");
  710. git.add().addFilepattern("file2").call();
  711. // second commit applies config value
  712. String expectedConfigSigningKey = "config-" + System.nanoTime();
  713. StoredConfig config = git.getRepository().getConfig();
  714. config.setString("user", null, "signingKey",
  715. expectedConfigSigningKey);
  716. config.save();
  717. git.commit().setCommitter(committer).setSign(Boolean.TRUE)
  718. .setMessage("initial commit")
  719. .call();
  720. assertEquals(expectedConfigSigningKey, signingKey[0]);
  721. assertEquals(2, callCount.get());
  722. assertSame(committer, signingCommitters[0]);
  723. writeTrashFile("file3", "file3");
  724. git.add().addFilepattern("file3").call();
  725. // now use specific on api
  726. String expectedSigningKey = "my-" + System.nanoTime();
  727. git.commit().setCommitter(committer).setSign(Boolean.TRUE)
  728. .setSigningKey(expectedSigningKey)
  729. .setMessage("initial commit").call();
  730. assertEquals(expectedSigningKey, signingKey[0]);
  731. assertEquals(3, callCount.get());
  732. assertSame(committer, signingCommitters[0]);
  733. }
  734. }
  735. @Test
  736. public void callSignerOnlyWhenSigning() throws Exception {
  737. try (Git git = new Git(db)) {
  738. writeTrashFile("file1", "file1");
  739. git.add().addFilepattern("file1").call();
  740. AtomicInteger callCount = new AtomicInteger();
  741. GpgSigner.setDefault(new GpgSigner() {
  742. @Override
  743. public void sign(CommitBuilder commit, String gpgSigningKey,
  744. PersonIdent signingCommitter, CredentialsProvider credentialsProvider) {
  745. callCount.incrementAndGet();
  746. }
  747. @Override
  748. public boolean canLocateSigningKey(String gpgSigningKey,
  749. PersonIdent signingCommitter,
  750. CredentialsProvider credentialsProvider)
  751. throws CanceledException {
  752. return false;
  753. }
  754. });
  755. // first call should use config, which is expected to be null at
  756. // this time
  757. git.commit().setMessage("initial commit").call();
  758. assertEquals(0, callCount.get());
  759. writeTrashFile("file2", "file2");
  760. git.add().addFilepattern("file2").call();
  761. // now force signing
  762. git.commit().setSign(Boolean.TRUE).setMessage("commit").call();
  763. assertEquals(1, callCount.get());
  764. writeTrashFile("file3", "file3");
  765. git.add().addFilepattern("file3").call();
  766. // now rely on config
  767. StoredConfig config = git.getRepository().getConfig();
  768. config.setBoolean("commit", null, "gpgSign", true);
  769. config.save();
  770. git.commit().setMessage("commit").call();
  771. assertEquals(2, callCount.get());
  772. writeTrashFile("file4", "file4");
  773. git.add().addFilepattern("file4").call();
  774. // now force "no-sign" (even though config is true)
  775. git.commit().setSign(Boolean.FALSE).setMessage("commit").call();
  776. assertEquals(2, callCount.get());
  777. }
  778. }
  779. }