diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java | 209 |
1 files changed, 164 insertions, 45 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java index 7e98348dd8..9fa45e185d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java @@ -15,18 +15,22 @@ package com.google.gerrit.server.git; import com.google.gerrit.lifecycle.LifecycleListener; -import com.google.gerrit.reviewdb.Project; +import com.google.gerrit.lifecycle.LifecycleModule; +import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; +import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryCache; import org.eclipse.jgit.lib.RepositoryCache.FileKey; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.storage.file.LockFile; import org.eclipse.jgit.storage.file.WindowCache; import org.eclipse.jgit.storage.file.WindowCacheConfig; @@ -39,6 +43,11 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Collections; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** Manages Git repositories stored on the local filesystem. */ @Singleton @@ -49,6 +58,20 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { private static final String UNNAMED = "Unnamed repository; edit this file to name it for gitweb."; + public static class Module extends AbstractModule { + @Override + protected void configure() { + bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class); + + install(new LifecycleModule() { + @Override + protected void configure() { + listener().to(LocalDiskRepositoryManager.Lifecycle.class); + } + }); + } + } + public static class Lifecycle implements LifecycleListener { private final Config cfg; @@ -70,6 +93,8 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { } private final File basePath; + private final Lock namesUpdateLock; + private volatile SortedSet<Project.NameKey> names; @Inject LocalDiskRepositoryManager(final SitePaths site, @@ -78,6 +103,8 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { if (basePath == null) { throw new IllegalStateException("gerrit.basePath must be configured"); } + namesUpdateLock = new ReentrantLock(true /* fair */); + names = list(); } /** @return base directory under which all projects are stored. */ @@ -94,9 +121,11 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { if (isUnreasonableName(name)) { throw new RepositoryNotFoundException("Invalid name: " + name); } - + if (!names.contains(name)) { + throw new RepositoryNotFoundException(gitDirOf(name)); + } + final FileKey loc = FileKey.lenient(gitDirOf(name), FS.DETECTED); try { - final FileKey loc = FileKey.lenient(gitDirOf(name), FS.DETECTED); return RepositoryCache.open(loc); } catch (IOException e1) { final RepositoryNotFoundException e2; @@ -107,63 +136,93 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { } public Repository createRepository(final Project.NameKey name) - throws RepositoryNotFoundException { + throws RepositoryNotFoundException, RepositoryCaseMismatchException { if (isUnreasonableName(name)) { throw new RepositoryNotFoundException("Invalid name: " + name); } - try { - File dir = FileKey.resolve(gitDirOf(name), FS.DETECTED); - FileKey loc; - if (dir != null) { - // Already exists on disk, use the repository we found. - // - loc = FileKey.exact(dir, FS.DETECTED); - } else { - // It doesn't exist under any of the standard permutations - // of the repository name, so prefer the standard bare name. - // - String n = name.get(); - if (!n.endsWith(Constants.DOT_GIT_EXT)) { - n = n + Constants.DOT_GIT_EXT; - } - loc = FileKey.exact(new File(basePath, n), FS.DETECTED); + File dir = FileKey.resolve(gitDirOf(name), FS.DETECTED); + FileKey loc; + if (dir != null) { + // Already exists on disk, use the repository we found. + // + loc = FileKey.exact(dir, FS.DETECTED); + + if (!names.contains(name)) { + throw new RepositoryCaseMismatchException(name); } - return RepositoryCache.open(loc, false); + } else { + // It doesn't exist under any of the standard permutations + // of the repository name, so prefer the standard bare name. + // + String n = name.get(); + if (!n.endsWith(Constants.DOT_GIT_EXT)) { + n = n + Constants.DOT_GIT_EXT; + } + loc = FileKey.exact(new File(basePath, n), FS.DETECTED); + } + + try { + Repository db = RepositoryCache.open(loc, false); + db.create(true /* bare */); + + StoredConfig config = db.getConfig(); + config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, + null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true); + config.save(); + + onCreateProject(name); + + return db; } catch (IOException e1) { final RepositoryNotFoundException e2; - e2 = new RepositoryNotFoundException("Cannot open repository " + name); + e2 = new RepositoryNotFoundException("Cannot create repository " + name); e2.initCause(e1); throw e2; } } + private void onCreateProject(final Project.NameKey newProjectName) { + namesUpdateLock.lock(); + try { + SortedSet<Project.NameKey> n = new TreeSet<Project.NameKey>(names); + n.add(newProjectName); + names = Collections.unmodifiableSortedSet(n); + } finally { + namesUpdateLock.unlock(); + } + } + public String getProjectDescription(final Project.NameKey name) throws RepositoryNotFoundException, IOException { final Repository e = openRepository(name); try { - final File d = new File(e.getDirectory(), "description"); + return getProjectDescription(e); + } finally { + e.close(); + } + } - String description; - try { - description = RawParseUtils.decode(IO.readFully(d)); - } catch (FileNotFoundException err) { - return null; - } + private String getProjectDescription(final Repository e) throws IOException { + final File d = new File(e.getDirectory(), "description"); - if (description != null) { - description = description.trim(); - if (description.isEmpty()) { - description = null; - } - if (UNNAMED.equals(description)) { - description = null; - } + String description; + try { + description = RawParseUtils.decode(IO.readFully(d)); + } catch (FileNotFoundException err) { + return null; + } + + if (description != null) { + description = description.trim(); + if (description.isEmpty()) { + description = null; + } + if (UNNAMED.equals(description)) { + description = null; } - return description; - } finally { - e.close(); } + return description; } public void setProjectDescription(final Project.NameKey name, @@ -171,12 +230,15 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { // Update git's description file, in case gitweb is being used // try { - final Repository e; - final LockFile f; - - e = openRepository(name); + final Repository e = openRepository(name); try { - f = new LockFile(new File(e.getDirectory(), "description"), FS.DETECTED); + final String old = getProjectDescription(e); + if ((old == null && description == null) + || (old != null && old.equals(description))) { + return; + } + + final LockFile f = new LockFile(new File(e.getDirectory(), "description"), FS.DETECTED); if (f.lock()) { String d = description; if (d != null) { @@ -204,6 +266,7 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { final String name = nameKey.get(); if (name.length() == 0) return true; // no empty paths + if (name.charAt(name.length() -1) == '/') return true; // no suffix if (name.indexOf('\\') >= 0) return true; // no windows/dos stlye paths if (name.charAt(0) == '/') return true; // no absolute paths @@ -216,4 +279,60 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { return false; // is a reasonable name } + + @Override + public SortedSet<Project.NameKey> list() { + // The results of this method are cached by ProjectCacheImpl. Control only + // enters here if the cache was flushed by the administrator to force + // scanning the filesystem. Don't rely on the cached names collection. + namesUpdateLock.lock(); + try { + SortedSet<Project.NameKey> n = new TreeSet<Project.NameKey>(); + scanProjects(basePath, "", n); + names = Collections.unmodifiableSortedSet(n); + return n; + } finally { + namesUpdateLock.unlock(); + } + } + + private void scanProjects(final File dir, final String prefix, + final SortedSet<Project.NameKey> names) { + final File[] ls = dir.listFiles(); + if (ls == null) { + return; + } + + for (File f : ls) { + String fileName = f.getName(); + if (FileKey.isGitRepository(f, FS.DETECTED)) { + Project.NameKey nameKey = getProjectName(prefix, fileName); + if (isUnreasonableName(nameKey)) { + log.warn("Ignoring unreasonably named repository " + f.getAbsolutePath()); + } else { + names.add(nameKey); + } + + } else if (f.isDirectory()) { + scanProjects(f, prefix + f.getName() + "/", names); + } + } + } + + private Project.NameKey getProjectName(final String prefix, + final String fileName) { + final String projectName; + if (fileName.equals(Constants.DOT_GIT)) { + projectName = prefix.substring(0, prefix.length() - 1); + + } else if (fileName.endsWith(Constants.DOT_GIT_EXT)) { + int newLen = fileName.length() - Constants.DOT_GIT_EXT.length(); + projectName = prefix + fileName.substring(0, newLen); + + } else { + projectName = prefix + fileName; + } + + return new Project.NameKey(projectName); + } } |