Browse Source

Implement rebase --continue and --skip

For --continue, the Rebase command asserts that there are no unmerged
paths in the current repository. Then it checks if a commit is needed.
If yes, the commit message and author are taken from the author_script
and message files, respectively, and a commit is performed before the
next step is applied.
For --skip, the workspace is reset to the current HEAD before applying
the next step.

Includes some tests and a refactoring that extracts Strings in the
code into constants.


Change-Id: I72d9968535727046e737ec20e23239fe79976179
Signed-off-by: Mathias Kinzler <mathias.kinzler@sap.com>
Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>
tags/v0.10.1
Mathias Kinzler 13 years ago
parent
commit
6bca46e168

+ 453
- 94
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java View File

@@ -51,10 +51,12 @@ import java.io.InputStreamReader;
import org.eclipse.jgit.api.RebaseCommand.Operation;
import org.eclipse.jgit.api.RebaseResult.Status;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.UnmergedPathsException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCacheCheckout;
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.RepositoryState;
import org.eclipse.jgit.lib.RepositoryTestCase;
@@ -62,6 +64,16 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;

public class RebaseCommandTest extends RepositoryTestCase {
private static final String FILE1 = "file1";

protected Git git;

@Override
protected void setUp() throws Exception {
super.setUp();
this.git = new Git(db);
}

private void createBranch(ObjectId objectId, String branchName)
throws IOException {
RefUpdate updateRef = db.updateRef(branchName);
@@ -88,8 +100,8 @@ public class RebaseCommandTest extends RepositoryTestCase {
IOException {
RevWalk walk = new RevWalk(db);
RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD));
DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree(),
db.lockDirCache(), commit.getTree());
DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree(), db
.lockDirCache(), commit.getTree());
dco.setFailOnConflict(true);
dco.checkout();
walk.release();
@@ -100,14 +112,12 @@ public class RebaseCommandTest extends RepositoryTestCase {
}

public void testFastForwardWithNewFile() throws Exception {
Git git = new Git(db);

// create file1 on master
writeTrashFile("file1", "file1");
git.add().addFilepattern("file1").call();
writeTrashFile(FILE1, FILE1);
git.add().addFilepattern(FILE1).call();
RevCommit first = git.commit().setMessage("Add file1").call();

assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());
// create a topic branch
createBranch(first, "refs/heads/topic");
// create file2 on master
@@ -124,32 +134,27 @@ public class RebaseCommandTest extends RepositoryTestCase {
}

public void testUpToDate() throws Exception {
Git git = new Git(db);

// create file1 on master
writeTrashFile("file1", "file1");
git.add().addFilepattern("file1").call();
writeTrashFile(FILE1, FILE1);
git.add().addFilepattern(FILE1).call();
RevCommit first = git.commit().setMessage("Add file1").call();

assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());

RebaseResult res = git.rebase().setUpstream(first).call();
assertEquals(Status.UP_TO_DATE, res.getStatus());
}

public void testUnknownUpstream() throws Exception {
Git git = new Git(db);

// create file1 on master
writeTrashFile("file1", "file1");
git.add().addFilepattern("file1").call();
writeTrashFile(FILE1, FILE1);
git.add().addFilepattern(FILE1).call();
git.commit().setMessage("Add file1").call();

assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());

try {
git.rebase().setUpstream("refs/heads/xyz")
.call();
git.rebase().setUpstream("refs/heads/xyz").call();
fail("expected exception was not thrown");
} catch (RefNotFoundException e) {
// expected exception
@@ -157,17 +162,15 @@ public class RebaseCommandTest extends RepositoryTestCase {
}

public void testConflictFreeWithSingleFile() throws Exception {
Git git = new Git(db);

// create file1 on master
File theFile = writeTrashFile("file1", "1\n2\n3\n");
git.add().addFilepattern("file1").call();
File theFile = writeTrashFile(FILE1, "1\n2\n3\n");
git.add().addFilepattern(FILE1).call();
RevCommit second = git.commit().setMessage("Add file1").call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());
// change first line in master and commit
writeTrashFile("file1", "1master\n2\n3\n");
writeTrashFile(FILE1, "1master\n2\n3\n");
checkFile(theFile, "1master\n2\n3\n");
git.add().addFilepattern("file1").call();
git.add().addFilepattern(FILE1).call();
RevCommit lastMasterChange = git.commit().setMessage(
"change file1 in master").call();

@@ -177,10 +180,10 @@ public class RebaseCommandTest extends RepositoryTestCase {
// we have the old content again
checkFile(theFile, "1\n2\n3\n");

assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());
// change third line in topic branch
writeTrashFile("file1", "1\n2\n3\ntopic\n");
git.add().addFilepattern("file1").call();
writeTrashFile(FILE1, "1\n2\n3\ntopic\n");
git.add().addFilepattern(FILE1).call();
git.commit().setMessage("change file1 in topic").call();

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
@@ -193,19 +196,17 @@ public class RebaseCommandTest extends RepositoryTestCase {
}

public void testDetachedHead() throws Exception {
Git git = new Git(db);

// create file1 on master
File theFile = writeTrashFile("file1", "1\n2\n3\n");
git.add().addFilepattern("file1").call();
File theFile = writeTrashFile(FILE1, "1\n2\n3\n");
git.add().addFilepattern(FILE1).call();
RevCommit second = git.commit().setMessage("Add file1").call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());
// change first line in master and commit
writeTrashFile("file1", "1master\n2\n3\n");
writeTrashFile(FILE1, "1master\n2\n3\n");
checkFile(theFile, "1master\n2\n3\n");
git.add().addFilepattern("file1").call();
RevCommit lastMasterChange = git.commit()
.setMessage("change file1 in master").call();
git.add().addFilepattern(FILE1).call();
RevCommit lastMasterChange = git.commit().setMessage(
"change file1 in master").call();

// create a topic branch based on second commit
createBranch(second, "refs/heads/topic");
@@ -213,10 +214,10 @@ public class RebaseCommandTest extends RepositoryTestCase {
// we have the old content again
checkFile(theFile, "1\n2\n3\n");

assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());
// change third line in topic branch
writeTrashFile("file1", "1\n2\n3\ntopic\n");
git.add().addFilepattern("file1").call();
writeTrashFile(FILE1, "1\n2\n3\ntopic\n");
git.add().addFilepattern(FILE1).call();
RevCommit topicCommit = git.commit()
.setMessage("change file1 in topic").call();
checkoutBranch("refs/heads/master");
@@ -226,18 +227,15 @@ public class RebaseCommandTest extends RepositoryTestCase {
RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.OK, res.getStatus());
checkFile(theFile, "1master\n2\n3\ntopic\n");
assertEquals(lastMasterChange,
new RevWalk(db).parseCommit(db.resolve(Constants.HEAD))
.getParent(0));
assertEquals(lastMasterChange, new RevWalk(db).parseCommit(
db.resolve(Constants.HEAD)).getParent(0));

}

