/******************************************************************************* * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com> * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org> * Copyright (C) 2008, Google Inc. * Copyright (C) 2012, François Rey <eclipse.org_@_francois_._rey_._name> * Copyright (C) 2013, Carsten Pfeiffer <carsten.pfeiffer@gebit.de> * Copyright (C) 2015, Stephan Hackstedt <stephan.hackstedt@googlemail.com> * Copyright (C) 2016, Thomas Wolf <thomas.wolf@paranor.ch> * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.egit.core.project; import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.egit.core.internal.CoreText; import org.eclipse.egit.core.internal.trace.GitTraceLocation; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.util.SystemReader; /** * Searches for existing Git repositories associated with a project's files. * <p> * This finder algorithm searches a project's contained files to see if any of * them are located within the working directory of an existing Git repository. * By default linked resources are ignored and not included in the search. * <p> * The search algorithm is exhaustive, it will find all matching repositories. * For the project itself and possibly for each linked container within the * project it scans down the local filesystem trees to locate any Git * repositories which may be found there. Descending into children can be * disabled, see {@link #setFindInChildren(boolean)}. * <p> * It also scans up the local filesystem tree to locate any Git repository which * may be outside of Eclipse's workspace-view of the world. * <p> * In short, if there is a Git repository associated, it finds it. * </p> */ public class RepositoryFinder { private final IProject proj; private final List<RepositoryMapping> results = new ArrayList<RepositoryMapping>(); private final Set<File> gitdirs = new HashSet<File>(); private final Set<File> ceilingDirectories = new HashSet<File>(); private boolean findInChildren = true; /** * Create a new finder to locate Git repositories for a project. * * @param p * the project this new finder should locate the existing Git * repositories of. */ public RepositoryFinder(final IProject p) { proj = p; String ceilingDirectoriesVar = SystemReader.getInstance().getenv( Constants.GIT_CEILING_DIRECTORIES_KEY); if (ceilingDirectoriesVar != null) { for (String path : ceilingDirectoriesVar.split(File.pathSeparator)) ceilingDirectories.add(new File(path)); } } /** * @param findInChildren * whether children of the project should also be scanned for a * .git directory * @since 3.4 */ public void setFindInChildren(boolean findInChildren) { this.findInChildren = findInChildren; } /** * Run the search algorithm, ignoring linked resources. * * @param m * a progress monitor to report feedback to; may be null. * @return all found {@link RepositoryMapping} instances associated with the * project supplied to this instance's constructor, in the order * they were found. * @throws CoreException * Eclipse was unable to access its workspace, and threw up on * us. We're throwing it back at the caller. */ public List<RepositoryMapping> find(IProgressMonitor m) throws CoreException { return find(m, false); } /** * Run the search algorithm. * * @param m * a progress monitor to report feedback to; may be null. * @param searchLinkedFolders * specify if linked folders should be included in the search * @return all found {@link RepositoryMapping} instances associated with the * project supplied to this instance's constructor, in the order * they were found. * @throws CoreException * Eclipse was unable to access its workspace, and threw up on * us. We're throwing it back at the caller. * @since 2.3 */ public List<RepositoryMapping> find(IProgressMonitor m, boolean searchLinkedFolders) throws CoreException { find(m, proj, searchLinkedFolders); return results; } private void find(final IProgressMonitor m, final IContainer c, boolean searchLinkedFolders) throws CoreException { if (!searchLinkedFolders && c.isLinked()) { return; // Ignore linked folders } final IPath loc = c.getLocation(); if (loc == null) { return; // Either gone, or provided by an EFS } SubMonitor progress = SubMonitor.convert(m, 101); progress.subTask(CoreText.RepositoryFinder_finding); final File fsLoc = loc.toFile(); assert fsLoc.isAbsolute(); if (c instanceof IProject) findInDirectoryAndParents(c, fsLoc); else findInDirectory(c, fsLoc); progress.worked(1); if (findInChildren) { final IResource[] children = c.members(); if (children != null && children.length > 0) { progress.setWorkRemaining(children.length); for (int k = 0; k < children.length; k++) { final IResource o = children[k]; if (o instanceof IContainer && !o.getName().equals(Constants.DOT_GIT)) { find(progress.newChild(1), (IContainer) o, searchLinkedFolders); } else { progress.worked(1); } } } } } private void findInDirectoryAndParents(IContainer container, File startPath) { File path = startPath; while (path != null && !ceilingDirectories.contains(path)) { findInDirectory(container, path); path = path.getParentFile(); } } private void findInDirectory(final IContainer container, final File path) { if (GitTraceLocation.CORE.isActive()) GitTraceLocation.getTrace().trace( GitTraceLocation.CORE.getLocation(), "Looking at candidate dir: " //$NON-NLS-1$ + path); FileRepositoryBuilder builder = new FileRepositoryBuilder(); File parent = path.getParentFile(); if (parent != null) builder.addCeilingDirectory(parent); builder.findGitDir(path); File gitDir = builder.getGitDir(); if (gitDir != null) register(container, gitDir); } private void register(final IContainer c, final File gitdir) { File f = gitdir.getAbsoluteFile(); if (gitdirs.contains(f)) { return; } gitdirs.add(f); RepositoryMapping mapping = RepositoryMapping.create(c, f); if (mapping != null) { results.add(mapping); } } }