From df7b554b80ca656acd04ce4887dbd92b57c88881 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 19:28:46 +0100 Subject: [PATCH 1/6] #873 implemented default branch repository property for git --- .../repository/spi/AbstractGitCommand.java | 47 ++++++++++++++++++- .../AbstractGitIncomingOutgoingCommand.java | 2 +- .../scm/repository/spi/GitBlameCommand.java | 2 +- .../scm/repository/spi/GitBrowseCommand.java | 16 ++----- .../scm/repository/spi/GitCatCommand.java | 16 ++----- .../scm/repository/spi/GitDiffCommand.java | 1 - .../scm/repository/spi/GitLogCommand.java | 13 +---- .../repository/spi/GitBlameCommandTest.java | 29 +++++++++++- .../repository/spi/GitBrowseCommandTest.java | 39 ++++++++++++++- .../scm/repository/spi/GitCatCommandTest.java | 21 +++++++++ .../scm/repository/spi/GitLogCommandTest.java | 35 +++++++++++++- .../spi/GitOutgoingCommandTest.java | 3 +- 12 files changed, 182 insertions(+), 42 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java index dc26777f65..9680a9136a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java @@ -34,11 +34,17 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import org.eclipse.jgit.lib.Repository; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; +import org.eclipse.jgit.lib.ObjectId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.GitUtil; /** * @@ -46,6 +52,14 @@ import java.io.IOException; */ public class AbstractGitCommand { + + /** + * the logger for AbstractGitCommand + */ + private static final Logger logger = LoggerFactory.getLogger(AbstractGitCommand.class); + + @VisibleForTesting + static final String PROPERTY_DEFAULT_BRANCH = "git.default-branch"; /** * Constructs ... @@ -54,7 +68,6 @@ public class AbstractGitCommand * * @param context * @param repository - * @param repositoryDirectory */ protected AbstractGitCommand(GitContext context, sonia.scm.repository.Repository repository) @@ -77,6 +90,38 @@ public class AbstractGitCommand { return context.open(); } + + protected ObjectId getCommitOrDefault(Repository gitRepository, String requestedCommit) throws IOException { + ObjectId commit; + if ( Strings.isNullOrEmpty(requestedCommit) ) { + commit = getDefaultBranch(gitRepository); + } else { + commit = gitRepository.resolve(requestedCommit); + } + return commit; + } + + protected ObjectId getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException { + ObjectId head; + if ( Strings.isNullOrEmpty(requestedBranch) ) { + head = getDefaultBranch(gitRepository); + } else { + head = GitUtil.getBranchId(gitRepository, requestedBranch); + } + return head; + } + + protected ObjectId getDefaultBranch(Repository gitRepository) throws IOException { + ObjectId head; + String defaultBranchName = repository.getProperty(PROPERTY_DEFAULT_BRANCH); + if (!Strings.isNullOrEmpty(defaultBranchName)) { + head = GitUtil.getBranchId(gitRepository, defaultBranchName); + } else { + logger.trace("no default branch configured, use repository head as default"); + head = GitUtil.getRepositoryHead(gitRepository); + } + return head; + } //~--- fields --------------------------------------------------------------- diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java index d510cd3b51..e3fec1c8e8 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java @@ -138,7 +138,7 @@ public abstract class AbstractGitIncomingOutgoingCommand GitUtil.fetch(git, handler.getDirectory(remoteRepository), remoteRepository); - ObjectId localId = GitUtil.getRepositoryHead(git.getRepository()); + ObjectId localId = getDefaultBranch(git.getRepository()); ObjectId remoteId = null; Ref remoteBranch = getRemoteBranch(git.getRepository(), localId, diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java index a48b4b2b1e..c797fd70eb 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java @@ -124,7 +124,7 @@ public class GitBlameCommand extends AbstractGitCommand implements BlameCommand blame.setFilePath(request.getPath()); - ObjectId revId = GitUtil.getRevisionId(gr, request.getRevision()); + ObjectId revId = getCommitOrDefault(gr, request.getRevision()); blame.setStartCommit(revId); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java index a2962f43c4..1af5a35d29 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java @@ -45,7 +45,6 @@ import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.submodule.SubmoduleWalk; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.PathFilter; @@ -72,7 +71,6 @@ import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Map.Entry; /** * @@ -96,11 +94,8 @@ public class GitBrowseCommand extends AbstractGitCommand /** * Constructs ... * - * - * * @param context * @param repository - * @param repositoryDirectory */ public GitBrowseCommand(GitContext context, Repository repository) { @@ -124,18 +119,15 @@ public class GitBrowseCommand extends AbstractGitCommand public BrowserResult getBrowserResult(BrowseCommandRequest request) throws IOException, RepositoryException { - if (logger.isDebugEnabled()) - { - logger.debug("try to create browse result for {}", request); - } + logger.debug("try to create browse result for {}", request); - BrowserResult result = null; + BrowserResult result; org.eclipse.jgit.lib.Repository repo = open(); - ObjectId revId = null; + ObjectId revId; if (Util.isEmpty(request.getRevision())) { - revId = GitUtil.getRepositoryHead(repo); + revId = getDefaultBranch(repo); } else { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java index 17dad775b1..4420b9a22a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java @@ -34,6 +34,7 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Strings; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; @@ -78,7 +79,6 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand * * @param context * @param repository - * @param repositoryDirectory */ public GitCatCommand(GitContext context, sonia.scm.repository.Repository repository) @@ -102,17 +102,11 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand public void getCatResult(CatCommandRequest request, OutputStream output) throws IOException, RepositoryException { - if (logger.isDebugEnabled()) - { - logger.debug("try to read content for {}", request); - } - - org.eclipse.jgit.lib.Repository repo = null; - - repo = open(); - - ObjectId revId = GitUtil.getRevisionId(repo, request.getRevision()); + logger.debug("try to read content for {}", request); + org.eclipse.jgit.lib.Repository repo = open(); + + ObjectId revId = getCommitOrDefault(repo, request.getRevision()); getContent(repo, revId, request.getPath(), output); } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java index a8edfa8848..83977ef290 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java @@ -79,7 +79,6 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand * * @param context * @param repository - * @param repositoryDirectory */ public GitDiffCommand(GitContext context, Repository repository) { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java index d47dea9440..9e4e3949f1 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java @@ -220,17 +220,8 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand AndTreeFilter.create( PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF)); } - - ObjectId head = null; - - if (!Strings.isNullOrEmpty(request.getBranch())) - { - head = GitUtil.getBranchId(gr, request.getBranch()); - } - else - { - head = GitUtil.getRepositoryHead(gr); - } + + ObjectId head = getBranchOrDefault(gr, request.getBranch()); if (head != null) { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java index 953372020b..88c9235373 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java @@ -47,12 +47,39 @@ import static org.junit.Assert.*; import java.io.IOException; /** - * + * Unit tests for {@link GitBlameCommand}. + * * @author Sebastian Sdorra */ public class GitBlameCommandTest extends AbstractGitCommandTestBase { + /** + * Tests blame command with default branch. + * + * @throws IOException + * @throws RepositoryException + */ + @Test + public void testDefaultBranch() throws IOException, RepositoryException { + // without default branch, the repository head should be used + BlameCommandRequest request = new BlameCommandRequest(); + request.setPath("a.txt"); + + BlameResult result = createCommand().getBlameResult(request); + assertNotNull(result); + assertEquals(2, result.getTotal()); + assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getLine(0).getRevision()); + assertEquals("fcd0ef1831e4002ac43ea539f4094334c79ea9ec", result.getLine(1).getRevision()); + + // set default branch and test again + repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + result = createCommand().getBlameResult(request); + assertNotNull(result); + assertEquals(1, result.getTotal()); + assertEquals("3f76a12f08a6ba0dc988c68b7f0b2cd190efc3c4", result.getLine(0).getRevision()); + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java index 3999e26f85..d1c2f6abc9 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java @@ -50,11 +50,48 @@ import java.io.IOException; import java.util.List; /** - * + * Unit tests for {@link GitBrowseCommand}. + * * @author Sebastian Sdorra */ public class GitBrowseCommandTest extends AbstractGitCommandTestBase { + + /** + * Test browse command with default branch. + * + * @throws IOException + * @throws RepositoryException + */ + @Test + public void testDefaultBranch() throws IOException, RepositoryException { + // without default branch, the repository head should be used + BrowserResult result = createCommand().getBrowserResult(new BrowseCommandRequest()); + assertNotNull(result); + + List foList = result.getFiles(); + assertNotNull(foList); + assertFalse(foList.isEmpty()); + assertEquals(4, foList.size()); + + assertEquals("a.txt", foList.get(0).getName()); + assertEquals("b.txt", foList.get(1).getName()); + assertEquals("c", foList.get(2).getName()); + assertEquals("f.txt", foList.get(3).getName()); + + // set default branch and fetch again + repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + result = createCommand().getBrowserResult(new BrowseCommandRequest()); + assertNotNull(result); + + foList = result.getFiles(); + assertNotNull(foList); + assertFalse(foList.isEmpty()); + assertEquals(2, foList.size()); + + assertEquals("a.txt", foList.get(0).getName()); + assertEquals("c", foList.get(1).getName()); + } /** * Method description diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java index 1a44a0076c..0d7d743e5a 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java @@ -46,12 +46,33 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; /** + * Unit tests for {@link GitCatCommand}. + * * TODO add not found test * * @author Sebastian Sdorra */ public class GitCatCommandTest extends AbstractGitCommandTestBase { + + /** + * Tests cat command with default branch. + * + * @throws IOException + * @throws RepositoryException + */ + @Test + public void testDefaultBranch() throws IOException, RepositoryException { + // without default branch, the repository head should be used + CatCommandRequest request = new CatCommandRequest(); + request.setPath("a.txt"); + + assertEquals("a\nline for blame", execute(request)); + + // set default branch for repository and check again + repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + assertEquals("a and b", execute(request)); + } /** * Method description diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java index 62c6069fba..640a28a598 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java @@ -49,14 +49,47 @@ import static org.junit.Assert.*; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; +import org.eclipse.jgit.api.errors.GitAPIException; /** - * + * Unit tests for {@link GitLogCommand}. + * * @author Sebastian Sdorra */ public class GitLogCommandTest extends AbstractGitCommandTestBase { + /** + * Tests log command with the usage of a default branch. + * + * @throws IOException + * @throws GitAPIException + * @throws RepositoryException + */ + @Test + public void testGetDefaultBranch() throws IOException, GitAPIException, RepositoryException { + // without default branch, the repository head should be used + ChangesetPagingResult result = createCommand().getChangesets(new LogCommandRequest()); + + assertNotNull(result); + assertEquals(4, result.getTotal()); + assertEquals("fcd0ef1831e4002ac43ea539f4094334c79ea9ec", result.getChangesets().get(0).getId()); + assertEquals("86a6645eceefe8b9a247db5eb16e3d89a7e6e6d1", result.getChangesets().get(1).getId()); + assertEquals("592d797cd36432e591416e8b2b98154f4f163411", result.getChangesets().get(2).getId()); + assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(3).getId()); + + // set default branch and fetch again + repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + + result = createCommand().getChangesets(new LogCommandRequest()); + + assertNotNull(result); + assertEquals(3, result.getTotal()); + assertEquals("3f76a12f08a6ba0dc988c68b7f0b2cd190efc3c4", result.getChangesets().get(0).getId()); + assertEquals("592d797cd36432e591416e8b2b98154f4f163411", result.getChangesets().get(1).getId()); + assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(2).getId()); + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java index dc47c80291..28be6f7df0 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java @@ -51,7 +51,8 @@ import static org.junit.Assert.assertNotNull; import java.io.IOException; /** - * + * Unit tests for {@link OutgoingCommand}. + * * @author Sebastian Sdorra */ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase From f2137bd761e0554554b925f836a946710d7a3005 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 19:48:13 +0100 Subject: [PATCH 2/6] #873 added reusable components for branch and tag combo boxes --- scm-webapp/src/main/webapp/index.mustache | 2 + .../sonia.repository.branchcombobox.js | 65 +++++++++++++++++++ .../sonia.repository.changesetviewerpanel.js | 23 +------ .../sonia.repository.repositorybrowser.js | 40 ++---------- .../sonia.repository.tagcombobox.js | 65 +++++++++++++++++++ 5 files changed, 138 insertions(+), 57 deletions(-) create mode 100644 scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js create mode 100644 scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js diff --git a/scm-webapp/src/main/webapp/index.mustache b/scm-webapp/src/main/webapp/index.mustache index 4c2c3c913c..fec01e96ca 100644 --- a/scm-webapp/src/main/webapp/index.mustache +++ b/scm-webapp/src/main/webapp/index.mustache @@ -115,6 +115,8 @@ + + diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js new file mode 100644 index 0000000000..9ba2575c11 --- /dev/null +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js @@ -0,0 +1,65 @@ +/* * + * Copyright (c) 2014, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + +Sonia.repository.BranchComboBox = Ext.extend(Ext.form.ComboBox, { + + repositoryId: null, + + initComponent: function(){ + var branchStore = new Sonia.rest.JsonStore({ + proxy: new Ext.data.HttpProxy({ + url: restUrl + 'repositories/' + this.repositoryId + '/branches.json', + method: 'GET', + disableCaching: false + }), + root: 'branch', + idProperty: 'name', + fields: ['name', 'revision'] + }); + + var config = { + valueField: 'revision', + displayField: 'name', + typeAhead: false, + editable: false, + triggerAction: 'all', + store: branchStore + }; + + Ext.apply(this, Ext.apply(this.initialConfig, config)); + Sonia.repository.BranchComboBox.superclass.initComponent.apply(this, arguments); + } + +}); + +// register xtype +Ext.reg('repositoryBranchComboBox', Sonia.repository.BranchComboBox); \ No newline at end of file diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.changesetviewerpanel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.changesetviewerpanel.js index 3e276d2c27..aa18d29e0d 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.changesetviewerpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.changesetviewerpanel.js @@ -120,33 +120,14 @@ Sonia.repository.ChangesetViewerPanel = Ext.extend(Ext.Panel, { }, createTopToolbar: function(){ - var branchStore = new Sonia.rest.JsonStore({ - proxy: new Ext.data.HttpProxy({ - url: restUrl + 'repositories/' + this.repository.id + '/branches.json', - method: 'GET', - disableCaching: false - }), - root: 'branch', - idProperty: 'name', - fields: [ 'name' ], - sortInfo: { - field: 'name' - } - }); - return { xtype: 'toolbar', items: [ this.repository.name, '->', 'Branches:', ' ',{ - xtype: 'combo', - valueField: 'name', - displayField: 'name', - typeAhead: false, - editable: false, - triggerAction: 'all', - store: branchStore, + xtype: 'repositoryBranchComboBox', + repositoryId: this.repository.id, listeners: { select: { fn: this.selectBranch, diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js index 7ec444222c..489dd3c06c 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js @@ -159,26 +159,10 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { branches = true; - var branchStore = new Sonia.rest.JsonStore({ - proxy: new Ext.data.HttpProxy({ - url: restUrl + 'repositories/' + this.repository.id + '/branches.json', - method: 'GET', - disableCaching: false - }), - root: 'branch', - idProperty: 'name', - fields: [ 'name', 'revision' ] - }); - items.push('->','Branches:', ' ',{ id: 'branchComboBox', - xtype: 'combo', - valueField: 'revision', - displayField: 'name', - typeAhead: false, - editable: false, - triggerAction: 'all', - store: branchStore, + xtype: 'repositoryBranchComboBox', + repositoryId: this.repository.id, listeners: { select: { fn: this.selectBranch, @@ -191,17 +175,6 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { if ( type && type.supportedCommands && type.supportedCommands.indexOf('TAGS') >= 0){ - var tagStore = new Sonia.rest.JsonStore({ - proxy: new Ext.data.HttpProxy({ - url: restUrl + 'repositories/' + this.repository.id + '/tags.json', - method: 'GET', - disableCaching: false - }), - root: 'tag', - idProperty: 'name', - fields: [ 'name', 'revision' ] - }); - if (branches){ items.push(' '); } else { @@ -210,13 +183,8 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { items.push('Tags:', ' ',{ id: 'tagComboBox', - xtype: 'combo', - valueField: 'revision', - displayField: 'name', - typeAhead: false, - editable: false, - triggerAction: 'all', - store: tagStore, + xtype: 'repositoryTagComboBox', + repositoryId: this.repository.id, listeners: { select: { fn: this.selectTag, diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js new file mode 100644 index 0000000000..0db635f8cd --- /dev/null +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js @@ -0,0 +1,65 @@ +/* * + * Copyright (c) 2014, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + +Sonia.repository.TagComboBox = Ext.extend(Ext.form.ComboBox, { + + repositoryId: null, + + initComponent: function(){ + var tagStore = new Sonia.rest.JsonStore({ + proxy: new Ext.data.HttpProxy({ + url: restUrl + 'repositories/' + this.repositoryId + '/tags.json', + method: 'GET', + disableCaching: false + }), + root: 'tag', + idProperty: 'name', + fields: [ 'name', 'revision' ] + }); + + var config = { + valueField: 'revision', + displayField: 'name', + typeAhead: false, + editable: false, + triggerAction: 'all', + store: tagStore, + }; + + Ext.apply(this, Ext.apply(this.initialConfig, config)); + Sonia.repository.TagComboBox.superclass.initComponent.apply(this, arguments); + } + +}); + +// register xtype +Ext.reg('repositoryTagComboBox', Sonia.repository.TagComboBox); \ No newline at end of file From 0c0bdfa37618f51de908570cc7a92e3aac365a48 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 20:54:20 +0100 Subject: [PATCH 3/6] #873 added default branch chooser to git settings --- .../main/resources/sonia/scm/git.config.js | 102 ++++++++++++++++++ .../sonia.repository.branchcombobox.js | 3 +- .../repository/sonia.repository.formpanel.js | 7 ++ .../js/repository/sonia.repository.grid.js | 25 ++--- .../sonia.repository.settingsformpanel.js | 6 ++ .../src/main/webapp/resources/js/sonia.scm.js | 18 ++++ 6 files changed, 148 insertions(+), 13 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js b/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js index 4a5c75caae..9e038e4dee 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js +++ b/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js @@ -90,6 +90,93 @@ Sonia.git.ConfigPanel = Ext.extend(Sonia.config.SimpleConfigForm, { Ext.reg("gitConfigPanel", Sonia.git.ConfigPanel); +// add default branch chooser to settings panel +Sonia.git.GitSettingsFormPanel = Ext.extend(Sonia.repository.SettingsFormPanel, { + + defaultBranchText: 'Default Branch', + defaultBranchHelpText: 'The default branch which is show first on source or commit view.', + + modifyDefaultConfig: function(config){ + if (this.item) { + var position = -1; + for ( var i=0; i= 0) { + config.items.splice(position, 0, defaultBranchComboxBox); + } else { + config.items.push(defaultBranchComboxBox); + } + } + }, + + getDefaultBranch: function(item){ + if (item.properties) { + for ( var i=0; i{0}', xtype: 'repositoryExtendedInfoPanel' }); + main.registerSettingsForm('git', { + xtype: 'gitSettingsForm' + }); }); // register panel diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js index 9ba2575c11..cddcad4552 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js @@ -33,6 +33,7 @@ Sonia.repository.BranchComboBox = Ext.extend(Ext.form.ComboBox, { repositoryId: null, + useNameAsValue: false, initComponent: function(){ var branchStore = new Sonia.rest.JsonStore({ @@ -47,8 +48,8 @@ Sonia.repository.BranchComboBox = Ext.extend(Ext.form.ComboBox, { }); var config = { - valueField: 'revision', displayField: 'name', + valueField: this.useNameAsValue ? 'name' : 'revision', typeAhead: false, editable: false, triggerAction: 'all', diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.formpanel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.formpanel.js index d717a77daf..66482399ac 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.formpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.formpanel.js @@ -61,8 +61,15 @@ Sonia.repository.FormPanel = Ext.extend(Sonia.rest.FormPanel,{ Sonia.repository.FormPanel.superclass.initComponent.apply(this, arguments); }, + prepareUpdate: function(item) { + + }, + update: function(item){ item = Ext.apply( this.item, item ); + // allow plugins to modify item + this.prepareUpdate(item); + if ( debug ){ console.debug( 'update repository: ' + item.name ); } diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js index 19f8fc311d..fb66b91a5a 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js @@ -432,21 +432,22 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { var infoPanel = main.getInfoPanel(item.type); infoPanel.item = item; + var settingsForm = main.getSettingsForm(item.type); + settingsForm.item = item; + settingsForm.onUpdate = { + fn: this.reload, + scope: this + }; + settingsForm.onCreate = { + fn: this.reload, + scope: this + }; + var panels = [infoPanel]; if ( owner ){ - panels.push({ - item: item, - xtype: 'repositorySettingsForm', - onUpdate: { - fn: this.reload, - scope: this - }, - onCreate: { - fn: this.reload, - scope: this - } - },{ + panels.push( + settingsForm, { item: item, xtype: 'repositoryPermissionsForm', listeners: { diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.settingsformpanel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.settingsformpanel.js index 5c6b902756..2b182a5de2 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.settingsformpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.settingsformpanel.js @@ -78,9 +78,15 @@ Sonia.repository.SettingsFormPanel = Ext.extend(Sonia.repository.FormPanel, { helpText: this.publicHelpText }] }; + + this.modifyDefaultConfig(config); Ext.apply(this, Ext.apply(this.initialConfig, config)); Sonia.repository.SettingsFormPanel.superclass.initComponent.apply(this, arguments); + }, + + modifyDefaultConfig: function(config){ + } }); diff --git a/scm-webapp/src/main/webapp/resources/js/sonia.scm.js b/scm-webapp/src/main/webapp/resources/js/sonia.scm.js index 4e54c3af09..6d7e6a3257 100644 --- a/scm-webapp/src/main/webapp/resources/js/sonia.scm.js +++ b/scm-webapp/src/main/webapp/resources/js/sonia.scm.js @@ -78,6 +78,7 @@ Sonia.scm.Main = Ext.extend(Ext.util.Observable, { mainTabPanel: null, infoPanels: [], + settingsForm: [], scripts: [], stylesheets: [], @@ -96,6 +97,23 @@ Sonia.scm.Main = Ext.extend(Ext.util.Observable, { this.infoPanels[type] = panel; }, + registerSettingsForm: function(type, form){ + this.settingsForm[type] = form; + }, + + getSettingsForm: function(type){ + var rp = null; + var panel = this.settingsForm[type]; + if ( ! panel ){ + rp = { + xtype: 'repositorySettingsForm' + }; + } else { + rp = Sonia.util.clone( panel ); + } + return rp; + }, + getInfoPanel: function(type){ var rp = null; var panel = this.infoPanels[type]; From 5cb32b268f979acb1b214cb249af6129572b517c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 21:56:27 +0100 Subject: [PATCH 4/6] #873 clear repository caches, if the git default branch has changed --- .../repository/ClearRepositoryCacheEvent.java | 64 +++++++ .../api/RepositoryServiceFactory.java | 11 ++ .../sonia/scm/repository/GitConstants.java | 49 ++++++ .../GitRepositoryModifyListener.java | 97 +++++++++++ .../repository/spi/AbstractGitCommand.java | 6 +- .../GitRepositoryModifyListenerTest.java | 163 ++++++++++++++++++ .../repository/spi/GitBlameCommandTest.java | 3 +- .../repository/spi/GitBrowseCommandTest.java | 3 +- .../scm/repository/spi/GitCatCommandTest.java | 3 +- .../scm/repository/spi/GitLogCommandTest.java | 3 +- 10 files changed, 394 insertions(+), 8 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/repository/ClearRepositoryCacheEvent.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConstants.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryModifyListener.java create mode 100644 scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryModifyListenerTest.java diff --git a/scm-core/src/main/java/sonia/scm/repository/ClearRepositoryCacheEvent.java b/scm-core/src/main/java/sonia/scm/repository/ClearRepositoryCacheEvent.java new file mode 100644 index 0000000000..13c228bd34 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/ClearRepositoryCacheEvent.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2014, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ +package sonia.scm.repository; + +import sonia.scm.event.Event; + +/** + * Event which causes clearing of repository cache. + * + * @author Sebastian Sdorra + * @since 1.50 + */ +@Event +public class ClearRepositoryCacheEvent { + + private final Repository repository; + + /** + * Constructs a new instance. + * + * @param repository repository + */ + public ClearRepositoryCacheEvent(Repository repository) { + this.repository = repository; + } + + /** + * Returns repository. + * + * @return repository + */ + public Repository getRepository() { + return repository; + } + +} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java index 68e7f30ba4..6af1eedc0c 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java @@ -37,6 +37,7 @@ package sonia.scm.repository.api; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -69,6 +70,8 @@ import sonia.scm.security.ScmSecurityException; //~--- JDK imports ------------------------------------------------------------ import java.util.Set; +import sonia.scm.event.ScmEventBus; +import sonia.scm.repository.ClearRepositoryCacheEvent; /** * The {@link RepositoryServiceFactory} is the entrypoint of the repository api. @@ -172,6 +175,9 @@ public final class RepositoryServiceFactory repositoryManager.addHook(cch); repositoryManager.addListener(cch); + + // register cache clear hook for incoming events + ScmEventBus.getInstance().register(cch); } //~--- methods -------------------------------------------------------------- @@ -345,6 +351,11 @@ public final class RepositoryServiceFactory //~--- methods ------------------------------------------------------------ + @Subscribe + public void onEvent(ClearRepositoryCacheEvent event) { + clearCaches(event.getRepository().getId()); + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConstants.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConstants.java new file mode 100644 index 0000000000..6d833577e1 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConstants.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2014, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ +package sonia.scm.repository; + +/** + * Constants for Git. + * + * @author Sebastian Sdorra + * @since 1.50 + */ +public final class GitConstants { + + /** + * Default branch repository property. + */ + public static final String PROPERTY_DEFAULT_BRANCH = "git.default-branch"; + + private GitConstants() { + } + +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryModifyListener.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryModifyListener.java new file mode 100644 index 0000000000..4309257350 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryModifyListener.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2014, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ +package sonia.scm.repository; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Objects; +import com.google.common.eventbus.Subscribe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.EagerSingleton; +import sonia.scm.HandlerEvent; +import sonia.scm.event.ScmEventBus; +import sonia.scm.plugin.ext.Extension; + +/** + * Repository listener which handles git related repository events. + * + * @author Sebastian Sdorra + * @since 1.50 + */ +@Extension +@EagerSingleton +public class GitRepositoryModifyListener { + + /** + * the logger for GitRepositoryModifyListener + */ + private static final Logger logger = LoggerFactory.getLogger(GitRepositoryModifyListener.class); + + /** + * Receives {@link RepositoryModificationEvent} and fires a {@link ClearRepositoryCacheEvent} if + * the default branch of a git repository was modified. + * + * @param event repository modification event + */ + @Subscribe + public void handleEvent(RepositoryModificationEvent event){ + Repository repository = event.getItem(); + + if ( isModifyEvent(event) && + isGitRepository(event.getItem()) && + hasDefaultBranchChanged(event.getItemBeforeModification(), repository)) + { + logger.info("git default branch of repository {} has changed, sending clear cache event", repository.getId()); + sendClearRepositoryCacheEvent(repository); + } + } + + @VisibleForTesting + protected void sendClearRepositoryCacheEvent(Repository repository) { + ScmEventBus.getInstance().post(new ClearRepositoryCacheEvent(repository)); + } + + private boolean isModifyEvent(RepositoryEvent event) { + return event.getEventType() == HandlerEvent.MODIFY; + } + + private boolean isGitRepository(Repository repository) { + return GitRepositoryHandler.TYPE_NAME.equals(repository.getType()); + } + + private boolean hasDefaultBranchChanged(Repository old, Repository current) { + return !Objects.equal( + old.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH), + current.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH) + ); + } + +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java index 9680a9136a..d865f77e48 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java @@ -44,6 +44,7 @@ import java.io.IOException; import org.eclipse.jgit.lib.ObjectId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.repository.GitConstants; import sonia.scm.repository.GitUtil; /** @@ -57,9 +58,6 @@ public class AbstractGitCommand * the logger for AbstractGitCommand */ private static final Logger logger = LoggerFactory.getLogger(AbstractGitCommand.class); - - @VisibleForTesting - static final String PROPERTY_DEFAULT_BRANCH = "git.default-branch"; /** * Constructs ... @@ -113,7 +111,7 @@ public class AbstractGitCommand protected ObjectId getDefaultBranch(Repository gitRepository) throws IOException { ObjectId head; - String defaultBranchName = repository.getProperty(PROPERTY_DEFAULT_BRANCH); + String defaultBranchName = repository.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH); if (!Strings.isNullOrEmpty(defaultBranchName)) { head = GitUtil.getBranchId(gitRepository, defaultBranchName); } else { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryModifyListenerTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryModifyListenerTest.java new file mode 100644 index 0000000000..9f6768aeac --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryModifyListenerTest.java @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2014, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository; + +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Before; +import sonia.scm.HandlerEvent; + +/** + * Unit tests for {@link GitRepositoryModifyListener}. + * + * @author Sebastian Sdorra + */ +public class GitRepositoryModifyListenerTest { + + private GitRepositoryModifyTestListener repositoryModifyListener; + + /** + * Set up test object. + */ + @Before + public void setUpObjectUnderTest(){ + repositoryModifyListener = new GitRepositoryModifyTestListener(); + } + + /** + * Tests happy path. + */ + @Test + public void testHandleEvent() { + Repository old = RepositoryTestData.createHeartOfGold("git"); + old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNotNull(repositoryModifyListener.repository); + assertSame(current, repositoryModifyListener.repository); + } + + /** + * Tests with new default branch. + */ + @Test + public void testWithNewDefaultBranch() { + Repository old = RepositoryTestData.createHeartOfGold("git"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNotNull(repositoryModifyListener.repository); + assertSame(current, repositoryModifyListener.repository); + } + + /** + * Tests with non git repositories. + */ + @Test + public void testNonGitRepository(){ + Repository old = RepositoryTestData.createHeartOfGold("hg"); + old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + Repository current = RepositoryTestData.createHeartOfGold("hg"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNull(repositoryModifyListener.repository); + } + + /** + * Tests without default branch. + */ + @Test + public void testWithoutDefaultBranch(){ + Repository old = RepositoryTestData.createHeartOfGold("git"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNull(repositoryModifyListener.repository); + } + + /** + * Tests with non modify event. + */ + @Test + public void testNonModifyEvent(){ + Repository old = RepositoryTestData.createHeartOfGold("git"); + old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.CREATE); + repositoryModifyListener.handleEvent(event); + + assertNull(repositoryModifyListener.repository); + } + + /** + * Tests with non git repositories. + */ + @Test + public void testNoModification(){ + Repository old = RepositoryTestData.createHeartOfGold("git"); + old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNull(repositoryModifyListener.repository); + } + + private static class GitRepositoryModifyTestListener extends GitRepositoryModifyListener { + + private Repository repository; + + @Override + protected void sendClearRepositoryCacheEvent(Repository repository) { + this.repository = repository; + } + + } + + +} \ No newline at end of file diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java index 88c9235373..d049447d7f 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java @@ -45,6 +45,7 @@ import static org.junit.Assert.*; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; +import sonia.scm.repository.GitConstants; /** * Unit tests for {@link GitBlameCommand}. @@ -73,7 +74,7 @@ public class GitBlameCommandTest extends AbstractGitCommandTestBase assertEquals("fcd0ef1831e4002ac43ea539f4094334c79ea9ec", result.getLine(1).getRevision()); // set default branch and test again - repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); result = createCommand().getBlameResult(request); assertNotNull(result); assertEquals(1, result.getTotal()); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java index d1c2f6abc9..727034b9ca 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java @@ -48,6 +48,7 @@ import static org.junit.Assert.*; import java.io.IOException; import java.util.List; +import sonia.scm.repository.GitConstants; /** * Unit tests for {@link GitBrowseCommand}. @@ -80,7 +81,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase assertEquals("f.txt", foList.get(3).getName()); // set default branch and fetch again - repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); result = createCommand().getBrowserResult(new BrowseCommandRequest()); assertNotNull(result); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java index 0d7d743e5a..ede6a53429 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java @@ -44,6 +44,7 @@ import static org.junit.Assert.*; import java.io.ByteArrayOutputStream; import java.io.IOException; +import sonia.scm.repository.GitConstants; /** * Unit tests for {@link GitCatCommand}. @@ -70,7 +71,7 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase assertEquals("a\nline for blame", execute(request)); // set default branch for repository and check again - repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); assertEquals("a and b", execute(request)); } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java index 640a28a598..c5af70e3a9 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java @@ -50,6 +50,7 @@ import static org.junit.Assert.*; import java.io.IOException; import org.eclipse.jgit.api.errors.GitAPIException; +import sonia.scm.repository.GitConstants; /** * Unit tests for {@link GitLogCommand}. @@ -79,7 +80,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(3).getId()); // set default branch and fetch again - repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); result = createCommand().getChangesets(new LogCommandRequest()); From 8628fd9e11df2f46b64e5c88c218fbb88eebac71 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 22:05:24 +0100 Subject: [PATCH 5/6] refactor CacheClearHook of RepositoryServiceFactory --- .../api/RepositoryServiceFactory.java | 102 ++++++++---------- 1 file changed, 42 insertions(+), 60 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java index 6af1eedc0c..0a532b652a 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java @@ -37,6 +37,7 @@ package sonia.scm.repository.api; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -316,51 +317,60 @@ public final class RepositoryServiceFactory //~--- inner classes -------------------------------------------------------- /** - * TODO find a more elegant way - * - * - * @version Enter version here..., 12/06/16 - * @author Enter your name here... + * Hook and listener to clear all relevant repository caches. */ private static class CacheClearHook extends PostReceiveRepositoryHook implements RepositoryListener { + + private final Set> caches = Sets.newHashSet(); /** - * Constructs ... + * Constructs a new instance and collect all repository relevant + * caches from the {@link CacheManager}. * - * - * @param cacheManager + * @param cacheManager cache manager */ public CacheClearHook(CacheManager cacheManager) { - this.blameCache = - cacheManager.getCache(BlameCommandBuilder.CacheKey.class, - BlameResult.class, BlameCommandBuilder.CACHE_NAME); - this.browseCache = - cacheManager.getCache(BrowseCommandBuilder.CacheKey.class, - BrowserResult.class, BrowseCommandBuilder.CACHE_NAME); - this.logCache = cacheManager.getCache(LogCommandBuilder.CacheKey.class, - ChangesetPagingResult.class, LogCommandBuilder.CACHE_NAME); - this.tagsCache = cacheManager.getCache(TagsCommandBuilder.CacheKey.class, - Tags.class, TagsCommandBuilder.CACHE_NAME); - this.branchesCache = - cacheManager.getCache(BranchesCommandBuilder.CacheKey.class, - Branches.class, BranchesCommandBuilder.CACHE_NAME); + this.caches.add(cacheManager.getCache( + BlameCommandBuilder.CacheKey.class, + BlameResult.class, BlameCommandBuilder.CACHE_NAME) + ); + this.caches.add(cacheManager.getCache( + BrowseCommandBuilder.CacheKey.class, + BrowserResult.class, BrowseCommandBuilder.CACHE_NAME) + ); + this.caches.add(cacheManager.getCache( + LogCommandBuilder.CacheKey.class, + ChangesetPagingResult.class, LogCommandBuilder.CACHE_NAME) + ); + this.caches.add(cacheManager.getCache( + TagsCommandBuilder.CacheKey.class, + Tags.class, TagsCommandBuilder.CACHE_NAME) + ); + this.caches.add(cacheManager.getCache( + BranchesCommandBuilder.CacheKey.class, + Branches.class, BranchesCommandBuilder.CACHE_NAME) + ); } //~--- methods ------------------------------------------------------------ + /** + * Clear caches on explicit repository cache clear event. + * + * @param event clear event + */ @Subscribe public void onEvent(ClearRepositoryCacheEvent event) { clearCaches(event.getRepository().getId()); } /** - * Method description + * Clear caches on repository push. * - * - * @param event + * @param event hook event */ @Override public void onEvent(RepositoryHookEvent event) @@ -376,11 +386,10 @@ public final class RepositoryServiceFactory } /** - * Method description + * Clear caches on repository delete event. * - * - * @param repository - * @param event + * @param repository changed repository + * @param event repository event */ @Override public void onEvent(Repository repository, HandlerEvent event) @@ -390,13 +399,7 @@ public final class RepositoryServiceFactory clearCaches(repository.getId()); } } - - /** - * Method description - * - * - * @param repositoryId - */ + @SuppressWarnings("unchecked") private void clearCaches(final String repositoryId) { @@ -405,32 +408,11 @@ public final class RepositoryServiceFactory logger.debug("clear caches for repository id {}", repositoryId); } - RepositoryCacheKeyFilter filter = - new RepositoryCacheKeyFilter(repositoryId); - - blameCache.removeAll(filter); - browseCache.removeAll(filter); - logCache.removeAll(filter); - tagsCache.removeAll(filter); - branchesCache.removeAll(filter); + RepositoryCacheKeyFilter filter = new RepositoryCacheKeyFilter(repositoryId); + for (Cache cache : caches) { + cache.removeAll(filter); + } } - - //~--- fields ------------------------------------------------------------- - - /** Field description */ - private Cache blameCache; - - /** Field description */ - private Cache branchesCache; - - /** Field description */ - private Cache browseCache; - - /** Field description */ - private Cache logCache; - - /** Field description */ - private Cache tagsCache; } From d940c2e9b93f050b1343cb1e70a42973da1c6389 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 22:14:29 +0100 Subject: [PATCH 6/6] fix release build --- .../resources/js/repository/sonia.repository.tagcombobox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js index 0db635f8cd..3f045a8cf6 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js @@ -52,7 +52,7 @@ Sonia.repository.TagComboBox = Ext.extend(Ext.form.ComboBox, { typeAhead: false, editable: false, triggerAction: 'all', - store: tagStore, + store: tagStore }; Ext.apply(this, Ext.apply(this.initialConfig, config));