Loading pom.xml +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ <distributionManagement> <repository> <id>inteligr8-public</id> <id>inteligr8-releases</id> <name>Inteligr8 Releases</name> <url>http://repos.yateslong.us/nexus/repository/inteligr8-public</url> <layout>default</layout> Loading src/main/java/com/inteligr8/git/CachedGit.java +10 −2 Original line number Diff line number Diff line package com.inteligr8.git; import java.util.List; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.URIish; public class CachedGit extends Git { Loading @@ -21,8 +25,12 @@ public class CachedGit extends Git { super(repo); } public String getFirstRemoteUrl() throws GitAPIException { return this.remoteList().call().iterator().next().getURIs().iterator().next().toString(); public URIish getRemoteUrl(String remoteName) throws GitAPIException { List<RemoteConfig> configs = this.remoteList().call(); for (RemoteConfig config : configs) if (config.getName().equals(remoteName)) return config.getURIs().iterator().next(); return null; } @Override Loading src/main/java/com/inteligr8/git/ExtendedGit.java +89 −55 Original line number Diff line number Diff line package com.inteligr8.git; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ListBranchCommand; import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode; import org.eclipse.jgit.api.ListBranchCommand.ListMode; import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.api.errors.CheckoutConflictException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRefNameException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.RefAlreadyExistsException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.URIish; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ExtendedGit extends CachedGit { private final Logger logger = LoggerFactory.getLogger(ExtendedGit.class); private final Pattern gitUrlPattern = Pattern.compile("(((ssh|http(s)?)://([^@]+@)?([^:/]+)(:[0-9]+)?/)|git@([^:]+):)([\\w\\.@\\:/\\-~]+)(\\.git)"); public ExtendedGit(Git git) { super(git.getRepository()); Loading @@ -47,64 +44,87 @@ public class ExtendedGit extends CachedGit { super(repo); } public String getRepositoryFullyQualifiedName() throws GitAPIException, URISyntaxException { String gitUrl = this.getFirstRemoteUrl(); if (this.logger.isDebugEnabled()) this.logger.debug("Remote URL: " + gitUrl); public Collection<RemoteConfig> findRemotesByHostname(String hostname) throws GitAPIException { List<RemoteConfig> matchingRemotes = new LinkedList<>(); Matcher matcher = this.gitUrlPattern.matcher(gitUrl); if (!matcher.find()) throw new URISyntaxException(gitUrl, "The Git URL does not match the expected regular expression: " + this.gitUrlPattern.toString()); return matcher.group(9); List<RemoteConfig> remotes = this.remoteList().call(); for (RemoteConfig remote : remotes) { for (URIish uri : remote.getURIs()) { GitUrl url = new GitUrl(uri); if (hostname.equalsIgnoreCase(url.getHostname())) { matchingRemotes.add(remote); break; } } } /** * Creates a local branch, if one does not already exist. * Checks out the repository, targeting the remote branch. * Resets the local branch up to the remote branch. */ public Ref normalize(String localBranchName, String exactRemoteBranchName) throws RefAlreadyExistsException, RefNotFoundException, InvalidRefNameException, CheckoutConflictException, GitAPIException, IOException { return matchingRemotes; } public Collection<String> findRemoteNames(String branchName) throws IOException, GitAPIException { List<String> remoteNames = new LinkedList<>(); List<RemoteConfig> remotes = this.remoteList().call(); for (RemoteConfig remote : remotes) if (this.getRepository().exactRef(Constants.R_REMOTES + remote.getName() + "/" + branchName) != null) remoteNames.add(remote.getName()); return remoteNames; } public Collection<Ref> findRemoteRefsByName(String branchName) throws IOException, GitAPIException { List<Ref> refs = new LinkedList<>(); List<RemoteConfig> remotes = this.remoteList().call(); for (RemoteConfig remote : remotes) { Ref ref = this.getRepository().exactRef(Constants.R_REMOTES + remote.getName() + "/" + branchName); if (ref != null) refs.add(ref); } return refs; } public Ref resetAndCheckout(Ref ref) throws IOException, GitAPIException { if (!ref.getName().startsWith(Constants.R_HEADS)) throw new IllegalArgumentException("The '" + ref + "' is not a local ref"); return this._resetAndCheckout(ref); } public Ref resetAndCheckout(String branchName) throws IOException, GitAPIException { if (this.logger.isTraceEnabled()) this.logger.trace("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "')"); this.logger.trace("resetOrCheckout('" + branchName + "')"); Ref localRef = this.getRepository().exactRef(Constants.R_HEADS + localBranchName); if (localRef == null) { if (this.logger.isDebugEnabled()) this.logger.debug("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "'): local branch does not yet exist"); localRef = this.branchCreate() .setUpstreamMode(SetupUpstreamMode.NOTRACK) .setName(localBranchName) .setStartPoint(exactRemoteBranchName) .call(); if (this.logger.isDebugEnabled()) this.logger.debug("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "'): created local branch: " + localRef.getName()); Ref localRef = this.getRepository().exactRef(Constants.R_HEADS + branchName); if (localRef == null) throw new IllegalArgumentException("The '" + branchName + "' branch must already exist"); return this._resetAndCheckout(localRef); } Ref checkoutRef = this.checkout() .setName(localBranchName) .setStartPoint(exactRemoteBranchName) .call(); if (this.logger.isDebugEnabled()) this.logger.debug("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "'): checked out"); if (!localRef.getObjectId().getName().equals(checkoutRef.getObjectId().getName())) { this.logger.warn("A checkout did not move the local branch to the proper commit; performing reset: " + localRef.getName()); this.reset() .setMode(ResetType.HARD) .setRef(exactRemoteBranchName) .call(); if (this.logger.isDebugEnabled()) this.logger.debug("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "'): reset"); private Ref _resetAndCheckout(Ref ref) throws IOException, GitAPIException { Ref localRef = ref; if (!this.status().call().isClean()) { if (this.logger.isTraceEnabled()) this.logger.trace("resetOrCheckout('" + ref + "'): repo is dirty"); localRef = this.reset().setMode(ResetType.HARD).call(); } if (!this.getRepository().getFullBranch().equals(localRef.getName())) { if (this.logger.isTraceEnabled()) this.logger.trace("resetOrCheckout('" + ref + "'): repo is on a different branch"); localRef = this.checkout().setName(localRef.getName()).call(); } return checkoutRef; return localRef; } /** * This method retrieves all branches, but excludes remote branches that are tracked with a local branch. * @return */ @Deprecated public List<Ref> getAllUniqueBranches() throws GitAPIException { return this.getAllUniqueBranches(null); } Loading @@ -113,6 +133,7 @@ public class ExtendedGit extends CachedGit { * This method retrieves all branches, but excludes remote branches that are tracked with a local branch. * @return */ @Deprecated public List<Ref> getAllUniqueBranches(String containsCommitish) throws GitAPIException { if (this.logger.isTraceEnabled()) this.logger.trace("getAllUniqueBranches('" + containsCommitish + "')"); Loading Loading @@ -182,4 +203,17 @@ public class ExtendedGit extends CachedGit { return trimmedBranches; } @Deprecated private boolean allSameCommit(Collection<Ref> refs) { if (refs.size() < 2) return true; Iterator<Ref> i = refs.iterator(); ObjectId commit = i.next().getObjectId(); while (i.hasNext()) if (!commit.equals(i.next().getObjectId())) return false; return true; } } src/main/java/com/inteligr8/git/GitUrl.java 0 → 100644 +44 −0 Original line number Diff line number Diff line package com.inteligr8.git; import java.net.URISyntaxException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jgit.transport.URIish; public class GitUrl { private static final Pattern gitUrlPattern = Pattern.compile("(((ssh|http(s)?)://([^@]+@)?([^:/]+)(:[0-9]+)?/)|git@([^:]+):)([\\w\\.@\\:/\\-~]+)(\\.git)"); private final String url; private final String hostname; private final String fullyQualifiedRepositoryName; public GitUrl(URIish url) { this.url = url.toString(); Matcher matcher = gitUrlPattern.matcher(url.toString()); if (!matcher.find()) throw new IllegalArgumentException("The '" + url + "' URL does not match the expected pattern: " + gitUrlPattern.toString()); this.hostname = matcher.group(6) != null ? matcher.group(6) : matcher.group(8); this.fullyQualifiedRepositoryName = matcher.group(9); } public GitUrl(String url) throws URISyntaxException { this(new URIish(url)); } public String getUrl() { return this.url; } public String getHostname() { return this.hostname; } public String getFullyQualifiedRepositoryName() { return this.fullyQualifiedRepositoryName; } } src/main/java/com/inteligr8/git/LocalRepositoryCache.java +40 −9 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.util.FileUtils; import org.slf4j.Logger; Loading @@ -36,13 +37,24 @@ public class LocalRepositoryCache { private final LRUExpiringHashMap<String, CachedGit> cachedGits = new LRUExpiringHashMap<>(30); private final Map<String, Semaphore> gitUrlSemaphores = new HashMap<>(); final File cacheDirectory = new File(System.getProperty("java.io.tmpdir"), "git"); private final int simultaneousProcessesPerGitRepo = 1; private int simultaneousThreadsPerGitRepo = 1; private LocalRepositoryCache() { this.cacheDirectory.mkdir(); this.cachedGits.addListener(new RepositoryCacheMapListener<CachedGit>()); } /** * This is helpful for the tweaking performance based on the environment of * execution. A value of 1 disables concurrency which results in the * persistence of Git Repositories between executions and disables cache * expiration. This is great for development, testing, and command line * usage. It is not recommended with web service frontends. */ public void setSimultaneousThreadsPerGitRepository(int threads) { this.simultaneousThreadsPerGitRepo = threads; } @Override protected void finalize() throws Throwable { try { Loading Loading @@ -91,17 +103,32 @@ public class LocalRepositoryCache { synchronized (this) { semaphore = this.gitUrlSemaphores.get(url); if (semaphore == null) this.gitUrlSemaphores.put(url, semaphore = new Semaphore(this.simultaneousProcessesPerGitRepo)); this.gitUrlSemaphores.put(url, semaphore = new Semaphore(this.simultaneousThreadsPerGitRepo)); } File gitRepoDirectory = null; semaphore.acquire(); try { CachedGit git = this.cachedGits.remove(url); if (git == null) { File gitRepoDirectory = new File(this.cacheDirectory, UUID.randomUUID().toString() + ".git"); String directoryBaseName; if (this.simultaneousThreadsPerGitRepo == 1) directoryBaseName = new GitUrl(url).getFullyQualifiedRepositoryName().replace('/', '_'); else directoryBaseName = UUID.randomUUID().toString(); gitRepoDirectory = new File(this.cacheDirectory, directoryBaseName + ".git"); if (this.logger.isDebugEnabled()) this.logger.debug("Git directory cache for clone: " + gitRepoDirectory); if (gitRepoDirectory.exists()) { if (this.logger.isInfoEnabled()) this.logger.info("Git Repository already exists; reusing: " + gitRepoDirectory); git = creds != null ? new CredentialedGit(Git.open(gitRepoDirectory), creds) : new ExtendedGit(Git.open(gitRepoDirectory)); } } if (git == null) { CloneCommand clone = new CloneCommand() .setURI(url) .setDirectory(gitRepoDirectory); Loading @@ -112,17 +139,21 @@ public class LocalRepositoryCache { this.logger.debug("Cloning Git repository: " + url); git = creds != null ? new CredentialedGit(clone, creds) : new ExtendedGit(clone); if (this.logger.isInfoEnabled()) this.logger.info("Cloned Git Repository: " + ((ExtendedGit)git).getRepositoryFullyQualifiedName()); this.logger.info("Cloned Git Repository: " + new GitUrl(git.getRemoteUrl("origin")).getFullyQualifiedRepositoryName()); } else { if (this.logger.isDebugEnabled()) this.logger.debug("reseting Git"); this.logger.debug("resetting Git"); git.reset().setMode(ResetType.HARD).call(); if (branch != null) { if (branch != null && !branch.equals(git.getRepository().getBranch())) { if (this.logger.isDebugEnabled()) this.logger.debug("switching Git branches: " + branch); if (git.getRepository().exactRef(Constants.R_HEADS + branch) == null) { git.checkout().setName(branch).setStartPoint("origin/" + branch).setCreateBranch(true).call(); } else { git.checkout().setName(branch).call(); } } if (this.logger.isDebugEnabled()) this.logger.debug("updating Git branch: " + git.getRepository().getBranch()); Loading @@ -132,7 +163,7 @@ public class LocalRepositoryCache { return git; } catch (URISyntaxException use) { semaphore.release(); throw new DeveloperException(this.logger, use); throw new IllegalArgumentException("The Git Repository URL is not properly formatted", use); } catch (IOException ie) { semaphore.release(); throw new TransportException("A I/O issue occurred", ie); Loading @@ -147,7 +178,7 @@ public class LocalRepositoryCache { this.logger.trace("release('" + git.getRepository().getIdentifier() + "')"); try { String url = git.getFirstRemoteUrl(); String url = git.getRemoteUrl("origin").toString(); synchronized (this) { Semaphore semaphore = this.gitUrlSemaphores.get(url); Loading Loading
pom.xml +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ <distributionManagement> <repository> <id>inteligr8-public</id> <id>inteligr8-releases</id> <name>Inteligr8 Releases</name> <url>http://repos.yateslong.us/nexus/repository/inteligr8-public</url> <layout>default</layout> Loading
src/main/java/com/inteligr8/git/CachedGit.java +10 −2 Original line number Diff line number Diff line package com.inteligr8.git; import java.util.List; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.URIish; public class CachedGit extends Git { Loading @@ -21,8 +25,12 @@ public class CachedGit extends Git { super(repo); } public String getFirstRemoteUrl() throws GitAPIException { return this.remoteList().call().iterator().next().getURIs().iterator().next().toString(); public URIish getRemoteUrl(String remoteName) throws GitAPIException { List<RemoteConfig> configs = this.remoteList().call(); for (RemoteConfig config : configs) if (config.getName().equals(remoteName)) return config.getURIs().iterator().next(); return null; } @Override Loading
src/main/java/com/inteligr8/git/ExtendedGit.java +89 −55 Original line number Diff line number Diff line package com.inteligr8.git; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ListBranchCommand; import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode; import org.eclipse.jgit.api.ListBranchCommand.ListMode; import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.api.errors.CheckoutConflictException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRefNameException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.RefAlreadyExistsException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.URIish; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ExtendedGit extends CachedGit { private final Logger logger = LoggerFactory.getLogger(ExtendedGit.class); private final Pattern gitUrlPattern = Pattern.compile("(((ssh|http(s)?)://([^@]+@)?([^:/]+)(:[0-9]+)?/)|git@([^:]+):)([\\w\\.@\\:/\\-~]+)(\\.git)"); public ExtendedGit(Git git) { super(git.getRepository()); Loading @@ -47,64 +44,87 @@ public class ExtendedGit extends CachedGit { super(repo); } public String getRepositoryFullyQualifiedName() throws GitAPIException, URISyntaxException { String gitUrl = this.getFirstRemoteUrl(); if (this.logger.isDebugEnabled()) this.logger.debug("Remote URL: " + gitUrl); public Collection<RemoteConfig> findRemotesByHostname(String hostname) throws GitAPIException { List<RemoteConfig> matchingRemotes = new LinkedList<>(); Matcher matcher = this.gitUrlPattern.matcher(gitUrl); if (!matcher.find()) throw new URISyntaxException(gitUrl, "The Git URL does not match the expected regular expression: " + this.gitUrlPattern.toString()); return matcher.group(9); List<RemoteConfig> remotes = this.remoteList().call(); for (RemoteConfig remote : remotes) { for (URIish uri : remote.getURIs()) { GitUrl url = new GitUrl(uri); if (hostname.equalsIgnoreCase(url.getHostname())) { matchingRemotes.add(remote); break; } } } /** * Creates a local branch, if one does not already exist. * Checks out the repository, targeting the remote branch. * Resets the local branch up to the remote branch. */ public Ref normalize(String localBranchName, String exactRemoteBranchName) throws RefAlreadyExistsException, RefNotFoundException, InvalidRefNameException, CheckoutConflictException, GitAPIException, IOException { return matchingRemotes; } public Collection<String> findRemoteNames(String branchName) throws IOException, GitAPIException { List<String> remoteNames = new LinkedList<>(); List<RemoteConfig> remotes = this.remoteList().call(); for (RemoteConfig remote : remotes) if (this.getRepository().exactRef(Constants.R_REMOTES + remote.getName() + "/" + branchName) != null) remoteNames.add(remote.getName()); return remoteNames; } public Collection<Ref> findRemoteRefsByName(String branchName) throws IOException, GitAPIException { List<Ref> refs = new LinkedList<>(); List<RemoteConfig> remotes = this.remoteList().call(); for (RemoteConfig remote : remotes) { Ref ref = this.getRepository().exactRef(Constants.R_REMOTES + remote.getName() + "/" + branchName); if (ref != null) refs.add(ref); } return refs; } public Ref resetAndCheckout(Ref ref) throws IOException, GitAPIException { if (!ref.getName().startsWith(Constants.R_HEADS)) throw new IllegalArgumentException("The '" + ref + "' is not a local ref"); return this._resetAndCheckout(ref); } public Ref resetAndCheckout(String branchName) throws IOException, GitAPIException { if (this.logger.isTraceEnabled()) this.logger.trace("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "')"); this.logger.trace("resetOrCheckout('" + branchName + "')"); Ref localRef = this.getRepository().exactRef(Constants.R_HEADS + localBranchName); if (localRef == null) { if (this.logger.isDebugEnabled()) this.logger.debug("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "'): local branch does not yet exist"); localRef = this.branchCreate() .setUpstreamMode(SetupUpstreamMode.NOTRACK) .setName(localBranchName) .setStartPoint(exactRemoteBranchName) .call(); if (this.logger.isDebugEnabled()) this.logger.debug("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "'): created local branch: " + localRef.getName()); Ref localRef = this.getRepository().exactRef(Constants.R_HEADS + branchName); if (localRef == null) throw new IllegalArgumentException("The '" + branchName + "' branch must already exist"); return this._resetAndCheckout(localRef); } Ref checkoutRef = this.checkout() .setName(localBranchName) .setStartPoint(exactRemoteBranchName) .call(); if (this.logger.isDebugEnabled()) this.logger.debug("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "'): checked out"); if (!localRef.getObjectId().getName().equals(checkoutRef.getObjectId().getName())) { this.logger.warn("A checkout did not move the local branch to the proper commit; performing reset: " + localRef.getName()); this.reset() .setMode(ResetType.HARD) .setRef(exactRemoteBranchName) .call(); if (this.logger.isDebugEnabled()) this.logger.debug("normalize('" + localBranchName + "', '" + exactRemoteBranchName + "'): reset"); private Ref _resetAndCheckout(Ref ref) throws IOException, GitAPIException { Ref localRef = ref; if (!this.status().call().isClean()) { if (this.logger.isTraceEnabled()) this.logger.trace("resetOrCheckout('" + ref + "'): repo is dirty"); localRef = this.reset().setMode(ResetType.HARD).call(); } if (!this.getRepository().getFullBranch().equals(localRef.getName())) { if (this.logger.isTraceEnabled()) this.logger.trace("resetOrCheckout('" + ref + "'): repo is on a different branch"); localRef = this.checkout().setName(localRef.getName()).call(); } return checkoutRef; return localRef; } /** * This method retrieves all branches, but excludes remote branches that are tracked with a local branch. * @return */ @Deprecated public List<Ref> getAllUniqueBranches() throws GitAPIException { return this.getAllUniqueBranches(null); } Loading @@ -113,6 +133,7 @@ public class ExtendedGit extends CachedGit { * This method retrieves all branches, but excludes remote branches that are tracked with a local branch. * @return */ @Deprecated public List<Ref> getAllUniqueBranches(String containsCommitish) throws GitAPIException { if (this.logger.isTraceEnabled()) this.logger.trace("getAllUniqueBranches('" + containsCommitish + "')"); Loading Loading @@ -182,4 +203,17 @@ public class ExtendedGit extends CachedGit { return trimmedBranches; } @Deprecated private boolean allSameCommit(Collection<Ref> refs) { if (refs.size() < 2) return true; Iterator<Ref> i = refs.iterator(); ObjectId commit = i.next().getObjectId(); while (i.hasNext()) if (!commit.equals(i.next().getObjectId())) return false; return true; } }
src/main/java/com/inteligr8/git/GitUrl.java 0 → 100644 +44 −0 Original line number Diff line number Diff line package com.inteligr8.git; import java.net.URISyntaxException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jgit.transport.URIish; public class GitUrl { private static final Pattern gitUrlPattern = Pattern.compile("(((ssh|http(s)?)://([^@]+@)?([^:/]+)(:[0-9]+)?/)|git@([^:]+):)([\\w\\.@\\:/\\-~]+)(\\.git)"); private final String url; private final String hostname; private final String fullyQualifiedRepositoryName; public GitUrl(URIish url) { this.url = url.toString(); Matcher matcher = gitUrlPattern.matcher(url.toString()); if (!matcher.find()) throw new IllegalArgumentException("The '" + url + "' URL does not match the expected pattern: " + gitUrlPattern.toString()); this.hostname = matcher.group(6) != null ? matcher.group(6) : matcher.group(8); this.fullyQualifiedRepositoryName = matcher.group(9); } public GitUrl(String url) throws URISyntaxException { this(new URIish(url)); } public String getUrl() { return this.url; } public String getHostname() { return this.hostname; } public String getFullyQualifiedRepositoryName() { return this.fullyQualifiedRepositoryName; } }
src/main/java/com/inteligr8/git/LocalRepositoryCache.java +40 −9 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.util.FileUtils; import org.slf4j.Logger; Loading @@ -36,13 +37,24 @@ public class LocalRepositoryCache { private final LRUExpiringHashMap<String, CachedGit> cachedGits = new LRUExpiringHashMap<>(30); private final Map<String, Semaphore> gitUrlSemaphores = new HashMap<>(); final File cacheDirectory = new File(System.getProperty("java.io.tmpdir"), "git"); private final int simultaneousProcessesPerGitRepo = 1; private int simultaneousThreadsPerGitRepo = 1; private LocalRepositoryCache() { this.cacheDirectory.mkdir(); this.cachedGits.addListener(new RepositoryCacheMapListener<CachedGit>()); } /** * This is helpful for the tweaking performance based on the environment of * execution. A value of 1 disables concurrency which results in the * persistence of Git Repositories between executions and disables cache * expiration. This is great for development, testing, and command line * usage. It is not recommended with web service frontends. */ public void setSimultaneousThreadsPerGitRepository(int threads) { this.simultaneousThreadsPerGitRepo = threads; } @Override protected void finalize() throws Throwable { try { Loading Loading @@ -91,17 +103,32 @@ public class LocalRepositoryCache { synchronized (this) { semaphore = this.gitUrlSemaphores.get(url); if (semaphore == null) this.gitUrlSemaphores.put(url, semaphore = new Semaphore(this.simultaneousProcessesPerGitRepo)); this.gitUrlSemaphores.put(url, semaphore = new Semaphore(this.simultaneousThreadsPerGitRepo)); } File gitRepoDirectory = null; semaphore.acquire(); try { CachedGit git = this.cachedGits.remove(url); if (git == null) { File gitRepoDirectory = new File(this.cacheDirectory, UUID.randomUUID().toString() + ".git"); String directoryBaseName; if (this.simultaneousThreadsPerGitRepo == 1) directoryBaseName = new GitUrl(url).getFullyQualifiedRepositoryName().replace('/', '_'); else directoryBaseName = UUID.randomUUID().toString(); gitRepoDirectory = new File(this.cacheDirectory, directoryBaseName + ".git"); if (this.logger.isDebugEnabled()) this.logger.debug("Git directory cache for clone: " + gitRepoDirectory); if (gitRepoDirectory.exists()) { if (this.logger.isInfoEnabled()) this.logger.info("Git Repository already exists; reusing: " + gitRepoDirectory); git = creds != null ? new CredentialedGit(Git.open(gitRepoDirectory), creds) : new ExtendedGit(Git.open(gitRepoDirectory)); } } if (git == null) { CloneCommand clone = new CloneCommand() .setURI(url) .setDirectory(gitRepoDirectory); Loading @@ -112,17 +139,21 @@ public class LocalRepositoryCache { this.logger.debug("Cloning Git repository: " + url); git = creds != null ? new CredentialedGit(clone, creds) : new ExtendedGit(clone); if (this.logger.isInfoEnabled()) this.logger.info("Cloned Git Repository: " + ((ExtendedGit)git).getRepositoryFullyQualifiedName()); this.logger.info("Cloned Git Repository: " + new GitUrl(git.getRemoteUrl("origin")).getFullyQualifiedRepositoryName()); } else { if (this.logger.isDebugEnabled()) this.logger.debug("reseting Git"); this.logger.debug("resetting Git"); git.reset().setMode(ResetType.HARD).call(); if (branch != null) { if (branch != null && !branch.equals(git.getRepository().getBranch())) { if (this.logger.isDebugEnabled()) this.logger.debug("switching Git branches: " + branch); if (git.getRepository().exactRef(Constants.R_HEADS + branch) == null) { git.checkout().setName(branch).setStartPoint("origin/" + branch).setCreateBranch(true).call(); } else { git.checkout().setName(branch).call(); } } if (this.logger.isDebugEnabled()) this.logger.debug("updating Git branch: " + git.getRepository().getBranch()); Loading @@ -132,7 +163,7 @@ public class LocalRepositoryCache { return git; } catch (URISyntaxException use) { semaphore.release(); throw new DeveloperException(this.logger, use); throw new IllegalArgumentException("The Git Repository URL is not properly formatted", use); } catch (IOException ie) { semaphore.release(); throw new TransportException("A I/O issue occurred", ie); Loading @@ -147,7 +178,7 @@ public class LocalRepositoryCache { this.logger.trace("release('" + git.getRepository().getIdentifier() + "')"); try { String url = git.getFirstRemoteUrl(); String url = git.getRemoteUrl("origin").toString(); synchronized (this) { Semaphore semaphore = this.gitUrlSemaphores.get(url); Loading