{"action":"created","comment":{"url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/comments/226993332","pull_request_review_id":166933148,"id":226993332,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyNjk5MzMzMg==","diff_hunk":"@@ -0,0 +1,336 @@\n+package de.unibremen.informatik.st.libvcs4j.spoon;\n+\n+import de.unibremen.informatik.st.libvcs4j.RevisionRange;\n+import de.unibremen.informatik.st.libvcs4j.Validate;\n+import de.unibremen.informatik.st.libvcs4j.FileChange;\n+import de.unibremen.informatik.st.libvcs4j.VCSFile;\n+import org.slf4j.Logger;\n+import org.slf4j.LoggerFactory;\n+import spoon.Launcher;\n+import spoon.SpoonModelBuilder;\n+import spoon.reflect.CtModel;\n+import spoon.reflect.cu.CompilationUnit;\n+import spoon.reflect.declaration.CtElement;\n+import spoon.reflect.declaration.CtPackage;\n+import spoon.reflect.declaration.CtType;\n+import spoon.reflect.declaration.CtTypeParameter;\n+import spoon.reflect.factory.CompilationUnitFactory;\n+import spoon.reflect.factory.Factory;\n+import spoon.reflect.visitor.filter.TypeFilter;\n+import spoon.support.compiler.FileSystemFile;\n+import spoon.support.compiler.FilteringFolder;\n+\n+import java.io.File;\n+import java.io.IOException;\n+import java.nio.file.Paths;\n+import java.nio.file.Path;\n+import java.nio.file.Files;\n+import java.nio.file.SimpleFileVisitor;\n+import java.nio.file.FileVisitResult;\n+import java.nio.file.attribute.BasicFileAttributes;\n+import java.util.List;\n+import java.util.Collection;\n+import java.util.Set;\n+import java.util.HashSet;\n+import java.util.Optional;\n+import java.util.ArrayList;\n+import java.util.stream.Collectors;\n+\n+import static java.lang.String.format;\n+import static java.lang.System.currentTimeMillis;\n+\n+public class SpoonModel {\n+    private static final Logger LOGGER = LoggerFactory.getLogger(SpoonModel.class);\n+\n+    private Optional<CtModel> model = Optional.empty();\n+\n+    private File temporaryDirectory = null;\n+\n+    private boolean noClasspath = true;\n+\n+    private Set<String> filesNotCompiledSinceLastUpdate = new HashSet<>();\n+\n+    public Optional<CtModel> getModel() {\n+        return model;\n+    }\n+\n+    /**\n+     * Builds the underlying spoon model with the given revision incrementally.\n+     * In addition, each increment identifies files that could not be compiled in the previous step. This method also\n+     * tries to recompile them in the next increment.\n+     *\n+     * @param revisionRange\n+     *      The current checked out revision. Contains a list of {@link FileChange} objects, from which the spoon model\n+     *      will be build.\n+     */\n+    public void update(final RevisionRange revisionRange) {\n+        Validate.notNull(revisionRange);\n+        final long current = currentTimeMillis();\n+\n+        if (temporaryDirectory == null) {\n+            createTmpDir();\n+        }\n+\n+        if (model.isPresent()) {\n+\n+            final Factory factory = model.get().getRootPackage().getFactory();\n+            final Launcher launcher = new Launcher(factory);\n+            launcher.getEnvironment().setNoClasspath(noClasspath);\n+            launcher.getModelBuilder().setSourceClasspath(temporaryDirectory.getPath());\n+            launcher.setBinaryOutputDirectory(temporaryDirectory);\n+            filesNotCompiledSinceLastUpdate.addAll(findPreviouslyNotCompiledSources());\n+\n+            final List<String> filesToBuild = revisionRange.getFileChanges()\n+                    .stream()\n+                    .filter(fileChange -> fileChange.getType() != FileChange.Type.REMOVE\n+                            && fileChange.getType() != FileChange.Type.ADD)\n+                    .map(FileChange::getNewFile)\n+                    .map(vcsFile -> vcsFile.orElseThrow(IllegalArgumentException::new))\n+                    .filter(sourceFile -> sourceFile.getPath().endsWith(\".java\"))\n+                    .map(VCSFile::getPath)\n+                    .collect(Collectors.toList());\n+\n+            filesNotCompiledSinceLastUpdate.addAll(filesToBuild);\n+\n+            //delete the removed files from the spoon model\n+            removeChangedTypes(getAllFilesFromFileChanges(revisionRange.getRemovedFiles(), true));\n+\n+            //delete removed files from the set, because they do not exist anymore\n+            getAllFilesFromFileChanges(revisionRange.getRemovedFiles(), true)\n+                    .forEach(path -> filesNotCompiledSinceLastUpdate.remove(path));\n+\n+            //delete relocated files from the set, because their path is outdated\n+            getAllFilesFromFileChanges(revisionRange.getRelocatedFiles(), true)\n+                    .forEach(path -> filesNotCompiledSinceLastUpdate.remove(path));\n+\n+            //remove the changed classes from spoon model\n+            removeChangedTypes(filesNotCompiledSinceLastUpdate);\n+\n+            filesNotCompiledSinceLastUpdate.addAll(getAllFilesFromFileChanges(revisionRange.getAddedFiles(), false));\n+\n+            launcher.addInputResource(createInputSource(filesNotCompiledSinceLastUpdate));\n+            launcher.getModelBuilder().addCompilationUnitFilter(path -> !filesToBuild.contains(path));\n+            launcher.getModelBuilder().compile(SpoonModelBuilder.InputType.FILES);\n+            model.get().setBuildModelIsFinished(false);\n+            try {\n+                model = Optional.of(launcher.buildModel());\n+            } catch (Exception e) {\n+                model = Optional.empty();\n+                filesNotCompiledSinceLastUpdate.clear();\n+            }\n+\n+        } else {\n+\n+            final Launcher launcher = new Launcher();\n+            launcher.setBinaryOutputDirectory(temporaryDirectory);\n+            launcher.getEnvironment().setNoClasspath(noClasspath);\n+            //Add the checked out directory here, so we do not have to add each single file\n+            launcher.addInputResource(revisionRange.getRevision().getOutput().toString());\n+            launcher.getModelBuilder().compile(SpoonModelBuilder.InputType.FILES);\n+            model = Optional.of(launcher.buildModel());\n+\n+        }\n+        LOGGER.info(format(\"Model built in %d milliseconds\", currentTimeMillis() - current));\n+    }\n+\n+    /**\n+     * Transforms the given Input into a {@link FilteringFolder}. Each ath in the given collection has to exist,\n+     * be readable and be a regular Java source file, otherwise it will not be added to the {@link FilteringFolder}.\n+     *\n+     * @param input The given collection with absolute paths to source files.\n+     * @return A {@link FilteringFolder} containing all files from the given input.\n+     */\n+    private FilteringFolder createInputSource(final Collection<String> input) {\n+        FilteringFolder folder = new FilteringFolder();\n+        input.forEach(path -> {\n+            final Path p = Paths.get(path);\n+            if (Files.exists(p) && Files.isReadable(p) && Files.isRegularFile(p)) {\n+                folder.addFile(new FileSystemFile(p.toFile()));\n+            }\n+        });\n+        return folder;\n+    }\n+\n+    /**\n+     * Removes all source files from {@link CtModel} that are given as input. These files have changed, so they need to be\n+     * removed from the {@link CtModel}. The corresponding binary files are getting deleted as well.\n+     *\n+     * @param pFileChanges The given collection with absolute paths to source files.\n+     */\n+    private void removeChangedTypes(final Collection<String> pFileChanges) {\n+        final CtPackage rootPackage = model.get().getRootPackage();\n+        final CompilationUnitFactory cuFactory = rootPackage.getFactory().CompilationUnit();\n+\n+        pFileChanges.stream()\n+                .filter(sourceFile -> sourceFile.endsWith(\".java\"))\n+                .forEach(path -> {\n+                    if (cuFactory.getMap().containsKey(path)) {\n+                        final CompilationUnit cu = cuFactory.removeFromCache(path);\n+                        cu.getDeclaredTypes().forEach(CtElement::delete);\n+                        cu.getBinaryFiles().forEach(this::deleteFile);\n+                    }\n+                });\n+    }\n+\n+    /**\n+     * Computes all previously not compiled classes. In Detail this means, that all source files get collected, which\n+     * do not have one (or more) corresponding class files on the hard drive.\n+     *\n+     * @return\n+     *      A list from all the sources files, which did not get compiled correctly in the previous iteration.\n+     */\n+    private List<String> findPreviouslyNotCompiledSources() {\n+        final List<String> filesNeedToBeRebuild = new ArrayList<>();\n+        final CtModel currentModel = model.get();\n+        List<File> expected;\n+        Validate.notNull(temporaryDirectory);\n+        final String output = temporaryDirectory.getAbsolutePath();\n+        for (final CtType type : currentModel.getAllTypes()) {\n+            final File base = Paths.get(output, type.getPackage().getQualifiedName().replace(\".\", File.separator)).toFile();\n+            expected = getExpectedBinaryFiles(base, null, type);\n+\n+            if (expected.stream().anyMatch(file -> !file.isFile())) {\n+                filesNeedToBeRebuild.add(type.getPosition().getFile().getAbsolutePath());\n+\n+                //if a class needs to be recompiled, rebuild all classes, that refer to this class\n+                for (final CtType oldType : currentModel.getAllTypes()) {\n+                    if (oldType.getReferencedTypes().contains(type.getReference())) {\n+                        filesNeedToBeRebuild.add(oldType.getPosition().getFile().getAbsolutePath());\n+                    }\n+                }\n+            } else {\n+                filesNotCompiledSinceLastUpdate.remove(type.getPosition().getFile().getAbsolutePath());\n+            }\n+\n+        }\n+\n+        return filesNeedToBeRebuild;\n+    }\n+\n+    /**\n+     * Deletes the given file. It only gets deleted, if it exists and is readable.\n+     *\n+     * @param fileToDelete\n+     *      The file that needs to be deleted.\n+     */\n+    private void deleteFile(final File fileToDelete) {\n+        if (Files.exists(fileToDelete.toPath()) && Files.isReadable(fileToDelete.toPath())) {\n+            fileToDelete.delete();\n+        }\n+    }\n+\n+    /**\n+     * Creates a temporary directory in which the class files get stored. This temporary directory\n+     * will be deleted on shutdown (see {@link #recursiveDeleteOnShutdownHook(Path)}).\n+     */\n+    private void createTmpDir() {\n+        try {\n+            temporaryDirectory = Files.createTempDirectory(\"tmpClassDirectory\").toFile();\n+            recursiveDeleteOnShutdownHook(temporaryDirectory.toPath());\n+        } catch (IOException e) {\n+            LOGGER.error(\"Failed creating temporary directory\");\n+            e.printStackTrace();\n+        }\n+    }\n+\n+    /**\n+     * This method adds a shutdown hook to the VM. This hook deletes the given directory and\n+     * all its subdirectories/files.\n+     *\n+     * @param path\n+     *      The absolute path to the directory that should be deleted on shutdown.\n+     */\n+    private void recursiveDeleteOnShutdownHook(final Path path) {\n+        Runtime.getRuntime().addShutdownHook(new Thread(\n+                () -> {\n+                    LOGGER.info(\"About to delete \" + path.toString());\n+                    try {\n+                        Files.walkFileTree(path, new SimpleFileVisitor<Path>() {\n+                            @Override\n+                            public FileVisitResult visitFile(final Path file, @SuppressWarnings(\"unused\") BasicFileAttributes attrs)\n+                                    throws IOException {\n+                                Files.delete(file);\n+                                return FileVisitResult.CONTINUE;\n+                            }\n+\n+                            @Override\n+                            public FileVisitResult postVisitDirectory(final Path dir, final IOException e)\n+                                    throws IOException {\n+                                if (e == null) {\n+                                    Files.delete(dir);\n+                                    return FileVisitResult.CONTINUE;\n+                                }\n+                                // directory iteration failed\n+                                throw e;\n+                            }\n+                        });\n+                    } catch (IOException e) {\n+                        throw new RuntimeException(\"Failed to delete \" + path, e);\n+                    }\n+                }));\n+    }\n+\n+    /**\n+     * Returns all old or new files from the given list of {@link FileChange} objects. Only java source\n+     * files will be contained in the output. The parameter {@code oldFile} indicates if the old or new files\n+     * should be extracted.\n+     *\n+     * @param fileChanges\n+     *      The list of {@link FileChange} objects.\n+     * @param oldFile\n+     *      If true, the old files get extracted, otherwise the new files.\n+     * @return\n+     *      List of strings containing all paths to the old or new files\n+     */\n+    private List<String> getAllFilesFromFileChanges(final List<FileChange> fileChanges, final boolean oldFile) {\n+        return fileChanges\n+                .stream()\n+                .map(oldFile ? FileChange::getOldFile : FileChange::getNewFile)\n+                .map(vcsFile -> vcsFile.orElseThrow(IllegalArgumentException::new))\n+                .filter(sourceFile -> sourceFile.getPath().endsWith(\".java\"))\n+                .map(VCSFile::getPath)\n+                .collect(Collectors.toList());\n+    }\n+\n+    /**\n+     * See https://github.com/INRIA/spoon/pull/2622 for more information.\n+     * Recursively computes all expected binary (.class) files for {@code type}\n+     * and all its inner/anonymous types. This method is used as a utility\n+     * method by {@link #findPreviouslyNotCompiledSources()}.\n+     *\n+     * @param baseDir\n+     * \t\tThe base directory of {@code type}. That is, the directory where\n+     * \t\tthe binary files of {@code type} are stored.\n+     * @param nameOfParent\n+     * \t\tThe name of the binary file of the parent of {@code type} without\n+     * \t\tits extension (.class). For instance, Foo$Bar. Pass {@code null} or\n+     * \t\tan empty string if {@code type} has no parent.\n+     * @param type\n+     * \t\tThe root type to start the computation from.\n+     * @return\n+     * \t\tAll binary (.class) files that should be available for {@code type}\n+     * \t\tand\tall its inner/anonymous types.\n+     */\n+    private List<File> getExpectedBinaryFiles(final File baseDir, final String nameOfParent, final CtType<?> type) {\n+        final List<File> binaries = new ArrayList<>();\n+        final String name = nameOfParent == null || nameOfParent.isEmpty()\n+                ? type.getSimpleName()\n+                : nameOfParent + \"$\" + type.getSimpleName();\n+        binaries.add(new File(baseDir, name + \".class\"));\n+        // Use 'getElements()' rather than 'getNestedTypes()' to also fetch\n+        // anonymous types.\n+        type.getElements(new TypeFilter<>(CtType.class)).stream()\n+                // Exclude 'type' itself.\n+                .filter(inner -> !inner.equals(type))\n+                // Exclude types that do not generate a binary file.\n+                .filter(inner -> !(inner instanceof CtPackage)\n+                        && !(inner instanceof CtTypeParameter))\n+                // Include only direct inner types.\n+                .filter(inner -> inner.getParent(CtType.class).equals(type))\n+                .forEach(inner -> {\n+                    binaries.addAll(getExpectedBinaryFiles(\n+                            baseDir, name, inner));\n+                });\n+        return binaries;\n+    }\n+}","path":"libvcs4j-spoon/src/main/java/de/unibremen/informatik/st/libvcs4j/spoon/SpoonModel.java","position":336,"original_position":336,"commit_id":"76976d5f7b137e95852c624a4d12fbc8f18bd81d","original_commit_id":"76976d5f7b137e95852c624a4d12fbc8f18bd81d","user":{"login":"msteinbeck","id":317428,"node_id":"MDQ6VXNlcjMxNzQyOA==","avatar_url":"https://avatars3.githubusercontent.com/u/317428?v=4","gravatar_id":"","url":"https://api.github.com/users/msteinbeck","html_url":"https://github.com/msteinbeck","followers_url":"https://api.github.com/users/msteinbeck/followers","following_url":"https://api.github.com/users/msteinbeck/following{/other_user}","gists_url":"https://api.github.com/users/msteinbeck/gists{/gist_id}","starred_url":"https://api.github.com/users/msteinbeck/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/msteinbeck/subscriptions","organizations_url":"https://api.github.com/users/msteinbeck/orgs","repos_url":"https://api.github.com/users/msteinbeck/repos","events_url":"https://api.github.com/users/msteinbeck/events{/privacy}","received_events_url":"https://api.github.com/users/msteinbeck/received_events","type":"User","site_admin":false},"body":"missing EOL","created_at":"2018-10-22T13:49:53Z","updated_at":"2018-10-22T13:49:54Z","html_url":"https://github.com/uni-bremen-agst/libvcs4j/pull/5#discussion_r226993332","pull_request_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/5","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/comments/226993332"},"html":{"href":"https://github.com/uni-bremen-agst/libvcs4j/pull/5#discussion_r226993332"},"pull_request":{"href":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/5"}}},"pull_request":{"url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/5","id":224647001,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI0NjQ3MDAx","html_url":"https://github.com/uni-bremen-agst/libvcs4j/pull/5","diff_url":"https://github.com/uni-bremen-agst/libvcs4j/pull/5.diff","patch_url":"https://github.com/uni-bremen-agst/libvcs4j/pull/5.patch","issue_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/issues/5","number":5,"state":"open","locked":false,"title":"Incremental compilation with spoon model","user":{"login":"DosOvO","id":43536130,"node_id":"MDQ6VXNlcjQzNTM2MTMw","avatar_url":"https://avatars2.githubusercontent.com/u/43536130?v=4","gravatar_id":"","url":"https://api.github.com/users/DosOvO","html_url":"https://github.com/DosOvO","followers_url":"https://api.github.com/users/DosOvO/followers","following_url":"https://api.github.com/users/DosOvO/following{/other_user}","gists_url":"https://api.github.com/users/DosOvO/gists{/gist_id}","starred_url":"https://api.github.com/users/DosOvO/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DosOvO/subscriptions","organizations_url":"https://api.github.com/users/DosOvO/orgs","repos_url":"https://api.github.com/users/DosOvO/repos","events_url":"https://api.github.com/users/DosOvO/events{/privacy}","received_events_url":"https://api.github.com/users/DosOvO/received_events","type":"User","site_admin":false},"body":"This PR integrates a spoon module for libvcs4j. ","created_at":"2018-10-22T11:03:22Z","updated_at":"2018-10-22T13:49:54Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1f4e8b45a740ef3d829a146cc47135b44505b50a","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/5/commits","review_comments_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/5/comments","review_comment_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/comments{/number}","comments_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/issues/5/comments","statuses_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/statuses/76976d5f7b137e95852c624a4d12fbc8f18bd81d","head":{"label":"DosOvO:master","ref":"master","sha":"76976d5f7b137e95852c624a4d12fbc8f18bd81d","user":{"login":"DosOvO","id":43536130,"node_id":"MDQ6VXNlcjQzNTM2MTMw","avatar_url":"https://avatars2.githubusercontent.com/u/43536130?v=4","gravatar_id":"","url":"https://api.github.com/users/DosOvO","html_url":"https://github.com/DosOvO","followers_url":"https://api.github.com/users/DosOvO/followers","following_url":"https://api.github.com/users/DosOvO/following{/other_user}","gists_url":"https://api.github.com/users/DosOvO/gists{/gist_id}","starred_url":"https://api.github.com/users/DosOvO/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DosOvO/subscriptions","organizations_url":"https://api.github.com/users/DosOvO/orgs","repos_url":"https://api.github.com/users/DosOvO/repos","events_url":"https://api.github.com/users/DosOvO/events{/privacy}","received_events_url":"https://api.github.com/users/DosOvO/received_events","type":"User","site_admin":false},"repo":{"id":150079583,"node_id":"MDEwOlJlcG9zaXRvcnkxNTAwNzk1ODM=","name":"libvcs4j","full_name":"DosOvO/libvcs4j","private":false,"owner":{"login":"DosOvO","id":43536130,"node_id":"MDQ6VXNlcjQzNTM2MTMw","avatar_url":"https://avatars2.githubusercontent.com/u/43536130?v=4","gravatar_id":"","url":"https://api.github.com/users/DosOvO","html_url":"https://github.com/DosOvO","followers_url":"https://api.github.com/users/DosOvO/followers","following_url":"https://api.github.com/users/DosOvO/following{/other_user}","gists_url":"https://api.github.com/users/DosOvO/gists{/gist_id}","starred_url":"https://api.github.com/users/DosOvO/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DosOvO/subscriptions","organizations_url":"https://api.github.com/users/DosOvO/orgs","repos_url":"https://api.github.com/users/DosOvO/repos","events_url":"https://api.github.com/users/DosOvO/events{/privacy}","received_events_url":"https://api.github.com/users/DosOvO/received_events","type":"User","site_admin":false},"html_url":"https://github.com/DosOvO/libvcs4j","description":"A Java Library for Repository Mining","fork":true,"url":"https://api.github.com/repos/DosOvO/libvcs4j","forks_url":"https://api.github.com/repos/DosOvO/libvcs4j/forks","keys_url":"https://api.github.com/repos/DosOvO/libvcs4j/keys{/key_id}","collaborators_url":"https://api.github.com/repos/DosOvO/libvcs4j/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/DosOvO/libvcs4j/teams","hooks_url":"https://api.github.com/repos/DosOvO/libvcs4j/hooks","issue_events_url":"https://api.github.com/repos/DosOvO/libvcs4j/issues/events{/number}","events_url":"https://api.github.com/repos/DosOvO/libvcs4j/events","assignees_url":"https://api.github.com/repos/DosOvO/libvcs4j/assignees{/user}","branches_url":"https://api.github.com/repos/DosOvO/libvcs4j/branches{/branch}","tags_url":"https://api.github.com/repos/DosOvO/libvcs4j/tags","blobs_url":"https://api.github.com/repos/DosOvO/libvcs4j/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/DosOvO/libvcs4j/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/DosOvO/libvcs4j/git/refs{/sha}","trees_url":"https://api.github.com/repos/DosOvO/libvcs4j/git/trees{/sha}","statuses_url":"https://api.github.com/repos/DosOvO/libvcs4j/statuses/{sha}","languages_url":"https://api.github.com/repos/DosOvO/libvcs4j/languages","stargazers_url":"https://api.github.com/repos/DosOvO/libvcs4j/stargazers","contributors_url":"https://api.github.com/repos/DosOvO/libvcs4j/contributors","subscribers_url":"https://api.github.com/repos/DosOvO/libvcs4j/subscribers","subscription_url":"https://api.github.com/repos/DosOvO/libvcs4j/subscription","commits_url":"https://api.github.com/repos/DosOvO/libvcs4j/commits{/sha}","git_commits_url":"https://api.github.com/repos/DosOvO/libvcs4j/git/commits{/sha}","comments_url":"https://api.github.com/repos/DosOvO/libvcs4j/comments{/number}","issue_comment_url":"https://api.github.com/repos/DosOvO/libvcs4j/issues/comments{/number}","contents_url":"https://api.github.com/repos/DosOvO/libvcs4j/contents/{+path}","compare_url":"https://api.github.com/repos/DosOvO/libvcs4j/compare/{base}...{head}","merges_url":"https://api.github.com/repos/DosOvO/libvcs4j/merges","archive_url":"https://api.github.com/repos/DosOvO/libvcs4j/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/DosOvO/libvcs4j/downloads","issues_url":"https://api.github.com/repos/DosOvO/libvcs4j/issues{/number}","pulls_url":"https://api.github.com/repos/DosOvO/libvcs4j/pulls{/number}","milestones_url":"https://api.github.com/repos/DosOvO/libvcs4j/milestones{/number}","notifications_url":"https://api.github.com/repos/DosOvO/libvcs4j/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/DosOvO/libvcs4j/labels{/name}","releases_url":"https://api.github.com/repos/DosOvO/libvcs4j/releases{/id}","deployments_url":"https://api.github.com/repos/DosOvO/libvcs4j/deployments","created_at":"2018-09-24T09:23:26Z","updated_at":"2018-10-22T10:15:33Z","pushed_at":"2018-10-22T10:15:28Z","git_url":"git://github.com/DosOvO/libvcs4j.git","ssh_url":"git@github.com:DosOvO/libvcs4j.git","clone_url":"https://github.com/DosOvO/libvcs4j.git","svn_url":"https://github.com/DosOvO/libvcs4j","homepage":null,"size":14087,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"uni-bremen-agst:master","ref":"master","sha":"5f552eb14fcdb71b83308ee2ac0c8d6516a58b87","user":{"login":"uni-bremen-agst","id":4242616,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQyNDI2MTY=","avatar_url":"https://avatars2.githubusercontent.com/u/4242616?v=4","gravatar_id":"","url":"https://api.github.com/users/uni-bremen-agst","html_url":"https://github.com/uni-bremen-agst","followers_url":"https://api.github.com/users/uni-bremen-agst/followers","following_url":"https://api.github.com/users/uni-bremen-agst/following{/other_user}","gists_url":"https://api.github.com/users/uni-bremen-agst/gists{/gist_id}","starred_url":"https://api.github.com/users/uni-bremen-agst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/uni-bremen-agst/subscriptions","organizations_url":"https://api.github.com/users/uni-bremen-agst/orgs","repos_url":"https://api.github.com/users/uni-bremen-agst/repos","events_url":"https://api.github.com/users/uni-bremen-agst/events{/privacy}","received_events_url":"https://api.github.com/users/uni-bremen-agst/received_events","type":"Organization","site_admin":false},"repo":{"id":127565224,"node_id":"MDEwOlJlcG9zaXRvcnkxMjc1NjUyMjQ=","name":"libvcs4j","full_name":"uni-bremen-agst/libvcs4j","private":false,"owner":{"login":"uni-bremen-agst","id":4242616,"node_id":"MDEyOk9yZ2FuaXphdGlvbjQyNDI2MTY=","avatar_url":"https://avatars2.githubusercontent.com/u/4242616?v=4","gravatar_id":"","url":"https://api.github.com/users/uni-bremen-agst","html_url":"https://github.com/uni-bremen-agst","followers_url":"https://api.github.com/users/uni-bremen-agst/followers","following_url":"https://api.github.com/users/uni-bremen-agst/following{/other_user}","gists_url":"https://api.github.com/users/uni-bremen-agst/gists{/gist_id}","starred_url":"https://api.github.com/users/uni-bremen-agst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/uni-bremen-agst/subscriptions","organizations_url":"https://api.github.com/users/uni-bremen-agst/orgs","repos_url":"https://api.github.com/users/uni-bremen-agst/repos","events_url":"https://api.github.com/users/uni-bremen-agst/events{/privacy}","received_events_url":"https://api.github.com/users/uni-bremen-agst/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/uni-bremen-agst/libvcs4j","description":"A Java Library for Repository Mining","fork":false,"url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j","forks_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/forks","keys_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/keys{/key_id}","collaborators_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/teams","hooks_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/hooks","issue_events_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/issues/events{/number}","events_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/events","assignees_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/assignees{/user}","branches_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/branches{/branch}","tags_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/tags","blobs_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/git/refs{/sha}","trees_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/git/trees{/sha}","statuses_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/statuses/{sha}","languages_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/languages","stargazers_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/stargazers","contributors_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/contributors","subscribers_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/subscribers","subscription_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/subscription","commits_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/commits{/sha}","git_commits_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/git/commits{/sha}","comments_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/comments{/number}","issue_comment_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/issues/comments{/number}","contents_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/contents/{+path}","compare_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/compare/{base}...{head}","merges_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/merges","archive_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/downloads","issues_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/issues{/number}","pulls_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls{/number}","milestones_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/milestones{/number}","notifications_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/labels{/name}","releases_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/releases{/id}","deployments_url":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/deployments","created_at":"2018-03-31T19:42:04Z","updated_at":"2018-09-27T12:43:15Z","pushed_at":"2018-10-22T11:03:25Z","git_url":"git://github.com/uni-bremen-agst/libvcs4j.git","ssh_url":"git@github.com:uni-bremen-agst/libvcs4j.git","clone_url":"https://github.com/uni-bremen-agst/libvcs4j.git","svn_url":"https://github.com/uni-bremen-agst/libvcs4j","homepage":null,"size":14063,"stargazers_count":7,"watchers_count":7,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":4,"open_issues":1,"watchers":7,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/5"},"html":{"href":"https://github.com/uni-bremen-agst/libvcs4j/pull/5"},"issue":{"href":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/issues/5"},"comments":{"href":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/issues/5/comments"},"review_comments":{"href":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/5/comments"},"review_comment":{"href":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/pulls/5/commits"},"statuses":{"href":"https://api.github.com/repos/uni-bremen-agst/libvcs4j/statuses/76976d5f7b137e95852c624a4d12fbc8f18bd81d"}},"author_association":"NONE"}}
{"actor":{"display_login":"msteinbeck"}}