package com.atlassian.jgitflow.core;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Callable;
import com.atlassian.jgitflow.core.exception.*;
import com.atlassian.jgitflow.core.util.GitHelper;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import static com.atlassian.jgitflow.core.util.Preconditions.checkNotNull;
/**
* The base class for all JGitFlow commands.
* <p>
* Most commands should extend this class as it provides common helper methods
* and methods to ensure valid state.
* </p>
* @param <T> The return type of the call() method
*/
public abstract class AbstractGitFlowCommand<T> implements Callable<T>
{
protected final Git git;
protected final GitFlowConfiguration gfConfig;
protected AbstractGitFlowCommand(Git git, GitFlowConfiguration gfConfig)
{
checkNotNull(git);
checkNotNull(gfConfig);
this.git = git;
this.gfConfig = gfConfig;
}
/**
* Requires that git flow has been initialized for the project represented by the internal {Git} instance
* @throws NotInitializedException
* @throws JGitFlowGitAPIException
*/
protected void requireGitFlowInitialized() throws NotInitializedException, JGitFlowGitAPIException
{
if (!gfConfig.gitFlowIsInitialized())
{
throw new NotInitializedException("Git flow is not initialized in " + git.getRepository().getWorkTree().getPath());
}
}
/**
* Requires that a local branch with the given name does not yet exist
* @param branch the name of the branch to test
* @throws LocalBranchExistsException
* @throws JGitFlowGitAPIException
*/
protected void requireLocalBranchAbsent(String branch) throws LocalBranchExistsException, JGitFlowGitAPIException
{
if (GitHelper.localBranchExists(git, branch))
{
throw new LocalBranchExistsException("local branch '" + branch + "' already exists");
}
}
/**
* Requires that a local branch with the given name exists
* @param branch The name of the branch to test
* @throws LocalBranchMissingException
* @throws JGitFlowGitAPIException
*/
protected void requireLocalBranchExists(String branch) throws LocalBranchMissingException, JGitFlowGitAPIException
{
if (!GitHelper.localBranchExists(git, branch))
{
throw new LocalBranchMissingException("local branch " + branch + " does not exist");
}
}
/**
* Requires that a remote branch with the given name does not yet exist
* @param branch The name of the branch to test
* @throws RemoteBranchExistsException
* @throws JGitFlowGitAPIException
*/
protected void requireRemoteBranchAbsent(String branch) throws RemoteBranchExistsException, JGitFlowGitAPIException
{
if (GitHelper.remoteBranchExists(git, branch))
{
throw new RemoteBranchExistsException("remote branch '" + branch + "' already exists");
}
}
/**
* Requires that a remote branch with the given name exists
* @param branch The name of the branch to test
* @throws RemoteBranchMissingException
* @throws JGitFlowGitAPIException
*/
protected void requireRemoteBranchExists(String branch) throws RemoteBranchMissingException, JGitFlowGitAPIException
{
if (!GitHelper.remoteBranchExists(git, branch))
{
throw new RemoteBranchMissingException("remote branch " + branch + " does not exist");
}
}
/**
* Requires that a tag with the given name does not yet exist
* @param name The name of the tag to test
* @throws TagExistsException
* @throws JGitFlowGitAPIException
*/
protected void requireTagAbsent(String name) throws TagExistsException, JGitFlowGitAPIException
{
if (GitHelper.tagExists(git, name))
{
throw new TagExistsException("tag '" + name + "' already exists");
}
}
/**
* Requires that the local branch with the given name is not behind a remote brach with the same name
* @param branch The name of the branch to test
* @throws BranchOutOfDateException
* @throws JGitFlowIOException
*/
protected void requireLocalBranchNotBehindRemote(String branch) throws BranchOutOfDateException, JGitFlowIOException
{
final RevWalk walk = new RevWalk(git.getRepository());
walk.setRetainBody(true);
boolean behind = false;
try
{
Ref remote = GitHelper.getRemoteBranch(git, branch);
Ref local = GitHelper.getLocalBranch(git, branch);
checkNotNull(remote);
checkNotNull(local);
ObjectId remoteId = git.getRepository().resolve(remote.getObjectId().getName());
RevCommit remoteCommit = walk.parseCommit(remoteId);
RevCommit localCommit = walk.parseCommit(local.getObjectId());
if (!localCommit.equals(remoteCommit))
{
behind = true;
walk.setRevFilter(RevFilter.MERGE_BASE);
walk.markStart(localCommit);
walk.markStart(remoteCommit);
RevCommit base = walk.next();
if (null != base)
{
walk.parseBody(base);
//remote is behind
if (remoteCommit.equals(base))
{
behind = false;
}
}
}
}
catch (IOException e)
{
throw new JGitFlowIOException(e);
}
finally
{
walk.release();
}
if (behind)
{
throw new BranchOutOfDateException("local branch '" + branch + "' is behind the remote branch");
}
}
/**
* Requires that the local working tree has no un-committed changes
* @throws DirtyWorkingTreeException
* @throws JGitFlowIOException
* @throws JGitFlowGitAPIException
*/
protected void requireCleanWorkingTree() throws DirtyWorkingTreeException, JGitFlowIOException, JGitFlowGitAPIException
{
if (!GitHelper.workingTreeIsClean(git))
{
throw new DirtyWorkingTreeException("Working tree has uncommitted changes");
}
}
/**
* Requires that no release branches already exist
* @throws ReleaseBranchExistsException
* @throws JGitFlowGitAPIException
*/
protected void requireNoExistingReleaseBranches() throws ReleaseBranchExistsException, JGitFlowGitAPIException
{
List<Ref> branches = GitHelper.listBranchesWithPrefix(git, JGitFlowConstants.PREFIXES.RELEASE.configKey());
if (!branches.isEmpty())
{
throw new ReleaseBranchExistsException("a release branch [" + branches.get(0).getName() + "] already exists. Finish that first!");
}
}
/**
* Requires that no hotfix branches already exist
* @throws HotfixBranchExistsException
* @throws JGitFlowGitAPIException
*/
protected void requireNoExistingHotfixBranches() throws HotfixBranchExistsException, JGitFlowGitAPIException
{
List<Ref> branches = GitHelper.listBranchesWithPrefix(git, JGitFlowConstants.PREFIXES.HOTFIX.configKey());
if (!branches.isEmpty())
{
throw new HotfixBranchExistsException("a hotfix branch [" + branches.get(0).getName() + "] already exists. Finish that first!");
}
}
}