public void testFilesAddedFromTwoBranches() throws Exception {
Git git = new Git(db);

// create file1 on master
writeTrashFile("file1", "file1");
git.add().addFilepattern("file1").call();
writeTrashFile(FILE1, FILE1);
git.add().addFilepattern(FILE1).call();
RevCommit masterCommit = git.commit().setMessage("Add file1 to master")
.call();

@@ -256,14 +254,14 @@ public class RebaseCommandTest extends RepositoryTestCase {
git.add().addFilepattern("file3").call();
git.commit().setMessage("Add file3 to branch file3").call();

assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());
assertFalse(new File(db.getWorkTree(), "file2").exists());
assertTrue(new File(db.getWorkTree(), "file3").exists());

RebaseResult res = git.rebase().setUpstream("refs/heads/file2").call();
assertEquals(Status.OK, res.getStatus());

assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
assertTrue(new File(db.getWorkTree(), "file3").exists());

@@ -273,54 +271,40 @@ public class RebaseCommandTest extends RepositoryTestCase {
db.resolve(Constants.HEAD)).getParent(0));

checkoutBranch("refs/heads/file2");
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), FILE1).exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
assertFalse(new File(db.getWorkTree(), "file3").exists());
}

public void testAbortOnConflict() throws Exception {
Git git = new Git(db);

public void testStopOnConflict() throws Exception {
// create file1 on master
File theFile = writeTrashFile("file1", "1\n2\n3\n");
git.add().addFilepattern("file1").call();
RevCommit firstInMaster = git.commit().setMessage("Add file1").call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
// change first line in master and commit
writeTrashFile("file1", "1master\n2\n3\n");
checkFile(theFile, "1master\n2\n3\n");
git.add().addFilepattern("file1").call();
git.commit().setMessage("change file1 in master").call();

RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
"2", "3");
// change first line in master
writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");
checkFile(FILE1, "1master", "2", "3");
// create a topic branch based on second commit
createBranch(firstInMaster, "refs/heads/topic");
checkoutBranch("refs/heads/topic");
// we have the old content again
checkFile(theFile, "1\n2\n3\n");
checkFile(FILE1, "1", "2", "3");

assertTrue(new File(db.getWorkTree(), "file1").exists());
// add a line (non-conflicting)
writeTrashFile("file1", "1\n2\n3\ntopic4\n");
git.add().addFilepattern("file1").call();
git.commit().setMessage("add a line to file1 in topic").call();
writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
"3", "topic4");

// change first line (conflicting)
writeTrashFile("file1", "1topic\n2\n3\ntopic4\n");
git.add().addFilepattern("file1").call();
RevCommit conflicting = git.commit()
.setMessage("change file1 in topic").call();
RevCommit conflicting = writeFileAndCommit(FILE1,
"change file1 in topic", "1topic", "2", "3", "topic4");

// change second line (not conflicting)
writeTrashFile("file1", "1topic\n2topic\n3\ntopic4\n");
git.add().addFilepattern("file1").call();
RevCommit lastTopicCommit = git.commit().setMessage(
"change file1 in topic again").call();
RevCommit lastTopicCommit = writeFileAndCommit(FILE1,
"change file1 in topic again", "1topic", "2", "3", "topic4");

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.STOPPED, res.getStatus());
assertEquals(conflicting, res.getCurrentCommit());
checkFile(theFile,
"<<<<<<< OURS\n1master\n=======\n1topic\n>>>>>>> THEIRS\n2\n3\ntopic4\n");
checkFile(FILE1,
"<<<<<<< OURS\n1master\n=======\n1topic\n>>>>>>> THEIRS\n2\n3\ntopic4");

