package it.unitn.molerat.repos.wrappers; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.*; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathSuffixFilter; import org.eclipse.jgit.lib.Ref; public final class GitRepoWrapper extends RepoWrapper { private final String repoRoot; private final Repository repo; private final Git git; public GitRepoWrapper(String root) throws IOException { this.diffFilePrefix = "diff --git a"; this.repoRoot = root; FileRepositoryBuilder builder = new FileRepositoryBuilder(); builder.setGitDir(new File(this.repoRoot + "/.git")); this.repo = builder.build(); this.git = new Git(this.repo); } public Map getTagsAndCommits() throws Exception { Map tags = new HashMap<>(); List call = git.tagList().call(); for (Ref tagref : call) { tags.put(tagref.getName(), tagref.getPeeledObjectId().getName()); } return tags; } @Override public String doDiff(String path, String leftRev, String rightRev) throws Exception { if (path.startsWith("/")) { path = path.substring(1); } OutputStream out = new ByteArrayOutputStream(); try { DiffFormatter df = new DiffFormatter(out); df.setRepository(this.git.getRepository()); df.setDetectRenames(true); List entries = df.scan(getTreeIterator(leftRev), getTreeIterator(rightRev)); df.close(); df.format(entries); } catch(Exception e) { System.out.println("[JGit error] " + e.getMessage()); throw new Exception(e); } return out.toString(); } @Override public String doDiff(String leftRev, String rightRev) throws Exception { return this.doDiff("", leftRev, rightRev); } @Override public String doCat(String path, String rev) throws Exception { if (path.startsWith("/")) { path = path.substring(1); } String output = ""; final ObjectId id = this.repo.resolve(rev); ObjectReader reader = this.repo.newObjectReader(); RevWalk walk = null; try { walk = new RevWalk(reader); RevCommit commit = walk.parseCommit(id); RevTree tree = commit.getTree(); TreeWalk treewalk = TreeWalk.forPath(reader, path, tree); if (treewalk != null) { byte[] data = reader.open(treewalk.getObjectId(0)).getBytes(); output = new String(data, "utf-8"); } } finally { reader.close(); if (walk != null) { walk.close(); } } return output; } @Override public String getBasePath() { return this.repoRoot; } private AbstractTreeIterator getTreeIterator(String name) throws IOException { final ObjectId id = this.repo.resolve(name); if (id == null) { throw new IllegalArgumentException(name); } final CanonicalTreeParser p = new CanonicalTreeParser(); try (ObjectReader or = this.repo.newObjectReader(); RevWalk rw = new RevWalk(this.repo)) { p.reset(or, rw.parseTree(id)); return p; } } // TODO: UNIMPLEMENTED @Override public void annotate(String path, String rev, Object callback) throws Exception { } @Override public Set getRevisionNumbers(String topRev) throws Exception { Set commits = new LinkedHashSet<>(); for (RevCommit commit : git.log().add(this.repo.resolve(topRev + "~1")).call()) { commits.add(commit.getName()); } return commits; } // TODO: UNIMPLEMENTED @Override public Map determineOriginatingRevision(String filePath, String revision, Map lines) throws Exception { return new HashMap<>(); } // TODO: UNIMPLEMENTED @Override protected String getReleaseTag(String release) throws Exception { /* Map tags = this.getTagsAndCommits(); for (String key : tags.keySet()) { String candidate = key.replace("refs/tags/", ""); System.out.println("KEY: " + candidate); } */ return ("refs/tags/" + release); } @Override public String getReleaseCommit(String release) throws Exception { String tagName = getReleaseTag(release); String tagCommit = this.getTagsAndCommits().get(tagName); Iterator iter = git.log().add(this.repo.resolve(tagCommit)).call().iterator(); iter.next(); return iter.next().getName(); } @Override public Set getRevisionFiles(String rev, String filter) throws Exception { Set files = new LinkedHashSet<>(); RevWalk revWalk = null; TreeWalk treeWalk = null; try { ObjectId revId = this.repo.resolve(rev); revWalk = new RevWalk(this.repo); RevCommit commit = revWalk.parseCommit(revId); RevTree tree = commit.getTree(); treeWalk = new TreeWalk(this.repo); treeWalk.addTree(tree); treeWalk.setRecursive(true); treeWalk.setFilter(PathSuffixFilter.create(filter)); while(treeWalk.next()) { files.add(treeWalk.getPathString()); } } catch(Exception e) { throw new Exception("[JGit error] " + e.getMessage()); } finally { if (revWalk != null){ revWalk.close(); } if (treeWalk != null){ treeWalk.close(); } } return files; } @Override public Set getAllRepositoryTransactions() throws Exception { Iterable logs = git.log().call(); Set commits = new LinkedHashSet<>(); Iterator it = logs.iterator(); if (it.hasNext()) { String latestCommit = it.next().getName(); commits.add(latestCommit); commits.addAll(this.getRevisionNumbers(latestCommit)); } return commits; } }