package apidiff.internal.service.git; import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jgit.api.CheckoutCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryBuilder; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalkUtils; import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.transport.FetchResult; import org.eclipse.jgit.transport.TrackingRefUpdate; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import apidiff.internal.util.UtilTools; public class GitServiceImpl implements GitService { private static final String REMOTE_REFS_PREFIX = "refs/remotes/origin/"; private RevFilterCommitValid commitsFilter = new RevFilterCommitValid(); private Logger logger = LoggerFactory.getLogger(GitServiceImpl.class); private static final Long MINUTE = 60000L; //60.000 miliseconds private static final Long HOUR = MINUTE * 60; private static final Long DAY = HOUR * 24; private static final Long SEVEN_DAYS = DAY * 7; //7 dias em milissegundos. private class RevFilterCommitValid extends RevFilter { @Override public final boolean include(final RevWalk walker, final RevCommit c) { Long diffTimestamp = 0L; diffTimestamp = this.calcDiffTimeCommit(c); if(c.getParentCount() > 1){//merge logger.info("Merge of the branches deleted. [commitId=" + c.getId().getName() + "]"); return false; } //TODO: create other filter to date. // if(diffTimestamp > SEVEN_DAYS){//old // logger.info("Old commit old deleted. [commitId=" + c.getId().getName() + "][date=" + getDateCommitFormat(c) + "]"); // return false; // } return true; } private Long calcDiffTimeCommit(final RevCommit c){ Long timestampNow = Calendar.getInstance().getTimeInMillis(); Long timestampCommit = c.getAuthorIdent().getWhen().getTime(); Calendar calendarCommit = Calendar.getInstance(); calendarCommit.setTime(new Date(timestampCommit)); return Math.abs(timestampCommit - timestampNow); } private String getDateCommitFormat(final RevCommit c){ Long timestampCommit = c.getAuthorIdent().getWhen().getTime(); Calendar calendarCommit = Calendar.getInstance(); calendarCommit.setTime(new Date(timestampCommit)); SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss"); return format.format(timestampCommit); } @Override public final RevFilter clone() { return this; } @Override public final boolean requiresCommitBody() { return false; } @Override public String toString() { return "RegularCommitsFilter"; } } @Override public Repository openRepositoryAndCloneIfNotExists(String path, String projectName, String cloneUrl) throws Exception { File folder = new File(UtilTools.getPathProject(path , projectName)); Repository repository = null; if (folder.exists()) { this.logger.info(projectName + " exists. Reading properties ... (wait)"); RepositoryBuilder builder = new RepositoryBuilder(); repository = builder .setGitDir(new File(folder, ".git")) .readEnvironment() .findGitDir() .build(); } else { this.logger.info("Cloning " + cloneUrl + " in " + cloneUrl + " ... (wait)"); Git git = Git.cloneRepository() .setDirectory(folder) .setURI(cloneUrl) .setCloneAllBranches(true) .call(); repository = git.getRepository(); } this.logger.info("Process " + projectName + " finish."); return repository; } @Override public RevWalk fetchAndCreateNewRevsWalk(Repository repository, String branch) throws Exception { List<ObjectId> currentRemoteRefs = new ArrayList<ObjectId>(); for (Ref ref : repository.getAllRefs().values()) { String refName = ref.getName(); if (refName.startsWith(REMOTE_REFS_PREFIX)) { currentRemoteRefs.add(ref.getObjectId()); } } List<TrackingRefUpdate> newRemoteRefs = this.fetch(repository); RevWalk walk = new RevWalk(repository); for (TrackingRefUpdate newRef : newRemoteRefs) { if (branch == null || newRef.getLocalName().endsWith("/" + branch)) { walk.markStart(walk.parseCommit(newRef.getNewObjectId())); } } for (ObjectId oldRef : currentRemoteRefs) { walk.markUninteresting(walk.parseCommit(oldRef)); } walk.setRevFilter(commitsFilter); return walk; } public RevWalk createAllRevsWalk(Repository repository, String branch) throws Exception { List<ObjectId> currentRemoteRefs = new ArrayList<ObjectId>(); for (Ref ref : repository.getAllRefs().values()) { String refName = ref.getName(); if (refName.startsWith(REMOTE_REFS_PREFIX)) { if (branch == null || refName.endsWith("/" + branch)) { currentRemoteRefs.add(ref.getObjectId()); } } } RevWalk walk = new RevWalk(repository); for (ObjectId newRef : currentRemoteRefs) { walk.markStart(walk.parseCommit(newRef)); } walk.setRevFilter(commitsFilter); return walk; } private List<TrackingRefUpdate> fetch(Repository repository) throws Exception { this.logger.info("Fetching changes of repository " + repository.getDirectory().toString()); try (Git git = new Git(repository)) { FetchResult result = git.fetch().call(); Collection<TrackingRefUpdate> updates = result.getTrackingRefUpdates(); List<TrackingRefUpdate> remoteRefsChanges = new ArrayList<TrackingRefUpdate>(); for (TrackingRefUpdate update : updates) { String refName = update.getLocalName(); if (refName.startsWith(REMOTE_REFS_PREFIX)) { ObjectId newObjectId = update.getNewObjectId(); this.logger.info(refName +" is now at " + newObjectId.getName()); remoteRefsChanges.add(update); } } if (updates.isEmpty()) { this.logger.info("Nothing changed"); } return remoteRefsChanges; } } @Override public Integer countCommits(Repository repository, String branch) throws Exception { RevWalk walk = new RevWalk(repository); try { Ref ref = repository.findRef(REMOTE_REFS_PREFIX + branch); ObjectId objectId = ref.getObjectId(); RevCommit start = walk.parseCommit(objectId); walk.setRevFilter(RevFilter.NO_MERGES); return RevWalkUtils.count(walk, start, null); } finally { walk.dispose(); } } @Override public Map<ChangeType, List<GitFile>> fileTreeDiff(Repository repository, RevCommit commitNew) throws Exception { Map<ChangeType, List<GitFile>> mapDiff = new HashMap<ChangeType, List<GitFile>>(); mapDiff.put(ChangeType.ADD, new ArrayList<>()); mapDiff.put(ChangeType.COPY, new ArrayList<>()); mapDiff.put(ChangeType.DELETE, new ArrayList<>()); mapDiff.put(ChangeType.MODIFY, new ArrayList<>()); mapDiff.put(ChangeType.RENAME, new ArrayList<>()); if(commitNew.getParentCount() == 0){ this.logger.warn("Commit don't have parent [commitId="+commitNew.getId().getName()+"]"); return mapDiff; } ObjectId headOld = commitNew.getParent(0).getTree(); //Commit pai no grafo. ObjectId headNew = commitNew.getTree(); //Commit corrente. // prepare the two iterators to compute the diff between ObjectReader reader = repository.newObjectReader(); CanonicalTreeParser treeRepositoryOld = new CanonicalTreeParser(); treeRepositoryOld.reset(reader, headOld); CanonicalTreeParser treeRepositoryNew = new CanonicalTreeParser(); treeRepositoryNew.reset(reader, headNew); // finally get the list of changed files List<DiffEntry> diffs = new Git(repository).diff() .setNewTree(treeRepositoryNew) .setOldTree(treeRepositoryOld) .setShowNameAndStatusOnly(true) .call(); for (DiffEntry entry : diffs) { if(UtilTools.isJavaFile(entry.getOldPath()) || UtilTools.isJavaFile(entry.getNewPath())) { String pathNew = "/dev/null".equals(entry.getNewPath())?null:entry.getNewPath(); String pathOld = "/dev/null".equals(entry.getOldPath())?null:entry.getOldPath(); GitFile file = new GitFile(pathOld, pathNew, entry.getChangeType()); mapDiff.get(entry.getChangeType()).add(file); } } return mapDiff; } @Override public void checkout(Repository repository, String commitId) throws Exception { this.logger.info("Checking out {} {} ...", repository.getDirectory().getParent().toString(), commitId); try (Git git = new Git(repository)) { CheckoutCommand checkout = git.checkout().setName(commitId); checkout.call(); } } @Override public RevCommit createRevCommitByCommitId(final Repository repository, final String commitId) throws Exception{ RevWalk walk = new RevWalk(repository); RevCommit commit = walk.parseCommit(repository.resolve(commitId)); walk.parseCommit(commit.getParent(0)); return commit; } }