diff options
3 files changed, 135 insertions, 0 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java index 0d03047d53..b39a68a22e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java @@ -46,12 +46,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.fail; import java.io.File; import java.util.Date; import java.util.List; import java.util.TimeZone; +import org.eclipse.jgit.api.errors.EmtpyCommitException; import org.eclipse.jgit.api.errors.WrongRepositoryStateException; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.dircache.DirCache; @@ -477,6 +480,34 @@ public class CommitCommandTest extends RepositoryTestCase { } @Test + public void commitEmptyCommits() throws Exception { + try (Git git = new Git(db)) { + + writeTrashFile("file1", "file1"); + git.add().addFilepattern("file1").call(); + RevCommit initial = git.commit().setMessage("initial commit") + .call(); + + RevCommit emptyFollowUp = git.commit() + .setAuthor("New Author", "newauthor@example.org") + .setMessage("no change").call(); + + assertNotEquals(initial.getId(), emptyFollowUp.getId()); + assertEquals(initial.getTree().getId(), + emptyFollowUp.getTree().getId()); + + try { + git.commit().setAuthor("New Author", "newauthor@example.org") + .setMessage("again no change").setAllowEmpty(false) + .call(); + fail("Didn't get the expected EmtpyCommitException"); + } catch (EmtpyCommitException e) { + // expect this exception + } + } + } + + @Test public void commitOnlyShouldCommitUnmergedPathAndNotAffectOthers() throws Exception { DirCache index = db.lockDirCache(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java index 4f9a5a3178..b5057ad282 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -53,6 +53,7 @@ import java.util.List; import org.eclipse.jgit.api.errors.AbortedByHookException; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; +import org.eclipse.jgit.api.errors.EmtpyCommitException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.NoFilepatternException; @@ -130,6 +131,8 @@ public class CommitCommand extends GitCommand<RevCommit> { private PrintStream hookOutRedirect; + private Boolean allowEmpty; + /** * @param repo */ @@ -231,6 +234,16 @@ public class CommitCommand extends GitCommand<RevCommit> { if (insertChangeId) insertChangeId(indexTreeId); + // Check for empty commits + if (headId != null && !allowEmpty.booleanValue()) { + RevCommit headCommit = rw.parseCommit(headId); + headCommit.getTree(); + if (indexTreeId.equals(headCommit.getTree())) { + throw new EmtpyCommitException( + JGitText.get().emptyCommit); + } + } + // Create a Commit object, populate it and write it CommitBuilder commit = new CommitBuilder(); commit.setCommitter(committer); @@ -457,6 +470,8 @@ public class CommitCommand extends GitCommand<RevCommit> { // there must be at least one change if (emptyCommit) + // Would like to throw a EmptyCommitException. But this would break the API + // TODO(ch): Change this in the next release throw new JGitInternalException(JGitText.get().emptyCommit); // update index @@ -510,6 +525,12 @@ public class CommitCommand extends GitCommand<RevCommit> { committer = new PersonIdent(repo); if (author == null && !amend) author = committer; + if (allowEmpty == null) + // JGit allows empty commits by default. Only when pathes are + // specified the commit should not be empty. This behaviour differs + // from native git but can only be adapted in the next release. + // TODO(ch) align the defaults with native git + allowEmpty = (only.isEmpty()) ? Boolean.TRUE : Boolean.FALSE; // when doing a merge commit parse MERGE_HEAD and MERGE_MSG files if (state == RepositoryState.MERGING_RESOLVED @@ -579,6 +600,27 @@ public class CommitCommand extends GitCommand<RevCommit> { } /** + * @param allowEmpty + * whether it should be allowed to create a commit which has the + * same tree as it's sole predecessor (a commit which doesn't + * change anything). By default when creating standard commits + * (without specifying paths) JGit allows to create such commits. + * When this flag is set to false an attempt to create an "empty" + * standard commit will lead to an EmptyCommitException. + * <p> + * By default when creating a commit containing only specified + * paths an attempt to create an empty commit leads to a + * {@link JGitInternalException}. By setting this flag to + * <code>true</code> this exception will not be thrown. + * @return {@code this} + * @since 4.2 + */ + public CommitCommand setAllowEmpty(boolean allowEmpty) { + this.allowEmpty = Boolean.valueOf(allowEmpty); + return this; + } + + /** * @return the commit message used for the <code>commit</code> */ public String getMessage() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/EmtpyCommitException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/EmtpyCommitException.java new file mode 100644 index 0000000000..b3cc1bfcf2 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/EmtpyCommitException.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015, 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.errors; + +/** + * Exception thrown when a newly created commit does not contain any changes + * + * @since 4.2 + */ +public class EmtpyCommitException extends GitAPIException { + private static final long serialVersionUID = 1L; + + /** + * @param message + * @param cause + */ + public EmtpyCommitException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param message + */ + public EmtpyCommitException(String message) { + super(message); + } +} |