/******************************************************************* * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein * are the sole and exclusive property of Cloudsmith Inc. and may * not be disclosed, used, modified, copied or distributed without * prior written consent or license from Cloudsmith Inc. * * Contributors: * Lorenzo Bettini - https://bugs.eclipse.org/bugs/show_bug.cgi?id=428301 */ package org.eclipse.buckminster.git.internal; import static org.eclipse.buckminster.core.helpers.MapUtils.getString; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.eclipse.buckminster.core.KeyConstants; import org.eclipse.buckminster.core.RMContext; import org.eclipse.buckminster.core.ctype.IComponentType; import org.eclipse.buckminster.core.materializer.MaterializationContext; import org.eclipse.buckminster.core.metadata.model.Resolution; import org.eclipse.buckminster.core.reader.CatalogReaderType; import org.eclipse.buckminster.core.reader.IComponentReader; import org.eclipse.buckminster.core.reader.ITeamReaderType; import org.eclipse.buckminster.core.reader.IVersionFinder; import org.eclipse.buckminster.core.resolver.NodeQuery; import org.eclipse.buckminster.core.rmap.model.Provider; import org.eclipse.buckminster.core.version.ProviderMatch; import org.eclipse.buckminster.runtime.Buckminster; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.Logger; import org.eclipse.buckminster.runtime.MonitorUtils; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.egit.core.GitProvider; import org.eclipse.egit.core.RepositoryUtil; import org.eclipse.egit.core.op.ConnectProviderOperation; import org.eclipse.egit.core.project.GitProjectData; import org.eclipse.egit.core.project.RepositoryMapping; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.PathFilterGroup; import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.eclipse.osgi.util.NLS; import org.eclipse.team.core.RepositoryProvider; public class GitReaderType extends CatalogReaderType implements ITeamReaderType { @Override public String convertFetchFactoryLocator(Map<String, Object> fetchFactoryLocator, String componentName) throws CoreException { String repo = getString(fetchFactoryLocator, "repo"); //$NON-NLS-1$ if (repo == null) throw new IllegalArgumentException(NLS.bind(Messages.git_reader_type_is_missing_required_property_0, "repo")); //$NON-NLS-1$ String localClone = "${workspace.root}"; //$NON-NLS-1$ try { URIish tmpURI = new URIish(repo); IPath tmpPath = Path.fromPortableString(tmpURI.getPath()); localClone += '/' + tmpPath.lastSegment(); } catch (URISyntaxException e) { throw BuckminsterException.wrap(e); } String path = getString(fetchFactoryLocator, "path"); //$NON-NLS-1$ if (path != null) localClone += ',' + path; return localClone; } @Override public URI getArtifactURL(Resolution resolution, RMContext context) throws CoreException { return null; } @Override public Map<String, String> getFetchFactoryProviderProps(Map<String, Object> fetchFactoryLocator, Provider delegee) { Map<String, String> props = new HashMap<String, String>(); props.put(KeyConstants.IS_SOURCE, Boolean.TRUE.toString()); props.put(KeyConstants.IS_MUTABLE, Boolean.TRUE.toString()); String repo = getString(fetchFactoryLocator, "repo"); //$NON-NLS-1$ if (repo == null) throw new IllegalArgumentException(NLS.bind(Messages.git_reader_type_is_missing_required_property_0, "repo")); //$NON-NLS-1$ props.put(IPropertyKeys.PROP_REMOTE_URI, repo); return props; } @Override public IPath getInstallLocation(Resolution resolution, MaterializationContext context) throws CoreException { String fmt = resolution.getRepository(); int comma = fmt.lastIndexOf(','); File repo; if (comma >= 0) { fmt = fmt.substring(0, comma); repo = new File(fmt); } else { // The repository _is_ the component, so the install location // will be the repository parent. repo = new File(fmt).getParentFile(); } return Path.fromOSString(repo.getAbsolutePath()).addTrailingSeparator(); } @Override public Date getLastModification(File workingCopy, IProgressMonitor monitor) throws CoreException { Logger logger = Buckminster.getLogger(); IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); String workingCopyStr = workingCopy.getAbsolutePath(); IPath workingCopyPath = Path.fromOSString(workingCopyStr); IResource resource = wsRoot.getContainerForLocation(workingCopyPath); if (resource == null) resource = wsRoot.getFileForLocation(workingCopyPath); if (resource == null) { // Try canonical path too before we give up try { workingCopyStr = workingCopy.getCanonicalPath(); workingCopyPath = Path.fromOSString(workingCopyStr); resource = wsRoot.getContainerForLocation(workingCopyPath); if (resource == null) resource = wsRoot.getFileForLocation(workingCopyPath); } catch (IOException e) { } } if (resource == null) { logger.debug("getLastModification: Failed get resource for path %s", workingCopy.getAbsolutePath()); //$NON-NLS-1$ return null; } RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject()); if (provider == null) { logger.debug("getLastModification: Unable to get repository provider for project %s", resource.getProject().getName()); //$NON-NLS-1$ return null; } RepositoryMapping rm = RepositoryMapping.getMapping(resource); if (rm == null) { logger.debug("getLastModification: Unable to get repository mapping for project %s", resource.getProject().getName()); //$NON-NLS-1$ return null; } Repository repository = rm.getRepository(); RevWalk walk = new RevWalk(repository); try { String gitPath = rm.getRepoRelativePath(resource); if (gitPath == null || gitPath.length() == 0) { walk.setTreeFilter(TreeFilter.ANY_DIFF); } else { walk.setTreeFilter(AndTreeFilter.create(PathFilterGroup.createFromStrings(Collections.singleton(gitPath)), TreeFilter.ANY_DIFF)); } final AnyObjectId headId = repository.resolve(Constants.HEAD); if (headId == null) { logger.debug("getLastModification: Unable to find head revision in repository %s", repository.getDirectory().getAbsolutePath()); //$NON-NLS-1$ return null; } walk.markStart(walk.parseCommit(headId)); RevCommit rc; long lastTimestamp = 0; while ((rc = walk.next()) != null) { long secondsSinceEpoch = rc.getCommitTime(); long commitTimestamp = secondsSinceEpoch * 1000L; if (commitTimestamp > lastTimestamp) lastTimestamp = commitTimestamp; } if (lastTimestamp == 0) { logger.debug("getLastModification: Unable to find any file revisions in project %s", resource.getProject().getName()); //$NON-NLS-1$ return null; } return new Date(lastTimestamp); } catch (IOException ex) { throw BuckminsterException.wrap(ex); } finally { walk.close(); } } @Override public IPath getLeafArtifact(Resolution resolution, MaterializationContext context) throws CoreException { String fmt = resolution.getRepository(); int comma = fmt.lastIndexOf(','); if (comma >= 0) fmt = fmt.substring(comma + 1); else { // The repository _is_ the component, so the leaf artifact is // the repository. fmt = new File(fmt).getName(); } return Path.fromOSString(fmt).addTrailingSeparator(); } @Override public IComponentReader getReader(ProviderMatch providerMatch, IProgressMonitor monitor) throws CoreException { MonitorUtils.complete(monitor); return new GitReader(this, providerMatch); } @Override public String getSourceReference(IResource resource, IProgressMonitor monitor) throws CoreException { IProject project = resource.getProject(); if (project == null) return null; RepositoryProvider provider = RepositoryProvider.getProvider(project); if (!(provider instanceof GitProvider)) return null; GitProjectData projectData = ((GitProvider) provider).getData(); if (projectData == null) return null; RepositoryMapping mapping = projectData.getRepositoryMapping(resource); if (mapping == null) return null; Repository repo = mapping.getRepository(); if (repo == null) return null; StoredConfig config = repo.getConfig(); String remoteConfig; Set<String> configNames = config.getSubsections("remote"); //$NON-NLS-1$ if (configNames.size() == 1) remoteConfig = configNames.iterator().next(); else if (configNames.contains(Constants.DEFAULT_REMOTE_NAME)) remoteConfig = Constants.DEFAULT_REMOTE_NAME; else return null; String remoteLocation = config.getString("remote", remoteConfig, "url"); //$NON-NLS-1$//$NON-NLS-2$ if (remoteLocation == null) return null; return "scm:git:" + remoteLocation; //$NON-NLS-1$ } @Override public IVersionFinder getVersionFinder(Provider provider, IComponentType ctype, NodeQuery nodeQuery, IProgressMonitor monitor) throws CoreException { MonitorUtils.complete(monitor); return new VersionFinder(provider, ctype, nodeQuery); } /** * Closes any cached RepositoryAccess instances. */ @Override public void postMaterialization(MaterializationContext context, IProgressMonitor monitor) throws CoreException { } @Override public void shareProject(IProject project, Resolution cr, RMContext context, IProgressMonitor monitor) throws CoreException { File repoDir = null; if (cr.getReaderTypeId().equals("git")) { //$NON-NLS-1$ String fmt = cr.getRepository(); int comma = fmt.lastIndexOf(','); if (comma >= 0) fmt = fmt.substring(0, comma); repoDir = Path.fromPortableString(fmt).append(Constants.DOT_GIT).toFile(); } else { FileRepositoryBuilder builder = new FileRepositoryBuilder(); builder.findGitDir(project.getLocation().toFile()); if (builder.getGitDir() != null) { repoDir = builder.getGitDir(); } else { Logger logger = Buckminster.getLogger(); logger.warning("No git repo found for Project: %s.", project.getName()); return; } } // Add repository if it's not already added try { repoDir = repoDir.getCanonicalFile(); } catch (IOException e) { } String absPath = repoDir.getAbsolutePath().intern(); synchronized (absPath) { Logger logger = Buckminster.getLogger(); RepositoryUtil repoUtil = org.eclipse.egit.core.Activator.getDefault().getRepositoryUtil(); if (repoUtil.addConfiguredRepository(repoDir)) logger.info("Added Git repository at %s to the set of known repositories", absPath); //$NON-NLS-1$ ConnectProviderOperation connectOp = new ConnectProviderOperation(project, repoDir); connectOp.execute(monitor); // Once connected, we should be able to get the provider for this // repository RepositoryProvider provider = RepositoryProvider.getProvider(project); if (provider == null) logger.warning("Failed to get team provider after connecting project %s to Git repository at %s", project.getName(), absPath); //$NON-NLS-1$ else logger.info("Connected project %s to Git repository at %s", project.getName(), absPath); //$NON-NLS-1$ } } @Override public IStatus tag(RepositoryProvider provider, IResource[] resources, String tag, boolean recurse, IProgressMonitor monitor) throws CoreException { // Not yet implemented. throw new UnsupportedOperationException(); } }