Skip to content
Snippets Groups Projects
IssueRepo.java 11.86 KiB
package git;

import issueData.Issue;
import issueData.User;
import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeCommand;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.ResetCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.Merger;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefSpec;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.List;

public class IssueRepo {

    private final ObjectInserter oi;
    private Repository repository;

    public IssueRepo() {
        try {
            this.repository = new FileRepositoryBuilder()
                    .setGitDir(new File("/home/jack/Documents/testrepo/.git"))
//                    .setGitDir(new File("/home/jack/Documents/testrepo2/.git"))
                    .build();

        } catch (IOException e) {
            e.printStackTrace();
        }
        oi = new ObjectInserter(repository);
    }

    public void push(String remote) {
        try (Git git = new Git(repository)) {
            try {
                Iterable<PushResult> results = git.push().setRemote(remote).add("issues").call();
                results.forEach(result -> result.getRemoteUpdates()
                        .forEach(remoteRefUpdate ->
                                System.out.println("Status - " + remoteRefUpdate.getStatus())));
            } catch (GitAPIException e) {
                e.printStackTrace();
            }
        }
    }

    public void pull(String remote) {
        String clone = null;
        try {
            clone = localClone();
            mergeOnClone(clone, remote);
        } catch (IOException | GitAPIException e) {
            e.printStackTrace();
        } finally {
            if (clone != null) {
                try {
                    FileUtils.deleteDirectory(new File(clone));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void pullClone(String clone) throws IOException, GitAPIException {

        try (Git git = new Git(repository)) {
            StoredConfig config = git.getRepository().getConfig();
            config.setString("remote", "remIssues", "url", clone);
            config.save();

            git.fetch().setRemote("remIssues")
                    .setRefSpecs(new RefSpec("refs/heads/issues:refs/remotes/remIssues"))
                    .call();

            Ref localRef = repository.findRef("refs/heads/issues");
            Ref remoteRef = repository.findRef("refs/remotes/remIssues");

            RevCommit localCommit = null;
            RevCommit remoteCommit = null;

            try (RevWalk revWalk = new RevWalk(repository)) {
                localCommit = revWalk.parseCommit(localRef.getObjectId());
                remoteCommit = revWalk.parseCommit(remoteRef.getObjectId());
                revWalk.dispose();
            } catch (IOException e) {
                e.printStackTrace();
            }

            Merger merger = MergeStrategy.THEIRS.newMerger(repository);


            if (merger.merge(localCommit, remoteCommit)) {
                String commit = "Merged " + localRef.getName() + " and " + remoteRef.getName();
                commit(commit, merger.getResultTreeId(), localCommit, remoteCommit);
                System.out.println("Issues pulled");
            }

        }

    }

    private String localClone() throws IOException, GitAPIException {
        Path tempDir = Files.createTempDirectory("ddit");
        tempDir.toFile().deleteOnExit();
        Git.cloneRepository()
                .setURI("/home/jack/Documents/testrepo/.git")
                .setDirectory(tempDir.toFile())
                .setBranchesToClone(Collections.singleton("refs/heads/issues"))
                .setBranch("refs/heads/issues")
                .call();
        return tempDir.toString();
    }

    private void mergeOnClone(String clone, String remote) throws IOException, GitAPIException {
        try (Git git = Git.open(new File(clone))) {

            StoredConfig config = git.getRepository().getConfig();
            config.setString("remote", "remIssues", "url", remote);
            config.save();

            git.fetch()
                    .setRemote("remIssues")
                    .setRefSpecs(new RefSpec("refs/heads/issues:refs/remotes/remIssues"))
                    .call();

            ObjectId mergeBase = git.getRepository().resolve("remIssues");
            MergeCommand merge = git.merge()
                    .include(mergeBase)
                    .setCommit(true)
                    .setMessage("Merged");
            MergeResult mergeResult = merge.call();
            System.out.println("attempting pullClone");

            if (!mergeResult.getMergeStatus().isSuccessful() && mergeResult.getConflicts() != null) {
                System.out.println("Conflicts exist in:");
                mergeResult.getConflicts().forEach((k, v) -> System.out.println(k));

                if (Desktop.isDesktopSupported()) {
                    List<Thread> threads = new ArrayList<>();

                    mergeResult.getConflicts().forEach((k, v) -> {
                        Runnable runnable = () -> {
                            try {
                                Desktop.getDesktop().open(new File(clone + "/" + k));
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        };
                        Thread thread = new Thread(runnable);
                        threads.add(thread);
                    });

                    threads.forEach(Thread::start);

                    threads.forEach(t -> {
                        try {
                            t.join();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    });

                    System.out.println("Enter \"abort\" to stop pullClone or \"resolved\" if finished resolving - any other input will be treated as abort");
                    Scanner scanner = new Scanner(System.in);
                    String input = scanner.nextLine();
                    scanner.close();

                    if (input.equals("resolved")) {
                        git.add().addFilepattern(".").call();
                        git.commit().setMessage("resolved merge conflict").call();
                        pullClone(clone);
                    } else {
                        git.reset().setMode(ResetCommand.ResetType.HARD).call();
                        System.out.println("Merge abandoned");
                    }
                }

            } else if (mergeResult.getMergeStatus().isSuccessful()) {
                pullClone(clone);
            }

        }
    }

    public void writeIssue(Issue issue, String commitMessage) throws IOException {
        RevCommit currentCommit = getCurrentCommit();

        IssueTreeBuilder issueWriter = new IssueTreeBuilder(repository);
        issueWriter.buildIssueTree(issue);

        List<Issue> sortedIssues = getIssues();
        sortedIssues.add(issue);
        sortedIssues.sort(Comparator.comparing(Issue::getOriginalHash));

        TreeFormatter repoTreeFormatter = new TreeFormatter();
        getTree(sortedIssues, repoTreeFormatter, issueWriter);

        ObjectId repoTree = oi.insert(Constants.OBJ_TREE, repoTreeFormatter.toByteArray());

        commit(commitMessage, repoTree, currentCommit);

    }

    public void updateIssues(Issue issue, String commitMessage) throws IOException {
        RevCommit currentCommit = getCurrentCommit();

        IssueTreeBuilder issueWriter = new IssueTreeBuilder(repository);
        issueWriter.buildIssueTree(issue);

        List<Issue> sortedIssues = getIssues();
        sortedIssues.sort(Comparator.comparing(Issue::getOriginalHash));

        int i = sortedIssues.indexOf(issue);
        sortedIssues.remove(i);
        sortedIssues.add(i, issue);

        TreeFormatter repoTreeFormatter = new TreeFormatter();

        getTree(sortedIssues, repoTreeFormatter, issueWriter);

        ObjectId repoTree = oi.insert(Constants.OBJ_TREE, repoTreeFormatter.toByteArray());

        commit(commitMessage, repoTree, currentCommit);
    }

    public void deleteIssue(String hash) throws IOException {
        RevCommit currentCommit = getCurrentCommit();

        List<Issue> sortedIssues = getIssues();
        sortedIssues.sort(Comparator.comparing(Issue::getOriginalHash));

        sortedIssues.removeIf(issue -> issue.getOriginalHash().equals(hash));

        TreeFormatter repoTreeFormatter = new TreeFormatter();
        IssueTreeBuilder issueWriter = new IssueTreeBuilder(repository);

        getTree(sortedIssues, repoTreeFormatter, issueWriter);

        ObjectId repoTree = oi.insert(Constants.OBJ_TREE, repoTreeFormatter.toByteArray());

        commit("Deleted issue " + hash, repoTree, currentCommit);


    }

    private void getTree(List<Issue> sortedIssues, TreeFormatter repoTreeFormatter, IssueTreeBuilder issueWriter) {
        sortedIssues.forEach(i -> {
            try {
                ObjectId issueObj = issueWriter.buildIssueTree(i);
                repoTreeFormatter.append(i.getOriginalHash(), FileMode.TREE, issueObj);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    public List<Issue> getIssues() {
        try {
            IssueReader issueReader = new IssueReader(repository, getCurrentCommit().getTree());
            return issueReader.getIssues();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return new ArrayList<>();
    }

    private void commit(String message, ObjectId tree, ObjectId... parent) throws IOException {
        CommitBuilder commitBuilder = new CommitBuilder();
        commitBuilder.setMessage(message);
        PersonIdent personIdent = new PersonIdent(getUserName(), getUserEmail());
        commitBuilder.setAuthor(personIdent);
        commitBuilder.setCommitter(personIdent);
        if (tree != null && parent != null) {
            commitBuilder.setTreeId(tree);
            commitBuilder.setParentIds(parent);
        } else {
            commitBuilder.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[]{}));
        }

        //update issues branch ref to point to new commit
        ObjectId newCommit = oi.insert(commitBuilder);

        RevCommit commit;

        try (RevWalk walk = new RevWalk(repository)) {
            commit = walk.parseCommit(newCommit);
            walk.dispose();
        }

        RefUpdate update = repository.updateRef("refs/heads/issues");
        update.setNewObjectId(commit);
        update.update();
    }

    public void commit(String message) throws IOException {
        commit(message, null);
    }

    private RevCommit getCurrentCommit() throws IOException {
        RevCommit commit;
        Ref issueRef = repository.findRef("refs/heads/issues");

        try (RevWalk walk = new RevWalk(repository)) {
            commit = walk.parseCommit(issueRef.getObjectId());
            walk.dispose();
            return commit;
        }
    }

    private String getUserName() {
        String name = repository.getConfig().getString("user", null, "name");
        return Objects.requireNonNullElse(name, "Username unknown ");
    }

    private String getUserEmail() {
        String email = repository.getConfig().getString("user", null, "email");
        return Objects.requireNonNullElse(email, "Email unknown");
    }

    public User getUser() {
        return new User(getUserName(), getUserEmail());
    }

}