| /* |
| * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> |
| * and other copyright owners as documented in the project's IP log. |
| * |
| * This program and the accompanying materials are made available |
| * under the terms of the Eclipse Distribution License v1.0 which |
| * accompanies this distribution, is reproduced below, and is |
| * available at http://www.eclipse.org/org/documents/edl-v10.php |
| * |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * |
| * - Neither the name of the Eclipse Foundation, Inc. nor the |
| * names of its contributors may be used to endorse or promote |
| * products derived from this software without specific prior |
| * written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
| * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
| * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package org.eclipse.jgit.api; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| |
| import org.eclipse.jgit.api.errors.GitAPIException; |
| import org.eclipse.jgit.api.errors.JGitInternalException; |
| import org.eclipse.jgit.api.errors.NoMessageException; |
| import org.eclipse.jgit.errors.IncorrectObjectTypeException; |
| import org.eclipse.jgit.errors.MissingObjectException; |
| import org.eclipse.jgit.junit.RepositoryTestCase; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.PersonIdent; |
| import org.eclipse.jgit.lib.RefUpdate; |
| import org.eclipse.jgit.lib.ReflogReader; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.treewalk.TreeWalk; |
| import org.eclipse.jgit.util.FS; |
| import org.eclipse.jgit.util.FileUtils; |
| import org.eclipse.jgit.util.RawParseUtils; |
| import org.junit.Test; |
| |
| /** |
| * Testing the git commit and log commands |
| */ |
| public class CommitAndLogCommandTest extends RepositoryTestCase { |
| @Test |
| public void testSomeCommits() throws JGitInternalException, IOException, |
| GitAPIException { |
| |
| // do 4 commits |
| Git git = new Git(db); |
| git.commit().setMessage("initial commit").call(); |
| git.commit().setMessage("second commit").setCommitter(committer).call(); |
| git.commit().setMessage("third commit").setAuthor(author).call(); |
| git.commit().setMessage("fourth commit").setAuthor(author) |
| .setCommitter(committer).call(); |
| Iterable<RevCommit> commits = git.log().call(); |
| |
| // check that all commits came in correctly |
| PersonIdent defaultCommitter = new PersonIdent(db); |
| PersonIdent expectedAuthors[] = new PersonIdent[] { defaultCommitter, |
| committer, author, author }; |
| PersonIdent expectedCommitters[] = new PersonIdent[] { |
| defaultCommitter, committer, defaultCommitter, committer }; |
| String expectedMessages[] = new String[] { "initial commit", |
| "second commit", "third commit", "fourth commit" }; |
| int l = expectedAuthors.length - 1; |
| for (RevCommit c : commits) { |
| assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent() |
| .getName()); |
| assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent() |
| .getName()); |
| assertEquals(c.getFullMessage(), expectedMessages[l]); |
| l--; |
| } |
| assertEquals(l, -1); |
| ReflogReader reader = db.getReflogReader(Constants.HEAD); |
| assertTrue(reader.getLastEntry().getComment().startsWith("commit:")); |
| reader = db.getReflogReader(db.getBranch()); |
| assertTrue(reader.getLastEntry().getComment().startsWith("commit:")); |
| } |
| |
| @Test |
| public void testLogWithFilter() throws IOException, JGitInternalException, |
| GitAPIException { |
| |
| Git git = new Git(db); |
| |
| // create first file |
| File file = new File(db.getWorkTree(), "a.txt"); |
| FileUtils.createNewFile(file); |
| PrintWriter writer = new PrintWriter(file); |
| writer.print("content1"); |
| writer.close(); |
| |
| // First commit - a.txt file |
| git.add().addFilepattern("a.txt").call(); |
| git.commit().setMessage("commit1").setCommitter(committer).call(); |
| |
| // create second file |
| file = new File(db.getWorkTree(), "b.txt"); |
| FileUtils.createNewFile(file); |
| writer = new PrintWriter(file); |
| writer.print("content2"); |
| writer.close(); |
| |
| // Second commit - b.txt file |
| git.add().addFilepattern("b.txt").call(); |
| git.commit().setMessage("commit2").setCommitter(committer).call(); |
| |
| // First log - a.txt filter |
| int count = 0; |
| for (RevCommit c : git.log().addPath("a.txt").call()) { |
| assertEquals("commit1", c.getFullMessage()); |
| count++; |
| } |
| assertEquals(1, count); |
| |
| // Second log - b.txt filter |
| count = 0; |
| for (RevCommit c : git.log().addPath("b.txt").call()) { |
| assertEquals("commit2", c.getFullMessage()); |
| count++; |
| } |
| assertEquals(1, count); |
| |
| // Third log - without filter |
| count = 0; |
| for (RevCommit c : git.log().call()) { |
| assertEquals(committer, c.getCommitterIdent()); |
| count++; |
| } |
| assertEquals(2, count); |
| } |
| |
| // try to do a commit without specifying a message. Should fail! |
| @Test |
| public void testWrongParams() throws GitAPIException { |
| Git git = new Git(db); |
| try { |
| git.commit().setAuthor(author).call(); |
| fail("Didn't get the expected exception"); |
| } catch (NoMessageException e) { |
| // expected |
| } |
| } |
| |
| // try to work with Commands after command has been invoked. Should throw |
| // exceptions |
| @Test |
| public void testMultipleInvocations() throws GitAPIException { |
| Git git = new Git(db); |
| CommitCommand commitCmd = git.commit(); |
| commitCmd.setMessage("initial commit").call(); |
| try { |
| // check that setters can't be called after invocation |
| commitCmd.setAuthor(author); |
| fail("didn't catch the expected exception"); |
| } catch (IllegalStateException e) { |
| // expected |
| } |
| LogCommand logCmd = git.log(); |
| logCmd.call(); |
| try { |
| // check that call can't be called twice |
| logCmd.call(); |
| fail("didn't catch the expected exception"); |
| } catch (IllegalStateException e) { |
| // expected |
| } |
| } |
| |
| @Test |
| public void testMergeEmptyBranches() throws IOException, |
| JGitInternalException, GitAPIException { |
| Git git = new Git(db); |
| git.commit().setMessage("initial commit").call(); |
| RefUpdate r = db.updateRef("refs/heads/side"); |
| r.setNewObjectId(db.resolve(Constants.HEAD)); |
| assertEquals(r.forceUpdate(), RefUpdate.Result.NEW); |
| RevCommit second = git.commit().setMessage("second commit").setCommitter(committer).call(); |
| db.updateRef(Constants.HEAD).link("refs/heads/side"); |
| RevCommit firstSide = git.commit().setMessage("first side commit").setAuthor(author).call(); |
| |
| write(new File(db.getDirectory(), Constants.MERGE_HEAD), ObjectId |
| .toString(db.resolve("refs/heads/master"))); |
| write(new File(db.getDirectory(), Constants.MERGE_MSG), "merging"); |
| |
| RevCommit commit = git.commit().call(); |
| RevCommit[] parents = commit.getParents(); |
| assertEquals(parents[0], firstSide); |
| assertEquals(parents[1], second); |
| assertEquals(2, parents.length); |
| } |
| |
| @Test |
| public void testAddUnstagedChanges() throws IOException, |
| JGitInternalException, GitAPIException { |
| File file = new File(db.getWorkTree(), "a.txt"); |
| FileUtils.createNewFile(file); |
| PrintWriter writer = new PrintWriter(file); |
| writer.print("content"); |
| writer.close(); |
| |
| Git git = new Git(db); |
| git.add().addFilepattern("a.txt").call(); |
| RevCommit commit = git.commit().setMessage("initial commit").call(); |
| TreeWalk tw = TreeWalk.forPath(db, "a.txt", commit.getTree()); |
| assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea", |
| tw.getObjectId(0).getName()); |
| |
| writer = new PrintWriter(file); |
| writer.print("content2"); |
| writer.close(); |
| commit = git.commit().setMessage("second commit").call(); |
| tw = TreeWalk.forPath(db, "a.txt", commit.getTree()); |
| assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea", |
| tw.getObjectId(0).getName()); |
| |
| commit = git.commit().setAll(true).setMessage("third commit") |
| .setAll(true).call(); |
| tw = TreeWalk.forPath(db, "a.txt", commit.getTree()); |
| assertEquals("db00fd65b218578127ea51f3dffac701f12f486a", |
| tw.getObjectId(0).getName()); |
| } |
| |
| @Test |
| public void testModeChange() throws IOException, GitAPIException { |
| if (System.getProperty("os.name").startsWith("Windows")) |
| return; // SKIP |
| Git git = new Git(db); |
| |
| // create file |
| File file = new File(db.getWorkTree(), "a.txt"); |
| FileUtils.createNewFile(file); |
| PrintWriter writer = new PrintWriter(file); |
| writer.print("content1"); |
| writer.close(); |
| |
| // First commit - a.txt file |
| git.add().addFilepattern("a.txt").call(); |
| git.commit().setMessage("commit1").setCommitter(committer).call(); |
| |
| // pure mode change should be committable |
| FS fs = db.getFS(); |
| fs.setExecute(file, true); |
| git.add().addFilepattern("a.txt").call(); |
| git.commit().setMessage("mode change").setCommitter(committer).call(); |
| |
| // pure mode change should be committable with -o option |
| fs.setExecute(file, false); |
| git.add().addFilepattern("a.txt").call(); |
| git.commit().setMessage("mode change").setCommitter(committer) |
| .setOnly("a.txt").call(); |
| } |
| |
| @Test |
| public void testCommitRange() throws GitAPIException, |
| JGitInternalException, MissingObjectException, |
| IncorrectObjectTypeException { |
| // do 4 commits and set the range to the second and fourth one |
| Git git = new Git(db); |
| git.commit().setMessage("first commit").call(); |
| RevCommit second = git.commit().setMessage("second commit") |
| .setCommitter(committer).call(); |
| git.commit().setMessage("third commit").setAuthor(author).call(); |
| RevCommit last = git.commit().setMessage("fourth commit").setAuthor( |
| author) |
| .setCommitter(committer).call(); |
| Iterable<RevCommit> commits = git.log().addRange(second.getId(), |
| last.getId()).call(); |
| |
| // check that we have the third and fourth commit |
| PersonIdent defaultCommitter = new PersonIdent(db); |
| PersonIdent expectedAuthors[] = new PersonIdent[] { author, author }; |
| PersonIdent expectedCommitters[] = new PersonIdent[] { |
| defaultCommitter, committer }; |
| String expectedMessages[] = new String[] { "third commit", |
| "fourth commit" }; |
| int l = expectedAuthors.length - 1; |
| for (RevCommit c : commits) { |
| assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent() |
| .getName()); |
| assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent() |
| .getName()); |
| assertEquals(c.getFullMessage(), expectedMessages[l]); |
| l--; |
| } |
| assertEquals(l, -1); |
| } |
| |
| @Test |
| public void testCommitAmend() throws JGitInternalException, IOException, |
| GitAPIException { |
| Git git = new Git(db); |
| git.commit().setMessage("first comit").call(); // typo |
| git.commit().setAmend(true).setMessage("first commit").call(); |
| |
| Iterable<RevCommit> commits = git.log().call(); |
| int c = 0; |
| for (RevCommit commit : commits) { |
| assertEquals("first commit", commit.getFullMessage()); |
| c++; |
| } |
| assertEquals(1, c); |
| ReflogReader reader = db.getReflogReader(Constants.HEAD); |
| assertTrue(reader.getLastEntry().getComment() |
| .startsWith("commit (amend):")); |
| reader = db.getReflogReader(db.getBranch()); |
| assertTrue(reader.getLastEntry().getComment() |
| .startsWith("commit (amend):")); |
| } |
| |
| @Test |
| public void testInsertChangeId() throws JGitInternalException, |
| GitAPIException { |
| Git git = new Git(db); |
| String messageHeader = "Some header line\n\nSome detail explanation\n"; |
| String changeIdTemplate = "\nChange-Id: I" |
| + ObjectId.zeroId().getName() + "\n"; |
| String messageFooter = "Some foooter lines\nAnother footer line\n"; |
| RevCommit commit = git.commit().setMessage( |
| messageHeader + messageFooter) |
| .setInsertChangeId(true).call(); |
| // we should find a real change id (at the end of the file) |
| byte[] chars = commit.getFullMessage().getBytes(); |
| int lastLineBegin = RawParseUtils.prevLF(chars, chars.length - 2); |
| String lastLine = RawParseUtils.decode(chars, lastLineBegin + 1, |
| chars.length); |
| assertTrue(lastLine.contains("Change-Id:")); |
| assertFalse(lastLine.contains( |
| "Change-Id: I" + ObjectId.zeroId().getName())); |
| |
| commit = git.commit().setMessage( |
| messageHeader + changeIdTemplate + messageFooter) |
| .setInsertChangeId(true).call(); |
| // we should find a real change id (in the line as dictated by the |
| // template) |
| chars = commit.getFullMessage().getBytes(); |
| int lineStart = 0; |
| int lineEnd = 0; |
| for (int i = 0; i < 4; i++) { |
| lineStart = RawParseUtils.nextLF(chars, lineStart); |
| } |
| lineEnd = RawParseUtils.nextLF(chars, lineStart); |
| |
| String line = RawParseUtils.decode(chars, lineStart, lineEnd); |
| |
| assertTrue(line.contains("Change-Id:")); |
| assertFalse(line.contains( |
| "Change-Id: I" + ObjectId.zeroId().getName())); |
| |
| commit = git.commit().setMessage( |
| messageHeader + changeIdTemplate + messageFooter) |
| .setInsertChangeId(false).call(); |
| // we should find the untouched template |
| chars = commit.getFullMessage().getBytes(); |
| lineStart = 0; |
| lineEnd = 0; |
| for (int i = 0; i < 4; i++) { |
| lineStart = RawParseUtils.nextLF(chars, lineStart); |
| } |
| lineEnd = RawParseUtils.nextLF(chars, lineStart); |
| |
| line = RawParseUtils.decode(chars, lineStart, lineEnd); |
| |
| assertTrue(commit.getFullMessage().contains( |
| "Change-Id: I" + ObjectId.zeroId().getName())); |
| } |
| } |