package org.springframework.roo.addon.git;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.PushResult;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.support.util.FileUtils;
/**
* Operations for Git addon.
*
* @author Stefan Schmidt
* @since 1.1
*/
@Component
@Service
public class GitOperationsImpl implements GitOperations {
private static final Logger LOGGER = Logger
.getLogger(GitOperationsImpl.class.getName());
private static final String REVISION_STRING_DELIMITER = "~";
@Reference private FileManager fileManager;
@Reference private PathResolver pathResolver;
private PersonIdent person;
public void commitAllChanges(final String message) {
final Repository repository = getRepository();
try {
final Git git = new Git(repository);
git.add().addFilepattern(".").call();
final Status status = git.status().call();
if (status.getChanged().size() > 0 || status.getAdded().size() > 0
|| status.getModified().size() > 0
|| status.getRemoved().size() > 0) {
final RevCommit rev = git.commit().setAll(true)
.setCommitter(person).setAuthor(person)
.setMessage(message).call();
LOGGER.info("Git commit " + rev.getName() + " [" + message
+ "]");
}
}
catch (final Exception e) {
throw new IllegalStateException(
"Could not commit changes to local Git repository", e);
}
}
private RevCommit findCommit(final String revstr,
final Repository repository) {
final RevWalk walk = new RevWalk(repository);
RevCommit commit = null;
try {
commit = walk.parseCommit(repository.resolve(revstr));
}
catch (final MissingObjectException e1) {
LOGGER.warning("Could not find commit with id: " + revstr);
}
catch (final IncorrectObjectTypeException e1) {
LOGGER.warning("The provided rev is not a commit: " + revstr);
}
catch (final Exception ignore) {
}
finally {
walk.release();
}
return commit;
}
private Repository getRepository() {
if (hasDotGit()) {
try {
final String repositoryPath = pathResolver
.getFocusedIdentifier(Path.ROOT, Constants.DOT_GIT);
return new FileRepositoryBuilder().readEnvironment()
.findGitDir(new File(repositoryPath)).build();
}
catch (final IOException e) {
throw new IllegalStateException(e);
}
}
throw new IllegalStateException("Git support not available");
}
private boolean hasDotGit() {
return fileManager.exists(pathResolver.getFocusedIdentifier(Path.ROOT,
Constants.DOT_GIT));
}
public boolean isAutomaticCommit() {
return getRepository().getConfig().getBoolean("roo", "automaticCommit",
true);
}
public boolean isGitCommandAvailable() {
return hasDotGit();
}
public boolean isGitInstallationPossible() {
return !hasDotGit();
}
public void log(final int maxHistory) {
final Repository repository = getRepository();
final Git git = new Git(repository);
try {
int counter = 0;
LOGGER.warning("---------- Start Git log ----------");
for (final RevCommit commit : git.log().call()) {
LOGGER.info("commit id: " + commit.getName());
LOGGER.info("message: " + commit.getFullMessage());
LOGGER.info("");
if (++counter >= maxHistory) {
break;
}
}
LOGGER.warning("---------- End Git log ----------");
}
catch (final Exception e) {
throw new IllegalStateException("Could not parse git log", e);
}
}
public void push() {
final Git git = new Git(getRepository());
try {
for (final PushResult result : git.push().setPushAll().call()) {
LOGGER.info(result.getMessages());
}
}
catch (final Exception e) {
throw new IllegalStateException(
"Unable to perform push operation ", e);
}
}
public void reset(final int noOfCommitsToRevert, final String message) {
final Repository repository = getRepository();
final RevCommit commit = findCommit(Constants.HEAD
+ REVISION_STRING_DELIMITER + noOfCommitsToRevert, repository);
if (commit == null) {
return;
}
try {
final Git git = new Git(repository);
git.reset().setRef(commit.getName()).setMode(ResetType.HARD).call();
// Commit changes
commitAllChanges(message);
LOGGER.info("Reset of last " + (noOfCommitsToRevert + 1)
+ " successful.");
}
catch (final Exception e) {
throw new IllegalStateException("Reset did not succeed.", e);
}
}
public void revertCommit(final String revstr, final String message) {
final Repository repository = getRepository();
final RevCommit commit = findCommit(revstr, repository);
if (commit == null) {
return;
}
try {
final Git git = new Git(repository);
git.revert().include(commit).call();
// Commit changes
commitAllChanges(message);
LOGGER.info("Revert of commit " + revstr + " successful.");
}
catch (final Exception e) {
throw new IllegalStateException("Revert of commit " + revstr
+ " did not succeed.", e);
}
}
public void revertLastCommit(final String message) {
revertCommit(Constants.HEAD + REVISION_STRING_DELIMITER + "0", message);
}
public void setConfig(final String category, final String key,
final String value) {
final Repository repository = getRepository();
try {
repository.getConfig().setString(category, null, key, value);
repository.getConfig().save();
}
catch (final IOException ex) {
throw new IllegalStateException(
"Could not initialize Git repository", ex);
}
}
public void setup() {
if (hasDotGit()) {
LOGGER.info("Git is already configured");
return;
}
if (person == null) {
person = new PersonIdent("Roo Git Add-On", "s2-roo@vmware.com");
}
try {
final String repositoryPath = pathResolver.getFocusedIdentifier(
Path.ROOT, Constants.DOT_GIT);
final Repository repository = new FileRepositoryBuilder()
.readEnvironment().setGitDir(new File(repositoryPath))
.build();
repository.create();
}
catch (final Exception e) {
throw new IllegalStateException(
"Could not initialize Git repository", e);
}
setConfig("user", "name", person.getName());
setConfig("user", "email", person.getEmailAddress());
setConfig("remote \"origin\"", "fetch",
"+refs/heads/*:refs/remotes/origin/*");
setConfig("branch \"master\"", "remote", "origin");
setConfig("branch \"master\"", "merge", "refs/heads/master");
final String gitIgnore = pathResolver.getFocusedIdentifier(Path.ROOT,
Constants.GITIGNORE_FILENAME);
if (!fileManager.exists(gitIgnore)) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = FileUtils.getInputStream(getClass(),
"gitignore-template");
outputStream = fileManager.createFile(gitIgnore)
.getOutputStream();
IOUtils.copy(inputStream, outputStream);
}
catch (final IOException e) {
throw new IllegalStateException("Could not install "
+ Constants.GITIGNORE_FILENAME + " file in project", e);
}
finally {
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
}
}
}