/*
* Copyright (c) 2011 Kevin Sawicki <kevinsawicki@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
package org.gitective.core;
import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.lib.Constants.R_NOTES;
import static org.eclipse.jgit.lib.Constants.R_REMOTES;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.api.LsRemoteCommand;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.RemoteConfig;
/**
* Utilities for dealing with Git repositories.
* <p>
* This class provides helpers for getting the branches, tags, and note
* references in a repository.
* <p>
* This class also provides helpers for knowing which references differ between
* a local and remote repository.
*/
public abstract class RepositoryUtils {
/**
* Wrapper class for a remote and local ref that are different
*/
public static class RefDiff {
private final Ref local;
private final Ref remote;
/**
* Create ref diff
*
* @param local
* @param remote
*/
protected RefDiff(final Ref local, final Ref remote) {
this.local = local;
this.remote = remote;
}
/**
* Get local ref. This will be null if the ref only exists remotely.
*
* @return ref
*/
public Ref getLocal() {
return local;
}
/**
* Get non-null remote ref
*
* @return ref
*/
public Ref getRemote() {
return remote;
}
}
/**
* Get the refs with prefix in repository
*
* @param repository
* @param prefix
* @return collection of refs
*/
protected static Collection<Ref> getRefs(final Repository repository,
final String prefix) {
try {
return repository.getRefDatabase().getRefs(prefix).values();
} catch (IOException e) {
throw new GitException(e, repository);
}
}
/**
* Get all the note references in the given repository.
*
* @param repository
* @return non-null but possibly empty array of note references
*/
public static Collection<String> getNoteRefs(final Repository repository) {
if (repository == null)
throw new IllegalArgumentException(
Assert.formatNotNull("Repository"));
final Collection<Ref> refs = getRefs(repository, R_NOTES);
final List<String> notes = new ArrayList<String>(refs.size());
for (Ref ref : refs)
notes.add(ref.getName());
return notes;
}
/**
* Get all the local and remote tracking branch references in the given
* repository.
*
* @param repository
* @return non-null but possibly array of branch reference names
*/
public static Collection<String> getBranches(final Repository repository) {
if (repository == null)
throw new IllegalArgumentException(
Assert.formatNotNull("Repository"));
final List<String> branches = new ArrayList<String>();
for (Ref ref : getRefs(repository, R_HEADS))
branches.add(ref.getName());
for (Ref ref : getRefs(repository, R_REMOTES))
branches.add(ref.getName());
return branches;
}
/**
* Get all the tag references in the given repository.
*
* @param repository
* @return non-null but possibly array of branch reference names
*/
public static Collection<String> getTags(final Repository repository) {
if (repository == null)
throw new IllegalArgumentException(
Assert.formatNotNull("Repository"));
return repository.getTags().keySet();
}
/**
* List the origin remote references and return all remote references that
* are missing locally or have a different remote object id than the local
* reference.
*
* @param repository
* @return non-null but possibly empty collection of {@link RefDiff}
*/
public static Collection<RefDiff> diffOriginRefs(final Repository repository) {
return diffRemoteRefs(repository, DEFAULT_REMOTE_NAME);
}
/**
* Does the given remote exist in the repository?
*
* @param repository
* @param remote
* @return true if exists, false otherwise
* @throws URISyntaxException
*/
protected static boolean hasRemote(final Repository repository,
final String remote) throws URISyntaxException {
final RemoteConfig config = new RemoteConfig(repository.getConfig(),
remote);
return !config.getURIs().isEmpty() || !config.getPushURIs().isEmpty();
}
/**
* List remote references and return all remote references that are missing
* locally or have a different remote object id than the local reference.
*
* @param repository
* @param remote
* @return non-null but possibly collection of {@link RefDiff}
*/
public static Collection<RefDiff> diffRemoteRefs(
final Repository repository, final String remote) {
if (repository == null)
throw new IllegalArgumentException(
Assert.formatNotNull("Repository"));
if (remote == null)
throw new IllegalArgumentException(Assert.formatNotNull("Remote"));
if (remote.length() == 0)
throw new IllegalArgumentException(Assert.formatNotEmpty("Remote"));
final LsRemoteCommand lsRemote = new LsRemoteCommand(repository);
lsRemote.setRemote(remote);
try {
final Collection<Ref> remoteRefs = lsRemote.call();
final List<RefDiff> diffs = new ArrayList<RefDiff>();
if (hasRemote(repository, remote)) {
final String refPrefix = R_REMOTES + remote + "/";
for (Ref remoteRef : remoteRefs) {
String name = remoteRef.getName();
if (name.startsWith(R_HEADS))
name = refPrefix + name.substring(R_HEADS.length());
else if (!name.startsWith(R_TAGS))
name = refPrefix + name;
final Ref localRef = repository.getRef(name);
if (localRef == null
|| !remoteRef.getObjectId().equals(
localRef.getObjectId()))
diffs.add(new RefDiff(localRef, remoteRef));
}
} else
for (Ref remoteRef : remoteRefs) {
final Ref localRef = repository.getRef(remoteRef.getName());
if (localRef == null
|| !remoteRef.getObjectId().equals(
localRef.getObjectId()))
diffs.add(new RefDiff(localRef, remoteRef));
}
return diffs;
} catch (Exception e) {
throw new GitException(e, repository);
}
}
/**
* Map names to e-mail addresses for all given {@link PersonIdent} instances
* <p>
* {@link PersonIdent} entries with a null or empty name will be ignored.
*
* @see PersonIdent#getName()
* @see PersonIdent#getEmailAddress()
* @param persons
* @return non-null but possibly empty map of names to e-mail addresses
*/
public static Map<String, Set<String>> mapNamesToEmails(
final Collection<PersonIdent> persons) {
if (persons == null || persons.isEmpty())
return Collections.emptyMap();
final Map<String, Set<String>> namesToEmails = new HashMap<String, Set<String>>(
persons.size());
for (PersonIdent person : persons) {
String name = person.getName();
if (name == null || name.length() == 0)
continue;
Set<String> emails = namesToEmails.get(name);
if (emails == null) {
emails = new HashSet<String>(2);
namesToEmails.put(name, emails);
}
String email = person.getEmailAddress();
if (email != null && email.length() > 0)
emails.add(email);
}
return namesToEmails;
}
/**
* Map e-mail addresses to names for all given {@link PersonIdent} instances
* <p>
* {@link PersonIdent} entries with a null or empty e-mail address will be
* ignored.
*
* @see PersonIdent#getName()
* @see PersonIdent#getEmailAddress()
* @param persons
* @return non-null but possibly empty map of e-mail addresses to names
*/
public static Map<String, Set<String>> mapEmailsToNames(
final Collection<PersonIdent> persons) {
if (persons == null || persons.isEmpty())
return Collections.emptyMap();
final Map<String, Set<String>> emailsToNames = new HashMap<String, Set<String>>(
persons.size());
for (PersonIdent person : persons) {
String email = person.getEmailAddress();
if (email == null || email.length() == 0)
continue;
Set<String> names = emailsToNames.get(email);
if (names == null) {
names = new HashSet<String>(2);
emailsToNames.put(email, names);
}
String name = person.getName();
if (name != null && name.length() > 0)
names.add(name);
}
return emailsToNames;
}
}