assertEquals(RepositoryState.REBASING_INTERACTIVE, db
.getRepositoryState());
@@ -341,7 +325,7 @@ public class RebaseCommandTest extends RepositoryTestCase {
res = git.rebase().setOperation(Operation.ABORT).call();
assertEquals(res.getStatus(), Status.ABORTED);
assertEquals("refs/heads/topic", db.getFullBranch());
checkFile(theFile, "1topic\n2topic\n3\ntopic4\n");
checkFile(FILE1, "1topic", "2", "3", "topic4");
RevWalk rw = new RevWalk(db);
assertEquals(lastTopicCommit, rw
.parseCommit(db.resolve(Constants.HEAD)));
@@ -351,12 +335,335 @@ public class RebaseCommandTest extends RepositoryTestCase {
assertFalse(new File(db.getDirectory(), "rebase-merge").exists());
}

public void testAbortOnConflictFileCreationAndDeletion() throws Exception {
Git git = new Git(db);
public void testStopOnConflictAndContinue() throws Exception {
// create file1 on master
RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
"2", "3");
// change in master
writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");

checkFile(FILE1, "1master", "2", "3");
// create a topic branch based on the first commit
createBranch(firstInMaster, "refs/heads/topic");
checkoutBranch("refs/heads/topic");
// we have the old content again
checkFile(FILE1, "1", "2", "3");

// add a line (non-conflicting)
writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
"3", "4topic");

// change first line (conflicting)
writeFileAndCommit(FILE1,
"change file1 in topic\n\nThis is conflicting", "1topic", "2",
"3", "4topic");

// change second line (not conflicting)
writeFileAndCommit(FILE1, "change file1 in topic again", "1topic",
"2topic", "3", "4topic");

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.STOPPED, res.getStatus());

// continue should throw a meaningful exception
try {
res = git.rebase().setOperation(Operation.CONTINUE).call();
fail("Expected Exception not thrown");
} catch (UnmergedPathsException e) {
// expected
}

// merge the file; the second topic commit should go through
writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");

res = git.rebase().setOperation(Operation.CONTINUE).call();
assertNotNull(res);
assertEquals(Status.OK, res.getStatus());
assertEquals(RepositoryState.SAFE, db.getRepositoryState());

ObjectId headId = db.resolve(Constants.HEAD);
RevWalk rw = new RevWalk(db);
RevCommit rc = rw.parseCommit(headId);
RevCommit parent = rw.parseCommit(rc.getParent(0));
assertEquals("change file1 in topic\n\nThis is conflicting", parent
.getFullMessage());
}

public void testStopOnConflictAndFailContinueIfFileIsDirty()
throws Exception {
// create file1 on master
RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
"2", "3");
// change in master
writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");

checkFile(FILE1, "1master", "2", "3");
// create a topic branch based on the first commit
createBranch(firstInMaster, "refs/heads/topic");
checkoutBranch("refs/heads/topic");
// we have the old content again
checkFile(FILE1, "1", "2", "3");

// add a line (non-conflicting)
writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
"3", "4topic");

// change first line (conflicting)
writeFileAndCommit(FILE1,
"change file1 in topic\n\nThis is conflicting", "1topic", "2",
"3", "4topic");

// change second line (not conflicting)
writeFileAndCommit(FILE1, "change file1 in topic again", "1topic",
"2topic", "3", "4topic");

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.STOPPED, res.getStatus());

git.add().addFilepattern(FILE1).call();
File trashFile = writeTrashFile(FILE1, "Some local change");

res = git.rebase().setOperation(Operation.CONTINUE).call();
assertNotNull(res);
assertEquals(Status.STOPPED, res.getStatus());
checkFile(trashFile, "Some local change");
}

public void testStopOnLastConflictAndContinue() throws Exception {
// create file1 on master
RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
"2", "3");
// change in master
writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");

checkFile(FILE1, "1master", "2", "3");
// create a topic branch based on the first commit
createBranch(firstInMaster, "refs/heads/topic");
checkoutBranch("refs/heads/topic");
// we have the old content again
checkFile(FILE1, "1", "2", "3");

// add a line (non-conflicting)
writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
"3", "4topic");

// change first line (conflicting)
writeFileAndCommit(FILE1,
"change file1 in topic\n\nThis is conflicting", "1topic", "2",
"3", "4topic");

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.STOPPED, res.getStatus());

// merge the file; the second topic commit should go through
writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");

res = git.rebase().setOperation(Operation.CONTINUE).call();
assertNotNull(res);
assertEquals(Status.OK, res.getStatus());
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
}

public void testStopOnLastConflictAndSkip() throws Exception {
// create file1 on master
RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
"2", "3");
// change in master
writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");

checkFile(FILE1, "1master", "2", "3");
// create a topic branch based on the first commit
createBranch(firstInMaster, "refs/heads/topic");
checkoutBranch("refs/heads/topic");
// we have the old content again
checkFile(FILE1, "1", "2", "3");

