GitRetriever.java

package org.docascode.api.retrieve;

import org.docascode.api.core.errors.DocAsCodeException;
import org.eclipse.jgit.api.DiffCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
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.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class GitRetriever {
    private RevCommit commit;

    public GitRetriever setCommit(RevCommit commit){
        this.commit = commit;
        return this;
    }

    private Repository repo;

    public GitRetriever setRepository(Repository repo){
        this.repo = repo;
        return this;
    }

    private String path;

    public GitRetriever setPath(String path){
        this.path = path;
        return this;
    }

    private File toDir;

    public GitRetriever toDir(File toDir){
        this.toDir = toDir;
        return this;
    }

    public File call() throws DocAsCodeException {
        try (ObjectReader reader = repo.newObjectReader();
            Git git = Git.open(repo.getWorkTree())
        ) {
            DiffCommand diff = git.diff();
            RevWalk revWalk = new RevWalk(repo);
            RevTree oldTree = revWalk.parseTree(commit.getTree().getId());
            CanonicalTreeParser oldTreeParser = new CanonicalTreeParser();
            oldTreeParser.reset(reader,oldTree.getId());
            RevCommit headCommit = revWalk.parseCommit(repo.resolve("HEAD"));
            RevTree headTree = revWalk.parseTree(headCommit.getTree().getId());
            CanonicalTreeParser headTreeParser = new CanonicalTreeParser();
            oldTreeParser.reset(reader,headTree.getId());
            DiffEntry diffEntry = diff.setOldTree(oldTreeParser)
                    .setPathFilter(PathFilter.create(path))
                    .setNewTree(headTreeParser)
                    .call()
                    .get(0);


            RevTree tree = commit.getTree();
            TreeWalk treeWalk = new TreeWalk(repo);
            treeWalk.addTree(tree);
            treeWalk.setRecursive(true);
            treeWalk.setFilter(PathFilter.create(diffEntry.getOldPath()));
            if (!treeWalk.next()) {
                return null;
            }
            ObjectId objectId = treeWalk.getObjectId(0);
            ObjectLoader loader = repo.open(objectId);
            if (!toDir.mkdirs()){
                throw new DocAsCodeException(
                        String.format("Failed to create directory '%s'.",toDir.getAbsolutePath()));
            }
            File file = new File(toDir,
                    diffEntry.getOldPath());
            if (!file.exists() && !file.createNewFile()){
                throw new DocAsCodeException(
                        String.format("Failed to create file '%s'.",file.getAbsolutePath()));
            }
            OutputStream out = new FileOutputStream(file);
            loader.copyTo(out);
            out.close();
            return file;
        } catch (IOException | GitAPIException e) {
            throw new DocAsCodeException(
                    String.format(
                            "Unable to retrieve %s from %s",
                            path,
                            commit.getId()
                    ),e);
        }
    }
}