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()); } }