Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Submodule support #79

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies {
compile 'org.eclipse.jgit:org.eclipse.jgit:3.6.2.201501210735-r'
compile 'com.github.spullara.mustache.java:compiler:0.8.18'
compile 'org.gitlab:java-gitlab-api:4.1.0'
compile 'org.apache.commons:commons-lang3:3.9'

compileOnly 'com.github.spotbugs:spotbugs-annotations:3.1.12'

Expand Down
20 changes: 17 additions & 3 deletions src/main/java/se/bjurr/gitchangelog/api/GitChangelogApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,15 @@
import java.io.Writer;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.*;
import org.eclipse.jgit.lib.ObjectId;
import se.bjurr.gitchangelog.api.exceptions.GitChangelogIntegrationException;
import se.bjurr.gitchangelog.api.exceptions.GitChangelogRepositoryException;
import se.bjurr.gitchangelog.api.model.Changelog;
import se.bjurr.gitchangelog.api.model.Issue;
import se.bjurr.gitchangelog.internal.git.GitRepo;
import se.bjurr.gitchangelog.internal.git.GitRepoData;
import se.bjurr.gitchangelog.internal.git.GitSubmoduleParser;
import se.bjurr.gitchangelog.internal.git.model.GitCommit;
import se.bjurr.gitchangelog.internal.git.model.GitTag;
import se.bjurr.gitchangelog.internal.integrations.mediawiki.MediaWikiClient;
Expand Down Expand Up @@ -380,6 +379,12 @@ public GitChangelogApi withSettings(final URL url) {
return this;
}

/** {@link Settings}. */
public GitChangelogApi withSettings(final Settings settings) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont like to expose Settings in the API.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way to provide an existing Settings object?

this.settings = settings;
return this;
}

/** Use string as template. {@link #withTemplatePath}. */
public GitChangelogApi withTemplateContent(final String templateContent) {
this.templateContent = templateContent;
Expand Down Expand Up @@ -465,13 +470,22 @@ private Changelog getChangelog(final GitRepo gitRepo, final boolean useIntegrati
diff = gitRepoData.getGitCommits();
}
final List<GitTag> tags = gitRepoData.getGitTags();

List<Changelog> submodules = new ArrayList<>();
if (gitRepo.hasSubmodules()) {
submodules =
new GitSubmoduleParser()
.parseForSubmodules(this, useIntegrationIfConfigured, gitRepo, diff);
}

final Transformer transformer = new Transformer(this.settings);
return new Changelog( //
transformer.toCommits(diff), //
transformer.toTags(tags, issues), //
transformer.toAuthors(diff), //
transformer.toIssues(issues), //
transformer.toIssueTypes(issues), //
submodules, //
gitRepoData.findOwnerName().orNull(), //
gitRepoData.findRepoName().orNull());
}
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/se/bjurr/gitchangelog/api/model/Changelog.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
import se.bjurr.gitchangelog.api.model.interfaces.IIssues;

