initial checkin
This commit is contained in:
commit
a40fec7b8f
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# Maven
|
||||
target
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
74
pom.xml
Normal file
74
pom.xml
Normal file
@ -0,0 +1,74 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>me.brianlong</groupId>
|
||||
<artifactId>git-utils</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>Git Utilities</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>5.9.0.202009080501-r</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
|
||||
<version>5.9.0.202009080501-r</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>2.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/*IT.class</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration-tests</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*IT.class</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
16
src/jetty/log4j2.xml
Normal file
16
src/jetty/log4j2.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="[%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="me.brianlong" level="trace" additivity="false">
|
||||
<AppenderRef ref="STDOUT" />
|
||||
</Logger>
|
||||
<Root level="trace">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
29
src/main/java/me/brianlong/git/CachedGit.java
Normal file
29
src/main/java/me/brianlong/git/CachedGit.java
Normal file
@ -0,0 +1,29 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
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;
|
||||
|
||||
public class CachedGit extends Git {
|
||||
|
||||
public CachedGit(Git git) {
|
||||
super(git.getRepository());
|
||||
}
|
||||
|
||||
public CachedGit(CloneCommand clone) throws TransportException, InvalidRemoteException, GitAPIException {
|
||||
super(clone.call().getRepository());
|
||||
}
|
||||
|
||||
public CachedGit(Repository repo) {
|
||||
super(repo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
LocalRepositoryCache.getInstance().release(this);
|
||||
}
|
||||
|
||||
}
|
42
src/main/java/me/brianlong/git/CompositeIterator.java
Normal file
42
src/main/java/me/brianlong/git/CompositeIterator.java
Normal file
@ -0,0 +1,42 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class CompositeIterator<E> implements Iterator<E> {
|
||||
|
||||
private final List<Iterator<E>> iterators = new LinkedList<Iterator<E>>();
|
||||
private final Iterator<Iterator<E>> iteratorIterators;
|
||||
private Iterator<E> iterator;
|
||||
|
||||
public CompositeIterator(@SuppressWarnings("unchecked") Collection<E>... cs) {
|
||||
for (Collection<E> c : cs)
|
||||
if (c != null && !c.isEmpty())
|
||||
this.iterators.add(c.iterator());
|
||||
this.iteratorIterators = this.iterators.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (this.iterator == null) {
|
||||
if (!this.iteratorIterators.hasNext())
|
||||
return false;
|
||||
this.iterator = this.iteratorIterators.next();
|
||||
}
|
||||
|
||||
while (!this.iterator.hasNext() && this.iteratorIterators.hasNext())
|
||||
this.iterator = this.iteratorIterators.next();
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
if (!this.hasNext())
|
||||
throw new NoSuchElementException();
|
||||
return this.iterator.next();
|
||||
}
|
||||
|
||||
}
|
53
src/main/java/me/brianlong/git/CredentialedGit.java
Normal file
53
src/main/java/me/brianlong/git/CredentialedGit.java
Normal file
@ -0,0 +1,53 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import org.eclipse.jgit.api.CloneCommand;
|
||||
import org.eclipse.jgit.api.FetchCommand;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.LsRemoteCommand;
|
||||
import org.eclipse.jgit.api.PullCommand;
|
||||
import org.eclipse.jgit.api.PushCommand;
|
||||
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.CredentialsProvider;
|
||||
|
||||
public class CredentialedGit extends ExtendedGit {
|
||||
|
||||
private CredentialsProvider credProvider;
|
||||
|
||||
public CredentialedGit(CloneCommand clone, CredentialsProvider creds) throws TransportException, InvalidRemoteException, GitAPIException {
|
||||
this(clone.setCredentialsProvider(creds).call(), creds);
|
||||
}
|
||||
|
||||
public CredentialedGit(Git git, CredentialsProvider creds) {
|
||||
super(git.getRepository());
|
||||
this.credProvider = creds;
|
||||
}
|
||||
|
||||
public CredentialedGit(Repository repo, CredentialsProvider creds) {
|
||||
super(repo);
|
||||
this.credProvider = creds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchCommand fetch() {
|
||||
return super.fetch().setCredentialsProvider(this.credProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LsRemoteCommand lsRemote() {
|
||||
return super.lsRemote().setCredentialsProvider(this.credProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PullCommand pull() {
|
||||
return super.pull().setCredentialsProvider(this.credProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushCommand push() {
|
||||
return super.push().setCredentialsProvider(this.credProvider);
|
||||
}
|
||||
|
||||
}
|
9
src/main/java/me/brianlong/git/ExpiringMapListener.java
Normal file
9
src/main/java/me/brianlong/git/ExpiringMapListener.java
Normal file
@ -0,0 +1,9 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public interface ExpiringMapListener<K, V> extends MapListener<K, V> {
|
||||
|
||||
void expired(Entry<K, V> entry);
|
||||
|
||||
}
|
139
src/main/java/me/brianlong/git/ExtendedGit.java
Normal file
139
src/main/java/me/brianlong/git/ExtendedGit.java
Normal file
@ -0,0 +1,139 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
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.ListBranchCommand.ListMode;
|
||||
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.Constants;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
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());
|
||||
}
|
||||
|
||||
public ExtendedGit(CloneCommand clone) throws TransportException, InvalidRemoteException, GitAPIException {
|
||||
super(clone.call().getRepository());
|
||||
}
|
||||
|
||||
public ExtendedGit(Repository repo) {
|
||||
super(repo);
|
||||
}
|
||||
|
||||
public String getFirstRemoteUrl() throws GitAPIException {
|
||||
return this.remoteList().call().iterator().next().getURIs().iterator().next().toString();
|
||||
}
|
||||
|
||||
public String getRepositoryFullyQualifiedName() throws GitAPIException, URISyntaxException {
|
||||
String gitUrl = this.getFirstRemoteUrl();
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("Remote URL: " + gitUrl);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves all branches, but excludes remote branches that are tracked with a local branch.
|
||||
* @return
|
||||
*/
|
||||
public List<Ref> getAllUniqueBranches() throws GitAPIException {
|
||||
return this.getAllUniqueBranches(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves all branches, but excludes remote branches that are tracked with a local branch.
|
||||
* @return
|
||||
*/
|
||||
public List<Ref> getAllUniqueBranches(String containsCommitish) throws GitAPIException {
|
||||
if (this.logger.isTraceEnabled())
|
||||
this.logger.trace("getAllUniqueBranches('" + containsCommitish + "')");
|
||||
|
||||
ListBranchCommand command = this.branchList().setListMode(ListMode.ALL);
|
||||
if (containsCommitish != null)
|
||||
command.setContains(containsCommitish);
|
||||
List<Ref> branches = command.call();
|
||||
|
||||
if (this.logger.isTraceEnabled())
|
||||
this.logger.trace("getAllUniqueBranches('" + containsCommitish + "'): branch count: " + branches.size());
|
||||
|
||||
Set<String> abbrevBranchNames = new HashSet<String>(branches.size());
|
||||
List<Ref> trimmedBranches = new ArrayList<Ref>(branches.size());
|
||||
|
||||
// sort them by local, then remote; keep order the same otherwise
|
||||
Collections.sort(branches, new Comparator<Ref>() {
|
||||
@Override
|
||||
public int compare(Ref o1, Ref o2) {
|
||||
if (o1 == null) return -1;
|
||||
else if (o2 == null) return 1;
|
||||
else {
|
||||
boolean o1local = o1.getName().startsWith(Constants.R_HEADS);
|
||||
boolean o2local = o2.getName().startsWith(Constants.R_HEADS);
|
||||
if (o1local && o2local) return 0;
|
||||
else if (o1local) return -1;
|
||||
else if (o2local) return 1;
|
||||
else return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (this.logger.isTraceEnabled())
|
||||
this.logger.trace("getAllUniqueBranches('" + containsCommitish + "'): sorted");
|
||||
|
||||
for (Ref branch : branches) {
|
||||
String fqBranchName = branch.getName();
|
||||
|
||||
String abbrevBranchName = null;
|
||||
if (fqBranchName.startsWith(Constants.R_REMOTES)) {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("Ref is a remote branch: " + fqBranchName);
|
||||
abbrevBranchName = this.getRepository().shortenRemoteBranchName(fqBranchName);
|
||||
} else if (fqBranchName.startsWith(Constants.R_HEADS)) {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("Ref is a local branch: " + fqBranchName);
|
||||
abbrevBranchName = Repository.shortenRefName(fqBranchName);
|
||||
} else {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("Ref is not a local or remote branch: " + fqBranchName);
|
||||
}
|
||||
|
||||
if (abbrevBranchName != null) {
|
||||
if (abbrevBranchNames.contains(abbrevBranchName)) {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("Branch already found; ignoring ...");
|
||||
} else {
|
||||
abbrevBranchNames.add(abbrevBranchName);
|
||||
trimmedBranches.add(branch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.logger.isTraceEnabled())
|
||||
this.logger.trace("getAllUniqueBranches('" + containsCommitish + "'): " + trimmedBranches);
|
||||
|
||||
return trimmedBranches;
|
||||
}
|
||||
|
||||
}
|
252
src/main/java/me/brianlong/git/LRUExpiringHashMap.java
Normal file
252
src/main/java/me/brianlong/git/LRUExpiringHashMap.java
Normal file
@ -0,0 +1,252 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
// TODO need to implement the actual expiration schedule
|
||||
public class LRUExpiringHashMap<K extends Serializable, V> implements ListeningMap<K, V> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(LRUExpiringHashMap.class);
|
||||
|
||||
private final Map<ExpiringHashKey, V> map;
|
||||
private final List<MapListener<K, V>> listeners = new LinkedList<MapListener<K, V>>();
|
||||
private final long expirationTimeMillis;
|
||||
|
||||
public LRUExpiringHashMap(int expirationTimeInMinutes) {
|
||||
this.map = new LinkedHashMap<ExpiringHashKey, V>();
|
||||
this.expirationTimeMillis = expirationTimeInMinutes * 60L + 1000L;
|
||||
}
|
||||
|
||||
public LRUExpiringHashMap(int expirationTimeInMinutes, int initialCapacity) {
|
||||
this.map = new LinkedHashMap<ExpiringHashKey, V>(initialCapacity);
|
||||
this.expirationTimeMillis = expirationTimeInMinutes * 60L + 1000L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(MapListener<K, V> listener) {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("adding listener");
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return this.map.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return this.map.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public V get(Object key) {
|
||||
if (!this.map.containsKey(key))
|
||||
return null;
|
||||
|
||||
// remove and put to move the entry to the end of the map; helping with finding expired entries
|
||||
V value = this.map.remove(key);
|
||||
this.map.put(new ExpiringHashKey((K)key, System.currentTimeMillis() + this.expirationTimeMillis), value);
|
||||
|
||||
for (MapListener<K, V> listener : this.listeners)
|
||||
listener.accessed(new ExpiringHashMapEntry((K)key, value));
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
ExpiringHashKey ehkey = new ExpiringHashKey(key, System.currentTimeMillis() + this.expirationTimeMillis);
|
||||
V oldValue = this.map.put(ehkey, value);
|
||||
for (MapListener<K, V> listener : this.listeners)
|
||||
listener.added(new ExpiringHashMapEntry(key, value));
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for (Entry<ExpiringHashKey, V> entry : this.map.entrySet()) {
|
||||
for (MapListener<K, V> listener : this.listeners)
|
||||
listener.cleared(new ExpiringHashMapEntry(entry.getKey().getKey(), entry.getValue()));
|
||||
}
|
||||
|
||||
this.map.clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
if (!this.map.containsKey(key))
|
||||
return null;
|
||||
|
||||
V value = this.map.remove(key);
|
||||
for (MapListener<K, V> listener : this.listeners)
|
||||
listener.removed(new ExpiringHashMapEntry((K)key, value));
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.map.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return this.map.equals(obj);
|
||||
}
|
||||
|
||||
public void exipriationCheck() {
|
||||
Iterator<Entry<ExpiringHashKey, V>> i = this.map.entrySet().iterator();
|
||||
for (Entry<ExpiringHashKey, V> entry = i.next(); i.hasNext(); entry = i.next()) {
|
||||
if (entry.getKey().isExpired()) {
|
||||
i.remove();
|
||||
for (MapListener<K, V> listener : this.listeners)
|
||||
if (listener instanceof ExpiringMapListener)
|
||||
((ExpiringMapListener<K, V>)listener).expired(new ExpiringHashMapEntry(entry.getKey().getKey(), entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void expire(K key) {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("expiring key from map: " + key);
|
||||
|
||||
if (!this.map.containsKey(key))
|
||||
return;
|
||||
|
||||
V value = this.map.remove(key);
|
||||
for (MapListener<K, V> listener : this.listeners)
|
||||
if (listener instanceof ExpiringMapListener)
|
||||
((ExpiringMapListener<K, V>)listener).expired(new ExpiringHashMapEntry(key, value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class ExpiringHashMapEntry implements Entry<K, V> {
|
||||
|
||||
private final K key;
|
||||
private V value;
|
||||
|
||||
public ExpiringHashMapEntry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
return this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof Entry<?, ?>) {
|
||||
return this.key.equals(((Entry<?, ?>)obj).getKey()) && this.value.equals(((Entry<?, ?>)obj).getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.key.hashCode() + this.value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + this.key + ", " + this.value + "}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ExpiringHashKey implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6511298315143655313L;
|
||||
|
||||
private long expirationTimeInMillis;
|
||||
private K key;
|
||||
|
||||
public ExpiringHashKey(K key, long expirationTimeInMillis) {
|
||||
this.key = key;
|
||||
this.expirationTimeInMillis = expirationTimeInMillis;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return this.expirationTimeInMillis <= System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.key.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.key.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof LRUExpiringHashMap.ExpiringHashKey) {
|
||||
return this.key.equals(((ExpiringHashKey)obj).key);
|
||||
} else {
|
||||
return this.key.equals(obj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
9
src/main/java/me/brianlong/git/ListeningMap.java
Normal file
9
src/main/java/me/brianlong/git/ListeningMap.java
Normal file
@ -0,0 +1,9 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface ListeningMap<K, V> extends Map<K, V> {
|
||||
|
||||
void addListener(MapListener<K, V> listener);
|
||||
|
||||
}
|
186
src/main/java/me/brianlong/git/LocalRepositoryCache.java
Normal file
186
src/main/java/me/brianlong/git/LocalRepositoryCache.java
Normal file
@ -0,0 +1,186 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
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.RefNotFoundException;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.util.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LocalRepositoryCache {
|
||||
|
||||
private static final LocalRepositoryCache INSTANCE = new LocalRepositoryCache();
|
||||
|
||||
public static LocalRepositoryCache getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(LocalRepositoryCache.class);
|
||||
|
||||
private final LRUExpiringHashMap<String, Git> cachedGits = new LRUExpiringHashMap<String, Git>(30);
|
||||
// private final Map<String, String> gitIdsToUrls = new HashMap<String, String>();
|
||||
private final File cacheDirectory = new File(System.getProperty("java.io.tmpdir"), "git");
|
||||
|
||||
private LocalRepositoryCache() {
|
||||
this.cacheDirectory.mkdir();
|
||||
this.cachedGits.addListener(new RepositoryCacheMapListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
this.destroy();
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
private void destroy() {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("clearing local repo cache");
|
||||
this.clear();
|
||||
}
|
||||
|
||||
public synchronized ExtendedGit acquire(String url) throws GitAPIException, InvalidRemoteException, RefNotFoundException {
|
||||
return this.acquire(url, null, null);
|
||||
}
|
||||
|
||||
public synchronized ExtendedGit acquire(String url, CredentialsProvider creds) throws GitAPIException, InvalidRemoteException, RefNotFoundException {
|
||||
return this.acquire(url, creds, null);
|
||||
}
|
||||
|
||||
public synchronized ExtendedGit acquire(String url, String branch) throws GitAPIException, InvalidRemoteException, RefNotFoundException {
|
||||
return this.acquire(url, null, branch);
|
||||
}
|
||||
|
||||
public synchronized ExtendedGit acquire(String url, CredentialsProvider creds, String branch) throws GitAPIException, InvalidRemoteException, RefNotFoundException {
|
||||
if (this.logger.isTraceEnabled())
|
||||
this.logger.trace("acquire('" + url + "', " + creds + ", '" + branch + "')");
|
||||
|
||||
// Git git = this.cachedGits.remove(url);
|
||||
// if (git == null) {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("creating temporary Git directory");
|
||||
File gitRepoDirectory = new File(this.cacheDirectory, UUID.randomUUID().toString() + ".git");
|
||||
|
||||
CloneCommand clone = new CloneCommand()
|
||||
.setURI(url)
|
||||
.setDirectory(gitRepoDirectory);
|
||||
if (branch != null)
|
||||
clone.setBranch(branch);
|
||||
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("cloning Git repository: " + url);
|
||||
ExtendedGit git = creds != null ? new CredentialedGit(clone, creds) : new ExtendedGit(clone);
|
||||
if (this.logger.isInfoEnabled())
|
||||
this.logger.info("Cloned Git Repository");
|
||||
return git;
|
||||
// git = creds != null ? new CredentialedGit(clone, creds) : new CachedGit(clone);
|
||||
// this.gitIdsToUrls.put(git.getRepository().getIdentifier(), url);
|
||||
// } else {
|
||||
// if (branch != null) {
|
||||
// if (this.logger.isDebugEnabled())
|
||||
// this.logger.debug("switching Git branches: " + branch);
|
||||
// git.checkout().setName(branch).call();
|
||||
// }
|
||||
//
|
||||
// if (this.logger.isDebugEnabled())
|
||||
// this.logger.debug("updating Git branch: " + branch);
|
||||
// git.pull().call();
|
||||
// }
|
||||
}
|
||||
|
||||
public synchronized void release(Git git) {
|
||||
if (this.logger.isTraceEnabled())
|
||||
this.logger.trace("release('" + git.getRepository().getIdentifier() + "')");
|
||||
|
||||
// String url = this.gitIdsToUrls.get(git.getRepository().getIdentifier());
|
||||
// this.cachedGits.put(url, git);
|
||||
this.expunge(git);
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
if (this.logger.isTraceEnabled())
|
||||
this.logger.trace("clear()");
|
||||
|
||||
// this.cachedGits.clear();
|
||||
}
|
||||
|
||||
private void expunge(Git git) {
|
||||
// gitIdsToUrls.remove(git.getRepository().getIdentifier());
|
||||
|
||||
File gitDir = git.getRepository().getDirectory();
|
||||
File workingTreeDir = git.getRepository().getWorkTree();
|
||||
git.getRepository().close();
|
||||
|
||||
try {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("deleting: " + gitDir);
|
||||
FileUtils.delete(gitDir, FileUtils.RECURSIVE);
|
||||
} catch (IOException ie) {
|
||||
this.logger.warn("Failed to delete a git directory: " + gitDir);
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug(ie.getMessage(), ie);
|
||||
gitDir.deleteOnExit();
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug("deleting: " + workingTreeDir);
|
||||
FileUtils.delete(workingTreeDir, FileUtils.RECURSIVE);
|
||||
} catch (IOException ie) {
|
||||
this.logger.warn("Failed to delete a git directory: " + workingTreeDir);
|
||||
if (this.logger.isDebugEnabled())
|
||||
this.logger.debug(ie.getMessage(), ie);
|
||||
workingTreeDir.deleteOnExit();
|
||||
}
|
||||
|
||||
if (this.logger.isInfoEnabled())
|
||||
this.logger.info("Deleted Git Repository");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class RepositoryCacheMapListener implements ExpiringMapListener<String, Git> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(LocalRepositoryCache.class);
|
||||
|
||||
@Override
|
||||
public void accessed(Entry<String, Git> entry) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(Entry<String, Git> entry) {
|
||||
// a clean one or one returned after being previously removed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expired(Entry<String, Git> entry) {
|
||||
if (this.logger.isTraceEnabled())
|
||||
this.logger.trace("expired('" + entry.getKey() + "', '" + entry.getValue().getRepository().getIdentifier() + "')");
|
||||
expunge(entry.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(Entry<String, Git> entry) {
|
||||
// expected to be removed only temporarily...for use elsewhere; do not close
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleared(Entry<String, Git> entry) {
|
||||
if (this.logger.isTraceEnabled())
|
||||
this.logger.trace("cleared('" + entry.getKey() + "', '" + entry.getValue().getRepository().getIdentifier() + "')");
|
||||
expunge(entry.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
15
src/main/java/me/brianlong/git/MapListener.java
Normal file
15
src/main/java/me/brianlong/git/MapListener.java
Normal file
@ -0,0 +1,15 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public interface MapListener<K, V> {
|
||||
|
||||
void added(Entry<K, V> entry);
|
||||
|
||||
void accessed(Entry<K, V> entry);
|
||||
|
||||
void removed(Entry<K, V> entry);
|
||||
|
||||
void cleared(Entry<K, V> entry);
|
||||
|
||||
}
|
236
src/main/java/me/brianlong/git/UniquePriorityFifoQueue.java
Normal file
236
src/main/java/me/brianlong/git/UniquePriorityFifoQueue.java
Normal file
@ -0,0 +1,236 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Queue;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class UniquePriorityFifoQueue<T> implements Queue<T> {
|
||||
|
||||
private final Counter counter = new SimpleCounter();
|
||||
private final Counter fakeCounter = new FakeCounter();
|
||||
private final TreeSet<PriorityFifoElement<T>> elements;
|
||||
|
||||
public UniquePriorityFifoQueue() {
|
||||
this.elements = new TreeSet<PriorityFifoElement<T>>();
|
||||
}
|
||||
|
||||
public UniquePriorityFifoQueue(Collection<? extends T> c) {
|
||||
this.elements = new TreeSet<PriorityFifoElement<T>>();
|
||||
for (T element : c)
|
||||
this.elements.add(new PriorityFifoElement<T>(element, this.counter));
|
||||
}
|
||||
|
||||
public UniquePriorityFifoQueue(final Comparator<? super T> comparator) {
|
||||
this.elements = new TreeSet<PriorityFifoElement<T>>(new Comparator<PriorityFifoElement<T>>() {
|
||||
@Override
|
||||
public int compare(PriorityFifoElement<T> o1, PriorityFifoElement<T> o2) {
|
||||
int compare = comparator.compare(o1.element, o2.element);
|
||||
if (compare != 0) return compare;
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T e) {
|
||||
return this.elements.add(new PriorityFifoElement<T>(e, this.counter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> c) {
|
||||
if (c != null) for (T e : c)
|
||||
this.elements.add(new PriorityFifoElement<T>(e, this.counter));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.elements.clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return this.elements.contains(new PriorityFifoElement<T>((T)o, this.fakeCounter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
if (c != null) for (Object o : c)
|
||||
if (!this.contains(o))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T element() {
|
||||
return this.elements.iterator().next().element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.elements.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
final Iterator<PriorityFifoElement<T>> i = this.elements.iterator();
|
||||
return new Iterator<T>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return i.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return i.next().element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
i.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(T e) {
|
||||
return this.elements.add(new PriorityFifoElement<T>(e, this.counter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public T peek() {
|
||||
Iterator<T> i = this.iterator();
|
||||
return i.hasNext() ? i.next() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T poll() {
|
||||
PriorityFifoElement<T> element = this.elements.pollFirst();
|
||||
return element == null ? null : element.element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove() {
|
||||
PriorityFifoElement<T> element = this.elements.pollFirst();
|
||||
if (element == null)
|
||||
throw new NoSuchElementException();
|
||||
return element.element;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return this.elements.remove(new PriorityFifoElement<T>((T)o, this.fakeCounter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
if (c != null) for (Object o : c)
|
||||
this.remove(o);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
Iterator<PriorityFifoElement<T>> i = this.elements.iterator();
|
||||
while (i.hasNext()) {
|
||||
T e = i.next().element;
|
||||
if (!c.contains(e))
|
||||
i.remove();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.elements.size();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
Object[] objs = this.elements.toArray();
|
||||
Object[] ts = new Object[objs.length];
|
||||
for (int i = 0; i < objs.length; i++)
|
||||
ts[i] = ((PriorityFifoElement<T>)objs[i]).element;
|
||||
return ts;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <U extends Object> U[] toArray(U[] a) {
|
||||
if (a.length < this.elements.size())
|
||||
a = Arrays.copyOf(a, this.elements.size());
|
||||
|
||||
int i = 0;
|
||||
for (PriorityFifoElement<T> element : this.elements)
|
||||
a[i] = (U)element.element;
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class PriorityFifoElement<E> implements Comparable<PriorityFifoElement<E>> {
|
||||
|
||||
private final E element;
|
||||
private final int fifoId;
|
||||
|
||||
public PriorityFifoElement(E element, Counter counter) {
|
||||
this.element = element;
|
||||
this.fifoId = counter.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PriorityFifoElement<E> o) {
|
||||
if (o == null) return 1;
|
||||
else return this.fifoId - o.fifoId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.element.hashCode();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof PriorityFifoElement) {
|
||||
return this.element.equals(((PriorityFifoElement)obj).element);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private interface Counter {
|
||||
|
||||
int next();
|
||||
|
||||
}
|
||||
|
||||
public class FakeCounter implements Counter {
|
||||
|
||||
public int next() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SimpleCounter implements Counter {
|
||||
|
||||
private int count = 0;
|
||||
|
||||
public synchronized int next() {
|
||||
this.count++;
|
||||
return this.count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
54
src/test/java/me/brianlong/git/CommandUnitTest.java
Normal file
54
src/test/java/me/brianlong/git/CommandUnitTest.java
Normal file
@ -0,0 +1,54 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.jgit.api.CloneCommand;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
|
||||
import org.eclipse.jgit.util.FileUtils;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CommandUnitTest {
|
||||
|
||||
private final static CredentialsProvider gitCreds = new UsernamePasswordCredentialsProvider(
|
||||
System.getProperty("git.username"), System.getProperty("git.token"));
|
||||
|
||||
private static File tmpdir;
|
||||
private static Git git;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() throws GitAPIException, IOException {
|
||||
tmpdir = new File(System.getProperty("java.io.tmpdir"), "git-" + UUID.randomUUID().toString() + ".tmp");
|
||||
|
||||
git = new CloneCommand()
|
||||
.setURI("git@github.com:bmlong137/env-docker-adbp.git")
|
||||
.setCredentialsProvider(gitCreds)
|
||||
.setDirectory(tmpdir)
|
||||
.call();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanup() throws IOException {
|
||||
git.close();
|
||||
|
||||
FileUtils.delete(tmpdir, FileUtils.RECURSIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lotsOfBranches() throws GitAPIException {
|
||||
List<Ref> remoteBranches = git.branchList().setListMode(ListMode.REMOTE).call();
|
||||
Assert.assertNotNull(remoteBranches);
|
||||
Assert.assertTrue(remoteBranches.size() > 5);
|
||||
}
|
||||
|
||||
}
|
127
src/test/java/me/brianlong/git/LocalRepositoryCacheUnitTest.java
Normal file
127
src/test/java/me/brianlong/git/LocalRepositoryCacheUnitTest.java
Normal file
@ -0,0 +1,127 @@
|
||||
package me.brianlong.git;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
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.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class LocalRepositoryCacheUnitTest {
|
||||
|
||||
private final static CredentialsProvider gitCreds = new UsernamePasswordCredentialsProvider(
|
||||
System.getProperty("git.username"), System.getProperty("git.token"));
|
||||
|
||||
@AfterClass
|
||||
public static void cleanup() {
|
||||
LocalRepositoryCache.getInstance().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Since "host-does-not-exist.com" does not exist, an UnknownHostException
|
||||
* is thrown. It is wrapped inside a JGit TransportException.
|
||||
*/
|
||||
@Test(expected = TransportException.class)
|
||||
public void cacheBadHostRepo() throws GitAPIException {
|
||||
LocalRepositoryCache.getInstance().acquire("https://host-does-not-exist.com/bmlong137/does-not-exist.git");
|
||||
}
|
||||
|
||||
/**
|
||||
* Since "does-not-exist" isn't a public repo, it requires authentication
|
||||
* to even check to see if it is private. This causes a
|
||||
* NoRemoteRepositoryException which is wrapped inside a JGit
|
||||
* InvalidRemoteException. This is because SSH authentication makes this
|
||||
* more like cacheNonExistentRepoAuth() than cacheNonExistentRepo().
|
||||
*/
|
||||
@Test(expected = InvalidRemoteException.class)
|
||||
public void cacheNonExistentRepoViaSsh() throws GitAPIException {
|
||||
LocalRepositoryCache.getInstance().acquire("git@github.com:bmlong137/does-not-exist.git");
|
||||
}
|
||||
|
||||
/**
|
||||
* Since "does-not-exist" isn't a public repo, it requires authentication
|
||||
* to even check to see if it is private. This causes a JSchException
|
||||
* which is wrapped inside a JGit TransportException.
|
||||
*/
|
||||
@Test(expected = TransportException.class)
|
||||
public void cacheNonExistentRepo() throws GitAPIException {
|
||||
LocalRepositoryCache.getInstance().acquire("https://github.com/bmlong137/does-not-exist.git");
|
||||
}
|
||||
|
||||
/**
|
||||
* Since "does-not-exist" isn't a repo, a NoRemoteRepositoryException is
|
||||
* thrown. It is wrapped inside a JGit InvalidRemoteException.
|
||||
*/
|
||||
@Test(expected = InvalidRemoteException.class)
|
||||
public void cacheNonExistentRepoAuth() throws GitAPIException {
|
||||
LocalRepositoryCache.getInstance().acquire("https://github.com/bmlong137/does-not-exist.git", gitCreds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cachePublicRepo() throws GitAPIException {
|
||||
Git git = LocalRepositoryCache.getInstance().acquire("https://github.com/bmlong137/maven-file-management.git");
|
||||
try {
|
||||
this.validateGenericGitRepo(git);
|
||||
} finally {
|
||||
git.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cachePublicRepoViaSsh() throws GitAPIException {
|
||||
Git git = LocalRepositoryCache.getInstance().acquire("git@github.com:bmlong137/maven-file-management.git");
|
||||
try {
|
||||
this.validateGenericGitRepo(git);
|
||||
} finally {
|
||||
git.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Since "github-api" isn't a public repo, it requires authentication
|
||||
* to even check to see if it is private. This causes a JSchException
|
||||
* which is wrapped inside a JGit TransportException.
|
||||
*/
|
||||
@Test(expected = TransportException.class)
|
||||
public void cachePrivateRepoUnauth() throws GitAPIException {
|
||||
LocalRepositoryCache.getInstance().acquire("https://github.com/bmlong137/github-api.git");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cachePrivateRepo() throws GitAPIException {
|
||||
Git git = LocalRepositoryCache.getInstance().acquire("https://github.com/bmlong137/github-api.git", gitCreds);
|
||||
try {
|
||||
this.validateGenericGitRepo(git);
|
||||
} finally {
|
||||
git.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cachePublicRepoBranch() throws GitAPIException, IOException {
|
||||
Git git = LocalRepositoryCache.getInstance().acquire("https://github.com/bmlong137/maven-file-management.git", "master");
|
||||
try {
|
||||
this.validateGenericGitRepo(git);
|
||||
|
||||
Assert.assertEquals("master", git.getRepository().getBranch());
|
||||
} finally {
|
||||
git.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void validateGenericGitRepo(Git git) throws GitAPIException {
|
||||
Assert.assertNotNull(git);
|
||||
|
||||
Repository repo = git.getRepository();
|
||||
Assert.assertTrue(repo.getDirectory().exists());
|
||||
Assert.assertTrue(repo.getWorkTree().exists());
|
||||
Assert.assertTrue(repo.getWorkTree().listFiles().length > 0);
|
||||
}
|
||||
|
||||
}
|
19
src/test/resources/log4j2.xml
Normal file
19
src/test/resources/log4j2.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="[%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="me.brianlong" level="trace" additivity="false">
|
||||
<AppenderRef ref="STDOUT" />
|
||||
</Logger>
|
||||
<Logger name="org.apache.http" level="trace" additivity="false">
|
||||
<AppenderRef ref="STDOUT" />
|
||||
</Logger>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
Loading…
x
Reference in New Issue
Block a user