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 15 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
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;
}
}
109 changes: 94 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 = null;
Copy link
Owner

Choose a reason for hiding this comment

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

I would prefer an empty map here, not null

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

}

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

if (SubmoduleWalk.containsGitModulesFile(repository)) {
this.submodules = new HashMap<>();
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();
}
}
} else {
this.submodules = null;
Copy link
Owner

Choose a reason for hiding this comment

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

Should not be needed.

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

}

} catch (final IOException e) {
throw new GitChangelogRepositoryException(
"Could not use GIT repo in " + repo.getAbsolutePath(), e);
Expand All @@ -89,6 +105,11 @@ public void close() throws IOException {
LOG.error(e.getMessage(), e);
}
}
if (submodules != null) {
Copy link
Owner

Choose a reason for hiding this comment

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

This if is not needed, if changing to empty map as default value.

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

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 +163,45 @@ public ObjectId getRef(final String fromRef) throws GitChangelogRepositoryExcept
throw new GitChangelogRepositoryException(fromRef + " not found in:\n" + toString());
}

public boolean hasSubmodules() {
return submodules != null && submodules.size() > 0;
Copy link
Owner

Choose a reason for hiding this comment

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

This null-check is not needed, if changing to empty map as default value.

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

}

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

public String getDiff(String commitHash) {
RevCommit commit = null;
RevCommit prevCommit = null;

try {
commit = this.revWalk.parseCommit(getCommit(commitHash));
prevCommit = commit.getParentCount() > 0 ? commit.getParent(0) : null;
} catch (GitChangelogRepositoryException | IOException e) {
e.printStackTrace();
Copy link
Owner

Choose a reason for hiding this comment

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

throw GitChangelogRepositoryException

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

}

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) {
e.printStackTrace();
Copy link
Owner

Choose a reason for hiding this comment

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

throw GitChangelogRepositoryException

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

}

return outputStream.toString();
}

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

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

private CanonicalTreeParser getTreeParser(RevCommit commit) {
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) {
e.printStackTrace();
Copy link
Owner

Choose a reason for hiding this comment

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

throw GitChangelogRepositoryException

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

}
return null;
}

private boolean addCommitToCurrentTag(
final Map<String, Set<GitCommit>> commitsPerTagName,
final String currentTagName,
Expand Down Expand Up @@ -291,6 +366,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,110 @@
package se.bjurr.gitchangelog.internal.git;

import static org.slf4j.LoggerFactory.getLogger;

import com.google.common.base.Optional;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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) {

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+)$");

Settings settings = gitChangelogApi.getSettings();

Optional<String> cachedFromCommit = settings.getFromCommit();
Optional<String> cachedToCommit = settings.getToCommit();
Optional<String> cachedFromRef = settings.getFromRef();
Optional<String> cachedToRef = settings.getToRef();
String cachedFromRepo = settings.getFromRepo();

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;
}
}

for (Map.Entry<String, SubmoduleEntry> submoduleEntry : submoduleEntries.entrySet()) {
settings.setFromCommit(submoduleEntry.getValue().previousSubmoduleHash);
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 changing the settings object, and later change it back. I would rather like a new instance created and used.

Copy link
Author

Choose a reason for hiding this comment

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

Switched to cloning the settings object via serialization utils.

settings.setToCommit(submoduleEntry.getValue().currentSubmoduleHash);
settings.setFromRef(null);
settings.setToRef(null);
settings.setFromRepo(submoduleEntry.getValue().gitRepo.getDirectory());

try {
submodules.add(
GitChangelogApi.gitChangelogApiBuilder()
.withSettings(settings)
.getChangelog(useIntegrationIfConfigured));
} catch (GitChangelogRepositoryException e) {
e.printStackTrace();
Copy link
Owner

Choose a reason for hiding this comment

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

throw GitChangelogRepositoryException

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

}
}

settings.setFromCommit(cachedFromCommit.orNull());
settings.setToCommit(cachedToCommit.orNull());
settings.setFromRef(cachedFromRef.orNull());
settings.setToRef(cachedToRef.orNull());
settings.setFromRepo(cachedFromRepo);

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