From 2afe9249cf818987e1e8a20c3f6691ca79370735 Mon Sep 17 00:00:00 2001 From: Christian Aistleitner Date: Thu, 30 May 2013 15:45:17 +0200 Subject: Make ${name} optional when replicating only a single repository Thereby we allow to replicate a project under a different name. Change-Id: I6b010807560f4b44e8aac6ccfb32b061ce366a66 (cherry picked from commit 50972e33baba9eaa034ef9637efaf27e928d7a46) Reviewed-by: Ismo Haataja Reviewed-by: Oswald Buddenhagen --- .../gerrit/plugins/replication/Destination.java | 19 +++++++++++- .../plugins/replication/ReplicationQueue.java | 34 +++++++++++++--------- src/main/resources/Documentation/config.md | 8 +++-- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java index 5032dd8..4bf12d5 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java @@ -367,6 +367,22 @@ class Destination { return false; } + boolean isSingleProjectMatch() { + boolean ret = (projects.length == 1); + if (ret) { + String projectMatch = projects[0]; + if (isRE(projectMatch) || isWildcard(projectMatch)) { + // projectMatch is either regular expression, or wild-card. + // + // Even though they might refer to a single project now, they need not + // after new projects have been created. Hence, we do not treat them as + // matching a single project. + ret = false; + } + } + return ret; + } + private static boolean isRE(String str) { return str.startsWith(AccessSection.REGEX_PREFIX); } @@ -407,7 +423,8 @@ class Destination { ReplicationQueue.log.debug(String.format( "Unknown remoteNameStyle: %s, falling back to slash", remoteNameStyle)); } - String replacedPath = ReplicationQueue.replaceName(uri.getPath(), name); + String replacedPath = ReplicationQueue.replaceName(uri.getPath(), name, + isSingleProjectMatch()); if (replacedPath != null) { uri = uri.setPath(replacedPath); r.add(uri); diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java index 8991b7c..ba4c0db 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java @@ -64,12 +64,15 @@ class ReplicationQueue implements NewProjectCreatedListener { static final Logger log = LoggerFactory.getLogger(ReplicationQueue.class); - static String replaceName(String in, String name) { + static String replaceName(String in, String name, boolean keyIsOptional) { String key = "${name}"; int n = in.indexOf(key); if (0 <= n) { return in.substring(0, n) + name + in.substring(n + key.length()); } + if (keyIsOptional) { + return in; + } return null; } @@ -188,14 +191,6 @@ class ReplicationQueue implements continue; } - for (URIish u : c.getURIs()) { - if (u.getPath() == null || !u.getPath().contains("${name}")) { - throw new ConfigInvalidException(String.format( - "remote.%s.url \"%s\" lacks ${name} placeholder in %s", - c.getName(), u, cfg.getFile())); - } - } - // If destination for push is not set assume equal to source. for (RefSpec ref : c.getPushRefSpecs()) { if (ref.getDestination() == null) { @@ -209,9 +204,21 @@ class ReplicationQueue implements .setForceUpdate(true)); } - dest.add(new Destination(injector, c, cfg, database, - replicationUserFactory, pluginUser, - gitRepositoryManager, groupBackend)); + Destination destination = new Destination(injector, c, cfg, database, + replicationUserFactory, pluginUser, gitRepositoryManager, + groupBackend); + + if (!destination.isSingleProjectMatch()) { + for (URIish u : c.getURIs()) { + if (u.getPath() == null || !u.getPath().contains("${name}")) { + throw new ConfigInvalidException(String.format( + "remote.%s.url \"%s\" lacks ${name} placeholder in %s", + c.getName(), u, cfg.getFile())); + } + } + } + + dest.add(destination); } return dest.build(); } @@ -263,7 +270,8 @@ class ReplicationQueue implements continue; } - String path = replaceName(uri.getPath(), projectName.get()); + String path = replaceName(uri.getPath(), projectName.get(), + config.isSingleProjectMatch()); if (path == null) { log.warn(String.format("adminURL %s does not contain ${name}", uri)); continue; diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md index 4efcebe..0532350 100644 --- a/src/main/resources/Documentation/config.md +++ b/src/main/resources/Documentation/config.md @@ -96,11 +96,15 @@ remote.NAME.url replaced with the Gerrit project name. This is a Gerrit specific extension to the otherwise standard Git URL syntax and it must be included in each URL so that Gerrit can figure - out where each project needs to be replicated. + out where each project needs to be replicated. `${name}` may + only be omitted if the remote refers to a single repository + (i.e.: Exactly one [remote.NAME.projects][3] and that name's + value is a single project match.). See [git push][1] for details on Git URL syntax. [1]: http://www.git-scm.com/docs/git-push#URLS +[3]: #remote.NAME.projects remote.NAME.adminUrl : Address of the alternative remote server only for repository @@ -229,7 +233,7 @@ remote.NAME.remoteNameStyle By default, "slash," remote name will contain slashes as they do in Gerrit. -remote.NAME.projects +remote.NAME.projects : Specifies which repositories should be replicated to the remote. It can be provided more than once, and supports three formats: regular expressions, wildcard matching, and single -- cgit v1.2.3