summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
diff options
context:
space:
mode:
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.java209
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);
+ }
}