Skip to content

Commit 86f2471

Browse files
author
Vladimir Kotal
committed
reimplement getHistory() in JGit
does not work for single file that was renamed
1 parent d5dc7f6 commit 86f2471

File tree

3 files changed

+123
-11
lines changed

3 files changed

+123
-11
lines changed

opengrok-indexer/src/main/java/org/opengrok/indexer/history/GitRepository.java

Lines changed: 122 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import java.util.Arrays;
4040
import java.util.HashMap;
4141
import java.util.Map;
42+
import java.util.Set;
43+
import java.util.SortedSet;
4244
import java.util.TreeSet;
4345
import java.util.concurrent.ExecutionException;
4446
import java.util.concurrent.ExecutorService;
@@ -53,8 +55,12 @@
5355

5456
import org.eclipse.jgit.api.BlameCommand;
5557
import org.eclipse.jgit.api.Git;
58+
import org.eclipse.jgit.api.LogCommand;
5659
import org.eclipse.jgit.api.errors.GitAPIException;
60+
import org.eclipse.jgit.api.errors.NoHeadException;
5761
import org.eclipse.jgit.blame.BlameResult;
62+
import org.eclipse.jgit.diff.DiffEntry;
63+
import org.eclipse.jgit.diff.DiffFormatter;
5864
import org.eclipse.jgit.diff.RawText;
5965
import org.eclipse.jgit.diff.RawTextComparator;
6066
import org.eclipse.jgit.lib.Constants;
@@ -67,14 +73,18 @@
6773
import org.eclipse.jgit.revwalk.RevTree;
6874
import org.eclipse.jgit.revwalk.RevWalk;
6975
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
76+
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
77+
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
7078
import org.eclipse.jgit.treewalk.TreeWalk;
7179
import org.eclipse.jgit.treewalk.filter.PathFilter;
7280
import org.eclipse.jgit.util.io.CountingOutputStream;
81+
import org.eclipse.jgit.util.io.NullOutputStream;
7382
import org.jetbrains.annotations.NotNull;
7483
import org.opengrok.indexer.configuration.CommandTimeoutType;
7584
import org.opengrok.indexer.configuration.RuntimeEnvironment;
7685
import org.opengrok.indexer.logger.LoggerFactory;
7786
import org.opengrok.indexer.util.Executor;
87+
import org.opengrok.indexer.util.ForbiddenSymlinkException;
7888
import org.opengrok.indexer.util.LazilyInstantiate;
7989
import org.opengrok.indexer.util.Version;
8090

@@ -586,19 +596,126 @@ History getHistory(File file) throws HistoryException {
586596
}
587597

588598
@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+
593664
// Assign tags to changesets they represent
594665
// We don't need to check if this repository supports tags,
595666
// because we know it :-)
596-
if (env.isTagsEnabled()) {
667+
if (RuntimeEnvironment.getInstance().isTagsEnabled()) {
597668
assignTagsInHistory(result);
598669
}
670+
599671
return result;
600672
}
601673

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+
602719
@Override
603720
boolean hasFileBasedTags() {
604721
return true;

opengrok-indexer/src/test/java/org/opengrok/indexer/history/GitRepositoryOctopusTest.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,7 @@ public void testOctopusHistory() throws Exception {
169169

170170
List<HistoryEntry> entries = history.getHistoryEntries();
171171
assertNotNull(entries, "git-octopus getHistoryEntries()");
172-
173-
/*
174-
* git-octopus has four-way merge, but GitHistoryParser condenses.
175-
*/
176-
assertEquals(4, entries.size(), "git-octopus log entries");
172+
assertEquals(5, entries.size(), "git-octopus log entries");
177173

178174
SortedSet<String> allFiles = new TreeSet<>();
179175
for (HistoryEntry entry : entries) {

opengrok-indexer/src/test/java/org/opengrok/indexer/history/GitRepositoryTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import java.util.HashSet;
3636
import java.util.List;
3737
import java.util.Set;
38-
import java.util.SortedSet;
3938
import java.util.TreeSet;
4039

4140
import org.eclipse.jgit.api.Git;

0 commit comments

Comments
 (0)