diff --git a/src/main/java/com/inteligr8/git/ExtendedGit.java b/src/main/java/com/inteligr8/git/ExtendedGit.java index 427f5fb..9e2f185 100644 --- a/src/main/java/com/inteligr8/git/ExtendedGit.java +++ b/src/main/java/com/inteligr8/git/ExtendedGit.java @@ -31,9 +31,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ExtendedGit extends CachedGit { - + + private static final Pattern gitUrlPattern = Pattern.compile("(((ssh|http(s)?)://([^@]+@)?([^:/]+)(:[0-9]+)?/)|git@([^:]+):)([\\w\\.@\\:/\\-~]+)(\\.git)"); 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()); @@ -51,10 +51,13 @@ public class ExtendedGit extends CachedGit { String gitUrl = this.getFirstRemoteUrl(); if (this.logger.isDebugEnabled()) this.logger.debug("Remote URL: " + gitUrl); - - Matcher matcher = this.gitUrlPattern.matcher(gitUrl); + return getRepositoryFullyQualifiedName(gitUrl); + } + + public static String getRepositoryFullyQualifiedName(String gitUrl) throws URISyntaxException { + Matcher matcher = gitUrlPattern.matcher(gitUrl); if (!matcher.find()) - throw new URISyntaxException(gitUrl, "The Git URL does not match the expected regular expression: " + this.gitUrlPattern.toString()); + throw new URISyntaxException(gitUrl, "The Git URL does not match the expected regular expression: " + gitUrlPattern.toString()); return matcher.group(9); } diff --git a/src/main/java/com/inteligr8/git/LocalRepositoryCache.java b/src/main/java/com/inteligr8/git/LocalRepositoryCache.java index 1b1c07d..5302d46 100644 --- a/src/main/java/com/inteligr8/git/LocalRepositoryCache.java +++ b/src/main/java/com/inteligr8/git/LocalRepositoryCache.java @@ -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; @@ -36,13 +37,24 @@ public class LocalRepositoryCache { private final LRUExpiringHashMap cachedGits = new LRUExpiringHashMap<>(30); private final Map 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()); } + /** + * 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 { @@ -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 = ExtendedGit.getRepositoryFullyQualifiedName(url).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); @@ -115,13 +142,17 @@ public class LocalRepositoryCache { this.logger.info("Cloned Git Repository: " + ((ExtendedGit)git).getRepositoryFullyQualifiedName()); } 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); - git.checkout().setName(branch).call(); + 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())