// add a line (non-conflicting)
writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
"3", "4topic");

// change first line (conflicting)
writeFileAndCommit(FILE1,
"change file1 in topic\n\nThis is conflicting", "1topic", "2",
"3", "4topic");

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.STOPPED, res.getStatus());

// merge the file; the second topic commit should go through
writeFileAndAdd(FILE1, "1topic", "2", "3", "4topic");

res = git.rebase().setOperation(Operation.SKIP).call();
assertNotNull(res);
assertEquals(Status.OK, res.getStatus());
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
}

public void testStopOnConflictAndSkipNoConflict() throws Exception {
// create file1 on master
RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
"2", "3");
// change in master
writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");

checkFile(FILE1, "1master", "2", "3");
// create a topic branch based on the first commit
createBranch(firstInMaster, "refs/heads/topic");
checkoutBranch("refs/heads/topic");
// we have the old content again
checkFile(FILE1, "1", "2", "3");

// add a line (non-conflicting)
writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
"3", "4topic");

// change first line (conflicting)
writeFileAndCommit(FILE1,
"change file1 in topic\n\nThis is conflicting", "1topic", "2",
"3", "4topic");

// change third line (not conflicting)
writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
"3topic", "4topic");

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.STOPPED, res.getStatus());

res = git.rebase().setOperation(Operation.SKIP).call();

checkFile(FILE1, "1master", "2", "3topic", "4topic");
assertEquals(Status.OK, res.getStatus());
}

public void testStopOnConflictAndSkipWithConflict() throws Exception {
// create file1 on master
RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
"2", "3", "4");
// change in master
writeFileAndCommit(FILE1, "change file1 in master", "1master", "2",
"3master", "4");

checkFile(FILE1, "1master", "2", "3master", "4");
// create a topic branch based on the first commit
createBranch(firstInMaster, "refs/heads/topic");
checkoutBranch("refs/heads/topic");
// we have the old content again
checkFile(FILE1, "1", "2", "3", "4");

// add a line (non-conflicting)
writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
"3", "4", "5topic");

// change first line (conflicting)
writeFileAndCommit(FILE1,
"change file1 in topic\n\nThis is conflicting", "1topic", "2",
"3", "4", "5topic");

// change third line (conflicting)
writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
"3topic", "4", "5topic");

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.STOPPED, res.getStatus());

res = git.rebase().setOperation(Operation.SKIP).call();
// TODO is this correct? It is what the command line returns
checkFile(FILE1,
"1master\n2\n<<<<<<< OURS\n3master\n=======\n3topic\n>>>>>>> THEIRS\n4\n5topic");
assertEquals(Status.STOPPED, res.getStatus());
}

public void testStopOnConflictCommitAndContinue() throws Exception {
// create file1 on master
RevCommit firstInMaster = writeFileAndCommit(FILE1, "Add file1", "1",
"2", "3");
// change in master
writeFileAndCommit(FILE1, "change file1 in master", "1master", "2", "3");

checkFile(FILE1, "1master", "2", "3");
// create a topic branch based on the first commit
createBranch(firstInMaster, "refs/heads/topic");
checkoutBranch("refs/heads/topic");
// we have the old content again
checkFile(FILE1, "1", "2", "3");

// add a line (non-conflicting)
writeFileAndCommit(FILE1, "add a line to file1 in topic", "1", "2",
"3", "4topic");

// change first line (conflicting)
writeFileAndCommit(FILE1,
"change file1 in topic\n\nThis is conflicting", "1topic", "2",
"3", "4topic");

// change second line (not conflicting)
writeFileAndCommit(FILE1, "change file1 in topic again", "1topic", "2",
"3topic", "4topic");

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.STOPPED, res.getStatus());

// continue should throw a meaningful exception
try {
res = git.rebase().setOperation(Operation.CONTINUE).call();
fail("Expected Exception not thrown");
} catch (UnmergedPathsException e) {
// expected
}

// merge the file; the second topic commit should go through
writeFileAndCommit(FILE1, "A different commit message", "1topic", "2",
"3", "4topic");

res = git.rebase().setOperation(Operation.CONTINUE).call();
assertNotNull(res);
assertEquals(Status.OK, res.getStatus());
assertEquals(RepositoryState.SAFE, db.getRepositoryState());

ObjectId headId = db.resolve(Constants.HEAD);
RevWalk rw = new RevWalk(db);
RevCommit rc = rw.parseCommit(headId);
RevCommit parent = rw.parseCommit(rc.getParent(0));
assertEquals("A different commit message", parent.getFullMessage());
}

private RevCommit writeFileAndCommit(String fileName, String commitMessage,
String... lines) throws Exception {
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line);
sb.append('\n');
}
writeTrashFile(fileName, sb.toString());
git.add().addFilepattern(fileName).call();
return git.commit().setMessage(commitMessage).call();
}

private void writeFileAndAdd(String fileName, String... lines)
throws Exception {
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line);
sb.append('\n');
}
writeTrashFile(fileName, sb.toString());
git.add().addFilepattern(fileName).call();
}

