-
Notifications
You must be signed in to change notification settings - Fork 93
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
Changes from all commits
47fe0b5
085fecc
9798556
8521cb7
8751a3a
7a95ab5
2a5d0c5
4e15853
34e5252
c33d1f4
c596fcd
5243233
d12b2c6
59f9c07
e8b3dc9
6dc2df0
1eb9994
319851f
3ea9223
0179e7e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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 { | ||
|
@@ -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); | ||
|
@@ -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 { | ||
|
@@ -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(); | ||
|
@@ -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, | ||
|
@@ -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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is a tag on There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? |
||
} | ||
|
||
this.commitsToInclude = getDiffingCommits(from, to); | ||
|
||
final List<Ref> tagList = tagsBetweenFromAndTo(from, to); | ||
|
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; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?