package com.beijunyi.parallelgit.utils;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.beijunyi.parallelgit.utils.exceptions.BranchAlreadyExistsException;
import com.beijunyi.parallelgit.utils.exceptions.NoSuchBranchException;
import com.beijunyi.parallelgit.utils.exceptions.RefUpdateValidator;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.revwalk.*;
import static com.beijunyi.parallelgit.utils.CommitUtils.getCommit;
import static com.beijunyi.parallelgit.utils.RefUtils.fullBranchName;
import static org.eclipse.jgit.lib.Constants.*;
import static org.eclipse.jgit.lib.ObjectId.zeroId;
public final class BranchUtils {
@Nonnull
public static List<RevCommit> getHistory(String name, Repository repo) throws IOException {
Ref branchRef = repo.exactRef(fullBranchName(name));
if(branchRef == null)
throw new NoSuchBranchException(name);
RevCommit head = getCommit(branchRef, repo);
return CommitUtils.getHistory(head, repo);
}
@Nonnull
public static Map<String, Ref> getBranches(Repository repo) throws IOException {
return repo.getRefDatabase().getRefs(R_HEADS);
}
public static boolean branchExists(String name, Repository repo) throws IOException {
Ref ref = repo.exactRef(fullBranchName(name));
return ref != null;
}
@Nonnull
public static RevCommit getHeadCommit(String name, Repository repo) throws IOException {
Ref ref = RefUtils.getBranchRef(name, repo);
return getCommit(ref.getObjectId(), repo);
}
public static void createBranch(String name, RevTag startPoint, Repository repo) throws IOException {
createBranch(name, startPoint.getObject(), repo, "tag " + startPoint.getName());
}
public static void createBranch(String name, RevCommit startPoint, Repository repo) throws IOException {
createBranch(name, startPoint, repo, "commit " + startPoint.getShortMessage());
}
public static void createBranch(String name, AnyObjectId startPoint, Repository repo) throws IOException {
try(RevWalk rw = new RevWalk(repo)) {
RevObject revObj = rw.parseAny(startPoint);
switch(revObj.getType()) {
case OBJ_TAG:
createBranch(name, (RevTag) revObj, repo);
break;
case OBJ_COMMIT:
createBranch(name, (RevCommit) revObj, repo);
break;
default:
throw new UnsupportedOperationException(revObj.getName());
}
}
}
public static void createBranch(String name, Ref startPoint, Repository repo) throws IOException {
if(RefUtils.isBranchRef(startPoint))
createBranch(name, startPoint.getObjectId(), repo, "branch " + startPoint.getName());
else if(RefUtils.isTagRef(startPoint))
createBranch(name, startPoint.getObjectId(), repo);
else
throw new UnsupportedOperationException(startPoint.getName());
}
public static void createBranch(String name, String startPoint, Repository repo) throws IOException {
Ref ref = repo.findRef(startPoint);
if(ref != null)
createBranch(name, ref, repo);
else {
RevCommit commit = getCommit(startPoint, repo);
createBranch(name, commit, repo);
}
}
public static void resetBranchHead(String name, AnyObjectId commitId, Repository repo) throws IOException {
setBranchHead(name, commitId, repo, makeRefLogMessage(fullBranchName(name), "updating HEAD"), true);
}
public static void newCommit(String name, AnyObjectId commitId, Repository repo) throws IOException {
setBranchHead(name, commitId, repo, makeRefLogMessage("commit", commitId, repo), false);
}
public static void amendCommit(String name, AnyObjectId commitId, Repository repo) throws IOException {
setBranchHead(name, commitId, repo, makeRefLogMessage("commit (amend)", commitId, repo), true);
}
public static void mergeCommit(String name, AnyObjectId commitId, Repository repo) throws IOException {
setBranchHead(name, commitId, repo, makeRefLogMessage("commit (merge)", commitId, repo), false);
}
public static void initBranch(String name, AnyObjectId commitId, Repository repo) throws IOException {
setBranchHead(name, commitId, repo, makeRefLogMessage("commit (initial)", commitId, repo), false);
}
public static void cherryPick(String name, AnyObjectId commitId, Repository repo) throws IOException {
setBranchHead(name, commitId, repo, makeRefLogMessage("cherry-pick", commitId, repo), false);
}
public static void merge(String name, AnyObjectId commitId, Ref sourceRef, String details, Repository repo) throws IOException {
setBranchHead(name, commitId, repo, makeRefLogMessage("merge " + sourceRef.getName(), details), false);
}
public static void deleteBranch(String name, Repository repo) throws IOException {
String refName = fullBranchName(name);
if(prepareDeleteBranch(refName, repo)) {
RefUpdate update = repo.updateRef(refName);
update.setRefLogMessage("branch deleted", false);
update.setForceUpdate(true);
RefUpdateValidator.validate(update.delete());
}
}
@Nonnull
public static List<ReflogEntry> getLogs(String name, int max, Repository repository) throws IOException{
return RefUtils.getRefLogs(fullBranchName(name), max, repository);
}
@Nonnull
public static List<ReflogEntry> getLogs(String name, Repository repository) throws IOException{
return RefUtils.getRefLogs(fullBranchName(name), Integer.MAX_VALUE, repository);
}
@Nullable
public static ReflogEntry getLastLog(String name, Repository repository) throws IOException {
List<ReflogEntry> entries = getLogs(name, 1, repository);
if(entries.isEmpty())
return null;
return entries.get(0);
}
private static void setBranchHead(String name, AnyObjectId commitId, Repository repo, @Nullable String refLogMessage, boolean forceUpdate) throws IOException {
String refName = fullBranchName(name);
AnyObjectId currentHead = repo.resolve(refName);
if(currentHead == null)
currentHead = zeroId();
RefUpdate update = repo.updateRef(refName);
update.setRefLogMessage(refLogMessage, false);
update.setForceUpdate(forceUpdate);
update.setNewObjectId(commitId);
update.setExpectedOldObjectId(currentHead);
RefUpdateValidator.validate(update.update());
}
private static void createBranch(String name, AnyObjectId startPoint, Repository repo, String startPointName) throws IOException {
String branchRef = fullBranchName(name);
if(branchExists(branchRef, repo))
throw new BranchAlreadyExistsException(branchRef);
setBranchHead(name, startPoint, repo, makeRefLogMessage("branch", "Created from " + startPointName), false);
}
private static boolean prepareDeleteBranch(String refName, Repository repo) throws IOException {
boolean branchExists = branchExists(refName, repo);
if(refName.equals(repo.getFullBranch())) {
if(branchExists)
RepositoryUtils.detachRepositoryHead(repo, repo.resolve(refName));
else
return false;
} else if(!branchExists)
throw new NoSuchBranchException(refName);
return true;
}
@Nonnull
private static String makeRefLogMessage(String action, String details) {
return action + ": " + details;
}
@Nonnull
private static String makeRefLogMessage(String action, AnyObjectId commit, Repository repo) throws IOException {
return makeRefLogMessage(action, getCommit(commit, repo).getShortMessage());
}
}