From 9f5a860d1ecaebb80530fc22607d34fc80d8c09d Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Thu, 04 Oct 2012 08:04:49 -0400 Subject: [PATCH] Fixed duplicate entries in repository cache (issue 140) --- src/com/gitblit/GitBlit.java | 200 +++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 159 insertions(+), 41 deletions(-) diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 699bbac..b14adc9 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -32,6 +32,7 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -81,6 +82,7 @@ import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.FederationSet; +import com.gitblit.models.ForkModel; import com.gitblit.models.Metric; import com.gitblit.models.ProjectModel; import com.gitblit.models.RepositoryModel; @@ -744,17 +746,17 @@ * Adds the repository to the list of cached repositories if Gitblit is * configured to cache the repository list. * - * @param name + * @param model */ - private void addToCachedRepositoryList(String name, RepositoryModel model) { + private void addToCachedRepositoryList(RepositoryModel model) { if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { - repositoryListCache.put(name, model); + repositoryListCache.put(model.name, model); // update the fork origin repository with this repository clone if (!StringUtils.isEmpty(model.originRepository)) { if (repositoryListCache.containsKey(model.originRepository)) { RepositoryModel origin = repositoryListCache.get(model.originRepository); - origin.addFork(name); + origin.addFork(model.name); } } } @@ -999,7 +1001,7 @@ if (model == null) { return null; } - addToCachedRepositoryList(repositoryName, model); + addToCachedRepositoryList(model); return model; } @@ -1021,7 +1023,7 @@ logger.info(MessageFormat.format("Config for \"{0}\" has changed. Reloading model and updating cache.", repositoryName)); model = loadRepositoryModel(repositoryName); removeFromCachedRepositoryList(repositoryName); - addToCachedRepositoryList(repositoryName, model); + addToCachedRepositoryList(model); } else { // update a few repository parameters if (!model.hasCommits) { @@ -1234,10 +1236,15 @@ return null; } RepositoryModel model = new RepositoryModel(); - model.name = repositoryName; + model.isBare = r.isBare(); + File basePath = getFileOrFolder(Keys.git.repositoriesFolder, "git"); + if (model.isBare) { + model.name = com.gitblit.utils.FileUtils.getRelativePath(basePath, r.getDirectory()); + } else { + model.name = com.gitblit.utils.FileUtils.getRelativePath(basePath, r.getDirectory().getParentFile()); + } model.hasCommits = JGitUtils.hasCommits(r); model.lastChange = JGitUtils.getLastChange(r); - model.isBare = r.isBare(); if (repositoryName.indexOf('/') == -1) { model.projectPath = ""; } else { @@ -1268,6 +1275,9 @@ Constants.CONFIG_GITBLIT, null, "federationSets"))); model.isFederated = getConfig(config, "isFederated", false); model.origin = config.getString("remote", "origin", "url"); + if (model.origin != null) { + model.origin = model.origin.replace('\\', '/'); + } model.preReceiveScripts = new ArrayList<String>(Arrays.asList(config.getStringList( Constants.CONFIG_GITBLIT, null, "preReceiveScript"))); model.postReceiveScripts = new ArrayList<String>(Arrays.asList(config.getStringList( @@ -1324,6 +1334,113 @@ } r.close(); return true; + } + + /** + * Determines if the specified user has a fork of the specified origin + * repository. + * + * @param username + * @param origin + * @return true the if the user has a fork + */ + public boolean hasFork(String username, String origin) { + return getFork(username, origin) != null; + } + + /** + * Gets the name of a user's fork of the specified origin + * repository. + * + * @param username + * @param origin + * @return the name of the user's fork, null otherwise + */ + public String getFork(String username, String origin) { + String userProject = "~" + username.toLowerCase(); + if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { + String userPath = userProject + "/"; + + // collect all origin nodes in fork network + Set<String> roots = new HashSet<String>(); + roots.add(origin); + RepositoryModel originModel = repositoryListCache.get(origin); + while (originModel != null) { + if (!ArrayUtils.isEmpty(originModel.forks)) { + for (String fork : originModel.forks) { + if (!fork.startsWith(userPath)) { + roots.add(fork); + } + } + } + + if (originModel.originRepository != null) { + roots.add(originModel.originRepository); + originModel = repositoryListCache.get(originModel.originRepository); + } else { + // break + originModel = null; + } + } + + for (String repository : repositoryListCache.keySet()) { + if (repository.toLowerCase().startsWith(userPath)) { + RepositoryModel model = repositoryListCache.get(repository); + if (!StringUtils.isEmpty(model.originRepository)) { + if (roots.contains(model.originRepository)) { + // user has a fork in this graph + return model.name; + } + } + } + } + } else { + // not caching + ProjectModel project = getProjectModel(userProject); + for (String repository : project.repositories) { + if (repository.toLowerCase().startsWith(userProject)) { + RepositoryModel model = repositoryListCache.get(repository); + if (model.originRepository.equalsIgnoreCase(origin)) { + // user has a fork + return model.name; + } + } + } + } + // user does not have a fork + return null; + } + + /** + * Returns the fork network for a repository by traversing up the fork graph + * to discover the root and then down through all children of the root node. + * + * @param repository + * @return a ForkModel + */ + public ForkModel getForkNetwork(String repository) { + if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { + // find the root + RepositoryModel model = repositoryListCache.get(repository); + while (model.originRepository != null) { + model = repositoryListCache.get(model.originRepository); + } + ForkModel root = getForkModel(model.name); + return root; + } + return null; + } + + private ForkModel getForkModel(String repository) { + RepositoryModel model = repositoryListCache.get(repository); + ForkModel fork = new ForkModel(model); + if (!ArrayUtils.isEmpty(model.forks)) { + for (String aFork : model.forks) { + ForkModel fm = getForkModel(aFork); + fork.forks.add(fm); + } + } + return fork; } /** @@ -1558,7 +1675,7 @@ // update repository cache removeFromCachedRepositoryList(repositoryName); // model will actually be replaced on next load because config is stale - addToCachedRepositoryList(repository.name, repository); + addToCachedRepositoryList(repository); } /** @@ -2528,44 +2645,45 @@ * * @param repository * @param user - * @return true, if successful + * @return the repository model of the fork, if successful + * @throws GitBlitException */ - public boolean fork(RepositoryModel repository, UserModel user) { + public RepositoryModel fork(RepositoryModel repository, UserModel user) throws GitBlitException { String cloneName = MessageFormat.format("~{0}/{1}.git", user.username, StringUtils.stripDotGit(StringUtils.getLastPathElement(repository.name))); String fromUrl = MessageFormat.format("file://{0}/{1}", repositoriesFolder.getAbsolutePath(), repository.name); + + // clone the repository try { - // clone the repository JGitUtils.cloneRepository(repositoriesFolder, cloneName, fromUrl, true, null); - - // create a Gitblit repository model for the clone - RepositoryModel cloneModel = repository.cloneAs(cloneName); - cloneModel.owner = user.username; - updateRepositoryModel(cloneName, cloneModel, false); - - if (AuthorizationControl.NAMED.equals(cloneModel.authorizationControl)) { - // add the owner of the source repository to the clone's access list - if (!StringUtils.isEmpty(repository.owner)) { - UserModel owner = getUserModel(repository.owner); - if (owner != null) { - owner.repositories.add(cloneName); - updateUserModel(owner.username, owner, false); - } - } - - // inherit origin's access lists - List<String> users = getRepositoryUsers(repository); - setRepositoryUsers(cloneModel, users); - - List<String> teams = getRepositoryTeams(repository); - setRepositoryTeams(cloneModel, teams); - } - - // add this clone to the cached model - addToCachedRepositoryList(cloneModel.name, cloneModel); - return true; } catch (Exception e) { - logger.error("failed to fork", e); + throw new GitBlitException(e); } - return false; + + // create a Gitblit repository model for the clone + RepositoryModel cloneModel = repository.cloneAs(cloneName); + cloneModel.owner = user.username; + updateRepositoryModel(cloneName, cloneModel, false); + + if (AuthorizationControl.NAMED.equals(cloneModel.authorizationControl)) { + // add the owner of the source repository to the clone's access list + if (!StringUtils.isEmpty(repository.owner)) { + UserModel owner = getUserModel(repository.owner); + if (owner != null) { + owner.repositories.add(cloneName); + updateUserModel(owner.username, owner, false); + } + } + + // inherit origin's access lists + List<String> users = getRepositoryUsers(repository); + setRepositoryUsers(cloneModel, users); + + List<String> teams = getRepositoryTeams(repository); + setRepositoryTeams(cloneModel, teams); + } + + // add this clone to the cached model + addToCachedRepositoryList(cloneModel); + return cloneModel; } } -- Gitblit v1.9.1