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.

CommitAndLogCommandTest.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /*
  2. * Copyright (C) 2010, Christian Halstrick <christian.halstrick@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 java.nio.charset.StandardCharsets.UTF_8;
  45. import static org.junit.Assert.assertEquals;
  46. import static org.junit.Assert.assertFalse;
  47. import static org.junit.Assert.assertTrue;
  48. import static org.junit.Assert.fail;
  49. import static org.junit.Assume.assumeFalse;
  50. import java.io.File;
  51. import java.io.IOException;
  52. import java.io.PrintWriter;
  53. import org.eclipse.jgit.api.errors.GitAPIException;
  54. import org.eclipse.jgit.api.errors.JGitInternalException;
  55. import org.eclipse.jgit.api.errors.NoMessageException;
  56. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  57. import org.eclipse.jgit.errors.MissingObjectException;
  58. import org.eclipse.jgit.junit.RepositoryTestCase;
  59. import org.eclipse.jgit.lib.Constants;
  60. import org.eclipse.jgit.lib.ObjectId;
  61. import org.eclipse.jgit.lib.PersonIdent;
  62. import org.eclipse.jgit.lib.RefUpdate;
  63. import org.eclipse.jgit.lib.ReflogReader;
  64. import org.eclipse.jgit.revwalk.RevCommit;
  65. import org.eclipse.jgit.treewalk.TreeWalk;
  66. import org.eclipse.jgit.util.FS;
  67. import org.eclipse.jgit.util.FileUtils;
  68. import org.eclipse.jgit.util.RawParseUtils;
  69. import org.junit.Test;
  70. /**
  71. * Testing the git commit and log commands
  72. */
  73. public class CommitAndLogCommandTest extends RepositoryTestCase {
  74. @Test
  75. public void testSomeCommits() throws JGitInternalException, IOException,
  76. GitAPIException {
  77. // do 4 commits
  78. try (Git git = new Git(db)) {
  79. git.commit().setMessage("initial commit").call();
  80. git.commit().setMessage("second commit").setCommitter(committer).call();
  81. git.commit().setMessage("third commit").setAuthor(author).call();
  82. git.commit().setMessage("fourth commit").setAuthor(author)
  83. .setCommitter(committer).call();
  84. Iterable<RevCommit> commits = git.log().call();
  85. // check that all commits came in correctly
  86. PersonIdent defaultCommitter = new PersonIdent(db);
  87. PersonIdent expectedAuthors[] = new PersonIdent[] { defaultCommitter,
  88. committer, author, author };
  89. PersonIdent expectedCommitters[] = new PersonIdent[] {
  90. defaultCommitter, committer, defaultCommitter, committer };
  91. String expectedMessages[] = new String[] { "initial commit",
  92. "second commit", "third commit", "fourth commit" };
  93. int l = expectedAuthors.length - 1;
  94. for (RevCommit c : commits) {
  95. assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent()
  96. .getName());
  97. assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent()
  98. .getName());
  99. assertEquals(c.getFullMessage(), expectedMessages[l]);
  100. l--;
  101. }
  102. assertEquals(l, -1);
  103. ReflogReader reader = db.getReflogReader(Constants.HEAD);
  104. assertTrue(reader.getLastEntry().getComment().startsWith("commit:"));
  105. reader = db.getReflogReader(db.getBranch());
  106. assertTrue(reader.getLastEntry().getComment().startsWith("commit:"));
  107. }
  108. }
  109. @Test
  110. public void testLogWithFilter() throws IOException, JGitInternalException,
  111. GitAPIException {
  112. try (Git git = new Git(db)) {
  113. // create first file
  114. File file = new File(db.getWorkTree(), "a.txt");
  115. FileUtils.createNewFile(file);
  116. try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
  117. writer.print("content1");
  118. }
  119. // First commit - a.txt file
  120. git.add().addFilepattern("a.txt").call();
  121. git.commit().setMessage("commit1").setCommitter(committer).call();
  122. // create second file
  123. file = new File(db.getWorkTree(), "b.txt");
  124. FileUtils.createNewFile(file);
  125. try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
  126. writer.print("content2");
  127. }
  128. // Second commit - b.txt file
  129. git.add().addFilepattern("b.txt").call();
  130. git.commit().setMessage("commit2").setCommitter(committer).call();
  131. // First log - a.txt filter
  132. int count = 0;
  133. for (RevCommit c : git.log().addPath("a.txt").call()) {
  134. assertEquals("commit1", c.getFullMessage());
  135. count++;
  136. }
  137. assertEquals(1, count);
  138. // Second log - b.txt filter
  139. count = 0;
  140. for (RevCommit c : git.log().addPath("b.txt").call()) {
  141. assertEquals("commit2", c.getFullMessage());
  142. count++;
  143. }
  144. assertEquals(1, count);
  145. // Third log - without filter
  146. count = 0;
  147. for (RevCommit c : git.log().call()) {
  148. assertEquals(committer, c.getCommitterIdent());
  149. count++;
  150. }
  151. assertEquals(2, count);
  152. }
  153. }
  154. // try to do a commit without specifying a message. Should fail!
  155. @Test
  156. public void testWrongParams() throws GitAPIException {
  157. try (Git git = new Git(db)) {
  158. git.commit().setAuthor(author).call();
  159. fail("Didn't get the expected exception");
  160. } catch (NoMessageException e) {
  161. // expected
  162. }
  163. }
  164. // try to work with Commands after command has been invoked. Should throw
  165. // exceptions
  166. @Test
  167. public void testMultipleInvocations() throws GitAPIException {
  168. try (Git git = new Git(db)) {
  169. CommitCommand commitCmd = git.commit();
  170. commitCmd.setMessage("initial commit").call();
  171. try {
  172. // check that setters can't be called after invocation
  173. commitCmd.setAuthor(author);
  174. fail("didn't catch the expected exception");
  175. } catch (IllegalStateException e) {
  176. // expected
  177. }
  178. LogCommand logCmd = git.log();
  179. logCmd.call();
  180. try {
  181. // check that call can't be called twice
  182. logCmd.call();
  183. fail("didn't catch the expected exception");
  184. } catch (IllegalStateException e) {
  185. // expected
  186. }
  187. }
  188. }
  189. @Test
  190. public void testMergeEmptyBranches() throws IOException,
  191. JGitInternalException, GitAPIException {
  192. try (Git git = new Git(db)) {
  193. git.commit().setMessage("initial commit").call();
  194. RefUpdate r = db.updateRef("refs/heads/side");
  195. r.setNewObjectId(db.resolve(Constants.HEAD));
  196. assertEquals(r.forceUpdate(), RefUpdate.Result.NEW);
  197. RevCommit second = git.commit().setMessage("second commit").setCommitter(committer).call();
  198. db.updateRef(Constants.HEAD).link("refs/heads/side");
  199. RevCommit firstSide = git.commit().setMessage("first side commit").setAuthor(author).call();
  200. write(new File(db.getDirectory(), Constants.MERGE_HEAD), ObjectId
  201. .toString(db.resolve("refs/heads/master")));
  202. write(new File(db.getDirectory(), Constants.MERGE_MSG), "merging");
  203. RevCommit commit = git.commit().call();
  204. RevCommit[] parents = commit.getParents();
  205. assertEquals(parents[0], firstSide);
  206. assertEquals(parents[1], second);
  207. assertEquals(2, parents.length);
  208. }
  209. }
  210. @Test
  211. public void testAddUnstagedChanges() throws IOException,
  212. JGitInternalException, GitAPIException {
  213. File file = new File(db.getWorkTree(), "a.txt");
  214. FileUtils.createNewFile(file);
  215. try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
  216. writer.print("content");
  217. }
  218. try (Git git = new Git(db)) {
  219. git.add().addFilepattern("a.txt").call();
  220. RevCommit commit = git.commit().setMessage("initial commit").call();
  221. TreeWalk tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
  222. assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
  223. tw.getObjectId(0).getName());
  224. try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
  225. writer.print("content2");
  226. }
  227. commit = git.commit().setMessage("second commit").call();
  228. tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
  229. assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
  230. tw.getObjectId(0).getName());
  231. commit = git.commit().setAll(true).setMessage("third commit")
  232. .setAll(true).call();
  233. tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
  234. assertEquals("db00fd65b218578127ea51f3dffac701f12f486a",
  235. tw.getObjectId(0).getName());
  236. }
  237. }
  238. @Test
  239. public void testModeChange() throws IOException, GitAPIException {
  240. assumeFalse(System.getProperty("os.name").startsWith("Windows"));// SKIP
  241. try (Git git = new Git(db)) {
  242. // create file
  243. File file = new File(db.getWorkTree(), "a.txt");
  244. FileUtils.createNewFile(file);
  245. try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
  246. writer.print("content1");
  247. }
  248. // First commit - a.txt file
  249. git.add().addFilepattern("a.txt").call();
  250. git.commit().setMessage("commit1").setCommitter(committer).call();
  251. // pure mode change should be committable
  252. FS fs = db.getFS();
  253. fs.setExecute(file, true);
  254. git.add().addFilepattern("a.txt").call();
  255. git.commit().setMessage("mode change").setCommitter(committer).call();
  256. // pure mode change should be committable with -o option
  257. fs.setExecute(file, false);
  258. git.add().addFilepattern("a.txt").call();
  259. git.commit().setMessage("mode change").setCommitter(committer)
  260. .setOnly("a.txt").call();
  261. }
  262. }
  263. @Test
  264. public void testCommitRange() throws GitAPIException,
  265. JGitInternalException, MissingObjectException,
  266. IncorrectObjectTypeException {
  267. // do 4 commits and set the range to the second and fourth one
  268. try (Git git = new Git(db)) {
  269. git.commit().setMessage("first commit").call();
  270. RevCommit second = git.commit().setMessage("second commit")
  271. .setCommitter(committer).call();
  272. git.commit().setMessage("third commit").setAuthor(author).call();
  273. RevCommit last = git.commit().setMessage("fourth commit").setAuthor(
  274. author)
  275. .setCommitter(committer).call();
  276. Iterable<RevCommit> commits = git.log().addRange(second.getId(),
  277. last.getId()).call();
  278. // check that we have the third and fourth commit
  279. PersonIdent defaultCommitter = new PersonIdent(db);
  280. PersonIdent expectedAuthors[] = new PersonIdent[] { author, author };
  281. PersonIdent expectedCommitters[] = new PersonIdent[] {
  282. defaultCommitter, committer };
  283. String expectedMessages[] = new String[] { "third commit",
  284. "fourth commit" };
  285. int l = expectedAuthors.length - 1;
  286. for (RevCommit c : commits) {
  287. assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent()
  288. .getName());
  289. assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent()
  290. .getName());
  291. assertEquals(c.getFullMessage(), expectedMessages[l]);
  292. l--;
  293. }
  294. assertEquals(l, -1);
  295. }
  296. }
  297. @Test
  298. public void testCommitAmend() throws JGitInternalException, IOException,
  299. GitAPIException {
  300. try (Git git = new Git(db)) {
  301. git.commit().setMessage("first comit").call(); // typo
  302. git.commit().setAmend(true).setMessage("first commit").call();
  303. Iterable<RevCommit> commits = git.log().call();
  304. int c = 0;
  305. for (RevCommit commit : commits) {
  306. assertEquals("first commit", commit.getFullMessage());
  307. c++;
  308. }
  309. assertEquals(1, c);
  310. ReflogReader reader = db.getReflogReader(Constants.HEAD);
  311. assertTrue(reader.getLastEntry().getComment()
  312. .startsWith("commit (amend):"));
  313. reader = db.getReflogReader(db.getBranch());
  314. assertTrue(reader.getLastEntry().getComment()
  315. .startsWith("commit (amend):"));
  316. }
  317. }
  318. @Test
  319. public void testInsertChangeId() throws JGitInternalException,
  320. GitAPIException {
  321. try (Git git = new Git(db)) {
  322. String messageHeader = "Some header line\n\nSome detail explanation\n";
  323. String changeIdTemplate = "\nChange-Id: I"
  324. + ObjectId.zeroId().getName() + "\n";
  325. String messageFooter = "Some foooter lines\nAnother footer line\n";
  326. RevCommit commit = git.commit().setMessage(
  327. messageHeader + messageFooter)
  328. .setInsertChangeId(true).call();
  329. // we should find a real change id (at the end of the file)
  330. byte[] chars = commit.getFullMessage().getBytes(UTF_8);
  331. int lastLineBegin = RawParseUtils.prevLF(chars, chars.length - 2);
  332. String lastLine = RawParseUtils.decode(chars, lastLineBegin + 1,
  333. chars.length);
  334. assertTrue(lastLine.contains("Change-Id:"));
  335. assertFalse(lastLine.contains(
  336. "Change-Id: I" + ObjectId.zeroId().getName()));
  337. commit = git.commit().setMessage(
  338. messageHeader + changeIdTemplate + messageFooter)
  339. .setInsertChangeId(true).call();
  340. // we should find a real change id (in the line as dictated by the
  341. // template)
  342. chars = commit.getFullMessage().getBytes(UTF_8);
  343. int lineStart = 0;
  344. int lineEnd = 0;
  345. for (int i = 0; i < 4; i++) {
  346. lineStart = RawParseUtils.nextLF(chars, lineStart);
  347. }
  348. lineEnd = RawParseUtils.nextLF(chars, lineStart);
  349. String line = RawParseUtils.decode(chars, lineStart, lineEnd);
  350. assertTrue(line.contains("Change-Id:"));
  351. assertFalse(line.contains(
  352. "Change-Id: I" + ObjectId.zeroId().getName()));
  353. commit = git.commit().setMessage(
  354. messageHeader + changeIdTemplate + messageFooter)
  355. .setInsertChangeId(false).call();
  356. // we should find the untouched template
  357. chars = commit.getFullMessage().getBytes(UTF_8);
  358. lineStart = 0;
  359. lineEnd = 0;
  360. for (int i = 0; i < 4; i++) {
  361. lineStart = RawParseUtils.nextLF(chars, lineStart);
  362. }
  363. lineEnd = RawParseUtils.nextLF(chars, lineStart);
  364. line = RawParseUtils.decode(chars, lineStart, lineEnd);
  365. assertTrue(commit.getFullMessage().contains(
  366. "Change-Id: I" + ObjectId.zeroId().getName()));
  367. }
  368. }
  369. }