public class Changelog implements ICommits, IAuthors, IIssues, Serializable {
private static final long serialVersionUID = 2193789018496738737L;
private static final long serialVersionUID = 2193789018496738738L;
private final List<Commit> commits;
private final List<Tag> tags;
private final List<Author> authors;
private final List<Issue> issues;
private final List<IssueType> issueTypes;
private final List<Changelog> submodules;
private final String ownerName;
private final String repoName;

Expand All @@ -24,13 +25,15 @@ public Changelog(
List<Author> authors,
List<Issue> issues,
List<IssueType> issueTypes,
List<Changelog> submodules,
String ownerName,
String repoName) {
this.commits = checkNotNull(commits, "commits");
this.tags = checkNotNull(tags, "tags");
this.authors = checkNotNull(authors, "authors");
this.issues = checkNotNull(issues, "issues");
this.issueTypes = checkNotNull(issueTypes, "issueTypes");
this.submodules = checkNotNull(submodules, "submodules");
this.ownerName = ownerName;
this.repoName = repoName;
}
Expand Down Expand Up @@ -65,4 +68,8 @@ public List<Tag> getTags() {
public List<IssueType> getIssueTypes() {
return issueTypes;
}

public List<Changelog> getSubmodules() {
return submodules;
}
}
105 changes: 90 additions & 15 deletions src/main/java/se/bjurr/gitchangelog/internal/git/GitRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,20 @@

import com.google.common.base.Optional;
import com.google.common.collect.Ordering;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.io.*;
import java.util.*;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.bjurr.gitchangelog.api.GitChangelogApiConstants;
Expand All @@ -47,10 +41,12 @@ public class GitRepo implements Closeable {
private Git git;
private final Repository repository;
private final RevWalk revWalk;
private final HashMap<String, GitRepo> submodules;

public GitRepo() {
this.repository = null;
this.revWalk = null;
this.submodules = new HashMap<>();
}

public GitRepo(final File repo) throws GitChangelogRepositoryException {
Expand All @@ -71,6 +67,23 @@ public GitRepo(final File repo) throws GitChangelogRepositoryException {
this.repository = builder.build();
this.revWalk = new RevWalk(this.repository);
this.git = new Git(this.repository);

this.submodules = new HashMap<>();
if (SubmoduleWalk.containsGitModulesFile(repository)) {
final SubmoduleWalk submoduleWalk = SubmoduleWalk.forIndex(repository);
while (submoduleWalk.next()) {
final Repository submoduleRepository = submoduleWalk.getRepository();
if (submoduleRepository != null) {
try {
String submodulePath = submoduleWalk.getModulesPath();
this.submodules.put(submodulePath, new GitRepo(submoduleRepository.getDirectory()));
} catch (ConfigInvalidException e) {
LOG.warn("invalid submodule configuration; skipping submodule\n" + e);
}
submoduleRepository.close();
}
}
}
} catch (final IOException e) {
throw new GitChangelogRepositoryException(
"Could not use GIT repo in " + repo.getAbsolutePath(), e);
Expand All @@ -89,6 +102,10 @@ public void close() throws IOException {
LOG.error(e.getMessage(), e);
}
}

for (Map.Entry<String, GitRepo> submodule : submodules.entrySet()) {
submodule.getValue().close();
}
}

public ObjectId getCommit(final String fromCommit) throws GitChangelogRepositoryException {
Expand Down Expand Up @@ -142,6 +159,45 @@ public ObjectId getRef(final String fromRef) throws GitChangelogRepositoryExcept
throw new GitChangelogRepositoryException(fromRef + " not found in:\n" + toString());
}

public boolean hasSubmodules() {
return submodules.size() > 0;
}

public GitRepo getSubmodule(String submodulePath) {
return submodules.getOrDefault(submodulePath, null);
}

public String getDiff(String commitHash) throws GitChangelogRepositoryException {
RevCommit commit;
RevCommit prevCommit;

try {
commit = this.revWalk.parseCommit(getCommit(commitHash));
prevCommit = commit.getParentCount() > 0 ? commit.getParent(0) : null;
} catch (GitChangelogRepositoryException | IOException e) {
throw new GitChangelogRepositoryException("", e);
}

OutputStream outputStream = new ByteArrayOutputStream();
DiffFormatter diffFormatter = new DiffFormatter(outputStream);
diffFormatter.setRepository(this.repository);
diffFormatter.setAbbreviationLength(10);

try {
for (DiffEntry entry : diffFormatter.scan(prevCommit, commit)) {
diffFormatter.format(diffFormatter.toFileHeader(entry));
}
} catch (IOException e) {
throw new GitChangelogRepositoryException("", e);
}

return outputStream.toString();
}

public String getDirectory() {
return this.repository.getDirectory().getAbsolutePath();
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
Expand All @@ -151,6 +207,21 @@ public String toString() {
return "Repo: " + this.repository + "\n" + sb.toString();
}

private CanonicalTreeParser getTreeParser(RevCommit commit)
throws GitChangelogRepositoryException {
RevTree revTree = commit.getTree();
if (revTree == null) {
return null;
}
ObjectId treeId = commit.getTree().getId();
ObjectReader reader = this.repository.newObjectReader();
try {
return new CanonicalTreeParser(null, reader, treeId);
} catch (IOException e) {
throw new GitChangelogRepositoryException("", e);
}
}

private boolean addCommitToCurrentTag(
final Map<String, Set<GitCommit>> commitsPerTagName,
final String currentTagName,
Expand Down Expand Up @@ -291,6 +362,10 @@ private List<GitTag> gitTags(
final RevCommit from = this.revWalk.lookupCommit(fromObjectId);
final RevCommit to = this.revWalk.lookupCommit(toObjectId);

if (from == to) {
return newArrayList();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a tag on from then this list should not be empty.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally speaking - yes, but it looks like we are only interested in the diff between the two commits in this case?
If the tag of a single from commit, which is also the to commit, is returned, we get a non-empty diff, just like in the #62 description.

}

this.commitsToInclude = getDiffingCommits(from, to);

final List<Ref> tagList = tagsBetweenFromAndTo(from, to);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package se.bjurr.gitchangelog.internal.git;

import static org.slf4j.LoggerFactory.getLogger;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.SerializationUtils;
import org.slf4j.Logger;
import se.bjurr.gitchangelog.api.GitChangelogApi;
import se.bjurr.gitchangelog.api.exceptions.GitChangelogRepositoryException;
import se.bjurr.gitchangelog.api.model.Changelog;
import se.bjurr.gitchangelog.internal.git.model.GitCommit;
import se.bjurr.gitchangelog.internal.settings.Settings;

public class GitSubmoduleParser {
private static final Logger LOG = getLogger(GitSubmoduleParser.class);

public GitSubmoduleParser() {}

public List<Changelog> parseForSubmodules(
final GitChangelogApi gitChangelogApi,
final boolean useIntegrationIfConfigured,
final GitRepo gitRepo,
final List<GitCommit> commits)
throws GitChangelogRepositoryException {

List<Changelog> submodules = new ArrayList<>();
Map<String, SubmoduleEntry> submoduleEntries = new TreeMap<>();
Pattern submoduleNamePattern =
Pattern.compile(
"(?m)^\\+{3} b/([\\w/\\s-]+)($\\n@.+)?\\n-Subproject commit (\\w+)$\\n\\+Subproject commit (\\w+)$");

for (GitCommit commit : commits) {
String diff = gitRepo.getDiff(commit.getHash());
Matcher submoduleMatch = submoduleNamePattern.matcher(diff);
while (submoduleMatch.find()) {
String submoduleName = submoduleMatch.group(1);
String previousSubmoduleHash = submoduleMatch.group(3);
String currentSubmoduleHash = submoduleMatch.group(4);
GitRepo submodule = gitRepo.getSubmodule(submoduleName);

if (submodule == null) {
continue;
}

SubmoduleEntry submoduleEntry =
new SubmoduleEntry(
submoduleName, previousSubmoduleHash, currentSubmoduleHash, submodule);

if (!submoduleEntries.containsKey(submoduleName)) {
submoduleEntries.put(submoduleName, submoduleEntry);
}

SubmoduleEntry existingEntry = submoduleEntries.getOrDefault(submoduleName, submoduleEntry);
existingEntry.previousSubmoduleHash = submoduleEntry.previousSubmoduleHash;
}
}

Settings settings = gitChangelogApi.getSettings();
for (Map.Entry<String, SubmoduleEntry> submoduleEntry : submoduleEntries.entrySet()) {
Settings submoduleSettings = SerializationUtils.clone(settings);

submoduleSettings.setFromCommit(submoduleEntry.getValue().previousSubmoduleHash);
submoduleSettings.setToCommit(submoduleEntry.getValue().currentSubmoduleHash);
submoduleSettings.setFromRef(null);
submoduleSettings.setToRef(null);
submoduleSettings.setFromRepo(submoduleEntry.getValue().gitRepo.getDirectory());

try {
submodules.add(
GitChangelogApi.gitChangelogApiBuilder()
.withSettings(submoduleSettings)
.getChangelog(useIntegrationIfConfigured));
} catch (GitChangelogRepositoryException e) {
throw new GitChangelogRepositoryException("", e);
}
}

return submodules;
}

private class SubmoduleEntry {
public final String name;
public String previousSubmoduleHash;
public final String currentSubmoduleHash;
public final GitRepo gitRepo;

public SubmoduleEntry(
final String name,
final String previousSubmoduleHash,
final String currentSubmoduleHash,
final GitRepo gitRepo) {
this.name = name;
this.previousSubmoduleHash = previousSubmoduleHash;
this.currentSubmoduleHash = currentSubmoduleHash;
this.gitRepo = gitRepo;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,9 @@
import com.google.common.base.Predicate;
import com.google.common.collect.Multimap;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.regex.Matcher;
import se.bjurr.gitchangelog.api.model.Author;
import se.bjurr.gitchangelog.api.model.Commit;
import se.bjurr.gitchangelog.api.model.Issue;
import se.bjurr.gitchangelog.api.model.IssueType;
import se.bjurr.gitchangelog.api.model.Tag;
import se.bjurr.gitchangelog.api.model.*;
import se.bjurr.gitchangelog.internal.git.model.GitCommit;
import se.bjurr.gitchangelog.internal.git.model.GitTag;
import se.bjurr.gitchangelog.internal.settings.IssuesUtil;
Expand Down
Loading