Skip to content

Commit

Permalink
Closes inspectIT#843 - Added author resolution to workspace diff (ins…
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonas Kunz authored Jul 16, 2020
1 parent f0b5590 commit 00f808a
Show file tree
Hide file tree
Showing 8 changed files with 586 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public AbstractWorkingDirectoryAccessor getWorkingDirectory() {
*/
public RevisionAccess getLiveRevision() {
CachingRevisionAccess currentRev = versioningManager.getLiveRevision();
if (cachedLiveRevision == null || !currentRev.getRevisionID().equals(cachedLiveRevision.getRevisionID())) {
if (cachedLiveRevision == null || !currentRev.getRevisionId().equals(cachedLiveRevision.getRevisionId())) {
cachedLiveRevision = currentRev;
}
return cachedLiveRevision;
Expand All @@ -105,7 +105,7 @@ public RevisionAccess getLiveRevision() {
public RevisionAccess getWorkspaceRevision() {
CachingRevisionAccess currentRev = versioningManager.getWorkspaceRevision();
if (cachedWorkspaceRevision == null
|| !currentRev.getRevisionID().equals(cachedWorkspaceRevision.getRevisionID())) {
|| !currentRev.getRevisionId().equals(cachedWorkspaceRevision.getRevisionId())) {
cachedWorkspaceRevision = currentRev;
}
return cachedWorkspaceRevision;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package rocks.inspectit.ocelot.file.accessor.git;

import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
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.TreeWalk;
import rocks.inspectit.ocelot.file.FileInfo;
import rocks.inspectit.ocelot.file.accessor.AbstractFileAccessor;
Expand All @@ -15,9 +17,7 @@
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;

/**
* Accessor to access specific Git revision/commits. Using this class ensures that all operations will be executed
Expand All @@ -38,22 +38,157 @@ public class RevisionAccess extends AbstractFileAccessor {

/**
* Constructor.
* Always resolves the commit.
*
* @param repository the repository to use
* @param revCommit the commit which will be used for the operations
*/
public RevisionAccess(Repository repository, RevCommit revCommit) {
this(repository, revCommit, true);
}

/**
* Constructor.
*
* @param repository the repository to use
* @param revCommit the commit which will be used for the operations
* @param resolveTree if true, the partial commit revCommit will be resolved using a RevWalk.
*/
public RevisionAccess(Repository repository, RevCommit revCommit, boolean resolveTree) {
this.repository = repository;
this.revCommit = revCommit;
if (resolveTree) {
try (RevWalk revWalk = new RevWalk(repository)) {
//reparse in case of incomplete revCommits, e.g if the RevCommit was received as a parent from another
this.revCommit = revWalk.parseCommit(revCommit.getId());
} catch (Exception e) {
throw new IllegalStateException(e);
}
} else {
this.revCommit = revCommit;
}
}

/**
* @return a unique ID for this revision
*/
public String getRevisionID() {
public String getRevisionId() {
return ObjectId.toString(revCommit.getId());
}

/**
* Returns the main parent of this Revision.
* For merge-commits the main parent is the Revision into which the other changes have been merged.
*
* @return the primary parent of this revision or an empty optional if this is a root commit.
*/
public Optional<RevisionAccess> getPreviousRevision() {
if (revCommit.getParentCount() >= 1) {
return Optional.of(new RevisionAccess(repository, revCommit.getParent(0)));
} else {
return Optional.empty();
}
}

/**
* @return the name of the author of this revision.
*/
public String getAuthorName() {
return revCommit.getAuthorIdent().getName();
}

/**
* Walks the history backwards to the a revision (A) which is a parent of this revision (B) and another given revision (C).
* This method will return a revision which minimizes MAX(distance(A,B), distance(A,C)).
* <p>
* Such a common ancestor should exist for all commits, because all branches originate from the root commit of the repo.
*
* @param other the other revision to find a common ancestor with
*
* @return the Revision which is a parent of both revisions.
*/
public RevisionAccess getCommonAncestor(RevisionAccess other) {
// unfortunately RevFilter.MERGE_BASE does not return the best ancestor,
// therefore we perform a BFS ourselves.
Set<String> ownVisited = new HashSet<>();
Set<String> otherVisited = new HashSet<>();
Deque<Node> openList = new ArrayDeque<>();
openList.addLast(new Node(true, this));
openList.addLast(new Node(false, other));
while (!openList.isEmpty()) {
Node current = openList.removeFirst();
String id = current.revAccess.getRevisionId();
if (current.isReachableFromOwn) {
if (otherVisited.contains(id)) {
return current.revAccess;
}
ownVisited.add(id);
} else {
if (ownVisited.contains(id)) {
return current.revAccess;
}
otherVisited.add(id);
}
for (int i = 0; i < current.revAccess.revCommit.getParentCount(); i++) {
RevCommit parent = current.revAccess.revCommit.getParent(i);
openList.addLast(new Node(current.isReachableFromOwn, new RevisionAccess(repository, parent)));
}
}
throw new IllegalStateException("No common ancestor!");
}

/**
* Checks if the given file exists in this revision but not in the parent revision.
*
* @param path the path of the file
*
* @return true, if the file was added in this revision.
*/
public boolean isConfigurationFileAdded(String path) {
if (!configurationFileExists(path)) {
return false;
}
Optional<RevisionAccess> parent = getPreviousRevision();
return !parent.isPresent() || !parent.get().configurationFileExists(path);
}

/**
* Checks if the given file exists in both this revision and the parent revision,
* but it's contents have changed.
*
* @param path the path of the file
*
* @return true, if the file exists both in this and the parent revision but with different contents.
*/
public boolean isConfigurationFileModified(String path) {
if (!configurationFileExists(path)) {
return false;
}
Optional<RevisionAccess> parent = getPreviousRevision();
if (!parent.isPresent() || !parent.get().configurationFileExists(path)) {
return false;
}
String currentContent = readConfigurationFile(path)
.orElseThrow(() -> new IllegalStateException("Expected file to exist"));
String previousContent = parent.get().readConfigurationFile(path)
.orElseThrow(() -> new IllegalStateException("Expected file to exist"));
return !currentContent.equals(previousContent);
}

/**
* Checks if the given file does not exist in this revision but existed in the parent revision.
*
* @param path the path of the file
*
* @return true, if the file was deleted in this revision.
*/
public boolean isConfigurationFileDeleted(String path) {
if (configurationFileExists(path)) {
return false;
}
Optional<RevisionAccess> parent = getPreviousRevision();
return parent.isPresent() && parent.get().configurationFileExists(path);
}

@Override
protected String verifyPath(String relativeBasePath, String relativePath) throws IllegalArgumentException {
if (relativePath.startsWith("/")) {
Expand Down Expand Up @@ -99,7 +234,7 @@ protected boolean exists(String path) {
try (TreeWalk treeWalk = TreeWalk.forPath(repository, path, revCommit.getTree())) {
return treeWalk != null;
} catch (Exception e) {
log.error("Could not read file {} from git repository", path, e);
log.error("Assuming file {} does not exist due to exception", path, e);
return false;
}
}
Expand Down Expand Up @@ -157,7 +292,9 @@ protected List<FileInfo> listFiles(String path) {
*
* @param treeWalk The {@link TreeWalk} to traverse.
* @param resultList the list which will be filled with the found files
*
* @return The files within the current tree.
*
* @throws IOException in case the repository cannot be read
*/
private boolean collectFiles(TreeWalk treeWalk, List<FileInfo> resultList) throws IOException {
Expand Down Expand Up @@ -192,4 +329,15 @@ private boolean collectFiles(TreeWalk treeWalk, List<FileInfo> resultList) throw

return false;
}

/**
* Container used for finding the commonAncestor
*/
@Value
private static class Node {

boolean isReachableFromOwn;

RevisionAccess revAccess;
}
}
Loading

0 comments on commit 00f808a

Please sign in to comment.