private void checkFile(String fileName, String... lines) throws Exception {
File file = new File(db.getWorkTree(), fileName);
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line);
sb.append('\n');
}
checkFile(file, sb.toString());
}

public void testStopOnConflictFileCreationAndDeletion() throws Exception {
// create file1 on master
writeTrashFile("file1", "Hello World");
git.add().addFilepattern("file1").call();
writeTrashFile(FILE1, "Hello World");
git.add().addFilepattern(FILE1).call();
// create file2 on master
File file2 = writeTrashFile("file2", "Hello World 2");
git.add().addFilepattern("file2").call();
@@ -378,10 +685,8 @@ public class RebaseCommandTest extends RepositoryTestCase {
writeTrashFile("folder6/file1", "Hello World folder6");
git.add().addFilepattern("folder6/file1").call();

git.commit()
.setMessage(
"Add file 4 and folder folder6, delete file2 on master")
.call();
git.commit().setMessage(
"Add file 4 and folder folder6, delete file2 on master").call();

// create a topic branch based on second commit
createBranch(firstInMaster, "refs/heads/topic");
@@ -403,9 +708,8 @@ public class RebaseCommandTest extends RepositoryTestCase {

deleteTrashFile("file5");
git.add().setUpdate(true).addFilepattern("file5").call();
RevCommit conflicting = git.commit()
.setMessage("Delete file5, add file folder6 and file7 in topic")
.call();
RevCommit conflicting = git.commit().setMessage(
"Delete file5, add file folder6 and file7 in topic").call();

RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
assertEquals(Status.STOPPED, res.getStatus());
@@ -429,8 +733,7 @@ public class RebaseCommandTest extends RepositoryTestCase {
assertEquals(res.getStatus(), Status.ABORTED);
assertEquals("refs/heads/topic", db.getFullBranch());
RevWalk rw = new RevWalk(db);
assertEquals(conflicting,
rw.parseCommit(db.resolve(Constants.HEAD)));
assertEquals(conflicting, rw.parseCommit(db.resolve(Constants.HEAD)));
assertEquals(RepositoryState.SAFE, db.getRepositoryState());

// rebase- dir in .git must be deleted
@@ -444,6 +747,62 @@ public class RebaseCommandTest extends RepositoryTestCase {

}

public void testAuthorScriptConverter() throws Exception {
// -1 h timezone offset
PersonIdent ident = new PersonIdent("Author name", "a.mail@some.com",
123456789123L, -60);
String convertedAuthor = git.rebase().toAuthorScript(ident);
String[] lines = convertedAuthor.split("\n");
assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]);
assertEquals("GIT_AUTHOR_DATE='123456789 -0100'", lines[2]);

PersonIdent parsedIdent = git.rebase().parseAuthor(
convertedAuthor.getBytes("UTF-8"));
assertEquals(ident.getName(), parsedIdent.getName());
assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
// this is rounded to the last second
assertEquals(123456789000L, parsedIdent.getWhen().getTime());
assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());

// + 9.5h timezone offset
ident = new PersonIdent("Author name", "a.mail@some.com",
123456789123L, +570);
convertedAuthor = git.rebase().toAuthorScript(ident);
lines = convertedAuthor.split("\n");
assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
assertEquals("GIT_AUTHOR_EMAIL='a.mail@some.com'", lines[1]);
assertEquals("GIT_AUTHOR_DATE='123456789 +0930'", lines[2]);

parsedIdent = git.rebase().parseAuthor(
convertedAuthor.getBytes("UTF-8"));
assertEquals(ident.getName(), parsedIdent.getName());
assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
assertEquals(123456789000L, parsedIdent.getWhen().getTime());
assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
}

public void testRepositoryStateChecks() throws Exception {
try {
git.rebase().setOperation(Operation.ABORT).call();
fail("Expected Exception not thrown");
} catch (WrongRepositoryStateException e) {
// expected
}
try {
git.rebase().setOperation(Operation.SKIP).call();
fail("Expected Exception not thrown");
} catch (WrongRepositoryStateException e) {
// expected
}
try {
git.rebase().setOperation(Operation.CONTINUE).call();
fail("Expected Exception not thrown");
} catch (WrongRepositoryStateException e) {
// expected
}
}

private int countPicks() throws IOException {
int count = 0;
File todoFile = new File(db.getDirectory(),

+ 2
- 0
org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties View File

@@ -69,6 +69,7 @@ cannotReadFile=Cannot read file {0}
cannotReadHEAD=cannot read HEAD: {0} {1}
cannotReadObject=Cannot read object
cannotReadTree=Cannot read tree {0}
cannotRebaseWithoutCurrentHead=Can not rebase without a current HEAD
cannotResolveLocalTrackingRefForUpdating=Cannot resolve local tracking ref {0} for updating.
cannotStoreObjects=cannot store objects
cannotUnloadAModifiedTree=Cannot unload a modified tree.
@@ -426,6 +427,7 @@ unknownRepositoryFormat=Unknown repository format
unknownZlibError=Unknown zlib error.
unpackException=Exception while parsing pack stream
unmergedPath=Unmerged path: {0}
unmergedPaths=Repository contains unmerged paths
unreadablePackIndex=Unreadable pack index: {0}
unrecognizedRef=Unrecognized ref: {0}
unsupportedCommand0=unsupported command 0

+ 2
- 0
org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java View File

@@ -129,6 +129,7 @@ public class JGitText extends TranslationBundle {
/***/ public String cannotReadHEAD;
/***/ public String cannotReadObject;
/***/ public String cannotReadTree;
/***/ public String cannotRebaseWithoutCurrentHead;
/***/ public String cannotResolveLocalTrackingRefForUpdating;
/***/ public String cannotStoreObjects;
/***/ public String cannotUnloadAModifiedTree;
@@ -485,6 +486,7 @@ public class JGitText extends TranslationBundle {
/***/ public String unknownRepositoryFormat;
/***/ public String unknownZlibError;
/***/ public String unmergedPath;
/***/ public String unmergedPaths;
/***/ public String unpackException;
/***/ public String unreadablePackIndex;
/***/ public String unrecognizedRef;

+ 217
- 49
org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java View File

@@ -47,6 +47,7 @@ import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -55,7 +56,9 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.api.RebaseResult.Status;
@@ -63,14 +66,18 @@ import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.UnmergedPathsException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
@@ -78,6 +85,8 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@@ -94,6 +103,40 @@ import org.eclipse.jgit.util.RawParseUtils;
* >Git documentation about Rebase</a>
*/
public class RebaseCommand extends GitCommand<RebaseResult> {
/**
* The name of the "rebase-merge" folder
*/
public static final String REBASE_MERGE = "rebase-merge";

/**
* The name of the "stopped-sha" file
*/
public static final String STOPPED_SHA = "stopped-sha";

private static final String AUTHOR_SCRIPT = "author-script";

private static final String DONE = "done";

private static final String GIT_AUTHOR_DATE = "GIT_AUTHOR_DATE";

private static final String GIT_AUTHOR_EMAIL = "GIT_AUTHOR_EMAIL";

private static final String GIT_AUTHOR_NAME = "GIT_AUTHOR_NAME";

private static final String GIT_REBASE_TODO = "git-rebase-todo";

private static final String HEAD_NAME = "head-name";

private static final String INTERACTIVE = "interactive";

private static final String MESSAGE = "message";

private static final String ONTO = "onto";

private static final String PATCH = "patch";

private static final String REBASE_HEAD = "head";

/**
* The available operations
*/
@@ -132,7 +175,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
protected RebaseCommand(Repository repo) {
super(repo);
walk = new RevWalk(repo);
rebaseDir = new File(repo.getDirectory(), "rebase-merge");
rebaseDir = new File(repo.getDirectory(), REBASE_MERGE);
}

/**
@@ -145,6 +188,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
*/
public RebaseResult call() throws NoHeadException, RefNotFoundException,
JGitInternalException, GitAPIException {
RevCommit newHead = null;
checkCallable();
checkParameters();
try {
@@ -158,7 +202,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
case SKIP:
// fall through
case CONTINUE:
String upstreamCommitName = readFile(rebaseDir, "onto");
String upstreamCommitName = readFile(rebaseDir, ONTO);
this.upstreamCommit = walk.parseCommit(repo
.resolve(upstreamCommitName));
break;
@@ -172,16 +216,13 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
return abort();

if (this.operation == Operation.CONTINUE)
throw new UnsupportedOperationException(
"--continue Not yet implemented");
newHead = continueRebase();

if (this.operation == Operation.SKIP)
throw new UnsupportedOperationException(
"--skip Not yet implemented");
List<Step> steps = loadSteps();

RevCommit newHead = null;
if (this.operation == Operation.SKIP && !steps.isEmpty())
checkoutCurrentHead();

List<Step> steps = loadSteps();
ObjectReader or = repo.newObjectReader();
int stepsToPop = 0;

@@ -211,16 +252,21 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
stepsToPop++;
}
if (newHead != null) {
if (newHead != null || steps.isEmpty()) {
// point the previous head (if any) to the new commit
String headName = readFile(rebaseDir, "head-name");
String headName = readFile(rebaseDir, HEAD_NAME);
if (headName.startsWith(Constants.R_REFS)) {
RefUpdate rup = repo.updateRef(headName);
rup.setNewObjectId(newHead);
rup.forceUpdate();
if (newHead != null) {
rup.setNewObjectId(newHead);
rup.forceUpdate();
}
rup = repo.updateRef(Constants.HEAD);
rup.link(headName);
}
if (this.operation == Operation.SKIP && steps.isEmpty()) {
checkoutCurrentHead();
}
FileUtils.delete(rebaseDir, FileUtils.RECURSIVE);
return new RebaseResult(Status.OK);
}
@@ -230,29 +276,118 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
}

private void checkoutCurrentHead() throws IOException, NoHeadException,
JGitInternalException {
ObjectId headTree = repo.resolve(Constants.HEAD + "^{tree}");
if (headTree == null)
throw new NoHeadException(
JGitText.get().cannotRebaseWithoutCurrentHead);
DirCache dc = repo.lockDirCache();
try {
DirCacheCheckout dco = new DirCacheCheckout(repo, dc, headTree);
dco.setFailOnConflict(false);
boolean needsDeleteFiles = dco.checkout();
if (needsDeleteFiles) {
List<String> fileList = dco.getToBeDeleted();
for (String filePath : fileList) {
File fileToDelete = new File(repo.getWorkTree(), filePath);
if (fileToDelete.exists())
FileUtils.delete(fileToDelete, FileUtils.RECURSIVE
| FileUtils.RETRY);
}
}
} finally {
dc.unlock();
}
}

/**
* @return the commit if we had to do a commit, otherwise null
* @throws GitAPIException
* @throws IOException
*/
private RevCommit continueRebase() throws GitAPIException, IOException {
// if there are still conflicts, we throw a specific Exception
DirCache dc = repo.readDirCache();
boolean hasUnmergedPaths = dc.hasUnmergedPaths();
if (hasUnmergedPaths)
throw new UnmergedPathsException();

// determine whether we need to commit
TreeWalk treeWalk = new TreeWalk(repo);
treeWalk.reset();
treeWalk.setRecursive(true);
treeWalk.addTree(new DirCacheIterator(dc));
ObjectId id = repo.resolve(Constants.HEAD + "^{tree}");
if (id == null)
throw new NoHeadException(
JGitText.get().cannotRebaseWithoutCurrentHead);

treeWalk.addTree(id);

treeWalk.setFilter(TreeFilter.ANY_DIFF);

boolean needsCommit = treeWalk.next();
treeWalk.release();

if (needsCommit) {
CommitCommand commit = new Git(repo).commit();
commit.setMessage(readFile(rebaseDir, MESSAGE));
commit.setAuthor(parseAuthor());
return commit.call();
}
return null;
}

private PersonIdent parseAuthor() throws IOException {
File authorScriptFile = new File(rebaseDir, AUTHOR_SCRIPT);
byte[] raw;
try {
raw = IO.readFully(authorScriptFile);
} catch (FileNotFoundException notFound) {
return null;
}
return parseAuthor(raw);
}

private RebaseResult stop(RevCommit commitToPick) throws IOException {
StringBuilder sb = new StringBuilder(100);
sb.append("GIT_AUTHOR_NAME='");
sb.append(commitToPick.getAuthorIdent().getName());
sb.append("'\n");
sb.append("GIT_AUTHOR_EMAIL='");
sb.append(commitToPick.getAuthorIdent().getEmailAddress());
sb.append("'\n");
sb.append("GIT_AUTHOR_DATE='");
sb.append(commitToPick.getAuthorIdent().getWhen());
sb.append("'\n");
createFile(rebaseDir, "author-script", sb.toString());
createFile(rebaseDir, "message", commitToPick.getShortMessage());
PersonIdent author = commitToPick.getAuthorIdent();
String authorScript = toAuthorScript(author);
createFile(rebaseDir, AUTHOR_SCRIPT, authorScript);
createFile(rebaseDir, MESSAGE, commitToPick.getFullMessage());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DiffFormatter df = new DiffFormatter(bos);
df.setRepository(repo);
df.format(commitToPick.getParent(0), commitToPick);
createFile(rebaseDir, "patch", new String(bos.toByteArray(), "UTF-8"));
createFile(rebaseDir, "stopped-sha", repo.newObjectReader().abbreviate(
createFile(rebaseDir, PATCH, new String(bos.toByteArray(),
Constants.CHARACTER_ENCODING));
createFile(rebaseDir, STOPPED_SHA, repo.newObjectReader().abbreviate(
commitToPick).name());
return new RebaseResult(commitToPick);
}

String toAuthorScript(PersonIdent author) {
StringBuilder sb = new StringBuilder(100);
sb.append(GIT_AUTHOR_NAME);
sb.append("='");
sb.append(author.getName());
sb.append("'\n");
sb.append(GIT_AUTHOR_EMAIL);
sb.append("='");
sb.append(author.getEmailAddress());
sb.append("'\n");
// the command line uses the "external String"
// representation for date and timezone
sb.append(GIT_AUTHOR_DATE);
sb.append("='");
String externalString = author.toExternalString();
sb
.append(externalString.substring(externalString
.lastIndexOf('>') + 2));
sb.append("'\n");
return sb.toString();
}

/**
* Removes the number of lines given in the parameter from the
* <code>git-rebase-todo</code> file but preserves comments and other lines
@@ -266,10 +401,10 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
return;
List<String> todoLines = new ArrayList<String>();
List<String> poppedLines = new ArrayList<String>();
File todoFile = new File(rebaseDir, "git-rebase-todo");
File doneFile = new File(rebaseDir, "done");
File todoFile = new File(rebaseDir, GIT_REBASE_TODO);
File doneFile = new File(rebaseDir, DONE);
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(todoFile), "UTF-8"));
new FileInputStream(todoFile), Constants.CHARACTER_ENCODING));
try {
// check if the line starts with a action tag (pick, skip...)
while (poppedLines.size() < numSteps) {
@@ -297,7 +432,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}

BufferedWriter todoWriter = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(todoFile), "UTF-8"));
new FileOutputStream(todoFile), Constants.CHARACTER_ENCODING));
try {
for (String writeLine : todoLines) {
todoWriter.write(writeLine);
@@ -311,7 +446,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
// append here
BufferedWriter doneWriter = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(doneFile, true), "UTF-8"));
new FileOutputStream(doneFile, true),
Constants.CHARACTER_ENCODING));
try {
for (String writeLine : poppedLines) {
doneWriter.write(writeLine);
@@ -363,14 +499,14 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
// create the folder for the meta information
rebaseDir.mkdir();

createFile(repo.getDirectory(), "ORIG_HEAD", headId.name());
createFile(rebaseDir, "head", headId.name());
createFile(rebaseDir, "head-name", headName);
createFile(rebaseDir, "onto", upstreamCommit.name());
createFile(rebaseDir, "interactive", "");
createFile(repo.getDirectory(), Constants.ORIG_HEAD, headId.name());
createFile(rebaseDir, REBASE_HEAD, headId.name());
createFile(rebaseDir, HEAD_NAME, headName);
createFile(rebaseDir, ONTO, upstreamCommit.name());
createFile(rebaseDir, INTERACTIVE, "");
BufferedWriter fw = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(new File(rebaseDir, "git-rebase-todo")),
"UTF-8"));
new FileOutputStream(new File(rebaseDir, GIT_REBASE_TODO)),
Constants.CHARACTER_ENCODING));
fw.write("# Created by EGit: rebasing " + upstreamCommit.name()
+ " onto " + headId.name());
fw.newLine();
@@ -404,13 +540,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
if (this.operation != Operation.BEGIN) {
// these operations are only possible while in a rebasing state
switch (repo.getRepositoryState()) {
case REBASING:
// fall through
case REBASING_INTERACTIVE:
// fall through
case REBASING_MERGE:
// fall through
case REBASING_REBASING:
break;
default:
throw new WrongRepositoryStateException(MessageFormat.format(
@@ -438,7 +568,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
File file = new File(parentDir, name);
FileOutputStream fos = new FileOutputStream(file);
try {
fos.write(content.getBytes("UTF-8"));
fos.write(content.getBytes(Constants.CHARACTER_ENCODING));
fos.write('\n');
} finally {
fos.close();
@@ -447,7 +577,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {

private RebaseResult abort() throws IOException {
try {
String commitId = readFile(repo.getDirectory(), "ORIG_HEAD");
String commitId = readFile(repo.getDirectory(), Constants.ORIG_HEAD);
monitor.beginTask(MessageFormat.format(
JGitText.get().abortingRebase, commitId),
ProgressMonitor.UNKNOWN);
@@ -463,7 +593,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
monitor.endTask();
}
try {
String headName = readFile(rebaseDir, "head-name");
String headName = readFile(rebaseDir, HEAD_NAME);
if (headName.startsWith(Constants.R_REFS)) {
monitor.beginTask(MessageFormat.format(
JGitText.get().resettingHead, headName),
@@ -527,7 +657,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}

private List<Step> loadSteps() throws IOException {
byte[] buf = IO.readFully(new File(rebaseDir, "git-rebase-todo"));
byte[] buf = IO.readFully(new File(rebaseDir, GIT_REBASE_TODO));
int ptr = 0;
int tokenBegin = 0;
ArrayList<Step> r = new ArrayList<Step>();
@@ -656,4 +786,42 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
this.action = action;
}
}

PersonIdent parseAuthor(byte[] raw) {
if (raw.length == 0)
return null;

Map<String, String> keyValueMap = new HashMap<String, String>();
for (int p = 0; p < raw.length;) {
int end = RawParseUtils.nextLF(raw, p);
if (end == p)
break;
int equalsIndex = RawParseUtils.next(raw, p, '=');
if (equalsIndex == end)
break;
String key = RawParseUtils.decode(raw, p, equalsIndex - 1);
String value = RawParseUtils.decode(raw, equalsIndex + 1, end - 2);
p = end;
keyValueMap.put(key, value);
}

String name = keyValueMap.get(GIT_AUTHOR_NAME);
String email = keyValueMap.get(GIT_AUTHOR_EMAIL);
String time = keyValueMap.get(GIT_AUTHOR_DATE);

// the time is saved as <seconds since 1970> <timezone offset>
long when = Long.parseLong(time.substring(0, time.indexOf(' '))) * 1000;
String tzOffsetString = time.substring(time.indexOf(' ') + 1);
int multiplier = -1;
if (tzOffsetString.charAt(0) == '+')
multiplier = 1;
int hours = Integer.parseInt(tzOffsetString.substring(1, 3));
int minutes = Integer.parseInt(tzOffsetString.substring(3, 5));
// this is in format (+/-)HHMM (hours and minutes)
// we need to convert into minutes
int tz = (hours * 60 + minutes) * multiplier;
if (name != null && email != null)
return new PersonIdent(name, email, when, tz);
return null;
}
}

+ 54
- 0
org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2010,Mathias Kinzler <mathias.kinzler@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;

import org.eclipse.jgit.JGitText;

/**
* Thrown when branch deletion fails due to unmerged data
*/
public class UnmergedPathsException extends GitAPIException {
private static final long serialVersionUID = 1L;

/**
* The default constructor with a default message
*/
public UnmergedPathsException() {
super(JGitText.get().unmergedPaths);
}
}

Loading…
Cancel
Save