/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.uberfire.java.nio.fs.jgit;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
import org.eclipse.jgit.api.RebaseResult;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.IllegalTodoFileModification;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RebaseTodoLine;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.uberfire.java.nio.fs.jgit.util.JGitUtil;
import org.uberfire.java.nio.fs.jgit.util.commands.Squash;
import org.uberfire.java.nio.fs.jgit.util.exceptions.GitException;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.fest.assertions.api.Assertions.fail;
import static org.uberfire.java.nio.fs.jgit.util.JGitUtil.PathType.DIRECTORY;
import static org.uberfire.java.nio.fs.jgit.util.JGitUtil.PathType.FILE;
import static org.uberfire.java.nio.fs.jgit.util.JGitUtil.PathType.NOT_FOUND;
import static org.uberfire.java.nio.fs.jgit.util.JGitUtil.cloneRepository;
import static org.uberfire.java.nio.fs.jgit.util.JGitUtil.commit;
import static org.uberfire.java.nio.fs.jgit.util.JGitUtil.resolveObjectId;
public class JGitSquashingTest extends AbstractTestInfra {
static {
CredentialsProvider.setDefault(new UsernamePasswordCredentialsProvider("guest",
""));
}
private Logger logger = LoggerFactory.getLogger(JGitSquashingTest.class);
/*
* The following test shows how to do a rebase with Fixup to squash a set of commits
* Notice that RebaseCommand only works on non-BARE repos that's why I need to clone
* the original repo using the BARE repo flag set to false
* This initial test is here to demonstrate what we need to achieve on the BARE repo
*/
@Test
public void rawRebaseWithFixUp() throws IOException, GitAPIException {
logger.info(">>>>>>>>>>>>>>>>>>> rawRebaseWithFixUp");
final File parentFolder = createTempDirectory();
final File gitFolder = new File(parentFolder,
"myrepo.git");
final Git origin = JGitUtil.newRepository(gitFolder,
true);
final File gitClonedFolder = new File(parentFolder,
"myclone.git");
final Git clone = cloneRepository(gitClonedFolder,
origin.getRepository().getDirectory().toString(),
false,
CredentialsProvider.getDefault());
createAddAndCommitFile(clone,
"testfile0");
Iterable<RevCommit> logs = clone.log().all().setMaxCount(1).call();
Iterator<RevCommit> iterator = logs.iterator();
assertThat(iterator.hasNext()).isTrue();
RevCommit firstCommit = iterator.next();
createAddAndCommitFile(clone,
"testfile1");
createAddAndCommitFile(clone,
"testfile2");
createAddAndCommitFile(clone,
"testfile3");
logs = clone.log().all().setMaxCount(1).call();
iterator = logs.iterator();
assertThat(iterator.hasNext()).isTrue();
final RevCommit thirdCommit = iterator.next();
createAddAndCommitFile(clone,
"testfile4");
final String squashedCommitMessage = "I'm here to squash some changes";
final RevCommit lastSquashedCommit = thirdCommit;
InteractiveHandler handler = new InteractiveHandler() {
public void prepareSteps(List<RebaseTodoLine> steps) {
try {
int counter = 0;
for (RebaseTodoLine step : steps) {
if (counter == 0) {
step.setAction(RebaseTodoLine.Action.PICK);
} else {
step.setAction(RebaseTodoLine.Action.SQUASH);
}
if (step.getCommit().prefixCompare(lastSquashedCommit) == 0) {
break;
}
counter++;
}
} catch (IllegalTodoFileModification ex) {
logger.error(ex.getLocalizedMessage(),
ex);
}
}
@Override
public String modifyCommitMessage(String oldMessage) {
return squashedCommitMessage;
}
};
logger.info("#### Before Rebase");
int counter = 0;
for (RevCommit commit : clone.log().all().call()) {
logger.info(">Commit: " + commit.getFullMessage());
counter++;
}
logger.info("#### Before Rebase Commits: " + counter);
RebaseResult rebaseResult = clone.rebase().setUpstream(firstCommit).runInteractively(handler).call();
assertThat(rebaseResult.getStatus()).isSameAs(RebaseResult.Status.OK);
logger.info("#### After Rebase");
counter = 0;
for (RevCommit commit : clone.log().all().call()) {
logger.info(">Commit: " + commit.getFullMessage());
counter++;
}
logger.info("#### After Rebase Commits: " + counter);
logger.info(">>>>>>>>>>>>>>>>>>> END rawRebaseWithFixUp");
}
/*
* This test make 5 commits and then squah the last 4 into a single commit
*/
@Test
public void testSquash4Of5Commits() throws IOException, GitAPIException {
final File parentFolder = createTempDirectory();
logger.info(">> Parent Forlder for the Test: " + parentFolder.getAbsolutePath());
final File gitFolder = new File(parentFolder,
"my-local-repo.git");
final Git origin = JGitUtil.newRepository(gitFolder,
true);
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 1!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file1.txt",
tempFile("initial content file 1"));
}
});
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 2!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file2.txt",
tempFile("initial content file 2"));
}
});
Iterable<RevCommit> logs = origin.log().setMaxCount(1).all().call();
RevCommit secondCommit = logs.iterator().next();
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 3!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file1.txt",
tempFile("new content file 1"));
}
});
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 4!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file2.txt",
tempFile("new content file 2"));
}
});
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 5!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file3.txt",
tempFile("initial content file 3"));
}
});
logs = origin.log().all().call();
int commitsCount = 0;
for (RevCommit commit : logs) {
logger.info(">>> Origin Commit: " + commit.getFullMessage() + " - " + commit.toString());
commitsCount++;
}
assertThat(commitsCount).isEqualTo(5);
assertThat(JGitUtil.checkPath(origin,
"master",
"pathx/").getK1()).isEqualTo(NOT_FOUND);
assertThat(JGitUtil.checkPath(origin,
"master",
"path/to/file1.txt").getK1()).isEqualTo(FILE);
assertThat(JGitUtil.checkPath(origin,
"master",
"path/to/file2.txt").getK1()).isEqualTo(FILE);
assertThat(JGitUtil.checkPath(origin,
"master",
"path/to/file3.txt").getK1()).isEqualTo(FILE);
assertThat(JGitUtil.checkPath(origin,
"master",
"path/to").getK1()).isEqualTo(DIRECTORY);
logger.info("Squashing from " + secondCommit.getName() + " to HEAD");
new Squash(origin,
"master",
secondCommit.getName(),
"squashed message").execute();
commitsCount = 0;
for (RevCommit commit : origin.log().all().call()) {
logger.info(">>> Final Commit: " + commit.getFullMessage() + " - " + commit.toString());
commitsCount++;
}
assertThat(commitsCount).isEqualTo(2);
}
@Test
public void testFailWhenTryToSquashCommitsFromDifferentBranches() throws IOException, GitAPIException {
final File parentFolder = createTempDirectory();
logger.info(">> Parent Forlder for the Test: " + parentFolder.getAbsolutePath());
final File gitFolder = new File(parentFolder,
"my-local-repo.git");
final Git origin = JGitUtil.newRepository(gitFolder,
true);
commit(origin,
"master",
"aparedes",
"aparedes@example.com",
"commit 1!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file1.txt",
tempFile("initial content file 1"));
}
});
commit(origin,
"develop",
"salaboy",
"salaboy@example.com",
"commit 2!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file2.txt",
tempFile("initial content file 2"));
}
});
commit(origin,
"master",
"aparedes",
"aparedes@example.com",
"commit 3!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file3.txt",
tempFile("initial content file 1"));
}
});
commit(origin,
"master",
"aparedes",
"aparedes@example.com",
"commit 4!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file4.txt",
tempFile("initial content file 1"));
}
});
List<RevCommit> masterCommits = getCommitsFromBranch(origin,
"master");
List<RevCommit> developCommits = getCommitsFromBranch(origin,
"develop");
assertThat(masterCommits.size()).isEqualTo(3);
assertThat(developCommits.size()).isEqualTo(1);
try {
new Squash(origin,
"master",
developCommits.get(0).getName(),
"squashed message").execute();
fail("If it reaches here the test has failed because he found the commit into the branch");
} catch (GitException e) {
logger.info(e.getMessage());
assertThat(e).isNotNull();
}
}
private List<RevCommit> getCommitsFromBranch(final Git origin,
String branch) throws GitAPIException, MissingObjectException, IncorrectObjectTypeException {
List<RevCommit> commits = new ArrayList<>();
final ObjectId id = resolveObjectId(origin,
branch);
for (RevCommit commit : origin.log().add(id).call()) {
logger.info(">>> " + branch + " Commits: " + commit.getFullMessage() + " - " + commit.toString());
commits.add(commit);
}
return commits;
}
/*
* This test also perform 5 commits and squash the last 4 into a single commit
* but now the changes are in different paths
*/
@Test
public void testSquashCommitsWithDifferentPaths() throws IOException, GitAPIException {
final File parentFolder = createTempDirectory();
logger.info(">> Parent Folder for the Test: " + parentFolder.getAbsolutePath());
final File gitFolder = new File(parentFolder,
"my-local-repo.git");
final Git origin = JGitUtil.newRepository(gitFolder,
true);
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 1!",
null,
null,
false,
new HashMap<String, File>() {
{
put("file1.txt",
tempFile("initial content file 1"));
}
});
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 2!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file2.txt",
tempFile("initial content file 2"));
}
});
Iterable<RevCommit> logs = origin.log().setMaxCount(1).all().call();
RevCommit secondCommit = logs.iterator().next();
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 3!",
null,
null,
false,
new HashMap<String, File>() {
{
put("file1.txt",
tempFile("new content file 1"));
}
});
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 4!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/to/file2.txt",
tempFile("new content file 2"));
}
});
commit(origin,
"master",
"salaboy",
"salaboy@example.com",
"commit 5!",
null,
null,
false,
new HashMap<String, File>() {
{
put("path/file3.txt",
tempFile("initial content file 3"));
}
});
for (RevCommit commit : origin.log().all().call()) {
logger.info(">>> Origin Commit: " + commit.getFullMessage() + " - " + commit.toString());
}
assertThat(JGitUtil.checkPath(origin,
"master",
"pathx/").getK1()).isEqualTo(NOT_FOUND);
assertThat(JGitUtil.checkPath(origin,
"master",
"file1.txt").getK1()).isEqualTo(FILE);
assertThat(JGitUtil.checkPath(origin,
"master",
"path/to/file2.txt").getK1()).isEqualTo(FILE);
assertThat(JGitUtil.checkPath(origin,
"master",
"path/file3.txt").getK1()).isEqualTo(FILE);
assertThat(JGitUtil.checkPath(origin,
"master",
"path/to").getK1()).isEqualTo(DIRECTORY);
logger.info("Squashing from " + secondCommit.getName() + " to HEAD");
new Squash(origin,
"master",
secondCommit.getName(),
"squashed message").execute();
int commitsCount = 0;
for (RevCommit commit : origin.log().all().call()) {
logger.info(">>> Final Commit: " + commit.getFullMessage() + " - " + commit.toString());
commitsCount++;
}
assertThat(commitsCount).isEqualTo(2);
}
@Test(expected = IllegalStateException.class)
public void repositoryIsBareTest() throws IOException {
final File parentFolder = createTempDirectory();
final File gitFolder = new File(parentFolder,
"myrepo.git");
final Git origin = JGitUtil.newRepository(gitFolder,
false);
new Squash(origin,
"master",
null,
"squashed message").execute();
}
private void createAddAndCommitFile(Git git,
String file) throws GitAPIException, IOException {
File myfile = new File(git.getRepository().getDirectory().getParent(),
file);
myfile.createNewFile();
git.add()
.addFilepattern(file)
.call();
git.commit()
.setMessage("Added " + file)
.call();
}
}