|
39 | 39 | import java.util.Arrays;
|
40 | 40 | import java.util.HashMap;
|
41 | 41 | import java.util.Map;
|
| 42 | +import java.util.Set; |
| 43 | +import java.util.SortedSet; |
42 | 44 | import java.util.TreeSet;
|
43 | 45 | import java.util.concurrent.ExecutionException;
|
44 | 46 | import java.util.concurrent.ExecutorService;
|
|
53 | 55 |
|
54 | 56 | import org.eclipse.jgit.api.BlameCommand;
|
55 | 57 | import org.eclipse.jgit.api.Git;
|
| 58 | +import org.eclipse.jgit.api.LogCommand; |
56 | 59 | import org.eclipse.jgit.api.errors.GitAPIException;
|
| 60 | +import org.eclipse.jgit.api.errors.NoHeadException; |
57 | 61 | import org.eclipse.jgit.blame.BlameResult;
|
| 62 | +import org.eclipse.jgit.diff.DiffEntry; |
| 63 | +import org.eclipse.jgit.diff.DiffFormatter; |
58 | 64 | import org.eclipse.jgit.diff.RawText;
|
59 | 65 | import org.eclipse.jgit.diff.RawTextComparator;
|
60 | 66 | import org.eclipse.jgit.lib.Constants;
|
|
67 | 73 | import org.eclipse.jgit.revwalk.RevTree;
|
68 | 74 | import org.eclipse.jgit.revwalk.RevWalk;
|
69 | 75 | import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
|
| 76 | +import org.eclipse.jgit.treewalk.AbstractTreeIterator; |
| 77 | +import org.eclipse.jgit.treewalk.CanonicalTreeParser; |
70 | 78 | import org.eclipse.jgit.treewalk.TreeWalk;
|
71 | 79 | import org.eclipse.jgit.treewalk.filter.PathFilter;
|
72 | 80 | import org.eclipse.jgit.util.io.CountingOutputStream;
|
| 81 | +import org.eclipse.jgit.util.io.NullOutputStream; |
73 | 82 | import org.jetbrains.annotations.NotNull;
|
74 | 83 | import org.opengrok.indexer.configuration.CommandTimeoutType;
|
75 | 84 | import org.opengrok.indexer.configuration.RuntimeEnvironment;
|
76 | 85 | import org.opengrok.indexer.logger.LoggerFactory;
|
77 | 86 | import org.opengrok.indexer.util.Executor;
|
| 87 | +import org.opengrok.indexer.util.ForbiddenSymlinkException; |
78 | 88 | import org.opengrok.indexer.util.LazilyInstantiate;
|
79 | 89 | import org.opengrok.indexer.util.Version;
|
80 | 90 |
|
@@ -586,19 +596,126 @@ History getHistory(File file) throws HistoryException {
|
586 | 596 | }
|
587 | 597 |
|
588 | 598 | @Override
|
589 |
| - History getHistory(File file, String sinceRevision) |
590 |
| - throws HistoryException { |
591 |
| - RuntimeEnvironment env = RuntimeEnvironment.getInstance(); |
592 |
| - History result = new GitHistoryParser(isHandleRenamedFiles()).parse(file, this, sinceRevision); |
| 599 | + History getHistory(File file, String sinceRevision) throws HistoryException { |
| 600 | + final List<HistoryEntry> entries = new ArrayList<>(); |
| 601 | + final List<String> renamedFiles = new ArrayList<>(); |
| 602 | + |
| 603 | + try (org.eclipse.jgit.lib.Repository repository = getJGitRepository(getDirectoryName())) { |
| 604 | + try (Git git = new Git(repository)) { |
| 605 | + |
| 606 | + // TODO: does log() take current branch into account ? |
| 607 | + LogCommand log = git.log(); |
| 608 | + if (sinceRevision != null) { |
| 609 | + log.addRange(repository.resolve(sinceRevision), repository.resolve("HEAD")); |
| 610 | + } |
| 611 | + |
| 612 | + String relativePath = RuntimeEnvironment.getInstance().getPathRelativeToSourceRoot(file); |
| 613 | + if (!getDirectoryNameRelative().equals(relativePath)) { |
| 614 | + log.addPath(getRepoRelativePath(file)); |
| 615 | + } |
| 616 | + |
| 617 | + Iterable<RevCommit> commits = log.call(); |
| 618 | + for (RevCommit commit : commits) { |
| 619 | + int numParents = commit.getParentCount(); |
| 620 | + if (numParents > 1 && !isMergeCommitsEnabled()) { |
| 621 | + continue; |
| 622 | + } |
| 623 | + |
| 624 | + HistoryEntry historyEntry = new HistoryEntry(commit.getId().abbreviate(GIT_ABBREV_LEN).name(), |
| 625 | + new Date((long) commit.getCommitTime() * 1000), |
| 626 | + commit.getAuthorIdent().getName() + |
| 627 | + " <" + commit.getAuthorIdent().getEmailAddress() + ">", |
| 628 | + null, commit.getFullMessage(), true); |
| 629 | + |
| 630 | + SortedSet<String> files = new TreeSet<>(); |
| 631 | + if (numParents == 1) { |
| 632 | + getFiles(repository, git, commit.getParent(0), commit, files, renamedFiles); |
| 633 | + } else if (numParents == 0) { // first commit (TODO: could be dangling ?) |
| 634 | + try (TreeWalk treeWalk = new TreeWalk(repository)) { |
| 635 | + treeWalk.addTree(commit.getTree()); |
| 636 | + treeWalk.setRecursive(true); |
| 637 | + |
| 638 | + while (treeWalk.next()) { |
| 639 | + files.add(getDirectoryNameRelative() + "/" + treeWalk.getPathString()); |
| 640 | + } |
| 641 | + } |
| 642 | + } else { |
| 643 | + getFiles(repository, git, commit.getParent(0), commit, files, renamedFiles); |
| 644 | + } |
| 645 | + |
| 646 | + historyEntry.setFiles(files); |
| 647 | + entries.add(historyEntry); |
| 648 | + } |
| 649 | + } catch (NoHeadException e) { |
| 650 | + // TODO |
| 651 | + e.printStackTrace(); |
| 652 | + } catch (GitAPIException e) { |
| 653 | + // TODO |
| 654 | + e.printStackTrace(); |
| 655 | + } catch (ForbiddenSymlinkException e) { |
| 656 | + e.printStackTrace(); |
| 657 | + } |
| 658 | + } catch (IOException e) { |
| 659 | + e.printStackTrace(); |
| 660 | + } |
| 661 | + |
| 662 | + History result = new History(entries, renamedFiles); |
| 663 | + |
593 | 664 | // Assign tags to changesets they represent
|
594 | 665 | // We don't need to check if this repository supports tags,
|
595 | 666 | // because we know it :-)
|
596 |
| - if (env.isTagsEnabled()) { |
| 667 | + if (RuntimeEnvironment.getInstance().isTagsEnabled()) { |
597 | 668 | assignTagsInHistory(result);
|
598 | 669 | }
|
| 670 | + |
599 | 671 | return result;
|
600 | 672 | }
|
601 | 673 |
|
| 674 | + private void getFiles(org.eclipse.jgit.lib.Repository repository, Git git, |
| 675 | + RevCommit oldCommit, RevCommit newCommit, |
| 676 | + Set<String> files, List<String> renamedFiles) |
| 677 | + throws IOException { |
| 678 | + |
| 679 | + OutputStream outputStream = NullOutputStream.INSTANCE; |
| 680 | + try (DiffFormatter formatter = new DiffFormatter(outputStream)) { |
| 681 | + formatter.setRepository(git.getRepository()); |
| 682 | + formatter.setDetectRenames(true); |
| 683 | + |
| 684 | + List<DiffEntry> diffs = formatter.scan(prepareTreeParser(repository, oldCommit), |
| 685 | + prepareTreeParser(repository, newCommit)); |
| 686 | + |
| 687 | + for (DiffEntry diff : diffs) { |
| 688 | + if (diff.getChangeType() == DiffEntry.ChangeType.RENAME) { |
| 689 | + if (isHandleRenamedFiles()) { |
| 690 | + renamedFiles.add(diff.getNewPath()); |
| 691 | + } else { |
| 692 | + files.add(getDirectoryNameRelative() + "/" + diff.getNewPath()); |
| 693 | + } |
| 694 | + } else { |
| 695 | + // TODO: !DELETE ? |
| 696 | + files.add(getDirectoryNameRelative() + "/" + diff.getNewPath()); |
| 697 | + } |
| 698 | + } |
| 699 | + } |
| 700 | + } |
| 701 | + |
| 702 | + private static AbstractTreeIterator prepareTreeParser(org.eclipse.jgit.lib.Repository repository, |
| 703 | + RevCommit commit) throws IOException { |
| 704 | + // from the commit we can build the tree which allows us to construct the TreeParser |
| 705 | + try (RevWalk walk = new RevWalk(repository)) { |
| 706 | + RevTree tree = walk.parseTree(commit.getTree().getId()); |
| 707 | + |
| 708 | + CanonicalTreeParser treeParser = new CanonicalTreeParser(); |
| 709 | + try (ObjectReader reader = repository.newObjectReader()) { |
| 710 | + treeParser.reset(reader, tree.getId()); |
| 711 | + } |
| 712 | + |
| 713 | + walk.dispose(); |
| 714 | + |
| 715 | + return treeParser; |
| 716 | + } |
| 717 | + } |
| 718 | + |
602 | 719 | @Override
|
603 | 720 | boolean hasFileBasedTags() {
|
604 | 721 | return true;
|
|
0 commit comments